Hi.

>But I think SEGV is not good even if so. I'll fix it.
>
># And I found my own bug on ruby 1.9 while survey.
># seekdir in win32/win32.c must update `bitpos`. :-(

This is patch. This is still hot, so I'll commit after my brain
cool down.

/////////////////////////////////////////
// ruby 1.9

Index: dir.h
===================================================================
RCS file: /src/ruby/win32/dir.h,v
retrieving revision 1.9
diff -u -w -b -p -r1.9 dir.h
--- dir.h	19 Jan 2004 12:28:14 -0000	1.9
+++ dir.h	31 Dec 2005 09:05:39 -0000
@@ -21,9 +21,9 @@ typedef struct {
     char *curr;
     long size;
     long nfiles;
+    long loc;  /* [0, nfiles) */
     struct direct dirstr;
     char *bits;  /* used for d_isdir and d_isrep */
-    long bitpos; /* used for d_isdir and d_isrep */
 } DIR;
 
 
Index: win32.c
===================================================================
RCS file: /src/ruby/win32/win32.c,v
retrieving revision 1.182
diff -u -w -b -p -r1.182 win32.c
--- win32.c	28 Nov 2005 04:21:24 -0000	1.182
+++ win32.c	31 Dec 2005 09:05:43 -0000
@@ -1345,8 +1345,8 @@ rb_w32_cmdvector(const char *cmd, char *
 // return the pointer to the current file name. 
 //
 
-#define GetBit(bits, i) ((bits)[(i) / 8] &  (1 << (i) % 8))
-#define SetBit(bits, i) ((bits)[(i) / 8] |= (1 << (i) % 8))
+#define GetBit(bits, loc, i) (bits[(loc * 2 + i) / 8] &  (1 << (loc * 2 + i) % 8))
+#define SetBit(bits, loc, i) (bits[(loc * 2 + i) / 8] |= (1 << (loc * 2 + i) % 8))
 
 DIR *
 rb_w32_opendir(const char *filename)
@@ -1413,9 +1413,9 @@ rb_w32_opendir(const char *filename)
     p->bits = ALLOC_N(char, 1);
     p->bits[0] = 0;
     if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
-	SetBit(p->bits, 0);
+	SetBit(p->bits, p->nfiles, 0);
     if (fd.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
-	SetBit(p->bits, 1);
+	SetBit(p->bits, p->nfiles, 1);
     p->nfiles++;
     
     //
@@ -1448,9 +1448,9 @@ rb_w32_opendir(const char *filename)
 	    p->bits[p->nfiles / 4] = 0;
 	}
 	if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
-	    SetBit(p->bits, p->nfiles * 2);
+	    SetBit(p->bits, p->nfiles, 0);
 	if (fd.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
-	    SetBit(p->bits, p->nfiles * 2 + 1);
+	    SetBit(p->bits, p->nfiles, 1);
 
 	p->nfiles++;
 	idx += len+1;
@@ -1461,6 +1461,21 @@ rb_w32_opendir(const char *filename)
     return p;
 }
 
+//
+// Move to next entry
+//
+
+static void
+move_to_next_entry(DIR *dirp)
+{
+    if (dirp->curr) {
+	dirp->loc++;
+	dirp->curr += strlen(dirp->curr) + 1;
+	if (dirp->curr >= (dirp->start + dirp->size)) {
+	    dirp->curr = NULL;
+	}
+    }
+}
 
 //
 // Readdir just returns the current string pointer and bumps the
@@ -1470,7 +1485,6 @@ rb_w32_opendir(const char *filename)
 struct direct  *
 rb_w32_readdir(DIR *dirp)
 {
-    int         len;
     static int  dummy = 0;
 
     if (dirp->curr) {
@@ -1479,9 +1493,8 @@ rb_w32_readdir(DIR *dirp)
 	// first set up the structure to return
 	//
 
-	len = strlen(dirp->curr);
 	strcpy(dirp->dirstr.d_name, dirp->curr);
-	dirp->dirstr.d_namlen = len;
+	dirp->dirstr.d_namlen = strlen(dirp->curr);
 
 	//
 	// Fake inode
@@ -1491,19 +1504,14 @@ rb_w32_readdir(DIR *dirp)
 	//
 	// Attributes
 	//
-	dirp->dirstr.d_isdir = GetBit(dirp->bits, dirp->bitpos);
-	dirp->bitpos++;
-	dirp->dirstr.d_isrep = GetBit(dirp->bits, dirp->bitpos);
-	dirp->bitpos++;
+	dirp->dirstr.d_isdir = GetBit(dirp->bits, dirp->loc, 0);
+	dirp->dirstr.d_isrep = GetBit(dirp->bits, dirp->loc, 1);
 
 	//
 	// Now set up for the next call to readdir
 	//
 
-	dirp->curr += len + 1;
-	if (dirp->curr >= (dirp->start + dirp->size)) {
-	    dirp->curr = NULL;
-	}
+	move_to_next_entry(dirp);
 
 	return &(dirp->dirstr);
 
@@ -1518,7 +1526,7 @@ rb_w32_readdir(DIR *dirp)
 long
 rb_w32_telldir(DIR *dirp)
 {
-	return (long) dirp->curr;	/* ouch! pointer to long cast */
+    return dirp->loc;
 }
 
 //
@@ -1528,7 +1536,11 @@ rb_w32_telldir(DIR *dirp)
 void
 rb_w32_seekdir(DIR *dirp, long loc)
 {
-	dirp->curr = (char *) loc;	/* ouch! long to pointer cast */
+    rb_w32_rewinddir(dirp);
+
+    while (--loc >= 0) {
+	move_to_next_entry(dirp);
+    }
 }
 
 //
@@ -1539,7 +1551,7 @@ void
 rb_w32_rewinddir(DIR *dirp)
 {
 	dirp->curr = dirp->start;
-	dirp->bitpos = 0;
+    dirp->loc = 0;
 }
 
 //


///////////////////////////////////////////
// ruby 1.8 (keep binary compatibility)

Index: win32.c
===================================================================
RCS file: /src/ruby/win32/win32.c,v
retrieving revision 1.103.2.38
diff -u -w -b -p -r1.103.2.38 win32.c
--- win32.c	29 Dec 2005 19:55:58 -0000	1.103.2.38
+++ win32.c	31 Dec 2005 10:04:13 -0000
@@ -1448,6 +1448,21 @@ rb_w32_opendir(const char *filename)
     return p;
 }
 
+//
+// Peek next dir entry
+//
+
+static char *
+next_entry(DIR *dirp, char *p)
+{
+    if (p) {
+	p += strlen(p) + 1;
+	if (p >= (dirp->start + dirp->size)) {
+	    p = NULL;
+	}
+    }
+    return p;
+}
 
 //
 // Readdir just returns the current string pointer and bumps the
@@ -1457,7 +1472,6 @@ rb_w32_opendir(const char *filename)
 struct direct  *
 rb_w32_readdir(DIR *dirp)
 {
-    int         len;
     static int  dummy = 0;
 
     if (dirp->curr) {
@@ -1466,9 +1480,8 @@ rb_w32_readdir(DIR *dirp)
 	// first set up the structure to return
 	//
 
-	len = strlen(dirp->curr);
 	strcpy(dirp->dirstr.d_name, dirp->curr);
-	dirp->dirstr.d_namlen = len;
+	dirp->dirstr.d_namlen = strlen(dirp->curr);
 
 	//
 	// Fake inode
@@ -1479,10 +1492,7 @@ rb_w32_readdir(DIR *dirp)
 	// Now set up for the next call to readdir
 	//
 
-	dirp->curr += len + 1;
-	if (dirp->curr >= (dirp->start + dirp->size)) {
-	    dirp->curr = NULL;
-	}
+	dirp->curr = next_entry(dirp, dirp->curr);
 
 	return &(dirp->dirstr);
 
@@ -1497,7 +1507,12 @@ rb_w32_readdir(DIR *dirp)
 long
 rb_w32_telldir(DIR *dirp)
 {
-	return (long) dirp->curr;	/* ouch! pointer to long cast */
+    char *p; long loc = 0;
+
+    for (p = dirp->start; p != dirp->curr; p = next_entry(dirp, p)) {
+	loc++;
+    }
+    return loc;
 }
 
 //
@@ -1507,7 +1522,11 @@ rb_w32_telldir(DIR *dirp)
 void
 rb_w32_seekdir(DIR *dirp, long loc)
 {
-	dirp->curr = (char *) loc;	/* ouch! long to pointer cast */
+    dirp->curr = dirp->start;
+
+    while (--loc >= 0) {
+	dirp->curr = next_entry(dirp, dirp->curr);
+    }
 }
 
 //

////////////////////////////////////
// test case

require 'test/unit'

require 'tmpdir'
require 'fileutils'

class TestDir < Test::Unit::TestCase

  ROOT = File.join(Dir.tmpdir, "__test_dir__#{$$}")

  def setup
    Dir.mkdir(ROOT)
    for i in ?a..?z
      if i % 2 == 0
        FileUtils.touch(File.join(ROOT, i.chr))
      else
        FileUtils.mkdir(File.join(ROOT, i.chr))
      end
    end
  end

  def teardown
    FileUtils.rm_rf ROOT if File.directory?(ROOT)
  end

  def test_seek
    dir = Dir.open(ROOT)
    begin
      cache = []
      loop do
        pos = dir.tell
        break unless name = dir.read
        cache << [pos, name]
      end
      cache.sort_by { rand }
      for x in cache
        dir.seek(x[0])
        assert_equal(x[1], dir.read)
      end
    ensure
      dir.close
    end
  end
end