/* 
 * Motif
 *
 * Copyright (c) 1987-2012, The Open Group. All rights reserved.
 *
 * These libraries and programs are free software; you can
 * redistribute them and/or modify them under the terms of the GNU
 * Lesser General Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * These libraries and programs are distributed in the hope that
 * they will be useful, but WITHOUT ANY WARRANTY; without even the
 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
 * PURPOSE. See the GNU Lesser General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with these librararies and programs; if not, write
 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
 * Floor, Boston, MA 02110-1301 USA
 */ 
/* 
 * HISTORY
 */ 
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif


#ifdef REV_INFO
#ifndef lint
static char rcsid[] = "$XConsortium: Mrmhier.c /main/17 1996/11/13 14:01:19 drk $"
#endif
#endif

/* (c) Copyright 1989, 1990, DIGITAL EQUIPMENT CORPORATION, MAYNARD, MASS. */


/*
 *++
 *  FACILITY:
 *
 *      UIL Resource Manager (URM):
 *
 *  ABSTRACT:
 *
 *--
 */

/*
 * This file contains routines which assist in managing URM hierarchies
 */


/*
 *
 *  INCLUDE FILES
 *
 */

#include <stdio.h>
#include <Mrm/MrmAppl.h>
#include <Mrm/Mrm.h>

#include <Xm/XmosI.h>		/* for _XmOSInitPath() */
#include <Xm/XmI.h>		/* for _XmGetDefaultDisplay() */

#include "MrmMsgI.h"

#ifndef NO_MESSAGE_CATALOG
#if !defined(NL_CAT_LOCALE)
#define NL_CAT_LOCALE	0
#endif
#endif


/*
 *
 *  TABLE OF CONTENTS
 *
 *	Urm__OpenHierarchy		Open hierarchy (internal version)
 *
 *	Urm__CloseHierarchy		Close hierarchy (internal version)
 *
 *	UrmHGetIndexedResource		Get resource from hierarchy
 *
 */


/*
 *
 *  DEFINE and MACRO DEFINITIONS
 *
 */

static Cardinal I18NOpenFile (Display		 *display, 
			      String		 name , 
			      MrmOsOpenParamPtr	 os_ext ,
			      IDBFile		 *file_id_return );


/*
 *
 *  OWN VARIABLE DECLARATIONS
 *
 */
static char		*uidPath;
static SubstitutionRec	uidSubs[1];



/*
 *++
 *
 *  PROCEDURE DESCRIPTION:
 *
 *	This routine allocates a hierarchy descriptor, and opens
 *	all the IDB files in the hierarchy. It initializes the
 *	optimized search lists in the hierarchy from the open files.
 *	All files are closed if there are any errors.
 *
 *  FORMAL PARAMETERS:
 *
 *	num_files		The number of files in the name list
 *	name_list		A list of the file names
 *	os_ext_list		A list of system-dependent ancillary
 *				structures corresponding to the files.
 *				This parameter may be NULL.
 *	hierarchy_id_return	To return the hierarchy id
 *
 *  IMPLICIT INPUTS:
 *
 *  IMPLICIT OUTPUTS:
 *
 *  FUNCTION VALUE:
 *
 *	MrmSUCCESS		operation succeeded
 *	MrmFAILURE		allocation or other failure
 *	MrmDISPLAY_NOT_OPENED	Display hasn't been opened yet
 *	Others			see UrmIdbOpenFileRead
 *
 *  SIDE EFFECTS:
 *
 *--
 */

