/* 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 - produce.c
 *
 * Author: Axel Mahler (Axel.Mahler@cs.tu-berlin.de)
 *
 * $Header: produce.c[8.1] Wed Jun  8 13:09:06 1994 axel@cs.tu-berlin.de frozen $
 */
#ifndef lint
static char *AtFSid = "$Header: produce.c[8.1] Wed Jun  8 13:09:06 1994 axel@cs.tu-berlin.de frozen $";
#endif

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

#define MAXCMDLENGTH 10240

LOCAL Bool error_happened = FALSE, simple_done = FALSE;
LOCAL time_t touch_time;

EXPORT Bool error_occ = FALSE;
EXPORT Bool no_comm_subst = FALSE;

typedef enum { Ok, Fail, Abort } TargetCode;
/* used by make_target */

EXPORT char rbrule[64];
EXPORT char rbtarg[64];
EXPORT char ruleset[32];
EXPORT Bool busy_done = FALSE;
EXPORT int depth;
LOCAL  Bool reallydone = FALSE;

#define IMPLICIT 0
#define EXPLICIT 1

LOCAL void mark_all_done(cur)
     struct rules *cur;
{
  struct rules *c;
  int i;
  if (is_pattern (cur->name))
    return;
  if(cur->done == rec_do_depth)
    return;
  cur->done++;
  i = cur->done;
  c = cur->next;
  while((c != (struct rules *) NIL) &&
	(c->done < i))
    {
      if(simple_done)
	c->done++;
      else
	{
	  if(locate_object (c->name, ALL))
	    c->done++;
	}
      c = c->next;
    }
}

LOCAL char *cooked_dep (current, base, nth, depkind)
     struct rules *current;
     char *base;
     int nth, depkind;
{
  static char curdep[MYMAXNAMLEN];
  char *this_dependent;

  if (*(this_dependent = get_dep (current, nth))) {
    if (depkind == IMPLICIT) {
      char *percent = stem (current->name, base);
      (void) strcpy (curdep, subst_char (this_dependent, '%', percent));
    }
    else 
      strcpy (curdep, this_dependent);
  }
  else
    *curdep = '\0';
  return curdep;
}

LOCAL char *name_prefix (full_name) char *full_name; {
  /*
   * Treat "raw_name" as some sort of object_ID (e.g. pathname, bound
   * version ID etc.) and try to cut out the name prefix. If the name
   * prefix is undefined, an empty string will be returned. The found
   * prefix otherwise.
   */
  static char this_prefix[MYMAXNAMLEN];
  char raw_name[MYMAXNAMLEN];
  char *last_slash, *last_dot, *last_open_bracket;

  this_prefix[0] = '\0';

  if (full_name) {
    strcpy (raw_name, full_name);
    last_slash = strrchr (raw_name, '/');
    last_dot = strrchr (raw_name, '.');
    last_open_bracket = strrchr (raw_name, '[');
    
    if (last_open_bracket && (last_open_bracket > last_slash)) {
      /* assume bound version notation */
      *last_open_bracket = '\0';
      last_dot = strrchr (raw_name, '.');
    }
    /* now we have a regular filename */

    if ((last_dot > raw_name) && (last_dot > last_slash))
      /* We do have a real prefix ... */
      *last_dot = '\0';

    strcpy (this_prefix, last_slash ? last_slash + 1 : raw_name);
  }
  return this_prefix;
}

LOCAL void reset_pathlist()
{
  register int i;
  for (i = 1; pathlist[i][0] != NIL; i++)
    {
      pathlist[i][0] = NIL;
      pathlist[i][1] = NIL;
      lastpath = 1;
    }
  if (pathlist[0][1] != NIL)
    {
      if (!strcmp(pathlist[0][1],"&$"))
	{
	  pathlist[0][0] = NIL;
	  pathlist[0][1] = NIL;
	  lastpath = 0;
	}
    }
}

