#include "m4.h"
#include <limits.h>
#ifdef DEBUG_SYM
struct profile
{
int entry;
int comparisons;
int misses;
long long bytes;
};
static struct profile profiles[5];
static symbol_lookup current_mode;
static void
show_profile (void)
{
int i;
for (i = 0; i < 5; i++)
{
fprintf(stderr, "m4: lookup mode %d called %d times, %d compares, "
"%d misses, %lld bytes\n",
i, profiles[i].entry, profiles[i].comparisons,
profiles[i].misses, profiles[i].bytes);
}
}
static int
profile_strcmp (const char *s1, const char *s2)
{
int i = 1;
int result;
while (*s1 && *s1 == *s2)
{
s1++;
s2++;
i++;
}
result = (unsigned char) *s1 - (unsigned char) *s2;
profiles[current_mode].comparisons++;
if (result != 0)
profiles[current_mode].misses++;
profiles[current_mode].bytes += i;
return result;
}
# define strcmp profile_strcmp
#endif
symbol **symtab;
void
symtab_init (void)
{
size_t i;
symbol **s;
s = symtab = (symbol **) xmalloc (hash_table_size * sizeof (symbol *));
for (i = 0; i < hash_table_size; i++)
s[i] = NULL;
#ifdef DEBUG_SYM
{
int e = atexit(show_profile);
if (e != 0)
M4ERROR ((warning_status, 0,
"INTERNAL ERROR: unable to show symtab profile"));
}
#endif
}
static size_t
hash (const char *s)
{
register size_t val = 0;
register const char *ptr = s;
register char ch;
while ((ch = *ptr++) != '\0')
val = (val << 7) + (val >> (sizeof (val) * CHAR_BIT - 7)) + ch;
return val;
}
void
free_symbol (symbol *sym)
{
if (SYMBOL_PENDING_EXPANSIONS (sym) > 0)
SYMBOL_DELETED (sym) = TRUE;
else
{
free (SYMBOL_NAME (sym));
if (SYMBOL_TYPE (sym) == TOKEN_TEXT)
free (SYMBOL_TEXT (sym));
free (sym);
}
}
symbol *
lookup_symbol (const char *name, symbol_lookup mode)
{
size_t h;
int cmp = 1;
symbol *sym, *prev;
symbol **spp;
#if DEBUG_SYM
current_mode = mode;
profiles[mode].entry++;
#endif
h = hash (name);
sym = symtab[h % hash_table_size];
for (prev = NULL; sym != NULL; prev = sym, sym = sym->next)
{
cmp = strcmp (SYMBOL_NAME (sym), name);
if (cmp >= 0)
break;
}
if (mode == SYMBOL_LOOKUP)
return cmp == 0 ? sym : NULL;
spp = (prev != NULL) ? &prev->next : &symtab[h % hash_table_size];
switch (mode)
{
case SYMBOL_INSERT:
if (cmp == 0 && sym != NULL)
{
if (SYMBOL_PENDING_EXPANSIONS (sym) > 0)
{
symbol *old = sym;
SYMBOL_DELETED (old) = TRUE;
sym = (symbol *) xmalloc (sizeof (symbol));
SYMBOL_TYPE (sym) = TOKEN_VOID;
SYMBOL_TRACED (sym) = SYMBOL_TRACED (old);
SYMBOL_NAME (sym) = xstrdup (name);
SYMBOL_SHADOWED (sym) = FALSE;
SYMBOL_MACRO_ARGS (sym) = FALSE;
SYMBOL_BLIND_NO_ARGS (sym) = FALSE;
SYMBOL_DELETED (sym) = FALSE;
SYMBOL_PENDING_EXPANSIONS (sym) = 0;
SYMBOL_NEXT (sym) = SYMBOL_NEXT (old);
SYMBOL_NEXT (old) = NULL;
(*spp) = sym;
}
return sym;
}
case SYMBOL_PUSHDEF:
sym = (symbol *) xmalloc (sizeof (symbol));
SYMBOL_TYPE (sym) = TOKEN_VOID;
SYMBOL_TRACED (sym) = FALSE;
SYMBOL_NAME (sym) = xstrdup (name);
SYMBOL_SHADOWED (sym) = FALSE;
SYMBOL_MACRO_ARGS (sym) = FALSE;
SYMBOL_BLIND_NO_ARGS (sym) = FALSE;
SYMBOL_DELETED (sym) = FALSE;
SYMBOL_PENDING_EXPANSIONS (sym) = 0;
SYMBOL_NEXT (sym) = *spp;
(*spp) = sym;
if (mode == SYMBOL_PUSHDEF && cmp == 0)
{
SYMBOL_SHADOWED (SYMBOL_NEXT (sym)) = TRUE;
SYMBOL_TRACED (sym) = SYMBOL_TRACED (SYMBOL_NEXT (sym));
}
return sym;
case SYMBOL_DELETE:
case SYMBOL_POPDEF:
if (cmp != 0 || sym == NULL)
return NULL;
{
boolean traced = FALSE;
if (SYMBOL_NEXT (sym) != NULL
&& SYMBOL_SHADOWED (SYMBOL_NEXT (sym))
&& mode == SYMBOL_POPDEF)
{
SYMBOL_SHADOWED (SYMBOL_NEXT (sym)) = FALSE;
SYMBOL_TRACED (SYMBOL_NEXT (sym)) = SYMBOL_TRACED (sym);
}
else
traced = SYMBOL_TRACED (sym);
do
{
*spp = SYMBOL_NEXT (sym);
free_symbol (sym);
sym = *spp;
}
while (*spp != NULL && SYMBOL_SHADOWED (*spp)
&& mode == SYMBOL_DELETE);
if (traced)
{
sym = (symbol *) xmalloc (sizeof (symbol));
SYMBOL_TYPE (sym) = TOKEN_VOID;
SYMBOL_TRACED (sym) = TRUE;
SYMBOL_NAME (sym) = xstrdup (name);
SYMBOL_SHADOWED (sym) = FALSE;
SYMBOL_MACRO_ARGS (sym) = FALSE;
SYMBOL_BLIND_NO_ARGS (sym) = FALSE;
SYMBOL_DELETED (sym) = FALSE;
SYMBOL_PENDING_EXPANSIONS (sym) = 0;
SYMBOL_NEXT (sym) = *spp;
(*spp) = sym;
}
}
return NULL;
default:
M4ERROR ((warning_status, 0,
"INTERNAL ERROR: invalid mode to symbol_lookup ()"));
abort ();
}
}
void
hack_all_symbols (hack_symbol *func, const char *data)
{
size_t h;
symbol *sym;
symbol *next;
for (h = 0; h < hash_table_size; h++)
{
for (sym = symtab[h]; sym != NULL; sym = next)
{
next = SYMBOL_NEXT (sym);
(*func) (sym, data);
}
}
}
#ifdef DEBUG_SYM
static void symtab_print_list (int i);
static void M4_GNUC_UNUSED
symtab_debug (void)
{
token_data td;
const char *text;
symbol *s;
int delete;
static int i;
while (next_token (&td) == TOKEN_WORD)
{
text = TOKEN_DATA_TEXT (&td);
if (*text == '_')
{
delete = 1;
text++;
}
else
delete = 0;
s = lookup_symbol (text, SYMBOL_LOOKUP);
if (s == NULL)
printf ("Name `%s' is unknown\n", text);
if (delete)
(void) lookup_symbol (text, SYMBOL_DELETE);
else
(void) lookup_symbol (text, SYMBOL_INSERT);
}
symtab_print_list (i++);
}
static void
symtab_print_list (int i)
{
symbol *sym;
size_t h;
printf ("Symbol dump #%d:\n", i);
for (h = 0; h < hash_table_size; h++)
for (sym = symtab[h]; sym != NULL; sym = sym->next)
printf ("\tname %s, bucket %lu, addr %p, next %p, "
"flags%s%s%s, pending %d\n",
SYMBOL_NAME (sym),
(unsigned long int) h, sym, SYMBOL_NEXT (sym),
SYMBOL_TRACED (sym) ? " traced" : "",
SYMBOL_SHADOWED (sym) ? " shadowed" : "",
SYMBOL_DELETED (sym) ? " deleted" : "",
SYMBOL_PENDING_EXPANSIONS (sym));
}
#endif