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