Enhanced gpg-conect-agent scripting.
authorWerner Koch <wk@gnupg.org>
Fri, 19 Oct 2007 14:51:39 +0000 (14:51 +0000)
committerWerner Koch <wk@gnupg.org>
Fri, 19 Oct 2007 14:51:39 +0000 (14:51 +0000)
Typo fixes in comments.

NEWS
common/xreadline.c
doc/ChangeLog
doc/tools.texi
tools/ChangeLog
tools/Makefile.am
tools/gpg-connect-agent.c
tools/no-libgcrypt.c

diff --git a/NEWS b/NEWS
index a729ff1..a0a2c10 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -5,6 +5,10 @@ Noteworthy changes in version 2.0.8
    taken into account.  This required a change of our the socket
    emulation code; thus old GnuPG modules can't be used anymore.
 
+ * Fixed a crash in gpgconf.
+
+ * Enhanced gpg-connect-agent with a small scripting language.
+
 
 Noteworthy changes in version 2.0.7 (2007-09-10)
 ------------------------------------------------
index 2b640b3..ab43c29 100644 (file)
@@ -33,7 +33,7 @@
    maximum allowed allocation.  
 
    Returns the length of the line. EOF is indicated by a line of
-   length zero. A truncated line is indicated my setting the value at
+   length zero. A truncated line is indicated by setting the value at
    MAX_LENGTH to 0.  If the returned value is less then 0 not enough
    memory was enable and ERRNO is set accordingly.
 
index d083158..096e8eb 100644 (file)
@@ -1,3 +1,7 @@
+2007-10-19  Werner Koch  <wk@g10code.com>
+
+       * tools.texi (Controlling gpg-connect-agent): Updated.
+
 2007-08-29  Werner Koch  <wk@g10code.com>
 
        * tools.texi (Checking programs): New.
index 902f080..d240aca 100644 (file)
@@ -1045,6 +1045,16 @@ When using @option{-S} or @option{--exec}, @command{gpg-connect-agent}
 connects to the assuan server in extended mode to allow descriptor
 passing.  This option makes it use the old mode.
 
+@item --run @var{file}
+@opindex run 
+Run the commands from @var{file} at startup and then continue with the
+regular input method.
+
+@item -s
+@itemx --subst
+@opindex subst
+Run the command @code{/subst} at startup.
+
 @item --hex
 @opindex hex
 Print data lines in a hex format and the ASCII representation of
@@ -1065,20 +1075,85 @@ While reading Assuan commands, gpg-agent also allows a few special
 commands to control its operation.  These control commands all start
 with a slash (@code{/}).
 
-
 @table @code
 
 @item /echo @var{args}
 Just print @var{args}.
 
-@item /definqfile @var{name} @var{file}
+@item /let @var{name} @var{value}
+Set the variable @var{name} to @var{value}.  Variables are only
+substituted on the input if the @command{/subst} has been used.
+Variables are referenced by prefixing the name with a dollr sign and
+optionally include the name in curly braces.  The rules for a valid name
+are idnetically to those of the standard bourne shell.  This is not yet
+enforced but may be in the future.  When used with curly braces no
+leading or trailing white space is allowed. 
+
+If a variable is not found, it is searched in the environment and if
+found copied to the table of variables.
+
+Variable functions are available: The name of the function must be
+followed by at least one space and the at least one argument.  The
+following functions are available:
+
+@table @code
+@item get
+Return a value described by the argument.  Available arguments are:
+
+@table @code    
+@item cwd
+The current working directory.
+@item homedir
+The gnupg homedir.
+@item sysconfdir
+GnuPG's system configuration directory.
+@item bindir
+GnuPG's binary directory.
+@item libdir
+GnuPG's library directory.
+@item libexecdir
+GnuPG's library directory for executable files.
+@item datadir
+GnuPG's data directory.
+@item serverpid
+The PID of the current server. Command @command{/serverpid} must
+have been given to return a useful value.
+@end table
+
+@item unescape @var{args}
+Remove C-style escapes from @var{args}.  Note that @code{\0} and
+@code{\x00} terminate the returned string implictly.  The string to be
+converted are the entire arguments right behind the delimiting space of
+the function name.
+
+@item unpercent @var{args}
+@itemx unpercent+ @var{args}
+Remove percent style ecaping from @var{args}.  Note that @code{%00}
+terminates the string implicitly.  The string to be converted are the
+entire arguments right behind the delimiting space of the function
+name. @code{unpercent+} also maps plus signs to a spaces.
+
+@item percent @var{args}
+@item percent+ @var{args}
+Escape the @var{args} using percent style ecaping.  Tabs, formfeeds,
+linefeeds, carriage returns and colons are escaped. @code{percent+} also
+maps spaces to plus signs.
+
+@end table
 
