/*****
 ** ** Module Header ******************************************************* **
 ** 									     **
 **   Modules Revision 3.0						     **
 **   Providing a flexible user environment				     **
 ** 									     **
 **   File:		cmdVersion.c					     **
 **   First Edition:	95/12/28					     **
 ** 									     **
 **   Authors:	Jens Hamisch, jens@Strawberry.COM			     **
 ** 									     **
 **   Description:	The Tcl module-version routine which provides the    **
 **			definition of symbolic version names and the module- **
 **			alias command providing the definition of module and **
 **			version aliases					     **
 ** 									     **
 **   Exports:		cmdModuleVersion				     **
 **			cmdModuleAlias					     **
 **			CleanupVersion					     **
 **			AliasLookup					     **
 **			ExpandVersions					     **
 ** 									     **
 **   Notes:	This module defines the callback functions for the defi-     **
 **		nition of symbolic module names and module aliases. The      **
 **		syntax of the according commands is defined as:		     **
 ** 									     **
 **	    Module-Versions:						     **
 **		module-version <module>/<version> <name> [ <name> ... ]	     **
 **		module-version /<version> <name> [ <name> ... ]		     **
 **		module-version <module> <name> [ <name> ... ]		     **
 **		module-version <alias> <name> [ <name> ... ]		     **
 ** 									     **
 **	    Module-Alias:						     **
 **		module-alias <alias> <module>/<version>			     **
 **		module-alias <alias> /<version>				     **
 **		module-alias <alias> <module>				     **
 **		module-alias <alias> <alias>				     **
 ** 									     **
 ** 									     **
 ** ************************************************************************ **
 ****/

/** ** Copyright *********************************************************** **
 ** 									     **
 ** Copyright 1991-1994 by John L. Furlan.                      	     **
 ** see LICENSE.GPL, which must be provided, for details		     **
 ** 									     ** 
 ** ************************************************************************ **/

static char Id[] = "@(#)$Id: cmdVersion.c,v 1.2 2001/06/09 09:48:46 rkowen Exp $";
static void *UseId[] = { &UseId, Id };

/** ************************************************************************ **/
/** 				      HEADERS				     **/
/** ************************************************************************ **/

#include "modules_def.h"

/** ************************************************************************ **/
/** 				  LOCAL DATATYPES			     **/
/** ************************************************************************ **/

/** ************************************************************************ **/
/**									     **/
/**   The whole thing is handled in memory. The structure is build of module **/
/**   and name records. There are 3 types of name records: version, name     **/
/**   and alias. 							     **/
/**									     **/
/**         |                                                    |	     **/
/**     +---+---+<-------------------------------+           +---+---+	     **/
/**     | module| ------------------------+      |           | alias |	     **/
/**     +---+---+ --------+               |      |           +-------+	     **/
/**         |   ^    +----+----+          |      |               |	     **/
/**         |   +--- | version |       +--+--+   |           +---+---+	     **/
/**         |   |    +----+----+       | name|---+     <-----| alias |	     **/
/**         |   |         |            +--+--+   |           +---+---+	     **/
/**         |   |    +----+----+          |      |               |	     **/
/**         |   +--- | version |------>+--+--+   |           +---+---+	     **/
/**         |   |    +----+----+<-+--- | name|---+           | alias |	     **/
/**         |   |         |       | +- +--+--+   |           +---+---+	     **/
/**         |   |    +----+----+  | |     |      |               |	     **/
/**         |   +--- | version |  | |  +--+--+   |           +---+---+	     **/
/**         |   |    +----+----+  | |  | name|---+     <-----| alias |	     **/
/**         |   |         |       | |  +--+--+   |           +---+---+	     **/
/**         |                     | |     |      |               |	     **/
/**         |                     | +->+--+--+   |           +---+---+	     **/
/**     +---+---+                 +--- | name|---+           | alias |	     **/
/**     | module|                      +--+--+   |           +---+---+	     **/
/**     +---+---+                         |      |               |	     **/
/**         |								     **/
/**			       alphabetic ordered     alphabtic ordered	     **/
/**			    list of names depending    list of aliases	     **/
/**			    to a single module file			     **/
/**									     **/
/**   Each module name points to a list of symbolic names and versions.	     **/
/**   The versions themselfes can be symbolic names and therefore are of the **/
/**   same record type as the names.					     **/
/**   The name and the version list is alphabetically sorted (even the       **/
/**   module list is). A version record points to a related name record	     **/
/**   containing a symbolic name for the version. Starting at this record,   **/
/**   the name records built a queue of symbolic names for the version.	     **/
/**   Both, the version and the name record do have a backward pointer to    **/
/**   the module record.						     **/
/**									     **/
/**   The alias list builds a alphabetic ordered list of defined aliases.    **/
/**   Each alias record points to the related name record.		     **/
/**									     **/
/** ************************************************************************ **/

