/* 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 - inherit.c
 *
 * Author: Axel Mahler (Axel.Mahler@cs.tu-berlin.de)
 *
 * $Header: inherit.c[8.0] Fri Aug  6 19:12:40 1993 axel@cs.tu-berlin.de frozen $
 */
#ifndef lint
static char *AtFSid = "$Header: inherit.c[8.0] Fri Aug  6 19:12:40 1993 axel@cs.tu-berlin.de frozen $";
#endif

#include "shape.h"

extern int af_errno;

LOCAL Bool finddep(rule,name)
     struct rules *rule;
     char *name;
{
  register int i = 0;
  char *dependent;
  while(*(dependent = get_dep(rule,i)))
    {
      if (!strcmp(dependent,name))
	return(TRUE);
      i++;
    }
  return(FALSE);
}

LOCAL Bool forbidden(syspath,name,type)
     char *syspath;
     char *name;
     char *type;
{
  char fullname[MYMAXNAMLEN];
  Bool bp = FALSE, nobp = FALSE;
  register struct rules *bpool, *nobpool;
  
  fullname[0] = '\0';
  if (strcmp(syspath,curvpath[0]))
    {
      strcpy(fullname,syspath);
      strcat(fullname,"/");
      strcat(fullname,name);
    }
  else
    {
      strcpy(fullname,name);
    }

  if (type && *type)
    {
      strcat(fullname,".");
      strcat(fullname,type);
    }

  bpool = get_target (".BPOOL");
  nobpool = get_target (".NOBPOOL");

  if (bpool != (struct rules *) NIL)
    {
      bp = finddep(bpool,fullname);
    }
  else
    bp = TRUE;
  
  if (nobpool != (struct rules *) NIL)
    {
      nobp = finddep(nobpool,fullname);
    }

  if (bp && nobp)
    return(TRUE);

  if (bp && (!nobp))
    return(FALSE);

  if ((!bp) && nobp)
    return(TRUE);
  
  if ((!bp) && (!nobp))
    return(TRUE);
/*NOTREACHED*/
return(FALSE);
}

LOCAL struct linked_key_list {
  char *string;
  Af_key done_key;
  struct linked_key_list *nextstring;
} *done_list;


/* This function should be part of AtFS ! */
LOCAL int af_dupkey (inkey, outkey)
     Af_key *inkey, *outkey;
{
  if (afAccessAso (inkey, 0))
    FAIL ("dropkey", "", AF_EINVKEY, ERROR);

  /* increase reference count in corresponding archive and in attrbuf */
  
  afIncreaseAccess (inkey);
  *outkey = *inkey;
  return (AF_OK);
}

EXPORT void add_to_done_list (targ_path, done_key)
     char *targ_path;
     Af_key *done_key;
{
  struct linked_key_list *c;
  static struct linked_key_list *l;
 
  if(done_list == (struct linked_key_list *) NIL) {
    done_list = (struct linked_key_list *)
	check_malloc (sizeof(struct linked_key_list));
    c = l = done_list;
    done_list->nextstring = (struct linked_key_list *) NIL;
  }
  else {
    l->nextstring = (struct linked_key_list *)
      check_malloc(sizeof(struct linked_key_list));
    c = l->nextstring;
    l = c;
  }
  c->nextstring = (struct linked_key_list *) NIL;
  
  c->string = check_strdup (targ_path);

  if (done_key) {
    if (af_dupkey (done_key, &c->done_key) < 0)
      af_perror ("af_dupkey");
  }
}

EXPORT Bool stk_matchuda (udas, attr) char *udas, *attr; {
  char *start, *end;
  int attrlen;

  if (udas == (char *)NULL) return FALSE;
  if (attr == (char *)NULL) return FALSE;

  attrlen = strlen (attr);
  start = udas;

  do {
    end = strchr (start, '\n');
    if (!end)
      end = start + strlen (start);
    if ((attrlen == end-start) && strncmp (start, attr, end-start) == 0)
      return TRUE;
    if (*end) {
      start = end+1;
    }
    else 
      return FALSE;
  } while (*start);

  return FALSE;
}

