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 | 27 | #include <qpdf/QPDFSystemError.hh> |
| 28 | 28 | #include <qpdf/QPDFCryptoProvider.hh> |
| 29 | 29 | #include <qpdf/QPDFEmbeddedFileDocumentHelper.hh> |
| 30 | +#include <qpdf/QPDFArgParser.hh> | |
| 30 | 31 | |
| 31 | 32 | #include <qpdf/QPDFWriter.hh> |
| 32 | 33 | #include <qpdf/QIntC.hh> |
| ... | ... | @@ -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 | 744 | class ArgParser |
| 752 | 745 | { |
| 753 | 746 | public: |
| ... | ... | @@ -755,38 +748,18 @@ class ArgParser |
| 755 | 748 | void parseOptions(); |
| 756 | 749 | |
| 757 | 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 | 760 | void argHelp(); |
| 786 | 761 | void argVersion(); |
| 787 | 762 | void argCopyright(); |
| 788 | - void argCompletionBash(); | |
| 789 | - void argCompletionZsh(); | |
| 790 | 763 | void argJsonHelp(); |
| 791 | 764 | void argShowCrypto(); |
| 792 | 765 | void argPositional(char* arg); |
| ... | ... | @@ -804,6 +777,9 @@ class ArgParser |
| 804 | 777 | void argCopyEncryption(char* parameter); |
| 805 | 778 | void argEncryptionFilePassword(char* parameter); |
| 806 | 779 | void argPages(); |
| 780 | + void argPagesPassword(char* parameter); | |
| 781 | + void argPagesPositional(char* parameter); | |
| 782 | + void argEndPages(); | |
| 807 | 783 | void argUnderlay(); |
| 808 | 784 | void argOverlay(); |
| 809 | 785 | void argRotate(char* parameter); |
| ... | ... | @@ -884,6 +860,7 @@ class ArgParser |
| 884 | 860 | void arg128UseAes(char* parameter); |
| 885 | 861 | void arg128ForceV4(); |
| 886 | 862 | void arg256ForceR5(); |
| 863 | + void argEncryptPositional(char* arg); | |
| 887 | 864 | void argEndEncrypt(); |
| 888 | 865 | void argUOpositional(char* arg); |
| 889 | 866 | void argUOto(char* parameter); |
| ... | ... | @@ -909,329 +886,257 @@ class ArgParser |
| 909 | 886 | void argEndCopyAttachments(); |
| 910 | 887 | |
| 911 | 888 | void usage(std::string const& message); |
| 912 | - void checkCompletion(); | |
| 913 | 889 | void initOptionTable(); |
| 914 | - void handleArgFileArguments(); | |
| 915 | - void handleBashArguments(); | |
| 916 | - void readArgsFromFile(char const* filename); | |
| 917 | 890 | void doFinalChecks(); |
| 918 | - void addOptionsToCompletions(); | |
| 919 | - void addChoicesToCompletions(std::string const&, std::string const&); | |
| 920 | - void handleCompletion(); | |
| 921 | - std::vector<PageSpec> parsePagesOptions(); | |
| 922 | 891 | void parseUnderOverlayOptions(UnderOverlay*); |
| 923 | 892 | void parseRotationParameter(std::string const&); |
| 924 | 893 | std::vector<int> parseNumrange(char const* range, int max, |
| 925 | 894 | bool throw_error = false); |
| 926 | 895 | |
| 927 | - int argc; | |
| 928 | - char** argv; | |
| 896 | + QPDFArgParser ap; | |
| 929 | 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 | 902 | ArgParser::ArgParser(int argc, char* argv[], Options& o) : |
| 954 | - argc(argc), | |
| 955 | - argv(argv), | |
| 903 | + ap(argc, argv, "QPDF_EXECUTABLE"), | |
| 956 | 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 | 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 | 910 | void |
| 1015 | 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 | 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 | 942 | char const* password_mode_choices[] = |
| 1041 | 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 | 952 | char const* stream_data_choices[] = |
| 1052 | 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 | 971 | char const* decode_level_choices[] = |
| 1070 | 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 | 978 | char const* object_streams_choices[] = { |
| 1077 | 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 | 990 | char const* remove_unref_choices[] = { |
| 1087 | 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 | 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 | 1032 | // The list of selectable top-level keys id duplicated in three |
| 1132 | 1033 | // places: json_schema, do_json, and initOptionTable. |
| 1133 | 1034 | char const* json_key_choices[] = { |
| 1134 | 1035 | "objects", "objectinfo", "pages", "pagelabels", "outlines", |
| 1135 | 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 | 1142 | void |
| ... | ... | @@ -1818,58 +1723,6 @@ ArgParser::argHelp() |
| 1818 | 1723 | } |
| 1819 | 1724 | |
| 1820 | 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 | 1726 | ArgParser::argJsonHelp() |
| 1874 | 1727 | { |
| 1875 | 1728 | // Make sure the output looks right on an 80-column display. |
| ... | ... | @@ -1962,50 +1815,54 @@ ArgParser::argLinearize() |
| 1962 | 1815 | void |
| 1963 | 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 | 1851 | if (len_str == "40") |
| 1995 | 1852 | { |
| 1996 | 1853 | o.keylen = 40; |
| 1997 | - this->option_table = &(this->encrypt40_option_table); | |
| 1854 | + this->ap.selectOptionTable(O_ENCRYPT_40); | |
| 1998 | 1855 | } |
| 1999 | 1856 | else if (len_str == "128") |
| 2000 | 1857 | { |
| 2001 | 1858 | o.keylen = 128; |
| 2002 | - this->option_table = &(this->encrypt128_option_table); | |
| 1859 | + this->ap.selectOptionTable(O_ENCRYPT_128); | |
| 2003 | 1860 | } |
| 2004 | 1861 | else if (len_str == "256") |
| 2005 | 1862 | { |
| 2006 | 1863 | o.keylen = 256; |
| 2007 | 1864 | o.use_aes = true; |
| 2008 | - this->option_table = &(this->encrypt256_option_table); | |
| 1865 | + this->ap.selectOptionTable(O_ENCRYPT_256); | |
| 2009 | 1866 | } |
| 2010 | 1867 | else |
| 2011 | 1868 | { |
| ... | ... | @@ -2096,12 +1953,116 @@ ArgParser::argCollate(char* parameter) |
| 2096 | 1953 | void |
| 2097 | 1954 | ArgParser::argPages() |
| 2098 | 1955 | { |
| 2099 | - ++cur_arg; | |
| 2100 | 1956 | if (! o.page_specs.empty()) |
| 2101 | 1957 | { |
| 2102 | 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 | 2066 | if (o.page_specs.empty()) |
| 2106 | 2067 | { |
| 2107 | 2068 | usage("--pages: no page specifications given"); |
| ... | ... | @@ -2155,15 +2116,15 @@ ArgParser::argRemoveAttachment(char* parameter) |
| 2155 | 2116 | void |
| 2156 | 2117 | ArgParser::argAddAttachment() |
| 2157 | 2118 | { |
| 2158 | - this->option_table = &(this->add_attachment_option_table); | |
| 2159 | 2119 | o.attachments_to_add.push_back(AddAttachment()); |
| 2120 | + this->ap.selectOptionTable(O_ATTACHMENT); | |
| 2160 | 2121 | } |
| 2161 | 2122 | |
| 2162 | 2123 | void |
| 2163 | 2124 | ArgParser::argCopyAttachments() |
| 2164 | 2125 | { |
| 2165 | - this->option_table = &(this->copy_attachments_option_table); | |
| 2166 | 2126 | o.attachments_to_copy.push_back(CopyAttachmentFrom()); |
| 2127 | + this->ap.selectOptionTable(O_COPY_ATTACHMENT); | |
| 2167 | 2128 | } |
| 2168 | 2129 | |
| 2169 | 2130 | void |
| ... | ... | @@ -2743,7 +2704,6 @@ ArgParser::argEndEncrypt() |
| 2743 | 2704 | o.encrypt = true; |
| 2744 | 2705 | o.decrypt = false; |
| 2745 | 2706 | o.copy_encryption = false; |
| 2746 | - this->option_table = &(this->main_option_table); | |
| 2747 | 2707 | } |
| 2748 | 2708 | |
| 2749 | 2709 | void |
| ... | ... | @@ -2795,7 +2755,6 @@ ArgParser::argUOpassword(char* parameter) |
| 2795 | 2755 | void |
| 2796 | 2756 | ArgParser::argEndUnderOverlay() |
| 2797 | 2757 | { |
| 2798 | - this->option_table = &(this->main_option_table); | |
| 2799 | 2758 | if (0 == o.under_overlay->filename) |
| 2800 | 2759 | { |
| 2801 | 2760 | usage(o.under_overlay->which + " file not specified"); |
| ... | ... | @@ -2888,7 +2847,6 @@ ArgParser::argEndAddAttachment() |
| 2888 | 2847 | { |
| 2889 | 2848 | static std::string now = QUtil::qpdf_time_to_pdf_time( |
| 2890 | 2849 | QUtil::get_current_qpdf_time()); |
| 2891 | - this->option_table = &(this->main_option_table); | |
| 2892 | 2850 | auto& cur = o.attachments_to_add.back(); |
| 2893 | 2851 | if (cur.path.empty()) |
| 2894 | 2852 | { |
| ... | ... | @@ -2938,161 +2896,12 @@ ArgParser::argCApassword(char* parameter) |
| 2938 | 2896 | void |
| 2939 | 2897 | ArgParser::argEndCopyAttachments() |
| 2940 | 2898 | { |
| 2941 | - this->option_table = &(this->main_option_table); | |
| 2942 | 2899 | if (o.attachments_to_copy.back().path.empty()) |
| 2943 | 2900 | { |
| 2944 | 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 | 2905 | void usageExit(std::string const& msg) |
| 3097 | 2906 | { |
| 3098 | 2907 | std::cerr |
| ... | ... | @@ -3108,7 +2917,7 @@ void usageExit(std::string const& msg) |
| 3108 | 2917 | void |
| 3109 | 2918 | ArgParser::usage(std::string const& message) |
| 3110 | 2919 | { |
| 3111 | - if (this->bash_completion) | |
| 2920 | + if (this->ap.isCompleting()) | |
| 3112 | 2921 | { |
| 3113 | 2922 | // This will cause bash to fall back to regular file completion. |
| 3114 | 2923 | exit(0); |
| ... | ... | @@ -3234,101 +3043,11 @@ ArgParser::parseNumrange(char const* range, int max, bool throw_error) |
| 3234 | 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 | 3046 | void |
| 3328 | 3047 | ArgParser::parseUnderOverlayOptions(UnderOverlay* uo) |
| 3329 | 3048 | { |
| 3330 | 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 | 3053 | QPDFPageData::QPDFPageData(std::string const& filename, |
| ... | ... | @@ -3374,28 +3093,6 @@ static void parse_version(std::string const& full_version_string, |
| 3374 | 3093 | } |
| 3375 | 3094 | |
| 3376 | 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 | 3096 | ArgParser::parseRotationParameter(std::string const& parameter) |
| 3400 | 3097 | { |
| 3401 | 3098 | std::string angle_str; |
| ... | ... | @@ -3462,232 +3159,21 @@ ArgParser::parseRotationParameter(std::string const& parameter) |
| 3462 | 3159 | } |
| 3463 | 3160 | |
| 3464 | 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 | 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 | 3174 | void |
| 3685 | 3175 | ArgParser::doFinalChecks() |
| 3686 | 3176 | { |
| 3687 | - if (this->option_table != &(this->main_option_table)) | |
| 3688 | - { | |
| 3689 | - usage("missing -- at end of options"); | |
| 3690 | - } | |
| 3691 | 3177 | if (o.replace_input) |
| 3692 | 3178 | { |
| 3693 | 3179 | if (o.outfilename) |
| ... | ... | @@ -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 | 3258 | static void set_qpdf_options(QPDF& pdf, Options& o) |
| 3894 | 3259 | { |
| 3895 | 3260 | if (o.ignore_xref_streams) | ... | ... |
qpdf/qpdf.testcov
| ... | ... | @@ -255,7 +255,7 @@ QPDF not caching overridden objstm object 0 |
| 255 | 255 | QPDFWriter original obj non-zero gen 0 |
| 256 | 256 | QPDF_optimization indirect outlines 0 |
| 257 | 257 | QPDF xref space 2 |
| 258 | -qpdf pages range omitted at end 0 | |
| 258 | +qpdf pages range omitted at end 1 | |
| 259 | 259 | qpdf pages range omitted in middle 0 |
| 260 | 260 | qpdf npages 0 |
| 261 | 261 | QPDF already reserved object 0 |
| ... | ... | @@ -273,8 +273,6 @@ QPDF resolve failure to null 0 |
| 273 | 273 | QPDFWriter preserve unreferenced standard 0 |
| 274 | 274 | QPDFObjectHandle errors in parsecontent 0 |
| 275 | 275 | qpdf same file error 0 |
| 276 | -qpdf read args from stdin 0 | |
| 277 | -qpdf read args from file 0 | |
| 278 | 276 | qpdf split-pages %d 0 |
| 279 | 277 | qpdf split-pages .pdf 0 |
| 280 | 278 | qpdf split-pages other 0 |
| ... | ... | @@ -362,8 +360,6 @@ QPDFOutlineObjectHelper named dest 0 |
| 362 | 360 | QPDFOutlineDocumentHelper name named dest 0 |
| 363 | 361 | QPDFOutlineDocumentHelper string named dest 0 |
| 364 | 362 | QPDFOutlineObjectHelper loop 0 |
| 365 | -qpdf required parameter 0 | |
| 366 | -qpdf required choices 0 | |
| 367 | 363 | QPDFObjectHandle merge top type mismatch 0 |
| 368 | 364 | QPDFObjectHandle merge shallow copy 0 |
| 369 | 365 | QPDFObjectHandle merge array 0 |
| ... | ... | @@ -599,7 +595,6 @@ qpdf copy fields non-first from orig 0 |
| 599 | 595 | QPDF resolve duplicated page in insert 0 |
| 600 | 596 | QPDFWriter preserve object streams 1 |
| 601 | 597 | QPDFWriter exclude from object stream 0 |
| 602 | -check unclosed --pages 1 | |
| 603 | 598 | QPDF_pages findPage not found 0 |
| 604 | 599 | qpdf overlay page with no resources 0 |
| 605 | 600 | QPDFObjectHandle check ownership 0 |
| ... | ... | @@ -629,3 +624,5 @@ qpdf-c called qpdf_oh_replace_stream_data 0 |
| 629 | 624 | qpdf-c silence oh errors 0 |
| 630 | 625 | qpdf-c called qpdf_oh_get_binary_string_value 0 |
| 631 | 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 | 149 | show_ntests(); |
| 150 | 150 | # ---------- |
| 151 | 151 | $td->notify("--- Argument Parsing ---"); |
| 152 | -$n_tests += 9; | |
| 152 | +$n_tests += 12; | |
| 153 | 153 | |
| 154 | 154 | $td->runtest("required argument", |
| 155 | 155 | {$td->COMMAND => "qpdf --password minimal.pdf"}, |
| ... | ... | @@ -182,18 +182,33 @@ $td->runtest("extra overlay filename", |
| 182 | 182 | $td->EXIT_STATUS => 2}, |
| 183 | 183 | $td->NORMALIZE_NEWLINES); |
| 184 | 184 | $td->runtest("multiple pages options", |
| 185 | - {$td->COMMAND => "qpdf --pages . -- --pages . --"}, | |
| 185 | + {$td->COMMAND => "qpdf --pages . --password=x -- --pages . --"}, | |
| 186 | 186 | {$td->REGEXP => ".*--pages may only be specified one time.*", |
| 187 | 187 | $td->EXIT_STATUS => 2}, |
| 188 | 188 | $td->NORMALIZE_NEWLINES); |
| 189 | 189 | $td->runtest("bad numeric range detects unclosed --pages", |
| 190 | 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 | 192 | $td->EXIT_STATUS => 2}, |
| 193 | 193 | $td->NORMALIZE_NEWLINES); |
| 194 | 194 | $td->runtest("bad file detected as unclosed --pages", |
| 195 | 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 | 212 | $td->EXIT_STATUS => 2}, |
| 198 | 213 | $td->NORMALIZE_NEWLINES); |
| 199 | 214 | ... | ... |