dir.c


DEFINITIONS

This source file includes following functions.
  1. NAMLEN
  2. NAMLEN
  3. NAMLEN
  4. CharNext
  5. CharNext
  6. find_dirsep
  7. range
  8. fnmatch
  9. free_dir
  10. dir_s_alloc
  11. dir_initialize
  12. dir_s_open
  13. dir_closed
  14. dir_path
  15. dir_read
  16. dir_each
  17. dir_tell
  18. dir_seek
  19. dir_set_pos
  20. dir_rewind
  21. dir_close
  22. dir_chdir
  23. chdir_restore
  24. dir_s_chdir
  25. dir_s_getwd
  26. dir_s_chroot
  27. dir_s_mkdir
  28. dir_s_rmdir
  29. has_magic
  30. extract_path
  31. extract_elem
  32. remove_backslashes
  33. S_ISDIR
  34. glob_helper
  35. rb_glob2
  36. rb_glob
  37. rb_globi
  38. push_pattern
  39. push_globs
  40. push_braces
  41. rb_push_glob
  42. dir_s_aref
  43. dir_s_glob
  44. dir_foreach
  45. dir_entries
  46. file_s_fnmatch
  47. Init_Dir


   1  /**********************************************************************
   2  
   3    dir.c -
   4  
   5    $Author: knu $
   6    $Date: 2002/06/15 10:24:38 $
   7    created at: Wed Jan  5 09:51:01 JST 1994
   8  
   9    Copyright (C) 1993-2002 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.h"
  16  
  17  #include <sys/types.h>
  18  #include <sys/stat.h>
  19  
  20  #ifdef HAVE_SYS_PARAM_H
  21  # include <sys/param.h>
  22  #else
  23  # define MAXPATHLEN 1024
  24  #endif
  25  #ifdef HAVE_UNISTD_H
  26  #include <unistd.h>
  27  #endif
  28  
  29  #if defined HAVE_DIRENT_H && !defined NT
  30  # include <dirent.h>
  31  # define NAMLEN(dirent) strlen((dirent)->d_name)
  32  #elif defined HAVE_DIRECT_H && !defined NT
  33  # include <direct.h>
  34  # define NAMLEN(dirent) strlen((dirent)->d_name)
  35  #else
  36  # define dirent direct
  37  # define NAMLEN(dirent) (dirent)->d_namlen
  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  # if defined(NT)
  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 "util.h"
  65  
  66  #ifndef HAVE_LSTAT
  67  #define lstat(path,st) stat(path,st)
  68  #endif
  69  
  70  #define FNM_NOESCAPE    0x01
  71  #define FNM_PATHNAME    0x02
  72  #define FNM_DOTMATCH    0x04
  73  #define FNM_CASEFOLD    0x08
  74  
  75  #define FNM_NOMATCH     1
  76  #define FNM_ERROR       2
  77  
  78  #define downcase(c) (nocase && ISUPPER(c) ? tolower(c) : (c))
  79  
  80  #ifndef CharNext                /* defined as CharNext[AW] on Windows. */
  81  # if defined(DJGPP)
  82  #   define CharNext(p) ((p) + mblen(p, MB_CUR_MAX))
  83  # else
  84  #   define CharNext(p) ((p) + 1)
  85  # endif
  86  #endif
  87  #if defined DOSISH
  88  #define isdirsep(c) ((c) == '/' || (c) == '\\')
  89  static char *
  90  find_dirsep(s)
  91      char *s;
  92  {
  93      while (*s) {
  94          if (isdirsep(*s))
  95              return s;
  96          s = CharNext(s);
  97      }
  98      return 0;
  99  }
 100  #else
 101  #define isdirsep(c) ((c) == '/')
 102  #define find_dirsep(s) strchr(s, '/')
 103  #endif
 104  
 105  static char *
 106  range(pat, test, flags)
 107      char *pat;
 108      char test;
 109      int flags;
 110  {
 111      int not, ok = 0;
 112      int nocase = flags & FNM_CASEFOLD;
 113      int escape = !(flags & FNM_NOESCAPE);
 114  
 115      not = *pat == '!' || *pat == '^';
 116      if (not)
 117          pat++;
 118  
 119      test = downcase(test);
 120  
 121      while (*pat) {
 122          int cstart, cend;
 123          cstart = cend = *pat++;
 124          if (cstart == ']')
 125              return ok == not ? 0 : pat;
 126          else if (escape && cstart == '\\')
 127              cstart = cend = *pat++;
 128          if (*pat == '-' && pat[1] != ']') {
 129              if (escape && pat[1] == '\\')
 130                  pat++;
 131              cend = pat[1];
 132              if (!cend)
 133                  return 0;
 134              pat += 2;
 135          }
 136          if (downcase(cstart) <= test && test <= downcase(cend))
 137              ok = 1;
 138      }
 139      return 0;
 140  }
 141  
 142  #define ISDIRSEP(c) (pathname && isdirsep(c))
 143  #define PERIOD(s) (period && *(s) == '.' && \
 144                    ((s) == string || ISDIRSEP((s)[-1])))
 145  static int
 146  fnmatch(pat, string, flags)
 147      const char *pat;
 148      const char *string;
 149      int flags;
 150  {
 151      int c;
 152      int test;
 153      const char *s = string;
 154      int escape = !(flags & FNM_NOESCAPE);
 155      int pathname = flags & FNM_PATHNAME;
 156      int period = !(flags & FNM_DOTMATCH);
 157      int nocase = flags & FNM_CASEFOLD;
 158  
 159      while (c = *pat++) {
 160          switch (c) {
 161          case '?':
 162              if (!*s || ISDIRSEP(*s) || PERIOD(s))
 163                  return FNM_NOMATCH;
 164              s++;
 165              break;
 166          case '*':
 167              while ((c = *pat++) == '*')
 168                  ;
 169  
 170              if (PERIOD(s))
 171                  return FNM_NOMATCH;
 172  
 173              if (!c) {
 174                  if (pathname && find_dirsep(s))
 175                      return FNM_NOMATCH;
 176                  else
 177                      return 0;
 178              }
 179              else if (ISDIRSEP(c)) {
 180                  s = find_dirsep(s);
 181                  if (s) {
 182                      s++;
 183                      break;
 184                  }
 185                  return FNM_NOMATCH;
 186              }
 187  
 188              test = escape && c == '\\' ? *pat : c;
 189              test = downcase(test);
 190              pat--;
 191              while (*s) {
 192                  if ((c == '[' || downcase(*s) == test) &&
 193                      !fnmatch(pat, s, flags | FNM_DOTMATCH))
 194                      return 0;
 195                  else if (ISDIRSEP(*s))
 196                      break;
 197                  s++;
 198              }
 199              return FNM_NOMATCH;
 200        
 201          case '[':
 202              if (!*s || ISDIRSEP(*s) || PERIOD(s))
 203                  return FNM_NOMATCH;
 204              pat = range(pat, *s, flags);
 205              if (!pat)
 206                  return FNM_NOMATCH;
 207              s++;
 208              break;
 209  
 210          case '\\':
 211              if (escape
 212  #if defined DOSISH
 213                  && *pat && strchr("*?[\\", *pat)
 214  #endif
 215                  ) {
 216                  c = *pat;
 217                  if (!c)
 218                      c = '\\';
 219                  else
 220                      pat++;
 221              }
 222              /* FALLTHROUGH */
 223  
 224          default:
 225  #if defined DOSISH
 226              if (ISDIRSEP(c) && isdirsep(*s))
 227                  ;
 228              else
 229  #endif
 230              if(downcase(c) != downcase(*s))
 231                  return FNM_NOMATCH;
 232              s++;
 233              break;
 234          }
 235      }
 236      return !*s ? 0 : FNM_NOMATCH;
 237  }
 238  
 239  VALUE rb_cDir;
 240  
 241  struct dir_data {
 242      DIR *dir;
 243      char *path;
 244  };
 245  
 246  static void
 247  free_dir(dir)
 248      struct dir_data *dir;
 249  {
 250      if (dir && dir->dir) closedir(dir->dir);
 251  }
 252  
 253  static VALUE dir_close _((VALUE));
 254  
 255  static VALUE
 256  dir_s_alloc(klass)
 257      VALUE klass;
 258  {
 259      struct dir_data *dirp;
 260      VALUE obj = Data_Make_Struct(klass, struct dir_data, 0, free_dir, dirp);
 261  
 262      dirp->dir = NULL;
 263      dirp->path = NULL;
 264  
 265      return obj;
 266  }
 267  
 268  static VALUE
 269  dir_initialize(dir, dirname)
 270      VALUE dir, dirname;
 271  {
 272      struct dir_data *dp;
 273  
 274      SafeStringValue(dirname);
 275      Data_Get_Struct(dir, struct dir_data, dp);
 276      if (dp->dir) closedir(dp->dir);
 277      if (dp->path) free(dp->path);
 278      dp->dir = NULL;
 279      dp->path = NULL;
 280      dp->dir = opendir(RSTRING(dirname)->ptr);
 281      if (dp->dir == NULL) {
 282          if (errno == EMFILE || errno == ENFILE) {
 283              rb_gc();
 284              dp->dir = opendir(RSTRING(dirname)->ptr);
 285          }
 286          if (dp->dir == NULL) {
 287              rb_sys_fail(RSTRING(dirname)->ptr);
 288          }
 289      }
 290      dp->path = strdup(RSTRING(dirname)->ptr);
 291  
 292      return dir;
 293  }
 294  
 295  static VALUE
 296  dir_s_open(klass, dirname)
 297      VALUE klass, dirname;
 298  {
 299      struct dir_data *dp;
 300      VALUE dir = Data_Make_Struct(klass, struct dir_data, 0, free_dir, dp);
 301  
 302      dir_initialize(dir, dirname);
 303      if (rb_block_given_p()) {
 304          return rb_ensure(rb_yield, dir, dir_close, dir);
 305      }
 306  
 307      return dir;
 308  }
 309  
 310  static void
 311  dir_closed()
 312  {
 313      rb_raise(rb_eIOError, "closed directory");
 314  }
 315  
 316  #define GetDIR(obj, dirp) do {\
 317      Data_Get_Struct(obj, struct dir_data, dirp);\
 318      if (dirp->dir == NULL) dir_closed();\
 319  } while (0)
 320  
 321  static VALUE
 322  dir_path(dir)
 323      VALUE dir;
 324  {
 325      struct dir_data *dirp;
 326  
 327      GetDIR(dir, dirp);
 328      if (!dirp->path) return Qnil;
 329      return rb_str_new2(dirp->path);
 330  }
 331  
 332  static VALUE
 333  dir_read(dir)
 334      VALUE dir;
 335  {
 336      struct dir_data *dirp;
 337      struct dirent *dp;
 338  
 339      GetDIR(dir, dirp);
 340      errno = 0;
 341      dp = readdir(dirp->dir);
 342      if (dp) {
 343          return rb_tainted_str_new(dp->d_name, NAMLEN(dp));
 344      }
 345      else if (errno == 0) {      /* end of stream */
 346          return Qnil;
 347      }
 348      else {
 349          rb_sys_fail(0);
 350      }
 351      return Qnil;                /* not reached */
 352  }
 353  
 354  static VALUE
 355  dir_each(dir)
 356      VALUE dir;
 357  {
 358      struct dir_data *dirp;
 359      struct dirent *dp;
 360  
 361      GetDIR(dir, dirp);
 362      for (dp = readdir(dirp->dir); dp != NULL; dp = readdir(dirp->dir)) {
 363          rb_yield(rb_tainted_str_new(dp->d_name, NAMLEN(dp)));
 364          if (dirp->dir == NULL) dir_closed();
 365      }
 366      return dir;
 367  }
 368  
 369  static VALUE
 370  dir_tell(dir)
 371      VALUE dir;
 372  {
 373  #ifdef HAVE_TELLDIR
 374      struct dir_data *dirp;
 375      long pos;
 376  
 377      GetDIR(dir, dirp);
 378      pos = telldir(dirp->dir);
 379      return rb_int2inum(pos);
 380  #else
 381      rb_notimplement();
 382  #endif
 383  }
 384  
 385  static VALUE
 386  dir_seek(dir, pos)
 387      VALUE dir, pos;
 388  {
 389      struct dir_data *dirp;
 390  
 391  #ifdef HAVE_SEEKDIR
 392      GetDIR(dir, dirp);
 393      seekdir(dirp->dir, NUM2INT(pos));
 394      return dir;
 395  #else
 396      rb_notimplement();
 397  #endif
 398  }
 399  
 400  static VALUE
 401  dir_set_pos(dir, pos)
 402      VALUE dir, pos;
 403  {
 404      dir_seek(dir, pos);
 405      return pos;
 406  }
 407  
 408  static VALUE
 409  dir_rewind(dir)
 410      VALUE dir;
 411  {
 412      struct dir_data *dirp;
 413  
 414      GetDIR(dir, dirp);
 415      rewinddir(dirp->dir);
 416      return dir;
 417  }
 418  
 419  static VALUE
 420  dir_close(dir)
 421      VALUE dir;
 422  {
 423      struct dir_data *dirp;
 424  
 425      GetDIR(dir, dirp);
 426      closedir(dirp->dir);
 427      dirp->dir = NULL;
 428  
 429      return Qnil;
 430  }
 431  
 432  static void
 433  dir_chdir(path)
 434      const char *path;
 435  {
 436      if (chdir(path) < 0)
 437          rb_sys_fail(path);
 438  }
 439  
 440  static int chdir_blocking = 0;
 441  static VALUE chdir_thread = Qnil;
 442  
 443  static VALUE
 444  chdir_restore(path)
 445      char *path;
 446  {
 447      chdir_blocking--;
 448      if (chdir_blocking == 0)
 449          chdir_thread = Qnil;
 450      dir_chdir(path);
 451      free(path);
 452      return Qnil;
 453  }
 454  
 455  static VALUE
 456  dir_s_chdir(argc, argv, obj)
 457      int argc;
 458      VALUE *argv;
 459      VALUE obj;
 460  {
 461      VALUE path = Qnil;
 462      char *dist = "";
 463  
 464      rb_secure(2);
 465      if (rb_scan_args(argc, argv, "01", &path) == 1) {
 466          SafeStringValue(path);
 467          dist = RSTRING(path)->ptr;
 468      }
 469      else {
 470          dist = getenv("HOME");
 471          if (!dist) {
 472              dist = getenv("LOGDIR");
 473              if (!dist) rb_raise(rb_eArgError, "HOME/LOGDIR not set");
 474          }
 475      }
 476  
 477      if (chdir_blocking > 0) {
 478          if (!rb_block_given_p() || rb_thread_current() != chdir_thread)
 479              rb_warn("conflicting chdir during another chdir block");
 480      }
 481  
 482      if (rb_block_given_p()) {
 483          char *cwd = my_getcwd();
 484          chdir_blocking++;
 485          if (chdir_thread == Qnil)
 486              chdir_thread = rb_thread_current();
 487          dir_chdir(dist);
 488          return rb_ensure(rb_yield, path, chdir_restore, (VALUE)cwd);
 489      }
 490      dir_chdir(dist);
 491  
 492      return INT2FIX(0);
 493  }
 494  
 495  static VALUE
 496  dir_s_getwd(dir)
 497      VALUE dir;
 498  {
 499      char *path = my_getcwd();
 500      VALUE cwd = rb_tainted_str_new2(path);
 501  
 502      free(path);
 503      return cwd;
 504  }
 505  
 506  static VALUE
 507  dir_s_chroot(dir, path)
 508      VALUE dir, path;
 509  {
 510  #if defined(HAVE_CHROOT) && !defined(__CHECKER__)
 511      rb_secure(2);
 512      SafeStringValue(path);
 513  
 514      if (chroot(RSTRING(path)->ptr) == -1)
 515          rb_sys_fail(RSTRING(path)->ptr);
 516  
 517      return INT2FIX(0);
 518  #else
 519      rb_notimplement();
 520      return Qnil;                /* not reached */
 521  #endif
 522  }
 523  
 524  static VALUE
 525  dir_s_mkdir(argc, argv, obj)
 526      int argc;
 527      VALUE *argv;
 528      VALUE obj;
 529  {
 530      VALUE path, vmode;
 531      int mode;
 532  
 533      if (rb_scan_args(argc, argv, "11", &path, &vmode) == 2) {
 534          mode = NUM2INT(vmode);
 535      }
 536      else {
 537          mode = 0777;
 538      }
 539  
 540      SafeStringValue(path);
 541      rb_secure(2);
 542  #if !defined(NT)
 543      if (mkdir(RSTRING(path)->ptr, mode) == -1)
 544          rb_sys_fail(RSTRING(path)->ptr);
 545  #else
 546      if (mkdir(RSTRING(path)->ptr) == -1)
 547          rb_sys_fail(RSTRING(path)->ptr);
 548  #endif
 549  
 550      return INT2FIX(0);
 551  }
 552  
 553  static VALUE
 554  dir_s_rmdir(obj, dir)
 555      VALUE obj, dir;
 556  {
 557      SafeStringValue(dir);
 558      rb_secure(2);
 559      if (rmdir(RSTRING(dir)->ptr) < 0)
 560          rb_sys_fail(RSTRING(dir)->ptr);
 561  
 562      return INT2FIX(0);
 563  }
 564  
 565  /* Return nonzero if S has any special globbing chars in it.  */
 566  static int
 567  has_magic(s, send, flags)
 568       char *s, *send;
 569       int flags;
 570  {
 571      register char *p = s;
 572      register char c;
 573      int open = 0;
 574      int escape = !(flags & FNM_NOESCAPE);
 575  
 576      while ((c = *p++) != '\0') {
 577          switch (c) {
 578            case '?':
 579            case '*':
 580              return Qtrue;
 581  
 582            case '[':     /* Only accept an open brace if there is a close */
 583              open++;     /* brace to match it.  Bracket expressions must be */
 584              continue;   /* complete, according to Posix.2 */
 585            case ']':
 586              if (open)
 587                  return Qtrue;
 588              continue;
 589  
 590            case '\\':
 591              if (escape && *p++ == '\0')
 592                  return Qfalse;
 593          }
 594  
 595          if (send && p >= send) break;
 596      }
 597      return Qfalse;
 598  }
 599  
 600  static char*
 601  extract_path(p, pend)
 602      char *p, *pend;
 603  {
 604      char *alloc;
 605      int len;
 606  
 607      len = pend - p;
 608      alloc = ALLOC_N(char, len+1);
 609      memcpy(alloc, p, len);
 610      if (len > 1 && pend[-1] == '/'
 611  #if defined DOSISH
 612      && pend[-2] != ':'
 613  #endif
 614      ) {
 615          alloc[len-1] = 0;
 616      }
 617      else {
 618          alloc[len] = 0;
 619      }
 620  
 621      return alloc;
 622  }
 623  
 624  static char*
 625  extract_elem(path)
 626      char *path;
 627  {
 628      char *pend;
 629  
 630      pend = strchr(path, '/');
 631      if (!pend) pend = path + strlen(path);
 632  
 633      return extract_path(path, pend);
 634  }
 635  
 636  static void
 637  remove_backslashes(p)
 638      char *p;
 639  {
 640      char *pend = p + strlen(p);
 641      char *t = p;
 642  
 643      while (p < pend) {
 644          if (*p == '\\') {
 645              if (++p == pend) break;
 646          }
 647          *t++ = *p++;
 648      }
 649      *t = '\0';
 650  }
 651  
 652  #ifndef S_ISDIR
 653  #   define S_ISDIR(m) ((m & S_IFMT) == S_IFDIR)
 654  #endif
 655  
 656  static void
 657  glob_helper(path, sub, flags, func, arg)
 658      char *path;
 659      char *sub;
 660      int flags;
 661      void (*func) _((const char*, VALUE));
 662      VALUE arg;
 663  {
 664      struct stat st;
 665      char *p, *m;
 666  
 667      p = sub ? sub : path;
 668      if (!has_magic(p, 0, flags)) {
 669  #if defined DOSISH
 670          remove_backslashes(path);
 671  #else
 672          if (!(flags & FNM_NOESCAPE)) remove_backslashes(p);
 673  #endif
 674          if (lstat(path, &st) == 0) {
 675              (*func)(path, arg);
 676          }
 677          else if (errno != ENOENT) {
 678              /* In case stat error is other than ENOENT and
 679                 we may want to know what is wrong. */
 680              rb_sys_warning(path);
 681          }
 682          return;
 683      }
 684  
 685      while (p) {
 686          if (*p == '/') p++;
 687          m = strchr(p, '/');
 688          if (has_magic(p, m, flags)) {
 689              char *dir, *base, *magic, *buf;
 690              DIR *dirp;
 691              struct dirent *dp;
 692              int recursive = 0;
 693  
 694              struct d_link {
 695                  char *path;
 696                  struct d_link *next;
 697              } *tmp, *link = 0;
 698  
 699              base = extract_path(path, p);
 700              if (path == p) dir = ".";
 701              else dir = base;
 702  
 703              magic = extract_elem(p);
 704              if (stat(dir, &st) < 0) {
 705                  if (errno != ENOENT) rb_sys_warning(dir);
 706                  free(base);
 707                  break;
 708              }
 709              if (S_ISDIR(st.st_mode)) {
 710                  if (m && strcmp(magic, "**") == 0) {
 711                      int n = strlen(base);
 712                      recursive = 1;
 713                      buf = ALLOC_N(char, n+strlen(m)+3);
 714                      sprintf(buf, "%s%s", base, *base ? m : m+1);
 715                      glob_helper(buf, buf+n, flags, func, arg);
 716                      free(buf);
 717                  }
 718                  dirp = opendir(dir);
 719                  if (dirp == NULL) {
 720                      rb_sys_warning(dir);
 721                      free(base);
 722                      break;
 723                  }
 724              }
 725              else {
 726                  free(base);
 727                  break;
 728              }
 729              
 730  #if defined DOSISH
 731  #define BASE (*base && !((isdirsep(*base) && !base[1]) || (base[1] == ':' && isdirsep(base[2]) && !base[3])))
 732  #else
 733  #define BASE (*base && !(*base == '/' && !base[1]))
 734  #endif
 735  
 736              for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
 737                  if (recursive) {
 738                      if (strcmp(".", dp->d_name) == 0 || strcmp("..", dp->d_name) == 0)
 739                          continue;
 740                      buf = ALLOC_N(char, strlen(base)+NAMLEN(dp)+strlen(m)+6);
 741                      sprintf(buf, "%s%s%s", base, (BASE) ? "/" : "", dp->d_name);
 742                      if (lstat(buf, &st) < 0) {
 743                          if (errno != ENOENT) rb_sys_warning(buf);
 744                          continue;
 745                      }
 746                      if (S_ISDIR(st.st_mode)) {
 747                          char *t = buf+strlen(buf);
 748                          strcpy(t, "/**");
 749                          strcpy(t+3, m);
 750                          glob_helper(buf, t, flags, func, arg);
 751                      }
 752                      free(buf);
 753                      continue;
 754                  }
 755                  if (fnmatch(magic, dp->d_name, flags) == 0) {
 756                      buf = ALLOC_N(char, strlen(base)+NAMLEN(dp)+2);
 757                      sprintf(buf, "%s%s%s", base, (BASE) ? "/" : "", dp->d_name);
 758                      if (!m) {
 759                          (*func)(buf, arg);
 760                          free(buf);
 761                          continue;
 762                      }
 763                      tmp = ALLOC(struct d_link);
 764                      tmp->path = buf;
 765                      tmp->next = link;
 766                      link = tmp;
 767                  }
 768              }
 769              closedir(dirp);
 770              free(base);
 771              free(magic);
 772              if (link) {
 773                  while (link) {
 774                      if (stat(link->path, &st) == 0) {
 775                          if (S_ISDIR(st.st_mode)) {
 776                              int len = strlen(link->path);
 777                              int mlen = strlen(m);
 778                              char *t = ALLOC_N(char, len+mlen+1);
 779  
 780                              sprintf(t, "%s%s", link->path, m);
 781                              glob_helper(t, t+len, flags, func, arg);
 782                              free(t);
 783                          }
 784                      }
 785                      else {
 786                          rb_sys_warning(link->path);
 787                      }
 788                      tmp = link;
 789                      link = link->next;
 790                      free(tmp->path);
 791                      free(tmp);
 792                  }
 793                  break;
 794              }
 795          }
 796          p = m;
 797      }
 798  }
 799  
 800  static void
 801  rb_glob2(path, flags, func, arg)
 802      char *path;
 803      int flags;
 804      void (*func) _((const char*, VALUE));
 805      VALUE arg;
 806  {
 807      glob_helper(path, 0, flags, func, arg);
 808  }
 809  
 810  void
 811  rb_glob(path, func, arg)
 812      char *path;
 813      void (*func) _((const char*, VALUE));
 814      VALUE arg;
 815  {
 816      rb_glob2(path, 0, func, arg);
 817  }
 818  
 819  void
 820  rb_globi(path, func, arg)
 821      char *path;
 822      void (*func) _((const char*, VALUE));
 823      VALUE arg;
 824  {
 825      rb_glob2(path, FNM_CASEFOLD, func, arg);
 826  }
 827  
 828  static void
 829  push_pattern(path, ary)
 830      const char *path;
 831      VALUE ary;
 832  {
 833      VALUE str = rb_tainted_str_new2(path);
 834  
 835      if (ary) {
 836          rb_ary_push(ary, str);
 837      }
 838      else {
 839          rb_yield(str);
 840      }
 841  }
 842  
 843  static void
 844  push_globs(ary, s, flags)
 845      VALUE ary;
 846      char *s;
 847      int flags;
 848  {
 849      rb_glob2(s, flags, push_pattern, ary);
 850  }
 851  
 852  static void
 853  push_braces(ary, s, flags)
 854      VALUE ary;
 855      char *s;
 856      int flags;
 857  {
 858      char *buf;
 859      char *p, *t, *b;
 860      char *lbrace, *rbrace;
 861      int nest = 0;
 862  
 863      p = s;
 864      lbrace = rbrace = 0;
 865      while (*p) {
 866          if (*p == '{') {
 867              lbrace = p;
 868              break;
 869          }
 870          p++;
 871      }
 872      while (*p) {
 873          if (*p == '{') nest++;
 874          if (*p == '}' && --nest == 0) {
 875              rbrace = p;
 876              break;
 877          }
 878          p++;
 879      }
 880  
 881      if (lbrace) {
 882          int len = strlen(s);
 883          buf = xmalloc(len + 1);
 884          memcpy(buf, s, lbrace-s);
 885          b = buf + (lbrace-s);
 886          p = lbrace;
 887          while (*p != '}') {
 888              t = p + 1;
 889              for (p = t; *p!='}' && *p!=','; p++) {
 890                  /* skip inner braces */
 891                  if (*p == '{') while (*p!='}') p++;
 892              }
 893              memcpy(b, t, p-t);
 894              strcpy(b+(p-t), rbrace+1);
 895              push_braces(ary, buf, flags);
 896          }
 897          free(buf);
 898      }
 899      else {
 900          push_globs(ary, s, flags);
 901      }
 902  }
 903  
 904  #define isdelim(c) ((c)=='\0')
 905  
 906  static VALUE
 907  rb_push_glob(str, flags)
 908      VALUE str;
 909      int flags;
 910  {
 911      char *p, *pend;
 912      char *buf;
 913      char *t;
 914      int nest, maxnest;
 915      int noescape = flags & FNM_NOESCAPE;
 916      VALUE ary;
 917  
 918      if (rb_block_given_p())
 919          ary = 0;
 920      else
 921          ary = rb_ary_new();
 922  
 923      SafeStringValue(str);
 924      buf = xmalloc(RSTRING(str)->len + 1);
 925  
 926      p = RSTRING(str)->ptr;
 927      pend = p + RSTRING(str)->len;
 928  
 929      while (p < pend) {
 930          t = buf;
 931          nest = maxnest = 0;
 932          while (p < pend && isdelim(*p)) p++;
 933          while (p < pend && !isdelim(*p)) {
 934              if (*p == '{') nest++, maxnest++;
 935              if (*p == '}') nest--;
 936              if (!noescape && *p == '\\') {
 937                  *t++ = *p++;
 938                  if (p == pend) break;
 939              }
 940              *t++ = *p++;
 941          }
 942          *t = '\0';
 943          if (maxnest == 0) {
 944              push_globs(ary, buf, flags);
 945          }
 946          else if (nest == 0) {
 947              push_braces(ary, buf, flags);
 948          }
 949          /* else unmatched braces */
 950      }
 951      free(buf);
 952  
 953      return ary;
 954  }
 955  
 956  static VALUE
 957  dir_s_aref(obj, str)
 958      VALUE obj, str;
 959  {
 960      return rb_push_glob(str, 0);
 961  }
 962  
 963  static VALUE
 964  dir_s_glob(argc, argv, obj)
 965      int argc;
 966      VALUE *argv;
 967      VALUE obj;
 968  {
 969      VALUE str, rflags;
 970      int flags;
 971  
 972      if (rb_scan_args(argc, argv, "11", &str, &rflags) == 2)
 973          flags = NUM2INT(rflags);
 974      else
 975          flags = 0;
 976  
 977      return rb_push_glob(str, flags);
 978  }
 979  
 980  static VALUE
 981  dir_foreach(io, dirname)
 982      VALUE io, dirname;
 983  {
 984      VALUE dir;
 985  
 986      dir = rb_funcall(rb_cDir, rb_intern("open"), 1, dirname);
 987      rb_ensure(dir_each, dir, dir_close, dir);
 988      return Qnil;
 989  }
 990  
 991  static VALUE
 992  dir_entries(io, dirname)
 993      VALUE io, dirname;
 994  {
 995      VALUE dir;
 996  
 997      dir = rb_funcall(rb_cDir, rb_intern("open"), 1, dirname);
 998      return rb_ensure(rb_Array, dir, dir_close, dir);
 999  }
