1 /*
2  * getopt() - command option parsing
3  *
4  * Gunnar Ritter, Freiburg i. Br., Germany, March 2002.
5  */
6 
7 /*	Sccsid @(#)getopt.c	1.7 (gritter) 12/16/07	*/
8 
9 #include	"config.h"
10 #include	<sys/types.h>
11 #ifdef	HAVE_ALLOCA
12 #ifdef	HAVE_ALLOCA_H
13 #include	<alloca.h>
14 #else	/* !HAVE_ALLOCA_H */
15 #include	<stdlib.h>
16 #endif	/* !HAVE_ALLOCA_H */
17 #endif	/* HAVE_ALLOCA */
18 #include	<string.h>
19 
20 #ifdef	HAVE_ALLOCA
21 #define	ac_alloc(n)	alloca(n)
22 #define	ac_free(n)
23 #else	/* !HAVE_ALLOCA */
24 extern void	*smalloc(size_t);
25 #define	ac_alloc(n)	smalloc(n)
26 #define	ac_free(n)	free(n)
27 #endif	/* !HAVE_ALLOCA */
28 
29 #ifndef	HAVE_SSIZE_T
30 typedef	int	ssize_t;
31 #endif	/* !HAVE_SSIZE_T */
32 
33 /*
34  * One should not think that re-implementing this is necessary, but
35  *
36  * - Some libcs print weird messages.
37  *
38  * - GNU libc getopt() is totally brain-damaged, as it requires special
39  *   care _not_ to reorder parameters and can't be told to work correctly
40  *   with ':' as first optstring character at all.
41  */
42 
43 char	*optarg;
44 int	optind = 1;
45 int	opterr = 1;
46 int	optopt;
47 
48 static void
error(const char * s,int c)49 error(const char *s, int c)
50 {
51 	/*
52 	 * Avoid including <unistd.h>, in case its getopt() declaration
53 	 * conflicts.
54 	 */
55 	extern ssize_t	write(int, const void *, size_t);
56 	const char	*msg = 0;
57 	char	*buf, *bp;
58 
59 	switch (c) {
60 	case '?':
61 		msg = ": illegal option -- ";
62 		break;
63 	case ':':
64 		msg = ": option requires an argument -- ";
65 		break;
66 	}
67 	bp = buf = ac_alloc(strlen(s) + strlen(msg) + 2);
68 	while (*s)
69 		*bp++ = *s++;
70 	while (*msg)
71 		*bp++ = *msg++;
72 	*bp++ = optopt;
73 	*bp++ = '\n';
74 	write(2, buf, bp - buf);
75 	ac_free(buf);
76 }
77 
78 int
getopt(int argc,char * const argv[],const char * optstring)79 getopt(int argc, char *const argv[], const char *optstring)
80 {
81 	int	colon;
82 	static const char	*lastp;
83 	const char	*curp;
84 
85 	if (optstring[0] == ':') {
86 		colon = 1;
87 		optstring++;
88 	} else
89 		colon = 0;
90 	if (lastp) {
91 		curp = lastp;
92 		lastp = 0;
93 	} else {
94 		if (optind >= argc || argv[optind] == 0 ||
95 				argv[optind][0] != '-' ||
96 				argv[optind][1] == '\0')
97 			return -1;
98 		if (argv[optind][1] == '-' && argv[optind][2] == '\0') {
99 			optind++;
100 			return -1;
101 		}
102 		curp = &argv[optind][1];
103 	}
104 	optopt = curp[0] & 0377;
105 	while (optstring[0]) {
106 		if (optstring[0] == ':') {
107 			optstring++;
108 			continue;
109 		}
110 		if ((optstring[0] & 0377) == optopt) {
111 			if (optstring[1] == ':') {
112 				if (curp[1] != '\0') {
113 					optarg = (char *)&curp[1];
114 					optind++;
115 				} else {
116 					if ((optind += 2) > argc) {
117 						if (!colon && opterr)
118 							error(argv[0], ':');
119 						return colon ? ':' : '?';
120 					}
121 					optarg = argv[optind - 1];
122 				}
123 			} else {
124 				if (curp[1] != '\0')
125 					lastp = &curp[1];
126 				else
127 					optind++;
128 				optarg = 0;
129 			}
130 			return optopt;
131 		}
132 		optstring++;
133 	}
134 	if (!colon && opterr)
135 		error(argv[0], '?');
136 	if (curp[1] != '\0')
137 		lastp = &curp[1];
138 	else
139 		optind++;
140 	optarg = 0;
141 	return '?';
142 }
143 
144 #ifdef __APPLE__
145 /*
146  * Starting with Mac OS 10.5 Leopard, <unistd.h> turns getopt()
147  * into getopt$UNIX2003() by default. Consequently, this function
148  * is called instead of the one defined above. However, optind is
149  * still taken from this file, so in effect, options are not
150  * properly handled. Defining an own getopt$UNIX2003() function
151  * works around this issue.
152  */
153 int
getopt$UNIX2003(int argc,char * const argv[],const char * optstring)154 getopt$UNIX2003(int argc, char *const argv[], const char *optstring)
155 {
156 	return getopt(argc, argv, optstring);
157 }
158 #endif	/* __APPLE__ */
159