typedef	struct	_mod_module	{
    struct _mod_module	*next;		/** alphabetic queue		     **/
    struct _mod_name	*version;	/** version queue   		     **/
    struct _mod_name	*name;		/** name queue      		     **/
    char		*module;	/** the name itsself		     **/
} ModModule;

typedef	struct	_mod_name	{
    struct _mod_name	*next;		/** alphabetic queue		     **/
    struct _mod_name	*ptr;		/** logical next    		     **/
    struct _mod_name	*version;	/** backwards version pointer	     **/
    struct _mod_module	*module;	/** related module  		     **/
    char		*name;		/** the name itsself		     **/
} ModName;

/** ************************************************************************ **/
/** 				     CONSTANTS				     **/
/** ************************************************************************ **/

#define	HISTTAB	100

/** ************************************************************************ **/
/**				      MACROS				     **/
/** ************************************************************************ **/

/** not applicable **/

/** ************************************************************************ **/
/** 				    LOCAL DATA				     **/
/** ************************************************************************ **/

static	char	module_name[] = "cmdVersion.c";	/** File name of this module **/
#if WITH_DEBUGGING_CALLBACK
static	char	_proc_cmdModuleVersion[] = "cmdModuleVersion";
static	char	_proc_cmdModuleAlias[] = "cmdModuleAlias";
#endif
#if WITH_DEBUGGING_UTIL_2
static	char	_proc_CleanupVersion[] = "CleanupVersion";
#endif
#if WITH_DEBUGGING_UTIL_1
static	char	_proc_AddModule[] = "AddModule";
static	char	_proc_FindModule[] = "FindModule";
static	char	_proc_AddName[] = "AddName";
static	char	_proc_FindName[] = "FindName";
#endif

/**
 **  The module and aliases list
 **/

static	ModModule	*modlist = (ModModule *) NULL;
static	ModName		*aliaslist = (ModName *) NULL;

/** ************************************************************************ **/
/**				    PROTOTYPES				     **/
/** ************************************************************************ **/

static	void		 CleanupVersionSub(	ModModule	 *ptr);

static	void		 CleanupName(		ModName		 *ptr);

static	ModModule	*AddModule(		char		 *name);

static	ModModule	*FindModule(		char		 *name,
						ModModule	**prev);

static	ModName		*AddName(		char		 *name,
						ModName		**start,
						ModModule	 *module);

static	ModName		*FindName(		char		 *name,
						ModName		 *start,
						ModName		**prev);

static	char		*CheckModuleVersion(	char 		 *name);

static	char		*scan_versions(		char 		 *buffer,
						char		 *base,
						ModName 	 *ptr,
						ModModule 	 *modptr);

/*++++
 ** ** Function-Header ***************************************************** **
 ** 									     **
 **   Function:		cmdModuleVersion				     **
 ** 									     **
 **   Description:	Callback function for 'version'			     **
 ** 									     **
 **   First Edition:	95/12/28					     **
 ** 									     **
 **   Parameters:	ClientData	 client_data			     **
 **			Tcl_Interp	*interp		According Tcl interp.**
 **			int		 argc		Number of arguments  **
 **			char		*argv[]		Argument array	     **
 ** 									     **
 **   Result:		int	TCL_OK		Successfull completion	     **
 **				TCL_ERROR	Any error		     **
 ** 									     **
 **   Attached Globals:	modlist		List containing all version names    **
 **   			g_flags		These are set up accordingly before  **
 **					this function is called in order to  **
 **					control everything		     **
 ** 									     **
 ** ************************************************************************ **
 ++++*/

