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