That's fine with me.
Mike Alexander
--On Thursday, November 30, 2000 9:03 AM +0000 Andy Piper
<andyp(a)bea.com> wrote:
I'm going to apply this unless anyone has any objections. I
don't
think it conflicts with Mike's patch and adds functionality for
cygwin.
andy
At 12:23 AM 9/1/00 +0200, Dan Holmsand wrote:
> Mike Alexander <mta(a)arbortext.com> writes:
>
> > It's annoyed me for some time that the native Windows version
> > of XEmacs often ends up with two (or more) buffers visiting
> > the same file, even if find-file-compare-truenames or
> > find-file-use-truenames is set.
>
> I totally agree. In fact I also started to fix this a while
> ago...
>
> [...]
>
> > The attached patch changed Ffile_truename to use
> > win32_get_long_filename on Windows after changing it to do
> > something reasonable when given a path that contains a file or
> > directory that doesn't exist. This latter change might cause
> > a problem with code that used win32-long-file-name to test for
> > the existence of a file, but I can't find any code that uses
> > it at all, let alone for that purpose. I also changed
> > xemacs.mak to remove realpath.c from the native Windows build
> > since it isn't needed anymore. With this patch I haven't had
> > any more problems with multiple buffers visiting the same file
> > if I set find-file-compare-truenames or
> > find-file-use-truenames.
>
> I chose the other alternative - making xrealpath work on
> windows. I obviously think this is a better choice, mainly
> because it's mine :-)
>
> My code have some real advantages, though. Mainly, it also works
> with cygwin xemacs, which is broken in basically the same way as
> the native built one. Besides that the current code doesn't care
> for case or short filenames, it is totally broken when you feed
> it UNC paths.
>
> My code deals with UNC paths by lower-casing the server and share
> parts of the filename (i.e. //server/share/...), since case is
> not significant here.
>
> It is perhaps also an advantage that the code is shared between
> unix, cygwin and native windows xemacsen. It should, for
> example, be quite easy to add support for Win2k's reparse points
> (which basically work like symlinks).
>
> I also definitely think find-file-compare-truenames should be
> true by default on windows.
>
> Anyway, here it is. I've been using it for a while, on both
> native xemacs and cygwin. However, I haven't tested the code on
> unix (the changes there are mainly trivial, though; mostly *s ==
> '/' -> IS_DIRECTORY_SEP (*s), etc.).
>
> /dan
>
> --
> Dan Ola Holmsand
> IB
> dan(a)eyebee.com
>
>
>
> 2000-08-31 Dan Holmsand <dan(a)eyebee.com>
>
> * buffer.c: Make find-file-compare-truenames default to
> true on windows.
>
> * realpath.c (win32_abs_start):
> (cygwin_readlink):
> (win32_readlink): New functions.
> (xrealpath): Return really real filenames on windows.
>
> * fileio.c (Ffile_truename): Make file-truename work on
> windows.
>
> Index: src/buffer.c
> ================================================================
> === RCS file: /usr/CVSroot/XEmacs/xemacs/src/buffer.c,v
> retrieving revision 1.36.2.38
> diff -u -u -r1.36.2.38 buffer.c
> --- buffer.c 2000/08/18 17:16:00 1.36.2.38
> +++ buffer.c 2000/08/31 21:19:18
> @@ -2227,7 +2227,11 @@
>
> See also the variable find-file-use-truenames.
> */ );
> +#if defined(CYGWIN) || defined(WIN32_NATIVE)
> + find_file_compare_truenames = 1;
> +#else
> find_file_compare_truenames = 0;
> +#endif
>
> DEFVAR_BOOL ("find-file-use-truenames",
> &find_file_use_truenames /* If this is true, then a buffer's
> visited file-name will always be Index: src/fileio.c
> ================================================================
> === RCS file: /usr/CVSroot/XEmacs/xemacs/src/fileio.c,v
> retrieving revision 1.51.2.26
> diff -u -u -r1.51.2.26 fileio.c
> --- fileio.c 2000/08/06 09:27:12 1.51.2.26
> +++ fileio.c 2000/08/31 21:19:40
> @@ -1316,14 +1316,30 @@
> we just use our own version in realpath.c. */
> for (;;)
> {
> - p = (Extbyte *) memchr (p + 1, '/', elen - (p + 1 -
> path)); - if (p)
> - *p = 0;
> + Extbyte *pos;
>
> +#ifdef WIN32_NATIVE
> + if (IS_DRIVE (p[0]) && IS_DEVICE_SEP (p[1])
> + && IS_DIRECTORY_SEP (p[2]))
> + /* don't test c: on windows */
> + p = p+2;
> + else if (IS_DIRECTORY_SEP (p[0]) && IS_DIRECTORY_SEP
> (p[1])) + /* start after // */
> + p = p+1;
> +#endif
> + for (pos = p + 1; pos < path + elen; pos++)
> + if (IS_DIRECTORY_SEP (*pos))
> + {
> + *(p = pos) = 0;
> + break;
> + }
> + if (p != pos)
> + p = 0;
> +
> if (xrealpath ((char *) path, resolved_path))
> {
> if (p)
> - *p = '/';
> + *p = DIRECTORY_SEP;
> else
> break;
>
> @@ -1342,13 +1358,13 @@
> {
> int plen = elen - (p - path);
>
> - if (rlen > 1 && resolved_path[rlen - 1] ==
> '/') + if (rlen > 1 && IS_DIRECTORY_SEP
> (resolved_path[rlen - 1]))
> rlen = rlen - 1;
>
> if (plen + rlen + 1 > countof
> (resolved_path)) goto toolong;
>
> - resolved_path[rlen] = '/';
> + resolved_path[rlen] = DIRECTORY_SEP;
> memcpy (resolved_path + rlen + 1, p + 1,
> plen + 1 - 1); }
> break;
> @@ -1361,12 +1377,12 @@
> {
> Lisp_Object resolved_name;
> int rlen = strlen (resolved_path);
> - if (elen > 0 && XSTRING_BYTE (expanded_name, elen - 1) ==
> '/' - && !(rlen > 0 && resolved_path[rlen - 1] ==
'/'))
> + if (elen > 0 && IS_DIRECTORY_SEP (XSTRING_BYTE
> (expanded_name, elen - 1))
> + && !(rlen > 0 && IS_DIRECTORY_SEP (resolved_path[rlen
> - 1]))) {
> if (rlen + 1 > countof (resolved_path))
> goto toolong;
> - resolved_path[rlen++] = '/';
> + resolved_path[rlen++] = DIRECTORY_SEP;
> resolved_path[rlen] = '\0';
> }
> TO_INTERNAL_FORMAT (DATA, (resolved_path, rlen),
> Index: src/realpath.c
> ================================================================
> === RCS file: /usr/CVSroot/XEmacs/xemacs/src/realpath.c,v
> retrieving revision 1.6.2.7
> diff -u -u -r1.6.2.7 realpath.c
> --- realpath.c 2000/06/12 04:18:21 1.6.2.7
> +++ realpath.c 2000/08/31 21:19:47
> @@ -23,42 +23,145 @@
> /* Synched up with: Not in FSF. */
>
> #include <config.h>
> +#include "lisp.h"
> +#include "sysfile.h"
>
> -#include <sys/types.h>
> #include <stdio.h>
> #include <string.h>
> -#include <errno.h>
> #include <limits.h>
>
> -#ifdef HAVE_UNISTD_H
> -#include <unistd.h>
> -#endif
> +/* First char after start of absolute filename. */
> +#define ABS_START(name) (name + ABS_LENGTH (name))
>
> -#if defined (HAVE_SYS_PARAM_H)
> -#include <sys/param.h>
> -#endif
> +#if defined (WIN32_NATIVE)
> +/* Length of start of absolute filename. */
> +# define ABS_LENGTH(name) (win32_abs_start (name))
> +static int win32_abs_start (const char * name);
> +/* System dependent version of readlink. */
> +# define sys_readlink(name, buf, size) win32_readlink (name,
> buf, size) +#else
> +# ifdef CYGWIN
> +# define ABS_LENGTH(name) (IS_DIRECTORY_SEP (*name) ? \
> + (IS_DIRECTORY_SEP (name[1]) ? 2 :
> 1) : 0) +# define sys_readlink(name, buf, size) cygwin_readlink
> (name, buf, size) +# else
> +# define ABS_LENGTH(name) (IS_DIRECTORY_SEP (*name) ? 1 : 0)
> +# define sys_readlink(name, buf, size) readlink (name, buf,
> size) +# endif /* CYGWIN */
> +#endif /* WIN32_NATIVE */
> +
> +#if defined (WIN32_NATIVE) || defined (CYGWIN)
> +#include "syswindows.h"
> +/* Emulate readlink on win32 - finds real name (i.e. correct
> case) of + a file. UNC servers and shares are lower-cased.
> Directories must be + given without trailing '/'. One day,
> this could read Win2K's + reparse points. */
> +static int
> +win32_readlink (const char * name, char * buf, int size)
> +{
> + WIN32_FIND_DATA find_data;
> + HANDLE dir_handle = NULL;
> + int len = 0;
> + int err = 0;
> + const char* lastname;
> + int count = 0;
> + const char* tmp;
> + char* res = NULL;
> +
> + assert (*name);
> +
> + /* Sort of check we have a valid filename. */
> + if (strpbrk (name, "*?|<>\"") || strlen (name) >=
MAX_PATH)
> + {
> + errno = EIO;
> + return -1;
> + }
> +
> + /* Find start of filename */
> + lastname = name + strlen (name);
> + while (lastname > name && !IS_DIRECTORY_SEP (lastname[-1]))
> + --lastname;
> +
> + /* Count slashes in unc path */
> + if (ABS_LENGTH (name) == 2)
> + for (tmp = name; *tmp; tmp++)
> + if (IS_DIRECTORY_SEP (*tmp))
> + count++;
> +
> + if (count >= 2 && count < 4)
> + {
> + /* UNC server or share name: just copy lowercased name. */
> + res = find_data.cFileName;
> + for (tmp = lastname; *tmp; tmp++)
> + *res++ = tolower (*tmp);
> + *res = '\0';
> + }
> + else
> + dir_handle = FindFirstFile (name, &find_data);
> +
> + if (res || dir_handle != INVALID_HANDLE_VALUE)
> + {
> + if ((len = strlen (find_data.cFileName)) < size)
> + {
> + if (strcmp (lastname, find_data.cFileName) == 0)
> + /* Signal that the name is already OK. */
> + err = EINVAL;
> + else
> + memcpy (buf, find_data.cFileName, len + 1);
> + }
> + else
> + err = ENAMETOOLONG;
> + if (!res) FindClose (dir_handle);
> + }
> + else
> + err = ENOENT;
>
> + errno = err;
> + return err ? -1 : len;
> +}
> +#endif /* WIN32_NATIVE || CYGWIN */
> +
> +#ifdef CYGWIN
> +/* Call readlink and try to find out the correct case for the
> file. */ +static int
> +cygwin_readlink (const char * name, char * buf, int size)
> +{
> + int n = readlink (name, buf, size);
> + if (n < 0)
> + {
> + /* The file may exist, but isn't a symlink. Try to find
> the + right name. */
> + char* tmp = alloca
> (cygwin_posix_to_win32_path_list_buf_size (name)); +
> cygwin_posix_to_win32_path_list (name, tmp);
> + n = win32_readlink (tmp, buf, size);
> + }
> + return n;
> +}
> +#endif /* CYGWIN */
> +
> #ifdef WIN32_NATIVE
> -#include <direct.h>
> +#ifndef ELOOP
> +#define ELOOP 10062 /* = WSAELOOP in winsock.h */
> #endif
> -
> -#include <sys/stat.h> /* for S_IFLNK */
> +/* Length of start of absolute filename. */
> +static int
> +win32_abs_start (const char * name)
> +{
> + if (isalpha (*name) && IS_DEVICE_SEP (name[1])
> + && IS_DIRECTORY_SEP (name[2]))
> + return 3;
> + else if (IS_DIRECTORY_SEP (*name))
> + return IS_DIRECTORY_SEP (name[1]) ? 2 : 1;
> + else
> + return 0;
> +}
> +#endif /* WIN32_NATIVE */
>
> #if !defined (HAVE_GETCWD) && defined (HAVE_GETWD)
> #undef getcwd
> #define getcwd(buffer, len) getwd (buffer)
> #endif
>
> -#ifndef PATH_MAX
> -# if defined (_POSIX_PATH_MAX)
> -# define PATH_MAX _POSIX_PATH_MAX
> -# elif defined (MAXPATHLEN)
> -# define PATH_MAX MAXPATHLEN
> -# else
> -# define PATH_MAX 1024
> -# endif
> -#endif
> -
> #define MAX_READLINKS 32
>
> char * xrealpath (const char *path, char resolved_path []);
> @@ -68,78 +171,63 @@
> char copy_path[PATH_MAX];
> char *new_path = resolved_path;
> char *max_path;
> -#ifdef S_IFLNK
> +#if defined (S_IFLNK) || defined (WIN32_NATIVE)
> int readlinks = 0;
> char link_path[PATH_MAX];
> int n;
> + int abslen = ABS_LENGTH (path);
> #endif
>
> /* Make a copy of the source path since we may need to modify
> it. */ strcpy (copy_path, path);
> path = copy_path;
> max_path = copy_path + PATH_MAX - 2;
> +
> #ifdef WIN32_NATIVE
> - /*
> - ** In NT we have two different cases: (1) the path name
> begins - ** with a drive letter, e.g., "C:"; and (2) the path
> name begins - ** with just a slash, which roots to the current
> drive. In the - ** first case we are going to leave things
> alone, in the second - ** case we will prepend the drive letter
> to the given path. - ** Note: So far in testing, I'm only
> seeing case #1, even though - ** I've tried to get the other
> cases to happen.
> - ** August Hill, 31 Aug 1997.
> - **
> - ** Check for a driver letter...C:/...
> - */
> - if (*(path + 1) == ':')
> + /* Check for c:/... or //server/... */
> + if (abslen == 2 || abslen == 3)
> {
> - strncpy(new_path, path, 3);
> - new_path += 3;
> - path += 3;
> + strncpy (new_path, path, abslen);
> + new_path += abslen;
> + path += abslen;
> }
> -
> - /*
> - ** No drive letter, but a beginning slash? Prepend the drive
> - ** letter...
> - */
> - else if (*path == '/')
> + /* No drive letter, but a beginning slash? Prepend drive
> letter. */ + else if (abslen == 1)
> {
> getcwd (new_path, PATH_MAX - 1);
> new_path += 3;
> path++;
> }
> -
> - /*
> - ** Just a path name, prepend the current directory
> - */
> + /* Just a path name, prepend the current directory */
> else
> {
> getcwd (new_path, PATH_MAX - 1);
> - new_path += strlen(new_path);
> - if (new_path[-1] != '/')
> - *new_path++ = '/';
> + new_path += strlen (new_path);
> + if (!IS_DIRECTORY_SEP (new_path[-1]))
> + *new_path++ = DIRECTORY_SEP;
> }
> -
> #else
> /* If it's a relative pathname use getcwd for starters. */
> - if (*path != '/')
> + if (abslen == 0)
> {
> getcwd (new_path, PATH_MAX - 1);
> - new_path += strlen(new_path);
> - if (new_path[-1] != '/')
> - *new_path++ = '/';
> + new_path += strlen (new_path);
> + if (!IS_DIRECTORY_SEP (new_path[-1]))
> + *new_path++ = DIRECTORY_SEP;
> }
> else
> {
> - *new_path++ = '/';
> - path++;
> + /* Copy first directory sep. May have two on cygwin. */
> + strncpy (new_path, path, abslen);
> + new_path += abslen;
> + path += abslen;
> }
> #endif
> /* Expand each slash-separated pathname component. */
> while (*path != '\0')
> {
> /* Ignore stray "/". */
> - if (*path == '/')
> + if (IS_DIRECTORY_SEP (*path))
> {
> path++;
> continue;
> @@ -148,7 +236,7 @@
> if (*path == '.')
> {
> /* Ignore ".". */
> - if (path[1] == '\0' || path[1] == '/')
> + if (path[1] == '\0' || IS_DIRECTORY_SEP (path[1]))
> {
> path++;
> continue;
> @@ -156,23 +244,24 @@
>
> /* Handle ".." */
> if (path[1] == '.' &&
> - (path[2] == '\0' || path[2] == '/'))
> + (path[2] == '\0' || IS_DIRECTORY_SEP (path[2])))
> {
> path += 2;
>
> /* Ignore ".." at root. */
> - if (new_path == resolved_path + 1)
> + if (new_path == ABS_START (resolved_path))
> continue;
>
> /* Handle ".." by backing up. */
> - while ((--new_path)[-1] != '/')
> - ;
> + --new_path;
> + while (!IS_DIRECTORY_SEP (new_path[-1]))
> + --new_path;
> continue;
> }
> }
>
> /* Safely copy the next pathname component. */
> - while (*path != '\0' && *path != '/')
> + while (*path != '\0' && !IS_DIRECTORY_SEP (*path))
> {
> if (path > max_path)
> {
> @@ -182,10 +271,10 @@
> *new_path++ = *path++;
> }
>
> -#ifdef S_IFLNK
> +#if defined (S_IFLNK) || defined (WIN32_NATIVE)
> /* See if latest pathname component is a symlink. */
> *new_path = '\0';
> - n = readlink (resolved_path, link_path, PATH_MAX - 1);
> + n = sys_readlink (resolved_path, link_path, PATH_MAX - 1);
>
> if (n < 0)
> {
> @@ -204,14 +293,14 @@
>
> /* Note: readlink doesn't add the null byte. */
> link_path[n] = '\0';
> -
> - if (*link_path == '/')
> +
> + if (ABS_LENGTH (link_path) > 0)
> /* Start over for an absolute symlink. */
> - new_path = resolved_path;
> + new_path = resolved_path + ABS_LENGTH (link_path) -
> 1; else
> /* Otherwise back up over this component. */
> - while (*(--new_path) != '/')
> - ;
> + for (--new_path; !IS_DIRECTORY_SEP (*new_path);
> --new_path) + assert (new_path > resolved_path);
>
> /* Safe sex check. */
> if (strlen(path) + n >= PATH_MAX)
> @@ -225,15 +314,21 @@
> strcpy(copy_path, link_path);
> path = copy_path;
> }
> -#endif /* S_IFLNK */
> - *new_path++ = '/';
> +#endif /* S_IFLNK || WIN32_NATIVE */
> + *new_path++ = DIRECTORY_SEP;
> }
>
> /* Delete trailing slash but don't whomp a lone slash. */
> - if (new_path != resolved_path + 1 && new_path[-1] == '/')
> + if (new_path != ABS_START (resolved_path) && IS_DIRECTORY_SEP
> (new_path[-1]))
> new_path--;
>
> /* Make sure it's null terminated. */
> *new_path = '\0';
> +
> +#ifdef WIN32_NATIVE
> + if (ABS_LENGTH (resolved_path) == 3)
> + /* Lowercase drive letter. */
> + *resolved_path = tolower (*resolved_path);
> +#endif
> return resolved_path;
> }
--------------------------------------------------------------
Dr Andy Piper
Principal Consultant, BEA Systems Ltd