1 /* $OpenBSD: misc.c,v 1.37 2009/04/19 20:34:05 sthen Exp $ */ 2 /* $OpenBSD: path.c,v 1.12 2005/03/30 17:16:37 deraadt Exp $ */ 3 4 /*- 5 * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 6 * Thorsten Glaser <tg (at) mirbsd.org> 7 * 8 * Provided that these terms and disclaimer and all copyright notices 9 * are retained or reproduced in an accompanying document, permission 10 * is granted to deal in this work without restriction, including un- 11 * limited rights to use, publicly perform, distribute, sell, modify, 12 * merge, give away, or sublicence. 13 * 14 * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to 15 * the utmost extent permitted by applicable law, neither express nor 16 * implied; without malicious intent or gross negligence. In no event 17 * may a licensor, author or contributor be held liable for indirect, 18 * direct, other damage, loss, or other issues arising in any way out 19 * of dealing in the work, even if advised of the possibility of such 20 * damage or existence of a defect, except proven that it results out 21 * of said person's immediate fault when using the work as intended. 22 */ 23 24 #include "sh.h" 25 #if !HAVE_GETRUSAGE 26 #include <sys/times.h> 27 #endif 28 #if HAVE_GRP_H 29 #include <grp.h> 30 #endif 31 32 __RCSID("$MirOS: src/bin/mksh/misc.c,v 1.141 2010/07/17 22:09:36 tg Exp $"); 33 34 unsigned char chtypes[UCHAR_MAX + 1]; /* type bits for unsigned char */ 35 36 #if !HAVE_SETRESUGID 37 uid_t kshuid; 38 gid_t kshgid, kshegid; 39 #endif 40 41 static int do_gmatch(const unsigned char *, const unsigned char *, 42 const unsigned char *, const unsigned char *); 43 static const unsigned char *cclass(const unsigned char *, int); 44 #ifdef TIOCSCTTY 45 static void chvt(const char *); 46 #endif 47 48 /* 49 * Fast character classes 50 */ 51 void 52 setctypes(const char *s, int t) 53 { 54 unsigned int i; 55 56 if (t & C_IFS) { 57 for (i = 0; i < UCHAR_MAX + 1; i++) 58 chtypes[i] &= ~C_IFS; 59 chtypes[0] |= C_IFS; /* include \0 in C_IFS */ 60 } 61 while (*s != 0) 62 chtypes[(unsigned char)*s++] |= t; 63 } 64 65 void 66 initctypes(void) 67 { 68 int c; 69 70 for (c = 'a'; c <= 'z'; c++) 71 chtypes[c] |= C_ALPHA; 72 for (c = 'A'; c <= 'Z'; c++) 73 chtypes[c] |= C_ALPHA; 74 chtypes['_'] |= C_ALPHA; 75 setctypes("0123456789", C_DIGIT); 76 setctypes(" \t\n|&;<>()", C_LEX1); /* \0 added automatically */ 77 setctypes("*@#!$-?", C_VAR1); 78 setctypes(" \t\n", C_IFSWS); 79 setctypes("=-+?", C_SUBOP1); 80 setctypes("\t\n \"#$&'()*;<=>?[\\]`|", C_QUOTE); 81 } 82 83 /* called from XcheckN() to grow buffer */ 84 char * 85 Xcheck_grow_(XString *xsp, const char *xp, unsigned int more) 86 { 87 const char *old_beg = xsp->beg; 88 89 xsp->len += more > xsp->len ? more : xsp->len; 90 xsp->beg = aresize(xsp->beg, xsp->len + 8, xsp->areap); 91 xsp->end = xsp->beg + xsp->len; 92 return (xsp->beg + (xp - old_beg)); 93 } 94 95 #define SHFLAGS_DEFNS 96 #include "sh_flags.h" 97 98 const struct shoption options[] = { 99 #define SHFLAGS_ITEMS 100 #include "sh_flags.h" 101 }; 102 103 /* 104 * translate -o option into F* constant (also used for test -o option) 105 */ 106 size_t 107 option(const char *n) 108 { 109 size_t i; 110 111 if ((n[0] == '-' || n[0] == '+') && n[1] && !n[2]) { 112 for (i = 0; i < NELEM(options); i++) 113 if (options[i].c == n[1]) 114 return (i); 115 } else for (i = 0; i < NELEM(options); i++) 116 if (options[i].name && strcmp(options[i].name, n) == 0) 117 return (i); 118 119 return ((size_t)-1); 120 } 121 122 struct options_info { 123 int opt_width; 124 int opts[NELEM(options)]; 125 }; 126 127 static char *options_fmt_entry(char *, int, int, const void *); 128 static void printoptions(bool); 129 130 /* format a single select menu item */ 131 static char * 132 options_fmt_entry(char *buf, int buflen, int i, const void *arg) 133 { 134 const struct options_info *oi = (const struct options_info *)arg; 135 136 shf_snprintf(buf, buflen, "%-*s %s", 137 oi->opt_width, options[oi->opts[i]].name, 138 Flag(oi->opts[i]) ? "on" : "off"); 139 return (buf); 140 } 141 142 static void 143 printoptions(bool verbose) 144 { 145 int i = 0; 146 147 if (verbose) { 148 int n = 0, len, octs = 0; 149 struct options_info oi; 150 151 /* verbose version */ 152 shf_puts("Current option settings\n", shl_stdout); 153 154 oi.opt_width = 0; 155 while (i < (int)NELEM(options)) { 156 if (options[i].name) { 157 oi.opts[n++] = i; 158 len = strlen(options[i].name); 159 if (len > octs) 160 octs = len; 161 len = utf_mbswidth(options[i].name); 162 if (len > oi.opt_width) 163 oi.opt_width = len; 164 } 165 ++i; 166 } 167 print_columns(shl_stdout, n, options_fmt_entry, &oi, 168 octs + 4, oi.opt_width + 4, true); 169 } else { 170 /* short version la AT&T ksh93 */ 171 shf_puts("set", shl_stdout); 172 while (i < (int)NELEM(options)) { 173 if (Flag(i) && options[i].name) 174 shprintf(" -o %s", options[i].name); 175 ++i; 176 } 177 shf_putc('\n', shl_stdout); 178 } 179 } 180 181 char * 182 getoptions(void) 183 { 184 unsigned int i; 185 char m[(int) FNFLAGS + 1]; 186 char *cp = m; 187 188 for (i = 0; i < NELEM(options); i++) 189 if (options[i].c && Flag(i)) 190 *cp++ = options[i].c; 191 strndupx(cp, m, cp - m, ATEMP); 192 return (cp); 193 } 194 195 /* change a Flag(*) value; takes care of special actions */ 196 void 197 change_flag(enum sh_flag f, int what, unsigned int newval) 198 { 199 unsigned char oldval; 200 201 oldval = Flag(f); 202 Flag(f) = newval ? 1 : 0; /* needed for tristates */ 203 #ifndef MKSH_UNEMPLOYED 204 if (f == FMONITOR) { 205 if (what != OF_CMDLINE && newval != oldval) 206 j_change(); 207 } else 208 #endif 209 if (( 210 #if !MKSH_S_NOVI 211 f == FVI || 212 #endif 213 f == FEMACS || f == FGMACS) && newval) { 214 #if !MKSH_S_NOVI 215 Flag(FVI) = 216 #endif 217 Flag(FEMACS) = Flag(FGMACS) = 0; 218 Flag(f) = (unsigned char)newval; 219 } else if (f == FPRIVILEGED && oldval && !newval) { 220 /* Turning off -p? */ 221 #if HAVE_SETRESUGID 222 gid_t kshegid = getgid(); 223 224 setresgid(kshegid, kshegid, kshegid); 225 #if HAVE_SETGROUPS 226 setgroups(1, &kshegid); 227 #endif 228 setresuid(ksheuid, ksheuid, ksheuid); 229 #else 230 seteuid(ksheuid = kshuid = getuid()); 231 setuid(ksheuid); 232 setegid(kshegid = kshgid = getgid()); 233 setgid(kshegid); 234 #endif 235 } else if ((f == FPOSIX || f == FSH) && newval) { 236 Flag(FPOSIX) = Flag(FSH) = Flag(FBRACEEXPAND) = 0; 237 Flag(f) = (unsigned char)newval; 238 } 239 /* Changing interactive flag? */ 240 if (f == FTALKING) { 241 if ((what == OF_CMDLINE || what == OF_SET) && procpid == kshpid) 242 Flag(FTALKING_I) = (unsigned char)newval; 243 } 244 } 245 246 /* Parse command line & set command arguments. Returns the index of 247 * non-option arguments, -1 if there is an error. 248 */ 249 int 250 parse_args(const char **argv, 251 int what, /* OF_CMDLINE or OF_SET */ 252 bool *setargsp) 253 { 254 static char cmd_opts[NELEM(options) + 5]; /* o:T:\0 */ 255 static char set_opts[NELEM(options) + 6]; /* A:o;s\0 */ 256 char set, *opts; 257 const char *array = NULL; 258 Getopt go; 259 size_t i; 260 int optc, sortargs = 0, arrayset = 0; 261 262 /* First call? Build option strings... */ 263 if (cmd_opts[0] == '\0') { 264 char *p = cmd_opts, *q = set_opts; 265 266 /* see cmd_opts[] declaration */ 267 *p++ = 'o'; 268 *p++ = ':'; 269 #if !defined(MKSH_SMALL) || defined(TIOCSCTTY) 270 *p++ = 'T'; 271 *p++ = ':'; 272 #endif 273 /* see set_opts[] declaration */ 274 *q++ = 'A'; 275 *q++ = ':'; 276 *q++ = 'o'; 277 *q++ = ';'; 278 *q++ = 's'; 279 280 for (i = 0; i < NELEM(options); i++) { 281 if (options[i].c) { 282 if (options[i].flags & OF_CMDLINE) 283 *p++ = options[i].c; 284 if (options[i].flags & OF_SET) 285 *q++ = options[i].c; 286 } 287 } 288 *p = '\0'; 289 *q = '\0'; 290 } 291 292 if (what == OF_CMDLINE) { 293 const char *p = argv[0], *q; 294 /* Set FLOGIN before parsing options so user can clear 295 * flag using +l. 296 */ 297 if (*p != '-') 298 for (q = p; *q; ) 299 if (*q++ == '/') 300 p = q; 301 Flag(FLOGIN) = (*p == '-'); 302 opts = cmd_opts; 303 } else if (what == OF_FIRSTTIME) { 304 opts = cmd_opts; 305 } else 306 opts = set_opts; 307 ksh_getopt_reset(&go, GF_ERROR|GF_PLUSOPT); 308 while ((optc = ksh_getopt(argv, &go, opts)) != -1) { 309 set = (go.info & GI_PLUS) ? 0 : 1; 310 switch (optc) { 311 case 'A': 312 if (what == OF_FIRSTTIME) 313 break; 314 arrayset = set ? 1 : -1; 315 array = go.optarg; 316 break; 317 318 case 'o': 319 if (what == OF_FIRSTTIME) 320 break; 321 if (go.optarg == NULL) { 322 /* lone -o: print options 323 * 324 * Note that on the command line, -o requires 325 * an option (ie, can't get here if what is 326 * OF_CMDLINE). 327 */ 328 printoptions(set); 329 break; 330 } 331 i = option(go.optarg); 332 if ((enum sh_flag)i == FARC4RANDOM) { 333 warningf(true, "Do not use set o arc4random," 334 " it will be removed in the next version" 335 " of mksh!"); 336 return (0); 337 } 338 if ((i != (size_t)-1) && set == Flag(i)) 339 /* Don't check the context if the flag 340 * isn't changing - makes "set -o interactive" 341 * work if you're already interactive. Needed 342 * if the output of "set +o" is to be used. 343 */ 344 ; 345 else if ((i != (size_t)-1) && (options[i].flags & what)) 346 change_flag((enum sh_flag)i, what, set); 347 else { 348 bi_errorf("%s: bad option", go.optarg); 349 return (-1); 350 } 351 break; 352 353 #if !defined(MKSH_SMALL) || defined(TIOCSCTTY) 354 case 'T': 355 if (what != OF_FIRSTTIME) 356 break; 357 #ifndef TIOCSCTTY 358 errorf("no TIOCSCTTY ioctl"); 359 #else 360 change_flag(FTALKING, OF_CMDLINE, 1); 361 chvt(go.optarg); 362 break; 363 #endif 364 #endif 365 366 case '?': 367 return (-1); 368 369 default: 370 if (what == OF_FIRSTTIME) 371 break; 372 /* -s: sort positional params (AT&T ksh stupidity) */ 373 if (what == OF_SET && optc == 's') { 374 sortargs = 1; 375 break; 376 } 377 for (i = 0; i < NELEM(options); i++) 378 if (optc == options[i].c && 379 (what & options[i].flags)) { 380 change_flag((enum sh_flag)i, what, set); 381 break; 382 } 383 if (i == NELEM(options)) 384 internal_errorf("parse_args: '%c'", optc); 385 } 386 } 387 if (!(go.info & GI_MINUSMINUS) && argv[go.optind] && 388 (argv[go.optind][0] == '-' || argv[go.optind][0] == '+') && 389 argv[go.optind][1] == '\0') { 390 /* lone - clears -v and -x flags */ 391 if (argv[go.optind][0] == '-') 392 Flag(FVERBOSE) = Flag(FXTRACE) = 0; 393 /* set skips lone - or + option */ 394 go.optind++; 395 } 396 if (setargsp) 397 /* -- means set $#/$* even if there are no arguments */ 398 *setargsp = !arrayset && ((go.info & GI_MINUSMINUS) || 399 argv[go.optind]); 400 401 if (arrayset && (!*array || *skip_varname(array, false))) { 402 bi_errorf("%s: is not an identifier", array); 403 return (-1); 404 } 405 if (sortargs) { 406 for (i = go.optind; argv[i]; i++) 407 ; 408 qsort(&argv[go.optind], i - go.optind, sizeof(void *), 409 xstrcmp); 410 } 411 if (arrayset) 412 go.optind += set_array(array, arrayset > 0 ? true : false, 413 argv + go.optind); 414 415 return (go.optind); 416 } 417 418 /* parse a decimal number: returns 0 if string isn't a number, 1 otherwise */ 419 int 420 getn(const char *s, int *ai) 421 { 422 int i, c, rv = 0; 423 bool neg = false; 424 425 do { 426 c = *s++; 427 } while (ksh_isspace(c)); 428 if (c == '-') { 429 neg = true; 430 c = *s++; 431 } else if (c == '+') 432 c = *s++; 433 *ai = i = 0; 434 do { 435 if (!ksh_isdigit(c)) 436 goto getn_out; 437 i *= 10; 438 if (i < *ai) 439 /* overflow */ 440 goto getn_out; 441 i += c - '0'; 442 *ai = i; 443 } while ((c = *s++)); 444 rv = 1; 445 446 getn_out: 447 if (neg) 448 *ai = -*ai; 449 return (rv); 450 } 451 452 /* getn() that prints error */ 453 int 454 bi_getn(const char *as, int *ai) 455 { 456 int rv; 457 458 if (!(rv = getn(as, ai))) 459 bi_errorf("%s: bad number", as); 460 return (rv); 461 } 462 463 /* -------- gmatch.c -------- */ 464 465 /* 466 * int gmatch(string, pattern) 467 * char *string, *pattern; 468 * 469 * Match a pattern as in sh(1). 470 * pattern character are prefixed with MAGIC by expand. 471 */ 472 473 int 474 gmatchx(const char *s, const char *p, bool isfile) 475 { 476 const char *se, *pe; 477 478 if (s == NULL || p == NULL) 479 return (0); 480 481 se = s + strlen(s); 482 pe = p + strlen(p); 483 /* isfile is false iff no syntax check has been done on 484 * the pattern. If check fails, just to a strcmp(). 485 */ 486 if (!isfile && !has_globbing(p, pe)) { 487 size_t len = pe - p + 1; 488 char tbuf[64]; 489 char *t = len <= sizeof(tbuf) ? tbuf : alloc(len, ATEMP); 490 debunk(t, p, len); 491 return (!strcmp(t, s)); 492 } 493 return (do_gmatch((const unsigned char *) s, (const unsigned char *) se, 494 (const unsigned char *) p, (const unsigned char *) pe)); 495 } 496 497 /* Returns if p is a syntacticly correct globbing pattern, false 498 * if it contains no pattern characters or if there is a syntax error. 499 * Syntax errors are: 500 * - [ with no closing ] 501 * - imbalanced $(...) expression 502 * - [...] and *(...) not nested (eg, [a$(b|]c), *(a[b|c]d)) 503 */ 504 /*XXX 505 - if no magic, 506 if dest given, copy to dst 507 return ? 508 - if magic && (no globbing || syntax error) 509 debunk to dst 510 return ? 511 - return ? 512 */ 513 int 514 has_globbing(const char *xp, const char *xpe) 515 { 516 const unsigned char *p = (const unsigned char *) xp; 517 const unsigned char *pe = (const unsigned char *) xpe; 518 int c; 519 int nest = 0, bnest = 0; 520 int saw_glob = 0; 521 int in_bracket = 0; /* inside [...] */ 522 523 for (; p < pe; p++) { 524 if (!ISMAGIC(*p)) 525 continue; 526 if ((c = *++p) == '*' || c == '?') 527 saw_glob = 1; 528 else if (c == '[') { 529 if (!in_bracket) { 530 saw_glob = 1; 531 in_bracket = 1; 532 if (ISMAGIC(p[1]) && p[2] == NOT) 533 p += 2; 534 if (ISMAGIC(p[1]) && p[2] == ']') 535 p += 2; 536 } 537 /* XXX Do we need to check ranges here? POSIX Q */ 538 } else if (c == ']') { 539 if (in_bracket) { 540 if (bnest) /* [a*(b]) */ 541 return (0); 542 in_bracket = 0; 543 } 544 } else if ((c & 0x80) && vstrchr("*+?@! ", c & 0x7f)) { 545 saw_glob = 1; 546 if (in_bracket) 547 bnest++; 548 else 549 nest++; 550 } else if (c == '|') { 551 if (in_bracket && !bnest) /* *(a[foo|bar]) */ 552 return (0); 553 } else if (c == /*(*/ ')') { 554 if (in_bracket) { 555 if (!bnest--) /* *(a[b)c] */ 556 return (0); 557 } else if (nest) 558 nest--; 559 } 560 /* 561 * else must be a MAGIC-MAGIC, or MAGIC-!, 562 * MAGIC--, MAGIC-], MAGIC-{, MAGIC-, MAGIC-} 563 */ 564 } 565 return (saw_glob && !in_bracket && !nest); 566 } 567 568 /* Function must return either 0 or 1 (assumed by code for 0x80|'!') */ 569 static int 570 do_gmatch(const unsigned char *s, const unsigned char *se, 571 const unsigned char *p, const unsigned char *pe) 572 { 573 int sc, pc; 574 const unsigned char *prest, *psub, *pnext; 575 const unsigned char *srest; 576 577 if (s == NULL || p == NULL) 578 return (0); 579 while (p < pe) { 580 pc = *p++; 581 sc = s < se ? *s : '\0'; 582 s++; 583 if (!ISMAGIC(pc)) { 584 if (sc != pc) 585 return (0); 586 continue; 587 } 588 switch (*p++) { 589 case '[': 590 if (sc == 0 || (p = cclass(p, sc)) == NULL) 591 return (0); 592 break; 593 594 case '?': 595 if (sc == 0) 596 return (0); 597 if (UTFMODE) { 598 --s; 599 s += utf_ptradj((const void *)s); 600 } 601 break; 602 603 case '*': 604 if (p == pe) 605 return (1); 606 s--; 607 do { 608 if (do_gmatch(s, se, p, pe)) 609 return (1); 610 } while (s++ < se); 611 return (0); 612 613 /** 614 * [*+?@!](pattern|pattern|..) 615 * This is also needed for ${..%..}, etc. 616 */ 617 case 0x80|'+': /* matches one or more times */ 618 case 0x80|'*': /* matches zero or more times */ 619 if (!(prest = pat_scan(p, pe, 0))) 620 return (0); 621 s--; 622 /* take care of zero matches */ 623 if (p[-1] == (0x80 | '*') && 624 do_gmatch(s, se, prest, pe)) 625 return (1); 626 for (psub = p; ; psub = pnext) { 627 pnext = pat_scan(psub, pe, 1); 628 for (srest = s; srest <= se; srest++) { 629 if (do_gmatch(s, srest, psub, pnext - 2) && 630 (do_gmatch(srest, se, prest, pe) || 631 (s != srest && do_gmatch(srest, 632 se, p - 2, pe)))) 633 return (1); 634 } 635 if (pnext == prest) 636 break; 637 } 638 return (0); 639 640 case 0x80|'?': /* matches zero or once */ 641 case 0x80|'@': /* matches one of the patterns */ 642 case 0x80|' ': /* simile for @ */ 643 if (!(prest = pat_scan(p, pe, 0))) 644 return (0); 645 s--; 646 /* Take care of zero matches */ 647 if (p[-1] == (0x80 | '?') && 648 do_gmatch(s, se, prest, pe)) 649 return (1); 650 for (psub = p; ; psub = pnext) { 651 pnext = pat_scan(psub, pe, 1); 652 srest = prest == pe ? se : s; 653 for (; srest <= se; srest++) { 654 if (do_gmatch(s, srest, psub, pnext - 2) && 655 do_gmatch(srest, se, prest, pe)) 656 return (1); 657 } 658 if (pnext == prest) 659 break; 660 } 661 return (0); 662 663 case 0x80|'!': /* matches none of the patterns */ 664 if (!(prest = pat_scan(p, pe, 0))) 665 return (0); 666 s--; 667 for (srest = s; srest <= se; srest++) { 668 int matched = 0; 669 670 for (psub = p; ; psub = pnext) { 671 pnext = pat_scan(psub, pe, 1); 672 if (do_gmatch(s, srest, psub, 673 pnext - 2)) { 674 matched = 1; 675 break; 676 } 677 if (pnext == prest) 678 break; 679 } 680 if (!matched && 681 do_gmatch(srest, se, prest, pe)) 682 return (1); 683 } 684 return (0); 685 686 default: 687 if (sc != p[-1]) 688 return (0); 689 break; 690 } 691 } 692 return (s == se); 693 } 694 695 static const unsigned char * 696 cclass(const unsigned char *p, int sub) 697 { 698 int c, d, notp, found = 0; 699 const unsigned char *orig_p = p; 700 701 if ((notp = (ISMAGIC(*p) && *++p == NOT))) 702 p++; 703 do { 704 c = *p++; 705 if (ISMAGIC(c)) { 706 c = *p++; 707 if ((c & 0x80) && !ISMAGIC(c)) { 708 c &= 0x7f;/* extended pattern matching: *+?@! */ 709 /* XXX the ( char isn't handled as part of [] */ 710 if (c == ' ') /* simile for @: plain (..) */ 711 c = '(' /*)*/; 712 } 713 } 714 if (c == '\0') 715 /* No closing ] - act as if the opening [ was quoted */ 716 return (sub == '[' ? orig_p : NULL); 717 if (ISMAGIC(p[0]) && p[1] == '-' && 718 (!ISMAGIC(p[2]) || p[3] != ']')) { 719 p += 2; /* MAGIC- */ 720 d = *p++; 721 if (ISMAGIC(d)) { 722 d = *p++; 723 if ((d & 0x80) && !ISMAGIC(d)) 724 d &= 0x7f; 725 } 726 /* POSIX says this is an invalid expression */ 727 if (c > d) 728 return (NULL); 729 } else 730 d = c; 731 if (c == sub || (c <= sub && sub <= d)) 732 found = 1; 733 } while (!(ISMAGIC(p[0]) && p[1] == ']')); 734 735 return ((found != notp) ? p+2 : NULL); 736 } 737 738 /* Look for next ) or | (if match_sep) in *(foo|bar) pattern */ 739 const unsigned char * 740 pat_scan(const unsigned char *p, const unsigned char *pe, int match_sep) 741 { 742 int nest = 0; 743 744 for (; p < pe; p++) { 745 if (!ISMAGIC(*p)) 746 continue; 747 if ((*++p == /*(*/ ')' && nest-- == 0) || 748 (*p == '|' && match_sep && nest == 0)) 749 return (p + 1); 750 if ((*p & 0x80) && vstrchr("*+?@! ", *p & 0x7f)) 751 nest++; 752 } 753 return (NULL); 754 } 755 756 int 757 xstrcmp(const void *p1, const void *p2) 758 { 759 return (strcmp(*(const char * const *)p1, *(const char * const *)p2)); 760 } 761 762 /* Initialise a Getopt structure */ 763 void 764 ksh_getopt_reset(Getopt *go, int flags) 765 { 766 go->optind = 1; 767 go->optarg = NULL; 768 go->p = 0; 769 go->flags = flags; 770 go->info = 0; 771 go->buf[1] = '\0'; 772 } 773 774 775 /* getopt() used for shell built-in commands, the getopts command, and 776 * command line options. 777 * A leading ':' in options means don't print errors, instead return '?' 778 * or ':' and set go->optarg to the offending option character. 779 * If GF_ERROR is set (and option doesn't start with :), errors result in 780 * a call to bi_errorf(). 781 * 782 * Non-standard features: 783 * - ';' is like ':' in options, except the argument is optional 784 * (if it isn't present, optarg is set to 0). 785 * Used for 'set -o'. 786 * - ',' is like ':' in options, except the argument always immediately 787 * follows the option character (optarg is set to the null string if 788 * the option is missing). 789 * Used for 'read -u2', 'print -u2' and fc -40. 790 * - '#' is like ':' in options, expect that the argument is optional 791 * and must start with a digit. If the argument doesn't start with a 792 * digit, it is assumed to be missing and normal option processing 793 * continues (optarg is set to 0 if the option is missing). 794 * Used for 'typeset -LZ4'. 795 * - accepts +c as well as -c IF the GF_PLUSOPT flag is present. If an 796 * option starting with + is accepted, the GI_PLUS flag will be set 797 * in go->info. 798 */ 799 int 800 ksh_getopt(const char **argv, Getopt *go, const char *optionsp) 801 { 802 char c; 803 const char *o; 804 805 if (go->p == 0 || (c = argv[go->optind - 1][go->p]) == '\0') { 806 const char *arg = argv[go->optind], flag = arg ? *arg : '\0'; 807 808 go->p = 1; 809 if (flag == '-' && arg[1] == '-' && arg[2] == '\0') { 810 go->optind++; 811 go->p = 0; 812 go->info |= GI_MINUSMINUS; 813 return (-1); 814 } 815 if (arg == NULL || 816 ((flag != '-' ) && /* neither a - nor a + (if + allowed) */ 817 (!(go->flags & GF_PLUSOPT) || flag != '+')) || 818 (c = arg[1]) == '\0') { 819 go->p = 0; 820 return (-1); 821 } 822 go->optind++; 823 go->info &= ~(GI_MINUS|GI_PLUS); 824 go->info |= flag == '-' ? GI_MINUS : GI_PLUS; 825 } 826 go->p++; 827 if (c == '?' || c == ':' || c == ';' || c == ',' || c == '#' || 828 !(o = cstrchr(optionsp, c))) { 829 if (optionsp[0] == ':') { 830 go->buf[0] = c; 831 go->optarg = go->buf; 832 } else { 833 warningf(true, "%s%s-%c: unknown option", 834 (go->flags & GF_NONAME) ? "" : argv[0], 835 (go->flags & GF_NONAME) ? "" : ": ", c); 836 if (go->flags & GF_ERROR) 837 bi_errorfz(); 838 } 839 return ('?'); 840 } 841 /* : means argument must be present, may be part of option argument 842 * or the next argument 843 * ; same as : but argument may be missing 844 * , means argument is part of option argument, and may be null. 845 */ 846 if (*++o == ':' || *o == ';') { 847 if (argv[go->optind - 1][go->p]) 848 go->optarg = argv[go->optind - 1] + go->p; 849 else if (argv[go->optind]) 850 go->optarg = argv[go->optind++]; 851 else if (*o == ';') 852 go->optarg = NULL; 853 else { 854 if (optionsp[0] == ':') { 855 go->buf[0] = c; 856 go->optarg = go->buf; 857 return (':'); 858 } 859 warningf(true, "%s%s-'%c' requires argument", 860 (go->flags & GF_NONAME) ? "" : argv[0], 861 (go->flags & GF_NONAME) ? "" : ": ", c); 862 if (go->flags & GF_ERROR) 863 bi_errorfz(); 864 return ('?'); 865 } 866 go->p = 0; 867 } else if (*o == ',') { 868 /* argument is attached to option character, even if null */ 869 go->optarg = argv[go->optind - 1] + go->p; 870 go->p = 0; 871 } else if (*o == '#') { 872 /* argument is optional and may be attached or unattached 873 * but must start with a digit. optarg is set to 0 if the 874 * argument is missing. 875 */ 876 if (argv[go->optind - 1][go->p]) { 877 if (ksh_isdigit(argv[go->optind - 1][go->p])) { 878 go->optarg = argv[go->optind - 1] + go->p; 879 go->p = 0; 880 } else 881 go->optarg = NULL; 882 } else { 883 if (argv[go->optind] && ksh_isdigit(argv[go->optind][0])) { 884 go->optarg = argv[go->optind++]; 885 go->p = 0; 886 } else 887 go->optarg = NULL; 888 } 889 } 890 return (c); 891 } 892 893 /* print variable/alias value using necessary quotes 894 * (POSIX says they should be suitable for re-entry...) 895 * No trailing newline is printed. 896 */ 897 void 898 print_value_quoted(const char *s) 899 { 900 const char *p; 901 int inquote = 0; 902 903 /* Test if any quotes are needed */ 904 for (p = s; *p; p++) 905 if (ctype(*p, C_QUOTE)) 906 break; 907 if (!*p) { 908 shf_puts(s, shl_stdout); 909 return; 910 } 911 for (p = s; *p; p++) { 912 if (*p == '\'') { 913 if (inquote) 914 shf_putc('\'', shl_stdout); 915 shf_putc('\\', shl_stdout); 916 inquote = 0; 917 } else if (!inquote) { 918 shf_putc('\'', shl_stdout); 919 inquote = 1; 920 } 921 shf_putc(*p, shl_stdout); 922 } 923 if (inquote) 924 shf_putc('\'', shl_stdout); 925 } 926 927 /* 928 * Print things in columns and rows - func() is called to format 929 * the i-th element 930 */ 931 void 932 print_columns(struct shf *shf, int n, 933 char *(*func)(char *, int, int, const void *), 934 const void *arg, int max_oct, int max_col, bool prefcol) 935 { 936 int i, r, c, rows, cols, nspace; 937 char *str; 938 939 if (n <= 0) { 940 #ifndef MKSH_SMALL 941 internal_warningf("print_columns called with n=%d <= 0", n); 942 #endif 943 return; 944 } 945 946 ++max_oct; 947 str = alloc(max_oct, ATEMP); 948 949 /* ensure x_cols is valid first */ 950 if (x_cols < MIN_COLS) 951 change_winsz(); 952 953 /* 954 * We use (max_col + 1) to consider the space separator. 955 * Note that no space is printed after the last column 956 * to avoid problems with terminals that have auto-wrap. 957 */ 958 cols = x_cols / (max_col + 1); 959 960 /* if we can only print one column anyway, skip the goo */ 961 if (cols < 2) { 962 for (i = 0; i < n; ++i) 963 shf_fprintf(shf, "%s \n", 964 (*func)(str, max_oct, i, arg)); 965 goto out; 966 } 967 968 rows = (n + cols - 1) / cols; 969 if (prefcol && cols > rows) { 970 i = rows; 971 rows = cols > n ? n : cols; 972 cols = i; 973 } 974 975 max_col = -max_col; 976 nspace = (x_cols + max_col * cols) / cols; 977 if (nspace <= 0) 978 nspace = 1; 979 for (r = 0; r < rows; r++) { 980 for (c = 0; c < cols; c++) { 981 i = c * rows + r; 982 if (i < n) { 983 shf_fprintf(shf, "%*s", max_col, 984 (*func)(str, max_oct, i, arg)); 985 if (c + 1 < cols) 986 shf_fprintf(shf, "%*s", nspace, null); 987 } 988 } 989 shf_putchar('\n', shf); 990 } 991 out: 992 afree(str, ATEMP); 993 } 994 995 /* Strip any nul bytes from buf - returns new length (nbytes - # of nuls) */ 996 void 997 strip_nuls(char *buf, int nbytes) 998 { 999 char *dst; 1000 1001 /* nbytes check because some systems (older FreeBSDs) have a buggy 1002 * memchr() 1003 */ 1004 if (nbytes && (dst = memchr(buf, '\0', nbytes))) { 1005 char *end = buf + nbytes; 1006 char *p, *q; 1007 1008 for (p = dst; p < end; p = q) { 1009 /* skip a block of nulls */ 1010 while (++p < end && *p == '\0') 1011 ; 1012 /* find end of non-null block */ 1013 if (!(q = memchr(p, '\0', end - p))) 1014 q = end; 1015 memmove(dst, p, q - p); 1016 dst += q - p; 1017 } 1018 *dst = '\0'; 1019 } 1020 } 1021 1022 /* Like read(2), but if read fails due to non-blocking flag, resets flag 1023 * and restarts read. 1024 */ 1025 int 1026 blocking_read(int fd, char *buf, int nbytes) 1027 { 1028 int ret; 1029 int tried_reset = 0; 1030 1031 while ((ret = read(fd, buf, nbytes)) < 0) { 1032 if (!tried_reset && errno == EAGAIN) { 1033 if (reset_nonblock(fd) > 0) { 1034 tried_reset = 1; 1035 continue; 1036 } 1037 errno = EAGAIN; 1038 } 1039 break; 1040 } 1041 return (ret); 1042 } 1043 1044 /* Reset the non-blocking flag on the specified file descriptor. 1045 * Returns -1 if there was an error, 0 if non-blocking wasn't set, 1046 * 1 if it was. 1047 */ 1048 int 1049 reset_nonblock(int fd) 1050 { 1051 int flags; 1052 1053 if ((flags = fcntl(fd, F_GETFL, 0)) < 0) 1054 return (-1); 1055 if (!(flags & O_NONBLOCK)) 1056 return (0); 1057 flags &= ~O_NONBLOCK; 1058 if (fcntl(fd, F_SETFL, flags) < 0) 1059 return (-1); 1060 return (1); 1061 } 1062 1063 1064 /* Like getcwd(), except bsize is ignored if buf is 0 (PATH_MAX is used) */ 1065 char * 1066 ksh_get_wd(size_t *dlen) 1067 { 1068 char *ret, *b; 1069 size_t len = 1; 1070 1071 #ifdef NO_PATH_MAX 1072 if ((b = get_current_dir_name())) { 1073 len = strlen(b) + 1; 1074 strndupx(ret, b, len - 1, ATEMP); 1075 free(b); 1076 } else 1077 ret = NULL; 1078 #else 1079 if ((ret = getcwd((b = alloc(PATH_MAX + 1, ATEMP)), PATH_MAX))) 1080 ret = aresize(b, len = (strlen(b) + 1), ATEMP); 1081 else 1082 afree(b, ATEMP); 1083 #endif 1084 1085 if (dlen) 1086 *dlen = len; 1087 return (ret); 1088 } 1089 1090 /* 1091 * Makes a filename into result using the following algorithm. 1092 * - make result NULL 1093 * - if file starts with '/', append file to result & set cdpathp to NULL 1094 * - if file starts with ./ or ../ append cwd and file to result 1095 * and set cdpathp to NULL 1096 * - if the first element of cdpathp doesnt start with a '/' xx or '.' xx 1097 * then cwd is appended to result. 1098 * - the first element of cdpathp is appended to result 1099 * - file is appended to result 1100 * - cdpathp is set to the start of the next element in cdpathp (or NULL 1101 * if there are no more elements. 1102 * The return value indicates whether a non-null element from cdpathp 1103 * was appended to result. 1104 */ 1105 int 1106 make_path(const char *cwd, const char *file, 1107 char **cdpathp, /* & of : separated list */ 1108 XString *xsp, 1109 int *phys_pathp) 1110 { 1111 int rval = 0; 1112 bool use_cdpath = true; 1113 char *plist; 1114 int len, plen = 0; 1115 char *xp = Xstring(*xsp, xp); 1116 1117 if (!file) 1118 file = null; 1119 1120 if (file[0] == '/') { 1121 *phys_pathp = 0; 1122 use_cdpath = false; 1123 } else { 1124 if (file[0] == '.') { 1125 char c = file[1]; 1126 1127 if (c == '.') 1128 c = file[2]; 1129 if (c == '/' || c == '\0') 1130 use_cdpath = false; 1131 } 1132 1133 plist = *cdpathp; 1134 if (!plist) 1135 use_cdpath = false; 1136 else if (use_cdpath) { 1137 char *pend; 1138 1139 for (pend = plist; *pend && *pend != ':'; pend++) 1140 ; 1141 plen = pend - plist; 1142 *cdpathp = *pend ? pend + 1 : NULL; 1143 } 1144 1145 if ((!use_cdpath || !plen || plist[0] != '/') && 1146 (cwd && *cwd)) { 1147 len = strlen(cwd); 1148 XcheckN(*xsp, xp, len); 1149 memcpy(xp, cwd, len); 1150 xp += len; 1151 if (cwd[len - 1] != '/') 1152 Xput(*xsp, xp, '/'); 1153 } 1154 *phys_pathp = Xlength(*xsp, xp); 1155 if (use_cdpath && plen) { 1156 XcheckN(*xsp, xp, plen); 1157 memcpy(xp, plist, plen); 1158 xp += plen; 1159 if (plist[plen - 1] != '/') 1160 Xput(*xsp, xp, '/'); 1161 rval = 1; 1162 } 1163 } 1164 1165 len = strlen(file) + 1; 1166 XcheckN(*xsp, xp, len); 1167 memcpy(xp, file, len); 1168 1169 if (!use_cdpath) 1170 *cdpathp = NULL; 1171 1172 return (rval); 1173 } 1174 1175 /* 1176 * Simplify pathnames containing "." and ".." entries. 1177 * ie, simplify_path("/a/b/c/./../d/..") returns "/a/b" 1178 */ 1179 void 1180 simplify_path(char *pathl) 1181 { 1182 char *cur, *t; 1183 bool isrooted; 1184 char *very_start = pathl, *start; 1185 1186 if (!*pathl) 1187 return; 1188 1189 if ((isrooted = pathl[0] == '/')) 1190 very_start++; 1191 1192 /* Before After 1193 * /foo/ /foo 1194 * /foo/../../bar /bar 1195 * /foo/./blah/.. /foo 1196 * . . 1197 * .. .. 1198 * ./foo foo 1199 * foo/../../../bar ../../bar 1200 */ 1201 1202 for (cur = t = start = very_start; ; ) { 1203 /* treat multiple '/'s as one '/' */ 1204 while (*t == '/') 1205 t++; 1206 1207 if (*t == '\0') { 1208 if (cur == pathl) 1209 /* convert empty path to dot */ 1210 *cur++ = '.'; 1211 *cur = '\0'; 1212 break; 1213 } 1214 1215 if (t[0] == '.') { 1216 if (!t[1] || t[1] == '/') { 1217 t += 1; 1218 continue; 1219 } else if (t[1] == '.' && (!t[2] || t[2] == '/')) { 1220 if (!isrooted && cur == start) { 1221 if (cur != very_start) 1222 *cur++ = '/'; 1223 *cur++ = '.'; 1224 *cur++ = '.'; 1225 start = cur; 1226 } else if (cur != start) 1227 while (--cur > start && *cur != '/') 1228 ; 1229 t += 2; 1230 continue; 1231 } 1232 } 1233 1234 if (cur != very_start) 1235 *cur++ = '/'; 1236 1237 /* find/copy next component of pathname */ 1238 while (*t && *t != '/') 1239 *cur++ = *t++; 1240 } 1241 } 1242 1243 1244 void 1245 set_current_wd(char *pathl) 1246 { 1247 size_t len = 1; 1248 char *p = pathl; 1249 1250 if (p == NULL) { 1251 if ((p = ksh_get_wd(&len)) == NULL) 1252 p = null; 1253 } else 1254 len = strlen(p) + 1; 1255 1256 if (len > current_wd_size) { 1257 afree(current_wd, APERM); 1258 current_wd = alloc(current_wd_size = len, APERM); 1259 } 1260 memcpy(current_wd, p, len); 1261 if (p != pathl && p != null) 1262 afree(p, ATEMP); 1263 } 1264 1265 #ifdef TIOCSCTTY 1266 extern void chvt_reinit(void); 1267 1268 static void 1269 chvt(const char *fn) 1270 { 1271 char dv[20]; 1272 struct stat sb; 1273 int fd; 1274 1275 /* for entropy */ 1276 kshstate_f.h = evilhash(fn); 1277 1278 if (*fn == '-') { 1279 memcpy(dv, "-/dev/null", sizeof("-/dev/null")); 1280 fn = dv + 1; 1281 } else { 1282 if (stat(fn, &sb)) { 1283 memcpy(dv, "/dev/ttyC", 9); 1284 strlcpy(dv + 9, fn, sizeof(dv) - 9); 1285 if (stat(dv, &sb)) { 1286 strlcpy(dv + 8, fn, sizeof(dv) - 8); 1287 if (stat(dv, &sb)) 1288 errorf("chvt: can't find tty %s", fn); 1289 } 1290 fn = dv; 1291 } 1292 if (!(sb.st_mode & S_IFCHR)) 1293 errorf("chvt: not a char device: %s", fn); 1294 if ((sb.st_uid != 0) && chown(fn, 0, 0)) 1295 warningf(false, "chvt: cannot chown root %s", fn); 1296 if (((sb.st_mode & 07777) != 0600) && chmod(fn, (mode_t)0600)) 1297 warningf(false, "chvt: cannot chmod 0600 %s", fn); 1298 #if HAVE_REVOKE 1299 if (revoke(fn)) 1300 #endif 1301 warningf(false, "chvt: cannot revoke %s, new shell is" 1302 " potentially insecure", fn); 1303 } 1304 if ((fd = open(fn, O_RDWR)) == -1) { 1305 sleep(1); 1306 if ((fd = open(fn, O_RDWR)) == -1) 1307 errorf("chvt: cannot open %s", fn); 1308 } 1309 switch (fork()) { 1310 case -1: 1311 errorf("chvt: %s failed", "fork"); 1312 case 0: 1313 break; 1314 default: 1315 exit(0); 1316 } 1317 if (setsid() == -1) 1318 errorf("chvt: %s failed", "setsid"); 1319 if (fn != dv + 1) { 1320 if (ioctl(fd, TIOCSCTTY, NULL) == -1) 1321 errorf("chvt: %s failed", "TIOCSCTTY"); 1322 if (tcflush(fd, TCIOFLUSH)) 1323 errorf("chvt: %s failed", "TCIOFLUSH"); 1324 } 1325 ksh_dup2(fd, 0, false); 1326 ksh_dup2(fd, 1, false); 1327 ksh_dup2(fd, 2, false); 1328 if (fd > 2) 1329 close(fd); 1330 chvt_reinit(); 1331 } 1332 #endif 1333 1334 #ifdef DEBUG 1335 char longsizes_are_okay[sizeof(long) == sizeof(unsigned long) ? 1 : -1]; 1336 char arisize_is_okay[sizeof(mksh_ari_t) == 4 ? 1 : -1]; 1337 char uarisize_is_okay[sizeof(mksh_uari_t) == 4 ? 1 : -1]; 1338 1339 char * 1340 strchr(char *p, int ch) 1341 { 1342 for (;; ++p) { 1343 if (*p == ch) 1344 return (p); 1345 if (!*p) 1346 return (NULL); 1347 } 1348 /* NOTREACHED */ 1349 } 1350 1351 char * 1352 strstr(char *b, const char *l) 1353 { 1354 char first, c; 1355 size_t n; 1356 1357 if ((first = *l++) == '\0') 1358 return (b); 1359 n = strlen(l); 1360 strstr_look: 1361 while ((c = *b++) != first) 1362 if (c == '\0') 1363 return (NULL); 1364 if (strncmp(b, l, n)) 1365 goto strstr_look; 1366 return (b - 1); 1367 } 1368 #endif 1369 1370 #ifndef MKSH_ASSUME_UTF8 1371 #if !HAVE_STRCASESTR 1372 const char * 1373 stristr(const char *b, const char *l) 1374 { 1375 char first, c; 1376 size_t n; 1377 1378 if ((first = *l++), ((first = ksh_tolower(first)) == '\0')) 1379 return (b); 1380 n = strlen(l); 1381 stristr_look: 1382 while ((c = *b++), ((c = ksh_tolower(c)) != first)) 1383 if (c == '\0') 1384 return (NULL); 1385 if (strncasecmp(b, l, n)) 1386 goto stristr_look; 1387 return (b - 1); 1388 } 1389 #endif 1390 #endif 1391 1392 #ifdef MKSH_SMALL 1393 char * 1394 strndup_(const char *src, size_t len, Area *ap) 1395 { 1396 char *dst = NULL; 1397 1398 if (src != NULL) { 1399 dst = alloc(len + 1, ap); 1400 memcpy(dst, src, len); 1401 dst[len] = '\0'; 1402 } 1403 return (dst); 1404 } 1405 1406 char * 1407 strdup_(const char *src, Area *ap) 1408 { 1409 return (src == NULL ? NULL : strndup_(src, strlen(src), ap)); 1410 } 1411 #endif 1412 1413 #if !HAVE_GETRUSAGE 1414 #define INVTCK(r,t) do { \ 1415 r.tv_usec = ((t) % (1000000 / CLK_TCK)) * (1000000 / CLK_TCK); \ 1416 r.tv_sec = (t) / CLK_TCK; \ 1417 } while (/* CONSTCOND */ 0) 1418 1419 int 1420 getrusage(int what, struct rusage *ru) 1421 { 1422 struct tms tms; 1423 clock_t u, s; 1424 1425 if (/* ru == NULL || */ times(&tms) == (clock_t)-1) 1426 return (-1); 1427 1428 switch (what) { 1429 case RUSAGE_SELF: 1430 u = tms.tms_utime; 1431 s = tms.tms_stime; 1432 break; 1433 case RUSAGE_CHILDREN: 1434 u = tms.tms_cutime; 1435 s = tms.tms_cstime; 1436 break; 1437 default: 1438 errno = EINVAL; 1439 return (-1); 1440 } 1441 INVTCK(ru->ru_utime, u); 1442 INVTCK(ru->ru_stime, s); 1443 return (0); 1444 } 1445 #endif 1446 1447 /* 1448 * process the string available via fg (get a char) 1449 * and fp (put back a char) for backslash escapes, 1450 * assuming the first call to *fg gets the char di- 1451 * rectly after the backslash; return the character 1452 * (0..0xFF), Unicode (wc + 0x100), or -1 if no known 1453 * escape sequence was found 1454 */ 1455 int 1456 unbksl(bool cstyle, int (*fg)(void), void (*fp)(int)) 1457 { 1458 int wc, i, c, fc; 1459 1460 fc = (*fg)(); 1461 switch (fc) { 1462 case 'a': 1463 /* 1464 * according to the comments in pdksh, \007 seems 1465 * to be more portable than \a (due to HP-UX cc, 1466 * Ultrix cc, old pcc, etc.) so we avoid the escape 1467 * sequence altogether in mksh and assume ASCII 1468 */ 1469 wc = 7; 1470 break; 1471 case 'b': 1472 wc = '\b'; 1473 break; 1474 case 'c': 1475 if (!cstyle) 1476 goto unknown_escape; 1477 c = (*fg)(); 1478 wc = CTRL(c); 1479 break; 1480 case 'E': 1481 case 'e': 1482 wc = 033; 1483 break; 1484 case 'f': 1485 wc = '\f'; 1486 break; 1487 case 'n': 1488 wc = '\n'; 1489 break; 1490 case 'r': 1491 wc = '\r'; 1492 break; 1493 case 't': 1494 wc = '\t'; 1495 break; 1496 case 'v': 1497 /* assume ASCII here as well */ 1498 wc = 11; 1499 break; 1500 case '1': 1501 case '2': 1502 case '3': 1503 case '4': 1504 case '5': 1505 case '6': 1506 case '7': 1507 if (!cstyle) 1508 goto unknown_escape; 1509 /* FALLTHROUGH */ 1510 case '0': 1511 if (cstyle) 1512 (*fp)(fc); 1513 /* 1514 * look for an octal number with up to three 1515 * digits, not counting the leading zero; 1516 * convert it to a raw octet 1517 */ 1518 wc = 0; 1519 i = 3; 1520 while (i--) 1521 if ((c = (*fg)()) >= '0' && c <= '7') 1522 wc = (wc << 3) + (c - '0'); 1523 else { 1524 (*fp)(c); 1525 break; 1526 } 1527 break; 1528 case 'U': 1529 i = 8; 1530 if (0) 1531 /* FALLTHROUGH */ 1532 case 'u': 1533 i = 4; 1534 if (0) 1535 /* FALLTHROUGH */ 1536 case 'x': 1537 i = cstyle ? -1 : 2; 1538 /* 1539 * x: look for a hexadecimal number with up to 1540 * two (C style: arbitrary) digits; convert 1541 * to raw octet (C style: Unicode if >0xFF) 1542 * u/U: look for a hexadecimal number with up to 1543 * four (U: eight) digits; convert to Unicode 1544 */ 1545 wc = 0; 1546 while (i--) { 1547 wc <<= 4; 1548 if ((c = (*fg)()) >= '0' && c <= '9') 1549 wc += c - '0'; 1550 else if (c >= 'A' && c <= 'F') 1551 wc += c - 'A' + 10; 1552 else if (c >= 'a' && c <= 'f') 1553 wc += c - 'a' + 10; 1554 else { 1555 wc >>= 4; 1556 (*fp)(c); 1557 break; 1558 } 1559 } 1560 if ((cstyle && wc > 0xFF) || fc != 'x') 1561 /* Unicode marker */ 1562 wc += 0x100; 1563 break; 1564 case '\'': 1565 if (!cstyle) 1566 goto unknown_escape; 1567 wc = '\''; 1568 break; 1569 case '\\': 1570 wc = '\\'; 1571 break; 1572 default: 1573 unknown_escape: 1574 (*fp)(fc); 1575 return (-1); 1576 } 1577 1578 return (wc); 1579 } 1580