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 $ */
5 * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
6 * 2011, 2012, 2013, 2014, 2015, 2016, 2017
7 * mirabilos <m@mirbsd.org>
9 * Daniel Richard G. <skunk@iSKUNK.ORG>
11 * Provided that these terms and disclaimer and all copyright notices
12 * are retained or reproduced in an accompanying document, permission
13 * is granted to deal in this work without restriction, including un-
14 * limited rights to use, publicly perform, distribute, sell, modify,
15 * merge, give away, or sublicence.
17 * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
18 * the utmost extent permitted by applicable law, neither express nor
19 * implied; without malicious intent or gross negligence. In no event
20 * may a licensor, author or contributor be held liable for indirect,
21 * direct, other damage, loss, or other issues arising in any way out
22 * of dealing in the work, even if advised of the possibility of such
23 * damage or existence of a defect, except proven that it results out
24 * of said person's immediate fault when using the work as intended.
29 #include <sys/times.h>
35 __RCSID("$MirOS: src/bin/mksh/misc.c,v 1.279 2017/08/07 21:39:25 tg Exp $");
46 /* type bits for unsigned char */
47 unsigned char chtypes[UCHAR_MAX + 1];
49 static const unsigned char *pat_scan(const unsigned char *,
50 const unsigned char *, bool) MKSH_A_PURE;
51 static int do_gmatch(const unsigned char *, const unsigned char *,
52 const unsigned char *, const unsigned char *,
53 const unsigned char *) MKSH_A_PURE;
54 static const unsigned char *gmatch_cclass(const unsigned char *, unsigned char)
57 static void chvt(const Getopt *);
60 /*XXX this should go away */
61 static int make_path(const char *, const char *, char **, XString *, int *);
63 #ifdef SETUID_CAN_FAIL_WITH_EAGAIN
64 /* we don't need to check for other codes, EPERM won't happen */
65 #define DO_SETUID(func, argvec) do { \
66 if ((func argvec) && errno == EAGAIN) \
67 errorf("%s failed with EAGAIN, probably due to a" \
68 " too low process limit; aborting", #func); \
69 } while (/* CONSTCOND */ 0)
71 #define DO_SETUID(func, argvec) func argvec
75 /* called from XcheckN() to grow buffer */
77 Xcheck_grow(XString *xsp, const char *xp, size_t more)
79 const char *old_beg = xsp->beg;
83 /* (xsp->len + X_EXTRA) never overflows */
84 checkoktoadd(more, xsp->len + X_EXTRA);
85 xsp->beg = aresize(xsp->beg, (xsp->len += more) + X_EXTRA, xsp->areap);
86 xsp->end = xsp->beg + xsp->len;
87 return (xsp->beg + (xp - old_beg));
92 #define FN(sname,cname,flags,ochar) \
93 static const struct { \
94 /* character flag (if any) */ \
97 unsigned char optflags; \
98 /* long name of option */ \
99 char name[sizeof(sname)]; \
100 } shoptione_ ## cname = { \
101 ochar, flags, sname \
103 #include "sh_flags.gen"
105 #define OFC(i) (options[i][-2])
106 #define OFF(i) (((const unsigned char *)options[i])[-1])
107 #define OFN(i) (options[i])
109 const char * const options[] = {
110 #define SHFLAGS_ITEMS
111 #include "sh_flags.gen"
115 * translate -o option into F* constant (also used for test -o option)
118 option(const char *n)
122 if (ctype(n[0], C_MINUS | C_PLUS) && n[1] && !n[2])
123 while (i < NELEM(options)) {
129 while (i < NELEM(options)) {
130 if (!strcmp(OFN(i), n))
138 struct options_info {
140 int opts[NELEM(options)];
143 static void options_fmt_entry(char *, size_t, unsigned int, const void *);
144 static void printoptions(bool);
146 /* format a single select menu item */
148 options_fmt_entry(char *buf, size_t buflen, unsigned int i, const void *arg)
150 const struct options_info *oi = (const struct options_info *)arg;
152 shf_snprintf(buf, buflen, "%-*s %s",
153 oi->opt_width, OFN(oi->opts[i]),
154 Flag(oi->opts[i]) ? "on" : "off");
158 printoptions(bool verbose)
163 size_t n = 0, len, octs = 0;
164 struct options_info oi;
165 struct columnise_opts co;
167 /* verbose version */
168 shf_puts("Current option settings\n", shl_stdout);
171 while (i < NELEM(options)) {
172 if ((len = strlen(OFN(i)))) {
176 len = utf_mbswidth(OFN(i));
177 if ((int)len > oi.opt_width)
178 oi.opt_width = (int)len;
184 co.prefcol = co.do_last = true;
185 print_columns(&co, n, options_fmt_entry, &oi,
186 octs + 4, oi.opt_width + 4);
188 /* short version like AT&T ksh93 */
189 shf_puts(Tset, shl_stdout);
190 while (i < NELEM(options)) {
191 if (Flag(i) && OFN(i)[0])
192 shprintf(" -o %s", OFN(i));
195 shf_putc('\n', shl_stdout);
203 char c, m[(int)FNFLAGS + 1];
206 while (i < NELEM(options)) {
207 if ((c = OFC(i)) && Flag(i))
211 strndupx(cp, m, cp - m, ATEMP);
215 /* change a Flag(*) value; takes care of special actions */
217 change_flag(enum sh_flag f, int what, bool newset)
219 unsigned char oldval;
220 unsigned char newval = (newset ? 1 : 0);
223 change_xtrace(newval, true);
227 Flag(f) = newval = (newset ? 1 : 0);
228 #ifndef MKSH_UNEMPLOYED
230 if (what != OF_CMDLINE && newval != oldval)
234 #ifndef MKSH_NO_CMDLINE_EDITING
239 f == FEMACS || f == FGMACS) && newval) {
243 Flag(FEMACS) = Flag(FGMACS) = 0;
247 if (f == FPRIVILEGED && oldval && !newval) {
248 /* Turning off -p? */
250 /*XXX this can probably be optimised */
251 kshegid = kshgid = getgid();
252 ksheuid = kshuid = getuid();
254 DO_SETUID(setresgid, (kshegid, kshegid, kshegid));
256 /* setgroups doesn't EAGAIN on Linux */
257 setgroups(1, &kshegid);
259 DO_SETUID(setresuid, (ksheuid, ksheuid, ksheuid));
260 #else /* !HAVE_SETRESUGID */
261 /* setgid, setegid, seteuid don't EAGAIN on Linux */
263 #ifndef MKSH__NO_SETEUGID
266 DO_SETUID(setuid, (ksheuid));
267 #ifndef MKSH__NO_SETEUGID
270 #endif /* !HAVE_SETRESUGID */
271 } else if ((f == FPOSIX || f == FSH) && newval) {
272 /* Turning on -o posix or -o sh? */
273 Flag(FBRACEEXPAND) = 0;
274 /* Turning on -o posix? */
276 /* C locale required for compliance */
279 } else if (f == FTALKING) {
280 /* Changing interactive flag? */
281 if ((what == OF_CMDLINE || what == OF_SET) && procpid == kshpid)
282 Flag(FTALKING_I) = newval;
287 change_xtrace(unsigned char newval, bool dosnapshot)
289 static bool in_xtrace;
294 if (!dosnapshot && newval == Flag(FXTRACE))
297 if (Flag(FXTRACE) == 2) {
298 shf_putc('\n', shl_xtrace);
300 shf_flush(shl_xtrace);
303 if (!dosnapshot && Flag(FXTRACE) == 1)
311 shf_flush(shl_xtrace);
312 if (shl_xtrace->fd != 2)
313 close(shl_xtrace->fd);
314 if (!newval || (shl_xtrace->fd = savefd(2)) == -1)
318 if ((Flag(FXTRACE) = newval) == 2) {
321 shf_puts(substitute(str_val(global("PS4")), 0), shl_xtrace);
328 * Parse command line and set command arguments. Returns the index of
329 * non-option arguments, -1 if there is an error.
332 parse_args(const char **argv,
333 /* OF_FIRSTTIME, OF_CMDLINE, or OF_SET */
337 static const char cmd_opts[] =
338 #define SHFLAGS_NOT_SET
339 #define SHFLAGS_OPTCS
340 #include "sh_flags.gen"
341 #undef SHFLAGS_NOT_SET
343 static const char set_opts[] =
344 #define SHFLAGS_NOT_CMD
345 #define SHFLAGS_OPTCS
346 #include "sh_flags.gen"
347 #undef SHFLAGS_NOT_CMD
351 const char *array = NULL;
354 int optc, arrayset = 0;
355 bool sortargs = false;
356 bool fcompatseen = false;
358 if (what == OF_CMDLINE) {
359 const char *p = argv[0], *q;
361 * Set FLOGIN before parsing options so user can clear
366 if (mksh_cdirsep(*q++))
368 Flag(FLOGIN) = (*p == '-');
370 } else if (what == OF_FIRSTTIME) {
374 ksh_getopt_reset(&go, GF_ERROR|GF_PLUSOPT);
375 while ((optc = ksh_getopt(argv, &go, opts)) != -1) {
376 set = tobool(!(go.info & GI_PLUS));
379 if (what == OF_FIRSTTIME)
381 arrayset = set ? 1 : -1;
386 if (what == OF_FIRSTTIME)
388 if (go.optarg == NULL) {
390 * lone -o: print options
392 * Note that on the command line, -o requires
393 * an option (ie, can't get here if what is
399 i = option(go.optarg);
400 if ((i == FPOSIX || i == FSH) && set && !fcompatseen) {
402 * If running 'set -o posix' or
403 * 'set -o sh', turn off the other;
404 * if running 'set -o posix -o sh'
405 * allow both to be set though.
411 if ((i != (size_t)-1) && (set ? 1U : 0U) == Flag(i))
413 * Don't check the context if the flag
414 * isn't changing - makes "set -o interactive"
415 * work if you're already interactive. Needed
416 * if the output of "set +o" is to be used.
419 else if ((i != (size_t)-1) && (OFF(i) & what))
420 change_flag((enum sh_flag)i, what, set);
422 bi_errorf(Tf_sD_s, go.optarg,
430 if (what != OF_FIRSTTIME)
432 #ifndef KSH_CHVT_CODE
433 errorf("no TIOCSCTTY ioctl");
435 change_flag(FTALKING, OF_CMDLINE, true);
445 if (what == OF_FIRSTTIME)
447 /* -s: sort positional params (AT&T ksh stupidity) */
448 if (what == OF_SET && optc == 's') {
452 for (i = 0; i < NELEM(options); i++)
453 if (optc == OFC(i) &&
455 change_flag((enum sh_flag)i, what, set);
458 if (i == NELEM(options))
459 internal_errorf("parse_args: '%c'", optc);
462 if (!(go.info & GI_MINUSMINUS) && argv[go.optind] &&
463 ctype(argv[go.optind][0], C_MINUS | C_PLUS) &&
464 argv[go.optind][1] == '\0') {
465 /* lone - clears -v and -x flags */
466 if (argv[go.optind][0] == '-') {
468 change_xtrace(0, false);
470 /* set skips lone - or + option */
474 /* -- means set $#/$* even if there are no arguments */
475 *setargsp = !arrayset && ((go.info & GI_MINUSMINUS) ||
479 const char *ccp = NULL;
482 ccp = skip_varname(array, false);
483 if (!ccp || !(!ccp[0] || (ccp[0] == '+' && !ccp[1]))) {
484 bi_errorf(Tf_sD_s, array, Tnot_ident);
489 for (i = go.optind; argv[i]; i++)
491 qsort(&argv[go.optind], i - go.optind, sizeof(void *),
495 go.optind += set_array(array, tobool(arrayset > 0),
501 /* parse a decimal number: returns 0 if string isn't a number, 1 otherwise */
503 getn(const char *s, int *ai)
513 } while (ctype(c, C_SPACE));
525 if (!ctype(c, C_DIGIT))
528 if (num.u > 214748364U)
529 /* overflow on multiplication */
531 num.u = num.u * 10U + (unsigned int)ksh_numdig(c);
532 /* now: num.u <= 2147483649U */
533 } while ((c = *s++));
535 if (num.u > (neg ? 2147483648U : 2147483647U))
536 /* overflow for signed 32-bit int */
546 * pattern simplifications:
547 * - @(x) -> x (not @(x|y) though)
551 simplify_gmatch_pattern(const unsigned char *sp)
554 unsigned char *cp, *dp;
555 const unsigned char *ps, *se;
557 cp = alloc(strlen((const void *)sp) + 1, ATEMP);
558 goto simplify_gmatch_pat1a;
560 /* foo@(b@(a)r)b@(a|a)z -> foobarb@(a|a)z */
561 simplify_gmatch_pat1:
563 simplify_gmatch_pat1a:
566 while ((c = *sp++)) {
571 switch ((c = *sp++)) {
575 /* check whether it has only one clause */
576 ps = pat_scan(sp, se, true);
577 if (!ps || ps[-1] != /*(*/ ')')
580 /* copy inner clause until matching close */
582 while ((const unsigned char *)sp < ps)
584 /* skip MAGIC and closing parenthesis */
586 /* copy the rest of the pattern */
587 memmove(dp, sp, strlen((const void *)sp) + 1);
588 /* redo from start */
589 goto simplify_gmatch_pat1;
596 /* collapse adjacent asterisk wildcards */
598 while ((c = *sp++)) {
603 switch ((c = *sp++)) {
605 while (ISMAGIC(sp[0]) && sp[1] == c)
614 /* return the result, allocated from ATEMP */
618 /* -------- gmatch.c -------- */
621 * int gmatch(string, pattern)
622 * char *string, *pattern;
624 * Match a pattern as in sh(1).
625 * pattern character are prefixed with MAGIC by expand.
628 gmatchx(const char *s, const char *p, bool isfile)
634 if (s == NULL || p == NULL)
639 * isfile is false iff no syntax check has been done on
640 * the pattern. If check fails, just do a strcmp().
642 if (!isfile && !has_globbing(p)) {
643 size_t len = pe - p + 1;
645 char *t = len <= sizeof(tbuf) ? tbuf : alloc(len, ATEMP);
647 return (!strcmp(t, s));
652 * since the do_gmatch() engine sucks so much, we must do some
653 * pattern simplifications
655 pnew = simplify_gmatch_pattern((const unsigned char *)p);
658 rv = do_gmatch((const unsigned char *)s, (const unsigned char *)se,
659 (const unsigned char *)pnew, (const unsigned char *)pe,
660 (const unsigned char *)s);
666 * Returns if p is a syntacticly correct globbing pattern, false
667 * if it contains no pattern characters or if there is a syntax error.
669 * - [ with no closing ]
670 * - imbalanced $(...) expression
671 * - [...] and *(...) not nested (eg, @(a[b|)c], *(a[b|c]d))
675 * if dest given, copy to dst
677 * - if magic && (no globbing || syntax error)
683 has_globbing(const char *pat)
685 unsigned char c, subc;
686 bool saw_glob = false;
687 unsigned int nest = 0;
688 const unsigned char *p = (const unsigned char *)pat;
689 const unsigned char *s;
692 /* regular character? ok. */
695 /* MAGIC + NUL? abort. */
699 if (ord(c) == ord('*') || ord(c) == ord('?')) {
700 /* easy glob, accept */
702 } else if (ord(c) == ord('[')) {
703 /* bracket expression; eat negation and initial ] */
704 if (ISMAGIC(p[0]) && ord(p[1]) == ord('!'))
706 if (ISMAGIC(p[0]) && ord(p[1]) == ord(']'))
708 /* check next string part */
711 /* regular chars are ok */
714 /* MAGIC + NUL cannot happen */
717 /* terminating bracket? */
718 if (ord(c) == ord(']')) {
719 /* accept and continue */
724 /* sub-bracket expressions */
725 if (ord(c) == ord('[') && (
726 /* collating element? */
727 ord(*s) == ord('.') ||
728 /* equivalence class? */
729 ord(*s) == ord('=') ||
730 /* character class? */
731 ord(*s) == ord(':'))) {
732 /* must stop with exactly the same c */
734 /* arbitrarily many chars in betwixt */
736 /* but only this sequence... */
737 if (c == subc && ISMAGIC(*s) &&
738 ord(s[1]) == ord(']')) {
739 /* accept, terminate */
743 /* EOS without: reject bracket expr */
748 /* anything else just goes on */
750 } else if ((c & 0x80) && ctype(c & 0x7F, C_PATMO | C_SPC)) {
751 /* opening pattern */
754 } else if (ord(c) == ord(/*(*/ ')')) {
755 /* closing pattern */
760 return (saw_glob && !nest);
763 /* Function must return either 0 or 1 (assumed by code for 0x80|'!') */
765 do_gmatch(const unsigned char *s, const unsigned char *se,
766 const unsigned char *p, const unsigned char *pe,
767 const unsigned char *smin)
769 unsigned char sc, pc, sl = 0;
770 const unsigned char *prest, *psub, *pnext;
771 const unsigned char *srest;
773 if (s == NULL || p == NULL)
775 if (s > smin && s <= se)
779 sc = s < se ? *s : '\0';
789 /* BSD cclass extension? */
790 if (ISMAGIC(p[0]) && ord(p[1]) == ord('[') &&
791 ord(p[2]) == ord(':') &&
792 ctype((pc = p[3]), C_ANGLE) &&
793 ord(p[4]) == ord(':') &&
794 ISMAGIC(p[5]) && ord(p[6]) == ord(']') &&
795 ISMAGIC(p[7]) && ord(p[8]) == ord(']')) {
796 /* zero-length match */
800 if (ord(pc) == ord('<') &&
801 !ctype(sl, C_ALNUX) &&
805 if (ord(pc) == ord('>') &&
806 ctype(sl, C_ALNUX) &&
812 if (sc == 0 || (p = gmatch_cclass(p, sc)) == NULL)
821 s += utf_ptradj((const void *)s);
830 if (do_gmatch(s, se, p, pe, smin))
836 * [+*?@!](pattern|pattern|..)
837 * This is also needed for ${..%..}, etc.
840 /* matches one or more times */
842 /* matches zero or more times */
844 if (!(prest = pat_scan(p, pe, false)))
847 /* take care of zero matches */
848 if (ord(p[-1]) == (0x80 | ord('*')) &&
849 do_gmatch(s, se, prest, pe, smin))
851 for (psub = p; ; psub = pnext) {
852 pnext = pat_scan(psub, pe, true);
853 for (srest = s; srest <= se; srest++) {
854 if (do_gmatch(s, srest, psub, pnext - 2, smin) &&
855 (do_gmatch(srest, se, prest, pe, smin) ||
857 do_gmatch(srest, se, p - 2, pe, smin))))
865 /* matches zero or once */
867 /* matches one of the patterns */
871 if (!(prest = pat_scan(p, pe, false)))
874 /* Take care of zero matches */
875 if (ord(p[-1]) == (0x80 | ord('?')) &&
876 do_gmatch(s, se, prest, pe, smin))
878 for (psub = p; ; psub = pnext) {
879 pnext = pat_scan(psub, pe, true);
880 srest = prest == pe ? se : s;
881 for (; srest <= se; srest++) {
882 if (do_gmatch(s, srest, psub, pnext - 2, smin) &&
883 do_gmatch(srest, se, prest, pe, smin))
891 /* matches none of the patterns */
893 if (!(prest = pat_scan(p, pe, false)))
896 for (srest = s; srest <= se; srest++) {
899 for (psub = p; ; psub = pnext) {
900 pnext = pat_scan(psub, pe, true);
901 if (do_gmatch(s, srest, psub,
910 do_gmatch(srest, se, prest, pe, smin))
925 /*XXX this is a prime example for bsearch or a const hashtable */
926 static const struct cclass {
931 { "alnum", C_ALNUM },
932 { "alpha", C_ALPHA },
933 { "blank", C_BLANK },
934 { "cntrl", C_CNTRL },
935 { "digit", C_DIGIT },
936 { "graph", C_GRAPH },
937 { "lower", C_LOWER },
938 { "print", C_PRINT },
939 { "punct", C_PUNCT },
940 { "space", C_SPACE },
941 { "upper", C_UPPER },
942 { "xdigit", C_SEDEC },
944 /* "<" and ">" are handled inline */
946 { "ascii", C_ASCII },
949 { "sh_alias", C_ALIAS },
952 { "sh_ifsws", C_IFSWS },
954 { "sh_quote", C_QUOTE },
959 static const unsigned char *
960 gmatch_cclass(const unsigned char *pat, unsigned char sc)
962 unsigned char c, subc, lc;
963 const unsigned char *p = pat, *s;
965 bool negated = false;
968 /* check for negation */
969 if (ISMAGIC(p[0]) && ord(p[1]) == ord('!')) {
973 /* make initial ] non-MAGIC */
974 if (ISMAGIC(p[0]) && ord(p[1]) == ord(']'))
976 /* iterate over bracket expression, debunk()ing on the fly */
979 /* non-regular character? */
981 /* MAGIC + NUL cannot happen */
984 /* terminating bracket? */
985 if (ord(c) == ord(']')) {
986 /* accept and return */
987 return (found != negated ? p : NULL);
989 /* sub-bracket expressions */
990 if (ord(c) == ord('[') && (
991 /* collating element? */
992 ord(*p) == ord('.') ||
993 /* equivalence class? */
994 ord(*p) == ord('=') ||
995 /* character class? */
996 ord(*p) == ord(':'))) {
997 /* must stop with exactly the same c */
999 /* save away start of substring */
1001 /* arbitrarily many chars in betwixt */
1003 /* but only this sequence... */
1004 if (c == subc && ISMAGIC(*p) &&
1005 ord(p[1]) == ord(']')) {
1006 /* accept, terminate */
1010 /* EOS without: reject bracket expr */
1013 /* debunk substring */
1014 strndupx(subp, s, p - s - 3, ATEMP);
1015 debunk(subp, subp, p - s - 3 + 1);
1017 /* whither subexpression */
1018 if (ord(subc) == ord(':')) {
1019 const struct cclass *cls = cclasses;
1021 /* search for name in cclass list */
1023 if (!strcmp(subp, cls->name)) {
1028 /* break either way */
1032 /* that's all here */
1036 /* collating element or equivalence class */
1037 /* Note: latter are treated as former */
1038 if (ctype(subp[0], C_ASCII) && !subp[1])
1039 /* [.a.] where a is one ASCII char */
1042 /* force no match */
1044 /* no longer needed */
1046 } else if (!ISMAGIC(c) && (c & 0x80)) {
1047 /* 0x80|' ' is plain (...) */
1048 if ((c &= 0x7F) != ' ') {
1049 /* check single match NOW */
1052 /* next character is (...) */
1057 /* range expression? */
1058 if (!(ISMAGIC(p[0]) && ord(p[1]) == ord('-') &&
1059 /* not terminating bracket? */
1060 (!ISMAGIC(p[2]) || ord(p[3]) != ord(']')))) {
1061 /* no, check single match */
1063 /* note: sc is never NUL */
1065 /* do the next "first" character */
1068 /* save lower range bound */
1070 /* skip over the range operator */
1072 /* do the same shit as above... almost */
1076 /* non-regular character? */
1078 /* MAGIC + NUL cannot happen */
1081 /* sub-bracket expressions */
1082 if (ord(c) == ord('[') && (
1083 /* collating element? */
1084 ord(*p) == ord('.') ||
1085 /* equivalence class? */
1086 ord(*p) == ord('=') ||
1087 /* character class? */
1088 ord(*p) == ord(':'))) {
1089 /* must stop with exactly the same c */
1091 /* save away start of substring */
1093 /* arbitrarily many chars in betwixt */
1095 /* but only this sequence... */
1096 if (c == subc && ISMAGIC(*p) &&
1097 ord(p[1]) == ord(']')) {
1098 /* accept, terminate */
1102 /* EOS without: reject bracket expr */
1105 /* debunk substring */
1106 strndupx(subp, s, p - s - 3, ATEMP);
1107 debunk(subp, subp, p - s - 3 + 1);
1108 /* whither subexpression */
1109 if (ord(subc) == ord(':')) {
1110 /* oops, not a range */
1112 /* match single previous char */
1113 if (lc && (sc == lc))
1115 /* match hyphen-minus */
1116 if (ord(sc) == ord('-'))
1118 /* handle cclass common part */
1121 /* collating element or equivalence class */
1122 /* Note: latter are treated as former */
1123 if (ctype(subp[0], C_ASCII) && !subp[1])
1124 /* [.a.] where a is one ASCII char */
1127 /* force no match */
1129 /* no longer needed */
1131 /* other meaning below */
1133 } else if (c == (0x80 | ' ')) {
1134 /* 0x80|' ' is plain (...) */
1136 } else if (!ISMAGIC(c) && (c & 0x80)) {
1141 /* now do the actual range match check */
1142 if (lc != 0 /* && c != 0 */ &&
1143 asciibetical(lc) <= asciibetical(sc) &&
1144 asciibetical(sc) <= asciibetical(c))
1146 /* forced next character? */
1151 /* otherwise, just go on with the pattern string */
1153 /* if we broke here, the bracket expression was invalid */
1154 if (ord(sc) == ord('['))
1155 /* initial opening bracket as literal match */
1157 /* or rather no match */
1161 /* Look for next ) or | (if match_sep) in *(foo|bar) pattern */
1162 static const unsigned char *
1163 pat_scan(const unsigned char *p, const unsigned char *pe, bool match_sep)
1167 for (; p < pe; p++) {
1170 if ((*++p == /*(*/ ')' && nest-- == 0) ||
1171 (*p == '|' && match_sep && nest == 0))
1173 if ((*p & 0x80) && ctype(*p & 0x7F, C_PATMO | C_SPC))
1180 ascstrcmp(const void *s1, const void *s2)
1182 const uint8_t *cp1 = s1, *cp2 = s2;
1184 while (*cp1 == *cp2) {
1189 return ((int)asciibetical(*cp1) - (int)asciibetical(*cp2));
1193 ascpstrcmp(const void *pstr1, const void *pstr2)
1195 return (ascstrcmp(*(const char * const *)pstr1,
1196 *(const char * const *)pstr2));
1199 /* Initialise a Getopt structure */
1201 ksh_getopt_reset(Getopt *go, int flags)
1213 * getopt() used for shell built-in commands, the getopts command, and
1214 * command line options.
1215 * A leading ':' in options means don't print errors, instead return '?'
1216 * or ':' and set go->optarg to the offending option character.
1217 * If GF_ERROR is set (and option doesn't start with :), errors result in
1218 * a call to bi_errorf().
1220 * Non-standard features:
1221 * - ';' is like ':' in options, except the argument is optional
1222 * (if it isn't present, optarg is set to 0).
1223 * Used for 'set -o'.
1224 * - ',' is like ':' in options, except the argument always immediately
1225 * follows the option character (optarg is set to the null string if
1226 * the option is missing).
1227 * Used for 'read -u2', 'print -u2' and fc -40.
1228 * - '#' is like ':' in options, expect that the argument is optional
1229 * and must start with a digit. If the argument doesn't start with a
1230 * digit, it is assumed to be missing and normal option processing
1231 * continues (optarg is set to 0 if the option is missing).
1232 * Used for 'typeset -LZ4'.
1233 * - accepts +c as well as -c IF the GF_PLUSOPT flag is present. If an
1234 * option starting with + is accepted, the GI_PLUS flag will be set
1238 ksh_getopt(const char **argv, Getopt *go, const char *optionsp)
1243 if (go->p == 0 || (c = argv[go->optind - 1][go->p]) == '\0') {
1244 const char *arg = argv[go->optind], flag = arg ? *arg : '\0';
1247 if (flag == '-' && ksh_isdash(arg + 1)) {
1250 go->info |= GI_MINUSMINUS;
1255 /* neither a - nor a + (if + allowed) */
1256 (!(go->flags & GF_PLUSOPT) || flag != '+')) ||
1257 (c = arg[1]) == '\0') {
1262 go->info &= ~(GI_MINUS|GI_PLUS);
1263 go->info |= flag == '-' ? GI_MINUS : GI_PLUS;
1266 if (ctype(c, C_QUEST | C_COLON | C_HASH) || c == ';' || c == ',' ||
1267 !(o = cstrchr(optionsp, c))) {
1268 if (optionsp[0] == ':') {
1270 go->optarg = go->buf;
1272 warningf(true, Tf_optfoo,
1273 (go->flags & GF_NONAME) ? "" : argv[0],
1274 (go->flags & GF_NONAME) ? "" : Tcolsp,
1275 c, Tunknown_option);
1276 if (go->flags & GF_ERROR)
1282 * : means argument must be present, may be part of option argument
1283 * or the next argument
1284 * ; same as : but argument may be missing
1285 * , means argument is part of option argument, and may be null.
1287 if (*++o == ':' || *o == ';') {
1288 if (argv[go->optind - 1][go->p])
1289 go->optarg = argv[go->optind - 1] + go->p;
1290 else if (argv[go->optind])
1291 go->optarg = argv[go->optind++];
1295 if (optionsp[0] == ':') {
1297 go->optarg = go->buf;
1300 warningf(true, Tf_optfoo,
1301 (go->flags & GF_NONAME) ? "" : argv[0],
1302 (go->flags & GF_NONAME) ? "" : Tcolsp,
1304 if (go->flags & GF_ERROR)
1309 } else if (*o == ',') {
1310 /* argument is attached to option character, even if null */
1311 go->optarg = argv[go->optind - 1] + go->p;
1313 } else if (*o == '#') {
1315 * argument is optional and may be attached or unattached
1316 * but must start with a digit. optarg is set to 0 if the
1317 * argument is missing.
1319 if (argv[go->optind - 1][go->p]) {
1320 if (ctype(argv[go->optind - 1][go->p], C_DIGIT)) {
1321 go->optarg = argv[go->optind - 1] + go->p;
1326 if (argv[go->optind] &&
1327 ctype(argv[go->optind][0], C_DIGIT)) {
1328 go->optarg = argv[go->optind++];
1338 * print variable/alias value using necessary quotes
1339 * (POSIX says they should be suitable for re-entry...)
1340 * No trailing newline is printed.
1343 print_value_quoted(struct shf *shf, const char *s)
1346 const unsigned char *p = (const unsigned char *)s;
1347 bool inquote = true;
1349 /* first, check whether any quotes are needed */
1350 while (rtt2asc(c = *p++) >= 32)
1351 if (ctype(c, C_QUOTE | C_SPC))
1354 p = (const unsigned char *)s;
1357 /* nope, use the shortcut */
1362 /* otherwise, quote nicely via state machine */
1363 while ((c = *p++) != 0) {
1366 * multiple single quotes or any of them
1367 * at the beginning of a string look nicer
1368 * this way than when simply substituting
1371 shf_putc('\'', shf);
1374 shf_putc('\\', shf);
1375 } else if (!inquote) {
1376 shf_putc('\'', shf);
1385 /* use $'...' quote format */
1387 shf_putc('\'', shf);
1388 while ((c = *p) != 0) {
1391 n = utf_mbtowc(&wc, (const char *)p);
1392 if (n != (size_t)-1) {
1394 shf_fprintf(shf, "\\u%04X", wc);
1401 /* see unbksl() in this file for comments */
1431 /* take E not e because \e is \ in *roff */
1435 shf_putc('\\', shf);
1440 #if defined(MKSH_EBCDIC) || defined(MKSH_FAUX_EBCDIC)
1443 if (!ctype(c, C_PRINT))
1448 shf_fprintf(shf, "\\%03o", c);
1459 shf_putc('\'', shf);
1463 * Print things in columns and rows - func() is called to format
1467 print_columns(struct columnise_opts *opts, unsigned int n,
1468 void (*func)(char *, size_t, unsigned int, const void *),
1469 const void *arg, size_t max_oct, size_t max_colz)
1471 unsigned int i, r = 0, c, rows, cols, nspace, max_col;
1477 if (max_colz > 2147483646) {
1479 internal_warningf("print_columns called with %s=%zu >= INT_MAX",
1480 "max_col", max_colz);
1484 max_col = (unsigned int)max_colz;
1486 if (max_oct > 2147483646) {
1488 internal_warningf("print_columns called with %s=%zu >= INT_MAX",
1489 "max_oct", max_oct);
1494 str = alloc(max_oct, ATEMP);
1497 * We use (max_col + 2) to consider the separator space.
1498 * Note that no spaces are printed after the last column
1499 * to avoid problems with terminals that have auto-wrap,
1500 * but we need to also take this into account in x_cols.
1502 cols = (x_cols + 1) / (max_col + 2);
1504 /* if we can only print one column anyway, skip the goo */
1508 shf_putc(opts->linesep, opts->shf);
1510 (*func)(str, max_oct, r++, arg);
1511 shf_puts(str, opts->shf);
1516 rows = (n + cols - 1) / cols;
1517 if (opts->prefcol && cols > rows) {
1519 rows = (n + cols - 1) / cols;
1522 nspace = (x_cols - max_col * cols) / cols;
1528 shf_putchar(opts->linesep, opts->shf);
1530 for (c = 0; c < cols; c++) {
1531 if ((i = c * rows + r) >= n)
1533 (*func)(str, max_oct, i, arg);
1535 shf_puts(str, opts->shf);
1537 shf_fprintf(opts->shf, "%*s%*s",
1538 (int)max_col, str, (int)nspace, null);
1544 shf_putchar(opts->linesep, opts->shf);
1548 /* strip all NUL bytes from buf; output is NUL-terminated if stripped */
1550 strip_nuls(char *buf, size_t len)
1554 if (!len || !(dp = memchr(buf, '\0', len)))
1561 while (cp++ < ep && *cp == '\0')
1563 while (cp < ep && *cp != '\0')
1566 goto cp_has_nul_byte;
1572 * Like read(2), but if read fails due to non-blocking flag,
1573 * resets flag and restarts read.
1576 blocking_read(int fd, char *buf, size_t nbytes)
1579 bool tried_reset = false;
1581 while ((ret = read(fd, buf, nbytes)) < 0) {
1582 if (!tried_reset && errno == EAGAIN) {
1583 if (reset_nonblock(fd) > 0) {
1595 * Reset the non-blocking flag on the specified file descriptor.
1596 * Returns -1 if there was an error, 0 if non-blocking wasn't set,
1600 reset_nonblock(int fd)
1604 if ((flags = fcntl(fd, F_GETFL, 0)) < 0)
1606 if (!(flags & O_NONBLOCK))
1608 flags &= ~O_NONBLOCK;
1609 if (fcntl(fd, F_SETFL, flags) < 0)
1614 /* getcwd(3) equivalent, allocates from ATEMP but doesn't resize */
1618 #ifdef MKSH__NO_PATH_MAX
1621 if ((cp = get_current_dir_name())) {
1622 strdupx(rv, cp, ATEMP);
1629 if (!getcwd((rv = alloc(PATH_MAX + 1, ATEMP)), PATH_MAX)) {
1643 do_realpath(const char *upath)
1645 char *xp, *ip, *tp, *ipath, *ldest = NULL;
1650 #ifdef MKSH__NO_PATH_MAX
1651 size_t ldestlen = 0;
1652 #define pathlen sb.st_size
1653 #define pathcnd (ldestlen < (pathlen + 1))
1655 #define pathlen PATH_MAX
1656 #define pathcnd (!ldest)
1658 /* max. recursion depth */
1661 if (mksh_abspath(upath)) {
1662 /* upath is an absolute pathname */
1663 strdupx(ipath, upath, ATEMP);
1665 /* upath is a relative pathname, prepend cwd */
1666 if ((tp = ksh_get_wd()) == NULL || !mksh_abspath(tp))
1668 ipath = shf_smprintf(Tf_sss, tp, "/", upath);
1672 /* ipath and upath are in memory at the same time -> unchecked */
1673 Xinit(xs, xp, strlen(ip = ipath) + 1, ATEMP);
1675 /* now jump into the deep of the loop */
1676 goto beginning_of_a_pathname;
1679 /* skip slashes in input */
1680 while (mksh_cdirsep(*ip))
1685 /* get next pathname component from input */
1687 while (*ip && !mksh_cdirsep(*ip))
1691 /* check input for "." and ".." */
1694 /* just continue with the next one */
1696 else if (len == 2 && tp[1] == '.') {
1697 /* strip off last pathname component */
1698 while (xp > Xstring(xs, xp))
1699 if (mksh_cdirsep(*--xp))
1701 /* then continue with the next one */
1706 /* store output position away, then append slash to output */
1707 pos = Xsavepos(xs, xp);
1708 /* 1 for the '/' and len + 1 for tp and the NUL from below */
1709 XcheckN(xs, xp, 1 + len + 1);
1712 /* append next pathname component to output */
1713 memcpy(xp, tp, len);
1717 /* lstat the current output, see if it's a symlink */
1718 if (mksh_lstat(Xstring(xs, xp), &sb)) {
1720 if (errno == ENOENT) {
1721 /* because the pathname does not exist */
1722 while (mksh_cdirsep(*ip))
1723 /* skip any trailing slashes */
1725 /* no more components left? */
1727 /* we can still return successfully */
1729 /* more components left? fall through */
1731 /* not ENOENT or not at the end of ipath */
1735 /* check if we encountered a symlink? */
1736 if (S_ISLNK(sb.st_mode)) {
1737 #ifndef MKSH__NO_SYMLINK
1738 /* reached maximum recursion depth? */
1740 /* yep, prevent infinite loops */
1745 /* get symlink(7) target */
1747 #ifdef MKSH__NO_PATH_MAX
1748 if (notoktoadd(pathlen, 1)) {
1749 errno = ENAMETOOLONG;
1753 ldest = aresize(ldest, pathlen + 1, ATEMP);
1755 llen = readlink(Xstring(xs, xp), ldest, pathlen);
1762 * restart if symlink target is an absolute path,
1763 * otherwise continue with currently resolved prefix
1765 /* append rest of current input path to link target */
1766 tp = shf_smprintf(Tf_sss, ldest, *ip ? "/" : "", ip);
1767 afree(ipath, ATEMP);
1769 if (!mksh_abspath(ldest)) {
1770 /* symlink target is a relative path */
1771 xp = Xrestpos(xs, xp, pos);
1775 /* symlink target is an absolute path */
1776 xp = Xstring(xs, xp);
1777 beginning_of_a_pathname:
1778 /* assert: mksh_cdirsep((ip == ipath)[0]) */
1779 /* assert: xp == xs.beg => start of path */
1781 /* exactly two leading slashes? (SUSv4 3.266) */
1782 if (ip[1] == ip[0] && !mksh_cdirsep(ip[2])) {
1783 /* keep them, e.g. for UNC pathnames */
1788 /* otherwise (no symlink) merely go on */
1792 * either found the target and successfully resolved it,
1793 * or found its parent directory and may create it
1795 if (Xlength(xs, xp) == 0)
1797 * if the resolved pathname is "", make it "/",
1798 * otherwise do not add a trailing slash
1804 * if source path had a trailing slash, check if target path
1805 * is not a non-directory existing file
1807 if (ip > ipath && mksh_cdirsep(ip[-1])) {
1808 if (stat(Xstring(xs, xp), &sb)) {
1809 if (errno != ENOENT)
1811 } else if (!S_ISDIR(sb.st_mode)) {
1815 /* target now either does not exist or is a directory */
1818 /* return target path */
1819 afree(ldest, ATEMP);
1820 afree(ipath, ATEMP);
1821 return (Xclose(xs, xp));
1824 /* save; freeing memory might trash it */
1826 afree(ldest, ATEMP);
1827 afree(ipath, ATEMP);
1837 * Makes a filename into result using the following algorithm.
1838 * - make result NULL
1839 * - if file starts with '/', append file to result & set cdpathp to NULL
1840 * - if file starts with ./ or ../ append cwd and file to result
1841 * and set cdpathp to NULL
1842 * - if the first element of cdpathp doesnt start with a '/' xx or '.' xx
1843 * then cwd is appended to result.
1844 * - the first element of cdpathp is appended to result
1845 * - file is appended to result
1846 * - cdpathp is set to the start of the next element in cdpathp (or NULL
1847 * if there are no more elements.
1848 * The return value indicates whether a non-null element from cdpathp
1849 * was appended to result.
1852 make_path(const char *cwd, const char *file,
1853 /* pointer to colon-separated list */
1859 bool use_cdpath = true;
1861 size_t len, plen = 0;
1862 char *xp = Xstring(*xsp, xp);
1867 if (mksh_abspath(file)) {
1871 if (file[0] == '.') {
1876 if (mksh_cdirsep(c) || c == '\0')
1883 else if (use_cdpath) {
1886 while (*pend && *pend != MKSH_PATHSEPC)
1888 plen = pend - plist;
1889 *cdpathp = *pend ? pend + 1 : NULL;
1892 if ((!use_cdpath || !plen || !mksh_abspath(plist)) &&
1895 XcheckN(*xsp, xp, len);
1896 memcpy(xp, cwd, len);
1898 if (!mksh_cdirsep(cwd[len - 1]))
1899 Xput(*xsp, xp, '/');
1901 *phys_pathp = Xlength(*xsp, xp);
1902 if (use_cdpath && plen) {
1903 XcheckN(*xsp, xp, plen);
1904 memcpy(xp, plist, plen);
1906 if (!mksh_cdirsep(plist[plen - 1]))
1907 Xput(*xsp, xp, '/');
1912 len = strlen(file) + 1;
1913 XcheckN(*xsp, xp, len);
1914 memcpy(xp, file, len);
1923 * Simplify pathnames containing "." and ".." entries.
1925 * simplify_path(this) = that
1926 * /a/b/c/./../d/.. /a/b
1927 * //./C/foo/bar/../baz //C/foo/baz
1929 * /foo/../../bar /bar
1930 * /foo/./blah/.. /foo
1934 * foo/../../../bar ../../bar
1937 simplify_path(char *p)
1939 char *dp, *ip, *sp, *tp;
1950 /* exactly two leading slashes? (SUSv4 3.266) */
1951 if (p[1] == p[0] && !mksh_cdirsep(p[2]))
1952 /* keep them, e.g. for UNC pathnames */
1962 /* skip slashes in input */
1963 while (mksh_cdirsep(*ip))
1968 /* get next pathname component from input */
1970 while (*ip && !mksh_cdirsep(*ip))
1974 /* check input for "." and ".." */
1977 /* just continue with the next one */
1979 else if (len == 2 && tp[1] == '.') {
1980 /* parent level, but how? */
1981 if (mksh_abspath(p))
1982 /* absolute path, only one way */
1983 goto strip_last_component;
1985 /* relative path, with subpaths */
1987 strip_last_component:
1988 /* strip off last pathname component */
1990 if (mksh_cdirsep(*--dp))
1993 /* relative path, at its beginning */
1995 /* or already dotdot-slash'd */
1997 /* keep dotdot-slash if not absolute */
2003 /* then continue with the next one */
2011 /* append next pathname component to output */
2012 memmove(dp, tp, len);
2015 /* append slash if we continue */
2017 /* try next component */
2020 /* empty path -> dot */
2021 *dp++ = needslash ? '/' : '.';
2026 set_current_wd(const char *nwd)
2028 char *allocd = NULL;
2031 allocd = ksh_get_wd();
2032 nwd = allocd ? allocd : null;
2035 afree(current_wd, APERM);
2036 strdupx(current_wd, nwd, APERM);
2038 afree(allocd, ATEMP);
2042 c_cd(const char **wp)
2044 int optc, rv, phys_path;
2045 bool physical = tobool(Flag(FPHYSICAL));
2046 /* was a node from cdpath added in? */
2048 /* show where we went?, error for $PWD */
2049 bool printpath = false, eflag = false;
2050 struct tbl *pwd_s, *oldpwd_s;
2052 char *dir, *allocd = NULL, *tryp, *pwd, *cdpath;
2054 while ((optc = ksh_getopt(wp, &builtin_opt, "eLP")) != -1)
2068 wp += builtin_opt.optind;
2070 if (Flag(FRESTRICTED)) {
2071 bi_errorf(Tcant_cd);
2075 pwd_s = global(TPWD);
2076 oldpwd_s = global(TOLDPWD);
2079 /* No arguments - go home */
2080 if ((dir = str_val(global("HOME"))) == null) {
2081 bi_errorf("no home directory (HOME not set)");
2084 } else if (!wp[1]) {
2085 /* One argument: - or dir */
2086 strdupx(allocd, wp[0], ATEMP);
2087 if (ksh_isdash((dir = allocd))) {
2088 afree(allocd, ATEMP);
2090 dir = str_val(oldpwd_s);
2092 bi_errorf(Tno_OLDPWD);
2097 } else if (!wp[2]) {
2098 /* Two arguments - substitute arg1 in PWD for arg2 */
2099 size_t ilen, olen, nlen, elen;
2102 if (!current_wd[0]) {
2103 bi_errorf("can't determine current directory");
2107 * substitute arg1 for arg2 in current path.
2108 * if the first substitution fails because the cd fails
2109 * we could try to find another substitution. For now
2112 if ((cp = strstr(current_wd, wp[0])) == NULL) {
2113 bi_errorf(Tbadsubst);
2117 * ilen = part of current_wd before wp[0]
2118 * elen = part of current_wd after wp[0]
2119 * because current_wd and wp[1] need to be in memory at the
2120 * same time beforehand the addition can stay unchecked
2122 ilen = cp - current_wd;
2123 olen = strlen(wp[0]);
2124 nlen = strlen(wp[1]);
2125 elen = strlen(current_wd + ilen + olen) + 1;
2126 dir = allocd = alloc(ilen + nlen + elen, ATEMP);
2127 memcpy(dir, current_wd, ilen);
2128 memcpy(dir + ilen, wp[1], nlen);
2129 memcpy(dir + ilen + nlen, current_wd + ilen + olen, elen);
2132 bi_errorf(Ttoo_many_args);
2136 #ifdef MKSH__NO_PATH_MAX
2137 /* only a first guess; make_path will enlarge xs if necessary */
2138 XinitN(xs, 1024, ATEMP);
2140 XinitN(xs, PATH_MAX, ATEMP);
2143 cdpath = str_val(global("CDPATH"));
2145 cdnode = make_path(current_wd, dir, &cdpath, &xs, &phys_path);
2147 rv = chdir(tryp = Xstring(xs, xp) + phys_path);
2149 simplify_path(Xstring(xs, xp));
2150 rv = chdir(tryp = Xstring(xs, xp));
2152 } while (rv < 0 && cdpath != NULL);
2156 bi_errorf(Tf_sD_s, dir, "bad directory");
2158 bi_errorf(Tf_sD_s, tryp, cstrerror(errno));
2159 afree(allocd, ATEMP);
2166 /* allocd (above) => dir, which is no longer used */
2167 afree(allocd, ATEMP);
2170 /* Clear out tracked aliases with relative paths */
2174 * Set OLDPWD (note: unsetting OLDPWD does not disable this
2175 * setting in AT&T ksh)
2178 /* Ignore failure (happens if readonly or integer) */
2179 setstr(oldpwd_s, current_wd, KSH_RETURN_ERROR);
2181 if (!mksh_abspath(Xstring(xs, xp))) {
2183 } else if (!physical) {
2184 goto norealpath_PWD;
2185 } else if ((pwd = allocd = do_realpath(Xstring(xs, xp))) == NULL) {
2189 pwd = Xstring(xs, xp);
2196 set_current_wd(ptmp);
2197 /* Ignore failure (happens if readonly or integer) */
2198 setstr(pwd_s, ptmp, KSH_RETURN_ERROR);
2200 set_current_wd(null);
2201 pwd = Xstring(xs, xp);
2202 /* XXX unset $PWD? */
2206 if (printpath || cdnode)
2207 shprintf(Tf_sN, pwd);
2209 afree(allocd, ATEMP);
2215 #ifdef KSH_CHVT_CODE
2216 extern void chvt_reinit(void);
2219 chvt(const Getopt *go)
2221 const char *dv = go->optarg;
2235 if (stat(dv, &sb)) {
2236 cp = shf_smprintf("/dev/ttyC%s", dv);
2238 if (stat(dv, &sb)) {
2239 memmove(cp + 1, cp, /* /dev/tty */ 8);
2241 if (stat(dv, &sb)) {
2242 errorf(Tf_sD_sD_s, "chvt",
2243 "can't find tty", go->optarg);
2247 if (!(sb.st_mode & S_IFCHR))
2248 errorf(Tf_sD_sD_s, "chvt", "not a char device", dv);
2249 #ifndef MKSH_DISABLE_REVOKE_WARNING
2253 warningf(false, Tf_sD_s_s, "chvt",
2254 "new shell is potentially insecure, can't revoke",
2259 if ((fd = binopen2(dv, O_RDWR)) < 0) {
2261 if ((fd = binopen2(dv, O_RDWR)) < 0) {
2262 errorf(Tf_sD_s_s, "chvt", Tcant_open, dv);
2265 if (go->optarg[0] != '!') {
2268 errorf(Tf_sD_s_s, "chvt", "fork", "failed");
2276 errorf(Tf_sD_s_s, "chvt", "setsid", "failed");
2277 if (go->optarg[0] != '-') {
2278 if (ioctl(fd, TIOCSCTTY, NULL) == -1)
2279 errorf(Tf_sD_s_s, "chvt", "TIOCSCTTY", "failed");
2280 if (tcflush(fd, TCIOFLUSH))
2281 errorf(Tf_sD_s_s, "chvt", "TCIOFLUSH", "failed");
2283 ksh_dup2(fd, 0, false);
2284 ksh_dup2(fd, 1, false);
2285 ksh_dup2(fd, 2, false);
2288 rndset((unsigned long)chvt_rndsetup(go, sizeof(Getopt)));
2295 strchr(char *p, int ch)
2307 strstr(char *b, const char *l)
2312 if ((first = *l++) == '\0')
2316 while ((c = *b++) != first)
2319 if (strncmp(b, l, n))
2325 #if defined(MKSH_SMALL) && !defined(MKSH_SMALL_BUT_FAST)
2327 strndup_i(const char *src, size_t len, Area *ap)
2332 dst = alloc(len + 1, ap);
2333 memcpy(dst, src, len);
2340 strdup_i(const char *src, Area *ap)
2342 return (src == NULL ? NULL : strndup_i(src, strlen(src), ap));
2347 #define INVTCK(r,t) do { \
2348 r.tv_usec = ((t) % (1000000 / CLK_TCK)) * (1000000 / CLK_TCK); \
2349 r.tv_sec = (t) / CLK_TCK; \
2350 } while (/* CONSTCOND */ 0)
2353 getrusage(int what, struct rusage *ru)
2358 if (/* ru == NULL || */ times(&tms) == (clock_t)-1)
2366 case RUSAGE_CHILDREN:
2374 INVTCK(ru->ru_utime, u);
2375 INVTCK(ru->ru_stime, s);
2381 * process the string available via fg (get a char)
2382 * and fp (put back a char) for backslash escapes,
2383 * assuming the first call to *fg gets the char di-
2384 * rectly after the backslash; return the character
2385 * (0..0xFF), Unicode (wc + 0x100), or -1 if no known
2386 * escape sequence was found
2389 unbksl(bool cstyle, int (*fg)(void), void (*fp)(int))
2391 int wc, i, c, fc, n;
2403 goto unknown_escape;
2434 goto unknown_escape;
2440 * look for an octal number with up to three
2441 * digits, not counting the leading zero;
2442 * convert it to a raw octet
2447 if (ctype((c = (*fg)()), C_OCTAL))
2448 wc = (wc << 3) + ksh_numdig(c);
2456 if (/* CONSTCOND */ 0)
2460 if (/* CONSTCOND */ 0)
2463 i = cstyle ? -1 : 2;
2465 * x: look for a hexadecimal number with up to
2466 * two (C style: arbitrary) digits; convert
2467 * to raw octet (C style: Unicode if >0xFF)
2468 * u/U: look for a hexadecimal number with up to
2469 * four (U: eight) digits; convert to Unicode
2473 while (n < i || i == -1) {
2475 if (!ctype((c = (*fg)()), C_SEDEC)) {
2480 if (ctype(c, C_DIGIT))
2481 wc += ksh_numdig(c);
2482 else if (ctype(c, C_UPPER))
2483 wc += ksh_numuc(c) + 10;
2485 wc += ksh_numlc(c) + 10;
2489 goto unknown_escape;
2490 if ((cstyle && wc > 0xFF) || fc != 'x')
2491 /* Unicode marker */
2496 goto unknown_escape;