+
+@item /definq @var{name} @var{var}
+Use content of the variable @var{var} for inquiries with @var{name}.
+@var{name} may be an asterisk (@code{*}) to match any inquiry.
+
+
+@item /definqfile @var{name} @var{file}
 Use content of @var{file} for inquiries with @var{name}.
-@var{name} may be an asterisk (@code{*} to match any inquiry.
+@var{name} may be an asterisk (@code{*}) to match any inquiry.
 
 @item /definqprog @var{name} @var{prog}
 Run @var{prog} for inquiries matching @var{name} and pass the
-entire line to it as command line arguments
+entire line to it as command line arguments.
 
 @item /showdef
 Print all definitions
@@ -1095,6 +1170,21 @@ input source for other commands.
 @item /recvfd
 Not yet implemented.
 
+@item /open @var{var} @var{file} [@var{mode}]
+Open @var{file} and assign the file descriptor to @var{var}.  Warning:
+This command is experimental and might change in future versions.
+
+@item /close @var{fd}
+Close the file descriptor @var{fd}.  Warning: This command is
+experimental and might change in future versions.
+
+@item /showopen
+Show a listy of open files.
+
+@item /serverpid
+Send the Assuan command @command{GETINFO pid} to the server and store
+the returned PID for internal purposes.
+
 @item /hex
 @itemx /nohex
 Same as the command line option @option{--hex}.
@@ -1103,6 +1193,17 @@ Same as the command line option @option{--hex}.
 @itemx /nodecode
 Same as the command line option @option{--decode}.
 
+@item /subst
+@itemx /nosubst
+Enable and disable variable substitution.  It defaults to disabled
+unless the command line option @option{--subst} has been used.
+
+@item /run @var{file}
+Run commands from @var{file}.
+
+@item /bye
+Terminate the connection and the program
+
 @item /help
 Print a list of available control commands.
 
index c42e75c..118a0fe 100644 (file)
@@ -1,5 +1,18 @@
 2007-10-19  Werner Koch  <wk@g10code.com>
 
+       * gpg-connect-agent.c (get_var_ext): New.
+       (substitute_line): Use it.
+       (assign_variable): Implement /slet in terms of get_var_ext.
+       (main): New option -s/--subst.
+       (add_definq): Add arg IS_VAR.  Change all callers.
+       (main): Add command /definq.
+       (handle_inquire): Implement new command.
+       (substitute_line_copy): New.
+       (unescape_string, unpercent_string): New.
+       * no-libgcrypt.c (gcry_set_outofcore_handler) 
+       (gcry_set_fatalerror_handler, gcry_set_log_handler): New.
+       * Makefile.am (gpg_connect_agent_LDADD): Link to libreadline.
+
        * gpgconf-comp.c (retrieve_options_from_file): Don't call fclose
        with NULL.  Fixes bug 842.
 
index a9004a7..e473923 100644 (file)
@@ -80,7 +80,9 @@ watchgnupg_LDADD = $(NETLIBS)
 gpg_connect_agent_SOURCES = gpg-connect-agent.c no-libgcrypt.c
 # FIXME: remove PTH_LIBS (why do we need them at all?)
 gpg_connect_agent_LDADD = $(common_libs) $(LIBASSUAN_LIBS) $(PTH_LIBS) \
-                         $(GPG_ERROR_LIBS) $(LIBINTL) $(NETLIBS) $(LIBICONV)
+                         $(GPG_ERROR_LIBS) \
+                          ../common/libgpgrl.a $(LIBREADLINE) \
+                          $(LIBINTL) $(NETLIBS) $(LIBICONV)    
 
 gpgkey2ssh_SOURCES = gpgkey2ssh.c
 gpgkey2ssh_CFLAGS = $(LIBGCRYPT_CFLAGS) $(GPG_ERROR_CFLAGS)
@@ -99,5 +101,5 @@ endif
 
 # Make sure that all libs are build before we use them.  This is
 # important for things like make -j2.
-$(PROGRAMS): $(common_libs) $(pwquery_libs)
+$(PROGRAMS): $(common_libs) $(pwquery_libs) ../common/libgpgrl.a
 
index cb3f063..197172e 100644 (file)
 #include <errno.h>
 #include <ctype.h>
 #include <assuan.h>
+#include <unistd.h>
 
 #include "i18n.h"
 #include "../common/util.h"
 #include "../common/asshelp.h"
 #include "../common/sysutils.h"
 #include "../common/membuf.h"
+#include "../common/ttyio.h"
+
+#define CONTROL_D ('D' - 'A' + 1)
+#define octdigitp(p) (*(p) >= '0' && *(p) <= '7')
 
 /* Constants to identify the commands and options. */
 enum cmd_and_opt_values
@@ -41,6 +46,7 @@ enum cmd_and_opt_values
     oRawSocket  = 'S',
     oExec       = 'E',
     oRun        = 'r',
+    oSubst      = 's',
 
     oNoVerbose = 500,
     oHomedir,
@@ -65,6 +71,7 @@ static ARGPARSE_OPTS opts[] =
     { oNoExtConnect, "no-ext-connect",
                             0, N_("do not use extended connect mode")},
     { oRun,  "run", 2,         N_("|FILE|run commands from FILE on startup")},
+    { oSubst, "subst", 0,      N_("run /subst on startup")}, 
     /* hidden options */
     { oNoVerbose, "no-verbose",  0, "@"},
     { oHomedir, "homedir", 2, "@" },   
@@ -94,7 +101,8 @@ struct definq_s
 {
   struct definq_s *next;
   char *name;     /* Name of inquiry or NULL for any name. */
-  int is_prog;     /* True if this is a program to run. */
+  int is_var;     /* True if FILE is a variable name. */
+  int is_prog;    /* True if FILE is a program to run. */
   char file[1];   /* Name of file or program. */
 };
 typedef struct definq_s *definq_t;
@@ -129,6 +137,7 @@ static struct
 
 
 /*-- local prototypes --*/
+static char *substitute_line_copy (const char *buffer);
 static int read_and_print_response (assuan_context_t ctx, int *r_goterr);
 static assuan_context_t start_agent (void);
 
@@ -185,6 +194,145 @@ gnu_getcwd (void)
 }
 
 
