/* namespace.c -- Copyright 1995, 1996 Liam R. E. Quin. * All Rights Reserved. * This code is NOT in the public domain. * See the file COPYRIGHT for full details. */ /* $Id: namespace.c,v 1.10 2001/05/31 03:50:13 liam Exp $ */ #include "error.h" #include #include #include "globals.h" #include #ifdef HAVE_STRING_H # include #else # include #endif #ifdef HAVE_STDLIB_H # include #else # include #endif #include "emalloc.h" #include "range.h" #include "lqutil.h" #include "liblqtext.h" #include "namespace.h" /** There are a number of functions in here surrounded by #ifndef; ** These functions are also defined as macros in namespace.h; if ** you change one, change the other. **/ #ifndef LQU_NameRefIsValid /* * LQU_NameRefIsValid * Utilities/Name Space * *

Determines whether the given NameRef is a valid reference to * a name in the given NameSpace.

*

A NameRef is invalid if it is a NULL pointer, or if the Name * to which it refers has been deleted from the NameSpace.

* *

This function does not check to see whether a NameRef * has been corrupted; the given NameRef must either be NULL, or have * previously been a valid NameRef in the given NameSpace. * * Non-zero if the NameRef is valid, and zero otherwise. * * LQU_StringToNameRef * LQU_SetNameVariable * */ API int LQU_NameRefIsValid(NameSpace, NameRef) t_NameSpace *NameSpace; t_NameRef NameRef; { return (NameSpace && NameRef && *NameRef); } #endif /** There are a number of functions in here surrounded by #ifndef; ** These functions are also defined as macros in namespace.h; if ** you change one, change the other. **/ #ifndef LQU_GetNameFromNameRef /* * LQU_GetNameFromNameRef * Utilities/Name Space * *

Retrieves the name of the given NameRef as a string. * The NameRef must be valid. * * A pointer to the name; you should not free this string. * * LQU_NameRefIsValid * */ API char * LQU_GetNameFromNameRef(NameRef) t_NameRef NameRef; { return (*NameRef)->Name; } #endif /** There are a number of functions in here surrounded by #ifndef; ** These functions are also defined as macros in namespace.h; if ** you change one, change the other. **/ #ifndef LQU_GetTypeFromNameRef /* * LQU_GetTypeFromNameRef * Utilities/Name Space * *

Returns the type of the variable associated with the * given NameRef. *

The types are defined in as an enumerated type.

* * The NameRef must be valid. *
*/ API t_NameType LQU_GetTypeFromNameRef(NameRef) t_NameRef NameRef; { return (*NameRef)->Type; } #endif /** There are a number of functions in here surrounded by #ifndef; ** These functions are also defined as macros in namespace.h; if ** you change one, change the other. **/ #ifndef LQU_GetVariableFromNameRef /* * LQU_GetVariableFromNameRef * Utilities/Name Space * *

Returns a pointer to the variable associated with a * given NameRef.

*

You have to cast the result of this function, perhaps using * LQU_GetTypeFromNameRef and a switch, since C lacks runtime type * information.

* * The NameRef must be valid. *
*/ API void * LQU_GetVariableFromNameRef(NameRef) { return (*NameRef)->Variable; } #endif /** There are a number of functions in here surrounded by #ifndef; ** These functions are also defined as macros in namespace.h; if ** you change one, change the other. **/ #ifndef LQU_GetDescriptionFromNameRef /* * LQU_GetDescriptionFromNameRef * Utilities/Name Space * *

Returns the textual description of the variable associated * with a given NameRef, or NULL if there is none.

*

Where the description is available, it is intended to be * presented to the user, for example in error messages, and not * to be parsed.

* * The NameRef must be valid. *
*/ API char * LQU_GetDescriptionFromNameRef(NameRef) t_NameRef NameRef; { return (*NameRef)->Description; } #endif /** There are a number of functions in here surrounded by #ifndef; ** These functions are also defined as macros in namespace.h; if ** you change one, change the other. **/ #ifndef LQU_NameRefVariableAllocatedByLibrary /* * LQU_NameRefVariableAllocatedByLibrary * Utilities/Name Space * *

