1 /*-----------------------------------------------------------------------
2
3 File : cio_commandline.c
4
5 Author: Stephan Schulz
6
7 Contents
8
9 Functions for handling options and recognising non-option
10 arguments.
11
12 Copyright 1998, 1999 by the author.
13 This code is released under the GNU General Public Licence and
14 the GNU Lesser General Public License.
15 See the file COPYING in the main E directory for details..
16 Run "eprover -h" for contact information.
17
18 Changes
19
20 <1> Wed Sep 10 00:01:33 MET DST 1997
21 New
22
23 -----------------------------------------------------------------------*/
24
25 #include "cio_commandline.h"
26
27
28
29 /*---------------------------------------------------------------------*/
30 /* Global Variables */
31 /*---------------------------------------------------------------------*/
32
33
34 /*---------------------------------------------------------------------*/
35 /* Forward Declarations */
36 /*---------------------------------------------------------------------*/
37
38
39 /*---------------------------------------------------------------------*/
40 /* Internal Functions */
41 /*---------------------------------------------------------------------*/
42
43 /*-----------------------------------------------------------------------
44 //
45 // Function: print_start_of_str()
46 //
47 // Print str up to the last blank character before the len's
48 // character or the first newline, whichever is first, followed by a
49 // newline. If there is no blank, break at
50 // character number len. Returns a pointer to the first character
51 // following the break, or NULL if the string was printed
52 // completely.
53 //
54 // Global Variables: -
55 //
56 // Side Effects : Output
57 //
58 /----------------------------------------------------------------------*/
59
print_start_of_str(FILE * out,char * str,int len)60 static char* print_start_of_str(FILE* out, char* str, int len)
61 {
62 int i = 0;
63 char* search = str;
64 char* blank = NULL;
65
66 while(*search && i<len) /* Search last blank before
67 EOString/newline or len characters. */
68 {
69 if(*search == ' ')
70 {
71 blank = search;
72 }
73 else if(*search == '\n')
74 {
75 blank = search;
76 i=len;
77 break;
78 }
79 search++;
80 i++;
81 }
82 if(i<len) /* Print remaining string */
83 {
84 while(*str)
85 {
86 putc(*str, out);
87 str++;
88 }
89 putc('\n', out);
90 return 0;
91 }
92 else if(blank) /* Print string up to space */
93 {
94 while(str!=blank)
95 {
96 putc(*str, out);
97 str++;
98 }
99 putc('\n', out);
100 str++;
101 return *str?str:NULL;
102 }
103 else /* Now have to do a hard break! */
104 {
105 while(str!=search)
106 {
107 putc(*str, out);
108 str++;
109 }
110 putc('\n', out);
111 return str;
112 }
113 }
114
115 /*-----------------------------------------------------------------------
116 //
117 // Function: shift_array_left()
118 //
119 // Shift a 0-terminated array of char* elements left by one,
120 // dropping the first element. Return false if no element is
121 // present.
122 //
123 // Global Variables: -
124 //
125 // Side Effects : -
126 //
127 /----------------------------------------------------------------------*/
128
shift_array_left(char * array[])129 static bool shift_array_left(char* array[])
130 {
131 int i;
132
133 if(!array[0])
134 {
135 return false;
136 }
137 for(i=0; array[i+1]; i++)
138 {
139 array[i] = array[i+1];
140 }
141 array[i] = NULL;
142
143 return true;
144 }
145
146
147 /*-----------------------------------------------------------------------
148 //
149 // Function: find_long_opt()
150 //
151 // Find an option entry by long name. Return NULL if not found.
152 //
153 // Global Variables: -
154 //
155 // Side Effects : -
156 //
157 /----------------------------------------------------------------------*/
158
find_long_opt(char * option,OptCell options[])159 static Opt_p find_long_opt(char* option, OptCell options[])
160 {
161 int i;
162 unsigned int len;
163
164 option+=2; /* Jump -- */
165 len = 0;
166 while(option[len])
167 {
168 if(option[len]=='=')
169 {
170 break;
171 }
172 len++;
173 }
174 for(i=0; options[i].option_code; i++)
175 {
176 if((strncmp(options[i].longopt, option, len)==0) &&
177 (strlen(options[i].longopt)==len))
178 {
179 return &(options[i]);
180 }
181 }
182 return NULL;
183 }
184
185
186 /*-----------------------------------------------------------------------
187 //
188 // Function: find_short_opt()
189 //
190 // Find an option entry by short name. Return NULL if not found.
191 //
192 // Global Variables: -
193 //
194 // Side Effects : -
195 //
196 /----------------------------------------------------------------------*/
197
find_short_opt(char option,OptCell options[])198 static Opt_p find_short_opt(char option, OptCell options[])
199 {
200 int i;
201
202 for(i=0; options[i].option_code; i++)
203 {
204 if(option == options[i].shortopt)
205 {
206 return &(options[i]);
207 }
208 }
209 return NULL;
210 }
211
212
213 /*-----------------------------------------------------------------------
214 //
215 // Function: process_long_option()
216 //
217 // Process the long option that is found in state->argc[state->argi]:
218 // Find the option, check for argument, set *arg to an argument,
219 // update state.
220 //
221 // Global Variables: -
222 //
223 // Side Effects : Changes state, may cause error
224 //
225 /----------------------------------------------------------------------*/
226
process_long_option(CLState_p state,char ** arg,OptCell options[])227 static Opt_p process_long_option(CLState_p state, char** arg,
228 OptCell options[])
229 {
230 Opt_p handle;
231 char* eq_sign;
232 DStr_p err = DStrAlloc();
233
234 if(!(handle = find_long_opt(state->argv[state->argi], options)))
235 {
236 DStrAppendStr(err, "Unknown Option: ");
237 DStrAppendStr(err, state->argv[state->argi]);
238 DStrAppendStr(err," (Use -h for a list of valid options)");
239 Error(DStrView(err), USAGE_ERROR);
240 }
241
242 eq_sign = strchr(state->argv[state->argi], '=');
243
244 switch(handle->type)
245 {
246 case NoArg:
247 if(eq_sign)
248 {
249 DStrAppendStr(err, state->argv[state->argi]);
250 DStrAppendStr(err, " does not accept an argument!");
251 Error(DStrView(err), USAGE_ERROR);
252 }
253 *arg = NULL;
254 break;
255 case OptArg:
256 if(eq_sign)
257 {
258 *arg = eq_sign+1;
259 }
260 else
261 {
262 assert(handle->arg_default);
263 *arg = handle->arg_default;
264 }
265 break;
266 case ReqArg:
267 if(!eq_sign)
268 {
269 DStrAppendStr(err, state->argv[state->argi]);
270 DStrAppendStr(err, " requires an argument!");
271 Error(DStrView(err), USAGE_ERROR);
272 }
273 *arg = eq_sign+1;
274 break;
275 default:
276 assert(false);
277 break;
278 }
279 shift_array_left(&(state->argv[state->argi]));
280 state->argc--;
281 DStrFree(err);
282
283 return handle;
284 }
285
286
287 /*-----------------------------------------------------------------------
288 //
289 // Function: process_short_option()
290 //
291 // Process the short option that is found in
292 // state->argc[state->argi][state->sc_opt_c]: Find the option, check
293 // for argument, set *arg to an argument, update state.
294 //
295 // Global Variables:
296 //
297 // Side Effects :
298 //
299 /----------------------------------------------------------------------*/
300
process_short_option(CLState_p state,char ** arg,OptCell options[])301 static Opt_p process_short_option(CLState_p state, char** arg,
302 OptCell options[])
303 {
304 Opt_p handle;
305 DStr_p err = DStrAlloc();
306 char* optstr = state->argv[state->argi];
307
308 if(!(handle = find_short_opt(optstr[state->sc_opt_c], options)))
309 {
310 DStrAppendStr(err, "Unknown Option: -");
311 DStrAppendChar(err, optstr[state->sc_opt_c]);
312 DStrAppendStr(err, " (processing ");
313 DStrAppendStr(err, optstr);
314 DStrAppendStr(err, ")");
315 DStrAppendStr(err," (Use -h for a list of valid options)");
316 Error(DStrView(err), USAGE_ERROR);
317 }
318 *arg = NULL;
319 switch(handle->type)
320 {
321 case OptArg:
322 assert(handle->arg_default);
323 *arg = handle->arg_default;
324 /* Fall-through intentional! */
325 case NoArg:
326 state->sc_opt_c++;
327 if(!optstr[state->sc_opt_c])
328 {
329 state->sc_opt_c = 0;
330 shift_array_left(&(state->argv[state->argi]));
331 state->argc--;
332 }
333 break;
334 case ReqArg:
335 if(state->sc_opt_c!=1)
336 {
337 DStrAppendStr(err, optstr);
338 DStrAppendStr(err, ": POSIX forbids the aggregation of"
339 " options which take arguments (but you"
340 " probably only forgot the second hyphen for"
341 " a long GNU-style option)");
342 Error(DStrView(err), USAGE_ERROR);
343 }
344 if(optstr[state->sc_opt_c+1])
345 {
346 *arg=&optstr[state->sc_opt_c+1];
347 }
348 else
349 {
350 shift_array_left(&(state->argv[state->argi]));
351 state->argc--;
352 if(!state->argv[state->argi])
353 {
354 DStrAppendChar(err, '-');
355 DStrAppendChar(err, optstr[state->sc_opt_c]);
356 DStrAppendStr(err, " requires an argument");
357 Error(DStrView(err), USAGE_ERROR);
358 }
359 *arg = state->argv[state->argi];
360 }
361 state->sc_opt_c = 0;
362 shift_array_left(&(state->argv[state->argi]));
363 state->argc--;
364 break;
365 default:
366 assert(false);
367 break;
368 }
369 DStrFree(err);
370 return handle;
371 }
372
373
374 /*-----------------------------------------------------------------------
375 //
376 // Function: append_option_desc()
377 //
378 // Append a description of the given option to the DStr.
379 //
380 // Global Variables: -
381 //
382 // Side Effects : Changes the string.
383 //
384 /----------------------------------------------------------------------*/
385
append_option_desc(DStr_p string,Opt_p option)386 static void append_option_desc(DStr_p string, Opt_p option)
387 {
388 assert(option->shortopt || option->longopt);
389
390 if(option->shortopt)
391 {
392 DStrAppendChar(string, '-');
393 DStrAppendChar(string, option->shortopt);
394 }
395 if(option->shortopt && option->longopt)
396 {
397 DStrAppendStr(string, " or ");
398 }
399 if(option->longopt)
400 {
401 DStrAppendStr(string, "--");
402 DStrAppendStr(string, option->longopt);
403 }
404 }
405
406
407 /*---------------------------------------------------------------------*/
408 /* Exported Functions */
409 /*---------------------------------------------------------------------*/
410
411
412 /*-----------------------------------------------------------------------
413 //
414 // Function: CLStateAlloc()
415 //
416 // Allocate initialized Structure for the description of a
417 // (partially processed) command line.
418 //
419 // Global Variables: -
420 //
421 // Side Effects : Memory operations
422 //
423 /----------------------------------------------------------------------*/
424
CLStateAlloc(int argc,char * argv[])425 CLState_p CLStateAlloc(int argc, char* argv[])
426 {
427 int i;
428 CLState_p handle = CLStateCellAlloc();
429
430 handle->sc_opt_c = 0;
431 handle->argi = 0;
432 handle->argc = argc;
433 handle->argsize = argc+2; /* Allocate one entry extra for
434 default inserting of "-" */
435 handle->argv = SecureMalloc(handle->argsize * sizeof(char*));
436 for(i=0; i<argc; i++)
437 {
438 handle->argv[i] = argv[i];
439 }
440 handle->argv[argc] = NULL;
441
442 shift_array_left(handle->argv);
443 handle->argc--;
444 return handle;
445 }
446
447
448 /*-----------------------------------------------------------------------
449 //
450 // Function: CLStateFree()
451 //
452 // Free a CLStateCell.
453 //
454 // Global Variables: -
455 //
456 // Side Effects : Memory operations
457 //
458 /----------------------------------------------------------------------*/
459
CLStateFree(CLState_p junk)460 void CLStateFree(CLState_p junk)
461 {
462 assert(junk);
463 FREE(junk->argv);
464 CLStateCellFree(junk);
465 }
466
467
468 /*-----------------------------------------------------------------------
469 //
470 // Function: CLStateInsertArg()
471 //
472 // Insert an additional argument at the end of state->argv, realloc
473 // for more memory if necessary. Return new state->argc value. arg
474 // is expected to be const, it is not copied!
475 //
476 // Global Variables: -
477 //
478 // Side Effects : Memory operations.
479 //
480 /----------------------------------------------------------------------*/
481
CLStateInsertArg(CLState_p state,char * arg)482 int CLStateInsertArg(CLState_p state, char* arg)
483 {
484 if(state->argsize == state->argc+2)
485 {
486 state->argsize++;
487 state->argv = SecureRealloc(state->argv, state->argsize *
488 sizeof(char*));
489 }
490 state->argv[state->argc] = arg;
491 state->argc++;
492 state->argv[state->argc] = NULL;
493
494 return state->argc;
495 }
496
497 /*-----------------------------------------------------------------------
498 //
499 // Function: CLStateGetOpt()
500 //
501 // Return a pointer to the next unprocessed option, set arg to point
502 // to the argument (if present) or the default (if present).
503 //
504 // Global Variables: -
505 //
506 // Side Effects : Updates state by removing options and arguments and
507 // adjusting counters accordingly.
508 //
509 /----------------------------------------------------------------------*/
510
CLStateGetOpt(CLState_p state,char ** arg,OptCell options[])511 Opt_p CLStateGetOpt(CLState_p state, char** arg, OptCell options[])
512 {
513 while(state->argv[state->argi])
514 {
515 if((state->argv[state->argi][0]=='-') &&
516 (state->argv[state->argi][1]!='\0'))
517 {
518 break;
519 }
520 state->argi++;
521 }
522 if(!state->argv[state->argi])
523 {
524 return NULL;
525 }
526 if(strcmp(state->argv[state->argi], "--")==0)
527 {
528 shift_array_left(&(state->argv[state->argi]));
529 state->argc--;
530 while(state->argv[state->argi])
531 {
532 state->argi++;
533 }
534 return NULL;
535 }
536 if(strncmp(state->argv[state->argi], "--", 2)==0)
537 {
538 return process_long_option(state, arg, options);
539 }
540 if(!state->sc_opt_c)
541 {
542 state->sc_opt_c = 1;
543 }
544 return process_short_option(state, arg, options);
545 }
546
547
548 /*-----------------------------------------------------------------------
549 //
550 // Function: CLStateGetFloatArg()
551 //
552 // Return the numerical value of the argument if it is a well-formed
553 // (double) float, print an error message otherwise.
554 //
555 // Global Variables: -
556 //
557 // Side Effects : May terminate program
558 //
559 /----------------------------------------------------------------------*/
560
CLStateGetFloatArg(Opt_p option,char * arg)561 double CLStateGetFloatArg(Opt_p option, char* arg)
562 {
563 double ret;
564 char* eoarg;
565
566 errno = 0;
567
568 ret = strtod(arg, &eoarg);
569
570 if(errno || *eoarg)
571 {
572 DStr_p err = DStrAlloc();
573 TmpErrno = errno;
574 append_option_desc(err, option);
575 DStrAppendStr(err, " expects float instead of '");
576 DStrAppendStr(err, arg);
577 DStrAppendChar(err, '\'');
578 if(TmpErrno)
579 {
580 SysError(DStrView(err), USAGE_ERROR);
581 }
582 else
583 {
584 Error(DStrView(err), USAGE_ERROR);
585 }
586 DStrFree(err);
587 }
588 return ret;
589 }
590
591
592 /*-----------------------------------------------------------------------
593 //
594 // Function: CLStateGetIntArg()
595 //
596 // Return the numerical value of the argument if it is a well-formed
597 // long, print an error message otherwise.
598 //
599 // Global Variables: -
600 //
601 // Side Effects : May terminate program
602 //
603 /----------------------------------------------------------------------*/
604
CLStateGetIntArg(Opt_p option,char * arg)605 long CLStateGetIntArg(Opt_p option, char* arg)
606 {
607 long ret;
608 char* eoarg;
609
610 errno = 0;
611
612 ret = strtol(arg, &eoarg, 10);
613
614 if(errno || *eoarg)
615 {
616 DStr_p err = DStrAlloc();
617 TmpErrno = errno;
618 append_option_desc(err, option);
619 DStrAppendStr(err, " expects integer instead of '");
620 DStrAppendStr(err, arg);
621 DStrAppendChar(err, '\'');
622 if(TmpErrno)
623 {
624 SysError(DStrView(err), USAGE_ERROR);
625 }
626 else
627 {
628 Error(DStrView(err), USAGE_ERROR);
629 }
630 DStrFree(err); /* Symmetry */
631 }
632 return ret;
633 }
634
635
636 /*-----------------------------------------------------------------------
637 //
638 // Function: CLStateGetBoolArg()
639 //
640 // Return the boolean value of the argument if it is either 'true'
641 // or 'false' long, print an error message otherwise.
642 //
643 // Global Variables: -
644 //
645 // Side Effects : May terminate program
646 //
647 /----------------------------------------------------------------------*/
648
CLStateGetBoolArg(Opt_p option,char * arg)649 bool CLStateGetBoolArg(Opt_p option, char* arg)
650 {
651 DStr_p err;
652
653 if(strcmp(arg, "true")==0)
654 {
655 return true;
656 }
657 else if(strcmp(arg, "false")==0)
658 {
659 return false;
660 }
661
662 err = DStrAlloc();
663 append_option_desc(err, option);
664 DStrAppendStr(err, " expects 'true' or 'false' instead of '");
665 DStrAppendStr(err, arg);
666 DStrAppendChar(err, '\'');
667 Error(DStrView(err), USAGE_ERROR);
668 DStrFree(err); /* Symmetry */
669 return false; /* Just to stiffle warings */
670 }
671
672
673 /*-----------------------------------------------------------------------
674 //
675 // Function: PrintOption()
676 //
677 // Print the formatted description of an option (generated from the
678 // information in an OptCell() to the desired stream.
679 //
680 // Global Variables: -
681 //
682 // Side Effects : Output
683 //
684 /----------------------------------------------------------------------*/
685
PrintOption(FILE * out,Opt_p option)686 void PrintOption(FILE* out, Opt_p option)
687 {
688 char* l_argdesc = "";
689 char* desc;
690 DStr_p optdesc = DStrAlloc();
691
692 switch(option->type)
693 {
694 case NoArg:
695 break;
696 case OptArg:
697 l_argdesc = "[=<arg>]";
698 break;
699 case ReqArg:
700 l_argdesc = "=<arg>";
701 break;
702 default:
703 assert(false);
704 break;
705 }
706
707 assert(option->longopt || option->shortopt);
708
709 if(option->shortopt)
710 {
711 fprintf(out, " -%c%s\n", option->shortopt,
712 option->type == ReqArg ? " <arg>" : "");
713 }
714 if(option->longopt)
715 {
716 fprintf(out, " --%s%s\n", option->longopt, l_argdesc);
717 }
718
719 DStrAppendStr(optdesc, option->desc);
720
721 if(option->type ==OptArg)
722 {
723 assert(option->longopt);
724
725 if(option->shortopt)
726 {
727 DStrAppendStr(optdesc,
728 " The short form or the long form without"
729 " the optional argument is equivalent to --");
730 }
731 else
732 {
733 DStrAppendStr(optdesc,
734 " The option without"
735 " the optional argument is equivalent to --");
736 }
737
738 DStrAppendStr(optdesc, option->longopt);
739 DStrAppendChar(optdesc, '=');
740 DStrAppendStr(optdesc, option->arg_default);
741 DStrAppendStr(optdesc, ".");
742 }
743
744 desc = DStrView(optdesc);
745 while(desc)
746 {
747 fprintf(out, " ");
748 desc = print_start_of_str(out, desc, FORMAT_WIDTH-4);
749 }
750 fprintf(out, "\n");
751 DStrFree(optdesc);
752 }
753
754
755 /*-----------------------------------------------------------------------
756 //
757 // Function: PrintOptions()
758 //
759 // Print the whole option array (terminated by an OptCell with type
760 // NoOption.
761 //
762 // Global Variables: -
763 //
764 // Side Effects : Output
765 //
766 /----------------------------------------------------------------------*/
767
PrintOptions(FILE * out,OptCell option[],char * header)768 void PrintOptions(FILE* out, OptCell option[], char* header)
769 {
770 int i;
771
772 if(header)
773 {
774 fprintf(out, "%s", header);
775 }
776
777 for(i=0; option[i].option_code; i++)
778 {
779 PrintOption(out, &option[i]);
780 }
781 }
782
783
784 /*---------------------------------------------------------------------*/
785 /* End of File */
786 /*---------------------------------------------------------------------*/
787
788
789
790