Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 300 lines (269 sloc) 6.898 kb
89dbed7 @antirez Visitors moved to Git
authored
1 /* antigetopt -- a getopt replacement
2 * Copyright(C) 2001 Salvatore Sanfilippo <antirez@invece.org>
7be3608 @antirez License moved to BSD
authored
3 * This software is released under the BSD license (see COPYING)
89dbed7 @antirez Visitors moved to Git
authored
4 * see the COPYING file for more information */
5
6 /* $Id: antigetopt.c,v 1.1.1.1 2005/10/02 22:44:47 antirez Exp $ */
7
8 /* TODO:
9 * argument list sanity check */
10
11 #include <stdlib.h>
12 #include <string.h>
13 #include <stdio.h>
14
15 #include "antigetopt.h"
16
17 /* global vars */
18 char *ago_optarg = NULL;
19 char *ago_optname = NULL;
20 char ago_optchar = '\0';
21
22 /* static vars */
23 static struct ago_exception {
24 int (*tester)(void);
25 char *msg;
26 } ago_exceptions[3] = {
27 { NULL, NULL },
28 { NULL, NULL },
29 { NULL, NULL }
30 };
31
32 static int ago_exception_bits[] = { AGO_EXCEPT0, AGO_EXCEPT1, AGO_EXCEPT2 };
33
34 /* static functions */
35 static struct ago_optlist
36 *ago_lookup(struct ago_optlist *list, char *arg, int *islong, int *amb);
37 static int strinitcmp(char *a, char *b);
38
39 /*----------------------------- implementation ------------------------------ */
40
41 int antigetopt(int argc, char **argv, struct ago_optlist *list)
42 {
43 static char **save_argv = NULL;
44 static char *chain = NULL;
45 static int endoptions = 0;
46 struct ago_optlist *opt;
47 int islong;
48
49 argc = argc; /* avoid a warning */
50
51 /* Reset */
52 if (argv == NULL) {
53 save_argv = NULL;
54 chain = NULL;
55 endoptions = 0;
56 return AGO_RESET;
57 } else {
58 if (save_argv == NULL) {
59 save_argv = argv+1; /* skips the argv[0] */
60 /* XXX: argument list sanity check */
61 }
62 }
63
64 chain_start:
65 if (chain) {
66 if (*chain == '\0')
67 chain = NULL;
68 else {
69 if ((opt = ago_lookup(list, chain, &islong, NULL))
70 == NULL)
71 return AGO_UNKNOWN;
72 if (!(opt->ao_flags & AGO_NOARG)) {
73 /* the if expression maybe false if the
74 * argument is optional */
75 if (chain[1] == '\0' && *save_argv)
76 ago_optarg = *save_argv++;
77 /* while it is mandatory for the NEEDARG type */
78 else if (opt->ao_flags & AGO_NEEDARG)
79 return AGO_REQARG;
80 }
81 chain++;
82 return opt->ao_id;
83 }
84 }
85
86 argv = save_argv;
87
88 /* handle the "--" special option */
89 if (*argv && strcmp(*argv, "--") == 0) {
90 endoptions = 1;
91 argv++;
92 save_argv++;
93 }
94
95 while(*argv) {
96 /* The option must start with '-' */
97 if (!endoptions && argv[0][0] == '-' && argv[0][1] != '\0') {
98 int amb;
99
100 /* note: ago_lookup also sets ago_optname */
101 if ((opt = ago_lookup(list, argv[0], &islong, &amb))
102 == NULL)
103 return amb ? AGO_AMBIG : AGO_UNKNOWN;
104
105 /* handle the collapsed short options */
106 if (!islong && argv[0][2] != '\0') {
107 chain = argv[0]+1;
108 save_argv++;
109 goto chain_start;
110 }
111
112 /* if the option require or may have an argument */
113 ago_optarg = NULL;
114 /* If the argument is needed we get the next argv[]
115 * element without care about what it contains */
116 if (opt->ao_flags & AGO_NEEDARG) {
117 if (argv[1] == NULL)
118 return AGO_REQARG;
119 ago_optarg = argv[1];
120 argv++;
121 }
122 /* If the argument is optional we only recognize it
123 * as argument if it does not starts with '-' */
124 else if (opt->ao_flags & AGO_OPTARG) {
125 if (argv[1] && argv[1][0] != '-') {
126 ago_optarg = argv[1];
127 argv++;
128 }
129 }
130 save_argv = argv+1;
131 return opt->ao_id;
132 } else {
133 save_argv = argv+1;
134 ago_optarg = argv[0];
135 ago_optchar = '\0';
136 ago_optname = NULL;
137 return AGO_ALONE;
138 }
139 }
140 return AGO_EOF;
141 }
142
143 #define UNK_SHORT_ERRSTRING "invalid option -- %c\n"
144 #define UNK_LONG_ERRSTRING "unrecognized option `--%s'\n"
145 #define ARG_SHORT_ERRSTRING "option requires an argument -- %c\n"
146 #define ARG_LONG_ERRSTRING "option `--%s' requires an argument\n"
147 #define AMB_ERRSTRING "option `--%s' is ambiguos\n"
148 #define IERR_ERRSTRING "internal error. ago_gnu_error() called with " \
149 "a bad error code (%d)\n"
150 void ago_gnu_error(char *pname, int error)
151 {
152 if (pname)
153 fprintf(stderr, "%s: ", pname);
154 switch(error) {
155 case AGO_UNKNOWN:
156 if (ago_optname)
157 fprintf(stderr, UNK_LONG_ERRSTRING,
158 ago_optname);
159 else
160 fprintf(stderr, UNK_SHORT_ERRSTRING,
161 ago_optchar);
162 break;
163 case AGO_REQARG:
164 if (ago_optname)
165 fprintf(stderr, ARG_LONG_ERRSTRING,
166 ago_optname);
167 else
168 fprintf(stderr, ARG_SHORT_ERRSTRING,
169 ago_optchar);
170 break;
171 case AGO_AMBIG:
172 fprintf(stderr, AMB_ERRSTRING, ago_optname);
173 break;
174 default:
175 fprintf(stderr, IERR_ERRSTRING, error);
176 break;
177 }
178 }
179
180 int ago_set_exception(int except_nr, int (*tester)(void), char *msg)
181 {
182 if (tester == NULL || msg == NULL || except_nr < 0 || except_nr >= 3)
183 return -1;
184 ago_exceptions[except_nr].tester = tester;
185 ago_exceptions[except_nr].msg = msg;
186 return 0;
187 }
188
189 /*-------------------------- static functions ------------------------------- */
190
191 struct ago_optlist
192 *ago_lookup(struct ago_optlist *list, char *arg, int *islong, int *amb)
193 {
194 int i;
195
196 /* ago_lookup can be receive as `arg' a pointer to a
197 * long argument, like --option, a pointer to a short
198 * argument like -O, or just a pointer to a char sequence
199 * in the case of collapsed short arguments like -abcde. */
200
201 /* Clear the 'ambiguos' flag, used to report the caller
202 * an ambiguos option abbreviation error */
203 if (amb) *amb = 0;
204
205 if (*arg == '-') /* skips the first - if any */
206 arg++;
207
208 switch(*arg) {
209 case '\0':
210 return NULL;
211 case '-':
212 *islong = 1;
213 arg++; /* skip the last - */
214 break;
215 default:
216 *islong = 0;
217 break;
218 }
219
220 /* search the argument in the list */
221 if (*islong) {
222 int retval;
223 struct ago_optlist *last = NULL;
224
225 while(!(list->ao_flags & AGO_ENDOFLIST)) {
226 ago_optname = arg;
227 ago_optchar = '\0';
228 if ((retval = strinitcmp(arg, list->ao_long)) != 0) {
229 switch(retval) {
230 case 1:
231 if (last) {
232 if (amb) *amb = 1;
233 return NULL;
234 }
235 last = list;
236 break;
237 case 2:
238 goto ok;
239 }
240 }
241 list++;
242 }
243 if (last) {
244 ago_optname = last->ao_long;
245 list = last;
246 goto ok;
247 }
248 } else {
249 ago_optchar = *arg;
250 ago_optname = NULL;
251 while(!(list->ao_flags & AGO_ENDOFLIST)) {
252 if (*arg == list->ao_short)
253 goto ok;
254 list++;
255 }
256 }
257 return NULL;
258 ok:
259 /* handle the exceptions if any */
260 for (i = 0; i < 3; i++) {
261 if ((list->ao_flags & ago_exception_bits[i]) &&
262 ago_exceptions[i].tester)
263 {
264 if (ago_exceptions[i].tester()) {
265 if (ago_optname) {
266 fprintf(stderr, "%s `--%s'\n",
267 ago_exceptions[i].msg,
268 ago_optname);
269 } else {
270 fprintf(stderr, "%s `-%c'\n",
271 ago_exceptions[i].msg,
272 ago_optchar);
273 }
274 exit(1);
275 }
276 }
277 }
278 return list;
279 }
280
281 /* Given two strings this function returns:
282 * 1, if the strings are the same for the len of the first string (abc, abcde)
283 * 2, if the strings are exactly the same: (abcd, abcd)
284 * otherwise zero is returned (abcde, abcd) ... (djf, 293492) */
285 int strinitcmp(char *a, char *b)
286 {
287 if (!a || !b)
288 return 0;
289 while (*a && *b) {
290 if (*a != *b)
291 return 0;
292 a++; b++;
293 }
294 if (*a)
295 return 0;
296 if (*a == *b)
297 return 2;
298 return 1;
299 }
Something went wrong with that request. Please try again.