/* Copyright (C) 1993,1994 by the author(s).
 
 This software is published in the hope that it will be useful, but
 WITHOUT ANY WARRANTY for any part of this software to work correctly
 or as described in the manuals. See the ShapeTools Public License
 for details.

 Permission is granted to use, copy, modify, or distribute any part of
 this software but only under the conditions described in the ShapeTools 
 Public License. A copy of this license is supposed to have been given
 to you along with ShapeTools in a file named LICENSE. Among other
 things, this copyright notice and the Public License must be
 preserved on all copies.
*/
/*
 * ShapeTools/shape program - files.c
 *
 * Author: Axel Mahler (Axel.Mahler@cs.tu-berlin.de)
 *
 * $Header: files.c[8.0] Thu May 19 13:58:14 1994 axel@cs.tu-berlin.de frozen $
 */
#ifndef lint
static char *AtFSid = "$Header: files.c[8.0] Thu May 19 13:58:14 1994 axel@cs.tu-berlin.de frozen $";
#endif

#include <errno.h>
#include "shape.h"

EXPORT char actpath[MYMAXNAMLEN];
EXPORT char dollarplus[MYMAXNAMLEN];
EXPORT char *pathlist[MAXPATHLIST][2];
EXPORT int lastpath = 0;
EXPORT char *curvpath[MAXVPATH] = {NIL,NIL,NIL,NIL,NIL,NIL,NIL,NIL,
		      NIL,NIL,NIL,NIL,NIL,NIL,NIL,NIL};
EXPORT Af_attrs buft;

LOCAL struct linked_list *confid_list;
LOCAL struct rlist_entry {
  struct rules *rule_ptr;
  Bool has_commands;
  char *active_vars;
} *rule_list;

LOCAL int rlist_size = 0, rlist_level = 0;

LOCAL void add_to_pathlist (name, path) char *name, *path; {
  char *namep = strrchr (name, '/');

  if (lastpath >= MAXPATHLIST)
    return;

  if ((pathlist[lastpath][0] = malloc (MYMAXNAMLEN)) == NIL)
    return;
  strcpy (pathlist[lastpath][0], namep ? namep + 1 : name);

  if (!strcmp (path, curvpath[0])) {
    if ((pathlist[lastpath][1] = malloc(3)) == NIL) {
      free (pathlist[lastpath][0]);
      return;
    }
    strcpy (pathlist[lastpath][1], "&$");
  }
  else {
    if ((pathlist[lastpath][1] = malloc(MYMAXNAMLEN)) == NIL) {
      free (pathlist[lastpath][0]);
      free (pathlist[lastpath][1]);
      return;
    }
    strcpy (pathlist[lastpath][1], path);
    strcat (pathlist[lastpath][1], "/");
    strcat (pathlist[lastpath][1], pathlist[lastpath][0]);
  }
  lastpath++;
}

LOCAL Bool is_in_pathlist (raw_name, sought_name, full_path) 
     char *raw_name, *sought_name; Bool full_path; 
{
  register int i;
  
  for(i = lastpath - 1; i >= 0; i--) {
    /* looking in pathlist if file already exists */
    
    if (!strcmp (sought_name, pathlist[i][0])) {
      if (!strcmp (pathlist[i][1], "&$"))
	strcpy (actpath, curvpath[0]);
      else {
	if (pathlist[i][1][0] != '/') {
	  strcpy (actpath, curvpath[0]);
	  strcat (actpath, "/");
	  strcat (actpath, pathlist[i][1]);
	}
	else {
	  strcpy (actpath, pathlist[i][1]);
	}
	if ((sought_name = strrchr (actpath, '/')) != NIL)
	  *sought_name = '\0';
      }
      if(full_path) {
	if (!strcmp (pathlist[i][1], raw_name)) {
	  strcpy (actpath, pathlist[i][1]);
	  if ((sought_name = strrchr (actpath, '/')) != NIL)
	    *sought_name = '\0';
	  return TRUE;
	}
	if (!strcmp (raw_name, curvpath[0]))	{
	  strcpy (actpath, curvpath[0]);
	  return TRUE;
	}
      }
      else {
	if ((pathlist[i][1][0] == '&') && (pathlist[i][1][1] == '$')) {
	  strcpy (actpath, curvpath[0]);
	  return TRUE;
	}
	else {
	  strcpy (actpath, pathlist[i][1]);
	  if ((sought_name = strrchr (actpath, '/')) != NIL)
	    *sought_name = '\0';
	  return TRUE;
	}
      }
      
    } /* end if (!strcmp (sought_name, pathlist[i][0])) */
    
  } /* end for (i = lastpath - 1; i >= 0; i--) */
  return FALSE;
}

