mirror of
https://github.com/qpdf/qpdf.git
synced 2024-12-22 19:08:59 +00:00
a0e70b370a
This commit contains only the manual changes. It is separated for clarity. This commit would not pass CI because it lacks the automated changes, which appear in the next commit.
469 lines
12 KiB
C++
469 lines
12 KiB
C++
#include <qpdf/QPDFJob.hh>
|
|
|
|
// See "HOW TO ADD A COMMAND-LINE ARGUMENT" in README-maintainer.
|
|
|
|
#include <cstring>
|
|
#include <iostream>
|
|
#include <memory>
|
|
|
|
#include <qpdf/QPDFArgParser.hh>
|
|
#include <qpdf/QPDFCryptoProvider.hh>
|
|
#include <qpdf/QPDFLogger.hh>
|
|
#include <qpdf/QTC.hh>
|
|
#include <qpdf/QUtil.hh>
|
|
|
|
namespace
|
|
{
|
|
class ArgParser
|
|
{
|
|
public:
|
|
ArgParser(QPDFArgParser& ap, std::shared_ptr<QPDFJob::Config> c_main);
|
|
void parseOptions();
|
|
|
|
private:
|
|
#include <qpdf/auto_job_decl.hh>
|
|
|
|
void usage(std::string const& message);
|
|
void initOptionTables();
|
|
|
|
QPDFArgParser ap;
|
|
std::shared_ptr<QPDFJob::Config> c_main;
|
|
std::shared_ptr<QPDFJob::CopyAttConfig> c_copy_att;
|
|
std::shared_ptr<QPDFJob::AttConfig> c_att;
|
|
std::shared_ptr<QPDFJob::PagesConfig> c_pages;
|
|
std::shared_ptr<QPDFJob::UOConfig> c_uo;
|
|
std::shared_ptr<QPDFJob::EncConfig> c_enc;
|
|
std::vector<std::string> accumulated_args;
|
|
std::shared_ptr<char> pages_password{nullptr};
|
|
std::string user_password;
|
|
std::string owner_password;
|
|
bool used_enc_password_args{false};
|
|
bool gave_input{false};
|
|
bool gave_output{false};
|
|
};
|
|
} // namespace
|
|
|
|
ArgParser::ArgParser(QPDFArgParser& ap, std::shared_ptr<QPDFJob::Config> c_main) :
|
|
ap(ap),
|
|
c_main(c_main)
|
|
{
|
|
initOptionTables();
|
|
}
|
|
|
|
#include <qpdf/auto_job_help.hh>
|
|
|
|
void
|
|
ArgParser::initOptionTables()
|
|
{
|
|
#include <qpdf/auto_job_init.hh>
|
|
this->ap.addFinalCheck([this]() { c_main->checkConfiguration(); });
|
|
// add_help is defined in auto_job_help.hh
|
|
add_help(this->ap);
|
|
// Special case: ignore -- at the top level. This undocumented behavior is for backward
|
|
// compatibility; it was unintentionally the case prior to 10.6, and some users were relying on
|
|
// it.
|
|
this->ap.selectMainOptionTable();
|
|
this->ap.addBare("--", []() {});
|
|
}
|
|
|
|
void
|
|
ArgParser::argPositional(std::string const& arg)
|
|
{
|
|
if (!this->gave_input) {
|
|
c_main->inputFile(arg);
|
|
this->gave_input = true;
|
|
} else if (!this->gave_output) {
|
|
c_main->outputFile(arg);
|
|
this->gave_output = true;
|
|
} else {
|
|
usage("unknown argument " + arg);
|
|
}
|
|
}
|
|
|
|
void
|
|
ArgParser::argEmpty()
|
|
{
|
|
c_main->emptyInput();
|
|
this->gave_input = true;
|
|
}
|
|
|
|
void
|
|
ArgParser::argReplaceInput()
|
|
{
|
|
c_main->replaceInput();
|
|
this->gave_output = true;
|
|
}
|
|
|
|
void
|
|
ArgParser::argVersion()
|
|
{
|
|
auto whoami = this->ap.getProgname();
|
|
*QPDFLogger::defaultLogger()->getInfo()
|
|
<< whoami << " version " << QPDF::QPDFVersion() << "\n"
|
|
<< "Run " << whoami << " --copyright to see copyright and license information.\n";
|
|
}
|
|
|
|
void
|
|
ArgParser::argCopyright()
|
|
{
|
|
// clang-format off
|
|
// Make sure the output looks right on an 80-column display.
|
|
// 1 2 3 4 5 6 7 8
|
|
// 12345678901234567890123456789012345678901234567890123456789012345678901234567890
|
|
*QPDFLogger::defaultLogger()->getInfo()
|
|
<< this->ap.getProgname()
|
|
<< " version " << QPDF::QPDFVersion() << "\n"
|
|
<< "\n"
|
|
<< "Copyright (c) 2005-2024 Jay Berkenbilt\n"
|
|
<< "QPDF is licensed under the Apache License, Version 2.0 (the \"License\");\n"
|
|
<< "you may not use this file except in compliance with the License.\n"
|
|
<< "You may obtain a copy of the License at\n"
|
|
<< "\n"
|
|
<< " http://www.apache.org/licenses/LICENSE-2.0\n"
|
|
<< "\n"
|
|
<< "Unless required by applicable law or agreed to in writing, software\n"
|
|
<< "distributed under the License is distributed on an \"AS IS\" BASIS,\n"
|
|
<< "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n"
|
|
<< "See the License for the specific language governing permissions and\n"
|
|
<< "limitations under the License.\n"
|
|
<< "\n"
|
|
<< "Versions of qpdf prior to version 7 were released under the terms\n"
|
|
<< "of version 2.0 of the Artistic License. At your option, you may\n"
|
|
<< "continue to consider qpdf to be licensed under those terms. Please\n"
|
|
<< "see the manual for additional information.\n";
|
|
// clang-format on
|
|
}
|
|
|
|
void
|
|
ArgParser::argJsonHelp(std::string const& parameter)
|
|
{
|
|
int version = JSON::LATEST;
|
|
if (!(parameter.empty() || (parameter == "latest"))) {
|
|
version = QUtil::string_to_int(parameter.c_str());
|
|
}
|
|
if ((version < 1) || (version > JSON::LATEST)) {
|
|
usage(std::string("unsupported json version ") + parameter);
|
|
}
|
|
*QPDFLogger::defaultLogger()->getInfo() << QPDFJob::json_out_schema(version) << "\n";
|
|
}
|
|
|
|
void
|
|
ArgParser::argShowCrypto()
|
|
{
|
|
auto crypto = QPDFCryptoProvider::getRegisteredImpls();
|
|
std::string default_crypto = QPDFCryptoProvider::getDefaultProvider();
|
|
*QPDFLogger::defaultLogger()->getInfo() << default_crypto << "\n";
|
|
for (auto const& iter: crypto) {
|
|
if (iter != default_crypto) {
|
|
*QPDFLogger::defaultLogger()->getInfo() << iter << "\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
ArgParser::argEncrypt()
|
|
{
|
|
this->c_enc = c_main->encrypt(0, "", "");
|
|
this->accumulated_args.clear();
|
|
this->ap.selectOptionTable(O_ENCRYPTION);
|
|
}
|
|
|
|
void
|
|
ArgParser::argEncPositional(std::string const& arg)
|
|
{
|
|
if (used_enc_password_args) {
|
|
usage("positional and dashed encryption arguments may not be mixed");
|
|
}
|
|
|
|
this->accumulated_args.push_back(arg);
|
|
if (this->accumulated_args.size() < 3) {
|
|
return;
|
|
}
|
|
user_password = this->accumulated_args.at(0);
|
|
owner_password = this->accumulated_args.at(1);
|
|
auto len_str = this->accumulated_args.at(2);
|
|
this->accumulated_args.clear();
|
|
argEncBits(len_str);
|
|
}
|
|
|
|
void
|
|
ArgParser::argEncUserPassword(std::string const& arg)
|
|
{
|
|
if (!accumulated_args.empty()) {
|
|
usage("positional and dashed encryption arguments may not be mixed");
|
|
}
|
|
this->used_enc_password_args = true;
|
|
this->user_password = arg;
|
|
}
|
|
|
|
void
|
|
ArgParser::argEncOwnerPassword(std::string const& arg)
|
|
{
|
|
if (!accumulated_args.empty()) {
|
|
usage("positional and dashed encryption arguments may not be mixed");
|
|
}
|
|
this->used_enc_password_args = true;
|
|
this->owner_password = arg;
|
|
}
|
|
|
|
void
|
|
ArgParser::argEncBits(std::string const& arg)
|
|
{
|
|
if (!accumulated_args.empty()) {
|
|
usage("positional and dashed encryption arguments may not be mixed");
|
|
}
|
|
int keylen = 0;
|
|
if (arg == "40") {
|
|
keylen = 40;
|
|
this->ap.selectOptionTable(O_40_BIT_ENCRYPTION);
|
|
} else if (arg == "128") {
|
|
keylen = 128;
|
|
this->ap.selectOptionTable(O_128_BIT_ENCRYPTION);
|
|
} else if (arg == "256") {
|
|
keylen = 256;
|
|
this->ap.selectOptionTable(O_256_BIT_ENCRYPTION);
|
|
} else {
|
|
usage("encryption key length must be 40, 128, or 256");
|
|
}
|
|
this->c_enc = c_main->encrypt(keylen, user_password, owner_password);
|
|
}
|
|
|
|
void
|
|
ArgParser::argPages()
|
|
{
|
|
this->accumulated_args.clear();
|
|
this->c_pages = c_main->pages();
|
|
this->ap.selectOptionTable(O_PAGES);
|
|
}
|
|
|
|
void
|
|
ArgParser::argPagesPassword(std::string const& parameter)
|
|
{
|
|
if (this->pages_password) {
|
|
QTC::TC("qpdf", "QPDFJob duplicated pages password");
|
|
usage("--password already specified for this file");
|
|
}
|
|
if (this->accumulated_args.size() != 1) {
|
|
QTC::TC("qpdf", "QPDFJob misplaced pages password");
|
|
usage("in --pages, --password must immediately follow a file name");
|
|
}
|
|
this->pages_password = QUtil::make_shared_cstr(parameter);
|
|
}
|
|
|
|
void
|
|
ArgParser::argPagesPositional(std::string const& arg)
|
|
{
|
|
if (arg.empty()) {
|
|
if (this->accumulated_args.empty()) {
|
|
return;
|
|
}
|
|
} else {
|
|
this->accumulated_args.push_back(arg);
|
|
}
|
|
|
|
std::string file = this->accumulated_args.at(0);
|
|
char const* range_p = nullptr;
|
|
|
|
size_t n_args = this->accumulated_args.size();
|
|
if (n_args >= 2) {
|
|
// will be copied before accumulated_args is cleared
|
|
range_p = this->accumulated_args.at(1).c_str();
|
|
}
|
|
|
|
// See if the user omitted the range entirely, in which case we assume "1-z".
|
|
std::string next_file;
|
|
if (range_p == nullptr) {
|
|
if (arg.empty()) {
|
|
// The filename or password was the last argument
|
|
QTC::TC("qpdf", "QPDFJob pages range omitted at end", this->pages_password ? 0 : 1);
|
|
} else {
|
|
// We need to accumulate some more arguments
|
|
return;
|
|
}
|
|
} else {
|
|
try {
|
|
QUtil::parse_numrange(range_p, 0);
|
|
} catch (std::runtime_error& e1) {
|
|
// The range is invalid. Let's see if it's a file.
|
|
if (strcmp(range_p, ".") == 0) {
|
|
// "." means the input file.
|
|
QTC::TC("qpdf", "QPDFJob pages range omitted with .");
|
|
} else if (QUtil::file_can_be_opened(range_p)) {
|
|
QTC::TC("qpdf", "QPDFJob pages range omitted in middle");
|
|
// Yup, it's a file.
|
|
} else {
|
|
// Give the range error
|
|
usage(e1.what());
|
|
}
|
|
next_file = range_p;
|
|
range_p = nullptr;
|
|
}
|
|
}
|
|
std::string range(range_p ? range_p : "1-z");
|
|
this->c_pages->pageSpec(file, range, this->pages_password.get());
|
|
this->accumulated_args.clear();
|
|
this->pages_password = nullptr;
|
|
if (!next_file.empty()) {
|
|
this->accumulated_args.push_back(next_file);
|
|
}
|
|
}
|
|
|
|
void
|
|
ArgParser::argEndPages()
|
|
{
|
|
argPagesPositional("");
|
|
c_pages->endPages();
|
|
c_pages = nullptr;
|
|
}
|
|
|
|
void
|
|
ArgParser::argUnderlay()
|
|
{
|
|
this->c_uo = c_main->underlay();
|
|
this->ap.selectOptionTable(O_UNDERLAY_OVERLAY);
|
|
}
|
|
|
|
void
|
|
ArgParser::argOverlay()
|
|
{
|
|
this->c_uo = c_main->overlay();
|
|
this->ap.selectOptionTable(O_UNDERLAY_OVERLAY);
|
|
}
|
|
|
|
void
|
|
ArgParser::argAddAttachment()
|
|
{
|
|
this->c_att = c_main->addAttachment();
|
|
this->ap.selectOptionTable(O_ATTACHMENT);
|
|
}
|
|
|
|
void
|
|
ArgParser::argCopyAttachmentsFrom()
|
|
{
|
|
this->c_copy_att = c_main->copyAttachmentsFrom();
|
|
this->ap.selectOptionTable(O_COPY_ATTACHMENT);
|
|
}
|
|
|
|
void
|
|
ArgParser::argEndEncryption()
|
|
{
|
|
c_enc->endEncrypt();
|
|
c_enc = nullptr;
|
|
}
|
|
|
|
void
|
|
ArgParser::argEnd40BitEncryption()
|
|
{
|
|
argEndEncryption();
|
|
}
|
|
|
|
void
|
|
ArgParser::argEnd128BitEncryption()
|
|
{
|
|
argEndEncryption();
|
|
}
|
|
|
|
void
|
|
ArgParser::argEnd256BitEncryption()
|
|
{
|
|
argEndEncryption();
|
|
}
|
|
|
|
void
|
|
ArgParser::argUOPositional(std::string const& arg)
|
|
{
|
|
c_uo->file(arg);
|
|
}
|
|
|
|
void
|
|
ArgParser::argEndUnderlayOverlay()
|
|
{
|
|
c_uo->endUnderlayOverlay();
|
|
c_uo = nullptr;
|
|
}
|
|
|
|
void
|
|
ArgParser::argAttPositional(std::string const& arg)
|
|
{
|
|
c_att->file(arg);
|
|
}
|
|
|
|
void
|
|
ArgParser::argEndAttachment()
|
|
{
|
|
c_att->endAddAttachment();
|
|
c_att = nullptr;
|
|
}
|
|
|
|
void
|
|
ArgParser::argCopyAttPositional(std::string const& arg)
|
|
{
|
|
c_copy_att->file(arg);
|
|
}
|
|
|
|
void
|
|
ArgParser::argEndCopyAttachment()
|
|
{
|
|
c_copy_att->endCopyAttachmentsFrom();
|
|
c_copy_att = nullptr;
|
|
}
|
|
|
|
void
|
|
ArgParser::argSetPageLabels()
|
|
{
|
|
this->ap.selectOptionTable(O_SET_PAGE_LABELS);
|
|
accumulated_args.clear();
|
|
}
|
|
|
|
void
|
|
ArgParser::argPageLabelsPositional(std::string const& arg)
|
|
{
|
|
accumulated_args.push_back(arg);
|
|
}
|
|
|
|
void
|
|
ArgParser::argEndSetPageLabels()
|
|
{
|
|
c_main->setPageLabels(accumulated_args);
|
|
accumulated_args.clear();
|
|
}
|
|
|
|
void
|
|
ArgParser::argJobJsonHelp()
|
|
{
|
|
*QPDFLogger::defaultLogger()->getInfo()
|
|
<< QPDFJob::job_json_schema(QPDFJob::LATEST_JOB_JSON) << "\n";
|
|
}
|
|
|
|
void
|
|
ArgParser::usage(std::string const& message)
|
|
{
|
|
this->ap.usage(message);
|
|
}
|
|
|
|
void
|
|
ArgParser::parseOptions()
|
|
{
|
|
try {
|
|
this->ap.parseArgs();
|
|
} catch (std::runtime_error& e) {
|
|
usage(e.what());
|
|
}
|
|
}
|
|
|
|
void
|
|
QPDFJob::initializeFromArgv(char const* const argv[], char const* progname_env)
|
|
{
|
|
if (progname_env == nullptr) {
|
|
progname_env = "QPDF_EXECUTABLE";
|
|
}
|
|
int argc = 0;
|
|
for (auto k = argv; *k; ++k) {
|
|
++argc;
|
|
}
|
|
QPDFArgParser qap(argc, argv, progname_env);
|
|
setMessagePrefix(qap.getProgname());
|
|
ArgParser ap(qap, config());
|
|
ap.parseOptions();
|
|
}
|