1 %{ 2 /* $NetBSD: testlang_conf.l,v 1.26 2021/11/15 21:45:46 blymn Exp $ */ 3 4 /*- 5 * Copyright 2009 Brett Lymn <blymn@NetBSD.org> 6 * Copyright 2021 Roland Illig <rillig@NetBSD.org> 7 * 8 * All rights reserved. 9 * 10 * This code has been donated to The NetBSD Foundation by the Author. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 2. The name of the author may not be used to endorse or promote products 18 * derived from this software without specific prior written permission 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 21 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 22 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 23 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 24 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 25 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 29 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include <curses.h> 33 #include <ctype.h> 34 #include <stdio.h> 35 #include <stdlib.h> 36 #include <string.h> 37 #include <sys/param.h> 38 #include <err.h> 39 #include "returns.h" 40 #include "testlang_parse.h" 41 42 #define MAX_INCLUDES 32 /* limit for the number of nested includes */ 43 44 int yylex(void); 45 46 extern size_t line; 47 extern char *cur_file; /* from director.c */ 48 49 static int include_stack[MAX_INCLUDES]; 50 static char *include_files[MAX_INCLUDES]; 51 static int include_ptr = 0; 52 53 static char * 54 dequote(const char *s, size_t *len) 55 { 56 const unsigned char *p; 57 char *buf, *q; 58 59 *len = 0; 60 p = (const unsigned char *)s; 61 while (*p) { 62 if (*p == '\\' && p[1]) { 63 if (isdigit(p[1]) && isdigit(p[2]) && isdigit(p[3])) 64 p += 3; 65 else 66 ++p; 67 } 68 ++(*len); 69 ++p; 70 } 71 72 buf = malloc(*len + 1); 73 if (buf == NULL) 74 return NULL; 75 76 p = (const unsigned char *)s; 77 q = buf; 78 while (*p) { 79 if (*p == '\\' && p[1]) { 80 ++p; 81 82 if (isdigit(p[0])) { 83 if (isdigit(p[1]) && isdigit(p[2])) { 84 *q++ = (p[0] - '0') * 64 + 85 (p[1] - '0') * 8 + 86 (p[2] - '0'); 87 p += 3; 88 } else { 89 *q++ = *p++; 90 } 91 continue; 92 } 93 94 switch (*p) { 95 case 'b': 96 /* backspace */ 97 *q++ = '\b'; 98 p++; 99 break; 100 101 case 'e': 102 /* escape */ 103 *q++ = '\e'; 104 p++; 105 break; 106 107 case 'n': 108 /* newline */ 109 *q++ = '\n'; 110 p++; 111 break; 112 113 case 'r': 114 /* carriage return */ 115 *q++ = '\r'; 116 p++; 117 break; 118 119 case 't': 120 /* tab */ 121 *q++ = '\t'; 122 p++; 123 break; 124 125 case '\\': 126 /* backslash */ 127 *q++ = '\\'; 128 p++; 129 break; 130 131 default: 132 if (isalpha(*p)) 133 errx(2, 134 "%s:%zu: Invalid escape sequence " 135 "'\\%c' in string literal", 136 cur_file, line, *p); 137 *q++ = *p++; 138 } 139 } else 140 *q++ = *p++; 141 } 142 *q++ = '\0'; 143 144 return buf; 145 } 146 %} 147 148 HEX 0[xX][0-9a-zA-Z]+ 149 STRING [0-9a-z!#-&(-^ \t%._\\]+ 150 numeric [-0-9]+ 151 PCHAR (\\.|[!-~]) 152 ASSIGN assign 153 CALL2 call2 154 CALL3 call3 155 CALL4 call4 156 CALL call 157 CHECK check 158 DELAY delay 159 INPUT input 160 NOINPUT noinput 161 OK_RET OK 162 ERR_RET ERR 163 COMPARE compare 164 COMPAREND comparend 165 FILENAME [A-Za-z0-9.][A-Za-z0-9./_-]+ 166 VARNAME [A-Za-z][A-Za-z0-9_-]+ 167 NULL_RET NULL 168 NON_NULL NON_NULL 169 CCHAR cchar 170 WCHAR wchar 171 BYTE BYTE 172 OR \| 173 LPAREN \( 174 RPAREN \) 175 LBRACK \[ 176 RBRACK \] 177 MULTIPLIER \* 178 COMMA , 179 180 %x incl 181 %option noinput nounput 182 183 %% 184 185 include BEGIN(incl); 186 187 <incl>[ \t]* /* eat the whitespace */ 188 <incl>[^ \t\n]+ { /* got the include file name */ 189 char *inc_file; 190 191 if (include_ptr > MAX_INCLUDES) { 192 errx(2, 193 "%s:%zu: Maximum number of nested includes " 194 "exceeded", cur_file, line); 195 } 196 197 const char *dir_begin; 198 int dir_len; 199 if (yytext[0] == '/') { 200 dir_begin = ""; 201 dir_len = 0; 202 } else { 203 dir_begin = cur_file; 204 const char *dir_end = strrchr(cur_file, '/'); 205 if (dir_end != NULL) { 206 dir_len = (int)(dir_end + 1 - dir_begin); 207 } else { 208 dir_begin = "."; 209 dir_len = 1; 210 } 211 } 212 213 if (asprintf(&inc_file, "%.*s%s", 214 dir_len, dir_begin, yytext) == -1) 215 err(2, "Cannot construct include path"); 216 217 yyin = fopen(inc_file, "r"); 218 219 if (!yyin) 220 err(1, "Error opening %s", inc_file); 221 222 yypush_buffer_state(yy_create_buffer(yyin, YY_BUF_SIZE)); 223 224 include_stack[include_ptr] = line; 225 include_files[include_ptr++] = cur_file; 226 cur_file = inc_file; 227 line = 1; 228 BEGIN(INITIAL); 229 } 230 231 <<EOF>> { 232 yypop_buffer_state(); 233 234 if (!YY_CURRENT_BUFFER) 235 yyterminate(); 236 237 if (--include_ptr < 0) 238 errx(2, "Include stack underflow"); 239 240 free(cur_file); 241 cur_file = include_files[include_ptr]; 242 line = include_stack[include_ptr]; 243 } 244 245 {ASSIGN} return ASSIGN; 246 {CALL2} return CALL2; 247 {CALL3} return CALL3; 248 {CALL4} return CALL4; 249 {CALL} return CALL; 250 {CHECK} return CHECK; 251 {DELAY} return DELAY; 252 {INPUT} return INPUT; 253 {NOINPUT} return NOINPUT; 254 {COMPARE} return COMPARE; 255 {COMPAREND} return COMPAREND; 256 {NON_NULL} return NON_NULL; 257 {NULL_RET} return NULL_RET; 258 {OK_RET} return OK_RET; 259 {ERR_RET} return ERR_RET; 260 {MULTIPLIER} return MULTIPLIER; 261 {COMMA} return COMMA; 262 {CCHAR} return CCHAR; 263 {WCHAR} return WCHAR; 264 {OR} return OR; 265 {LPAREN} return LPAREN; 266 {RPAREN} return RPAREN; 267 {LBRACK} return LBRACK; 268 {RBRACK} return RBRACK; 269 270 {HEX} { 271 /* Hex value, convert to decimal and return numeric */ 272 unsigned long val; 273 274 if (sscanf(yytext, "%lx", &val) != 1) 275 errx(1, "Bad hex conversion"); 276 277 asprintf(&yylval.string, "%ld", val); 278 return numeric; 279 } 280 281 {numeric} { 282 if ((yylval.string = strdup(yytext)) == NULL) 283 err(1, "Cannot allocate numeric string"); 284 return numeric; 285 } 286 287 {VARNAME} { 288 if ((yylval.string = strdup(yytext)) == NULL) 289 err(1, "Cannot allocate string for varname"); 290 return VARNAME; 291 } 292 293 {FILENAME} { 294 size_t len; 295 296 if ((yylval.string = dequote(yytext, &len)) == NULL) 297 err(1, "Cannot allocate filename string"); 298 return FILENAME; 299 } 300 301 /* path */ 302 \/{PCHAR}+ { 303 size_t len; 304 if ((yylval.string = dequote(yytext, &len)) == NULL) 305 err(1, "Cannot allocate string"); 306 return PATH; 307 } 308 309 \'{STRING}\' { 310 char *p; 311 size_t len; 312 313 if ((yylval.retval = malloc(sizeof(ct_data_t))) == NULL) 314 err(1, "Cannot allocate return struct"); 315 p = yytext; 316 p++; /* skip the leading ' */ 317 if ((yylval.retval->data_value = dequote(p, &len)) 318 == NULL) 319 err(1, "Cannot allocate string"); 320 321 yylval.retval->data_type = data_byte; 322 /* trim trailing ' */ 323 yylval.retval->data_len = len - 1; 324 return BYTE; 325 } 326 327 \`{STRING}\` { 328 char *p, *str; 329 size_t len, chlen; 330 size_t i; 331 chtype *rv; 332 333 if ((yylval.retval = malloc(sizeof(ct_data_t))) == NULL) 334 err(1, "Cannot allocate return struct"); 335 p = yytext; 336 p++; /* skip the leading ` */ 337 if ((str = dequote(p, &len)) == NULL) 338 err(1, "Cannot allocate string"); 339 len--; /* trim trailing ` */ 340 if ((len % 2) != 0) 341 len--; 342 343 chlen = ((len / 2) + 1) * sizeof(chtype); 344 if ((yylval.retval->data_value = malloc(chlen)) 345 == NULL) 346 err(1, "Cannot allocate chtype array"); 347 348 rv = yylval.retval->data_value; 349 for (i = 0; i < len; i += 2) 350 *rv++ = (str[i] << 8) | str[i+1]; 351 *rv = __NORMAL | '\0'; /* terminates chtype array */ 352 yylval.retval->data_type = data_byte; 353 yylval.retval->data_len = chlen; 354 return BYTE; 355 } 356 357 \"{STRING}\" { 358 char *p; 359 size_t len; 360 361 p = yytext; 362 p++; /* skip the leading " */ 363 if ((yylval.string = dequote(p, &len)) == NULL) 364 err(1, "Cannot allocate string"); 365 366 /* remove trailing " */ 367 yylval.string[len - 1] = '\0'; 368 return STRING; 369 } 370 371 \${VARNAME} { 372 char *p; 373 374 p = yytext; 375 p++; /* skip $ before var name */ 376 if ((yylval.string = strdup(p)) == NULL) 377 err(1, "Cannot allocate string for varname"); 378 return VARIABLE; 379 } 380 381 /* whitespace, comments */ 382 [ \t\r] | 383 #.* ; 384 385 ^[ \t\r]*#.*\n | 386 \\\n | 387 ^\n line++; 388 389 /* eol on a line with data. need to process, return eol */ 390 #.*\n | 391 \n { 392 line++; 393 return EOL; 394 } 395 396 . { 397 if (isprint((unsigned char)yytext[0])) 398 errx(1, "%s:%zu: Invalid character '%c'", 399 cur_file, line + 1, yytext[0]); 400 else 401 errx(1, "%s:%zu: Invalid character '0x%02x'", 402 cur_file, line + 1, yytext[0]); 403 } 404 405 %% 406 407 int 408 yywrap(void) 409 { 410 return 1; 411 } 412