LOCAL void cl_variants (vdepth, vcuri)
     int vdepth, vcuri;
{
  register int vardepi;

  vcuri--;
  for (vardepi = vdepth; vardepi >= 0; vardepi--) {
    de_activate (curvar[vcuri-vardepi]);
    strcpy (curvar[vcuri-vardepi], "");
    reset_pathlist ();
  }
}

LOCAL char *expand_command (cmd, cur, rel_dep)
     char *cmd;
     struct rules *cur;
     char *rel_dep;
{
  char curdep[512], hhh[64], hhh2[64];
  char comm[MAXCMDLENGTH], *common_prefix, *p, *hhhp, *hhh2p;
  register int i = 0, j = 0, k = 0;

  while(cmd[i] != '\0')
    {
      if((cmd[i] == '\\') && (cmd[i+1] == '`'))
	i++;
      if ((cmd[i] == '\\') && (cmd[i+1] == '%')) {
	comm[j++] = cmd[++i];
	i++;
      }
      if((cmd[i] != '$') && (cmd[i] != '%'))
	{
	  comm[j] =  cmd[i];
	  i++;
	  j++;
	}
      else
	{
	  switch (cmd[i+1])
	    {
	    case '@':
	      if (!is_pattern (cur->name))
		{
		  comm[j] = '\0';
		  strcat(comm, cur->name);
		  j = j + strlen(cur->name);
		  i = i + 2;
		}
	      else
		{
		  comm[j] = '\0';
		  strcpy(hhh,rel_dep);
		  hhhp = strrchr(hhh,'.');
		  if (hhhp != NIL)
		    {
		      hhhp[0] = '\0';
		    }
		  strcpy(hhh2, cur->name);
		  hhh2p = strrchr(hhh2,'.');
		  if (hhh2p)
		    strcat(hhh,hhh2p);
		  strcat(comm, hhh);
		  j = j + strlen(hhh);
		  i = i + 2;
		}
	      
	      break;
	    case '#':
	      {
		char target_name_prefix[MYMAXNAMLEN];
		struct rules *use_rule;

		if (is_pattern (cur->name))
		  use_rule = cur;
		else
		  use_rule = implicit_target (cur->name);

		strcpy (target_name_prefix, name_prefix (rel_dep));

		k = 1;
		if (use_rule) {
		  strcpy (curdep, cooked_dep (use_rule,
			         rel_dep, 0, IMPLICIT));
		}
		else {
		  strcpy (curdep, get_dep (cur, 0));
		}
		while (*curdep) {
		  comm[j] = '\0';
		  if((curdep[0] != '+') && (!atBindTestRule(curdep))) {
		    char *roid;
		    roid = restored_object_id (curdep);
		    if ((k > 1) && roid)
		      strcat (comm, " ");
		    strcat (comm, roid ? roid : "");
		    j = j + (roid ? (strlen (roid) + ((k > 1) ? 1 : 0)) : 0);
		  }
		  if (use_rule) {
		    strcpy (curdep, cooked_dep (use_rule, 
					     rel_dep, k++, IMPLICIT));
		  }
		  else {
		    strcpy (curdep, get_dep (cur, k++));
		  }
		}
	      }
	      i = i + 2;
	      break;
	    case '?':
	      if (*get_dep(cur,0))
		{
		  Bool target_is_pattern = is_pattern (cur->name);
		  k = 1;
		  strcpy(curdep, cooked_dep (cur, 
			     target_is_pattern ? rel_dep :
						    NULL, 0, 
						    target_is_pattern ?
						    IMPLICIT : EXPLICIT));
		  while (*curdep) {
		    comm[j] = '\0';
		    if ((curdep[0] != '+') && (!atBindTestRule (curdep))) {
		      strcat (comm, curdep);
		      j = j + strlen (curdep);
		    }
		    strcpy (curdep, cooked_dep (cur, 
			     target_is_pattern ? rel_dep :
						       NULL, k++,
						       target_is_pattern ?
						       IMPLICIT: EXPLICIT));
		    if (*curdep) {
		      strcat(comm," ");
		      j++;
		    }
		  }
		}
	      i = i + 2;
	      break;
	    case '<':
	      if (*get_dep(cur,0))
		{
		  Bool target_is_pattern = is_pattern (cur->name);
		  k = 1;
		  strcpy(curdep, cooked_dep (cur, 
			     target_is_pattern ? rel_dep :
						    NULL, 0, 
						    target_is_pattern ?
						    IMPLICIT : EXPLICIT));
		  while (*curdep) {
		    comm[j] = '\0';
		    if ((curdep[0] != '+') && (!atBindTestRule (curdep))) {
		      strcat (comm, curdep);
		      j = j + strlen(curdep);
		      break;
		    }
		    strcpy (curdep, cooked_dep (cur, 
			     target_is_pattern ? stem (cur->name, rel_dep) :
						       NULL, k++,
						       target_is_pattern ?
						       IMPLICIT: EXPLICIT));
		  }
		}
	      i = i + 2;
	      break;
	    case '*':
	    case '.':
	    case ' ':
	      if (!is_pattern (cur->name)) {
		if ((common_prefix = 
		     malloc((unsigned) (strlen(cur->name) + 1))) == NIL)
		  errexit(10,"malloc");
		strcpy(common_prefix, cur->name);
		if ((p = strrchr(common_prefix,'.')) != NIL) {
		  p[0] = '\0';
		}
		comm[j] = '\0';
		strcat(comm, common_prefix);
		j = j + strlen(common_prefix);
		if (cmd[i+1] == '*')
		  i = i + 2;
		else
		  i++;
	      }
	      else {
		char *st = stem (cur->name, rel_dep);
		comm[j] = '\0';
		strcat(comm, st);
		j += st ? strlen(st) : 0;
		i += (cmd[i+1] == '*') ? 2 : 1;
	      }
	      
	      break;
	    case '(':
	      errexit(99,"output translation $(name:str1=str2)");
	      /* ???? output translation, not yet implemented */
	      break;
	    case '$':
	      comm[j] = '$';
	      j++;
	      i = i+2;
	      break;
	    default:
	      if(cmd[i] == '%')
		/* single suffix rule */
		{
		  char *st = stem (cur->name, rel_dep);
		  comm[j] = '\0';
		  strcat(comm, st);
		  j += st ? strlen(st) : 0;
		  i += (cmd[i+1] == '*') ? 2 : 1;
		}
	      else
		{
		  comm[j] = cmd[i];
		  j++;
		  i++;
		}
	      break;
	    }
	}
    }
  comm[j] = '\0';
  if (comm[0] == '\0')
    strcpy(comm,cmd);
  return(comm);
}

