Import libtu instead of using submodules
[notion/jeffpc.git] / libtu / optparser.c
blobfc65523bb9420ebee21e7d2579118eed3e122b1a
1 /*
2 * libtu/optparser.c
4 * Copyright (c) Tuomo Valkonen 1999-2004.
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 */
10 #include <string.h>
11 #include <stdlib.h>
13 #include "util.h"
14 #include "misc.h"
15 #include "optparser.h"
16 #include "output.h"
17 #include "private.h"
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)
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;
36 /* */
39 void optparser_init(int argc, char *const argv[], int mode,
40 const OptParserOpt *opts)
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;
52 /* */
55 static const OptParserOpt *find_chain_opt(char p, const OptParserOpt *o)
57 for(;O_ID(o);o++){
58 if((O_ID(o)&~OPT_ID_RESERVED_FLAG)==p)
59 return o;
61 return NULL;
65 static bool is_option(const char *p)
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;
79 /* */
81 enum{
82 SHORT, MIDLONG, LONG
86 int optparser_get_opt()
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;
95 while(o_args_left)
96 optparser_get_arg();
98 o_tmp=NULL;
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;
106 o=find_chain_opt(*p, o_opts);
108 if(o==NULL)
109 RET(E_OPT_INVALID_CHAIN_OPTION);
111 goto do_arg;
114 if(o_left<1)
115 return OPT_ID_END;
117 o_left--;
118 p=*o_current++;
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);
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);
141 if(*(p+2)!='\0' && o_mode==OPTP_MIDLONG)
142 type=MIDLONG;
144 p2=p+1;
147 o=o_opts;
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;
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);
171 continue;
172 }else if(type==MIDLONG){
173 if(o->longopt==NULL)
174 continue;
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;
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;
197 return O_ID(o);
198 }else{
199 RET(E_OPT_SYNTAX_ERROR);
204 do_arg:
206 if(!O_ARGS(o))
207 return O_ID(o);
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);
215 o_args_left=1;
216 return O_ID(o);
219 if(dash)
220 RET(E_OPT_INVALID_OPTION);
222 RET(OPT_ID_ARGUMENT);
223 #undef RET
227 /* */
230 const char* optparser_get_arg()
232 const char *p;
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)
239 if(o_args_left>0)
240 o_args_left--;
241 p=o_tmp;
242 o_tmp=NULL;
243 return p;
246 if(o_args_left<1 || o_left<1)
247 return NULL;
249 o_left--;
250 o_args_left--;
251 return *o_current++;
255 /* */
257 static void warn_arg(const char *e)
259 const char *p=optparser_get_arg();
261 if(p==NULL)
262 warn("%s (null)", e);
263 else
264 warn("%s \'%s\'", e, p);
268 static void warn_opt(const char *e)
270 if(o_tmp!=NULL && o_chain_ptr!=NULL)
271 warn("%s \'-%c\'", e, *o_tmp);
272 else
273 warn_arg(e);
277 void optparser_print_error()
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;
285 case E_OPT_SYNTAX_ERROR:
286 warn_arg(TR("Syntax error while parsing"));
287 break;
289 case E_OPT_MISSING_ARGUMENT:
290 warn_opt(TR("Missing argument to"));
291 break;
293 case E_OPT_UNEXPECTED_ARGUMENT:
294 warn_opt(TR("No argument expected:"));
295 break;
297 case OPT_ID_ARGUMENT:
298 warn(TR("Unexpected argument"));
299 break;
301 default:
302 warn(TR("(unknown error)"));
305 o_tmp=NULL;
306 o_error=0;
310 /* */
313 static uint opt_w(const OptParserOpt *opt, bool midlong)
315 uint w=0;
317 if((opt->optid&OPT_ID_NOSHORT_FLAG)==0){
318 w+=2; /* "-o" */
319 if(opt->longopt!=NULL)
320 w+=2; /* ", " */
323 if(opt->longopt!=NULL)
324 w+=strlen(opt->longopt)+(midlong ? 1 : 2);
326 if(O_ARGS(opt)){
327 if(opt->argname==NULL)
328 w+=4;
329 else
330 w+=1+strlen(opt->argname); /* "=ARG" or " ARG" */
332 if(O_OPT_ARG(opt))
333 w+=2; /* [ARG] */
336 return w;
340 #define TERM_W 80
341 #define OFF1 2
342 #define OFF2 2
343 #define SPACER1 " "
344 #define SPACER2 " "
346 static void print_opt(const OptParserOpt *opt, bool midlong,
347 uint maxw, uint tw)
349 FILE *f=stdout;
350 const char *p, *p2, *p3;
351 uint w=0;
353 fprintf(f, SPACER1);
355 /* short opt */
357 if((O_ID(opt)&OPT_ID_NOSHORT_FLAG)==0){
358 fprintf(f, "-%c", O_ID(opt)&~OPT_ID_RESERVED_FLAG);
359 w+=2;
361 if(opt->longopt!=NULL){
362 fprintf(f, ", ");
363 w+=2;
367 /* long opt */
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);
377 w+=strlen(opt->longopt);
380 /* arg */
382 if(O_ARGS(opt)){
383 w++;
384 if(opt->longopt!=NULL && !midlong)
385 putc('=', f);
386 else
387 putc(' ', f);
389 if(O_OPT_ARG(opt)){
390 w+=2;
391 putc('[', f);
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");
402 if(O_OPT_ARG(opt))
403 putc(']', f);
406 while(w++<maxw)
407 putc(' ', f);
409 /* descr */
411 p=p2=opt->descr;
413 if(p==NULL){
414 putc('\n', f);
415 return;
418 fprintf(f, SPACER2);
420 maxw+=OFF1+OFF2;
421 tw-=maxw;
423 while(strlen(p)>tw){
424 p3=p2=p+tw-2;
426 while(*p2!=' ' && p2!=p)
427 p2--;
429 while(*p3!=' ' && *p3!='\0')
430 p3++;
432 if((uint)(p3-p2)>tw){
433 /* long word - just wrap */
434 p2=p+tw-2;
437 writef(f, p, p2-p);
438 if(*p2==' ')
439 putc('\n', f);
440 else
441 fprintf(f, "\\\n");
443 p=p2+1;
445 w=maxw;
446 while(w--)
447 putc(' ', f);
450 fprintf(f, "%s\n", p);
454 void optparser_printhelp(int mode, const OptParserOpt *opts)
456 uint w, maxw=0;
457 const OptParserOpt *o;
458 bool midlong=mode&OPTP_MIDLONG;
460 o=opts;
461 for(; O_ID(o); o++){
462 w=opt_w(o, midlong);
463 if(w>maxw)
464 maxw=w;
467 o=opts;
469 for(; O_ID(o); o++)
470 print_opt(o, midlong, maxw, TERM_W);