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