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}
669