+/* Unescale STRING and returned the malloced result.  The surrounding
+   quotes must already be removed from STRING.  */
+static char *
+unescape_string (const char *string)
+{
+  const unsigned char *s;
+  int esc;
+  size_t n;
+  char *buffer;
+  unsigned char *d;
+
+  n = 0;
+  for (s = (const unsigned char*)string, esc=0; *s; s++)
+    {
+      if (esc)
+        {
+          switch (*s)
+            {
+            case 'b':  
+            case 't':  
+            case 'v':  
+            case 'n':  
+            case 'f':  
+            case 'r':  
+            case '"':  
+            case '\'': 
+            case '\\': n++; break;
+            case 'x': 
+              if (s[1] && s[2] && hexdigitp (s+1) && hexdigitp (s+2))
+                n++;
+              break;
+
+            default:
+              if (s[1] && s[2] 
+                  && octdigitp (s) && octdigitp (s+1) && octdigitp (s+2))
+                n++;
+              break;
+           }
+          esc = 0;
+        }
+      else if (*s == '\\')
+        esc = 1;
+      else
+        n++;
+    } 
+
+  buffer = xmalloc (n+1);
+  d = (unsigned char*)buffer;
+  for (s = (const unsigned char*)string, esc=0; *s; s++)
+    {
+      if (esc)
+        {
+          switch (*s)
+            {
+            case 'b':  *d++ = '\b'; break;
+            case 't':  *d++ = '\t'; break;
+            case 'v':  *d++ = '\v'; break;
+            case 'n':  *d++ = '\n'; break;
+            case 'f':  *d++ = '\f'; break;
+            case 'r':  *d++ = '\r'; break;
+            case '"':  *d++ = '\"'; break;
+            case '\'': *d++ = '\''; break;
+            case '\\': *d++ = '\\'; break;
+            case 'x': 
+              if (s[1] && s[2] && hexdigitp (s+1) && hexdigitp (s+2))
+                {
+                  s++;
+                  *d++ = xtoi_2 (s);
+                  s++;
+                }
+              break;
+
+            default:
+              if (s[1] && s[2] 
+                  && octdigitp (s) && octdigitp (s+1) && octdigitp (s+2))
+                {
+                  *d++ = (atoi_1 (s)*64) + (atoi_1 (s+1)*8) + atoi_1 (s+2);
+                  s += 2;
+                }
+              break;
+           }
+          esc = 0;
+        }
+      else if (*s == '\\')
+        esc = 1;
+      else
+        *d++ = *s;
+    } 
+  *d = 0;
+  return buffer;
+}
+
+
+/* Do the percent unescaping and return a newly malloced string.
+   If WITH_PLUS is set '+' characters will be changed to space. */
+static char *
+unpercent_string (const char *string, int with_plus)
+{
+  const unsigned char *s;
+  unsigned char *buffer, *p;
+  size_t n;
+
+  n = 0;
+  for (s=(const unsigned char *)string; *s; s++)
+    {
+      if (*s == '%' && s[1] && s[2])
+        { 
+          s++;
+          n++;
+          s++;
+        }
+      else if (with_plus && *s == '+')
+        n++;
+      else
+        n++;
+    }
+
+  buffer = xmalloc (n+1);
+  p = buffer;
+  for (s=(const unsigned char *)string; *s; s++)
+    {
+      if (*s == '%' && s[1] && s[2])
+        { 
+          s++;
+          *p++ = xtoi_2 (s);
+          s++;
+        }
+      else if (with_plus && *s == '+')
+        *p++ = ' ';
+      else
+        *p++ = *s;
+    }
+  *p = 0;
+  return (char*)buffer;
+}
+
+
+
+
 \f
 static const char *
 set_var (const char *name, const char *value)