LOCAL void execute(cmd, itp) char *cmd, *itp; {
  int retcode = 0;
  Bool ignflg = FALSE, silflg = FALSE;
  char *rc;

  silflg = silentflg;
  while (isspace(*cmd)) cmd++;

  if (!*cmd) return; /* empty commands aren't executed */

 again:  
  switch (*cmd) {
  case '@':
    silflg = TRUE;
    cmd++;
    goto again;
  case '-':
    ignflg = TRUE;
    cmd++;
    goto again;
  default:
    if (noexflg && !shape_command) {
      printf("%s\n", cmd);
      fflush(stdout);
      reallydone = TRUE;
      shape_command = FALSE;
      return;
    }
  }
  /*    printf("malloc_verify: %d\n", malloc_verify()); */
  
  if (!silflg || ((noexflg))) {
    printf("%s%s\n", noexflg ? "" : "shape - executing: ", cmd);
    fflush(stdout);
  }
  
  if (!noexflg || shape_command) {
    reallydone = TRUE;
    retcode = stCallCmd (itp, cmd);
    if (retcode < 0) {
      switch (retcode) {
      case CMDPROC_EMPTY:
	stLog ("No Shell", ST_LOG_ERROR);
	break;
      case NO_MORE_CORE:
	stLog ("Not enough memory", ST_LOG_ERROR);
	break;
      case FORK_FAILED:
	stLog ("Couldn't fork", ST_LOG_ERROR);
	break;
      case PIPE_FAILED:
	stLog ("Couldn't open pipe to command interpreter", ST_LOG_ERROR);
	break;
      case WAIT_ERROR:
	stLog ("Couldn't wait for command", ST_LOG_ERROR);
	break;
      case EXEC_FAILED:
	stLog ("Unable to load command interpreter", ST_LOG_ERROR);
	break;
      case CHILD_KILLED:
	stLog ("Child process died from an accident", ST_LOG_ERROR);
	break;
      case WRITE_FAILED:
	stLog ("Command interpreter doesn't read", ST_LOG_ERROR);
	break;
      case NO_PROGRAM:
	stLog ("Couldn't find command interpreter", ST_LOG_ERROR);
	break;
      }
    }
    simple_done = TRUE;
    if (retcode != 0) {
      if(ignflg == FALSE) {
	error_happened = TRUE;
	error_occ = TRUE;
      }
      if ((rc = malloc (10 * sizeof(char))) == NIL)
	errexit(10,"malloc");
      sprintf (rc, "%d", retcode); 
      if (!(ignflg || goflg))
	errexit(13,rc);
    }
  }
  shape_command = FALSE;
}

