1 /* $OpenBSD: parse.y,v 1.6 2007/09/11 23:06:37 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(FILE *); 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(FILE *f) 179 { 180 int c, next; 181 182 if (parsebuf) { 183 /* Read character from the parsebuffer instead of input. */ 184 if (parseindex >= 0) { 185 c = parsebuf[parseindex++]; 186 if (c != '\0') 187 return (c); 188 parsebuf = NULL; 189 } else 190 parseindex++; 191 } 192 193 if (pushback_index) 194 return (pushback_buffer[--pushback_index]); 195 196 while ((c = getc(f)) == '\\') { 197 next = getc(f); 198 if (next != '\n') { 199 c = next; 200 break; 201 } 202 yylval.lineno = lineno; 203 lineno++; 204 } 205 if (c == '\t' || c == ' ') { 206 /* Compress blanks to a single space. */ 207 do { 208 c = getc(f); 209 } while (c == '\t' || c == ' '); 210 ungetc(c, f); 211 c = ' '; 212 } 213 214 return (c); 215 } 216 217 int 218 lungetc(int c) 219 { 220 if (c == EOF) 221 return (EOF); 222 if (parsebuf) { 223 parseindex--; 224 if (parseindex >= 0) 225 return (c); 226 } 227 if (pushback_index < MAXPUSHBACK-1) 228 return (pushback_buffer[pushback_index++] = c); 229 else 230 return (EOF); 231 } 232 233 int 234 findeol(void) 235 { 236 int c; 237 238 parsebuf = NULL; 239 pushback_index = 0; 240 241 /* skip to either EOF or the first real EOL */ 242 while (1) { 243 c = lgetc(fin); 244 if (c == '\n') { 245 lineno++; 246 break; 247 } 248 if (c == EOF) 249 break; 250 } 251 return (ERROR); 252 } 253 254 int 255 yylex(void) 256 { 257 char buf[8096]; 258 char *p; 259 int endc, c; 260 int token; 261 262 p = buf; 263 while ((c = lgetc(fin)) == ' ') 264 ; /* nothing */ 265 266 yylval.lineno = lineno; 267 if (c == '#') 268 while ((c = lgetc(fin)) != '\n' && c != EOF) 269 ; /* nothing */ 270 271 switch (c) { 272 case '\'': 273 case '"': 274 endc = c; 275 while (1) { 276 if ((c = lgetc(fin)) == EOF) 277 return (0); 278 if (c == endc) { 279 *p = '\0'; 280 break; 281 } 282 if (c == '\n') { 283 lineno++; 284 continue; 285 } 286 if (p + 1 >= buf + sizeof(buf) - 1) { 287 yyerror("string too long"); 288 return (findeol()); 289 } 290 *p++ = (char)c; 291 } 292 yylval.v.string = strdup(buf); 293 if (yylval.v.string == NULL) 294 err(1, "yylex: strdup"); 295 return (STRING); 296 } 297 298 #define allowed_to_end_number(x) \ 299 (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}') 300 301 if (c == '-' || isdigit(c)) { 302 do { 303 *p++ = c; 304 if ((unsigned)(p-buf) >= sizeof(buf)) { 305 yyerror("string too long"); 306 return (findeol()); 307 } 308 } while ((c = lgetc(fin)) != EOF && isdigit(c)); 309 lungetc(c); 310 if (p == buf + 1 && buf[0] == '-') 311 goto nodigits; 312 if (c == EOF || allowed_to_end_number(c)) { 313 const char *errstr = NULL; 314 315 *p = '\0'; 316 yylval.v.number = strtonum(buf, LLONG_MIN, 317 LLONG_MAX, &errstr); 318 if (errstr) { 319 yyerror("\"%s\" invalid number: %s", 320 buf, errstr); 321 return (findeol()); 322 } 323 return (NUMBER); 324 } else { 325 nodigits: 326 while (p > buf + 1) 327 lungetc(*--p); 328 c = *--p; 329 if (c == '-') 330 return (c); 331 } 332 } 333 334 #define allowed_in_string(x) \ 335 (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \ 336 x != '{' && x != '}' && x != '<' && x != '>' && \ 337 x != '!' && x != '=' && x != '/' && x != '#' && \ 338 x != ',')) 339 340 if (isalnum(c) || c == ':' || c == '_' || c == '*') { 341 do { 342 *p++ = c; 343 if ((unsigned)(p-buf) >= sizeof(buf)) { 344 yyerror("string too long"); 345 return (findeol()); 346 } 347 } while ((c = lgetc(fin)) != EOF && (allowed_in_string(c))); 348 lungetc(c); 349 *p = '\0'; 350 if ((token = lookup(buf)) == STRING) 351 if ((yylval.v.string = strdup(buf)) == NULL) 352 err(1, "yylex: strdup"); 353 return (token); 354 } 355 if (c == '\n') { 356 yylval.lineno = lineno; 357 lineno++; 358 } 359 if (c == EOF) 360 return (0); 361 return (c); 362 } 363 364 char * 365 parse_tapedev(const char *filename, const char *changer, int drive) 366 { 367 struct changer *p; 368 char *tapedev = NULL; 369 370 lineno = 1; 371 errors = 0; 372 TAILQ_INIT(&changers); 373 374 if ((fin = fopen(filename, "r")) == NULL) 375 goto guess; 376 377 infile = filename; 378 379 yyparse(); 380 381 fclose(fin); 382 383 TAILQ_FOREACH(p, &changers, entry) { 384 if (strcmp(basename(changer), p->name) == 0) { 385 if (drive >= 0 && drive < p->drivecnt) { 386 if (asprintf(&tapedev, "/dev/%s", 387 p->drives[drive]) == -1) 388 errx(1, "malloc failed"); 389 } else 390 tapedev = NULL; 391 } 392 } 393 394 guess: 395 /* if no device found, do the default of /dev/rstX */ 396 if (tapedev == NULL) 397 if (asprintf(&tapedev, "/dev/rst%d", drive) == -1) 398 errx(1, "malloc failed"); 399 return (tapedev); 400 } 401 402 struct changer * 403 new_changer(char *name) 404 { 405 struct changer *p; 406 407 if ((p = calloc(1, sizeof(*p))) == NULL) 408 err(1, NULL); 409 410 if ((p->name = strdup(name)) == NULL) 411 err(1, NULL); 412 413 return (p); 414 } 415 416 417