]> code.delx.au - gnu-emacs/blobdiff - nt/addpm.c
malloc.h hygiene
[gnu-emacs] / nt / addpm.c
index 52c2d32a95920505f84da27a65ff8e51ae0ef15f..ed5014f44aff168674f6c6c81d1337990fa13d1b 100644 (file)
@@ -1,13 +1,12 @@
 /* Add entries to the GNU Emacs Program Manager folder.
-   Copyright (C) 1995, 2001, 2002, 2003, 2004, 2005,
-      2006, 2007, 2008  Free Software Foundation, Inc.
+   Copyright (C) 1995, 2001-2016 Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
-GNU Emacs is free software; you can redistribute it and/or modify
+GNU Emacs is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 3, or (at your option)
-any later version.
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
 
 GNU Emacs is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -15,9 +14,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
-along with GNU Emacs; see the file COPYING.  If not, write to
-the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
-Boston, MA 02110-1301, USA.  */
+along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 
 /****************************************************************************
  *
@@ -25,15 +22,37 @@ Boston, MA 02110-1301, USA.  */
  *
  * Usage:
  *     argv[1] = install path for emacs
- *     argv[2] = full path to icon for emacs (optional)
+ *
+ * argv[2] used to be an optional argument for setting the icon.
+ * But now Emacs has a professional looking icon of its own.
+ * If users really want to change it, they can go into the settings of
+ * the shortcut that is created and do it there.
  */
 
-#include <windows.h>
-#include <ddeml.h>
+/* Use parts of shell API that were introduced by the merge of IE4
+   into the desktop shell.  If Windows 95 or NT4 users do not have IE4
+   installed, then the DDE fallback for creating icons the Windows 3.1
+   progman way will be used instead, but that is prone to lockups
+   caused by other applications not servicing their message queues.  */
 #include <stdlib.h>
 #include <stdio.h>
 #include <malloc.h>
 
