root/usr/src/cmd/abi/spectrans/spec2trace/symtab.c
/*
 * 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 (c) 1997-1999 by Sun Microsystems, Inc.
 * All rights reserved.
 */

#include <stdio.h>
#include <string.h>
#include <limits.h>
#include <malloc.h>
#include "parser.h"
#include "trace.h"
#include "util.h"
#include "symtab.h"
#include "errlog.h"

/* Types */
enum kind_t { PRIMITIVE = 0, COMPOSITE, VARARG };

struct entry_t {
        char    *e_name;
        int     e_valid;
        int     e_line;
        char    *e_file;
        int     e_kind;         /* PRIMITIVE, COMPOSITE... */
        char    *e_type;        /* where kind == PRIMITIVE */
        /* base type, ie. char if e_type is char */
        char    *e_basetype;
        int     e_levels;       /* levels of indirection */
        char    *e_attribute;   /* kind == COMPOSITE or VARARG. */
        char    *e_assertion;   /* reserved for kind == VARARG. */
        char    *e_comment;     /* reserved for per-element comments. */
        int     e_pre_uses;
        int     e_post_uses;
};

typedef struct entry_head_t {
        int     used;
        int     n_entries;
        ENTRY   entry[1]; /* Actually entry[n_entries]. */
} EHEAD;

static struct symtab_t {
        ENTRY   *Function;
        EHEAD   *Args;
        EHEAD   *Varargs;
        EHEAD   *Globals;
        ENTRY   *Errval;

        /* Includes */
        table_t *Includes;

        /* Bindings */
        ENTRY   *Exception;

        /* Types */
        table_t *Print_Types;

        /* Error-message information. */
        int     Line;
        char    Filename[MAXLINE];

        /* Trace additions */
        char    Prototype[MAXLINE];
        char    Formals[MAXLINE];
        char    Actuals[MAXLINE];
        char    Cast[MAXLINE];
        int     Nonreturn;
        int     Skip;

        /* Adl additions */
        /* various assertions, one hopes */
} Symtab;

/* File Globals. */
static EHEAD *create_entry_table(int);
static EHEAD *add_entry_table(EHEAD *,
        char *, int, char *, int, char *, char *, int, char *, int, int);
static ENTRY *get_entry_table(EHEAD *, int);
static EHEAD *free_entry_table(EHEAD *);
static void clear_entries(EHEAD *, int, int);
static ENTRY *allocate_entry(ENTRY *, char *, int, char *, int,
    char *, char *, int, char *, int, int);
static ENTRY *set_entry(ENTRY *,
        char *, int, char *, int, char *, char *, int, char *, int, int);
static ENTRY *free_entry(ENTRY *);
static void symtab_clear_varargs(void);
static void symtab_clear_globals(void);
static void symtab_clear_print_types(void);
static void symtab_set_nonreturn(int);
static table_t *symtab_free_print_types(table_t *);

/*
 * symtab_new_function -- clear counts, variables for a new function.
 */
void
symtab_new_function(const int line, const char *file)
{
        errlog(BEGIN, "symtab_new_function() {");
        Symtab.Line = line;     /* Set, don't clear. */
        symtab_set_filename(file);

        symtab_clear_function();
        symtab_clear_varargs();
        symtab_clear_globals();
        symtab_clear_errval();
        symtab_clear_exception();
        symtab_clear_print_types();

        symtab_set_nonreturn(NO);
        symtab_set_skip(NO);
        errlog(END, "}");
}


/*
 * symtab_clear_function -- clear function-prototype-derived
 *      values. Called on each prototype line and at beginning
 *      of interface.
 */
void
symtab_clear_function(void)
{

        errlog(BEGIN, "symtab_clear_function() {");
        Symtab.Function = free_entry(Symtab.Function);
        Symtab.Args = free_entry_table(Symtab.Args);
        Symtab.Prototype[0] = '\0';
        Symtab.Formals[0] = '\0';
        Symtab.Actuals[0] = '\0';
        Symtab.Cast[0] = '\0';
        errlog(END, "}");
}


