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