Commit 245723c5702d9b9290178f3c4ae14629c2990920

Authored by Jay Berkenbilt
1 parent 15120660

Table-driven parsing for top-level arguments

qpdf/qpdf.cc
@@ -256,12 +256,87 @@ class ArgParser @@ -256,12 +256,87 @@ class ArgParser
256 void parseOptions(); 256 void parseOptions();
257 257
258 private: 258 private:
  259 + typedef void (ArgParser::*bare_arg_handler_t)();
  260 + typedef void (ArgParser::*param_arg_handler_t)(char* parameter);
  261 +
  262 + struct OptionEntry
  263 + {
  264 + OptionEntry() :
  265 + parameter_needed(false),
  266 + bare_arg_handler(0),
  267 + param_arg_handler(0)
  268 + {
  269 + }
  270 + bool parameter_needed;
  271 + std::string parameter_name;
  272 + std::set<std::string> choices;
  273 + bare_arg_handler_t bare_arg_handler;
  274 + param_arg_handler_t param_arg_handler;
  275 + };
  276 + friend struct OptionEntry;
  277 +
  278 + OptionEntry oe_positional(param_arg_handler_t);
  279 + OptionEntry oe_bare(bare_arg_handler_t);
  280 + OptionEntry oe_requiredParameter(param_arg_handler_t, char const* name);
  281 + OptionEntry oe_optionalParameter(param_arg_handler_t);
  282 + OptionEntry oe_requiredChoices(param_arg_handler_t, char const** choices);
  283 +
  284 + void argPositional(char* arg);
  285 + void argPassword(char* parameter);
  286 + void argEmpty();
  287 + void argLinearize();
  288 + void argEncrypt();
  289 + void argDecrypt();
  290 + void argPasswordIsHexKey();
  291 + void argCopyEncryption(char* parameter);
  292 + void argEncryptionFilePassword(char* parameter);
  293 + void argPages();
  294 + void argRotate(char* parameter);
  295 + void argStreamData(char* parameter);
  296 + void argCompressStreams(char* parameter);
  297 + void argDecodeLevel(char* parameter);
  298 + void argNormalizeContent(char* parameter);
  299 + void argSuppressRecovery();
  300 + void argObjectStreams(char* parameter);
  301 + void argIgnoreXrefStreams();
  302 + void argQdf();
  303 + void argPreserveUnreferenced();
  304 + void argPreserveUnreferencedResources();
  305 + void argKeepFilesOpen(char* parameter);
  306 + void argNewlineBeforeEndstream();
  307 + void argLinearizePass1(char* parameter);
  308 + void argCoalesceContents();
  309 + void argMinVersion(char* parameter);
  310 + void argForceVersion(char* parameter);
  311 + void argSplitPages(char* parameter);
  312 + void argVerbose();
  313 + void argProgress();
  314 + void argNoWarn();
  315 + void argDeterministicId();
  316 + void argStaticId();
  317 + void argStaticAesIv();
  318 + void argNoOriginalObjectIds();
  319 + void argShowEncryption();
  320 + void argShowEncryptionKey();
  321 + void argCheckLinearization();
  322 + void argShowLinearization();
  323 + void argShowXref();
  324 + void argShowObject(char* parameter);
  325 + void argShowObject();
  326 + void argFilteredStreamData();
  327 + void argShowNpages();
  328 + void argShowPages();
  329 + void argWithImages();
  330 + void argShowJson();
  331 + void argCheck();
  332 +
259 void usage(std::string const& message); 333 void usage(std::string const& message);
  334 + void initOptionTable();
260 void handleHelpVersion(); 335 void handleHelpVersion();
261 void handleArgFileArguments(); 336 void handleArgFileArguments();
262 void readArgsFromFile(char const* filename); 337 void readArgsFromFile(char const* filename);
263 - void parseEncryptOptions(int& cur_arg);  
264 - std::vector<PageSpec> parsePagesOptions(int& cur_arg); 338 + void parseEncryptOptions();
  339 + std::vector<PageSpec> parsePagesOptions();
265 void parseRotationParameter(std::string const&); 340 void parseRotationParameter(std::string const&);
266 std::vector<int> parseNumrange(char const* range, int max, 341 std::vector<int> parseNumrange(char const* range, int max,
267 bool throw_error = false); 342 bool throw_error = false);
@@ -271,7 +346,9 @@ class ArgParser @@ -271,7 +346,9 @@ class ArgParser
271 int argc; 346 int argc;
272 char** argv; 347 char** argv;
273 Options& o; 348 Options& o;
  349 + int cur_arg;
274 350
  351 + std::map<std::string, OptionEntry> option_table;
