Merge branch 'vendor/MDOCML'
[dragonfly.git] / contrib / opie / glob.c
CommitLineData
984263bc
MD
1/* glob.c: The csh et al glob pattern matching routines.
2
3%%% copyright-cmetz-96
4This software is Copyright 1996-2001 by Craig Metz, All Rights Reserved.
5The Inner Net License Version 3 applies to this software.
6You should have received a copy of the license with this software. If
7you didn't get a copy, you may request one from <license@inner.net>.
8
9Portions of this software are Copyright 1995 by Randall Atkinson and Dan
10McDonald, All Rights Reserved. All Rights under this copyright are assigned
11to the U.S. Naval Research Laboratory (NRL). The NRL Copyright Notice and
12License Agreement applies to this software.
13
14 History:
15
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.
22*/
23/*
24 * Copyright (c) 1980 Regents of the University of California.
25 * All rights reserved.
26 *
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.
42 *
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.
54 */
55
56/*
57 * C-shell glob for random programs.
58 */
59
60#include "opie_cfg.h"
61
62#if HAVE_SYS_PARAM_H
63#include <sys/param.h>
64#endif /* HAVE_SYS_PARAM_H */
65#include <sys/stat.h>
66
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 */
76
77#include "opie.h"
78
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)
87
88static char **gargv; /* Pointer to the (stack) arglist */
89static int gargc; /* Number args in gargv */
90static int gnleft;
91static short gflag;
92
93static int letter __P((register char));
94static int digit __P((register char));
95static int any __P((int, char *));
96static int blklen __P((register char **));
97VOIDRET blkfree __P((char **));
98static char *strspl __P((register char *, register char *));
99
100static int tglob __P((register char c));
101
102extern int errno;
103static char *strend __P((char *));
104
105static int globcnt;
106
107static char *globchars = "`{[*?";
108char *globerr = NULL;
109char *home = NULL;
110
111static char *gpath, *gpathp, *lastgpathp;
112static int globbed;
113static char *entp;
114static char **sortbas;
115
116static int amatch __P((char *p, char *s));
117static int execbrc __P((register char *p, register char *s));
118VOIDRET opiefatal __P((char *));
119char **copyblk __P((char **));
120
121static int match FUNCTION((s, p), char *s AND char *p)
122{
123 register int c;
124 register char *sentp;
125 char sglobbed = globbed;
126
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);
135}
136
137
138static int Gmatch FUNCTION((s, p), register char *s AND register char *p)
139{
140 register int scc;
141 int ok, lc;
142 int c, cc;
143
144 for (;;) {
145 scc = *s++ & TRIM;
146 switch (c = *p++) {
147
148 case '[':
149 ok = 0;
150 lc = 077777;
151 while (cc = *p++) {
152 if (cc == ']') {
153 if (ok)
154 break;
155 return (0);
156 }
157 if (cc == '-') {
158 if (lc <= scc && scc <= *p++)
159 ok++;
160 } else
161 if (scc == (lc = cc))
162 ok++;
163 }
164 if (cc == 0)
165 if (ok)
166 p--;
167 else
168 return 0;
169 continue;
170
171 case '*':
172 if (!*p)
173 return (1);
174 for (s--; *s; s++)
175 if (Gmatch(s, p))
176 return (1);
177 return (0);
178
179 case 0:
180 return (scc == 0);
181
182 default:
183 if ((c & TRIM) != scc)
184 return (0);
185 continue;
186
187 case '?':
188 if (scc == 0)
189 return (0);
190 continue;
191
192 }
193 }
194}
195
196static VOIDRET Gcat FUNCTION((s1, s2), register char *s1 AND register char *s2)
197{
198 register int len = strlen(s1) + strlen(s2) + 1;
199
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);
207 }
208}
209
210static VOIDRET addpath FUNCTION((c), char c)
211{
212
213 if (gpathp >= lastgpathp)
214 globerr = "Pathname too long";
215 else {
216 *gpathp++ = c;
217 *gpathp = 0;
218 }
219}
220
221static VOIDRET rscan FUNCTION((t, f), register char **t AND int (*f)__P((char)))
222{
223 register char *p, c;
224
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);
234 }
235}
236
237static int tglob FUNCTION((c), register char c)
238{
239 if (any(c, globchars))
240 gflag |= c == '{' ? 2 : 1;
241 return (c);
242}
243
244static int letter FUNCTION((c), register char c)
245{
246 return (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c == '_');
247}
248
249static int digit FUNCTION((c), register char c)
250{
251 return (c >= '0' && c <= '9');
252}
253
254static int any FUNCTION((c, s), int c AND char *s)
255{
256 while (*s)
257 if (*s++ == c)
258 return (1);
259 return (0);
260}
261
262static int blklen FUNCTION((av), register char **av)
263{
264 register int i = 0;
265
266 while (*av++)
267 i++;
268 return (i);
269}
270
271static char **blkcpy FUNCTION((oav, bv), char **oav AND register char **bv)
272{
273 register char **av = oav;
274
275 while (*av++ = *bv++)
276 continue;
277 return (oav);
278}
279
280VOIDRET blkfree FUNCTION((av0), char **av0)
281{
282 register char **av = av0;
283
284 while (*av)
285 free(*av++);
286}
287
288static char *strspl FUNCTION((cp, dp), register char *cp AND register char *dp)
289{
290 register char *ep = (char *) malloc((unsigned) (strlen(cp) +
291 strlen(dp) + 1));
292
293 if (ep == (char *) 0)
294 opiefatal("Out of memory");
295 strcpy(ep, cp);
296 strcat(ep, dp);
297 return (ep);
298}
299
300char **copyblk FUNCTION((v), char **v)
301{
302 register char **nv = (char **) malloc((unsigned) ((blklen(v) + 1) *
303 sizeof(char **)));
304
305 if (nv == (char **) 0)
306 opiefatal("Out of memory");
307
308 return (blkcpy(nv, v));
309}
310
311static char *strend FUNCTION((cp), register char *cp)
312{
313
314 while (*cp)
315 cp++;
316 return (cp);
317}
318
319/*
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.
324 */
325static int gethdir FUNCTION((home), char *home)
326{
327 register struct passwd *pp = getpwnam(home);
328
329 if (!pp || home + strlen(pp->pw_dir) >= lastgpathp)
330 return (1);
331 strcpy(home, pp->pw_dir);
332 return (0);
333}
334
335static VOIDRET ginit FUNCTION((agargv), char **agargv)
336{
337 agargv[0] = 0;
338 gargv = agargv;
339 sortbas = agargv;
340 gargc = 0;
341 gnleft = NCARGS - 4;
342}
343
344static VOIDRET sort FUNCTION_NOARGS
345{
346 register char **p1, **p2, *c;
347 char **Gvp = &gargv[gargc];
348
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++;
356 }
357 sortbas = Gvp;
358}
359
360static VOIDRET matchdir FUNCTION((pattern), char *pattern)
361{
362 struct stat stb;
363
364 register struct dirent *dp;
365
366 DIR *dirp;
367
368 dirp = opendir(*gpath == '\0' ? "." : gpath);
369 if (dirp == NULL) {
370 if (globbed)
371 return;
372 goto patherr2;
373 }
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;
380 }
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++;
388 }
389 }
390 closedir(dirp);
391 return;
392
393patherr1:
394 closedir(dirp);
395patherr2:
396 globerr = "Bad directory components";
397}
398
399static VOIDRET expand FUNCTION((as), char *as)
400{
401 register char *cs;
402 register char *sgpathp, *oldcs;
403 struct stat stb;
404
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);
420 }
421 }
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++;
430 }
431 goto endit;
432 }
433 addpath(*cs++);
434 }
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;
444 }
445 matchdir(cs);
446endit:
447 gpathp = sgpathp;
448 *gpathp = 0;
449}
450
451static int execbrc FUNCTION((p, s), char *p AND char *s)
452{
453 char restbuf[BUFSIZ + 2];
454 register char *pe, *pm, *pl;
455 int brclev = 0;
456 char *lm, savec, *sgpathp;
457
458 for (lm = restbuf; *p != '{'; *lm++ = *p++)
459 continue;
460 for (pe = ++p; *pe; pe++)
461 switch (*pe) {
462
463 case '{':
464 brclev++;
465 continue;
466
467 case '}':
468 if (brclev == 0)
469 goto pend;
470 brclev--;
471 continue;
472
473 case '[':
474 for (pe++; *pe && *pe != ']'; pe++)
475 continue;
476 continue;
477 }
478pend:
479 brclev = 0;
480 for (pl = pm = p; pm <= pe; pm++)
481 switch (*pm & (QUOTE | TRIM)) {
482
483 case '{':
484 brclev++;
485 continue;
486
487 case '}':
488 if (brclev) {
489 brclev--;
490 continue;
491 }
492 goto doit;
493
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;
517
518 case '[':
519 for (pm++; *pm && *pm != ']'; pm++)
520 continue;
521 if (!*pm)
522 pm--;
523 continue;
524 }
525 if (brclev)
526 goto doit;
527 return (0);
528}
529
530static VOIDRET acollect FUNCTION((as), register char *as)
531{
532 register int ogargc = gargc;
533
534 gpathp = gpath;
535 *gpathp = 0;
536 globbed = 0;
537 expand(as);
538 if (gargc != ogargc)
539 sort();
540}
541
542static VOIDRET collect FUNCTION((as), register char *as)
543{
544 if (eq(as, "{") || eq(as, "{}")) {
545 Gcat(as, "");
546 sort();
547 } else
548 acollect(as);
549}
550
551static int amatch FUNCTION((s, p), register char *s AND register char *p)
552{
553 register int scc;
554 int ok, lc;
555 char *sgpathp;
556 struct stat stb;
557 int c, cc;
558
559 globbed = 1;
560 for (;;) {
561 scc = *s++ & TRIM;
562 switch (c = *p++) {
563
564 case '{':
565 return (execbrc(p - 1, s - 1));
566
567 case '[':
568 ok = 0;
569 lc = 077777;
570 while (cc = *p++) {
571 if (cc == ']') {
572 if (ok)
573 break;
574 return (0);
575 }
576 if (cc == '-') {
577 if (lc <= scc && scc <= *p++)
578 ok++;
579 } else
580 if (scc == (lc = cc))
581 ok++;
582 }
583 if (cc == 0)
584 if (ok)
585 p--;
586 else
587 return 0;
588 continue;
589
590 case '*':
591 if (!*p)
592 return (1);
593 if (*p == '/') {
594 p++;
595 goto slash;
596 }
597 s--;
598 do {
599 if (amatch(s, p))
600 return (1);
601 }
602 while (*s++);
603 return (0);
604
605 case 0:
606 return (scc == 0);
607
608 default:
609 if (c != scc)
610 return (0);
611 continue;
612
613 case '?':
614 if (scc == 0)
615 return (0);
616 continue;
617
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);
636 }
637 }
638}
639
640
641char **ftpglob FUNCTION((v), register char *v)
642{
643 char agpath[BUFSIZ];
644 char *agargv[GAVSIZ];
645 char *vv[2];
646
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));
654 }
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));
668}