Commit 151206603b80225ff9f59235bf38a3677ebbe6d2
1 parent
6580ffe9
Move argument parsing into a class
Showing
1 changed file
with
157 additions
and
103 deletions
qpdf/qpdf.cc
| @@ -241,6 +241,78 @@ ProgressReporter::reportProgress(int percentage) | @@ -241,6 +241,78 @@ ProgressReporter::reportProgress(int percentage) | ||
| 241 | << percentage << "%" << std::endl; | 241 | << percentage << "%" << std::endl; |
| 242 | } | 242 | } |
| 243 | 243 | ||
| 244 | +// This is not a general-purpose argument parser. It is tightly | ||
| 245 | +// crafted to work with qpdf. qpdf's command-line syntax is very | ||
| 246 | +// complex because of its long history, and it doesn't really follow | ||
| 247 | +// any kind of normal standard for arguments, but I don't want to | ||
| 248 | +// break compatibility by changing what constitutes a valid command. | ||
| 249 | +// This class is intended to simplify the argument parsing code and | ||
| 250 | +// also to make it possible to add bash completion support while | ||
| 251 | +// guaranteeing consistency with the actual argument syntax. | ||
| 252 | +class ArgParser | ||
| 253 | +{ | ||
| 254 | + public: | ||
| 255 | + ArgParser(int argc, char* argv[], Options& o); | ||
| 256 | + void parseOptions(); | ||
| 257 | + | ||
| 258 | + private: | ||
| 259 | + void usage(std::string const& message); | ||
| 260 | + void handleHelpVersion(); | ||
| 261 | + void handleArgFileArguments(); | ||
| 262 | + void readArgsFromFile(char const* filename); | ||
| 263 | + void parseEncryptOptions(int& cur_arg); | ||
| 264 | + std::vector<PageSpec> parsePagesOptions(int& cur_arg); | ||
| 265 | + void parseRotationParameter(std::string const&); | ||
| 266 | + std::vector<int> parseNumrange(char const* range, int max, | ||
| 267 | + bool throw_error = false); | ||
| 268 | + | ||
| 269 | + static char const* help; | ||
| 270 | + | ||
| 271 | + int argc; | ||
| 272 | + char** argv; | ||
| 273 | + Options& o; | ||
| 274 | + | ||
| 275 | + std::vector<PointerHolder<char> > new_argv; | ||
| 276 | + PointerHolder<char*> argv_ph; | ||
| 277 | +}; | ||
| 278 | + | ||
| 279 | +ArgParser::ArgParser(int argc, char* argv[], Options& o) : | ||
| 280 | + argc(argc), | ||
| 281 | + argv(argv), | ||
| 282 | + o(o) | ||
| 283 | +{ | ||
| 284 | +} | ||
| 285 | + | ||
| 286 | +void | ||
| 287 | +ArgParser::handleArgFileArguments() | ||
| 288 | +{ | ||
| 289 | + // Support reading arguments from files. Create a new argv. Ensure | ||
| 290 | + // that argv itself as well as all its contents are automatically | ||
| 291 | + // deleted by using PointerHolder objects to back the pointers in | ||
| 292 | + // argv. | ||
| 293 | + new_argv.push_back(PointerHolder<char>(true, QUtil::copy_string(argv[0]))); | ||
| 294 | + for (int i = 1; i < argc; ++i) | ||
| 295 | + { | ||
| 296 | + if ((strlen(argv[i]) > 1) && (argv[i][0] == '@')) | ||
| 297 | + { | ||
| 298 | + readArgsFromFile(1+argv[i]); | ||
| 299 | + } | ||
| 300 | + else | ||
| 301 | + { | ||
| 302 | + new_argv.push_back( | ||
| 303 | + PointerHolder<char>(true, QUtil::copy_string(argv[i]))); | ||
| 304 | + } | ||
| 305 | + } | ||
| 306 | + argv_ph = PointerHolder<char*>(true, new char*[1+new_argv.size()]); | ||
| 307 | + argv = argv_ph.getPointer(); | ||
| 308 | + for (size_t i = 0; i < new_argv.size(); ++i) | ||
| 309 | + { | ||
| 310 | + argv[i] = new_argv.at(i).getPointer(); | ||
| 311 | + } | ||
| 312 | + argc = static_cast<int>(new_argv.size()); | ||
| 313 | + argv[argc] = 0; | ||
| 314 | +} | ||
| 315 | + | ||
| 244 | // Note: let's not be too noisy about documenting the fact that this | 316 | // Note: let's not be too noisy about documenting the fact that this |
| 245 | // software purposely fails to enforce the distinction between user | 317 | // software purposely fails to enforce the distinction between user |
| 246 | // and owner passwords. A user password is sufficient to gain full | 318 | // and owner passwords. A user password is sufficient to gain full |
| @@ -250,7 +322,7 @@ ProgressReporter::reportProgress(int percentage) | @@ -250,7 +322,7 @@ ProgressReporter::reportProgress(int percentage) | ||
| 250 | // (Setting this value requires the owner password.) The | 322 | // (Setting this value requires the owner password.) The |
| 251 | // documentation discusses this as well. | 323 | // documentation discusses this as well. |
| 252 | 324 | ||
| 253 | -static char const* help = "\ | 325 | +char const* ArgParser::help = "\ |
| 254 | \n\ | 326 | \n\ |
| 255 | Usage: qpdf [ options ] { infilename | --empty } [ outfilename ]\n\ | 327 | Usage: qpdf [ options ] { infilename | --empty } [ outfilename ]\n\ |
| 256 | \n\ | 328 | \n\ |
| @@ -540,7 +612,7 @@ exits with a status of 3. If warnings would have been issued but --no-warn\n\ | @@ -540,7 +612,7 @@ exits with a status of 3. If warnings would have been issued but --no-warn\n\ | ||
| 540 | was given, an exit status of 3 is still used.\n\ | 612 | was given, an exit status of 3 is still used.\n\ |
| 541 | \n"; | 613 | \n"; |
| 542 | 614 | ||
| 543 | -void usage(std::string const& msg) | 615 | +void usageExit(std::string const& msg) |
| 544 | { | 616 | { |
| 545 | std::cerr | 617 | std::cerr |
| 546 | << std::endl | 618 | << std::endl |
| @@ -552,6 +624,12 @@ void usage(std::string const& msg) | @@ -552,6 +624,12 @@ void usage(std::string const& msg) | ||
| 552 | exit(EXIT_ERROR); | 624 | exit(EXIT_ERROR); |
| 553 | } | 625 | } |
| 554 | 626 | ||
| 627 | +void | ||
| 628 | +ArgParser::usage(std::string const& message) | ||
| 629 | +{ | ||
| 630 | + usageExit(message); | ||
| 631 | +} | ||
| 632 | + | ||
| 555 | static JSON json_schema() | 633 | static JSON json_schema() |
| 556 | { | 634 | { |
| 557 | // This JSON object doubles as a schema and as documentation for | 635 | // This JSON object doubles as a schema and as documentation for |
| @@ -707,8 +785,8 @@ static void show_encryption(QPDF& pdf, Options& o) | @@ -707,8 +785,8 @@ static void show_encryption(QPDF& pdf, Options& o) | ||
| 707 | } | 785 | } |
| 708 | } | 786 | } |
| 709 | 787 | ||
| 710 | -static std::vector<int> parse_numrange(char const* range, int max, | ||
| 711 | - bool throw_error = false) | 788 | +std::vector<int> |
| 789 | +ArgParser::parseNumrange(char const* range, int max, bool throw_error) | ||
| 712 | { | 790 | { |
| 713 | try | 791 | try |
| 714 | { | 792 | { |
| @@ -728,35 +806,28 @@ static std::vector<int> parse_numrange(char const* range, int max, | @@ -728,35 +806,28 @@ static std::vector<int> parse_numrange(char const* range, int max, | ||
| 728 | return std::vector<int>(); | 806 | return std::vector<int>(); |
| 729 | } | 807 | } |
| 730 | 808 | ||
| 731 | -static void | ||
| 732 | -parse_encrypt_options( | ||
| 733 | - int argc, char* argv[], int& cur_arg, | ||
| 734 | - std::string& user_password, std::string& owner_password, int& keylen, | ||
| 735 | - bool& r2_print, bool& r2_modify, bool& r2_extract, bool& r2_annotate, | ||
| 736 | - bool& r3_accessibility, bool& r3_extract, | ||
| 737 | - qpdf_r3_print_e& r3_print, qpdf_r3_modify_e& r3_modify, | ||
| 738 | - bool& force_V4, bool& cleartext_metadata, bool& use_aes, | ||
| 739 | - bool& force_R5) | 809 | +void |
| 810 | +ArgParser::parseEncryptOptions(int& cur_arg) | ||
| 740 | { | 811 | { |
| 741 | if (cur_arg + 3 >= argc) | 812 | if (cur_arg + 3 >= argc) |
| 742 | { | 813 | { |
| 743 | usage("insufficient arguments to --encrypt"); | 814 | usage("insufficient arguments to --encrypt"); |
| 744 | } | 815 | } |
| 745 | - user_password = argv[cur_arg++]; | ||
| 746 | - owner_password = argv[cur_arg++]; | 816 | + o.user_password = argv[cur_arg++]; |
| 817 | + o.owner_password = argv[cur_arg++]; | ||
| 747 | std::string len_str = argv[cur_arg++]; | 818 | std::string len_str = argv[cur_arg++]; |
| 748 | if (len_str == "40") | 819 | if (len_str == "40") |
| 749 | { | 820 | { |
| 750 | - keylen = 40; | 821 | + o.keylen = 40; |
| 751 | } | 822 | } |
| 752 | else if (len_str == "128") | 823 | else if (len_str == "128") |
| 753 | { | 824 | { |
| 754 | - keylen = 128; | 825 | + o.keylen = 128; |
| 755 | } | 826 | } |
| 756 | else if (len_str == "256") | 827 | else if (len_str == "256") |
| 757 | { | 828 | { |
| 758 | - keylen = 256; | ||
| 759 | - use_aes = true; | 829 | + o.keylen = 256; |
| 830 | + o.use_aes = true; | ||
| 760 | } | 831 | } |
| 761 | else | 832 | else |
| 762 | { | 833 | { |
| @@ -798,15 +869,15 @@ parse_encrypt_options( | @@ -798,15 +869,15 @@ parse_encrypt_options( | ||
| 798 | usage("--print must be given as --print=option"); | 869 | usage("--print must be given as --print=option"); |
| 799 | } | 870 | } |
| 800 | std::string val = parameter; | 871 | std::string val = parameter; |
| 801 | - if (keylen == 40) | 872 | + if (o.keylen == 40) |
| 802 | { | 873 | { |
| 803 | if (val == "y") | 874 | if (val == "y") |
| 804 | { | 875 | { |
| 805 | - r2_print = true; | 876 | + o.r2_print = true; |
| 806 | } | 877 | } |
| 807 | else if (val == "n") | 878 | else if (val == "n") |
| 808 | { | 879 | { |
| 809 | - r2_print = false; | 880 | + o.r2_print = false; |
| 810 | } | 881 | } |
| 811 | else | 882 | else |
| 812 | { | 883 | { |
| @@ -817,15 +888,15 @@ parse_encrypt_options( | @@ -817,15 +888,15 @@ parse_encrypt_options( | ||
| 817 | { | 888 | { |
| 818 | if (val == "full") | 889 | if (val == "full") |
| 819 | { | 890 | { |
| 820 | - r3_print = qpdf_r3p_full; | 891 | + o.r3_print = qpdf_r3p_full; |
| 821 | } | 892 | } |
| 822 | else if (val == "low") | 893 | else if (val == "low") |
| 823 | { | 894 | { |
| 824 | - r3_print = qpdf_r3p_low; | 895 | + o.r3_print = qpdf_r3p_low; |
| 825 | } | 896 | } |
| 826 | else if (val == "none") | 897 | else if (val == "none") |
| 827 | { | 898 | { |
| 828 | - r3_print = qpdf_r3p_none; | 899 | + o.r3_print = qpdf_r3p_none; |
| 829 | } | 900 | } |
| 830 | else | 901 | else |
| 831 | { | 902 | { |
| @@ -840,15 +911,15 @@ parse_encrypt_options( | @@ -840,15 +911,15 @@ parse_encrypt_options( | ||
| 840 | usage("--modify must be given as --modify=option"); | 911 | usage("--modify must be given as --modify=option"); |
| 841 | } | 912 | } |
| 842 | std::string val = parameter; | 913 | std::string val = parameter; |
| 843 | - if (keylen == 40) | 914 | + if (o.keylen == 40) |
| 844 | { | 915 | { |
| 845 | if (val == "y") | 916 | if (val == "y") |
| 846 | { | 917 | { |
| 847 | - r2_modify = true; | 918 | + o.r2_modify = true; |
| 848 | } | 919 | } |
| 849 | else if (val == "n") | 920 | else if (val == "n") |
| 850 | { | 921 | { |
| 851 | - r2_modify = false; | 922 | + o.r2_modify = false; |
| 852 | } | 923 | } |
| 853 | else | 924 | else |
| 854 | { | 925 | { |
| @@ -859,23 +930,23 @@ parse_encrypt_options( | @@ -859,23 +930,23 @@ parse_encrypt_options( | ||
| 859 | { | 930 | { |
| 860 | if (val == "all") | 931 | if (val == "all") |
| 861 | { | 932 | { |
| 862 | - r3_modify = qpdf_r3m_all; | 933 | + o.r3_modify = qpdf_r3m_all; |
| 863 | } | 934 | } |
| 864 | else if (val == "annotate") | 935 | else if (val == "annotate") |
| 865 | { | 936 | { |
| 866 | - r3_modify = qpdf_r3m_annotate; | 937 | + o.r3_modify = qpdf_r3m_annotate; |
| 867 | } | 938 | } |
| 868 | else if (val == "form") | 939 | else if (val == "form") |
| 869 | { | 940 | { |
| 870 | - r3_modify = qpdf_r3m_form; | 941 | + o.r3_modify = qpdf_r3m_form; |
| 871 | } | 942 | } |
| 872 | else if (val == "assembly") | 943 | else if (val == "assembly") |
| 873 | { | 944 | { |
| 874 | - r3_modify = qpdf_r3m_assembly; | 945 | + o.r3_modify = qpdf_r3m_assembly; |
| 875 | } | 946 | } |
| 876 | else if (val == "none") | 947 | else if (val == "none") |
| 877 | { | 948 | { |
| 878 | - r3_modify = qpdf_r3m_none; | 949 | + o.r3_modify = qpdf_r3m_none; |
| 879 | } | 950 | } |
| 880 | else | 951 | else |
| 881 | { | 952 | { |
| @@ -903,13 +974,13 @@ parse_encrypt_options( | @@ -903,13 +974,13 @@ parse_encrypt_options( | ||
| 903 | { | 974 | { |
| 904 | usage("invalid -extract parameter"); | 975 | usage("invalid -extract parameter"); |
| 905 | } | 976 | } |
| 906 | - if (keylen == 40) | 977 | + if (o.keylen == 40) |
| 907 | { | 978 | { |
| 908 | - r2_extract = result; | 979 | + o.r2_extract = result; |
| 909 | } | 980 | } |
| 910 | else | 981 | else |
| 911 | { | 982 | { |
| 912 | - r3_extract = result; | 983 | + o.r3_extract = result; |
| 913 | } | 984 | } |
| 914 | } | 985 | } |
| 915 | else if (strcmp(arg, "annotate") == 0) | 986 | else if (strcmp(arg, "annotate") == 0) |
| @@ -932,9 +1003,9 @@ parse_encrypt_options( | @@ -932,9 +1003,9 @@ parse_encrypt_options( | ||
| 932 | { | 1003 | { |
| 933 | usage("invalid -annotate parameter"); | 1004 | usage("invalid -annotate parameter"); |
| 934 | } | 1005 | } |
| 935 | - if (keylen == 40) | 1006 | + if (o.keylen == 40) |
| 936 | { | 1007 | { |
| 937 | - r2_annotate = result; | 1008 | + o.r2_annotate = result; |
| 938 | } | 1009 | } |
| 939 | else | 1010 | else |
| 940 | { | 1011 | { |
| @@ -962,13 +1033,13 @@ parse_encrypt_options( | @@ -962,13 +1033,13 @@ parse_encrypt_options( | ||
| 962 | { | 1033 | { |
| 963 | usage("invalid -accessibility parameter"); | 1034 | usage("invalid -accessibility parameter"); |
| 964 | } | 1035 | } |
| 965 | - if (keylen == 40) | 1036 | + if (o.keylen == 40) |
| 966 | { | 1037 | { |
| 967 | usage("-accessibility invalid for 40-bit keys"); | 1038 | usage("-accessibility invalid for 40-bit keys"); |
| 968 | } | 1039 | } |
| 969 | else | 1040 | else |
| 970 | { | 1041 | { |
| 971 | - r3_accessibility = result; | 1042 | + o.r3_accessibility = result; |
| 972 | } | 1043 | } |
| 973 | } | 1044 | } |
| 974 | else if (strcmp(arg, "cleartext-metadata") == 0) | 1045 | else if (strcmp(arg, "cleartext-metadata") == 0) |
| @@ -977,13 +1048,13 @@ parse_encrypt_options( | @@ -977,13 +1048,13 @@ parse_encrypt_options( | ||
| 977 | { | 1048 | { |
| 978 | usage("--cleartext-metadata does not take a parameter"); | 1049 | usage("--cleartext-metadata does not take a parameter"); |
| 979 | } | 1050 | } |
| 980 | - if (keylen == 40) | 1051 | + if (o.keylen == 40) |
| 981 | { | 1052 | { |
| 982 | usage("--cleartext-metadata is invalid for 40-bit keys"); | 1053 | usage("--cleartext-metadata is invalid for 40-bit keys"); |
| 983 | } | 1054 | } |
| 984 | else | 1055 | else |
| 985 | { | 1056 | { |
| 986 | - cleartext_metadata = true; | 1057 | + o.cleartext_metadata = true; |
| 987 | } | 1058 | } |
| 988 | } | 1059 | } |
| 989 | else if (strcmp(arg, "force-V4") == 0) | 1060 | else if (strcmp(arg, "force-V4") == 0) |
| @@ -992,13 +1063,13 @@ parse_encrypt_options( | @@ -992,13 +1063,13 @@ parse_encrypt_options( | ||
| 992 | { | 1063 | { |
| 993 | usage("--force-V4 does not take a parameter"); | 1064 | usage("--force-V4 does not take a parameter"); |
| 994 | } | 1065 | } |
| 995 | - if (keylen != 128) | 1066 | + if (o.keylen != 128) |
| 996 | { | 1067 | { |
| 997 | usage("--force-V4 is invalid only for 128-bit keys"); | 1068 | usage("--force-V4 is invalid only for 128-bit keys"); |
| 998 | } | 1069 | } |
| 999 | else | 1070 | else |
| 1000 | { | 1071 | { |
| 1001 | - force_V4 = true; | 1072 | + o.force_V4 = true; |
| 1002 | } | 1073 | } |
| 1003 | } | 1074 | } |
| 1004 | else if (strcmp(arg, "force-R5") == 0) | 1075 | else if (strcmp(arg, "force-R5") == 0) |
| @@ -1007,13 +1078,13 @@ parse_encrypt_options( | @@ -1007,13 +1078,13 @@ parse_encrypt_options( | ||
| 1007 | { | 1078 | { |
| 1008 | usage("--force-R5 does not take a parameter"); | 1079 | usage("--force-R5 does not take a parameter"); |
| 1009 | } | 1080 | } |
| 1010 | - if (keylen != 256) | 1081 | + if (o.keylen != 256) |
| 1011 | { | 1082 | { |
| 1012 | usage("--force-R5 is invalid only for 256-bit keys"); | 1083 | usage("--force-R5 is invalid only for 256-bit keys"); |
| 1013 | } | 1084 | } |
| 1014 | else | 1085 | else |
| 1015 | { | 1086 | { |
| 1016 | - force_R5 = true; | 1087 | + o.force_R5 = true; |
| 1017 | } | 1088 | } |
| 1018 | } | 1089 | } |
| 1019 | else if (strcmp(arg, "use-aes") == 0) | 1090 | else if (strcmp(arg, "use-aes") == 0) |
| @@ -1036,11 +1107,11 @@ parse_encrypt_options( | @@ -1036,11 +1107,11 @@ parse_encrypt_options( | ||
| 1036 | { | 1107 | { |
| 1037 | usage("invalid -use-aes parameter"); | 1108 | usage("invalid -use-aes parameter"); |
| 1038 | } | 1109 | } |
| 1039 | - if ((keylen == 40) && result) | 1110 | + if ((o.keylen == 40) && result) |
| 1040 | { | 1111 | { |
| 1041 | usage("use-aes is invalid for 40-bit keys"); | 1112 | usage("use-aes is invalid for 40-bit keys"); |
| 1042 | } | 1113 | } |
| 1043 | - else if ((keylen == 256) && (! result)) | 1114 | + else if ((o.keylen == 256) && (! result)) |
| 1044 | { | 1115 | { |
| 1045 | // qpdf would happily create files encrypted with RC4 | 1116 | // qpdf would happily create files encrypted with RC4 |
| 1046 | // using /V=5, but Adobe reader can't read them. | 1117 | // using /V=5, but Adobe reader can't read them. |
| @@ -1048,7 +1119,7 @@ parse_encrypt_options( | @@ -1048,7 +1119,7 @@ parse_encrypt_options( | ||
| 1048 | } | 1119 | } |
| 1049 | else | 1120 | else |
| 1050 | { | 1121 | { |
| 1051 | - use_aes = result; | 1122 | + o.use_aes = result; |
| 1052 | } | 1123 | } |
| 1053 | } | 1124 | } |
| 1054 | else | 1125 | else |
| @@ -1058,9 +1129,8 @@ parse_encrypt_options( | @@ -1058,9 +1129,8 @@ parse_encrypt_options( | ||
| 1058 | } | 1129 | } |
| 1059 | } | 1130 | } |
| 1060 | 1131 | ||
| 1061 | -static std::vector<PageSpec> | ||
| 1062 | -parse_pages_options( | ||
| 1063 | - int argc, char* argv[], int& cur_arg) | 1132 | +std::vector<PageSpec> |
| 1133 | +ArgParser::parsePagesOptions(int& cur_arg) | ||
| 1064 | { | 1134 | { |
| 1065 | std::vector<PageSpec> result; | 1135 | std::vector<PageSpec> result; |
| 1066 | while (1) | 1136 | while (1) |
| @@ -1100,7 +1170,7 @@ parse_pages_options( | @@ -1100,7 +1170,7 @@ parse_pages_options( | ||
| 1100 | { | 1170 | { |
| 1101 | try | 1171 | try |
| 1102 | { | 1172 | { |
| 1103 | - parse_numrange(range, 0, true); | 1173 | + parseNumrange(range, 0, true); |
| 1104 | } | 1174 | } |
| 1105 | catch (std::runtime_error& e1) | 1175 | catch (std::runtime_error& e1) |
| 1106 | { | 1176 | { |
| @@ -1112,10 +1182,10 @@ parse_pages_options( | @@ -1112,10 +1182,10 @@ parse_pages_options( | ||
| 1112 | QTC::TC("qpdf", "qpdf pages range omitted in middle"); | 1182 | QTC::TC("qpdf", "qpdf pages range omitted in middle"); |
| 1113 | range_omitted = true; | 1183 | range_omitted = true; |
| 1114 | } | 1184 | } |
| 1115 | - catch (std::runtime_error& e2) | 1185 | + catch (std::runtime_error&) |
| 1116 | { | 1186 | { |
| 1117 | - // Ignore. The range is invalid and not a file. | ||
| 1118 | - // We'll get an error message later. | 1187 | + // Give the range error |
| 1188 | + usage(e1.what()); | ||
| 1119 | } | 1189 | } |
| 1120 | } | 1190 | } |
| 1121 | } | 1191 | } |
| @@ -1137,7 +1207,15 @@ QPDFPageData::QPDFPageData(std::string const& filename, | @@ -1137,7 +1207,15 @@ QPDFPageData::QPDFPageData(std::string const& filename, | ||
| 1137 | qpdf(qpdf), | 1207 | qpdf(qpdf), |
| 1138 | orig_pages(qpdf->getAllPages()) | 1208 | orig_pages(qpdf->getAllPages()) |
| 1139 | { | 1209 | { |
| 1140 | - this->selected_pages = parse_numrange(range, this->orig_pages.size()); | 1210 | + try |
| 1211 | + { | ||
| 1212 | + this->selected_pages = | ||
| 1213 | + QUtil::parse_numrange(range, this->orig_pages.size()); | ||
| 1214 | + } | ||
| 1215 | + catch (std::runtime_error& e) | ||
| 1216 | + { | ||
| 1217 | + usageExit("parsing numeric range for " + filename + ": " + e.what()); | ||
| 1218 | + } | ||
| 1141 | } | 1219 | } |
| 1142 | 1220 | ||
| 1143 | static void parse_version(std::string const& full_version_string, | 1221 | static void parse_version(std::string const& full_version_string, |
| @@ -1155,8 +1233,8 @@ static void parse_version(std::string const& full_version_string, | @@ -1155,8 +1233,8 @@ static void parse_version(std::string const& full_version_string, | ||
| 1155 | version = v; | 1233 | version = v; |
| 1156 | } | 1234 | } |
| 1157 | 1235 | ||
| 1158 | -static void read_args_from_file(char const* filename, | ||
| 1159 | - std::vector<PointerHolder<char> >& new_argv) | 1236 | +void |
| 1237 | +ArgParser::readArgsFromFile(char const* filename) | ||
| 1160 | { | 1238 | { |
| 1161 | std::list<std::string> lines; | 1239 | std::list<std::string> lines; |
| 1162 | if (strcmp(filename, "-") == 0) | 1240 | if (strcmp(filename, "-") == 0) |
| @@ -1177,7 +1255,8 @@ static void read_args_from_file(char const* filename, | @@ -1177,7 +1255,8 @@ static void read_args_from_file(char const* filename, | ||
| 1177 | } | 1255 | } |
| 1178 | } | 1256 | } |
| 1179 | 1257 | ||
| 1180 | -static void handle_help_version(int argc, char* argv[]) | 1258 | +void |
| 1259 | +ArgParser::handleHelpVersion() | ||
| 1181 | { | 1260 | { |
| 1182 | // Make sure the output looks right on an 80-column display. | 1261 | // Make sure the output looks right on an 80-column display. |
| 1183 | 1262 | ||
| @@ -1244,7 +1323,8 @@ static void handle_help_version(int argc, char* argv[]) | @@ -1244,7 +1323,8 @@ static void handle_help_version(int argc, char* argv[]) | ||
| 1244 | } | 1323 | } |
| 1245 | } | 1324 | } |
| 1246 | 1325 | ||
| 1247 | -static void parse_rotation_parameter(Options& o, std::string const& parameter) | 1326 | +void |
| 1327 | +ArgParser::parseRotationParameter(std::string const& parameter) | ||
| 1248 | { | 1328 | { |
| 1249 | std::string angle_str; | 1329 | std::string angle_str; |
| 1250 | std::string range; | 1330 | std::string range; |
| @@ -1285,7 +1365,7 @@ static void parse_rotation_parameter(Options& o, std::string const& parameter) | @@ -1285,7 +1365,7 @@ static void parse_rotation_parameter(Options& o, std::string const& parameter) | ||
| 1285 | bool range_valid = false; | 1365 | bool range_valid = false; |
| 1286 | try | 1366 | try |
| 1287 | { | 1367 | { |
| 1288 | - parse_numrange(range.c_str(), 0, true); | 1368 | + parseNumrange(range.c_str(), 0, true); |
| 1289 | range_valid = true; | 1369 | range_valid = true; |
| 1290 | } | 1370 | } |
| 1291 | catch (std::runtime_error const&) | 1371 | catch (std::runtime_error const&) |
| @@ -1308,8 +1388,11 @@ static void parse_rotation_parameter(Options& o, std::string const& parameter) | @@ -1308,8 +1388,11 @@ static void parse_rotation_parameter(Options& o, std::string const& parameter) | ||
| 1308 | } | 1388 | } |
| 1309 | } | 1389 | } |
| 1310 | 1390 | ||
| 1311 | -static void parse_options(int argc, char* argv[], Options& o) | 1391 | +void |
| 1392 | +ArgParser::parseOptions() | ||
| 1312 | { | 1393 | { |
| 1394 | + handleHelpVersion(); | ||
| 1395 | + handleArgFileArguments(); | ||
| 1313 | for (int i = 1; i < argc; ++i) | 1396 | for (int i = 1; i < argc; ++i) |
| 1314 | { | 1397 | { |
| 1315 | char const* arg = argv[i]; | 1398 | char const* arg = argv[i]; |
| @@ -1345,12 +1428,7 @@ static void parse_options(int argc, char* argv[], Options& o) | @@ -1345,12 +1428,7 @@ static void parse_options(int argc, char* argv[], Options& o) | ||
| 1345 | } | 1428 | } |
| 1346 | else if (strcmp(arg, "encrypt") == 0) | 1429 | else if (strcmp(arg, "encrypt") == 0) |
| 1347 | { | 1430 | { |
| 1348 | - parse_encrypt_options( | ||
| 1349 | - argc, argv, ++i, | ||
| 1350 | - o.user_password, o.owner_password, o.keylen, | ||
| 1351 | - o.r2_print, o.r2_modify, o.r2_extract, o.r2_annotate, | ||
| 1352 | - o.r3_accessibility, o.r3_extract, o.r3_print, o.r3_modify, | ||
| 1353 | - o.force_V4, o.cleartext_metadata, o.use_aes, o.force_R5); | 1431 | + parseEncryptOptions(++i); |
| 1354 | o.encrypt = true; | 1432 | o.encrypt = true; |
| 1355 | o.decrypt = false; | 1433 | o.decrypt = false; |
| 1356 | o.copy_encryption = false; | 1434 | o.copy_encryption = false; |
| @@ -1388,7 +1466,7 @@ static void parse_options(int argc, char* argv[], Options& o) | @@ -1388,7 +1466,7 @@ static void parse_options(int argc, char* argv[], Options& o) | ||
| 1388 | } | 1466 | } |
| 1389 | else if (strcmp(arg, "pages") == 0) | 1467 | else if (strcmp(arg, "pages") == 0) |
| 1390 | { | 1468 | { |
| 1391 | - o.page_specs = parse_pages_options(argc, argv, ++i); | 1469 | + o.page_specs = parsePagesOptions(++i); |
| 1392 | if (o.page_specs.empty()) | 1470 | if (o.page_specs.empty()) |
| 1393 | { | 1471 | { |
| 1394 | usage("--pages: no page specifications given"); | 1472 | usage("--pages: no page specifications given"); |
| @@ -1401,7 +1479,7 @@ static void parse_options(int argc, char* argv[], Options& o) | @@ -1401,7 +1479,7 @@ static void parse_options(int argc, char* argv[], Options& o) | ||
| 1401 | usage("--rotate must be given as" | 1479 | usage("--rotate must be given as" |
| 1402 | " --rotate=[+|-]angle:page-range"); | 1480 | " --rotate=[+|-]angle:page-range"); |
| 1403 | } | 1481 | } |
| 1404 | - parse_rotation_parameter(o, parameter); | 1482 | + parseRotationParameter(parameter); |
| 1405 | } | 1483 | } |
| 1406 | else if (strcmp(arg, "stream-data") == 0) | 1484 | else if (strcmp(arg, "stream-data") == 0) |
| 1407 | { | 1485 | { |
| @@ -2436,7 +2514,9 @@ static void handle_rotations(QPDF& pdf, Options& o) | @@ -2436,7 +2514,9 @@ static void handle_rotations(QPDF& pdf, Options& o) | ||
| 2436 | { | 2514 | { |
| 2437 | std::string const& range = (*iter).first; | 2515 | std::string const& range = (*iter).first; |
| 2438 | RotationSpec const& rspec = (*iter).second; | 2516 | RotationSpec const& rspec = (*iter).second; |
| 2439 | - std::vector<int> to_rotate = parse_numrange(range.c_str(), npages); | 2517 | + // range has been previously validated |
| 2518 | + std::vector<int> to_rotate = | ||
| 2519 | + QUtil::parse_numrange(range.c_str(), npages); | ||
| 2440 | for (std::vector<int>::iterator i2 = to_rotate.begin(); | 2520 | for (std::vector<int>::iterator i2 = to_rotate.begin(); |
| 2441 | i2 != to_rotate.end(); ++i2) | 2521 | i2 != to_rotate.end(); ++i2) |
| 2442 | { | 2522 | { |
| @@ -2741,43 +2821,17 @@ int main(int argc, char* argv[]) | @@ -2741,43 +2821,17 @@ int main(int argc, char* argv[]) | ||
| 2741 | whoami = QUtil::getWhoami(argv[0]); | 2821 | whoami = QUtil::getWhoami(argv[0]); |
| 2742 | QUtil::setLineBuf(stdout); | 2822 | QUtil::setLineBuf(stdout); |
| 2743 | 2823 | ||
| 2744 | - // For libtool's sake.... | 2824 | + // Remove prefix added by libtool for consistency during testing. |
| 2745 | if (strncmp(whoami, "lt-", 3) == 0) | 2825 | if (strncmp(whoami, "lt-", 3) == 0) |
| 2746 | { | 2826 | { |
| 2747 | whoami += 3; | 2827 | whoami += 3; |
| 2748 | } | 2828 | } |
| 2749 | 2829 | ||
| 2750 | - handle_help_version(argc, argv); | ||
| 2751 | - | ||
| 2752 | - // Support reading arguments from files. Create a new argv. Ensure | ||
| 2753 | - // that argv itself as well as all its contents are automatically | ||
| 2754 | - // deleted by using PointerHolder objects to back the pointers in | ||
| 2755 | - // argv. | ||
| 2756 | - std::vector<PointerHolder<char> > new_argv; | ||
| 2757 | - new_argv.push_back(PointerHolder<char>(true, QUtil::copy_string(argv[0]))); | ||
| 2758 | - for (int i = 1; i < argc; ++i) | ||
| 2759 | - { | ||
| 2760 | - if ((strlen(argv[i]) > 1) && (argv[i][0] == '@')) | ||
| 2761 | - { | ||
| 2762 | - read_args_from_file(1+argv[i], new_argv); | ||
| 2763 | - } | ||
| 2764 | - else | ||
| 2765 | - { | ||
| 2766 | - new_argv.push_back( | ||
| 2767 | - PointerHolder<char>(true, QUtil::copy_string(argv[i]))); | ||
| 2768 | - } | ||
| 2769 | - } | ||
| 2770 | - PointerHolder<char*> argv_ph(true, new char*[1+new_argv.size()]); | ||
| 2771 | - argv = argv_ph.getPointer(); | ||
| 2772 | - for (size_t i = 0; i < new_argv.size(); ++i) | ||
| 2773 | - { | ||
| 2774 | - argv[i] = new_argv.at(i).getPointer(); | ||
| 2775 | - } | ||
| 2776 | - argc = static_cast<int>(new_argv.size()); | ||
| 2777 | - argv[argc] = 0; | ||
| 2778 | - | 2830 | + // ArgParser must stay in scope for the duration of qpdf's run as |
| 2831 | + // it holds dynamic memory used for argv. | ||
| 2779 | Options o; | 2832 | Options o; |
| 2780 | - parse_options(argc, argv, o); | 2833 | + ArgParser ap(argc, argv, o); |
| 2834 | + ap.parseOptions(); | ||
| 2781 | 2835 | ||
| 2782 | try | 2836 | try |
| 2783 | { | 2837 | { |