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>
|
||||
|
||||
* 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
|
||||
avoid overwriting and losing input file. Fixes #29.
|
||||
|
||||
|
@ -161,6 +161,11 @@ namespace QUtil
|
||||
QPDF_DLL
|
||||
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
|
||||
// classes without using ctype, which we avoid because of locale
|
||||
// considerations.
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include <cmath>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
#include <stdexcept>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
@ -566,3 +567,55 @@ QUtil::is_number(char const* p)
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
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[])
|
||||
{
|
||||
try
|
||||
@ -187,6 +197,8 @@ int main(int argc, char* argv[])
|
||||
get_whoami_test();
|
||||
std::cout << "----" << std::endl;
|
||||
same_file_test();
|
||||
std::cout << "----" << std::endl;
|
||||
read_lines_from_file_test();
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
|
@ -254,6 +254,14 @@ make
|
||||
were going to add pages from another source, as discussed in <xref
|
||||
linkend="ref.page-selection"/>.
|
||||
</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>
|
||||
<option>outfilename</option> does not have to be seekable, even
|
||||
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\
|
||||
An option summary appears below. Please see the documentation for details.\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\
|
||||
provided last take precedence.\n\
|
||||
\n\
|
||||
@ -953,6 +957,28 @@ static void parse_version(std::string const& full_version_string,
|
||||
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[])
|
||||
{
|
||||
whoami = QUtil::getWhoami(argv[0]);
|
||||
@ -1060,6 +1086,33 @@ int main(int argc, char* argv[])
|
||||
char const* infilename = 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)
|
||||
{
|
||||
char const* arg = argv[i];
|
||||
|
@ -285,3 +285,5 @@ QPDFObjectHandle non-stream in parsecontent 0
|
||||
QPDFObjectHandle errors in parsecontent 0
|
||||
QPDF stream with non-space 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->EXIT_STATUS => 0},
|
||||
$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->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->EXIT_STATUS => 3},
|
||||
$td->NORMALIZE_NEWLINES);
|
||||
unlink "args";
|
||||
|
||||
$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->EXIT_STATUS => 2});
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user