LOCAL Bool execute_commands (current, rel_dep, q, made_key)
     struct rules *current;
     char *rel_dep;
     DepQ *q;
     BKey *made_key;
{
  register struct cmds *curcmd;
  char *expcmd, x_command[MAXCMDLENGTH], fname[MYMAXNAMLEN],
       command_processor[MYMAXNAMLEN];

  if (rel_dep) 
    strcpy (fname, rel_dep);

  if (current)
    curcmd = current->cmdlist;

  if (curcmd) {
    restore_all_vers ();
    strcpy (command_processor, expandmacro ("$(SHELL)"));
    while (curcmd) {
      if (curcmd->command) {
	no_comm_subst = TRUE;
	strcpy (x_command, expandmacro (curcmd->command));
	no_comm_subst = FALSE;
	if ((expcmd = malloc (MAXCMDLENGTH)) == NIL)
	  errexit(10,"malloc");
	strcpy (expcmd, expand_command (x_command, current, fname));
	execute (expcmd, command_processor);
	free (expcmd);
      }
      else
	break;
      curcmd = curcmd->nextcmd;
    }

    if (simple_done) {
      mark_all_done (current);
      simple_done = FALSE;
    }
    save_targets (current, fname, q, made_key, touch_time);
  }

  cleanup_links (link_reg);
  free_linklist ();
  link_reg = (struct linkreg *) NIL;

  return TRUE;
}

