/* 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 - dkey.c
 *
 * Author: Axel Mahler (Axel.Mahler@cs.tu-berlin.de)
 *
 * $Header: dkey.c[8.0] Mon Jun  6 15:09:55 1994 axel@cs.tu-berlin.de frozen $
 */
#ifndef lint
static char *AtFSid = "$Header: dkey.c[8.0] Mon Jun  6 15:09:55 1994 axel@cs.tu-berlin.de frozen $";
#endif
/*
 * Handling of derivation key and appropriate data structures
 */

#include "shape.h"

#define QUEUESIZE 64

LOCAL char *object_id (key) Af_key *key; {
  static char buffer[PATH_MAX];
  char datebuf[12], *s;
  Bool from_cache = FALSE;

  buffer[0] = '\0';
  strcat (buffer, af_retattr (key, AF_ATTUNIXNAME));
  strcat (buffer, "[");
  if ((s = af_retattr (key, AT_ATTCACHEKEY))) {
    from_cache = TRUE;
    strcat (buffer, "#");
    strcat (buffer, s);
    free (s);
  }
  else {
    sprintf (datebuf, "%ld", af_rettimeattr (key, AF_ATTMTIME));
    strcat (buffer, datebuf);
  }
  if (!from_cache && (af_retnumattr (key, AF_ATTSTATE) != AF_BUSY)) {
    /* non busy versions are expanded by default */
    if (!noexpflg)
      (void) strcat (buffer, "*");
  }
  else { /* busy versions not expanded by default */
    if (expflg)
      (void) strcat (buffer, "*");
  }
  strcat (buffer, "]");
  return buffer;
}


LOCAL char *dependent_ids (q, disl) DepQ *q; int *disl; {
  static char buffer[AT_MAXATTRSIZE];
  Af_key *this = q->head;
  int curlen = 0;
  char *s;

  buffer[0] = '\0';

  if (!q->dependent_queue_initialized) return buffer;

  while (this && (this <= q->tail)) {
    s = object_id (this++);
    curlen += strlen (s);
    if (curlen < AT_MAXATTRSIZE)
      strcat (buffer, s);
    else
      break;
  }
  *disl = curlen;
  return buffer;
}

LOCAL int init_dependent_queue (q) DepQ *q; {
  static Af_key nullkey;

  if (q->dependent_queue_initialized) return TRUE;
  if ((q->dependent_queue = (Af_key *)malloc (QUEUESIZE * sizeof (Af_key)))
      == (Af_key *)NULL) {
    stLog ("malloc", ST_LOG_ERROR);
    return FALSE;
  }
  q->head = q->tail = q->dependent_queue;
  *q->head = nullkey;
  q->current_max_dependents = QUEUESIZE;
  q->queued_dependents = 0;
  q->dependent_queue_initialized = TRUE;
  return TRUE;
}

LOCAL int grow_dependent_queue (q) DepQ *q; {
  unsigned int tail_offset = q->tail - q->dependent_queue;
  Af_key *queue_anchor = q->dependent_queue;

  if (!q->dependent_queue_initialized) {
    return init_dependent_queue(q);
  }

  if ((q->dependent_queue = (Af_key *)realloc ((char *)queue_anchor, 
			    2 * q->current_max_dependents * sizeof (Af_key)))
      == (Af_key *)NULL) {
    q->dependent_queue = queue_anchor;
    return FALSE;
  }
  if (q->dependent_queue != queue_anchor) {
    q->head = q->dependent_queue;
    q->tail = q->dependent_queue + tail_offset;
  }
  q->current_max_dependents = q->current_max_dependents * 2;
  return TRUE;
}

EXPORT DepQ *new_DepQ () {
  DepQ *q;

  if ((q = (DepQ *)malloc (sizeof (DepQ))) == NULL) return (DepQ *)NULL;
  q->dependent_queue_initialized = FALSE;
  q->queue_changed = TRUE;
  return q;
}

EXPORT void drop_dependent_queue (q) DepQ *q; {
  if (!q) return;
  if (q->dependent_queue_initialized) {
    while (q->head <= q->tail) af_dropkey (q->head++);
    free (q->dependent_queue);
    free (q);
  }
}