+/* MinGW64 barfs if _WIN32_IE is defined to anything below 0x500.  */
+#ifndef MINGW_W64
+#define _WIN32_IE 0x400
+#endif
+/* Request C Object macros for COM interfaces.  */
+#define COBJMACROS 1
+
+#include <windows.h>
+#include <shlobj.h>
+#include <ddeml.h>
+
+#ifndef OLD_PATHS
+#include "../src/epaths.h"
+#endif
+
 HDDEDATA CALLBACK
 DdeCallback (UINT uType, UINT uFmt, HCONV hconv,
             HSZ hsz1, HSZ hsz2, HDDEDATA hdata,
@@ -43,23 +62,23 @@ DdeCallback (UINT uType, UINT uFmt, HCONV hconv,
 }
 
 #define DdeCommand(str)        \
-       DdeClientTransaction (str, strlen (str)+1, HConversation, (HSZ)NULL, \
+       DdeClientTransaction ((LPBYTE)str, strlen (str)+1, conversation, (HSZ)NULL, \
                              CF_TEXT, XTYP_EXECUTE, 30000, NULL)
 
 #define REG_ROOT "SOFTWARE\\GNU\\Emacs"
-#define REG_GTK "SOFTWARE\\GTK\\2.0"
 #define REG_APP_PATH \
   "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\emacs.exe"
 
 static struct entry
 {
-  char *name;
-  char *value;
+  const char *name;
+  const char *value;
 }
 env_vars[] =
 {
+#ifdef OLD_PATHS
   {"emacs_dir", NULL},
-  {"EMACSLOADPATH", "%emacs_dir%/site-lisp;%emacs_dir%/../site-lisp;%emacs_dir%/lisp;%emacs_dir%/leim"},
+  {"EMACSLOADPATH", "%emacs_dir%/site-lisp;%emacs_dir%/../site-lisp;%emacs_dir%/lisp"},
   {"SHELL", "%emacs_dir%/bin/cmdproxy.exe"},
   {"EMACSDATA", "%emacs_dir%/etc"},
   {"EMACSPATH", "%emacs_dir%/bin"},
@@ -68,16 +87,25 @@ env_vars[] =
   /*  {"INFOPATH", "%emacs_dir%/info"},  */
   {"EMACSDOC", "%emacs_dir%/etc"},
   {"TERM", "cmd"}
+#else  /* !OLD_PATHS */
+  {"emacs_dir", NULL},
+  {"EMACSLOADPATH", PATH_SITELOADSEARCH ";" PATH_LOADSEARCH},
+  {"SHELL", PATH_EXEC "/cmdproxy.exe"},
+  {"EMACSDATA", PATH_DATA},
+  {"EMACSPATH", PATH_EXEC},
+  /* We no longer set INFOPATH because Info-default-directory-list
+     is then ignored.  */
+  /*  {"INFOPATH", "%emacs_dir%/info"},  */
+  {"EMACSDOC", PATH_DOC},
+  {"TERM", "cmd"}
+#endif
 };
 
-BOOL
-add_registry (path)
-     char *path;
+void
+add_registry (const char *path)
 {
   HKEY hrootkey = NULL;
   int i;
-  BOOL ok = TRUE;
-  DWORD size;
 
   /* Record the location of Emacs to the App Paths key if we have
      sufficient permissions to do so.  This helps Windows find emacs quickly
@@ -88,44 +116,18 @@ add_registry (path)
      affect the general operation of other installations of Emacs, and we
      are blindly overwriting the Start Menu entries already.
   */
-  if (RegCreateKeyEx (HKEY_LOCAL_MACHINE, REG_APP_PATH, 0, "", 
+  if (RegCreateKeyEx (HKEY_LOCAL_MACHINE, REG_APP_PATH, 0, NULL,
                       REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL,
                       &hrootkey, NULL) == ERROR_SUCCESS)
     {
       int len;
       char *emacs_path;
-      HKEY gtk_key = NULL;
 
       len = strlen (path) + 15; /* \bin\emacs.exe + terminator.  */
       emacs_path = (char *) alloca (len);
       sprintf (emacs_path, "%s\\bin\\emacs.exe", path);
 
-      RegSetValueEx (hrootkey, NULL, 0, REG_SZ, emacs_path, len);
-
-      /* Look for a GTK installation. If found, add it to the library search
-         path for Emacs so that the image libraries it provides are available
-         to Emacs regardless of whether it is in the path or not.  */
-      if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, REG_GTK, REG_OPTION_NON_VOLATILE,
-                        KEY_READ, &gtk_key) == ERROR_SUCCESS)
-        {
-          if (RegQueryValueEx (gtk_key, "DllPath", NULL, NULL,
-                               NULL, &size) == ERROR_SUCCESS)
-            {
-              char *gtk_path = (char *) alloca (size);
-              if (RegQueryValueEx (gtk_key, "DllPath", NULL, NULL,
-                                   gtk_path, &size) == ERROR_SUCCESS)
-                {
-                  /* Make sure the emacs bin directory continues to be searched
-                     first by including it as well.  */
-                  char *dll_paths;
-                  len = strlen (path) + 5 + size;
-                  dll_paths = (char *) alloca (size + strlen (path) + 1);
-                  sprintf (dll_paths, "%s\\bin;%s", path, gtk_path);
-                  RegSetValueEx (hrootkey, "Path", 0, REG_SZ, dll_paths, len);
-                }
-            }
-          RegCloseKey (gtk_key);
-        }
+      RegSetValueEx (hrootkey, NULL, 0, REG_EXPAND_SZ, emacs_path, len);
       RegCloseKey (hrootkey);
     }
 
@@ -137,54 +139,41 @@ add_registry (path)
   /* Check both the current user and the local machine to see if we
      have any resources.  */
 
-  if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, REG_ROOT,
-                     REG_OPTION_NON_VOLATILE,
-                     KEY_WRITE, &hrootkey) != ERROR_SUCCESS
-      && RegOpenKeyEx (HKEY_CURRENT_USER, REG_ROOT,
-                        REG_OPTION_NON_VOLATILE,
-                        KEY_WRITE, &hrootkey) != ERROR_SUCCESS)
-    {
-      return FALSE;
-    }
+  if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, REG_ROOT, 0,
+                   KEY_WRITE | KEY_QUERY_VALUE, &hrootkey) != ERROR_SUCCESS
+      && RegOpenKeyEx (HKEY_CURRENT_USER, REG_ROOT, 0,
+                      KEY_WRITE | KEY_QUERY_VALUE, &hrootkey) != ERROR_SUCCESS)
+    return;
 
   for (i = 0; i < (sizeof (env_vars) / sizeof (env_vars[0])); i++)
     {
-      char * value = env_vars[i].value ? env_vars[i].value : path;
+      const char * value = env_vars[i].value ? env_vars[i].value : path;
 
-      if (RegSetValueEx (hrootkey, env_vars[i].name,
-                        0, REG_EXPAND_SZ,
-                        value, lstrlen (value) + 1) != ERROR_SUCCESS)
-       ok = FALSE;
+      /* Replace only those settings that already exist.  */
+      if (RegQueryValueEx (hrootkey, env_vars[i].name, NULL,
+                          NULL, NULL, NULL) == ERROR_SUCCESS)
+       RegSetValueEx (hrootkey, env_vars[i].name, 0, REG_EXPAND_SZ,
+                      value, lstrlen (value) + 1);
     }
 
   RegCloseKey (hrootkey);