/*================ restore list ================== */
/* partially rewritten by andy@cs.tu-berlin.de -- 27. Jan. 92 */

LOCAL struct restoreItem {
  char     *unixName;
  Af_key   restoreKey;
  int      restoreType; /* 0 = local, 1 = vpath, 2 = external */
  struct   restoreItem *next;
} **restoreList; /* need one restore list for each recursion level */

LOCAL int maxResListDepth = 0;

LOCAL void add_to_restorelist (restoreKey, unixName, restoreType)
     Af_key *restoreKey;
     char *unixName;
     int restoreType;
{
  struct restoreItem *currentItem;

  if (depth >= maxResListDepth)
    {
      int i;
      if (maxResListDepth == 0)
	if((restoreList = (struct restoreItem **) malloc (16*sizeof(void *))) == (struct restoreItem **)0)
	  errexit(10,"malloc");
      else
	if((restoreList = (struct restoreItem **) realloc (restoreList, 16*sizeof(void *))) == (struct restoreItem **)0)
	  errexit(10,"realloc");
      for (i=maxResListDepth; i<maxResListDepth+16; i++)
	restoreList[i] = (struct restoreItem *)0;
      maxResListDepth += 16;
    }
	

  if(restoreList[depth] == (struct restoreItem *)0)
    {
      if ((restoreList[depth] =
	   (struct restoreItem *) malloc (sizeof(struct restoreItem)))
	  == (struct restoreItem *)0)
	errexit(10,"malloc");
      currentItem = restoreList[depth];
    }
  else
    {
      currentItem = restoreList[depth];
      while (currentItem->next) currentItem = currentItem->next;
      if ((currentItem->next =
	   (struct restoreItem *) malloc (sizeof(struct restoreItem)))
	  == (struct restoreItem *) 0)
	errexit(10,"malloc");
      currentItem = currentItem->next;
    }
  
  if((currentItem->unixName = malloc((unsigned) (strlen(unixName) + sizeof(char)))) == NIL)
    errexit(10,"malloc");
  strcpy(currentItem->unixName, unixName);
  currentItem->restoreKey = *restoreKey;
  currentItem->restoreType = restoreType;
  currentItem->next = (struct restoreItem *)0;
}

LOCAL void restore_vers (restoreKey, unixName, restoreType)
     Af_key *restoreKey;
     char *unixName;
     int restoreType;
{
  extern int errno;
  FILE *atfs_fp, *tmp_fp;
  char tmpName[PATH_MAX], hiddenName[PATH_MAX];
  char *buf, *at_getfilename();
  Bool busy_exist;
  struct utimbuf rv_time;
  
  if (noexflg)  /* don't execute commands (-n Option) */
    return;

  /* creation of a temporary file for the revision to be restored */
  if (restoreType == 2) /* external */
    strcpy (tmpName, stTmpFile (af_retattr (restoreKey, AF_ATTSPATH)));
  else
    strcpy (tmpName, stTmpFile ("."));

  /* link, unlink, set busy */
  if ((atfs_fp = af_open (restoreKey, "r")) == NULL)
    errexit(10,"af_open");
  
  /* copy old revision into temp. file */
  if ((buf = malloc((unsigned) af_retnumattr (restoreKey, AF_ATTSIZE))) == NIL)
    errexit(10,"malloc");
  fread (buf, sizeof(char), (int) af_retnumattr (restoreKey, AF_ATTSIZE), atfs_fp);
  af_close(atfs_fp);
  
  if ((tmp_fp = fopen (tmpName, "w")) == NULL)
    errexit(10, "fopen; could not create tmpfile");
  
  if(noexpflg)
    fwrite (buf, sizeof(char), (int) af_retnumattr (restoreKey, AF_ATTSIZE), tmp_fp);
  else
    atExpandAttrs (restoreKey, buf, af_retnumattr (restoreKey, AF_ATTSIZE), tmp_fp, 0, 
		   AT_EXPAND_FILE);
  free(buf);
  fclose(tmp_fp);

  /* save busy version into AtFS directory if necessary */

  errno = 0;
  if (access (unixName, R_OK) == 0) { /* unix File already exists */
    /* make name for real version that must be temporarily moved */
    if (restoreType == 2) /* external */
      strcpy (hiddenName, stTmpFile (af_retattr (restoreKey, AF_ATTSPATH)));
    else
      strcpy (hiddenName, stTmpFile (curvpath[0]));
    
    busy_exist = TRUE;

    if (link (unixName, hiddenName) != 0) {
      unlink (tmpName);
      errexit(20, unixName);
    }
    if (unlink (unixName) != 0) {
      unlink (tmpName);
      errexit(21, unixName);
    }
  }
  else {
    hiddenName[0] = '\0';
    busy_exist = FALSE;
  }

  if (link (tmpName, unixName) != 0)
    errexit(22, unixName);
  if (unlink (tmpName) != 0)
    errexit(21, tmpName);
  rv_time.actime = af_rettimeattr (restoreKey, AF_ATTATIME);
  rv_time.modtime = af_rettimeattr (restoreKey, AF_ATTMTIME);
  if (utime (unixName, &rv_time) < 0) {
    perror ("shape -- warning: (restore_vers) can't set modification time.\n");
  }
  register_link (unixName, hiddenName, busy_exist);
  if (af_retnumattr (restoreKey, AF_ATTSTATE) != AF_BUSY)
    busy_done = TRUE;
}

