]>
Commit | Line | Data |
---|---|---|
c4453a3d | 1 | /* mkshortcut.c -- create a Windows shortcut |
0bb67178 | 2 | * |
0bb67178 CW |
3 | * Copyright (c) 2002 Joshua Daniel Franklin |
4 | * | |
3a076f8e CW |
5 | * This program is free software: you can redistribute it and/or modify |
6 | * it under the terms of the GNU General Public License as published by | |
7 | * the Free Software Foundation, either version 3 of the License, or | |
8 | * (at your option) any later version. | |
0bb67178 CW |
9 | * |
10 | * This program is distributed in the hope that it will be useful, | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
3a076f8e | 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
0bb67178 CW |
13 | * GNU General Public License for more details. |
14 | * | |
15 | * You should have received a copy of the GNU General Public License | |
3a076f8e | 16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
0bb67178 | 17 | * |
3a076f8e | 18 | * See the COPYING file for full license information. |
0bb67178 CW |
19 | * |
20 | * Exit values | |
21 | * 1: user error (syntax error) | |
22 | * 2: system error (out of memory, etc.) | |
23 | * 3: windows error (interface failed) | |
c4453a3d CW |
24 | * |
25 | * Compile with: gcc -o prog.exe mkshortcut.c -lpopt -lole32 -luuid | |
7ab0751f | 26 | * (You'd need to uncomment the moved to common.h lines.) |
0bb67178 CW |
27 | * |
28 | */ | |
c4453a3d | 29 | |
0bb67178 | 30 | #if HAVE_CONFIG_H |
fe3a7d70 | 31 | # include "config.h" |
b96d6602 | 32 | #endif |
0bb67178 | 33 | #include "common.h" |
5f56ef9c CW |
34 | |
35 | #define NOCOMATTRIBUTE | |
36 | ||
37 | #include <shlobj.h> | |
835dc6f3 | 38 | #include <olectl.h> |
0bb67178 CW |
39 | /* moved to common.h */ |
40 | /* | |
5f56ef9c | 41 | #include <stdio.h> |
c4453a3d | 42 | #include <popt.h> |
0bb67178 | 43 | */ |
d2d984ab | 44 | #include <sys/cygwin.h> |
0bb67178 | 45 | |
b96d6602 | 46 | static const char versionID[] = PACKAGE_VERSION; |
0bb67178 | 47 | static const char revID[] = |
7ab0751f | 48 | "$Id$"; |
0bb67178 | 49 | static const char copyrightID[] = |
7ab0751f | 50 | "Copyright (c) 2002\nJoshua Daniel Franklin. All rights reserved.\nLicensed under GPL v2.0\n"; |
5f56ef9c | 51 | |
fe3a7d70 CW |
52 | typedef struct optvals_s |
53 | { | |
c4453a3d CW |
54 | int icon_flag; |
55 | int unix_flag; | |
56 | int windows_flag; | |
57 | int allusers_flag; | |
58 | int desktop_flag; | |
59 | int smprograms_flag; | |
6d007d60 | 60 | int show_flag; |
c4453a3d | 61 | int offset; |
fe3a7d70 CW |
62 | char *name_arg; |
63 | char *desc_arg; | |
64 | char *dir_name_arg; | |
65 | char *argument_arg; | |
66 | char *target_arg; | |
67 | char *icon_name_arg; | |
c4453a3d | 68 | } optvals; |
5f56ef9c | 69 | |
a4905427 | 70 | static int mkshortcut (optvals opts); |
fe3a7d70 CW |
71 | static void printTopDescription (FILE * f, char *name); |
72 | static void printBottomDescription (FILE * f, char *name); | |
73 | static const char *getVersion (); | |
a4905427 CW |
74 | static void usage (FILE * f, char *name); |
75 | static void help (FILE * f, char *name); | |
76 | static void version (FILE * f, char *name); | |
77 | static void license (FILE * f, char *name); | |
c4453a3d CW |
78 | |
79 | static char *program_name; | |
a4905427 | 80 | static poptContext optCon; |
c4453a3d CW |
81 | |
82 | int | |
83 | main (int argc, const char **argv) | |
7ab0751f | 84 | { |
fe3a7d70 | 85 | const char **rest; |
c4453a3d CW |
86 | int rc; |
87 | int ec = 0; | |
88 | optvals opts; | |
89 | ||
90 | const char *tmp_str; | |
91 | int icon_offset_flag; | |
fe3a7d70 | 92 | const char *arg; |
c4453a3d CW |
93 | |
94 | struct poptOption helpOptionsTable[] = { | |
fe3a7d70 CW |
95 | {"help", 'h', POPT_ARG_NONE, NULL, '?', |
96 | "Show this help message", NULL}, | |
97 | {"usage", '\0', POPT_ARG_NONE, NULL, 'u', | |
98 | "Display brief usage message", NULL}, | |
99 | {"version", 'v', POPT_ARG_NONE, NULL, 'v', | |
100 | "Display version information", NULL}, | |
101 | {"license", '\0', POPT_ARG_NONE, NULL, 'l', | |
102 | "Display licensing information", NULL}, | |
103 | {NULL, '\0', 0, NULL, 0, NULL, NULL} | |
c4453a3d CW |
104 | }; |
105 | ||
106 | struct poptOption generalOptionsTable[] = { | |
fe3a7d70 CW |
107 | {"arguments", 'a', POPT_ARG_STRING, NULL, 'a', |
108 | "Use arguments ARGS", "ARGS"}, | |
109 | {"desc", 'd', POPT_ARG_STRING, NULL, 'd', | |
110 | "Text for description/tooltip (defaults to POSIX path of TARGET)", | |
111 | "DESC"}, | |
112 | {"icon", 'i', POPT_ARG_STRING, NULL, 'i', | |
113 | "Icon file for link to use", "ICONFILE"}, | |
114 | {"iconoffset", 'j', POPT_ARG_INT, &(opts.offset), 'j', | |
115 | "Offset of icon in icon file (default is 0)", NULL}, | |
116 | {"name", 'n', POPT_ARG_STRING, NULL, 'n', | |
117 | "Name for link (defaults to TARGET)", "NAME"}, | |
118 | {"show", 's', POPT_ARG_STRING, NULL, 's', | |
119 | "Window to show: normal, minimized, maximized", "norm|min|max"}, | |
120 | {"workingdir", 'w', POPT_ARG_STRING, NULL, 'w', | |
121 | "Set working directory (defaults to directory path of TARGET)", "PATH"}, | |
122 | {"allusers", 'A', POPT_ARG_VAL, &(opts.allusers_flag), 1, | |
123 | "Use 'All Users' instead of current user for -D,-P", NULL}, | |
124 | {"desktop", 'D', POPT_ARG_VAL, &(opts.desktop_flag), 1, | |
125 | "Create link relative to 'Desktop' directory", NULL}, | |
126 | {"smprograms", 'P', POPT_ARG_VAL, &(opts.smprograms_flag), 1, | |
127 | "Create link relative to Start Menu 'Programs' directory", NULL}, | |
128 | {NULL, '\0', 0, NULL, 0, NULL, NULL} | |
c4453a3d | 129 | }; |
fe3a7d70 | 130 | |
c4453a3d | 131 | struct poptOption opt[] = { |
fe3a7d70 CW |
132 | {NULL, '\0', POPT_ARG_INCLUDE_TABLE, generalOptionsTable, 0, |
133 | "General options", NULL}, | |
134 | {NULL, '\0', POPT_ARG_INCLUDE_TABLE, helpOptionsTable, 0, | |
135 | "Help options", NULL}, | |
136 | {NULL, '\0', 0, NULL, 0, NULL, NULL} | |
c4453a3d CW |
137 | }; |
138 | ||
139 | tmp_str = strrchr (argv[0], '/'); | |
fe3a7d70 CW |
140 | if (tmp_str == NULL) |
141 | { | |
142 | tmp_str = strrchr (argv[0], '\\'); | |
143 | } | |
144 | if (tmp_str == NULL) | |
145 | { | |
146 | tmp_str = argv[0]; | |
147 | } | |
148 | else | |
149 | { | |
150 | tmp_str++; | |
151 | } | |
152 | if ((program_name = strdup (tmp_str)) == NULL) | |
153 | { | |
154 | fprintf (stderr, "%s: memory allocation error\n", argv[0]); | |
155 | exit (2); | |
156 | } | |
c4453a3d CW |
157 | |
158 | icon_offset_flag = 0; | |
159 | ||
160 | opts.offset = 0; | |
161 | opts.icon_flag = 0; | |
162 | opts.unix_flag = 0; | |
163 | opts.windows_flag = 0; | |
164 | opts.allusers_flag = 0; | |
165 | opts.desktop_flag = 0; | |
166 | opts.smprograms_flag = 0; | |
6d007d60 | 167 | opts.show_flag = SW_SHOWNORMAL; |
c4453a3d CW |
168 | opts.target_arg = NULL; |
169 | opts.argument_arg = NULL; | |
170 | opts.name_arg = NULL; | |
08cb7edc | 171 | opts.desc_arg = NULL; |
b2682e03 | 172 | opts.dir_name_arg = NULL; |
c4453a3d CW |
173 | opts.icon_name_arg = NULL; |
174 | ||
175 | /* Parse options */ | |
fe3a7d70 CW |
176 | optCon = poptGetContext (NULL, argc, argv, opt, 0); |
177 | poptSetOtherOptionHelp (optCon, "[OPTION]* TARGET"); | |
178 | while ((rc = poptGetNextOpt (optCon)) > 0) | |
179 | { | |
180 | switch (rc) | |
181 | { | |
182 | case '?': | |
a4905427 | 183 | help (stdout, program_name); |
fe3a7d70 CW |
184 | goto exit; |
185 | case 'u': | |
a4905427 | 186 | usage (stdout, program_name); |
fe3a7d70 CW |
187 | goto exit; |
188 | case 'v': | |
a4905427 | 189 | version (stdout, program_name); |
fe3a7d70 CW |
190 | goto exit; |
191 | case 'l': | |
a4905427 | 192 | license (stdout, program_name); |
fe3a7d70 CW |
193 | goto exit; |
194 | case 'd': | |
195 | if (arg = poptGetOptArg (optCon)) | |
196 | { | |
197 | if ((opts.desc_arg = strdup (arg)) == NULL) | |
198 | { | |
199 | fprintf (stderr, "%s: memory allocation error\n", | |
200 | program_name); | |
201 | ec = 2; | |
202 | goto exit; | |
203 | } | |
204 | } | |
205 | break; | |
206 | case 'i': | |
207 | opts.icon_flag = 1; | |
208 | if (arg = poptGetOptArg (optCon)) | |
209 | { | |
1ec3acf3 CW |
210 | opts.icon_name_arg = (char *) cygwin_create_path( |
211 | CCP_POSIX_TO_WIN_A, arg); | |
212 | if (opts.icon_name_arg == NULL) | |
fe3a7d70 | 213 | { |
1ec3acf3 CW |
214 | fprintf (stderr, "%s: error converting posix path to win32 (%s)\n", |
215 | program_name, strerror(errno)); | |
fe3a7d70 CW |
216 | ec = 2; |
217 | goto exit; | |
218 | } | |
219 | } | |
220 | break; | |
221 | case 'j': | |
222 | icon_offset_flag = 1; | |
223 | break; | |
224 | case 'n': | |
225 | if (arg = poptGetOptArg (optCon)) | |
226 | { | |
227 | if ((opts.name_arg = strdup (arg)) == NULL) | |
228 | { | |
229 | fprintf (stderr, "%s: memory allocation error\n", | |
230 | program_name); | |
231 | ec = 2; | |
232 | goto exit; | |
233 | } | |
234 | } | |
235 | break; | |
236 | case 's': | |
237 | if (arg = poptGetOptArg (optCon)) | |
238 | { | |
239 | if (strcmp (arg, "min") == 0) | |
240 | { | |
241 | opts.show_flag = SW_SHOWMINNOACTIVE; | |
242 | } | |
243 | else if (strcmp (arg, "max") == 0) | |
244 | { | |
245 | opts.show_flag = SW_SHOWMAXIMIZED; | |
246 | } | |
247 | else if (strcmp (arg, "norm") == 0) | |
248 | { | |
249 | opts.show_flag = SW_SHOWNORMAL; | |
250 | } | |
251 | else | |
252 | { | |
253 | fprintf (stderr, "%s: %s not valid for show window\n", | |
254 | program_name, arg); | |
255 | ec = 2; | |
256 | goto exit; | |
257 | } | |
258 | } | |
259 | break; | |
260 | case 'w': | |
261 | if (arg = poptGetOptArg (optCon)) | |
262 | { | |
263 | if ((opts.dir_name_arg = strdup (arg)) == NULL) | |
264 | { | |
265 | fprintf (stderr, "%s: memory allocation error\n", | |
266 | program_name); | |
267 | ec = 2; | |
268 | goto exit; | |
269 | } | |
270 | } | |
271 | break; | |
272 | case 'a': | |
273 | if (arg = poptGetOptArg (optCon)) | |
274 | { | |
275 | if ((opts.argument_arg = strdup (arg)) == NULL) | |
276 | { | |
277 | fprintf (stderr, "%s: memory allocation error\n", | |
278 | program_name); | |
279 | ec = 2; | |
280 | goto exit; | |
281 | } | |
282 | } | |
283 | break; | |
284 | // case 'A' | |
285 | // case 'D' | |
286 | // case 'P' all handled by popt itself | |
287 | } | |
288 | } | |
289 | ||
290 | if (icon_offset_flag & !opts.icon_flag) | |
291 | { | |
292 | fprintf (stderr, | |
293 | "%s: --iconoffset|-j only valid in conjuction with --icon|-i\n", | |
294 | program_name); | |
a4905427 | 295 | usage (stderr, program_name); |
fe3a7d70 | 296 | ec = 1; |
c4453a3d | 297 | goto exit; |
7ab0751f | 298 | } |
fe3a7d70 CW |
299 | |
300 | if (opts.smprograms_flag && opts.desktop_flag) | |
301 | { | |
302 | fprintf (stderr, | |
303 | "%s: --smprograms|-P not valid in conjuction with --desktop|-D\n", | |
304 | program_name); | |
a4905427 | 305 | usage (stderr, program_name); |
fe3a7d70 CW |
306 | ec = 1; |
307 | goto exit; | |
308 | } | |
309 | ||
310 | if (rc < -1) | |
311 | { | |
312 | fprintf (stderr, "%s: bad argument %s: %s\n", | |
313 | program_name, poptBadOption (optCon, POPT_BADOPTION_NOALIAS), | |
314 | poptStrerror (rc)); | |
315 | ec = 1; | |
316 | goto exit; | |
317 | } | |
318 | ||
319 | rest = poptGetArgs (optCon); | |
320 | ||
321 | if (rest && *rest) | |
322 | { | |
323 | if ((opts.target_arg = strdup (*rest)) == NULL) | |
324 | { | |
325 | fprintf (stderr, "%s: memory allocation error\n", program_name); | |
326 | ec = 2; | |
327 | goto exit; | |
328 | } | |
329 | rest++; | |
330 | if (rest && *rest) | |
331 | { | |
332 | fprintf (stderr, "%s: Too many arguments: ", program_name); | |
333 | while (*rest) | |
334 | fprintf (stderr, "%s ", *rest++); | |
335 | fprintf (stderr, "\n"); | |
a4905427 | 336 | usage (stderr, program_name); |
fe3a7d70 CW |
337 | ec = 1; |
338 | } | |
339 | else | |
340 | { | |
341 | // THE MEAT GOES HERE | |
a4905427 | 342 | ec = mkshortcut (opts); |
fe3a7d70 CW |
343 | } |
344 | } | |
345 | else | |
346 | { | |
347 | fprintf (stderr, "%s: TARGET not specified\n", program_name); | |
a4905427 | 348 | usage (stderr, program_name); |
fe3a7d70 | 349 | ec = 1; |
08cb7edc | 350 | } |
c4453a3d CW |
351 | |
352 | exit: | |
fe3a7d70 CW |
353 | poptFreeContext (optCon); |
354 | if (opts.target_arg) | |
355 | { | |
356 | free (opts.target_arg); | |
357 | } | |
358 | if (opts.name_arg) | |
359 | { | |
360 | free (opts.name_arg); | |
361 | } | |
362 | if (opts.desc_arg) | |
363 | { | |
364 | free (opts.desc_arg); | |
365 | } | |
366 | if (opts.dir_name_arg) | |
367 | { | |
368 | free (opts.dir_name_arg); | |
369 | } | |
370 | if (opts.argument_arg) | |
371 | { | |
372 | free (opts.argument_arg); | |
373 | } | |
374 | if (opts.icon_name_arg) | |
375 | { | |
376 | free (opts.icon_name_arg); | |
377 | } | |
378 | free (program_name); | |
379 | return (ec); | |
7ab0751f CW |
380 | } |
381 | ||
1ec3acf3 CW |
382 | static char * |
383 | xstrndup (const char *string, size_t n) | |
384 | { | |
385 | char *s = strndup (string, n); | |
386 | if (!s) | |
387 | { | |
388 | fprintf (stderr, "%s: out of memory\n", program_name); | |
389 | exit(2); | |
390 | } | |
391 | return s; | |
392 | } | |
393 | ||
394 | static char * | |
395 | xstrncat (char **dest, const char *add, size_t n) | |
396 | { | |
bcf87db2 | 397 | size_t len = strlen (*dest) + n + 1; |
1ec3acf3 CW |
398 | char *s = (char *) realloc (*dest, len * sizeof (char)); |
399 | if (!s) | |
400 | { | |
401 | fprintf (stderr, "%s: out of memory\n", program_name); | |
402 | exit(2); | |
403 | } | |
404 | *dest = s; | |
405 | return strncat (*dest, add, n); | |
406 | } | |
407 | ||
fe3a7d70 | 408 | int |
a4905427 | 409 | mkshortcut (optvals opts) |
5f56ef9c | 410 | { |
1ec3acf3 CW |
411 | char* link_name = NULL; |
412 | char* exe_name = NULL; | |
413 | char* dir_name = NULL; | |
414 | char* desc = NULL; | |
c3bb28d5 | 415 | char *buf_str, *tmp_str, *base_str; |
c4453a3d | 416 | int tmp; |
7ab0751f CW |
417 | |
418 | /* For OLE interface */ | |
5f56ef9c CW |
419 | LPITEMIDLIST id; |
420 | HRESULT hres; | |
7ab0751f CW |
421 | IShellLink *shell_link; |
422 | IPersistFile *persist_file; | |
5f56ef9c | 423 | WCHAR widepath[MAX_PATH]; |
c3bb28d5 | 424 | char link_path[MAX_PATH]; |
5f56ef9c | 425 | |
7ab0751f | 426 | /* If there's a colon in the TARGET, it should be a URL */ |
c4453a3d | 427 | if (strchr (opts.target_arg, ':') != NULL) |
7ab0751f CW |
428 | { |
429 | /* Nope, somebody's trying a W32 path */ | |
fe3a7d70 CW |
430 | if (opts.target_arg[1] == ':') |
431 | { | |
fe3a7d70 CW |
432 | fprintf (stderr, "%s: all paths must be in POSIX format\n", |
433 | program_name); | |
a4905427 | 434 | usage (stderr, program_name); |
fe3a7d70 CW |
435 | return (1); |
436 | } | |
1ec3acf3 CW |
437 | exe_name = xstrndup (opts.target_arg, strlen(opts.target_arg)); |
438 | dir_name = xstrndup ("", 0); /* No working dir for URL */ | |
7ab0751f CW |
439 | } |
440 | /* Convert TARGET to win32 path */ | |
5f56ef9c | 441 | else |
7ab0751f | 442 | { |
1ec3acf3 CW |
443 | buf_str = xstrndup (opts.target_arg, strlen(opts.target_arg)); |
444 | exe_name = (char *) cygwin_create_path (CCP_POSIX_TO_WIN_A, | |
445 | opts.target_arg); | |
446 | if (!exe_name) | |
447 | { | |
448 | fprintf (stderr, "%s: error converting posix path to win32 (%s)\n", | |
449 | program_name, strerror(errno)); | |
450 | free (buf_str); | |
451 | return 2; | |
452 | } | |
5f56ef9c | 453 | |
b2682e03 CW |
454 | /* Get a working dir from 'w' option */ |
455 | if (opts.dir_name_arg != NULL) | |
456 | { | |
457 | if (strchr (opts.dir_name_arg, ':') != NULL) | |
458 | { | |
08cb7edc | 459 | free (buf_str); |
1ec3acf3 | 460 | free (exe_name); |
fe3a7d70 CW |
461 | fprintf (stderr, "%s: all paths must be in POSIX format\n", |
462 | program_name); | |
a4905427 | 463 | usage (stderr, program_name); |
fe3a7d70 | 464 | return (1); |
b2682e03 | 465 | } |
1ec3acf3 CW |
466 | dir_name = (char *) cygwin_create_path (CCP_POSIX_TO_WIN_A, |
467 | opts.dir_name_arg); | |
468 | if (!dir_name) | |
469 | { | |
470 | fprintf (stderr, "%s: error converting posix path to win32 (%s)\n", | |
471 | program_name, strerror(errno)); | |
472 | free (buf_str); | |
473 | free (exe_name); | |
474 | return 2; | |
475 | } | |
b2682e03 | 476 | } |
7ab0751f | 477 | /* Get a working dir from the exepath */ |
b2682e03 CW |
478 | else |
479 | { | |
480 | tmp_str = strrchr (exe_name, '\\'); | |
481 | tmp = strlen (exe_name) - strlen (tmp_str); | |
1ec3acf3 | 482 | dir_name = xstrndup (exe_name, tmp); |
b2682e03 CW |
483 | dir_name[tmp] = '\0'; |
484 | } | |
1ec3acf3 | 485 | if (buf_str) free (buf_str); |
7ab0751f | 486 | } |
5f56ef9c | 487 | |
7ab0751f | 488 | /* Generate a name for the link if not given */ |
c4453a3d | 489 | if (opts.name_arg == NULL) |
5f56ef9c | 490 | { |
7ab0751f | 491 | /* Strip trailing /'s if any */ |
1ec3acf3 | 492 | buf_str = xstrndup (opts.target_arg, strlen(opts.target_arg)); |
c3bb28d5 | 493 | base_str = buf_str; |
7ab0751f CW |
494 | tmp_str = buf_str; |
495 | tmp = strlen (buf_str) - 1; | |
496 | while (strrchr (buf_str, '/') == (buf_str + tmp)) | |
fe3a7d70 CW |
497 | { |
498 | buf_str[tmp] = '\0'; | |
499 | tmp--; | |
500 | } | |
7ab0751f CW |
501 | /* Get basename */ |
502 | while (*buf_str) | |
fe3a7d70 CW |
503 | { |
504 | if (*buf_str == '/') | |
505 | tmp_str = buf_str + 1; | |
506 | buf_str++; | |
507 | } | |
1ec3acf3 | 508 | link_name = xstrndup (tmp_str, strlen (tmp_str)); |
c3bb28d5 | 509 | free (base_str); |
fe3a7d70 | 510 | } |
7ab0751f CW |
511 | /* User specified a name, so check it and convert */ |
512 | else | |
5f56ef9c | 513 | { |
c4453a3d | 514 | if (opts.desktop_flag || opts.smprograms_flag) |
fe3a7d70 CW |
515 | { |
516 | /* Cannot have absolute path relative to Desktop/SM Programs */ | |
517 | if (opts.name_arg[0] == '/') | |
518 | { | |
fe3a7d70 CW |
519 | fprintf (stderr, |
520 | "%s: absolute pathnames not allowed with -D/-P\n", | |
521 | program_name); | |
a4905427 | 522 | usage (stderr, program_name); |
fe3a7d70 CW |
523 | return (1); |
524 | } | |
525 | } | |
7ab0751f | 526 | /* Sigh. Another W32 path */ |
fe3a7d70 CW |
527 | if (strchr (opts.name_arg, ':') != NULL) |
528 | { | |
fe3a7d70 CW |
529 | fprintf (stderr, "%s: all paths must be in POSIX format\n", |
530 | program_name); | |
a4905427 | 531 | usage (stderr, program_name); |
fe3a7d70 CW |
532 | return (1); |
533 | } | |
1ec3acf3 CW |
534 | link_name = (char *) cygwin_create_path ( |
535 | CCP_POSIX_TO_WIN_A | CCP_RELATIVE, opts.name_arg); | |
536 | if (!link_name) | |
537 | { | |
538 | fprintf (stderr, "%s: error converting posix path to win32 (%s)\n", | |
539 | program_name, strerror(errno)); | |
540 | return 2; | |
541 | } | |
fe3a7d70 | 542 | } |
7ab0751f CW |
543 | |
544 | /* Add suffix to link name if necessary */ | |
545 | if (strlen (link_name) > 4) | |
5f56ef9c | 546 | { |
7ab0751f CW |
547 | tmp = strlen (link_name) - 4; |
548 | if (strncmp (link_name + tmp, ".lnk", 4) != 0) | |
1ec3acf3 | 549 | xstrncat (&link_name, ".lnk", 4); |
5f56ef9c | 550 | } |
7ab0751f | 551 | else |
1ec3acf3 | 552 | xstrncat (&link_name, ".lnk", 4); |
5f56ef9c | 553 | |
7ab0751f | 554 | /* Prepend relative path if necessary */ |
c4453a3d | 555 | if (opts.desktop_flag) |
7ab0751f | 556 | { |
1ec3acf3 CW |
557 | char local_buf[MAX_PATH]; |
558 | buf_str = xstrndup (link_name, strlen (link_name)); | |
559 | ||
c4453a3d | 560 | if (!opts.allusers_flag) |
fe3a7d70 | 561 | SHGetSpecialFolderLocation (NULL, CSIDL_DESKTOPDIRECTORY, &id); |
7ab0751f | 562 | else |
fe3a7d70 | 563 | SHGetSpecialFolderLocation (NULL, CSIDL_COMMON_DESKTOPDIRECTORY, &id); |
1ec3acf3 | 564 | SHGetPathFromIDList (id, local_buf); |
7ab0751f | 565 | /* Make sure Win95 without "All Users" has output */ |
1ec3acf3 | 566 | if (strlen (local_buf) == 0) |
fe3a7d70 CW |
567 | { |
568 | SHGetSpecialFolderLocation (NULL, CSIDL_DESKTOPDIRECTORY, &id); | |
1ec3acf3 | 569 | SHGetPathFromIDList (id, local_buf); |
fe3a7d70 | 570 | } |
1ec3acf3 CW |
571 | free (link_name); |
572 | link_name = xstrndup (local_buf, strlen (local_buf)); | |
573 | xstrncat (&link_name, "\\", 1); | |
574 | xstrncat (&link_name, buf_str, strlen (buf_str)); | |
575 | free (buf_str); | |
5f56ef9c | 576 | } |
5f56ef9c | 577 | |
c4453a3d | 578 | if (opts.smprograms_flag) |
7ab0751f | 579 | { |
1ec3acf3 CW |
580 | char local_buf[MAX_PATH]; |
581 | buf_str = xstrndup (link_name, strlen (link_name)); | |
582 | ||
c4453a3d | 583 | if (!opts.allusers_flag) |
fe3a7d70 | 584 | SHGetSpecialFolderLocation (NULL, CSIDL_PROGRAMS, &id); |
7ab0751f | 585 | else |
fe3a7d70 | 586 | SHGetSpecialFolderLocation (NULL, CSIDL_COMMON_PROGRAMS, &id); |
1ec3acf3 | 587 | SHGetPathFromIDList (id, local_buf); |
7ab0751f | 588 | /* Make sure Win95 without "All Users" has output */ |
1ec3acf3 | 589 | if (strlen (local_buf) == 0) |
fe3a7d70 CW |
590 | { |
591 | SHGetSpecialFolderLocation (NULL, CSIDL_PROGRAMS, &id); | |
1ec3acf3 | 592 | SHGetPathFromIDList (id, local_buf); |
fe3a7d70 | 593 | } |
1ec3acf3 CW |
594 | free (link_name); |
595 | link_name = xstrndup (local_buf, strlen (local_buf)); | |
596 | xstrncat (&link_name, "\\", 1); | |
597 | xstrncat (&link_name, buf_str, strlen (buf_str)); | |
598 | free (buf_str); | |
5f56ef9c | 599 | } |
5f56ef9c | 600 | |
c3bb28d5 MG |
601 | /* After Windows 7, saving link to relative path fails; work around that */ |
602 | hres = GetFullPathName (link_name, sizeof (link_path), link_path, 0); | |
603 | if (hres == 0) | |
604 | { | |
605 | fprintf (stderr, "%s: Could not qualify link name\n", program_name); | |
606 | return 2; | |
607 | } | |
608 | free (link_name); | |
609 | link_name = xstrndup (link_path, strlen (link_path)); | |
610 | ||
08cb7edc CW |
611 | /* Setup description text */ |
612 | if (opts.desc_arg != NULL) | |
613 | { | |
1ec3acf3 | 614 | desc = xstrndup (opts.desc_arg, strlen (opts.desc_arg)); |
08cb7edc CW |
615 | } |
616 | else | |
617 | { | |
618 | /* Put the POSIX path in the "Description", just to be nice */ | |
1ec3acf3 CW |
619 | desc = (char *) cygwin_create_path (CCP_WIN_A_TO_POSIX, exe_name); |
620 | if (!desc) | |
621 | { | |
622 | fprintf (stderr, "%s: error converting win32 path to posix (%s)\n", | |
623 | program_name, strerror(errno)); | |
624 | return 2; | |
625 | } | |
08cb7edc CW |
626 | } |
627 | ||
7ab0751f CW |
628 | /* Beginning of Windows interface */ |
629 | hres = OleInitialize (NULL); | |
630 | if (hres != S_FALSE && hres != S_OK) | |
fe3a7d70 | 631 | { |
fe3a7d70 CW |
632 | fprintf (stderr, "%s: Could not initialize OLE interface\n", |
633 | program_name); | |
1ec3acf3 CW |
634 | free (link_name); |
635 | free (exe_name); | |
636 | free (dir_name); | |
637 | free (desc); | |
c4453a3d | 638 | return (3); |
7ab0751f | 639 | } |
5f56ef9c | 640 | |
7ab0751f CW |
641 | hres = |
642 | CoCreateInstance (&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, | |
fe3a7d70 | 643 | &IID_IShellLink, (void **) &shell_link); |
7ab0751f | 644 | if (SUCCEEDED (hres)) |
5f56ef9c | 645 | { |
7ab0751f | 646 | hres = |
fe3a7d70 CW |
647 | shell_link->lpVtbl->QueryInterface (shell_link, &IID_IPersistFile, |
648 | (void **) &persist_file); | |
7ab0751f | 649 | if (SUCCEEDED (hres)) |
fe3a7d70 CW |
650 | { |
651 | shell_link->lpVtbl->SetPath (shell_link, exe_name); | |
652 | shell_link->lpVtbl->SetDescription (shell_link, desc); | |
653 | shell_link->lpVtbl->SetWorkingDirectory (shell_link, dir_name); | |
654 | if (opts.argument_arg) | |
655 | shell_link->lpVtbl->SetArguments (shell_link, opts.argument_arg); | |
656 | if (opts.icon_flag) | |
657 | shell_link->lpVtbl->SetIconLocation (shell_link, | |
658 | opts.icon_name_arg, | |
659 | opts.offset); | |
6d007d60 | 660 | if (opts.show_flag != SW_SHOWNORMAL) |
fe3a7d70 CW |
661 | shell_link->lpVtbl->SetShowCmd (shell_link, opts.show_flag); |
662 | ||
663 | ||
664 | /* Make link name Unicode-compliant */ | |
665 | hres = | |
666 | MultiByteToWideChar (CP_ACP, 0, link_name, -1, widepath, | |
667 | MAX_PATH); | |
668 | if (!SUCCEEDED (hres)) | |
669 | { | |
fe3a7d70 CW |
670 | fprintf (stderr, "%s: Unicode translation failed%d\n", |
671 | program_name, hres); | |
1ec3acf3 CW |
672 | free (link_name); |
673 | free (exe_name); | |
674 | free (dir_name); | |
675 | free (desc); | |
fe3a7d70 CW |
676 | return (3); |
677 | } | |
678 | hres = persist_file->lpVtbl->Save (persist_file, widepath, TRUE); | |
679 | if (!SUCCEEDED (hres)) | |
680 | { | |
fe3a7d70 CW |
681 | fprintf (stderr, |
682 | "%s: Saving \"%s\" failed; does the target directory exist?\n", | |
683 | program_name, link_name); | |
1ec3acf3 CW |
684 | free (link_name); |
685 | free (exe_name); | |
686 | free (dir_name); | |
687 | free (desc); | |
fe3a7d70 CW |
688 | return (3); |
689 | } | |
690 | persist_file->lpVtbl->Release (persist_file); | |
691 | shell_link->lpVtbl->Release (shell_link); | |
d2d984ab CW |
692 | |
693 | /* If we are creating shortcut for all users, ensure it is readable by all users */ | |
694 | if (opts.allusers_flag) | |
695 | { | |
696 | char *posixpath = (char *) cygwin_create_path ( | |
697 | CCP_WIN_W_TO_POSIX | CCP_ABSOLUTE, widepath); | |
698 | if (posixpath && *posixpath) | |
699 | { | |
700 | struct stat statbuf; | |
701 | if (stat(posixpath, &statbuf)) | |
702 | { | |
703 | fprintf (stderr, | |
704 | "%s: stat \"%s\" failed\n", | |
705 | program_name, posixpath); | |
706 | } | |
707 | else if (chmod(posixpath, statbuf.st_mode|S_IRUSR|S_IRGRP|S_IROTH)) | |
708 | { | |
709 | fprintf (stderr, | |
710 | "%s: chmod \"%s\" failed\n", | |
711 | program_name, posixpath); | |
712 | } | |
713 | } | |
714 | free(posixpath); | |
715 | } | |
1ec3acf3 CW |
716 | free (link_name); |
717 | free (exe_name); | |
718 | free (dir_name); | |
719 | free (desc); | |
3d86489d | 720 | return (0); |
fe3a7d70 | 721 | } |
7ab0751f | 722 | else |
fe3a7d70 | 723 | { |
fe3a7d70 | 724 | fprintf (stderr, "%s: QueryInterface failed\n", program_name); |
1ec3acf3 CW |
725 | free (link_name); |
726 | free (exe_name); | |
727 | free (dir_name); | |
728 | free (desc); | |
fe3a7d70 CW |
729 | return (3); |
730 | } | |
5f56ef9c | 731 | } |
7ab0751f | 732 | else |
b96d6602 | 733 | { |
c4453a3d | 734 | fprintf (stderr, "%s: CoCreateInstance failed\n", program_name); |
1ec3acf3 CW |
735 | free (link_name); |
736 | free (exe_name); | |
737 | free (dir_name); | |
738 | free (desc); | |
c4453a3d | 739 | return (3); |
5f56ef9c | 740 | } |
c4453a3d CW |
741 | } |
742 | ||
fe3a7d70 CW |
743 | static const char * |
744 | getVersion () | |
c4453a3d | 745 | { |
b96d6602 | 746 | return versionID; |
c4453a3d CW |
747 | } |
748 | ||
fe3a7d70 CW |
749 | static void |
750 | printTopDescription (FILE * f, char *name) | |
c4453a3d CW |
751 | { |
752 | char s[20]; | |
fe3a7d70 CW |
753 | fprintf (f, "%s is part of cygutils version %s\n", name, getVersion ()); |
754 | fprintf (f, " create a Windows shortcut\n\n"); | |
c4453a3d | 755 | } |
fe3a7d70 CW |
756 | static void |
757 | printBottomDescription (FILE * f, char *name) | |
c4453a3d | 758 | { |
fe3a7d70 CW |
759 | fprintf (f, |
760 | "\nNOTE: All filename arguments must be in unix (POSIX) format\n"); | |
c4453a3d | 761 | } |
fe3a7d70 CW |
762 | |
763 | static | |
764 | printLicense (FILE * f, char *name) | |
c4453a3d | 765 | { |
fe3a7d70 | 766 | fprintf (f, |
3a076f8e CW |
767 | "This program is free software: you can redistribute it and/or modify\n" |
768 | "it under the terms of the GNU General Public License as published by\n" | |
769 | "the Free Software Foundation, either version 3 of the License, or\n" | |
770 | "(at your option) any later version.\n\n" | |
771 | "This program is distributed in the hope that it will be useful,\n" | |
772 | "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" | |
773 | "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" | |
774 | "GNU General Public License for more details.\n\n" | |
775 | "You should have received a copy of the GNU General Public License\n" | |
776 | "along with this program. If not, see <http://www.gnu.org/licenses/>.\n\n" | |
777 | "See the COPYING file for full license information.\n"); | |
c4453a3d | 778 | } |
3a076f8e | 779 | |
fe3a7d70 | 780 | static void |
a4905427 | 781 | usage (FILE * f, char *name) |
c4453a3d | 782 | { |
fe3a7d70 | 783 | poptPrintUsage (optCon, f, 0); |
c4453a3d | 784 | } |
7ab0751f | 785 | |
fe3a7d70 | 786 | static void |
a4905427 | 787 | help (FILE * f, char *name) |
c4453a3d | 788 | { |
fe3a7d70 CW |
789 | printTopDescription (f, name); |
790 | poptPrintHelp (optCon, f, 0); | |
791 | printBottomDescription (f, name); | |
c4453a3d CW |
792 | } |
793 | ||
fe3a7d70 | 794 | static void |
a4905427 | 795 | version (FILE * f, char *name) |
c4453a3d | 796 | { |
fe3a7d70 CW |
797 | printTopDescription (f, name); |
798 | fprintf (f, copyrightID); | |
5f56ef9c | 799 | } |
c4453a3d | 800 | |
fe3a7d70 | 801 | static void |
a4905427 | 802 | license (FILE * f, char *name) |
c4453a3d | 803 | { |
fe3a7d70 CW |
804 | printTopDescription (f, name); |
805 | printLicense (f, name); | |
806 | } |