home *** CD-ROM | disk | FTP | other *** search
- /* RCS -- $Header: /u2/dvadura/src/generic/dmake/src/RCS/dag.c,v 1.1 91/05/06 15:23:04 dvadura Exp $
- -- SYNOPSIS -- Routines to construct the internal dag.
- --
- -- DESCRIPTION
- -- This file contains all the routines that are responsible for
- -- defining and manipulating all objects used by the make facility.
- --
- -- AUTHOR
- -- Dennis Vadura, dvadura@watdragon.uwaterloo.ca
- -- CS DEPT, University of Waterloo, Waterloo, Ont., Canada
- --
- -- COPYRIGHT
- -- Copyright (c) 1990 by Dennis Vadura. All rights reserved.
- --
- -- This program is free software; you can redistribute it and/or
- -- modify it under the terms of the GNU General Public License
- -- (version 1), as published by the Free Software Foundation, and
- -- found in the file 'LICENSE' included with this distribution.
- --
- -- This program is distributed in the hope that it will be useful,
- -- but WITHOUT ANY WARRANTY; without even the implied warrant of
- -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- -- GNU General Public License for more details.
- --
- -- You should have received a copy of the GNU General Public License
- -- along with this program; if not, write to the Free Software
- -- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- --
- -- LOG
- -- $Log: dag.c,v $
- * Revision 1.1 91/05/06 15:23:04 dvadura
- * dmake Release Version 3.7
- *
- */
-
- #include "extern.h"
-
-
- PUBLIC HASHPTR
- Get_name( name, tab, define )/*
- ===============================
- Look to see if the name is defined, if it is then return
- a pointer to its node, if not return NIL(HASH).
- If define is TRUE and the name is not found it will be added. */
-
- char *name; /* name we are looking for */
- HASHPTR *tab; /* the hash table to look in */
- int define; /* TRUE => add to table */
- {
- register HASHPTR hp;
- register char *p;
- uint16 hv;
- uint32 hash_key;
-
- DB_ENTER( "Get_name" );
- DB_PRINT( "name", ("Looking for %s", name) );
-
- hp = Search_table( tab, name, &hv, &hash_key );
-
- if( hp == NIL(HASH) && define ) {
- /* Check to make sure that CELL name contains only printable chars */
- for( p=name; *p; p++ )
- if( !isprint(*p) )
- Fatal( "Name contains non-printable character [0x%02x]", *p );
-
- TALLOC( hp, 1, HASH ); /* allocate a cell and add it in */
-
- hp->ht_name = _strdup( name );
- hp->ht_hash = hash_key;
- hp->ht_next = tab[ hv ];
- tab[ hv ] = hp;
-
- DB_PRINT( "name", ("Adding %s", name) );
- }
-
- DB_PRINT( "name",("Returning: [%s,%lu]",
- (hp == NIL(HASH)) ? "":hp->ht_name, hv) );
- DB_RETURN( hp );
- }
-
-
- PUBLIC HASHPTR
- Search_table( tab, name, phv, phkey )
- HASHPTR *tab;
- char *name;
- uint16 *phv;
- uint32 *phkey;
- {
- HASHPTR hp;
-
- *phv = Hash( name, phkey );
-
- for( hp = tab[ *phv ]; hp != NIL(HASH); hp = hp->ht_next )
- if( hp->ht_hash == *phkey
- && !strcmp(hp->ht_name, name) )
- break;
-
- return( hp );
- }
-
-
- PUBLIC HASHPTR
- Def_macro( name, value, flags )/*
- =================================
- This routine is used to define a macro, and it's value.
- The flags indicates if it is a permanent macro or if it's value
- can be redefined. A flags of M_PRECIOUS means it is a precious
- macro and cannot be further redefined. If the flags flag also
- contains the M_MULTI bit it means that the macro can be redefined
- multiple times and no warning of the redefinitions should be issued.
- Once a macro's VAR flags are set they are preserved through all future
- macro definitions.
-
- Macro definitions that have one of the variable bits set are treated
- specially. In each case the hash table entry var field points at the
- global variable that can be set by assigning to the macro.
-
- bit valued global vars must be computed when the macro value is changed.
- char valued global vars must have the first char of ht_value copied to
- them. string valued global vars have the same value as ht_value and should
- just have the new value of ht_value copied to them. */
-
- char *name; /* macro name to define */
- char *value; /* macro value to set */
- int flags; /* initial ht_flags */
- {
- register HASHPTR hp;
- register char *p, *q;
-
- DB_ENTER( "Def_macro" );
- DB_PRINT( "mac", ("Defining macro %s = %s, %x", name, value, flags) );
-
- /* check to see if name is in the table, if so then just overwrite
- the previous definition. Otherwise allocate a new node, and
- stuff it in the hash table, at the front of any linked list */
-
- if( Readenv ) flags |= M_LITERAL;
-
- hp = Get_name( name, Macs, TRUE );
-
- if( (hp->ht_flag & M_PRECIOUS) && !(flags & M_FORCE) ) {
- Warning( "Macro `%s' cannot be redefined", name );
- DB_RETURN( hp );
- }
-
- if( hp->ht_value != NIL(char) ) FREE( hp->ht_value );
-
- if( (hp->ht_flag & M_USED) && !((flags | hp->ht_flag) & M_MULTI) )
- Warning( "Macro `%s' redefined after use", name );
-
- if( (value != NIL(char)) && (*value) ) {
- /* strip out any \<nl> combinations where \ is the current CONTINUATION
- * char */
-
- for( p = value; (p = strchr(p, CONTINUATION_CHAR)) != NIL(char); )
- if( p[1] == '\n' )
- strcpy( p, p+2 );
- else
- p++;
-
- if( !(flags & M_LITERAL) ) {
- p = _strdup( _strspn( value, " \t" ) ); /* strip white space before */
- /* ... and after value */
- if( *p ) {
- for(q=p+strlen(p)-1; ((*q == ' ')||(*q == '\t')); q--);
- *++q = '\0';
- }
- flags &= ~M_LITERAL;
- }
- else
- p = _strdup( value ); /* take string literally */
-
- if( !*p ) { /* check if result is "" */
- FREE( p );
- p = NIL(char);
- flags |= M_EXPANDED;
- }
- else if( *_strpbrk( p, "${}" ) == '\0' )
- flags |= M_EXPANDED;
-
- hp->ht_value = p;
- }
- else
- hp->ht_value = NIL(char);
-
- /* Assign the hash table flag less the M_MULTI flag, it is used only
- * to silence the warning. But carry it over if it was previously
- * defined in ht_flag, as this is a permanent M_MULTI variable. */
-
- hp->ht_flag = (flags & ~(M_MULTI|M_FORCE)) |
- (hp->ht_flag & (M_VAR_MASK | M_MULTI));
-
- /* Check for macro variables and make the necessary adjustment in the
- * corresponding global variables */
-
- if( hp->ht_flag & M_VAR_MASK )
- if( !(flags & M_EXPANDED) )
- Error( "Macro variables must be assigned with :=" );
- else switch( hp->ht_flag & M_VAR_MASK ) /* only one var type per var */
- {
- case M_VAR_STRING:
- *hp->MV_SVAR = hp->ht_value;
- break;
-
- case M_VAR_CHAR:
- *hp->MV_CVAR = (hp->ht_value == NIL(char)) ? '\0':*hp->ht_value;
- break;
-
- case M_VAR_INT: {
- int tvalue;
- if( hp->MV_IVAR == NIL(int) ) break; /* first time */
-
- tvalue = atoi(hp->ht_value);
- if( hp->MV_IVAR == &Buffer_size ) {
- /* If Buffer_size is modified then make sure you change the
- * size of the real buffer as well. */
- tvalue = (tvalue < (BUFSIZ-2)) ? BUFSIZ : tvalue+2;
- if( Buffer_size == tvalue ) break;
- if( Buffer ) FREE(Buffer);
- if((Buffer=MALLOC(tvalue, char)) == NIL(char)) No_ram();
- *Buffer = '\0';
- }
- *hp->MV_IVAR = tvalue;
-
- if( hp->MV_IVAR == &Max_proc || hp->MV_IVAR == &Max_proclmt ) {
- if( tvalue < 1 )
- Fatal( "Process limit value must be > 1" );
-
- if( Max_proc > Max_proclmt )
- Fatal( "Specified # of processes exceeds limit of [%d]",
- Max_proclmt );
- }
- } break;
-
- case M_VAR_BIT:
- /* Bit variables are set to 1 if ht_value is not NULL and 0
- * otherwise */
-
- if( hp->ht_value == NIL(char) )
- *hp->MV_BVAR &= ~hp->MV_MASK;
- else
- *hp->MV_BVAR |= hp->MV_MASK;
- break;
- }
-
- DB_RETURN( hp );
- }
-
-
-
- PUBLIC CELLPTR
- Def_cell( name )/*
- ==================
- Take a string passed in and define it as a cell
- If the cell exists then return a pointer to it. */
- char *name;
- {
- register HASHPTR hp;
- register CELLPTR cp;
- register CELLPTR lib;
- char *member;
- char *end;
-
- DB_ENTER( "Def_cell" );
-
- /* Check to see if the cell is a member of the form lib(member) or
- * lib((symbol)) and handle the cases appropriately.
- * What we do is we look at the target, if it is of the above two
- * forms we get the lib, and add the member/symbol to the list of
- * prerequisites for the library. If this is a symbol name def'n
- * we additionally add the attribute A_SYMBOL, so that stat can
- * try to do the right thing. */
-
- if( ((member = strchr(name, '(')) != NIL(char)) &&
- ((end = strrchr(member, ')')) != NIL(char)) &&
- (member > name) && (member[-1] != '$') &&
- (end > member+1) && (end[1] == '\0') )
- {
- *member++ = *end = '\0';
-
- if( (*member == '(') && (member[strlen(member)-1] == ')') ) {
- member[ strlen(member)-1 ] = '\0';
- cp = Def_cell( member+1 );
- cp->ce_attr |= A_SYMBOL;
- }
- else
- cp = Def_cell( member );
-
- lib = Def_cell( name );
-
- Add_prerequisite( lib, cp, FALSE, FALSE );
- lib->ce_attr |= A_LIBRARY | A_COMPOSITE;
-
- if( !Def_targets ) cp = lib;
- }
- else {
- hp = Get_name( name, Defs, TRUE );/* get the name from hash table */
-
- if( hp->CP_OWNR == NIL(CELL) ) /* was it previously defined */
- { /* NO, so define a new cell */
- DB_PRINT( "cell", ("Defining cell [%s]", name) );
-
- TALLOC( cp, 1, CELL );
- hp->CP_OWNR = cp;
- cp->ce_name = hp;
- cp->ce_fname = hp->ht_name;
- }
- else /* YES, so return the old cell */
- {
- DB_PRINT( "cell", ("Getting cell [%s]", hp->ht_name) );
- cp = hp->CP_OWNR;
- }
- }
-
- DB_RETURN( cp );
- }
-
-
-
-
- PUBLIC LINKPTR
- Add_prerequisite( cell, prq, head, force )/*
- ============================================
- Add a dependency node to the dag. It adds it to the prerequisites,
- if any, of the cell and makes certain they are in linear order.
- If head == 1, then add to head of the prerequisite list, else
- add to tail. */
- CELLPTR cell;
- CELLPTR prq;
- int head;
- int force;
- {
- register LINKPTR lp, tlp;
-
- DB_ENTER( "Add_prerequisite" );
- DB_PRINT( "cell", ("Defining prerequisite %s", prq->CE_NAME) );
-
- if( (prq->ce_flag & (F_MAGIC | F_PERCENT)) && !force )
- Fatal( "Special target [%s] cannot be a prerequisite",
- prq->CE_NAME );
-
- if( cell->ce_prq == NIL(LINK) ) { /* it's the first one */
- TALLOC( lp, 1, LINK );
- lp->cl_prq = prq;
- cell->ce_prq = lp;
- }
- else { /* search the list, checking for duplicates */
- for( lp = cell->ce_prq;
- (lp->cl_next != NIL(LINK)) && (lp->cl_prq != prq);
- lp = lp->cl_next );
-
- /* If the prq is not found and we are at the last prq in the list,
- * allocate a new prq and place it into the list, insert it at the
- * head if head == 1, else we add it to the end. */
-
- if( lp->cl_prq != prq ) {
- TALLOC( tlp, 1, LINK );
- tlp->cl_prq = prq;
-
- if( head ) {
- tlp->cl_next = cell->ce_prq;
- cell->ce_prq = tlp;
- }
- else
- lp->cl_next = tlp;
-
- lp = tlp;
- }
- }
-
- DB_RETURN( lp );
- }
-
-
-
- PUBLIC void
- Clear_prerequisites( cell )/*
- =============================
- Clear out the list of prerequisites, freeing all of the LINK nodes,
- and setting the list to NULL */
- CELLPTR cell;
- {
- LINKPTR lp, tlp;
-
- DB_ENTER( "Clear_prerequisites" );
- DB_PRINT( "cell", ("Nuking prerequisites") );
-
- if( cell == NIL(CELL) ) { DB_VOID_RETURN; }
-
- for( lp=cell->ce_prq; lp != NIL(LINK); lp=tlp ) {
- tlp=lp->cl_next;
- FREE( lp );
- }
-
- cell->ce_prq = NIL(LINK);
-
- DB_VOID_RETURN;
- }
-
-
- PUBLIC int
- Test_circle( cp, fail )/*
- =========================
- Actually run through the graph */
- CELLPTR cp;
- int fail;
- {
- register LINKPTR lp;
- int res = 0;
-
- DB_ENTER( "Test_circle" );
- DB_PRINT( "tc", ("checking [%s]", cp->CE_NAME) );
-
- if( cp->ce_flag & F_MARK )
- if( fail )
- Fatal("Detected circular dependency in graph at [%s]", cp->CE_NAME);
- else
- DB_RETURN( 1 );
-
- cp->ce_flag |= F_MARK;
- for( lp = cp->ce_prq; !res && lp != NIL(LINK); lp = lp->cl_next )
- res = Test_circle( lp->cl_prq, fail );
- cp->ce_flag ^= F_MARK;
-
- DB_RETURN( res );
- }
-
-
-
- PUBLIC STRINGPTR
- Def_recipe( rcp, sp, white_too, no_check )/*
- =============================================
- Take the recipe and add it to the list of recipes
- pointed to by sp. sp points to the last element.
- return a pointer to the new recipe. If white_too == TRUE add the
- recipe even if it contains only white space.
- If no_check is true then don't look for -@ at the start of the
- recipe line. */
- char *rcp;
- STRINGPTR sp;
- int white_too;
- int no_check;
- {
- register STRINGPTR nsp;
- register char *rp;
-
- DB_ENTER( "Def_recipe" );
- DB_PRINT( "rul", ("Defining recipe %s", rcp) );
-
- if( !white_too ) rcp = _strspn( rcp, " \t" );
- if( (rcp == NIL(char)) || (*rcp == 0 && !white_too) )
- DB_RETURN( sp ); /* return last recipe when new recipe not added */
-
- rp = no_check ? rcp : _strspn( rcp, " \t@-+%" );
-
- TALLOC( nsp, 1, STRING );
- nsp->st_string = _strdup( rp );
-
- if( sp != NIL(STRING) ) sp->st_next = nsp;
- nsp->st_next = NIL(STRING);
-
- if( !no_check ) nsp->st_attr |= Rcp_attribute( rcp );
-
- DB_RETURN( nsp );
- }
-
-
- PUBLIC t_attr
- Rcp_attribute( rp )/*
- ======================
- Look at the recipe and return the set of attributes that it defines. */
- char *rp;
- {
- t_attr flag = A_DEFAULT;
- int done = FALSE;
-
- while( !done )
- switch( *rp++ )
- {
- case '@' : flag |= A_SILENT; break;
- case '-' : flag |= A_IGNORE; break;
- case '+' : flag |= A_SHELL; break;
- case '%' : flag |= A_SWAP; break;
-
- case ' ' :
- case '\t': break;
-
- default: done = TRUE; break;
- }
-
- return(flag);
- }
-