Don't reimplement move-if-change badly
[emacs.git] / nt / addpm.c
blobcd91a3e2d80be452917f7076f97ccd7148f391fa
1 /* Add entries to the GNU Emacs Program Manager folder.
2 Copyright (C) 1995, 2001-2015 Free Software Foundation, Inc.
4 This file is part of GNU Emacs.
6 GNU Emacs is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
11 GNU Emacs is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
19 /****************************************************************************
21 * Program: addpm (adds emacs to the Windows program manager)
23 * Usage:
24 * argv[1] = install path for emacs
26 * argv[2] used to be an optional argument for setting the icon.
27 * But now Emacs has a professional looking icon of its own.
28 * If users really want to change it, they can go into the settings of
29 * the shortcut that is created and do it there.
32 /* Use parts of shell API that were introduced by the merge of IE4
33 into the desktop shell. If Windows 95 or NT4 users do not have IE4
34 installed, then the DDE fallback for creating icons the Windows 3.1
35 progman way will be used instead, but that is prone to lockups
36 caused by other applications not servicing their message queues. */
37 #include <stdlib.h>
38 #include <stdio.h>
39 #include <malloc.h>
41 /* MinGW64 barfs if _WIN32_IE is defined to anything below 0x500. */
42 #ifndef MINGW_W64
43 #define _WIN32_IE 0x400
44 #endif
45 /* Request C Object macros for COM interfaces. */
46 #define COBJMACROS 1
48 #include <windows.h>
49 #include <shlobj.h>
50 #include <ddeml.h>
52 #ifndef OLD_PATHS
53 #include "../src/epaths.h"
54 #endif
56 HDDEDATA CALLBACK
57 DdeCallback (UINT uType, UINT uFmt, HCONV hconv,
58 HSZ hsz1, HSZ hsz2, HDDEDATA hdata,
59 DWORD dwData1, DWORD dwData2)
61 return ((HDDEDATA) NULL);
64 #define DdeCommand(str) \
65 DdeClientTransaction (str, strlen (str)+1, conversation, (HSZ)NULL, \
66 CF_TEXT, XTYP_EXECUTE, 30000, NULL)
68 #define REG_ROOT "SOFTWARE\\GNU\\Emacs"
69 #define REG_GTK "SOFTWARE\\GTK\\2.0"
70 #define REG_APP_PATH \
71 "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\emacs.exe"
72 #define REG_RUNEMACS_PATH \
73 "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\runemacs.exe"
75 static struct entry
77 const char *name;
78 const char *value;
80 env_vars[] =
82 #ifdef OLD_PATHS
83 {"emacs_dir", NULL},
84 {"EMACSLOADPATH", "%emacs_dir%/site-lisp;%emacs_dir%/../site-lisp;%emacs_dir%/lisp"},
85 {"SHELL", "%emacs_dir%/bin/cmdproxy.exe"},
86 {"EMACSDATA", "%emacs_dir%/etc"},
87 {"EMACSPATH", "%emacs_dir%/bin"},
88 /* We no longer set INFOPATH because Info-default-directory-list
89 is then ignored. */
90 /* {"INFOPATH", "%emacs_dir%/info"}, */
91 {"EMACSDOC", "%emacs_dir%/etc"},
92 {"TERM", "cmd"}
93 #else /* !OLD_PATHS */
94 {"emacs_dir", NULL},
95 {"EMACSLOADPATH", PATH_SITELOADSEARCH ";" PATH_LOADSEARCH},
96 {"SHELL", PATH_EXEC "/cmdproxy.exe"},
97 {"EMACSDATA", PATH_DATA},
98 {"EMACSPATH", PATH_EXEC},
99 /* We no longer set INFOPATH because Info-default-directory-list
100 is then ignored. */
101 /* {"INFOPATH", "%emacs_dir%/info"}, */
102 {"EMACSDOC", PATH_DOC},
103 {"TERM", "cmd"}
104 #endif
107 BOOL
108 add_registry (const char *path)
110 HKEY hrootkey = NULL;
111 int i;
112 BOOL ok = TRUE;
113 DWORD size;
115 /* Record the location of Emacs to the App Paths key if we have
116 sufficient permissions to do so. This helps Windows find emacs quickly
117 if the user types emacs.exe in the "Run Program" dialog without having
118 emacs on their path. Some applications also use the same registry key
119 to discover the installation directory for programs they are looking for.
120 Multiple installations cannot be handled by this method, but it does not
121 affect the general operation of other installations of Emacs, and we
122 are blindly overwriting the Start Menu entries already.
124 if (RegCreateKeyEx (HKEY_LOCAL_MACHINE, REG_APP_PATH, 0, "",
125 REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL,
126 &hrootkey, NULL) == ERROR_SUCCESS)
128 int len;
129 char *emacs_path;
130 HKEY gtk_key = NULL;
132 len = strlen (path) + 15; /* \bin\emacs.exe + terminator. */
133 emacs_path = (char *) alloca (len);
134 sprintf (emacs_path, "%s\\bin\\emacs.exe", path);
136 RegSetValueEx (hrootkey, NULL, 0, REG_EXPAND_SZ, emacs_path, len);
138 /* Look for a GTK installation. If found, add it to the library search
139 path for Emacs so that the image libraries it provides are available
140 to Emacs regardless of whether it is in the path or not. */
141 if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, REG_GTK, REG_OPTION_NON_VOLATILE,
142 KEY_READ, &gtk_key) == ERROR_SUCCESS)
144 if (RegQueryValueEx (gtk_key, "DllPath", NULL, NULL,
145 NULL, &size) == ERROR_SUCCESS)
147 char *gtk_path = (char *) alloca (size);
148 if (RegQueryValueEx (gtk_key, "DllPath", NULL, NULL,
149 gtk_path, &size) == ERROR_SUCCESS)
151 /* Make sure the emacs bin directory continues to be searched
152 first by including it as well. */
153 char *dll_paths;
154 HKEY runemacs_key = NULL;
155 len = strlen (path) + 5 + size;
156 dll_paths = (char *) alloca (size + strlen (path) + 1);
157 sprintf (dll_paths, "%s\\bin;%s", path, gtk_path);
158 RegSetValueEx (hrootkey, "Path", 0, REG_EXPAND_SZ,
159 dll_paths, len);
161 /* Set the same path for runemacs.exe, as the Explorer shell
162 looks this up, so the above does not take effect when
163 emacs.exe is spawned from runemacs.exe. */
164 if (RegCreateKeyEx (HKEY_LOCAL_MACHINE, REG_RUNEMACS_PATH,
165 0, "", REG_OPTION_NON_VOLATILE,
166 KEY_WRITE, NULL, &runemacs_key, NULL)
167 == ERROR_SUCCESS)
169 RegSetValueEx (runemacs_key, "Path", 0, REG_EXPAND_SZ,
170 dll_paths, len);
172 RegCloseKey (runemacs_key);
176 RegCloseKey (gtk_key);
178 RegCloseKey (hrootkey);
181 /* Previous versions relied on registry settings, but we do not need
182 them any more. If registry settings are installed from a previous
183 version, replace them to ensure they are the current settings.
184 Otherwise, do nothing. */
186 /* Check both the current user and the local machine to see if we
187 have any resources. */
189 if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, REG_ROOT,
190 REG_OPTION_NON_VOLATILE,
191 KEY_WRITE, &hrootkey) != ERROR_SUCCESS
192 && RegOpenKeyEx (HKEY_CURRENT_USER, REG_ROOT,
193 REG_OPTION_NON_VOLATILE,
194 KEY_WRITE, &hrootkey) != ERROR_SUCCESS)
196 return FALSE;
199 for (i = 0; i < (sizeof (env_vars) / sizeof (env_vars[0])); i++)
201 const char * value = env_vars[i].value ? env_vars[i].value : path;
203 if (RegSetValueEx (hrootkey, env_vars[i].name,
204 0, REG_EXPAND_SZ,
205 value, lstrlen (value) + 1) != ERROR_SUCCESS)
206 ok = FALSE;
209 RegCloseKey (hrootkey);
211 return (ok);
215 main (int argc, char *argv[])
217 char start_folder[MAX_PATH + 1];
218 int shortcuts_created = 0;
219 int com_available = 1;
220 char modname[MAX_PATH];
221 const char *prog_name;
222 const char *emacs_path;
223 char *p;
224 int quiet = 0;
225 HRESULT result;
226 IShellLinkA *shortcut;
228 /* If no args specified, use our location to set emacs_path. */
230 if (argc > 1
231 && (argv[1][0] == '/' || argv[1][0] == '-')
232 && argv[1][1] == 'q')
234 quiet = 1;
235 --argc;
236 ++argv;
239 if (argc > 1)
240 emacs_path = argv[1];
241 else
243 if (!GetModuleFileName (NULL, modname, MAX_PATH) ||
244 (p = strrchr (modname, '\\')) == NULL)
246 fprintf (stderr, "fatal error");
247 exit (1);
249 *p = 0;
251 /* Set emacs_path to emacs_dir if we are in "%emacs_dir%\bin". */
252 if ((p = strrchr (modname, '\\')) && stricmp (p, "\\bin") == 0)
254 *p = 0;
255 emacs_path = modname;
257 else
259 fprintf (stderr, "usage: addpm emacs_path\n");
260 exit (1);
263 /* Tell user what we are going to do. */
264 if (!quiet)
266 int result;
268 char msg[ MAX_PATH ];
269 sprintf (msg, "Install Emacs at %s?\n", emacs_path);
270 result = MessageBox (NULL, msg, "Install Emacs",
271 MB_OKCANCEL | MB_ICONQUESTION);
272 if (result != IDOK)
274 fprintf (stderr, "Install canceled\n");
275 exit (1);
280 add_registry (emacs_path);
281 prog_name = "runemacs.exe";
283 /* Try to install globally. */
285 if (!SUCCEEDED (CoInitialize (NULL))
286 || !SUCCEEDED (CoCreateInstance (&CLSID_ShellLink, NULL,
287 CLSCTX_INPROC_SERVER, &IID_IShellLinkA,
288 (void **) &shortcut)))
290 com_available = 0;
293 if (com_available
294 && SHGetSpecialFolderPath (NULL, start_folder, CSIDL_COMMON_PROGRAMS, 0))
296 if (strlen (start_folder) < (MAX_PATH - 20))
298 strcat (start_folder, "\\Gnu Emacs");
299 if (CreateDirectory (start_folder, NULL)
300 || GetLastError () == ERROR_ALREADY_EXISTS)
302 char full_emacs_path[MAX_PATH + 1];
303 IPersistFile *lnk;
304 strcat (start_folder, "\\Emacs.lnk");
305 sprintf (full_emacs_path, "%s\\bin\\%s", emacs_path, prog_name);
306 IShellLinkA_SetPath (shortcut, full_emacs_path);
307 IShellLinkA_SetDescription (shortcut, "GNU Emacs");
308 result = IShellLinkA_QueryInterface (shortcut, &IID_IPersistFile,
309 (void **) &lnk);
310 if (SUCCEEDED (result))
312 wchar_t unicode_path[MAX_PATH];
313 MultiByteToWideChar (CP_ACP, 0, start_folder, -1,
314 unicode_path, MAX_PATH);
315 if (SUCCEEDED (IPersistFile_Save (lnk, unicode_path, TRUE)))
316 shortcuts_created = 1;
317 IPersistFile_Release (lnk);
323 if (!shortcuts_created && com_available
324 && SHGetSpecialFolderPath (NULL, start_folder, CSIDL_PROGRAMS, 0))
326 /* Ensure there is enough room for "...\GNU Emacs\Emacs.lnk". */
327 if (strlen (start_folder) < (MAX_PATH - 20))
329 strcat (start_folder, "\\Gnu Emacs");
330 if (CreateDirectory (start_folder, NULL)
331 || GetLastError () == ERROR_ALREADY_EXISTS)
333 char full_emacs_path[MAX_PATH + 1];
334 IPersistFile *lnk;
335 strcat (start_folder, "\\Emacs.lnk");
336 sprintf (full_emacs_path, "%s\\bin\\%s", emacs_path, prog_name);
337 IShellLinkA_SetPath (shortcut, full_emacs_path);
338 IShellLinkA_SetDescription (shortcut, "GNU Emacs");
339 result = IShellLinkA_QueryInterface (shortcut, &IID_IPersistFile,
340 (void **) &lnk);
341 if (SUCCEEDED (result))
343 wchar_t unicode_path[MAX_PATH];
344 MultiByteToWideChar (CP_ACP, 0, start_folder, -1,
345 unicode_path, MAX_PATH);
346 if (SUCCEEDED (IPersistFile_Save (lnk, unicode_path, TRUE)))
347 shortcuts_created = 1;
348 IPersistFile_Release (lnk);
355 if (com_available)
356 IShellLinkA_Release (shortcut);
358 /* Need to call uninitialize, even if ComInitialize failed. */
359 CoUninitialize ();
361 /* Fallback on old DDE method if the above failed. */
362 if (!shortcuts_created)
364 DWORD dde = 0;
365 HCONV conversation;
366 HSZ progman;
367 char add_item[MAX_PATH*2 + 100];
369 DdeInitialize (&dde, (PFNCALLBACK) DdeCallback, APPCMD_CLIENTONLY, 0);
370 progman = DdeCreateStringHandle (dde, "PROGMAN", CP_WINANSI);
371 conversation = DdeConnect (dde, progman, progman, NULL);
372 if (conversation)
374 DdeCommand ("[CreateGroup (\"Gnu Emacs\")]");
375 DdeCommand ("[ReplaceItem (Emacs)]");
376 sprintf (add_item, "[AddItem (\"%s\\bin\\%s\", Emacs)]",
377 emacs_path, prog_name);
378 DdeCommand (add_item);
380 DdeDisconnect (conversation);
383 DdeFreeStringHandle (dde, progman);
384 DdeUninitialize (dde);
387 return 0;