LOCAL Bool already_in_confid (key)
     Af_key *key;
{
  char *name;
  register struct linked_list *clist;

  name = atRetAttr (key, AF_ATTBOUND);

  if (confid_list == (struct linked_list *) NIL) {
    if ((confid_list = (struct linked_list *)
	malloc (sizeof(struct linked_list))) == (struct linked_list *)NIL)
      errexit (10,"malloc");
    if ((confid_list->string = malloc ((unsigned)(strlen (name)+
						 sizeof(char)))) == NIL)
      errexit (10,"malloc");
    confid_list->nextstring = (struct linked_list *) NIL;
    strcpy (confid_list->string, name);
    return FALSE;
  }
  else {
    clist = confid_list;
    while (strcmp (name, clist->string)) {
      if (clist->nextstring != (struct linked_list *) NIL)
	clist = clist->nextstring;
      else
	break;
    }
    
    if (clist->nextstring != (struct linked_list *) NIL)
      return TRUE;
    
    if ((clist->nextstring = (struct linked_list *)
	malloc (sizeof(struct linked_list))) == (struct linked_list *) NIL)
      errexit (10,"malloc");
    if((clist->nextstring->string = 
	malloc ((unsigned) (strlen (name) + sizeof(char)))) == NIL)
      errexit (10,"malloc");
    strcpy (clist->nextstring->string,name);
    clist->nextstring->nextstring = (struct linked_list *) NIL;
    return FALSE;
  }
}


LOCAL void write_confid (key, cid_mode)
     Af_key *key;
     int cid_mode;
{
  char *netPathPrefix, *at;

  if (already_in_confid (key))
    return;

  netPathPrefix = atNetworkPath (key);
  if ((at = strrchr (netPathPrefix, '@'))) {
    *at = '\0';
  }
  
  if (af_retnumattr (key, AF_ATTSTATE) == AF_BUSY) {
    if (cid_mode == 0) {
      fprintf (cid,"\t%s[", atRetAttr (key, AF_ATTUNIXNAME));
      fprintf (cid, "%s], ", atRetAttr (key, AF_ATTMTIME));
      fprintf (cid, "msg (\"warning: using (yet unchanged) busy version of %s.\");\n",
	       atRetAttr (key, AF_ATTUNIXNAME));
      fprintf (cid,"\t%s[busy], ", atRetAttr (key, AF_ATTUNIXNAME));
      fprintf (cid, "msg (\"warning: using busy version of %s (modified since baseline!).\");\n",
	       atRetAttr (key, AF_ATTUNIXNAME));
      fflush (cid);
      fprintf (stderr, "shape - warning: defining unsafe baseline. %s is a busy version.\n",
	       atRetAttr (key, AF_ATTUNIXNAME));
      return;
    }
    else {
      fprintf (cid, "\t%s[%s], ", netPathPrefix, atRetAttr (key, AF_ATTMTIME));
      fprintf (cid, "msg (\"warning: using (yet unchanged) busy version of %s of %s.\");\n",
	       netPathPrefix, atRetAttr (key, AF_ATTMTIME));
      fprintf (cid,"\t%s[busy], ", netPathPrefix);
      fprintf (cid, "msg (\"warning: using busy version of %s (modified since baseline!).\");\n",
	       netPathPrefix);
      fflush (cid);
      fprintf (stderr, "shape - warning: defining unsafe baseline. %s is a busy version.\n",
	       netPathPrefix);
      return;
    }
  }
 
  if (cid_mode == 0)
    fprintf (cid,"\t%s", atRetAttr (key, AF_ATTBOUND));
  else
    fprintf (cid, "\t%s", atNetworkPath (key));


  if (af_retnumattr (key, AF_ATTSTATE) < AF_PUBLISHED) {
    fprintf (cid,",\n\tmsg (\"warning: %s had state",
	     atRetAttr (key, AF_ATTBOUND));
    fprintf (cid, " %s when baselined.\")",
	     atRetAttr (key, AF_ATTSTATE));
    fprintf(stderr,"shape - warning: %s had state",
	     atRetAttr (key, AF_ATTBOUND));
    fprintf (stderr, " %s when baselined.\n",
	     atRetAttr (key, AF_ATTSTATE));
   }
  fprintf (cid,";\n");
  fflush (cid);
}