1000  
1001  static VALUE
1002  file_s_fnmatch(argc, argv, obj)
1003      int argc;
1004      VALUE *argv;
1005      VALUE obj;
1006  {
1007      VALUE pattern, path;
1008      VALUE rflags;
1009      int flags;
1010  
1011      if (rb_scan_args(argc, argv, "21", &pattern, &path, &rflags) == 3)
1012          flags = NUM2INT(rflags);
1013      else
1014          flags = 0;
1015  
1016      StringValue(pattern);
1017      StringValue(path);
1018  
1019      if (fnmatch(RSTRING(pattern)->ptr, RSTRING(path)->ptr, flags) == 0)
1020          return Qtrue;
1021  
1022      return Qfalse;
1023  }
1024  
1025  void
1026  Init_Dir()
1027  {
1028      rb_cDir = rb_define_class("Dir", rb_cObject);
1029  
1030      rb_include_module(rb_cDir, rb_mEnumerable);
1031  
1032      rb_define_singleton_method(rb_cDir, "allocate", dir_s_alloc, 0);
1033      rb_define_singleton_method(rb_cDir, "open", dir_s_open, 1);
1034      rb_define_singleton_method(rb_cDir, "foreach", dir_foreach, 1);
1035      rb_define_singleton_method(rb_cDir, "entries", dir_entries, 1);
1036  
1037      rb_define_method(rb_cDir,"initialize", dir_initialize, 1);
1038      rb_define_method(rb_cDir,"path", dir_path, 0);
1039      rb_define_method(rb_cDir,"read", dir_read, 0);
1040      rb_define_method(rb_cDir,"each", dir_each, 0);
1041      rb_define_method(rb_cDir,"rewind", dir_rewind, 0);
1042      rb_define_method(rb_cDir,"tell", dir_tell, 0);
1043      rb_define_method(rb_cDir,"seek", dir_seek, 1);
1044      rb_define_method(rb_cDir,"pos", dir_tell, 0);
1045      rb_define_method(rb_cDir,"pos=", dir_set_pos, 1);
1046      rb_define_method(rb_cDir,"close", dir_close, 0);
1047  
1048      rb_define_singleton_method(rb_cDir,"chdir", dir_s_chdir, -1);
1049      rb_define_singleton_method(rb_cDir,"getwd", dir_s_getwd, 0);
1050      rb_define_singleton_method(rb_cDir,"pwd", dir_s_getwd, 0);
1051      rb_define_singleton_method(rb_cDir,"chroot", dir_s_chroot, 1);
1052      rb_define_singleton_method(rb_cDir,"mkdir", dir_s_mkdir, -1);
1053      rb_define_singleton_method(rb_cDir,"rmdir", dir_s_rmdir, 1);
1054      rb_define_singleton_method(rb_cDir,"delete", dir_s_rmdir, 1);
1055      rb_define_singleton_method(rb_cDir,"unlink", dir_s_rmdir, 1);
1056  
1057      rb_define_singleton_method(rb_cDir,"glob", dir_s_glob, -1);
1058      rb_define_singleton_method(rb_cDir,"[]", dir_s_aref, 1);
1059  
1060      rb_define_singleton_method(rb_cFile,"fnmatch", file_s_fnmatch, -1);
1061      rb_define_singleton_method(rb_cFile,"fnmatch?", file_s_fnmatch, -1);
1062  
1063      rb_file_const("FNM_NOESCAPE", INT2FIX(FNM_NOESCAPE));
1064      rb_file_const("FNM_PATHNAME", INT2FIX(FNM_PATHNAME));
1065      rb_file_const("FNM_DOTMATCH", INT2FIX(FNM_DOTMATCH));
1066      rb_file_const("FNM_CASEFOLD", INT2FIX(FNM_CASEFOLD));
1067  }