EXPORT void save_targets (dep_rule, primary_target_name, q, primary_target_key, touch_time)
     struct rules *dep_rule;
     char *primary_target_name;
     DepQ *q;
     BKey *primary_target_key;
     time_t touch_time;
{
  Af_key busykey, savekey;
  Af_attrs attrbuf;
  struct rules *this_rule = dep_rule;
  char *do_key, *syspath, name[NAME_MAX+1], type[NAME_MAX+1], *p;
  int major_version = AF_BUSYVERS, minor_version = AF_BUSYVERS, retcode;
  char savepath[MYMAXNAMLEN], *targetpath, *derivation_key;

  primary_target_key->bk_isdefined = FALSE;

  if (noexflg)
    return;

  if (dep_rule == (struct rules *)NULL)
    return;

  /*
   * 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 (!q->dependent_queue_initialized &&
	(dep_rule->cmdlist != (struct cmds *)NIL))
      return;

  if (!q->dependent_queue_initialized &&
	(dep_rule->heritage != (char **)NIL))
      return;

  do_key = make_derivation_key (dep_rule, q);

  if (strrchr (primary_target_name, '/')) {
    strcpy (savepath, primary_target_name);
    p = strrchr (savepath, '/');
    *p = '\0';
    p++;
    primary_target_name = p;
    syspath = savepath;
  }
  else
    syspath = curvpath[0];

  strcpy (name, primary_target_name);

  strcpy (type, af_aftype (name));
  strcpy (name, af_afname (name));

  if((derivation_key = malloc((unsigned) (strlen(do_key) + 
					  strlen (ATTRNAME) + 2))) == NIL)
    errexit(10,"malloc");
  strcpy (derivation_key, ATTRNAME);
  strcat (derivation_key, "=");
  strcat (derivation_key, do_key);
  
  while (this_rule && !this_rule->saved) {
    this_rule->saved = TRUE;

    if (error_occ) {
      error_occ = FALSE;
      goto next_spin;
    }

    if (is_pattern (this_rule->name)) {
      int pathlen = strlen (af_afpath (this_rule->name));
      strcpy (name, af_afname (primary_target_name));
      strcat(name, suffix (this_rule->name));
      if (pathlen) {
	targetpath = check_malloc (pathlen + strlen (name) + 2);
	(void) sprintf (targetpath, "%s/%s\0", af_afpath (this_rule->name),
			name);
      }
      else {
	targetpath = check_strdup (name);
      }
      strcpy (type, af_aftype (name));
      strcpy (name, af_afname (name));
    }
    else {
      strcpy (name, af_afname (this_rule->name));
      strcpy (type, af_aftype (this_rule->name));
      targetpath = check_strdup (this_rule->name);
    }

    if (af_getkey (syspath, name, type, major_version, minor_version, &busykey) == -1)
      goto next_spin;

    /*
     * I hope this is right. Should we return the key from 
     * the bpool instead (if possible) ?
     */
    if (!primary_target_key->bk_isdefined) {
      primary_target_key->bk_isdefined = TRUE;
      primary_target_key->bk_key = busykey;
    }

    if (af_allattrs (&busykey, &attrbuf) < 0) {
      af_perror ("save_targets");
    }
    else if (attrbuf.af_mtime < touch_time) {
      /*
       * This case happens if an implicit rule specifies multiple 
       * targets but not all of them are actually made, perhaps due
       * to optimizations in the tool script (e.g. the well-known
       * "yacc -d"-problem). We want to prevent caching of identical
       * derived (actually *not* re-derived) versions.
       */
      char *busy_dkeys = af_retattr (&busykey, ATTRNAME);

      if (!stk_matchuda (busy_dkeys, do_key))
	if ((af_setattr (&busykey,AF_ADD,derivation_key)) == -1) {
	  af_perror ("save_targets");
	}
      if (busy_dkeys) free (busy_dkeys);
      af_freeattrbuf (&attrbuf);
      goto next_spin;
    }
    af_freeattrbuf (&attrbuf);
    
    retcode = af_setattr (&busykey, AF_REPLACE, atCacheKey(&busykey));
    if ((retcode == -1) && (af_errno == AF_ENOATFSDIR)) {
      warning (1, "while trying to set cachekey");
      af_initattrs (&buft);
      af_dropkey (&busykey);
      goto next_spin;
    }

    if ((af_setattr (&busykey, AF_REPLACE, derivation_key)) == -1) {
      af_perror ("save_targets");
      goto next_spin;
    }

    add_to_done_list (targetpath, &busykey);

    free (targetpath);

    if ((bpoolflg) || (nobpoolflg)) {
      if (!forbidden (syspath, name, type)) {
	if ((atSaveCache (&busykey, &savekey, NULL, AF_STORE_COMPLETE)) == -1)
	  switch (af_errno) {
	  case AF_ENOATFSDIR:
	    warning (1,NIL);
	    break;
	  default:
	    af_perror ("save_targets");
	    goto next_spin;
	  }
	else {
	  if (buft.af_gen != -1) {
	    if ((af_svnum (&savekey, buft.af_gen, buft.af_rev)) == -1) {
	      af_perror ("save_targets");
	      goto next_spin;
	    }
	  }
	  else {
	    if ((af_svnum (&savekey, AF_BUSYVERS, AF_BUSYVERS)) == -1)
	      af_perror ("save_targets");
	    af_dropkey (&savekey);
	  }
	}
      }
    }
  next_spin:
    this_rule = this_rule->next;
  }

  free (derivation_key);
  /*
   * We have to clean up our flags if "dep_rule" is a pattern rule.
   */
  if (is_pattern (dep_rule->name))
    for (this_rule = dep_rule; this_rule && this_rule->saved; 
	 this_rule = this_rule->next)
      this_rule->saved = FALSE;
}

EXPORT Bool already_in_done_list(targetname, done_key)
     char *targetname;
     Af_key *done_key;
{
  struct linked_key_list *c;
  
  c = done_list;

  while(c != (struct linked_key_list *) NIL)
    {
      if(!strcmp(c->string,targetname)) {
	if (af_dupkey (&c->done_key, done_key) < 0)
	  af_perror ("af_dupkey");
	return(TRUE);
      }
      else
	c = c->nextstring;
    }
  return(FALSE);
}
