1a11ead337d6daa6dd565b190be07daf71e25998
[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
271   while (nleft > 0)
272     {
273       int n;
274
275       if (pending_len)
276         {
277           if (pending_len >= nleft)
278             die ("received line too large");
279           memcpy (buf, pending, pending_len);
280           n = pending_len;
281           pending_len = 0;
282         }
283       else
284         n = read (fd, buf, nleft);
285       if (n < 0)
286         {
287           if (errno == EINTR)
288             continue;
289           die ("reading fd %d failed: %s", fd, strerror (errno));
290         }
291       else if (!n)
292         die ("received incomplete line on fd %d", fd);
293       p = buf;
294       nleft -= n;
295       buf += n;
296       
297       for (; n && *p != '\n'; n--, p++)
298         ;
299       if (n)
300         {
301           if (n>1)
302             {
303               n--;
304               memcpy (pending, p + 1, n);
305               pending_len = n;
306             }
307           *p = '\0';
308           break;
309         }
310     }
311   if (!nleft)
312     die ("received line too large");
313
314   p = recv_line;
315   if (p[0] == 'O' && p[1] == 'K' && (p[2] == ' ' || !p[2]))
316     {
317       recv_type = LINE_OK;
318       p += 3;
319     }
320   else if (p[0] == 'E' && p[1] == 'R' && p[2] == 'R'
321            && (p[3] == ' ' || !p[3]))
322     {
323       recv_type = LINE_ERR;
324       p += 4;
325     }
326   else if (p[0] == 'S' && (p[1] == ' ' || !p[1]))
327     {
328       recv_type = LINE_STAT;
329       p += 2;
330     }
331   else if (p[0] == 'D' && p[1] == ' ')
332     {
333       recv_type = LINE_DATA;
334       p += 2;
335     }
336   else if (p[0] == 'E' && p[1] == 'N' &&  p[2] == 'D' && !p[3])
337     {
338       recv_type = LINE_END;
339       p += 3;
340     }
341   else 
342     die ("invalid line type (%.5s)", p);
343
344   return p;
345 }
346
347 /* Write LINE to the server using FD.  It is expected that the line
348    contains the terminating linefeed as last character. */
349 static void
350 write_assuan (int fd, const char *line)
351 {
352   char buffer[1026];
353   size_t n = strlen (line);
354
355   if (n > 1024)
356     die ("line too long for Assuan protocol");
357   strcpy (buffer, line);
358   if (!n || buffer[n-1] != '\n')
359     buffer[n++] = '\n';
360
361   if (writen (fd, buffer, n))
362       die ("sending line to %d failed: %s", fd, strerror (errno));
363 }
364
365
366 /* Start the server with path PGMNAME and connect its stdout and
367    strerr to a newly created pipes; the file descriptors are then
368    store in the gloabl variables SERVER_SEND_FD and
369    SERVER_RECV_FD. The initial handcheck is performed.*/
370 static void
371 start_server (const char *pgmname)
372 {
373   int rp[2];
374   int wp[2];
375   pid_t pid;
376
377   if (pipe (rp) < 0)
378     die ("pipe creation failed: %s", strerror (errno));
379   if (pipe (wp) < 0)
380     die ("pipe creation failed: %s", strerror (errno));
381
382   fflush (stdout);
383   fflush (stderr);
384   pid = fork ();
385   if (pid < 0)
386     die ("fork failed");
387
388   if (!pid)
389     {
390       const char *arg0;
391
392       arg0 = strrchr (pgmname, '/');
393       if (arg0)
394         arg0++;
395       else
396         arg0 = pgmname;
397
398       if (wp[0] != STDIN_FILENO)
399         {
400           if (dup2 (wp[0], STDIN_FILENO) == -1)
401               die ("dup2 failed in child: %s", strerror (errno));
402           close (wp[0]);
403         }
404       if (rp[1] != STDOUT_FILENO)
405         {
406           if (dup2 (rp[1], STDOUT_FILENO) == -1)
407               die ("dup2 failed in child: %s", strerror (errno));
408           close (rp[1]);
409         }
410       if (!opt_verbose)
411         {
412           int fd = open ("/dev/null", O_WRONLY);
413           if (fd == -1)
414             die ("can't open `/dev/null': %s", strerror (errno));
415           if (dup2 (fd, STDERR_FILENO) == -1)
416             die ("dup2 failed in child: %s", strerror (errno));
417           close (fd);
418         }
419
420       close (wp[1]);
421       close (rp[0]);
422       execl (pgmname, arg0, "--server", NULL); 
423       die ("exec failed for `%s': %s", pgmname, strerror (errno));
424     }
425   close (wp[0]);
426   close (rp[1]);
427   server_send_fd = wp[1];
428   server_recv_fd = rp[0];
429
430   read_assuan (server_recv_fd);
431   if (recv_type != LINE_OK)
432     die ("no greating message");
433 }
434
435
436
437
438 \f
439 /* Script intepreter. */
440
441 static void
442 unset_var (const char *name)
443 {
444   VARIABLE var;
445
446   for (var=variable_list; var && strcmp (var->name, name); var = var->next)
447     ;
448   if (!var)
449     return;
450 /*    fprintf (stderr, "unsetting `%s'\n", name); */
451
452   if (var->type == VARTYPE_FD && var->value)
453     {
454       int fd;
455
456       fd = atoi (var->value);
457       if (fd != -1 && fd != 0 && fd != 1 && fd != 2)
458           close (fd);
459     }
460
461   free (var->value);
462   var->value = NULL;
463   var->type = 0;
464   var->count = 0;
465 }
466
467
468 static void
469 set_type_var (const char *name, const char *value, VARTYPE type)
470 {
471   VARIABLE var;
472
473   if (!name)
474     name = "?"; 
475   for (var=variable_list; var && strcmp (var->name, name); var = var->next)
476     ;
477   if (!var)
478     {
479       var = xcalloc (1, sizeof *var + strlen (name));
480       strcpy (var->name, name);
481       var->next = variable_list;
482       variable_list = var;
483     }
484   else
485     free (var->value);
486
487   if (var->type == VARTYPE_FD && var->value)
488     {
489       int fd;
490
491       fd = atoi (var->value);
492       if (fd != -1 && fd != 0 && fd != 1 && fd != 2)
493           close (fd);
494     }
495   
496   var->type = type;
497   var->count = 0;
498   if (var->type == VARTYPE_COUNTER)
499     {
500       /* We need some extra sapce as scratch area for get_var. */
501       var->value = xmalloc (strlen (value) + 1 + 20);
502       strcpy (var->value, value);
503     }
504   else
505     var->value = xstrdup (value);
506 }
507
508 static void
509 set_var (const char *name, const char *value)
510 {
511   set_type_var (name, value, 0);
512 }
513
514
515 static const char *
516 get_var (const char *name)
517 {
518   VARIABLE var;
519
520   for (var=variable_list; var && strcmp (var->name, name); var = var->next)
521     ;
522   if (!var)
523     return NULL;
524   if (var->type == VARTYPE_COUNTER && var->value)
525     { /* Use the scratch space allocated by set_var. */
526       char *p = var->value + strlen(var->value)+1;
527       sprintf (p, "%u", var->count);
528       return p;
529     }
530   else
531     return var->value;
532 }
533
534
535 /* Incremente all counter type variables with NAME in their VALUE. */
536 static void
537 inc_counter (const char *name)
538 {
539   VARIABLE var;
540
541   if (!*name)
542     return;
543   for (var=variable_list; var; var = var->next)
544     {
545       if (var->type == VARTYPE_COUNTER
546           && var->value && !strcmp (var->value, name))
547         var->count++;
548     }
549 }
550
551
552 /* Expand variables in LINE and return a new allocated buffer if
553    required.  The function might modify LINE if the expanded version
554    fits into it. */
555 static char *
556 expand_line (char *buffer)
557 {
558   char *line = buffer;
559   char *p, *pend;
560   const char *value;
561   size_t valuelen, n;
562   char *result = NULL;
563
564   while (*line)
565     {
566       p = strchr (line, '$');
567       if (!p)
568         return result; /* nothing more to expand */
569       
570       if (p[1] == '$') /* quoted */
571         {
572           memmove (p, p+1, strlen (p+1)+1);
573           line = p + 1;
574           continue;
575         }
576       for (pend=p+1; *pend && !spacep (pend)
577            && *pend != '$' && *pend != '/'; pend++)
578         ;
579       if (*pend)
580         {
581           int save = *pend;
582           *pend = 0;
583           value = get_var (p+1);
584           *pend = save;
585         }
586       else
587         value = get_var (p+1);
588       if (!value)
589         value = "";
590       valuelen = strlen (value);
591       if (valuelen <= pend - p)
592         {
593           memcpy (p, value, valuelen);
594           p += valuelen;
595           n = pend - p;
596           if (n)
597             memmove (p, p+n, strlen (p+n)+1);
598           line = p;
599         }
600       else
601         {
602           char *src = result? result : buffer;
603           char *dst;
604
605           dst = xmalloc (strlen (src) + valuelen + 1);
606           n = p - src;
607           memcpy (dst, src, n);
608           memcpy (dst + n, value, valuelen);
609           n += valuelen;
610           strcpy (dst + n, pend);
611           line = dst + n;
612           free (result);
613           result = dst;
614         }
615     }
616   return result;
617 }
618
619
620 /* Evaluate COND and return the result. */
621 static int 
622 eval_boolean (const char *cond)
623 {
624   int true = 1;
625
626   for ( ; *cond == '!'; cond++)
627     true = !true;
628   if (!*cond || (*cond == '0' && !cond[1]))
629     return !true;
630   return true;
631 }
632
633
634
635
636 \f
637 static void
638 cmd_let (const char *assign_to, char *arg)
639 {
640   set_var (assign_to, arg);
641 }
642
643
644 static void
645 cmd_echo (const char *assign_to, char *arg)
646 {
647   if (!opt_no_echo)
648     printf ("%s\n", arg);
649 }
650
651 static void
652 cmd_send (const char *assign_to, char *arg)
653 {
654   if (opt_verbose)
655     fprintf (stderr, "sending `%s'\n", arg);
656   write_assuan (server_send_fd, arg); 
657 }
658
659 static void
660 handle_status_line (char *arg)
661 {
662   char *p;
663
664   for (p=arg; *p && !spacep (p); p++)
665     ;
666   if (*p)
667     {
668       int save = *p;
669       *p = 0;
670       inc_counter (arg);
671       *p = save;
672     }
673   else
674     inc_counter (arg);
675 }
676
677 static void
678 cmd_expect_ok (const char *assign_to, char *arg)
679 {
680   if (opt_verbose)
681     fprintf (stderr, "expecting OK\n");
682   do
683     {
684       char *p = read_assuan (server_recv_fd);
685       if (opt_verbose > 1)
686         fprintf (stderr, "got line `%s'\n", recv_line);
687       if (recv_type == LINE_STAT)
688         handle_status_line (p);
689     }
690   while (recv_type != LINE_OK && recv_type != LINE_ERR);
691   if (recv_type != LINE_OK)
692     die ("expected OK but got `%s'", recv_line);
693 }
694
695 static void
696 cmd_expect_err (const char *assign_to, char *arg)
697 {
698   if (opt_verbose)
699     fprintf (stderr, "expecting ERR\n");
700   do
701     {
702       char *p = read_assuan (server_recv_fd);
703       if (opt_verbose > 1)
704         fprintf (stderr, "got line `%s'\n", recv_line);
705       if (recv_type == LINE_STAT)
706         handle_status_line (p);
707     }
708   while (recv_type != LINE_OK && recv_type != LINE_ERR);
709   if (recv_type != LINE_ERR)
710     die ("expected ERR but got `%s'", recv_line);
711 }
712
713 static void
714 cmd_count_status (const char *assign_to, char *arg)
715 {
716   char *p;
717
718   if (!*assign_to || !*arg)
719     die ("syntax error: count-status requires an argument and a variable");
720
721   for (p=arg; *p && !spacep (p); p++)
722     ;
723   if (*p)
724     {
725       for (*p++ = 0; spacep (p); p++)
726         ;
727       if (*p)
728         die ("cmpfiles: syntax error");
729     }
730   set_type_var (assign_to, arg, VARTYPE_COUNTER);
731 }
732
733 static void
734 cmd_openfile (const char *assign_to, char *arg)
735 {
736   int fd;
737   char numbuf[20];
738
739   do 
740     fd = open (arg, O_RDONLY);
741   while (fd == -1 && errno == EINTR);
742   if (fd == -1)
743     die ("error opening `%s': %s", arg, strerror (errno));
744   
745   sprintf (numbuf, "%d", fd);
746   set_type_var (assign_to, numbuf, VARTYPE_FD);
747 }
748
749 static void
750 cmd_createfile (const char *assign_to, char *arg)
751 {
752   int fd;
753   char numbuf[20];
754
755   do 
756     fd = open (arg, O_WRONLY|O_CREAT|O_TRUNC, 0666);
757   while (fd == -1 && errno == EINTR);
758   if (fd == -1)
759     die ("error creating `%s': %s", arg, strerror (errno));
760
761   sprintf (numbuf, "%d", fd);
762   set_type_var (assign_to, numbuf, VARTYPE_FD);
763 }
764
765
766 static void
767 cmd_pipeserver (const char *assign_to, char *arg)
768 {
769   if (!*arg)
770     die ("syntax error: servername missing");
771
772   start_server (arg);
773 }
774
775
776 static void
777 cmd_quit_if(const char *assign_to, char *arg)
778 {
779   if (eval_boolean (arg))
780     exit (0);
781 }
782
783 static void
784 cmd_fail_if(const char *assign_to, char *arg)
785 {
786   if (eval_boolean (arg))
787     exit (1);
788 }
789
790
791 static void
792 cmd_cmpfiles (const char *assign_to, char *arg)
793 {
794   char *p = arg;
795   char *second;
796   FILE *fp1, *fp2;
797   char buffer1[2048]; /* note: both must be of equal size. */
798   char buffer2[2048];
799   size_t nread1, nread2;
800   int rc = 0;
801
802   set_var (assign_to, "0"); 
803   for (p=arg; *p && !spacep (p); p++)
804     ;
805   if (!*p)
806     die ("cmpfiles: syntax error");
807   for (*p++ = 0; spacep (p); p++)
808     ;
809   second = p;
810   for (; *p && !spacep (p); p++)
811     ;
812   if (*p)
813     {
814       for (*p++ = 0; spacep (p); p++)
815         ;
816       if (*p)
817         die ("cmpfiles: syntax error");
818     }
819   
820   fp1 = fopen (arg, "rb");
821   if (!fp1)
822     {
823       err ("can't open `%s': %s", arg, strerror (errno));
824       return;
825     }
826   fp2 = fopen (second, "rb");
827   if (!fp2)
828     {
829       err ("can't open `%s': %s", second, strerror (errno));
830       fclose (fp1);
831       return;
832     }
833   while ( (nread1 = fread (buffer1, 1, sizeof buffer1, fp1)))
834     {
835       if (ferror (fp1))
836         break;
837       nread2 = fread (buffer2, 1, sizeof buffer2, fp2);
838       if (ferror (fp2))
839         break;
840       if (nread1 != nread2 || memcmp (buffer1, buffer2, nread1))
841         {
842           rc = 1;
843           break;
844         }
845     }
846   if (feof (fp1) && feof (fp2) && !rc)
847     {
848       if (opt_verbose)
849         err ("files match");
850       set_var (assign_to, "1"); 
851     }
852   else if (!rc)
853     err ("cmpfiles: read error: %s", strerror (errno));
854   else
855     err ("cmpfiles: mismatch");
856   fclose (fp1);
857   fclose (fp2);
858 }
859
860 static void
861 cmd_getenv (const char *assign_to, char *arg)
862 {
863   const char *s;
864   s = *arg? getenv (arg):"";
865   set_var (assign_to, s? s:"");
866 }
867
868
869
870 \f
871 /* Process the current script line LINE. */
872 static int
873 interpreter (char *line)
874 {
875   static struct {
876     const char *name;
877     void (*fnc)(const char*, char*);
878   } cmdtbl[] = {
879     { "let"       , cmd_let },
880     { "echo"      , cmd_echo },
881     { "send"      , cmd_send },
882     { "expect-ok" , cmd_expect_ok },
883     { "expect-err", cmd_expect_err },
884     { "count-status", cmd_count_status },
885     { "openfile"  , cmd_openfile },
886     { "createfile", cmd_createfile },
887     { "pipeserver", cmd_pipeserver },
888     { "quit"      , NULL },
889     { "quit-if"   , cmd_quit_if },
890     { "fail-if"   , cmd_fail_if },
891     { "cmpfiles"  , cmd_cmpfiles },
892     { "getenv"    , cmd_getenv },
893     { NULL }
894   };
895   char *p, *save_p;
896   int i, save_c;
897   char *stmt = NULL;
898   char *assign_to = NULL;
899   char *must_free = NULL;
900
901   for ( ;spacep (line); line++)
902     ;
903   if (!*line || *line == '#')
904     return 0; /* empty or comment */
905   p = expand_line (line);
906   if (p)
907     {
908       must_free = p;
909       line = p;
910       for ( ;spacep (line); line++)
911         ;
912       if (!*line || *line == '#')
913         {
914           free (must_free);
915           return 0; /* empty or comment */
916         }
917     }
918   for (p=line; *p && !spacep (p) && *p != '='; p++)
919     ;
920   if (*p == '=')
921     {
922       *p = 0;
923       assign_to = line;
924     }
925   else if (*p)
926     {
927       for (*p++ = 0; spacep (p); p++)
928         ;
929       if (*p == '=')
930         assign_to = line;
931     }
932   if (!*line)
933     die ("syntax error");
934   stmt = line;
935   save_c = 0;
936   save_p = NULL;
937   if (assign_to)
938     { /* this is an assignment */
939       for (p++; spacep (p); p++)
940         ;
941       if (!*p)
942         {
943           unset_var (assign_to);
944           free (must_free);
945           return 0;
946         }
947       stmt = p;
948       for (; *p && !spacep (p); p++)
949         ;
950       if (*p)
951         {
952           save_p = p;
953           save_c = *p;
954           for (*p++ = 0; spacep (p);  p++)
955             ;
956         }
957     }
958   for (i=0; cmdtbl[i].name && strcmp (stmt, cmdtbl[i].name); i++)
959     ;
960   if (!cmdtbl[i].name)
961     {
962       if (!assign_to)
963         die ("invalid statement `%s'\n", stmt);
964       if (save_p)
965         *save_p = save_c;
966       set_var (assign_to, stmt);
967       free (must_free);
968       return 0;
969     }
970
971   if (cmdtbl[i].fnc)
972     cmdtbl[i].fnc (assign_to, p);
973   free (must_free);
974   return cmdtbl[i].fnc? 0:1;
975 }
976
977
978
979 int
980 main (int argc, char **argv)
981 {
982   char buffer[2048];
983   char *p, *pend;
984
985   if (!argc)
986     invocation_name = "asschk";
987   else
988     {
989       invocation_name = *argv++;
990       argc--;
991       p = strrchr (invocation_name, '/');
992       if (p)
993         invocation_name = p+1;
994     }
995
996
997   set_var ("?","1"); /* defaults to true */
998
999   for (; argc; argc--, argv++)
1000     {
1001       p = *argv;
1002       if (*p != '-')
1003         break;
1004       if (!strcmp (p, "--verbose"))
1005         opt_verbose++;
1006       else if (!strcmp (p, "--no-echo"))
1007         opt_no_echo++;
1008       else if (*p == '-' && p[1] == 'D')
1009         {
1010           p += 2;
1011           pend = strchr (p, '=');
1012           if (pend)
1013             {
1014               int tmp = *pend;
1015               *pend = 0;
1016               set_var (p, pend+1);
1017               *pend = tmp;
1018             }
1019           else
1020             set_var (p, "1");
1021         }
1022       else if (*p == '-' && p[1] == '-' && !p[2])
1023         {
1024           argc--; argv++;
1025           break;
1026         }
1027       else
1028         break;
1029     }
1030   if (argc)
1031     die ("usage: asschk [--verbose] {-D<name>[=<value>]}");
1032
1033
1034   while (fgets (buffer, sizeof buffer, stdin))
1035     {
1036       p = strchr (buffer,'\n');
1037       if (!p)
1038         die ("incomplete script line");
1039       *p = 0;
1040       if (interpreter (buffer))
1041         break;
1042       fflush (stdout);
1043     }
1044   return 0;
1045 }
1046