]> git.decadent.org.uk Git - ion3.git/blob - libtu/optparser.c
[svn-inject] Installing original source of ion3
[ion3.git] / libtu / optparser.c
1 /*
2  * libtu/optparser.c
3  *
4  * Copyright (c) Tuomo Valkonen 1999-2004.
5  *
6  * You may distribute and modify this library under the terms of either
7  * the Clarified Artistic License or the GNU LGPL, version 2.1 or later.
8  */
9
10 #include <string.h>
11 #include <stdlib.h>
12
13 #include "util.h"
14 #include "misc.h"
15 #include "optparser.h"
16 #include "output.h"
17 #include "private.h"
18
19
20 #define O_ARGS(o)       (o->flags&OPT_OPT_ARG)
21 #define O_ARG(o)        (o->flasg&OPT_ARG)
22 #define O_OPT_ARG(o)    (O_ARGS(o)==OPT_OPT_ARG)
23 #define O_ID(o)         (o->optid)
24
25
26 static const OptParserOpt *o_opts=NULL;
27 static char *const *o_current=NULL;
28 static int o_left=0;
29 static const char* o_chain_ptr=NULL;
30 static int o_args_left=0;
31 static const char*o_tmp=NULL;
32 static int o_error=0;
33 static int o_mode=OPTP_CHAIN;
34
35
36 /* */
37
38
39 void optparser_init(int argc, char *const argv[], int mode,
40                     const OptParserOpt *opts)
41 {
42     o_mode=mode;
43     o_opts=opts;
44     o_current=argv+1;
45     o_left=argc-1;
46     o_chain_ptr=NULL;
47     o_args_left=0;
48     o_tmp=NULL;
49 }
50
51
52 /* */
53
54
55 static const OptParserOpt *find_chain_opt(char p, const OptParserOpt *o)
56 {
57     for(;O_ID(o);o++){
58         if((O_ID(o)&~OPT_ID_RESERVED_FLAG)==p)
59             return o;
60     }
61     return NULL;
62 }
63
64
65 static bool is_option(const char *p)
66 {
67      if(p==NULL)
68           return FALSE;
69      if(*p++!='-')
70           return FALSE;
71      if(*p++!='-')
72           return FALSE;
73      if(*p=='\0')
74           return FALSE;
75      return TRUE;
76 }
77     
78
79 /* */
80
81 enum{
82     SHORT, MIDLONG, LONG
83 };
84
85
86 int optparser_get_opt()
87 {
88 #define RET(X) return o_tmp=p, o_error=X
89     const char *p, *p2=NULL;
90     bool dash=TRUE;
91     int type=SHORT;
92     const OptParserOpt *o;
93     int l;
94
95     while(o_args_left)
96         optparser_get_arg();
97
98     o_tmp=NULL;
99
100     /* Are we doing a chain (i.e. opt. of style 'tar xzf')? */
101     if(o_chain_ptr!=NULL){
102         p=o_chain_ptr++;
103         if(!*o_chain_ptr)
104             o_chain_ptr=NULL;
105
106         o=find_chain_opt(*p, o_opts);
107         
108         if(o==NULL)
109             RET(E_OPT_INVALID_CHAIN_OPTION);
110
111         goto do_arg;
112     }
113     
114     if(o_left<1)
115         return OPT_ID_END;
116
117     o_left--;
118     p=*o_current++;
119     
120     if(*p!='-'){
121         dash=FALSE;
122         if(o_mode!=OPTP_NO_DASH)
123             RET(OPT_ID_ARGUMENT);
124         p2=p;
125     }else if(*(p+1)=='-'){
126         /* --foo */
127         if(*(p+2)=='\0'){
128             /* -- arguments */
129             o_args_left=o_left;
130             RET(OPT_ID_ARGUMENT);
131         }
132         type=LONG;
133         p2=p+2;
134     }else{
135         /* -foo */
136         if(*(p+1)=='\0'){
137             /* - */
138             o_args_left=1;
139             RET(OPT_ID_ARGUMENT);
140         }
141         if(*(p+2)!='\0' && o_mode==OPTP_MIDLONG)
142             type=MIDLONG;
143     
144         p2=p+1;
145     }
146
147     o=o_opts;
148
149     for(; O_ID(o); o++){
150         if(type==LONG){
151             /* Do long option (--foo=bar) */
152             if(o->longopt==NULL)
153                 continue;
154             l=strlen(o->longopt);
155             if(strncmp(p2, o->longopt, l)!=0)
156                 continue;
157             
158             if(p2[l]=='\0'){
159                 if(O_ARGS(o)==OPT_ARG)
160                      RET(E_OPT_MISSING_ARGUMENT);
161                 return O_ID(o);
162             }else if(p2[l]=='='){
163                 if(!O_ARGS(o))
164                      RET(E_OPT_UNEXPECTED_ARGUMENT);
165                 if(p2[l+1]=='\0')
166                      RET(E_OPT_MISSING_ARGUMENT);
167                 o_tmp=p2+l+1;
168                 o_args_left=1;
169                 return O_ID(o);
170             }
171             continue;
172         }else if(type==MIDLONG){
173             if(o->longopt==NULL)
174                 continue;
175             
176             if(strcmp(p2, o->longopt)!=0)
177                 continue;
178         }else{ /* type==SHORT */
179             if(*p2!=(O_ID(o)&~OPT_ID_RESERVED_FLAG))
180                 continue;
181                
182             if(*(p2+1)!='\0'){
183                 if(o_mode==OPTP_CHAIN || o_mode==OPTP_NO_DASH){
184                     /*valid_chain(p2+1, o_opts)*/
185                     o_chain_ptr=p2+1;
186                     p++;
187                 }else if(o_mode==OPTP_IMMEDIATE){
188                     if(!O_ARGS(o)){
189                         if(*(p2+1)!='\0')
190                             RET(E_OPT_UNEXPECTED_ARGUMENT);
191                     }else{
192                         if(*(p2+1)=='\0')
193                             RET(E_OPT_MISSING_ARGUMENT);
194                         o_tmp=p2+1;
195                         o_args_left=1;
196                     }
197                     return O_ID(o);
198                 }else{
199                     RET(E_OPT_SYNTAX_ERROR);
200                 }
201             }
202         }
203         
204     do_arg:
205         
206         if(!O_ARGS(o))
207             return O_ID(o);
208         
209         if(!o_left || is_option(*o_current)){
210             if(O_ARGS(o)==OPT_OPT_ARG)
211                 return O_ID(o);
212             RET(E_OPT_MISSING_ARGUMENT);
213         }
214
215         o_args_left=1;
216         return O_ID(o);
217     }
218     
219     if(dash)
220         RET(E_OPT_INVALID_OPTION);
221     
222     RET(OPT_ID_ARGUMENT);
223 #undef RET
224 }
225
226
227 /* */
228
229
230 const char* optparser_get_arg()
231 {
232     const char *p;
233     
234     if(o_tmp!=NULL){
235         /* If o_args_left==0, then were returning an invalid option
236          * otherwise an immediate argument (e.g. -funsigned-char
237          * where '-f' is the option and 'unsigned-char' the argument)
238          */
239         if(o_args_left>0)
240             o_args_left--;
241         p=o_tmp;
242         o_tmp=NULL;
243         return p;
244     }
245         
246     if(o_args_left<1 || o_left<1)
247         return NULL;
248
249     o_left--;
250     o_args_left--;
251     return *o_current++;
252 }
253
254
255 /* */
256
257 static void warn_arg(const char *e)
258 {
259     const char *p=optparser_get_arg();
260     
261     if(p==NULL)
262         warn("%s (null)", e);
263     else
264         warn("%s \'%s\'", e, p);
265 }
266
267
268 static void warn_opt(const char *e)
269 {
270     if(o_tmp!=NULL && o_chain_ptr!=NULL)
271         warn("%s \'-%c\'", e, *o_tmp);
272     else
273         warn_arg(e);
274 }
275
276
277 void optparser_print_error()
278 {
279     switch(o_error){
280     case E_OPT_INVALID_OPTION:
281     case E_OPT_INVALID_CHAIN_OPTION:
282         warn_opt(TR("Invalid option"));
283         break;
284
285     case E_OPT_SYNTAX_ERROR:
286         warn_arg(TR("Syntax error while parsing"));
287         break;
288                  
289     case E_OPT_MISSING_ARGUMENT:
290         warn_opt(TR("Missing argument to"));
291         break;
292
293     case E_OPT_UNEXPECTED_ARGUMENT:
294         warn_opt(TR("No argument expected:"));
295         break;
296         
297     case OPT_ID_ARGUMENT:
298         warn(TR("Unexpected argument"));
299         break;
300
301     default:
302         warn(TR("(unknown error)"));
303     }
304     
305     o_tmp=NULL;
306     o_error=0;
307 }
308
309
310 /* */
311
312
313 static uint opt_w(const OptParserOpt *opt, bool midlong)
314 {
315     uint w=0;
316     
317     if((opt->optid&OPT_ID_NOSHORT_FLAG)==0){
318         w+=2; /* "-o" */
319         if(opt->longopt!=NULL)
320             w+=2; /* ", " */
321     }
322     
323     if(opt->longopt!=NULL)
324         w+=strlen(opt->longopt)+(midlong ? 1 : 2);
325     
326     if(O_ARGS(opt)){
327         if(opt->argname==NULL)
328             w+=4;
329         else
330             w+=1+strlen(opt->argname); /* "=ARG" or " ARG" */
331         
332         if(O_OPT_ARG(opt))
333             w+=2; /* [ARG] */
334     }
335     
336     return w;
337 }
338
339
340 #define TERM_W 80
341 #define OFF1 2
342 #define OFF2 2
343 #define SPACER1 "  "
344 #define SPACER2 "  "
345
346 static void print_opt(const OptParserOpt *opt, bool midlong,
347                       uint maxw, uint tw)
348 {
349     FILE *f=stdout;
350     const char *p, *p2, *p3;
351     uint w=0;
352     
353     fprintf(f, SPACER1);
354     
355     /* short opt */
356     
357     if((O_ID(opt)&OPT_ID_NOSHORT_FLAG)==0){
358         fprintf(f, "-%c", O_ID(opt)&~OPT_ID_RESERVED_FLAG);
359         w+=2;
360         
361         if(opt->longopt!=NULL){
362             fprintf(f, ", ");
363             w+=2;
364         }
365     }
366     
367     /* long opt */
368     
369     if(opt->longopt!=NULL){
370         if(midlong){
371             w++;
372             fprintf(f, "-%s", opt->longopt);
373         }else{
374             w+=2;
375             fprintf(f, "--%s", opt->longopt);
376         }
377         w+=strlen(opt->longopt);
378     }
379     
380     /* arg */
381     
382     if(O_ARGS(opt)){
383         w++;
384         if(opt->longopt!=NULL && !midlong)
385             putc('=', f);
386         else
387             putc(' ', f);
388         
389         if(O_OPT_ARG(opt)){
390             w+=2;
391             putc('[', f);
392         }
393         
394         if(opt->argname!=NULL){
395             fprintf(f, "%s", opt->argname);
396             w+=strlen(opt->argname);
397         }else{
398             w+=3;
399             fprintf(f, "ARG");
400         }
401
402         if(O_OPT_ARG(opt))
403             putc(']', f);
404     }
405     
406     while(w++<maxw)
407         putc(' ', f);
408     
409     /* descr */
410     
411     p=p2=opt->descr;
412     
413     if(p==NULL){
414         putc('\n', f);
415         return;
416     }
417     
418     fprintf(f, SPACER2);
419
420     maxw+=OFF1+OFF2;
421     tw-=maxw;
422     
423     while(strlen(p)>tw){
424         p3=p2=p+tw-2;
425         
426         while(*p2!=' ' && p2!=p)
427             p2--;
428         
429         while(*p3!=' ' && *p3!='\0')
430             p3++;
431         
432         if((uint)(p3-p2)>tw){
433             /* long word - just wrap */
434             p2=p+tw-2;
435         }
436             
437         writef(f, p, p2-p);
438         if(*p2==' ')
439             putc('\n', f);
440         else
441             fprintf(f, "\\\n");
442         
443         p=p2+1;
444         
445         w=maxw;
446         while(w--)
447             putc(' ', f);
448     }
449     
450     fprintf(f, "%s\n", p);
451 }
452
453                       
454 void optparser_printhelp(int mode, const OptParserOpt *opts)
455 {
456     uint w, maxw=0;
457     const OptParserOpt *o;
458     bool midlong=mode&OPTP_MIDLONG;
459     
460     o=opts;
461     for(; O_ID(o); o++){
462         w=opt_w(o, midlong);
463         if(w>maxw)
464             maxw=w;
465     }
466     
467     o=opts;
468     
469     for(; O_ID(o); o++)
470         print_opt(o, midlong, maxw, TERM_W);
471 }