int	cmdModuleVersion(	ClientData	 client_data,
	      			Tcl_Interp	*interp,
	      			int		 argc,
	      			char		*argv[])
{
    char	*version, *module;
    ModModule	*modptr;
    ModName	*versptr, *nameptr, *tmp, *ptr;
    int 	 i;

#if WITH_DEBUGGING_CALLBACK
    ErrorLogger( NO_ERR_START, LOC, _proc_cmdModuleVersion, NULL);
#endif

    /**
     **  Whatis mode?
     **/

    if( g_flags & M_WHATIS) 
        return( TCL_OK);		/** ------- EXIT PROCEDURE -------> **/
	
    /**
     **  Parameter check
     **/

    if( argc < 3) {
	if( OK != ErrorLogger( ERR_USAGE, LOC, argv[0], " modulename ",
	    " symbolic-version [symbolic-version ...] ", NULL))
	    return( TCL_ERROR);		/** -------- EXIT (FAILURE) -------> **/
    }

    if((char *) NULL == (module = CheckModuleVersion( argv[1]))) {
	ErrorLogger( ERR_BADMODNAM, LOC, argv[1], NULL);
	return( TCL_ERROR);		/** -------- EXIT (FAILURE) -------> **/
    }

    /**
     **  Display mode?
     **/

    if( g_flags & M_DISPLAY) {
	fprintf( stderr, "%s\t ", argv[ 0]);
	for( i=1; i<argc; i++)
	    fprintf( stderr, "%s ", argv[ i]);
	fprintf( stderr, "\n");
    }

    /**
     **  get the version from the argument
     **/

    if((char *) NULL == (version = strrchr( module, '/'))) {
	if( OK != ErrorLogger( ERR_INTERAL, LOC, NULL))
	    return( TCL_ERROR);		/** -------- EXIT (FAILURE) -------> **/
    }
    *version++ = '\0';

    /**
     **  Now we have a module and a version.
     **  Check wheter it exists (cond. create them). Check both, the version
     **  and the name queue in order to locate the desired version ...
     **/

    if((ModModule *) NULL == (modptr = AddModule( module))) {
	ErrorLogger( ERR_BADMODNAM, LOC, argv[1], NULL);
	return( TCL_ERROR);		/** -------- EXIT (FAILURE) -------> **/
    }

    if((ModName *) NULL == (ptr = FindName( version, modptr->version, &tmp))) {
	if((ModName *) NULL == (ptr = FindName( version, modptr->name, &tmp))) 
	    versptr = AddName( version, &modptr->version, modptr);
	else
	    versptr = ptr->version;
    } else
	versptr = ptr;

    /**
     **  Check all symbolic names now and allocate a name record for them
     **/
    
    for( i=2; i<argc; i++) {

	if( FindName( argv[ i], modptr->name, &tmp)) {
	    if( OK != ErrorLogger( ERR_DUP_SYMVERS, LOC, argv[ i], NULL))
		break;
	    else
		continue;
	}

	if((ModName *) NULL == (nameptr = AddName( argv[ i], &modptr->name,
	    modptr))) {
	    if( OK != ErrorLogger( ERR_INTERAL, LOC, NULL))
		break;
	    else
		continue;
	}

	/**
	 **  Concat the new element at the beginning of the name queue ...
	 **/

	nameptr->ptr = versptr->ptr;
	versptr->ptr = nameptr;
	nameptr->version = versptr;
    }

#if WITH_DEBUGGING_CALLBACK
    ErrorLogger( NO_ERR_END, LOC, _proc_cmdModuleVersion, NULL);
#endif

    return( TCL_OK);

} /** End of 'cmdModuleVersion' **/

/*++++
 ** ** Function-Header ***************************************************** **
 ** 									     **
 **   Function:		ExpandVersions					     **
 ** 									     **
 **   Description:	Callback function for 'version'			     **
 ** 									     **
 **   First Edition:	95/12/28					     **
 ** 									     **
 **   Parameters:	char	*name		Name to be expanded	     **
 ** 									     **
 **   Result:		char*	NULL		No symbols found	     **
 **				Otherwise	Pointer to the list string   **
 ** 									     **
 ** ************************************************************************ **
 ++++*/

char	*ExpandVersions( char	*name)
{
    char	*version, *module, *s;
    static char	 buffer[ BUFSIZ];
    ModModule	*modptr, *tmp1;
    ModName	*ptr, *tmp2;

#if WITH_DEBUGGING_CALLBACK
    ErrorLogger( NO_ERR_START, LOC, _proc_cmdModuleVersion, NULL);
#endif

    /**
     **  Parameter check
     **/

    if((char *) NULL == (module = CheckModuleVersion( name))) 
	return((char *) NULL );		/** -------- EXIT (FAILURE) -------> **/

    if((char *) NULL == (version = strrchr( module, '/'))) {
	if( OK != ErrorLogger( ERR_INTERAL, LOC, NULL))
	    return((char *) NULL );	/** -------- EXIT (FAILURE) -------> **/
    }

    *version++ = '\0';

    /**
     **  Now we have a module and a version.
     **  Check wheter it exists
     **/

    if((ModModule *) NULL == (modptr = FindModule( module, &tmp1)))
	return((char *) NULL );		/** -------- EXIT (FAILURE) -------> **/

    if((ModName *) NULL == (ptr = FindName( version, modptr->version, &tmp2))) {
	if((ModName *) NULL == (ptr = FindName( version, modptr->name, &tmp2))) 
	    return((char *) NULL );	/** -------- EXIT (FAILURE) -------> **/
	ptr = ptr->version;
    }

    if( !ptr->ptr)
	return((char *) NULL );		/** -------- EXIT (FAILURE) -------> **/

    /**
     **  Now scan in all the symbolic version names
     **/
 
    *buffer = '\0';
    if( s = scan_versions( buffer, buffer, ptr->ptr, modptr)) 
	*--s = '\0';			/** remove trailing ':'		     **/

#if WITH_DEBUGGING_CALLBACK
    ErrorLogger( NO_ERR_END, LOC, _proc_cmdModuleVersion, NULL);
#endif

    return( buffer);

} /** End of 'ExpandVersions' **/

