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.35 (Berkeley) 02/05/92"; 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 vl = nv; 310 expbrace(&vl, &el, size); 311 312 313 /* 314 * Step 3: expand ~ 315 */ 316 vl = nv; 317 for (s = *vl; s; s = *++vl) 318 if (*s == '~') 319 *vl = globtilde(nv, s); 320 vl = nv; 321 return (vl); 322 } 323 324 static Char * 325 handleone(str, vl, action) 326 Char *str, **vl; 327 int action; 328 { 329 330 Char *cp, **vlp = vl; 331 332 switch (action) { 333 case G_ERROR: 334 setname(vis_str(str)); 335 blkfree(vl); 336 stderror(ERR_NAME | ERR_AMBIG); 337 break; 338 case G_APPEND: 339 trim(vlp); 340 str = Strsave(*vlp++); 341 do { 342 cp = Strspl(str, STRspace); 343 xfree((ptr_t) str); 344 str = Strspl(cp, *vlp); 345 xfree((ptr_t) cp); 346 } 347 while (*++vlp); 348 blkfree(vl); 349 break; 350 case G_IGNORE: 351 str = Strsave(strip(*vlp)); 352 blkfree(vl); 353 break; 354 default: 355 break; 356 } 357 return (str); 358 } 359 360 static Char ** 361 libglob(vl) 362 Char **vl; 363 { 364 int gflgs = GLOB_QUOTE | GLOB_NOMAGIC; 365 glob_t globv; 366 char *ptr; 367 int nonomatch = adrof(STRnonomatch) != 0, magic = 0, match = 0; 368 369 if (!vl || !vl[0]) 370 return (vl); 371 372 globv.gl_offs = 0; 373 globv.gl_pathv = 0; 374 globv.gl_pathc = 0; 375 376 if (nonomatch) 377 gflgs |= GLOB_NOCHECK; 378 379 do { 380 ptr = short2qstr(*vl); 381 switch (glob(ptr, gflgs, 0, &globv)) { 382 case GLOB_ABEND: 383 setname(vis_str(*vl)); 384 stderror(ERR_NAME | ERR_GLOB); 385 /* NOTREACHED */ 386 case GLOB_NOSPACE: 387 stderror(ERR_NOMEM); 388 /* NOTREACHED */ 389 default: 390 break; 391 } 392 if (globv.gl_flags & GLOB_MAGCHAR) { 393 match |= (globv.gl_matchc != 0); 394 magic = 1; 395 } 396 gflgs |= GLOB_APPEND; 397 } 398 while (*++vl); 399 vl = (globv.gl_pathc == 0 || (magic && !match && !nonomatch)) ? 400 NULL : blk2short(globv.gl_pathv); 401 globfree(&globv); 402 return (vl); 403 } 404 405 Char * 406 globone(str, action) 407 Char *str; 408 int action; 409 { 410 Char *v[2], **vl, **vo; 411 int gflg; 412 413 noglob = adrof(STRnoglob) != 0; 414 gflag = 0; 415 v[0] = str; 416 v[1] = 0; 417 tglob(v); 418 gflg = gflag; 419 if (gflg == G_NONE) 420 return (strip(Strsave(str))); 421 422 if (gflg & G_CSH) { 423 /* 424 * Expand back-quote, tilde and brace 425 */ 426 vo = globexpand(v); 427 if (noglob || (gflg & G_GLOB) == 0) { 428 if (vo[0] == NULL) { 429 xfree((ptr_t) vo); 430 return (Strsave(STRNULL)); 431 } 432 if (vo[1] != NULL) 433 return (handleone(str, vo, action)); 434 else { 435 str = strip(vo[0]); 436 xfree((ptr_t) vo); 437 return (str); 438 } 439 } 440 } 441 else if (noglob || (gflg & G_GLOB) == 0) 442 return (strip(Strsave(str))); 443 else 444 vo = v; 445 446 vl = libglob(vo); 447 if ((gflg & G_CSH) && vl != vo) 448 blkfree(vo); 449 if (vl == NULL) { 450 setname(vis_str(str)); 451 stderror(ERR_NAME | ERR_NOMATCH); 452 } 453 if (vl[0] == NULL) { 454 xfree((ptr_t) vl); 455 return (Strsave(STRNULL)); 456 } 457 if (vl[1] != NULL) 458 return (handleone(str, vl, action)); 459 else { 460 str = strip(*vl); 461 xfree((ptr_t) vl); 462 return (str); 463 } 464 } 465 466 Char ** 467 globall(v) 468 Char **v; 469 { 470 Char **vl, **vo; 471 int gflg = gflag; 472 473 if (!v || !v[0]) { 474 gargv = saveblk(v); 475 gargc = blklen(gargv); 476 return (gargv); 477 } 478 479 noglob = adrof(STRnoglob) != 0; 480 481 if (gflg & G_CSH) 482 /* 483 * Expand back-quote, tilde and brace 484 */ 485 vl = vo = globexpand(v); 486 else 487 vl = vo = saveblk(v); 488 489 if (!noglob && (gflg & G_GLOB)) { 490 vl = libglob(vo); 491 if ((gflg & G_CSH) && vl != vo) 492 blkfree(vo); 493 } 494 else 495 trim(vl); 496 497 gargc = vl ? blklen(vl) : 0; 498 return (gargv = vl); 499 } 500 501 void 502 ginit() 503 { 504 gargsiz = GLOBSPACE; 505 gargv = (Char **) xmalloc((size_t) sizeof(Char *) * gargsiz); 506 gargv[0] = 0; 507 gargc = 0; 508 } 509 510 void 511 rscan(t, f) 512 register Char **t; 513 void (*f) (); 514 { 515 register Char *p; 516 517 while (p = *t++) 518 while (*p) 519 (*f) (*p++); 520 } 521 522 void 523 trim(t) 524 register Char **t; 525 { 526 register Char *p; 527 528 while (p = *t++) 529 while (*p) 530 *p++ &= TRIM; 531 } 532 533 void 534 tglob(t) 535 register Char **t; 536 { 537 register Char *p, c; 538 539 while (p = *t++) { 540 if (*p == '~' || *p == '=') 541 gflag |= G_CSH; 542 else if (*p == '{' && 543 (p[1] == '\0' || (p[1] == '}' && p[2] == '\0'))) 544 continue; 545 while (c = *p++) { 546 /* 547 * eat everything inside the matching backquotes 548 */ 549 if (c == '`') { 550 gflag |= G_CSH; 551 while (*p && *p != '`') 552 if (*p++ == '\\') { 553 if (*p) /* Quoted chars */ 554 p++; 555 else 556 break; 557 } 558 if (*p) /* The matching ` */ 559 p++; 560 else 561 break; 562 } 563 else if (c == '{') 564 gflag |= G_CSH; 565 else if (isglob(c)) 566 gflag |= G_GLOB; 567 } 568 } 569 } 570 571 /* 572 * Command substitute cp. If literal, then this is a substitution from a 573 * << redirection, and so we should not crunch blanks and tabs, separating 574 * words only at newlines. 575 */ 576 Char ** 577 dobackp(cp, literal) 578 Char *cp; 579 bool literal; 580 { 581 register Char *lp, *rp; 582 Char *ep, word[MAXPATHLEN]; 583 584 if (pargv) { 585 #ifdef notdef 586 abort(); 587 #endif 588 blkfree(pargv); 589 } 590 pargsiz = GLOBSPACE; 591 pargv = (Char **) xmalloc((size_t) sizeof(Char *) * pargsiz); 592 pargv[0] = NULL; 593 pargcp = pargs = word; 594 pargc = 0; 595 pnleft = MAXPATHLEN - 4; 596 for (;;) { 597 for (lp = cp; *lp != '`'; lp++) { 598 if (*lp == 0) { 599 if (pargcp != pargs) 600 pword(); 601 return (pargv); 602 } 603 psave(*lp); 604 } 605 lp++; 606 for (rp = lp; *rp && *rp != '`'; rp++) 607 if (*rp == '\\') { 608 rp++; 609 if (!*rp) 610 goto oops; 611 } 612 if (!*rp) 613 oops: stderror(ERR_UNMATCHED, '`'); 614 ep = Strsave(lp); 615 ep[rp - lp] = 0; 616 backeval(ep, literal); 617 cp = rp + 1; 618 } 619 } 620 621 static void 622 backeval(cp, literal) 623 Char *cp; 624 bool literal; 625 { 626 register int icnt, c; 627 register Char *ip; 628 struct command faket; 629 bool hadnl; 630 int pvec[2], quoted; 631 Char *fakecom[2], ibuf[BUFSIZ]; 632 char tibuf[BUFSIZ]; 633 634 hadnl = 0; 635 icnt = 0; 636 quoted = (literal || (cp[0] & QUOTE)) ? QUOTE : 0; 637 faket.t_dtyp = NODE_COMMAND; 638 faket.t_dflg = 0; 639 faket.t_dlef = 0; 640 faket.t_drit = 0; 641 faket.t_dspr = 0; 642 faket.t_dcom = fakecom; 643 fakecom[0] = STRfakecom1; 644 fakecom[1] = 0; 645 646 /* 647 * We do the psave job to temporarily change the current job so that the 648 * following fork is considered a separate job. This is so that when 649 * backquotes are used in a builtin function that calls glob the "current 650 * job" is not corrupted. We only need one level of pushed jobs as long as 651 * we are sure to fork here. 652 */ 653 psavejob(); 654 655 /* 656 * It would be nicer if we could integrate this redirection more with the 657 * routines in sh.sem.c by doing a fake execute on a builtin function that 658 * was piped out. 659 */ 660 mypipe(pvec); 661 if (pfork(&faket, -1) == 0) { 662 struct wordent paraml; 663 struct command *t; 664 665 (void) close(pvec[0]); 666 (void) dmove(pvec[1], 1); 667 (void) dmove(SHERR, 2); 668 initdesc(); 669 /* 670 * Bugfix for nested backquotes by Michael Greim <greim@sbsvax.UUCP>, 671 * posted to comp.bugs.4bsd 12 Sep. 1989. 672 */ 673 if (pargv) /* mg, 21.dec.88 */ 674 blkfree(pargv), pargv = 0, pargsiz = 0; 675 /* mg, 21.dec.88 */ 676 arginp = cp; 677 while (*cp) 678 *cp++ &= TRIM; 679 680 /* 681 * In the child ``forget'' everything about current aliases or 682 * eval vectors. 683 */ 684 alvec = NULL; 685 evalvec = NULL; 686 alvecp = NULL; 687 evalp = NULL; 688 (void) lex(¶ml); 689 if (seterr) 690 stderror(ERR_OLD); 691 alias(¶ml); 692 t = syntax(paraml.next, ¶ml, 0); 693 if (seterr) 694 stderror(ERR_OLD); 695 if (t) 696 t->t_dflg |= F_NOFORK; 697 (void) signal(SIGTSTP, SIG_IGN); 698 (void) signal(SIGTTIN, SIG_IGN); 699 (void) signal(SIGTTOU, SIG_IGN); 700 execute(t, -1, NULL, NULL); 701 exitstat(); 702 } 703 xfree((ptr_t) cp); 704 (void) close(pvec[1]); 705 c = 0; 706 ip = NULL; 707 do { 708 int cnt = 0; 709 710 for (;;) { 711 if (icnt == 0) { 712 int i; 713 714 ip = ibuf; 715 do 716 icnt = read(pvec[0], tibuf, BUFSIZ); 717 while (icnt == -1 && errno == EINTR); 718 if (icnt <= 0) { 719 c = -1; 720 break; 721 } 722 for (i = 0; i < icnt; i++) 723 ip[i] = (unsigned char) tibuf[i]; 724 } 725 if (hadnl) 726 break; 727 --icnt; 728 c = (*ip++ & TRIM); 729 if (c == 0) 730 break; 731 if (c == '\n') { 732 /* 733 * Continue around the loop one more time, so that we can eat 734 * the last newline without terminating this word. 735 */ 736 hadnl = 1; 737 continue; 738 } 739 if (!quoted && (c == ' ' || c == '\t')) 740 break; 741 cnt++; 742 psave(c | quoted); 743 } 744 /* 745 * Unless at end-of-file, we will form a new word here if there were 746 * characters in the word, or in any case when we take text literally. 747 * If we didn't make empty words here when literal was set then we 748 * would lose blank lines. 749 */ 750 if (c != -1 && (cnt || literal)) 751 pword(); 752 hadnl = 0; 753 } while (c >= 0); 754 (void) close(pvec[0]); 755 pwait(); 756 prestjob(); 757 } 758 759 static void 760 psave(c) 761 int c; 762 { 763 if (--pnleft <= 0) 764 stderror(ERR_WTOOLONG); 765 *pargcp++ = c; 766 } 767 768 static void 769 pword() 770 { 771 psave(0); 772 if (pargc == pargsiz - 1) { 773 pargsiz += GLOBSPACE; 774 pargv = (Char **) xrealloc((ptr_t) pargv, 775 (size_t) pargsiz * sizeof(Char *)); 776 } 777 pargv[pargc++] = Strsave(pargs); 778 pargv[pargc] = NULL; 779 pargcp = pargs; 780 pnleft = MAXPATHLEN - 4; 781 } 782 783 int 784 Gmatch(string, pattern) 785 Char *string, *pattern; 786 { 787 Char **blk, **p; 788 int gpol = 1, gres = 0; 789 790 if (*pattern == '^') { 791 gpol = 0; 792 pattern++; 793 } 794 795 blk = (Char **) xmalloc(GLOBSPACE * sizeof(Char *)); 796 blk[0] = Strsave(pattern); 797 blk[1] = NULL; 798 799 expbrace(&blk, NULL, GLOBSPACE); 800 801 for (p = blk; *p; p++) 802 gres |= pmatch(string, *p); 803 804 blkfree(blk); 805 return(gres == gpol); 806 } 807 808 static int 809 pmatch(string, pattern) 810 register Char *string, *pattern; 811 { 812 register Char stringc, patternc; 813 int match, negate_range; 814 Char rangec; 815 816 for (;; ++string) { 817 stringc = *string & TRIM; 818 patternc = *pattern++; 819 switch (patternc) { 820 case 0: 821 return (stringc == 0); 822 case '?': 823 if (stringc == 0) 824 return (0); 825 break; 826 case '*': 827 if (!*pattern) 828 return (1); 829 while (*string) 830 if (Gmatch(string++, pattern)) 831 return (1); 832 return (0); 833 case '[': 834 match = 0; 835 if (negate_range = (*pattern == '^')) 836 pattern++; 837 while (rangec = *pattern++) { 838 if (rangec == ']') 839 break; 840 if (match) 841 continue; 842 if (rangec == '-' && *(pattern-2) != '[' && *pattern != ']') { 843 match = (stringc <= (*pattern & TRIM) && 844 (*(pattern-2) & TRIM) <= stringc); 845 pattern++; 846 } 847 else 848 match = (stringc == (rangec & TRIM)); 849 } 850 if (rangec == 0) 851 stderror(ERR_NAME | ERR_MISSING, ']'); 852 if (match == negate_range) 853 return (0); 854 break; 855 default: 856 if ((patternc & TRIM) != stringc) 857 return (0); 858 break; 859 860 } 861 } 862 } 863 864 void 865 Gcat(s1, s2) 866 Char *s1, *s2; 867 { 868 register Char *p, *q; 869 int n; 870 871 for (p = s1; *p++;) 872 continue; 873 for (q = s2; *q++;) 874 continue; 875 n = (p - s1) + (q - s2) - 1; 876 if (++gargc >= gargsiz) { 877 gargsiz += GLOBSPACE; 878 gargv = (Char **) xrealloc((ptr_t) gargv, 879 (size_t) gargsiz * sizeof(Char *)); 880 } 881 gargv[gargc] = 0; 882 p = gargv[gargc - 1] = (Char *) xmalloc((size_t) n * sizeof(Char)); 883 for (q = s1; *p++ = *q++;) 884 continue; 885 for (p--, q = s2; *p++ = *q++;) 886 continue; 887 } 888 889 #ifdef FILEC 890 int 891 sortscmp(a, b) 892 register const ptr_t a, b; 893 { 894 #if defined(NLS) && !defined(NOSTRCOLL) 895 char buf[2048]; 896 #endif 897 898 if (a) /* check for NULL */ 899 return (b ? 1 : 0); 900 if (!b) 901 return (-1); 902 903 if (!*(Char **)a) /* check for NULL */ 904 return (*(Char **)b ? 1 : 0); 905 if (!*(Char **)b) 906 return (-1); 907 908 #if defined(NLS) && !defined(NOSTRCOLL) 909 (void) strcpy(buf, short2str(*(Char **)a)); 910 return ((int) strcoll(buf, short2str(*(Char **)b))); 911 #else 912 return ((int) Strcmp(*(Char **)a, *(Char **)b)); 913 #endif 914 } 915 #endif /* FILEC */ 916