1 /***************************************************************************
3 getarg.c - routines to grab the parameters from the command line:
5 Names of all the routines except the main one start with GA (Get
6 Arguments) to prevent conflicts.
8 The following routines are available in this module:
10 1. int GAGetArgs(argc, argv, CtrlStr, Variables...)
11 where argc, argv are received on entry.
12 CtrlStr is the contrl string (see below)
13 Variables are all the variables to be set according to CtrlStr.
14 Note that all the variables MUST be transfered by address.
15 Return 0 on correct parsing, otherwise error number (see GetArg.h).
17 2. GAPrintHowTo(CtrlStr)
18 Print the control string to stderr, in the correct format.
19 This feature is very useful in case of an error during GetArgs parsing.
20 Chars equal to SPACE_CHAR are not printed (regular spaces are NOT
21 allowed, and so using SPACE_CHAR you can create space in PrintHowTo).
23 3. GAPrintErrMsg(Error)
24 Describe the error to stderr, according to Error (usually returned by
29 The control string passed to GetArgs controls the way argv (argc) are
30 parsed. Each entry in this string must not have any spaces in it. The
31 First Entry is the name of the program, which is usually ignored except
32 when GAPrintHowTo is called. All the other entries (except the last one
33 which we will come back to later) must have the following format:
35 1. One letter which sets the option letter.
36 2. '!' or '%' to determines if this option is really optional ('%') or
38 3. '-' must always be given.
39 4. Alphanumeric string, usually ignored, but used by GAPrintHowTo to
40 print the meaning of this option.
41 5. Sequences starts with '!' or '%'. Again if '!' then this sequence
42 must exist (only if its option flag is given of course), and if '%'
43 it is optional. Each sequence will continue with one or two
44 characters which defines the kind of the input:
45 a: d, x, o, u - integer is expected (decimal, hex, octal base or unsigned).
46 b: D, X, O, U - long integer is expected (same as above).
47 c: f - float number is expected.
48 d: F - double number is expected.
49 e: s - string is expected.
50 f: *? - any number of '?' kind (d, x, o, u, D, X, O, U, f, F, s)
51 will match this one. If '?' is numeric, it scans until
52 non-numeric input is given. If '?' is 's' then it scans
53 up to the next option or end of argv.
55 If the last parameter given in the CtrlStr, is not an option (i.e. the
56 second char is not in ['!', '%'] and the third one is not '-'), all what
57 remained from argv is linked to it.
59 The variables passed to GAGetArgs (starting from 4th parameter) MUST
60 match the order of the CtrlStr:
62 For each option, one integer address must be passed. This integer must
63 be initialized with 0. If that option is given in the command line, it will
66 In addition, the sequences that might follow an option require the
67 following parameters to pass:
69 1. d, x, o, u - pointer to integer (int *).
70 2. D, X, O, U - pointer to long (long *).
71 3. f - pointer to float (float *).
72 4. F - pointer to double (double *).
73 5. s - pointer to char (char *). NO allocation is needed!
74 6. *? - TWO variables are passed for each wild request. the first
75 one is (address of) integer, and it will return number of
76 parameters actually matched this sequence, and the second
77 one is a pointer to pointer to ? (? **), and will return an
78 address to a block of pointers to ? kind, terminated with
79 NULL pointer. NO pre-allocation is required. The caller is
80 responsible for freeing this memory, including the pointed to
83 Note that these two variables are pretty like the argv/argc pair...
87 "Example1 i%-OneInteger!d s%-Strings!*s j%- k!-Float!f Files"
89 Will match: Example1 -i 77 -s String1 String2 String3 -k 88.2 File1 File2
90 or: Example1 -s String1 -k 88.3 -i 999 -j
91 but not: Example1 -i 77 78 (option i expects one integer, k must be).
93 Note the option k must exist, and that the order of the options is not
94 important. In the first examples File1 & File2 will match the Files
97 A call to GAPrintHowTo with this CtrlStr will print to stderr:
98 Example1 [-i OneIngeter] [-s Strings...] [-j] -k Float Files...
102 1. This module assumes that all the pointers to all kind of data types
103 have the same length and format, i.e. sizeof(int *) == sizeof(char *).
105 SPDX-License-Identifier: MIT
107 **************************************************************************/
117 #define MAX_PARAM 100 /* maximum number of parameters allowed. */
118 #define CTRL_STR_MAX_LEN 1024
120 #define SPACE_CHAR '|' /* The character not to print using HowTo. */
124 #define ISSPACE(x) ((x) <= ' ') /* Not conventional - but works fine! */
126 /* The two characters '%' and '!' are used in the control string: */
127 #define ISCTRLCHAR(x) (((x) == '%') || ((x) == '!'))
129 static char *GAErrorToken; /* On error, ErrorToken is set to point to it. */
130 static int GATestAllSatis(char *CtrlStrCopy, char *CtrlStr, char **argv_end,
131 char ***argv, void *Parameters[MAX_PARAM],
133 static bool GAUpdateParameters(void *Parameters[], int *ParamCount,
134 char *Option, char *CtrlStrCopy, char *CtrlStr,
135 char **argv_end, char ***argv);
136 static int GAGetParmeters(void *Parameters[], int *ParamCount,
137 char *CtrlStrCopy, char *Option, char **argv_end,
139 static int GAGetMultiParmeters(void *Parameters[], int *ParamCount,
140 char *CtrlStrCopy, char **argv_end, char ***argv);
141 static void GASetParamCount(char *CtrlStr, int Max, int *ParamCount);
142 static bool GAOptionExists(char **argv_end, char **argv);
144 /***************************************************************************
146 ***************************************************************************/
148 xmalloc(unsigned size) {
152 if ((p = malloc(size)) != NULL)
155 fprintf(stderr, "Not enough memory, exit.\n");
158 return NULL; /* Makes warning silent. */
160 /***************************************************************************
161 Routine to access the command line argument and interpret them:
162 Return ARG_OK (0) is case of successful parsing, error code else...
163 ***************************************************************************/
167 char *CtrlStr, ...) {
169 int i, ParamCount = 0;
170 void *Parameters[MAX_PARAM]; /* Save here parameter addresses. */
171 char CtrlStrCopy[CTRL_STR_MAX_LEN];
172 char **argv_end = argv + argc;
175 strncpy(CtrlStrCopy, CtrlStr, sizeof(CtrlStrCopy)-1);
176 GASetParamCount(CtrlStr, strlen(CtrlStr), &ParamCount);
177 va_start(ap, CtrlStr);
178 for (i = 1; i <= ParamCount; i++)
179 Parameters[i - 1] = va_arg(ap, void *);
182 argv++; /* Skip the program name (first in argv/c list). */
183 while (argv < argv_end) {
185 if (!GAOptionExists(argv_end, argv))
186 break; /* The loop. */
187 char *Option = *argv++;
188 if ((Error = GAUpdateParameters(Parameters, &ParamCount, Option,
189 CtrlStrCopy, CtrlStr, argv_end,
193 /* Check for results and update trail of command line: */
194 return GATestAllSatis(CtrlStrCopy, CtrlStr, argv_end, &argv, Parameters,
195 &ParamCount) != ARG_OK;
198 /***************************************************************************
199 Routine to search for unsatisfied flags - simply scan the list for !-
200 sequence. Before this scan, this routine updates the rest of the command
201 line into the last two parameters if it is requested by the CtrlStr
202 (last item in CtrlStr is NOT an option).
203 Return ARG_OK if all satisfied, CMD_ERR_AllSatis error else.
204 ***************************************************************************/
206 GATestAllSatis(char *CtrlStrCopy,
210 void *Parameters[MAX_PARAM],
214 static char *LocalToken = NULL;
216 /* If LocalToken is not initialized - do it now. Note that this string
217 * should be writable as well so we can not assign it directly.
219 if (LocalToken == NULL) {
220 LocalToken = (char *)malloc(3);
221 strcpy(LocalToken, "-?");
224 /* Check if last item is an option. If not then copy rest of command
225 * line into it as 1. NumOfprm, 2. pointer to block of pointers.
227 for (i = strlen(CtrlStr) - 1; i > 0 && !ISSPACE(CtrlStr[i]); i--) ;
228 if (!ISCTRLCHAR(CtrlStr[i + 2])) {
229 GASetParamCount(CtrlStr, i, ParamCount); /* Point in correct param. */
230 *(int *)Parameters[(*ParamCount)++] = argv_end - *argv;
231 *(char ***)Parameters[(*ParamCount)++] = *argv;
235 while (++i < (int)strlen(CtrlStrCopy))
236 if ((CtrlStrCopy[i] == '-') && (CtrlStrCopy[i - 1] == '!')) {
237 GAErrorToken = LocalToken;
238 LocalToken[1] = CtrlStrCopy[i - 2]; /* Set the correct flag. */
239 return CMD_ERR_AllSatis;
245 /***************************************************************************
246 Routine to update the parameters according to the given Option:
247 **************************************************************************/
249 GAUpdateParameters(void *Parameters[],
258 bool BooleanTrue = Option[2] != '-';
260 if (Option[0] != '-') {
261 GAErrorToken = Option;
262 return CMD_ERR_NotAnOpt;
264 i = 0; /* Scan the CtrlStrCopy for that option: */
265 while (i + 2 < (int)strlen(CtrlStrCopy)) {
266 if ((CtrlStrCopy[i] == Option[1]) && (ISCTRLCHAR(CtrlStrCopy[i + 1]))
267 && (CtrlStrCopy[i + 2] == '-')) {
268 /* We found that option! */
273 if (i + 2 >= (int)strlen(CtrlStrCopy)) {
274 GAErrorToken = Option;
275 return CMD_ERR_NoSuchOpt;
278 /* If we are here, then we found that option in CtrlStr - Strip it off: */
279 CtrlStrCopy[i] = CtrlStrCopy[i + 1] = CtrlStrCopy[i + 2] = (char)' ';
280 GASetParamCount(CtrlStr, i, ParamCount); /* Set it to point in
283 /* Set boolean flag for that option. */
284 *(bool *)Parameters[(*ParamCount)++] = BooleanTrue;
285 if (ISSPACE(CtrlStrCopy[i]))
286 return ARG_OK; /* Only a boolean flag is needed. */
288 /* Skip the text between the boolean option and data follows: */
289 while (!ISCTRLCHAR(CtrlStrCopy[i]))
291 /* Get the parameters and return the appropriete return code: */
292 return GAGetParmeters(Parameters, ParamCount, &CtrlStrCopy[i],
293 Option, argv_end, argv);
296 /***************************************************************************
297 Routine to get parameters according to the CtrlStr given from argv/argc
298 ***************************************************************************/
300 GAGetParmeters(void *Parameters[],
309 while (!(ISSPACE(CtrlStrCopy[i]))) {
310 switch (CtrlStrCopy[i + 1]) {
311 case 'd': /* Get signed integers. */
312 ScanRes = sscanf(*((*argv)++), "%d",
313 (int *)Parameters[(*ParamCount)++]);
315 case 'u': /* Get unsigned integers. */
316 ScanRes = sscanf(*((*argv)++), "%u",
317 (unsigned *)Parameters[(*ParamCount)++]);
319 case 'x': /* Get hex integers. */
320 ScanRes = sscanf(*((*argv)++), "%x",
321 (unsigned int *)Parameters[(*ParamCount)++]);
323 case 'o': /* Get octal integers. */
324 ScanRes = sscanf(*((*argv)++), "%o",
325 (unsigned int *)Parameters[(*ParamCount)++]);
327 case 'D': /* Get signed long integers. */
328 ScanRes = sscanf(*((*argv)++), "%ld",
329 (long *)Parameters[(*ParamCount)++]);
331 case 'U': /* Get unsigned long integers. */
332 ScanRes = sscanf(*((*argv)++), "%lu",
333 (unsigned long *)Parameters[(*ParamCount)++]);
335 case 'X': /* Get hex long integers. */
336 ScanRes = sscanf(*((*argv)++), "%lx",
337 (unsigned long *)Parameters[(*ParamCount)++]);
339 case 'O': /* Get octal long integers. */
340 ScanRes = sscanf(*((*argv)++), "%lo",
341 (unsigned long *)Parameters[(*ParamCount)++]);
343 case 'f': /* Get float number. */
344 ScanRes = sscanf(*((*argv)++), "%f",
345 (float *)Parameters[(*ParamCount)++]);
347 case 'F': /* Get double float number. */
348 ScanRes = sscanf(*((*argv)++), "%lf",
349 (double *)Parameters[(*ParamCount)++]);
351 case 's': /* It as a string. */
352 ScanRes = 1; /* Allways O.K. */
353 *(char **)Parameters[(*ParamCount)++] = *((*argv)++);
355 case '*': /* Get few parameters into one: */
356 ScanRes = GAGetMultiParmeters(Parameters, ParamCount,
357 &CtrlStrCopy[i], argv_end, argv);
358 if ((ScanRes == 0) && (CtrlStrCopy[i] == '!')) {
359 GAErrorToken = Option;
360 return CMD_ERR_WildEmpty;
364 ScanRes = 0; /* Make optimizer warning silent. */
366 /* If reading fails and this number is a must (!) then error: */
367 if ((ScanRes == 0) && (CtrlStrCopy[i] == '!')) {
368 GAErrorToken = Option;
369 return CMD_ERR_NumRead;
371 if (CtrlStrCopy[i + 1] != '*') {
372 i += 2; /* Skip to next parameter (if any). */
374 i += 3; /* Skip the '*' also! */
380 /***************************************************************************
381 Routine to get a few parameters into one pointer such that the returned
382 pointer actually points on a block of pointers to the parameters...
383 For example *F means a pointer to pointers on floats.
384 Returns number of parameters actually read.
385 This routine assumes that all pointers (on any kind of scalar) has the
386 same size (and the union below is totally ovelapped bteween dif. arrays)
387 ***************************************************************************/
389 GAGetMultiParmeters(void *Parameters[],
395 int i = 0, ScanRes, NumOfPrm = 0;
396 void **Pmain, **Ptemp;
397 union TmpArray { /* Save here the temporary data before copying it to */
398 void *VoidArray[MAX_PARAM]; /* the returned pointer block. */
399 int *IntArray[MAX_PARAM];
400 long *LngArray[MAX_PARAM];
401 float *FltArray[MAX_PARAM];
402 double *DblArray[MAX_PARAM];
403 char *ChrArray[MAX_PARAM];
407 switch (CtrlStrCopy[2]) { /* CtrlStr == '!*?' or '%*?' where ? is. */
408 case 'd': /* Format to read the parameters: */
409 TmpArray.IntArray[NumOfPrm] = xmalloc(sizeof(int));
410 ScanRes = sscanf(*((*argv)++), "%d",
411 (int *)TmpArray.IntArray[NumOfPrm++]);
414 TmpArray.IntArray[NumOfPrm] = xmalloc(sizeof(int));
415 ScanRes = sscanf(*((*argv)++), "%u",
416 (unsigned int *)TmpArray.IntArray[NumOfPrm++]);
419 TmpArray.IntArray[NumOfPrm] = xmalloc(sizeof(int));
420 ScanRes = sscanf(*((*argv)++), "%o",
421 (unsigned int *)TmpArray.IntArray[NumOfPrm++]);
424 TmpArray.IntArray[NumOfPrm] = xmalloc(sizeof(int));
425 ScanRes = sscanf(*((*argv)++), "%x",
426 (unsigned int *)TmpArray.IntArray[NumOfPrm++]);
429 TmpArray.LngArray[NumOfPrm] = xmalloc(sizeof(long));
430 ScanRes = sscanf(*((*argv)++), "%ld",
431 (long *)TmpArray.IntArray[NumOfPrm++]);
434 TmpArray.LngArray[NumOfPrm] = xmalloc(sizeof(long));
435 ScanRes = sscanf(*((*argv)++), "%lu",
436 (unsigned long *)TmpArray.
437 IntArray[NumOfPrm++]);
440 TmpArray.LngArray[NumOfPrm] = xmalloc(sizeof(long));
441 ScanRes = sscanf(*((*argv)++), "%lo",
442 (unsigned long *)TmpArray.
443 IntArray[NumOfPrm++]);
446 TmpArray.LngArray[NumOfPrm] = xmalloc(sizeof(long));
447 ScanRes = sscanf(*((*argv)++), "%lx",
448 (unsigned long *)TmpArray.
449 IntArray[NumOfPrm++]);
452 TmpArray.FltArray[NumOfPrm] = xmalloc(sizeof(float));
453 ScanRes = sscanf(*((*argv)++), "%f",
454 // cppcheck-suppress invalidPointerCast
455 (float *)TmpArray.LngArray[NumOfPrm++]);
458 TmpArray.DblArray[NumOfPrm] = xmalloc(sizeof(double));
459 ScanRes = sscanf(*((*argv)++), "%lf",
460 // cppcheck-suppress invalidPointerCast
461 (double *)TmpArray.LngArray[NumOfPrm++]);
464 while ((*argv < argv_end) && ((**argv)[0] != '-')) {
465 TmpArray.ChrArray[NumOfPrm++] = *((*argv)++);
467 ScanRes = 0; /* Force quit from do - loop. */
468 NumOfPrm++; /* Updated again immediately after loop! */
472 ScanRes = 0; /* Make optimizer warning silent. */
475 while (ScanRes == 1); /* Exactly one parameter was read. */
479 /* Now allocate the block with the exact size, and set it: */
480 Ptemp = Pmain = xmalloc((unsigned)(NumOfPrm + 1) * sizeof(void *));
481 /* And here we use the assumption that all pointers are the same: */
482 for (i = 0; i < NumOfPrm; i++)
483 *Ptemp++ = TmpArray.VoidArray[i];
484 *Ptemp = NULL; /* Close the block with NULL pointer. */
486 /* That it save the number of parameters read as first parameter to
487 * return and the pointer to the block as second, and return: */
488 *(int *)Parameters[(*ParamCount)++] = NumOfPrm;
489 *(void ***)Parameters[(*ParamCount)++] = Pmain;
490 /* free(Pmain); -- can not free here as caller needs to access memory */
494 /***************************************************************************
495 Routine to scan the CtrlStr, up to Max and count the number of parameters
497 1. Each option is counted as one parameter - boolean variable (int)
498 2. Within an option, each %? or !? is counted once - pointer to something
499 3. Within an option, %*? or !*? is counted twice - one for item count
500 and one for pointer to block pointers.
501 Note ALL variables are passed by address and so of fixed size (address).
502 ***************************************************************************/
504 GASetParamCount(char *CtrlStr,
510 for (i = 0; i < Max; i++)
511 if (ISCTRLCHAR(CtrlStr[i])) {
512 if (CtrlStr[i + 1] == '*')
519 /***************************************************************************
520 Routine to check if more option (i.e. first char == '-') exists in the
521 given list argc, argv:
522 ***************************************************************************/
524 GAOptionExists(char **argv_end,
527 while (argv < argv_end)
528 if ((*argv++)[0] == '-')
533 /***************************************************************************
534 Routine to print some error messages, for this module:
535 ***************************************************************************/
537 GAPrintErrMsg(int Error) {
539 fprintf(stderr, "Error in command line parsing - ");
542 fprintf(stderr, "Undefined error");
544 case CMD_ERR_NotAnOpt:
545 fprintf(stderr, "None option Found");
547 case CMD_ERR_NoSuchOpt:
548 fprintf(stderr, "Undefined option Found");
550 case CMD_ERR_WildEmpty:
551 fprintf(stderr, "Empty input for '!*?' seq.");
553 case CMD_ERR_NumRead:
554 fprintf(stderr, "Failed on reading number");
556 case CMD_ERR_AllSatis:
557 fprintf(stderr, "Fail to satisfy");
560 fprintf(stderr, " - '%s'.\n", GAErrorToken);
563 /***************************************************************************
564 Routine to print correct format of command line allowed:
565 ***************************************************************************/
567 GAPrintHowTo(char *CtrlStr) {
572 fprintf(stderr, "Usage: ");
573 /* Print program name - first word in ctrl. str. (optional): */
574 while (!(ISSPACE(CtrlStr[i])) && (!ISCTRLCHAR(CtrlStr[i + 1])))
575 fprintf(stderr, "%c", CtrlStr[i++]);
577 while (i < (int)strlen(CtrlStr)) {
578 // cppcheck-suppress arrayIndexThenCheck
579 while ((ISSPACE(CtrlStr[i])) && (i < (int)strlen(CtrlStr)))
581 switch (CtrlStr[i + 1]) {
583 fprintf(stderr, " [-%c", CtrlStr[i++]);
584 i += 2; /* Skip the '%-' or '!- after the char! */
586 while (!ISCTRLCHAR(CtrlStr[i]) && (i < (int)strlen(CtrlStr)) &&
587 (!ISSPACE(CtrlStr[i])))
589 if (CtrlStr[i++] == SPACE_CHAR)
590 fprintf(stderr, " ");
592 fprintf(stderr, " %c", CtrlStr[i - 1]);
594 } else if (CtrlStr[i++] == SPACE_CHAR)
595 fprintf(stderr, " ");
597 fprintf(stderr, "%c", CtrlStr[i - 1]);
598 while (!ISSPACE(CtrlStr[i]) && (i < (int)strlen(CtrlStr))) {
599 if (CtrlStr[i] == '*')
600 fprintf(stderr, "...");
601 i++; /* Skip the rest of it. */
603 fprintf(stderr, "]");
606 fprintf(stderr, " -%c", CtrlStr[i++]);
607 i += 2; /* Skip the '%-' or '!- after the char! */
609 while (!ISCTRLCHAR(CtrlStr[i]) && (i < (int)strlen(CtrlStr)) &&
610 (!ISSPACE(CtrlStr[i])))
612 if (CtrlStr[i++] == SPACE_CHAR)
613 fprintf(stderr, " ");
615 fprintf(stderr, " %c", CtrlStr[i - 1]);
617 } else if (CtrlStr[i++] == SPACE_CHAR)
618 fprintf(stderr, " ");
620 fprintf(stderr, "%c", CtrlStr[i - 1]);
621 while (!ISSPACE(CtrlStr[i]) && (i < (int)strlen(CtrlStr))) {
622 if (CtrlStr[i] == '*')
623 fprintf(stderr, "...");
624 i++; /* Skip the rest of it. */
627 default: /* Not checked, but must be last one! */
628 fprintf(stderr, " ");
629 while (!ISSPACE(CtrlStr[i]) && (i < (int)strlen(CtrlStr)) &&
630 !ISCTRLCHAR(CtrlStr[i]))
631 fprintf(stderr, "%c", CtrlStr[i++]);
632 fprintf(stderr, "\n");
636 fprintf(stderr, "\n");