/*++++
 ** ** Function-Header ***************************************************** **
 ** 									     **
 **   Function:		scan_versions					     **
 ** 									     **
 **   Description:	Scan all symbolic versions pointed to be the passed  **
 **			ModName pointer and print them as a list into the    **
 **			passed buffer.					     **
 ** 									     **
 **   First Edition:	95/12/28					     **
 ** 									     **
 **   Parameters:	char	*buffer		Buffer for printing in	     **
 **			ModName	*ptr		Name structure pointer	     **
 **			ModModule  *modptr	Assigned module name	     **
 ** 									     **
 **   Result:		char*	NULL		Nothing printed into the     **
 **						buffer			     **
 **				Otherwise	Pointer to the end of the    **
 **						string in the buffer	     **
 ** 									     **
 ** ************************************************************************ **
 ++++*/

static	char	*scan_versions( char		 *buffer,
				char		 *base,
				ModName		 *ptr,
				ModModule	 *modptr)
{
    ModName     *tmp, *vers;
    char 	*s;

    /**
     **  Recursively print the queue of names
     **/

    if( !ptr)
	return((char *) NULL);			/** ------ EXIT (END) -----> **/

    if( !ptr->name) {
	if( OK != ErrorLogger( ERR_INTERAL, LOC, NULL))
	    return((char *) NULL);		/** ---- EXIT (FAILURE) ---> **/

    } else {

	/**
	 **  Prevent endless loops
	 **/

	if( strstr( base, ptr->name)) {
	    ErrorLogger( ERR_SYMLOOP, LOC, ptr->name, NULL);
	    return((char *) NULL);		/** ---- EXIT (FAILURE) ---> **/
	}

	/**
	 **  Now print ...
	 **/

	/* sprintf( buffer, "%s:", ptr->name); */
	strcpy( buffer, ptr->name);
	strcat( buffer, ":");
	buffer += strlen( buffer);

	/**
	 **  Check wheter this is a version name again ...
	 **  This is a recursion, too
	 **/

	if((ModName *) NULL != (vers = FindName( ptr->name, modptr->version,
	    &tmp))) {
	    if( s = scan_versions( buffer, base, vers->ptr, modptr))
		buffer = s;
	}
    }

    /**
     **  This is the recursion. Preserve the buffer end pointer
     **/

    if( s = scan_versions( buffer, base, ptr->ptr, modptr))
	buffer = s;

    return( buffer);

} /** End of 'scan_versions' **/

/*++++
 ** ** Function-Header ***************************************************** **
 ** 									     **
 **   Function:		CheckModuleVersion				     **
 ** 									     **
 **   Description:	Reduce the passed module name into a <mod>/<vers>    **
 **			string						     **
 ** 									     **
 **   First Edition:	95/12/28					     **
 ** 									     **
 **   Parameters:	char	*name		name to be checked	     **
 ** 									     **
 **   Result:		char*	NULL		any error		     **
 **				Otherwise	Pointer to a <mod>/<vers>    **
 **						string			     **
 ** 									     **
 **   Attached Globals:	modlist		List containing all version names    **
 **			aliaslist	List containing all alises	     **
 **			g_current_module	The module which is handled  **
 **						by the current command	     **
 ** 									     **
 ** ************************************************************************ **
 ++++*/

static	char	*CheckModuleVersion( char *name)
{
    static char	 buffer[ BUFSIZ];
    char	*s, *t;

    /**
     **  Check the first parameter and extract modulename and version
     **/

    if( '/' == *name) {			/** only the version specified	     **/

	/**
	 **  get the module name from the current module ...
	 **/

	if( !g_current_module)
	    return((char *) NULL);

	strcpy( buffer, g_current_module);
	if((char *) NULL == (t = strrchr( buffer, '/')))
	    t = buffer + strlen( buffer);
	*t++ = '/';
	*t = '\0';

	/**
	 **  The version has been specified as a parameter
	 **/

	if( s = strrchr( name, '/')) {
	    s++;
	} else {
	    ErrorLogger( ERR_INTERAL, LOC, NULL);
	    return((char *) NULL);
	}

	strcpy( t, s);

    } else {				/** Maybe an alias or a module	     **/

	strcpy( buffer, name);
	if( !strrchr( buffer, '/')) {

	    /**
	     **  Check wheter this is an alias ...
	     **/

	    if( AliasLookup( buffer, &s, &t)) {

		/* sprintf( buffer, "%s/%s", s, t); */
		strcpy( buffer, s);
		strcat( buffer, "/");
		strcat( buffer, t);

	    } else {

		/**
		 **  The default version is being selected
		 **/

		t = buffer + strlen( buffer);
		if( '/' != *t)
		    *t++ = '/';
		strcpy( t, _default);
	    }
	}
    }

    /**
     **  Pass the buffer reference to the caller
     **/

    return( buffer);

} /** End of 'CheckModuleVersion' **/

