/* ------------------------------------------------------------------------ @NAME : macros.c @DESCRIPTION: Front-end to the standard PCCTS symbol table code (sym.c) to abstract my "macro table". @GLOBALS : @CALLS : @CREATED : 1997/01/12, Greg Ward @MODIFIED : @VERSION : $Id: macros.c,v 1.19 1999/11/29 01:13:10 greg Rel $ @COPYRIGHT : Copyright (c) 1996-99 by Gregory P. Ward. All rights reserved. This file is part of the btparse library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. -------------------------------------------------------------------------- */ /*#include "bt_config.h"*/ #include #include #include "sym.h" #include "prototypes.h" #include "error.h" /*#include "my_dmalloc.h"*/ #include "bt_debug.h" /* * NUM_MACROS and STRING_SIZE define the size of the static data * structure that holds the macro table. The defaults are to allocate * 4096 bytes of string space that will be divided up amongst 547 * macros. This should be fine for most applications, but if you have a * big macro table you might need to change these and recompile (don't * forget to rebuild and reinstall Text::BibTeX if you're using it!). * You can set these as high as you like; just remember that a block of * STRING_SIZE bytes will be allocated and not freed as long as you're * using btparse. Also, NUM_MACROS defines the size of a hashtable, so * it should probably be a prime a bit greater than a power of 2 -- or * something like that. I'm not sure of the exact Knuthian * specification. */ #define NUM_MACROS 547 #define STRING_SIZE 4096 Sym *AllMacros = NULL; /* `scope' so we can get back list */ /* of all macros when done */ GEN_PRIVATE_ERRFUNC (macro_warning, (char * filename, int line, const char * fmt, ...), BTERR_CONTENT, filename, line, NULL, -1, fmt) /* ------------------------------------------------------------------------ @NAME : init_macros() @INPUT : @OUTPUT : @RETURNS : @DESCRIPTION: Initializes the symbol table used to store macro values. @GLOBALS : AllMacros @CALLS : zzs_init(), zzs_scope() (sym.c) @CALLERS : bt_initialize() (init.c) @CREATED : Jan 1997, GPW -------------------------------------------------------------------------- */ void init_macros (void) { zzs_init (NUM_MACROS, STRING_SIZE); zzs_scope (&AllMacros); } /* ------------------------------------------------------------------------ @NAME : done_macros() @INPUT : @OUTPUT : @RETURNS : @DESCRIPTION: Frees up all the macro values in the symbol table, and then frees up the symbol table itself. @GLOBALS : AllMacros @CALLS : zzs_rmscope(), zzs_done() @CALLERS : bt_cleanup() (init.c) @CREATED : Jan 1997, GPW -------------------------------------------------------------------------- */ void done_macros (void) { bt_delete_all_macros (); zzs_done (); } static void delete_macro_entry (Sym * sym) { Sym * cur; Sym * prev; /* * Yechh! All this mucking about with the scope list really * ought to be handled by the symbol table code. Must write * my own someday. */ /* Find this entry in the list of all macro table entries */ cur = AllMacros; prev = NULL; while (cur != NULL && cur != sym) { prev = cur; cur = cur->scope; } if (cur == NULL) /* uh-oh -- wasn't found! */ { internal_error ("macro table entry for \"%s\" not found in scope list", sym->symbol); } /* Now unlink from the "scope" list */ if (prev == NULL) /* it's the head of the list */ AllMacros = cur->scope; else prev->scope = cur->scope; /* Remove it from the macro hash table */ zzs_del (sym); /* And finally, free up the entry's text and the entry itself */ if (sym->text) free (sym->text); free (sym); } /* delete_macro_entry() */ /* ------------------------------------------------------------------------ @NAME : bt_add_macro_value() @INPUT : assignment - AST node representing "macro = value" options - string-processing options that were used to process this string after parsing @OUTPUT : @RETURNS : @DESCRIPTION: Adds a value to the symbol table used for macros. If the value was not already post-processed as a macro value (expand macros, paste substrings, but don't collapse whitespace), then this post-processing is done before adding the macro text to the table. If the macro is already defined, a warning is printed and the old text is overridden. @GLOBALS : @CALLS : bt_add_macro_text() bt_postprocess_field() @CALLERS : bt_postprocess_entry() (post_parse.c) @CREATED : Jan 1997, GPW -------------------------------------------------------------------------- */ void bt_add_macro_value (AST *assignment, ushort options) { AST * value; char * macro; char * text; boolean free_text; if (assignment == NULL || assignment->down == NULL) return; value = assignment->down; /* * If the options that were used to process the macro's expansion text * are anything other than BTO_MACRO, then we'll have to do it ourselves. */ if ((options & BTO_STRINGMASK) != BTO_MACRO) { text = bt_postprocess_field (assignment, BTO_MACRO, FALSE); free_text = TRUE; /* because it's alloc'd by */ /* bt_postprocess_field() */ } else { /* * First a sanity check to make sure that the presumed post-processing * had the desired effect. */ if (value->nodetype != BTAST_STRING || value->right != NULL) { internal_error ("add_macro: macro value was not " "correctly preprocessed"); } text = assignment->down->text; free_text = FALSE; } macro = assignment->text; bt_add_macro_text (macro, text, assignment->filename, assignment->line); if (free_text && text != NULL) free (text); } /* bt_add_macro_value() */ /* ------------------------------------------------------------------------ @NAME : bt_add_macro_text() @INPUT : macro - the name of the macro to define text - the macro text filename, line - where the macro is defined; pass NULL for filename if no file, 0 for line if no line number (just used to generate warning message) @OUTPUT : @RETURNS : @DESCRIPTION: Sets the text value for a macro. If the macro is already defined, a warning is printed and the old value is overridden. @GLOBALS : @CALLS : zzs_get(), zzs_newadd() @CALLERS : bt_add_macro_value() (exported from library) @CREATED : 1997/11/13, GPW (from code in bt_add_macro_value()) @MODIFIED : -------------------------------------------------------------------------- */ void bt_add_macro_text (char * macro, char * text, char * filename, int line) { Sym * sym; Sym * new_rec; #if DEBUG == 1 printf ("adding macro \"%s\" = \"%s\"\n", macro, text); #elif DEBUG >= 2 printf ("add_macro: macro = %p (%s)\n" " text = %p (%s)\n", macro, macro, text, text); #endif if ((sym = zzs_get (macro))) { macro_warning (filename, line, "overriding existing definition of macro \"%s\"", macro); delete_macro_entry (sym); } new_rec = zzs_newadd (macro); new_rec->text = (text != NULL) ? strdup (text) : NULL; DBG_ACTION (2, printf (" saved = %p (%s)\n", new_rec->text, new_rec->text);) } /* bt_add_macro_text() */ /* ------------------------------------------------------------------------ @NAME : bt_delete_macro() @INPUT : macro - name of macro to delete @DESCRIPTION: Deletes a macro from the macro table. @CALLS : zzs_get() @CALLERS : @CREATED : 1998/03/01, GPW @MODIFIED : -------------------------------------------------------------------------- */ void bt_delete_macro (char * macro) { Sym * sym; sym = zzs_get (macro); if (! sym) return; delete_macro_entry (sym); } /* ------------------------------------------------------------------------ @NAME : bt_delete_all_macros() @DESCRIPTION: Deletes all macros from the macro table. @CALLS : zzs_rmscore() @CALLERS : @CREATED : 1998/03/01, GPW @MODIFIED : -------------------------------------------------------------------------- */ void bt_delete_all_macros (void) { Sym *cur, *next; DBG_ACTION (2, printf ("bt_delete_all_macros():\n");) /* * Use the current `scope' (same one for all macros) to get access to * a linked list of all macros. Then traverse the list, free()'ing * both the text (which was strdup()'d in add_macro(), below) and * the records themselves (which are calloc()'d by zzs_new()). */ cur = zzs_rmscope (&AllMacros); while (cur != NULL) { DBG_ACTION (2, printf (" freeing macro \"%s\" (%p=\"%s\") at %p\n", cur->symbol, cur->text, cur->text, cur);) next = cur->scope; if (cur->text != NULL) free (cur->text); free (cur); cur = next; } } /* ------------------------------------------------------------------------ @NAME : bt_macro_length() @INPUT : macro - the macro name @OUTPUT : @RETURNS : length of the macro's text, or zero if the macro is undefined @DESCRIPTION: Returns length of a macro's text. @GLOBALS : @CALLS : zzs_get() @CALLERS : bt_postprocess_value() (exported from library) @CREATED : Jan 1997, GPW -------------------------------------------------------------------------- */ int bt_macro_length (char *macro) { Sym *sym; DBG_ACTION (2, printf ("bt_macro_length: looking up \"%s\"\n", macro);) sym = zzs_get (macro); if (sym) return strlen (sym->text); else return 0; } /* ------------------------------------------------------------------------ @NAME : bt_macro_text() @INPUT : macro - the macro name filename, line - where the macro was invoked; NULL for `filename' and zero for `line' if not applicable @OUTPUT : @RETURNS : The text of the macro, or NULL if it's undefined. @DESCRIPTION: Fetches a macros text; prints warning and returns NULL if macro is undefined. @CALLS : zzs_get() @CALLERS : bt_postprocess_value() @CREATED : Jan 1997, GPW -------------------------------------------------------------------------- */ char * bt_macro_text (char * macro, char * filename, int line) { Sym * sym; DBG_ACTION (2, printf ("bt_macro_text: looking up \"%s\"\n", macro);) sym = zzs_get (macro); if (!sym) { macro_warning (filename, line, "undefined macro \"%s\"", macro); return NULL; } return sym->text; }