icon: Convert windows stub into launcher program
[fw/altos] / icon / windows-stub.c
1 /*
2  * Copyright © 2015 Keith Packard <keithp@keithp.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; version 2 of the License.
7  *
8  * This program is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License along
14  * with this program; if not, write to the Free Software Foundation, Inc.,
15  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
16  */
17
18 /* A windows stub program to launch a java program with suitable parameters
19  *
20  * Given that the name of this exe is altusmetrum-foo.exe living in directory bar, and
21  * that it was run with 'args' extra command line parameters, run:
22  *
23  *      javaw.exe -Djava.library.path="bar" -jar "bar/foo-fat.jar" args
24  */
25
26 #define _UNICODE
27 #define UNICODE
28 #include <stdlib.h>
29 #include <windows.h>
30 #include <setupapi.h>
31 #include <stdio.h>
32 #include <stdarg.h>
33 #include <shlwapi.h>
34
35 /* Concatenate a list of strings together
36  */
37 static LPTSTR
38 wcsbuild(LPTSTR first, ...)
39 {
40         va_list args;
41         int     len;
42         LPTSTR  buf;
43         LPTSTR  arg;
44
45         buf = wcsdup(first);
46         va_start(args, first);
47         while ((arg = va_arg(args, LPTSTR)) != NULL) {
48                 len = wcslen(buf) + wcslen(arg) + 1;
49                 buf = realloc(buf, len * sizeof (wchar_t));
50                 wcscat(buf, arg);
51         }
52         va_end(args);
53         return buf;
54 }
55
56 /* Quote a single string, taking care to escape embedded quote and
57  * backslashes within
58  */
59 static LPTSTR
60 quote_arg(LPTSTR arg)
61 {
62         LPTSTR  result;
63         LPTSTR  in, out;
64         int     out_len = 3;    /* quotes and terminating null */
65
66         /* Find quote and backslashes */
67         for (in = arg; *in; in++) {
68                 switch (*in) {
69                 case '"':
70                 case '\\':
71                         out_len += 2;
72                         break;
73                 default:
74                         out_len++;
75                         break;
76                 }
77         }
78
79         result = malloc ((out_len + 1) * sizeof (wchar_t));
80         out = result;
81         *out++ = '"';
82         for (in = arg; *in; in++) {
83                 switch (*in) {
84                 case '"':
85                 case '\\':
86                         *out++ = '\\';
87                         break;
88                 }
89                 *out++ = *in;
90         }
91         *out++ = '"';
92         *out++ = '\0';
93         return result;
94 }
95
96 /* Construct a single string from a list of arguments
97  */
98 static LPTSTR
99 quote_args(LPTSTR *argv, int argc)
100 {
101         LPTSTR  result = NULL, arg;
102         int     i;
103
104         result = malloc(1 * sizeof (wchar_t));
105         result[0] = '\0';
106         for (i = 0; i < argc; i++) {
107                 arg = quote_arg(argv[i]);
108                 result = realloc(result, (wcslen(result) + 1 + wcslen(arg) + 1) * sizeof (wchar_t));
109                 wcscat(result, L" ");
110                 wcscat(result, arg);
111                 free(arg);
112         }
113         return result;
114 }
115
116 /* Return the directory portion of the provided file
117  */
118 static LPTSTR
119 get_dir(LPTSTR file)
120 {
121         DWORD   len = GetFullPathName(file, 0, NULL, NULL);
122         LPTSTR  full = malloc (len * sizeof (wchar_t));
123         GetFullPathName(file, len, full, NULL);
124         PathRemoveFileSpec(full);
125         return full;
126 }
127
128 /* Convert a .exe name into a -fat.jar name, starting
129  * by computing the complete path name of the source filename
130  */
131 static LPTSTR
132 make_jar(LPTSTR file)
133 {
134         DWORD   len = GetFullPathName(file, 0, NULL, NULL);
135         LPTSTR  full = malloc (len * sizeof (wchar_t));
136         LPTSTR  base_part;
137         LPTSTR  jar;
138         LPTSTR  dot;
139         GetFullPathName(file, len, full, &base_part);
140         static const wchar_t head[] = L"altusmetrum-";
141
142         if (wcsncmp(base_part, head, wcslen(head)) == 0)
143                 base_part += wcslen(head);
144         dot = wcsrchr(base_part, '.');
145         if (dot)
146                 *dot = '\0';
147         jar = wcsdup(base_part);
148         PathRemoveFileSpec(full);
149         return wcsbuild(full, L"\\", jar, L"-fat.jar", NULL);
150 }
151
152 /* Build the complete command line from the pieces
153  */
154 static LPTSTR
155 make_cmd(LPTSTR dir, LPTSTR jar, LPTSTR quote_args)
156 {
157         LPTSTR  quote_dir = quote_arg(dir);
158         LPTSTR  quote_jar = quote_arg(jar);
159         LPTSTR  cmd;
160
161         cmd = wcsbuild(L"javaw.exe -Djava.library.path=", quote_dir, L" -jar ", quote_jar, quote_args, NULL);
162         free(quote_jar);
163         free(jar);
164         free(quote_dir);
165         return cmd;
166 }
167
168 int WINAPI
169 WinMain(HINSTANCE instance, HINSTANCE prev_instance, LPSTR cmd_line_a, int cmd_show)
170 {
171         STARTUPINFO             startup_info;
172         PROCESS_INFORMATION     process_information;
173         BOOL                    result;
174         wchar_t                 *command_line;
175         int                     argc;
176         LPTSTR                  *argv = CommandLineToArgvW(GetCommandLine(), &argc);
177         LPTSTR                  my_dir;
178         LPTSTR                  my_jar;
179         LPTSTR                  args = quote_args(argv + 1, argc - 1);
180
181         my_dir = get_dir(argv[0]);
182         my_jar = make_jar(argv[0]);
183         command_line = make_cmd(my_dir, my_jar, args);
184         memset(&startup_info, '\0', sizeof startup_info);
185         startup_info.cb = sizeof startup_info;
186         result = CreateProcess(NULL,
187                                command_line,
188                                NULL,
189                                NULL,
190                                FALSE,
191                                CREATE_NO_WINDOW,
192                                NULL,
193                                NULL,
194                                &startup_info,
195                                &process_information);
196         if (result) {
197                 CloseHandle(process_information.hProcess);
198                 CloseHandle(process_information.hThread);
199         }
200         exit(0);
201 }