OSDN Git Service

Add scripting hooks to support creation of MS-Windows shortcuts.
authorKeith Marshall <keithmarshall@users.sourceforge.net>
Fri, 6 Apr 2012 22:49:36 +0000 (22:49 +0000)
committerKeith Marshall <keithmarshall@users.sourceforge.net>
Fri, 6 Apr 2012 22:49:36 +0000 (22:49 +0000)
ChangeLog
Makefile.in
scripts/libexec/setup.lua [new file with mode: 0644]
scripts/libexec/shlink.js [new file with mode: 0644]
scripts/libexec/unlink.js [new file with mode: 0644]
scripts/libexec/wsh.lua [new file with mode: 0644]
src/clistub.c

index 690c081..1879a01 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,27 @@
 2012-04-06  Keith Marshall  <keithmarshall@users.sourceforge.net>
 
+       Add scripting hooks to support creation of MS-Windows shortcuts.
+
+       * src/clistub.c (progname): New static variable with file scope; it
+       replaces former automatic variable, of same name, in main() function.
+       (set_script_hook): New static function; implement it, and call it...
+       (main) [--desktop, --start-menu]: ...from here, to interpret these new
+       command line options; they are propagated to scripts via...
+       (MINGW_GET_DESKTOP_HOOK, MINGW_GET_START_MENU_HOOK): ...these new
+       internally assigned environment variables.
+       (help_text): Document --desktop and --start-menu.
+
+       * scripts/libexec/setup.lua scripts/libexec/wsh.lua: New lua module
+       files; they implement a lua scripting API; they delegate requests to
+       the Windows Scripting Host, via either of this pair of...
+       * scripts/libexec/shlink.js: ...new script to create shortcuts, or...
+       * scripts/libexec/unlink.js: ...its complement, to remove them.
+       * Makefile.in (SRCDIST_SUBDIRS): Integrate them; enumerate them in...
+       (scripts_srcdir, LIBEXEC_SCRIPTS): ...these new macros; use to...
+       (install): ...install them.
+
+2012-04-06  Keith Marshall  <keithmarshall@users.sourceforge.net>
+
        Initialise $LUA_PATH to locate mingw-get specific scripts.
 
        * src/pkgexec.cpp (lua_path_init): New static inline function.
index 03f8c1e..daaf9e1 100644 (file)
@@ -63,8 +63,12 @@ CORE_DLL_OBJECTS  =  climain.$(OBJEXT) pkgshow.$(OBJEXT) dmh.$(OBJEXT) \
    tinyxml.$(OBJEXT) tinyxmlparser.$(OBJEXT) \
    tinystr.$(OBJEXT) tinyxmlerror.$(OBJEXT)
 
+script_srcdir = ${srcdir}/scripts/libexec
+
 BIN_PROGRAMS = pkginfo$(EXEEXT) mingw-get$(EXEEXT)
 LIBEXEC_PROGRAMS = gui$(EXEEXT) lastrites$(EXEEXT)
+LIBEXEC_SCRIPTS = ${script_srcdir}/setup.lua ${script_srcdir}/wsh.lua \
+   ${script_srcdir}/shlink.js ${script_srcdir}/unlink.js
 LIBEXEC_DATA = mingw-get-0.dll
 
 all: $(BIN_PROGRAMS) $(LIBEXEC_PROGRAMS) $(LIBEXEC_DATA)
@@ -133,7 +137,7 @@ install: installdirs install-profile
        for image in $(LIBEXEC_PROGRAMS); do \
          $(INSTALL_PROGRAM) $$image ${libexecdir}/${PACKAGE_TARNAME}; \
          done
-       for image in $(LIBEXEC_DATA); do \
+       for image in $(LIBEXEC_DATA) $(LIBEXEC_SCRIPTS); do \
          $(INSTALL_DATA) $$image ${libexecdir}/${PACKAGE_TARNAME}; \
          done
 
@@ -155,7 +159,8 @@ LICENCE_FILES = README COPYING
 SRCDIST_FILES = $(LICENCE_FILES) ChangeLog NEWS INSTALL \
   aclocal.m4 configure.ac configure Makefile.in version.c.in
 
