1 /*
2  * mpatrol
3  * A library for controlling and tracing dynamic memory allocations.
4  * Copyright (C) 1997-2002 Graeme S. Roy <graeme.roy@analog.com>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the Free
18  * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
19  * MA 02111-1307, USA.
20  */
21 
22 
23 /*
24  * Option parsing.  Implements a routine, similar to getopt() provided on most
25  * UNIX systems, which is used to parse command line options in the mpatrol
26  * tools.  Options with long names are also supported in a way that is similar
27  * to the GNU style of command line option handling.
28  */
29 
30 
31 #include "getopt.h"
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <errno.h>
36 #include <limits.h>
37 
38 
39 #if MP_IDENT_SUPPORT
40 #ident "$Id: getopt.c,v 1.17 2002/01/08 20:13:59 graeme Exp $"
41 #else /* MP_IDENT_SUPPORT */
42 static MP_CONST MP_VOLATILE char *getopt_id = "$Id: getopt.c,v 1.17 2002/01/08 20:13:59 graeme Exp $";
43 #endif /* MP_IDENT_SUPPORT */
44 
45 
46 #ifdef __cplusplus
47 extern "C"
48 {
49 #endif /* __cplusplus */
50 
51 
52 /* The index of the current option in the command line argument array.
53  */
54 
55 MP_GLOBAL unsigned long __mp_optindex;
56 
57 
58 /* The argument for the current option.
59  */
60 
61 MP_GLOBAL char *__mp_optarg;
62 
63 
64 /* Strip the path component from a fully-qualified filename.
65  */
66 
67 MP_GLOBAL
68 char *
__mp_basename(char * s)69 __mp_basename(char *s)
70 {
71     char *t;
72 
73 #if TARGET == TARGET_UNIX
74     while (t = strchr(s, '/'))
75 #elif TARGET == TARGET_AMIGA
76     while (t = strpbrk(s, ":/"))
77 #else /* TARGET */
78     while (t = strpbrk(s, ":/\\"))
79 #endif /* TARGET */
80         s = t + 1;
81     return s;
82 }
83 
84 
85 /* Convert a string representation of a number to an integer,
86  * reporting any errors that occur during the conversion.
87  */
88 
89 MP_GLOBAL
90 int
__mp_getnum(char * p,char * s,long * n,int u)91 __mp_getnum(char *p, char *s, long *n, int u)
92 {
93     char *t;
94     int e;
95 
96     e = errno;
97     errno = 0;
98     if ((u == 1) && (*s == '-'))
99     {
100         fprintf(stderr, "%s: Illegal positive number `%s'\n", p, s);
101         t = s;
102     }
103     else if ((u == 0) && (*s == '-') && (s[1] == '0') && ((s[2] == 'b') ||
104              (s[2] == 'B')))
105         /* This is a negative binary number.
106          */
107         *n = -strtol(s + 3, &t, 2);
108     else if ((*s == '0') && ((s[1] == 'b') || (s[1] == 'B')))
109     {
110         /* This is a positive binary number.
111          */
112         if (u == 0)
113             *n = strtol(s + 2, &t, 2);
114         else
115             *n = strtoul(s + 2, &t, 2);
116     }
117     /* Otherwise let the conversion function work out the number base
118      * from the prefix.
119      */
120     else if (u == 0)
121         *n = strtol(s, &t, 0);
122     else
123         *n = strtoul(s, &t, 0);
124     if (errno == ERANGE)
125         fprintf(stderr, "%s: %s number overflow in `%s'\n", p, ((u == 0) &&
126                  (*n == LONG_MIN)) ? "Negative" : "Positive", s);
127     errno = e;
128     return (*t == '\0');
129 }
130 
131 
132 /* Search for an option in the long options table.
133  */
134 
135 static
136 option *
findopt(char * s,option * l,char ** a)137 findopt(char *s, option *l, char **a)
138 {
139     char *t;
140     size_t n;
141 
142     if (t = strchr(s, '='))
143         n = t - s;
144     else
145         n = strlen(s);
146     while (l->name != NULL)
147     {
148         if ((strncmp(s, l->name, n) == 0) && (l->name[n] == '\0'))
149         {
150             if (t != NULL)
151                 n++;
152             *a = s + n;
153             return l;
154         }
155         l++;
156     }
157     return NULL;
158 }
159 
160 
161 /* Read an option from a supplied command line argument array.
162  */
163 
164 MP_GLOBAL
165 int
__mp_getopt(unsigned long n,char ** a,char * s,option * l)166 __mp_getopt(unsigned long n, char **a, char *s, option *l)
167 {
168     static char *t;
169     option *m;
170     char *b, *p;
171     int r;
172 
173     __mp_optarg = NULL;
174     /* If the index of the current option is zero or if there are no more
175      * options in this argument then we proceed to the next argument.
176      */
177     if ((__mp_optindex == 0) || ((t != NULL) && (*t == '\0')))
178     {
179         __mp_optindex++;
180         t = NULL;
181     }
182     /* Get the basename of the filename that the program was invoked with so
183      * that we can use that for any diagnostics.
184      */
185     b = __mp_basename(a[0]);
186     /* If there is not a current option then attempt to locate it, otherwise
187      * return EOF if there are no more options.
188      */
189     if (t == NULL)
190     {
191         if (__mp_optindex >= n)
192             return EOF;
193         t = a[__mp_optindex];
194         /* Stop parsing options if either the argument is not an option, if it
195          * is a single dash (representing stdin) or if it is a double dash
196          * representing the end of options.
197          */
198         if ((*t != '-') || (t[1] == '\0') || ((t[1] == '-') && (t[2] == '\0')))
199         {
200             if ((*t == '-') && (t[1] == '-') && (t[2] == '\0'))
201                 __mp_optindex++;
202             t = NULL;
203             return EOF;
204         }
205         t++;
206         /* Parse a long option and possibly its argument.
207          */
208         if ((*t == '-') && (l != NULL))
209         {
210             t++;
211             /* Check that the option appears in the long options table.
212              */
213             if ((m = findopt(t, l, &t)) == NULL)
214             {
215                 fprintf(stderr, "%s: Illegal option `--%s'\n", b, t);
216                 __mp_optindex++;
217                 t = NULL;
218                 return '?';
219             }
220             /* Check to see if the option takes an argument.
221              */
222             if (m->arg)
223                 if (*t == '\0')
224                 {
225                     /* The rest of this argument is empty, so we proceed to the
226                      * next argument.
227                      */
228                     if ((++__mp_optindex >= n) ||
229                         (strcmp(a[__mp_optindex], "--") == 0))
230                     {
231                         fprintf(stderr, "%s: Option `--%s' requires an "
232                                 "argument\n", b, m->name);
233                         t = NULL;
234                         return '?';
235                     }
236                     __mp_optarg = a[__mp_optindex];
237                 }
238                 else
239                     __mp_optarg = t;
240             else if (*t != '\0')
241                 fprintf(stderr, "%s: Ignoring argument `%s' for option "
242                         "`--%s'\n", b, t, m->name);
243             __mp_optindex++;
244             t = NULL;
245             return m->value;
246         }
247     }
248     /* Check that the option appears in the string of recognised options.
249      */
250     if ((*t == ':') || ((p = strchr(s, *t)) == NULL))
251     {
252         fprintf(stderr, "%s: Illegal option `-%c'\n", b, *t++);
253         return '?';
254     }
255     r = *t++;
256     /* Check to see if the option takes an argument.
257      */
258     if (p[1] == ':')
259     {
260         if (*t == '\0')
261         {
262             /* The rest of this argument is empty, so we proceed to the next
263              * argument.
264              */
265             if ((++__mp_optindex >= n) || (strcmp(a[__mp_optindex], "--") == 0))
266             {
267                 fprintf(stderr, "%s: Option `-%c' requires an argument\n", b,
268                         r);
269                 t = NULL;
270                 return '?';
271             }
272             __mp_optarg = a[__mp_optindex];
273         }
274         else
275             __mp_optarg = t;
276         __mp_optindex++;
277         t = NULL;
278     }
279     return r;
280 }
281 
282 
283 /* Build a string of short options letters from the long options table.
284  */
285 
286 MP_GLOBAL
287 char *
__mp_shortopts(char * s,option * l)288 __mp_shortopts(char *s, option *l)
289 {
290     char *t;
291 
292     t = s;
293     while (l->name != NULL)
294     {
295         if ((l->value >= SHORTOPT_MIN) && (l->value <= SHORTOPT_MAX))
296         {
297             *t++ = l->value;
298             if (l->arg)
299                 *t++ = ':';
300         }
301         l++;
302     }
303     *t = '\0';
304     return s;
305 }
306 
307 
308 /* Display a quick-reference option summary.
309  */
310 
311 MP_GLOBAL
312 void
__mp_showopts(option * l)313 __mp_showopts(option *l)
314 {
315     fputs("Options:\n", stdout);
316     while (l->name != NULL)
317     {
318         if ((l->value >= SHORTOPT_MIN) && (l->value <= SHORTOPT_MAX))
319             fprintf(stdout, "  -%c", l->value);
320         else
321             fputs("    ", stdout);
322         fprintf(stdout, "  --%s", l->name);
323         if (l->arg)
324             fprintf(stdout, "=<%s>", l->arg);
325         fputc('\n', stdout);
326         fputs(l->desc, stdout);
327         l++;
328     }
329 }
330 
331 
332 /* Perform pattern-matching with shell metacharacters.
333  */
334 
335 MP_GLOBAL
336 int
__mp_match(char * s,char * t)337 __mp_match(char *s, char *t)
338 {
339     int i, n;
340     char c, d;
341 
342     while ((c = *t++) != '\0')
343         if (c == '[')
344         {
345             i = 0;
346             if (n = (*t == '!'))
347                 t++;
348             if ((*t == ']') || ((d = *s++) == '\0'))
349                 return 0;
350             while ((c = *t++) != ']')
351                 if (*t == '-')
352                 {
353                     if ((c <= d) && (d <= t[1]))
354                         i = 1;
355                     t += 2;
356                 }
357                 else if (c == d)
358                     i = 1;
359                 else if (c == '\0')
360                     return 0;
361             if (i == n)
362                 return 0;
363         }
364         else if (c == '*')
365         {
366             if (*t == '\0')
367                 return 1;
368             do
369                 if (__mp_match(s, t))
370                     return 1;
371             while (*s++ != '\0');
372             return 0;
373         }
374         else if (c == '?')
375         {
376             if (*s++ == '\0')
377                 return 0;
378         }
379         else if (*s++ != c)
380             return 0;
381     return (*s == '\0');
382 }
383 
384 
385 #ifdef __cplusplus
386 }
387 #endif /* __cplusplus */
388