OSDN Git Service

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