LOCAL Bool try_recache (do_name, do_key, dep_rule, key_from_cache)
     char *do_name, *do_key;
     struct rules *dep_rule;
     BKey *key_from_cache;
{
  Af_set bset;
  Af_attrs buf;
  Af_key bkey, key1, restorekey;
  int object_is_cached, busy_version_exists;
  char type[MYMAXNAMLEN], sysp[MYMAXNAMLEN], *retrv_attr;

  key_from_cache->bk_isdefined = FALSE;
  if (!locate_object (do_name, ALL))
    return FALSE;

  /*
   * This is handling of the special case, when a target (pseudo
   * or real) depends on nothing but a commandlist. In this case
   * reproduction of the target must be performed unconditionally.
   * The resulting target (in case it is a file) is stored in the 
   * BPOOL (should be avoided) with an empty attribute. We have to
   * avoid restoration from BPOOL and have to set the existing 
   * file to non-current.
   */
  if ((do_key == NIL) || (*do_key == '\0')) {
    if ((dep_rule->deplist == (char *)NIL) && 
	(dep_rule->cmdlist != (struct cmds *)NIL))
      return FALSE;
  }

  af_initattrs(&buf);
  
  buf.af_gen = AF_BUSYVERS;
  buf.af_rev = AF_BUSYVERS;

  strcpy (type, af_aftype (do_name));
  strcpy (sysp, af_afpath (do_name));
  if(sysp[0] != '\0')
    strcpy (buf.af_syspath, sysp);
  else
    strcpy (buf.af_syspath, curvpath[0]);

  strcpy (buf.af_name, af_afname (do_name));
  strcpy (buf.af_type, af_aftype (do_name));

  if (forceflg) {
    if (is_in_forcelist (do_name)) {
      if (D_flag)
	printf ("shape - must reshape %s (caused by -force)\n", do_name);
      return FALSE;
    }
  }

  if (D_flag)
    printf ("shape - recache %s:\n  looking for key: %s.\n\n", 
	    do_name, do_key);

  busy_version_exists = (af_getkey (buf.af_syspath, af_afname (do_name),
				    af_aftype (do_name), AF_BUSYVERS, 
				    AF_BUSYVERS, &key1) > -1);

  if (busy_version_exists && (retrv_attr = af_retattr (&key1, ATTRNAME))) {
    if (stk_matchuda (retrv_attr, do_key)) {
      /*
       * Double-check, if this is really the busy version we need !
       * If the derived object has been rederived with other tools
       * than shape (e.g. make), the derived object's contents may 
       * have changed but the derivation key is still the same...
       * To make things sure we check the busy version's mtime against
       * the mtime that was stored in the cachekey when the object was 
       * created..
       */
      char *ap = af_retattr (&key1, AT_ATTCACHEKEY);
      Af_attrs busy_attrs;
      if (ap && (af_allattrs (&key1, &busy_attrs) > -1)) {
	char *auxp = strchr (ap, '.');
	if (auxp) *auxp = '\0';
	if (busy_attrs.af_mtime == atol (ap)) {
	  key_from_cache->bk_key = key1;
	  key_from_cache->bk_isdefined = TRUE;
	  af_freeattrbuf (&busy_attrs);
	  free (retrv_attr);
	  free (ap);
	  if (D_flag)
	    printf ("shape - recache %s:\n  got the busyversion.\n\n", 
		    do_name);
	  return TRUE;
	}
      }
      if (ap) free (ap);
    }
    if (retrv_attr) free (retrv_attr);
  }
  
  /* 
   * ASSERT: Either there is no un-cached busyversion, or it has not
   *         the attributes we're looking for.
   */

  buf.af_gen = AF_NOVNUM;
  buf.af_rev = AF_NOVNUM;

  if ((retrv_attr = malloc ((unsigned)(strlen (do_key) + 
				       strlen (ATTRNAME) + 2))) == NIL)
    errexit(10,"malloc");

  strcpy (retrv_attr, ATTRNAME);
  strcat (retrv_attr, "=");
  strcat (retrv_attr, do_key);

  buf.af_udattrs[0] = retrv_attr;
  buf.af_udattrs[1] = NIL;

  if ((object_is_cached = af_cachefind (&buf, &bset)) == -1) 
    errexit (10, "af_cachefind");
  
  if (!object_is_cached) {
    if (af_dropset (&bset) == -1)
      errexit (10, "af_dropset");
    free (retrv_attr);

    if (D_flag) {
      printf ("shape - recache %s:\n  failed!\n\n", do_name);
      printf ("shape - recache %s: the derived object cache is:\n", do_name);
      buf.af_udattrs[0] = NIL;
      if ((object_is_cached = af_cachefind (&buf, &bset)) == -1) {
	printf ("  unaccessible. This is an error!\n\n");
      }
      else {
	int i, nkeys = af_nrofkeys (&bset);
	if (nkeys == 0)
	  printf ("  empty.\n\n");
	else {
	  for (i = 0; i < nkeys; i++) {
	    char *a;
	    af_setgkey (&bset, i, &bkey);
	    a = af_retattr (&bkey, ATTRNAME);
	    printf ("  %s\n", a);
	    free (a);
	    af_dropkey (&bkey);
	  }
	  af_dropset (&bset);
	  printf ("\n");
	}
      }
    }
    if (busy_version_exists && (af_dropkey (&key1) == -1))
      af_perror ("try_recache");
    return FALSE;
  }
  else {
    if (D_flag)
      printf ("shape - recache %s:\n  got one!\n\n", do_name);

    if (af_setgkey (&bset, 0, &bkey) == -1)
      errexit (10,"af_setgkey");
    if (!noexflg) {
      if (af_restore (&bkey, &restorekey) == -1)
	errexit (10,"af_restore");
      printf ("... %s%s%s[%s] restored from derived object cache\n",
		  buf.af_name, (buf.af_type && buf.af_type[0]) ? "." : "", 
		  buf.af_type ? buf.af_type : "", 
	      af_retattr (&bkey, AT_ATTCACHEKEY));
    }
    else
      printf ("... %s%s%s[%s] found in derived object cache (not restored)\n",
	      buf.af_name, (buf.af_type && buf.af_type[0]) ? "." : "",
	      buf.af_type ? buf.af_type : "",
	      af_retattr (&bkey, AT_ATTCACHEKEY));

    key_from_cache->bk_key = bkey;
    key_from_cache->bk_isdefined = TRUE;
    if (af_dropset (&bset) == -1)
      errexit (10, "af_dropset");
    if (buf.af_udattrs[0])
      free (buf.af_udattrs[0]); /* implicitly freeing "retrv_attr" */
    return TRUE;
    }
}

