1 /* $OpenBSD: parse.y,v 1.10 2007/10/16 20:01:23 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; 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 quoted string"); 204 if (popfile() == EOF) 205 return (EOF); 206 return (quotec); 207 } 208 return (c); 209 } 210 211 while ((c = getc(file->stream)) == '\\') { 212 next = getc(file->stream); 213 if (next != '\n') { 214 c = next; 215 break; 216 } 217 yylval.lineno = file->lineno; 218 file->lineno++; 219 } 220 if (c == '\t' || c == ' ') { 221 /* Compress blanks to a single space. */ 222 do { 223 c = getc(file->stream); 224 } while (c == '\t' || c == ' '); 225 ungetc(c, file->stream); 226 c = ' '; 227 } 228 229 while (c == EOF) { 230 if (popfile() == EOF) 231 return (EOF); 232 c = getc(file->stream); 233 } 234 return (c); 235 } 236 237 int 238 lungetc(int c) 239 { 240 if (c == EOF) 241 return (EOF); 242 if (parsebuf) { 243 parseindex--; 244 if (parseindex >= 0) 245 return (c); 246 } 247 if (pushback_index < MAXPUSHBACK-1) 248 return (pushback_buffer[pushback_index++] = c); 249 else 250 return (EOF); 251 } 252 253 int 254 findeol(void) 255 { 256 int c; 257 258 parsebuf = NULL; 259 pushback_index = 0; 260 261 /* skip to either EOF or the first real EOL */ 262 while (1) { 263 c = lgetc(0); 264 if (c == '\n') { 265 file->lineno++; 266 break; 267 } 268 if (c == EOF) 269 break; 270 } 271 return (ERROR); 272 } 273 274 int 275 yylex(void) 276 { 277 char buf[8096]; 278 char *p; 279 int quotec, next, c; 280 int token; 281 282 p = buf; 283 while ((c = lgetc(0)) == ' ') 284 ; /* nothing */ 285 286 yylval.lineno = file->lineno; 287 if (c == '#') 288 while ((c = lgetc(0)) != '\n' && c != EOF) 289 ; /* nothing */ 290 291 switch (c) { 292 case '\'': 293 case '"': 294 quotec = c; 295 while (1) { 296 if ((c = lgetc(quotec)) == EOF) 297 return (0); 298 if (c == '\n') { 299 file->lineno++; 300 continue; 301 } else if (c == '\\') { 302 if ((next = lgetc(quotec)) == EOF) 303 return (0); 304 if (next == quotec || c == ' ' || c == '\t') 305 c = next; 306 else if (next == '\n') 307 continue; 308 else 309 lungetc(next); 310 } else if (c == quotec) { 311 *p = '\0'; 312 break; 313 } 314 if (p + 1 >= buf + sizeof(buf) - 1) { 315 yyerror("string too long"); 316 return (findeol()); 317 } 318 *p++ = (char)c; 319 } 320 yylval.v.string = strdup(buf); 321 if (yylval.v.string == NULL) 322 err(1, "yylex: strdup"); 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 ((unsigned)(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 ((unsigned)(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, "yylex: strdup"); 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 (nfile->name = strdup(name)) == NULL) 399 return (NULL); 400 if ((nfile->stream = fopen(nfile->name, "r")) == NULL) { 401 free(nfile->name); 402 free(nfile); 403 return (NULL); 404 } 405 nfile->lineno = 1; 406 TAILQ_INSERT_TAIL(&files, nfile, entry); 407 return (nfile); 408 } 409 410 int 411 popfile(void) 412 { 413 struct file *prev; 414 415 if ((prev = TAILQ_PREV(file, files, entry)) != NULL) { 416 prev->errors += file->errors; 417 TAILQ_REMOVE(&files, file, entry); 418 fclose(file->stream); 419 free(file->name); 420 free(file); 421 file = prev; 422 return (0); 423 } 424 return (EOF); 425 } 426 427 char * 428 parse_tapedev(const char *filename, const char *changer, int drive) 429 { 430 struct changer *p; 431 char *tapedev = NULL; 432 int errors = 0; 433 434 TAILQ_INIT(&changers); 435 436 if ((file = pushfile(filename)) == NULL) { 437 warnx("cannot open the main config file!"); 438 goto guess; 439 } 440 441 yyparse(); 442 errors = file->errors; 443 popfile(); 444 445 TAILQ_FOREACH(p, &changers, entry) { 446 if (strcmp(basename(changer), p->name) == 0) { 447 if (drive >= 0 && drive < p->drivecnt) { 448 if (asprintf(&tapedev, "/dev/%s", 449 p->drives[drive]) == -1) 450 errx(1, "malloc failed"); 451 } else 452 tapedev = NULL; 453 } 454 } 455 456 guess: 457 /* if no device found, do the default of /dev/rstX */ 458 if (tapedev == NULL) 459 if (asprintf(&tapedev, "/dev/rst%d", drive) == -1) 460 errx(1, "malloc failed"); 461 return (tapedev); 462 } 463 464 struct changer * 465 new_changer(char *name) 466 { 467 struct changer *p; 468 469 if ((p = calloc(1, sizeof(*p))) == NULL) 470 err(1, NULL); 471 472 if ((p->name = strdup(name)) == NULL) 473 err(1, NULL); 474 475 return (p); 476 } 477 478 479