From: Geoff Voelker Date: Sun, 21 Jan 1996 00:31:34 +0000 (+0000) Subject: (nt_stat): Use alloca instead of xmalloc. X-Git-Tag: emacs-19.34~1614 X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=00b3b7b3ae95a8c044ca548209a18732c2a19d27;p=emacs.git (nt_stat): Use alloca instead of xmalloc. (get_unassigned_drive_letter, is_toplevel_share_name, stat_toplevel_share): New functions for stat on remote shares. (readdir): Use IS_ANY_SEP. --- diff --git a/src/w32.c b/src/w32.c index 600d308aacd..36c11cc9abb 100644 --- a/src/w32.c +++ b/src/w32.c @@ -21,30 +21,37 @@ Boston, MA 02111-1307, USA. Geoff Voelker (voelker@cs.washington.edu) 7-29-94 */ + /* Define stat before including config.h. */ #include #include +#include + +static int is_toplevel_share_name (char *); +static int stat_toplevel_share (char *, void *); + int nt_stat (char *filename, struct stat *statbuf) { - int r, l = strlen (filename); + int l = strlen (filename); char *str = NULL; - extern long *xmalloc (); - extern void xfree (); /* stat has a bug when passed a name of a directory with a trailing backslash (but a trailing forward slash works fine). */ if (filename[l - 1] == '\\') { - str = (char *) xmalloc (l + 1); + str = (char *) alloca (l + 1); strcpy (str, filename); str[l - 1] = '/'; - r = stat (str, statbuf); - xfree (str); - return r; + return stat (str, statbuf); } + + if (stat (filename, statbuf) == 0) + return 0; + else if (is_toplevel_share_name (filename)) + return stat_toplevel_share (filename, statbuf); else - return stat (filename, statbuf); + return -1; } /* Place a wrapper around the NT version of ctime. It returns NULL @@ -76,6 +83,102 @@ nt_ctime (const time_t *t) extern int report_file_error (char *, Lisp_Object); +/* Routines for extending stat above. */ +static int +get_unassigned_drive_letter () +{ + int i; + unsigned int mask; + + mask = GetLogicalDrives (); + for (i = 0; i < 26; i++) + { + if (mask & (1 << i)) + continue; + break; + } + return (i == 26 ? -1 : 'A' + i); +} + +void dostounix_filename (char *); + +/* Return nonzero if NAME is of the form \\host\share (forward slashes + also valid), otherwise return 0. */ +static int +is_toplevel_share_name (char *filename) +{ + int len; + char *name; + char *host; + char *share; + char *suffix; + + len = strlen (filename); + name = alloca (len + 1); + strcpy (name, filename); + + dostounix_filename (name); + if (name[0] != '/' || name[1] != '/') + return 0; + + host = strtok (&name[2], "/"); + share = strtok (NULL, "/"); + suffix = strtok (NULL, "/"); + if (!host || !share || suffix) + return 0; + + return 1; +} + + +/* FILENAME is of the form \\host\share, and stat can't handle names + of this form. But stat can handle \\host\share if it's been + assigned a drive letter. So we create a network connection to this + share, assign it a drive letter, stat the drive letter, and + disconnect from the share. Hassle... */ +static int +stat_toplevel_share (char *filename, void *statbuf) +{ + NETRESOURCE net; + int drive_letter; + char drive[4]; + int result; + + drive_letter = get_unassigned_drive_letter (); + if (drive_letter < 0) + return -1; + + drive[0] = drive_letter; + drive[1] = ':'; + drive[2] = '\0'; + net.dwType = RESOURCETYPE_DISK; + net.lpLocalName = drive; + net.lpRemoteName = filename; + net.lpProvider = NULL; + + switch (WNetAddConnection2 (&net, NULL, NULL, 0)) + { + case NO_ERROR: + break; + case ERROR_ALREADY_ASSIGNED: + default: + return -1; + } + + /* Name the toplevel directory on the drive letter. */ + drive[2] = '/'; + drive[3] = '\0'; + result = stat (drive, (void *) statbuf); + + /* Strip the slash so we can disconnect. */ + drive[2] = '\0'; + if (WNetCancelConnection2 (drive, 0, TRUE) != NO_ERROR) + result = -1; + + return result; +} + + /* Get the current working directory. */ int getwd (char *dir) @@ -173,7 +276,7 @@ readdir (DIR *dirp) strncpy (filename, dirp->dd_buf, MAXNAMLEN); ln = strlen (filename)-1; - if (filename[ln] != '\\' && filename[ln] != ':') + if (!IS_ANY_SEP (filename[ln])) strcat (filename, "\\"); strcat (filename, "*.*");