2
1
mirror of https://github.com/qpdf/qpdf.git synced 2024-05-31 17:30:54 +00:00

Allow reading command-line args from files (fixes #16)

This commit is contained in:
Jay Berkenbilt 2017-07-29 22:23:21 -04:00
parent 5993c3e83c
commit 2d5b854468
10 changed files with 152 additions and 3 deletions

View File

@ -1,5 +1,9 @@
2017-07-29 Jay Berkenbilt <ejb@ql.org> 2017-07-29 Jay Berkenbilt <ejb@ql.org>
* Support @filename and @- in the qpdf command-line tool to read
command-line arguments, one per line, from the named file. @-
reads from standard input. Fixes #16.
* Detect when input file and output file are the same and exit to * Detect when input file and output file are the same and exit to
avoid overwriting and losing input file. Fixes #29. avoid overwriting and losing input file. Fixes #29.

View File

@ -161,6 +161,11 @@ namespace QUtil
QPDF_DLL QPDF_DLL
RandomDataProvider* getRandomDataProvider(); RandomDataProvider* getRandomDataProvider();
QPDF_DLL
std::list<std::string> read_lines_from_file(char const* filename);
QPDF_DLL
std::list<std::string> read_lines_from_file(std::istream&);
// These routines help the tokenizer recognize certain character // These routines help the tokenizer recognize certain character
// classes without using ctype, which we avoid because of locale // classes without using ctype, which we avoid because of locale
// considerations. // considerations.

View File

@ -11,6 +11,7 @@
#include <cmath> #include <cmath>
#include <iomanip> #include <iomanip>
#include <sstream> #include <sstream>
#include <fstream>
#include <stdexcept> #include <stdexcept>
#include <stdio.h> #include <stdio.h>
#include <errno.h> #include <errno.h>
@ -566,3 +567,55 @@ QUtil::is_number(char const* p)
} }
return found_digit; return found_digit;
} }
std::list<std::string>
QUtil::read_lines_from_file(char const* filename)
{
std::ifstream in(filename, std::ios_base::binary);
if (! in.is_open())
{
throw_system_error(std::string("open ") + filename);
}
std::list<std::string> lines = read_lines_from_file(in);
in.close();
return lines;
}
std::list<std::string>
QUtil::read_lines_from_file(std::istream& in)
{
std::list<std::string> result;
std::string* buf = 0;
char c;
while (in.get(c))
{
if (buf == 0)
{
result.push_back("");
buf = &(result.back());
buf->reserve(80);
}
if (buf->capacity() == buf->size())
{
buf->reserve(buf->capacity() * 2);
}
if (c == '\n')
{
// Remove any carriage return that preceded the
// newline and discard the newline
if ((! buf->empty()) && ((*(buf->rbegin())) == '\r'))
{
buf->erase(buf->length() - 1);
}
buf = 0;
}
else
{
buf->append(1, c);
}
}
return result;
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -170,6 +170,16 @@ void same_file_test()
assert_same_file("", "qutil.out", false); assert_same_file("", "qutil.out", false);
} }
void read_lines_from_file_test()
{
std::list<std::string> lines = QUtil::read_lines_from_file("other-file");
for (std::list<std::string>::iterator iter = lines.begin();
iter != lines.end(); ++iter)
{
std::cout << *iter << std::endl;
}
}
int main(int argc, char* argv[]) int main(int argc, char* argv[])
{ {
try try
@ -187,6 +197,8 @@ int main(int argc, char* argv[])
get_whoami_test(); get_whoami_test();
std::cout << "----" << std::endl; std::cout << "----" << std::endl;
same_file_test(); same_file_test();
std::cout << "----" << std::endl;
read_lines_from_file_test();
} }
catch (std::exception& e) catch (std::exception& e)
{ {

View File

@ -254,6 +254,14 @@ make
were going to add pages from another source, as discussed in <xref were going to add pages from another source, as discussed in <xref
linkend="ref.page-selection"/>. linkend="ref.page-selection"/>.
</para> </para>
<para>
If <option>@filename</option> appears anywhere in the
command-line, it will be read line by line, and each line will be
treated as a command-line argument. The <option>@-</option> option
allows arguments to be read from standard input. This allows qpdf
to be invoked with an arbitrary number of arbitrarily long
arguments.
</para>
<para> <para>
<option>outfilename</option> does not have to be seekable, even <option>outfilename</option> does not have to be seekable, even
when generating linearized files. Specifying when generating linearized files. Specifying

View File

@ -69,6 +69,10 @@ Usage: qpdf [ options ] { infilename | --empty } [ outfilename ]\n\
\n\ \n\
An option summary appears below. Please see the documentation for details.\n\ An option summary appears below. Please see the documentation for details.\n\
\n\ \n\
If @filename appears anywhere in the command-line, each line of filename\n\
will be interpreted as an argument. No interpolation is done. Line\n\
terminators are stripped. @- can be specified to read from standard input.\n\
\n\
Note that when contradictory options are provided, whichever options are\n\ Note that when contradictory options are provided, whichever options are\n\
provided last take precedence.\n\ provided last take precedence.\n\
\n\ \n\
@ -953,6 +957,28 @@ static void parse_version(std::string const& full_version_string,
version = v; version = v;
} }
static void read_args_from_file(char const* filename,
std::vector<PointerHolder<char> >& new_argv)
{
std::list<std::string> lines;
if (strcmp(filename, "-") == 0)
{
QTC::TC("qpdf", "qpdf read args from stdin");
lines = QUtil::read_lines_from_file(std::cin);
}
else
{
QTC::TC("qpdf", "qpdf read args from file");
lines = QUtil::read_lines_from_file(filename);
}
for (std::list<std::string>::iterator iter = lines.begin();
iter != lines.end(); ++iter)
{
new_argv.push_back(
PointerHolder<char>(QUtil::copy_string((*iter).c_str()), true));
}
}
int main(int argc, char* argv[]) int main(int argc, char* argv[])
{ {
whoami = QUtil::getWhoami(argv[0]); whoami = QUtil::getWhoami(argv[0]);
@ -1060,6 +1086,33 @@ int main(int argc, char* argv[])
char const* infilename = 0; char const* infilename = 0;
char const* outfilename = 0; char const* outfilename = 0;
// Support reading arguments from files. Create a new argv. Ensure
// that argv itself as well as all its contents are automatically
// deleted by using PointerHolder objects to back the pointers in
// argv.
std::vector<PointerHolder<char> > new_argv;
new_argv.push_back(PointerHolder<char>(QUtil::copy_string(argv[0]), true));
for (int i = 1; i < argc; ++i)
{
if ((strlen(argv[i]) > 1) && (argv[i][0] == '@'))
{
read_args_from_file(1+argv[i], new_argv);
}
else
{
new_argv.push_back(
PointerHolder<char>(QUtil::copy_string(argv[i]), true));
}
}
PointerHolder<char*> argv_ph(new char*[1+new_argv.size()], true);
argv = argv_ph.getPointer();
for (size_t i = 0; i < new_argv.size(); ++i)
{
argv[i] = new_argv.at(i).getPointer();
}
argc = static_cast<int>(new_argv.size());
argv[argc] = 0;
for (int i = 1; i < argc; ++i) for (int i = 1; i < argc; ++i)
{ {
char const* arg = argv[i]; char const* arg = argv[i];

View File

@ -285,3 +285,5 @@ QPDFObjectHandle non-stream in parsecontent 0
QPDFObjectHandle errors in parsecontent 0 QPDFObjectHandle errors in parsecontent 0
QPDF stream with non-space 0 QPDF stream with non-space 0
qpdf same file error 0 qpdf same file error 0
qpdf read args from stdin 0
qpdf read args from file 0

View File

@ -635,14 +635,20 @@ $td->runtest("dump bad xref",
{$td->FILE => "bad-xref-entry.out", {$td->FILE => "bad-xref-entry.out",
$td->EXIT_STATUS => 0}, $td->EXIT_STATUS => 0},
$td->NORMALIZE_NEWLINES); $td->NORMALIZE_NEWLINES);
# Test @file here too.
open(F, ">args") or die;
print F "--check\n";
print F "--show-xref\n";
close(F);
$td->runtest("dump corrected bad xref", $td->runtest("dump corrected bad xref",
{$td->COMMAND => "qpdf --check --show-xref bad-xref-entry.pdf"}, {$td->COMMAND => "qpdf \@args bad-xref-entry.pdf"},
{$td->FILE => "bad-xref-entry-corrected.out", {$td->FILE => "bad-xref-entry-corrected.out",
$td->EXIT_STATUS => 3}, $td->EXIT_STATUS => 3},
$td->NORMALIZE_NEWLINES); $td->NORMALIZE_NEWLINES);
unlink "args";
$td->runtest("don't overwrite self", $td->runtest("don't overwrite self",
{$td->COMMAND => "qpdf a.pdf a.pdf"}, {$td->COMMAND => "(echo a.pdf; echo a.pdf) | qpdf \@-"},
{$td->REGEXP => "input file and output file are the same.*", {$td->REGEXP => "input file and output file are the same.*",
$td->EXIT_STATUS => 2}); $td->EXIT_STATUS => 2});