Cardinal 
Urm__OpenHierarchy (MrmCount			num_files,
		    String			*name_list,
		    MrmOsOpenParamPtr		*os_ext_list,
		    MrmHierarchy		*hierarchy_id_return,
		    MrmFlag			in_memory,
		    unsigned char		*uid_buffer)
{

  /*
   *  Local variables
   */
  Cardinal		result ;	/* function result */
  MrmHierarchy		hiptr ;		/* hierarchy descriptor */
  MrmCount		list_size ;	/* # bytes for file lists */
  int			ndx ;		/* loop index */
  IDBFile		*idvec ;	/* current file id vector */
  int			file_ndx ;	/* file loop index */
  IDBFile		cur_file ;	/* current IDB file */
  URMResourceContextPtr	class_ctx;	/* for class compression table */
  URMResourceContextPtr	resource_ctx;	/* for resource compression table */
  Display		*display;	/* display for XtResolvePathNames */
  char                 	err_stg[300];

#ifndef NO_MESSAGE_CATALOG
  static Boolean first = True;

  /*
   * We open the message catalog from MrmOpenHierarchy... routines.
   * We cannot open it from MrmInitialize, since this call can be done 
   * before Xt has been initialized, so at that time the locale could
   * be wrong.
   */

  /* We only want to open the message catalog once. */

  if (first) 
    {
      Mrm_catd = catopen("Mrm", NL_CAT_LOCALE);    
      first = False;
    }
#endif

  if ( os_ext_list == NULL )
    {
      display = NULL;
    }
  else 
    {
      MrmOsOpenParamPtr os_data;
      os_data = *os_ext_list;
      if (os_data->display == NULL)
	{
	  display = NULL;
	}
      else
	{
	  display = os_data->display;
	}
    }

  if (display == NULL)
    {
      display = _XmGetDefaultDisplay();
    }

  if (display == NULL)
    {
      return Urm__UT_Error ("Urm__OpenHierarchy", _MrmMMsg_0030,
			    NULL, NULL, MrmDISPLAY_NOT_OPENED);
    };

  /*
   * If the uidPath was previously set, XtFree it so we can try any
   * new paths that may have been setup.
   */
  if (uidPath != 0) 
    {
      XtFree (uidPath);
      uidPath = 0;
    }

  /*
   * Allocate a hierarchy, and allocate all file lists.
   */
  hiptr = (MrmHierarchy) XtMalloc (sizeof(MrmHierarchyDesc)) ;
  if ( hiptr == NULL ) return MrmFAILURE ;

  hiptr->validation = MrmHIERARCHY_VALID;
  hiptr->num_file = 0 ;

  list_size = num_files * sizeof(IDBFile) ;
  hiptr->file_list = (IDBFile *) XtMalloc (list_size) ;
  if ( hiptr->file_list == NULL ) return MrmFAILURE ;

  for ( ndx=URMgMin ; ndx<=URMgMax ; ndx++ )
    {
      hiptr->grp_num[ndx] = 0 ;
      idvec = (IDBFile *) XtMalloc (list_size) ;
      if ( idvec == NULL ) return MrmFAILURE ;
      hiptr->grp_ids[ndx] = idvec ;
    }

  hiptr->name_registry = NULL;

  /*
   * Now open each file. Any failure causes an error return, with any
   * open files closed and the descriptor and lists freed.
   */
  for ( file_ndx=0 ; file_ndx<num_files ; file_ndx++ )
    {
      if ( in_memory == TRUE ) 
	{
	  result = UrmIdbOpenBuffer(uid_buffer, &cur_file) ;
	  switch ( result )
	    {
	    case MrmSUCCESS:
	      break;
	    case MrmNOT_VALID:
	      sprintf (err_stg, _MrmMMsg_0113);
	      break;
	    default:
	      sprintf (err_stg, _MrmMMsg_0114);
	      break;
	    }
	}
      else if ( os_ext_list == NULL )
	result = I18NOpenFile (display, name_list[file_ndx], NULL, &cur_file) ;
      else
        result = I18NOpenFile (display, name_list[file_ndx], 
			       os_ext_list[file_ndx], &cur_file) ;
      if ( result != MrmSUCCESS )
        {
	  XtFree (uidPath);
	  uidPath = 0;
	  Urm__CloseHierarchy (hiptr) ;
	  return result;
        }

      hiptr->file_list[hiptr->num_file] = cur_file ;
      hiptr->num_file++ ;
      for ( ndx=URMgMin ; ndx<=URMgMax ; ndx++ )
        if ( cur_file->group_counts[ndx] > 0 )
	  {
            idvec = hiptr->grp_ids[ndx] ;
            idvec[hiptr->grp_num[ndx]] = cur_file ;
            hiptr->grp_num[ndx]++ ;
	  }

      /*
       * Attempt to read in compression tables for this UID file.
       * Retain and fixup the tables if they are found.
       */
      cur_file->class_ctable = NULL;
      cur_file->resource_ctable = NULL;
      result = UrmGetResourceContext ((char *(*)())NULL, (void(*)())NULL,
				      0, &class_ctx);
      if ( result != MrmSUCCESS ) return result;
      result = UrmGetResourceContext ((char *(*)())NULL, (void(*)())NULL,
				      0, &resource_ctx);
      if ( result != MrmSUCCESS ) return result;
      result = UrmGetIndexedLiteral (cur_file, UilMrmClassTableIndex, 
				     class_ctx);
      if ( result != MrmSUCCESS ) continue;
      result = UrmGetIndexedLiteral (cur_file, UilMrmResourceTableIndex, 
				     resource_ctx);
      if ( result != MrmSUCCESS ) continue;

      /*
       * Retain the buffers from the contexts, but free the contexts
       * themselves. Fixup the tables.
       */
      cur_file->class_ctable =  (UidCompressionTablePtr)
	UrmRCBuffer (class_ctx);
      UrmRCSetBuffer (class_ctx, NULL);
      UrmFreeResourceContext (class_ctx);
      Urm__FixupCompressionTable (cur_file->class_ctable, TRUE, 
				  cur_file->byte_swapped);
      cur_file->resource_ctable = (UidCompressionTablePtr)
	UrmRCBuffer (resource_ctx);
      UrmRCSetBuffer (resource_ctx, NULL);
      UrmFreeResourceContext (resource_ctx);
      Urm__FixupCompressionTable (cur_file->resource_ctable, FALSE, 
				  cur_file->byte_swapped);
    }

  /*
   * successfully opened. Free the uidPath at this point
   */
  XtFree (uidPath);
  uidPath = 0;
  *hierarchy_id_return = hiptr ;
  return MrmSUCCESS ;

}



