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