dir.c

Go to the documentation of this file.
00001 /**********************************************************************
00002 
00003   dir.c -
00004 
00005   $Author: nagachika $
00006   created at: Wed Jan  5 09:51:01 JST 1994
00007 
00008   Copyright (C) 1993-2007 Yukihiro Matsumoto
00009   Copyright (C) 2000  Network Applied Communication Laboratory, Inc.
00010   Copyright (C) 2000  Information-technology Promotion Agency, Japan
00011 
00012 **********************************************************************/
00013 
00014 #include "ruby/ruby.h"
00015 #include "ruby/encoding.h"
00016 #include "internal.h"
00017 
00018 #include <sys/types.h>
00019 #include <sys/stat.h>
00020 
00021 #ifdef HAVE_UNISTD_H
00022 #include <unistd.h>
00023 #endif
00024 
00025 #if defined HAVE_DIRENT_H && !defined _WIN32
00026 # include <dirent.h>
00027 # define NAMLEN(dirent) strlen((dirent)->d_name)
00028 #elif defined HAVE_DIRECT_H && !defined _WIN32
00029 # include <direct.h>
00030 # define NAMLEN(dirent) strlen((dirent)->d_name)
00031 #else
00032 # define dirent direct
00033 # define NAMLEN(dirent) (dirent)->d_namlen
00034 # if HAVE_SYS_NDIR_H
00035 #  include <sys/ndir.h>
00036 # endif
00037 # if HAVE_SYS_DIR_H
00038 #  include <sys/dir.h>
00039 # endif
00040 # if HAVE_NDIR_H
00041 #  include <ndir.h>
00042 # endif
00043 # ifdef _WIN32
00044 #  include "win32/dir.h"
00045 # endif
00046 #endif
00047 #if defined(__native_client__) && defined(NACL_NEWLIB)
00048 # include "nacl/dirent.h"
00049 # include "nacl/stat.h"
00050 #endif
00051 
00052 #include <errno.h>
00053 
00054 #ifndef HAVE_STDLIB_H
00055 char *getenv();
00056 #endif
00057 
00058 #ifndef HAVE_STRING_H
00059 char *strchr(char*,char);
00060 #endif
00061 
00062 #include <ctype.h>
00063 
00064 #include "ruby/util.h"
00065 
00066 /* define system APIs */
00067 #ifdef _WIN32
00068 #undef chdir
00069 #define chdir(p) rb_w32_uchdir(p)
00070 #undef mkdir
00071 #define mkdir(p, m) rb_w32_umkdir((p), (m))
00072 #undef rmdir
00073 #define rmdir(p) rb_w32_urmdir(p)
00074 #undef opendir
00075 #define opendir(p) rb_w32_uopendir(p)
00076 #endif
00077 
00078 #ifdef __APPLE__
00079 # define HAVE_HFS 1
00080 #else
00081 # define HAVE_HFS 0
00082 #endif
00083 #if HAVE_HFS
00084 #include <sys/param.h>
00085 #include <sys/mount.h>
00086 
00087 static inline int
00088 is_hfs(DIR *dirp)
00089 {
00090     struct statfs buf;
00091     if (fstatfs(dirfd(dirp), &buf) == 0) {
00092         return buf.f_type == 17; /* HFS on darwin */
00093     }
00094     return FALSE;
00095 }
00096 
00097 static inline int
00098 has_nonascii(const char *ptr, size_t len)
00099 {
00100     while (len > 0) {
00101         if (!ISASCII(*ptr)) return 1;
00102         ptr++;
00103         --len;
00104     }
00105     return 0;
00106 }
00107 
00108 # define IF_HAVE_HFS(something) something
00109 #else
00110 # define IF_HAVE_HFS(something) /* nothing */
00111 #endif
00112 
00113 #define FNM_NOESCAPE    0x01
00114 #define FNM_PATHNAME    0x02
00115 #define FNM_DOTMATCH    0x04
00116 #define FNM_CASEFOLD    0x08
00117 #define FNM_EXTGLOB     0x10
00118 #if CASEFOLD_FILESYSTEM
00119 #define FNM_SYSCASE     FNM_CASEFOLD
00120 #else
00121 #define FNM_SYSCASE     0
00122 #endif
00123 
00124 #define FNM_NOMATCH     1
00125 #define FNM_ERROR       2
00126 
00127 # define Next(p, e, enc) ((p)+ rb_enc_mbclen((p), (e), (enc)))
00128 # define Inc(p, e, enc) ((p) = Next((p), (e), (enc)))
00129 
00130 static char *
00131 bracket(
00132     const char *p, /* pattern (next to '[') */
00133     const char *pend,
00134     const char *s, /* string */
00135     const char *send,
00136     int flags,
00137     rb_encoding *enc)
00138 {
00139     const int nocase = flags & FNM_CASEFOLD;
00140     const int escape = !(flags & FNM_NOESCAPE);
00141     unsigned int c1, c2;
00142     int r;
00143     int ok = 0, not = 0;
00144 
00145     if (p >= pend) return NULL;
00146     if (*p == '!' || *p == '^') {
00147         not = 1;
00148         p++;
00149     }
00150 
00151     while (*p != ']') {
00152         const char *t1 = p;
00153         if (escape && *t1 == '\\')
00154             t1++;
00155         if (!*t1)
00156             return NULL;
00157         p = t1 + (r = rb_enc_mbclen(t1, pend, enc));
00158         if (p >= pend) return NULL;
00159         if (p[0] == '-' && p[1] != ']') {
00160             const char *t2 = p + 1;
00161             int r2;
00162             if (escape && *t2 == '\\')
00163                 t2++;
00164             if (!*t2)
00165                 return NULL;
00166             p = t2 + (r2 = rb_enc_mbclen(t2, pend, enc));
00167             if (ok) continue;
00168             if ((r <= (send-s) && memcmp(t1, s, r) == 0) ||
00169                 (r2 <= (send-s) && memcmp(t2, s, r2) == 0)) {
00170                 ok = 1;
00171                 continue;
00172             }
00173             c1 = rb_enc_codepoint(s, send, enc);
00174             if (nocase) c1 = rb_enc_toupper(c1, enc);
00175             c2 = rb_enc_codepoint(t1, pend, enc);
00176             if (nocase) c2 = rb_enc_toupper(c2, enc);
00177             if (c1 < c2) continue;
00178             c2 = rb_enc_codepoint(t2, pend, enc);
00179             if (nocase) c2 = rb_enc_toupper(c2, enc);
00180             if (c1 > c2) continue;
00181         }
00182         else {
00183             if (ok) continue;
00184             if (r <= (send-s) && memcmp(t1, s, r) == 0) {
00185                 ok = 1;
00186                 continue;
00187             }
00188             if (!nocase) continue;
00189             c1 = rb_enc_toupper(rb_enc_codepoint(s, send, enc), enc);
00190             c2 = rb_enc_toupper(rb_enc_codepoint(p, pend, enc), enc);
00191             if (c1 != c2) continue;
00192         }
00193         ok = 1;
00194     }
00195 
00196     return ok == not ? NULL : (char *)p + 1;
00197 }
00198 
00199 /* If FNM_PATHNAME is set, only path element will be matched. (upto '/' or '\0')
00200    Otherwise, entire string will be matched.
00201    End marker itself won't be compared.
00202    And if function succeeds, *pcur reaches end marker.
00203 */
00204 #define UNESCAPE(p) (escape && *(p) == '\\' ? (p) + 1 : (p))
00205 #define ISEND(p) (!*(p) || (pathname && *(p) == '/'))
00206 #define RETURN(val) return *pcur = p, *scur = s, (val);
00207 
00208 static int
00209 fnmatch_helper(
00210     const char **pcur, /* pattern */
00211     const char **scur, /* string */
00212     int flags,
00213     rb_encoding *enc)
00214 {
00215     const int period = !(flags & FNM_DOTMATCH);
00216     const int pathname = flags & FNM_PATHNAME;
00217     const int escape = !(flags & FNM_NOESCAPE);
00218     const int nocase = flags & FNM_CASEFOLD;
00219 
00220     const char *ptmp = 0;
00221     const char *stmp = 0;
00222 
00223     const char *p = *pcur;
00224     const char *pend = p + strlen(p);
00225     const char *s = *scur;
00226     const char *send = s + strlen(s);
00227 
00228     int r;
00229 
00230     if (period && *s == '.' && *UNESCAPE(p) != '.') /* leading period */
00231         RETURN(FNM_NOMATCH);
00232 
00233     while (1) {
00234         switch (*p) {
00235           case '*':
00236             do { p++; } while (*p == '*');
00237             if (ISEND(UNESCAPE(p))) {
00238                 p = UNESCAPE(p);
00239                 RETURN(0);
00240             }
00241             if (ISEND(s))
00242                 RETURN(FNM_NOMATCH);
00243             ptmp = p;
00244             stmp = s;
00245             continue;
00246 
00247           case '?':
00248             if (ISEND(s))
00249                 RETURN(FNM_NOMATCH);
00250             p++;
00251             Inc(s, send, enc);
00252             continue;
00253 
00254           case '[': {
00255             const char *t;
00256             if (ISEND(s))
00257                 RETURN(FNM_NOMATCH);
00258             if ((t = bracket(p + 1, pend, s, send, flags, enc)) != 0) {
00259                 p = t;
00260                 Inc(s, send, enc);
00261                 continue;
00262             }
00263             goto failed;
00264           }
00265         }
00266 
00267         /* ordinary */
00268         p = UNESCAPE(p);
00269         if (ISEND(s))
00270             RETURN(ISEND(p) ? 0 : FNM_NOMATCH);
00271         if (ISEND(p))
00272             goto failed;
00273         r = rb_enc_precise_mbclen(p, pend, enc);
00274         if (!MBCLEN_CHARFOUND_P(r))
00275             goto failed;
00276         if (r <= (send-s) && memcmp(p, s, r) == 0) {
00277             p += r;
00278             s += r;
00279             continue;
00280         }
00281         if (!nocase) goto failed;
00282         if (rb_enc_toupper(rb_enc_codepoint(p, pend, enc), enc) !=
00283             rb_enc_toupper(rb_enc_codepoint(s, send, enc), enc))
00284             goto failed;
00285         p += r;
00286         Inc(s, send, enc);
00287         continue;
00288 
00289       failed: /* try next '*' position */
00290         if (ptmp && stmp) {
00291             p = ptmp;
00292             Inc(stmp, send, enc); /* !ISEND(*stmp) */
00293             s = stmp;
00294             continue;
00295         }
00296         RETURN(FNM_NOMATCH);
00297     }
00298 }
00299 
00300 static int
00301 fnmatch(
00302     const char *pattern,
00303     rb_encoding *enc,
00304     const char *string,
00305     int flags)
00306 {
00307     const char *p = pattern;
00308     const char *s = string;
00309     const char *send = s + strlen(string);
00310     const int period = !(flags & FNM_DOTMATCH);
00311     const int pathname = flags & FNM_PATHNAME;
00312 
00313     const char *ptmp = 0;
00314     const char *stmp = 0;
00315 
00316     if (pathname) {
00317         while (1) {
00318             if (p[0] == '*' && p[1] == '*' && p[2] == '/') {
00319                 do { p += 3; } while (p[0] == '*' && p[1] == '*' && p[2] == '/');
00320                 ptmp = p;
00321                 stmp = s;
00322             }
00323             if (fnmatch_helper(&p, &s, flags, enc) == 0) {
00324                 while (*s && *s != '/') Inc(s, send, enc);
00325                 if (*p && *s) {
00326                     p++;
00327                     s++;
00328                     continue;
00329                 }
00330                 if (!*p && !*s)
00331                     return 0;
00332             }
00333             /* failed : try next recursion */
00334             if (ptmp && stmp && !(period && *stmp == '.')) {
00335                 while (*stmp && *stmp != '/') Inc(stmp, send, enc);
00336                 if (*stmp) {
00337                     p = ptmp;
00338                     stmp++;
00339                     s = stmp;
00340                     continue;
00341                 }
00342             }
00343             return FNM_NOMATCH;
00344         }
00345     }
00346     else
00347         return fnmatch_helper(&p, &s, flags, enc);
00348 }
00349 
00350 VALUE rb_cDir;
00351 
00352 struct dir_data {
00353     DIR *dir;
00354     VALUE path;
00355     rb_encoding *enc;
00356 };
00357 
00358 static void
00359 dir_mark(void *ptr)
00360 {
00361     struct dir_data *dir = ptr;
00362     rb_gc_mark(dir->path);
00363 }
00364 
00365 static void
00366 dir_free(void *ptr)
00367 {
00368     struct dir_data *dir = ptr;
00369     if (dir) {
00370         if (dir->dir) closedir(dir->dir);
00371     }
00372     xfree(dir);
00373 }
00374 
00375 static size_t
00376 dir_memsize(const void *ptr)
00377 {
00378     return ptr ? sizeof(struct dir_data) : 0;
00379 }
00380 
00381 static const rb_data_type_t dir_data_type = {
00382     "dir",
00383     {dir_mark, dir_free, dir_memsize,},
00384     NULL, NULL, RUBY_TYPED_FREE_IMMEDIATELY
00385 };
00386 
00387 static VALUE dir_close(VALUE);
00388 
00389 #define GlobPathValue(str, safe) \
00390     /* can contain null bytes as separators */  \
00391     (!RB_TYPE_P((str), T_STRING) ?              \
00392      (void)FilePathValue(str) :                 \
00393      (void)(check_safe_glob((str), (safe)),             \
00394       check_glob_encoding(str), (str)))
00395 #define check_safe_glob(str, safe) ((safe) ? rb_check_safe_obj(str) : (void)0)
00396 #define check_glob_encoding(str) rb_enc_check((str), rb_enc_from_encoding(rb_usascii_encoding()))
00397 
00398 static VALUE
00399 dir_s_alloc(VALUE klass)
00400 {
00401     struct dir_data *dirp;
00402     VALUE obj = TypedData_Make_Struct(klass, struct dir_data, &dir_data_type, dirp);
00403 
00404     dirp->dir = NULL;
00405     dirp->path = Qnil;
00406     dirp->enc = NULL;
00407 
00408     return obj;
00409 }
00410 
00411 /*
00412  *  call-seq:
00413  *     Dir.new( string ) -> aDir
00414  *     Dir.new( string, encoding: enc ) -> aDir
00415  *
00416  *  Returns a new directory object for the named directory.
00417  *
00418  *  The optional <i>enc</i> argument specifies the encoding of the directory.
00419  *  If not specified, the filesystem encoding is used.
00420  */
00421 static VALUE
00422 dir_initialize(int argc, VALUE *argv, VALUE dir)
00423 {
00424     struct dir_data *dp;
00425     rb_encoding  *fsenc;
00426     VALUE dirname, opt, orig;
00427     static ID keyword_ids[1];
00428 
00429     if (!keyword_ids[0]) {
00430         keyword_ids[0] = rb_intern("encoding");
00431     }
00432 
00433     fsenc = rb_filesystem_encoding();
00434 
00435     rb_scan_args(argc, argv, "1:", &dirname, &opt);
00436 
00437     if (!NIL_P(opt)) {
00438         VALUE enc;
00439         rb_get_kwargs(opt, keyword_ids, 0, 1, &enc);
00440         if (enc != Qundef && !NIL_P(enc)) {
00441             fsenc = rb_to_encoding(enc);
00442         }
00443     }
00444 
00445     GlobPathValue(dirname, FALSE);
00446     orig = rb_str_dup_frozen(dirname);
00447     dirname = rb_str_encode_ospath(dirname);
00448     dirname = rb_str_dup_frozen(dirname);
00449 
00450     TypedData_Get_Struct(dir, struct dir_data, &dir_data_type, dp);
00451     if (dp->dir) closedir(dp->dir);
00452     dp->dir = NULL;
00453     dp->path = Qnil;
00454     dp->enc = fsenc;
00455     dp->dir = opendir(RSTRING_PTR(dirname));
00456     if (dp->dir == NULL) {
00457         if (errno == EMFILE || errno == ENFILE) {
00458             rb_gc();
00459             dp->dir = opendir(RSTRING_PTR(dirname));
00460         }
00461         if (dp->dir == NULL) {
00462             rb_sys_fail_path(orig);
00463         }
00464     }
00465     dp->path = orig;
00466 
00467     return dir;
00468 }
00469 
00470 /*
00471  *  call-seq:
00472  *     Dir.open( string ) -> aDir
00473  *     Dir.open( string, encoding: enc ) -> aDir
00474  *     Dir.open( string ) {| aDir | block } -> anObject
00475  *     Dir.open( string, encoding: enc ) {| aDir | block } -> anObject
00476  *
00477  *  The optional <i>enc</i> argument specifies the encoding of the directory.
00478  *  If not specified, the filesystem encoding is used.
00479  *
00480  *  With no block, <code>open</code> is a synonym for
00481  *  <code>Dir::new</code>. If a block is present, it is passed
00482  *  <i>aDir</i> as a parameter. The directory is closed at the end of
00483  *  the block, and <code>Dir::open</code> returns the value of the
00484  *  block.
00485  */
00486 static VALUE
00487 dir_s_open(int argc, VALUE *argv, VALUE klass)
00488 {
00489     struct dir_data *dp;
00490     VALUE dir = TypedData_Make_Struct(klass, struct dir_data, &dir_data_type, dp);
00491 
00492     dir_initialize(argc, argv, dir);
00493     if (rb_block_given_p()) {
00494         return rb_ensure(rb_yield, dir, dir_close, dir);
00495     }
00496 
00497     return dir;
00498 }
00499 
00500 static void
00501 dir_closed(void)
00502 {
00503     rb_raise(rb_eIOError, "closed directory");
00504 }
00505 
00506 static struct dir_data *
00507 dir_check(VALUE dir)
00508 {
00509     struct dir_data *dirp;
00510     rb_check_frozen(dir);
00511     dirp = rb_check_typeddata(dir, &dir_data_type);
00512     if (!dirp->dir) dir_closed();
00513     return dirp;
00514 }
00515 
00516 #define GetDIR(obj, dirp) ((dirp) = dir_check(obj))
00517 
00518 
00519 /*
00520  *  call-seq:
00521  *     dir.inspect -> string
00522  *
00523  *  Return a string describing this Dir object.
00524  */
00525 static VALUE
00526 dir_inspect(VALUE dir)
00527 {
00528     struct dir_data *dirp;
00529 
00530     TypedData_Get_Struct(dir, struct dir_data, &dir_data_type, dirp);
00531     if (!NIL_P(dirp->path)) {
00532         VALUE str = rb_str_new_cstr("#<");
00533         rb_str_append(str, rb_class_name(CLASS_OF(dir)));
00534         rb_str_cat2(str, ":");
00535         rb_str_append(str, dirp->path);
00536         rb_str_cat2(str, ">");
00537         return str;
00538     }
00539     return rb_funcall(dir, rb_intern("to_s"), 0, 0);
00540 }
00541 
00542 /*
00543  *  call-seq:
00544  *     dir.path -> string or nil
00545  *     dir.to_path -> string or nil
00546  *
00547  *  Returns the path parameter passed to <em>dir</em>'s constructor.
00548  *
00549  *     d = Dir.new("..")
00550  *     d.path   #=> ".."
00551  */
00552 static VALUE
00553 dir_path(VALUE dir)
00554 {
00555     struct dir_data *dirp;
00556 
00557     TypedData_Get_Struct(dir, struct dir_data, &dir_data_type, dirp);
00558     if (NIL_P(dirp->path)) return Qnil;
00559     return rb_str_dup(dirp->path);
00560 }
00561 
00562 #if defined _WIN32
00563 # define READDIR(dir, enc) rb_w32_readdir((dir), (enc))
00564 #else
00565 # define READDIR(dir, enc) readdir((dir))
00566 #endif
00567 
00568 /*
00569  *  call-seq:
00570  *     dir.read -> string or nil
00571  *
00572  *  Reads the next entry from <em>dir</em> and returns it as a string.
00573  *  Returns <code>nil</code> at the end of the stream.
00574  *
00575  *     d = Dir.new("testdir")
00576  *     d.read   #=> "."
00577  *     d.read   #=> ".."
00578  *     d.read   #=> "config.h"
00579  */
00580 static VALUE
00581 dir_read(VALUE dir)
00582 {
00583     struct dir_data *dirp;
00584     struct dirent *dp;
00585 
00586     GetDIR(dir, dirp);
00587     errno = 0;
00588     if ((dp = READDIR(dirp->dir, dirp->enc)) != NULL) {
00589         return rb_external_str_new_with_enc(dp->d_name, NAMLEN(dp), dirp->enc);
00590     }
00591     else {
00592         if (errno != 0) rb_sys_fail(0);
00593         return Qnil;            /* end of stream */
00594     }
00595 }
00596 
00597 /*
00598  *  call-seq:
00599  *     dir.each { |filename| block }  -> dir
00600  *     dir.each                       -> an_enumerator
00601  *
00602  *  Calls the block once for each entry in this directory, passing the
00603  *  filename of each entry as a parameter to the block.
00604  *
00605  *  If no block is given, an enumerator is returned instead.
00606  *
00607  *     d = Dir.new("testdir")
00608  *     d.each  {|x| puts "Got #{x}" }
00609  *
00610  *  <em>produces:</em>
00611  *
00612  *     Got .
00613  *     Got ..
00614  *     Got config.h
00615  *     Got main.rb
00616  */
00617 static VALUE
00618 dir_each(VALUE dir)
00619 {
00620     struct dir_data *dirp;
00621     struct dirent *dp;
00622     IF_HAVE_HFS(int hfs_p);
00623 
00624     RETURN_ENUMERATOR(dir, 0, 0);
00625     GetDIR(dir, dirp);
00626     rewinddir(dirp->dir);
00627     IF_HAVE_HFS(hfs_p = is_hfs(dirp->dir));
00628     while ((dp = READDIR(dirp->dir, dirp->enc)) != NULL) {
00629         const char *name = dp->d_name;
00630         size_t namlen = NAMLEN(dp);
00631         VALUE path;
00632 #if HAVE_HFS
00633         if (hfs_p && has_nonascii(name, namlen) &&
00634             !NIL_P(path = rb_str_normalize_ospath(name, namlen))) {
00635             path = rb_external_str_with_enc(path, dirp->enc);
00636         }
00637         else
00638 #endif
00639         path = rb_external_str_new_with_enc(name, namlen, dirp->enc);
00640         rb_yield(path);
00641         if (dirp->dir == NULL) dir_closed();
00642     }
00643     return dir;
00644 }
00645 
00646 #ifdef HAVE_TELLDIR
00647 /*
00648  *  call-seq:
00649  *     dir.pos -> integer
00650  *     dir.tell -> integer
00651  *
00652  *  Returns the current position in <em>dir</em>. See also
00653  *  <code>Dir#seek</code>.
00654  *
00655  *     d = Dir.new("testdir")
00656  *     d.tell   #=> 0
00657  *     d.read   #=> "."
00658  *     d.tell   #=> 12
00659  */
00660 static VALUE
00661 dir_tell(VALUE dir)
00662 {
00663     struct dir_data *dirp;
00664     long pos;
00665 
00666     GetDIR(dir, dirp);
00667     pos = telldir(dirp->dir);
00668     return rb_int2inum(pos);
00669 }
00670 #else
00671 #define dir_tell rb_f_notimplement
00672 #endif
00673 
00674 #ifdef HAVE_SEEKDIR
00675 /*
00676  *  call-seq:
00677  *     dir.seek( integer ) -> dir
00678  *
00679  *  Seeks to a particular location in <em>dir</em>. <i>integer</i>
00680  *  must be a value returned by <code>Dir#tell</code>.
00681  *
00682  *     d = Dir.new("testdir")   #=> #<Dir:0x401b3c40>
00683  *     d.read                   #=> "."
00684  *     i = d.tell               #=> 12
00685  *     d.read                   #=> ".."
00686  *     d.seek(i)                #=> #<Dir:0x401b3c40>
00687  *     d.read                   #=> ".."
00688  */
00689 static VALUE
00690 dir_seek(VALUE dir, VALUE pos)
00691 {
00692     struct dir_data *dirp;
00693     long p = NUM2LONG(pos);
00694 
00695     GetDIR(dir, dirp);
00696     seekdir(dirp->dir, p);
00697     return dir;
00698 }
00699 #else
00700 #define dir_seek rb_f_notimplement
00701 #endif
00702 
00703 #ifdef HAVE_SEEKDIR
00704 /*
00705  *  call-seq:
00706  *     dir.pos = integer  -> integer
00707  *
00708  *  Synonym for <code>Dir#seek</code>, but returns the position
00709  *  parameter.
00710  *
00711  *     d = Dir.new("testdir")   #=> #<Dir:0x401b3c40>
00712  *     d.read                   #=> "."
00713  *     i = d.pos                #=> 12
00714  *     d.read                   #=> ".."
00715  *     d.pos = i                #=> 12
00716  *     d.read                   #=> ".."
00717  */
00718 static VALUE
00719 dir_set_pos(VALUE dir, VALUE pos)
00720 {
00721     dir_seek(dir, pos);
00722     return pos;
00723 }
00724 #else
00725 #define dir_set_pos rb_f_notimplement
00726 #endif
00727 
00728 /*
00729  *  call-seq:
00730  *     dir.rewind -> dir
00731  *
00732  *  Repositions <em>dir</em> to the first entry.
00733  *
00734  *     d = Dir.new("testdir")
00735  *     d.read     #=> "."
00736  *     d.rewind   #=> #<Dir:0x401b3fb0>
00737  *     d.read     #=> "."
00738  */
00739 static VALUE
00740 dir_rewind(VALUE dir)
00741 {
00742     struct dir_data *dirp;
00743 
00744     GetDIR(dir, dirp);
00745     rewinddir(dirp->dir);
00746     return dir;
00747 }
00748 
00749 /*
00750  *  call-seq:
00751  *     dir.close -> nil
00752  *
00753  *  Closes the directory stream. Any further attempts to access
00754  *  <em>dir</em> will raise an <code>IOError</code>.
00755  *
00756  *     d = Dir.new("testdir")
00757  *     d.close   #=> nil
00758  */
00759 static VALUE
00760 dir_close(VALUE dir)
00761 {
00762     struct dir_data *dirp;
00763 
00764     GetDIR(dir, dirp);
00765     closedir(dirp->dir);
00766     dirp->dir = NULL;
00767 
00768     return Qnil;
00769 }
00770 
00771 static void
00772 dir_chdir(VALUE path)
00773 {
00774     if (chdir(RSTRING_PTR(path)) < 0)
00775         rb_sys_fail_path(path);
00776 }
00777 
00778 static int chdir_blocking = 0;
00779 static VALUE chdir_thread = Qnil;
00780 
00781 struct chdir_data {
00782     VALUE old_path, new_path;
00783     int done;
00784 };
00785 
00786 static VALUE
00787 chdir_yield(struct chdir_data *args)
00788 {
00789     dir_chdir(args->new_path);
00790     args->done = TRUE;
00791     chdir_blocking++;
00792     if (chdir_thread == Qnil)
00793         chdir_thread = rb_thread_current();
00794     return rb_yield(args->new_path);
00795 }
00796 
00797 static VALUE
00798 chdir_restore(struct chdir_data *args)
00799 {
00800     if (args->done) {
00801         chdir_blocking--;
00802         if (chdir_blocking == 0)
00803             chdir_thread = Qnil;
00804         dir_chdir(args->old_path);
00805     }
00806     return Qnil;
00807 }
00808 
00809 /*
00810  *  call-seq:
00811  *     Dir.chdir( [ string] ) -> 0
00812  *     Dir.chdir( [ string] ) {| path | block }  -> anObject
00813  *
00814  *  Changes the current working directory of the process to the given
00815  *  string. When called without an argument, changes the directory to
00816  *  the value of the environment variable <code>HOME</code>, or
00817  *  <code>LOGDIR</code>. <code>SystemCallError</code> (probably
00818  *  <code>Errno::ENOENT</code>) if the target directory does not exist.
00819  *
00820  *  If a block is given, it is passed the name of the new current
00821  *  directory, and the block is executed with that as the current
00822  *  directory. The original working directory is restored when the block
00823  *  exits. The return value of <code>chdir</code> is the value of the
00824  *  block. <code>chdir</code> blocks can be nested, but in a
00825  *  multi-threaded program an error will be raised if a thread attempts
00826  *  to open a <code>chdir</code> block while another thread has one
00827  *  open.
00828  *
00829  *     Dir.chdir("/var/spool/mail")
00830  *     puts Dir.pwd
00831  *     Dir.chdir("/tmp") do
00832  *       puts Dir.pwd
00833  *       Dir.chdir("/usr") do
00834  *         puts Dir.pwd
00835  *       end
00836  *       puts Dir.pwd
00837  *     end
00838  *     puts Dir.pwd
00839  *
00840  *  <em>produces:</em>
00841  *
00842  *     /var/spool/mail
00843  *     /tmp
00844  *     /usr
00845  *     /tmp
00846  *     /var/spool/mail
00847  */
00848 static VALUE
00849 dir_s_chdir(int argc, VALUE *argv, VALUE obj)
00850 {
00851     VALUE path = Qnil;
00852 
00853     rb_secure(2);
00854     if (rb_scan_args(argc, argv, "01", &path) == 1) {
00855         FilePathValue(path);
00856         path = rb_str_encode_ospath(path);
00857     }
00858     else {
00859         const char *dist = getenv("HOME");
00860         if (!dist) {
00861             dist = getenv("LOGDIR");
00862             if (!dist) rb_raise(rb_eArgError, "HOME/LOGDIR not set");
00863         }
00864         path = rb_str_new2(dist);
00865     }
00866 
00867     if (chdir_blocking > 0) {
00868         if (!rb_block_given_p() || rb_thread_current() != chdir_thread)
00869             rb_warn("conflicting chdir during another chdir block");
00870     }
00871 
00872     if (rb_block_given_p()) {
00873         struct chdir_data args;
00874 
00875         args.old_path = rb_str_encode_ospath(rb_dir_getwd());
00876         args.new_path = path;
00877         args.done = FALSE;
00878         return rb_ensure(chdir_yield, (VALUE)&args, chdir_restore, (VALUE)&args);
00879     }
00880     dir_chdir(path);
00881 
00882     return INT2FIX(0);
00883 }
00884 
00885 VALUE
00886 rb_dir_getwd(void)
00887 {
00888     char *path;
00889     VALUE cwd;
00890 
00891     path = my_getcwd();
00892     cwd = rb_tainted_str_new2(path);
00893     rb_enc_associate(cwd, rb_filesystem_encoding());
00894 
00895     xfree(path);
00896     return cwd;
00897 }
00898 
00899 /*
00900  *  call-seq:
00901  *     Dir.getwd -> string
00902  *     Dir.pwd -> string
00903  *
00904  *  Returns the path to the current working directory of this process as
00905  *  a string.
00906  *
00907  *     Dir.chdir("/tmp")   #=> 0
00908  *     Dir.getwd           #=> "/tmp"
00909  */
00910 static VALUE
00911 dir_s_getwd(VALUE dir)
00912 {
00913     return rb_dir_getwd();
00914 }
00915 
00916 static void
00917 check_dirname(volatile VALUE *dir)
00918 {
00919     VALUE d = *dir;
00920     char *path, *pend;
00921     long len;
00922     rb_encoding *enc;
00923 
00924     rb_secure(2);
00925     FilePathValue(d);
00926     enc = rb_enc_get(d);
00927     RSTRING_GETMEM(d, path, len);
00928     pend = path + len;
00929     pend = rb_enc_path_end(rb_enc_path_skip_prefix(path, pend, enc), pend, enc);
00930     if (pend - path < len) {
00931         d = rb_str_subseq(d, 0, pend - path);
00932     }
00933     *dir = rb_str_encode_ospath(d);
00934 }
00935 
00936 #if defined(HAVE_CHROOT)
00937 /*
00938  *  call-seq:
00939  *     Dir.chroot( string ) -> 0
00940  *
00941  *  Changes this process's idea of the file system root. Only a
00942  *  privileged process may make this call. Not available on all
00943  *  platforms. On Unix systems, see <code>chroot(2)</code> for more
00944  *  information.
00945  */
00946 static VALUE
00947 dir_s_chroot(VALUE dir, VALUE path)
00948 {
00949     check_dirname(&path);
00950     if (chroot(RSTRING_PTR(path)) == -1)
00951         rb_sys_fail_path(path);
00952 
00953     return INT2FIX(0);
00954 }
00955 #else
00956 #define dir_s_chroot rb_f_notimplement
00957 #endif
00958 
00959 /*
00960  *  call-seq:
00961  *     Dir.mkdir( string [, integer] ) -> 0
00962  *
00963  *  Makes a new directory named by <i>string</i>, with permissions
00964  *  specified by the optional parameter <i>anInteger</i>. The
00965  *  permissions may be modified by the value of
00966  *  <code>File::umask</code>, and are ignored on NT. Raises a
00967  *  <code>SystemCallError</code> if the directory cannot be created. See
00968  *  also the discussion of permissions in the class documentation for
00969  *  <code>File</code>.
00970  *
00971  *    Dir.mkdir(File.join(Dir.home, ".foo"), 0700) #=> 0
00972  *
00973  */
00974 static VALUE
00975 dir_s_mkdir(int argc, VALUE *argv, VALUE obj)
00976 {
00977     VALUE path, vmode;
00978     int mode;
00979 
00980     if (rb_scan_args(argc, argv, "11", &path, &vmode) == 2) {
00981         mode = NUM2INT(vmode);
00982     }
00983     else {
00984         mode = 0777;
00985     }
00986 
00987     check_dirname(&path);
00988     if (mkdir(RSTRING_PTR(path), mode) == -1)
00989         rb_sys_fail_path(path);
00990 
00991     return INT2FIX(0);
00992 }
00993 
00994 /*
00995  *  call-seq:
00996  *     Dir.delete( string ) -> 0
00997  *     Dir.rmdir( string ) -> 0
00998  *     Dir.unlink( string ) -> 0
00999  *
01000  *  Deletes the named directory. Raises a subclass of
01001  *  <code>SystemCallError</code> if the directory isn't empty.
01002  */
01003 static VALUE
01004 dir_s_rmdir(VALUE obj, VALUE dir)
01005 {
01006     check_dirname(&dir);
01007     if (rmdir(RSTRING_PTR(dir)) < 0)
01008         rb_sys_fail_path(dir);
01009 
01010     return INT2FIX(0);
01011 }
01012 
01013 static VALUE
01014 sys_warning_1(VALUE mesg)
01015 {
01016     rb_sys_warning("%s:%s", strerror(errno), (const char *)mesg);
01017     return Qnil;
01018 }
01019 
01020 #define GLOB_VERBOSE    (1U << (sizeof(int) * CHAR_BIT - 1))
01021 #define sys_warning(val) \
01022     (void)((flags & GLOB_VERBOSE) && rb_protect(sys_warning_1, (VALUE)(val), 0))
01023 
01024 #define GLOB_ALLOC(type) ((type *)malloc(sizeof(type)))
01025 #define GLOB_ALLOC_N(type, n) ((type *)malloc(sizeof(type) * (n)))
01026 #define GLOB_FREE(ptr) free(ptr)
01027 #define GLOB_JUMP_TAG(status) (((status) == -1) ? rb_memerror() : rb_jump_tag(status))
01028 
01029 /*
01030  * ENOTDIR can be returned by stat(2) if a non-leaf element of the path
01031  * is not a directory.
01032  */
01033 #define to_be_ignored(e) ((e) == ENOENT || (e) == ENOTDIR)
01034 
01035 #ifdef _WIN32
01036 #define STAT(p, s)      rb_w32_ustati64((p), (s))
01037 #else
01038 #define STAT(p, s)      stat((p), (s))
01039 #endif
01040 
01041 /* System call with warning */
01042 static int
01043 do_stat(const char *path, struct stat *pst, int flags)
01044 
01045 {
01046     int ret = STAT(path, pst);
01047     if (ret < 0 && !to_be_ignored(errno))
01048         sys_warning(path);
01049 
01050     return ret;
01051 }
01052 
01053 #if defined HAVE_LSTAT || defined lstat
01054 static int
01055 do_lstat(const char *path, struct stat *pst, int flags)
01056 {
01057     int ret = lstat(path, pst);
01058     if (ret < 0 && !to_be_ignored(errno))
01059         sys_warning(path);
01060 
01061     return ret;
01062 }
01063 #else
01064 #define do_lstat do_stat
01065 #endif
01066 
01067 static DIR *
01068 do_opendir(const char *path, int flags, rb_encoding *enc)
01069 {
01070     DIR *dirp;
01071 #ifdef _WIN32
01072     volatile VALUE tmp;
01073     if (enc != rb_usascii_encoding() &&
01074         enc != rb_ascii8bit_encoding() &&
01075         enc != rb_utf8_encoding()) {
01076         tmp = rb_enc_str_new(path, strlen(path), enc);
01077         tmp = rb_str_encode_ospath(tmp);
01078         path = RSTRING_PTR(tmp);
01079     }
01080 #endif
01081     dirp = opendir(path);
01082     if (dirp == NULL && !to_be_ignored(errno))
01083         sys_warning(path);
01084 
01085     return dirp;
01086 }
01087 
01088 /* Return nonzero if S has any special globbing chars in it.  */
01089 static int
01090 has_magic(const char *p, const char *pend, int flags, rb_encoding *enc)
01091 {
01092     const int escape = !(flags & FNM_NOESCAPE);
01093     const int nocase = flags & FNM_CASEFOLD;
01094 
01095     register char c;
01096 
01097     while (p < pend && (c = *p++) != 0) {
01098         switch (c) {
01099           case '*':
01100           case '?':
01101           case '[':
01102             return 1;
01103 
01104           case '\\':
01105             if (escape && !(c = *p++))
01106                 return 0;
01107             continue;
01108 
01109           default:
01110             if (!FNM_SYSCASE && ISALPHA(c) && nocase)
01111                 return 1;
01112         }
01113 
01114         p = Next(p-1, pend, enc);
01115     }
01116 
01117     return 0;
01118 }
01119 
01120 /* Find separator in globbing pattern. */
01121 static char *
01122 find_dirsep(const char *p, const char *pend, int flags, rb_encoding *enc)
01123 {
01124     const int escape = !(flags & FNM_NOESCAPE);
01125 
01126     register char c;
01127     int open = 0;
01128 
01129     while ((c = *p++) != 0) {
01130         switch (c) {
01131           case '[':
01132             open = 1;
01133             continue;
01134           case ']':
01135             open = 0;
01136             continue;
01137 
01138           case '/':
01139             if (!open)
01140                 return (char *)p-1;
01141             continue;
01142 
01143           case '\\':
01144             if (escape && !(c = *p++))
01145                 return (char *)p-1;
01146             continue;
01147         }
01148 
01149         p = Next(p-1, pend, enc);
01150     }
01151 
01152     return (char *)p-1;
01153 }
01154 
01155 /* Remove escaping backslashes */
01156 static char *
01157 remove_backslashes(char *p, register const char *pend, rb_encoding *enc)
01158 {
01159     char *t = p;
01160     char *s = p;
01161 
01162     while (*p) {
01163         if (*p == '\\') {
01164             if (t != s)
01165                 memmove(t, s, p - s);
01166             t += p - s;
01167             s = ++p;
01168             if (!*p) break;
01169         }
01170         Inc(p, pend, enc);
01171     }
01172 
01173     while (*p++);
01174 
01175     if (t != s)
01176         memmove(t, s, p - s); /* move '\0' too */
01177 
01178     return p;
01179 }
01180 
01181 /* Globing pattern */
01182 enum glob_pattern_type { PLAIN, MAGICAL, RECURSIVE, MATCH_ALL, MATCH_DIR };
01183 
01184 struct glob_pattern {
01185     char *str;
01186     enum glob_pattern_type type;
01187     struct glob_pattern *next;
01188 };
01189 
01190 static void glob_free_pattern(struct glob_pattern *list);
01191 
01192 static struct glob_pattern *
01193 glob_make_pattern(const char *p, const char *e, int flags, rb_encoding *enc)
01194 {
01195     struct glob_pattern *list, *tmp, **tail = &list;
01196     int dirsep = 0; /* pattern is terminated with '/' */
01197     int recursive = 0;
01198 
01199     while (p < e && *p) {
01200         tmp = GLOB_ALLOC(struct glob_pattern);
01201         if (!tmp) goto error;
01202         if (p[0] == '*' && p[1] == '*' && p[2] == '/') {
01203             /* fold continuous RECURSIVEs (needed in glob_helper) */
01204             do { p += 3; while (*p == '/') p++; } while (p[0] == '*' && p[1] == '*' && p[2] == '/');
01205             tmp->type = RECURSIVE;
01206             tmp->str = 0;
01207             dirsep = 1;
01208             recursive = 1;
01209         }
01210         else {
01211             const char *m = find_dirsep(p, e, flags, enc);
01212             int magic = has_magic(p, m, flags, enc);
01213             char *buf;
01214 
01215             if (!magic && !recursive && *m) {
01216                 const char *m2;
01217                 while (!has_magic(m+1, m2 = find_dirsep(m+1, e, flags, enc), flags, enc) &&
01218                        *m2) {
01219                     m = m2;
01220                 }
01221             }
01222             buf = GLOB_ALLOC_N(char, m-p+1);
01223             if (!buf) {
01224                 GLOB_FREE(tmp);
01225                 goto error;
01226             }
01227             memcpy(buf, p, m-p);
01228             buf[m-p] = '\0';
01229             tmp->type = magic ? MAGICAL : PLAIN;
01230             tmp->str = buf;
01231             if (*m) {
01232                 dirsep = 1;
01233                 p = m + 1;
01234             }
01235             else {
01236                 dirsep = 0;
01237                 p = m;
01238             }
01239         }
01240         *tail = tmp;
01241         tail = &tmp->next;
01242     }
01243 
01244     tmp = GLOB_ALLOC(struct glob_pattern);
01245     if (!tmp) {
01246       error:
01247         *tail = 0;
01248         glob_free_pattern(list);
01249         return 0;
01250     }
01251     tmp->type = dirsep ? MATCH_DIR : MATCH_ALL;
01252     tmp->str = 0;
01253     *tail = tmp;
01254     tmp->next = 0;
01255 
01256     return list;
01257 }
01258 
01259 static void
01260 glob_free_pattern(struct glob_pattern *list)
01261 {
01262     while (list) {
01263         struct glob_pattern *tmp = list;
01264         list = list->next;
01265         if (tmp->str)
01266             GLOB_FREE(tmp->str);
01267         GLOB_FREE(tmp);
01268     }
01269 }
01270 
01271 static char *
01272 join_path(const char *path, long len, int dirsep, const char *name, size_t namlen)
01273 {
01274     char *buf = GLOB_ALLOC_N(char, len+namlen+(dirsep?1:0)+1);
01275 
01276     if (!buf) return 0;
01277     memcpy(buf, path, len);
01278     if (dirsep) {
01279         buf[len++] = '/';
01280     }
01281     memcpy(buf+len, name, namlen);
01282     buf[len+namlen] = '\0';
01283     return buf;
01284 }
01285 
01286 enum answer {UNKNOWN = -1, NO, YES};
01287 
01288 #ifndef S_ISDIR
01289 #   define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
01290 #endif
01291 
01292 #ifndef S_ISLNK
01293 #  ifndef S_IFLNK
01294 #    define S_ISLNK(m) (0)
01295 #  else
01296 #    define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
01297 #  endif
01298 #endif
01299 
01300 struct glob_args {
01301     void (*func)(const char *, VALUE, void *);
01302     const char *path;
01303     VALUE value;
01304     rb_encoding *enc;
01305 };
01306 
01307 static VALUE
01308 glob_func_caller(VALUE val)
01309 {
01310     struct glob_args *args = (struct glob_args *)val;
01311 
01312     (*args->func)(args->path, args->value, args->enc);
01313     return Qnil;
01314 }
01315 
01316 #define glob_call_func(func, path, arg, enc) (*(func))((path), (arg), (enc))
01317 
01318 static int
01319 glob_helper(
01320     const char *path,
01321     int dirsep, /* '/' should be placed before appending child entry's name to 'path'. */
01322     enum answer exist, /* Does 'path' indicate an existing entry? */
01323     enum answer isdir, /* Does 'path' indicate a directory or a symlink to a directory? */
01324     struct glob_pattern **beg,
01325     struct glob_pattern **end,
01326     int flags,
01327     ruby_glob_func *func,
01328     VALUE arg,
01329     rb_encoding *enc)
01330 {
01331     struct stat st;
01332     int status = 0;
01333     struct glob_pattern **cur, **new_beg, **new_end;
01334     int plain = 0, magical = 0, recursive = 0, match_all = 0, match_dir = 0;
01335     int escape = !(flags & FNM_NOESCAPE);
01336     long pathlen;
01337 
01338     for (cur = beg; cur < end; ++cur) {
01339         struct glob_pattern *p = *cur;
01340         if (p->type == RECURSIVE) {
01341             recursive = 1;
01342             p = p->next;
01343         }
01344         switch (p->type) {
01345           case PLAIN:
01346             plain = 1;
01347             break;
01348           case MAGICAL:
01349             magical = 1;
01350             break;
01351           case MATCH_ALL:
01352             match_all = 1;
01353             break;
01354           case MATCH_DIR:
01355             match_dir = 1;
01356             break;
01357           case RECURSIVE:
01358             rb_bug("continuous RECURSIVEs");
01359         }
01360     }
01361 
01362     pathlen = strlen(path);
01363     if (*path) {
01364         if (match_all && exist == UNKNOWN) {
01365             if (do_lstat(path, &st, flags) == 0) {
01366                 exist = YES;
01367                 isdir = S_ISDIR(st.st_mode) ? YES : S_ISLNK(st.st_mode) ? UNKNOWN : NO;
01368             }
01369             else {
01370                 exist = NO;
01371                 isdir = NO;
01372             }
01373         }
01374         if (match_dir && isdir == UNKNOWN) {
01375             if (do_stat(path, &st, flags) == 0) {
01376                 exist = YES;
01377                 isdir = S_ISDIR(st.st_mode) ? YES : NO;
01378             }
01379             else {
01380                 exist = NO;
01381                 isdir = NO;
01382             }
01383         }
01384         if (match_all && exist == YES) {
01385             status = glob_call_func(func, path, arg, enc);
01386             if (status) return status;
01387         }
01388         if (match_dir && isdir == YES) {
01389             char *tmp = join_path(path, pathlen, dirsep, "", 0);
01390             if (!tmp) return -1;
01391             status = glob_call_func(func, tmp, arg, enc);
01392             GLOB_FREE(tmp);
01393             if (status) return status;
01394         }
01395     }
01396 
01397     if (exist == NO || isdir == NO) return 0;
01398 
01399     if (magical || recursive) {
01400         struct dirent *dp;
01401         DIR *dirp;
01402         IF_HAVE_HFS(int hfs_p);
01403         dirp = do_opendir(*path ? path : ".", flags, enc);
01404         if (dirp == NULL) return 0;
01405         IF_HAVE_HFS(hfs_p = is_hfs(dirp));
01406 
01407         while ((dp = READDIR(dirp, enc)) != NULL) {
01408             char *buf;
01409             enum answer new_isdir = UNKNOWN;
01410             const char *name;
01411             size_t namlen;
01412             int dotfile = 0;
01413             IF_HAVE_HFS(VALUE utf8str = Qnil);
01414 
01415             if (recursive && dp->d_name[0] == '.') {
01416                 ++dotfile;
01417                 if (!dp->d_name[1]) {
01418                     /* unless DOTMATCH, skip current directories not to recurse infinitely */
01419                     if (!(flags & FNM_DOTMATCH)) continue;
01420                     ++dotfile;
01421                 }
01422                 else if (dp->d_name[1] == '.' && !dp->d_name[2]) {
01423                     /* always skip parent directories not to recurse infinitely */
01424                     continue;
01425                 }
01426             }
01427 
01428             name = dp->d_name;
01429             namlen = NAMLEN(dp);
01430 # if HAVE_HFS
01431             if (hfs_p && has_nonascii(name, namlen)) {
01432                 if (!NIL_P(utf8str = rb_str_normalize_ospath(name, namlen))) {
01433                     RSTRING_GETMEM(utf8str, name, namlen);
01434                 }
01435             }
01436 # endif
01437             buf = join_path(path, pathlen, dirsep, name, namlen);
01438             IF_HAVE_HFS(if (!NIL_P(utf8str)) rb_str_resize(utf8str, 0));
01439             if (!buf) {
01440                 status = -1;
01441                 break;
01442             }
01443             name = buf + pathlen + (dirsep != 0);
01444             if (recursive && dotfile < ((flags & FNM_DOTMATCH) ? 2 : 1)) {
01445                 /* RECURSIVE never match dot files unless FNM_DOTMATCH is set */
01446 #ifndef _WIN32
01447                 if (do_lstat(buf, &st, flags) == 0)
01448                     new_isdir = S_ISDIR(st.st_mode) ? YES : S_ISLNK(st.st_mode) ? UNKNOWN : NO;
01449                 else
01450                     new_isdir = NO;
01451 #else
01452                 new_isdir = dp->d_isdir ? (!dp->d_isrep ? YES : UNKNOWN) : NO;
01453 #endif
01454             }
01455 
01456             new_beg = new_end = GLOB_ALLOC_N(struct glob_pattern *, (end - beg) * 2);
01457             if (!new_beg) {
01458                 GLOB_FREE(buf);
01459                 status = -1;
01460                 break;
01461             }
01462 
01463             for (cur = beg; cur < end; ++cur) {
01464                 struct glob_pattern *p = *cur;
01465                 if (p->type == RECURSIVE) {
01466                     if (new_isdir == YES) /* not symlink but real directory */
01467                         *new_end++ = p; /* append recursive pattern */
01468                     p = p->next; /* 0 times recursion */
01469                 }
01470                 if (p->type == PLAIN || p->type == MAGICAL) {
01471                     if (fnmatch(p->str, enc, name, flags) == 0)
01472                         *new_end++ = p->next;
01473                 }
01474             }
01475 
01476             status = glob_helper(buf, 1, YES, new_isdir, new_beg, new_end,
01477                                  flags, func, arg, enc);
01478             GLOB_FREE(buf);
01479             GLOB_FREE(new_beg);
01480             if (status) break;
01481         }
01482 
01483         closedir(dirp);
01484     }
01485     else if (plain) {
01486         struct glob_pattern **copy_beg, **copy_end, **cur2;
01487 
01488         copy_beg = copy_end = GLOB_ALLOC_N(struct glob_pattern *, end - beg);
01489         if (!copy_beg) return -1;
01490         for (cur = beg; cur < end; ++cur)
01491             *copy_end++ = (*cur)->type == PLAIN ? *cur : 0;
01492 
01493         for (cur = copy_beg; cur < copy_end; ++cur) {
01494             if (*cur) {
01495                 char *buf;
01496                 char *name;
01497                 size_t len = strlen((*cur)->str) + 1;
01498                 name = GLOB_ALLOC_N(char, len);
01499                 if (!name) {
01500                     status = -1;
01501                     break;
01502                 }
01503                 memcpy(name, (*cur)->str, len);
01504                 if (escape)
01505                     len = remove_backslashes(name, name+len-1, enc) - name;
01506 
01507                 new_beg = new_end = GLOB_ALLOC_N(struct glob_pattern *, end - beg);
01508                 if (!new_beg) {
01509                     GLOB_FREE(name);
01510                     status = -1;
01511                     break;
01512                 }
01513                 *new_end++ = (*cur)->next;
01514                 for (cur2 = cur + 1; cur2 < copy_end; ++cur2) {
01515                     if (*cur2 && fnmatch((*cur2)->str, enc, name, flags) == 0) {
01516                         *new_end++ = (*cur2)->next;
01517                         *cur2 = 0;
01518                     }
01519                 }
01520 
01521                 buf = join_path(path, pathlen, dirsep, name, len);
01522                 GLOB_FREE(name);
01523                 if (!buf) {
01524                     GLOB_FREE(new_beg);
01525                     status = -1;
01526                     break;
01527                 }
01528                 status = glob_helper(buf, 1, UNKNOWN, UNKNOWN, new_beg,
01529                                      new_end, flags, func, arg, enc);
01530                 GLOB_FREE(buf);
01531                 GLOB_FREE(new_beg);
01532                 if (status) break;
01533             }
01534         }
01535 
01536         GLOB_FREE(copy_beg);
01537     }
01538 
01539     return status;
01540 }
01541 
01542 static int
01543 ruby_glob0(const char *path, int flags, ruby_glob_func *func, VALUE arg, rb_encoding *enc)
01544 {
01545     struct glob_pattern *list;
01546     const char *root, *start;
01547     char *buf;
01548     size_t n;
01549     int status;
01550 
01551     start = root = path;
01552     flags |= FNM_SYSCASE;
01553 #if defined DOSISH
01554     root = rb_enc_path_skip_prefix(root, root + strlen(root), enc);
01555 #endif
01556 
01557     if (root && *root == '/') root++;
01558 
01559     n = root - start;
01560     buf = GLOB_ALLOC_N(char, n + 1);
01561     if (!buf) return -1;
01562     MEMCPY(buf, start, char, n);
01563     buf[n] = '\0';
01564 
01565     list = glob_make_pattern(root, root + strlen(root), flags, enc);
01566     if (!list) {
01567         GLOB_FREE(buf);
01568         return -1;
01569     }
01570     status = glob_helper(buf, 0, UNKNOWN, UNKNOWN, &list, &list + 1, flags, func, arg, enc);
01571     glob_free_pattern(list);
01572     GLOB_FREE(buf);
01573 
01574     return status;
01575 }
01576 
01577 int
01578 ruby_glob(const char *path, int flags, ruby_glob_func *func, VALUE arg)
01579 {
01580     return ruby_glob0(path, flags & ~GLOB_VERBOSE, func, arg,
01581                       rb_ascii8bit_encoding());
01582 }
01583 
01584 static int
01585 rb_glob_caller(const char *path, VALUE a, void *enc)
01586 {
01587     int status;
01588     struct glob_args *args = (struct glob_args *)a;
01589 
01590     args->path = path;
01591     rb_protect(glob_func_caller, a, &status);
01592     return status;
01593 }
01594 
01595 static int
01596 rb_glob2(const char *path, int flags,
01597          void (*func)(const char *, VALUE, void *), VALUE arg,
01598          rb_encoding* enc)
01599 {
01600     struct glob_args args;
01601 
01602     args.func = func;
01603     args.value = arg;
01604     args.enc = enc;
01605 
01606     if (flags & FNM_SYSCASE) {
01607         rb_warning("Dir.glob() ignores File::FNM_CASEFOLD");
01608     }
01609 
01610     return ruby_glob0(path, flags | GLOB_VERBOSE, rb_glob_caller, (VALUE)&args,
01611                       enc);
01612 }
01613 
01614 void
01615 rb_glob(const char *path, void (*func)(const char *, VALUE, void *), VALUE arg)
01616 {
01617     int status = rb_glob2(path, 0, func, arg, rb_ascii8bit_encoding());
01618     if (status) GLOB_JUMP_TAG(status);
01619 }
01620 
01621 static void
01622 push_pattern(const char *path, VALUE ary, void *enc)
01623 {
01624     rb_ary_push(ary, rb_external_str_new_with_enc(path, strlen(path), enc));
01625 }
01626 
01627 static int
01628 ruby_brace_expand(const char *str, int flags, ruby_glob_func *func, VALUE arg,
01629                   rb_encoding *enc)
01630 {
01631     const int escape = !(flags & FNM_NOESCAPE);
01632     const char *p = str;
01633     const char *pend = p + strlen(p);
01634     const char *s = p;
01635     const char *lbrace = 0, *rbrace = 0;
01636     int nest = 0, status = 0;
01637 
01638     while (*p) {
01639         if (*p == '{' && nest++ == 0) {
01640             lbrace = p;
01641         }
01642         if (*p == '}' && --nest <= 0) {
01643             rbrace = p;
01644             break;
01645         }
01646         if (*p == '\\' && escape) {
01647             if (!*++p) break;
01648         }
01649         Inc(p, pend, enc);
01650     }
01651 
01652     if (lbrace && rbrace) {
01653         size_t len = strlen(s) + 1;
01654         char *buf = GLOB_ALLOC_N(char, len);
01655         long shift;
01656 
01657         if (!buf) return -1;
01658         memcpy(buf, s, lbrace-s);
01659         shift = (lbrace-s);
01660         p = lbrace;
01661         while (p < rbrace) {
01662             const char *t = ++p;
01663             nest = 0;
01664             while (p < rbrace && !(*p == ',' && nest == 0)) {
01665                 if (*p == '{') nest++;
01666                 if (*p == '}') nest--;
01667                 if (*p == '\\' && escape) {
01668                     if (++p == rbrace) break;
01669                 }
01670                 Inc(p, pend, enc);
01671             }
01672             memcpy(buf+shift, t, p-t);
01673             strlcpy(buf+shift+(p-t), rbrace+1, len-(shift+(p-t)));
01674             status = ruby_brace_expand(buf, flags, func, arg, enc);
01675             if (status) break;
01676         }
01677         GLOB_FREE(buf);
01678     }
01679     else if (!lbrace && !rbrace) {
01680         status = (*func)(s, arg, enc);
01681     }
01682 
01683     return status;
01684 }
01685 
01686 struct brace_args {
01687     ruby_glob_func *func;
01688     VALUE value;
01689     int flags;
01690 };
01691 
01692 static int
01693 glob_brace(const char *path, VALUE val, void *enc)
01694 {
01695     struct brace_args *arg = (struct brace_args *)val;
01696 
01697     return ruby_glob0(path, arg->flags, arg->func, arg->value, enc);
01698 }
01699 
01700 static int
01701 ruby_brace_glob0(const char *str, int flags, ruby_glob_func *func, VALUE arg,
01702                  rb_encoding* enc)
01703 {
01704     struct brace_args args;
01705 
01706     args.func = func;
01707     args.value = arg;
01708     args.flags = flags;
01709     return ruby_brace_expand(str, flags, glob_brace, (VALUE)&args, enc);
01710 }
01711 
01712 int
01713 ruby_brace_glob(const char *str, int flags, ruby_glob_func *func, VALUE arg)
01714 {
01715     return ruby_brace_glob0(str, flags & ~GLOB_VERBOSE, func, arg,
01716                             rb_ascii8bit_encoding());
01717 }
01718 
01719 int
01720 ruby_brace_glob_with_enc(const char *str, int flags, ruby_glob_func *func, VALUE arg, rb_encoding *enc)
01721 {
01722     return ruby_brace_glob0(str, flags & ~GLOB_VERBOSE, func, arg, enc);
01723 }
01724 
01725 static int
01726 push_glob(VALUE ary, VALUE str, int flags)
01727 {
01728     struct glob_args args;
01729     rb_encoding *enc = rb_enc_get(str);
01730 
01731     if (enc == rb_usascii_encoding()) enc = rb_filesystem_encoding();
01732     args.func = push_pattern;
01733     args.value = ary;
01734     args.enc = enc;
01735 
01736     RB_GC_GUARD(str);
01737     return ruby_brace_glob0(RSTRING_PTR(str), flags | GLOB_VERBOSE,
01738                             rb_glob_caller, (VALUE)&args, enc);
01739 }
01740 
01741 static VALUE
01742 rb_push_glob(VALUE str, int flags) /* '\0' is delimiter */
01743 {
01744     long offset = 0;
01745     VALUE ary;
01746 
01747     GlobPathValue(str, TRUE);
01748     ary = rb_ary_new();
01749 
01750     while (offset < RSTRING_LEN(str)) {
01751         char *p, *pend;
01752         int status;
01753         p = RSTRING_PTR(str) + offset;
01754         status = push_glob(ary, rb_enc_str_new(p, strlen(p), rb_enc_get(str)),
01755                            flags);
01756         if (status) GLOB_JUMP_TAG(status);
01757         if (offset >= RSTRING_LEN(str)) break;
01758         p += strlen(p) + 1;
01759         pend = RSTRING_PTR(str) + RSTRING_LEN(str);
01760         while (p < pend && !*p)
01761             p++;
01762         offset = p - RSTRING_PTR(str);
01763     }
01764 
01765     return ary;
01766 }
01767 
01768 static VALUE
01769 dir_globs(long argc, const VALUE *argv, int flags)
01770 {
01771     VALUE ary = rb_ary_new();
01772     long i;
01773 
01774     for (i = 0; i < argc; ++i) {
01775         int status;
01776         VALUE str = argv[i];
01777         GlobPathValue(str, TRUE);
01778         status = push_glob(ary, str, flags);
01779         if (status) GLOB_JUMP_TAG(status);
01780     }
01781 
01782     return ary;
01783 }
01784 
01785 /*
01786  *  call-seq:
01787  *     Dir[ string [, string ...] ] -> array
01788  *
01789  *  Equivalent to calling
01790  *  <code>Dir.glob([</code><i>string,...</i><code>],0)</code>.
01791  *
01792  */
01793 static VALUE
01794 dir_s_aref(int argc, VALUE *argv, VALUE obj)
01795 {
01796     if (argc == 1) {
01797         return rb_push_glob(argv[0], 0);
01798     }
01799     return dir_globs(argc, argv, 0);
01800 }
01801 
01802 /*
01803  *  call-seq:
01804  *     Dir.glob( pattern, [flags] ) -> matches
01805  *     Dir.glob( pattern, [flags] ) { |filename| block }  -> nil
01806  *
01807  *  Expands +pattern+, which is an Array of patterns or a pattern String, and
01808  *  returns the results as +matches+ or as arguments given to the block.
01809  *
01810  *  Note that this pattern is not a regexp, it's closer to a shell glob.  See
01811  *  File::fnmatch for the meaning of the +flags+ parameter.  Note that case
01812  *  sensitivity depends on your system (so File::FNM_CASEFOLD is ignored), as
01813  *  does the order in which the results are returned.
01814  *
01815  *  <code>*</code>::
01816  *    Matches any file. Can be restricted by other values in the glob.
01817  *    Equivalent to <code>/ .* /x</code> in regexp.
01818  *
01819  *    <code>*</code>::     Matches all files
01820  *    <code>c*</code>::    Matches all files beginning with <code>c</code>
01821  *    <code>*c</code>::    Matches all files ending with <code>c</code>
01822  *    <code>\*c\*</code>:: Match all files that have <code>c</code> in them
01823  *                         (including at the beginning or end).
01824  *
01825  *    Note, this will not match Unix-like hidden files (dotfiles).  In order
01826  *    to include those in the match results, you must use the
01827  *    File::FNM_DOTMATCH flag or something like <code>"{*,.*}"</code>.
01828  *
01829  *  <code>**</code>::
01830  *    Matches directories recursively.
01831  *
01832  *  <code>?</code>::
01833  *    Matches any one character. Equivalent to <code>/.{1}/</code> in regexp.
01834  *
01835  *  <code>[set]</code>::
01836  *    Matches any one character in +set+.  Behaves exactly like character sets
01837  *    in Regexp, including set negation (<code>[^a-z]</code>).
01838  *
01839  *  <code>{p,q}</code>::
01840  *    Matches either literal <code>p</code> or literal <code>q</code>.
01841  *    Equivalent to pattern alternation in regexp.
01842  *
01843  *    Matching literals may be more than one character in length.  More than
01844  *    two literals may be specified.
01845  *
01846  *  <code> \\ </code>::
01847  *    Escapes the next metacharacter.
01848  *
01849  *    Note that this means you cannot use backslash on windows as part of a
01850  *    glob, i.e.  <code>Dir["c:\\foo*"]</code> will not work, use
01851  *    <code>Dir["c:/foo*"]</code> instead.
01852  *
01853  *  Examples:
01854  *
01855  *     Dir["config.?"]                     #=> ["config.h"]
01856  *     Dir.glob("config.?")                #=> ["config.h"]
01857  *     Dir.glob("*.[a-z][a-z]")            #=> ["main.rb"]
01858  *     Dir.glob("*.[^r]*")                 #=> ["config.h"]
01859  *     Dir.glob("*.{rb,h}")                #=> ["main.rb", "config.h"]
01860  *     Dir.glob("*")                       #=> ["config.h", "main.rb"]
01861  *     Dir.glob("*", File::FNM_DOTMATCH)   #=> [".", "..", "config.h", "main.rb"]
01862  *
01863  *     rbfiles = File.join("**", "*.rb")
01864  *     Dir.glob(rbfiles)                   #=> ["main.rb",
01865  *                                         #    "lib/song.rb",
01866  *                                         #    "lib/song/karaoke.rb"]
01867  *     libdirs = File.join("**", "lib")
01868  *     Dir.glob(libdirs)                   #=> ["lib"]
01869  *
01870  *     librbfiles = File.join("**", "lib", "**", "*.rb")
01871  *     Dir.glob(librbfiles)                #=> ["lib/song.rb",
01872  *                                         #    "lib/song/karaoke.rb"]
01873  *
01874  *     librbfiles = File.join("**", "lib", "*.rb")
01875  *     Dir.glob(librbfiles)                #=> ["lib/song.rb"]
01876  */
01877 static VALUE
01878 dir_s_glob(int argc, VALUE *argv, VALUE obj)
01879 {
01880     VALUE str, rflags, ary;
01881     int flags;
01882 
01883     if (rb_scan_args(argc, argv, "11", &str, &rflags) == 2)
01884         flags = NUM2INT(rflags);
01885     else
01886         flags = 0;
01887 
01888     ary = rb_check_array_type(str);
01889     if (NIL_P(ary)) {
01890         ary = rb_push_glob(str, flags);
01891     }
01892     else {
01893         volatile VALUE v = ary;
01894         ary = dir_globs(RARRAY_LEN(v), RARRAY_CONST_PTR(v), flags);
01895     }
01896 
01897     if (rb_block_given_p()) {
01898         rb_ary_each(ary);
01899         return Qnil;
01900     }
01901     return ary;
01902 }
01903 
01904 static VALUE
01905 dir_open_dir(int argc, VALUE *argv)
01906 {
01907     VALUE dir = rb_funcall2(rb_cDir, rb_intern("open"), argc, argv);
01908 
01909     rb_check_typeddata(dir, &dir_data_type);
01910     return dir;
01911 }
01912 
01913 
01914 /*
01915  *  call-seq:
01916  *     Dir.foreach( dirname ) {| filename | block }                 -> nil
01917  *     Dir.foreach( dirname, encoding: enc ) {| filename | block }  -> nil
01918  *     Dir.foreach( dirname )                                       -> an_enumerator
01919  *     Dir.foreach( dirname, encoding: enc )                        -> an_enumerator
01920  *
01921  *  Calls the block once for each entry in the named directory, passing
01922  *  the filename of each entry as a parameter to the block.
01923  *
01924  *  If no block is given, an enumerator is returned instead.
01925  *
01926  *     Dir.foreach("testdir") {|x| puts "Got #{x}" }
01927  *
01928  *  <em>produces:</em>
01929  *
01930  *     Got .
01931  *     Got ..
01932  *     Got config.h
01933  *     Got main.rb
01934  *
01935  */
01936 static VALUE
01937 dir_foreach(int argc, VALUE *argv, VALUE io)
01938 {
01939     VALUE dir;
01940 
01941     RETURN_ENUMERATOR(io, argc, argv);
01942     dir = dir_open_dir(argc, argv);
01943     rb_ensure(dir_each, dir, dir_close, dir);
01944     return Qnil;
01945 }
01946 
01947 /*
01948  *  call-seq:
01949  *     Dir.entries( dirname )                -> array
01950  *     Dir.entries( dirname, encoding: enc ) -> array
01951  *
01952  *  Returns an array containing all of the filenames in the given
01953  *  directory. Will raise a <code>SystemCallError</code> if the named
01954  *  directory doesn't exist.
01955  *
01956  *  The optional <i>enc</i> argument specifies the encoding of the directory.
01957  *  If not specified, the filesystem encoding is used.
01958  *
01959  *     Dir.entries("testdir")   #=> [".", "..", "config.h", "main.rb"]
01960  *
01961  */
01962 static VALUE
01963 dir_entries(int argc, VALUE *argv, VALUE io)
01964 {
01965     VALUE dir;
01966 
01967     dir = dir_open_dir(argc, argv);
01968     return rb_ensure(rb_Array, dir, dir_close, dir);
01969 }
01970 
01971 static int
01972 fnmatch_brace(const char *pattern, VALUE val, void *enc)
01973 {
01974     struct brace_args *arg = (struct brace_args *)val;
01975     VALUE path = arg->value;
01976     rb_encoding *enc_pattern = enc;
01977     rb_encoding *enc_path = rb_enc_get(path);
01978 
01979     if (enc_pattern != enc_path) {
01980         if (!rb_enc_asciicompat(enc_pattern))
01981             return FNM_NOMATCH;
01982         if (!rb_enc_asciicompat(enc_path))
01983             return FNM_NOMATCH;
01984         if (!rb_enc_str_asciionly_p(path)) {
01985             int cr = ENC_CODERANGE_7BIT;
01986             long len = strlen(pattern);
01987             if (rb_str_coderange_scan_restartable(pattern, pattern + len,
01988                                                   enc_pattern, &cr) != len)
01989                 return FNM_NOMATCH;
01990             if (cr != ENC_CODERANGE_7BIT)
01991                 return FNM_NOMATCH;
01992         }
01993     }
01994     return (fnmatch(pattern, enc, RSTRING_PTR(path), arg->flags) == 0);
01995 }
01996 
01997 /*
01998  *  call-seq:
01999  *     File.fnmatch( pattern, path, [flags] ) -> (true or false)
02000  *     File.fnmatch?( pattern, path, [flags] ) -> (true or false)
02001  *
02002  *  Returns true if +path+ matches against +pattern+.  The pattern is not a
02003  *  regular expression; instead it follows rules similar to shell filename
02004  *  globbing.  It may contain the following metacharacters:
02005  *
02006  *  <code>*</code>::
02007  *    Matches any file. Can be restricted by other values in the glob.
02008  *    Equivalent to <code>/ .* /x</code> in regexp.
02009  *
02010  *    <code>*</code>::    Matches all files regular files
02011  *    <code>c*</code>::   Matches all files beginning with <code>c</code>
02012  *    <code>*c</code>::   Matches all files ending with <code>c</code>
02013  *    <code>\*c*</code>:: Matches all files that have <code>c</code> in them
02014  *                        (including at the beginning or end).
02015  *
02016  *    To match hidden files (that start with a <code>.</code> set the
02017  *    File::FNM_DOTMATCH flag.
02018  *
02019  *  <code>**</code>::
02020  *    Matches directories recursively or files expansively.
02021  *
02022  *  <code>?</code>::
02023  *    Matches any one character. Equivalent to <code>/.{1}/</code> in regexp.
02024  *
02025  *  <code>[set]</code>::
02026  *    Matches any one character in +set+.  Behaves exactly like character sets
02027  *    in Regexp, including set negation (<code>[^a-z]</code>).
02028  *
02029  *  <code> \ </code>::
02030  *    Escapes the next metacharacter.
02031  *
02032  *  <code>{a,b}</code>::
02033  *    Matches pattern a and pattern b if File::FNM_EXTGLOB flag is enabled.
02034  *    Behaves like a Regexp union (<code>(?:a|b)</code>).
02035  *
02036  *  +flags+ is a bitwise OR of the <code>FNM_XXX</code> constants. The same
02037  *  glob pattern and flags are used by Dir::glob.
02038  *
02039  *  Examples:
02040  *
02041  *     File.fnmatch('cat',       'cat')        #=> true  # match entire string
02042  *     File.fnmatch('cat',       'category')   #=> false # only match partial string
02043  *
02044  *     File.fnmatch('c{at,ub}s', 'cats')                    #=> false # { } isn't supported by default
02045  *     File.fnmatch('c{at,ub}s', 'cats', File::FNM_EXTGLOB) #=> true  # { } is supported on FNM_EXTGLOB
02046  *
02047  *     File.fnmatch('c?t',     'cat')          #=> true  # '?' match only 1 character
02048  *     File.fnmatch('c??t',    'cat')          #=> false # ditto
02049  *     File.fnmatch('c*',      'cats')         #=> true  # '*' match 0 or more characters
02050  *     File.fnmatch('c*t',     'c/a/b/t')      #=> true  # ditto
02051  *     File.fnmatch('ca[a-z]', 'cat')          #=> true  # inclusive bracket expression
02052  *     File.fnmatch('ca[^t]',  'cat')          #=> false # exclusive bracket expression ('^' or '!')
02053  *
02054  *     File.fnmatch('cat', 'CAT')                     #=> false # case sensitive
02055  *     File.fnmatch('cat', 'CAT', File::FNM_CASEFOLD) #=> true  # case insensitive
02056  *
02057  *     File.fnmatch('?',   '/', File::FNM_PATHNAME)  #=> false # wildcard doesn't match '/' on FNM_PATHNAME
02058  *     File.fnmatch('*',   '/', File::FNM_PATHNAME)  #=> false # ditto
02059  *     File.fnmatch('[/]', '/', File::FNM_PATHNAME)  #=> false # ditto
02060  *
02061  *     File.fnmatch('\?',   '?')                       #=> true  # escaped wildcard becomes ordinary
02062  *     File.fnmatch('\a',   'a')                       #=> true  # escaped ordinary remains ordinary
02063  *     File.fnmatch('\a',   '\a', File::FNM_NOESCAPE)  #=> true  # FNM_NOESCAPE makes '\' ordinary
02064  *     File.fnmatch('[\?]', '?')                       #=> true  # can escape inside bracket expression
02065  *
02066  *     File.fnmatch('*',   '.profile')                      #=> false # wildcard doesn't match leading
02067  *     File.fnmatch('*',   '.profile', File::FNM_DOTMATCH)  #=> true  # period by default.
02068  *     File.fnmatch('.*',  '.profile')                      #=> true
02069  *
02070  *     rbfiles = '**' '/' '*.rb' # you don't have to do like this. just write in single string.
02071  *     File.fnmatch(rbfiles, 'main.rb')                    #=> false
02072  *     File.fnmatch(rbfiles, './main.rb')                  #=> false
02073  *     File.fnmatch(rbfiles, 'lib/song.rb')                #=> true
02074  *     File.fnmatch('**.rb', 'main.rb')                    #=> true
02075  *     File.fnmatch('**.rb', './main.rb')                  #=> false
02076  *     File.fnmatch('**.rb', 'lib/song.rb')                #=> true
02077  *     File.fnmatch('*',           'dave/.profile')                      #=> true
02078  *
02079  *     pattern = '*' '/' '*'
02080  *     File.fnmatch(pattern, 'dave/.profile', File::FNM_PATHNAME)  #=> false
02081  *     File.fnmatch(pattern, 'dave/.profile', File::FNM_PATHNAME | File::FNM_DOTMATCH) #=> true
02082  *
02083  *     pattern = '**' '/' 'foo'
02084  *     File.fnmatch(pattern, 'a/b/c/foo', File::FNM_PATHNAME)     #=> true
02085  *     File.fnmatch(pattern, '/a/b/c/foo', File::FNM_PATHNAME)    #=> true
02086  *     File.fnmatch(pattern, 'c:/a/b/c/foo', File::FNM_PATHNAME)  #=> true
02087  *     File.fnmatch(pattern, 'a/.b/c/foo', File::FNM_PATHNAME)    #=> false
02088  *     File.fnmatch(pattern, 'a/.b/c/foo', File::FNM_PATHNAME | File::FNM_DOTMATCH) #=> true
02089  */
02090 static VALUE
02091 file_s_fnmatch(int argc, VALUE *argv, VALUE obj)
02092 {
02093     VALUE pattern, path;
02094     VALUE rflags;
02095     int flags;
02096 
02097     if (rb_scan_args(argc, argv, "21", &pattern, &path, &rflags) == 3)
02098         flags = NUM2INT(rflags);
02099     else
02100         flags = 0;
02101 
02102     StringValue(pattern);
02103     FilePathStringValue(path);
02104 
02105     if (flags & FNM_EXTGLOB) {
02106         struct brace_args args;
02107 
02108         args.value = path;
02109         args.flags = flags;
02110         if (ruby_brace_expand(RSTRING_PTR(pattern), flags, fnmatch_brace,
02111                               (VALUE)&args, rb_enc_get(pattern)) > 0)
02112             return Qtrue;
02113     }
02114     else {
02115         rb_encoding *enc = rb_enc_compatible(pattern, path);
02116         if (!enc) return Qfalse;
02117         if (fnmatch(RSTRING_PTR(pattern), enc, RSTRING_PTR(path), flags) == 0)
02118             return Qtrue;
02119     }
02120     RB_GC_GUARD(pattern);
02121 
02122     return Qfalse;
02123 }
02124 
02125 /*
02126  *  call-seq:
02127  *    Dir.home()       -> "/home/me"
02128  *    Dir.home("root") -> "/root"
02129  *
02130  *  Returns the home directory of the current user or the named user
02131  *  if given.
02132  */
02133 static VALUE
02134 dir_s_home(int argc, VALUE *argv, VALUE obj)
02135 {
02136     VALUE user;
02137     const char *u = 0;
02138 
02139     rb_check_arity(argc, 0, 1);
02140     user = (argc > 0) ? argv[0] : Qnil;
02141     if (!NIL_P(user)) {
02142         SafeStringValue(user);
02143         rb_must_asciicompat(user);
02144         u = StringValueCStr(user);
02145         if (*u) {
02146             return rb_home_dir_of(user, rb_str_new(0, 0));
02147         }
02148     }
02149     return rb_default_home_dir(rb_str_new(0, 0));
02150 
02151 }
02152 
02153 #if 0
02154 /*
02155  * call-seq:
02156  *   Dir.exist?(file_name)   ->  true or false
02157  *   Dir.exists?(file_name)   ->  true or false
02158  *
02159  * Returns <code>true</code> if the named file is a directory,
02160  * <code>false</code> otherwise.
02161  *
02162  */
02163 VALUE
02164 rb_file_directory_p()
02165 {
02166 }
02167 #endif
02168 
02169 static VALUE
02170 rb_dir_exists_p(VALUE obj, VALUE fname)
02171 {
02172     rb_warning("Dir.exists? is a deprecated name, use Dir.exist? instead");
02173     return rb_file_directory_p(obj, fname);
02174 }
02175 
02176 /*
02177  *  Objects of class <code>Dir</code> are directory streams representing
02178  *  directories in the underlying file system. They provide a variety of
02179  *  ways to list directories and their contents. See also
02180  *  <code>File</code>.
02181  *
02182  *  The directory used in these examples contains the two regular files
02183  *  (<code>config.h</code> and <code>main.rb</code>), the parent
02184  *  directory (<code>..</code>), and the directory itself
02185  *  (<code>.</code>).
02186  */
02187 void
02188 Init_Dir(void)
02189 {
02190     rb_cDir = rb_define_class("Dir", rb_cObject);
02191 
02192     rb_include_module(rb_cDir, rb_mEnumerable);
02193 
02194     rb_define_alloc_func(rb_cDir, dir_s_alloc);
02195     rb_define_singleton_method(rb_cDir, "open", dir_s_open, -1);
02196     rb_define_singleton_method(rb_cDir, "foreach", dir_foreach, -1);
02197     rb_define_singleton_method(rb_cDir, "entries", dir_entries, -1);
02198 
02199     rb_define_method(rb_cDir,"initialize", dir_initialize, -1);
02200     rb_define_method(rb_cDir,"path", dir_path, 0);
02201     rb_define_method(rb_cDir,"to_path", dir_path, 0);
02202     rb_define_method(rb_cDir,"inspect", dir_inspect, 0);
02203     rb_define_method(rb_cDir,"read", dir_read, 0);
02204     rb_define_method(rb_cDir,"each", dir_each, 0);
02205     rb_define_method(rb_cDir,"rewind", dir_rewind, 0);
02206     rb_define_method(rb_cDir,"tell", dir_tell, 0);
02207     rb_define_method(rb_cDir,"seek", dir_seek, 1);
02208     rb_define_method(rb_cDir,"pos", dir_tell, 0);
02209     rb_define_method(rb_cDir,"pos=", dir_set_pos, 1);
02210     rb_define_method(rb_cDir,"close", dir_close, 0);
02211 
02212     rb_define_singleton_method(rb_cDir,"chdir", dir_s_chdir, -1);
02213     rb_define_singleton_method(rb_cDir,"getwd", dir_s_getwd, 0);
02214     rb_define_singleton_method(rb_cDir,"pwd", dir_s_getwd, 0);
02215     rb_define_singleton_method(rb_cDir,"chroot", dir_s_chroot, 1);
02216     rb_define_singleton_method(rb_cDir,"mkdir", dir_s_mkdir, -1);
02217     rb_define_singleton_method(rb_cDir,"rmdir", dir_s_rmdir, 1);
02218     rb_define_singleton_method(rb_cDir,"delete", dir_s_rmdir, 1);
02219     rb_define_singleton_method(rb_cDir,"unlink", dir_s_rmdir, 1);
02220     rb_define_singleton_method(rb_cDir,"home", dir_s_home, -1);
02221 
02222     rb_define_singleton_method(rb_cDir,"glob", dir_s_glob, -1);
02223     rb_define_singleton_method(rb_cDir,"[]", dir_s_aref, -1);
02224     rb_define_singleton_method(rb_cDir,"exist?", rb_file_directory_p, 1);
02225     rb_define_singleton_method(rb_cDir,"exists?", rb_dir_exists_p, 1);
02226 
02227     rb_define_singleton_method(rb_cFile,"fnmatch", file_s_fnmatch, -1);
02228     rb_define_singleton_method(rb_cFile,"fnmatch?", file_s_fnmatch, -1);
02229 
02230     /*  Document-const: File::Constants::FNM_NOESCAPE
02231      *
02232      *  Disables escapes in File.fnmatch and Dir.glob patterns
02233      */
02234     rb_file_const("FNM_NOESCAPE", INT2FIX(FNM_NOESCAPE));
02235 
02236     /*  Document-const: File::Constants::FNM_PATHNAME
02237      *
02238      *  Wildcards in File.fnmatch and Dir.glob patterns do not match directory
02239      *  separators
02240      */
02241     rb_file_const("FNM_PATHNAME", INT2FIX(FNM_PATHNAME));
02242 
02243     /*  Document-const: File::Constants::FNM_DOTMATCH
02244      *
02245      *  The '*' wildcard matches filenames starting with "." in File.fnmatch
02246      *  and Dir.glob patterns
02247      */
02248     rb_file_const("FNM_DOTMATCH", INT2FIX(FNM_DOTMATCH));
02249 
02250     /*  Document-const: File::Constants::FNM_CASEFOLD
02251      *
02252      *  Makes File.fnmatch patterns case insensitive (but not Dir.glob
02253      *  patterns).
02254      */
02255     rb_file_const("FNM_CASEFOLD", INT2FIX(FNM_CASEFOLD));
02256 
02257     /*  Document-const: File::Constants::FNM_EXTGLOB
02258      *
02259      *  Allows file globbing through "{a,b}" in File.fnmatch patterns.
02260      */
02261     rb_file_const("FNM_EXTGLOB", INT2FIX(FNM_EXTGLOB));
02262     rb_file_const("FNM_SYSCASE", INT2FIX(FNM_SYSCASE));
02263 }
02264 

Generated on 19 Jul 2016 for Ruby by  doxygen 1.4.7