/* Synched up with: Not in FSF. */
#include <config.h>
-
-#include <sys/types.h>
-#include <stdio.h>
-#include <string.h>
+#include "lisp.h"
#include <errno.h>
+
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
-#ifdef _POSIX_VERSION
-#include <limits.h> /* for PATH_MAX */
-#else
-#include <sys/param.h> /* for MAXPATHLEN */
+
+#if defined (HAVE_SYS_PARAM_H) && !defined (WIN32_NATIVE)
+#include <sys/param.h>
#endif
-#ifdef WINDOWSNT
+#ifdef WIN32_NATIVE
#include <direct.h>
#endif
#include <sys/stat.h> /* for S_IFLNK */
+/* First char after start of absolute filename. */
+#define ABS_START(name) (name + ABS_LENGTH (name))
+
+#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 system_readlink win32_readlink
+#else
+# ifdef CYGWIN
+# define ABS_LENGTH(name) (IS_DIRECTORY_SEP (*name) ? \
+ (IS_DIRECTORY_SEP (name[1]) ? 2 : 1) : 0)
+# define system_readlink cygwin_readlink
+# else
+# define ABS_LENGTH(name) (IS_DIRECTORY_SEP (*name) ? 1 : 0)
+# define system_readlink readlink
+# 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
+#ifndef ELOOP
+#define ELOOP 10062 /* = WSAELOOP in winsock.h */
+#endif
+/* 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)
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);
+ strcpy (copy_path, path);
path = copy_path;
max_path = copy_path + PATH_MAX - 2;
-#ifdef WINDOWSNT
- /*
- ** 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) == ':')
+
+#ifdef WIN32_NATIVE
+ /* 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;
if (*path == '.')
{
/* Ignore ".". */
- if (path[1] == '\0' || path[1] == '/')
+ if (path[1] == '\0' || IS_DIRECTORY_SEP (path[1]))
{
path++;
continue;
}
- if (path[1] == '.')
+ /* Handle ".." */
+ if (path[1] == '.' &&
+ (path[2] == '\0' || IS_DIRECTORY_SEP (path[2])))
{
- if (path[2] == '\0' || path[2] == '/')
- {
- path += 2;
-
- /* Ignore ".." at root. */
- if (new_path == resolved_path + 1)
- continue;
-
- /* Handle ".." by backing up. */
- while ((--new_path)[-1] != '/')
- ;
- continue;
- }
+ path += 2;
+
+ /* Ignore ".." at root. */
+ if (new_path == ABS_START (resolved_path))
+ continue;
+
+ /* Handle ".." by backing up. */
+ --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)
{
*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 = system_readlink (resolved_path, link_path, PATH_MAX - 1);
if (n < 0)
{
/* 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)
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;
}