Main Page | Modules | Alphabetical List | Data Structures | File List | Data Fields | Globals

dir.c

Go to the documentation of this file.
00001 /**********************************************************************
00002 
00003   dir.c -
00004 
00005   $Author: nobu $
00006   $Date: 2005/09/14 13:40:58 $
00007   created at: Wed Jan  5 09:51:01 JST 1994
00008 
00009   Copyright (C) 1993-2003 Yukihiro Matsumoto
00010   Copyright (C) 2000  Network Applied Communication Laboratory, Inc.
00011   Copyright (C) 2000  Information-technology Promotion Agency, Japan
00012 
00013 **********************************************************************/
00014 
00015 #include "ruby.h"
00016 
00017 #include <sys/types.h>
00018 #include <sys/stat.h>
00019 
00020 #ifdef HAVE_UNISTD_H
00021 #include <unistd.h>
00022 #endif
00023 
00024 #if defined HAVE_DIRENT_H && !defined _WIN32
00025 # include <dirent.h>
00026 # define NAMLEN(dirent) strlen((dirent)->d_name)
00027 #elif defined HAVE_DIRECT_H && !defined _WIN32
00028 # include <direct.h>
00029 # define NAMLEN(dirent) strlen((dirent)->d_name)
00030 #else
00031 # define dirent direct
00032 # if !defined __NeXT__
00033 #  define NAMLEN(dirent) (dirent)->d_namlen
00034 # else
00035 #  /* On some versions of NextStep, d_namlen is always zero, so avoid it. */
00036 #  define NAMLEN(dirent) strlen((dirent)->d_name)
00037 # endif
00038 # if HAVE_SYS_NDIR_H
00039 #  include <sys/ndir.h>
00040 # endif
00041 # if HAVE_SYS_DIR_H
00042 #  include <sys/dir.h>
00043 # endif
00044 # if HAVE_NDIR_H
00045 #  include <ndir.h>
00046 # endif
00047 # ifdef _WIN32
00048 #  include "win32/dir.h"
00049 # endif
00050 #endif
00051 
00052 #include <errno.h>
00053 
00054 #ifndef HAVE_STDLIB_H
00055 char *getenv();
00056 #endif
00057 
00058 #ifndef HAVE_STRING_H
00059 char *strchr (char*,char);
00060 #endif
00061 
00062 #include <ctype.h>
00063 
00064 #include "util.h"
00065 
00066 #if !defined HAVE_LSTAT && !defined lstat
00067 #define lstat stat
00068 #endif
00069 
00070 #define FNM_NOESCAPE    0x01
00071 #define FNM_PATHNAME    0x02
00072 #define FNM_DOTMATCH    0x04
00073 #define FNM_CASEFOLD    0x08
00074 
00075 #define FNM_NOMATCH     1
00076 #define FNM_ERROR       2
00077 
00078 #define downcase(c) (nocase && ISUPPER(c) ? tolower(c) : (c))
00079 
00080 #ifndef CharNext                /* defined as CharNext[AW] on Windows. */
00081 # if defined(DJGPP)
00082 #   define CharNext(p) ((p) + mblen(p, MB_CUR_MAX))
00083 # else
00084 #   define CharNext(p) ((p) + 1)
00085 # endif
00086 #endif
00087 
00088 #if defined DOSISH
00089 #define isdirsep(c) ((c) == '/' || (c) == '\\')
00090 #else
00091 #define isdirsep(c) ((c) == '/')
00092 #endif
00093 
00094 static char *
00095 range(pat, test, flags)
00096     const char *pat;
00097     int test;
00098     int flags;
00099 {
00100     int not, ok = 0;
00101     int nocase = flags & FNM_CASEFOLD;
00102     int escape = !(flags & FNM_NOESCAPE);
00103 
00104     not = *pat == '!' || *pat == '^';
00105     if (not)
00106         pat++;
00107 
00108     test = downcase(test);
00109 
00110     while (*pat != ']') {
00111         int cstart, cend;
00112         if (escape && *pat == '\\')
00113             pat++;
00114         cstart = cend = *pat++;
00115         if (!cstart)
00116             return NULL;
00117         if (*pat == '-' && pat[1] != ']') {
00118             pat++;
00119             if (escape && *pat == '\\')
00120                 pat++;
00121             cend = *pat++;
00122             if (!cend)
00123                 return NULL;
00124         }
00125         if (downcase(cstart) <= test && test <= downcase(cend))
00126             ok = 1;
00127     }
00128     return ok == not ? NULL : (char *)pat + 1;
00129 }
00130 
00131 #define ISDIRSEP(c) (pathname && isdirsep(c))
00132 #define PERIOD(s) (period && *(s) == '.' && \
00133                   ((s) == string || ISDIRSEP((s)[-1])))
00134 static int
00135 fnmatch(pat, string, flags)
00136     const char *pat;
00137     const char *string;
00138     int flags;
00139 {
00140     int c;
00141     int test;
00142     const char *s = string;
00143     int escape = !(flags & FNM_NOESCAPE);
00144     int pathname = flags & FNM_PATHNAME;
00145     int period = !(flags & FNM_DOTMATCH);
00146     int nocase = flags & FNM_CASEFOLD;
00147 
00148     while (c = *pat++) {
00149         switch (c) {
00150         case '?':
00151             if (!*s || ISDIRSEP(*s) || PERIOD(s))
00152                 return FNM_NOMATCH;
00153             s++;
00154             break;
00155         case '*':
00156             while ((c = *pat++) == '*')
00157                 ;
00158 
00159             if (PERIOD(s))
00160                 return FNM_NOMATCH;
00161 
00162             if (!c) {
00163                 if (pathname && *rb_path_next(s))
00164                     return FNM_NOMATCH;
00165                 else
00166                     return 0;
00167             }
00168             else if (ISDIRSEP(c)) {
00169                 s = rb_path_next(s);
00170                 if (*s) {
00171                     s++;
00172                     break;
00173                 }
00174                 return FNM_NOMATCH;
00175             }
00176 
00177             test = escape && c == '\\' ? *pat : c;
00178             test = downcase(test);
00179             pat--;
00180             while (*s) {
00181                 if ((c == '?' || c == '[' || downcase(*s) == test) &&
00182                     !fnmatch(pat, s, flags | FNM_DOTMATCH))
00183                     return 0;
00184                 else if (ISDIRSEP(*s))
00185                     break;
00186                 s++;
00187             }
00188             return FNM_NOMATCH;
00189 
00190         case '[':
00191             if (!*s || ISDIRSEP(*s) || PERIOD(s))
00192                 return FNM_NOMATCH;
00193             pat = range(pat, *s, flags);
00194             if (pat == NULL)
00195                 return FNM_NOMATCH;
00196             s++;
00197             break;
00198 
00199         case '\\':
00200             if (escape
00201 #if defined DOSISH
00202                 && *pat && strchr("*?[]\\", *pat)
00203 #endif
00204                 ) {
00205                 c = *pat;
00206                 if (!c)
00207                     c = '\\';
00208                 else
00209                     pat++;
00210             }
00211             /* FALLTHROUGH */
00212 
00213         default:
00214 #if defined DOSISH
00215             if (ISDIRSEP(c) && isdirsep(*s))
00216                 ;
00217             else
00218 #endif
00219             if(downcase(c) != downcase(*s))
00220                 return FNM_NOMATCH;
00221             s++;
00222             break;
00223         }
00224     }
00225     return !*s ? 0 : FNM_NOMATCH;
00226 }
00227 
00228 VALUE rb_cDir;
00229 
00230 struct dir_data {
00231     DIR *dir;
00232     char *path;
00233 };
00234 
00235 static void
00236 free_dir(dir)
00237     struct dir_data *dir;
00238 {
00239     if (dir) {
00240         if (dir->dir) closedir(dir->dir);
00241         if (dir->path) free(dir->path);
00242     }
00243     free(dir);
00244 }
00245 
00246 static VALUE dir_close (VALUE);
00247 
00248 static VALUE dir_s_alloc (VALUE);
00249 static VALUE
00250 dir_s_alloc(klass)
00251     VALUE klass;
00252 {
00253     struct dir_data *dirp;
00254     VALUE obj = Data_Make_Struct(klass, struct dir_data, 0, free_dir, dirp);
00255 
00256     dirp->dir = NULL;
00257     dirp->path = NULL;
00258 
00259     return obj;
00260 }
00261 
00262 /*
00263  *  call-seq:
00264  *     Dir.new( string ) -> aDir
00265  *
00266  *  Returns a new directory object for the named directory.
00267  */
00268 static VALUE
00269 dir_initialize(dir, dirname)
00270     VALUE dir, dirname;
00271 {
00272     struct dir_data *dp;
00273 
00274     SafeStringValue(dirname);
00275     Data_Get_Struct(dir, struct dir_data, dp);
00276     if (dp->dir) closedir(dp->dir);
00277     if (dp->path) free(dp->path);
00278     dp->dir = NULL;
00279     dp->path = NULL;
00280     dp->dir = opendir(RSTRING(dirname)->ptr);
00281     if (dp->dir == NULL) {
00282         if (errno == EMFILE || errno == ENFILE) {
00283             rb_gc();
00284             dp->dir = opendir(RSTRING(dirname)->ptr);
00285         }
00286         if (dp->dir == NULL) {
00287             rb_sys_fail(RSTRING(dirname)->ptr);
00288         }
00289     }
00290     dp->path = strdup(RSTRING(dirname)->ptr);
00291 
00292     return dir;
00293 }
00294 
00295 /*
00296  *  call-seq:
00297  *     Dir.open( string ) => aDir
00298  *     Dir.open( string ) {| aDir | block } => anObject
00299  *
00300  *  With no block, <code>open</code> is a synonym for
00301  *  <code>Dir::new</code>. If a block is present, it is passed
00302  *  <i>aDir</i> as a parameter. The directory is closed at the end of
00303  *  the block, and <code>Dir::open</code> returns the value of the
00304  *  block.
00305  */
00306 
00307 static VALUE
00308 dir_s_open(klass, dirname)
00309     VALUE klass, dirname;
00310 {
00311     struct dir_data *dp;
00312     VALUE dir = Data_Make_Struct(klass, struct dir_data, 0, free_dir, dp);
00313 
00314     dir_initialize(dir, dirname);
00315     if (rb_block_given_p()) {
00316         return rb_ensure(rb_yield, dir, dir_close, dir);
00317     }
00318 
00319     return dir;
00320 }
00321 
00322 static void
00323 dir_closed()
00324 {
00325     rb_raise(rb_eIOError, "closed directory");
00326 }
00327 
00328 #define GetDIR(obj, dirp) do {\
00329     Data_Get_Struct(obj, struct dir_data, dirp);\
00330     if (dirp->dir == NULL) dir_closed();\
00331 } while (0)
00332 
00333 /*
00334  *  call-seq:
00335  *     dir.path => string or nil
00336  *
00337  *  Returns the path parameter passed to <em>dir</em>'s constructor.
00338  *
00339  *     d = Dir.new("..")
00340  *     d.path   #=> ".."
00341  */
00342 static VALUE
00343 dir_path(dir)
00344     VALUE dir;
00345 {
00346     struct dir_data *dirp;
00347 
00348     GetDIR(dir, dirp);
00349     if (!dirp->path) return Qnil;
00350     return rb_str_new2(dirp->path);
00351 }
00352 
00353 /*
00354  *  call-seq:
00355  *     dir.read => string or nil
00356  *
00357  *  Reads the next entry from <em>dir</em> and returns it as a string.
00358  *  Returns <code>nil</code> at the end of the stream.
00359  *
00360  *     d = Dir.new("testdir")
00361  *     d.read   #=> "."
00362  *     d.read   #=> ".."
00363  *     d.read   #=> "config.h"
00364  */
00365 static VALUE
00366 dir_read(dir)
00367     VALUE dir;
00368 {
00369     struct dir_data *dirp;
00370     struct dirent *dp;
00371 
00372     GetDIR(dir, dirp);
00373     errno = 0;
00374     dp = readdir(dirp->dir);
00375     if (dp) {
00376         return rb_tainted_str_new(dp->d_name, NAMLEN(dp));
00377     }
00378     else if (errno == 0) {      /* end of stream */
00379         return Qnil;
00380     }
00381     else {
00382         rb_sys_fail(0);
00383     }
00384     return Qnil;                /* not reached */
00385 }
00386 
00387 /*
00388  *  call-seq:
00389  *     dir.each { |filename| block }  => dir
00390  *
00391  *  Calls the block once for each entry in this directory, passing the
00392  *  filename of each entry as a parameter to the block.
00393  *
00394  *     d = Dir.new("testdir")
00395  *     d.each  {|x| puts "Got #{x}" }
00396  *
00397  *  <em>produces:</em>
00398  *
00399  *     Got .
00400  *     Got ..
00401  *     Got config.h
00402  *     Got main.rb
00403  */
00404 static VALUE
00405 dir_each(dir)
00406     VALUE dir;
00407 {
00408     struct dir_data *dirp;
00409     struct dirent *dp;
00410 
00411     GetDIR(dir, dirp);
00412     rewinddir(dirp->dir);
00413     for (dp = readdir(dirp->dir); dp != NULL; dp = readdir(dirp->dir)) {
00414         rb_yield(rb_tainted_str_new(dp->d_name, NAMLEN(dp)));
00415         if (dirp->dir == NULL) dir_closed();
00416     }
00417     return dir;
00418 }
00419 
00420 /*
00421  *  call-seq:
00422  *     dir.pos => integer
00423  *     dir.tell => integer
00424  *
00425  *  Returns the current position in <em>dir</em>. See also
00426  *  <code>Dir#seek</code>.
00427  *
00428  *     d = Dir.new("testdir")
00429  *     d.tell   #=> 0
00430  *     d.read   #=> "."
00431  *     d.tell   #=> 12
00432  */
00433 static VALUE
00434 dir_tell(dir)
00435     VALUE dir;
00436 {
00437 #ifdef HAVE_TELLDIR
00438     struct dir_data *dirp;
00439     long pos;
00440 
00441     GetDIR(dir, dirp);
00442     pos = telldir(dirp->dir);
00443     return rb_int2inum(pos);
00444 #else
00445     rb_notimplement();
00446 #endif
00447 }
00448 
00449 /*
00450  *  call-seq:
00451  *     dir.seek( integer ) => dir
00452  *
00453  *  Seeks to a particular location in <em>dir</em>. <i>integer</i>
00454  *  must be a value returned by <code>Dir#tell</code>.
00455  *
00456  *     d = Dir.new("testdir")   #=> #<Dir:0x401b3c40>
00457  *     d.read                   #=> "."
00458  *     i = d.tell               #=> 12
00459  *     d.read                   #=> ".."
00460  *     d.seek(i)                #=> #<Dir:0x401b3c40>
00461  *     d.read                   #=> ".."
00462  */
00463 static VALUE
00464 dir_seek(dir, pos)
00465     VALUE dir, pos;
00466 {
00467     struct dir_data *dirp;
00468     off_t p = NUM2OFFT(pos);
00469 
00470     GetDIR(dir, dirp);
00471 #ifdef HAVE_SEEKDIR
00472     seekdir(dirp->dir, p);
00473     return dir;
00474 #else
00475     rb_notimplement();
00476 #endif
00477 }
00478 
00479 /*
00480  *  call-seq:
00481  *     dir.pos( integer ) => integer
00482  *
00483  *  Synonym for <code>Dir#seek</code>, but returns the position
00484  *  parameter.
00485  *
00486  *     d = Dir.new("testdir")   #=> #<Dir:0x401b3c40>
00487  *     d.read                   #=> "."
00488  *     i = d.pos                #=> 12
00489  *     d.read                   #=> ".."
00490  *     d.pos = i                #=> 12
00491  *     d.read                   #=> ".."
00492  */
00493 static VALUE
00494 dir_set_pos(dir, pos)
00495     VALUE dir, pos;
00496 {
00497     dir_seek(dir, pos);
00498     return pos;
00499 }
00500 
00501 /*
00502  *  call-seq:
00503  *     dir.rewind => dir
00504  *
00505  *  Repositions <em>dir</em> to the first entry.
00506  *
00507  *     d = Dir.new("testdir")
00508  *     d.read     #=> "."
00509  *     d.rewind   #=> #<Dir:0x401b3fb0>
00510  *     d.read     #=> "."
00511  */
00512 static VALUE
00513 dir_rewind(dir)
00514     VALUE dir;
00515 {
00516     struct dir_data *dirp;
00517 
00518     GetDIR(dir, dirp);
00519     rewinddir(dirp->dir);
00520     return dir;
00521 }
00522 
00523 /*
00524  *  call-seq:
00525  *     dir.close => nil
00526  *
00527  *  Closes the directory stream. Any further attempts to access
00528  *  <em>dir</em> will raise an <code>IOError</code>.
00529  *
00530  *     d = Dir.new("testdir")
00531  *     d.close   #=> nil
00532  */
00533 static VALUE
00534 dir_close(dir)
00535     VALUE dir;
00536 {
00537     struct dir_data *dirp;
00538 
00539     GetDIR(dir, dirp);
00540     closedir(dirp->dir);
00541     dirp->dir = NULL;
00542 
00543     return Qnil;
00544 }
00545 
00546 static void
00547 dir_chdir(path)
00548     VALUE path;
00549 {
00550     if (chdir(RSTRING(path)->ptr) < 0)
00551         rb_sys_fail(RSTRING(path)->ptr);
00552 }
00553 
00554 static int chdir_blocking = 0;
00555 static VALUE chdir_thread = Qnil;
00556 
00557 struct chdir_data {
00558     VALUE old_path, new_path;
00559     int done;
00560 };
00561 
00562 static VALUE
00563 chdir_yield(args)
00564     struct chdir_data *args;
00565 {
00566     dir_chdir(args->new_path);
00567     args->done = Qtrue;
00568     chdir_blocking++;
00569     if (chdir_thread == Qnil)
00570         chdir_thread = rb_thread_current();
00571     return rb_yield(args->new_path);
00572 }
00573 
00574 static VALUE
00575 chdir_restore(args)
00576     struct chdir_data *args;
00577 {
00578     if (args->done) {
00579         chdir_blocking--;
00580         if (chdir_blocking == 0)
00581             chdir_thread = Qnil;
00582         dir_chdir(args->old_path);
00583     }
00584     return Qnil;
00585 }
00586 
00587 /*
00588  *  call-seq:
00589  *     Dir.chdir( [ string] ) => 0
00590  *     Dir.chdir( [ string] ) {| path | block }  => anObject
00591  *
00592  *  Changes the current working directory of the process to the given
00593  *  string. When called without an argument, changes the directory to
00594  *  the value of the environment variable <code>HOME</code>, or
00595  *  <code>LOGDIR</code>. <code>SystemCallError</code> (probably
00596  *  <code>Errno::ENOENT</code>) if the target directory does not exist.
00597  *
00598  *  If a block is given, it is passed the name of the new current
00599  *  directory, and the block is executed with that as the current
00600  *  directory. The original working directory is restored when the block
00601  *  exits. The return value of <code>chdir</code> is the value of the
00602  *  block. <code>chdir</code> blocks can be nested, but in a
00603  *  multi-threaded program an error will be raised if a thread attempts
00604  *  to open a <code>chdir</code> block while another thread has one
00605  *  open.
00606  *
00607  *     Dir.chdir("/var/spool/mail")
00608  *     puts Dir.pwd
00609  *     Dir.chdir("/tmp") do
00610  *       puts Dir.pwd
00611  *       Dir.chdir("/usr") do
00612  *         puts Dir.pwd
00613  *       end
00614  *       puts Dir.pwd
00615  *     end
00616  *     puts Dir.pwd
00617  *
00618  *  <em>produces:</em>
00619  *
00620  *     /var/spool/mail
00621  *     /tmp
00622  *     /usr
00623  *     /tmp
00624  *     /var/spool/mail
00625  */
00626 static VALUE
00627 dir_s_chdir(argc, argv, obj)
00628     int argc;
00629     VALUE *argv;
00630     VALUE obj;
00631 {
00632     VALUE path = Qnil;
00633 
00634     rb_secure(2);
00635     if (rb_scan_args(argc, argv, "01", &path) == 1) {
00636         SafeStringValue(path);
00637     }
00638     else {
00639         const char *dist = getenv("HOME");
00640         if (!dist) {
00641             dist = getenv("LOGDIR");
00642             if (!dist) rb_raise(rb_eArgError, "HOME/LOGDIR not set");
00643         }
00644         path = rb_str_new2(dist);
00645     }
00646 
00647     if (chdir_blocking > 0) {
00648         if (!rb_block_given_p() || rb_thread_current() != chdir_thread)
00649             rb_warn("conflicting chdir during another chdir block");
00650     }
00651 
00652     if (rb_block_given_p()) {
00653         struct chdir_data args;
00654         char *cwd = my_getcwd();
00655 
00656         args.old_path = rb_tainted_str_new2(cwd); free(cwd);
00657         args.new_path = path;
00658         args.done = Qfalse;
00659         return rb_ensure(chdir_yield, (VALUE)&args, chdir_restore, (VALUE)&args);
00660     }
00661     dir_chdir(path);
00662 
00663     return INT2FIX(0);
00664 }
00665 
00666 /*
00667  *  call-seq:
00668  *     Dir.getwd => string
00669  *     Dir.pwd => string
00670  *
00671  *  Returns the path to the current working directory of this process as
00672  *  a string.
00673  *
00674  *     Dir.chdir("/tmp")   #=> 0
00675  *     Dir.getwd           #=> "/tmp"
00676  */
00677 static VALUE
00678 dir_s_getwd(dir)
00679     VALUE dir;
00680 {
00681     char *path;
00682     VALUE cwd;
00683 
00684     rb_secure(4);
00685     path = my_getcwd();
00686     cwd = rb_tainted_str_new2(path);
00687 
00688     free(path);
00689     return cwd;
00690 }
00691 
00692 static void check_dirname (volatile VALUE *);
00693 static void
00694 check_dirname(dir)
00695     volatile VALUE *dir;
00696 {
00697     char *path, *pend;
00698 
00699     SafeStringValue(*dir);
00700     rb_secure(2);
00701     path = RSTRING(*dir)->ptr;
00702     if (path && *(pend = rb_path_end(rb_path_skip_prefix(path)))) {
00703         *dir = rb_str_new(path, pend - path);
00704     }
00705 }
00706 
00707 /*
00708  *  call-seq:
00709  *     Dir.chroot( string ) => 0
00710  *
00711  *  Changes this process's idea of the file system root. Only a
00712  *  privileged process may make this call. Not available on all
00713  *  platforms. On Unix systems, see <code>chroot(2)</code> for more
00714  *  information.
00715  */
00716 static VALUE
00717 dir_s_chroot(dir, path)
00718     VALUE dir, path;
00719 {
00720 #if defined(HAVE_CHROOT) && !defined(__CHECKER__)
00721     check_dirname(&path);
00722 
00723     if (chroot(RSTRING(path)->ptr) == -1)
00724         rb_sys_fail(RSTRING(path)->ptr);
00725 
00726     return INT2FIX(0);
00727 #else
00728     rb_notimplement();
00729     return Qnil;                /* not reached */
00730 #endif
00731 }
00732 
00733 /*
00734  *  call-seq:
00735  *     Dir.mkdir( string [, integer] ) => 0
00736  *
00737  *  Makes a new directory named by <i>string</i>, with permissions
00738  *  specified by the optional parameter <i>anInteger</i>. The
00739  *  permissions may be modified by the value of
00740  *  <code>File::umask</code>, and are ignored on NT. Raises a
00741  *  <code>SystemCallError</code> if the directory cannot be created. See
00742  *  also the discussion of permissions in the class documentation for
00743  *  <code>File</code>.
00744  *
00745  */
00746 static VALUE
00747 dir_s_mkdir(argc, argv, obj)
00748     int argc;
00749     VALUE *argv;
00750     VALUE obj;
00751 {
00752     VALUE path, vmode;
00753     int mode;
00754 
00755     if (rb_scan_args(argc, argv, "11", &path, &vmode) == 2) {
00756         mode = NUM2INT(vmode);
00757     }
00758     else {
00759         mode = 0777;
00760     }
00761 
00762     check_dirname(&path);
00763     if (mkdir(RSTRING(path)->ptr, mode) == -1)
00764         rb_sys_fail(RSTRING(path)->ptr);
00765 
00766     return INT2FIX(0);
00767 }
00768 
00769 /*
00770  *  call-seq:
00771  *     Dir.delete( string ) => 0
00772  *     Dir.rmdir( string ) => 0
00773  *     Dir.unlink( string ) => 0
00774  *
00775  *  Deletes the named directory. Raises a subclass of
00776  *  <code>SystemCallError</code> if the directory isn't empty.
00777  */
00778 static VALUE
00779 dir_s_rmdir(obj, dir)
00780     VALUE obj, dir;
00781 {
00782     check_dirname(&dir);
00783     if (rmdir(RSTRING(dir)->ptr) < 0)
00784         rb_sys_fail(RSTRING(dir)->ptr);
00785 
00786     return INT2FIX(0);
00787 }
00788 
00789 #define GLOB_VERBOSE    (1 << (sizeof(int) * CHAR_BIT - 1))
00790 #define sys_warning(val) \
00791     ((flags & GLOB_VERBOSE) && rb_protect((VALUE (*)(VALUE))rb_sys_warning, (VALUE)(val), 0))
00792 
00793 /* Return nonzero if S has any special globbing chars in it.  */
00794 static int
00795 has_magic(s, send, flags)
00796     const char *s, *send;
00797     int flags;
00798 {
00799     register const char *p = s;
00800     register char c;
00801     int open = 0;
00802     int escape = !(flags & FNM_NOESCAPE);
00803 
00804     while ((c = *p++) != '\0') {
00805         switch (c) {
00806           case '?':
00807           case '*':
00808             return Qtrue;
00809 
00810           case '[':     /* Only accept an open brace if there is a close */
00811             open++;     /* brace to match it.  Bracket expressions must be */
00812             continue;   /* complete, according to Posix.2 */
00813           case ']':
00814             if (open)
00815                 return Qtrue;
00816             continue;
00817 
00818           case '\\':
00819             if (escape && *p++ == '\0')
00820                 return Qfalse;
00821         }
00822 
00823         if (send && p >= send) break;
00824     }
00825     return Qfalse;
00826 }
00827 
00828 static char*
00829 extract_path(p, pend)
00830     const char *p, *pend;
00831 {
00832     char *alloc;
00833     int len;
00834 
00835     len = pend - p;
00836     alloc = ALLOC_N(char, len+1);
00837     memcpy(alloc, p, len);
00838     if (len > 1 && pend[-1] == '/'
00839 #if defined DOSISH_DRIVE_LETTER
00840     && pend[-2] != ':'
00841 #endif
00842     ) {
00843         alloc[len-1] = 0;
00844     }
00845     else {
00846         alloc[len] = 0;
00847     }
00848 
00849     return alloc;
00850 }
00851 
00852 static char*
00853 extract_elem(path)
00854     const char *path;
00855 {
00856     const char *pend;
00857 
00858     pend = strchr(path, '/');
00859     if (!pend) pend = path + strlen(path);
00860 
00861     return extract_path(path, pend);
00862 }
00863 
00864 static void
00865 remove_backslashes(p)
00866     char *p;
00867 {
00868     char *pend = p + strlen(p);
00869     char *t = p;
00870 
00871     while (p < pend) {
00872         if (*p == '\\') {
00873             if (++p == pend) break;
00874         }
00875         *t++ = *p++;
00876     }
00877     *t = '\0';
00878 }
00879 
00880 #ifndef S_ISDIR
00881 #   define S_ISDIR(m) ((m & S_IFMT) == S_IFDIR)
00882 #endif
00883 
00884 struct glob_args {
00885     void (*func) (const char*, VALUE);
00886     const char *c;
00887     VALUE v;
00888 };
00889 
00890 static VALUE glob_func_caller (VALUE);
00891 
00892 static VALUE
00893 glob_func_caller(val)
00894     VALUE val;
00895 {
00896     struct glob_args *args = (struct glob_args *)val;
00897 
00898     (*args->func)(args->c, args->v);
00899     return Qnil;
00900 }
00901 
00902 #define glob_call_func(func, path, arg) (*func)(path, arg)
00903 
00904 static int glob_helper (const char *path, const char *sub, int flags, int (*func(const char *,VALUE), VALUE arg));
00905 
00906 static int
00907 glob_helper(path, sub, flags, func, arg)
00908     const char *path;
00909     const char *sub;
00910     int flags;
00911     int (*func) (const char *, VALUE);
00912     VALUE arg;
00913 {
00914     struct stat st;
00915     const char *p, *m;
00916     int status = 0;
00917     char *buf = 0;
00918     char *newpath = 0;
00919 
00920     p = sub ? sub : path;
00921     if (!has_magic(p, 0, flags)) {
00922         if (
00923 #if defined DOSISH
00924             TRUE
00925 #else
00926             !(flags & FNM_NOESCAPE)
00927 #endif
00928             )
00929         {
00930             newpath = strdup(path);
00931             if (sub) {
00932                 p = newpath + (sub - path);
00933                 remove_backslashes(newpath + (sub - path));
00934                 sub = p;
00935             }
00936             else {
00937                 remove_backslashes(newpath);
00938                 p = path = newpath;
00939             }
00940         }
00941         if (lstat(path, &st) == 0) {
00942             status = glob_call_func(func, path, arg);
00943         }
00944         else if (errno != ENOENT) {
00945             /* In case stat error is other than ENOENT and
00946                we may want to know what is wrong. */
00947             sys_warning(path);
00948         }
00949         xfree(newpath);
00950         return status;
00951     }
00952 
00953     while (p && !status) {
00954         if (*p == '/') p++;
00955         m = strchr(p, '/');
00956         if (has_magic(p, m, flags)) {
00957             char *dir, *base, *magic;
00958             DIR *dirp;
00959             struct dirent *dp;
00960             int recursive = 0;
00961 
00962             struct d_link {
00963                 char *path;
00964                 struct d_link *next;
00965             } *tmp, *link, **tail = &link;
00966 
00967             base = extract_path(path, p);
00968             if (path == p) dir = ".";
00969             else dir = base;
00970 
00971             magic = extract_elem(p);
00972             if (stat(dir, &st) < 0) {
00973                 if (errno != ENOENT)
00974                     sys_warning(dir);
00975                 free(base);
00976                 free(magic);
00977                 break;
00978             }
00979             if (S_ISDIR(st.st_mode)) {
00980                 if (m && strcmp(magic, "**") == 0) {
00981                     int n = strlen(base);
00982                     recursive = 1;
00983                     REALLOC_N(buf, char, n+strlen(m)+3);
00984                     sprintf(buf, "%s%s", base, *base ? m : m+1);
00985                     status = glob_helper(buf, buf+n, flags, func, arg);
00986                     if (status) goto finalize;
00987                 }
00988                 dirp = opendir(dir);
00989                 if (dirp == NULL) {
00990                     sys_warning(dir);
00991                     free(base);
00992                     free(magic);
00993                     break;
00994                 }
00995             }
00996             else {
00997                 free(base);
00998                 free(magic);
00999                 break;
01000             }
01001 
01002 #if defined DOSISH_DRIVE_LETTER
01003 #define BASE (*base && !((isdirsep(*base) && !base[1]) || (base[1] == ':' && isdirsep(base[2]) && !base[3])))
01004 #else
01005 #define BASE (*base && !(isdirsep(*base) && !base[1]))
01006 #endif
01007 
01008             for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
01009                 if (recursive) {
01010                     if (strcmp(".", dp->d_name) == 0 || strcmp("..", dp->d_name) == 0)
01011                         continue;
01012                     if (fnmatch("*", dp->d_name, flags) != 0)
01013                         continue;
01014                     REALLOC_N(buf, char, strlen(base)+NAMLEN(dp)+strlen(m)+6);
01015                     sprintf(buf, "%s%s%s", base, (BASE) ? "/" : "", dp->d_name);
01016                     if (lstat(buf, &st) < 0) {
01017                         if (errno != ENOENT)
01018                             sys_warning(buf);
01019                         continue;
01020                     }
01021                     if (S_ISDIR(st.st_mode)) {
01022                         char *t = buf+strlen(buf);
01023                         strcpy(t, "
01208                 if (*p == '{') {
01209                     nest = 1;
01210                     while (*++p != '}' || --nest) {
01211                         if (*p == '{') nest++;
01212                     }
01213                 }
01214             }
01215             REALLOC_N(buf, char, len+1);
01216             memcpy(buf, s, lbrace-s);
01217             b = buf + (lbrace-s);
01218             memcpy(b, t, p-t);
01219             strcpy(b+(p-t), rbrace+1);
01220             status = push_braces(ary, buf, flags);
01221             if (status) break;
01222         }
01223     }
01224     else {
01225         status = push_globs(ary, str, flags);
01226     }
01227     xfree(buf);
01228 
01229     return status;
01230 }
01231 
01232 #define isdelim(c) ((c)=='\0')
01233 
01234 static VALUE
01235 rb_push_glob(str, flags)
01236     VALUE str;
01237     int flags;
01238 {
01239     const char *p, *pend, *buf;
01240     int nest, maxnest;
01241     int status = 0;
01242     int noescape = flags & FNM_NOESCAPE;
01243     VALUE ary;
01244 
01245     ary = rb_ary_new();
01246     SafeStringValue(str);
01247     p = RSTRING(str)->ptr;
01248     pend = p + RSTRING(str)->len;
01249 
01250     while (p < pend) {
01251         nest = maxnest = 0;
01252         while (p < pend && isdelim(*p)) p++;
01253         buf = p;
01254         while (p < pend && !isdelim(*p)) {
01255             if (*p == '{') nest++, maxnest++;
01256             if (*p == '}') nest--;
01257             if (!noescape && *p == '\\') {
01258                 if (++p == pend) break;
01259             }
01260             p++;
01261         }
01262         if (maxnest == 0) {
01263             status = push_globs(ary, buf, flags);
01264             if (status) break;
01265         }
01266         else if (nest == 0) {
01267             status = push_braces(ary, buf, flags);
01268             if (status) break;
01269         }
01270         /* else unmatched braces */
01271     }
01272     if (status) rb_jump_tag(status);
01273     if (rb_block_given_p()) {
01274         rb_ary_each(ary);
01275         return Qnil;
01276     }
01277     return ary;
01278 }
01279 
01280 /*
01281  *  call-seq:
01282  *     Dir[ string ] => array
01283  *
01284  *  Equivalent to calling
01285  *  <em>dir</em>.<code>glob(</code><i>string,</i><code>0)</code>.
01286  *
01287  */
01288 static VALUE
01289 dir_s_aref(obj, str)
01290     VALUE obj, str;
01291 {
01292     return rb_push_glob(str, 0);
01293 }
01294 
01295 /*
01296  *  call-seq:
01297  *     Dir.glob( string, [flags] ) => array
01298  *     Dir.glob( string, [flags] ) {| filename | block }  => nil
01299  *
01300  *  Returns the filenames found by expanding the pattern given in
01301  *  <i>string</i>, either as an <i>array</i> or as parameters to the
01302  *  block. Note that this pattern is not a regexp (it's closer to a
01303  *  shell glob). See <code>File::fnmatch</code> for the meaning of
01304  *  the <i>flags</i> parameter.
01305  *
01306  *  <code>*</code>::        Matches any file. Can be restricted by
01307  *                          other values in the glob. <code>*</code>
01308  *                          will match all files; <code>c*</code> will
01309  *                          match all files beginning with
01310  *                          <code>c</code>; <code>*c</code> will match
01311  *                          all files ending with <code>c</code>; and
01312  *                          <code>*c*</code> will match all files that
01313  *                          have <code>c</code> in them (including at
01314  *                          the beginning or end). Equivalent to
01315  *                          <code>/ .* /x</code> in regexp.
01316  *  <code>**</code>::       Matches directories recursively.
01317  *  <code>?</code>::        Matches any one character. Equivalent to
01318  *                          <code>/.{1}/</code> in regexp.
01319  *  <code>[set]</code>::    Matches any one character in +set+.
01320  *                          Behaves exactly like character sets in
01321  *                          Regexp, including set negation
01322  *                          (<code>[^a-z]</code>).
01323  *  <code>{p,q}</code>::    Matches either literal <code>p</code> or
01324  *                          literal <code>q</code>. Matching literals
01325  *                          may be more than one character in length.
01326  *                          More than two literals may be specified.
01327  *                          Equivalent to pattern alternation in
01328  *                          regexp.
01329  *  <code></code>::        Escapes the next metacharacter.
01330  *
01331  *     Dir["config.?"]                     #=> ["config.h"]
01332  *     Dir.glob("config.?")                #=> ["config.h"]
01333  *     Dir.glob("*.[a-z][a-z]")            #=> ["main.rb"]
01334  *     Dir.glob("*.[^r]*")                 #=> ["config.h"]
01335  *     Dir.glob("*.{rb,h}")                #=> ["main.rb", "config.h"]
01336  *     Dir.glob("*")                       #=> ["config.h", "main.rb"]
01337  *     Dir.glob("*", File::FNM_DOTMATCH)   #=> [".", "..", "config.h", "main.rb"]
01338  *
01339  *     rbfiles = File.join("**", "*.rb")
01340  *     Dir.glob(rbfiles)                   #=> ["main.rb",
01341  *                                              "lib/song.rb",
01342  *                                              "lib/song/karaoke.rb"]
01343  *     libdirs = File.join("**", "lib")
01344  *     Dir.glob(libdirs)                   #=> ["lib"]
01345  *
01346  *     librbfiles = File.join("**", "lib", "**", "*.rb")
01347  *     Dir.glob(librbfiles)                #=> ["lib/song.rb",
01348  *                                              "lib/song/karaoke.rb"]
01349  *
01350  *     librbfiles = File.join("**", "lib", "*.rb")
01351  *     Dir.glob(librbfiles)                #=> ["lib/song.rb"]
01352  */
01353 static VALUE
01354 dir_s_glob(argc, argv, obj)
01355     int argc;
01356     VALUE *argv;
01357     VALUE obj;
01358 {
01359     VALUE str, rflags;
01360     int flags;
01361 
01362     if (rb_scan_args(argc, argv, "11", &str, &rflags) == 2)
01363         flags = NUM2INT(rflags);
01364     else
01365         flags = 0;
01366 
01367     return rb_push_glob(str, flags);
01368 }
01369 
01370 static VALUE
01371 dir_open_dir(path)
01372     VALUE path;
01373 {
01374     VALUE dir = rb_funcall(rb_cDir, rb_intern("open"), 1, path);
01375 
01376     if (TYPE(dir) != T_DATA ||
01377         RDATA(dir)->dfree != (RUBY_DATA_FUNC)free_dir) {
01378         rb_raise(rb_eTypeError, "wrong argument type %s (expected Dir)",
01379                  rb_obj_classname(dir));
01380     }
01381     return dir;
01382 }
01383 
01384 
01385 /*
01386  *  call-seq:
01387  *     Dir.foreach( dirname ) {| filename | block }  => nil
01388  *
01389  *  Calls the block once for each entry in the named directory, passing
01390  *  the filename of each entry as a parameter to the block.
01391  *
01392  *     Dir.foreach("testdir") {|x| puts "Got #{x}" }
01393  *
01394  *  <em>produces:</em>
01395  *
01396  *     Got .
01397  *     Got ..
01398  *     Got config.h
01399  *     Got main.rb
01400  *
01401  */
01402 static VALUE
01403 dir_foreach(io, dirname)
01404     VALUE io, dirname;
01405 {
01406     VALUE dir;
01407 
01408     dir = dir_open_dir(dirname);
01409     rb_ensure(dir_each, dir, dir_close, dir);
01410     return Qnil;
01411 }
01412 
01413 /*
01414  *  call-seq:
01415  *     Dir.entries( dirname ) => array
01416  *
01417  *  Returns an array containing all of the filenames in the given
01418  *  directory. Will raise a <code>SystemCallError</code> if the named
01419  *  directory doesn't exist.
01420  *
01421  *     Dir.entries("testdir")   #=> [".", "..", "config.h", "main.rb"]
01422  *
01423  */
01424 static VALUE
01425 dir_entries(io, dirname)
01426     VALUE io, dirname;
01427 {
01428     VALUE dir;
01429 
01430     dir = dir_open_dir(dirname);
01431     return rb_ensure(rb_Array, dir, dir_close, dir);
01432 }
01433 
01434 /*
01435  *  call-seq:
01436  *     File.fnmatch( pattern, path, [flags] ) => (true or false)
01437  *     File.fnmatch?( pattern, path, [flags] ) => (true or false)
01438  *
01439  *  Returns true if <i>path</i> matches against <i>pattern</i> The
01440  *  pattern is not a regular expression; instead it follows rules
01441  *  similar to shell filename globbing. It may contain the following
01442  *  metacharacters:
01443  *
01444  *  <code>*</code>::        Matches any file. Can be restricted by
01445  *                          other values in the glob. <code>*</code>
01446  *                          will match all files; <code>c*</code> will
01447  *                          match all files beginning with
01448  *                          <code>c</code>; <code>*c</code> will match
01449  *                          all files ending with <code>c</code>; and
01450  *                          <code>*c*</code> will match all files that
01451  *                          have <code>c</code> in them (including at
01452  *                          the beginning or end). Equivalent to
01453  *                          <code>/ .* /x</code> in regexp.
01454  *  <code>?</code>::        Matches any one character. Equivalent to
01455  *                          <code>/.{1}/</code> in regexp.
01456  *  <code>[set]</code>::    Matches any one character in +set+.
01457  *                          Behaves exactly like character sets in
01458  *                          Regexp, including set negation
01459  *                          (<code>[^a-z]</code>).
01460  *  <code></code>::        Escapes the next metacharacter.
01461  *
01462  *  <i>flags</i> is a bitwise OR of the <code>FNM_xxx</code>
01463  *  parameters. The same glob pattern and flags are used by
01464  *  <code>Dir::glob</code>.
01465  *
01466  *     File.fnmatch('cat',       'cat')        #=> true
01467  *     File.fnmatch('cat',       'category')   #=> false
01468  *     File.fnmatch('c{at,ub}s', 'cats')       #=> false
01469  *     File.fnmatch('c{at,ub}s', 'cubs')       #=> false
01470  *     File.fnmatch('c{at,ub}s', 'cat')        #=> false
01471  *
01472  *     File.fnmatch('c?t',    'cat')                       #=> true
01473  *     File.fnmatch('c\?t',   'cat')                       #=> false
01474  *     File.fnmatch('c??t',   'cat')                       #=> false
01475  *     File.fnmatch('c*',     'cats')                      #=> true
01476  *     File.fnmatch('c/ * FIXME * /t', 'c/a/b/c/t')                 #=> true
01477  *     File.fnmatch('c*t',    'cat')                       #=> true
01478  *     File.fnmatch('c\at',   'cat')                       #=> true
01479  *     File.fnmatch('c\at',   'cat', File::FNM_NOESCAPE)   #=> false
01480  *     File.fnmatch('a?b',    'a/b')                       #=> true
01481  *     File.fnmatch('a?b',    'a/b', File::FNM_PATHNAME)   #=> false
01482  *
01483  *     File.fnmatch('*',   '.profile')                            #=> false
01484  *     File.fnmatch('*',   '.profile', File::FNM_DOTMATCH)        #=> true
01485  *     File.fnmatch('*',   'dave/.profile')                       #=> true
01486  *     File.fnmatch('*',   'dave/.profile', File::FNM_DOTMATCH)   #=> true
01487  *     File.fnmatch('*',   'dave/.profile', File::FNM_PATHNAME)   #=> false
01488  *     File.fnmatch('* / FIXME *', 'dave/.profile', File::FNM_PATHNAME)   #=> false
01489  *     STRICT = File::FNM_PATHNAME | File::FNM_DOTMATCH
01490  *     File.fnmatch('* / FIXME *', 'dave/.profile', STRICT)               #=> true
01491  */
01492 static VALUE
01493 file_s_fnmatch(argc, argv, obj)
01494     int argc;
01495     VALUE *argv;
01496     VALUE obj;
01497 {
01498     VALUE pattern, path;
01499     VALUE rflags;
01500     int flags;
01501 
01502     if (rb_scan_args(argc, argv, "21", &pattern, &path, &rflags) == 3)
01503         flags = NUM2INT(rflags);
01504     else
01505         flags = 0;
01506 
01507     StringValue(pattern);
01508     StringValue(path);
01509 
01510     if (fnmatch(RSTRING(pattern)->ptr, RSTRING(path)->ptr, flags) == 0)
01511         return Qtrue;
01512 
01513     return Qfalse;
01514 }
01515 
01516 /*
01517  *  Objects of class <code>Dir</code> are directory streams representing
01518  *  directories in the underlying file system. They provide a variety of
01519  *  ways to list directories and their contents. See also
01520  *  <code>File</code>.
01521  *
01522  *  The directory used in these examples contains the two regular files
01523  *  (<code>config.h</code> and <code>main.rb</code>), the parent
01524  *  directory (<code>..</code>), and the directory itself
01525  *  (<code>.</code>).
01526  */
01527 void
01528 Init_Dir()
01529 {
01530     rb_cDir = rb_define_class("Dir", rb_cObject);
01531 
01532     rb_include_module(rb_cDir, rb_mEnumerable);
01533 
01534     rb_define_alloc_func(rb_cDir, dir_s_alloc);
01535     rb_define_singleton_method(rb_cDir, "open", dir_s_open, 1);
01536     rb_define_singleton_method(rb_cDir, "foreach", dir_foreach, 1);
01537     rb_define_singleton_method(rb_cDir, "entries", dir_entries, 1);
01538 
01539     rb_define_method(rb_cDir,"initialize", dir_initialize, 1);
01540     rb_define_method(rb_cDir,"path", dir_path, 0);
01541     rb_define_method(rb_cDir,"read", dir_read, 0);
01542     rb_define_method(rb_cDir,"each", dir_each, 0);
01543     rb_define_method(rb_cDir,"rewind", dir_rewind, 0);
01544     rb_define_method(rb_cDir,"tell", dir_tell, 0);
01545     rb_define_method(rb_cDir,"seek", dir_seek, 1);
01546     rb_define_method(rb_cDir,"pos", dir_tell, 0);
01547     rb_define_method(rb_cDir,"pos=", dir_set_pos, 1);
01548     rb_define_method(rb_cDir,"close", dir_close, 0);
01549 
01550     rb_define_singleton_method(rb_cDir,"chdir", dir_s_chdir, -1);
01551     rb_define_singleton_method(rb_cDir,"getwd", dir_s_getwd, 0);
01552     rb_define_singleton_method(rb_cDir,"pwd", dir_s_getwd, 0);
01553     rb_define_singleton_method(rb_cDir,"chroot", dir_s_chroot, 1);
01554     rb_define_singleton_method(rb_cDir,"mkdir", dir_s_mkdir, -1);
01555     rb_define_singleton_method(rb_cDir,"rmdir", dir_s_rmdir, 1);
01556     rb_define_singleton_method(rb_cDir,"delete", dir_s_rmdir, 1);
01557     rb_define_singleton_method(rb_cDir,"unlink", dir_s_rmdir, 1);
01558 
01559     rb_define_singleton_method(rb_cDir,"glob", dir_s_glob, -1);
01560     rb_define_singleton_method(rb_cDir,"[]", dir_s_aref, 1);
01561 
01562     rb_define_singleton_method(rb_cFile,"fnmatch", file_s_fnmatch, -1);
01563     rb_define_singleton_method(rb_cFile,"fnmatch?", file_s_fnmatch, -1);
01564 
01565     rb_file_const("FNM_NOESCAPE", INT2FIX(FNM_NOESCAPE));
01566     rb_file_const("FNM_PATHNAME", INT2FIX(FNM_PATHNAME));
01567     rb_file_const("FNM_DOTMATCH", INT2FIX(FNM_DOTMATCH));
01568     rb_file_const("FNM_CASEFOLD", INT2FIX(FNM_CASEFOLD));
01569 }
01570 

Generated on Wed Jan 18 23:31:58 2006 for Ruby by doxygen 1.3.5