OSDN Git Service

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