LOCAL void dump_rules (cidfile) FILE *cidfile; {
  register int i;

  for (i = 0; i < rlist_level; i++) {
    if (rule_list[i].active_vars) {
      char *var_name = stStrtok (rule_list[i].active_vars);
      while (var_name) {
	(void) activate_variant (var_name);
	var_name = stStrtok (NULL);
      }
    }
    dump_rule (cidfile, rule_list[i].rule_ptr, rule_list[i].has_commands);
    if (rule_list[i].active_vars) {
      char *var_name = stStrtok (rule_list[i].active_vars);
      while (var_name) {
	de_activate (var_name);
	var_name = stStrtok (NULL);
      }
    }
  }
}

LOCAL void dump_variant_classes (destination) FILE *destination;
{
  /*
   * prints a list of all known variant class definitions to 
   * the file passed in "destination". This must be an open 
   * FILE. A variat definition is printed in correct Shapefile
   * syntax.
   */

  register int vc_count = 0;

  (void) fputs ("#\n#\tVariant Class Definitions\n#\n\n", destination);
  while (variantClasses[vc_count].name) {
    print_variant_class (destination, &variantClasses[vc_count]);
    vc_count++;
  };
  (void) fputs ("\n", destination);
}

LOCAL void dump_variant_definitions (destination) FILE *destination;
{
  /*
   * print a list of all known variant definitions to the file
   * passed in "destination", which must point to an open FILE.
   * The definitions are printed in proper Shapefile variant 
   * defintion syntax.
   */

  register int vd_count = 0;

  (void) fputs ("#\n#\tVariant Definitions\n#\n\n", destination);

  while (variantDefs[vd_count].name) {
    print_variant_definition (destination, &variantDefs[vd_count]);
    vd_count++;
  };
}
  

LOCAL void dump_variants (cidfile) FILE *cidfile; {
  dump_variant_classes (cidfile);
  dump_variant_definitions (cidfile);
}

LOCAL void free_rlist_entries () {
  register int i;

  for (i = 0; i < rlist_level; i++) {
    if (rule_list[i].active_vars) free (rule_list[i].active_vars);
  }
}

/* not used
 * 
 * LOCAL void add_to_selmacro (fname)
 *      char *fname;
 * {
 *   if(fname[strlen(fname)-1] == ']') {
 *     define_macro ("SELECTED_AtFS", fname, APPEND);
 *   }
 *   else {
 *     define_macro ("SELECTED_FS", fname, APPEND);
 *   }
 * }
 * 
 *
 * LOCAL void notfounditem (dependent)
 *      char *dependent;
 * {
 *   static char depname[MYMAXNAMLEN];
 *   if(strcmp(dependent,depname))
 *     printf("%s\n", dependent);
 *   strcpy(depname,dependent);
 * }
 * 
 * LOCAL void write_select_list (key)
 *      Af_key *key;
 * {
 *   char *filename = atRetAttr (key, AF_ATTBOUND);
 *   if(!nomsgflg)
 *     printf ("%s\n", filename);
 *   add_to_selmacro (filename);
 * }
 */

