optparser.c

changeset 0
86b7f6f9c5c0
child 3
b1fbfab67908
equal deleted inserted replaced
-1:000000000000 0:86b7f6f9c5c0
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 }

mercurial