From: "U.Nakamura" <usa@...> Date: 2001-07-26T11:31:46+09:00 Subject: [ruby-dev:14097] DOSISH file.c こんにちは、なかむら(う)@帰省中です。 最新のfile.cへのパッチです。 最新のfile.cからの差分に(たぶん)なっています。 1. \対応(オリジナルはCheng氏) 2. マルチバイト対応(1のため必須) 不完全かも... 3. ドライブ文字対応 4. [ruby-dev:14075] File.joinへのパッチは含んでいませんが、必要ではあると思います。 --- current/file.c Wed Jul 25 00:59:00 2001 +++ mytree/file.c Thu Jul 26 11:00:08 2001 @@ -1300,4 +1300,5 @@ rb_file_s_umask(argc, argv) #define isdirsep(x) ((x) == '/') #endif + #ifndef CharNext /* defined as CharNext[AW] on Windows. */ # if defined(DJGPP) @@ -1308,4 +1309,47 @@ rb_file_s_umask(argc, argv) #endif +#ifdef DOSISH +static inline int +has_drive_letter(buf) + char *buf; +{ + if (ISALPHA(buf[0]) && buf[1] == ':') { + return 1; + } + else { + return 0; + } +} + +static void +getcwdofdrv(drv, buf, len) + int drv; + char *buf; + int len; +{ + char drive[4]; + char oldcwd[MAXPATHLEN+1]; + + drive[0] = drv; + drive[1] = ':'; + drive[2] = '\0'; + + /* the only way that I know to get the current directory + of a particular drive is to change chdir() to that drive, + so save the old cwd before chdir() + */ + getcwd(oldcwd, MAXPATHLEN); + if (chdir(drive) == 0) { + getcwd(buf, len); + chdir(oldcwd); + } + else { + drive[2] = '/'; + drive[3] = '\0'; + strncpy(buf, drive, len); + } +} +#endif + static char * strrdirsep(path) @@ -1314,5 +1358,5 @@ strrdirsep(path) char *last = NULL; #ifdef DOSISH - if (ISALPHA(path[0]) && path[1] == ':') path += 2; + if (has_drive_letter(path)) path += 2; #endif while (*path) { @@ -1327,4 +1371,10 @@ strrdirsep(path) } +#if !defined(TOLOWER) +#define TOLOWER(c) (ISUPPER(c) ? tolower(c) : (c)) +#endif + +static int is_absolute_path _((const char*)); + VALUE rb_file_s_expand_path(argc, argv) @@ -1333,5 +1383,5 @@ rb_file_s_expand_path(argc, argv) { VALUE fname, dname; - char *s, *p; + char *s, *p, *t; char buf[MAXPATHLEN+2]; char *bend = buf + sizeof(buf) - 2; @@ -1352,4 +1402,8 @@ rb_file_s_expand_path(argc, argv) if (strlen(dir) > MAXPATHLEN) goto toolong; strcpy(buf, dir); + for (p = buf; *p && p < bend; p = CharNext(p)) { + if (isdirsep(*p)) *p = '/'; + } + if (p >= bend) goto toolong; p = &buf[strlen(buf)]; s++; @@ -1362,5 +1416,7 @@ rb_file_s_expand_path(argc, argv) #endif while (*s && !isdirsep(*s)) { - *p++ = *s++; + for (t = CharNext(s); s != t; s++) { + *p++ = *s; + } if (p >= bend) goto toolong; } @@ -1381,5 +1437,56 @@ rb_file_s_expand_path(argc, argv) #if defined DOSISH /* skip drive letter */ - else if (ISALPHA(s[0]) && s[1] == ':' && isdirsep(s[2])) { + else if (has_drive_letter(s)) { + if (isdirsep(s[2])) { + /* specified drive letter, and full path */ + /* skip drive letter */ + while (*s && !isdirsep(*s)) { + *p++ = *s++; + if (p >= bend) goto toolong; + } + } + else { + /* specified drive, but not full path */ + int same = 0; + if (!NIL_P(dname)) { + dname = rb_file_s_expand_path(1, &dname); + if (has_drive_letter(RSTRING(dname)->ptr) && + TOLOWER(*RSTRING(dname)->ptr) == TOLOWER(s[0])) { + /* ok, same drive */ + same = 1; + } + } + if (same) { + if (OBJ_TAINTED(dname)) tainted = 1; + if (strlen(RSTRING(dname)->ptr) > MAXPATHLEN) goto toolong; + strcpy(buf, RSTRING(dname)->ptr); + p = &buf[strlen(buf)]; + s += 2; + } + else { + getcwdofdrv(*s, buf, MAXPATHLEN); + s += 2; + + tainted = 1; + p = &buf[strlen(buf)]; + if (*s) { + /* need to append '/' before appending the rest of the path */ + if (!isdirsep(*(p - 1))) *p++ = '/'; + while (*s && !isdirsep(*s)) { + *p++ = *s++; + if (p >= bend) goto toolong; + } + } + } + } + } +#endif +#if defined DOSISH && ! defined(__CYGWIN__) + else if (isdirsep(*s) && !is_absolute_path(s)) { + /* specified full path, but not drive letter */ + /* we need to get the drive letter */ + tainted = 1; + getcwd(buf, MAXPATHLEN); + p = &buf[2]; while (*s && !isdirsep(*s)) { *p++ = *s++; @@ -1388,5 +1495,5 @@ rb_file_s_expand_path(argc, argv) } #endif - else if (!isdirsep(*s)) { + else if (!is_absolute_path(s)) { if (!NIL_P(dname)) { dname = rb_file_s_expand_path(1, &dname); @@ -1400,5 +1507,5 @@ rb_file_s_expand_path(argc, argv) } p = &buf[strlen(buf)]; - while (p > buf && *(p - 1) == '/') p--; + while (p > buf && strrdirsep(buf) == p - 1) p--; } else { @@ -1415,5 +1522,8 @@ rb_file_s_expand_path(argc, argv) switch (*s) { case '.': - if (*(s+1)) { + if (!isdirsep(*p)) { + *++p = '.'; + } + else if (*(s+1)) { switch (*++s) { case '.': @@ -1425,9 +1535,6 @@ rb_file_s_expand_path(argc, argv) else { *++p = '.'; - do { - *++p = '.'; - if (p >= bend) goto toolong; - } while (*++s == '.'); - --s; + *++p = *s; + if (p >= bend) goto toolong; } break; @@ -1436,5 +1543,6 @@ rb_file_s_expand_path(argc, argv) case '\\': #endif - if (!isdirsep(*p)) *++p = '/'; + *(p + 1) = '\0'; + if (strrdirsep(buf) != p) *++p = '/'; break; default: @@ -1447,7 +1555,12 @@ rb_file_s_expand_path(argc, argv) case '\\': #endif - if (!isdirsep(*p)) *++p = '/'; break; + *(p + 1) = '\0'; + if (strrdirsep(buf) != p) *++p = '/'; + break; default: - *++p = *s; + for (t = CharNext(s); s != t; s++) { + *++p = *s; + } + s--; if (p >= bend) goto toolong; } @@ -1455,5 +1568,13 @@ rb_file_s_expand_path(argc, argv) /* Place a \0 at end. If path ends with a "/", delete it */ - if (p == buf || !isdirsep(*p)) p++; + *(p + 1) = '\0'; + if (p == buf || strrdirsep(buf) != p) p++; +#if defined(DOSISH) + if (has_drive_letter(buf) && isdirsep(buf[2])) { + /* root directory needs a trailing backslash, + otherwise it mean the current directory of the drive */ + if (p == (buf+2)) p++; + } +#endif *p = '\0'; @@ -1505,4 +1626,16 @@ rb_file_s_basename(argc, argv) p = strrdirsep(name); if (!p) { +#if defined(DOSISH) + if (has_drive_letter(name)) { + name += 2; + if (NIL_P(fext)) { + f = strlen(name); + } + else { + f = rmext(name, ext); + } + } + else +#endif if (NIL_P(fext) || !(f = rmext(name, ext))) return fname; @@ -1532,8 +1665,19 @@ rb_file_s_dirname(klass, fname) p = strrdirsep(name); if (!p) { +#if defined(DOSISH) + if (has_drive_letter(name)) { + dirname = rb_str_new(name, 2); + OBJ_INFECT(dirname, fname); + return dirname; + } +#endif return rb_str_new2("."); } if (p == name) p++; +#if defined(DOSISH) + if (has_drive_letter(name) && p == &name[2]) + p++; +#endif dirname = rb_str_new(name, p - name); if (OBJ_TAINTED(fname)) OBJ_TAINT(dirname); @@ -2164,5 +2308,5 @@ is_absolute_path(path) { #ifdef DOSISH - if (ISALPHA(path[0]) && path[1] == ':' && isdirsep(path[2])) return 1; + if (has_drive_letter(path) && isdirsep(path[2])) return 1; if (isdirsep(path[0]) && isdirsep(path[1])) return 1; #else それでは -- U.Nakamura <usa@osb.att.ne.jp>