LOCAL char *path_to_net_id (path) char *path; {
  /*
   * compute the network id of the object referred to by <path>
   * Result is returned in static memory.
   */

  static char net_id[MYMAXNAMLEN], *empty_path = "";
  Af_key *key;

  if (! (path && *path)) return empty_path;

  key = atBindVersion (path, "");
  if (key) {
    (void) strcpy (net_id, atNetworkPath (key));
    af_dropkey (key);
    return net_id;
  }
  else
    return empty_path;
}

LOCAL char *make_repository_id (path) char *path; {
  /*
   * compute the canonical network path name for the object pointed
   * to by <path>.
   * Result is returned in static memory.
   */
  char raw_repository_path[MYMAXNAMLEN], *at, *psuff, vbinding[MYMAXNAMLEN];
  static char cooked_repository_path[MYMAXNAMLEN];

  (void) sprintf (raw_repository_path, "%s/AtFS", path);
  (void) strcpy (raw_repository_path, path_to_net_id (raw_repository_path));
  if (*raw_repository_path) {
    at = strrchr (raw_repository_path, '@');
    psuff = strrchr (raw_repository_path, '/');
    if (at) *at = ' ';
    if (psuff) *psuff = ' ';
    (void) sscanf (raw_repository_path, 
		   "%s AtFS %s", cooked_repository_path, vbinding);
  }
  else {
    (void) strcpy (raw_repository_path, path_to_net_id (path));
    at = strrchr (raw_repository_path, '@');
    if (at) *at = ' ';
    (void) sscanf (raw_repository_path, 
		   "%s %s", cooked_repository_path, vbinding);
  }
  return cooked_repository_path;
}


EXPORT Bool locate_object (name, mode)
     char *name;
     int mode;
{
  /*
   * Try to locate a history or at least a regular file of the specified
   * name. In doing so, consider current vpath. If an object is found
   * write its path-prefix (i.e. not the name, just the path) into the
   * global variable "actpath" and return TRUE. If no object can be
   * found, return FALSE - "actpath" is undefined.
   * "mode" == ALL: check if "name" exists in any of BPOOL or SRC-R-KIVE
   * "mode" == NONE: ???
   * "mode" == SOURCE: check if "name" exists in SRC-R-KIVE and nowhere else
   * "mode" == BINARY: check if "name" exists in BPOOL
   */
  char bname[MYMAXNAMLEN], sysp[MYMAXNAMLEN], lname[MYMAXNAMLEN];
  char current_path[MYMAXNAMLEN];
  register char *px;
  register int i;

#define YES 0
#define NO -1
  int file_exists = NO, 
      file_exists_in_src_rkive, 
      file_exists_in_bpool;

  int ii;
  Af_attrs buf;
  Bool is_in_pathlist(), full_path = FALSE;
  
  if (!name || (name[0] == '\0'))
    return (FALSE);

  sysp[0] = '\0';

  strcpy (lname, name);
  if ((px = strrchr (lname,'/'))) {
    full_path = TRUE;
    *px = '\0';
    px++;
  }
  else
    px = lname;

  if ((mode != BINARY) && is_in_pathlist (lname, px, full_path)) 
    return TRUE;

  strcpy (bname, name);

  if (strrchr (bname, '/')) {
    strcpy (sysp, af_afpath (bname));
  }

  af_initattrs (&buf);
  strcpy (buf.af_name, af_afname (bname));
  strcpy (buf.af_type, af_aftype (bname));

  ii = 1;
  while (curvpath[ii]) ii++;

  i = ii - 1;

  /* "i" is index of last "vpath" element */

  while ( i >= 0 ) {
    if (i) { /* still vpath entries to look through ... */
      strcpy (current_path, expandmacro (curvpath[i]));
      if (!*current_path)
	break;
      if(*current_path != '/') {
	strcpy (buf.af_syspath, curvpath[0]);
	strcat (buf.af_syspath, "/");
	strcat (buf.af_syspath, current_path);
      }
      else
	strcpy (buf.af_syspath, current_path);
    }
    else { /* i == 0 -- curvpath[0] points to build directory */
      if (*sysp)
	strcpy (buf.af_syspath, sysp);
      else
	strcpy (buf.af_syspath, curvpath[0]);
    }
    
    if (mode == ALL) {
      file_exists_in_src_rkive = af_access (buf.af_syspath, buf.af_name, 
					    buf.af_type, AF_CLASS_SOURCE);
      file_exists_in_bpool = af_access (buf.af_syspath, buf.af_name, 
					buf.af_type, AF_CLASS_DERIVED);
      if((file_exists_in_src_rkive == YES) || 
	 (file_exists_in_bpool == YES))
	file_exists = YES;
      else
	file_exists = NO;
    }
    
    if (mode == NONE)
      file_exists = af_access (buf.af_syspath, buf.af_name, 
			       buf.af_type, AF_CLASS_SOURCE);
    
    if (mode == SOURCE) {
      file_exists = af_access (buf.af_syspath, buf.af_name, 
			       buf.af_type, AF_CLASS_DERIVED);
      if (file_exists == NO)
	file_exists = af_access (buf.af_syspath, buf.af_name, 
				 buf.af_type, AF_CLASS_SOURCE);
      else
	file_exists = NO;
    }
    
    if (mode == BINARY)
      file_exists = af_access(buf.af_syspath, buf.af_name, 
			      buf.af_type, AF_CLASS_DERIVED);
    
    if (file_exists == YES) {
      strcpy (actpath, buf.af_syspath);
      if (!full_path)
	add_to_pathlist (bname, buf.af_syspath);
      break;
    }
    i--;
    
  } /* end while ( i >= 0 ) */
  
  return (file_exists == YES);
  
#undef YES
#undef NO
}

