summaryrefslogtreecommitdiffstats
path: root/tests/asschk.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 16:14:06 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 16:14:06 +0000
commiteee068778cb28ecf3c14e1bf843a95547d72c42d (patch)
tree0e07b30ddc5ea579d682d5dbe57998200d1c9ab7 /tests/asschk.c
parentInitial commit. (diff)
downloadgnupg2-eee068778cb28ecf3c14e1bf843a95547d72c42d.tar.xz
gnupg2-eee068778cb28ecf3c14e1bf843a95547d72c42d.zip
Adding upstream version 2.2.40.upstream/2.2.40
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'tests/asschk.c')
-rw-r--r--tests/asschk.c1094
1 files changed, 1094 insertions, 0 deletions
diff --git a/tests/asschk.c b/tests/asschk.c
new file mode 100644
index 0000000..65828e5
--- /dev/null
+++ b/tests/asschk.c
@@ -0,0 +1,1094 @@
+/* asschk.c - Assuan Server Checker
+ * Copyright (C) 2002 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG 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.
+ *
+ * GnuPG 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 <https://www.gnu.org/licenses/>.
+ */
+
+/* This is a simple stand-alone Assuan server test program. We don't
+ want to use the assuan library because we don't want to hide errors
+ in that library.
+
+ The script language is line based. Empty lines or lines containing
+ only white spaces are ignored, line with a hash sign as first non
+ white space character are treated as comments.
+
+ A simple macro mechanism is implemnted. Macros are expanded before
+ a line is processed but after comment processing. Macros are only
+ expanded once and non existing macros expand to the empty string.
+ A macro is dereferenced by prefixing its name with a dollar sign;
+ the end of the name is currently indicated by a white space, a
+ dollar sign or a slash. To use a dollor sign verbatim, double it.
+
+ A macro is assigned by prefixing a statement with the macro name
+ and an equal sign. The value is assigned verbatim if it does not
+ resemble a command, otherwise the return value of the command will
+ get assigned. The command "let" may be used to assign values
+ unambigiously and it should be used if the value starts with a
+ letter.
+
+ Conditions are not yes implemented except for a simple evaluation
+ which yields false for an empty string or the string "0". The
+ result may be negated by prefixing with a '!'.
+
+ The general syntax of a command is:
+
+ [<name> =] <statement> [<args>]
+
+ If NAME is not specifed but the statement returns a value it is
+ assigned to the name "?" so that it can be referenced using "$?".
+ The following commands are implemented:
+
+ let <value>
+ Return VALUE.
+
+ echo <value>
+ Print VALUE.
+
+ openfile <filename>
+ Open file FILENAME for read access and return the file descriptor.
+
+ createfile <filename>
+ Create file FILENAME, open for write access and return the file
+ descriptor.
+
+ pipeserver <program>
+ Connect to the Assuan server PROGRAM.
+
+ send <line>
+ Send LINE to the server.
+
+ expect-ok
+ Expect an OK response from the server. Status and data out put
+ is ignored.
+
+ expect-err
+ Expect an ERR response from the server. Status and data out put
+ is ignored.
+
+ count-status <code>
+ Initialize the assigned variable to 0 and assign it as an counter for
+ status code CODE. This command must be called with an assignment.
+
+ quit
+ Terminate the process.
+
+ quit-if <condition>
+ Terminate the process if CONDITION evaluates to true.
+
+ fail-if <condition>
+ Terminate the process with an exit code of 1 if CONDITION
+ evaluates to true.
+
+ cmpfiles <first> <second>
+ Returns true when the content of the files FIRST and SECOND match.
+
+ getenv <name>
+ Return the value of the environment variable NAME.
+
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <assert.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 5 )
+# define ATTR_PRINTF(f,a) __attribute__ ((format (printf,f,a)))
+#else
+# define ATTR_PRINTF(f,a)
+#endif
+
+#if __STDC_VERSION__ < 199901L
+# if __GNUC__ >= 2 && !defined (__func__)
+# define __func__ __FUNCTION__
+# else
+/* Let's try our luck here. Some systems may provide __func__ without
+ providing __STDC_VERSION__ 199901L. */
+# if 0
+# define __func__ "<unknown>"
+# endif
+# endif
+#endif
+
+#define spacep(p) (*(p) == ' ' || *(p) == '\t')
+
+#define MAX_LINELEN 2048
+
+typedef enum {
+ LINE_OK = 0,
+ LINE_ERR,
+ LINE_STAT,
+ LINE_DATA,
+ LINE_END,
+} LINETYPE;
+
+typedef enum {
+ VARTYPE_SIMPLE = 0,
+ VARTYPE_FD,
+ VARTYPE_COUNTER
+} VARTYPE;
+
+
+struct variable_s {
+ struct variable_s *next;
+ VARTYPE type;
+ unsigned int count;
+ char *value;
+ char name[1];
+};
+typedef struct variable_s *VARIABLE;
+
+
+static void die (const char *format, ...) ATTR_PRINTF(1,2);
+
+
+/* Name of this program to be printed in error messages. */
+static const char *invocation_name;
+
+/* Talk a bit about what is going on. */
+static int opt_verbose;
+
+/* Option to ignore the echo command. */
+static int opt_no_echo;
+
+/* File descriptors used to communicate with the current server. */
+static int server_send_fd = -1;
+static int server_recv_fd = -1;
+
+/* The Assuan protocol limits the line length to 1024, so we can
+ safely use a (larger) buffer. The buffer is filled using the
+ read_assuan(). */
+static char recv_line[MAX_LINELEN];
+/* Tell the status of the current line. */
+static LINETYPE recv_type;
+
+/* This is our variable storage. */
+static VARIABLE variable_list;
+
+
+static void
+die (const char *format, ...)
+{
+ va_list arg_ptr;
+
+ fflush (stdout);
+ fprintf (stderr, "%s: ", invocation_name);
+
+ va_start (arg_ptr, format);
+ vfprintf (stderr, format, arg_ptr);
+ va_end (arg_ptr);
+ putc ('\n', stderr);
+
+ exit (1);
+}
+
+#define die_0(format) (die) ("%s: " format, __func__)
+#define die_1(format, a) (die) ("%s: " format, __func__, (a))
+#define die_2(format, a, b) (die) ("%s: " format, __func__, (a),(b))
+#define die_3(format, a, b, c) (die) ("%s: " format, __func__, (a),(b),(c))
+
+static void
+err (const char *format, ...)
+{
+ va_list arg_ptr;
+
+ fflush (stdout);
+ fprintf (stderr, "%s: ", invocation_name);
+
+ va_start (arg_ptr, format);
+ vfprintf (stderr, format, arg_ptr);
+ va_end (arg_ptr);
+ putc ('\n', stderr);
+}
+
+static void *
+xmalloc (size_t n)
+{
+ void *p = malloc (n);
+ if (!p)
+ die ("out of core");
+ return p;
+}
+
+static void *
+xcalloc (size_t n, size_t m)
+{
+ void *p = calloc (n, m);
+ if (!p)
+ die ("out of core");
+ return p;
+}
+
+static char *
+xstrdup (const char *s)
+{
+ char *p = xmalloc (strlen (s)+1);
+ strcpy (p, s);
+ return p;
+}
+
+
+/* Write LENGTH bytes from BUFFER to FD. */
+static int
+writen (int fd, const char *buffer, size_t length)
+{
+ while (length)
+ {
+ int nwritten = write (fd, buffer, length);
+
+ if (nwritten < 0)
+ {
+ if (errno == EINTR)
+ continue;
+ return -1; /* write error */
+ }
+ length -= nwritten;
+ buffer += nwritten;
+ }
+ return 0; /* okay */
+}
+
+
+
+
+/* Assuan specific stuff. */
+
+/* Read a line from FD, store it in the global recv_line, analyze the
+ type and store that in recv_type. The function terminates on a
+ communication error. Returns a pointer into the inputline to the
+ first byte of the arguments. The parsing is very strict to match
+ exaclty what we want to send. */
+static char *
+read_assuan (int fd)
+{
+ /* FIXME: For general robustness, the pending stuff needs to be
+ associated with FD. */
+ static char pending[MAX_LINELEN];
+ static size_t pending_len;
+ size_t nleft = sizeof recv_line;
+ char *buf = recv_line;
+ char *p;
+
+ while (nleft > 0)
+ {
+ int n;
+
+ if (pending_len)
+ {
+ if (pending_len >= nleft)
+ die_0 ("received line too large");
+ memcpy (buf, pending, pending_len);
+ n = pending_len;
+ pending_len = 0;
+ }
+ else
+ {
+ do
+ {
+ n = read (fd, buf, nleft);
+ }
+ while (n < 0 && errno == EINTR);
+ }
+
+ if (opt_verbose && n >= 0 )
+ {
+ int i;
+
+ printf ("%s: read \"", __func__);
+ for (i = 0; i < n; i ++)
+ putc (buf[i], stdout);
+ printf ("\"\n");
+ }
+
+ if (n < 0)
+ die_2 ("reading fd %d failed: %s", fd, strerror (errno));
+ else if (!n)
+ die_1 ("received incomplete line on fd %d", fd);
+ p = buf;
+ nleft -= n;
+ buf += n;
+
+ for (; n && *p != '\n'; n--, p++)
+ ;
+ if (n)
+ {
+ if (n>1)
+ {
+ n--;
+ memcpy (pending, p + 1, n);
+ pending_len = n;
+ }
+ *p = '\0';
+ break;
+ }
+ }
+ if (!nleft)
+ die_0 ("received line too large");
+
+ p = recv_line;
+ if (p[0] == 'O' && p[1] == 'K' && (p[2] == ' ' || !p[2]))
+ {
+ recv_type = LINE_OK;
+ p += 3;
+ }
+ else if (p[0] == 'E' && p[1] == 'R' && p[2] == 'R'
+ && (p[3] == ' ' || !p[3]))
+ {
+ recv_type = LINE_ERR;
+ p += 4;
+ }
+ else if (p[0] == 'S' && (p[1] == ' ' || !p[1]))
+ {
+ recv_type = LINE_STAT;
+ p += 2;
+ }
+ else if (p[0] == 'D' && p[1] == ' ')
+ {
+ recv_type = LINE_DATA;
+ p += 2;
+ }
+ else if (p[0] == 'E' && p[1] == 'N' && p[2] == 'D' && !p[3])
+ {
+ recv_type = LINE_END;
+ p += 3;
+ }
+ else
+ die_1 ("invalid line type (%.5s)", p);
+
+ return p;
+}
+
+/* Write LINE to the server using FD. It is expected that the line
+ contains the terminating linefeed as last character. */
+static void
+write_assuan (int fd, const char *line)
+{
+ char buffer[1026];
+ size_t n = strlen (line);
+
+ if (n > 1024)
+ die_0 ("line too long for Assuan protocol");
+ strcpy (buffer, line);
+ if (!n || buffer[n-1] != '\n')
+ buffer[n++] = '\n';
+
+ if (writen (fd, buffer, n))
+ die_3 ("sending line (\"%s\") to %d failed: %s", buffer, fd,
+ strerror (errno));
+}
+
+
+/* Start the server with path PGMNAME and connect its stdout and
+ strerr to a newly created pipes; the file descriptors are then
+ store in the gloabl variables SERVER_SEND_FD and
+ SERVER_RECV_FD. The initial handcheck is performed.*/
+static void
+start_server (const char *pgmname)
+{
+ int rp[2];
+ int wp[2];
+ pid_t pid;
+
+ if (pipe (rp) < 0)
+ die_1 ("pipe creation failed: %s", strerror (errno));
+ if (pipe (wp) < 0)
+ die_1 ("pipe creation failed: %s", strerror (errno));
+
+ fflush (stdout);
+ fflush (stderr);
+ pid = fork ();
+ if (pid < 0)
+ die_0 ("fork failed");
+
+ if (!pid)
+ {
+ const char *arg0;
+
+ arg0 = strrchr (pgmname, '/');
+ if (arg0)
+ arg0++;
+ else
+ arg0 = pgmname;
+
+ if (wp[0] != STDIN_FILENO)
+ {
+ if (dup2 (wp[0], STDIN_FILENO) == -1)
+ die_1 ("dup2 failed in child: %s", strerror (errno));
+ close (wp[0]);
+ }
+ if (rp[1] != STDOUT_FILENO)
+ {
+ if (dup2 (rp[1], STDOUT_FILENO) == -1)
+ die_1 ("dup2 failed in child: %s", strerror (errno));
+ close (rp[1]);
+ }
+ if (!opt_verbose)
+ {
+ int fd = open ("/dev/null", O_WRONLY);
+ if (fd == -1)
+ die_1 ("can't open '/dev/null': %s", strerror (errno));
+ if (dup2 (fd, STDERR_FILENO) == -1)
+ die_1 ("dup2 failed in child: %s", strerror (errno));
+ close (fd);
+ }
+
+ close (wp[1]);
+ close (rp[0]);
+ execl (pgmname, arg0, "--server", NULL);
+ die_2 ("exec failed for '%s': %s", pgmname, strerror (errno));
+ }
+ close (wp[0]);
+ close (rp[1]);
+ server_send_fd = wp[1];
+ server_recv_fd = rp[0];
+
+ read_assuan (server_recv_fd);
+ if (recv_type != LINE_OK)
+ die_0 ("no greating message");
+}
+
+
+
+
+
+/* Script intepreter. */
+
+static void
+unset_var (const char *name)
+{
+ VARIABLE var;
+
+ for (var=variable_list; var && strcmp (var->name, name); var = var->next)
+ ;
+ if (!var)
+ return;
+/* fprintf (stderr, "unsetting '%s'\n", name); */
+
+ if (var->type == VARTYPE_FD && var->value)
+ {
+ int fd;
+
+ fd = atoi (var->value);
+ if (fd != -1 && fd != 0 && fd != 1 && fd != 2)
+ close (fd);
+ }
+
+ free (var->value);
+ var->value = NULL;
+ var->type = 0;
+ var->count = 0;
+}
+
+
+static void
+set_type_var (const char *name, const char *value, VARTYPE type)
+{
+ VARIABLE var;
+
+ if (!name)
+ name = "?";
+ for (var=variable_list; var && strcmp (var->name, name); var = var->next)
+ ;
+ if (!var)
+ {
+ var = xcalloc (1, sizeof *var + strlen (name));
+ strcpy (var->name, name);
+ var->next = variable_list;
+ variable_list = var;
+ }
+ else
+ {
+ free (var->value);
+ var->value = NULL;
+ }
+
+ if (var->type == VARTYPE_FD && var->value)
+ {
+ int fd;
+
+ fd = atoi (var->value);
+ if (fd != -1 && fd != 0 && fd != 1 && fd != 2)
+ close (fd);
+ }
+
+ var->type = type;
+ var->count = 0;
+ if (var->type == VARTYPE_COUNTER)
+ {
+ /* We need some extra sapce as scratch area for get_var. */
+ var->value = xmalloc (strlen (value) + 1 + 20);
+ strcpy (var->value, value);
+ }
+ else
+ var->value = xstrdup (value);
+}
+
+static void
+set_var (const char *name, const char *value)
+{
+ set_type_var (name, value, 0);
+}
+
+
+static const char *
+get_var (const char *name)
+{
+ VARIABLE var;
+
+ for (var=variable_list; var && strcmp (var->name, name); var = var->next)
+ ;
+ if (!var)
+ return NULL;
+ if (var->type == VARTYPE_COUNTER && var->value)
+ { /* Use the scratch space allocated by set_var. */
+ char *p = var->value + strlen(var->value)+1;
+ sprintf (p, "%u", var->count);
+ return p;
+ }
+ else
+ return var->value;
+}
+
+
+/* Incremente all counter type variables with NAME in their VALUE. */
+static void
+inc_counter (const char *name)
+{
+ VARIABLE var;
+
+ if (!*name)
+ return;
+ for (var=variable_list; var; var = var->next)
+ {
+ if (var->type == VARTYPE_COUNTER
+ && var->value && !strcmp (var->value, name))
+ var->count++;
+ }
+}
+
+
+/* Expand variables in LINE and return a new allocated buffer if
+ required. The function might modify LINE if the expanded version
+ fits into it. */
+static char *
+expand_line (char *buffer)
+{
+ char *line = buffer;
+ char *p, *pend;
+ const char *value;
+ size_t valuelen, n;
+ char *result = NULL;
+
+ while (*line)
+ {
+ p = strchr (line, '$');
+ if (!p)
+ return result; /* nothing more to expand */
+
+ if (p[1] == '$') /* quoted */
+ {
+ memmove (p, p+1, strlen (p+1)+1);
+ line = p + 1;
+ continue;
+ }
+ for (pend=p+1; *pend && !spacep (pend)
+ && *pend != '$' && *pend != '/'; pend++)
+ ;
+ if (*pend)
+ {
+ int save = *pend;
+ *pend = 0;
+ value = get_var (p+1);
+ *pend = save;
+ }
+ else
+ value = get_var (p+1);
+ if (!value)
+ value = "";
+ valuelen = strlen (value);
+ if (valuelen <= pend - p)
+ {
+ memcpy (p, value, valuelen);
+ p += valuelen;
+ n = pend - p;
+ if (n)
+ memmove (p, p+n, strlen (p+n)+1);
+ line = p;
+ }
+ else
+ {
+ char *src = result? result : buffer;
+ char *dst;
+
+ dst = xmalloc (strlen (src) + valuelen + 1);
+ n = p - src;
+ memcpy (dst, src, n);
+ memcpy (dst + n, value, valuelen);
+ n += valuelen;
+ strcpy (dst + n, pend);
+ line = dst + n;
+ free (result);
+ result = dst;
+ }
+ }
+ return result;
+}
+
+
+/* Evaluate COND and return the result. */
+static int
+eval_boolean (const char *cond)
+{
+ int true = 1;
+
+ for ( ; *cond == '!'; cond++)
+ true = !true;
+ if (!*cond || (*cond == '0' && !cond[1]))
+ return !true;
+ return true;
+}
+
+
+
+
+
+static void
+cmd_let (const char *assign_to, char *arg)
+{
+ set_var (assign_to, arg);
+}
+
+
+static void
+cmd_echo (const char *assign_to, char *arg)
+{
+ (void)assign_to;
+ if (!opt_no_echo)
+ printf ("%s\n", arg);
+}
+
+static void
+cmd_send (const char *assign_to, char *arg)
+{
+ (void)assign_to;
+ if (opt_verbose)
+ fprintf (stderr, "sending '%s'\n", arg);
+ write_assuan (server_send_fd, arg);
+}
+
+static void
+handle_status_line (char *arg)
+{
+ char *p;
+
+ for (p=arg; *p && !spacep (p); p++)
+ ;
+ if (*p)
+ {
+ int save = *p;
+ *p = 0;
+ inc_counter (arg);
+ *p = save;
+ }
+ else
+ inc_counter (arg);
+}
+
+static void
+cmd_expect_ok (const char *assign_to, char *arg)
+{
+ (void)assign_to;
+ (void)arg;
+
+ if (opt_verbose)
+ fprintf (stderr, "expecting OK\n");
+ do
+ {
+ char *p = read_assuan (server_recv_fd);
+ if (opt_verbose > 1)
+ fprintf (stderr, "got line '%s'\n", recv_line);
+ if (recv_type == LINE_STAT)
+ handle_status_line (p);
+ }
+ while (recv_type != LINE_OK && recv_type != LINE_ERR);
+ if (recv_type != LINE_OK)
+ die_1 ("expected OK but got '%s'", recv_line);
+}
+
+static void
+cmd_expect_err (const char *assign_to, char *arg)
+{
+ (void)assign_to;
+ (void)arg;
+
+ if (opt_verbose)
+ fprintf (stderr, "expecting ERR\n");
+ do
+ {
+ char *p = read_assuan (server_recv_fd);
+ if (opt_verbose > 1)
+ fprintf (stderr, "got line '%s'\n", recv_line);
+ if (recv_type == LINE_STAT)
+ handle_status_line (p);
+ }
+ while (recv_type != LINE_OK && recv_type != LINE_ERR);
+ if (recv_type != LINE_ERR)
+ die_1 ("expected ERR but got '%s'", recv_line);
+}
+
+static void
+cmd_count_status (const char *assign_to, char *arg)
+{
+ char *p;
+
+ if (!*assign_to || !*arg)
+ die_0 ("syntax error: count-status requires an argument and a variable");
+
+ for (p=arg; *p && !spacep (p); p++)
+ ;
+ if (*p)
+ {
+ for (*p++ = 0; spacep (p); p++)
+ ;
+ if (*p)
+ die_0 ("cmpfiles: syntax error");
+ }
+ set_type_var (assign_to, arg, VARTYPE_COUNTER);
+}
+
+static void
+cmd_openfile (const char *assign_to, char *arg)
+{
+ int fd;
+ char numbuf[20];
+
+ do
+ fd = open (arg, O_RDONLY);
+ while (fd == -1 && errno == EINTR);
+ if (fd == -1)
+ die_2 ("error opening '%s': %s", arg, strerror (errno));
+
+ sprintf (numbuf, "%d", fd);
+ set_type_var (assign_to, numbuf, VARTYPE_FD);
+}
+
+static void
+cmd_createfile (const char *assign_to, char *arg)
+{
+ int fd;
+ char numbuf[20];
+
+ do
+ fd = open (arg, O_WRONLY|O_CREAT|O_TRUNC, 0666);
+ while (fd == -1 && errno == EINTR);
+ if (fd == -1)
+ die_2 ("error creating '%s': %s", arg, strerror (errno));
+
+ sprintf (numbuf, "%d", fd);
+ set_type_var (assign_to, numbuf, VARTYPE_FD);
+}
+
+
+static void
+cmd_pipeserver (const char *assign_to, char *arg)
+{
+ (void)assign_to;
+
+ if (!*arg)
+ die_0 ("syntax error: servername missing");
+
+ start_server (arg);
+}
+
+
+static void
+cmd_quit_if(const char *assign_to, char *arg)
+{
+ (void)assign_to;
+
+ if (eval_boolean (arg))
+ exit (0);
+}
+
+static void
+cmd_fail_if(const char *assign_to, char *arg)
+{
+ (void)assign_to;
+
+ if (eval_boolean (arg))
+ exit (1);
+}
+
+
+static void
+cmd_cmpfiles (const char *assign_to, char *arg)
+{
+ char *p = arg;
+ char *second;
+ FILE *fp1, *fp2;
+ char buffer1[2048]; /* note: both must be of equal size. */
+ char buffer2[2048];
+ size_t nread1, nread2;
+ int rc = 0;
+
+ set_var (assign_to, "0");
+ for (p=arg; *p && !spacep (p); p++)
+ ;
+ if (!*p)
+ die_0 ("cmpfiles: syntax error");
+ for (*p++ = 0; spacep (p); p++)
+ ;
+ second = p;
+ for (; *p && !spacep (p); p++)
+ ;
+ if (*p)
+ {
+ for (*p++ = 0; spacep (p); p++)
+ ;
+ if (*p)
+ die_0 ("cmpfiles: syntax error");
+ }
+
+ fp1 = fopen (arg, "rb");
+ if (!fp1)
+ {
+ err ("can't open '%s': %s", arg, strerror (errno));
+ return;
+ }
+ fp2 = fopen (second, "rb");
+ if (!fp2)
+ {
+ err ("can't open '%s': %s", second, strerror (errno));
+ fclose (fp1);
+ return;
+ }
+ while ( (nread1 = fread (buffer1, 1, sizeof buffer1, fp1)))
+ {
+ if (ferror (fp1))
+ break;
+ nread2 = fread (buffer2, 1, sizeof buffer2, fp2);
+ if (ferror (fp2))
+ break;
+ if (nread1 != nread2 || memcmp (buffer1, buffer2, nread1))
+ {
+ rc = 1;
+ break;
+ }
+ }
+ if (feof (fp1) && feof (fp2) && !rc)
+ {
+ if (opt_verbose)
+ err ("files match");
+ set_var (assign_to, "1");
+ }
+ else if (!rc)
+ err ("cmpfiles: read error: %s", strerror (errno));
+ else
+ err ("cmpfiles: mismatch");
+ fclose (fp1);
+ fclose (fp2);
+}
+
+static void
+cmd_getenv (const char *assign_to, char *arg)
+{
+ const char *s;
+ s = *arg? getenv (arg):"";
+ set_var (assign_to, s? s:"");
+}
+
+
+
+
+/* Process the current script line LINE. */
+static int
+interpreter (char *line)
+{
+ static struct {
+ const char *name;
+ void (*fnc)(const char*, char*);
+ } cmdtbl[] = {
+ { "let" , cmd_let },
+ { "echo" , cmd_echo },
+ { "send" , cmd_send },
+ { "expect-ok" , cmd_expect_ok },
+ { "expect-err", cmd_expect_err },
+ { "count-status", cmd_count_status },
+ { "openfile" , cmd_openfile },
+ { "createfile", cmd_createfile },
+ { "pipeserver", cmd_pipeserver },
+ { "quit" , NULL },
+ { "quit-if" , cmd_quit_if },
+ { "fail-if" , cmd_fail_if },
+ { "cmpfiles" , cmd_cmpfiles },
+ { "getenv" , cmd_getenv },
+ { NULL }
+ };
+ char *p, *save_p;
+ int i, save_c;
+ char *stmt = NULL;
+ char *assign_to = NULL;
+ char *must_free = NULL;
+
+ for ( ;spacep (line); line++)
+ ;
+ if (!*line || *line == '#')
+ return 0; /* empty or comment */
+ p = expand_line (line);
+ if (p)
+ {
+ must_free = p;
+ line = p;
+ for ( ;spacep (line); line++)
+ ;
+ if (!*line || *line == '#')
+ {
+ free (must_free);
+ return 0; /* empty or comment */
+ }
+ }
+ for (p=line; *p && !spacep (p) && *p != '='; p++)
+ ;
+ if (*p == '=')
+ {
+ *p = 0;
+ assign_to = line;
+ }
+ else if (*p)
+ {
+ for (*p++ = 0; spacep (p); p++)
+ ;
+ if (*p == '=')
+ assign_to = line;
+ }
+ if (!*line)
+ die_0 ("syntax error");
+ stmt = line;
+ save_c = 0;
+ save_p = NULL;
+ if (assign_to)
+ { /* this is an assignment */
+ for (p++; spacep (p); p++)
+ ;
+ if (!*p)
+ {
+ unset_var (assign_to);
+ free (must_free);
+ return 0;
+ }
+ stmt = p;
+ for (; *p && !spacep (p); p++)
+ ;
+ if (*p)
+ {
+ save_p = p;
+ save_c = *p;
+ for (*p++ = 0; spacep (p); p++)
+ ;
+ }
+ }
+ for (i=0; cmdtbl[i].name && strcmp (stmt, cmdtbl[i].name); i++)
+ ;
+ if (!cmdtbl[i].name)
+ {
+ if (!assign_to)
+ die_1 ("invalid statement '%s'\n", stmt);
+ if (save_p)
+ *save_p = save_c;
+ set_var (assign_to, stmt);
+ free (must_free);
+ return 0;
+ }
+
+ if (cmdtbl[i].fnc)
+ cmdtbl[i].fnc (assign_to, p);
+ free (must_free);
+ return cmdtbl[i].fnc? 0:1;
+}
+
+
+
+int
+main (int argc, char **argv)
+{
+ char buffer[2048];
+ char *p, *pend;
+
+ if (!argc)
+ invocation_name = "asschk";
+ else
+ {
+ invocation_name = *argv++;
+ argc--;
+ p = strrchr (invocation_name, '/');
+ if (p)
+ invocation_name = p+1;
+ }
+
+
+ set_var ("?","1"); /* defaults to true */
+
+ for (; argc; argc--, argv++)
+ {
+ p = *argv;
+ if (*p != '-')
+ break;
+ if (!strcmp (p, "--verbose"))
+ opt_verbose++;
+ else if (!strcmp (p, "--no-echo"))
+ opt_no_echo++;
+ else if (*p == '-' && p[1] == 'D')
+ {
+ p += 2;
+ pend = strchr (p, '=');
+ if (pend)
+ {
+ int tmp = *pend;
+ *pend = 0;
+ set_var (p, pend+1);
+ *pend = tmp;
+ }
+ else
+ set_var (p, "1");
+ }
+ else if (*p == '-' && p[1] == '-' && !p[2])
+ {
+ argc--; argv++;
+ break;
+ }
+ else
+ break;
+ }
+ if (argc)
+ die ("usage: asschk [--verbose] {-D<name>[=<value>]}");
+
+
+ while (fgets (buffer, sizeof buffer, stdin))
+ {
+ p = strchr (buffer,'\n');
+ if (!p)
+ die_0 ("incomplete script line");
+ *p = 0;
+ if (interpreter (buffer))
+ break;
+ fflush (stdout);
+ }
+ return 0;
+}