LOCAL Bool abort_this_try (targ) char *targ; {
  char messg[80];

  if (goflg) {
    sprintf (messg, "shape - don't know how to shape %s",
		    targ);
    warning (0, messg);
    return FALSE;
  }
  errexit (3, targ);

  /* NOTREACHED */
  return FALSE;
}

static TargetCode make_target();

LOCAL Bool try_to_make_target (targ, sr, force, made_object_key) 
     char *targ;
     char *sr;
     Bool force;
     BKey *made_object_key;
{

#ifdef DEBUG_MALLOC
      dssize ("In try_make, ", targ);
#endif

  switch (make_target (targ, sr, made_object_key)) {
  case Ok:
    return TRUE;
  case Fail:
    return FALSE;
  case Abort:
    return abort_this_try (targ);
  }
  return FALSE;
}

LOCAL TargetCode make_target (targ, sr, made_object_key)
     char *targ;
     char *sr;
     BKey *made_object_key;
{
  register struct rules *current;
  char *lastcurdep = NULL;
  char *curdep = NULL, *curselrule = NULL;
  char *this_dependent;
  Bool err_happ = FALSE;
  Bool lbusy_done = FALSE;
  BKey bkey;
  DepQ *new_DepQ(), *deriv_key_deps = new_DepQ();
  int dep = 0, var_depth = -1;

# define free_depvars { if (curdep) free (curdep); \
			  if (curselrule) free (curselrule); \
			    if (lastcurdep) free (lastcurdep); }

  lastcurdep = check_strdup ("");
  made_object_key->bk_isdefined = FALSE;
  if (already_in_done_list (targ, &made_object_key->bk_key)) {
    made_object_key->bk_isdefined = TRUE;
    drop_dependent_queue (deriv_key_deps);
    free_depvars;
    return Ok;
  }

  if (depth > MAXDEPTH)  {
    drop_dependent_queue (deriv_key_deps);
    warning (7, targ);
    free_depvars;
    return Fail;
  }

  depth++;
  if (!(current = get_target (targ))) {
    /* 
     * We don't have a rule to produce "targ", so it must be
     * a source object. If not - give up !
     */
    if (!locate_object (targ, ALL)) {
      depth--;
      drop_dependent_queue (deriv_key_deps);
      free_depvars;
      return Abort;
    }
    else {
      if (strcmp (targ, lastcurdep)) {
	if (try_to_bind_version (targ, sr, BIND_IT, &bkey)) {
	  free (lastcurdep);
	  lastcurdep = check_strdup (targ);
	  depth--;
	  drop_dependent_queue (deriv_key_deps);
	  free_depvars;
	  return Ok;
	}
	else {
	  if (D_flag)
	    D_debug (targ, NIL, NIL);
	  depth--;
	  drop_dependent_queue (deriv_key_deps);
	  free_depvars;
	  return Fail;
	}
      }
      else {
	depth--;
	drop_dependent_queue (deriv_key_deps);
	free_depvars;
	return Fail;
      }
    }
  }
  
  /**/
  /* ASSERT: current != NIL ; "targ" is name of a derived object */
  /**/

  dep = 0;
  
  lbusy_done = busy_done;
  
  /*
   * Now, all the dependents of the current target are examined and 
   * requested.
   */
  curselrule = check_strdup (sr);
  reset_dep_list (current);

  if (confid) {
    add_rule_to_confid (current);
  }

  if (*(this_dependent = get_dep (current, dep))) {
    if (atBindTestRule (this_dependent)) {
      if((!ruleflg) || (depth > 1)) {
	free (curselrule);
	curselrule = check_strdup (this_dependent);
      }
      dep++;
    }

    while (is_varname (this_dependent = get_dep (current, dep))) {
      var_depth += activate_variant (this_dependent);
      dep++;
    }
  }
  
  curdep = check_strdup (get_dep (current, dep));
  if (*curdep)
    dep++;
    
  /*
   * skip bogus variant activations (coming from empty expanding var macros)
   */
  while (!strcmp (curdep, "+"))
    if (*get_dep (current, dep)) {
      free (curdep);
      curdep = check_strdup (get_dep (current, dep++));
    }
    else
      *curdep = '\0';

  if (is_pattern (current->name)) {
    dep--;
    free (curdep);
    curdep = check_strdup (cooked_dep (current, targ, dep++, IMPLICIT));
  }
  else if ((current->cmdlist == (struct cmds *)NIL) ||
	   (current->cmdlist->command == (char *)NIL)) {
    add_defaults (targ, current);
    if (!*curdep) {
      free (curdep);
      curdep = check_strdup (get_dep (current, dep++));
    }
  }

  while (*curdep) {
    struct rules *dep_is_derivable = get_target (curdep);
    Bool dep_exists = locate_object (curdep, ALL);
    
    if (!strcmp (curdep, lastcurdep))
      goto next_dep;
      
    if (!dep_is_derivable && dep_exists && (strcmp (curdep, lastcurdep))) {
      free (lastcurdep);
      lastcurdep = check_strdup (curdep);

#ifdef DEBUG_MALLOC
      dssize ("Vor bind_version, ", curdep);
#endif

      try_to_bind_version (curdep, curselrule, BIND_IT, &bkey);

#ifdef DEBUG_MALLOC
      dssize ("Nach bind_version, ", curdep);
#endif

      if (bkey.bk_isdefined)
	append_dependent (&bkey.bk_key, deriv_key_deps);
      else {
	try_to_make_target (curdep, sr, TRUE, &bkey);
	/* basically produces an Abort! */
	depth--;
	free_depvars;
	return Abort;
      }
    }
    else {
      if (dep_is_derivable && try_to_make_target (curdep, curselrule, TRUE, &bkey)) {
	if (error_happened) {
	  err_happ = TRUE;
	  error_happened = FALSE;
	}
	if (bkey.bk_isdefined)
	  append_dependent (&bkey.bk_key, deriv_key_deps);
      }
      else {
	abort_this_try (curdep);
	depth--;
	cl_variants (var_depth, variants_active);
	drop_dependent_queue (deriv_key_deps);
	mark_all_done (current);
	free_depvars;
	return Fail;
      }
    }

  next_dep:
    free (curdep);
    curdep = check_strdup (cooked_dep (current, targ, dep++,
				       is_pattern (current->name) ?
				       IMPLICIT : EXPLICIT));
  } /* end while (*curdep) */

  /*
   * At this point all dependents of "targ" are either bound to 
   * an appropriate source version or have been derived. Now we
   * can see whether "targ" needs to be made.
   */

  mark_all_done (current);
    
  if (err_happ) {
    drop_dependent_queue (deriv_key_deps);
    cl_variants (var_depth, variants_active);
    depth--;
    free_depvars;
    return Fail;
  }

  if (locate_object (targ, ALL) &&
      try_recache (targ, make_derivation_key (current, deriv_key_deps), 
		   current, made_object_key)) {
    add_to_done_list (targ, &made_object_key->bk_key);
    drop_dependent_queue (deriv_key_deps);
    drop_restorelist ();
    cl_variants (var_depth, variants_active);
    depth--;
    free_depvars;
    return Ok;
  }
  else {
    if (D_flag)
      printf ("shape - initially making %s\n", targ);
    if (execute_commands (current, targ, deriv_key_deps,
			  made_object_key)) {
      drop_dependent_queue (deriv_key_deps);
      drop_restorelist ();
      cl_variants (var_depth, variants_active);
      depth--;
      free_depvars;
      return Ok;
    }
    else {
      drop_dependent_queue (deriv_key_deps);
      cl_variants (var_depth, variants_active);
      depth--;
      free_depvars;
      return Fail;
    }
  }
}

