• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*	$OpenBSD: misc.c,v 1.41 2015/09/10 22:48:58 nicm Exp $	*/
2 /*	$OpenBSD: path.c,v 1.13 2015/09/05 09:47:08 jsg Exp $	*/
3 
4 /*-
5  * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
6  *		 2011, 2012, 2013, 2014, 2015, 2016, 2017
7  *	mirabilos <m@mirbsd.org>
8  *
9  * Provided that these terms and disclaimer and all copyright notices
10  * are retained or reproduced in an accompanying document, permission
11  * is granted to deal in this work without restriction, including un-
12  * limited rights to use, publicly perform, distribute, sell, modify,
13  * merge, give away, or sublicence.
14  *
15  * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
16  * the utmost extent permitted by applicable law, neither express nor
17  * implied; without malicious intent or gross negligence. In no event
18  * may a licensor, author or contributor be held liable for indirect,
19  * direct, other damage, loss, or other issues arising in any way out
20  * of dealing in the work, even if advised of the possibility of such
21  * damage or existence of a defect, except proven that it results out
22  * of said person's immediate fault when using the work as intended.
23  */
24 
25 #include "sh.h"
26 #if !HAVE_GETRUSAGE
27 #include <sys/times.h>
28 #endif
29 #if HAVE_GRP_H
30 #include <grp.h>
31 #endif
32 
33 __RCSID("$MirOS: src/bin/mksh/misc.c,v 1.255 2017/04/12 16:46:22 tg Exp $");
34 
35 #define KSH_CHVT_FLAG
36 #ifdef MKSH_SMALL
37 #undef KSH_CHVT_FLAG
38 #endif
39 #ifdef TIOCSCTTY
40 #define KSH_CHVT_CODE
41 #define KSH_CHVT_FLAG
42 #endif
43 
44 /* type bits for unsigned char */
45 unsigned char chtypes[UCHAR_MAX + 1];
46 
47 static const unsigned char *pat_scan(const unsigned char *,
48     const unsigned char *, bool) MKSH_A_PURE;
49 static int do_gmatch(const unsigned char *, const unsigned char *,
50     const unsigned char *, const unsigned char *) MKSH_A_PURE;
51 static const unsigned char *gmatch_cclass(const unsigned char *, unsigned char)
52     MKSH_A_PURE;
53 #ifdef KSH_CHVT_CODE
54 static void chvt(const Getopt *);
55 #endif
56 
57 /*XXX this should go away */
58 static int make_path(const char *, const char *, char **, XString *, int *);
59 
60 #ifdef SETUID_CAN_FAIL_WITH_EAGAIN
61 /* we don't need to check for other codes, EPERM won't happen */
62 #define DO_SETUID(func, argvec) do {					\
63 	if ((func argvec) && errno == EAGAIN)				\
64 		errorf("%s failed with EAGAIN, probably due to a"	\
65 		    " too low process limit; aborting", #func);		\
66 } while (/* CONSTCOND */ 0)
67 #else
68 #define DO_SETUID(func, argvec) func argvec
69 #endif
70 
71 /*
72  * Fast character classes
73  */
74 void
setctypes(const char * s,int t)75 setctypes(const char *s, int t)
76 {
77 	if (t & C_IFS) {
78 		unsigned int i = 0;
79 
80 		while (++i <= UCHAR_MAX)
81 			chtypes[i] &= ~C_IFS;
82 		/* include '\0' in C_IFS */
83 		chtypes[0] |= C_IFS;
84 	}
85 	while (*s != 0)
86 		chtypes[(unsigned char)*s++] |= t;
87 }
88 
89 void
initctypes(void)90 initctypes(void)
91 {
92 	setctypes(letters_uc, C_ALPHX);
93 	setctypes(letters_lc, C_ALPHX);
94 	chtypes['_'] |= C_ALPHX;
95 	setctypes("0123456789", C_DIGIT);
96 	setctypes(TC_LEX1, C_LEX1);
97 	setctypes("*@#!$-?", C_VAR1);
98 	setctypes(TC_IFSWS, C_IFSWS);
99 	setctypes("=-+?", C_SUBOP1);
100 	setctypes("\t\n \"#$&'()*;<=>?[\\]`|", C_QUOTE);
101 }
102 
103 /* called from XcheckN() to grow buffer */
104 char *
Xcheck_grow(XString * xsp,const char * xp,size_t more)105 Xcheck_grow(XString *xsp, const char *xp, size_t more)
106 {
107 	const char *old_beg = xsp->beg;
108 
109 	if (more < xsp->len)
110 		more = xsp->len;
111 	/* (xsp->len + X_EXTRA) never overflows */
112 	checkoktoadd(more, xsp->len + X_EXTRA);
113 	xsp->beg = aresize(xsp->beg, (xsp->len += more) + X_EXTRA, xsp->areap);
114 	xsp->end = xsp->beg + xsp->len;
115 	return (xsp->beg + (xp - old_beg));
116 }
117 
118 
119 #define SHFLAGS_DEFNS
120 #define FN(sname,cname,flags,ochar)		\
121 	static const struct {			\
122 		/* character flag (if any) */	\
123 		char c;				\
124 		/* OF_* */			\
125 		unsigned char optflags;		\
126 		/* long name of option */	\
127 		char name[sizeof(sname)];	\
128 	} shoptione_ ## cname = {		\
129 		ochar, flags, sname		\
130 	};
131 #include "sh_flags.gen"
132 
133 #define OFC(i) (options[i][-2])
134 #define OFF(i) (((const unsigned char *)options[i])[-1])
135 #define OFN(i) (options[i])
136 
137 const char * const options[] = {
138 #define SHFLAGS_ITEMS
139 #include "sh_flags.gen"
140 };
141 
142 /*
143  * translate -o option into F* constant (also used for test -o option)
144  */
145 size_t
option(const char * n)146 option(const char *n)
147 {
148 	size_t i = 0;
149 
150 	if ((n[0] == '-' || n[0] == '+') && n[1] && !n[2])
151 		while (i < NELEM(options)) {
152 			if (OFC(i) == n[1])
153 				return (i);
154 			++i;
155 		}
156 	else
157 		while (i < NELEM(options)) {
158 			if (!strcmp(OFN(i), n))
159 				return (i);
160 			++i;
161 		}
162 
163 	return ((size_t)-1);
164 }
165 
166 struct options_info {
167 	int opt_width;
168 	int opts[NELEM(options)];
169 };
170 
171 static void options_fmt_entry(char *, size_t, unsigned int, const void *);
172 static void printoptions(bool);
173 
174 /* format a single select menu item */
175 static void
options_fmt_entry(char * buf,size_t buflen,unsigned int i,const void * arg)176 options_fmt_entry(char *buf, size_t buflen, unsigned int i, const void *arg)
177 {
178 	const struct options_info *oi = (const struct options_info *)arg;
179 
180 	shf_snprintf(buf, buflen, "%-*s %s",
181 	    oi->opt_width, OFN(oi->opts[i]),
182 	    Flag(oi->opts[i]) ? "on" : "off");
183 }
184 
185 static void
printoptions(bool verbose)186 printoptions(bool verbose)
187 {
188 	size_t i = 0;
189 
190 	if (verbose) {
191 		size_t n = 0, len, octs = 0;
192 		struct options_info oi;
193 		struct columnise_opts co;
194 
195 		/* verbose version */
196 		shf_puts("Current option settings\n", shl_stdout);
197 
198 		oi.opt_width = 0;
199 		while (i < NELEM(options)) {
200 			if ((len = strlen(OFN(i)))) {
201 				oi.opts[n++] = i;
202 				if (len > octs)
203 					octs = len;
204 				len = utf_mbswidth(OFN(i));
205 				if ((int)len > oi.opt_width)
206 					oi.opt_width = (int)len;
207 			}
208 			++i;
209 		}
210 		co.shf = shl_stdout;
211 		co.linesep = '\n';
212 		co.prefcol = co.do_last = true;
213 		print_columns(&co, n, options_fmt_entry, &oi,
214 		    octs + 4, oi.opt_width + 4);
215 	} else {
216 		/* short version like AT&T ksh93 */
217 		shf_puts(Tset, shl_stdout);
218 		while (i < NELEM(options)) {
219 			if (Flag(i) && OFN(i)[0])
220 				shprintf(" -o %s", OFN(i));
221 			++i;
222 		}
223 		shf_putc('\n', shl_stdout);
224 	}
225 }
226 
227 char *
getoptions(void)228 getoptions(void)
229 {
230 	size_t i = 0;
231 	char c, m[(int)FNFLAGS + 1];
232 	char *cp = m;
233 
234 	while (i < NELEM(options)) {
235 		if ((c = OFC(i)) && Flag(i))
236 			*cp++ = c;
237 		++i;
238 	}
239 	strndupx(cp, m, cp - m, ATEMP);
240 	return (cp);
241 }
242 
243 /* change a Flag(*) value; takes care of special actions */
244 void
change_flag(enum sh_flag f,int what,bool newset)245 change_flag(enum sh_flag f, int what, bool newset)
246 {
247 	unsigned char oldval;
248 	unsigned char newval = (newset ? 1 : 0);
249 
250 	if (f == FXTRACE) {
251 		change_xtrace(newval, true);
252 		return;
253 	}
254 	oldval = Flag(f);
255 	Flag(f) = newval = (newset ? 1 : 0);
256 #ifndef MKSH_UNEMPLOYED
257 	if (f == FMONITOR) {
258 		if (what != OF_CMDLINE && newval != oldval)
259 			j_change();
260 	} else
261 #endif
262 #ifndef MKSH_NO_CMDLINE_EDITING
263 	  if ((
264 #if !MKSH_S_NOVI
265 	    f == FVI ||
266 #endif
267 	    f == FEMACS || f == FGMACS) && newval) {
268 #if !MKSH_S_NOVI
269 		Flag(FVI) =
270 #endif
271 		    Flag(FEMACS) = Flag(FGMACS) = 0;
272 		Flag(f) = newval;
273 	} else
274 #endif
275 	  if (f == FPRIVILEGED && oldval && !newval) {
276 		/* Turning off -p? */
277 
278 		/*XXX this can probably be optimised */
279 		kshegid = kshgid = getgid();
280 		ksheuid = kshuid = getuid();
281 #if HAVE_SETRESUGID
282 		DO_SETUID(setresgid, (kshegid, kshegid, kshegid));
283 #if HAVE_SETGROUPS
284 		/* setgroups doesn't EAGAIN on Linux */
285 		setgroups(1, &kshegid);
286 #endif
287 		DO_SETUID(setresuid, (ksheuid, ksheuid, ksheuid));
288 #else /* !HAVE_SETRESUGID */
289 		/* setgid, setegid, seteuid don't EAGAIN on Linux */
290 		setgid(kshegid);
291 #ifndef MKSH__NO_SETEUGID
292 		setegid(kshegid);
293 #endif
294 		DO_SETUID(setuid, (ksheuid));
295 #ifndef MKSH__NO_SETEUGID
296 		seteuid(ksheuid);
297 #endif
298 #endif /* !HAVE_SETRESUGID */
299 	} else if ((f == FPOSIX || f == FSH) && newval) {
300 		/* Turning on -o posix or -o sh? */
301 		Flag(FBRACEEXPAND) = 0;
302 	} else if (f == FTALKING) {
303 		/* Changing interactive flag? */
304 		if ((what == OF_CMDLINE || what == OF_SET) && procpid == kshpid)
305 			Flag(FTALKING_I) = newval;
306 	}
307 }
308 
309 void
change_xtrace(unsigned char newval,bool dosnapshot)310 change_xtrace(unsigned char newval, bool dosnapshot)
311 {
312 	static bool in_xtrace;
313 
314 	if (in_xtrace)
315 		return;
316 
317 	if (!dosnapshot && newval == Flag(FXTRACE))
318 		return;
319 
320 	if (Flag(FXTRACE) == 2) {
321 		shf_putc('\n', shl_xtrace);
322 		Flag(FXTRACE) = 1;
323 		shf_flush(shl_xtrace);
324 	}
325 
326 	if (!dosnapshot && Flag(FXTRACE) == 1)
327 		switch (newval) {
328 		case 1:
329 			return;
330 		case 2:
331 			goto changed_xtrace;
332 		}
333 
334 	shf_flush(shl_xtrace);
335 	if (shl_xtrace->fd != 2)
336 		close(shl_xtrace->fd);
337 	if (!newval || (shl_xtrace->fd = savefd(2)) == -1)
338 		shl_xtrace->fd = 2;
339 
340  changed_xtrace:
341 	if ((Flag(FXTRACE) = newval) == 2) {
342 		in_xtrace = true;
343 		Flag(FXTRACE) = 0;
344 		shf_puts(substitute(str_val(global("PS4")), 0), shl_xtrace);
345 		Flag(FXTRACE) = 2;
346 		in_xtrace = false;
347 	}
348 }
349 
350 /*
351  * Parse command line and set command arguments. Returns the index of
352  * non-option arguments, -1 if there is an error.
353  */
354 int
parse_args(const char ** argv,int what,bool * setargsp)355 parse_args(const char **argv,
356     /* OF_FIRSTTIME, OF_CMDLINE, or OF_SET */
357     int what,
358     bool *setargsp)
359 {
360 	static const char cmd_opts[] =
361 #define SHFLAGS_NOT_SET
362 #define SHFLAGS_OPTCS
363 #include "sh_flags.gen"
364 #undef SHFLAGS_NOT_SET
365 	    ;
366 	static const char set_opts[] =
367 #define SHFLAGS_NOT_CMD
368 #define SHFLAGS_OPTCS
369 #include "sh_flags.gen"
370 #undef SHFLAGS_NOT_CMD
371 	    ;
372 	bool set;
373 	const char *opts;
374 	const char *array = NULL;
375 	Getopt go;
376 	size_t i;
377 	int optc, arrayset = 0;
378 	bool sortargs = false;
379 	bool fcompatseen = false;
380 
381 	if (what == OF_CMDLINE) {
382 		const char *p = argv[0], *q;
383 		/*
384 		 * Set FLOGIN before parsing options so user can clear
385 		 * flag using +l.
386 		 */
387 		if (*p != '-')
388 			for (q = p; *q; )
389 				if (mksh_cdirsep(*q++))
390 					p = q;
391 		Flag(FLOGIN) = (*p == '-');
392 		opts = cmd_opts;
393 	} else if (what == OF_FIRSTTIME) {
394 		opts = cmd_opts;
395 	} else
396 		opts = set_opts;
397 	ksh_getopt_reset(&go, GF_ERROR|GF_PLUSOPT);
398 	while ((optc = ksh_getopt(argv, &go, opts)) != -1) {
399 		set = tobool(!(go.info & GI_PLUS));
400 		switch (optc) {
401 		case 'A':
402 			if (what == OF_FIRSTTIME)
403 				break;
404 			arrayset = set ? 1 : -1;
405 			array = go.optarg;
406 			break;
407 
408 		case 'o':
409 			if (what == OF_FIRSTTIME)
410 				break;
411 			if (go.optarg == NULL) {
412 				/*
413 				 * lone -o: print options
414 				 *
415 				 * Note that on the command line, -o requires
416 				 * an option (ie, can't get here if what is
417 				 * OF_CMDLINE).
418 				 */
419 				printoptions(set);
420 				break;
421 			}
422 			i = option(go.optarg);
423 			if ((i == FPOSIX || i == FSH) && set && !fcompatseen) {
424 				/*
425 				 * If running 'set -o posix' or
426 				 * 'set -o sh', turn off the other;
427 				 * if running 'set -o posix -o sh'
428 				 * allow both to be set though.
429 				 */
430 				Flag(FPOSIX) = 0;
431 				Flag(FSH) = 0;
432 				fcompatseen = true;
433 			}
434 			if ((i != (size_t)-1) && (set ? 1U : 0U) == Flag(i))
435 				/*
436 				 * Don't check the context if the flag
437 				 * isn't changing - makes "set -o interactive"
438 				 * work if you're already interactive. Needed
439 				 * if the output of "set +o" is to be used.
440 				 */
441 				;
442 			else if ((i != (size_t)-1) && (OFF(i) & what))
443 				change_flag((enum sh_flag)i, what, set);
444 			else {
445 				bi_errorf(Tf_sD_s, go.optarg,
446 				    Tunknown_option);
447 				return (-1);
448 			}
449 			break;
450 
451 #ifdef KSH_CHVT_FLAG
452 		case 'T':
453 			if (what != OF_FIRSTTIME)
454 				break;
455 #ifndef KSH_CHVT_CODE
456 			errorf("no TIOCSCTTY ioctl");
457 #else
458 			change_flag(FTALKING, OF_CMDLINE, true);
459 			chvt(&go);
460 			break;
461 #endif
462 #endif
463 
464 		case '?':
465 			return (-1);
466 
467 		default:
468 			if (what == OF_FIRSTTIME)
469 				break;
470 			/* -s: sort positional params (AT&T ksh stupidity) */
471 			if (what == OF_SET && optc == 's') {
472 				sortargs = true;
473 				break;
474 			}
475 			for (i = 0; i < NELEM(options); i++)
476 				if (optc == OFC(i) &&
477 				    (what & OFF(i))) {
478 					change_flag((enum sh_flag)i, what, set);
479 					break;
480 				}
481 			if (i == NELEM(options))
482 				internal_errorf("parse_args: '%c'", optc);
483 		}
484 	}
485 	if (!(go.info & GI_MINUSMINUS) && argv[go.optind] &&
486 	    (argv[go.optind][0] == '-' || argv[go.optind][0] == '+') &&
487 	    argv[go.optind][1] == '\0') {
488 		/* lone - clears -v and -x flags */
489 		if (argv[go.optind][0] == '-') {
490 			Flag(FVERBOSE) = 0;
491 			change_xtrace(0, false);
492 		}
493 		/* set skips lone - or + option */
494 		go.optind++;
495 	}
496 	if (setargsp)
497 		/* -- means set $#/$* even if there are no arguments */
498 		*setargsp = !arrayset && ((go.info & GI_MINUSMINUS) ||
499 		    argv[go.optind]);
500 
501 	if (arrayset) {
502 		const char *ccp = NULL;
503 
504 		if (array && *array)
505 			ccp = skip_varname(array, false);
506 		if (!ccp || !(!ccp[0] || (ccp[0] == '+' && !ccp[1]))) {
507 			bi_errorf(Tf_sD_s, array, Tnot_ident);
508 			return (-1);
509 		}
510 	}
511 	if (sortargs) {
512 		for (i = go.optind; argv[i]; i++)
513 			;
514 		qsort(&argv[go.optind], i - go.optind, sizeof(void *),
515 		    xstrcmp);
516 	}
517 	if (arrayset)
518 		go.optind += set_array(array, tobool(arrayset > 0),
519 		    argv + go.optind);
520 
521 	return (go.optind);
522 }
523 
524 /* parse a decimal number: returns 0 if string isn't a number, 1 otherwise */
525 int
getn(const char * s,int * ai)526 getn(const char *s, int *ai)
527 {
528 	char c;
529 	mksh_ari_u num;
530 	bool neg = false;
531 
532 	num.u = 0;
533 
534 	do {
535 		c = *s++;
536 	} while (ksh_isspace(c));
537 
538 	switch (c) {
539 	case '-':
540 		neg = true;
541 		/* FALLTHROUGH */
542 	case '+':
543 		c = *s++;
544 		break;
545 	}
546 
547 	do {
548 		if (!ksh_isdigit(c))
549 			/* not numeric */
550 			return (0);
551 		if (num.u > 214748364U)
552 			/* overflow on multiplication */
553 			return (0);
554 		num.u = num.u * 10U + (unsigned int)ksh_numdig(c);
555 		/* now: num.u <= 2147483649U */
556 	} while ((c = *s++));
557 
558 	if (num.u > (neg ? 2147483648U : 2147483647U))
559 		/* overflow for signed 32-bit int */
560 		return (0);
561 
562 	if (neg)
563 		num.u = -num.u;
564 	*ai = num.i;
565 	return (1);
566 }
567 
568 /**
569  * pattern simplifications:
570  * - @(x) -> x (not @(x|y) though)
571  * - ** -> *
572  */
573 static void *
simplify_gmatch_pattern(const unsigned char * sp)574 simplify_gmatch_pattern(const unsigned char *sp)
575 {
576 	uint8_t c;
577 	unsigned char *cp, *dp;
578 	const unsigned char *ps, *se;
579 
580 	cp = alloc(strlen((const void *)sp) + 1, ATEMP);
581 	goto simplify_gmatch_pat1a;
582 
583 	/* foo@(b@(a)r)b@(a|a)z -> foobarb@(a|a)z */
584  simplify_gmatch_pat1:
585 	sp = cp;
586  simplify_gmatch_pat1a:
587 	dp = cp;
588 	se = sp + strlen((const void *)sp);
589 	while ((c = *sp++)) {
590 		if (!ISMAGIC(c)) {
591 			*dp++ = c;
592 			continue;
593 		}
594 		switch ((c = *sp++)) {
595 		case 0x80|'@':
596 		/* simile for @ */
597 		case 0x80|' ':
598 			/* check whether it has only one clause */
599 			ps = pat_scan(sp, se, true);
600 			if (!ps || ps[-1] != /*(*/ ')')
601 				/* nope */
602 				break;
603 			/* copy inner clause until matching close */
604 			ps -= 2;
605 			while ((const unsigned char *)sp < ps)
606 				*dp++ = *sp++;
607 			/* skip MAGIC and closing parenthesis */
608 			sp += 2;
609 			/* copy the rest of the pattern */
610 			memmove(dp, sp, strlen((const void *)sp) + 1);
611 			/* redo from start */
612 			goto simplify_gmatch_pat1;
613 		}
614 		*dp++ = MAGIC;
615 		*dp++ = c;
616 	}
617 	*dp = '\0';
618 
619 	/* collapse adjacent asterisk wildcards */
620 	sp = dp = cp;
621 	while ((c = *sp++)) {
622 		if (!ISMAGIC(c)) {
623 			*dp++ = c;
624 			continue;
625 		}
626 		switch ((c = *sp++)) {
627 		case '*':
628 			while (ISMAGIC(sp[0]) && sp[1] == c)
629 				sp += 2;
630 			break;
631 		}
632 		*dp++ = MAGIC;
633 		*dp++ = c;
634 	}
635 	*dp = '\0';
636 
637 	/* return the result, allocated from ATEMP */
638 	return (cp);
639 }
640 
641 /* -------- gmatch.c -------- */
642 
643 /*
644  * int gmatch(string, pattern)
645  * char *string, *pattern;
646  *
647  * Match a pattern as in sh(1).
648  * pattern character are prefixed with MAGIC by expand.
649  */
650 int
gmatchx(const char * s,const char * p,bool isfile)651 gmatchx(const char *s, const char *p, bool isfile)
652 {
653 	const char *se, *pe;
654 	char *pnew;
655 	int rv;
656 
657 	if (s == NULL || p == NULL)
658 		return (0);
659 
660 	se = s + strlen(s);
661 	pe = p + strlen(p);
662 	/*
663 	 * isfile is false iff no syntax check has been done on
664 	 * the pattern. If check fails, just to a strcmp().
665 	 */
666 	if (!isfile && !has_globbing(p, pe)) {
667 		size_t len = pe - p + 1;
668 		char tbuf[64];
669 		char *t = len <= sizeof(tbuf) ? tbuf : alloc(len, ATEMP);
670 		debunk(t, p, len);
671 		return (!strcmp(t, s));
672 	}
673 
674 	/*
675 	 * since the do_gmatch() engine sucks so much, we must do some
676 	 * pattern simplifications
677 	 */
678 	pnew = simplify_gmatch_pattern((const unsigned char *)p);
679 	pe = pnew + strlen(pnew);
680 
681 	rv = do_gmatch((const unsigned char *)s, (const unsigned char *)se,
682 	    (const unsigned char *)pnew, (const unsigned char *)pe);
683 	afree(pnew, ATEMP);
684 	return (rv);
685 }
686 
687 /**
688  * Returns if p is a syntacticly correct globbing pattern, false
689  * if it contains no pattern characters or if there is a syntax error.
690  * Syntax errors are:
691  *	- [ with no closing ]
692  *	- imbalanced $(...) expression
693  *	- [...] and *(...) not nested (eg, [a$(b|]c), *(a[b|c]d))
694  */
695 /*XXX
696  * - if no magic,
697  *	if dest given, copy to dst
698  *	return ?
699  * - if magic && (no globbing || syntax error)
700  *	debunk to dst
701  *	return ?
702  * - return ?
703  */
704 int
has_globbing(const char * xp,const char * xpe)705 has_globbing(const char *xp, const char *xpe)
706 {
707 	const unsigned char *p = (const unsigned char *) xp;
708 	const unsigned char *pe = (const unsigned char *) xpe;
709 	int c;
710 	int nest = 0, bnest = 0;
711 	bool saw_glob = false;
712 	/* inside [...] */
713 	bool in_bracket = false;
714 
715 	for (; p < pe; p++) {
716 		if (!ISMAGIC(*p))
717 			continue;
718 		if ((c = *++p) == '*' || c == '?')
719 			saw_glob = true;
720 		else if (c == '[') {
721 			if (!in_bracket) {
722 				saw_glob = true;
723 				in_bracket = true;
724 				if (ISMAGIC(p[1]) && p[2] == '!')
725 					p += 2;
726 				if (ISMAGIC(p[1]) && p[2] == ']')
727 					p += 2;
728 			}
729 			/*XXX Do we need to check ranges here? POSIX Q */
730 		} else if (c == ']') {
731 			if (in_bracket) {
732 				if (bnest)
733 					/* [a*(b]) */
734 					return (0);
735 				in_bracket = false;
736 			}
737 		} else if ((c & 0x80) && vstrchr("*+?@! ", c & 0x7f)) {
738 			saw_glob = true;
739 			if (in_bracket)
740 				bnest++;
741 			else
742 				nest++;
743 		} else if (c == '|') {
744 			if (in_bracket && !bnest)
745 				/* *(a[foo|bar]) */
746 				return (0);
747 		} else if (c == /*(*/ ')') {
748 			if (in_bracket) {
749 				if (!bnest--)
750 					/* *(a[b)c] */
751 					return (0);
752 			} else if (nest)
753 				nest--;
754 		}
755 		/*
756 		 * else must be a MAGIC-MAGIC, or MAGIC-!,
757 		 * MAGIC--, MAGIC-], MAGIC-{, MAGIC-, MAGIC-}
758 		 */
759 	}
760 	return (saw_glob && !in_bracket && !nest);
761 }
762 
763 /* Function must return either 0 or 1 (assumed by code for 0x80|'!') */
764 static int
do_gmatch(const unsigned char * s,const unsigned char * se,const unsigned char * p,const unsigned char * pe)765 do_gmatch(const unsigned char *s, const unsigned char *se,
766     const unsigned char *p, const unsigned char *pe)
767 {
768 	unsigned char sc, pc;
769 	const unsigned char *prest, *psub, *pnext;
770 	const unsigned char *srest;
771 
772 	if (s == NULL || p == NULL)
773 		return (0);
774 	while (p < pe) {
775 		pc = *p++;
776 		sc = s < se ? *s : '\0';
777 		s++;
778 		if (!ISMAGIC(pc)) {
779 			if (sc != pc)
780 				return (0);
781 			continue;
782 		}
783 		switch (*p++) {
784 		case '[':
785 			if (sc == 0 || (p = gmatch_cclass(p, sc)) == NULL)
786 				return (0);
787 			break;
788 
789 		case '?':
790 			if (sc == 0)
791 				return (0);
792 			if (UTFMODE) {
793 				--s;
794 				s += utf_ptradj((const void *)s);
795 			}
796 			break;
797 
798 		case '*':
799 			if (p == pe)
800 				return (1);
801 			s--;
802 			do {
803 				if (do_gmatch(s, se, p, pe))
804 					return (1);
805 			} while (s++ < se);
806 			return (0);
807 
808 		/**
809 		 * [*+?@!](pattern|pattern|..)
810 		 * This is also needed for ${..%..}, etc.
811 		 */
812 
813 		/* matches one or more times */
814 		case 0x80|'+':
815 		/* matches zero or more times */
816 		case 0x80|'*':
817 			if (!(prest = pat_scan(p, pe, false)))
818 				return (0);
819 			s--;
820 			/* take care of zero matches */
821 			if (p[-1] == (0x80 | '*') &&
822 			    do_gmatch(s, se, prest, pe))
823 				return (1);
824 			for (psub = p; ; psub = pnext) {
825 				pnext = pat_scan(psub, pe, true);
826 				for (srest = s; srest <= se; srest++) {
827 					if (do_gmatch(s, srest, psub, pnext - 2) &&
828 					    (do_gmatch(srest, se, prest, pe) ||
829 					    (s != srest && do_gmatch(srest,
830 					    se, p - 2, pe))))
831 						return (1);
832 				}
833 				if (pnext == prest)
834 					break;
835 			}
836 			return (0);
837 
838 		/* matches zero or once */
839 		case 0x80|'?':
840 		/* matches one of the patterns */
841 		case 0x80|'@':
842 		/* simile for @ */
843 		case 0x80|' ':
844 			if (!(prest = pat_scan(p, pe, false)))
845 				return (0);
846 			s--;
847 			/* Take care of zero matches */
848 			if (p[-1] == (0x80 | '?') &&
849 			    do_gmatch(s, se, prest, pe))
850 				return (1);
851 			for (psub = p; ; psub = pnext) {
852 				pnext = pat_scan(psub, pe, true);
853 				srest = prest == pe ? se : s;
854 				for (; srest <= se; srest++) {
855 					if (do_gmatch(s, srest, psub, pnext - 2) &&
856 					    do_gmatch(srest, se, prest, pe))
857 						return (1);
858 				}
859 				if (pnext == prest)
860 					break;
861 			}
862 			return (0);
863 
864 		/* matches none of the patterns */
865 		case 0x80|'!':
866 			if (!(prest = pat_scan(p, pe, false)))
867 				return (0);
868 			s--;
869 			for (srest = s; srest <= se; srest++) {
870 				int matched = 0;
871 
872 				for (psub = p; ; psub = pnext) {
873 					pnext = pat_scan(psub, pe, true);
874 					if (do_gmatch(s, srest, psub,
875 					    pnext - 2)) {
876 						matched = 1;
877 						break;
878 					}
879 					if (pnext == prest)
880 						break;
881 				}
882 				if (!matched &&
883 				    do_gmatch(srest, se, prest, pe))
884 					return (1);
885 			}
886 			return (0);
887 
888 		default:
889 			if (sc != p[-1])
890 				return (0);
891 			break;
892 		}
893 	}
894 	return (s == se);
895 }
896 
897 static const unsigned char *
gmatch_cclass(const unsigned char * p,unsigned char sub)898 gmatch_cclass(const unsigned char *p, unsigned char sub)
899 {
900 	unsigned char c, d;
901 	bool notp, found = false;
902 	const unsigned char *orig_p = p;
903 
904 	if ((notp = tobool(ISMAGIC(*p) && *++p == '!')))
905 		p++;
906 	do {
907 		c = *p++;
908 		if (ISMAGIC(c)) {
909 			c = *p++;
910 			if ((c & 0x80) && !ISMAGIC(c)) {
911 				/* extended pattern matching: *+?@! */
912 				c &= 0x7F;
913 				/* XXX the ( char isn't handled as part of [] */
914 				if (c == ' ')
915 					/* simile for @: plain (..) */
916 					c = '(' /*)*/;
917 			}
918 		}
919 		if (c == '\0')
920 			/* No closing ] - act as if the opening [ was quoted */
921 			return (sub == '[' ? orig_p : NULL);
922 		if (ISMAGIC(p[0]) && p[1] == '-' &&
923 		    (!ISMAGIC(p[2]) || p[3] != ']')) {
924 			/* MAGIC- */
925 			p += 2;
926 			d = *p++;
927 			if (ISMAGIC(d)) {
928 				d = *p++;
929 				if ((d & 0x80) && !ISMAGIC(d))
930 					d &= 0x7f;
931 			}
932 			/* POSIX says this is an invalid expression */
933 			if (c > d)
934 				return (NULL);
935 		} else
936 			d = c;
937 		if (c == sub || (c <= sub && sub <= d))
938 			found = true;
939 	} while (!(ISMAGIC(p[0]) && p[1] == ']'));
940 
941 	return ((found != notp) ? p+2 : NULL);
942 }
943 
944 /* Look for next ) or | (if match_sep) in *(foo|bar) pattern */
945 static const unsigned char *
pat_scan(const unsigned char * p,const unsigned char * pe,bool match_sep)946 pat_scan(const unsigned char *p, const unsigned char *pe, bool match_sep)
947 {
948 	int nest = 0;
949 
950 	for (; p < pe; p++) {
951 		if (!ISMAGIC(*p))
952 			continue;
953 		if ((*++p == /*(*/ ')' && nest-- == 0) ||
954 		    (*p == '|' && match_sep && nest == 0))
955 			return (p + 1);
956 		if ((*p & 0x80) && vstrchr("*+?@! ", *p & 0x7f))
957 			nest++;
958 	}
959 	return (NULL);
960 }
961 
962 int
xstrcmp(const void * p1,const void * p2)963 xstrcmp(const void *p1, const void *p2)
964 {
965 	return (strcmp(*(const char * const *)p1, *(const char * const *)p2));
966 }
967 
968 /* Initialise a Getopt structure */
969 void
ksh_getopt_reset(Getopt * go,int flags)970 ksh_getopt_reset(Getopt *go, int flags)
971 {
972 	go->optind = 1;
973 	go->optarg = NULL;
974 	go->p = 0;
975 	go->flags = flags;
976 	go->info = 0;
977 	go->buf[1] = '\0';
978 }
979 
980 
981 /**
982  * getopt() used for shell built-in commands, the getopts command, and
983  * command line options.
984  * A leading ':' in options means don't print errors, instead return '?'
985  * or ':' and set go->optarg to the offending option character.
986  * If GF_ERROR is set (and option doesn't start with :), errors result in
987  * a call to bi_errorf().
988  *
989  * Non-standard features:
990  *	- ';' is like ':' in options, except the argument is optional
991  *	  (if it isn't present, optarg is set to 0).
992  *	  Used for 'set -o'.
993  *	- ',' is like ':' in options, except the argument always immediately
994  *	  follows the option character (optarg is set to the null string if
995  *	  the option is missing).
996  *	  Used for 'read -u2', 'print -u2' and fc -40.
997  *	- '#' is like ':' in options, expect that the argument is optional
998  *	  and must start with a digit. If the argument doesn't start with a
999  *	  digit, it is assumed to be missing and normal option processing
1000  *	  continues (optarg is set to 0 if the option is missing).
1001  *	  Used for 'typeset -LZ4'.
1002  *	- accepts +c as well as -c IF the GF_PLUSOPT flag is present. If an
1003  *	  option starting with + is accepted, the GI_PLUS flag will be set
1004  *	  in go->info.
1005  */
1006 int
ksh_getopt(const char ** argv,Getopt * go,const char * optionsp)1007 ksh_getopt(const char **argv, Getopt *go, const char *optionsp)
1008 {
1009 	char c;
1010 	const char *o;
1011 
1012 	if (go->p == 0 || (c = argv[go->optind - 1][go->p]) == '\0') {
1013 		const char *arg = argv[go->optind], flag = arg ? *arg : '\0';
1014 
1015 		go->p = 1;
1016 		if (flag == '-' && ksh_isdash(arg + 1)) {
1017 			go->optind++;
1018 			go->p = 0;
1019 			go->info |= GI_MINUSMINUS;
1020 			return (-1);
1021 		}
1022 		if (arg == NULL ||
1023 		    ((flag != '-' ) &&
1024 		    /* neither a - nor a + (if + allowed) */
1025 		    (!(go->flags & GF_PLUSOPT) || flag != '+')) ||
1026 		    (c = arg[1]) == '\0') {
1027 			go->p = 0;
1028 			return (-1);
1029 		}
1030 		go->optind++;
1031 		go->info &= ~(GI_MINUS|GI_PLUS);
1032 		go->info |= flag == '-' ? GI_MINUS : GI_PLUS;
1033 	}
1034 	go->p++;
1035 	if (c == '?' || c == ':' || c == ';' || c == ',' || c == '#' ||
1036 	    !(o = cstrchr(optionsp, c))) {
1037 		if (optionsp[0] == ':') {
1038 			go->buf[0] = c;
1039 			go->optarg = go->buf;
1040 		} else {
1041 			warningf(true, Tf_optfoo,
1042 			    (go->flags & GF_NONAME) ? "" : argv[0],
1043 			    (go->flags & GF_NONAME) ? "" : Tcolsp,
1044 			    c, Tunknown_option);
1045 			if (go->flags & GF_ERROR)
1046 				bi_errorfz();
1047 		}
1048 		return ('?');
1049 	}
1050 	/**
1051 	 * : means argument must be present, may be part of option argument
1052 	 *   or the next argument
1053 	 * ; same as : but argument may be missing
1054 	 * , means argument is part of option argument, and may be null.
1055 	 */
1056 	if (*++o == ':' || *o == ';') {
1057 		if (argv[go->optind - 1][go->p])
1058 			go->optarg = argv[go->optind - 1] + go->p;
1059 		else if (argv[go->optind])
1060 			go->optarg = argv[go->optind++];
1061 		else if (*o == ';')
1062 			go->optarg = NULL;
1063 		else {
1064 			if (optionsp[0] == ':') {
1065 				go->buf[0] = c;
1066 				go->optarg = go->buf;
1067 				return (':');
1068 			}
1069 			warningf(true, Tf_optfoo,
1070 			    (go->flags & GF_NONAME) ? "" : argv[0],
1071 			    (go->flags & GF_NONAME) ? "" : Tcolsp,
1072 			    c, Treq_arg);
1073 			if (go->flags & GF_ERROR)
1074 				bi_errorfz();
1075 			return ('?');
1076 		}
1077 		go->p = 0;
1078 	} else if (*o == ',') {
1079 		/* argument is attached to option character, even if null */
1080 		go->optarg = argv[go->optind - 1] + go->p;
1081 		go->p = 0;
1082 	} else if (*o == '#') {
1083 		/*
1084 		 * argument is optional and may be attached or unattached
1085 		 * but must start with a digit. optarg is set to 0 if the
1086 		 * argument is missing.
1087 		 */
1088 		if (argv[go->optind - 1][go->p]) {
1089 			if (ksh_isdigit(argv[go->optind - 1][go->p])) {
1090 				go->optarg = argv[go->optind - 1] + go->p;
1091 				go->p = 0;
1092 			} else
1093 				go->optarg = NULL;
1094 		} else {
1095 			if (argv[go->optind] && ksh_isdigit(argv[go->optind][0])) {
1096 				go->optarg = argv[go->optind++];
1097 				go->p = 0;
1098 			} else
1099 				go->optarg = NULL;
1100 		}
1101 	}
1102 	return (c);
1103 }
1104 
1105 /*
1106  * print variable/alias value using necessary quotes
1107  * (POSIX says they should be suitable for re-entry...)
1108  * No trailing newline is printed.
1109  */
1110 void
print_value_quoted(struct shf * shf,const char * s)1111 print_value_quoted(struct shf *shf, const char *s)
1112 {
1113 	unsigned char c;
1114 	const unsigned char *p = (const unsigned char *)s;
1115 	bool inquote = true;
1116 
1117 	/* first, check whether any quotes are needed */
1118 	while ((c = *p++) >= 32)
1119 		if (ctype(c, C_QUOTE))
1120 			inquote = false;
1121 
1122 	p = (const unsigned char *)s;
1123 	if (c == 0) {
1124 		if (inquote) {
1125 			/* nope, use the shortcut */
1126 			shf_puts(s, shf);
1127 			return;
1128 		}
1129 
1130 		/* otherwise, quote nicely via state machine */
1131 		while ((c = *p++) != 0) {
1132 			if (c == '\'') {
1133 				/*
1134 				 * multiple single quotes or any of them
1135 				 * at the beginning of a string look nicer
1136 				 * this way than when simply substituting
1137 				 */
1138 				if (inquote) {
1139 					shf_putc('\'', shf);
1140 					inquote = false;
1141 				}
1142 				shf_putc('\\', shf);
1143 			} else if (!inquote) {
1144 				shf_putc('\'', shf);
1145 				inquote = true;
1146 			}
1147 			shf_putc(c, shf);
1148 		}
1149 	} else {
1150 		unsigned int wc;
1151 		size_t n;
1152 
1153 		/* use $'...' quote format */
1154 		shf_putc('$', shf);
1155 		shf_putc('\'', shf);
1156 		while ((c = *p) != 0) {
1157 			if (c >= 0xC2) {
1158 				n = utf_mbtowc(&wc, (const char *)p);
1159 				if (n != (size_t)-1) {
1160 					p += n;
1161 					shf_fprintf(shf, "\\u%04X", wc);
1162 					continue;
1163 				}
1164 			}
1165 			++p;
1166 			switch (c) {
1167 			/* see unbksl() in this file for comments */
1168 			case 7:
1169 				c = 'a';
1170 				if (0)
1171 					/* FALLTHROUGH */
1172 			case '\b':
1173 				  c = 'b';
1174 				if (0)
1175 					/* FALLTHROUGH */
1176 			case '\f':
1177 				  c = 'f';
1178 				if (0)
1179 					/* FALLTHROUGH */
1180 			case '\n':
1181 				  c = 'n';
1182 				if (0)
1183 					/* FALLTHROUGH */
1184 			case '\r':
1185 				  c = 'r';
1186 				if (0)
1187 					/* FALLTHROUGH */
1188 			case '\t':
1189 				  c = 't';
1190 				if (0)
1191 					/* FALLTHROUGH */
1192 			case 11:
1193 				  c = 'v';
1194 				if (0)
1195 					/* FALLTHROUGH */
1196 			case '\033':
1197 				/* take E not e because \e is \ in *roff */
1198 				  c = 'E';
1199 				/* FALLTHROUGH */
1200 			case '\\':
1201 				shf_putc('\\', shf);
1202 
1203 				if (0)
1204 					/* FALLTHROUGH */
1205 			default:
1206 				  if (c < 32 || c > 0x7E) {
1207 					/* FALLTHROUGH */
1208 			case '\'':
1209 					shf_fprintf(shf, "\\%03o", c);
1210 					break;
1211 				}
1212 
1213 				shf_putc(c, shf);
1214 				break;
1215 			}
1216 		}
1217 		inquote = true;
1218 	}
1219 	if (inquote)
1220 		shf_putc('\'', shf);
1221 }
1222 
1223 /*
1224  * Print things in columns and rows - func() is called to format
1225  * the i-th element
1226  */
1227 void
print_columns(struct columnise_opts * opts,unsigned int n,void (* func)(char *,size_t,unsigned int,const void *),const void * arg,size_t max_oct,size_t max_colz)1228 print_columns(struct columnise_opts *opts, unsigned int n,
1229     void (*func)(char *, size_t, unsigned int, const void *),
1230     const void *arg, size_t max_oct, size_t max_colz)
1231 {
1232 	unsigned int i, r = 0, c, rows, cols, nspace, max_col;
1233 	char *str;
1234 
1235 	if (!n)
1236 		return;
1237 
1238 	if (max_colz > 2147483646) {
1239 #ifndef MKSH_SMALL
1240 		internal_warningf("print_columns called with %s=%zu >= INT_MAX",
1241 		    "max_col", max_colz);
1242 #endif
1243 		return;
1244 	}
1245 	max_col = (unsigned int)max_colz;
1246 
1247 	if (max_oct > 2147483646) {
1248 #ifndef MKSH_SMALL
1249 		internal_warningf("print_columns called with %s=%zu >= INT_MAX",
1250 		    "max_oct", max_oct);
1251 #endif
1252 		return;
1253 	}
1254 	++max_oct;
1255 	str = alloc(max_oct, ATEMP);
1256 
1257 	/*
1258 	 * We use (max_col + 2) to consider the separator space.
1259 	 * Note that no spaces are printed after the last column
1260 	 * to avoid problems with terminals that have auto-wrap,
1261 	 * but we need to also take this into account in x_cols.
1262 	 */
1263 	cols = (x_cols + 1) / (max_col + 2);
1264 
1265 	/* if we can only print one column anyway, skip the goo */
1266 	if (cols < 2) {
1267 		goto prcols_easy;
1268 		while (r < n) {
1269 			shf_putc(opts->linesep, opts->shf);
1270  prcols_easy:
1271 			(*func)(str, max_oct, r++, arg);
1272 			shf_puts(str, opts->shf);
1273 		}
1274 		goto out;
1275 	}
1276 
1277 	rows = (n + cols - 1) / cols;
1278 	if (opts->prefcol && cols > rows) {
1279 		cols = rows;
1280 		rows = (n + cols - 1) / cols;
1281 	}
1282 
1283 	nspace = (x_cols - max_col * cols) / cols;
1284 	if (nspace < 2)
1285 		nspace = 2;
1286 	max_col = -max_col;
1287 	goto prcols_hard;
1288 	while (r < rows) {
1289 		shf_putchar(opts->linesep, opts->shf);
1290  prcols_hard:
1291 		for (c = 0; c < cols; c++) {
1292 			if ((i = c * rows + r) >= n)
1293 				break;
1294 			(*func)(str, max_oct, i, arg);
1295 			if (i + rows >= n)
1296 				shf_puts(str, opts->shf);
1297 			else
1298 				shf_fprintf(opts->shf, "%*s%*s",
1299 				    (int)max_col, str, (int)nspace, null);
1300 		}
1301 		++r;
1302 	}
1303  out:
1304 	if (opts->do_last)
1305 		shf_putchar(opts->linesep, opts->shf);
1306 	afree(str, ATEMP);
1307 }
1308 
1309 /* strip all NUL bytes from buf; output is NUL-terminated if stripped */
1310 void
strip_nuls(char * buf,size_t len)1311 strip_nuls(char *buf, size_t len)
1312 {
1313 	char *cp, *dp, *ep;
1314 
1315 	if (!len || !(dp = memchr(buf, '\0', len)))
1316 		return;
1317 
1318 	ep = buf + len;
1319 	cp = dp;
1320 
1321  cp_has_nul_byte:
1322 	while (cp++ < ep && *cp == '\0')
1323 		;	/* nothing */
1324 	while (cp < ep && *cp != '\0')
1325 		*dp++ = *cp++;
1326 	if (cp < ep)
1327 		goto cp_has_nul_byte;
1328 
1329 	*dp = '\0';
1330 }
1331 
1332 /*
1333  * Like read(2), but if read fails due to non-blocking flag,
1334  * resets flag and restarts read.
1335  */
1336 ssize_t
blocking_read(int fd,char * buf,size_t nbytes)1337 blocking_read(int fd, char *buf, size_t nbytes)
1338 {
1339 	ssize_t ret;
1340 	bool tried_reset = false;
1341 
1342 	while ((ret = read(fd, buf, nbytes)) < 0) {
1343 		if (!tried_reset && errno == EAGAIN) {
1344 			if (reset_nonblock(fd) > 0) {
1345 				tried_reset = true;
1346 				continue;
1347 			}
1348 			errno = EAGAIN;
1349 		}
1350 		break;
1351 	}
1352 	return (ret);
1353 }
1354 
1355 /*
1356  * Reset the non-blocking flag on the specified file descriptor.
1357  * Returns -1 if there was an error, 0 if non-blocking wasn't set,
1358  * 1 if it was.
1359  */
1360 int
reset_nonblock(int fd)1361 reset_nonblock(int fd)
1362 {
1363 	int flags;
1364 
1365 	if ((flags = fcntl(fd, F_GETFL, 0)) < 0)
1366 		return (-1);
1367 	if (!(flags & O_NONBLOCK))
1368 		return (0);
1369 	flags &= ~O_NONBLOCK;
1370 	if (fcntl(fd, F_SETFL, flags) < 0)
1371 		return (-1);
1372 	return (1);
1373 }
1374 
1375 /* getcwd(3) equivalent, allocates from ATEMP but doesn't resize */
1376 char *
ksh_get_wd(void)1377 ksh_get_wd(void)
1378 {
1379 #ifdef MKSH__NO_PATH_MAX
1380 	char *rv, *cp;
1381 
1382 	if ((cp = get_current_dir_name())) {
1383 		strdupx(rv, cp, ATEMP);
1384 		free_gnu_gcdn(cp);
1385 	} else
1386 		rv = NULL;
1387 #else
1388 	char *rv;
1389 
1390 	if (!getcwd((rv = alloc(PATH_MAX + 1, ATEMP)), PATH_MAX)) {
1391 		afree(rv, ATEMP);
1392 		rv = NULL;
1393 	}
1394 #endif
1395 
1396 	return (rv);
1397 }
1398 
1399 #ifndef ELOOP
1400 #define ELOOP		E2BIG
1401 #endif
1402 
1403 char *
do_realpath(const char * upath)1404 do_realpath(const char *upath)
1405 {
1406 	char *xp, *ip, *tp, *ipath, *ldest = NULL;
1407 	XString xs;
1408 	size_t pos, len;
1409 	int llen;
1410 	struct stat sb;
1411 #ifdef MKSH__NO_PATH_MAX
1412 	size_t ldestlen = 0;
1413 #define pathlen sb.st_size
1414 #define pathcnd (ldestlen < (pathlen + 1))
1415 #else
1416 #define pathlen PATH_MAX
1417 #define pathcnd (!ldest)
1418 #endif
1419 	/* max. recursion depth */
1420 	int symlinks = 32;
1421 
1422 	if (mksh_abspath(upath)) {
1423 		/* upath is an absolute pathname */
1424 		strdupx(ipath, upath, ATEMP);
1425 	} else {
1426 		/* upath is a relative pathname, prepend cwd */
1427 		if ((tp = ksh_get_wd()) == NULL || !mksh_abspath(tp))
1428 			return (NULL);
1429 		ipath = shf_smprintf(Tf_sss, tp, "/", upath);
1430 		afree(tp, ATEMP);
1431 	}
1432 
1433 	/* ipath and upath are in memory at the same time -> unchecked */
1434 	Xinit(xs, xp, strlen(ip = ipath) + 1, ATEMP);
1435 
1436 	/* now jump into the deep of the loop */
1437 	goto beginning_of_a_pathname;
1438 
1439 	while (*ip) {
1440 		/* skip slashes in input */
1441 		while (mksh_cdirsep(*ip))
1442 			++ip;
1443 		if (!*ip)
1444 			break;
1445 
1446 		/* get next pathname component from input */
1447 		tp = ip;
1448 		while (*ip && !mksh_cdirsep(*ip))
1449 			++ip;
1450 		len = ip - tp;
1451 
1452 		/* check input for "." and ".." */
1453 		if (tp[0] == '.') {
1454 			if (len == 1)
1455 				/* just continue with the next one */
1456 				continue;
1457 			else if (len == 2 && tp[1] == '.') {
1458 				/* strip off last pathname component */
1459 				while (xp > Xstring(xs, xp))
1460 					if (mksh_cdirsep(*--xp))
1461 						break;
1462 				/* then continue with the next one */
1463 				continue;
1464 			}
1465 		}
1466 
1467 		/* store output position away, then append slash to output */
1468 		pos = Xsavepos(xs, xp);
1469 		/* 1 for the '/' and len + 1 for tp and the NUL from below */
1470 		XcheckN(xs, xp, 1 + len + 1);
1471 		Xput(xs, xp, '/');
1472 
1473 		/* append next pathname component to output */
1474 		memcpy(xp, tp, len);
1475 		xp += len;
1476 		*xp = '\0';
1477 
1478 		/* lstat the current output, see if it's a symlink */
1479 		if (mksh_lstat(Xstring(xs, xp), &sb)) {
1480 			/* lstat failed */
1481 			if (errno == ENOENT) {
1482 				/* because the pathname does not exist */
1483 				while (mksh_cdirsep(*ip))
1484 					/* skip any trailing slashes */
1485 					++ip;
1486 				/* no more components left? */
1487 				if (!*ip)
1488 					/* we can still return successfully */
1489 					break;
1490 				/* more components left? fall through */
1491 			}
1492 			/* not ENOENT or not at the end of ipath */
1493 			goto notfound;
1494 		}
1495 
1496 		/* check if we encountered a symlink? */
1497 		if (S_ISLNK(sb.st_mode)) {
1498 #ifndef MKSH__NO_SYMLINK
1499 			/* reached maximum recursion depth? */
1500 			if (!symlinks--) {
1501 				/* yep, prevent infinite loops */
1502 				errno = ELOOP;
1503 				goto notfound;
1504 			}
1505 
1506 			/* get symlink(7) target */
1507 			if (pathcnd) {
1508 #ifdef MKSH__NO_PATH_MAX
1509 				if (notoktoadd(pathlen, 1)) {
1510 					errno = ENAMETOOLONG;
1511 					goto notfound;
1512 				}
1513 #endif
1514 				ldest = aresize(ldest, pathlen + 1, ATEMP);
1515 			}
1516 			llen = readlink(Xstring(xs, xp), ldest, pathlen);
1517 			if (llen < 0)
1518 				/* oops... */
1519 				goto notfound;
1520 			ldest[llen] = '\0';
1521 
1522 			/*
1523 			 * restart if symlink target is an absolute path,
1524 			 * otherwise continue with currently resolved prefix
1525 			 */
1526 			/* append rest of current input path to link target */
1527 			tp = shf_smprintf(Tf_sss, ldest, *ip ? "/" : "", ip);
1528 			afree(ipath, ATEMP);
1529 			ip = ipath = tp;
1530 			if (!mksh_abspath(ldest)) {
1531 				/* symlink target is a relative path */
1532 				xp = Xrestpos(xs, xp, pos);
1533 			} else
1534 #endif
1535 			  {
1536 				/* symlink target is an absolute path */
1537 				xp = Xstring(xs, xp);
1538  beginning_of_a_pathname:
1539 				/* assert: mksh_cdirsep((ip == ipath)[0]) */
1540 				/* assert: xp == xs.beg => start of path */
1541 
1542 				/* exactly two leading slashes? (SUSv4 3.266) */
1543 				if (ip[1] == ip[0] && !mksh_cdirsep(ip[2])) {
1544 					/* keep them, e.g. for UNC pathnames */
1545 					Xput(xs, xp, '/');
1546 				}
1547 			}
1548 		}
1549 		/* otherwise (no symlink) merely go on */
1550 	}
1551 
1552 	/*
1553 	 * either found the target and successfully resolved it,
1554 	 * or found its parent directory and may create it
1555 	 */
1556 	if (Xlength(xs, xp) == 0)
1557 		/*
1558 		 * if the resolved pathname is "", make it "/",
1559 		 * otherwise do not add a trailing slash
1560 		 */
1561 		Xput(xs, xp, '/');
1562 	Xput(xs, xp, '\0');
1563 
1564 	/*
1565 	 * if source path had a trailing slash, check if target path
1566 	 * is not a non-directory existing file
1567 	 */
1568 	if (ip > ipath && mksh_cdirsep(ip[-1])) {
1569 		if (stat(Xstring(xs, xp), &sb)) {
1570 			if (errno != ENOENT)
1571 				goto notfound;
1572 		} else if (!S_ISDIR(sb.st_mode)) {
1573 			errno = ENOTDIR;
1574 			goto notfound;
1575 		}
1576 		/* target now either does not exist or is a directory */
1577 	}
1578 
1579 	/* return target path */
1580 	afree(ldest, ATEMP);
1581 	afree(ipath, ATEMP);
1582 	return (Xclose(xs, xp));
1583 
1584  notfound:
1585 	/* save; freeing memory might trash it */
1586 	llen = errno;
1587 	afree(ldest, ATEMP);
1588 	afree(ipath, ATEMP);
1589 	Xfree(xs, xp);
1590 	errno = llen;
1591 	return (NULL);
1592 
1593 #undef pathlen
1594 #undef pathcnd
1595 }
1596 
1597 /**
1598  *	Makes a filename into result using the following algorithm.
1599  *	- make result NULL
1600  *	- if file starts with '/', append file to result & set cdpathp to NULL
1601  *	- if file starts with ./ or ../ append cwd and file to result
1602  *	  and set cdpathp to NULL
1603  *	- if the first element of cdpathp doesnt start with a '/' xx or '.' xx
1604  *	  then cwd is appended to result.
1605  *	- the first element of cdpathp is appended to result
1606  *	- file is appended to result
1607  *	- cdpathp is set to the start of the next element in cdpathp (or NULL
1608  *	  if there are no more elements.
1609  *	The return value indicates whether a non-null element from cdpathp
1610  *	was appended to result.
1611  */
1612 static int
make_path(const char * cwd,const char * file,char ** cdpathp,XString * xsp,int * phys_pathp)1613 make_path(const char *cwd, const char *file,
1614     /* pointer to colon-separated list */
1615     char **cdpathp,
1616     XString *xsp,
1617     int *phys_pathp)
1618 {
1619 	int rval = 0;
1620 	bool use_cdpath = true;
1621 	char *plist;
1622 	size_t len, plen = 0;
1623 	char *xp = Xstring(*xsp, xp);
1624 
1625 	if (!file)
1626 		file = null;
1627 
1628 	if (mksh_abspath(file)) {
1629 		*phys_pathp = 0;
1630 		use_cdpath = false;
1631 	} else {
1632 		if (file[0] == '.') {
1633 			char c = file[1];
1634 
1635 			if (c == '.')
1636 				c = file[2];
1637 			if (mksh_cdirsep(c) || c == '\0')
1638 				use_cdpath = false;
1639 		}
1640 
1641 		plist = *cdpathp;
1642 		if (!plist)
1643 			use_cdpath = false;
1644 		else if (use_cdpath) {
1645 			char *pend = plist;
1646 
1647 			while (*pend && *pend != MKSH_PATHSEPC)
1648 				++pend;
1649 			plen = pend - plist;
1650 			*cdpathp = *pend ? pend + 1 : NULL;
1651 		}
1652 
1653 		if ((!use_cdpath || !plen || !mksh_abspath(plist)) &&
1654 		    (cwd && *cwd)) {
1655 			len = strlen(cwd);
1656 			XcheckN(*xsp, xp, len);
1657 			memcpy(xp, cwd, len);
1658 			xp += len;
1659 			if (!mksh_cdirsep(cwd[len - 1]))
1660 				Xput(*xsp, xp, '/');
1661 		}
1662 		*phys_pathp = Xlength(*xsp, xp);
1663 		if (use_cdpath && plen) {
1664 			XcheckN(*xsp, xp, plen);
1665 			memcpy(xp, plist, plen);
1666 			xp += plen;
1667 			if (!mksh_cdirsep(plist[plen - 1]))
1668 				Xput(*xsp, xp, '/');
1669 			rval = 1;
1670 		}
1671 	}
1672 
1673 	len = strlen(file) + 1;
1674 	XcheckN(*xsp, xp, len);
1675 	memcpy(xp, file, len);
1676 
1677 	if (!use_cdpath)
1678 		*cdpathp = NULL;
1679 
1680 	return (rval);
1681 }
1682 
1683 /*-
1684  * Simplify pathnames containing "." and ".." entries.
1685  *
1686  * simplify_path(this)			= that
1687  * /a/b/c/./../d/..			/a/b
1688  * //./C/foo/bar/../baz			//C/foo/baz
1689  * /foo/				/foo
1690  * /foo/../../bar			/bar
1691  * /foo/./blah/..			/foo
1692  * .					.
1693  * ..					..
1694  * ./foo				foo
1695  * foo/../../../bar			../../bar
1696  */
1697 void
simplify_path(char * p)1698 simplify_path(char *p)
1699 {
1700 	char *dp, *ip, *sp, *tp;
1701 	size_t len;
1702 	bool needslash;
1703 
1704 	switch (*p) {
1705 	case 0:
1706 		return;
1707 	case '/':
1708 #ifdef MKSH_DOSPATH
1709 	case '\\':
1710 #endif
1711 		/* exactly two leading slashes? (SUSv4 3.266) */
1712 		if (p[1] == p[0] && !mksh_cdirsep(p[2]))
1713 			/* keep them, e.g. for UNC pathnames */
1714 			++p;
1715 		needslash = true;
1716 		break;
1717 	default:
1718 		needslash = false;
1719 	}
1720 	dp = ip = sp = p;
1721 
1722 	while (*ip) {
1723 		/* skip slashes in input */
1724 		while (mksh_cdirsep(*ip))
1725 			++ip;
1726 		if (!*ip)
1727 			break;
1728 
1729 		/* get next pathname component from input */
1730 		tp = ip;
1731 		while (*ip && !mksh_cdirsep(*ip))
1732 			++ip;
1733 		len = ip - tp;
1734 
1735 		/* check input for "." and ".." */
1736 		if (tp[0] == '.') {
1737 			if (len == 1)
1738 				/* just continue with the next one */
1739 				continue;
1740 			else if (len == 2 && tp[1] == '.') {
1741 				/* parent level, but how? */
1742 				if (mksh_abspath(p))
1743 					/* absolute path, only one way */
1744 					goto strip_last_component;
1745 				else if (dp > sp) {
1746 					/* relative path, with subpaths */
1747 					needslash = false;
1748  strip_last_component:
1749 					/* strip off last pathname component */
1750 					while (dp > sp)
1751 						if (mksh_cdirsep(*--dp))
1752 							break;
1753 				} else {
1754 					/* relative path, at its beginning */
1755 					if (needslash)
1756 						/* or already dotdot-slash'd */
1757 						*dp++ = '/';
1758 					/* keep dotdot-slash if not absolute */
1759 					*dp++ = '.';
1760 					*dp++ = '.';
1761 					needslash = true;
1762 					sp = dp;
1763 				}
1764 				/* then continue with the next one */
1765 				continue;
1766 			}
1767 		}
1768 
1769 		if (needslash)
1770 			*dp++ = '/';
1771 
1772 		/* append next pathname component to output */
1773 		memmove(dp, tp, len);
1774 		dp += len;
1775 
1776 		/* append slash if we continue */
1777 		needslash = true;
1778 		/* try next component */
1779 	}
1780 	if (dp == p)
1781 		/* empty path -> dot */
1782 		*dp++ = needslash ? '/' : '.';
1783 	*dp = '\0';
1784 }
1785 
1786 void
set_current_wd(const char * nwd)1787 set_current_wd(const char *nwd)
1788 {
1789 	char *allocd = NULL;
1790 
1791 	if (nwd == NULL) {
1792 		allocd = ksh_get_wd();
1793 		nwd = allocd ? allocd : null;
1794 	}
1795 
1796 	afree(current_wd, APERM);
1797 	strdupx(current_wd, nwd, APERM);
1798 
1799 	afree(allocd, ATEMP);
1800 }
1801 
1802 int
c_cd(const char ** wp)1803 c_cd(const char **wp)
1804 {
1805 	int optc, rv, phys_path;
1806 	bool physical = tobool(Flag(FPHYSICAL));
1807 	/* was a node from cdpath added in? */
1808 	int cdnode;
1809 	/* show where we went?, error for $PWD */
1810 	bool printpath = false, eflag = false;
1811 	struct tbl *pwd_s, *oldpwd_s;
1812 	XString xs;
1813 	char *dir, *allocd = NULL, *tryp, *pwd, *cdpath;
1814 
1815 	while ((optc = ksh_getopt(wp, &builtin_opt, "eLP")) != -1)
1816 		switch (optc) {
1817 		case 'e':
1818 			eflag = true;
1819 			break;
1820 		case 'L':
1821 			physical = false;
1822 			break;
1823 		case 'P':
1824 			physical = true;
1825 			break;
1826 		case '?':
1827 			return (2);
1828 		}
1829 	wp += builtin_opt.optind;
1830 
1831 	if (Flag(FRESTRICTED)) {
1832 		bi_errorf(Tcant_cd);
1833 		return (2);
1834 	}
1835 
1836 	pwd_s = global(TPWD);
1837 	oldpwd_s = global(TOLDPWD);
1838 
1839 	if (!wp[0]) {
1840 		/* No arguments - go home */
1841 		if ((dir = str_val(global("HOME"))) == null) {
1842 			bi_errorf("no home directory (HOME not set)");
1843 			return (2);
1844 		}
1845 	} else if (!wp[1]) {
1846 		/* One argument: - or dir */
1847 		strdupx(allocd, wp[0], ATEMP);
1848 		if (ksh_isdash((dir = allocd))) {
1849 			afree(allocd, ATEMP);
1850 			allocd = NULL;
1851 			dir = str_val(oldpwd_s);
1852 			if (dir == null) {
1853 				bi_errorf(Tno_OLDPWD);
1854 				return (2);
1855 			}
1856 			printpath = true;
1857 		}
1858 	} else if (!wp[2]) {
1859 		/* Two arguments - substitute arg1 in PWD for arg2 */
1860 		size_t ilen, olen, nlen, elen;
1861 		char *cp;
1862 
1863 		if (!current_wd[0]) {
1864 			bi_errorf("can't determine current directory");
1865 			return (2);
1866 		}
1867 		/*
1868 		 * substitute arg1 for arg2 in current path.
1869 		 * if the first substitution fails because the cd fails
1870 		 * we could try to find another substitution. For now
1871 		 * we don't
1872 		 */
1873 		if ((cp = strstr(current_wd, wp[0])) == NULL) {
1874 			bi_errorf(Tbadsubst);
1875 			return (2);
1876 		}
1877 		/*-
1878 		 * ilen = part of current_wd before wp[0]
1879 		 * elen = part of current_wd after wp[0]
1880 		 * because current_wd and wp[1] need to be in memory at the
1881 		 * same time beforehand the addition can stay unchecked
1882 		 */
1883 		ilen = cp - current_wd;
1884 		olen = strlen(wp[0]);
1885 		nlen = strlen(wp[1]);
1886 		elen = strlen(current_wd + ilen + olen) + 1;
1887 		dir = allocd = alloc(ilen + nlen + elen, ATEMP);
1888 		memcpy(dir, current_wd, ilen);
1889 		memcpy(dir + ilen, wp[1], nlen);
1890 		memcpy(dir + ilen + nlen, current_wd + ilen + olen, elen);
1891 		printpath = true;
1892 	} else {
1893 		bi_errorf(Ttoo_many_args);
1894 		return (2);
1895 	}
1896 
1897 #ifdef MKSH__NO_PATH_MAX
1898 	/* only a first guess; make_path will enlarge xs if necessary */
1899 	XinitN(xs, 1024, ATEMP);
1900 #else
1901 	XinitN(xs, PATH_MAX, ATEMP);
1902 #endif
1903 
1904 	cdpath = str_val(global("CDPATH"));
1905 	do {
1906 		cdnode = make_path(current_wd, dir, &cdpath, &xs, &phys_path);
1907 		if (physical)
1908 			rv = chdir(tryp = Xstring(xs, xp) + phys_path);
1909 		else {
1910 			simplify_path(Xstring(xs, xp));
1911 			rv = chdir(tryp = Xstring(xs, xp));
1912 		}
1913 	} while (rv < 0 && cdpath != NULL);
1914 
1915 	if (rv < 0) {
1916 		if (cdnode)
1917 			bi_errorf(Tf_sD_s, dir, "bad directory");
1918 		else
1919 			bi_errorf(Tf_sD_s, tryp, cstrerror(errno));
1920 		afree(allocd, ATEMP);
1921 		Xfree(xs, xp);
1922 		return (2);
1923 	}
1924 
1925 	rv = 0;
1926 
1927 	/* allocd (above) => dir, which is no longer used */
1928 	afree(allocd, ATEMP);
1929 	allocd = NULL;
1930 
1931 	/* Clear out tracked aliases with relative paths */
1932 	flushcom(false);
1933 
1934 	/*
1935 	 * Set OLDPWD (note: unsetting OLDPWD does not disable this
1936 	 * setting in AT&T ksh)
1937 	 */
1938 	if (current_wd[0])
1939 		/* Ignore failure (happens if readonly or integer) */
1940 		setstr(oldpwd_s, current_wd, KSH_RETURN_ERROR);
1941 
1942 	if (!mksh_abspath(Xstring(xs, xp))) {
1943 		pwd = NULL;
1944 	} else if (!physical) {
1945 		goto norealpath_PWD;
1946 	} else if ((pwd = allocd = do_realpath(Xstring(xs, xp))) == NULL) {
1947 		if (eflag)
1948 			rv = 1;
1949  norealpath_PWD:
1950 		pwd = Xstring(xs, xp);
1951 	}
1952 
1953 	/* Set PWD */
1954 	if (pwd) {
1955 		char *ptmp = pwd;
1956 
1957 		set_current_wd(ptmp);
1958 		/* Ignore failure (happens if readonly or integer) */
1959 		setstr(pwd_s, ptmp, KSH_RETURN_ERROR);
1960 	} else {
1961 		set_current_wd(null);
1962 		pwd = Xstring(xs, xp);
1963 		/* XXX unset $PWD? */
1964 		if (eflag)
1965 			rv = 1;
1966 	}
1967 	if (printpath || cdnode)
1968 		shprintf(Tf_sN, pwd);
1969 
1970 	afree(allocd, ATEMP);
1971 	Xfree(xs, xp);
1972 	return (rv);
1973 }
1974 
1975 
1976 #ifdef KSH_CHVT_CODE
1977 extern void chvt_reinit(void);
1978 
1979 static void
chvt(const Getopt * go)1980 chvt(const Getopt *go)
1981 {
1982 	const char *dv = go->optarg;
1983 	char *cp = NULL;
1984 	int fd;
1985 
1986 	switch (*dv) {
1987 	case '-':
1988 		dv = "/dev/null";
1989 		break;
1990 	case '!':
1991 		++dv;
1992 		/* FALLTHROUGH */
1993 	default: {
1994 		struct stat sb;
1995 
1996 		if (stat(dv, &sb)) {
1997 			cp = shf_smprintf("/dev/ttyC%s", dv);
1998 			dv = cp;
1999 			if (stat(dv, &sb)) {
2000 				memmove(cp + 1, cp, /* /dev/tty */ 8);
2001 				dv = cp + 1;
2002 				if (stat(dv, &sb)) {
2003 					errorf(Tf_sD_sD_s, "chvt",
2004 					    "can't find tty", go->optarg);
2005 				}
2006 			}
2007 		}
2008 		if (!(sb.st_mode & S_IFCHR))
2009 			errorf(Tf_sD_sD_s, "chvt", "not a char device", dv);
2010 #ifndef MKSH_DISABLE_REVOKE_WARNING
2011 #if HAVE_REVOKE
2012 		if (revoke(dv))
2013 #endif
2014 			warningf(false, Tf_sD_s_s, "chvt",
2015 			    "new shell is potentially insecure, can't revoke",
2016 			    dv);
2017 #endif
2018 	    }
2019 	}
2020 	if ((fd = binopen2(dv, O_RDWR)) < 0) {
2021 		sleep(1);
2022 		if ((fd = binopen2(dv, O_RDWR)) < 0) {
2023 			errorf(Tf_sD_s_s, "chvt", Tcant_open, dv);
2024 		}
2025 	}
2026 	if (go->optarg[0] != '!') {
2027 		switch (fork()) {
2028 		case -1:
2029 			errorf(Tf_sD_s_s, "chvt", "fork", "failed");
2030 		case 0:
2031 			break;
2032 		default:
2033 			exit(0);
2034 		}
2035 	}
2036 	if (setsid() == -1)
2037 		errorf(Tf_sD_s_s, "chvt", "setsid", "failed");
2038 	if (go->optarg[0] != '-') {
2039 		if (ioctl(fd, TIOCSCTTY, NULL) == -1)
2040 			errorf(Tf_sD_s_s, "chvt", "TIOCSCTTY", "failed");
2041 		if (tcflush(fd, TCIOFLUSH))
2042 			errorf(Tf_sD_s_s, "chvt", "TCIOFLUSH", "failed");
2043 	}
2044 	ksh_dup2(fd, 0, false);
2045 	ksh_dup2(fd, 1, false);
2046 	ksh_dup2(fd, 2, false);
2047 	if (fd > 2)
2048 		close(fd);
2049 	rndset((unsigned long)chvt_rndsetup(go, sizeof(Getopt)));
2050 	chvt_reinit();
2051 }
2052 #endif
2053 
2054 #ifdef DEBUG
2055 char *
strchr(char * p,int ch)2056 strchr(char *p, int ch)
2057 {
2058 	for (;; ++p) {
2059 		if (*p == ch)
2060 			return (p);
2061 		if (!*p)
2062 			return (NULL);
2063 	}
2064 	/* NOTREACHED */
2065 }
2066 
2067 char *
strstr(char * b,const char * l)2068 strstr(char *b, const char *l)
2069 {
2070 	char first, c;
2071 	size_t n;
2072 
2073 	if ((first = *l++) == '\0')
2074 		return (b);
2075 	n = strlen(l);
2076  strstr_look:
2077 	while ((c = *b++) != first)
2078 		if (c == '\0')
2079 			return (NULL);
2080 	if (strncmp(b, l, n))
2081 		goto strstr_look;
2082 	return (b - 1);
2083 }
2084 #endif
2085 
2086 #if defined(MKSH_SMALL) && !defined(MKSH_SMALL_BUT_FAST)
2087 char *
strndup_i(const char * src,size_t len,Area * ap)2088 strndup_i(const char *src, size_t len, Area *ap)
2089 {
2090 	char *dst = NULL;
2091 
2092 	if (src != NULL) {
2093 		dst = alloc(len + 1, ap);
2094 		memcpy(dst, src, len);
2095 		dst[len] = '\0';
2096 	}
2097 	return (dst);
2098 }
2099 
2100 char *
strdup_i(const char * src,Area * ap)2101 strdup_i(const char *src, Area *ap)
2102 {
2103 	return (src == NULL ? NULL : strndup_i(src, strlen(src), ap));
2104 }
2105 #endif
2106 
2107 #if !HAVE_GETRUSAGE
2108 #define INVTCK(r,t)	do {						\
2109 	r.tv_usec = ((t) % (1000000 / CLK_TCK)) * (1000000 / CLK_TCK);	\
2110 	r.tv_sec = (t) / CLK_TCK;					\
2111 } while (/* CONSTCOND */ 0)
2112 
2113 int
getrusage(int what,struct rusage * ru)2114 getrusage(int what, struct rusage *ru)
2115 {
2116 	struct tms tms;
2117 	clock_t u, s;
2118 
2119 	if (/* ru == NULL || */ times(&tms) == (clock_t)-1)
2120 		return (-1);
2121 
2122 	switch (what) {
2123 	case RUSAGE_SELF:
2124 		u = tms.tms_utime;
2125 		s = tms.tms_stime;
2126 		break;
2127 	case RUSAGE_CHILDREN:
2128 		u = tms.tms_cutime;
2129 		s = tms.tms_cstime;
2130 		break;
2131 	default:
2132 		errno = EINVAL;
2133 		return (-1);
2134 	}
2135 	INVTCK(ru->ru_utime, u);
2136 	INVTCK(ru->ru_stime, s);
2137 	return (0);
2138 }
2139 #endif
2140 
2141 /*
2142  * process the string available via fg (get a char)
2143  * and fp (put back a char) for backslash escapes,
2144  * assuming the first call to *fg gets the char di-
2145  * rectly after the backslash; return the character
2146  * (0..0xFF), Unicode (wc + 0x100), or -1 if no known
2147  * escape sequence was found
2148  */
2149 int
unbksl(bool cstyle,int (* fg)(void),void (* fp)(int))2150 unbksl(bool cstyle, int (*fg)(void), void (*fp)(int))
2151 {
2152 	int wc, i, c, fc, n;
2153 
2154 	fc = (*fg)();
2155 	switch (fc) {
2156 	case 'a':
2157 		/*
2158 		 * according to the comments in pdksh, \007 seems
2159 		 * to be more portable than \a (due to HP-UX cc,
2160 		 * Ultrix cc, old pcc, etc.) so we avoid the escape
2161 		 * sequence altogether in mksh and assume ASCII
2162 		 */
2163 		wc = 7;
2164 		break;
2165 	case 'b':
2166 		wc = '\b';
2167 		break;
2168 	case 'c':
2169 		if (!cstyle)
2170 			goto unknown_escape;
2171 		c = (*fg)();
2172 		wc = CTRL(c);
2173 		break;
2174 	case 'E':
2175 	case 'e':
2176 		wc = 033;
2177 		break;
2178 	case 'f':
2179 		wc = '\f';
2180 		break;
2181 	case 'n':
2182 		wc = '\n';
2183 		break;
2184 	case 'r':
2185 		wc = '\r';
2186 		break;
2187 	case 't':
2188 		wc = '\t';
2189 		break;
2190 	case 'v':
2191 		/* assume ASCII here as well */
2192 		wc = 11;
2193 		break;
2194 	case '1':
2195 	case '2':
2196 	case '3':
2197 	case '4':
2198 	case '5':
2199 	case '6':
2200 	case '7':
2201 		if (!cstyle)
2202 			goto unknown_escape;
2203 		/* FALLTHROUGH */
2204 	case '0':
2205 		if (cstyle)
2206 			(*fp)(fc);
2207 		/*
2208 		 * look for an octal number with up to three
2209 		 * digits, not counting the leading zero;
2210 		 * convert it to a raw octet
2211 		 */
2212 		wc = 0;
2213 		i = 3;
2214 		while (i--)
2215 			if ((c = (*fg)()) >= ord('0') && c <= ord('7'))
2216 				wc = (wc << 3) + ksh_numdig(c);
2217 			else {
2218 				(*fp)(c);
2219 				break;
2220 			}
2221 		break;
2222 	case 'U':
2223 		i = 8;
2224 		if (/* CONSTCOND */ 0)
2225 			/* FALLTHROUGH */
2226 	case 'u':
2227 		  i = 4;
2228 		if (/* CONSTCOND */ 0)
2229 			/* FALLTHROUGH */
2230 	case 'x':
2231 		  i = cstyle ? -1 : 2;
2232 		/**
2233 		 * x:	look for a hexadecimal number with up to
2234 		 *	two (C style: arbitrary) digits; convert
2235 		 *	to raw octet (C style: Unicode if >0xFF)
2236 		 * u/U:	look for a hexadecimal number with up to
2237 		 *	four (U: eight) digits; convert to Unicode
2238 		 */
2239 		wc = 0;
2240 		n = 0;
2241 		while (n < i || i == -1) {
2242 			wc <<= 4;
2243 			if ((c = (*fg)()) >= ord('0') && c <= ord('9'))
2244 				wc += ksh_numdig(c);
2245 			else if (c >= ord('A') && c <= ord('F'))
2246 				wc += ksh_numuc(c) + 10;
2247 			else if (c >= ord('a') && c <= ord('f'))
2248 				wc += ksh_numlc(c) + 10;
2249 			else {
2250 				wc >>= 4;
2251 				(*fp)(c);
2252 				break;
2253 			}
2254 			++n;
2255 		}
2256 		if (!n)
2257 			goto unknown_escape;
2258 		if ((cstyle && wc > 0xFF) || fc != 'x')
2259 			/* Unicode marker */
2260 			wc += 0x100;
2261 		break;
2262 	case '\'':
2263 		if (!cstyle)
2264 			goto unknown_escape;
2265 		wc = '\'';
2266 		break;
2267 	case '\\':
2268 		wc = '\\';
2269 		break;
2270 	default:
2271  unknown_escape:
2272 		(*fp)(fc);
2273 		return (-1);
2274 	}
2275 
2276 	return (wc);
2277 }
2278