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