/*
 *++
 *
 *  PROCEDURE DESCRIPTION:
 *
 *	This is the internal routine which closes a URM search hierarchy
 *
 *  FORMAL PARAMETERS:
 *
 *	hierarchy_id	ID of an open URM database hierarchy
 *
 *  IMPLICIT INPUTS:
 *
 *  IMPLICIT OUTPUTS:
 *
 *  FUNCTION VALUE:
 *
 *	MrmSUCCESS	operation succeeded
 *	MrmBAD_HIERARCHY	invalid URM hierarchy
 *	MrmFAILURE	operation failed, no further reason
 *
 *  SIDE EFFECTS:
 *
 *--
 */

Cardinal 
Urm__CloseHierarchy (MrmHierarchy	hierarchy_id)
{

  /*
   *  Local variables
   */
  int			ndx ;		/* loop index */
  URMHashTableEntryPtr	cp, np;         /* for fixing 7303 */

  /*
   * validity check the hierarchy, then close all files, deallocate all
   * lists, and deallocate the descriptor
   */
  if ( hierarchy_id == NULL )
    return Urm__UT_Error ("Urm__CloseHierarchy", _MrmMMsg_0023,
			  NULL, NULL, MrmBAD_HIERARCHY) ;
  if ( ! MrmHierarchyValid(hierarchy_id) )
    return Urm__UT_Error ("Urm__CloseHierarchy", _MrmMMsg_0024,
			  NULL, NULL, MrmBAD_HIERARCHY) ;

  for ( ndx=0 ; ndx<hierarchy_id->num_file ; ndx++ )
    if (hierarchy_id->file_list[ndx]->in_memory == FALSE )
      UrmIdbCloseFile (hierarchy_id->file_list[ndx], FALSE) ;

  /* Begin fixing DTS 7303 */
  if(hierarchy_id->name_registry){
    for ( ndx=0 ; ndx<k_hash_table_size; ndx++ )
      if((cp = hierarchy_id->name_registry[ndx]) != NULL)
	while(cp){
	  np = cp->az_next_entry;
	  XtFree((char*)cp);
	  cp = np;
	};
    XtFree ((char*)hierarchy_id->name_registry);
  }
  /* End fixing DTS 7303 */
 
  XtFree ((char*)hierarchy_id->file_list);

  for ( ndx=URMgMin ; ndx<=URMgMax ; ndx++ )
    XtFree ((char*)hierarchy_id->grp_ids[ndx]) ;

  hierarchy_id->validation = 0;
  XtFree ((char*)hierarchy_id) ;
  return MrmSUCCESS ;

}



/*
 *++
 *
 *  PROCEDURE DESCRIPTION:
 *
 *	UrmHGetIndexedResource attempts to retrieve a resource
 *	from an open URM hierarchy. It functions exactly like
 *	UrmIdbGetIndexedResource except that it queries each file
 *	in the hierarchy in turn. It uses the optimized search lists
 *	where possible.
 *
 *  FORMAL PARAMETERS:
 *
 *	hierarchy_id		open URM hierarchy to search
 *	index			case-sensitive index for the entry to match
 *	group_filter		if not null, entry found must match this group
 *	type_filter		if not null, entry found must match this type
 *	context_id		URM resource context to receieve data block
 *	file_id_return		to return file in which resource was found
 *
 *  IMPLICIT INPUTS:
 *
 *  IMPLICIT OUTPUTS:
 *
 *  FUNCTION VALUE:
 *
 *	MrmSUCCESS	operation succeeded
 *	MrmBAD_HIERARCHY	invalid URM hierarchy
 *	MrmNOT_FOUND	entry not found
 *	MrmWRONG_GROUP	entry didn't match group filter
 *	MrmWRONG_TYPE	entry didn't match type filter
 *	MrmFAILURE	operation failed, no further reason
 *
 *  SIDE EFFECTS:
 *
 *--
 */

