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
{
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
);
78 static char *program_name
;
81 main (int argc
, const char **argv
)
91 char icon_name
[MAX_PATH
];
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
}
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
}
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
}
134 tmp_str
= strrchr (argv
[0], '/');
135 if (tmp_str
== NULL
) {
136 tmp_str
= strrchr (argv
[0], '\\');
138 if (tmp_str
== NULL
) {
143 if ((program_name
= strdup(tmp_str
)) == NULL
) {
144 fprintf(stderr
, "%s: memory allocation error\n", argv
[0]);
148 icon_offset_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
;
164 optCon
= poptGetContext(NULL
, argc
, argv
, opt
, 0);
165 poptSetOtherOptionHelp(optCon
, "[OPTION]* TARGET");
166 while ((rc
= poptGetNextOpt(optCon
)) > 0) {
168 case '?': help(optCon
, stderr
, program_name
);
170 case 'u': usage(optCon
, stderr
, program_name
);
172 case 'v': version(optCon
, stderr
, program_name
);
174 case 'l': license(optCon
, stderr
, program_name
);
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
);
186 case 'j': icon_offset_flag
= 1;
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
);
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
);
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
);
214 // case 'P' all handled by popt itself
218 if (icon_offset_flag
& !opts
.icon_flag
) {
220 "%s: --iconoffset|-j only valid in conjuction with --icon|-i\n",
222 usage(optCon
, stderr
, program_name
);
227 if (opts
.smprograms_flag
&& opts
.desktop_flag
) {
229 "%s: --smprograms|-P not valid in conjuction with --desktop|-D\n",
231 usage(optCon
, stderr
, program_name
);
237 fprintf(stderr
, "%s: bad argument %s: %s\n",
238 program_name
, poptBadOption(optCon
, POPT_BADOPTION_NOALIAS
),
244 rest
= poptGetArgs(optCon
);
247 if ((opts
.target_arg
= strdup(*rest
)) == NULL
) {
248 fprintf(stderr
, "%s: memory allocation error\n", program_name
);
254 fprintf(stderr
, "%s: Too many arguments: ", program_name
);
256 fprintf(stderr
, "%s ", *rest
++);
257 fprintf(stderr
, "\n");
258 usage(optCon
, stderr
, program_name
);
261 // THE MEAT GOES HERE
262 ec
= mkshortcut(opts
, optCon
);
265 fprintf(stderr
, "%s: TARGET not specified\n", program_name
);
266 usage(optCon
, stderr
, program_name
);
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
); }
281 int mkshortcut(optvals opts
, poptContext optCon
)
283 char link_name
[MAX_PATH
];
284 char exe_name
[MAX_PATH
];
285 char dir_name
[MAX_PATH
];
286 char *buf_str
, *tmp_str
;
289 /* For OLE interface */
292 IShellLink
*shell_link
;
293 IPersistFile
*persist_file
;
294 WCHAR widepath
[MAX_PATH
];
296 buf_str
= (char *) malloc (PATH_MAX
);
297 if (buf_str
== NULL
) {
298 fprintf (stderr
, "%s: out of memory\n", program_name
);
302 /* If there's a colon in the TARGET, it should be a URL */
303 if (strchr (opts
.target_arg
, ':') != NULL
)
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",
309 usage (optCon
, stderr
, program_name
);
312 strcpy (exe_name
, opts
.target_arg
);
313 dir_name
[0] = '\0'; /* No working dir for URL */
315 /* Convert TARGET to win32 path */
318 strcpy (buf_str
, opts
.target_arg
);
319 cygwin_conv_to_full_win32_path (buf_str
, exe_name
);
321 /* Get a working dir from 'w' option */
322 if (opts
.dir_name_arg
!= NULL
)
324 if (strchr (opts
.dir_name_arg
, ':') != NULL
)
326 fprintf(stderr
, "%s: all paths must be in POSIX format\n",
328 usage (optCon
, stderr
, program_name
);
331 cygwin_conv_to_win32_path (opts
.dir_name_arg
, dir_name
);
333 /* Get a working dir from the exepath */
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';
343 /* Generate a name for the link if not given */
344 if (opts
.name_arg
== NULL
)
346 /* Strip trailing /'s if any */
347 strcpy (buf_str
, opts
.target_arg
);
349 tmp
= strlen (buf_str
) - 1;
350 while (strrchr (buf_str
, '/') == (buf_str
+ tmp
))
359 tmp_str
= buf_str
+ 1;
362 strcpy (link_name
, tmp_str
);
364 /* User specified a name, so check it and convert */
367 if (opts
.desktop_flag
|| opts
.smprograms_flag
)
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",
373 usage (optCon
, stderr
, program_name
);
377 /* Sigh. Another W32 path */
378 if (strchr (opts
.name_arg
, ':') != NULL
) {
379 fprintf(stderr
, "%s: all paths must be in POSIX format\n",
381 usage (optCon
, stderr
, program_name
);
384 cygwin_conv_to_win32_path (opts
.name_arg
, link_name
);
387 /* Add suffix to link name if necessary */
388 if (strlen (link_name
) > 4)
390 tmp
= strlen (link_name
) - 4;
391 if (strncmp (link_name
+ tmp
, ".lnk", 4) != 0)
392 strcat (link_name
, ".lnk");
395 strcat (link_name
, ".lnk");
397 /* Prepend relative path if necessary */
398 if (opts
.desktop_flag
)
400 strcpy (buf_str
, link_name
);
401 if (!opts
.allusers_flag
)
402 SHGetSpecialFolderLocation (NULL
, CSIDL_DESKTOPDIRECTORY
, &id
);
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)
409 SHGetSpecialFolderLocation (NULL
, CSIDL_DESKTOPDIRECTORY
, &id
);
410 SHGetPathFromIDList (id
, link_name
);
412 strcat (link_name
, "\\");
413 strcat (link_name
, buf_str
);
416 if (opts
.smprograms_flag
)
418 strcpy (buf_str
, link_name
);
419 if (!opts
.allusers_flag
)
420 SHGetSpecialFolderLocation (NULL
, CSIDL_PROGRAMS
, &id
);
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)
427 SHGetSpecialFolderLocation (NULL
, CSIDL_PROGRAMS
, &id
);
428 SHGetPathFromIDList (id
, link_name
);
430 strcat (link_name
, "\\");
431 strcat (link_name
, buf_str
);
434 /* Beginning of Windows interface */
435 hres
= OleInitialize (NULL
);
436 if (hres
!= S_FALSE
&& hres
!= S_OK
)
438 fprintf (stderr
, "%s: Could not initialize OLE interface\n",
444 CoCreateInstance (&CLSID_ShellLink
, NULL
, CLSCTX_INPROC_SERVER
,
445 &IID_IShellLink
, (void **) &shell_link
);
446 if (SUCCEEDED (hres
))
449 shell_link
->lpVtbl
->QueryInterface (shell_link
, &IID_IPersistFile
,
450 (void **) &persist_file
);
451 if (SUCCEEDED (hres
))
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
);
461 shell_link
->lpVtbl
->SetIconLocation (shell_link
, opts
.icon_name_arg
,
464 /* Make link name Unicode-compliant */
466 MultiByteToWideChar (CP_ACP
, 0, link_name
, -1, widepath
,
468 if (!SUCCEEDED (hres
))
470 fprintf (stderr
, "%s: Unicode translation failed%d\n",
474 hres
= persist_file
->lpVtbl
->Save (persist_file
, widepath
, TRUE
);
475 if (!SUCCEEDED (hres
))
478 "%s: Saving \"%s\" failed; does the target directory exist?\n",
479 program_name
, link_name
);
482 persist_file
->lpVtbl
->Release (persist_file
);
483 shell_link
->lpVtbl
->Release (shell_link
);
487 fprintf (stderr
, "%s: QueryInterface failed\n", program_name
);
493 fprintf (stderr
, "%s: CoCreateInstance failed\n", program_name
);
498 static char * getVersion(char * s
, int slen
)
500 const char *v
= strchr (versionStr
, ':');
508 len
= strchr (v
, ' ') - v
;
510 snprintf (s
,slen
,"%.*s", len
, v
);
514 static void printTopDescription(FILE * f
, char * name
)
517 fprintf(f
, "%s (cygutils) version %s\n", name
, getVersion(s
, 20));
518 fprintf(f
, " create a Windows shortcut\n\n");
520 static void printBottomDescription(FILE * f
, char * name
)
522 fprintf(f
, "\nNOTE: All filename arguments must be in unix (POSIX) format\n");
524 static printLicense(FILE * f
, char * name
)
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");
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");
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");
540 fprintf(f
, "See the COPYING file for license information.\n");
542 static void usage(poptContext optCon
, FILE * f
, char * name
)
544 poptPrintUsage(optCon
, f
, 0);
547 static void help(poptContext optCon
, FILE * f
, char * name
)
549 printTopDescription(f
, name
);
550 poptPrintHelp(optCon
, f
, 0);
551 printBottomDescription(f
, name
);
554 static void version(poptContext optCon
, FILE * f
, char * name
)
556 printTopDescription(f
, name
);
557 fprintf(f
, copyrightID
);
560 static void license(poptContext optCon
, FILE * f
, char * name
)
562 printTopDescription(f
, name
);
563 printLicense(f
, name
);