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