EXPORT Bool try_to_bind_version (dep, bind_rule, try_or_bind, bind_result)
     char *dep;
     char *bind_rule;
     Bool try_or_bind;
     BKey *bind_result;
{
  /*
   * locate the repository location of the object named "dep", and
   * attempt to bind it to a version, implied by "bind_rule". If an
   * object can be bound, a pointer to it is stored in the BKey structure.
   * As a global side effect, all attributes of the bound object are stored 
   * in the global attribute buffe "buft".
   */

  Af_key *bound_key, *tmpKey;
  Bool objectExists, object_lives_in_vpath = FALSE,
  object_has_explicit_path = FALSE;
  char *atBindRule;
  int restoreType;

  bind_result->bk_isdefined = FALSE;

  if ((objectExists = locate_object (dep, ALL))) {
    if (strrchr (dep, '/') != NIL) {
      object_has_explicit_path = TRUE;
    }

    atBindExpandMacro = expandmacro;

    atBindRule = malloc (strlen (bind_rule ? bind_rule : "")+2);
    if (!atBindRule) {
      errexit (10, "malloc");
    }
    *atBindRule = '\0';
    strcat (atBindRule, bind_rule);
    strcat (atBindRule, ":");

    chdir (actpath);
    if (D_flag)
      atBindTrace = TRUE;
    bound_key = atBindVersion (dep, atBindRule);
    chdir (curvpath[0]);
    if (bound_key) {
      bind_result->bk_key = *bound_key;
      bind_result->bk_isdefined = TRUE;

      af_allattrs (bound_key, &buft);
      af_freeattrbuf (&buft);

      if (!object_has_explicit_path)
	object_lives_in_vpath = 
	  strcmp (af_retattr (bound_key, AF_ATTSPATH), curvpath[0]);

      if(confid) {
	if(dep[0] == '/')
	  write_confid (bound_key, 1);
	else
	  write_confid (bound_key, 0);
      }
      

      
      /* If this version is found via vpath, we want it to be
       * "restored" to the current directory. Even if it is a
       * busy-version. If the version is non-busy,
       * we have to restore it anyway.
       */
      if (object_lives_in_vpath) {
	restoreType = 1; /* vpath */
      }
      else if (strrchr (dep, '/')) {
	restoreType = 2; /* explicit */
      }
      else
	restoreType = 0; /* local */
      
      if (object_lives_in_vpath ||
	  (af_retnumattr (bound_key, AF_ATTSTATE) != AF_BUSY ) || 
	  (expflg)) {
	add_to_restorelist (bound_key, dep, restoreType);
	return TRUE;
      }
      else {
	return TRUE;
      }
    }
  }

  reset_vclass();
  
  if (!D_flag) { /* tracing already done */
    atBindTrace = TRUE;
    chdir (actpath);
    tmpKey = atBindVersion (dep, atBindRule);
    af_dropkey (tmpKey);
    chdir (curvpath[0]);
  }
  errexit(17,dep);
  

  return FALSE;
}


