This commit sets new users to see the DragonFly-tips fortunes instead
[dragonfly.git] / contrib / opie / glob.c
blobc1bd65801079b906ca9781c0cd7fe45d2328bb65
1 /* glob.c: The csh et al glob pattern matching routines.
3 %%% copyright-cmetz-96
4 This software is Copyright 1996-2001 by Craig Metz, All Rights Reserved.
5 The Inner Net License Version 3 applies to this software.
6 You should have received a copy of the license with this software. If
7 you didn't get a copy, you may request one from <license@inner.net>.
9 Portions of this software are Copyright 1995 by Randall Atkinson and Dan
10 McDonald, All Rights Reserved. All Rights under this copyright are assigned
11 to the U.S. Naval Research Laboratory (NRL). The NRL Copyright Notice and
12 License Agreement applies to this software.
14 History:
16 Modified by cmetz for OPIE 2.32. Remove include of dirent.h here; it's
17 done already (and conditionally) in opie_cfg.h.
18 Modified by cmetz for OPIE 2.2. Use FUNCTION declaration et al.
19 Remove useless strings. Prototype right.
20 Modified at NRL for OPIE 2.0.
21 Originally from BSD.
24 * Copyright (c) 1980 Regents of the University of California.
25 * All rights reserved.
27 * Redistribution and use in source and binary forms, with or without
28 * modification, are permitted provided that the following conditions
29 * are met:
30 * 1. Redistributions of source code must retain the above copyright
31 * notice, this list of conditions and the following disclaimer.
32 * 2. Redistributions in binary form must reproduce the above copyright
33 * notice, this list of conditions and the following disclaimer in the
34 * documentation and/or other materials provided with the distribution.
35 * 3. All advertising materials mentioning features or use of this software
36 * must display the following acknowledgement:
37 * This product includes software developed by the University of
38 * California, Berkeley and its contributors.
39 * 4. Neither the name of the University nor the names of its contributors
40 * may be used to endorse or promote products derived from this software
41 * without specific prior written permission.
43 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
44 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
45 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
46 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
47 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
48 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
49 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
50 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
51 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
52 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
53 * SUCH DAMAGE.
57 * C-shell glob for random programs.
60 #include "opie_cfg.h"
62 #if HAVE_SYS_PARAM_H
63 #include <sys/param.h>
64 #endif /* HAVE_SYS_PARAM_H */
65 #include <sys/stat.h>
67 #if HAVE_PWD_H
68 #include <pwd.h>
69 #endif /* HAVE_PWD_H */
70 #include <errno.h>
71 #include <stdio.h>
72 #include <string.h>
73 #if HAVE_LIMITS_H
74 #include <limits.h>
75 #endif /* HAVE_LIMITS_H */
77 #include "opie.h"
79 #ifndef NCARGS
80 #define NCARGS 600
81 #endif /* NCARGS */
82 #define QUOTE 0200
83 #define TRIM 0177
84 #define eq(a,b) (strcmp((a),(b)) == (0))
85 #define GAVSIZ (NCARGS/6)
86 #define isdir(d) (((d.st_mode) & S_IFMT) == S_IFDIR)
88 static char **gargv; /* Pointer to the (stack) arglist */
89 static int gargc; /* Number args in gargv */
90 static int gnleft;
91 static short gflag;
93 static int letter __P((register char));
94 static int digit __P((register char));
95 static int any __P((int, char *));
96 static int blklen __P((register char **));
97 VOIDRET blkfree __P((char **));
98 static char *strspl __P((register char *, register char *));
100 static int tglob __P((register char c));
102 extern int errno;
103 static char *strend __P((char *));
105 static int globcnt;
107 static char *globchars = "`{[*?";
108 char *globerr = NULL;
109 char *home = NULL;
111 static char *gpath, *gpathp, *lastgpathp;
112 static int globbed;
113 static char *entp;
114 static char **sortbas;
116 static int amatch __P((char *p, char *s));
117 static int execbrc __P((register char *p, register char *s));
118 VOIDRET opiefatal __P((char *));
119 char **copyblk __P((char **));
121 static int match FUNCTION((s, p), char *s AND char *p)
123 register int c;
124 register char *sentp;
125 char sglobbed = globbed;
127 if (*s == '.' && *p != '.')
128 return (0);
129 sentp = entp;
130 entp = s;
131 c = amatch(s, p);
132 entp = sentp;
133 globbed = sglobbed;
134 return (c);
138 static int Gmatch FUNCTION((s, p), register char *s AND register char *p)
140 register int scc;
141 int ok, lc;
142 int c, cc;
144 for (;;) {
145 scc = *s++ & TRIM;
146 switch (c = *p++) {
148 case '[':
149 ok = 0;
150 lc = 077777;
151 while (cc = *p++) {
152 if (cc == ']') {
153 if (ok)
154 break;
155 return (0);
157 if (cc == '-') {
158 if (lc <= scc && scc <= *p++)
159 ok++;
160 } else
161 if (scc == (lc = cc))
162 ok++;
164 if (cc == 0)
165 if (ok)
166 p--;
167 else
168 return 0;
169 continue;
171 case '*':
172 if (!*p)
173 return (1);
174 for (s--; *s; s++)
175 if (Gmatch(s, p))
176 return (1);
177 return (0);
179 case 0:
180 return (scc == 0);
182 default:
183 if ((c & TRIM) != scc)
184 return (0);
185 continue;
187 case '?':
188 if (scc == 0)
189 return (0);
190 continue;
196 static VOIDRET Gcat FUNCTION((s1, s2), register char *s1 AND register char *s2)
198 register int len = strlen(s1) + strlen(s2) + 1;
200 if (len >= gnleft || gargc >= GAVSIZ - 1)
201 globerr = "Arguments too long";
202 else {
203 gargc++;
204 gnleft -= len;
205 gargv[gargc] = 0;
206 gargv[gargc - 1] = strspl(s1, s2);
210 static VOIDRET addpath FUNCTION((c), char c)
213 if (gpathp >= lastgpathp)
214 globerr = "Pathname too long";
215 else {
216 *gpathp++ = c;
217 *gpathp = 0;
221 static VOIDRET rscan FUNCTION((t, f), register char **t AND int (*f)__P((char)))
223 register char *p, c;
225 while (p = *t++) {
226 if (f == tglob)
227 if (*p == '~')
228 gflag |= 2;
229 else
230 if (eq(p, "{") || eq(p, "{}"))
231 continue;
232 while (c = *p++)
233 (*f) (c);
237 static int tglob FUNCTION((c), register char c)
239 if (any(c, globchars))
240 gflag |= c == '{' ? 2 : 1;
241 return (c);
244 static int letter FUNCTION((c), register char c)
246 return (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c == '_');
249 static int digit FUNCTION((c), register char c)
251 return (c >= '0' && c <= '9');
254 static int any FUNCTION((c, s), int c AND char *s)
256 while (*s)
257 if (*s++ == c)
258 return (1);
259 return (0);
262 static int blklen FUNCTION((av), register char **av)
264 register int i = 0;
266 while (*av++)
267 i++;
268 return (i);
271 static char **blkcpy FUNCTION((oav, bv), char **oav AND register char **bv)
273 register char **av = oav;
275 while (*av++ = *bv++)
276 continue;
277 return (oav);
280 VOIDRET blkfree FUNCTION((av0), char **av0)
282 register char **av = av0;
284 while (*av)
285 free(*av++);
288 static char *strspl FUNCTION((cp, dp), register char *cp AND register char *dp)
290 register char *ep = (char *) malloc((unsigned) (strlen(cp) +
291 strlen(dp) + 1));
293 if (ep == (char *) 0)
294 opiefatal("Out of memory");
295 strcpy(ep, cp);
296 strcat(ep, dp);
297 return (ep);
300 char **copyblk FUNCTION((v), char **v)
302 register char **nv = (char **) malloc((unsigned) ((blklen(v) + 1) *
303 sizeof(char **)));
305 if (nv == (char **) 0)
306 opiefatal("Out of memory");
308 return (blkcpy(nv, v));
311 static char *strend FUNCTION((cp), register char *cp)
314 while (*cp)
315 cp++;
316 return (cp);
320 * Extract a home directory from the password file
321 * The argument points to a buffer where the name of the
322 * user whose home directory is sought is currently.
323 * We write the home directory of the user back there.
325 static int gethdir FUNCTION((home), char *home)
327 register struct passwd *pp = getpwnam(home);
329 if (!pp || home + strlen(pp->pw_dir) >= lastgpathp)
330 return (1);
331 strcpy(home, pp->pw_dir);
332 return (0);
335 static VOIDRET ginit FUNCTION((agargv), char **agargv)
337 agargv[0] = 0;
338 gargv = agargv;
339 sortbas = agargv;
340 gargc = 0;
341 gnleft = NCARGS - 4;
344 static VOIDRET sort FUNCTION_NOARGS
346 register char **p1, **p2, *c;
347 char **Gvp = &gargv[gargc];
349 p1 = sortbas;
350 while (p1 < Gvp - 1) {
351 p2 = p1;
352 while (++p2 < Gvp)
353 if (strcmp(*p1, *p2) > 0)
354 c = *p1, *p1 = *p2, *p2 = c;
355 p1++;
357 sortbas = Gvp;
360 static VOIDRET matchdir FUNCTION((pattern), char *pattern)
362 struct stat stb;
364 register struct dirent *dp;
366 DIR *dirp;
368 dirp = opendir(*gpath == '\0' ? "." : gpath);
369 if (dirp == NULL) {
370 if (globbed)
371 return;
372 goto patherr2;
374 #if !defined(linux)
375 if (fstat(dirp->dd_fd, &stb) < 0)
376 goto patherr1;
377 if (!isdir(stb)) {
378 errno = ENOTDIR;
379 goto patherr1;
381 #endif /* !defined(linux) */
382 while ((dp = readdir(dirp)) != NULL) {
383 if (dp->d_ino == 0)
384 continue;
385 if (match(dp->d_name, pattern)) {
386 Gcat(gpath, dp->d_name);
387 globcnt++;
390 closedir(dirp);
391 return;
393 patherr1:
394 closedir(dirp);
395 patherr2:
396 globerr = "Bad directory components";
399 static VOIDRET expand FUNCTION((as), char *as)
401 register char *cs;
402 register char *sgpathp, *oldcs;
403 struct stat stb;
405 sgpathp = gpathp;
406 cs = as;
407 if (*cs == '~' && gpathp == gpath) {
408 addpath('~');
409 for (cs++; letter(*cs) || digit(*cs) || *cs == '-';)
410 addpath(*cs++);
411 if (!*cs || *cs == '/') {
412 if (gpathp != gpath + 1) {
413 *gpathp = 0;
414 if (gethdir(gpath + 1))
415 globerr = "Unknown user name after ~";
416 strcpy(gpath, gpath + 1);
417 } else
418 strcpy(gpath, home);
419 gpathp = strend(gpath);
422 while (!any(*cs, globchars)) {
423 if (*cs == 0) {
424 if (!globbed)
425 Gcat(gpath, "");
426 else
427 if (stat(gpath, &stb) >= 0) {
428 Gcat(gpath, "");
429 globcnt++;
431 goto endit;
433 addpath(*cs++);
435 oldcs = cs;
436 while (cs > as && *cs != '/')
437 cs--, gpathp--;
438 if (*cs == '/')
439 cs++, gpathp++;
440 *gpathp = 0;
441 if (*oldcs == '{') {
442 execbrc(cs, ((char *) 0));
443 return;
445 matchdir(cs);
446 endit:
447 gpathp = sgpathp;
448 *gpathp = 0;
451 static int execbrc FUNCTION((p, s), char *p AND char *s)
453 char restbuf[BUFSIZ + 2];
454 register char *pe, *pm, *pl;
455 int brclev = 0;
456 char *lm, savec, *sgpathp;
458 for (lm = restbuf; *p != '{'; *lm++ = *p++)
459 continue;
460 for (pe = ++p; *pe; pe++)
461 switch (*pe) {
463 case '{':
464 brclev++;
465 continue;
467 case '}':
468 if (brclev == 0)
469 goto pend;
470 brclev--;
471 continue;
473 case '[':
474 for (pe++; *pe && *pe != ']'; pe++)
475 continue;
476 continue;
478 pend:
479 brclev = 0;
480 for (pl = pm = p; pm <= pe; pm++)
481 switch (*pm & (QUOTE | TRIM)) {
483 case '{':
484 brclev++;
485 continue;
487 case '}':
488 if (brclev) {
489 brclev--;
490 continue;
492 goto doit;
494 case ',' | QUOTE:
495 case ',':
496 if (brclev)
497 continue;
498 doit:
499 savec = *pm;
500 *pm = 0;
501 strcpy(lm, pl);
502 strcat(restbuf, pe + 1);
503 *pm = savec;
504 if (s == 0) {
505 sgpathp = gpathp;
506 expand(restbuf);
507 gpathp = sgpathp;
508 *gpathp = 0;
509 } else
510 if (amatch(s, restbuf))
511 return (1);
512 sort();
513 pl = pm + 1;
514 if (brclev)
515 return (0);
516 continue;
518 case '[':
519 for (pm++; *pm && *pm != ']'; pm++)
520 continue;
521 if (!*pm)
522 pm--;
523 continue;
525 if (brclev)
526 goto doit;
527 return (0);
530 static VOIDRET acollect FUNCTION((as), register char *as)
532 register int ogargc = gargc;
534 gpathp = gpath;
535 *gpathp = 0;
536 globbed = 0;
537 expand(as);
538 if (gargc != ogargc)
539 sort();
542 static VOIDRET collect FUNCTION((as), register char *as)
544 if (eq(as, "{") || eq(as, "{}")) {
545 Gcat(as, "");
546 sort();
547 } else
548 acollect(as);
551 static int amatch FUNCTION((s, p), register char *s AND register char *p)
553 register int scc;
554 int ok, lc;
555 char *sgpathp;
556 struct stat stb;
557 int c, cc;
559 globbed = 1;
560 for (;;) {
561 scc = *s++ & TRIM;
562 switch (c = *p++) {
564 case '{':
565 return (execbrc(p - 1, s - 1));
567 case '[':
568 ok = 0;
569 lc = 077777;
570 while (cc = *p++) {
571 if (cc == ']') {
572 if (ok)
573 break;
574 return (0);
576 if (cc == '-') {
577 if (lc <= scc && scc <= *p++)
578 ok++;
579 } else
580 if (scc == (lc = cc))
581 ok++;
583 if (cc == 0)
584 if (ok)
585 p--;
586 else
587 return 0;
588 continue;
590 case '*':
591 if (!*p)
592 return (1);
593 if (*p == '/') {
594 p++;
595 goto slash;
597 s--;
598 do {
599 if (amatch(s, p))
600 return (1);
602 while (*s++);
603 return (0);
605 case 0:
606 return (scc == 0);
608 default:
609 if (c != scc)
610 return (0);
611 continue;
613 case '?':
614 if (scc == 0)
615 return (0);
616 continue;
618 case '/':
619 if (scc)
620 return (0);
621 slash:
622 s = entp;
623 sgpathp = gpathp;
624 while (*s)
625 addpath(*s++);
626 addpath('/');
627 if (stat(gpath, &stb) == 0 && isdir(stb))
628 if (*p == 0) {
629 Gcat(gpath, "");
630 globcnt++;
631 } else
632 expand(p);
633 gpathp = sgpathp;
634 *gpathp = 0;
635 return (0);
641 char **ftpglob FUNCTION((v), register char *v)
643 char agpath[BUFSIZ];
644 char *agargv[GAVSIZ];
645 char *vv[2];
647 vv[0] = v;
648 vv[1] = 0;
649 gflag = 0;
650 rscan(vv, tglob);
651 if (gflag == 0) {
652 vv[0] = strspl(v, "");
653 return (copyblk(vv));
655 globerr = 0;
656 gpath = agpath;
657 gpathp = gpath;
658 *gpathp = 0;
659 lastgpathp = &gpath[sizeof agpath - 2];
660 ginit(agargv);
661 globcnt = 0;
662 collect(v);
663 if (globcnt == 0 && (gflag & 1)) {
664 blkfree(gargv), gargv = 0;
665 return (0);
666 } else
667 return (gargv = copyblk(gargv));