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