386BSD 0.1 development
[unix-history] / usr / src / usr.bin / ftp / glob.c
CommitLineData
f54c1094
WJ
1/*
2 * Copyright (c) 1980 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
35static char sccsid[] = "@(#)glob.c 5.9 (Berkeley) 2/25/91";
36#endif /* not lint */
37
38/*
39 * C-shell glob for random programs.
40 */
41
42#include <sys/param.h>
43#include <sys/stat.h>
44#include <dirent.h>
45
46#include <pwd.h>
47#include <errno.h>
48#include <stdio.h>
49#include <stdlib.h>
50#include <string.h>
51
52#define QUOTE 0200
53#define TRIM 0177
54#define eq(a,b) (strcmp(a, b)==0)
55#define GAVSIZ (NCARGS/6)
56#define isdir(d) ((d.st_mode & S_IFMT) == S_IFDIR)
57
58static char **gargv; /* Pointer to the (stack) arglist */
59static int gargc; /* Number args in gargv */
60static int gnleft;
61static short gflag;
62static int tglob();
63char **ftpglob();
64char *globerr;
65char *home;
66extern int errno;
67static char *strspl(), *strend();
68char **copyblk();
69
70static void acollect(), addpath(), collect(), expand(), Gcat();
71static void ginit(), matchdir(), rscan(), sort();
72static int amatch(), execbrc(), match();
73
74static int globcnt;
75
76char *globchars = "`{[*?";
77
78static char *gpath, *gpathp, *lastgpathp;
79static int globbed;
80static char *entp;
81static char **sortbas;
82
83char **
84ftpglob(v)
85 register char *v;
86{
87 char agpath[BUFSIZ];
88 char *agargv[GAVSIZ];
89 char *vv[2];
90 vv[0] = v;
91 vv[1] = 0;
92 gflag = 0;
93 rscan(vv, tglob);
94 if (gflag == 0)
95 return (copyblk(vv));
96
97 globerr = 0;
98 gpath = agpath; gpathp = gpath; *gpathp = 0;
99 lastgpathp = &gpath[sizeof agpath - 2];
100 ginit(agargv); globcnt = 0;
101 collect(v);
102 if (globcnt == 0 && (gflag&1)) {
103 blkfree(gargv), gargv = 0;
104 return (0);
105 } else
106 return (gargv = copyblk(gargv));
107}
108
109static void
110ginit(agargv)
111 char **agargv;
112{
113
114 agargv[0] = 0; gargv = agargv; sortbas = agargv; gargc = 0;
115 gnleft = NCARGS - 4;
116}
117
118static void
119collect(as)
120 register char *as;
121{
122 if (eq(as, "{") || eq(as, "{}")) {
123 Gcat(as, "");
124 sort();
125 } else
126 acollect(as);
127}
128
129static void
130acollect(as)
131 register char *as;
132{
133 register int ogargc = gargc;
134
135 gpathp = gpath; *gpathp = 0; globbed = 0;
136 expand(as);
137 if (gargc != ogargc)
138 sort();
139}
140
141static void
142sort()
143{
144 register char **p1, **p2, *c;
145 char **Gvp = &gargv[gargc];
146
147 p1 = sortbas;
148 while (p1 < Gvp-1) {
149 p2 = p1;
150 while (++p2 < Gvp)
151 if (strcmp(*p1, *p2) > 0)
152 c = *p1, *p1 = *p2, *p2 = c;
153 p1++;
154 }
155 sortbas = Gvp;
156}
157
158static void
159expand(as)
160 char *as;
161{
162 register char *cs;
163 register char *sgpathp, *oldcs;
164 struct stat stb;
165
166 sgpathp = gpathp;
167 cs = as;
168 if (*cs == '~' && gpathp == gpath) {
169 addpath('~');
170 for (cs++; letter(*cs) || digit(*cs) || *cs == '-';)
171 addpath(*cs++);
172 if (!*cs || *cs == '/') {
173 if (gpathp != gpath + 1) {
174 *gpathp = 0;
175 if (gethdir(gpath + 1))
176 globerr = "Unknown user name after ~";
177 (void) strcpy(gpath, gpath + 1);
178 } else
179 (void) strcpy(gpath, home);
180 gpathp = strend(gpath);
181 }
182 }
183 while (!any(*cs, globchars)) {
184 if (*cs == 0) {
185 if (!globbed)
186 Gcat(gpath, "");
187 else if (stat(gpath, &stb) >= 0) {
188 Gcat(gpath, "");
189 globcnt++;
190 }
191 goto endit;
192 }
193 addpath(*cs++);
194 }
195 oldcs = cs;
196 while (cs > as && *cs != '/')
197 cs--, gpathp--;
198 if (*cs == '/')
199 cs++, gpathp++;
200 *gpathp = 0;
201 if (*oldcs == '{') {
202 (void) execbrc(cs, ((char *)0));
203 return;
204 }
205 matchdir(cs);
206endit:
207 gpathp = sgpathp;
208 *gpathp = 0;
209}
210
211static void
212matchdir(pattern)
213 char *pattern;
214{
215 struct stat stb;
216 register struct dirent *dp;
217 DIR *dirp;
218
219 dirp = opendir(gpath);
220 if (dirp == NULL) {
221 if (globbed)
222 return;
223 goto patherr2;
224 }
225 if (fstat(dirp->dd_fd, &stb) < 0)
226 goto patherr1;
227 if (!isdir(stb)) {
228 errno = ENOTDIR;
229 goto patherr1;
230 }
231 while ((dp = readdir(dirp)) != NULL) {
232 if (dp->d_ino == 0)
233 continue;
234 if (match(dp->d_name, pattern)) {
235 Gcat(gpath, dp->d_name);
236 globcnt++;
237 }
238 }
239 closedir(dirp);
240 return;
241
242patherr1:
243 closedir(dirp);
244patherr2:
245 globerr = "Bad directory components";
246}
247
248static int
249execbrc(p, s)
250 char *p, *s;
251{
252 char restbuf[BUFSIZ + 2];
253 register char *pe, *pm, *pl;
254 int brclev = 0;
255 char *lm, savec, *sgpathp;
256
257 for (lm = restbuf; *p != '{'; *lm++ = *p++)
258 continue;
259 for (pe = ++p; *pe; pe++)
260 switch (*pe) {
261
262 case '{':
263 brclev++;
264 continue;
265
266 case '}':
267 if (brclev == 0)
268 goto pend;
269 brclev--;
270 continue;
271
272 case '[':
273 for (pe++; *pe && *pe != ']'; pe++)
274 continue;
275 continue;
276 }
277pend:
278 brclev = 0;
279 for (pl = pm = p; pm <= pe; pm++)
280 switch (*pm & (QUOTE|TRIM)) {
281
282 case '{':
283 brclev++;
284 continue;
285
286 case '}':
287 if (brclev) {
288 brclev--;
289 continue;
290 }
291 goto doit;
292
293 case ','|QUOTE:
294 case ',':
295 if (brclev)
296 continue;
297doit:
298 savec = *pm;
299 *pm = 0;
300 (void) strcpy(lm, pl);
301 (void) strcat(restbuf, pe + 1);
302 *pm = savec;
303 if (s == 0) {
304 sgpathp = gpathp;
305 expand(restbuf);
306 gpathp = sgpathp;
307 *gpathp = 0;
308 } else if (amatch(s, restbuf))
309 return (1);
310 sort();
311 pl = pm + 1;
312 if (brclev)
313 return (0);
314 continue;
315
316 case '[':
317 for (pm++; *pm && *pm != ']'; pm++)
318 continue;
319 if (!*pm)
320 pm--;
321 continue;
322 }
323 if (brclev)
324 goto doit;
325 return (0);
326}
327
328static int
329match(s, p)
330 char *s, *p;
331{
332 register int c;
333 register char *sentp;
334 char sglobbed = globbed;
335
336 if (*s == '.' && *p != '.')
337 return (0);
338 sentp = entp;
339 entp = s;
340 c = amatch(s, p);
341 entp = sentp;
342 globbed = sglobbed;
343 return (c);
344}
345
346static int
347amatch(s, p)
348 register char *s, *p;
349{
350 register int scc;
351 int ok, lc;
352 char *sgpathp;
353 struct stat stb;
354 int c, cc;
355
356 globbed = 1;
357 for (;;) {
358 scc = *s++ & TRIM;
359 switch (c = *p++) {
360
361 case '{':
362 return (execbrc(p - 1, s - 1));
363
364 case '[':
365 ok = 0;
366 lc = 077777;
367 while (cc = *p++) {
368 if (cc == ']') {
369 if (ok)
370 break;
371 return (0);
372 }
373 if (cc == '-') {
374 if (lc <= scc && scc <= *p++)
375 ok++;
376 } else
377 if (scc == (lc = cc))
378 ok++;
379 }
380 if (cc == 0)
381 if (ok)
382 p--;
383 else
384 return 0;
385 continue;
386
387 case '*':
388 if (!*p)
389 return (1);
390 if (*p == '/') {
391 p++;
392 goto slash;
393 }
394 s--;
395 do {
396 if (amatch(s, p))
397 return (1);
398 } while (*s++);
399 return (0);
400
401 case 0:
402 return (scc == 0);
403
404 default:
405 if (c != scc)
406 return (0);
407 continue;
408
409 case '?':
410 if (scc == 0)
411 return (0);
412 continue;
413
414 case '/':
415 if (scc)
416 return (0);
417slash:
418 s = entp;
419 sgpathp = gpathp;
420 while (*s)
421 addpath(*s++);
422 addpath('/');
423 if (stat(gpath, &stb) == 0 && isdir(stb))
424 if (*p == 0) {
425 Gcat(gpath, "");
426 globcnt++;
427 } else
428 expand(p);
429 gpathp = sgpathp;
430 *gpathp = 0;
431 return (0);
432 }
433 }
434}
435
436static
437Gmatch(s, p)
438 register char *s, *p;
439{
440 register int scc;
441 int ok, lc;
442 int c, cc;
443
444 for (;;) {
445 scc = *s++ & TRIM;
446 switch (c = *p++) {
447
448 case '[':
449 ok = 0;
450 lc = 077777;
451 while (cc = *p++) {
452 if (cc == ']') {
453 if (ok)
454 break;
455 return (0);
456 }
457 if (cc == '-') {
458 if (lc <= scc && scc <= *p++)
459 ok++;
460 } else
461 if (scc == (lc = cc))
462 ok++;
463 }
464 if (cc == 0)
465 if (ok)
466 p--;
467 else
468 return 0;
469 continue;
470
471 case '*':
472 if (!*p)
473 return (1);
474 for (s--; *s; s++)
475 if (Gmatch(s, p))
476 return (1);
477 return (0);
478
479 case 0:
480 return (scc == 0);
481
482 default:
483 if ((c & TRIM) != scc)
484 return (0);
485 continue;
486
487 case '?':
488 if (scc == 0)
489 return (0);
490 continue;
491
492 }
493 }
494}
495
496static void
497Gcat(s1, s2)
498 register char *s1, *s2;
499{
500 register int len = strlen(s1) + strlen(s2) + 1;
501
502 if (len >= gnleft || gargc >= GAVSIZ - 1)
503 globerr = "Arguments too long";
504 else {
505 gargc++;
506 gnleft -= len;
507 gargv[gargc] = 0;
508 gargv[gargc - 1] = strspl(s1, s2);
509 }
510}
511
512static void
513addpath(c)
514 char c;
515{
516
517 if (gpathp >= lastgpathp)
518 globerr = "Pathname too long";
519 else {
520 *gpathp++ = c;
521 *gpathp = 0;
522 }
523}
524
525static void
526rscan(t, f)
527 register char **t;
528 int (*f)();
529{
530 register char *p, c;
531
532 while (p = *t++) {
533 if (f == tglob)
534 if (*p == '~')
535 gflag |= 2;
536 else if (eq(p, "{") || eq(p, "{}"))
537 continue;
538 while (c = *p++)
539 (*f)(c);
540 }
541}
542/*
543static
544scan(t, f)
545 register char **t;
546 int (*f)();
547{
548 register char *p, c;
549
550 while (p = *t++)
551 while (c = *p)
552 *p++ = (*f)(c);
553} */
554
555static
556tglob(c)
557 register char c;
558{
559
560 if (any(c, globchars))
561 gflag |= c == '{' ? 2 : 1;
562 return (c);
563}
564/*
565static
566trim(c)
567 char c;
568{
569
570 return (c & TRIM);
571} */
572
573
574letter(c)
575 register char c;
576{
577
578 return (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c == '_');
579}
580
581digit(c)
582 register char c;
583{
584
585 return (c >= '0' && c <= '9');
586}
587
588any(c, s)
589 register int c;
590 register char *s;
591{
592
593 while (*s)
594 if (*s++ == c)
595 return(1);
596 return(0);
597}
598blklen(av)
599 register char **av;
600{
601 register int i = 0;
602
603 while (*av++)
604 i++;
605 return (i);
606}
607
608char **
609blkcpy(oav, bv)
610 char **oav;
611 register char **bv;
612{
613 register char **av = oav;
614
615 while (*av++ = *bv++)
616 continue;
617 return (oav);
618}
619
620blkfree(av0)
621 char **av0;
622{
623 register char **av = av0;
624
625 while (*av)
626 free(*av++);
627}
628
629static
630char *
631strspl(cp, dp)
632 register char *cp, *dp;
633{
634 register char *ep = malloc((unsigned)(strlen(cp) + strlen(dp) + 1));
635
636 if (ep == (char *)0)
637 fatal("Out of memory");
638 (void) strcpy(ep, cp);
639 (void) strcat(ep, dp);
640 return (ep);
641}
642
643char **
644copyblk(v)
645 register char **v;
646{
647 register char **nv = (char **)malloc((unsigned)((blklen(v) + 1) *
648 sizeof(char **)));
649 if (nv == (char **)0)
650 fatal("Out of memory");
651
652 return (blkcpy(nv, v));
653}
654
655static
656char *
657strend(cp)
658 register char *cp;
659{
660
661 while (*cp)
662 cp++;
663 return (cp);
664}
665/*
666 * Extract a home directory from the password file
667 * The argument points to a buffer where the name of the
668 * user whose home directory is sought is currently.
669 * We write the home directory of the user back there.
670 */
671gethdir(home)
672 char *home;
673{
674 register struct passwd *pp = getpwnam(home);
675
676 if (!pp || home + strlen(pp->pw_dir) >= lastgpathp)
677 return (1);
678 (void) strcpy(home, pp->pw_dir);
679 return (0);
680}