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 <stdlib.h>
110 #include <stdio.h>
111 #include <string.h>
112 #include <stdbool.h>
113 #include <stdarg.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, char **argv_end,
131 char ***argv, void *Parameters[MAX_PARAM],
132 int *ParamCount);
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,
138 char ***argv);
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);
143
144 /***************************************************************************
145 Allocate or die
146 ***************************************************************************/
147 static void *
xmalloc(unsigned size)148 xmalloc(unsigned size) {
149
150 void *p;
151
152 if ((p = malloc(size)) != NULL)
153 return p;
154
155 fprintf(stderr, "Not enough memory, exit.\n");
156 exit(2);
157
158 return NULL; /* Makes warning silent. */
159 }
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 ***************************************************************************/
164 bool
GAGetArgs(int argc,char ** argv,char * CtrlStr,...)165 GAGetArgs(int argc,
166 char **argv,
167 char *CtrlStr, ...) {
168
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;
173 va_list ap;
174
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 *);
180 va_end(ap);
181
182 argv++; /* Skip the program name (first in argv/c list). */
183 while (argv < argv_end) {
184 bool Error = false;
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,
190 &argv)) != false)
191 return Error;
192 }
193 /* Check for results and update trail of command line: */
194 return GATestAllSatis(CtrlStrCopy, CtrlStr, argv_end, &argv, Parameters,
195 &ParamCount) != ARG_OK;
196 }
197
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 ***************************************************************************/
205 static int
GATestAllSatis(char * CtrlStrCopy,char * CtrlStr,char ** argv_end,char *** argv,void * Parameters[MAX_PARAM],int * ParamCount)206 GATestAllSatis(char *CtrlStrCopy,
207 char *CtrlStr,
208 char **argv_end,
209 char ***argv,
210 void *Parameters[MAX_PARAM],
211 int *ParamCount) {
212
213 int i;
214 static char *LocalToken = NULL;
215
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.
218 */
219 if (LocalToken == NULL) {
220 LocalToken = (char *)malloc(3);
221 strcpy(LocalToken, "-?");
222 }
223
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.
226 */
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;
232 }
233
234 i = 0;
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;
240 }
241
242 return ARG_OK;
243 }
244
245 /***************************************************************************
246 Routine to update the parameters according to the given Option:
247 **************************************************************************/
248 static bool
GAUpdateParameters(void * Parameters[],int * ParamCount,char * Option,char * CtrlStrCopy,char * CtrlStr,char ** argv_end,char *** argv)249 GAUpdateParameters(void *Parameters[],
250 int *ParamCount,
251 char *Option,
252 char *CtrlStrCopy,
253 char *CtrlStr,
254 char **argv_end,
255 char ***argv) {
256
257 int i;
258 bool BooleanTrue = Option[2] != '-';
259
260 if (Option[0] != '-') {
261 GAErrorToken = Option;
262 return CMD_ERR_NotAnOpt;
263 }
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! */
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 CtrlStrCopy[i] = CtrlStrCopy[i + 1] = CtrlStrCopy[i + 2] = (char)' ';
280 GASetParamCount(CtrlStr, i, ParamCount); /* Set it to point in
281 correct prm. */
282 i += 3;
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. */
287
288 /* Skip the text between the boolean option and data follows: */
289 while (!ISCTRLCHAR(CtrlStrCopy[i]))
290 i++;
291 /* Get the parameters and return the appropriete return code: */
292 return GAGetParmeters(Parameters, ParamCount, &CtrlStrCopy[i],
293 Option, argv_end, argv);
294 }
295
296 /***************************************************************************
297 Routine to get parameters according to the CtrlStr given from argv/argc
298 ***************************************************************************/
299 static int
GAGetParmeters(void * Parameters[],int * ParamCount,char * CtrlStrCopy,char * Option,char ** argv_end,char *** argv)300 GAGetParmeters(void *Parameters[],
301 int *ParamCount,
302 char *CtrlStrCopy,
303 char *Option,
304 char **argv_end,
305 char ***argv) {
306
307 int i = 0, ScanRes;
308
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)++]);
314 break;
315 case 'u': /* Get unsigned integers. */
316 ScanRes = sscanf(*((*argv)++), "%u",
317 (unsigned *)Parameters[(*ParamCount)++]);
318 break;
319 case 'x': /* Get hex integers. */
320 ScanRes = sscanf(*((*argv)++), "%x",
321 (unsigned int *)Parameters[(*ParamCount)++]);
322 break;
323 case 'o': /* Get octal integers. */
324 ScanRes = sscanf(*((*argv)++), "%o",
325 (unsigned int *)Parameters[(*ParamCount)++]);
326 break;
327 case 'D': /* Get signed long integers. */
328 ScanRes = sscanf(*((*argv)++), "%ld",
329 (long *)Parameters[(*ParamCount)++]);
330 break;
331 case 'U': /* Get unsigned long integers. */
332 ScanRes = sscanf(*((*argv)++), "%lu",
333 (unsigned long *)Parameters[(*ParamCount)++]);
334 break;
335 case 'X': /* Get hex long integers. */
336 ScanRes = sscanf(*((*argv)++), "%lx",
337 (unsigned long *)Parameters[(*ParamCount)++]);
338 break;
339 case 'O': /* Get octal long integers. */
340 ScanRes = sscanf(*((*argv)++), "%lo",
341 (unsigned long *)Parameters[(*ParamCount)++]);
342 break;
343 case 'f': /* Get float number. */
344 ScanRes = sscanf(*((*argv)++), "%f",
345 (float *)Parameters[(*ParamCount)++]);
346 break;
347 case 'F': /* Get double float number. */
348 ScanRes = sscanf(*((*argv)++), "%lf",
349 (double *)Parameters[(*ParamCount)++]);
350 break;
351 case 's': /* It as a string. */
352 ScanRes = 1; /* Allways O.K. */
353 *(char **)Parameters[(*ParamCount)++] = *((*argv)++);
354 break;
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;
361 }
362 break;
363 default:
364 ScanRes = 0; /* Make optimizer warning silent. */
365 }
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;
370 }
371 if (CtrlStrCopy[i + 1] != '*') {
372 i += 2; /* Skip to next parameter (if any). */
373 } else
374 i += 3; /* Skip the '*' also! */
375 }
376
377 return ARG_OK;
378 }
379
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 ***************************************************************************/
388 static int
GAGetMultiParmeters(void * Parameters[],int * ParamCount,char * CtrlStrCopy,char ** argv_end,char *** argv)389 GAGetMultiParmeters(void *Parameters[],
390 int *ParamCount,
391 char *CtrlStrCopy,
392 char **argv_end,
393 char ***argv) {
394
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];
404 } TmpArray;
405
406 do {
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++]);
412 break;
413 case 'u':
414 TmpArray.IntArray[NumOfPrm] = xmalloc(sizeof(int));
415 ScanRes = sscanf(*((*argv)++), "%u",
416 (unsigned int *)TmpArray.IntArray[NumOfPrm++]);
417 break;
418 case 'o':
419 TmpArray.IntArray[NumOfPrm] = xmalloc(sizeof(int));
420 ScanRes = sscanf(*((*argv)++), "%o",
421 (unsigned int *)TmpArray.IntArray[NumOfPrm++]);
422 break;
423 case 'x':
424 TmpArray.IntArray[NumOfPrm] = xmalloc(sizeof(int));
425 ScanRes = sscanf(*((*argv)++), "%x",
426 (unsigned int *)TmpArray.IntArray[NumOfPrm++]);
427 break;
428 case 'D':
429 TmpArray.LngArray[NumOfPrm] = xmalloc(sizeof(long));
430 ScanRes = sscanf(*((*argv)++), "%ld",
431 (long *)TmpArray.IntArray[NumOfPrm++]);
432 break;
433 case 'U':
434 TmpArray.LngArray[NumOfPrm] = xmalloc(sizeof(long));
435 ScanRes = sscanf(*((*argv)++), "%lu",
436 (unsigned long *)TmpArray.
437 IntArray[NumOfPrm++]);
438 break;
439 case 'O':
440 TmpArray.LngArray[NumOfPrm] = xmalloc(sizeof(long));
441 ScanRes = sscanf(*((*argv)++), "%lo",
442 (unsigned long *)TmpArray.
443 IntArray[NumOfPrm++]);
444 break;
445 case 'X':
446 TmpArray.LngArray[NumOfPrm] = xmalloc(sizeof(long));
447 ScanRes = sscanf(*((*argv)++), "%lx",
448 (unsigned long *)TmpArray.
449 IntArray[NumOfPrm++]);
450 break;
451 case 'f':
452 TmpArray.FltArray[NumOfPrm] = xmalloc(sizeof(float));
453 ScanRes = sscanf(*((*argv)++), "%f",
454 // cppcheck-suppress invalidPointerCast
455 (float *)TmpArray.LngArray[NumOfPrm++]);
456 break;
457 case 'F':
458 TmpArray.DblArray[NumOfPrm] = xmalloc(sizeof(double));
459 ScanRes = sscanf(*((*argv)++), "%lf",
460 // cppcheck-suppress invalidPointerCast
461 (double *)TmpArray.LngArray[NumOfPrm++]);
462 break;
463 case 's':
464 while ((*argv < argv_end) && ((**argv)[0] != '-')) {
465 TmpArray.ChrArray[NumOfPrm++] = *((*argv)++);
466 }
467 ScanRes = 0; /* Force quit from do - loop. */
468 NumOfPrm++; /* Updated again immediately after loop! */
469 (*argv)++; /* "" */
470 break;
471 default:
472 ScanRes = 0; /* Make optimizer warning silent. */
473 }
474 }
475 while (ScanRes == 1); /* Exactly one parameter was read. */
476 (*argv)--;
477 NumOfPrm--;
478
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. */
485
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 */
491 return NumOfPrm;
492 }
493
494 /***************************************************************************
495 Routine to scan the CtrlStr, up to Max and count the number of parameters
496 to that point:
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 ***************************************************************************/
503 static void
GASetParamCount(char * CtrlStr,int Max,int * ParamCount)504 GASetParamCount(char *CtrlStr,
505 int Max,
506 int *ParamCount) {
507 int i;
508
509 *ParamCount = 0;
510 for (i = 0; i < Max; i++)
511 if (ISCTRLCHAR(CtrlStr[i])) {
512 if (CtrlStr[i + 1] == '*')
513 *ParamCount += 2;
514 else
515 (*ParamCount)++;
516 }
517 }
518
519 /***************************************************************************
520 Routine to check if more option (i.e. first char == '-') exists in the
521 given list argc, argv:
522 ***************************************************************************/
523 static bool
GAOptionExists(char ** argv_end,char ** argv)524 GAOptionExists(char **argv_end,
525 char **argv) {
526
527 while (argv < argv_end)
528 if ((*argv++)[0] == '-')
529 return true;
530 return false;
531 }
532
533 /***************************************************************************
534 Routine to print some error messages, for this module:
535 ***************************************************************************/
536 void
GAPrintErrMsg(int Error)537 GAPrintErrMsg(int Error) {
538
539 fprintf(stderr, "Error in command line parsing - ");
540 switch (Error) {
541 case 0:;
542 fprintf(stderr, "Undefined error");
543 break;
544 case CMD_ERR_NotAnOpt:
545 fprintf(stderr, "None option Found");
546 break;
547 case CMD_ERR_NoSuchOpt:
548 fprintf(stderr, "Undefined option Found");
549 break;
550 case CMD_ERR_WildEmpty:
551 fprintf(stderr, "Empty input for '!*?' seq.");
552 break;
553 case CMD_ERR_NumRead:
554 fprintf(stderr, "Failed on reading number");
555 break;
556 case CMD_ERR_AllSatis:
557 fprintf(stderr, "Fail to satisfy");
558 break;
559 }
560 fprintf(stderr, " - '%s'.\n", GAErrorToken);
561 }
562
563 /***************************************************************************
564 Routine to print correct format of command line allowed:
565 ***************************************************************************/
566 void
GAPrintHowTo(char * CtrlStr)567 GAPrintHowTo(char *CtrlStr) {
568
569 int i = 0;
570 bool SpaceFlag;
571
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++]);
576
577 while (i < (int)strlen(CtrlStr)) {
578 // cppcheck-suppress arrayIndexThenCheck
579 while ((ISSPACE(CtrlStr[i])) && (i < (int)strlen(CtrlStr)))
580 i++;
581 switch (CtrlStr[i + 1]) {
582 case '%':
583 fprintf(stderr, " [-%c", CtrlStr[i++]);
584 i += 2; /* Skip the '%-' or '!- after the char! */
585 SpaceFlag = true;
586 while (!ISCTRLCHAR(CtrlStr[i]) && (i < (int)strlen(CtrlStr)) &&
587 (!ISSPACE(CtrlStr[i])))
588 if (SpaceFlag) {
589 if (CtrlStr[i++] == SPACE_CHAR)
590 fprintf(stderr, " ");
591 else
592 fprintf(stderr, " %c", CtrlStr[i - 1]);
593 SpaceFlag = false;
594 } else if (CtrlStr[i++] == SPACE_CHAR)
595 fprintf(stderr, " ");
596 else
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. */
602 }
603 fprintf(stderr, "]");
604 break;
605 case '!':
606 fprintf(stderr, " -%c", CtrlStr[i++]);
607 i += 2; /* Skip the '%-' or '!- after the char! */
608 SpaceFlag = true;
609 while (!ISCTRLCHAR(CtrlStr[i]) && (i < (int)strlen(CtrlStr)) &&
610 (!ISSPACE(CtrlStr[i])))
611 if (SpaceFlag) {
612 if (CtrlStr[i++] == SPACE_CHAR)
613 fprintf(stderr, " ");
614 else
615 fprintf(stderr, " %c", CtrlStr[i - 1]);
616 SpaceFlag = false;
617 } else if (CtrlStr[i++] == SPACE_CHAR)
618 fprintf(stderr, " ");
619 else
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. */
625 }
626 break;
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");
633 return;
634 }
635 }
636 fprintf(stderr, "\n");
637 }
638
639 /* end */
640