@@ -241,6 +389,145 @@ get_var (const char *name)
 }
 
 
+/* Extended version of get_var.  This returns a malloced string and
+   understand the fucntion syntax: "func args". 
+
+   Defined functions are
+   
+     get - Return a value described by the next argument:
+           cwd        - The current working directory.
+           homedir    - The gnupg homedir.
+           sysconfdir - GnuPG's system configuration directory.
+           bindir     - GnuPG's binary directory.
+           libdir     - GnuPG's library directory.
+           libexecdir - GnuPG's library directory for executable files.
+           datadir    - GnuPG's data directory.
+           serverpid  - The PID of the current server.
+
+     unescape ARGS
+           Remove C-style escapes from string.  Note that "\0" and
+           "\x00" terminate the string implictly.  Use "\x7d" to
+           represent the closing brace.  The args start right after
+           the first space after the function name.
+
+     unpercent ARGS
+     unpercent+ ARGS
+           Remove percent style ecaping from string.  NOte that "%00
+           terminates the string implicitly.  Use "%7d" to represetn
+           the closing brace.  The args start right after the first
+           space after the function name.  "unpercent+" also maps '+'
+           to space.
+
+     percent ARGS
+     percent+ ARGS
+           Escape the args using the percent style.  Tabs, formfeeds,
+           linefeeds and carriage returns are also escaped.
+           "percent+" also maps spaces to plus characters.
+
+   Example: get_var_ext ("get sysconfdir") -> "/etc/gnupg"
+    
+  */
+static char *
+get_var_ext (const char *name)
+{
+  static int recursion_count;
+  const char *s;
+  char *result;
+  char *p;
+  char *free_me = NULL;
+
+  if (recursion_count > 50)
+    {
+      log_error ("variables nested too deeply\n");
+      return NULL;
+    }
+
+  recursion_count++;
+  free_me = opt.enable_varsubst? substitute_line_copy (name) : NULL;
+  if (free_me)
+    name = free_me;
+  for (s=name; *s && !spacep (s); s++)
+    ;
+  if (!*s)
+    {
+      s = get_var (name);
+      result = s? xstrdup (s): NULL;
+    }
+  else if ( (s - name) == 3 && !strncmp (name, "get", 3))
+    {
+      while ( spacep (s) )
+        s++;
+      if (!strcmp (s, "cwd"))
+        {
+          result = gnu_getcwd ();
+          if (!result)
+            log_error ("getcwd failed: %s\n", strerror (errno));
+        }
+      else if (!strcmp (s, "homedir"))
+        result = make_filename (opt.homedir, NULL);
+      else if (!strcmp (s, "sysconfdir"))
+        result = xstrdup (gnupg_sysconfdir ());
+      else if (!strcmp (s, "bindir"))
+        result = xstrdup (gnupg_bindir ());
+      else if (!strcmp (s, "libdir"))
+        result = xstrdup (gnupg_libdir ());
+      else if (!strcmp (s, "libexecdir"))
+        result = xstrdup (gnupg_libexecdir ());
+      else if (!strcmp (s, "datadir"))
+        result = xstrdup (gnupg_datadir ());
+      else if (!strcmp (s, "serverpid"))
+        {
+          char numbuf[30];
+          snprintf (numbuf, sizeof numbuf, "%d", (int)server_pid);
+          result = xstrdup (numbuf);
+        }
+      else
+        {
+          log_error ("invalid argument `%s' for variable function `get'\n", s);
+          log_info  ("valid are: cwd, "
+                     "{home,bin,lib,libexec,data}dir, serverpid\n");
+          result = NULL;
+        }
+    }
+  else if ( (s - name) == 8 && !strncmp (name, "unescape", 8))
+    {
+      s++;
+      result = unescape_string (s);
+    }
+  else if ( (s - name) == 9 && !strncmp (name, "unpercent", 9))
+    {
+      s++;
+      result = unpercent_string (s, 0);
+    }
+  else if ( (s - name) == 10 && !strncmp (name, "unpercent+", 10))
+    {
+      s++;
+      result = unpercent_string (s, 1);
+    }
+  else if ( (s - name) == 7 && !strncmp (name, "percent", 7))
+    {
+      s++;
+      result = percent_escape (s, "\t\r\n\f\v");
+    }
+  else if ( (s - name) == 8 && !strncmp (name, "percent+", 8))
+    {
+      s++;
+      result = percent_escape (s, "\t\r\n\f\v");
+      for (p=result; *p; p++)
+        if (*p == ' ')
+          *p = '+';
+    }
+  else
+    {
+      log_error ("unknown variable function `%.*s'\n", (int)(s-name), name);
+      result = NULL;
+    }
+    
+  xfree (free_me);
+  recursion_count--;
+  return result;
+}
+
 
 /* Substitute variables in LINE and return a new allocated buffer if
    required.  The function might modify LINE if the expanded version
@@ -253,6 +540,7 @@ substitute_line (char *buffer)
   const char *value;
   size_t valuelen, n;
   char *result = NULL;
+  char *freeme = NULL;
 
   while (*line)
     {
@@ -268,8 +556,18 @@ substitute_line (char *buffer)
         }
       if (p[1] == '{')
         {
-          for (pend=p+2; *pend && *pend != '}' ; pend++)
-            ;
+          int count = 0;
+
+          for (pend=p+2; *pend; pend++)
+            {
+              if (*pend == '{')
+                count++;
+              else if (*pend == '}')
+                {
+                  if (--count < 0)
+                    break;
+                }
+            }
           if (!*pend)
             return result; /* Unclosed - don't substitute.  */
         }
