1 /*-------------- Telecommunications & Signal Processing Lab ---------------
2                              McGill University
3 
4 Routine:
5   int UTgetOption (int *Index, int argc, const char *argv[],
6                    const char *OptTable[], const char *OptArg[])
7 
8 Purpose:
9   Decode command line options and arguments
10 
11 Description:
12   This routine decodes command line options, returning option values and other
13   (non-option) argument strings.  Typically this routine is used to decode Unix
14   style command options and to return argument strings.  An example of a Unix
15   command line is as follows.
16     command -x -f name --delay=30 argA argB.
17   Each of the items in the command line, including the command itself, appears
18   in an array of strings.  This routine compares the elements of the command
19   line after the command name for a match to a table of option keywords.
20   Options normally have a leading "-" character.
21 
22   The table of keywords contains entries which can specify the minimum length
23   needed for a match with an optional "*" character.
24   -d
25       This form specifies that an exact match to "-d" is required.  The "-"
26       character is just treated as an ordinary character for the match.
27   --d*elay
28       This form specifies that the option must match the first three characters
29       and optionally match additional characters.  For instance, "--d" matches
30       as does "--del", but not "--dx".
31 
32   The need for an option value is signalled in two ways.
33   -d#
34       The presence of the trailing "#" indicates that the option can either
35       directly follow the option as in "-d30" or in the next argument as in
36       "-d" followed by "30" in the next argument.
37   --d*elay=
38       The presence of the trailing "=" indicates that the option can either
39       follow the option, separated by a '=' character as in "--del=30" or in
40       the next argument as in "--del" followed by "30" in the next argument.
41 
42   The keywords are searched in order.  Keywords can contain a second asterisk
43   can be used to indicate that characters after this point need not match.
44 
45   Errors detected by this routine result in an error message being printed and
46   an error code being returned.
47   Invalid option:
48       If all keywords in the list of option keywords start with a "-"
49       character, then any argument with a leading "-", followed by one or
50       more characters and not matching any of the options causes an error
51       condition.  This means that ordinary arguments (non-option) cannot start
52       with a "-" if the first option keyword starts with a "-".
53   Missing option value:
54       If an option requires a value, and none is supplied, an error is
55       reported.  Such an error can only happen if the option is the last item
56       in the list of arguments, since in other cases the item after the option
57       will be picked up as the option value.
58 
59   The results of the option scan are communicated to the calling program via
60   the pointer OptArg.  OptArg is a pointer to a pointer to a null terminated
61   strings (substrings of the strings in argv).  The interpretation of these
62   strings is given by the return code.  When this routine is called in a
63   loop, the OptArg strings are returned in the same order as the options and
64   arguments appeared in argv.  The OptArg pointer can be NULL.  This is the
65   case for an option with no option value.  Each call to UTgetOption returns
66   at most one option or argument.
67 
68   Return codes:
69     -2 - Error detected, invalid option or missing value
70     -1 - Normal end of processing
71      0 - Argument string, not an option.
72      n - Option specified by the n'th keyword, n >= 1.
73   For the non-negative return codes, the argument string or the option value
74   string are pointed to by OptArg.  OptArg will be NULL for an option for which
75   no option value is allowed.
76 
77   The use of this routine is illustrated in the following program fragment.
78     static const char *OptTable[] = {
79       "-d#", "--d*elay=",
80       "-p", "--p*rint",
81       NULL};
82 
83     void main (int argc, const char *argv[])
84     {
85       const char *OptArg;
86       int Index;
87       int n;
88 
89       Index = 1;
90       while (Index < argc) {
91         n = UTgetOption (&Index, argc, argv, OptTable, &OptArg);
92         switch (n) {
93         case 0:
94           ... process non-option OptArg
95           break;
96         case 1:
97         case 2:
98           ... process option 1 (-d or --delay)
99           break;
100         case 3:
101        	case 4:
102           ... process option 2 (-p or --print)
103           break;
104         default:
105           ... error
106         }
107       }
108     }
109 
110 Parameters:
111   <-  int UTgetOptions
112       Status code,
113          -2 - Error, invalid option or missing value
114          -1 - End of arguments/options
115           0 - Argument value
116         >=1 - Option code
117   <-> int *Index
118       Integer used internally for saving the state of the search.  It is the
119       index of the next element of argv to be examined.  This value should be
120       saved from call to call.  Note that the index may be incremented by zero
121       (error detected), one or two (option followed by a value in the next
122       element of argv).  The initial value of Index should be one.
123    -> int argc
124       Number of argument strings in argv
125    -> const char *argv[]
126       Pointers to option/argument strings.  Note that with ANSI C, if the
127       actual parameter is not declared to have the const attribute, an explicit
128       cast to (const char **) is required.
129    -> const char *OptTable[]
130       Pointers to the option keywords.  The end of the keyword table is
131       signalled by a NULL pointer.  Note that with ANSI C, if the actual
132       parameter is not declared to have the const attribute, an explicit cast
133       to (const char **) is required.
134   <-  const char *OptArg[]
135       Argument/option string.  For an argument or an option taking a value,
136       this is a pointer to a null terminated substring string in argv.  If the
137       decoded option does not take a value, this pointer will be NULL.  Note
138       that with ANSI C, if the actual parameter is not declared to have the
139       const attribute, an explict cast to (const char **) is required.
140 
141 Author / revision:
142   P. Kabal  Copyright (C) 2003
143   $Revision: 1.31 $  $Date: 2003/05/09 03:20:37 $
144 
145 -------------------------------------------------------------------------*/
146 
147 #include <libtsp.h>
148 #include <libtsp/nucleus.h>
149 #include <libtsp/UTmsg.h>
150 
151 #define NOMATCH		0
152 #define MATCH		1
153 #define MVAL		-2
154 
155 #define RET_ERROR	-2
156 #define RET_END		-1
157 #define RET_ARG		0
158 
159 
160 int
UTgetOption(int * Index,const int argc,const char * argv[],const char * OptTable[],const char ** OptArg)161 UTgetOption (int *Index, const int argc, const char *argv[],
162 	     const char *OptTable[], const char **OptArg)
163 
164 {
165   int n, status, ip, rv, allminus;
166 
167   if (*Index <= 0)
168     ip = 1;
169   else
170     ip = *Index;
171 
172 /* Check the index, return if finished processing arguments from argv */
173   if (ip >= argc) {
174     /* Finished processing */
175     *OptArg = NULL;
176     return RET_END;
177   }
178 
179 /* Decode the option */
180   rv = NOMATCH;
181   allminus = (OptTable[0] != NULL);
182   for (n = 0; OptTable[n] != NULL; ++n) {
183     if (OptTable[n][0] != '-')
184       allminus = 0;
185     rv = UTdecOption (argv[ip], OptTable[n], OptArg);
186     if (rv != NOMATCH)
187       goto Match;
188   }
189 
190   /* Not an option */
191   if (allminus && argv[ip][0] == '-' && argv[ip][1] != '\0') {
192     /* The argument has a leading '-' */
193     UTwarn ("UTgetOption - %s: \"%s\"", UTM_BadOption, argv[ip]);
194     *OptArg = NULL;
195     status = RET_ERROR;
196   }
197   else {
198     /* Ordinary argument */
199     *OptArg = argv[ip];
200     status = RET_ARG;
201   }
202 
203   *Index = ip + 1;
204   return status;
205 
206 /* ===================== */
207 /* Found an option match */
208  Match:
209   if (rv == MATCH)
210     /* Found an option match */
211     status = n+1;
212 
213   else {
214     /* Error - missing value */
215     /* Try to pick up the option from the next argument */
216     if (ip+1 < argc) {
217       ++ip;		/* extra increment */
218       *OptArg = argv[ip];
219       status = n+1;
220     }
221     else {
222       UTwarn ("UTgetOption - %s \"%s\"", UTM_ExpectValue, argv[ip]);
223       status = RET_ERROR;
224     }
225   }
226 
227   /* Update for next call */
228   *Index = ip + 1;
229   return status;
230 }
231