mirror of
				https://github.com/mintty/wsltty.git
				synced 2025-10-31 07:04:00 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			815 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			815 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* This is a tweaked version of mkshortcut.c -- create a Windows shortcut
 | |
|    Changes:
 | |
|    * Facilitate path entries starting with Windows environment variables.
 | |
|      (works for working directory and icon location but not for target path)
 | |
|    * Do not barf on Windows path syntax.
 | |
|  */
 | |
| 
 | |
| #include <sys/cygwin.h>
 | |
| #include <string.h>
 | |
| #include <wchar.h>
 | |
| 
 | |
| #define dont_debug_cygwin_create_path
 | |
| 
 | |
| /* Preserve leading Windows environment variable for shortcut entries.
 | |
|    So e.g. %USERPROFILE% is not pseudo-resolved to some subdirectory 
 | |
|    but can be used as working directory.
 | |
|    NOTE:
 | |
|    This works for working directory and icon location but not for the 
 | |
|    target path which is still polluted with a drive prefix by Windows.
 | |
|  */
 | |
| void * _cygwin_create_path (int line, cygwin_conv_path_t what, const void *from)
 | |
| {
 | |
|   what &= CCP_CONVTYPE_MASK;
 | |
|   void * to = cygwin_create_path(what, from);
 | |
|   if (what == CCP_WIN_W_TO_POSIX ? *(wchar_t*)from == '%' : *(char*)from == '%') {
 | |
|     if (what == CCP_POSIX_TO_WIN_W) {
 | |
|       to = wcschr(to, '%') ?: to;
 | |
|     } else {
 | |
|       to = strchr(to, '%') ?: to;
 | |
|     }
 | |
|   }
 | |
| #ifdef debug_cygwin_create_path
 | |
|   switch (what) {
 | |
|     case CCP_POSIX_TO_WIN_A:
 | |
|       printf("[%d] %s -> %s\n", line, from, to);
 | |
|       break;
 | |
|     case CCP_POSIX_TO_WIN_W:
 | |
|       printf("[%d] %s -> %ls\n", line, from, to);
 | |
|       break;
 | |
|     case CCP_WIN_A_TO_POSIX:
 | |
|       printf("[%d] %s -> %s\n", line, from, to);
 | |
|       break;
 | |
|     case CCP_WIN_W_TO_POSIX:
 | |
|       printf("[%d] %ls -> %s\n", line, from, to);
 | |
|       break;
 | |
|   }
 | |
| #endif
 | |
|   return to;
 | |
| }
 | |
| 
 | |
| #define cygwin_create_path(what, from)	_cygwin_create_path(__LINE__, what, from)
 | |
| 
 | |
| 
 | |
| /* mkshortcut.c -- create a Windows shortcut
 | |
|  *
 | |
|  * Copyright (c) 2002 Joshua Daniel Franklin
 | |
|  *
 | |
|  * Unicode-enabled by (C) 2015 Thomas Wolff
 | |
|  * semantic changes:
 | |
|         Allow dir to be empty (legal in shortcut)
 | |
|  *
 | |
|  * This program is free software: you can redistribute it and/or modify
 | |
|  * it under the terms of the GNU General Public License as published by
 | |
|  * the Free Software Foundation, either version 3 of the License, or
 | |
|  * (at your option) any later version.
 | |
|  *
 | |
|  * 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
 | |
|  * GNU General Public License for more details.
 | |
|  *
 | |
|  * You should have received a copy of the GNU General Public License
 | |
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | |
|  *
 | |
|  * See the COPYING file for full license information.
 | |
|  *
 | |
|  * Exit values
 | |
|  *   1: user error (syntax error)
 | |
|  *   2: system error (out of memory, etc.)
 | |
|  *   3: windows error (interface failed)
 | |
|  *
 | |
|  * Compile with: gcc -o mkshortcut mkshortcut.c -lpopt -lole32 /usr/lib/w32api/libuuid.a
 | |
|  *  (You'd need to uncomment the moved to common.h lines.)
 | |
|  *
 | |
|  */
 | |
| 
 | |
| #if HAVE_CONFIG_H
 | |
| # include "config.h"
 | |
| #endif
 | |
| 
 | |
| //#include "common.h"
 | |
| #include <stdio.h>
 | |
| #include <popt.h>
 | |
| #include <sys/stat.h>
 | |