EXPORT void produce ()
{
  register int k = 0;
  char *comm;
  BKey produced_object;
  char *initial_selection = "";
  int tfd = open ("...__tt", O_RDONLY | O_CREAT, S_IRUSR);
  struct stat sbuf;

  touch_time = time ((time_t *)0); /* used in execute_commands */
  if (tfd >= 0) {
    unlink ("...__tt");
    fstat (tfd, &sbuf);
    close (tfd);
    if (abs (touch_time - sbuf.st_ctime) >1) {
      fprintf (stderr, "shape -- WARNING: possible clock discrepancy between host and NFS server.\n");
    }
  }
  else {
    fprintf (stderr, "shape -- WARNING: can't create files in `.'.\n");
  }

  shape_command = FALSE;

  
  if (cmdtargets[0] == NIL)
    comm = firsttarget;
  else
    comm = cmdtargets[k++];

  if ((nostdfile) && (!fileflg) && (cmdtargets[0] == NIL))
    errexit(11,NIL);

  if (!rebuildflg) {
    if (ruleflg) {
      if (atBindTestRule (ruleset))
	initial_selection = ruleset;
      else
	errexit(32,ruleset);
    }
  }
  else
    initial_selection = rbrule;
  
  if (!rebuildflg) {
    Bool build_success = TRUE;
    while (comm != NIL)	{
      char bct_name[MYMAXNAMLEN];
      Bool spin_success;
      depth = 0;

      if (confid) {
	(void) strcpy (bct_name, comm);
	(void) strcat (bct_name, ".bct");
	if ((cid = fopen (bct_name, "w")) == NULL)
	  errexit(12, bct_name);
	stRegisterFile (bct_name);
	init_confid (comm);
      }

      spin_success = try_to_make_target (comm, initial_selection, 
					 TRUE, &produced_object);
      if (spin_success && !reallydone)
	printf("shape - `%s' is up to date\n", comm);
      else if (!spin_success)
	warning (2, comm);
      
      build_success = build_success && spin_success;

      if (confid) {
	finish_confid ();
	fclose (cid);
	stUnRegisterFile (bct_name);
      }

      if (comm != firsttarget)
	comm = cmdtargets[k++];
      else
	comm = NIL;
    }
  }
  else {
    Bool build_success;
    depth = 0;
    build_success = try_to_make_target (rbtarg, initial_selection, TRUE,
			       &produced_object);
      
    if (build_success && !reallydone && !rebuildflg)
      printf("shape - `%s' is up to date\n", rbtarg);
    else if (reallydone)
	printf("shape - `%s' rebuilt according to bound configuration.\n", rbtarg);
    else if (!build_success)
      warning (2, rbtarg);
  }
}
