1 /* $OpenBSD: glob.c,v 1.22 2015/12/26 13:48:38 mestre Exp $ */ 2 /* $NetBSD: glob.c,v 1.10 1995/03/21 09:03:01 cgd Exp $ */ 3 4 /*- 5 * Copyright (c) 1980, 1991, 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the name of the University nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33 #include <sys/types.h> 34 #include <glob.h> 35 #include <errno.h> 36 #include <stdlib.h> 37 #include <string.h> 38 #include <unistd.h> 39 #include <limits.h> 40 #include <stdarg.h> 41 42 #include "csh.h" 43 #include "extern.h" 44 45 static int noglob; 46 static int pargsiz, gargsiz; 47 48 /* 49 * Values for gflag 50 */ 51 #define G_NONE 0 /* No globbing needed */ 52 #define G_GLOB 1 /* string contains *?[] characters */ 53 #define G_CSH 2 /* string contains ~`{ characters */ 54 55 #define GLOBSPACE 100 /* Alloc increment */ 56 57 #define LBRC '{' 58 #define RBRC '}' 59 #define LBRK '[' 60 #define RBRK ']' 61 #define EOS '\0' 62 63 Char **gargv = NULL; 64 long gargc = 0; 65 Char **pargv = NULL; 66 long pargc = 0; 67 68 /* 69 * globbing is now done in two stages. In the first pass we expand 70 * csh globbing idioms ~`{ and then we proceed doing the normal 71 * globbing if needed ?*[ 72 * 73 * Csh type globbing is handled in globexpand() and the rest is 74 * handled in glob() which is part of the 4.4BSD libc. 75 * 76 */ 77 static Char *globtilde(Char **, Char *); 78 static Char **libglob(Char **); 79 static Char **globexpand(Char **); 80 static int globbrace(Char *, Char *, Char ***); 81 static void expbrace(Char ***, Char ***, int); 82 static int pmatch(Char *, Char *); 83 static void pword(void); 84 static void psave(int); 85 static void backeval(Char *, bool); 86 87 88 static Char * 89 globtilde(Char **nv, Char *s) 90 { 91 Char gbuf[PATH_MAX], *gstart, *b, *u, *e; 92 93 gstart = gbuf; 94 *gstart++ = *s++; 95 u = s; 96 for (b = gstart, e = &gbuf[PATH_MAX - 1]; 97 *s && *s != '/' && *s != ':' && b < e; 98 *b++ = *s++) 99 continue; 100 *b = EOS; 101 if (gethdir(gstart, &gbuf[sizeof(gbuf)/sizeof(Char)] - gstart)) { 102 blkfree(nv); 103 if (*gstart) 104 stderror(ERR_UNKUSER, vis_str(gstart)); 105 else 106 stderror(ERR_NOHOME); 107 } 108 b = &gstart[Strlen(gstart)]; 109 while (*s) 110 *b++ = *s++; 111 *b = EOS; 112 --u; 113 free(u); 114 return (Strsave(gstart)); 115 } 116 117 static int 118 globbrace(Char *s, Char *p, Char ***bl) 119 { 120 int i, len; 121 Char *pm, *pe, *lm, *pl; 122 Char **nv, **vl; 123 Char gbuf[PATH_MAX]; 124 int size = GLOBSPACE; 125 126 nv = vl = xreallocarray(NULL, size, sizeof(Char *)); 127 *vl = NULL; 128 129 len = 0; 130 /* copy part up to the brace */ 131 for (lm = gbuf, p = s; *p != LBRC; *lm++ = *p++) 132 continue; 133 134 /* check for balanced braces */ 135 for (i = 0, pe = ++p; *pe; pe++) 136 if (*pe == LBRK) { 137 /* Ignore everything between [] */ 138 for (++pe; *pe != RBRK && *pe != EOS; pe++) 139 continue; 140 if (*pe == EOS) { 141 blkfree(nv); 142 return (-RBRK); 143 } 144 } 145 else if (*pe == LBRC) 146 i++; 147 else if (*pe == RBRC) { 148 if (i == 0) 149 break; 150 i--; 151 } 152 153 if (i != 0 || *pe == '\0') { 154 blkfree(nv); 155 return (-RBRC); 156 } 157 158 for (i = 0, pl = pm = p; pm <= pe; pm++) 159 switch (*pm) { 160 case LBRK: 161 for (++pm; *pm != RBRK && *pm != EOS; pm++) 162 continue; 163 if (*pm == EOS) { 164 *vl = NULL; 165 blkfree(nv); 166 return (-RBRK); 167 } 168 break; 169 case LBRC: 170 i++; 171 break; 172 case RBRC: 173 if (i) { 174 i--; 175 break; 176 } 177 /* FALLTHROUGH */ 178 case ',': 179 if (i && *pm == ',') 180 break; 181 else { 182 Char savec = *pm; 183 184 *pm = EOS; 185 (void) Strlcpy(lm, pl, &gbuf[sizeof(gbuf)/sizeof(Char)] - lm); 186 (void) Strlcat(gbuf, pe + 1, PATH_MAX); 187 *pm = savec; 188 *vl++ = Strsave(gbuf); 189 len++; 190 pl = pm + 1; 191 if (vl == &nv[size]) { 192 size += GLOBSPACE; 193 nv = xreallocarray(nv, size, sizeof(Char *)); 194 vl = &nv[size - GLOBSPACE]; 195 } 196 } 197 break; 198 default: 199 break; 200 } 201 *vl = NULL; 202 *bl = nv; 203 return (len); 204 } 205 206 207 static void 208 expbrace(Char ***nvp, Char ***elp, int size) 209 { 210 Char **vl, **el, **nv, *s; 211 212 vl = nv = *nvp; 213 if (elp != NULL) 214 el = *elp; 215 else 216 for (el = vl; *el; el++) 217 continue; 218 219 for (s = *vl; s; s = *++vl) { 220 Char *b; 221 Char **vp, **bp; 222 223 /* leave {} untouched for find */ 224 if (s[0] == '{' && (s[1] == '\0' || (s[1] == '}' && s[2] == '\0'))) 225 continue; 226 if ((b = Strchr(s, '{')) != NULL) { 227 Char **bl; 228 int len; 229 230 if ((len = globbrace(s, b, &bl)) < 0) { 231 free(nv); 232 stderror(ERR_MISSING, -len); 233 } 234 free(s); 235 if (len == 1) { 236 *vl-- = *bl; 237 free(bl); 238 continue; 239 } 240 len = blklen(bl); 241 if (&el[len] >= &nv[size]) { 242 int l, e; 243 244 l = &el[len] - &nv[size]; 245 size += GLOBSPACE > l ? GLOBSPACE : l; 246 l = vl - nv; 247 e = el - nv; 248 nv = xreallocarray(nv, size, sizeof(Char *)); 249 vl = nv + l; 250 el = nv + e; 251 } 252 vp = vl--; 253 *vp = *bl; 254 len--; 255 for (bp = el; bp != vp; bp--) 256 bp[len] = *bp; 257 el += len; 258 vp++; 259 for (bp = bl + 1; *bp; *vp++ = *bp++) 260 continue; 261 free(bl); 262 } 263 264 } 265 if (elp != NULL) 266 *elp = el; 267 *nvp = nv; 268 } 269 270 static Char ** 271 globexpand(Char **v) 272 { 273 Char *s; 274 Char **nv, **vl, **el; 275 int size = GLOBSPACE; 276 277 278 nv = vl = xreallocarray(NULL, size, sizeof(Char *)); 279 *vl = NULL; 280 281 /* 282 * Step 1: expand backquotes. 283 */ 284 while ((s = *v++) != NULL) { 285 if (Strchr(s, '`')) { 286 int i; 287 288 (void) dobackp(s, 0); 289 for (i = 0; i < pargc; i++) { 290 *vl++ = pargv[i]; 291 if (vl == &nv[size]) { 292 size += GLOBSPACE; 293 nv = xreallocarray(nv, size, sizeof(Char *)); 294 vl = &nv[size - GLOBSPACE]; 295 } 296 } 297 free(pargv); 298 pargv = NULL; 299 } 300 else { 301 *vl++ = Strsave(s); 302 if (vl == &nv[size]) { 303 size += GLOBSPACE; 304 nv = xreallocarray(nv, size, sizeof(Char *)); 305 vl = &nv[size - GLOBSPACE]; 306 } 307 } 308 } 309 *vl = NULL; 310 311 if (noglob) 312 return (nv); 313 314 /* 315 * Step 2: expand braces 316 */ 317 el = vl; 318 expbrace(&nv, &el, size); 319 320 /* 321 * Step 3: expand ~ 322 */ 323 vl = nv; 324 for (s = *vl; s; s = *++vl) 325 if (*s == '~') 326 *vl = globtilde(nv, s); 327 vl = nv; 328 return (vl); 329 } 330 331 static Char * 332 handleone(Char *str, Char **vl, int action) 333 { 334 335 Char *cp, **vlp = vl; 336 337 switch (action) { 338 case G_ERROR: 339 setname(vis_str(str)); 340 blkfree(vl); 341 stderror(ERR_NAME | ERR_AMBIG); 342 break; 343 case G_APPEND: 344 trim(vlp); 345 str = Strsave(*vlp++); 346 do { 347 cp = Strspl(str, STRspace); 348 free(str); 349 str = Strspl(cp, *vlp); 350 free(cp); 351 } 352 while (*++vlp) 353 ; 354 blkfree(vl); 355 break; 356 case G_IGNORE: 357 str = Strsave(strip(*vlp)); 358 blkfree(vl); 359 break; 360 default: 361 break; 362 } 363 return (str); 364 } 365 366 static Char ** 367 libglob(Char **vl) 368 { 369 int gflgs = GLOB_QUOTE | GLOB_NOMAGIC; 370 glob_t globv; 371 char *ptr; 372 int nonomatch = adrof(STRnonomatch) != 0, magic = 0, match = 0; 373 374 if (!vl || !vl[0]) 375 return (vl); 376 377 globv.gl_offs = 0; 378 globv.gl_pathv = 0; 379 globv.gl_pathc = 0; 380 381 if (nonomatch) 382 gflgs |= GLOB_NOCHECK; 383 384 do { 385 ptr = short2qstr(*vl); 386 switch (glob(ptr, gflgs, 0, &globv)) { 387 case GLOB_ABORTED: 388 setname(vis_str(*vl)); 389 stderror(ERR_NAME | ERR_GLOB); 390 /* NOTREACHED */ 391 case GLOB_NOSPACE: 392 stderror(ERR_NOMEM); 393 /* NOTREACHED */ 394 default: 395 break; 396 } 397 if (globv.gl_flags & GLOB_MAGCHAR) { 398 match |= (globv.gl_matchc != 0); 399 magic = 1; 400 } 401 gflgs |= GLOB_APPEND; 402 } 403 while (*++vl) 404 ; 405 vl = (globv.gl_pathc == 0 || (magic && !match && !nonomatch)) ? 406 NULL : blk2short(globv.gl_pathv); 407 globfree(&globv); 408 return (vl); 409 } 410 411 Char * 412 globone(Char *str, int action) 413 { 414 Char *v[2], **vl, **vo; 415 int gflg; 416 417 noglob = adrof(STRnoglob) != 0; 418 gflag = 0; 419 v[0] = str; 420 v[1] = 0; 421 tglob(v); 422 gflg = gflag; 423 if (gflg == G_NONE) 424 return (strip(Strsave(str))); 425 426 if (gflg & G_CSH) { 427 /* 428 * Expand back-quote, tilde and brace 429 */ 430 vo = globexpand(v); 431 if (noglob || (gflg & G_GLOB) == 0) { 432 if (vo[0] == NULL) { 433 free(vo); 434 return (Strsave(STRNULL)); 435 } 436 if (vo[1] != NULL) 437 return (handleone(str, vo, action)); 438 else { 439 str = strip(vo[0]); 440 free(vo); 441 return (str); 442 } 443 } 444 } 445 else if (noglob || (gflg & G_GLOB) == 0) 446 return (strip(Strsave(str))); 447 else 448 vo = v; 449 450 vl = libglob(vo); 451 if ((gflg & G_CSH) && vl != vo) 452 blkfree(vo); 453 if (vl == NULL) { 454 setname(vis_str(str)); 455 stderror(ERR_NAME | ERR_NOMATCH); 456 } 457 if (vl[0] == NULL) { 458 free(vl); 459 return (Strsave(STRNULL)); 460 } 461 if (vl[1] != NULL) 462 return (handleone(str, vl, action)); 463 else { 464 str = strip(*vl); 465 free(vl); 466 return (str); 467 } 468 } 469 470 Char ** 471 globall(Char **v) 472 { 473 Char **vl, **vo; 474 int gflg = gflag; 475 476 if (!v || !v[0]) { 477 gargv = saveblk(v); 478 gargc = blklen(gargv); 479 return (gargv); 480 } 481 482 noglob = adrof(STRnoglob) != 0; 483 484 if (gflg & G_CSH) 485 /* 486 * Expand back-quote, tilde and brace 487 */ 488 vl = vo = globexpand(v); 489 else 490 vl = vo = saveblk(v); 491 492 if (!noglob && (gflg & G_GLOB)) { 493 vl = libglob(vo); 494 if ((gflg & G_CSH) && vl != vo) 495 blkfree(vo); 496 } 497 else 498 trim(vl); 499 500 gargc = vl ? blklen(vl) : 0; 501 return (gargv = vl); 502 } 503 504 void 505 ginit(void) 506 { 507 gargsiz = GLOBSPACE; 508 gargv = xreallocarray(NULL, gargsiz, sizeof(Char *)); 509 gargv[0] = 0; 510 gargc = 0; 511 } 512 513 void 514 rscan(Char **t, void (*f)(int)) 515 { 516 Char *p; 517 518 while ((p = *t++) != NULL) 519 while (*p) 520 (*f) (*p++); 521 } 522 523 void 524 trim(Char **t) 525 { 526 Char *p; 527 528 while ((p = *t++) != NULL) 529 while (*p) 530 *p++ &= TRIM; 531 } 532 533 void 534 tglob(Char **t) 535 { 536 Char *p, c; 537 538 while ((p = *t++) != NULL) { 539 if (*p == '~' || *p == '=') 540 gflag |= G_CSH; 541 else if (*p == '{' && 542 (p[1] == '\0' || (p[1] == '}' && p[2] == '\0'))) 543 continue; 544 while ((c = *p++) != '\0') { 545 /* 546 * eat everything inside the matching backquotes 547 */ 548 if (c == '`') { 549 gflag |= G_CSH; 550 while (*p && *p != '`') 551 if (*p++ == '\\') { 552 if (*p) /* Quoted chars */ 553 p++; 554 else 555 break; 556 } 557 if (*p) /* The matching ` */ 558 p++; 559 else 560 break; 561 } 562 else if (c == '{') 563 gflag |= G_CSH; 564 else if (isglob(c)) 565 gflag |= G_GLOB; 566 } 567 } 568 } 569 570 /* 571 * Command substitute cp. If literal, then this is a substitution from a 572 * << redirection, and so we should not crunch blanks and tabs, separating 573 * words only at newlines. 574 */ 575 Char ** 576 dobackp(Char *cp, bool literal) 577 { 578 Char *lp, *rp; 579 Char *ep, word[PATH_MAX]; 580 581 if (pargv) { 582 blkfree(pargv); 583 } 584 pargsiz = GLOBSPACE; 585 pargv = xreallocarray(NULL, pargsiz, sizeof(Char *)); 586 pargv[0] = NULL; 587 pargcp = pargs = word; 588 pargc = 0; 589 pnleft = PATH_MAX - 4; 590 for (;;) { 591 for (lp = cp; *lp != '`'; lp++) { 592 if (*lp == 0) { 593 if (pargcp != pargs) 594 pword(); 595 return (pargv); 596 } 597 psave(*lp); 598 } 599 lp++; 600 for (rp = lp; *rp && *rp != '`'; rp++) 601 if (*rp == '\\') { 602 rp++; 603 if (!*rp) 604 goto oops; 605 } 606 if (!*rp) 607 oops: stderror(ERR_UNMATCHED, '`'); 608 ep = Strsave(lp); 609 ep[rp - lp] = 0; 610 backeval(ep, literal); 611 cp = rp + 1; 612 } 613 } 614 615 static void 616 backeval(Char *cp, bool literal) 617 { 618 int icnt, c; 619 Char *ip; 620 struct command faket; 621 bool hadnl; 622 int pvec[2], quoted; 623 Char *fakecom[2], ibuf[BUFSIZ]; 624 char tibuf[BUFSIZ]; 625 626 hadnl = 0; 627 icnt = 0; 628 quoted = (literal || (cp[0] & QUOTE)) ? QUOTE : 0; 629 faket.t_dtyp = NODE_COMMAND; 630 faket.t_dflg = 0; 631 faket.t_dlef = 0; 632 faket.t_drit = 0; 633 faket.t_dspr = 0; 634 faket.t_dcom = fakecom; 635 fakecom[0] = STRfakecom1; 636 fakecom[1] = 0; 637 638 /* 639 * We do the psave job to temporarily change the current job so that the 640 * following fork is considered a separate job. This is so that when 641 * backquotes are used in a builtin function that calls glob the "current 642 * job" is not corrupted. We only need one level of pushed jobs as long as 643 * we are sure to fork here. 644 */ 645 psavejob(); 646 647 /* 648 * It would be nicer if we could integrate this redirection more with the 649 * routines in sh.sem.c by doing a fake execute on a builtin function that 650 * was piped out. 651 */ 652 mypipe(pvec); 653 if (pfork(&faket, -1) == 0) { 654 struct wordent paraml; 655 struct command *t; 656 657 (void) close(pvec[0]); 658 (void) dmove(pvec[1], 1); 659 (void) dmove(SHERR, 2); 660 initdesc(); 661 /* 662 * Bugfix for nested backquotes by Michael Greim <greim@sbsvax.UUCP>, 663 * posted to comp.bugs.4bsd 12 Sep. 1989. 664 */ 665 if (pargv) /* mg, 21.dec.88 */ 666 blkfree(pargv), pargv = 0, pargsiz = 0; 667 /* mg, 21.dec.88 */ 668 arginp = cp; 669 while (*cp) 670 *cp++ &= TRIM; 671 672 /* 673 * In the child ``forget'' everything about current aliases or 674 * eval vectors. 675 */ 676 alvec = NULL; 677 evalvec = NULL; 678 alvecp = NULL; 679 evalp = NULL; 680 (void) lex(¶ml); 681 if (seterr) 682 stderror(ERR_OLD); 683 alias(¶ml); 684 t = syntax(paraml.next, ¶ml, 0); 685 if (seterr) 686 stderror(ERR_OLD); 687 if (t) 688 t->t_dflg |= F_NOFORK; 689 (void) signal(SIGTSTP, SIG_IGN); 690 (void) signal(SIGTTIN, SIG_IGN); 691 (void) signal(SIGTTOU, SIG_IGN); 692 execute(t, -1, NULL, NULL); 693 exitstat(); 694 } 695 free(cp); 696 (void) close(pvec[1]); 697 c = 0; 698 ip = NULL; 699 do { 700 int cnt = 0; 701 702 for (;;) { 703 if (icnt == 0) { 704 int i; 705 706 ip = ibuf; 707 do 708 icnt = read(pvec[0], tibuf, BUFSIZ); 709 while (icnt == -1 && errno == EINTR); 710 if (icnt <= 0) { 711 c = -1; 712 break; 713 } 714 for (i = 0; i < icnt; i++) 715 ip[i] = (unsigned char) tibuf[i]; 716 } 717 if (hadnl) 718 break; 719 --icnt; 720 c = (*ip++ & TRIM); 721 if (c == 0) 722 break; 723 if (c == '\n') { 724 /* 725 * Continue around the loop one more time, so that we can eat 726 * the last newline without terminating this word. 727 */ 728 hadnl = 1; 729 continue; 730 } 731 if (!quoted && (c == ' ' || c == '\t')) 732 break; 733 cnt++; 734 psave(c | quoted); 735 } 736 /* 737 * Unless at end-of-file, we will form a new word here if there were 738 * characters in the word, or in any case when we take text literally. 739 * If we didn't make empty words here when literal was set then we 740 * would lose blank lines. 741 */ 742 if (c != -1 && (cnt || literal)) 743 pword(); 744 hadnl = 0; 745 } while (c >= 0); 746 (void) close(pvec[0]); 747 pwait(); 748 prestjob(); 749 } 750 751 static void 752 psave(int c) 753 { 754 if (--pnleft <= 0) 755 stderror(ERR_WTOOLONG); 756 *pargcp++ = c; 757 } 758 759 static void 760 pword(void) 761 { 762 psave(0); 763 if (pargc == pargsiz - 1) { 764 pargsiz += GLOBSPACE; 765 pargv = xreallocarray(pargv, pargsiz, sizeof(Char *)); 766 } 767 pargv[pargc++] = Strsave(pargs); 768 pargv[pargc] = NULL; 769 pargcp = pargs; 770 pnleft = PATH_MAX - 4; 771 } 772 773 int 774 Gmatch(Char *string, Char *pattern) 775 { 776 Char **blk, **p; 777 int gpol = 1, gres = 0; 778 779 if (*pattern == '^') { 780 gpol = 0; 781 pattern++; 782 } 783 784 blk = xreallocarray(NULL, GLOBSPACE, sizeof(Char *)); 785 blk[0] = Strsave(pattern); 786 blk[1] = NULL; 787 788 expbrace(&blk, NULL, GLOBSPACE); 789 790 for (p = blk; *p; p++) 791 gres |= pmatch(string, *p); 792 793 blkfree(blk); 794 return(gres == gpol); 795 } 796 797 static int 798 pmatch(Char *string, Char *pattern) 799 { 800 Char stringc, patternc; 801 int match, negate_range; 802 Char rangec; 803 804 for (;; ++string) { 805 stringc = *string & TRIM; 806 patternc = *pattern++; 807 switch (patternc) { 808 case 0: 809 return (stringc == 0); 810 case '?': 811 if (stringc == 0) 812 return (0); 813 break; 814 case '*': 815 if (!*pattern) 816 return (1); 817 while (*string) 818 if (Gmatch(string++, pattern)) 819 return (1); 820 return (0); 821 case '[': 822 match = 0; 823 if ((negate_range = (*pattern == '^')) != 0) 824 pattern++; 825 while ((rangec = *pattern++) != '\0') { 826 if (rangec == ']') 827 break; 828 if (match) 829 continue; 830 if (rangec == '-' && *(pattern-2) != '[' && *pattern != ']') { 831 match = (stringc <= (*pattern & TRIM) && 832 (*(pattern-2) & TRIM) <= stringc); 833 pattern++; 834 } 835 else 836 match = (stringc == (rangec & TRIM)); 837 } 838 if (rangec == 0) 839 stderror(ERR_NAME | ERR_MISSING, ']'); 840 if (match == negate_range) 841 return (0); 842 break; 843 default: 844 if ((patternc & TRIM) != stringc) 845 return (0); 846 break; 847 848 } 849 } 850 } 851 852 void 853 Gcat(Char *s1, Char *s2) 854 { 855 Char *p, *q; 856 int n; 857 858 for (p = s1; *p++;) 859 continue; 860 for (q = s2; *q++;) 861 continue; 862 n = (p - s1) + (q - s2) - 1; 863 if (++gargc >= gargsiz) { 864 gargsiz += GLOBSPACE; 865 gargv = xreallocarray(gargv, gargsiz, sizeof(Char *)); 866 } 867 gargv[gargc] = 0; 868 p = gargv[gargc - 1] = xreallocarray(NULL, n, sizeof(Char)); 869 for (q = s1; (*p++ = *q++) != '\0';) 870 continue; 871 for (p--, q = s2; (*p++ = *q++) != '\0';) 872 continue; 873 } 874 875 int 876 sortscmp(const void *a, const void *b) 877 { 878 char buf[2048]; 879 880 if (!a) /* check for NULL */ 881 return (b ? 1 : 0); 882 if (!b) 883 return (-1); 884 885 if (!*(Char **)a) /* check for NULL */ 886 return (*(Char **)b ? 1 : 0); 887 if (!*(Char **)b) 888 return (-1); 889 890 (void) strlcpy(buf, short2str(*(Char **)a), sizeof buf); 891 return ((int) strcoll(buf, short2str(*(Char **)b))); 892 } 893