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