-
-  return (ok);
 }
 
 int
-main (argc, argv)
-     int argc;
-     char *argv[];
+main (int argc, char *argv[])
 {
-  DWORD idDde = 0;
-  HCONV HConversation;
-  HSZ ProgMan;
+  char start_folder[MAX_PATH + 1];
+  int shortcuts_created = 0;
+  int com_available = 1;
   char modname[MAX_PATH];
-  char additem[MAX_PATH*2 + 100];
-  char *prog_name;
-  char *emacs_path;
+  const char *prog_name;
+  const char *emacs_path;
   char *p;
   int quiet = 0;
+  HRESULT result;
+  IShellLinkA *shortcut;
 
   /* If no args specified, use our location to set emacs_path.  */
-#if 0
-  if (argc < 2 || argc > 3)
-    {
-      fprintf (stderr, "usage: addpm [-q] [emacs_path [icon_path]]\n");
-      exit (1);
-    }
-#endif
 
   if (argc > 1
       && (argv[1][0] == '/' || argv[1][0] == '-')
@@ -215,7 +204,7 @@ main (argc, argv)
        }
       else
        {
-         fprintf (stderr, "usage: addpm emacs_path [icon_path]\n");
+         fprintf (stderr, "usage: addpm emacs_path\n");
          exit (1);
        }
 
@@ -230,7 +219,7 @@ main (argc, argv)
                               MB_OKCANCEL | MB_ICONQUESTION);
          if (result != IDOK)
            {
-             fprintf (stderr, "Install cancelled\n");
+             fprintf (stderr, "Install canceled\n");
              exit (1);
            }
        }
@@ -239,32 +228,109 @@ main (argc, argv)
   add_registry (emacs_path);
   prog_name =  "runemacs.exe";
 
-  DdeInitialize (&idDde, (PFNCALLBACK)DdeCallback, APPCMD_CLIENTONLY, 0);
+  /* Try to install globally.  */
 
-  ProgMan = DdeCreateStringHandle (idDde, "PROGMAN", CP_WINANSI);
+  if (!SUCCEEDED (CoInitialize (NULL))
+      || !SUCCEEDED (CoCreateInstance (&CLSID_ShellLink, NULL,
+                                       CLSCTX_INPROC_SERVER, &IID_IShellLinkA,
+                                       (void **) &shortcut)))
+    {
+      com_available = 0;
+    }
 
