1 %{ 2 /* $NetBSD: testlang_conf.l,v 1.27 2023/12/10 18:04:55 rillig 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 errx(2, 90 "%s:%zu: Invalid escape sequence " 91 "'\\%c' in string literal; octal " 92 "numbers must be 3 digits wide", 93 cur_file, line, *p); 94 } 95 continue; 96 } 97 98 switch (*p) { 99 case 'b': 100 /* backspace */ 101 *q++ = '\b'; 102 p++; 103 break; 104 105 case 'e': 106 /* escape */ 107 *q++ = '\e'; 108 p++; 109 break; 110 111 case 'n': 112 /* newline */ 113 *q++ = '\n'; 114 p++; 115 break; 116 117 case 'r': 118 /* carriage return */ 119 *q++ = '\r'; 120 p++; 121 break; 122 123 case 't': 124 /* tab */ 125 *q++ = '\t'; 126 p++; 127 break; 128 129 case '\\': 130 /* backslash */ 131 *q++ = '\\'; 132 p++; 133 break; 134 135 default: 136 if (isalpha(*p)) 137 errx(2, 138 "%s:%zu: Invalid escape sequence " 139 "'\\%c' in string literal", 140 cur_file, line, *p); 141 *q++ = *p++; 142 } 143 } else 144 *q++ = *p++; 145 } 146 *q++ = '\0'; 147 148 return buf; 149 } 150 %} 151 152 HEX 0[xX][0-9a-zA-Z]+ 153 STRING [0-9a-z!#-&(-^ \t%._\\]+ 154 numeric [-0-9]+ 155 PCHAR (\\.|[!-~]) 156 ASSIGN assign 157 CALL2 call2 158 CALL3 call3 159 CALL4 call4 160 CALL call 161 CHECK check 162 DELAY delay 163 INPUT input 164 NOINPUT noinput 165 OK_RET OK 166 ERR_RET ERR 167 COMPARE compare 168 COMPAREND comparend 169 FILENAME [A-Za-z0-9.][A-Za-z0-9./_-]+ 170 VARNAME [A-Za-z][A-Za-z0-9_-]+ 171 NULL_RET NULL 172 NON_NULL NON_NULL 173 CCHAR cchar 174 WCHAR wchar 175 BYTE BYTE 176 OR \| 177 LPAREN \( 178 RPAREN \) 179 LBRACK \[ 180 RBRACK \] 181 MULTIPLIER \* 182 COMMA , 183 184 %x incl 185 %option noinput nounput 186 187 %% 188 189 include BEGIN(incl); 190 191 <incl>[ \t]* /* eat the whitespace */ 192 <incl>[^ \t\n]+ { /* got the include file name */ 193 char *inc_file; 194 195 if (include_ptr > MAX_INCLUDES) { 196 errx(2, 197 "%s:%zu: Maximum number of nested includes " 198 "exceeded", cur_file, line); 199 } 200 201 const char *dir_begin; 202 int dir_len; 203 if (yytext[0] == '/') { 204 dir_begin = ""; 205 dir_len = 0; 206 } else { 207 dir_begin = cur_file; 208 const char *dir_end = strrchr(cur_file, '/'); 209 if (dir_end != NULL) { 210 dir_len = (int)(dir_end + 1 - dir_begin); 211 } else { 212 dir_begin = "."; 213 dir_len = 1; 214 } 215 } 216 217 if (asprintf(&inc_file, "%.*s%s", 218 dir_len, dir_begin, yytext) == -1) 219 err(2, "Cannot construct include path"); 220 221 yyin = fopen(inc_file, "r"); 222 223 if (!yyin) 224 err(1, "Error opening %s", inc_file); 225 226 yypush_buffer_state(yy_create_buffer(yyin, YY_BUF_SIZE)); 227 228 include_stack[include_ptr] = line; 229 include_files[include_ptr++] = cur_file; 230 cur_file = inc_file; 231 line = 1; 232 BEGIN(INITIAL); 233 } 234 235 <<EOF>> { 236 yypop_buffer_state(); 237 238 if (!YY_CURRENT_BUFFER) 239 yyterminate(); 240 241 if (--include_ptr < 0) 242 errx(2, "Include stack underflow"); 243 244 free(cur_file); 245 cur_file = include_files[include_ptr]; 246 line = include_stack[include_ptr]; 247 } 248 249 {ASSIGN} return ASSIGN; 250 {CALL2} return CALL2; 251 {CALL3} return CALL3; 252 {CALL4} return CALL4; 253 {CALL} return CALL; 254 {CHECK} return CHECK; 255 {DELAY} return DELAY; 256 {INPUT} return INPUT; 257 {NOINPUT} return NOINPUT; 258 {COMPARE} return COMPARE; 259 {COMPAREND} return COMPAREND; 260 {NON_NULL} return NON_NULL; 261 {NULL_RET} return NULL_RET; 262 {OK_RET} return OK_RET; 263 {ERR_RET} return ERR_RET; 264 {MULTIPLIER} return MULTIPLIER; 265 {COMMA} return COMMA; 266 {CCHAR} return CCHAR; 267 {WCHAR} return WCHAR; 268 {OR} return OR; 269 {LPAREN} return LPAREN; 270 {RPAREN} return RPAREN; 271 {LBRACK} return LBRACK; 272 {RBRACK} return RBRACK; 273 274 {HEX} { 275 /* Hex value, convert to decimal and return numeric */ 276 unsigned long val; 277 278 if (sscanf(yytext, "%lx", &val) != 1) 279 errx(1, "Bad hex conversion"); 280 281 asprintf(&yylval.string, "%ld", val); 282 return numeric; 283 } 284 285 {numeric} { 286 if ((yylval.string = strdup(yytext)) == NULL) 287 err(1, "Cannot allocate numeric string"); 288 return numeric; 289 } 290 291 {VARNAME} { 292 if ((yylval.string = strdup(yytext)) == NULL) 293 err(1, "Cannot allocate string for varname"); 294 return VARNAME; 295 } 296 297 {FILENAME} { 298 size_t len; 299 300 if ((yylval.string = dequote(yytext, &len)) == NULL) 301 err(1, "Cannot allocate filename string"); 302 return FILENAME; 303 } 304 305 /* path */ 306 \/{PCHAR}+ { 307 size_t len; 308 if ((yylval.string = dequote(yytext, &len)) == NULL) 309 err(1, "Cannot allocate string"); 310 return PATH; 311 } 312 313 \'{STRING}\' { 314 char *p; 315 size_t len; 316 317 if ((yylval.retval = malloc(sizeof(ct_data_t))) == NULL) 318 err(1, "Cannot allocate return struct"); 319 p = yytext; 320 p++; /* skip the leading ' */ 321 if ((yylval.retval->data_value = dequote(p, &len)) 322 == NULL) 323 err(1, "Cannot allocate string"); 324 325 yylval.retval->data_type = data_byte; 326 /* trim trailing ' */ 327 yylval.retval->data_len = len - 1; 328 return BYTE; 329 } 330 331 \`{STRING}\` { 332 char *p, *str; 333 size_t len, chlen; 334 size_t i; 335 chtype *rv; 336 337 if ((yylval.retval = malloc(sizeof(ct_data_t))) == NULL) 338 err(1, "Cannot allocate return struct"); 339 p = yytext; 340 p++; /* skip the leading ` */ 341 if ((str = dequote(p, &len)) == NULL) 342 err(1, "Cannot allocate string"); 343 len--; /* trim trailing ` */ 344 if ((len % 2) != 0) 345 len--; 346 347 chlen = ((len / 2) + 1) * sizeof(chtype); 348 if ((yylval.retval->data_value = malloc(chlen)) 349 == NULL) 350 err(1, "Cannot allocate chtype array"); 351 352 rv = yylval.retval->data_value; 353 for (i = 0; i < len; i += 2) 354 *rv++ = (str[i] << 8) | str[i+1]; 355 *rv = __NORMAL | '\0'; /* terminates chtype array */ 356 yylval.retval->data_type = data_byte; 357 yylval.retval->data_len = chlen; 358 return BYTE; 359 } 360 361 \"{STRING}\" { 362 char *p; 363 size_t len; 364 365 p = yytext; 366 p++; /* skip the leading " */ 367 if ((yylval.string = dequote(p, &len)) == NULL) 368 err(1, "Cannot allocate string"); 369 370 /* remove trailing " */ 371 yylval.string[len - 1] = '\0'; 372 return STRING; 373 } 374 375 \${VARNAME} { 376 char *p; 377 378 p = yytext; 379 p++; /* skip $ before var name */ 380 if ((yylval.string = strdup(p)) == NULL) 381 err(1, "Cannot allocate string for varname"); 382 return VARIABLE; 383 } 384 385 /* whitespace, comments */ 386 [ \t\r] | 387 #.* ; 388 389 ^[ \t\r]*#.*\n | 390 \\\n | 391 ^\n line++; 392 393 /* eol on a line with data. need to process, return eol */ 394 #.*\n | 395 \n { 396 line++; 397 return EOL; 398 } 399 400 . { 401 if (isprint((unsigned char)yytext[0])) 402 errx(1, "%s:%zu: Invalid character '%c'", 403 cur_file, line + 1, yytext[0]); 404 else 405 errx(1, "%s:%zu: Invalid character '0x%02x'", 406 cur_file, line + 1, yytext[0]); 407 } 408 409 %% 410 411 int 412 yywrap(void) 413 { 414 return 1; 415 } 416