275 std::vector<PointerHolder<char> > new_argv; 352 std::vector<PointerHolder<char> > new_argv;
276 PointerHolder<char*> argv_ph; 353 PointerHolder<char*> argv_ph;
277 }; 354 };
@@ -279,8 +356,525 @@ class ArgParser @@ -279,8 +356,525 @@ class ArgParser
279 ArgParser::ArgParser(int argc, char* argv[], Options& o) : 356 ArgParser::ArgParser(int argc, char* argv[], Options& o) :
280 argc(argc), 357 argc(argc),
281 argv(argv), 358 argv(argv),
282 - o(o) 359 + o(o),
  360 + cur_arg(0)
  361 +{
  362 + initOptionTable();
  363 +}
  364 +
  365 +ArgParser::OptionEntry
  366 +ArgParser::oe_positional(param_arg_handler_t h)
  367 +{
  368 + OptionEntry oe;
  369 + oe.param_arg_handler = h;
  370 + return oe;
  371 +}
  372 +
  373 +ArgParser::OptionEntry
  374 +ArgParser::oe_bare(bare_arg_handler_t h)
  375 +{
  376 + OptionEntry oe;
  377 + oe.parameter_needed = false;
  378 + oe.bare_arg_handler = h;
  379 + return oe;
  380 +}
  381 +
  382 +ArgParser::OptionEntry
  383 +ArgParser::oe_requiredParameter(param_arg_handler_t h, char const* name)
  384 +{
  385 + OptionEntry oe;
  386 + oe.parameter_needed = true;
  387 + oe.parameter_name = name;
  388 + oe.param_arg_handler = h;
  389 + return oe;
  390 +}
  391 +
  392 +ArgParser::OptionEntry
  393 +ArgParser::oe_optionalParameter(param_arg_handler_t h)
  394 +{
  395 + OptionEntry oe;
  396 + oe.parameter_needed = false;
  397 + oe.param_arg_handler = h;
  398 + return oe;
  399 +}
  400 +
  401 +ArgParser::OptionEntry
  402 +ArgParser::oe_requiredChoices(param_arg_handler_t h, char const** choices)
  403 +{
  404 + OptionEntry oe;
  405 + oe.parameter_needed = true;
  406 + oe.param_arg_handler = h;
  407 + for (char const** i = choices; *i; ++i)
  408 + {
  409 + oe.choices.insert(*i);
  410 + }
  411 + return oe;
  412 +}
  413 +
  414 +void
  415 +ArgParser::initOptionTable()
  416 +{
  417 + std::map<std::string, OptionEntry>& t = this->option_table;
  418 + char const* yn[] = {"y", "n", 0};
  419 + t[""] = oe_positional(&ArgParser::argPositional);
  420 + t["password"] = oe_requiredParameter(&ArgParser::argPassword, "pass");
  421 + t["empty"] = oe_bare(&ArgParser::argEmpty);
  422 + t["linearize"] = oe_bare(&ArgParser::argLinearize);
  423 + t["encrypt"] = oe_bare(&ArgParser::argEncrypt);
  424 + t["decrypt"] = oe_bare(&ArgParser::argDecrypt);
  425 + t["password-is-hex-key"] = oe_bare(&ArgParser::argPasswordIsHexKey);
  426 + t["copy-encryption"] = oe_requiredParameter(
  427 + &ArgParser::argCopyEncryption, "file");
  428 + t["encryption-file-password"] = oe_requiredParameter(
  429 + &ArgParser::argEncryptionFilePassword, "password");
  430 + t["pages"] = oe_bare(&ArgParser::argPages);
  431 + t["rotate"] = oe_requiredParameter(
  432 + &ArgParser::argRotate, "[+|-]angle:page-range");
  433 + char const* streamDataChoices[] =
  434 + {"compress", "preserve", "uncompress", 0};
  435 + t["stream-data"] = oe_requiredChoices(
  436 + &ArgParser::argStreamData, streamDataChoices);
  437 + t["compress-streams"] = oe_requiredChoices(
  438 + &ArgParser::argCompressStreams, yn);
  439 + char const* decodeLevelChoices[] =
  440 + {"none", "generalized", "specialized", "all", 0};
  441 + t["decode-level"] = oe_requiredChoices(
  442 + &ArgParser::argDecodeLevel, decodeLevelChoices);
  443 + t["normalize-content"] = oe_requiredChoices(
  444 + &ArgParser::argNormalizeContent, yn);
  445 + t["suppress-recovery"] = oe_bare(&ArgParser::argSuppressRecovery);
  446 + char const* objectStreamsChoices[] = {"disable", "preserve", "generate", 0};
  447 + t["object-streams"] = oe_requiredChoices(
  448 + &ArgParser::argObjectStreams, objectStreamsChoices);
  449 + t["ignore-xref-streams"] = oe_bare(&ArgParser::argIgnoreXrefStreams);
  450 + t["qdf"] = oe_bare(&ArgParser::argQdf);
  451 + t["preserve-unreferenced"] = oe_bare(&ArgParser::argPreserveUnreferenced);
  452 + t["preserve-unreferenced-resources"] = oe_bare(
  453 + &ArgParser::argPreserveUnreferencedResources);
  454 + t["keep-files-open"] = oe_requiredChoices(&ArgParser::argKeepFilesOpen, yn);
  455 + t["newline-before-endstream"] = oe_bare(
  456 + &ArgParser::argNewlineBeforeEndstream);
  457 + t["linearize-pass1"] = oe_requiredParameter(
  458 + &ArgParser::argLinearizePass1, "filename");
  459 + t["coalesce-contents"] = oe_bare(&ArgParser::argCoalesceContents);
  460 + t["min-version"] = oe_requiredParameter(
  461 + &ArgParser::argMinVersion, "version");
  462 + t["force-version"] = oe_requiredParameter(
  463 + &ArgParser::argForceVersion, "version");
  464 + t["split-pages"] = oe_optionalParameter(&ArgParser::argSplitPages);
  465 + t["verbose"] = oe_bare(&ArgParser::argVerbose);
  466 + t["progress"] = oe_bare(&ArgParser::argProgress);
  467 + t["no-warn"] = oe_bare(&ArgParser::argNoWarn);
  468 + t["deterministic-id"] = oe_bare(&ArgParser::argDeterministicId);
  469 + t["static-id"] = oe_bare(&ArgParser::argStaticId);
  470 + t["static-aes-iv"] = oe_bare(&ArgParser::argStaticAesIv);
  471 + t["no-original-object-ids"] = oe_bare(&ArgParser::argNoOriginalObjectIds);
  472 + t["show-encryption"] = oe_bare(&ArgParser::argShowEncryption);
  473 + t["show-encryption-key"] = oe_bare(&ArgParser::argShowEncryptionKey);
  474 + t["check-linearization"] = oe_bare(&ArgParser::argCheckLinearization);
  475 + t["show-linearization"] = oe_bare(&ArgParser::argShowLinearization);
  476 + t["show-xref"] = oe_bare(&ArgParser::argShowXref);
  477 + t["show-object"] = oe_requiredParameter(
  478 + &ArgParser::argShowObject, "obj[,gen]");
  479 + t["raw-stream-data"] = oe_bare(&ArgParser::argShowObject);
  480 + t["filtered-stream-data"] = oe_bare(&ArgParser::argFilteredStreamData);
  481 + t["show-npages"] = oe_bare(&ArgParser::argShowNpages);
  482 + t["show-pages"] = oe_bare(&ArgParser::argShowPages);
  483 + t["with-images"] = oe_bare(&ArgParser::argWithImages);
  484 + t["show-json"] = oe_bare(&ArgParser::argShowJson);
  485 + t["check"] = oe_bare(&ArgParser::argCheck);
  486 +}
  487 +
  488 +void
  489 +ArgParser::argPositional(char* arg)
  490 +{
  491 + if (o.infilename == 0)
  492 + {
  493 + o.infilename = arg;
  494 + }
  495 + else if (o.outfilename == 0)
  496 + {
  497 + o.outfilename = arg;
  498 + }
  499 + else
  500 + {
  501 + usage(std::string("unknown argument ") + arg);
  502 + }
  503 +}
  504 +
  505 +void
  506 +ArgParser::argPassword(char* parameter)
  507 +{
  508 + o.password = parameter;
  509 +}
  510 +
  511 +void
  512 +ArgParser::argEmpty()
  513 +{
  514 + o.infilename = "";
  515 +}
  516 +
  517 +void
  518 +ArgParser::argLinearize()
  519 +{
  520 + o.linearize = true;
  521 +}
  522 +
  523 +void
  524 +ArgParser::argEncrypt()
  525 +{
  526 + ++cur_arg;
  527 + parseEncryptOptions();
  528 + o.encrypt = true;
  529 + o.decrypt = false;
  530 + o.copy_encryption = false;
  531 +}
  532 +
  533 +void
  534 +ArgParser::argDecrypt()
  535 +{
  536 + o.decrypt = true;
  537 + o.encrypt = false;
  538 + o.copy_encryption = false;
  539 +}
  540 +
  541 +void
  542 +ArgParser::argPasswordIsHexKey()
  543 +{
  544 + o.password_is_hex_key = true;
  545 +}
  546 +
  547 +void
  548 +ArgParser::argCopyEncryption(char* parameter)
