diff --git a/generate_auto_job b/generate_auto_job index 0bc04fa0..5e1e7e8a 100755 --- a/generate_auto_job +++ b/generate_auto_job @@ -302,22 +302,25 @@ class Main: def handle_trivial(self, i, identifier, cfg, prefix, kind, v): decl_arg = 1 + decl_arg_optional = False if kind == 'bare': decl_arg = 0 self.init.append(f'this->ap.addBare("{i}", ' f'[this](){{{cfg}->{identifier}();}});') - elif kind == 'optional_parameter': - self.init.append(f'this->ap.addOptionalParameter("{i}", ' - f'[this](char *x){{{cfg}->{identifier}(x);}});') elif kind == 'required_parameter': self.init.append(f'this->ap.addRequiredParameter("{i}", ' f'[this](char *x){{{cfg}->{identifier}(x);}}' f', "{v}");') + elif kind == 'optional_parameter': + decl_arg_optional = True + self.init.append(f'this->ap.addOptionalParameter("{i}", ' + f'[this](char *x){{{cfg}->{identifier}(x);}});') elif kind == 'required_choices': self.init.append(f'this->ap.addChoices("{i}", ' f'[this](char *x){{{cfg}->{identifier}(x);}}' f', true, {v}_choices);') elif kind == 'optional_choices': + decl_arg_optional = True self.init.append(f'this->ap.addChoices("{i}", ' f'[this](char *x){{{cfg}->{identifier}(x);}}' f', false, {v}_choices);') @@ -332,21 +335,30 @@ class Main: if fn not in self.declared_configs: self.declared_configs.add(fn) self.config_decls[cfg].append(f'QPDF_DLL {fn};') + if decl_arg_optional: + # Rather than making the parameter optional, add an + # overloaded method that takes no arguments. This + # strategy enables us to change an option from bare to + # optional_parameter or optional_choices without + # breaking binary compatibility. The overloaded + # methods both have to be implemented manually. + self.config_decls[cfg].append( + f'QPDF_DLL {config_prefix}* {identifier}();') def handle_flag(self, i, identifier, kind, v): if kind == 'bare': self.decls.append(f'void {identifier}();') self.init.append(f'this->ap.addBare("{i}", ' f'b(&ArgParser::{identifier}));') - elif kind == 'optional_parameter': - self.decls.append(f'void {identifier}(char *);') - self.init.append(f'this->ap.addOptionalParameter("{i}", ' - f'p(&ArgParser::{identifier}));') elif kind == 'required_parameter': self.decls.append(f'void {identifier}(char *);') self.init.append(f'this->ap.addRequiredParameter("{i}", ' f'p(&ArgParser::{identifier})' f', "{v}");') + elif kind == 'optional_parameter': + self.decls.append(f'void {identifier}(char *);') + self.init.append(f'this->ap.addOptionalParameter("{i}", ' + f'p(&ArgParser::{identifier}));') elif kind == 'required_choices': self.decls.append(f'void {identifier}(char *);') self.init.append(f'this->ap.addChoices("{i}", ' @@ -429,10 +441,10 @@ class Main: for i in o.get('bare', []): flags[i] = ['bare', None] - for i in o.get('optional_parameter', []): - flags[i] = ['optional_parameter', None] for i, v in o.get('required_parameter', {}).items(): flags[i] = ['required_parameter', v] + for i in o.get('optional_parameter', []): + flags[i] = ['optional_parameter', None] for i, v in o.get('required_choices', {}).items(): flags[i] = ['required_choices', v] for i, v in o.get('optional_choices', {}).items(): @@ -464,21 +476,21 @@ class Main: if kind == 'bare': self.json_init.append( f'addBare([this]() {{ {config}->{flag_key}(); }});') - elif kind == 'optional_parameter' or kind == 'required_parameter': + elif kind == 'required_parameter' or kind == 'optional_parameter': # Optional parameters end up just being the empty string, # so the handler has to deal with it. The empty string is # also allowed for non-optional. self.json_init.append( f'addParameter([this](char const* p)' f' {{ {config}->{flag_key}(p); }});') - elif kind == 'optional_choices': - self.json_init.append( - f'addChoices({v}_choices, false,' - f' [this](char const* p) {{ {config}->{flag_key}(p); }});') elif kind == 'required_choices': self.json_init.append( f'addChoices({v}_choices, true,' f' [this](char const* p) {{ {config}->{flag_key}(p); }});') + elif kind == 'optional_choices': + self.json_init.append( + f'addChoices({v}_choices, false,' + f' [this](char const* p) {{ {config}->{flag_key}(p); }});') def handle_json_manual(self, path): method = re.sub(r'\.([a-zA-Z0-9])', diff --git a/include/qpdf/auto_job_c_main.hh b/include/qpdf/auto_job_c_main.hh index 42572a3d..c3b4f63c 100644 --- a/include/qpdf/auto_job_c_main.hh +++ b/include/qpdf/auto_job_c_main.hh @@ -44,8 +44,6 @@ QPDF_DLL Config* suppressRecovery(); QPDF_DLL Config* verbose(); QPDF_DLL Config* warningExit0(); QPDF_DLL Config* withImages(); -QPDF_DLL Config* collate(char const* parameter); -QPDF_DLL Config* splitPages(char const* parameter); QPDF_DLL Config* compressionLevel(char const* parameter); QPDF_DLL Config* copyEncryption(char const* parameter); QPDF_DLL Config* encryptionFilePassword(char const* parameter); @@ -65,6 +63,10 @@ QPDF_DLL Config* removeAttachment(char const* parameter); QPDF_DLL Config* rotate(char const* parameter); QPDF_DLL Config* showAttachment(char const* parameter); QPDF_DLL Config* showObject(char const* parameter); +QPDF_DLL Config* collate(char const* parameter); +QPDF_DLL Config* collate(); +QPDF_DLL Config* splitPages(char const* parameter); +QPDF_DLL Config* splitPages(); QPDF_DLL Config* compressStreams(char const* parameter); QPDF_DLL Config* decodeLevel(char const* parameter); QPDF_DLL Config* flattenAnnotations(char const* parameter); @@ -76,3 +78,4 @@ QPDF_DLL Config* passwordMode(char const* parameter); QPDF_DLL Config* removeUnreferencedResources(char const* parameter); QPDF_DLL Config* streamData(char const* parameter); QPDF_DLL Config* json(char const* parameter); +QPDF_DLL Config* json(); diff --git a/job.sums b/job.sums index 4598af4c..0c574cc1 100644 --- a/job.sums +++ b/job.sums @@ -1,15 +1,15 @@ # Generated by generate_auto_job -generate_auto_job cb0945a8aa05eb4a523d0931b2cf1a70188e4667d42e012d7621b3aba4f32621 +generate_auto_job 1fdb113412a444aad67b0232f3f6c4f50d9e2a5701691e5146fd1b559039ef2e include/qpdf/auto_job_c_att.hh 7ad43bb374c1370ef32ebdcdcb7b73a61d281f7f4e3f12755585872ab30fb60e include/qpdf/auto_job_c_copy_att.hh 32275d03cdc69b703dd7e02ba0bbe15756e714e9ad185484773a6178dc09e1ee include/qpdf/auto_job_c_enc.hh 72e138c7b96ed5aacdce78c1dec04b1c20d361faec4f8faf52f64c1d6be99265 -include/qpdf/auto_job_c_main.hh ff776dd643279330fbf59770d1abf5aaeb13f20bfc5f6a25997aaa72a0907b44 +include/qpdf/auto_job_c_main.hh 69d5ea26098bcb6ec5b5e37ba0bca9e7d16a784d2618e0c05d635046848d5123 include/qpdf/auto_job_c_pages.hh 931840b329a36ca0e41401190e04537b47f2867671a6643bfd8da74014202671 include/qpdf/auto_job_c_uo.hh 0585b7de459fa479d9e51a45fa92de0ff6dee748efc9ec1cedd0dde6cee1ad50 job.yml effc93a805fb74503be2213ad885238db21991ba3d084fbfeff01183c66cb002 libqpdf/qpdf/auto_job_decl.hh 9f79396ec459f191be4c5fe34cf88c265cf47355a1a945fa39169d1c94cf04f6 libqpdf/qpdf/auto_job_help.hh 6002f503368f319a3d717484ac39d1558f34e67989d442f394791f6f6f5f0500 -libqpdf/qpdf/auto_job_init.hh c244e03e8b83ed7db732920f40aff0134e5f2e78a6edb9473ea4dd1934a8953e +libqpdf/qpdf/auto_job_init.hh fd13b9f730e6275a39a15d193bd9af19cf37f4495699ec1886c2b208d7811ab1 libqpdf/qpdf/auto_job_json_decl.hh c5e3fd38a3b0c569eb0c6b4c60953a09cd6bc7d3361a357a81f64fe36af2b0cf libqpdf/qpdf/auto_job_json_init.hh 3f86ce40931ca8f417d050fcd49104d73c1fa4e977ad19d54b372831a8ea17ed libqpdf/qpdf/auto_job_schema.hh 18a3780671d95224cb9a27dcac627c421cae509d59f33a63e6bda0ab53cce923 diff --git a/libqpdf/QPDFJob_config.cc b/libqpdf/QPDFJob_config.cc index ed8943f6..fb61924c 100644 --- a/libqpdf/QPDFJob_config.cc +++ b/libqpdf/QPDFJob_config.cc @@ -105,6 +105,12 @@ QPDFJob::Config::coalesceContents() return this; } +QPDFJob::Config* +QPDFJob::Config::collate() +{ + return collate(nullptr); +} + QPDFJob::Config* QPDFJob::Config::collate(char const* parameter) { @@ -234,10 +240,16 @@ QPDFJob::Config::isEncrypted() return this; } +QPDFJob::Config* +QPDFJob::Config::json() +{ + return json(nullptr); +} + QPDFJob::Config* QPDFJob::Config::json(char const* parameter) { - if (parameter) + if (parameter && strlen(parameter)) { if (strcmp(parameter, "latest") == 0) { @@ -516,6 +528,12 @@ QPDFJob::Config::showXref() return this; } +QPDFJob::Config* +QPDFJob::Config::splitPages() +{ + return splitPages(nullptr); +} + QPDFJob::Config* QPDFJob::Config::splitPages(char const* parameter) { diff --git a/libqpdf/qpdf/auto_job_init.hh b/libqpdf/qpdf/auto_job_init.hh index 28c47be4..17ab0848 100644 --- a/libqpdf/qpdf/auto_job_init.hh +++ b/libqpdf/qpdf/auto_job_init.hh @@ -79,8 +79,6 @@ this->ap.addBare("underlay", b(&ArgParser::argUnderlay)); this->ap.addBare("verbose", [this](){c_main->verbose();}); this->ap.addBare("warning-exit-0", [this](){c_main->warningExit0();}); this->ap.addBare("with-images", [this](){c_main->withImages();}); -this->ap.addOptionalParameter("collate", [this](char *x){c_main->collate(x);}); -this->ap.addOptionalParameter("split-pages", [this](char *x){c_main->splitPages(x);}); this->ap.addRequiredParameter("compression-level", [this](char *x){c_main->compressionLevel(x);}, "level"); this->ap.addRequiredParameter("copy-encryption", [this](char *x){c_main->copyEncryption(x);}, "file"); this->ap.addRequiredParameter("encryption-file-password", [this](char *x){c_main->encryptionFilePassword(x);}, "password"); @@ -100,6 +98,8 @@ this->ap.addRequiredParameter("remove-attachment", [this](char *x){c_main->remov this->ap.addRequiredParameter("rotate", [this](char *x){c_main->rotate(x);}, "[+|-]angle"); this->ap.addRequiredParameter("show-attachment", [this](char *x){c_main->showAttachment(x);}, "attachment"); this->ap.addRequiredParameter("show-object", [this](char *x){c_main->showObject(x);}, "trailer"); +this->ap.addOptionalParameter("collate", [this](char *x){c_main->collate(x);}); +this->ap.addOptionalParameter("split-pages", [this](char *x){c_main->splitPages(x);}); this->ap.addChoices("compress-streams", [this](char *x){c_main->compressStreams(x);}, true, yn_choices); this->ap.addChoices("decode-level", [this](char *x){c_main->decodeLevel(x);}, true, decode_level_choices); this->ap.addChoices("flatten-annotations", [this](char *x){c_main->flattenAnnotations(x);}, true, flatten_choices);