1 /*- 2 * Copyright (c) 1991, 1993 3 * The Regents of the University of California. All rights reserved. 4 * Copyright (c) 1997-2005 5 * Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. 6 * 7 * This code is derived from software contributed to Berkeley by 8 * Kenneth Almquist. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. Neither the name of the University nor the names of its contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 * 34 * @(#)expand.c 8.5 (Berkeley) 5/15/95 35 * $FreeBSD: head/bin/sh/expand.c 248980 2013-04-01 17:18:22Z jilles $ 36 */ 37 38 #include <sys/types.h> 39 #include <sys/time.h> 40 #include <sys/stat.h> 41 #include <dirent.h> 42 #include <errno.h> 43 #include <inttypes.h> 44 #include <limits.h> 45 #include <pwd.h> 46 #include <stdio.h> 47 #include <stdlib.h> 48 #include <string.h> 49 #include <unistd.h> 50 #include <wchar.h> 51 #include <wctype.h> 52 53 /* 54 * Routines to expand arguments to commands. We have to deal with 55 * backquotes, shell variables, and file metacharacters. 56 */ 57 58 #include "shell.h" 59 #include "main.h" 60 #include "nodes.h" 61 #include "eval.h" 62 #include "expand.h" 63 #include "syntax.h" 64 #include "parser.h" 65 #include "jobs.h" 66 #include "options.h" 67 #include "var.h" 68 #include "input.h" 69 #include "output.h" 70 #include "memalloc.h" 71 #include "error.h" 72 #include "mystring.h" 73 #include "arith.h" 74 #include "show.h" 75 #include "builtins.h" 76 77 /* 78 * Structure specifying which parts of the string should be searched 79 * for IFS characters. 80 */ 81 82 struct ifsregion { 83 struct ifsregion *next; /* next region in list */ 84 int begoff; /* offset of start of region */ 85 int endoff; /* offset of end of region */ 86 int inquotes; /* search for nul bytes only */ 87 }; 88 89 90 static char *expdest; /* output of current string */ 91 static struct nodelist *argbackq; /* list of back quote expressions */ 92 static struct ifsregion ifsfirst; /* first struct in list of ifs regions */ 93 static struct ifsregion *ifslastp; /* last struct in list */ 94 static struct arglist exparg; /* holds expanded arg list */ 95 96 static void argstr(char *, int); 97 static char *exptilde(char *, int); 98 static void expbackq(union node *, int, int); 99 static int subevalvar(char *, char *, int, int, int, int, int); 100 static char *evalvar(char *, int); 101 static int varisset(char *, int); 102 static void varvalue(char *, int, int, int); 103 static void recordregion(int, int, int); 104 static void removerecordregions(int); 105 static void ifsbreakup(char *, struct arglist *); 106 static void expandmeta(struct strlist *, int); 107 static void expmeta(char *, char *); 108 static void addfname(char *); 109 static struct strlist *expsort(struct strlist *); 110 static struct strlist *msort(struct strlist *, int); 111 static int patmatch(const char *, const char *, int); 112 static char *cvtnum(int, char *); 113 static int collate_range_cmp(wchar_t, wchar_t); 114 115 static int 116 collate_range_cmp(wchar_t c1, wchar_t c2) 117 { 118 static wchar_t s1[2], s2[2]; 119 120 s1[0] = c1; 121 s2[0] = c2; 122 return (wcscoll(s1, s2)); 123 } 124 125 static char * 126 stputs_quotes(const char *data, const char *syntax, char *p) 127 { 128 while (*data) { 129 CHECKSTRSPACE(2, p); 130 if (syntax[(int)*data] == CCTL) 131 USTPUTC(CTLESC, p); 132 USTPUTC(*data++, p); 133 } 134 return (p); 135 } 136 #define STPUTS_QUOTES(data, syntax, p) p = stputs_quotes((data), syntax, p) 137 138 /* 139 * Perform expansions on an argument, placing the resulting list of arguments 140 * in arglist. Parameter expansion, command substitution and arithmetic 141 * expansion are always performed; additional expansions can be requested 142 * via flag (EXP_*). 143 * The result is left in the stack string. 144 * When arglist is NULL, perform here document expansion. 145 * 146 * Caution: this function uses global state and is not reentrant. 147 * However, a new invocation after an interrupted invocation is safe 148 * and will reset the global state for the new call. 149 */ 150 void 151 expandarg(union node *arg, struct arglist *arglist, int flag) 152 { 153 struct strlist *sp; 154 char *p; 155 156 argbackq = arg->narg.backquote; 157 STARTSTACKSTR(expdest); 158 ifsfirst.next = NULL; 159 ifslastp = NULL; 160 argstr(arg->narg.text, flag); 161 if (arglist == NULL) { 162 STACKSTRNUL(expdest); 163 return; /* here document expanded */ 164 } 165 STPUTC('\0', expdest); 166 p = grabstackstr(expdest); 167 exparg.lastp = &exparg.list; 168 /* 169 * TODO - EXP_REDIR 170 */ 171 if (flag & EXP_FULL) { 172 ifsbreakup(p, &exparg); 173 *exparg.lastp = NULL; 174 exparg.lastp = &exparg.list; 175 expandmeta(exparg.list, flag); 176 } else { 177 if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */ 178 rmescapes(p); 179 sp = (struct strlist *)stalloc(sizeof (struct strlist)); 180 sp->text = p; 181 *exparg.lastp = sp; 182 exparg.lastp = &sp->next; 183 } 184 while (ifsfirst.next != NULL) { 185 struct ifsregion *ifsp; 186 INTOFF; 187 ifsp = ifsfirst.next->next; 188 ckfree(ifsfirst.next); 189 ifsfirst.next = ifsp; 190 INTON; 191 } 192 *exparg.lastp = NULL; 193 if (exparg.list) { 194 *arglist->lastp = exparg.list; 195 arglist->lastp = exparg.lastp; 196 } 197 } 198 199 200 201 /* 202 * Perform parameter expansion, command substitution and arithmetic 203 * expansion, and tilde expansion if requested via EXP_TILDE/EXP_VARTILDE. 204 * Processing ends at a CTLENDVAR character as well as '\0'. 205 * This is used to expand word in ${var+word} etc. 206 * If EXP_FULL, EXP_CASE or EXP_REDIR are set, keep and/or generate CTLESC 207 * characters to allow for further processing. 208 * If EXP_FULL is set, also preserve CTLQUOTEMARK characters. 209 */ 210 static void 211 argstr(char *p, int flag) 212 { 213 char c; 214 int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR); /* do CTLESC */ 215 int firsteq = 1; 216 int split_lit; 217 int lit_quoted; 218 219 split_lit = flag & EXP_SPLIT_LIT; 220 lit_quoted = flag & EXP_LIT_QUOTED; 221 flag &= ~(EXP_SPLIT_LIT | EXP_LIT_QUOTED); 222 if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE))) 223 p = exptilde(p, flag); 224 for (;;) { 225 CHECKSTRSPACE(2, expdest); 226 switch (c = *p++) { 227 case '\0': 228 case CTLENDVAR: 229 goto breakloop; 230 case CTLQUOTEMARK: 231 lit_quoted = 1; 232 /* "$@" syntax adherence hack */ 233 if (p[0] == CTLVAR && p[2] == '@' && p[3] == '=') 234 break; 235 if ((flag & EXP_FULL) != 0) 236 USTPUTC(c, expdest); 237 break; 238 case CTLQUOTEEND: 239 lit_quoted = 0; 240 break; 241 case CTLESC: 242 if (quotes) 243 USTPUTC(c, expdest); 244 c = *p++; 245 USTPUTC(c, expdest); 246 if (split_lit && !lit_quoted) 247 recordregion(expdest - stackblock() - 248 (quotes ? 2 : 1), 249 expdest - stackblock(), 0); 250 break; 251 case CTLVAR: 252 p = evalvar(p, flag); 253 break; 254 case CTLBACKQ: 255 case CTLBACKQ|CTLQUOTE: 256 expbackq(argbackq->n, c & CTLQUOTE, flag); 257 argbackq = argbackq->next; 258 break; 259 case CTLENDARI: 260 expari(flag); 261 break; 262 case ':': 263 case '=': 264 /* 265 * sort of a hack - expand tildes in variable 266 * assignments (after the first '=' and after ':'s). 267 */ 268 USTPUTC(c, expdest); 269 if (split_lit && !lit_quoted) 270 recordregion(expdest - stackblock() - 1, 271 expdest - stackblock(), 0); 272 if (flag & EXP_VARTILDE && *p == '~' && 273 (c != '=' || firsteq)) { 274 if (c == '=') 275 firsteq = 0; 276 p = exptilde(p, flag); 277 } 278 break; 279 default: 280 USTPUTC(c, expdest); 281 if (split_lit && !lit_quoted) 282 recordregion(expdest - stackblock() - 1, 283 expdest - stackblock(), 0); 284 } 285 } 286 breakloop:; 287 } 288 289 /* 290 * Perform tilde expansion, placing the result in the stack string and 291 * returning the next position in the input string to process. 292 */ 293 static char * 294 exptilde(char *p, int flag) 295 { 296 char c, *startp = p; 297 struct passwd *pw; 298 char *home; 299 int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR); 300 301 while ((c = *p) != '\0') { 302 switch(c) { 303 case CTLESC: /* This means CTL* are always considered quoted. */ 304 case CTLVAR: 305 case CTLBACKQ: 306 case CTLBACKQ | CTLQUOTE: 307 case CTLARI: 308 case CTLENDARI: 309 case CTLQUOTEMARK: 310 return (startp); 311 case ':': 312 if (flag & EXP_VARTILDE) 313 goto done; 314 break; 315 case '/': 316 case CTLENDVAR: 317 goto done; 318 } 319 p++; 320 } 321 done: 322 *p = '\0'; 323 if (*(startp+1) == '\0') { 324 if ((home = lookupvar("HOME")) == NULL) 325 goto lose; 326 } else { 327 if ((pw = getpwnam(startp+1)) == NULL) 328 goto lose; 329 home = pw->pw_dir; 330 } 331 if (*home == '\0') 332 goto lose; 333 *p = c; 334 if (quotes) 335 STPUTS_QUOTES(home, SQSYNTAX, expdest); 336 else 337 STPUTS(home, expdest); 338 return (p); 339 lose: 340 *p = c; 341 return (startp); 342 } 343 344 345 static void 346 removerecordregions(int endoff) 347 { 348 if (ifslastp == NULL) 349 return; 350 351 if (ifsfirst.endoff > endoff) { 352 while (ifsfirst.next != NULL) { 353 struct ifsregion *ifsp; 354 INTOFF; 355 ifsp = ifsfirst.next->next; 356 ckfree(ifsfirst.next); 357 ifsfirst.next = ifsp; 358 INTON; 359 } 360 if (ifsfirst.begoff > endoff) 361 ifslastp = NULL; 362 else { 363 ifslastp = &ifsfirst; 364 ifsfirst.endoff = endoff; 365 } 366 return; 367 } 368 369 ifslastp = &ifsfirst; 370 while (ifslastp->next && ifslastp->next->begoff < endoff) 371 ifslastp=ifslastp->next; 372 while (ifslastp->next != NULL) { 373 struct ifsregion *ifsp; 374 INTOFF; 375 ifsp = ifslastp->next->next; 376 ckfree(ifslastp->next); 377 ifslastp->next = ifsp; 378 INTON; 379 } 380 if (ifslastp->endoff > endoff) 381 ifslastp->endoff = endoff; 382 } 383 384 /* 385 * Expand arithmetic expression. Backup to start of expression, 386 * evaluate, place result in (backed up) result, adjust string position. 387 */ 388 void 389 expari(int flag) 390 { 391 char *p, *q, *start; 392 arith_t result; 393 int begoff; 394 int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR); 395 int quoted; 396 397 /* 398 * This routine is slightly over-complicated for 399 * efficiency. First we make sure there is 400 * enough space for the result, which may be bigger 401 * than the expression. Next we 402 * scan backwards looking for the start of arithmetic. If the 403 * next previous character is a CTLESC character, then we 404 * have to rescan starting from the beginning since CTLESC 405 * characters have to be processed left to right. 406 */ 407 CHECKSTRSPACE(DIGITS(result) - 2, expdest); 408 USTPUTC('\0', expdest); 409 start = stackblock(); 410 p = expdest - 2; 411 while (p >= start && *p != CTLARI) 412 --p; 413 if (p < start || *p != CTLARI) 414 error("missing CTLARI (shouldn't happen)"); 415 if (p > start && *(p - 1) == CTLESC) 416 for (p = start; *p != CTLARI; p++) 417 if (*p == CTLESC) 418 p++; 419 420 if (p[1] == '"') 421 quoted=1; 422 else 423 quoted=0; 424 begoff = p - start; 425 removerecordregions(begoff); 426 if (quotes) 427 rmescapes(p+2); 428 q = grabstackstr(expdest); 429 result = arith(p+2); 430 ungrabstackstr(q, expdest); 431 fmtstr(p, DIGITS(result), ARITH_FORMAT_STR, result); 432 while (*p++) 433 ; 434 if (quoted == 0) 435 recordregion(begoff, p - 1 - start, 0); 436 result = expdest - p + 1; 437 STADJUST(-result, expdest); 438 } 439 440 441 /* 442 * Perform command substitution. 443 */ 444 static void 445 expbackq(union node *cmd, int quoted, int flag) 446 { 447 struct backcmd in; 448 int i; 449 char buf[128]; 450 char *p; 451 char *dest = expdest; 452 struct ifsregion saveifs, *savelastp; 453 struct nodelist *saveargbackq; 454 char lastc; 455 int startloc = dest - stackblock(); 456 char const *syntax = quoted? DQSYNTAX : BASESYNTAX; 457 int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR); 458 size_t nnl; 459 460 INTOFF; 461 saveifs = ifsfirst; 462 savelastp = ifslastp; 463 saveargbackq = argbackq; 464 p = grabstackstr(dest); 465 evalbackcmd(cmd, &in); 466 ungrabstackstr(p, dest); 467 ifsfirst = saveifs; 468 ifslastp = savelastp; 469 argbackq = saveargbackq; 470 471 p = in.buf; 472 lastc = '\0'; 473 nnl = 0; 474 /* Don't copy trailing newlines */ 475 for (;;) { 476 if (--in.nleft < 0) { 477 if (in.fd < 0) 478 break; 479 while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR); 480 TRACE(("expbackq: read returns %d\n", i)); 481 if (i <= 0) 482 break; 483 p = buf; 484 in.nleft = i - 1; 485 } 486 lastc = *p++; 487 if (lastc != '\0') { 488 if (lastc == '\n') { 489 nnl++; 490 } else { 491 CHECKSTRSPACE(nnl + 2, dest); 492 while (nnl > 0) { 493 nnl--; 494 USTPUTC('\n', dest); 495 } 496 if (quotes && syntax[(int)lastc] == CCTL) 497 USTPUTC(CTLESC, dest); 498 USTPUTC(lastc, dest); 499 } 500 } 501 } 502 503 if (in.fd >= 0) 504 close(in.fd); 505 if (in.buf) 506 ckfree(in.buf); 507 if (in.jp) 508 exitstatus = waitforjob(in.jp, NULL); 509 if (quoted == 0) 510 recordregion(startloc, dest - stackblock(), 0); 511 TRACE(("expbackq: size=%td: \"%.*s\"\n", 512 ((dest - stackblock()) - startloc), 513 (int)((dest - stackblock()) - startloc), 514 stackblock() + startloc)); 515 expdest = dest; 516 INTON; 517 } 518 519 520 521 static int 522 subevalvar(char *p, char *str, int strloc, int subtype, int startloc, 523 int varflags, int quotes) 524 { 525 char *startp; 526 char *loc = NULL; 527 char *q; 528 int c = 0; 529 struct nodelist *saveargbackq = argbackq; 530 int amount; 531 532 argstr(p, (subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX || 533 subtype == VSTRIMRIGHT || subtype == VSTRIMRIGHTMAX ? 534 EXP_CASE : 0) | EXP_TILDE); 535 STACKSTRNUL(expdest); 536 argbackq = saveargbackq; 537 startp = stackblock() + startloc; 538 if (str == NULL) 539 str = stackblock() + strloc; 540 541 switch (subtype) { 542 case VSASSIGN: 543 setvar(str, startp, 0); 544 amount = startp - expdest; 545 STADJUST(amount, expdest); 546 varflags &= ~VSNUL; 547 return 1; 548 549 case VSQUESTION: 550 if (*p != CTLENDVAR) { 551 outfmt(out2, "%s\n", startp); 552 error(NULL); 553 } 554 error("%.*s: parameter %snot set", (int)(p - str - 1), 555 str, (varflags & VSNUL) ? "null or " 556 : nullstr); 557 return 0; 558 559 case VSTRIMLEFT: 560 for (loc = startp; loc < str; loc++) { 561 c = *loc; 562 *loc = '\0'; 563 if (patmatch(str, startp, quotes)) { 564 *loc = c; 565 goto recordleft; 566 } 567 *loc = c; 568 if (quotes && *loc == CTLESC) 569 loc++; 570 } 571 return 0; 572 573 case VSTRIMLEFTMAX: 574 for (loc = str - 1; loc >= startp;) { 575 c = *loc; 576 *loc = '\0'; 577 if (patmatch(str, startp, quotes)) { 578 *loc = c; 579 goto recordleft; 580 } 581 *loc = c; 582 loc--; 583 if (quotes && loc > startp && *(loc - 1) == CTLESC) { 584 for (q = startp; q < loc; q++) 585 if (*q == CTLESC) 586 q++; 587 if (q > loc) 588 loc--; 589 } 590 } 591 return 0; 592 593 case VSTRIMRIGHT: 594 for (loc = str - 1; loc >= startp;) { 595 if (patmatch(str, loc, quotes)) { 596 amount = loc - expdest; 597 STADJUST(amount, expdest); 598 return 1; 599 } 600 loc--; 601 if (quotes && loc > startp && *(loc - 1) == CTLESC) { 602 for (q = startp; q < loc; q++) 603 if (*q == CTLESC) 604 q++; 605 if (q > loc) 606 loc--; 607 } 608 } 609 return 0; 610 611 case VSTRIMRIGHTMAX: 612 for (loc = startp; loc < str - 1; loc++) { 613 if (patmatch(str, loc, quotes)) { 614 amount = loc - expdest; 615 STADJUST(amount, expdest); 616 return 1; 617 } 618 if (quotes && *loc == CTLESC) 619 loc++; 620 } 621 return 0; 622 623 624 default: 625 abort(); 626 } 627 628 recordleft: 629 amount = ((str - 1) - (loc - startp)) - expdest; 630 STADJUST(amount, expdest); 631 while (loc != str - 1) 632 *startp++ = *loc++; 633 return 1; 634 } 635 636 637 /* 638 * Expand a variable, and return a pointer to the next character in the 639 * input string. 640 */ 641 642 static char * 643 evalvar(char *p, int flag) 644 { 645 int subtype; 646 int varflags; 647 char *var; 648 char *val; 649 int patloc; 650 int c; 651 int set; 652 int special; 653 int startloc; 654 int varlen; 655 int varlenb; 656 int easy; 657 int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR); 658 659 varflags = (unsigned char)*p++; 660 subtype = varflags & VSTYPE; 661 var = p; 662 special = 0; 663 if (! is_name(*p)) 664 special = 1; 665 p = strchr(p, '=') + 1; 666 again: /* jump here after setting a variable with ${var=text} */ 667 if (varflags & VSLINENO) { 668 set = 1; 669 special = 0; 670 val = var; 671 p[-1] = '\0'; /* temporarily overwrite '=' to have \0 672 terminated string */ 673 } else if (special) { 674 set = varisset(var, varflags & VSNUL); 675 val = NULL; 676 } else { 677 val = bltinlookup(var, 1); 678 if (val == NULL || ((varflags & VSNUL) && val[0] == '\0')) { 679 val = NULL; 680 set = 0; 681 } else 682 set = 1; 683 } 684 varlen = 0; 685 startloc = expdest - stackblock(); 686 if (!set && uflag && *var != '@' && *var != '*') { 687 switch (subtype) { 688 case VSNORMAL: 689 case VSTRIMLEFT: 690 case VSTRIMLEFTMAX: 691 case VSTRIMRIGHT: 692 case VSTRIMRIGHTMAX: 693 case VSLENGTH: 694 error("%.*s: parameter not set", (int)(p - var - 1), 695 var); 696 } 697 } 698 if (set && subtype != VSPLUS) { 699 /* insert the value of the variable */ 700 if (special) { 701 varvalue(var, varflags & VSQUOTE, subtype, flag); 702 if (subtype == VSLENGTH) { 703 varlenb = expdest - stackblock() - startloc; 704 varlen = varlenb; 705 if (localeisutf8) { 706 val = stackblock() + startloc; 707 for (;val != expdest; val++) 708 if ((*val & 0xC0) == 0x80) 709 varlen--; 710 } 711 STADJUST(-varlenb, expdest); 712 } 713 } else { 714 char const *syntax = (varflags & VSQUOTE) ? DQSYNTAX 715 : BASESYNTAX; 716 717 if (subtype == VSLENGTH) { 718 for (;*val; val++) 719 if (!localeisutf8 || 720 (*val & 0xC0) != 0x80) 721 varlen++; 722 } 723 else { 724 if (quotes) 725 STPUTS_QUOTES(val, syntax, expdest); 726 else 727 STPUTS(val, expdest); 728 729 } 730 } 731 } 732 733 if (subtype == VSPLUS) 734 set = ! set; 735 736 easy = ((varflags & VSQUOTE) == 0 || 737 (*var == '@' && shellparam.nparam != 1)); 738 739 740 switch (subtype) { 741 case VSLENGTH: 742 expdest = cvtnum(varlen, expdest); 743 goto record; 744 745 case VSNORMAL: 746 if (!easy) 747 break; 748 record: 749 recordregion(startloc, expdest - stackblock(), 750 varflags & VSQUOTE || (ifsset() && ifsval()[0] == '\0' && 751 (*var == '@' || *var == '*'))); 752 break; 753 754 case VSPLUS: 755 case VSMINUS: 756 if (!set) { 757 argstr(p, flag | (flag & EXP_FULL ? EXP_SPLIT_LIT : 0) | 758 (varflags & VSQUOTE ? EXP_LIT_QUOTED : 0)); 759 break; 760 } 761 if (easy) 762 goto record; 763 break; 764 765 case VSTRIMLEFT: 766 case VSTRIMLEFTMAX: 767 case VSTRIMRIGHT: 768 case VSTRIMRIGHTMAX: 769 if (!set) 770 break; 771 /* 772 * Terminate the string and start recording the pattern 773 * right after it 774 */ 775 STPUTC('\0', expdest); 776 patloc = expdest - stackblock(); 777 if (subevalvar(p, NULL, patloc, subtype, 778 startloc, varflags, quotes) == 0) { 779 int amount = (expdest - stackblock() - patloc) + 1; 780 STADJUST(-amount, expdest); 781 } 782 /* Remove any recorded regions beyond start of variable */ 783 removerecordregions(startloc); 784 goto record; 785 786 case VSASSIGN: 787 case VSQUESTION: 788 if (!set) { 789 if (subevalvar(p, var, 0, subtype, startloc, varflags, 790 quotes)) { 791 varflags &= ~VSNUL; 792 /* 793 * Remove any recorded regions beyond 794 * start of variable 795 */ 796 removerecordregions(startloc); 797 goto again; 798 } 799 break; 800 } 801 if (easy) 802 goto record; 803 break; 804 805 case VSERROR: 806 c = p - var - 1; 807 error("${%.*s%s}: Bad substitution", c, var, 808 (c > 0 && *p != CTLENDVAR) ? "..." : ""); 809 810 default: 811 abort(); 812 } 813 p[-1] = '='; /* recover overwritten '=' */ 814 815 if (subtype != VSNORMAL) { /* skip to end of alternative */ 816 int nesting = 1; 817 for (;;) { 818 if ((c = *p++) == CTLESC) 819 p++; 820 else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) { 821 if (set) 822 argbackq = argbackq->next; 823 } else if (c == CTLVAR) { 824 if ((*p++ & VSTYPE) != VSNORMAL) 825 nesting++; 826 } else if (c == CTLENDVAR) { 827 if (--nesting == 0) 828 break; 829 } 830 } 831 } 832 return p; 833 } 834 835 836 837 /* 838 * Test whether a specialized variable is set. 839 */ 840 841 static int 842 varisset(char *name, int nulok) 843 { 844 845 if (*name == '!') 846 return backgndpidset(); 847 else if (*name == '@' || *name == '*') { 848 if (*shellparam.p == NULL) 849 return 0; 850 851 if (nulok) { 852 char **av; 853 854 for (av = shellparam.p; *av; av++) 855 if (**av != '\0') 856 return 1; 857 return 0; 858 } 859 } else if (is_digit(*name)) { 860 char *ap; 861 int num = atoi(name); 862 863 if (num > shellparam.nparam) 864 return 0; 865 866 if (num == 0) 867 ap = arg0; 868 else 869 ap = shellparam.p[num - 1]; 870 871 if (nulok && (ap == NULL || *ap == '\0')) 872 return 0; 873 } 874 return 1; 875 } 876 877 static void 878 strtodest(const char *p, int flag, int subtype, int quoted) 879 { 880 if (flag & (EXP_FULL | EXP_CASE) && subtype != VSLENGTH) 881 STPUTS_QUOTES(p, quoted ? DQSYNTAX : BASESYNTAX, expdest); 882 else 883 STPUTS(p, expdest); 884 } 885 886 /* 887 * Add the value of a specialized variable to the stack string. 888 */ 889 890 static void 891 varvalue(char *name, int quoted, int subtype, int flag) 892 { 893 int num; 894 char *p; 895 int i; 896 char sep; 897 char **ap; 898 899 switch (*name) { 900 case '$': 901 num = rootpid; 902 goto numvar; 903 case '?': 904 num = oexitstatus; 905 goto numvar; 906 case '#': 907 num = shellparam.nparam; 908 goto numvar; 909 case '!': 910 num = backgndpidval(); 911 numvar: 912 expdest = cvtnum(num, expdest); 913 break; 914 case '-': 915 for (i = 0 ; i < NOPTS ; i++) { 916 if (optlist[i].val) 917 STPUTC(optlist[i].letter, expdest); 918 } 919 break; 920 case '@': 921 if (flag & EXP_FULL && quoted) { 922 for (ap = shellparam.p ; (p = *ap++) != NULL ; ) { 923 strtodest(p, flag, subtype, quoted); 924 if (*ap) 925 STPUTC('\0', expdest); 926 } 927 break; 928 } 929 /* FALLTHROUGH */ 930 case '*': 931 if (ifsset()) 932 sep = ifsval()[0]; 933 else 934 sep = ' '; 935 for (ap = shellparam.p ; (p = *ap++) != NULL ; ) { 936 strtodest(p, flag, subtype, quoted); 937 if (!*ap) 938 break; 939 if (sep || (flag & EXP_FULL && !quoted && **ap != '\0')) 940 STPUTC(sep, expdest); 941 } 942 break; 943 case '0': 944 p = arg0; 945 strtodest(p, flag, subtype, quoted); 946 break; 947 default: 948 if (is_digit(*name)) { 949 num = atoi(name); 950 if (num > 0 && num <= shellparam.nparam) { 951 p = shellparam.p[num - 1]; 952 strtodest(p, flag, subtype, quoted); 953 } 954 } 955 break; 956 } 957 } 958 959 960 961 /* 962 * Record the fact that we have to scan this region of the 963 * string for IFS characters. 964 */ 965 966 static void 967 recordregion(int start, int end, int inquotes) 968 { 969 struct ifsregion *ifsp; 970 971 if (ifslastp == NULL) { 972 ifsp = &ifsfirst; 973 } else { 974 if (ifslastp->endoff == start 975 && ifslastp->inquotes == inquotes) { 976 /* extend previous area */ 977 ifslastp->endoff = end; 978 return; 979 } 980 ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion)); 981 ifslastp->next = ifsp; 982 } 983 ifslastp = ifsp; 984 ifslastp->next = NULL; 985 ifslastp->begoff = start; 986 ifslastp->endoff = end; 987 ifslastp->inquotes = inquotes; 988 } 989 990 991 992 /* 993 * Break the argument string into pieces based upon IFS and add the 994 * strings to the argument list. The regions of the string to be 995 * searched for IFS characters have been stored by recordregion. 996 * CTLESC characters are preserved but have little effect in this pass 997 * other than escaping CTL* characters. In particular, they do not escape 998 * IFS characters: that should be done with the ifsregion mechanism. 999 * CTLQUOTEMARK characters are used to preserve empty quoted strings. 1000 * This pass treats them as a regular character, making the string non-empty. 1001 * Later, they are removed along with the other CTL* characters. 1002 */ 1003 static void 1004 ifsbreakup(char *string, struct arglist *arglist) 1005 { 1006 struct ifsregion *ifsp; 1007 struct strlist *sp; 1008 char *start; 1009 char *p; 1010 char *q; 1011 const char *ifs; 1012 const char *ifsspc; 1013 int had_param_ch = 0; 1014 1015 start = string; 1016 1017 if (ifslastp == NULL) { 1018 /* Return entire argument, IFS doesn't apply to any of it */ 1019 sp = (struct strlist *)stalloc(sizeof *sp); 1020 sp->text = start; 1021 *arglist->lastp = sp; 1022 arglist->lastp = &sp->next; 1023 return; 1024 } 1025 1026 ifs = ifsset() ? ifsval() : " \t\n"; 1027 1028 for (ifsp = &ifsfirst; ifsp != NULL; ifsp = ifsp->next) { 1029 p = string + ifsp->begoff; 1030 while (p < string + ifsp->endoff) { 1031 q = p; 1032 if (*p == CTLESC) 1033 p++; 1034 if (ifsp->inquotes) { 1035 /* Only NULs (should be from "$@") end args */ 1036 had_param_ch = 1; 1037 if (*p != 0) { 1038 p++; 1039 continue; 1040 } 1041 ifsspc = NULL; 1042 } else { 1043 if (!strchr(ifs, *p)) { 1044 had_param_ch = 1; 1045 p++; 1046 continue; 1047 } 1048 ifsspc = strchr(" \t\n", *p); 1049 1050 /* Ignore IFS whitespace at start */ 1051 if (q == start && ifsspc != NULL) { 1052 p++; 1053 start = p; 1054 continue; 1055 } 1056 had_param_ch = 0; 1057 } 1058 1059 /* Save this argument... */ 1060 *q = '\0'; 1061 sp = (struct strlist *)stalloc(sizeof *sp); 1062 sp->text = start; 1063 *arglist->lastp = sp; 1064 arglist->lastp = &sp->next; 1065 p++; 1066 1067 if (ifsspc != NULL) { 1068 /* Ignore further trailing IFS whitespace */ 1069 for (; p < string + ifsp->endoff; p++) { 1070 q = p; 1071 if (*p == CTLESC) 1072 p++; 1073 if (strchr(ifs, *p) == NULL) { 1074 p = q; 1075 break; 1076 } 1077 if (strchr(" \t\n", *p) == NULL) { 1078 p++; 1079 break; 1080 } 1081 } 1082 } 1083 start = p; 1084 } 1085 } 1086 1087 /* 1088 * Save anything left as an argument. 1089 * Traditionally we have treated 'IFS=':'; set -- x$IFS' as 1090 * generating 2 arguments, the second of which is empty. 1091 * Some recent clarification of the Posix spec say that it 1092 * should only generate one.... 1093 */ 1094 if (had_param_ch || *start != 0) { 1095 sp = (struct strlist *)stalloc(sizeof *sp); 1096 sp->text = start; 1097 *arglist->lastp = sp; 1098 arglist->lastp = &sp->next; 1099 } 1100 } 1101 1102 1103 static char expdir[PATH_MAX]; 1104 #define expdir_end (expdir + sizeof(expdir)) 1105 1106 /* 1107 * Perform pathname generation and remove control characters. 1108 * At this point, the only control characters should be CTLESC and CTLQUOTEMARK. 1109 * The results are stored in the list exparg. 1110 */ 1111 static void 1112 expandmeta(struct strlist *str, int flag __unused) 1113 { 1114 char *p; 1115 struct strlist **savelastp; 1116 struct strlist *sp; 1117 char c; 1118 /* TODO - EXP_REDIR */ 1119 1120 while (str) { 1121 if (fflag) 1122 goto nometa; 1123 p = str->text; 1124 for (;;) { /* fast check for meta chars */ 1125 if ((c = *p++) == '\0') 1126 goto nometa; 1127 if (c == '*' || c == '?' || c == '[') 1128 break; 1129 } 1130 savelastp = exparg.lastp; 1131 INTOFF; 1132 expmeta(expdir, str->text); 1133 INTON; 1134 if (exparg.lastp == savelastp) { 1135 /* 1136 * no matches 1137 */ 1138 nometa: 1139 *exparg.lastp = str; 1140 rmescapes(str->text); 1141 exparg.lastp = &str->next; 1142 } else { 1143 *exparg.lastp = NULL; 1144 *savelastp = sp = expsort(*savelastp); 1145 while (sp->next != NULL) 1146 sp = sp->next; 1147 exparg.lastp = &sp->next; 1148 } 1149 str = str->next; 1150 } 1151 } 1152 1153 1154 /* 1155 * Do metacharacter (i.e. *, ?, [...]) expansion. 1156 */ 1157 1158 static void 1159 expmeta(char *enddir, char *name) 1160 { 1161 const char *p; 1162 const char *q; 1163 const char *start; 1164 char *endname; 1165 int metaflag; 1166 struct stat statb; 1167 DIR *dirp; 1168 struct dirent *dp; 1169 int atend; 1170 int matchdot; 1171 int esc; 1172 int namlen; 1173 1174 metaflag = 0; 1175 start = name; 1176 for (p = name; esc = 0, *p; p += esc + 1) { 1177 if (*p == '*' || *p == '?') 1178 metaflag = 1; 1179 else if (*p == '[') { 1180 q = p + 1; 1181 if (*q == '!' || *q == '^') 1182 q++; 1183 for (;;) { 1184 while (*q == CTLQUOTEMARK) 1185 q++; 1186 if (*q == CTLESC) 1187 q++; 1188 if (*q == '/' || *q == '\0') 1189 break; 1190 if (*++q == ']') { 1191 metaflag = 1; 1192 break; 1193 } 1194 } 1195 } else if (*p == '\0') 1196 break; 1197 else if (*p == CTLQUOTEMARK) 1198 continue; 1199 else { 1200 if (*p == CTLESC) 1201 esc++; 1202 if (p[esc] == '/') { 1203 if (metaflag) 1204 break; 1205 start = p + esc + 1; 1206 } 1207 } 1208 } 1209 if (metaflag == 0) { /* we've reached the end of the file name */ 1210 if (enddir != expdir) 1211 metaflag++; 1212 for (p = name ; ; p++) { 1213 if (*p == CTLQUOTEMARK) 1214 continue; 1215 if (*p == CTLESC) 1216 p++; 1217 *enddir++ = *p; 1218 if (*p == '\0') 1219 break; 1220 if (enddir == expdir_end) 1221 return; 1222 } 1223 if (metaflag == 0 || lstat(expdir, &statb) >= 0) 1224 addfname(expdir); 1225 return; 1226 } 1227 endname = name + (p - name); 1228 if (start != name) { 1229 p = name; 1230 while (p < start) { 1231 while (*p == CTLQUOTEMARK) 1232 p++; 1233 if (*p == CTLESC) 1234 p++; 1235 *enddir++ = *p++; 1236 if (enddir == expdir_end) 1237 return; 1238 } 1239 } 1240 if (enddir == expdir) { 1241 p = __DECONST(char *, "."); 1242 } else if (enddir == expdir + 1 && *expdir == '/') { 1243 p = __DECONST(char *, "/"); 1244 } else { 1245 p = expdir; 1246 enddir[-1] = '\0'; 1247 } 1248 if ((dirp = opendir(p)) == NULL) 1249 return; 1250 if (enddir != expdir) 1251 enddir[-1] = '/'; 1252 if (*endname == 0) { 1253 atend = 1; 1254 } else { 1255 atend = 0; 1256 *endname = '\0'; 1257 endname += esc + 1; 1258 } 1259 matchdot = 0; 1260 p = start; 1261 while (*p == CTLQUOTEMARK) 1262 p++; 1263 if (*p == CTLESC) 1264 p++; 1265 if (*p == '.') 1266 matchdot++; 1267 while (! int_pending() && (dp = readdir(dirp)) != NULL) { 1268 if (dp->d_name[0] == '.' && ! matchdot) 1269 continue; 1270 if (patmatch(start, dp->d_name, 0)) { 1271 namlen = dp->d_namlen; 1272 if (enddir + namlen + 1 > expdir_end) 1273 continue; 1274 memcpy(enddir, dp->d_name, namlen + 1); 1275 if (atend) 1276 addfname(expdir); 1277 else { 1278 if (dp->d_type != DT_UNKNOWN && 1279 dp->d_type != DT_DIR && 1280 dp->d_type != DT_LNK) 1281 continue; 1282 if (enddir + namlen + 2 > expdir_end) 1283 continue; 1284 enddir[namlen] = '/'; 1285 enddir[namlen + 1] = '\0'; 1286 expmeta(enddir + namlen + 1, endname); 1287 } 1288 } 1289 } 1290 closedir(dirp); 1291 if (! atend) 1292 endname[-esc - 1] = esc ? CTLESC : '/'; 1293 } 1294 1295 1296 /* 1297 * Add a file name to the list. 1298 */ 1299 1300 static void 1301 addfname(char *name) 1302 { 1303 char *p; 1304 struct strlist *sp; 1305 1306 p = stalloc(strlen(name) + 1); 1307 scopy(name, p); 1308 sp = (struct strlist *)stalloc(sizeof *sp); 1309 sp->text = p; 1310 *exparg.lastp = sp; 1311 exparg.lastp = &sp->next; 1312 } 1313 1314 1315 /* 1316 * Sort the results of file name expansion. It calculates the number of 1317 * strings to sort and then calls msort (short for merge sort) to do the 1318 * work. 1319 */ 1320 1321 static struct strlist * 1322 expsort(struct strlist *str) 1323 { 1324 int len; 1325 struct strlist *sp; 1326 1327 len = 0; 1328 for (sp = str ; sp ; sp = sp->next) 1329 len++; 1330 return msort(str, len); 1331 } 1332 1333 1334 static struct strlist * 1335 msort(struct strlist *list, int len) 1336 { 1337 struct strlist *p, *q = NULL; 1338 struct strlist **lpp; 1339 int half; 1340 int n; 1341 1342 if (len <= 1) 1343 return list; 1344 half = len >> 1; 1345 p = list; 1346 for (n = half ; --n >= 0 ; ) { 1347 q = p; 1348 p = p->next; 1349 } 1350 q->next = NULL; /* terminate first half of list */ 1351 q = msort(list, half); /* sort first half of list */ 1352 p = msort(p, len - half); /* sort second half */ 1353 lpp = &list; 1354 for (;;) { 1355 if (strcmp(p->text, q->text) < 0) { 1356 *lpp = p; 1357 lpp = &p->next; 1358 if ((p = *lpp) == NULL) { 1359 *lpp = q; 1360 break; 1361 } 1362 } else { 1363 *lpp = q; 1364 lpp = &q->next; 1365 if ((q = *lpp) == NULL) { 1366 *lpp = p; 1367 break; 1368 } 1369 } 1370 } 1371 return list; 1372 } 1373 1374 1375 1376 static wchar_t 1377 get_wc(const char **p) 1378 { 1379 wchar_t c; 1380 int chrlen; 1381 1382 chrlen = mbtowc(&c, *p, 4); 1383 if (chrlen == 0) 1384 return 0; 1385 else if (chrlen == -1) 1386 c = 0; 1387 else 1388 *p += chrlen; 1389 return c; 1390 } 1391 1392 1393 /* 1394 * See if a character matches a character class, starting at the first colon 1395 * of "[:class:]". 1396 * If a valid character class is recognized, a pointer to the next character 1397 * after the final closing bracket is stored into *end, otherwise a null 1398 * pointer is stored into *end. 1399 */ 1400 static int 1401 match_charclass(const char *p, wchar_t chr, const char **end) 1402 { 1403 char name[20]; 1404 const char *nameend; 1405 wctype_t cclass; 1406 1407 *end = NULL; 1408 p++; 1409 nameend = strstr(p, ":]"); 1410 if (nameend == NULL || (size_t)(nameend - p) >= sizeof(name) || 1411 nameend == p) 1412 return 0; 1413 memcpy(name, p, nameend - p); 1414 name[nameend - p] = '\0'; 1415 *end = nameend + 2; 1416 cclass = wctype(name); 1417 /* An unknown class matches nothing but is valid nevertheless. */ 1418 if (cclass == 0) 1419 return 0; 1420 return iswctype(chr, cclass); 1421 } 1422 1423 1424 /* 1425 * Returns true if the pattern matches the string. 1426 */ 1427 1428 static int 1429 patmatch(const char *pattern, const char *string, int squoted) 1430 { 1431 const char *p, *q, *end; 1432 const char *bt_p, *bt_q; 1433 char c; 1434 wchar_t wc, wc2; 1435 1436 p = pattern; 1437 q = string; 1438 bt_p = NULL; 1439 bt_q = NULL; 1440 for (;;) { 1441 switch (c = *p++) { 1442 case '\0': 1443 if (*q != '\0') 1444 goto backtrack; 1445 return 1; 1446 case CTLESC: 1447 if (squoted && *q == CTLESC) 1448 q++; 1449 if (*q++ != *p++) 1450 goto backtrack; 1451 break; 1452 case CTLQUOTEMARK: 1453 continue; 1454 case '?': 1455 if (squoted && *q == CTLESC) 1456 q++; 1457 if (*q == '\0') 1458 return 0; 1459 if (localeisutf8) { 1460 wc = get_wc(&q); 1461 /* 1462 * A '?' does not match invalid UTF-8 but a 1463 * '*' does, so backtrack. 1464 */ 1465 if (wc == 0) 1466 goto backtrack; 1467 } else 1468 wc = (unsigned char)*q++; 1469 break; 1470 case '*': 1471 c = *p; 1472 while (c == CTLQUOTEMARK || c == '*') 1473 c = *++p; 1474 /* 1475 * If the pattern ends here, we know the string 1476 * matches without needing to look at the rest of it. 1477 */ 1478 if (c == '\0') 1479 return 1; 1480 /* 1481 * First try the shortest match for the '*' that 1482 * could work. We can forget any earlier '*' since 1483 * there is no way having it match more characters 1484 * can help us, given that we are already here. 1485 */ 1486 bt_p = p; 1487 bt_q = q; 1488 break; 1489 case '[': { 1490 const char *endp; 1491 int invert, found; 1492 wchar_t chr; 1493 1494 endp = p; 1495 if (*endp == '!' || *endp == '^') 1496 endp++; 1497 for (;;) { 1498 while (*endp == CTLQUOTEMARK) 1499 endp++; 1500 if (*endp == 0) 1501 goto dft; /* no matching ] */ 1502 if (*endp == CTLESC) 1503 endp++; 1504 if (*++endp == ']') 1505 break; 1506 } 1507 invert = 0; 1508 if (*p == '!' || *p == '^') { 1509 invert++; 1510 p++; 1511 } 1512 found = 0; 1513 if (squoted && *q == CTLESC) 1514 q++; 1515 if (*q == '\0') 1516 return 0; 1517 if (localeisutf8) { 1518 chr = get_wc(&q); 1519 if (chr == 0) 1520 goto backtrack; 1521 } else 1522 chr = (unsigned char)*q++; 1523 c = *p++; 1524 do { 1525 if (c == CTLQUOTEMARK) 1526 continue; 1527 if (c == '[' && *p == ':') { 1528 found |= match_charclass(p, chr, &end); 1529 if (end != NULL) 1530 p = end; 1531 } 1532 if (c == CTLESC) 1533 c = *p++; 1534 if (localeisutf8 && c & 0x80) { 1535 p--; 1536 wc = get_wc(&p); 1537 if (wc == 0) /* bad utf-8 */ 1538 return 0; 1539 } else 1540 wc = (unsigned char)c; 1541 if (*p == '-' && p[1] != ']') { 1542 p++; 1543 while (*p == CTLQUOTEMARK) 1544 p++; 1545 if (*p == CTLESC) 1546 p++; 1547 if (localeisutf8) { 1548 wc2 = get_wc(&p); 1549 if (wc2 == 0) /* bad utf-8 */ 1550 return 0; 1551 } else 1552 wc2 = (unsigned char)*p++; 1553 if ( collate_range_cmp(chr, wc) >= 0 1554 && collate_range_cmp(chr, wc2) <= 0 1555 ) 1556 found = 1; 1557 } else { 1558 if (chr == wc) 1559 found = 1; 1560 } 1561 } while ((c = *p++) != ']'); 1562 if (found == invert) 1563 goto backtrack; 1564 break; 1565 } 1566 dft: default: 1567 if (squoted && *q == CTLESC) 1568 q++; 1569 if (*q == '\0') 1570 return 0; 1571 if (*q++ == c) 1572 break; 1573 backtrack: 1574 /* 1575 * If we have a mismatch (other than hitting the end 1576 * of the string), go back to the last '*' seen and 1577 * have it match one additional character. 1578 */ 1579 if (bt_p == NULL) 1580 return 0; 1581 if (squoted && *bt_q == CTLESC) 1582 bt_q++; 1583 if (*bt_q == '\0') 1584 return 0; 1585 bt_q++; 1586 p = bt_p; 1587 q = bt_q; 1588 break; 1589 } 1590 } 1591 } 1592 1593 1594 1595 /* 1596 * Remove any CTLESC and CTLQUOTEMARK characters from a string. 1597 */ 1598 1599 void 1600 rmescapes(char *str) 1601 { 1602 char *p, *q; 1603 1604 p = str; 1605 while (*p != CTLESC && *p != CTLQUOTEMARK && *p != CTLQUOTEEND) { 1606 if (*p++ == '\0') 1607 return; 1608 } 1609 q = p; 1610 while (*p) { 1611 if (*p == CTLQUOTEMARK || *p == CTLQUOTEEND) { 1612 p++; 1613 continue; 1614 } 1615 if (*p == CTLESC) 1616 p++; 1617 *q++ = *p++; 1618 } 1619 *q = '\0'; 1620 } 1621 1622 1623 1624 /* 1625 * See if a pattern matches in a case statement. 1626 */ 1627 1628 int 1629 casematch(union node *pattern, const char *val) 1630 { 1631 struct stackmark smark; 1632 int result; 1633 char *p; 1634 1635 setstackmark(&smark); 1636 argbackq = pattern->narg.backquote; 1637 STARTSTACKSTR(expdest); 1638 ifslastp = NULL; 1639 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE); 1640 STPUTC('\0', expdest); 1641 p = grabstackstr(expdest); 1642 result = patmatch(p, val, 0); 1643 popstackmark(&smark); 1644 return result; 1645 } 1646 1647 /* 1648 * Our own itoa(). 1649 */ 1650 1651 static char * 1652 cvtnum(int num, char *buf) 1653 { 1654 char temp[32]; 1655 int neg = num < 0; 1656 char *p = temp + 31; 1657 1658 temp[31] = '\0'; 1659 1660 do { 1661 *--p = num % 10 + '0'; 1662 } while ((num /= 10) != 0); 1663 1664 if (neg) 1665 *--p = '-'; 1666 1667 STPUTS(p, buf); 1668 return buf; 1669 } 1670 1671 /* 1672 * Do most of the work for wordexp(3). 1673 */ 1674 1675 int 1676 wordexpcmd(int argc, char **argv) 1677 { 1678 size_t len; 1679 int i; 1680 1681 out1fmt("%08x", argc - 1); 1682 for (i = 1, len = 0; i < argc; i++) 1683 len += strlen(argv[i]); 1684 out1fmt("%08x", (int)len); 1685 for (i = 1; i < argc; i++) 1686 outbin(argv[i], strlen(argv[i]) + 1, out1); 1687 return (0); 1688 } 1689