|
Ruby
1.9.3p286(2012-10-12revision37165)
|
00001 /********************************************************************** 00002 00003 dir.c - 00004 00005 $Author: naruse $ 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 00914 rb_secure(2); 00915 FilePathValue(d); 00916 path = RSTRING_PTR(d); 00917 if (path && *(pend = rb_path_end(rb_path_skip_prefix(path)))) { 00918 d = rb_str_subseq(d, 0, pend - path); 00919 } 00920 *dir = rb_str_encode_ospath(d); 00921 } 00922 00923 #if defined(HAVE_CHROOT) 00924 /* 00925 * call-seq: 00926 * Dir.chroot( string ) -> 0 00927 * 00928 * Changes this process's idea of the file system root. Only a 00929 * privileged process may make this call. Not available on all 00930 * platforms. On Unix systems, see <code>chroot(2)</code> for more 00931 * information. 00932 */ 00933 static VALUE 00934 dir_s_chroot(VALUE dir, VALUE path) 00935 { 00936 check_dirname(&path); 00937 if (chroot(RSTRING_PTR(path)) == -1) 00938 rb_sys_fail_path(path); 00939 00940 return INT2FIX(0); 00941 } 00942 #else 00943 #define dir_s_chroot rb_f_notimplement 00944 #endif 00945 00946 /* 00947 * call-seq: 00948 * Dir.mkdir( string [, integer] ) -> 0 00949 * 00950 * Makes a new directory named by <i>string</i>, with permissions 00951 * specified by the optional parameter <i>anInteger</i>. The 00952 * permissions may be modified by the value of 00953 * <code>File::umask</code>, and are ignored on NT. Raises a 00954 * <code>SystemCallError</code> if the directory cannot be created. See 00955 * also the discussion of permissions in the class documentation for 00956 * <code>File</code>. 00957 * 00958 * Dir.mkdir(File.join(Dir.home, ".foo"), 0700) #=> 0 00959 * 00960 */ 00961 static VALUE 00962 dir_s_mkdir(int argc, VALUE *argv, VALUE obj) 00963 { 00964 VALUE path, vmode; 00965 int mode; 00966 00967 if (rb_scan_args(argc, argv, "11", &path, &vmode) == 2) { 00968 mode = NUM2INT(vmode); 00969 } 00970 else { 00971 mode = 0777; 00972 } 00973 00974 check_dirname(&path); 00975 if (mkdir(RSTRING_PTR(path), mode) == -1) 00976 rb_sys_fail_path(path); 00977 00978 return INT2FIX(0); 00979 } 00980 00981 /* 00982 * call-seq: 00983 * Dir.delete( string ) -> 0 00984 * Dir.rmdir( string ) -> 0 00985 * Dir.unlink( string ) -> 0 00986 * 00987 * Deletes the named directory. Raises a subclass of 00988 * <code>SystemCallError</code> if the directory isn't empty. 00989 */ 00990 static VALUE 00991 dir_s_rmdir(VALUE obj, VALUE dir) 00992 { 00993 check_dirname(&dir); 00994 if (rmdir(RSTRING_PTR(dir)) < 0) 00995 rb_sys_fail_path(dir); 00996 00997 return INT2FIX(0); 00998 } 00999 01000 static VALUE 01001 sys_warning_1(VALUE mesg) 01002 { 01003 rb_sys_warning("%s:%s", strerror(errno), (const char *)mesg); 01004 return Qnil; 01005 } 01006 01007 #define GLOB_VERBOSE (1U << (sizeof(int) * CHAR_BIT - 1)) 01008 #define sys_warning(val) \ 01009 (void)((flags & GLOB_VERBOSE) && rb_protect(sys_warning_1, (VALUE)(val), 0)) 01010 01011 #define GLOB_ALLOC(type) ((type *)malloc(sizeof(type))) 01012 #define GLOB_ALLOC_N(type, n) ((type *)malloc(sizeof(type) * (n))) 01013 #define GLOB_FREE(ptr) free(ptr) 01014 #define GLOB_JUMP_TAG(status) (((status) == -1) ? rb_memerror() : rb_jump_tag(status)) 01015 01016 /* 01017 * ENOTDIR can be returned by stat(2) if a non-leaf element of the path 01018 * is not a directory. 01019 */ 01020 #define to_be_ignored(e) ((e) == ENOENT || (e) == ENOTDIR) 01021 01022 /* System call with warning */ 01023 static int 01024 do_stat(const char *path, struct stat *pst, int flags) 01025 01026 { 01027 int ret = stat(path, pst); 01028 if (ret < 0 && !to_be_ignored(errno)) 01029 sys_warning(path); 01030 01031 return ret; 01032 } 01033 01034 static int 01035 do_lstat(const char *path, struct stat *pst, int flags) 01036 { 01037 int ret = lstat(path, pst); 01038 if (ret < 0 && !to_be_ignored(errno)) 01039 sys_warning(path); 01040 01041 return ret; 01042 } 01043 01044 static DIR * 01045 do_opendir(const char *path, int flags, rb_encoding *enc) 01046 { 01047 DIR *dirp; 01048 #ifdef _WIN32 01049 volatile VALUE tmp; 01050 if (enc != rb_usascii_encoding() && 01051 enc != rb_ascii8bit_encoding() && 01052 enc != rb_utf8_encoding()) { 01053 tmp = rb_enc_str_new(path, strlen(path), enc); 01054 tmp = rb_str_encode_ospath(tmp); 01055 path = RSTRING_PTR(tmp); 01056 } 01057 #endif 01058 dirp = opendir(path); 01059 if (dirp == NULL && !to_be_ignored(errno)) 01060 sys_warning(path); 01061 01062 return dirp; 01063 } 01064 01065 /* Return nonzero if S has any special globbing chars in it. */ 01066 static int 01067 has_magic(const char *p, const char *pend, int flags, rb_encoding *enc) 01068 { 01069 const int escape = !(flags & FNM_NOESCAPE); 01070 const int nocase = flags & FNM_CASEFOLD; 01071 01072 register char c; 01073 01074 while (p < pend && (c = *p++) != 0) { 01075 switch (c) { 01076 case '*': 01077 case '?': 01078 case '[': 01079 return 1; 01080 01081 case '\\': 01082 if (escape && !(c = *p++)) 01083 return 0; 01084 continue; 01085 01086 default: 01087 if (!FNM_SYSCASE && ISALPHA(c) && nocase) 01088 return 1; 01089 } 01090 01091 p = Next(p-1, pend, enc); 01092 } 01093 01094 return 0; 01095 } 01096 01097 /* Find separator in globbing pattern. */ 01098 static char * 01099 find_dirsep(const char *p, const char *pend, int flags, rb_encoding *enc) 01100 { 01101 const int escape = !(flags & FNM_NOESCAPE); 01102 01103 register char c; 01104 int open = 0; 01105 01106 while ((c = *p++) != 0) { 01107 switch (c) { 01108 case '[': 01109 open = 1; 01110 continue; 01111 case ']': 01112 open = 0; 01113 continue; 01114 01115 case '/': 01116 if (!open) 01117 return (char *)p-1; 01118 continue; 01119 01120 case '\\': 01121 if (escape && !(c = *p++)) 01122 return (char *)p-1; 01123 continue; 01124 } 01125 01126 p = Next(p-1, pend, enc); 01127 } 01128 01129 return (char *)p-1; 01130 } 01131 01132 /* Remove escaping backslashes */ 01133 static void 01134 remove_backslashes(char *p, rb_encoding *enc) 01135 { 01136 register const char *pend = p + strlen(p); 01137 char *t = p; 01138 char *s = p; 01139 01140 while (*p) { 01141 if (*p == '\\') { 01142 if (t != s) 01143 memmove(t, s, p - s); 01144 t += p - s; 01145 s = ++p; 01146 if (!*p) break; 01147 } 01148 Inc(p, pend, enc); 01149 } 01150 01151 while (*p++); 01152 01153 if (t != s) 01154 memmove(t, s, p - s); /* move '\0' too */ 01155 } 01156 01157 /* Globing pattern */ 01158 enum glob_pattern_type { PLAIN, MAGICAL, RECURSIVE, MATCH_ALL, MATCH_DIR }; 01159 01160 struct glob_pattern { 01161 char *str; 01162 enum glob_pattern_type type; 01163 struct glob_pattern *next; 01164 }; 01165 01166 static void glob_free_pattern(struct glob_pattern *list); 01167 01168 static struct glob_pattern * 01169 glob_make_pattern(const char *p, const char *e, int flags, rb_encoding *enc) 01170 { 01171 struct glob_pattern *list, *tmp, **tail = &list; 01172 int dirsep = 0; /* pattern is terminated with '/' */ 01173 01174 while (p < e && *p) { 01175 tmp = GLOB_ALLOC(struct glob_pattern); 01176 if (!tmp) goto error; 01177 if (p[0] == '*' && p[1] == '*' && p[2] == '/') { 01178 /* fold continuous RECURSIVEs (needed in glob_helper) */ 01179 do { p += 3; while (*p == '/') p++; } while (p[0] == '*' && p[1] == '*' && p[2] == '/'); 01180 tmp->type = RECURSIVE; 01181 tmp->str = 0; 01182 dirsep = 1; 01183 } 01184 else { 01185 const char *m = find_dirsep(p, e, flags, enc); 01186 int magic = has_magic(p, m, flags, enc); 01187 char *buf; 01188 01189 if (!magic && *m) { 01190 const char *m2; 01191 while (!has_magic(m+1, m2 = find_dirsep(m+1, e, flags, enc), flags, enc) && 01192 *m2) { 01193 m = m2; 01194 } 01195 } 01196 buf = GLOB_ALLOC_N(char, m-p+1); 01197 if (!buf) { 01198 GLOB_FREE(tmp); 01199 goto error; 01200 } 01201 memcpy(buf, p, m-p); 01202 buf[m-p] = '\0'; 01203 tmp->type = magic ? MAGICAL : PLAIN; 01204 tmp->str = buf; 01205 if (*m) { 01206 dirsep = 1; 01207 p = m + 1; 01208 } 01209 else { 01210 dirsep = 0; 01211 p = m; 01212 } 01213 } 01214 *tail = tmp; 01215 tail = &tmp->next; 01216 } 01217 01218 tmp = GLOB_ALLOC(struct glob_pattern); 01219 if (!tmp) { 01220 error: 01221 *tail = 0; 01222 glob_free_pattern(list); 01223 return 0; 01224 } 01225 tmp->type = dirsep ? MATCH_DIR : MATCH_ALL; 01226 tmp->str = 0; 01227 *tail = tmp; 01228 tmp->next = 0; 01229 01230 return list; 01231 } 01232 01233 static void 01234 glob_free_pattern(struct glob_pattern *list) 01235 { 01236 while (list) { 01237 struct glob_pattern *tmp = list; 01238 list = list->next; 01239 if (tmp->str) 01240 GLOB_FREE(tmp->str); 01241 GLOB_FREE(tmp); 01242 } 01243 } 01244 01245 static char * 01246 join_path(const char *path, int dirsep, const char *name) 01247 { 01248 long len = strlen(path); 01249 long len2 = strlen(name)+(dirsep?1:0)+1; 01250 char *buf = GLOB_ALLOC_N(char, len+len2); 01251 01252 if (!buf) return 0; 01253 memcpy(buf, path, len); 01254 if (dirsep) { 01255 buf[len++] = '/'; 01256 } 01257 buf[len] = '\0'; 01258 strlcat(buf+len, name, len2); 01259 return buf; 01260 } 01261 01262 enum answer { YES, NO, UNKNOWN }; 01263 01264 #ifndef S_ISDIR 01265 # define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) 01266 #endif 01267 01268 #ifndef S_ISLNK 01269 # ifndef S_IFLNK 01270 # define S_ISLNK(m) (0) 01271 # else 01272 # define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) 01273 # endif 01274 #endif 01275 01276 struct glob_args { 01277 void (*func)(const char *, VALUE, void *); 01278 const char *path; 01279 VALUE value; 01280 rb_encoding *enc; 01281 }; 01282 01283 static VALUE 01284 glob_func_caller(VALUE val) 01285 { 01286 struct glob_args *args = (struct glob_args *)val; 01287 01288 (*args->func)(args->path, args->value, args->enc); 01289 return Qnil; 01290 } 01291 01292 #define glob_call_func(func, path, arg, enc) (*(func))((path), (arg), (enc)) 01293 01294 static int 01295 glob_helper( 01296 const char *path, 01297 int dirsep, /* '/' should be placed before appending child entry's name to 'path'. */ 01298 enum answer exist, /* Does 'path' indicate an existing entry? */ 01299 enum answer isdir, /* Does 'path' indicate a directory or a symlink to a directory? */ 01300 struct glob_pattern **beg, 01301 struct glob_pattern **end, 01302 int flags, 01303 ruby_glob_func *func, 01304 VALUE arg, 01305 rb_encoding *enc) 01306 { 01307 struct stat st; 01308 int status = 0; 01309 struct glob_pattern **cur, **new_beg, **new_end; 01310 int plain = 0, magical = 0, recursive = 0, match_all = 0, match_dir = 0; 01311 int escape = !(flags & FNM_NOESCAPE); 01312 01313 for (cur = beg; cur < end; ++cur) { 01314 struct glob_pattern *p = *cur; 01315 if (p->type == RECURSIVE) { 01316 recursive = 1; 01317 p = p->next; 01318 } 01319 switch (p->type) { 01320 case PLAIN: 01321 plain = 1; 01322 break; 01323 case MAGICAL: 01324 magical = 1; 01325 break; 01326 case MATCH_ALL: 01327 match_all = 1; 01328 break; 01329 case MATCH_DIR: 01330 match_dir = 1; 01331 break; 01332 case RECURSIVE: 01333 rb_bug("continuous RECURSIVEs"); 01334 } 01335 } 01336 01337 if (*path) { 01338 if (match_all && exist == UNKNOWN) { 01339 if (do_lstat(path, &st, flags) == 0) { 01340 exist = YES; 01341 isdir = S_ISDIR(st.st_mode) ? YES : S_ISLNK(st.st_mode) ? UNKNOWN : NO; 01342 } 01343 else { 01344 exist = NO; 01345 isdir = NO; 01346 } 01347 } 01348 if (match_dir && isdir == UNKNOWN) { 01349 if (do_stat(path, &st, flags) == 0) { 01350 exist = YES; 01351 isdir = S_ISDIR(st.st_mode) ? YES : NO; 01352 } 01353 else { 01354 exist = NO; 01355 isdir = NO; 01356 } 01357 } 01358 if (match_all && exist == YES) { 01359 status = glob_call_func(func, path, arg, enc); 01360 if (status) return status; 01361 } 01362 if (match_dir && isdir == YES) { 01363 char *tmp = join_path(path, dirsep, ""); 01364 if (!tmp) return -1; 01365 status = glob_call_func(func, tmp, arg, enc); 01366 GLOB_FREE(tmp); 01367 if (status) return status; 01368 } 01369 } 01370 01371 if (exist == NO || isdir == NO) return 0; 01372 01373 if (magical || recursive) { 01374 struct dirent *dp; 01375 DIR *dirp; 01376 IF_HAVE_READDIR_R(DEFINE_STRUCT_DIRENT entry); 01377 dirp = do_opendir(*path ? path : ".", flags, enc); 01378 if (dirp == NULL) return 0; 01379 01380 while (READDIR(dirp, enc, &STRUCT_DIRENT(entry), dp)) { 01381 char *buf = join_path(path, dirsep, dp->d_name); 01382 enum answer new_isdir = UNKNOWN; 01383 01384 if (!buf) { 01385 status = -1; 01386 break; 01387 } 01388 if (recursive && strcmp(dp->d_name, ".") != 0 && strcmp(dp->d_name, "..") != 0 01389 && fnmatch("*", rb_usascii_encoding(), dp->d_name, flags) == 0) { 01390 #ifndef _WIN32 01391 if (do_lstat(buf, &st, flags) == 0) 01392 new_isdir = S_ISDIR(st.st_mode) ? YES : S_ISLNK(st.st_mode) ? UNKNOWN : NO; 01393 else 01394 new_isdir = NO; 01395 #else 01396 new_isdir = dp->d_isdir ? (!dp->d_isrep ? YES : UNKNOWN) : NO; 01397 #endif 01398 } 01399 01400 new_beg = new_end = GLOB_ALLOC_N(struct glob_pattern *, (end - beg) * 2); 01401 if (!new_beg) { 01402 GLOB_FREE(buf); 01403 status = -1; 01404 break; 01405 } 01406 01407 for (cur = beg; cur < end; ++cur) { 01408 struct glob_pattern *p = *cur; 01409 if (p->type == RECURSIVE) { 01410 if (new_isdir == YES) /* not symlink but real directory */ 01411 *new_end++ = p; /* append recursive pattern */ 01412 p = p->next; /* 0 times recursion */ 01413 } 01414 if (p->type == PLAIN || p->type == MAGICAL) { 01415 if (fnmatch(p->str, enc, dp->d_name, flags) == 0) 01416 *new_end++ = p->next; 01417 } 01418 } 01419 01420 status = glob_helper(buf, 1, YES, new_isdir, new_beg, new_end, 01421 flags, func, arg, enc); 01422 GLOB_FREE(buf); 01423 GLOB_FREE(new_beg); 01424 if (status) break; 01425 } 01426 01427 closedir(dirp); 01428 } 01429 else if (plain) { 01430 struct glob_pattern **copy_beg, **copy_end, **cur2; 01431 01432 copy_beg = copy_end = GLOB_ALLOC_N(struct glob_pattern *, end - beg); 01433 if (!copy_beg) return -1; 01434 for (cur = beg; cur < end; ++cur) 01435 *copy_end++ = (*cur)->type == PLAIN ? *cur : 0; 01436 01437 for (cur = copy_beg; cur < copy_end; ++cur) { 01438 if (*cur) { 01439 char *buf; 01440 char *name; 01441 size_t len = strlen((*cur)->str) + 1; 01442 name = GLOB_ALLOC_N(char, len); 01443 if (!name) { 01444 status = -1; 01445 break; 01446 } 01447 memcpy(name, (*cur)->str, len); 01448 if (escape) remove_backslashes(name, enc); 01449 01450 new_beg = new_end = GLOB_ALLOC_N(struct glob_pattern *, end - beg); 01451 if (!new_beg) { 01452 GLOB_FREE(name); 01453 status = -1; 01454 break; 01455 } 01456 *new_end++ = (*cur)->next; 01457 for (cur2 = cur + 1; cur2 < copy_end; ++cur2) { 01458 if (*cur2 && fnmatch((*cur2)->str, enc, name, flags) == 0) { 01459 *new_end++ = (*cur2)->next; 01460 *cur2 = 0; 01461 } 01462 } 01463 01464 buf = join_path(path, dirsep, name); 01465 GLOB_FREE(name); 01466 if (!buf) { 01467 GLOB_FREE(new_beg); 01468 status = -1; 01469 break; 01470 } 01471 status = glob_helper(buf, 1, UNKNOWN, UNKNOWN, new_beg, 01472 new_end, flags, func, arg, enc); 01473 GLOB_FREE(buf); 01474 GLOB_FREE(new_beg); 01475 if (status) break; 01476 } 01477 } 01478 01479 GLOB_FREE(copy_beg); 01480 } 01481 01482 return status; 01483 } 01484 01485 static int 01486 ruby_glob0(const char *path, int flags, ruby_glob_func *func, VALUE arg, rb_encoding *enc) 01487 { 01488 struct glob_pattern *list; 01489 const char *root, *start; 01490 char *buf; 01491 size_t n; 01492 int status; 01493 01494 start = root = path; 01495 flags |= FNM_SYSCASE; 01496 #if defined DOSISH 01497 root = rb_path_skip_prefix(root); 01498 #endif 01499 01500 if (root && *root == '/') root++; 01501 01502 n = root - start; 01503 buf = GLOB_ALLOC_N(char, n + 1); 01504 if (!buf) return -1; 01505 MEMCPY(buf, start, char, n); 01506 buf[n] = '\0'; 01507 01508 list = glob_make_pattern(root, root + strlen(root), flags, enc); 01509 if (!list) { 01510 GLOB_FREE(buf); 01511 return -1; 01512 } 01513 status = glob_helper(buf, 0, UNKNOWN, UNKNOWN, &list, &list + 1, flags, func, arg, enc); 01514 glob_free_pattern(list); 01515 GLOB_FREE(buf); 01516 01517 return status; 01518 } 01519 01520 int 01521 ruby_glob(const char *path, int flags, ruby_glob_func *func, VALUE arg) 01522 { 01523 return ruby_glob0(path, flags & ~GLOB_VERBOSE, func, arg, 01524 rb_ascii8bit_encoding()); 01525 } 01526 01527 static int 01528 rb_glob_caller(const char *path, VALUE a, void *enc) 01529 { 01530 int status; 01531 struct glob_args *args = (struct glob_args *)a; 01532 01533 args->path = path; 01534 rb_protect(glob_func_caller, a, &status); 01535 return status; 01536 } 01537 01538 static int 01539 rb_glob2(const char *path, int flags, 01540 void (*func)(const char *, VALUE, void *), VALUE arg, 01541 rb_encoding* enc) 01542 { 01543 struct glob_args args; 01544 01545 args.func = func; 01546 args.value = arg; 01547 args.enc = enc; 01548 01549 if (flags & FNM_SYSCASE) { 01550 rb_warning("Dir.glob() ignores File::FNM_CASEFOLD"); 01551 } 01552 01553 return ruby_glob0(path, flags | GLOB_VERBOSE, rb_glob_caller, (VALUE)&args, 01554 enc); 01555 } 01556 01557 void 01558 rb_glob(const char *path, void (*func)(const char *, VALUE, void *), VALUE arg) 01559 { 01560 int status = rb_glob2(path, 0, func, arg, rb_ascii8bit_encoding()); 01561 if (status) GLOB_JUMP_TAG(status); 01562 } 01563 01564 static void 01565 push_pattern(const char *path, VALUE ary, void *enc) 01566 { 01567 rb_ary_push(ary, rb_external_str_new_with_enc(path, strlen(path), enc)); 01568 } 01569 01570 static int 01571 ruby_brace_expand(const char *str, int flags, ruby_glob_func *func, VALUE arg, 01572 rb_encoding *enc) 01573 { 01574 const int escape = !(flags & FNM_NOESCAPE); 01575 const char *p = str; 01576 const char *pend = p + strlen(p); 01577 const char *s = p; 01578 const char *lbrace = 0, *rbrace = 0; 01579 int nest = 0, status = 0; 01580 01581 while (*p) { 01582 if (*p == '{' && nest++ == 0) { 01583 lbrace = p; 01584 } 01585 if (*p == '}' && --nest <= 0) { 01586 rbrace = p; 01587 break; 01588 } 01589 if (*p == '\\' && escape) { 01590 if (!*++p) break; 01591 } 01592 Inc(p, pend, enc); 01593 } 01594 01595 if (lbrace && rbrace) { 01596 size_t len = strlen(s) + 1; 01597 char *buf = GLOB_ALLOC_N(char, len); 01598 long shift; 01599 01600 if (!buf) return -1; 01601 memcpy(buf, s, lbrace-s); 01602 shift = (lbrace-s); 01603 p = lbrace; 01604 while (p < rbrace) { 01605 const char *t = ++p; 01606 nest = 0; 01607 while (p < rbrace && !(*p == ',' && nest == 0)) { 01608 if (*p == '{') nest++; 01609 if (*p == '}') nest--; 01610 if (*p == '\\' && escape) { 01611 if (++p == rbrace) break; 01612 } 01613 Inc(p, pend, enc); 01614 } 01615 memcpy(buf+shift, t, p-t); 01616 strlcpy(buf+shift+(p-t), rbrace+1, len-(shift+(p-t))); 01617 status = ruby_brace_expand(buf, flags, func, arg, enc); 01618 if (status) break; 01619 } 01620 GLOB_FREE(buf); 01621 } 01622 else if (!lbrace && !rbrace) { 01623 status = (*func)(s, arg, enc); 01624 } 01625 01626 return status; 01627 } 01628 01629 struct brace_args { 01630 ruby_glob_func *func; 01631 VALUE value; 01632 int flags; 01633 }; 01634 01635 static int 01636 glob_brace(const char *path, VALUE val, void *enc) 01637 { 01638 struct brace_args *arg = (struct brace_args *)val; 01639 01640 return ruby_glob0(path, arg->flags, arg->func, arg->value, enc); 01641 } 01642 01643 static int 01644 ruby_brace_glob0(const char *str, int flags, ruby_glob_func *func, VALUE arg, 01645 rb_encoding* enc) 01646 { 01647 struct brace_args args; 01648 01649 args.func = func; 01650 args.value = arg; 01651 args.flags = flags; 01652 return ruby_brace_expand(str, flags, glob_brace, (VALUE)&args, enc); 01653 } 01654 01655 int 01656 ruby_brace_glob(const char *str, int flags, ruby_glob_func *func, VALUE arg) 01657 { 01658 return ruby_brace_glob0(str, flags & ~GLOB_VERBOSE, func, arg, 01659 rb_ascii8bit_encoding()); 01660 } 01661 01662 int 01663 ruby_brace_glob_with_enc(const char *str, int flags, ruby_glob_func *func, VALUE arg, rb_encoding *enc) 01664 { 01665 return ruby_brace_glob0(str, flags & ~GLOB_VERBOSE, func, arg, enc); 01666 } 01667 01668 static int 01669 push_glob(VALUE ary, VALUE str, int flags) 01670 { 01671 struct glob_args args; 01672 rb_encoding *enc = rb_enc_get(str); 01673 01674 if (enc == rb_usascii_encoding()) enc = rb_filesystem_encoding(); 01675 args.func = push_pattern; 01676 args.value = ary; 01677 args.enc = enc; 01678 01679 RB_GC_GUARD(str); 01680 return ruby_brace_glob0(RSTRING_PTR(str), flags | GLOB_VERBOSE, 01681 rb_glob_caller, (VALUE)&args, enc); 01682 } 01683 01684 static VALUE 01685 rb_push_glob(VALUE str, int flags) /* '\0' is delimiter */ 01686 { 01687 long offset = 0; 01688 VALUE ary; 01689 01690 GlobPathValue(str, TRUE); 01691 ary = rb_ary_new(); 01692 01693 while (offset < RSTRING_LEN(str)) { 01694 char *p, *pend; 01695 int status; 01696 p = RSTRING_PTR(str) + offset; 01697 status = push_glob(ary, rb_enc_str_new(p, strlen(p), rb_enc_get(str)), 01698 flags); 01699 if (status) GLOB_JUMP_TAG(status); 01700 if (offset >= RSTRING_LEN(str)) break; 01701 p += strlen(p) + 1; 01702 pend = RSTRING_PTR(str) + RSTRING_LEN(str); 01703 while (p < pend && !*p) 01704 p++; 01705 offset = p - RSTRING_PTR(str); 01706 } 01707 01708 return ary; 01709 } 01710 01711 static VALUE 01712 dir_globs(long argc, VALUE *argv, int flags) 01713 { 01714 VALUE ary = rb_ary_new(); 01715 long i; 01716 01717 for (i = 0; i < argc; ++i) { 01718 int status; 01719 VALUE str = argv[i]; 01720 GlobPathValue(str, TRUE); 01721 status = push_glob(ary, str, flags); 01722 if (status) GLOB_JUMP_TAG(status); 01723 } 01724 01725 return ary; 01726 } 01727 01728 /* 01729 * call-seq: 01730 * Dir[ array ] -> array 01731 * Dir[ string [, string ...] ] -> array 01732 * 01733 * Equivalent to calling 01734 * <code>Dir.glob(</code><i>array,</i><code>0)</code> and 01735 * <code>Dir.glob([</code><i>string,...</i><code>],0)</code>. 01736 * 01737 */ 01738 static VALUE 01739 dir_s_aref(int argc, VALUE *argv, VALUE obj) 01740 { 01741 if (argc == 1) { 01742 return rb_push_glob(argv[0], 0); 01743 } 01744 return dir_globs(argc, argv, 0); 01745 } 01746 01747 /* 01748 * call-seq: 01749 * Dir.glob( pattern, [flags] ) -> array 01750 * Dir.glob( pattern, [flags] ) {| filename | block } -> nil 01751 * 01752 * Returns the filenames found by expanding <i>pattern</i> which is 01753 * an +Array+ of the patterns or the pattern +String+, either as an 01754 * <i>array</i> or as parameters to the block. Note that this pattern 01755 * is not a regexp (it's closer to a shell glob). See 01756 * <code>File::fnmatch</code> for the meaning of the <i>flags</i> 01757 * parameter. Note that case sensitivity depends on your system (so 01758 * <code>File::FNM_CASEFOLD</code> is ignored), as does the order 01759 * in which the results are returned. 01760 * 01761 * <code>*</code>:: Matches any file. Can be restricted by 01762 * other values in the glob. <code>*</code> 01763 * will match all files; <code>c*</code> will 01764 * match all files beginning with 01765 * <code>c</code>; <code>*c</code> will match 01766 * all files ending with <code>c</code>; and 01767 * <code>\*c\*</code> will match all files that 01768 * have <code>c</code> in them (including at 01769 * the beginning or end). Equivalent to 01770 * <code>/ .* /x</code> in regexp. Note, this 01771 * will not match Unix-like hidden files (dotfiles). 01772 * In order to include those in the match results, 01773 * you must use something like "{*,.*}". 01774 * <code>**</code>:: Matches directories recursively. 01775 * <code>?</code>:: Matches any one character. Equivalent to 01776 * <code>/.{1}/</code> in regexp. 01777 * <code>[set]</code>:: Matches any one character in +set+. 01778 * Behaves exactly like character sets in 01779 * Regexp, including set negation 01780 * (<code>[^a-z]</code>). 01781 * <code>{p,q}</code>:: Matches either literal <code>p</code> or 01782 * literal <code>q</code>. Matching literals 01783 * may be more than one character in length. 01784 * More than two literals may be specified. 01785 * Equivalent to pattern alternation in 01786 * regexp. 01787 * <code></code>:: Escapes the next metacharacter. 01788 * Note that this means you cannot use backslash in windows 01789 * as part of a glob, i.e. Dir["c:\\foo*"] will not work 01790 * use Dir["c:/foo*"] instead 01791 * 01792 * Dir["config.?"] #=> ["config.h"] 01793 * Dir.glob("config.?") #=> ["config.h"] 01794 * Dir.glob("*.[a-z][a-z]") #=> ["main.rb"] 01795 * Dir.glob("*.[^r]*") #=> ["config.h"] 01796 * Dir.glob("*.{rb,h}") #=> ["main.rb", "config.h"] 01797 * Dir.glob("*") #=> ["config.h", "main.rb"] 01798 * Dir.glob("*", File::FNM_DOTMATCH) #=> [".", "..", "config.h", "main.rb"] 01799 * 01800 * rbfiles = File.join("**", "*.rb") 01801 * Dir.glob(rbfiles) #=> ["main.rb", 01802 * # "lib/song.rb", 01803 * # "lib/song/karaoke.rb"] 01804 * libdirs = File.join("**", "lib") 01805 * Dir.glob(libdirs) #=> ["lib"] 01806 * 01807 * librbfiles = File.join("**", "lib", "**", "*.rb") 01808 * Dir.glob(librbfiles) #=> ["lib/song.rb", 01809 * # "lib/song/karaoke.rb"] 01810 * 01811 * librbfiles = File.join("**", "lib", "*.rb") 01812 * Dir.glob(librbfiles) #=> ["lib/song.rb"] 01813 */ 01814 static VALUE 01815 dir_s_glob(int argc, VALUE *argv, VALUE obj) 01816 { 01817 VALUE str, rflags, ary; 01818 int flags; 01819 01820 if (rb_scan_args(argc, argv, "11", &str, &rflags) == 2) 01821 flags = NUM2INT(rflags); 01822 else 01823 flags = 0; 01824 01825 ary = rb_check_array_type(str); 01826 if (NIL_P(ary)) { 01827 ary = rb_push_glob(str, flags); 01828 } 01829 else { 01830 volatile VALUE v = ary; 01831 ary = dir_globs(RARRAY_LEN(v), RARRAY_PTR(v), flags); 01832 } 01833 01834 if (rb_block_given_p()) { 01835 rb_ary_each(ary); 01836 return Qnil; 01837 } 01838 return ary; 01839 } 01840 01841 static VALUE 01842 dir_open_dir(int argc, VALUE *argv) 01843 { 01844 VALUE dir = rb_funcall2(rb_cDir, rb_intern("open"), argc, argv); 01845 struct dir_data *dirp; 01846 01847 TypedData_Get_Struct(dir, struct dir_data, &dir_data_type, dirp); 01848 return dir; 01849 } 01850 01851 01852 /* 01853 * call-seq: 01854 * Dir.foreach( dirname ) {| filename | block } -> nil 01855 * Dir.foreach( dirname ) -> an_enumerator 01856 * 01857 * Calls the block once for each entry in the named directory, passing 01858 * the filename of each entry as a parameter to the block. 01859 * 01860 * If no block is given, an enumerator is returned instead. 01861 * 01862 * Dir.foreach("testdir") {|x| puts "Got #{x}" } 01863 * 01864 * <em>produces:</em> 01865 * 01866 * Got . 01867 * Got .. 01868 * Got config.h 01869 * Got main.rb 01870 * 01871 */ 01872 static VALUE 01873 dir_foreach(int argc, VALUE *argv, VALUE io) 01874 { 01875 VALUE dir; 01876 01877 RETURN_ENUMERATOR(io, argc, argv); 01878 dir = dir_open_dir(argc, argv); 01879 rb_ensure(dir_each, dir, dir_close, dir); 01880 return Qnil; 01881 } 01882 01883 /* 01884 * call-seq: 01885 * Dir.entries( dirname ) -> array 01886 * 01887 * Returns an array containing all of the filenames in the given 01888 * directory. Will raise a <code>SystemCallError</code> if the named 01889 * directory doesn't exist. 01890 * 01891 * Dir.entries("testdir") #=> [".", "..", "config.h", "main.rb"] 01892 * 01893 */ 01894 static VALUE 01895 dir_entries(int argc, VALUE *argv, VALUE io) 01896 { 01897 VALUE dir; 01898 01899 dir = dir_open_dir(argc, argv); 01900 return rb_ensure(rb_Array, dir, dir_close, dir); 01901 } 01902 01903 /* 01904 * call-seq: 01905 * File.fnmatch( pattern, path, [flags] ) -> (true or false) 01906 * File.fnmatch?( pattern, path, [flags] ) -> (true or false) 01907 * 01908 * Returns true if <i>path</i> matches against <i>pattern</i> The 01909 * pattern is not a regular expression; instead it follows rules 01910 * similar to shell filename globbing. It may contain the following 01911 * metacharacters: 01912 * 01913 * <code>*</code>:: Matches any file. Can be restricted by 01914 * other values in the glob. <code>*</code> 01915 * will match all files; <code>c*</code> will 01916 * match all files beginning with 01917 * <code>c</code>; <code>*c</code> will match 01918 * all files ending with <code>c</code>; and 01919 * <code>\*c*</code> will match all files that 01920 * have <code>c</code> in them (including at 01921 * the beginning or end). Equivalent to 01922 * <code>/ .* /x</code> in regexp. 01923 * <code>**</code>:: Matches directories recursively or files 01924 * expansively. 01925 * <code>?</code>:: Matches any one character. Equivalent to 01926 * <code>/.{1}/</code> in regexp. 01927 * <code>[set]</code>:: Matches any one character in +set+. 01928 * Behaves exactly like character sets in 01929 * Regexp, including set negation 01930 * (<code>[^a-z]</code>). 01931 * <code></code>:: Escapes the next metacharacter. 01932 * 01933 * <i>flags</i> is a bitwise OR of the <code>FNM_xxx</code> 01934 * parameters. The same glob pattern and flags are used by 01935 * <code>Dir::glob</code>. 01936 * 01937 * File.fnmatch('cat', 'cat') #=> true # match entire string 01938 * File.fnmatch('cat', 'category') #=> false # only match partial string 01939 * File.fnmatch('c{at,ub}s', 'cats') #=> false # { } isn't supported 01940 * 01941 * File.fnmatch('c?t', 'cat') #=> true # '?' match only 1 character 01942 * File.fnmatch('c??t', 'cat') #=> false # ditto 01943 * File.fnmatch('c*', 'cats') #=> true # '*' match 0 or more characters 01944 * File.fnmatch('c*t', 'c/a/b/t') #=> true # ditto 01945 * File.fnmatch('ca[a-z]', 'cat') #=> true # inclusive bracket expression 01946 * File.fnmatch('ca[^t]', 'cat') #=> false # exclusive bracket expression ('^' or '!') 01947 * 01948 * File.fnmatch('cat', 'CAT') #=> false # case sensitive 01949 * File.fnmatch('cat', 'CAT', File::FNM_CASEFOLD) #=> true # case insensitive 01950 * 01951 * File.fnmatch('?', '/', File::FNM_PATHNAME) #=> false # wildcard doesn't match '/' on FNM_PATHNAME 01952 * File.fnmatch('*', '/', File::FNM_PATHNAME) #=> false # ditto 01953 * File.fnmatch('[/]', '/', File::FNM_PATHNAME) #=> false # ditto 01954 * 01955 * File.fnmatch('\?', '?') #=> true # escaped wildcard becomes ordinary 01956 * File.fnmatch('\a', 'a') #=> true # escaped ordinary remains ordinary 01957 * File.fnmatch('\a', '\a', File::FNM_NOESCAPE) #=> true # FNM_NOESACPE makes '\' ordinary 01958 * File.fnmatch('[\?]', '?') #=> true # can escape inside bracket expression 01959 * 01960 * File.fnmatch('*', '.profile') #=> false # wildcard doesn't match leading 01961 * File.fnmatch('*', '.profile', File::FNM_DOTMATCH) #=> true # period by default. 01962 * File.fnmatch('.*', '.profile') #=> true 01963 * 01964 * rbfiles = '**' '/' '*.rb' # you don't have to do like this. just write in single string. 01965 * File.fnmatch(rbfiles, 'main.rb') #=> false 01966 * File.fnmatch(rbfiles, './main.rb') #=> false 01967 * File.fnmatch(rbfiles, 'lib/song.rb') #=> true 01968 * File.fnmatch('**.rb', 'main.rb') #=> true 01969 * File.fnmatch('**.rb', './main.rb') #=> false 01970 * File.fnmatch('**.rb', 'lib/song.rb') #=> true 01971 * File.fnmatch('*', 'dave/.profile') #=> true 01972 * 01973 * pattern = '*' '/' '*' 01974 * File.fnmatch(pattern, 'dave/.profile', File::FNM_PATHNAME) #=> false 01975 * File.fnmatch(pattern, 'dave/.profile', File::FNM_PATHNAME | File::FNM_DOTMATCH) #=> true 01976 * 01977 * pattern = '**' '/' 'foo' 01978 * File.fnmatch(pattern, 'a/b/c/foo', File::FNM_PATHNAME) #=> true 01979 * File.fnmatch(pattern, '/a/b/c/foo', File::FNM_PATHNAME) #=> true 01980 * File.fnmatch(pattern, 'c:/a/b/c/foo', File::FNM_PATHNAME) #=> true 01981 * File.fnmatch(pattern, 'a/.b/c/foo', File::FNM_PATHNAME) #=> false 01982 * File.fnmatch(pattern, 'a/.b/c/foo', File::FNM_PATHNAME | File::FNM_DOTMATCH) #=> true 01983 */ 01984 static VALUE 01985 file_s_fnmatch(int argc, VALUE *argv, VALUE obj) 01986 { 01987 VALUE pattern, path; 01988 VALUE rflags; 01989 int flags; 01990 01991 if (rb_scan_args(argc, argv, "21", &pattern, &path, &rflags) == 3) 01992 flags = NUM2INT(rflags); 01993 else 01994 flags = 0; 01995 01996 StringValue(pattern); 01997 FilePathStringValue(path); 01998 01999 if (fnmatch(RSTRING_PTR(pattern), rb_enc_get(pattern), RSTRING_PTR(path), 02000 flags) == 0) 02001 return Qtrue; 02002 02003 return Qfalse; 02004 } 02005 02006 /* 02007 * call-seq: 02008 * Dir.home() -> "/home/me" 02009 * Dir.home("root") -> "/root" 02010 * 02011 * Returns the home directory of the current user or the named user 02012 * if given. 02013 */ 02014 static VALUE 02015 dir_s_home(int argc, VALUE *argv, VALUE obj) 02016 { 02017 VALUE user; 02018 const char *u = 0; 02019 02020 rb_scan_args(argc, argv, "01", &user); 02021 if (!NIL_P(user)) { 02022 SafeStringValue(user); 02023 u = StringValueCStr(user); 02024 } 02025 return rb_home_dir(u, rb_str_new(0, 0)); 02026 } 02027 02028 /* 02029 * Objects of class <code>Dir</code> are directory streams representing 02030 * directories in the underlying file system. They provide a variety of 02031 * ways to list directories and their contents. See also 02032 * <code>File</code>. 02033 * 02034 * The directory used in these examples contains the two regular files 02035 * (<code>config.h</code> and <code>main.rb</code>), the parent 02036 * directory (<code>..</code>), and the directory itself 02037 * (<code>.</code>). 02038 */ 02039 void 02040 Init_Dir(void) 02041 { 02042 rb_cDir = rb_define_class("Dir", rb_cObject); 02043 02044 rb_include_module(rb_cDir, rb_mEnumerable); 02045 02046 rb_define_alloc_func(rb_cDir, dir_s_alloc); 02047 rb_define_singleton_method(rb_cDir, "open", dir_s_open, -1); 02048 rb_define_singleton_method(rb_cDir, "foreach", dir_foreach, -1); 02049 rb_define_singleton_method(rb_cDir, "entries", dir_entries, -1); 02050 02051 rb_define_method(rb_cDir,"initialize", dir_initialize, -1); 02052 rb_define_method(rb_cDir,"path", dir_path, 0); 02053 rb_define_method(rb_cDir,"to_path", dir_path, 0); 02054 rb_define_method(rb_cDir,"inspect", dir_inspect, 0); 02055 rb_define_method(rb_cDir,"read", dir_read, 0); 02056 rb_define_method(rb_cDir,"each", dir_each, 0); 02057 rb_define_method(rb_cDir,"rewind", dir_rewind, 0); 02058 rb_define_method(rb_cDir,"tell", dir_tell, 0); 02059 rb_define_method(rb_cDir,"seek", dir_seek, 1); 02060 rb_define_method(rb_cDir,"pos", dir_tell, 0); 02061 rb_define_method(rb_cDir,"pos=", dir_set_pos, 1); 02062 rb_define_method(rb_cDir,"close", dir_close, 0); 02063 02064 rb_define_singleton_method(rb_cDir,"chdir", dir_s_chdir, -1); 02065 rb_define_singleton_method(rb_cDir,"getwd", dir_s_getwd, 0); 02066 rb_define_singleton_method(rb_cDir,"pwd", dir_s_getwd, 0); 02067 rb_define_singleton_method(rb_cDir,"chroot", dir_s_chroot, 1); 02068 rb_define_singleton_method(rb_cDir,"mkdir", dir_s_mkdir, -1); 02069 rb_define_singleton_method(rb_cDir,"rmdir", dir_s_rmdir, 1); 02070 rb_define_singleton_method(rb_cDir,"delete", dir_s_rmdir, 1); 02071 rb_define_singleton_method(rb_cDir,"unlink", dir_s_rmdir, 1); 02072 rb_define_singleton_method(rb_cDir,"home", dir_s_home, -1); 02073 02074 rb_define_singleton_method(rb_cDir,"glob", dir_s_glob, -1); 02075 rb_define_singleton_method(rb_cDir,"[]", dir_s_aref, -1); 02076 rb_define_singleton_method(rb_cDir,"exist?", rb_file_directory_p, 1); /* in file.c */ 02077 rb_define_singleton_method(rb_cDir,"exists?", rb_file_directory_p, 1); /* in file.c */ 02078 02079 rb_define_singleton_method(rb_cFile,"fnmatch", file_s_fnmatch, -1); 02080 rb_define_singleton_method(rb_cFile,"fnmatch?", file_s_fnmatch, -1); 02081 02082 rb_file_const("FNM_NOESCAPE", INT2FIX(FNM_NOESCAPE)); 02083 rb_file_const("FNM_PATHNAME", INT2FIX(FNM_PATHNAME)); 02084 rb_file_const("FNM_DOTMATCH", INT2FIX(FNM_DOTMATCH)); 02085 rb_file_const("FNM_CASEFOLD", INT2FIX(FNM_CASEFOLD)); 02086 rb_file_const("FNM_SYSCASE", INT2FIX(FNM_SYSCASE)); 02087 } 02088
1.7.6.1