Implement dynamic loading of GnuTLS on Windows.
[bpt/emacs.git] / src / w32.c
index 495f878..d81fdf3 100644 (file)
--- a/src/w32.c
+++ b/src/w32.c
@@ -34,6 +34,7 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 #include <mbstring.h>  /* for _mbspbrk */
 #include <math.h>
 #include <setjmp.h>
+#include <time.h>
 
 /* must include CRT headers *before* config.h */
 
@@ -62,6 +63,8 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 
 #undef strerror
 
+#undef localtime
+
 #include "lisp.h"
 
 #include <pwd.h>
@@ -138,12 +141,17 @@ typedef struct _PROCESS_MEMORY_COUNTERS_EX {
 #include "dispextern.h"                /* for xstrcasecmp */
 #include "coding.h"            /* for Vlocale_coding_system */
 
+#include "careadlinkat.h"
+#include "allocator.h"
+
 /* For serial_configure and serial_open.  */
 #include "process.h"
 
 typedef HRESULT (WINAPI * ShGetFolderPath_fn)
   (IN HWND, IN int, IN HANDLE, IN DWORD, OUT char *);
 
+Lisp_Object QCloaded_from;
+
 void globals_of_w32 (void);
 static DWORD get_rid (PSID);
 
@@ -1505,6 +1513,7 @@ w32_get_resource (char *key, LPDWORD lpdwtype)
 }
 
 char *get_emacs_configuration (void);
