OSDN Git Service

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