Determines whether the variable associated with the given * NameRef was allocated automatically (and is anonymous), or * whether it was allocated externally and supplied to on of * the Name Space creation functions.

* * The NameRef must be valid. *
*/ API int LQU_NameRefVariableAllocatedByLibrary(NameRef) t_NameRef NameRef; { return ((*NameRef)->VariableAllocatedByLibrary == 1); } #endif /** There are a number of functions in here surrounded by #ifndef; ** These functions are also defined as macros in namespace.h; if ** you change one, change the other. **/ #ifndef LQU_SetNameRefVariableAllocatedByLibrary /* * LQU_SetNameRefVariableAllocatedByLibrary * Utilities/Name Space * *

Stores within the NameRef the fact that the variable * associated with it is a piece of dynamically allocated memory * internal to the Name Space library.

* *

The NameRef must be valid.

*

This function should not be used by client software.

*
*/ API int LQU_SetNameRefVariableAllocatedByLibrary(NameRef) t_NameRef NameRef; { return ((*NameRef)->VariableAllocatedByLibrary = 1); } #endif /** There are a number of functions in here surrounded by #ifndef; ** These functions are also defined as macros in namespace.h; if ** you change one, change the other. **/ #ifndef LQU_NameRefVariablePointsToFunction /* * LQU_NameRefVariablePointsToFunction * Utilities/Name Space * *

Returns non-zero if the variable associated with the given * NameRef has previously been marked a pointer to a function, * for example with LQU_SetNameRefVariablePointsToFunction.

* *

The NameRef must be valid.

*
*/ API int LQU_NameRefVariablePointsToFunction(NameRef) t_NameRef NameRef; { return ((*NameRef)->VariablePointsToFunction == 1); } #endif /** There are a number of functions in here surrounded by #ifndef; ** These functions are also defined as macros in namespace.h; if ** you change one, change the other. **/ #ifndef LQU_SetNameRefVariablePointsToFunction /* * LQU_SetNameRefVariableAllocatedByLibrary * Utilities/Name Space * *

Stores within the NameRef the fact that the variable * associated with it is a pointer to a function.

* *

The NameRef must be valid.

*
*/ API void LQU_SetNameRefVariablePointsToFunction(NameRef) t_NameRef NameRef; { (*NameRef)->VariablePointsToFunction = 1; } #endif /** There are a number of functions in here surrounded by #ifndef; ** These functions are also defined as macros in namespace.h; if ** you change one, change the other. **/ #ifndef LQU_NameRefFunctionTakesArgument /* * LQU_NameRefFunctionTakesArgument * Utilities/Name Space * *

Returns non-zero if the function pointer associated with * the given Name Ref is a pointer to a function that takes an * argument. Before calling this function (or macro), you * should check that LQU_NameRefVariablePointsToFunction * returns non-zero for the given NameRef.

* *

The NameRef must be valid.

*
*/ API int LQU_NameRefFunctionTakesArgument(NameRef) t_NameRef NameRef; { return ((*NameRef)->FunctionTakesArgument == 1); } #endif /** There are a number of functions in here surrounded by #ifndef; ** These functions are also defined as macros in namespace.h; if ** you change one, change the other. **/ #ifndef LQU_SetNameRefFunctionTakesArgument /* * LQU_SetNameRefFunctionTakesArgument * Utilities/Name Space * *

Stores within the NameRef the fact that the variable * associated with it is a pointer to a function that takes * an argument. The NameRef must previously have been marked * as being associated with a function pointer using * LQU_SetNameRefVariablePointsToFunction.

* *

The NameRef must be valid.

*
*/ API void LQU_SetNameRefFunctionTakesArgument(NameRef) t_NameRef NameRef; { (*NameRef)->FunctionTakesArgument = 1; } #endif /** There are a number of functions in here surrounded by #ifndef; ** These functions are also defined as macros in namespace.h; if ** you change one, change the other. **/ #ifndef LQU_GetDescriptionFromNameSpace /* * LQU_GetDescriptionFromNameSpace * Utilities/Name Space * *