+
 void
 init_environment (char ** argv)
 {
@@ -1554,6 +1563,7 @@ init_environment (char ** argv)
     char locale_name[32];
     struct stat ignored;
     char default_home[MAX_PATH];
+    int appdata = 0;
 
     static const struct env_entry
     {
@@ -1591,25 +1601,28 @@ init_environment (char ** argv)
        If not, then we can try to default to the appdata directory under the
        user's profile, which is more likely to be writable.   */
     if (stat ("C:/.emacs", &ignored) < 0)
-    {
-      HRESULT profile_result;
-      /* Dynamically load ShGetFolderPath, as it won't exist on versions
-        of Windows 95 and NT4 that have not been updated to include
-        MSIE 5.  */
-      ShGetFolderPath_fn get_folder_path;
-      get_folder_path = (ShGetFolderPath_fn)
-       GetProcAddress (GetModuleHandle ("shell32.dll"), "SHGetFolderPathA");
-
-      if (get_folder_path != NULL)
-       {
-         profile_result = get_folder_path (NULL, CSIDL_APPDATA, NULL,
-                                           0, default_home);
+      {
+       HRESULT profile_result;
+       /* Dynamically load ShGetFolderPath, as it won't exist on versions
+          of Windows 95 and NT4 that have not been updated to include
+          MSIE 5.  */
+       ShGetFolderPath_fn get_folder_path;
+       get_folder_path = (ShGetFolderPath_fn)
+         GetProcAddress (GetModuleHandle ("shell32.dll"), "SHGetFolderPathA");
+
+       if (get_folder_path != NULL)
+         {
+           profile_result = get_folder_path (NULL, CSIDL_APPDATA, NULL,
+                                             0, default_home);
 
-         /* If we can't get the appdata dir, revert to old behavior.  */
-         if (profile_result == S_OK)
-           env_vars[0].def_value = default_home;
-       }
-    }
+           /* If we can't get the appdata dir, revert to old behavior.  */
+           if (profile_result == S_OK)
+             {
+               env_vars[0].def_value = default_home;
+               appdata = 1;
+             }
+         }
+      }
 
   /* Get default locale info and use it for LANG.  */
   if (GetLocaleInfo (LOCALE_USER_DEFAULT,
@@ -1694,6 +1707,14 @@ init_environment (char ** argv)
                lpval = env_vars[i].def_value;
                dwType = REG_EXPAND_SZ;
                dont_free = 1;
+               if (!strcmp (env_vars[i].name, "HOME") && !appdata)
+                 {
+                   Lisp_Object warning[2];
+                   warning[0] = intern ("initialization");
+                   warning[1] = build_string ("Setting HOME to C:\\ by default is deprecated");
+                   Vdelayed_warnings_list = Fcons (Flist (2, warning),
+                                                   Vdelayed_warnings_list);
+                 }
              }
 
            if (lpval)
@@ -1938,6 +1959,12 @@ gettimeofday (struct timeval *tv, struct timezone *tz)
 
   tv->tv_sec = tb.time;
   tv->tv_usec = tb.millitm * 1000L;
+  /* Implementation note: _ftime sometimes doesn't update the dstflag
+     according to the new timezone when the system timezone is
+     changed.  We could fix that by using GetSystemTime and
+     GetTimeZoneInformation, but that doesn't seem necessary, since
+     Emacs always calls gettimeofday with the 2nd argument NULL (see
+     EMACS_GET_TIME).  */
   if (tz)
     {
       tz->tz_minuteswest = tb.timezone;        /* minutes west of Greenwich  */
@@ -2082,42 +2109,42 @@ GetCachedVolumeInformation (char * root_dir)
   info = lookup_volume_info (root_dir);
 
   if (info == NULL || ! VOLINFO_STILL_VALID (root_dir, info))
-  {
-    char  name[ 256 ];
-    DWORD serialnum;
-    DWORD maxcomp;
-    DWORD flags;
-    char  type[ 256 ];
-
-    /* Info is not cached, or is stale. */
-    if (!GetVolumeInformation (root_dir,
-                              name, sizeof (name),
-                              &serialnum,
-                              &maxcomp,
-                              &flags,
-                              type, sizeof (type)))
-      return NULL;
+    {
+      char  name[ 256 ];
+      DWORD serialnum;
+      DWORD maxcomp;
+      DWORD flags;
+      char  type[ 256 ];
+
+      /* Info is not cached, or is stale. */
+      if (!GetVolumeInformation (root_dir,
+                                name, sizeof (name),
+                                &serialnum,
+                                &maxcomp,
+                                &flags,
+                                type, sizeof (type)))
+       return NULL;
 
-    /* Cache the volume information for future use, overwriting existing
-       entry if present.  */
-    if (info == NULL)
-      {
-       info = (volume_info_data *) xmalloc (sizeof (volume_info_data));
-       add_volume_info (root_dir, info);
-      }
-    else
-      {
-       xfree (info->name);
-       xfree (info->type);
-      }
+      /* Cache the volume information for future use, overwriting existing
+        entry if present.  */
+      if (info == NULL)
+       {
+         info = (volume_info_data *) xmalloc (sizeof (volume_info_data));
+         add_volume_info (root_dir, info);
+       }
+      else
+       {
+         xfree (info->name);
+         xfree (info->type);
+       }
 
-    info->name = xstrdup (name);
-    info->serialnum = serialnum;
-    info->maxcomp = maxcomp;
-    info->flags = flags;
-    info->type = xstrdup (type);
-    info->timestamp = GetTickCount ();
-  }
+      info->name = xstrdup (name);
+      info->serialnum = serialnum;
+      info->maxcomp = maxcomp;
+      info->flags = flags;
+      info->type = xstrdup (type);
+      info->timestamp = GetTickCount ();
+    }
 
   return info;
 }
@@ -3609,8 +3636,8 @@ utime (const char *name, struct utimbuf *times)
 }
 
 \f
-/* Symlink-related functions that always fail.  Used in fileio.c to
-   avoid #ifdef's.  */
+/* Symlink-related functions that always fail.  Used in fileio.c and in
+   sysdep.c to avoid #ifdef's.  */
 int
 symlink (char const *dummy1, char const *dummy2)
 {
@@ -3627,6 +3654,24 @@ readlink (const char *name, char *dummy1, size_t dummy2)
   return -1;
 }
 
+char *
+careadlinkat (int fd, char const *filename,
+              char *buffer, size_t buffer_size,
+              struct allocator const *alloc,
+              ssize_t (*preadlinkat) (int, char const *, char *, size_t))
+{
+  errno = ENOSYS;
+  return NULL;
+}
+
+ssize_t
+careadlinkatcwd (int fd, char const *filename, char *buffer,
+                 size_t buffer_size)
+{
+  (void) fd;
+  return readlink (filename, buffer, buffer_size);
+}
+
 \f
 /* Support for browsing other processes and their attributes.  See
    process.c for the Lisp bindings.  */
@@ -5656,6 +5701,67 @@ sys_write (int fd, const void * buffer, unsigned int count)
   return nchars;
 }
 
+/* The Windows CRT functions are "optimized for speed", so they don't
+   check for timezone and DST changes if they were last called less
+   than 1 minute ago (see http://support.microsoft.com/kb/821231).  So
+   all Emacs features that repeatedly call time functions (e.g.,
+   display-time) are in real danger of missing timezone and DST
+   changes.  Calling tzset before each localtime call fixes that.  */
+struct tm *
+sys_localtime (const time_t *t)
+{
+  tzset ();
+  return localtime (t);
+}
+
+
+\f
+/* Delayed loading of libraries.  */
+
+Lisp_Object Vlibrary_cache;
+
+/* The argument LIBRARIES is an alist that associates a symbol
+   LIBRARY_ID, identifying an external DLL library known to Emacs, to
+   a list of filenames under which the library is usually found.  In
+   most cases, the argument passed as LIBRARIES is the variable
+   `dynamic-library-alist', which is initialized to a list of common
+   library names.  If the function loads the library successfully, it
+   returns the handle of the DLL, and records the filename in the
+   property :loaded-from of LIBRARY_ID; it returns NULL if the library
+   could not be found, or when it was already loaded (because the
+   handle is not recorded anywhere, and so is lost after use).  It
+   would be trivial to save the handle too in :loaded-from, but
+   currently there's no use case for it.  */
+HMODULE
+w32_delayed_load (Lisp_Object libraries, Lisp_Object library_id)
+{
+  HMODULE library_dll = NULL;
+
+  CHECK_SYMBOL (library_id);
+
+  if (CONSP (libraries) && NILP (Fassq (library_id, Vlibrary_cache)))
+    {
+      Lisp_Object found = Qnil;
+      Lisp_Object dlls = Fassq (library_id, libraries);
+
+      if (CONSP (dlls))
+        for (dlls = XCDR (dlls); CONSP (dlls); dlls = XCDR (dlls))
+          {
+            CHECK_STRING_CAR (dlls);
+            if (library_dll = LoadLibrary (SDATA (XCAR (dlls))))
+              {
+                found = XCAR (dlls);
+                break;
+              }
+          }
+
+      Fput (library_id, QCloaded_from, found);
+    }
+
+  return library_dll;
+}
+
+\f
 static void
 check_windows_init_file (void)
 {
@@ -5854,6 +5960,12 @@ globals_of_w32 (void)
   get_process_times_fn = (GetProcessTimes_Proc)
     GetProcAddress (kernel32, "GetProcessTimes");
 
+  QCloaded_from = intern_c_string (":loaded-from");
+  staticpro (&QCloaded_from);
+
+  Vlibrary_cache = Qnil;
+  staticpro (&Vlibrary_cache);
+
   g_b_init_is_windows_9x = 0;
   g_b_init_open_process_token = 0;
   g_b_init_get_token_information = 0;
@@ -6080,5 +6192,72 @@ serial_configure (struct Lisp_Process *p, Lisp_Object contact)
   p->childp = childp2;
 }
 
-/* end of w32.c */
+#ifdef HAVE_GNUTLS
+
+ssize_t
+emacs_gnutls_pull (gnutls_transport_ptr_t p, void* buf, size_t sz)
+{
+  int n, sc, err;
+  SELECT_TYPE fdset;
+  EMACS_TIME timeout;
+  struct Lisp_Process *process = (struct Lisp_Process *)p;
+  int fd = process->infd;
 
+  for (;;)
+    {
+      n = sys_read(fd, (char*)buf, sz);
+
+      if (n >= 0)
+        return n;
+
+      err = errno;
+
+      if (err == EWOULDBLOCK)
+        {
+          /* Set a small timeout.  */
+          EMACS_SET_SECS_USECS(timeout, 1, 0);
+          FD_ZERO (&fdset);
+          FD_SET ((int)fd, &fdset);
+
+          /* Use select with the timeout to poll the selector.  */
+          sc = select (fd + 1, &fdset, (SELECT_TYPE *)0, (SELECT_TYPE *)0,
+                       &timeout);
+
+          if (sc > 0)
+            continue;  /* Try again.  */
+
+          /* Translate the WSAEWOULDBLOCK alias EWOULDBLOCK to EAGAIN.
+             Also accept select return 0 as an indicator to EAGAIN.  */
+          if (sc == 0 || errno == EWOULDBLOCK)
+            err = EAGAIN;
+          else
+            err = errno; /* Other errors are just passed on.  */
+        }
+
+      emacs_gnutls_transport_set_errno (process->gnutls_state, err);
+
+      return -1;
+    }
+}
+
+ssize_t
+emacs_gnutls_push (gnutls_transport_ptr_t p, const void* buf, size_t sz)
+{
+  struct Lisp_Process *process = (struct Lisp_Process *)p;
+  int fd = process->outfd;
+  ssize_t n = sys_write(fd, buf, sz);
+
+  /* 0 or more bytes written means everything went fine.  */
+  if (n >= 0)
+    return n;
+
+  /* Negative bytes written means we got an error in errno.
+     Translate the WSAEWOULDBLOCK alias EWOULDBLOCK to EAGAIN.  */
+  emacs_gnutls_transport_set_errno (process->gnutls_state,
+                                    errno == EWOULDBLOCK ? EAGAIN : errno);
+
+  return -1;
+}
+#endif /* HAVE_GNUTLS */
+
+/* end of w32.c */