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