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