summaryrefslogtreecommitdiff
path: root/dmake/rulparse.c
diff options
context:
space:
mode:
authorJens-Heiner Rechtien <hr@openoffice.org>2000-09-22 14:33:37 +0000
committerJens-Heiner Rechtien <hr@openoffice.org>2000-09-22 14:33:37 +0000
commitb501a54cf959bc549810fb24ca6f9584dc5c9cf3 (patch)
treeb78fbbc5c0f6959529f2553e3bbb83b077eaa17c /dmake/rulparse.c
parent17e0108b4e8d91f9acfe376ee231418830392460 (diff)
initial import
Diffstat (limited to 'dmake/rulparse.c')
-rw-r--r--dmake/rulparse.c1473
1 files changed, 1473 insertions, 0 deletions
diff --git a/dmake/rulparse.c b/dmake/rulparse.c
new file mode 100644
index 000000000000..f0e9f0cb390f
--- /dev/null
+++ b/dmake/rulparse.c
@@ -0,0 +1,1473 @@
+/* RCS $Id: rulparse.c,v 1.1.1.1 2000-09-22 15:33:25 hr Exp $
+--
+-- SYNOPSIS
+-- Perform semantic analysis on input
+--
+-- DESCRIPTION
+-- This code performs semantic analysis on the input, and builds
+-- the complex internal datastructure that is used to represent
+-- the user makefile.
+--
+-- AUTHOR
+-- Dennis Vadura, dvadura@dmake.wticorp.com
+--
+-- WWW
+-- http://dmake.wticorp.com/
+--
+-- COPYRIGHT
+-- Copyright (c) 1996,1997 by WTI Corp. All rights reserved.
+--
+-- This program is NOT free software; you can redistribute it and/or
+-- modify it under the terms of the Software License Agreement Provided
+-- in the file <distribution-root>/readme/license.txt.
+--
+-- LOG
+-- Use cvs log to obtain detailed change logs.
+*/
+
+#include "extern.h"
+
+/* prototypes for local functions */
+static void _add_global_prereq ANSI((CELLPTR));
+static int _add_root ANSI((CELLPTR));
+static CELLPTR _build_graph ANSI((int, CELLPTR, CELLPTR));
+static char* _build_meta ANSI((char*));
+static int _do_magic ANSI((int, char*, CELLPTR, CELLPTR, t_attr, char*));
+static void _do_special ANSI((int, int, t_attr,char*,CELLPTR,CELLPTR,int*));
+static int _do_targets ANSI((int, t_attr, char*, CELLPTR, CELLPTR));
+static t_attr _is_attribute ANSI((char*));
+static int _is_special ANSI((char*));
+static char* _is_magic ANSI((char*));
+static int _is_percent ANSI((char*));
+static CELLPTR _make_multi ANSI((CELLPTR));
+static CELLPTR _replace_cell ANSI((CELLPTR,CELLPTR,CELLPTR));
+static void _set_attributes ANSI((t_attr, char*, CELLPTR ));
+static void _stick_at_head ANSI((CELLPTR, CELLPTR));
+static void _set_global_attr ANSI((t_attr));
+
+
+/* static variables that must persist across invocation of Parse_rule_def */
+static CELLPTR _sv_targets = NIL(CELL);
+static STRINGPTR _sv_rules = NIL(STRING);
+static STRINGPTR _sv_crule = NIL(STRING);
+static CELLPTR _sv_edgel = NIL(CELL);
+static LINKPTR _sv_glb_prq = NIL(LINK);
+static int _sp_target = FALSE;
+static t_attr _sv_attr;
+static int _sv_flag;
+static int _sv_op;
+static char *_sv_setdir;
+static char _sv_globprq_only = 0;
+
+/* Define for global attribute mask */
+#define A_GLOB (A_PRECIOUS | A_SILENT | A_IGNORE | A_EPILOG | A_SWAP |\
+ A_SHELL | A_PROLOG | A_NOINFER | A_SEQ | A_MKSARGS )
+
+
+PUBLIC int
+Parse_rule_def( state )/*
+=========================
+ Parse the rule definition contained in Buffer, and modify the state
+ if appropriate. The function returns 0, if the definition is found to
+ be an illegal rule definition, and it returns 1 if it is a rule definition.
+ */
+int *state;
+{
+ TKSTR input; /* input string struct for token search */
+ CELLPTR targets; /* list of targets if any */
+ CELLPTR prereq; /* list of prereq if any */
+ CELLPTR prereqtail; /* tail of prerequisite list */
+ CELLPTR cp; /* temporary cell pointer for list making */
+ char *result; /* temporary storage for result */
+ char *tok; /* temporary pointer for tokens */
+ char *set_dir; /* value of setdir attribute */
+ char *brk; /* break char list for Get_token */
+ char *firstrcp; /* first recipe line, from ; in rule line */
+ t_attr attr; /* sum of attribute flags for current tgts*/
+ t_attr at; /* temp place to keep an attribute code */
+ int op; /* rule operator */
+ int special; /* indicate special targets in rule */
+ int percent; /* indicate percent rule target */
+ int mixed_glob_prq; /* indicate mixed %-rule prereq possible */
+
+ DB_ENTER( "Parse_rule_def" );
+
+ op = 0;
+ attr = 0;
+ special = 0;
+ percent = 0;
+ set_dir = NIL( char );
+ targets = NIL(CELL);
+ prereq = NIL(CELL);
+ prereqtail = NIL(CELL);
+ mixed_glob_prq = 0;
+
+ /* Check to see if the line is of the form:
+ * targets : prerequisites; first recipe line
+ * If so remember the first_recipe part of the line. */
+
+ firstrcp = strchr( Buffer, ';' );
+ if( firstrcp != NIL( char ) ) {
+ *firstrcp++ = 0;
+ firstrcp = DmStrSpn( firstrcp, " \t" );
+ }
+
+ result = Expand( Buffer );
+ for( brk=strchr(result,'\\'); brk != NIL(char); brk=strchr(brk,'\\') )
+ if( brk[1] == '\n' )
+ *brk = ' ';
+ else
+ brk++;
+
+ DB_PRINT( "par", ("Scanning: [%s]", result) );
+
+ SET_TOKEN( &input, result );
+ brk = ":-^!|";
+ Def_targets = TRUE;
+
+ /* Scan the input rule line collecting targets, the operator, and any
+ * prerequisites. Stop when we run out of targets and prerequisites. */
+
+ while( *(tok = Get_token( &input, brk, TRUE )) != '\0' )
+ if( !op ) {
+ /* we are scanning targets and attributes
+ * check to see if token is an operator. */
+
+ op = Rule_op( tok );
+
+ if( !op ) {
+ /* define a new cell, or get old cell */
+ cp = Def_cell( tok );
+ DB_PRINT( "par", ("tg_cell [%s]", tok) );
+
+ if( (at = _is_attribute(tok)) != 0 ) {
+ /* Logically OR the attributes specified into one main
+ * ATTRIBUTE mask. */
+
+ if( at == A_SETDIR )
+ if( set_dir != NIL( char ) )
+ Warning( "Multiple .SETDIR attribute ignored" );
+ else
+ set_dir = DmStrDup( tok );
+
+ attr |= at;
+ }
+ else {
+ int tmp;
+
+ tmp = _is_special( tok );
+ if( _is_percent( tok ) ) percent++;
+
+ if( percent )
+ cp->ce_flag |= F_PERCENT;
+
+ if( special )
+ Fatal( "Special target must appear alone", tok );
+ else if( !(cp->ce_flag & F_MARK) ) {
+ /* Targets are kept in this list in lexically sorted order.
+ * This allows for easy equality comparison of target
+ * sets.*/
+ CELLPTR prev,cur;
+ for(prev=NIL(CELL),cur=targets;cur;prev=cur,cur=cur->ce_link)
+ if(strcmp(cur->CE_NAME,cp->CE_NAME) > 0)
+ break;
+
+ cp->ce_link = cur;
+
+ if (!prev)
+ targets = cp;
+ else
+ prev->ce_link = cp;
+
+ cp->ce_flag |= F_MARK | F_EXPLICIT;
+ special = tmp;
+ }
+ }
+ }
+ else {
+ /* found an operator so empty out break list
+ * and clear mark bits on target list, setting them all to F_USED*/
+
+ brk = "";
+ for( cp=targets; cp != NIL(CELL); cp=cp->ce_link ) {
+ cp->ce_flag ^= F_MARK;
+ cp->ce_flag |= F_USED;
+ }
+
+ Def_targets = FALSE;
+ }
+ }
+ else {
+ /* Scanning prerequisites so build the prerequisite list. We use
+ * F_MARK flag to make certain we have only a single copy of the
+ * prerequisite in the list */
+
+ cp = Def_cell( tok );
+
+ if( _is_percent( tok ) ) {
+ if( !percent && !attr )
+ Fatal( "Syntax error in %% rule, missing %% target");
+ mixed_glob_prq = 1;
+ }
+
+ if( cp->ce_flag & F_USED ) {
+ if( cp->ce_attr & A_COMPOSITE )
+ continue;
+ else
+ Fatal( "Detected circular dependency in graph at [%s]",
+ cp->CE_NAME );
+ }
+ else if( !(cp->ce_flag & F_MARK) ) {
+ DB_PRINT( "par", ("pq_cell [%s]", tok) );
+ cp->ce_flag |= F_MARK;
+
+ if( prereqtail == NIL(CELL) ) /* keep prereq's in order */
+ prereq = cp;
+ else
+ prereqtail->ce_link = cp;
+
+ prereqtail = cp;
+ cp->ce_link = NIL(CELL);
+ }
+ else if( !(cp->ce_attr & A_LIBRARY) && (Verbose & V_WARNALL))
+ Warning("Duplicate entry [%s] in prerequisite list",cp->CE_NAME);
+ }
+
+ /* Check to see if we have a percent rule that has only global
+ * prerequisites. If so then set the flag so that later on, we don't issue
+ * an error if such targets supply an empty set of rules. */
+
+ if( percent && !mixed_glob_prq && (prereq != NIL(CELL)) )
+ _sv_globprq_only = 1;
+
+ /* It's ok to have targets with attributes, and no prerequisites, but it's
+ * not ok to have no targets and no attributes, or no operator */
+
+ if( !op ) {
+ CLEAR_TOKEN( &input );
+ DB_PRINT( "par", ("Not a rule [%s]", Buffer) );
+ DB_RETURN( 0 );
+ }
+
+ if( !attr && targets == NIL(CELL) ) {
+ Fatal( "Missing targets or attributes in rule" );
+ if( set_dir != NIL( char )) FREE( set_dir );
+ DB_RETURN( 0 );
+ }
+
+ /* We have established we have a legal rules line, so we must process it.
+ * In doing so we must handle any special targets. Special targets must
+ * appear alone possibly accompanied by attributes.
+ * NOTE: special != 0 ==> targets != NIL(CELL) */
+
+ if( prereqtail != NIL(CELL) ) prereqtail->ce_link = NIL(CELL);
+
+ /* Clear out MARK bits used in duplicate checking. I originally wanted
+ * to do this as the lists get processed but that got too error prone
+ * so I bit the bullit and added these two loops. */
+
+ for( cp=prereq; cp != NIL(CELL); cp=cp->ce_link ) cp->ce_flag &= ~F_MARK;
+ for( cp=targets; cp != NIL(CELL); cp=cp->ce_link ) cp->ce_flag &= ~F_USED;
+
+ /* Check to see if the previous rule line was bound if, not the call
+ * Bind_rules_to_targets to go and bind the line */
+
+ if( _sv_rules != NIL(STRING) ) Bind_rules_to_targets( F_DEFAULT );
+
+ /* Add the first recipe line to the list */
+ if( firstrcp != NIL( char ) )
+ Add_recipe_to_list( firstrcp, TRUE, FALSE );
+
+ /* Save these prior to calling _do_targets, since _build_graph needs the
+ * _sv_setdir value for matching edges. */
+ _sv_op = op;
+ _sv_setdir = set_dir;
+
+ if( special )
+ _do_special( special, op, attr, set_dir, targets, prereq, state );
+ else
+ *state = _do_targets( op, attr, set_dir, targets, prereq );
+
+ DB_RETURN( 1 );
+}
+
+
+PUBLIC int
+Rule_op( op )/*
+================
+ Check the passed in op string and map it to one of the rule operators */
+char *op;
+{
+ int ret = 0;
+
+ DB_ENTER( "rule_op" );
+
+ if( *op == TGT_DEP_SEP ) {
+ ret = R_OP_CL;
+ op++;
+
+ /* All rule operations begin with a :, but may include any one of the
+ * four modifiers. In order for the rule to be properly mapped we must
+ * check for each of the modifiers in turn, building up our return bit
+ * string. */
+
+ while( *op && ret )
+ switch( *op ) {
+ case ':': ret |= R_OP_DCL; op++; break;
+ case '!': ret |= R_OP_BG; op++; break;
+ case '^': ret |= R_OP_UP; op++; break;
+ case '-': ret |= R_OP_MI; op++; break;
+ case '|': ret |= R_OP_OR; op++; break;
+
+ default : ret = 0; /* an invalid modifier, chuck whole string */
+ }
+
+ if( *op != '\0' ) ret = 0;
+ }
+
+ DB_RETURN( ret );
+}
+
+
+PUBLIC void
+Add_recipe_to_list( rule, white_too, no_check )/*
+=================================================
+ Take the provided string and add it to the list of recipe lines
+ we are saving to be added to the list of targets we have built
+ previously. If white_too == TRUE add the rule EVEN IF it contains only
+ whitespace. */
+char *rule;
+int white_too;
+int no_check;
+{
+ DB_ENTER( "Add_recipe_to_list" );
+
+ if( rule != NIL( char ) && (*rule != '\0' || white_too) ) {
+ DB_PRINT( "par", ("Adding recipe [%s]", rule) );
+ _sv_crule = Def_recipe( rule, _sv_crule, white_too, no_check );
+
+ if( _sv_rules == NIL(STRING) )
+ _sv_rules = _sv_crule;
+ }
+
+ DB_VOID_RETURN;
+}
+
+
+PUBLIC void
+Bind_rules_to_targets( flag )/*
+===============================
+ Take the rules we have defined and bind them with proper attributes
+ to the targets that were previously defined in the parse. The
+ attributes that get passed here are merged with those that are were
+ previously defined. (namely F_SINGLE) */
+int flag;
+{
+ CELLPTR tg; /* pointer to current target in list */
+ LINKPTR lp; /* pointer to link cell */
+ int magic; /* TRUE if target is .xxx.yyy form */
+ int tflag; /* TRUE if we assigned targets here */
+
+ DB_ENTER( "Bind_rules_to_targets" );
+
+ /* This line is needed since Parse may call us twice when the last
+ * GROUP rule appears at the end of file. In this case the rules
+ * have already been bound and we want to ignore them. */
+
+ if( _sv_targets == NIL(CELL) ) { DB_VOID_RETURN; }
+
+ tflag = FALSE;
+ flag |= (_sv_flag & F_SINGLE);
+ flag |= ((_sv_attr & A_GROUP) ? F_GROUP : 0);
+
+ for( tg = _sv_targets; tg != NIL(CELL); tg = tg->ce_link ) {
+ DB_PRINT( "par", ("Binding to %s, %04x", tg->CE_NAME, tg->ce_flag) );
+ magic = tg->ce_flag & F_PERCENT;
+
+#if 0
+ /* Check to see if we had a rule of the form '%.o : a.h b.h ; xxx'
+ * In which case we must build a NULL prq node to hold the recipe */
+ if( _sv_globprq_only && (_sv_rules != NIL(STRING)) )
+ _build_graph( _sv_op, tg, NIL(CELL) );
+#endif
+
+ /* NOTE: For targets that are magic we ignore any previously defined
+ * rules. ie. We throw away the old definition and use the new.*/
+ if( !(tg->ce_flag & F_MULTI) && !magic && (tg->CE_RECIPE != NIL(STRING))
+ && !_sp_target && (_sv_rules != NIL(STRING)) )
+ Fatal( "Multiply defined recipe for target %s", tg->CE_NAME );
+
+ if( (magic || _sp_target) && (_sv_rules == NIL(STRING)) &&
+ !(tg->ce_flag & F_SPECIAL) && !_sv_globprq_only )
+ Warning( "Empty recipe for special target %s", tg->CE_NAME );
+
+ if( magic ) {
+ CELLPTR ep;
+
+ for( ep=_sv_edgel; ep != NIL(CELL); ep=ep->ce_link ) {
+ _set_attributes( _sv_attr, _sv_setdir, ep );
+ ep->ce_flag |= (F_TARGET|flag);
+
+ if( _sv_rules != NIL(STRING) ) {
+ ep->ce_recipe = _sv_rules;
+ ep->ce_indprq = _sv_glb_prq;
+ }
+ }
+ }
+ else {
+ tg->ce_attr |= _sv_attr;
+ tg->ce_flag |= flag;
+
+ if( _sv_rules != NIL(STRING) ) {
+ tg->ce_recipe = _sv_rules;
+ tg->ce_flag |= F_RULES | F_TARGET;
+
+ /* Bind the current set of prerequisites as belonging to the
+ * original recipe given for the target */
+ for( lp=tg->ce_prq; lp != NIL(LINK); lp = lp->cl_next )
+ if( !(lp->cl_flag & F_USED) ) lp->cl_flag |= F_TARGET;
+ }
+ else for( lp=tg->ce_prq; lp != NIL(LINK); lp = lp->cl_next )
+ lp->cl_flag |= F_USED;
+ }
+
+ tflag |= _add_root(tg);
+ }
+
+ if( tflag ) Target = TRUE;
+ if( _sv_setdir ) FREE(_sv_setdir);
+ _sv_rules = NIL(STRING);
+ _sv_crule = NIL(STRING);
+ _sv_targets = NIL(CELL);
+ _sv_glb_prq = NIL(LINK);
+ _sv_edgel = NIL(CELL);
+ _sp_target = FALSE;
+ _sv_globprq_only = 0;
+
+ DB_VOID_RETURN;
+}
+
+
+
+PUBLIC int
+Set_group_attributes( list )/*
+==============================
+ Scan list looking for the standard @ and - (as in recipe line defs)
+ and set the flags accordingly so that they apply when we bind the
+ rules to the appropriate targets. */
+char *list;
+{
+ int res = FALSE;
+
+ if ( !((_sv_attr|Glob_attr)&A_IGNOREGROUP) ) {
+ res = (*DmStrSpn(list,"@-%+ \t") == '[');
+ if( res ) _sv_attr |= Rcp_attribute(list);
+ }
+
+ return(res);
+}
+
+
+static void
+_do_special( special, op, attr, set_dir, target, prereq, state )/*
+==================================================================
+ Process a special target. So far the only special targets we have
+ are those recognized by the _is_special function.
+
+ target is always only a single special target.
+
+ NOTE: For the cases of .IMPORT, and .INCLUDE, the cells created by the
+ parser are never freed. This is due to the fact that it is too much
+ trouble to get them out of the hash table once they are defined, and
+ if they are per chance used again it will be ok, anyway, since the
+ cell is not really used by the code below. */
+
+int special;
+int op;
+t_attr attr;
+char *set_dir;
+CELLPTR target;
+CELLPTR prereq;
+int *state;
+{
+ HASHPTR hp; /* pointer to macro def cell */
+ CELLPTR cp; /* temporary pointer into cells list */
+ CELLPTR dp; /* pointer to directory dir cell */
+ LINKPTR lp; /* pointer at prerequisite list */
+ char *dir; /* current dir to prepend */
+ char *path; /* resulting path to try to read */
+ char *name; /* File name for processing a .INCLUDE */
+ char *tmp; /* temporary string pointer */
+ FILE *fil; /* File descriptor returned by Openfile */
+
+ DB_ENTER( "_do_special" );
+
+ target->ce_flag = F_SPECIAL; /* mark the target as special */
+
+ switch( special ) {
+ case ST_EXPORT:
+ for( ; prereq != NIL(CELL); prereq = prereq->ce_link ) {
+ DB_PRINT( "par", ("Exporting [%s]", prereq->CE_NAME) );
+ hp = GET_MACRO( prereq->CE_NAME );
+
+ if( hp != NIL(HASH) ) {
+ char *tmpstr = hp->ht_value;
+
+ if( tmpstr == NIL(char) ) tmpstr = "";
+
+ if( Write_env_string( prereq->CE_NAME, tmpstr ) != 0 )
+ Warning( "Could not export %s", prereq->CE_NAME );
+ }
+ }
+ break;
+
+ /* Simply cause the parser to fail on the next input read */
+ case ST_EXIT:
+ Skip_to_eof = TRUE;
+ break;
+
+ case ST_IMPORT:
+ for( ; prereq != NIL(CELL); prereq = prereq->ce_link ) {
+ char *tmpstr;
+
+ DB_PRINT( "par", ("Importing [%s]", prereq->CE_NAME) );
+
+ if( strcmp(prereq->CE_NAME, ".EVERYTHING") == 0 ) {
+ t_attr sattr = Glob_attr;
+ Glob_attr |= A_SILENT;
+
+ ReadEnvironment();
+
+ Glob_attr = sattr;
+ }
+ else {
+ tmpstr = Read_env_string( prereq->CE_NAME );
+
+ if( tmpstr != NIL(char) )
+ Def_macro(prereq->CE_NAME, tmpstr, M_EXPANDED|M_LITERAL);
+ else
+ if( !((Glob_attr | attr) & A_IGNORE) )
+ Fatal("Imported macro `%s' not found",prereq->CE_NAME);
+ }
+ }
+
+ attr &= ~A_IGNORE;
+ break;
+
+ case ST_INCLUDE:
+ {
+ int pushed = FALSE;
+ int first = (attr & A_FIRST);
+ int ignore = (((Glob_attr | attr) & A_IGNORE) != 0);
+ int found = FALSE;
+ int noinf = (attr & A_NOINFER);
+ LINKPTR prqlnk = NIL(LINK);
+ LINKPTR prqlst = NIL(LINK);
+
+ if( prereq == NIL(CELL) ) Fatal( "No .INCLUDE file(s) specified" );
+
+ dp = Def_cell( ".INCLUDEDIRS" );
+
+ if( (attr & A_SETDIR) && *(dir = strchr(set_dir, '=')+1) )
+ pushed = Push_dir( dir, ".INCLUDE", ignore );
+
+ for( cp=prereq; cp != NIL(CELL); cp = cp->ce_link ) {
+ LINKPTR ltmp;
+ TALLOC(ltmp, 1, LINK);
+ ltmp->cl_prq = cp;
+
+ if( prqlnk == NIL(LINK) )
+ prqlst = ltmp;
+ else
+ prqlnk->cl_next = ltmp;
+
+ prqlnk = ltmp;
+ }
+
+ for( ; prqlst != NIL(LINK); FREE(prqlst), prqlst=prqlnk ) {
+ prqlnk = prqlst->cl_next;
+ cp = prqlst->cl_prq;
+ name = cp->CE_NAME;
+
+ /* Leave this here, it ensures that prqlst gets propely free'd */
+ if ( first && found )
+ continue;
+
+ if( *name == '<' ) {
+ /* We have a file name enclosed in <....>
+ * so get rid of the <> arround the file name */
+
+ name++;
+ if( (tmp = strrchr( name, '>' )) != NIL( char ) )
+ *tmp = 0;
+
+ if( If_root_path( name ) )
+ fil = Openfile( name, FALSE, FALSE );
+ else
+ fil = NIL(FILE);
+ }
+ else
+ fil = Openfile( name, FALSE, FALSE );
+
+ if( fil == NIL(FILE) ) { /*if true ==> not found in current dir*/
+
+ /* Now we must scan the list of prerequisites for .INCLUDEDIRS
+ * looking for the file in each of the specified directories.
+ * if we don't find it then we issue an error. The error
+ * message is suppressed if the .IGNORE attribute of attr is
+ * set. If a file is found we call Parse on the file to
+ * perform the parse and then continue on from where we left
+ * off. */
+
+ for(lp=dp->CE_PRQ; lp && fil == NIL(FILE); lp=lp->cl_next) {
+ dir = lp->cl_prq->CE_NAME;
+ if( strchr(dir, '$') ) dir = Expand(dir);
+ path = Build_path( dir, name );
+
+ DB_PRINT( "par", ("Trying to include [%s]", path) );
+
+ fil = Openfile( path, FALSE, FALSE );
+ if( dir != lp->cl_prq->CE_NAME ) FREE(dir);
+ }
+ }
+
+ if (!noinf && fil == NIL(FILE)) {
+ t_attr glob = Glob_attr;
+ t_attr cattr = prqlst->cl_prq->ce_attr;
+
+ prqlst->cl_next = NIL(LINK);
+ Glob_attr |= ((attr&A_IGNORE)|A_SILENT);
+ prqlst->cl_prq->ce_attr &= ~A_FRINGE;
+
+ fil = TryFiles(prqlst);
+
+ Glob_attr = glob;
+ prqlst->cl_prq->ce_attr |= (cattr & A_FRINGE);
+ }
+
+ if( fil != NIL(FILE) ) {
+ Parse( fil );
+ found = TRUE;
+ }
+ else if( !(ignore || first) )
+ Fatal( "Include file %s, not found", name );
+ }
+
+ if ( !ignore && first && !found )
+ Fatal( "No include file was found" );
+
+ if( pushed ) Pop_dir(FALSE);
+ attr &= ~(A_IGNORE|A_SETDIR|A_FIRST|A_NOINFER);
+ }
+ break;
+
+ case ST_SOURCE:
+ /* case ST_SUFFIXES: */
+ if( prereq != NIL(CELL) )
+ _do_targets( op & (R_OP_CL | R_OP_MI | R_OP_UP), attr, set_dir,
+ target, prereq );
+ else {
+ /* The old semantics of .SOURCE were that an empty list of
+ * prerequisites clears the .SOURCE list. So we must implement
+ * that here as a clearout prerequisite operation. Since this is
+ * a standard operation with the :- opcode we can simply call the
+ * proper routine with the target cell and it should do the trick
+ */
+
+ if( op == R_OP_CL || (op & R_OP_MI) )
+ Clear_prerequisites( target );
+ }
+
+ op &= ~(R_OP_MI | R_OP_UP);
+ break;
+
+ case ST_KEEP:
+ if( Keep_state != NIL(char) ) break;
+ Def_macro( ".KEEP_STATE", "_state.mk", M_EXPANDED );
+ break;
+
+ case ST_REST:
+ /* The rest of the special targets can all take rules, as such they
+ * must be able to affect the state of the parser. */
+
+ {
+ int s_targ = Target;
+
+ Target = TRUE;
+ _sp_target = TRUE;
+ *state = _do_targets( op, attr, set_dir, target, prereq );
+ Target = s_targ;
+
+ target->ce_flag |= F_TARGET;
+
+ attr = A_DEFAULT;
+ op = R_OP_CL;
+ }
+ break;
+
+ default:break;
+ }
+
+ if( op != R_OP_CL ) Warning( "Modifier(s) for operator ignored" );
+ if( attr != A_DEFAULT ) Warning( "Extra attributes ignored" );
+
+ DB_VOID_RETURN;
+}
+
+
+
+static int
+_do_targets( op, attr, set_dir, targets, prereq )/*
+================================================= */
+int op;
+t_attr attr;
+char *set_dir;
+CELLPTR targets;
+CELLPTR prereq;
+{
+ CELLPTR tg1; /* temporary target pointer */
+ CELLPTR tp1; /* temporary prerequisite pointer */
+ LINKPTR prev_cell; /* pointer for .UPDATEALL processing */
+ char *p; /* temporary char pointer */
+ int tflag = FALSE; /* set to TRUE if we add target to root */
+
+ DB_ENTER( "_do_targets" );
+
+ if( attr & A_UPDATEALL ) {
+ if( targets == NIL(CELL) )
+ Fatal( ".UPDATEALL attribute requires non-empty list of targets" );
+
+ if (targets->ce_set == NIL(CELL)) {
+ for(
+ prev_cell=CeMeToo(targets),tg1=targets->ce_link;
+ tg1 != NIL(CELL);
+ tg1=tg1->ce_link
+ ) {
+ if (tg1->ce_set)
+ Fatal( "Target [%s] appears on multiple .UPDATEALL lists",
+ tg1->CE_NAME);
+ tg1->ce_set = targets;
+ TALLOC(prev_cell->cl_next, 1, LINK);
+ prev_cell = prev_cell->cl_next;
+ prev_cell->cl_prq = tg1;
+ }
+ targets->ce_set = targets;
+ }
+ else {
+ LINKPTR ap;
+ CELLPTR tp;
+
+ tp = targets;
+ ap = CeMeToo(targets);
+ while (ap && tp && ap->cl_prq == tp && tp->ce_set == targets) {
+ ap = ap->cl_next;
+ tp = tp->ce_link;
+ }
+ if (ap || tp)
+ Fatal("Inconsistent .UPDATEALL lists for target [%s]",
+ targets->CE_NAME);
+ }
+ targets->ce_link = NIL(CELL);
+ }
+
+ for( tg1 = targets; tg1 != NIL(CELL); tg1 = tg1->ce_link ) {
+ /* Check each target. Check for inconsistencies between :: and : rule
+ * sets. :: may follow either : or :: but not the reverse.
+ *
+ * Any targets that contain :: rules are represented by a prerequisite
+ * list hanging off the main target cell where each of the prerequisites
+ * is a copy of the target cell but is not entered into the hash table.
+ */
+ int magic = (tg1->ce_flag & F_PERCENT) && !(tg1->ce_flag & F_MAGIC);
+
+ if( !(op & R_OP_DCL ) && (tg1->ce_flag & F_MULTI) && !magic )
+ Fatal( "':' vs '::' inconsistency in rules for %s", tg1->CE_NAME );
+
+ if( magic ) {
+ if (op & R_OP_OR)
+ for(tp1=prereq; tp1; tp1=tp1->ce_link) {
+ CELLPTR tmpcell = tp1->ce_link;
+ tp1->ce_link = NIL(CELL);
+ _build_graph(op,tg1,tp1);
+ tp1->ce_link = tmpcell;
+ }
+ else
+ prereq = _build_graph(op,tg1,prereq);
+ }
+ else if( !(tg1->ce_flag & F_SPECIAL) &&
+ (prereq == NIL(CELL)) &&
+ (p = _is_magic( tg1->CE_NAME )) != NIL(char))
+ _do_magic( op, p, tg1, prereq, attr, set_dir );
+ else if( op & R_OP_DCL ) {
+ CELLPTR tmp_cell = _make_multi(tg1);
+ tflag |= _add_root(tg1);
+ targets = _replace_cell( targets, tg1, tmp_cell );
+ tg1 = tmp_cell;
+ }
+
+ if( !magic ) _set_attributes( attr, set_dir, tg1 );
+
+ /* Build the proper prerequisite list of the target. If the `-',
+ * modifier was used clear the prerequisite list before adding any
+ * new prerequisites. Else add them to the head/tail as appropriate.
+ *
+ * If the target has F_PERCENT set then no prerequisites are used. */
+
+ if( !(tg1->ce_flag & F_PERCENT) ) {
+ if( op & R_OP_MI ) Clear_prerequisites( tg1 );
+
+ if( (op & R_OP_UP) && (tg1->ce_prq != NIL(LINK)) )
+ _stick_at_head( tg1, prereq );
+ else for( tp1=prereq; tp1 != NIL(CELL); tp1 = tp1->ce_link )
+ Add_prerequisite( tg1, tp1, FALSE, FALSE );
+ }
+ else if( op & (R_OP_MI | R_OP_UP) )
+ Warning( "Modifier(s) `^!' for %-meta target ignored" );
+ }
+
+ if(tflag)
+ Target = TRUE;
+
+ /* Check to see if we have NO targets but some attributes. IF so then
+ * apply all of the attributes to the complete list of prerequisites.
+ */
+
+ if( (targets == NIL(CELL)) && attr )
+ if( prereq != NIL(CELL) )
+ for( tp1=prereq; tp1 != NIL(CELL); tp1 = tp1->ce_link )
+ _set_attributes( attr, set_dir, tp1 );
+ else
+ _set_global_attr( attr );
+
+ /* Now that we have built the lists of targets, the parser must parse the
+ * rules if there are any. However we must start the rule list with the
+ * rule specified as via the ; kludge, if there is one */
+ _sv_targets = targets;
+ _sv_attr = attr;
+ _sv_flag = ((op & R_OP_BG) ? F_SINGLE : F_DEFAULT);
+
+ DB_RETURN( RULE_SCAN );
+}
+
+
+static int
+_do_magic( op, dot, target, prereq, attr, set_dir )/*
+=====================================================
+ This function takes a magic target of the form .<chars>.<chars> or
+ .<chars> and builds the appropriate % rules for that target.
+
+ The function builds the % rule, `%.o : %.c' from .c.o, and
+ `%.a :' from .a */
+
+int op;
+char *dot;
+CELLPTR target;
+CELLPTR prereq;
+t_attr attr;
+char *set_dir;
+{
+ CELLPTR tg;
+ CELLPTR prq;
+ char *tmp, *tmp2;
+
+ DB_ENTER( "_do_magic" );
+
+ if( prereq != NIL(CELL) )
+ Warning( "Ignoring prerequisites of old style meta-target" );
+
+ if( dot == target->CE_NAME ) { /* its of the form .a */
+ tg = Def_cell( "%" ); /* ==> no prerequisite */
+ tmp = _build_meta( target->CE_NAME );
+ prq = Def_cell( tmp );
+ FREE( tmp );
+
+ _build_graph( op, tg, prq );
+ }
+ else {
+ tmp = _build_meta( dot );
+ tg = Def_cell( tmp );
+ FREE( tmp );
+
+ tmp = _build_meta( tmp2 = DmSubStr( target->CE_NAME, dot ) );
+ prq = Def_cell( tmp );
+ FREE( tmp );
+ FREE( tmp2 );
+
+ _build_graph( op, tg, prq );
+ }
+
+ tg->ce_flag |= F_PERCENT;
+ target->ce_flag |= (F_MAGIC|F_PERCENT);
+
+ _set_attributes( attr, set_dir, tg );
+
+ DB_RETURN(1);
+}
+
+
+static CELLPTR
+_replace_cell( lst, cell, rep )
+CELLPTR lst;
+CELLPTR cell;
+CELLPTR rep;
+{
+ register CELLPTR tp;
+
+ if( lst == cell ) {
+ rep->ce_link = lst->ce_link;
+ lst = rep;
+ }
+ else {
+ for( tp=lst; tp->ce_link != cell; tp=tp->ce_link );
+ rep->ce_link = tp->ce_link->ce_link;
+ tp->ce_link = rep;
+ }
+
+ return(lst);
+}
+
+
+static char *
+_build_meta( name )/*
+=====================
+ Check to see if the name is of the form .c~ if so and if Augmake
+ translation is enabled then return s.%.c, else return %.suff, where if the
+ suffix ends in '~' then leave it be.*/
+char *name;
+{
+ char *tmp;
+ int test = (STOBOOL(Augmake) ? name[strlen(name)-1] == '~' : 0);
+
+ tmp = DmStrJoin( test ? "s.%" : "%", name, -1, FALSE);
+ if( test ) tmp[ strlen(tmp)-1 ] = '\0';
+
+ return(tmp);
+}
+
+
+static CELLPTR
+_build_graph( op, target, prereq )/*
+====================================
+ This function is called to build the graph for the % rule given by
+ target : prereq cell combination. This function assumes that target
+ is a % target and that prereq is a single % prerequisite. R_OP_CL
+ rules replace existing rules if any, only R_OP_CL works for meta-rules.
+ %.o :: %.c is meaningless. If target has ce_all set then all the cells
+ on the list must match in order for the match to work. If prereq->ce_link
+ is not nil then all prerequisites listed by the link set must match also.
+ This latter match is more difficult because in general the prerequisite
+ sets may not be listed in the same order.
+
+ It also assumes that target cell has F_PERCENT set already. */
+int op;
+CELLPTR target;
+CELLPTR prereq;
+{
+ LINKPTR edl;
+ CELLPTR edge;
+ CELLPTR tpq,cur;
+ int match;
+
+ DB_ENTER( "_build_graph" );
+ DB_PRINT( "%", ("Building graph for [%s : %s]", target->CE_NAME,
+ (prereq == NIL(CELL)) ? "" : prereq->CE_NAME) );
+
+ tpq = NIL(CELL);
+ for(cur=prereq;cur;cur=cur->ce_link) {
+ char *name = cur->CE_NAME;
+ int len = strlen(name);
+
+ if( *name == '\'' && name[len-1]=='\'' ){
+ _add_global_prereq( cur );
+ name[len-1] = '\0';
+ strcpy(name,name+1);
+ }
+ else {
+ if (tpq)
+ tpq->ce_link = cur;
+ else
+ prereq = cur;
+ tpq = cur;
+ }
+ }
+ if(tpq)
+ tpq->ce_link=NIL(CELL);
+ else
+ prereq = NIL(CELL);
+
+ /* Search the list of prerequisites for the current target and see if
+ * any of them match the current %-meta's : prereq's pair. NOTE that
+ * %-metas are built as if they were F_MULTI targets. */
+ match = FALSE;
+ for(edl=target->ce_prq; !match && edl != NIL(LINK); edl=edl->cl_next) {
+ LINKPTR l1,l2;
+ edge = edl->cl_prq;
+
+ DB_PRINT("%", ("Trying to match [%s]",edge?edge->CE_NAME:"(nil)"));
+
+ /* First we match the target sets, if this fails then we don't have to
+ * bother with the prerequisite sets. The targets sets are sorted.
+ * this makes life very simple. */
+
+ l1 = CeMeToo(target);
+ l2 = CeMeToo(edge);
+ while(l1 && l2 && l1->cl_prq == l2->cl_prq) {
+ l1=l1->cl_next;
+ l2=l2->cl_next;
+ }
+
+ if (l1 || l2)
+ continue;
+
+ /* target sets match, so check prerequisites. */
+
+ if( (!edge->ce_prq && !prereq)
+ || ( edge->ce_prq
+ && ( edge->ce_dir == _sv_setdir
+ || ( edge->ce_dir
+ && _sv_setdir
+ && !strcmp(edge->ce_dir,strchr(_sv_setdir,'=')+1)
+ )
+ )
+ )
+ ) {
+ LINKPTR prql;
+
+ /* this is a really gross way to compare two sets, it's n^2 but
+ * since the sets are assumed to always be tiny, it should be ok. */
+ for(tpq=prereq; tpq; tpq=tpq->ce_link) {
+ for(prql=edge->ce_prq;prql;prql=prql->cl_next)
+ if (prql->cl_prq == tpq)
+ break;
+
+ if(prql == NIL(LINK))
+ break;
+
+ prql->cl_prq->ce_flag |= F_MARK;
+ }
+
+ if (tpq == NIL(CELL)) {
+ for(prql=edge->ce_prq;prql;prql=prql->cl_next)
+ if(!(prql->cl_prq->ce_flag & F_MARK))
+ break;
+
+ if(prql == NIL(LINK))
+ match = TRUE;
+ }
+
+ /* clean up the mark bits. */
+ for(prql=edge->ce_prq;prql;prql=prql->cl_next)
+ prql->cl_prq->ce_flag &= ~F_MARK;
+ }
+ }
+
+ if( match ) {
+ /* match is TRUE hence, we found an edge joining the target and the
+ * prerequisite so reset the new edge so that new values replace it. */
+ DB_PRINT( "%", ("It's an old edge") );
+
+ edge->ce_dir = NIL(char);
+ edge->ce_flag &= (F_PERCENT|F_MAGIC|F_DFA);
+ edge->ce_attr &= A_NOINFER;
+ }
+ else {
+ DB_PRINT( "%", ("Adding a new edge") );
+
+ edge = _make_multi(target);
+
+ for(edl=CeMeToo(target);edl;edl=edl->cl_next) {
+ if( !((tpq=edl->cl_prq)->ce_flag & F_DFA) ) {
+ Add_nfa( tpq->CE_NAME );
+ tpq->ce_flag |= F_DFA;
+ }
+
+ edl->cl_prq->ce_set = edge;
+ }
+
+ edge->ce_all = target->ce_all;
+ target->ce_all.cl_next = NIL(LINK);
+ target->ce_set = NIL(CELL);
+
+ for(tpq=prereq; tpq; tpq=tpq->ce_link)
+ Add_prerequisite(edge, tpq, FALSE, TRUE);
+ }
+
+ if( op & R_OP_DCL )
+ Warning("'::' operator for meta-target '%s' ignored, ':' operator assumed.",
+ target->CE_NAME );
+
+ edge->ce_link = _sv_edgel;
+ _sv_edgel = edge;
+ _sv_globprq_only = 0;
+
+ DB_RETURN(NIL(CELL));
+}
+
+
+static CELLPTR
+_make_multi( tg )
+CELLPTR tg;
+{
+ CELLPTR cp;
+
+ /* This first handles the case when a : foo ; exists prior to seeing
+ * a :: fee; */
+ if( !(tg->ce_flag & F_MULTI) && (tg->ce_prq || tg->ce_recipe) ) {
+ TALLOC(cp, 1, CELL);
+ *cp = *tg;
+
+ tg->ce_prq = NIL(LINK);
+ tg->ce_flag |= F_RULES|F_MULTI|F_TARGET;
+ tg->ce_attr |= A_SEQ;
+ tg->ce_recipe = NIL(STRING);
+ tg->ce_dir = NIL(char);
+ cp->ce_count = ++tg->ce_index;
+ cp->ce_cond = NIL(STRING);
+ cp->ce_set = NIL(CELL);
+ cp->ce_all.cl_prq = cp;
+ CeNotMe(cp) = NIL(LINK);
+
+ Add_prerequisite(tg, cp, FALSE, TRUE);
+ }
+
+ TALLOC(cp, 1, CELL);
+ *cp = *tg;
+
+ if( !(tg->ce_flag & F_MULTI) ) {
+ tg->ce_prq = NIL(LINK);
+ tg->ce_flag |= F_RULES|F_MULTI|F_TARGET;
+ tg->ce_attr |= A_SEQ;
+ tg->ce_recipe = NIL(STRING);
+ tg->ce_dir = NIL(char);
+ cp->ce_cond = NIL(STRING);
+ }
+ else {
+ cp->ce_flag &= ~(F_RULES|F_MULTI);
+ cp->ce_attr &= ~A_SEQ;
+ cp->ce_prq = NIL(LINK);
+ cp->ce_index = 0;
+ cp->ce_cond = NIL(STRING);
+ }
+ cp->ce_count = ++tg->ce_index;
+ cp->ce_flag |= F_TARGET;
+ cp->ce_set = NIL(CELL);
+ cp->ce_all.cl_prq = cp;
+ CeNotMe(cp) = NIL(LINK);
+
+ Add_prerequisite(tg, cp, FALSE, TRUE);
+ return(cp);
+}
+
+
+static void
+_add_global_prereq( pq )/*
+==========================
+ Prerequisite is a non-% prerequisite for a %-rule target, add it to
+ the target's list of global prerequsites to add on match */
+CELLPTR pq;
+{
+ register LINKPTR ln;
+
+ for(ln=_sv_glb_prq; ln; ln=ln->cl_next)
+ if(strcmp(ln->cl_prq->CE_NAME,pq->CE_NAME) == 0)
+ return;
+
+ TALLOC( ln, 1, LINK );
+ ln->cl_next = _sv_glb_prq;
+ ln->cl_prq = pq;
+ _sv_glb_prq = ln;
+}
+
+
+
+static void
+_set_attributes( attr, set_dir, cp )/*
+======================================
+ Set the appropriate attributes for a cell */
+t_attr attr;
+char *set_dir;
+CELLPTR cp;
+{
+ char *dir;
+
+ DB_ENTER( "_set_attributes" );
+
+ /* If .SETDIR attribute is set then we have at least .SETDIR= in the
+ * set_dir string. So go and fishout what is at the end of the =.
+ * If not set and not NULL then propagate it to the target cell. */
+
+ if( attr & A_SETDIR ) {
+ char *p;
+ if( (p = strchr( set_dir, '=' )) != NULL )
+ dir = p + 1;
+
+ if( cp->ce_dir )
+ Warning( "Multiple .SETDIR for %s ignored", cp->CE_NAME );
+ else if( *dir )
+ cp->ce_dir = DmStrDup(dir);
+ }
+ cp->ce_attr |= attr; /* set rest of attributes for target */
+
+ DB_VOID_RETURN;
+}
+
+
+
+static void
+_set_global_attr( attr )/*
+==========================
+ Handle the setting of the global attribute functions based on
+ The attribute flags set in attr. */
+t_attr attr;
+{
+ t_attr flag;
+
+ /* Some compilers can't handle a switch on a long, and t_attr is now a long
+ * integer on some systems. foey! */
+ for( flag = MAX_ATTR; flag; flag >>= 1 )
+ if( flag & attr )
+ if( flag == A_PRECIOUS) Def_macro(".PRECIOUS", "y", M_EXPANDED);
+ else if( flag == A_SILENT) Def_macro(".SILENT", "y", M_EXPANDED);
+ else if( flag == A_IGNORE ) Def_macro(".IGNORE", "y", M_EXPANDED);
+ else if( flag == A_EPILOG ) Def_macro(".EPILOG", "y", M_EXPANDED);
+ else if( flag == A_PROLOG ) Def_macro(".PROLOG", "y", M_EXPANDED);
+ else if( flag == A_NOINFER ) Def_macro(".NOINFER", "y", M_EXPANDED);
+ else if( flag == A_SEQ ) Def_macro(".SEQUENTIAL","y", M_EXPANDED);
+ else if( flag == A_SHELL ) Def_macro(".USESHELL", "y", M_EXPANDED);
+ else if( flag == A_MKSARGS ) Def_macro(".MKSARGS", "y", M_EXPANDED);
+ else if( flag == A_SWAP ) Def_macro(".SWAP", "y", M_EXPANDED);
+
+ attr &= ~A_GLOB;
+ if( attr ) Warning( "Non global attribute(s) ignored" );
+}
+
+
+
+static void
+_stick_at_head( cp, pq )/*
+==========================
+ Add the prerequisite list to the head of the existing prerequisite
+ list */
+
+CELLPTR cp; /* cell for target node */
+CELLPTR pq; /* list of prerequisites to add */
+{
+ DB_ENTER( "_stick_at_head" );
+
+ if( pq->ce_link != NIL(CELL) ) _stick_at_head( cp, pq->ce_link );
+ Add_prerequisite( cp, pq, TRUE, FALSE );
+
+ DB_VOID_RETURN;
+}
+
+
+
+static t_attr
+_is_attribute( name )/*
+=======================
+ Check the passed name against the list of valid attributes and return the
+ attribute index if it is, else return 0, indicating the name is not a valid
+ attribute. The present attributes are defined in dmake.h as A_xxx #defines,
+ with the corresponding makefile specification: (note they must be named
+ exactly as defined below)
+
+ Valid attributes are: .IGNORE, .SETDIR=, .SILENT, .PRECIOUS, .LIBRARY,
+ .EPILOG, .PROLOG, .LIBRARYM, .SYMBOL, .UPDATEALL,
+ .USESHELL, .NOINFER, .PHONY, .SWAP, .SEQUENTIAL
+ .NOSTATE, .MKSARGS, .IGNOREGROUP, .GROUP, .FIRST
+ .EXECUTE, .ERRREMOVE
+
+ NOTE: The strcmp's are OK since at most three are ever executed for any
+ one attribute check, and that happens only when we can be fairly
+ certain we have an attribute. */
+char *name;
+{
+ t_attr attr = 0;
+
+ DB_ENTER( "_is_attribute" );
+
+ if( *name++ == '.' )
+ switch( *name )
+ {
+ case 'E':
+ if( !strcmp(name, "EPILOG") ) attr = A_EPILOG;
+ else if( !strcmp(name, "EXECUTE")) attr = A_EXECUTE;
+ else if( !strcmp(name, "ERRREMOVE")) attr = A_ERRREMOVE;
+ else attr = 0;
+ break;
+
+ /* A_FIRST implies A_IGNORE, handled in ST_INCLUDE */
+ case 'F':
+ attr = (strcmp(name, "FIRST")) ? 0 : A_FIRST;
+ break;
+
+ case 'G': attr = (strcmp(name, "GROUP")) ? 0 : A_GROUP; break;
+ case 'L': attr = (strcmp(name, "LIBRARY")) ? 0 : A_LIBRARY; break;
+ case 'M': attr = (strcmp(name, "MKSARGS")) ? 0 : A_MKSARGS; break;
+
+ case 'I':
+ if( !strcmp(name, "IGNORE") ) attr = A_IGNORE;
+ else if( !strcmp(name, "IGNOREGROUP")) attr = A_IGNOREGROUP;
+ else attr = 0;
+ break;
+
+ case 'N':
+ if( !strcmp(name, "NOINFER") ) attr = A_NOINFER;
+ else if( !strcmp(name, "NOSTATE")) attr = A_NOSTATE;
+ else attr = 0;
+ break;
+
+ case 'U':
+ if( !strcmp(name, "UPDATEALL") ) attr = A_UPDATEALL;
+ else if( !strcmp(name, "USESHELL")) attr = A_SHELL;
+ else attr = 0;
+ break;
+
+ case 'P':
+ if( !strcmp(name, "PRECIOUS") ) attr = A_PRECIOUS;
+ else if( !strcmp(name, "PROLOG") ) attr = A_PROLOG;
+ else if( !strcmp(name, "PHONY") ) attr = A_PHONY;
+ else attr = 0;
+ break;
+
+ case 'S':
+ if( !strncmp(name, "SETDIR=", 7) ) attr = A_SETDIR;
+ else if( !strcmp(name, "SILENT") ) attr = A_SILENT;
+ else if( !strcmp(name, "SYMBOL") ) attr = A_SYMBOL;
+ else if( !strcmp(name, "SEQUENTIAL")) attr = A_SEQ;
+ else if( !strcmp(name, "SWAP")) attr = A_SWAP;
+ else attr = 0;
+ break;
+ }
+
+ DB_RETURN( attr );
+}
+
+
+
+static int
+_is_special( tg )/*
+===================
+ This function returns TRUE if the name passed in represents a special
+ target, otherwise it returns false. A special target is one that has
+ a special meaning to dmake, and may require processing at the time that
+ it is parsed.
+
+ Current Special targets are:
+ .GROUPPROLOG .GROUPEPILOG .INCLUDE .IMPORT
+ .EXPORT .SOURCE .SUFFIXES .ERROR .EXIT
+ .INCLUDEDIRS .MAKEFILES .REMOVE .KEEP_STATE
+*/
+char *tg;
+{
+ DB_ENTER( "_is_special" );
+
+ if( *tg++ != '.' ) DB_RETURN( 0 );
+
+ switch( *tg )
+ {
+ case 'E':
+ if( !strcmp( tg, "ERROR" ) ) DB_RETURN( ST_REST );
+ else if( !strcmp( tg, "EXPORT" ) ) DB_RETURN( ST_EXPORT );
+ else if( !strcmp( tg, "EXIT" ) ) DB_RETURN( ST_EXIT );
+ break;
+
+ case 'G':
+ if( !strcmp( tg, "GROUPPROLOG" )) DB_RETURN( ST_REST );
+ else if( !strcmp( tg, "GROUPEPILOG" )) DB_RETURN( ST_REST );
+ break;
+
+ case 'I':
+ if( !strcmp( tg, "IMPORT" ) ) DB_RETURN( ST_IMPORT );
+ else if( !strcmp( tg, "INCLUDE" ) ) DB_RETURN( ST_INCLUDE );
+ else if( !strcmp( tg, "INCLUDEDIRS" )) DB_RETURN( ST_REST );
+ break;
+
+ case 'K':
+ if( !strcmp( tg, "KEEP_STATE" ) ) DB_RETURN( ST_KEEP );
+ break;
+
+ case 'M':
+ if( !strcmp( tg, "MAKEFILES" ) ) DB_RETURN( ST_REST );
+ break;
+
+ case 'R':
+ if( !strcmp( tg, "REMOVE" ) ) DB_RETURN( ST_REST );
+ break;
+
+ case 'S':
+ if( !strncmp( tg, "SOURCE", 6 ) ) DB_RETURN( ST_SOURCE );
+ else if( !strncmp(tg, "SUFFIXES", 8 )) DB_RETURN( ST_SOURCE );
+ break;
+ }
+
+ DB_RETURN( 0 );
+}
+
+
+
+static int
+_is_percent( np )/*
+===================
+ return TRUE if np points at a string containing a % sign */
+char *np;
+{
+ return( (strchr(np,'%') && (*np != '\'' && np[strlen(np)-1] != '\'')) ?
+ TRUE : FALSE );
+}
+
+
+static char *
+_is_magic( np )/*
+=================
+ return TRUE if np points at a string of the form
+ .<chars>.<chars> or .<chars>
+ where chars are only alpha characters.
+
+ NOTE: reject target if it begins with ./ or ../ */
+char *np;
+{
+ register char *n;
+
+ n = np;
+ if( *n != '.' ) return( NIL(char) );
+ if (strchr(DirBrkStr, *(n+1))!=NULL || *(n+1) == '.' )
+ return (NIL(char));
+
+ for( n++; isgraph(*n) && (*n != '.'); n++ );
+
+ if( *n != '\0' ) {
+ if( *n != '.' ) return( NIL(char) );
+ for( np = n++; isgraph( *n ) && (*n != '.'); n++ );
+ if( *n != '\0' ) return( NIL(char) );
+ }
+ else if( STOBOOL(Augmake) )
+ return( NIL(char) );
+
+ /* np points at the second . of .<chars>.<chars> string.
+ * if the special target is of the form .<chars> then np points at the
+ * first . in the token. */
+
+ return( np );
+}
+
+
+static int
+_add_root(tg)
+CELLPTR tg;
+{
+ int res = FALSE;
+
+ if(tg == Targets)
+ return(TRUE);
+
+ if( !Target && !(tg->ce_flag & (F_SPECIAL|F_PERCENT)) ) {
+ Add_prerequisite(Targets, tg, FALSE, TRUE);
+
+ tg->ce_flag |= F_TARGET;
+ tg->ce_attr |= A_FRINGE;
+ res = TRUE;
+ }
+
+ return(res);
+}