]> cygwin.com Git - cygwin-apps/cygutils.git/blob - src/mkshortcut/mkshortcut.c
428a132376864c60f74fe40a6244abfaecc4fe56
[cygwin-apps/cygutils.git] / src / mkshortcut / mkshortcut.c
1 /* mkshortcut.c -- create a Windows shortcut
2 *
3 * Copyright (c) 2002 Joshua Daniel Franklin
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
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
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 *
19 * See the COPYING file for license information.
20 *
21 * Exit values
22 * 1: user error (syntax error)
23 * 2: system error (out of memory, etc.)
24 * 3: windows error (interface failed)
25 *
26 * Compile with: gcc -o prog.exe mkshortcut.c -lpopt -lole32 -luuid
27 * (You'd need to uncomment the moved to common.h lines.)
28 *
29 */
30
31 #if HAVE_CONFIG_H
32 # include "config.h"
33 #endif
34 #include "common.h"
35
36 #define NOCOMATTRIBUTE
37
38 #include <shlobj.h>
39 #include <olectlid.h>
40 /* moved to common.h */
41 /*
42 #include <stdio.h>
43 #include <popt.h>
44 */
45
46 static const char versionStr[] = "$Revision$";
47 static const char versionID[] = "1.02.0";
48 /* for CVS */
49 static const char revID[] =
50 "$Id$";
51 static const char copyrightID[] =
52 "Copyright (c) 2002\nJoshua Daniel Franklin. All rights reserved.\nLicensed under GPL v2.0\n";
53
54 typedef struct optvals_s {
55 int icon_flag;
56 int unix_flag;
57 int windows_flag;
58 int allusers_flag;
59 int desktop_flag;
60 int smprograms_flag;
61 int show_flag;
62 int offset;
63 char * name_arg;
64 char * dir_name_arg;
65 char * argument_arg;
66 char * target_arg;
67 char * icon_name_arg;
68 } optvals;
69
70 static int mkshortcut(optvals opts, poptContext optCon);
71 static void printTopDescription(FILE * f, char * name);
72 static void printBottomDescription(FILE * f, char * name);
73 static char * getVersion(char * s, int slen);
74 static void usage(poptContext optCon, FILE * f, char * name);
75 static void help(poptContext optCon, FILE * f, char * name);
76 static void version(poptContext optCon, FILE * f, char * name);
77 static void license(poptContext optCon, FILE * f, char * name);
78
79 static char *program_name;
80
81 int
82 main (int argc, const char **argv)
83 {
84 poptContext optCon;
85 const char ** rest;
86 int rc;
87 int ec = 0;
88 optvals opts;
89
90 const char *tmp_str;
91 int icon_offset_flag;
92 char icon_name[MAX_PATH];
93 const char * arg;
94
95 struct poptOption helpOptionsTable[] = {
96 { "help", 'h', POPT_ARG_NONE, NULL, '?', \
97 "Show this help message", NULL},
98 { "usage", '\0', POPT_ARG_NONE, NULL, 'u', \
99 "Display brief usage message", NULL},
100 { "version", 'v', POPT_ARG_NONE, NULL, 'v', \
101 "Display version information", NULL},
102 { "license", '\0', POPT_ARG_NONE, NULL, 'l', \
103 "Display licensing information", NULL},
104 { NULL, '\0', 0, NULL, 0, NULL, NULL }
105 };
106
107 struct poptOption generalOptionsTable[] = {
108 { "arguments", 'a', POPT_ARG_STRING, NULL, 'a', \
109 "Use arguments ARGS", "ARGS"},
110 { "icon", 'i', POPT_ARG_STRING, NULL, 'i', \
111 "icon file for link to use", "iconfile"},
112 { "iconoffset", 'j', POPT_ARG_INT, &(opts.offset), 'j', \
113 "offset of icon in icon file (default is 0)", NULL},
114 { "name", 'n', POPT_ARG_STRING, NULL, 'n', \
115 "name for link (defaults to TARGET)", "NAME"},
116 { "show", 's', POPT_ARG_STRING, NULL, 's', \
117 "window to show: normal, minimized, maximized", "norm|min|max"},
118 { "workingdir", 'w', POPT_ARG_STRING, NULL, 'w', \
119 "set working directory (defaults to directory path of TARGET)", "PATH"},
120 { "allusers", 'A', POPT_ARG_VAL, &(opts.allusers_flag), 1, \
121 "use 'All Users' instead of current user for -D,-P", NULL},
122 { "desktop", 'D', POPT_ARG_VAL, &(opts.desktop_flag), 1, \
123 "create link relative to 'Desktop' directory", NULL},
124 { "smprograms", 'P', POPT_ARG_VAL, &(opts.smprograms_flag), 1, \
125 "create link relative to Start Menu 'Programs' directory", NULL},
126 { NULL, '\0', 0, NULL, 0, NULL, NULL }
127 };
128
129 struct poptOption opt[] = {
130 { NULL, '\0', POPT_ARG_INCLUDE_TABLE, generalOptionsTable, 0, \
131 "General options", NULL },
132 { NULL, '\0', POPT_ARG_INCLUDE_TABLE, helpOptionsTable, 0, \
133 "Help options", NULL },
134 { NULL, '\0', 0, NULL, 0, NULL, NULL }
135 };
136
137 tmp_str = strrchr (argv[0], '/');
138 if (tmp_str == NULL) {
139 tmp_str = strrchr (argv[0], '\\');
140 }
141 if (tmp_str == NULL) {
142 tmp_str = argv[0];
143 } else {
144 tmp_str++;
145 }
146 if ((program_name = strdup(tmp_str)) == NULL ) {
147 fprintf(stderr, "%s: memory allocation error\n", argv[0]);
148 exit(2);
149 }
150
151 icon_offset_flag = 0;
152
153 opts.offset = 0;
154 opts.icon_flag = 0;
155 opts.unix_flag = 0;
156 opts.windows_flag = 0;
157 opts.allusers_flag = 0;
158 opts.desktop_flag = 0;
159 opts.smprograms_flag = 0;
160 opts.show_flag = SW_SHOWNORMAL;
161 opts.target_arg = NULL;
162 opts.argument_arg = NULL;
163 opts.name_arg = NULL;
164 opts.dir_name_arg = NULL;
165 opts.icon_name_arg = NULL;
166
167 /* Parse options */
168 optCon = poptGetContext(NULL, argc, argv, opt, 0);
169 poptSetOtherOptionHelp(optCon, "[OPTION]* TARGET");
170 while ((rc = poptGetNextOpt(optCon)) > 0) {
171 switch (rc) {
172 case '?': help(optCon, stderr, program_name);
173 goto exit;
174 case 'u': usage(optCon, stderr, program_name);
175 goto exit;
176 case 'v': version(optCon, stderr, program_name);
177 goto exit;
178 case 'l': license(optCon, stderr, program_name);
179 goto exit;
180 case 'i': opts.icon_flag = 1;
181 if (arg = poptGetOptArg(optCon)) {
182 cygwin_conv_to_full_win32_path (arg, icon_name);
183 if ((opts.icon_name_arg = strdup(icon_name)) == NULL ) {
184 fprintf(stderr, "%s: memory allocation error\n", program_name);
185 ec=2;
186 goto exit;
187 }
188 }
189 break;
190 case 'j': icon_offset_flag = 1;
191 break;
192 case 'n': if (arg = poptGetOptArg(optCon)) {
193 if ((opts.name_arg = strdup(arg)) == NULL ) {
194 fprintf(stderr, "%s: memory allocation error\n", program_name);
195 ec=2;
196 goto exit;
197 }
198 }
199 break;
200 case 's': if (arg = poptGetOptArg(optCon)) {
201 if (strcmp(arg, "min") == 0) {
202 opts.show_flag = SW_SHOWMINNOACTIVE;
203 } else if (strcmp(arg, "max") == 0) {
204 opts.show_flag = SW_SHOWMAXIMIZED;
205 } else if (strcmp(arg, "norm") == 0) {
206 opts.show_flag = SW_SHOWNORMAL;
207 } else {
208 fprintf(stderr, "%s: %s not valid for show window\n",
209 program_name, arg);
210 ec=2;
211 goto exit;
212 }
213 }
214 break;
215 case 'w': if (arg = poptGetOptArg(optCon)) {
216 if ((opts.dir_name_arg = strdup(arg)) == NULL ) {
217 fprintf(stderr, "%s: memory allocation error\n", program_name);
218 ec=2;
219 goto exit;
220 }
221 }
222 break;
223 case 'a': if (arg = poptGetOptArg(optCon)) {
224 if ((opts.argument_arg = strdup(arg)) == NULL ) {
225 fprintf(stderr, "%s: memory allocation error\n", program_name);
226 ec=2;
227 goto exit;
228 }
229 }
230 break;
231 // case 'A'
232 // case 'D'
233 // case 'P' all handled by popt itself
234 }
235 }
236
237 if (icon_offset_flag & !opts.icon_flag) {
238 fprintf(stderr,
239 "%s: --iconoffset|-j only valid in conjuction with --icon|-i\n",
240 program_name);
241 usage(optCon, stderr, program_name);
242 ec=1;
243 goto exit;
244 }
245
246 if (opts.smprograms_flag && opts.desktop_flag) {
247 fprintf(stderr,
248 "%s: --smprograms|-P not valid in conjuction with --desktop|-D\n",
249 program_name);
250 usage(optCon, stderr, program_name);
251 ec=1;
252 goto exit;
253 }
254
255 if (rc < -1 ) {
256 fprintf(stderr, "%s: bad argument %s: %s\n",
257 program_name, poptBadOption(optCon, POPT_BADOPTION_NOALIAS),
258 poptStrerror(rc));
259 ec = 1;
260 goto exit;
261 }
262
263 rest = poptGetArgs(optCon);
264
265 if (rest && *rest) {
266 if ((opts.target_arg = strdup(*rest)) == NULL) {
267 fprintf(stderr, "%s: memory allocation error\n", program_name);
268 ec=2;
269 goto exit;
270 }
271 rest++;
272 if (rest && *rest) {
273 fprintf(stderr, "%s: Too many arguments: ", program_name);
274 while (*rest)
275 fprintf(stderr, "%s ", *rest++);
276 fprintf(stderr, "\n");
277 usage(optCon, stderr, program_name);
278 ec=1;
279 } else {
280 // THE MEAT GOES HERE
281 ec = mkshortcut(opts, optCon);
282 }
283 } else {
284 fprintf(stderr, "%s: TARGET not specified\n", program_name);
285 usage(optCon, stderr, program_name);
286 ec=1;
287 }
288
289 exit:
290 poptFreeContext(optCon);
291 if (opts.target_arg) { free(opts.target_arg); }
292 if (opts.name_arg) { free(opts.name_arg); }
293 if (opts.dir_name_arg) { free(opts.dir_name_arg); }
294 if (opts.argument_arg) { free(opts.argument_arg); }
295 if (opts.icon_name_arg) { free(opts.icon_name_arg); }
296 free(program_name);
297 return(ec);
298 }
299
300 int mkshortcut(optvals opts, poptContext optCon)
301 {
302 char link_name[MAX_PATH];
303 char exe_name[MAX_PATH];
304 char dir_name[MAX_PATH];
305 char *buf_str, *tmp_str;
306 int tmp;
307
308 /* For OLE interface */
309 LPITEMIDLIST id;
310 HRESULT hres;
311 IShellLink *shell_link;
312 IPersistFile *persist_file;
313 WCHAR widepath[MAX_PATH];
314
315 buf_str = (char *) malloc (PATH_MAX);
316 if (buf_str == NULL) {
317 fprintf (stderr, "%s: out of memory\n", program_name);
318 return(2);
319 }
320
321 /* If there's a colon in the TARGET, it should be a URL */
322 if (strchr (opts.target_arg, ':') != NULL)
323 {
324 /* Nope, somebody's trying a W32 path */
325 if (opts.target_arg[1] == ':') {
326 fprintf(stderr, "%s: all paths must be in POSIX format\n",
327 program_name);
328 usage (optCon, stderr, program_name);
329 return(1);
330 }
331 strcpy (exe_name, opts.target_arg);
332 dir_name[0] = '\0'; /* No working dir for URL */
333 }
334 /* Convert TARGET to win32 path */
335 else
336 {
337 strcpy (buf_str, opts.target_arg);
338 cygwin_conv_to_full_win32_path (buf_str, exe_name);
339
340 /* Get a working dir from 'w' option */
341 if (opts.dir_name_arg != NULL)
342 {
343 if (strchr (opts.dir_name_arg, ':') != NULL)
344 {
345 fprintf(stderr, "%s: all paths must be in POSIX format\n",
346 program_name);
347 usage (optCon, stderr, program_name);
348 return(1);
349 }
350 cygwin_conv_to_win32_path (opts.dir_name_arg, dir_name);
351 }
352 /* Get a working dir from the exepath */
353 else
354 {
355 tmp_str = strrchr (exe_name, '\\');
356 tmp = strlen (exe_name) - strlen (tmp_str);
357 strncpy (dir_name, exe_name, tmp);
358 dir_name[tmp] = '\0';
359 }
360 }
361
362 /* Generate a name for the link if not given */
363 if (opts.name_arg == NULL)
364 {
365 /* Strip trailing /'s if any */
366 strcpy (buf_str, opts.target_arg);
367 tmp_str = buf_str;
368 tmp = strlen (buf_str) - 1;
369 while (strrchr (buf_str, '/') == (buf_str + tmp))
370 {
371 buf_str[tmp] = '\0';
372 tmp--;
373 }
374 /* Get basename */
375 while (*buf_str)
376 {
377 if (*buf_str == '/')
378 tmp_str = buf_str + 1;
379 buf_str++;
380 }
381 strcpy (link_name, tmp_str);
382 }
383 /* User specified a name, so check it and convert */
384 else
385 {
386 if (opts.desktop_flag || opts.smprograms_flag)
387 {
388 /* Cannot have absolute path relative to Desktop/SM Programs */
389 if (opts.name_arg[0] == '/') {
390 fprintf(stderr, "%s: absolute pathnames not allowed with -D/-P\n",
391 program_name);
392 usage (optCon, stderr, program_name);
393 return(1);
394 }
395 }
396 /* Sigh. Another W32 path */
397 if (strchr (opts.name_arg, ':') != NULL) {
398 fprintf(stderr, "%s: all paths must be in POSIX format\n",
399 program_name);
400 usage (optCon, stderr, program_name);
401 return(1);
402 }
403 cygwin_conv_to_win32_path (opts.name_arg, link_name);
404 }
405
406 /* Add suffix to link name if necessary */
407 if (strlen (link_name) > 4)
408 {
409 tmp = strlen (link_name) - 4;
410 if (strncmp (link_name + tmp, ".lnk", 4) != 0)
411 strcat (link_name, ".lnk");
412 }
413 else
414 strcat (link_name, ".lnk");
415
416 /* Prepend relative path if necessary */
417 if (opts.desktop_flag)
418 {
419 strcpy (buf_str, link_name);
420 if (!opts.allusers_flag)
421 SHGetSpecialFolderLocation (NULL, CSIDL_DESKTOPDIRECTORY, &id);
422 else
423 SHGetSpecialFolderLocation (NULL, CSIDL_COMMON_DESKTOPDIRECTORY, &id);
424 SHGetPathFromIDList (id, link_name);
425 /* Make sure Win95 without "All Users" has output */
426 if (strlen (link_name) == 0)
427 {
428 SHGetSpecialFolderLocation (NULL, CSIDL_DESKTOPDIRECTORY, &id);
429 SHGetPathFromIDList (id, link_name);
430 }
431 strcat (link_name, "\\");
432 strcat (link_name, buf_str);
433 }
434
435 if (opts.smprograms_flag)
436 {
437 strcpy (buf_str, link_name);
438 if (!opts.allusers_flag)
439 SHGetSpecialFolderLocation (NULL, CSIDL_PROGRAMS, &id);
440 else
441 SHGetSpecialFolderLocation (NULL, CSIDL_COMMON_PROGRAMS, &id);
442 SHGetPathFromIDList (id, link_name);
443 /* Make sure Win95 without "All Users" has output */
444 if (strlen (link_name) == 0)
445 {
446 SHGetSpecialFolderLocation (NULL, CSIDL_PROGRAMS, &id);
447 SHGetPathFromIDList (id, link_name);
448 }
449 strcat (link_name, "\\");
450 strcat (link_name, buf_str);
451 }
452
453 /* Beginning of Windows interface */
454 hres = OleInitialize (NULL);
455 if (hres != S_FALSE && hres != S_OK)
456 {
457 fprintf (stderr, "%s: Could not initialize OLE interface\n",
458 program_name);
459 return (3);
460 }
461
462 hres =
463 CoCreateInstance (&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
464 &IID_IShellLink, (void **) &shell_link);
465 if (SUCCEEDED (hres))
466 {
467 hres =
468 shell_link->lpVtbl->QueryInterface (shell_link, &IID_IPersistFile,
469 (void **) &persist_file);
470 if (SUCCEEDED (hres))
471 {
472 shell_link->lpVtbl->SetPath (shell_link, exe_name);
473 /* Put the POSIX path in the "Description", just to be nice */
474 cygwin_conv_to_full_posix_path (exe_name, buf_str);
475 shell_link->lpVtbl->SetDescription (shell_link, buf_str);
476 shell_link->lpVtbl->SetWorkingDirectory (shell_link, dir_name);
477 if (opts.argument_arg)
478 shell_link->lpVtbl->SetArguments (shell_link, opts.argument_arg);
479 if (opts.icon_flag)
480 shell_link->lpVtbl->SetIconLocation (shell_link, opts.icon_name_arg,
481 opts.offset);
482 if (opts.show_flag != SW_SHOWNORMAL)
483 shell_link->lpVtbl->SetShowCmd(shell_link, opts.show_flag);
484
485
486 /* Make link name Unicode-compliant */
487 hres =
488 MultiByteToWideChar (CP_ACP, 0, link_name, -1, widepath,
489 MAX_PATH);
490 if (!SUCCEEDED (hres))
491 {
492 fprintf (stderr, "%s: Unicode translation failed%d\n",
493 program_name, hres);
494 return (3);
495 }
496 hres = persist_file->lpVtbl->Save (persist_file, widepath, TRUE);
497 if (!SUCCEEDED (hres))
498 {
499 fprintf (stderr,
500 "%s: Saving \"%s\" failed; does the target directory exist?\n",
501 program_name, link_name);
502 return (3);
503 }
504 persist_file->lpVtbl->Release (persist_file);
505 shell_link->lpVtbl->Release (shell_link);
506 }
507 else
508 {
509 fprintf (stderr, "%s: QueryInterface failed\n", program_name);
510 return (3);
511 }
512 }
513 else
514 {
515 fprintf (stderr, "%s: CoCreateInstance failed\n", program_name);
516 return (3);
517 }
518 }
519
520 static char * getVersion(char * s, int slen)
521 {
522 const char *v = strchr (versionStr, ':');
523
524 int len;
525 if (!v) {
526 v = "?";
527 len = 1;
528 } else {
529 v += 2;
530 len = strchr (v, ' ') - v;
531 }
532 snprintf (s,slen,"%.*s", len, v);
533 return s;
534 }
535
536 static void printTopDescription(FILE * f, char * name)
537 {
538 char s[20];
539 fprintf(f, "%s (cygutils) version %s\n", name, getVersion(s, 20));
540 fprintf(f, " create a Windows shortcut\n\n");
541 }
542 static void printBottomDescription(FILE * f, char * name)
543 {
544 fprintf(f, "\nNOTE: All filename arguments must be in unix (POSIX) format\n");
545 }
546 static printLicense(FILE * f, char * name)
547 {
548 fprintf(f, "This program is free software; you can redistribute it and/or\n");
549 fprintf(f, "modify it under the terms of the GNU General Public License\n");
550 fprintf(f, "as published by the Free Software Foundation; either version 2\n");
551 fprintf(f, "of the License, or (at your option) any later version.\n");
552 fprintf(f, "\n");
553 fprintf(f, "This program is distributed in the hope that it will be useful,\n");
554 fprintf(f, "but WITHOUT ANY WARRANTY; without even the implied warranty of\n");
555 fprintf(f, "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n");
556 fprintf(f, "GNU General Public License for more details.\n");
557 fprintf(f, "\n");
558 fprintf(f, "You should have received a copy of the GNU General Public License\n");
559 fprintf(f, "along with this program; if not, write to the Free Software\n");
560 fprintf(f, "Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.\n");
561 fprintf(f, "\n");
562 fprintf(f, "See the COPYING file for license information.\n");
563 }
564 static void usage(poptContext optCon, FILE * f, char * name)
565 {
566 poptPrintUsage(optCon, f, 0);
567 }
568
569 static void help(poptContext optCon, FILE * f, char * name)
570 {
571 printTopDescription(f, name);
572 poptPrintHelp(optCon, f, 0);
573 printBottomDescription(f, name);
574 }
575
576 static void version(poptContext optCon, FILE * f, char * name)
577 {
578 printTopDescription(f, name);
579 fprintf(f, copyrightID);
580 }
581
582 static void license(poptContext optCon, FILE * f, char * name)
583 {
584 printTopDescription(f, name);
585 printLicense(f, name);
586 }
587
This page took 0.063129 seconds and 5 git commands to generate.