EXPORT void init_confid (name)
     char *name;
{
  char *hosttype = getenv ("HOSTTYPE"), *now = af_asctime(), *vp;

  if (now && *now) {
    char *nl = strchr (now, '\n');
    if (nl && (*nl == '\n')) *nl = '\0';
  }
  
  fprintf (cid, "########################################");
  fprintf (cid, "########################################\n");
  fprintf (cid, "######\n");
  fprintf (cid, "###### Bound configuration for %s\n", name);
  fprintf (cid, "######\n");
  fprintf (cid, "###### Date of creation: %s\n", now);
  fprintf (cid, "###### Created by: %s\n", atUserName (af_afuser (getuid ())));
  fprintf (cid, "###### With: %s (%s)\n", stProgramName, version());
  fprintf (cid, "######\n");
  fprintf (cid, "###### Hostname: %s.%s", af_gethostname (), af_getdomain ());
  if (hosttype) {
    fprintf (cid, " (a %s computer)\n", hosttype);
  }
  else {
    fprintf (cid, "\n");
  }
  fprintf (cid, "###### Build directory: %s\n", curvpath[0]);
  fprintf (cid, "######\n");
  fprintf (cid, "###### Repository base: %s\n", make_repository_id (curvpath[0]));
  vp = expandmacro ("$(vpath)");
  if (vp && *vp) {
    char *var_repo;
    register char *c;

    c = strchr (vp, ':');
    while (c) {
      *c = ' ';
      c = strchr (vp, ':');
    }
    var_repo = stStrtok (vp);
    while (var_repo) {
      fprintf (cid, "######             %s\n", make_repository_id (var_repo));
      var_repo = stStrtok (NULL);
    }
  }
  fprintf (cid, "######\n");
  if (cmd_line_vars[0]) {
    register int cmdlv_count = 0;
    fprintf (cid, "###### The following variants were requested from the\n");
    fprintf (cid, "###### command line: %s", cmd_line_vars[cmdlv_count]);
    cmdlv_count++;
    while (cmd_line_vars[cmdlv_count]) {
      fprintf (cid, ", %s", cmd_line_vars[cmdlv_count]);
      cmdlv_count++;
    }
    fputs ("\n", cid);
  }
  fprintf (cid, "######\n");
  fprintf (cid, "###### To rebuild a program according to this bound\n");
  fprintf (cid, "###### configuration, issue the command:\n");
  fprintf (cid, "######\n");
  fprintf (cid, "######       shape -rebuild %s\n", name);
  fprintf (cid, "######\n");
  fprintf (cid, "########################################");
  fprintf (cid, "########################################\n\n");
  fprintf (cid, "vpath = %s\n", vp);
  fprintf (cid,"\n");
  fprintf (cid,"bound_configuration_thread:-\n");
}


#define RLSIZE 1024

EXPORT void add_rule_to_confid (rule)
     struct rules *rule;
{
  register int i;
  char *active_vars;

  if (rule == NULL) return;

  /* if rule is already in list and variant context is the same, do nothing */
  active_vars = active_variants ();
  for (i = 0; i < rlist_level; i++) {
    if (rule_list[i].rule_ptr == rule) 
      if (!strcmp (rule_list[i].active_vars, active_vars)) return;
  }

  /* if list is full, expand it */
  if (rlist_level == rlist_size) {
    if (rlist_size == 0) {
      rule_list = (struct rlist_entry *) malloc (RLSIZE * sizeof (struct rlist_entry));
      if (!rule_list) errexit (10, "malloc");
      rlist_size = RLSIZE * sizeof (struct rlist_entry);
    }
    else {
      struct rlist_entry *tmp = (struct rlist_entry *) 
	realloc (rule_list, 2 * rlist_size);
      if (!tmp) errexit (10, "realloc");
      rule_list = tmp;
      rlist_size *= 2;
    }
  }
  
  /* register rule */
  rule_list[rlist_level].rule_ptr = rule;
  rule_list[rlist_level].active_vars = active_vars ? 
    check_strdup (active_vars) : NULL;
  rule_list[rlist_level++].has_commands = (rule->cmdlist != NULL);
}

