OSDN Git Service

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