]> cygwin.com Git - cygwin-apps/cygutils.git/blame - src/mkshortcut/mkshortcut.c
fix bug in Makefile.am
[cygwin-apps/cygutils.git] / src / mkshortcut / mkshortcut.c
CommitLineData
c4453a3d 1/* mkshortcut.c -- create a Windows shortcut
0bb67178 2 *
0bb67178
CW
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
c4453a3d 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
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)
c4453a3d
CW
25 *
26 * Compile with: gcc -o prog.exe mkshortcut.c -lpopt -lole32 -luuid
7ab0751f 27 * (You'd need to uncomment the moved to common.h lines.)
0bb67178
CW
28 *
29 */
c4453a3d 30
0bb67178
CW
31#if HAVE_CONFIG_H
32# include "config.h"
c4453a3d 33#endif
0bb67178 34#include "common.h"
5f56ef9c
CW
35
36#define NOCOMATTRIBUTE
37
38#include <shlobj.h>
39#include <olectlid.h>
0bb67178
CW
40/* moved to common.h */
41/*
5f56ef9c 42#include <stdio.h>
c4453a3d 43#include <popt.h>
0bb67178
CW
44*/
45
c4453a3d
CW
46static const char versionStr[] = "$Revision$";
47static const char versionID[] = "1.02.0";
0bb67178
CW
48/* for CVS */
49static const char revID[] =
7ab0751f 50 "$Id$";
0bb67178 51static const char copyrightID[] =
7ab0751f 52 "Copyright (c) 2002\nJoshua Daniel Franklin. All rights reserved.\nLicensed under GPL v2.0\n";
5f56ef9c 53
c4453a3d
CW
54typedef 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;
b2682e03 63 char * dir_name_arg;
c4453a3d
CW
64 char * argument_arg;
65 char * target_arg;
66 char * icon_name_arg;
67} optvals;
5f56ef9c 68
c4453a3d
CW
69static int mkshortcut(optvals opts, poptContext optCon);
70static void printTopDescription(FILE * f, char * name);
71static void printBottomDescription(FILE * f, char * name);
72static char * getVersion(char * s, int slen);
73static void usage(poptContext optCon, FILE * f, char * name);
74static void help(poptContext optCon, FILE * f, char * name);
75static void version(poptContext optCon, FILE * f, char * name);
76static void license(poptContext optCon, FILE * f, char * name);
77
78static char *program_name;
79
80int
81main (int argc, const char **argv)
7ab0751f 82{
c4453a3d
CW
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"},
b2682e03
CW
115 { "workingdir", 'w', POPT_ARG_STRING, NULL, 'w', \
116 "set working directory (defaults to directory path of TARGET)", "PATH"},
c4453a3d
CW
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;
b2682e03 160 opts.dir_name_arg = NULL;
c4453a3d
CW
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;
b2682e03
CW
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;
c4453a3d
CW
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;
7ab0751f 251 }
c4453a3d
CW
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
270exit:
271 poptFreeContext(optCon);
272 if (opts.target_arg) { free(opts.target_arg); }
273 if (opts.name_arg) { free(opts.name_arg); }
b2682e03 274 if (opts.dir_name_arg) { free(opts.dir_name_arg); }
c4453a3d
CW
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);
7ab0751f
CW
279}
280
c4453a3d 281int mkshortcut(optvals opts, poptContext optCon)
5f56ef9c 282{
7ab0751f
CW
283 char link_name[MAX_PATH];
284 char exe_name[MAX_PATH];
285 char dir_name[MAX_PATH];
c4453a3d
CW
286 char *buf_str, *tmp_str;
287 int tmp;
7ab0751f
CW
288
289 /* For OLE interface */
5f56ef9c
CW
290 LPITEMIDLIST id;
291 HRESULT hres;
7ab0751f
CW
292 IShellLink *shell_link;
293 IPersistFile *persist_file;
5f56ef9c
CW
294 WCHAR widepath[MAX_PATH];
295
7ab0751f 296 buf_str = (char *) malloc (PATH_MAX);
c4453a3d
CW
297 if (buf_str == NULL) {
298 fprintf (stderr, "%s: out of memory\n", program_name);
299 return(2);
300 }
5f56ef9c 301
7ab0751f 302 /* If there's a colon in the TARGET, it should be a URL */
c4453a3d 303 if (strchr (opts.target_arg, ':') != NULL)
7ab0751f
CW
304 {
305 /* Nope, somebody's trying a W32 path */
c4453a3d
CW
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);
7ab0751f
CW
313 dir_name[0] = '\0'; /* No working dir for URL */
314 }
315 /* Convert TARGET to win32 path */
5f56ef9c 316 else
7ab0751f 317 {
c4453a3d 318 strcpy (buf_str, opts.target_arg);
7ab0751f 319 cygwin_conv_to_full_win32_path (buf_str, exe_name);
5f56ef9c 320
b2682e03
CW
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 }
7ab0751f 333 /* Get a working dir from the exepath */
b2682e03
CW
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 }
7ab0751f 341 }
5f56ef9c 342
7ab0751f 343 /* Generate a name for the link if not given */
c4453a3d 344 if (opts.name_arg == NULL)
5f56ef9c 345 {
7ab0751f 346 /* Strip trailing /'s if any */
c4453a3d 347 strcpy (buf_str, opts.target_arg);
7ab0751f
CW
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);
c4453a3d 363 }
7ab0751f
CW
364 /* User specified a name, so check it and convert */
365 else
5f56ef9c 366 {
c4453a3d 367 if (opts.desktop_flag || opts.smprograms_flag)
7ab0751f
CW
368 {
369 /* Cannot have absolute path relative to Desktop/SM Programs */
c4453a3d
CW
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 }
7ab0751f
CW
376 }
377 /* Sigh. Another W32 path */
c4453a3d
CW
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 }
7ab0751f
CW
386
387 /* Add suffix to link name if necessary */
388 if (strlen (link_name) > 4)
5f56ef9c 389 {
7ab0751f
CW
390 tmp = strlen (link_name) - 4;
391 if (strncmp (link_name + tmp, ".lnk", 4) != 0)
392 strcat (link_name, ".lnk");
5f56ef9c 393 }
7ab0751f
CW
394 else
395 strcat (link_name, ".lnk");
5f56ef9c 396
7ab0751f 397 /* Prepend relative path if necessary */
c4453a3d 398 if (opts.desktop_flag)
7ab0751f
CW
399 {
400 strcpy (buf_str, link_name);
c4453a3d 401 if (!opts.allusers_flag)
7ab0751f
CW
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);
5f56ef9c 414 }
5f56ef9c 415
c4453a3d 416 if (opts.smprograms_flag)
7ab0751f
CW
417 {
418 strcpy (buf_str, link_name);
c4453a3d 419 if (!opts.allusers_flag)
7ab0751f
CW
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);
5f56ef9c 432 }
5f56ef9c 433
7ab0751f
CW
434 /* Beginning of Windows interface */
435 hres = OleInitialize (NULL);
436 if (hres != S_FALSE && hres != S_OK)
437 {
c4453a3d
CW
438 fprintf (stderr, "%s: Could not initialize OLE interface\n",
439 program_name);
440 return (3);
7ab0751f 441 }
5f56ef9c 442
7ab0751f
CW
443 hres =
444 CoCreateInstance (&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
445 &IID_IShellLink, (void **) &shell_link);
446 if (SUCCEEDED (hres))
5f56ef9c 447 {
7ab0751f
CW
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);
c4453a3d
CW
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);
5f56ef9c 463
7ab0751f
CW
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",
c4453a3d
CW
471 program_name, hres);
472 return (3);
7ab0751f
CW
473 }
474 hres = persist_file->lpVtbl->Save (persist_file, widepath, TRUE);
475 if (!SUCCEEDED (hres))
476 {
477 fprintf (stderr,
c4453a3d
CW
478 "%s: Saving \"%s\" failed; does the target directory exist?\n",
479 program_name, link_name);
480 return (3);
7ab0751f
CW
481 }
482 persist_file->lpVtbl->Release (persist_file);
483 shell_link->lpVtbl->Release (shell_link);
484 }
485 else
486 {
c4453a3d
CW
487 fprintf (stderr, "%s: QueryInterface failed\n", program_name);
488 return (3);
7ab0751f 489 }
5f56ef9c 490 }
7ab0751f 491 else
5f56ef9c 492 {
c4453a3d
CW
493 fprintf (stderr, "%s: CoCreateInstance failed\n", program_name);
494 return (3);
5f56ef9c 495 }
c4453a3d
CW
496}
497
498static 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
514static 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}
520static void printBottomDescription(FILE * f, char * name)
521{
014ae39c 522 fprintf(f, "\nNOTE: All filename arguments must be in unix (POSIX) format\n");
c4453a3d
CW
523}
524static 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}
542static void usage(poptContext optCon, FILE * f, char * name)
543{
544 poptPrintUsage(optCon, f, 0);
545}
7ab0751f 546
c4453a3d
CW
547static void help(poptContext optCon, FILE * f, char * name)
548{
549 printTopDescription(f, name);
550 poptPrintHelp(optCon, f, 0);
014ae39c 551 printBottomDescription(f, name);
c4453a3d
CW
552}
553
554static void version(poptContext optCon, FILE * f, char * name)
555{
556 printTopDescription(f, name);
557 fprintf(f, copyrightID);
5f56ef9c 558}
c4453a3d
CW
559
560static void license(poptContext optCon, FILE * f, char * name)
561{
562 printTopDescription(f, name);
563 printLicense(f, name);
c4453a3d
CW
564}
565
This page took 0.078034 seconds and 5 git commands to generate.