1 /* @(#)optget.c	1.9 16/06/19 Copyright 2015-2016 J. Schilling */
2 #include <schily/mconfig.h>
3 static	UConst char sccsid[] =
4 	"@(#)optget.c	1.9 16/06/19 Copyright 2015-2016 J. Schilling";
5 /*
6  *	A version of getopt() that maintains state
7  *	so it can be used from witin a shell builtin
8  *	without being in conflict with getopts(1).
9  *
10  *	Copyright (c) 2015-2016 J. Schilling
11  */
12 /*
13  * The contents of this file are subject to the terms of the
14  * Common Development and Distribution License, Version 1.0 only
15  * (the "License").  You may not use this file except in compliance
16  * with the License.
17  *
18  * See the file CDDL.Schily.txt in this distribution for details.
19  * A copy of the CDDL is also available via the Internet at
20  * http://www.opensource.org/licenses/cddl1.txt
21  *
22  * When distributing Covered Code, include this CDDL HEADER in each
23  * file and include the License file CDDL.Schily.txt from this distribution.
24  */
25 
26 #include "defs.h"
27 
28 void
optinit(optv)29 optinit(optv)
30 	struct optv	*optv;
31 {
32 #ifdef	__never__
33 	optv->opterr = 1;
34 #else
35 	optv->opterr = 0;	/* In the shell, like getopt() to be quiet */
36 #endif
37 	optv->optind = 1;
38 	optv->optopt = 0;
39 	optv->opt_sp = 1;
40 	optv->optret = 0;
41 	optv->ooptind = 1;
42 	optv->optflag = 0;
43 	optv->optarg = 0;
44 }
45 
46 /*
47  * Wrapper around getopt() that helps to preserve the state of the
48  * getopts(1) builtin. This only works with the AT&T getopt() extensions
49  * introduced in 1989 for the Bourne Shell (the state variable _sp).
50  */
51 int
optget(argc,argv,optv,optstring)52 optget(argc, argv, optv, optstring)
53 	int		argc;
54 	unsigned char	**argv;
55 	struct optv	*optv;
56 	const char	*optstring;
57 {
58 	int	ret;
59 	int	savopterr;
60 	int	savoptind;
61 	int	savoptopt;
62 	int	savopt_sp;
63 	char	*savoptarg;
64 
65 	savopterr = opterr;
66 	savoptind = optind;
67 	savoptopt = optopt;
68 	savopt_sp = _sp;
69 	savoptarg = optarg;
70 
71 	opterr = optv->opterr;
72 	optind = optv->optind;
73 	optopt = optv->optopt;
74 	_sp    = optv->opt_sp;
75 	optarg = optv->optarg;
76 
77 	optv->ooptind = optind;
78 	ret = getopt(argc, (char **)argv, optstring);
79 
80 	optv->opterr = opterr;
81 	optv->optind = optind;
82 	optv->optopt = optopt;
83 	optv->opt_sp = _sp;
84 	optv->optret = ret;
85 	optv->optarg = optarg;
86 
87 	opterr = savopterr;
88 	optind = savoptind;
89 	optopt = savoptopt;
90 	_sp    = savopt_sp;
91 	optarg = savoptarg;
92 
93 	return (ret);
94 }
95 
96 #ifndef	NO_OPTNEXT
97 /*
98  * Routine to complain about bad option arguments.
99  */
100 void
optbad(argc,argv,optv)101 optbad(argc, argv, optv)
102 	int		argc;
103 	unsigned char	**argv;
104 	struct optv	*optv;
105 {
106 	unsigned char	*p;
107 	unsigned char	opt[4];
108 
109 	if (argc > optv->ooptind) {
110 		/*
111 		 * This may be a long option, so print the whole argument.
112 		 */
113 		p = locstak();
114 		*p = ' ';
115 		movstrstak(argv[optv->ooptind], &p[1]);
116 		p += stakbot - p;
117 	} else {
118 		opt[0] = ' '; opt[1] = '-',
119 		opt[2] = optv->optopt; opt[3] = '\0';
120 		p = opt;
121 	}
122 	if (optv->optret == ':')
123 		bfailure(argv[0], "option requires argument", p);
124 	else
125 		bfailure(argv[0], badopt, p);
126 
127 	if ((optv->optflag & OPT_SPC) && !(flags & noexit))
128 		exitsh(ERROR);
129 }
130 
131 /*
132  * A variant of optget() that supports the -help option.
133  * Make sure not to use 999 as a long only option identifier in optstring.
134  * Use the null string ("") if no options besides -help are supported.
135  *
136  * Returns:
137  *		-1	End of args
138  *		0	-help or bad option
139  *		> 0	Option character or option identifier
140  */
141 int
optnext(argc,argv,optv,optstring,use)142 optnext(argc, argv, optv, optstring, use)
143 	int	argc;
144 	unsigned char	**argv;
145 	struct optv	*optv;
146 	const	 char	*optstring;
147 	const	 char	*use;
148 {
149 	struct optv soptv;
150 	int c;
151 
152 	soptv = *optv;
153 	if ((c = optget(argc, argv, optv, optstring)) != '?') {
154 		/* EMPTY */
155 		;
156 	} else if ((c = optget(argc, argv, &soptv, "()?999?(help)")) != '?') {
157 		*optv = soptv;
158 	}
159 
160 	switch (c) {
161 
162 	default:
163 		break;
164 
165 	case ':':	/* Option requires argument	*/
166 	case '?':	/* Bad option			*/
167 		if (optv->optflag & OPT_NOFAIL)
168 			return (c);
169 		optbad(argc, argv, optv);
170 		/* FALLTHROUGH */
171 
172 	case 999:
173 		if (use)
174 			gfailure((unsigned char *)usage, use);
175 		return (0);
176 	}
177 	return (c);
178 }
179 
180 /*
181  * This is the routine to enable POSIX untility syntax guidelines for builtins
182  * that do not implement options. We always implement "-help" and complain if
183  * any other (unknown) option was specified.
184  *
185  * Returns:
186  *		-1	-help or bad option
187  *		> 0	Arg index for next file type argument.
188  */
189 int
optskip(argc,argv,use)190 optskip(argc, argv, use)
191 	int	argc;
192 	unsigned char	**argv;
193 	const	 char	*use;
194 {
195 	struct optv optv;
196 	int c;
197 
198 	optinit(&optv);
199 
200 	while ((c = optnext(argc, argv, &optv, nullstr, use)) != -1) {
201 		if (c == 0)
202 			return (-1);
203 	}
204 	return (optv.optind);
205 }
206 #endif
207