Commit c60b4ea55a7d0844a81417d004f32c1b5f9f9df0
1 parent
52817f0a
Refactor arg parsing in qpdf.cc to use QPDFArgParser
Showing
3 changed files
with
391 additions
and
1014 deletions
qpdf/qpdf.cc
| @@ -27,6 +27,7 @@ | @@ -27,6 +27,7 @@ | ||
| 27 | #include <qpdf/QPDFSystemError.hh> | 27 | #include <qpdf/QPDFSystemError.hh> |
| 28 | #include <qpdf/QPDFCryptoProvider.hh> | 28 | #include <qpdf/QPDFCryptoProvider.hh> |
| 29 | #include <qpdf/QPDFEmbeddedFileDocumentHelper.hh> | 29 | #include <qpdf/QPDFEmbeddedFileDocumentHelper.hh> |
| 30 | +#include <qpdf/QPDFArgParser.hh> | ||
| 30 | 31 | ||
| 31 | #include <qpdf/QPDFWriter.hh> | 32 | #include <qpdf/QPDFWriter.hh> |
| 32 | #include <qpdf/QIntC.hh> | 33 | #include <qpdf/QIntC.hh> |
| @@ -740,14 +741,6 @@ static void parse_object_id(std::string const& objspec, | @@ -740,14 +741,6 @@ static void parse_object_id(std::string const& objspec, | ||
| 740 | } | 741 | } |
| 741 | } | 742 | } |
| 742 | 743 | ||
| 743 | -// This is not a general-purpose argument parser. It is tightly | ||
| 744 | -// crafted to work with qpdf. qpdf's command-line syntax is very | ||
| 745 | -// complex because of its long history, and it doesn't really follow | ||
| 746 | -// any kind of normal standard for arguments, but I don't want to | ||
| 747 | -// break compatibility by changing what constitutes a valid command. | ||
| 748 | -// This class is intended to simplify the argument parsing code and | ||
| 749 | -// also to make it possible to add bash completion support while | ||
| 750 | -// guaranteeing consistency with the actual argument syntax. | ||
| 751 | class ArgParser | 744 | class ArgParser |
| 752 | { | 745 | { |
| 753 | public: | 746 | public: |
| @@ -755,38 +748,18 @@ class ArgParser | @@ -755,38 +748,18 @@ class ArgParser | ||
| 755 | void parseOptions(); | 748 | void parseOptions(); |
| 756 | 749 | ||
| 757 | private: | 750 | private: |
| 758 | - typedef void (ArgParser::*bare_arg_handler_t)(); | ||
| 759 | - typedef void (ArgParser::*param_arg_handler_t)(char* parameter); | ||
| 760 | - | ||
| 761 | - struct OptionEntry | ||
| 762 | - { | ||
| 763 | - OptionEntry() : | ||
| 764 | - parameter_needed(false), | ||
| 765 | - bare_arg_handler(0), | ||
| 766 | - param_arg_handler(0) | ||
| 767 | - { | ||
| 768 | - } | ||
| 769 | - bool parameter_needed; | ||
| 770 | - std::string parameter_name; | ||
| 771 | - std::set<std::string> choices; | ||
| 772 | - bare_arg_handler_t bare_arg_handler; | ||
| 773 | - param_arg_handler_t param_arg_handler; | ||
| 774 | - }; | ||
| 775 | - friend struct OptionEntry; | ||
| 776 | - | ||
| 777 | - OptionEntry oe_positional(param_arg_handler_t); | ||
| 778 | - OptionEntry oe_bare(bare_arg_handler_t); | ||
| 779 | - OptionEntry oe_requiredParameter(param_arg_handler_t, char const* name); | ||
| 780 | - OptionEntry oe_optionalParameter(param_arg_handler_t); | ||
| 781 | - OptionEntry oe_requiredChoices(param_arg_handler_t, char const** choices); | ||
| 782 | - | ||
| 783 | - void completionCommon(bool zsh); | 751 | + static constexpr char const* O_PAGES = "pages"; |
| 752 | + static constexpr char const* O_ENCRYPT = "encryption"; | ||
| 753 | + static constexpr char const* O_ENCRYPT_40 = "40-bit encryption"; | ||
| 754 | + static constexpr char const* O_ENCRYPT_128 = "128-bit encryption"; | ||
| 755 | + static constexpr char const* O_ENCRYPT_256 = "256-bit encryption"; | ||
| 756 | + static constexpr char const* O_UNDER_OVERLAY = "underlay/overlay"; | ||
| 757 | + static constexpr char const* O_ATTACHMENT = "attachment"; | ||
| 758 | + static constexpr char const* O_COPY_ATTACHMENT = "copy attachment"; | ||
| 784 | 759 | ||
| 785 | void argHelp(); | 760 | void argHelp(); |
| 786 | void argVersion(); | 761 | void argVersion(); |
| 787 | void argCopyright(); | 762 | void argCopyright(); |
| 788 | - void argCompletionBash(); | ||
| 789 | - void argCompletionZsh(); | ||
| 790 | void argJsonHelp(); | 763 | void argJsonHelp(); |
| 791 | void argShowCrypto(); | 764 | void argShowCrypto(); |
| 792 | void argPositional(char* arg); | 765 | void argPositional(char* arg); |
| @@ -804,6 +777,9 @@ class ArgParser | @@ -804,6 +777,9 @@ class ArgParser | ||
| 804 | void argCopyEncryption(char* parameter); | 777 | void argCopyEncryption(char* parameter); |
| 805 | void argEncryptionFilePassword(char* parameter); | 778 | void argEncryptionFilePassword(char* parameter); |
| 806 | void argPages(); | 779 | void argPages(); |
| 780 | + void argPagesPassword(char* parameter); | ||
| 781 | + void argPagesPositional(char* parameter); | ||
| 782 | + void argEndPages(); | ||
| 807 | void argUnderlay(); | 783 | void argUnderlay(); |
| 808 | void argOverlay(); | 784 | void argOverlay(); |
| 809 | void argRotate(char* parameter); | 785 | void argRotate(char* parameter); |
| @@ -884,6 +860,7 @@ class ArgParser | @@ -884,6 +860,7 @@ class ArgParser | ||
| 884 | void arg128UseAes(char* parameter); | 860 | void arg128UseAes(char* parameter); |
| 885 | void arg128ForceV4(); | 861 | void arg128ForceV4(); |
| 886 | void arg256ForceR5(); | 862 | void arg256ForceR5(); |
| 863 | + void argEncryptPositional(char* arg); | ||
| 887 | void argEndEncrypt(); | 864 | void argEndEncrypt(); |
| 888 | void argUOpositional(char* arg); | 865 | void argUOpositional(char* arg); |
| 889 | void argUOto(char* parameter); | 866 | void argUOto(char* parameter); |
| @@ -909,329 +886,257 @@ class ArgParser | @@ -909,329 +886,257 @@ class ArgParser | ||
| 909 | void argEndCopyAttachments(); | 886 | void argEndCopyAttachments(); |
| 910 | 887 | ||
| 911 | void usage(std::string const& message); | 888 | void usage(std::string const& message); |
| 912 | - void checkCompletion(); | ||
| 913 | void initOptionTable(); | 889 | void initOptionTable(); |
| 914 | - void handleArgFileArguments(); | ||
| 915 | - void handleBashArguments(); | ||
| 916 | - void readArgsFromFile(char const* filename); | ||
| 917 | void doFinalChecks(); | 890 | void doFinalChecks(); |
| 918 | - void addOptionsToCompletions(); | ||
| 919 | - void addChoicesToCompletions(std::string const&, std::string const&); | ||
| 920 | - void handleCompletion(); | ||
| 921 | - std::vector<PageSpec> parsePagesOptions(); | ||
| 922 | void parseUnderOverlayOptions(UnderOverlay*); | 891 | void parseUnderOverlayOptions(UnderOverlay*); |
| 923 | void parseRotationParameter(std::string const&); | 892 | void parseRotationParameter(std::string const&); |
| 924 | std::vector<int> parseNumrange(char const* range, int max, | 893 | std::vector<int> parseNumrange(char const* range, int max, |
| 925 | bool throw_error = false); | 894 | bool throw_error = false); |
| 926 | 895 | ||
| 927 | - int argc; | ||
| 928 | - char** argv; | 896 | + QPDFArgParser ap; |
| 929 | Options& o; | 897 | Options& o; |
| 930 | - int cur_arg; | ||
| 931 | - bool bash_completion; | ||
| 932 | - bool zsh_completion; | ||
| 933 | - std::string bash_prev; | ||
| 934 | - std::string bash_cur; | ||
| 935 | - std::string bash_line; | ||
| 936 | - std::set<std::string> completions; | ||
| 937 | - | ||
| 938 | - std::map<std::string, OptionEntry>* option_table; | ||
| 939 | - std::map<std::string, OptionEntry> help_option_table; | ||
| 940 | - std::map<std::string, OptionEntry> main_option_table; | ||
| 941 | - std::map<std::string, OptionEntry> encrypt40_option_table; | ||
| 942 | - std::map<std::string, OptionEntry> encrypt128_option_table; | ||
| 943 | - std::map<std::string, OptionEntry> encrypt256_option_table; | ||
| 944 | - std::map<std::string, OptionEntry> under_overlay_option_table; | ||
| 945 | - std::map<std::string, OptionEntry> add_attachment_option_table; | ||
| 946 | - std::map<std::string, OptionEntry> copy_attachments_option_table; | ||
| 947 | - std::vector<PointerHolder<char> > new_argv; | ||
| 948 | - std::vector<PointerHolder<char> > bash_argv; | ||
| 949 | - PointerHolder<char*> argv_ph; | ||
| 950 | - PointerHolder<char*> bash_argv_ph; | 898 | + std::vector<char*> accumulated_args; |
| 899 | + char* pages_password; | ||
| 951 | }; | 900 | }; |
| 952 | 901 | ||
| 953 | ArgParser::ArgParser(int argc, char* argv[], Options& o) : | 902 | ArgParser::ArgParser(int argc, char* argv[], Options& o) : |
| 954 | - argc(argc), | ||
| 955 | - argv(argv), | 903 | + ap(argc, argv, "QPDF_EXECUTABLE"), |
| 956 | o(o), | 904 | o(o), |
| 957 | - cur_arg(0), | ||
| 958 | - bash_completion(false), | ||
| 959 | - zsh_completion(false) | 905 | + pages_password(nullptr) |
| 960 | { | 906 | { |
| 961 | - option_table = &main_option_table; | ||
| 962 | initOptionTable(); | 907 | initOptionTable(); |
| 963 | } | 908 | } |
| 964 | 909 | ||
| 965 | -ArgParser::OptionEntry | ||
| 966 | -ArgParser::oe_positional(param_arg_handler_t h) | ||
| 967 | -{ | ||
| 968 | - OptionEntry oe; | ||
| 969 | - oe.param_arg_handler = h; | ||
| 970 | - return oe; | ||
| 971 | -} | ||
| 972 | - | ||
| 973 | -ArgParser::OptionEntry | ||
| 974 | -ArgParser::oe_bare(bare_arg_handler_t h) | ||
| 975 | -{ | ||
| 976 | - OptionEntry oe; | ||
| 977 | - oe.parameter_needed = false; | ||
| 978 | - oe.bare_arg_handler = h; | ||
| 979 | - return oe; | ||
| 980 | -} | ||
| 981 | - | ||
| 982 | -ArgParser::OptionEntry | ||
| 983 | -ArgParser::oe_requiredParameter(param_arg_handler_t h, char const* name) | ||
| 984 | -{ | ||
| 985 | - OptionEntry oe; | ||
| 986 | - oe.parameter_needed = true; | ||
| 987 | - oe.parameter_name = name; | ||
| 988 | - oe.param_arg_handler = h; | ||
| 989 | - return oe; | ||
| 990 | -} | ||
| 991 | - | ||
| 992 | -ArgParser::OptionEntry | ||
| 993 | -ArgParser::oe_optionalParameter(param_arg_handler_t h) | ||
| 994 | -{ | ||
| 995 | - OptionEntry oe; | ||
| 996 | - oe.parameter_needed = false; | ||
| 997 | - oe.param_arg_handler = h; | ||
| 998 | - return oe; | ||
| 999 | -} | ||
| 1000 | - | ||
| 1001 | -ArgParser::OptionEntry | ||
| 1002 | -ArgParser::oe_requiredChoices(param_arg_handler_t h, char const** choices) | ||
| 1003 | -{ | ||
| 1004 | - OptionEntry oe; | ||
| 1005 | - oe.parameter_needed = true; | ||
| 1006 | - oe.param_arg_handler = h; | ||
| 1007 | - for (char const** i = choices; *i; ++i) | ||
| 1008 | - { | ||
| 1009 | - oe.choices.insert(*i); | ||
| 1010 | - } | ||
| 1011 | - return oe; | ||
| 1012 | -} | ||
| 1013 | - | ||
| 1014 | void | 910 | void |
| 1015 | ArgParser::initOptionTable() | 911 | ArgParser::initOptionTable() |
| 1016 | { | 912 | { |
| 1017 | - std::map<std::string, OptionEntry>* t = &this->help_option_table; | ||
| 1018 | - (*t)["help"] = oe_bare(&ArgParser::argHelp); | ||
| 1019 | - (*t)["version"] = oe_bare(&ArgParser::argVersion); | ||
| 1020 | - (*t)["copyright"] = oe_bare(&ArgParser::argCopyright); | ||
| 1021 | - (*t)["completion-bash"] = oe_bare(&ArgParser::argCompletionBash); | ||
| 1022 | - (*t)["completion-zsh"] = oe_bare(&ArgParser::argCompletionZsh); | ||
| 1023 | - (*t)["json-help"] = oe_bare(&ArgParser::argJsonHelp); | ||
| 1024 | - (*t)["show-crypto"] = oe_bare(&ArgParser::argShowCrypto); | 913 | + auto b = [this](void (ArgParser::*f)()) { |
| 914 | + return QPDFArgParser::bindBare(f, this); | ||
| 915 | + }; | ||
| 916 | + auto p = [this](void (ArgParser::*f)(char *)) { | ||
| 917 | + return QPDFArgParser::bindParam(f, this); | ||
| 918 | + }; | ||
| 919 | + | ||
| 920 | + this->ap.addFinalCheck(b(&ArgParser::doFinalChecks)); | ||
| 1025 | 921 | ||
| 1026 | - t = &this->main_option_table; | 922 | + this->ap.selectHelpOptionTable(); |
| 923 | + this->ap.addBare("help", b(&ArgParser::argHelp)); | ||
| 924 | + this->ap.addBare("version", b(&ArgParser::argVersion)); | ||
| 925 | + this->ap.addBare("copyright", b(&ArgParser::argCopyright)); | ||
| 926 | + this->ap.addBare("json-help", b(&ArgParser::argJsonHelp)); | ||
| 927 | + this->ap.addBare("show-crypto", b(&ArgParser::argShowCrypto)); | ||
| 928 | + | ||
| 929 | + this->ap.selectMainOptionTable(); | ||
| 1027 | char const* yn[] = {"y", "n", 0}; | 930 | char const* yn[] = {"y", "n", 0}; |
| 1028 | - (*t)[""] = oe_positional(&ArgParser::argPositional); | ||
| 1029 | - (*t)["password"] = oe_requiredParameter( | ||
| 1030 | - &ArgParser::argPassword, "password"); | ||
| 1031 | - (*t)["password-file"] = oe_requiredParameter( | ||
| 1032 | - &ArgParser::argPasswordFile, "password-file"); | ||
| 1033 | - (*t)["empty"] = oe_bare(&ArgParser::argEmpty); | ||
| 1034 | - (*t)["linearize"] = oe_bare(&ArgParser::argLinearize); | ||
| 1035 | - (*t)["encrypt"] = oe_bare(&ArgParser::argEncrypt); | ||
| 1036 | - (*t)["decrypt"] = oe_bare(&ArgParser::argDecrypt); | ||
| 1037 | - (*t)["password-is-hex-key"] = oe_bare(&ArgParser::argPasswordIsHexKey); | ||
| 1038 | - (*t)["suppress-password-recovery"] = | ||
| 1039 | - oe_bare(&ArgParser::argSuppressPasswordRecovery); | 931 | + this->ap.addPositional(p(&ArgParser::argPositional)); |
| 932 | + this->ap.addRequiredParameter("password", | ||
| 933 | + p(&ArgParser::argPassword), "password"); | ||
| 934 | + this->ap.addRequiredParameter("password-file", | ||
| 935 | + p(&ArgParser::argPasswordFile), "password-file"); | ||
| 936 | + this->ap.addBare("empty", b(&ArgParser::argEmpty)); | ||
| 937 | + this->ap.addBare("linearize", b(&ArgParser::argLinearize)); | ||
| 938 | + this->ap.addBare("decrypt", b(&ArgParser::argDecrypt)); | ||
| 939 | + this->ap.addBare("password-is-hex-key", b(&ArgParser::argPasswordIsHexKey)); | ||
| 940 | + this->ap.addBare("suppress-password-recovery", | ||
| 941 | + b(&ArgParser::argSuppressPasswordRecovery)); | ||
| 1040 | char const* password_mode_choices[] = | 942 | char const* password_mode_choices[] = |
| 1041 | {"bytes", "hex-bytes", "unicode", "auto", 0}; | 943 | {"bytes", "hex-bytes", "unicode", "auto", 0}; |
| 1042 | - (*t)["password-mode"] = oe_requiredChoices( | ||
| 1043 | - &ArgParser::argPasswordMode, password_mode_choices); | ||
| 1044 | - (*t)["copy-encryption"] = oe_requiredParameter( | ||
| 1045 | - &ArgParser::argCopyEncryption, "file"); | ||
| 1046 | - (*t)["encryption-file-password"] = oe_requiredParameter( | ||
| 1047 | - &ArgParser::argEncryptionFilePassword, "password"); | ||
| 1048 | - (*t)["pages"] = oe_bare(&ArgParser::argPages); | ||
| 1049 | - (*t)["rotate"] = oe_requiredParameter( | ||
| 1050 | - &ArgParser::argRotate, "[+|-]angle:page-range"); | 944 | + this->ap.addRequiredChoices("password-mode", |
| 945 | + p(&ArgParser::argPasswordMode), password_mode_choices); | ||
| 946 | + this->ap.addRequiredParameter("copy-encryption", | ||
| 947 | + p(&ArgParser::argCopyEncryption), "file"); | ||
| 948 | + this->ap.addRequiredParameter("encryption-file-password", | ||
| 949 | + p(&ArgParser::argEncryptionFilePassword), "password"); | ||
| 950 | + this->ap.addRequiredParameter("rotate", | ||
| 951 | + p(&ArgParser::argRotate), "[+|-]angle:page-range"); | ||
| 1051 | char const* stream_data_choices[] = | 952 | char const* stream_data_choices[] = |
| 1052 | {"compress", "preserve", "uncompress", 0}; | 953 | {"compress", "preserve", "uncompress", 0}; |
| 1053 | - (*t)["collate"] = oe_optionalParameter(&ArgParser::argCollate); | ||
| 1054 | - (*t)["flatten-rotation"] = oe_bare(&ArgParser::argFlattenRotation); | ||
| 1055 | - (*t)["list-attachments"] = oe_bare(&ArgParser::argListAttachments); | ||
| 1056 | - (*t)["show-attachment"] = oe_requiredParameter( | ||
| 1057 | - &ArgParser::argShowAttachment, "attachment-key"); | ||
| 1058 | - (*t)["remove-attachment"] = oe_requiredParameter( | ||
| 1059 | - &ArgParser::argRemoveAttachment, "attachment-key"); | ||
| 1060 | - (*t)["add-attachment"] = oe_bare(&ArgParser::argAddAttachment); | ||
| 1061 | - (*t)["copy-attachments-from"] = oe_bare(&ArgParser::argCopyAttachments); | ||
| 1062 | - (*t)["stream-data"] = oe_requiredChoices( | ||
| 1063 | - &ArgParser::argStreamData, stream_data_choices); | ||
| 1064 | - (*t)["compress-streams"] = oe_requiredChoices( | ||
| 1065 | - &ArgParser::argCompressStreams, yn); | ||
| 1066 | - (*t)["recompress-flate"] = oe_bare(&ArgParser::argRecompressFlate); | ||
| 1067 | - (*t)["compression-level"] = oe_requiredParameter( | ||
| 1068 | - &ArgParser::argCompressionLevel, "level"); | 954 | + this->ap.addOptionalParameter("collate",p(&ArgParser::argCollate)); |
| 955 | + this->ap.addBare("flatten-rotation", b(&ArgParser::argFlattenRotation)); | ||
| 956 | + this->ap.addBare("list-attachments", b(&ArgParser::argListAttachments)); | ||
| 957 | + this->ap.addRequiredParameter("show-attachment", | ||
| 958 | + p(&ArgParser::argShowAttachment), "attachment-key"); | ||
| 959 | + this->ap.addRequiredParameter("remove-attachment", | ||
| 960 | + p(&ArgParser::argRemoveAttachment), "attachment-key"); | ||
| 961 | + this->ap.addBare("add-attachment", b(&ArgParser::argAddAttachment)); | ||
| 962 | + this->ap.addBare( | ||
| 963 | + "copy-attachments-from", b(&ArgParser::argCopyAttachments)); | ||
| 964 | + this->ap.addRequiredChoices("stream-data", | ||
| 965 | + p(&ArgParser::argStreamData), stream_data_choices); | ||
| 966 | + this->ap.addRequiredChoices("compress-streams", | ||
| 967 | + p(&ArgParser::argCompressStreams), yn); | ||
| 968 | + this->ap.addBare("recompress-flate", b(&ArgParser::argRecompressFlate)); | ||
| 969 | + this->ap.addRequiredParameter("compression-level", | ||
| 970 | + p(&ArgParser::argCompressionLevel), "level"); | ||
| 1069 | char const* decode_level_choices[] = | 971 | char const* decode_level_choices[] = |
| 1070 | {"none", "generalized", "specialized", "all", 0}; | 972 | {"none", "generalized", "specialized", "all", 0}; |
| 1071 | - (*t)["decode-level"] = oe_requiredChoices( | ||
| 1072 | - &ArgParser::argDecodeLevel, decode_level_choices); | ||
| 1073 | - (*t)["normalize-content"] = oe_requiredChoices( | ||
| 1074 | - &ArgParser::argNormalizeContent, yn); | ||
| 1075 | - (*t)["suppress-recovery"] = oe_bare(&ArgParser::argSuppressRecovery); | 973 | + this->ap.addRequiredChoices("decode-level", |
| 974 | + p(&ArgParser::argDecodeLevel), decode_level_choices); | ||
| 975 | + this->ap.addRequiredChoices("normalize-content", | ||
| 976 | + p(&ArgParser::argNormalizeContent), yn); | ||
| 977 | + this->ap.addBare("suppress-recovery", b(&ArgParser::argSuppressRecovery)); | ||
| 1076 | char const* object_streams_choices[] = { | 978 | char const* object_streams_choices[] = { |
| 1077 | "disable", "preserve", "generate", 0}; | 979 | "disable", "preserve", "generate", 0}; |
| 1078 | - (*t)["object-streams"] = oe_requiredChoices( | ||
| 1079 | - &ArgParser::argObjectStreams, object_streams_choices); | ||
| 1080 | - (*t)["ignore-xref-streams"] = oe_bare(&ArgParser::argIgnoreXrefStreams); | ||
| 1081 | - (*t)["qdf"] = oe_bare(&ArgParser::argQdf); | ||
| 1082 | - (*t)["preserve-unreferenced"] = oe_bare( | ||
| 1083 | - &ArgParser::argPreserveUnreferenced); | ||
| 1084 | - (*t)["preserve-unreferenced-resources"] = oe_bare( | ||
| 1085 | - &ArgParser::argPreserveUnreferencedResources); | 980 | + this->ap.addRequiredChoices("object-streams", |
| 981 | + p(&ArgParser::argObjectStreams), object_streams_choices); | ||
| 982 | + this->ap.addBare( | ||
| 983 | + "ignore-xref-streams", b(&ArgParser::argIgnoreXrefStreams)); | ||
| 984 | + this->ap.addBare("qdf", b(&ArgParser::argQdf)); | ||
| 985 | + this->ap.addBare( | ||
| 986 | + "preserve-unreferenced", b(&ArgParser::argPreserveUnreferenced)); | ||
| 987 | + this->ap.addBare( | ||
| 988 | + "preserve-unreferenced-resources", | ||
| 989 | + b(&ArgParser::argPreserveUnreferencedResources)); | ||
| 1086 | char const* remove_unref_choices[] = { | 990 | char const* remove_unref_choices[] = { |
| 1087 | "auto", "yes", "no", 0}; | 991 | "auto", "yes", "no", 0}; |
| 1088 | - (*t)["remove-unreferenced-resources"] = oe_requiredChoices( | ||
| 1089 | - &ArgParser::argRemoveUnreferencedResources, remove_unref_choices); | ||
| 1090 | - (*t)["keep-files-open"] = oe_requiredChoices( | ||
| 1091 | - &ArgParser::argKeepFilesOpen, yn); | ||
| 1092 | - (*t)["keep-files-open-threshold"] = oe_requiredParameter( | ||
| 1093 | - &ArgParser::argKeepFilesOpenThreshold, "count"); | ||
| 1094 | - (*t)["newline-before-endstream"] = oe_bare( | ||
| 1095 | - &ArgParser::argNewlineBeforeEndstream); | ||
| 1096 | - (*t)["linearize-pass1"] = oe_requiredParameter( | ||
| 1097 | - &ArgParser::argLinearizePass1, "filename"); | ||
| 1098 | - (*t)["coalesce-contents"] = oe_bare(&ArgParser::argCoalesceContents); | 992 | + this->ap.addRequiredChoices("remove-unreferenced-resources", |
| 993 | + p(&ArgParser::argRemoveUnreferencedResources), remove_unref_choices); | ||
| 994 | + this->ap.addRequiredChoices("keep-files-open", | ||
| 995 | + p(&ArgParser::argKeepFilesOpen), yn); | ||
| 996 | + this->ap.addRequiredParameter("keep-files-open-threshold", | ||
| 997 | + p(&ArgParser::argKeepFilesOpenThreshold), "count"); | ||
| 998 | + this->ap.addBare("newline-before-endstream", b(&ArgParser::argNewlineBeforeEndstream)); | ||
| 999 | + this->ap.addRequiredParameter("linearize-pass1", | ||
| 1000 | + p(&ArgParser::argLinearizePass1), "filename"); | ||
| 1001 | + this->ap.addBare("coalesce-contents", b(&ArgParser::argCoalesceContents)); | ||
| 1099 | char const* flatten_choices[] = {"all", "print", "screen", 0}; | 1002 | char const* flatten_choices[] = {"all", "print", "screen", 0}; |
| 1100 | - (*t)["flatten-annotations"] = oe_requiredChoices( | ||
| 1101 | - &ArgParser::argFlattenAnnotations, flatten_choices); | ||
| 1102 | - (*t)["generate-appearances"] = | ||
| 1103 | - oe_bare(&ArgParser::argGenerateAppearances); | ||
| 1104 | - (*t)["min-version"] = oe_requiredParameter( | ||
| 1105 | - &ArgParser::argMinVersion, "version"); | ||
| 1106 | - (*t)["force-version"] = oe_requiredParameter( | ||
| 1107 | - &ArgParser::argForceVersion, "version"); | ||
| 1108 | - (*t)["split-pages"] = oe_optionalParameter(&ArgParser::argSplitPages); | ||
| 1109 | - (*t)["verbose"] = oe_bare(&ArgParser::argVerbose); | ||
| 1110 | - (*t)["progress"] = oe_bare(&ArgParser::argProgress); | ||
| 1111 | - (*t)["no-warn"] = oe_bare(&ArgParser::argNoWarn); | ||
| 1112 | - (*t)["warning-exit-0"] = oe_bare(&ArgParser::argWarningExitZero); | ||
| 1113 | - (*t)["deterministic-id"] = oe_bare(&ArgParser::argDeterministicId); | ||
| 1114 | - (*t)["static-id"] = oe_bare(&ArgParser::argStaticId); | ||
| 1115 | - (*t)["static-aes-iv"] = oe_bare(&ArgParser::argStaticAesIv); | ||
| 1116 | - (*t)["no-original-object-ids"] = oe_bare( | ||
| 1117 | - &ArgParser::argNoOriginalObjectIds); | ||
| 1118 | - (*t)["show-encryption"] = oe_bare(&ArgParser::argShowEncryption); | ||
| 1119 | - (*t)["show-encryption-key"] = oe_bare(&ArgParser::argShowEncryptionKey); | ||
| 1120 | - (*t)["check-linearization"] = oe_bare(&ArgParser::argCheckLinearization); | ||
| 1121 | - (*t)["show-linearization"] = oe_bare(&ArgParser::argShowLinearization); | ||
| 1122 | - (*t)["show-xref"] = oe_bare(&ArgParser::argShowXref); | ||
| 1123 | - (*t)["show-object"] = oe_requiredParameter( | ||
| 1124 | - &ArgParser::argShowObject, "trailer|obj[,gen]"); | ||
| 1125 | - (*t)["raw-stream-data"] = oe_bare(&ArgParser::argRawStreamData); | ||
| 1126 | - (*t)["filtered-stream-data"] = oe_bare(&ArgParser::argFilteredStreamData); | ||
| 1127 | - (*t)["show-npages"] = oe_bare(&ArgParser::argShowNpages); | ||
| 1128 | - (*t)["show-pages"] = oe_bare(&ArgParser::argShowPages); | ||
| 1129 | - (*t)["with-images"] = oe_bare(&ArgParser::argWithImages); | ||
| 1130 | - (*t)["json"] = oe_bare(&ArgParser::argJson); | 1003 | + this->ap.addRequiredChoices("flatten-annotations", |
| 1004 | + p(&ArgParser::argFlattenAnnotations), flatten_choices); | ||
| 1005 | + this->ap.addBare("generate-appearances", b(&ArgParser::argGenerateAppearances)); | ||
| 1006 | + this->ap.addRequiredParameter("min-version", | ||
| 1007 | + p(&ArgParser::argMinVersion), "version"); | ||
| 1008 | + this->ap.addRequiredParameter("force-version", | ||
| 1009 | + p(&ArgParser::argForceVersion), "version"); | ||
| 1010 | + this->ap.addOptionalParameter("split-pages",p(&ArgParser::argSplitPages)); | ||
| 1011 | + this->ap.addBare("verbose", b(&ArgParser::argVerbose)); | ||
| 1012 | + this->ap.addBare("progress", b(&ArgParser::argProgress)); | ||
| 1013 | + this->ap.addBare("no-warn", b(&ArgParser::argNoWarn)); | ||
| 1014 | + this->ap.addBare("warning-exit-0", b(&ArgParser::argWarningExitZero)); | ||
| 1015 | + this->ap.addBare("deterministic-id", b(&ArgParser::argDeterministicId)); | ||
| 1016 | + this->ap.addBare("static-id", b(&ArgParser::argStaticId)); | ||
| 1017 | + this->ap.addBare("static-aes-iv", b(&ArgParser::argStaticAesIv)); | ||
| 1018 | + this->ap.addBare("no-original-object-ids", b(&ArgParser::argNoOriginalObjectIds)); | ||
| 1019 | + this->ap.addBare("show-encryption", b(&ArgParser::argShowEncryption)); | ||
| 1020 | + this->ap.addBare("show-encryption-key", b(&ArgParser::argShowEncryptionKey)); | ||
| 1021 | + this->ap.addBare("check-linearization", b(&ArgParser::argCheckLinearization)); | ||
| 1022 | + this->ap.addBare("show-linearization", b(&ArgParser::argShowLinearization)); | ||
| 1023 | + this->ap.addBare("show-xref", b(&ArgParser::argShowXref)); | ||
| 1024 | + this->ap.addRequiredParameter("show-object", | ||
| 1025 | + p(&ArgParser::argShowObject), "trailer|obj[,gen]"); | ||
| 1026 | + this->ap.addBare("raw-stream-data", b(&ArgParser::argRawStreamData)); | ||
| 1027 | + this->ap.addBare("filtered-stream-data", b(&ArgParser::argFilteredStreamData)); | ||
| 1028 | + this->ap.addBare("show-npages", b(&ArgParser::argShowNpages)); | ||
| 1029 | + this->ap.addBare("show-pages", b(&ArgParser::argShowPages)); | ||
| 1030 | + this->ap.addBare("with-images", b(&ArgParser::argWithImages)); | ||
| 1031 | + this->ap.addBare("json", b(&ArgParser::argJson)); | ||
| 1131 | // The list of selectable top-level keys id duplicated in three | 1032 | // The list of selectable top-level keys id duplicated in three |
| 1132 | // places: json_schema, do_json, and initOptionTable. | 1033 | // places: json_schema, do_json, and initOptionTable. |
| 1133 | char const* json_key_choices[] = { | 1034 | char const* json_key_choices[] = { |
| 1134 | "objects", "objectinfo", "pages", "pagelabels", "outlines", | 1035 | "objects", "objectinfo", "pages", "pagelabels", "outlines", |
| 1135 | "acroform", "encrypt", "attachments", 0}; | 1036 | "acroform", "encrypt", "attachments", 0}; |
| 1136 | - (*t)["json-key"] = oe_requiredChoices( | ||
| 1137 | - &ArgParser::argJsonKey, json_key_choices); | ||
| 1138 | - (*t)["json-object"] = oe_requiredParameter( | ||
| 1139 | - &ArgParser::argJsonObject, "trailer|obj[,gen]"); | ||
| 1140 | - (*t)["check"] = oe_bare(&ArgParser::argCheck); | ||
| 1141 | - (*t)["optimize-images"] = oe_bare(&ArgParser::argOptimizeImages); | ||
| 1142 | - (*t)["externalize-inline-images"] = | ||
| 1143 | - oe_bare(&ArgParser::argExternalizeInlineImages); | ||
| 1144 | - (*t)["keep-inline-images"] = oe_bare(&ArgParser::argKeepInlineImages); | ||
| 1145 | - (*t)["remove-page-labels"] = oe_bare(&ArgParser::argRemovePageLabels); | ||
| 1146 | - (*t)["oi-min-width"] = oe_requiredParameter( | ||
| 1147 | - &ArgParser::argOiMinWidth, "minimum-width"); | ||
| 1148 | - (*t)["oi-min-height"] = oe_requiredParameter( | ||
| 1149 | - &ArgParser::argOiMinHeight, "minimum-height"); | ||
| 1150 | - (*t)["oi-min-area"] = oe_requiredParameter( | ||
| 1151 | - &ArgParser::argOiMinArea, "minimum-area"); | ||
| 1152 | - (*t)["ii-min-bytes"] = oe_requiredParameter( | ||
| 1153 | - &ArgParser::argIiMinBytes, "minimum-bytes"); | ||
| 1154 | - (*t)["overlay"] = oe_bare(&ArgParser::argOverlay); | ||
| 1155 | - (*t)["underlay"] = oe_bare(&ArgParser::argUnderlay); | ||
| 1156 | - (*t)["replace-input"] = oe_bare(&ArgParser::argReplaceInput); | ||
| 1157 | - (*t)["is-encrypted"] = oe_bare(&ArgParser::argIsEncrypted); | ||
| 1158 | - (*t)["requires-password"] = oe_bare(&ArgParser::argRequiresPassword); | ||
| 1159 | - (*t)["allow-weak-crypto"] = oe_bare(&ArgParser::argAllowWeakCrypto); | ||
| 1160 | - | ||
| 1161 | - t = &this->encrypt40_option_table; | ||
| 1162 | - (*t)["--"] = oe_bare(&ArgParser::argEndEncrypt); | ||
| 1163 | - // The above 40-bit options are also 128-bit and 256-bit options, | ||
| 1164 | - // so copy what we have so far to 128. Then continue separately | ||
| 1165 | - // with 128. We later copy 128 to 256. | ||
| 1166 | - this->encrypt128_option_table = this->encrypt40_option_table; | ||
| 1167 | - (*t)["print"] = oe_requiredChoices(&ArgParser::arg40Print, yn); | ||
| 1168 | - (*t)["modify"] = oe_requiredChoices(&ArgParser::arg40Modify, yn); | ||
| 1169 | - (*t)["extract"] = oe_requiredChoices(&ArgParser::arg40Extract, yn); | ||
| 1170 | - (*t)["annotate"] = oe_requiredChoices(&ArgParser::arg40Annotate, yn); | ||
| 1171 | - | ||
| 1172 | - t = &this->encrypt128_option_table; | ||
| 1173 | - (*t)["accessibility"] = oe_requiredChoices( | ||
| 1174 | - &ArgParser::arg128Accessibility, yn); | ||
| 1175 | - (*t)["extract"] = oe_requiredChoices(&ArgParser::arg128Extract, yn); | ||
| 1176 | - char const* print128_choices[] = {"full", "low", "none", 0}; | ||
| 1177 | - (*t)["print"] = oe_requiredChoices( | ||
| 1178 | - &ArgParser::arg128Print, print128_choices); | ||
| 1179 | - (*t)["assemble"] = oe_requiredChoices(&ArgParser::arg128Assemble, yn); | ||
| 1180 | - (*t)["annotate"] = oe_requiredChoices(&ArgParser::arg128Annotate, yn); | ||
| 1181 | - (*t)["form"] = oe_requiredChoices(&ArgParser::arg128Form, yn); | ||
| 1182 | - (*t)["modify-other"] = oe_requiredChoices(&ArgParser::arg128ModOther, yn); | ||
| 1183 | - char const* modify128_choices[] = | ||
| 1184 | - {"all", "annotate", "form", "assembly", "none", 0}; | ||
| 1185 | - (*t)["modify"] = oe_requiredChoices( | ||
| 1186 | - &ArgParser::arg128Modify, modify128_choices); | ||
| 1187 | - (*t)["cleartext-metadata"] = oe_bare(&ArgParser::arg128ClearTextMetadata); | ||
| 1188 | - | ||
| 1189 | - // The above 128-bit options are also 256-bit options, so copy | ||
| 1190 | - // what we have so far. Then continue separately with 128 and 256. | ||
| 1191 | - this->encrypt256_option_table = this->encrypt128_option_table; | ||
| 1192 | - (*t)["use-aes"] = oe_requiredChoices(&ArgParser::arg128UseAes, yn); | ||
| 1193 | - (*t)["force-V4"] = oe_bare(&ArgParser::arg128ForceV4); | ||
| 1194 | - | ||
| 1195 | - t = &this->encrypt256_option_table; | ||
| 1196 | - (*t)["force-R5"] = oe_bare(&ArgParser::arg256ForceR5); | ||
| 1197 | - (*t)["allow-insecure"] = oe_bare(&ArgParser::argAllowInsecure); | ||
| 1198 | - | ||
| 1199 | - t = &this->under_overlay_option_table; | ||
| 1200 | - (*t)[""] = oe_positional(&ArgParser::argUOpositional); | ||
| 1201 | - (*t)["to"] = oe_requiredParameter( | ||
| 1202 | - &ArgParser::argUOto, "page-range"); | ||
| 1203 | - (*t)["from"] = oe_requiredParameter( | ||
| 1204 | - &ArgParser::argUOfrom, "page-range"); | ||
| 1205 | - (*t)["repeat"] = oe_requiredParameter( | ||
| 1206 | - &ArgParser::argUOrepeat, "page-range"); | ||
| 1207 | - (*t)["password"] = oe_requiredParameter( | ||
| 1208 | - &ArgParser::argUOpassword, "password"); | ||
| 1209 | - (*t)["--"] = oe_bare(&ArgParser::argEndUnderOverlay); | ||
| 1210 | - | ||
| 1211 | - t = &this->add_attachment_option_table; | ||
| 1212 | - (*t)[""] = oe_positional(&ArgParser::argAApositional); | ||
| 1213 | - (*t)["key"] = oe_requiredParameter( | ||
| 1214 | - &ArgParser::argAAKey, "attachment-key"); | ||
| 1215 | - (*t)["filename"] = oe_requiredParameter( | ||
| 1216 | - &ArgParser::argAAFilename, "filename"); | ||
| 1217 | - (*t)["creationdate"] = oe_requiredParameter( | ||
| 1218 | - &ArgParser::argAACreationDate, "creation-date"); | ||
| 1219 | - (*t)["moddate"] = oe_requiredParameter( | ||
| 1220 | - &ArgParser::argAAModDate, "modification-date"); | ||
| 1221 | - (*t)["mimetype"] = oe_requiredParameter( | ||
| 1222 | - &ArgParser::argAAMimeType, "mime/type"); | ||
| 1223 | - (*t)["description"] = oe_requiredParameter( | ||
| 1224 | - &ArgParser::argAADescription, "description"); | ||
| 1225 | - (*t)["replace"] = oe_bare(&ArgParser::argAAReplace); | ||
| 1226 | - (*t)["--"] = oe_bare(&ArgParser::argEndAddAttachment); | ||
| 1227 | - | ||
| 1228 | - t = &this->copy_attachments_option_table; | ||
| 1229 | - (*t)[""] = oe_positional(&ArgParser::argCApositional); | ||
| 1230 | - (*t)["prefix"] = oe_requiredParameter( | ||
| 1231 | - &ArgParser::argCAprefix, "prefix"); | ||
| 1232 | - (*t)["password"] = oe_requiredParameter( | ||
| 1233 | - &ArgParser::argCApassword, "password"); | ||
| 1234 | - (*t)["--"] = oe_bare(&ArgParser::argEndCopyAttachments); | 1037 | + this->ap.addRequiredChoices("json-key", |
| 1038 | + p(&ArgParser::argJsonKey), json_key_choices); | ||
| 1039 | + this->ap.addRequiredParameter("json-object", | ||
| 1040 | + p(&ArgParser::argJsonObject), "trailer|obj[,gen]"); | ||
| 1041 | + this->ap.addBare("check", b(&ArgParser::argCheck)); | ||
| 1042 | + this->ap.addBare("optimize-images", b(&ArgParser::argOptimizeImages)); | ||
| 1043 | + this->ap.addBare("externalize-inline-images", b(&ArgParser::argExternalizeInlineImages)); | ||
| 1044 | + this->ap.addBare("keep-inline-images", b(&ArgParser::argKeepInlineImages)); | ||
| 1045 | + this->ap.addBare("remove-page-labels", b(&ArgParser::argRemovePageLabels)); | ||
| 1046 | + this->ap.addRequiredParameter("oi-min-width", | ||
| 1047 | + p(&ArgParser::argOiMinWidth), "minimum-width"); | ||
| 1048 | + this->ap.addRequiredParameter("oi-min-height", | ||
| 1049 | + p(&ArgParser::argOiMinHeight), "minimum-height"); | ||
| 1050 | + this->ap.addRequiredParameter("oi-min-area", | ||
| 1051 | + p(&ArgParser::argOiMinArea), "minimum-area"); | ||
| 1052 | + this->ap.addRequiredParameter("ii-min-bytes", | ||
| 1053 | + p(&ArgParser::argIiMinBytes), "minimum-bytes"); | ||
| 1054 | + this->ap.addBare("overlay", b(&ArgParser::argOverlay)); | ||
| 1055 | + this->ap.addBare("underlay", b(&ArgParser::argUnderlay)); | ||
| 1056 | + this->ap.addBare("replace-input", b(&ArgParser::argReplaceInput)); | ||
| 1057 | + this->ap.addBare("is-encrypted", b(&ArgParser::argIsEncrypted)); | ||
| 1058 | + this->ap.addBare("requires-password", b(&ArgParser::argRequiresPassword)); | ||
| 1059 | + this->ap.addBare("allow-weak-crypto", b(&ArgParser::argAllowWeakCrypto)); | ||
| 1060 | + | ||
| 1061 | + this->ap.selectMainOptionTable(); | ||
| 1062 | + this->ap.addBare("pages", b(&ArgParser::argPages)); | ||
| 1063 | + this->ap.registerOptionTable(O_PAGES, b(&ArgParser::argEndPages)); | ||
| 1064 | + this->ap.addRequiredParameter( | ||
| 1065 | + "password", p(&ArgParser::argPagesPassword), "password"); | ||
| 1066 | + this->ap.addPositional(p(&ArgParser::argPagesPositional)); | ||
| 1067 | + | ||
| 1068 | + this->ap.selectMainOptionTable(); | ||
| 1069 | + this->ap.addBare("encrypt", b(&ArgParser::argEncrypt)); | ||
| 1070 | + this->ap.registerOptionTable(O_ENCRYPT, b(&ArgParser::argEndEncrypt)); | ||
| 1071 | + this->ap.addPositional(p(&ArgParser::argEncryptPositional)); | ||
| 1072 | + this->ap.registerOptionTable(O_ENCRYPT_40, b(&ArgParser::argEndEncrypt)); | ||
| 1073 | + this->ap.addRequiredChoices("extract",p(&ArgParser::arg40Extract), yn); | ||
| 1074 | + this->ap.addRequiredChoices("annotate",p(&ArgParser::arg40Annotate), yn); | ||
| 1075 | + this->ap.addRequiredChoices("print",p(&ArgParser::arg40Print), yn); | ||
| 1076 | + this->ap.addRequiredChoices("modify",p(&ArgParser::arg40Modify), yn); | ||
| 1077 | + this->ap.registerOptionTable(O_ENCRYPT_128, b(&ArgParser::argEndEncrypt)); | ||
| 1078 | + this->ap.registerOptionTable(O_ENCRYPT_256, b(&ArgParser::argEndEncrypt)); | ||
| 1079 | + for (char const* k: {O_ENCRYPT_128, O_ENCRYPT_256}) | ||
| 1080 | + { | ||
| 1081 | + this->ap.selectOptionTable(k); | ||
| 1082 | + this->ap.addRequiredChoices("accessibility", | ||
| 1083 | + p(&ArgParser::arg128Accessibility), yn); | ||
| 1084 | + this->ap.addRequiredChoices("extract", p(&ArgParser::arg128Extract), yn); | ||
| 1085 | + char const* print128_choices[] = {"full", "low", "none", 0}; | ||
| 1086 | + this->ap.addRequiredChoices("print", | ||
| 1087 | + p(&ArgParser::arg128Print), print128_choices); | ||
| 1088 | + this->ap.addRequiredChoices("assemble",p(&ArgParser::arg128Assemble), yn); | ||
| 1089 | + this->ap.addRequiredChoices("annotate",p(&ArgParser::arg128Annotate), yn); | ||
| 1090 | + this->ap.addRequiredChoices("form",p(&ArgParser::arg128Form), yn); | ||
| 1091 | + this->ap.addRequiredChoices("modify-other",p(&ArgParser::arg128ModOther), yn); | ||
| 1092 | + char const* modify128_choices[] = | ||
| 1093 | + {"all", "annotate", "form", "assembly", "none", 0}; | ||
| 1094 | + this->ap.addRequiredChoices("modify", | ||
| 1095 | + p(&ArgParser::arg128Modify), modify128_choices); | ||
| 1096 | + this->ap.addBare("cleartext-metadata", b(&ArgParser::arg128ClearTextMetadata)); | ||
| 1097 | + } | ||
| 1098 | + | ||
| 1099 | + this->ap.selectOptionTable(O_ENCRYPT_128); | ||
| 1100 | + this->ap.addRequiredChoices("use-aes",p(&ArgParser::arg128UseAes), yn); | ||
| 1101 | + this->ap.addBare("force-V4", b(&ArgParser::arg128ForceV4)); | ||
| 1102 | + | ||
| 1103 | + this->ap.selectOptionTable(O_ENCRYPT_256); | ||
| 1104 | + this->ap.addBare("force-R5", b(&ArgParser::arg256ForceR5)); | ||
| 1105 | + this->ap.addBare("allow-insecure", b(&ArgParser::argAllowInsecure)); | ||
| 1106 | + | ||
| 1107 | + this->ap.registerOptionTable(O_UNDER_OVERLAY, b(&ArgParser::argEndUnderOverlay)); | ||
| 1108 | + this->ap.addPositional(p(&ArgParser::argUOpositional)); | ||
| 1109 | + this->ap.addRequiredParameter("to", | ||
| 1110 | + p(&ArgParser::argUOto), "page-range"); | ||
| 1111 | + this->ap.addRequiredParameter("from", | ||
| 1112 | + p(&ArgParser::argUOfrom), "page-range"); | ||
| 1113 | + this->ap.addRequiredParameter("repeat", | ||
| 1114 | + p(&ArgParser::argUOrepeat), "page-range"); | ||
| 1115 | + this->ap.addRequiredParameter("password", | ||
| 1116 | + p(&ArgParser::argUOpassword), "password"); | ||
| 1117 | + | ||
| 1118 | + this->ap.registerOptionTable(O_ATTACHMENT, b(&ArgParser::argEndAddAttachment)); | ||
| 1119 | + this->ap.addPositional(p(&ArgParser::argAApositional)); | ||
| 1120 | + this->ap.addRequiredParameter("key", | ||
| 1121 | + p(&ArgParser::argAAKey), "attachment-key"); | ||
| 1122 | + this->ap.addRequiredParameter("filename", | ||
| 1123 | + p(&ArgParser::argAAFilename), "filename"); | ||
| 1124 | + this->ap.addRequiredParameter("creationdate", | ||
| 1125 | + p(&ArgParser::argAACreationDate), "creation-date"); | ||
| 1126 | + this->ap.addRequiredParameter("moddate", | ||
| 1127 | + p(&ArgParser::argAAModDate), "modification-date"); | ||
| 1128 | + this->ap.addRequiredParameter("mimetype", | ||
| 1129 | + p(&ArgParser::argAAMimeType), "mime/type"); | ||
| 1130 | + this->ap.addRequiredParameter("description", | ||
| 1131 | + p(&ArgParser::argAADescription), "description"); | ||
| 1132 | + this->ap.addBare("replace", b(&ArgParser::argAAReplace)); | ||
| 1133 | + | ||
| 1134 | + this->ap.registerOptionTable(O_COPY_ATTACHMENT, b(&ArgParser::argEndCopyAttachments)); | ||
| 1135 | + this->ap.addPositional(p(&ArgParser::argCApositional)); | ||
| 1136 | + this->ap.addRequiredParameter("prefix", | ||
| 1137 | + p(&ArgParser::argCAprefix), "prefix"); | ||
| 1138 | + this->ap.addRequiredParameter("password", | ||
| 1139 | + p(&ArgParser::argCApassword), "password"); | ||
| 1235 | } | 1140 | } |
| 1236 | 1141 | ||
| 1237 | void | 1142 | void |
| @@ -1818,58 +1723,6 @@ ArgParser::argHelp() | @@ -1818,58 +1723,6 @@ ArgParser::argHelp() | ||
| 1818 | } | 1723 | } |
| 1819 | 1724 | ||
| 1820 | void | 1725 | void |
| 1821 | -ArgParser::completionCommon(bool zsh) | ||
| 1822 | -{ | ||
| 1823 | - std::string progname = argv[0]; | ||
| 1824 | - std::string executable; | ||
| 1825 | - std::string appdir; | ||
| 1826 | - std::string appimage; | ||
| 1827 | - if (QUtil::get_env("QPDF_EXECUTABLE", &executable)) | ||
| 1828 | - { | ||
| 1829 | - progname = executable; | ||
| 1830 | - } | ||
| 1831 | - else if (QUtil::get_env("APPDIR", &appdir) && | ||
| 1832 | - QUtil::get_env("APPIMAGE", &appimage)) | ||
| 1833 | - { | ||
| 1834 | - // Detect if we're in an AppImage and adjust | ||
| 1835 | - if ((appdir.length() < strlen(argv[0])) && | ||
| 1836 | - (strncmp(appdir.c_str(), argv[0], appdir.length()) == 0)) | ||
| 1837 | - { | ||
| 1838 | - progname = appimage; | ||
| 1839 | - } | ||
| 1840 | - } | ||
| 1841 | - if (zsh) | ||
| 1842 | - { | ||
| 1843 | - std::cout << "autoload -U +X bashcompinit && bashcompinit && "; | ||
| 1844 | - } | ||
| 1845 | - std::cout << "complete -o bashdefault -o default"; | ||
| 1846 | - if (! zsh) | ||
| 1847 | - { | ||
| 1848 | - std::cout << " -o nospace"; | ||
| 1849 | - } | ||
| 1850 | - std::cout << " -C " << progname << " " << whoami << std::endl; | ||
| 1851 | - // Put output before error so calling from zsh works properly | ||
| 1852 | - std::string path = progname; | ||
| 1853 | - size_t slash = path.find('/'); | ||
| 1854 | - if ((slash != 0) && (slash != std::string::npos)) | ||
| 1855 | - { | ||
| 1856 | - std::cerr << "WARNING: qpdf completion enabled" | ||
| 1857 | - << " using relative path to qpdf" << std::endl; | ||
| 1858 | - } | ||
| 1859 | -} | ||
| 1860 | - | ||
| 1861 | -void | ||
| 1862 | -ArgParser::argCompletionBash() | ||
| 1863 | -{ | ||
| 1864 | - completionCommon(false); | ||
| 1865 | -} | ||
| 1866 | - | ||
| 1867 | -void | ||
| 1868 | -ArgParser::argCompletionZsh() | ||
| 1869 | -{ | ||
| 1870 | - completionCommon(true); | ||
| 1871 | -} | ||
| 1872 | -void | ||
| 1873 | ArgParser::argJsonHelp() | 1726 | ArgParser::argJsonHelp() |
| 1874 | { | 1727 | { |
| 1875 | // Make sure the output looks right on an 80-column display. | 1728 | // Make sure the output looks right on an 80-column display. |
| @@ -1962,50 +1815,54 @@ ArgParser::argLinearize() | @@ -1962,50 +1815,54 @@ ArgParser::argLinearize() | ||
| 1962 | void | 1815 | void |
| 1963 | ArgParser::argEncrypt() | 1816 | ArgParser::argEncrypt() |
| 1964 | { | 1817 | { |
| 1965 | - ++cur_arg; | ||
| 1966 | - if (cur_arg + 3 > argc) | 1818 | + this->accumulated_args.clear(); |
| 1819 | + if (this->ap.isCompleting() && this->ap.argsLeft() == 0) | ||
| 1820 | + { | ||
| 1821 | + this->ap.insertCompletion("user-password"); | ||
| 1822 | + } | ||
| 1823 | + this->ap.selectOptionTable(O_ENCRYPT); | ||
| 1824 | +} | ||
| 1825 | + | ||
| 1826 | +void | ||
| 1827 | +ArgParser::argEncryptPositional(char* arg) | ||
| 1828 | +{ | ||
| 1829 | + this->accumulated_args.push_back(arg); | ||
| 1830 | + size_t n_args = this->accumulated_args.size(); | ||
| 1831 | + if (n_args < 3) | ||
| 1967 | { | 1832 | { |
| 1968 | - if (this->bash_completion) | 1833 | + if (this->ap.isCompleting() && (this->ap.argsLeft() == 0)) |
| 1969 | { | 1834 | { |
| 1970 | - if (cur_arg == argc) | ||
| 1971 | - { | ||
| 1972 | - this->completions.insert("user-password"); | ||
| 1973 | - } | ||
| 1974 | - else if (cur_arg + 1 == argc) | 1835 | + if (n_args == 1) |
| 1975 | { | 1836 | { |
| 1976 | - this->completions.insert("owner-password"); | 1837 | + this->ap.insertCompletion("owner-password"); |
| 1977 | } | 1838 | } |
| 1978 | - else if (cur_arg + 2 == argc) | 1839 | + else if (n_args == 2) |
| 1979 | { | 1840 | { |
| 1980 | - this->completions.insert("40"); | ||
| 1981 | - this->completions.insert("128"); | ||
| 1982 | - this->completions.insert("256"); | 1841 | + this->ap.insertCompletion("40"); |
| 1842 | + this->ap.insertCompletion("128"); | ||
| 1843 | + this->ap.insertCompletion("256"); | ||
| 1983 | } | 1844 | } |
| 1984 | - return; | ||
| 1985 | - } | ||
| 1986 | - else | ||
| 1987 | - { | ||
| 1988 | - usage("insufficient arguments to --encrypt"); | ||
| 1989 | } | 1845 | } |
| 1846 | + return; | ||
| 1990 | } | 1847 | } |
| 1991 | - o.user_password = argv[cur_arg++]; | ||
| 1992 | - o.owner_password = argv[cur_arg++]; | ||
| 1993 | - std::string len_str = argv[cur_arg]; | 1848 | + o.user_password = this->accumulated_args.at(0); |
| 1849 | + o.owner_password = this->accumulated_args.at(1); | ||
| 1850 | + std::string len_str = this->accumulated_args.at(2); | ||
| 1994 | if (len_str == "40") | 1851 | if (len_str == "40") |
| 1995 | { | 1852 | { |
| 1996 | o.keylen = 40; | 1853 | o.keylen = 40; |
| 1997 | - this->option_table = &(this->encrypt40_option_table); | 1854 | + this->ap.selectOptionTable(O_ENCRYPT_40); |
| 1998 | } | 1855 | } |
| 1999 | else if (len_str == "128") | 1856 | else if (len_str == "128") |
| 2000 | { | 1857 | { |
| 2001 | o.keylen = 128; | 1858 | o.keylen = 128; |
| 2002 | - this->option_table = &(this->encrypt128_option_table); | 1859 | + this->ap.selectOptionTable(O_ENCRYPT_128); |
| 2003 | } | 1860 | } |
| 2004 | else if (len_str == "256") | 1861 | else if (len_str == "256") |
| 2005 | { | 1862 | { |
| 2006 | o.keylen = 256; | 1863 | o.keylen = 256; |
| 2007 | o.use_aes = true; | 1864 | o.use_aes = true; |
| 2008 | - this->option_table = &(this->encrypt256_option_table); | 1865 | + this->ap.selectOptionTable(O_ENCRYPT_256); |
| 2009 | } | 1866 | } |
| 2010 | else | 1867 | else |
| 2011 | { | 1868 | { |
| @@ -2096,12 +1953,116 @@ ArgParser::argCollate(char* parameter) | @@ -2096,12 +1953,116 @@ ArgParser::argCollate(char* parameter) | ||
| 2096 | void | 1953 | void |
| 2097 | ArgParser::argPages() | 1954 | ArgParser::argPages() |
| 2098 | { | 1955 | { |
| 2099 | - ++cur_arg; | ||
| 2100 | if (! o.page_specs.empty()) | 1956 | if (! o.page_specs.empty()) |
| 2101 | { | 1957 | { |
| 2102 | usage("the --pages may only be specified one time"); | 1958 | usage("the --pages may only be specified one time"); |
| 2103 | } | 1959 | } |
| 2104 | - o.page_specs = parsePagesOptions(); | 1960 | + this->accumulated_args.clear(); |
| 1961 | + this->ap.selectOptionTable(O_PAGES); | ||
| 1962 | +} | ||
| 1963 | + | ||
| 1964 | +void | ||
| 1965 | +ArgParser::argPagesPassword(char* parameter) | ||
| 1966 | +{ | ||
| 1967 | + if (this->pages_password != nullptr) | ||
| 1968 | + { | ||
| 1969 | + QTC::TC("qpdf", "qpdf duplicated pages password"); | ||
| 1970 | + usage("--password already specified for this file"); | ||
| 1971 | + } | ||
| 1972 | + if (this->accumulated_args.size() != 1) | ||
| 1973 | + { | ||
| 1974 | + QTC::TC("qpdf", "qpdf misplaced pages password"); | ||
| 1975 | + usage("in --pages, --password must immediately follow a file name"); | ||
| 1976 | + } | ||
| 1977 | + this->pages_password = parameter; | ||
| 1978 | +} | ||
| 1979 | + | ||
| 1980 | +void | ||
| 1981 | +ArgParser::argPagesPositional(char* arg) | ||
| 1982 | +{ | ||
| 1983 | + if (arg == nullptr) | ||
| 1984 | + { | ||
| 1985 | + if (this->accumulated_args.empty()) | ||
| 1986 | + { | ||
| 1987 | + return; | ||
| 1988 | + } | ||
| 1989 | + } | ||
| 1990 | + else | ||
| 1991 | + { | ||
| 1992 | + this->accumulated_args.push_back(arg); | ||
| 1993 | + } | ||
| 1994 | + | ||
| 1995 | + char const* file = this->accumulated_args.at(0); | ||
| 1996 | + char const* range = nullptr; | ||
| 1997 | + | ||
| 1998 | + size_t n_args = this->accumulated_args.size(); | ||
| 1999 | + if (n_args >= 2) | ||
| 2000 | + { | ||
| 2001 | + range = this->accumulated_args.at(1); | ||
| 2002 | + } | ||
| 2003 | + | ||
| 2004 | + // See if the user omitted the range entirely, in which case we | ||
| 2005 | + // assume "1-z". | ||
| 2006 | + char* next_file = nullptr; | ||
| 2007 | + if (range == nullptr) | ||
| 2008 | + { | ||
| 2009 | + if (arg == nullptr) | ||
| 2010 | + { | ||
| 2011 | + // The filename or password was the last argument | ||
| 2012 | + QTC::TC("qpdf", "qpdf pages range omitted at end", | ||
| 2013 | + this->pages_password == nullptr ? 0 : 1); | ||
| 2014 | + } | ||
| 2015 | + else | ||
| 2016 | + { | ||
| 2017 | + // We need to accumulate some more arguments | ||
| 2018 | + return; | ||
| 2019 | + } | ||
| 2020 | + } | ||
| 2021 | + else | ||
| 2022 | + { | ||
| 2023 | + try | ||
| 2024 | + { | ||
| 2025 | + parseNumrange(range, 0, true); | ||
| 2026 | + } | ||
| 2027 | + catch (std::runtime_error& e1) | ||
| 2028 | + { | ||
| 2029 | + // The range is invalid. Let's see if it's a file. | ||
| 2030 | + if (strcmp(range, ".") == 0) | ||
| 2031 | + { | ||
| 2032 | + // "." means the input file. | ||
| 2033 | + QTC::TC("qpdf", "qpdf pages range omitted with ."); | ||
| 2034 | + } | ||
| 2035 | + else if (QUtil::file_can_be_opened(range)) | ||
| 2036 | + { | ||
| 2037 | + QTC::TC("qpdf", "qpdf pages range omitted in middle"); | ||
| 2038 | + // Yup, it's a file. | ||
| 2039 | + } | ||
| 2040 | + else | ||
| 2041 | + { | ||
| 2042 | + // Give the range error | ||
| 2043 | + usage(e1.what()); | ||
| 2044 | + } | ||
| 2045 | + next_file = const_cast<char*>(range); | ||
| 2046 | + range = nullptr; | ||
| 2047 | + } | ||
| 2048 | + } | ||
| 2049 | + if (range == nullptr) | ||
| 2050 | + { | ||
| 2051 | + range = "1-z"; | ||
| 2052 | + } | ||
| 2053 | + o.page_specs.push_back(PageSpec(file, this->pages_password, range)); | ||
| 2054 | + this->accumulated_args.clear(); | ||
| 2055 | + this->pages_password = nullptr; | ||
| 2056 | + if (next_file != nullptr) | ||
| 2057 | + { | ||
| 2058 | + this->accumulated_args.push_back(next_file); | ||
| 2059 | + } | ||
| 2060 | +} | ||
| 2061 | + | ||
| 2062 | +void | ||
| 2063 | +ArgParser::argEndPages() | ||
| 2064 | +{ | ||
| 2065 | + argPagesPositional(nullptr); | ||
| 2105 | if (o.page_specs.empty()) | 2066 | if (o.page_specs.empty()) |
| 2106 | { | 2067 | { |
| 2107 | usage("--pages: no page specifications given"); | 2068 | usage("--pages: no page specifications given"); |
| @@ -2155,15 +2116,15 @@ ArgParser::argRemoveAttachment(char* parameter) | @@ -2155,15 +2116,15 @@ ArgParser::argRemoveAttachment(char* parameter) | ||
| 2155 | void | 2116 | void |
| 2156 | ArgParser::argAddAttachment() | 2117 | ArgParser::argAddAttachment() |
| 2157 | { | 2118 | { |
| 2158 | - this->option_table = &(this->add_attachment_option_table); | ||
| 2159 | o.attachments_to_add.push_back(AddAttachment()); | 2119 | o.attachments_to_add.push_back(AddAttachment()); |
| 2120 | + this->ap.selectOptionTable(O_ATTACHMENT); | ||
| 2160 | } | 2121 | } |
| 2161 | 2122 | ||
| 2162 | void | 2123 | void |
| 2163 | ArgParser::argCopyAttachments() | 2124 | ArgParser::argCopyAttachments() |
| 2164 | { | 2125 | { |
| 2165 | - this->option_table = &(this->copy_attachments_option_table); | ||
| 2166 | o.attachments_to_copy.push_back(CopyAttachmentFrom()); | 2126 | o.attachments_to_copy.push_back(CopyAttachmentFrom()); |
| 2127 | + this->ap.selectOptionTable(O_COPY_ATTACHMENT); | ||
| 2167 | } | 2128 | } |
| 2168 | 2129 | ||
| 2169 | void | 2130 | void |
| @@ -2743,7 +2704,6 @@ ArgParser::argEndEncrypt() | @@ -2743,7 +2704,6 @@ ArgParser::argEndEncrypt() | ||
| 2743 | o.encrypt = true; | 2704 | o.encrypt = true; |
| 2744 | o.decrypt = false; | 2705 | o.decrypt = false; |
| 2745 | o.copy_encryption = false; | 2706 | o.copy_encryption = false; |
| 2746 | - this->option_table = &(this->main_option_table); | ||
| 2747 | } | 2707 | } |
| 2748 | 2708 | ||
| 2749 | void | 2709 | void |
| @@ -2795,7 +2755,6 @@ ArgParser::argUOpassword(char* parameter) | @@ -2795,7 +2755,6 @@ ArgParser::argUOpassword(char* parameter) | ||
| 2795 | void | 2755 | void |
| 2796 | ArgParser::argEndUnderOverlay() | 2756 | ArgParser::argEndUnderOverlay() |
| 2797 | { | 2757 | { |
| 2798 | - this->option_table = &(this->main_option_table); | ||
| 2799 | if (0 == o.under_overlay->filename) | 2758 | if (0 == o.under_overlay->filename) |
| 2800 | { | 2759 | { |
| 2801 | usage(o.under_overlay->which + " file not specified"); | 2760 | usage(o.under_overlay->which + " file not specified"); |
| @@ -2888,7 +2847,6 @@ ArgParser::argEndAddAttachment() | @@ -2888,7 +2847,6 @@ ArgParser::argEndAddAttachment() | ||
| 2888 | { | 2847 | { |
| 2889 | static std::string now = QUtil::qpdf_time_to_pdf_time( | 2848 | static std::string now = QUtil::qpdf_time_to_pdf_time( |
| 2890 | QUtil::get_current_qpdf_time()); | 2849 | QUtil::get_current_qpdf_time()); |
| 2891 | - this->option_table = &(this->main_option_table); | ||
| 2892 | auto& cur = o.attachments_to_add.back(); | 2850 | auto& cur = o.attachments_to_add.back(); |
| 2893 | if (cur.path.empty()) | 2851 | if (cur.path.empty()) |
| 2894 | { | 2852 | { |
| @@ -2938,161 +2896,12 @@ ArgParser::argCApassword(char* parameter) | @@ -2938,161 +2896,12 @@ ArgParser::argCApassword(char* parameter) | ||
| 2938 | void | 2896 | void |
| 2939 | ArgParser::argEndCopyAttachments() | 2897 | ArgParser::argEndCopyAttachments() |
| 2940 | { | 2898 | { |
| 2941 | - this->option_table = &(this->main_option_table); | ||
| 2942 | if (o.attachments_to_copy.back().path.empty()) | 2899 | if (o.attachments_to_copy.back().path.empty()) |
| 2943 | { | 2900 | { |
| 2944 | usage("copy attachments: no path specified"); | 2901 | usage("copy attachments: no path specified"); |
| 2945 | } | 2902 | } |
| 2946 | } | 2903 | } |
| 2947 | 2904 | ||
| 2948 | -void | ||
| 2949 | -ArgParser::handleArgFileArguments() | ||
| 2950 | -{ | ||
| 2951 | - // Support reading arguments from files. Create a new argv. Ensure | ||
| 2952 | - // that argv itself as well as all its contents are automatically | ||
| 2953 | - // deleted by using PointerHolder objects to back the pointers in | ||
| 2954 | - // argv. | ||
| 2955 | - new_argv.push_back(PointerHolder<char>(true, QUtil::copy_string(argv[0]))); | ||
| 2956 | - for (int i = 1; i < argc; ++i) | ||
| 2957 | - { | ||
| 2958 | - char* argfile = 0; | ||
| 2959 | - if ((strlen(argv[i]) > 1) && (argv[i][0] == '@')) | ||
| 2960 | - { | ||
| 2961 | - argfile = 1 + argv[i]; | ||
| 2962 | - if (strcmp(argfile, "-") != 0) | ||
| 2963 | - { | ||
| 2964 | - if (! QUtil::file_can_be_opened(argfile)) | ||
| 2965 | - { | ||
| 2966 | - // The file's not there; treating as regular option | ||
| 2967 | - argfile = nullptr; | ||
| 2968 | - } | ||
| 2969 | - } | ||
| 2970 | - } | ||
| 2971 | - if (argfile) | ||
| 2972 | - { | ||
| 2973 | - readArgsFromFile(1+argv[i]); | ||
| 2974 | - } | ||
| 2975 | - else | ||
| 2976 | - { | ||
| 2977 | - new_argv.push_back( | ||
| 2978 | - PointerHolder<char>(true, QUtil::copy_string(argv[i]))); | ||
| 2979 | - } | ||
| 2980 | - } | ||
| 2981 | - argv_ph = PointerHolder<char*>(true, new char*[1+new_argv.size()]); | ||
| 2982 | - argv = argv_ph.getPointer(); | ||
| 2983 | - for (size_t i = 0; i < new_argv.size(); ++i) | ||
| 2984 | - { | ||
| 2985 | - argv[i] = new_argv.at(i).getPointer(); | ||
| 2986 | - } | ||
| 2987 | - argc = QIntC::to_int(new_argv.size()); | ||
| 2988 | - argv[argc] = 0; | ||
| 2989 | -} | ||
| 2990 | - | ||
| 2991 | -void | ||
| 2992 | -ArgParser::handleBashArguments() | ||
| 2993 | -{ | ||
| 2994 | - // Do a minimal job of parsing bash_line into arguments. This | ||
| 2995 | - // doesn't do everything the shell does (e.g. $(...), variable | ||
| 2996 | - // expansion, arithmetic, globs, etc.), but it should be good | ||
| 2997 | - // enough for purposes of handling completion. As we build up the | ||
| 2998 | - // new argv, we can't use this->new_argv because this code has to | ||
| 2999 | - // interoperate with @file arguments, so memory for both ways of | ||
| 3000 | - // fabricating argv has to be protected. | ||
| 3001 | - | ||
| 3002 | - bool last_was_backslash = false; | ||
| 3003 | - enum { st_top, st_squote, st_dquote } state = st_top; | ||
| 3004 | - std::string arg; | ||
| 3005 | - for (std::string::iterator iter = bash_line.begin(); | ||
| 3006 | - iter != bash_line.end(); ++iter) | ||
| 3007 | - { | ||
| 3008 | - char ch = (*iter); | ||
| 3009 | - if (last_was_backslash) | ||
| 3010 | - { | ||
| 3011 | - arg.append(1, ch); | ||
| 3012 | - last_was_backslash = false; | ||
| 3013 | - } | ||
| 3014 | - else if (ch == '\\') | ||
| 3015 | - { | ||
| 3016 | - last_was_backslash = true; | ||
| 3017 | - } | ||
| 3018 | - else | ||
| 3019 | - { | ||
| 3020 | - bool append = false; | ||
| 3021 | - switch (state) | ||
| 3022 | - { | ||
| 3023 | - case st_top: | ||
| 3024 | - if (QUtil::is_space(ch)) | ||
| 3025 | - { | ||
| 3026 | - if (! arg.empty()) | ||
| 3027 | - { | ||
| 3028 | - bash_argv.push_back( | ||
| 3029 | - PointerHolder<char>( | ||
| 3030 | - true, QUtil::copy_string(arg.c_str()))); | ||
| 3031 | - arg.clear(); | ||
| 3032 | - } | ||
| 3033 | - } | ||
| 3034 | - else if (ch == '"') | ||
| 3035 | - { | ||
| 3036 | - state = st_dquote; | ||
| 3037 | - } | ||
| 3038 | - else if (ch == '\'') | ||
| 3039 | - { | ||
| 3040 | - state = st_squote; | ||
| 3041 | - } | ||
| 3042 | - else | ||
| 3043 | - { | ||
| 3044 | - append = true; | ||
| 3045 | - } | ||
| 3046 | - break; | ||
| 3047 | - | ||
| 3048 | - case st_squote: | ||
| 3049 | - if (ch == '\'') | ||
| 3050 | - { | ||
| 3051 | - state = st_top; | ||
| 3052 | - } | ||
| 3053 | - else | ||
| 3054 | - { | ||
| 3055 | - append = true; | ||
| 3056 | - } | ||
| 3057 | - break; | ||
| 3058 | - | ||
| 3059 | - case st_dquote: | ||
| 3060 | - if (ch == '"') | ||
| 3061 | - { | ||
| 3062 | - state = st_top; | ||
| 3063 | - } | ||
| 3064 | - else | ||
| 3065 | - { | ||
| 3066 | - append = true; | ||
| 3067 | - } | ||
| 3068 | - break; | ||
| 3069 | - } | ||
| 3070 | - if (append) | ||
| 3071 | - { | ||
| 3072 | - arg.append(1, ch); | ||
| 3073 | - } | ||
| 3074 | - } | ||
| 3075 | - } | ||
| 3076 | - if (bash_argv.empty()) | ||
| 3077 | - { | ||
| 3078 | - // This can't happen if properly invoked by bash, but ensure | ||
| 3079 | - // we have a valid argv[0] regardless. | ||
| 3080 | - bash_argv.push_back( | ||
| 3081 | - PointerHolder<char>( | ||
| 3082 | - true, QUtil::copy_string(argv[0]))); | ||
| 3083 | - } | ||
| 3084 | - // Explicitly discard any non-space-terminated word. The "current | ||
| 3085 | - // word" is handled specially. | ||
| 3086 | - bash_argv_ph = PointerHolder<char*>(true, new char*[1+bash_argv.size()]); | ||
| 3087 | - argv = bash_argv_ph.getPointer(); | ||
| 3088 | - for (size_t i = 0; i < bash_argv.size(); ++i) | ||
| 3089 | - { | ||
| 3090 | - argv[i] = bash_argv.at(i).getPointer(); | ||
| 3091 | - } | ||
| 3092 | - argc = QIntC::to_int(bash_argv.size()); | ||
| 3093 | - argv[argc] = 0; | ||
| 3094 | -} | ||
| 3095 | - | ||
| 3096 | void usageExit(std::string const& msg) | 2905 | void usageExit(std::string const& msg) |
| 3097 | { | 2906 | { |
| 3098 | std::cerr | 2907 | std::cerr |
| @@ -3108,7 +2917,7 @@ void usageExit(std::string const& msg) | @@ -3108,7 +2917,7 @@ void usageExit(std::string const& msg) | ||
| 3108 | void | 2917 | void |
| 3109 | ArgParser::usage(std::string const& message) | 2918 | ArgParser::usage(std::string const& message) |
| 3110 | { | 2919 | { |
| 3111 | - if (this->bash_completion) | 2920 | + if (this->ap.isCompleting()) |
| 3112 | { | 2921 | { |
| 3113 | // This will cause bash to fall back to regular file completion. | 2922 | // This will cause bash to fall back to regular file completion. |
| 3114 | exit(0); | 2923 | exit(0); |
| @@ -3234,101 +3043,11 @@ ArgParser::parseNumrange(char const* range, int max, bool throw_error) | @@ -3234,101 +3043,11 @@ ArgParser::parseNumrange(char const* range, int max, bool throw_error) | ||
| 3234 | return std::vector<int>(); | 3043 | return std::vector<int>(); |
| 3235 | } | 3044 | } |
| 3236 | 3045 | ||
| 3237 | -std::vector<PageSpec> | ||
| 3238 | -ArgParser::parsePagesOptions() | ||
| 3239 | -{ | ||
| 3240 | - auto check_unclosed = [this](char const* arg, int n) { | ||
| 3241 | - if ((strlen(arg) > 0) && (arg[0] == '-')) | ||
| 3242 | - { | ||
| 3243 | - // A common error is to forget to close --pages with --, | ||
| 3244 | - // so catch this as special case | ||
| 3245 | - QTC::TC("qpdf", "check unclosed --pages", n); | ||
| 3246 | - usage("the --pages option must be terminated with -- by itself"); | ||
| 3247 | - } | ||
| 3248 | - }; | ||
| 3249 | - | ||
| 3250 | - std::vector<PageSpec> result; | ||
| 3251 | - while (1) | ||
| 3252 | - { | ||
| 3253 | - if ((cur_arg < argc) && (strcmp(argv[cur_arg], "--") == 0)) | ||
| 3254 | - { | ||
| 3255 | - break; | ||
| 3256 | - } | ||
| 3257 | - if (cur_arg + 1 >= argc) | ||
| 3258 | - { | ||
| 3259 | - usage("insufficient arguments to --pages"); | ||
| 3260 | - } | ||
| 3261 | - char const* file = argv[cur_arg++]; | ||
| 3262 | - char const* password = 0; | ||
| 3263 | - char const* range = argv[cur_arg++]; | ||
| 3264 | - if (! QUtil::file_can_be_opened(file)) | ||
| 3265 | - { | ||
| 3266 | - check_unclosed(file, 0); | ||
| 3267 | - } | ||
| 3268 | - if (strncmp(range, "--password=", 11) == 0) | ||
| 3269 | - { | ||
| 3270 | - // Oh, that's the password, not the range | ||
| 3271 | - if (cur_arg + 1 >= argc) | ||
| 3272 | - { | ||
| 3273 | - usage("insufficient arguments to --pages"); | ||
| 3274 | - } | ||
| 3275 | - password = range + 11; | ||
| 3276 | - range = argv[cur_arg++]; | ||
| 3277 | - } | ||
| 3278 | - | ||
| 3279 | - // See if the user omitted the range entirely, in which case | ||
| 3280 | - // we assume "1-z". | ||
| 3281 | - bool range_omitted = false; | ||
| 3282 | - if (strcmp(range, "--") == 0) | ||
| 3283 | - { | ||
| 3284 | - // The filename or password was the last argument | ||
| 3285 | - QTC::TC("qpdf", "qpdf pages range omitted at end"); | ||
| 3286 | - range_omitted = true; | ||
| 3287 | - } | ||
| 3288 | - else | ||
| 3289 | - { | ||
| 3290 | - try | ||
| 3291 | - { | ||
| 3292 | - parseNumrange(range, 0, true); | ||
| 3293 | - } | ||
| 3294 | - catch (std::runtime_error& e1) | ||
| 3295 | - { | ||
| 3296 | - // The range is invalid. Let's see if it's a file. | ||
| 3297 | - range_omitted = true; | ||
| 3298 | - if (strcmp(range, ".") == 0) | ||
| 3299 | - { | ||
| 3300 | - // "." means the input file. | ||
| 3301 | - QTC::TC("qpdf", "qpdf pages range omitted with ."); | ||
| 3302 | - } | ||
| 3303 | - else if (QUtil::file_can_be_opened(range)) | ||
| 3304 | - { | ||
| 3305 | - QTC::TC("qpdf", "qpdf pages range omitted in middle"); | ||
| 3306 | - // Yup, it's a file. | ||
| 3307 | - } | ||
| 3308 | - else | ||
| 3309 | - { | ||
| 3310 | - check_unclosed(range, 1); | ||
| 3311 | - // Give the range error | ||
| 3312 | - usage(e1.what()); | ||
| 3313 | - } | ||
| 3314 | - } | ||
| 3315 | - } | ||
| 3316 | - if (range_omitted) | ||
| 3317 | - { | ||
| 3318 | - --cur_arg; | ||
| 3319 | - range = "1-z"; | ||
| 3320 | - } | ||
| 3321 | - | ||
| 3322 | - result.push_back(PageSpec(file, password, range)); | ||
| 3323 | - } | ||
| 3324 | - return result; | ||
| 3325 | -} | ||
| 3326 | - | ||
| 3327 | void | 3046 | void |
| 3328 | ArgParser::parseUnderOverlayOptions(UnderOverlay* uo) | 3047 | ArgParser::parseUnderOverlayOptions(UnderOverlay* uo) |
| 3329 | { | 3048 | { |
| 3330 | o.under_overlay = uo; | 3049 | o.under_overlay = uo; |
| 3331 | - this->option_table = &(this->under_overlay_option_table); | 3050 | + this->ap.selectOptionTable(O_UNDER_OVERLAY); |
| 3332 | } | 3051 | } |
| 3333 | 3052 | ||
| 3334 | QPDFPageData::QPDFPageData(std::string const& filename, | 3053 | QPDFPageData::QPDFPageData(std::string const& filename, |
| @@ -3374,28 +3093,6 @@ static void parse_version(std::string const& full_version_string, | @@ -3374,28 +3093,6 @@ static void parse_version(std::string const& full_version_string, | ||
| 3374 | } | 3093 | } |
| 3375 | 3094 | ||
| 3376 | void | 3095 | void |
| 3377 | -ArgParser::readArgsFromFile(char const* filename) | ||
| 3378 | -{ | ||
| 3379 | - std::list<std::string> lines; | ||
| 3380 | - if (strcmp(filename, "-") == 0) | ||
| 3381 | - { | ||
| 3382 | - QTC::TC("qpdf", "qpdf read args from stdin"); | ||
| 3383 | - lines = QUtil::read_lines_from_file(std::cin); | ||
| 3384 | - } | ||
| 3385 | - else | ||
| 3386 | - { | ||
| 3387 | - QTC::TC("qpdf", "qpdf read args from file"); | ||
| 3388 | - lines = QUtil::read_lines_from_file(filename); | ||
| 3389 | - } | ||
| 3390 | - for (std::list<std::string>::iterator iter = lines.begin(); | ||
| 3391 | - iter != lines.end(); ++iter) | ||
| 3392 | - { | ||
| 3393 | - new_argv.push_back( | ||
| 3394 | - PointerHolder<char>(true, QUtil::copy_string((*iter).c_str()))); | ||
| 3395 | - } | ||
| 3396 | -} | ||
| 3397 | - | ||
| 3398 | -void | ||
| 3399 | ArgParser::parseRotationParameter(std::string const& parameter) | 3096 | ArgParser::parseRotationParameter(std::string const& parameter) |
| 3400 | { | 3097 | { |
| 3401 | std::string angle_str; | 3098 | std::string angle_str; |
| @@ -3462,232 +3159,21 @@ ArgParser::parseRotationParameter(std::string const& parameter) | @@ -3462,232 +3159,21 @@ ArgParser::parseRotationParameter(std::string const& parameter) | ||
| 3462 | } | 3159 | } |
| 3463 | 3160 | ||
| 3464 | void | 3161 | void |
| 3465 | -ArgParser::checkCompletion() | ||
| 3466 | -{ | ||
| 3467 | - // See if we're being invoked from bash completion. | ||
| 3468 | - std::string bash_point_env; | ||
| 3469 | - // On Windows with mingw, there have been times when there appears | ||
| 3470 | - // to be no way to distinguish between an empty environment | ||
| 3471 | - // variable and an unset variable. There are also conditions under | ||
| 3472 | - // which bash doesn't set COMP_LINE. Therefore, enter this logic | ||
| 3473 | - // if either COMP_LINE or COMP_POINT are set. They will both be | ||
| 3474 | - // set together under ordinary circumstances. | ||
| 3475 | - bool got_line = QUtil::get_env("COMP_LINE", &bash_line); | ||
| 3476 | - bool got_point = QUtil::get_env("COMP_POINT", &bash_point_env); | ||
| 3477 | - if (got_line || got_point) | ||
| 3478 | - { | ||
| 3479 | - size_t p = QUtil::string_to_uint(bash_point_env.c_str()); | ||
| 3480 | - if (p < bash_line.length()) | ||
| 3481 | - { | ||
| 3482 | - // Truncate the line. We ignore everything at or after the | ||
| 3483 | - // cursor for completion purposes. | ||
| 3484 | - bash_line = bash_line.substr(0, p); | ||
| 3485 | - } | ||
| 3486 | - if (p > bash_line.length()) | ||
| 3487 | - { | ||
| 3488 | - p = bash_line.length(); | ||
| 3489 | - } | ||
| 3490 | - // Set bash_cur and bash_prev based on bash_line rather than | ||
| 3491 | - // relying on argv. This enables us to use bashcompinit to get | ||
| 3492 | - // completion in zsh too since bashcompinit sets COMP_LINE and | ||
| 3493 | - // COMP_POINT but doesn't invoke the command with options like | ||
| 3494 | - // bash does. | ||
| 3495 | - | ||
| 3496 | - // p is equal to length of the string. Walk backwards looking | ||
| 3497 | - // for the first separator. bash_cur is everything after the | ||
| 3498 | - // last separator, possibly empty. | ||
| 3499 | - char sep(0); | ||
| 3500 | - while (p > 0) | ||
| 3501 | - { | ||
| 3502 | - --p; | ||
| 3503 | - char ch = bash_line.at(p); | ||
| 3504 | - if ((ch == ' ') || (ch == '=') || (ch == ':')) | ||
| 3505 | - { | ||
| 3506 | - sep = ch; | ||
| 3507 | - break; | ||
| 3508 | - } | ||
| 3509 | - } | ||
| 3510 | - if (1+p <= bash_line.length()) | ||
| 3511 | - { | ||
| 3512 | - bash_cur = bash_line.substr(1+p, std::string::npos); | ||
| 3513 | - } | ||
| 3514 | - if ((sep == ':') || (sep == '=')) | ||
| 3515 | - { | ||
| 3516 | - // Bash sets prev to the non-space separator if any. | ||
| 3517 | - // Actually, if there are multiple separators in a row, | ||
| 3518 | - // they are all included in prev, but that detail is not | ||
| 3519 | - // important to us and not worth coding. | ||
| 3520 | - bash_prev = bash_line.substr(p, 1); | ||
| 3521 | - } | ||
| 3522 | - else | ||
| 3523 | - { | ||
| 3524 | - // Go back to the last separator and set prev based on | ||
| 3525 | - // that. | ||
| 3526 | - size_t p1 = p; | ||
| 3527 | - while (p1 > 0) | ||
| 3528 | - { | ||
| 3529 | - --p1; | ||
| 3530 | - char ch = bash_line.at(p1); | ||
| 3531 | - if ((ch == ' ') || (ch == ':') || (ch == '=')) | ||
| 3532 | - { | ||
| 3533 | - bash_prev = bash_line.substr(p1 + 1, p - p1 - 1); | ||
| 3534 | - break; | ||
| 3535 | - } | ||
| 3536 | - } | ||
| 3537 | - } | ||
| 3538 | - if (bash_prev.empty()) | ||
| 3539 | - { | ||
| 3540 | - bash_prev = bash_line.substr(0, p); | ||
| 3541 | - } | ||
| 3542 | - if (argc == 1) | ||
| 3543 | - { | ||
| 3544 | - // This is probably zsh using bashcompinit. There are a | ||
| 3545 | - // few differences in the expected output. | ||
| 3546 | - zsh_completion = true; | ||
| 3547 | - } | ||
| 3548 | - handleBashArguments(); | ||
| 3549 | - bash_completion = true; | ||
| 3550 | - } | ||
| 3551 | -} | ||
| 3552 | - | ||
| 3553 | -void | ||
| 3554 | ArgParser::parseOptions() | 3162 | ArgParser::parseOptions() |
| 3555 | { | 3163 | { |
| 3556 | - checkCompletion(); | ||
| 3557 | - handleArgFileArguments(); | ||
| 3558 | - for (cur_arg = 1; cur_arg < argc; ++cur_arg) | ||
| 3559 | - { | ||
| 3560 | - bool help_option = false; | ||
| 3561 | - auto oep = this->option_table->end(); | ||
| 3562 | - char* arg = argv[cur_arg]; | ||
| 3563 | - char* parameter = nullptr; | ||
| 3564 | - std::string o_arg(arg); | ||
| 3565 | - std::string arg_s(arg); | ||
| 3566 | - if (strcmp(arg, "--") == 0) | ||
| 3567 | - { | ||
| 3568 | - // Special case for -- option, which is used to break out | ||
| 3569 | - // of subparsers. | ||
| 3570 | - oep = this->option_table->find("--"); | ||
| 3571 | - if (oep == this->option_table->end()) | ||
| 3572 | - { | ||
| 3573 | - throw std::logic_error("ArgParser: -- handler not registered"); | ||
| 3574 | - } | ||
| 3575 | - } | ||
| 3576 | - else if ((arg[0] == '-') && (strcmp(arg, "-") != 0)) | ||
| 3577 | - { | ||
| 3578 | - ++arg; | ||
| 3579 | - if (arg[0] == '-') | ||
| 3580 | - { | ||
| 3581 | - // Be lax about -arg vs --arg | ||
| 3582 | - ++arg; | ||
| 3583 | - } | ||
| 3584 | - if (strlen(arg) > 0) | ||
| 3585 | - { | ||
| 3586 | - // Prevent --=something from being treated as an empty | ||
| 3587 | - // arg since the empty string in the option table is | ||
| 3588 | - // for positional arguments. | ||
| 3589 | - parameter = const_cast<char*>(strchr(1 + arg, '=')); | ||
| 3590 | - } | ||
| 3591 | - if (parameter) | ||
| 3592 | - { | ||
| 3593 | - *parameter++ = 0; | ||
| 3594 | - } | ||
| 3595 | - | ||
| 3596 | - arg_s = arg; | ||
| 3597 | - if (! (arg_s.empty() || (arg_s.at(0) == '-'))) | ||
| 3598 | - { | ||
| 3599 | - oep = this->option_table->find(arg_s); | ||
| 3600 | - } | ||
| 3601 | - | ||
| 3602 | - if ((! this->bash_completion) && | ||
| 3603 | - (argc == 2) && (cur_arg == 1) && | ||
| 3604 | - (oep == this->option_table->end())) | ||
| 3605 | - { | ||
| 3606 | - // Handle help option, which is only valid as the sole | ||
| 3607 | - // option. | ||
| 3608 | - oep = this->help_option_table.find(arg_s); | ||
| 3609 | - help_option = true; | ||
| 3610 | - } | ||
| 3611 | - } | ||
| 3612 | - else | ||
| 3613 | - { | ||
| 3614 | - // The empty string maps to the positional argument | ||
| 3615 | - // handler. | ||
| 3616 | - oep = this->option_table->find(""); | ||
| 3617 | - parameter = arg; | ||
| 3618 | - } | ||
| 3619 | - | ||
| 3620 | - if (oep == this->option_table->end()) | ||
| 3621 | - { | ||
| 3622 | - usage("unrecognized argument " + o_arg); | ||
| 3623 | - } | ||
| 3624 | - | ||
| 3625 | - OptionEntry& oe = oep->second; | ||
| 3626 | - if ((oe.parameter_needed && (0 == parameter)) || | ||
| 3627 | - ((! oe.choices.empty() && | ||
| 3628 | - ((0 == parameter) || | ||
| 3629 | - (0 == oe.choices.count(parameter)))))) | ||
| 3630 | - { | ||
| 3631 | - std::string message = | ||
| 3632 | - "--" + arg_s + " must be given as --" + arg_s + "="; | ||
| 3633 | - if (! oe.choices.empty()) | ||
| 3634 | - { | ||
| 3635 | - QTC::TC("qpdf", "qpdf required choices"); | ||
| 3636 | - message += "{"; | ||
| 3637 | - for (std::set<std::string>::iterator iter = | ||
| 3638 | - oe.choices.begin(); | ||
| 3639 | - iter != oe.choices.end(); ++iter) | ||
| 3640 | - { | ||
| 3641 | - if (iter != oe.choices.begin()) | ||
| 3642 | - { | ||
| 3643 | - message += ","; | ||
| 3644 | - } | ||
| 3645 | - message += *iter; | ||
| 3646 | - } | ||
| 3647 | - message += "}"; | ||
| 3648 | - } | ||
| 3649 | - else if (! oe.parameter_name.empty()) | ||
| 3650 | - { | ||
| 3651 | - QTC::TC("qpdf", "qpdf required parameter"); | ||
| 3652 | - message += oe.parameter_name; | ||
| 3653 | - } | ||
| 3654 | - else | ||
| 3655 | - { | ||
| 3656 | - // should not be possible | ||
| 3657 | - message += "option"; | ||
| 3658 | - } | ||
| 3659 | - usage(message); | ||
| 3660 | - } | ||
| 3661 | - if (oe.bare_arg_handler) | ||
| 3662 | - { | ||
| 3663 | - (this->*(oe.bare_arg_handler))(); | ||
| 3664 | - } | ||
| 3665 | - else if (oe.param_arg_handler) | ||
| 3666 | - { | ||
| 3667 | - (this->*(oe.param_arg_handler))(parameter); | ||
| 3668 | - } | ||
| 3669 | - if (help_option) | ||
| 3670 | - { | ||
| 3671 | - exit(0); | ||
| 3672 | - } | ||
| 3673 | - } | ||
| 3674 | - if (this->bash_completion) | 3164 | + try |
| 3675 | { | 3165 | { |
| 3676 | - handleCompletion(); | 3166 | + this->ap.parseArgs(); |
| 3677 | } | 3167 | } |
| 3678 | - else | 3168 | + catch (QPDFArgParser::Usage& e) |
| 3679 | { | 3169 | { |
| 3680 | - doFinalChecks(); | 3170 | + usage(e.what()); |
| 3681 | } | 3171 | } |
| 3682 | } | 3172 | } |
| 3683 | 3173 | ||
| 3684 | void | 3174 | void |
| 3685 | ArgParser::doFinalChecks() | 3175 | ArgParser::doFinalChecks() |
| 3686 | { | 3176 | { |
| 3687 | - if (this->option_table != &(this->main_option_table)) | ||
| 3688 | - { | ||
| 3689 | - usage("missing -- at end of options"); | ||
| 3690 | - } | ||
| 3691 | if (o.replace_input) | 3177 | if (o.replace_input) |
| 3692 | { | 3178 | { |
| 3693 | if (o.outfilename) | 3179 | if (o.outfilename) |
| @@ -3769,127 +3255,6 @@ ArgParser::doFinalChecks() | @@ -3769,127 +3255,6 @@ ArgParser::doFinalChecks() | ||
| 3769 | } | 3255 | } |
| 3770 | } | 3256 | } |
| 3771 | 3257 | ||
| 3772 | -void | ||
| 3773 | -ArgParser::addChoicesToCompletions(std::string const& option, | ||
| 3774 | - std::string const& extra_prefix) | ||
| 3775 | -{ | ||
| 3776 | - if (this->option_table->count(option) != 0) | ||
| 3777 | - { | ||
| 3778 | - OptionEntry& oe = (*this->option_table)[option]; | ||
| 3779 | - for (std::set<std::string>::iterator iter = oe.choices.begin(); | ||
| 3780 | - iter != oe.choices.end(); ++iter) | ||
| 3781 | - { | ||
| 3782 | - completions.insert(extra_prefix + *iter); | ||
| 3783 | - } | ||
| 3784 | - } | ||
| 3785 | -} | ||
| 3786 | - | ||
| 3787 | -void | ||
| 3788 | -ArgParser::addOptionsToCompletions() | ||
| 3789 | -{ | ||
| 3790 | - for (std::map<std::string, OptionEntry>::iterator iter = | ||
| 3791 | - this->option_table->begin(); | ||
| 3792 | - iter != this->option_table->end(); ++iter) | ||
| 3793 | - { | ||
| 3794 | - std::string const& arg = (*iter).first; | ||
| 3795 | - if (arg == "--") | ||
| 3796 | - { | ||
| 3797 | - continue; | ||
| 3798 | - } | ||
| 3799 | - OptionEntry& oe = (*iter).second; | ||
| 3800 | - std::string base = "--" + arg; | ||
| 3801 | - if (oe.param_arg_handler) | ||
| 3802 | - { | ||
| 3803 | - if (zsh_completion) | ||
| 3804 | - { | ||
| 3805 | - // zsh doesn't treat = as a word separator, so add all | ||
| 3806 | - // the options so we don't get a space after the =. | ||
| 3807 | - addChoicesToCompletions(arg, base + "="); | ||
| 3808 | - } | ||
| 3809 | - completions.insert(base + "="); | ||
| 3810 | - } | ||
| 3811 | - if (! oe.parameter_needed) | ||
| 3812 | - { | ||
| 3813 | - completions.insert(base); | ||
| 3814 | - } | ||
| 3815 | - } | ||
| 3816 | -} | ||
| 3817 | - | ||
| 3818 | -void | ||
| 3819 | -ArgParser::handleCompletion() | ||
| 3820 | -{ | ||
| 3821 | - std::string extra_prefix; | ||
| 3822 | - if (this->completions.empty()) | ||
| 3823 | - { | ||
| 3824 | - // Detect --option=... Bash treats the = as a word separator. | ||
| 3825 | - std::string choice_option; | ||
| 3826 | - if (bash_cur.empty() && (bash_prev.length() > 2) && | ||
| 3827 | - (bash_prev.at(0) == '-') && | ||
| 3828 | - (bash_prev.at(1) == '-') && | ||
| 3829 | - (bash_line.at(bash_line.length() - 1) == '=')) | ||
| 3830 | - { | ||
| 3831 | - choice_option = bash_prev.substr(2, std::string::npos); | ||
| 3832 | - } | ||
| 3833 | - else if ((bash_prev == "=") && | ||
| 3834 | - (bash_line.length() > (bash_cur.length() + 1))) | ||
| 3835 | - { | ||
| 3836 | - // We're sitting at --option=x. Find previous option. | ||
| 3837 | - size_t end_mark = bash_line.length() - bash_cur.length() - 1; | ||
| 3838 | - char before_cur = bash_line.at(end_mark); | ||
| 3839 | - if (before_cur == '=') | ||
| 3840 | - { | ||
| 3841 | - size_t space = bash_line.find_last_of(' ', end_mark); | ||
| 3842 | - if (space != std::string::npos) | ||
| 3843 | - { | ||
| 3844 | - std::string candidate = | ||
| 3845 | - bash_line.substr(space + 1, end_mark - space - 1); | ||
| 3846 | - if ((candidate.length() > 2) && | ||
| 3847 | - (candidate.at(0) == '-') && | ||
| 3848 | - (candidate.at(1) == '-')) | ||
| 3849 | - { | ||
| 3850 | - choice_option = | ||
| 3851 | - candidate.substr(2, std::string::npos); | ||
| 3852 | - } | ||
| 3853 | - } | ||
| 3854 | - } | ||
| 3855 | - } | ||
| 3856 | - if (! choice_option.empty()) | ||
| 3857 | - { | ||
| 3858 | - if (zsh_completion) | ||
| 3859 | - { | ||
| 3860 | - // zsh wants --option=choice rather than just choice | ||
| 3861 | - extra_prefix = "--" + choice_option + "="; | ||
| 3862 | - } | ||
| 3863 | - addChoicesToCompletions(choice_option, extra_prefix); | ||
| 3864 | - } | ||
| 3865 | - else if ((! bash_cur.empty()) && (bash_cur.at(0) == '-')) | ||
| 3866 | - { | ||
| 3867 | - addOptionsToCompletions(); | ||
| 3868 | - if (this->argc == 1) | ||
| 3869 | - { | ||
| 3870 | - // Help options are valid only by themselves. | ||
| 3871 | - for (std::map<std::string, OptionEntry>::iterator iter = | ||
| 3872 | - this->help_option_table.begin(); | ||
| 3873 | - iter != this->help_option_table.end(); ++iter) | ||
| 3874 | - { | ||
| 3875 | - this->completions.insert("--" + (*iter).first); | ||
| 3876 | - } | ||
| 3877 | - } | ||
| 3878 | - } | ||
| 3879 | - } | ||
| 3880 | - std::string prefix = extra_prefix + bash_cur; | ||
| 3881 | - for (std::set<std::string>::iterator iter = completions.begin(); | ||
| 3882 | - iter != completions.end(); ++iter) | ||
| 3883 | - { | ||
| 3884 | - if (prefix.empty() || | ||
| 3885 | - ((*iter).substr(0, prefix.length()) == prefix)) | ||
| 3886 | - { | ||
| 3887 | - std::cout << *iter << std::endl; | ||
| 3888 | - } | ||
| 3889 | - } | ||
| 3890 | - exit(0); | ||
| 3891 | -} | ||
| 3892 | - | ||
| 3893 | static void set_qpdf_options(QPDF& pdf, Options& o) | 3258 | static void set_qpdf_options(QPDF& pdf, Options& o) |
| 3894 | { | 3259 | { |
| 3895 | if (o.ignore_xref_streams) | 3260 | if (o.ignore_xref_streams) |
qpdf/qpdf.testcov
| @@ -255,7 +255,7 @@ QPDF not caching overridden objstm object 0 | @@ -255,7 +255,7 @@ QPDF not caching overridden objstm object 0 | ||
| 255 | QPDFWriter original obj non-zero gen 0 | 255 | QPDFWriter original obj non-zero gen 0 |
| 256 | QPDF_optimization indirect outlines 0 | 256 | QPDF_optimization indirect outlines 0 |
| 257 | QPDF xref space 2 | 257 | QPDF xref space 2 |
| 258 | -qpdf pages range omitted at end 0 | 258 | +qpdf pages range omitted at end 1 |
| 259 | qpdf pages range omitted in middle 0 | 259 | qpdf pages range omitted in middle 0 |
| 260 | qpdf npages 0 | 260 | qpdf npages 0 |
| 261 | QPDF already reserved object 0 | 261 | QPDF already reserved object 0 |
| @@ -273,8 +273,6 @@ QPDF resolve failure to null 0 | @@ -273,8 +273,6 @@ QPDF resolve failure to null 0 | ||
| 273 | QPDFWriter preserve unreferenced standard 0 | 273 | QPDFWriter preserve unreferenced standard 0 |
| 274 | QPDFObjectHandle errors in parsecontent 0 | 274 | QPDFObjectHandle errors in parsecontent 0 |
| 275 | qpdf same file error 0 | 275 | qpdf same file error 0 |
| 276 | -qpdf read args from stdin 0 | ||
| 277 | -qpdf read args from file 0 | ||
| 278 | qpdf split-pages %d 0 | 276 | qpdf split-pages %d 0 |
| 279 | qpdf split-pages .pdf 0 | 277 | qpdf split-pages .pdf 0 |
| 280 | qpdf split-pages other 0 | 278 | qpdf split-pages other 0 |
| @@ -362,8 +360,6 @@ QPDFOutlineObjectHelper named dest 0 | @@ -362,8 +360,6 @@ QPDFOutlineObjectHelper named dest 0 | ||
| 362 | QPDFOutlineDocumentHelper name named dest 0 | 360 | QPDFOutlineDocumentHelper name named dest 0 |
| 363 | QPDFOutlineDocumentHelper string named dest 0 | 361 | QPDFOutlineDocumentHelper string named dest 0 |
| 364 | QPDFOutlineObjectHelper loop 0 | 362 | QPDFOutlineObjectHelper loop 0 |
| 365 | -qpdf required parameter 0 | ||
| 366 | -qpdf required choices 0 | ||
| 367 | QPDFObjectHandle merge top type mismatch 0 | 363 | QPDFObjectHandle merge top type mismatch 0 |
| 368 | QPDFObjectHandle merge shallow copy 0 | 364 | QPDFObjectHandle merge shallow copy 0 |
| 369 | QPDFObjectHandle merge array 0 | 365 | QPDFObjectHandle merge array 0 |
| @@ -599,7 +595,6 @@ qpdf copy fields non-first from orig 0 | @@ -599,7 +595,6 @@ qpdf copy fields non-first from orig 0 | ||
| 599 | QPDF resolve duplicated page in insert 0 | 595 | QPDF resolve duplicated page in insert 0 |
| 600 | QPDFWriter preserve object streams 1 | 596 | QPDFWriter preserve object streams 1 |
| 601 | QPDFWriter exclude from object stream 0 | 597 | QPDFWriter exclude from object stream 0 |
| 602 | -check unclosed --pages 1 | ||
| 603 | QPDF_pages findPage not found 0 | 598 | QPDF_pages findPage not found 0 |
| 604 | qpdf overlay page with no resources 0 | 599 | qpdf overlay page with no resources 0 |
| 605 | QPDFObjectHandle check ownership 0 | 600 | QPDFObjectHandle check ownership 0 |
| @@ -629,3 +624,5 @@ qpdf-c called qpdf_oh_replace_stream_data 0 | @@ -629,3 +624,5 @@ qpdf-c called qpdf_oh_replace_stream_data 0 | ||
| 629 | qpdf-c silence oh errors 0 | 624 | qpdf-c silence oh errors 0 |
| 630 | qpdf-c called qpdf_oh_get_binary_string_value 0 | 625 | qpdf-c called qpdf_oh_get_binary_string_value 0 |
| 631 | qpdf-c called qpdf_oh_new_binary_string 0 | 626 | qpdf-c called qpdf_oh_new_binary_string 0 |
| 627 | +qpdf duplicated pages password 0 | ||
| 628 | +qpdf misplaced pages password 0 |
qpdf/qtest/qpdf.test
| @@ -149,7 +149,7 @@ foreach my $c (@completion_tests) | @@ -149,7 +149,7 @@ foreach my $c (@completion_tests) | ||
| 149 | show_ntests(); | 149 | show_ntests(); |
| 150 | # ---------- | 150 | # ---------- |
| 151 | $td->notify("--- Argument Parsing ---"); | 151 | $td->notify("--- Argument Parsing ---"); |
| 152 | -$n_tests += 9; | 152 | +$n_tests += 12; |
| 153 | 153 | ||
| 154 | $td->runtest("required argument", | 154 | $td->runtest("required argument", |
| 155 | {$td->COMMAND => "qpdf --password minimal.pdf"}, | 155 | {$td->COMMAND => "qpdf --password minimal.pdf"}, |
| @@ -182,18 +182,33 @@ $td->runtest("extra overlay filename", | @@ -182,18 +182,33 @@ $td->runtest("extra overlay filename", | ||
| 182 | $td->EXIT_STATUS => 2}, | 182 | $td->EXIT_STATUS => 2}, |
| 183 | $td->NORMALIZE_NEWLINES); | 183 | $td->NORMALIZE_NEWLINES); |
| 184 | $td->runtest("multiple pages options", | 184 | $td->runtest("multiple pages options", |
| 185 | - {$td->COMMAND => "qpdf --pages . -- --pages . --"}, | 185 | + {$td->COMMAND => "qpdf --pages . --password=x -- --pages . --"}, |
| 186 | {$td->REGEXP => ".*--pages may only be specified one time.*", | 186 | {$td->REGEXP => ".*--pages may only be specified one time.*", |
| 187 | $td->EXIT_STATUS => 2}, | 187 | $td->EXIT_STATUS => 2}, |
| 188 | $td->NORMALIZE_NEWLINES); | 188 | $td->NORMALIZE_NEWLINES); |
| 189 | $td->runtest("bad numeric range detects unclosed --pages", | 189 | $td->runtest("bad numeric range detects unclosed --pages", |
| 190 | {$td->COMMAND => "qpdf --pages . --pages . --"}, | 190 | {$td->COMMAND => "qpdf --pages . --pages . --"}, |
| 191 | - {$td->REGEXP => ".*--pages option must be terminated with --.*", | 191 | + {$td->REGEXP => ".*pages options must be terminated with --.*", |
| 192 | $td->EXIT_STATUS => 2}, | 192 | $td->EXIT_STATUS => 2}, |
| 193 | $td->NORMALIZE_NEWLINES); | 193 | $td->NORMALIZE_NEWLINES); |
| 194 | $td->runtest("bad file detected as unclosed --pages", | 194 | $td->runtest("bad file detected as unclosed --pages", |
| 195 | {$td->COMMAND => "qpdf --pages . 1 --xyz out"}, | 195 | {$td->COMMAND => "qpdf --pages . 1 --xyz out"}, |
| 196 | - {$td->REGEXP => ".*--pages option must be terminated with --.*", | 196 | + {$td->REGEXP => ".*pages options must be terminated with --.*", |
| 197 | + $td->EXIT_STATUS => 2}, | ||
| 198 | + $td->NORMALIZE_NEWLINES); | ||
| 199 | +$td->runtest("misplaced pages password 1", | ||
| 200 | + {$td->COMMAND => "qpdf --pages . 1 --password=z --"}, | ||
| 201 | + {$td->REGEXP => ".*password must immediately follow a file name.*", | ||
| 202 | + $td->EXIT_STATUS => 2}, | ||
| 203 | + $td->NORMALIZE_NEWLINES); | ||
| 204 | +$td->runtest("misplaced pages password 2", | ||
| 205 | + {$td->COMMAND => "qpdf --pages --password=z . 1 --"}, | ||
| 206 | + {$td->REGEXP => ".*password must immediately follow a file name.*", | ||
| 207 | + $td->EXIT_STATUS => 2}, | ||
| 208 | + $td->NORMALIZE_NEWLINES); | ||
| 209 | +$td->runtest("duplicated pages password", | ||
| 210 | + {$td->COMMAND => "qpdf --pages . --password=z --password=z --"}, | ||
| 211 | + {$td->REGEXP => ".*password already specified.*", | ||
| 197 | $td->EXIT_STATUS => 2}, | 212 | $td->EXIT_STATUS => 2}, |
| 198 | $td->NORMALIZE_NEWLINES); | 213 | $td->NORMALIZE_NEWLINES); |
| 199 | 214 |