| #include <errno.h>
 | |
| #define PACKAGE_VERSION "*"
 | |
| 
 | |
| #include <locale.h>
 | |
| 
 | |
| #include <wchar.h>
 | |
| 
 | |
| #define NOCOMATTRIBUTE
 | |
| 
 | |
| #include <shlobj.h>
 | |
| #include <olectl.h>
 | |
| /* moved to common.h */
 | |
| /*
 | |
| #include <stdio.h>
 | |
| #include <popt.h>
 | |
| */
 | |
| #include <sys/cygwin.h>
 | |
| #include <string.h> // strlen
 | |
| 
 | |
| 
 | |
| static const char versionID[] = PACKAGE_VERSION;
 | |
| static const char revID[] =
 | |
|   "$Id$";
 | |
| static const char copyrightID[] =
 | |
|   "Copyright (c) 2002\nJoshua Daniel Franklin. All rights reserved.\nLicensed under GPL v2.0\n";
 | |
| 
 | |
| typedef struct optvals_s
 | |
| {
 | |
|   int icon_flag;
 | |
|   int unix_flag;
 | |
|   int windows_flag;
 | |
|   int allusers_flag;
 | |
|   int desktop_flag;
 | |
|   int smprograms_flag;
 | |
|   int show_flag;
 | |
|   int offset;
 | |
|   char *name_arg;
 | |
|   char *desc_arg;
 | |
|   char *dir_name_arg;
 | |
|   char *argument_arg;
 | |
|   char *target_arg;
 | |
|   char *icon_name_arg;
 | |
| } optvals;
 | |
| 
 | |
| static int mkshortcut (optvals opts);
 | |
| static void printTopDescription (FILE * f, char *name);
 | |
| static void printBottomDescription (FILE * f, char *name);
 | |
| static const char *getVersion ();
 | |
| static void usage (FILE * f, char *name);
 | |
| static void help (FILE * f, char *name);
 | |
| static void version (FILE * f, char *name);
 | |
| static void license (FILE * f, char *name);
 | |
| 
 | |
| static char *program_name;
 | |
| static poptContext optCon;
 | |
| 
 | |
| static WCHAR *
 | |
| towcs (const char * s)
 | |
| {
 | |
|   int sizew = (strlen (s) * 2 + 1); // worst case: surrogates
 | |
|   WCHAR * ws = malloc (sizew * sizeof (WCHAR));
 | |
|   mbstowcs (ws, s, sizew);
 | |
|   return ws;
 | |
| }
 | |
| 
 | |
| int
 | |
| main (int argc, const char **argv)
 | |
| {
 | |
|   const char **rest;
 | |
|   int rc;
 | |
|   int ec = 0;
 | |
|   optvals opts;
 | |
| 
 | |
|   const char *tmp_str;
 | |
|   int icon_offset_flag;
 | |
|   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"},
 | |
|     {"desc", 'd', POPT_ARG_STRING, NULL, 'd',
 | |
|      "Text for description/tooltip (defaults to POSIX path of TARGET)",
 | |
|      "DESC"},
 | |
|     {"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"},
 | |
|     {"show", 's', POPT_ARG_STRING, NULL, 's',
 | |
|      "Window to show: normal, minimized, maximized", "norm|min|max"},
 | |
|     {"workingdir", 'w', POPT_ARG_STRING, NULL, 'w',
 | |
|      "Set working directory (defaults to directory path of TARGET)", "PATH"},
 | |
|     {"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}
 | |
|   };
 | |
| 
 | |
|   setlocale (LC_CTYPE, "");
 | |
| 
 | |
|   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.show_flag = SW_SHOWNORMAL;
 | |
|   opts.target_arg = NULL;
 | |
|   opts.argument_arg = NULL;
 | |
|   opts.name_arg = NULL;
 | |
|   opts.desc_arg = NULL;
 | |
|   opts.dir_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 (stdout, program_name);
 | |
|           goto exit;
 | |
|         case 'u':
 | |
|           usage (stdout, program_name);
 | |
|           goto exit;
 | |
|         case 'v':
 | |
|           version (stdout, program_name);
 | |
|           goto exit;
 | |
|         case 'l':
 | |
|           license (stdout, program_name);
 | |
|           goto exit;
 | |
|         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;
 | |
|                 }
 | |
|             }
 | |
