From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!not-for-mail From: Eli Zaretskii Newsgroups: gmane.emacs.bugs Subject: bug#13553: 24.3.50; incorrect usage of IS_DIRECTORY_SEP Date: Sat, 26 Jan 2013 14:56:46 +0200 Message-ID: <83libgje5t.fsf@gnu.org> References: Reply-To: Eli Zaretskii NNTP-Posting-Host: plane.gmane.org X-Trace: ger.gmane.org 1359205073 665 80.91.229.3 (26 Jan 2013 12:57:53 GMT) X-Complaints-To: usenet@ger.gmane.org NNTP-Posting-Date: Sat, 26 Jan 2013 12:57:53 +0000 (UTC) Cc: 13553@debbugs.gnu.org To: Shigeru Fukaya Original-X-From: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane.org@gnu.org Sat Jan 26 13:58:12 2013 Return-path: Envelope-to: geb-bug-gnu-emacs@m.gmane.org Original-Received: from lists.gnu.org ([208.118.235.17]) by plane.gmane.org with esmtp (Exim 4.69) (envelope-from ) id 1Tz5Kl-0007bw-TH for geb-bug-gnu-emacs@m.gmane.org; Sat, 26 Jan 2013 13:58:12 +0100 Original-Received: from localhost ([::1]:33690 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Tz5KU-00031u-9Z for geb-bug-gnu-emacs@m.gmane.org; Sat, 26 Jan 2013 07:57:54 -0500 Original-Received: from eggs.gnu.org ([208.118.235.92]:60178) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Tz5KQ-00030Z-HD for bug-gnu-emacs@gnu.org; Sat, 26 Jan 2013 07:57:52 -0500 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1Tz5KO-0000NZ-K9 for bug-gnu-emacs@gnu.org; Sat, 26 Jan 2013 07:57:50 -0500 Original-Received: from debbugs.gnu.org ([140.186.70.43]:44698) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Tz5KO-0000NV-Gf for bug-gnu-emacs@gnu.org; Sat, 26 Jan 2013 07:57:48 -0500 Original-Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.72) (envelope-from ) id 1Tz5Kc-0000cR-1X for bug-gnu-emacs@gnu.org; Sat, 26 Jan 2013 07:58:02 -0500 X-Loop: help-debbugs@gnu.org Resent-From: Eli Zaretskii Original-Sender: debbugs-submit-bounces@debbugs.gnu.org Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Sat, 26 Jan 2013 12:58:02 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 13553 X-GNU-PR-Package: emacs X-GNU-PR-Keywords: Original-Received: via spool by 13553-submit@debbugs.gnu.org id=B13553.13592050222302 (code B ref 13553); Sat, 26 Jan 2013 12:58:02 +0000 Original-Received: (at 13553) by debbugs.gnu.org; 26 Jan 2013 12:57:02 +0000 Original-Received: from localhost ([127.0.0.1]:50161 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.72) (envelope-from ) id 1Tz5Jd-0000ar-ED for submit@debbugs.gnu.org; Sat, 26 Jan 2013 07:57:02 -0500 Original-Received: from mtaout22.012.net.il ([80.179.55.172]:44683) by debbugs.gnu.org with esmtp (Exim 4.72) (envelope-from ) id 1Tz5Ja-0000af-64 for 13553@debbugs.gnu.org; Sat, 26 Jan 2013 07:57:00 -0500 Original-Received: from conversion-daemon.a-mtaout22.012.net.il by a-mtaout22.012.net.il (HyperSendmail v2007.08) id <0MH800200H1LX100@a-mtaout22.012.net.il> for 13553@debbugs.gnu.org; Sat, 26 Jan 2013 14:56:37 +0200 (IST) Original-Received: from HOME-C4E4A596F7 ([87.69.4.28]) by a-mtaout22.012.net.il (HyperSendmail v2007.08) with ESMTPA id <0MH800215HACWD30@a-mtaout22.012.net.il>; Sat, 26 Jan 2013 14:56:37 +0200 (IST) In-reply-to: X-012-Sender: halo1@inter.net.il X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.13 Precedence: list X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.6.x X-Received-From: 140.186.70.43 X-BeenThere: bug-gnu-emacs@gnu.org List-Id: "Bug reports for GNU Emacs, the Swiss army knife of text editors" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane.org@gnu.org Original-Sender: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane.org@gnu.org Xref: news.gmane.org gmane.emacs.bugs:70340 Archived-At: > From: Shigeru Fukaya > Date: Sat, 26 Jan 2013 14:52:59 +0900 > > `file-attributes' returns nil as stat info for a file, its name ending > with a character whose second byte is 0x5c. > > It is because IS_DIRECTORY_SEP is wrongly used. > IS_DIRECTORY_SEP only works when its argument is surely on a start > byte of dbcs characters. > I find incorrect usage, at least in some functions of `w32.c'. > (readdir, stat_worker, read_unc_volume) Thanks. I fixed w32.c functions that use IS_DIRECTORY_SEP in revision 111200 on the emacs-24 branch. As I cannot easily test DBCS file-name encodings on my system, please test the fixed version and see if the problem indeed goes away. The patch relative to emacs-24 branch is below, for your convenience. After looking at the code which I fixed, I'm afraid I don't understand why file-attributes returned nil in your case. The code that uses IS_DIRECTORY_SEP there is only a fallback, for when the GetFileInformationByHandle API fails for some reason. You don't say what version of Windows you are using, but I won't expect that fallback code be executed for any reasonably recent Windows version. So could you please trace through the code that file-attributes calls, and see which use of IS_DIRECTORY_SEP caused the problem? I'm afraid that it's not in w32.c at all, but elsewhere. === modified file 'src/w32.c' --- src/w32.c 2013-01-25 14:47:37 +0000 +++ src/w32.c 2013-01-26 12:49:34 +0000 @@ -1503,12 +1503,17 @@ parse_root (char * name, char ** pPath) else if (IS_DIRECTORY_SEP (name[0]) && IS_DIRECTORY_SEP (name[1])) { int slashes = 2; + int dbcs_p = max_filename_mbslen () > 1; + name += 2; do { if (IS_DIRECTORY_SEP (*name) && --slashes == 0) break; - name++; + if (dbcs_p) + name = CharNextExA (file_name_codepage, name, 0); + else + name++; } while ( *name ); if (IS_DIRECTORY_SEP (name[0])) @@ -2369,12 +2374,23 @@ get_volume_info (const char * name, cons { char *str = temp; int slashes = 4; + int dbcs_p = max_filename_mbslen () > 1; + rootname = temp; do { if (IS_DIRECTORY_SEP (*name) && --slashes == 0) break; - *str++ = *name++; + if (!dbcs_p) + *str++ = *name++; + else + { + const char *p = name; + + name = CharNextExA (file_name_codepage, name, 0); + memcpy (str, p, name - p); + str += name - p; + } } while ( *name ); @@ -2610,11 +2626,23 @@ readdir (DIR *dirp) { char filename[MAXNAMLEN + 3]; int ln; + int dbcs_p = max_filename_mbslen () > 1; strcpy (filename, dir_pathname); ln = strlen (filename) - 1; - if (!IS_DIRECTORY_SEP (filename[ln])) - strcat (filename, "\\"); + if (!dbcs_p) + { + if (!IS_DIRECTORY_SEP (filename[ln])) + strcat (filename, "\\"); + } + else + { + char *end = filename + ln + 1; + char *last_char = CharPrevExA (file_name_codepage, filename, end, 0); + + if (!IS_DIRECTORY_SEP (*last_char)) + strcat (filename, "\\"); + } strcat (filename, "*"); /* Note: No need to resolve symlinks in FILENAME, because @@ -2719,6 +2747,7 @@ read_unc_volume (HANDLE henum, char *rea DWORD bufsize = 512; char *buffer; char *ptr; + int dbcs_p = max_filename_mbslen () > 1; count = 1; buffer = alloca (bufsize); @@ -2729,7 +2758,13 @@ read_unc_volume (HANDLE henum, char *rea /* WNetEnumResource returns \\resource\share...skip forward to "share". */ ptr = ((LPNETRESOURCE) buffer)->lpRemoteName; ptr += 2; - while (*ptr && !IS_DIRECTORY_SEP (*ptr)) ptr++; + if (!dbcs_p) + while (*ptr && !IS_DIRECTORY_SEP (*ptr)) ptr++; + else + { + while (*ptr && !IS_DIRECTORY_SEP (*ptr)) + ptr = CharNextExA (file_name_codepage, ptr, 0); + } ptr++; strncpy (readbuf, ptr, size); @@ -2766,9 +2801,11 @@ logon_network_drive (const char *path) { NETRESOURCE resource; char share[MAX_PATH]; - int i, n_slashes; + int n_slashes; char drive[4]; UINT drvtype; + char *p; + int dbcs_p; if (IS_DIRECTORY_SEP (path[0]) && IS_DIRECTORY_SEP (path[1])) drvtype = DRIVE_REMOTE; @@ -2790,13 +2827,18 @@ logon_network_drive (const char *path) n_slashes = 2; strncpy (share, path, MAX_PATH); /* Truncate to just server and share name. */ - for (i = 2; i < MAX_PATH; i++) + dbcs_p = max_filename_mbslen () > 1; + for (p = share + 2; *p && p < share + MAX_PATH; ) { - if (IS_DIRECTORY_SEP (share[i]) && ++n_slashes > 3) + if (IS_DIRECTORY_SEP (*p) && ++n_slashes > 3) { - share[i] = '\0'; + *p = '\0'; break; } + if (dbcs_p) + p = CharNextExA (file_name_codepage, p, 0); + else + p++; } resource.dwType = RESOURCETYPE_DISK; @@ -3557,6 +3599,7 @@ stat_worker (const char * path, struct s DWORD access_rights = 0; DWORD fattrs = 0, serialnum = 0, fs_high = 0, fs_low = 0, nlinks = 1; FILETIME ctime, atime, wtime; + int dbcs_p; if (path == NULL || buf == NULL) { @@ -3751,6 +3794,7 @@ stat_worker (const char * path, struct s did not ask for extra precision, resolving symlinks will fly in the face of that request, since the user then wants the lightweight version of the code. */ + dbcs_p = max_filename_mbslen () > 1; rootdir = (path >= save_name + len - 1 && (IS_DIRECTORY_SEP (*path) || *path == 0)); @@ -3778,8 +3822,19 @@ stat_worker (const char * path, struct s } else if (rootdir) { - if (!IS_DIRECTORY_SEP (name[len-1])) - strcat (name, "\\"); + if (!dbcs_p) + { + if (!IS_DIRECTORY_SEP (name[len-1])) + strcat (name, "\\"); + } + else + { + char *end = name + len; + char *n = CharPrevExA (file_name_codepage, name, end, 0); + + if (!IS_DIRECTORY_SEP (*n)) + strcat (name, "\\"); + } if (GetDriveType (name) < 2) { errno = ENOENT; @@ -3791,15 +3846,37 @@ stat_worker (const char * path, struct s } else { - if (IS_DIRECTORY_SEP (name[len-1])) - name[len - 1] = 0; + if (!dbcs_p) + { + if (IS_DIRECTORY_SEP (name[len-1])) + name[len - 1] = 0; + } + else + { + char *end = name + len; + char *n = CharPrevExA (file_name_codepage, name, end, 0); + + if (IS_DIRECTORY_SEP (*n)) + *n = 0; + } /* (This is hacky, but helps when doing file completions on network drives.) Optimize by using information available from active readdir if possible. */ len = strlen (dir_pathname); - if (IS_DIRECTORY_SEP (dir_pathname[len-1])) - len--; + if (!dbcs_p) + { + if (IS_DIRECTORY_SEP (dir_pathname[len-1])) + len--; + } + else + { + char *end = dir_pathname + len; + char *n = CharPrevExA (file_name_codepage, dir_pathname, end, 0); + + if (IS_DIRECTORY_SEP (*n)) + len--; + } if (dir_find_handle != INVALID_HANDLE_VALUE && !(is_a_symlink && follow_symlinks) && strnicmp (save_name, dir_pathname, len) == 0 @@ -4060,6 +4137,7 @@ symlink (char const *filename, char cons char linkfn[MAX_PATH], *tgtfn; DWORD flags = 0; int dir_access, filename_ends_in_slash; + int dbcs_p; /* Diagnostics follows Posix as much as possible. */ if (filename == NULL || linkname == NULL) @@ -4085,6 +4163,8 @@ symlink (char const *filename, char cons return -1; } + dbcs_p = max_filename_mbslen () > 1; + /* Note: since empty FILENAME was already rejected, we can safely refer to FILENAME[1]. */ if (!(IS_DIRECTORY_SEP (filename[0]) || IS_DEVICE_SEP (filename[1]))) @@ -4099,8 +4179,21 @@ symlink (char const *filename, char cons char tem[MAX_PATH]; char *p = linkfn + strlen (linkfn); - while (p > linkfn && !IS_ANY_SEP (p[-1])) - p--; + if (!dbcs_p) + { + while (p > linkfn && !IS_ANY_SEP (p[-1])) + p--; + } + else + { + char *p1 = CharPrevExA (file_name_codepage, linkfn, p, 0); + + while (p > linkfn && !IS_ANY_SEP (*p1)) + { + p = p1; + p1 = CharPrevExA (file_name_codepage, linkfn, p1, 0); + } + } if (p > linkfn) strncpy (tem, linkfn, p - linkfn); tem[p - linkfn] = '\0'; @@ -4115,7 +4208,15 @@ symlink (char const *filename, char cons exist, but ends in a slash, we create a symlink to directory. If FILENAME exists and is a directory, we always create a symlink to directory. */ - filename_ends_in_slash = IS_DIRECTORY_SEP (filename[strlen (filename) - 1]); + if (!dbcs_p) + filename_ends_in_slash = IS_DIRECTORY_SEP (filename[strlen (filename) - 1]); + else + { + const char *end = filename + strlen (filename); + const char *n = CharPrevExA (file_name_codepage, filename, end, 0); + + filename_ends_in_slash = IS_DIRECTORY_SEP (*n); + } if (dir_access == 0 || filename_ends_in_slash) flags = SYMBOLIC_LINK_FLAG_DIRECTORY; @@ -4440,6 +4541,7 @@ chase_symlinks (const char *file) char link[MAX_PATH]; ssize_t res, link_len; int loop_count = 0; + int dbcs_p; if (is_windows_9x () == TRUE || !is_symlink (file)) return (char *)file; @@ -4447,13 +4549,27 @@ chase_symlinks (const char *file) if ((link_len = GetFullPathName (file, MAX_PATH, link, NULL)) == 0) return (char *)file; + dbcs_p = max_filename_mbslen () > 1; target[0] = '\0'; do { /* Remove trailing slashes, as we want to resolve the last non-trivial part of the link name. */ - while (link_len > 3 && IS_DIRECTORY_SEP (link[link_len-1])) - link[link_len--] = '\0'; + if (!dbcs_p) + { + while (link_len > 3 && IS_DIRECTORY_SEP (link[link_len-1])) + link[link_len--] = '\0'; + } + else if (link_len > 3) + { + char *n = CharPrevExA (file_name_codepage, link, link + link_len, 0); + + while (n >= link + 2 && IS_DIRECTORY_SEP (*n)) + { + n[1] = '\0'; + n = CharPrevExA (file_name_codepage, link, n, 0); + } + } res = readlink (link, target, MAX_PATH); if (res > 0) @@ -4466,8 +4582,21 @@ chase_symlinks (const char *file) the symlink, then copy the result back to target. */ char *p = link + link_len; - while (p > link && !IS_ANY_SEP (p[-1])) - p--; + if (!dbcs_p) + { + while (p > link && !IS_ANY_SEP (p[-1])) + p--; + } + else + { + char *p1 = CharPrevExA (file_name_codepage, link, p, 0); + + while (p > link && !IS_ANY_SEP (*p1)) + { + p = p1; + p1 = CharPrevExA (file_name_codepage, link, p1, 0); + } + } strcpy (p, target); strcpy (target, link); }