Cardinal 
UrmHGetIndexedResource (MrmHierarchy		hierarchy_id,
			String			index ,
			MrmGroup			group_filter, 
			MrmType			type_filter,
			URMResourceContextPtr	context_id,
			IDBFile			*file_id_return)
{

  /*
   *  Local variables
   */
  Cardinal		result ;	/* function results */
  IDBFile		*file_ids ;	/* list of files to search */
  int			num_ids ;	/* number of entries in file_ids */
  int			ndx ;		/* loop index */


  /*
   * Validate hierarchy. Then loop through the files which define the resource,
   * searching for the desired resource.
   */
  if ( hierarchy_id == NULL )
    return Urm__UT_Error ("UrmHGetIndexedResource", _MrmMMsg_0023,
			  NULL, NULL, MrmBAD_HIERARCHY) ;
  if ( ! MrmHierarchyValid(hierarchy_id) )
    return Urm__UT_Error ("UrmHGetIndexedResource", _MrmMMsg_0024,
			  NULL, context_id, MrmBAD_HIERARCHY) ;

  if ( group_filter>=URMgMin && group_filter<=URMgMax)
    {
      file_ids = hierarchy_id->grp_ids[group_filter] ;
      num_ids = hierarchy_id->grp_num[group_filter] ;
    }
  else
    {
      file_ids = hierarchy_id->file_list ;
      num_ids = hierarchy_id->num_file ;
    }
  for ( ndx=0 ; ndx<num_ids ; ndx++ )
    {
      *file_id_return = file_ids[ndx] ;
      result = UrmIdbGetIndexedResource
        (*file_id_return, index, group_filter, type_filter, context_id) ;
      if ( result == MrmSUCCESS ) return result ;
    }

  /*
   * Not found
   */
  return MrmNOT_FOUND ;

}



/*
 *++
 *
 *  PROCEDURE DESCRIPTION:
 *
 *	This routine inserts the names and their associated values into
 *	the hierarchy's name registration hash table (which is constructed
 *	if needed). In all respects it functions like Urm__WCI_RegisterNames.
 *
 *  FORMAL PARAMETERS:
 *
 *	hierarchy_id	open URM hierarchy in which to register names.
 *	names		A vector of case-sensitive callback routine names.
 *	values		A vector of the corresponding routine addresses
 *	num_cb		The number of entries in names and values.
 *
 *  IMPLICIT INPUTS:
 *
 *  IMPLICIT OUTPUTS:
 *
 *  FUNCTION VALUE:
 *
 *	MrmSUCCESS	operation succeeded
 *
 *  SIDE EFFECTS:
 *
 *--
 */

Cardinal 
Urm__RegisterNamesInHierarchy (MrmHierarchy		hierarchy_id, 
			       String			*names,
			       XtPointer			*values,
			       MrmCount			num_cb)
{

  URMHashTableEntryPtr	*name_table;
  Boolean		inited = FALSE;
  int			ndx;
  URMHashTableEntryPtr	hash_entry;
  char			*current_name;
  char			*current_value;


  /*
   * Make sure the hash table is initialized
   */
  name_table = hierarchy_id->name_registry;
  if ( name_table == NULL )
    {
      name_table = (URMHashTableEntryPtr *)
	XtMalloc(sizeof(URMHashTableEntryPtr)*k_hash_table_size);
      /* Begin fixing DTS 7303 */
      for ( ndx=0 ; ndx<k_hash_table_size; ndx++ )
	name_table[ndx] = NULL;
      /* End fixing DTS 7303 */
      hierarchy_id->name_registry = name_table;
      hash_initialize (name_table, &inited);
    }

  /*
   * Store each name-value pair in the hash table.
   */
  for (ndx = 0 ; ndx < num_cb ; ndx++)
    {
      current_name = names [ndx];
      current_value = values [ndx];
      hash_entry = (URMHashTableEntryPtr)
	hash_insert_name (name_table, current_name);
      hash_entry->az_value = current_value;
    }

  return MrmSUCCESS;

}



/*
 *++
 *
 *  PROCEDURE DESCRIPTION:
 *
 *	This routine returns the value registered for a name. It first
 *	attempts to look up the name in the hierarchy's name registry.
 *	If that fails, or their is no registry, then a global lookup is
 *	attempted.
 *
 *  FORMAL PARAMETERS:
 *
 *	hierarchy_id	open URM hierarchy to search.
 *	name		case-sensitive name to be matched
 *	value_return	to return value.
 *
 *  IMPLICIT INPUTS:
 *
 *  IMPLICIT OUTPUTS:
 *
 *  FUNCTION VALUE:
 *
 *	MrmSUCCESS	operation succeeded
 *	MrmNOT_FOUND	no match found
 *
 *  SIDE EFFECTS:
 *
 *--
 */

