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