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 256 void parseOptions();
257 257  
258 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 333 void usage(std::string const& message);
  334 + void initOptionTable();
260 335 void handleHelpVersion();
261 336 void handleArgFileArguments();
262 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 340 void parseRotationParameter(std::string const&);
266 341 std::vector<int> parseNumrange(char const* range, int max,
267 342 bool throw_error = false);
... ... @@ -271,7 +346,9 @@ class ArgParser
271 346 int argc;
272 347 char** argv;
273 348 Options& o;
  349 + int cur_arg;
274 350  
  351 + std::map<std::string, OptionEntry> option_table;
275 352 std::vector<PointerHolder<char> > new_argv;
276 353 PointerHolder<char*> argv_ph;
277 354 };
... ... @@ -279,8 +356,525 @@ class ArgParser
279 356 ArgParser::ArgParser(int argc, char* argv[], Options& o) :
280 357 argc(argc),
281 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 880 void
... ... @@ -807,7 +1401,7 @@ ArgParser::parseNumrange(char const* range, int max, bool throw_error)
807 1401 }
808 1402  
809 1403 void
810   -ArgParser::parseEncryptOptions(int& cur_arg)
  1404 +ArgParser::parseEncryptOptions()
811 1405 {
812 1406 if (cur_arg + 3 >= argc)
813 1407 {
... ... @@ -1130,7 +1724,7 @@ ArgParser::parseEncryptOptions(int&amp; cur_arg)
1130 1724 }
1131 1725  
1132 1726 std::vector<PageSpec>
1133   -ArgParser::parsePagesOptions(int& cur_arg)
  1727 +ArgParser::parsePagesOptions()
1134 1728 {
1135 1729 std::vector<PageSpec> result;
1136 1730 while (1)
... ... @@ -1391,11 +1985,11 @@ ArgParser::parseRotationParameter(std::string const&amp; parameter)
1391 1985 void
1392 1986 ArgParser::parseOptions()
1393 1987 {
1394   - handleHelpVersion();
  1988 + handleHelpVersion(); // QXXXQ calls std::cout
1395 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 1993 if ((arg[0] == '-') && (strcmp(arg, "-") != 0))
1400 1994 {
1401 1995 ++arg;
... ... @@ -1410,381 +2004,64 @@ ArgParser::parseOptions()
1410 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 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 2066 else
1790 2067 {
... ... @@ -1827,7 +2104,8 @@ ArgParser::parseOptions()
1827 2104 if (QUtil::same_file(o.infilename, o.outfilename))
1828 2105 {
1829 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 367 QPDFOutlineDocumentHelper name named dest 0
368 368 QPDFOutlineDocumentHelper string named dest 0
369 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 102  
103 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 126 $td->notify("--- Form Tests ---");
106 127  
107 128 my @form_tests = (
... ...