-SRCDIST_SUBDIRS = build-aux m4 src src/pkginfo srcdist-doc tinyxml xml
+SRCDIST_SUBDIRS = build-aux m4 src src/pkginfo srcdist-doc \
+  scripts/libexec tinyxml xml
 
 # The names of distributed pacakge archive files incorporate version
 # information, derived from PACKAGE_VERSION; this is decomposed, so that
diff --git a/scripts/libexec/setup.lua b/scripts/libexec/setup.lua
new file mode 100644 (file)
index 0000000..ab25f78
--- /dev/null
@@ -0,0 +1,72 @@
+--
+-- setup.lua
+--
+-- $Id$
+--
+-- Lua 5.2 module providing common setup hooks for mingw-get.
+--
+--
+-- This file is a component of mingw-get.
+--
+-- Written by Keith Marshall <keithmarshall@users.sourceforge.net>
+-- Copyright (C) 2012, MinGW Project
+--
+--
+-- Permission is hereby granted, free of charge, to any person obtaining a
+-- copy of this software and associated documentation files (the "Software"),
+-- to deal in the Software without restriction, including without limitation
+-- the rights to use, copy, modify, merge, publish, distribute, sublicense,
+-- and/or sell copies of the Software, and to permit persons to whom the
+-- Software is furnished to do so, subject to the following conditions:
+--
+-- The above copyright notice and this permission notice shall be included
+-- in all copies or substantial portions of the Software.
+--
+-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+-- OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+-- THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+-- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+-- DEALINGS IN THE SOFTWARE.
+--
+   local M = {}
+   local wsh = require "wsh"
+--
+   function M.libexec_path( script, subsystem )
+     if not subsystem
+     then
+       subsystem = "mingw-get"
+     end
+     return wsh.libexec_path( script, subsystem )
+   end
+--
+   function M.shlink( args, ... )
+     if args
+     then
+       wsh.execute( M.libexec_path( "shlink.js" ), args, ... )
+     end
+   end
+--
+   function M.unlink( args, ... )
+     if args
+     then
+       wsh.execute( M.libexec_path( "unlink.js" ), args, ... )
+     end
+   end
+--
+   function M.create_shortcuts( ... )
+     M.shlink( os.getenv( "MINGW_GET_DESKTOP_HOOK" ), ... )
+     M.shlink( os.getenv( "MINGW_GET_START_MENU_HOOK" ), ... )
+   end
+--
+   function M.delete_shortcuts( ... )
+     M.unlink( "--desktop", ... )
+     M.unlink( "--all-users --desktop", ... )
+     M.unlink( "--all-users --start-menu", ... )
+     M.unlink( "--start-menu", ... )
+   end
+--
+   return M
+--
+-- $RCSfile$: end of file */
diff --git a/scripts/libexec/shlink.js b/scripts/libexec/shlink.js
new file mode 100644 (file)
index 0000000..0b42c70
--- /dev/null
@@ -0,0 +1,385 @@
+/*
+ * shlink.js
+ *
+ * $Id$
+ *
+ * Invoke the Windows Scripting Host to create or modify a windows shortcut,
+ * with specified parameters.
+ *
+ * Usage:
+ *   cscript -nologo shlink.js [--option[=value] ...] target name
+ *
+ * Options:
+ *   --verbose report shortcut parameter assignments on stdout
+ *
+ *   --all-users
+ *             create the shortcut in the start menu, or on the
+ *             desktop available to all users; (has no effect,
+ *             unless --start-menu or --desktop is specified)
+ *
+ *   --desktop create the shortcut on the desktop of the current
+ *             user, (or for all users, with --all-users)
+ *
+ *   --start-menu
+ *             create the shortcut in the user's start menu, (or
+ *             all users' start menu, with --all-users)
+ *
+ *   --arguments="argument list ..."
+ *             specify arguments to be passed to the command
+ *             invoked by the shortcut
+ *
+ *   --description="text ..."
+ *             specify the "tool tip" for the shortcut
+ *
+ *   --hotkey="[SHIFT+][CTRL+][ALT+]key"
+ *             specify a hot-key combination which may be used
+ *             to invoke the shortcut command
+ *
+ *   --icon="d:\path\to\icon\file[,index]"
+ *             specify an icon to associate with the shortcut;
+ *             if unspecified, the first icon in the target file,
+ *             or failing that, a system default is used
+ *
+ *   --show=normal|maximised|minimised
+ *             specify the style of window in which the program
+ *             invoked by the shortcut will start running
+ *
+ *   --workingdir="d:\path\to\working\directory"
+ *             specify the absolute path name for the directory
+ *             which will become the current working directory when
+ *             the program invoked by the shortcut is started
+ *
+ * Parameters:
+ *   target    the path name to the program to invoke, or the file
+ *             to be opened, when the shortcut is activated.
+ *
+ *   name      the path name for the shortcut file itself; if this
+ *             does not end with either a ".lnk" or ".url" extension,
+ *             then ".lnk" will be appended, so creating a regular
+ *             file system link type shortcut.
+ *
+ *
+ * This file is a component of mingw-get.
+ *
+ * Written by Keith Marshall <keithmarshall@users.sourceforge.net>
+ * Copyright (C) 2012, MinGW Project
+ *
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ */
+var cmdName = WScript.ScriptName;
+var WinShell = WScript.CreateObject("WScript.Shell");
+
+/* Properties for the shortcut may be specified as arguments
+ * to command line options, each of which is represented in the
+ * GNU long option form, by keywords with property associations
+ * as specified by:
+ */
+var options = Array(
+      "name",          /*  File name/path of .lnk file */
+      "all-users",
+      "start-menu",
+      "desktop",
+      "verbose",
+  /* 
+   *  Option               Property
+   *  -------------        -------------------------
+   */ 
+      "arguments",     /*  Shortcut.Arguments          */
+      "description",   /*  Shortcut.Description        */
+      "hotkey",                /*  Shortcut.HotKey             */
+      "icon",          /*  Shortcut.IconLocation       */
+      "show",          /*  Shortcut.WindowStyle        */
+      "target",                /*  Shortcut.TargetPath         */
+      "workingdir"     /*  Shortcut.WorkingDirectory   */
+    );
+
+/* The "complain" function provides a mechanism for writing
+ * diagnostic messages to stderr, and optionally aborting the
+ * calling script.
+ */
+function complain( condition, message )
+{
+  WScript.StdErr.WriteLine( cmdName + ": " + message );
+  if( condition < 0 )
+    WScript.Quit( -condition );
+  return condition;
+}
+
+/* Strip ".js" suffix from command name.
+ */
+var i = cmdName.lastIndexOf( ".js" );
+if( (i > 0) &&  (cmdName.substr( i ) == ".js") )
+  cmdName = cmdName.substr( 0, i );
+
+/* Initialise all options to "unassigned" state.
+ */
+var target = -1;
+var lnkname = -1;
+var desktop = -1;
+var allusers = -1;
+var startmenu = -1;
+var verbosity = -1;
+var unassigned = "+++unassigned+++";
+var assigned = Array( options.length );
+for( var k = 0; k < assigned.length; k++ )
+{
+  switch( options[k] )
+  {
+    case "name":
+      lnkname = k;
+      break;
+
+    case "desktop":
+      desktop = k;
+      break;
+
+    case "start-menu":
+      startmenu = k;
+      break;
+
+    case "all-users":
+      allusers = k;
+      break;
+
+    case "target":
+      target = k;
+      break;
+
+    case "verbose":
+      verbosity = k;
+  }
+  assigned[k] = unassigned;
+}
+
+/* Define the prefix, which will qualify the location
+ * of the shortcut; initialise it to nothing, so that by
+ * default shortcuts will be located by absolute path name,
+ * as specified, or relative to current working directory.
+ */
+var prefix = "";
+
+var j;
+function assign_option( name, value )
+{
+  switch( name )
+  {
+    case "desktop":
+    case "start-menu":
+    case "all-users":
+    case "verbose":
+      j = i;
+      return "set";
+  }
+  return value;
+}
+
+/* Parse the command line.
+ */
+var argv = WScript.Arguments;
+for( i = 0; i < argv.length; i++ )
+{
+  if( argv( i ).indexOf( "--" ) == 0 )
+  {
+    /* Handle arguments specifying options...
+     */
+    if( (optind = argv( j = i ).indexOf( "=" ) + 1) > 3 )
+    {
+      optnam = argv( j ).substr( 2, optind - 3 );
+      optarg = argv( j ).substr( optind );
+    }
+    else
+    {
+      optnam = argv( j ).substr( 2 );
+      if( ++j < argv.length )
+       optarg = argv( j );
+      else
+       optarg = unassigned;
+    }
+
+    var matched = 0;
+    for( var k = 0; k < options.length; k++ )
+    {
+      if( optnam == options[k] )
+      {
+       matched = 1;
+       assigned[k] = assign_option( options[k], optarg );
+       k = options.length;
+      }
+      else if( options[k].indexOf(optnam) == 0 )
+      {
+       if( ++matched > 1 )
+         complain( -2, cmdName + "option '" + argv( i ) + "' is ambiguous" );
+       assigned[k] = assign_option( options[k], optarg );
+      }
+    }
+    if( matched == 1 )
+    {
+      i = j;
+    }
+    else
+      complain( -2, "option '" + argv( i ) + "' is not supported" );
+  }
+
+  /* Handle non-option arguments...
+   */
+  else if( (target >= 0) && (assigned[target] == unassigned) )
+    /*
+     * ...the first of which is the target to which the
+     * (shortcut) link is to refer...
+     */
+    assigned[target] = argv( i );
+
+  else if( (lnkname >= 0) && (assigned[lnkname] == unassigned) )
+    /*
+     * ...the second is the file system path name at which
+     * the link file itself is to be saved...
+     */
+    assigned[lnkname] = argv( i );
+
+  else
+    /* ...and any more than two is an error.
+     */
+    complain( -2, "too many arguments" );
+}
+
+/* Verify that mandatory arguments have been specified...
+ */
+if( assigned[target] == unassigned )
+  /*
+   * First of these, the TargetPath specification, is required;
+   * diagnose and abort, if missing.
+   */
+  complain( -2, "missing argument: no target path specified" );
+
+if( assigned[lnkname] == unassigned )
+  /*
+   * Second, the path name for the link file itself is essential;
+   * again, diagnose and abort if missing.
+   */
+  complain( -2, "missing argument: no shortcut name specified" );
+
+/* We have both the mandatory arguments; check that the link name
+ * is properly terminated by a ".lnk" or ".url" suffix...
+ */
+var suffix = "";
+if( (i = assigned[lnkname].length - 4) > 0 )
+  suffix = assigned[lnkname].substr( i );
+
+/* ...and append ".lnk" as default, if not already present.
+ */
+if( (suffix != ".lnk") && (suffix != ".url") )
+  assigned[lnkname] += ".lnk";
+
+/* Add the appropriate prefix for '--desktop' or '--start-menu' shortcuts.
+ */
+if( assigned[desktop] != unassigned )
+{
+  if( assigned[startmenu] != unassigned )
+    complain( -2, "options '--desktop' and '--start-menu' are incompatible" );
+  else prefix = WinShell.SpecialFolders( (assigned[allusers] == unassigned)
+      ? "Desktop" : "AllUsersDesktop" ) + "\\";
+}
+else if( assigned[startmenu] != unassigned )
+  prefix = WinShell.SpecialFolders( (assigned[allusers] == unassigned)
+      ? "StartMenu" : "AllUsersStartMenu" ) + "\\";
+
+else if( assigned[allusers] != unassigned )
+  complain( -2,
+      "option '--all-users' also requires '--desktop' or '--start-menu'"
+    );
+
+/* Handle verbosity...
+ */
+function verbose_assignment( property, value )
+{
+  if( assigned[verbosity] == "set" )
+    WScript.Echo( cmdName + ": set " + property + " = " + value );
+}
+
+/* Initialise the shortcut entity reference.
+ */
+if( assigned[verbosity] == "set" )
+  WScript.Echo( cmdName + ": create shortcut: " + prefix + assigned[lnkname] );
+
+var Shortcut = WinShell.CreateShortcut( prefix + assigned[lnkname] );
+
+for( var k = 0; k < options.length; k++ )
+{
+  if( assigned[k] != unassigned )
+    switch( options[k] )
+    {
+      case "arguments":
+       verbose_assignment( "Shortcut.Arguments", assigned[k] );
+       Shortcut.Arguments = assigned[k];
+       break;
+
+      case "description":
+       verbose_assignment( "Shortcut.Description", assigned[k] );
+       Shortcut.Description = assigned[k];
+       break;
+
+      case "hotkey":
+       verbose_assignment( "Shortcut.HotKey", assigned[k] );
+       Shortcut.HotKey = assigned[k];
+       break;
+
+      case "icon":
+       verbose_assignment( "Shortcut.IconLocation", assigned[k] );
+       Shortcut.IconLocation = assigned[k];
+       break;
+
+      case "show":
+       verbose_assignment( "Shortcut.WindowStyle", assigned[k] );
+        var style = Array(
+           "normal",     1,
+           "maximised",  3,
+           "maximized",  3,
+           "minimised",  7,
+           "minimized",  7
+         );
+       for( j = 0; j < style.length; j++ )
+         if( style[j++].indexOf( assigned[k] ) == 0 )
+         {
+           Shortcut.WindowStyle = style[j];
+           j = 100;
+         }
+       if( j < 100 )
+         complain( 1, "unrecognised mode '--show=" + assigned[k] + "' ignored" );
+       break;
+
+      case "target":
+       verbose_assignment( "Shortcut.TargetPath", assigned[k] );
+       Shortcut.TargetPath = assigned[k];
+       break;
+
+      case "workingdir":
+       verbose_assignment( "Shortcut.WorkingDirectory", assigned[k] );
+       Shortcut.WorkingDirectory = assigned[k];
+    }
+  else if( assigned[verbosity] == "set" )
+    WScript.Echo( cmdName + ": option '--" + options[k] + "' is unassigned" );
+}
+
+/* Commit the shortcut entity to disk.
+ */
+Shortcut.Save();
+
+/* $RCSfile$: end of file */
diff --git a/scripts/libexec/unlink.js b/scripts/libexec/unlink.js
new file mode 100644 (file)
index 0000000..f4b544c
--- /dev/null
@@ -0,0 +1,244 @@
+/*
+ * unlink.js
+ *
+ * $Id$
+ *
+ * Invoke the Windows Scripting Host to remove specified files, typically
+ * from the user's or all users' start menu or desktop directories.
+ *
+ * Usage:
+ *   cscript -nologo unlink.js [--option ...] file ...
+ *
+ * Options:
+ *   --all-users
+ *             references to desktop or start menu files apply
+ *             to the directories serving all users
+ *
+ *   --desktop remove files from the user's desktop directory, (or
+ *             from all users' desktop directory, with --all-users)
+ *
+ *   --start-menu
+ *             remove files from the user's start menu directory, (or
+ *             from all users' start menu directory, with --all-users)
+ *
+ *   --if-linked=target_path
+ *             do not remove any matched file which is not a shortcut
+ *             (.lnk or .url) referring to the specified target path;
+ *             when this option is specified, any specified file name
+ *             which does not already have a .lnk or .url extension
+ *             will have .lnk appended, before seeking any match
+ *
+ *   --force   override read-only attributes when removing files.
+ *
+ * Parameters:
+ *   file      the path name for a file to be removed; if --desktop
+ *             or --start-menu is specified, it should be a relative
+ *             path name, which will then be resolved relative to the
+ *             appropriate system directory path.
+ *
+ *
+ * This file is a component of mingw-get.
+ *
+ * Written by Keith Marshall <keithmarshall@users.sourceforge.net>
+ * Copyright (C) 2012, MinGW Project
+ *
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ */
+var cmdName = WScript.ScriptName;
+var FileSystem = WScript.CreateObject("Scripting.FileSystemObject");
+var WinShell = WScript.CreateObject("WScript.Shell");
+
+/* Strip ".js" suffix from command name.
+ */
+var i = cmdName.lastIndexOf( ".js" );
+if( (i > 0) &&  (cmdName.substr( i ) == ".js") )
+  cmdName = cmdName.substr( 0, i );
+
+/* Specify the keywords which identify command line options.
+ */
+var options = Array(
+    /*
+     *  Option             Effect
+     *  -------------      -------------------------
+     */
+       "all-users",     /* Resolve file path names in all-users domain... */
+       "start-menu",    /* ...relative to user's or all-users' start menu... */
+       "desktop",       /* ...or desktop directories. */
+       "if-linked",     /* Only delete shortcuts with specified target path */
+       "force"          /* Forcibly override read-only attribute */
+    );
+
+/* Initialise all options as "unassigned".
+ */
+var unassigned = "+++unassigned+++";
+var assigned = Array( options.length );
+for( i = 0; i < assigned.length; i++ )
+  assigned[i] = unassigned;
+
+/* The "complain" function provides a mechanism for writing
+ * diagnostic messages to stderr, and optionally aborting the
+ * calling script.
+ */
+function complain( condition, message )
+{
+  WScript.StdErr.WriteLine( cmdName + ": " + message );
+  if( condition < 0 )
+    WScript.Quit( -condition );
+  return condition;
+}
+
+/* Parse the command line, to identify any specified options.
+ */
+var chklink = "";
+var argv = WScript.Arguments;
+for( i = 0; i < argv.length; i++ )
+{
+  if( argv( i ).indexOf( "--" ) == 0 )
+  {
+    /* Handle arguments specifying options...
+     */
+    var optind;
+    var matched = 0;
+    var optname = argv( i ).substr( 2 );
+    var optarg = "";
+    for( var k = 0; k < options.length; k++ )
+    {
+      /* Try to match each defined option in turn...
+       */
+      var argind = optname.indexOf( "=" );
+      if( argind > 0 )
+      {
+       optarg = optname.substr( argind + 1 );
+       optname = optname.substr( 0, argind );
+      }
+
+      if( optname == options[k] )
+      {
+       /* A exact (complete) match trumps any possible abbreviation...
+        */
+       matched = 1;
+       assigned[optind = k] = optname;
+       k = options.length;
+      }
+
+      else if( options[k].indexOf( optname ) == 0 )
+      {
+       /* Otherwise abbreviations are allowed, but must uniquely
+        * identify only one defined option name...
+        */
+       if( matched++ > 0 )
+         complain( -2, "option '" + argv( i ) + "' is ambiguous" );
+       assigned[optind = k] = options[k];
+      }
+    }
+    if( matched == 0 )
+      /*
+       * Bail out, if no match identified.
+       */
+      complain( -2, "option '" + argv( i ) + "' is undefined" );
+
+    if( assigned[optind] == "if-linked" )
+      chklink = optarg;
+  }
+}
+
+/* Establish prefix for resolving paths relative to desktop
+ * or start menu directories, and handle the --force option.
+ */
+var prefix = "";
+var allusers = false;
+var force = false;
+for( i = 0; i < options.length; i++ )
+{
+  switch( assigned[i] )
+  {
+    case "force":
+      force = true;
+      break;
+
+    case "all-users":
+      allusers = true;
+      break;
+
+    case "start-menu":
+      prefix = WinShell.SpecialFolders( allusers ? "AllUsersStartMenu"
+         : "StartMenu"
+       ) + "\\";
+      break;
+
+    case "desktop":
+      if( prefix != "" )
+       complain( -2, "options '--start-menu' and '--desktop' are incompatible" );
+      prefix = WinShell.SpecialFolders( allusers ? "AllUsersDesktop"
+         : "Desktop"
+       ) + "\\";
+  }
+}
+
+/* Parse the command line again, to process path name arguments,
+ * and delete the specified files.
+ */
+for( i = 0; i < argv.length; i++ )
+{
+  if( argv( i ).indexOf( "--" ) != 0 )
+  {
+    /* Not an option argument, so assume it's a path name.
+     */
+    var filename = prefix + argv( i );
+    if( chklink != "" )
+    {
+      /* The "--if-linked" option is in effect, and a viable
+       * target path has been defined.  The file to be deleted
+       * MUST be a shortcut file, (i.e. it MUST bear a ".lnk" or
+       * ".url" extension); supply ".lnk" as default, if this is
+       * not so.
+       */
+      var l = filename.length - 4;
+      var suffix = (l > 0) ? filename.substr( l ) : 0;
+      if( (suffix != ".lnk") && (suffix != ".url") )
+       filename += ".lnk";
+    }
+    if( FileSystem.FileExists( filename ) )
+    {
+      /* The requisite file DOES exist...
+       */
+      if( chklink != "" )
+      {
+       /* ...but when "--if-linked is in effect, we must verify
+        * that the link target is matched, before...
+        */
+       var ref = WinShell.CreateShortcut( filename );
+       if( ref.TargetPath == chklink )
+         /*
+          * ...we may proceed with deletion.
+          */
+         FileSystem.DeleteFile( filename, force );
+      }
+      else
+       /* When "--if-linked" is NOT in effect, we may simply
+        * proceed with deletion, without further ado.
+        */
+       FileSystem.DeleteFile( filename, force );
+    }
+  }
+}
+
+/* $RCSfile$: end of file */
diff --git a/scripts/libexec/wsh.lua b/scripts/libexec/wsh.lua
new file mode 100644 (file)
index 0000000..0f1398c
--- /dev/null
@@ -0,0 +1,65 @@
+--
+-- wsh.lua
+--
+-- $Id$
+--
+-- Lua 5.2 module providing a simple API for invoking system services
+-- via the Microsoft Windows Scripting Host.
+--
+--
+-- This file is a component of mingw-get.
+--
+-- Written by Keith Marshall <keithmarshall@users.sourceforge.net>
+-- Copyright (C) 2012, MinGW Project
+--
+--
+-- Permission is hereby granted, free of charge, to any person obtaining a
+-- copy of this software and associated documentation files (the "Software"),
+-- to deal in the Software without restriction, including without limitation
+-- the rights to use, copy, modify, merge, publish, distribute, sublicense,
+-- and/or sell copies of the Software, and to permit persons to whom the
+-- Software is furnished to do so, subject to the following conditions:
+--
+-- The above copyright notice and this permission notice shall be included
+-- in all copies or substantial portions of the Software.
+--
+-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+-- OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+-- THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+-- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+-- DEALINGS IN THE SOFTWARE.
+--
+   local M = {}
+   local cscript = "cscript -nologo"
+--
+   function M.execute( ... )
+     local function wsh_prepare( interpreter, ... )
+       for argind, argval in ipairs {...}
+       do
+        interpreter = interpreter .. " " .. argval
+       end
+       return interpreter
+     end
+     os.execute( wsh_prepare( cscript, ... ) )
+   end
+--
+   function M.libexec_path( script, subsystem )
+     local script_path = os.getenv( "APPROOT" )
+     if script_path
+     then
+       script_path = script_path .. "libexec\\"
+     else
+       script_path = ".\\libexec\\"
+     end
+     if subsystem
+     then
+       script_path = script_path .. subsystem .. "\\"
+     end
+     return script_path .. script
+   end
+--
+   return M
+--
+-- $RCSfile$: end of file */
index 6d5ff62..b0e593e 100644 (file)
@@ -40,6 +40,8 @@
 
 #define EXIT_FATAL  EXIT_FAILURE + 1
 