283 { 549 {
  550 + o.encryption_file = parameter;
  551 + o.copy_encryption = true;
  552 + o.encrypt = false;
  553 + o.decrypt = false;
  554 +}
  555 +
  556 +void
  557 +ArgParser::argEncryptionFilePassword(char* parameter)
  558 +{
  559 + o.encryption_file_password = parameter;
  560 +}
  561 +
  562 +void
  563 +ArgParser::argPages()
  564 +{
  565 + ++cur_arg;
  566 + o.page_specs = parsePagesOptions();
  567 + if (o.page_specs.empty())
  568 + {
  569 + usage("--pages: no page specifications given");
  570 + }
  571 +}
  572 +
  573 +void
  574 +ArgParser::argRotate(char* parameter)
  575 +{
  576 + parseRotationParameter(parameter);
  577 +}
  578 +
  579 +void
  580 +ArgParser::argStreamData(char* parameter)
  581 +{
  582 + o.stream_data_set = true;
  583 + if (strcmp(parameter, "compress") == 0)
  584 + {
  585 + o.stream_data_mode = qpdf_s_compress;
  586 + }
  587 + else if (strcmp(parameter, "preserve") == 0)
  588 + {
  589 + o.stream_data_mode = qpdf_s_preserve;
  590 + }
  591 + else if (strcmp(parameter, "uncompress") == 0)
  592 + {
  593 + o.stream_data_mode = qpdf_s_uncompress;
  594 + }
  595 + else
  596 + {
  597 + // If this happens, it means streamDataChoices in
  598 + // ArgParser::initOptionTable is wrong.
  599 + usage("invalid stream-data option");
  600 + }
  601 +}
  602 +
  603 +void
  604 +ArgParser::argCompressStreams(char* parameter)
  605 +{
  606 + o.compress_streams_set = true;
  607 + o.compress_streams = (strcmp(parameter, "y") == 0);
  608 +}
  609 +
  610 +void
  611 +ArgParser::argDecodeLevel(char* parameter)
  612 +{
  613 + o.decode_level_set = true;
  614 + if (strcmp(parameter, "none") == 0)
  615 + {
  616 + o.decode_level = qpdf_dl_none;
  617 + }
  618 + else if (strcmp(parameter, "generalized") == 0)
  619 + {
  620 + o.decode_level = qpdf_dl_generalized;
  621 + }
  622 + else if (strcmp(parameter, "specialized") == 0)
  623 + {
  624 + o.decode_level = qpdf_dl_specialized;
  625 + }
  626 + else if (strcmp(parameter, "all") == 0)
  627 + {
  628 + o.decode_level = qpdf_dl_all;
  629 + }
  630 + else
  631 + {
  632 + // If this happens, it means decodeLevelChoices in
  633 + // ArgParser::initOptionTable is wrong.
  634 + usage("invalid option");
  635 + }
  636 +}
  637 +
  638 +void
  639 +ArgParser::argNormalizeContent(char* parameter)
  640 +{
  641 + o.normalize_set = true;
  642 + o.normalize = (strcmp(parameter, "y") == 0);
  643 +}
  644 +
  645 +void
  646 +ArgParser::argSuppressRecovery()
  647 +{
  648 + o.suppress_recovery = true;
  649 +}
  650 +
  651 +void
  652 +ArgParser::argObjectStreams(char* parameter)
  653 +{
  654 + o.object_stream_set = true;
  655 + if (strcmp(parameter, "disable") == 0)
  656 + {
  657 + o.object_stream_mode = qpdf_o_disable;
  658 + }
  659 + else if (strcmp(parameter, "preserve") == 0)
  660 + {
  661 + o.object_stream_mode = qpdf_o_preserve;
  662 + }
  663 + else if (strcmp(parameter, "generate") == 0)
  664 + {
  665 + o.object_stream_mode = qpdf_o_generate;
  666 + }
  667 + else
  668 + {
  669 + // If this happens, it means objectStreamsChoices in
  670 + // ArgParser::initOptionTable is wrong.
  671 + usage("invalid object stream mode");
  672 + }
  673 +}
  674 +
  675 +void
  676 +ArgParser::argIgnoreXrefStreams()
  677 +{
  678 + o.ignore_xref_streams = true;
  679 +}
  680 +
  681 +void
  682 +ArgParser::argQdf()
  683 +{
  684 + o.qdf_mode = true;
  685 +}
  686 +
  687 +void
  688 +ArgParser::argPreserveUnreferenced()
  689 +{
  690 + o.preserve_unreferenced_objects = true;
  691 +}
  692 +
  693 +void
  694 +ArgParser::argPreserveUnreferencedResources()
  695 +{
  696 + o.preserve_unreferenced_page_resources = true;
  697 +}
  698 +
  699 +void
  700 +ArgParser::argKeepFilesOpen(char* parameter)
  701 +{
  702 + o.keep_files_open_set = true;
  703 + o.keep_files_open = (strcmp(parameter, "y") == 0);
  704 +}
  705 +
  706 +void
  707 +ArgParser::argNewlineBeforeEndstream()
  708 +{
  709 + o.newline_before_endstream = true;
  710 +}
  711 +
  712 +void
  713 +ArgParser::argLinearizePass1(char* parameter)
  714 +{
  715 + o.linearize_pass1 = parameter;
  716 +}
  717 +
  718 +void
  719 +ArgParser::argCoalesceContents()
  720 +{
  721 + o.coalesce_contents = true;
  722 +}
  723 +
  724 +void
  725 +ArgParser::argMinVersion(char* parameter)
  726 +{
  727 + o.min_version = parameter;
  728 +}
  729 +
  730 +void
  731 +ArgParser::argForceVersion(char* parameter)
  732 +{
  733 + o.force_version = parameter;
  734 +}
  735 +
  736 +void
  737 +ArgParser::argSplitPages(char* parameter)
  738 +{
  739 + int n = ((parameter == 0) ? 1 :
  740 + QUtil::string_to_int(parameter));
  741 + o.split_pages = n;
  742 +}
  743 +
  744 +void
  745 +ArgParser::argVerbose()
  746 +{
  747 + o.verbose = true;
  748 +}
  749 +
  750 +void
  751 +ArgParser::argProgress()
  752 +{
  753 + o.progress = true;
  754 +}
  755 +
  756 +void
  757 +ArgParser::argNoWarn()
  758 +{
  759 + o.suppress_warnings = true;
  760 +}
  761 +
  762 +void
  763 +ArgParser::argDeterministicId()
  764 +{
  765 + o.deterministic_id = true;
  766 +}
  767 +
  768 +void
  769 +ArgParser::argStaticId()
  770 +{
  771 + o.static_id = true;
  772 +}
  773 +
  774 +void
  775 +ArgParser::argStaticAesIv()
  776 +{
  777 + o.static_aes_iv = true;
  778 +}
  779 +
  780 +void
  781 +ArgParser::argNoOriginalObjectIds()
  782 +{
  783 + o.suppress_original_object_id = true;
  784 +}
  785 +
  786 +void
  787 +ArgParser::argShowEncryption()
  788 +{
  789 + o.show_encryption = true;
  790 + o.require_outfile = false;
  791 +}
  792 +
  793 +void
  794 +ArgParser::argShowEncryptionKey()
  795 +{
  796 + o.show_encryption_key = true;
  797 +}
  798 +
  799 +void
  800 +ArgParser::argCheckLinearization()
  801 +{
  802 + o.check_linearization = true;
  803 + o.require_outfile = false;
  804 +}
  805 +
  806 +void
  807 +ArgParser::argShowLinearization()
  808 +{
  809 + o.show_linearization = true;
  810 + o.require_outfile = false;
  811 +}
  812 +
  813 +void
  814 +ArgParser::argShowXref()
  815 +{
  816 + o.show_xref = true;
  817 + o.require_outfile = false;
  818 +}
  819 +
  820 +void
  821 +ArgParser::argShowObject(char* parameter)
  822 +{
  823 + char* obj = parameter;
  824 + char* gen = obj;
  825 + if ((gen = strchr(obj, ',')) != 0)
  826 + {
  827 + *gen++ = 0;
  828 + o.show_gen = QUtil::string_to_int(gen);
  829 + }
  830 + o.show_obj = QUtil::string_to_int(obj);
  831 + o.require_outfile = false;
  832 +}
  833 +
  834 +void
  835 +ArgParser::argShowObject()
  836 +{
  837 + o.show_raw_stream_data = true;
  838 +}
  839 +
  840 +void
  841 +ArgParser::argFilteredStreamData()
  842 +{
  843 + o.show_filtered_stream_data = true;
  844 +}
  845 +
  846 +void
  847 +ArgParser::argShowNpages()
  848 +{
  849 + o.show_npages = true;
  850 + o.require_outfile = false;
  851 +}
  852 +
  853 +void
  854 +ArgParser::argShowPages()
  855 +{
  856 + o.show_pages = true;
  857 + o.require_outfile = false;
  858 +}
  859 +
  860 +void
  861 +ArgParser::argWithImages()
  862 +{
  863 + o.show_page_images = true;
  864 +}
  865 +
  866 +void
  867 +ArgParser::argShowJson()
  868 +{
  869 + o.show_json = true;
  870 + o.require_outfile = false;
  871 +}
  872 +
  873 +void
  874 +ArgParser::argCheck()
  875 +{
  876 + o.check = true;
  877 + o.require_outfile = false;
284 } 878 }
285 879
286 void 880 void
@@ -807,7 +1401,7 @@ ArgParser::parseNumrange(char const* range, int max, bool throw_error) @@ -807,7 +1401,7 @@ ArgParser::parseNumrange(char const* range, int max, bool throw_error)
807 } 1401 }
808 1402
809 void 1403 void
810 -ArgParser::parseEncryptOptions(int& cur_arg) 1404 +ArgParser::parseEncryptOptions()
811 { 1405 {
812 if (cur_arg + 3 >= argc) 1406 if (cur_arg + 3 >= argc)
813 { 1407 {
@@ -1130,7 +1724,7 @@ ArgParser::parseEncryptOptions(int&amp; cur_arg) @@ -1130,7 +1724,7 @@ ArgParser::parseEncryptOptions(int&amp; cur_arg)
1130 } 1724 }
1131 1725
1132 std::vector<PageSpec> 1726 std::vector<PageSpec>
1133 -ArgParser::parsePagesOptions(int& cur_arg) 1727 +ArgParser::parsePagesOptions()
1134 { 1728 {
1135 std::vector<PageSpec> result; 1729 std::vector<PageSpec> result;
1136 while (1) 1730 while (1)
@@ -1391,11 +1985,11 @@ ArgParser::parseRotationParameter(std::string const&amp; parameter) @@ -1391,11 +1985,11 @@ ArgParser::parseRotationParameter(std::string const&amp; parameter)
1391 void 1985 void
1392 ArgParser::parseOptions() 1986 ArgParser::parseOptions()
1393 { 1987 {
1394 - handleHelpVersion(); 1988 + handleHelpVersion(); // QXXXQ calls std::cout
1395 handleArgFileArguments(); 1989 handleArgFileArguments();
1396 - for (int i = 1; i < argc; ++i) 1990 + for (cur_arg = 1; cur_arg < argc; ++cur_arg)
1397 { 1991 {
1398 - char const* arg = argv[i]; 1992 + char* arg = argv[cur_arg];
1399 if ((arg[0] == '-') && (strcmp(arg, "-") != 0)) 1993 if ((arg[0] == '-') && (strcmp(arg, "-") != 0))
1400 { 1994 {
1401 ++arg; 1995 ++arg;
@@ -1410,381 +2004,64 @@ ArgParser::parseOptions() @@ -1410,381 +2004,64 @@ ArgParser::parseOptions()
1410 *parameter++ = 0; 2004 *parameter++ = 0;
1411 } 2005 }
1412 2006
1413 - if (strcmp(arg, "password") == 0)  
1414 - {  
1415 - if (parameter == 0)  
1416 - {  
1417 - usage("--password must be given as --password=pass");  
1418 - }  
1419 - o.password = parameter;  
1420 - }  
1421 - else if (strcmp(arg, "empty") == 0)  
1422 - {  
1423 - o.infilename = "";  
1424 - }  
1425 - else if (strcmp(arg, "linearize") == 0)  
1426 - {  
1427 - o.linearize = true;  
1428 - }  
1429 - else if (strcmp(arg, "encrypt") == 0)  
1430 - {  
1431 - parseEncryptOptions(++i);  
1432 - o.encrypt = true;  
1433 - o.decrypt = false;  
1434 - o.copy_encryption = false;  
1435 - }  
1436 - else if (strcmp(arg, "decrypt") == 0)  
1437 - {  
1438 - o.decrypt = true;  
1439 - o.encrypt = false;  
1440 - o.copy_encryption = false;  
1441 - }  
1442 - else if (strcmp(arg, "password-is-hex-key") == 0)  
1443 - {  
1444 - o.password_is_hex_key = true;  
1445 - }  
1446 - else if (strcmp(arg, "copy-encryption") == 0)  
1447 - {  
1448 - if (parameter == 0)  
1449 - {  
1450 - usage("--copy-encryption must be given as"  
1451 - "--copy_encryption=file");  
1452 - }  
1453 - o.encryption_file = parameter;  
1454 - o.copy_encryption = true;  
1455 - o.encrypt = false;  
1456 - o.decrypt = false;  
1457 - }  
1458 - else if (strcmp(arg, "encryption-file-password") == 0)  
1459 - {  
1460 - if (parameter == 0)  
1461 - {  
1462 - usage("--encryption-file-password must be given as"  
1463 - "--encryption-file-password=password");  
1464 - }  
1465 - o.encryption_file_password = parameter;  
1466 - }  
1467 - else if (strcmp(arg, "pages") == 0)  
1468 - {  
1469 - o.page_specs = parsePagesOptions(++i);  
1470 - if (o.page_specs.empty())  
1471 - {  
1472 - usage("--pages: no page specifications given");  
1473 - }  
1474 - }  
1475 - else if (strcmp(arg, "rotate") == 0)  
1476 - {  
1477 - if (parameter == 0)  
1478 - {  
1479 - usage("--rotate must be given as"  
1480 - " --rotate=[+|-]angle:page-range");  
1481 - }  
1482 - parseRotationParameter(parameter);  
1483 - }  
1484 - else if (strcmp(arg, "stream-data") == 0)  
1485 - {  
1486 - if (parameter == 0)  
1487 - {  
1488 - usage("--stream-data must be given as"  
1489 - "--stream-data=option");  
1490 - }  
1491 - o.stream_data_set = true;  
1492 - if (strcmp(parameter, "compress") == 0)  
1493 - {  
1494 - o.stream_data_mode = qpdf_s_compress;  
1495 - }  
1496 - else if (strcmp(parameter, "preserve") == 0)  
1497 - {  
1498 - o.stream_data_mode = qpdf_s_preserve;  
1499 - }  
1500 - else if (strcmp(parameter, "uncompress") == 0)  
1501 - {  
1502 - o.stream_data_mode = qpdf_s_uncompress;  
1503 - }  
1504 - else  
1505 - {  
1506 - usage("invalid stream-data option");  
1507 - }  
1508 - }  
1509 - else if (strcmp(arg, "compress-streams") == 0)  
1510 - {  
1511 - o.compress_streams_set = true;  
1512 - if (parameter && (strcmp(parameter, "y") == 0))  
1513 - {  
1514 - o.compress_streams = true;  
1515 - }  
1516 - else if (parameter && (strcmp(parameter, "n") == 0))  
1517 - {  
1518 - o.compress_streams = false;  
1519 - }  
1520 - else  
1521 - {  
1522 - usage("--compress-streams must be given as"  
1523 - " --compress-streams=[yn]");  
1524 - }  
1525 - }  
1526 - else if (strcmp(arg, "decode-level") == 0)  
1527 - {  
1528 - if (parameter == 0)  
1529 - {  
1530 - usage("--decode-level must be given as"  
1531 - "--decode-level=option");  
1532 - }  
1533 - o.decode_level_set = true;  
1534 - if (strcmp(parameter, "none") == 0)  
1535 - {  
1536 - o.decode_level = qpdf_dl_none;  
1537 - }  
1538 - else if (strcmp(parameter, "generalized") == 0)  
1539 - {  
1540 - o.decode_level = qpdf_dl_generalized;  
1541 - }  
1542 - else if (strcmp(parameter, "specialized") == 0)  
1543 - {  
1544 - o.decode_level = qpdf_dl_specialized;  
1545 - }  
1546 - else if (strcmp(parameter, "all") == 0)  
1547 - {  
1548 - o.decode_level = qpdf_dl_all;  
1549 - }  
1550 - else  
1551 - {  
1552 - usage("invalid stream-data option");  
1553 - }  
1554 - }  
1555 - else if (strcmp(arg, "normalize-content") == 0) 2007 + std::string arg_s(arg);
  2008 + if (0 == this->option_table.count(arg_s))
1556 { 2009 {
1557 - o.normalize_set = true;  
1558 - if (parameter && (strcmp(parameter, "y") == 0))  
1559 - {  
1560 - o.normalize = true;  
1561 - }  
1562 - else if (parameter && (strcmp(parameter, "n") == 0))  
1563 - {  
1564 - o.normalize = false;  
1565 - }  
1566 - else  
1567 - {  
1568 - usage("--normalize-content must be given as"  
1569 - " --normalize-content=[yn]");  
1570 - }  
1571 - }  
1572 - else if (strcmp(arg, "suppress-recovery") == 0)  
1573 - {  
1574 - o.suppress_recovery = true;  
1575 - }  
1576 - else if (strcmp(arg, "object-streams") == 0)  
1577 - {  
1578 - if (parameter == 0)  
1579 - {  
1580 - usage("--object-streams must be given as"  
1581 - " --object-streams=option");  
1582 - }  
1583 - o.object_stream_set = true;  
1584 - if (strcmp(parameter, "disable") == 0)  
1585 - {  
1586 - o.object_stream_mode = qpdf_o_disable;  
1587 - }  
1588 - else if (strcmp(parameter, "preserve") == 0)  
1589 - {  
1590 - o.object_stream_mode = qpdf_o_preserve;  
1591 - }  
1592 - else if (strcmp(parameter, "generate") == 0)  
1593 - {  
1594 - o.object_stream_mode = qpdf_o_generate;  
1595 - }  
1596 - else  
1597 - {  
1598 - usage("invalid object stream mode");  
1599 - }  
1600 - }  
1601 - else if (strcmp(arg, "ignore-xref-streams") == 0)  
1602 - {  
1603 - o.ignore_xref_streams = true;  
1604 - }  
1605 - else if (strcmp(arg, "qdf") == 0)  
1606 - {  
1607 - o.qdf_mode = true;  
1608 - }  
1609 - else if (strcmp(arg, "preserve-unreferenced") == 0)  
1610 - {  
1611 - o.preserve_unreferenced_objects = true;  
1612 - }  
1613 - else if (strcmp(arg, "preserve-unreferenced-resources") == 0)  
1614 - {  
1615 - o.preserve_unreferenced_page_resources = true; 2010 + usage(std::string("unknown option --") + arg);
1616 } 2011 }
1617 - else if (strcmp(arg, "keep-files-open") == 0) 2012 +
  2013 + OptionEntry& oe = this->option_table[arg_s];
  2014 + if ((oe.parameter_needed && (0 == parameter)) ||
  2015 + ((! oe.choices.empty() &&
  2016 + ((0 == parameter) ||
  2017 + (0 == oe.choices.count(parameter))))))
1618 { 2018 {
1619 - o.keep_files_open_set = true;  
1620 - if (parameter && (strcmp(parameter, "y") == 0)) 2019 + std::string message =
  2020 + "--" + arg_s + " must be given as --" + arg_s + "=";
  2021 + if (! oe.choices.empty())
1621 { 2022 {
1622 - o.keep_files_open = true; 2023 + QTC::TC("qpdf", "qpdf required choices");
  2024 + message += "{";
  2025 + for (std::set<std::string>::iterator iter =
  2026 + oe.choices.begin();
  2027 + iter != oe.choices.end(); ++iter)
  2028 + {
  2029 + if (iter != oe.choices.begin())
  2030 + {
  2031 + message += ",";
  2032 + }
  2033 + message += *iter;
  2034 + }
  2035 + message += "}";
1623 } 2036 }
1624 - else if (parameter && (strcmp(parameter, "n") == 0)) 2037 + else if (! oe.parameter_name.empty())
1625 { 2038 {
1626 - o.keep_files_open = false; 2039 + QTC::TC("qpdf", "qpdf required parameter");
  2040 + message += oe.parameter_name;
1627 } 2041 }
1628 else 2042 else
1629 { 2043 {
1630 - usage("--keep-files-open must be given as"  
1631 - " --keep-files-open=[yn]");  
1632 - }  
1633 - }  
1634 - else if (strcmp(arg, "newline-before-endstream") == 0)  
1635 - {  
1636 - o.newline_before_endstream = true;  
1637 - }  
1638 - else if (strcmp(arg, "linearize-pass1") == 0)  
1639 - {  
1640 - if (parameter == 0)  
1641 - {  
1642 - usage("--linearize-pass1 be given as"  
1643 - "--linearize-pass1=filename"); 2044 + // should not be possible
  2045 + message += "option";
1644 } 2046 }
1645 - o.linearize_pass1 = parameter;  
1646 - }  
1647 - else if (strcmp(arg, "coalesce-contents") == 0)  
1648 - {  
1649 - o.coalesce_contents = true; 2047 + usage(message);
1650 } 2048 }
1651 - else if (strcmp(arg, "min-version") == 0) 2049 + if (oe.bare_arg_handler)
1652 { 2050 {
1653 - if (parameter == 0)  
1654 - {  
1655 - usage("--min-version be given as"  
1656 - "--min-version=version");  
1657 - }  
1658 - o.min_version = parameter;  
1659 - }  
1660 - else if (strcmp(arg, "force-version") == 0)  
1661 - {  
1662 - if (parameter == 0)  
1663 - {  
1664 - usage("--force-version be given as"  
1665 - "--force-version=version");  
1666 - }  
1667 - o.force_version = parameter; 2051 + (this->*(oe.bare_arg_handler))();
1668 } 2052 }
1669 - else if (strcmp(arg, "split-pages") == 0) 2053 + else if (oe.param_arg_handler)
1670 { 2054 {
1671 - int n = ((parameter == 0) ? 1 :  
1672 - QUtil::string_to_int(parameter));  
1673 - o.split_pages = n; 2055 + (this->*(oe.param_arg_handler))(parameter);
1674 } 2056 }
1675 - else if (strcmp(arg, "verbose") == 0)  
1676 - {  
1677 - o.verbose = true;  
1678 - }  
1679 - else if (strcmp(arg, "progress") == 0)  
1680 - {  
1681 - o.progress = true;  
1682 - }  
1683 - else if (strcmp(arg, "no-warn") == 0)  
1684 - {  
1685 - o.suppress_warnings = true;  
1686 - }  
1687 - else if (strcmp(arg, "deterministic-id") == 0)  
1688 - {  
1689 - o.deterministic_id = true;  
1690 - }  
1691 - else if (strcmp(arg, "static-id") == 0)  
1692 - {  
1693 - o.static_id = true;  
1694 - }  
1695 - else if (strcmp(arg, "static-aes-iv") == 0)  
1696 - {  
1697 - o.static_aes_iv = true;  
1698 - }  
1699 - else if (strcmp(arg, "no-original-object-ids") == 0)  
1700 - {  
1701 - o.suppress_original_object_id = true;  
1702 - }  
1703 - else if (strcmp(arg, "show-encryption") == 0)  
1704 - {  
1705 - o.show_encryption = true;  
1706 - o.require_outfile = false;  
1707 - }  
1708 - else if (strcmp(arg, "show-encryption-key") == 0)  
1709 - {  
1710 - o.show_encryption_key = true;  
1711 - }  
1712 - else if (strcmp(arg, "check-linearization") == 0)  
1713 - {  
1714 - o.check_linearization = true;  
1715 - o.require_outfile = false;  
1716 - }  
1717 - else if (strcmp(arg, "show-linearization") == 0)  
1718 - {  
1719 - o.show_linearization = true;  
1720 - o.require_outfile = false;  
1721 - }  
1722 - else if (strcmp(arg, "show-xref") == 0)  
1723 - {  
1724 - o.show_xref = true;  
1725 - o.require_outfile = false;  
1726 - }  
1727 - else if (strcmp(arg, "show-object") == 0)  
1728 - {  
1729 - if (parameter == 0)  
1730 - {  
1731 - usage("--show-object must be given as"  
1732 - " --show-object=obj[,gen]");  
1733 - }  
1734 - char* obj = parameter;  
1735 - char* gen = obj;  
1736 - if ((gen = strchr(obj, ',')) != 0)  
1737 - {  
1738 - *gen++ = 0;  
1739 - o.show_gen = QUtil::string_to_int(gen);  
1740 - }  
1741 - o.show_obj = QUtil::string_to_int(obj);  
1742 - o.require_outfile = false;  
1743 - }  
1744 - else if (strcmp(arg, "raw-stream-data") == 0)  
1745 - {  
1746 - o.show_raw_stream_data = true;  
1747 - }  
1748 - else if (strcmp(arg, "filtered-stream-data") == 0)  
1749 - {  
1750 - o.show_filtered_stream_data = true;  
1751 - }  
1752 - else if (strcmp(arg, "show-npages") == 0)  
1753 - {  
1754 - o.show_npages = true;  
1755 - o.require_outfile = false;  
1756 - }  
1757 - else if (strcmp(arg, "show-pages") == 0)  
1758 - {  
1759 - o.show_pages = true;  
1760 - o.require_outfile = false;  
1761 - }  
1762 - else if (strcmp(arg, "with-images") == 0)  
1763 - {  
1764 - o.show_page_images = true;  
1765 - }  
1766 - else if (strcmp(arg, "show-json") == 0)  
1767 - {  
1768 - o.show_json = true;  
1769 - o.require_outfile = false;  
1770 - }  
1771 - else if (strcmp(arg, "check") == 0)  
1772 - {  
1773 - o.check = true;  
1774 - o.require_outfile = false;  
1775 - }  
1776 - else  
1777 - {  
1778 - usage(std::string("unknown option --") + arg);  
1779 - }  
1780 - }  
1781 - else if (o.infilename == 0)  
1782 - {  
1783 - o.infilename = arg;  
1784 } 2057 }
1785 - else if (o.outfilename == 0) 2058 + else if (0 != this->option_table.count(""))
1786 { 2059 {
1787 - o.outfilename = arg; 2060 + OptionEntry& oe = this->option_table[""];
  2061 + if (oe.param_arg_handler)
  2062 + {
  2063 + (this->*(oe.param_arg_handler))(arg);
  2064 + }
1788 } 2065 }
1789 else 2066 else
1790 { 2067 {
@@ -1827,7 +2104,8 @@ ArgParser::parseOptions() @@ -1827,7 +2104,8 @@ ArgParser::parseOptions()
1827 if (QUtil::same_file(o.infilename, o.outfilename)) 2104 if (QUtil::same_file(o.infilename, o.outfilename))
1828 { 2105 {
1829 QTC::TC("qpdf", "qpdf same file error"); 2106 QTC::TC("qpdf", "qpdf same file error");
1830 - usage("input file and output file are the same; this would cause input file to be lost"); 2107 + usage("input file and output file are the same;"
  2108 + " this would cause input file to be lost");
1831 } 2109 }
1832 } 2110 }
1833 2111
qpdf/qpdf.testcov
@@ -367,3 +367,5 @@ QPDFOutlineObjectHelper named dest 0 @@ -367,3 +367,5 @@ QPDFOutlineObjectHelper named dest 0
367 QPDFOutlineDocumentHelper name named dest 0 367 QPDFOutlineDocumentHelper name named dest 0
368 QPDFOutlineDocumentHelper string named dest 0 368 QPDFOutlineDocumentHelper string named dest 0
369 QPDFOutlineObjectHelper loop 0 369 QPDFOutlineObjectHelper loop 0
  370 +qpdf required parameter 0
  371 +qpdf required choices 0
qpdf/qtest/qpdf.test
@@ -102,6 +102,27 @@ $td-&gt;runtest(&quot;UTF-16 encoding errors&quot;, @@ -102,6 +102,27 @@ $td-&gt;runtest(&quot;UTF-16 encoding errors&quot;,
102 102
103 show_ntests(); 103 show_ntests();
104 # ---------- 104 # ----------
  105 +$td->notify("--- Argument Parsing ---");
  106 +$n_tests += 3;
  107 +
  108 +$td->runtest("required argument",
  109 + {$td->COMMAND => "qpdf --password minimal.pdf"},
  110 + {$td->REGEXP => "must be given as --password=pass",
  111 + $td->EXIT_STATUS => 2},
  112 + $td->NORMALIZE_NEWLINES);
  113 +$td->runtest("required argument with choices",
  114 + {$td->COMMAND => "qpdf --decode-level minimal.pdf"},
  115 + {$td->REGEXP => "must be given as --decode-level=\\{.*all.*\\}",
  116 + $td->EXIT_STATUS => 2},
  117 + $td->NORMALIZE_NEWLINES);
  118 +$td->runtest("required argument with choices",
  119 + {$td->COMMAND => "qpdf --decode-level minimal.pdf"},
  120 + {$td->REGEXP => "must be given as --decode-level=\\{.*all.*\\}",
  121 + $td->EXIT_STATUS => 2},
  122 + $td->NORMALIZE_NEWLINES);
  123 +
  124 +show_ntests();
  125 +# ----------
105 $td->notify("--- Form Tests ---"); 126 $td->notify("--- Form Tests ---");
106 127
107 my @form_tests = ( 128 my @form_tests = (