/*
 * symtab_clear_varargs -- called only at end
 */
static void
symtab_clear_varargs(void)
{

        errlog(BEGIN, "symtab_clear_varargs() {");
        Symtab.Varargs = free_entry_table(Symtab.Varargs);
        errlog(END, "}");
}

/*
 * symtab_clear_includes -- clear only at end of file (union++)
 */
void
symtab_clear_includes(void)
{

        errlog(BEGIN, "symtab_clear_includes() {");
        Symtab.Includes = free_string_table(Symtab.Includes);
        errlog(END, "}");
}

static void
symtab_clear_globals(void)
{

        errlog(BEGIN, "symtab_clear_globals() {");
        Symtab.Globals = free_entry_table(Symtab.Globals);
        errlog(END, "}");
}

void
symtab_clear_errval(void)
{

        errlog(BEGIN, "symtab_clear_errval() {");
        Symtab.Errval = free_entry(Symtab.Errval);
        errlog(END, "}");
}

void
symtab_clear_exception(void)
{

        errlog(BEGIN, "symtab_clear_exception() {");
        Symtab.Exception = free_entry(Symtab.Exception);
        errlog(END, "}");
}

static void
symtab_clear_print_types(void)
{

        errlog(BEGIN, "symtab_clear_print_types() {");
        Symtab.Print_Types = symtab_free_print_types(Symtab.Print_Types);
        errlog(END, "}");
}


/* Generated by m4 -- character string values */

void
symtab_set_prototype(char *p)
{

        errlog(BEGIN, "symtab_set_prototype(void) {");
        (void) strncpy(Symtab.Prototype, p, sizeof (Symtab.Prototype));
        Symtab.Prototype[sizeof (Symtab.Prototype)-1] = '\0';
        errlog(END, "}");
}

char *
symtab_get_prototype(void)
{
        errlog(BEGIN, "symtab_get_prototype() {"); errlog(END, "}");
        return (Symtab.Prototype);
}

void
symtab_set_formals(char *p)
{
        errlog(BEGIN, "symtab_set_formals() {");
        errlog(VERBOSE, "p = %s", p);
        (void) strncpy(Symtab.Formals, p, sizeof (Symtab.Formals));
        Symtab.Formals[sizeof (Symtab.Formals)-1] = '\0';
        errlog(END, "}");
}

char *
symtab_get_formals(void)
{
        errlog(BEGIN, "symtab_get_formals() {"); errlog(END, "}");
        return (Symtab.Formals);
}

void
symtab_set_actuals(char *p)
{
        errlog(BEGIN, "symtab_set_actuals() {"); errlog(END, "}");
        errlog(VERBOSE, "p = %s", p);
        (void) strncpy(Symtab.Actuals, p, sizeof (Symtab.Actuals));
        Symtab.Actuals[sizeof (Symtab.Actuals)-1] = '\0';
}

char *
symtab_get_actuals(void)
{
        errlog(BEGIN, "symtab_get_actuals() {"); errlog(END, "}");
        return (Symtab.Actuals);
}

void
symtab_set_cast(char *p)
{
        errlog(BEGIN, "symtab_set_cast() {"); errlog(END, "}");
        (void) strncpy(Symtab.Cast, p, sizeof (Symtab.Cast));
        Symtab.Cast[sizeof (Symtab.Cast)-1] = '\0';
}

char *
symtab_get_cast(void)
{
        errlog(BEGIN, "symtab_get_cast() {"); errlog(END, "}");
        return (Symtab.Cast);
}


void
symtab_set_filename(const char *p)
{
        errlog(BEGIN, "symtab_set_filename() {"); errlog(END, "}");
        (void) strncpy(Symtab.Filename, p, sizeof (Symtab.Filename));
        Symtab.Filename[sizeof (Symtab.Filename)-1] = '\0';
}

