summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWerner Koch <wk@gnupg.org>2007-10-24 17:34:23 +0200
committerWerner Koch <wk@gnupg.org>2007-10-24 17:34:23 +0200
commiteda26e299f0b8d8a22503756d033386cc29780d0 (patch)
tree56d9df6065be65fb4c42963c9898ed3fe2319677
parentChanged wording of passphrase checking messages. (diff)
downloadgnupg2-eda26e299f0b8d8a22503756d033386cc29780d0.tar.xz
gnupg2-eda26e299f0b8d8a22503756d033386cc29780d0.zip
Add a /while loop.
-rw-r--r--doc/tools.texi34
-rw-r--r--tools/ChangeLog1
-rw-r--r--tools/Makefile.am7
-rw-r--r--tools/gpg-connect-agent.c228
4 files changed, 253 insertions, 17 deletions
diff --git a/doc/tools.texi b/doc/tools.texi
index c65de93c7..c0e897bcd 100644
--- a/doc/tools.texi
+++ b/doc/tools.texi
@@ -1197,11 +1197,20 @@ 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}
+@itemx 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.
+@item +
+@itemx -
+@item *
+@item /
+@item %
+Evaluate all arguments as long integers using @code{strtol} and apply
+this operator. A division by zero yields an empty string.
+
+
@end table
@@ -1248,6 +1257,9 @@ Show a listy of open files.
Send the Assuan command @command{GETINFO pid} to the server and store
the returned PID for internal purposes.
+@item /sleep
+Sleep for a second.
+
@item /hex
@itemx /nohex
Same as the command line option @option{--hex}.
@@ -1260,6 +1272,26 @@ Same as the command line option @option{--decode}.
@itemx /nosubst
Enable and disable variable substitution. It defaults to disabled
unless the command line option @option{--subst} has been used.
+If /subst as been enabled once, leading white spaces are removed from
+input lines which makes scripts easier to read.
+
+@item /while @var{condition}
+@itemx /end
+These commands provide a way for executing loops. All lines between the
+@code{while} and the corresponding @code{end} are executed as long as
+the evaluation of @var{condition} yields a non-zero value. The
+evaluation is done by passing @var{condition} to the @code{strtol}
+function. Example:
+
+@smallexample
+ /subst
+ /let i 3
+ /while $i
+ /echo loop couter is $i
+ /let i $@{- $i 1@}
+ /end
+@end smallexample
+
@item /run @var{file}
Run commands from @var{file}.
diff --git a/tools/ChangeLog b/tools/ChangeLog
index a032c06af..1d755a598 100644
--- a/tools/ChangeLog
+++ b/tools/ChangeLog
@@ -2,6 +2,7 @@
* gpg-connect-agent.c (substitute_line): Restore temporary nul
marker.
+ (main): Add /while command.
2007-10-23 Werner Koch <wk@g10code.com>
diff --git a/tools/Makefile.am b/tools/Makefile.am
index e47392300..2a63fa558 100644
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -79,10 +79,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) \
- ../common/libgpgrl.a $(LIBREADLINE) \
- $(LIBINTL) $(NETLIBS) $(LIBICONV)
+gpg_connect_agent_LDADD = ../common/libgpgrl.a $(common_libs) \
+ $(LIBASSUAN_LIBS) $(PTH_LIBS) $(GPG_ERROR_LIBS) \
+ $(LIBREADLINE) $(LIBINTL) $(NETLIBS) $(LIBICONV)
gpgkey2ssh_SOURCES = gpgkey2ssh.c
gpgkey2ssh_CFLAGS = $(LIBGCRYPT_CFLAGS) $(GPG_ERROR_CFLAGS)
diff --git a/tools/gpg-connect-agent.c b/tools/gpg-connect-agent.c
index ae931e791..1af1e6c93 100644
--- a/tools/gpg-connect-agent.c
+++ b/tools/gpg-connect-agent.c
@@ -26,6 +26,7 @@
#include <ctype.h>
#include <assuan.h>
#include <unistd.h>
+#include <assert.h>
#include "i18n.h"
#include "../common/util.h"
@@ -91,6 +92,7 @@ struct
int exec; /* Run the pgm given on the command line. */
unsigned int connect_flags; /* Flags used for connecting. */
int enable_varsubst; /* Set if variable substitution is enabled. */
+ int trim_leading_spaces;
} opt;
@@ -122,6 +124,16 @@ typedef struct variable_s *variable_t;
static variable_t variable_table;
+
+/* To implement loops we store entire lines in a linked list. */
+struct loopline_s
+{
+ struct loopline_s *next;
+ char line[1];
+};
+typedef struct loopline_s *loopline_t;
+
+
/* This is used to store the pid of the server. */
static pid_t server_pid = (pid_t)(-1);
@@ -389,6 +401,56 @@ get_var (const char *name)
}
+/* Perform some simple arithmentic operations. Caller must release
+ the return value. On error the return value is NULL. */
+static char *
+arithmetic_op (int operator, const char *operands)
+{
+ long result, value;
+ char numbuf[35];
+
+ while ( spacep (operands) )
+ operands++;
+ if (!*operands)
+ return NULL;
+ result = strtol (operands, NULL, 0);
+ while (*operands && !spacep (operands) )
+ operands++;
+ while (*operands)
+ {
+ while ( spacep (operands) )
+ operands++;
+ if (!*operands)
+ break;
+ value = strtol (operands, NULL, 0);
+ while (*operands && !spacep (operands) )
+ operands++;
+ switch (operator)
+ {
+ case '+': result += value; break;
+ case '-': result -= value; break;
+ case '*': result *= value; break;
+ case '/':
+ if (!value)
+ return NULL;
+ result /= value;
+ break;
+ case '%':
+ if (!value)
+ return NULL;
+ result %= value;
+ break;
+ default:
+ log_error ("unknown arithmetic operator `%c'\n", operator);
+ return NULL;
+ }
+ }
+ snprintf (numbuf, sizeof numbuf, "%ld", result);
+ return xstrdup (numbuf);
+}
+
+
+
/* Extended version of get_var. This returns a malloced string and
understand the fucntion syntax: "func args".
@@ -412,7 +474,7 @@ get_var (const char *name)
unpercent ARGS
unpercent+ ARGS
- Remove percent style ecaping from string. NOte that "%00
+ 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 '+'
@@ -517,6 +579,10 @@ get_var_ext (const char *name)
if (*p == ' ')
*p = '+';
}
+ else if ( (s - name) == 1 && strchr ("+-*/%", *name))
+ {
+ result = arithmetic_op (*name, s+1);
+ }
else
{
log_error ("unknown variable function `%.*s'\n", (int)(s-name), name);
@@ -1025,8 +1091,16 @@ main (int argc, char **argv)
int cmderr;
const char *opt_run = NULL;
FILE *script_fp = NULL;
- int use_tty, last_was_tty;
-
+ int use_tty, keep_line;
+ struct {
+ int collecting;
+ loopline_t head;
+ loopline_t *tail;
+ loopline_t current;
+ unsigned int nestlevel;
+ char *condition;
+ } loopstack[20];
+ int loopidx;
gnupg_rl_initialize ();
set_strusage (my_strusage);
@@ -1060,7 +1134,10 @@ 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;
+ case oSubst:
+ opt.enable_varsubst = 1;
+ opt.trim_leading_spaces = 1;
+ break;
default: pargs.err = 2; break;
}
@@ -1143,18 +1220,36 @@ main (int argc, char **argv)
}
+ for (loopidx=0; loopidx < DIM (loopstack); loopidx++)
+ loopstack[loopidx].collecting = 0;
+ loopidx = -1;
line = NULL;
linesize = 0;
- last_was_tty = 0;
+ keep_line = 1;
for (;;)
{
int n;
- size_t maxlength;
+ size_t maxlength = 2048;
- maxlength = 2048;
- if (use_tty && !script_fp)
+ assert (loopidx < (int)DIM (loopstack));
+ if (loopidx >= 0 && loopstack[loopidx].current)
{
- last_was_tty = 1;
+ keep_line = 0;
+ xfree (line);
+ line = xstrdup (loopstack[loopidx].current->line);
+ n = strlen (line);
+ /* Never go beyond of the final /end. */
+ if (loopstack[loopidx].current->next)
+ loopstack[loopidx].current = loopstack[loopidx].current->next;
+ else if (!strncmp (line, "/end", 4) && (!line[4]||spacep(line+4)))
+ ;
+ else
+ log_fatal ("/end command vanished\n");
+ }
+ else if (use_tty && !script_fp)
+ {
+ keep_line = 0;
+ xfree (line);
line = tty_get ("> ");
n = strlen (line);
if (n==1 && *line == CONTROL_D)
@@ -1164,12 +1259,12 @@ main (int argc, char **argv)
}
else
{
- if (last_was_tty)
+ if (!keep_line)
{
xfree (line);
line = NULL;
linesize = 0;
- last_was_tty = 0;
+ keep_line = 1;
}
n = read_line (script_fp? script_fp:stdin,
&line, &linesize, &maxlength);
@@ -1208,6 +1303,44 @@ main (int argc, char **argv)
log_info (_("line shortened due to embedded Nul character\n"));
if (line[n-1] == '\n')
line[n-1] = 0;
+
+ if (opt.trim_leading_spaces)
+ {
+ const char *s = line;
+
+ while (spacep (s))
+ s++;
+ if (s != line)
+ {
+ for (p=line; *s;)
+ *p++ = *s++;
+ *p = 0;
+ n = p - line;
+ }
+ }
+
+ if (loopidx+1 >= 0 && loopstack[loopidx+1].collecting)
+ {
+ loopline_t ll;
+
+ ll = xmalloc (sizeof *ll + strlen (line));
+ ll->next = NULL;
+ strcpy (ll->line, line);
+ *loopstack[loopidx+1].tail = ll;
+ loopstack[loopidx+1].tail = &ll->next;
+
+ if (!strncmp (line, "/end", 4) && (!line[4]||spacep(line+4)))
+ loopstack[loopidx+1].nestlevel--;
+ else if (!strncmp (line, "/while", 6) && (!line[6]||spacep(line+6)))
+ loopstack[loopidx+1].nestlevel++;
+
+ if (loopstack[loopidx+1].nestlevel)
+ continue;
+ /* We reached the corresponding /end. */
+ loopstack[loopidx+1].collecting = 0;
+ loopidx++;
+ }
+
if (*line == '/')
{
/* Handle control commands. */
@@ -1347,12 +1480,16 @@ main (int argc, char **argv)
else if (!strcmp (cmd, "nodecode"))
opt.decode = 0;
else if (!strcmp (cmd, "subst"))
- opt.enable_varsubst = 1;
+ {
+ opt.enable_varsubst = 1;
+ opt.trim_leading_spaces = 1;
+ }
else if (!strcmp (cmd, "nosubst"))
opt.enable_varsubst = 0;
else if (!strcmp (cmd, "run"))
{
char *p2;
+
for (p2=p; *p2 && !spacep (p2); p2++)
;
if (*p2)
@@ -1382,10 +1519,75 @@ main (int argc, char **argv)
else if (opt.verbose)
log_info ("running commands from `%s'\n", p);
}
+ else if (!strcmp (cmd, "while"))
+ {
+ if (loopidx+2 >= (int)DIM(loopstack))
+ {
+ log_error ("loops are nested too deep\n");
+ /* We should better die or break all loop in this
+ case as recovering from this error won't be
+ easy. */
+ }
+ else
+ {
+ loopstack[loopidx+1].head = NULL;
+ loopstack[loopidx+1].tail = &loopstack[loopidx+1].head;
+ loopstack[loopidx+1].current = NULL;
+ loopstack[loopidx+1].nestlevel = 1;
+ loopstack[loopidx+1].condition = xstrdup (p);
+ loopstack[loopidx+1].collecting = 1;
+ }
+ }
+ else if (!strcmp (cmd, "end"))
+ {
+ if (loopidx < 0)
+ log_error ("stray /end command encountered - ignored\n");
+ else
+ {
+ char *tmpcond;
+ const char *value;
+ long condition;
+
+ /* Evaluate the condition. */
+ tmpcond = xstrdup (loopstack[loopidx].condition);
+ tmpline = substitute_line (tmpcond);
+ value = tmpline? tmpline : tmpcond;
+ condition = strtol (value, NULL, 0);
+ xfree (tmpline);
+ xfree (tmpcond);
+
+ if (condition)
+ {
+ /* Run loop. */
+ loopstack[loopidx].current = loopstack[loopidx].head;
+ }
+ else
+ {
+ /* Cleanup. */
+ while (loopstack[loopidx].head)
+ {
+ loopline_t tmp = loopstack[loopidx].head->next;
+ xfree (loopstack[loopidx].head);
+ loopstack[loopidx].head = tmp;
+ }
+ loopstack[loopidx].tail = NULL;
+ loopstack[loopidx].current = NULL;
+ loopstack[loopidx].nestlevel = 0;
+ loopstack[loopidx].collecting = 0;
+ xfree (loopstack[loopidx].condition);
+ loopstack[loopidx].condition = NULL;
+ loopidx--;
+ }
+ }
+ }
else if (!strcmp (cmd, "bye"))
{
break;
}
+ else if (!strcmp (cmd, "sleep"))
+ {
+ gnupg_sleep (1);
+ }
else if (!strcmp (cmd, "help"))
{
puts (
@@ -1408,6 +1610,8 @@ main (int argc, char **argv)
"/[no]decode Enable decoding of received data lines.\n"
"/[no]subst Enable varibale substitution.\n"
"/run FILE Run commands from FILE.\n"
+"/while VAR Begin loop controlled by VAR.\n"
+"/end End loop.\n"
"/bye Terminate gpg-connect-agent.\n"
"/help Print this help.");
}