xref: /aosp_15_r20/external/giflib/getarg.c (revision 324bb76b8d05e2a05aa88511fff61cf3f9ca5892)
1 /***************************************************************************
2 
3 getarg.c - routines to grab the parameters from the command line:
4 
5 Names of all the routines except the main one start with GA (Get
6 Arguments) to prevent conflicts.
7 
8 The following routines are available in this module:
9 
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).
16 
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).
22 
23 3. GAPrintErrMsg(Error)
24 Describe the error to stderr, according to Error (usually returned by
25 GAGetArgs).
26 
27 CtrlStr format:
28 
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:
34 
35 1. One letter which sets the option letter.
36 2. '!' or '%' to determines if this option is really optional ('%') or
37    required ('!')...
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.
54 
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.
58 
59 The variables passed to GAGetArgs (starting from 4th parameter) MUST
60 match the order of the CtrlStr:
61 
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
64 be set.
65 
66 In addition, the sequences that might follow an option require the
67 following parameters to pass:
68 
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
81    memory.
82 
83 Note that these two variables are pretty like the argv/argc pair...
84 
85 Examples:
86 
87 "Example1 i%-OneInteger!d s%-Strings!*s j%- k!-Float!f Files"
88 
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).
92 
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
95 in the command line.
96 
97 A call to GAPrintHowTo with this CtrlStr will print to stderr:
98 Example1 [-i OneIngeter] [-s Strings...] [-j] -k Float Files...
99 
100 Notes:
101 
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 *).
104 
105 SPDX-License-Identifier: MIT
106 
107 **************************************************************************/
108 
109 #include <stdarg.h>
110 #include <stdbool.h>
111 #include <stdio.h>
112 #include <stdlib.h>
113 #include <string.h>
114 
115 #include "getarg.h"
116 
117 #define MAX_PARAM 100 /* maximum number of parameters allowed. */
118 #define CTRL_STR_MAX_LEN 1024
119 
120 #define SPACE_CHAR '|' /* The character not to print using HowTo. */
121 
122 #define ARG_OK false
123 
124 #define ISSPACE(x) ((x) <= ' ') /* Not conventional - but works fine! */
125 
126 /* The two characters '%' and '!' are used in the control string: */
127 #define ISCTRLCHAR(x) (((x) == '%') || ((x) == '!'))
128 
129 static char *GAErrorToken; /* On error, ErrorToken is set to point to it. */
130 static int GATestAllSatis(char *CtrlStrCopy, char *CtrlStr,
131                           const char **argv_end, const char ***argv,
132                           void *Parameters[MAX_PARAM], int *ParamCount);
133 static int GAUpdateParameters(void *Parameters[], int *ParamCount, char *Option,
134                               char *CtrlStrCopy, char *CtrlStr, char **argv_end,
135                               char ***argv);
136 static int GAGetParmeters(void *Parameters[], int *ParamCount,
137                           char *CtrlStrCopy, char *Option, char **argv_end,
138                           char ***argv);
139 static int GAGetMultiParmeters(void *Parameters[], int *ParamCount,
140                                char *CtrlStrCopy, char **argv_end,
141                                char ***argv);
142 static void GASetParamCount(const char *CtrlStr, const int Max,
143                             int *ParamCount);
144 static bool GAOptionExists(const char **argv_end, const char **argv);
145 
146 /***************************************************************************
147  Allocate or die
148 ***************************************************************************/
xmalloc(unsigned size)149 static void *xmalloc(unsigned size) {
150 
151 	void *p;
152 
153 	if ((p = malloc(size)) != NULL) {
154 		return p;
155 	}
156 
157 	fprintf(stderr, "Not enough memory, exit.\n");
158 	exit(2);
159 
160 	return NULL; /* Makes warning silent. */
161 }
162 /***************************************************************************
163  Routine to access the command line argument and interpret them:
164  Return ARG_OK (0) is case of successful parsing, error code else...
165 ***************************************************************************/
GAGetArgs(int argc,char ** argv,char * CtrlStr,...)166 bool GAGetArgs(int argc, char **argv, char *CtrlStr, ...) {
167 
168 	int i, ParamCount = 0;
169 	void *Parameters[MAX_PARAM]; /* Save here parameter addresses. */
170 	char CtrlStrCopy[CTRL_STR_MAX_LEN];
171 	const char **argv_end = (const char **)argv + argc;
172 	va_list ap;
173 
174 	strncpy(CtrlStrCopy, CtrlStr, sizeof(CtrlStrCopy) - 1);
175 	GASetParamCount(CtrlStr, strlen(CtrlStr), &ParamCount);
176 	va_start(ap, CtrlStr);
177 	for (i = 1; i <= ParamCount; i++) {
178 		Parameters[i - 1] = va_arg(ap, void *);
179 	}
180 	va_end(ap);
181 
182 	argv++; /* Skip the program name (first in argv/c list). */
183 	while (argv < (char **)argv_end) {
184 		bool Error = false;
185 		if (!GAOptionExists(argv_end, (const char **)argv)) {
186 			break; /* The loop. */
187 		}
188 		char *Option = *argv++;
189 		if ((Error = GAUpdateParameters(
190 		         Parameters, &ParamCount, Option, CtrlStrCopy, CtrlStr,
191 		         (char **)argv_end, &argv)) != false) {
192 			return Error;
193 		}
194 	}
195 	/* Check for results and update trail of command line: */
196 	return GATestAllSatis(CtrlStrCopy, CtrlStr, argv_end,
197 	                      (const char ***)&argv, Parameters,
198 	                      &ParamCount) != ARG_OK;
199 }
200 
201 /***************************************************************************
202  Routine to search for unsatisfied flags - simply scan the list for !-
203  sequence. Before this scan, this routine updates the rest of the command
204  line into the last two parameters if it is requested by the CtrlStr
205  (last item in CtrlStr is NOT an option).
206  Return ARG_OK if all satisfied, CMD_ERR_AllSatis error else.
207 ***************************************************************************/
GATestAllSatis(char * CtrlStrCopy,char * CtrlStr,const char ** argv_end,const char *** argv,void * Parameters[MAX_PARAM],int * ParamCount)208 static int GATestAllSatis(char *CtrlStrCopy, char *CtrlStr,
209                           const char **argv_end, const char ***argv,
210                           void *Parameters[MAX_PARAM], int *ParamCount) {
211 
212 	int i;
213 	static char *LocalToken = NULL;
214 
215 	/* If LocalToken is not initialized - do it now. Note that this string
216 	 * should be writable as well so we can not assign it directly.
217 	 */
218 	if (LocalToken == NULL) {
219 		LocalToken = (char *)malloc(3);
220 		strcpy(LocalToken, "-?");
221 	}
222 
223 	/* Check if last item is an option. If not then copy rest of command
224 	 * line into it as 1. NumOfprm, 2. pointer to block of pointers.
225 	 */
226 	for (i = strlen(CtrlStr) - 1; i > 0 && !ISSPACE(CtrlStr[i]); i--) {
227 		;
228 	}
229 	if (!ISCTRLCHAR(CtrlStr[i + 2])) {
230 		GASetParamCount(CtrlStr, i,
231 		                ParamCount); /* Point in correct param. */
232 		*(int *)Parameters[(*ParamCount)++] = argv_end - *argv;
233 		*(char ***)Parameters[(*ParamCount)++] = *(char ***)argv;
234 	}
235 
236 	i = 0;
237 	while (++i < (int)strlen(CtrlStrCopy)) {
238 		if ((CtrlStrCopy[i] == '-') && (CtrlStrCopy[i - 1] == '!')) {
239 			GAErrorToken = LocalToken;
240 			LocalToken[1] =
241 			    CtrlStrCopy[i - 2]; /* Set the correct flag. */
242 			return CMD_ERR_AllSatis;
243 		}
244 	}
245 
246 	return ARG_OK;
247 }
248 
249 /***************************************************************************
250  Routine to update the parameters according to the given Option:
251  **************************************************************************/
GAUpdateParameters(void * Parameters[],int * ParamCount,char * Option,char * CtrlStrCopy,char * CtrlStr,char ** argv_end,char *** argv)252 static int GAUpdateParameters(void *Parameters[], int *ParamCount, char *Option,
253                               char *CtrlStrCopy, char *CtrlStr, char **argv_end,
254                               char ***argv) {
255 
256 	int i;
257 	bool BooleanTrue = Option[2] != '-';
258 
259 	if (Option[0] != '-') {
260 		GAErrorToken = Option;
261 		return CMD_ERR_NotAnOpt;
262 	}
263 	i = 0; /* Scan the CtrlStrCopy for that option: */
264 	while (i + 2 < (int)strlen(CtrlStrCopy)) {
265 		if ((CtrlStrCopy[i] == Option[1]) &&
266 		    (ISCTRLCHAR(CtrlStrCopy[i + 1])) &&
267 		    (CtrlStrCopy[i + 2] == '-')) {
268 			/* We found that option! */
269 			break;
270 		}
271 		i++;
272 	}
273 	if (i + 2 >= (int)strlen(CtrlStrCopy)) {
274 		GAErrorToken = Option;
275 		return CMD_ERR_NoSuchOpt;
276 	}
277 
278 	/* If we are here, then we found that option in CtrlStr - Strip it off:
279 	 */
280 	CtrlStrCopy[i] = CtrlStrCopy[i + 1] = CtrlStrCopy[i + 2] = (char)' ';
281 	GASetParamCount(CtrlStr, i, ParamCount); /* Set it to point in
282 	                                            correct prm. */
283 	i += 3;
284 	/* Set boolean flag for that option. */
285 	*(bool *)Parameters[(*ParamCount)++] = BooleanTrue;
286 	if (ISSPACE(CtrlStrCopy[i])) {
287 		return ARG_OK; /* Only a boolean flag is needed. */
288 	}
289 	/* Skip the text between the boolean option and data follows: */
290 	while (!ISCTRLCHAR(CtrlStrCopy[i])) {
291 		i++;
292 	}
293 	/* Get the parameters and return the appropriete return code: */
294 	return GAGetParmeters(Parameters, ParamCount, &CtrlStrCopy[i], Option,
295 	                      argv_end, argv);
296 }
297 
298 /***************************************************************************
299  Routine to get parameters according to the CtrlStr given from argv/argc
300 ***************************************************************************/
GAGetParmeters(void * Parameters[],int * ParamCount,char * CtrlStrCopy,char * Option,char ** argv_end,char *** argv)301 static int GAGetParmeters(void *Parameters[], int *ParamCount,
302                           char *CtrlStrCopy, char *Option, char **argv_end,
303                           char ***argv) {
304 
305 	int i = 0, ScanRes;
306 
307 	while (!(ISSPACE(CtrlStrCopy[i]))) {
308 
309 		if ((*argv) == argv_end) {
310 			GAErrorToken = Option;
311 			return CMD_ERR_NumRead;
312 		}
313 
314 		switch (CtrlStrCopy[i + 1]) {
315 		case 'd': /* Get signed integers. */
316 			ScanRes = sscanf(*((*argv)++), "%d",
317 			                 (int *)Parameters[(*ParamCount)++]);
318 			break;
319 		case 'u': /* Get unsigned integers. */
320 			ScanRes =
321 			    sscanf(*((*argv)++), "%u",
322 			           (unsigned *)Parameters[(*ParamCount)++]);
323 			break;
324 		case 'x': /* Get hex integers. */
325 			ScanRes =
326 			    sscanf(*((*argv)++), "%x",
327 			           (unsigned int *)Parameters[(*ParamCount)++]);
328 			break;
329 		case 'o': /* Get octal integers. */
330 			ScanRes =
331 			    sscanf(*((*argv)++), "%o",
332 			           (unsigned int *)Parameters[(*ParamCount)++]);
333 			break;
334 		case 'D': /* Get signed long integers. */
335 			ScanRes = sscanf(*((*argv)++), "%ld",
336 			                 (long *)Parameters[(*ParamCount)++]);
337 			break;
338 		case 'U': /* Get unsigned long integers. */
339 			ScanRes = sscanf(
340 			    *((*argv)++), "%lu",
341 			    (unsigned long *)Parameters[(*ParamCount)++]);
342 			break;
343 		case 'X': /* Get hex long integers. */
344 			ScanRes = sscanf(
345 			    *((*argv)++), "%lx",
346 			    (unsigned long *)Parameters[(*ParamCount)++]);
347 			break;
348 		case 'O': /* Get octal long integers. */
349 			ScanRes = sscanf(
350 			    *((*argv)++), "%lo",
351 			    (unsigned long *)Parameters[(*ParamCount)++]);
352 			break;
353 		case 'f': /* Get float number. */
354 			ScanRes = sscanf(*((*argv)++), "%f",
355 			                 (float *)Parameters[(*ParamCount)++]);
356 			break;
357 		case 'F': /* Get double float number. */
358 			ScanRes = sscanf(*((*argv)++), "%lf",
359 			                 (double *)Parameters[(*ParamCount)++]);
360 			break;
361 		case 's':            /* It as a string. */
362 			ScanRes = 1; /* Allways O.K. */
363 			*(char **)Parameters[(*ParamCount)++] = *((*argv)++);
364 			break;
365 		case '*': /* Get few parameters into one: */
366 			ScanRes = GAGetMultiParmeters(Parameters, ParamCount,
367 			                              &CtrlStrCopy[i], argv_end,
368 			                              argv);
369 			if ((ScanRes == 0) && (CtrlStrCopy[i] == '!')) {
370 				GAErrorToken = Option;
371 				return CMD_ERR_WildEmpty;
372 			}
373 			break;
374 		default:
375 			ScanRes = 0; /* Make optimizer warning silent. */
376 		}
377 		/* If reading fails and this number is a must (!) then error: */
378 		if ((ScanRes == 0) && (CtrlStrCopy[i] == '!')) {
379 			GAErrorToken = Option;
380 			return CMD_ERR_NumRead;
381 		}
382 		if (CtrlStrCopy[i + 1] != '*') {
383 			i += 2; /* Skip to next parameter (if any). */
384 		} else {
385 			i += 3; /* Skip the '*' also! */
386 		}
387 	}
388 
389 	return ARG_OK;
390 }
391 
392 /***************************************************************************
393  Routine to get a few parameters into one pointer such that the returned
394  pointer actually points on a block of pointers to the parameters...
395  For example *F means a pointer to pointers on floats.
396  Returns number of parameters actually read.
397  This routine assumes that all pointers (on any kind of scalar) has the
398  same size (and the union below is totally ovelapped bteween dif. arrays)
399 ***************************************************************************/
GAGetMultiParmeters(void * Parameters[],int * ParamCount,char * CtrlStrCopy,char ** argv_end,char *** argv)400 static int GAGetMultiParmeters(void *Parameters[], int *ParamCount,
401                                char *CtrlStrCopy, char **argv_end,
402                                char ***argv) {
403 
404 	int i = 0, ScanRes, NumOfPrm = 0;
405 	void **Pmain, **Ptemp;
406 	union TmpArray { /* Save here the temporary data before copying it to */
407 		void *VoidArray[MAX_PARAM]; /* the returned pointer block. */
408 		int *IntArray[MAX_PARAM];
409 		long *LngArray[MAX_PARAM];
410 		float *FltArray[MAX_PARAM];
411 		double *DblArray[MAX_PARAM];
412 		char *ChrArray[MAX_PARAM];
413 	} TmpArray;
414 
415 	do {
416 		switch (CtrlStrCopy[2]) { /* CtrlStr == '!*?' or '%*?' where ?
417 			                     is. */
418 		case 'd':                 /* Format to read the parameters: */
419 			TmpArray.IntArray[NumOfPrm] = xmalloc(sizeof(int));
420 			ScanRes = sscanf(*((*argv)++), "%d",
421 			                 (int *)TmpArray.IntArray[NumOfPrm++]);
422 			break;
423 		case 'u':
424 			TmpArray.IntArray[NumOfPrm] = xmalloc(sizeof(int));
425 			ScanRes = sscanf(
426 			    *((*argv)++), "%u",
427 			    (unsigned int *)TmpArray.IntArray[NumOfPrm++]);
428 			break;
429 		case 'o':
430 			TmpArray.IntArray[NumOfPrm] = xmalloc(sizeof(int));
431 			ScanRes = sscanf(
432 			    *((*argv)++), "%o",
433 			    (unsigned int *)TmpArray.IntArray[NumOfPrm++]);
434 			break;
435 		case 'x':
436 			TmpArray.IntArray[NumOfPrm] = xmalloc(sizeof(int));
437 			ScanRes = sscanf(
438 			    *((*argv)++), "%x",
439 			    (unsigned int *)TmpArray.IntArray[NumOfPrm++]);
440 			break;
441 		case 'D':
442 			TmpArray.LngArray[NumOfPrm] = xmalloc(sizeof(long));
443 			ScanRes = sscanf(*((*argv)++), "%ld",
444 			                 (long *)TmpArray.IntArray[NumOfPrm++]);
445 			break;
446 		case 'U':
447 			TmpArray.LngArray[NumOfPrm] = xmalloc(sizeof(long));
448 			ScanRes = sscanf(
449 			    *((*argv)++), "%lu",
450 			    (unsigned long *)TmpArray.IntArray[NumOfPrm++]);
451 			break;
452 		case 'O':
453 			TmpArray.LngArray[NumOfPrm] = xmalloc(sizeof(long));
454 			ScanRes = sscanf(
455 			    *((*argv)++), "%lo",
456 			    (unsigned long *)TmpArray.IntArray[NumOfPrm++]);
457 			break;
458 		case 'X':
459 			TmpArray.LngArray[NumOfPrm] = xmalloc(sizeof(long));
460 			ScanRes = sscanf(
461 			    *((*argv)++), "%lx",
462 			    (unsigned long *)TmpArray.IntArray[NumOfPrm++]);
463 			break;
464 		case 'f':
465 			TmpArray.FltArray[NumOfPrm] = xmalloc(sizeof(float));
466 			ScanRes =
467 			    sscanf(*((*argv)++), "%f",
468 			           // cppcheck-suppress invalidPointerCast
469 			           (float *)TmpArray.LngArray[NumOfPrm++]);
470 			break;
471 		case 'F':
472 			TmpArray.DblArray[NumOfPrm] = xmalloc(sizeof(double));
473 			ScanRes =
474 			    sscanf(*((*argv)++), "%lf",
475 			           // cppcheck-suppress invalidPointerCast
476 			           (double *)TmpArray.LngArray[NumOfPrm++]);
477 			break;
478 		case 's':
479 			while ((*argv < argv_end) && ((**argv)[0] != '-')) {
480 				TmpArray.ChrArray[NumOfPrm++] = *((*argv)++);
481 			}
482 			ScanRes = 0; /* Force quit from do - loop. */
483 			NumOfPrm++;  /* Updated again immediately after loop! */
484 			(*argv)++;   /* "" */
485 			break;
486 		default:
487 			ScanRes = 0; /* Make optimizer warning silent. */
488 		}
489 	} while (ScanRes == 1); /* Exactly one parameter was read. */
490 	(*argv)--;
491 	NumOfPrm--;
492 
493 	/* Now allocate the block with the exact size, and set it: */
494 	Ptemp = Pmain = xmalloc((unsigned)(NumOfPrm + 1) * sizeof(void *));
495 	/* And here we use the assumption that all pointers are the same: */
496 	for (i = 0; i < NumOfPrm; i++) {
497 		*Ptemp++ = TmpArray.VoidArray[i];
498 	}
499 	*Ptemp = NULL; /* Close the block with NULL pointer. */
500 
501 	/* That it save the number of parameters read as first parameter to
502 	 * return and the pointer to the block as second, and return: */
503 	*(int *)Parameters[(*ParamCount)++] = NumOfPrm;
504 	*(void ***)Parameters[(*ParamCount)++] = Pmain;
505 	/* free(Pmain); -- can not free here as caller needs to access memory */
506 	return NumOfPrm;
507 }
508 
509 /***************************************************************************
510  Routine to scan the CtrlStr, up to Max and count the number of parameters
511  to that point:
512  1. Each option is counted as one parameter - boolean variable (int)
513  2. Within an option, each %? or !? is counted once - pointer to something
514  3. Within an option, %*? or !*? is counted twice - one for item count
515  and one for pointer to block pointers.
516  Note ALL variables are passed by address and so of fixed size (address).
517 ***************************************************************************/
GASetParamCount(char const * CtrlStr,const int Max,int * ParamCount)518 static void GASetParamCount(char const *CtrlStr, const int Max,
519                             int *ParamCount) {
520 	int i;
521 
522 	*ParamCount = 0;
523 	for (i = 0; i < Max; i++) {
524 		if (ISCTRLCHAR(CtrlStr[i])) {
525 			if (CtrlStr[i + 1] == '*') {
526 				*ParamCount += 2;
527 			} else {
528 				(*ParamCount)++;
529 			}
530 		}
531 	}
532 }
533 
534 /***************************************************************************
535  Routine to check if more option (i.e. first char == '-') exists in the
536  given list argc, argv:
537 ***************************************************************************/
GAOptionExists(const char ** argv_end,const char ** argv)538 static bool GAOptionExists(const char **argv_end, const char **argv) {
539 
540 	while (argv < argv_end) {
541 		if ((*argv++)[0] == '-') {
542 			return true;
543 		}
544 	}
545 	return false;
546 }
547 
548 /***************************************************************************
549  Routine to print some error messages, for this module:
550 ***************************************************************************/
GAPrintErrMsg(int Error)551 void GAPrintErrMsg(int Error) {
552 
553 	fprintf(stderr, "Error in command line parsing - ");
554 	switch (Error) {
555 	case 0:;
556 		fprintf(stderr, "Undefined error");
557 		break;
558 	case CMD_ERR_NotAnOpt:
559 		fprintf(stderr, "None option Found");
560 		break;
561 	case CMD_ERR_NoSuchOpt:
562 		fprintf(stderr, "Undefined option Found");
563 		break;
564 	case CMD_ERR_WildEmpty:
565 		fprintf(stderr, "Empty input for '!*?' seq.");
566 		break;
567 	case CMD_ERR_NumRead:
568 		fprintf(stderr, "Failed on reading number");
569 		break;
570 	case CMD_ERR_AllSatis:
571 		fprintf(stderr, "Fail to satisfy");
572 		break;
573 	}
574 	fprintf(stderr, " - '%s'.\n", GAErrorToken);
575 }
576 
577 /***************************************************************************
578  Routine to print correct format of command line allowed:
579 ***************************************************************************/
GAPrintHowTo(char * CtrlStr)580 void GAPrintHowTo(char *CtrlStr) {
581 
582 	int i = 0;
583 	bool SpaceFlag;
584 
585 	fprintf(stderr, "Usage: ");
586 	/* Print program name - first word in ctrl. str. (optional): */
587 	while (!(ISSPACE(CtrlStr[i])) && (!ISCTRLCHAR(CtrlStr[i + 1]))) {
588 		fprintf(stderr, "%c", CtrlStr[i++]);
589 	}
590 
591 	while (i < (int)strlen(CtrlStr)) {
592 		// cppcheck-suppress arrayIndexThenCheck
593 		while ((ISSPACE(CtrlStr[i])) && (i < (int)strlen(CtrlStr))) {
594 			i++;
595 		}
596 		switch (CtrlStr[i + 1]) {
597 		case '%':
598 			fprintf(stderr, " [-%c", CtrlStr[i++]);
599 			i += 2; /* Skip the '%-' or '!- after the char! */
600 			SpaceFlag = true;
601 			while (!ISCTRLCHAR(CtrlStr[i]) &&
602 			       (i < (int)strlen(CtrlStr)) &&
603 			       (!ISSPACE(CtrlStr[i]))) {
604 				if (SpaceFlag) {
605 					if (CtrlStr[i++] == SPACE_CHAR) {
606 						fprintf(stderr, " ");
607 					} else {
608 						fprintf(stderr, " %c",
609 						        CtrlStr[i - 1]);
610 					}
611 					SpaceFlag = false;
612 				} else if (CtrlStr[i++] == SPACE_CHAR) {
613 					fprintf(stderr, " ");
614 				} else {
615 					fprintf(stderr, "%c", CtrlStr[i - 1]);
616 				}
617 			}
618 			while (!ISSPACE(CtrlStr[i]) &&
619 			       (i < (int)strlen(CtrlStr))) {
620 				if (CtrlStr[i] == '*') {
621 					fprintf(stderr, "...");
622 				}
623 				i++; /* Skip the rest of it. */
624 			}
625 			fprintf(stderr, "]");
626 			break;
627 		case '!':
628 			fprintf(stderr, " -%c", CtrlStr[i++]);
629 			i += 2; /* Skip the '%-' or '!- after the char! */
630 			SpaceFlag = true;
631 			while (!ISCTRLCHAR(CtrlStr[i]) &&
632 			       (i < (int)strlen(CtrlStr)) &&
633 			       (!ISSPACE(CtrlStr[i]))) {
634 				if (SpaceFlag) {
635 					if (CtrlStr[i++] == SPACE_CHAR) {
636 						fprintf(stderr, " ");
637 					} else {
638 						fprintf(stderr, " %c",
639 						        CtrlStr[i - 1]);
640 					}
641 					SpaceFlag = false;
642 				} else if (CtrlStr[i++] == SPACE_CHAR) {
643 					fprintf(stderr, " ");
644 				} else {
645 					fprintf(stderr, "%c", CtrlStr[i - 1]);
646 				}
647 			}
648 			while (!ISSPACE(CtrlStr[i]) &&
649 			       (i < (int)strlen(CtrlStr))) {
650 				if (CtrlStr[i] == '*') {
651 					fprintf(stderr, "...");
652 				}
653 				i++; /* Skip the rest of it. */
654 			}
655 			break;
656 		default: /* Not checked, but must be last one! */
657 			fprintf(stderr, " ");
658 			while (!ISSPACE(CtrlStr[i]) &&
659 			       (i < (int)strlen(CtrlStr)) &&
660 			       !ISCTRLCHAR(CtrlStr[i])) {
661 				fprintf(stderr, "%c", CtrlStr[i++]);
662 			}
663 			fprintf(stderr, "\n");
664 			return;
665 		}
666 	}
667 	fprintf(stderr, "\n");
668 }
669 
670 /* end */
671