Merge from vendor branch OPENSSH:
[dragonfly.git] / contrib / opie / glob.c
1 /* glob.c: The csh et al glob pattern matching routines.
2
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>.
8
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.
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
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;
92
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 *));
99
100 static int tglob __P((register char c));
101
102 extern int errno;
103 static char *strend __P((char *));
104
105 static int globcnt;
106
107 static char *globchars = "`{[*?";
108 char *globerr = NULL;
109 char *home = NULL;
110
111 static char *gpath, *gpathp, *lastgpathp;
112 static int globbed;
113 static char *entp;
114 static char **sortbas;
115
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 **));
120
121 static 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
138 static 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
196 static 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
210 static 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
221 static 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
237 static int tglob FUNCTION((c), register char c)
238 {
239   if (any(c, globchars))
240     gflag |= c == '{' ? 2 : 1;
241   return (c);
242 }
243
244 static int letter FUNCTION((c), register char c)
245 {
246   return (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c == '_');
247 }
248
249 static int digit FUNCTION((c), register char c)
250 {
251   return (c >= '0' && c <= '9');
252 }
253
254 static 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
262 static int blklen FUNCTION((av), register char **av)
263 {
264   register int i = 0;
265
266   while (*av++)
267     i++;
268   return (i);
269 }
270
271 static 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
280 VOIDRET blkfree FUNCTION((av0), char **av0)
281 {
282   register char **av = av0;
283
284   while (*av)
285     free(*av++);
286 }
287
288 static 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
300 char **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
311 static 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  */
325 static 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
335 static 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
344 static 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
360 static 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
393 patherr1:
394   closedir(dirp);
395 patherr2:
396   globerr = "Bad directory components";
397 }
398
399 static 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);
446 endit:
447   gpathp = sgpathp;
448   *gpathp = 0;
449 }
450
451 static 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     }
478 pend:
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
530 static 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
542 static 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
551 static 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
641 char **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 }