testlang_parse.y revision 1.9 1 1.1 blymn %{
2 1.9 christos /* $NetBSD: testlang_parse.y,v 1.9 2011/08/29 12:49:37 christos Exp $ */
3 1.1 blymn
4 1.1 blymn /*-
5 1.1 blymn * Copyright 2009 Brett Lymn <blymn (at) NetBSD.org>
6 1.1 blymn *
7 1.1 blymn * All rights reserved.
8 1.1 blymn *
9 1.1 blymn * This code has been donated to The NetBSD Foundation by the Author.
10 1.1 blymn *
11 1.1 blymn * Redistribution and use in source and binary forms, with or without
12 1.1 blymn * modification, are permitted provided that the following conditions
13 1.1 blymn * are met:
14 1.1 blymn * 1. Redistributions of source code must retain the above copyright
15 1.1 blymn * notice, this list of conditions and the following disclaimer.
16 1.1 blymn * 2. The name of the author may not be used to endorse or promote products
17 1.1 blymn * derived from this software withough specific prior written permission
18 1.1 blymn *
19 1.1 blymn * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 1.1 blymn * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 1.1 blymn * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 1.1 blymn * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 1.1 blymn * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 1.1 blymn * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 1.1 blymn * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 1.1 blymn * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 1.1 blymn * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 1.1 blymn * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 1.1 blymn *
30 1.1 blymn *
31 1.1 blymn */
32 1.1 blymn #include <assert.h>
33 1.1 blymn #include <errno.h>
34 1.1 blymn #include <fcntl.h>
35 1.3 christos #include <err.h>
36 1.3 christos #include <unistd.h>
37 1.1 blymn #include <poll.h>
38 1.1 blymn #include <stdbool.h>
39 1.1 blymn #include <stdio.h>
40 1.1 blymn #include <string.h>
41 1.1 blymn #include <sys/syslimits.h>
42 1.1 blymn #include <time.h>
43 1.6 christos #include <vis.h>
44 1.9 christos #include <stdint.h>
45 1.1 blymn #include "returns.h"
46 1.1 blymn
47 1.1 blymn #define YYDEBUG 1
48 1.1 blymn
49 1.1 blymn extern int verbose;
50 1.1 blymn extern int cmdpipe[2];
51 1.1 blymn extern int slvpipe[2];
52 1.1 blymn extern int master;
53 1.1 blymn extern struct pollfd readfd;
54 1.1 blymn extern char *check_path;
55 1.1 blymn extern char *cur_file; /* from director.c */
56 1.1 blymn
57 1.3 christos int yylex(void);
58 1.3 christos
59 1.1 blymn size_t line;
60 1.1 blymn
61 1.1 blymn static int input_delay;
62 1.1 blymn
63 1.1 blymn /* time delay between inputs chars - default to 0.1ms minimum to prevent
64 1.1 blymn * problems with input tests
65 1.1 blymn */
66 1.1 blymn #define DELAY_MIN 0.1
67 1.1 blymn static struct timespec delay_spec = {0, 1000 * DELAY_MIN};
68 1.1 blymn static char *input_str; /* string to feed in as input */
69 1.1 blymn static bool no_input; /* don't need more input */
70 1.1 blymn
71 1.1 blymn #define READ_PIPE 0
72 1.1 blymn #define WRITE_PIPE 1
73 1.1 blymn
74 1.4 christos const char *returns_enum_names[] = {
75 1.1 blymn "unused", "numeric", "string", "byte", "ERR", "OK", "NULL", "not NULL",
76 1.1 blymn "variable", "reference", "returns count", "slave error"
77 1.1 blymn };
78 1.1 blymn
79 1.1 blymn typedef enum {
80 1.1 blymn arg_static,
81 1.1 blymn arg_byte,
82 1.2 blymn arg_var,
83 1.2 blymn arg_null
84 1.1 blymn } args_state_t;
85 1.1 blymn
86 1.4 christos static const char *args_enum_names[] = {
87 1.2 blymn "static", "byte", "var", "NULL"
88 1.1 blymn };
89 1.1 blymn
90 1.1 blymn typedef struct {
91 1.1 blymn args_state_t arg_type;
92 1.1 blymn size_t arg_len;
93 1.1 blymn char *arg_string;
94 1.1 blymn int var_index;
95 1.1 blymn } args_t;
96 1.1 blymn
97 1.1 blymn typedef struct {
98 1.1 blymn char *function;
99 1.1 blymn int nrets; /* number of returns */
100 1.1 blymn returns_t *returns; /* array of expected returns */
101 1.1 blymn int nargs; /* number of arguments */
102 1.1 blymn args_t *args; /* arguments for the call */
103 1.1 blymn } cmd_line_t;
104 1.1 blymn
105 1.1 blymn static cmd_line_t command;
106 1.1 blymn
107 1.1 blymn typedef struct {
108 1.1 blymn char *name;
109 1.1 blymn size_t len;
110 1.1 blymn returns_enum_t type;
111 1.1 blymn void *value;
112 1.1 blymn } var_t;
113 1.1 blymn
114 1.1 blymn static size_t nvars; /* Number of declared variables */
115 1.1 blymn static var_t *vars; /* Variables defined during the test. */
116 1.1 blymn
117 1.1 blymn static int check_function_table(char *, const char *[], int);
118 1.4 christos static int find_var_index(const char *);
119 1.1 blymn static void assign_arg(args_state_t, void *);
120 1.1 blymn static int assign_var(char *);
121 1.1 blymn void init_parse_variables(int);
122 1.1 blymn static void validate(int, void *);
123 1.4 christos static void validate_return(const char *, const char *, int);
124 1.4 christos static void validate_variable(int, returns_enum_t, const void *, int, int);
125 1.1 blymn static void validate_byte(returns_t *, returns_t *, int);
126 1.1 blymn static void write_cmd_pipe(char *);
127 1.1 blymn static void write_cmd_pipe_args(args_state_t, void *);
128 1.1 blymn static void read_cmd_pipe(returns_t *);
129 1.4 christos static void write_func_and_args(void);
130 1.1 blymn static void compare_streams(char *, bool);
131 1.4 christos static void do_function_call(size_t);
132 1.1 blymn static void save_slave_output(bool);
133 1.1 blymn static void validate_type(returns_enum_t, returns_t *, int);
134 1.1 blymn static void set_var(returns_enum_t, char *, void *);
135 1.1 blymn static void validate_reference(int, void *);
136 1.1 blymn static char *numeric_or(char *, char *);
137 1.4 christos static char *get_numeric_var(const char *);
138 1.1 blymn
139 1.1 blymn static const char *input_functions[] = {
140 1.1 blymn "inch", "getch", "getnstr", "getstr", "innstr", "instr", "mvgetnstr",
141 1.1 blymn "mvgetstr", "mvinchstr", "mvinchnstr", "mvgetnstr", "mvgetstr",
142 1.1 blymn "mvinchstr", "mvinchnstr", "winch", "wgetch", "wgetnstr", "wgetstr",
143 1.1 blymn "winchnstr", "winchstr", "winnstr", "winstr"
144 1.1 blymn };
145 1.1 blymn
146 1.1 blymn static const unsigned ninput_functions =
147 1.1 blymn sizeof(input_functions) / sizeof(char *);
148 1.1 blymn
149 1.1 blymn saved_data_t saved_output;
150 1.1 blymn
151 1.1 blymn %}
152 1.1 blymn
153 1.1 blymn %union {
154 1.1 blymn char *string;
155 1.1 blymn returns_t *retval;
156 1.1 blymn }
157 1.1 blymn
158 1.1 blymn %token <string> PATH
159 1.1 blymn %token <string> STRING
160 1.1 blymn %token <retval> BYTE
161 1.1 blymn %token <string> VARNAME
162 1.1 blymn %token <string> FILENAME
163 1.1 blymn %token <string> VARIABLE
164 1.1 blymn %token <string> REFERENCE
165 1.1 blymn %token <string> NULL_RET
166 1.1 blymn %token <string> NON_NULL
167 1.1 blymn %token <string> ERR_RET
168 1.1 blymn %token <string> OK_RET
169 1.1 blymn %token <string> numeric
170 1.1 blymn %token <string> DELAY
171 1.1 blymn %token <string> INPUT
172 1.1 blymn %token <string> COMPARE
173 1.1 blymn %token <string> COMPAREND
174 1.1 blymn %token <string> ASSIGN
175 1.1 blymn %token EOL CALL CHECK NOINPUT OR LHB RHB
176 1.1 blymn %token CALL2 CALL3 CALL4 DRAIN
177 1.1 blymn
178 1.1 blymn %nonassoc OR
179 1.1 blymn
180 1.1 blymn %%
181 1.1 blymn
182 1.1 blymn statement : /* empty */
183 1.1 blymn | assign statement
184 1.1 blymn | call statement
185 1.1 blymn | call2 statement
186 1.1 blymn | call3 statement
187 1.1 blymn | call4 statement
188 1.1 blymn | check statement
189 1.1 blymn | delay statement
190 1.1 blymn | input statement
191 1.1 blymn | noinput statement
192 1.1 blymn | compare statement
193 1.1 blymn | comparend statement
194 1.1 blymn | eol statement
195 1.1 blymn ;
196 1.1 blymn
197 1.1 blymn assign : ASSIGN VARNAME numeric {set_var(ret_number, $2, $3);} eol
198 1.1 blymn | ASSIGN VARNAME LHB expr RHB {set_var(ret_number, $2, $<string>4);} eol
199 1.1 blymn | ASSIGN VARNAME STRING {set_var(ret_string, $2, $3);} eol
200 1.1 blymn | ASSIGN VARNAME BYTE {set_var(ret_byte, $2, $3);} eol
201 1.1 blymn ;
202 1.1 blymn
203 1.1 blymn call : CALL result fn_name args eol {
204 1.1 blymn do_function_call(1);
205 1.1 blymn }
206 1.1 blymn ;
207 1.1 blymn
208 1.1 blymn call2 : CALL2 result result fn_name args eol {
209 1.1 blymn do_function_call(2);
210 1.1 blymn }
211 1.1 blymn ;
212 1.1 blymn
213 1.1 blymn call3 : CALL3 result result result fn_name args eol {
214 1.1 blymn do_function_call(3);
215 1.1 blymn }
216 1.1 blymn ;
217 1.1 blymn
218 1.1 blymn call4 : CALL4 result result result result fn_name args eol {
219 1.1 blymn do_function_call(4);
220 1.1 blymn }
221 1.1 blymn ;
222 1.1 blymn
223 1.1 blymn check : CHECK var returns eol {
224 1.4 christos returns_t retvar;
225 1.1 blymn var_t *vptr;
226 1.1 blymn if (command.returns[0].return_index == -1)
227 1.4 christos err(1, "Undefined variable in check statement, line %zu"
228 1.1 blymn " of file %s", line, cur_file);
229 1.1 blymn
230 1.9 christos if (verbose) {
231 1.1 blymn fprintf(stderr, "Checking contents of variable %s for %s\n",
232 1.5 christos vars[command.returns[0].return_index].name,
233 1.5 christos returns_enum_names[command.returns[1].return_type]);
234 1.9 christos }
235 1.1 blymn
236 1.8 christos if (((command.returns[1].return_type == ret_byte) &&
237 1.1 blymn (vars[command.returns[0].return_index].type != ret_byte)) ||
238 1.8 christos vars[command.returns[0].return_index].type != ret_string)
239 1.1 blymn err(1, "Var type %s (%d) does not match return type %s (%d)",
240 1.9 christos returns_enum_names[
241 1.9 christos vars[command.returns[0].return_index].type],
242 1.1 blymn vars[command.returns[0].return_index].type,
243 1.1 blymn returns_enum_names[command.returns[1].return_type],
244 1.1 blymn command.returns[1].return_type);
245 1.1 blymn
246 1.1 blymn switch (command.returns[1].return_type) {
247 1.1 blymn case ret_err:
248 1.1 blymn validate_variable(0, ret_string, "ERR",
249 1.1 blymn command.returns[0].return_index, 0);
250 1.1 blymn break;
251 1.1 blymn
252 1.1 blymn case ret_ok:
253 1.1 blymn validate_variable(0, ret_string, "OK",
254 1.1 blymn command.returns[0].return_index, 0);
255 1.1 blymn break;
256 1.1 blymn
257 1.1 blymn case ret_null:
258 1.1 blymn validate_variable(0, ret_string, "NULL",
259 1.1 blymn command.returns[0].return_index, 0);
260 1.1 blymn break;
261 1.1 blymn
262 1.1 blymn case ret_nonnull:
263 1.1 blymn validate_variable(0, ret_string, "NULL",
264 1.1 blymn command.returns[0].return_index, 1);
265 1.1 blymn break;
266 1.1 blymn
267 1.1 blymn case ret_string:
268 1.1 blymn case ret_number:
269 1.9 christos if (verbose) {
270 1.1 blymn fprintf(stderr, " %s == returned %s\n",
271 1.5 christos (const char *)command.returns[1].return_value,
272 1.5 christos (const char *)
273 1.5 christos vars[command.returns[0].return_index].value);
274 1.9 christos }
275 1.1 blymn validate_variable(0, ret_string,
276 1.5 christos command.returns[1].return_value,
277 1.5 christos command.returns[0].return_index, 0);
278 1.1 blymn break;
279 1.1 blymn
280 1.1 blymn case ret_byte:
281 1.1 blymn vptr = &vars[command.returns[0].return_index];
282 1.4 christos retvar.return_len = vptr->len;
283 1.4 christos retvar.return_type = vptr->type;
284 1.4 christos retvar.return_value = vptr->value;
285 1.4 christos validate_byte(&retvar, &command.returns[1], 0);
286 1.1 blymn break;
287 1.1 blymn
288 1.1 blymn default:
289 1.4 christos err(1, "Malformed check statement at line %zu "
290 1.1 blymn "of file %s", line, cur_file);
291 1.1 blymn break;
292 1.1 blymn }
293 1.1 blymn
294 1.1 blymn init_parse_variables(0);
295 1.1 blymn }
296 1.1 blymn ;
297 1.1 blymn
298 1.1 blymn delay : DELAY numeric eol {
299 1.1 blymn /* set the inter-character delay */
300 1.1 blymn if (sscanf($2, "%d", &input_delay) == 0)
301 1.1 blymn err(1, "delay specification %s could not be converted to "
302 1.4 christos "numeric at line %zu of file %s", $2, line, cur_file);
303 1.9 christos if (verbose) {
304 1.1 blymn fprintf(stderr, "Set input delay to %d ms\n", input_delay);
305 1.9 christos }
306 1.1 blymn
307 1.1 blymn if (input_delay < DELAY_MIN)
308 1.1 blymn input_delay = 1000 * DELAY_MIN; /* ms to ns */
309 1.1 blymn /*
310 1.1 blymn * Fill in the timespec structure now ready for use later.
311 1.1 blymn * The delay is specified in milliseconds so convert to timespec
312 1.1 blymn * values
313 1.1 blymn */
314 1.1 blymn delay_spec.tv_sec = input_delay / 1000;
315 1.1 blymn delay_spec.tv_nsec = (input_delay - 1000 * delay_spec.tv_sec) * 1000;
316 1.9 christos if (verbose) {
317 1.9 christos fprintf(stderr, "set delay to %jd.%jd\n",
318 1.9 christos (intmax_t)delay_spec.tv_sec,
319 1.9 christos (intmax_t)delay_spec.tv_nsec);
320 1.9 christos }
321 1.1 blymn
322 1.1 blymn init_parse_variables(0);
323 1.1 blymn }
324 1.1 blymn ;
325 1.1 blymn
326 1.1 blymn input : INPUT STRING eol {
327 1.1 blymn if (input_str != NULL) {
328 1.5 christos warnx("%s, %zu: Discarding unused input string",
329 1.5 christos cur_file, line);
330 1.1 blymn free(input_str);
331 1.1 blymn }
332 1.1 blymn
333 1.1 blymn if ((input_str = malloc(strlen($2) + 1)) == NULL)
334 1.1 blymn err(2, "Cannot allocate memory for input string");
335 1.1 blymn
336 1.1 blymn strlcpy(input_str, $2, strlen($2) + 1);
337 1.1 blymn }
338 1.1 blymn ;
339 1.1 blymn
340 1.1 blymn
341 1.1 blymn noinput : NOINPUT eol {
342 1.1 blymn if (input_str != NULL) {
343 1.5 christos warnx("%s, %zu: Discarding unused input string",
344 1.5 christos cur_file, line);
345 1.1 blymn free(input_str);
346 1.1 blymn }
347 1.1 blymn
348 1.1 blymn no_input = true;
349 1.1 blymn }
350 1.1 blymn
351 1.1 blymn compare : COMPARE PATH eol
352 1.1 blymn | COMPARE FILENAME eol
353 1.1 blymn {
354 1.1 blymn compare_streams($2, true);
355 1.1 blymn }
356 1.1 blymn ;
357 1.1 blymn
358 1.1 blymn
359 1.1 blymn comparend : COMPAREND PATH eol
360 1.1 blymn | COMPAREND FILENAME eol
361 1.1 blymn {
362 1.1 blymn compare_streams($2, false);
363 1.1 blymn }
364 1.1 blymn ;
365 1.1 blymn
366 1.1 blymn
367 1.1 blymn result : returns
368 1.1 blymn | var
369 1.1 blymn | reference
370 1.1 blymn ;
371 1.1 blymn
372 1.1 blymn returns : numeric { assign_rets(ret_number, $1); }
373 1.1 blymn | LHB expr RHB { assign_rets(ret_number, $<string>2); }
374 1.1 blymn | STRING { assign_rets(ret_string, $1); }
375 1.1 blymn | BYTE { assign_rets(ret_byte, (void *) $1); }
376 1.1 blymn | ERR_RET { assign_rets(ret_err, NULL); }
377 1.1 blymn | OK_RET { assign_rets(ret_ok, NULL); }
378 1.1 blymn | NULL_RET { assign_rets(ret_null, NULL); }
379 1.1 blymn | NON_NULL { assign_rets(ret_nonnull, NULL); }
380 1.1 blymn ;
381 1.1 blymn
382 1.1 blymn var : VARNAME {
383 1.1 blymn assign_rets(ret_var, $1);
384 1.1 blymn }
385 1.1 blymn ;
386 1.1 blymn
387 1.1 blymn reference : VARIABLE {
388 1.1 blymn assign_rets(ret_ref, $1);
389 1.1 blymn }
390 1.1 blymn
391 1.1 blymn fn_name : VARNAME {
392 1.1 blymn if (command.function != NULL)
393 1.1 blymn free(command.function);
394 1.1 blymn
395 1.1 blymn command.function = malloc(strlen($1) + 1);
396 1.1 blymn if (command.function == NULL)
397 1.1 blymn err(1, "Could not allocate memory for function name");
398 1.1 blymn strcpy(command.function, $1);
399 1.1 blymn }
400 1.1 blymn ;
401 1.1 blymn
402 1.1 blymn expr : numeric
403 1.1 blymn | VARIABLE
404 1.1 blymn { $<string>$ = get_numeric_var($1); }
405 1.1 blymn | expr OR expr
406 1.1 blymn { $<string>$ = numeric_or($<string>1, $<string>3); }
407 1.1 blymn ;
408 1.1 blymn
409 1.1 blymn args : /* empty */
410 1.1 blymn | LHB expr RHB { assign_arg(arg_static, $<string>2); } args
411 1.1 blymn | numeric { assign_arg(arg_static, $1); } args
412 1.1 blymn | STRING { assign_arg(arg_static, $1); } args
413 1.1 blymn | BYTE { assign_arg(arg_byte, $1); } args
414 1.1 blymn | PATH { assign_arg(arg_static, $1); } args
415 1.1 blymn | FILENAME { assign_arg(arg_static, $1); } args
416 1.1 blymn | VARNAME { assign_arg(arg_static, $1); } args
417 1.1 blymn | VARIABLE { assign_arg(arg_var, $1); } args
418 1.2 blymn | NULL_RET { assign_arg(arg_null, $1); } args
419 1.1 blymn ;
420 1.1 blymn
421 1.1 blymn eol : EOL
422 1.1 blymn ;
423 1.1 blymn
424 1.1 blymn %%
425 1.1 blymn
426 1.6 christos static void
427 1.6 christos excess(const char *fname, size_t lineno, const char *func, const char *comment,
428 1.6 christos const void *data, size_t datalen)
429 1.6 christos {
430 1.6 christos size_t dstlen = datalen * 4 + 1;
431 1.6 christos char *dst = malloc(dstlen);
432 1.6 christos
433 1.6 christos if (dst == NULL)
434 1.6 christos err(1, "malloc");
435 1.6 christos
436 1.6 christos if (strnvisx(dst, dstlen, data, datalen, VIS_WHITE | VIS_OCTAL) == -1)
437 1.6 christos err(1, "strnvisx");
438 1.6 christos
439 1.6 christos warnx("%s, %zu: [%s] Excess %zu bytes%s [%s]",
440 1.6 christos fname, lineno, func, datalen, comment, dst);
441 1.6 christos free(dst);
442 1.6 christos }
443 1.6 christos
444 1.1 blymn /*
445 1.1 blymn * Get the value of a variable, error if the variable has not been set or
446 1.1 blymn * is not a numeric type.
447 1.1 blymn */
448 1.1 blymn static char *
449 1.4 christos get_numeric_var(const char *var)
450 1.1 blymn {
451 1.4 christos int i;
452 1.1 blymn
453 1.4 christos if ((i = find_var_index(var)) < 0)
454 1.1 blymn err(1, "Variable %s is undefined", var);
455 1.1 blymn
456 1.4 christos if (vars[i].type != ret_number)
457 1.4 christos err(1, "Variable %s is not a numeric type", var);
458 1.1 blymn
459 1.4 christos return vars[i].value;
460 1.1 blymn }
461 1.1 blymn
462 1.1 blymn /*
463 1.1 blymn * Perform a bitwise OR on two numbers and return the result.
464 1.1 blymn */
465 1.1 blymn static char *
466 1.1 blymn numeric_or(char *n1, char *n2)
467 1.1 blymn {
468 1.1 blymn unsigned long i1, i2, result;
469 1.1 blymn char *ret;
470 1.1 blymn
471 1.1 blymn i1 = strtoul(n1, NULL, 10);
472 1.1 blymn i2 = strtoul(n2, NULL, 10);
473 1.1 blymn
474 1.1 blymn result = i1 | i2;
475 1.1 blymn asprintf(&ret, "%lu", result);
476 1.1 blymn
477 1.9 christos if (verbose) {
478 1.5 christos fprintf(stderr, "numeric or of 0x%lx (%s) and 0x%lx (%s)"
479 1.5 christos " results in 0x%lx (%s)\n",
480 1.5 christos i1, n1, i2, n2, result, ret);
481 1.9 christos }
482 1.1 blymn
483 1.1 blymn return ret;
484 1.1 blymn }
485 1.1 blymn
486 1.1 blymn /*
487 1.1 blymn * Assign the value given to the named variable.
488 1.1 blymn */
489 1.1 blymn static void
490 1.1 blymn set_var(returns_enum_t type, char *name, void *value)
491 1.1 blymn {
492 1.4 christos int i;
493 1.1 blymn char *number;
494 1.1 blymn returns_t *ret;
495 1.1 blymn
496 1.4 christos i = find_var_index(name);
497 1.4 christos if (i < 0)
498 1.4 christos i = assign_var(name);
499 1.1 blymn
500 1.4 christos vars[i].type = type;
501 1.1 blymn if ((type == ret_number) || (type == ret_string)) {
502 1.1 blymn number = value;
503 1.4 christos vars[i].len = strlen(number) + 1;
504 1.4 christos vars[i].value = malloc(vars[i].len + 1);
505 1.4 christos if (vars[i].value == NULL)
506 1.1 blymn err(1, "Could not malloc memory for assign string");
507 1.4 christos strcpy(vars[i].value, number);
508 1.1 blymn } else {
509 1.1 blymn /* can only be a byte value */
510 1.1 blymn ret = value;
511 1.4 christos vars[i].len = ret->return_len;
512 1.4 christos vars[i].value = malloc(vars[i].len);
513 1.4 christos if (vars[i].value == NULL)
514 1.1 blymn err(1, "Could not malloc memory to assign byte string");
515 1.4 christos memcpy(vars[i].value, ret->return_value, vars[i].len);
516 1.1 blymn }
517 1.1 blymn }
518 1.1 blymn
519 1.1 blymn /*
520 1.1 blymn * Add a new variable to the vars array, the value will be assigned later,
521 1.1 blymn * when a test function call returns.
522 1.1 blymn */
523 1.1 blymn static int
524 1.1 blymn assign_var(char *varname)
525 1.1 blymn {
526 1.1 blymn var_t *temp;
527 1.1 blymn char *name;
528 1.1 blymn
529 1.1 blymn if ((name = malloc(strlen(varname) + 1)) == NULL)
530 1.1 blymn err(1, "Alloc of varname failed");
531 1.1 blymn
532 1.9 christos if ((temp = realloc(vars, sizeof(*temp) * (nvars + 1))) == NULL) {
533 1.1 blymn free(name);
534 1.1 blymn err(1, "Realloc of vars array failed");
535 1.1 blymn }
536 1.1 blymn
537 1.1 blymn strcpy(name, varname);
538 1.1 blymn vars = temp;
539 1.1 blymn vars[nvars].name = name;
540 1.1 blymn vars[nvars].len = 0;
541 1.1 blymn vars[nvars].value = NULL;
542 1.1 blymn nvars++;
543 1.1 blymn
544 1.1 blymn return (nvars - 1);
545 1.1 blymn }
546 1.1 blymn
547 1.1 blymn /*
548 1.1 blymn * Allocate and assign a new argument of the given type.
549 1.1 blymn */
550 1.1 blymn static void
551 1.1 blymn assign_arg(args_state_t arg_type, void *arg)
552 1.1 blymn {
553 1.1 blymn args_t *temp, cur;
554 1.4 christos char *str = arg;
555 1.1 blymn returns_t *ret;
556 1.1 blymn
557 1.9 christos if (verbose) {
558 1.9 christos fprintf(stderr, "function is >%s<, adding arg >%s< type %s\n",
559 1.4 christos command.function, str, args_enum_names[arg_type]);
560 1.9 christos }
561 1.1 blymn
562 1.1 blymn cur.arg_type = arg_type;
563 1.2 blymn switch (arg_type) {
564 1.2 blymn case arg_var:
565 1.1 blymn cur.var_index = find_var_index(arg);
566 1.1 blymn if (cur.var_index < 0)
567 1.4 christos err(1, "Invalid variable %s at line %zu of file %s",
568 1.4 christos str, line, cur_file);
569 1.1 blymn cur.arg_type = ret_string;
570 1.2 blymn break;
571 1.2 blymn
572 1.2 blymn case arg_byte:
573 1.2 blymn ret = arg;
574 1.2 blymn cur.arg_len = ret->return_len;
575 1.2 blymn cur.arg_string = malloc(cur.arg_len);
576 1.2 blymn if (cur.arg_string == NULL)
577 1.2 blymn err(1, "Could not malloc memory for arg bytes");
578 1.2 blymn memcpy(cur.arg_string, ret->return_value, cur.arg_len);
579 1.2 blymn break;
580 1.2 blymn
581 1.2 blymn case arg_null:
582 1.2 blymn cur.arg_len = 0;
583 1.2 blymn cur.arg_string = NULL;
584 1.2 blymn break;
585 1.2 blymn
586 1.2 blymn default:
587 1.2 blymn cur.arg_len = strlen(str);
588 1.2 blymn cur.arg_string = malloc(cur.arg_len + 1);
589 1.2 blymn if (cur.arg_string == NULL)
590 1.2 blymn err(1, "Could not malloc memory for arg string");
591 1.2 blymn strcpy(cur.arg_string, arg);
592 1.1 blymn }
593 1.1 blymn
594 1.9 christos temp = realloc(command.args, sizeof(*temp) * (command.nargs + 1));
595 1.1 blymn if (temp == NULL)
596 1.1 blymn err(1, "Failed to reallocate args");
597 1.1 blymn command.args = temp;
598 1.1 blymn memcpy(&command.args[command.nargs], &cur, sizeof(args_t));
599 1.1 blymn command.nargs++;
600 1.1 blymn }
601 1.1 blymn
602 1.1 blymn /*
603 1.1 blymn * Allocate and assign a new return.
604 1.1 blymn */
605 1.1 blymn static void
606 1.1 blymn assign_rets(returns_enum_t ret_type, void *ret)
607 1.1 blymn {
608 1.1 blymn returns_t *temp, cur;
609 1.1 blymn char *ret_str;
610 1.1 blymn returns_t *ret_ret;
611 1.1 blymn
612 1.1 blymn cur.return_type = ret_type;
613 1.1 blymn if (ret_type != ret_var) {
614 1.1 blymn if ((ret_type == ret_number) || (ret_type == ret_string)) {
615 1.1 blymn ret_str = ret;
616 1.1 blymn cur.return_len = strlen(ret_str) + 1;
617 1.1 blymn cur.return_value = malloc(cur.return_len + 1);
618 1.1 blymn if (cur.return_value == NULL)
619 1.1 blymn err(1,
620 1.1 blymn "Could not malloc memory for arg string");
621 1.1 blymn strcpy(cur.return_value, ret_str);
622 1.1 blymn } else if (ret_type == ret_byte) {
623 1.1 blymn ret_ret = ret;
624 1.1 blymn cur.return_len = ret_ret->return_len;
625 1.1 blymn cur.return_value = malloc(cur.return_len);
626 1.1 blymn if (cur.return_value == NULL)
627 1.1 blymn err(1,
628 1.1 blymn "Could not malloc memory for byte string");
629 1.1 blymn memcpy(cur.return_value, ret_ret->return_value,
630 1.1 blymn cur.return_len);
631 1.1 blymn } else if (ret_type == ret_ref) {
632 1.1 blymn if ((cur.return_index = find_var_index(ret)) < 0)
633 1.1 blymn err(1, "Undefined variable reference");
634 1.1 blymn }
635 1.1 blymn } else {
636 1.1 blymn cur.return_index = find_var_index(ret);
637 1.1 blymn if (cur.return_index < 0)
638 1.1 blymn cur.return_index = assign_var(ret);
639 1.1 blymn }
640 1.1 blymn
641 1.9 christos temp = realloc(command.returns, sizeof(*temp) * (command.nrets + 1));
642 1.1 blymn if (temp == NULL)
643 1.1 blymn err(1, "Failed to reallocate returns");
644 1.1 blymn command.returns = temp;
645 1.1 blymn memcpy(&command.returns[command.nrets], &cur, sizeof(returns_t));
646 1.1 blymn command.nrets++;
647 1.1 blymn }
648 1.1 blymn
649 1.1 blymn /*
650 1.4 christos * Find the given variable name in the var array and return the i
651 1.1 blymn * return -1 if var is not found.
652 1.1 blymn */
653 1.1 blymn static int
654 1.4 christos find_var_index(const char *var_name)
655 1.1 blymn {
656 1.4 christos int result;
657 1.4 christos size_t i;
658 1.1 blymn
659 1.1 blymn result = -1;
660 1.1 blymn
661 1.1 blymn for (i = 0; i < nvars; i++) {
662 1.1 blymn if (strcmp(var_name, vars[i].name) == 0) {
663 1.1 blymn result = i;
664 1.1 blymn break;
665 1.1 blymn }
666 1.1 blymn }
667 1.1 blymn
668 1.1 blymn return result;
669 1.1 blymn }
670 1.1 blymn
671 1.1 blymn /*
672 1.1 blymn * Check the given function name in the given table of names, return 1 if
673 1.1 blymn * there is a match.
674 1.1 blymn */
675 1.1 blymn static int check_function_table(char *function, const char *table[],
676 1.1 blymn int nfunctions)
677 1.1 blymn {
678 1.1 blymn int i;
679 1.1 blymn
680 1.1 blymn for (i = 0; i < nfunctions; i++) {
681 1.1 blymn if ((strlen(function) == strlen(table[i])) &&
682 1.1 blymn (strcmp(function, table[i]) == 0))
683 1.1 blymn return 1;
684 1.1 blymn }
685 1.1 blymn
686 1.1 blymn return 0;
687 1.1 blymn }
688 1.1 blymn
689 1.1 blymn /*
690 1.1 blymn * Compare the output from the slave against the given file and report
691 1.1 blymn * any differences.
692 1.1 blymn */
693 1.1 blymn static void
694 1.1 blymn compare_streams(char *filename, bool discard)
695 1.1 blymn {
696 1.5 christos char check_file[PATH_MAX], drain[100], ref, data;
697 1.1 blymn struct pollfd fds[2];
698 1.5 christos int nfd, check_fd;
699 1.5 christos ssize_t result;
700 1.9 christos size_t offs;
701 1.1 blymn
702 1.1 blymn /*
703 1.1 blymn * Don't prepend check path iff check file has an absolute
704 1.1 blymn * path.
705 1.1 blymn */
706 1.1 blymn if (filename[0] != '/') {
707 1.1 blymn if (strlcpy(check_file, check_path, sizeof(check_file))
708 1.1 blymn >= sizeof(check_file))
709 1.1 blymn err(2, "CHECK_PATH too long");
710 1.1 blymn
711 1.1 blymn if (strlcat(check_file, "/", sizeof(check_file))
712 1.1 blymn >= sizeof(check_file))
713 1.1 blymn err(2, "Could not append / to check file path");
714 1.1 blymn } else {
715 1.1 blymn check_file[0] = '\0';
716 1.1 blymn }
717 1.1 blymn
718 1.1 blymn if (strlcat(check_file, filename, sizeof(check_file))
719 1.1 blymn >= sizeof(check_file))
720 1.1 blymn err(2, "Path to check file path overflowed");
721 1.1 blymn
722 1.1 blymn if ((check_fd = open(check_file, O_RDONLY, 0)) < 0)
723 1.4 christos err(2, "failed to open file %s line %zu of file %s",
724 1.1 blymn check_file, line, cur_file);
725 1.1 blymn
726 1.1 blymn fds[0].fd = check_fd;
727 1.1 blymn fds[0].events = POLLIN;
728 1.1 blymn fds[1].fd = master;
729 1.1 blymn fds[1].events = POLLIN;
730 1.1 blymn
731 1.1 blymn nfd = 2;
732 1.1 blymn /*
733 1.1 blymn * if we have saved output then only check for data in the
734 1.1 blymn * reference file since the slave data may already be drained.
735 1.1 blymn */
736 1.1 blymn if (saved_output.count > 0)
737 1.1 blymn nfd = 1;
738 1.1 blymn
739 1.9 christos offs = 0;
740 1.9 christos while (poll(fds, nfd, 500) == nfd) {
741 1.9 christos if (fds[0].revents & POLLIN) {
742 1.9 christos if ((result = read(check_fd, &ref, 1)) < 1) {
743 1.9 christos if (result != 0) {
744 1.9 christos err(2,
745 1.9 christos "Bad read on file %s", check_file);
746 1.9 christos } else {
747 1.9 christos break;
748 1.9 christos }
749 1.1 blymn }
750 1.1 blymn }
751 1.1 blymn
752 1.1 blymn if (saved_output.count > 0) {
753 1.1 blymn data = saved_output.data[saved_output.readp];
754 1.1 blymn saved_output.count--;
755 1.1 blymn saved_output.readp++;
756 1.1 blymn /* run out of saved data, switch to file */
757 1.1 blymn if (saved_output.count == 0)
758 1.1 blymn nfd = 2;
759 1.1 blymn } else {
760 1.9 christos if (fds[0].revents & POLLIN) {
761 1.9 christos if (read(master, &data, 1) < 1)
762 1.9 christos err(2, "Bad read on slave pty");
763 1.9 christos } else
764 1.9 christos continue;
765 1.1 blymn }
766 1.1 blymn
767 1.1 blymn if (verbose) {
768 1.1 blymn fprintf(stderr, "Comparing reference byte 0x%x (%c)"
769 1.9 christos " against slave byte 0x%x (%c)\n",
770 1.9 christos ref, (ref >= ' ') ? ref : '-',
771 1.9 christos data, (data >= ' ' )? data : '-');
772 1.1 blymn }
773 1.1 blymn
774 1.1 blymn if (ref != data) {
775 1.9 christos errx(2, "%s, %zu: refresh data from slave does "
776 1.9 christos "not match expected from file %s offs %zu "
777 1.9 christos "[reference 0x%x (%c) != slave 0x%x (%c)]",
778 1.9 christos cur_file, line, check_file, offs,
779 1.9 christos ref, (ref >= ' ') ? ref : '-',
780 1.9 christos data, (data >= ' ') ? data : '-');
781 1.1 blymn }
782 1.1 blymn
783 1.9 christos offs++;
784 1.1 blymn }
785 1.1 blymn
786 1.1 blymn
787 1.1 blymn if (saved_output.count > 0)
788 1.6 christos excess(cur_file, line, __func__, " from slave",
789 1.6 christos &saved_output.data[saved_output.readp], saved_output.count);
790 1.1 blymn
791 1.1 blymn /* discard any excess saved output if required */
792 1.1 blymn if (discard) {
793 1.1 blymn saved_output.count = 0;
794 1.1 blymn saved_output.readp = 0;
795 1.1 blymn }
796 1.1 blymn
797 1.1 blymn if ((result = poll(&fds[0], 2, 0)) != 0) {
798 1.1 blymn if (result == -1)
799 1.1 blymn err(2, "poll of file descriptors failed");
800 1.1 blymn
801 1.5 christos if ((fds[1].revents & POLLIN) == POLLIN) {
802 1.1 blymn save_slave_output(true);
803 1.9 christos } else if ((fds[0].revents & POLLIN) == POLLIN) {
804 1.1 blymn /*
805 1.1 blymn * handle excess in file if it exists. Poll
806 1.1 blymn * says there is data until EOF is read.
807 1.1 blymn * Check next read is EOF, if it is not then
808 1.1 blymn * the file really has more data than the
809 1.1 blymn * slave produced so flag this as a warning.
810 1.1 blymn */
811 1.5 christos result = read(check_fd, drain, sizeof(drain));
812 1.1 blymn if (result == -1)
813 1.3 christos err(1, "read of data file failed");
814 1.1 blymn
815 1.1 blymn if (result > 0) {
816 1.6 christos excess(check_file, 0, __func__, "", drain,
817 1.6 christos result);
818 1.1 blymn }
819 1.1 blymn }
820 1.1 blymn }
821 1.1 blymn
822 1.1 blymn close(check_fd);
823 1.1 blymn }
824 1.1 blymn
825 1.1 blymn /*
826 1.1 blymn * Pass a function call and arguments to the slave and wait for the
827 1.1 blymn * results. The variable nresults determines how many returns we expect
828 1.1 blymn * back from the slave. These results will be validated against the
829 1.1 blymn * expected returns or assigned to variables.
830 1.1 blymn */
831 1.1 blymn static void
832 1.4 christos do_function_call(size_t nresults)
833 1.1 blymn {
834 1.1 blymn #define MAX_RESULTS 4
835 1.1 blymn char *p;
836 1.4 christos int do_input;
837 1.4 christos size_t i;
838 1.1 blymn struct pollfd fds[3];
839 1.1 blymn returns_t response[MAX_RESULTS], returns_count;
840 1.1 blymn
841 1.1 blymn assert(nresults <= MAX_RESULTS);
842 1.1 blymn
843 1.1 blymn do_input = check_function_table(command.function, input_functions,
844 1.5 christos ninput_functions);
845 1.1 blymn
846 1.1 blymn write_func_and_args();
847 1.1 blymn
848 1.1 blymn /*
849 1.1 blymn * We should get the number of returns back here, grab it before
850 1.1 blymn * doing input otherwise it will confuse the input poll
851 1.1 blymn */
852 1.1 blymn read_cmd_pipe(&returns_count);
853 1.5 christos if (returns_count.return_type != ret_count)
854 1.1 blymn err(2, "expected return type of ret_count but received %s",
855 1.1 blymn returns_enum_names[returns_count.return_type]);
856 1.1 blymn
857 1.9 christos if (verbose) {
858 1.4 christos fprintf(stderr, "Expect %zu results from slave, slave "
859 1.9 christos "reported %zu\n", nresults, returns_count.return_len);
860 1.9 christos }
861 1.1 blymn
862 1.1 blymn if ((no_input == false) && (do_input == 1)) {
863 1.9 christos if (verbose) {
864 1.1 blymn fprintf(stderr, "doing input with inputstr >%s<\n",
865 1.9 christos input_str);
866 1.9 christos }
867 1.1 blymn
868 1.5 christos if (input_str == NULL)
869 1.5 christos errx(2, "%s, %zu: Call to input function "
870 1.5 christos "but no input defined", cur_file, line);
871 1.1 blymn
872 1.1 blymn fds[0].fd = slvpipe[READ_PIPE];
873 1.1 blymn fds[0].events = POLLIN;
874 1.9 christos fds[1].fd = master;
875 1.9 christos fds[1].events = POLLOUT;
876 1.1 blymn p = input_str;
877 1.1 blymn save_slave_output(false);
878 1.1 blymn while(*p != '\0') {
879 1.9 christos nanosleep(&delay_spec, NULL);
880 1.9 christos if (poll(fds, 2, 0) < 0)
881 1.9 christos err(2, "poll failed");
882 1.9 christos if (fds[0].revents & POLLIN) {
883 1.9 christos warnx("%s, %zu: Slave function "
884 1.9 christos "returned before end of input string",
885 1.9 christos cur_file, line);
886 1.9 christos break;
887 1.9 christos }
888 1.9 christos if ((fds[1].revents & POLLOUT) == 0)
889 1.9 christos continue;
890 1.1 blymn if (verbose) {
891 1.1 blymn fprintf(stderr, "Writing char >%c< to slave\n",
892 1.9 christos *p);
893 1.1 blymn }
894 1.9 christos if (write(master, p, 1) != 1) {
895 1.9 christos warn("%s, %zu: Slave function write error",
896 1.5 christos cur_file, line);
897 1.1 blymn break;
898 1.1 blymn }
899 1.9 christos p++;
900 1.9 christos
901 1.1 blymn }
902 1.9 christos save_slave_output(false);
903 1.1 blymn
904 1.1 blymn if (verbose) {
905 1.1 blymn fprintf(stderr, "Input done.\n");
906 1.1 blymn }
907 1.1 blymn
908 1.1 blymn /* done with the input string, free the resources */
909 1.1 blymn free(input_str);
910 1.1 blymn input_str = NULL;
911 1.1 blymn }
912 1.1 blymn
913 1.9 christos if (verbose && 0) {
914 1.1 blymn fds[0].fd = slvpipe[READ_PIPE];
915 1.1 blymn fds[0].events = POLLIN;
916 1.1 blymn
917 1.1 blymn fds[1].fd = slvpipe[WRITE_PIPE];
918 1.1 blymn fds[1].events = POLLOUT;
919 1.1 blymn
920 1.1 blymn fds[2].fd = master;
921 1.1 blymn fds[2].events = POLLIN | POLLOUT;
922 1.1 blymn
923 1.1 blymn i = poll(&fds[0], 3, 1000);
924 1.4 christos fprintf(stderr, "Poll returned %zu\n", i);
925 1.1 blymn for (i = 0; i < 3; i++) {
926 1.4 christos fprintf(stderr, "revents for fd[%zu] = 0x%x\n",
927 1.1 blymn i, fds[i].revents);
928 1.1 blymn }
929 1.1 blymn }
930 1.1 blymn
931 1.1 blymn /* drain any trailing output */
932 1.1 blymn save_slave_output(false);
933 1.1 blymn
934 1.1 blymn for (i = 0; i < returns_count.return_len; i++) {
935 1.1 blymn read_cmd_pipe(&response[i]);
936 1.1 blymn }
937 1.1 blymn
938 1.1 blymn /*
939 1.1 blymn * Check for a slave error in the first return slot, if the
940 1.1 blymn * slave errored then we may not have the number of returns we
941 1.1 blymn * expect but in this case we should report the slave error
942 1.1 blymn * instead of a return count mismatch.
943 1.1 blymn */
944 1.1 blymn if ((returns_count.return_len > 0) &&
945 1.1 blymn (response[0].return_type == ret_slave_error))
946 1.4 christos err(2, "Slave returned error: %s",
947 1.4 christos (const char *)response[0].return_value);
948 1.1 blymn
949 1.1 blymn if (returns_count.return_len != nresults)
950 1.4 christos err(2, "Incorrect number of returns from slave, expected %zu "
951 1.4 christos "but received %zu", nresults, returns_count.return_len);
952 1.1 blymn
953 1.1 blymn if (verbose) {
954 1.1 blymn for (i = 0; i < nresults; i++) {
955 1.1 blymn if ((response[i].return_type != ret_byte) &&
956 1.1 blymn (response[i].return_type != ret_err) &&
957 1.1 blymn (response[i].return_type != ret_ok))
958 1.1 blymn fprintf(stderr,
959 1.1 blymn "received response >%s< "
960 1.4 christos "expected",
961 1.4 christos (const char *)response[i].return_value);
962 1.1 blymn else
963 1.1 blymn fprintf(stderr, "received");
964 1.1 blymn
965 1.1 blymn fprintf(stderr, " return_type %s\n",
966 1.5 christos returns_enum_names[command.returns[i].return_type]);
967 1.1 blymn }
968 1.1 blymn }
969 1.1 blymn
970 1.1 blymn for (i = 0; i < nresults; i++) {
971 1.1 blymn if (command.returns[i].return_type != ret_var) {
972 1.1 blymn validate(i, &response[i]);
973 1.1 blymn } else {
974 1.1 blymn vars[command.returns[i].return_index].len =
975 1.1 blymn response[i].return_len;
976 1.1 blymn vars[command.returns[i].return_index].value =
977 1.1 blymn response[i].return_value;
978 1.1 blymn vars[command.returns[i].return_index].type =
979 1.1 blymn response[i].return_type;
980 1.1 blymn }
981 1.1 blymn }
982 1.5 christos #if 0
983 1.1 blymn if (saved_output.count > 0)
984 1.6 christos excess(cur_file, line, __func__, " from slave",
985 1.6 christos &saved_output.data[saved_output.readp], saved_output.count);
986 1.5 christos #endif
987 1.1 blymn
988 1.1 blymn init_parse_variables(0);
989 1.1 blymn }
990 1.1 blymn
991 1.1 blymn /*
992 1.1 blymn * Write the function and command arguments to the command pipe.
993 1.1 blymn */
994 1.1 blymn static void
995 1.4 christos write_func_and_args(void)
996 1.1 blymn {
997 1.1 blymn int i;
998 1.1 blymn
999 1.9 christos if (verbose) {
1000 1.1 blymn fprintf(stderr, "calling function >%s<\n", command.function);
1001 1.9 christos }
1002 1.1 blymn
1003 1.1 blymn write_cmd_pipe(command.function);
1004 1.1 blymn for (i = 0; i < command.nargs; i++) {
1005 1.1 blymn if (command.args[i].arg_type == arg_var)
1006 1.1 blymn write_cmd_pipe_args(command.args[i].arg_type,
1007 1.1 blymn &vars[command.args[i].var_index]);
1008 1.1 blymn else
1009 1.1 blymn write_cmd_pipe_args(command.args[i].arg_type,
1010 1.1 blymn &command.args[i]);
1011 1.1 blymn }
1012 1.1 blymn
1013 1.1 blymn write_cmd_pipe(NULL); /* signal end of arguments */
1014 1.1 blymn }
1015 1.1 blymn
1016 1.1 blymn /*
1017 1.1 blymn * Initialise the command structure - if initial is non-zero then just set
1018 1.1 blymn * everything to sane values otherwise free any memory that was allocated
1019 1.1 blymn * when building the structure.
1020 1.1 blymn */
1021 1.1 blymn void
1022 1.1 blymn init_parse_variables(int initial)
1023 1.1 blymn {
1024 1.1 blymn int i, result;
1025 1.1 blymn struct pollfd slave_pty;
1026 1.1 blymn
1027 1.1 blymn if (initial == 0) {
1028 1.1 blymn free(command.function);
1029 1.1 blymn for (i = 0; i < command.nrets; i++) {
1030 1.1 blymn if (command.returns[i].return_type == ret_number)
1031 1.1 blymn free(command.returns[i].return_value);
1032 1.1 blymn }
1033 1.1 blymn free(command.returns);
1034 1.1 blymn
1035 1.1 blymn for (i = 0; i < command.nargs; i++) {
1036 1.1 blymn if (command.args[i].arg_type != arg_var)
1037 1.1 blymn free(command.args[i].arg_string);
1038 1.1 blymn }
1039 1.1 blymn free(command.args);
1040 1.1 blymn } else {
1041 1.1 blymn line = 0;
1042 1.1 blymn input_delay = 0;
1043 1.1 blymn vars = NULL;
1044 1.1 blymn nvars = 0;
1045 1.1 blymn input_str = NULL;
1046 1.1 blymn saved_output.allocated = 0;
1047 1.1 blymn saved_output.count = 0;
1048 1.1 blymn saved_output.readp = 0;
1049 1.1 blymn saved_output.data = NULL;
1050 1.1 blymn }
1051 1.1 blymn
1052 1.1 blymn no_input = false;
1053 1.1 blymn command.function = NULL;
1054 1.1 blymn command.nargs = 0;
1055 1.1 blymn command.args = NULL;
1056 1.1 blymn command.nrets = 0;
1057 1.1 blymn command.returns = NULL;
1058 1.1 blymn
1059 1.1 blymn /*
1060 1.1 blymn * Check the slave pty for stray output from the slave, at this
1061 1.1 blymn * point we should not see any data as it should have been
1062 1.1 blymn * consumed by the test functions. If we see data then we have
1063 1.1 blymn * either a bug or are not handling an output generating function
1064 1.1 blymn * correctly.
1065 1.1 blymn */
1066 1.1 blymn slave_pty.fd = master;
1067 1.1 blymn slave_pty.events = POLLIN;
1068 1.1 blymn result = poll(&slave_pty, 1, 0);
1069 1.1 blymn
1070 1.1 blymn if (result < 0)
1071 1.1 blymn err(2, "Poll of slave pty failed");
1072 1.1 blymn else if (result > 0)
1073 1.5 christos warnx("%s, %zu: Unexpected data from slave", cur_file, line);
1074 1.1 blymn }
1075 1.1 blymn
1076 1.1 blymn /*
1077 1.1 blymn * Validate the response against the expected return. The variable
1078 1.4 christos * i is the i into the rets array in command.
1079 1.1 blymn */
1080 1.1 blymn static void
1081 1.4 christos validate(int i, void *data)
1082 1.1 blymn {
1083 1.1 blymn char *response;
1084 1.1 blymn returns_t *byte_response;
1085 1.1 blymn
1086 1.1 blymn byte_response = data;
1087 1.4 christos if ((command.returns[i].return_type != ret_byte) &&
1088 1.4 christos (command.returns[i].return_type != ret_err) &&
1089 1.4 christos (command.returns[i].return_type != ret_ok))
1090 1.1 blymn response = byte_response->return_value;
1091 1.1 blymn
1092 1.4 christos switch (command.returns[i].return_type) {
1093 1.1 blymn case ret_err:
1094 1.1 blymn validate_type(ret_err, byte_response, 0);
1095 1.1 blymn break;
1096 1.1 blymn
1097 1.1 blymn case ret_ok:
1098 1.1 blymn validate_type(ret_ok, byte_response, 0);
1099 1.1 blymn break;
1100 1.1 blymn
1101 1.1 blymn case ret_null:
1102 1.1 blymn validate_return("NULL", response, 0);
1103 1.1 blymn break;
1104 1.1 blymn
1105 1.1 blymn case ret_nonnull:
1106 1.1 blymn validate_return("NULL", response, 1);
1107 1.1 blymn break;
1108 1.1 blymn
1109 1.1 blymn case ret_string:
1110 1.1 blymn case ret_number:
1111 1.4 christos validate_return(command.returns[i].return_value,
1112 1.1 blymn response, 0);
1113 1.1 blymn break;
1114 1.1 blymn
1115 1.1 blymn case ret_ref:
1116 1.4 christos validate_reference(i, response);
1117 1.1 blymn break;
1118 1.1 blymn
1119 1.1 blymn case ret_byte:
1120 1.4 christos validate_byte(&command.returns[i], byte_response, 0);
1121 1.1 blymn break;
1122 1.1 blymn
1123 1.1 blymn default:
1124 1.4 christos err(1, "Malformed statement at line %zu of file %s",
1125 1.1 blymn line, cur_file);
1126 1.1 blymn break;
1127 1.1 blymn }
1128 1.1 blymn }
1129 1.1 blymn
1130 1.1 blymn /*
1131 1.1 blymn * Validate the return against the contents of a variable.
1132 1.1 blymn */
1133 1.1 blymn static void
1134 1.4 christos validate_reference(int i, void *data)
1135 1.1 blymn {
1136 1.1 blymn char *response;
1137 1.1 blymn returns_t *byte_response;
1138 1.1 blymn var_t *varp;
1139 1.1 blymn
1140 1.4 christos varp = &vars[command.returns[i].return_index];
1141 1.1 blymn
1142 1.1 blymn byte_response = data;
1143 1.4 christos if (command.returns[i].return_type != ret_byte)
1144 1.1 blymn response = data;
1145 1.1 blymn
1146 1.9 christos if (1 || verbose) {
1147 1.1 blymn fprintf(stderr,
1148 1.9 christos "validate_reference: return type of %s, value %s \n",
1149 1.9 christos returns_enum_names[varp->type],
1150 1.9 christos (const char *)varp->value);
1151 1.9 christos }
1152 1.1 blymn
1153 1.1 blymn switch (varp->type) {
1154 1.1 blymn case ret_string:
1155 1.1 blymn case ret_number:
1156 1.1 blymn validate_return(varp->value, response, 0);
1157 1.1 blymn break;
1158 1.1 blymn
1159 1.1 blymn case ret_byte:
1160 1.1 blymn validate_byte(varp->value, byte_response, 0);
1161 1.1 blymn break;
1162 1.1 blymn
1163 1.1 blymn default:
1164 1.1 blymn err(1,
1165 1.4 christos "Invalid return type for reference at line %zu of file %s",
1166 1.1 blymn line, cur_file);
1167 1.1 blymn break;
1168 1.1 blymn }
1169 1.1 blymn }
1170 1.1 blymn
1171 1.1 blymn /*
1172 1.1 blymn * Validate the return type against the expected type, throw an error
1173 1.1 blymn * if they don't match.
1174 1.1 blymn */
1175 1.1 blymn static void
1176 1.1 blymn validate_type(returns_enum_t expected, returns_t *value, int check)
1177 1.1 blymn {
1178 1.1 blymn if (((check == 0) && (expected != value->return_type)) ||
1179 1.1 blymn ((check == 1) && (expected == value->return_type)))
1180 1.4 christos err(1, "Validate expected type %s %s %s line %zu of file %s",
1181 1.1 blymn returns_enum_names[expected],
1182 1.1 blymn (check == 0)? "matching" : "not matching",
1183 1.1 blymn returns_enum_names[value->return_type], line, cur_file);
1184 1.1 blymn
1185 1.9 christos if (verbose) {
1186 1.4 christos fprintf(stderr, "Validate expected type %s %s %s line %zu"
1187 1.9 christos " of file %s\n",
1188 1.9 christos returns_enum_names[expected],
1189 1.9 christos (check == 0)? "matching" : "not matching",
1190 1.9 christos returns_enum_names[value->return_type], line, cur_file);
1191 1.9 christos }
1192 1.1 blymn }
1193 1.1 blymn
1194 1.1 blymn /*
1195 1.1 blymn * Validate the return value against the expected value, throw an error
1196 1.1 blymn * if they don't match.
1197 1.1 blymn */
1198 1.1 blymn static void
1199 1.4 christos validate_return(const char *expected, const char *value, int check)
1200 1.1 blymn {
1201 1.1 blymn if (((check == 0) && strcmp(expected, value) != 0) ||
1202 1.1 blymn ((check == 1) && strcmp(expected, value) == 0))
1203 1.9 christos errx(1, "Validate expected %s %s %s line %zu of file %s",
1204 1.1 blymn expected,
1205 1.1 blymn (check == 0)? "matching" : "not matching", value,
1206 1.1 blymn line, cur_file);
1207 1.9 christos if (verbose) {
1208 1.1 blymn fprintf(stderr, "Validated expected value %s %s %s "
1209 1.9 christos "at line %zu of file %s\n", expected,
1210 1.9 christos (check == 0)? "matches" : "does not match",
1211 1.9 christos value, line, cur_file);
1212 1.9 christos }
1213 1.1 blymn }
1214 1.1 blymn
1215 1.1 blymn /*
1216 1.1 blymn * Validate the return value against the expected value, throw an error
1217 1.1 blymn * if they don't match expectations.
1218 1.1 blymn */
1219 1.1 blymn static void
1220 1.1 blymn validate_byte(returns_t *expected, returns_t *value, int check)
1221 1.1 blymn {
1222 1.1 blymn /*
1223 1.1 blymn * No chance of a match if lengths differ...
1224 1.1 blymn */
1225 1.1 blymn if ((check == 0) && (expected->return_len != value->return_len))
1226 1.9 christos errx(1, "Byte validation failed, length mismatch");
1227 1.1 blymn
1228 1.1 blymn /*
1229 1.1 blymn * If check is 0 then we want to throw an error IFF the byte streams
1230 1.1 blymn * do not match, if check is 1 then throw an error if the byte
1231 1.1 blymn * streams match.
1232 1.1 blymn */
1233 1.1 blymn if (((check == 0) && memcmp(expected->return_value, value->return_value,
1234 1.1 blymn value->return_len) != 0) ||
1235 1.1 blymn ((check == 1) && (expected->return_len == value->return_len) &&
1236 1.1 blymn memcmp(expected->return_value, value->return_value,
1237 1.1 blymn value->return_len) == 0))
1238 1.9 christos errx(1, "Validate expected %s byte stream at line %zu"
1239 1.1 blymn "of file %s",
1240 1.1 blymn (check == 0)? "matching" : "not matching", line, cur_file);
1241 1.9 christos if (verbose) {
1242 1.1 blymn fprintf(stderr, "Validated expected %s byte stream "
1243 1.9 christos "at line %zu of file %s\n",
1244 1.9 christos (check == 0)? "matching" : "not matching",
1245 1.9 christos line, cur_file);
1246 1.9 christos }
1247 1.1 blymn }
1248 1.1 blymn
1249 1.1 blymn /*
1250 1.4 christos * Validate the variable at i against the expected value, throw an
1251 1.1 blymn * error if they don't match, if check is non-zero then the match is
1252 1.1 blymn * negated.
1253 1.1 blymn */
1254 1.1 blymn static void
1255 1.4 christos validate_variable(int ret, returns_enum_t type, const void *value, int i,
1256 1.4 christos int check)
1257 1.1 blymn {
1258 1.1 blymn returns_t *retval;
1259 1.1 blymn var_t *varptr;
1260 1.1 blymn
1261 1.7 joerg retval = &command.returns[ret];
1262 1.1 blymn varptr = &vars[command.returns[ret].return_index];
1263 1.1 blymn
1264 1.1 blymn if (varptr->value == NULL)
1265 1.4 christos err(1, "Variable %s has no value assigned to it", varptr->name);
1266 1.1 blymn
1267 1.1 blymn
1268 1.1 blymn if (varptr->type != type)
1269 1.4 christos err(1, "Variable %s is not the expected type", varptr->name);
1270 1.1 blymn
1271 1.1 blymn if (type != ret_byte) {
1272 1.1 blymn if ((((check == 0) && strcmp(value, varptr->value) != 0))
1273 1.1 blymn || ((check == 1) && strcmp(value, varptr->value) == 0))
1274 1.1 blymn err(1, "Variable %s contains %s instead of %s"
1275 1.4 christos " value %s at line %zu of file %s",
1276 1.4 christos varptr->name, (const char *)varptr->value,
1277 1.4 christos (check == 0)? "expected" : "not matching",
1278 1.4 christos (const char *)value,
1279 1.1 blymn line, cur_file);
1280 1.9 christos if (verbose) {
1281 1.1 blymn fprintf(stderr, "Variable %s contains %s value "
1282 1.9 christos "%s at line %zu of file %s\n",
1283 1.9 christos varptr->name,
1284 1.9 christos (check == 0)? "expected" : "not matching",
1285 1.9 christos (const char *)varptr->value, line, cur_file);
1286 1.9 christos }
1287 1.1 blymn } else {
1288 1.1 blymn if ((check == 0) && (retval->return_len != varptr->len))
1289 1.1 blymn err(1, "Byte validation failed, length mismatch");
1290 1.1 blymn
1291 1.1 blymn /*
1292 1.1 blymn * If check is 0 then we want to throw an error IFF
1293 1.1 blymn * the byte streams do not match, if check is 1 then
1294 1.1 blymn * throw an error if the byte streams match.
1295 1.1 blymn */
1296 1.1 blymn if (((check == 0) && memcmp(retval->return_value, varptr->value,
1297 1.1 blymn varptr->len) != 0) ||
1298 1.1 blymn ((check == 1) && (retval->return_len == varptr->len) &&
1299 1.1 blymn memcmp(retval->return_value, varptr->value,
1300 1.1 blymn varptr->len) == 0))
1301 1.4 christos err(1, "Validate expected %s byte stream at line %zu"
1302 1.1 blymn " of file %s",
1303 1.1 blymn (check == 0)? "matching" : "not matching",
1304 1.1 blymn line, cur_file);
1305 1.9 christos if (verbose) {
1306 1.1 blymn fprintf(stderr, "Validated expected %s byte stream "
1307 1.9 christos "at line %zu of file %s\n",
1308 1.9 christos (check == 0)? "matching" : "not matching",
1309 1.9 christos line, cur_file);
1310 1.9 christos }
1311 1.1 blymn }
1312 1.1 blymn }
1313 1.1 blymn
1314 1.1 blymn /*
1315 1.1 blymn * Write a string to the command pipe - we feed the number of bytes coming
1316 1.1 blymn * down first to allow storage allocation and then follow up with the data.
1317 1.1 blymn * If cmd is NULL then feed a -1 down the pipe to say the end of the args.
1318 1.1 blymn */
1319 1.1 blymn static void
1320 1.1 blymn write_cmd_pipe(char *cmd)
1321 1.1 blymn {
1322 1.1 blymn args_t arg;
1323 1.1 blymn size_t len;
1324 1.1 blymn
1325 1.1 blymn if (cmd == NULL)
1326 1.1 blymn len = 0;
1327 1.1 blymn else
1328 1.1 blymn len = strlen(cmd);
1329 1.1 blymn
1330 1.1 blymn arg.arg_type = arg_static;
1331 1.1 blymn arg.arg_len = len;
1332 1.1 blymn arg.arg_string = cmd;
1333 1.1 blymn write_cmd_pipe_args(arg.arg_type, &arg);
1334 1.1 blymn
1335 1.1 blymn }
1336 1.1 blymn
1337 1.1 blymn static void
1338 1.1 blymn write_cmd_pipe_args(args_state_t type, void *data)
1339 1.1 blymn {
1340 1.1 blymn var_t *var_data;
1341 1.1 blymn args_t *arg_data;
1342 1.1 blymn int len, send_type;
1343 1.1 blymn void *cmd;
1344 1.1 blymn
1345 1.1 blymn arg_data = data;
1346 1.2 blymn switch (type) {
1347 1.2 blymn case arg_var:
1348 1.2 blymn var_data = data;
1349 1.2 blymn len = var_data->len;
1350 1.2 blymn cmd = var_data->value;
1351 1.8 christos if (type == arg_byte)
1352 1.2 blymn send_type = ret_byte;
1353 1.2 blymn else
1354 1.2 blymn send_type = ret_string;
1355 1.2 blymn break;
1356 1.2 blymn
1357 1.2 blymn case arg_null:
1358 1.2 blymn send_type = ret_null;
1359 1.2 blymn len = 0;
1360 1.2 blymn break;
1361 1.2 blymn
1362 1.2 blymn default:
1363 1.1 blymn if ((arg_data->arg_len == 0) && (arg_data->arg_string == NULL))
1364 1.1 blymn len = -1;
1365 1.1 blymn else
1366 1.1 blymn len = arg_data->arg_len;
1367 1.1 blymn cmd = arg_data->arg_string;
1368 1.1 blymn if (type == arg_byte)
1369 1.1 blymn send_type = ret_byte;
1370 1.1 blymn else
1371 1.1 blymn send_type = ret_string;
1372 1.1 blymn }
1373 1.1 blymn
1374 1.9 christos if (verbose) {
1375 1.1 blymn fprintf(stderr, "Writing type %s to command pipe\n",
1376 1.9 christos returns_enum_names[send_type]);
1377 1.9 christos }
1378 1.1 blymn
1379 1.4 christos if (write(cmdpipe[WRITE_PIPE], &send_type, sizeof(int)) < 0)
1380 1.4 christos err(1, "command pipe write for type failed");
1381 1.1 blymn
1382 1.9 christos if (verbose) {
1383 1.1 blymn fprintf(stderr, "Writing length %d to command pipe\n", len);
1384 1.9 christos }
1385 1.1 blymn
1386 1.4 christos if (write(cmdpipe[WRITE_PIPE], &len, sizeof(int)) < 0)
1387 1.4 christos err(1, "command pipe write for length failed");
1388 1.1 blymn
1389 1.1 blymn if (len > 0) {
1390 1.9 christos if (verbose) {
1391 1.1 blymn fprintf(stderr, "Writing data >%s< to command pipe\n",
1392 1.9 christos (const char *)cmd);
1393 1.9 christos }
1394 1.4 christos if (write(cmdpipe[WRITE_PIPE], cmd, len) < 0)
1395 1.4 christos err(1, "command pipe write of data failed");
1396 1.1 blymn }
1397 1.1 blymn }
1398 1.1 blymn
1399 1.1 blymn /*
1400 1.1 blymn * Read a response from the command pipe, first we will receive the
1401 1.1 blymn * length of the response then the actual data.
1402 1.1 blymn */
1403 1.1 blymn static void
1404 1.1 blymn read_cmd_pipe(returns_t *response)
1405 1.1 blymn {
1406 1.1 blymn int len, type;
1407 1.4 christos struct pollfd rfd[2];
1408 1.1 blymn char *str;
1409 1.1 blymn
1410 1.1 blymn /*
1411 1.1 blymn * Check if there is data to read - just in case slave has died, we
1412 1.1 blymn * don't want to block on the read and just hang. We also check
1413 1.1 blymn * output from the slave because the slave may be blocked waiting
1414 1.1 blymn * for a flush on its stdout.
1415 1.1 blymn */
1416 1.4 christos rfd[0].fd = slvpipe[READ_PIPE];
1417 1.4 christos rfd[0].events = POLLIN;
1418 1.4 christos rfd[1].fd = master;
1419 1.4 christos rfd[1].events = POLLIN;
1420 1.1 blymn
1421 1.1 blymn do {
1422 1.5 christos if (poll(rfd, 2, 4000) == 0)
1423 1.5 christos errx(2, "%s, %zu: Command pipe read timeout",
1424 1.5 christos cur_file, line);
1425 1.1 blymn
1426 1.4 christos if ((rfd[1].revents & POLLIN) == POLLIN) {
1427 1.9 christos if (verbose) {
1428 1.1 blymn fprintf(stderr,
1429 1.9 christos "draining output from slave\n");
1430 1.9 christos }
1431 1.1 blymn save_slave_output(false);
1432 1.1 blymn }
1433 1.1 blymn }
1434 1.4 christos while((rfd[1].revents & POLLIN) == POLLIN);
1435 1.1 blymn
1436 1.4 christos if (read(slvpipe[READ_PIPE], &type, sizeof(int)) < 0)
1437 1.4 christos err(1, "command pipe read for type failed");
1438 1.1 blymn response->return_type = type;
1439 1.1 blymn
1440 1.1 blymn if ((type != ret_ok) && (type != ret_err) && (type != ret_count)) {
1441 1.4 christos if (read(slvpipe[READ_PIPE], &len, sizeof(int)) < 0)
1442 1.4 christos err(1, "command pipe read for length failed");
1443 1.1 blymn response->return_len = len;
1444 1.1 blymn
1445 1.9 christos if (verbose) {
1446 1.1 blymn fprintf(stderr,
1447 1.9 christos "Reading %d bytes from command pipe\n", len);
1448 1.9 christos }
1449 1.1 blymn
1450 1.1 blymn if ((response->return_value = malloc(len + 1)) == NULL)
1451 1.1 blymn err(1, "Failed to alloc memory for cmd pipe read");
1452 1.1 blymn
1453 1.4 christos if (read(slvpipe[READ_PIPE], response->return_value, len) < 0)
1454 1.4 christos err(1, "command pipe read of data failed");
1455 1.1 blymn
1456 1.1 blymn if (response->return_type != ret_byte) {
1457 1.1 blymn str = response->return_value;
1458 1.1 blymn str[len] = '\0';
1459 1.1 blymn
1460 1.9 christos if (verbose) {
1461 1.1 blymn fprintf(stderr, "Read data >%s< from pipe\n",
1462 1.9 christos (const char *)response->return_value);
1463 1.9 christos }
1464 1.1 blymn }
1465 1.1 blymn } else {
1466 1.1 blymn response->return_value = NULL;
1467 1.1 blymn if (type == ret_count) {
1468 1.4 christos if (read(slvpipe[READ_PIPE], &len, sizeof(int)) < 0)
1469 1.4 christos err(1, "command pipe read for number of "
1470 1.1 blymn "returns failed");
1471 1.1 blymn response->return_len = len;
1472 1.1 blymn }
1473 1.1 blymn
1474 1.9 christos if (verbose) {
1475 1.1 blymn fprintf(stderr, "Read type %s from pipe\n",
1476 1.9 christos returns_enum_names[type]);
1477 1.9 christos }
1478 1.1 blymn }
1479 1.1 blymn }
1480 1.1 blymn
1481 1.1 blymn /*
1482 1.1 blymn * Check for writes from the slave on the pty, save the output into a
1483 1.1 blymn * buffer for later checking if discard is false.
1484 1.1 blymn */
1485 1.1 blymn #define MAX_DRAIN 256
1486 1.1 blymn
1487 1.1 blymn static void
1488 1.1 blymn save_slave_output(bool discard)
1489 1.1 blymn {
1490 1.1 blymn char *new_data, drain[MAX_DRAIN];
1491 1.1 blymn size_t to_allocate;
1492 1.4 christos ssize_t result;
1493 1.4 christos size_t i;
1494 1.1 blymn
1495 1.1 blymn result = 0;
1496 1.9 christos for (;;) {
1497 1.1 blymn if (result == -1)
1498 1.1 blymn err(2, "poll of slave pty failed");
1499 1.1 blymn result = MAX_DRAIN;
1500 1.9 christos if ((result = read(master, drain, result)) < 0) {
1501 1.1 blymn if (errno == EAGAIN)
1502 1.1 blymn break;
1503 1.1 blymn else
1504 1.1 blymn err(2, "draining slave pty failed");
1505 1.1 blymn }
1506 1.9 christos if (result == 0)
1507 1.9 christos abort();
1508 1.1 blymn
1509 1.5 christos if (!discard) {
1510 1.4 christos if ((size_t)result >
1511 1.1 blymn (saved_output.allocated - saved_output.count)) {
1512 1.1 blymn to_allocate = 1024 * ((result / 1024) + 1);
1513 1.1 blymn
1514 1.1 blymn if ((new_data = realloc(saved_output.data,
1515 1.1 blymn saved_output.allocated + to_allocate))
1516 1.1 blymn == NULL)
1517 1.1 blymn err(2, "Realloc of saved_output failed");
1518 1.1 blymn saved_output.data = new_data;
1519 1.1 blymn saved_output.allocated += to_allocate;
1520 1.1 blymn }
1521 1.1 blymn
1522 1.1 blymn if (verbose) {
1523 1.5 christos fprintf(stderr, "count = %zu, "
1524 1.5 christos "allocated = %zu\n", saved_output.count,
1525 1.5 christos saved_output.allocated);
1526 1.4 christos for (i = 0; i < (size_t)result; i++) {
1527 1.1 blymn fprintf(stderr, "Saving slave output "
1528 1.9 christos "at %zu: 0x%x (%c)\n",
1529 1.9 christos saved_output.count + i, drain[i],
1530 1.5 christos (drain[i] >= ' ')? drain[i] : '-');
1531 1.1 blymn }
1532 1.1 blymn }
1533 1.1 blymn
1534 1.1 blymn memcpy(&saved_output.data[saved_output.count], drain,
1535 1.1 blymn result);
1536 1.1 blymn saved_output.count += result;
1537 1.1 blymn
1538 1.1 blymn if (verbose) {
1539 1.5 christos fprintf(stderr, "count = %zu, "
1540 1.5 christos "allocated = %zu\n", saved_output.count,
1541 1.5 christos saved_output.allocated);
1542 1.1 blymn }
1543 1.1 blymn } else {
1544 1.1 blymn if (verbose) {
1545 1.9 christos for (i = 0; i < (size_t)result; i++) {
1546 1.1 blymn fprintf(stderr, "Discarding slave "
1547 1.5 christos "output 0x%x (%c)\n",
1548 1.5 christos drain[i],
1549 1.5 christos (drain[i] >= ' ')? drain[i] : '-');
1550 1.1 blymn }
1551 1.1 blymn }
1552 1.1 blymn }
1553 1.1 blymn }
1554 1.1 blymn }
1555 1.1 blymn
1556 1.1 blymn static void
1557 1.1 blymn yyerror(const char *msg)
1558 1.1 blymn {
1559 1.1 blymn warnx("%s in line %zu of file %s", msg, line, cur_file);
1560 1.1 blymn }
1561