/*++++
 ** ** Function-Header ***************************************************** **
 ** 									     **
 **   Function:		cmdModuleAlias					     **
 ** 									     **
 **   Description:	Callback function for 'alias'			     **
 ** 									     **
 **   First Edition:	95/12/28					     **
 ** 									     **
 **   Parameters:	ClientData	 client_data			     **
 **			Tcl_Interp	*interp		According Tcl interp.**
 **			int		 argc		Number of arguments  **
 **			char		*argv[]		Argument array	     **
 ** 									     **
 **   Result:		int	TCL_OK		Successfull completion	     **
 **				TCL_ERROR	Any error		     **
 ** 									     **
 **   Attached Globals:	aliaslist	List containing all alises	     **
 **   			g_flags		These are set up accordingly before  **
 **					this function is called in order to  **
 **					control everything		     **
 ** 									     **
 ** ************************************************************************ **
 ++++*/

int	cmdModuleAlias(	ClientData	 client_data,
	      		Tcl_Interp	*interp,
	      		int		 argc,
	      		char		*argv[])
{
    ModName	*tmp, *ptr;
    char	*version, *module;
    ModName	*trg_alias;
    ModModule	*modptr;

#if WITH_DEBUGGING_CALLBACK
    ErrorLogger( NO_ERR_START, LOC, _proc_cmdModuleAlias, NULL);
#endif

    /**
     **  Parameter check
     **/

    if( argc != 3) {
	if( OK != ErrorLogger( ERR_USAGE, LOC, argv[0], " aliasname ",
	    "modulename", NULL))
	    return( TCL_ERROR);		/** -------- EXIT (FAILURE) -------> **/
    }

    /**
     **  Whatis mode?
     **/

    if( g_flags & M_WHATIS) 
        return( TCL_OK);		/** ------- EXIT PROCEDURE -------> **/

    if( g_flags & M_DISPLAY) {
	fprintf( stderr, "%s\t %s %s\n", argv[ 0], argv[ 1], argv[ 2]);
    }
	
    /**
     **  Check if the target is an alias ...
     **  Conditionally split up the passed <module>/<version> pair.
     **/

    trg_alias = FindName( argv[ 2], aliaslist, &tmp);
    if( !trg_alias) {

    	if((char *) NULL == (module = CheckModuleVersion( argv[2])))
	    module = argv[ 2];

    	if((char *) NULL != (version = strrchr( module, '/')))
	    *version++ = '\0';
    }

    /**
     **  Any alias record existing with this name?
     **  If it does, we're finished ...
     **/

    if( ptr = FindName( argv[ 1], aliaslist, &tmp)) {

	if( !ptr->ptr || !ptr->ptr->name ||
	    !trg_alias && (!ptr->ptr->module || !ptr->ptr->module->module) ) {
	    ErrorLogger( ERR_INTERAL, LOC, NULL);
	    return( TCL_ERROR);		/** -------- EXIT (FAILURE) -------> **/
	}

	if( trg_alias && !strcmp( ptr->ptr->name, argv[ 2]) ||
	    !trg_alias && !strcmp( ptr->ptr->name, version) &&
		!strcmp( ptr->ptr->module->module, module))
	    return( TCL_OK);		/** -------- EXIT (SUCCESS) -------> **/

	if( OK != ErrorLogger( ERR_DUP_ALIAS, LOC, argv[1], NULL))
	    return( TCL_ERROR);		/** -------- EXIT (FAILURE) -------> **/

    } else {

	/**
	 **  We have to allocate a new alias entry
	 **/

	if((ModName *) NULL == (ptr = AddName( argv[ 1], &aliaslist, NULL))) {
	    ErrorLogger( ERR_INTERAL, LOC, NULL);
	    return( TCL_ERROR);		/** -------- EXIT (FAILURE) -------> **/
	}
    }

    /**
     **  Now ptr points to the affected module alias record ...
     **  Conditionally we have to create the module and the version record now.
     **/

    if( trg_alias) {
	ptr->ptr = trg_alias;

    } else {
	if((ModModule *) NULL == (modptr = AddModule( module))) {
	    ErrorLogger( ERR_BADMODNAM, LOC, argv[2], NULL);
	    ptr->ptr = (ModName *) NULL;
	    return( TCL_ERROR);		/** -------- EXIT (FAILURE) -------> **/
	}
	ptr->ptr = AddName( (version ? version : _default), &modptr->version,
	    modptr);
    }

#if WITH_DEBUGGING_CALLBACK
    ErrorLogger( NO_ERR_END, LOC, _proc_cmdModuleAlias, NULL);
#endif

    return( TCL_OK);

} /** End of 'cmdModuleAlias' **/

/*++++
 ** ** Function-Header ***************************************************** **
 ** 									     **
 **   Function:		AliasLookup					     **
 ** 									     **
 **   Description:	Resolves a given alias to a module/version string    **
 ** 									     **
 **   First Edition:	95/12/28					     **
 ** 									     **
 **   Parameters:	char	*alias		Name of the alias to be re-  **
 **						solved			     **
 **			char	**module	Buffer for the module name   **
 **			char	**version	Buffer for the module version**
 ** 									     **
 **   Result:		int	1		Success, value in the buffer **
 **						is valid		     **
 **				0		Any error, or not found	     **
 ** 									     **
 **   Attached Globals:	aliaslist	List containing all alises	     **
 ** 									     **
 ** ************************************************************************ **
 ++++*/