char *
symtab_get_filename(void)
{
        errlog(BEGIN, "symtab_get_filename() {"); errlog(END, "}");
        return (Symtab.Filename);
}


/* Generated by m4 -- int values */

static void
symtab_set_nonreturn(int val)
{
        errlog(BEGIN, "symtab_set_nonreturn() {"); errlog(END, "}");
        Symtab.Nonreturn = val;
}

int
symtab_get_nonreturn(void)
{
        errlog(BEGIN, "symtab_get_nonreturn() {"); errlog(END, "}");
        return (Symtab.Nonreturn);
}

void
symtab_set_line(int val)
{
        errlog(BEGIN, "symtab_set_line() {"); errlog(END, "}");
        Symtab.Line = val;
}

int
symtab_get_line(void)
{
        errlog(BEGIN, "symtab_get_line() {"); errlog(END, "}");
        return (Symtab.Line);
}


void
symtab_set_skip(int value)
{
        errlog(BEGIN, "symtab_set_skip() {"); errlog(END, "}");
        Symtab.Skip = value;
}

int
symtab_get_skip(void)
{
        errlog(BEGIN, "symtab_get_skip() {"); errlog(END, "}");
        return (Symtab.Skip);
}

/*
 * Manually written access functions for ENTRY * variables.
 */

void
symtab_set_function(char *name, int line, char *file,
    char *type, char *basetype, int levels)
{

        errlog(BEGIN, "symtab_set_function() {");
        Symtab.Function = allocate_entry(Symtab.Function,
            name, line, file, PRIMITIVE, type, basetype, levels, "", -1, -1);
        errlog(END, "}");
}

ENTRY *
symtab_get_function(void)
{
        errlog(BEGIN, "symtab_get_function() {"); errlog(END, "}");
        if (Symtab.Function == NULL)
                return (NULL);
        else
                return ((Symtab.Function->e_valid)? Symtab.Function: NULL);
}

void
symtab_set_exception(char *value, int line, char *file)
{

        errlog(BEGIN, "symtab_set_exception() {");
        Symtab.Exception = allocate_entry(Symtab.Exception,
                value, line, file, COMPOSITE, "", "", 0, "", -1, -1);
        errlog(END, "}");
}

ENTRY *
symtab_get_exception(void)
{

        errlog(BEGIN, "symtab_get_exception() {"); errlog(END, "}");
        if (Symtab.Exception == NULL)
                return (NULL);
        else
                return ((Symtab.Exception->e_valid)? Symtab.Exception: NULL);
}

void
symtab_set_errval(char *name, int line, char *file, char *type, char *basetype,
    int levels)
{

        errlog(BEGIN, "symtab_set_errval() {");
        Symtab.Errval = allocate_entry(Symtab.Errval,
            name, line, file, PRIMITIVE, type, basetype, levels,
            "", -1, -1);
        errlog(END, "}");
}

ENTRY *
symtab_get_errval(void)
{

        errlog(BEGIN, "symtab_get_errval() {"); errlog(END, "}");
        if (Symtab.Errval == NULL)
                return (NULL);
        else
                return ((Symtab.Errval->e_valid)? Symtab.Errval: NULL);
}

/*
 * Manually written  access function for tables of ENTRYs
 */
void
symtab_add_args(char *name, int line, char *file,
    char *type, char *basetype, int levels)
{

        errlog(BEGIN, "symtab_add_args() {");
        if (Symtab.Args == NULL) {
                Symtab.Args = create_entry_table(10);
        }
        Symtab.Args = add_entry_table(Symtab.Args,
            name, line, file, PRIMITIVE, type, basetype, levels, "", -1, -1);
        errlog(END, "}");
}

static int curr_arg;

ENTRY *
symtab_get_first_arg(void)
{

        errlog(BEGIN, "symtab_get_first_arg() {"); errlog(END, "}");
        curr_arg = 1;
        return (get_entry_table(Symtab.Args, 0));
}

