1 /*
2  * Copyright (c) 1990 Rene' Seindal
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are permitted
6  * provided that the above copyright notice and this paragraph are
7  * duplicated in all such forms.
8  *
9  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
10  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
11  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
12  */
13 
14 /*
15  * Generalised to allow multiple types of directory structures
16  * by Philip G. Richards 1992; all these modifications are copyright
17  * Philip G. Richards, 1992.  All rights reserved.
18  *
19  * Both redistribution, use, and warranty are as above.
20  */
21 
22 #include "client.h"
23 #include <pwd.h>
24 #include <stdlib.h>
25 
26 #ifdef TEST
27 #define ffprintf (void)fprintf
28 static FILE *STDDBG = STDERR;
29 #endif
30 
31 static char *real_getname(VOIDDIRENT *dp);
32 static VOIDDIR *real_opendir(char *dirname);
33 static void real_closedir(VOIDDIR *dirp);
34 static VOIDDIRENT *real_readdir(VOIDDIR *dirp);
35 static int real_stat(const char *buf, struct stat *s);
36 
37 static VOIDDIR *(*OPENDIR)(char *)                  = real_opendir;
38 static void (*CLOSEDIR)(VOIDDIR *)                  = real_closedir;
39 static VOIDDIRENT *(*READDIR)(VOIDDIR *)            = real_readdir;
40 static char *(*GETNAME)(VOIDDIRENT *)               = real_getname;
41 static int (*GLOBSTAT)(const char *, struct stat *) = real_stat;
42 
43 static void matchdir(char *path_end, char **gpat); /* non-simple globbing */
44 static void do_glob(char *path_end, char **gpat);
45 				/* do simple globbing, on segment at a time */
46 static void add_name(void); /* add a name to namelist */
47 static int split_pat(char *pattern, char **table); /* split pattern into segments */
48 
49 /* glob_match matches pattern against string according to the normal
50  * rules for shell globbing.  It returns SUCCES if string matches
51  * pattern, FAIL if it doesn't, and ERROR if pattern is illformed.
52  *
53  * To be more specific, a pattern can be:
54  *	?	which matches any single character
55  *	*	which matches zero or more character
56  *	[...]	which matches any single character in ...
57  *	[^...]	which matches any single character not in ...
58  *	\x	where x is any character, matches that character literally
59  *	x	where x is any character except ? * [ and \, matches x
60  *
61  * Character within [...] can include single characters and ranges of
62  * the form x-y, which matches any characters in the ranges x - y
63  * inclusive.  It is considered an error is y < x.  An ] can be included
64  * as the very first character in ..., and a - as the first (after a
65  * possibly [) or last character in ...
66  */
67 
68 #define PSUCCES	2
69 #define SUCCES	1
70 #define FAIL	0
71 #define ERROR	0
72 
73 #define NEXTP {if ( (p = *++pattern) == '\0') return ERROR;}
74 
75 int
glob_match(const char * pattern,char * string)76 glob_match(const char *pattern, char *string)
77 {
78     char p;
79     char seenstar = '\0';
80 
81     for ( ; (p = *pattern); pattern++ ) {
82 
83 	if ( p == '\\' ) {
84 	    NEXTP;		/* matches next char literally (but not \0) */
85 	    if ( p != *string++ )
86 		return FAIL;	/* string too short */
87 	    continue;
88 	} else if ( p == '?' ) {
89 	    if ( *string++ )	/* matches any character */
90 		continue;
91 	    else
92 		return FAIL;	/* string too short */
93 	} else if ( p == '*' ) {
94 	    seenstar = '\1';
95 	    continue;
96 	} else {
97 
98 	    if ( seenstar ) {
99 		int tmp;
100 		while ( *string ) {
101 		    tmp = glob_match( pattern, string );
102 		    if ( tmp != FAIL )
103 			return tmp;
104 		    string++;
105 		}
106 		return FAIL;
107 		/* NOTREACHED */
108 	    }
109 
110 	    if ( p == '\0' )
111 		return FAIL;
112 
113 	    if ( p == '[' ) {
114 		char s = *string++;
115 		char reverse = '\0';
116 		char first, last;
117 		char gotcha = '\0';
118 
119 		NEXTP;
120 		if ( p == '^' ) {
121 		    reverse = '\1';
122 		    NEXTP;
123 		}
124 		if ( p == ']' ) { /* special case */
125 		    gotcha = (s==p);
126 		    NEXTP;
127 		}
128 
129 		while (  p != ']' && !gotcha ) {
130 		    first = p;
131 		    NEXTP;
132 		    if ( p == '-' && pattern[1] != ']' ) {
133 			NEXTP;
134 			last = p;
135 			NEXTP;
136 		    } else
137 			last = first;
138 		    if ( first > last )
139 			return ERROR;
140 		    gotcha = (first <= s && s <= last );
141 		}
142 		while ( p != ']' )
143 		    NEXTP;
144 
145 		if ( reverse ? gotcha : !gotcha )
146 		    return FAIL;
147 	    } else if ( p != *string )
148 		return FAIL;
149 	    else
150 		string++;
151 	}
152     }
153     if ( seenstar )
154 	return SUCCES;
155 
156     if ( *string )
157 	return FAIL;
158     return SUCCES;
159 }
160 
161 static char *main_path;		/* ptr to scratchpad */
162 static int offset;		/* no of leading char in main_path to ignore */
163 static char **namelist;		/* name list buildup */
164 static int nnames;		/* no of names found */
165 static int left;		/* no of slots allocated but not used yet */
166 
167 #define GLOBMAXSEG	50	/* max segments in pattern */
168 #define GLOBCHUNK	20	/* no of slots to allocate at a time */
169 
170 #ifndef MAXNAMLEN
171 #define MAXNAMLEN 256
172 #endif
173 
174 int
glob_path(const char * pattern,char *** names)175 glob_path(const char *pattern, char ***names)
176 {
177     char mpath[ MAXPATHLEN + MAXNAMLEN + 1 ];
178     char *gpat[GLOBMAXSEG];
179     char *pat;
180 
181     if (pattern == 0)
182 	return -1;
183 
184     if ((pat = strdup(pattern)) == NULL)
185 	return -1;
186 
187     if (split_pat(pat, gpat) < 0)
188     {
189 	(void)free(pat);
190 	return -1;
191     }
192 
193     main_path = mpath;		/* initalisation of static storage */
194     namelist = 0;
195     nnames = left = 0;
196 
197     if ( *gpat && **gpat == '/' )
198     {
199 	main_path[0] = '/';
200 	main_path[1] = '\0';
201 	offset = 0;
202 	do_glob(main_path, gpat + 1);
203     }
204     else
205     {
206 	main_path[0] = '.';
207 	main_path[1] = '\0';
208 	offset = 2;
209 	do_glob(main_path + 1, gpat);
210     }
211 
212     (void)free(pat);
213 
214     if (namelist == 0)
215 	*names = (char **)malloc(sizeof(char *));
216     else
217 	*names = (char **)realloc(namelist, (nnames+1) * sizeof(char *));
218 
219     if (*names == 0)
220 	return -1;
221     (*names)[nnames] = 0;
222     return nnames;
223 }
224 
225 static int
split_pat(char * pattern,char ** table)226 split_pat(char *pattern, char **table)
227 {
228     char *pp = pattern;
229     int size = GLOBMAXSEG;
230 
231     if (*pattern == '/')
232     {
233 	*table++ = "/";
234 	--size;
235     }
236 
237     do
238     {
239 	while (*pp == '/')
240 	    *pp++ = '\0';
241 	if (*pp == '\0')
242 	    break;
243 	if (--size < 0)
244 	    return -1;
245 	*table++ = pp;
246 	while (*pp && *pp != '/')
247 	    pp++;
248     }
249     while (*pp);
250 
251     *table = 0;
252     return 0;
253 }
254 
255 
256 #define ISGLOB(x) ((x)=='*' || (x)=='?' || (x)=='[')
257 
258 static int
no_glob(char * pat)259 no_glob(char *pat)
260 {
261     while (*pat && !ISGLOB(*pat))
262 	pat++;
263 
264     return (*pat == '\0');
265 }
266 
267 static void
do_glob(char * path_end,char ** gpat)268 do_glob(char *path_end, char **gpat)
269                    		/* ptr to the end of main_path */
270                 		/* the rest of the pattern segments */
271 {
272     char *saved_end = path_end;	/* saved to be resored */
273     char *pat;			/* current pattern segment */
274     struct stat st;		/* to check if file exists */
275 
276 #ifdef GLOBDEBUG
277     ffprintf(STDDBG,"do_glob: path = '%s', pat = '%s'\n", main_path, *gpat );
278 #endif
279 
280     for ( ; (pat = *gpat) != 0 && no_glob(pat); gpat++ )
281     {
282 #ifdef GLOBDEBUG
283 	ffprintf(STDDBG,"no_glob: path = '%s', pat = '%s'\n", main_path, pat );
284 #endif
285 	*path_end = '/';
286 	(void)strcpy(path_end+1, pat);
287 	path_end += strlen(pat) + 1;
288 
289 	if (GLOBSTAT(main_path, &st) != 0 )
290 	{
291 	    *saved_end = '\0';
292 	    return;
293 	}
294     }
295     if (pat)
296 	matchdir(path_end, gpat);
297     else
298 	add_name();
299 
300     *saved_end = '\0';
301     return;
302 }
303 
304 static void
matchdir(char * path_end,char ** gpat)305 matchdir(char *path_end, char **gpat)
306                    		/* ptr to end of main_path */
307                 		/* the rest of the pattern segments */
308 {
309     char *x;			/* scratch */
310     VOIDDIR *dirp;		/* for directory reading */
311     VOIDDIRENT *dp;		/* directory entry */
312     struct stat st;		/* to determine files type */
313 
314 #ifdef GLOBDEBUG
315     ffprintf(STDDBG,"matchdir: path = '%s', pat = '%s'\n", main_path, *gpat );
316 #endif
317     if ((dirp = OPENDIR(main_path)) == NULL)
318 	return;
319 
320     *path_end = '/';
321 
322     while ((dp = READDIR(dirp)) != NULL)
323     {
324 	char *dirname;
325 	x = dirname = GETNAME(dp);	/* was dp->d_name */
326 	if (*x == '.' && (*++x == '\0' || (*x == '.' && *++x == '\0')))
327 	    continue;
328 	if (*dirname == '.' && **gpat != '.')
329 	    continue;
330 
331 	(void)strcpy(path_end + 1, dirname);
332 
333 	if (glob_match(*gpat, dirname))
334 	{   /* this is a match */
335 	    if ( *(gpat+1) == 0 )
336 	    {	/* and it is the last */
337 		add_name();	/* so eat it */
338 		continue;
339 	    }
340 	    if (GLOBSTAT(main_path, &st) == 0 /* else not the last */
341 		&& (st.st_mode & S_IFMT) == S_IFDIR)
342 		do_glob(path_end + strlen(dirname) + 1, gpat + 1);
343 	}
344     }
345 
346     (void)CLOSEDIR(dirp);
347 
348     *path_end = '\0';
349 }
350 
351 static void
add_name(void)352 add_name(void)
353 {
354     char *np;
355 #ifdef GLOBDEBUG
356     ffprintf(STDDBG,"Globbed: %s\n", main_path+offset);
357 #endif
358 
359     if (--left <= 0)
360     {
361 	if (namelist == 0)
362 	    namelist = (char**)malloc(GLOBCHUNK * sizeof(char*));
363 	else
364 	    namelist = (char**)realloc(namelist,
365 			       (nnames+GLOBCHUNK)*sizeof(char*));
366 
367 	if (namelist == NULL)
368 	    return;
369 
370 	left = GLOBCHUNK;
371     }
372 
373     if ((np = strdup(main_path + offset)) == 0)
374 	return;
375 
376     namelist[nnames++] = np;
377 }
378 
379 /* the following is not general purpose code. */
380 const char *globerr;
381 char globerr_buf[1024];
382 char *home = 0;
383 
384 /* extern int qsort(); */
385 
386 static int
name_cmp(char ** s1,char ** s2)387 name_cmp(char **s1, char **s2)
388 {
389     return strcmp(*s1, *s2);
390 }
391 
392 
393 char *
expand_tilde(char * pat)394 expand_tilde(char *pat)
395 {
396     static char buf[BUFSIZ];
397 
398     struct passwd *pw;
399     char *tmp;
400     char *bp;
401 
402     if (*pat != '~')
403 	return 0;
404 
405     pat++;
406     if (*pat && *pat != '/')
407     {
408 	bp = buf;
409 	for (tmp = pat; *tmp && *tmp != '/'; )
410 	    *bp++ = *tmp++;
411 	*bp = '\0';
412 
413 	pw = getpwnam(buf);
414 	if (pw == 0)
415 	{
416 	    (void)sprintf(globerr_buf, "%s: Unknown user.", pat);
417 	    globerr = globerr_buf;
418 	    return 0;
419 	}
420 	pat = tmp ? tmp : "";
421 	tmp = pw->pw_dir;
422     }
423     else
424     {
425 	if (*pat)
426 	    pat++;
427 	tmp = (char *)getenv("HOME");
428      }
429 
430     for (bp = buf; *tmp; )
431 	*bp++ = *tmp++;
432     *bp++ = '/';
433 
434     while (*pat)
435 	*bp++ = *pat++;
436     *bp = '\0';
437 
438     return buf;
439 }
440 
441 const char **
glob(const char * pat)442 glob(const char *pat)
443 {
444     char **names;
445     int nnames;
446 
447     if (*pat == '~')
448     {
449 	pat = expand_tilde(pat);
450 	if (pat == 0)
451 	    return 0;
452     }
453 
454     nnames = glob_path(pat, &names);
455 
456     switch (nnames)
457     {
458       case -1:
459 	globerr = strerror(errno);
460 	return 0;
461       case 0:
462 	globerr = "No match.";
463 	return 0;
464       default:
465 	qsort(names, nnames, sizeof(char *), name_cmp);
466 	return names;
467     }
468 }
469 
470 void
free_glob(char ** argv)471 free_glob(char **argv)
472 {
473     char **a;
474 
475     if (argv == (char**)0)
476 	return;
477 
478     for (a = argv; *a; a++)
479 	(void)free(*a);
480 
481     (void)free((char*)argv);
482 }
483 
484 void
set_glob_routines(VOIDDIR * (* dopen)(char *),void (* dclose)(VOIDDIR *),VOIDDIRENT * (* dread)(VOIDDIR *),char * (* dgetname)(VOIDDIRENT *),int (* dstat)(const char *,struct stat *))485 set_glob_routines(VOIDDIR *(*dopen)(char *),
486 		  void (*dclose)(VOIDDIR *),
487 		  VOIDDIRENT *(*dread)(VOIDDIR *),
488 		  char *(*dgetname)(VOIDDIRENT *),
489 		  int (*dstat)(const char *, struct stat *))
490 {
491     OPENDIR	= dopen;
492     CLOSEDIR	= dclose;
493     GETNAME	= dgetname;
494     READDIR	= dread;
495     GLOBSTAT	= dstat;
496 }
497 
498 static char
real_getname(VOIDDIRENT * dp)499 *real_getname(VOIDDIRENT *dp)
500 {
501     return ((struct dirent *)dp)->d_name;
502 }
503 
504 /*****************************************************************************
505 * wrappers for the real functions
506 *****************************************************************************/
507 static VOIDDIR *
real_opendir(char * dirname)508 real_opendir(char *dirname)
509 {
510     return (VOIDDIR*)opendir(dirname);
511 }
512 
513 static void
real_closedir(VOIDDIR * dirp)514 real_closedir(VOIDDIR *dirp)
515 {
516     (void)closedir((DIR*)dirp);
517 }
518 
519 static VOIDDIRENT *
real_readdir(VOIDDIR * dirp)520 real_readdir(VOIDDIR *dirp)
521 {
522     return (VOIDDIRENT*)readdir((DIR*)dirp);
523 }
524 
525 static int
real_stat(const char * buf,struct stat * s)526 real_stat(const char *buf, struct stat *s)
527 {
528     return stat(buf, s);
529 }
530 
531 void
local_glob_routines(void)532 local_glob_routines(void)
533 {
534     set_glob_routines(real_opendir, real_closedir,
535 		      real_readdir, real_getname, real_stat);
536 }
537 
538 #ifndef TEST
539 /*****************************************************************************
540 * wrappers for the FSP remote functions
541 *****************************************************************************/
542 static char *
my_getname(VOIDDIRENT * dp)543 my_getname(VOIDDIRENT *dp)
544 {
545     return ((struct rdirent *)dp)->rd_name;
546 }
547 
548 static VOIDDIR *
my_opendir(char * dirname)549 my_opendir(char *dirname)
550 {
551     return (VOIDDIR*)util_opendir(dirname);
552 }
553 
554 static void
my_closedir(VOIDDIR * dirp)555 my_closedir(VOIDDIR *dirp)
556 {
557     (void)util_closedir((RDIR*)dirp);
558 }
559 
560 static VOIDDIRENT *
my_readdir(VOIDDIR * dirp)561 my_readdir(VOIDDIR *dirp)
562 {
563     return (VOIDDIRENT*)util_readdir((RDIR*)dirp);
564 }
565 
566 void
remote_glob_routines(void)567 remote_glob_routines(void)
568 {
569     set_glob_routines(my_opendir,my_closedir,my_readdir,my_getname,util_stat);
570 }
571 #endif
572 
573 #ifdef TEST
574 
575 int
main(int argc,char * argv[])576 main(int argc, char *argv[])
577 {
578     char **names;
579 
580     globerr = 0;
581     home = getenv("HOME");
582 
583     names = glob(argv[1]);
584 
585     if (names == 0) {
586 	ffprintf(STDERR, "glob error: %s\n", globerr);
587 	return 1;
588     }
589 
590     while (*names)
591 	ffprintf(STDOUT,"%s\n", *names++);
592 
593     return 0;
594 }
595 #endif
596