-/* mkshortcut.c -- create a Windows shortcut
+/* mkshortcut.c -- create a Windows shortcut
*
* Copyright (c) 2002 Joshua Daniel Franklin
*
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* 1: user error (syntax error)
* 2: system error (out of memory, etc.)
* 3: windows error (interface failed)
- *
- * Compile with: gcc -o prog.exe mkshortcut.c -lole32 -luuid
+ *
+ * Compile with: gcc -o prog.exe mkshortcut.c -lpopt -lole32 -luuid
* (You'd need to uncomment the moved to common.h lines.)
*
*/
+
#if HAVE_CONFIG_H
# include "config.h"
-#endif
+#endif
#include "common.h"
#define NOCOMATTRIBUTE
/* moved to common.h */
/*
#include <stdio.h>
-#include <getopt.h>
+#include <popt.h>
*/
-static const char version[] = "$Revision$";
-static const char versionID[] = "1.01.0";
+static const char versionStr[] = "$Revision$";
+static const char versionID[] = "1.02.0";
/* for CVS */
static const char revID[] =
"$Id$";
static const char copyrightID[] =
"Copyright (c) 2002\nJoshua Daniel Franklin. All rights reserved.\nLicensed under GPL v2.0\n";
-static char *prog_name;
-static char *argument_arg, *name_arg;
-static int icon_flag, unix_flag, windows_flag;
-static int allusers_flag, desktop_flag, smprograms_flag;
-
-static struct option long_options[] = {
- {"arguments", required_argument, NULL, 'a'},
- {"help", no_argument, NULL, 'h'},
- {"icon", required_argument, NULL, 'i'},
- {"iconoffset", required_argument, NULL, 'j'},
- {"name", required_argument, NULL, 'n'},
- {"version", no_argument, NULL, 'v'},
- {"allusers", no_argument, NULL, 'A'},
- {"desktop", no_argument, NULL, 'D'},
- {"smprograms", no_argument, NULL, 'P'},
- {NULL, 0, NULL, 0}
-};
-
-static void
-usage (FILE * stream, int status)
-{
- fprintf (stream, "\
-Usage %s [OPTION]... TARGET \n\
-NOTE: All filename arguments must be in unix (POSIX) format\n\
- -a|--arguments=ARGS use arguments ARGS \n\
- -h|--help output usage information and exit\n\
- -i|--icon icon file for link to use\n\
- -j|--iconoffset offset of icon in icon file (default is 0)\n\
- -n|--name name for link (defaults to TARGET)\n\
- -v|--version output version information and exit\n\
- -A|--allusers use 'All Users' instead of current user for -D,-P\n\
- -D|--desktop create link relative to 'Desktop' directory\n\
- -P|--smprograms create link relative to Start Menu 'Programs' directory\
-", prog_name);
- exit (status);
-}
+typedef struct optvals_s {
+ int icon_flag;
+ int unix_flag;
+ int windows_flag;
+ int allusers_flag;
+ int desktop_flag;
+ int smprograms_flag;
+ int offset;
+ char * name_arg;
+ char * argument_arg;
+ char * target_arg;
+ char * icon_name_arg;
+} optvals;
-print_version ()
+static int mkshortcut(optvals opts, poptContext optCon);
+static void printTopDescription(FILE * f, char * name);
+static void printBottomDescription(FILE * f, char * name);
+static char * getVersion(char * s, int slen);
+static void usage(poptContext optCon, FILE * f, char * name);
+static void help(poptContext optCon, FILE * f, char * name);
+static void version(poptContext optCon, FILE * f, char * name);
+static void license(poptContext optCon, FILE * f, char * name);
+
+static char *program_name;
+
+int
+main (int argc, const char **argv)
{
- const char *v = strchr (version, ':');
- int len;
- if (!v)
- {
- v = "?";
- len = 1;
- }
- else
- {
- v += 2;
- len = strchr (v, ' ') - v;
+ poptContext optCon;
+ const char ** rest;
+ int rc;
+ int ec = 0;
+ optvals opts;
+
+ const char *tmp_str;
+ int icon_offset_flag;
+ char icon_name[MAX_PATH];
+ const char * arg;
+
+ struct poptOption helpOptionsTable[] = {
+ { "help", 'h', POPT_ARG_NONE, NULL, '?', \
+ "Show this help message", NULL},
+ { "usage", '\0', POPT_ARG_NONE, NULL, 'u', \
+ "Display brief usage message", NULL},
+ { "version", 'v', POPT_ARG_NONE, NULL, 'v', \
+ "Display version information", NULL},
+ { "license", '\0', POPT_ARG_NONE, NULL, 'l', \
+ "Display licensing information", NULL},
+ { NULL, '\0', 0, NULL, 0, NULL, NULL }
+ };
+
+ struct poptOption generalOptionsTable[] = {
+ { "arguments", 'a', POPT_ARG_STRING, NULL, 'a', \
+ "Use arguments ARGS", "ARGS"},
+ { "icon", 'i', POPT_ARG_STRING, NULL, 'i', \
+ "icon file for link to use", "iconfile"},
+ { "iconoffset", 'j', POPT_ARG_INT, &(opts.offset), 'j', \
+ "offset of icon in icon file (default is 0)", NULL},
+ { "name", 'n', POPT_ARG_STRING, NULL, 'n', \
+ "name for link (defaults to TARGET)", "NAME"},
+ { "allusers", 'A', POPT_ARG_VAL, &(opts.allusers_flag), 1, \
+ "use 'All Users' instead of current user for -D,-P", NULL},
+ { "desktop", 'D', POPT_ARG_VAL, &(opts.desktop_flag), 1, \
+ "create link relative to 'Desktop' directory", NULL},
+ { "smprograms", 'P', POPT_ARG_VAL, &(opts.smprograms_flag), 1, \
+ "create link relative to Start Menu 'Programs' directory", NULL},
+ { NULL, '\0', 0, NULL, 0, NULL, NULL }
+ };
+
+ struct poptOption opt[] = {
+ { NULL, '\0', POPT_ARG_INCLUDE_TABLE, generalOptionsTable, 0, \
+ "General options", NULL },
+ { NULL, '\0', POPT_ARG_INCLUDE_TABLE, helpOptionsTable, 0, \
+ "Help options", NULL },
+ { NULL, '\0', 0, NULL, 0, NULL, NULL }
+ };
+
+ tmp_str = strrchr (argv[0], '/');
+ if (tmp_str == NULL) {
+ tmp_str = strrchr (argv[0], '\\');
+ }
+ if (tmp_str == NULL) {
+ tmp_str = argv[0];
+ } else {
+ tmp_str++;
+ }
+ if ((program_name = strdup(tmp_str)) == NULL ) {
+ fprintf(stderr, "%s: memory allocation error\n", argv[0]);
+ exit(2);
+ }
+
+ icon_offset_flag = 0;
+
+ opts.offset = 0;
+ opts.icon_flag = 0;
+ opts.unix_flag = 0;
+ opts.windows_flag = 0;
+ opts.allusers_flag = 0;
+ opts.desktop_flag = 0;
+ opts.smprograms_flag = 0;
+ opts.target_arg = NULL;
+ opts.argument_arg = NULL;
+ opts.name_arg = NULL;
+ opts.icon_name_arg = NULL;
+
+ /* Parse options */
+ optCon = poptGetContext(NULL, argc, argv, opt, 0);
+ poptSetOtherOptionHelp(optCon, "[OPTION]* TARGET");
+ while ((rc = poptGetNextOpt(optCon)) > 0) {
+ switch (rc) {
+ case '?': help(optCon, stderr, program_name);
+ goto exit;
+ case 'u': usage(optCon, stderr, program_name);
+ goto exit;
+ case 'v': version(optCon, stderr, program_name);
+ goto exit;
+ case 'l': license(optCon, stderr, program_name);
+ goto exit;
+ case 'i': opts.icon_flag = 1;
+ if (arg = poptGetOptArg(optCon)) {
+ cygwin_conv_to_full_win32_path (arg, icon_name);
+ if ((opts.icon_name_arg = strdup(icon_name)) == NULL ) {
+ fprintf(stderr, "%s: memory allocation error\n", program_name);
+ ec=2;
+ goto exit;
+ }
+ }
+ break;
+ case 'j': icon_offset_flag = 1;
+ break;
+ case 'n': if (arg = poptGetOptArg(optCon)) {
+ if ((opts.name_arg = strdup(arg)) == NULL ) {
+ fprintf(stderr, "%s: memory allocation error\n", program_name);
+ ec=2;
+ goto exit;
+ }
+ }
+ break;
+ case 'a': if (arg = poptGetOptArg(optCon)) {
+ if ((opts.argument_arg = strdup(arg)) == NULL ) {
+ fprintf(stderr, "%s: memory allocation error\n", program_name);
+ ec=2;
+ goto exit;
+ }
+ }
+ break;
+ // case 'A'
+ // case 'D'
+ // case 'P' all handled by popt itself
+ }
+ }
+
+ if (icon_offset_flag & !opts.icon_flag) {
+ fprintf(stderr,
+ "%s: --iconoffset|-j only valid in conjuction with --icon|-i\n",
+ program_name);
+ usage(optCon, stderr, program_name);
+ ec=1;
+ goto exit;
+ }
+
+ if (opts.smprograms_flag && opts.desktop_flag) {
+ fprintf(stderr,
+ "%s: --smprograms|-P not valid in conjuction with --desktop|-D\n",
+ program_name);
+ usage(optCon, stderr, program_name);
+ ec=1;
+ goto exit;
+ }
+
+ if (rc < -1 ) {
+ fprintf(stderr, "%s: bad argument %s: %s\n",
+ program_name, poptBadOption(optCon, POPT_BADOPTION_NOALIAS),
+ poptStrerror(rc));
+ ec = 1;
+ goto exit;
+ }
+
+ rest = poptGetArgs(optCon);
+
+ if (rest && *rest) {
+ if ((opts.target_arg = strdup(*rest)) == NULL) {
+ fprintf(stderr, "%s: memory allocation error\n", program_name);
+ ec=2;
+ goto exit;
}
- printf ("\
-mkshortcut (cygutils) %.*s\n%s\
-", len, v, copyrightID);
+ rest++;
+ if (rest && *rest) {
+ fprintf(stderr, "%s: Too many arguments: ", program_name);
+ while (*rest)
+ fprintf(stderr, "%s ", *rest++);
+ fprintf(stderr, "\n");
+ usage(optCon, stderr, program_name);
+ ec=1;
+ } else {
+ // THE MEAT GOES HERE
+ ec = mkshortcut(opts, optCon);
+ }
+ } else {
+ fprintf(stderr, "%s: TARGET not specified\n", program_name);
+ usage(optCon, stderr, program_name);
+ ec=1;
+ }
+
+exit:
+ poptFreeContext(optCon);
+ if (opts.target_arg) { free(opts.target_arg); }
+ if (opts.name_arg) { free(opts.name_arg); }
+ if (opts.argument_arg) { free(opts.argument_arg); }
+ if (opts.icon_name_arg) { free(opts.icon_name_arg); }
+ free(program_name);
+ return(ec);
}
-int
-main (int argc, char **argv)
+int mkshortcut(optvals opts, poptContext optCon)
{
- int opt, tmp, offset;
- char *args, *tmp_str, *buf_str;
char link_name[MAX_PATH];
char exe_name[MAX_PATH];
char dir_name[MAX_PATH];
- char icon_name[MAX_PATH];
+ char *buf_str, *tmp_str;
+ int tmp;
/* For OLE interface */
LPITEMIDLIST id;
WCHAR widepath[MAX_PATH];
buf_str = (char *) malloc (PATH_MAX);
- if (buf_str == NULL)
- {
- fprintf (stderr, "%s: out of memory\n", prog_name);
- exit (2);
- }
-
- offset = 0;
- icon_flag = 0;
- unix_flag = 0;
- windows_flag = 0;
- allusers_flag = 0;
- desktop_flag = 0;
- smprograms_flag = 0;
-
- prog_name = strrchr (argv[0], '/');
- if (prog_name == NULL)
- prog_name = strrchr (argv[0], '\\');
- if (prog_name == NULL)
- prog_name = argv[0];
- else
- prog_name++;
-
- while ((opt =
- getopt_long (argc, argv, (char *) "a:i:j:n:hvADP", long_options,
- NULL)) != EOF)
- {
- switch (opt)
- {
- case 'a':
- argument_arg = optarg;
- break;
- case 'h':
- usage (stdout, 0);
- break;
- case 'i':
- icon_flag = 1;
- cygwin_conv_to_full_win32_path (optarg, icon_name);
- break;
- case 'j':
- if (!icon_flag)
- usage (stderr, 1);
- offset = atoi (optarg);
- break;
- case 'n':
- name_arg = optarg;
- break;
- case 'v':
- print_version ();
- exit (0);
- case 'A':
- allusers_flag = 1;
- break;
- case 'D':
- if (smprograms_flag)
- usage (stderr, 1);
- desktop_flag = 1;
- break;
- case 'P':
- if (desktop_flag)
- usage (stderr, 1);
- smprograms_flag = 1;
- break;
- default:
- usage (stderr, 1);
- break;
- }
- }
-
- if (optind != argc - 1)
- usage (stderr, 1);
+ if (buf_str == NULL) {
+ fprintf (stderr, "%s: out of memory\n", program_name);
+ return(2);
+ }
/* If there's a colon in the TARGET, it should be a URL */
- if (strchr (argv[optind], ':') != NULL)
+ if (strchr (opts.target_arg, ':') != NULL)
{
/* Nope, somebody's trying a W32 path */
- if (argv[optind][1] == ':')
- usage (stderr, 1);
- strcpy (exe_name, argv[optind]);
+ if (opts.target_arg[1] == ':') {
+ fprintf(stderr, "%s: all paths must be in POSIX format\n",
+ program_name);
+ usage (optCon, stderr, program_name);
+ return(1);
+ }
+ strcpy (exe_name, opts.target_arg);
dir_name[0] = '\0'; /* No working dir for URL */
}
/* Convert TARGET to win32 path */
else
{
- strcpy (buf_str, argv[optind]);
+ strcpy (buf_str, opts.target_arg);
cygwin_conv_to_full_win32_path (buf_str, exe_name);
/* Get a working dir from the exepath */
}
/* Generate a name for the link if not given */
- if (name_arg == NULL)
+ if (opts.name_arg == NULL)
{
/* Strip trailing /'s if any */
- strcpy (buf_str, argv[optind]);
+ strcpy (buf_str, opts.target_arg);
tmp_str = buf_str;
tmp = strlen (buf_str) - 1;
while (strrchr (buf_str, '/') == (buf_str + tmp))
buf_str++;
}
strcpy (link_name, tmp_str);
- }
+ }
/* User specified a name, so check it and convert */
else
{
- if (desktop_flag || smprograms_flag)
+ if (opts.desktop_flag || opts.smprograms_flag)
{
/* Cannot have absolute path relative to Desktop/SM Programs */
- if (name_arg[0] == '/')
- usage (stderr, 1);
+ if (opts.name_arg[0] == '/') {
+ fprintf(stderr, "%s: absolute pathnames not allowed with -D/-P\n",
+ program_name);
+ usage (optCon, stderr, program_name);
+ return(1);
+ }
}
/* Sigh. Another W32 path */
- if (strchr (name_arg, ':') != NULL)
- usage (stderr, 1);
- cygwin_conv_to_win32_path (name_arg, link_name);
- }
+ if (strchr (opts.name_arg, ':') != NULL) {
+ fprintf(stderr, "%s: all paths must be in POSIX format\n",
+ program_name);
+ usage (optCon, stderr, program_name);
+ return(1);
+ }
+ cygwin_conv_to_win32_path (opts.name_arg, link_name);
+ }
/* Add suffix to link name if necessary */
if (strlen (link_name) > 4)
strcat (link_name, ".lnk");
/* Prepend relative path if necessary */
- if (desktop_flag)
+ if (opts.desktop_flag)
{
strcpy (buf_str, link_name);
- if (!allusers_flag)
+ if (!opts.allusers_flag)
SHGetSpecialFolderLocation (NULL, CSIDL_DESKTOPDIRECTORY, &id);
else
SHGetSpecialFolderLocation (NULL, CSIDL_COMMON_DESKTOPDIRECTORY, &id);
strcat (link_name, buf_str);
}
- if (smprograms_flag)
+ if (opts.smprograms_flag)
{
strcpy (buf_str, link_name);
- if (!allusers_flag)
+ if (!opts.allusers_flag)
SHGetSpecialFolderLocation (NULL, CSIDL_PROGRAMS, &id);
else
SHGetSpecialFolderLocation (NULL, CSIDL_COMMON_PROGRAMS, &id);
hres = OleInitialize (NULL);
if (hres != S_FALSE && hres != S_OK)
{
- fprintf (stderr, "%s: Could not initialize OLE interface\n", prog_name);
- exit (3);
+ fprintf (stderr, "%s: Could not initialize OLE interface\n",
+ program_name);
+ return (3);
}
hres =
cygwin_conv_to_full_posix_path (exe_name, buf_str);
shell_link->lpVtbl->SetDescription (shell_link, buf_str);
shell_link->lpVtbl->SetWorkingDirectory (shell_link, dir_name);
- if (argument_arg)
- shell_link->lpVtbl->SetArguments (shell_link, argument_arg);
- if (icon_flag)
- shell_link->lpVtbl->SetIconLocation (shell_link, icon_name,
- offset);
+ if (opts.argument_arg)
+ shell_link->lpVtbl->SetArguments (shell_link, opts.argument_arg);
+ if (opts.icon_flag)
+ shell_link->lpVtbl->SetIconLocation (shell_link, opts.icon_name_arg,
+ opts.offset);
/* Make link name Unicode-compliant */
hres =
if (!SUCCEEDED (hres))
{
fprintf (stderr, "%s: Unicode translation failed%d\n",
- prog_name, hres);
- exit (3);
+ program_name, hres);
+ return (3);
}
hres = persist_file->lpVtbl->Save (persist_file, widepath, TRUE);
if (!SUCCEEDED (hres))
{
fprintf (stderr,
- "%s: Saving \"%s\" failed; does the target directory exist?\n",
- prog_name, link_name);
- exit (3);
+ "%s: Saving \"%s\" failed; does the target directory exist?\n",
+ program_name, link_name);
+ return (3);
}
persist_file->lpVtbl->Release (persist_file);
shell_link->lpVtbl->Release (shell_link);
}
else
{
- fprintf (stderr, "%s: QueryInterface failed\n", prog_name);
- exit (3);
+ fprintf (stderr, "%s: QueryInterface failed\n", program_name);
+ return (3);
}
}
else
{
- fprintf (stderr, "%s: CoCreateInstance failed\n", prog_name);
- exit (3);
+ fprintf (stderr, "%s: CoCreateInstance failed\n", program_name);
+ return (3);
}
+}
+
+static char * getVersion(char * s, int slen)
+{
+ const char *v = strchr (versionStr, ':');
+
+ int len;
+ if (!v) {
+ v = "?";
+ len = 1;
+ } else {
+ v += 2;
+ len = strchr (v, ' ') - v;
+ }
+ snprintf (s,slen,"%.*s", len, v);
+ return s;
+}
+
+static void printTopDescription(FILE * f, char * name)
+{
+ char s[20];
+ fprintf(f, "%s (cygutils) version %s\n", name, getVersion(s, 20));
+ fprintf(f, " create a Windows shortcut\n\n");
+}
+static void printBottomDescription(FILE * f, char * name)
+{
+ fprintf(f, "NOTE: All filename arguments must be in unix (POSIX) format\n");
+}
+static printLicense(FILE * f, char * name)
+{
+ fprintf(f, "This program is free software; you can redistribute it and/or\n");
+ fprintf(f, "modify it under the terms of the GNU General Public License\n");
+ fprintf(f, "as published by the Free Software Foundation; either version 2\n");
+ fprintf(f, "of the License, or (at your option) any later version.\n");
+ fprintf(f, "\n");
+ fprintf(f, "This program is distributed in the hope that it will be useful,\n");
+ fprintf(f, "but WITHOUT ANY WARRANTY; without even the implied warranty of\n");
+ fprintf(f, "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n");
+ fprintf(f, "GNU General Public License for more details.\n");
+ fprintf(f, "\n");
+ fprintf(f, "You should have received a copy of the GNU General Public License\n");
+ fprintf(f, "along with this program; if not, write to the Free Software\n");
+ fprintf(f, "Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.\n");
+ fprintf(f, "\n");
+ fprintf(f, "See the COPYING file for license information.\n");
+}
+static void usage(poptContext optCon, FILE * f, char * name)
+{
+ poptPrintUsage(optCon, f, 0);
+}
- exit (0);
+static void help(poptContext optCon, FILE * f, char * name)
+{
+ printTopDescription(f, name);
+ poptPrintHelp(optCon, f, 0);
+}
+
+static void version(poptContext optCon, FILE * f, char * name)
+{
+ printTopDescription(f, name);
+ fprintf(f, copyrightID);
}
+
+static void license(poptContext optCon, FILE * f, char * name)
+{
+ printTopDescription(f, name);
+ printLicense(f, name);
+ printBottomDescription(f, name);
+}
+