]> cvs.zerfleddert.de Git - proxmark3-svn/blame_incremental - client/cliparser/argtable3.c
Merge pull request #969 from pwpiwi/gcc10_fixes
[proxmark3-svn] / client / cliparser / argtable3.c
... / ...
CommitLineData
1/*******************************************************************************
2 * This file is part of the argtable3 library.
3 *
4 * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann
5 * <sheitmann@users.sourceforge.net>
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are met:
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * * Neither the name of STEWART HEITMANN nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT,
23 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 ******************************************************************************/
30
31#include "argtable3.h"
32
33#define ARG_AMALGAMATION
34
35/*******************************************************************************
36 * argtable3_private: Declares private types, constants, and interfaces
37 *
38 * This file is part of the argtable3 library.
39 *
40 * Copyright (C) 2013-2019 Tom G. Huang
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
70#include <stdlib.h>
71
72#define ARG_ENABLE_TRACE 0
73#define ARG_ENABLE_LOG 1
74
75#ifdef __cplusplus
76extern "C" {
77#endif
78
79enum { ARG_ERR_MINCOUNT = 1, ARG_ERR_MAXCOUNT, ARG_ERR_BADINT, ARG_ERR_OVERFLOW, ARG_ERR_BADDOUBLE, ARG_ERR_BADDATE, ARG_ERR_REGNOMATCH };
80
81typedef void(arg_panicfn)(const char* fmt, ...);
82
83#if defined(_MSC_VER)
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) \
90 __pragma(warning(pop))
91
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) \
98 __pragma(warning(pop))
99#else
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);
185
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);
256
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)); }
259
260#ifdef __cplusplus
261}
262#endif
263
264#endif
265/*******************************************************************************
266 * arg_utils: Implements memory, panic, and other utility functions
267 *
268 * This file is part of the argtable3 library.
269 *
270 * Copyright (C) 2013-2019 Tom G. Huang
271 * <tomghuang@gmail.com>
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
297#include "argtable3.h"
298
299#ifndef ARG_AMALGAMATION
300#include "argtable3_private.h"
301#endif
302
303#include <stdarg.h>
304#include <stdio.h>
305#include <stdlib.h>
306#include <string.h>
307
308static void panic(const char* fmt, ...);
309static arg_panicfn* s_panic = panic;
310
311void dbg_printf(const char* fmt, ...) {
312 va_list args;
313 va_start(args, fmt);
314 vfprintf(stderr, fmt, args);
315 va_end(args);
316}
317
318static void panic(const char* fmt, ...) {
319 va_list args;
320 char* s;
321
322 va_start(args, fmt);
323 vfprintf(stderr, fmt, args);
324 va_end(args);
325
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
334
335 if (s != NULL && *s != '\0') {
336 abort();
337 } else {
338 exit(EXIT_FAILURE);
339 }
340}
341
342void arg_set_panic(arg_panicfn* proc) {
343 s_panic = proc;
344}
345
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}
353
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}
363
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}
372
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
445 *
446 * This file is part of the argtable3 library.
447 *
448 * Copyright (C) 2013-2019 Tom G. Huang
449 * <tomghuang@gmail.com>
450 * All rights reserved.
451 *
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
478
479#include <math.h>
480#include <stdio.h>
481#include <stdlib.h>
482#include <string.h>
483
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:
487 *
488 * Copyright (c) 2002, Christopher Clark
489 * All rights reserved.
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:
494 *
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.
518 */
519
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"
902#endif
903
904#include <stdarg.h>
905#include <stdlib.h>
906#include <string.h>
907
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>
1355
1356#define GNU_COMPATIBLE /* Be more compatible, configure's use us! */
1357
1358int opterr = 1; /* if error message should be printed */
1359int optind = 1; /* index into parent argv vector */
1360int optopt = '?'; /* character checked for validity */
1361int optreset; /* reset getopt */
1362char *optarg; /* argument associated with option */
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
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
1384static int getopt_internal(int, char * const *, const char *,
1385 const struct option *, int *, int);
1386static int parse_long_options(char * const *, const char *,
1387 const struct option *, int *, int, int);
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";
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
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";
1412static const char illoptstring[] = "unknown option -- %s";
1413#endif
1414
1415#ifdef _WIN32
1416
1417/*
1418 * Windows needs warnx(). We change the definition though:
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
1425#include <stdarg.h>
1426#include <stdio.h>
1427
1428#define MAX_OPTERRMSG_SIZE 128
1429
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);
1436
1437 /*
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);
1445#else
1446 _vsnprintf(opterrmsg, sizeof(opterrmsg) - 1, fmt, ap);
1447#endif
1448
1449 va_end(ap);
1450
1451#ifdef _MSC_VER
1452#pragma warning(suppress : 6053)
1453#endif
1454 fprintf(stderr, "%s\n", opterrmsg);
1455}
1456
1457#else
1458#include <err.h>
1459#endif /*_WIN32*/
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,
1522 const struct option *long_options, int *idx, int short_too, int flags)
1523{
1524 char *current_argv, *has_equal;
1525#ifdef GNU_COMPATIBLE
1526 char *current_dash;
1527#endif
1528 size_t current_argv_len;
1529 int i, match, exact_match, second_partial_match;
1530
1531 current_argv = place;
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
1548 match = -1;
1549 exact_match = 0;
1550 second_partial_match = 0;
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;
1570 exact_match = 1;
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
1580 if (match == -1) /* first partial match */
1581 match = i;
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);
1600 }
1601 if (match != -1) { /* option found */
1602 if (long_options[match].has_arg == no_argument
1603 && has_equal) {
1604 if (PRINT_ERROR)
1605 warnx(noarg,
1606#ifdef GNU_COMPATIBLE
1607 current_dash,
1608#endif
1609 (int)current_argv_len,
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;
1618#ifdef GNU_COMPATIBLE
1619 return (BADCH);
1620#else
1621 return (BADARG);
1622#endif
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,
1644#ifdef GNU_COMPATIBLE
1645 current_dash,
1646#endif
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)
1664 warnx(illoptstring,
1665#ifdef GNU_COMPATIBLE
1666 current_dash,
1667#endif
1668 current_argv);
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;
1692
1693 if (options == NULL)
1694 return (-1);
1695
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
1703 /*
1704 * Disable GNU extensions if POSIXLY_CORRECT is set or options
1705 * string begins with a '+'.
1706 */
1707 if (posixly_correct == -1 || optreset) {
1708#if (defined(__STDC_LIB_EXT1__) && defined(__STDC_WANT_LIB_EXT1__)) || (defined(__STDC_SECURE_LIB__) && defined(__STDC_WANT_SECURE_LIB__))
1709 size_t requiredSize;
1710 getenv_s(&requiredSize, NULL, 0, "POSIXLY_CORRECT");
1711 posixly_correct = requiredSize != 0;
1712#else
1713 posixly_correct = (getenv("POSIXLY_CORRECT") != NULL);
1714#endif
1715 }
1716
1717 if (*options == '-')
1718 flags |= FLAG_ALLARGS;
1719 else if (posixly_correct || *options == '+')
1720 flags &= ~FLAG_PERMUTE;
1721 if (*options == '+' || *options == '-')
1722 options++;
1723
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]) != '-' ||
1749#ifdef GNU_COMPATIBLE
1750 place[1] == '\0') {
1751#else
1752 (place[1] == '\0' && strchr(options, '-') == NULL)) {
1753#endif
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;
1816#ifdef GNU_COMPATIBLE
1817 dash_prefix = D_PREFIX;
1818#endif
1819 if (*place == '-') {
1820 place++; /* --foo long option */
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)
1827 short_too = 1; /* could be short option too */
1828
1829 optchar = parse_long_options(nargv, options, long_options,
1830 idx, short_too, flags);
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;
1849#ifdef GNU_COMPATIBLE
1850 if (PRINT_ERROR)
1851 warnx(posixly_correct ? illoptchar : gnuoptchar,
1852 optchar);
1853#else
1854 if (PRINT_ERROR)
1855 warnx(illoptchar, optchar);
1856#endif
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];
1872#ifdef GNU_COMPATIBLE
1873 dash_prefix = W_PREFIX;
1874#endif
1875 optchar = parse_long_options(nargv, options, long_options,
1876 idx, 0, flags);
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
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}
1924
1925/*
1926 * getopt_long --
1927 * Parse argc/argv argument vector.
1928 */
1929int
1930getopt_long(int nargc, char * const *nargv, const char *options,
1931 const struct option *long_options, int *idx)
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,
1944 const struct option *long_options, int *idx)
1945{
1946
1947 return (getopt_internal(nargc, nargv, options, long_options, idx,
1948 FLAG_PERMUTE|FLAG_LONGONLY));
1949}
1950
1951#endif /* ARG_REPLACE_GETOPT == 1 */
1952/*******************************************************************************
1953 * arg_date: Implements the date command-line option
1954 *
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
1984#include "argtable3.h"
1985
1986#ifndef ARG_AMALGAMATION
1987#include "argtable3_private.h"
1988#endif
1989
1990#include <stdlib.h>
1991#include <string.h>
1992
1993char* arg_strptime(const char* buf, const char* fmt, struct tm* tm);
1994
1995static void arg_date_resetfn(struct arg_date* parent) {
1996 ARG_TRACE(("%s:resetfn(%p)\n", __FILE__, parent));
1997 parent->count = 0;
1998}
1999
2000static int arg_date_scanfn(struct arg_date* parent, const char* argval) {
2001 int errorcode = 0;
2002
2003 if (parent->count == parent->hdr.maxcount) {
2004 errorcode = ARG_ERR_MAXCOUNT;
2005 } else if (!argval) {
2006 /* no argument value was given, leave parent->tmval[] unaltered but still count it */
2007 parent->count++;
2008 } else {
2009 const char* pend;
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
2017 errorcode = ARG_ERR_BADDATE;
2018 }
2019
2020 ARG_TRACE(("%s:scanfn(%p) returns %d\n", __FILE__, parent, errorcode));
2021 return errorcode;
2022}
2023
2024static int arg_date_checkfn(struct arg_date* parent) {
2025 int errorcode = (parent->count < parent->hdr.mincount) ? ARG_ERR_MINCOUNT : 0;
2026
2027 ARG_TRACE(("%s:checkfn(%p) returns %d\n", __FILE__, parent, errorcode));
2028 return errorcode;
2029}
2030
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;
2035
2036 /* make argval NULL safe */
2037 argval = argval ? argval : "";
2038
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;
2045
2046 case ARG_ERR_MAXCOUNT:
2047 arg_dstr_cat(ds, "excess option ");
2048 arg_print_option_ds(ds, shortopts, longopts, argval, "\n");
2049 break;
2050
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 }
2062 }
2063}
2064
2065struct arg_date* arg_date0(const char* shortopts, const char* longopts, const char* format, const char* datatype, const char* glossary) {
2066 return arg_daten(shortopts, longopts, format, datatype, 0, 1, glossary);
2067}
2068
2069struct arg_date* arg_date1(const char* shortopts, const char* longopts, const char* format, const char* datatype, const char* glossary) {
2070 return arg_daten(shortopts, longopts, format, datatype, 1, 1, glossary);
2071}
2072
2073struct arg_date*
2074arg_daten(const char* shortopts, const char* longopts, const char* format, const char* datatype, int mincount, int maxcount, const char* glossary) {
2075 size_t nbytes;
2076 struct arg_date* result;
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 */
2086 + maxcount * sizeof(struct tm); /* storage for tmval[maxcount] array */
2087
2088 /* allocate storage for the arg_date struct + tmval[] array. */
2089 /* we use calloc because we want the tmval[] array zero filled. */
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;
2112
2113 ARG_TRACE(("arg_daten() returns %p\n", result));
2114 return result;
2115}
2116
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 */
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)
2162
2163static int conv_num(const char**, int*, int, int);
2164
2165static const char* day[7] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
2166
2167static const char* abday[7] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
2168
2169static const char* mon[12] = {"January", "February", "March", "April", "May", "June",
2170 "July", "August", "September", "October", "November", "December"};
2171
2172static const char* abmon[12] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
2173
2174static const char* am_pm[2] = {"AM", "PM"};
2175
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;
2179 while (tolower(*us1) == tolower(*us2++))
2180 if (*us1++ == '\0')
2181 return 0;
2182
2183 return tolower(*us1) - tolower(*--us2);
2184}
2185
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 {
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
2202char* arg_strptime(const char* buf, const char* fmt, struct tm* tm) {
2203 char c;
2204 const char* bp;
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. */
2215 if (isspace(c)) {
2216 while (isspace(*bp))
2217 bp++;
2218
2219 fmt++;
2220 continue;
2221 }
2222
2223 if ((c = *fmt++) != '%')
2224 goto literal;
2225
2226 again:
2227 switch (c = *fmt++) {
2228 case '%': /* "%%" is converted to "%". */
2229 literal:
2230 if (c != *bp++)
2231 return (0);
2232 break;
2233
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;
2242
2243 case 'O': /* "%O?" alternative conversion modifier. */
2244 LEGAL_ALT(0);
2245 alt_format |= ALT_O;
2246 goto again;
2247
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;
2257
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;
2264
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;
2271
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;
2278
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;
2285
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;
2292
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;
2299
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 }
2317
2318 /* Nothing matched. */
2319 if (i == 7)
2320 return (0);
2321
2322 tm->tm_wday = i;
2323 bp += len;
2324 break;
2325
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 }
2341
2342 /* Nothing matched. */
2343 if (i == 12)
2344 return (0);
2345
2346 tm->tm_mon = i;
2347 bp += len;
2348 break;
2349
2350 case 'C': /* The century number. */
2351 LEGAL_ALT(ALT_E);
2352 if (!(conv_num(&bp, &i, 0, 99)))
2353 return (0);
2354
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;
2362
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;
2369
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;
2378
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;
2389
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;
2396
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;
2402
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;
2409
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);
2416
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);
2424
2425 tm->tm_hour += 12;
2426 bp += strlen(am_pm[1]);
2427 break;
2428 }
2429
2430 /* Nothing matched. */
2431 return (0);
2432
2433 case 'S': /* The seconds. */
2434 LEGAL_ALT(ALT_O);
2435 if (!(conv_num(&bp, &tm->tm_sec, 0, 61)))
2436 return (0);
2437 break;
2438
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);
2450 break;
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)))
2461 return (0);
2462
2463 tm->tm_year = i - TM_YEAR_BASE;
2464 break;
2465
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);
2470
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;
2481
2482 /*
2483 * Miscellaneous conversions.
2484 */
2485 case 'n': /* Any kind of white-space. */
2486 case 't':
2487 LEGAL_ALT(0);
2488 while (isspace(*bp))
2489 bp++;
2490 break;
2491
2492 default: /* Unknown/unsupported conversion. */
2493 return (0);
2494 }
2495 }
2496
2497 /* LINTED functional specification */
2498 return ((char*)bp);
2499}
2500
2501static int conv_num(const char** buf, int* dest, int llim, int ulim) {
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/*******************************************************************************
2523 * arg_dbl: Implements the double command-line option
2524 *
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
2554#include "argtable3.h"
2555
2556#ifndef ARG_AMALGAMATION
2557#include "argtable3_private.h"
2558#endif
2559
2560#include <stdlib.h>
2561
2562static void arg_dbl_resetfn(struct arg_dbl* parent) {
2563 ARG_TRACE(("%s:resetfn(%p)\n", __FILE__, parent));
2564 parent->count = 0;
2565}
2566
2567static int arg_dbl_scanfn(struct arg_dbl* parent, const char* argval) {
2568 int errorcode = 0;
2569
2570 if (parent->count == parent->hdr.maxcount) {
2571 /* maximum number of arguments exceeded */
2572 errorcode = ARG_ERR_MAXCOUNT;
2573 } else if (!argval) {
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++;
2578 } else {
2579 double val;
2580 char* end;
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
2589 errorcode = ARG_ERR_BADDOUBLE;
2590 }
2591
2592 ARG_TRACE(("%s:scanfn(%p) returns %d\n", __FILE__, parent, errorcode));
2593 return errorcode;
2594}
2595
2596static int arg_dbl_checkfn(struct arg_dbl* parent) {
2597 int errorcode = (parent->count < parent->hdr.mincount) ? ARG_ERR_MINCOUNT : 0;
2598
2599 ARG_TRACE(("%s:checkfn(%p) returns %d\n", __FILE__, parent, errorcode));
2600 return errorcode;
2601}
2602
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;
2607
2608 /* make argval NULL safe */
2609 argval = argval ? argval : "";
2610
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;
2627 }
2628}
2629
2630struct arg_dbl* arg_dbl0(const char* shortopts, const char* longopts, const char* datatype, const char* glossary) {
2631 return arg_dbln(shortopts, longopts, datatype, 0, 1, glossary);
2632}
2633
2634struct arg_dbl* arg_dbl1(const char* shortopts, const char* longopts, const char* datatype, const char* glossary) {
2635 return arg_dbln(shortopts, longopts, datatype, 1, 1, glossary);
2636}
2637
2638struct arg_dbl* arg_dbln(const char* shortopts, const char* longopts, const char* datatype, int mincount, int maxcount, const char* glossary) {
2639 size_t nbytes;
2640 struct arg_dbl* result;
2641 size_t addr;
2642 size_t rem;
2643
2644 /* foolproof things by ensuring maxcount is not less than mincount */
2645 maxcount = (maxcount < mincount) ? mincount : maxcount;
2646
2647 nbytes = sizeof(struct arg_dbl) /* storage for struct arg_dbl */
2648 + (maxcount + 1) * sizeof(double); /* storage for dval[maxcount] array plus one extra for padding to memory boundary */
2649
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;
2677
2678 ARG_TRACE(("arg_dbln() returns %p\n", result));
2679 return result;
2680}
2681/*******************************************************************************
2682 * arg_end: Implements the error handling utilities
2683 *
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
2713#include "argtable3.h"
2714
2715#ifndef ARG_AMALGAMATION
2716#include "argtable3_private.h"
2717#endif
2718
2719#include <stdlib.h>
2720
2721static void arg_end_resetfn(struct arg_end* parent) {
2722 ARG_TRACE(("%s:resetfn(%p)\n", __FILE__, parent));
2723 parent->count = 0;
2724}
2725
2726static void arg_end_errorfn(void* parent, arg_dstr_t ds, int error, const char* argval, const char* progname) {
2727 /* suppress unreferenced formal parameter warning */
2728 (void)parent;
2729
2730 progname = progname ? progname : "";
2731 argval = argval ? argval : "";
2732
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;
2753 }
2754
2755 arg_dstr_cat(ds, "\n");
2756}
2757
2758struct arg_end* arg_end(int maxcount) {
2759 size_t nbytes;
2760 struct arg_end* result;
2761
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 */
2765
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);
2790
2791 ARG_TRACE(("arg_end(%d) returns %p\n", maxcount, result));
2792 return result;
2793}
2794
2795void arg_print_errors_ds(arg_dstr_t ds, struct arg_end* end, const char* progname) {
2796 int i;
2797 ARG_TRACE(("arg_errors()\n"));
2798 for (i = 0; i < end->count; i++) {
2799 struct arg_hdr* errorparent = (struct arg_hdr*)(end->parent[i]);
2800 if (errorparent->errorfn)
2801 errorparent->errorfn(end->parent[i], ds, end->error[i], end->argval[i], progname);
2802 }
2803}
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}
2811/*******************************************************************************
2812 * arg_file: Implements the file command-line option
2813 *
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
2843#include "argtable3.h"
2844
2845#ifndef ARG_AMALGAMATION
2846#include "argtable3_private.h"
2847#endif
2848
2849#include <stdlib.h>
2850#include <string.h>
2851
2852#ifdef WIN32
2853#define FILESEPARATOR1 '\\'
2854#define FILESEPARATOR2 '/'
2855#else
2856#define FILESEPARATOR1 '/'
2857#define FILESEPARATOR2 '/'
2858#endif
2859
2860static void arg_file_resetfn(struct arg_file* parent) {
2861 ARG_TRACE(("%s:resetfn(%p)\n", __FILE__, parent));
2862 parent->count = 0;
2863}
2864
2865/* Returns ptr to the base filename within *filename */
2866static const char* arg_basename(const char* filename) {
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)
2876 result = result2 + 1; /* using FILESEPARATOR2 (the alternative file separator) */
2877
2878 if (result1)
2879 result = result1 + 1; /* using FILESEPARATOR1 (the preferred file separator) */
2880
2881 if (!result)
2882 result = filename; /* neither file separator was found so basename is the whole filename */
2883
2884 /* special cases of "." and ".." are not considered basenames */
2885 if (result && (strcmp(".", result) == 0 || strcmp("..", result) == 0))
2886 result = filename + strlen(filename);
2887
2888 return result;
2889}
2890
2891/* Returns ptr to the file extension within *basename */
2892static const char* arg_extension(const char* basename) {
2893 /* find the last occurrence of '.' in basename */
2894 const char* result = (basename ? strrchr(basename, '.') : NULL);
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 */
2905 if (basename && result && strlen(result) == 1)
2906 result = basename + strlen(basename);
2907
2908 return result;
2909}
2910
2911static int arg_file_scanfn(struct arg_file* parent, const char* argval) {
2912 int errorcode = 0;
2913
2914 if (parent->count == parent->hdr.maxcount) {
2915 /* maximum number of arguments exceeded */
2916 errorcode = ARG_ERR_MAXCOUNT;
2917 } else if (!argval) {
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++;
2922 } else {
2923 parent->filename[parent->count] = argval;
2924 parent->basename[parent->count] = arg_basename(argval);
2925 parent->extension[parent->count] =
2926 arg_extension(parent->basename[parent->count]); /* only seek extensions within the basename (not the file path)*/
2927 parent->count++;
2928 }
2929
2930 ARG_TRACE(("%s4:scanfn(%p) returns %d\n", __FILE__, parent, errorcode));
2931 return errorcode;
2932}
2933
2934static int arg_file_checkfn(struct arg_file* parent) {
2935 int errorcode = (parent->count < parent->hdr.mincount) ? ARG_ERR_MINCOUNT : 0;
2936
2937 ARG_TRACE(("%s:checkfn(%p) returns %d\n", __FILE__, parent, errorcode));
2938 return errorcode;
2939}
2940
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;
2945
2946 /* make argval NULL safe */
2947 argval = argval ? argval : "";
2948
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);
2963 }
2964}
2965
2966struct arg_file* arg_file0(const char* shortopts, const char* longopts, const char* datatype, const char* glossary) {
2967 return arg_filen(shortopts, longopts, datatype, 0, 1, glossary);
2968}
2969
2970struct arg_file* arg_file1(const char* shortopts, const char* longopts, const char* datatype, const char* glossary) {
2971 return arg_filen(shortopts, longopts, datatype, 1, 1, glossary);
2972}
2973
2974struct arg_file* arg_filen(const char* shortopts, const char* longopts, const char* datatype, int mincount, int maxcount, const char* glossary) {
2975 size_t nbytes;
2976 struct arg_file* result;
2977 int i;
2978
2979 /* foolproof things by ensuring maxcount is not less than mincount */
2980 maxcount = (maxcount < mincount) ? mincount : maxcount;
2981
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 */
2986
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] = "";
3014 }
3015
3016 ARG_TRACE(("arg_filen() returns %p\n", result));
3017 return result;
3018}
3019/*******************************************************************************
3020 * arg_int: Implements the int command-line option
3021 *
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
3051#include "argtable3.h"
3052
3053#ifndef ARG_AMALGAMATION
3054#include "argtable3_private.h"
3055#endif
3056
3057#include <ctype.h>
3058#include <limits.h>
3059#include <stdlib.h>
3060
3061static void arg_int_resetfn(struct arg_int* parent) {
3062 ARG_TRACE(("%s:resetfn(%p)\n", __FILE__, parent));
3063 parent->count = 0;
3064}
3065
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. */
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 */
3080
3081 /* skip leading whitespace */
3082 while (isspace(*ptr))
3083 ptr++;
3084 /* printf("1) %s\n",ptr); */
3085
3086 /* scan optional sign character */
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;
3099 }
3100 /* printf("2) %s\n",ptr); */
3101
3102 /* '0X' prefix */
3103 if ((*ptr++) != '0') {
3104 /* printf("failed to detect '0'\n"); */
3105 *endptr = str;
3106 return 0;
3107 }
3108 /* printf("3) %s\n",ptr); */
3109 if (toupper(*ptr++) != toupper(X)) {
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() */
3117 val = strtol(ptr, (char**)endptr, base);
3118 if (*endptr == ptr) {
3119 /* conversion failed */
3120 *endptr = str;
3121 return 0;
3122 }
3123
3124 /* success */
3125 return s * val;
3126}
3127
3128/* Returns 1 if str matches suffix (case insensitive). */
3129/* Str may contain trailing whitespace, but nothing else. */
3130static int detectsuffix(const char* str, const char* suffix) {
3131 /* scan pairwise through strings until mismatch detected */
3132 while (toupper(*str) == toupper(*suffix)) {
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)
3147 return 0; /* failed to consume entire suffix */
3148
3149 /* skip any remaining whitespace in str */
3150 while (isspace(*str))
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
3157static int arg_int_scanfn(struct arg_int* parent, const char* argval) {
3158 int errorcode = 0;
3159
3160 if (parent->count == parent->hdr.maxcount) {
3161 /* maximum number of arguments exceeded */
3162 errorcode = ARG_ERR_MAXCOUNT;
3163 } else if (!argval) {
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++;
3168 } else {
3169 long int val;
3170 const char* end;
3171
3172 /* attempt to extract hex integer (eg: +0x123) from argval into val conversion */
3173 val = strtol0X(argval, &end, 'X', 16);
3174 if (end == argval) {
3175 /* hex failed, attempt octal conversion (eg +0o123) */
3176 val = strtol0X(argval, &end, 'O', 8);
3177 if (end == argval) {
3178 /* octal failed, attempt binary conversion (eg +0B101) */
3179 val = strtol0X(argval, &end, 'B', 2);
3180 if (end == argval) {
3181 /* binary failed, attempt decimal conversion with no prefix (eg 1234) */
3182 val = strtol(argval, (char**)&end, 10);
3183 if (end == argval) {
3184 /* all supported number formats failed */
3185 return ARG_ERR_BADINT;
3186 }
3187 }
3188 }
3189 }
3190
3191 /* Safety check for integer overflow. WARNING: this check */
3192 /* achieves nothing on machines where size(int)==size(long). */
3193 if (val > INT_MAX || val < INT_MIN)
3194 errorcode = ARG_ERR_OVERFLOW;
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. */
3198 if (detectsuffix(end, "KB")) /* kilobytes */
3199 {
3200 if (val > (INT_MAX / 1024) || val < (INT_MIN / 1024))
3201 errorcode = ARG_ERR_OVERFLOW; /* Overflow would occur if we proceed */
3202 else
3203 val *= 1024; /* 1KB = 1024 */
3204 } else if (detectsuffix(end, "MB")) /* megabytes */
3205 {
3206 if (val > (INT_MAX / 1048576) || val < (INT_MIN / 1048576))
3207 errorcode = ARG_ERR_OVERFLOW; /* Overflow would occur if we proceed */
3208 else
3209 val *= 1048576; /* 1MB = 1024*1024 */
3210 } else if (detectsuffix(end, "GB")) /* gigabytes */
3211 {
3212 if (val > (INT_MAX / 1073741824) || val < (INT_MIN / 1073741824))
3213 errorcode = ARG_ERR_OVERFLOW; /* Overflow would occur if we proceed */
3214 else
3215 val *= 1073741824; /* 1GB = 1024*1024*1024 */
3216 } else if (!detectsuffix(end, ""))
3217 errorcode = ARG_ERR_BADINT; /* invalid suffix detected */
3218
3219 /* if success then store result in parent->ival[] array */
3220 if (errorcode == 0)
3221 parent->ival[parent->count++] = (int)val;
3222 }
3223
3224 /* printf("%s:scanfn(%p,%p) returns %d\n",__FILE__,parent,argval,errorcode); */
3225 return errorcode;
3226}
3227
3228static int arg_int_checkfn(struct arg_int* parent) {
3229 int errorcode = (parent->count < parent->hdr.mincount) ? ARG_ERR_MINCOUNT : 0;
3230 /*printf("%s:checkfn(%p) returns %d\n",__FILE__,parent,errorcode);*/
3231 return errorcode;
3232}
3233
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;
3238
3239 /* make argval NULL safe */
3240 argval = argval ? argval : "";
3241
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;
3264 }
3265}
3266
3267struct arg_int* arg_int0(const char* shortopts, const char* longopts, const char* datatype, const char* glossary) {
3268 return arg_intn(shortopts, longopts, datatype, 0, 1, glossary);
3269}
3270
3271struct arg_int* arg_int1(const char* shortopts, const char* longopts, const char* datatype, const char* glossary) {
3272 return arg_intn(shortopts, longopts, datatype, 1, 1, glossary);
3273}
3274
3275struct arg_int* arg_intn(const char* shortopts, const char* longopts, const char* datatype, int mincount, int maxcount, const char* glossary) {
3276 size_t nbytes;
3277 struct arg_int* result;
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
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
3305 ARG_TRACE(("arg_intn() returns %p\n", result));
3306 return result;
3307}
3308/*******************************************************************************
3309 * arg_lit: Implements the literature command-line option
3310 *
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
3340#include "argtable3.h"
3341
3342#ifndef ARG_AMALGAMATION
3343#include "argtable3_private.h"
3344#endif
3345
3346#include <stdlib.h>
3347
3348static void arg_lit_resetfn(struct arg_lit* parent) {
3349 ARG_TRACE(("%s:resetfn(%p)\n", __FILE__, parent));
3350 parent->count = 0;
3351}
3352
3353static int arg_lit_scanfn(struct arg_lit* parent, const char* argval) {
3354 int errorcode = 0;
3355 if (parent->count < parent->hdr.maxcount)
3356 parent->count++;
3357 else
3358 errorcode = ARG_ERR_MAXCOUNT;
3359
3360 ARG_TRACE(("%s:scanfn(%p,%s) returns %d\n", __FILE__, parent, argval, errorcode));
3361 return errorcode;
3362}
3363
3364static int arg_lit_checkfn(struct arg_lit* parent) {
3365 int errorcode = (parent->count < parent->hdr.mincount) ? ARG_ERR_MINCOUNT : 0;
3366 ARG_TRACE(("%s:checkfn(%p) returns %d\n", __FILE__, parent, errorcode));
3367 return errorcode;
3368}
3369
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;
3374
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;
3381
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;
3386 }
3387
3388 ARG_TRACE(("%s:errorfn(%p, %p, %d, %s, %s)\n", __FILE__, parent, ds, errorcode, argval, progname));
3389}
3390
3391struct arg_lit* arg_lit0(const char* shortopts, const char* longopts, const char* glossary) {
3392 return arg_litn(shortopts, longopts, 0, 1, glossary);
3393}
3394
3395struct arg_lit* arg_lit1(const char* shortopts, const char* longopts, const char* glossary) {
3396 return arg_litn(shortopts, longopts, 1, 1, glossary);
3397}
3398
3399struct arg_lit* arg_litn(const char* shortopts, const char* longopts, int mincount, int maxcount, const char* glossary) {
3400 struct arg_lit* result;
3401
3402 /* foolproof things by ensuring maxcount is not less than mincount */
3403 maxcount = (maxcount < mincount) ? mincount : maxcount;
3404
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
3424 ARG_TRACE(("arg_litn() returns %p\n", result));
3425 return result;
3426}
3427/*******************************************************************************
3428 * arg_rem: Implements the rem command-line option
3429 *
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
3459#include "argtable3.h"
3460
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;
3482
3483 ARG_TRACE(("arg_rem() returns %p\n", result));
3484 return result;
3485}
3486/*******************************************************************************
3487 * arg_rex: Implements the regex command-line option
3488 *
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
3518#include "argtable3.h"
3519
3520#ifndef ARG_AMALGAMATION
3521#include "argtable3_private.h"
3522#endif
3523
3524#include <stdlib.h>
3525#include <string.h>
3526
3527#ifndef _TREX_H_
3528#define _TREX_H_
3529
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 */
3556
3557#ifdef __cplusplus
3558extern "C" {
3559#endif
3560
3561#define TRexChar char
3562#define MAX_CHAR 0xFF
3563#define _TREXC(c) (c)
3564#define trex_strlen strlen
3565#define trex_printf printf
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 {
3580 const TRexChar* begin;
3581 int len;
3582} TRexMatch;
3583
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);
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);
3592TREX_API TRexBool
3593trex_searchrange(TRex* exp, const TRexChar* text_begin, const TRexChar* text_end, const TRexChar** out_begin, const TRexChar** out_end);
3594TREX_API int trex_getsubexpcount(TRex* exp);
3595TREX_API TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch* subexp);
3596
3597#ifdef __cplusplus
3598}
3599#endif
3600
3601#endif
3602
3603struct privhdr {
3604 const char* pattern;
3605 int flags;
3606};
3607
3608static void arg_rex_resetfn(struct arg_rex* parent) {
3609 ARG_TRACE(("%s:resetfn(%p)\n", __FILE__, parent));
3610 parent->count = 0;
3611}
3612
3613static int arg_rex_scanfn(struct arg_rex* parent, const char* argval) {
3614 int errorcode = 0;
3615 const TRexChar* error = NULL;
3616 TRex* rex = NULL;
3617 TRexBool is_match = TRex_False;
3618
3619 if (parent->count == parent->hdr.maxcount) {
3620 /* maximum number of arguments exceeded */
3621 errorcode = ARG_ERR_MAXCOUNT;
3622 } else if (!argval) {
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++;
3627 } else {
3628 struct privhdr* priv = (struct privhdr*)parent->hdr.priv;
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)
3636 errorcode = ARG_ERR_REGNOMATCH;
3637 else
3638 parent->sval[parent->count++] = argval;
3639
3640 trex_free(rex);
3641 }
3642
3643 ARG_TRACE(("%s:scanfn(%p) returns %d\n", __FILE__, parent, errorcode));
3644 return errorcode;
3645}
3646
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;
3651
3652 /* free the regex "program" we constructed in resetfn */
3653 regfree(&(priv->regex));
3654
3655 /*printf("%s:checkfn(%p) returns %d\n",__FILE__,parent,errorcode);*/
3656#endif
3657 return errorcode;
3658}
3659
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;
3664
3665 /* make argval NULL safe */
3666 argval = argval ? argval : "";
3667
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;
3692 }
3693}
3694
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}
3698
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) {
3711 size_t nbytes;
3712 struct arg_rex* result;
3713 struct privhdr* priv;
3714 int i;
3715 const TRexChar* error = NULL;
3716 TRex* rex = NULL;
3717
3718 if (!pattern) {
3719 printf("argtable: ERROR - illegal regular expression pattern \"(NULL)\"\n");
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
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 */
3730
3731 /* init the arg_hdr struct */
3732 result = (struct arg_rex*)xmalloc(nbytes);
3733 result->hdr.flag = ARG_HASVALUE;
3734 result->hdr.shortopts = shortopts;
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;
3745
3746 /* store the arg_rex_priv struct immediately after the arg_rex struct */
3747 result->hdr.priv = result + 1;
3748 priv = (struct privhdr*)(result->hdr.priv);
3749 priv->pattern = pattern;
3750 priv->flags = flags;
3751
3752 /* store the sval[maxcount] array immediately after the arg_rex_priv struct */
3753 result->sval = (const char**)(priv + 1);
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);
3767 if (rex == NULL) {
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
3778/* see copyright notice in trex.h */
3779#include <ctype.h>
3780#include <setjmp.h>
3781#include <stdlib.h>
3782#include <string.h>
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
3796#ifdef ARG_REX_DEBUG
3797#include <stdio.h>
3798
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")};
3802
3803#endif
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)
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
3827typedef int TRexNodeType;
3828
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;
3851};
3852
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;
3869}
3870
3871static void trex_error(TRex* exp, const TRexChar* error) {
3872 if (exp->_error)
3873 *exp->_error = error;
3874 longjmp(*((jmp_buf*)exp->_jmpbuf), -1);
3875}
3876
3877static void trex_expect(TRex* exp, int n) {
3878 if ((*exp->_p) != n)
3879 trex_error(exp, _SC("expected paren"));
3880 exp->_p++;
3881}
3882
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++);
3908}
3909
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;
3914}
3915
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);
3976}
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;
4002 exp->_nodes[chain].next = r;
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;
4026}
4027
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;
4039}
4040
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 }
4137
4138 return ret;
4139}
4140
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}
4164
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*/
4201}
4202
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}
4233
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 }
4248
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 }
4277
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 }
4319
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));
4335
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 }
4383}
4384
4385/* public api */
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 }
4418#endif
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;
4426}
4427
4428void trex_free(TRex* exp) {
4429 if (exp) {
4430 xfree(exp->_nodes);
4431 xfree(exp->_jmpbuf);
4432 xfree(exp->_matches);
4433 xfree(exp);
4434 }
4435}
4436
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;
4446}
4447
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);
4466
4467 if (cur == NULL)
4468 return TRex_False;
4469
4470 --text_begin;
4471
4472 if (out_begin)
4473 *out_begin = text_begin;
4474 if (out_end)
4475 *out_end = cur;
4476 return TRex_True;
4477}
4478
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);
4481}
4482
4483int trex_getsubexpcount(TRex* exp) {
4484 return exp->_nsubexpr;
4485}
4486
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;
4492}
4493/*******************************************************************************
4494 * arg_str: Implements the str command-line option
4495 *
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
4525#include "argtable3.h"
4526
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;
4535
4536 ARG_TRACE(("%s:resetfn(%p)\n", __FILE__, parent));
4537 for (i = 0; i < parent->count; i++) {
4538 parent->sval[i] = "";
4539 }
4540 parent->count = 0;
4541}
4542
4543static int arg_str_scanfn(struct arg_str* parent, const char* argval) {
4544 int errorcode = 0;
4545
4546 if (parent->count == parent->hdr.maxcount) {
4547 /* maximum number of arguments exceeded */
4548 errorcode = ARG_ERR_MAXCOUNT;
4549 } else if (!argval) {
4550 /* a valid argument with no argument value was given. */
4551 /* This happens when an optional argument value was invoked. */
4552 /* leave parent argument value unaltered but still count the argument. */
4553 parent->count++;
4554 } else {
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
4562static int arg_str_checkfn(struct arg_str* parent) {
4563 int errorcode = (parent->count < parent->hdr.mincount) ? ARG_ERR_MINCOUNT : 0;
4564
4565 ARG_TRACE(("%s:checkfn(%p) returns %d\n", __FILE__, parent, errorcode));
4566 return errorcode;
4567}
4568
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;
4573
4574 /* make argval NULL safe */
4575 argval = argval ? argval : "";
4576
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;
4588 }
4589}
4590
4591struct arg_str* arg_str0(const char* shortopts, const char* longopts, const char* datatype, const char* glossary) {
4592 return arg_strn(shortopts, longopts, datatype, 0, 1, glossary);
4593}
4594
4595struct arg_str* arg_str1(const char* shortopts, const char* longopts, const char* datatype, const char* glossary) {
4596 return arg_strn(shortopts, longopts, datatype, 1, 1, glossary);
4597}
4598
4599struct arg_str* arg_strn(const char* shortopts, const char* longopts, const char* datatype, int mincount, int maxcount, const char* glossary) {
4600 size_t nbytes;
4601 struct arg_str* result;
4602 int i;
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
4609 nbytes = sizeof(struct arg_str) /* storage for struct arg_str */
4610 + maxcount * sizeof(char*); /* storage for sval[maxcount] array */
4611
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;
4915 }
4916
4917 return 0;
4918}
4919/*******************************************************************************
4920 * argtable3: Implements the main interfaces of the library
4921 *
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
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>
4975#include <stdlib.h>
4976#include <string.h>
4977
4978static void arg_register_error(struct arg_end* end, void* parent, int error, const char* argval) {
4979 /* printf("arg_register_error(%p,%p,%d,%s)\n",end,parent,error,argval); */
4980 if (end->count < end->hdr.maxcount) {
4981 end->error[end->count] = error;
4982 end->parent[end->count] = parent;
4983 end->argval[end->count] = argval;
4984 end->count++;
4985 } else {
4986 end->error[end->hdr.maxcount - 1] = ARG_ELIMIT;
4987 end->parent[end->hdr.maxcount - 1] = end;
4988 end->argval[end->hdr.maxcount - 1] = NULL;
4989 }
4990}
4991
4992/*
4993 * Return index of first table entry with a matching short option
4994 * or -1 if no match was found.
4995 */
4996static int find_shortoption(struct arg_hdr** table, char shortopt) {
4997 int tabindex;
4998 for (tabindex = 0; !(table[tabindex]->flag & ARG_TERMINATOR); tabindex++) {
4999 if (table[tabindex]->shortopts && strchr(table[tabindex]->shortopts, shortopt))
5000 return tabindex;
5001 }
5002 return -1;
5003}
5004
5005struct longoptions {
5006 int getoptval;
5007 int noptions;
5008 struct option* options;
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
5030static struct longoptions* alloc_longoptions(struct arg_hdr** table) {
5031 struct longoptions* result;
5032 size_t nbytes;
5033 int noptions = 1;
5034 size_t longoptlen = 0;
5035 int tabindex;
5036 int option_index = 0;
5037 char* store;
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;
5050 do {
5051 const char* longopts = table[tabindex]->longopts;
5052 longoptlen += (longopts ? strlen(longopts) : 0) + 1;
5053 while (longopts) {
5054 noptions++;
5055 longopts = strchr(longopts + 1, ',');
5056 }
5057 } while (!(table[tabindex++]->flag & ARG_TERMINATOR));
5058 /*printf("%d long options consuming %d chars in total\n",noptions,longoptlen);*/
5059
5060 /* allocate storage for return data structure as: */
5061 /* (struct longoptions) + (struct options)[noptions] + char[longoptlen] */
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;
5093
5094 option_index++;
5095 }
5096 }
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;
5102
5103 /*dump_longoptions(result);*/
5104 return result;
5105}
5106
5107static char* alloc_shortoptions(struct arg_hdr** table) {
5108 char* result;
5109 size_t len = 2;
5110 int tabindex;
5111 char* res;
5112
5113 /* determine the total number of option chars required */
5114 for (tabindex = 0; !(table[tabindex]->flag & ARG_TERMINATOR); tabindex++) {
5115 struct arg_hdr* hdr = table[tabindex];
5116 len += 3 * (hdr->shortopts ? strlen(hdr->shortopts) : 0);
5117 }
5118
5119 result = xmalloc(len);
5120
5121 res = result;
5122
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++ = ':';
5136 }
5137 }
5138 /* null terminate the string */
5139 *res = 0;
5140
5141 /*printf("alloc_shortoptions() returns \"%s\"\n",(result?result:"NULL"));*/
5142 return result;
5143}
5144
5145/* return index of the table terminator entry */
5146static int arg_endindex(struct arg_hdr** table) {
5147 int tabindex = 0;
5148 while (!(table[tabindex]->flag & ARG_TERMINATOR))
5149 tabindex++;
5150 return tabindex;
5151}
5152
5153static void arg_parse_tagged(int argc, char** argv, struct arg_hdr** table, struct arg_end* endtable) {
5154 struct longoptions* longoptions;
5155 char* shortoptions;
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. */
5162 longoptions = alloc_longoptions(table);
5163 shortoptions = alloc_shortoptions(table);
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 */
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
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 */
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) {
5194 int errorcode = table[tabindex]->scanfn(parent, optarg);
5195 if (errorcode != 0)
5196 arg_register_error(endtable, parent, errorcode, optarg);
5197 }
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;
5243 }
5244 }
5245 }
5246
5247 xfree(shortoptions);
5248 xfree(longoptions);
5249}
5250
5251static void arg_parse_untagged(int argc, char** argv, struct arg_hdr** table, struct arg_end* endtable) {
5252 int tabindex = 0;
5253 int errorlast = 0;
5254 const char* optarglast = NULL;
5255 void* parentlast = NULL;
5256
5257 /*printf("arg_parse_untagged(%d,%p,%p,%p)\n",argc,argv,table,endtable);*/
5258 while (!(table[tabindex]->flag & ARG_TERMINATOR)) {
5259 void* parent;
5260 int errorcode;
5261
5262 /* if we have exhausted our argv[optind] entries then we have finished */
5263 if (optind >= argc) {
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) */
5269 if (table[tabindex]->longopts || table[tabindex]->shortopts) {
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 */
5276 if (!(table[tabindex]->scanfn)) {
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]);
5287 if (errorcode == 0) {
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;
5294 } else {
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 }
5304 }
5305
5306 /* if a tenative error still remains at this point then register it as a proper error */
5307 if (errorlast) {
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 */
5314 while (optind < argc) {
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
5322static void arg_parse_check(struct arg_hdr** table, struct arg_end* endtable) {
5323 int tabindex = 0;
5324 /* printf("arg_parse_check()\n"); */
5325 do {
5326 if (table[tabindex]->checkfn) {
5327 void* parent = table[tabindex]->parent;
5328 int errorcode = table[tabindex]->checkfn(parent);
5329 if (errorcode != 0)
5330 arg_register_error(endtable, parent, errorcode, NULL);
5331 }
5332 } while (!(table[tabindex++]->flag & ARG_TERMINATOR));
5333}
5334
5335static void arg_reset(void** argtable) {
5336 struct arg_hdr** table = (struct arg_hdr**)argtable;
5337 int tabindex = 0;
5338 /*printf("arg_reset(%p)\n",argtable);*/
5339 do {
5340 if (table[tabindex]->resetfn)
5341 table[tabindex]->resetfn(table[tabindex]->parent);
5342 } while (!(table[tabindex++]->flag & ARG_TERMINATOR));
5343}
5344
5345int arg_parse(int argc, char** argv, void** argtable) {
5346 struct arg_hdr** table = (struct arg_hdr**)argtable;
5347 struct arg_end* endtable;
5348 int endindex;
5349 char** argvcopy = NULL;
5350 int i;
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);
5359 endtable = (struct arg_end*)table[endindex];
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). */
5364 if (argc == 0) {
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
5372 argvcopy = (char**)xmalloc(sizeof(char*) * (argc + 1));
5373
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];
5381
5382 argvcopy[argc] = NULL;
5383
5384 /* parse the command line (local copy) for tagged options */
5385 arg_parse_tagged(argc, argvcopy, table, endtable);
5386
5387 /* parse the command line (local copy) for untagged options */
5388 arg_parse_untagged(argc, argvcopy, table, endtable);
5389
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);
5396
5397 return endtable->count;
5398}
5399
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 */
5420static void arg_cat(char** pdest, const char* src, size_t* pndest) {
5421 char* dest = *pdest;
5422 char* end = dest + *pndest;
5423
5424 /*locate null terminator of dest string */
5425 while (dest < end && *dest != 0)
5426 dest++;
5427
5428 /* concat src string to dest string */
5429 while (dest < end && *src != 0)
5430 *dest++ = *src++;
5431
5432 /* null terminate dest string */
5433 *dest = 0;
5434
5435 /* update *pdest and *pndest */
5436 *pndest = end - dest;
5437 *pdest = dest;
5438}
5439
5440static void arg_cat_option(char* dest, size_t ndest, const char* shortopts, const char* longopts, const char* datatype, int optvalue) {
5441 if (shortopts) {
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);
5451 if (datatype) {
5452 arg_cat(&dest, " ", &ndest);
5453 if (optvalue) {
5454 arg_cat(&dest, "[", &ndest);
5455 arg_cat(&dest, datatype, &ndest);
5456 arg_cat(&dest, "]", &ndest);
5457 } else
5458 arg_cat(&dest, datatype, &ndest);
5459 }
5460 } else if (longopts) {
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, ",");
5468#if (defined(__STDC_LIB_EXT1__) && defined(__STDC_WANT_LIB_EXT1__)) || (defined(__STDC_SECURE_LIB__) && defined(__STDC_WANT_SECURE_LIB__))
5469 strncat_s(dest, ndest, longopts, (ncspn < ndest) ? ncspn : ndest);
5470#else
5471 strncat(dest, longopts, (ncspn < ndest) ? ncspn : ndest);
5472#endif
5473
5474 if (datatype) {
5475 arg_cat(&dest, "=", &ndest);
5476 if (optvalue) {
5477 arg_cat(&dest, "[", &ndest);
5478 arg_cat(&dest, datatype, &ndest);
5479 arg_cat(&dest, "]", &ndest);
5480 } else
5481 arg_cat(&dest, datatype, &ndest);
5482 }
5483 } else if (datatype) {
5484 if (optvalue) {
5485 arg_cat(&dest, "[", &ndest);
5486 arg_cat(&dest, datatype, &ndest);
5487 arg_cat(&dest, "]", &ndest);
5488 } else
5489 arg_cat(&dest, datatype, &ndest);
5490 }
5491}
5492
5493static void arg_cat_optionv(char* dest, size_t ndest, const char* shortopts, const char* longopts, const char* datatype, int optvalue, const char* separator) {
5494 separator = separator ? separator : "";
5495
5496 if (shortopts) {
5497 const char* c = shortopts;
5498 while (*c) {
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
5518 if (longopts) {
5519 const char* c = longopts;
5520 while (*c) {
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, ",");
5528#if (defined(__STDC_LIB_EXT1__) && defined(__STDC_WANT_LIB_EXT1__)) || (defined(__STDC_SECURE_LIB__) && defined(__STDC_WANT_SECURE_LIB__))
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 */
5536 if (*c == ',') {
5537 arg_cat(&dest, separator, &ndest);
5538 c++;
5539 }
5540 }
5541 }
5542
5543 if (datatype) {
5544 if (longopts)
5545 arg_cat(&dest, "=", &ndest);
5546 else if (shortopts)
5547 arg_cat(&dest, " ", &ndest);
5548
5549 if (optvalue) {
5550 arg_cat(&dest, "[", &ndest);
5551 arg_cat(&dest, datatype, &ndest);
5552 arg_cat(&dest, "]", &ndest);
5553 } else
5554 arg_cat(&dest, datatype, &ndest);
5555 }
5556}
5557
5558void arg_print_option_ds(arg_dstr_t ds, const char* shortopts, const char* longopts, const char* datatype, const char* suffix) {
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 */
5563 arg_cat_optionv(syntax, sizeof(syntax) - 1, shortopts, longopts, datatype, 0, "|");
5564
5565 arg_dstr_cat(ds, syntax);
5566 arg_dstr_cat(ds, (char*)suffix);
5567}
5568
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}
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 */
5582static void arg_print_gnuswitch_ds(arg_dstr_t ds, struct arg_hdr** table) {
5583 int tabindex;
5584 char* format1 = " -%c";
5585 char* format2 = " [-%c";
5586 char* suffix = "";
5587
5588 /* print all mandatory switches that are without argument values */
5589 for (tabindex = 0; table[tabindex] && !(table[tabindex]->flag & ARG_TERMINATOR); tabindex++) {
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)*/
5603 arg_dstr_catf(ds, format1, table[tabindex]->shortopts[0]);
5604 format1 = "%c";
5605 format2 = "[%c";
5606 }
5607
5608 /* print all optional switches that are without argument values */
5609 for (tabindex = 0; table[tabindex] && !(table[tabindex]->flag & ARG_TERMINATOR); tabindex++) {
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 */
5623 arg_dstr_catf(ds, format2, table[tabindex]->shortopts[0]);
5624 format2 = "%c";
5625 suffix = "]";
5626 }
5627
5628 arg_dstr_catf(ds, "%s", suffix);
5629}
5630
5631void arg_print_syntax_ds(arg_dstr_t ds, void** argtable, const char* suffix) {
5632 struct arg_hdr** table = (struct arg_hdr**)argtable;
5633 int i, tabindex;
5634
5635 /* print GNU style [OPTION] string */
5636 arg_print_gnuswitch_ds(ds, table);
5637
5638 /* print remaining options in abbreviated style */
5639 for (tabindex = 0; table[tabindex] && !(table[tabindex]->flag & ARG_TERMINATOR); tabindex++) {
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) */
5644 if (table[tabindex]->shortopts && !(table[tabindex]->flag & ARG_HASVALUE))
5645 continue;
5646
5647 shortopts = table[tabindex]->shortopts;
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) {
5653 /* print mandatory instances of this option */
5654 for (i = 0; i < table[tabindex]->mincount; i++) {
5655 arg_dstr_cat(ds, " ");
5656 arg_dstr_cat(ds, syntax);
5657 }
5658
5659 /* print optional instances enclosed in "[..]" */
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;
5681 }
5682 }
5683 }
5684
5685 if (suffix) {
5686 arg_dstr_cat(ds, (char*)suffix);
5687 }
5688}
5689
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}
5696
5697void arg_print_syntaxv_ds(arg_dstr_t ds, void** argtable, const char* suffix) {
5698 struct arg_hdr** table = (struct arg_hdr**)argtable;
5699 int i, tabindex;
5700
5701 /* print remaining options in abbreviated style */
5702 for (tabindex = 0; table[tabindex] && !(table[tabindex]->flag & ARG_TERMINATOR); tabindex++) {
5703 char syntax[200] = "";
5704 const char *shortopts, *longopts, *datatype;
5705
5706 shortopts = table[tabindex]->shortopts;
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, "|");
5710
5711 /* print mandatory options */
5712 for (i = 0; i < table[tabindex]->mincount; i++) {
5713 arg_dstr_cat(ds, " ");
5714 arg_dstr_cat(ds, syntax);
5715 }
5716
5717 /* print optional args enclosed in "[..]" */
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;
5739 }
5740 }
5741
5742 if (suffix) {
5743 arg_dstr_cat(ds, (char*)suffix);
5744 }
5745}
5746
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}
5753
5754void arg_print_glossary_ds(arg_dstr_t ds, void** argtable, const char* format) {
5755 struct arg_hdr** table = (struct arg_hdr**)argtable;
5756 int tabindex;
5757
5758 format = format ? format : " %-20s %s\n";
5759 for (tabindex = 0; !(table[tabindex]->flag & ARG_TERMINATOR); tabindex++) {
5760 if (table[tabindex]->glossary) {
5761 char syntax[200] = "";
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);
5768 }
5769 }
5770}
5771
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}
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 */
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);
5818
5819 /* Someone doesn't like us... */
5820 if (line_end < line_start) {
5821 arg_dstr_catf(ds, "%s\n", text);
5822 }
5823
5824 while (line_end > line_start) {
5825 /* Eat leading white spaces. This is essential because while
5826 wrapping lines, there will often be a whitespace at beginning
5827 of line */
5828 while (isspace(*(text + line_start))) {
5829 line_start++;
5830 }
5831
5832 /* Find last whitespace, that fits into line */
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 }
5839
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 }
5848
5849 /* Output line of text */
5850 while (line_start < line_end) {
5851 char c = *(text + line_start);
5852 arg_dstr_catc(ds, c);
5853 line_start++;
5854 }
5855 arg_dstr_cat(ds, "\n");
5856
5857 /* Initialize another line */
5858 if (line_end < textlen) {
5859 unsigned i;
5860
5861 for (i = 0; i < lmargin; i++) {
5862 arg_dstr_cat(ds, " ");
5863 }
5864
5865 line_end = textlen;
5866 }
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 */
5879void arg_print_glossary_gnu_ds(arg_dstr_t ds, void** argtable) {
5880 struct arg_hdr** table = (struct arg_hdr**)argtable;
5881 int tabindex;
5882
5883 for (tabindex = 0; !(table[tabindex]->flag & ARG_TERMINATOR); tabindex++) {
5884 if (table[tabindex]->glossary) {
5885 char syntax[200] = "";
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;
5890
5891 if (!shortopts && longopts) {
5892 /* Indent trailing line by 4 spaces... */
5893 memset(syntax, ' ', 4);
5894 *(syntax + 4) = '\0';
5895 }
5896
5897 arg_cat_optionv(syntax, sizeof(syntax) - 1, shortopts, longopts, datatype, table[tabindex]->flag & ARG_HASOPTVALUE, ", ");
5898
5899 /* If syntax fits not into column, print glossary in new line... */
5900 if (strlen(syntax) > 25) {
5901 arg_dstr_catf(ds, " %-25s %s\n", syntax, "");
5902 *syntax = '\0';
5903 }
5904
5905 arg_dstr_catf(ds, " %-25s ", syntax);
5906 arg_print_formatted_ds(ds, 28, 79, glossary);
5907 }
5908 } /* for each table entry */
5909
5910 arg_dstr_cat(ds, "\n");
5911}
5912
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}
5919
5920/**
5921 * Checks the argtable[] array for NULL entries and returns 1
5922 * if any are found, zero otherwise.
5923 */
5924int arg_nullcheck(void** argtable) {
5925 struct arg_hdr** table = (struct arg_hdr**)argtable;
5926 int tabindex;
5927 /*printf("arg_nullcheck(%p)\n",argtable);*/
5928
5929 if (!table)
5930 return 1;
5931
5932 tabindex = 0;
5933 do {
5934 /*printf("argtable[%d]=%p\n",tabindex,argtable[tabindex]);*/
5935 if (!table[tabindex])
5936 return 1;
5937 } while (!(table[tabindex++]->flag & ARG_TERMINATOR));
5938
5939 return 0;
5940}
5941
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 */
5953void arg_free(void** argtable) {
5954 struct arg_hdr** table = (struct arg_hdr**)argtable;
5955 int tabindex = 0;
5956 int flag;
5957 /*printf("arg_free(%p)\n",argtable);*/
5958 do {
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;
5969 xfree(table[tabindex]);
5970 table[tabindex++] = NULL;
5971
5972 } while (!(flag & ARG_TERMINATOR));
5973}
5974
5975/* frees each non-NULL element of argtable[], where n is the size of the number of entries in the array */
5976void arg_freetable(void** argtable, size_t n) {
5977 struct arg_hdr** table = (struct arg_hdr**)argtable;
5978 size_t tabindex = 0;
5979 /*printf("arg_freetable(%p)\n",argtable);*/
5980 for (tabindex = 0; tabindex < n; tabindex++) {
5981 if (table[tabindex] == NULL)
5982 continue;
5983
5984 xfree(table[tabindex]);
5985 table[tabindex] = NULL;
5986 };
5987}
5988
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