Commit 5a7bb3474eb10ec9dea8409466a14f72ead73e60

Authored by Jay Berkenbilt
1 parent 59531166

generate_auto_job: generate overloaded config decls for optional

For optional parameter/choices, generate an overloaded config method
that takes no arguments. This makes it possible to convert from a bare
argument to one that takes an optional parameter without breaking
binary compatibility.
generate_auto_job
@@ -302,22 +302,25 @@ class Main: @@ -302,22 +302,25 @@ class Main:
302 302
303 def handle_trivial(self, i, identifier, cfg, prefix, kind, v): 303 def handle_trivial(self, i, identifier, cfg, prefix, kind, v):
304 decl_arg = 1 304 decl_arg = 1
  305 + decl_arg_optional = False
305 if kind == 'bare': 306 if kind == 'bare':
306 decl_arg = 0 307 decl_arg = 0
307 self.init.append(f'this->ap.addBare("{i}", ' 308 self.init.append(f'this->ap.addBare("{i}", '
308 f'[this](){{{cfg}->{identifier}();}});') 309 f'[this](){{{cfg}->{identifier}();}});')
309 - elif kind == 'optional_parameter':  
310 - self.init.append(f'this->ap.addOptionalParameter("{i}", '  
311 - f'[this](char *x){{{cfg}->{identifier}(x);}});')  
312 elif kind == 'required_parameter': 310 elif kind == 'required_parameter':
313 self.init.append(f'this->ap.addRequiredParameter("{i}", ' 311 self.init.append(f'this->ap.addRequiredParameter("{i}", '
314 f'[this](char *x){{{cfg}->{identifier}(x);}}' 312 f'[this](char *x){{{cfg}->{identifier}(x);}}'
315 f', "{v}");') 313 f', "{v}");')
  314 + elif kind == 'optional_parameter':
  315 + decl_arg_optional = True
  316 + self.init.append(f'this->ap.addOptionalParameter("{i}", '
  317 + f'[this](char *x){{{cfg}->{identifier}(x);}});')
316 elif kind == 'required_choices': 318 elif kind == 'required_choices':
317 self.init.append(f'this->ap.addChoices("{i}", ' 319 self.init.append(f'this->ap.addChoices("{i}", '
318 f'[this](char *x){{{cfg}->{identifier}(x);}}' 320 f'[this](char *x){{{cfg}->{identifier}(x);}}'
319 f', true, {v}_choices);') 321 f', true, {v}_choices);')
320 elif kind == 'optional_choices': 322 elif kind == 'optional_choices':
  323 + decl_arg_optional = True
321 self.init.append(f'this->ap.addChoices("{i}", ' 324 self.init.append(f'this->ap.addChoices("{i}", '
322 f'[this](char *x){{{cfg}->{identifier}(x);}}' 325 f'[this](char *x){{{cfg}->{identifier}(x);}}'
323 f', false, {v}_choices);') 326 f', false, {v}_choices);')
@@ -332,21 +335,30 @@ class Main: @@ -332,21 +335,30 @@ class Main:
332 if fn not in self.declared_configs: 335 if fn not in self.declared_configs:
333 self.declared_configs.add(fn) 336 self.declared_configs.add(fn)
334 self.config_decls[cfg].append(f'QPDF_DLL {fn};') 337 self.config_decls[cfg].append(f'QPDF_DLL {fn};')
  338 + if decl_arg_optional:
  339 + # Rather than making the parameter optional, add an
  340 + # overloaded method that takes no arguments. This
  341 + # strategy enables us to change an option from bare to
  342 + # optional_parameter or optional_choices without
  343 + # breaking binary compatibility. The overloaded
  344 + # methods both have to be implemented manually.
  345 + self.config_decls[cfg].append(
  346 + f'QPDF_DLL {config_prefix}* {identifier}();')