|           break;
 | |
|         case 'i':
 | |
|           opts.icon_flag = 1;
 | |
|           if (arg = poptGetOptArg (optCon))
 | |
|             {
 | |
|               opts.icon_name_arg = (char *) cygwin_create_path (
 | |
|                   CCP_POSIX_TO_WIN_A, arg);
 | |
|               if (opts.icon_name_arg == NULL)
 | |
|                 {
 | |
|                   fprintf (stderr, "%s: error converting posix path to win32 (%s)\n",
 | |
|                            program_name, strerror (errno));
 | |
|                   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 's':
 | |
|           if (arg = poptGetOptArg (optCon))
 | |
|             {
 | |
|               if (strcmp (arg, "min") == 0)
 | |
|                 {
 | |
|                   opts.show_flag = SW_SHOWMINNOACTIVE;
 | |
|                 }
 | |
|               else if (strcmp (arg, "max") == 0)
 | |
|                 {
 | |
|                   opts.show_flag = SW_SHOWMAXIMIZED;
 | |
|                 }
 | |
|               else if (strcmp (arg, "norm") == 0)
 | |
|                 {
 | |
|                   opts.show_flag = SW_SHOWNORMAL;
 | |
|                 }
 | |
|               else
 | |
|                 {
 | |
|                   fprintf (stderr, "%s: %s not valid for show window\n",
 | |
|                            program_name, arg);
 | |
|                   ec = 2;
 | |
|                   goto exit;
 | |
|                 }
 | |
|             }
 | |
|           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;
 | |
|                 }
 | |
|             }
 | |
|           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 (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 (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;
 | |
|         }
 | |
|       rest++;
 | |
|       if (rest && *rest)
 | |
|         {
 | |
|           fprintf (stderr, "%s: Too many arguments: ", program_name);
 | |
|           while (*rest)
 | |
|             fprintf (stderr, "%s ", *rest++);
 | |
|           fprintf (stderr, "\n");
 | |
|           usage (stderr, program_name);
 | |
|           ec = 1;
 | |
|         }
 | |
|       else
 | |
|         {
 | |
|           // THE MEAT GOES HERE
 | |
|           ec = mkshortcut (opts);
 | |
|         }
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       fprintf (stderr, "%s: TARGET not specified\n", program_name);
 | |
|       usage (stderr, program_name);
 | |
|       ec = 1;
 | |
|     }
 | |
| 
 | |
| exit:
 | |
|   return ec;
 | |
| }
 | |
| 
 | |
| static char *
 | |
| xstrncat (char **dest, const char *add)
 | |
| {
 | |
|   size_t n = strlen (add);
 | |
|   size_t len = strlen (*dest) + n + 1;
 | |
|   char *s = (char *) realloc (*dest, len * sizeof (char));
 | |
|   if (!s)
 | |
|   {
 | |
|     fprintf (stderr, "%s: out of memory\n", program_name);
 | |
|     exit (2);
 | |
|   }
 | |
|   *dest = s;
 | |
|   return strncat (*dest, add, n);
 | |
| }
 | |
| 
 | |
| int
 | |
| mkshortcut (optvals opts)
 | |
