]> cygwin.com Git - cygwin-apps/cygutils.git/blame - src/mkshortcut/mkshortcut.c
Add -s option to mkshortcut
[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;
6d007d60 61 int show_flag;
c4453a3d
CW
62 int offset;
63 char * name_arg;
b2682e03 64 char * dir_name_arg;
c4453a3d
CW
65 char * argument_arg;
66 char * target_arg;
67 char * icon_name_arg;
68} optvals;
5f56ef9c 69
c4453a3d
CW
70static int mkshortcut(optvals opts, poptContext optCon);
71static void printTopDescription(FILE * f, char * name);
72static void printBottomDescription(FILE * f, char * name);
73static char * getVersion(char * s, int slen);
74static void usage(poptContext optCon, FILE * f, char * name);
75static void help(poptContext optCon, FILE * f, char * name);
76static void version(poptContext optCon, FILE * f, char * name);
77static void license(poptContext optCon, FILE * f, char * name);
78
79static char *program_name;
80
81int
82main (int argc, const char **argv)
7ab0751f 83{
c4453a3d
CW
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"},
6d007d60
CW
116 { "show", 's', POPT_ARG_STRING, NULL, 's', \
117 "window to show: normal, minimized, maximized", "norm|min|max"},
b2682e03
CW
118 { "workingdir", 'w', POPT_ARG_STRING, NULL, 'w', \
119 "set working directory (defaults to directory path of TARGET)", "PATH"},
c4453a3d
CW
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;
6d007d60 160 opts.show_flag = SW_SHOWNORMAL;
c4453a3d
CW
161 opts.target_arg = NULL;
162 opts.argument_arg = NULL;
163 opts.name_arg = NULL;
b2682e03 164 opts.dir_name_arg = NULL;
c4453a3d
CW
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;
6d007d60
CW
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;
b2682e03
CW
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;
c4453a3d
CW
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;
7ab0751f 270 }
c4453a3d
CW
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
289exit:
290 poptFreeContext(optCon);
291 if (opts.target_arg) { free(opts.target_arg); }
292 if (opts.name_arg) { free(opts.name_arg); }
b2682e03 293 if (opts.dir_name_arg) { free(opts.dir_name_arg); }
c4453a3d
CW
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);
7ab0751f
CW
298}
299
c4453a3d 300int mkshortcut(optvals opts, poptContext optCon)
5f56ef9c 301{
7ab0751f
CW
302 char link_name[MAX_PATH];
303 char exe_name[MAX_PATH];
304 char dir_name[MAX_PATH];
c4453a3d
CW
305 char *buf_str, *tmp_str;
306 int tmp;
7ab0751f
CW
307
308 /* For OLE interface */
5f56ef9c
CW
309 LPITEMIDLIST id;
310 HRESULT hres;
7ab0751f
CW
311 IShellLink *shell_link;
312 IPersistFile *persist_file;
5f56ef9c
CW
313 WCHAR widepath[MAX_PATH];
314
7ab0751f 315 buf_str = (char *) malloc (PATH_MAX);
c4453a3d
CW
316 if (buf_str == NULL) {
317 fprintf (stderr, "%s: out of memory\n", program_name);
318 return(2);
319 }
5f56ef9c 320
7ab0751f 321 /* If there's a colon in the TARGET, it should be a URL */
c4453a3d 322 if (strchr (opts.target_arg, ':') != NULL)
7ab0751f
CW
323 {
324 /* Nope, somebody's trying a W32 path */
c4453a3d
CW
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);
7ab0751f
CW
332 dir_name[0] = '\0'; /* No working dir for URL */
333 }
334 /* Convert TARGET to win32 path */
5f56ef9c 335 else
7ab0751f 336 {
c4453a3d 337 strcpy (buf_str, opts.target_arg);
7ab0751f 338 cygwin_conv_to_full_win32_path (buf_str, exe_name);
5f56ef9c 339
b2682e03
CW
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 }
7ab0751f 352 /* Get a working dir from the exepath */
b2682e03
CW
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 }
7ab0751f 360 }
5f56ef9c 361
7ab0751f 362 /* Generate a name for the link if not given */
c4453a3d 363 if (opts.name_arg == NULL)
5f56ef9c 364 {
7ab0751f 365 /* Strip trailing /'s if any */
c4453a3d 366 strcpy (buf_str, opts.target_arg);
7ab0751f
CW
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);
c4453a3d 382 }
7ab0751f
CW
383 /* User specified a name, so check it and convert */
384 else
5f56ef9c 385 {
c4453a3d 386 if (opts.desktop_flag || opts.smprograms_flag)
7ab0751f
CW
387 {
388 /* Cannot have absolute path relative to Desktop/SM Programs */
c4453a3d
CW
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 }
7ab0751f
CW
395 }
396 /* Sigh. Another W32 path */
c4453a3d
CW
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 }
7ab0751f
CW
405
406 /* Add suffix to link name if necessary */
407 if (strlen (link_name) > 4)
5f56ef9c 408 {
7ab0751f
CW
409 tmp = strlen (link_name) - 4;
410 if (strncmp (link_name + tmp, ".lnk", 4) != 0)
411 strcat (link_name, ".lnk");
5f56ef9c 412 }
7ab0751f
CW
413 else
414 strcat (link_name, ".lnk");
5f56ef9c 415
7ab0751f 416 /* Prepend relative path if necessary */
c4453a3d 417 if (opts.desktop_flag)
7ab0751f
CW
418 {
419 strcpy (buf_str, link_name);
c4453a3d 420 if (!opts.allusers_flag)
7ab0751f
CW
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);
5f56ef9c 433 }
5f56ef9c 434
c4453a3d 435 if (opts.smprograms_flag)
7ab0751f
CW
436 {
437 strcpy (buf_str, link_name);
c4453a3d 438 if (!opts.allusers_flag)
7ab0751f
CW
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);
5f56ef9c 451 }
5f56ef9c 452
7ab0751f
CW
453 /* Beginning of Windows interface */
454 hres = OleInitialize (NULL);
455 if (hres != S_FALSE && hres != S_OK)
456 {
c4453a3d
CW
457 fprintf (stderr, "%s: Could not initialize OLE interface\n",
458 program_name);
459 return (3);
7ab0751f 460 }
5f56ef9c 461
7ab0751f
CW
462 hres =
463 CoCreateInstance (&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
464 &IID_IShellLink, (void **) &shell_link);
465 if (SUCCEEDED (hres))
5f56ef9c 466 {
7ab0751f
CW
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);
c4453a3d
CW
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);
6d007d60
CW
482 if (opts.show_flag != SW_SHOWNORMAL)
483 shell_link->lpVtbl->SetShowCmd(shell_link, opts.show_flag);
484
5f56ef9c 485
7ab0751f
CW
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",
c4453a3d
CW
493 program_name, hres);
494 return (3);
7ab0751f
CW
495 }
496 hres = persist_file->lpVtbl->Save (persist_file, widepath, TRUE);
497 if (!SUCCEEDED (hres))
498 {
499 fprintf (stderr,
c4453a3d
CW
500 "%s: Saving \"%s\" failed; does the target directory exist?\n",
501 program_name, link_name);
502 return (3);
7ab0751f
CW
503 }
504 persist_file->lpVtbl->Release (persist_file);
505 shell_link->lpVtbl->Release (shell_link);
506 }
507 else
508 {
c4453a3d
CW
509 fprintf (stderr, "%s: QueryInterface failed\n", program_name);
510 return (3);
7ab0751f 511 }
5f56ef9c 512 }
7ab0751f 513 else
5f56ef9c 514 {
c4453a3d
CW
515 fprintf (stderr, "%s: CoCreateInstance failed\n", program_name);
516 return (3);
5f56ef9c 517 }
c4453a3d
CW
518}
519
520static 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
536static 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}
542static void printBottomDescription(FILE * f, char * name)
543{
014ae39c 544 fprintf(f, "\nNOTE: All filename arguments must be in unix (POSIX) format\n");
c4453a3d
CW
545}
546static 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}
564static void usage(poptContext optCon, FILE * f, char * name)
565{
566 poptPrintUsage(optCon, f, 0);
567}
7ab0751f 568
c4453a3d
CW
569static void help(poptContext optCon, FILE * f, char * name)
570{
571 printTopDescription(f, name);
572 poptPrintHelp(optCon, f, 0);
014ae39c 573 printBottomDescription(f, name);
c4453a3d
CW
574}
575
576static void version(poptContext optCon, FILE * f, char * name)
577{
578 printTopDescription(f, name);
579 fprintf(f, copyrightID);
5f56ef9c 580}
c4453a3d
CW
581
582static void license(poptContext optCon, FILE * f, char * name)
583{
584 printTopDescription(f, name);
585 printLicense(f, name);
c4453a3d
CW
586}
587
This page took 0.08476 seconds and 5 git commands to generate.