mirror of
https://github.com/qpdf/qpdf.git
synced 2025-01-02 22:50:20 +00:00
Allow reading command-line args from files (fixes #16)
This commit is contained in:
parent
5993c3e83c
commit
2d5b854468
@ -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.
|
||||||
|
|
||||||
|
@ -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.
|
||||||
|
@ -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
@ -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)
|
||||||
{
|
{
|
||||||
|
@ -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
|
||||||
|
53
qpdf/qpdf.cc
53
qpdf/qpdf.cc
@ -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];
|
||||||
|
@ -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
|
||||||
|
@ -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});
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user