| {
 | |
|   char * link_name = NULL;
 | |
|   WCHAR * exe_name = NULL;
 | |
|   WCHAR * dir_name = NULL;
 | |
|   WCHAR * desc = NULL;
 | |
|   char * buf_str;
 | |
|   char * tmp_str;
 | |
|   char * base_str;
 | |
|   int tmp;
 | |
| 
 | |
|   /* For OLE interface */
 | |
|   LPITEMIDLIST id;
 | |
|   HRESULT hres;
 | |
|   IShellLinkW * shell_link;
 | |
|   IPersistFile * persist_file;
 | |
| 
 | |
|   exe_name = (WCHAR *) cygwin_create_path (CCP_POSIX_TO_WIN_W, opts.target_arg);
 | |
|   if (!exe_name)
 | |
|     {
 | |
|       fprintf (stderr, "%s: error converting posix path to win32 (%s)\n",
 | |
|                program_name, strerror (errno));
 | |
|       return 2;
 | |
|     }
 | |
| 
 | |
| #ifdef colon_stuff
 | |
|   /*  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] == ':')
 | |
|         {
 | |
|           fprintf (stderr, "%s: all paths must be in POSIX format\n",
 | |
|                    program_name);
 | |
|           usage (stderr, program_name);
 | |
|           return 1;
 | |
|         }
 | |
|       dir_name = L"";
 | |
|     }
 | |
|   /* Convert TARGET to win32 path */
 | |
|   else
 | |
| #endif
 | |
|     {
 | |
|       buf_str = strdup (opts.target_arg);
 | |
| 
 | |
|       if (opts.dir_name_arg != NULL)
 | |
|       /*  Get a working dir from 'w' option */
 | |
|         {
 | |
| #ifdef colon_stuff
 | |
|           if (strchr (opts.dir_name_arg, ':') != NULL)
 | |
|             {
 | |
|               fprintf (stderr, "%s: all paths must be in POSIX format\n",
 | |
|                        program_name);
 | |
|               usage (stderr, program_name);
 | |
|               return 1;
 | |
|             }
 | |
| #endif
 | |
|           dir_name = (WCHAR *) cygwin_create_path (CCP_POSIX_TO_WIN_W,
 | |
|                                                    opts.dir_name_arg);
 | |
|           if (!dir_name)
 | |
|           {
 | |
|             fprintf (stderr, "%s: error converting posix path to win32 (%s)\n",
 | |
|                      program_name, strerror (errno));
 | |
|             return 2;
 | |
|           }
 | |
|         }
 | |
|       else
 | |
|       /*  Allow dir to be empty (legal in shortcut) */
 | |
|         {
 | |
|           dir_name = L"";
 | |
|         }
 | |
|     }
 | |
| 
 | |
|   /*  Generate a name for the link if not given */
 | |
|   if (opts.name_arg == NULL)
 | |
|     {
 | |
|       /*  Strip trailing /'s if any */
 | |
|       buf_str = strdup (opts.target_arg);
 | |
|       base_str = buf_str;
 | |
|       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++;
 | |
|         }
 | |
|       link_name = strdup (tmp_str);
 | |
|     }
 | |
|   /*  User specified a name, so check it and convert  */
 | |
|   else
 | |
