00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #include <stlib/tools/CmdLineParser.h>
00025 #include <stlib/Callback.h>
00026
00027 #include <stlib/tools/CmdLineMissingArgument.h>
00028 #include <stlib/tools/CmdLineUnknownArgument.h>
00029 #include <stlib/tools/CmdLineUnsufficientParams.h>
00030
00031 #include <stlib/Array.h>
00032 #include <stlib/Character.h>
00033 #include <stlib/Iterator.h>
00034 #include <stlib/Object.h>
00035 #include <stlib/OrderedCollection.h>
00036 #include <stlib/String.h>
00037 #include <stlib/WriteStream.h>
00038 #include <stlib/POOBoundsError.h>
00039
00040 #include <getopt.h>
00041
00042 namespace Tools {
00043
00044 class CommandLineArgument : public Core::Object
00045 {
00046 protected:
00047 String *long_argument;
00048 Character *short_argument;
00049 bool _mandatory;
00050 Callback<void> *_callback;
00051 Object *user_data;
00052 bool _evaluated;
00053
00054 public:
00055 CommandLineArgument(String *longArg, Character *shortArg, bool mandatory,
00056 Callback<void> *callback, Object *userData = nil)
00057 {
00058 long_argument = longArg;
00059 short_argument = shortArg;
00060 _mandatory = mandatory;
00061 _callback = callback;
00062 user_data = userData;
00063 _evaluated = false;
00064 }
00065
00066
00067 virtual void printGetOptShortOn(Stream *stream)
00068 {
00069 if (short_argument == nil) return;
00070
00071 stream->nextPut(short_argument);
00072 if (acceptsParameter())
00073 stream->nextPut(':');
00074 }
00075
00076
00077 virtual void performCallback(void)
00078 {
00079 _callback->executeWith(user_data);
00080 _evaluated = true;
00081 }
00082
00083 virtual void performCallbackWith(String *param)
00084 {
00085 _callback->executeWith(param);
00086 _evaluated = true;
00087 }
00088
00089
00090 virtual bool acceptsParameter(void)
00091 {
00092 return _callback->argumentsNumber() > 0 && user_data == nil;
00093 }
00094
00095 virtual bool matchShortArgument(int character)
00096 {
00097 return short_argument->isEqual((char) character);
00098 }
00099
00100 virtual bool isMandatory(void)
00101 {
00102 return _mandatory;
00103 }
00104
00105 virtual bool wasEvaluated(void)
00106 {
00107 return _evaluated;
00108 }
00109
00110
00111 virtual void fillGetOptLongOption(struct option *longopt)
00112 {
00113 longopt->name = long_argument->asCString();
00114 longopt->has_arg = acceptsParameter() ? 1 : 0;
00115 longopt->flag = NULL;
00116 if (short_argument == nil) longopt->val = 0;
00117 else
00118 longopt->val = short_argument->asInteger();
00119 }
00120 };
00121
00122
00123 CommandLineParser::CommandLineParser(void)
00124 {
00125 _arguments = new OrderedCollection;
00126 extra_argument_handler = nil;
00127 parameters_mandatory = false;
00128 }
00129
00130 String *CommandLineParser::className(void) const
00131 {
00132 return new String("CommandLineParser");
00133 }
00134
00135
00136 void CommandLineParser::parametersMandatory(bool isMandatory)
00137 {
00138 parameters_mandatory = isMandatory;
00139 }
00140
00141
00142 void CommandLineParser::process(int argc, char **argv)
00143 {
00144 int option_index = 0;
00145 char *shortOptions = collectShortOptions()->asCString();
00146 struct option *longOptions = collectLongOptions();
00147 CommandLineArgument *argument;
00148
00149 optind = 0;
00150 while (1) {
00151 int c;
00152 c = getopt_long(argc, argv, shortOptions, longOptions, &option_index);
00153 if (c == -1) break;
00154
00155 if (c == 0) {
00156 if (option_index >= _arguments->size()) {
00157 (new CommandLineUnknownArgument(__PRETTY_FUNCTION__, new String(argv[optind-1])))
00158 ->raiseFrom(this);
00159 }
00160 argument = dynamic_cast<CommandLineArgument *>(_arguments->at(option_index));
00161 } else {
00162 Iterator *i = _arguments->iterator();
00163 while (!i->finished()) {
00164 argument = dynamic_cast<CommandLineArgument *>(i->value());
00165 if (argument->matchShortArgument(c)) break;
00166 i->next();
00167 }
00168 if (i->finished()) {
00169 (new CommandLineUnknownArgument(__PRETTY_FUNCTION__, new String(argv[optind-1])))
00170 ->raiseFrom(this);
00171 }
00172 }
00173
00174 if (argument->acceptsParameter()) {
00175 argument->performCallbackWith(new String(optarg));
00176 } else {
00177 argument->performCallback();
00178 }
00179 }
00180
00181
00182 if (optind < argc) {
00183 if (extra_argument_handler != nil) {
00184 Array *extras = new Array(argc - optind);
00185 for (int i = 0; optind + i < argc; i++) {
00186 extras->put(i, new String(argv[optind]));
00187 }
00188 extra_argument_handler->executeWith(extras);
00189 }
00190 } else if (parameters_mandatory) {
00191 (new CommandLineUnsufficientParameters(__PRETTY_FUNCTION__))
00192 ->raiseFrom(this);
00193 }
00194
00195 checkMissingArguments();
00196 }
00197
00198
00199 void CommandLineParser::addArgument(String *longArg, Character *shortArg,
00200 bool mandatory, Callback<void> *callback,
00201 Object *userData)
00202 {
00203 CommandLineArgument *argument;
00204 argument = new CommandLineArgument(longArg, shortArg, mandatory,
00205 callback, userData);
00206 _arguments->add(argument);
00207 }
00208
00209 String *CommandLineParser::collectShortOptions(void)
00210 {
00211 Iterator *i;
00212 Stream *stream;
00213 stream = (new String(2 * _arguments->size()))->writeStream();
00214
00215 for (i = _arguments->iterator(); !i->finished(); i->next())
00216 {
00217 dynamic_cast<CommandLineArgument *>(i->value())->printGetOptShortOn(stream);
00218 }
00219 return dynamic_cast<String *>(stream->contents());
00220 }
00221
00222 struct option *CommandLineParser::collectLongOptions(void)
00223 {
00224 struct option *longOptions;
00225 CommandLineArgument *argument;
00226 Iterator *i;
00227 int index;
00228
00229 longOptions = (struct option *) GC_malloc((_arguments->size() + 1) * sizeof(struct option));
00230 for (i = _arguments->iterator(), index = 0; !i->finished(); i->next(), index++) {
00231 argument = dynamic_cast<CommandLineArgument *>(i->value());
00232 argument->fillGetOptLongOption(&longOptions[index]);
00233 }
00234
00235 longOptions[index].name = NULL;
00236 longOptions[index].has_arg = 0;
00237 longOptions[index].flag = NULL;
00238 longOptions[index].val = 0;
00239 return longOptions;
00240 }
00241
00242 void CommandLineParser::checkMissingArguments(void)
00243 {
00244 CommandLineArgument *argument;
00245 Iterator *i;
00246
00247 for (i = _arguments->iterator(); !i->finished(); i->next()) {
00248 argument = dynamic_cast<CommandLineArgument *>(i->value());
00249 if (argument->isMandatory() && !argument->wasEvaluated()) {
00250 (new CommandLineMissingArgument(__PRETTY_FUNCTION__, argument))
00251 ->raiseFrom(this);
00252 }
00253 }
00254 }
00255
00256 };