int	AliasLookup(	char	*alias,
			char	**module,
			char	**version)
{
    ModName	*ptr, *tmp, *oldptr = NULL;

    while( 1) {

	/**
	 **  Lokate the alias entry and check intergrity
	 **/

	if((ModName *) NULL == (ptr = FindName( alias, aliaslist, &tmp)))
	    return( 0);			/** ------- EXIT (not found) ------> **/

	if( ptr == oldptr || !ptr->ptr || !ptr->ptr->name ) {
	    ErrorLogger( ERR_INTERAL, LOC, NULL);
	    return( 0);			/** -------- EXIT (FAILURE) -------> **/
	}

	/**
	 **  Do we have to loop? Another alias has no module reference ...
	 **/

        if( !ptr->ptr->module) {
	    alias = ptr->ptr->name;
	    oldptr = ptr;
	    continue;
	}

	/**
	 **  Got it. Get the module name and the version from the found
	 **  entry.
	 **  Dereference symbolic module versions
	 **/

	*module = ptr->ptr->module->module;
	*version = ptr->ptr->name;

	if((ModName *) NULL != (ptr = FindName( *version,
	    ptr->ptr->module->name, &tmp))) {
	    if( !ptr->version || !ptr->version->name) {
		if( OK != ErrorLogger( ERR_INTERAL, LOC, NULL))
		    return( 0);
	    } else 
		*version = ptr->version->name;
	}

	break;

    } /** while **/

    return( 1);

} /** End of 'AliasLookup' **/

/*++++
 ** ** Function-Header ***************************************************** **
 ** 									     **
 **   Function:		VersionLookup					     **
 ** 									     **
 **   Description:	Resolves a given alias to a module/version string    **
 ** 									     **
 **   First Edition:	95/12/28					     **
 ** 									     **
 **   Parameters:	char	*alias		Name of the alias to be re-  **
 **						solved			     **
 **			char	**module	Buffer for the module name   **
 **			char	**version	Buffer for the module version**
 ** 									     **
 **   Result:		int	1		Success, value in the buffer **
 **						is valid		     **
 **				0		Any error, or not found	     **

 **   Attached Globals:	g_current_module	The module which is handled  **
 **						by the current command	     **
 ** 									     **
 ** ************************************************************************ **
 ++++*/

int	VersionLookup(	char *name, char **module, char **version)
{
    static char  buffer[ BUFSIZ];
    ModModule	*mptr, *mtmp;
    ModName	*vptr, *vtmp;
    ModName	**history;
    char	*s, *t;
    int		 histsize = 0, histndx = 0, i;

    /**
     **  Check whether this is an alias ...
     **  BTW: Alias lookups return the FQMN (full qualifed module name ;-)
     **/

    if( '/' == *name) {
	strcpy( buffer, g_current_module);
	if( s = strrchr( buffer, '/'))
	    *s = '\0';
	*module = buffer;
	*version = name + 1;
    
    } else {

	strcpy( buffer, name);
	*module = buffer;

	if((char *) NULL == (*version = strrchr( buffer, '/'))) {

	    if( AliasLookup( buffer, &s, &t)) {
		*module = s; *version = t;

	    } else
		*version = _default;

	} else 
	    *(*version)++ = '\0';
    }

    /**
     **  Look up modulename ...
     **  We call it success, if we do not find a registerd name.
     **  In this case <module>/<version> will be returned as passed.
     **/

    if((ModModule *) NULL == (mptr = FindModule( *module, &mtmp))) {
	return( 1);			/** -------- EXIT (SUCCESS) -------> **/
    }

    /**
     **  This is for preventing from endless loops
     **/

    histsize = HISTTAB;
    histndx = 0;

    if((ModName **) NULL == (history = (ModName **) malloc( histsize * 
	sizeof( ModName *)))) {
	ErrorLogger( ERR_ALLOC, LOC, NULL);
	return( 0);			/** -------- EXIT (FAILURE) -------> **/
    }

    /**
     **  Now look up the version name. Check symbolic names first. If some-
     **  thing is found, check if the related version record itsself relates
     **  to a name record ...
     **/

    while( 1) {

	/**
	 **  Check the symbolic names ...
	 **/

	if((ModName *) NULL != (vptr = FindName( *version, mptr->name, &vtmp))){
	    if( !vptr->version || !vptr->version->name) {
		if( OK != ErrorLogger( ERR_INTERAL, LOC, NULL)) 
		    *version = (char *) NULL;
		break;
	    }

	    *version = vptr->version->name;

	    /**
	     **  Prevent from looping ...
	     **/

	    for( i=0; i<histndx; i++) {
		if( history[ i] == vptr) {		/** That's the loop  **/
		    ErrorLogger( ERR_SYMLOOP, LOC, *version, NULL);
		    *version = (char *) NULL;
		    break;
		}
	    }

	    if( !*version)
		break;

	    if( histndx >= histsize) {
		histsize += HISTTAB;

		if((ModName **) NULL == (history = (ModName **) realloc(
		    history, histsize * sizeof( ModName *)))) {
		    ErrorLogger( ERR_ALLOC, LOC, NULL);
		    return( 0);		/** -------- EXIT (FAILURE) -------> **/
		}
	    }

	    history[ histndx++] = vptr;

	} else {
	    break;

	} /** if( FindName) **/
    } /** while( 1) **/

    /**
     **  Free the loop preventing list
     **  If version is NULL now, something went wrong in the lookup loop above
     **/

    free( history);
    return((char *) NULL != *version);

} /** End of 'VersionLookup' **/