|     {
 | |
|       if (opts.desktop_flag || opts.smprograms_flag)
 | |
|         {
 | |
|           /*  Cannot have absolute path relative to Desktop/SM Programs */
 | |
|           if (opts.name_arg[0] == '/')
 | |
|             {
 | |
|               fprintf (stderr,
 | |
|                        "%s: absolute pathnames not allowed with -D/-P\n",
 | |
|                        program_name);
 | |
|               usage (stderr, program_name);
 | |
|               return 1;
 | |
|             }
 | |
|         }
 | |
|       /*  Sigh. Another W32 path */
 | |
| #ifdef colon_stuff
 | |
|       if (strchr (opts.name_arg, ':') != NULL)
 | |
|         {
 | |
|           fprintf (stderr, "%s: all paths must be in POSIX format\n",
 | |
|                    program_name);
 | |
|           usage (stderr, program_name);
 | |
|           return 1;
 | |
|         }
 | |
| #endif
 | |
|       link_name = (char *) cygwin_create_path (
 | |
|           CCP_POSIX_TO_WIN_A | CCP_RELATIVE, opts.name_arg);
 | |
|           // passing multi-byte characters transparently per byte
 | |
|       if (!link_name)
 | |
|       {
 | |
|         fprintf (stderr, "%s: error converting posix path to win32 (%s)\n",
 | |
|                  program_name, strerror (errno));
 | |
|         return 2;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|   /*  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)
 | |
|         xstrncat (&link_name, ".lnk");
 | |
|     }
 | |
|   else
 | |
|     xstrncat (&link_name, ".lnk");
 | |
| 
 | |
|   /*  Prepend relative path if necessary  */
 | |
|   if (opts.desktop_flag)
 | |
|     {
 | |
|       char local_buf[MAX_PATH];
 | |
|       buf_str = strdup (link_name);
 | |
| 
 | |
|       if (!opts.allusers_flag)
 | |
|         SHGetSpecialFolderLocation (NULL, CSIDL_DESKTOPDIRECTORY, &id);
 | |
|       else
 | |
|         SHGetSpecialFolderLocation (NULL, CSIDL_COMMON_DESKTOPDIRECTORY, &id);
 | |
|       SHGetPathFromIDList (id, local_buf);
 | |
|       /*  Make sure Win95 without "All Users" has output  */
 | |
|       if (strlen (local_buf) == 0)
 | |
|         {
 | |
|           SHGetSpecialFolderLocation (NULL, CSIDL_DESKTOPDIRECTORY, &id);
 | |
|           SHGetPathFromIDList (id, local_buf);
 | |
|         }
 | |
|       link_name = strdup (local_buf);
 | |
|       xstrncat (&link_name, "\\");
 | |
|       xstrncat (&link_name, buf_str);
 | |
|     }
 | |
| 
 | |
|   if (opts.smprograms_flag)
 | |
|     {
 | |
|       char local_buf[MAX_PATH];
 | |
|       buf_str = strdup (link_name);
 | |
| 
 | |
|       if (!opts.allusers_flag)
 | |
|         SHGetSpecialFolderLocation (NULL, CSIDL_PROGRAMS, &id);
 | |
|       else
 | |
|         SHGetSpecialFolderLocation (NULL, CSIDL_COMMON_PROGRAMS, &id);
 | |
|       SHGetPathFromIDList (id, local_buf);
 | |
|       /*  Make sure Win95 without "All Users" has output  */
 | |
|       if (strlen (local_buf) == 0)
 | |
|         {
 | |
|           SHGetSpecialFolderLocation (NULL, CSIDL_PROGRAMS, &id);
 | |
|           SHGetPathFromIDList (id, local_buf);
 | |
|         }
 | |
|       link_name = strdup (local_buf);
 | |
|       xstrncat (&link_name, "\\");
 | |
|       xstrncat (&link_name, buf_str);
 | |
|     }
 | |
| 
 | |
|   /*  Make link name Unicode-compliant  */
 | |
|   WCHAR * widename = towcs (link_name);
 | |
| 
 | |
|   /* After Windows 7, saving link to relative path fails; work around that */
 | |
| #ifdef corrupt_memory
 | |
|   WCHAR widepath[MAX_PATH];
 | |
|   hres = GetFullPathNameW (widename, sizeof (widepath), widepath, NULL);
 | |
|   if (hres == 0)
 | |
|     {
 | |
|       fprintf (stderr, "%s: Could not qualify link name\n", program_name);
 | |
|       return 2;
 | |
|     }
 | |
| #else
 | |
|   WCHAR * widepath = (WCHAR *) cygwin_create_path (CCP_POSIX_TO_WIN_W, link_name);
 | |
| #endif
 | |
|   link_name = (char *) cygwin_create_path (CCP_WIN_W_TO_POSIX, widepath);
 | |
| 
 | |
|   /* Setup description text */
 | |
|   if (opts.desc_arg != NULL)
 | |
|     {
 | |
|       desc = towcs (opts.desc_arg);
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       /* Put the POSIX path in the "Description", just to be nice */
 | |
|       desc = towcs (cygwin_create_path (CCP_WIN_A_TO_POSIX, exe_name));
 | |
|       if (!desc)
 | |
|       {
 | |
|         fprintf (stderr, "%s: error converting win32 path to posix (%s)\n",
 | |
|                  program_name, strerror (errno));
 | |
|         return 2;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|   /*  Beginning of Windows interface */
 | |
|   hres = OleInitialize (NULL);
 | |
|   if (hres != S_FALSE && hres != S_OK)
 | |
|     {
 | |
|       fprintf (stderr, "%s: Could not initialize OLE interface\n",
 | |
|                program_name);
 | |
|       return 3;
 | |
|     }
 | |
| 
 | |
|   hres =
 | |
|     CoCreateInstance (&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
 | |
|                       &IID_IShellLinkW, (void **) &shell_link);
 | |
|   if (SUCCEEDED (hres))
 | |
|     {
 | |
|       hres =
 | |
|         shell_link->lpVtbl->QueryInterface (shell_link, &IID_IPersistFile,
 | |
|                                             (void **) &persist_file);
 | |
|       if (SUCCEEDED (hres))
 | |
|         {
 | |
|           shell_link->lpVtbl->SetPath (shell_link, exe_name);
 | |
|           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,
 | |
|                                               towcs (opts.argument_arg));
 | |
|           if (opts.icon_flag)
 | |
|             shell_link->lpVtbl->SetIconLocation (shell_link,
 | |
|                                                  towcs (opts.icon_name_arg),
 | |
|                                                  opts.offset);
 | |
|           if (opts.show_flag != SW_SHOWNORMAL)
 | |
|             shell_link->lpVtbl->SetShowCmd (shell_link, opts.show_flag);
 | |
| 
 | |
|           hres = persist_file->lpVtbl->Save (persist_file, widepath, TRUE);
 | |
|           if (!SUCCEEDED (hres))
 | |
|             {
 | |
|               fprintf (stderr,
 | |
|                        "%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);
 | |
| 
 | |
|           /* If we are creating shortcut for all users, ensure it is readable by all users */
 | |
|           if (opts.allusers_flag)
 | |
|             {
 | |
|               char *posixpath = (char *) cygwin_create_path (
 | |
|                 CCP_WIN_W_TO_POSIX | CCP_ABSOLUTE, widepath);
 | |
|               if (posixpath && *posixpath)
 | |
|                 {
 | |
|                   struct stat statbuf;
 | |
|                   if (stat (posixpath, &statbuf))
 | |
|                   {
 | |
|                     fprintf (stderr,
 | |
|                              "%s: stat \"%s\" failed\n",
 | |
|                              program_name, posixpath);
 | |
|                   }
 | |
|                   else if (chmod (posixpath, statbuf.st_mode|S_IRUSR|S_IRGRP|S_IROTH))
 | |
|                   {
 | |
|                     fprintf (stderr,
 | |
|                              "%s: chmod \"%s\" failed\n",
 | |
|                              program_name, posixpath);
 | |
|                   }
 | |
|                 }
 | |
|             }
 | |
|           return 0;
 | |
|         }
 | |
|       else
 | |
|         {
 | |
|           fprintf (stderr, "%s: QueryInterface failed\n", program_name);
 | |
|           return 3;
 | |
|         }
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       fprintf (stderr, "%s: CoCreateInstance failed\n", program_name);
 | |
|       return 3;
 | |
|     }
 | |
| }
 | |
| 
 | |
| static const char *
 | |
| getVersion ()
 | |
| {
 | |
|   return versionID;
 | |
| }
 | |
| 
 | |
| static void
 | |
| printTopDescription (FILE * f, char *name)
 | |
| {
 | |
|   char s[20];
 | |
|   fprintf (f, "%s is part of cygutils version %s\n", name, getVersion ());
 | |
|   fprintf (f, "  create a Windows shortcut\n\n");
 | |
| }
 | |
| 
 | |
| static void
 | |
| printBottomDescription (FILE * f, char *name)
 | |
| {
 | |
|   fprintf (f,
 | |
|            "\nNOTE: All filename arguments must be in unix (POSIX) format\n");
 | |
| }
 | |
| 
 | |
| static void
 | |
| printLicense (FILE * f, char *name)
 | |
| {
 | |
|   fprintf (f,
 | |
|            "This program is free software: you can redistribute it and/or modify\n"
 | |
|            "it under the terms of the GNU General Public License as published by\n"
 | |
|            "the Free Software Foundation, either version 3 of the License, or\n"
 | |
|            "(at your option) any later version.\n\n"
 | |
|            "This program is distributed in the hope that it will be useful,\n"
 | |
|            "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
 | |
|            "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
 | |
|            "GNU General Public License for more details.\n\n"
 | |
|            "You should have received a copy of the GNU General Public License\n"
 | |
|            "along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n"
 | |
|            "See the COPYING file for full license information.\n");
 | |
| }
 | |
| 
 | |
| static void
 | |
| usage (FILE * f, char *name)
 | |
| {
 | |
|   poptPrintUsage (optCon, f, 0);
 | |
| }
 | |
| 
 | |
| static void
 | |
| help (FILE * f, char *name)
 | |
| {
 | |
|   printTopDescription (f, name);
 | |
|   poptPrintHelp (optCon, f, 0);
 | |
|   printBottomDescription (f, name);
 | |
| }
 | |
| 
 | |
| static void
 | |
| version (FILE * f, char *name)
 | |
| {
 | |
|   printTopDescription (f, name);
 | |
|   fprintf (f, copyrightID);
 | |
| }
 | |
| 
 | |
| static void
 | |
| license (FILE * f, char *name)
 | |
| {
 | |
|   printTopDescription (f, name);
 | |
|   printLicense (f, name);
 | |
| }
 |