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