ENTRY *
symtab_get_next_arg(void)
{

        errlog(BEGIN, "symtab_get_next_arg() {"); errlog(END, "}");
        return (get_entry_table(Symtab.Args, curr_arg++));
}

ENTRY *
symtab_get_last_arg(void)
{

        errlog(BEGIN, "symtab_get_last_arg() {"); errlog(END, "}");
        return (get_entry_table(Symtab.Args, Symtab.Args->used));
}

void
symtab_add_varargs(char *name, int line, char *file, char *type, char *print)
{

        errlog(BEGIN, "symtab_add_varargs() {");
        if (Symtab.Varargs == NULL) {
                Symtab.Varargs = create_entry_table(10);
        }
        Symtab.Varargs = add_entry_table(Symtab.Varargs,
                name, line, file, PRIMITIVE, type, print, 0, "", -1, -1);
        errlog(END, "}");
}

static int curr_vararg;

ENTRY *
symtab_get_first_vararg(void)
{

        errlog(BEGIN, "symtab_get_first_vararg() {"); errlog(END, "}");
        curr_vararg = 1;
        return (get_entry_table(Symtab.Varargs, 0));
}

ENTRY *
symtab_get_next_vararg(void)
{

        errlog(BEGIN, "symtab_get_next_vararg() {"); errlog(END, "}");
        return (get_entry_table(Symtab.Varargs, curr_vararg++));
}

void
symtab_add_globals(char *name, int line, char *file, char *type,
    char *basetype, int levels)
{

        errlog(BEGIN, "symtab_add_globals() {");
        if (Symtab.Globals == NULL) {
                Symtab.Globals = create_entry_table(10);
        }
        Symtab.Globals = add_entry_table(Symtab.Globals,
            name, line, file, PRIMITIVE, type, basetype, levels, "", -1, -1);
        errlog(END, "}");
}


static int curr_global;

ENTRY *
symtab_get_first_global(void)
{

        errlog(BEGIN, "symtab_get_first_global() {"); errlog(END, "}");
        curr_global = 1;
        return (get_entry_table(Symtab.Globals, 0));
}

ENTRY *
symtab_get_next_global(void)
{

        errlog(BEGIN, "symtab_get_next_global() {"); errlog(END, "}");
        return (get_entry_table(Symtab.Globals, curr_global++));
}

/*
 * manually written functions for accessing tables of strings
 */

/*
 * symtab_add_print_types -- add only non-void print types (due to
 *      parser errors in collect.c, yuck). Also note trick compare...
 *      TBD : common code in db, symtab needs to be
 *      pulled out, as they're getting out of sync.
 */
void
symtab_add_print_types(char *print_type, char *c_type)
{
        char    buffer[MAXLINE];

        errlog(BEGIN, "symtab_add_print_types() {");
#ifdef notdef
        if (strcmp(print_type, "void") == 0 || *print_type == NULL) {
                errlog(END, "}");
                return;
        }
#endif
        (void) snprintf(buffer, sizeof (buffer), "%s, %s", print_type, c_type);
        if (Symtab.Print_Types == NULL) {
        Symtab.Print_Types = create_string_table(50);
        }
        if (in_string_table(Symtab.Print_Types, print_type) == NO) {
                Symtab.Print_Types = add_string_table(Symtab.Print_Types,
                                        &buffer[0]);
        }
        errlog(END, "}");
}

static table_t *
symtab_free_print_types(table_t *t)
{
        errlog(BEGIN, "symtab_free_print_types() {"); errlog(END, "}");
        return (free_string_table(t));
}


static int curr_print_type;

char *
symtab_get_first_print_type(void)
{

        errlog(BEGIN, "symtab_get_first_print_type() {"); errlog(END, "}");
        curr_print_type = 1;
        return (get_string_table(Symtab.Print_Types, 0));
}

char *
symtab_get_next_print_type(void)
{

        errlog(BEGIN, "symtab_get_next_print_type() {"); errlog(END, "}");
        return (get_string_table(Symtab.Print_Types, curr_print_type++));
}

