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