/*++++
 ** ** Function-Header ***************************************************** **
 ** 									     **
 **   Function:		CleanupVersion					     **
 ** 									     **
 **   Description:	Cleanup the version structure			     **
 ** 									     **
 **   First Edition:	95/12/28					     **
 ** 									     **
 **   Parameters:	-						     **
 ** 									     **
 **   Result:		-						     **
 ** 									     **
 **   Attached Globals:	modlist		List containing all version names    **
 **			aliaslist	List containing all alises	     **
 ** 									     **
 ** ************************************************************************ **
 ++++*/

void	CleanupVersion(ModModule *ptr)
{
#if WITH_DEBUGGING_UTIL_2
    ErrorLogger( NO_ERR_START, LOC, _proc_CleanupVersion, NULL);
#endif

    CleanupVersionSub( modlist);
    modlist = (ModModule *) NULL;

    CleanupName( aliaslist);
    aliaslist = (ModName *) NULL;

} /** End of 'CleanupVersion' **/

static void	CleanupVersionSub( ModModule *ptr)
{
    /**
     **  Recursion
     **/

    if( !ptr)
	return;

    CleanupVersion( ptr->next);

    /**
     **  Cleanup everything that relates to this record
     **/

    CleanupName( ptr->version);
    CleanupName( ptr->name);
    free( ptr->module);

} /** End of 'CleanupVersionSub' **/

static void	CleanupName( ModName *ptr)
{
    /**
     **  Recursion
     **/

    if( !ptr)
	return;

    CleanupName( ptr->next);

    /**
     **  Cleanup everything that relates to this record
     **/

    free( ptr->name);

} /** End of 'CleanupName' **/

/*++++
 ** ** Function-Header ***************************************************** **
 ** 									     **
 **   Function:		AddModule					     **
 ** 									     **
 **   Description:	Add a new entry to the modules queue		     **
 ** 									     **
 **   First Edition:	95/12/28					     **
 ** 									     **
 **   Parameters:	char	*name		Name of the new module	     **
 ** 									     **
 **   Result:		ModModule*	NULL	Any error                    **
 **					Else	Pointer to the new record    **
 ** 									     **
 **   Attached Globals:	modlist		List containing all version names    **
 ** 									     **
 ** ************************************************************************ **
 ++++*/

static	ModModule	*AddModule(	char	*name)
{
    ModModule	*app_ptr, *ptr;

#if WITH_DEBUGGING_UTIL_1
    ErrorLogger( NO_ERR_START, LOC, _proc_AddModule, NULL);
#endif

    /**
     **  We do not trust in NULL module names
     **/

    if( !name || !*name)
	return((ModModule *) NULL);

    /**
     **  Check if the module name already exists and save the 'prev' pointer
     **  for appending the new one.
     **/

    if( ptr = FindModule( name, &app_ptr))
	return( ptr);

    /**
     **  Allocate a new guy
     **/

    if((ModModule *) NULL == (ptr = (ModModule *) malloc( sizeof(ModModule)))) {
	ErrorLogger( ERR_ALLOC, LOC, NULL);
	return((ModModule *) NULL);
    }

    /**
     **  Fill the name in and put it in the queue
     **/

    if((char *) NULL == (ptr->module = strdup( name))) {
	ErrorLogger( ERR_ALLOC, LOC, NULL);
	free( ptr);
	return((ModModule *) NULL);
    }

    if( app_ptr) {
	ptr->next = app_ptr->next;
	app_ptr->next = ptr;
    } else {
	ptr->next = modlist;
	modlist = ptr;
    }

    ptr->version = (ModName *) NULL;
    ptr->name = (ModName *) NULL;

    /**
     **  Pass back the pointer to the new entry
     **/

#if WITH_DEBUGGING_UTIL_1
    ErrorLogger( NO_ERR_END, LOC, _proc_AddModule, NULL);
#endif

    return( ptr);

} /** End of 'AddModule' **/

