xref: /freebsd/usr.bin/pr/egetopt.c (revision 3494f7c0)
1 /*-
2  * SPDX-License-Identifier: BSD-4-Clause
3  *
4  * Copyright (c) 1991 Keith Muller.
5  * Copyright (c) 1993
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  * This code is derived from software contributed to Berkeley by
9  * Keith Muller of the University of California, San Diego.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. All advertising materials mentioning features or use of this software
20  *    must display the following acknowledgement:
21  *	This product includes software developed by the University of
22  *	California, Berkeley and its contributors.
23  * 4. Neither the name of the University nor the names of its contributors
24  *    may be used to endorse or promote products derived from this software
25  *    without specific prior written permission.
26  *
27  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37  * SUCH DAMAGE.
38  */
39 
40 #include <sys/cdefs.h>
41 #include <ctype.h>
42 #include <stdio.h>
43 #include <string.h>
44 
45 #include "extern.h"
46 
47 /*
48  * egetopt:	get option letter from argument vector (an extended
49  *		version of getopt).
50  *
51  * Non standard additions to the ostr specs are:
52  * 1) '?': immediate value following arg is optional (no white space
53  *    between the arg and the value)
54  * 2) '#': +/- followed by a number (with an optional sign but
55  *    no white space between the arg and the number). The - may be
56  *    combined with other options, but the + cannot.
57  */
58 
59 int	eopterr = 1;		/* if error message should be printed */
60 int	eoptind = 1;		/* index into parent argv vector */
61 int	eoptopt;		/* character checked for validity */
62 char	*eoptarg;		/* argument associated with option */
63 
64 #define	BADCH	(int)'?'
65 
66 static char	emsg[] = "";
67 
68 int
69 egetopt(int nargc, char * const *nargv, const char *ostr)
70 {
71 	static char *place = emsg;	/* option letter processing */
72 	char *oli;			/* option letter list index */
73 	static int delim;		/* which option delimiter */
74 	char *p;
75 	static char savec = '\0';
76 
77 	if (savec != '\0') {
78 		*place = savec;
79 		savec = '\0';
80 	}
81 
82 	if (!*place) {
83 		/*
84 		 * update scanning pointer
85 		 */
86 		if ((eoptind >= nargc) ||
87 		    ((*(place = nargv[eoptind]) != '-') && (*place != '+'))) {
88 			place = emsg;
89 			return (-1);
90 		}
91 
92 		delim = (int)*place;
93 		if (place[1] && *++place == '-' && !place[1]) {
94 			/*
95 			 * found "--"
96 			 */
97 			++eoptind;
98 			place = emsg;
99 			return (-1);
100 		}
101 	}
102 
103 	/*
104 	 * check option letter
105 	 */
106 	if ((eoptopt = (int)*place++) == (int)':' || (eoptopt == (int)'?') ||
107 	    !(oli = strchr(ostr, eoptopt))) {
108 		/*
109 		 * if the user didn't specify '-' as an option,
110 		 * assume it means -1 when by itself.
111 		 */
112 		if ((eoptopt == (int)'-') && !*place)
113 			return (-1);
114 		if (strchr(ostr, '#') && (isdigit(eoptopt) ||
115 		    (((eoptopt == (int)'-') || (eoptopt == (int)'+')) &&
116 		      isdigit(*place)))) {
117 			/*
118 			 * # option: +/- with a number is ok
119 			 */
120 			for (p = place; *p != '\0'; ++p) {
121 				if (!isdigit(*p))
122 					break;
123 			}
124 			eoptarg = place-1;
125 
126 			if (*p == '\0') {
127 				place = emsg;
128 				++eoptind;
129 			} else {
130 				place = p;
131 				savec = *p;
132 				*place = '\0';
133 			}
134 			return (delim);
135 		}
136 
137 		if (!*place)
138 			++eoptind;
139 		if (eopterr) {
140 			if (!(p = strrchr(*nargv, '/')))
141 				p = *nargv;
142 			else
143 				++p;
144 			(void)fprintf(stderr, "%s: illegal option -- %c\n",
145 			    p, eoptopt);
146 		}
147 		return (BADCH);
148 	}
149 	if (delim == (int)'+') {
150 		/*
151 		 * '+' is only allowed with numbers
152 		 */
153 		if (!*place)
154 			++eoptind;
155 		if (eopterr) {
156 			if (!(p = strrchr(*nargv, '/')))
157 				p = *nargv;
158 			else
159 				++p;
160 			(void)fprintf(stderr,
161 				"%s: illegal '+' delimiter with option -- %c\n",
162 				p, eoptopt);
163 		}
164 		return (BADCH);
165 	}
166 	++oli;
167 	if ((*oli != ':') && (*oli != '?')) {
168 		/*
169 		 * don't need argument
170 		 */
171 		eoptarg = NULL;
172 		if (!*place)
173 			++eoptind;
174 		return (eoptopt);
175 	}
176 
177 	if (*place) {
178 		/*
179 		 * no white space
180 		 */
181 		eoptarg = place;
182 	} else if (*oli == '?') {
183 		/*
184 		 * no arg, but NOT required
185 		 */
186 		eoptarg = NULL;
187 	} else if (nargc <= ++eoptind) {
188 		/*
189 		 * no arg, but IS required
190 		 */
191 		place = emsg;
192 		if (eopterr) {
193 			if (!(p = strrchr(*nargv, '/')))
194 				p = *nargv;
195 			else
196 				++p;
197 			(void)fprintf(stderr,
198 			    "%s: option requires an argument -- %c\n", p,
199 			    eoptopt);
200 		}
201 		return (BADCH);
202 	} else {
203 		/*
204 		 * arg has white space
205 		 */
206 		eoptarg = nargv[eoptind];
207 	}
208 	place = emsg;
209 	++eoptind;
210 	return (eoptopt);
211 }
212