Returns a pointer to the textual description of the * given NameSpace. The text is in private memory, and so * should not be freed by the caller.

*
*/ API char * LQU_GetDescriptionFromNameSpace(NameSpace) t_NameSpace *NameSpace; { return NameSpace->Description; } #endif /* * LQU_NameSpaceTableToNameSpace * Utilities/Name Space * *

Converts a Name Space Table into a Name Space. This is useful * if you have a statically initialised Name Space Table, for example.

*

The new Name Space has the given Name as its name. The string * is pointed to but not copied, and should therefore be allocated by * the caller if it is not static data. The entries in the Name * Space Table are copied, but their Name fields are simply pointed to.

* * the newly created Name Space if successful. Currently, a * failure is always fatal. * * LQU_StringToNameRef * LQU_SetNameVariable *
*/ API t_NameSpace * LQU_NameSpaceTableToNameSpace(Name, theTable) char *Name; t_NameSpaceTable theTable; { t_NameSpace *Result; int tableEntry; int newIndex = 0; /* index into the NameSpace's Names array */ t_NameSpace *Names; Names = Result = (t_NameSpace *) emalloc(Name, sizeof(t_NameSpace)); Result->Name = Name; Result->MoreNames = (t_NameSpace *) 0; Result->NamesAreCaseSensitive = 1; Result->FollowNestedReferences = 1; Result->Names[0] = 0; for ( tableEntry = 0; theTable[tableEntry].Name != (char *) 0; tableEntry++ ) { t_Name *newName = (t_Name *) emalloc("namespace entry",sizeof(t_Name)); newName->Name = theTable[tableEntry].Name; newName->Type = theTable[tableEntry].Type; newName->VariableAllocatedByLibrary = 0; newName->VariablePointsToFunction = 0; newName->FunctionTakesArgument = 0; newName->Argument = theTable[tableEntry].Argument; newName->Variable = (char *) theTable[tableEntry].Variable; if (newIndex == 30) { Names->MoreNames = (t_NameSpace *) emalloc(Name, sizeof(t_NameSpace)); Names->Name = Name; Names->MoreNames = (t_NameSpace *) 0; newIndex = 0; } Names->Names[newIndex] = newName; newName->MyRef = &Names->Names[newIndex]; Names->Names[newIndex + 1] = 0; /* operations that need a NameRef: */ if (theTable[tableEntry].Flags & LQU_NS_Function) { LQU_SetNameRefVariablePointsToFunction(newName->MyRef); if (theTable[tableEntry].Flags & LQU_NS_FunctionOneArgument) { LQU_SetNameRefFunctionTakesArgument(newName->MyRef); } } ++newIndex; } return Result; } /* * LQU_SetNameVariable * Utilities/Name Space * * Associates a variable with a Name that you have retrieved from * a Name Space. You should pass a pointer to the variable, which * must remain in scope for as long as the Name can be accessed. * * the given Name Reference. * * LQU_SetNameTypeAndVariable * LQU_StringToNameRef * */ API t_NameRef LQU_SetNameVariable(NameRef, Variable) t_NameRef NameRef; void *Variable; { if (!NameRef || !*NameRef) { Error(E_FATAL|E_INTERNAL, "LQU_SetNameVariable passed invalid NameRef" ); } if (LQU_NameRefVariableAllocatedByLibrary(NameRef)) { efree((*NameRef)->Variable); (*NameRef)->Variable = 0; } (*NameRef)->Variable = Variable; return NameRef; } /* * LQU_SetNameTypeAndVariable * Utilities/Name Space * * Associates the given NameRef with the given Variable, first * changing the remembered type of the NameRef. * You should pass a pointer to the variable you want to use. * The variable itself should be static if there is any chance of * the Name within the NameSpace being used after the variable has * gone out of scope. * * The given NameRef, possibly changed, is returned. * * static int MyToes = 10; * LQU_SetNameTypeAndVariable(NameRef, LQU_NameType_Integer, &MyToes); * * * LQU_SetNameVariable * */ API t_NameRef LQU_SetNameTypeAndVariable(theNameRef, theNameType, theVariable) t_NameRef theNameRef; t_NameType theNameType; void *theVariable; { if (LQU_NameRefVariableAllocatedByLibrary(theNameRef)) { efree((*theNameRef)->Variable); (*theNameRef)->Variable = 0; } (*theNameRef)->Type = theNameType; return LQU_SetNameVariable(theNameRef, theVariable); } /* * LQU_SetNameValue * Utilities/Name Space * * Sets the value of the variable associated with the given NameRef. * * the given NameRef. * * LQU_SetNameVariable * LQU_SetNameTypeAndVariable * */ API t_NameRef LQU_SetNameValue(NameRef, Value) t_NameRef NameRef; void *Value; { *(char **)((*NameRef)->Variable) = (char *)Value; return NameRef; } typedef char *(* t_StringPointerFunction)(); API void * LQU_GetValueFromNameRef(theNameRef) t_NameRef theNameRef; { if (LQU_NameRefVariablePointsToFunction(theNameRef)) { t_StringPointerFunction theFunction; theFunction = (t_StringPointerFunction) LQU_GetVariableFromNameRef(theNameRef); if (LQU_NameRefFunctionTakesArgument(theNameRef)) { return (*theFunction)( (char *) (*theNameRef)->Argument ); } else { return (*theFunction)(); } } return (*(char **)LQU_GetVariableFromNameRef(theNameRef)); } /* functions to iterate over name spaces */ /* * LQU_FirstNameRef * Utilities/Name Space * * Used in conjunction with LQU_NextNameRef to iterate over all * of the Names in a Name Space. * * A reference to the first Name in the given Name Space, if there are * any. Use LQU_NameRefIsValid() to determine if the returned reference * is valid; if not, LQU_NameRefIsError will determine if there was * an error, and LQU_GetNameError will handle the error using Error(). * * t_NameRef NameRef; * * for ( * NameRef = LQU_FirstNameRef(NameSpace); * LQU_NameRefIsValid(NameSpace, NameRef); * NameRef = LQU_NextNameRef(NameSpace, NameRef) * ) { * now use the Name Reference: * printf("%s\n", LQU_GetNameFromNameRef(NameRef)); * } * * * LQU_GetNameFromNameRef * LQU_GetTypeFromNameRef * LQU_NameRefIsValid * LQU_NameRefIsError * */ API t_NameRef LQU_FirstNameRef(NameSpace) t_NameSpace *NameSpace; { if (!NameSpace) { return 0; } if (!NameSpace->Names[0]->Name) { return 0; } return NameSpace->Names[0]->MyRef; } /* * LQU_NextNameRef * Utilities/Name Space * * Used in conjunction with LQU_NextNameRef to iterate over all * of the Names in a Name Space. * * A reference to the first Name in the given Name Space, if there are * any. Use LQU_NameRefIsValid() to determine if the returned reference * is valid; if not, LQU_NameRefIsError will determine if there was * an error, and LQU_GetNameError will handle the error using Error(). * * LQU_GetNameFromNameRef * LQU_GetTypeFromNameRef * LQU_NameRefIsValid * LQU_NameRefIsError * */ API t_NameRef LQU_NextNameRef(NameSpace, NameRef) t_NameSpace *NameSpace; t_NameRef NameRef; { int thisEntry = -1; /* TODO: add propper error handling */ if (!NameSpace) { Error(E_FATAL|E_INTERNAL, "LQU_NextNameRef(0,...)"); return 0; } if (!NameSpace->Names[0]) { Error(E_FATAL|E_INTERNAL, "LQU_NextNameRef on empty namespace"); return 0; } if (!LQU_NameRefIsValid(NameSpace, NameRef)) { Error(E_FATAL|E_INTERNAL, "LQU_NextNameRef on invalid NameRef 0x%x", NameRef ); return 0; } while (NameRef - NameSpace->Names >= 30) { if (NameSpace->MoreNames) { NameSpace = NameSpace->MoreNames; } else { Error(E_FATAL|E_INTERNAL, "LQU_NextNameRef: NameRef not found in NameSpace!" ); return 0; /* didn't find it!? TODO: ERROR */ } } thisEntry = NameRef - NameSpace->Names; if (thisEntry >= 29) { NameSpace = NameSpace->MoreNames; if (NameSpace->MoreNames) { NameSpace = NameSpace->MoreNames; if (NameSpace->Names[0] && NameSpace->Names[0]->Name) { return NameSpace->Names[0]->MyRef; } else { return 0; /* no more names */ } } else { return 0; /* got to the end */ } } return &NameSpace->Names[thisEntry + 1]; } /* * LQU_StringToNameRef * Utilities/Name Space * *

Treats the given `theName' string as a Name, and looks this * up in the given NameSpace. * If the NameSpace allows nested NameSpace references, the Name is * allowed to have any number of prefixes consisting of a name followed * by a dot; the name must be the name of a NameSpace in the NameSpace * being searched, and in this case the search proceeds using the * newly found NameSpace on the rest of the string.

* * the NameRef, or NULL * * If given the string `Children.Boys.Simon', and a NameSpace called * `People', LQU_StringToNameRef will search `People' for a NameSpace * called Children, and if that should succeed, it will then search * `Children' for a NameSpace called `Boys'. * If this last search succeeds, the namespace `Boys' is searched * for `Simon', and the result, either the NameRef called `Simon' or * NULL for failure, is returned. * * * LQU_SetNameTypeAndVariable * LQU_GetVariableFromNameRef *
*/ API t_NameRef LQU_StringToNameRef(theNameSpace, theName) t_NameSpace *theNameSpace; char *theName; { t_NameRef theNameRef; for ( theNameRef = LQU_FirstNameRef(theNameSpace); LQU_NameRefIsValid(theNameSpace, theNameRef); theNameRef = LQU_NextNameRef(theNameSpace, theNameRef) ) { register char *p, *q; for (p = LQU_GetNameFromNameRef(theNameRef), q = theName; *q; q++) { if (*p == *q || ( theNameSpace->NamesAreCaseSensitive == 0 && ( isascii(*p) && ( (isupper(*p) && tolower(*p) == *q) || (islower(*p) && toupper(*p) == *q) ) ) )) { p++; } else if (!*p && *q == '.' && q[1]) { if (theNameSpace->FollowNestedReferences && LQU_GetTypeFromNameRef(theNameRef) == LQU_NameType_NameSpace ) { return LQU_StringToNameRef(theNameSpace, &q[1]); } else { /* Don't give up, because even if the variable name * was Boys.Simon * and we've just rejected "Boys" because it isn't a * NameSpace, there might actuially be a variable * called Boys.Simon! * Of course, randomly getting either is obnoxious, * so this is a bug whatever I do. * FIX: don't allow . in a variable name. * TODO NOTDOE FIXME */ break; } } else { /* give up on this name */ break; } } /* for each character in the variable name */ if (!*q && !*p) { return theNameRef; } } /* for each variable name */ return (t_NameRef) 0; } /* Input and output of name spaces */ /* * LQU_NameRefToString * Utilities/Name Space * * Converts the value pointed to by the variable associated * with the given Name Reference into a string. * * a dynamically allocated string, which the caller must free. * * LQU_NameRefValueToString * LQU_SetNameTypeAndVariable * LQU_GetNameFromNameRef * LQU_GetVariableFromNameRef * */ API char * LQU_NameRefToString(NameRef) t_NameRef NameRef; { char *NoValue = "(value unset)"; char *format = "NameRef: Name = \"%s\" Type = %s, Value = %s"; char *value; char *name; char *type; char *Result; int length; if (!NameRef || !*NameRef) { Error(E_FATAL|E_INTERNAL, "LQU_NameRefToString passed invalid nameref 0x%x", NameRef ); } name = LQU_GetNameFromNameRef(NameRef); if (!name || !*name) { Error(E_FATAL|E_INTERNAL, "LQU_NameRefToString passed NameRef with %s name", name ? "empty" : "null" ); } value = LQU_NameRefValueToString(NameRef); if (!value) { value = NoValue; } type = LQU_NameRefTypeToString(LQU_GetTypeFromNameRef(NameRef)); switch (LQU_GetTypeFromNameRef(NameRef)) { case LQU_NameType_NameRef: case LQU_NameType_NameSpace: format = "NameRef: Name = \"%s\" Type = %s, Value = [\n %s\n]"; default: break; } length = strlen(value) + strlen(type) + strlen(name) + strlen(format) + 1; Result = emalloc("NameRefToString", length); (void) sprintf(Result, format, name, type, value); if (value != NoValue) { efree(NoValue); } return Result; } /* * LQU_NameRefValueToString * Utilities/Name Space * * Converts the value pointed to by the variable associated * with the given Name Reference into a string. * * a dynamically allocated string, which the caller must free. * * LQU_SetNameTypeAndVariable * LQU_GetNameFromNameRef * LQU_GetVariableFromNameRef * */ API char * LQU_NameRefValueToString(NameRef) t_NameRef NameRef; { char *Result; char tmp[25]; /* enough to hold a 64 bit -ve integer. */ switch(LQU_GetTypeFromNameRef(NameRef)) { case LQU_NameType_Long: (void) sprintf(tmp, "%lu", (unsigned long) LQU_GetValueFromNameRef(NameRef) ); Result = emalloc(LQU_GetNameFromNameRef(NameRef), strlen(tmp) + 1); (void) strcpy(Result, tmp); return Result; case LQU_NameType_Integer: case LQU_NameType_Character: (void) sprintf(tmp, "%d", (int) LQU_GetValueFromNameRef(NameRef)); Result = emalloc(LQU_GetNameFromNameRef(NameRef), strlen(tmp) + 1); (void) strcpy(Result, tmp); return Result; case LQU_NameType_String: { char *s = (char *) LQU_GetValueFromNameRef(NameRef); if (!s) { s = "(null)"; } Result = emalloc(LQU_GetNameFromNameRef(NameRef), strlen(s) + 1); (void) strcpy(Result, s); return Result; } case LQU_NameType_NameRef: { t_NameRef NR2 = (t_NameRef) LQU_GetValueFromNameRef(NameRef); if (NR2) { if (*NR2 == *NameRef) { /* the same pointer... */ return "(recursive reference to NameRef)"; } return LQU_NameRefToString(NR2); } #define NOVALUE "(no value set)" Result = emalloc(LQU_GetNameFromNameRef(NameRef), sizeof(NOVALUE)); (void) strcpy(Result, NOVALUE); return Result; } break; case LQU_NameType_NameSpace: { #define NAMESPACE "(compound object: NameSpace)" Result = emalloc(LQU_GetNameFromNameRef(NameRef),sizeof(NAMESPACE)); (void) strcpy(Result, NAMESPACE); return Result; } break; } Error(E_FATAL|E_INTERNAL, "case %d not handled in switch", LQU_GetTypeFromNameRef(NameRef) ); return (char *) 0; } /* silly utility things */ /* * LQU_NameTypeToString * Utilities/Name Space * * Returns a string representation of the given NameType. * * A statically allocated string, which need not be freed. * * LQU_SetNameTypeAndVariable * LQU_GetNameFromNameRef * */ API char * LQU_NameRefTypeToString(NameType) t_NameType NameType; { switch(NameType) { case LQU_NameType_Integer: return "Integer"; case LQU_NameType_Long: return "Unsigned Long"; case LQU_NameType_Character: return "Character"; break; case LQU_NameType_String: return "String"; break; case LQU_NameType_NameRef: return "NameRef"; break; case LQU_NameType_NameSpace: return "NameSpace"; break; } Error(E_FATAL|E_INTERNAL, "case %d not handled in switch", (int) NameType ); return (char *) 0; }