| |
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 } |