diff options
Diffstat (limited to 'usr/src/cmd/abi/spectrans/parser/frontend.c')
-rw-r--r-- | usr/src/cmd/abi/spectrans/parser/frontend.c | 569 |
1 files changed, 569 insertions, 0 deletions
diff --git a/usr/src/cmd/abi/spectrans/parser/frontend.c b/usr/src/cmd/abi/spectrans/parser/frontend.c new file mode 100644 index 0000000000..db33b99442 --- /dev/null +++ b/usr/src/cmd/abi/spectrans/parser/frontend.c @@ -0,0 +1,569 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdio.h> +#include <ctype.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <dlfcn.h> +#include <dirent.h> +#include <libgen.h> +#include <sys/param.h> +#include <errno.h> + +#include "parser.h" +#include "errlog.h" + +static char const *ARCH_I386 = "i386"; +static char const *ARCH_SPARC = "sparc"; +static char const *ARCH_SPARCV9 = "sparcv9"; +static char const *ARCH_IA64 = "ia64"; +static char const *ARCH_AMD64 = "amd64"; +static char const *ARCH_ALL = "all"; + +static int dofiles(const Translator_info *); +static int read_spec(const Translator_info *, char *); + +static int Curlineno; + +xlator_keyword_t *keywordlist; + +/* + * frontend entry point + * returns the number of errors encountered + */ +int +frontend(const Translator_info *T_info) +{ + int retval, i = 0, errors = 0; + + keywordlist = xlator_init(T_info); + if (keywordlist == NULL) { + errlog(ERROR, "Error: Unable to get keywordlist\n"); + return (1); + } + + if (T_info->ti_verbosity >= STATUS) { + errlog(STATUS, "interesting keywords:\n"); + while (keywordlist[i].key != NULL) { + errlog(STATUS, "\t%s\n", keywordlist[i].key); + ++i; + }; + } + + retval = xlator_startlib(T_info->ti_liblist); + switch (retval) { + case XLATOR_SKIP: + if (T_info->ti_verbosity >= STATUS) + errlog(STATUS, "Skipping %s\n", T_info->ti_liblist); + retval = 0; + break; + + case XLATOR_NONFATAL: + ++errors; + retval = 0; + break; + + case XLATOR_SUCCESS: + retval = dofiles(T_info); + errors += retval; + if ((retval = xlator_endlib()) != XLATOR_SUCCESS) + ++errors; + retval = 0; + break; + + default: + errlog(ERROR | FATAL, + "Error: Invalid return code from xlator_startlib()\n"); + exit(1); + } + + if ((retval = xlator_end()) != XLATOR_SUCCESS) + ++errors; + + return (errors); +} + +/* + * dofiles(const Translator_info *T_info); + * iterate through files specified in the command line and process + * them one by one + * requires spec files to have a ".spec" suffix + * returns the number of errors; + */ +static int +dofiles(const Translator_info *T_info) +{ + int nfiles, flen, findex, retval = 0, errors = 0; + + nfiles = T_info->ti_nfiles; + + for (findex = 0; findex < nfiles; ++findex) { + flen = strlen(filelist[findex]); + if ((flen <= 5) || + strcmp(&filelist[findex][flen-5], ".spec") != 0) { + errlog(ERROR, + "Error: File specified does not have the " + ".spec extension: %s\n", filelist[findex]); + ++errors; + continue; + }; + retval = read_spec(T_info, filelist[findex]); + errors += retval; + } + return (errors); +} + +/* + * read_spec - + * Given a filename, this function will reads the spec file to + * recognize keywords which it passes along with the corresponding + * value to the back-end translator to process. The following + * back-end interfaces are called: + * xlator_startfile + * xlator_start_if + * xlator_take_kvpair + * xlator_end_if + * xlator_endfile + */ +static int +read_spec(const Translator_info *T_info, char *spec_filename) +{ + FILE *spec_fp; + Meta_info meta_info; + char key[BUFSIZ], *value = NULL, *p = NULL; + char *buf2 = NULL; + int retval = 0, errors = 0, ki = 0; /* keyword indicator */ + int start_if_fail = 0, skip_if = 0; + int extends_err = 0; + + meta_info.mi_ext_cnt = 0; /* All info is non-extends */ + meta_info.mi_flags = 0; + + retval = xlator_startfile(spec_filename); + + switch (retval) { + case XLATOR_SKIP: + if (T_info->ti_verbosity >= WARNING) + errlog(WARNING, "Warning: Skipping %s\n", + spec_filename); + return (errors); + + case XLATOR_NONFATAL: + errlog(ERROR, "Error in xlator_startfile\n"); + ++errors; + return (errors); + + case XLATOR_SUCCESS: + break; + + default: + errlog(ERROR, + "Error: Invalid return code from xlator_startfile()\n"); + ++errors; + return (errors); + }; + + /* file processing */ + spec_fp = fopen(spec_filename, "r"); + if (spec_fp == NULL) { + errlog(ERROR, "Error: Unable to open spec file %s: %s\n", + spec_filename, strerror(errno)); + ++errors; + return (errors); + } + + (void) strncpy(meta_info.mi_filename, spec_filename, BUFSIZ); + meta_info.mi_line_number = 0; + Curlineno = meta_info.mi_line_number; + while (meta_info.mi_nlines = readline(&buf2, spec_fp)) { + meta_info.mi_line_number += meta_info.mi_nlines; + Curlineno = meta_info.mi_line_number; + if (!non_empty(buf2)) { + free(buf2); + buf2 = NULL; + continue; + } + p = realloc(value, sizeof (char)*(strlen(buf2)+1)); + if (p == NULL) { + errlog(ERROR | FATAL, + "Error: Unable to allocate memory for " + "value: %d\n", errno); + } + value = p; + split(buf2, key, value); + ki = interesting_keyword(keywordlist, key); + switch (ki) { + case XLATOR_KW_FUNC: /* Function keyword */ + case XLATOR_KW_DATA: /* Data keyword */ + meta_info.mi_extended = 0; + retval = xlator_start_if(meta_info, ki, value); + switch (retval) { + case XLATOR_FATAL: /* FATAL ERROR */ + if (T_info->ti_verbosity >= STATUS) { + errlog(STATUS, + "Error in xlator_start_if: "); + } + ++errors; + return (errors); + case XLATOR_NONFATAL: /* NON-FATAL ERROR */ + if (T_info->ti_verbosity >= STATUS) + errlog(STATUS, + "Error in xlator_start_if\n"); + ++errors; + start_if_fail = 1; + break; + case XLATOR_SUCCESS: /* OK */ + start_if_fail = 0; + extends_err = check4extends(spec_filename, + value, T_info->ti_archtoken, spec_fp); + switch (extends_err) { + case -1: /* Error */ + errlog(ERROR, "\"%s\", line %d: " + "Error occurred while " + "checking for extends clause\n", + spec_filename, Curlineno); + ++errors; + /*FALLTHRU*/ + case 0: /* No Extends */ + break; + case 1: /* Extends */ + meta_info.mi_extended = 1; + extends_err = do_extends(meta_info, + T_info, value); + if (extends_err) { + errors += extends_err; + } + break; + default: /* Programmer Error */ + errlog(ERROR | FATAL, + "Error: invalid return from " + "check4extends %d\n", extends_err); + } + break; + case XLATOR_SKIP: /* SKIP */ + if (T_info->ti_verbosity >= WARNING) + errlog(WARNING, "Warning: Skipping " + "interface %s\n", value); + skip_if = 1; + start_if_fail = 0; + break; + default: + /* Invalid Return */ + errlog(ERROR | FATAL, + "Error: Invalid return code " + "from xlator_start_if (): %d\n", retval); + } + break; + case XLATOR_KW_END: /* END keyword */ + if (start_if_fail == 0 && skip_if == 0) { + retval = xlator_end_if(meta_info, value); + if (retval) + ++errors; + } + skip_if = 0; + break; + case XLATOR_KW_NOTFOUND: + if (T_info->ti_verbosity >= TRACING) + errlog(TRACING, "uninteresting keyword: %s\n", + key); + break; + default: + if (skip_if == 0 && start_if_fail == 0) { + retval = xlator_take_kvpair(meta_info, + ki, value); + if (retval) { + if (T_info->ti_verbosity >= STATUS) + errlog(STATUS, "Error in " + "xlator_take_kvpair\n"); + ++errors; + } + } + } + free(buf2); + buf2 = NULL; + } + + if ((retval = xlator_endfile()) != XLATOR_SUCCESS) { + if (T_info->ti_verbosity >= STATUS) + errlog(STATUS, "Error in xlator_endfile\n"); + ++errors; + } + free(p); + (void) fclose(spec_fp); + return (errors); +} + +/* + * interesting_keyword(char **keywordlist, const char *key) { + * returns the token associated with key if key is found in keywordlist + * returns XLATOR_KW_NOTFOUND if key is NOT found in keywordlist + * "Function" and "End" are always interesting, return XLATOR_KW_FUNC + * and XLATOR_KW_DATA respectively; + * "End" is always interesting, return XLATOR_KW_END; + * + */ +int +interesting_keyword(xlator_keyword_t *keywordlist, const char *key) +{ + int i = 0; + + if (strcasecmp(key, "data") == 0) { + return (XLATOR_KW_DATA); + } + if (strcasecmp(key, "function") == 0) { + return (XLATOR_KW_FUNC); + } + + if (strcasecmp(key, "end") == 0) + return (XLATOR_KW_END); + + while (keywordlist[i].key != NULL) { + if (strcasecmp(keywordlist[i].key, key) == 0) + return (keywordlist[i].token); + ++i; + } + return (XLATOR_KW_NOTFOUND); +} + +/* + * line_to_buf(char *dest, const char *src) { + * appends src to dest, dynamically increasing the size of dest. + * replaces the trailing '\' continuation character with a space. + * + * if src is continuation of dest, dest != NULL, and + * the last character in dest before the newline must be a `\' + * if src is not continuation of dest, then dest must be NULL + */ +char * +line_to_buf(char *dest, const char *src) +{ + int slen = strlen(src); + int dlen; + + if (dest == NULL) { + /* We're being called for the first time */ + dest = malloc(sizeof (char) * (slen + 1)); + if (dest == NULL) { + errlog(ERROR | FATAL, + "Error: Unable to allocate memory for dest\n"); + } + (void) strcpy(dest, src); + return (dest); + } + + dlen = strlen(dest); + + dest = realloc(dest, (size_t)(sizeof (char) * (dlen+slen+1))); + if (dest == NULL) { + errlog(ERROR | FATAL, + "Error: Unable to allocate memory for dest\n"); + } + + if (dlen > 1) { + /* + * remove continuation character + * we replace the '\' from the previous line with a space + */ + if (dest[dlen-2] == '\\') { + dest[dlen-2] = ' '; + } + } + + /* join the two strings */ + (void) strcat(dest, src); + + return (dest); +} + +/* + * non_empty(const char *str) + * assumes str is non null + * checks if str is a non empty string + * returns 1 if string contains non whitespace + * returns 0 if string contains only whitespace + */ +int +non_empty(const char *str) +{ + while (*str != '\0') { + if (!isspace(*str)) + return (1); + ++str; + }; + return (0); +} + +/* + * split(const char *line, char *key, char *value); + * splits the line into keyword (key) and value pair + */ +void +split(const char *line, char *key, char *value) +{ + char *p; + + p = (char *)line; + + /* skip leading whitespace */ + while (isspace(*p)&& *p != '\0') + ++p; + + /* copy keyword from line into key */ + while (!isspace(*p) && *p != '\0') + *key++ = *p++; + + *key = '\0'; + + /* skip whitespace */ + while (isspace(*p) && *p != '\0') + p++; + + (void) strcpy(value, p); + +} + +/* + * check4extends(char *filename, char *value, int arch, FILE *fp) + * if no arch keyword is found or there is a MATCHING arch keyword + * returns 1 if value is of the form "data|function name extends" + * -1 for error + * 0 no other keyword after the function name + * else + * return 0 + * + * filename is used only for error reporting + */ +int +check4extends(const char *filename, const char *value, int arch, FILE *fp) +{ + char fun[BUFSIZ]; + char extends[BUFSIZ]; + int n; + + if (arch_match(fp, arch)) { + split(value, fun, extends); + n = strlen(extends); + if (extends[n-1] == '\n') + extends[n-1] = '\0'; + if (strncasecmp("extends", extends, 7) == 0) { + return (1); + } else { + if (*extends != '\0') { + errlog(ERROR, "\"%s\", line %d: Error: " + "Trailing garbage after function name\n", + filename, Curlineno); + return (-1); + } + } + } + return (0); +} + +/* + * remcomment (char *buf) + * replace comments with single whitespace + */ +/* XXX: There is currently no way to escape a comment character */ +void +remcomment(char const *buf) +{ + char *p; + p = strchr(buf, '#'); + if (p) { + *p = ' '; + *(p+1) = '\0'; + } +} + +/* + * arch_strtoi() + * + * input: string + * return: XLATOR_I386 if string == ARCH_I386 + * XLATOR_SPARC if string == ARCH_SPARC + * XLATOR_SPARCV9 if string == ARCH_SPARCV9 + * XLATOR_IA64 if string == ARCH_IA64 + * XLATOR_AMD64 if string == ARCH_AMD64 + * XLATOR_ALLARCH if string == ARCH_ALL + * 0 if outside the known set {i386, sparc, sparcv9, ia64, amd64}. + */ +int +arch_strtoi(const char *arch_str) +{ + if (arch_str != NULL) { + if (strcmp(arch_str, ARCH_I386) == 0) + return (XLATOR_I386); + else if (strcmp(arch_str, ARCH_SPARC) == 0) + return (XLATOR_SPARC); + else if (strcmp(arch_str, ARCH_SPARCV9) == 0) + return (XLATOR_SPARCV9); + else if (strcmp(arch_str, ARCH_IA64) == 0) + return (XLATOR_IA64); + else if (strcmp(arch_str, ARCH_AMD64) == 0) + return (XLATOR_AMD64); + else if (strcmp(arch_str, ARCH_ALL) == 0) + return (XLATOR_ALLARCH); + } else { + errlog(ERROR, "\"%s\", line %d: Error: " + "arch keyword with no value"); + } + return (0); +} + +int +readline(char **buffer, FILE *fp) +{ + int nlines = 0; + int len; + char buf[BUFSIZ]; + + if (fgets(buf, BUFSIZ, fp)) { + nlines++; + /* replace comments with single whitespace */ + remcomment(buf); + + /* get complete line */ + *buffer = line_to_buf(*buffer, buf); /* append buf to buffer */ + len = strlen(buf); + if (len > 1) { + /* handle continuation lines */ + while (buf[len-2] == '\\') { + if (!fgets(buf, BUFSIZ, fp)) { + *buffer = line_to_buf(*buffer, buf); + break; + } + nlines++; + len = strlen(buf); + *buffer = line_to_buf(*buffer, buf); + } + } /* end of 'get complete line' */ + } + return (nlines); +} |