@@ -281,7 +579,8 @@ substitute_line (char *buffer)
       if (p[1] == '{' && *pend == '}')
         {
           *pend++ = 0;
-          value = get_var (p+2);
+          freeme = get_var_ext (p+2);
+          value = freeme;
         }
       else if (*pend)
         {
@@ -319,16 +618,32 @@ substitute_line (char *buffer)
           free (result);
           result = dst;
         }
+      xfree (freeme);
+      freeme = NULL;
     }
   return result;
 }
 
+/* Same as substitute_line but do not modify BUFFER.  */
+static char *
+substitute_line_copy (const char *buffer)
+{
+  char *result, *p;
+  
+  p = xstrdup (buffer?buffer:"");
+  result = substitute_line (p);
+  if (!result)
+    result = p;
+  else
+    xfree (p);
+  return result;
+}
 
 
 static void
 assign_variable (char *line, int syslet)
 {
-  char *name, *p, *tmp, *free_me;
+  char *name, *p, *tmp, *free_me, *buffer;
 
   /* Get the  name. */
   name = line;
@@ -343,42 +658,20 @@ assign_variable (char *line, int syslet)
     set_var (name, NULL); /* Remove variable.  */ 
   else if (syslet)
     {
-      free_me = opt.enable_varsubst? substitute_line (p) : NULL;
+      free_me = opt.enable_varsubst? substitute_line_copy (p) : NULL;
       if (free_me)
         p = free_me;
-      if (!strcmp (p, "cwd"))
-        {
-          tmp = gnu_getcwd ();
-          if (!tmp)
-            log_error ("getcwd failed: %s\n", strerror (errno));
-          set_var (name, tmp);
-          xfree (tmp);
-        }
-      else if (!strcmp (p, "homedir"))
-        set_var (name, opt.homedir);
-      else if (!strcmp (p, "sysconfdir"))
-        set_var (name, gnupg_sysconfdir ());
-      else if (!strcmp (p, "bindir"))
-        set_var (name, gnupg_bindir ());
-      else if (!strcmp (p, "libdir"))
-        set_var (name, gnupg_libdir ());
-      else if (!strcmp (p, "libexecdir"))
-        set_var (name, gnupg_libexecdir ());
-      else if (!strcmp (p, "datadir"))
-        set_var (name, gnupg_datadir ());
-      else if (!strcmp (p, "serverpid"))
-        set_int_var (name, (int)server_pid);
-      else
-        {
-          log_error ("undefined tag `%s'\n", p);
-          log_info  ("valid tags are: cwd, {home,bin,lib,libexec,data}dir, "
-                     "serverpid\n");
-        }
+      buffer = xmalloc (4 + strlen (p) + 1);
+      strcpy (stpcpy (buffer, "get "), p);
+      tmp = get_var_ext (buffer);
+      xfree (buffer);
+      set_var (name, tmp);
+      xfree (tmp);
       xfree (free_me);
     }
   else 
     {
-      tmp = opt.enable_varsubst? substitute_line (p) : NULL;
+      tmp = opt.enable_varsubst? substitute_line_copy (p) : NULL;
       if (tmp)
         {
           set_var (name, tmp);
@@ -405,7 +698,7 @@ show_variables (void)
    change the content of LINE.  We assume that leading white spaces
    are already removed. */
 static void
-add_definq (char *line, int is_prog)
+add_definq (char *line, int is_var, int is_prog)
 {
   definq_t d;
   char *name, *p;
@@ -421,6 +714,7 @@ add_definq (char *line, int is_prog)
 
   d = xmalloc (sizeof *d + strlen (p) );
   strcpy (d->file, p);
+  d->is_var  = is_var;
   d->is_prog = is_prog;
   if ( !strcmp (name, "*"))
     d->name = NULL;
@@ -441,10 +735,12 @@ show_definq (void)
 
   for (d=definq_list; d; d = d->next)
     if (d->name)
-      printf ("%-20s %c %s\n", d->name, d->is_prog? 'p':'f', d->file);
+      printf ("%-20s %c %s\n", 
+              d->name, d->is_var? 'v' : d->is_prog? 'p':'f', d->file);
   for (d=definq_list; d; d = d->next)
     if (!d->name)
-      printf ("%-20s %c %s\n", "*", d->is_prog? 'p':'f', d->file);
+      printf ("%-20s %c %s\n", "*", 
+              d->is_var? 'v': d->is_prog? 'p':'f', d->file);
 }
 
 
@@ -727,7 +1023,10 @@ main (int argc, char **argv)
   int cmderr;
   const char *opt_run = NULL;
   FILE *script_fp = NULL;
+  int use_tty, last_was_tty;
 
+
+  gnupg_rl_initialize ();
   set_strusage (my_strusage);
   log_set_prefix ("gpg-connect-agent", 1);
 
@@ -759,6 +1058,7 @@ main (int argc, char **argv)
         case oExec:      opt.exec = 1; break;
         case oNoExtConnect: opt.connect_flags &= ~(1); break;
         case oRun:       opt_run = pargs.r.ret_str; break;
+        case oSubst:     opt.enable_varsubst = 1; break;
 
         default: pargs.err = 2; break;
        }
@@ -767,6 +1067,8 @@ main (int argc, char **argv)
   if (log_get_errorcount (0))
     exit (2);
 
+  use_tty = (isatty ( fileno (stdin)) && isatty (fileno (stdout)));
+
   if (opt.exec)
     {
       if (!argc)
@@ -841,13 +1143,35 @@ main (int argc, char **argv)
  
   line = NULL;
   linesize = 0;
+  last_was_tty = 0;
   for (;;)
     {
       int n;
       size_t maxlength;
 
       maxlength = 2048;
-      n = read_line (script_fp? script_fp:stdin, &line, &linesize, &maxlength);
+      if (use_tty && !script_fp)
+        {
+          last_was_tty = 1;
+          line = tty_get ("> ");
+          n = strlen (line);
+          if (n==1 && *line == CONTROL_D)
+            n = 0;
+          if (n >= maxlength)
+            maxlength = 0;
+        }
+      else
+        {
+          if (last_was_tty)
+            {
+              xfree (line);
+              line = NULL;
+              linesize = 0;
+              last_was_tty = 0;
+            }
+          n = read_line (script_fp? script_fp:stdin, 
+                         &line, &linesize, &maxlength);
+        }
       if (n < 0)
         {
           log_error (_("error reading input: %s\n"), strerror (errno));
@@ -899,19 +1223,45 @@ main (int argc, char **argv)
             }
           else if (!strcmp (cmd, "slet"))
             {
+              /* Deprecated - never used in a released version.  */
               assign_variable (p, 1);
             }
           else if (!strcmp (cmd, "showvar"))
             {
               show_variables ();
             }
+          else if (!strcmp (cmd, "definq"))
+            {
+              tmpline = opt.enable_varsubst? substitute_line (p) : NULL;
+              if (tmpline)
+                {
+                  add_definq (tmpline, 1, 0);
+                  xfree (tmpline);
+                }
+              else
+                add_definq (p, 1, 0);
+            }
           else if (!strcmp (cmd, "definqfile"))
             {
-              add_definq (p, 0);
+              tmpline = opt.enable_varsubst? substitute_line (p) : NULL;
+              if (tmpline)
+                {
+                  add_definq (tmpline, 0, 0);
+                  xfree (tmpline);
+                }
+              else
+                add_definq (p, 0, 0);
             }
           else if (!strcmp (cmd, "definqprog"))
             {
-              add_definq (p, 1);
+              tmpline = opt.enable_varsubst? substitute_line (p) : NULL;
+              if (tmpline)
+                {
+                  add_definq (tmpline, 0, 1);
+                  xfree (tmpline);
+                }
+              else
+                add_definq (p, 0, 1);
             }
           else if (!strcmp (cmd, "showdef"))
             {
@@ -934,21 +1284,49 @@ main (int argc, char **argv)
             }
           else if (!strcmp (cmd, "sendfd"))
             {
-              do_sendfd (ctx, p);
+              tmpline = opt.enable_varsubst? substitute_line (p) : NULL;
+              if (tmpline)
+                {
+                  do_sendfd (ctx, tmpline);
+                  xfree (tmpline);
+                }
+              else
+                do_sendfd (ctx, p);
               continue;
             }
           else if (!strcmp (cmd, "recvfd"))
             {
-              do_recvfd (ctx, p);
+              tmpline = opt.enable_varsubst? substitute_line (p) : NULL;
+              if (tmpline)
+                {
+                  do_recvfd (ctx, tmpline);
+                  xfree (tmpline);
+                }
+              else
+                do_recvfd (ctx, p);
               continue;
             }
           else if (!strcmp (cmd, "open"))
             {
-              do_open (p);
+              tmpline = opt.enable_varsubst? substitute_line (p) : NULL;
+              if (tmpline)
+                {
+                  do_open (tmpline);
+                  xfree (tmpline);
+                }
+              else
+                do_open (p);
             }
           else if (!strcmp (cmd, "close"))
             {
-              do_close (p);
+              tmpline = opt.enable_varsubst? substitute_line (p) : NULL;
+              if (tmpline)
+                {
+                  do_close (tmpline);
+                  xfree (tmpline);
+                }
+              else
+                do_close (p);
             }
           else if (!strcmp (cmd, "showopen"))
             {
@@ -1012,19 +1390,15 @@ main (int argc, char **argv)
 "Available commands:\n"
 "/echo ARGS             Echo ARGS.\n"
 "/let  NAME VALUE       Set variable NAME to VALUE.\n"
-"/slet NAME TAG         Set variable NAME to the value described by TAG.\n" 
 "/showvar               Show all variables.\n"
-"/definqfile NAME FILE\n"
-"    Use content of FILE for inquiries with NAME.\n"
-"    NAME may be \"*\" to match any inquiry.\n"
-"/definqprog NAME PGM\n"
-"    Run PGM for inquiries matching NAME and pass the\n"
-"    entire line to it as arguments.\n"
+"/definq NAME VAR       Use content of VAR for inquiries with NAME.\n"
+"/definqfile NAME FILE  Use content of FILE for inquiries with NAME.\n"
+"/definqprog NAME PGM   Run PGM for inquiries with NAME.\n"
 "/showdef               Print all definitions.\n"
 "/cleardef              Delete all definitions.\n"
 "/sendfd FILE MODE      Open FILE and pass descriptor to server.\n"
 "/recvfd                Receive FD from server and print.\n"
-"/open VAR FILE MODE    Open FILE and assign the descrptor to VAR.\n" 
+"/open VAR FILE MODE    Open FILE and assign the file descriptor to VAR.\n" 
 "/close FD              Close file with descriptor FD.\n"
 "/showopen              Show descriptors of all open files.\n"
 "/serverpid             Retrieve the pid of the server.\n"
@@ -1094,7 +1468,7 @@ handle_inquire (assuan_context_t ctx, char *line)
 {
   const char *name;
   definq_t d;
-  FILE *fp;
+  FILE *fp = NULL;
   char buffer[1024];
   int rc, n;
 
@@ -1126,43 +1500,58 @@ handle_inquire (assuan_context_t ctx, char *line)
       return 0;
     }
 
-  if (d->is_prog)
+  if (d->is_var)
     {
-      fp = popen (d->file, "r");
-      if (!fp)
-        log_error ("error executing `%s': %s\n", d->file, strerror (errno));
-      else if (opt.verbose)
-        log_error ("handling inquiry `%s' by running `%s'\n", name, d->file);
+      char *tmpvalue = get_var_ext (d->file);
+      rc = assuan_send_data (ctx, tmpvalue, strlen (tmpvalue));
+      xfree (tmpvalue);
+      if (rc)
+        log_error ("sending data back failed: %s\n", gpg_strerror (rc) );
     }
   else
     {
-      fp = fopen (d->file, "rb");
+      if (d->is_prog)
+        {
+          fp = popen (d->file, "r");
+          if (!fp)
+            log_error ("error executing `%s': %s\n",
+                       d->file, strerror (errno));
+          else if (opt.verbose)
+            log_error ("handling inquiry `%s' by running `%s'\n", 
+                       name, d->file);
+        }
+      else
+        {
+          fp = fopen (d->file, "rb");
+          if (!fp)
+            log_error ("error opening `%s': %s\n", d->file, strerror (errno));
+          else if (opt.verbose)
+            log_error ("handling inquiry `%s' by returning content of `%s'\n",
+                       name, d->file);
+        }
       if (!fp)
-        log_error ("error opening `%s': %s\n", d->file, strerror (errno));
-      else if (opt.verbose)
-        log_error ("handling inquiry `%s' by returning content of `%s'\n",
-                   name, d->file);
-    }
-  if (!fp)
-    return 0;
+        return 0;
 
-  while ( (n = fread (buffer, 1, sizeof buffer, fp)) )
-    {
-      rc = assuan_send_data (ctx, buffer, n);
-      if (rc)
+      while ( (n = fread (buffer, 1, sizeof buffer, fp)) )
         {
-          log_error ("sending data back failed: %s\n", gpg_strerror (rc) );
-          break;
+          rc = assuan_send_data (ctx, buffer, n);
+          if (rc)
+            {
+              log_error ("sending data back failed: %s\n", gpg_strerror (rc) );
+              break;
+            }
         }
+      if (ferror (fp))
+        log_error ("error reading from `%s': %s\n", d->file, strerror (errno));
     }
-  if (ferror (fp))
-    log_error ("error reading from `%s': %s\n", d->file, strerror (errno));
 
   rc = assuan_send_data (ctx, NULL, 0);
   if (rc)
     log_error ("sending data back failed: %s\n", gpg_strerror (rc) );
 
-  if (d->is_prog)
+  if (d->is_var)
+    ;
+  else if (d->is_prog)
     {
       if (pclose (fp))
         log_error ("error running `%s': %s\n", d->file, strerror (errno));
index a21ccdb..009fd95 100644 (file)
@@ -117,3 +117,24 @@ gcry_control (enum gcry_ctl_cmds CMD, ...)
 {
   return 0;
 }
+
+void 
+gcry_set_outofcore_handler (gcry_handler_no_mem_t h, void *opaque)
+{
+  (void)h;
+  (void)opaque;
+}
+
+void 
+gcry_set_fatalerror_handler (gcry_handler_error_t fnc, void *opaque)
+{
+  (void)fnc;
+  (void)opaque;
+}
+
+void 
+gcry_set_log_handler (gcry_handler_log_t f, void *opaque)
+{
+  (void)f;
+  (void)opaque;
+}