void
symtab_add_includes(char *value)
{

        errlog(BEGIN, "symtab_add_includes() {");
        if (Symtab.Includes == NULL) {
                Symtab.Includes = create_string_table(50);
        }
        if (in_string_table(Symtab.Includes, value) == NO) {
                Symtab.Includes = add_string_table(Symtab.Includes, value);
        }
        errlog(END, "}");
}

static int curr_include;

char *
symtab_get_first_include(void)
{

        errlog(BEGIN, "symtab_get_first_include() {"); errlog(END, "}");
        curr_include = 1;
        return (get_string_table(Symtab.Includes, 0));
}

char *
symtab_get_next_include(void)
{

        errlog(BEGIN, "symtab_get_next_include() {"); errlog(END, "}");
        return (get_string_table(Symtab.Includes, curr_include++));
}


void
symtab_sort_includes(void)
{
        errlog(BEGIN, "symtab_sort_includes() {");
        sort_string_table(Symtab.Includes);
        errlog(END, "}");
}

/*
 * ENTRYs  -- access functions to contents of an entry.
 */

char *
name_of(ENTRY *e)
{
        return (e->e_name);
}

int
validity_of(ENTRY *e)
{

        if (e == NULL)
                return (NO);
        else
                return (e->e_valid);
}

int
line_of(ENTRY *e)
{
        return (e->e_line);
}


char *
file_of(ENTRY *e)
{
        return (e->e_file);
}

/*
 * x_type_of -- return (type with an extension: an embedded %s where
 *      the name goes.
 */
char *
x_type_of(ENTRY *e)
{
        if (e != NULL && (e->e_kind == PRIMITIVE || e->e_kind == VARARG))
                return (e->e_type);
        else
                return (NULL);
}


/*
 * type_of -- return (just the type, with the %s removed. This is the common
 *      case, and its also the slowest... TBD.
 */
char *
type_of(ENTRY *e)
{
        static char buffer[MAXLINE];
        char    *p, *q;

        if (e != NULL && (e->e_kind == PRIMITIVE || e->e_kind == VARARG)) {
                p = e->e_type;
                q = &buffer[0];
                while (*p != '\0') {
                        if (*p == '%') {
                                p += 2;
                        } else {
                                *q++ = *p++;
                        }
                }
                *q = '\0';
                return (strtrim(&buffer[0]));
        }
        else
                return (NULL);
}

char *
basetype_of(ENTRY *e)
{
        if (e != NULL && (e->e_kind == PRIMITIVE || e->e_kind == VARARG))
                return (e->e_basetype);
        else
                return (NULL);
}

int
levels_of(ENTRY *e)
{
        if (e != NULL && (e->e_kind == PRIMITIVE || e->e_kind == VARARG))
                return (e->e_levels);
        else
                return (0);
}

char *
inverse_of(ENTRY *e)
{

        if (e != NULL && e->e_kind == COMPOSITE)
                return (e->e_attribute);
        else
                return (NULL);
}

char *
selector_of(ENTRY *e)
{

        if (e != NULL && e->e_kind == VARARG)
                return (e->e_attribute);
        else
                return (NULL);
}

int
preuses_of(ENTRY *e)
{

        if (e)
                return (e->e_pre_uses);
        else
                return (-1);
}

int
postuses_of(ENTRY *e)
{

        if (e)
                return (e->e_post_uses);
        else
                return (-1);
}


/*
 * allocate_entry -- make a parameter list into a complete
 *      ENTRY struct, allocated dynamically.
 */
        /* ARGSUSED -- lint bug */
static ENTRY *
allocate_entry(ENTRY *e,
    char *name, int line, char *file,
    int kind, char *type, char *basetype, int levels, char *attribute,
    int npre, int npost)
{

        errlog(BEGIN, "allocate_entry() {");
        if (e == NULL) {
                if ((e = (ENTRY *)calloc(1, sizeof (ENTRY))) == NULL) {
                        errlog(FATAL, "can't allocate space for an ENTRY");
                }
        }
        errlog(END, "}");
        return (set_entry(e, name, line, file, kind, type, basetype, levels,
                        attribute, npre, npost));
}

