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