1 /*----------------------------------------------------*- Fundamental -*-
2 
3 Module:		getopt()	[X/OPEN]
4 
5 File:		getopt.c
6 
7 Assoc. files:	config.h	(but see below)
8 
9 Description:	This module implements a command line parser. It
10 		returns option flags (specified by the user) and
11 		associated values (if any) to the calling program.
12 
13 Notes:		This module began life as a Pascal routine for Oregon
14 		Software's Pascal-2 compiler, and was later rewritten
15 		into C.
16 
17 Acknowl.:	Some features of the AT&T public domain getopt()
18 		released to Usenet have been incorporated:
19 
20 		*  It is now possible to use syscalls for error
21 		   messages. I disagree slightly with this: the
22 		   only reason I can think of is to avoid getting
23 		   the stdio-package in linkage. getopt() is likely
24 		   to be used in programs where code size is a minor
25 		   issue. However, I've made it configurable.
26 
27 Author:		Anders Thulin
28 		Rydsvagen 288
29 		S-582 50 Linkoping
30 		SWEDEN
31 
32 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
33 
34 This program/module/routine has been developed as a part of `The Source Code
35 Project', a personal attempt at producing well-written, portable software
36 primarily intended to be executed in the ANSI C and POSIX / X/OPEN Unix
37 environments.
38 
39 The program/module/routine is hereby placed in the public domain, which
40 means that it may be freely copied, sold for profit or for fun, praised or
41 buried as your fancy takes you.
42 
43 Comments on the portability and adaptability of the program/module/routine
44 will be appreciated, and, if they result in modifications, acknowledged in
45 future releases.
46 
47 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
48 
49 Edit history :
50 
51 Vers  Ed   Date        By                Comments
52 ----  ---  ----------  ----------------  -------------------------------
53  1.0    0  1987-12-18  Anders Thulin     Initial version
54         1  1988-06-06  Anders Thulin     Got a copy of the AT&T public
55 					 domain getopt(). Changed
56 					 a few things to the better.
57 
58 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
59 
60 /* #include "config.h" -- included below:  */
61 
62 /*----------------------------------------------------*- Fundamental -*-
63 
64 Module:			getopt(3c)
65 
66 File:			config.h
67 
68 Associated files:	getopt.c
69 
70 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
71 
72 /*---  Configuration:  ------------------------------------------------
73 
74 getopt() may write error messages to stderr.
75 
76   USE_STDIO == 0	Use write(2, ...) for error messages
77   USE_STDIO == 1	Use fprintf(stderr, ...) for error messages
78 
79 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
80 
81 #define USE_STDIO	1	/* Default is 1 */
82 
83 /* -- end of "config.h" */
84 
85 #include <assert.h>
86 #include <stdio.h>		/* for EOF and (optionally) fprintf() */
87 #include <string.h>
88 
89 char   *optarg = (char *) 0;	/* Points to option argument (if any)	*/
90 int	opterr = 1;		/* Enable error reporting		*/
91 int	optind = 1;		/* Begin with argv[optind]		*/
92 
93 
94 #define BUMP(p)	(*++p == '\0' ? (optind++, (char *) 0) : p)
95 
96 #if USE_STDIO
97 # define ERROR(pname, s, ch)		\
98 	do {								\
99 	  if (opterr != 0) {						\
100              fprintf(stderr, "%s: %s -- '%c'\n", pname, (s), (ch));	\
101           }								\
102         } while (0)
103 #else
104 # define ERROR(pname, s, c)	do {					\
105 			  if (opterr != 0) {			\
106 #if __STDC__ != 0						\
107 		            extern int write(int, char *, unsigned); 	\
108 #else								\
109 			    extern int write();			\
110 #endif								\
111 			    char errbuf[4];			\
112 			    errbuf[0] = errbuf[2] = '\'';	\
113 			    errbuf[1] = (c); errbuf[3] = '\0';	\
114 			    write(2, (pname), strlen(pname));	\
115 			    write(2, ": " s " -- ", strlen(s)+6); \
116 			    write(2, errbuf,  strlen(errbuf));	\
117 			  }					\
118 			} while (1 == 0)
119 #endif /* USE_STDIO */
120 
121 
122 /*----------------------------------------------------------------------
123 
124 Routine:	getopt
125 
126 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
127 
getopt(argc,argv,optstring)128 int getopt(argc, argv, optstring)
129 int   argc;
130 char *argv[];
131 char *optstring;
132 {
133   static char *ap = (char *) 0;
134   char  *vp;
135   char   ch;
136 
137   optarg = (char *) 0;		/* Presume no-argument option		*/
138 
139   if (optind >= argc) {		/* Reached end of argv[]		*/
140     return EOF;
141   }
142 
143  /*
144   *  ap points into argv[optind], and is used to keep track of how far
145   *  we have parsed the current option string.  Remember that an argv[]
146   *  string may contain several separate option letters (e.g.  the line
147   *  'foo -abc -x -y' contains five option letters, but only three
148   *  arguments).
149   *
150   *  If ap == 0, we have reached a new string.  Check that it *is* a
151   *  legal option, that is:
152   *
153   *  +  check that the first character is '-'
154   *  +  check that the string isn't "-"
155   *  +  check that the string isn't "--"
156   *
157   */
158 
159   if (ap == (char *) 0) {
160     ap = argv[optind];
161 
162     if (*ap != '-') {		/* Doesn't begin with '-' -- not an option */
163       ap = (char *) 0;
164       return EOF;
165     }
166 
167     ap++;			/* Examine second character */
168 
169     if (*ap == '\0') {		/* String was "-" -- not an option */
170       ap = (char *) 0;
171       return EOF;
172     }
173 
174     if (*ap == '-' && *(ap+1) == '\0') {	/* String is "--" -- n.a.o. */
175       ap = (char *) 0;
176       optind++;
177       return EOF;
178     }
179   }
180 
181  /*
182   *  At this point we know that the string appears to be an option string.
183   *  Check that the current character really is one of those specified in
184   *  'optstring' (remember, ':' is NOT a permissible option character, as
185   *  it is used in optstring to specify that the option takes an argument).
186   *
187   *  Also note that strchr() treats the terminating NUL character as a part
188   *  of the string -- hence strchr() always finds NUL in optstring.  The
189   *  routine relies on ch never taking the value NUL.
190   *
191   */
192 
193   ch = *ap;
194   assert(ch != '\0');
195 
196   if (ch == ':' || (vp = strchr(optstring, ch)) == (char *) 0) {
197     ERROR(argv[0], "illegal option", ch);
198     ap = BUMP(ap);
199     return '?';
200   }
201 
202  /*
203   *  ch is a permissible option letter, and vp points to it in optstring.
204   *  If it takes an argument, that is it is followed by ':' in optstring,
205   *  extract the argument, and set optarg to point to it.  If not, just
206   *  return the option letter.
207   *
208   */
209 
210   if (*++vp == ':') {
211     optarg = BUMP(ap);
212     if (optarg == (char *) 0) {	 /* option value might be in next argument */
213       if (optind >= argc) {	 /* No, it wasn't */
214         ERROR(argv[0], "option requires an argument", ch);
215         ch = '?';
216       }
217       else {
218         optarg = argv[optind];
219       }
220     }
221     optind++;
222     ap = (char *) 0;
223   }
224   else {
225     ap = BUMP(ap);
226   }
227 
228   return ch;
229 }
230 
231 #ifdef TEST
232 
233 /*----------------------------------------------------------------------
234 
235 Routine:	main()
236 
237 Description:	This routine provides confidence testing of the
238 		getopt() routine.  It is rather	crude at present.  A
239 		better way of doing things would be to build argument
240 		structures in memory, sending them to getopt(), and
241 		check that the returned values are as expected.
242 
243 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
244 
main(argc,argv)245 int main(argc, argv)
246 int	argc;
247 char   *argv[];
248 {
249   int	c;
250   char *opts = "abcd:g0%";
251 
252   extern char *optarg;
253   extern int   optind;
254 
255   fprintf(stderr, "optstring = '%s'\n", opts);
256 
257   while ( (c = getopt(argc, argv, opts)) != EOF) {
258     switch(c) {
259       default :
260         fprintf(stderr, "getopt() returned unknown character (0x%x)\n", c);
261         break;
262 
263       case '?' :
264         fprintf(stderr, "getopt() returned '?' -- error detected\n");
265         break;
266 
267       case 'a' :
268       case 'b' :
269       case 'c' :
270       case 'g' :
271       case '0' :
272       case '%' :
273         fprintf(stderr, "getopt() returned option letter %c\n", c);
274         break;
275 
276       case 'd' :
277         fprintf(stderr, "getopt() returned option letter %c\n", c);
278         fprintf(stderr, "option value is '%s'\n", optarg);
279         break;
280 
281     }
282   }
283 
284   fprintf(stderr, "end of option list detected\n");
285   if (optind < argc) {
286     fprintf(stderr, "remaining arguments are:\n");
287     for ( ; optind <argc; optind++) {
288       fprintf(stderr, "argv[%d] = '%s'\n", optind, argv[optind]);
289     }
290   }
291 
292   return 0;
293 }
294 #endif
295