335 347
336 def handle_flag(self, i, identifier, kind, v): 348 def handle_flag(self, i, identifier, kind, v):
337 if kind == 'bare': 349 if kind == 'bare':
338 self.decls.append(f'void {identifier}();') 350 self.decls.append(f'void {identifier}();')
339 self.init.append(f'this->ap.addBare("{i}", ' 351 self.init.append(f'this->ap.addBare("{i}", '
340 f'b(&ArgParser::{identifier}));') 352 f'b(&ArgParser::{identifier}));')
341 - elif kind == 'optional_parameter':  
342 - self.decls.append(f'void {identifier}(char *);')  
343 - self.init.append(f'this->ap.addOptionalParameter("{i}", '  
344 - f'p(&ArgParser::{identifier}));')  
345 elif kind == 'required_parameter': 353 elif kind == 'required_parameter':
346 self.decls.append(f'void {identifier}(char *);') 354 self.decls.append(f'void {identifier}(char *);')
347 self.init.append(f'this->ap.addRequiredParameter("{i}", ' 355 self.init.append(f'this->ap.addRequiredParameter("{i}", '
348 f'p(&ArgParser::{identifier})' 356 f'p(&ArgParser::{identifier})'
349 f', "{v}");') 357 f', "{v}");')
  358 + elif kind == 'optional_parameter':
  359 + self.decls.append(f'void {identifier}(char *);')
  360 + self.init.append(f'this->ap.addOptionalParameter("{i}", '
  361 + f'p(&ArgParser::{identifier}));')
