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