EXPORT void finish_confid()
{
  fprintf (cid,"\t.\n");
  fprintf (cid,"\n");
  dump_variants (cid);
  dump_rules (cid);
  free_rlist_entries ();
  if (rule_list) free (rule_list);
  rlist_size = 0;
  rlist_level = 0;
}


EXPORT FILE *cmfopen( filename, mode, major_version,minor_version)
     char *filename;
     char *mode;
     /*ARGSUSED*/
     int major_version;
     int minor_version;
{
  char syspath[MYMAXNAMLEN];
  Af_key key;
  FILE *fp;

  if (strrchr(filename,'/') == NIL)
    strcpy(syspath,curvpath[0]);
  else
    strcpy(syspath,af_afpath(filename));
  
  if(af_crkey(syspath, af_afname(filename), af_aftype (filename),&key) == -1)
    errexit(10,"af_crkey");
  else
    {
      fp = af_open(&key, mode);
      return (fp);
    }
  return((FILE *) NIL);
}

EXPORT void restore_all_vers()
{
  struct restoreItem *currentItem;
  sb_ptr restored_objs_macro = sbnew (512);

  if (depth >= maxResListDepth) return;

  currentItem = restoreList[depth];
  while(currentItem != (struct restoreItem *)0) {
    sbcat (restored_objs_macro, currentItem->unixName, strlen (currentItem->unixName));
    sbcat (restored_objs_macro, " ", 1);
    restore_vers (&currentItem->restoreKey, currentItem->unixName, currentItem->restoreType);
    currentItem = currentItem->next;
  }
  define_macro ("RESTORED_OBJS", sbstr (restored_objs_macro), DYNAMIC);
  sbfree (restored_objs_macro);
}

EXPORT char *restored_object_id (unixname) char *unixname; {
  /*
   * Check whether an object with name "unixname" is in the restorelist, 
   * return a string giving the actual location and id of the object.
   * The returned string format is as follows: if an object lives 
   * somewhere in a vpath-subdirectory, the relative (to the build-dir)
   * pathname of the object is returned. To obtain the relative path,
   * we use the object's absolute path and skip the part of it that is
   * made up by the current directory's absolute path (plus the remaining
   * slash). This solution works only if vpath is actually a subdirectory
   * of the current dir.
   * If the object is a busy-file, just the object's name is used. If it's
   * a real version, bound version notation is used.
   */
  static char roid[MYMAXNAMLEN];
  struct restoreItem *currentItem;

  if (!(unixname && *unixname))
    return (char *)NULL;

  if (depth >= maxResListDepth)
    return unixname;

  currentItem = restoreList[depth];
  while(currentItem != (struct restoreItem *)0) {
    if (strcmp (unixname, currentItem->unixName) == 0) {
      Af_key *rkey = &currentItem->restoreKey;
      Bool is_busy = af_retnumattr (rkey, AF_ATTSTATE) == AF_BUSY;

      switch (currentItem->restoreType) {
      case REGULAR:
	strcpy (roid, af_retattr (rkey, is_busy ? AF_ATTUNIXNAME : AF_ATTBOUND));
	break;
      case VPATH:
	strcpy (roid, af_retattr (rkey, AF_ATTSPATH) + strlen (curvpath[0]) + 1);
	strcat (roid, "/");
	strcat (roid, af_retattr (rkey, is_busy ? AF_ATTUNIXNAME : AF_ATTBOUND));
	break;
      case EXTERNAL:
	strcpy (roid, af_retattr (rkey, AF_ATTSPATH));
	strcat (roid, "/");
	strcat (roid, af_retattr (rkey, is_busy ? AF_ATTUNIXNAME : AF_ATTBOUND));
	break;
      default:
	fprintf (stderr, "internal error: wrong switch in `restored_object_id'.\n");
	return (char *)NULL;
      }
      return roid;
    }
    currentItem = currentItem->next;
  }
  return unixname;
}

EXPORT void drop_restorelist ()
{
  struct restoreItem *currentItem, *lastItem;

  if (depth >= maxResListDepth) return;

  currentItem = restoreList[depth];
  while(currentItem != (struct restoreItem *)0)
    {
      free (currentItem->unixName);
      lastItem = currentItem;
      currentItem = currentItem->next;
      free (lastItem);
    }
  restoreList[depth] = (struct restoreItem *)0;
}
