Commit 151206603b80225ff9f59235bf38a3677ebbe6d2

Authored by Jay Berkenbilt
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&amp; msg) @@ -552,6 +624,12 @@ void usage(std::string const&amp; 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&amp; pdf, Options&amp; o) @@ -707,8 +785,8 @@ static void show_encryption(QPDF&amp; pdf, Options&amp; 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&lt;int&gt; parse_numrange(char const* range, int max, @@ -728,35 +806,28 @@ static std::vector&lt;int&gt; 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&amp; filename, @@ -1137,7 +1207,15 @@ QPDFPageData::QPDFPageData(std::string const&amp; 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&amp; full_version_string, @@ -1155,8 +1233,8 @@ static void parse_version(std::string const&amp; 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&amp; o, std::string const&amp; parameter) @@ -1285,7 +1365,7 @@ static void parse_rotation_parameter(Options&amp; o, std::string const&amp; 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&amp; o, std::string const&amp; parameter) @@ -1308,8 +1388,11 @@ static void parse_rotation_parameter(Options&amp; o, std::string const&amp; 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&amp; o) @@ -1345,12 +1428,7 @@ static void parse_options(int argc, char* argv[], Options&amp; 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&amp; o) @@ -1388,7 +1466,7 @@ static void parse_options(int argc, char* argv[], Options&amp; 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&amp; o) @@ -1401,7 +1479,7 @@ static void parse_options(int argc, char* argv[], Options&amp; 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&amp; pdf, Options&amp; o) @@ -2436,7 +2514,9 @@ static void handle_rotations(QPDF&amp; pdf, Options&amp; 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 {