1 /* $OpenBSD: parse.y,v 1.11 2007/11/12 23:59:40 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 221 while (c == EOF) { 222 if (popfile() == EOF) 223 return (EOF); 224 c = getc(file->stream); 225 } 226 return (c); 227 } 228 229 int 230 lungetc(int c) 231 { 232 if (c == EOF) 233 return (EOF); 234 if (parsebuf) { 235 parseindex--; 236 if (parseindex >= 0) 237 return (c); 238 } 239 if (pushback_index < MAXPUSHBACK-1) 240 return (pushback_buffer[pushback_index++] = c); 241 else 242 return (EOF); 243 } 244 245 int 246 findeol(void) 247 { 248 int c; 249 250 parsebuf = NULL; 251 pushback_index = 0; 252 253 /* skip to either EOF or the first real EOL */ 254 while (1) { 255 c = lgetc(0); 256 if (c == '\n') { 257 file->lineno++; 258 break; 259 } 260 if (c == EOF) 261 break; 262 } 263 return (ERROR); 264 } 265 266 int 267 yylex(void) 268 { 269 char buf[8096]; 270 char *p; 271 int quotec, next, c; 272 int token; 273 274 p = buf; 275 while ((c = lgetc(0)) == ' ' || c == '\t') 276 ; /* nothing */ 277 278 yylval.lineno = file->lineno; 279 if (c == '#') 280 while ((c = lgetc(0)) != '\n' && c != EOF) 281 ; /* nothing */ 282 283 switch (c) { 284 case '\'': 285 case '"': 286 quotec = c; 287 while (1) { 288 if ((c = lgetc(quotec)) == EOF) 289 return (0); 290 if (c == '\n') { 291 file->lineno++; 292 continue; 293 } else if (c == '\\') { 294 if ((next = lgetc(quotec)) == EOF) 295 return (0); 296 if (next == quotec || c == ' ' || c == '\t') 297 c = next; 298 else if (next == '\n') 299 continue; 300 else 301 lungetc(next); 302 } else if (c == quotec) { 303 *p = '\0'; 304 break; 305 } 306 if (p + 1 >= buf + sizeof(buf) - 1) { 307 yyerror("string too long"); 308 return (findeol()); 309 } 310 *p++ = (char)c; 311 } 312 yylval.v.string = strdup(buf); 313 if (yylval.v.string == NULL) 314 err(1, "yylex: strdup"); 315 return (STRING); 316 } 317 318 #define allowed_to_end_number(x) \ 319 (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=') 320 321 if (c == '-' || isdigit(c)) { 322 do { 323 *p++ = c; 324 if ((unsigned)(p-buf) >= sizeof(buf)) { 325 yyerror("string too long"); 326 return (findeol()); 327 } 328 } while ((c = lgetc(0)) != EOF && isdigit(c)); 329 lungetc(c); 330 if (p == buf + 1 && buf[0] == '-') 331 goto nodigits; 332 if (c == EOF || allowed_to_end_number(c)) { 333 const char *errstr = NULL; 334 335 *p = '\0'; 336 yylval.v.number = strtonum(buf, LLONG_MIN, 337 LLONG_MAX, &errstr); 338 if (errstr) { 339 yyerror("\"%s\" invalid number: %s", 340 buf, errstr); 341 return (findeol()); 342 } 343 return (NUMBER); 344 } else { 345 nodigits: 346 while (p > buf + 1) 347 lungetc(*--p); 348 c = *--p; 349 if (c == '-') 350 return (c); 351 } 352 } 353 354 #define allowed_in_string(x) \ 355 (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \ 356 x != '{' && x != '}' && x != '<' && x != '>' && \ 357 x != '!' && x != '=' && x != '/' && x != '#' && \ 358 x != ',')) 359 360 if (isalnum(c) || c == ':' || c == '_' || c == '*') { 361 do { 362 *p++ = c; 363 if ((unsigned)(p-buf) >= sizeof(buf)) { 364 yyerror("string too long"); 365 return (findeol()); 366 } 367 } while ((c = lgetc(0)) != EOF && (allowed_in_string(c))); 368 lungetc(c); 369 *p = '\0'; 370 if ((token = lookup(buf)) == STRING) 371 if ((yylval.v.string = strdup(buf)) == NULL) 372 err(1, "yylex: strdup"); 373 return (token); 374 } 375 if (c == '\n') { 376 yylval.lineno = file->lineno; 377 file->lineno++; 378 } 379 if (c == EOF) 380 return (0); 381 return (c); 382 } 383 384 struct file * 385 pushfile(const char *name) 386 { 387 struct file *nfile; 388 389 if ((nfile = calloc(1, sizeof(struct file))) == NULL || 390 (nfile->name = strdup(name)) == NULL) 391 return (NULL); 392 if ((nfile->stream = fopen(nfile->name, "r")) == NULL) { 393 free(nfile->name); 394 free(nfile); 395 return (NULL); 396 } 397 nfile->lineno = 1; 398 TAILQ_INSERT_TAIL(&files, nfile, entry); 399 return (nfile); 400 } 401 402 int 403 popfile(void) 404 { 405 struct file *prev; 406 407 if ((prev = TAILQ_PREV(file, files, entry)) != NULL) { 408 prev->errors += file->errors; 409 TAILQ_REMOVE(&files, file, entry); 410 fclose(file->stream); 411 free(file->name); 412 free(file); 413 file = prev; 414 return (0); 415 } 416 return (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 433 yyparse(); 434 errors = file->errors; 435 popfile(); 436 437 TAILQ_FOREACH(p, &changers, entry) { 438 if (strcmp(basename(changer), p->name) == 0) { 439 if (drive >= 0 && drive < p->drivecnt) { 440 if (asprintf(&tapedev, "/dev/%s", 441 p->drives[drive]) == -1) 442 errx(1, "malloc failed"); 443 } else 444 tapedev = NULL; 445 } 446 } 447 448 guess: 449 /* if no device found, do the default of /dev/rstX */ 450 if (tapedev == NULL) 451 if (asprintf(&tapedev, "/dev/rst%d", drive) == -1) 452 errx(1, "malloc failed"); 453 return (tapedev); 454 } 455 456 struct changer * 457 new_changer(char *name) 458 { 459 struct changer *p; 460 461 if ((p = calloc(1, sizeof(*p))) == NULL) 462 err(1, NULL); 463 464 if ((p->name = strdup(name)) == NULL) 465 err(1, NULL); 466 467 return (p); 468 } 469 470 471