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