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