1 /*******************************************************************************
2  * This file is part of the argtable3 library.
3  *
4  * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann
5  * <sheitmann@users.sourceforge.net>
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions are met:
10  *     * Redistributions of source code must retain the above copyright
11  *       notice, this list of conditions and the following disclaimer.
12  *     * Redistributions in binary form must reproduce the above copyright
13  *       notice, this list of conditions and the following disclaimer in the
14  *       documentation and/or other materials provided with the distribution.
15  *     * Neither the name of STEWART HEITMANN nor the  names of its contributors
16  *       may be used to endorse or promote products derived from this software
17  *       without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT,
23  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  ******************************************************************************/
30 
31 #include "argtable3.h"
32 
33 /*******************************************************************************
34  * This file is part of the argtable3 library.
35  *
36  * Copyright (C) 2013 Tom G. Huang
37  * <tomghuang@gmail.com>
38  * All rights reserved.
39  *
40  * Redistribution and use in source and binary forms, with or without
41  * modification, are permitted provided that the following conditions are met:
42  *     * Redistributions of source code must retain the above copyright
43  *       notice, this list of conditions and the following disclaimer.
44  *     * Redistributions in binary form must reproduce the above copyright
45  *       notice, this list of conditions and the following disclaimer in the
46  *       documentation and/or other materials provided with the distribution.
47  *     * Neither the name of STEWART HEITMANN nor the  names of its contributors
48  *       may be used to endorse or promote products derived from this software
49  *       without specific prior written permission.
50  *
51  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
52  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
53  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
54  * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT,
55  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
56  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
57  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
58  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
59  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
60  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
61  ******************************************************************************/
62 
63 #ifndef ARG_UTILS_H
64 #define ARG_UTILS_H
65 
66 #define ARG_ENABLE_TRACE 0
67 #define ARG_ENABLE_LOG 1
68 
69 #ifdef __cplusplus
70 extern "C" {
71 #endif
72 
73 enum
74 {
75     EMINCOUNT = 1,
76     EMAXCOUNT,
77     EBADINT,
78     EOVERFLOW,
79     EBADDOUBLE,
80     EBADDATE,
81     EREGNOMATCH
82 };
83 
84 
85 #if defined(_MSC_VER)
86 #define ARG_TRACE(x) \
87     __pragma(warning(push)) \
88     __pragma(warning(disable:4127)) \
89     do { if (ARG_ENABLE_TRACE) dbg_printf x; } while (0) \
90     __pragma(warning(pop))
91 
92 #define ARG_LOG(x) \
93     __pragma(warning(push)) \
94     __pragma(warning(disable:4127)) \
95     do { if (ARG_ENABLE_LOG) dbg_printf x; } while (0) \
96     __pragma(warning(pop))
97 #else
98 #define ARG_TRACE(x) \
99     do { if (ARG_ENABLE_TRACE) dbg_printf x; } while (0)
100 
101 #define ARG_LOG(x) \
102     do { if (ARG_ENABLE_LOG) dbg_printf x; } while (0)
103 #endif
104 
105 extern void dbg_printf(const char *fmt, ...);
106 
107 #ifdef __cplusplus
108 }
109 #endif
110 
111 #endif
112 
113 /*******************************************************************************
114  * This file is part of the argtable3 library.
115  *
116  * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann
117  * <sheitmann@users.sourceforge.net>
118  * All rights reserved.
119  *
120  * Redistribution and use in source and binary forms, with or without
121  * modification, are permitted provided that the following conditions are met:
122  *     * Redistributions of source code must retain the above copyright
123  *       notice, this list of conditions and the following disclaimer.
124  *     * Redistributions in binary form must reproduce the above copyright
125  *       notice, this list of conditions and the following disclaimer in the
126  *       documentation and/or other materials provided with the distribution.
127  *     * Neither the name of STEWART HEITMANN nor the  names of its contributors
128  *       may be used to endorse or promote products derived from this software
129  *       without specific prior written permission.
130  *
131  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
132  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
133  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
134  * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT,
135  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
136  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
137  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
138  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
139  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
140  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
141  ******************************************************************************/
142 
143 #include <stdarg.h>
144 #include <stdio.h>
145 
146 
dbg_printf(const char * fmt,...)147 void dbg_printf(const char *fmt, ...)
148 {
149     va_list args;
150     va_start(args, fmt);
151     vfprintf(stderr, fmt, args);
152     va_end(args);
153 }
154 
155 /*	$Id: getopt.h,v 1.1 2009/10/16 19:50:28 rodney Exp rodney $ */
156 /*	$OpenBSD: getopt.h,v 1.1 2002/12/03 20:24:29 millert Exp $	*/
157 /*	$NetBSD: getopt.h,v 1.4 2000/07/07 10:43:54 ad Exp $	*/
158 
159 /*-
160  * Copyright (c) 2000 The NetBSD Foundation, Inc.
161  * All rights reserved.
162  *
163  * This code is derived from software contributed to The NetBSD Foundation
164  * by Dieter Baron and Thomas Klausner.
165  *
166  * Redistribution and use in source and binary forms, with or without
167  * modification, are permitted provided that the following conditions
168  * are met:
169  * 1. Redistributions of source code must retain the above copyright
170  *    notice, this list of conditions and the following disclaimer.
171  * 2. Redistributions in binary form must reproduce the above copyright
172  *    notice, this list of conditions and the following disclaimer in the
173  *    documentation and/or other materials provided with the distribution.
174  * 3. All advertising materials mentioning features or use of this software
175  *    must display the following acknowledgement:
176  *        This product includes software developed by the NetBSD
177  *        Foundation, Inc. and its contributors.
178  * 4. Neither the name of The NetBSD Foundation nor the names of its
179  *    contributors may be used to endorse or promote products derived
180  *    from this software without specific prior written permission.
181  *
182  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
183  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
184  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
185  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
186  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
187  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
188  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
189  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
190  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
191  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
192  * POSSIBILITY OF SUCH DAMAGE.
193  */
194 
195 #ifndef _GETOPT_H_
196 #define _GETOPT_H_
197 
198 #if 0
199 #include <sys/cdefs.h>
200 #endif
201 
202 /*
203  * GNU-like getopt_long() and 4.4BSD getsubopt()/optreset extensions
204  */
205 #define no_argument        0
206 #define required_argument  1
207 #define optional_argument  2
208 
209 struct option {
210 	/* name of long option */
211 	const char *name;
212 	/*
213 	 * one of no_argument, required_argument, and optional_argument:
214 	 * whether option takes an argument
215 	 */
216 	int has_arg;
217 	/* if not NULL, set *flag to val when option found */
218 	int *flag;
219 	/* if flag not NULL, value to set *flag to; else return value */
220 	int val;
221 };
222 
223 #ifdef __cplusplus
224 extern "C" {
225 #endif
226 
227 int	 getopt_long(int, char * const *, const char *,
228 	    const struct option *, int *);
229 int	 getopt_long_only(int, char * const *, const char *,
230 	    const struct option *, int *);
231 #ifndef _GETOPT_DEFINED
232 #define _GETOPT_DEFINED
233 int	 getopt(int, char * const *, const char *);
234 int	 getsubopt(char **, char * const *, char **);
235 
236 extern   char *optarg;                  /* getopt(3) external variables */
237 extern   int opterr;
238 extern   int optind;
239 extern   int optopt;
240 extern   int optreset;
241 extern   char *suboptarg;               /* getsubopt(3) external variable */
242 #endif /* _GETOPT_DEFINED */
243 
244 #ifdef __cplusplus
245 }
246 #endif
247 #endif /* !_GETOPT_H_ */
248 /*	$Id: getopt_long.c,v 1.1 2009/10/16 19:50:28 rodney Exp rodney $	*/
249 /*	$OpenBSD: getopt_long.c,v 1.23 2007/10/31 12:34:57 chl Exp $	*/
250 /*	$NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv Exp $	*/
251 
252 /*
253  * Copyright (c) 2002 Todd C. Miller <Todd.Miller@courtesan.com>
254  *
255  * Permission to use, copy, modify, and distribute this software for any
256  * purpose with or without fee is hereby granted, provided that the above
257  * copyright notice and this permission notice appear in all copies.
258  *
259  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
260  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
261  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
262  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
263  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
264  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
265  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
266  *
267  * Sponsored in part by the Defense Advanced Research Projects
268  * Agency (DARPA) and Air Force Research Laboratory, Air Force
269  * Materiel Command, USAF, under agreement number F39502-99-1-0512.
270  */
271 
272 #ifndef lint
273 //static const char rcsid[]="$Id: getopt_long.c,v 1.1 2009/10/16 19:50:28 rodney Exp rodney $";
274 #endif /* lint */
275 /*-
276  * Copyright (c) 2000 The NetBSD Foundation, Inc.
277  * All rights reserved.
278  *
279  * This code is derived from software contributed to The NetBSD Foundation
280  * by Dieter Baron and Thomas Klausner.
281  *
282  * Redistribution and use in source and binary forms, with or without
283  * modification, are permitted provided that the following conditions
284  * are met:
285  * 1. Redistributions of source code must retain the above copyright
286  *    notice, this list of conditions and the following disclaimer.
287  * 2. Redistributions in binary form must reproduce the above copyright
288  *    notice, this list of conditions and the following disclaimer in the
289  *    documentation and/or other materials provided with the distribution.
290  *
291  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
292  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
293  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
294  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
295  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
296  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
297  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
298  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
299  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
300  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
301  * POSSIBILITY OF SUCH DAMAGE.
302  */
303 
304 #include <errno.h>
305 #include <stdlib.h>
306 #include <string.h>
307 
308 // Define this to replace system getopt
309 //
310 //#define	REPLACE_GETOPT		/* use this getopt as the system getopt(3) */
311 
312 #ifdef REPLACE_GETOPT
313 int	opterr = 1;		/* if error message should be printed */
314 int	optind = 1;		/* index into parent argv vector */
315 int	optopt = '?';		/* character checked for validity */
316 int	optreset;		/* reset getopt */
317 char    *optarg;		/* argument associated with option */
318 
319 
320 #define PRINT_ERROR	((opterr) && (*options != ':'))
321 
322 #define FLAG_PERMUTE	0x01	/* permute non-options to the end of argv */
323 #define FLAG_ALLARGS	0x02	/* treat non-options as args to option "-1" */
324 #define FLAG_LONGONLY	0x04	/* operate as getopt_long_only */
325 
326 /* return values */
327 #define	BADCH		(int)'?'
328 #define	BADARG		((*options == ':') ? (int)':' : (int)'?')
329 #define	INORDER 	(int)1
330 
331 #define	EMSG		""
332 
333 static int getopt_internal(int, char * const *, const char *,
334 			   const struct option *, int *, int);
335 static int parse_long_options(char * const *, const char *,
336 			      const struct option *, int *, int);
337 static int gcd(int, int);
338 static void permute_args(int, int, int, char * const *);
339 
340 static char *place = EMSG; /* option letter processing */
341 
342 /* XXX: set optreset to 1 rather than these two */
343 static int nonopt_start = -1; /* first non option argument (for permute) */
344 static int nonopt_end = -1;   /* first option after non options (for permute) */
345 
346 /* Error messages */
347 static const char recargchar[] = "option requires an argument -- %c";
348 static const char recargstring[] = "option requires an argument -- %s";
349 static const char ambig[] = "ambiguous option -- %.*s";
350 static const char noarg[] = "option doesn't take an argument -- %.*s";
351 static const char illoptchar[] = "unknown option -- %c";
352 static const char illoptstring[] = "unknown option -- %s";
353 
354 
355 #if defined(_WIN32) || defined(ESP_PLATFORM)
356 
357 /* Windows needs warnx().  We change the definition though:
358  *  1. (another) global is defined, opterrmsg, which holds the error message
359  *  2. errors are always printed out on stderr w/o the program name
360  * Note that opterrmsg always gets set no matter what opterr is set to.  The
361  * error message will not be printed if opterr is 0 as usual.
362  */
363 
364 #include <stdio.h>
365 #include <stdarg.h>
366 
367 extern char opterrmsg[128];
368 char opterrmsg[128]; /* buffer for the last error message */
369 
warnx(const char * fmt,...)370 static void warnx(const char *fmt, ...)
371 {
372 	va_list ap;
373 	va_start(ap, fmt);
374     /*
375     Make sure opterrmsg is always zero-terminated despite the _vsnprintf()
376     implementation specifics and manually suppress the warning.
377     */
378     memset(opterrmsg, 0, sizeof opterrmsg);
379 	if (fmt != NULL)
380 		vsnprintf(opterrmsg, sizeof(opterrmsg) - 1, fmt, ap);
381 	va_end(ap);
382 #if defined(_WIN32)
383 #pragma warning(suppress: 6053)
384 #endif
385 	fprintf(stderr, "%s\n", opterrmsg);
386 }
387 
388 #else
389 #include <err.h>
390 #endif /*_WIN32*/
391 
392 /*
393  * Compute the greatest common divisor of a and b.
394  */
395 static int
gcd(int a,int b)396 gcd(int a, int b)
397 {
398 	int c;
399 
400 	c = a % b;
401 	while (c != 0) {
402 		a = b;
403 		b = c;
404 		c = a % b;
405 	}
406 
407 	return (b);
408 }
409 
410 /*
411  * Exchange the block from nonopt_start to nonopt_end with the block
412  * from nonopt_end to opt_end (keeping the same order of arguments
413  * in each block).
414  */
415 static void
permute_args(int panonopt_start,int panonopt_end,int opt_end,char * const * nargv)416 permute_args(int panonopt_start, int panonopt_end, int opt_end,
417 	char * const *nargv)
418 {
419 	int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
420 	char *swap;
421 
422 	/*
423 	 * compute lengths of blocks and number and size of cycles
424 	 */
425 	nnonopts = panonopt_end - panonopt_start;
426 	nopts = opt_end - panonopt_end;
427 	ncycle = gcd(nnonopts, nopts);
428 	cyclelen = (opt_end - panonopt_start) / ncycle;
429 
430 	for (i = 0; i < ncycle; i++) {
431 		cstart = panonopt_end+i;
432 		pos = cstart;
433 		for (j = 0; j < cyclelen; j++) {
434 			if (pos >= panonopt_end)
435 				pos -= nnonopts;
436 			else
437 				pos += nopts;
438 			swap = nargv[pos];
439 			/* LINTED const cast */
440 			((char **) nargv)[pos] = nargv[cstart];
441 			/* LINTED const cast */
442 			((char **)nargv)[cstart] = swap;
443 		}
444 	}
445 }
446 
447 /*
448  * parse_long_options --
449  *	Parse long options in argc/argv argument vector.
450  * Returns -1 if short_too is set and the option does not match long_options.
451  */
452 static int
parse_long_options(char * const * nargv,const char * options,const struct option * long_options,int * idx,int short_too)453 parse_long_options(char * const *nargv, const char *options,
454 	const struct option *long_options, int *idx, int short_too)
455 {
456 	char *current_argv, *has_equal;
457 	size_t current_argv_len;
458 	int i, match;
459 
460 	current_argv = place;
461 	match = -1;
462 
463 	optind++;
464 
465 	if ((has_equal = strchr(current_argv, '=')) != NULL) {
466 		/* argument found (--option=arg) */
467 		current_argv_len = has_equal - current_argv;
468 		has_equal++;
469 	} else
470 		current_argv_len = strlen(current_argv);
471 
472 	for (i = 0; long_options[i].name; i++) {
473 		/* find matching long option */
474 		if (strncmp(current_argv, long_options[i].name,
475 		    current_argv_len))
476 			continue;
477 
478 		if (strlen(long_options[i].name) == current_argv_len) {
479 			/* exact match */
480 			match = i;
481 			break;
482 		}
483 		/*
484 		 * If this is a known short option, don't allow
485 		 * a partial match of a single character.
486 		 */
487 		if (short_too && current_argv_len == 1)
488 			continue;
489 
490 		if (match == -1)	/* partial match */
491 			match = i;
492 		else {
493 			/* ambiguous abbreviation */
494 			if (PRINT_ERROR)
495 				warnx(ambig, (int)current_argv_len,
496 				     current_argv);
497 			optopt = 0;
498 			return (BADCH);
499 		}
500 	}
501 	if (match != -1) {		/* option found */
502 		if (long_options[match].has_arg == no_argument
503 		    && has_equal) {
504 			if (PRINT_ERROR)
505 				warnx(noarg, (int)current_argv_len,
506 				     current_argv);
507 			/*
508 			 * XXX: GNU sets optopt to val regardless of flag
509 			 */
510 			if (long_options[match].flag == NULL)
511 				optopt = long_options[match].val;
512 			else
513 				optopt = 0;
514 			return (BADARG);
515 		}
516 		if (long_options[match].has_arg == required_argument ||
517 		    long_options[match].has_arg == optional_argument) {
518 			if (has_equal)
519 				optarg = has_equal;
520 			else if (long_options[match].has_arg ==
521 			    required_argument) {
522 				/*
523 				 * optional argument doesn't use next nargv
524 				 */
525 				optarg = nargv[optind++];
526 			}
527 		}
528 		if ((long_options[match].has_arg == required_argument)
529 		    && (optarg == NULL)) {
530 			/*
531 			 * Missing argument; leading ':' indicates no error
532 			 * should be generated.
533 			 */
534 			if (PRINT_ERROR)
535 				warnx(recargstring,
536 				    current_argv);
537 			/*
538 			 * XXX: GNU sets optopt to val regardless of flag
539 			 */
540 			if (long_options[match].flag == NULL)
541 				optopt = long_options[match].val;
542 			else
543 				optopt = 0;
544 			--optind;
545 			return (BADARG);
546 		}
547 	} else {			/* unknown option */
548 		if (short_too) {
549 			--optind;
550 			return (-1);
551 		}
552 		if (PRINT_ERROR)
553 			warnx(illoptstring, current_argv);
554 		optopt = 0;
555 		return (BADCH);
556 	}
557 	if (idx)
558 		*idx = match;
559 	if (long_options[match].flag) {
560 		*long_options[match].flag = long_options[match].val;
561 		return (0);
562 	} else
563 		return (long_options[match].val);
564 }
565 
566 /*
567  * getopt_internal --
568  *	Parse argc/argv argument vector.  Called by user level routines.
569  */
570 static int
getopt_internal(int nargc,char * const * nargv,const char * options,const struct option * long_options,int * idx,int flags)571 getopt_internal(int nargc, char * const *nargv, const char *options,
572 	const struct option *long_options, int *idx, int flags)
573 {
574 	char *oli;				/* option letter list index */
575 	int optchar, short_too;
576 	static int posixly_correct = -1;
577 
578 	if (options == NULL)
579 		return (-1);
580 
581 	/*
582 	 * Disable GNU extensions if POSIXLY_CORRECT is set or options
583 	 * string begins with a '+'.
584 	 */
585 	if (posixly_correct == -1)
586 		posixly_correct = (getenv("POSIXLY_CORRECT") != NULL);
587 	if (posixly_correct || *options == '+')
588 		flags &= ~FLAG_PERMUTE;
589 	else if (*options == '-')
590 		flags |= FLAG_ALLARGS;
591 	if (*options == '+' || *options == '-')
592 		options++;
593 
594 	/*
595 	 * XXX Some GNU programs (like cvs) set optind to 0 instead of
596 	 * XXX using optreset.  Work around this braindamage.
597 	 */
598 	if (optind == 0)
599 		optind = optreset = 1;
600 
601 	optarg = NULL;
602 	if (optreset)
603 		nonopt_start = nonopt_end = -1;
604 start:
605 	if (optreset || !*place) {		/* update scanning pointer */
606 		optreset = 0;
607 		if (optind >= nargc) {          /* end of argument vector */
608 			place = EMSG;
609 			if (nonopt_end != -1) {
610 				/* do permutation, if we have to */
611 				permute_args(nonopt_start, nonopt_end,
612 				    optind, nargv);
613 				optind -= nonopt_end - nonopt_start;
614 			}
615 			else if (nonopt_start != -1) {
616 				/*
617 				 * If we skipped non-options, set optind
618 				 * to the first of them.
619 				 */
620 				optind = nonopt_start;
621 			}
622 			nonopt_start = nonopt_end = -1;
623 			return (-1);
624 		}
625 		if (*(place = nargv[optind]) != '-' ||
626 		    (place[1] == '\0' && strchr(options, '-') == NULL)) {
627 			place = EMSG;		/* found non-option */
628 			if (flags & FLAG_ALLARGS) {
629 				/*
630 				 * GNU extension:
631 				 * return non-option as argument to option 1
632 				 */
633 				optarg = nargv[optind++];
634 				return (INORDER);
635 			}
636 			if (!(flags & FLAG_PERMUTE)) {
637 				/*
638 				 * If no permutation wanted, stop parsing
639 				 * at first non-option.
640 				 */
641 				return (-1);
642 			}
643 			/* do permutation */
644 			if (nonopt_start == -1)
645 				nonopt_start = optind;
646 			else if (nonopt_end != -1) {
647 				permute_args(nonopt_start, nonopt_end,
648 				    optind, nargv);
649 				nonopt_start = optind -
650 				    (nonopt_end - nonopt_start);
651 				nonopt_end = -1;
652 			}
653 			optind++;
654 			/* process next argument */
655 			goto start;
656 		}
657 		if (nonopt_start != -1 && nonopt_end == -1)
658 			nonopt_end = optind;
659 
660 		/*
661 		 * If we have "-" do nothing, if "--" we are done.
662 		 */
663 		if (place[1] != '\0' && *++place == '-' && place[1] == '\0') {
664 			optind++;
665 			place = EMSG;
666 			/*
667 			 * We found an option (--), so if we skipped
668 			 * non-options, we have to permute.
669 			 */
670 			if (nonopt_end != -1) {
671 				permute_args(nonopt_start, nonopt_end,
672 				    optind, nargv);
673 				optind -= nonopt_end - nonopt_start;
674 			}
675 			nonopt_start = nonopt_end = -1;
676 			return (-1);
677 		}
678 	}
679 
680 	/*
681 	 * Check long options if:
682 	 *  1) we were passed some
683 	 *  2) the arg is not just "-"
684 	 *  3) either the arg starts with -- we are getopt_long_only()
685 	 */
686 	if (long_options != NULL && place != nargv[optind] &&
687 	    (*place == '-' || (flags & FLAG_LONGONLY))) {
688 		short_too = 0;
689 		if (*place == '-')
690 			place++;		/* --foo long option */
691 		else if (*place != ':' && strchr(options, *place) != NULL)
692 			short_too = 1;		/* could be short option too */
693 
694 		optchar = parse_long_options(nargv, options, long_options,
695 		    idx, short_too);
696 		if (optchar != -1) {
697 			place = EMSG;
698 			return (optchar);
699 		}
700 	}
701 
702 	if ((optchar = (int)*place++) == (int)':' ||
703 	    (optchar == (int)'-' && *place != '\0') ||
704 	    (oli = strchr(options, optchar)) == NULL) {
705 		/*
706 		 * If the user specified "-" and  '-' isn't listed in
707 		 * options, return -1 (non-option) as per POSIX.
708 		 * Otherwise, it is an unknown option character (or ':').
709 		 */
710 		if (optchar == (int)'-' && *place == '\0')
711 			return (-1);
712 		if (!*place)
713 			++optind;
714 		if (PRINT_ERROR)
715 			warnx(illoptchar, optchar);
716 		optopt = optchar;
717 		return (BADCH);
718 	}
719 	if (long_options != NULL && optchar == 'W' && oli[1] == ';') {
720 		/* -W long-option */
721 		if (*place)			/* no space */
722 			/* NOTHING */;
723 		else if (++optind >= nargc) {	/* no arg */
724 			place = EMSG;
725 			if (PRINT_ERROR)
726 				warnx(recargchar, optchar);
727 			optopt = optchar;
728 			return (BADARG);
729 		} else				/* white space */
730 			place = nargv[optind];
731 		optchar = parse_long_options(nargv, options, long_options,
732 		    idx, 0);
733 		place = EMSG;
734 		return (optchar);
735 	}
736 	if (*++oli != ':') {			/* doesn't take argument */
737 		if (!*place)
738 			++optind;
739 	} else {				/* takes (optional) argument */
740 		optarg = NULL;
741 		if (*place)			/* no white space */
742 			optarg = place;
743 		else if (oli[1] != ':') {	/* arg not optional */
744 			if (++optind >= nargc) {	/* no arg */
745 				place = EMSG;
746 				if (PRINT_ERROR)
747 					warnx(recargchar, optchar);
748 				optopt = optchar;
749 				return (BADARG);
750 			} else
751 				optarg = nargv[optind];
752 		}
753 		place = EMSG;
754 		++optind;
755 	}
756 	/* dump back option letter */
757 	return (optchar);
758 }
759 
760 
761 /*
762  * getopt --
763  *	Parse argc/argv argument vector.
764  *
765  * [eventually this will replace the BSD getopt]
766  */
767 int
getopt(int nargc,char * const * nargv,const char * options)768 getopt(int nargc, char * const *nargv, const char *options)
769 {
770 
771 	/*
772 	 * We don't pass FLAG_PERMUTE to getopt_internal() since
773 	 * the BSD getopt(3) (unlike GNU) has never done this.
774 	 *
775 	 * Furthermore, since many privileged programs call getopt()
776 	 * before dropping privileges it makes sense to keep things
777 	 * as simple (and bug-free) as possible.
778 	 */
779 	return (getopt_internal(nargc, nargv, options, NULL, NULL, 0));
780 }
781 
782 
783 /*
784  * getopt_long --
785  *	Parse argc/argv argument vector.
786  */
787 int
getopt_long(int nargc,char * const * nargv,const char * options,const struct option * long_options,int * idx)788 getopt_long(int nargc, char * const *nargv, const char *options,
789     const struct option *long_options, int *idx)
790 {
791 
792 	return (getopt_internal(nargc, nargv, options, long_options, idx,
793 	    FLAG_PERMUTE));
794 }
795 
796 /*
797  * getopt_long_only --
798  *	Parse argc/argv argument vector.
799  */
800 int
getopt_long_only(int nargc,char * const * nargv,const char * options,const struct option * long_options,int * idx)801 getopt_long_only(int nargc, char * const *nargv, const char *options,
802     const struct option *long_options, int *idx)
803 {
804 
805 	return (getopt_internal(nargc, nargv, options, long_options, idx,
806 	    FLAG_PERMUTE|FLAG_LONGONLY));
807 }
808 #endif /* REPLACE_GETOPT */
809 /*******************************************************************************
810  * This file is part of the argtable3 library.
811  *
812  * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann
813  * <sheitmann@users.sourceforge.net>
814  * All rights reserved.
815  *
816  * Redistribution and use in source and binary forms, with or without
817  * modification, are permitted provided that the following conditions are met:
818  *     * Redistributions of source code must retain the above copyright
819  *       notice, this list of conditions and the following disclaimer.
820  *     * Redistributions in binary form must reproduce the above copyright
821  *       notice, this list of conditions and the following disclaimer in the
822  *       documentation and/or other materials provided with the distribution.
823  *     * Neither the name of STEWART HEITMANN nor the  names of its contributors
824  *       may be used to endorse or promote products derived from this software
825  *       without specific prior written permission.
826  *
827  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
828  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
829  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
830  * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT,
831  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
832  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
833  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
834  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
835  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
836  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
837  ******************************************************************************/
838 
839 #include <stdlib.h>
840 #include <string.h>
841 
842 #include "argtable3.h"
843 
844 
845 char * arg_strptime(const char *buf, const char *fmt, struct tm *tm);
846 
847 
arg_date_resetfn(struct arg_date * parent)848 static void arg_date_resetfn(struct arg_date *parent)
849 {
850     ARG_TRACE(("%s:resetfn(%p)\n", __FILE__, parent));
851     parent->count = 0;
852 }
853 
854 
arg_date_scanfn(struct arg_date * parent,const char * argval)855 static int arg_date_scanfn(struct arg_date *parent, const char *argval)
856 {
857     int errorcode = 0;
858 
859     if (parent->count == parent->hdr.maxcount)
860     {
861         errorcode = EMAXCOUNT;
862     }
863     else if (!argval)
864     {
865         /* no argument value was given, leave parent->tmval[] unaltered but still count it */
866         parent->count++;
867     }
868     else
869     {
870         const char *pend;
871         struct tm tm = parent->tmval[parent->count];
872 
873         /* parse the given argument value, store result in parent->tmval[] */
874         pend = arg_strptime(argval, parent->format, &tm);
875         if (pend && pend[0] == '\0')
876             parent->tmval[parent->count++] = tm;
877         else
878             errorcode = EBADDATE;
879     }
880 
881     ARG_TRACE(("%s:scanfn(%p) returns %d\n", __FILE__, parent, errorcode));
882     return errorcode;
883 }
884 
885 
arg_date_checkfn(struct arg_date * parent)886 static int arg_date_checkfn(struct arg_date *parent)
887 {
888     int errorcode = (parent->count < parent->hdr.mincount) ? EMINCOUNT : 0;
889 
890     ARG_TRACE(("%s:checkfn(%p) returns %d\n", __FILE__, parent, errorcode));
891     return errorcode;
892 }
893 
894 
arg_date_errorfn(struct arg_date * parent,FILE * fp,int errorcode,const char * argval,const char * progname)895 static void arg_date_errorfn(
896     struct arg_date *parent,
897     FILE *fp,
898     int errorcode,
899     const char *argval,
900     const char *progname)
901 {
902     const char *shortopts = parent->hdr.shortopts;
903     const char *longopts  = parent->hdr.longopts;
904     const char *datatype  = parent->hdr.datatype;
905 
906     /* make argval NULL safe */
907     argval = argval ? argval : "";
908 
909     fprintf(fp, "%s: ", progname);
910     switch(errorcode)
911     {
912     case EMINCOUNT:
913         fputs("missing option ", fp);
914         arg_print_option(fp, shortopts, longopts, datatype, "\n");
915         break;
916 
917     case EMAXCOUNT:
918         fputs("excess option ", fp);
919         arg_print_option(fp, shortopts, longopts, argval, "\n");
920         break;
921 
922     case EBADDATE:
923     {
924         struct tm tm;
925         char buff[200];
926 
927         fprintf(fp, "illegal timestamp format \"%s\"\n", argval);
928         memset(&tm, 0, sizeof(tm));
929         arg_strptime("1999-12-31 23:59:59", "%F %H:%M:%S", &tm);
930         strftime(buff, sizeof(buff), parent->format, &tm);
931         printf("correct format is \"%s\"\n", buff);
932         break;
933     }
934     }
935 }
936 
937 
arg_date0(const char * shortopts,const char * longopts,const char * format,const char * datatype,const char * glossary)938 struct arg_date * arg_date0(
939     const char * shortopts,
940     const char * longopts,
941     const char * format,
942     const char *datatype,
943     const char *glossary)
944 {
945     return arg_daten(shortopts, longopts, format, datatype, 0, 1, glossary);
946 }
947 
948 
arg_date1(const char * shortopts,const char * longopts,const char * format,const char * datatype,const char * glossary)949 struct arg_date * arg_date1(
950     const char * shortopts,
951     const char * longopts,
952     const char * format,
953     const char *datatype,
954     const char *glossary)
955 {
956     return arg_daten(shortopts, longopts, format, datatype, 1, 1, glossary);
957 }
958 
959 
arg_daten(const char * shortopts,const char * longopts,const char * format,const char * datatype,int mincount,int maxcount,const char * glossary)960 struct arg_date * arg_daten(
961     const char * shortopts,
962     const char * longopts,
963     const char * format,
964     const char *datatype,
965     int mincount,
966     int maxcount,
967     const char *glossary)
968 {
969     size_t nbytes;
970     struct arg_date *result;
971 
972     /* foolproof things by ensuring maxcount is not less than mincount */
973     maxcount = (maxcount < mincount) ? mincount : maxcount;
974 
975     /* default time format is the national date format for the locale */
976     if (!format)
977         format = "%x";
978 
979     nbytes = sizeof(struct arg_date)         /* storage for struct arg_date */
980         + maxcount * sizeof(struct tm);    /* storage for tmval[maxcount] array */
981 
982     /* allocate storage for the arg_date struct + tmval[] array.    */
983     /* we use calloc because we want the tmval[] array zero filled. */
984     result = (struct arg_date *)calloc(1, nbytes);
985     if (result)
986     {
987         /* init the arg_hdr struct */
988         result->hdr.flag      = ARG_HASVALUE;
989         result->hdr.shortopts = shortopts;
990         result->hdr.longopts  = longopts;
991         result->hdr.datatype  = datatype ? datatype : format;
992         result->hdr.glossary  = glossary;
993         result->hdr.mincount  = mincount;
994         result->hdr.maxcount  = maxcount;
995         result->hdr.parent    = result;
996         result->hdr.resetfn   = (arg_resetfn *)arg_date_resetfn;
997         result->hdr.scanfn    = (arg_scanfn *)arg_date_scanfn;
998         result->hdr.checkfn   = (arg_checkfn *)arg_date_checkfn;
999         result->hdr.errorfn   = (arg_errorfn *)arg_date_errorfn;
1000 
1001         /* store the tmval[maxcount] array immediately after the arg_date struct */
1002         result->tmval  = (struct tm *)(result + 1);
1003 
1004         /* init the remaining arg_date member variables */
1005         result->count = 0;
1006         result->format = format;
1007     }
1008 
1009     ARG_TRACE(("arg_daten() returns %p\n", result));
1010     return result;
1011 }
1012 
1013 
1014 /*-
1015  * Copyright (c) 1997, 1998, 2005, 2008 The NetBSD Foundation, Inc.
1016  * All rights reserved.
1017  *
1018  * This code was contributed to The NetBSD Foundation by Klaus Klein.
1019  * Heavily optimised by David Laight
1020  *
1021  * Redistribution and use in source and binary forms, with or without
1022  * modification, are permitted provided that the following conditions
1023  * are met:
1024  * 1. Redistributions of source code must retain the above copyright
1025  *    notice, this list of conditions and the following disclaimer.
1026  * 2. Redistributions in binary form must reproduce the above copyright
1027  *    notice, this list of conditions and the following disclaimer in the
1028  *    documentation and/or other materials provided with the distribution.
1029  *
1030  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
1031  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
1032  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
1033  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
1034  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
1035  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
1036  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
1037  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
1038  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
1039  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
1040  * POSSIBILITY OF SUCH DAMAGE.
1041  */
1042 
1043 #include <ctype.h>
1044 #include <string.h>
1045 #include <time.h>
1046 
1047 /*
1048  * We do not implement alternate representations. However, we always
1049  * check whether a given modifier is allowed for a certain conversion.
1050  */
1051 #define ALT_E                   0x01
1052 #define ALT_O                   0x02
1053 #define LEGAL_ALT(x)            { if (alt_format & ~(x)) return (0); }
1054 #define TM_YEAR_BASE   (1900)
1055 
1056 static int conv_num(const char * *, int *, int, int);
1057 
1058 static const char *day[7] = {
1059     "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday",
1060     "Friday", "Saturday"
1061 };
1062 
1063 static const char *abday[7] = {
1064     "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
1065 };
1066 
1067 static const char *mon[12] = {
1068     "January", "February", "March", "April", "May", "June", "July",
1069     "August", "September", "October", "November", "December"
1070 };
1071 
1072 static const char *abmon[12] = {
1073     "Jan", "Feb", "Mar", "Apr", "May", "Jun",
1074     "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
1075 };
1076 
1077 static const char *am_pm[2] = {
1078     "AM", "PM"
1079 };
1080 
1081 
arg_strcasecmp(const char * s1,const char * s2)1082 static int arg_strcasecmp(const char *s1, const char *s2)
1083 {
1084     const unsigned char *us1 = (const unsigned char *)s1;
1085     const unsigned char *us2 = (const unsigned char *)s2;
1086     while (tolower(*us1) == tolower(*us2++))
1087         if (*us1++ == '\0')
1088             return 0;
1089 
1090     return tolower(*us1) - tolower(*--us2);
1091 }
1092 
1093 
arg_strncasecmp(const char * s1,const char * s2,size_t n)1094 static int arg_strncasecmp(const char *s1, const char *s2, size_t n)
1095 {
1096     if (n != 0)
1097     {
1098         const unsigned char *us1 = (const unsigned char *)s1;
1099         const unsigned char *us2 = (const unsigned char *)s2;
1100         do
1101         {
1102             if (tolower(*us1) != tolower(*us2++))
1103                 return tolower(*us1) - tolower(*--us2);
1104 
1105             if (*us1++ == '\0')
1106                 break;
1107         } while (--n != 0);
1108     }
1109 
1110     return 0;
1111 }
1112 
1113 
arg_strptime(const char * buf,const char * fmt,struct tm * tm)1114 char * arg_strptime(const char *buf, const char *fmt, struct tm *tm)
1115 {
1116     char c;
1117     const char *bp;
1118     size_t len = 0;
1119     int alt_format, i, split_year = 0;
1120 
1121     bp = buf;
1122 
1123     while ((c = *fmt) != '\0') {
1124         /* Clear `alternate' modifier prior to new conversion. */
1125         alt_format = 0;
1126 
1127         /* Eat up white-space. */
1128         if (isspace((int) c)) {
1129             while (isspace((int) *bp))
1130                 bp++;
1131 
1132             fmt++;
1133             continue;
1134         }
1135 
1136         if ((c = *fmt++) != '%')
1137             goto literal;
1138 
1139 
1140 again:
1141         switch (c = *fmt++)
1142         {
1143         case '%': /* "%%" is converted to "%". */
1144 literal:
1145             if (c != *bp++)
1146                 return (0);
1147             break;
1148 
1149         /*
1150          * "Alternative" modifiers. Just set the appropriate flag
1151          * and start over again.
1152          */
1153         case 'E': /* "%E?" alternative conversion modifier. */
1154             LEGAL_ALT(0);
1155             alt_format |= ALT_E;
1156             goto again;
1157 
1158         case 'O': /* "%O?" alternative conversion modifier. */
1159             LEGAL_ALT(0);
1160             alt_format |= ALT_O;
1161             goto again;
1162 
1163         /*
1164          * "Complex" conversion rules, implemented through recursion.
1165          */
1166         case 'c': /* Date and time, using the locale's format. */
1167             LEGAL_ALT(ALT_E);
1168             bp = arg_strptime(bp, "%x %X", tm);
1169             if (!bp)
1170                 return (0);
1171             break;
1172 
1173         case 'D': /* The date as "%m/%d/%y". */
1174             LEGAL_ALT(0);
1175             bp = arg_strptime(bp, "%m/%d/%y", tm);
1176             if (!bp)
1177                 return (0);
1178             break;
1179 
1180         case 'R': /* The time as "%H:%M". */
1181             LEGAL_ALT(0);
1182             bp = arg_strptime(bp, "%H:%M", tm);
1183             if (!bp)
1184                 return (0);
1185             break;
1186 
1187         case 'r': /* The time in 12-hour clock representation. */
1188             LEGAL_ALT(0);
1189             bp = arg_strptime(bp, "%I:%M:%S %p", tm);
1190             if (!bp)
1191                 return (0);
1192             break;
1193 
1194         case 'T': /* The time as "%H:%M:%S". */
1195             LEGAL_ALT(0);
1196             bp = arg_strptime(bp, "%H:%M:%S", tm);
1197             if (!bp)
1198                 return (0);
1199             break;
1200 
1201         case 'X': /* The time, using the locale's format. */
1202             LEGAL_ALT(ALT_E);
1203             bp = arg_strptime(bp, "%H:%M:%S", tm);
1204             if (!bp)
1205                 return (0);
1206             break;
1207 
1208         case 'x': /* The date, using the locale's format. */
1209             LEGAL_ALT(ALT_E);
1210             bp = arg_strptime(bp, "%m/%d/%y", tm);
1211             if (!bp)
1212                 return (0);
1213             break;
1214 
1215         /*
1216          * "Elementary" conversion rules.
1217          */
1218         case 'A': /* The day of week, using the locale's form. */
1219         case 'a':
1220             LEGAL_ALT(0);
1221             for (i = 0; i < 7; i++) {
1222                 /* Full name. */
1223                 len = strlen(day[i]);
1224                 if (arg_strncasecmp(day[i], bp, len) == 0)
1225                     break;
1226 
1227                 /* Abbreviated name. */
1228                 len = strlen(abday[i]);
1229                 if (arg_strncasecmp(abday[i], bp, len) == 0)
1230                     break;
1231             }
1232 
1233             /* Nothing matched. */
1234             if (i == 7)
1235                 return (0);
1236 
1237             tm->tm_wday = i;
1238             bp += len;
1239             break;
1240 
1241         case 'B': /* The month, using the locale's form. */
1242         case 'b':
1243         case 'h':
1244             LEGAL_ALT(0);
1245             for (i = 0; i < 12; i++) {
1246                 /* Full name. */
1247                 len = strlen(mon[i]);
1248                 if (arg_strncasecmp(mon[i], bp, len) == 0)
1249                     break;
1250 
1251                 /* Abbreviated name. */
1252                 len = strlen(abmon[i]);
1253                 if (arg_strncasecmp(abmon[i], bp, len) == 0)
1254                     break;
1255             }
1256 
1257             /* Nothing matched. */
1258             if (i == 12)
1259                 return (0);
1260 
1261             tm->tm_mon = i;
1262             bp += len;
1263             break;
1264 
1265         case 'C': /* The century number. */
1266             LEGAL_ALT(ALT_E);
1267             if (!(conv_num(&bp, &i, 0, 99)))
1268                 return (0);
1269 
1270             if (split_year) {
1271                 tm->tm_year = (tm->tm_year % 100) + (i * 100);
1272             } else {
1273                 tm->tm_year = i * 100;
1274                 split_year = 1;
1275             }
1276             break;
1277 
1278         case 'd': /* The day of month. */
1279         case 'e':
1280             LEGAL_ALT(ALT_O);
1281             if (!(conv_num(&bp, &tm->tm_mday, 1, 31)))
1282                 return (0);
1283             break;
1284 
1285         case 'k': /* The hour (24-hour clock representation). */
1286             LEGAL_ALT(0);
1287         /* FALLTHROUGH */
1288         case 'H':
1289             LEGAL_ALT(ALT_O);
1290             if (!(conv_num(&bp, &tm->tm_hour, 0, 23)))
1291                 return (0);
1292             break;
1293 
1294         case 'l': /* The hour (12-hour clock representation). */
1295             LEGAL_ALT(0);
1296         /* FALLTHROUGH */
1297         case 'I':
1298             LEGAL_ALT(ALT_O);
1299             if (!(conv_num(&bp, &tm->tm_hour, 1, 12)))
1300                 return (0);
1301             if (tm->tm_hour == 12)
1302                 tm->tm_hour = 0;
1303             break;
1304 
1305         case 'j': /* The day of year. */
1306             LEGAL_ALT(0);
1307             if (!(conv_num(&bp, &i, 1, 366)))
1308                 return (0);
1309             tm->tm_yday = i - 1;
1310             break;
1311 
1312         case 'M': /* The minute. */
1313             LEGAL_ALT(ALT_O);
1314             if (!(conv_num(&bp, &tm->tm_min, 0, 59)))
1315                 return (0);
1316             break;
1317 
1318         case 'm': /* The month. */
1319             LEGAL_ALT(ALT_O);
1320             if (!(conv_num(&bp, &i, 1, 12)))
1321                 return (0);
1322             tm->tm_mon = i - 1;
1323             break;
1324 
1325         case 'p': /* The locale's equivalent of AM/PM. */
1326             LEGAL_ALT(0);
1327             /* AM? */
1328             if (arg_strcasecmp(am_pm[0], bp) == 0) {
1329                 if (tm->tm_hour > 11)
1330                     return (0);
1331 
1332                 bp += strlen(am_pm[0]);
1333                 break;
1334             }
1335             /* PM? */
1336             else if (arg_strcasecmp(am_pm[1], bp) == 0) {
1337                 if (tm->tm_hour > 11)
1338                     return (0);
1339 
1340                 tm->tm_hour += 12;
1341                 bp += strlen(am_pm[1]);
1342                 break;
1343             }
1344 
1345             /* Nothing matched. */
1346             return (0);
1347 
1348         case 'S': /* The seconds. */
1349             LEGAL_ALT(ALT_O);
1350             if (!(conv_num(&bp, &tm->tm_sec, 0, 61)))
1351                 return (0);
1352             break;
1353 
1354         case 'U': /* The week of year, beginning on sunday. */
1355         case 'W': /* The week of year, beginning on monday. */
1356             LEGAL_ALT(ALT_O);
1357             /*
1358              * XXX This is bogus, as we can not assume any valid
1359              * information present in the tm structure at this
1360              * point to calculate a real value, so just check the
1361              * range for now.
1362              */
1363             if (!(conv_num(&bp, &i, 0, 53)))
1364                 return (0);
1365             break;
1366 
1367         case 'w': /* The day of week, beginning on sunday. */
1368             LEGAL_ALT(ALT_O);
1369             if (!(conv_num(&bp, &tm->tm_wday, 0, 6)))
1370                 return (0);
1371             break;
1372 
1373         case 'Y': /* The year. */
1374             LEGAL_ALT(ALT_E);
1375             if (!(conv_num(&bp, &i, 0, 9999)))
1376                 return (0);
1377 
1378             tm->tm_year = i - TM_YEAR_BASE;
1379             break;
1380 
1381         case 'y': /* The year within 100 years of the epoch. */
1382             LEGAL_ALT(ALT_E | ALT_O);
1383             if (!(conv_num(&bp, &i, 0, 99)))
1384                 return (0);
1385 
1386             if (split_year) {
1387                 tm->tm_year = ((tm->tm_year / 100) * 100) + i;
1388                 break;
1389             }
1390             split_year = 1;
1391             if (i <= 68)
1392                 tm->tm_year = i + 2000 - TM_YEAR_BASE;
1393             else
1394                 tm->tm_year = i + 1900 - TM_YEAR_BASE;
1395             break;
1396 
1397         /*
1398          * Miscellaneous conversions.
1399          */
1400         case 'n': /* Any kind of white-space. */
1401         case 't':
1402             LEGAL_ALT(0);
1403             while (isspace((int) *bp))
1404                 bp++;
1405             break;
1406 
1407 
1408         default: /* Unknown/unsupported conversion. */
1409             return (0);
1410         }
1411 
1412 
1413     }
1414 
1415     /* LINTED functional specification */
1416     return ((char *)bp);
1417 }
1418 
1419 
conv_num(const char ** buf,int * dest,int llim,int ulim)1420 static int conv_num(const char * *buf, int *dest, int llim, int ulim)
1421 {
1422     int result = 0;
1423 
1424     /* The limit also determines the number of valid digits. */
1425     int rulim = ulim;
1426 
1427     if (**buf < '0' || **buf > '9')
1428         return (0);
1429 
1430     do {
1431         result *= 10;
1432         result += *(*buf)++ - '0';
1433         rulim /= 10;
1434     } while ((result * 10 <= ulim) && rulim && **buf >= '0' && **buf <= '9');
1435 
1436     if (result < llim || result > ulim)
1437         return (0);
1438 
1439     *dest = result;
1440     return (1);
1441 }
1442 /*******************************************************************************
1443  * This file is part of the argtable3 library.
1444  *
1445  * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann
1446  * <sheitmann@users.sourceforge.net>
1447  * All rights reserved.
1448  *
1449  * Redistribution and use in source and binary forms, with or without
1450  * modification, are permitted provided that the following conditions are met:
1451  *     * Redistributions of source code must retain the above copyright
1452  *       notice, this list of conditions and the following disclaimer.
1453  *     * Redistributions in binary form must reproduce the above copyright
1454  *       notice, this list of conditions and the following disclaimer in the
1455  *       documentation and/or other materials provided with the distribution.
1456  *     * Neither the name of STEWART HEITMANN nor the  names of its contributors
1457  *       may be used to endorse or promote products derived from this software
1458  *       without specific prior written permission.
1459  *
1460  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
1461  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1462  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1463  * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT,
1464  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
1465  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
1466  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
1467  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
1468  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
1469  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1470  ******************************************************************************/
1471 
1472 #include <stdlib.h>
1473 
1474 #include "argtable3.h"
1475 
1476 
arg_dbl_resetfn(struct arg_dbl * parent)1477 static void arg_dbl_resetfn(struct arg_dbl *parent)
1478 {
1479     ARG_TRACE(("%s:resetfn(%p)\n", __FILE__, parent));
1480     parent->count = 0;
1481 }
1482 
1483 
arg_dbl_scanfn(struct arg_dbl * parent,const char * argval)1484 static int arg_dbl_scanfn(struct arg_dbl *parent, const char *argval)
1485 {
1486     int errorcode = 0;
1487 
1488     if (parent->count == parent->hdr.maxcount)
1489     {
1490         /* maximum number of arguments exceeded */
1491         errorcode = EMAXCOUNT;
1492     }
1493     else if (!argval)
1494     {
1495         /* a valid argument with no argument value was given. */
1496         /* This happens when an optional argument value was invoked. */
1497         /* leave parent argument value unaltered but still count the argument. */
1498         parent->count++;
1499     }
1500     else
1501     {
1502         double val;
1503         char *end;
1504 
1505         /* extract double from argval into val */
1506         val = strtod(argval, &end);
1507 
1508         /* if success then store result in parent->dval[] array otherwise return error*/
1509         if (*end == 0)
1510             parent->dval[parent->count++] = val;
1511         else
1512             errorcode = EBADDOUBLE;
1513     }
1514 
1515     ARG_TRACE(("%s:scanfn(%p) returns %d\n", __FILE__, parent, errorcode));
1516     return errorcode;
1517 }
1518 
1519 
arg_dbl_checkfn(struct arg_dbl * parent)1520 static int arg_dbl_checkfn(struct arg_dbl *parent)
1521 {
1522     int errorcode = (parent->count < parent->hdr.mincount) ? EMINCOUNT : 0;
1523 
1524     ARG_TRACE(("%s:checkfn(%p) returns %d\n", __FILE__, parent, errorcode));
1525     return errorcode;
1526 }
1527 
1528 
arg_dbl_errorfn(struct arg_dbl * parent,FILE * fp,int errorcode,const char * argval,const char * progname)1529 static void arg_dbl_errorfn(
1530     struct arg_dbl *parent,
1531     FILE *fp,
1532     int errorcode,
1533     const char *argval,
1534     const char *progname)
1535 {
1536     const char *shortopts = parent->hdr.shortopts;
1537     const char *longopts  = parent->hdr.longopts;
1538     const char *datatype  = parent->hdr.datatype;
1539 
1540     /* make argval NULL safe */
1541     argval = argval ? argval : "";
1542 
1543     fprintf(fp, "%s: ", progname);
1544     switch(errorcode)
1545     {
1546     case EMINCOUNT:
1547         fputs("missing option ", fp);
1548         arg_print_option(fp, shortopts, longopts, datatype, "\n");
1549         break;
1550 
1551     case EMAXCOUNT:
1552         fputs("excess option ", fp);
1553         arg_print_option(fp, shortopts, longopts, argval, "\n");
1554         break;
1555 
1556     case EBADDOUBLE:
1557         fprintf(fp, "invalid argument \"%s\" to option ", argval);
1558         arg_print_option(fp, shortopts, longopts, datatype, "\n");
1559         break;
1560     }
1561 }
1562 
1563 
arg_dbl0(const char * shortopts,const char * longopts,const char * datatype,const char * glossary)1564 struct arg_dbl * arg_dbl0(
1565     const char * shortopts,
1566     const char * longopts,
1567     const char *datatype,
1568     const char *glossary)
1569 {
1570     return arg_dbln(shortopts, longopts, datatype, 0, 1, glossary);
1571 }
1572 
1573 
arg_dbl1(const char * shortopts,const char * longopts,const char * datatype,const char * glossary)1574 struct arg_dbl * arg_dbl1(
1575     const char * shortopts,
1576     const char * longopts,
1577     const char *datatype,
1578     const char *glossary)
1579 {
1580     return arg_dbln(shortopts, longopts, datatype, 1, 1, glossary);
1581 }
1582 
1583 
arg_dbln(const char * shortopts,const char * longopts,const char * datatype,int mincount,int maxcount,const char * glossary)1584 struct arg_dbl * arg_dbln(
1585     const char * shortopts,
1586     const char * longopts,
1587     const char *datatype,
1588     int mincount,
1589     int maxcount,
1590     const char *glossary)
1591 {
1592     size_t nbytes;
1593     struct arg_dbl *result;
1594 
1595     /* foolproof things by ensuring maxcount is not less than mincount */
1596     maxcount = (maxcount < mincount) ? mincount : maxcount;
1597 
1598     nbytes = sizeof(struct arg_dbl)     /* storage for struct arg_dbl */
1599              + (maxcount + 1) * sizeof(double); /* storage for dval[maxcount] array plus one extra for padding to memory boundary */
1600 
1601     result = (struct arg_dbl *)malloc(nbytes);
1602     if (result)
1603     {
1604         size_t addr;
1605         size_t rem;
1606 
1607         /* init the arg_hdr struct */
1608         result->hdr.flag      = ARG_HASVALUE;
1609         result->hdr.shortopts = shortopts;
1610         result->hdr.longopts  = longopts;
1611         result->hdr.datatype  = datatype ? datatype : "<double>";
1612         result->hdr.glossary  = glossary;
1613         result->hdr.mincount  = mincount;
1614         result->hdr.maxcount  = maxcount;
1615         result->hdr.parent    = result;
1616         result->hdr.resetfn   = (arg_resetfn *)arg_dbl_resetfn;
1617         result->hdr.scanfn    = (arg_scanfn *)arg_dbl_scanfn;
1618         result->hdr.checkfn   = (arg_checkfn *)arg_dbl_checkfn;
1619         result->hdr.errorfn   = (arg_errorfn *)arg_dbl_errorfn;
1620 
1621         /* Store the dval[maxcount] array on the first double boundary that
1622          * immediately follows the arg_dbl struct. We do the memory alignment
1623          * purely for SPARC and Motorola systems. They require floats and
1624          * doubles to be aligned on natural boundaries.
1625          */
1626         addr = (size_t)(result + 1);
1627         rem  = addr % sizeof(double);
1628         result->dval  = (double *)(addr + sizeof(double) - rem);
1629         ARG_TRACE(("addr=%p, dval=%p, sizeof(double)=%d rem=%d\n", addr, result->dval, (int)sizeof(double), (int)rem));
1630 
1631         result->count = 0;
1632     }
1633 
1634     ARG_TRACE(("arg_dbln() returns %p\n", result));
1635     return result;
1636 }
1637 /*******************************************************************************
1638  * This file is part of the argtable3 library.
1639  *
1640  * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann
1641  * <sheitmann@users.sourceforge.net>
1642  * All rights reserved.
1643  *
1644  * Redistribution and use in source and binary forms, with or without
1645  * modification, are permitted provided that the following conditions are met:
1646  *     * Redistributions of source code must retain the above copyright
1647  *       notice, this list of conditions and the following disclaimer.
1648  *     * Redistributions in binary form must reproduce the above copyright
1649  *       notice, this list of conditions and the following disclaimer in the
1650  *       documentation and/or other materials provided with the distribution.
1651  *     * Neither the name of STEWART HEITMANN nor the  names of its contributors
1652  *       may be used to endorse or promote products derived from this software
1653  *       without specific prior written permission.
1654  *
1655  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
1656  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1657  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1658  * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT,
1659  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
1660  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
1661  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
1662  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
1663  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
1664  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1665  ******************************************************************************/
1666 
1667 #include <stdlib.h>
1668 
1669 #include "argtable3.h"
1670 
1671 
arg_end_resetfn(struct arg_end * parent)1672 static void arg_end_resetfn(struct arg_end *parent)
1673 {
1674     ARG_TRACE(("%s:resetfn(%p)\n", __FILE__, parent));
1675     parent->count = 0;
1676 }
1677 
arg_end_errorfn(void * parent,FILE * fp,int error,const char * argval,const char * progname)1678 static void arg_end_errorfn(
1679     void *parent,
1680     FILE *fp,
1681     int error,
1682     const char *argval,
1683     const char *progname)
1684 {
1685     /* suppress unreferenced formal parameter warning */
1686     (void)parent;
1687 
1688     progname = progname ? progname : "";
1689     argval = argval ? argval : "";
1690 
1691     fprintf(fp, "%s: ", progname);
1692     switch(error)
1693     {
1694     case ARG_ELIMIT:
1695         fputs("too many errors to display", fp);
1696         break;
1697     case ARG_EMALLOC:
1698         fputs("insufficent memory", fp);
1699         break;
1700     case ARG_ENOMATCH:
1701         fprintf(fp, "unexpected argument \"%s\"", argval);
1702         break;
1703     case ARG_EMISSARG:
1704         fprintf(fp, "option \"%s\" requires an argument", argval);
1705         break;
1706     case ARG_ELONGOPT:
1707         fprintf(fp, "invalid option \"%s\"", argval);
1708         break;
1709     default:
1710         fprintf(fp, "invalid option \"-%c\"", error);
1711         break;
1712     }
1713 
1714     fputc('\n', fp);
1715 }
1716 
1717 
arg_end(int maxcount)1718 struct arg_end * arg_end(int maxcount)
1719 {
1720     size_t nbytes;
1721     struct arg_end *result;
1722 
1723     nbytes = sizeof(struct arg_end)
1724              + maxcount * sizeof(int)     /* storage for int error[maxcount] array*/
1725              + maxcount * sizeof(void *)  /* storage for void* parent[maxcount] array */
1726              + maxcount * sizeof(char *); /* storage for char* argval[maxcount] array */
1727 
1728     result = (struct arg_end *)malloc(nbytes);
1729     if (result)
1730     {
1731         /* init the arg_hdr struct */
1732         result->hdr.flag      = ARG_TERMINATOR;
1733         result->hdr.shortopts = NULL;
1734         result->hdr.longopts  = NULL;
1735         result->hdr.datatype  = NULL;
1736         result->hdr.glossary  = NULL;
1737         result->hdr.mincount  = 1;
1738         result->hdr.maxcount  = maxcount;
1739         result->hdr.parent    = result;
1740         result->hdr.resetfn   = (arg_resetfn *)arg_end_resetfn;
1741         result->hdr.scanfn    = NULL;
1742         result->hdr.checkfn   = NULL;
1743         result->hdr.errorfn   = (arg_errorfn *)arg_end_errorfn;
1744 
1745         /* store error[maxcount] array immediately after struct arg_end */
1746         result->error = (int *)(result + 1);
1747 
1748         /* store parent[maxcount] array immediately after error[] array */
1749         result->parent = (void * *)(result->error + maxcount );
1750 
1751         /* store argval[maxcount] array immediately after parent[] array */
1752         result->argval = (const char * *)(result->parent + maxcount );
1753     }
1754 
1755     ARG_TRACE(("arg_end(%d) returns %p\n", maxcount, result));
1756     return result;
1757 }
1758 
1759 
arg_print_errors(FILE * fp,struct arg_end * end,const char * progname)1760 void arg_print_errors(FILE * fp, struct arg_end * end, const char * progname)
1761 {
1762     int i;
1763     ARG_TRACE(("arg_errors()\n"));
1764     for (i = 0; i < end->count; i++)
1765     {
1766         struct arg_hdr *errorparent = (struct arg_hdr *)(end->parent[i]);
1767         if (errorparent->errorfn)
1768             errorparent->errorfn(end->parent[i],
1769                                  fp,
1770                                  end->error[i],
1771                                  end->argval[i],
1772                                  progname);
1773     }
1774 }
1775 /*******************************************************************************
1776  * This file is part of the argtable3 library.
1777  *
1778  * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann
1779  * <sheitmann@users.sourceforge.net>
1780  * All rights reserved.
1781  *
1782  * Redistribution and use in source and binary forms, with or without
1783  * modification, are permitted provided that the following conditions are met:
1784  *     * Redistributions of source code must retain the above copyright
1785  *       notice, this list of conditions and the following disclaimer.
1786  *     * Redistributions in binary form must reproduce the above copyright
1787  *       notice, this list of conditions and the following disclaimer in the
1788  *       documentation and/or other materials provided with the distribution.
1789  *     * Neither the name of STEWART HEITMANN nor the  names of its contributors
1790  *       may be used to endorse or promote products derived from this software
1791  *       without specific prior written permission.
1792  *
1793  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
1794  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1795  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1796  * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT,
1797  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
1798  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
1799  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
1800  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
1801  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
1802  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1803  ******************************************************************************/
1804 
1805 #include <string.h>
1806 #include <stdlib.h>
1807 
1808 #include "argtable3.h"
1809 
1810 #ifdef WIN32
1811 # define FILESEPARATOR1 '\\'
1812 # define FILESEPARATOR2 '/'
1813 #else
1814 # define FILESEPARATOR1 '/'
1815 # define FILESEPARATOR2 '/'
1816 #endif
1817 
1818 
arg_file_resetfn(struct arg_file * parent)1819 static void arg_file_resetfn(struct arg_file *parent)
1820 {
1821     ARG_TRACE(("%s:resetfn(%p)\n", __FILE__, parent));
1822     parent->count = 0;
1823 }
1824 
1825 
1826 /* Returns ptr to the base filename within *filename */
arg_basename(const char * filename)1827 static const char * arg_basename(const char *filename)
1828 {
1829     const char *result = NULL, *result1, *result2;
1830 
1831     /* Find the last occurrence of eother file separator character. */
1832     /* Two alternative file separator chars are supported as legal  */
1833     /* file separators but not both together in the same filename.  */
1834     result1 = (filename ? strrchr(filename, FILESEPARATOR1) : NULL);
1835     result2 = (filename ? strrchr(filename, FILESEPARATOR2) : NULL);
1836 
1837     if (result2)
1838         result = result2 + 1;  /* using FILESEPARATOR2 (the alternative file separator) */
1839 
1840     if (result1)
1841         result = result1 + 1;  /* using FILESEPARATOR1 (the preferred file separator) */
1842 
1843     if (!result)
1844         result = filename;  /* neither file separator was found so basename is the whole filename */
1845 
1846     /* special cases of "." and ".." are not considered basenames */
1847     if (result && ( strcmp(".", result) == 0 || strcmp("..", result) == 0 ))
1848         result = filename + strlen(filename);
1849 
1850     return result;
1851 }
1852 
1853 
1854 /* Returns ptr to the file extension within *basename */
arg_extension(const char * basename)1855 static const char * arg_extension(const char *basename)
1856 {
1857     /* find the last occurrence of '.' in basename */
1858     const char *result = (basename ? strrchr(basename, '.') : NULL);
1859 
1860     /* if no '.' was found then return pointer to end of basename */
1861     if (basename && !result)
1862         result = basename + strlen(basename);
1863 
1864     /* special case: basenames with a single leading dot (eg ".foo") are not considered as true extensions */
1865     if (basename && result == basename)
1866         result = basename + strlen(basename);
1867 
1868     /* special case: empty extensions (eg "foo.","foo..") are not considered as true extensions */
1869     if (basename && result && result[1] == '\0')
1870         result = basename + strlen(basename);
1871 
1872     return result;
1873 }
1874 
1875 
arg_file_scanfn(struct arg_file * parent,const char * argval)1876 static int arg_file_scanfn(struct arg_file *parent, const char *argval)
1877 {
1878     int errorcode = 0;
1879 
1880     if (parent->count == parent->hdr.maxcount)
1881     {
1882         /* maximum number of arguments exceeded */
1883         errorcode = EMAXCOUNT;
1884     }
1885     else if (!argval)
1886     {
1887         /* a valid argument with no argument value was given. */
1888         /* This happens when an optional argument value was invoked. */
1889         /* leave parent arguiment value unaltered but still count the argument. */
1890         parent->count++;
1891     }
1892     else
1893     {
1894         parent->filename[parent->count]  = argval;
1895         parent->basename[parent->count]  = arg_basename(argval);
1896         parent->extension[parent->count] =
1897             arg_extension(parent->basename[parent->count]);                                /* only seek extensions within the basename (not the file path)*/
1898         parent->count++;
1899     }
1900 
1901     ARG_TRACE(("%s4:scanfn(%p) returns %d\n", __FILE__, parent, errorcode));
1902     return errorcode;
1903 }
1904 
1905 
arg_file_checkfn(struct arg_file * parent)1906 static int arg_file_checkfn(struct arg_file *parent)
1907 {
1908     int errorcode = (parent->count < parent->hdr.mincount) ? EMINCOUNT : 0;
1909 
1910     ARG_TRACE(("%s:checkfn(%p) returns %d\n", __FILE__, parent, errorcode));
1911     return errorcode;
1912 }
1913 
1914 
arg_file_errorfn(struct arg_file * parent,FILE * fp,int errorcode,const char * argval,const char * progname)1915 static void arg_file_errorfn(
1916     struct arg_file *parent,
1917     FILE *fp,
1918     int errorcode,
1919     const char *argval,
1920     const char *progname)
1921 {
1922     const char *shortopts = parent->hdr.shortopts;
1923     const char *longopts  = parent->hdr.longopts;
1924     const char *datatype  = parent->hdr.datatype;
1925 
1926     /* make argval NULL safe */
1927     argval = argval ? argval : "";
1928 
1929     fprintf(fp, "%s: ", progname);
1930     switch(errorcode)
1931     {
1932     case EMINCOUNT:
1933         fputs("missing option ", fp);
1934         arg_print_option(fp, shortopts, longopts, datatype, "\n");
1935         break;
1936 
1937     case EMAXCOUNT:
1938         fputs("excess option ", fp);
1939         arg_print_option(fp, shortopts, longopts, argval, "\n");
1940         break;
1941 
1942     default:
1943         fprintf(fp, "unknown error at \"%s\"\n", argval);
1944     }
1945 }
1946 
1947 
arg_file0(const char * shortopts,const char * longopts,const char * datatype,const char * glossary)1948 struct arg_file * arg_file0(
1949     const char * shortopts,
1950     const char * longopts,
1951     const char *datatype,
1952     const char *glossary)
1953 {
1954     return arg_filen(shortopts, longopts, datatype, 0, 1, glossary);
1955 }
1956 
1957 
arg_file1(const char * shortopts,const char * longopts,const char * datatype,const char * glossary)1958 struct arg_file * arg_file1(
1959     const char * shortopts,
1960     const char * longopts,
1961     const char *datatype,
1962     const char *glossary)
1963 {
1964     return arg_filen(shortopts, longopts, datatype, 1, 1, glossary);
1965 }
1966 
1967 
arg_filen(const char * shortopts,const char * longopts,const char * datatype,int mincount,int maxcount,const char * glossary)1968 struct arg_file * arg_filen(
1969     const char * shortopts,
1970     const char * longopts,
1971     const char *datatype,
1972     int mincount,
1973     int maxcount,
1974     const char *glossary)
1975 {
1976     size_t nbytes;
1977     struct arg_file *result;
1978 
1979     /* foolproof things by ensuring maxcount is not less than mincount */
1980     maxcount = (maxcount < mincount) ? mincount : maxcount;
1981 
1982     nbytes = sizeof(struct arg_file)      /* storage for struct arg_file */
1983              + sizeof(char *) * maxcount  /* storage for filename[maxcount] array */
1984              + sizeof(char *) * maxcount  /* storage for basename[maxcount] array */
1985              + sizeof(char *) * maxcount; /* storage for extension[maxcount] array */
1986 
1987     result = (struct arg_file *)malloc(nbytes);
1988     if (result)
1989     {
1990         int i;
1991 
1992         /* init the arg_hdr struct */
1993         result->hdr.flag      = ARG_HASVALUE;
1994         result->hdr.shortopts = shortopts;
1995         result->hdr.longopts  = longopts;
1996         result->hdr.glossary  = glossary;
1997         result->hdr.datatype  = datatype ? datatype : "<file>";
1998         result->hdr.mincount  = mincount;
1999         result->hdr.maxcount  = maxcount;
2000         result->hdr.parent    = result;
2001         result->hdr.resetfn   = (arg_resetfn *)arg_file_resetfn;
2002         result->hdr.scanfn    = (arg_scanfn *)arg_file_scanfn;
2003         result->hdr.checkfn   = (arg_checkfn *)arg_file_checkfn;
2004         result->hdr.errorfn   = (arg_errorfn *)arg_file_errorfn;
2005 
2006         /* store the filename,basename,extension arrays immediately after the arg_file struct */
2007         result->filename  = (const char * *)(result + 1);
2008         result->basename  = result->filename + maxcount;
2009         result->extension = result->basename + maxcount;
2010         result->count = 0;
2011 
2012         /* foolproof the string pointers by initialising them with empty strings */
2013         for (i = 0; i < maxcount; i++)
2014         {
2015             result->filename[i] = "";
2016             result->basename[i] = "";
2017             result->extension[i] = "";
2018         }
2019     }
2020 
2021     ARG_TRACE(("arg_filen() returns %p\n", result));
2022     return result;
2023 }
2024 /*******************************************************************************
2025  * This file is part of the argtable3 library.
2026  *
2027  * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann
2028  * <sheitmann@users.sourceforge.net>
2029  * All rights reserved.
2030  *
2031  * Redistribution and use in source and binary forms, with or without
2032  * modification, are permitted provided that the following conditions are met:
2033  *     * Redistributions of source code must retain the above copyright
2034  *       notice, this list of conditions and the following disclaimer.
2035  *     * Redistributions in binary form must reproduce the above copyright
2036  *       notice, this list of conditions and the following disclaimer in the
2037  *       documentation and/or other materials provided with the distribution.
2038  *     * Neither the name of STEWART HEITMANN nor the  names of its contributors
2039  *       may be used to endorse or promote products derived from this software
2040  *       without specific prior written permission.
2041  *
2042  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
2043  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2044  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2045  * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT,
2046  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
2047  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
2048  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
2049  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2050  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
2051  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2052  ******************************************************************************/
2053 
2054 #include <stdlib.h>
2055 #include <limits.h>
2056 #include <ctype.h>
2057 
2058 #include "argtable3.h"
2059 
2060 
arg_int_resetfn(struct arg_int * parent)2061 static void arg_int_resetfn(struct arg_int *parent)
2062 {
2063     ARG_TRACE(("%s:resetfn(%p)\n", __FILE__, parent));
2064     parent->count = 0;
2065 }
2066 
2067 
2068 /* strtol0x() is like strtol() except that the numeric string is    */
2069 /* expected to be prefixed by "0X" where X is a user supplied char. */
2070 /* The string may optionally be prefixed by white space and + or -  */
2071 /* as in +0X123 or -0X123.                                          */
2072 /* Once the prefix has been scanned, the remainder of the numeric   */
2073 /* string is converted using strtol() with the given base.          */
2074 /* eg: to parse hex str="-0X12324", specify X='X' and base=16.      */
2075 /* eg: to parse oct str="+0o12324", specify X='O' and base=8.       */
2076 /* eg: to parse bin str="-0B01010", specify X='B' and base=2.       */
2077 /* Failure of conversion is indicated by result where *endptr==str. */
strtol0X(const char * str,const char ** endptr,char X,int base)2078 static long int strtol0X(const char * str,
2079                          const char * *endptr,
2080                          char X,
2081                          int base)
2082 {
2083     long int val;               /* stores result */
2084     int s = 1;                    /* sign is +1 or -1 */
2085     const char *ptr = str;        /* ptr to current position in str */
2086 
2087     /* skip leading whitespace */
2088     while (isspace((int) *ptr))
2089         ptr++;
2090     /* printf("1) %s\n",ptr); */
2091 
2092     /* scan optional sign character */
2093     switch (*ptr)
2094     {
2095     case '+':
2096         ptr++;
2097         s = 1;
2098         break;
2099     case '-':
2100         ptr++;
2101         s = -1;
2102         break;
2103     default:
2104         s = 1;
2105         break;
2106     }
2107     /* printf("2) %s\n",ptr); */
2108 
2109     /* '0X' prefix */
2110     if ((*ptr++) != '0')
2111     {
2112         /* printf("failed to detect '0'\n"); */
2113         *endptr = str;
2114         return 0;
2115     }
2116     /* printf("3) %s\n",ptr); */
2117     if (toupper((int) *ptr++) != toupper((int) X))
2118     {
2119         /* printf("failed to detect '%c'\n",X); */
2120         *endptr = str;
2121         return 0;
2122     }
2123     /* printf("4) %s\n",ptr); */
2124 
2125     /* attempt conversion on remainder of string using strtol() */
2126     val = strtol(ptr, (char * *)endptr, base);
2127     if (*endptr == ptr)
2128     {
2129         /* conversion failed */
2130         *endptr = str;
2131         return 0;
2132     }
2133 
2134     /* success */
2135     return s * val;
2136 }
2137 
2138 
2139 /* Returns 1 if str matches suffix (case insensitive).    */
2140 /* Str may contain trailing whitespace, but nothing else. */
detectsuffix(const char * str,const char * suffix)2141 static int detectsuffix(const char *str, const char *suffix)
2142 {
2143     /* scan pairwise through strings until mismatch detected */
2144     while( toupper((int) *str) == toupper((int) *suffix) )
2145     {
2146         /* printf("'%c' '%c'\n", *str, *suffix); */
2147 
2148         /* return 1 (success) if match persists until the string terminator */
2149         if (*str == '\0')
2150             return 1;
2151 
2152         /* next chars */
2153         str++;
2154         suffix++;
2155     }
2156     /* printf("'%c' '%c' mismatch\n", *str, *suffix); */
2157 
2158     /* return 0 (fail) if the matching did not consume the entire suffix */
2159     if (*suffix != 0)
2160         return 0;   /* failed to consume entire suffix */
2161 
2162     /* skip any remaining whitespace in str */
2163     while (isspace((int) *str))
2164         str++;
2165 
2166     /* return 1 (success) if we have reached end of str else return 0 (fail) */
2167     return (*str == '\0') ? 1 : 0;
2168 }
2169 
2170 
arg_int_scanfn(struct arg_int * parent,const char * argval)2171 static int arg_int_scanfn(struct arg_int *parent, const char *argval)
2172 {
2173     int errorcode = 0;
2174 
2175     if (parent->count == parent->hdr.maxcount)
2176     {
2177         /* maximum number of arguments exceeded */
2178         errorcode = EMAXCOUNT;
2179     }
2180     else if (!argval)
2181     {
2182         /* a valid argument with no argument value was given. */
2183         /* This happens when an optional argument value was invoked. */
2184         /* leave parent arguiment value unaltered but still count the argument. */
2185         parent->count++;
2186     }
2187     else
2188     {
2189         long int val;
2190         const char *end;
2191 
2192         /* attempt to extract hex integer (eg: +0x123) from argval into val conversion */
2193         val = strtol0X(argval, &end, 'X', 16);
2194         if (end == argval)
2195         {
2196             /* hex failed, attempt octal conversion (eg +0o123) */
2197             val = strtol0X(argval, &end, 'O', 8);
2198             if (end == argval)
2199             {
2200                 /* octal failed, attempt binary conversion (eg +0B101) */
2201                 val = strtol0X(argval, &end, 'B', 2);
2202                 if (end == argval)
2203                 {
2204                     /* binary failed, attempt decimal conversion with no prefix (eg 1234) */
2205                     val = strtol(argval, (char * *)&end, 10);
2206                     if (end == argval)
2207                     {
2208                         /* all supported number formats failed */
2209                         return EBADINT;
2210                     }
2211                 }
2212             }
2213         }
2214 
2215         /* Safety check for integer overflow. WARNING: this check    */
2216         /* achieves nothing on machines where size(int)==size(long). */
2217         if ( val > INT_MAX || val < INT_MIN )
2218             errorcode = EOVERFLOW;
2219 
2220         /* Detect any suffixes (KB,MB,GB) and multiply argument value appropriately. */
2221         /* We need to be mindful of integer overflows when using such big numbers.   */
2222         if (detectsuffix(end, "KB"))             /* kilobytes */
2223         {
2224             if ( val > (INT_MAX / 1024) || val < (INT_MIN / 1024) )
2225                 errorcode = EOVERFLOW;          /* Overflow would occur if we proceed */
2226             else
2227                 val *= 1024;                    /* 1KB = 1024 */
2228         }
2229         else if (detectsuffix(end, "MB"))        /* megabytes */
2230         {
2231             if ( val > (INT_MAX / 1048576) || val < (INT_MIN / 1048576) )
2232                 errorcode = EOVERFLOW;          /* Overflow would occur if we proceed */
2233             else
2234                 val *= 1048576;                 /* 1MB = 1024*1024 */
2235         }
2236         else if (detectsuffix(end, "GB"))        /* gigabytes */
2237         {
2238             if ( val > (INT_MAX / 1073741824) || val < (INT_MIN / 1073741824) )
2239                 errorcode = EOVERFLOW;          /* Overflow would occur if we proceed */
2240             else
2241                 val *= 1073741824;              /* 1GB = 1024*1024*1024 */
2242         }
2243         else if (!detectsuffix(end, ""))
2244             errorcode = EBADINT;                /* invalid suffix detected */
2245 
2246         /* if success then store result in parent->ival[] array */
2247         if (errorcode == 0)
2248             parent->ival[parent->count++] = val;
2249     }
2250 
2251     /* printf("%s:scanfn(%p,%p) returns %d\n",__FILE__,parent,argval,errorcode); */
2252     return errorcode;
2253 }
2254 
2255 
arg_int_checkfn(struct arg_int * parent)2256 static int arg_int_checkfn(struct arg_int *parent)
2257 {
2258     int errorcode = (parent->count < parent->hdr.mincount) ? EMINCOUNT : 0;
2259     /*printf("%s:checkfn(%p) returns %d\n",__FILE__,parent,errorcode);*/
2260     return errorcode;
2261 }
2262 
2263 
arg_int_errorfn(struct arg_int * parent,FILE * fp,int errorcode,const char * argval,const char * progname)2264 static void arg_int_errorfn(
2265     struct arg_int *parent,
2266     FILE *fp,
2267     int errorcode,
2268     const char *argval,
2269     const char *progname)
2270 {
2271     const char *shortopts = parent->hdr.shortopts;
2272     const char *longopts  = parent->hdr.longopts;
2273     const char *datatype  = parent->hdr.datatype;
2274 
2275     /* make argval NULL safe */
2276     argval = argval ? argval : "";
2277 
2278     fprintf(fp, "%s: ", progname);
2279     switch(errorcode)
2280     {
2281     case EMINCOUNT:
2282         fputs("missing option ", fp);
2283         arg_print_option(fp, shortopts, longopts, datatype, "\n");
2284         break;
2285 
2286     case EMAXCOUNT:
2287         fputs("excess option ", fp);
2288         arg_print_option(fp, shortopts, longopts, argval, "\n");
2289         break;
2290 
2291     case EBADINT:
2292         fprintf(fp, "invalid argument \"%s\" to option ", argval);
2293         arg_print_option(fp, shortopts, longopts, datatype, "\n");
2294         break;
2295 
2296     case EOVERFLOW:
2297         fputs("integer overflow at option ", fp);
2298         arg_print_option(fp, shortopts, longopts, datatype, " ");
2299         fprintf(fp, "(%s is too large)\n", argval);
2300         break;
2301     }
2302 }
2303 
2304 
arg_int0(const char * shortopts,const char * longopts,const char * datatype,const char * glossary)2305 struct arg_int * arg_int0(
2306     const char *shortopts,
2307     const char *longopts,
2308     const char *datatype,
2309     const char *glossary)
2310 {
2311     return arg_intn(shortopts, longopts, datatype, 0, 1, glossary);
2312 }
2313 
2314 
arg_int1(const char * shortopts,const char * longopts,const char * datatype,const char * glossary)2315 struct arg_int * arg_int1(
2316     const char *shortopts,
2317     const char *longopts,
2318     const char *datatype,
2319     const char *glossary)
2320 {
2321     return arg_intn(shortopts, longopts, datatype, 1, 1, glossary);
2322 }
2323 
2324 
arg_intn(const char * shortopts,const char * longopts,const char * datatype,int mincount,int maxcount,const char * glossary)2325 struct arg_int * arg_intn(
2326     const char *shortopts,
2327     const char *longopts,
2328     const char *datatype,
2329     int mincount,
2330     int maxcount,
2331     const char *glossary)
2332 {
2333     size_t nbytes;
2334     struct arg_int *result;
2335 
2336     /* foolproof things by ensuring maxcount is not less than mincount */
2337     maxcount = (maxcount < mincount) ? mincount : maxcount;
2338 
2339     nbytes = sizeof(struct arg_int)    /* storage for struct arg_int */
2340              + maxcount * sizeof(int); /* storage for ival[maxcount] array */
2341 
2342     result = (struct arg_int *)malloc(nbytes);
2343     if (result)
2344     {
2345         /* init the arg_hdr struct */
2346         result->hdr.flag      = ARG_HASVALUE;
2347         result->hdr.shortopts = shortopts;
2348         result->hdr.longopts  = longopts;
2349         result->hdr.datatype  = datatype ? datatype : "<int>";
2350         result->hdr.glossary  = glossary;
2351         result->hdr.mincount  = mincount;
2352         result->hdr.maxcount  = maxcount;
2353         result->hdr.parent    = result;
2354         result->hdr.resetfn   = (arg_resetfn *)arg_int_resetfn;
2355         result->hdr.scanfn    = (arg_scanfn *)arg_int_scanfn;
2356         result->hdr.checkfn   = (arg_checkfn *)arg_int_checkfn;
2357         result->hdr.errorfn   = (arg_errorfn *)arg_int_errorfn;
2358 
2359         /* store the ival[maxcount] array immediately after the arg_int struct */
2360         result->ival  = (int *)(result + 1);
2361         result->count = 0;
2362     }
2363 
2364     ARG_TRACE(("arg_intn() returns %p\n", result));
2365     return result;
2366 }
2367 /*******************************************************************************
2368  * This file is part of the argtable3 library.
2369  *
2370  * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann
2371  * <sheitmann@users.sourceforge.net>
2372  * All rights reserved.
2373  *
2374  * Redistribution and use in source and binary forms, with or without
2375  * modification, are permitted provided that the following conditions are met:
2376  *     * Redistributions of source code must retain the above copyright
2377  *       notice, this list of conditions and the following disclaimer.
2378  *     * Redistributions in binary form must reproduce the above copyright
2379  *       notice, this list of conditions and the following disclaimer in the
2380  *       documentation and/or other materials provided with the distribution.
2381  *     * Neither the name of STEWART HEITMANN nor the  names of its contributors
2382  *       may be used to endorse or promote products derived from this software
2383  *       without specific prior written permission.
2384  *
2385  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
2386  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2387  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2388  * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT,
2389  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
2390  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
2391  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
2392  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2393  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
2394  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2395  ******************************************************************************/
2396 
2397 #include <stdlib.h>
2398 
2399 #include "argtable3.h"
2400 
2401 
arg_lit_resetfn(struct arg_lit * parent)2402 static void arg_lit_resetfn(struct arg_lit *parent)
2403 {
2404     ARG_TRACE(("%s:resetfn(%p)\n", __FILE__, parent));
2405     parent->count = 0;
2406 }
2407 
2408 
arg_lit_scanfn(struct arg_lit * parent,const char * argval)2409 static int arg_lit_scanfn(struct arg_lit *parent, const char *argval)
2410 {
2411     int errorcode = 0;
2412     if (parent->count < parent->hdr.maxcount )
2413         parent->count++;
2414     else
2415         errorcode = EMAXCOUNT;
2416 
2417     ARG_TRACE(("%s:scanfn(%p,%s) returns %d\n", __FILE__, parent, argval,
2418                errorcode));
2419     return errorcode;
2420 }
2421 
2422 
arg_lit_checkfn(struct arg_lit * parent)2423 static int arg_lit_checkfn(struct arg_lit *parent)
2424 {
2425     int errorcode = (parent->count < parent->hdr.mincount) ? EMINCOUNT : 0;
2426     ARG_TRACE(("%s:checkfn(%p) returns %d\n", __FILE__, parent, errorcode));
2427     return errorcode;
2428 }
2429 
2430 
arg_lit_errorfn(struct arg_lit * parent,FILE * fp,int errorcode,const char * argval,const char * progname)2431 static void arg_lit_errorfn(
2432     struct arg_lit *parent,
2433     FILE *fp,
2434     int errorcode,
2435     const char *argval,
2436     const char *progname)
2437 {
2438     const char *shortopts = parent->hdr.shortopts;
2439     const char *longopts  = parent->hdr.longopts;
2440     const char *datatype  = parent->hdr.datatype;
2441 
2442     switch(errorcode)
2443     {
2444     case EMINCOUNT:
2445         fprintf(fp, "%s: missing option ", progname);
2446         arg_print_option(fp, shortopts, longopts, datatype, "\n");
2447         fprintf(fp, "\n");
2448         break;
2449 
2450     case EMAXCOUNT:
2451         fprintf(fp, "%s: extraneous option ", progname);
2452         arg_print_option(fp, shortopts, longopts, datatype, "\n");
2453         break;
2454     }
2455 
2456     ARG_TRACE(("%s:errorfn(%p, %p, %d, %s, %s)\n", __FILE__, parent, fp,
2457                errorcode, argval, progname));
2458 }
2459 
2460 
arg_lit0(const char * shortopts,const char * longopts,const char * glossary)2461 struct arg_lit * arg_lit0(
2462     const char * shortopts,
2463     const char * longopts,
2464     const char * glossary)
2465 {
2466     return arg_litn(shortopts, longopts, 0, 1, glossary);
2467 }
2468 
2469 
arg_lit1(const char * shortopts,const char * longopts,const char * glossary)2470 struct arg_lit * arg_lit1(
2471     const char *shortopts,
2472     const char *longopts,
2473     const char *glossary)
2474 {
2475     return arg_litn(shortopts, longopts, 1, 1, glossary);
2476 }
2477 
2478 
arg_litn(const char * shortopts,const char * longopts,int mincount,int maxcount,const char * glossary)2479 struct arg_lit * arg_litn(
2480     const char *shortopts,
2481     const char *longopts,
2482     int mincount,
2483     int maxcount,
2484     const char *glossary)
2485 {
2486     struct arg_lit *result;
2487 
2488     /* foolproof things by ensuring maxcount is not less than mincount */
2489     maxcount = (maxcount < mincount) ? mincount : maxcount;
2490 
2491     result = (struct arg_lit *)malloc(sizeof(struct arg_lit));
2492     if (result)
2493     {
2494         /* init the arg_hdr struct */
2495         result->hdr.flag      = 0;
2496         result->hdr.shortopts = shortopts;
2497         result->hdr.longopts  = longopts;
2498         result->hdr.datatype  = NULL;
2499         result->hdr.glossary  = glossary;
2500         result->hdr.mincount  = mincount;
2501         result->hdr.maxcount  = maxcount;
2502         result->hdr.parent    = result;
2503         result->hdr.resetfn   = (arg_resetfn *)arg_lit_resetfn;
2504         result->hdr.scanfn    = (arg_scanfn *)arg_lit_scanfn;
2505         result->hdr.checkfn   = (arg_checkfn *)arg_lit_checkfn;
2506         result->hdr.errorfn   = (arg_errorfn *)arg_lit_errorfn;
2507 
2508         /* init local variables */
2509         result->count = 0;
2510     }
2511 
2512     ARG_TRACE(("arg_litn() returns %p\n", result));
2513     return result;
2514 }
2515 /*******************************************************************************
2516  * This file is part of the argtable3 library.
2517  *
2518  * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann
2519  * <sheitmann@users.sourceforge.net>
2520  * All rights reserved.
2521  *
2522  * Redistribution and use in source and binary forms, with or without
2523  * modification, are permitted provided that the following conditions are met:
2524  *     * Redistributions of source code must retain the above copyright
2525  *       notice, this list of conditions and the following disclaimer.
2526  *     * Redistributions in binary form must reproduce the above copyright
2527  *       notice, this list of conditions and the following disclaimer in the
2528  *       documentation and/or other materials provided with the distribution.
2529  *     * Neither the name of STEWART HEITMANN nor the  names of its contributors
2530  *       may be used to endorse or promote products derived from this software
2531  *       without specific prior written permission.
2532  *
2533  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
2534  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2535  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2536  * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT,
2537  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
2538  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
2539  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
2540  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2541  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
2542  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2543  ******************************************************************************/
2544 
2545 #include <stdlib.h>
2546 
2547 #include "argtable3.h"
2548 
arg_rem(const char * datatype,const char * glossary)2549 struct arg_rem *arg_rem(const char *datatype, const char *glossary)
2550 {
2551     struct arg_rem *result = (struct arg_rem *)malloc(sizeof(struct arg_rem));
2552     if (result)
2553     {
2554         result->hdr.flag = 0;
2555         result->hdr.shortopts = NULL;
2556         result->hdr.longopts = NULL;
2557         result->hdr.datatype = datatype;
2558         result->hdr.glossary = glossary;
2559         result->hdr.mincount = 1;
2560         result->hdr.maxcount = 1;
2561         result->hdr.parent = result;
2562         result->hdr.resetfn = NULL;
2563         result->hdr.scanfn = NULL;
2564         result->hdr.checkfn = NULL;
2565         result->hdr.errorfn = NULL;
2566     }
2567 
2568     ARG_TRACE(("arg_rem() returns %p\n", result));
2569     return result;
2570 }
2571 
2572 /*******************************************************************************
2573  * This file is part of the argtable3 library.
2574  *
2575  * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann
2576  * <sheitmann@users.sourceforge.net>
2577  * All rights reserved.
2578  *
2579  * Redistribution and use in source and binary forms, with or without
2580  * modification, are permitted provided that the following conditions are met:
2581  *     * Redistributions of source code must retain the above copyright
2582  *       notice, this list of conditions and the following disclaimer.
2583  *     * Redistributions in binary form must reproduce the above copyright
2584  *       notice, this list of conditions and the following disclaimer in the
2585  *       documentation and/or other materials provided with the distribution.
2586  *     * Neither the name of STEWART HEITMANN nor the  names of its contributors
2587  *       may be used to endorse or promote products derived from this software
2588  *       without specific prior written permission.
2589  *
2590  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
2591  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2592  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2593  * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT,
2594  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
2595  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
2596  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
2597  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2598  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
2599  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2600  ******************************************************************************/
2601 
2602 #include <stdlib.h>
2603 #include <string.h>
2604 
2605 #include "argtable3.h"
2606 
2607 
2608 #ifndef _TREX_H_
2609 #define _TREX_H_
2610 /***************************************************************
2611 	T-Rex a tiny regular expression library
2612 
2613 	Copyright (C) 2003-2006 Alberto Demichelis
2614 
2615 	This software is provided 'as-is', without any express
2616 	or implied warranty. In no event will the authors be held
2617 	liable for any damages arising from the use of this software.
2618 
2619 	Permission is granted to anyone to use this software for
2620 	any purpose, including commercial applications, and to alter
2621 	it and redistribute it freely, subject to the following restrictions:
2622 
2623 		1. The origin of this software must not be misrepresented;
2624 		you must not claim that you wrote the original software.
2625 		If you use this software in a product, an acknowledgment
2626 		in the product documentation would be appreciated but
2627 		is not required.
2628 
2629 		2. Altered source versions must be plainly marked as such,
2630 		and must not be misrepresented as being the original software.
2631 
2632 		3. This notice may not be removed or altered from any
2633 		source distribution.
2634 
2635 ****************************************************************/
2636 
2637 #ifdef __cplusplus
2638 extern "C" {
2639 #endif
2640 
2641 #ifdef _UNICODE
2642 #define TRexChar unsigned short
2643 #define MAX_CHAR 0xFFFF
2644 #define _TREXC(c) L##c
2645 #define trex_strlen wcslen
2646 #define trex_printf wprintf
2647 #else
2648 #define TRexChar char
2649 #define MAX_CHAR 0xFF
2650 #define _TREXC(c) (c)
2651 #define trex_strlen strlen
2652 #define trex_printf printf
2653 #endif
2654 
2655 #ifndef TREX_API
2656 #define TREX_API extern
2657 #endif
2658 
2659 #define TRex_True 1
2660 #define TRex_False 0
2661 
2662 #define TREX_ICASE ARG_REX_ICASE
2663 
2664 typedef unsigned int TRexBool;
2665 typedef struct TRex TRex;
2666 
2667 typedef struct {
2668 	const TRexChar *begin;
2669 	int len;
2670 } TRexMatch;
2671 
2672 TREX_API TRex *trex_compile(const TRexChar *pattern, const TRexChar **error, int flags);
2673 TREX_API void trex_free(TRex *exp);
2674 TREX_API TRexBool trex_match(TRex* exp, const TRexChar* text);
2675 TREX_API TRexBool trex_search(TRex* exp, const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end);
2676 TREX_API TRexBool trex_searchrange(TRex* exp, const TRexChar* text_begin, const TRexChar* text_end, const TRexChar** out_begin, const TRexChar** out_end);
2677 TREX_API int trex_getsubexpcount(TRex* exp);
2678 TREX_API TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp);
2679 
2680 #ifdef __cplusplus
2681 }
2682 #endif
2683 
2684 #endif
2685 
2686 
2687 
2688 struct privhdr
2689 {
2690     const char *pattern;
2691     int flags;
2692 };
2693 
2694 
arg_rex_resetfn(struct arg_rex * parent)2695 static void arg_rex_resetfn(struct arg_rex *parent)
2696 {
2697     ARG_TRACE(("%s:resetfn(%p)\n", __FILE__, parent));
2698     parent->count = 0;
2699 }
2700 
arg_rex_scanfn(struct arg_rex * parent,const char * argval)2701 static int arg_rex_scanfn(struct arg_rex *parent, const char *argval)
2702 {
2703     int errorcode = 0;
2704     const TRexChar *error = NULL;
2705     TRex *rex = NULL;
2706     TRexBool is_match = TRex_False;
2707 
2708     if (parent->count == parent->hdr.maxcount )
2709     {
2710         /* maximum number of arguments exceeded */
2711         errorcode = EMAXCOUNT;
2712     }
2713     else if (!argval)
2714     {
2715         /* a valid argument with no argument value was given. */
2716         /* This happens when an optional argument value was invoked. */
2717         /* leave parent argument value unaltered but still count the argument. */
2718         parent->count++;
2719     }
2720     else
2721     {
2722         struct privhdr *priv = (struct privhdr *)parent->hdr.priv;
2723 
2724         /* test the current argument value for a match with the regular expression */
2725         /* if a match is detected, record the argument value in the arg_rex struct */
2726 
2727         rex = trex_compile(priv->pattern, &error, priv->flags);
2728         is_match = trex_match(rex, argval);
2729         if (!is_match)
2730             errorcode = EREGNOMATCH;
2731         else
2732             parent->sval[parent->count++] = argval;
2733 
2734         trex_free(rex);
2735     }
2736 
2737     ARG_TRACE(("%s:scanfn(%p) returns %d\n",__FILE__,parent,errorcode));
2738     return errorcode;
2739 }
2740 
arg_rex_checkfn(struct arg_rex * parent)2741 static int arg_rex_checkfn(struct arg_rex *parent)
2742 {
2743     int errorcode = (parent->count < parent->hdr.mincount) ? EMINCOUNT : 0;
2744     //struct privhdr *priv = (struct privhdr*)parent->hdr.priv;
2745 
2746     /* free the regex "program" we constructed in resetfn */
2747     //regfree(&(priv->regex));
2748 
2749     /*printf("%s:checkfn(%p) returns %d\n",__FILE__,parent,errorcode);*/
2750     return errorcode;
2751 }
2752 
arg_rex_errorfn(struct arg_rex * parent,FILE * fp,int errorcode,const char * argval,const char * progname)2753 static void arg_rex_errorfn(struct arg_rex *parent,
2754                     FILE *fp,
2755                     int errorcode,
2756                     const char *argval,
2757                     const char *progname)
2758 {
2759     const char *shortopts = parent->hdr.shortopts;
2760     const char *longopts  = parent->hdr.longopts;
2761     const char *datatype  = parent->hdr.datatype;
2762 
2763     /* make argval NULL safe */
2764     argval = argval ? argval : "";
2765 
2766     fprintf(fp, "%s: ", progname);
2767     switch(errorcode)
2768     {
2769     case EMINCOUNT:
2770         fputs("missing option ", fp);
2771         arg_print_option(fp, shortopts, longopts, datatype, "\n");
2772         break;
2773 
2774     case EMAXCOUNT:
2775         fputs("excess option ", fp);
2776         arg_print_option(fp, shortopts, longopts, argval, "\n");
2777         break;
2778 
2779     case EREGNOMATCH:
2780         fputs("illegal value  ", fp);
2781         arg_print_option(fp, shortopts, longopts, argval, "\n");
2782         break;
2783 
2784     default:
2785     {
2786         //char errbuff[256];
2787         //regerror(errorcode, NULL, errbuff, sizeof(errbuff));
2788         //printf("%s\n", errbuff);
2789     }
2790     break;
2791     }
2792 }
2793 
2794 
arg_rex0(const char * shortopts,const char * longopts,const char * pattern,const char * datatype,int flags,const char * glossary)2795 struct arg_rex * arg_rex0(const char * shortopts,
2796                           const char * longopts,
2797                           const char * pattern,
2798                           const char *datatype,
2799                           int flags,
2800                           const char *glossary)
2801 {
2802     return arg_rexn(shortopts,
2803                     longopts,
2804                     pattern,
2805                     datatype,
2806                     0,
2807                     1,
2808                     flags,
2809                     glossary);
2810 }
2811 
arg_rex1(const char * shortopts,const char * longopts,const char * pattern,const char * datatype,int flags,const char * glossary)2812 struct arg_rex * arg_rex1(const char * shortopts,
2813                           const char * longopts,
2814                           const char * pattern,
2815                           const char *datatype,
2816                           int flags,
2817                           const char *glossary)
2818 {
2819     return arg_rexn(shortopts,
2820                     longopts,
2821                     pattern,
2822                     datatype,
2823                     1,
2824                     1,
2825                     flags,
2826                     glossary);
2827 }
2828 
2829 
arg_rexn(const char * shortopts,const char * longopts,const char * pattern,const char * datatype,int mincount,int maxcount,int flags,const char * glossary)2830 struct arg_rex * arg_rexn(const char * shortopts,
2831                           const char * longopts,
2832                           const char * pattern,
2833                           const char *datatype,
2834                           int mincount,
2835                           int maxcount,
2836                           int flags,
2837                           const char *glossary)
2838 {
2839     size_t nbytes;
2840     struct arg_rex *result;
2841     struct privhdr *priv;
2842     int i;
2843     const TRexChar *error = NULL;
2844     TRex *rex = NULL;
2845 
2846     if (!pattern)
2847     {
2848         printf(
2849             "argtable: ERROR - illegal regular expression pattern \"(NULL)\"\n");
2850         printf("argtable: Bad argument table.\n");
2851         return NULL;
2852     }
2853 
2854     /* foolproof things by ensuring maxcount is not less than mincount */
2855     maxcount = (maxcount < mincount) ? mincount : maxcount;
2856 
2857     nbytes = sizeof(struct arg_rex)       /* storage for struct arg_rex */
2858              + sizeof(struct privhdr)     /* storage for private arg_rex data */
2859              + maxcount * sizeof(char *);  /* storage for sval[maxcount] array */
2860 
2861     result = (struct arg_rex *)malloc(nbytes);
2862     if (result == NULL)
2863         return result;
2864 
2865     /* init the arg_hdr struct */
2866     result->hdr.flag      = ARG_HASVALUE;
2867     result->hdr.shortopts = shortopts;
2868     result->hdr.longopts  = longopts;
2869     result->hdr.datatype  = datatype ? datatype : pattern;
2870     result->hdr.glossary  = glossary;
2871     result->hdr.mincount  = mincount;
2872     result->hdr.maxcount  = maxcount;
2873     result->hdr.parent    = result;
2874     result->hdr.resetfn   = (arg_resetfn *)arg_rex_resetfn;
2875     result->hdr.scanfn    = (arg_scanfn *)arg_rex_scanfn;
2876     result->hdr.checkfn   = (arg_checkfn *)arg_rex_checkfn;
2877     result->hdr.errorfn   = (arg_errorfn *)arg_rex_errorfn;
2878 
2879     /* store the arg_rex_priv struct immediately after the arg_rex struct */
2880     result->hdr.priv  = result + 1;
2881     priv = (struct privhdr *)(result->hdr.priv);
2882     priv->pattern = pattern;
2883     priv->flags = flags;
2884 
2885     /* store the sval[maxcount] array immediately after the arg_rex_priv struct */
2886     result->sval  = (const char * *)(priv + 1);
2887     result->count = 0;
2888 
2889     /* foolproof the string pointers by initializing them to reference empty strings */
2890     for (i = 0; i < maxcount; i++)
2891         result->sval[i] = "";
2892 
2893     /* here we construct and destroy a regex representation of the regular
2894      * expression for no other reason than to force any regex errors to be
2895      * trapped now rather than later. If we don't, then errors may go undetected
2896      * until an argument is actually parsed.
2897      */
2898 
2899     rex = trex_compile(priv->pattern, &error, priv->flags);
2900     if (rex == NULL)
2901     {
2902         ARG_LOG(("argtable: %s \"%s\"\n", error ? error : _TREXC("undefined"), priv->pattern));
2903         ARG_LOG(("argtable: Bad argument table.\n"));
2904     }
2905 
2906     trex_free(rex);
2907 
2908     ARG_TRACE(("arg_rexn() returns %p\n", result));
2909     return result;
2910 }
2911 
2912 
2913 
2914 /* see copyright notice in trex.h */
2915 #include <string.h>
2916 #include <stdlib.h>
2917 #include <ctype.h>
2918 #include <setjmp.h>
2919 
2920 #ifdef _UINCODE
2921 #define scisprint iswprint
2922 #define scstrlen wcslen
2923 #define scprintf wprintf
2924 #define _SC(x) L(x)
2925 #else
2926 #define scisprint isprint
2927 #define scstrlen strlen
2928 #define scprintf printf
2929 #define _SC(x) (x)
2930 #endif
2931 
2932 #ifdef _DEBUG
2933 #include <stdio.h>
2934 
2935 static const TRexChar *g_nnames[] =
2936 {
2937 	_SC("NONE"),_SC("OP_GREEDY"),	_SC("OP_OR"),
2938 	_SC("OP_EXPR"),_SC("OP_NOCAPEXPR"),_SC("OP_DOT"),	_SC("OP_CLASS"),
2939 	_SC("OP_CCLASS"),_SC("OP_NCLASS"),_SC("OP_RANGE"),_SC("OP_CHAR"),
2940 	_SC("OP_EOL"),_SC("OP_BOL"),_SC("OP_WB")
2941 };
2942 
2943 #endif
2944 #define OP_GREEDY		(MAX_CHAR+1) // * + ? {n}
2945 #define OP_OR			(MAX_CHAR+2)
2946 #define OP_EXPR			(MAX_CHAR+3) //parentesis ()
2947 #define OP_NOCAPEXPR	(MAX_CHAR+4) //parentesis (?:)
2948 #define OP_DOT			(MAX_CHAR+5)
2949 #define OP_CLASS		(MAX_CHAR+6)
2950 #define OP_CCLASS		(MAX_CHAR+7)
2951 #define OP_NCLASS		(MAX_CHAR+8) //negates class the [^
2952 #define OP_RANGE		(MAX_CHAR+9)
2953 #define OP_CHAR			(MAX_CHAR+10)
2954 #define OP_EOL			(MAX_CHAR+11)
2955 #define OP_BOL			(MAX_CHAR+12)
2956 #define OP_WB			(MAX_CHAR+13)
2957 
2958 #define TREX_SYMBOL_ANY_CHAR ('.')
2959 #define TREX_SYMBOL_GREEDY_ONE_OR_MORE ('+')
2960 #define TREX_SYMBOL_GREEDY_ZERO_OR_MORE ('*')
2961 #define TREX_SYMBOL_GREEDY_ZERO_OR_ONE ('?')
2962 #define TREX_SYMBOL_BRANCH ('|')
2963 #define TREX_SYMBOL_END_OF_STRING ('$')
2964 #define TREX_SYMBOL_BEGINNING_OF_STRING ('^')
2965 #define TREX_SYMBOL_ESCAPE_CHAR ('\\')
2966 
2967 
2968 typedef int TRexNodeType;
2969 
2970 typedef struct tagTRexNode{
2971 	TRexNodeType type;
2972 	int left;
2973 	int right;
2974 	int next;
2975 }TRexNode;
2976 
2977 struct TRex{
2978 	const TRexChar *_eol;
2979 	const TRexChar *_bol;
2980 	const TRexChar *_p;
2981 	int _first;
2982 	int _op;
2983 	TRexNode *_nodes;
2984 	int _nallocated;
2985 	int _nsize;
2986 	int _nsubexpr;
2987 	TRexMatch *_matches;
2988 	int _currsubexp;
2989 	void *_jmpbuf;
2990 	const TRexChar **_error;
2991 	int _flags;
2992 };
2993 
2994 static int trex_list(TRex *exp);
2995 
trex_newnode(TRex * exp,TRexNodeType type)2996 static int trex_newnode(TRex *exp, TRexNodeType type)
2997 {
2998 	TRexNode n;
2999 	int newid;
3000 	n.type = type;
3001 	n.next = n.right = n.left = -1;
3002 	if(type == OP_EXPR)
3003 		n.right = exp->_nsubexpr++;
3004 	if(exp->_nallocated < (exp->_nsize + 1)) {
3005 		exp->_nallocated *= 2;
3006 		exp->_nodes = (TRexNode *)realloc(exp->_nodes, exp->_nallocated * sizeof(TRexNode));
3007 	}
3008 	exp->_nodes[exp->_nsize++] = n; // NOLINT(clang-analyzer-unix.Malloc)
3009 	newid = exp->_nsize - 1;
3010 	return (int)newid;
3011 }
3012 
trex_error(TRex * exp,const TRexChar * error)3013 static void trex_error(TRex *exp,const TRexChar *error)
3014 {
3015 	if(exp->_error) *exp->_error = error;
3016 	longjmp(*((jmp_buf*)exp->_jmpbuf),-1);
3017 }
3018 
trex_expect(TRex * exp,int n)3019 static void trex_expect(TRex *exp, int n){
3020 	if((*exp->_p) != n)
3021 		trex_error(exp, _SC("expected paren"));
3022 	exp->_p++;
3023 }
3024 
trex_escapechar(TRex * exp)3025 static TRexChar trex_escapechar(TRex *exp)
3026 {
3027 	if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR){
3028 		exp->_p++;
3029 		switch(*exp->_p) {
3030 		case 'v': exp->_p++; return '\v';
3031 		case 'n': exp->_p++; return '\n';
3032 		case 't': exp->_p++; return '\t';
3033 		case 'r': exp->_p++; return '\r';
3034 		case 'f': exp->_p++; return '\f';
3035 		default: return (*exp->_p++);
3036 		}
3037 	} else if(!scisprint((int) *exp->_p)) trex_error(exp,_SC("letter expected"));
3038 	return (*exp->_p++);
3039 }
3040 
trex_charclass(TRex * exp,int classid)3041 static int trex_charclass(TRex *exp,int classid)
3042 {
3043 	int n = trex_newnode(exp,OP_CCLASS);
3044 	exp->_nodes[n].left = classid;
3045 	return n;
3046 }
3047 
trex_charnode(TRex * exp,TRexBool isclass)3048 static int trex_charnode(TRex *exp,TRexBool isclass)
3049 {
3050 	TRexChar t;
3051 	if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR) {
3052 		exp->_p++;
3053 		switch(*exp->_p) {
3054 			case 'n': exp->_p++; return trex_newnode(exp,'\n');
3055 			case 't': exp->_p++; return trex_newnode(exp,'\t');
3056 			case 'r': exp->_p++; return trex_newnode(exp,'\r');
3057 			case 'f': exp->_p++; return trex_newnode(exp,'\f');
3058 			case 'v': exp->_p++; return trex_newnode(exp,'\v');
3059 			case 'a': case 'A': case 'w': case 'W': case 's': case 'S':
3060 			case 'd': case 'D': case 'x': case 'X': case 'c': case 'C':
3061 			case 'p': case 'P': case 'l': case 'u':
3062 				{
3063 				t = *exp->_p; exp->_p++;
3064 				return trex_charclass(exp,t);
3065 				}
3066 			case 'b':
3067 			case 'B':
3068 				if(!isclass) {
3069 					int node = trex_newnode(exp,OP_WB);
3070 					exp->_nodes[node].left = *exp->_p;
3071 					exp->_p++;
3072 					return node;
3073 				} //else default
3074 				/* falls through */
3075 			default:
3076 				t = *exp->_p; exp->_p++;
3077 				return trex_newnode(exp,t);
3078 		}
3079 	}
3080 	else if(!scisprint((int) *exp->_p)) {
3081 
3082 		trex_error(exp,_SC("letter expected"));
3083 	}
3084 	t = *exp->_p; exp->_p++;
3085 	return trex_newnode(exp,t);
3086 }
trex_class(TRex * exp)3087 static int trex_class(TRex *exp)
3088 {
3089 	int ret = -1;
3090 	int first = -1,chain;
3091 	if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING){
3092 		ret = trex_newnode(exp,OP_NCLASS);
3093 		exp->_p++;
3094 	}else ret = trex_newnode(exp,OP_CLASS);
3095 
3096 	if(*exp->_p == ']') trex_error(exp,_SC("empty class"));
3097 	chain = ret;
3098 	while(*exp->_p != ']' && exp->_p != exp->_eol) {
3099 		if(*exp->_p == '-' && first != -1){
3100 			int r,t;
3101 			if(*exp->_p++ == ']') trex_error(exp,_SC("unfinished range"));
3102 			r = trex_newnode(exp,OP_RANGE);
3103 			if(first>*exp->_p) trex_error(exp,_SC("invalid range"));
3104 			if(exp->_nodes[first].type == OP_CCLASS) trex_error(exp,_SC("cannot use character classes in ranges"));
3105 			exp->_nodes[r].left = exp->_nodes[first].type;
3106 			t = trex_escapechar(exp);
3107 			exp->_nodes[r].right = t;
3108             exp->_nodes[chain].next = r;
3109 			chain = r;
3110 			first = -1;
3111 		}
3112 		else{
3113 			if(first!=-1){
3114 				int c = first;
3115 				exp->_nodes[chain].next = c;
3116 				chain = c;
3117 				first = trex_charnode(exp,TRex_True);
3118 			}
3119 			else{
3120 				first = trex_charnode(exp,TRex_True);
3121 			}
3122 		}
3123 	}
3124 	if(first!=-1){
3125 		int c = first;
3126 		exp->_nodes[chain].next = c;
3127 		chain = c;
3128 		first = -1;
3129 	}
3130 	/* hack? */
3131 	exp->_nodes[ret].left = exp->_nodes[ret].next;
3132 	exp->_nodes[ret].next = -1;
3133 	return ret;
3134 }
3135 
trex_parsenumber(TRex * exp)3136 static int trex_parsenumber(TRex *exp)
3137 {
3138 	int ret = *exp->_p-'0';
3139 	int positions = 10;
3140 	exp->_p++;
3141 	while(isdigit((int) *exp->_p)) {
3142 		ret = ret*10+(*exp->_p++-'0');
3143 		if(positions==1000000000) trex_error(exp,_SC("overflow in numeric constant"));
3144 		positions *= 10;
3145 	};
3146 	return ret;
3147 }
3148 
trex_element(TRex * exp)3149 static int trex_element(TRex *exp)
3150 {
3151 	int ret = -1;
3152 	switch(*exp->_p)
3153 	{
3154 	case '(': {
3155 		int expr,newn;
3156 		exp->_p++;
3157 
3158 
3159 		if(*exp->_p =='?') {
3160 			exp->_p++;
3161 			trex_expect(exp,':');
3162 			expr = trex_newnode(exp,OP_NOCAPEXPR);
3163 		}
3164 		else
3165 			expr = trex_newnode(exp,OP_EXPR);
3166 		newn = trex_list(exp);
3167 		exp->_nodes[expr].left = newn;
3168 		ret = expr;
3169 		trex_expect(exp,')');
3170 			  }
3171 			  break;
3172 	case '[':
3173 		exp->_p++;
3174 		ret = trex_class(exp);
3175 		trex_expect(exp,']');
3176 		break;
3177 	case TREX_SYMBOL_END_OF_STRING: exp->_p++; ret = trex_newnode(exp,OP_EOL);break;
3178 	case TREX_SYMBOL_ANY_CHAR: exp->_p++; ret = trex_newnode(exp,OP_DOT);break;
3179 	default:
3180 		ret = trex_charnode(exp,TRex_False);
3181 		break;
3182 	}
3183 
3184 	{
3185 		TRexBool isgreedy = TRex_False;
3186 		unsigned short p0 = 0, p1 = 0;
3187 		switch(*exp->_p){
3188 			case TREX_SYMBOL_GREEDY_ZERO_OR_MORE: p0 = 0; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
3189 			case TREX_SYMBOL_GREEDY_ONE_OR_MORE: p0 = 1; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
3190 			case TREX_SYMBOL_GREEDY_ZERO_OR_ONE: p0 = 0; p1 = 1; exp->_p++; isgreedy = TRex_True; break;
3191 			case '{':
3192 				exp->_p++;
3193 				if(!isdigit((int) *exp->_p)) trex_error(exp,_SC("number expected"));
3194 				p0 = (unsigned short)trex_parsenumber(exp);
3195 				/*******************************/
3196 				switch(*exp->_p) {
3197 			case '}':
3198 				p1 = p0; exp->_p++;
3199 				break;
3200 			case ',':
3201 				exp->_p++;
3202 				p1 = 0xFFFF;
3203 				if(isdigit((int) *exp->_p)){
3204 					p1 = (unsigned short)trex_parsenumber(exp);
3205 				}
3206 				trex_expect(exp,'}');
3207 				break;
3208 			default:
3209 				trex_error(exp,_SC(", or } expected"));
3210 		}
3211 		/*******************************/
3212 		isgreedy = TRex_True;
3213 		break;
3214 
3215 		}
3216 		if(isgreedy) {
3217 			int nnode = trex_newnode(exp,OP_GREEDY);
3218 			exp->_nodes[nnode].left = ret;
3219 			exp->_nodes[nnode].right = ((p0)<<16)|p1;
3220 			ret = nnode;
3221 		}
3222 	}
3223 	if((*exp->_p != TREX_SYMBOL_BRANCH) && (*exp->_p != ')') && (*exp->_p != TREX_SYMBOL_GREEDY_ZERO_OR_MORE) && (*exp->_p != TREX_SYMBOL_GREEDY_ONE_OR_MORE) && (*exp->_p != '\0')) {
3224 		int nnode = trex_element(exp);
3225 		exp->_nodes[ret].next = nnode;
3226 	}
3227 
3228 	return ret;
3229 }
3230 
trex_list(TRex * exp)3231 static int trex_list(TRex *exp)
3232 {
3233 	int ret=-1,e;
3234 	if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING) {
3235 		exp->_p++;
3236 		ret = trex_newnode(exp,OP_BOL);
3237 	}
3238 	e = trex_element(exp);
3239 	if(ret != -1) {
3240 		exp->_nodes[ret].next = e;
3241 	}
3242 	else ret = e;
3243 
3244 	if(*exp->_p == TREX_SYMBOL_BRANCH) {
3245 		int temp,tright;
3246 		exp->_p++;
3247 		temp = trex_newnode(exp,OP_OR);
3248 		exp->_nodes[temp].left = ret;
3249 		tright = trex_list(exp);
3250 		exp->_nodes[temp].right = tright;
3251 		ret = temp;
3252 	}
3253 	return ret;
3254 }
3255 
trex_matchcclass(int cclass,TRexChar ch)3256 static TRexBool trex_matchcclass(int cclass,TRexChar ch)
3257 {
3258     int c = ch;
3259 	switch(cclass) {
3260 	case 'a': return isalpha(c)?TRex_True:TRex_False;
3261 	case 'A': return !isalpha(c)?TRex_True:TRex_False;
3262 	case 'w': return (isalnum(c) || c == '_')?TRex_True:TRex_False;
3263 	case 'W': return (!isalnum(c) && c != '_')?TRex_True:TRex_False;
3264 	case 's': return isspace(c)?TRex_True:TRex_False;
3265 	case 'S': return !isspace(c)?TRex_True:TRex_False;
3266 	case 'd': return isdigit(c)?TRex_True:TRex_False;
3267 	case 'D': return !isdigit(c)?TRex_True:TRex_False;
3268 	case 'x': return isxdigit(c)?TRex_True:TRex_False;
3269 	case 'X': return !isxdigit(c)?TRex_True:TRex_False;
3270 	case 'c': return iscntrl(c)?TRex_True:TRex_False;
3271 	case 'C': return !iscntrl(c)?TRex_True:TRex_False;
3272 	case 'p': return ispunct(c)?TRex_True:TRex_False;
3273 	case 'P': return !ispunct(c)?TRex_True:TRex_False;
3274 	case 'l': return islower(c)?TRex_True:TRex_False;
3275 	case 'u': return isupper(c)?TRex_True:TRex_False;
3276 	}
3277 	return TRex_False; /*cannot happen*/
3278 }
3279 
trex_matchclass(TRex * exp,TRexNode * node,TRexChar c)3280 static TRexBool trex_matchclass(TRex* exp,TRexNode *node,TRexChar c)
3281 {
3282 	do {
3283 		switch(node->type) {
3284 			case OP_RANGE:
3285 				if (exp->_flags & TREX_ICASE)
3286 				{
3287 					if(c >= toupper(node->left) && c <= toupper(node->right)) return TRex_True;
3288 					if(c >= tolower(node->left) && c <= tolower(node->right)) return TRex_True;
3289 				}
3290 				else
3291 				{
3292 					if(c >= node->left && c <= node->right) return TRex_True;
3293 				}
3294 				break;
3295 			case OP_CCLASS:
3296 				if(trex_matchcclass(node->left,c)) return TRex_True;
3297 				break;
3298 			default:
3299 				if (exp->_flags & TREX_ICASE)
3300 				{
3301 					if (c == tolower(node->type) || c == toupper(node->type)) return TRex_True;
3302 				}
3303 				else
3304 				{
3305 					if(c == node->type)return TRex_True;
3306 				}
3307 
3308 		}
3309 	} while((node->next != -1) && (node = &exp->_nodes[node->next]));
3310 	return TRex_False;
3311 }
3312 
trex_matchnode(TRex * exp,TRexNode * node,const TRexChar * str,TRexNode * next)3313 static const TRexChar *trex_matchnode(TRex* exp,TRexNode *node,const TRexChar *str,TRexNode *next)
3314 {
3315 
3316 	TRexNodeType type = node->type;
3317 	switch(type) {
3318 	case OP_GREEDY: {
3319 		//TRexNode *greedystop = (node->next != -1) ? &exp->_nodes[node->next] : NULL;
3320 		TRexNode *greedystop = NULL;
3321 		int p0 = (node->right >> 16)&0x0000FFFF, p1 = node->right&0x0000FFFF, nmaches = 0;
3322 		const TRexChar *s=str, *good = str;
3323 
3324 		if(node->next != -1) {
3325 			greedystop = &exp->_nodes[node->next];
3326 		}
3327 		else {
3328 			greedystop = next;
3329 		}
3330 
3331 		while((nmaches == 0xFFFF || nmaches < p1)) {
3332 
3333 			const TRexChar *stop;
3334 			if(!(s = trex_matchnode(exp,&exp->_nodes[node->left],s,greedystop)))
3335 				break;
3336 			nmaches++;
3337 			good=s;
3338 			if(greedystop) {
3339 				//checks that 0 matches satisfy the expression(if so skips)
3340 				//if not would always stop(for instance if is a '?')
3341 				if(greedystop->type != OP_GREEDY ||
3342 				(greedystop->type == OP_GREEDY && ((greedystop->right >> 16)&0x0000FFFF) != 0))
3343 				{
3344 					TRexNode *gnext = NULL;
3345 					if(greedystop->next != -1) {
3346 						gnext = &exp->_nodes[greedystop->next];
3347 					}else if(next && next->next != -1){
3348 						gnext = &exp->_nodes[next->next];
3349 					}
3350 					stop = trex_matchnode(exp,greedystop,s,gnext);
3351 					if(stop) {
3352 						//if satisfied stop it
3353 						if(p0 == p1 && p0 == nmaches) break;
3354 						else if(nmaches >= p0 && p1 == 0xFFFF) break;
3355 						else if(nmaches >= p0 && nmaches <= p1) break;
3356 					}
3357 				}
3358 			}
3359 
3360 			if(s >= exp->_eol)
3361 				break;
3362 		}
3363 		if(p0 == p1 && p0 == nmaches) return good;
3364 		else if(nmaches >= p0 && p1 == 0xFFFF) return good;
3365 		else if(nmaches >= p0 && nmaches <= p1) return good;
3366 		return NULL;
3367 	}
3368 	case OP_OR: {
3369 			const TRexChar *asd = str;
3370 			TRexNode *temp=&exp->_nodes[node->left];
3371 			while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
3372 				if(temp->next != -1)
3373 					temp = &exp->_nodes[temp->next];
3374 				else
3375 					return asd;
3376 			}
3377 			asd = str;
3378 			temp = &exp->_nodes[node->right];
3379 			while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
3380 				if(temp->next != -1)
3381 					temp = &exp->_nodes[temp->next];
3382 				else
3383 					return asd;
3384 			}
3385 			return NULL;
3386 			break;
3387 	}
3388 	case OP_EXPR:
3389 	case OP_NOCAPEXPR:{
3390 			TRexNode *n = &exp->_nodes[node->left];
3391 			const TRexChar *cur = str;
3392 			int capture = -1;
3393 			if(node->type != OP_NOCAPEXPR && node->right == exp->_currsubexp) {
3394 				capture = exp->_currsubexp;
3395 				exp->_matches[capture].begin = cur;
3396 				exp->_currsubexp++;
3397 			}
3398 
3399 			do {
3400 				TRexNode *subnext = NULL;
3401 				if(n->next != -1) {
3402 					subnext = &exp->_nodes[n->next];
3403 				}else {
3404 					subnext = next;
3405 				}
3406 				if(!(cur = trex_matchnode(exp,n,cur,subnext))) {
3407 					if(capture != -1){
3408 						exp->_matches[capture].begin = 0;
3409 						exp->_matches[capture].len = 0;
3410 					}
3411 					return NULL;
3412 				}
3413 			} while((n->next != -1) && (n = &exp->_nodes[n->next]));
3414 
3415 			if(capture != -1)
3416 				exp->_matches[capture].len = cur - exp->_matches[capture].begin;
3417 			return cur;
3418 	}
3419 	case OP_WB:
3420 		if((str == exp->_bol && !isspace((int) *str))
3421 		 || ((str == exp->_eol && !isspace((int) *(str-1))))
3422 		 || ((!isspace((int) *str) && isspace((int) *(str+1))))
3423 		 || ((isspace((int) *str) && !isspace((int) *(str+1)))) ) {
3424 			return (node->left == 'b')?str:NULL;
3425 		}
3426 		return (node->left == 'b')?NULL:str;
3427 	case OP_BOL:
3428 		if(str == exp->_bol) return str;
3429 		return NULL;
3430 	case OP_EOL:
3431 		if(str == exp->_eol) return str;
3432 		return NULL;
3433 	case OP_DOT:
3434 		str++;
3435 		return str;
3436 	case OP_NCLASS:
3437 	case OP_CLASS:
3438 		if(trex_matchclass(exp,&exp->_nodes[node->left],*str)?(type == OP_CLASS?TRex_True:TRex_False):(type == OP_NCLASS?TRex_True:TRex_False)) {
3439                         str++;
3440 			return str;
3441 		}
3442 		return NULL;
3443 	case OP_CCLASS:
3444 		if(trex_matchcclass(node->left,*str)) {
3445                         str++;
3446 			return str;
3447 		}
3448 		return NULL;
3449 	default: /* char */
3450 		if (exp->_flags & TREX_ICASE)
3451 		{
3452 			if(*str != tolower(node->type) && *str != toupper(node->type)) return NULL;
3453 		}
3454 		else
3455 		{
3456 			if (*str != node->type) return NULL;
3457 		}
3458 		str++;
3459 		return str;
3460 	}
3461 	return NULL;
3462 }
3463 
3464 /* public api */
trex_compile(const TRexChar * pattern,const TRexChar ** error,int flags)3465 TRex *trex_compile(const TRexChar *pattern,const TRexChar **error,int flags)
3466 {
3467 	TRex *exp = (TRex *)malloc(sizeof(TRex));
3468 	exp->_eol = exp->_bol = NULL;
3469 	exp->_p = pattern;
3470 	exp->_nallocated = (int)scstrlen(pattern) * sizeof(TRexChar);
3471 	exp->_nodes = (TRexNode *)malloc(exp->_nallocated * sizeof(TRexNode));
3472 	exp->_nsize = 0;
3473 	exp->_matches = 0;
3474 	exp->_nsubexpr = 0;
3475 	exp->_first = trex_newnode(exp,OP_EXPR);
3476 	exp->_error = error;
3477 	exp->_jmpbuf = malloc(sizeof(jmp_buf));
3478 	exp->_flags = flags;
3479 	if(setjmp(*((jmp_buf*)exp->_jmpbuf)) == 0) {
3480 		int res = trex_list(exp);
3481 		exp->_nodes[exp->_first].left = res;
3482 		if(*exp->_p!='\0')
3483 			trex_error(exp,_SC("unexpected character"));
3484 #ifdef _DEBUG
3485 		{
3486 			int nsize,i;
3487 			TRexNode *t;
3488 			nsize = exp->_nsize;
3489 			t = &exp->_nodes[0];
3490 			scprintf(_SC("\n"));
3491 			for(i = 0;i < nsize; i++) {
3492 				if(exp->_nodes[i].type>MAX_CHAR)
3493 					scprintf(_SC("[%02d] %10s "),i,g_nnames[exp->_nodes[i].type-MAX_CHAR]);
3494 				else
3495 					scprintf(_SC("[%02d] %10c "),i,exp->_nodes[i].type);
3496 				scprintf(_SC("left %02d right %02d next %02d\n"),exp->_nodes[i].left,exp->_nodes[i].right,exp->_nodes[i].next);
3497 			}
3498 			scprintf(_SC("\n"));
3499 		}
3500 #endif
3501 		exp->_matches = (TRexMatch *) malloc(exp->_nsubexpr * sizeof(TRexMatch));
3502 		memset(exp->_matches,0,exp->_nsubexpr * sizeof(TRexMatch));
3503 	}
3504 	else{
3505 		trex_free(exp);
3506 		return NULL;
3507 	}
3508 	return exp;
3509 }
3510 
trex_free(TRex * exp)3511 void trex_free(TRex *exp)
3512 {
3513 	if(exp)	{
3514 		if(exp->_nodes) free(exp->_nodes);
3515 		if(exp->_jmpbuf) free(exp->_jmpbuf);
3516 		if(exp->_matches) free(exp->_matches);
3517 		free(exp);
3518 	}
3519 }
3520 
trex_match(TRex * exp,const TRexChar * text)3521 TRexBool trex_match(TRex* exp,const TRexChar* text)
3522 {
3523 	const TRexChar* res = NULL;
3524 	exp->_bol = text;
3525 	exp->_eol = text + scstrlen(text);
3526 	exp->_currsubexp = 0;
3527 	res = trex_matchnode(exp,exp->_nodes,text,NULL);
3528 	if(res == NULL || res != exp->_eol)
3529 		return TRex_False;
3530 	return TRex_True;
3531 }
3532 
trex_searchrange(TRex * exp,const TRexChar * text_begin,const TRexChar * text_end,const TRexChar ** out_begin,const TRexChar ** out_end)3533 TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end)
3534 {
3535 	const TRexChar *cur = NULL;
3536 	int node = exp->_first;
3537 	if(text_begin >= text_end) return TRex_False;
3538 	exp->_bol = text_begin;
3539 	exp->_eol = text_end;
3540 	do {
3541 		cur = text_begin;
3542 		while(node != -1) {
3543 			exp->_currsubexp = 0;
3544 			cur = trex_matchnode(exp,&exp->_nodes[node],cur,NULL);
3545 			if(!cur)
3546 				break;
3547 			node = exp->_nodes[node].next;
3548 		}
3549 		text_begin++;
3550 	} while(cur == NULL && text_begin != text_end);
3551 
3552 	if(cur == NULL)
3553 		return TRex_False;
3554 
3555 	--text_begin;
3556 
3557 	if(out_begin) *out_begin = text_begin;
3558 	if(out_end) *out_end = cur;
3559 	return TRex_True;
3560 }
3561 
trex_search(TRex * exp,const TRexChar * text,const TRexChar ** out_begin,const TRexChar ** out_end)3562 TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end)
3563 {
3564 	return trex_searchrange(exp,text,text + scstrlen(text),out_begin,out_end);
3565 }
3566 
trex_getsubexpcount(TRex * exp)3567 int trex_getsubexpcount(TRex* exp)
3568 {
3569 	return exp->_nsubexpr;
3570 }
3571 
trex_getsubexp(TRex * exp,int n,TRexMatch * subexp)3572 TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp)
3573 {
3574 	if( n<0 || n >= exp->_nsubexpr) return TRex_False;
3575 	*subexp = exp->_matches[n];
3576 	return TRex_True;
3577 }
3578 /*******************************************************************************
3579  * This file is part of the argtable3 library.
3580  *
3581  * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann
3582  * <sheitmann@users.sourceforge.net>
3583  * All rights reserved.
3584  *
3585  * Redistribution and use in source and binary forms, with or without
3586  * modification, are permitted provided that the following conditions are met:
3587  *     * Redistributions of source code must retain the above copyright
3588  *       notice, this list of conditions and the following disclaimer.
3589  *     * Redistributions in binary form must reproduce the above copyright
3590  *       notice, this list of conditions and the following disclaimer in the
3591  *       documentation and/or other materials provided with the distribution.
3592  *     * Neither the name of STEWART HEITMANN nor the  names of its contributors
3593  *       may be used to endorse or promote products derived from this software
3594  *       without specific prior written permission.
3595  *
3596  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
3597  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
3598  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
3599  * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT,
3600  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
3601  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
3602  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
3603  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3604  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
3605  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3606  ******************************************************************************/
3607 
3608 #include <stdlib.h>
3609 
3610 #include "argtable3.h"
3611 
3612 
arg_str_resetfn(struct arg_str * parent)3613 static void arg_str_resetfn(struct arg_str *parent)
3614 {
3615     ARG_TRACE(("%s:resetfn(%p)\n", __FILE__, parent));
3616     parent->count = 0;
3617 }
3618 
3619 
arg_str_scanfn(struct arg_str * parent,const char * argval)3620 static int arg_str_scanfn(struct arg_str *parent, const char *argval)
3621 {
3622     int errorcode = 0;
3623 
3624     if (parent->count == parent->hdr.maxcount)
3625     {
3626         /* maximum number of arguments exceeded */
3627         errorcode = EMAXCOUNT;
3628     }
3629     else if (!argval)
3630     {
3631         /* a valid argument with no argument value was given. */
3632         /* This happens when an optional argument value was invoked. */
3633         /* leave parent arguiment value unaltered but still count the argument. */
3634         parent->count++;
3635     }
3636     else
3637     {
3638         parent->sval[parent->count++] = argval;
3639     }
3640 
3641     ARG_TRACE(("%s:scanfn(%p) returns %d\n", __FILE__, parent, errorcode));
3642     return errorcode;
3643 }
3644 
3645 
arg_str_checkfn(struct arg_str * parent)3646 static int arg_str_checkfn(struct arg_str *parent)
3647 {
3648     int errorcode = (parent->count < parent->hdr.mincount) ? EMINCOUNT : 0;
3649 
3650     ARG_TRACE(("%s:checkfn(%p) returns %d\n", __FILE__, parent, errorcode));
3651     return errorcode;
3652 }
3653 
3654 
arg_str_errorfn(struct arg_str * parent,FILE * fp,int errorcode,const char * argval,const char * progname)3655 static void arg_str_errorfn(
3656     struct arg_str *parent,
3657     FILE *fp,
3658     int errorcode,
3659     const char *argval,
3660     const char *progname)
3661 {
3662     const char *shortopts = parent->hdr.shortopts;
3663     const char *longopts  = parent->hdr.longopts;
3664     const char *datatype  = parent->hdr.datatype;
3665 
3666     /* make argval NULL safe */
3667     argval = argval ? argval : "";
3668 
3669     fprintf(fp, "%s: ", progname);
3670     switch(errorcode)
3671     {
3672     case EMINCOUNT:
3673         fputs("missing option ", fp);
3674         arg_print_option(fp, shortopts, longopts, datatype, "\n");
3675         break;
3676 
3677     case EMAXCOUNT:
3678         fputs("excess option ", fp);
3679         arg_print_option(fp, shortopts, longopts, argval, "\n");
3680         break;
3681     }
3682 }
3683 
3684 
arg_str0(const char * shortopts,const char * longopts,const char * datatype,const char * glossary)3685 struct arg_str * arg_str0(
3686     const char *shortopts,
3687     const char *longopts,
3688     const char *datatype,
3689     const char *glossary)
3690 {
3691     return arg_strn(shortopts, longopts, datatype, 0, 1, glossary);
3692 }
3693 
3694 
arg_str1(const char * shortopts,const char * longopts,const char * datatype,const char * glossary)3695 struct arg_str * arg_str1(
3696     const char *shortopts,
3697     const char *longopts,
3698     const char *datatype,
3699     const char *glossary)
3700 {
3701     return arg_strn(shortopts, longopts, datatype, 1, 1, glossary);
3702 }
3703 
3704 
arg_strn(const char * shortopts,const char * longopts,const char * datatype,int mincount,int maxcount,const char * glossary)3705 struct arg_str * arg_strn(
3706     const char *shortopts,
3707     const char *longopts,
3708     const char *datatype,
3709     int mincount,
3710     int maxcount,
3711     const char *glossary)
3712 {
3713     size_t nbytes;
3714     struct arg_str *result;
3715 
3716     /* should not allow this stupid error */
3717     /* we should return an error code warning this logic error */
3718     /* foolproof things by ensuring maxcount is not less than mincount */
3719     maxcount = (maxcount < mincount) ? mincount : maxcount;
3720 
3721     nbytes = sizeof(struct arg_str)     /* storage for struct arg_str */
3722              + maxcount * sizeof(char *); /* storage for sval[maxcount] array */
3723 
3724     result = (struct arg_str *)malloc(nbytes);
3725     if (result)
3726     {
3727         int i;
3728 
3729         /* init the arg_hdr struct */
3730         result->hdr.flag      = ARG_HASVALUE;
3731         result->hdr.shortopts = shortopts;
3732         result->hdr.longopts  = longopts;
3733         result->hdr.datatype  = datatype ? datatype : "<string>";
3734         result->hdr.glossary  = glossary;
3735         result->hdr.mincount  = mincount;
3736         result->hdr.maxcount  = maxcount;
3737         result->hdr.parent    = result;
3738         result->hdr.resetfn   = (arg_resetfn *)arg_str_resetfn;
3739         result->hdr.scanfn    = (arg_scanfn *)arg_str_scanfn;
3740         result->hdr.checkfn   = (arg_checkfn *)arg_str_checkfn;
3741         result->hdr.errorfn   = (arg_errorfn *)arg_str_errorfn;
3742 
3743         /* store the sval[maxcount] array immediately after the arg_str struct */
3744         result->sval  = (const char * *)(result + 1);
3745         result->count = 0;
3746 
3747         /* foolproof the string pointers by initialising them to reference empty strings */
3748         for (i = 0; i < maxcount; i++)
3749             result->sval[i] = "";
3750     }
3751 
3752     ARG_TRACE(("arg_strn() returns %p\n", result));
3753     return result;
3754 }
3755 /*******************************************************************************
3756  * This file is part of the argtable3 library.
3757  *
3758  * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann
3759  * <sheitmann@users.sourceforge.net>
3760  * All rights reserved.
3761  *
3762  * Redistribution and use in source and binary forms, with or without
3763  * modification, are permitted provided that the following conditions are met:
3764  *     * Redistributions of source code must retain the above copyright
3765  *       notice, this list of conditions and the following disclaimer.
3766  *     * Redistributions in binary form must reproduce the above copyright
3767  *       notice, this list of conditions and the following disclaimer in the
3768  *       documentation and/or other materials provided with the distribution.
3769  *     * Neither the name of STEWART HEITMANN nor the  names of its contributors
3770  *       may be used to endorse or promote products derived from this software
3771  *       without specific prior written permission.
3772  *
3773  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
3774  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
3775  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
3776  * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT,
3777  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
3778  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
3779  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
3780  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3781  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
3782  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3783  ******************************************************************************/
3784 
3785 #include <stdlib.h>
3786 #include <string.h>
3787 #include <stdlib.h>
3788 #include <ctype.h>
3789 
3790 #include "argtable3.h"
3791 
3792 static
arg_register_error(struct arg_end * end,void * parent,int error,const char * argval)3793 void arg_register_error(struct arg_end *end,
3794                         void *parent,
3795                         int error,
3796                         const char *argval)
3797 {
3798     /* printf("arg_register_error(%p,%p,%d,%s)\n",end,parent,error,argval); */
3799     if (end->count < end->hdr.maxcount)
3800     {
3801         end->error[end->count] = error;
3802         end->parent[end->count] = parent;
3803         end->argval[end->count] = argval;
3804         end->count++;
3805     }
3806     else
3807     {
3808         end->error[end->hdr.maxcount - 1]  = ARG_ELIMIT;
3809         end->parent[end->hdr.maxcount - 1] = end;
3810         end->argval[end->hdr.maxcount - 1] = NULL;
3811     }
3812 }
3813 
3814 
3815 /*
3816  * Return index of first table entry with a matching short option
3817  * or -1 if no match was found.
3818  */
3819 static
find_shortoption(struct arg_hdr ** table,char shortopt)3820 int find_shortoption(struct arg_hdr * *table, char shortopt)
3821 {
3822     int tabindex;
3823     for(tabindex = 0; !(table[tabindex]->flag & ARG_TERMINATOR); tabindex++)
3824     {
3825         if (table[tabindex]->shortopts &&
3826             strchr(table[tabindex]->shortopts, shortopt))
3827             return tabindex;
3828     }
3829     return -1;
3830 }
3831 
3832 
3833 struct longoptions
3834 {
3835     int getoptval;
3836     int noptions;
3837     struct option *options;
3838 };
3839 
3840 #if 0
3841 static
3842 void dump_longoptions(struct longoptions * longoptions)
3843 {
3844     int i;
3845     printf("getoptval = %d\n", longoptions->getoptval);
3846     printf("noptions  = %d\n", longoptions->noptions);
3847     for (i = 0; i < longoptions->noptions; i++)
3848     {
3849         printf("options[%d].name    = \"%s\"\n",
3850                i,
3851                longoptions->options[i].name);
3852         printf("options[%d].has_arg = %d\n", i, longoptions->options[i].has_arg);
3853         printf("options[%d].flag    = %p\n", i, longoptions->options[i].flag);
3854         printf("options[%d].val     = %d\n", i, longoptions->options[i].val);
3855     }
3856 }
3857 #endif
3858 
3859 static
alloc_longoptions(struct arg_hdr ** table)3860 struct longoptions * alloc_longoptions(struct arg_hdr * *table)
3861 {
3862     struct longoptions *result;
3863     size_t nbytes;
3864     int noptions = 1;
3865     size_t longoptlen = 0;
3866     int tabindex;
3867 
3868     /*
3869      * Determine the total number of option structs required
3870      * by counting the number of comma separated long options
3871      * in all table entries and return the count in noptions.
3872      * note: noptions starts at 1 not 0 because we getoptlong
3873      * requires a NULL option entry to terminate the option array.
3874      * While we are at it, count the number of chars required
3875      * to store private copies of all the longoption strings
3876      * and return that count in logoptlen.
3877      */
3878     tabindex = 0;
3879     do
3880     {
3881         const char *longopts = table[tabindex]->longopts;
3882         longoptlen += (longopts ? strlen(longopts) : 0) + 1;
3883         while (longopts)
3884         {
3885             noptions++;
3886             longopts = strchr(longopts + 1, ',');
3887         }
3888     } while(!(table[tabindex++]->flag & ARG_TERMINATOR));
3889     /*printf("%d long options consuming %d chars in total\n",noptions,longoptlen);*/
3890 
3891 
3892     /* allocate storage for return data structure as: */
3893     /* (struct longoptions) + (struct options)[noptions] + char[longoptlen] */
3894     nbytes = sizeof(struct longoptions)
3895              + sizeof(struct option) * noptions
3896              + longoptlen;
3897     result = (struct longoptions *)malloc(nbytes);
3898     if (result)
3899     {
3900         int option_index = 0;
3901         char *store;
3902 
3903         result->getoptval = 0;
3904         result->noptions = noptions;
3905         result->options = (struct option *)(result + 1);
3906         store = (char *)(result->options + noptions);
3907 
3908         for(tabindex = 0; !(table[tabindex]->flag & ARG_TERMINATOR); tabindex++)
3909         {
3910             const char *longopts = table[tabindex]->longopts;
3911 
3912             while(longopts && *longopts)
3913             {
3914                 char *storestart = store;
3915 
3916                 /* copy progressive longopt strings into the store */
3917                 while (*longopts != 0 && *longopts != ',')
3918                     *store++ = *longopts++;
3919                 *store++ = 0;
3920                 if (*longopts == ',')
3921                     longopts++;
3922                 /*fprintf(stderr,"storestart=\"%s\"\n",storestart);*/
3923 
3924                 result->options[option_index].name    = storestart;
3925                 result->options[option_index].flag    = &(result->getoptval);
3926                 result->options[option_index].val     = tabindex;
3927                 if (table[tabindex]->flag & ARG_HASOPTVALUE)
3928                     result->options[option_index].has_arg = 2;
3929                 else if (table[tabindex]->flag & ARG_HASVALUE)
3930                     result->options[option_index].has_arg = 1;
3931                 else
3932                     result->options[option_index].has_arg = 0;
3933 
3934                 option_index++;
3935             }
3936         }
3937         /* terminate the options array with a zero-filled entry */
3938         result->options[option_index].name    = 0;
3939         result->options[option_index].has_arg = 0;
3940         result->options[option_index].flag    = 0;
3941         result->options[option_index].val     = 0;
3942     }
3943 
3944     /*dump_longoptions(result);*/
3945     return result;
3946 }
3947 
3948 static
alloc_shortoptions(struct arg_hdr ** table)3949 char * alloc_shortoptions(struct arg_hdr * *table)
3950 {
3951     char *result;
3952     size_t len = 2;
3953     int tabindex;
3954 
3955     /* determine the total number of option chars required */
3956     for(tabindex = 0; !(table[tabindex]->flag & ARG_TERMINATOR); tabindex++)
3957     {
3958         struct arg_hdr *hdr = table[tabindex];
3959         len += 3 * (hdr->shortopts ? strlen(hdr->shortopts) : 0);
3960     }
3961 
3962     result = malloc(len);
3963     if (result)
3964     {
3965         char *res = result;
3966 
3967         /* add a leading ':' so getopt return codes distinguish    */
3968         /* unrecognised option and options missing argument values */
3969         *res++ = ':';
3970 
3971         for(tabindex = 0; !(table[tabindex]->flag & ARG_TERMINATOR); tabindex++)
3972         {
3973             struct arg_hdr *hdr = table[tabindex];
3974             const char *shortopts = hdr->shortopts;
3975             while(shortopts && *shortopts)
3976             {
3977                 *res++ = *shortopts++;
3978                 if (hdr->flag & ARG_HASVALUE)
3979                     *res++ = ':';
3980                 if (hdr->flag & ARG_HASOPTVALUE)
3981                     *res++ = ':';
3982             }
3983         }
3984         /* null terminate the string */
3985         *res = 0;
3986     }
3987 
3988     /*printf("alloc_shortoptions() returns \"%s\"\n",(result?result:"NULL"));*/
3989     return result;
3990 }
3991 
3992 
3993 /* return index of the table terminator entry */
3994 static
arg_endindex(struct arg_hdr ** table)3995 int arg_endindex(struct arg_hdr * *table)
3996 {
3997     int tabindex = 0;
3998     while (!(table[tabindex]->flag & ARG_TERMINATOR))
3999         tabindex++;
4000     return tabindex;
4001 }
4002 
4003 
4004 static
arg_parse_tagged(int argc,char ** argv,struct arg_hdr ** table,struct arg_end * endtable)4005 void arg_parse_tagged(int argc,
4006                       char * *argv,
4007                       struct arg_hdr * *table,
4008                       struct arg_end *endtable)
4009 {
4010     struct longoptions *longoptions;
4011     char *shortoptions;
4012     int copt;
4013 
4014     /*printf("arg_parse_tagged(%d,%p,%p,%p)\n",argc,argv,table,endtable);*/
4015 
4016     /* allocate short and long option arrays for the given opttable[].   */
4017     /* if the allocs fail then put an error msg in the last table entry. */
4018     longoptions  = alloc_longoptions(table);
4019     shortoptions = alloc_shortoptions(table);
4020     if (!longoptions || !shortoptions)
4021     {
4022         /* one or both memory allocs failed */
4023         arg_register_error(endtable, endtable, ARG_EMALLOC, NULL);
4024         /* free anything that was allocated (this is null safe) */
4025         free(shortoptions);
4026         free(longoptions);
4027         return;
4028     }
4029 
4030     /*dump_longoptions(longoptions);*/
4031 
4032     /* reset getopts internal option-index to zero, and disable error reporting */
4033     optind = 0;
4034     opterr = 0;
4035 
4036     /* fetch and process args using getopt_long */
4037     while( (copt =
4038                 getopt_long(argc, argv, shortoptions, longoptions->options,
4039                             NULL)) != -1)
4040     {
4041         /*
4042            printf("optarg='%s'\n",optarg);
4043            printf("optind=%d\n",optind);
4044            printf("copt=%c\n",(char)copt);
4045            printf("optopt=%c (%d)\n",optopt, (int)(optopt));
4046          */
4047         switch(copt)
4048         {
4049         case 0:
4050         {
4051             int tabindex = longoptions->getoptval;
4052             void *parent  = table[tabindex]->parent;
4053             /*printf("long option detected from argtable[%d]\n", tabindex);*/
4054             if (optarg && optarg[0] == 0 &&
4055                 (table[tabindex]->flag & ARG_HASVALUE))
4056             {
4057                 /* printf(": long option %s requires an argument\n",argv[optind-1]); */
4058                 arg_register_error(endtable, endtable, ARG_EMISSARG,
4059                                    argv[optind - 1]);
4060                 /* continue to scan the (empty) argument value to enforce argument count checking */
4061             }
4062             if (table[tabindex]->scanfn)
4063             {
4064                 int errorcode = table[tabindex]->scanfn(parent, optarg);
4065                 if (errorcode != 0)
4066                     arg_register_error(endtable, parent, errorcode, optarg);
4067             }
4068         }
4069         break;
4070 
4071         case '?':
4072             /*
4073              * getopt_long() found an unrecognised short option.
4074              * if it was a short option its value is in optopt
4075              * if it was a long option then optopt=0
4076              */
4077             switch (optopt)
4078             {
4079             case 0:
4080                 /*printf("?0 unrecognised long option %s\n",argv[optind-1]);*/
4081                 arg_register_error(endtable, endtable, ARG_ELONGOPT,
4082                                    argv[optind - 1]);
4083                 break;
4084             default:
4085                 /*printf("?* unrecognised short option '%c'\n",optopt);*/
4086                 arg_register_error(endtable, endtable, optopt, NULL);
4087                 break;
4088             }
4089             break;
4090 
4091         case ':':
4092             /*
4093              * getopt_long() found an option with its argument missing.
4094              */
4095             /*printf(": option %s requires an argument\n",argv[optind-1]); */
4096             arg_register_error(endtable, endtable, ARG_EMISSARG,
4097                                argv[optind - 1]);
4098             break;
4099 
4100         default:
4101         {
4102             /* getopt_long() found a valid short option */
4103             int tabindex = find_shortoption(table, (char)copt);
4104             /*printf("short option detected from argtable[%d]\n", tabindex);*/
4105             if (tabindex == -1)
4106             {
4107                 /* should never get here - but handle it just in case */
4108                 /*printf("unrecognised short option %d\n",copt);*/
4109                 arg_register_error(endtable, endtable, copt, NULL);
4110             }
4111             else
4112             {
4113                 if (table[tabindex]->scanfn)
4114                 {
4115                     void *parent  = table[tabindex]->parent;
4116                     int errorcode = table[tabindex]->scanfn(parent, optarg);
4117                     if (errorcode != 0)
4118                         arg_register_error(endtable, parent, errorcode, optarg);
4119                 }
4120             }
4121             break;
4122         }
4123         }
4124     }
4125 
4126     free(shortoptions);
4127     free(longoptions);
4128 }
4129 
4130 
4131 static
arg_parse_untagged(int argc,char ** argv,struct arg_hdr ** table,struct arg_end * endtable)4132 void arg_parse_untagged(int argc,
4133                         char * *argv,
4134                         struct arg_hdr * *table,
4135                         struct arg_end *endtable)
4136 {
4137     int tabindex = 0;
4138     int errorlast = 0;
4139     const char *optarglast = NULL;
4140     void *parentlast = NULL;
4141 
4142     /*printf("arg_parse_untagged(%d,%p,%p,%p)\n",argc,argv,table,endtable);*/
4143     while (!(table[tabindex]->flag & ARG_TERMINATOR))
4144     {
4145         void *parent;
4146         int errorcode;
4147 
4148         /* if we have exhausted our argv[optind] entries then we have finished */
4149         if (optind >= argc)
4150         {
4151             /*printf("arg_parse_untagged(): argv[] exhausted\n");*/
4152             return;
4153         }
4154 
4155         /* skip table entries with non-null long or short options (they are not untagged entries) */
4156         if (table[tabindex]->longopts || table[tabindex]->shortopts)
4157         {
4158             /*printf("arg_parse_untagged(): skipping argtable[%d] (tagged argument)\n",tabindex);*/
4159             tabindex++;
4160             continue;
4161         }
4162 
4163         /* skip table entries with NULL scanfn */
4164         if (!(table[tabindex]->scanfn))
4165         {
4166             /*printf("arg_parse_untagged(): skipping argtable[%d] (NULL scanfn)\n",tabindex);*/
4167             tabindex++;
4168             continue;
4169         }
4170 
4171         /* attempt to scan the current argv[optind] with the current     */
4172         /* table[tabindex] entry. If it succeeds then keep it, otherwise */
4173         /* try again with the next table[] entry.                        */
4174         parent = table[tabindex]->parent;
4175         errorcode = table[tabindex]->scanfn(parent, argv[optind]);
4176         if (errorcode == 0)
4177         {
4178             /* success, move onto next argv[optind] but stay with same table[tabindex] */
4179             /*printf("arg_parse_untagged(): argtable[%d] successfully matched\n",tabindex);*/
4180             optind++;
4181 
4182             /* clear the last tentative error */
4183             errorlast = 0;
4184         }
4185         else
4186         {
4187             /* failure, try same argv[optind] with next table[tabindex] entry */
4188             /*printf("arg_parse_untagged(): argtable[%d] failed match\n",tabindex);*/
4189             tabindex++;
4190 
4191             /* remember this as a tentative error we may wish to reinstate later */
4192             errorlast = errorcode;
4193             optarglast = argv[optind];
4194             parentlast = parent;
4195         }
4196 
4197     }
4198 
4199     /* if a tenative error still remains at this point then register it as a proper error */
4200     if (errorlast)
4201     {
4202         arg_register_error(endtable, parentlast, errorlast, optarglast);
4203         optind++;
4204     }
4205 
4206     /* only get here when not all argv[] entries were consumed */
4207     /* register an error for each unused argv[] entry */
4208     while (optind < argc)
4209     {
4210         /*printf("arg_parse_untagged(): argv[%d]=\"%s\" not consumed\n",optind,argv[optind]);*/
4211         arg_register_error(endtable, endtable, ARG_ENOMATCH, argv[optind++]);
4212     }
4213 
4214     return;
4215 }
4216 
4217 
4218 static
arg_parse_check(struct arg_hdr ** table,struct arg_end * endtable)4219 void arg_parse_check(struct arg_hdr * *table, struct arg_end *endtable)
4220 {
4221     int tabindex = 0;
4222     /* printf("arg_parse_check()\n"); */
4223     do
4224     {
4225         if (table[tabindex]->checkfn)
4226         {
4227             void *parent  = table[tabindex]->parent;
4228             int errorcode = table[tabindex]->checkfn(parent);
4229             if (errorcode != 0)
4230                 arg_register_error(endtable, parent, errorcode, NULL);
4231         }
4232     } while(!(table[tabindex++]->flag & ARG_TERMINATOR));
4233 }
4234 
4235 
4236 static
arg_reset(void ** argtable)4237 void arg_reset(void * *argtable)
4238 {
4239     struct arg_hdr * *table = (struct arg_hdr * *)argtable;
4240     int tabindex = 0;
4241     /*printf("arg_reset(%p)\n",argtable);*/
4242     do
4243     {
4244         if (table[tabindex]->resetfn)
4245             table[tabindex]->resetfn(table[tabindex]->parent);
4246     } while(!(table[tabindex++]->flag & ARG_TERMINATOR));
4247 }
4248 
4249 
arg_parse(int argc,char ** argv,void ** argtable)4250 int arg_parse(int argc, char * *argv, void * *argtable)
4251 {
4252     struct arg_hdr * *table = (struct arg_hdr * *)argtable;
4253     struct arg_end *endtable;
4254     int endindex;
4255     char * *argvcopy = NULL;
4256 
4257     /*printf("arg_parse(%d,%p,%p)\n",argc,argv,argtable);*/
4258 
4259     /* reset any argtable data from previous invocations */
4260     arg_reset(argtable);
4261 
4262     /* locate the first end-of-table marker within the array */
4263     endindex = arg_endindex(table);
4264     endtable = (struct arg_end *)table[endindex];
4265 
4266     /* Special case of argc==0.  This can occur on Texas Instruments DSP. */
4267     /* Failure to trap this case results in an unwanted NULL result from  */
4268     /* the malloc for argvcopy (next code block).                         */
4269     if (argc == 0)
4270     {
4271         /* We must still perform post-parse checks despite the absence of command line arguments */
4272         arg_parse_check(table, endtable);
4273 
4274         /* Now we are finished */
4275         return endtable->count;
4276     }
4277 
4278     argvcopy = (char **)malloc(sizeof(char *) * (argc + 1));
4279     if (argvcopy)
4280     {
4281         int i;
4282 
4283         /*
4284            Fill in the local copy of argv[]. We need a local copy
4285            because getopt rearranges argv[] which adversely affects
4286            susbsequent parsing attempts.
4287          */
4288         for (i = 0; i < argc; i++)
4289             argvcopy[i] = argv[i];
4290 
4291         argvcopy[argc] = NULL;
4292 
4293         /* parse the command line (local copy) for tagged options */
4294         arg_parse_tagged(argc, argvcopy, table, endtable);
4295 
4296         /* parse the command line (local copy) for untagged options */
4297         arg_parse_untagged(argc, argvcopy, table, endtable);
4298 
4299         /* if no errors so far then perform post-parse checks otherwise dont bother */
4300         if (endtable->count == 0)
4301             arg_parse_check(table, endtable);
4302 
4303         /* release the local copt of argv[] */
4304         free(argvcopy);
4305     }
4306     else
4307     {
4308         /* memory alloc failed */
4309         arg_register_error(endtable, endtable, ARG_EMALLOC, NULL);
4310     }
4311 
4312     return endtable->count;
4313 }
4314 
4315 
4316 /*
4317  * Concatenate contents of src[] string onto *pdest[] string.
4318  * The *pdest pointer is altered to point to the end of the
4319  * target string and *pndest is decremented by the same number
4320  * of chars.
4321  * Does not append more than *pndest chars into *pdest[]
4322  * so as to prevent buffer overruns.
4323  * Its something like strncat() but more efficient for repeated
4324  * calls on the same destination string.
4325  * Example of use:
4326  *   char dest[30] = "good"
4327  *   size_t ndest = sizeof(dest);
4328  *   char *pdest = dest;
4329  *   arg_char(&pdest,"bye ",&ndest);
4330  *   arg_char(&pdest,"cruel ",&ndest);
4331  *   arg_char(&pdest,"world!",&ndest);
4332  * Results in:
4333  *   dest[] == "goodbye cruel world!"
4334  *   ndest  == 10
4335  */
4336 static
arg_cat(char ** pdest,const char * src,size_t * pndest)4337 void arg_cat(char * *pdest, const char *src, size_t *pndest)
4338 {
4339     char *dest = *pdest;
4340     char *end  = dest + *pndest;
4341 
4342     /*locate null terminator of dest string */
4343     while(dest < end && *dest != 0)
4344         dest++;
4345 
4346     /* concat src string to dest string */
4347     while(dest < end && *src != 0)
4348         *dest++ = *src++;
4349 
4350     /* null terminate dest string */
4351     *dest = 0;
4352 
4353     /* update *pdest and *pndest */
4354     *pndest = end - dest;
4355     *pdest  = dest;
4356 }
4357 
4358 
4359 static
arg_cat_option(char * dest,size_t ndest,const char * shortopts,const char * longopts,const char * datatype,int optvalue)4360 void arg_cat_option(char *dest,
4361                     size_t ndest,
4362                     const char *shortopts,
4363                     const char *longopts,
4364                     const char *datatype,
4365                     int optvalue)
4366 {
4367     if (shortopts)
4368     {
4369         char option[3];
4370 
4371         /* note: option array[] is initialiazed dynamically here to satisfy   */
4372         /* a deficiency in the watcom compiler wrt static array initializers. */
4373         option[0] = '-';
4374         option[1] = shortopts[0];
4375         option[2] = 0;
4376 
4377         arg_cat(&dest, option, &ndest);
4378         if (datatype)
4379         {
4380             arg_cat(&dest, " ", &ndest);
4381             if (optvalue)
4382             {
4383                 arg_cat(&dest, "[", &ndest);
4384                 arg_cat(&dest, datatype, &ndest);
4385                 arg_cat(&dest, "]", &ndest);
4386             }
4387             else
4388                 arg_cat(&dest, datatype, &ndest);
4389         }
4390     }
4391     else if (longopts)
4392     {
4393         size_t ncspn;
4394 
4395         /* add "--" tag prefix */
4396         arg_cat(&dest, "--", &ndest);
4397 
4398         /* add comma separated option tag */
4399         ncspn = strcspn(longopts, ",");
4400         strncat(dest, longopts, (ncspn < ndest) ? ncspn : ndest);
4401 
4402         if (datatype)
4403         {
4404             arg_cat(&dest, "=", &ndest);
4405             if (optvalue)
4406             {
4407                 arg_cat(&dest, "[", &ndest);
4408                 arg_cat(&dest, datatype, &ndest);
4409                 arg_cat(&dest, "]", &ndest);
4410             }
4411             else
4412                 arg_cat(&dest, datatype, &ndest);
4413         }
4414     }
4415     else if (datatype)
4416     {
4417         if (optvalue)
4418         {
4419             arg_cat(&dest, "[", &ndest);
4420             arg_cat(&dest, datatype, &ndest);
4421             arg_cat(&dest, "]", &ndest);
4422         }
4423         else
4424             arg_cat(&dest, datatype, &ndest);
4425     }
4426 }
4427 
4428 static
arg_cat_optionv(char * dest,size_t ndest,const char * shortopts,const char * longopts,const char * datatype,int optvalue,const char * separator)4429 void arg_cat_optionv(char *dest,
4430                      size_t ndest,
4431                      const char *shortopts,
4432                      const char *longopts,
4433                      const char *datatype,
4434                      int optvalue,
4435                      const char *separator)
4436 {
4437     separator = separator ? separator : "";
4438 
4439     if (shortopts)
4440     {
4441         const char *c = shortopts;
4442         while(*c)
4443         {
4444             /* "-a|-b|-c" */
4445             char shortopt[3];
4446 
4447             /* note: shortopt array[] is initialiazed dynamically here to satisfy */
4448             /* a deficiency in the watcom compiler wrt static array initializers. */
4449             shortopt[0] = '-';
4450             shortopt[1] = *c;
4451             shortopt[2] = 0;
4452 
4453             arg_cat(&dest, shortopt, &ndest);
4454             if (*++c)
4455                 arg_cat(&dest, separator, &ndest);
4456         }
4457     }
4458 
4459     /* put separator between long opts and short opts */
4460     if (shortopts && longopts)
4461         arg_cat(&dest, separator, &ndest);
4462 
4463     if (longopts)
4464     {
4465         const char *c = longopts;
4466         while(*c)
4467         {
4468             size_t ncspn;
4469 
4470             /* add "--" tag prefix */
4471             arg_cat(&dest, "--", &ndest);
4472 
4473             /* add comma separated option tag */
4474             ncspn = strcspn(c, ",");
4475             strncat(dest, c, (ncspn < ndest) ? ncspn : ndest);
4476             c += ncspn;
4477 
4478             /* add given separator in place of comma */
4479             if (*c == ',')
4480             {
4481                 arg_cat(&dest, separator, &ndest);
4482                 c++;
4483             }
4484         }
4485     }
4486 
4487     if (datatype)
4488     {
4489         if (longopts)
4490             arg_cat(&dest, "=", &ndest);
4491         else if (shortopts)
4492             arg_cat(&dest, " ", &ndest);
4493 
4494         if (optvalue)
4495         {
4496             arg_cat(&dest, "[", &ndest);
4497             arg_cat(&dest, datatype, &ndest);
4498             arg_cat(&dest, "]", &ndest);
4499         }
4500         else
4501             arg_cat(&dest, datatype, &ndest);
4502     }
4503 }
4504 
4505 
4506 /* this function should be deprecated because it doesnt consider optional argument values (ARG_HASOPTVALUE) */
arg_print_option(FILE * fp,const char * shortopts,const char * longopts,const char * datatype,const char * suffix)4507 void arg_print_option(FILE *fp,
4508                       const char *shortopts,
4509                       const char *longopts,
4510                       const char *datatype,
4511                       const char *suffix)
4512 {
4513     char syntax[200] = "";
4514     suffix = suffix ? suffix : "";
4515 
4516     /* there is no way of passing the proper optvalue for optional argument values here, so we must ignore it */
4517     arg_cat_optionv(syntax,
4518                     sizeof(syntax),
4519                     shortopts,
4520                     longopts,
4521                     datatype,
4522                     0,
4523                     "|");
4524 
4525     fputs(syntax, fp);
4526     fputs(suffix, fp);
4527 }
4528 
4529 
4530 /*
4531  * Print a GNU style [OPTION] string in which all short options that
4532  * do not take argument values are presented in abbreviated form, as
4533  * in: -xvfsd, or -xvf[sd], or [-xvsfd]
4534  */
4535 static
arg_print_gnuswitch(FILE * fp,struct arg_hdr ** table)4536 void arg_print_gnuswitch(FILE *fp, struct arg_hdr * *table)
4537 {
4538     int tabindex;
4539     const char *format1 = " -%c";
4540     const char *format2 = " [-%c";
4541     const char *suffix = "";
4542 
4543     /* print all mandatory switches that are without argument values */
4544     for(tabindex = 0;
4545         table[tabindex] && !(table[tabindex]->flag & ARG_TERMINATOR);
4546         tabindex++)
4547     {
4548         /* skip optional options */
4549         if (table[tabindex]->mincount < 1)
4550             continue;
4551 
4552         /* skip non-short options */
4553         if (table[tabindex]->shortopts == NULL)
4554             continue;
4555 
4556         /* skip options that take argument values */
4557         if (table[tabindex]->flag & ARG_HASVALUE)
4558             continue;
4559 
4560         /* print the short option (only the first short option char, ignore multiple choices)*/
4561         fprintf(fp, format1, table[tabindex]->shortopts[0]);
4562         format1 = "%c";
4563         format2 = "[%c";
4564     }
4565 
4566     /* print all optional switches that are without argument values */
4567     for(tabindex = 0;
4568         table[tabindex] && !(table[tabindex]->flag & ARG_TERMINATOR);
4569         tabindex++)
4570     {
4571         /* skip mandatory args */
4572         if (table[tabindex]->mincount > 0)
4573             continue;
4574 
4575         /* skip args without short options */
4576         if (table[tabindex]->shortopts == NULL)
4577             continue;
4578 
4579         /* skip args with values */
4580         if (table[tabindex]->flag & ARG_HASVALUE)
4581             continue;
4582 
4583         /* print first short option */
4584         fprintf(fp, format2, table[tabindex]->shortopts[0]);
4585         format2 = "%c";
4586         suffix = "]";
4587     }
4588 
4589     fprintf(fp, "%s", suffix);
4590 }
4591 
4592 
arg_print_syntax(FILE * fp,void ** argtable,const char * suffix)4593 void arg_print_syntax(FILE *fp, void * *argtable, const char *suffix)
4594 {
4595     struct arg_hdr * *table = (struct arg_hdr * *)argtable;
4596     int i, tabindex;
4597 
4598     /* print GNU style [OPTION] string */
4599     arg_print_gnuswitch(fp, table);
4600 
4601     /* print remaining options in abbreviated style */
4602     for(tabindex = 0;
4603         table[tabindex] && !(table[tabindex]->flag & ARG_TERMINATOR);
4604         tabindex++)
4605     {
4606         char syntax[200] = "";
4607         const char *shortopts, *longopts, *datatype;
4608 
4609         /* skip short options without arg values (they were printed by arg_print_gnu_switch) */
4610         if (table[tabindex]->shortopts &&
4611             !(table[tabindex]->flag & ARG_HASVALUE))
4612             continue;
4613 
4614         shortopts = table[tabindex]->shortopts;
4615         longopts  = table[tabindex]->longopts;
4616         datatype  = table[tabindex]->datatype;
4617         arg_cat_option(syntax,
4618                        sizeof(syntax),
4619                        shortopts,
4620                        longopts,
4621                        datatype,
4622                        table[tabindex]->flag & ARG_HASOPTVALUE);
4623 
4624         if (strlen(syntax) > 0)
4625         {
4626             /* print mandatory instances of this option */
4627             for (i = 0; i < table[tabindex]->mincount; i++)
4628                 fprintf(fp, " %s", syntax);
4629 
4630             /* print optional instances enclosed in "[..]" */
4631             switch ( table[tabindex]->maxcount - table[tabindex]->mincount )
4632             {
4633             case 0:
4634                 break;
4635             case 1:
4636                 fprintf(fp, " [%s]", syntax);
4637                 break;
4638             case 2:
4639                 fprintf(fp, " [%s] [%s]", syntax, syntax);
4640                 break;
4641             default:
4642                 fprintf(fp, " [%s]...", syntax);
4643                 break;
4644             }
4645         }
4646     }
4647 
4648     if (suffix)
4649         fprintf(fp, "%s", suffix);
4650 }
4651 
4652 
arg_print_syntaxv(FILE * fp,void ** argtable,const char * suffix)4653 void arg_print_syntaxv(FILE *fp, void * *argtable, const char *suffix)
4654 {
4655     struct arg_hdr * *table = (struct arg_hdr * *)argtable;
4656     int i, tabindex;
4657 
4658     /* print remaining options in abbreviated style */
4659     for(tabindex = 0;
4660         table[tabindex] && !(table[tabindex]->flag & ARG_TERMINATOR);
4661         tabindex++)
4662     {
4663         char syntax[200] = "";
4664         const char *shortopts, *longopts, *datatype;
4665 
4666         shortopts = table[tabindex]->shortopts;
4667         longopts  = table[tabindex]->longopts;
4668         datatype  = table[tabindex]->datatype;
4669         arg_cat_optionv(syntax,
4670                         sizeof(syntax),
4671                         shortopts,
4672                         longopts,
4673                         datatype,
4674                         table[tabindex]->flag & ARG_HASOPTVALUE,
4675                         "|");
4676 
4677         /* print mandatory options */
4678         for (i = 0; i < table[tabindex]->mincount; i++)
4679             fprintf(fp, " %s", syntax);
4680 
4681         /* print optional args enclosed in "[..]" */
4682         switch ( table[tabindex]->maxcount - table[tabindex]->mincount )
4683         {
4684         case 0:
4685             break;
4686         case 1:
4687             fprintf(fp, " [%s]", syntax);
4688             break;
4689         case 2:
4690             fprintf(fp, " [%s] [%s]", syntax, syntax);
4691             break;
4692         default:
4693             fprintf(fp, " [%s]...", syntax);
4694             break;
4695         }
4696     }
4697 
4698     if (suffix)
4699         fprintf(fp, "%s", suffix);
4700 }
4701 
4702 
arg_print_glossary(FILE * fp,void ** argtable,const char * format)4703 void arg_print_glossary(FILE *fp, void * *argtable, const char *format)
4704 {
4705     struct arg_hdr * *table = (struct arg_hdr * *)argtable;
4706     int tabindex;
4707 
4708     format = format ? format : "  %-20s %s\n";
4709     for (tabindex = 0; !(table[tabindex]->flag & ARG_TERMINATOR); tabindex++)
4710     {
4711         if (table[tabindex]->glossary)
4712         {
4713             char syntax[200] = "";
4714             const char *shortopts = table[tabindex]->shortopts;
4715             const char *longopts  = table[tabindex]->longopts;
4716             const char *datatype  = table[tabindex]->datatype;
4717             const char *glossary  = table[tabindex]->glossary;
4718             arg_cat_optionv(syntax,
4719                             sizeof(syntax),
4720                             shortopts,
4721                             longopts,
4722                             datatype,
4723                             table[tabindex]->flag & ARG_HASOPTVALUE,
4724                             ", ");
4725             fprintf(fp, format, syntax, glossary);
4726         }
4727     }
4728 }
4729 
4730 
4731 /**
4732  * Print a piece of text formatted, which means in a column with a
4733  * left and a right margin. The lines are wrapped at whitspaces next
4734  * to right margin. The function does not indent the first line, but
4735  * only the following ones.
4736  *
4737  * Example:
4738  * arg_print_formatted( fp, 0, 5, "Some text that doesn't fit." )
4739  * will result in the following output:
4740  *
4741  * Some
4742  * text
4743  * that
4744  * doesn'
4745  * t fit.
4746  *
4747  * Too long lines will be wrapped in the middle of a word.
4748  *
4749  * arg_print_formatted( fp, 2, 7, "Some text that doesn't fit." )
4750  * will result in the following output:
4751  *
4752  * Some
4753  *   text
4754  *   that
4755  *   doesn'
4756  *   t fit.
4757  *
4758  * As you see, the first line is not indented. This enables output of
4759  * lines, which start in a line where output already happened.
4760  *
4761  * Author: Uli Fouquet
4762  */
arg_print_formatted(FILE * fp,const unsigned lmargin,const unsigned rmargin,const char * text)4763 void arg_print_formatted( FILE *fp,
4764                           const unsigned lmargin,
4765                           const unsigned rmargin,
4766                           const char *text )
4767 {
4768     const unsigned textlen = strlen( text );
4769     unsigned line_start = 0;
4770     unsigned line_end = textlen + 1;
4771     const unsigned colwidth = (rmargin - lmargin) + 1;
4772 
4773     /* Someone doesn't like us... */
4774     if ( line_end < line_start )
4775     { fprintf( fp, "%s\n", text ); }
4776 
4777     while (line_end - 1 > line_start )
4778     {
4779         /* Eat leading whitespaces. This is essential because while
4780            wrapping lines, there will often be a whitespace at beginning
4781            of line */
4782         while ( isspace((int) *(text + line_start)) )
4783         { line_start++; }
4784 
4785         if ((line_end - line_start) > colwidth )
4786         { line_end = line_start + colwidth; }
4787 
4788         /* Find last whitespace, that fits into line */
4789         while ( ( line_end > line_start )
4790                 && ( line_end - line_start > colwidth )
4791                 && !isspace((int) *(text + line_end)))
4792         { line_end--; }
4793 
4794         /* Do not print trailing whitespace. If this text
4795            has got only one line, line_end now points to the
4796            last char due to initialization. */
4797         line_end--;
4798 
4799         /* Output line of text */
4800         while ( line_start < line_end )
4801         {
4802             fputc(*(text + line_start), fp );
4803             line_start++;
4804         }
4805         fputc( '\n', fp );
4806 
4807         /* Initialize another line */
4808         if ( line_end + 1 < textlen )
4809         {
4810             unsigned i;
4811 
4812             for (i = 0; i < lmargin; i++ )
4813             { fputc( ' ', fp ); }
4814 
4815             line_end = textlen;
4816         }
4817 
4818         /* If we have to print another line, get also the last char. */
4819         line_end++;
4820 
4821     } /* lines of text */
4822 }
4823 
4824 /**
4825  * Prints the glossary in strict GNU format.
4826  * Differences to arg_print_glossary() are:
4827  *   - wraps lines after 80 chars
4828  *   - indents lines without shortops
4829  *   - does not accept formatstrings
4830  *
4831  * Contributed by Uli Fouquet
4832  */
arg_print_glossary_gnu(FILE * fp,void ** argtable)4833 void arg_print_glossary_gnu(FILE *fp, void * *argtable )
4834 {
4835     struct arg_hdr * *table = (struct arg_hdr * *)argtable;
4836     int tabindex;
4837 
4838     for(tabindex = 0; !(table[tabindex]->flag & ARG_TERMINATOR); tabindex++)
4839     {
4840         if (table[tabindex]->glossary)
4841         {
4842             char syntax[200] = "";
4843             const char *shortopts = table[tabindex]->shortopts;
4844             const char *longopts  = table[tabindex]->longopts;
4845             const char *datatype  = table[tabindex]->datatype;
4846             const char *glossary  = table[tabindex]->glossary;
4847 
4848             if ( !shortopts && longopts )
4849             {
4850                 /* Indent trailing line by 4 spaces... */
4851                 memset( syntax, ' ', 4 );
4852                 *(syntax + 4) = '\0';
4853             }
4854 
4855             arg_cat_optionv(syntax,
4856                             sizeof(syntax),
4857                             shortopts,
4858                             longopts,
4859                             datatype,
4860                             table[tabindex]->flag & ARG_HASOPTVALUE,
4861                             ", ");
4862 
4863             /* If syntax fits not into column, print glossary in new line... */
4864             if ( strlen(syntax) > 25 )
4865             {
4866                 fprintf( fp, "  %-25s %s\n", syntax, "" );
4867                 *syntax = '\0';
4868             }
4869 
4870             fprintf( fp, "  %-25s ", syntax );
4871             arg_print_formatted( fp, 28, 79, glossary );
4872         }
4873     } /* for each table entry */
4874 
4875     fputc( '\n', fp );
4876 }
4877 
4878 
4879 /**
4880  * Checks the argtable[] array for NULL entries and returns 1
4881  * if any are found, zero otherwise.
4882  */
arg_nullcheck(void ** argtable)4883 int arg_nullcheck(void * *argtable)
4884 {
4885     struct arg_hdr * *table = (struct arg_hdr * *)argtable;
4886     int tabindex;
4887     /*printf("arg_nullcheck(%p)\n",argtable);*/
4888 
4889     if (!table)
4890         return 1;
4891 
4892     tabindex = 0;
4893     do
4894     {
4895         /*printf("argtable[%d]=%p\n",tabindex,argtable[tabindex]);*/
4896         if (!table[tabindex])
4897             return 1;
4898     } while(!(table[tabindex++]->flag & ARG_TERMINATOR));
4899 
4900     return 0;
4901 }
4902 
4903 
4904 /*
4905  * arg_free() is deprecated in favour of arg_freetable() due to a flaw in its design.
4906  * The flaw results in memory leak in the (very rare) case that an intermediate
4907  * entry in the argtable array failed its memory allocation while others following
4908  * that entry were still allocated ok. Those subsequent allocations will not be
4909  * deallocated by arg_free().
4910  * Despite the unlikeliness of the problem occurring, and the even unlikelier event
4911  * that it has any deliterious effect, it is fixed regardless by replacing arg_free()
4912  * with the newer arg_freetable() function.
4913  * We still keep arg_free() for backwards compatibility.
4914  */
arg_free(void ** argtable)4915 void arg_free(void * *argtable)
4916 {
4917     struct arg_hdr * *table = (struct arg_hdr * *)argtable;
4918     int tabindex = 0;
4919     int flag;
4920     /*printf("arg_free(%p)\n",argtable);*/
4921     do
4922     {
4923         /*
4924            if we encounter a NULL entry then somewhat incorrectly we presume
4925            we have come to the end of the array. It isnt strictly true because
4926            an intermediate entry could be NULL with other non-NULL entries to follow.
4927            The subsequent argtable entries would then not be freed as they should.
4928          */
4929         if (table[tabindex] == NULL)
4930             break;
4931 
4932         flag = table[tabindex]->flag;
4933         free(table[tabindex]);
4934         table[tabindex++] = NULL;
4935 
4936     } while(!(flag & ARG_TERMINATOR));
4937 }
4938 
4939 /* frees each non-NULL element of argtable[], where n is the size of the number of entries in the array */
arg_freetable(void ** argtable,size_t n)4940 void arg_freetable(void * *argtable, size_t n)
4941 {
4942     struct arg_hdr * *table = (struct arg_hdr * *)argtable;
4943     size_t tabindex = 0;
4944     /*printf("arg_freetable(%p)\n",argtable);*/
4945     for (tabindex = 0; tabindex < n; tabindex++)
4946     {
4947         if (table[tabindex] == NULL)
4948             continue;
4949 
4950         free(table[tabindex]);
4951         table[tabindex] = NULL;
4952     };
4953 }
4954