>From f598db99c0da4aefce0b393f52a4da8f0c4c055e Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Sat, 16 Jul 2016 14:20:23 +0300 Subject: [PATCH] Fix 'dirname' and 'basename' on MS-Windows * libguile/filesys.c (is_drive_letter): New helper function. (scm_dirname, scm_basename): Use it. Don't assume the leading slash is always at the beginning of the file name. Support UNC file names on MS-Windows. --- libguile/filesys.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 76 insertions(+), 11 deletions(-) diff --git a/libguile/filesys.c b/libguile/filesys.c index c47c2f4..55df768 100644 --- a/libguile/filesys.c +++ b/libguile/filesys.c @@ -449,6 +449,18 @@ is_file_name_separator (SCM c) return 0; } +static int +is_drive_letter (SCM c) +{ +#ifdef __MINGW32__ + if (SCM_CHAR (c) >= 'a' && SCM_CHAR (c) <= 'z') + return 1; + else if (SCM_CHAR (c) >= 'A' && SCM_CHAR (c) <= 'Z') + return 1; +#endif + return 0; +} + SCM_DEFINE (scm_stat, "stat", 1, 1, 0, (SCM object, SCM exception_on_error), "Return an object containing various information about the file\n" @@ -1522,24 +1534,60 @@ SCM_DEFINE (scm_dirname, "dirname", 1, 0, 0, { long int i; unsigned long int len; + /* Length of prefix before the top-level slash. Always zero on + Posix hosts, but may be non-zero on Windows. */ + long prefix_len = 0; + int is_unc = 0; + unsigned long unc_end = 0; SCM_VALIDATE_STRING (1, filename); len = scm_i_string_length (filename); + if (len >= 2 + && is_drive_letter (scm_c_string_ref (filename, 0)) + && scm_is_eq (scm_c_string_ref (filename, 1), SCM_MAKE_CHAR (':'))) + { + prefix_len = 1; + if (len > 2 && is_file_name_separator (scm_c_string_ref (filename, 2))) + prefix_len++; + } +#ifdef __MINGW32__ + if (len > 1 + && is_file_name_separator (scm_c_string_ref (filename, 0)) + && is_file_name_separator (scm_c_string_ref (filename, 1))) + { + is_unc = 1; + prefix_len = 1; + } +#endif i = len - 1; - while (i >= 0 && is_file_name_separator (scm_c_string_ref (filename, i))) + while (i >= prefix_len + && is_file_name_separator (scm_c_string_ref (filename, i))) --i; - while (i >= 0 && !is_file_name_separator (scm_c_string_ref (filename, i))) + if (is_unc) + unc_end = i + 1; + while (i >= prefix_len + && !is_file_name_separator (scm_c_string_ref (filename, i))) --i; - while (i >= 0 && is_file_name_separator (scm_c_string_ref (filename, i))) + while (i >= prefix_len + && is_file_name_separator (scm_c_string_ref (filename, i))) --i; - if (i < 0) + if (i < prefix_len) { - if (len > 0 && is_file_name_separator (scm_c_string_ref (filename, 0))) - return scm_c_substring (filename, 0, 1); + if (is_unc) + return scm_c_substring (filename, 0, unc_end); + else if (len > prefix_len + && is_file_name_separator (scm_c_string_ref (filename, prefix_len))) + return scm_c_substring (filename, 0, prefix_len + 1); +#ifdef __MINGW32__ + else if (len > prefix_len + && scm_is_eq (scm_c_string_ref (filename, 1), + SCM_MAKE_CHAR (':'))) + return scm_c_substring (filename, 0, prefix_len + 1); +#endif else return scm_dot_string; } @@ -1557,6 +1605,9 @@ SCM_DEFINE (scm_basename, "basename", 1, 1, 0, #define FUNC_NAME s_scm_basename { int i, j, len, end; + /* Length of prefix before the top-level slash. Always zero on + Posix hosts, but may be non-zero on Windows. */ + long prefix_len = 0; SCM_VALIDATE_STRING (1, filename); len = scm_i_string_length (filename); @@ -1568,11 +1619,17 @@ SCM_DEFINE (scm_basename, "basename", 1, 1, 0, SCM_VALIDATE_STRING (2, suffix); j = scm_i_string_length (suffix) - 1; } + if (len >= 2 + && is_drive_letter (scm_c_string_ref (filename, 0)) + && scm_is_eq (scm_c_string_ref (filename, 1), SCM_MAKE_CHAR (':'))) + prefix_len = 2; + i = len - 1; - while (i >= 0 && is_file_name_separator (scm_c_string_ref (filename, i))) + while (i >= prefix_len + && is_file_name_separator (scm_c_string_ref (filename, i))) --i; end = i; - while (i >= 0 && j >= 0 + while (i >= prefix_len && j >= 0 && (scm_i_string_ref (filename, i) == scm_i_string_ref (suffix, j))) { @@ -1581,12 +1638,20 @@ SCM_DEFINE (scm_basename, "basename", 1, 1, 0, } if (j == -1) end = i; - while (i >= 0 && !is_file_name_separator (scm_c_string_ref (filename, i))) + while (i >= prefix_len + && !is_file_name_separator (scm_c_string_ref (filename, i))) --i; if (i == end) { - if (len > 0 && is_file_name_separator (scm_c_string_ref (filename, 0))) - return scm_c_substring (filename, 0, 1); + if (len > prefix_len + && is_file_name_separator (scm_c_string_ref (filename, prefix_len))) + return scm_c_substring (filename, 0, prefix_len + 1); +#ifdef __MINGW32__ + else if (len > prefix_len + && scm_is_eq (scm_c_string_ref (filename, 1), + SCM_MAKE_CHAR (':'))) + return scm_c_substring (filename, 0, prefix_len + 1); +#endif else return scm_dot_string; } -- 2.9.0.windows.1