/* 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 - queues.c
 *
 * by Juergen.Nickelsen@cs.tu-berlin.de
 *
 * $Header: queues.c[8.0] Tue Jun 29 19:57:47 1993 axel@cs.tu-berlin.de frozen $
 */
#ifndef lint
static char *AtFSid = "$Header: queues.c[8.0] Tue Jun 29 19:57:47 1993 axel@cs.tu-berlin.de frozen $";
#endif

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

struct deprule
{
    struct stringlist *macros ;	/* list of macros target depends on */
    struct stringlist *deps ;	/* list of dependants */
    struct stringlist *targets ;/* list of targets */
    struct stringlist *cmds ;	/* list of commands */
    struct deprule *next ;	/* pointer to next rule */
} ;

static struct deprule *deprulelist = NULL ; /* list of all dependency rules */
static struct deprule *lastdeprule = NULL ; /* pointer to last entry */

struct selrule {		/* selection rule */
    char *name ;		/* name of rule */
    char *body ;		/* body of rule */
    char *src_file;             /* name of src file containing rule */
    int  src_lineno;           /* lineno of rule-head */
    struct selrule *next ;	/* pointer to next rule */
} ;

static struct selrule *selrulelist = NULL ;
static struct selrule *lastselrule = NULL ;

struct vclassdef {
    char *name ;
    struct stringlist *elements ;
    struct vclassdef *next ;
} ;

void queue_dependency_rule (targets, deps, macros, cmds)
struct stringlist *targets ;
struct stringlist *deps ;
struct stringlist *macros ; 
struct stringlist *cmds ;
{
    struct deprule *rule ;

    rule = (struct deprule *) check_malloc(sizeof(struct deprule)) ;
    rule->targets = targets ;
    rule->deps = deps ;
    rule->macros = macros ;
    rule->cmds = cmds ;

    if (lastdeprule == NULL) {
	lastdeprule = deprulelist = rule ;
    } else {
	lastdeprule->next = rule ;
	lastdeprule = lastdeprule->next ;
    }
    lastdeprule->next = NULL ;
}


void queue_selection_rule(name, body, src_filename, src_lineno)
char *name ;
char *body ;
char *src_filename;
int src_lineno;
{
    struct selrule *rule ;

    rule = (struct selrule *) check_malloc(sizeof(struct selrule)) ;
    rule->name = name ;
    rule->body = body ;
    rule->src_lineno = src_lineno;
    rule->src_file = check_strdup (src_filename);

    if (lastselrule == NULL) {
	lastselrule = selrulelist = rule ;
    } else {
	lastselrule->next = rule ;
	lastselrule = lastselrule->next ;
    }
    lastselrule->next = NULL ;
}


/* Insert a variant definition into Shape's data structures. */
void queue_variant_definition(name, macros)
char *name ;
struct stringlist *macros ;
{
    /* variant definitions are not really queued, since we need them
     * as early as macro definitions. They are directly written
     * into Shape's data structures instead. */

    struct stringlist *mac ;	/* loops over the "macros" list */
    struct stringlist *pr_mac ; /* loops over the "macros" list, but
				 * one behind mac. Used for freeing the
				 * stringlist struct. */
    int curvdef ;		/* loops over variantDefs[] */
    int curmdef ;		/* loops over variantDefs[curvdef].vmacros[] */


    /* Look for empty slot in variantDefs[].
     * If a variant definition with the same name is found,
     * this is considered an error.
     */

    for (curvdef = 0;
	 variantDefs[curvdef].name != NULL && curvdef < MAXVARDEFS;
	 curvdef++) {
	
	if (!strcmp (variantDefs[curvdef].name, name)) {
	    /* variant name already defined */
	    errexit (33, name) ; /* I'll kill him later for 33... */
	}
    }
    if (curvdef == MAXVARDEFS) {
	/* variant table overflow */
	errexit (27, "variant definitions") ;
    }

    /* vardef_end() uses this variable */
    lastVariantDef = curvdef ;

    /* Now we have a free slot in variantDefs[curvdef],
     * so initialize name, vpath, vflags, and vmacros fields
     */
    variantDefs[curvdef].name = name ;
    variantDefs[curvdef].vpath = NULL ;
    variantDefs[curvdef].vflags = NULL ;
    for(curmdef = 0; curmdef < MAXVMACROS; curmdef++) {
	variantDefs[curvdef].vmacros[curmdef] = NULL ;
    }

    /* mark next slot as last+1 with NULL in .name */
    variantDefs[curvdef + 1].name = NULL ;
    
    /* now insert macro definitions for this variant
     * macros "vpath" and "vflags" get special treatment */

