ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/src/vendor/MirOS/mksh/R50e/misc.c
Revision: 6980
Committed: Fri Mar 20 00:51:42 2015 UTC (9 years, 1 month ago) by laffer1
Content type: text/plain
File size: 49441 byte(s)
Log Message:
mksh R50e

File Contents

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