xref: /netbsd/bin/ksh/c_ksh.c (revision 17485d16)
1*17485d16Schristos /*	$NetBSD: c_ksh.c,v 1.30 2021/09/16 19:43:33 christos Exp $	*/
22ab2e20cStls 
3e1b2664cSjtc /*
4e1b2664cSjtc  * built-in Korn commands: c_*
5e1b2664cSjtc  */
66377cac7Sagc #include <sys/cdefs.h>
76377cac7Sagc 
86377cac7Sagc #ifndef lint
9*17485d16Schristos __RCSID("$NetBSD: c_ksh.c,v 1.30 2021/09/16 19:43:33 christos Exp $");
106377cac7Sagc #endif
11e1b2664cSjtc 
121fe37413Skamil #include <sys/stat.h>
13e1b2664cSjtc #include <ctype.h>
14e1b2664cSjtc 
151fe37413Skamil #include "sh.h"
161fe37413Skamil 
17e1b2664cSjtc int
c_cd(wp)18e1b2664cSjtc c_cd(wp)
19e1b2664cSjtc 	char	**wp;
20e1b2664cSjtc {
21e1b2664cSjtc 	int optc;
22e1b2664cSjtc 	int physical = Flag(FPHYSICAL);
23e1b2664cSjtc 	int cdnode;			/* was a node from cdpath added in? */
24e1b2664cSjtc 	int printpath = 0;		/* print where we cd'd? */
25e1b2664cSjtc 	int rval;
26e1b2664cSjtc 	struct tbl *pwd_s, *oldpwd_s;
27e1b2664cSjtc 	XString xs;
28e1b2664cSjtc 	char *xp;
29e1b2664cSjtc 	char *dir, *try, *pwd;
30e1b2664cSjtc 	int phys_path;
31e1b2664cSjtc 	char *cdpath;
32eab2cd7cSchristos 	char *fdir = NULL;
33e1b2664cSjtc 
34e1b2664cSjtc 	while ((optc = ksh_getopt(wp, &builtin_opt, "LP")) != EOF)
35e1b2664cSjtc 		switch (optc) {
36e1b2664cSjtc 		case 'L':
37e1b2664cSjtc 			physical = 0;
38e1b2664cSjtc 			break;
39e1b2664cSjtc 		case 'P':
40e1b2664cSjtc 			physical = 1;
41e1b2664cSjtc 			break;
42e1b2664cSjtc 		case '?':
43e1b2664cSjtc 			return 1;
44e1b2664cSjtc 		}
45e1b2664cSjtc 	wp += builtin_opt.optind;
46e1b2664cSjtc 
47e1b2664cSjtc 	if (Flag(FRESTRICTED)) {
48e1b2664cSjtc 		bi_errorf("restricted shell - can't cd");
49e1b2664cSjtc 		return 1;
50e1b2664cSjtc 	}
51e1b2664cSjtc 
52e1b2664cSjtc 	pwd_s = global("PWD");
53e1b2664cSjtc 	oldpwd_s = global("OLDPWD");
54e1b2664cSjtc 
55e1b2664cSjtc 	if (!wp[0]) {
56e1b2664cSjtc 		/* No arguments - go home */
57e1b2664cSjtc 		if ((dir = str_val(global("HOME"))) == null) {
58e1b2664cSjtc 			bi_errorf("no home directory (HOME not set)");
59e1b2664cSjtc 			return 1;
60e1b2664cSjtc 		}
61e1b2664cSjtc 	} else if (!wp[1]) {
62e1b2664cSjtc 		/* One argument: - or dir */
63e1b2664cSjtc 		dir = wp[0];
64e1b2664cSjtc 		if (strcmp(dir, "-") == 0) {
65e1b2664cSjtc 			dir = str_val(oldpwd_s);
66e1b2664cSjtc 			if (dir == null) {
67e1b2664cSjtc 				bi_errorf("no OLDPWD");
68e1b2664cSjtc 				return 1;
69e1b2664cSjtc 			}
70e1b2664cSjtc 			printpath++;
71e1b2664cSjtc 		}
72e1b2664cSjtc 	} else if (!wp[2]) {
73e1b2664cSjtc 		/* Two arguments - substitute arg1 in PWD for arg2 */
74e1b2664cSjtc 		int ilen, olen, nlen, elen;
75e1b2664cSjtc 		char *cp;
76e1b2664cSjtc 
77e1b2664cSjtc 		if (!current_wd[0]) {
78e1b2664cSjtc 			bi_errorf("don't know current directory");
79e1b2664cSjtc 			return 1;
80e1b2664cSjtc 		}
81f662a744Smycroft 		/* substitute arg1 for arg2 in current path.
82e1b2664cSjtc 		 * if the first substitution fails because the cd fails
83e1b2664cSjtc 		 * we could try to find another substitution. For now
84e1b2664cSjtc 		 * we don't
85e1b2664cSjtc 		 */
86e1b2664cSjtc 		if ((cp = strstr(current_wd, wp[0])) == (char *) 0) {
87e1b2664cSjtc 			bi_errorf("bad substitution");
88e1b2664cSjtc 			return 1;
89e1b2664cSjtc 		}
90e1b2664cSjtc 		ilen = cp - current_wd;
91e1b2664cSjtc 		olen = strlen(wp[0]);
92e1b2664cSjtc 		nlen = strlen(wp[1]);
93e1b2664cSjtc 		elen = strlen(current_wd + ilen + olen) + 1;
94eab2cd7cSchristos 		fdir = dir = alloc(ilen + nlen + elen, ATEMP);
95e1b2664cSjtc 		memcpy(dir, current_wd, ilen);
96e1b2664cSjtc 		memcpy(dir + ilen, wp[1], nlen);
97e1b2664cSjtc 		memcpy(dir + ilen + nlen, current_wd + ilen + olen, elen);
98e1b2664cSjtc 		printpath++;
99e1b2664cSjtc 	} else {
100e1b2664cSjtc 		bi_errorf("too many arguments");
101e1b2664cSjtc 		return 1;
102e1b2664cSjtc 	}
103e1b2664cSjtc 
104e1b2664cSjtc 	Xinit(xs, xp, PATH, ATEMP);
105e1b2664cSjtc 	/* xp will have a bogus value after make_path() - set it to 0
106e1b2664cSjtc 	 * so that if it's used, it will cause a dump
107e1b2664cSjtc 	 */
108e1b2664cSjtc 	xp = (char *) 0;
109e1b2664cSjtc 
110e1b2664cSjtc 	cdpath = str_val(global("CDPATH"));
111e1b2664cSjtc 	do {
112e1b2664cSjtc 		cdnode = make_path(current_wd, dir, &cdpath, &xs, &phys_path);
113e1b2664cSjtc #ifdef S_ISLNK
114e1b2664cSjtc 		if (physical)
115e1b2664cSjtc 			rval = chdir(try = Xstring(xs, xp) + phys_path);
116e1b2664cSjtc 		else
117e1b2664cSjtc #endif /* S_ISLNK */
118e1b2664cSjtc 		{
119e1b2664cSjtc 			simplify_path(Xstring(xs, xp));
120e1b2664cSjtc 			rval = chdir(try = Xstring(xs, xp));
121e1b2664cSjtc 		}
122e1b2664cSjtc 	} while (rval < 0 && cdpath != (char *) 0);
123e1b2664cSjtc 
124e1b2664cSjtc 	if (rval < 0) {
125e1b2664cSjtc 		if (cdnode)
126e1b2664cSjtc 			bi_errorf("%s: bad directory", dir);
127e1b2664cSjtc 		else
128e1b2664cSjtc 			bi_errorf("%s - %s", try, strerror(errno));
129eab2cd7cSchristos 		if (fdir)
130eab2cd7cSchristos 			afree(fdir, ATEMP);
131e1b2664cSjtc 		return 1;
132e1b2664cSjtc 	}
133e1b2664cSjtc 
134e1b2664cSjtc 	/* Clear out tracked aliases with relative paths */
135e1b2664cSjtc 	flushcom(0);
136e1b2664cSjtc 
13748ee8d12Shubertf 	/* Set OLDPWD (note: unsetting OLDPWD does not disable this
13848ee8d12Shubertf 	 * setting in at&t ksh)
13948ee8d12Shubertf 	 */
140e1b2664cSjtc 	if (current_wd[0])
14148ee8d12Shubertf 		/* Ignore failure (happens if readonly or integer) */
14248ee8d12Shubertf 		setstr(oldpwd_s, current_wd, KSH_RETURN_ERROR);
143e1b2664cSjtc 
144e1b2664cSjtc 	if (!ISABSPATH(Xstring(xs, xp))) {
145e1b2664cSjtc 		pwd = (char *) 0;
146e1b2664cSjtc 	} else
147e1b2664cSjtc #ifdef S_ISLNK
148e1b2664cSjtc 	if (!physical || !(pwd = get_phys_path(Xstring(xs, xp))))
149e1b2664cSjtc #endif /* S_ISLNK */
150e1b2664cSjtc 		pwd = Xstring(xs, xp);
151e1b2664cSjtc 
152e1b2664cSjtc 	/* Set PWD */
153e1b2664cSjtc 	if (pwd) {
15448ee8d12Shubertf 		char *ptmp = pwd;
15548ee8d12Shubertf 		set_current_wd(ptmp);
15648ee8d12Shubertf 		/* Ignore failure (happens if readonly or integer) */
15748ee8d12Shubertf 		setstr(pwd_s, ptmp, KSH_RETURN_ERROR);
158e1b2664cSjtc 	} else {
159e1b2664cSjtc 		set_current_wd(null);
160e1b2664cSjtc 		pwd = Xstring(xs, xp);
161e1b2664cSjtc 		/* XXX unset $PWD? */
162e1b2664cSjtc 	}
163e1b2664cSjtc 	if (printpath || cdnode)
164e1b2664cSjtc 		shprintf("%s\n", pwd);
165e1b2664cSjtc 
166eab2cd7cSchristos 	if (fdir)
167eab2cd7cSchristos 		afree(fdir, ATEMP);
168eab2cd7cSchristos 
169e1b2664cSjtc 	return 0;
170e1b2664cSjtc }
171e1b2664cSjtc 
172e1b2664cSjtc int
c_pwd(wp)173e1b2664cSjtc c_pwd(wp)
174e1b2664cSjtc 	char	**wp;
175e1b2664cSjtc {
176e1b2664cSjtc 	int optc;
177e1b2664cSjtc 	int physical = Flag(FPHYSICAL);
1786ce96df0Schristos 	char *p, *freep = NULL;
179e1b2664cSjtc 
180e1b2664cSjtc 	while ((optc = ksh_getopt(wp, &builtin_opt, "LP")) != EOF)
181e1b2664cSjtc 		switch (optc) {
182e1b2664cSjtc 		case 'L':
183e1b2664cSjtc 			physical = 0;
184e1b2664cSjtc 			break;
185e1b2664cSjtc 		case 'P':
186e1b2664cSjtc 			physical = 1;
187e1b2664cSjtc 			break;
188e1b2664cSjtc 		case '?':
189e1b2664cSjtc 			return 1;
190e1b2664cSjtc 		}
191e1b2664cSjtc 	wp += builtin_opt.optind;
192e1b2664cSjtc 
193e1b2664cSjtc 	if (wp[0]) {
194e1b2664cSjtc 		bi_errorf("too many arguments");
195e1b2664cSjtc 		return 1;
196e1b2664cSjtc 	}
197e1b2664cSjtc #ifdef S_ISLNK
198e1b2664cSjtc 	p = current_wd[0] ? (physical ? get_phys_path(current_wd) : current_wd)
199e1b2664cSjtc 			  : (char *) 0;
200e1b2664cSjtc #else /* S_ISLNK */
201e1b2664cSjtc 	p = current_wd[0] ? current_wd : (char *) 0;
202e1b2664cSjtc #endif /* S_ISLNK */
203e1b2664cSjtc 	if (p && eaccess(p, R_OK) < 0)
204e1b2664cSjtc 		p = (char *) 0;
205e1b2664cSjtc 	if (!p) {
2066ce96df0Schristos 		freep = p = ksh_get_wd((char *) 0, 0);
207e1b2664cSjtc 		if (!p) {
208e1b2664cSjtc 			bi_errorf("can't get current directory - %s",
209e1b2664cSjtc 				strerror(errno));
210e1b2664cSjtc 			return 1;
211e1b2664cSjtc 		}
212e1b2664cSjtc 	}
213e1b2664cSjtc 	shprintf("%s\n", p);
2146ce96df0Schristos 	if (freep)
2156ce96df0Schristos 		afree(freep, ATEMP);
216e1b2664cSjtc 	return 0;
217e1b2664cSjtc }
218e1b2664cSjtc 
219e1b2664cSjtc int
c_print(wp)220e1b2664cSjtc c_print(wp)
221e1b2664cSjtc 	char **wp;
222e1b2664cSjtc {
223e1b2664cSjtc #define PO_NL		BIT(0)	/* print newline */
224e1b2664cSjtc #define PO_EXPAND	BIT(1)	/* expand backslash sequences */
225e1b2664cSjtc #define PO_PMINUSMINUS	BIT(2)	/* print a -- argument */
226e1b2664cSjtc #define PO_HIST		BIT(3)	/* print to history instead of stdout */
227e1b2664cSjtc #define PO_COPROC	BIT(4)	/* printing to coprocess: block SIGPIPE */
228e1b2664cSjtc 	int fd = 1;
229e1b2664cSjtc 	int flags = PO_EXPAND|PO_NL;
230e1b2664cSjtc 	char *s;
231e1b2664cSjtc 	const char *emsg;
232e1b2664cSjtc 	XString xs;
233e1b2664cSjtc 	char *xp;
234e1b2664cSjtc 
235e1b2664cSjtc 	if (wp[0][0] == 'e') {	/* echo command */
236e1b2664cSjtc 		int nflags = flags;
237e1b2664cSjtc 
238e1b2664cSjtc 		/* A compromise between sysV and BSD echo commands:
239e1b2664cSjtc 		 * escape sequences are enabled by default, and
240e1b2664cSjtc 		 * -n, -e and -E are recognized if they appear
241e1b2664cSjtc 		 * in arguments with no illegal options (ie, echo -nq
242e1b2664cSjtc 		 * will print -nq).
243e1b2664cSjtc 		 * Different from sysV echo since options are recognized,
244e1b2664cSjtc 		 * different from BSD echo since escape sequences are enabled
245e1b2664cSjtc 		 * by default.
246e1b2664cSjtc 		 */
247e1b2664cSjtc 		wp += 1;
248e1b2664cSjtc 		while ((s = *wp) && *s == '-' && s[1]) {
249e1b2664cSjtc 			while (*++s)
250e1b2664cSjtc 				if (*s == 'n')
251e1b2664cSjtc 					nflags &= ~PO_NL;
252e1b2664cSjtc 				else if (*s == 'e')
253e1b2664cSjtc 					nflags |= PO_EXPAND;
254e1b2664cSjtc 				else if (*s == 'E')
255e1b2664cSjtc 					nflags &= ~PO_EXPAND;
256e1b2664cSjtc 				else
257e1b2664cSjtc 					/* bad option: don't use nflags, print
258e1b2664cSjtc 					 * argument
259e1b2664cSjtc 					 */
260e1b2664cSjtc 					break;
261e1b2664cSjtc 			if (*s)
262e1b2664cSjtc 				break;
263e1b2664cSjtc 			wp++;
264e1b2664cSjtc 			flags = nflags;
265e1b2664cSjtc 		}
266e1b2664cSjtc 	} else {
267e1b2664cSjtc 		int optc;
268e1b2664cSjtc 		const char *options = "Rnprsu,";
269e1b2664cSjtc 		while ((optc = ksh_getopt(wp, &builtin_opt, options)) != EOF)
270e1b2664cSjtc 			switch (optc) {
271e1b2664cSjtc 			  case 'R': /* fake BSD echo command */
272e1b2664cSjtc 				flags |= PO_PMINUSMINUS;
273e1b2664cSjtc 				flags &= ~PO_EXPAND;
274e1b2664cSjtc 				options = "ne";
275e1b2664cSjtc 				break;
276e1b2664cSjtc 			  case 'e':
277e1b2664cSjtc 				flags |= PO_EXPAND;
278e1b2664cSjtc 				break;
279e1b2664cSjtc 			  case 'n':
280e1b2664cSjtc 				flags &= ~PO_NL;
281e1b2664cSjtc 				break;
282e1b2664cSjtc #ifdef KSH
283e1b2664cSjtc 			  case 'p':
284e1b2664cSjtc 				if ((fd = coproc_getfd(W_OK, &emsg)) < 0) {
285e1b2664cSjtc 					bi_errorf("-p: %s", emsg);
286e1b2664cSjtc 					return 1;
287e1b2664cSjtc 				}
288e1b2664cSjtc 				break;
289e1b2664cSjtc #endif /* KSH */
290e1b2664cSjtc 			  case 'r':
291e1b2664cSjtc 				flags &= ~PO_EXPAND;
292e1b2664cSjtc 				break;
293e1b2664cSjtc 			  case 's':
294e1b2664cSjtc 				flags |= PO_HIST;
295e1b2664cSjtc 				break;
296e1b2664cSjtc 			  case 'u':
297e1b2664cSjtc 				if (!*(s = builtin_opt.optarg))
298e1b2664cSjtc 					fd = 0;
299e1b2664cSjtc 				else if ((fd = check_fd(s, W_OK, &emsg)) < 0) {
300e1b2664cSjtc 					bi_errorf("-u: %s: %s", s, emsg);
301e1b2664cSjtc 					return 1;
302e1b2664cSjtc 				}
303e1b2664cSjtc 				break;
304e1b2664cSjtc 			  case '?':
305e1b2664cSjtc 				return 1;
306e1b2664cSjtc 			}
307e1b2664cSjtc 		if (!(builtin_opt.info & GI_MINUSMINUS)) {
308e1b2664cSjtc 			/* treat a lone - like -- */
309e1b2664cSjtc 			if (wp[builtin_opt.optind]
310e1b2664cSjtc 			    && strcmp(wp[builtin_opt.optind], "-") == 0)
311e1b2664cSjtc 				builtin_opt.optind++;
312e1b2664cSjtc 		} else if (flags & PO_PMINUSMINUS)
313e1b2664cSjtc 			builtin_opt.optind--;
314e1b2664cSjtc 		wp += builtin_opt.optind;
315e1b2664cSjtc 	}
316e1b2664cSjtc 
317e1b2664cSjtc 	Xinit(xs, xp, 128, ATEMP);
318e1b2664cSjtc 
319e1b2664cSjtc 	while (*wp != NULL) {
320ce8041bdSkamil 		int c;
321e1b2664cSjtc 		s = *wp;
322e1b2664cSjtc 		while ((c = *s++) != '\0') {
323e1b2664cSjtc 			Xcheck(xs, xp);
324e1b2664cSjtc 			if ((flags & PO_EXPAND) && c == '\\') {
325e1b2664cSjtc 				int i;
326e1b2664cSjtc 
327e1b2664cSjtc 				switch ((c = *s++)) {
328e1b2664cSjtc 				/* Oddly enough, \007 seems more portable than
3296a952bdfSkamil 				 * \a (due to old pcc's,
330e1b2664cSjtc 				 * etc.).
331e1b2664cSjtc 				 */
332e1b2664cSjtc 				case 'a': c = '\007'; break;
333e1b2664cSjtc 				case 'b': c = '\b'; break;
334e1b2664cSjtc 				case 'c': flags &= ~PO_NL;
335e1b2664cSjtc 					  continue; /* AT&T brain damage */
336e1b2664cSjtc 				case 'f': c = '\f'; break;
337e1b2664cSjtc 				case 'n': c = '\n'; break;
338e1b2664cSjtc 				case 'r': c = '\r'; break;
339e1b2664cSjtc 				case 't': c = '\t'; break;
340e1b2664cSjtc 				case 'v': c = 0x0B; break;
341e1b2664cSjtc 				case '0':
342e1b2664cSjtc 					/* Look for an octal number: can have
343e1b2664cSjtc 					 * three digits (not counting the
344f662a744Smycroft 					 * leading 0).  Truly burnt.
345e1b2664cSjtc 					 */
346e1b2664cSjtc 					c = 0;
347e1b2664cSjtc 					for (i = 0; i < 3; i++) {
348e1b2664cSjtc 						if (*s >= '0' && *s <= '7')
349e1b2664cSjtc 							c = c*8 + *s++ - '0';
350e1b2664cSjtc 						else
351e1b2664cSjtc 							break;
352e1b2664cSjtc 					}
353e1b2664cSjtc 					break;
354e1b2664cSjtc 				case '\0': s--; c = '\\'; break;
355e1b2664cSjtc 				case '\\': break;
356e1b2664cSjtc 				default:
357e1b2664cSjtc 					Xput(xs, xp, '\\');
358e1b2664cSjtc 				}
359e1b2664cSjtc 			}
360e1b2664cSjtc 			Xput(xs, xp, c);
361e1b2664cSjtc 		}
362e1b2664cSjtc 		if (*++wp != NULL)
363e1b2664cSjtc 			Xput(xs, xp, ' ');
364e1b2664cSjtc 	}
365e1b2664cSjtc 	if (flags & PO_NL)
366e1b2664cSjtc 		Xput(xs, xp, '\n');
367e1b2664cSjtc 
368e1b2664cSjtc 	if (flags & PO_HIST) {
369e1b2664cSjtc 		Xput(xs, xp, '\0');
370e1b2664cSjtc 		source->line++;
371e1b2664cSjtc 		histsave(source->line, Xstring(xs, xp), 1);
372e1b2664cSjtc 		Xfree(xs, xp);
373e1b2664cSjtc 	} else {
374e1b2664cSjtc 		int n, len = Xlength(xs, xp);
375e1b2664cSjtc 		int UNINITIALIZED(opipe);
376f662a744Smycroft #ifdef KSH
377e1b2664cSjtc 
378e1b2664cSjtc 		/* Ensure we aren't killed by a SIGPIPE while writing to
379e1b2664cSjtc 		 * a coprocess.  at&t ksh doesn't seem to do this (seems
380e1b2664cSjtc 		 * to just check that the co-process is alive, which is
381e1b2664cSjtc 		 * not enough).
382e1b2664cSjtc 		 */
383e1b2664cSjtc 		if (coproc.write >= 0 && coproc.write == fd) {
384e1b2664cSjtc 			flags |= PO_COPROC;
385e1b2664cSjtc 			opipe = block_pipe();
386e1b2664cSjtc 		}
387e1b2664cSjtc #endif /* KSH */
388e1b2664cSjtc 		for (s = Xstring(xs, xp); len > 0; ) {
389e1b2664cSjtc 			n = write(fd, s, len);
390e1b2664cSjtc 			if (n < 0) {
3915a412486Sjtc #ifdef KSH
392e1b2664cSjtc 				if (flags & PO_COPROC)
393e1b2664cSjtc 					restore_pipe(opipe);
3945a412486Sjtc #endif /* KSH */
395e1b2664cSjtc 				if (errno == EINTR) {
396e1b2664cSjtc 					/* allow user to ^C out */
397e1b2664cSjtc 					intrcheck();
3985a412486Sjtc #ifdef KSH
399e1b2664cSjtc 					if (flags & PO_COPROC)
400e1b2664cSjtc 						opipe = block_pipe();
4015a412486Sjtc #endif /* KSH */
402e1b2664cSjtc 					continue;
403e1b2664cSjtc 				}
404e1b2664cSjtc #ifdef KSH
405e1b2664cSjtc 				/* This doesn't really make sense - could
406e1b2664cSjtc 				 * break scripts (print -p generates
407e1b2664cSjtc 				 * error message).
408e1b2664cSjtc 				*if (errno == EPIPE)
409e1b2664cSjtc 				*	coproc_write_close(fd);
410e1b2664cSjtc 				 */
411e1b2664cSjtc #endif /* KSH */
412e1b2664cSjtc 				return 1;
413e1b2664cSjtc 			}
414e1b2664cSjtc 			s += n;
415e1b2664cSjtc 			len -= n;
416e1b2664cSjtc 		}
417e1b2664cSjtc #ifdef KSH
418e1b2664cSjtc 		if (flags & PO_COPROC)
419e1b2664cSjtc 			restore_pipe(opipe);
420e1b2664cSjtc #endif /* KSH */
421e1b2664cSjtc 	}
422e1b2664cSjtc 
423e1b2664cSjtc 	return 0;
424e1b2664cSjtc }
425e1b2664cSjtc 
426e1b2664cSjtc int
c_whence(wp)427e1b2664cSjtc c_whence(wp)
428e1b2664cSjtc 	char **wp;
429e1b2664cSjtc {
430e1b2664cSjtc 	struct tbl *tp;
431e1b2664cSjtc 	char *id;
432e1b2664cSjtc 	int pflag = 0, vflag = 0, Vflag = 0;
433e1b2664cSjtc 	int ret = 0;
434e1b2664cSjtc 	int optc;
435e1b2664cSjtc 	int iam_whence = wp[0][0] == 'w';
436e1b2664cSjtc 	int fcflags;
437e1b2664cSjtc 	const char *options = iam_whence ? "pv" : "pvV";
438e1b2664cSjtc 
439e1b2664cSjtc 	while ((optc = ksh_getopt(wp, &builtin_opt, options)) != EOF)
440e1b2664cSjtc 		switch (optc) {
441e1b2664cSjtc 		case 'p':
442e1b2664cSjtc 			pflag = 1;
443e1b2664cSjtc 			break;
444e1b2664cSjtc 		case 'v':
445e1b2664cSjtc 			vflag = 1;
446e1b2664cSjtc 			break;
447e1b2664cSjtc 		case 'V':
448e1b2664cSjtc 			Vflag = 1;
449e1b2664cSjtc 			break;
450e1b2664cSjtc 		case '?':
451e1b2664cSjtc 			return 1;
452e1b2664cSjtc 		}
453e1b2664cSjtc 	wp += builtin_opt.optind;
454e1b2664cSjtc 
455e1b2664cSjtc 
456e1b2664cSjtc 	fcflags = FC_BI | FC_PATH | FC_FUNC;
457e1b2664cSjtc 	if (!iam_whence) {
458e1b2664cSjtc 		/* Note that -p on its own is deal with in comexec() */
459e1b2664cSjtc 		if (pflag)
460e1b2664cSjtc 			fcflags |= FC_DEFPATH;
461e1b2664cSjtc 		/* Convert command options to whence options - note that
462e1b2664cSjtc 		 * command -pV uses a different path search than whence -v
463e1b2664cSjtc 		 * or whence -pv.  This should be considered a feature.
464e1b2664cSjtc 		 */
465e1b2664cSjtc 		vflag = Vflag;
466e1b2664cSjtc 	}
467e1b2664cSjtc 	if (pflag)
468e1b2664cSjtc 		fcflags &= ~(FC_BI | FC_FUNC);
469e1b2664cSjtc 
470e1b2664cSjtc 	while ((vflag || ret == 0) && (id = *wp++) != NULL) {
471e1b2664cSjtc 		tp = NULL;
472e1b2664cSjtc 		if ((iam_whence || vflag) && !pflag)
473ed120974Skamil 			tp = mytsearch(&keywords, id, hash(id));
474e1b2664cSjtc 		if (!tp && !pflag) {
475ed120974Skamil 			tp = mytsearch(&aliases, id, hash(id));
476e1b2664cSjtc 			if (tp && !(tp->flag & ISSET))
477e1b2664cSjtc 				tp = NULL;
478e1b2664cSjtc 		}
479e1b2664cSjtc 		if (!tp)
480e1b2664cSjtc 			tp = findcom(id, fcflags);
481e1b2664cSjtc 		if (vflag || (tp->type != CALIAS && tp->type != CEXEC
482e1b2664cSjtc 			      && tp->type != CTALIAS))
483e1b2664cSjtc 			shprintf("%s", id);
484e1b2664cSjtc 		switch (tp->type) {
485e1b2664cSjtc 		  case CKEYWD:
486e1b2664cSjtc 			if (vflag)
487e1b2664cSjtc 				shprintf(" is a reserved word");
488e1b2664cSjtc 			break;
489e1b2664cSjtc 		  case CALIAS:
490e1b2664cSjtc 			if (vflag)
491e1b2664cSjtc 				shprintf(" is an %salias for ",
492e1b2664cSjtc 					(tp->flag & EXPORT) ? "exported "
493e1b2664cSjtc 							    : null);
494e1b2664cSjtc 			if (!iam_whence && !vflag)
495e1b2664cSjtc 				shprintf("alias %s=", id);
496e1b2664cSjtc 			print_value_quoted(tp->val.s);
497e1b2664cSjtc 			break;
498e1b2664cSjtc 		  case CFUNC:
499e1b2664cSjtc 			if (vflag) {
500e1b2664cSjtc 				shprintf(" is a");
501e1b2664cSjtc 				if (tp->flag & EXPORT)
502e1b2664cSjtc 					shprintf("n exported");
503e1b2664cSjtc 				if (tp->flag & TRACE)
504e1b2664cSjtc 					shprintf(" traced");
505e1b2664cSjtc 				if (!(tp->flag & ISSET)) {
506e1b2664cSjtc 					shprintf(" undefined");
507e1b2664cSjtc 					if (tp->u.fpath)
508e1b2664cSjtc 						shprintf(" (autoload from %s)",
509e1b2664cSjtc 							tp->u.fpath);
510e1b2664cSjtc 				}
511e1b2664cSjtc 				shprintf(" function");
512e1b2664cSjtc 			}
513e1b2664cSjtc 			break;
514e1b2664cSjtc 		  case CSHELL:
515e1b2664cSjtc 			if (vflag)
516e1b2664cSjtc 				shprintf(" is a%s shell builtin",
517e1b2664cSjtc 				    (tp->flag & SPEC_BI) ? " special" : null);
518e1b2664cSjtc 			break;
519e1b2664cSjtc 		  case CTALIAS:
520e1b2664cSjtc 		  case CEXEC:
521e1b2664cSjtc 			if (tp->flag & ISSET) {
522e1b2664cSjtc 				if (vflag) {
523e1b2664cSjtc 					shprintf(" is ");
524e1b2664cSjtc 					if (tp->type == CTALIAS)
525e1b2664cSjtc 						shprintf(
526e1b2664cSjtc 						    "a tracked %salias for ",
527e1b2664cSjtc 							(tp->flag & EXPORT) ?
528e1b2664cSjtc 								"exported "
529e1b2664cSjtc 							      : null);
530e1b2664cSjtc 				}
531e1b2664cSjtc 				shprintf("%s", tp->val.s);
532e1b2664cSjtc 			} else {
533e1b2664cSjtc 				if (vflag)
534e1b2664cSjtc 					shprintf(" not found");
535e1b2664cSjtc 				ret = 1;
536e1b2664cSjtc 			}
537e1b2664cSjtc 			break;
538e1b2664cSjtc 		  default:
539e1b2664cSjtc 			shprintf("%s is *GOK*", id);
540e1b2664cSjtc 			break;
541e1b2664cSjtc 		}
542e1b2664cSjtc 		if (vflag || !ret)
5435d331600Sjoerg 			shprintf("%s", newline);
544e1b2664cSjtc 	}
545e1b2664cSjtc 	return ret;
546e1b2664cSjtc }
547e1b2664cSjtc 
548e1b2664cSjtc /* Deal with command -vV - command -p dealt with in comexec() */
549e1b2664cSjtc int
c_command(wp)550e1b2664cSjtc c_command(wp)
551e1b2664cSjtc 	char **wp;
552e1b2664cSjtc {
553e1b2664cSjtc 	/* Let c_whence do the work.  Note that c_command() must be
554e1b2664cSjtc 	 * a distinct function from c_whence() (tested in comexec()).
555e1b2664cSjtc 	 */
556e1b2664cSjtc 	return c_whence(wp);
557e1b2664cSjtc }
558e1b2664cSjtc 
559e1b2664cSjtc /* typeset, export, and readonly */
560e1b2664cSjtc int
c_typeset(wp)561e1b2664cSjtc c_typeset(wp)
562e1b2664cSjtc 	char **wp;
563e1b2664cSjtc {
564e1b2664cSjtc 	struct block *l = e->loc;
565e1b2664cSjtc 	struct tbl *vp, **p;
566e1b2664cSjtc 	Tflag fset = 0, fclr = 0;
567a397ec1fSchristos 	int thing = 0, func = 0, localv = 0;
56848ee8d12Shubertf 	const char *options = "L#R#UZ#fi#lprtux";	/* see comment below */
569e1b2664cSjtc 	char *fieldstr, *basestr;
570e1b2664cSjtc 	int field, base;
571e1b2664cSjtc 	int optc;
572e1b2664cSjtc 	Tflag flag;
573e1b2664cSjtc 	int pflag = 0;
574e1b2664cSjtc 
575e1b2664cSjtc 	switch (**wp) {
576e1b2664cSjtc  	  case 'e':		/* export */
577e1b2664cSjtc  		fset |= EXPORT;
578e1b2664cSjtc 		options = "p";
579e1b2664cSjtc  		break;
580e1b2664cSjtc  	  case 'r':		/* readonly */
581e1b2664cSjtc  		fset |= RDONLY;
582e1b2664cSjtc 		options = "p";
583e1b2664cSjtc  		break;
584e1b2664cSjtc 	  case 's':		/* set */
585e1b2664cSjtc 		/* called with 'typeset -' */
586e1b2664cSjtc 		break;
587e1b2664cSjtc  	  case 't':		/* typeset */
588a397ec1fSchristos  		localv = 1;
589e1b2664cSjtc  		break;
590e1b2664cSjtc  	}
591e1b2664cSjtc 
592e1b2664cSjtc 	fieldstr = basestr = (char *) 0;
593e1b2664cSjtc 	builtin_opt.flags |= GF_PLUSOPT;
594e1b2664cSjtc 	/* at&t ksh seems to have 0-9 as options, which are multiplied
595e1b2664cSjtc 	 * to get a number that is used with -L, -R, -Z or -i (eg, -1R2
596e1b2664cSjtc 	 * sets right justify in a field of 12).  This allows options
597e1b2664cSjtc 	 * to be grouped in an order (eg, -Lu12), but disallows -i8 -L3 and
59848ee8d12Shubertf 	 * does not allow the number to be specified as a separate argument
599e1b2664cSjtc 	 * Here, the number must follow the RLZi option, but is optional
600e1b2664cSjtc 	 * (see the # kludge in ksh_getopt()).
601e1b2664cSjtc 	 */
602e1b2664cSjtc 	while ((optc = ksh_getopt(wp, &builtin_opt, options)) != EOF) {
603e1b2664cSjtc 		flag = 0;
604e1b2664cSjtc 		switch (optc) {
605e1b2664cSjtc 		  case 'L':
60648ee8d12Shubertf 			flag = LJUST;
607e1b2664cSjtc 			fieldstr = builtin_opt.optarg;
608e1b2664cSjtc 			break;
609e1b2664cSjtc 		  case 'R':
61048ee8d12Shubertf 			flag = RJUST;
611e1b2664cSjtc 			fieldstr = builtin_opt.optarg;
612e1b2664cSjtc 			break;
613e1b2664cSjtc 		  case 'U':
614e1b2664cSjtc 			/* at&t ksh uses u, but this conflicts with
615e1b2664cSjtc 			 * upper/lower case.  If this option is changed,
616e1b2664cSjtc 			 * need to change the -U below as well
617e1b2664cSjtc 			 */
61848ee8d12Shubertf 			flag = INT_U;
619e1b2664cSjtc 			break;
620e1b2664cSjtc 		  case 'Z':
62148ee8d12Shubertf 			flag = ZEROFIL;
622e1b2664cSjtc 			fieldstr = builtin_opt.optarg;
623e1b2664cSjtc 			break;
624e1b2664cSjtc 		  case 'f':
625e1b2664cSjtc 			func = 1;
626e1b2664cSjtc 			break;
627e1b2664cSjtc 		  case 'i':
62848ee8d12Shubertf 			flag = INTEGER;
629e1b2664cSjtc 			basestr = builtin_opt.optarg;
630e1b2664cSjtc 			break;
631e1b2664cSjtc 		  case 'l':
63248ee8d12Shubertf 			flag = LCASEV;
633e1b2664cSjtc 			break;
63448ee8d12Shubertf 		  case 'p': /* posix export/readonly -p flag.
635f662a744Smycroft 			     * typeset -p is the same as typeset (in pdksh);
63640ac8480Swiz 			     * here for compatibility with ksh93.
63748ee8d12Shubertf 			     */
638e1b2664cSjtc 			pflag = 1;
639e1b2664cSjtc 			break;
640e1b2664cSjtc 		  case 'r':
64148ee8d12Shubertf 			flag = RDONLY;
642e1b2664cSjtc 			break;
643e1b2664cSjtc 		  case 't':
64448ee8d12Shubertf 			flag = TRACE;
645e1b2664cSjtc 			break;
646e1b2664cSjtc 		  case 'u':
64748ee8d12Shubertf 			flag = UCASEV_AL;	/* upper case / autoload */
648e1b2664cSjtc 			break;
649e1b2664cSjtc 		  case 'x':
65048ee8d12Shubertf 			flag = EXPORT;
651e1b2664cSjtc 			break;
652e1b2664cSjtc 		  case '?':
653e1b2664cSjtc 			return 1;
654e1b2664cSjtc 		}
655e1b2664cSjtc 		if (builtin_opt.info & GI_PLUS) {
656e1b2664cSjtc 			fclr |= flag;
657e1b2664cSjtc 			fset &= ~flag;
658e1b2664cSjtc 			thing = '+';
659e1b2664cSjtc 		} else {
660e1b2664cSjtc 			fset |= flag;
661e1b2664cSjtc 			fclr &= ~flag;
662e1b2664cSjtc 			thing = '-';
663e1b2664cSjtc 		}
664e1b2664cSjtc 	}
665e1b2664cSjtc 
666e1b2664cSjtc 	field = 0;
667e1b2664cSjtc 	if (fieldstr && !bi_getn(fieldstr, &field))
668e1b2664cSjtc 		return 1;
669e1b2664cSjtc 	base = 0;
670e1b2664cSjtc 	if (basestr && !bi_getn(basestr, &base))
671e1b2664cSjtc 		return 1;
672e1b2664cSjtc 
673e1b2664cSjtc 	if (!(builtin_opt.info & GI_MINUSMINUS) && wp[builtin_opt.optind]
674e1b2664cSjtc 	    && (wp[builtin_opt.optind][0] == '-'
675e1b2664cSjtc 		|| wp[builtin_opt.optind][0] == '+')
676e1b2664cSjtc 	    && wp[builtin_opt.optind][1] == '\0')
677e1b2664cSjtc 	{
678e1b2664cSjtc 		thing = wp[builtin_opt.optind][0];
679e1b2664cSjtc 		builtin_opt.optind++;
680e1b2664cSjtc 	}
681e1b2664cSjtc 
682e1b2664cSjtc 	if (func && ((fset|fclr) & ~(TRACE|UCASEV_AL|EXPORT))) {
683e1b2664cSjtc 		bi_errorf("only -t, -u and -x options may be used with -f");
684e1b2664cSjtc 		return 1;
685e1b2664cSjtc 	}
686e1b2664cSjtc 	if (wp[builtin_opt.optind]) {
68748ee8d12Shubertf 		/* Take care of exclusions.
68848ee8d12Shubertf 		 * At this point, flags in fset are cleared in fclr and vise
68948ee8d12Shubertf 		 * versa.  This property should be preserved.
69048ee8d12Shubertf 		 */
69148ee8d12Shubertf 		if (fset & LCASEV)	/* LCASEV has priority over UCASEV_AL */
69248ee8d12Shubertf 			fset &= ~UCASEV_AL;
69348ee8d12Shubertf 		if (fset & LJUST)	/* LJUST has priority over RJUST */
69448ee8d12Shubertf 			fset &= ~RJUST;
69548ee8d12Shubertf 		if ((fset & (ZEROFIL|LJUST)) == ZEROFIL) { /* -Z implies -ZR */
69648ee8d12Shubertf 			fset |= RJUST;
69748ee8d12Shubertf 			fclr &= ~RJUST;
69848ee8d12Shubertf 		}
69948ee8d12Shubertf 		/* Setting these attributes clears the others, unless they
700e1b2664cSjtc 		 * are also set in this command
701e1b2664cSjtc 		 */
702e1b2664cSjtc 		if (fset & (LJUST|RJUST|ZEROFIL|UCASEV_AL|LCASEV|INTEGER
703e1b2664cSjtc 			    |INT_U|INT_L))
704e1b2664cSjtc 			fclr |= ~fset &
705e1b2664cSjtc 				(LJUST|RJUST|ZEROFIL|UCASEV_AL|LCASEV|INTEGER
706e1b2664cSjtc 				 |INT_U|INT_L);
707e1b2664cSjtc 	}
708e1b2664cSjtc 
709e1b2664cSjtc 	/* set variables and attributes */
710e1b2664cSjtc 	if (wp[builtin_opt.optind]) {
711e1b2664cSjtc 		int i;
712e1b2664cSjtc 		int rval = 0;
713e1b2664cSjtc 		struct tbl *f;
714e1b2664cSjtc 
715a397ec1fSchristos 		if (localv && !func)
716e1b2664cSjtc 			fset |= LOCAL;
717e1b2664cSjtc 		for (i = builtin_opt.optind; wp[i]; i++) {
718e1b2664cSjtc 			if (func) {
719e1b2664cSjtc 				f = findfunc(wp[i], hash(wp[i]),
72013351aa1Skamil 					     (fset&UCASEV_AL) ? true : false);
721e1b2664cSjtc 				if (!f) {
722e1b2664cSjtc 					/* at&t ksh does ++rval: bogus */
723e1b2664cSjtc 					rval = 1;
724e1b2664cSjtc 					continue;
725e1b2664cSjtc 				}
726e1b2664cSjtc 				if (fset | fclr) {
727e1b2664cSjtc 					f->flag |= fset;
728e1b2664cSjtc 					f->flag &= ~fclr;
729e1b2664cSjtc 				} else
730e1b2664cSjtc 					fptreef(shl_stdout, 0,
73148ee8d12Shubertf 						f->flag & FKSH ?
73248ee8d12Shubertf 						    "function %s %T\n"
73348ee8d12Shubertf 						    : "%s() %T\n"
73448ee8d12Shubertf 						,
735e1b2664cSjtc 						wp[i], f->val.t);
736e1b2664cSjtc 			} else if (!typeset(wp[i], fset, fclr, field, base)) {
737e1b2664cSjtc 				bi_errorf("%s: not identifier", wp[i]);
738e1b2664cSjtc 				return 1;
739e1b2664cSjtc 			}
740e1b2664cSjtc 		}
741e1b2664cSjtc 		return rval;
742e1b2664cSjtc 	}
743e1b2664cSjtc 
744e1b2664cSjtc 	/* list variables and attributes */
745e1b2664cSjtc 	flag = fset | fclr; /* no difference at this point.. */
746e1b2664cSjtc 	if (func) {
747e1b2664cSjtc 	    for (l = e->loc; l; l = l->next) {
748e1b2664cSjtc 		for (p = tsort(&l->funs); (vp = *p++); ) {
749e1b2664cSjtc 		    if (flag && (vp->flag & flag) == 0)
750e1b2664cSjtc 			    continue;
751e1b2664cSjtc 		    if (thing == '-')
75248ee8d12Shubertf 			fptreef(shl_stdout, 0, vp->flag & FKSH ?
75348ee8d12Shubertf 						    "function %s %T\n"
75448ee8d12Shubertf 						    : "%s() %T\n",
755e1b2664cSjtc 				vp->name, vp->val.t);
756e1b2664cSjtc 		    else
757e1b2664cSjtc 			shprintf("%s\n", vp->name);
758e1b2664cSjtc 		}
759e1b2664cSjtc 	    }
760e1b2664cSjtc 	} else {
761e1b2664cSjtc 	    for (l = e->loc; l; l = l->next) {
76248ee8d12Shubertf 		for (p = tsort(&l->vars); (vp = *p++); ) {
76348ee8d12Shubertf 		    struct tbl *tvp;
76448ee8d12Shubertf 		    int any_set = 0;
76548ee8d12Shubertf 		    /*
76648ee8d12Shubertf 		     * See if the parameter is set (for arrays, if any
76748ee8d12Shubertf 		     * element is set).
76848ee8d12Shubertf 		     */
76948ee8d12Shubertf 		    for (tvp = vp; tvp; tvp = tvp->u.array)
77048ee8d12Shubertf 			if (tvp->flag & ISSET) {
77148ee8d12Shubertf 			    any_set = 1;
77248ee8d12Shubertf 			    break;
77348ee8d12Shubertf 			}
77448ee8d12Shubertf 		    /*
77548ee8d12Shubertf 		     * Check attributes - note that all array elements
77648ee8d12Shubertf 		     * have (should have?) the same attributes, so checking
77748ee8d12Shubertf 		     * the first is sufficient.
77848ee8d12Shubertf 		     *
77948ee8d12Shubertf 		     * Report an unset param only if the user has
780614eee46Sjtc 		     * explicitly given it some attribute (like export);
781614eee46Sjtc 		     * otherwise, after "echo $FOO", we would report FOO...
782614eee46Sjtc 		     */
78348ee8d12Shubertf 		    if (!any_set && !(vp->flag & USERATTRIB))
784614eee46Sjtc 			continue;
785e1b2664cSjtc 		    if (flag && (vp->flag & flag) == 0)
786e1b2664cSjtc 			continue;
78748ee8d12Shubertf 		    for (; vp; vp = vp->u.array) {
78848ee8d12Shubertf 			/* Ignore array elements that aren't set unless there
78948ee8d12Shubertf 			 * are no set elements, in which case the first is
79048ee8d12Shubertf 			 * reported on
79148ee8d12Shubertf 			 */
79248ee8d12Shubertf 			if ((vp->flag&ARRAY) && any_set && !(vp->flag & ISSET))
79348ee8d12Shubertf 			    continue;
794e1b2664cSjtc 			/* no arguments */
795e1b2664cSjtc 			if (thing == 0 && flag == 0) {
796e1b2664cSjtc 			    /* at&t ksh prints things like export, integer,
797e1b2664cSjtc 			     * leftadj, zerofill, etc., but POSIX says must
798e1b2664cSjtc 			     * be suitable for re-entry...
799e1b2664cSjtc 			     */
800e1b2664cSjtc 			    shprintf("typeset ");
801e1b2664cSjtc 			    if ((vp->flag&INTEGER))
802e1b2664cSjtc 				shprintf("-i ");
803e1b2664cSjtc 			    if ((vp->flag&EXPORT))
804e1b2664cSjtc 				shprintf("-x ");
805e1b2664cSjtc 			    if ((vp->flag&RDONLY))
806e1b2664cSjtc 				shprintf("-r ");
807e1b2664cSjtc 			    if ((vp->flag&TRACE))
808e1b2664cSjtc 				shprintf("-t ");
809e1b2664cSjtc 			    if ((vp->flag&LJUST))
810e1b2664cSjtc 				shprintf("-L%d ", vp->u2.field);
811e1b2664cSjtc 			    if ((vp->flag&RJUST))
812e1b2664cSjtc 				shprintf("-R%d ", vp->u2.field);
813e1b2664cSjtc 			    if ((vp->flag&ZEROFIL))
814e1b2664cSjtc 				shprintf("-Z ");
815e1b2664cSjtc 			    if ((vp->flag&LCASEV))
816e1b2664cSjtc 				shprintf("-l ");
817e1b2664cSjtc 			    if ((vp->flag&UCASEV_AL))
818e1b2664cSjtc 				shprintf("-u ");
819e1b2664cSjtc 			    if ((vp->flag&INT_U))
820e1b2664cSjtc 				shprintf("-U ");
821e1b2664cSjtc 			    shprintf("%s\n", vp->name);
82248ee8d12Shubertf 			    if (vp->flag&ARRAY)
82348ee8d12Shubertf 				break;
824e1b2664cSjtc 			} else {
825e1b2664cSjtc 			    if (pflag)
826e1b2664cSjtc 				shprintf("%s ",
827e1b2664cSjtc 				    (flag & EXPORT) ?  "export" : "readonly");
82848ee8d12Shubertf 			    if ((vp->flag&ARRAY) && any_set)
829e1b2664cSjtc 				shprintf("%s[%d]", vp->name, vp->index);
830e1b2664cSjtc 			    else
831e1b2664cSjtc 				shprintf("%s", vp->name);
832e1b2664cSjtc 			    if (thing == '-' && (vp->flag&ISSET)) {
833e1b2664cSjtc 				char *s = str_val(vp);
834e1b2664cSjtc 
835e1b2664cSjtc 				shprintf("=");
836e1b2664cSjtc 				/* at&t ksh can't have justified integers.. */
837e1b2664cSjtc 				if ((vp->flag & (INTEGER|LJUST|RJUST))
838e1b2664cSjtc 								== INTEGER)
839e1b2664cSjtc 				    shprintf("%s", s);
840e1b2664cSjtc 				else
841e1b2664cSjtc 				    print_value_quoted(s);
842e1b2664cSjtc 			    }
8435d331600Sjoerg 			    shprintf("%s", newline);
844e1b2664cSjtc 			}
84548ee8d12Shubertf 			/* Only report first `element' of an array with
84648ee8d12Shubertf 			 * no set elements.
84748ee8d12Shubertf 			 */
84848ee8d12Shubertf 			if (!any_set)
84948ee8d12Shubertf 			    break;
85048ee8d12Shubertf 		    }
851e1b2664cSjtc 		}
852e1b2664cSjtc 	    }
853e1b2664cSjtc 	}
854e1b2664cSjtc 	return 0;
855e1b2664cSjtc }
856e1b2664cSjtc 
857e1b2664cSjtc int
c_alias(wp)858e1b2664cSjtc c_alias(wp)
859e1b2664cSjtc 	char **wp;
860e1b2664cSjtc {
861e1b2664cSjtc 	struct table *t = &aliases;
86248ee8d12Shubertf 	int rv = 0, rflag = 0, tflag, Uflag = 0, pflag = 0;
86348ee8d12Shubertf 	int prefix = 0;
864e1b2664cSjtc 	Tflag xflag = 0;
865e1b2664cSjtc 	int optc;
866e1b2664cSjtc 
86748ee8d12Shubertf 	builtin_opt.flags |= GF_PLUSOPT;
86848ee8d12Shubertf 	while ((optc = ksh_getopt(wp, &builtin_opt, "dprtUx")) != EOF) {
86948ee8d12Shubertf 		prefix = builtin_opt.info & GI_PLUS ? '+' : '-';
870e1b2664cSjtc 		switch (optc) {
871e1b2664cSjtc 		  case 'd':
872e1b2664cSjtc 			t = &homedirs;
873e1b2664cSjtc 			break;
87448ee8d12Shubertf 		  case 'p':
87548ee8d12Shubertf 			pflag = 1;
87648ee8d12Shubertf 			break;
877e1b2664cSjtc 		  case 'r':
878e1b2664cSjtc 			rflag = 1;
879e1b2664cSjtc 			break;
880e1b2664cSjtc 		  case 't':
881e1b2664cSjtc 			t = &taliases;
882e1b2664cSjtc 			break;
883e1b2664cSjtc 		  case 'U': /* kludge for tracked alias initialization
884e1b2664cSjtc 			     * (don't do a path search, just make an entry)
885e1b2664cSjtc 			     */
886e1b2664cSjtc 			Uflag = 1;
887e1b2664cSjtc 			break;
888e1b2664cSjtc 		  case 'x':
889e1b2664cSjtc 			xflag = EXPORT;
890e1b2664cSjtc 			break;
891e1b2664cSjtc 		  case '?':
892e1b2664cSjtc 			return 1;
893e1b2664cSjtc 		}
89448ee8d12Shubertf 	}
895e1b2664cSjtc 	wp += builtin_opt.optind;
896e1b2664cSjtc 
89748ee8d12Shubertf 	if (!(builtin_opt.info & GI_MINUSMINUS) && *wp
89848ee8d12Shubertf 	    && (wp[0][0] == '-' || wp[0][0] == '+') && wp[0][1] == '\0')
89948ee8d12Shubertf 	{
90048ee8d12Shubertf 		prefix = wp[0][0];
90148ee8d12Shubertf 		wp++;
90248ee8d12Shubertf 	}
90348ee8d12Shubertf 
904e1b2664cSjtc 	tflag = t == &taliases;
905e1b2664cSjtc 
906e1b2664cSjtc 	/* "hash -r" means reset all the tracked aliases.. */
907e1b2664cSjtc 	if (rflag) {
908e1b2664cSjtc 		static const char *const args[] = {
909e1b2664cSjtc 			    "unalias", "-ta", (const char *) 0
910e1b2664cSjtc 			};
911e1b2664cSjtc 
912e1b2664cSjtc 		if (!tflag || *wp) {
913e1b2664cSjtc 			shprintf(
914e1b2664cSjtc 	    "alias: -r flag can only be used with -t and without arguments\n");
915e1b2664cSjtc 			return 1;
916e1b2664cSjtc 		}
917e1b2664cSjtc 		ksh_getopt_reset(&builtin_opt, GF_ERROR);
918a397ec1fSchristos 		return c_unalias((char **)__UNCONST(args));
919e1b2664cSjtc 	}
920e1b2664cSjtc 
92148ee8d12Shubertf 
922e1b2664cSjtc 	if (*wp == NULL) {
923e1b2664cSjtc 		struct tbl *ap, **p;
924e1b2664cSjtc 
925e1b2664cSjtc 		for (p = tsort(t); (ap = *p++) != NULL; )
926e1b2664cSjtc 			if ((ap->flag & (ISSET|xflag)) == (ISSET|xflag)) {
92748ee8d12Shubertf 				if (pflag)
92848ee8d12Shubertf 					shf_puts("alias ", shl_stdout);
92948ee8d12Shubertf 				shf_puts(ap->name, shl_stdout);
93048ee8d12Shubertf 				if (prefix != '+') {
93148ee8d12Shubertf 					shf_putc('=', shl_stdout);
932e1b2664cSjtc 					print_value_quoted(ap->val.s);
93348ee8d12Shubertf 				}
9345d331600Sjoerg 				shprintf("%s", newline);
935e1b2664cSjtc 			}
936e1b2664cSjtc 	}
937e1b2664cSjtc 
938e1b2664cSjtc 	for (; *wp != NULL; wp++) {
939e1b2664cSjtc 		char *alias = *wp;
940e1b2664cSjtc 		char *val = strchr(alias, '=');
941e1b2664cSjtc 		char *newval;
942e1b2664cSjtc 		struct tbl *ap;
943e1b2664cSjtc 		int h;
944e1b2664cSjtc 
945e1b2664cSjtc 		if (val)
946e1b2664cSjtc 			alias = str_nsave(alias, val++ - alias, ATEMP);
947e1b2664cSjtc 		h = hash(alias);
948e1b2664cSjtc 		if (val == NULL && !tflag && !xflag) {
949ed120974Skamil 			ap = mytsearch(t, alias, h);
950e1b2664cSjtc 			if (ap != NULL && (ap->flag&ISSET)) {
95148ee8d12Shubertf 				if (pflag)
95248ee8d12Shubertf 					shf_puts("alias ", shl_stdout);
95348ee8d12Shubertf 				shf_puts(ap->name, shl_stdout);
95448ee8d12Shubertf 				if (prefix != '+') {
95548ee8d12Shubertf 					shf_putc('=', shl_stdout);
956e1b2664cSjtc 					print_value_quoted(ap->val.s);
95748ee8d12Shubertf 				}
9585d331600Sjoerg 				shprintf("%s", newline);
959e1b2664cSjtc 			} else {
960e1b2664cSjtc 				shprintf("%s alias not found\n", alias);
961e1b2664cSjtc 				rv = 1;
962e1b2664cSjtc 			}
963e1b2664cSjtc 			continue;
964e1b2664cSjtc 		}
965e1b2664cSjtc 		ap = tenter(t, alias, h);
966e1b2664cSjtc 		ap->type = tflag ? CTALIAS : CALIAS;
967e1b2664cSjtc 		/* Are we setting the value or just some flags? */
968e1b2664cSjtc 		if ((val && !tflag) || (!val && tflag && !Uflag)) {
969e1b2664cSjtc 			if (ap->flag&ALLOC) {
970e1b2664cSjtc 				ap->flag &= ~(ALLOC|ISSET);
971e1b2664cSjtc 				afree((void*)ap->val.s, APERM);
972e1b2664cSjtc 			}
973e1b2664cSjtc 			/* ignore values for -t (at&t ksh does this) */
974e1b2664cSjtc 			newval = tflag ? search(alias, path, X_OK, (int *) 0)
975e1b2664cSjtc 					: val;
976e1b2664cSjtc 			if (newval) {
977e1b2664cSjtc 				ap->val.s = str_save(newval, APERM);
978e1b2664cSjtc 				ap->flag |= ALLOC|ISSET;
979e1b2664cSjtc 			} else
980e1b2664cSjtc 				ap->flag &= ~ISSET;
981e1b2664cSjtc 		}
98248ee8d12Shubertf 		ap->flag |= DEFINED;
98348ee8d12Shubertf 		if (prefix == '+')
98448ee8d12Shubertf 			ap->flag &= ~xflag;
98548ee8d12Shubertf 		else
98648ee8d12Shubertf 			ap->flag |= xflag;
987e1b2664cSjtc 		if (val)
988e1b2664cSjtc 			afree(alias, ATEMP);
989e1b2664cSjtc 	}
990e1b2664cSjtc 
991e1b2664cSjtc 	return rv;
992e1b2664cSjtc }
993e1b2664cSjtc 
994e1b2664cSjtc int
c_unalias(wp)995e1b2664cSjtc c_unalias(wp)
996e1b2664cSjtc 	char **wp;
997e1b2664cSjtc {
998ce8041bdSkamil 	struct table *t = &aliases;
999ce8041bdSkamil 	struct tbl *ap;
1000e1b2664cSjtc 	int rv = 0, all = 0;
1001e1b2664cSjtc 	int optc;
1002e1b2664cSjtc 
1003e1b2664cSjtc 	while ((optc = ksh_getopt(wp, &builtin_opt, "adt")) != EOF)
1004e1b2664cSjtc 		switch (optc) {
1005e1b2664cSjtc 		  case 'a':
1006e1b2664cSjtc 			all = 1;
1007e1b2664cSjtc 			break;
1008e1b2664cSjtc 		  case 'd':
1009e1b2664cSjtc 			t = &homedirs;
1010e1b2664cSjtc 			break;
1011e1b2664cSjtc 		  case 't':
1012e1b2664cSjtc 			t = &taliases;
1013e1b2664cSjtc 			break;
1014e1b2664cSjtc 		  case '?':
1015e1b2664cSjtc 			return 1;
1016e1b2664cSjtc 		}
1017e1b2664cSjtc 	wp += builtin_opt.optind;
1018e1b2664cSjtc 
1019e1b2664cSjtc 	for (; *wp != NULL; wp++) {
1020ed120974Skamil 		ap = mytsearch(t, *wp, hash(*wp));
1021e1b2664cSjtc 		if (ap == NULL) {
1022e1b2664cSjtc 			rv = 1;	/* POSIX */
1023e1b2664cSjtc 			continue;
1024e1b2664cSjtc 		}
1025e1b2664cSjtc 		if (ap->flag&ALLOC) {
1026e1b2664cSjtc 			ap->flag &= ~(ALLOC|ISSET);
1027e1b2664cSjtc 			afree((void*)ap->val.s, APERM);
1028e1b2664cSjtc 		}
1029e1b2664cSjtc 		ap->flag &= ~(DEFINED|ISSET|EXPORT);
1030e1b2664cSjtc 	}
1031e1b2664cSjtc 
1032e1b2664cSjtc 	if (all) {
1033e1b2664cSjtc 		struct tstate ts;
1034e1b2664cSjtc 
1035822c36bbSkamil 		for (ksh_twalk(&ts, t); (ap = tnext(&ts)); ) {
1036e1b2664cSjtc 			if (ap->flag&ALLOC) {
1037e1b2664cSjtc 				ap->flag &= ~(ALLOC|ISSET);
1038e1b2664cSjtc 				afree((void*)ap->val.s, APERM);
1039e1b2664cSjtc 			}
1040e1b2664cSjtc 			ap->flag &= ~(DEFINED|ISSET|EXPORT);
1041e1b2664cSjtc 		}
1042e1b2664cSjtc 	}
1043e1b2664cSjtc 
1044e1b2664cSjtc 	return rv;
1045e1b2664cSjtc }
1046e1b2664cSjtc 
10475a412486Sjtc #ifdef KSH
1048e1b2664cSjtc int
c_let(wp)1049e1b2664cSjtc c_let(wp)
1050e1b2664cSjtc 	char **wp;
1051e1b2664cSjtc {
1052e1b2664cSjtc 	int rv = 1;
1053e1b2664cSjtc 	long val;
1054e1b2664cSjtc 
1055e1b2664cSjtc 	if (wp[1] == (char *) 0) /* at&t ksh does this */
1056e1b2664cSjtc 		bi_errorf("no arguments");
1057e1b2664cSjtc 	else
1058e1b2664cSjtc 		for (wp++; *wp; wp++)
105948ee8d12Shubertf 			if (!evaluate(*wp, &val, KSH_RETURN_ERROR)) {
1060e1b2664cSjtc 				rv = 2;	/* distinguish error from zero result */
1061e1b2664cSjtc 				break;
1062e1b2664cSjtc 			} else
1063e1b2664cSjtc 				rv = val == 0;
1064e1b2664cSjtc 	return rv;
1065e1b2664cSjtc }
10665a412486Sjtc #endif /* KSH */
1067e1b2664cSjtc 
1068e1b2664cSjtc int
c_jobs(wp)1069e1b2664cSjtc c_jobs(wp)
1070e1b2664cSjtc 	char **wp;
1071e1b2664cSjtc {
1072e1b2664cSjtc 	int optc;
1073e1b2664cSjtc 	int flag = 0;
1074e1b2664cSjtc 	int nflag = 0;
1075*17485d16Schristos 	int Zflag = 0;
1076e1b2664cSjtc 	int rv = 0;
1077e1b2664cSjtc 
1078*17485d16Schristos 	while ((optc = ksh_getopt(wp, &builtin_opt, "lpnzZ")) != EOF)
1079e1b2664cSjtc 		switch (optc) {
1080e1b2664cSjtc 		  case 'l':
1081e1b2664cSjtc 			flag = 1;
1082e1b2664cSjtc 			break;
1083e1b2664cSjtc 		  case 'p':
1084e1b2664cSjtc 			flag = 2;
1085e1b2664cSjtc 			break;
1086e1b2664cSjtc 		  case 'n':
1087e1b2664cSjtc 			nflag = 1;
1088e1b2664cSjtc 			break;
1089e1b2664cSjtc 		  case 'z':	/* debugging: print zombies */
1090e1b2664cSjtc 			nflag = -1;
1091e1b2664cSjtc 			break;
1092*17485d16Schristos 		  case 'Z':
1093*17485d16Schristos 			Zflag = 1;
1094*17485d16Schristos 			break;
1095e1b2664cSjtc 		  case '?':
1096e1b2664cSjtc 			return 1;
1097e1b2664cSjtc 		}
1098e1b2664cSjtc 	wp += builtin_opt.optind;
1099*17485d16Schristos 	if (Zflag) {
1100*17485d16Schristos 		if (*wp && **wp) {
1101*17485d16Schristos 			setproctitle("%s", *wp);
1102*17485d16Schristos 		} else {
1103*17485d16Schristos 			setproctitle(NULL);
1104*17485d16Schristos 		}
1105*17485d16Schristos 		return 0;
1106*17485d16Schristos 	}
110745e5a869Sthorpej 	if (!*wp) {
1108e1b2664cSjtc 		if (j_jobs((char *) 0, flag, nflag))
1109e1b2664cSjtc 			rv = 1;
111045e5a869Sthorpej 	} else {
1111f662a744Smycroft 		for (; *wp; wp++)
1112e1b2664cSjtc 			if (j_jobs(*wp, flag, nflag))
1113e1b2664cSjtc 				rv = 1;
111445e5a869Sthorpej 	}
1115e1b2664cSjtc 	return rv;
1116e1b2664cSjtc }
1117e1b2664cSjtc 
1118e1b2664cSjtc #ifdef JOBS
1119e1b2664cSjtc int
c_fgbg(wp)1120e1b2664cSjtc c_fgbg(wp)
1121e1b2664cSjtc 	char **wp;
1122e1b2664cSjtc {
1123e1b2664cSjtc 	int bg = strcmp(*wp, "bg") == 0;
1124e1b2664cSjtc 	int UNINITIALIZED(rv);
1125e1b2664cSjtc 
1126e1b2664cSjtc 	if (!Flag(FMONITOR)) {
1127e1b2664cSjtc 		bi_errorf("job control not enabled");
1128e1b2664cSjtc 		return 1;
1129e1b2664cSjtc 	}
1130e1b2664cSjtc 	if (ksh_getopt(wp, &builtin_opt, null) == '?')
1131e1b2664cSjtc 		return 1;
1132e1b2664cSjtc 	wp += builtin_opt.optind;
1133e1b2664cSjtc 	if (*wp)
1134e1b2664cSjtc 		for (; *wp; wp++)
1135e1b2664cSjtc 			rv = j_resume(*wp, bg);
1136e1b2664cSjtc 	else
1137e1b2664cSjtc 		rv = j_resume("%%", bg);
1138e1b2664cSjtc 	/* POSIX says fg shall return 0 (unless an error occurs).
1139e1b2664cSjtc 	 * at&t ksh returns the exit value of the job...
1140e1b2664cSjtc 	 */
1141e1b2664cSjtc 	return (bg || Flag(FPOSIX)) ? 0 : rv;
1142e1b2664cSjtc }
1143e1b2664cSjtc #endif
1144e1b2664cSjtc 
1145e1b2664cSjtc struct kill_info {
1146e1b2664cSjtc 	int num_width;
1147e1b2664cSjtc 	int name_width;
1148e1b2664cSjtc };
1149e1b2664cSjtc static char *kill_fmt_entry ARGS((void *arg, int i, char *buf, int buflen));
1150e1b2664cSjtc 
1151e1b2664cSjtc /* format a single kill item */
1152e1b2664cSjtc static char *
kill_fmt_entry(arg,i,buf,buflen)1153e1b2664cSjtc kill_fmt_entry(arg, i, buf, buflen)
1154e1b2664cSjtc 	void *arg;
1155e1b2664cSjtc 	int i;
1156e1b2664cSjtc 	char *buf;
1157e1b2664cSjtc 	int buflen;
1158e1b2664cSjtc {
1159e1b2664cSjtc 	struct kill_info *ki = (struct kill_info *) arg;
1160e1b2664cSjtc 
1161e1b2664cSjtc 	i++;
1162e1b2664cSjtc 	if (sigtraps[i].name)
1163e1b2664cSjtc 		shf_snprintf(buf, buflen, "%*d %*s %s",
1164e1b2664cSjtc 			ki->num_width, i,
1165e1b2664cSjtc 			ki->name_width, sigtraps[i].name,
1166e1b2664cSjtc 			sigtraps[i].mess);
1167e1b2664cSjtc 	else
1168e1b2664cSjtc 		shf_snprintf(buf, buflen, "%*d %*d %s",
1169e1b2664cSjtc 			ki->num_width, i,
1170e1b2664cSjtc 			ki->name_width, sigtraps[i].signal,
1171e1b2664cSjtc 			sigtraps[i].mess);
1172e1b2664cSjtc 	return buf;
1173e1b2664cSjtc }
1174e1b2664cSjtc 
1175e1b2664cSjtc 
1176e1b2664cSjtc int
c_kill(wp)1177e1b2664cSjtc c_kill(wp)
1178e1b2664cSjtc 	char **wp;
1179e1b2664cSjtc {
1180e1b2664cSjtc 	Trap *t = (Trap *) 0;
1181e1b2664cSjtc 	char *p;
1182e1b2664cSjtc 	int lflag = 0;
1183e1b2664cSjtc 	int i, n, rv, sig;
1184e1b2664cSjtc 
1185e1b2664cSjtc 	/* assume old style options if -digits or -UPPERCASE */
11867f5b5f6cSdsl 	if ((p = wp[1]) && *p == '-'
11877f5b5f6cSdsl 	    && (digit(p[1]) || isupper((unsigned char)p[1]))) {
118813351aa1Skamil 		if (!(t = gettrap(p + 1, true))) {
1189e1b2664cSjtc 			bi_errorf("bad signal `%s'", p + 1);
1190e1b2664cSjtc 			return 1;
1191e1b2664cSjtc 		}
1192e1b2664cSjtc 		i = (wp[2] && strcmp(wp[2], "--") == 0) ? 3 : 2;
1193e1b2664cSjtc 	} else {
1194e1b2664cSjtc 		int optc;
1195e1b2664cSjtc 
1196e1b2664cSjtc 		while ((optc = ksh_getopt(wp, &builtin_opt, "ls:")) != EOF)
1197e1b2664cSjtc 			switch (optc) {
1198e1b2664cSjtc 			  case 'l':
1199e1b2664cSjtc 				lflag = 1;
1200e1b2664cSjtc 				break;
1201e1b2664cSjtc 			  case 's':
120213351aa1Skamil 				if (!(t = gettrap(builtin_opt.optarg, true))) {
1203e1b2664cSjtc 					bi_errorf("bad signal `%s'",
1204e1b2664cSjtc 						builtin_opt.optarg);
1205e1b2664cSjtc 					return 1;
1206e1b2664cSjtc 				}
1207ffdf32a7Swiz 				break;
1208e1b2664cSjtc 			  case '?':
1209e1b2664cSjtc 				return 1;
1210e1b2664cSjtc 			}
1211e1b2664cSjtc 		i = builtin_opt.optind;
1212e1b2664cSjtc 	}
1213e1b2664cSjtc 	if ((lflag && t) || (!wp[i] && !lflag)) {
1214e1b2664cSjtc 		shf_fprintf(shl_out,
1215b635f565Sjmmv "usage: kill [ -s signame | -signum | -signame ] {pid|job}...\n\
1216e1b2664cSjtc        kill -l [exit_status]\n"
1217e1b2664cSjtc 			);
12185d331600Sjoerg 		bi_errorf("%s", null);
1219e1b2664cSjtc 		return 1;
1220e1b2664cSjtc 	}
1221e1b2664cSjtc 
1222e1b2664cSjtc 	if (lflag) {
1223e1b2664cSjtc 		if (wp[i]) {
1224e1b2664cSjtc 			for (; wp[i]; i++) {
1225e1b2664cSjtc 				if (!bi_getn(wp[i], &n))
1226e1b2664cSjtc 					return 1;
1227e1b2664cSjtc 				if (n > 128 && n < 128 + SIGNALS)
1228e1b2664cSjtc 					n -= 128;
1229e1b2664cSjtc 				if (n > 0 && n < SIGNALS && sigtraps[n].name)
1230e1b2664cSjtc 					shprintf("%s\n", sigtraps[n].name);
1231e1b2664cSjtc 				else
1232e1b2664cSjtc 					shprintf("%d\n", n);
1233e1b2664cSjtc 			}
1234e1b2664cSjtc 		} else if (Flag(FPOSIX)) {
1235e1b2664cSjtc 			p = null;
1236e1b2664cSjtc 			for (i = 1; i < SIGNALS; i++, p = space)
1237e1b2664cSjtc 				if (sigtraps[i].name)
1238e1b2664cSjtc 					shprintf("%s%s", p, sigtraps[i].name);
12395d331600Sjoerg 			shprintf("%s", newline);
1240e1b2664cSjtc 		} else {
1241a397ec1fSchristos 			int w, si;
1242e1b2664cSjtc 			int mess_width;
1243e1b2664cSjtc 			struct kill_info ki;
1244e1b2664cSjtc 
1245a397ec1fSchristos 			for (si = SIGNALS, ki.num_width = 1; si >= 10; si /= 10)
1246e1b2664cSjtc 				ki.num_width++;
1247e1b2664cSjtc 			ki.name_width = mess_width = 0;
1248a397ec1fSchristos 			for (si = 0; si < SIGNALS; si++) {
1249a397ec1fSchristos 				w = sigtraps[si].name ?
12507ca13b8bSlukem 				    (int)strlen(sigtraps[si].name) :
12517ca13b8bSlukem 				    ki.num_width;
1252e1b2664cSjtc 				if (w > ki.name_width)
1253e1b2664cSjtc 					ki.name_width = w;
1254a397ec1fSchristos 				w = strlen(sigtraps[si].mess);
1255e1b2664cSjtc 				if (w > mess_width)
1256e1b2664cSjtc 					mess_width = w;
1257e1b2664cSjtc 			}
1258e1b2664cSjtc 
1259e1b2664cSjtc 			print_columns(shl_stdout, SIGNALS - 1,
1260e1b2664cSjtc 				kill_fmt_entry, (void *) &ki,
1261f9b44bb1Sprovos 				ki.num_width + ki.name_width + mess_width + 3, 1);
1262e1b2664cSjtc 		}
1263e1b2664cSjtc 		return 0;
1264e1b2664cSjtc 	}
1265e1b2664cSjtc 	rv = 0;
1266e1b2664cSjtc 	sig = t ? t->signal : SIGTERM;
1267e1b2664cSjtc 	for (; (p = wp[i]); i++) {
1268e1b2664cSjtc 		if (*p == '%') {
1269e1b2664cSjtc 			if (j_kill(p, sig))
1270e1b2664cSjtc 				rv = 1;
1271e1b2664cSjtc 		} else if (!getn(p, &n)) {
1272f662a744Smycroft 			bi_errorf("%s: arguments must be jobs or process IDs",
1273e1b2664cSjtc 				p);
1274e1b2664cSjtc 			rv = 1;
1275e1b2664cSjtc 		} else {
1276e1b2664cSjtc 			/* use killpg if < -1 since -1 does special things for
1277e1b2664cSjtc 			 * some non-killpg-endowed kills
1278e1b2664cSjtc 			 */
1279e1b2664cSjtc 			if ((n < -1 ? killpg(-n, sig) : kill(n, sig)) < 0) {
1280e1b2664cSjtc 				bi_errorf("%s: %s", p, strerror(errno));
1281e1b2664cSjtc 				rv = 1;
1282e1b2664cSjtc 			}
1283e1b2664cSjtc 		}
1284e1b2664cSjtc 	}
1285e1b2664cSjtc 	return rv;
1286e1b2664cSjtc }
1287e1b2664cSjtc 
1288e1b2664cSjtc void
getopts_reset(val)1289e1b2664cSjtc getopts_reset(val)
1290e1b2664cSjtc 	int val;
1291e1b2664cSjtc {
129248ee8d12Shubertf 	if (val >= 1) {
1293e1b2664cSjtc 		ksh_getopt_reset(&user_opt,
1294e1b2664cSjtc 			GF_NONAME | (Flag(FPOSIX) ? 0 : GF_PLUSOPT));
129548ee8d12Shubertf 		user_opt.optind = user_opt.uoptind = val;
1296e1b2664cSjtc 	}
1297e1b2664cSjtc }
1298e1b2664cSjtc 
1299e1b2664cSjtc int
c_getopts(wp)1300e1b2664cSjtc c_getopts(wp)
1301e1b2664cSjtc 	char **wp;
1302e1b2664cSjtc {
1303e1b2664cSjtc 	int	argc;
1304e1b2664cSjtc 	const char *options;
1305e1b2664cSjtc 	const char *var;
1306e1b2664cSjtc 	int	optc;
130748ee8d12Shubertf 	int	ret;
1308e1b2664cSjtc 	char	buf[3];
130948ee8d12Shubertf 	struct tbl *vq, *voptarg;
1310e1b2664cSjtc 
1311e1b2664cSjtc 	if (ksh_getopt(wp, &builtin_opt, null) == '?')
1312e1b2664cSjtc 		return 1;
1313e1b2664cSjtc 	wp += builtin_opt.optind;
1314e1b2664cSjtc 
1315e1b2664cSjtc 	options = *wp++;
1316e1b2664cSjtc 	if (!options) {
1317e1b2664cSjtc 		bi_errorf("missing options argument");
1318e1b2664cSjtc 		return 1;
1319e1b2664cSjtc 	}
1320e1b2664cSjtc 
1321e1b2664cSjtc 	var = *wp++;
1322e1b2664cSjtc 	if (!var) {
1323e1b2664cSjtc 		bi_errorf("missing name argument");
1324e1b2664cSjtc 		return 1;
1325e1b2664cSjtc 	}
132613351aa1Skamil 	if (!*var || *skip_varname(var, true)) {
1327e1b2664cSjtc 		bi_errorf("%s: is not an identifier", var);
1328e1b2664cSjtc 		return 1;
1329e1b2664cSjtc 	}
1330e1b2664cSjtc 
1331e1b2664cSjtc 	if (e->loc->next == (struct block *) 0) {
1332e1b2664cSjtc 		internal_errorf(0, "c_getopts: no argv");
1333e1b2664cSjtc 		return 1;
1334e1b2664cSjtc 	}
1335e1b2664cSjtc 	/* Which arguments are we parsing... */
1336e1b2664cSjtc 	if (*wp == (char *) 0)
1337e1b2664cSjtc 		wp = e->loc->next->argv;
1338e1b2664cSjtc 	else
1339e1b2664cSjtc 		*--wp = e->loc->next->argv[0];
1340e1b2664cSjtc 
1341e1b2664cSjtc 	/* Check that our saved state won't cause a core dump... */
1342e1b2664cSjtc 	for (argc = 0; wp[argc]; argc++)
1343e1b2664cSjtc 		;
1344e1b2664cSjtc 	if (user_opt.optind > argc
1345e1b2664cSjtc 	    || (user_opt.p != 0
1346e1b2664cSjtc 		&& user_opt.p > strlen(wp[user_opt.optind - 1])))
1347e1b2664cSjtc 	{
1348e1b2664cSjtc 	      bi_errorf("arguments changed since last call");
1349e1b2664cSjtc 	      return 1;
1350e1b2664cSjtc 	}
1351e1b2664cSjtc 
1352e1b2664cSjtc 	user_opt.optarg = (char *) 0;
1353e1b2664cSjtc 	optc = ksh_getopt(wp, &user_opt, options);
1354e1b2664cSjtc 
1355e1b2664cSjtc 	if (optc >= 0 && optc != '?' && (user_opt.info & GI_PLUS)) {
1356e1b2664cSjtc 		buf[0] = '+';
1357e1b2664cSjtc 		buf[1] = optc;
1358e1b2664cSjtc 		buf[2] = '\0';
1359e1b2664cSjtc 	} else {
1360e1b2664cSjtc 		/* POSIX says var is set to ? at end-of-options, at&t ksh
1361e1b2664cSjtc 		 * sets it to null - we go with POSIX...
1362e1b2664cSjtc 		 */
1363e1b2664cSjtc 		buf[0] = optc < 0 ? '?' : optc;
1364e1b2664cSjtc 		buf[1] = '\0';
1365e1b2664cSjtc 	}
1366e1b2664cSjtc 
1367e1b2664cSjtc 	/* at&t ksh does not change OPTIND if it was an unknown option.
1368e1b2664cSjtc 	 * Scripts counting on this are prone to break... (ie, don't count
1369e1b2664cSjtc 	 * on this staying).
1370e1b2664cSjtc 	 */
1371e1b2664cSjtc 	if (optc != '?') {
137248ee8d12Shubertf 		user_opt.uoptind = user_opt.optind;
1373e1b2664cSjtc 	}
1374e1b2664cSjtc 
137548ee8d12Shubertf 	voptarg = global("OPTARG");
137648ee8d12Shubertf 	voptarg->flag &= ~RDONLY;	/* at&t ksh clears ro and int */
137748ee8d12Shubertf 	/* Paranoia: ensure no bizarre results. */
137848ee8d12Shubertf 	if (voptarg->flag & INTEGER)
137948ee8d12Shubertf 	    typeset("OPTARG", 0, INTEGER, 0, 0);
1380e1b2664cSjtc 	if (user_opt.optarg == (char *) 0)
138148ee8d12Shubertf 		unset(voptarg, 0);
1382e1b2664cSjtc 	else
138348ee8d12Shubertf 		/* This can't fail (have cleared readonly/integer) */
138448ee8d12Shubertf 		setstr(voptarg, user_opt.optarg, KSH_RETURN_ERROR);
138548ee8d12Shubertf 
138648ee8d12Shubertf 	ret = 0;
1387e1b2664cSjtc 
1388e1b2664cSjtc 	vq = global(var);
138948ee8d12Shubertf 	/* Error message already printed (integer, readonly) */
139048ee8d12Shubertf 	if (!setstr(vq, buf, KSH_RETURN_ERROR))
139148ee8d12Shubertf 	    ret = 1;
1392e1b2664cSjtc 	if (Flag(FEXPORT))
1393e1b2664cSjtc 		typeset(var, EXPORT, 0, 0, 0);
1394e1b2664cSjtc 
139548ee8d12Shubertf 	return optc < 0 ? 1 : ret;
1396e1b2664cSjtc }
1397e1b2664cSjtc 
1398e1b2664cSjtc #ifdef EMACS
1399e1b2664cSjtc int
c_bind(wp)1400e1b2664cSjtc c_bind(wp)
1401e1b2664cSjtc 	char **wp;
1402e1b2664cSjtc {
1403e1b2664cSjtc 	int rv = 0, macro = 0, list = 0;
1404ce8041bdSkamil 	char *cp;
1405e1b2664cSjtc 	int optc;
1406e1b2664cSjtc 
1407e1b2664cSjtc 	while ((optc = ksh_getopt(wp, &builtin_opt, "lm")) != EOF)
1408e1b2664cSjtc 		switch (optc) {
1409e1b2664cSjtc 		  case 'l':
1410e1b2664cSjtc 			list = 1;
1411e1b2664cSjtc 			break;
1412e1b2664cSjtc 		  case 'm':
1413e1b2664cSjtc 			macro = 1;
1414e1b2664cSjtc 			break;
1415e1b2664cSjtc 		  case '?':
1416e1b2664cSjtc 			return 1;
1417e1b2664cSjtc 		}
1418e1b2664cSjtc 	wp += builtin_opt.optind;
1419e1b2664cSjtc 
1420e1b2664cSjtc 	if (*wp == NULL)	/* list all */
14214a1b3429Splunky 		rv = x_bind(NULL, NULL, 0, list);
1422e1b2664cSjtc 
1423e1b2664cSjtc 	for (; *wp != NULL; wp++) {
1424e1b2664cSjtc 		cp = strchr(*wp, '=');
1425e1b2664cSjtc 		if (cp != NULL)
1426e1b2664cSjtc 			*cp++ = '\0';
1427e1b2664cSjtc 		if (x_bind(*wp, cp, macro, 0))
1428e1b2664cSjtc 			rv = 1;
1429e1b2664cSjtc 	}
1430e1b2664cSjtc 
1431e1b2664cSjtc 	return rv;
1432e1b2664cSjtc }
1433e1b2664cSjtc #endif
1434e1b2664cSjtc 
1435e1b2664cSjtc /* A leading = means assignments before command are kept;
1436e1b2664cSjtc  * a leading * means a POSIX special builtin;
1437e1b2664cSjtc  * a leading + means a POSIX regular builtin
1438e1b2664cSjtc  * (* and + should not be combined).
1439e1b2664cSjtc  */
1440e1b2664cSjtc const struct builtin kshbuiltins [] = {
1441e1b2664cSjtc 	{"+alias", c_alias},	/* no =: at&t manual wrong */
1442e1b2664cSjtc 	{"+cd", c_cd},
1443e1b2664cSjtc 	{"+command", c_command},
1444e1b2664cSjtc 	{"echo", c_print},
1445e1b2664cSjtc  	{"*=export", c_typeset},
1446e1b2664cSjtc #ifdef HISTORY
1447e1b2664cSjtc 	{"+fc", c_fc},
1448e1b2664cSjtc #endif /* HISTORY */
1449e1b2664cSjtc 	{"+getopts", c_getopts},
1450e1b2664cSjtc 	{"+jobs", c_jobs},
1451e1b2664cSjtc 	{"+kill", c_kill},
14525a412486Sjtc #ifdef KSH
1453e1b2664cSjtc 	{"let", c_let},
14545a412486Sjtc #endif /* KSH */
1455e1b2664cSjtc 	{"print", c_print},
1456e1b2664cSjtc 	{"pwd", c_pwd},
1457e1b2664cSjtc  	{"*=readonly", c_typeset},
1458e1b2664cSjtc 	{"=typeset", c_typeset},
1459e1b2664cSjtc 	{"+unalias", c_unalias},
1460e1b2664cSjtc 	{"whence", c_whence},
1461e1b2664cSjtc #ifdef JOBS
1462e1b2664cSjtc 	{"+bg", c_fgbg},
1463e1b2664cSjtc 	{"+fg", c_fgbg},
1464e1b2664cSjtc #endif
1465e1b2664cSjtc #ifdef EMACS
1466e1b2664cSjtc 	{"bind", c_bind},
1467e1b2664cSjtc #endif
1468e1b2664cSjtc 	{NULL, NULL}
1469e1b2664cSjtc };
1470