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