/*
* OpenBIOS - free your system!
* ( FCode tokenizer )
*
* This program is part of a free implementation of the IEEE 1275-1994
* Standard for Boot (Initialization Configuration) Firmware.
*
* Copyright (C) 2001-2005 Stefan Reinauer, <stepan@openbios.org>
*
* This program 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; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA
*
*/
/* **************************************************************************
*
* General-purpose support functions for
* Threaded Interpretive Code (T. I. C.)-type vocabularies
*
* (C) Copyright 2005 IBM Corporation. All Rights Reserved.
* Module Author: David L. Paktor dlpaktor@us.ibm.com
*
**************************************************************************** */
/* **************************************************************************
*
* We are going to implement a strategy that takes better advantage
* of the concept of Threaded Interpretive Code (well, okay,
* it won't really be interpretive ... ) We will use it to
* implement a small (but expandable) subset of FORTH-like
* commands in Tokenizer-Escape mode, as well as a few other
* things, such as conditional-tokenization.
*
* The Threaded Interpretive Code Header data structure is described
* in detail in the accompanying ticvocab.h header-file.
*
* In most cases, the contents of a beginning portion of the vocabulary
* are known at compile-time, and later parts are added by the
* user at run-time. (The linked-list structure is needed to allow
* for that.) We can initialize the known start of the vocabulary
* easily, except for the link-pointers, as an array.
* We can either explicitly state an index for each entry's link-pointer
* to the previous entry (which can become a problem to maintain) or
* have a function to initialize the links dynamically, at run-time.
* I think I will (regretfully, resignedly) choose the latter.
*
* We will define a few general-purpose functions for dealing with
* T. I. C. -type vocabularies. Eventually, it might be a good
* idea to convert all the vocabularies to this structure...
*
**************************************************************************** */
/* **************************************************************************
*
*
* Revision History:
* Mon, 19 Dec 2005 by David L. Paktor
* Begin converting most, if not all, of the vocabularies to
* T. I. C. -type structure.
*
**************************************************************************** */
/* **************************************************************************
*
* Functions Exported:
* init_tic_vocab Initialize a TIC_HDR -type vocabulary
* add_tic_entry Add an entry to a TIC_HDR -type vocabulary
* lookup_tic_entry Look for a name in a TIC_HDR -type vocabulary
* handle_tic_vocab Perform a function in a TIC_HDR -type vocab
* exists_in_tic_vocab Confirm whether a given name exists in a
* TIC_HDR -type vocabulary
* create_tic_alias Duplicate the behavior of one name with
* another name. Return a "success" flag.
* reset_tic_vocab Reset a given TIC_HDR -type vocabulary to
* its "Built-In" position.
*
**************************************************************************** */
/* **************************************************************************
*
* Global Variables Exported
* tic_found The entry, in a TIC_HDR -type vocabulary,
* that has just been found and is being
* "handled". Needed for protection against
* recursion in a User-defined Macro (which
* should occur only rarely).
*
**************************************************************************** */
#include <stdlib.h>
#include <string.h>
#include "ticvocab.h"
#include "errhandler.h"
#include "tracesyms.h"
#include "scanner.h"
#include "devnode.h"
#include "vocabfuncts.h"
tic_hdr_t *tic_found;
/* **************************************************************************
*
* Function name: init_tic_vocab
* Synopsis: Dynamically initialize the link-pointers
* of the.given TIC_HDR -type vocabulary
*
* Inputs:
* Parameters:
* tic_vocab_tbl Pointer to the initial TIC_HDR vocab array
* max_indx Maximum Index of the initial array.
* tic_vocab_ptr Pointer to the vocab "tail"
*
* Outputs:
* Returned Value: None
* Global Variables:
* The link-fields of the initial TIC_HDR vocab array entries
* will be filled in.
* Supplied Pointers:
* *tic_vocab_ptr Points to the last element in the array
*
* Process Explanation:
* The value that tic_vocab_ptr has upon entry to the routine
* (which may point to the end of another array which is to
* precede this one in the voacbulary) gets entered into
* the link-pointer field of the first element of the array.
* For this reason, it is important that all TIC_HDR vocabulary
* pointers that will be passed to this routine have their
* initial values explicitly declared NULL.
* If the user has asked to Trace any built-in name, the support
* routine will set its tracing field and dispay a message.
*
**************************************************************************** */
void init_tic_vocab( tic_hdr_t *tic_vocab_tbl,
int max_indx,
tic_hdr_t **tic_vocab_ptr)
{
int indx;
for ( indx = 0 ; indx < max_indx ; indx++ )
{
tic_vocab_tbl[indx].next = *tic_vocab_ptr;
*tic_vocab_ptr = &tic_vocab_tbl[indx];
trace_builtin( &tic_vocab_tbl[indx]);
}
}
/* **************************************************************************
*
* Function name: make_tic_entry
* Synopsis: Construct a new entry to a TIC_HDR -type vocab-list
* but do not add it to a vocabulary
*
* Inputs:
* Parameters:
* tname Pointer to space containing the name of the entry
* tfunct Pointer to the routine the new entry will call
* tparam The "parameter field" value (may be a pointer)
* fw_defr FWord Token of the entry's Definer
* pfldsiz Size of "param field" (if a pointer to alloc'd mem)
* is_single TRUE if entry is a single-token FCode
* ign_fnc Pointer to "ignoring" routine for new entry
* trace_this TRUE if new entry is to be "Traced"
* tic_vocab Address of the variable that holds the latest
* pointer to the "tail" of the T.I.C.-type
* vocab-list to which we are adding.
*
* Outputs:
* Returned Value: Pointer to the new entry
* Memory Allocated:
* For the new entry.
* When Freed?
* When reset_tic_vocab() is applied to the same vocab-list.
*
* Error Detection:
* Failure to allocate memory is a Fatal Error.
*
* Process Explanation:
* The name pointer is presumed to already point to a stable,
* newly-allocated memory-space. If the parameter field is
* actually a pointer, it, too, is presumed to already have
* been allocated.
* Memory will be allocated for the entry itself and the given
* data will be placed into its fields.
*
* Extraneous Remarks:
* This is a retro-fit; it's a factor of the add_tic_entry()
* routine, whose functionality has been expanded to include
* issuing the Trace-Note and Duplication Warning messages.
* Having it separate allows it to be called (internally) by
* create_split_alias(), which has special requirements for
* its call to trace_creation()
*
**************************************************************************** */
static tic_hdr_t *make_tic_entry( char *tname,
void (*tfunct)(),
TIC_P_DEFLT_TYPE tparam,
fwtoken fw_defr,
int pfldsiz,
bool is_single,
void (*ign_fnc)(),
bool trace_this,
tic_hdr_t **tic_vocab )
{
tic_hdr_t *new_entry;
new_entry = safe_malloc(sizeof(tic_hdr_t), "adding tic_entry");
new_entry->name = tname;
new_entry->next = *tic_vocab;
new_entry->funct = tfunct;
new_entry->pfield.deflt_elem = tparam;
new_entry->fword_defr = fw_defr;
new_entry->is_token = is_single;
new_entry->ign_func = ign_fnc;
new_entry->pfld_size = pfldsiz;
new_entry->tracing = trace_this;
return( new_entry);
}
/* **************************************************************************
*
* Function name: add_tic_entry
* Synopsis: Add an entry to the given TIC_HDR -type vocabulary;
* issue the Creation Tracing and Duplicate-Name
* messages as applicable.
*
* Inputs:
* Parameters:
* tname Pointer to space containing entry's new name
* tfunct Pointer to the routine the new entry will call
* tparam The "parameter field" value (may be a pointer)
* fw_defr FWord Token of the entry's Definer
* pfldsiz Size of "param field" (if a ptr to alloc'd mem)
* is_single TRUE if entry is a single-token FCode
* ign_fnc Pointer to "ignoring" routine for new entry
* NOTE: No trace_this param here; it's only in make_tic_entry()
* tic_vocab Address of the variable that holds the latest
* pointer to the "tail" of the T.I.C.-type
* vocab-list to which we are adding.
* Global Variables:
* scope_is_global TRUE if "global" scope is in effect
* (passed to "Trace-Creation" message)
*
* Outputs:
* Returned Value: NONE
* Supplied Pointers:
* *tic_vocab Will point to new entry
* Printout:
* "Trace-Creation" message
*
* Error Detection:
* Warning on duplicate name (subject to command-line control)
*
* Process Explanation:
* The entry itself will be created by the make_tic_entry() routine.
* This routine will test whether the new name is to be Traced,
* and will pass that datum to the make_tic_entry() routine.
* If the new name is to be Traced, issue a Creation Tracing message.
* (We want it to appear first). Use the new entry.
* Because this routine will not be called for creating aliases, the
* second param to trace_creation() is NULL.
* Do the duplicate-name check _before_ linking the new entry in to
* the given vocabulary. We don't want the duplicate-name test
* to find the name in the new entry, only in pre-existing ones...
* Now we're ready to update the given pointer-to-the-tail-of-the-
* -vocabulary to point to the new entry.
*
**************************************************************************** */
void add_tic_entry( char *tname,
void (*tfunct)(),
TIC_P_DEFLT_TYPE tparam,
fwtoken fw_defr,
int pfldsiz,
bool is_single,
void (*ign_fnc)(),
tic_hdr_t **tic_vocab )
{
bool trace_this = is_on_trace_list( tname);
tic_hdr_t *new_entry = make_tic_entry( tname,
tfunct,
tparam,
fw_defr, pfldsiz,
is_single,
ign_fnc,
trace_this,
tic_vocab );
if ( trace_this )
{
trace_creation( new_entry, NULL, scope_is_global);
}
warn_if_duplicate( tname);
*tic_vocab = new_entry;
}
/* **************************************************************************
*
* Function name: lookup_tic_entry
* Synopsis: Look for a name in the given TIC_HDR -type vocabulary
*
* Inputs:
* Parameters:
* tname The "target" name for which to look
* tic_vocab Pointer to the T. I. C. -type vocabulary
* in which to search
*
* Outputs:
* Returned Value: Pointer to the relevant entry, or
* NULL if name not found.
*
* Extraneous Remarks:
* We don't set the global tic_found here because this routine
* is not always called when the found function is going to
* be executed; sometimes it is called for error-detection;
* for instance, duplicate-name checking...
*
**************************************************************************** */
tic_hdr_t *lookup_tic_entry( char *tname, tic_hdr_t *tic_vocab )
{
tic_hdr_t *curr ;
for (curr = tic_vocab ; curr != NULL ; curr=curr->next)
{
if ( strcasecmp(tname, curr->name) == 0 )
{
break;
}
}
return ( curr ) ;
}
/* **************************************************************************
*
* Function name: exists_in_tic_vocab
* Synopsis: Confirm whether the given name exists in the
* given TIC_HDR -type vocabulary
*
* Inputs:
* Parameters:
* tname The name for which to look
* tic_vocab Pointer to the T. I. C. -type vocabulary
*
* Outputs:
* Returned Value: TRUE if name is found,
*
**************************************************************************** */
bool exists_in_tic_vocab( char *tname, tic_hdr_t *tic_vocab )
{
tic_hdr_t *found ;
bool retval = FALSE;
found = lookup_tic_entry( tname, tic_vocab );
if ( found != NULL )
{
retval = TRUE;
}
return ( retval );
}
/* **************************************************************************
*
* Function name: create_split_alias
* Synopsis: Create an Alias in one TIC_HDR -type vocabulary
* for a word in (optionally) another vocabulary.
* Return a "success" flag.
* This is the work-horse for create_tic_alias()
*
* Associated FORTH word: ALIAS
*
* Inputs:
* Parameters:
* old_name Name of existing entry
* new_name New name for which to create an entry
* *src_vocab Pointer to the "tail" of the "Source"
* TIC_HDR -type vocab-list
* *dest_vocab Pointer to the "tail" of the "Destination"
* TIC_HDR -type vocab-list
*
* Outputs:
* Returned Value: TRUE if old_name found in "Source" vocab
* Supplied Pointers:
* *dest_vocab Will be updated to point to the new entry
* Memory Allocated:
* For the new entry, by the support routine.
* When Freed?
* When reset_tic_vocab() is applied to "Destination" vocab-list.
* Printout:
* "Trace-Creation" message
*
* Error Detection:
* Warning on duplicate name (subject to command-line control)
*
* Process Explanation:
* Both the "old" and "new" names are presumed to already point to
* stable, freshly allocated memory-spaces.
* Even if the "old" entry's pfld_size is not zero, meaning its
* param-field is a pointer to allocated memory, we still do
* not need to copy it into a freshly allocated memory-space,
* as long as we make the new alias-entry's pfld_size zero:
* the reference to the old space will work, and the old
* entry's param-field memory space will not be freed with
* the alias-entry but only with the "old" entry.
* We will do both the "Creation-Tracing" announcement and the
* Duplicate Name Warning here. "Tracing" happens either if
* the entry for the old name has its tracing flag set, or
* if the new name is on the trace-list. The "source" vocab
* and the "dest" vocab can only be different when the "old"
* name has defined Global scope. We will pass that along
* to the trace_creation() routine.
* We're doing the "Tracing" and Duplicate-Name functions because
* we're applying the "Tracing" message to the "old" name's
* entry. Because of this, we must call make_tic_entry() to
* bypass add_tic_entry(), which now does its own "Tracing"
* and Duplicate-Name functions on the new entry.
*
**************************************************************************** */
bool create_split_alias( char *new_name, char *old_name,
tic_hdr_t **src_vocab, tic_hdr_t **dest_vocab )
{
tic_hdr_t *found ;
bool retval = FALSE;
found = lookup_tic_entry( old_name, *src_vocab );
if ( found != NULL )
{
bool trace_it = found->tracing;
if ( ! trace_it )
{
trace_it = is_on_trace_list( new_name);
}
if ( trace_it )
{
bool old_is_global = BOOLVAL( src_vocab != dest_vocab );
trace_creation( found, new_name, old_is_global);
}
warn_if_duplicate( new_name);
*dest_vocab = make_tic_entry( new_name,
found->funct,
found->pfield.deflt_elem,
found->fword_defr, 0,
found->is_token,
found->ign_func,
trace_it,
dest_vocab );
retval = TRUE;
}
return ( retval );
}
/* **************************************************************************
*
* Function name: create_tic_alias
* Synopsis: Create an Alias in a TIC_HDR -type vocabulary
* Return a "success" flag.
*
* Associated FORTH word: ALIAS
*
* Inputs:
* Parameters:
* old_name Name of existing entry
* new_name New name for which to create an entry
* *tic_vocab Pointer to the "tail" of the
* T. I. C. -type vocab-list
*
* Outputs:
* Returned Value: TRUE if old_name found in given vocab
* Supplied Pointers:
* *tic_vocab Will be updated to point to the new entry
* Memory Allocated:
* For the new entry, by the support routine.
* When Freed?
* When reset_tic_vocab() is applied to the same vocab-list.
*
* Process Explanation:
* The given vocabulary is both the "Source" and the "Destination".
* Pass them both to create_split_alias.
**************************************************************************** */
bool create_tic_alias( char *new_name, char *old_name, tic_hdr_t **tic_vocab )
{
return ( create_split_alias( new_name, old_name, tic_vocab, tic_vocab ) );
}
/* **************************************************************************
*
* Function name: handle_tic_vocab
* Synopsis: Perform the function associated with the given name
* in the given TIC_HDR -type vocabulary
*
* Inputs:
* Parameters:
* tname The "target" name for which to look
* tic_vocab Pointer to the T. I. C. -type vocabulary
*
* Outputs:
* Returned Value: TRUE if the given name is valid in the given vocab
* Global Variables:
* tic_found Points to the TIC entry of the "target"
* name, if it was found; else, NULL.
* Global Behavior:
* Whatever the associated function does...
*
* Process Explanation:
* Find the name and execute its associated function.
* If the name is not in the given vocabulary, return
* an indication; leave it to the calling routine
* to decide how to proceed.
*
**************************************************************************** */
bool handle_tic_vocab( char *tname, tic_hdr_t *tic_vocab )
{
bool retval = FALSE;
tic_found = lookup_tic_entry( tname, tic_vocab );
if ( tic_found != NULL )
{
tic_found->funct( tic_found->pfield);
retval = TRUE;
}
return ( retval ) ;
}
/* **************************************************************************
*
* Function name: reset_tic_vocab
* Synopsis: Reset a given TIC_HDR -type vocabulary to
* its given "Built-In" position.
*
* Inputs:
* Parameters:
* *tic_vocab Pointer to the T. I. C.-type vocab-list
* reset_position Position to which to reset the list
*
* Outputs:
* Returned Value: NONE
* Supplied Pointers:
* *tic_vocab Reset to given "Built-In" position.
* Memory Freed
* All memory allocated by user-definitions will be freed
*
* Process Explanation:
* The "stable memory-spaces" to which the name and parameter
* field pointers point are presumed to have been acquired
* by allocation of memory, which is reasonable for entries
* created by the user as opposed to the built-in entries,
* which we are, in any case, not releasing.
* The parameter-field size field tells us whether we need to
* free() the parameter-field pointer.
*
**************************************************************************** */
void reset_tic_vocab( tic_hdr_t **tic_vocab, tic_hdr_t *reset_position )
{
tic_hdr_t *next_t;
next_t = *tic_vocab;
while ( next_t != reset_position )
{
next_t = (*tic_vocab)->next ;
free( (*tic_vocab)->name );
if ( (*tic_vocab)->pfld_size != 0 )
{
free( (*tic_vocab)->pfield.chr_ptr );
}
free( *tic_vocab );
*tic_vocab = next_t ;
}
}