1 /* mkshortcut.c -- create a Windows shortcut
3 * Copyright (c) 2002 Joshua Daniel Franklin
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.
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.
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.
19 * See the COPYING file for license information.
22 * 1: user error (syntax error)
23 * 2: system error (out of memory, etc.)
24 * 3: windows error (interface failed)
26 * Compile with: gcc -o prog.exe mkshortcut.c -lpopt -lole32 -luuid
27 * (You'd need to uncomment the moved to common.h lines.)
36 #define NOCOMATTRIBUTE
40 /* moved to common.h */
46 static const char versionStr
[] = "$Revision$";
47 static const char versionID
[] = "1.02.0";
49 static const char revID
[] =
51 static const char copyrightID
[] =
52 "Copyright (c) 2002\nJoshua Daniel Franklin. All rights reserved.\nLicensed under GPL v2.0\n";
54 typedef struct optvals_s
{
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
);
79 static char *program_name
;
82 main (int argc
, const char **argv
)
92 char icon_name
[MAX_PATH
];
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
}
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
}
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
}
137 tmp_str
= strrchr (argv
[0], '/');
138 if (tmp_str
== NULL
) {
139 tmp_str
= strrchr (argv
[0], '\\');
141 if (tmp_str
== NULL
) {
146 if ((program_name
= strdup(tmp_str
)) == NULL
) {
147 fprintf(stderr
, "%s: memory allocation error\n", argv
[0]);
151 icon_offset_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
;
168 optCon
= poptGetContext(NULL
, argc
, argv
, opt
, 0);
169 poptSetOtherOptionHelp(optCon
, "[OPTION]* TARGET");
170 while ((rc
= poptGetNextOpt(optCon
)) > 0) {
172 case '?': help(optCon
, stderr
, program_name
);
174 case 'u': usage(optCon
, stderr
, program_name
);
176 case 'v': version(optCon
, stderr
, program_name
);
178 case 'l': license(optCon
, stderr
, program_name
);
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
);
190 case 'j': icon_offset_flag
= 1;
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
);
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
;
208 fprintf(stderr
, "%s: %s not valid for show window\n",
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
);
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
);
233 // case 'P' all handled by popt itself
237 if (icon_offset_flag
& !opts
.icon_flag
) {
239 "%s: --iconoffset|-j only valid in conjuction with --icon|-i\n",
241 usage(optCon
, stderr
, program_name
);
246 if (opts
.smprograms_flag
&& opts
.desktop_flag
) {
248 "%s: --smprograms|-P not valid in conjuction with --desktop|-D\n",
250 usage(optCon
, stderr
, program_name
);
256 fprintf(stderr
, "%s: bad argument %s: %s\n",
257 program_name
, poptBadOption(optCon
, POPT_BADOPTION_NOALIAS
),
263 rest
= poptGetArgs(optCon
);
266 if ((opts
.target_arg
= strdup(*rest
)) == NULL
) {
267 fprintf(stderr
, "%s: memory allocation error\n", program_name
);
273 fprintf(stderr
, "%s: Too many arguments: ", program_name
);
275 fprintf(stderr
, "%s ", *rest
++);
276 fprintf(stderr
, "\n");
277 usage(optCon
, stderr
, program_name
);
280 // THE MEAT GOES HERE
281 ec
= mkshortcut(opts
, optCon
);
284 fprintf(stderr
, "%s: TARGET not specified\n", program_name
);
285 usage(optCon
, stderr
, program_name
);
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
); }
300 int mkshortcut(optvals opts
, poptContext optCon
)
302 char link_name
[MAX_PATH
];
303 char exe_name
[MAX_PATH
];
304 char dir_name
[MAX_PATH
];
305 char *buf_str
, *tmp_str
;
308 /* For OLE interface */
311 IShellLink
*shell_link
;
312 IPersistFile
*persist_file
;
313 WCHAR widepath
[MAX_PATH
];
315 buf_str
= (char *) malloc (PATH_MAX
);
316 if (buf_str
== NULL
) {
317 fprintf (stderr
, "%s: out of memory\n", program_name
);
321 /* If there's a colon in the TARGET, it should be a URL */
322 if (strchr (opts
.target_arg
, ':') != NULL
)
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",
328 usage (optCon
, stderr
, program_name
);
331 strcpy (exe_name
, opts
.target_arg
);
332 dir_name
[0] = '\0'; /* No working dir for URL */
334 /* Convert TARGET to win32 path */
337 strcpy (buf_str
, opts
.target_arg
);
338 cygwin_conv_to_full_win32_path (buf_str
, exe_name
);
340 /* Get a working dir from 'w' option */
341 if (opts
.dir_name_arg
!= NULL
)
343 if (strchr (opts
.dir_name_arg
, ':') != NULL
)
345 fprintf(stderr
, "%s: all paths must be in POSIX format\n",
347 usage (optCon
, stderr
, program_name
);
350 cygwin_conv_to_win32_path (opts
.dir_name_arg
, dir_name
);
352 /* Get a working dir from the exepath */
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';
362 /* Generate a name for the link if not given */
363 if (opts
.name_arg
== NULL
)
365 /* Strip trailing /'s if any */
366 strcpy (buf_str
, opts
.target_arg
);
368 tmp
= strlen (buf_str
) - 1;
369 while (strrchr (buf_str
, '/') == (buf_str
+ tmp
))
378 tmp_str
= buf_str
+ 1;
381 strcpy (link_name
, tmp_str
);
383 /* User specified a name, so check it and convert */
386 if (opts
.desktop_flag
|| opts
.smprograms_flag
)
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",
392 usage (optCon
, stderr
, program_name
);
396 /* Sigh. Another W32 path */
397 if (strchr (opts
.name_arg
, ':') != NULL
) {
398 fprintf(stderr
, "%s: all paths must be in POSIX format\n",
400 usage (optCon
, stderr
, program_name
);
403 cygwin_conv_to_win32_path (opts
.name_arg
, link_name
);
406 /* Add suffix to link name if necessary */
407 if (strlen (link_name
) > 4)
409 tmp
= strlen (link_name
) - 4;
410 if (strncmp (link_name
+ tmp
, ".lnk", 4) != 0)
411 strcat (link_name
, ".lnk");
414 strcat (link_name
, ".lnk");
416 /* Prepend relative path if necessary */
417 if (opts
.desktop_flag
)
419 strcpy (buf_str
, link_name
);
420 if (!opts
.allusers_flag
)
421 SHGetSpecialFolderLocation (NULL
, CSIDL_DESKTOPDIRECTORY
, &id
);
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)
428 SHGetSpecialFolderLocation (NULL
, CSIDL_DESKTOPDIRECTORY
, &id
);
429 SHGetPathFromIDList (id
, link_name
);
431 strcat (link_name
, "\\");
432 strcat (link_name
, buf_str
);
435 if (opts
.smprograms_flag
)
437 strcpy (buf_str
, link_name
);
438 if (!opts
.allusers_flag
)
439 SHGetSpecialFolderLocation (NULL
, CSIDL_PROGRAMS
, &id
);
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)
446 SHGetSpecialFolderLocation (NULL
, CSIDL_PROGRAMS
, &id
);
447 SHGetPathFromIDList (id
, link_name
);
449 strcat (link_name
, "\\");
450 strcat (link_name
, buf_str
);
453 /* Beginning of Windows interface */
454 hres
= OleInitialize (NULL
);
455 if (hres
!= S_FALSE
&& hres
!= S_OK
)
457 fprintf (stderr
, "%s: Could not initialize OLE interface\n",
463 CoCreateInstance (&CLSID_ShellLink
, NULL
, CLSCTX_INPROC_SERVER
,
464 &IID_IShellLink
, (void **) &shell_link
);
465 if (SUCCEEDED (hres
))
468 shell_link
->lpVtbl
->QueryInterface (shell_link
, &IID_IPersistFile
,
469 (void **) &persist_file
);
470 if (SUCCEEDED (hres
))
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
);
480 shell_link
->lpVtbl
->SetIconLocation (shell_link
, opts
.icon_name_arg
,
482 if (opts
.show_flag
!= SW_SHOWNORMAL
)
483 shell_link
->lpVtbl
->SetShowCmd(shell_link
, opts
.show_flag
);
486 /* Make link name Unicode-compliant */
488 MultiByteToWideChar (CP_ACP
, 0, link_name
, -1, widepath
,
490 if (!SUCCEEDED (hres
))
492 fprintf (stderr
, "%s: Unicode translation failed%d\n",
496 hres
= persist_file
->lpVtbl
->Save (persist_file
, widepath
, TRUE
);
497 if (!SUCCEEDED (hres
))
500 "%s: Saving \"%s\" failed; does the target directory exist?\n",
501 program_name
, link_name
);
504 persist_file
->lpVtbl
->Release (persist_file
);
505 shell_link
->lpVtbl
->Release (shell_link
);
509 fprintf (stderr
, "%s: QueryInterface failed\n", program_name
);
515 fprintf (stderr
, "%s: CoCreateInstance failed\n", program_name
);
520 static char * getVersion(char * s
, int slen
)
522 const char *v
= strchr (versionStr
, ':');
530 len
= strchr (v
, ' ') - v
;
532 snprintf (s
,slen
,"%.*s", len
, v
);
536 static void printTopDescription(FILE * f
, char * name
)
539 fprintf(f
, "%s (cygutils) version %s\n", name
, getVersion(s
, 20));
540 fprintf(f
, " create a Windows shortcut\n\n");
542 static void printBottomDescription(FILE * f
, char * name
)
544 fprintf(f
, "\nNOTE: All filename arguments must be in unix (POSIX) format\n");
546 static printLicense(FILE * f
, char * name
)
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");
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");
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");
562 fprintf(f
, "See the COPYING file for license information.\n");
564 static void usage(poptContext optCon
, FILE * f
, char * name
)
566 poptPrintUsage(optCon
, f
, 0);
569 static void help(poptContext optCon
, FILE * f
, char * name
)
571 printTopDescription(f
, name
);
572 poptPrintHelp(optCon
, f
, 0);
573 printBottomDescription(f
, name
);
576 static void version(poptContext optCon
, FILE * f
, char * name
)
578 printTopDescription(f
, name
);
579 fprintf(f
, copyrightID
);
582 static void license(poptContext optCon
, FILE * f
, char * name
)
584 printTopDescription(f
, name
);
585 printLicense(f
, name
);