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