1 /*
2 * sqsh_getopt.c - Reusable version of getopt(3c)
3 *
4 * Copyright (C) 1995, 1996 by Scott C. Gray
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, write to the Free Software
18 * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
19 *
20 * You may contact the author :
21 * e-mail: gray@voicenet.com
22 * grays@xtend-tech.com
23 * gray@xenotropic.com
24 */
25 #include <stdio.h>
26 #include <ctype.h>
27 #include "sqsh_config.h"
28 #include "sqsh_error.h"
29 #include "sqsh_varbuf.h"
30 #include "sqsh_global.h"
31 #include "sqsh_env.h"
32 #include "sqsh_getopt.h"
33
34 /*-- Current Version --*/
35 #if !defined(lint) && !defined(__LINT__)
36 static char RCS_Id[] = "$Id: sqsh_getopt.c,v 1.4 2013/02/24 22:21:10 mwesdorp Exp $" ;
37 USE(RCS_Id)
38 #endif /* !defined(lint) */
39
40 /*
41 * The following variables don't following my usual naming convention
42 * for global variables, primarily because I wanted them to look
43 * like the standard getopt variable names.
44 */
45 int sqsh_optind ;
46 char *sqsh_optarg ;
47
48 static char **sg_argv = NULL ; /* Current argv we are using */
49 static char *sg_flags = NULL ;
50 static int sg_nargs; /* Number of arguments left to process */
51 static int sg_argc ; /* Current end of argv */
52
53 /*
54 * The is_flag() macro is used to test a string to see if it is
55 * potentially a flag (that is, it is of the format -[a-zA-Z0-9] or -\250).
56 */
57 #define is_flag(s) (*(s) == '-' && (isalnum((int)*((s)+1)) || *((s)+1) == '\250'))
58
59 /*-- Prototypes --*/
60 static int sqsh_move _ANSI_ARGS(( int, char**, int ));
61
sqsh_getopt(argc,argv,opt_flags)62 int sqsh_getopt( argc, argv, opt_flags )
63 int argc;
64 char *argv[];
65 char *opt_flags;
66 {
67 char *opt;
68 int flag; /* Which flag we have hit */
69 int ignore;
70 int nskip;
71 int i;
72
73 /*
74 * If any one of argc, argv, or opt_flags is different from what
75 * we saw the last time we were run, then we have a new set of
76 * options and should start over again.
77 */
78 if( sg_argv != argv || sg_argc != argc || 0 != strcmp( sg_flags, opt_flags) ) {
79 sqsh_optind = 1 ; /* Skip argv[0] */
80 sg_argv = argv ;
81 sg_argc = argc ;
82 sg_flags = opt_flags ;
83 sg_nargs = argc ;
84 }
85
86 /*
87 * While there are arguments left to process...
88 */
89 while (sqsh_optind < sg_nargs) {
90
91
92 /*
93 * If the option doesn't start with a '-', then it isn't
94 * an option, so we move it to the end of our array.
95 */
96 if (!is_flag(argv[sqsh_optind])) {
97
98 /*
99 * This moves the entry located at sqsh_optind to the end
100 * of our array of options. We don't need to increment
101 * sqsh_optind, because we have effectively already done
102 * that.
103 */
104 sqsh_move( argc, argv, sqsh_optind );
105 --sg_nargs;
106 continue;
107
108 }
109
110 /*
111 * If we have reached this point, then we have an option to
112 * process. Note that thanks to the new '-' modifier in the
113 * opt_flags list, we may still not really be interested in this
114 * flag.
115 */
116 flag = argv[sqsh_optind][1];
117
118 if ((opt = strchr( opt_flags, flag )) == NULL) {
119 /*
120 * If we don't have an entry for this option, then it is
121 * considered illegal and must be severely mistreated.
122 */
123 sqsh_set_error( SQSH_E_BADPARAM, "illegal option -%c", flag ) ;
124 ++sqsh_optind;
125 return '?';
126 }
127
128 /*
129 * Now, figure out which options are available for this particular
130 * flag. Currently there are three supported: A ':' indicates that
131 * the flag requires and argument, a ';' indicates that it may have
132 * an optional argument, and a '-' indicates that we are to ignore
133 * the current argument. Note that the '-' may be used in combination
134 * with the other two.
135 */
136 ++opt;
137 if (*opt == '-') {
138 ignore = True;
139 ++opt;
140 } else {
141 ignore = False;
142 }
143
144 /*
145 * Check to see if we may need an agument for this particular
146 * flag.
147 */
148 if (*opt == ';' || *opt == ':') {
149 if (*(opt + 1) == '-')
150 ignore = True;
151
152 /*
153 * If there is more text left on this option (for example,
154 * if we have "-foo", then "oo" is the argument to -f. This
155 * means that we need to skip the current argument to get to
156 * the next potential flag.
157 */
158 if (argv[sqsh_optind][2] != '\0') {
159
160 sqsh_optarg = &argv[sqsh_optind][2];
161 nskip = 1;
162
163 } else if (sqsh_optind < (argc - 1) && !is_flag(argv[sqsh_optind+1])) {
164 /*
165 * Otherwise, if there is a next argument on the command line
166 * and it does not begin with a '-', then it is the argument
167 * to our option. If this is the case, then we must skip two
168 * ahead to get to the next flag.
169 */
170 sqsh_optarg = argv[sqsh_optind+1];
171 nskip = 2;
172 } else {
173 /*
174 * Oops, it looks like this flag does not have an argument
175 * at all. But, that may actually be OK.
176 */
177 sqsh_optarg = NULL;
178 nskip = 1;
179 }
180
181 /*
182 * If the option modifier indicates that we have a required
183 * argument and there isn't one there, then generate an error
184 * message.
185 */
186 if (*opt == ':' && sqsh_optarg == NULL) {
187 sqsh_set_error( SQSH_E_BADPARAM,
188 "option requires an argument -%c", flag ) ;
189 ++sqsh_optind;
190 return '?';
191 }
192
193 } else {
194
195 /*
196 * We have an option that does not take an argument, so we
197 * want to make sure that one wasn't supplied.
198 */
199 if (argv[sqsh_optind][2] != '\0') {
200 sqsh_set_error( SQSH_E_BADPARAM,
201 "option does not take an argument -%c", flag );
202 ++sqsh_optind;
203 return '?';
204 }
205
206 sqsh_optarg = NULL;
207 nskip = 1;
208
209 }
210
211
212 /*
213 * At this point, sqsh_optind points to the next possible
214 * argument, flag contains the current command line flag,
215 * and sqsh_optarg contains the argument to the flag. Now,
216 * if we aren't supposed to ignore it, return the flag.
217 */
218 if (ignore == False) {
219 sqsh_optind += nskip;
220 return flag;
221 }
222
223 /*
224 * If we are to ignore this flag, then we need to do two things:
225 * first, we need to move both the flag and, potentially, its
226 * argument onto the end of our array of arguments, then treat
227 * it as if those two arguments don't exist (by reducing our count
228 * of the total number of arguments on the array).
229 */
230 sg_nargs -= nskip;
231 for (i = 0; i < nskip; i++)
232 sqsh_move( argc, argv, sqsh_optind );
233
234 }
235
236 sqsh_optarg = NULL;
237 sqsh_set_error( SQSH_E_NONE, NULL );
238 return EOF;
239 }
240
241 /*
242 * sqsh_move():
243 *
244 * Helper function for sqsh_getopt() to move the item located
245 * at position idx onto the end of the array argv containing
246 * argc entries.
247 */
sqsh_move(argc,argv,idx)248 static int sqsh_move( argc, argv, idx )
249 int argc;
250 char *argv[];
251 int idx;
252 {
253 char *cptr;
254 int i;
255
256 /*-- Save pointer to argument --*/
257 cptr = argv[idx] ;
258
259 /*
260 * First, we need to shift the rest of the arguments down,
261 * removing this argument.
262 */
263 for( i = idx; i < argc - 1; i++ )
264 argv[i] = argv[i+1] ;
265
266 /*
267 * Now, stick this argument on the end of the argv array.
268 * Since we know that one more argument on the list is not
269 * a flag we can decrement sg_argc as well.
270 */
271 argv[argc - 1] = cptr ;
272 return 1;
273 }
274
275 /*
276 * sqsh_getopt_combined():
277 *
278 * This function combines sqsh_getopt_env() and sqsh_getopt(), by
279 * first traversing the contents of the environment variable var_name
280 * followed by the command line options argc and argv, returning
281 * results as it goes.
282 */
sqsh_getopt_combined(var_name,argc,argv,opt_flags)283 int sqsh_getopt_combined( var_name, argc, argv, opt_flags )
284 char *var_name ;
285 int argc ;
286 char **argv ;
287 char *opt_flags ;
288 {
289 static int var_exhausted = False ;
290 static char *old_var_name = NULL ;
291 int ch ;
292
293 if( var_name != NULL ) {
294 /*
295 * If the name of the variable changed then we are probably dealing
296 * with an entirely new set of arguments, so we need to reset
297 * everything.
298 */
299 if( var_name != old_var_name ) {
300 old_var_name = var_name ;
301 sqsh_getopt_reset() ;
302 if( (ch = sqsh_getopt_env( var_name, opt_flags )) == EOF )
303 var_exhausted = True ;
304 else
305 return ch ;
306 }
307
308 if( var_exhausted == False ) {
309 if( (ch = sqsh_getopt_env( NULL, opt_flags )) == EOF )
310 var_exhausted = True ;
311 else
312 return ch ;
313 }
314 }
315
316 return sqsh_getopt( argc, argv, opt_flags ) ;
317 }
318
319 /*
320 * sqsh_getopt_env():
321 *
322 * Given the name of an environment variable containing command line
323 * options, sqsh_getopt_env() returns each option supplied in opt_flags
324 * (of the same format as sqsh_getopt()), setting sqsh_optarg as needed.
325 * A subsequent call to sqsh_getopt_env() with a NULL var_name causes
326 * the next argument to be returned.
327 */
sqsh_getopt_env(var_name,opt_flags)328 int sqsh_getopt_env( var_name, opt_flags )
329 char *var_name ;
330 char *opt_flags ;
331 {
332 static char *var_value = NULL ;
333 static varbuf_t *arg_buf = NULL ;
334 char *opt_ptr ;
335 int flag ;
336 int ret_value ;
337
338 /*
339 * The first time through this function the user must pass a
340 * variable name in to be parsed. So, we check to see if the
341 * variable exists and is non-empty.
342 */
343 if( var_name != NULL ) {
344 env_get( g_env, var_name, &var_value ) ;
345 if( var_value == NULL || *var_value == '\0' )
346 goto leave_eof ;
347 }
348
349 /*
350 * We need to create a buffer in which to store the argument to
351 * a given parameter. This buffer should only exist until we
352 * reach EOF.
353 */
354 if( arg_buf == NULL ) {
355 if( (arg_buf = varbuf_create( 64 )) == NULL )
356 goto leave_err ;
357 }
358 varbuf_clear( arg_buf ) ;
359
360 /*-- Skip leading white-space --*/
361 for( ; *var_value != '\0' && isspace((int)*var_value); ++var_value ) ;
362
363 /*-- Are we at the end? --*/
364 if( *var_value == '\0' )
365 goto leave_eof ;
366
367 /*-- Arguments must begin with a '-' --*/
368 if( *var_value != '-' ) {
369 sqsh_set_error( SQSH_E_BADPARAM, "Options must begin with '-' (found: %s)",
370 var_value ) ;
371 goto leave_err ;
372 }
373
374 /*-- Flag is character following the '-' --*/
375 flag = *(var_value + 1) ;
376
377 /*-- Skip to character following the flag --*/
378 var_value += 2 ;
379
380 /*-- Check for invalid argument --*/
381 if( flag == '\0' || (opt_ptr = strchr( opt_flags, flag )) == NULL ) {
382 sqsh_set_error( SQSH_E_BADPARAM, "illegal option -%c", flag ) ;
383 goto leave_err ;
384 }
385
386 /*-- Skip white space --*/
387 for( ; *var_value != '\0' && isspace((int)*var_value); ++var_value ) ;
388
389 /*
390 * If this is an option that allows an argument, then we need
391 * to try to pull out the argument part.
392 */
393 if( *(opt_ptr+1) == ':' || *(opt_ptr+1) == ';' ) {
394 /*
395 * Check to see if the next thing on the line is either EOF
396 * or another argument.
397 */
398 if( *var_value == '\0' || is_flag(var_value) ) {
399 /*
400 * If this option requires an argument and there isn't one
401 * available, then spew an error message.
402 */
403 if( *(opt_ptr+1) == ':' ) {
404 sqsh_set_error( SQSH_E_BADPARAM, "option requires an argument -%c",
405 flag ) ;
406 goto leave_err ;
407 }
408
409 /*
410 * This was an optional argument, and it didn't exist, so
411 * let the caller know.
412 */
413 sqsh_optarg = NULL ;
414 return flag ;
415 }
416
417 /*
418 * There was an argument to the option, so copy from the current
419 * position to either the next white space or EOF into our arg_buf.
420 */
421 while( *var_value != '\0' && !(isspace((int)*var_value)) ) {
422 if( varbuf_charcat( arg_buf, *var_value ) == -1 )
423 goto leave_err ;
424 ++var_value ;
425 }
426
427 sqsh_optarg = varbuf_getstr( arg_buf ) ;
428 return flag ;
429 }
430
431 /*
432 * The option doesn't take an argument, so simply return.
433 */
434 sqsh_optarg = NULL ;
435 return flag ;
436
437 leave_err:
438 ret_value = '?' ;
439 goto clean_up ;
440
441 /*
442 * Clean up following an EOF. This consists of destroying the
443 * argument buffer, setting the var_value to NULL, and returning.
444 */
445 leave_eof:
446 ret_value = EOF ;
447
448 clean_up:
449 if( arg_buf != NULL ) {
450 varbuf_destroy( arg_buf ) ;
451 arg_buf = NULL ;
452 }
453 var_value = NULL ;
454 return ret_value ;
455 }
456
457 /*
458 * sqsh_getopt_reset():
459 *
460 * Forces sqsh_getopt() to start from the beginning of the argument
461 * list.
462 */
sqsh_getopt_reset()463 int sqsh_getopt_reset()
464 {
465 sg_argv = NULL ;
466 return True ;
467 }
468