1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #include <stdio.h> 28 #include <ctype.h> 29 #include <stdlib.h> 30 #include <unistd.h> 31 #include <string.h> 32 #include <dlfcn.h> 33 #include <dirent.h> 34 #include <libgen.h> 35 #include <sys/param.h> 36 #include <errno.h> 37 38 #include "parser.h" 39 #include "errlog.h" 40 41 static char const *ARCH_I386 = "i386"; 42 static char const *ARCH_SPARC = "sparc"; 43 static char const *ARCH_SPARCV9 = "sparcv9"; 44 static char const *ARCH_IA64 = "ia64"; 45 static char const *ARCH_AMD64 = "amd64"; 46 static char const *ARCH_ALL = "all"; 47 48 static int dofiles(const Translator_info *); 49 static int read_spec(const Translator_info *, char *); 50 51 static int Curlineno; 52 53 xlator_keyword_t *keywordlist; 54 55 /* 56 * frontend entry point 57 * returns the number of errors encountered 58 */ 59 int 60 frontend(const Translator_info *T_info) 61 { 62 int retval, i = 0, errors = 0; 63 64 keywordlist = xlator_init(T_info); 65 if (keywordlist == NULL) { 66 errlog(ERROR, "Error: Unable to get keywordlist\n"); 67 return (1); 68 } 69 70 if (T_info->ti_verbosity >= STATUS) { 71 errlog(STATUS, "interesting keywords:\n"); 72 while (keywordlist[i].key != NULL) { 73 errlog(STATUS, "\t%s\n", keywordlist[i].key); 74 ++i; 75 }; 76 } 77 78 retval = xlator_startlib(T_info->ti_liblist); 79 switch (retval) { 80 case XLATOR_SKIP: 81 if (T_info->ti_verbosity >= STATUS) 82 errlog(STATUS, "Skipping %s\n", T_info->ti_liblist); 83 retval = 0; 84 break; 85 86 case XLATOR_NONFATAL: 87 ++errors; 88 retval = 0; 89 break; 90 91 case XLATOR_SUCCESS: 92 retval = dofiles(T_info); 93 errors += retval; 94 if ((retval = xlator_endlib()) != XLATOR_SUCCESS) 95 ++errors; 96 retval = 0; 97 break; 98 99 default: 100 errlog(ERROR | FATAL, 101 "Error: Invalid return code from xlator_startlib()\n"); 102 exit(1); 103 } 104 105 if ((retval = xlator_end()) != XLATOR_SUCCESS) 106 ++errors; 107 108 return (errors); 109 } 110 111 /* 112 * dofiles(const Translator_info *T_info); 113 * iterate through files specified in the command line and process 114 * them one by one 115 * requires spec files to have a ".spec" suffix 116 * returns the number of errors; 117 */ 118 static int 119 dofiles(const Translator_info *T_info) 120 { 121 int nfiles, flen, findex, retval = 0, errors = 0; 122 123 nfiles = T_info->ti_nfiles; 124 125 for (findex = 0; findex < nfiles; ++findex) { 126 flen = strlen(filelist[findex]); 127 if ((flen <= 5) || 128 strcmp(&filelist[findex][flen-5], ".spec") != 0) { 129 errlog(ERROR, 130 "Error: File specified does not have the " 131 ".spec extension: %s\n", filelist[findex]); 132 ++errors; 133 continue; 134 }; 135 retval = read_spec(T_info, filelist[findex]); 136 errors += retval; 137 } 138 return (errors); 139 } 140 141 /* 142 * read_spec - 143 * Given a filename, this function will reads the spec file to 144 * recognize keywords which it passes along with the corresponding 145 * value to the back-end translator to process. The following 146 * back-end interfaces are called: 147 * xlator_startfile 148 * xlator_start_if 149 * xlator_take_kvpair 150 * xlator_end_if 151 * xlator_endfile 152 */ 153 static int 154 read_spec(const Translator_info *T_info, char *spec_filename) 155 { 156 FILE *spec_fp; 157 Meta_info meta_info; 158 char key[BUFSIZ], *value = NULL, *p = NULL; 159 char *buf2 = NULL; 160 int retval = 0, errors = 0, ki = 0; /* keyword indicator */ 161 int start_if_fail = 0, skip_if = 0; 162 int extends_err = 0; 163 164 meta_info.mi_ext_cnt = 0; /* All info is non-extends */ 165 meta_info.mi_flags = 0; 166 167 retval = xlator_startfile(spec_filename); 168 169 switch (retval) { 170 case XLATOR_SKIP: 171 if (T_info->ti_verbosity >= WARNING) 172 errlog(WARNING, "Warning: Skipping %s\n", 173 spec_filename); 174 return (errors); 175 176 case XLATOR_NONFATAL: 177 errlog(ERROR, "Error in xlator_startfile\n"); 178 ++errors; 179 return (errors); 180 181 case XLATOR_SUCCESS: 182 break; 183 184 default: 185 errlog(ERROR, 186 "Error: Invalid return code from xlator_startfile()\n"); 187 ++errors; 188 return (errors); 189 }; 190 191 /* file processing */ 192 spec_fp = fopen(spec_filename, "r"); 193 if (spec_fp == NULL) { 194 errlog(ERROR, "Error: Unable to open spec file %s: %s\n", 195 spec_filename, strerror(errno)); 196 ++errors; 197 return (errors); 198 } 199 200 (void) strncpy(meta_info.mi_filename, spec_filename, BUFSIZ); 201 meta_info.mi_line_number = 0; 202 Curlineno = meta_info.mi_line_number; 203 while (meta_info.mi_nlines = readline(&buf2, spec_fp)) { 204 meta_info.mi_line_number += meta_info.mi_nlines; 205 Curlineno = meta_info.mi_line_number; 206 if (!non_empty(buf2)) { 207 free(buf2); 208 buf2 = NULL; 209 continue; 210 } 211 p = realloc(value, sizeof (char)*(strlen(buf2)+1)); 212 if (p == NULL) { 213 errlog(ERROR | FATAL, 214 "Error: Unable to allocate memory for " 215 "value: %d\n", errno); 216 } 217 value = p; 218 split(buf2, key, value); 219 ki = interesting_keyword(keywordlist, key); 220 switch (ki) { 221 case XLATOR_KW_FUNC: /* Function keyword */ 222 case XLATOR_KW_DATA: /* Data keyword */ 223 meta_info.mi_extended = 0; 224 retval = xlator_start_if(meta_info, ki, value); 225 switch (retval) { 226 case XLATOR_FATAL: /* FATAL ERROR */ 227 if (T_info->ti_verbosity >= STATUS) { 228 errlog(STATUS, 229 "Error in xlator_start_if: "); 230 } 231 ++errors; 232 return (errors); 233 case XLATOR_NONFATAL: /* NON-FATAL ERROR */ 234 if (T_info->ti_verbosity >= STATUS) 235 errlog(STATUS, 236 "Error in xlator_start_if\n"); 237 ++errors; 238 start_if_fail = 1; 239 break; 240 case XLATOR_SUCCESS: /* OK */ 241 start_if_fail = 0; 242 extends_err = check4extends(spec_filename, 243 value, T_info->ti_archtoken, spec_fp); 244 switch (extends_err) { 245 case -1: /* Error */ 246 errlog(ERROR, "\"%s\", line %d: " 247 "Error occurred while " 248 "checking for extends clause\n", 249 spec_filename, Curlineno); 250 ++errors; 251 /*FALLTHRU*/ 252 case 0: /* No Extends */ 253 break; 254 case 1: /* Extends */ 255 meta_info.mi_extended = 1; 256 extends_err = do_extends(meta_info, 257 T_info, value); 258 if (extends_err) { 259 errors += extends_err; 260 } 261 break; 262 default: /* Programmer Error */ 263 errlog(ERROR | FATAL, 264 "Error: invalid return from " 265 "check4extends %d\n", extends_err); 266 } 267 break; 268 case XLATOR_SKIP: /* SKIP */ 269 if (T_info->ti_verbosity >= WARNING) 270 errlog(WARNING, "Warning: Skipping " 271 "interface %s\n", value); 272 skip_if = 1; 273 start_if_fail = 0; 274 break; 275 default: 276 /* Invalid Return */ 277 errlog(ERROR | FATAL, 278 "Error: Invalid return code " 279 "from xlator_start_if (): %d\n", retval); 280 } 281 break; 282 case XLATOR_KW_END: /* END keyword */ 283 if (start_if_fail == 0 && skip_if == 0) { 284 retval = xlator_end_if(meta_info, value); 285 if (retval) 286 ++errors; 287 } 288 skip_if = 0; 289 break; 290 case XLATOR_KW_NOTFOUND: 291 if (T_info->ti_verbosity >= TRACING) 292 errlog(TRACING, "uninteresting keyword: %s\n", 293 key); 294 break; 295 default: 296 if (skip_if == 0 && start_if_fail == 0) { 297 retval = xlator_take_kvpair(meta_info, 298 ki, value); 299 if (retval) { 300 if (T_info->ti_verbosity >= STATUS) 301 errlog(STATUS, "Error in " 302 "xlator_take_kvpair\n"); 303 ++errors; 304 } 305 } 306 } 307 free(buf2); 308 buf2 = NULL; 309 } 310 311 if ((retval = xlator_endfile()) != XLATOR_SUCCESS) { 312 if (T_info->ti_verbosity >= STATUS) 313 errlog(STATUS, "Error in xlator_endfile\n"); 314 ++errors; 315 } 316 free(p); 317 (void) fclose(spec_fp); 318 return (errors); 319 } 320 321 /* 322 * interesting_keyword(char **keywordlist, const char *key) { 323 * returns the token associated with key if key is found in keywordlist 324 * returns XLATOR_KW_NOTFOUND if key is NOT found in keywordlist 325 * "Function" and "End" are always interesting, return XLATOR_KW_FUNC 326 * and XLATOR_KW_DATA respectively; 327 * "End" is always interesting, return XLATOR_KW_END; 328 * 329 */ 330 int 331 interesting_keyword(xlator_keyword_t *keywordlist, const char *key) 332 { 333 int i = 0; 334 335 if (strcasecmp(key, "data") == 0) { 336 return (XLATOR_KW_DATA); 337 } 338 if (strcasecmp(key, "function") == 0) { 339 return (XLATOR_KW_FUNC); 340 } 341 342 if (strcasecmp(key, "end") == 0) 343 return (XLATOR_KW_END); 344 345 while (keywordlist[i].key != NULL) { 346 if (strcasecmp(keywordlist[i].key, key) == 0) 347 return (keywordlist[i].token); 348 ++i; 349 } 350 return (XLATOR_KW_NOTFOUND); 351 } 352 353 /* 354 * line_to_buf(char *dest, const char *src) { 355 * appends src to dest, dynamically increasing the size of dest. 356 * replaces the trailing '\' continuation character with a space. 357 * 358 * if src is continuation of dest, dest != NULL, and 359 * the last character in dest before the newline must be a `\' 360 * if src is not continuation of dest, then dest must be NULL 361 */ 362 char * 363 line_to_buf(char *dest, const char *src) 364 { 365 int slen = strlen(src); 366 int dlen; 367 368 if (dest == NULL) { 369 /* We're being called for the first time */ 370 dest = malloc(sizeof (char) * (slen + 1)); 371 if (dest == NULL) { 372 errlog(ERROR | FATAL, 373 "Error: Unable to allocate memory for dest\n"); 374 } 375 (void) strcpy(dest, src); 376 return (dest); 377 } 378 379 dlen = strlen(dest); 380 381 dest = realloc(dest, (size_t)(sizeof (char) * (dlen+slen+1))); 382 if (dest == NULL) { 383 errlog(ERROR | FATAL, 384 "Error: Unable to allocate memory for dest\n"); 385 } 386 387 if (dlen > 1) { 388 /* 389 * remove continuation character 390 * we replace the '\' from the previous line with a space 391 */ 392 if (dest[dlen-2] == '\\') { 393 dest[dlen-2] = ' '; 394 } 395 } 396 397 /* join the two strings */ 398 (void) strcat(dest, src); 399 400 return (dest); 401 } 402 403 /* 404 * non_empty(const char *str) 405 * assumes str is non null 406 * checks if str is a non empty string 407 * returns 1 if string contains non whitespace 408 * returns 0 if string contains only whitespace 409 */ 410 int 411 non_empty(const char *str) 412 { 413 while (*str != '\0') { 414 if (!isspace(*str)) 415 return (1); 416 ++str; 417 }; 418 return (0); 419 } 420 421 /* 422 * split(const char *line, char *key, char *value); 423 * splits the line into keyword (key) and value pair 424 */ 425 void 426 split(const char *line, char *key, char *value) 427 { 428 char *p; 429 430 p = (char *)line; 431 432 /* skip leading whitespace */ 433 while (isspace(*p)&& *p != '\0') 434 ++p; 435 436 /* copy keyword from line into key */ 437 while (!isspace(*p) && *p != '\0') 438 *key++ = *p++; 439 440 *key = '\0'; 441 442 /* skip whitespace */ 443 while (isspace(*p) && *p != '\0') 444 p++; 445 446 (void) strcpy(value, p); 447 448 } 449 450 /* 451 * check4extends(char *filename, char *value, int arch, FILE *fp) 452 * if no arch keyword is found or there is a MATCHING arch keyword 453 * returns 1 if value is of the form "data|function name extends" 454 * -1 for error 455 * 0 no other keyword after the function name 456 * else 457 * return 0 458 * 459 * filename is used only for error reporting 460 */ 461 int 462 check4extends(const char *filename, const char *value, int arch, FILE *fp) 463 { 464 char fun[BUFSIZ]; 465 char extends[BUFSIZ]; 466 int n; 467 468 if (arch_match(fp, arch)) { 469 split(value, fun, extends); 470 n = strlen(extends); 471 if (extends[n-1] == '\n') 472 extends[n-1] = '\0'; 473 if (strncasecmp("extends", extends, 7) == 0) { 474 return (1); 475 } else { 476 if (*extends != '\0') { 477 errlog(ERROR, "\"%s\", line %d: Error: " 478 "Trailing garbage after function name\n", 479 filename, Curlineno); 480 return (-1); 481 } 482 } 483 } 484 return (0); 485 } 486 487 /* 488 * remcomment (char *buf) 489 * replace comments with single whitespace 490 */ 491 /* XXX: There is currently no way to escape a comment character */ 492 void 493 remcomment(char const *buf) 494 { 495 char *p; 496 p = strchr(buf, '#'); 497 if (p) { 498 *p = ' '; 499 *(p+1) = '\0'; 500 } 501 } 502 503 /* 504 * arch_strtoi() 505 * 506 * input: string 507 * return: XLATOR_I386 if string == ARCH_I386 508 * XLATOR_SPARC if string == ARCH_SPARC 509 * XLATOR_SPARCV9 if string == ARCH_SPARCV9 510 * XLATOR_IA64 if string == ARCH_IA64 511 * XLATOR_AMD64 if string == ARCH_AMD64 512 * XLATOR_ALLARCH if string == ARCH_ALL 513 * 0 if outside the known set {i386, sparc, sparcv9, ia64, amd64}. 514 */ 515 int 516 arch_strtoi(const char *arch_str) 517 { 518 if (arch_str != NULL) { 519 if (strcmp(arch_str, ARCH_I386) == 0) 520 return (XLATOR_I386); 521 else if (strcmp(arch_str, ARCH_SPARC) == 0) 522 return (XLATOR_SPARC); 523 else if (strcmp(arch_str, ARCH_SPARCV9) == 0) 524 return (XLATOR_SPARCV9); 525 else if (strcmp(arch_str, ARCH_IA64) == 0) 526 return (XLATOR_IA64); 527 else if (strcmp(arch_str, ARCH_AMD64) == 0) 528 return (XLATOR_AMD64); 529 else if (strcmp(arch_str, ARCH_ALL) == 0) 530 return (XLATOR_ALLARCH); 531 } else { 532 errlog(ERROR, "\"%s\", line %d: Error: " 533 "arch keyword with no value"); 534 } 535 return (0); 536 } 537 538 int 539 readline(char **buffer, FILE *fp) 540 { 541 int nlines = 0; 542 int len; 543 char buf[BUFSIZ]; 544 545 if (fgets(buf, BUFSIZ, fp)) { 546 nlines++; 547 /* replace comments with single whitespace */ 548 remcomment(buf); 549 550 /* get complete line */ 551 *buffer = line_to_buf(*buffer, buf); /* append buf to buffer */ 552 len = strlen(buf); 553 if (len > 1) { 554 /* handle continuation lines */ 555 while (buf[len-2] == '\\') { 556 if (!fgets(buf, BUFSIZ, fp)) { 557 *buffer = line_to_buf(*buffer, buf); 558 break; 559 } 560 nlines++; 561 len = strlen(buf); 562 *buffer = line_to_buf(*buffer, buf); 563 } 564 } /* end of 'get complete line' */ 565 } 566 return (nlines); 567 } 568