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