EXPORT void append_dependent (key, q) Af_key *key; DepQ *q; {
  if (!q) return;
  q->queue_changed = TRUE;
  if (!q->dependent_queue_initialized && !init_dependent_queue(q)) {
    stLog ("Internal error: can't initialize dependent_queue", ST_LOG_ERROR);
    return;
  }
  if ((q->queued_dependents < q->current_max_dependents) || 
      (grow_dependent_queue (q))) {
    if (q->queued_dependents) 
      /* "tail" points to last element, exept when Q is empty */
      *++(q->tail) = *key;
    else
      *q->tail = *key;
    q->queued_dependents++;
    return;
  }
  else {
    stLog ("Internal error: can't expand dependent_queue", ST_LOG_ERROR);
    return;
  }
}

#define KVERS "AV(1.0)"

EXPORT char *make_derivation_key (current_rule, q) 
     struct rules *current_rule; 
     DepQ *q; 
{
  register char *buf_end, *peek, *buf_bound;
  register int i;
  int len;
  char edit_buffer[AT_MAXATTRSIZE], *dis, *dependent_ids();
  static char *mts = "";
  short is_implicit_rule;
  struct rules *implicit_target();
  char **heritage;
  
  /*
   * This function finally builds the derivation key for the
   * current derivation. The produced key has the general 
   * form <magic opt-ingredients ; production ; opt-sequence-of-word >.
   * The key is returned in static memory. The variable is "keybuffer".
   */
  
  if (!q || !current_rule) return "";
  if (!q->queue_changed) return q->keybuffer;
  
  q->keybuffer[0] = '\0';
  buf_bound = &q->keybuffer[AT_MAXATTRSIZE - 1];
  heritage = current_rule->heritage;

  /* keybuffer <- magic */
  strcat (q->keybuffer, KVERS);
  
  /* keybuffer <- opt-ingredients */
  dis = dependent_ids (q, &len);
  if ((len += strlen (KVERS)) < AT_MAXATTRSIZE)
    strcat (q->keybuffer, dis);
  else
    return mts;

  buf_end = q->keybuffer + len;

  if (buf_end < buf_bound) {
    strcat (q->keybuffer, ";");
    buf_end++;
  }
  else
    return mts;
  
  /* production is made up of <production-key> "{" opt-macro-defs "}">. */
  /* first the production-key */
  is_implicit_rule = (strchr (current_rule->name, '%') != NIL);
  if (!is_implicit_rule) {
    if (!current_rule->cmdlist || !current_rule->cmdlist->command ||
	!*current_rule->cmdlist->command) {
      current_rule = implicit_target (current_rule->name);
      is_implicit_rule = TRUE;
    }
    if (!current_rule) 
      return mts;
    heritage = current_rule->heritage;
  }

  /* ... now the opt-macro-defs */
  if ((buf_end += 1) < buf_bound)
    strcat (q->keybuffer, "{");
  else
    return mts;

  if (heritage) {
    for (i = 0; heritage[i]; i++) {
      Bool got_plus = FALSE;
      Bool got_open_paren = FALSE;

      peek = heritage[i];
      while (*peek && (*peek != ')') && (buf_end < buf_bound)) {
	if (!got_plus) {
	  if (*peek == '+')  { got_plus = TRUE; ++peek; continue; }
	}
	if (!got_open_paren) {
	  if (*peek == '(') { got_open_paren = TRUE; ++peek; continue; }
	}
	*buf_end = *peek;
	buf_end++; peek++;
      }
      if ((buf_end +1) >= buf_bound)
	return mts;

      /* let's assume we've seen the closing paren */
      *buf_end++ = '{';
      *buf_end = '\0';
      strcpy (edit_buffer, heritage[i]);
      if (edit_buffer[0] == '+') {
	edit_buffer[0] = '$';
	dis = expandmacro (edit_buffer);
	if ((buf_end += strlen (dis) + 1) >= buf_bound)
	  return mts;
	strcat (q->keybuffer, dis);
      }
      else {
	/* f*ck it !!! */
      }
      strcat (q->keybuffer, "}");
    }
  }
  strcat (q->keybuffer, "};");
  
  /* 
   * opt-sequence-of-word is used to add additional parameters
   * to the derivation key.
   */
  strcpy (edit_buffer, expandmacro ("$(HOSTTYPE)"));
  if (!*edit_buffer)
    strcpy (edit_buffer, expandmacro ("$(hosttype)"));
  if (!*edit_buffer)
    strcpy (edit_buffer, af_getdomain());
  if ((buf_end += strlen (edit_buffer) +5) >= buf_bound)
    return mts;
  strcat (q->keybuffer, edit_buffer);
  
  q->queue_changed = FALSE;
  return q->keybuffer;
}
