Tweaked the build system so that make distcheck finanly said Well
[gnupg.git] / tests / asschk.c
1 /* asschk.c - Assuan Server Checker
2  *      Copyright (C) 2002 Free Software Foundation, Inc.
3  *
4  * This file is part of GnuPG.
5  *
6  * GnuPG is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * GnuPG is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19  */
20
21 /* This is a simple stand-alone Assuan server test program.  We don't
22    want to use the assuan library because we don't want to hide errors
23    in that library. 
24
25    The script language is line based.  Empty lines or lines containing
26    only white spaces are ignored, line with a hash sign as first non
27    white space character are treated as comments.
28    
29    A simple macro mechanism is implemnted.  Macros are expanded before
30    a line is processed but after comment processing.  Macros are only
31    expanded once and non existing macros expand to the empty string.
32    A macro is dereferenced by prefixing its name with a dollar sign;
33    the end of the name is currently indicated by a white space, a
34    dollar sign or a slash.  To use a dollor sign verbatim, double it.
35
36    A macro is assigned by prefixing a statement with the macro name
37    and an equal sign.  The value is assigned verbatim if it does not
38    resemble a command, otherwise the return value of the command will
39    get assigned.  The command "let" may be used to assign values
40    unambigiously and it should be used if the value starts with a
41    letter.
42
43    Conditions are not yes implemented except for a simple evaluation
44    which yields false for an empty string or the string "0".  The
45    result may be negated by prefixing with a '!'.
46
47    The general syntax of a command is:
48
49    [<name> =] <statement> [<args>]
50
51    If NAME is not specifed but the statement returns a value it is
52    assigned to the name "?" so that it can be referenced using "$?".
53    The following commands are implemented:
54
55    let <value>
56       Return VALUE.
57
58    echo <value>
59       Print VALUE.
60
61    openfile <filename>
62       Open file FILENAME for read access and retrun the file descriptor.
63
64    createfile <filename>
65       Create file FILENAME, open for write access and retrun the file
66       descriptor.
67
68    pipeserver <program>
69       Connect to the Assuan server PROGRAM.
70
71    send <line>
72       Send LINE to the server.
73
74    expect-ok
75       Expect an OK response from the server.  Status and data out put
76       is ignored.
77
78    expect-err
79       Expect an ERR response from the server.  Status and data out put
80       is ignored.
81
82    count-status <code>
83       Initialize the assigned variable to 0 and assign it as an counter for 
84       status code CODE.  This command must be called with an assignment.
85
86    quit
87       Terminate the process.
88
89    quit-if <condition>
90       Terminate the process if CONDITION evaluates to true.
91
92    fail-if <condition>
93       Terminate the process with an exit code of 1 if CONDITION
94       evaluates to true.
95
96    cmpfiles <first> <second>
97       Returns true when the content of the files FIRST and SECOND match.
98
99    getenv <name>
100       Return the value of the environment variable NAME.
101
102 */
103
104 #include <stdio.h>
105 #include <stdlib.h>
106 #include <string.h>
107 #include <errno.h>
108 #include <stdarg.h>
109 #include <assert.h>
110 #include <unistd.h>
111 #include <fcntl.h>
112
113 #if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 5 )
114 # define ATTR_PRINTF(f,a)  __attribute__ ((format (printf,f,a)))
115 #else
116 # define ATTR_PRINTF(f,a)
117 #endif
118
119 #define spacep(p) (*(p) == ' ' || *(p) == '\t')
120
121 #define MAX_LINELEN 2048
122
123 typedef enum {
124   LINE_OK = 0,
125   LINE_ERR,
126   LINE_STAT,
127   LINE_DATA,
128   LINE_END,
129 } LINETYPE;
130
131 typedef enum {
132   VARTYPE_SIMPLE = 0,
133   VARTYPE_FD,
134   VARTYPE_COUNTER
135 } VARTYPE;
136
137
138 struct variable_s {
139   struct variable_s *next;
140   VARTYPE type;
141   unsigned int count;
142   char *value;
143   char name[1];
144 };
145 typedef struct variable_s *VARIABLE;
146
147
148 static void die (const char *format, ...)  ATTR_PRINTF(1,2);
149
150
151 /* Name of this program to be printed in error messages. */
152 static const char *invocation_name;
153
154 /* Talk a bit about what is going on. */
155 static int opt_verbose;
156
157 /* Option to ignore the echo command. */
158 static int opt_no_echo;
159
160 /* File descriptors used to communicate with the current server. */
161 static int server_send_fd = -1;
162 static int server_recv_fd = -1;
163
164 /* The Assuan protocol limits the line length to 1024, so we can
165    safely use a (larger) buffer.  The buffer is filled using the
166    read_assuan(). */
167 static char recv_line[MAX_LINELEN];
168 /* Tell the status of the current line. */
169 static LINETYPE recv_type;
170
171 /* This is our variable storage. */
172 static VARIABLE variable_list;
173
174 \f
175 static void
176 die (const char *format, ...)
177 {
178   va_list arg_ptr;
179
180   fflush (stdout);
181   fprintf (stderr, "%s: ", invocation_name);
182
183   va_start (arg_ptr, format);
184   vfprintf (stderr, format, arg_ptr);
185   va_end (arg_ptr);
186   putc ('\n', stderr);
187
188   exit (1);
189 }
190
191 static void
192 err (const char *format, ...)
193 {
194   va_list arg_ptr;
195
196   fflush (stdout);
197   fprintf (stderr, "%s: ", invocation_name);
198
199   va_start (arg_ptr, format);
200   vfprintf (stderr, format, arg_ptr);
201   va_end (arg_ptr);
202   putc ('\n', stderr);
203 }
204
205 static void *
206 xmalloc (size_t n)
207 {
208   void *p = malloc (n);
209   if (!p)
210     die ("out of core");
211   return p;
212 }
213
214 static void *
215 xcalloc (size_t n, size_t m)
216 {
217   void *p = calloc (n, m);
218   if (!p)
219     die ("out of core");
220   return p;
221 }
222
223 static char *
224 xstrdup (const char *s)
225 {
226   char *p = xmalloc (strlen (s)+1);
227   strcpy (p, s);
228   return p;
229 }
230
231
232 /* Write LENGTH bytes from BUFFER to FD. */
233 static int
234 writen (int fd, const char *buffer, size_t length)
235 {
236   while (length)
237     {
238       int nwritten = write (fd, buffer, length);
239       
240       if (nwritten < 0)
241         {
242           if (errno == EINTR)
243             continue;
244           return -1; /* write error */
245         }
246       length -= nwritten;
247       buffer += nwritten;
248     }
249   return 0;  /* okay */
250 }
251
252
253
254 \f
255 /* Assuan specific stuff. */
256
257 /* Read a line from FD, store it in the global recv_line, analyze the
258    type and store that in recv_type.  The function terminates on a
259    communication error.  Returns a pointer into the inputline to the
260    first byte of the arguments.  The parsing is very strict to match
261    excalty what we want to send. */
262 static char *
263 read_assuan (int fd)
264 {
265   static char pending[MAX_LINELEN];
266   static size_t pending_len;
267   size_t nleft = sizeof recv_line;
268   char *buf = recv_line;
269   char *p;
270   int nread = 0;
271
272   while (nleft > 0)
273     {
274       int n;
275
276       if (pending_len)
277         {
278           if (pending_len >= nleft)
279             die ("received line too large");
280           memcpy (buf, pending, pending_len);
281           n = pending_len;
282           pending_len = 0;
283         }
284       else
285         n = read (fd, buf, nleft);
286       if (n < 0)
287         {
288           if (errno == EINTR)
289             continue;
290           die ("reading fd %d failed: %s", fd, strerror (errno));
291         }
292       else if (!n)
293         die ("received incomplete line on fd %d", fd);
294       p = buf;
295       nleft -= n;
296       buf += n;
297       nread += n;
298       
299       for (; n && *p != '\n'; n--, p++)
300         ;
301       if (n)
302         {
303           if (n>1)
304             {
305               n--;
306               memcpy (pending, p+1, n);
307               pending_len = n;
308             }
309           break;
310         }
311     }
312   if (!nleft)
313     die ("received line too large");
314   assert (nread>0);
315   recv_line[nread-1] = 0;
316
317   p = recv_line;
318   if (p[0] == 'O' && p[1] == 'K' && (p[2] == ' ' || !p[2]))
319     {
320       recv_type = LINE_OK;
321       p += 3;
322     }
323   else if (p[0] == 'E' && p[1] == 'R' && p[2] == 'R'
324            && (p[3] == ' ' || !p[3]))
325     {
326       recv_type = LINE_ERR;
327       p += 4;
328     }
329   else if (p[0] == 'S' && (p[1] == ' ' || !p[1]))
330     {
331       recv_type = LINE_STAT;
332       p += 2;
333     }
334   else if (p[0] == 'D' && p[1] == ' ')
335     {
336       recv_type = LINE_DATA;
337       p += 2;
338     }
339   else if (p[0] == 'E' && p[1] == 'N' &&  p[2] == 'D' && !p[3])
340     {
341       recv_type = LINE_END;
342       p += 3;
343     }
344   else 
345     die ("invalid line type (%.5s)", p);
346
347   return p;
348 }
349
350 /* Write LINE to the server using FD.  It is expected that the line
351    contains the terminating linefeed as last character. */
352 static void
353 write_assuan (int fd, const char *line)
354 {
355   char buffer[1026];
356   size_t n = strlen (line);
357
358   if (n > 1024)
359     die ("line too long for Assuan protocol");
360   strcpy (buffer, line);
361   if (!n || buffer[n-1] != '\n')
362     buffer[n++] = '\n';
363
364   if (writen (fd, buffer, n))
365       die ("sending line to %d failed: %s", fd, strerror (errno));
366 }
367
368
369 /* Start the server with path PGMNAME and connect its stdout and
370    strerr to a newly created pipes; the file descriptors are then
371    store in the gloabl variables SERVER_SEND_FD and
372    SERVER_RECV_FD. The initial handcheck is performed.*/
373 static void
374 start_server (const char *pgmname)
375 {
376   int rp[2];
377   int wp[2];
378   pid_t pid;
379
380   if (pipe (rp) < 0)
381     die ("pipe creation failed: %s", strerror (errno));
382   if (pipe (wp) < 0)
383     die ("pipe creation failed: %s", strerror (errno));
384
385   fflush (stdout);
386   fflush (stderr);
387   pid = fork ();
388   if (pid < 0)
389     die ("fork failed");
390
391   if (!pid)
392     {
393       const char *arg0;
394
395       arg0 = strrchr (pgmname, '/');
396       if (arg0)
397         arg0++;
398       else
399         arg0 = pgmname;
400
401       if (wp[0] != STDIN_FILENO)
402         {
403           if (dup2 (wp[0], STDIN_FILENO) == -1)
404               die ("dup2 failed in child: %s", strerror (errno));
405           close (wp[0]);
406         }
407       if (rp[1] != STDOUT_FILENO)
408         {
409           if (dup2 (rp[1], STDOUT_FILENO) == -1)
410               die ("dup2 failed in child: %s", strerror (errno));
411           close (rp[1]);
412         }
413       if (!opt_verbose)
414         {
415           int fd = open ("/dev/null", O_WRONLY);
416           if (fd == -1)
417             die ("can't open `/dev/null': %s", strerror (errno));
418           if (dup2 (fd, STDERR_FILENO) == -1)
419             die ("dup2 failed in child: %s", strerror (errno));
420           close (fd);
421         }
422
423       execl (pgmname, arg0, "--server", NULL); 
424       die ("exec failed for `%s': %s", pgmname, strerror (errno));
425     }
426   close (wp[0]);
427   close (rp[1]);
428   server_send_fd = wp[1];
429   server_recv_fd = rp[0];
430
431   read_assuan (server_recv_fd);
432   if (recv_type != LINE_OK)
433     die ("no greating message");
434 }
435
436
437
438
439 \f
440 /* Script intepreter. */
441
442 static void
443 unset_var (const char *name)
444 {
445   VARIABLE var;
446
447   for (var=variable_list; var && strcmp (var->name, name); var = var->next)
448     ;
449   if (!var)
450     return;
451 /*    fprintf (stderr, "unsetting `%s'\n", name); */
452
453   if (var->type == VARTYPE_FD && var->value)
454     {
455       int fd;
456
457       fd = atoi (var->value);
458       if (fd != -1 && fd != 0 && fd != 1 && fd != 2)
459           close (fd);
460     }
461
462   free (var->value);
463   var->value = NULL;
464   var->type = 0;
465   var->count = 0;
466 }
467
468
469 static void
470 set_type_var (const char *name, const char *value, VARTYPE type)
471 {
472   VARIABLE var;
473
474   if (!name)
475     name = "?"; 
476   for (var=variable_list; var && strcmp (var->name, name); var = var->next)
477     ;
478   if (!var)
479     {
480       var = xcalloc (1, sizeof *var + strlen (name));
481       strcpy (var->name, name);
482       var->next = variable_list;
483       variable_list = var;
484     }
485   else
486     free (var->value);
487
488   if (var->type == VARTYPE_FD && var->value)
489     {
490       int fd;
491
492       fd = atoi (var->value);
493       if (fd != -1 && fd != 0 && fd != 1 && fd != 2)
494           close (fd);
495     }
496   
497   var->type = type;
498   var->count = 0;
499   if (var->type == VARTYPE_COUNTER)
500     {
501       /* We need some extra sapce as scratch area for get_var. */
502       var->value = xmalloc (strlen (value) + 1 + 20);
503       strcpy (var->value, value);
504     }
505   else
506     var->value = xstrdup (value);
507 }
508
509 static void
510 set_var (const char *name, const char *value)
511 {
512   set_type_var (name, value, 0);
513 }
514
515
516 static const char *
517 get_var (const char *name)
518 {
519   VARIABLE var;
520
521   for (var=variable_list; var && strcmp (var->name, name); var = var->next)
522     ;
523   if (!var)
524     return NULL;
525   if (var->type == VARTYPE_COUNTER && var->value)
526     { /* Use the scratch space allocated by set_var. */
527       char *p = var->value + strlen(var->value)+1;
528       sprintf (p, "%u", var->count);
529       return p;
530     }
531   else
532     return var->value;
533 }
534
535
536 /* Incremente all counter type variables with NAME in their VALUE. */
537 static void
538 inc_counter (const char *name)
539 {
540   VARIABLE var;
541
542   if (!*name)
543     return;
544   for (var=variable_list; var; var = var->next)
545     {
546       if (var->type == VARTYPE_COUNTER
547           && var->value && !strcmp (var->value, name))
548         var->count++;
549     }
550 }
551
552
553 /* Expand variables in LINE and return a new allocated buffer if
554    required.  The function might modify LINE if the expanded version
555    fits into it. */
556 static char *
557 expand_line (char *buffer)
558 {
559   char *line = buffer;
560   char *p, *pend;
561   const char *value;
562   size_t valuelen, n;
563   char *result = NULL;
564
565   while (*line)
566     {
567       p = strchr (line, '$');
568       if (!p)
569         return result; /* nothing more to expand */
570       
571       if (p[1] == '$') /* quoted */
572         {
573           memmove (p, p+1, strlen (p+1)+1);
574           line = p + 1;
575           continue;
576         }
577       for (pend=p+1; *pend && !spacep (pend)
578            && *pend != '$' && *pend != '/'; pend++)
579         ;
580       if (*pend)
581         {
582           int save = *pend;
583           *pend = 0;
584           value = get_var (p+1);
585           *pend = save;
586         }
587       else
588         value = get_var (p+1);
589       if (!value)
590         value = "";
591       valuelen = strlen (value);
592       if (valuelen <= pend - p)
593         {
594           memcpy (p, value, valuelen);
595           p += valuelen;
596           n = pend - p;
597           if (n)
598             memmove (p, p+n, strlen (p+n)+1);
599           line = p;
600         }
601       else
602         {
603           char *src = result? result : buffer;
604           char *dst;
605
606           dst = xmalloc (strlen (src) + valuelen + 1);
607           n = p - src;
608           memcpy (dst, src, n);
609           memcpy (dst + n, value, valuelen);
610           n += valuelen;
611           strcpy (dst + n, pend);
612           line = dst + n;
613           free (result);
614           result = dst;
615         }
616     }
617   return result;
618 }
619
620
621 /* Evaluate COND and return the result. */
622 static int 
623 eval_boolean (const char *cond)
624 {
625   int true = 1;
626
627   for ( ; *cond == '!'; cond++)
628     true = !true;
629   if (!*cond || (*cond == '0' && !cond[1]))
630     return !true;
631   return true;
632 }
633
634
635
636
637 \f
638 static void
639 cmd_let (const char *assign_to, char *arg)
640 {
641   set_var (assign_to, arg);
642 }
643
644
645 static void
646 cmd_echo (const char *assign_to, char *arg)
647 {
648   if (!opt_no_echo)
649     printf ("%s\n", arg);
650 }
651
652 static void
653 cmd_send (const char *assign_to, char *arg)
654 {
655   if (opt_verbose)
656     fprintf (stderr, "sending `%s'\n", arg);
657   write_assuan (server_send_fd, arg); 
658 }
659
660 static void
661 handle_status_line (char *arg)
662 {
663   char *p;
664
665   for (p=arg; *p && !spacep (p); p++)
666     ;
667   if (*p)
668     {
669       int save = *p;
670       *p = 0;
671       inc_counter (arg);
672       *p = save;
673     }
674   else
675     inc_counter (arg);
676 }
677
678 static void
679 cmd_expect_ok (const char *assign_to, char *arg)
680 {
681   if (opt_verbose)
682     fprintf (stderr, "expecting OK\n");
683   do
684     {
685       char *p = read_assuan (server_recv_fd);
686       if (opt_verbose > 1)
687         fprintf (stderr, "got line `%s'\n", recv_line);
688       if (recv_type == LINE_STAT)
689         handle_status_line (p);
690     }
691   while (recv_type != LINE_OK && recv_type != LINE_ERR);
692   if (recv_type != LINE_OK)
693     die ("expected OK but got `%s'", recv_line);
694 }
695
696 static void
697 cmd_expect_err (const char *assign_to, char *arg)
698 {
699   if (opt_verbose)
700     fprintf (stderr, "expecting ERR\n");
701   do
702     {
703       char *p = read_assuan (server_recv_fd);
704       if (opt_verbose > 1)
705         fprintf (stderr, "got line `%s'\n", recv_line);
706       if (recv_type == LINE_STAT)
707         handle_status_line (p);
708     }
709   while (recv_type != LINE_OK && recv_type != LINE_ERR);
710   if (recv_type != LINE_ERR)
711     die ("expected ERR but got `%s'", recv_line);
712 }
713
714 static void
715 cmd_count_status (const char *assign_to, char *arg)
716 {
717   char *p;
718
719   if (!*assign_to || !*arg)
720     die ("syntax error: count-status requires an argument and a variable");
721
722   for (p=arg; *p && !spacep (p); p++)
723     ;
724   if (*p)
725     {
726       for (*p++ = 0; spacep (p); p++)
727         ;
728       if (*p)
729         die ("cmpfiles: syntax error");
730     }
731   set_type_var (assign_to, arg, VARTYPE_COUNTER);
732 }
733
734 static void
735 cmd_openfile (const char *assign_to, char *arg)
736 {
737   int fd;
738   char numbuf[20];
739
740   do 
741     fd = open (arg, O_RDONLY);
742   while (fd == -1 && errno == EINTR);
743   if (fd == -1)
744     die ("error opening `%s': %s", arg, strerror (errno));
745   
746   sprintf (numbuf, "%d", fd);
747   set_type_var (assign_to, numbuf, VARTYPE_FD);
748 }
749
750 static void
751 cmd_createfile (const char *assign_to, char *arg)
752 {
753   int fd;
754   char numbuf[20];
755
756   do 
757     fd = open (arg, O_WRONLY|O_CREAT|O_TRUNC, 0666);
758   while (fd == -1 && errno == EINTR);
759   if (fd == -1)
760     die ("error creating `%s': %s", arg, strerror (errno));
761
762   sprintf (numbuf, "%d", fd);
763   set_type_var (assign_to, numbuf, VARTYPE_FD);
764 }
765
766
767 static void
768 cmd_pipeserver (const char *assign_to, char *arg)
769 {
770   if (!*arg)
771     die ("syntax error: servername missing");
772
773   start_server (arg);
774 }
775
776
777 static void
778 cmd_quit_if(const char *assign_to, char *arg)
779 {
780   if (eval_boolean (arg))
781     exit (0);
782 }
783
784 static void
785 cmd_fail_if(const char *assign_to, char *arg)
786 {
787   if (eval_boolean (arg))
788     exit (1);
789 }
790
791
792 static void
793 cmd_cmpfiles (const char *assign_to, char *arg)
794 {
795   char *p = arg;
796   char *second;
797   FILE *fp1, *fp2;
798   char buffer1[2048]; /* note: both must be of equal size. */
799   char buffer2[2048];
800   size_t nread1, nread2;
801   int rc = 0;
802
803   set_var (assign_to, "0"); 
804   for (p=arg; *p && !spacep (p); p++)
805     ;
806   if (!*p)
807     die ("cmpfiles: syntax error");
808   for (*p++ = 0; spacep (p); p++)
809     ;
810   second = p;
811   for (; *p && !spacep (p); p++)
812     ;
813   if (*p)
814     {
815       for (*p++ = 0; spacep (p); p++)
816         ;
817       if (*p)
818         die ("cmpfiles: syntax error");
819     }
820   
821   fp1 = fopen (arg, "rb");
822   if (!fp1)
823     {
824       err ("can't open `%s': %s", arg, strerror (errno));
825       return;
826     }
827   fp2 = fopen (second, "rb");
828   if (!fp2)
829     {
830       err ("can't open `%s': %s", second, strerror (errno));
831       fclose (fp1);
832       return;
833     }
834   while ( (nread1 = fread (buffer1, 1, sizeof buffer1, fp1)))
835     {
836       if (ferror (fp1))
837         break;
838       nread2 = fread (buffer2, 1, sizeof buffer2, fp2);
839       if (ferror (fp2))
840         break;
841       if (nread1 != nread2 || memcmp (buffer1, buffer2, nread1))
842         {
843           rc = 1;
844           break;
845         }
846     }
847   if (feof (fp1) && feof (fp2) && !rc)
848     {
849       if (opt_verbose)
850         err ("files match");
851       set_var (assign_to, "1"); 
852     }
853   else if (!rc)
854     err ("cmpfiles: read error: %s", strerror (errno));
855   else
856     err ("cmpfiles: mismatch");
857   fclose (fp1);
858   fclose (fp2);
859 }
860
861 static void
862 cmd_getenv (const char *assign_to, char *arg)
863 {
864   const char *s;
865   s = *arg? getenv (arg):"";
866   set_var (assign_to, s? s:"");
867 }
868
869
870
871 \f
872 /* Process the current script line LINE. */
873 static int
874 interpreter (char *line)
875 {
876   static struct {
877     const char *name;
878     void (*fnc)(const char*, char*);
879   } cmdtbl[] = {
880     { "let"       , cmd_let },
881     { "echo"      , cmd_echo },
882     { "send"      , cmd_send },
883     { "expect-ok" , cmd_expect_ok },
884     { "expect-err", cmd_expect_err },
885     { "count-status", cmd_count_status },
886     { "openfile"  , cmd_openfile },
887     { "createfile", cmd_createfile },
888     { "pipeserver", cmd_pipeserver },
889     { "quit"      , NULL },
890     { "quit-if"   , cmd_quit_if },
891     { "fail-if"   , cmd_fail_if },
892     { "cmpfiles"  , cmd_cmpfiles },
893     { "getenv"    , cmd_getenv },
894     { NULL }
895   };
896   char *p, *save_p;
897   int i, save_c;
898   char *stmt = NULL;
899   char *assign_to = NULL;
900   char *must_free = NULL;
901
902   for ( ;spacep (line); line++)
903     ;
904   if (!*line || *line == '#')
905     return 0; /* empty or comment */
906   p = expand_line (line);
907   if (p)
908     {
909       must_free = p;
910       line = p;
911       for ( ;spacep (line); line++)
912         ;
913       if (!*line || *line == '#')
914         {
915           free (must_free);
916           return 0; /* empty or comment */
917         }
918     }
919   for (p=line; *p && !spacep (p) && *p != '='; p++)
920     ;
921   if (*p == '=')
922     {
923       *p = 0;
924       assign_to = line;
925     }
926   else if (*p)
927     {
928       for (*p++ = 0; spacep (p); p++)
929         ;
930       if (*p == '=')
931         assign_to = line;
932     }
933   if (!*line)
934     die ("syntax error");
935   stmt = line;
936   save_c = 0;
937   save_p = NULL;
938   if (assign_to)
939     { /* this is an assignment */
940       for (p++; spacep (p); p++)
941         ;
942       if (!*p)
943         {
944           unset_var (assign_to);
945           free (must_free);
946           return 0;
947         }
948       stmt = p;
949       for (; *p && !spacep (p); p++)
950         ;
951       if (*p)
952         {
953           save_p = p;
954           save_c = *p;
955           for (*p++ = 0; spacep (p);  p++)
956             ;
957         }
958     }
959   for (i=0; cmdtbl[i].name && strcmp (stmt, cmdtbl[i].name); i++)
960     ;
961   if (!cmdtbl[i].name)
962     {
963       if (!assign_to)
964         die ("invalid statement `%s'\n", stmt);
965       if (save_p)
966         *save_p = save_c;
967       set_var (assign_to, stmt);
968       free (must_free);
969       return 0;
970     }
971
972   if (cmdtbl[i].fnc)
973     cmdtbl[i].fnc (assign_to, p);
974   free (must_free);
975   return cmdtbl[i].fnc? 0:1;
976 }
977
978
979
980 int
981 main (int argc, char **argv)
982 {
983   char buffer[2048];
984   char *p, *pend;
985
986   if (!argc)
987     invocation_name = "asschk";
988   else
989     {
990       invocation_name = *argv++;
991       argc--;
992       p = strrchr (invocation_name, '/');
993       if (p)
994         invocation_name = p+1;
995     }
996
997
998   set_var ("?","1"); /* defaults to true */
999
1000   for (; argc; argc--, argv++)
1001     {
1002       p = *argv;
1003       if (*p != '-')
1004         break;
1005       if (!strcmp (p, "--verbose"))
1006         opt_verbose++;
1007       else if (!strcmp (p, "--no-echo"))
1008         opt_no_echo++;
1009       else if (*p == '-' && p[1] == 'D')
1010         {
1011           p += 2;
1012           pend = strchr (p, '=');
1013           if (pend)
1014             {
1015               int tmp = *pend;
1016               *pend = 0;
1017               set_var (p, pend+1);
1018               *pend = tmp;
1019             }
1020           else
1021             set_var (p, "1");
1022         }
1023       else if (*p == '-' && p[1] == '-' && !p[2])
1024         {
1025           argc--; argv++;
1026           break;
1027         }
1028       else
1029         break;
1030     }
1031   if (argc)
1032     die ("usage: asschk [--verbose] {-D<name>[=<value>]}");
1033
1034
1035   while (fgets (buffer, sizeof buffer, stdin))
1036     {
1037       p = strchr (buffer,'\n');
1038       if (!p)
1039         die ("incomplete script line");
1040       *p = 0;
1041       if (interpreter (buffer))
1042         break;
1043       fflush (stdout);
1044     }
1045   return 0;
1046 }
1047