7b7321675192456c413984fc661df74125e73ed3
[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.  To
34    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 */
100
101 #include <stdio.h>
102 #include <stdlib.h>
103 #include <string.h>
104 #include <errno.h>
105 #include <stdarg.h>
106 #include <assert.h>
107 #include <unistd.h>
108 #include <fcntl.h>
109
110 #if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 5 )
111 # define ATTR_PRINTF(f,a)  __attribute__ ((format (printf,f,a)))
112 #else
113 # define ATTR_PRINTF(f,a)
114 #endif
115
116 #define spacep(p) (*(p) == ' ' || *(p) == '\t')
117
118 #define MAX_LINELEN 2048
119
120 typedef enum {
121   LINE_OK = 0,
122   LINE_ERR,
123   LINE_STAT,
124   LINE_DATA,
125   LINE_END,
126 } LINETYPE;
127
128 typedef enum {
129   VARTYPE_SIMPLE = 0,
130   VARTYPE_FD,
131   VARTYPE_COUNTER
132 } VARTYPE;
133
134
135 struct variable_s {
136   struct variable_s *next;
137   VARTYPE type;
138   unsigned int count;
139   char *value;
140   char name[1];
141 };
142 typedef struct variable_s *VARIABLE;
143
144
145 static void die (const char *format, ...)  ATTR_PRINTF(1,2);
146
147
148 /* Name of this program to be printed in error messages. */
149 static const char *invocation_name;
150
151 /* Talk a bit about what is going on. */
152 static int opt_verbose;
153
154 /* Option to ignore the echo command. */
155 static int opt_no_echo;
156
157 /* File descriptors used to communicate with the current server. */
158 static int server_send_fd = -1;
159 static int server_recv_fd = -1;
160
161 /* The Assuan protocol limits the line length to 1024, so we can
162    safely use a (larger) buffer.  The buffer is filled using the
163    read_assuan(). */
164 static char recv_line[MAX_LINELEN];
165 /* Tell the status of the current line. */
166 static LINETYPE recv_type;
167
168 /* This is our variable storage. */
169 static VARIABLE variable_list;
170
171 \f
172 static void
173 die (const char *format, ...)
174 {
175   va_list arg_ptr;
176
177   fflush (stdout);
178   fprintf (stderr, "%s: ", invocation_name);
179
180   va_start (arg_ptr, format);
181   vfprintf (stderr, format, arg_ptr);
182   va_end (arg_ptr);
183   putc ('\n', stderr);
184
185   exit (1);
186 }
187
188 static void
189 err (const char *format, ...)
190 {
191   va_list arg_ptr;
192
193   fflush (stdout);
194   fprintf (stderr, "%s: ", invocation_name);
195
196   va_start (arg_ptr, format);
197   vfprintf (stderr, format, arg_ptr);
198   va_end (arg_ptr);
199   putc ('\n', stderr);
200 }
201
202 static void *
203 xmalloc (size_t n)
204 {
205   void *p = malloc (n);
206   if (!p)
207     die ("out of core");
208   return p;
209 }
210
211 static void *
212 xcalloc (size_t n, size_t m)
213 {
214   void *p = calloc (n, m);
215   if (!p)
216     die ("out of core");
217   return p;
218 }
219
220 static char *
221 xstrdup (const char *s)
222 {
223   char *p = xmalloc (strlen (s)+1);
224   strcpy (p, s);
225   return p;
226 }
227
228
229 /* Write LENGTH bytes from BUFFER to FD. */
230 static int
231 writen (int fd, const char *buffer, size_t length)
232 {
233   while (length)
234     {
235       int nwritten = write (fd, buffer, length);
236       
237       if (nwritten < 0)
238         {
239           if (errno == EINTR)
240             continue;
241           return -1; /* write error */
242         }
243       length -= nwritten;
244       buffer += nwritten;
245     }
246   return 0;  /* okay */
247 }
248
249
250
251 \f
252 /* Assuan specific stuff. */
253
254 /* Read a line from FD, store it in the global recv_line, analyze the
255    type and store that in recv_type.  The function terminates on a
256    communication error.  Returns a pointer into the inputline to the
257    first byte of the arguments.  The parsing is very strict to match
258    excalty what we want to send. */
259 static char *
260 read_assuan (int fd)
261 {
262   static char pending[MAX_LINELEN];
263   static size_t pending_len;
264   size_t nleft = sizeof recv_line;
265   char *buf = recv_line;
266   char *p;
267   int nread = 0;
268
269   while (nleft > 0)
270     {
271       int n;
272
273       if (pending_len)
274         {
275           if (pending_len >= nleft)
276             die ("received line too large");
277           memcpy (buf, pending, pending_len);
278           n = pending_len;
279           pending_len = 0;
280         }
281       else
282         n = read (fd, buf, nleft);
283       if (n < 0)
284         {
285           if (errno == EINTR)
286             continue;
287           die ("reading fd %d failed: %s", fd, strerror (errno));
288         }
289       else if (!n)
290         die ("received incomplete line on fd %d", fd);
291       p = buf;
292       nleft -= n;
293       buf += n;
294       nread += n;
295       
296       for (; n && *p != '\n'; n--, p++)
297         ;
298       if (n)
299         {
300           if (n>1)
301             {
302               n--;
303               memcpy (pending, p+1, n);
304               pending_len = n;
305             }
306           break;
307         }
308     }
309   if (!nleft)
310     die ("received line too large");
311   assert (nread>0);
312   recv_line[nread-1] = 0;
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       execl (pgmname, arg0, "--server", NULL); 
421       die ("exec failed for `%s': %s", pgmname, strerror (errno));
422     }
423   close (wp[0]);
424   close (rp[1]);
425   server_send_fd = wp[1];
426   server_recv_fd = rp[0];
427
428   read_assuan (server_recv_fd);
429   if (recv_type != LINE_OK)
430     die ("no greating message");
431 }
432
433
434
435
436 \f
437 /* Script intepreter. */
438
439 static void
440 unset_var (const char *name)
441 {
442   VARIABLE var;
443
444   for (var=variable_list; var && strcmp (var->name, name); var = var->next)
445     ;
446   if (!var)
447     return;
448 /*    fprintf (stderr, "unsetting `%s'\n", name); */
449
450   if (var->type == VARTYPE_FD && var->value)
451     {
452       int fd;
453
454       fd = atoi (var->value);
455       if (fd != -1 && fd != 0 && fd != 1 && fd != 2)
456           close (fd);
457     }
458
459   free (var->value);
460   var->value = NULL;
461   var->type = 0;
462   var->count = 0;
463 }
464
465
466 static void
467 set_type_var (const char *name, const char *value, VARTYPE type)
468 {
469   VARIABLE var;
470
471   if (!name)
472     name = "?"; 
473   for (var=variable_list; var && strcmp (var->name, name); var = var->next)
474     ;
475   if (!var)
476     {
477       var = xcalloc (1, sizeof *var + strlen (name));
478       strcpy (var->name, name);
479       var->next = variable_list;
480       variable_list = var;
481     }
482   else
483     free (var->value);
484
485   if (var->type == VARTYPE_FD && var->value)
486     {
487       int fd;
488
489       fd = atoi (var->value);
490       if (fd != -1 && fd != 0 && fd != 1 && fd != 2)
491           close (fd);
492     }
493   
494   var->type = type;
495   var->count = 0;
496   if (var->type == VARTYPE_COUNTER)
497     {
498       /* We need some extra sapce as scratch area for get_var. */
499       var->value = xmalloc (strlen (value) + 1 + 20);
500       strcpy (var->value, value);
501     }
502   else
503     var->value = xstrdup (value);
504 }
505
506 static void
507 set_var (const char *name, const char *value)
508 {
509   set_type_var (name, value, 0);
510 }
511
512
513 static const char *
514 get_var (const char *name)
515 {
516   VARIABLE var;
517
518   for (var=variable_list; var && strcmp (var->name, name); var = var->next)
519     ;
520   if (!var)
521     return NULL;
522   if (var->type == VARTYPE_COUNTER && var->value)
523     { /* Use the scratch space allocated by set_var. */
524       char *p = var->value + strlen(var->value)+1;
525       sprintf (p, "%u", var->count);
526       return p;
527     }
528   else
529     return var->value;
530 }
531
532
533 /* Incremente all counter type variables with NAME in their VALUE. */
534 static void
535 inc_counter (const char *name)
536 {
537   VARIABLE var;
538
539   if (!*name)
540     return;
541   for (var=variable_list; var; var = var->next)
542     {
543       if (var->type == VARTYPE_COUNTER
544           && var->value && !strcmp (var->value, name))
545         var->count++;
546     }
547 }
548
549
550 /* Expand variables in LINE and return a new allocated buffer if
551    required.  The function might modify LINE if the expanded version
552    fits into it. */
553 static char *
554 expand_line (char *buffer)
555 {
556   char *line = buffer;
557   char *p, *pend;
558   const char *value;
559   size_t valuelen, n;
560   char *result = NULL;
561
562   while (*line)
563     {
564       p = strchr (line, '$');
565       if (!p)
566         return result; /* nothing more to expand */
567       
568       if (p[1] == '$') /* quoted */
569         {
570           memmove (p, p+1, strlen (p+1)+1);
571           line = p + 1;
572           continue;
573         }
574       for (pend=p+1; *pend && !spacep (pend) && *pend != '$'; pend++)
575         ;
576       if (*pend)
577         {
578           int save = *pend;
579           *pend = 0;
580           value = get_var (p+1);
581           *pend = save;
582         }
583       else
584         value = get_var (p+1);
585       if (!value)
586         value = "";
587       valuelen = strlen (value);
588       if (valuelen <= pend - p)
589         {
590           memcpy (p, value, valuelen);
591           p += valuelen;
592           n = pend - p;
593           if (n)
594             memmove (p, p+n, strlen (p+n)+1);
595           line = p;
596         }
597       else
598         {
599           char *src = result? result : buffer;
600           char *dst;
601
602           dst = xmalloc (strlen (src) + valuelen + 1);
603           n = p - src;
604           memcpy (dst, src, n);
605           memcpy (dst + n, value, valuelen);
606           n += valuelen;
607           strcpy (dst + n, pend);
608           line = dst + n;
609           free (result);
610           result = dst;
611         }
612     }
613   return result;
614 }
615
616
617 /* Evaluate COND and return the result. */
618 static int 
619 eval_boolean (const char *cond)
620 {
621   int true = 1;
622
623   for ( ; *cond == '!'; cond++)
624     true = !true;
625   if (!*cond || (*cond == '0' && !cond[1]))
626     return !true;
627   return true;
628 }
629
630
631
632
633 \f
634 static void
635 cmd_let (const char *assign_to, char *arg)
636 {
637   set_var (assign_to, arg);
638 }
639
640
641 static void
642 cmd_echo (const char *assign_to, char *arg)
643 {
644   if (!opt_no_echo)
645     printf ("%s\n", arg);
646 }
647
648 static void
649 cmd_send (const char *assign_to, char *arg)
650 {
651   if (opt_verbose)
652     fprintf (stderr, "sending `%s'\n", arg);
653   write_assuan (server_send_fd, arg); 
654 }
655
656 static void
657 handle_status_line (char *arg)
658 {
659   char *p;
660
661   for (p=arg; *p && !spacep (p); p++)
662     ;
663   if (*p)
664     {
665       int save = *p;
666       *p = 0;
667       inc_counter (arg);
668       *p = save;
669     }
670   else
671     inc_counter (arg);
672 }
673
674 static void
675 cmd_expect_ok (const char *assign_to, char *arg)
676 {
677   if (opt_verbose)
678     fprintf (stderr, "expecting OK\n");
679   do
680     {
681       char *p = read_assuan (server_recv_fd);
682       if (opt_verbose > 1)
683         fprintf (stderr, "got line `%s'\n", recv_line);
684       if (recv_type == LINE_STAT)
685         handle_status_line (p);
686     }
687   while (recv_type != LINE_OK && recv_type != LINE_ERR);
688   if (recv_type != LINE_OK)
689     die ("expected OK but got `%s'", recv_line);
690 }
691
692 static void
693 cmd_expect_err (const char *assign_to, char *arg)
694 {
695   if (opt_verbose)
696     fprintf (stderr, "expecting ERR\n");
697   do
698     {
699       char *p = read_assuan (server_recv_fd);
700       if (opt_verbose > 1)
701         fprintf (stderr, "got line `%s'\n", recv_line);
702       if (recv_type == LINE_STAT)
703         handle_status_line (p);
704     }
705   while (recv_type != LINE_OK && recv_type != LINE_ERR);
706   if (recv_type != LINE_ERR)
707     die ("expected ERR but got `%s'", recv_line);
708 }
709
710 static void
711 cmd_count_status (const char *assign_to, char *arg)
712 {
713   char *p;
714
715   if (!*assign_to || !*arg)
716     die ("syntax error: count-status requires an argument and a variable");
717
718   for (p=arg; *p && !spacep (p); p++)
719     ;
720   if (*p)
721     {
722       for (*p++ = 0; spacep (p); p++)
723         ;
724       if (*p)
725         die ("cmpfiles: syntax error");
726     }
727   set_type_var (assign_to, arg, VARTYPE_COUNTER);
728 }
729
730 static void
731 cmd_openfile (const char *assign_to, char *arg)
732 {
733   int fd;
734   char numbuf[20];
735
736   do 
737     fd = open (arg, O_RDONLY);
738   while (fd == -1 && errno == EINTR);
739   if (fd == -1)
740     die ("error opening `%s': %s", arg, strerror (errno));
741   
742   sprintf (numbuf, "%d", fd);
743   set_type_var (assign_to, numbuf, VARTYPE_FD);
744 }
745
746 static void
747 cmd_createfile (const char *assign_to, char *arg)
748 {
749   int fd;
750   char numbuf[20];
751
752   do 
753     fd = open (arg, O_WRONLY|O_CREAT|O_TRUNC, 0666);
754   while (fd == -1 && errno == EINTR);
755   if (fd == -1)
756     die ("error creating `%s': %s", arg, strerror (errno));
757
758   sprintf (numbuf, "%d", fd);
759   set_type_var (assign_to, numbuf, VARTYPE_FD);
760 }
761
762
763 static void
764 cmd_pipeserver (const char *assign_to, char *arg)
765 {
766   if (!*arg)
767     die ("syntax error: servername missing");
768
769   start_server (arg);
770 }
771
772
773 static void
774 cmd_quit_if(const char *assign_to, char *arg)
775 {
776   if (eval_boolean (arg))
777     exit (0);
778 }
779
780 static void
781 cmd_fail_if(const char *assign_to, char *arg)
782 {
783   if (eval_boolean (arg))
784     exit (1);
785 }
786
787
788 static void
789 cmd_cmpfiles (const char *assign_to, char *arg)
790 {
791   char *p = arg;
792   char *second;
793   FILE *fp1, *fp2;
794   char buffer1[2048]; /* note: both must be of equal size. */
795   char buffer2[2048];
796   size_t nread1, nread2;
797   int rc = 0;
798
799   set_var (assign_to, "0"); 
800   for (p=arg; *p && !spacep (p); p++)
801     ;
802   if (!*p)
803     die ("cmpfiles: syntax error");
804   for (*p++ = 0; spacep (p); p++)
805     ;
806   second = p;
807   for (; *p && !spacep (p); p++)
808     ;
809   if (*p)
810     {
811       for (*p++ = 0; spacep (p); p++)
812         ;
813       if (*p)
814         die ("cmpfiles: syntax error");
815     }
816   
817   fp1 = fopen (arg, "rb");
818   if (!fp1)
819     {
820       err ("can't open `%s': %s", arg, strerror (errno));
821       return;
822     }
823   fp2 = fopen (second, "rb");
824   if (!fp2)
825     {
826       err ("can't open `%s': %s", second, strerror (errno));
827       fclose (fp1);
828       return;
829     }
830   while ( (nread1 = fread (buffer1, 1, sizeof buffer1, fp1)))
831     {
832       if (ferror (fp1))
833         break;
834       nread2 = fread (buffer2, 1, sizeof buffer2, fp2);
835       if (ferror (fp2))
836         break;
837       if (nread1 != nread2 || memcmp (buffer1, buffer2, nread1))
838         {
839           rc = 1;
840           break;
841         }
842     }
843   if (feof (fp1) && feof (fp2) && !rc)
844     {
845       if (opt_verbose)
846         err ("files match");
847       set_var (assign_to, "1"); 
848     }
849   else if (!rc)
850     err ("cmpfiles: read error: %s", strerror (errno));
851   else
852     err ("cmpfiles: mismatch");
853   fclose (fp1);
854   fclose (fp2);
855 }
856
857 /* Process the current script line LINE. */
858 static int
859 interpreter (char *line)
860 {
861   static struct {
862     const char *name;
863     void (*fnc)(const char*, char*);
864   } cmdtbl[] = {
865     { "let"       , cmd_let },
866     { "echo"      , cmd_echo },
867     { "send"      , cmd_send },
868     { "expect-ok" , cmd_expect_ok },
869     { "expect-err", cmd_expect_err },
870     { "count-status", cmd_count_status },
871     { "openfile"  , cmd_openfile },
872     { "createfile", cmd_createfile },
873     { "pipeserver", cmd_pipeserver },
874     { "quit"      , NULL },
875     { "quit-if"   , cmd_quit_if },
876     { "fail-if"   , cmd_fail_if },
877     { "cmpfiles"  , cmd_cmpfiles },
878     { NULL }
879   };
880   char *p, *save_p;
881   int i, save_c;
882   char *stmt = NULL;
883   char *assign_to = NULL;
884   char *must_free = NULL;
885
886   for ( ;spacep (line); line++)
887     ;
888   if (!*line || *line == '#')
889     return 0; /* empty or comment */
890   p = expand_line (line);
891   if (p)
892     {
893       must_free = p;
894       line = p;
895       for ( ;spacep (line); line++)
896         ;
897       if (!*line || *line == '#')
898         {
899           free (must_free);
900           return 0; /* empty or comment */
901         }
902     }
903   for (p=line; *p && !spacep (p) && *p != '='; p++)
904     ;
905   if (*p == '=')
906     {
907       *p = 0;
908       assign_to = line;
909     }
910   else if (*p)
911     {
912       for (*p++ = 0; spacep (p); p++)
913         ;
914       if (*p == '=')
915         assign_to = line;
916     }
917   if (!*line)
918     die ("syntax error");
919   stmt = line;
920   save_c = 0;
921   save_p = NULL;
922   if (assign_to)
923     { /* this is an assignment */
924       for (p++; spacep (p); p++)
925         ;
926       if (!*p)
927         {
928           unset_var (assign_to);
929           free (must_free);
930           return 0;
931         }
932       stmt = p;
933       for (; *p && !spacep (p); p++)
934         ;
935       if (*p)
936         {
937           save_p = p;
938           save_c = *p;
939           for (*p++ = 0; spacep (p);  p++)
940             ;
941         }
942     }
943   for (i=0; cmdtbl[i].name && strcmp (stmt, cmdtbl[i].name); i++)
944     ;
945   if (!cmdtbl[i].name)
946     {
947       if (!assign_to)
948         die ("invalid statement `%s'\n", stmt);
949       if (save_p)
950         *save_p = save_c;
951       set_var (assign_to, stmt);
952       free (must_free);
953       return 0;
954     }
955
956   if (cmdtbl[i].fnc)
957     cmdtbl[i].fnc (assign_to, p);
958   free (must_free);
959   return cmdtbl[i].fnc? 0:1;
960 }
961
962
963
964 int
965 main (int argc, char **argv)
966 {
967   char buffer[2048];
968   char *p, *pend;
969
970   if (!argc)
971     invocation_name = "asschk";
972   else
973     {
974       invocation_name = *argv++;
975       argc--;
976       p = strrchr (invocation_name, '/');
977       if (p)
978         invocation_name = p+1;
979     }
980
981
982   set_var ("?","1"); /* defaults to true */
983
984   for (; argc; argc--, argv++)
985     {
986       p = *argv;
987       if (*p != '-')
988         break;
989       if (!strcmp (p, "--verbose"))
990         opt_verbose++;
991       else if (!strcmp (p, "--no-echo"))
992         opt_no_echo++;
993       else if (*p == '-' && p[1] == 'D')
994         {
995           p += 2;
996           pend = strchr (p, '=');
997           if (pend)
998             {
999               int tmp = *pend;
1000               *pend = 0;
1001               set_var (p, pend+1);
1002               *pend = tmp;
1003             }
1004           else
1005             set_var (p, "1");
1006         }
1007       else if (*p == '-' && p[1] == '-' && !p[2])
1008         {
1009           argc--; argv++;
1010           break;
1011         }
1012       else
1013         break;
1014     }
1015   if (argc)
1016     die ("usage: asschk [--verbose] {-D<name>[=<value>]}");
1017
1018
1019   while (fgets (buffer, sizeof buffer, stdin))
1020     {
1021       p = strchr (buffer,'\n');
1022       if (!p)
1023         die ("incomplete script line");
1024       *p = 0;
1025       if (interpreter (buffer))
1026         break;
1027       fflush (stdout);
1028     }
1029   return 0;
1030 }
1031