ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/gclib/gclib/GArgs.cpp
(Generate patch)
# Line 1 | Line 1
1 < #include <stdlib.h>
2 < #include <string.h>
1 > #include "GBase.h"
2   #include "GArgs.h"
3   #include <ctype.h>
4  
6 #define TRACE 1
7
8 #include "GBase.h"
9
10 //GArgs::is_opt="1"; //just to have a non-NULL value for switch testing
11
12
5   GArgs::GArgs(int argc, char* const argv[], const char* format, bool nodigitopts) {
6 <   /* format is:
7 <       <letter>[:]    for e.g. p:hT    <-  -p testing -ptesting -h -T
8 <       <string>=      for e.g. PID=S=  <-  PID=50 S=3.5
17 <   This means that the = options, if present, must NEVER be given after
18 <   dashed switches (non-value) directly
6 >   /* format can be:
7 >      <string>{;|=} e.g. disable-test;PID=S= for --disable-test PID=50 (or --PID 50) S=3.5 etc.
8 >      <letter>[:]  e.g. p:hT  for -p testing (or -ptesting) -h -T
9     */
20  
21 //parse format string first:
10   const char* fstr=format;
11   fmtcount=0;
12   count=0;
# Line 26 | Line 14
14   nonOptPos=0;
15   optPos=0;
16   errarg=0;
17 + err_valmissing=false;
18   args=NULL;
19   fmt=NULL;
20 + _argc=argc;
21 + _argv=argv;
22   int fmtlen=strlen(format);
23 + //---- first parse the format string
24   while (fstr-format < fmtlen ) {
25 <  int l=strcspn(fstr, ":=");
26 <  if (fstr[l]=='\0') { //end of string reached
25 >  int l=strcspn(fstr, ";=:");
26 >  if (fstr[l]==0) { //end of string reached
27        //all previous chars are just switches:
28         GREALLOC(fmt, (fmtcount+l)*sizeof(fmtdef));
29 <       //store each switches
29 >       //store each switch
30         for (int i=0; i<l;i++) {
31 <         GCALLOC(fmt[fmtcount+i].opt, 2); //one char length
32 <         fmt[fmtcount+i].opt[0]=fstr[i];
33 <         fmt[fmtcount+i].type = 0;
31 >         fmt[fmtcount+i].longopt=NULL;
32 >         fmt[fmtcount+i].opt=fstr[i];
33 >         fmt[fmtcount+i].req_value = false;
34 >         fmt[fmtcount+i].code=fmtcount+i+1;
35           }
36         fmtcount+=l;
37         break;
# Line 47 | Line 40
40       if (fstr[l]==':') {
41           //fstr[l-1] is an argument, but all the previous are just switches
42           GREALLOC(fmt, (fmtcount+l)*sizeof(fmtdef));
43 <         //store each switches AND the option
43 >         //store each switch AND the option
44           for (int i=0; i<l;i++) {
45 <           GCALLOC(fmt[fmtcount+i].opt, 2); //one char length
46 <           fmt[fmtcount+i].opt[0]=fstr[i];
47 <           fmt[fmtcount+i].type = (i==l-1)?1:0;
45 >           fmt[fmtcount+i].longopt=NULL; //one char length
46 >           fmt[fmtcount+i].opt=fstr[i];
47 >           fmt[fmtcount+i].req_value = (i==l-1);
48 >           fmt[fmtcount+i].code=fmtcount+i+1;
49             }
50           fmtcount+=l;
51           }
52 <      else { // fstr[l]=='=' case!
59 <         //all these chars are one = style argument
52 >      else { // fstr[l]=='=' or ';'
53           GREALLOC(fmt, (fmtcount+1)*sizeof(fmtdef));
54 <         GMALLOC(fmt[fmtcount].opt, l+1);
55 <         strncpy(fmt[fmtcount].opt, fstr, l);
56 <         fmt[fmtcount].opt[l]='\0';
57 <         fmt[fmtcount].type=2;
54 >         fmt[fmtcount].longopt=Gstrdup(fstr, fstr+l-1);
55 >         fmt[fmtcount].opt=0;
56 >         fmt[fmtcount].req_value=(fstr[l]=='=');
57 >         fmt[fmtcount].code=fmtcount+1;
58           fmtcount++;
59           }
60       fstr+=l+1;
61       }
62    }
63 < //---- that was the parsing of the format string
64 < //now parse the arguments based on given format specification
65 < int p=1; //skip program name
66 < int f=0;
67 < //GMessage("argc=%d\n", argc);
68 < while (p<argc) {
69 < if (argv[p][0]=='-') { //dashed argument ?
70 <   int cpos=1;
71 <   char c=argv[p][cpos];
72 <   if (c==0 || (nodigitopts && isdigit(c))) {
73 <      //special case: plain argument '-' or negative number
74 <      GREALLOC(args, (count+1)*sizeof(argdata));
75 <      args[count].opt=NULL;
76 <      if (c==0) {
77 <        GCALLOC(args[count].value, 2);
78 <        args[count].value[0]='-';
79 <        }
80 <       else {
81 <        args[count].value=Gstrdup(argv[p]);
63 > //-- now parse the arguments based on the given format specification
64 > parseArgs(nodigitopts);
65 > }
66 >
67 > int GArgs::parseArgs(bool nodigitopts) {
68 >  int p=1; //skip program name
69 >  int f=0;
70 >  while (p<_argc) {
71 >   if (_argv[p][0]=='-' && (_argv[p][1]==0 || _argv[p][1]!='-')) {
72 >     //single-dash argument
73 >     int cpos=1;
74 >     char c=_argv[p][cpos];
75 >     if (c==0 || (nodigitopts && isdigit(c)) ||
76 >            (c=='.' && isdigit(_argv[p][cpos+1]))) {
77 >        //special case: plain argument '-' or just a negative number
78 >        GREALLOC(args, (count+1)*sizeof(argdata));
79 >        args[count].opt=NULL;
80 >        args[count].fmti=-1;
81 >        if (c==0) {
82 >          GCALLOC(args[count].value, 2);
83 >          args[count].value[0]='-';
84 >          }
85 >         else { //negative number given
86 >          args[count].value=Gstrdup(_argv[p]);
87 >          }
88 >        count++;
89 >        nonOptCount++;
90          }
91 <      count++;
92 <      nonOptCount++;
93 <      }
93 <    else { //dashed argument or switch
94 <     COLLAPSED:
95 <      if ((f=validOpt(c))>=0) {
96 <        if (fmt[f].type==0) {//switch type
91 >      else { //single-dash argument or switch
92 >       COLLAPSED:
93 >        if ((f=validShortOpt(c))>=0) {
94            GREALLOC(args, (count+1)*sizeof(argdata));
95            GCALLOC(args[count].opt, 2);
96            args[count].opt[0]=c;
97 <          GCALLOC(args[count].value, 1);
98 <          count++;
99 <          // only switches can be grouped with some other switches or options
103 <          if (argv[p][cpos+1]!='\0') {
104 <             cpos++;
105 <             c=argv[p][cpos];
106 <             goto COLLAPSED;
107 <             }
108 <          }
109 <         else
110 <           if (fmt[f].type==1) { //dash argument
111 <            GREALLOC(args, (count+1)*sizeof(argdata));
112 <            GCALLOC(args[count].opt, 2);
113 <            args[count].opt[0]=c;
114 <            if (argv[p][cpos+1]=='\0') {
115 <              if (p+1<argc) { //value is the whole next argument
116 <                 p++;
117 <                 GMALLOC(args[count].value, strlen(argv[p])+1);
118 <                 strcpy(args[count].value, argv[p]);
119 <                 }
120 <               else {
121 <                 errarg=p;
122 <                 return;
123 <                 }
124 <              }
125 <             else { //value immediately follows the dash-option
126 <                GMALLOC(args[count].value, strlen(argv[p])-cpos);
127 <                strcpy(args[count].value, (argv[p]+cpos+1));
128 <                //GMessage("args[%d].value = '%s'",count, args[count].value);
129 <              }
97 >          args[count].fmti=f;
98 >          if (!fmt[f].req_value) {//switch type
99 >            GCALLOC(args[count].value,1);//so getOpt() functions would not return NULL
100              count++;
101 <            }
102 <           else {//inconsistent type
103 <             errarg=p;
104 <             return;
105 <             }
106 <        } //was validOpt
137 <       else { //option not found in format definition!
138 <         errarg=p;
139 <         return;
140 <         }
141 <      }
142 <   }
143 < else {//not a dashed argument
144 <   char* e=strchr(argv[p],'=');
145 <   if (e!=NULL && strchr(format,'=')!=NULL && e!=argv[p] && *(e-1)!='\\') {
146 <     //this must be an '=' option
147 <     //yet the '=' char can be preceded by a '\' in order to not be parsed
148 <     //as a = option
149 <     char part[128];
150 <     strncpy(part, argv[p], e-argv[p]);
151 <     part[e-argv[p]]='\0';
152 <     if ((f=validOpt(part))>=0 && fmt[f].type==2) {
153 <          GREALLOC(args, (count+1)*sizeof(argdata));
154 <          args[count].opt=Gstrdup(part);
155 <          if (strlen(argv[p])-strlen(part)>0) {
156 <            GMALLOC(args[count].value, strlen(argv[p])-strlen(part)+1);
157 <            strcpy(args[count].value, e+1);
101 >            // only switches can be grouped with some other switches or options
102 >            if (_argv[p][cpos+1]!='\0') {
103 >               cpos++;
104 >               c=_argv[p][cpos];
105 >               goto COLLAPSED;
106 >               }
107              }
108             else {
109 <            args[count].value=NULL;
110 <            }
111 <          count++;
109 >              //single-dash argument followed by a value
110 >            if (_argv[p][cpos+1]=='\0') {
111 >                if (p+1<_argc && _argv[p+1][0]!=0) { //value is the whole next argument
112 >                   p++;
113 >                   args[count].value=Gstrdup(_argv[p]);
114 >                   }
115 >                  else {
116 >                   errarg=p;
117 >                   err_valmissing=true;
118 >                   return errarg;
119 >                   }
120 >                }
121 >               else { //value immediately follows the dash-option
122 >                args[count].value=Gstrdup(_argv[p]+cpos+1);
123 >                }
124 >            count++;
125 >            }
126 >          } //was validShortOpt
127 >         else { //option not found in format definition!
128 >           errarg=p;
129 >           return errarg;
130 >           }
131 >        }
132 >     } //-single-dash
133 >   else {//not a single-dash argument
134 >     char* ap=_argv[p];
135 >     bool is_longopt=false;
136 >     if (*ap=='-' && ap[1]=='-') {
137 >        is_longopt=true;
138 >        ap+=2;
139 >        }
140 >     char* e=strchr(ap+1,'=');
141 >     while (e!=NULL && *(e-1)=='\\') e=strchr(e,'=');
142 >     if (e==NULL && is_longopt) {
143 >        e=ap;
144 >        while (*e!=0 && *e!=' ') e++;
145 >        //e will be on eos or next space
146 >        }
147 >     if (e!=NULL && e>ap) {
148 >       //this must be a long option
149 >       //e is on eos, space or '='
150 >       if ((f=validLongOpt(ap,e-1))>=0) {
151 >            GREALLOC(args, (count+1)*sizeof(argdata));
152 >            args[count].opt=Gstrdup(ap,e-1);
153 >            args[count].fmti=f;
154 >            if (fmt[f].req_value) {
155 >               if (*e==0) {
156 >                   //value is the next argument
157 >                   if (p+1<_argc && _argv[p+1][0]!=0) {
158 >                      p++;
159 >                      args[count].value=Gstrdup(_argv[p]);
160 >                      }
161 >                    else {
162 >                      errarg=p;
163 >                      err_valmissing=true;
164 >                      return errarg;
165 >                      }
166 >                   }
167 >                else { //value is in the same argument
168 >                   //while (*e!=0 && (*e==' ' || *e=='=')) e++;
169 >                   if (*e=='=') e++;
170 >                   if (*e==0) {
171 >                      errarg=p;
172 >                      err_valmissing=true;
173 >                      return errarg;
174 >                      }
175 >                   args[count].value=Gstrdup(e);
176 >                   }
177 >               } //value required
178 >              else { //no value expected
179 >               GCALLOC(args[count].value,1); //do not return NULL
180 >               }
181 >            count++;
182 >            }
183 >          else { //error - this long argument not recognized
184 >           errarg=p;
185 >           return errarg;
186 >           }
187 >        }
188 >      else { //just a plain non-option argument
189 >       if (e==ap) { //i.e. just "--"
190 >          errarg=p;
191 >          return errarg;
192            }
193 <        else { //error - format does not match this '=' argument
194 <         errarg=p;
195 <         return;        
196 <         }
197 <      }
198 <    else { //it seems it's just a plain argument, like a filename, etc.
199 <     GREALLOC(args, (count+1)*sizeof(argdata));
200 <     args[count].opt=NULL; //it's not an option
201 <     args[count].value=Gstrdup(argv[p]);
202 <     count++;
203 <     nonOptCount++;
193 >       GREALLOC(args, (count+1)*sizeof(argdata));
194 >       args[count].opt=NULL; //it's not an option
195 >       args[count].value=Gstrdup(_argv[p]);
196 >       args[count].fmti=-1;
197 >       count++;
198 >       nonOptCount++;
199 >       }
200 >     }
201 >   p++;//check next arg string
202 >   } //while arguments
203 > return errarg;
204 > }
205 >
206 > void GArgs::printError(FILE* fout, const char* usage, bool exitProgram) {
207 > if (errarg==0) return;
208 > if (usage) fprintf(fout, "%s\n", usage);
209 > if (err_valmissing)
210 >     fprintf(fout, "Error: value required for option '%s'\n", _argv[errarg]);
211 >    else
212 >     fprintf(fout, "Error: invalid argument '%s'\n", _argv[errarg]);
213 > if (exitProgram)
214 >     exit(1);
215 > }
216 >
217 > void GArgs::printError(const char* usage, bool exitProgram) {
218 > printError(stderr, usage, exitProgram);
219 > }
220 >
221 > void GArgs::printCmdLine(FILE* fout) {
222 > if (_argv==NULL) return;
223 > for (int i=0;i<_argc;i++) {
224 >   fprintf(fout, "%s%c", _argv[i], (i==_argc-1)?'\n':' ');
225 >   }
226 > }
227 >
228 > GArgs::GArgs(int argc, char* const argv[], const GArgsDef fmtrecs[], bool nodigitopts) {
229 > fmtcount=0;
230 > count=0;
231 > nonOptCount=0;
232 > nonOptPos=0;
233 > optPos=0;
234 > errarg=0;
235 > err_valmissing=false;
236 > args=NULL;
237 > fmt=NULL;
238 > _argc=argc;
239 > _argv=argv;
240 > if (fmtrecs==NULL) return;
241 >
242 > const GArgsDef* frec=fmtrecs;
243 > while ((frec->longopt || frec->opt) && fmtcount<255) {
244 >     fmtcount++;
245 >     frec=&(fmtrecs[fmtcount]);
246       }
247 + GCALLOC(fmt, fmtcount*sizeof(fmtdef));
248 + for (int i=0;i<fmtcount;i++) {
249 +   fmt[i].longopt=Gstrdup(fmtrecs[i].longopt); //do we need to use Gstrdup here?
250 +   fmt[i].opt=fmtrecs[i].opt;
251 +   fmt[i].req_value=fmtrecs[i].req_value;
252 +   fmt[i].code=fmtrecs[i].code;
253     }
254 < p++;//check next arg string
178 < }
254 > parseArgs(nodigitopts);
255   }
256  
257 +
258   GArgs::~GArgs() {
259   int i;
260   for (i=0; i<fmtcount; i++)
261 <    GFREE(fmt[i].opt);
261 >    GFREE(fmt[i].longopt);
262   GFREE(fmt);
263   for (i=0; i<count; i++) {
264    GFREE(args[i].opt);
265    GFREE(args[i].value);
266    }
267 < GFREE(args);  
267 > GFREE(args);
268   }
269  
270 < int GArgs::validOpt(char o) {
270 > int GArgs::validShortOpt(char o) {
271   for (int i=0; i<fmtcount; i++)
272 <  if (fmt[i].opt[0]==o && fmt[i].opt[1]=='\0') return i;
272 >  if (fmt[i].opt==o) return i;
273   return -1;
274   }
275  
276 < int GArgs::validOpt(char* o) {
276 > int GArgs::validLongOpt(char* o, char* to) {
277 > char* pstr=Gstrdup(o,to);
278 > for (int i=0; i<fmtcount; i++) {
279 >  if (fmt[i].longopt && strcmp(fmt[i].longopt, pstr)==0) {
280 >       GFREE(pstr);
281 >       return i;
282 >       }
283 >  }
284 > GFREE(pstr);
285 > return -1;
286 > }
287 >
288 > int GArgs::validOpt(int code) {
289   for (int i=0; i<fmtcount; i++)
290 <  if (strcmp(fmt[i].opt, o)==0) return i;
290 >   if (fmt[i].code==code) return i;
291   return -1;
292   }
293  
294 +
295   int GArgs::isError() { // returns the offending argv position or 0 if no error
296   return errarg;
297   }
# Line 215 | Line 305
305   for (int i=0; i<count; i++)
306    if (args[i].opt!=NULL && strcmp(args[i].opt, o)==0)
307             return args[i].value;
308 < return NULL;                    
308 > return NULL;
309   }
310  
311   char* GArgs::getOpt(const char o) {
# Line 225 | Line 315
315   return NULL;
316   }
317  
318 < int GArgs::startNonOpt(){ //reset iteration through non-dashed arguments
319 <   //returns the number of non-dashed arguments
318 > char* GArgs::getOpt(int c) {
319 > for (int i=0; i<count; i++)
320 >  if (args[i].fmti>=0 && fmt[args[i].fmti].code==c)
321 >      return args[i].value;
322 > return NULL;
323 > }
324 >
325 > char* GArgs::getOptName(int c) {
326 > for (int i=0; i<count; i++)
327 >  if (args[i].fmti>=0 && fmt[args[i].fmti].code==c)
328 >      return args[i].opt;
329 > return NULL;
330 > }
331 >
332 >
333 > int GArgs::startNonOpt(){ //reset iteration through non-option arguments
334 >   //returns the number of non-option arguments
335   nonOptPos=0;
336   return nonOptCount;  
337   }
# Line 242 | Line 347
347   return NULL;
348   }
349  
350 < int GArgs::startOpt(){ //reset iteration through non-dashed arguments
351 <   //returns the number of non-dashed arguments
350 > int GArgs::startOpt(){ //reset iteration through option arguments
351 >   //returns the number of option arguments
352   optPos=0;
353   return count-nonOptCount;
354   }
# Line 258 | Line 363
363        }
364   return NULL;
365   }
366 +
367 + int GArgs::nextCode() { //get the next non-dashed argument
368 +               //or NULL if no more
369 + for (int i=optPos;i<count;i++)
370 + if (args[i].opt!=NULL && args[i].fmti>=0) {
371 +      optPos=i+1;
372 +      return fmt[args[i].fmti].code;
373 +      }
374 + return 0; //must make sure that codes are > 0 for this to work properly
375 + }
376 +

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines