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