/*
 * set_entry -- set a passed-in entry, using
 *      passed parameters, to values suitable for a
 *      symtab entry
 */
static ENTRY *
set_entry(ENTRY *e,
    char *name, int line, char *file,
    int kind, char *type, char *basetype, int levels, char *attribute,
    int npre, int npost)
{

        errlog(BEGIN, "set_entry() {");
        if (e == NULL) {
                errlog(FATAL, "programmer error: passed a NULL ENTRY");
        }
        e->e_name = strset(e->e_name, name);
        e->e_valid = YES;
        e->e_line = line,
        e->e_file = strset(e->e_file, file);
        e->e_kind = kind;
        switch (kind) {
        case PRIMITIVE:
                e->e_type = strset(e->e_type, type);
                e->e_basetype = strset(e->e_basetype, basetype);
                e->e_levels = levels;
                break;
        case COMPOSITE:
                e->e_attribute = strset(e->e_attribute, attribute);
                break;
        case VARARG:
                e->e_attribute = strset(e->e_attribute, attribute);
                break;
        default:
                errlog(FATAL, "programmer error: impossible kind of ENTRY");
        }

        e->e_pre_uses = npre;
        e->e_post_uses = npost;
        errlog(END, "}");
        return (e);
}


/*
 * free_entry -- really just mark an entry as invalid
 */
static ENTRY *
free_entry(ENTRY *e)
{
        if (e != NULL)
                e->e_valid = NO;
        return (e);
}


/*
 * ENTRY tables.
 */
#define ENTRY_INCREMENT 10

static EHEAD *
create_entry_table(int n)
{
        EHEAD   *p;

        errlog(BEGIN, "create_entry_table() {");
        if ((p = (EHEAD *)calloc(1,
            sizeof (EHEAD)+(n*sizeof (ENTRY)))) == NULL) {
                errlog(FATAL, "can't allocate space for an ENTRY table");
        }
        p->used = -1;
        p->n_entries = n;
        errlog(END, "}");
        return (p);
}

static EHEAD *
add_entry_table(EHEAD *t, char *name, int line, char *file,
    int kind, char *type, char *basetype, int levels, char *attribute,
    int npre, int npost)
{
        EHEAD   *t2;

        errlog(BEGIN, "add_entry_table() {");
        if (t == NULL) {
                errlog(FATAL, "programmer error: tried to add to NULL EHEAD");
        }
        t->used++;
        if (t->used >= t->n_entries) {
                if ((t2 = (EHEAD *)realloc(t,
                        sizeof (EHEAD)+(sizeof (ENTRY)*
                                (t->n_entries+ENTRY_INCREMENT)))) == NULL) {
                        errlog(FATAL, "out of memory extending an EHEAD");
                }
                t = t2;
                clear_entries(t, t->n_entries, (t->n_entries+ENTRY_INCREMENT));
                t->n_entries += ENTRY_INCREMENT;
        }
        (void) set_entry(&t->entry[t->used],
            name, line, file, kind, type, basetype, levels,
            attribute, npre, npost);
        errlog(END, "}");
        return (t);
}

static ENTRY *
get_entry_table(EHEAD *t, int index)
{
        if (t == NULL)  {
                return (NULL);
        } else if (index > t->used) {
                return (NULL);
        } else {
                return (&(t->entry[index]));
        }
}

static EHEAD *
free_entry_table(EHEAD *t)
{
        if (t != NULL)
                t->used = -1;
        return (t);
}

static void
clear_entries(EHEAD *t, int start, int end)
{
        int     i;

        for (i = start; i < end; i++) {
                (void) memset(&t->entry[i], 0, sizeof (ENTRY));
        }
}