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