/*++++
 ** ** Function-Header ***************************************************** **
 ** 									     **
 **   Function:		FindModule					     **
 ** 									     **
 **   Description:	Find a new entry in the modules queue		     **
 ** 									     **
 **   First Edition:	95/12/28					     **
 ** 									     **
 **   Parameters:	char		*name	Name of be found	     **
 **			ModModule	**prev	Buffer for the 'previous'    **
 **						pointer			     **
 ** 									     **
 **   Result:		ModModule*	NULL	Any error or not found       **
 **					Else	Pointer to the record	     **
 ** 									     **
 **   Attached Globals:	modlist		List containing all version names    **
 ** 									     **
 ** ************************************************************************ **
 ++++*/

static	ModModule	*FindModule(	char		 *name,
					ModModule	**prev)
{
    ModModule	*ptr = modlist;
    int		 cmp = 1;

#if WITH_DEBUGGING_UTIL_1
    ErrorLogger( NO_ERR_START, LOC, _proc_FindModule, NULL);
#endif

    *prev = (ModModule *) NULL;
    while( ptr && 0 < (cmp = strcmp( name, ptr->module))) {
	*prev = ptr;
	ptr = ptr->next;
    }

#if WITH_DEBUGGING_UTIL_1
    ErrorLogger( NO_ERR_END, LOC, _proc_FindModule, NULL);
#endif

    return( cmp ? (ModModule *) NULL : ptr);

} /** End of 'FindModule' **/

/*++++
 ** ** Function-Header ***************************************************** **
 ** 									     **
 **   Function:		AddName						     **
 ** 									     **
 **   Description:	Add a new entry to the name queue		     **
 ** 									     **
 **   First Edition:	95/12/28					     **
 ** 									     **
 **   Parameters:	char	 *name		Name of the new entry	     **
 **			ModName	**start		Start of the queue	     **
 **			ModModule *module	Parent module record pointer **
 ** 									     **
 **   Result:		ModName*	NULL	Any error                    **
 **					Else	Pointer to the new record    **
 ** 									     **
 ** ************************************************************************ **
 ++++*/

static	ModName	*AddName(	char	 *name,
				ModName	**start,
				ModModule *module)
{
    ModName	*app_ptr, *ptr;

#if WITH_DEBUGGING_UTIL_1
    ErrorLogger( NO_ERR_START, LOC, _proc_AddName, NULL);
#endif

    /**
     **  Check if the name already exists and save the 'prev' pointer
     **  for appending the new one.
     **/

    if( ptr = FindName( name, *start, &app_ptr))
	return( ptr);

    /**
     **  Allocate a new guy
     **/

    if((ModName *) NULL == (ptr = (ModName *) malloc( sizeof(ModName)))) {
	ErrorLogger( ERR_ALLOC, LOC, NULL);
	return((ModName *) NULL);
    }

    /**
     **  Fill the name in and put it in the queue
     **/

    if((char *) NULL == (ptr->name = strdup( name))) {
	ErrorLogger( ERR_ALLOC, LOC, NULL);
	free( ptr);
	return((ModName *) NULL);
    }

    if( app_ptr) {
	ptr->next = app_ptr->next;
	app_ptr->next = ptr;
    } else {
	ptr->next = *start;
	*start = ptr;
    }

    ptr->module = module;
    ptr->version = ptr->ptr = (ModName *) NULL;

    /**
     **  Pass back the pointer to the new entry
     **/

#if WITH_DEBUGGING_UTIL_1
    ErrorLogger( NO_ERR_END, LOC, _proc_AddName, NULL);
#endif

    return( ptr);

} /** End of 'AddName' **/

/*++++
 ** ** Function-Header ***************************************************** **
 ** 									     **
 **   Function:		FindName					     **
 ** 									     **
 **   Description:	Find a new entry in the modules queue		     **
 ** 									     **
 **   First Edition:	95/12/28					     **
 ** 									     **
 **   Parameters:	char	 *name		Name of be found	     **
 **			ModName  *start		Start of the name queue      **
 **			ModName	**prev		Buffer for the 'previous'    **
 **						pointer			     **
 ** 									     **
 **   Result:		ModName*	NULL	Any error or not found       **
 **					Else	Pointer to the record	     **
 ** 									     **
 ** ************************************************************************ **
 ++++*/

static	ModName	*FindName(	char	 *name,
				ModName  *start,
				ModName	**prev)
{
    ModName	*ptr = start;
    int		 cmp = 1;

#if WITH_DEBUGGING_UTIL_1
    ErrorLogger( NO_ERR_START, LOC, _proc_FindName, NULL);
#endif

    *prev = (ModName *) NULL;
    while( ptr && 0 < (cmp = strcmp( name, ptr->name))) {
	*prev = ptr;
	ptr = ptr->next;
    }

#if WITH_DEBUGGING_UTIL_1
    ErrorLogger( NO_ERR_END, LOC, _proc_FindName, NULL);
#endif

    return( cmp ? (ModName *) NULL : ptr);

} /** End of 'FindName' **/


syntax highlighted by Code2HTML, v. 0.9.1