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.293 2018/08/10 02:53:35 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 *
Xcheck_grow(XString * xsp,const char * xp,size_t more)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
option(const char * n)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
options_fmt_entry(char * buf,size_t buflen,unsigned int i,const void * arg)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
printoptions(bool verbose)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 *
getoptions(void)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
change_flag(enum sh_flag f,int what,bool newset)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
change_xtrace(unsigned char newval,bool dosnapshot)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
parse_args(const char ** argv,int what,bool * setargsp)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
getn(const char * s,int * ai)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 *
simplify_gmatch_pattern(const unsigned char * sp)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
gmatchx(const char * s,const char * p,bool isfile)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
has_globbing(const char * pat)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
do_gmatch(const unsigned char * s,const unsigned char * se,const unsigned char * p,const unsigned char * pe,const unsigned char * smin)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 ORD('+') | 0x80:
842 /* matches zero or more times */
843 case ORD('*') | 0x80:
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 ORD('?') | 0x80:
867 /* matches one of the patterns */
868 case ORD('@') | 0x80:
869 /* simile for @ */
870 case ORD(' ') | 0x80:
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 ORD('!') | 0x80:
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 *
gmatch_cclass(const unsigned char * pat,unsigned char sc)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 *
pat_scan(const unsigned char * p,const unsigned char * pe,bool match_sep)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
ascstrcmp(const void * s1,const void * s2)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
ascpstrcmp(const void * pstr1,const void * pstr2)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
ksh_getopt_reset(Getopt * go,int flags)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
ksh_getopt(const char ** argv,Getopt * go,const char * optionsp)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
print_value_quoted(struct shf * shf,const char * s)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, special-case empty strings (for re-entrancy) */
1350 if (!*s) {
1351 shf_putc('\'', shf);
1352 shf_putc('\'', shf);
1353 return;
1354 }
1355
1356 /* non-empty; check whether any quotes are needed */
1357 while (rtt2asc(c = *p++) >= 32)
1358 if (ctype(c, C_QUOTE | C_SPC))
1359 inquote = false;
1360
1361 p = (const unsigned char *)s;
1362 if (c == 0) {
1363 if (inquote) {
1364 /* nope, use the shortcut */
1365 shf_puts(s, shf);
1366 return;
1367 }
1368
1369 /* otherwise, quote nicely via state machine */
1370 while ((c = *p++) != 0) {
1371 if (c == '\'') {
1372 /*
1373 * multiple single quotes or any of them
1374 * at the beginning of a string look nicer
1375 * this way than when simply substituting
1376 */
1377 if (inquote) {
1378 shf_putc('\'', shf);
1379 inquote = false;
1380 }
1381 shf_putc('\\', shf);
1382 } else if (!inquote) {
1383 shf_putc('\'', shf);
1384 inquote = true;
1385 }
1386 shf_putc(c, shf);
1387 }
1388 } else {
1389 unsigned int wc;
1390 size_t n;
1391
1392 /* use $'...' quote format */
1393 shf_putc('$', shf);
1394 shf_putc('\'', shf);
1395 while ((c = *p) != 0) {
1396 #ifndef MKSH_EBCDIC
1397 if (c >= 0xC2) {
1398 n = utf_mbtowc(&wc, (const char *)p);
1399 if (n != (size_t)-1) {
1400 p += n;
1401 shf_fprintf(shf, "\\u%04X", wc);
1402 continue;
1403 }
1404 }
1405 #endif
1406 ++p;
1407 switch (c) {
1408 /* see unbksl() in this file for comments */
1409 case KSH_BEL:
1410 c = 'a';
1411 if (0)
1412 /* FALLTHROUGH */
1413 case '\b':
1414 c = 'b';
1415 if (0)
1416 /* FALLTHROUGH */
1417 case '\f':
1418 c = 'f';
1419 if (0)
1420 /* FALLTHROUGH */
1421 case '\n':
1422 c = 'n';
1423 if (0)
1424 /* FALLTHROUGH */
1425 case '\r':
1426 c = 'r';
1427 if (0)
1428 /* FALLTHROUGH */
1429 case '\t':
1430 c = 't';
1431 if (0)
1432 /* FALLTHROUGH */
1433 case KSH_VTAB:
1434 c = 'v';
1435 if (0)
1436 /* FALLTHROUGH */
1437 case KSH_ESC:
1438 /* take E not e because \e is \ in *roff */
1439 c = 'E';
1440 /* FALLTHROUGH */
1441 case '\\':
1442 shf_putc('\\', shf);
1443
1444 if (0)
1445 /* FALLTHROUGH */
1446 default:
1447 #if defined(MKSH_EBCDIC) || defined(MKSH_FAUX_EBCDIC)
1448 if (ksh_isctrl(c))
1449 #else
1450 if (!ctype(c, C_PRINT))
1451 #endif
1452 {
1453 /* FALLTHROUGH */
1454 case '\'':
1455 shf_fprintf(shf, "\\%03o", c);
1456 break;
1457 }
1458
1459 shf_putc(c, shf);
1460 break;
1461 }
1462 }
1463 inquote = true;
1464 }
1465 if (inquote)
1466 shf_putc('\'', shf);
1467 }
1468
1469 /*
1470 * Print things in columns and rows - func() is called to format
1471 * the i-th element
1472 */
1473 void
print_columns(struct columnise_opts * opts,unsigned int n,void (* func)(char *,size_t,unsigned int,const void *),const void * arg,size_t max_oct,size_t max_colz)1474 print_columns(struct columnise_opts *opts, unsigned int n,
1475 void (*func)(char *, size_t, unsigned int, const void *),
1476 const void *arg, size_t max_oct, size_t max_colz)
1477 {
1478 unsigned int i, r = 0, c, rows, cols, nspace, max_col;
1479 char *str;
1480
1481 if (!n)
1482 return;
1483
1484 if (max_colz > 2147483646) {
1485 #ifndef MKSH_SMALL
1486 internal_warningf("print_columns called with %s=%zu >= INT_MAX",
1487 "max_col", max_colz);
1488 #endif
1489 return;
1490 }
1491 max_col = (unsigned int)max_colz;
1492
1493 if (max_oct > 2147483646) {
1494 #ifndef MKSH_SMALL
1495 internal_warningf("print_columns called with %s=%zu >= INT_MAX",
1496 "max_oct", max_oct);
1497 #endif
1498 return;
1499 }
1500 ++max_oct;
1501 str = alloc(max_oct, ATEMP);
1502
1503 /*
1504 * We use (max_col + 2) to consider the separator space.
1505 * Note that no spaces are printed after the last column
1506 * to avoid problems with terminals that have auto-wrap,
1507 * but we need to also take this into account in x_cols.
1508 */
1509 cols = (x_cols + 1) / (max_col + 2);
1510
1511 /* if we can only print one column anyway, skip the goo */
1512 if (cols < 2) {
1513 goto prcols_easy;
1514 while (r < n) {
1515 shf_putc(opts->linesep, opts->shf);
1516 prcols_easy:
1517 (*func)(str, max_oct, r++, arg);
1518 shf_puts(str, opts->shf);
1519 }
1520 goto out;
1521 }
1522
1523 rows = (n + cols - 1) / cols;
1524 if (opts->prefcol && cols > rows) {
1525 cols = rows;
1526 rows = (n + cols - 1) / cols;
1527 }
1528
1529 nspace = (x_cols - max_col * cols) / cols;
1530 if (nspace < 2)
1531 nspace = 2;
1532 max_col = -max_col;
1533 goto prcols_hard;
1534 while (r < rows) {
1535 shf_putchar(opts->linesep, opts->shf);
1536 prcols_hard:
1537 for (c = 0; c < cols; c++) {
1538 if ((i = c * rows + r) >= n)
1539 break;
1540 (*func)(str, max_oct, i, arg);
1541 if (i + rows >= n)
1542 shf_puts(str, opts->shf);
1543 else
1544 shf_fprintf(opts->shf, "%*s%*s",
1545 (int)max_col, str, (int)nspace, null);
1546 }
1547 ++r;
1548 }
1549 out:
1550 if (opts->do_last)
1551 shf_putchar(opts->linesep, opts->shf);
1552 afree(str, ATEMP);
1553 }
1554
1555 /* strip all NUL bytes from buf; output is NUL-terminated if stripped */
1556 void
strip_nuls(char * buf,size_t len)1557 strip_nuls(char *buf, size_t len)
1558 {
1559 char *cp, *dp, *ep;
1560
1561 if (!len || !(dp = memchr(buf, '\0', len)))
1562 return;
1563
1564 ep = buf + len;
1565 cp = dp;
1566
1567 cp_has_nul_byte:
1568 while (cp++ < ep && *cp == '\0')
1569 ; /* nothing */
1570 while (cp < ep && *cp != '\0')
1571 *dp++ = *cp++;
1572 if (cp < ep)
1573 goto cp_has_nul_byte;
1574
1575 *dp = '\0';
1576 }
1577
1578 /*
1579 * Like read(2), but if read fails due to non-blocking flag,
1580 * resets flag and restarts read.
1581 */
1582 ssize_t
blocking_read(int fd,char * buf,size_t nbytes)1583 blocking_read(int fd, char *buf, size_t nbytes)
1584 {
1585 ssize_t ret;
1586 bool tried_reset = false;
1587
1588 while ((ret = read(fd, buf, nbytes)) < 0) {
1589 if (!tried_reset && errno == EAGAIN) {
1590 if (reset_nonblock(fd) > 0) {
1591 tried_reset = true;
1592 continue;
1593 }
1594 errno = EAGAIN;
1595 }
1596 break;
1597 }
1598 return (ret);
1599 }
1600
1601 /*
1602 * Reset the non-blocking flag on the specified file descriptor.
1603 * Returns -1 if there was an error, 0 if non-blocking wasn't set,
1604 * 1 if it was.
1605 */
1606 int
reset_nonblock(int fd)1607 reset_nonblock(int fd)
1608 {
1609 int flags;
1610
1611 if ((flags = fcntl(fd, F_GETFL, 0)) < 0)
1612 return (-1);
1613 if (!(flags & O_NONBLOCK))
1614 return (0);
1615 flags &= ~O_NONBLOCK;
1616 if (fcntl(fd, F_SETFL, flags) < 0)
1617 return (-1);
1618 return (1);
1619 }
1620
1621 /* getcwd(3) equivalent, allocates from ATEMP but doesn't resize */
1622 char *
ksh_get_wd(void)1623 ksh_get_wd(void)
1624 {
1625 #ifdef MKSH__NO_PATH_MAX
1626 char *rv, *cp;
1627
1628 if ((cp = get_current_dir_name())) {
1629 strdupx(rv, cp, ATEMP);
1630 free_gnu_gcdn(cp);
1631 } else
1632 rv = NULL;
1633 #else
1634 char *rv;
1635
1636 if (!getcwd((rv = alloc(PATH_MAX + 1, ATEMP)), PATH_MAX)) {
1637 afree(rv, ATEMP);
1638 rv = NULL;
1639 }
1640 #endif
1641
1642 return (rv);
1643 }
1644
1645 #ifndef ELOOP
1646 #define ELOOP E2BIG
1647 #endif
1648
1649 char *
do_realpath(const char * upath)1650 do_realpath(const char *upath)
1651 {
1652 char *xp, *ip, *tp, *ipath, *ldest = NULL;
1653 XString xs;
1654 size_t pos, len;
1655 int llen;
1656 struct stat sb;
1657 #ifdef MKSH__NO_PATH_MAX
1658 size_t ldestlen = 0;
1659 #define pathlen sb.st_size
1660 #define pathcnd (ldestlen < (pathlen + 1))
1661 #else
1662 #define pathlen PATH_MAX
1663 #define pathcnd (!ldest)
1664 #endif
1665 /* max. recursion depth */
1666 int symlinks = 32;
1667
1668 if (mksh_abspath(upath)) {
1669 /* upath is an absolute pathname */
1670 strdupx(ipath, upath, ATEMP);
1671 #ifdef MKSH_DOSPATH
1672 } else if (mksh_drvltr(upath)) {
1673 /* upath is a drive-relative pathname */
1674 if (getdrvwd(&ldest, ord(*upath)))
1675 return (NULL);
1676 /* A:foo -> A:/cwd/foo; A: -> A:/cwd */
1677 ipath = shf_smprintf(Tf_sss, ldest,
1678 upath[2] ? "/" : "", upath + 2);
1679 #endif
1680 } else {
1681 /* upath is a relative pathname, prepend cwd */
1682 if ((tp = ksh_get_wd()) == NULL || !mksh_abspath(tp))
1683 return (NULL);
1684 ipath = shf_smprintf(Tf_sss, tp, "/", upath);
1685 afree(tp, ATEMP);
1686 }
1687
1688 /* ipath and upath are in memory at the same time -> unchecked */
1689 Xinit(xs, xp, strlen(ip = ipath) + 1, ATEMP);
1690
1691 /* now jump into the deep of the loop */
1692 goto beginning_of_a_pathname;
1693
1694 while (*ip) {
1695 /* skip slashes in input */
1696 while (mksh_cdirsep(*ip))
1697 ++ip;
1698 if (!*ip)
1699 break;
1700
1701 /* get next pathname component from input */
1702 tp = ip;
1703 while (*ip && !mksh_cdirsep(*ip))
1704 ++ip;
1705 len = ip - tp;
1706
1707 /* check input for "." and ".." */
1708 if (tp[0] == '.') {
1709 if (len == 1)
1710 /* just continue with the next one */
1711 continue;
1712 else if (len == 2 && tp[1] == '.') {
1713 /* strip off last pathname component */
1714 /*XXX consider a rooted pathname */
1715 while (xp > Xstring(xs, xp))
1716 if (mksh_cdirsep(*--xp))
1717 break;
1718 /* then continue with the next one */
1719 continue;
1720 }
1721 }
1722
1723 /* store output position away, then append slash to output */
1724 pos = Xsavepos(xs, xp);
1725 /* 1 for the '/' and len + 1 for tp and the NUL from below */
1726 XcheckN(xs, xp, 1 + len + 1);
1727 Xput(xs, xp, '/');
1728
1729 /* append next pathname component to output */
1730 memcpy(xp, tp, len);
1731 xp += len;
1732 *xp = '\0';
1733
1734 /* lstat the current output, see if it's a symlink */
1735 if (mksh_lstat(Xstring(xs, xp), &sb)) {
1736 /* lstat failed */
1737 if (errno == ENOENT) {
1738 /* because the pathname does not exist */
1739 while (mksh_cdirsep(*ip))
1740 /* skip any trailing slashes */
1741 ++ip;
1742 /* no more components left? */
1743 if (!*ip)
1744 /* we can still return successfully */
1745 break;
1746 /* more components left? fall through */
1747 }
1748 /* not ENOENT or not at the end of ipath */
1749 goto notfound;
1750 }
1751
1752 /* check if we encountered a symlink? */
1753 if (S_ISLNK(sb.st_mode)) {
1754 #ifndef MKSH__NO_SYMLINK
1755 /* reached maximum recursion depth? */
1756 if (!symlinks--) {
1757 /* yep, prevent infinite loops */
1758 errno = ELOOP;
1759 goto notfound;
1760 }
1761
1762 /* get symlink(7) target */
1763 if (pathcnd) {
1764 #ifdef MKSH__NO_PATH_MAX
1765 if (notoktoadd(pathlen, 1)) {
1766 errno = ENAMETOOLONG;
1767 goto notfound;
1768 }
1769 #endif
1770 ldest = aresize(ldest, pathlen + 1, ATEMP);
1771 }
1772 llen = readlink(Xstring(xs, xp), ldest, pathlen);
1773 if (llen < 0)
1774 /* oops... */
1775 goto notfound;
1776 ldest[llen] = '\0';
1777
1778 /*
1779 * restart if symlink target is an absolute path,
1780 * otherwise continue with currently resolved prefix
1781 */
1782 #ifdef MKSH_DOSPATH
1783 assemble_symlink:
1784 #endif
1785 /* append rest of current input path to link target */
1786 tp = shf_smprintf(Tf_sss, ldest, *ip ? "/" : "", ip);
1787 afree(ipath, ATEMP);
1788 ip = ipath = tp;
1789 if (!mksh_abspath(ipath)) {
1790 #ifdef MKSH_DOSPATH
1791 /* symlink target might be drive-relative */
1792 if (mksh_drvltr(ipath)) {
1793 if (getdrvwd(&ldest, ord(*ipath)))
1794 goto notfound;
1795 ip += 2;
1796 goto assemble_symlink;
1797 }
1798 #endif
1799 /* symlink target is a relative path */
1800 xp = Xrestpos(xs, xp, pos);
1801 } else
1802 #endif
1803 {
1804 /* symlink target is an absolute path */
1805 xp = Xstring(xs, xp);
1806 beginning_of_a_pathname:
1807 /* assert: mksh_abspath(ip == ipath) */
1808 /* assert: xp == xs.beg => start of path */
1809
1810 /* exactly two leading slashes? (SUSv4 3.266) */
1811 if (ip[1] == ip[0] && !mksh_cdirsep(ip[2])) {
1812 /* keep them, e.g. for UNC pathnames */
1813 Xput(xs, xp, '/');
1814 }
1815 #ifdef MKSH_DOSPATH
1816 /* drive letter? */
1817 if (mksh_drvltr(ip)) {
1818 /* keep it */
1819 Xput(xs, xp, *ip++);
1820 Xput(xs, xp, *ip++);
1821 }
1822 #endif
1823 }
1824 }
1825 /* otherwise (no symlink) merely go on */
1826 }
1827
1828 /*
1829 * either found the target and successfully resolved it,
1830 * or found its parent directory and may create it
1831 */
1832 if (Xlength(xs, xp) == 0)
1833 /*
1834 * if the resolved pathname is "", make it "/",
1835 * otherwise do not add a trailing slash
1836 */
1837 Xput(xs, xp, '/');
1838 Xput(xs, xp, '\0');
1839
1840 /*
1841 * if source path had a trailing slash, check if target path
1842 * is not a non-directory existing file
1843 */
1844 if (ip > ipath && mksh_cdirsep(ip[-1])) {
1845 if (stat(Xstring(xs, xp), &sb)) {
1846 if (errno != ENOENT)
1847 goto notfound;
1848 } else if (!S_ISDIR(sb.st_mode)) {
1849 errno = ENOTDIR;
1850 goto notfound;
1851 }
1852 /* target now either does not exist or is a directory */
1853 }
1854
1855 /* return target path */
1856 afree(ldest, ATEMP);
1857 afree(ipath, ATEMP);
1858 return (Xclose(xs, xp));
1859
1860 notfound:
1861 /* save; freeing memory might trash it */
1862 llen = errno;
1863 afree(ldest, ATEMP);
1864 afree(ipath, ATEMP);
1865 Xfree(xs, xp);
1866 errno = llen;
1867 return (NULL);
1868
1869 #undef pathlen
1870 #undef pathcnd
1871 }
1872
1873 /**
1874 * Makes a filename into result using the following algorithm.
1875 * - make result NULL
1876 * - if file starts with '/', append file to result & set cdpathp to NULL
1877 * - if file starts with ./ or ../ append cwd and file to result
1878 * and set cdpathp to NULL
1879 * - if the first element of cdpathp doesnt start with a '/' xx or '.' xx
1880 * then cwd is appended to result.
1881 * - the first element of cdpathp is appended to result
1882 * - file is appended to result
1883 * - cdpathp is set to the start of the next element in cdpathp (or NULL
1884 * if there are no more elements.
1885 * The return value indicates whether a non-null element from cdpathp
1886 * was appended to result.
1887 */
1888 static int
make_path(const char * cwd,const char * file,char ** cdpathp,XString * xsp,int * phys_pathp)1889 make_path(const char *cwd, const char *file,
1890 /* pointer to colon-separated list */
1891 char **cdpathp,
1892 XString *xsp,
1893 int *phys_pathp)
1894 {
1895 int rval = 0;
1896 bool use_cdpath = true;
1897 char *plist;
1898 size_t len, plen = 0;
1899 char *xp = Xstring(*xsp, xp);
1900
1901 if (!file)
1902 file = null;
1903
1904 if (mksh_abspath(file)) {
1905 *phys_pathp = 0;
1906 use_cdpath = false;
1907 } else {
1908 if (file[0] == '.') {
1909 char c = file[1];
1910
1911 if (c == '.')
1912 c = file[2];
1913 if (mksh_cdirsep(c) || c == '\0')
1914 use_cdpath = false;
1915 }
1916
1917 plist = *cdpathp;
1918 if (!plist)
1919 use_cdpath = false;
1920 else if (use_cdpath) {
1921 char *pend = plist;
1922
1923 while (*pend && *pend != MKSH_PATHSEPC)
1924 ++pend;
1925 plen = pend - plist;
1926 *cdpathp = *pend ? pend + 1 : NULL;
1927 }
1928
1929 if ((!use_cdpath || !plen || !mksh_abspath(plist)) &&
1930 (cwd && *cwd)) {
1931 len = strlen(cwd);
1932 XcheckN(*xsp, xp, len);
1933 memcpy(xp, cwd, len);
1934 xp += len;
1935 if (!mksh_cdirsep(cwd[len - 1]))
1936 Xput(*xsp, xp, '/');
1937 }
1938 *phys_pathp = Xlength(*xsp, xp);
1939 if (use_cdpath && plen) {
1940 XcheckN(*xsp, xp, plen);
1941 memcpy(xp, plist, plen);
1942 xp += plen;
1943 if (!mksh_cdirsep(plist[plen - 1]))
1944 Xput(*xsp, xp, '/');
1945 rval = 1;
1946 }
1947 }
1948
1949 len = strlen(file) + 1;
1950 XcheckN(*xsp, xp, len);
1951 memcpy(xp, file, len);
1952
1953 if (!use_cdpath)
1954 *cdpathp = NULL;
1955
1956 return (rval);
1957 }
1958
1959 /*-
1960 * Simplify pathnames containing "." and ".." entries.
1961 *
1962 * simplify_path(this) = that
1963 * /a/b/c/./../d/.. /a/b
1964 * //./C/foo/bar/../baz //C/foo/baz
1965 * /foo/ /foo
1966 * /foo/../../bar /bar
1967 * /foo/./blah/.. /foo
1968 * . .
1969 * .. ..
1970 * ./foo foo
1971 * foo/../../../bar ../../bar
1972 * C:/foo/../.. C:/
1973 * C:. C:
1974 * C:.. C:..
1975 * C:foo/../../blah C:../blah
1976 *
1977 * XXX consider a rooted pathname: we cannot really 'cd ..' for
1978 * pathnames like: '/', 'c:/', '//foo', '//foo/', '/@unixroot/'
1979 * (no effect), 'c:', 'c:.' (effect is retaining the '../') but
1980 * we need to honour this throughout the shell
1981 */
1982 void
simplify_path(char * p)1983 simplify_path(char *p)
1984 {
1985 char *dp, *ip, *sp, *tp;
1986 size_t len;
1987 bool needslash;
1988 #ifdef MKSH_DOSPATH
1989 bool needdot = true;
1990
1991 /* keep drive letter */
1992 if (mksh_drvltr(p)) {
1993 p += 2;
1994 needdot = false;
1995 }
1996 #else
1997 #define needdot true
1998 #endif
1999
2000 switch (*p) {
2001 case 0:
2002 return;
2003 case '/':
2004 #ifdef MKSH_DOSPATH
2005 case '\\':
2006 #endif
2007 /* exactly two leading slashes? (SUSv4 3.266) */
2008 if (p[1] == p[0] && !mksh_cdirsep(p[2]))
2009 /* keep them, e.g. for UNC pathnames */
2010 ++p;
2011 needslash = true;
2012 break;
2013 default:
2014 needslash = false;
2015 }
2016 dp = ip = sp = p;
2017
2018 while (*ip) {
2019 /* skip slashes in input */
2020 while (mksh_cdirsep(*ip))
2021 ++ip;
2022 if (!*ip)
2023 break;
2024
2025 /* get next pathname component from input */
2026 tp = ip;
2027 while (*ip && !mksh_cdirsep(*ip))
2028 ++ip;
2029 len = ip - tp;
2030
2031 /* check input for "." and ".." */
2032 if (tp[0] == '.') {
2033 if (len == 1)
2034 /* just continue with the next one */
2035 continue;
2036 else if (len == 2 && tp[1] == '.') {
2037 /* parent level, but how? (see above) */
2038 if (mksh_abspath(p))
2039 /* absolute path, only one way */
2040 goto strip_last_component;
2041 else if (dp > sp) {
2042 /* relative path, with subpaths */
2043 needslash = false;
2044 strip_last_component:
2045 /* strip off last pathname component */
2046 while (dp > sp)
2047 if (mksh_cdirsep(*--dp))
2048 break;
2049 } else {
2050 /* relative path, at its beginning */
2051 if (needslash)
2052 /* or already dotdot-slash'd */
2053 *dp++ = '/';
2054 /* keep dotdot-slash if not absolute */
2055 *dp++ = '.';
2056 *dp++ = '.';
2057 needslash = true;
2058 sp = dp;
2059 }
2060 /* then continue with the next one */
2061 continue;
2062 }
2063 }
2064
2065 if (needslash)
2066 *dp++ = '/';
2067
2068 /* append next pathname component to output */
2069 memmove(dp, tp, len);
2070 dp += len;
2071
2072 /* append slash if we continue */
2073 needslash = true;
2074 /* try next component */
2075 }
2076 if (dp == p) {
2077 /* empty path -> dot (or slash, when absolute) */
2078 if (needslash)
2079 *dp++ = '/';
2080 else if (needdot)
2081 *dp++ = '.';
2082 }
2083 *dp = '\0';
2084 #undef needdot
2085 }
2086
2087 void
set_current_wd(const char * nwd)2088 set_current_wd(const char *nwd)
2089 {
2090 char *allocd = NULL;
2091
2092 if (nwd == NULL) {
2093 allocd = ksh_get_wd();
2094 nwd = allocd ? allocd : null;
2095 }
2096
2097 afree(current_wd, APERM);
2098 strdupx(current_wd, nwd, APERM);
2099
2100 afree(allocd, ATEMP);
2101 }
2102
2103 int
c_cd(const char ** wp)2104 c_cd(const char **wp)
2105 {
2106 int optc, rv, phys_path;
2107 bool physical = tobool(Flag(FPHYSICAL));
2108 /* was a node from cdpath added in? */
2109 int cdnode;
2110 /* show where we went?, error for $PWD */
2111 bool printpath = false, eflag = false;
2112 struct tbl *pwd_s, *oldpwd_s;
2113 XString xs;
2114 char *dir, *allocd = NULL, *tryp, *pwd, *cdpath;
2115
2116 while ((optc = ksh_getopt(wp, &builtin_opt, "eLP")) != -1)
2117 switch (optc) {
2118 case 'e':
2119 eflag = true;
2120 break;
2121 case 'L':
2122 physical = false;
2123 break;
2124 case 'P':
2125 physical = true;
2126 break;
2127 case '?':
2128 return (2);
2129 }
2130 wp += builtin_opt.optind;
2131
2132 if (Flag(FRESTRICTED)) {
2133 bi_errorf(Tcant_cd);
2134 return (2);
2135 }
2136
2137 pwd_s = global(TPWD);
2138 oldpwd_s = global(TOLDPWD);
2139
2140 if (!wp[0]) {
2141 /* No arguments - go home */
2142 if ((dir = str_val(global("HOME"))) == null) {
2143 bi_errorf("no home directory (HOME not set)");
2144 return (2);
2145 }
2146 } else if (!wp[1]) {
2147 /* One argument: - or dir */
2148 strdupx(allocd, wp[0], ATEMP);
2149 if (ksh_isdash((dir = allocd))) {
2150 afree(allocd, ATEMP);
2151 allocd = NULL;
2152 dir = str_val(oldpwd_s);
2153 if (dir == null) {
2154 bi_errorf(Tno_OLDPWD);
2155 return (2);
2156 }
2157 printpath = true;
2158 }
2159 } else if (!wp[2]) {
2160 /* Two arguments - substitute arg1 in PWD for arg2 */
2161 size_t ilen, olen, nlen, elen;
2162 char *cp;
2163
2164 if (!current_wd[0]) {
2165 bi_errorf("can't determine current directory");
2166 return (2);
2167 }
2168 /*
2169 * substitute arg1 for arg2 in current path.
2170 * if the first substitution fails because the cd fails
2171 * we could try to find another substitution. For now
2172 * we don't
2173 */
2174 if ((cp = strstr(current_wd, wp[0])) == NULL) {
2175 bi_errorf(Tbadsubst);
2176 return (2);
2177 }
2178 /*-
2179 * ilen = part of current_wd before wp[0]
2180 * elen = part of current_wd after wp[0]
2181 * because current_wd and wp[1] need to be in memory at the
2182 * same time beforehand the addition can stay unchecked
2183 */
2184 ilen = cp - current_wd;
2185 olen = strlen(wp[0]);
2186 nlen = strlen(wp[1]);
2187 elen = strlen(current_wd + ilen + olen) + 1;
2188 dir = allocd = alloc(ilen + nlen + elen, ATEMP);
2189 memcpy(dir, current_wd, ilen);
2190 memcpy(dir + ilen, wp[1], nlen);
2191 memcpy(dir + ilen + nlen, current_wd + ilen + olen, elen);
2192 printpath = true;
2193 } else {
2194 bi_errorf(Ttoo_many_args);
2195 return (2);
2196 }
2197
2198 #ifdef MKSH_DOSPATH
2199 tryp = NULL;
2200 if (mksh_drvltr(dir) && !mksh_cdirsep(dir[2]) &&
2201 !getdrvwd(&tryp, ord(*dir))) {
2202 dir = shf_smprintf(Tf_sss, tryp,
2203 dir[2] ? "/" : "", dir + 2);
2204 afree(tryp, ATEMP);
2205 afree(allocd, ATEMP);
2206 allocd = dir;
2207 }
2208 #endif
2209
2210 #ifdef MKSH__NO_PATH_MAX
2211 /* only a first guess; make_path will enlarge xs if necessary */
2212 XinitN(xs, 1024, ATEMP);
2213 #else
2214 XinitN(xs, PATH_MAX, ATEMP);
2215 #endif
2216
2217 cdpath = str_val(global("CDPATH"));
2218 do {
2219 cdnode = make_path(current_wd, dir, &cdpath, &xs, &phys_path);
2220 if (physical)
2221 rv = chdir(tryp = Xstring(xs, xp) + phys_path);
2222 else {
2223 simplify_path(Xstring(xs, xp));
2224 rv = chdir(tryp = Xstring(xs, xp));
2225 }
2226 } while (rv < 0 && cdpath != NULL);
2227
2228 if (rv < 0) {
2229 if (cdnode)
2230 bi_errorf(Tf_sD_s, dir, "bad directory");
2231 else
2232 bi_errorf(Tf_sD_s, tryp, cstrerror(errno));
2233 afree(allocd, ATEMP);
2234 Xfree(xs, xp);
2235 return (2);
2236 }
2237
2238 rv = 0;
2239
2240 /* allocd (above) => dir, which is no longer used */
2241 afree(allocd, ATEMP);
2242 allocd = NULL;
2243
2244 /* Clear out tracked aliases with relative paths */
2245 flushcom(false);
2246
2247 /*
2248 * Set OLDPWD (note: unsetting OLDPWD does not disable this
2249 * setting in AT&T ksh)
2250 */
2251 if (current_wd[0])
2252 /* Ignore failure (happens if readonly or integer) */
2253 setstr(oldpwd_s, current_wd, KSH_RETURN_ERROR);
2254
2255 if (!mksh_abspath(Xstring(xs, xp))) {
2256 pwd = NULL;
2257 } else if (!physical) {
2258 goto norealpath_PWD;
2259 } else if ((pwd = allocd = do_realpath(Xstring(xs, xp))) == NULL) {
2260 if (eflag)
2261 rv = 1;
2262 norealpath_PWD:
2263 pwd = Xstring(xs, xp);
2264 }
2265
2266 /* Set PWD */
2267 if (pwd) {
2268 char *ptmp = pwd;
2269
2270 set_current_wd(ptmp);
2271 /* Ignore failure (happens if readonly or integer) */
2272 setstr(pwd_s, ptmp, KSH_RETURN_ERROR);
2273 } else {
2274 set_current_wd(null);
2275 pwd = Xstring(xs, xp);
2276 /* XXX unset $PWD? */
2277 if (eflag)
2278 rv = 1;
2279 }
2280 if (printpath || cdnode)
2281 shprintf(Tf_sN, pwd);
2282
2283 afree(allocd, ATEMP);
2284 Xfree(xs, xp);
2285 return (rv);
2286 }
2287
2288
2289 #ifdef KSH_CHVT_CODE
2290 extern void chvt_reinit(void);
2291
2292 static void
chvt(const Getopt * go)2293 chvt(const Getopt *go)
2294 {
2295 const char *dv = go->optarg;
2296 char *cp = NULL;
2297 int fd;
2298
2299 switch (*dv) {
2300 case '-':
2301 dv = "/dev/null";
2302 break;
2303 case '!':
2304 ++dv;
2305 /* FALLTHROUGH */
2306 default: {
2307 struct stat sb;
2308
2309 if (stat(dv, &sb)) {
2310 cp = shf_smprintf("/dev/ttyC%s", dv);
2311 dv = cp;
2312 if (stat(dv, &sb)) {
2313 memmove(cp + 1, cp, /* /dev/tty */ 8);
2314 dv = cp + 1;
2315 if (stat(dv, &sb)) {
2316 errorf(Tf_sD_sD_s, "chvt",
2317 "can't find tty", go->optarg);
2318 }
2319 }
2320 }
2321 if (!(sb.st_mode & S_IFCHR))
2322 errorf(Tf_sD_sD_s, "chvt", "not a char device", dv);
2323 #ifndef MKSH_DISABLE_REVOKE_WARNING
2324 #if HAVE_REVOKE
2325 if (revoke(dv))
2326 #endif
2327 warningf(false, Tf_sD_s_s, "chvt",
2328 "new shell is potentially insecure, can't revoke",
2329 dv);
2330 #endif
2331 }
2332 }
2333 if ((fd = binopen2(dv, O_RDWR)) < 0) {
2334 sleep(1);
2335 if ((fd = binopen2(dv, O_RDWR)) < 0) {
2336 errorf(Tf_sD_s_s, "chvt", Tcant_open, dv);
2337 }
2338 }
2339 if (go->optarg[0] != '!') {
2340 switch (fork()) {
2341 case -1:
2342 errorf(Tf_sD_s_s, "chvt", "fork", "failed");
2343 case 0:
2344 break;
2345 default:
2346 exit(0);
2347 }
2348 }
2349 if (setsid() == -1)
2350 errorf(Tf_sD_s_s, "chvt", "setsid", "failed");
2351 if (go->optarg[0] != '-') {
2352 if (ioctl(fd, TIOCSCTTY, NULL) == -1)
2353 errorf(Tf_sD_s_s, "chvt", "TIOCSCTTY", "failed");
2354 if (tcflush(fd, TCIOFLUSH))
2355 errorf(Tf_sD_s_s, "chvt", "TCIOFLUSH", "failed");
2356 }
2357 ksh_dup2(fd, 0, false);
2358 ksh_dup2(fd, 1, false);
2359 ksh_dup2(fd, 2, false);
2360 if (fd > 2)
2361 close(fd);
2362 rndset((unsigned long)chvt_rndsetup(go, sizeof(Getopt)));
2363 chvt_reinit();
2364 }
2365 #endif
2366
2367 #ifdef DEBUG
2368 char *
strchr(char * p,int ch)2369 strchr(char *p, int ch)
2370 {
2371 for (;; ++p) {
2372 if (*p == ch)
2373 return (p);
2374 if (!*p)
2375 return (NULL);
2376 }
2377 /* NOTREACHED */
2378 }
2379
2380 char *
strstr(char * b,const char * l)2381 strstr(char *b, const char *l)
2382 {
2383 char first, c;
2384 size_t n;
2385
2386 if ((first = *l++) == '\0')
2387 return (b);
2388 n = strlen(l);
2389 strstr_look:
2390 while ((c = *b++) != first)
2391 if (c == '\0')
2392 return (NULL);
2393 if (strncmp(b, l, n))
2394 goto strstr_look;
2395 return (b - 1);
2396 }
2397 #endif
2398
2399 #if defined(MKSH_SMALL) && !defined(MKSH_SMALL_BUT_FAST)
2400 char *
strndup_i(const char * src,size_t len,Area * ap)2401 strndup_i(const char *src, size_t len, Area *ap)
2402 {
2403 char *dst = NULL;
2404
2405 if (src != NULL) {
2406 dst = alloc(len + 1, ap);
2407 memcpy(dst, src, len);
2408 dst[len] = '\0';
2409 }
2410 return (dst);
2411 }
2412
2413 char *
strdup_i(const char * src,Area * ap)2414 strdup_i(const char *src, Area *ap)
2415 {
2416 return (src == NULL ? NULL : strndup_i(src, strlen(src), ap));
2417 }
2418 #endif
2419
2420 #if !HAVE_GETRUSAGE
2421 #define INVTCK(r,t) do { \
2422 r.tv_usec = ((t) % (1000000 / CLK_TCK)) * (1000000 / CLK_TCK); \
2423 r.tv_sec = (t) / CLK_TCK; \
2424 } while (/* CONSTCOND */ 0)
2425
2426 int
getrusage(int what,struct rusage * ru)2427 getrusage(int what, struct rusage *ru)
2428 {
2429 struct tms tms;
2430 clock_t u, s;
2431
2432 if (/* ru == NULL || */ times(&tms) == (clock_t)-1)
2433 return (-1);
2434
2435 switch (what) {
2436 case RUSAGE_SELF:
2437 u = tms.tms_utime;
2438 s = tms.tms_stime;
2439 break;
2440 case RUSAGE_CHILDREN:
2441 u = tms.tms_cutime;
2442 s = tms.tms_cstime;
2443 break;
2444 default:
2445 errno = EINVAL;
2446 return (-1);
2447 }
2448 INVTCK(ru->ru_utime, u);
2449 INVTCK(ru->ru_stime, s);
2450 return (0);
2451 }
2452 #endif
2453
2454 /*
2455 * process the string available via fg (get a char)
2456 * and fp (put back a char) for backslash escapes,
2457 * assuming the first call to *fg gets the char di-
2458 * rectly after the backslash; return the character
2459 * (0..0xFF), UCS (wc + 0x100), or -1 if no known
2460 * escape sequence was found
2461 */
2462 int
unbksl(bool cstyle,int (* fg)(void),void (* fp)(int))2463 unbksl(bool cstyle, int (*fg)(void), void (*fp)(int))
2464 {
2465 int wc, i, c, fc, n;
2466
2467 fc = (*fg)();
2468 switch (fc) {
2469 case 'a':
2470 wc = KSH_BEL;
2471 break;
2472 case 'b':
2473 wc = '\b';
2474 break;
2475 case 'c':
2476 if (!cstyle)
2477 goto unknown_escape;
2478 c = (*fg)();
2479 wc = ksh_toctrl(c);
2480 break;
2481 case 'E':
2482 case 'e':
2483 wc = KSH_ESC;
2484 break;
2485 case 'f':
2486 wc = '\f';
2487 break;
2488 case 'n':
2489 wc = '\n';
2490 break;
2491 case 'r':
2492 wc = '\r';
2493 break;
2494 case 't':
2495 wc = '\t';
2496 break;
2497 case 'v':
2498 wc = KSH_VTAB;
2499 break;
2500 case '1':
2501 case '2':
2502 case '3':
2503 case '4':
2504 case '5':
2505 case '6':
2506 case '7':
2507 if (!cstyle)
2508 goto unknown_escape;
2509 /* FALLTHROUGH */
2510 case '0':
2511 if (cstyle)
2512 (*fp)(fc);
2513 /*
2514 * look for an octal number with up to three
2515 * digits, not counting the leading zero;
2516 * convert it to a raw octet
2517 */
2518 wc = 0;
2519 i = 3;
2520 while (i--)
2521 if (ctype((c = (*fg)()), C_OCTAL))
2522 wc = (wc << 3) + ksh_numdig(c);
2523 else {
2524 (*fp)(c);
2525 break;
2526 }
2527 break;
2528 case 'U':
2529 i = 8;
2530 if (/* CONSTCOND */ 0)
2531 /* FALLTHROUGH */
2532 case 'u':
2533 i = 4;
2534 if (/* CONSTCOND */ 0)
2535 /* FALLTHROUGH */
2536 case 'x':
2537 i = cstyle ? -1 : 2;
2538 /**
2539 * x: look for a hexadecimal number with up to
2540 * two (C style: arbitrary) digits; convert
2541 * to raw octet (C style: UCS if >0xFF)
2542 * u/U: look for a hexadecimal number with up to
2543 * four (U: eight) digits; convert to UCS
2544 */
2545 wc = 0;
2546 n = 0;
2547 while (n < i || i == -1) {
2548 wc <<= 4;
2549 if (!ctype((c = (*fg)()), C_SEDEC)) {
2550 wc >>= 4;
2551 (*fp)(c);
2552 break;
2553 }
2554 if (ctype(c, C_DIGIT))
2555 wc += ksh_numdig(c);
2556 else if (ctype(c, C_UPPER))
2557 wc += ksh_numuc(c) + 10;
2558 else
2559 wc += ksh_numlc(c) + 10;
2560 ++n;
2561 }
2562 if (!n)
2563 goto unknown_escape;
2564 if ((cstyle && wc > 0xFF) || fc != 'x')
2565 /* UCS marker */
2566 wc += 0x100;
2567 break;
2568 case '\'':
2569 if (!cstyle)
2570 goto unknown_escape;
2571 wc = '\'';
2572 break;
2573 case '\\':
2574 wc = '\\';
2575 break;
2576 default:
2577 unknown_escape:
2578 (*fp)(fc);
2579 return (-1);
2580 }
2581
2582 return (wc);
2583 }
2584