350 elif kind == 'required_choices': 362 elif kind == 'required_choices':
351 self.decls.append(f'void {identifier}(char *);') 363 self.decls.append(f'void {identifier}(char *);')
352 self.init.append(f'this->ap.addChoices("{i}", ' 364 self.init.append(f'this->ap.addChoices("{i}", '
@@ -429,10 +441,10 @@ class Main: @@ -429,10 +441,10 @@ class Main:
429 441
430 for i in o.get('bare', []): 442 for i in o.get('bare', []):
431 flags[i] = ['bare', None] 443 flags[i] = ['bare', None]
432 - for i in o.get('optional_parameter', []):  
433 - flags[i] = ['optional_parameter', None]  
434 for i, v in o.get('required_parameter', {}).items(): 444 for i, v in o.get('required_parameter', {}).items():
435 flags[i] = ['required_parameter', v] 445 flags[i] = ['required_parameter', v]
  446 + for i in o.get('optional_parameter', []):
  447 + flags[i] = ['optional_parameter', None]
436 for i, v in o.get('required_choices', {}).items(): 448 for i, v in o.get('required_choices', {}).items():
437 flags[i] = ['required_choices', v] 449 flags[i] = ['required_choices', v]
438 for i, v in o.get('optional_choices', {}).items(): 450 for i, v in o.get('optional_choices', {}).items():
@@ -464,21 +476,21 @@ class Main: @@ -464,21 +476,21 @@ class Main:
464 if kind == 'bare': 476 if kind == 'bare':
465 self.json_init.append( 477 self.json_init.append(
466 f'addBare([this]() {{ {config}->{flag_key}(); }});') 478 f'addBare([this]() {{ {config}->{flag_key}(); }});')
467 - elif kind == 'optional_parameter' or kind == 'required_parameter': 479 + elif kind == 'required_parameter' or kind == 'optional_parameter':
468 # Optional parameters end up just being the empty string, 480 # Optional parameters end up just being the empty string,
469 # so the handler has to deal with it. The empty string is 481 # so the handler has to deal with it. The empty string is
470 # also allowed for non-optional. 482 # also allowed for non-optional.
471 self.json_init.append( 483 self.json_init.append(
472 f'addParameter([this](char const* p)' 484 f'addParameter([this](char const* p)'
473 f' {{ {config}->{flag_key}(p); }});') 485 f' {{ {config}->{flag_key}(p); }});')
474 - elif kind == 'optional_choices':  
475 - self.json_init.append(  
476 - f'addChoices({v}_choices, false,'  
477 - f' [this](char const* p) {{ {config}->{flag_key}(p); }});')  
478 elif kind == 'required_choices': 486 elif kind == 'required_choices':
479 self.json_init.append( 487 self.json_init.append(
480 f'addChoices({v}_choices, true,' 488 f'addChoices({v}_choices, true,'
481 f' [this](char const* p) {{ {config}->{flag_key}(p); }});') 489 f' [this](char const* p) {{ {config}->{flag_key}(p); }});')
  490 + elif kind == 'optional_choices':
  491 + self.json_init.append(
  492 + f'addChoices({v}_choices, false,'
  493 + f' [this](char const* p) {{ {config}->{flag_key}(p); }});')
482 494
483 def handle_json_manual(self, path): 495 def handle_json_manual(self, path):
484 method = re.sub(r'\.([a-zA-Z0-9])', 496 method = re.sub(r'\.([a-zA-Z0-9])',
include/qpdf/auto_job_c_main.hh
@@ -44,8 +44,6 @@ QPDF_DLL Config* suppressRecovery(); @@ -44,8 +44,6 @@ QPDF_DLL Config* suppressRecovery();
44 QPDF_DLL Config* verbose(); 44 QPDF_DLL Config* verbose();
45 QPDF_DLL Config* warningExit0(); 45 QPDF_DLL Config* warningExit0();
46 QPDF_DLL Config* withImages(); 46 QPDF_DLL Config* withImages();
47 -QPDF_DLL Config* collate(char const* parameter);  
48 -QPDF_DLL Config* splitPages(char const* parameter);  
49 QPDF_DLL Config* compressionLevel(char const* parameter); 47 QPDF_DLL Config* compressionLevel(char const* parameter);
50 QPDF_DLL Config* copyEncryption(char const* parameter); 48 QPDF_DLL Config* copyEncryption(char const* parameter);
51 QPDF_DLL Config* encryptionFilePassword(char const* parameter); 49 QPDF_DLL Config* encryptionFilePassword(char const* parameter);
@@ -65,6 +63,10 @@ QPDF_DLL Config* removeAttachment(char const* parameter); @@ -65,6 +63,10 @@ QPDF_DLL Config* removeAttachment(char const* parameter);
65 QPDF_DLL Config* rotate(char const* parameter); 63 QPDF_DLL Config* rotate(char const* parameter);
66 QPDF_DLL Config* showAttachment(char const* parameter); 64 QPDF_DLL Config* showAttachment(char const* parameter);
67 QPDF_DLL Config* showObject(char const* parameter); 65 QPDF_DLL Config* showObject(char const* parameter);
  66 +QPDF_DLL Config* collate(char const* parameter);
  67 +QPDF_DLL Config* collate();
  68 +QPDF_DLL Config* splitPages(char const* parameter);
  69 +QPDF_DLL Config* splitPages();
68 QPDF_DLL Config* compressStreams(char const* parameter); 70 QPDF_DLL Config* compressStreams(char const* parameter);
69 QPDF_DLL Config* decodeLevel(char const* parameter); 71 QPDF_DLL Config* decodeLevel(char const* parameter);
70 QPDF_DLL Config* flattenAnnotations(char const* parameter); 72 QPDF_DLL Config* flattenAnnotations(char const* parameter);
@@ -76,3 +78,4 @@ QPDF_DLL Config* passwordMode(char const* parameter); @@ -76,3 +78,4 @@ QPDF_DLL Config* passwordMode(char const* parameter);
76 QPDF_DLL Config* removeUnreferencedResources(char const* parameter); 78 QPDF_DLL Config* removeUnreferencedResources(char const* parameter);
77 QPDF_DLL Config* streamData(char const* parameter); 79 QPDF_DLL Config* streamData(char const* parameter);
78 QPDF_DLL Config* json(char const* parameter); 80 QPDF_DLL Config* json(char const* parameter);
  81 +QPDF_DLL Config* json();
job.sums
1 # Generated by generate_auto_job 1 # Generated by generate_auto_job
2 -generate_auto_job cb0945a8aa05eb4a523d0931b2cf1a70188e4667d42e012d7621b3aba4f32621 2 +generate_auto_job 1fdb113412a444aad67b0232f3f6c4f50d9e2a5701691e5146fd1b559039ef2e
3 include/qpdf/auto_job_c_att.hh 7ad43bb374c1370ef32ebdcdcb7b73a61d281f7f4e3f12755585872ab30fb60e 3 include/qpdf/auto_job_c_att.hh 7ad43bb374c1370ef32ebdcdcb7b73a61d281f7f4e3f12755585872ab30fb60e
4 include/qpdf/auto_job_c_copy_att.hh 32275d03cdc69b703dd7e02ba0bbe15756e714e9ad185484773a6178dc09e1ee 4 include/qpdf/auto_job_c_copy_att.hh 32275d03cdc69b703dd7e02ba0bbe15756e714e9ad185484773a6178dc09e1ee
5 include/qpdf/auto_job_c_enc.hh 72e138c7b96ed5aacdce78c1dec04b1c20d361faec4f8faf52f64c1d6be99265 5 include/qpdf/auto_job_c_enc.hh 72e138c7b96ed5aacdce78c1dec04b1c20d361faec4f8faf52f64c1d6be99265
6 -include/qpdf/auto_job_c_main.hh ff776dd643279330fbf59770d1abf5aaeb13f20bfc5f6a25997aaa72a0907b44 6 +include/qpdf/auto_job_c_main.hh 69d5ea26098bcb6ec5b5e37ba0bca9e7d16a784d2618e0c05d635046848d5123
7 include/qpdf/auto_job_c_pages.hh 931840b329a36ca0e41401190e04537b47f2867671a6643bfd8da74014202671 7 include/qpdf/auto_job_c_pages.hh 931840b329a36ca0e41401190e04537b47f2867671a6643bfd8da74014202671
8 include/qpdf/auto_job_c_uo.hh 0585b7de459fa479d9e51a45fa92de0ff6dee748efc9ec1cedd0dde6cee1ad50 8 include/qpdf/auto_job_c_uo.hh 0585b7de459fa479d9e51a45fa92de0ff6dee748efc9ec1cedd0dde6cee1ad50
9 job.yml effc93a805fb74503be2213ad885238db21991ba3d084fbfeff01183c66cb002 9 job.yml effc93a805fb74503be2213ad885238db21991ba3d084fbfeff01183c66cb002
10 libqpdf/qpdf/auto_job_decl.hh 9f79396ec459f191be4c5fe34cf88c265cf47355a1a945fa39169d1c94cf04f6 10 libqpdf/qpdf/auto_job_decl.hh 9f79396ec459f191be4c5fe34cf88c265cf47355a1a945fa39169d1c94cf04f6
11 libqpdf/qpdf/auto_job_help.hh 6002f503368f319a3d717484ac39d1558f34e67989d442f394791f6f6f5f0500 11 libqpdf/qpdf/auto_job_help.hh 6002f503368f319a3d717484ac39d1558f34e67989d442f394791f6f6f5f0500
12 -libqpdf/qpdf/auto_job_init.hh c244e03e8b83ed7db732920f40aff0134e5f2e78a6edb9473ea4dd1934a8953e 12 +libqpdf/qpdf/auto_job_init.hh fd13b9f730e6275a39a15d193bd9af19cf37f4495699ec1886c2b208d7811ab1
13 libqpdf/qpdf/auto_job_json_decl.hh c5e3fd38a3b0c569eb0c6b4c60953a09cd6bc7d3361a357a81f64fe36af2b0cf 13 libqpdf/qpdf/auto_job_json_decl.hh c5e3fd38a3b0c569eb0c6b4c60953a09cd6bc7d3361a357a81f64fe36af2b0cf
14 libqpdf/qpdf/auto_job_json_init.hh 3f86ce40931ca8f417d050fcd49104d73c1fa4e977ad19d54b372831a8ea17ed 14 libqpdf/qpdf/auto_job_json_init.hh 3f86ce40931ca8f417d050fcd49104d73c1fa4e977ad19d54b372831a8ea17ed
15 libqpdf/qpdf/auto_job_schema.hh 18a3780671d95224cb9a27dcac627c421cae509d59f33a63e6bda0ab53cce923 15 libqpdf/qpdf/auto_job_schema.hh 18a3780671d95224cb9a27dcac627c421cae509d59f33a63e6bda0ab53cce923
libqpdf/QPDFJob_config.cc
@@ -106,6 +106,12 @@ QPDFJob::Config::coalesceContents() @@ -106,6 +106,12 @@ QPDFJob::Config::coalesceContents()
106 } 106 }
107 107
108 QPDFJob::Config* 108 QPDFJob::Config*
  109 +QPDFJob::Config::collate()
  110 +{
  111 + return collate(nullptr);
  112 +}
  113 +
  114 +QPDFJob::Config*
109 QPDFJob::Config::collate(char const* parameter) 115 QPDFJob::Config::collate(char const* parameter)
110 { 116 {
111 auto n = (((parameter == 0) || (strlen(parameter) == 0)) ? 1 : 117 auto n = (((parameter == 0) || (strlen(parameter) == 0)) ? 1 :
@@ -235,9 +241,15 @@ QPDFJob::Config::isEncrypted() @@ -235,9 +241,15 @@ QPDFJob::Config::isEncrypted()
235 } 241 }
236 242
237 QPDFJob::Config* 243 QPDFJob::Config*
  244 +QPDFJob::Config::json()
  245 +{
  246 + return json(nullptr);
  247 +}
  248 +
  249 +QPDFJob::Config*
238 QPDFJob::Config::json(char const* parameter) 250 QPDFJob::Config::json(char const* parameter)
239 { 251 {
240 - if (parameter) 252 + if (parameter && strlen(parameter))
241 { 253 {
242 if (strcmp(parameter, "latest") == 0) 254 if (strcmp(parameter, "latest") == 0)
243 { 255 {
@@ -517,6 +529,12 @@ QPDFJob::Config::showXref() @@ -517,6 +529,12 @@ QPDFJob::Config::showXref()
517 } 529 }
518 530
519 QPDFJob::Config* 531 QPDFJob::Config*
  532 +QPDFJob::Config::splitPages()
  533 +{
  534 + return splitPages(nullptr);
  535 +}
  536 +
  537 +QPDFJob::Config*
520 QPDFJob::Config::splitPages(char const* parameter) 538 QPDFJob::Config::splitPages(char const* parameter)
521 { 539 {
522 int n = (((parameter == 0) || (strlen(parameter) == 0)) ? 1 : 540 int n = (((parameter == 0) || (strlen(parameter) == 0)) ? 1 :
libqpdf/qpdf/auto_job_init.hh
@@ -79,8 +79,6 @@ this->ap.addBare("underlay", b(&ArgParser::argUnderlay)); @@ -79,8 +79,6 @@ this->ap.addBare("underlay", b(&ArgParser::argUnderlay));
79 this->ap.addBare("verbose", [this](){c_main->verbose();}); 79 this->ap.addBare("verbose", [this](){c_main->verbose();});
80 this->ap.addBare("warning-exit-0", [this](){c_main->warningExit0();}); 80 this->ap.addBare("warning-exit-0", [this](){c_main->warningExit0();});
81 this->ap.addBare("with-images", [this](){c_main->withImages();}); 81 this->ap.addBare("with-images", [this](){c_main->withImages();});
82 -this->ap.addOptionalParameter("collate", [this](char *x){c_main->collate(x);});  
83 -this->ap.addOptionalParameter("split-pages", [this](char *x){c_main->splitPages(x);});  
84 this->ap.addRequiredParameter("compression-level", [this](char *x){c_main->compressionLevel(x);}, "level"); 82 this->ap.addRequiredParameter("compression-level", [this](char *x){c_main->compressionLevel(x);}, "level");
85 this->ap.addRequiredParameter("copy-encryption", [this](char *x){c_main->copyEncryption(x);}, "file"); 83 this->ap.addRequiredParameter("copy-encryption", [this](char *x){c_main->copyEncryption(x);}, "file");
86 this->ap.addRequiredParameter("encryption-file-password", [this](char *x){c_main->encryptionFilePassword(x);}, "password"); 84 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 @@ -100,6 +98,8 @@ this->ap.addRequiredParameter("remove-attachment", [this](char *x){c_main->remov
100 this->ap.addRequiredParameter("rotate", [this](char *x){c_main->rotate(x);}, "[+|-]angle"); 98 this->ap.addRequiredParameter("rotate", [this](char *x){c_main->rotate(x);}, "[+|-]angle");
101 this->ap.addRequiredParameter("show-attachment", [this](char *x){c_main->showAttachment(x);}, "attachment"); 99 this->ap.addRequiredParameter("show-attachment", [this](char *x){c_main->showAttachment(x);}, "attachment");
102 this->ap.addRequiredParameter("show-object", [this](char *x){c_main->showObject(x);}, "trailer"); 100 this->ap.addRequiredParameter("show-object", [this](char *x){c_main->showObject(x);}, "trailer");
  101 +this->ap.addOptionalParameter("collate", [this](char *x){c_main->collate(x);});
  102 +this->ap.addOptionalParameter("split-pages", [this](char *x){c_main->splitPages(x);});
103 this->ap.addChoices("compress-streams", [this](char *x){c_main->compressStreams(x);}, true, yn_choices); 103 this->ap.addChoices("compress-streams", [this](char *x){c_main->compressStreams(x);}, true, yn_choices);
104 this->ap.addChoices("decode-level", [this](char *x){c_main->decodeLevel(x);}, true, decode_level_choices); 104 this->ap.addChoices("decode-level", [this](char *x){c_main->decodeLevel(x);}, true, decode_level_choices);
105 this->ap.addChoices("flatten-annotations", [this](char *x){c_main->flattenAnnotations(x);}, true, flatten_choices); 105 this->ap.addChoices("flatten-annotations", [this](char *x){c_main->flattenAnnotations(x);}, true, flatten_choices);