#undef read
#undef write
+#undef strerror
+
#include "lisp.h"
#include <pwd.h>
extern Lisp_Object Vw32_downcase_file_names;
extern Lisp_Object Vw32_generate_fake_inodes;
extern Lisp_Object Vw32_get_true_file_attributes;
+extern Lisp_Object Vw32_num_mouse_buttons;
static char startup_dir[MAXPATHLEN];
HANDLE dir_handle;
int len = 0;
- /* must be valid filename, no wild cards or other illegal characters */
+ /* must be valid filename, no wild cards or other invalid characters */
if (strpbrk (name, "*?|<>\""))
return 0;
len = parse_root (full, &p);
memcpy (o, full, len);
o += len;
+ *o = '\0';
size -= len;
- do
+ while (p != NULL && *p)
{
q = p;
p = strchr (q, '\\');
else
return FALSE;
}
- while (p != NULL && *p);
return TRUE;
}
+int
+is_unc_volume (const char *filename)
+{
+ const char *ptr = filename;
+
+ if (!IS_DIRECTORY_SEP (ptr[0]) || !IS_DIRECTORY_SEP (ptr[1]) || !ptr[2])
+ return 0;
+
+ if (strpbrk (ptr + 2, "*?|<>\"\\/"))
+ return 0;
+
+ return 1;
+}
/* Routines that are no-ops on NT but are defined to get Emacs to compile. */
return 0;
}
+int
+sigmask (int sig)
+{
+ return 0;
+}
+
int
sigblock (int sig)
{
return 0;
}
+int
+sigunblock (int sig)
+{
+ return 0;
+}
+
int
setpgrp (int pid, int gid)
{
extern Lisp_Object Vsystem_configuration;
void
-init_environment ()
+init_environment (char ** argv)
{
int len;
static const char * const tempdirs[] = {
{
char *p;
- char modname[MAX_PATH];
+ static char modname[MAX_PATH];
if (!GetModuleFileName (NULL, modname, MAX_PATH))
abort ();
*p = 0;
SetCurrentDirectory (modname);
+
+ /* Ensure argv[0] has the full path to Emacs. */
+ *p = '\\';
+ argv[0] = modname;
}
+ /* Determine if there is a middle mouse button, to allow parse_button
+ to decide whether right mouse events should be mouse-2 or
+ mouse-3. */
+ XSETINT (Vw32_num_mouse_buttons, GetSystemMetrics (SM_CMOUSEBUTTONS));
+
init_user_info ();
}
get_emacs_configuration (void)
{
char *arch, *oem, *os;
+ int build_num;
/* Determine the processor type. */
switch (get_processor_type ())
/* Let oem be "*" until we figure out how to decode the OEM field. */
oem = "*";
- os = (GetVersion () & OS_WIN95) ? "windows95" : "nt";
+ switch (osinfo_cache.dwPlatformId) {
+ case VER_PLATFORM_WIN32_NT:
+ os = "nt";
+ build_num = osinfo_cache.dwBuildNumber;
+ break;
+ case VER_PLATFORM_WIN32_WINDOWS:
+ if (osinfo_cache.dwMinorVersion == 0) {
+ os = "windows95";
+ } else {
+ os = "windows98";
+ }
+ build_num = LOWORD (osinfo_cache.dwBuildNumber);
+ break;
+ case VER_PLATFORM_WIN32s:
+ /* Not supported, should not happen. */
+ os = "windows32s";
+ build_num = LOWORD (osinfo_cache.dwBuildNumber);
+ break;
+ default:
+ os = "unknown";
+ build_num = 0;
+ break;
+ }
+
+ if (osinfo_cache.dwPlatformId == VER_PLATFORM_WIN32_NT) {
+ sprintf (configuration_buffer, "%s-%s-%s%d.%d.%d", arch, oem, os,
+ get_w32_major_version (), get_w32_minor_version (), build_num);
+ } else {
+ sprintf (configuration_buffer, "%s-%s-%s.%d", arch, oem, os, build_num);
+ }
- sprintf (configuration_buffer, "%s-%s-%s%d.%d", arch, oem, os,
- get_w32_major_version (), get_w32_minor_version ());
return configuration_buffer;
}
static char dir_pathname[MAXPATHLEN+1];
static WIN32_FIND_DATA dir_find_data;
+/* Support shares on a network resource as subdirectories of a read-only
+ root directory. */
+static HANDLE wnet_enum_handle = INVALID_HANDLE_VALUE;
+HANDLE open_unc_volume (char *);
+char *read_unc_volume (HANDLE, char *, int);
+void close_unc_volume (HANDLE);
+
DIR *
opendir (char *filename)
{
/* Opening is done by FindFirstFile. However, a read is inherent to
this operation, so we defer the open until read time. */
- if (!(dirp = (DIR *) malloc (sizeof (DIR))))
- return NULL;
if (dir_find_handle != INVALID_HANDLE_VALUE)
return NULL;
+ if (wnet_enum_handle != INVALID_HANDLE_VALUE)
+ return NULL;
+
+ if (is_unc_volume (filename))
+ {
+ wnet_enum_handle = open_unc_volume (filename);
+ if (wnet_enum_handle == INVALID_HANDLE_VALUE)
+ return NULL;
+ }
+
+ if (!(dirp = (DIR *) malloc (sizeof (DIR))))
+ return NULL;
dirp->dd_fd = 0;
dirp->dd_loc = 0;
FindClose (dir_find_handle);
dir_find_handle = INVALID_HANDLE_VALUE;
}
+ else if (wnet_enum_handle != INVALID_HANDLE_VALUE)
+ {
+ close_unc_volume (wnet_enum_handle);
+ wnet_enum_handle = INVALID_HANDLE_VALUE;
+ }
xfree ((char *) dirp);
}
struct direct *
readdir (DIR *dirp)
{
+ if (wnet_enum_handle != INVALID_HANDLE_VALUE)
+ {
+ if (!read_unc_volume (wnet_enum_handle,
+ dir_find_data.cFileName,
+ MAX_PATH))
+ return NULL;
+ }
/* If we aren't dir_finding, do a find-first, otherwise do a find-next. */
- if (dir_find_handle == INVALID_HANDLE_VALUE)
+ else if (dir_find_handle == INVALID_HANDLE_VALUE)
{
char filename[MAXNAMLEN + 3];
int ln;
return &dir_static;
}
+HANDLE
+open_unc_volume (char *path)
+{
+ NETRESOURCE nr;
+ HANDLE henum;
+ int result;
+
+ nr.dwScope = RESOURCE_GLOBALNET;
+ nr.dwType = RESOURCETYPE_DISK;
+ nr.dwDisplayType = RESOURCEDISPLAYTYPE_SERVER;
+ nr.dwUsage = RESOURCEUSAGE_CONTAINER;
+ nr.lpLocalName = NULL;
+ nr.lpRemoteName = map_w32_filename (path, NULL);
+ nr.lpComment = NULL;
+ nr.lpProvider = NULL;
+
+ result = WNetOpenEnum(RESOURCE_GLOBALNET, RESOURCETYPE_DISK,
+ RESOURCEUSAGE_CONNECTABLE, &nr, &henum);
+
+ if (result == NO_ERROR)
+ return henum;
+ else
+ return INVALID_HANDLE_VALUE;
+}
+
+char *
+read_unc_volume (HANDLE henum, char *readbuf, int size)
+{
+ int count;
+ int result;
+ int bufsize = 512;
+ char *buffer;
+ char *ptr;
+
+ count = 1;
+ buffer = alloca (bufsize);
+ result = WNetEnumResource (wnet_enum_handle, &count, buffer, &bufsize);
+ if (result != NO_ERROR)
+ return NULL;
+
+ /* WNetEnumResource returns \\resource\share...skip forward to "share". */
+ ptr = ((LPNETRESOURCE) buffer)->lpRemoteName;
+ ptr += 2;
+ while (*ptr && !IS_DIRECTORY_SEP (*ptr)) ptr++;
+ ptr++;
+
+ strncpy (readbuf, ptr, size);
+ return readbuf;
+}
+
+void
+close_unc_volume (HANDLE henum)
+{
+ if (henum != INVALID_HANDLE_VALUE)
+ WNetCloseEnum (henum);
+}
+
+DWORD
+unc_volume_file_attributes (char *path)
+{
+ HANDLE henum;
+ DWORD attrs;
+
+ henum = open_unc_volume (path);
+ if (henum == INVALID_HANDLE_VALUE)
+ return -1;
+
+ attrs = FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_DIRECTORY;
+
+ close_unc_volume (henum);
+
+ return attrs;
+}
+
/* Shadow some MSVC runtime functions to map requests for long filenames
to reasonable short names if necessary. This was originally added to
/* MSVC implementation doesn't recognize D_OK. */
path = map_w32_filename (path, NULL);
- if ((attributes = GetFileAttributes (path)) == -1)
+ if (is_unc_volume (path))
+ {
+ attributes = unc_volume_file_attributes (path);
+ if (attributes == -1) {
+ errno = EACCES;
+ return -1;
+ }
+ }
+ else if ((attributes = GetFileAttributes (path)) == -1)
{
/* Should try mapping GetLastError to errno; for now just indicate
that path doesn't exist. */
{
/* Force temp name to require a manufactured 8.3 alias - this
seems to make the second rename work properly. */
- sprintf (p, ".%s.%u", o, i);
+ sprintf (p, "_.%s.%u", o, i);
i++;
result = rename (oldname, temp);
}
/* This loop must surely terminate! */
- while (result < 0 && errno == EEXIST);
+ while (result < 0 && (errno == EEXIST || errno == EACCES));
if (result < 0)
return -1;
}
result = rename (temp, newname);
if (result < 0
- && errno == EEXIST
+ && (errno == EEXIST || errno == EACCES)
&& _chmod (newname, 0666) == 0
&& _unlink (newname) == 0)
result = rename (temp, newname);
}
name = (char *) map_w32_filename (path, &path);
- /* must be valid filename, no wild cards or other illegal characters */
+ /* must be valid filename, no wild cards or other invalid characters */
if (strpbrk (name, "*?|<>\""))
{
errno = ENOENT;
&& (IS_DIRECTORY_SEP (*path) || *path == 0));
name = strcpy (alloca (len + 2), name);
- if (rootdir)
+ if (is_unc_volume (name))
+ {
+ DWORD attrs = unc_volume_file_attributes (name);
+
+ if (attrs == -1)
+ return -1;
+
+ memset (&wfd, 0, sizeof (wfd));
+ wfd.dwFileAttributes = attrs;
+ wfd.ftCreationTime = utc_base_ft;
+ wfd.ftLastAccessTime = utc_base_ft;
+ wfd.ftLastWriteTime = utc_base_ft;
+ strcpy (wfd.cFileName, name);
+ }
+ else if (rootdir)
{
if (!IS_DIRECTORY_SEP (name[len-1]))
strcat (name, "\\");
/* (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 (dir_find_handle != INVALID_HANDLE_VALUE
- && (len = strlen (dir_pathname)),
- strnicmp (name, dir_pathname, len) == 0
+ && strnicmp (name, dir_pathname, len) == 0
&& IS_DIRECTORY_SEP (name[len])
&& stricmp (name + len + 1, dir_static.d_name) == 0)
{
buf->st_nlink = 2; /* doesn't really matter */
fake_inode = 0; /* this doesn't either I think */
}
- else if (!NILP (Vw32_get_true_file_attributes))
+ else if (!NILP (Vw32_get_true_file_attributes)
+ /* No access rights required to get info. */
+ && (fh = CreateFile (name, 0, 0, NULL, OPEN_EXISTING, 0, NULL))
+ != INVALID_HANDLE_VALUE)
{
/* This is more accurate in terms of gettting the correct number
of links, but is quite slow (it is noticable when Emacs is
making a list of file name completions). */
BY_HANDLE_FILE_INFORMATION info;
- /* No access rights required to get info. */
- fh = CreateFile (name, 0, 0, NULL, OPEN_EXISTING, 0, NULL);
-
if (GetFileInformationByHandle (fh, &info))
{
- switch (GetFileType (fh))
- {
- case FILE_TYPE_DISK:
- buf->st_mode = _S_IFREG;
- break;
- case FILE_TYPE_PIPE:
- buf->st_mode = _S_IFIFO;
- break;
- case FILE_TYPE_CHAR:
- case FILE_TYPE_UNKNOWN:
- default:
- buf->st_mode = _S_IFCHR;
- }
buf->st_nlink = info.nNumberOfLinks;
/* Might as well use file index to fake inode values, but this
is not guaranteed to be unique unless we keep a handle open
not unique). Reputedly, there are at most 48 bits of info
(on NTFS, presumably less on FAT). */
fake_inode = info.nFileIndexLow ^ info.nFileIndexHigh;
- CloseHandle (fh);
}
else
{
- errno = EACCES;
- return -1;
+ buf->st_nlink = 1;
+ fake_inode = 0;
}
+
+ switch (GetFileType (fh))
+ {
+ case FILE_TYPE_DISK:
+ buf->st_mode = _S_IFREG;
+ break;
+ case FILE_TYPE_PIPE:
+ buf->st_mode = _S_IFIFO;
+ break;
+ case FILE_TYPE_CHAR:
+ case FILE_TYPE_UNKNOWN:
+ default:
+ buf->st_mode = _S_IFCHR;
+ }
+ CloseHandle (fh);
}
else
{
pfn_WSASetLastError (0);
}
+/* Extend strerror to handle the winsock-specific error codes. */
+struct {
+ int errnum;
+ char * msg;
+} _wsa_errlist[] = {
+ WSAEINTR , "Interrupted function call",
+ WSAEBADF , "Bad file descriptor",
+ WSAEACCES , "Permission denied",
+ WSAEFAULT , "Bad address",
+ WSAEINVAL , "Invalid argument",
+ WSAEMFILE , "Too many open files",
+
+ WSAEWOULDBLOCK , "Resource temporarily unavailable",
+ WSAEINPROGRESS , "Operation now in progress",
+ WSAEALREADY , "Operation already in progress",
+ WSAENOTSOCK , "Socket operation on non-socket",
+ WSAEDESTADDRREQ , "Destination address required",
+ WSAEMSGSIZE , "Message too long",
+ WSAEPROTOTYPE , "Protocol wrong type for socket",
+ WSAENOPROTOOPT , "Bad protocol option",
+ WSAEPROTONOSUPPORT , "Protocol not supported",
+ WSAESOCKTNOSUPPORT , "Socket type not supported",
+ WSAEOPNOTSUPP , "Operation not supported",
+ WSAEPFNOSUPPORT , "Protocol family not supported",
+ WSAEAFNOSUPPORT , "Address family not supported by protocol family",
+ WSAEADDRINUSE , "Address already in use",
+ WSAEADDRNOTAVAIL , "Cannot assign requested address",
+ WSAENETDOWN , "Network is down",
+ WSAENETUNREACH , "Network is unreachable",
+ WSAENETRESET , "Network dropped connection on reset",
+ WSAECONNABORTED , "Software caused connection abort",
+ WSAECONNRESET , "Connection reset by peer",
+ WSAENOBUFS , "No buffer space available",
+ WSAEISCONN , "Socket is already connected",
+ WSAENOTCONN , "Socket is not connected",
+ WSAESHUTDOWN , "Cannot send after socket shutdown",
+ WSAETOOMANYREFS , "Too many references", /* not sure */
+ WSAETIMEDOUT , "Connection timed out",
+ WSAECONNREFUSED , "Connection refused",
+ WSAELOOP , "Network loop", /* not sure */
+ WSAENAMETOOLONG , "Name is too long",
+ WSAEHOSTDOWN , "Host is down",
+ WSAEHOSTUNREACH , "No route to host",
+ WSAENOTEMPTY , "Buffer not empty", /* not sure */
+ WSAEPROCLIM , "Too many processes",
+ WSAEUSERS , "Too many users", /* not sure */
+ WSAEDQUOT , "Double quote in host name", /* really not sure */
+ WSAESTALE , "Data is stale", /* not sure */
+ WSAEREMOTE , "Remote error", /* not sure */
+
+ WSASYSNOTREADY , "Network subsystem is unavailable",
+ WSAVERNOTSUPPORTED , "WINSOCK.DLL version out of range",
+ WSANOTINITIALISED , "Winsock not initialized successfully",
+ WSAEDISCON , "Graceful shutdown in progress",
+#ifdef WSAENOMORE
+ WSAENOMORE , "No more operations allowed", /* not sure */
+ WSAECANCELLED , "Operation cancelled", /* not sure */
+ WSAEINVALIDPROCTABLE , "Invalid procedure table from service provider",
+ WSAEINVALIDPROVIDER , "Invalid service provider version number",
+ WSAEPROVIDERFAILEDINIT , "Unable to initialize a service provider",
+ WSASYSCALLFAILURE , "System call failured",
+ WSASERVICE_NOT_FOUND , "Service not found", /* not sure */
+ WSATYPE_NOT_FOUND , "Class type not found",
+ WSA_E_NO_MORE , "No more resources available", /* really not sure */
+ WSA_E_CANCELLED , "Operation already cancelled", /* really not sure */
+ WSAEREFUSED , "Operation refused", /* not sure */
+#endif
+
+ WSAHOST_NOT_FOUND , "Host not found",
+ WSATRY_AGAIN , "Authoritative host not found during name lookup",
+ WSANO_RECOVERY , "Non-recoverable error during name lookup",
+ WSANO_DATA , "Valid name, no data record of requested type",
+
+ -1, NULL
+};
+
+char *
+sys_strerror(int error_no)
+{
+ int i;
+ static char unknown_msg[40];
+
+ if (error_no >= 0 && error_no < _sys_nerr)
+ return _sys_errlist[error_no];
+
+ for (i = 0; _wsa_errlist[i].errnum >= 0; i++)
+ if (_wsa_errlist[i].errnum == error_no)
+ return _wsa_errlist[i].msg;
+
+ sprintf(unknown_msg, "Unidentified error: %d", error_no);
+ return unknown_msg;
+}
+
/* [andrewi 3-May-96] I've had conflicting results using both methods,
but I believe the method of keeping the socket handle separate (and
insuring it is not inheritable) is the correct one. */
it cannot find the Windows installation file. If this file does
not exist in the expected place, tell the user. */
- if (!noninteractive && !inhibit_window_system) {
- extern Lisp_Object Vwindow_system, Vload_path;
- Lisp_Object init_file;
- int fd;
-
- init_file = build_string ("term/w32-win");
- fd = openp (Vload_path, init_file, ".el:.elc", NULL, 0);
- if (fd < 0) {
- Lisp_Object load_path_print = Fprin1_to_string (Vload_path, Qnil);
- char *init_file_name = XSTRING (init_file)->data;
- char *load_path = XSTRING (load_path_print)->data;
- char *buffer = alloca (1024);
-
- sprintf (buffer,
- "The Emacs Windows initialization file \"%s.el\" "
- "could not be found in your Emacs installation. "
- "Emacs checked the following directories for this file:\n"
- "\n%s\n\n"
- "When Emacs cannot find this file, it usually means that it "
- "was not installed properly, or its distribution file was "
- "not unpacked properly.\nSee the README.W32 file in the "
- "top-level Emacs directory for more information.",
- init_file_name, load_path);
- MessageBox (NULL,
- buffer,
- "Emacs Abort Dialog",
- MB_OK | MB_ICONEXCLAMATION | MB_TASKMODAL);
- close (fd);
+ if (!noninteractive && !inhibit_window_system)
+ {
+ extern Lisp_Object Vwindow_system, Vload_path, Qfile_exists_p;
+ Lisp_Object objs[2];
+ Lisp_Object full_load_path;
+ Lisp_Object init_file;
+ int fd;
+ objs[0] = Vload_path;
+ objs[1] = decode_env_path (0, (getenv ("EMACSLOADPATH")));
+ full_load_path = Fappend (2, objs);
+ init_file = build_string ("term/w32-win");
+ fd = openp (full_load_path, init_file, ".el:.elc", NULL, 0);
+ if (fd < 0)
+ {
+ Lisp_Object load_path_print = Fprin1_to_string (full_load_path, Qnil);
+ char *init_file_name = XSTRING (init_file)->data;
+ char *load_path = XSTRING (load_path_print)->data;
+ char *buffer = alloca (1024);
+
+ sprintf (buffer,
+ "The Emacs Windows initialization file \"%s.el\" "
+ "could not be found in your Emacs installation. "
+ "Emacs checked the following directories for this file:\n"
+ "\n%s\n\n"
+ "When Emacs cannot find this file, it usually means that it "
+ "was not installed properly, or its distribution file was "
+ "not unpacked properly.\nSee the README.W32 file in the "
+ "top-level Emacs directory for more information.",
+ init_file_name, load_path);
+ MessageBox (NULL,
+ buffer,
+ "Emacs Abort Dialog",
+ MB_OK | MB_ICONEXCLAMATION | MB_TASKMODAL);
/* Use the low-level Emacs abort. */
#undef abort
- abort ();
+ abort ();
+ }
+ else
+ {
+ close (fd);
+ }
}
- }
}
void
/* shutdown the socket interface if necessary */
term_winsock ();
#endif
-
- /* Check whether we are shutting down because we cannot find the
- Windows initialization file. Do this during shutdown so that
- Emacs is initialized as possible, and so that it is out of the
- critical startup path. */
- check_windows_init_file ();
}
void
(*drive)++;
}
}
+
+ /* Check to see if Emacs has been installed correctly. */
+ check_windows_init_file ();
}
/* end of nt.c */