1 /* $OpenBSD: misc.c,v 1.73 2019/06/28 13:34:59 deraadt Exp $ */ 2 3 /* 4 * Miscellaneous functions 5 */ 6 7 #include <ctype.h> 8 #include <errno.h> 9 #include <fcntl.h> 10 #include <limits.h> 11 #include <stdlib.h> 12 #include <string.h> 13 #include <unistd.h> 14 15 #include "sh.h" 16 #include "charclass.h" 17 18 short ctypes [UCHAR_MAX+1]; /* type bits for unsigned char */ 19 static int dropped_privileges; 20 21 static int do_gmatch(const unsigned char *, const unsigned char *, 22 const unsigned char *, const unsigned char *); 23 static const unsigned char *cclass(const unsigned char *, int); 24 25 /* 26 * Fast character classes 27 */ 28 void 29 setctypes(const char *s, int t) 30 { 31 int i; 32 33 if (t & C_IFS) { 34 for (i = 0; i < UCHAR_MAX+1; i++) 35 ctypes[i] &= ~C_IFS; 36 ctypes[0] |= C_IFS; /* include \0 in C_IFS */ 37 } 38 while (*s != 0) 39 ctypes[(unsigned char) *s++] |= t; 40 } 41 42 void 43 initctypes(void) 44 { 45 int c; 46 47 for (c = 'a'; c <= 'z'; c++) 48 ctypes[c] |= C_ALPHA; 49 for (c = 'A'; c <= 'Z'; c++) 50 ctypes[c] |= C_ALPHA; 51 ctypes['_'] |= C_ALPHA; 52 setctypes(" \t\n|&;<>()", C_LEX1); /* \0 added automatically */ 53 setctypes("*@#!$-?", C_VAR1); 54 setctypes(" \t\n", C_IFSWS); 55 setctypes("=-+?", C_SUBOP1); 56 setctypes("#%", C_SUBOP2); 57 setctypes(" \n\t\"#$&'()*;<>?[\\`|", C_QUOTE); 58 } 59 60 /* convert uint64_t to base N string */ 61 62 char * 63 u64ton(uint64_t n, int base) 64 { 65 char *p; 66 static char buf [20]; 67 68 p = &buf[sizeof(buf)]; 69 *--p = '\0'; 70 do { 71 *--p = "0123456789ABCDEF"[n%base]; 72 n /= base; 73 } while (n != 0); 74 return p; 75 } 76 77 char * 78 str_save(const char *s, Area *ap) 79 { 80 size_t len; 81 char *p; 82 83 if (!s) 84 return NULL; 85 len = strlen(s)+1; 86 p = alloc(len, ap); 87 strlcpy(p, s, len); 88 return (p); 89 } 90 91 /* Allocate a string of size n+1 and copy upto n characters from the possibly 92 * null terminated string s into it. Always returns a null terminated string 93 * (unless n < 0). 94 */ 95 char * 96 str_nsave(const char *s, int n, Area *ap) 97 { 98 char *ns; 99 100 if (n < 0) 101 return 0; 102 ns = alloc(n + 1, ap); 103 ns[0] = '\0'; 104 return strncat(ns, s, n); 105 } 106 107 /* called from expand.h:XcheckN() to grow buffer */ 108 char * 109 Xcheck_grow_(XString *xsp, char *xp, size_t more) 110 { 111 char *old_beg = xsp->beg; 112 113 xsp->len += more > xsp->len ? more : xsp->len; 114 xsp->beg = aresize(xsp->beg, xsp->len + 8, xsp->areap); 115 xsp->end = xsp->beg + xsp->len; 116 return xsp->beg + (xp - old_beg); 117 } 118 119 const struct option sh_options[] = { 120 /* Special cases (see parse_args()): -A, -o, -s. 121 * Options are sorted by their longnames - the order of these 122 * entries MUST match the order of sh_flag F* enumerations in sh.h. 123 */ 124 { "allexport", 'a', OF_ANY }, 125 { "braceexpand", 0, OF_ANY }, /* non-standard */ 126 { "bgnice", 0, OF_ANY }, 127 { NULL, 'c', OF_CMDLINE }, 128 { "csh-history", 0, OF_ANY }, /* non-standard */ 129 #ifdef EMACS 130 { "emacs", 0, OF_ANY }, 131 #endif 132 { "errexit", 'e', OF_ANY }, 133 #ifdef EMACS 134 { "gmacs", 0, OF_ANY }, 135 #endif 136 { "ignoreeof", 0, OF_ANY }, 137 { "interactive",'i', OF_CMDLINE }, 138 { "keyword", 'k', OF_ANY }, 139 { "login", 'l', OF_CMDLINE }, 140 { "markdirs", 'X', OF_ANY }, 141 { "monitor", 'm', OF_ANY }, 142 { "noclobber", 'C', OF_ANY }, 143 { "noexec", 'n', OF_ANY }, 144 { "noglob", 'f', OF_ANY }, 145 { "nohup", 0, OF_ANY }, 146 { "nolog", 0, OF_ANY }, /* no effect */ 147 { "notify", 'b', OF_ANY }, 148 { "nounset", 'u', OF_ANY }, 149 { "physical", 0, OF_ANY }, /* non-standard */ 150 { "posix", 0, OF_ANY }, /* non-standard */ 151 { "privileged", 'p', OF_ANY }, 152 { "restricted", 'r', OF_CMDLINE }, 153 { "sh", 0, OF_ANY }, /* non-standard */ 154 { "stdin", 's', OF_CMDLINE }, /* pseudo non-standard */ 155 { "trackall", 'h', OF_ANY }, 156 { "verbose", 'v', OF_ANY }, 157 #ifdef VI 158 { "vi", 0, OF_ANY }, 159 { "viraw", 0, OF_ANY }, /* no effect */ 160 { "vi-show8", 0, OF_ANY }, /* non-standard */ 161 { "vi-tabcomplete", 0, OF_ANY }, /* non-standard */ 162 { "vi-esccomplete", 0, OF_ANY }, /* non-standard */ 163 #endif 164 { "xtrace", 'x', OF_ANY }, 165 /* Anonymous flags: used internally by shell only 166 * (not visible to user) 167 */ 168 { NULL, 0, OF_INTERNAL }, /* FTALKING_I */ 169 }; 170 171 /* 172 * translate -o option into F* constant (also used for test -o option) 173 */ 174 int 175 option(const char *n) 176 { 177 unsigned int ele; 178 179 for (ele = 0; ele < NELEM(sh_options); ele++) 180 if (sh_options[ele].name && strcmp(sh_options[ele].name, n) == 0) 181 return ele; 182 183 return -1; 184 } 185 186 struct options_info { 187 int opt_width; 188 struct { 189 const char *name; 190 int flag; 191 } opts[NELEM(sh_options)]; 192 }; 193 194 static char *options_fmt_entry(void *arg, int i, char *buf, int buflen); 195 static void printoptions(int verbose); 196 197 /* format a single select menu item */ 198 static char * 199 options_fmt_entry(void *arg, int i, char *buf, int buflen) 200 { 201 struct options_info *oi = (struct options_info *) arg; 202 203 shf_snprintf(buf, buflen, "%-*s %s", 204 oi->opt_width, oi->opts[i].name, 205 Flag(oi->opts[i].flag) ? "on" : "off"); 206 return buf; 207 } 208 209 static void 210 printoptions(int verbose) 211 { 212 unsigned int ele; 213 214 if (verbose) { 215 struct options_info oi; 216 unsigned int n; 217 int len; 218 219 /* verbose version */ 220 shprintf("Current option settings\n"); 221 222 for (ele = n = oi.opt_width = 0; ele < NELEM(sh_options); ele++) { 223 if (sh_options[ele].name) { 224 len = strlen(sh_options[ele].name); 225 oi.opts[n].name = sh_options[ele].name; 226 oi.opts[n++].flag = ele; 227 if (len > oi.opt_width) 228 oi.opt_width = len; 229 } 230 } 231 print_columns(shl_stdout, n, options_fmt_entry, &oi, 232 oi.opt_width + 5, 1); 233 } else { 234 /* short version ala ksh93 */ 235 shprintf("set"); 236 for (ele = 0; ele < NELEM(sh_options); ele++) { 237 if (sh_options[ele].name) 238 shprintf(" %co %s", 239 Flag(ele) ? '-' : '+', 240 sh_options[ele].name); 241 } 242 shprintf("\n"); 243 } 244 } 245 246 char * 247 getoptions(void) 248 { 249 unsigned int ele; 250 char m[(int) FNFLAGS + 1]; 251 char *cp = m; 252 253 for (ele = 0; ele < NELEM(sh_options); ele++) 254 if (sh_options[ele].c && Flag(ele)) 255 *cp++ = sh_options[ele].c; 256 *cp = 0; 257 return str_save(m, ATEMP); 258 } 259 260 /* change a Flag(*) value; takes care of special actions */ 261 void 262 change_flag(enum sh_flag f, 263 int what, /* flag to change */ 264 int newval) /* what is changing the flag (command line vs set) */ 265 { 266 int oldval; 267 268 oldval = Flag(f); 269 Flag(f) = newval; 270 if (f == FMONITOR) { 271 if (what != OF_CMDLINE && newval != oldval) 272 j_change(); 273 } else 274 if (0 275 #ifdef VI 276 || f == FVI 277 #endif /* VI */ 278 #ifdef EMACS 279 || f == FEMACS || f == FGMACS 280 #endif /* EMACS */ 281 ) 282 { 283 if (newval) { 284 #ifdef VI 285 Flag(FVI) = 0; 286 #endif /* VI */ 287 #ifdef EMACS 288 Flag(FEMACS) = Flag(FGMACS) = 0; 289 #endif /* EMACS */ 290 Flag(f) = newval; 291 } 292 } else 293 /* Turning off -p? */ 294 if (f == FPRIVILEGED && oldval && !newval && issetugid() && 295 !dropped_privileges) { 296 gid_t gid = getgid(); 297 298 setresgid(gid, gid, gid); 299 setgroups(1, &gid); 300 setresuid(ksheuid, ksheuid, ksheuid); 301 302 if (pledge("stdio rpath wpath cpath fattr flock getpw proc " 303 "exec tty", NULL) == -1) 304 bi_errorf("pledge fail"); 305 dropped_privileges = 1; 306 } else if (f == FPOSIX && newval) { 307 Flag(FBRACEEXPAND) = 0; 308 } 309 /* Changing interactive flag? */ 310 if (f == FTALKING) { 311 if ((what == OF_CMDLINE || what == OF_SET) && procpid == kshpid) 312 Flag(FTALKING_I) = newval; 313 } 314 } 315 316 /* parse command line & set command arguments. returns the index of 317 * non-option arguments, -1 if there is an error. 318 */ 319 int 320 parse_args(char **argv, 321 int what, /* OF_CMDLINE or OF_SET */ 322 int *setargsp) 323 { 324 static char cmd_opts[NELEM(sh_options) + 3]; /* o:\0 */ 325 static char set_opts[NELEM(sh_options) + 5]; /* Ao;s\0 */ 326 char *opts; 327 char *array = NULL; 328 Getopt go; 329 int i, optc, sortargs = 0, arrayset = 0; 330 unsigned int ele; 331 332 /* First call? Build option strings... */ 333 if (cmd_opts[0] == '\0') { 334 char *p, *q; 335 336 /* see cmd_opts[] declaration */ 337 strlcpy(cmd_opts, "o:", sizeof cmd_opts); 338 p = cmd_opts + strlen(cmd_opts); 339 /* see set_opts[] declaration */ 340 strlcpy(set_opts, "A:o;s", sizeof set_opts); 341 q = set_opts + strlen(set_opts); 342 for (ele = 0; ele < NELEM(sh_options); ele++) { 343 if (sh_options[ele].c) { 344 if (sh_options[ele].flags & OF_CMDLINE) 345 *p++ = sh_options[ele].c; 346 if (sh_options[ele].flags & OF_SET) 347 *q++ = sh_options[ele].c; 348 } 349 } 350 *p = '\0'; 351 *q = '\0'; 352 } 353 354 if (what == OF_CMDLINE) { 355 char *p; 356 /* Set FLOGIN before parsing options so user can clear 357 * flag using +l. 358 */ 359 Flag(FLOGIN) = (argv[0][0] == '-' || 360 ((p = strrchr(argv[0], '/')) && *++p == '-')); 361 opts = cmd_opts; 362 } else 363 opts = set_opts; 364 ksh_getopt_reset(&go, GF_ERROR|GF_PLUSOPT); 365 while ((optc = ksh_getopt(argv, &go, opts)) != -1) { 366 int set = (go.info & GI_PLUS) ? 0 : 1; 367 switch (optc) { 368 case 'A': 369 arrayset = set ? 1 : -1; 370 array = go.optarg; 371 break; 372 373 case 'o': 374 if (go.optarg == NULL) { 375 /* lone -o: print options 376 * 377 * Note that on the command line, -o requires 378 * an option (ie, can't get here if what is 379 * OF_CMDLINE). 380 */ 381 printoptions(set); 382 break; 383 } 384 i = option(go.optarg); 385 if (i != -1 && set == Flag(i)) 386 /* Don't check the context if the flag 387 * isn't changing - makes "set -o interactive" 388 * work if you're already interactive. Needed 389 * if the output of "set +o" is to be used. 390 */ 391 ; 392 else if (i != -1 && (sh_options[i].flags & what)) 393 change_flag((enum sh_flag) i, what, set); 394 else { 395 bi_errorf("%s: bad option", go.optarg); 396 return -1; 397 } 398 break; 399 400 case '?': 401 return -1; 402 403 default: 404 /* -s: sort positional params (at&t ksh stupidity) */ 405 if (what == OF_SET && optc == 's') { 406 sortargs = 1; 407 break; 408 } 409 for (ele = 0; ele < NELEM(sh_options); ele++) 410 if (optc == sh_options[ele].c && 411 (what & sh_options[ele].flags)) { 412 change_flag((enum sh_flag) ele, what, 413 set); 414 break; 415 } 416 if (ele == NELEM(sh_options)) { 417 internal_errorf("%s: `%c'", __func__, optc); 418 return -1; /* not reached */ 419 } 420 } 421 } 422 if (!(go.info & GI_MINUSMINUS) && argv[go.optind] && 423 (argv[go.optind][0] == '-' || argv[go.optind][0] == '+') && 424 argv[go.optind][1] == '\0') { 425 /* lone - clears -v and -x flags */ 426 if (argv[go.optind][0] == '-' && !Flag(FPOSIX)) 427 Flag(FVERBOSE) = Flag(FXTRACE) = 0; 428 /* set skips lone - or + option */ 429 go.optind++; 430 } 431 if (setargsp) 432 /* -- means set $#/$* even if there are no arguments */ 433 *setargsp = !arrayset && ((go.info & GI_MINUSMINUS) || 434 argv[go.optind]); 435 436 if (arrayset && (!*array || *skip_varname(array, false))) { 437 bi_errorf("%s: is not an identifier", array); 438 return -1; 439 } 440 if (sortargs) { 441 for (i = go.optind; argv[i]; i++) 442 ; 443 qsortp((void **) &argv[go.optind], (size_t) (i - go.optind), 444 xstrcmp); 445 } 446 if (arrayset) { 447 set_array(array, arrayset, argv + go.optind); 448 for (; argv[go.optind]; go.optind++) 449 ; 450 } 451 452 return go.optind; 453 } 454 455 /* parse a decimal number: returns 0 if string isn't a number, 1 otherwise */ 456 int 457 getn(const char *as, int *ai) 458 { 459 char *p; 460 long n; 461 462 n = strtol(as, &p, 10); 463 464 if (!*as || *p || INT_MIN >= n || n >= INT_MAX) 465 return 0; 466 467 *ai = (int)n; 468 return 1; 469 } 470 471 /* getn() that prints error */ 472 int 473 bi_getn(const char *as, int *ai) 474 { 475 int rv = getn(as, ai); 476 477 if (!rv) 478 bi_errorf("%s: bad number", as); 479 return rv; 480 } 481 482 /* -------- gmatch.c -------- */ 483 484 /* 485 * int gmatch(string, pattern) 486 * char *string, *pattern; 487 * 488 * Match a pattern as in sh(1). 489 * pattern character are prefixed with MAGIC by expand. 490 */ 491 492 int 493 gmatch(const char *s, const char *p, int isfile) 494 { 495 const char *se, *pe; 496 497 if (s == NULL || p == NULL) 498 return 0; 499 se = s + strlen(s); 500 pe = p + strlen(p); 501 /* isfile is false iff no syntax check has been done on 502 * the pattern. If check fails, just to a strcmp(). 503 */ 504 if (!isfile && !has_globbing(p, pe)) { 505 size_t len = pe - p + 1; 506 char tbuf[64]; 507 char *t = len <= sizeof(tbuf) ? tbuf : 508 alloc(len, ATEMP); 509 debunk(t, p, len); 510 return !strcmp(t, s); 511 } 512 return do_gmatch((const unsigned char *) s, (const unsigned char *) se, 513 (const unsigned char *) p, (const unsigned char *) pe); 514 } 515 516 /* Returns if p is a syntacticly correct globbing pattern, false 517 * if it contains no pattern characters or if there is a syntax error. 518 * Syntax errors are: 519 * - [ with no closing ] 520 * - imbalanced $(...) expression 521 * - [...] and *(...) not nested (eg, [a$(b|]c), *(a[b|c]d)) 522 */ 523 /*XXX 524 - if no magic, 525 if dest given, copy to dst 526 return ? 527 - if magic && (no globbing || syntax error) 528 debunk to dst 529 return ? 530 - return ? 531 */ 532 int 533 has_globbing(const char *xp, const char *xpe) 534 { 535 const unsigned char *p = (const unsigned char *) xp; 536 const unsigned char *pe = (const unsigned char *) xpe; 537 int c; 538 int nest = 0, bnest = 0; 539 int saw_glob = 0; 540 int in_bracket = 0; /* inside [...] */ 541 542 for (; p < pe; p++) { 543 if (!ISMAGIC(*p)) 544 continue; 545 if ((c = *++p) == '*' || c == '?') 546 saw_glob = 1; 547 else if (c == '[') { 548 if (!in_bracket) { 549 saw_glob = 1; 550 in_bracket = 1; 551 if (ISMAGIC(p[1]) && p[2] == '!') 552 p += 2; 553 if (ISMAGIC(p[1]) && p[2] == ']') 554 p += 2; 555 } 556 /* XXX Do we need to check ranges here? POSIX Q */ 557 } else if (c == ']') { 558 if (in_bracket) { 559 if (bnest) /* [a*(b]) */ 560 return 0; 561 in_bracket = 0; 562 } 563 } else if ((c & 0x80) && strchr("*+?@! ", c & 0x7f)) { 564 saw_glob = 1; 565 if (in_bracket) 566 bnest++; 567 else 568 nest++; 569 } else if (c == '|') { 570 if (in_bracket && !bnest) /* *(a[foo|bar]) */ 571 return 0; 572 } else if (c == /*(*/ ')') { 573 if (in_bracket) { 574 if (!bnest--) /* *(a[b)c] */ 575 return 0; 576 } else if (nest) 577 nest--; 578 } 579 /* else must be a MAGIC-MAGIC, or MAGIC-!, MAGIC--, MAGIC-] 580 MAGIC-{, MAGIC-,, MAGIC-} */ 581 } 582 return saw_glob && !in_bracket && !nest; 583 } 584 585 /* Function must return either 0 or 1 (assumed by code for 0x80|'!') */ 586 static int 587 do_gmatch(const unsigned char *s, const unsigned char *se, 588 const unsigned char *p, const unsigned char *pe) 589 { 590 int sc, pc; 591 const unsigned char *prest, *psub, *pnext; 592 const unsigned char *srest; 593 594 if (s == NULL || p == NULL) 595 return 0; 596 while (p < pe) { 597 pc = *p++; 598 sc = s < se ? *s : '\0'; 599 s++; 600 if (!ISMAGIC(pc)) { 601 if (sc != pc) 602 return 0; 603 continue; 604 } 605 switch (*p++) { 606 case '[': 607 if (sc == 0 || (p = cclass(p, sc)) == NULL) 608 return 0; 609 break; 610 611 case '?': 612 if (sc == 0) 613 return 0; 614 break; 615 616 case '*': 617 if (p == pe) 618 return 1; 619 s--; 620 do { 621 if (do_gmatch(s, se, p, pe)) 622 return 1; 623 } while (s++ < se); 624 return 0; 625 626 /* 627 * [*+?@!](pattern|pattern|..) 628 * 629 * Not ifdef'd KSH as this is needed for ${..%..}, etc. 630 */ 631 case 0x80|'+': /* matches one or more times */ 632 case 0x80|'*': /* matches zero or more times */ 633 if (!(prest = pat_scan(p, pe, 0))) 634 return 0; 635 s--; 636 /* take care of zero matches */ 637 if (p[-1] == (0x80 | '*') && 638 do_gmatch(s, se, prest, pe)) 639 return 1; 640 for (psub = p; ; psub = pnext) { 641 pnext = pat_scan(psub, pe, 1); 642 for (srest = s; srest <= se; srest++) { 643 if (do_gmatch(s, srest, psub, pnext - 2) && 644 (do_gmatch(srest, se, prest, pe) || 645 (s != srest && do_gmatch(srest, 646 se, p - 2, pe)))) 647 return 1; 648 } 649 if (pnext == prest) 650 break; 651 } 652 return 0; 653 654 case 0x80|'?': /* matches zero or once */ 655 case 0x80|'@': /* matches one of the patterns */ 656 case 0x80|' ': /* simile for @ */ 657 if (!(prest = pat_scan(p, pe, 0))) 658 return 0; 659 s--; 660 /* Take care of zero matches */ 661 if (p[-1] == (0x80 | '?') && 662 do_gmatch(s, se, prest, pe)) 663 return 1; 664 for (psub = p; ; psub = pnext) { 665 pnext = pat_scan(psub, pe, 1); 666 srest = prest == pe ? se : s; 667 for (; srest <= se; srest++) { 668 if (do_gmatch(s, srest, psub, pnext - 2) && 669 do_gmatch(srest, se, prest, pe)) 670 return 1; 671 } 672 if (pnext == prest) 673 break; 674 } 675 return 0; 676 677 case 0x80|'!': /* matches none of the patterns */ 678 if (!(prest = pat_scan(p, pe, 0))) 679 return 0; 680 s--; 681 for (srest = s; srest <= se; srest++) { 682 int matched = 0; 683 684 for (psub = p; ; psub = pnext) { 685 pnext = pat_scan(psub, pe, 1); 686 if (do_gmatch(s, srest, psub, 687 pnext - 2)) { 688 matched = 1; 689 break; 690 } 691 if (pnext == prest) 692 break; 693 } 694 if (!matched && 695 do_gmatch(srest, se, prest, pe)) 696 return 1; 697 } 698 return 0; 699 700 default: 701 if (sc != p[-1]) 702 return 0; 703 break; 704 } 705 } 706 return s == se; 707 } 708 709 static int 710 posix_cclass(const unsigned char *pattern, int test, const unsigned char **ep) 711 { 712 struct cclass *cc; 713 const unsigned char *colon; 714 size_t len; 715 int rval = 0; 716 717 if ((colon = strchr(pattern, ':')) == NULL || colon[1] != MAGIC) { 718 *ep = pattern - 2; 719 return -1; 720 } 721 *ep = colon + 3; /* skip MAGIC */ 722 len = (size_t)(colon - pattern); 723 724 for (cc = cclasses; cc->name != NULL; cc++) { 725 if (!strncmp(pattern, cc->name, len) && cc->name[len] == '\0') { 726 if (cc->isctype(test)) 727 rval = 1; 728 break; 729 } 730 } 731 if (cc->name == NULL) { 732 rval = -2; /* invalid character class */ 733 } 734 return rval; 735 } 736 737 static const unsigned char * 738 cclass(const unsigned char *p, int sub) 739 { 740 int c, d, rv, not, found = 0; 741 const unsigned char *orig_p = p; 742 743 if ((not = (ISMAGIC(*p) && *++p == '!'))) 744 p++; 745 do { 746 /* check for POSIX character class (e.g. [[:alpha:]]) */ 747 if ((p[0] == MAGIC && p[1] == '[' && p[2] == ':') || 748 (p[0] == '[' && p[1] == ':')) { 749 do { 750 const char *pp = p + (*p == MAGIC) + 2; 751 rv = posix_cclass(pp, sub, &p); 752 switch (rv) { 753 case 1: 754 found = 1; 755 break; 756 case -2: 757 return NULL; 758 } 759 } while (rv != -1 && p[0] == MAGIC && p[1] == '[' && p[2] == ':'); 760 if (p[0] == MAGIC && p[1] == ']') 761 break; 762 } 763 764 c = *p++; 765 if (ISMAGIC(c)) { 766 c = *p++; 767 if ((c & 0x80) && !ISMAGIC(c)) { 768 c &= 0x7f;/* extended pattern matching: *+?@! */ 769 /* XXX the ( char isn't handled as part of [] */ 770 if (c == ' ') /* simile for @: plain (..) */ 771 c = '(' /*)*/; 772 } 773 } 774 if (c == '\0') 775 /* No closing ] - act as if the opening [ was quoted */ 776 return sub == '[' ? orig_p : NULL; 777 if (ISMAGIC(p[0]) && p[1] == '-' && 778 (!ISMAGIC(p[2]) || p[3] != ']')) { 779 p += 2; /* MAGIC- */ 780 d = *p++; 781 if (ISMAGIC(d)) { 782 d = *p++; 783 if ((d & 0x80) && !ISMAGIC(d)) 784 d &= 0x7f; 785 } 786 /* POSIX says this is an invalid expression */ 787 if (c > d) 788 return NULL; 789 } else 790 d = c; 791 if (c == sub || (c <= sub && sub <= d)) 792 found = 1; 793 } while (!(ISMAGIC(p[0]) && p[1] == ']')); 794 795 return (found != not) ? p+2 : NULL; 796 } 797 798 /* Look for next ) or | (if match_sep) in *(foo|bar) pattern */ 799 const unsigned char * 800 pat_scan(const unsigned char *p, const unsigned char *pe, int match_sep) 801 { 802 int nest = 0; 803 804 for (; p < pe; p++) { 805 if (!ISMAGIC(*p)) 806 continue; 807 if ((*++p == /*(*/ ')' && nest-- == 0) || 808 (*p == '|' && match_sep && nest == 0)) 809 return ++p; 810 if ((*p & 0x80) && strchr("*+?@! ", *p & 0x7f)) 811 nest++; 812 } 813 return NULL; 814 } 815 816 /* 817 * quick sort of array of generic pointers to objects. 818 */ 819 void 820 qsortp(void **base, /* base address */ 821 size_t n, /* elements */ 822 int (*f) (const void *, const void *)) /* compare function */ 823 { 824 qsort(base, n, sizeof(char *), f); 825 } 826 827 int 828 xstrcmp(const void *p1, const void *p2) 829 { 830 return (strcmp(*(char **)p1, *(char **)p2)); 831 } 832 833 /* Initialize a Getopt structure */ 834 void 835 ksh_getopt_reset(Getopt *go, int flags) 836 { 837 go->optind = 1; 838 go->optarg = NULL; 839 go->p = 0; 840 go->flags = flags; 841 go->info = 0; 842 go->buf[1] = '\0'; 843 } 844 845 846 /* getopt() used for shell built-in commands, the getopts command, and 847 * command line options. 848 * A leading ':' in options means don't print errors, instead return '?' 849 * or ':' and set go->optarg to the offending option character. 850 * If GF_ERROR is set (and option doesn't start with :), errors result in 851 * a call to bi_errorf(). 852 * 853 * Non-standard features: 854 * - ';' is like ':' in options, except the argument is optional 855 * (if it isn't present, optarg is set to 0). 856 * Used for 'set -o'. 857 * - ',' is like ':' in options, except the argument always immediately 858 * follows the option character (optarg is set to the null string if 859 * the option is missing). 860 * Used for 'read -u2', 'print -u2' and fc -40. 861 * - '#' is like ':' in options, expect that the argument is optional 862 * and must start with a digit or be the string "unlimited". If the 863 * argument doesn't match, it is assumed to be missing and normal option 864 * processing continues (optarg is set to 0 if the option is missing). 865 * Used for 'typeset -LZ4' and 'ulimit -adunlimited'. 866 * - accepts +c as well as -c IF the GF_PLUSOPT flag is present. If an 867 * option starting with + is accepted, the GI_PLUS flag will be set 868 * in go->info. 869 */ 870 int 871 ksh_getopt(char **argv, Getopt *go, const char *options) 872 { 873 char c; 874 char *o; 875 876 if (go->p == 0 || (c = argv[go->optind - 1][go->p]) == '\0') { 877 char *arg = argv[go->optind], flag = arg ? *arg : '\0'; 878 879 go->p = 1; 880 if (flag == '-' && arg[1] == '-' && arg[2] == '\0') { 881 go->optind++; 882 go->p = 0; 883 go->info |= GI_MINUSMINUS; 884 return -1; 885 } 886 if (arg == NULL || 887 ((flag != '-' ) && /* neither a - nor a + (if + allowed) */ 888 (!(go->flags & GF_PLUSOPT) || flag != '+')) || 889 (c = arg[1]) == '\0') { 890 go->p = 0; 891 return -1; 892 } 893 go->optind++; 894 go->info &= ~(GI_MINUS|GI_PLUS); 895 go->info |= flag == '-' ? GI_MINUS : GI_PLUS; 896 } 897 go->p++; 898 if (c == '?' || c == ':' || c == ';' || c == ',' || c == '#' || 899 !(o = strchr(options, c))) { 900 if (options[0] == ':') { 901 go->buf[0] = c; 902 go->optarg = go->buf; 903 } else { 904 warningf(true, "%s%s-%c: unknown option", 905 (go->flags & GF_NONAME) ? "" : argv[0], 906 (go->flags & GF_NONAME) ? "" : ": ", c); 907 if (go->flags & GF_ERROR) 908 bi_errorf(NULL); 909 } 910 return '?'; 911 } 912 /* : means argument must be present, may be part of option argument 913 * or the next argument 914 * ; same as : but argument may be missing 915 * , means argument is part of option argument, and may be null. 916 */ 917 if (*++o == ':' || *o == ';') { 918 if (argv[go->optind - 1][go->p]) 919 go->optarg = argv[go->optind - 1] + go->p; 920 else if (argv[go->optind]) 921 go->optarg = argv[go->optind++]; 922 else if (*o == ';') 923 go->optarg = NULL; 924 else { 925 if (options[0] == ':') { 926 go->buf[0] = c; 927 go->optarg = go->buf; 928 return ':'; 929 } 930 warningf(true, "%s%s-`%c' requires argument", 931 (go->flags & GF_NONAME) ? "" : argv[0], 932 (go->flags & GF_NONAME) ? "" : ": ", c); 933 if (go->flags & GF_ERROR) 934 bi_errorf(NULL); 935 return '?'; 936 } 937 go->p = 0; 938 } else if (*o == ',') { 939 /* argument is attached to option character, even if null */ 940 go->optarg = argv[go->optind - 1] + go->p; 941 go->p = 0; 942 } else if (*o == '#') { 943 /* argument is optional and may be attached or unattached 944 * but must start with a digit. optarg is set to 0 if the 945 * argument is missing. 946 */ 947 if (argv[go->optind - 1][go->p]) { 948 if (digit(argv[go->optind - 1][go->p]) || 949 !strcmp(&argv[go->optind - 1][go->p], "unlimited")) { 950 go->optarg = argv[go->optind - 1] + go->p; 951 go->p = 0; 952 } else 953 go->optarg = NULL; 954 } else { 955 if (argv[go->optind] && (digit(argv[go->optind][0]) || 956 !strcmp(argv[go->optind], "unlimited"))) { 957 go->optarg = argv[go->optind++]; 958 go->p = 0; 959 } else 960 go->optarg = NULL; 961 } 962 } 963 return c; 964 } 965 966 /* print variable/alias value using necessary quotes 967 * (POSIX says they should be suitable for re-entry...) 968 * No trailing newline is printed. 969 */ 970 void 971 print_value_quoted(const char *s) 972 { 973 const char *p; 974 int inquote = 0; 975 976 /* Test if any quotes are needed */ 977 for (p = s; *p; p++) 978 if (ctype(*p, C_QUOTE)) 979 break; 980 if (!*p) { 981 shprintf("%s", s); 982 return; 983 } 984 for (p = s; *p; p++) { 985 if (*p == '\'') { 986 shprintf(inquote ? "'\\'" : "\\'"); 987 inquote = 0; 988 } else { 989 if (!inquote) { 990 shprintf("'"); 991 inquote = 1; 992 } 993 shf_putc(*p, shl_stdout); 994 } 995 } 996 if (inquote) 997 shprintf("'"); 998 } 999 1000 /* Print things in columns and rows - func() is called to format the ith 1001 * element 1002 */ 1003 void 1004 print_columns(struct shf *shf, int n, char *(*func) (void *, int, char *, int), 1005 void *arg, int max_width, int prefcol) 1006 { 1007 char *str = alloc(max_width + 1, ATEMP); 1008 int i; 1009 int r, c; 1010 int rows, cols; 1011 int nspace; 1012 int col_width; 1013 1014 /* max_width + 1 for the space. Note that no space 1015 * is printed after the last column to avoid problems 1016 * with terminals that have auto-wrap. 1017 */ 1018 cols = x_cols / (max_width + 1); 1019 if (!cols) 1020 cols = 1; 1021 rows = (n + cols - 1) / cols; 1022 if (prefcol && n && cols > rows) { 1023 int tmp = rows; 1024 1025 rows = cols; 1026 cols = tmp; 1027 if (rows > n) 1028 rows = n; 1029 } 1030 1031 col_width = max_width; 1032 if (cols == 1) 1033 col_width = 0; /* Don't pad entries in single column output. */ 1034 nspace = (x_cols - max_width * cols) / cols; 1035 if (nspace <= 0) 1036 nspace = 1; 1037 for (r = 0; r < rows; r++) { 1038 for (c = 0; c < cols; c++) { 1039 i = c * rows + r; 1040 if (i < n) { 1041 shf_fprintf(shf, "%-*s", 1042 col_width, 1043 (*func)(arg, i, str, max_width + 1)); 1044 if (c + 1 < cols) 1045 shf_fprintf(shf, "%*s", nspace, ""); 1046 } 1047 } 1048 shf_putchar('\n', shf); 1049 } 1050 afree(str, ATEMP); 1051 } 1052 1053 /* Strip any nul bytes from buf - returns new length (nbytes - # of nuls) */ 1054 int 1055 strip_nuls(char *buf, int nbytes) 1056 { 1057 char *dst; 1058 1059 if ((dst = memchr(buf, '\0', nbytes))) { 1060 char *end = buf + nbytes; 1061 char *p, *q; 1062 1063 for (p = dst; p < end; p = q) { 1064 /* skip a block of nulls */ 1065 while (++p < end && *p == '\0') 1066 ; 1067 /* find end of non-null block */ 1068 if (!(q = memchr(p, '\0', end - p))) 1069 q = end; 1070 memmove(dst, p, q - p); 1071 dst += q - p; 1072 } 1073 *dst = '\0'; 1074 return dst - buf; 1075 } 1076 return nbytes; 1077 } 1078 1079 /* Like read(2), but if read fails due to non-blocking flag, resets flag 1080 * and restarts read. 1081 */ 1082 int 1083 blocking_read(int fd, char *buf, int nbytes) 1084 { 1085 int ret; 1086 int tried_reset = 0; 1087 1088 while ((ret = read(fd, buf, nbytes)) == -1) { 1089 if (!tried_reset && errno == EAGAIN) { 1090 int oerrno = errno; 1091 if (reset_nonblock(fd) > 0) { 1092 tried_reset = 1; 1093 continue; 1094 } 1095 errno = oerrno; 1096 } 1097 break; 1098 } 1099 return ret; 1100 } 1101 1102 /* Reset the non-blocking flag on the specified file descriptor. 1103 * Returns -1 if there was an error, 0 if non-blocking wasn't set, 1104 * 1 if it was. 1105 */ 1106 int 1107 reset_nonblock(int fd) 1108 { 1109 int flags; 1110 1111 if ((flags = fcntl(fd, F_GETFL)) == -1) 1112 return -1; 1113 if (!(flags & O_NONBLOCK)) 1114 return 0; 1115 flags &= ~O_NONBLOCK; 1116 if (fcntl(fd, F_SETFL, flags) == -1) 1117 return -1; 1118 return 1; 1119 } 1120 1121 1122 /* Like getcwd(), except bsize is ignored if buf is 0 (PATH_MAX is used) */ 1123 char * 1124 ksh_get_wd(char *buf, int bsize) 1125 { 1126 char *b; 1127 char *ret; 1128 1129 /* Note: we could just use plain getcwd(), but then we'd had to 1130 * inject possibly allocated space into the ATEMP area. */ 1131 /* Assume getcwd() available */ 1132 if (!buf) { 1133 bsize = PATH_MAX; 1134 b = alloc(PATH_MAX + 1, ATEMP); 1135 } else 1136 b = buf; 1137 1138 ret = getcwd(b, bsize); 1139 1140 if (!buf) { 1141 if (ret) 1142 ret = aresize(b, strlen(b) + 1, ATEMP); 1143 else 1144 afree(b, ATEMP); 1145 } 1146 1147 return ret; 1148 } 1149