    curmdef = 0 ;		/* number of current macro definition */
    for (mac = macros, pr_mac = NULL; 
	 mac != NULL;
	 pr_mac = mac, mac = mac->next) {

	char *macname ;		/* name of macro */
	char *macptr ;		/* pointer into macro definition */
	char *macval ;		/* pointer to macro value */

	if (pr_mac != NULL) {
	    free(pr_mac) ;	/* free previous struct stringlist */
	}

	/* get name of macro */
	macptr = mac->string ;
	macname = get_name(&macptr) ;
	/* macptr points to position after name now */
	macptr = skip_over_whitespace(macptr) ;

	/* this shodul be more general some time: check for
	 * "=", "-=", "+=", ":=" */
	if (*macptr != '=') {
	    parsing_error("\"=\" expected in variant definition",
			  macptr, name) ;
	}
	macval = macptr = skip_over_whitespace(macptr + 1) ;
	/* macptr and macval now point to macro value */

	/* check for special macros vpath and vflags if there is a value */
	if (*macval && !strcmp(macname, "vpath")) {
	    char *cp ;		/* keep value to free it later */
	    
	    /* This is a very simple heuristic:
	     * if vpath has only one component and this is "..",
	     * use prev_dir instead */

	    if (!strcmp((cp = get_name(&macval)), "..") &&
		/* macval now points behind the first component */
		*skip_over_whitespace(macval) == '\0') {
		variantDefs[curvdef].vpath = check_strdup(prev_dir) ;
	    } else {
		/* macval's value is useless now, so use macptr */
		variantDefs[curvdef].vpath = macptr ;
	    }
	    free(cp) ;
	} else if (*macval && !strcmp(macname, "vflags")) {
	    variantDefs[curvdef].vflags = macval ;
	} else {
	    /* So this is a "real" variant macro --
	     * do we still have a free macro slot? */
	    if (curmdef >= MAXVMACROS) {
		fatal("too many variant macro definitions", name) ;
	    }
	    
	    /* now insert definition */
	    variantDefs[curvdef].vmacros[curmdef++] = mac->string ;
	}
	free(macname) ;
    }
    free(pr_mac) ;

    vardef_end() ;		/* This is a hook from which this variant
				 * may already be activated */
}


void queue_vclass_definition(name, elements)
char *name ;
struct stringlist *elements ;
{
    /* vclass definitions are not really queued, since we need them
     * as early as macro definitions */

    vclassdef(name, elements) ;
    free_stringlist(elements) ;
}


void commit_selrule_definitions()
{
    struct selrule *srule, *prev_srule = NULL ;
    int backup = atBindDisplayErrors;

    /* selection rules */
    atBindDisplayErrors = TRUE;
    for (srule = selrulelist; srule != NULL; srule = srule->next) {
	atBindAddRule(srule->name, srule->body, srule->src_file, 
		      srule->src_lineno) ;
	free(srule->name) ;
	free(srule->body) ;
	if (prev_srule != NULL) free(prev_srule) ;
	prev_srule = srule ;
    }
    atBindDisplayErrors = backup;
    if (prev_srule != NULL) free(prev_srule) ;
    lastselrule = selrulelist = NULL ;
}


void commit_deprule_definitions()
{
    struct deprule *drule, *prev_drule = NULL ;
    
    /* dependency rules */
    for (drule = deprulelist; drule != NULL; drule = drule->next) {
	sb_ptr sb ;		/* string buffer to accumulate rule */
	struct stringlist *tmp ; /* pointer into rulep->targets,
				  * rulep->deps, and rulep->cmds */

	sb = check_sbnew(SB_LENGTH) ;

	/* copy targets to string buffer */
	for (tmp = drule->targets; tmp != NULL; tmp = tmp->next) {
	    char *xtmp = expandmacro (tmp->string);
	    while (xtmp && *xtmp && isspace(*xtmp)) xtmp++;
	    sb = check_sbcat(sb, xtmp) ;
	    sb = check_sbcat(sb, " ") ;
	}
	/* separator */
	sb = check_sbcat(sb, ": ") ;
	/* copy dependants to string buffer */
	for (tmp = drule->deps; tmp != NULL; tmp = tmp->next) {
	    sb = check_sbcat(sb, tmp->string) ;
	    sb = check_sbcat(sb, " ") ;
	}
	if (drule->macros) {
	    sb = check_sbcat(sb, ": ") ;
	    for (tmp = drule->macros; tmp != NULL; tmp = tmp->next) {
		sb = check_sbcat(sb, tmp->string) ;
		sb = check_sbcat(sb, " ") ;
	    }
	}
	
	/* give rule to shape */
	ruledef(sbstr(sb)) ;
	sbfree(sb) ;

	/* add commands to rule */
	for (tmp = drule->cmds; tmp != NULL; tmp = tmp->next) {
	    rulecont(tmp->string) ;
	}

	ruleend() ;
	free_stringlist(drule->targets) ;
	free_stringlist(drule->deps) ;
	free_stringlist(drule->macros) ;
	free_stringlist(drule->cmds) ;
	if (prev_drule != NULL) free(prev_drule) ;
	prev_drule = drule ;
    }
    if (prev_drule != NULL) free(prev_drule) ;
    lastdeprule = deprulelist = NULL ;
}


void commit_definitions()
{
    commit_selrule_definitions() ;
    commit_deprule_definitions() ;
}