+static const char *progname;
+
 wchar_t *AppPathNameW( const wchar_t *relpath )
 {
   /* UTF-16LE implementation; NOT thread safe...
@@ -218,6 +220,32 @@ static const char *help_text =
 "                    runtime prerequisites of, and in addition to,\n"
 "                    the nominated package\n"
 "\n"
+"  --desktop[=all-users]\n"
+"                    Enable the creation of desktop shortcuts, for\n"
+"                    packages which provide the capability via pre-\n"
+"                    or post-install scripts; the optional 'all-users'\n"
+"                    qualifier requests that all such shortcuts are\n"
+"                    to be made available to all users; without it\n"
+"                    shortcuts will be created for current user only\n"
+"\n"
+"                    Note that specification of this option does not\n"
+"                    guarantee that shortcuts will be created; the\n"
+"                    onus lies with individual package maintainers\n"
+"                    to provide scripting to support this capability\n"
+"\n"
+"  --start-menu[=all-users]\n"
+"                    Enable the creation of start menu shortcuts, for\n"
+"                    packages which provide the capability via pre-\n"
+"                    or post-install scripts; the optional 'all-users'\n"
+"                    qualifier requests that all such shortcuts are\n"
+"                    to be made available to all users; without it\n"
+"                    shortcuts will be created for current user only\n"
+"\n"
+"                    Note that specification of this option does not\n"
+"                    guarantee that shortcuts will be created; the\n"
+"                    onus lies with individual package maintainers\n"
+"                    to provide scripting to support this capability\n"
+"\n"
 "Actions:\n"
 "  update            Update local copy of repository catalogues\n"
 "  list, show        List and show details of available packages\n"
@@ -368,15 +396,66 @@ static int xatoi( const char *input )
   return result;
 }
 
+static void set_script_hook( const char *hook, const char *optarg )
+{
+  /* Helper function to initialise the environment variables which
+   * are associated with Lua scripting hooks, when the user specifies
+   * the appropriate activation options on the command line.
+   */
+  if( optarg != NULL )
+  {
+    /* An optional argument was assigned for the hook...
+     */
+    int arglen = strlen( optarg );
+    const char *all_users = "all-users";
+    const char *value_none = "none";
+    if( strncmp( optarg, all_users, arglen ) == 0 )
+    {
+      /* When this is the "all-users" qualifier, we append it to
+       * the value to be assigned to the environment variable.
+       */
+      const char *fmt = "%s --%s";
+      char tmp[1 + snprintf( NULL, 0, fmt, hook, all_users )];
+      snprintf( tmp, sizeof( tmp ), fmt, hook, all_users );
+      putenv( tmp );
+    }
+    else if( strncmp( optarg, value_none, arglen ) == 0 )
+    {
+      /* When it is the "none" qualifier, we remove any prior
+       * assignment to the respective environment variable.
+       *
+       * FIXME: to support assignment from within profile.xml,
+       * we will eventually need additional coding here, to
+       * override any profile.xml assignment.
+       */
+      char tmp[strlen( hook )];
+      char *p = tmp;
+      do { *p++ = *hook;
+        } while( *hook++ != '=' );
+      *p = '\0';
+      putenv( tmp );
+    }
+    else
+    { /* No other qualifier is supported; diagnose and ignore.
+       */
+      while( *hook++ != '=' ) /* advance pointer; no other action */;
+      fprintf( stderr,
+         "%s: *** WARNING *** invalid argument '%s' to option %s ignored\n",
+         progname, optarg, hook
+       );
+    }
+  }
+  else
+    /* No qualifying option argument specified; simply assign the
+     * hook variable value, as passed from the getopts() handler.
+     */
+    putenv( hook );
+}
+
 #define atmost( lim, val )             ((lim) < (val)) ? (lim) : (val)
 
 int main( int argc, char **argv )
 {
-  /* Make a note of...
-   */
-  const char *progname = basename( *argv );    /* ...this program's name    */
-  wchar_t *approot;                            /* and where it is installed */
-
   /* Provide storage for interpretation of any parsed command line options.
    * Note that we could also initialise them here, but then we would need to
    * give attention to the number of initialisers required; to save us that
@@ -384,6 +463,11 @@ int main( int argc, char **argv )
    */
   struct pkgopts parsed_options;
 
+  /* Make a note of this program's name, and where it's installed.
+   */
+  wchar_t *approot;
+  progname = basename( *argv );
+
   if( argc > 1 )
   {
     /* The user specified arguments on the command line...
@@ -407,6 +491,9 @@ int main( int argc, char **argv )
 
       { "all-related",    no_argument,         &optref,   OPTION_ALL_RELATED },
 
+      { "desktop",        optional_argument,   NULL,      'D'                },
+      { "start-menu",     optional_argument,   NULL,      'M'                },
+
 #     if DEBUG_ENABLED( DEBUG_TRACE_DYNAMIC )
        /* The "--trace" option is supported only when dynamic tracing
         * debugging support has been compiled in.
@@ -469,6 +556,20 @@ int main( int argc, char **argv )
            ++parsed_options.flags[OPTION_FLAGS].numeric;
          break;
 
+       case 'D':
+         /* This is a request to enable, or disable, the Lua scripting
+          * hook for installation of desktop shortcuts.
+          */
+         set_script_hook( "MINGW_GET_DESKTOP_HOOK=--desktop", optarg );
+         break;
+
+       case 'M':
+         /* This is a request to enable, or disable, the Lua scripting
+          * hook for installation of start menu shortcuts.
+          */
+         set_script_hook( "MINGW_GET_START_MENU_HOOK=--start-menu", optarg );
+         break;
+
        case OPTION_GENERIC:
          switch( optref & OPTION_STORAGE_CLASS )
          {