Index: src/mkshortcut/mkshortcut.c =================================================================== RCS file: /cvs/cygwin-apps/cygutils/src/mkshortcut/mkshortcut.c,v retrieving revision 1.10 diff -u -r1.10 mkshortcut.c --- src/mkshortcut/mkshortcut.c 3 Dec 2009 08:19:44 -0000 1.10 +++ src/mkshortcut/mkshortcut.c 3 Jan 2010 21:02:15 -0000 @@ -1,6 +1,7 @@ /* mkshortcut.c -- create a Windows shortcut * * Copyright (c) 2002 Joshua Daniel Franklin + * Copyright (c) 2010 Andy Koppe * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -28,6 +29,8 @@ * */ +#define UNICODE + #if HAVE_CONFIG_H # include "config.h" #endif @@ -35,6 +38,9 @@ #define NOCOMATTRIBUTE +#include +#include +#include #include #include /* moved to common.h */ @@ -47,7 +53,10 @@ static const char revID[] = "$Id: mkshortcut.c,v 1.10 2009/12/03 08:19:44 cwilson Exp $"; static const char copyrightID[] = - "Copyright (c) 2002\nJoshua Daniel Franklin. All rights reserved.\nLicensed under GPL v2.0\n"; + "Copyright (c) 2002 Joshua Daniel Franklin.\n" + "Copyright (c) 2010 Andy Koppe.\n" + "All rights reserved.\n" + "Licensed under GPL v2.0\n"; typedef struct optvals_s { @@ -67,29 +76,29 @@ char *icon_name_arg; } optvals; -static int mkshortcut (optvals opts, poptContext optCon); -static void printTopDescription (FILE * f, char *name); -static void printBottomDescription (FILE * f, char *name); +static int mkshortcut (optvals opts); +static void printTopDescription (void); +static void printBottomDescription (void); static const char *getVersion (); -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 void usage (void); +static void help (void); +static void version (void); +static void license (void); +static void error (char *); +static void *nullcheck (void *); static char *program_name; +static poptContext optCon; int main (int argc, const char **argv) { - 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[] = { @@ -136,6 +145,8 @@ "Help options", NULL}, {NULL, '\0', 0, NULL, 0, NULL, NULL} }; + + setlocale(LC_CTYPE, ""); tmp_str = strrchr (argv[0], '/'); if (tmp_str == NULL) @@ -150,11 +161,7 @@ { tmp_str++; } - if ((program_name = strdup (tmp_str)) == NULL) - { - fprintf (stderr, "%s: memory allocation error\n", argv[0]); - exit (2); - } + nullcheck (program_name = strdup (tmp_str)); icon_offset_flag = 0; @@ -181,57 +188,32 @@ switch (rc) { case '?': - help (optCon, stderr, program_name); - goto exit; + help (); + return 0; case 'u': - usage (optCon, stderr, program_name); - goto exit; + usage (); + return 0; case 'v': - version (optCon, stderr, program_name); - goto exit; + version (); + return 0; case 'l': - license (optCon, stderr, program_name); - goto exit; + license (); + return 0; case 'd': if (arg = poptGetOptArg (optCon)) - { - if ((opts.desc_arg = strdup (arg)) == NULL) - { - fprintf (stderr, "%s: memory allocation error\n", - program_name); - ec = 2; - goto exit; - } - } + nullcheck (opts.desc_arg = strdup (arg)); break; 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; - } - } + nullcheck (opts.icon_name_arg = strdup (arg)); 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; - } - } + nullcheck (opts.name_arg = strdup (arg)); break; case 's': if (arg = poptGetOptArg (optCon)) @@ -252,34 +234,17 @@ { fprintf (stderr, "%s: %s not valid for show window\n", program_name, arg); - ec = 2; - goto exit; + return 2; } } break; case 'w': if (arg = poptGetOptArg (optCon)) - { - if ((opts.dir_name_arg = strdup (arg)) == NULL) - { - fprintf (stderr, "%s: memory allocation error\n", - program_name); - ec = 2; - goto exit; - } - } + nullcheck (opts.dir_name_arg = strdup (arg)); 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; - } - } + nullcheck (opts.argument_arg = strdup (arg)); break; // case 'A' // case 'D' @@ -288,44 +253,24 @@ } 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; - } + error ("--iconoffset|-j only valid in conjuction with --icon|-i"); 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; - } + error ("--smprograms|-P not valid in conjuction with --desktop|-D"); if (rc < -1) { fprintf (stderr, "%s: bad argument %s: %s\n", program_name, poptBadOption (optCon, POPT_BADOPTION_NOALIAS), poptStrerror (rc)); - ec = 1; - goto exit; + return 1; } 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; - } + nullcheck (opts.target_arg = strdup (*rest)); rest++; if (rest && *rest) { @@ -333,140 +278,118 @@ while (*rest) fprintf (stderr, "%s ", *rest++); fprintf (stderr, "\n"); - usage (optCon, stderr, program_name); - ec = 1; + usage (); + return 1; } else { // THE MEAT GOES HERE - ec = mkshortcut (opts, optCon); + return mkshortcut (opts); } } else - { - fprintf (stderr, "%s: TARGET not specified\n", program_name); - usage (optCon, stderr, program_name); - ec = 1; - } + error ("TARGET not specified"); +} -exit: - poptFreeContext (optCon); - if (opts.target_arg) - { - free (opts.target_arg); - } - if (opts.name_arg) - { - free (opts.name_arg); - } - if (opts.desc_arg) - { - free (opts.desc_arg); - } - if (opts.dir_name_arg) - { - free (opts.dir_name_arg); - } - if (opts.argument_arg) - { - free (opts.argument_arg); - } - if (opts.icon_name_arg) +static wchar_t * +conv_arg (char *arg, char *arg_name) +{ + wchar_t *ret; + int len = mbstowcs(0, arg, 0); + if (len < 0) + { + fprintf (stderr, "%s: invalid bytes in %s\n", + program_name, arg_name); + exit (1); + } + ret = nullcheck (malloc (++len * sizeof (wchar_t))); + mbstowcs(ret, arg, len); + return ret; +} + +static wchar_t * +conv_path (char *path, cygwin_conv_path_t flags) +{ + wchar_t *wpath; + + if (strchr (path, ':') != NULL) + error ("all paths must be in POSIX format"); + + wpath = cygwin_create_path (CCP_POSIX_TO_WIN_W | flags, path); + if (!wpath) { - free (opts.icon_name_arg); + fprintf (stderr, + "%s: could not convert path: $s\n", + program_name, path); + exit (1); } - free (program_name); - return (ec); + + if (wcsncmp(wpath, L"\\\\?\\UNC\\", 8) == 0) + wpath += 6, *wpath = '\\'; /* Replace '\\?\UNC\' with \\ */ + else if (wcsncmp(wpath, L"\\\\?\\", 4) == 0) + wpath += 4; /* Drop '\\?\' */ + + return wpath; } int -mkshortcut (optvals opts, poptContext optCon) +mkshortcut (optvals opts) { - char link_name[MAX_PATH]; - char exe_name[MAX_PATH]; - char dir_name[MAX_PATH]; - char desc[MAX_PATH]; - char *buf_str, *tmp_str; - int tmp; + wchar_t *link_name, *link_base; + wchar_t *exe_name; + wchar_t *dir_name; + wchar_t *icon_name; + wchar_t *desc; + wchar_t *args; + int len; /* For OLE interface */ - LPITEMIDLIST id; HRESULT hres; IShellLink *shell_link; IPersistFile *persist_file; - WCHAR widepath[MAX_PATH]; - - buf_str = (char *) malloc (PATH_MAX); - 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 (opts.target_arg, ':') != NULL) { /* Nope, somebody's trying a W32 path */ if (opts.target_arg[1] == ':') - { - free (buf_str); - 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 */ + error ("all paths must be in POSIX format"); + + exe_name = conv_arg (opts.target_arg, "target URL"); + dir_name = L""; /* No working dir for URL */ } /* Convert TARGET to win32 path */ else { - strcpy (buf_str, opts.target_arg); - cygwin_conv_to_full_win32_path (buf_str, exe_name); + exe_name = conv_path (opts.target_arg, 0); /* Get a working dir from 'w' option */ if (opts.dir_name_arg != NULL) - { - if (strchr (opts.dir_name_arg, ':') != NULL) - { - free (buf_str); - 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.dir_name_arg, dir_name); - } + dir_name = conv_path (opts.dir_name_arg, 0); /* Get a working dir from the exepath */ else { - tmp_str = strrchr (exe_name, '\\'); - tmp = strlen (exe_name) - strlen (tmp_str); - strncpy (dir_name, exe_name, tmp); - dir_name[tmp] = '\0'; + wchar_t *last_slash = wcsrchr (exe_name, '\\'); + if (last_slash) { + len = last_slash - exe_name; + dir_name = nullcheck (malloc ((len + 1) * sizeof(wchar_t))); + wmemcpy (dir_name, exe_name, len); + dir_name[len] = 0; + } + else + dir_name = L""; } } /* Generate a name for the link if not given */ if (opts.name_arg == NULL) { - /* Strip trailing /'s if any */ - strcpy (buf_str, opts.target_arg); - tmp_str = buf_str; - tmp = strlen (buf_str) - 1; - while (strrchr (buf_str, '/') == (buf_str + tmp)) - { - buf_str[tmp] = '\0'; - tmp--; - } - /* Get basename */ - while (*buf_str) - { - if (*buf_str == '/') - tmp_str = buf_str + 1; - buf_str++; - } - strcpy (link_name, tmp_str); + wchar_t *last_slash = wcsrchr (exe_name, '\\'); + if (!last_slash) + last_slash = wcsrchr (exe_name, '/'); + if (!last_slash) + error("need link name"); + link_base = last_slash + 1; } /* User specified a name, so check it and convert */ else @@ -475,93 +398,66 @@ { /* Cannot have absolute path relative to Desktop/SM Programs */ if (opts.name_arg[0] == '/') - { - free (buf_str); - 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 (opts.name_arg, ':') != NULL) - { - free (buf_str); - fprintf (stderr, "%s: all paths must be in POSIX format\n", - program_name); - usage (optCon, stderr, program_name); - return (1); + error ("absolute pathnames not allowed with -D/-P"); } - cygwin_conv_to_win32_path (opts.name_arg, link_name); + + link_base = conv_path (opts.name_arg, CCP_RELATIVE); } - - /* Add suffix to link name if necessary */ - if (strlen (link_name) > 4) - { - tmp = strlen (link_name) - 4; - if (strncmp (link_name + tmp, ".lnk", 4) != 0) - strcat (link_name, ".lnk"); - } - else - strcat (link_name, ".lnk"); - - /* Prepend relative path if necessary */ - if (opts.desktop_flag) - { - strcpy (buf_str, link_name); - if (!opts.allusers_flag) - SHGetSpecialFolderLocation (NULL, CSIDL_DESKTOPDIRECTORY, &id); + + /* Make space for the full link path */ + len = wcslen (link_base); + link_name = nullcheck (malloc((MAX_PATH + len + 8) * sizeof (wchar_t))); + + /* Get special folder location if required */ + if (opts.desktop_flag || opts.smprograms_flag) + { + int dir; + LPITEMIDLIST id; + if (opts.desktop_flag) + dir = opts.allusers_flag ? CSIDL_COMMON_DESKTOPDIRECTORY + : CSIDL_DESKTOPDIRECTORY; else - SHGetSpecialFolderLocation (NULL, CSIDL_COMMON_DESKTOPDIRECTORY, &id); + dir = opts.allusers_flag ? CSIDL_COMMON_PROGRAMS + : CSIDL_PROGRAMS; + SHGetSpecialFolderLocation (NULL, dir, &id); SHGetPathFromIDList (id, link_name); - /* Make sure Win95 without "All Users" has output */ - if (strlen (link_name) == 0) - { - SHGetSpecialFolderLocation (NULL, CSIDL_DESKTOPDIRECTORY, &id); - SHGetPathFromIDList (id, link_name); - } - strcat (link_name, "\\"); - strcat (link_name, buf_str); + wcscat (link_name, L"\\"); } - - if (opts.smprograms_flag) - { - strcpy (buf_str, link_name); - if (!opts.allusers_flag) - SHGetSpecialFolderLocation (NULL, CSIDL_PROGRAMS, &id); - else - SHGetSpecialFolderLocation (NULL, CSIDL_COMMON_PROGRAMS, &id); - SHGetPathFromIDList (id, link_name); - /* Make sure Win95 without "All Users" has output */ - if (strlen (link_name) == 0) - { - SHGetSpecialFolderLocation (NULL, CSIDL_PROGRAMS, &id); - SHGetPathFromIDList (id, link_name); - } - strcat (link_name, "\\"); - strcat (link_name, buf_str); - } - - /* Setup description text */ + else + *link_name = 0; + + wcscat(link_name, link_base); + + /* Append ".lnk" if necessary. */ + if (len <= 4 || wcscmp (link_base + len - 4, L".lnk") != 0) + wcscat(link_name, L".lnk"); + + /* Convert icon name */ + if (opts.icon_name_arg != NULL) + icon_name = conv_path (opts.icon_name_arg, 0); + else + icon_name = NULL; + + /* Convert shortcut argument */ + if (opts.argument_arg != NULL) + args = conv_arg (opts.argument_arg, "argument"); + else + args = NULL; + + /* Convert description */ if (opts.desc_arg != NULL) - { - strncpy (desc, opts.desc_arg, MAX_PATH); - - /* There won't be a null terminated if strlen(desc_arg)>MAX_PATH */ - desc[MAX_PATH - 1] = '\0'; - } + desc = conv_arg (opts.desc_arg, "description"); else { /* Put the POSIX path in the "Description", just to be nice */ - cygwin_conv_to_full_posix_path (exe_name, desc); + desc = conv_arg(cygwin_create_path(CCP_WIN_W_TO_POSIX, exe_name), + "executable name"); } /* Beginning of Windows interface */ hres = OleInitialize (NULL); if (hres != S_FALSE && hres != S_OK) { - free (buf_str); fprintf (stderr, "%s: Could not initialize OLE interface\n", program_name); return (3); @@ -581,32 +477,19 @@ shell_link->lpVtbl->SetDescription (shell_link, desc); shell_link->lpVtbl->SetWorkingDirectory (shell_link, dir_name); if (opts.argument_arg) - shell_link->lpVtbl->SetArguments (shell_link, opts.argument_arg); + shell_link->lpVtbl->SetArguments (shell_link, args); if (opts.icon_flag) shell_link->lpVtbl->SetIconLocation (shell_link, - opts.icon_name_arg, + icon_name, opts.offset); if (opts.show_flag != SW_SHOWNORMAL) shell_link->lpVtbl->SetShowCmd (shell_link, opts.show_flag); - - /* Make link name Unicode-compliant */ - hres = - MultiByteToWideChar (CP_ACP, 0, link_name, -1, widepath, - MAX_PATH); + hres = persist_file->lpVtbl->Save (persist_file, link_name, TRUE); if (!SUCCEEDED (hres)) { - free (buf_str); - fprintf (stderr, "%s: Unicode translation failed%d\n", - program_name, hres); - return (3); - } - hres = persist_file->lpVtbl->Save (persist_file, widepath, TRUE); - if (!SUCCEEDED (hres)) - { - free (buf_str); fprintf (stderr, - "%s: Saving \"%s\" failed; does the target directory exist?\n", + "%s: Saving \"%ls\" failed\n", program_name, link_name); return (3); } @@ -615,17 +498,17 @@ } else { - free (buf_str); fprintf (stderr, "%s: QueryInterface failed\n", program_name); return (3); } } else { - free (buf_str); fprintf (stderr, "%s: CoCreateInstance failed\n", program_name); return (3); } + + return 0; } static const char * @@ -635,71 +518,91 @@ } static void -printTopDescription (FILE * f, char *name) +printTopDescription (void) { - char s[20]; - fprintf (f, "%s is part of cygutils version %s\n", name, getVersion ()); - fprintf (f, " create a Windows shortcut\n\n"); + fprintf (stderr, "%s is part of cygutils version %s\n", + program_name, getVersion ()); + fprintf (stderr, " create a Windows shortcut\n\n"); } static void -printBottomDescription (FILE * f, char *name) +printBottomDescription (void) { - fprintf (f, + fprintf (stderr, "\nNOTE: All filename arguments must be in unix (POSIX) format\n"); } static -printLicense (FILE * f, char *name) +printLicense (void) { - fprintf (f, + fprintf (stderr, "This program is free software; you can redistribute it and/or\n"); - fprintf (f, + fprintf (stderr, "modify it under the terms of the GNU General Public License\n"); - fprintf (f, + fprintf (stderr, "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, + fprintf (stderr, "of the License, or (at your option) any later version.\n"); + fprintf (stderr, "\n"); + fprintf (stderr, "This program is distributed in the hope that it will be useful,\n"); - fprintf (f, + fprintf (stderr, "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"); - fprintf (f, + fprintf (stderr, "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, + fprintf (stderr, "GNU General Public License for more details.\n"); + fprintf (stderr, "\n"); + fprintf (stderr, "You should have received a copy of the GNU General Public License\n"); - fprintf (f, + fprintf (stderr, "along with this program; if not, write to the Free Software\n"); - fprintf (f, + fprintf (stderr, "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"); + fprintf (stderr, "\n"); + fprintf (stderr, "See the COPYING file for license information.\n"); } + +static void +usage (void) +{ + poptPrintUsage (optCon, stderr, 0); +} + static void -usage (poptContext optCon, FILE * f, char *name) +help (void) { - poptPrintUsage (optCon, f, 0); + printTopDescription (); + poptPrintHelp (optCon, stderr, 0); + printBottomDescription (); } static void -help (poptContext optCon, FILE * f, char *name) +version (void) { - printTopDescription (f, name); - poptPrintHelp (optCon, f, 0); - printBottomDescription (f, name); + printTopDescription (); + fprintf (stderr, copyrightID); } static void -version (poptContext optCon, FILE * f, char *name) +license (void) { - printTopDescription (f, name); - fprintf (f, copyrightID); + printTopDescription (); + printLicense (); } static void -license (poptContext optCon, FILE * f, char *name) +error (char *msg) { - printTopDescription (f, name); - printLicense (f, name); + fprintf (stderr, "%s: %s\n", program_name, msg); + usage (); + exit (1); +} + +static void * +nullcheck (void *p) +{ + if (p == NULL) + { + fprintf (stderr, "%s: memory allocation error\n", program_name); + exit (2); + } + return p; }