1 /* $OpenBSD: parse.y,v 1.23 2020/10/15 19:42:56 naddy Exp $ */ 2 3 /* 4 * Copyright (c) 2006 Bob Beck <beck@openbsd.org> 5 * Copyright (c) 2002-2006 Henning Brauer <henning@openbsd.org> 6 * Copyright (c) 2001 Markus Friedl. All rights reserved. 7 * Copyright (c) 2001 Daniel Hartmeier. All rights reserved. 8 * Copyright (c) 2001 Theo de Raadt. All rights reserved. 9 * 10 * Permission to use, copy, modify, and distribute this software for any 11 * purpose with or without fee is hereby granted, provided that the above 12 * copyright notice and this permission notice appear in all copies. 13 * 14 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 15 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 16 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 17 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 18 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 19 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 20 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 21 */ 22 23 %{ 24 #include <sys/types.h> 25 #include <sys/socket.h> 26 #include <sys/queue.h> 27 28 #include <ctype.h> 29 #include <err.h> 30 #include <libgen.h> 31 #include <limits.h> 32 #include <paths.h> 33 #include <stdarg.h> 34 #include <stdio.h> 35 #include <string.h> 36 37 TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files); 38 static struct file { 39 TAILQ_ENTRY(file) entry; 40 FILE *stream; 41 char *name; 42 int lineno; 43 int errors; 44 } *file, *topfile; 45 struct file *pushfile(const char *); 46 int popfile(void); 47 int yyparse(void); 48 int yylex(void); 49 int yyerror(const char *, ...) 50 __attribute__((__format__ (printf, 1, 2))) 51 __attribute__((__nonnull__ (1))); 52 int kw_cmp(const void *, const void *); 53 int lookup(char *); 54 int lgetc(int); 55 int lungetc(int); 56 int findeol(void); 57 char *parse_tapedev(const char *, const char *, int); 58 struct changer *new_changer(char *); 59 60 struct changer { 61 TAILQ_ENTRY(changer) entry; 62 char *name; 63 char **drives; 64 u_int drivecnt; 65 }; 66 TAILQ_HEAD(changers, changer) changers; 67 struct changer *curchanger; 68 69 typedef struct { 70 union { 71 int64_t number; 72 char *string; 73 } v; 74 int lineno; 75 } YYSTYPE; 76 77 %} 78 79 %token CHANGER 80 %token DRIVE 81 %token ERROR 82 %token <v.string> STRING 83 %token <v.number> NUMBER 84 %% 85 86 grammar : /* empty */ 87 | grammar '\n' 88 | grammar main '\n' 89 | grammar error '\n' { file->errors++; } 90 ; 91 92 optnl : '\n' optnl 93 | 94 ; 95 96 nl : '\n' optnl 97 ; 98 99 main : CHANGER STRING optnl '{' optnl { 100 curchanger = new_changer($2); 101 } 102 changeropts_l '}' { 103 TAILQ_INSERT_TAIL(&changers, curchanger, entry); 104 curchanger = NULL; 105 } 106 ; 107 108 changeropts_l : changeropts_l changeroptsl 109 | changeroptsl 110 ; 111 112 changeroptsl : changeropts nl 113 | error nl 114 ; 115 116 changeropts : DRIVE STRING { 117 void *newp; 118 119 if ((newp = reallocarray(curchanger->drives, 120 curchanger->drivecnt + 1, 121 sizeof(curchanger->drives))) == NULL) 122 err(1, NULL); 123 curchanger->drives = newp; 124 if ((curchanger->drives[curchanger->drivecnt] = 125 strdup($2)) == NULL) 126 err(1, NULL); 127 curchanger->drivecnt++; 128 free($2); 129 } 130 ; 131 132 %% 133 134 struct keywords { 135 const char *k_name; 136 int k_val; 137 }; 138 139 int 140 yyerror(const char *fmt, ...) 141 { 142 va_list ap; 143 char *msg; 144 145 file->errors++; 146 va_start(ap, fmt); 147 if (vasprintf(&msg, fmt, ap) == -1) 148 err(1, "yyerror vasprintf"); 149 va_end(ap); 150 err(1, "%s:%d: %s", file->name, yylval.lineno, msg); 151 free(msg); 152 return (0); 153 } 154 155 int 156 kw_cmp(const void *k, const void *e) 157 { 158 return (strcmp(k, ((const struct keywords *)e)->k_name)); 159 } 160 161 int 162 lookup(char *s) 163 { 164 /* this has to be sorted always */ 165 static const struct keywords keywords[] = { 166 { "changer", CHANGER}, 167 { "drive", DRIVE} 168 }; 169 const struct keywords *p; 170 171 p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]), 172 sizeof(keywords[0]), kw_cmp); 173 174 if (p) 175 return (p->k_val); 176 else 177 return (STRING); 178 } 179 180 #define MAXPUSHBACK 128 181 182 u_char *parsebuf; 183 int parseindex; 184 u_char pushback_buffer[MAXPUSHBACK]; 185 int pushback_index = 0; 186 187 int 188 lgetc(int quotec) 189 { 190 int c, next; 191 192 if (parsebuf) { 193 /* Read character from the parsebuffer instead of input. */ 194 if (parseindex >= 0) { 195 c = parsebuf[parseindex++]; 196 if (c != '\0') 197 return (c); 198 parsebuf = NULL; 199 } else 200 parseindex++; 201 } 202 203 if (pushback_index) 204 return (pushback_buffer[--pushback_index]); 205 206 if (quotec) { 207 if ((c = getc(file->stream)) == EOF) { 208 yyerror("reached end of file while parsing " 209 "quoted string"); 210 if (file == topfile || popfile() == EOF) 211 return (EOF); 212 return (quotec); 213 } 214 return (c); 215 } 216 217 while ((c = getc(file->stream)) == '\\') { 218 next = getc(file->stream); 219 if (next != '\n') { 220 c = next; 221 break; 222 } 223 yylval.lineno = file->lineno; 224 file->lineno++; 225 } 226 227 while (c == EOF) { 228 if (file == topfile || popfile() == EOF) 229 return (EOF); 230 c = getc(file->stream); 231 } 232 return (c); 233 } 234 235 int 236 lungetc(int c) 237 { 238 if (c == EOF) 239 return (EOF); 240 if (parsebuf) { 241 parseindex--; 242 if (parseindex >= 0) 243 return (c); 244 } 245 if (pushback_index < MAXPUSHBACK-1) 246 return (pushback_buffer[pushback_index++] = c); 247 else 248 return (EOF); 249 } 250 251 int 252 findeol(void) 253 { 254 int c; 255 256 parsebuf = NULL; 257 pushback_index = 0; 258 259 /* skip to either EOF or the first real EOL */ 260 while (1) { 261 c = lgetc(0); 262 if (c == '\n') { 263 file->lineno++; 264 break; 265 } 266 if (c == EOF) 267 break; 268 } 269 return (ERROR); 270 } 271 272 int 273 yylex(void) 274 { 275 u_char buf[8096]; 276 u_char *p; 277 int quotec, next, c; 278 int token; 279 280 p = buf; 281 while ((c = lgetc(0)) == ' ' || c == '\t') 282 ; /* nothing */ 283 284 yylval.lineno = file->lineno; 285 if (c == '#') 286 while ((c = lgetc(0)) != '\n' && c != EOF) 287 ; /* nothing */ 288 289 switch (c) { 290 case '\'': 291 case '"': 292 quotec = c; 293 while (1) { 294 if ((c = lgetc(quotec)) == EOF) 295 return (0); 296 if (c == '\n') { 297 file->lineno++; 298 continue; 299 } else if (c == '\\') { 300 if ((next = lgetc(quotec)) == EOF) 301 return (0); 302 if (next == quotec || c == ' ' || c == '\t') 303 c = next; 304 else if (next == '\n') 305 continue; 306 else 307 lungetc(next); 308 } else if (c == quotec) { 309 *p = '\0'; 310 break; 311 } else if (c == '\0') { 312 yyerror("syntax error"); 313 return (findeol()); 314 } 315 if (p + 1 >= buf + sizeof(buf) - 1) { 316 yyerror("string too long"); 317 return (findeol()); 318 } 319 *p++ = c; 320 } 321 yylval.v.string = strdup(buf); 322 if (yylval.v.string == NULL) 323 err(1, "%s", __func__); 324 return (STRING); 325 } 326 327 #define allowed_to_end_number(x) \ 328 (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=') 329 330 if (c == '-' || isdigit(c)) { 331 do { 332 *p++ = c; 333 if ((size_t)(p-buf) >= sizeof(buf)) { 334 yyerror("string too long"); 335 return (findeol()); 336 } 337 } while ((c = lgetc(0)) != EOF && isdigit(c)); 338 lungetc(c); 339 if (p == buf + 1 && buf[0] == '-') 340 goto nodigits; 341 if (c == EOF || allowed_to_end_number(c)) { 342 const char *errstr = NULL; 343 344 *p = '\0'; 345 yylval.v.number = strtonum(buf, LLONG_MIN, 346 LLONG_MAX, &errstr); 347 if (errstr) { 348 yyerror("\"%s\" invalid number: %s", 349 buf, errstr); 350 return (findeol()); 351 } 352 return (NUMBER); 353 } else { 354 nodigits: 355 while (p > buf + 1) 356 lungetc(*--p); 357 c = *--p; 358 if (c == '-') 359 return (c); 360 } 361 } 362 363 #define allowed_in_string(x) \ 364 (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \ 365 x != '{' && x != '}' && x != '<' && x != '>' && \ 366 x != '!' && x != '=' && x != '/' && x != '#' && \ 367 x != ',')) 368 369 if (isalnum(c) || c == ':' || c == '_' || c == '*') { 370 do { 371 *p++ = c; 372 if ((size_t)(p-buf) >= sizeof(buf)) { 373 yyerror("string too long"); 374 return (findeol()); 375 } 376 } while ((c = lgetc(0)) != EOF && (allowed_in_string(c))); 377 lungetc(c); 378 *p = '\0'; 379 if ((token = lookup(buf)) == STRING) 380 if ((yylval.v.string = strdup(buf)) == NULL) 381 err(1, "%s", __func__); 382 return (token); 383 } 384 if (c == '\n') { 385 yylval.lineno = file->lineno; 386 file->lineno++; 387 } 388 if (c == EOF) 389 return (0); 390 return (c); 391 } 392 393 struct file * 394 pushfile(const char *name) 395 { 396 struct file *nfile; 397 398 if ((nfile = calloc(1, sizeof(struct file))) == NULL) 399 return (NULL); 400 if ((nfile->name = strdup(name)) == NULL) { 401 free(nfile); 402 return (NULL); 403 } 404 if ((nfile->stream = fopen(nfile->name, "r")) == NULL) { 405 free(nfile->name); 406 free(nfile); 407 return (NULL); 408 } 409 nfile->lineno = 1; 410 TAILQ_INSERT_TAIL(&files, nfile, entry); 411 return (nfile); 412 } 413 414 int 415 popfile(void) 416 { 417 struct file *prev; 418 419 if ((prev = TAILQ_PREV(file, files, entry)) != NULL) 420 prev->errors += file->errors; 421 422 TAILQ_REMOVE(&files, file, entry); 423 fclose(file->stream); 424 free(file->name); 425 free(file); 426 file = prev; 427 return (file ? 0 : EOF); 428 } 429 430 char * 431 parse_tapedev(const char *filename, const char *changer, int drive) 432 { 433 struct changer *p; 434 char *tapedev = NULL; 435 int errors = 0; 436 437 TAILQ_INIT(&changers); 438 439 if ((file = pushfile(filename)) == NULL) { 440 warnx("cannot open the main config file!"); 441 goto guess; 442 } 443 topfile = file; 444 445 yyparse(); 446 errors = file->errors; 447 popfile(); 448 449 if (strncmp(changer, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0) 450 changer += sizeof(_PATH_DEV) - 1; 451 TAILQ_FOREACH(p, &changers, entry) { 452 if (strcmp(changer, p->name) == 0) { 453 if (drive >= 0 && drive < p->drivecnt) { 454 if (asprintf(&tapedev, _PATH_DEV "%s", 455 p->drives[drive]) == -1) 456 errx(1, "malloc failed"); 457 } else 458 tapedev = NULL; 459 } 460 } 461 462 guess: 463 /* if no device found, do the default of /dev/rstX */ 464 if (tapedev == NULL) 465 if (asprintf(&tapedev, "/dev/rst%d", drive) == -1) 466 errx(1, "malloc failed"); 467 return (tapedev); 468 } 469 470 struct changer * 471 new_changer(char *name) 472 { 473 struct changer *p; 474 475 if ((p = calloc(1, sizeof(*p))) == NULL) 476 err(1, NULL); 477 478 if ((p->name = strdup(name)) == NULL) 479 err(1, NULL); 480 481 return (p); 482 } 483 484 485