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