|
1 /* |
|
2 * libtu/optparser.c |
|
3 * |
|
4 * Copyright (c) Tuomo Valkonen 1999-2000. |
|
5 * |
|
6 * This file is distributed under the terms of the "Artistic License". |
|
7 * See the included file LICENSE for details. |
|
8 */ |
|
9 |
|
10 #include <string.h> |
|
11 #include <stdlib.h> |
|
12 |
|
13 #include "include/util.h" |
|
14 #include "include/misc.h" |
|
15 #include "include/optparser.h" |
|
16 #include "include/output.h" |
|
17 |
|
18 |
|
19 #define O_CHAINABLE(o) (o->flags&OPT_CHAINABLE) |
|
20 #define O_IMM_ARG(o) (o->flags&OPT__IMM_ARG) |
|
21 #define O_NO_DASH(o) (o->flags&OPT_NO_DASH) |
|
22 #define O_MIDLONG(o) (o->flags&OPT_MIDLONG) |
|
23 #define O_NO_LONG(o) (o->flags&OPT_NO_LONG) |
|
24 #define O_ARGS(o) (o->flags&OPT_OPT_ARG) |
|
25 #define O_ARG(o) (o->flasg&OPT_ARG) |
|
26 #define O_OPT_ARG(o) (O_ARGS(o)==OPT_OPT_ARG) |
|
27 #define O_ID(o) (o->optid) |
|
28 |
|
29 |
|
30 static const OptParserOpt *o_opts=NULL; |
|
31 static char *const *o_current=NULL; |
|
32 static int o_left=0; |
|
33 static const char* o_chain_ptr=NULL; |
|
34 static int o_args_left=0; |
|
35 static const char*o_tmp=NULL; |
|
36 static int o_error=0; |
|
37 |
|
38 |
|
39 /* */ |
|
40 |
|
41 |
|
42 void optparser_init(int argc, char *const argv[], const OptParserOpt *opts) |
|
43 { |
|
44 o_opts=opts; |
|
45 o_current=argv+1; |
|
46 o_left=argc-1; |
|
47 o_chain_ptr=NULL; |
|
48 o_args_left=0; |
|
49 o_tmp=NULL; |
|
50 } |
|
51 |
|
52 |
|
53 /* */ |
|
54 |
|
55 |
|
56 static const OptParserOpt *find_chain_opt(char p, const OptParserOpt *o) |
|
57 { |
|
58 for(;O_ID(o);o++){ |
|
59 if(O_CHAINABLE(o) && O_ID(o)==p) |
|
60 return o; |
|
61 } |
|
62 return NULL; |
|
63 } |
|
64 |
|
65 |
|
66 static bool valid_chain(const char *p, const OptParserOpt *o) |
|
67 { |
|
68 while(*p!='\0'){ |
|
69 if(!find_chain_opt(*p, o)) |
|
70 return FALSE; |
|
71 p++; |
|
72 } |
|
73 return TRUE; |
|
74 } |
|
75 |
|
76 |
|
77 static bool is_option(const char *p) |
|
78 { |
|
79 if(p==NULL) |
|
80 return FALSE; |
|
81 if(*p++!='-') |
|
82 return FALSE; |
|
83 if(*p++!='-') |
|
84 return FALSE; |
|
85 if(*p=='\0') |
|
86 return FALSE; |
|
87 return TRUE; |
|
88 } |
|
89 |
|
90 |
|
91 /* */ |
|
92 |
|
93 |
|
94 int optparser_get_opt() |
|
95 { |
|
96 #define RET(X) return o_tmp=p, o_error=X |
|
97 const char *p, *p2=NULL; |
|
98 bool lo=FALSE, dash=TRUE; |
|
99 const OptParserOpt *o; |
|
100 int l; |
|
101 |
|
102 while(o_args_left) |
|
103 optparser_get_arg(); |
|
104 |
|
105 o_tmp=NULL; |
|
106 |
|
107 /* Are we doing a chain (i.e. opt. of style 'tar xzf') ? */ |
|
108 if(o_chain_ptr!=NULL){ |
|
109 p=o_chain_ptr++; |
|
110 if(!*o_chain_ptr) |
|
111 o_chain_ptr=NULL; |
|
112 |
|
113 o=find_chain_opt(*p, o_opts); |
|
114 |
|
115 if(o==NULL) |
|
116 RET(E_OPT_INVALID_CHAIN_OPTION); |
|
117 |
|
118 goto do_arg; |
|
119 } |
|
120 |
|
121 if(o_left<1) |
|
122 return OPT_ID_END; |
|
123 |
|
124 o_left--; |
|
125 p=*o_current++; |
|
126 |
|
127 if(*p!='-'){ |
|
128 /* foo */ |
|
129 dash=FALSE; |
|
130 }else if(*(p+1)=='-'){ |
|
131 /* --foo */ |
|
132 if(*(p+2)=='\0'){ |
|
133 /* -- */ |
|
134 o_args_left=o_left; |
|
135 RET(OPT_ID_ARGUMENT); |
|
136 } |
|
137 lo=TRUE; |
|
138 p2=p+2; |
|
139 }else{ |
|
140 /* -foo */ |
|
141 if(*(p+1)=='\0'){ |
|
142 /* - */ |
|
143 o_args_left=1; |
|
144 RET(OPT_ID_ARGUMENT); |
|
145 } |
|
146 p2=p+1; |
|
147 } |
|
148 |
|
149 o=o_opts; |
|
150 |
|
151 again: |
|
152 for(;O_ID(o);o++){ |
|
153 if(lo){ |
|
154 /* Do long option (--foo=bar) */ |
|
155 if(o->longopt==NULL) |
|
156 continue; |
|
157 l=strlen(o->longopt); |
|
158 if(strncmp(p2, o->longopt, l)!=0) |
|
159 continue; |
|
160 |
|
161 if(O_NO_LONG(o)) |
|
162 RET(E_OPT_INVALID_OPTION); |
|
163 |
|
164 if(p2[l]=='\0'){ |
|
165 if(O_ARGS(o)==OPT_ARG) |
|
166 RET(E_OPT_MISSING_ARGUMENT); |
|
167 return O_ID(o); |
|
168 }else if(p2[l]=='='){ |
|
169 if(!O_ARGS(o)) |
|
170 RET(E_OPT_UNEXPECTED_ARGUMENT); |
|
171 if(p2[l+1]=='\0') |
|
172 RET(E_OPT_MISSING_ARGUMENT); |
|
173 o_tmp=p2+l+1; |
|
174 o_args_left=1; |
|
175 return O_ID(o); |
|
176 } |
|
177 }else{ |
|
178 /* Do midlong option (-foobar) */ |
|
179 if(dash && o->longopt!=NULL && strcmp(p2, o->longopt)==0){ |
|
180 if(!O_MIDLONG(o)) |
|
181 RET(E_OPT_INVALID_OPTION); |
|
182 goto do_arg; |
|
183 } |
|
184 |
|
185 /* Do short option (-f) */ |
|
186 if((!dash && !O_NO_DASH(o)) || *p2!=O_ID(o)) |
|
187 continue; |
|
188 |
|
189 if(*(p2+1)!='\0'){ |
|
190 if(O_CHAINABLE(o) && valid_chain(p2+1, o_opts)){ |
|
191 o_chain_ptr=p2+1; |
|
192 }else if(O_IMM_ARG(o)){ |
|
193 o_tmp=p2+1; |
|
194 o_args_left=1; |
|
195 return O_ID(o); |
|
196 }else{ |
|
197 continue; |
|
198 } |
|
199 } |
|
200 do_arg: |
|
201 if(!O_ARGS(o)) |
|
202 return O_ID(o); |
|
203 |
|
204 if(O_ARGS(o)==OPT_ARG){ |
|
205 if(o_left==0) |
|
206 RET(E_OPT_MISSING_ARGUMENT); |
|
207 }else{ |
|
208 if(!o_left || is_option(*o_current)) |
|
209 return O_ID(o); |
|
210 } |
|
211 |
|
212 o_args_left=1; |
|
213 return O_ID(o); |
|
214 } |
|
215 } |
|
216 |
|
217 if(dash) |
|
218 RET(E_OPT_INVALID_OPTION); |
|
219 |
|
220 RET(OPT_ID_ARGUMENT); |
|
221 #undef RET |
|
222 } |
|
223 |
|
224 |
|
225 /* */ |
|
226 |
|
227 |
|
228 const char* optparser_get_arg() |
|
229 { |
|
230 const char *p; |
|
231 |
|
232 if(o_tmp!=NULL){ |
|
233 /* If o_args_left==0, then were returning an invalid option |
|
234 * otherwise an immediate argument (e.g. -funsigned-char |
|
235 * where '-f' is the option and 'unsigned-char' the argument) |
|
236 */ |
|
237 if(o_args_left>0) |
|
238 o_args_left--; |
|
239 p=o_tmp; |
|
240 o_tmp=NULL; |
|
241 return p; |
|
242 } |
|
243 |
|
244 if(o_args_left<1 || o_left<1) |
|
245 return NULL; |
|
246 |
|
247 o_left--; |
|
248 o_args_left--; |
|
249 return *o_current++; |
|
250 } |
|
251 |
|
252 |
|
253 /* */ |
|
254 |
|
255 static void warn_arg(const char *e) |
|
256 { |
|
257 const char*p=optparser_get_arg(); |
|
258 |
|
259 if(p==NULL) |
|
260 warn("%s (null)", e); |
|
261 else |
|
262 warn("%s \'%s\'", e, p); |
|
263 } |
|
264 |
|
265 |
|
266 static void warn_opt(const char *e) |
|
267 { |
|
268 if(o_chain_ptr!=NULL && o_tmp!=NULL) |
|
269 warn("%s \'-%c\'", e, *o_tmp); |
|
270 else |
|
271 warn_arg(e); |
|
272 } |
|
273 |
|
274 |
|
275 void optparser_print_error() |
|
276 { |
|
277 switch(o_error){ |
|
278 case E_OPT_INVALID_OPTION: |
|
279 warn_opt(TR("Invalid option")); |
|
280 break; |
|
281 |
|
282 case E_OPT_INVALID_CHAIN_OPTION: |
|
283 warn_opt(TR("Invalid option in chain (this cannot happen)")); |
|
284 break; |
|
285 |
|
286 case E_OPT_SYNTAX_ERROR: |
|
287 warn_arg(TR("Syntax error while parsing")); |
|
288 break; |
|
289 |
|
290 case E_OPT_MISSING_ARGUMENT: |
|
291 warn_opt(TR("Missing argument to")); |
|
292 break; |
|
293 |
|
294 case E_OPT_UNEXPECTED_ARGUMENT: |
|
295 warn_opt(TR("No argument expected to")); |
|
296 break; |
|
297 |
|
298 case OPT_ID_ARGUMENT: |
|
299 warn(TR("Unexpected argument")); |
|
300 break; |
|
301 |
|
302 default: |
|
303 warn(TR("(unknown error)")); |
|
304 } |
|
305 |
|
306 o_tmp=NULL; |
|
307 o_error=0; |
|
308 } |