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