aboutsummaryrefslogtreecommitdiff
path: root/contrib/netbsd-tests/lib/libcurses/director
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/netbsd-tests/lib/libcurses/director')
-rw-r--r--contrib/netbsd-tests/lib/libcurses/director/director.c279
-rw-r--r--contrib/netbsd-tests/lib/libcurses/director/returns.h66
-rw-r--r--contrib/netbsd-tests/lib/libcurses/director/testlang_conf.l437
-rw-r--r--contrib/netbsd-tests/lib/libcurses/director/testlang_parse.y1618
4 files changed, 2400 insertions, 0 deletions
diff --git a/contrib/netbsd-tests/lib/libcurses/director/director.c b/contrib/netbsd-tests/lib/libcurses/director/director.c
new file mode 100644
index 000000000000..c73ddae27112
--- /dev/null
+++ b/contrib/netbsd-tests/lib/libcurses/director/director.c
@@ -0,0 +1,279 @@
+/* $NetBSD: director.c,v 1.10 2012/06/03 23:19:11 joerg Exp $ */
+
+/*-
+ * Copyright 2009 Brett Lymn <blymn@NetBSD.org>
+ *
+ * All rights reserved.
+ *
+ * This code has been donated to The NetBSD Foundation by the Author.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. The name of the author may not be used to endorse or promote products
+ * derived from this software withough specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <termios.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <util.h>
+#include <err.h>
+#include "returns.h"
+
+void yyparse(void);
+#define DEF_TERMPATH "."
+#define DEF_TERM "atf"
+#define DEF_SLAVE "./slave"
+
+const char *def_check_path = "./"; /* default check path */
+const char *def_include_path = "./"; /* default include path */
+
+extern size_t nvars; /* In testlang_conf.y */
+saved_data_t saved_output; /* In testlang_conf.y */
+int cmdpipe[2]; /* command pipe between director and slave */
+int slvpipe[2]; /* reply pipe back from slave */
+int master; /* pty to the slave */
+int verbose; /* control verbosity of tests */
+const char *check_path; /* path to prepend to check files for output
+ validation */
+const char *include_path; /* path to prepend to include files */
+char *cur_file; /* name of file currently being read */
+
+void init_parse_variables(int); /* in testlang_parse.y */
+
+/*
+ * Handle the slave exiting unexpectedly, try to recover the exit message
+ * and print it out.
+ */
+static void
+slave_died(int param)
+{
+ char last_words[256];
+ size_t count;
+
+ fprintf(stderr, "ERROR: Slave has exited\n");
+ if (saved_output.count > 0) {
+ fprintf(stderr, "output from slave: ");
+ for (count = 0; count < saved_output.count; count ++) {
+ if (isprint((unsigned char)saved_output.data[count]))
+ fprintf(stderr, "%c", saved_output.data[count]);
+ }
+ fprintf(stderr, "\n");
+ }
+
+ if ((count = read(master, &last_words, 255)) > 0) {
+ last_words[count] = '\0';
+ fprintf(stderr, "slave exited with message \"%s\"\n",
+ last_words);
+ }
+
+ exit(2);
+}
+
+
+static void
+usage(void)
+{
+ fprintf(stderr, "Usage: %s [-v] [-I include-path] [-C check-path] "
+ "[-T terminfo-file] [-s pathtoslave] [-t term] "
+ "commandfile\n", getprogname());
+ fprintf(stderr, " where:\n");
+ fprintf(stderr, " -v enables verbose test output\n");
+ fprintf(stderr, " -T is a directory containing the terminfo.cdb "
+ "file, or a file holding the terminfo description n");
+ fprintf(stderr, " -s is the path to the slave executable\n");
+ fprintf(stderr, " -t is value to set TERM to for the test\n");
+ fprintf(stderr, " -I is the directory to include files\n");
+ fprintf(stderr, " -C is the directory for config files\n");
+ fprintf(stderr, " commandfile is a file of test directives\n");
+ exit(1);
+}
+
+
+int
+main(int argc, char *argv[])
+{
+ extern char *optarg;
+ extern int optind;
+ const char *termpath, *term, *slave;
+ int ch;
+ pid_t slave_pid;
+ extern FILE *yyin;
+ char *arg1, *arg2, *arg3, *arg4;
+ struct termios term_attr;
+ struct stat st;
+
+ termpath = term = slave = NULL;
+ verbose = 0;
+
+ while ((ch = getopt(argc, argv, "vC:I:p:s:t:T:")) != -1) {
+ switch(ch) {
+ case 'I':
+ include_path = optarg;
+ break;
+ case 'C':
+ check_path = optarg;
+ break;
+ case 'T':
+ termpath = optarg;
+ break;
+ case 'p':
+ termpath = optarg;
+ break;
+ case 's':
+ slave = optarg;
+ break;
+ case 't':
+ term = optarg;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ break;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+ if (argc < 1)
+ usage();
+
+ if (termpath == NULL)
+ termpath = DEF_TERMPATH;
+
+ if (slave == NULL)
+ slave = DEF_SLAVE;
+
+ if (term == NULL)
+ term = DEF_TERM;
+
+ if (check_path == NULL)
+ check_path = getenv("CHECK_PATH");
+ if ((check_path == NULL) || (check_path[0] == '\0')) {
+ warn("$CHECK_PATH not set, defaulting to %s", def_check_path);
+ check_path = def_check_path;
+ }
+
+ if (include_path == NULL)
+ include_path = getenv("INCLUDE_PATH");
+ if ((include_path == NULL) || (include_path[0] == '\0')) {
+ warn("$INCLUDE_PATH not set, defaulting to %s",
+ def_include_path);
+ include_path = def_include_path;
+ }
+
+ signal(SIGCHLD, slave_died);
+
+ if (setenv("TERM", term, 1) != 0)
+ err(2, "Failed to set TERM variable");
+
+ if (stat(termpath, &st) == -1)
+ err(1, "Cannot stat %s", termpath);
+
+ if (S_ISDIR(st.st_mode)) {
+ char tinfo[MAXPATHLEN];
+ int l = snprintf(tinfo, sizeof(tinfo), "%s/%s", termpath,
+ "terminfo.cdb");
+ if (stat(tinfo, &st) == -1)
+ err(1, "Cannot stat `%s'", tinfo);
+ if (l >= 4)
+ tinfo[l - 4] = '\0';
+ if (setenv("TERMINFO", tinfo, 1) != 0)
+ err(1, "Failed to set TERMINFO variable");
+ } else {
+ int fd;
+ char *tinfo;
+ if ((fd = open(termpath, O_RDONLY)) == -1)
+ err(1, "Cannot open `%s'", termpath);
+ if ((tinfo = mmap(NULL, (size_t)st.st_size, PROT_READ, MAP_FILE,
+ fd, 0)) == MAP_FAILED)
+ err(1, "Cannot map `%s'", termpath);
+ if (setenv("TERMINFO", tinfo, 1) != 0)
+ err(1, "Failed to set TERMINFO variable");
+ close(fd);
+ munmap(tinfo, (size_t)st.st_size);
+ }
+
+ if (pipe(cmdpipe) < 0)
+ err(1, "Command pipe creation failed");
+
+ if (pipe(slvpipe) < 0)
+ err(1, "Slave pipe creation failed");
+
+ /*
+ * Create default termios settings for later use
+ */
+ memset(&term_attr, 0, sizeof(term_attr));
+ term_attr.c_iflag = TTYDEF_IFLAG;
+ term_attr.c_oflag = TTYDEF_OFLAG;
+ term_attr.c_cflag = TTYDEF_CFLAG;
+ term_attr.c_lflag = TTYDEF_LFLAG;
+ cfsetspeed(&term_attr, TTYDEF_SPEED);
+ term_attr.c_cc[VERASE] = '\b';
+ term_attr.c_cc[VKILL] = '\025'; /* ^U */
+
+ if ((slave_pid = forkpty(&master, NULL, &term_attr, NULL)) < 0)
+ err(1, "Fork of pty for slave failed\n");
+
+ if (slave_pid == 0) {
+ /* slave side, just exec the slave process */
+ if (asprintf(&arg1, "%d", cmdpipe[0]) < 0)
+ err(1, "arg1 conversion failed");
+
+ if (asprintf(&arg2, "%d", cmdpipe[1]) < 0)
+ err(1, "arg2 conversion failed");
+
+ if (asprintf(&arg3, "%d", slvpipe[0]) < 0)
+ err(1, "arg3 conversion failed");
+
+ if (asprintf(&arg4, "%d", slvpipe[1]) < 0)
+ err(1, "arg4 conversion failed");
+
+ if (execl(slave, slave, arg1, arg2, arg3, arg4, NULL) < 0)
+ err(1, "Exec of slave %s failed", slave);
+
+ /* NOT REACHED */
+ }
+
+ fcntl(master, F_SETFL, O_NONBLOCK);
+
+ if ((yyin = fopen(argv[0], "r")) == NULL)
+ err(1, "Cannot open command file %s", argv[0]);
+
+ if ((cur_file = strdup(argv[0])) == NULL)
+ err(2, "Failed to alloc memory for test file name");
+
+ init_parse_variables(1);
+
+ yyparse();
+ fclose(yyin);
+
+ exit(0);
+}
diff --git a/contrib/netbsd-tests/lib/libcurses/director/returns.h b/contrib/netbsd-tests/lib/libcurses/director/returns.h
new file mode 100644
index 000000000000..150e35833576
--- /dev/null
+++ b/contrib/netbsd-tests/lib/libcurses/director/returns.h
@@ -0,0 +1,66 @@
+/* $NetBSD: returns.h,v 1.1 2011/04/10 09:55:09 blymn Exp $ */
+
+/*-
+ * Copyright 2009 Brett Lymn <blymn@NetBSD.org>
+ *
+ * All rights reserved.
+ *
+ * This code has been donated to The NetBSD Foundation by the Author.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. The name of the author may not be used to endorse or promote products
+ * derived from this software withough specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ */
+#ifndef CTF_RETURNS_H
+#define CTF_RETURNS_H 1
+
+
+typedef enum {
+ ret_number = 1,
+ ret_string,
+ ret_byte,
+ ret_err,
+ ret_ok,
+ ret_null,
+ ret_nonnull,
+ ret_var,
+ ret_ref,
+ ret_count,
+ ret_slave_error
+} returns_enum_t;
+
+typedef struct {
+ returns_enum_t return_type;
+ void *return_value; /* used if return_type is ret_num or
+ or ret_byte or ret_string */
+ size_t return_len; /* number of bytes in return_value iff
+ return_type is ret_byte */
+ int return_index; /* index into var array for return
+ if return_type is ret_var */
+} returns_t;
+
+typedef struct {
+ size_t count;
+ size_t allocated;
+ size_t readp;
+ char *data;
+} saved_data_t;
+
+#endif /* CTF_RETURNS_H */
diff --git a/contrib/netbsd-tests/lib/libcurses/director/testlang_conf.l b/contrib/netbsd-tests/lib/libcurses/director/testlang_conf.l
new file mode 100644
index 000000000000..a732afce3a89
--- /dev/null
+++ b/contrib/netbsd-tests/lib/libcurses/director/testlang_conf.l
@@ -0,0 +1,437 @@
+%{
+/* $NetBSD: testlang_conf.l,v 1.7 2013/11/21 11:06:04 blymn Exp $ */
+
+/*-
+ * Copyright 2009 Brett Lymn <blymn@NetBSD.org>
+ *
+ * All rights reserved.
+ *
+ * This code has been donated to The NetBSD Foundation by the Author.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. The name of the author may not be used to endorse or promote products
+ * derived from this software withough specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ */
+
+#include <curses.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/param.h>
+#include <err.h>
+#include "returns.h"
+#include "testlang_parse.h"
+
+#define MAX_INCLUDES 32 /* limit for the number of nested includes */
+
+int yylex(void);
+
+extern size_t line;
+extern char *include_path; /* from director.c */
+extern char *cur_file; /* from director.c */
+
+static int include_stack[MAX_INCLUDES];
+static char *include_files[MAX_INCLUDES];
+static int include_ptr = 0;
+
+static char *
+dequote(const char *s, size_t *len)
+{
+ const unsigned char *p;
+ char *buf, *q;
+
+ *len = 0;
+ p = (const unsigned char *)s;
+ while (*p) {
+ if (*p == '\\' && *(p+1)) {
+ if (isdigit(*(p+1)) && *(p+2) && isdigit(*(p+2)) &&
+ *(p+3) && isdigit(*(p+3)))
+ p += 3;
+ else
+ ++p;
+ }
+ ++(*len);
+ ++p;
+ }
+
+ buf = malloc(*len + 1);
+ if (buf == NULL)
+ return NULL;
+
+ p = (const unsigned char *)s;
+ q = buf;
+ while (*p) {
+ if (*p == '\\' && *(p+1)) {
+ ++p;
+ if (isdigit(*p)) {
+ if (*(p+1) && isdigit(*(p+1)) && *(p+2) &&
+ isdigit(*(p+2))) {
+ *q++ = ((*p - '0') * 8 + (*(p+1) - '0')) * 8 + (*(p+2) - '0');
+ p += 3;
+ } else {
+ *q++ = *p++;
+ }
+ } else {
+ switch (*p) {
+ case 'e':
+ /* escape */
+ *q++ = '\e';
+ p++;
+ break;
+
+ case 'n':
+ /* newline */
+ *q++ = '\n';
+ p++;
+ break;
+
+ case 'r':
+ /* carriage return */
+ *q++ = '\r';
+ p++;
+ break;
+
+ case 't':
+ /* tab */
+ *q++ = '\t';
+ p++;
+ break;
+
+ case '\\':
+ /* backslash */
+ *q++ = '\\';
+ p++;
+ break;
+
+ default:
+ *q++ = *p++;
+ }
+ }
+ } else
+ *q++ = *p++;
+ }
+ *q++ = '\0';
+
+ return buf;
+}
+%}
+
+HEX 0[xX][0-9a-zA-Z]+
+STRING [0-9a-z!#-&(-^ \t%._\\]+
+numeric [-0-9]+
+PCHAR (\\.|[^ \t\n])
+ASSIGN [aA][sS][sS][iI][gG][nN]
+CALL2 [cC][aA][lL][lL]2
+CALL3 [cC][aA][lL][lL]3
+CALL4 [cC][aA][lL][lL]4
+CALL [cC][aA][lL][lL]
+CHECK [cC][hH][eE][cC][kK]
+DELAY [dD][eE][lL][aA][yY]
+INPUT [iI][nN][pP][uU][tT]
+NOINPUT [nN][oO][iI][nN][pP][uU][tT]
+OK_RET [oO][kK]
+ERR_RET [eE][rR][rR]
+COMPARE [cC][oO][mM][pP][aA][rR][eE]
+COMPAREND [cC][oO][mM][pP][aA][rR][eE][Nn][Dd]
+FILENAME [A-Za-z0-9.][A-Za-z0-9./_-]+
+VARNAME [A-Za-z][A-Za-z0-9_-]+
+NULL_RET NULL
+NON_NULL NON_NULL
+BYTE BYTE
+OR \|
+LHB \(
+RHB \)
+
+%x incl
+%option noinput nounput
+
+%%
+
+include BEGIN(incl);
+
+<incl>[ \t]* /* eat the whitespace */
+<incl>[^ \t\n]+ { /* got the include file name */
+ char inc_file[MAXPATHLEN];
+
+ if (include_ptr > MAX_INCLUDES) {
+ fprintf(stderr,
+ "Maximum number of nested includes exceeded "
+ "at line %zu of file %s\n", line, cur_file);
+ exit(2);
+ }
+
+ if (yytext[0] != '/') {
+ if (strlcpy(inc_file, include_path, sizeof(inc_file))
+ >= sizeof(inc_file))
+ err(2, "CHECK_PATH too long");
+ if ((include_path[strlen(include_path) - 1] != '/') &&
+ ((strlcat(inc_file, "/", sizeof(inc_file))
+ >= sizeof(inc_file))))
+ err(2, "Could not append / to include file path");
+ } else {
+ inc_file[0] = '\0';
+ }
+
+ if (strlcat(inc_file, yytext, sizeof(inc_file))
+ >= sizeof(inc_file))
+ err(2, "Path to include file path overflowed");
+
+ yyin = fopen(inc_file, "r" );
+
+ if (!yyin)
+ err(1, "Error opening %s", inc_file);
+
+ yypush_buffer_state(yy_create_buffer(yyin, YY_BUF_SIZE));
+
+ include_stack[include_ptr] = line;
+ include_files[include_ptr++] = cur_file;
+ cur_file = strdup(inc_file);
+ if (cur_file == NULL)
+ err(2, "Cannot allocate new include file string");
+ line = 0;
+ BEGIN(INITIAL);
+ }
+
+<<EOF>> {
+ yypop_buffer_state();
+
+ if ( !YY_CURRENT_BUFFER )
+ {
+ yyterminate();
+ }
+
+ if (--include_ptr < 0)
+ err(2, "Include stack underflow");
+
+ free(cur_file);
+ cur_file = include_files[include_ptr];
+ line = include_stack[include_ptr];
+ }
+
+{ASSIGN} {
+ return ASSIGN;
+ }
+
+{CALL2} {
+ return CALL2;
+ }
+
+{CALL3} {
+ return CALL3;
+ }
+
+{CALL4} {
+ return CALL4;
+ }
+
+{CALL} {
+ return CALL;
+ }
+
+{CHECK} {
+ return CHECK;
+ }
+
+{DELAY} {
+ return DELAY;
+ }
+
+{INPUT} {
+ return INPUT;
+ }
+
+{NOINPUT} {
+ return NOINPUT;
+ }
+
+{COMPARE} {
+ return COMPARE;
+ }
+
+{COMPAREND} {
+ return COMPAREND;
+ }
+
+{NON_NULL} {
+ return NON_NULL;
+ }
+
+{NULL_RET} {
+ return NULL_RET;
+ }
+
+{OK_RET} {
+ return OK_RET;
+ }
+
+{ERR_RET} {
+ return ERR_RET;
+ }
+
+{OR} {
+ return OR;
+ }
+
+{LHB} {
+ return LHB;
+ }
+
+{RHB} {
+ return RHB;
+ }
+
+{HEX} {
+ /* Hex value, convert to decimal and return numeric */
+ unsigned long val;
+
+ if (sscanf(yytext, "%lx", &val) != 1)
+ err(1, "Bad hex conversion");
+
+ asprintf(&yylval.string, "%ld", val);
+ return numeric;
+ }
+
+
+{numeric} {
+ if ((yylval.string = strdup(yytext)) == NULL)
+ err(1, "Cannot allocate numeric string");
+ return numeric;
+}
+
+{VARNAME} {
+ if ((yylval.string = strdup(yytext)) == NULL)
+ err(1, "Cannot allocate string for varname");
+ return VARNAME;
+ }
+
+{FILENAME} {
+ size_t len;
+
+ if ((yylval.string = dequote(yytext, &len)) == NULL)
+ err(1, "Cannot allocate filename string");
+ return FILENAME;
+ }
+
+ /* path */
+\/{PCHAR}+ {
+ size_t len;
+ if ((yylval.string = dequote(yytext, &len)) == NULL)
+ err(1, "Cannot allocate string");
+ return PATH;
+ }
+
+\'{STRING}\' {
+ char *p;
+ size_t len;
+
+ if ((yylval.retval = malloc(sizeof(returns_t))) == NULL)
+ err(1, "Cannot allocate return struct");
+ p = yytext;
+ p++; /* skip the leading ' */
+ if ((yylval.retval->return_value = dequote(p, &len))
+ == NULL)
+ err(1, "Cannot allocate string");
+
+ yylval.retval->return_type = ret_byte;
+ /* trim trailing ' */
+ yylval.retval->return_len = len - 1;
+ return BYTE;
+ }
+
+\`{STRING}\` {
+ char *p, *str;
+ size_t len, chlen;
+ size_t i;
+ chtype *rv;
+
+ if ((yylval.retval = malloc(sizeof(returns_t))) == NULL)
+ err(1, "Cannot allocate return struct");
+ p = yytext;
+ p++; /* skip the leading ' */
+ if ((str = dequote(p, &len)) == NULL)
+ err(1, "Cannot allocate string");
+ len--; /* trim trailing ` */
+ if ((len % 2) != 0)
+ len--;
+
+ chlen = ((len / 2) + 1) * sizeof(chtype);
+ if ((yylval.retval->return_value = malloc(chlen))
+ == NULL)
+ err(1, "Cannot allocate chtype array");
+
+ rv = yylval.retval->return_value;
+ for (i = 0; i < len; i += 2)
+ *rv++ = (str[i] << 8) | str[i+1];
+ *rv = __NORMAL | '\0'; /* terminates chtype array */
+ yylval.retval->return_type = ret_byte;
+ yylval.retval->return_len = chlen;
+ return BYTE;
+ }
+
+\"{STRING}\" {
+ char *p;
+ size_t len;
+
+ p = yytext;
+ p++; /* skip the leading " */
+ if ((yylval.string = dequote(p, &len)) == NULL)
+ err(1, "Cannot allocate string");
+
+ /* remove trailing " */
+ yylval.string[len - 1] = '\0';
+ return STRING;
+ }
+
+\${VARNAME} {
+ char *p;
+
+ p = yytext;
+ p++; /* skip $ before var name */
+ if ((yylval.string = strdup(p)) == NULL)
+ err(1, "Cannot allocate string for varname");
+ return VARIABLE;
+ }
+
+ /* comments, white-outs */
+[ \t\r] |
+#.* ;
+^#.*\n |
+#.*\n |
+\\\n |
+^\n {
+line++; }
+
+ /* eol on a line with data. need to process, return eol */
+\n {
+ line++;
+ return EOL;
+ }
+
+. {
+ }
+
+%%
+
+int
+yywrap(void)
+{
+ return 1;
+}
diff --git a/contrib/netbsd-tests/lib/libcurses/director/testlang_parse.y b/contrib/netbsd-tests/lib/libcurses/director/testlang_parse.y
new file mode 100644
index 000000000000..75aaf62d6220
--- /dev/null
+++ b/contrib/netbsd-tests/lib/libcurses/director/testlang_parse.y
@@ -0,0 +1,1618 @@
+%{
+/* $NetBSD: testlang_parse.y,v 1.14 2015/01/04 20:19:46 christos Exp $ */
+
+/*-
+ * Copyright 2009 Brett Lymn <blymn@NetBSD.org>
+ *
+ * All rights reserved.
+ *
+ * This code has been donated to The NetBSD Foundation by the Author.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. The name of the author may not be used to endorse or promote products
+ * derived from this software withough specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ */
+#include <assert.h>
+#include <curses.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <err.h>
+#include <unistd.h>
+#include <poll.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <time.h>
+#include <vis.h>
+#include <stdint.h>
+#include "returns.h"
+
+#define YYDEBUG 1
+
+extern int verbose;
+extern int cmdpipe[2];
+extern int slvpipe[2];
+extern int master;
+extern struct pollfd readfd;
+extern char *check_path;
+extern char *cur_file; /* from director.c */
+
+int yylex(void);
+
+size_t line;
+
+static int input_delay;
+
+/* time delay between inputs chars - default to 0.1ms minimum to prevent
+ * problems with input tests
+ */
+#define DELAY_MIN 0.1
+
+/* time delay after a function call - allows the slave time to
+ * run the function and output data before we do other actions.
+ * Set this to 50ms.
+ */
+#define POST_CALL_DELAY 50
+
+static struct timespec delay_spec = {0, 1000 * DELAY_MIN};
+static struct timespec delay_post_call = {0, 1000 * POST_CALL_DELAY};
+
+static char *input_str; /* string to feed in as input */
+static bool no_input; /* don't need more input */
+
+#define READ_PIPE 0
+#define WRITE_PIPE 1
+
+const char *returns_enum_names[] = {
+ "unused", "numeric", "string", "byte", "ERR", "OK", "NULL", "not NULL",
+ "variable", "reference", "returns count", "slave error"
+};
+
+typedef enum {
+ arg_static,
+ arg_byte,
+ arg_var,
+ arg_null
+} args_state_t;
+
+static const char *args_enum_names[] = {
+ "static", "byte", "var", "NULL"
+};
+
+typedef struct {
+ args_state_t arg_type;
+ size_t arg_len;
+ char *arg_string;
+ int var_index;
+} args_t;
+
+typedef struct {
+ char *function;
+ int nrets; /* number of returns */
+ returns_t *returns; /* array of expected returns */
+ int nargs; /* number of arguments */
+ args_t *args; /* arguments for the call */
+} cmd_line_t;
+
+static cmd_line_t command;
+
+typedef struct {
+ char *name;
+ size_t len;
+ returns_enum_t type;
+ void *value;
+} var_t;
+
+static size_t nvars; /* Number of declared variables */
+static var_t *vars; /* Variables defined during the test. */
+
+static int check_function_table(char *, const char *[], int);
+static int find_var_index(const char *);
+static void assign_arg(args_state_t, void *);
+static int assign_var(char *);
+void init_parse_variables(int);
+static void validate(int, void *);
+static void validate_return(const char *, const char *, int);
+static void validate_variable(int, returns_enum_t, const void *, int, int);
+static void validate_byte(returns_t *, returns_t *, int);
+static void write_cmd_pipe(char *);
+static void write_cmd_pipe_args(args_state_t, void *);
+static void read_cmd_pipe(returns_t *);
+static void write_func_and_args(void);
+static void compare_streams(char *, bool);
+static void do_function_call(size_t);
+static void save_slave_output(bool);
+static void validate_type(returns_enum_t, returns_t *, int);
+static void set_var(returns_enum_t, char *, void *);
+static void validate_reference(int, void *);
+static char *numeric_or(char *, char *);
+static char *get_numeric_var(const char *);
+static void perform_delay(struct timespec *);
+
+static const char *input_functions[] = {
+ "getch", "getnstr", "getstr", "mvgetnstr", "mvgetstr", "mvgetnstr",
+ "mvgetstr", "mvscanw", "mvwscanw", "scanw", "wgetch", "wgetnstr",
+ "wgetstr"
+};
+
+static const unsigned ninput_functions =
+ sizeof(input_functions) / sizeof(char *);
+
+saved_data_t saved_output;
+
+%}
+
+%union {
+ char *string;
+ returns_t *retval;
+}
+
+%token <string> PATH
+%token <string> STRING
+%token <retval> BYTE
+%token <string> VARNAME
+%token <string> FILENAME
+%token <string> VARIABLE
+%token <string> REFERENCE
+%token <string> NULL_RET
+%token <string> NON_NULL
+%token <string> ERR_RET
+%token <string> OK_RET
+%token <string> numeric
+%token <string> DELAY
+%token <string> INPUT
+%token <string> COMPARE
+%token <string> COMPAREND
+%token <string> ASSIGN
+%token EOL CALL CHECK NOINPUT OR LHB RHB
+%token CALL2 CALL3 CALL4 DRAIN
+
+%nonassoc OR
+
+%%
+
+statement : /* empty */
+ | assign statement
+ | call statement
+ | call2 statement
+ | call3 statement
+ | call4 statement
+ | check statement
+ | delay statement
+ | input statement
+ | noinput statement
+ | compare statement
+ | comparend statement
+ | eol statement
+ ;
+
+assign : ASSIGN VARNAME numeric {set_var(ret_number, $2, $3);} eol
+ | ASSIGN VARNAME LHB expr RHB {set_var(ret_number, $2, $<string>4);} eol
+ | ASSIGN VARNAME STRING {set_var(ret_string, $2, $3);} eol
+ | ASSIGN VARNAME BYTE {set_var(ret_byte, $2, $3);} eol
+ ;
+
+call : CALL result fn_name args eol {
+ do_function_call(1);
+}
+ ;
+
+call2 : CALL2 result result fn_name args eol {
+ do_function_call(2);
+}
+ ;
+
+call3 : CALL3 result result result fn_name args eol {
+ do_function_call(3);
+}
+ ;
+
+call4 : CALL4 result result result result fn_name args eol {
+ do_function_call(4);
+ }
+ ;
+
+check : CHECK var returns eol {
+ returns_t retvar;
+ var_t *vptr;
+ if (command.returns[0].return_index == -1)
+ err(1, "Undefined variable in check statement, line %zu"
+ " of file %s", line, cur_file);
+
+ if (verbose) {
+ fprintf(stderr, "Checking contents of variable %s for %s\n",
+ vars[command.returns[0].return_index].name,
+ returns_enum_names[command.returns[1].return_type]);
+ }
+
+ if (((command.returns[1].return_type == ret_byte) &&
+ (vars[command.returns[0].return_index].type != ret_byte)) ||
+ vars[command.returns[0].return_index].type != ret_string)
+ err(1, "Var type %s (%d) does not match return type %s (%d)",
+ returns_enum_names[
+ vars[command.returns[0].return_index].type],
+ vars[command.returns[0].return_index].type,
+ returns_enum_names[command.returns[1].return_type],
+ command.returns[1].return_type);
+
+ switch (command.returns[1].return_type) {
+ case ret_err:
+ validate_variable(0, ret_string, "ERR",
+ command.returns[0].return_index, 0);
+ break;
+
+ case ret_ok:
+ validate_variable(0, ret_string, "OK",
+ command.returns[0].return_index, 0);
+ break;
+
+ case ret_null:
+ validate_variable(0, ret_string, "NULL",
+ command.returns[0].return_index, 0);
+ break;
+
+ case ret_nonnull:
+ validate_variable(0, ret_string, "NULL",
+ command.returns[0].return_index, 1);
+ break;
+
+ case ret_string:
+ case ret_number:
+ if (verbose) {
+ fprintf(stderr, " %s == returned %s\n",
+ (const char *)command.returns[1].return_value,
+ (const char *)
+ vars[command.returns[0].return_index].value);
+ }
+ validate_variable(0, ret_string,
+ command.returns[1].return_value,
+ command.returns[0].return_index, 0);
+ break;
+
+ case ret_byte:
+ vptr = &vars[command.returns[0].return_index];
+ retvar.return_len = vptr->len;
+ retvar.return_type = vptr->type;
+ retvar.return_value = vptr->value;
+ validate_byte(&retvar, &command.returns[1], 0);
+ break;
+
+ default:
+ err(1, "Malformed check statement at line %zu "
+ "of file %s", line, cur_file);
+ break;
+ }
+
+ init_parse_variables(0);
+ }
+ ;
+
+delay : DELAY numeric eol {
+ /* set the inter-character delay */
+ if (sscanf($2, "%d", &input_delay) == 0)
+ err(1, "delay specification %s could not be converted to "
+ "numeric at line %zu of file %s", $2, line, cur_file);
+ if (verbose) {
+ fprintf(stderr, "Set input delay to %d ms\n", input_delay);
+ }
+
+ if (input_delay < DELAY_MIN)
+ input_delay = DELAY_MIN;
+ /*
+ * Fill in the timespec structure now ready for use later.
+ * The delay is specified in milliseconds so convert to timespec
+ * values
+ */
+ delay_spec.tv_sec = input_delay / 1000;
+ delay_spec.tv_nsec = (input_delay - 1000 * delay_spec.tv_sec) * 1000;
+ if (verbose) {
+ fprintf(stderr, "set delay to %jd.%jd\n",
+ (intmax_t)delay_spec.tv_sec,
+ (intmax_t)delay_spec.tv_nsec);
+ }
+
+ init_parse_variables(0);
+ }
+ ;
+
+input : INPUT STRING eol {
+ if (input_str != NULL) {
+ warnx("%s, %zu: Discarding unused input string",
+ cur_file, line);
+ free(input_str);
+ }
+
+ if ((input_str = malloc(strlen($2) + 1)) == NULL)
+ err(2, "Cannot allocate memory for input string");
+
+ strlcpy(input_str, $2, strlen($2) + 1);
+}
+ ;
+
+
+noinput : NOINPUT eol {
+ if (input_str != NULL) {
+ warnx("%s, %zu: Discarding unused input string",
+ cur_file, line);
+ free(input_str);
+ }
+
+ no_input = true;
+ }
+
+compare : COMPARE PATH eol
+ | COMPARE FILENAME eol
+{
+ compare_streams($2, true);
+}
+ ;
+
+
+comparend : COMPAREND PATH eol
+ | COMPAREND FILENAME eol
+{
+ compare_streams($2, false);
+}
+ ;
+
+
+result : returns
+ | var
+ | reference
+ ;
+
+returns : numeric { assign_rets(ret_number, $1); }
+ | LHB expr RHB { assign_rets(ret_number, $<string>2); }
+ | STRING { assign_rets(ret_string, $1); }
+ | BYTE { assign_rets(ret_byte, (void *) $1); }
+ | ERR_RET { assign_rets(ret_err, NULL); }
+ | OK_RET { assign_rets(ret_ok, NULL); }
+ | NULL_RET { assign_rets(ret_null, NULL); }
+ | NON_NULL { assign_rets(ret_nonnull, NULL); }
+ ;
+
+var : VARNAME {
+ assign_rets(ret_var, $1);
+ }
+ ;
+
+reference : VARIABLE {
+ assign_rets(ret_ref, $1);
+ }
+
+fn_name : VARNAME {
+ if (command.function != NULL)
+ free(command.function);
+
+ command.function = malloc(strlen($1) + 1);
+ if (command.function == NULL)
+ err(1, "Could not allocate memory for function name");
+ strcpy(command.function, $1);
+ }
+ ;
+
+expr : numeric
+ | VARIABLE
+ { $<string>$ = get_numeric_var($1); }
+ | expr OR expr
+ { $<string>$ = numeric_or($<string>1, $<string>3); }
+ ;
+
+args : /* empty */
+ | LHB expr RHB { assign_arg(arg_static, $<string>2); } args
+ | numeric { assign_arg(arg_static, $1); } args
+ | STRING { assign_arg(arg_static, $1); } args
+ | BYTE { assign_arg(arg_byte, $1); } args
+ | PATH { assign_arg(arg_static, $1); } args
+ | FILENAME { assign_arg(arg_static, $1); } args
+ | VARNAME { assign_arg(arg_static, $1); } args
+ | VARIABLE { assign_arg(arg_var, $1); } args
+ | NULL_RET { assign_arg(arg_null, $1); } args
+ ;
+
+eol : EOL
+ ;
+
+%%
+
+static void
+excess(const char *fname, size_t lineno, const char *func, const char *comment,
+ const void *data, size_t datalen)
+{
+ size_t dstlen = datalen * 4 + 1;
+ char *dst = malloc(dstlen);
+
+ if (dst == NULL)
+ err(1, "malloc");
+
+ if (strnvisx(dst, dstlen, data, datalen, VIS_WHITE | VIS_OCTAL) == -1)
+ err(1, "strnvisx");
+
+ warnx("%s, %zu: [%s] Excess %zu bytes%s [%s]",
+ fname, lineno, func, datalen, comment, dst);
+ free(dst);
+}
+
+/*
+ * Get the value of a variable, error if the variable has not been set or
+ * is not a numeric type.
+ */
+static char *
+get_numeric_var(const char *var)
+{
+ int i;
+
+ if ((i = find_var_index(var)) < 0)
+ err(1, "Variable %s is undefined", var);
+
+ if (vars[i].type != ret_number)
+ err(1, "Variable %s is not a numeric type", var);
+
+ return vars[i].value;
+}
+
+/*
+ * Perform a bitwise OR on two numbers and return the result.
+ */
+static char *
+numeric_or(char *n1, char *n2)
+{
+ unsigned long i1, i2, result;
+ char *ret;
+
+ i1 = strtoul(n1, NULL, 10);
+ i2 = strtoul(n2, NULL, 10);
+
+ result = i1 | i2;
+ asprintf(&ret, "%lu", result);
+
+ if (verbose) {
+ fprintf(stderr, "numeric or of 0x%lx (%s) and 0x%lx (%s)"
+ " results in 0x%lx (%s)\n",
+ i1, n1, i2, n2, result, ret);
+ }
+
+ return ret;
+}
+
+/*
+ * Sleep for the specified time, handle the sleep getting interrupted
+ * by a signal.
+ */
+static void
+perform_delay(struct timespec *ts)
+{
+ struct timespec delay_copy, delay_remainder;
+
+ delay_copy = *ts;
+ while (nanosleep(&delay_copy, &delay_remainder) < 0) {
+ if (errno != EINTR)
+ err(2, "nanosleep returned error");
+ delay_copy = delay_remainder;
+ }
+}
+
+/*
+ * Assign the value given to the named variable.
+ */
+static void
+set_var(returns_enum_t type, char *name, void *value)
+{
+ int i;
+ char *number;
+ returns_t *ret;
+
+ i = find_var_index(name);
+ if (i < 0)
+ i = assign_var(name);
+
+ vars[i].type = type;
+ if ((type == ret_number) || (type == ret_string)) {
+ number = value;
+ vars[i].len = strlen(number) + 1;
+ vars[i].value = malloc(vars[i].len + 1);
+ if (vars[i].value == NULL)
+ err(1, "Could not malloc memory for assign string");
+ strcpy(vars[i].value, number);
+ } else {
+ /* can only be a byte value */
+ ret = value;
+ vars[i].len = ret->return_len;
+ vars[i].value = malloc(vars[i].len);
+ if (vars[i].value == NULL)
+ err(1, "Could not malloc memory to assign byte string");
+ memcpy(vars[i].value, ret->return_value, vars[i].len);
+ }
+}
+
+/*
+ * Add a new variable to the vars array, the value will be assigned later,
+ * when a test function call returns.
+ */
+static int
+assign_var(char *varname)
+{
+ var_t *temp;
+ char *name;
+
+ if ((name = malloc(strlen(varname) + 1)) == NULL)
+ err(1, "Alloc of varname failed");
+
+ if ((temp = realloc(vars, sizeof(*temp) * (nvars + 1))) == NULL) {
+ free(name);
+ err(1, "Realloc of vars array failed");
+ }
+
+ strcpy(name, varname);
+ vars = temp;
+ vars[nvars].name = name;
+ vars[nvars].len = 0;
+ vars[nvars].value = NULL;
+ nvars++;
+
+ return (nvars - 1);
+}
+
+/*
+ * Allocate and assign a new argument of the given type.
+ */
+static void
+assign_arg(args_state_t arg_type, void *arg)
+{
+ args_t *temp, cur;
+ char *str = arg;
+ returns_t *ret;
+
+ if (verbose) {
+ fprintf(stderr, "function is >%s<, adding arg >%s< type %s\n",
+ command.function, str, args_enum_names[arg_type]);
+ }
+
+ cur.arg_type = arg_type;
+ switch (arg_type) {
+ case arg_var:
+ cur.var_index = find_var_index(arg);
+ if (cur.var_index < 0)
+ err(1, "Invalid variable %s at line %zu of file %s",
+ str, line, cur_file);
+ cur.arg_type = ret_string;
+ break;
+
+ case arg_byte:
+ ret = arg;
+ cur.arg_len = ret->return_len;
+ cur.arg_string = malloc(cur.arg_len);
+ if (cur.arg_string == NULL)
+ err(1, "Could not malloc memory for arg bytes");
+ memcpy(cur.arg_string, ret->return_value, cur.arg_len);
+ break;
+
+ case arg_null:
+ cur.arg_len = 0;
+ cur.arg_string = NULL;
+ break;
+
+ default:
+ cur.arg_len = strlen(str);
+ cur.arg_string = malloc(cur.arg_len + 1);
+ if (cur.arg_string == NULL)
+ err(1, "Could not malloc memory for arg string");
+ strcpy(cur.arg_string, arg);
+ }
+
+ temp = realloc(command.args, sizeof(*temp) * (command.nargs + 1));
+ if (temp == NULL)
+ err(1, "Failed to reallocate args");
+ command.args = temp;
+ memcpy(&command.args[command.nargs], &cur, sizeof(args_t));
+ command.nargs++;
+}
+
+/*
+ * Allocate and assign a new return.
+ */
+static void
+assign_rets(returns_enum_t ret_type, void *ret)
+{
+ returns_t *temp, cur;
+ char *ret_str;
+ returns_t *ret_ret;
+
+ cur.return_type = ret_type;
+ if (ret_type != ret_var) {
+ if ((ret_type == ret_number) || (ret_type == ret_string)) {
+ ret_str = ret;
+ cur.return_len = strlen(ret_str) + 1;
+ cur.return_value = malloc(cur.return_len + 1);
+ if (cur.return_value == NULL)
+ err(1,
+ "Could not malloc memory for arg string");
+ strcpy(cur.return_value, ret_str);
+ } else if (ret_type == ret_byte) {
+ ret_ret = ret;
+ cur.return_len = ret_ret->return_len;
+ cur.return_value = malloc(cur.return_len);
+ if (cur.return_value == NULL)
+ err(1,
+ "Could not malloc memory for byte string");
+ memcpy(cur.return_value, ret_ret->return_value,
+ cur.return_len);
+ } else if (ret_type == ret_ref) {
+ if ((cur.return_index = find_var_index(ret)) < 0)
+ err(1, "Undefined variable reference");
+ }
+ } else {
+ cur.return_index = find_var_index(ret);
+ if (cur.return_index < 0)
+ cur.return_index = assign_var(ret);
+ }
+
+ temp = realloc(command.returns, sizeof(*temp) * (command.nrets + 1));
+ if (temp == NULL)
+ err(1, "Failed to reallocate returns");
+ command.returns = temp;
+ memcpy(&command.returns[command.nrets], &cur, sizeof(returns_t));
+ command.nrets++;
+}
+
+/*
+ * Find the given variable name in the var array and return the i
+ * return -1 if var is not found.
+ */
+static int
+find_var_index(const char *var_name)
+{
+ int result;
+ size_t i;
+
+ result = -1;
+
+ for (i = 0; i < nvars; i++) {
+ if (strcmp(var_name, vars[i].name) == 0) {
+ result = i;
+ break;
+ }
+ }
+
+ return result;
+}
+
+/*
+ * Check the given function name in the given table of names, return 1 if
+ * there is a match.
+ */
+static int check_function_table(char *function, const char *table[],
+ int nfunctions)
+{
+ int i;
+
+ for (i = 0; i < nfunctions; i++) {
+ if ((strlen(function) == strlen(table[i])) &&
+ (strcmp(function, table[i]) == 0))
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * Compare the output from the slave against the given file and report
+ * any differences.
+ */
+static void
+compare_streams(char *filename, bool discard)
+{
+ char check_file[PATH_MAX], drain[100], ref, data;
+ struct pollfd fds[2];
+ int nfd, check_fd;
+ ssize_t result;
+ size_t offs;
+
+ /*
+ * Don't prepend check path iff check file has an absolute
+ * path.
+ */
+ if (filename[0] != '/') {
+ if (strlcpy(check_file, check_path, sizeof(check_file))
+ >= sizeof(check_file))
+ err(2, "CHECK_PATH too long");
+
+ if (strlcat(check_file, "/", sizeof(check_file))
+ >= sizeof(check_file))
+ err(2, "Could not append / to check file path");
+ } else {
+ check_file[0] = '\0';
+ }
+
+ if (strlcat(check_file, filename, sizeof(check_file))
+ >= sizeof(check_file))
+ err(2, "Path to check file path overflowed");
+
+ if ((check_fd = open(check_file, O_RDONLY, 0)) < 0)
+ err(2, "failed to open file %s line %zu of file %s",
+ check_file, line, cur_file);
+
+ fds[0].fd = check_fd;
+ fds[0].events = POLLIN;
+ fds[1].fd = master;
+ fds[1].events = POLLIN;
+
+ nfd = 2;
+ /*
+ * if we have saved output then only check for data in the
+ * reference file since the slave data may already be drained.
+ */
+ if (saved_output.count > 0)
+ nfd = 1;
+
+ offs = 0;
+ while (poll(fds, nfd, 500) == nfd) {
+ if (fds[0].revents & POLLIN) {
+ if ((result = read(check_fd, &ref, 1)) < 1) {
+ if (result != 0) {
+ err(2,
+ "Bad read on file %s", check_file);
+ } else {
+ break;
+ }
+ }
+ }
+
+ if (saved_output.count > 0) {
+ data = saved_output.data[saved_output.readp];
+ saved_output.count--;
+ saved_output.readp++;
+ /* run out of saved data, switch to file */
+ if (saved_output.count == 0)
+ nfd = 2;
+ } else {
+ if (fds[0].revents & POLLIN) {
+ if (read(master, &data, 1) < 1)
+ err(2, "Bad read on slave pty");
+ } else
+ continue;
+ }
+
+ if (verbose) {
+ fprintf(stderr, "Comparing reference byte 0x%x (%c)"
+ " against slave byte 0x%x (%c)\n",
+ ref, (ref >= ' ') ? ref : '-',
+ data, (data >= ' ' )? data : '-');
+ }
+
+ if (ref != data) {
+ errx(2, "%s, %zu: refresh data from slave does "
+ "not match expected from file %s offs %zu "
+ "[reference 0x%x (%c) != slave 0x%x (%c)]",
+ cur_file, line, check_file, offs,
+ ref, (ref >= ' ') ? ref : '-',
+ data, (data >= ' ') ? data : '-');
+ }
+
+ offs++;
+ }
+
+
+ if (saved_output.count > 0)
+ excess(cur_file, line, __func__, " from slave",
+ &saved_output.data[saved_output.readp], saved_output.count);
+
+ /* discard any excess saved output if required */
+ if (discard) {
+ saved_output.count = 0;
+ saved_output.readp = 0;
+ }
+
+ if ((result = poll(&fds[0], 2, 0)) != 0) {
+ if (result == -1)
+ err(2, "poll of file descriptors failed");
+
+ if ((fds[1].revents & POLLIN) == POLLIN) {
+ save_slave_output(true);
+ } else if ((fds[0].revents & POLLIN) == POLLIN) {
+ /*
+ * handle excess in file if it exists. Poll
+ * says there is data until EOF is read.
+ * Check next read is EOF, if it is not then
+ * the file really has more data than the
+ * slave produced so flag this as a warning.
+ */
+ result = read(check_fd, drain, sizeof(drain));
+ if (result == -1)
+ err(1, "read of data file failed");
+
+ if (result > 0) {
+ excess(check_file, 0, __func__, "", drain,
+ result);
+ }
+ }
+ }
+
+ close(check_fd);
+}
+
+/*
+ * Pass a function call and arguments to the slave and wait for the
+ * results. The variable nresults determines how many returns we expect
+ * back from the slave. These results will be validated against the
+ * expected returns or assigned to variables.
+ */
+static void
+do_function_call(size_t nresults)
+{
+#define MAX_RESULTS 4
+ char *p;
+ int do_input;
+ size_t i;
+ struct pollfd fds[3];
+ returns_t response[MAX_RESULTS], returns_count;
+ assert(nresults <= MAX_RESULTS);
+
+ do_input = check_function_table(command.function, input_functions,
+ ninput_functions);
+
+ write_func_and_args();
+
+ /*
+ * We should get the number of returns back here, grab it before
+ * doing input otherwise it will confuse the input poll
+ */
+ read_cmd_pipe(&returns_count);
+ if (returns_count.return_type != ret_count)
+ err(2, "expected return type of ret_count but received %s",
+ returns_enum_names[returns_count.return_type]);
+
+ perform_delay(&delay_post_call); /* let slave catch up */
+
+ if (verbose) {
+ fprintf(stderr, "Expect %zu results from slave, slave "
+ "reported %zu\n", nresults, returns_count.return_len);
+ }
+
+ if ((no_input == false) && (do_input == 1)) {
+ if (verbose) {
+ fprintf(stderr, "doing input with inputstr >%s<\n",
+ input_str);
+ }
+
+ if (input_str == NULL)
+ errx(2, "%s, %zu: Call to input function "
+ "but no input defined", cur_file, line);
+
+ fds[0].fd = slvpipe[READ_PIPE];
+ fds[0].events = POLLIN;
+ fds[1].fd = master;
+ fds[1].events = POLLOUT;
+ p = input_str;
+ save_slave_output(false);
+ while(*p != '\0') {
+ perform_delay(&delay_spec);
+
+ if (poll(fds, 2, 0) < 0)
+ err(2, "poll failed");
+ if (fds[0].revents & POLLIN) {
+ warnx("%s, %zu: Slave function "
+ "returned before end of input string",
+ cur_file, line);
+ break;
+ }
+ if ((fds[1].revents & POLLOUT) == 0)
+ continue;
+ if (verbose) {
+ fprintf(stderr, "Writing char >%c< to slave\n",
+ *p);
+ }
+ if (write(master, p, 1) != 1) {
+ warn("%s, %zu: Slave function write error",
+ cur_file, line);
+ break;
+ }
+ p++;
+
+ }
+ save_slave_output(false);
+
+ if (verbose) {
+ fprintf(stderr, "Input done.\n");
+ }
+
+ /* done with the input string, free the resources */
+ free(input_str);
+ input_str = NULL;
+ }
+
+ if (verbose) {
+ fds[0].fd = slvpipe[READ_PIPE];
+ fds[0].events = POLLIN;
+
+ fds[1].fd = slvpipe[WRITE_PIPE];
+ fds[1].events = POLLOUT;
+
+ fds[2].fd = master;
+ fds[2].events = POLLIN | POLLOUT;
+
+ i = poll(&fds[0], 3, 1000);
+ fprintf(stderr, "Poll returned %zu\n", i);
+ for (i = 0; i < 3; i++) {
+ fprintf(stderr, "revents for fd[%zu] = 0x%x\n",
+ i, fds[i].revents);
+ }
+ }
+
+ /* drain any trailing output */
+ save_slave_output(false);
+
+ for (i = 0; i < returns_count.return_len; i++) {
+ read_cmd_pipe(&response[i]);
+ }
+
+ /*
+ * Check for a slave error in the first return slot, if the
+ * slave errored then we may not have the number of returns we
+ * expect but in this case we should report the slave error
+ * instead of a return count mismatch.
+ */
+ if ((returns_count.return_len > 0) &&
+ (response[0].return_type == ret_slave_error))
+ err(2, "Slave returned error: %s",
+ (const char *)response[0].return_value);
+
+ if (returns_count.return_len != nresults)
+ err(2, "Incorrect number of returns from slave, expected %zu "
+ "but received %zu", nresults, returns_count.return_len);
+
+ if (verbose) {
+ for (i = 0; i < nresults; i++) {
+ if ((response[i].return_type != ret_byte) &&
+ (response[i].return_type != ret_err) &&
+ (response[i].return_type != ret_ok))
+ fprintf(stderr,
+ "received response >%s< "
+ "expected",
+ (const char *)response[i].return_value);
+ else
+ fprintf(stderr, "received");
+
+ fprintf(stderr, " return_type %s\n",
+ returns_enum_names[command.returns[i].return_type]);
+ }
+ }
+
+ for (i = 0; i < nresults; i++) {
+ if (command.returns[i].return_type != ret_var) {
+ validate(i, &response[i]);
+ } else {
+ vars[command.returns[i].return_index].len =
+ response[i].return_len;
+ vars[command.returns[i].return_index].value =
+ response[i].return_value;
+ vars[command.returns[i].return_index].type =
+ response[i].return_type;
+ }
+ }
+
+ if (verbose && (saved_output.count > 0))
+ excess(cur_file, line, __func__, " from slave",
+ &saved_output.data[saved_output.readp], saved_output.count);
+
+ init_parse_variables(0);
+}
+
+/*
+ * Write the function and command arguments to the command pipe.
+ */
+static void
+write_func_and_args(void)
+{
+ int i;
+
+ if (verbose) {
+ fprintf(stderr, "calling function >%s<\n", command.function);
+ }
+
+ write_cmd_pipe(command.function);
+ for (i = 0; i < command.nargs; i++) {
+ if (command.args[i].arg_type == arg_var)
+ write_cmd_pipe_args(command.args[i].arg_type,
+ &vars[command.args[i].var_index]);
+ else
+ write_cmd_pipe_args(command.args[i].arg_type,
+ &command.args[i]);
+ }
+
+ write_cmd_pipe(NULL); /* signal end of arguments */
+}
+
+/*
+ * Initialise the command structure - if initial is non-zero then just set
+ * everything to sane values otherwise free any memory that was allocated
+ * when building the structure.
+ */
+void
+init_parse_variables(int initial)
+{
+ int i, result;
+ struct pollfd slave_pty;
+
+ if (initial == 0) {
+ free(command.function);
+ for (i = 0; i < command.nrets; i++) {
+ if (command.returns[i].return_type == ret_number)
+ free(command.returns[i].return_value);
+ }
+ free(command.returns);
+
+ for (i = 0; i < command.nargs; i++) {
+ if (command.args[i].arg_type != arg_var)
+ free(command.args[i].arg_string);
+ }
+ free(command.args);
+ } else {
+ line = 0;
+ input_delay = 0;
+ vars = NULL;
+ nvars = 0;
+ input_str = NULL;
+ saved_output.allocated = 0;
+ saved_output.count = 0;
+ saved_output.readp = 0;
+ saved_output.data = NULL;
+ }
+
+ no_input = false;
+ command.function = NULL;
+ command.nargs = 0;
+ command.args = NULL;
+ command.nrets = 0;
+ command.returns = NULL;
+
+ /*
+ * Check the slave pty for stray output from the slave, at this
+ * point we should not see any data as it should have been
+ * consumed by the test functions. If we see data then we have
+ * either a bug or are not handling an output generating function
+ * correctly.
+ */
+ slave_pty.fd = master;
+ slave_pty.events = POLLIN;
+ result = poll(&slave_pty, 1, 0);
+
+ if (result < 0)
+ err(2, "Poll of slave pty failed");
+ else if (result > 0)
+ warnx("%s, %zu: Unexpected data from slave", cur_file, line);
+}
+
+/*
+ * Validate the response against the expected return. The variable
+ * i is the i into the rets array in command.
+ */
+static void
+validate(int i, void *data)
+{
+ char *response;
+ returns_t *byte_response;
+
+ byte_response = data;
+ if ((command.returns[i].return_type != ret_byte) &&
+ (command.returns[i].return_type != ret_err) &&
+ (command.returns[i].return_type != ret_ok)) {
+ if ((byte_response->return_type == ret_byte) ||
+ (byte_response->return_type == ret_err) ||
+ (byte_response->return_type == ret_ok))
+ err(1, "%s: expecting type %s, received type %s"
+ " at line %zu of file %s", __func__,
+ returns_enum_names[command.returns[i].return_type],
+ returns_enum_names[byte_response->return_type],
+ line, cur_file);
+
+ response = byte_response->return_value;
+ }
+
+ switch (command.returns[i].return_type) {
+ case ret_err:
+ validate_type(ret_err, byte_response, 0);
+ break;
+
+ case ret_ok:
+ validate_type(ret_ok, byte_response, 0);
+ break;
+
+ case ret_null:
+ validate_return("NULL", response, 0);
+ break;
+
+ case ret_nonnull:
+ validate_return("NULL", response, 1);
+ break;
+
+ case ret_string:
+ case ret_number:
+ validate_return(command.returns[i].return_value,
+ response, 0);
+ break;
+
+ case ret_ref:
+ validate_reference(i, response);
+ break;
+
+ case ret_byte:
+ validate_byte(&command.returns[i], byte_response, 0);
+ break;
+
+ default:
+ err(1, "Malformed statement at line %zu of file %s",
+ line, cur_file);
+ break;
+ }
+}
+
+/*
+ * Validate the return against the contents of a variable.
+ */
+static void
+validate_reference(int i, void *data)
+{
+ char *response;
+ returns_t *byte_response;
+ var_t *varp;
+
+ varp = &vars[command.returns[i].return_index];
+
+ byte_response = data;
+ if (command.returns[i].return_type != ret_byte)
+ response = data;
+
+ if (verbose) {
+ fprintf(stderr,
+ "%s: return type of %s, value %s \n", __func__,
+ returns_enum_names[varp->type],
+ (const char *)varp->value);
+ }
+
+ switch (varp->type) {
+ case ret_string:
+ case ret_number:
+ validate_return(varp->value, response, 0);
+ break;
+
+ case ret_byte:
+ validate_byte(varp->value, byte_response, 0);
+ break;
+
+ default:
+ err(1,
+ "Invalid return type for reference at line %zu of file %s",
+ line, cur_file);
+ break;
+ }
+}
+
+/*
+ * Validate the return type against the expected type, throw an error
+ * if they don't match.
+ */
+static void
+validate_type(returns_enum_t expected, returns_t *value, int check)
+{
+ if (((check == 0) && (expected != value->return_type)) ||
+ ((check == 1) && (expected == value->return_type)))
+ err(1, "Validate expected type %s %s %s line %zu of file %s",
+ returns_enum_names[expected],
+ (check == 0)? "matching" : "not matching",
+ returns_enum_names[value->return_type], line, cur_file);
+
+ if (verbose) {
+ fprintf(stderr, "Validate expected type %s %s %s line %zu"
+ " of file %s\n",
+ returns_enum_names[expected],
+ (check == 0)? "matching" : "not matching",
+ returns_enum_names[value->return_type], line, cur_file);
+ }
+}
+
+/*
+ * Validate the return value against the expected value, throw an error
+ * if they don't match.
+ */
+static void
+validate_return(const char *expected, const char *value, int check)
+{
+ if (((check == 0) && strcmp(expected, value) != 0) ||
+ ((check == 1) && strcmp(expected, value) == 0))
+ errx(1, "Validate expected %s %s %s line %zu of file %s",
+ expected,
+ (check == 0)? "matching" : "not matching", value,
+ line, cur_file);
+ if (verbose) {
+ fprintf(stderr, "Validated expected value %s %s %s "
+ "at line %zu of file %s\n", expected,
+ (check == 0)? "matches" : "does not match",
+ value, line, cur_file);
+ }
+}
+
+/*
+ * Validate the return value against the expected value, throw an error
+ * if they don't match expectations.
+ */
+static void
+validate_byte(returns_t *expected, returns_t *value, int check)
+{
+ char *ch;
+ size_t i;
+
+ if (verbose) {
+ ch = value->return_value;
+ fprintf(stderr, "checking returned byte stream: ");
+ for (i = 0; i < value->return_len; i++)
+ fprintf(stderr, "%s0x%x", (i != 0)? ", " : "", ch[i]);
+ fprintf(stderr, "\n");
+
+ fprintf(stderr, "%s byte stream: ",
+ (check == 0)? "matches" : "does not match");
+ ch = (char *) expected->return_value;
+ for (i = 0; i < expected->return_len; i++)
+ fprintf(stderr, "%s0x%x", (i != 0)? ", " : "", ch[i]);
+ fprintf(stderr, "\n");
+ }
+
+ /*
+ * No chance of a match if lengths differ...
+ */
+ if ((check == 0) && (expected->return_len != value->return_len))
+ errx(1, "Byte validation failed, length mismatch, expected %zu,"
+ "received %zu", expected->return_len, value->return_len);
+
+ /*
+ * If check is 0 then we want to throw an error IFF the byte streams
+ * do not match, if check is 1 then throw an error if the byte
+ * streams match.
+ */
+ if (((check == 0) && memcmp(expected->return_value, value->return_value,
+ value->return_len) != 0) ||
+ ((check == 1) && (expected->return_len == value->return_len) &&
+ memcmp(expected->return_value, value->return_value,
+ value->return_len) == 0))
+ errx(1, "Validate expected %s byte stream at line %zu"
+ "of file %s",
+ (check == 0)? "matching" : "not matching", line, cur_file);
+ if (verbose) {
+ fprintf(stderr, "Validated expected %s byte stream "
+ "at line %zu of file %s\n",
+ (check == 0)? "matching" : "not matching",
+ line, cur_file);
+ }
+}
+
+/*
+ * Validate the variable at i against the expected value, throw an
+ * error if they don't match, if check is non-zero then the match is
+ * negated.
+ */
+static void
+validate_variable(int ret, returns_enum_t type, const void *value, int i,
+ int check)
+{
+ returns_t *retval;
+ var_t *varptr;
+
+ retval = &command.returns[ret];
+ varptr = &vars[command.returns[ret].return_index];
+
+ if (varptr->value == NULL)
+ err(1, "Variable %s has no value assigned to it", varptr->name);
+
+
+ if (varptr->type != type)
+ err(1, "Variable %s is not the expected type", varptr->name);
+
+ if (type != ret_byte) {
+ if ((((check == 0) && strcmp(value, varptr->value) != 0))
+ || ((check == 1) && strcmp(value, varptr->value) == 0))
+ err(1, "Variable %s contains %s instead of %s"
+ " value %s at line %zu of file %s",
+ varptr->name, (const char *)varptr->value,
+ (check == 0)? "expected" : "not matching",
+ (const char *)value,
+ line, cur_file);
+ if (verbose) {
+ fprintf(stderr, "Variable %s contains %s value "
+ "%s at line %zu of file %s\n",
+ varptr->name,
+ (check == 0)? "expected" : "not matching",
+ (const char *)varptr->value, line, cur_file);
+ }
+ } else {
+ if ((check == 0) && (retval->return_len != varptr->len))
+ err(1, "Byte validation failed, length mismatch");
+
+ /*
+ * If check is 0 then we want to throw an error IFF
+ * the byte streams do not match, if check is 1 then
+ * throw an error if the byte streams match.
+ */
+ if (((check == 0) && memcmp(retval->return_value, varptr->value,
+ varptr->len) != 0) ||
+ ((check == 1) && (retval->return_len == varptr->len) &&
+ memcmp(retval->return_value, varptr->value,
+ varptr->len) == 0))
+ err(1, "Validate expected %s byte stream at line %zu"
+ " of file %s",
+ (check == 0)? "matching" : "not matching",
+ line, cur_file);
+ if (verbose) {
+ fprintf(stderr, "Validated expected %s byte stream "
+ "at line %zu of file %s\n",
+ (check == 0)? "matching" : "not matching",
+ line, cur_file);
+ }
+ }
+}
+
+/*
+ * Write a string to the command pipe - we feed the number of bytes coming
+ * down first to allow storage allocation and then follow up with the data.
+ * If cmd is NULL then feed a -1 down the pipe to say the end of the args.
+ */
+static void
+write_cmd_pipe(char *cmd)
+{
+ args_t arg;
+ size_t len;
+
+ if (cmd == NULL)
+ len = 0;
+ else
+ len = strlen(cmd);
+
+ arg.arg_type = arg_static;
+ arg.arg_len = len;
+ arg.arg_string = cmd;
+ write_cmd_pipe_args(arg.arg_type, &arg);
+
+}
+
+static void
+write_cmd_pipe_args(args_state_t type, void *data)
+{
+ var_t *var_data;
+ args_t *arg_data;
+ int len, send_type;
+ void *cmd;
+
+ arg_data = data;
+ switch (type) {
+ case arg_var:
+ var_data = data;
+ len = var_data->len;
+ cmd = var_data->value;
+ if (type == arg_byte)
+ send_type = ret_byte;
+ else
+ send_type = ret_string;
+ break;
+
+ case arg_null:
+ send_type = ret_null;
+ len = 0;
+ break;
+
+ default:
+ if ((arg_data->arg_len == 0) && (arg_data->arg_string == NULL))
+ len = -1;
+ else
+ len = arg_data->arg_len;
+ cmd = arg_data->arg_string;
+ if (type == arg_byte)
+ send_type = ret_byte;
+ else
+ send_type = ret_string;
+ }
+
+ if (verbose) {
+ fprintf(stderr, "Writing type %s to command pipe\n",
+ returns_enum_names[send_type]);
+ }
+
+ if (write(cmdpipe[WRITE_PIPE], &send_type, sizeof(int)) < 0)
+ err(1, "command pipe write for type failed");
+
+ if (verbose) {
+ fprintf(stderr, "Writing length %d to command pipe\n", len);
+ }
+
+ if (write(cmdpipe[WRITE_PIPE], &len, sizeof(int)) < 0)
+ err(1, "command pipe write for length failed");
+
+ if (len > 0) {
+ if (verbose) {
+ fprintf(stderr, "Writing data >%s< to command pipe\n",
+ (const char *)cmd);
+ }
+ if (write(cmdpipe[WRITE_PIPE], cmd, len) < 0)
+ err(1, "command pipe write of data failed");
+ }
+}
+
+/*
+ * Read a response from the command pipe, first we will receive the
+ * length of the response then the actual data.
+ */
+static void
+read_cmd_pipe(returns_t *response)
+{
+ int len, type;
+ struct pollfd rfd[2];
+ char *str;
+
+ /*
+ * Check if there is data to read - just in case slave has died, we
+ * don't want to block on the read and just hang. We also check
+ * output from the slave because the slave may be blocked waiting
+ * for a flush on its stdout.
+ */
+ rfd[0].fd = slvpipe[READ_PIPE];
+ rfd[0].events = POLLIN;
+ rfd[1].fd = master;
+ rfd[1].events = POLLIN;
+
+ do {
+ if (poll(rfd, 2, 4000) == 0)
+ errx(2, "%s, %zu: Command pipe read timeout",
+ cur_file, line);
+
+ if ((rfd[1].revents & POLLIN) == POLLIN) {
+ if (verbose) {
+ fprintf(stderr,
+ "draining output from slave\n");
+ }
+ save_slave_output(false);
+ }
+ }
+ while((rfd[1].revents & POLLIN) == POLLIN);
+
+ if (read(slvpipe[READ_PIPE], &type, sizeof(int)) < 0)
+ err(1, "command pipe read for type failed");
+ response->return_type = type;
+
+ if ((type != ret_ok) && (type != ret_err) && (type != ret_count)) {
+ if (read(slvpipe[READ_PIPE], &len, sizeof(int)) < 0)
+ err(1, "command pipe read for length failed");
+ response->return_len = len;
+
+ if (verbose) {
+ fprintf(stderr,
+ "Reading %d bytes from command pipe\n", len);
+ }
+
+ if ((response->return_value = malloc(len + 1)) == NULL)
+ err(1, "Failed to alloc memory for cmd pipe read");
+
+ if (read(slvpipe[READ_PIPE], response->return_value, len) < 0)
+ err(1, "command pipe read of data failed");
+
+ if (response->return_type != ret_byte) {
+ str = response->return_value;
+ str[len] = '\0';
+
+ if (verbose) {
+ fprintf(stderr, "Read data >%s< from pipe\n",
+ (const char *)response->return_value);
+ }
+ }
+ } else {
+ response->return_value = NULL;
+ if (type == ret_count) {
+ if (read(slvpipe[READ_PIPE], &len, sizeof(int)) < 0)
+ err(1, "command pipe read for number of "
+ "returns failed");
+ response->return_len = len;
+ }
+
+ if (verbose) {
+ fprintf(stderr, "Read type %s from pipe\n",
+ returns_enum_names[type]);
+ }
+ }
+}
+
+/*
+ * Check for writes from the slave on the pty, save the output into a
+ * buffer for later checking if discard is false.
+ */
+#define MAX_DRAIN 256
+
+static void
+save_slave_output(bool discard)
+{
+ char *new_data, drain[MAX_DRAIN];
+ size_t to_allocate;
+ ssize_t result;
+ size_t i;
+
+ result = 0;
+ for (;;) {
+ if (result == -1)
+ err(2, "poll of slave pty failed");
+ result = MAX_DRAIN;
+ if ((result = read(master, drain, result)) < 0) {
+ if (errno == EAGAIN)
+ break;
+ else
+ err(2, "draining slave pty failed");
+ }
+ if (result == 0)
+ abort();
+
+ if (!discard) {
+ if ((size_t)result >
+ (saved_output.allocated - saved_output.count)) {
+ to_allocate = 1024 * ((result / 1024) + 1);
+
+ if ((new_data = realloc(saved_output.data,
+ saved_output.allocated + to_allocate))
+ == NULL)
+ err(2, "Realloc of saved_output failed");
+ saved_output.data = new_data;
+ saved_output.allocated += to_allocate;
+ }
+
+ if (verbose) {
+ fprintf(stderr, "count = %zu, "
+ "allocated = %zu\n", saved_output.count,
+ saved_output.allocated);
+ for (i = 0; i < (size_t)result; i++) {
+ fprintf(stderr, "Saving slave output "
+ "at %zu: 0x%x (%c)\n",
+ saved_output.count + i, drain[i],
+ (drain[i] >= ' ')? drain[i] : '-');
+ }
+ }
+
+ memcpy(&saved_output.data[saved_output.count], drain,
+ result);
+ saved_output.count += result;
+
+ if (verbose) {
+ fprintf(stderr, "count = %zu, "
+ "allocated = %zu\n", saved_output.count,
+ saved_output.allocated);
+ }
+ } else {
+ if (verbose) {
+ for (i = 0; i < (size_t)result; i++) {
+ fprintf(stderr, "Discarding slave "
+ "output 0x%x (%c)\n",
+ drain[i],
+ (drain[i] >= ' ')? drain[i] : '-');
+ }
+ }
+ }
+ }
+}
+
+static void
+yyerror(const char *msg)
+{
+ warnx("%s in line %zu of file %s", msg, line, cur_file);
+}