-  HConversation = DdeConnect (idDde, ProgMan, ProgMan, NULL);
-  if (HConversation != 0)
+  if (com_available
+      && SHGetSpecialFolderPath (NULL, start_folder, CSIDL_COMMON_PROGRAMS, 0))
     {
-      DdeCommand ("[CreateGroup (\"Gnu Emacs\")]");
-      DdeCommand ("[ReplaceItem (Emacs)]");
-      if (argc > 2)
-       sprintf (additem, "[AddItem (\"%s\\bin\\%s\", Emacs, \"%s\")]",
-                emacs_path, prog_name, argv[2]);
-      else
-       sprintf (additem, "[AddItem (\"%s\\bin\\%s\", Emacs)]",
-                emacs_path, prog_name);
-      DdeCommand (additem);
+      if (strlen (start_folder) < (MAX_PATH - 20))
+       {
+         strcat (start_folder, "\\Gnu Emacs");
+         if (CreateDirectory (start_folder, NULL)
+             || GetLastError () == ERROR_ALREADY_EXISTS)
+           {
+             char full_emacs_path[MAX_PATH + 1];
+             IPersistFile *lnk;
+             strcat (start_folder, "\\Emacs.lnk");
+             sprintf (full_emacs_path, "%s\\bin\\%s", emacs_path, prog_name);
+             IShellLinkA_SetPath (shortcut, full_emacs_path);
+             IShellLinkA_SetDescription (shortcut, "GNU Emacs");
+             result = IShellLinkA_QueryInterface (shortcut, &IID_IPersistFile,
+                                                  (void **) &lnk);
+             if (SUCCEEDED (result))
+               {
+                 wchar_t unicode_path[MAX_PATH];
+                 MultiByteToWideChar (CP_ACP, 0, start_folder, -1,
+                                      unicode_path, MAX_PATH);
+                 if (SUCCEEDED (IPersistFile_Save (lnk, unicode_path, TRUE)))
+                   shortcuts_created = 1;
+                 IPersistFile_Release (lnk);
+               }
+           }
+       }
+    }
 
-      DdeDisconnect (HConversation);
+  if (!shortcuts_created && com_available
+      && SHGetSpecialFolderPath (NULL, start_folder, CSIDL_PROGRAMS, 0))
+    {
+      /* Ensure there is enough room for "...\GNU Emacs\Emacs.lnk".  */
+      if (strlen (start_folder) < (MAX_PATH - 20))
+       {
+         strcat (start_folder, "\\Gnu Emacs");
+         if (CreateDirectory (start_folder, NULL)
+             || GetLastError () == ERROR_ALREADY_EXISTS)
+           {
+             char full_emacs_path[MAX_PATH + 1];
+             IPersistFile *lnk;
+             strcat (start_folder, "\\Emacs.lnk");
+             sprintf (full_emacs_path, "%s\\bin\\%s", emacs_path, prog_name);
+             IShellLinkA_SetPath (shortcut, full_emacs_path);
+             IShellLinkA_SetDescription (shortcut, "GNU Emacs");
+             result = IShellLinkA_QueryInterface (shortcut, &IID_IPersistFile,
+                                                  (void **) &lnk);
+             if (SUCCEEDED (result))
+               {
+                 wchar_t unicode_path[MAX_PATH];
+                 MultiByteToWideChar (CP_ACP, 0, start_folder, -1,
+                                      unicode_path, MAX_PATH);
+                 if (SUCCEEDED (IPersistFile_Save (lnk, unicode_path, TRUE)))
+                   shortcuts_created = 1;
+                 IPersistFile_Release (lnk);
+
+               }
+           }
+       }
     }
 
-  DdeFreeStringHandle (idDde, ProgMan);
+  if (com_available)
+    IShellLinkA_Release (shortcut);
 
-  DdeUninitialize (idDde);
+  /* Need to call uninitialize, even if ComInitialize failed.  */
+  CoUninitialize ();
 
-  return (0);
-}
+  /* Fallback on old DDE method if the above failed.  */
+  if (!shortcuts_created)
+    {
+      DWORD dde = 0;
+      HCONV conversation;
+      HSZ progman;
+      char add_item[MAX_PATH*2 + 100];
+
+      DdeInitialize (&dde, (PFNCALLBACK) DdeCallback, APPCMD_CLIENTONLY, 0);
+      progman = DdeCreateStringHandle (dde, "PROGMAN", CP_WINANSI);
+      conversation = DdeConnect (dde, progman, progman, NULL);
+      if (conversation)
+       {
+         DdeCommand ("[CreateGroup (\"Gnu Emacs\")]");
+         DdeCommand ("[ReplaceItem (Emacs)]");
+         sprintf (add_item, "[AddItem (\"%s\\bin\\%s\", Emacs)]",
+                  emacs_path, prog_name);
+         DdeCommand (add_item);
+
+         DdeDisconnect (conversation);
+       }
 
-/* arch-tag: f923609d-b781-4ef4-abce-ca0da29cbbf0
-   (do not change this comment) */
+      DdeFreeStringHandle (dde, progman);
+      DdeUninitialize (dde);
+   }
+
+  return 0;
+}