glob.c revision 1.5 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.5 1993/11/03 18:02:57 mycroft 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) {
161 blkfree(nv);
162 return (-LBRC);
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 (at) 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