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.291 2018/01/14 00:03:03 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, 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
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)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
strip_nuls(char * buf,size_t len)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
blocking_read(int fd,char * buf,size_t nbytes)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
reset_nonblock(int fd)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 *
ksh_get_wd(void)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 *
do_realpath(const char * upath)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 #ifdef MKSH_DOSPATH
1665 } else if (mksh_drvltr(upath)) {
1666 /* upath is a drive-relative pathname */
1667 if (getdrvwd(&ldest, ord(*upath)))
1668 return (NULL);
1669 /* A:foo -> A:/cwd/foo; A: -> A:/cwd */
1670 ipath = shf_smprintf(Tf_sss, ldest,
1671 upath[2] ? "/" : "", upath + 2);
1672 #endif
1673 } else {
1674 /* upath is a relative pathname, prepend cwd */
1675 if ((tp = ksh_get_wd()) == NULL || !mksh_abspath(tp))
1676 return (NULL);
1677 ipath = shf_smprintf(Tf_sss, tp, "/", upath);
1678 afree(tp, ATEMP);
1679 }
1680
1681 /* ipath and upath are in memory at the same time -> unchecked */
1682 Xinit(xs, xp, strlen(ip = ipath) + 1, ATEMP);
1683
1684 /* now jump into the deep of the loop */
1685 goto beginning_of_a_pathname;
1686
1687 while (*ip) {
1688 /* skip slashes in input */
1689 while (mksh_cdirsep(*ip))
1690 ++ip;
1691 if (!*ip)
1692 break;
1693
1694 /* get next pathname component from input */
1695 tp = ip;
1696 while (*ip && !mksh_cdirsep(*ip))
1697 ++ip;
1698 len = ip - tp;
1699
1700 /* check input for "." and ".." */
1701 if (tp[0] == '.') {
1702 if (len == 1)
1703 /* just continue with the next one */
1704 continue;
1705 else if (len == 2 && tp[1] == '.') {
1706 /* strip off last pathname component */
1707 /*XXX consider a rooted pathname */
1708 while (xp > Xstring(xs, xp))
1709 if (mksh_cdirsep(*--xp))
1710 break;
1711 /* then continue with the next one */
1712 continue;
1713 }
1714 }
1715
1716 /* store output position away, then append slash to output */
1717 pos = Xsavepos(xs, xp);
1718 /* 1 for the '/' and len + 1 for tp and the NUL from below */
1719 XcheckN(xs, xp, 1 + len + 1);
1720 Xput(xs, xp, '/');
1721
1722 /* append next pathname component to output */
1723 memcpy(xp, tp, len);
1724 xp += len;
1725 *xp = '\0';
1726
1727 /* lstat the current output, see if it's a symlink */
1728 if (mksh_lstat(Xstring(xs, xp), &sb)) {
1729 /* lstat failed */
1730 if (errno == ENOENT) {
1731 /* because the pathname does not exist */
1732 while (mksh_cdirsep(*ip))
1733 /* skip any trailing slashes */
1734 ++ip;
1735 /* no more components left? */
1736 if (!*ip)
1737 /* we can still return successfully */
1738 break;
1739 /* more components left? fall through */
1740 }
1741 /* not ENOENT or not at the end of ipath */
1742 goto notfound;
1743 }
1744
1745 /* check if we encountered a symlink? */
1746 if (S_ISLNK(sb.st_mode)) {
1747 #ifndef MKSH__NO_SYMLINK
1748 /* reached maximum recursion depth? */
1749 if (!symlinks--) {
1750 /* yep, prevent infinite loops */
1751 errno = ELOOP;
1752 goto notfound;
1753 }
1754
1755 /* get symlink(7) target */
1756 if (pathcnd) {
1757 #ifdef MKSH__NO_PATH_MAX
1758 if (notoktoadd(pathlen, 1)) {
1759 errno = ENAMETOOLONG;
1760 goto notfound;
1761 }
1762 #endif
1763 ldest = aresize(ldest, pathlen + 1, ATEMP);
1764 }
1765 llen = readlink(Xstring(xs, xp), ldest, pathlen);
1766 if (llen < 0)
1767 /* oops... */
1768 goto notfound;
1769 ldest[llen] = '\0';
1770
1771 /*
1772 * restart if symlink target is an absolute path,
1773 * otherwise continue with currently resolved prefix
1774 */
1775 #ifdef MKSH_DOSPATH
1776 assemble_symlink:
1777 #endif
1778 /* append rest of current input path to link target */
1779 tp = shf_smprintf(Tf_sss, ldest, *ip ? "/" : "", ip);
1780 afree(ipath, ATEMP);
1781 ip = ipath = tp;
1782 if (!mksh_abspath(ipath)) {
1783 #ifdef MKSH_DOSPATH
1784 /* symlink target might be drive-relative */
1785 if (mksh_drvltr(ipath)) {
1786 if (getdrvwd(&ldest, ord(*ipath)))
1787 goto notfound;
1788 ip += 2;
1789 goto assemble_symlink;
1790 }
1791 #endif
1792 /* symlink target is a relative path */
1793 xp = Xrestpos(xs, xp, pos);
1794 } else
1795 #endif
1796 {
1797 /* symlink target is an absolute path */
1798 xp = Xstring(xs, xp);
1799 beginning_of_a_pathname:
1800 /* assert: mksh_abspath(ip == ipath) */
1801 /* assert: xp == xs.beg => start of path */
1802
1803 /* exactly two leading slashes? (SUSv4 3.266) */
1804 if (ip[1] == ip[0] && !mksh_cdirsep(ip[2])) {
1805 /* keep them, e.g. for UNC pathnames */
1806 Xput(xs, xp, '/');
1807 }
1808 #ifdef MKSH_DOSPATH
1809 /* drive letter? */
1810 if (mksh_drvltr(ip)) {
1811 /* keep it */
1812 Xput(xs, xp, *ip++);
1813 Xput(xs, xp, *ip++);
1814 }
1815 #endif
1816 }
1817 }
1818 /* otherwise (no symlink) merely go on */
1819 }
1820
1821 /*
1822 * either found the target and successfully resolved it,
1823 * or found its parent directory and may create it
1824 */
1825 if (Xlength(xs, xp) == 0)
1826 /*
1827 * if the resolved pathname is "", make it "/",
1828 * otherwise do not add a trailing slash
1829 */
1830 Xput(xs, xp, '/');
1831 Xput(xs, xp, '\0');
1832
1833 /*
1834 * if source path had a trailing slash, check if target path
1835 * is not a non-directory existing file
1836 */
1837 if (ip > ipath && mksh_cdirsep(ip[-1])) {
1838 if (stat(Xstring(xs, xp), &sb)) {
1839 if (errno != ENOENT)
1840 goto notfound;
1841 } else if (!S_ISDIR(sb.st_mode)) {
1842 errno = ENOTDIR;
1843 goto notfound;
1844 }
1845 /* target now either does not exist or is a directory */
1846 }
1847
1848 /* return target path */
1849 afree(ldest, ATEMP);
1850 afree(ipath, ATEMP);
1851 return (Xclose(xs, xp));
1852
1853 notfound:
1854 /* save; freeing memory might trash it */
1855 llen = errno;
1856 afree(ldest, ATEMP);
1857 afree(ipath, ATEMP);
1858 Xfree(xs, xp);
1859 errno = llen;
1860 return (NULL);
1861
1862 #undef pathlen
1863 #undef pathcnd
1864 }
1865
1866 /**
1867 * Makes a filename into result using the following algorithm.
1868 * - make result NULL
1869 * - if file starts with '/', append file to result & set cdpathp to NULL
1870 * - if file starts with ./ or ../ append cwd and file to result
1871 * and set cdpathp to NULL
1872 * - if the first element of cdpathp doesnt start with a '/' xx or '.' xx
1873 * then cwd is appended to result.
1874 * - the first element of cdpathp is appended to result
1875 * - file is appended to result
1876 * - cdpathp is set to the start of the next element in cdpathp (or NULL
1877 * if there are no more elements.
1878 * The return value indicates whether a non-null element from cdpathp
1879 * was appended to result.
1880 */
1881 static int
make_path(const char * cwd,const char * file,char ** cdpathp,XString * xsp,int * phys_pathp)1882 make_path(const char *cwd, const char *file,
1883 /* pointer to colon-separated list */
1884 char **cdpathp,
1885 XString *xsp,
1886 int *phys_pathp)
1887 {
1888 int rval = 0;
1889 bool use_cdpath = true;
1890 char *plist;
1891 size_t len, plen = 0;
1892 char *xp = Xstring(*xsp, xp);
1893
1894 if (!file)
1895 file = null;
1896
1897 if (mksh_abspath(file)) {
1898 *phys_pathp = 0;
1899 use_cdpath = false;
1900 } else {
1901 if (file[0] == '.') {
1902 char c = file[1];
1903
1904 if (c == '.')
1905 c = file[2];
1906 if (mksh_cdirsep(c) || c == '\0')
1907 use_cdpath = false;
1908 }
1909
1910 plist = *cdpathp;
1911 if (!plist)
1912 use_cdpath = false;
1913 else if (use_cdpath) {
1914 char *pend = plist;
1915
1916 while (*pend && *pend != MKSH_PATHSEPC)
1917 ++pend;
1918 plen = pend - plist;
1919 *cdpathp = *pend ? pend + 1 : NULL;
1920 }
1921
1922 if ((!use_cdpath || !plen || !mksh_abspath(plist)) &&
1923 (cwd && *cwd)) {
1924 len = strlen(cwd);
1925 XcheckN(*xsp, xp, len);
1926 memcpy(xp, cwd, len);
1927 xp += len;
1928 if (!mksh_cdirsep(cwd[len - 1]))
1929 Xput(*xsp, xp, '/');
1930 }
1931 *phys_pathp = Xlength(*xsp, xp);
1932 if (use_cdpath && plen) {
1933 XcheckN(*xsp, xp, plen);
1934 memcpy(xp, plist, plen);
1935 xp += plen;
1936 if (!mksh_cdirsep(plist[plen - 1]))
1937 Xput(*xsp, xp, '/');
1938 rval = 1;
1939 }
1940 }
1941
1942 len = strlen(file) + 1;
1943 XcheckN(*xsp, xp, len);
1944 memcpy(xp, file, len);
1945
1946 if (!use_cdpath)
1947 *cdpathp = NULL;
1948
1949 return (rval);
1950 }
1951
1952 /*-
1953 * Simplify pathnames containing "." and ".." entries.
1954 *
1955 * simplify_path(this) = that
1956 * /a/b/c/./../d/.. /a/b
1957 * //./C/foo/bar/../baz //C/foo/baz
1958 * /foo/ /foo
1959 * /foo/../../bar /bar
1960 * /foo/./blah/.. /foo
1961 * . .
1962 * .. ..
1963 * ./foo foo
1964 * foo/../../../bar ../../bar
1965 * C:/foo/../.. C:/
1966 * C:. C:
1967 * C:.. C:..
1968 * C:foo/../../blah C:../blah
1969 *
1970 * XXX consider a rooted pathname: we cannot really 'cd ..' for
1971 * pathnames like: '/', 'c:/', '//foo', '//foo/', '/@unixroot/'
1972 * (no effect), 'c:', 'c:.' (effect is retaining the '../') but
1973 * we need to honour this throughout the shell
1974 */
1975 void
simplify_path(char * p)1976 simplify_path(char *p)
1977 {
1978 char *dp, *ip, *sp, *tp;
1979 size_t len;
1980 bool needslash;
1981 #ifdef MKSH_DOSPATH
1982 bool needdot = true;
1983
1984 /* keep drive letter */
1985 if (mksh_drvltr(p)) {
1986 p += 2;
1987 needdot = false;
1988 }
1989 #else
1990 #define needdot true
1991 #endif
1992
1993 switch (*p) {
1994 case 0:
1995 return;
1996 case '/':
1997 #ifdef MKSH_DOSPATH
1998 case '\\':
1999 #endif
2000 /* exactly two leading slashes? (SUSv4 3.266) */
2001 if (p[1] == p[0] && !mksh_cdirsep(p[2]))
2002 /* keep them, e.g. for UNC pathnames */
2003 ++p;
2004 needslash = true;
2005 break;
2006 default:
2007 needslash = false;
2008 }
2009 dp = ip = sp = p;
2010
2011 while (*ip) {
2012 /* skip slashes in input */
2013 while (mksh_cdirsep(*ip))
2014 ++ip;
2015 if (!*ip)
2016 break;
2017
2018 /* get next pathname component from input */
2019 tp = ip;
2020 while (*ip && !mksh_cdirsep(*ip))
2021 ++ip;
2022 len = ip - tp;
2023
2024 /* check input for "." and ".." */
2025 if (tp[0] == '.') {
2026 if (len == 1)
2027 /* just continue with the next one */
2028 continue;
2029 else if (len == 2 && tp[1] == '.') {
2030 /* parent level, but how? (see above) */
2031 if (mksh_abspath(p))
2032 /* absolute path, only one way */
2033 goto strip_last_component;
2034 else if (dp > sp) {
2035 /* relative path, with subpaths */
2036 needslash = false;
2037 strip_last_component:
2038 /* strip off last pathname component */
2039 while (dp > sp)
2040 if (mksh_cdirsep(*--dp))
2041 break;
2042 } else {
2043 /* relative path, at its beginning */
2044 if (needslash)
2045 /* or already dotdot-slash'd */
2046 *dp++ = '/';
2047 /* keep dotdot-slash if not absolute */
2048 *dp++ = '.';
2049 *dp++ = '.';
2050 needslash = true;
2051 sp = dp;
2052 }
2053 /* then continue with the next one */
2054 continue;
2055 }
2056 }
2057
2058 if (needslash)
2059 *dp++ = '/';
2060
2061 /* append next pathname component to output */
2062 memmove(dp, tp, len);
2063 dp += len;
2064
2065 /* append slash if we continue */
2066 needslash = true;
2067 /* try next component */
2068 }
2069 if (dp == p) {
2070 /* empty path -> dot (or slash, when absolute) */
2071 if (needslash)
2072 *dp++ = '/';
2073 else if (needdot)
2074 *dp++ = '.';
2075 }
2076 *dp = '\0';
2077 #undef needdot
2078 }
2079
2080 void
set_current_wd(const char * nwd)2081 set_current_wd(const char *nwd)
2082 {
2083 char *allocd = NULL;
2084
2085 if (nwd == NULL) {
2086 allocd = ksh_get_wd();
2087 nwd = allocd ? allocd : null;
2088 }
2089
2090 afree(current_wd, APERM);
2091 strdupx(current_wd, nwd, APERM);
2092
2093 afree(allocd, ATEMP);
2094 }
2095
2096 int
c_cd(const char ** wp)2097 c_cd(const char **wp)
2098 {
2099 int optc, rv, phys_path;
2100 bool physical = tobool(Flag(FPHYSICAL));
2101 /* was a node from cdpath added in? */
2102 int cdnode;
2103 /* show where we went?, error for $PWD */
2104 bool printpath = false, eflag = false;
2105 struct tbl *pwd_s, *oldpwd_s;
2106 XString xs;
2107 char *dir, *allocd = NULL, *tryp, *pwd, *cdpath;
2108
2109 while ((optc = ksh_getopt(wp, &builtin_opt, "eLP")) != -1)
2110 switch (optc) {
2111 case 'e':
2112 eflag = true;
2113 break;
2114 case 'L':
2115 physical = false;
2116 break;
2117 case 'P':
2118 physical = true;
2119 break;
2120 case '?':
2121 return (2);
2122 }
2123 wp += builtin_opt.optind;
2124
2125 if (Flag(FRESTRICTED)) {
2126 bi_errorf(Tcant_cd);
2127 return (2);
2128 }
2129
2130 pwd_s = global(TPWD);
2131 oldpwd_s = global(TOLDPWD);
2132
2133 if (!wp[0]) {
2134 /* No arguments - go home */
2135 if ((dir = str_val(global("HOME"))) == null) {
2136 bi_errorf("no home directory (HOME not set)");
2137 return (2);
2138 }
2139 } else if (!wp[1]) {
2140 /* One argument: - or dir */
2141 strdupx(allocd, wp[0], ATEMP);
2142 if (ksh_isdash((dir = allocd))) {
2143 afree(allocd, ATEMP);
2144 allocd = NULL;
2145 dir = str_val(oldpwd_s);
2146 if (dir == null) {
2147 bi_errorf(Tno_OLDPWD);
2148 return (2);
2149 }
2150 printpath = true;
2151 }
2152 } else if (!wp[2]) {
2153 /* Two arguments - substitute arg1 in PWD for arg2 */
2154 size_t ilen, olen, nlen, elen;
2155 char *cp;
2156
2157 if (!current_wd[0]) {
2158 bi_errorf("can't determine current directory");
2159 return (2);
2160 }
2161 /*
2162 * substitute arg1 for arg2 in current path.
2163 * if the first substitution fails because the cd fails
2164 * we could try to find another substitution. For now
2165 * we don't
2166 */
2167 if ((cp = strstr(current_wd, wp[0])) == NULL) {
2168 bi_errorf(Tbadsubst);
2169 return (2);
2170 }
2171 /*-
2172 * ilen = part of current_wd before wp[0]
2173 * elen = part of current_wd after wp[0]
2174 * because current_wd and wp[1] need to be in memory at the
2175 * same time beforehand the addition can stay unchecked
2176 */
2177 ilen = cp - current_wd;
2178 olen = strlen(wp[0]);
2179 nlen = strlen(wp[1]);
2180 elen = strlen(current_wd + ilen + olen) + 1;
2181 dir = allocd = alloc(ilen + nlen + elen, ATEMP);
2182 memcpy(dir, current_wd, ilen);
2183 memcpy(dir + ilen, wp[1], nlen);
2184 memcpy(dir + ilen + nlen, current_wd + ilen + olen, elen);
2185 printpath = true;
2186 } else {
2187 bi_errorf(Ttoo_many_args);
2188 return (2);
2189 }
2190
2191 #ifdef MKSH_DOSPATH
2192 tryp = NULL;
2193 if (mksh_drvltr(dir) && !mksh_cdirsep(dir[2]) &&
2194 !getdrvwd(&tryp, ord(*dir))) {
2195 dir = shf_smprintf(Tf_sss, tryp,
2196 dir[2] ? "/" : "", dir + 2);
2197 afree(tryp, ATEMP);
2198 afree(allocd, ATEMP);
2199 allocd = dir;
2200 }
2201 #endif
2202
2203 #ifdef MKSH__NO_PATH_MAX
2204 /* only a first guess; make_path will enlarge xs if necessary */
2205 XinitN(xs, 1024, ATEMP);
2206 #else
2207 XinitN(xs, PATH_MAX, ATEMP);
2208 #endif
2209
2210 cdpath = str_val(global("CDPATH"));
2211 do {
2212 cdnode = make_path(current_wd, dir, &cdpath, &xs, &phys_path);
2213 if (physical)
2214 rv = chdir(tryp = Xstring(xs, xp) + phys_path);
2215 else {
2216 simplify_path(Xstring(xs, xp));
2217 rv = chdir(tryp = Xstring(xs, xp));
2218 }
2219 } while (rv < 0 && cdpath != NULL);
2220
2221 if (rv < 0) {
2222 if (cdnode)
2223 bi_errorf(Tf_sD_s, dir, "bad directory");
2224 else
2225 bi_errorf(Tf_sD_s, tryp, cstrerror(errno));
2226 afree(allocd, ATEMP);
2227 Xfree(xs, xp);
2228 return (2);
2229 }
2230
2231 rv = 0;
2232
2233 /* allocd (above) => dir, which is no longer used */
2234 afree(allocd, ATEMP);
2235 allocd = NULL;
2236
2237 /* Clear out tracked aliases with relative paths */
2238 flushcom(false);
2239
2240 /*
2241 * Set OLDPWD (note: unsetting OLDPWD does not disable this
2242 * setting in AT&T ksh)
2243 */
2244 if (current_wd[0])
2245 /* Ignore failure (happens if readonly or integer) */
2246 setstr(oldpwd_s, current_wd, KSH_RETURN_ERROR);
2247
2248 if (!mksh_abspath(Xstring(xs, xp))) {
2249 pwd = NULL;
2250 } else if (!physical) {
2251 goto norealpath_PWD;
2252 } else if ((pwd = allocd = do_realpath(Xstring(xs, xp))) == NULL) {
2253 if (eflag)
2254 rv = 1;
2255 norealpath_PWD:
2256 pwd = Xstring(xs, xp);
2257 }
2258
2259 /* Set PWD */
2260 if (pwd) {
2261 char *ptmp = pwd;
2262
2263 set_current_wd(ptmp);
2264 /* Ignore failure (happens if readonly or integer) */
2265 setstr(pwd_s, ptmp, KSH_RETURN_ERROR);
2266 } else {
2267 set_current_wd(null);
2268 pwd = Xstring(xs, xp);
2269 /* XXX unset $PWD? */
2270 if (eflag)
2271 rv = 1;
2272 }
2273 if (printpath || cdnode)
2274 shprintf(Tf_sN, pwd);
2275
2276 afree(allocd, ATEMP);
2277 Xfree(xs, xp);
2278 return (rv);
2279 }
2280
2281
2282 #ifdef KSH_CHVT_CODE
2283 extern void chvt_reinit(void);
2284
2285 static void
chvt(const Getopt * go)2286 chvt(const Getopt *go)
2287 {
2288 const char *dv = go->optarg;
2289 char *cp = NULL;
2290 int fd;
2291
2292 switch (*dv) {
2293 case '-':
2294 dv = "/dev/null";
2295 break;
2296 case '!':
2297 ++dv;
2298 /* FALLTHROUGH */
2299 default: {
2300 struct stat sb;
2301
2302 if (stat(dv, &sb)) {
2303 cp = shf_smprintf("/dev/ttyC%s", dv);
2304 dv = cp;
2305 if (stat(dv, &sb)) {
2306 memmove(cp + 1, cp, /* /dev/tty */ 8);
2307 dv = cp + 1;
2308 if (stat(dv, &sb)) {
2309 errorf(Tf_sD_sD_s, "chvt",
2310 "can't find tty", go->optarg);
2311 }
2312 }
2313 }
2314 if (!(sb.st_mode & S_IFCHR))
2315 errorf(Tf_sD_sD_s, "chvt", "not a char device", dv);
2316 #ifndef MKSH_DISABLE_REVOKE_WARNING
2317 #if HAVE_REVOKE
2318 if (revoke(dv))
2319 #endif
2320 warningf(false, Tf_sD_s_s, "chvt",
2321 "new shell is potentially insecure, can't revoke",
2322 dv);
2323 #endif
2324 }
2325 }
2326 if ((fd = binopen2(dv, O_RDWR)) < 0) {
2327 sleep(1);
2328 if ((fd = binopen2(dv, O_RDWR)) < 0) {
2329 errorf(Tf_sD_s_s, "chvt", Tcant_open, dv);
2330 }
2331 }
2332 if (go->optarg[0] != '!') {
2333 switch (fork()) {
2334 case -1:
2335 errorf(Tf_sD_s_s, "chvt", "fork", "failed");
2336 case 0:
2337 break;
2338 default:
2339 exit(0);
2340 }
2341 }
2342 if (setsid() == -1)
2343 errorf(Tf_sD_s_s, "chvt", "setsid", "failed");
2344 if (go->optarg[0] != '-') {
2345 if (ioctl(fd, TIOCSCTTY, NULL) == -1)
2346 errorf(Tf_sD_s_s, "chvt", "TIOCSCTTY", "failed");
2347 if (tcflush(fd, TCIOFLUSH))
2348 errorf(Tf_sD_s_s, "chvt", "TCIOFLUSH", "failed");
2349 }
2350 ksh_dup2(fd, 0, false);
2351 ksh_dup2(fd, 1, false);
2352 ksh_dup2(fd, 2, false);
2353 if (fd > 2)
2354 close(fd);
2355 rndset((unsigned long)chvt_rndsetup(go, sizeof(Getopt)));
2356 chvt_reinit();
2357 }
2358 #endif
2359
2360 #ifdef DEBUG
2361 char *
strchr(char * p,int ch)2362 strchr(char *p, int ch)
2363 {
2364 for (;; ++p) {
2365 if (*p == ch)
2366 return (p);
2367 if (!*p)
2368 return (NULL);
2369 }
2370 /* NOTREACHED */
2371 }
2372
2373 char *
strstr(char * b,const char * l)2374 strstr(char *b, const char *l)
2375 {
2376 char first, c;
2377 size_t n;
2378
2379 if ((first = *l++) == '\0')
2380 return (b);
2381 n = strlen(l);
2382 strstr_look:
2383 while ((c = *b++) != first)
2384 if (c == '\0')
2385 return (NULL);
2386 if (strncmp(b, l, n))
2387 goto strstr_look;
2388 return (b - 1);
2389 }
2390 #endif
2391
2392 #if defined(MKSH_SMALL) && !defined(MKSH_SMALL_BUT_FAST)
2393 char *
strndup_i(const char * src,size_t len,Area * ap)2394 strndup_i(const char *src, size_t len, Area *ap)
2395 {
2396 char *dst = NULL;
2397
2398 if (src != NULL) {
2399 dst = alloc(len + 1, ap);
2400 memcpy(dst, src, len);
2401 dst[len] = '\0';
2402 }
2403 return (dst);
2404 }
2405
2406 char *
strdup_i(const char * src,Area * ap)2407 strdup_i(const char *src, Area *ap)
2408 {
2409 return (src == NULL ? NULL : strndup_i(src, strlen(src), ap));
2410 }
2411 #endif
2412
2413 #if !HAVE_GETRUSAGE
2414 #define INVTCK(r,t) do { \
2415 r.tv_usec = ((t) % (1000000 / CLK_TCK)) * (1000000 / CLK_TCK); \
2416 r.tv_sec = (t) / CLK_TCK; \
2417 } while (/* CONSTCOND */ 0)
2418
2419 int
getrusage(int what,struct rusage * ru)2420 getrusage(int what, struct rusage *ru)
2421 {
2422 struct tms tms;
2423 clock_t u, s;
2424
2425 if (/* ru == NULL || */ times(&tms) == (clock_t)-1)
2426 return (-1);
2427
2428 switch (what) {
2429 case RUSAGE_SELF:
2430 u = tms.tms_utime;
2431 s = tms.tms_stime;
2432 break;
2433 case RUSAGE_CHILDREN:
2434 u = tms.tms_cutime;
2435 s = tms.tms_cstime;
2436 break;
2437 default:
2438 errno = EINVAL;
2439 return (-1);
2440 }
2441 INVTCK(ru->ru_utime, u);
2442 INVTCK(ru->ru_stime, s);
2443 return (0);
2444 }
2445 #endif
2446
2447 /*
2448 * process the string available via fg (get a char)
2449 * and fp (put back a char) for backslash escapes,
2450 * assuming the first call to *fg gets the char di-
2451 * rectly after the backslash; return the character
2452 * (0..0xFF), Unicode (wc + 0x100), or -1 if no known
2453 * escape sequence was found
2454 */
2455 int
unbksl(bool cstyle,int (* fg)(void),void (* fp)(int))2456 unbksl(bool cstyle, int (*fg)(void), void (*fp)(int))
2457 {
2458 int wc, i, c, fc, n;
2459
2460 fc = (*fg)();
2461 switch (fc) {
2462 case 'a':
2463 wc = KSH_BEL;
2464 break;
2465 case 'b':
2466 wc = '\b';
2467 break;
2468 case 'c':
2469 if (!cstyle)
2470 goto unknown_escape;
2471 c = (*fg)();
2472 wc = ksh_toctrl(c);
2473 break;
2474 case 'E':
2475 case 'e':
2476 wc = KSH_ESC;
2477 break;
2478 case 'f':
2479 wc = '\f';
2480 break;
2481 case 'n':
2482 wc = '\n';
2483 break;
2484 case 'r':
2485 wc = '\r';
2486 break;
2487 case 't':
2488 wc = '\t';
2489 break;
2490 case 'v':
2491 wc = KSH_VTAB;
2492 break;
2493 case '1':
2494 case '2':
2495 case '3':
2496 case '4':
2497 case '5':
2498 case '6':
2499 case '7':
2500 if (!cstyle)
2501 goto unknown_escape;
2502 /* FALLTHROUGH */
2503 case '0':
2504 if (cstyle)
2505 (*fp)(fc);
2506 /*
2507 * look for an octal number with up to three
2508 * digits, not counting the leading zero;
2509 * convert it to a raw octet
2510 */
2511 wc = 0;
2512 i = 3;
2513 while (i--)
2514 if (ctype((c = (*fg)()), C_OCTAL))
2515 wc = (wc << 3) + ksh_numdig(c);
2516 else {
2517 (*fp)(c);
2518 break;
2519 }
2520 break;
2521 case 'U':
2522 i = 8;
2523 if (/* CONSTCOND */ 0)
2524 /* FALLTHROUGH */
2525 case 'u':
2526 i = 4;
2527 if (/* CONSTCOND */ 0)
2528 /* FALLTHROUGH */
2529 case 'x':
2530 i = cstyle ? -1 : 2;
2531 /**
2532 * x: look for a hexadecimal number with up to
2533 * two (C style: arbitrary) digits; convert
2534 * to raw octet (C style: Unicode if >0xFF)
2535 * u/U: look for a hexadecimal number with up to
2536 * four (U: eight) digits; convert to Unicode
2537 */
2538 wc = 0;
2539 n = 0;
2540 while (n < i || i == -1) {
2541 wc <<= 4;
2542 if (!ctype((c = (*fg)()), C_SEDEC)) {
2543 wc >>= 4;
2544 (*fp)(c);
2545 break;
2546 }
2547 if (ctype(c, C_DIGIT))
2548 wc += ksh_numdig(c);
2549 else if (ctype(c, C_UPPER))
2550 wc += ksh_numuc(c) + 10;
2551 else
2552 wc += ksh_numlc(c) + 10;
2553 ++n;
2554 }
2555 if (!n)
2556 goto unknown_escape;
2557 if ((cstyle && wc > 0xFF) || fc != 'x')
2558 /* Unicode marker */
2559 wc += 0x100;
2560 break;
2561 case '\'':
2562 if (!cstyle)
2563 goto unknown_escape;
2564 wc = '\'';
2565 break;
2566 case '\\':
2567 wc = '\\';
2568 break;
2569 default:
2570 unknown_escape:
2571 (*fp)(fc);
2572 return (-1);
2573 }
2574
2575 return (wc);
2576 }
2577