(linenum→info "unix/slp.c:2238")

ruby/1.9.0/dir.c

    1: /**********************************************************************
    2: 
    3:   dir.c -
    4: 
    5:   $Author: matz $
    6:   $Date: 2007-12-25 13:17:06 +0900 (Tue, 25 Dec 2007) $
    7:   created at: Wed Jan  5 09:51:01 JST 1994
    8: 
    9:   Copyright (C) 1993-2007 Yukihiro Matsumoto
   10:   Copyright (C) 2000  Network Applied Communication Laboratory, Inc.
   11:   Copyright (C) 2000  Information-technology Promotion Agency, Japan
   12: 
   13: **********************************************************************/
   14: 
   15: #include "ruby/ruby.h"
   16: 
   17: #include <sys/types.h>
   18: #include <sys/stat.h>
   19: 
   20: #ifdef HAVE_UNISTD_H
   21: #include <unistd.h>
   22: #endif
   23: 
   24: #if defined HAVE_DIRENT_H && !defined _WIN32
   25: # include <dirent.h>
   26: # define NAMLEN(dirent) strlen((dirent)->d_name)
   27: #elif defined HAVE_DIRECT_H && !defined _WIN32
   28: # include <direct.h>
   29: # define NAMLEN(dirent) strlen((dirent)->d_name)
   30: #else
   31: # define dirent direct
   32: # if !defined __NeXT__
   33: #  define NAMLEN(dirent) (dirent)->d_namlen
   34: # else
   35: #  /* On some versions of NextStep, d_namlen is always zero, so avoid it. */
   36: #  define NAMLEN(dirent) strlen((dirent)->d_name)
   37: # endif
   38: # if HAVE_SYS_NDIR_H
   39: #  include <sys/ndir.h>
   40: # endif
   41: # if HAVE_SYS_DIR_H
   42: #  include <sys/dir.h>
   43: # endif
   44: # if HAVE_NDIR_H
   45: #  include <ndir.h>
   46: # endif
   47: # ifdef _WIN32
   48: #  include "win32/dir.h"
   49: # endif
   50: #endif
   51: 
   52: #include <errno.h>
   53: 
   54: #ifndef HAVE_STDLIB_H
   55: char *getenv();
   56: #endif
   57: 
   58: #ifndef HAVE_STRING_H
   59: char *strchr(char*,char);
   60: #endif
   61: 
   62: #include <ctype.h>
   63: 
   64: #include "ruby/util.h"
   65: 
   66: #if !defined HAVE_LSTAT && !defined lstat
   67: #define lstat stat
   68: #endif
   69: 
   70: #ifndef CASEFOLD_FILESYSTEM
   71: # if defined DOSISH || defined __VMS
   72: #   define CASEFOLD_FILESYSTEM 1
   73: # else
   74: #   define CASEFOLD_FILESYSTEM 0
   75: # endif
   76: #endif
   77: 
   78: #define FNM_NOESCAPE    0x01
   79: #define FNM_PATHNAME    0x02
   80: #define FNM_DOTMATCH    0x04
   81: #define FNM_CASEFOLD    0x08
   82: #if CASEFOLD_FILESYSTEM
   83: #define FNM_SYSCASE     FNM_CASEFOLD
   84: #else
   85: #define FNM_SYSCASE     0
   86: #endif
   87: 
   88: #define FNM_NOMATCH     1
   89: #define FNM_ERROR       2
   90: 
   91: #define downcase(c) (nocase && ISUPPER(c) ? tolower(c) : (c))
   92: #define compare(c1, c2) (((unsigned char)(c1)) - ((unsigned char)(c2)))
   93: 
   94: /* caution: in case *p == '\0'
   95:    Next(p) == p + 1 in single byte environment
   96:    Next(p) == p     in multi byte environment
   97: */
   98: #if defined(CharNext)
   99: # define Next(p) CharNext(p)
  100: #elif defined(DJGPP)
  101: # define Next(p) ((p) + mblen(p, RUBY_MBCHAR_MAXSIZE))
  102: #elif defined(__EMX__)
  103: # define Next(p) ((p) + emx_mblen(p))
  104: static inline int
  105: emx_mblen(const char *p)
  106: {
  107:     int n = mblen(p, RUBY_MBCHAR_MAXSIZE);
  108:     return (n < 0) ? 1 : n;
  109: }
  110: #endif
  111: 
  112: #ifndef Next /* single byte environment */
  113: # define Next(p) ((p) + 1)
  114: # define Inc(p) (++(p))
  115: # define Compare(p1, p2) (compare(downcase(*(p1)), downcase(*(p2))))
  116: #else /* multi byte environment */
  117: # define Inc(p) ((p) = Next(p))
  118: # define Compare(p1, p2) (CompareImpl(p1, p2, nocase))
  119: static int
  120: CompareImpl(const char *p1, const char *p2, int nocase)
  121: {
  122:     const int len1 = Next(p1) - p1;
  123:     const int len2 = Next(p2) - p2;
  124: #ifdef _WIN32
  125:     char buf1[10], buf2[10]; /* large enough? */
  126: #endif
  127: 
  128:     if (len1 < 0 || len2 < 0) {
  129:         rb_fatal("CompareImpl: negative len");
  130:     }
  131: 
  132:     if (len1 == 0) return  len2;
  133:     if (len2 == 0) return -len1;
  134: 
  135: #ifdef _WIN32
  136:     if (nocase && rb_w32_iswinnt()) {
  137:         if (len1 > 1) {
  138:             if (len1 >= sizeof(buf1)) {
  139:                 rb_fatal("CompareImpl: too large len");
  140:             }
  141:             memcpy(buf1, p1, len1);
  142:             buf1[len1] = '\0';
  143:             CharLower(buf1);
  144:             p1 = buf1; /* trick */
  145:         }
  146:         if (len2 > 1) {
  147:             if (len2 >= sizeof(buf2)) {
  148:                 rb_fatal("CompareImpl: too large len");
  149:             }
  150:             memcpy(buf2, p2, len2);
  151:             buf2[len2] = '\0';
  152:             CharLower(buf2);
  153:             p2 = buf2; /* trick */
  154:         }
  155:     }
  156: #endif
  157:     if (len1 == 1)
  158:         if (len2 == 1)
  159:             return compare(downcase(*p1), downcase(*p2));
  160:         else {
  161:             const int ret = compare(downcase(*p1), *p2);
  162:             return ret ? ret : -1;
  163:         }
  164:     else
  165:         if (len2 == 1) {
  166:             const int ret = compare(*p1, downcase(*p2));
  167:             return ret ? ret : 1;
  168:         }
  169:         else {
  170:             const int ret = memcmp(p1, p2, len1 < len2 ? len1 : len2);
  171:             return ret ? ret : len1 - len2;
  172:         }
  173: }
  174: #endif /* environment */
  175: 
  176: static char *
  177: bracket(
  178:     const char *p, /* pattern (next to '[') */
  179:     const char *s, /* string */
  180:     int flags)
  181: {
  182:     const int nocase = flags & FNM_CASEFOLD;
  183:     const int escape = !(flags & FNM_NOESCAPE);
  184: 
  185:     int ok = 0, not = 0;
  186: 
  187:     if (*p == '!' || *p == '^') {
  188:         not = 1;
  189:         p++;
  190:     }
  191: 
  192:     while (*p != ']') {
  193:         const char *t1 = p;
  194:         if (escape && *t1 == '\\')
  195:             t1++;
  196:         if (!*t1)
  197:             return NULL;
  198:         p = Next(t1);
  199:         if (p[0] == '-' && p[1] != ']') {
  200:             const char *t2 = p + 1;
  201:             if (escape && *t2 == '\\')
  202:                 t2++;
  203:             if (!*t2)
  204:                 return NULL;
  205:             p = Next(t2);
  206:             if (!ok && Compare(t1, s) <= 0 && Compare(s, t2) <= 0)
  207:                 ok = 1;
  208:         }
  209:         else
  210:             if (!ok && Compare(t1, s) == 0)
  211:                 ok = 1;
  212:     }
  213: 
  214:     return ok == not ? NULL : (char *)p + 1;
  215: }
  216: 
  217: /* If FNM_PATHNAME is set, only path element will be matched. (upto '/' or '\0')
  218:    Otherwise, entire string will be matched.
  219:    End marker itself won't be compared.
  220:    And if function succeeds, *pcur reaches end marker.
  221: */
  222: #define UNESCAPE(p) (escape && *(p) == '\\' ? (p) + 1 : (p))
  223: #define ISEND(p) (!*(p) || (pathname && *(p) == '/'))
  224: #define RETURN(val) return *pcur = p, *scur = s, (val);
  225: 
  226: static int
  227: fnmatch_helper(
  228:     const char **pcur, /* pattern */
  229:     const char **scur, /* string */
  230:     int flags)
  231: {
  232:     const int period = !(flags & FNM_DOTMATCH);
  233:     const int pathname = flags & FNM_PATHNAME;
  234:     const int escape = !(flags & FNM_NOESCAPE);
  235:     const int nocase = flags & FNM_CASEFOLD;
  236: 
  237:     const char *ptmp = 0;
  238:     const char *stmp = 0;
  239: 
  240:     const char *p = *pcur;
  241:     const char *s = *scur;
  242: 
  243:     if (period && *s == '.' && *UNESCAPE(p) != '.') /* leading period */
  244:         RETURN(FNM_NOMATCH);
  245: 
  246:     while (1) {
  247:         switch (*p) {
  248:           case '*':
  249:             do { p++; } while (*p == '*');
  250:             if (ISEND(UNESCAPE(p))) {
  251:                 p = UNESCAPE(p);
  252:                 RETURN(0);
  253:             }
  254:             if (ISEND(s))
  255:                 RETURN(FNM_NOMATCH);
  256:             ptmp = p;
  257:             stmp = s;
  258:             continue;
  259: 
  260:           case '?':
  261:             if (ISEND(s))
  262:                 RETURN(FNM_NOMATCH);
  263:             p++;
  264:             Inc(s);
  265:             continue;
  266: 
  267:           case '[': {
  268:             const char *t;
  269:             if (ISEND(s))
  270:                 RETURN(FNM_NOMATCH);
  271:             if ((t = bracket(p + 1, s, flags)) != 0) {
  272:                 p = t;
  273:                 Inc(s);
  274:                 continue;
  275:             }
  276:             goto failed;
  277:           }
  278:         }
  279: 
  280:         /* ordinary */
  281:         p = UNESCAPE(p);
  282:         if (ISEND(s))
  283:             RETURN(ISEND(p) ? 0 : FNM_NOMATCH);
  284:         if (ISEND(p))
  285:             goto failed;
  286:         if (Compare(p, s) != 0)
  287:             goto failed;
  288:         Inc(p);
  289:         Inc(s);
  290:         continue;
  291: 
  292:       failed: /* try next '*' position */
  293:         if (ptmp && stmp) {
  294:             p = ptmp;
  295:             Inc(stmp); /* !ISEND(*stmp) */
  296:             s = stmp;
  297:             continue;
  298:         }
  299:         RETURN(FNM_NOMATCH);
  300:     }
  301: }
  302: 
  303: static int
  304: fnmatch(
  305:     const char *p, /* pattern */
  306:     const char *s, /* string */
  307:     int flags)
  308: {
  309:     const int period = !(flags & FNM_DOTMATCH);
  310:     const int pathname = flags & FNM_PATHNAME;
  311: 
  312:     const char *ptmp = 0;
  313:     const char *stmp = 0;
  314: 
  315:     if (pathname) {
  316:         while (1) {
  317:             if (p[0] == '*' && p[1] == '*' && p[2] == '/') {
  318:                 do { p += 3; } while (p[0] == '*' && p[1] == '*' && p[2] == '/');
  319:                 ptmp = p;
  320:                 stmp = s;
  321:             }
  322:             if (fnmatch_helper(&p, &s, flags) == 0) {
  323:                 while (*s && *s != '/') Inc(s);
  324:                 if (*p && *s) {
  325:                     p++;
  326:                     s++;
  327:                     continue;
  328:                 }
  329:                 if (!*p && !*s)
  330:                     return 0;
  331:             }
  332:             /* failed : try next recursion */
  333:             if (ptmp && stmp && !(period && *stmp == '.')) {
  334:                 while (*stmp && *stmp != '/') Inc(stmp);
  335:                 if (*stmp) {
  336:                     p = ptmp;
  337:                     stmp++;
  338:                     s = stmp;
  339:                     continue;
  340:                 }
  341:             }
  342:             return FNM_NOMATCH;
  343:         }
  344:     }
  345:     else
  346:         return fnmatch_helper(&p, &s, flags);
  347: }
  348: 
  349: VALUE rb_cDir;
  350: 
  351: struct dir_data {
  352:     DIR *dir;
  353:     char *path;
  354: };
  355: 
  356: static void
  357: free_dir(struct dir_data *dir)
  358: {
  359:     if (dir) {
  360:         if (dir->dir) closedir(dir->dir);
  361:         if (dir->path) free(dir->path);
  362:     }
  363:     free(dir);
  364: }
  365: 
  366: static VALUE dir_close(VALUE);
  367: 
  368: static VALUE
  369: dir_s_alloc(VALUE klass)
  370: {
  371:     struct dir_data *dirp;
  372:     VALUE obj = Data_Make_Struct(klass, struct dir_data, 0, free_dir, dirp);
  373: 
  374:     dirp->dir = NULL;
  375:     dirp->path = NULL;
  376: 
  377:     return obj;
  378: }
  379: 
  380: /*
  381:  *  call-seq:
  382:  *     Dir.new( string ) -> aDir
  383:  *
  384:  *  Returns a new directory object for the named directory.
  385:  */
  386: static VALUE
  387: dir_initialize(VALUE dir, VALUE dirname)
  388: {
  389:     struct dir_data *dp;
  390: 
  391:     FilePathValue(dirname);
  392:     Data_Get_Struct(dir, struct dir_data, dp);
  393:     if (dp->dir) closedir(dp->dir);
  394:     if (dp->path) free(dp->path);
  395:     dp->dir = NULL;
  396:     dp->path = NULL;
  397:     dp->dir = opendir(RSTRING_PTR(dirname));
  398:     if (dp->dir == NULL) {
  399:         if (errno == EMFILE || errno == ENFILE) {
  400:             rb_gc();
  401:             dp->dir = opendir(RSTRING_PTR(dirname));
  402:         }
  403:         if (dp->dir == NULL) {
  404:             rb_sys_fail(RSTRING_PTR(dirname));
  405:         }
  406:     }
  407:     dp->path = strdup(RSTRING_PTR(dirname));
  408: 
  409:     return dir;
  410: }
  411: 
  412: /*
  413:  *  call-seq:
  414:  *     Dir.open( string ) => aDir
  415:  *     Dir.open( string ) {| aDir | block } => anObject
  416:  *
  417:  *  With no block, <code>open</code> is a synonym for
  418:  *  <code>Dir::new</code>. If a block is present, it is passed
  419:  *  <i>aDir</i> as a parameter. The directory is closed at the end of
  420:  *  the block, and <code>Dir::open</code> returns the value of the
  421:  *  block.
  422:  */
  423: static VALUE
  424: dir_s_open(VALUE klass, VALUE dirname)
  425: {
  426:     struct dir_data *dp;
  427:     VALUE dir = Data_Make_Struct(klass, struct dir_data, 0, free_dir, dp);
  428: 
  429:     dir_initialize(dir, dirname);
  430:     if (rb_block_given_p()) {
  431:         return rb_ensure(rb_yield, dir, dir_close, dir);
  432:     }
  433: 
  434:     return dir;
  435: }
  436: 
  437: static void
  438: dir_closed(void)
  439: {
  440:     rb_raise(rb_eIOError, "closed directory");
  441: }
  442: 
  443: static void
  444: dir_check(VALUE dir)
  445: {
  446:     if (!OBJ_TAINTED(dir) && rb_safe_level() >= 4)
  447:         rb_raise(rb_eSecurityError, "Insecure: operation on untainted Dir");
  448:     rb_check_frozen(dir);
  449: }
  450: 
  451: #define GetDIR(obj, dirp) do {\
  452:     dir_check(dir);\
  453:     Data_Get_Struct(obj, struct dir_data, dirp);\
  454:     if (dirp->dir == NULL) dir_closed();\
  455: } while (0)
  456: 
  457: /*
  458:  *  call-seq:
  459:  *     dir.inspect => string
  460:  *
  461:  *  Return a string describing this Dir object.
  462:  */
  463: static VALUE
  464: dir_inspect(VALUE dir)
  465: {
  466:     struct dir_data *dirp;
  467: 
  468:     Data_Get_Struct(dir, struct dir_data, dirp);
  469:     if (dirp->path) {
  470:         char *c = rb_obj_classname(dir);
  471:         int len = strlen(c) + strlen(dirp->path) + 4;
  472:         VALUE s = rb_str_new(0, len);
  473:         snprintf(RSTRING_PTR(s), len+1, "#<%s:%s>", c, dirp->path);
  474:         return s;
  475:     }
  476:     return rb_funcall(dir, rb_intern("to_s"), 0, 0);
  477: }
  478: 
  479: /*
  480:  *  call-seq:
  481:  *     dir.path => string or nil
  482:  *
  483:  *  Returns the path parameter passed to <em>dir</em>'s constructor.
  484:  *
  485:  *     d = Dir.new("..")
  486:  *     d.path   #=> ".."
  487:  */
  488: static VALUE
  489: dir_path(VALUE dir)
  490: {
  491:     struct dir_data *dirp;
  492: 
  493:     Data_Get_Struct(dir, struct dir_data, dirp);
  494:     if (!dirp->path) return Qnil;
  495:     return rb_str_new2(dirp->path);
  496: }
  497: 
  498: /*
  499:  *  call-seq:
  500:  *     dir.read => string or nil
  501:  *
  502:  *  Reads the next entry from <em>dir</em> and returns it as a string.
  503:  *  Returns <code>nil</code> at the end of the stream.
  504:  *
  505:  *     d = Dir.new("testdir")
  506:  *     d.read   #=> "."
  507:  *     d.read   #=> ".."
  508:  *     d.read   #=> "config.h"
  509:  */
  510: static VALUE
  511: dir_read(VALUE dir)
  512: {
  513:     struct dir_data *dirp;
  514:     struct dirent *dp;
  515: 
  516:     GetDIR(dir, dirp);
  517:     errno = 0;
  518:     dp = readdir(dirp->dir);
  519:     if (dp) {
  520:         return rb_tainted_str_new(dp->d_name, NAMLEN(dp));
  521:     }
  522:     else if (errno == 0) {      /* end of stream */
  523:         return Qnil;
  524:     }
  525:     else {
  526:         rb_sys_fail(0);
  527:     }
  528:     return Qnil;                /* not reached */
  529: }
  530: 
  531: /*
  532:  *  call-seq:
  533:  *     dir.each { |filename| block }  => dir
  534:  *
  535:  *  Calls the block once for each entry in this directory, passing the
  536:  *  filename of each entry as a parameter to the block.
  537:  *
  538:  *     d = Dir.new("testdir")
  539:  *     d.each  {|x| puts "Got #{x}" }
  540:  *
  541:  *  <em>produces:</em>
  542:  *
  543:  *     Got .
  544:  *     Got ..
  545:  *     Got config.h
  546:  *     Got main.rb
  547:  */
  548: static VALUE
  549: dir_each(VALUE dir)
  550: {
  551:     struct dir_data *dirp;
  552:     struct dirent *dp;
  553: 
  554:     RETURN_ENUMERATOR(dir, 0, 0);
  555:     GetDIR(dir, dirp);
  556:     rewinddir(dirp->dir);
  557:     for (dp = readdir(dirp->dir); dp != NULL; dp = readdir(dirp->dir)) {
  558:         rb_yield(rb_tainted_str_new(dp->d_name, NAMLEN(dp)));
  559:         if (dirp->dir == NULL) dir_closed();
  560:     }
  561:     return dir;
  562: }
  563: 
  564: /*
  565:  *  call-seq:
  566:  *     dir.pos => integer
  567:  *     dir.tell => integer
  568:  *
  569:  *  Returns the current position in <em>dir</em>. See also
  570:  *  <code>Dir#seek</code>.
  571:  *
  572:  *     d = Dir.new("testdir")
  573:  *     d.tell   #=> 0
  574:  *     d.read   #=> "."
  575:  *     d.tell   #=> 12
  576:  */
  577: static VALUE
  578: dir_tell(VALUE dir)
  579: {
  580: #ifdef HAVE_TELLDIR
  581:     struct dir_data *dirp;
  582:     long pos;
  583: 
  584:     Data_Get_Struct(dir, struct dir_data, dirp);
  585:     pos = telldir(dirp->dir);
  586:     return rb_int2inum(pos);
  587: #else
  588:     rb_notimplement();
  589: #endif
  590: }
  591: 
  592: /*
  593:  *  call-seq:
  594:  *     dir.seek( integer ) => dir
  595:  *
  596:  *  Seeks to a particular location in <em>dir</em>. <i>in