Cardinal
Urm__LookupNameInHierarchy (MrmHierarchy	hierarchy_id,
			    String		name,
			    XtPointer		*value_return)

{

  URMHashTableEntryPtr	*name_table;
  URMHashTableEntryPtr	hash_entry;


  /*
   * Look up in hierarchy first (if there is a registry)
   */
  name_table = hierarchy_id->name_registry;
  if ( name_table != NULL )
    {
      hash_entry = (URMHashTableEntryPtr) hash_find_name (name_table, name);
      if (hash_entry != NULL)
	{
	  *value_return = hash_entry->az_value;
	  return MrmSUCCESS;
	}
    }

  /*
   * Fall back on global table
   */
  return Urm__WCI_LookupRegisteredName (name, value_return);

}



/*
 *++
 *
 *  PROCEDURE DESCRIPTION:
 *
 *	This routine opens a single UID file in a platform-dependent way,
 *	performing i18n language switching in order to do so.
 *
 *	Per the latest agreement on semantics, this routine does:
 *		- first, try to open in the local directory (that is, with
 *		  no switching).
 *		- second, try language switching and open
 *
 *  FORMAL PARAMETERS:
 *
 *	name		A system-dependent string specifying the IDB file
 *			to be opened.
 *	os_ext		An operating-system specific structure which
 *			supports using specific file system features
 *	file_id_return	returns the IDB file id used in all other IDB routines
 *
 *  IMPLICIT INPUTS:
 *
 *  IMPLICIT OUTPUTS:
 *
 *  FUNCTION VALUE:
 *
 *  SIDE EFFECTS:
 *
 *--
 */

static Cardinal 
I18NOpenFile (Display			*display,
	      String			name,
	      MrmOsOpenParamPtr		os_ext,
	      IDBFile			*file_id_return)
{

  /*
   *  Local variables
   */
  Cardinal		result;		/* function results */
  char			dummy[300];	/* file name (unused) */
  char			err_stg[300];

  /*
   * Use XtResolvePathName.  If the last 4 characters of the file name
   * are not .uid then pass in the suffix of .uid.  If a file is not
   * found with the suffix passed in then try without the suffix.
   */
  char			*resolvedname; /* current resolved name */
  Boolean		user_path ;

  uidSubs[0].substitution = name;

  if (uidPath == 0)
    {
      uidPath = _XmOSInitPath(name, "UIDPATH", &user_path);
      if (user_path) uidSubs[0].match = 'U';
      else           uidSubs[0].match = MATCH_CHAR ;
    }

  resolvedname = 0;

  /*
   * Check and see if the .uid suffix is already on the file. If not then try to
   * resolve the pathname with .uid suffix first. If that fails or the suffix is
   * already on the file then just try to resolve the pathname.
   */
  if ( strcmp (&name[strlen(name)-4],".uid") != 0 ) 
    resolvedname = XtResolvePathname (display,
				      "uid",
				      NULL,
				      ".uid",
				      uidPath,
				      uidSubs,
				      XtNumber(uidSubs),
				      (XtFilePredicate)NULL);

  /*
   * No .uid suffix or a failure to resolve the pathname with the .uid suffix
   */
  if (resolvedname == 0) 
    resolvedname = XtResolvePathname (display,
				      "uid",
				      NULL,
				      NULL,
				      uidPath,
				      uidSubs,
				      XtNumber(uidSubs),
				      (XtFilePredicate)NULL);

  if (resolvedname == 0)
    {
      sprintf (err_stg, _MrmMMsg_0031, name) ;
      return Urm__UT_Error ("I18NOpenFile", err_stg, NULL, NULL, MrmNOT_FOUND);
    }

  result = UrmIdbOpenFileRead (resolvedname, os_ext, file_id_return, dummy) ;
  switch ( result )
    {
    case MrmSUCCESS:
      break;
    case MrmNOT_VALID:
      sprintf (err_stg, _MrmMMsg_0032, resolvedname) ;
      break;
    case MrmNOT_FOUND:
    default:
      sprintf (err_stg, _MrmMMsg_0031, resolvedname) ;
      break;
    }

  XtFree(resolvedname); /* allocated in XtResolvePathName() */

  if (result == MrmSUCCESS)
    return result;
  else
    return Urm__UT_Error ("I18NOpenFile", err_stg, NULL, NULL, result);
}

