00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026 #include "Common/OptionStream.h"
00027
00028
00029 #include <algorithm>
00030 #include <cstring>
00031 #include <iterator>
00032
00033
00034
00035 #define ERROR os.err << os.argv[0] << ": error: option "
00036
00037 namespace IXE {
00038 using namespace std;
00039
00043 ostream&
00044 Options::usage(Options::spec const options[], ostream& err)
00045 {
00046 err << "options: (unambiguous abbreviations accepted)\n"
00047 "========\n";
00048 for (Options::spec const* opt = options; *(int*)opt; opt++)
00049 err << opt->use_msg << endl;
00050 return err;
00051 }
00052
00053
00061
00062
00063 void
00064 OptionStream::Option::copy(Option const &o)
00065 {
00066 c = o.c;
00067 _arg = ::strdup(o._arg);
00068 am_copy = true;
00069 }
00070
00071
00082
00083
00084 OptionStream::OptionStream(int argc, char* argv[],
00085 Options::spec const specs[], ostream& err)
00086 : argc(argc), argv(argv), specs(specs), err(err),
00087 index(0), next_c(0), end(false)
00088 { }
00089
00090
00117
00118
00119 OptionStream&
00120 operator >>(OptionStream& os, OptionStream::Option& o)
00121 {
00122 char* arg;
00123 register Options::spec const* s, *found = 0;
00124
00125 o.c = '\0';
00126
00127 if (os.next_c && *os.next_c) {
00128
00129
00130
00131
00132
00133 arg = os.next_c;
00134 goto short_option;
00135 }
00136
00137 if (++os.index >= os.argc)
00138 goto the_end;
00139 arg = os.argv[os.index];
00140 if (!arg || *arg != '-' || !*++arg)
00141 goto the_end;
00142
00143 if (*arg == '-') {
00144 if (!*++arg) {
00145 ++os.index;
00146 goto the_end;
00147 }
00148
00149
00150
00151
00152
00153 char* end;
00154 for (end = arg; *end && *end != '='; ++end);
00155
00156
00157
00158
00159 for (s = os.specs; s->long_name; ++s) {
00160 int const len = end - arg;
00161 if (::strncmp(arg, s->long_name, len))
00162 continue;
00163 if (::strlen(s->long_name) == len)
00164 {
00165 found = s;
00166 break;
00167 }
00168 if (!found)
00169 {
00170 found = s;
00171 continue;
00172 }
00173 ERROR << '"';
00174 std::copy(arg, end, std::ostream_iterator<char>(os.err, ""));
00175 os.err << "\" is ambiguous\n";
00176 return os;
00177 }
00178 if (!found) {
00179 ERROR << '"';
00180 std::copy(arg, end, std::ostream_iterator<char>(os.err, ""));
00181 os.err << "\" unrecognized\n";
00182 return os;
00183 }
00184
00185
00186
00187
00188 arg = 0;
00189 switch (found->arg_type) {
00190
00191 case Options::no_arg:
00192 if (*end == '=') {
00193 ERROR << '"' << found->long_name << "\" takes no argument\n";
00194 goto set_arg;
00195 }
00196 break;
00197
00198 case Options::req_arg:
00199 case Options::opt_arg:
00200 if (*end == '=') {
00201 arg = ++end;
00202 break;
00203 }
00204 if (++os.index >= os.argc)
00205 break;
00206 arg = os.argv[os.index];
00207 if (*arg == '-') {
00208
00209
00210
00211
00212
00213 arg = 0;
00214 }
00215 break;
00216
00217 default:
00218 goto bad_spec;
00219 }
00220
00221 if (!arg && found->arg_type == Options::req_arg)
00222 ERROR << '"' << found->long_name << "\" requires an argument\n";
00223 else
00224 o.c = found->c;
00225
00226 goto set_arg;
00227 }
00228
00229 short_option:
00230
00231
00232
00233 for (s = os.specs; s->c; ++s)
00234 if (*arg == s->c) {
00235 found = s;
00236 break;
00237 }
00238 if (!found) {
00239 ERROR << '"' << *arg << "\" unrecognized\n";
00240 return os;
00241 }
00242
00243
00244
00245
00246 switch (found->arg_type) {
00247
00248 case Options::no_arg:
00249
00250
00251
00252
00253
00254
00255 os.next_c = arg + 1;
00256 arg = 0;
00257 break;
00258
00259 case Options::req_arg:
00260 case Options::opt_arg:
00261
00262
00263
00264
00265
00266 os.next_c = 0;
00267
00268 if (!*++arg) {
00269
00270
00271
00272
00273 if (++os.index >= os.argc)
00274 break;
00275 arg = os.argv[os.index];
00276 if (*arg == '-') {
00277
00278
00279
00280
00281
00282 arg = 0;
00283 }
00284 }
00285 break;
00286
00287 default:
00288 goto bad_spec;
00289 }
00290
00291 if (!arg && found->arg_type == Options::req_arg)
00292 ERROR << '"' << found->c << "\" requires an argument\n";
00293 else
00294 o.c = found->c;
00295
00296 set_arg:
00297 o._arg = arg;
00298 return os;
00299 the_end:
00300 os.end = true;
00301 return os;
00302 bad_spec:
00303 ERROR << "invalid option argument spec: " << found->arg_type << '\n';
00304 ::abort();
00305 return os;
00306 }
00307
00308 }
00309
00310
00311
00312
00313 #ifdef TEST_OPTIONSTREAM
00314
00315 using namespace IXE;
00316
00317 int
00318 main(int argc, char *argv[])
00319 {
00320 static Options::spec const spec[] = {
00321 "no-arg", 0, 'n',
00322 "req-arg", 1, 'r',
00323 "opt-arg", 2, 'o',
00324 0,
00325 };
00326 OptionStream opt_in(argc, argv, spec);
00327 OptionStream::Option opt;
00328 while (opt_in >> opt) {
00329 switch (opt) {
00330 case 'n':
00331 cout << "got --no-arg\n";
00332 break;
00333 case 'r':
00334 cout << "got --req-arg=" << opt.arg() << '\n';
00335 break;
00336 case 'o':
00337 cout << "got --opt-arg=";
00338 cout << (opt.arg()? opt.arg() : "(null)") << endl;
00339 break;
00340 default:
00341 cout << "got weird=" << (int)(char) opt << '\n';
00342 }
00343 }
00344 cout << "shift=" << opt_in.shift() << '\n';
00345
00346 argc -= opt_in.shift();
00347 argv += opt_in.shift();
00348
00349 cout << "argc=" << argc << endl;
00350 cout << "first non-option=" << (argv[0] ? argv[0] : "(null)") << endl;
00351 }
00352
00353 #endif