1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15: #include "ruby/ruby.h"
16:
17: #include <sys/types.h>
18: #include <sys/stat.h>
19:
20: #ifdef HAVE_UNISTD_H
21: #include <unistd.h>
22: #endif
23:
24: #if defined HAVE_DIRENT_H && !defined _WIN32
25: # include <dirent.h>
26: # define NAMLEN(dirent) strlen((dirent)->d_name)
27: #elif defined HAVE_DIRECT_H && !defined _WIN32
28: # include <direct.h>
29: # define NAMLEN(dirent) strlen((dirent)->d_name)
30: #else
31: # define dirent direct
32: # if !defined __NeXT__
33: # define NAMLEN(dirent) (dirent)->d_namlen
34: # else
35: #
36: # define NAMLEN(dirent) strlen((dirent)->d_name)
37: # endif
38: # if HAVE_SYS_NDIR_H
39: # include <sys/ndir.h>
40: # endif
41: # if HAVE_SYS_DIR_H
42: # include <sys/dir.h>
43: # endif
44: # if HAVE_NDIR_H
45: # include <ndir.h>
46: # endif
47: # ifdef _WIN32
48: # include "win32/dir.h"
49: # endif
50: #endif
51:
52: #include <errno.h>
53:
54: #ifndef HAVE_STDLIB_H
55: char *getenv();
56: #endif
57:
58: #ifndef HAVE_STRING_H
59: char *strchr(char*,char);
60: #endif
61:
62: #include <ctype.h>
63:
64: #include "ruby/util.h"
65:
66: #if !defined HAVE_LSTAT && !defined lstat
67: #define lstat stat
68: #endif
69:
70: #ifndef CASEFOLD_FILESYSTEM
71: # if defined DOSISH || defined __VMS
72: # define CASEFOLD_FILESYSTEM 1
73: # else
74: # define CASEFOLD_FILESYSTEM 0
75: # endif
76: #endif
77:
78: #define FNM_NOESCAPE 0x01
79: #define FNM_PATHNAME 0x02
80: #define FNM_DOTMATCH 0x04
81: #define FNM_CASEFOLD 0x08
82: #if CASEFOLD_FILESYSTEM
83: #define FNM_SYSCASE FNM_CASEFOLD
84: #else
85: #define FNM_SYSCASE 0
86: #endif
87:
88: #define FNM_NOMATCH 1
89: #define FNM_ERROR 2
90:
91: #define downcase(c) (nocase && ISUPPER(c) ? tolower(c) : (c))
92: #define compare(c1, c2) (((unsigned char)(c1)) - ((unsigned char)(c2)))
93:
94:
95:
96:
97:
98: #if defined(CharNext)
99: # define Next(p) CharNext(p)
100: #elif defined(DJGPP)
101: # define Next(p) ((p) + mblen(p, RUBY_MBCHAR_MAXSIZE))
102: #elif defined(__EMX__)
103: # define Next(p) ((p) + emx_mblen(p))
104: static inline int
105: emx_mblen(const char *p)
106: {
107: int n = mblen(p, RUBY_MBCHAR_MAXSIZE);
108: return (n < 0) ? 1 : n;
109: }
110: #endif
111:
112: #ifndef Next
113: # define Next(p) ((p) + 1)
114: # define Inc(p) (++(p))
115: # define Compare(p1, p2) (compare(downcase(*(p1)), downcase(*(p2))))
116: #else
117: # define Inc(p) ((p) = Next(p))
118: # define Compare(p1, p2) (CompareImpl(p1, p2, nocase))
119: static int
120: CompareImpl(const char *p1, const char *p2, int nocase)
121: {
122: const int len1 = Next(p1) - p1;
123: const int len2 = Next(p2) - p2;
124: #ifdef _WIN32
125: char buf1[10], buf2[10];
126: #endif
127:
128: if (len1 < 0 || len2 < 0) {
129: rb_fatal("CompareImpl: negative len");
130: }
131:
132: if (len1 == 0) return len2;
133: if (len2 == 0) return -len1;
134:
135: #ifdef _WIN32
136: if (nocase && rb_w32_iswinnt()) {
137: if (len1 > 1) {
138: if (len1 >= sizeof(buf1)) {
139: rb_fatal("CompareImpl: too large len");
140: }
141: memcpy(buf1, p1, len1);
142: buf1[len1] = '\0';
143: CharLower(buf1);
144: p1 = buf1;
145: }
146: if (len2 > 1) {
147: if (len2 >= sizeof(buf2)) {
148: rb_fatal("CompareImpl: too large len");
149: }
150: memcpy(buf2, p2, len2);
151: buf2[len2] = '\0';
152: CharLower(buf2);
153: p2 = buf2;
154: }
155: }
156: #endif
157: if (len1 == 1)
158: if (len2 == 1)
159: return compare(downcase(*p1), downcase(*p2));
160: else {
161: const int ret = compare(downcase(*p1), *p2);
162: return ret ? ret : -1;
163: }
164: else
165: if (len2 == 1) {
166: const int ret = compare(*p1, downcase(*p2));
167: return ret ? ret : 1;
168: }
169: else {
170: const int ret = memcmp(p1, p2, len1 < len2 ? len1 : len2);
171: return ret ? ret : len1 - len2;
172: }
173: }
174: #endif
175:
176: static char *
177: bracket(
178: const char *p,
179: const char *s,
180: int flags)
181: {
182: const int nocase = flags & FNM_CASEFOLD;
183: const int escape = !(flags & FNM_NOESCAPE);
184:
185: int ok = 0, not = 0;
186:
187: if (*p == '!' || *p == '^') {
188: not = 1;
189: p++;
190: }
191:
192: while (*p != ']') {
193: const char *t1 = p;
194: if (escape && *t1 == '\\')
195: t1++;
196: if (!*t1)
197: return NULL;
198: p = Next(t1);
199: if (p[0] == '-' && p[1] != ']') {
200: const char *t2 = p + 1;
201: if (escape && *t2 == '\\')
202: t2++;
203: if (!*t2)
204: return NULL;
205: p = Next(t2);
206: if (!ok && Compare(t1, s) <= 0 && Compare(s, t2) <= 0)
207: ok = 1;
208: }
209: else
210: if (!ok && Compare(t1, s) == 0)
211: ok = 1;
212: }
213:
214: return ok == not ? NULL : (char *)p + 1;
215: }
216:
217:
218:
219:
220:
221:
222: #define UNESCAPE(p) (escape && *(p) == '\\' ? (p) + 1 : (p))
223: #define ISEND(p) (!*(p) || (pathname && *(p) == '/'))
224: #define RETURN(val) return *pcur = p, *scur = s, (val);
225:
226: static int
227: fnmatch_helper(
228: const char **pcur,
229: const char **scur,
230: int flags)
231: {
232: const int period = !(flags & FNM_DOTMATCH);
233: const int pathname = flags & FNM_PATHNAME;
234: const int escape = !(flags & FNM_NOESCAPE);
235: const int nocase = flags & FNM_CASEFOLD;
236:
237: const char *ptmp = 0;
238: const char *stmp = 0;
239:
240: const char *p = *pcur;
241: const char *s = *scur;
242:
243: if (period && *s == '.' && *UNESCAPE(p) != '.')
244: RETURN(FNM_NOMATCH);
245:
246: while (1) {
247: switch (*p) {
248: case '*':
249: do { p++; } while (*p == '*');
250: if (ISEND(UNESCAPE(p))) {
251: p = UNESCAPE(p);
252: RETURN(0);
253: }
254: if (ISEND(s))
255: RETURN(FNM_NOMATCH);
256: ptmp = p;
257: stmp = s;
258: continue;
259:
260: case '?':
261: if (ISEND(s))
262: RETURN(FNM_NOMATCH);
263: p++;
264: Inc(s);
265: continue;
266:
267: case '[': {
268: const char *t;
269: if (ISEND(s))
270: RETURN(FNM_NOMATCH);
271: if ((t = bracket(p + 1, s, flags)) != 0) {
272: p = t;
273: Inc(s);
274: continue;
275: }
276: goto failed;
277: }
278: }
279:
280:
281: p = UNESCAPE(p);
282: if (ISEND(s))
283: RETURN(ISEND(p) ? 0 : FNM_NOMATCH);
284: if (ISEND(p))
285: goto failed;
286: if (Compare(p, s) != 0)
287: goto failed;
288: Inc(p);
289: Inc(s);
290: continue;
291:
292: failed:
293: if (ptmp && stmp) {
294: p = ptmp;
295: Inc(stmp);
296: s = stmp;
297: continue;
298: }
299: RETURN(FNM_NOMATCH);
300: }
301: }
302:
303: static int
304: fnmatch(
305: const char *p,
306: const char *s,
307: int flags)
308: {
309: const int period = !(flags & FNM_DOTMATCH);
310: const int pathname = flags & FNM_PATHNAME;
311:
312: const char *ptmp = 0;
313: const char *stmp = 0;
314:
315: if (pathname) {
316: while (1) {
317: if (p[0] == '*' && p[1] == '*' && p[2] == '/') {
318: do { p += 3; } while (p[0] == '*' && p[1] == '*' && p[2] == '/');
319: ptmp = p;
320: stmp = s;
321: }
322: if (fnmatch_helper(&p, &s, flags) == 0) {
323: while (*s && *s != '/') Inc(s);
324: if (*p && *s) {
325: p++;
326: s++;
327: continue;
328: }
329: if (!*p && !*s)
330: return 0;
331: }
332:
333: if (ptmp && stmp && !(period && *stmp == '.')) {
334: while (*stmp && *stmp != '/') Inc(stmp);
335: if (*stmp) {
336: p = ptmp;
337: stmp++;
338: s = stmp;
339: continue;
340: }
341: }
342: return FNM_NOMATCH;
343: }
344: }
345: else
346: return fnmatch_helper(&p, &s, flags);
347: }
348:
349: VALUE rb_cDir;
350:
351: struct dir_data {
352: DIR *dir;
353: char *path;
354: };
355:
356: static void
357: free_dir(struct dir_data *dir)
358: {
359: if (dir) {
360: if (dir->dir) closedir(dir->dir);
361: if (dir->path) free(dir->path);
362: }
363: free(dir);
364: }
365:
366: static VALUE dir_close(VALUE);
367:
368: static VALUE
369: dir_s_alloc(VALUE klass)
370: {
371: struct dir_data *dirp;
372: VALUE obj = Data_Make_Struct(klass, struct dir_data, 0, free_dir, dirp);
373:
374: dirp->dir = NULL;
375: dirp->path = NULL;
376:
377: return obj;
378: }
379:
380:
381:
382:
383:
384:
385:
386: static VALUE
387: dir_initialize(VALUE dir, VALUE dirname)
388: {
389: struct dir_data *dp;
390:
391: FilePathValue(dirname);
392: Data_Get_Struct(dir, struct dir_data, dp);
393: if (dp->dir) closedir(dp->dir);
394: if (dp->path) free(dp->path);
395: dp->dir = NULL;
396: dp->path = NULL;
397: dp->dir = opendir(RSTRING_PTR(dirname));
398: if (dp->dir == NULL) {
399: if (errno == EMFILE || errno == ENFILE) {
400: rb_gc();
401: dp->dir = opendir(RSTRING_PTR(dirname));
402: }
403: if (dp->dir == NULL) {
404: rb_sys_fail(RSTRING_PTR(dirname));
405: }
406: }
407: dp->path = strdup(RSTRING_PTR(dirname));
408:
409: return dir;
410: }
411:
412:
413:
414:
415:
416:
417:
418:
419:
420:
421:
422:
423: static VALUE
424: dir_s_open(VALUE klass, VALUE dirname)
425: {
426: struct dir_data *dp;
427: VALUE dir = Data_Make_Struct(klass, struct dir_data, 0, free_dir, dp);
428:
429: dir_initialize(dir, dirname);
430: if (rb_block_given_p()) {
431: return rb_ensure(rb_yield, dir, dir_close, dir);
432: }
433:
434: return dir;
435: }
436:
437: static void
438: dir_closed(void)
439: {
440: rb_raise(rb_eIOError, "closed directory");
441: }
442:
443: static void
444: dir_check(VALUE dir)
445: {
446: if (!OBJ_TAINTED(dir) && rb_safe_level() >= 4)
447: rb_raise(rb_eSecurityError, "Insecure: operation on untainted Dir");
448: rb_check_frozen(dir);
449: }
450:
451: #define GetDIR(obj, dirp) do {\
452: dir_check(dir);\
453: Data_Get_Struct(obj, struct dir_data, dirp);\
454: if (dirp->dir == NULL) dir_closed();\
455: } while (0)
456:
457:
458:
459:
460:
461:
462:
463: static VALUE
464: dir_inspect(VALUE dir)
465: {
466: struct dir_data *dirp;
467:
468: Data_Get_Struct(dir, struct dir_data, dirp);
469: if (dirp->path) {
470: char *c = rb_obj_classname(dir);
471: int len = strlen(c) + strlen(dirp->path) + 4;
472: VALUE s = rb_str_new(0, len);
473: snprintf(RSTRING_PTR(s), len+1, "#<%s:%s>", c, dirp->path);
474: return s;
475: }
476: return rb_funcall(dir, rb_intern("to_s"), 0, 0);
477: }
478:
479:
480:
481:
482:
483:
484:
485:
486:
487:
488: static VALUE
489: dir_path(VALUE dir)
490: {
491: struct dir_data *dirp;
492:
493: Data_Get_Struct(dir, struct dir_data, dirp);
494: if (!dirp->path) return Qnil;
495: return rb_str_new2(dirp->path);
496: }
497:
498:
499:
500:
501:
502:
503:
504:
505:
506:
507:
508:
509:
510: static VALUE
511: dir_read(VALUE dir)
512: {
513: struct dir_data *dirp;
514: struct dirent *dp;
515:
516: GetDIR(dir, dirp);
517: errno = 0;
518: dp = readdir(dirp->dir);
519: if (dp) {
520: return rb_tainted_str_new(dp->d_name, NAMLEN(dp));
521: }
522: else if (errno == 0) {
523: return Qnil;
524: }
525: else {
526: rb_sys_fail(0);
527: }
528: return Qnil;
529: }
530:
531:
532:
533:
534:
535:
536:
537:
538:
539:
540:
541:
542:
543:
544:
545:
546:
547:
548: static VALUE
549: dir_each(VALUE dir)
550: {
551: struct dir_data *dirp;
552: struct dirent *dp;
553:
554: RETURN_ENUMERATOR(dir, 0, 0);
555: GetDIR(dir, dirp);
556: rewinddir(dirp->dir);
557: for (dp = readdir(dirp->dir); dp != NULL; dp = readdir(dirp->dir)) {
558: rb_yield(rb_tainted_str_new(dp->d_name, NAMLEN(dp)));
559: if (dirp->dir == NULL) dir_closed();
560: }
561: return dir;
562: }
563:
564:
565:
566:
567:
568:
569:
570:
571:
572:
573:
574:
575:
576:
577: static VALUE
578: dir_tell(VALUE dir)
579: {
580: #ifdef HAVE_TELLDIR
581: struct dir_data *dirp;
582: long pos;
583:
584: Data_Get_Struct(dir, struct dir_data, dirp);
585: pos = telldir(dirp->dir);
586: return rb_int2inum(pos);
587: #else
588: rb_notimplement();
589: #endif
590: }
591:
592:
593:
594:
595:
596: