diff options
author | Jens-Heiner Rechtien <hr@openoffice.org> | 2000-09-22 14:33:37 +0000 |
---|---|---|
committer | Jens-Heiner Rechtien <hr@openoffice.org> | 2000-09-22 14:33:37 +0000 |
commit | b501a54cf959bc549810fb24ca6f9584dc5c9cf3 (patch) | |
tree | b78fbbc5c0f6959529f2553e3bbb83b077eaa17c /dmake/rulparse.c | |
parent | 17e0108b4e8d91f9acfe376ee231418830392460 (diff) |
initial import
Diffstat (limited to 'dmake/rulparse.c')
-rw-r--r-- | dmake/rulparse.c | 1473 |
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); +} |