Dennis Vadura <dvadura@watdragon.waterloo.edu> (05/13/91)
Submitted-by: Dennis Vadura <dvadura@watdragon.waterloo.edu> Posting-number: Volume 19, Issue 50 Archive-name: dmake/part29 Supersedes: dmake-3.6: Volume 15, Issue 52-77 ---- Cut Here and feed the following to sh ---- #!/bin/sh # this is dmake.shar.29 (part 29 of a multipart archive) # do not concatenate these parts, unpack them in order with /bin/sh # file dmake/rulparse.c continued # if test ! -r _shar_seq_.tmp; then echo 'Please unpack part 1 first!' exit 1 fi (read Scheck if test "$Scheck" != 29; then echo Please unpack part "$Scheck" next! exit 1 else exit 0 fi ) < _shar_seq_.tmp || exit 1 if test -f _shar_wnt_.tmp; then sed 's/^X//' << 'SHAR_EOF' >> 'dmake/rulparse.c' && X be an illegal rule definition, and it returns 1 if it is a rule definition. X */ int *state; { X TKSTR input; /* input string struct for token search */ X CELLPTR targets; /* list of targets if any */ X CELLPTR prereq; /* list of prereq if any */ X CELLPTR prereqtail; /* tail of prerequisite list */ X CELLPTR cp; /* temporary cell pointer for list making */ X char *result; /* temporary storage for result */ X char *tok; /* temporary pointer for tokens */ X char *set_dir; /* value of setdir attribute */ X char *brk; /* break char list for Get_token */ X char *firstrcp; /* first recipe line, from ; in rule line */ X t_attr attr; /* sum of attribute flags for current tgts*/ X t_attr at; /* temp place to keep an attribute code */ X int op; /* rule operator */ X int special; /* indicate special targets in rule */ X int percent; /* indicate percent rule target */ X int mixed_glob_prq; /* indicate mixed %-rule prereq possible */ X X DB_ENTER( "Parse_rule_def" ); X X op = 0; X attr = 0; X special = 0; X percent = 0; X set_dir = NIL( char ); X targets = NIL(CELL); X prereq = NIL(CELL); X prereqtail = NIL(CELL); X mixed_glob_prq = 0; X X /* Check to see if the line is of the form: X * targets : prerequisites; first recipe line X * If so remember the first_recipe part of the line. */ X X firstrcp = strchr( Buffer, ';' ); X if( firstrcp != NIL( char ) ) { X *firstrcp++ = 0; X firstrcp = _strspn( firstrcp, " \t" ); X } X X result = Expand( Buffer ); X for( brk=strchr(result,'\\'); brk != NIL(char); brk=strchr(brk,'\\') ) X if( brk[1] == '\n' ) X *brk = ' '; X else X brk++; X X DB_PRINT( "par", ("Scanning: [%s]", result) ); X X SET_TOKEN( &input, result ); X brk = ":-^!"; X Def_targets = TRUE; X X /* Scan the input rule line collecting targets, the operator, and any X * prerequisites. Stop when we run out of targets and prerequisites. */ X X while( *(tok = Get_token( &input, brk, TRUE )) != '\0' ) X if( !op ) { X /* we are scanning targets and attributes X * check to see if token is an operator. */ X X op = Rule_op( tok ); X X if( !op ) { X /* define a new cell, or get old cell */ X cp = Def_cell( tok ); X DB_PRINT( "par", ("tg_cell [%s]", tok) ); X X if( at = _is_attribute( tok ) ) { X /* Logically OR the attributes specified into one main X * ATTRIBUTE mask. */ X X if( at == A_SETDIR ) X if( set_dir != NIL( char ) ) X Warning( "Multiple .SETDIR attribute ignored" ); X else X set_dir = _strdup( tok ); X X attr |= at; X } X else { X int tmp; X X tmp = _is_special( tok ); X if( _is_percent( tok ) ) percent++; X X if( percent ) X if( targets != NIL(CELL) ) X Fatal( "Multiple targets are not allowed in %% rules" ); X else X cp->ce_flag |= F_PERCENT; X X if( special ) X Fatal( "Special target must appear alone", tok ); X else if( !(cp->ce_flag & F_MARK) ) { X cp->ce_link = targets; /* targets are stacked in this list*/ X cp->ce_flag |= F_MARK | F_EXPLICIT; X targets = cp; X X special = tmp; X } X else if( !(cp->ce_attr & A_LIBRARY) ) X Warning("Duplicate entry [%s] in target list",cp->CE_NAME); X } X } X else { X /* found an operator so empty out break list X * and clear mark bits on target list, setting them all to F_USED */ X X brk = ""; X for( cp=targets; cp != NIL(CELL); cp=cp->ce_link ) { X cp->ce_flag ^= F_MARK; X cp->ce_flag |= F_USED; X } X X Def_targets = FALSE; X } X } X else { X /* Scanning prerequisites so build the prerequisite list. We use X * F_MARK flag to make certain we have only a single copy of the X * prerequisite in the list */ X X cp = Def_cell( tok ); X X if( _is_percent( tok ) ) { X if( !percent && !attr ) X Fatal( "Syntax error in %% rule, missing %% target"); X mixed_glob_prq = 1; X } X X if( cp->ce_flag & F_USED ) { X if( cp->ce_attr & A_COMPOSITE ) X continue; X else X Fatal( "Detected circular dependency in graph at [%s]", X cp->CE_NAME ); X } X else if( !(cp->ce_flag & F_MARK) ) { X DB_PRINT( "par", ("pq_cell [%s]", tok) ); X cp->ce_flag |= F_MARK; X X if( prereqtail == NIL(CELL) ) /* keep prereq's in order */ X prereq = cp; X else X prereqtail->ce_link = cp; X X prereqtail = cp; X cp->ce_link = NIL(CELL); X } X else if( !(cp->ce_attr & A_LIBRARY) ) X Warning("Duplicate entry [%s] in prerequisite list",cp->CE_NAME); X } X X /* Check to see if we have a percent rule that has only global X * prerequisites. If so then set the flag so that later on, we don't issue X * an error if such targets supply an empty set of rules. */ X X if( percent && !mixed_glob_prq && (prereq != NIL(CELL)) ) X _sv_globprq_only = 1; X X /* It's ok to have targets with attributes, and no prerequisites, but it's X * not ok to have no targets and no attributes, or no operator */ X X if( !op ) { X CLEAR_TOKEN( &input ); X DB_PRINT( "par", ("Not a rule [%s]", Buffer) ); X DB_RETURN( 0 ); X } X X if( !attr && targets == NIL(CELL) ) { X Fatal( "Missing targets or attributes in rule" ); X if( set_dir != NIL( char )) FREE( set_dir ); X DB_RETURN( 0 ); X } X X /* We have established we have a legal rules line, so we must process it. X * In doing so we must handle any special targets. Special targets must X * appear alone possibly accompanied by attributes. X * NOTE: special != 0 ==> targets != NIL(CELL) */ X X if( prereqtail != NIL(CELL) ) prereqtail->ce_link = NIL(CELL); X X /* Clear out MARK bits used in duplicate checking. I originally wanted X * to do this as the lists get processed but that got too error prone X * so I bit the bullit and added these two loops. */ X X for( cp=prereq; cp != NIL(CELL); cp=cp->ce_link ) cp->ce_flag &= ~F_MARK; X for( cp=targets; cp != NIL(CELL); cp=cp->ce_link ) cp->ce_flag &= ~F_USED; X X /* Check to see if the previous rule line was bound if, not the call X * Bind_rules_to_targets to go and bind the line */ X X if( _sv_rules != NIL(STRING) ) Bind_rules_to_targets( F_DEFAULT ); X X /* Add the first recipe line to the list */ X if( firstrcp != NIL( char ) ) X Add_recipe_to_list( firstrcp, TRUE, FALSE ); X X /* Save these prior to calling _do_targets, since _build_graph needs the X * _sv_setdir value for matching edges. */ X _sv_op = op; X _sv_setdir = set_dir; X X if( special ) X _do_special( special, op, attr, set_dir, targets, prereq, state ); X else X *state = _do_targets( op, attr, set_dir, targets, prereq ); X X DB_RETURN( 1 ); } X X PUBLIC int Rule_op( op )/* ================ X Check the passed in op string and map it to one of the rule operators */ char *op; { X int ret = 0; X X DB_ENTER( "rule_op" ); X X if( *op == TGT_DEP_SEP ) { X ret = R_OP_CL; X op++; X X /* All rule operations begin with a :, but may include any one of the X * four modifiers. In order for the rule to be properly mapped we must X * check for each of the modifiers in turn, building up our return bit X * string. */ X X while( *op && ret ) X switch( *op ) { X case ':': ret |= R_OP_DCL; op++; break; X case '!': ret |= R_OP_BG; op++; break; X case '^': ret |= R_OP_UP; op++; break; X case '-': ret |= R_OP_MI; op++; break; X X default : ret = 0; /* an invalid modifier, chuck whole string */ X } X X if( *op != '\0' ) ret = 0; X } X X DB_RETURN( ret ); } X X PUBLIC void Add_recipe_to_list( rule, white_too, no_check )/* ================================================= X Take the provided string and add it to the list of recipe lines X we are saving to be added to the list of targets we have built X previously. If white_too == TRUE add the rule EVEN IF it contains only X whitespace. */ char *rule; int white_too; int no_check; { X DB_ENTER( "Add_recipe_to_list" ); X X if( rule != NIL( char ) && (*rule != '\0' || white_too) ) { X DB_PRINT( "par", ("Adding recipe [%s]", rule) ); X _sv_crule = Def_recipe( rule, _sv_crule, white_too, no_check ); X X if( _sv_rules == NIL(STRING) ) X _sv_rules = _sv_crule; X } X X DB_VOID_RETURN; } X X PUBLIC void Bind_rules_to_targets( flag )/* =============================== X Take the rules we have defined and bind them with proper attributes X to the targets that were previously defined in the parse. The X attributes that get passed here are merged with those that are were X previously defined. (namely F_SINGLE) */ int flag; { X CELLPTR tg; /* pointer to current target in list */ X LINKPTR lp; /* pointer to link cell */ X int magic; /* TRUE if target is .xxx.yyy form */ X int tflag; /* TRUE if we assigned targets here */ X X DB_ENTER( "Bind_rules_to_targets" ); X X /* This line is needed since Parse may call us twice when the last X * GROUP rule appears at the end of file. In this case the rules X * have already been bound and we want to ignore them. */ X X if( _sv_targets == NIL(CELL) ) { DB_VOID_RETURN; } X X tflag = FALSE; X flag |= (_sv_flag & F_SINGLE); X X for( tg = _sv_targets; tg != NIL(CELL); tg = tg->ce_link ) { X DB_PRINT( "par", ("Binding to %s, %04x", tg->CE_NAME, tg->ce_flag) ); X magic = tg->ce_flag & F_PERCENT; X X /* Check to see if we had a rule of the form '%.o : a.h b.h ; xxx' X * In which case we must build a NULL prq node to hold the recipe */ X if( _sv_globprq_only && (_sv_rules != NIL(STRING)) ) X _build_graph( _sv_op, tg, NIL(CELL) ); X X /* NOTE: For targets that are magic we ignore any previously defined X * rules. ie. We throw away the old definition and use the new. */ X if( !(tg->ce_flag & F_MULTI) && !magic && (tg->CE_RECIPE != NIL(STRING)) X && !_sp_target && (_sv_rules != NIL(STRING)) ) X Fatal( "Multiply defined recipe for target %s", tg->CE_NAME ); X X if( (magic || _sp_target) && (_sv_rules == NIL(STRING)) && X !(tg->ce_flag & F_SPECIAL) && !_sv_globprq_only ) X Warning( "Empty recipe for special target %s", tg->CE_NAME ); X X if( magic ) { X CELLPTR ep; X X for( ep=_sv_edgel; ep != NIL(CELL); ep=ep->ce_link ) { X _set_attributes( _sv_attro, _sv_setdir, ep ); X ep->ce_flag |= (F_TARGET|flag); X X if( _sv_rules != NIL(STRING) ) { X ep->ce_recipe = _sv_rules; X ep->ce_indprq = _sv_glb_prq; X } X } X } X else { X tg->ce_attr |= _sv_attr; X tg->ce_flag |= flag; X X if( _sv_rules != NIL(STRING) ) { X tg->ce_recipe = _sv_rules; X tg->ce_flag |= F_RULES | F_TARGET; X X /* Bind the current set of prerequisites as belonging to the X * original recipe given for the target */ X for( lp=tg->ce_prq; lp != NIL(LINK); lp = lp->cl_next ) X if( !(lp->cl_flag & F_USED) ) lp->cl_flag |= F_TARGET; X } X else for( lp=tg->ce_prq; lp != NIL(LINK); lp = lp->cl_next ) X lp->cl_flag |= F_USED; X } X X if( !Target && !magic && !(tg->ce_flag & F_SPECIAL) ) { X Add_prerequisite( Root, tg, FALSE, TRUE ); X X tg->ce_flag |= F_TARGET; X tg->ce_attr |= A_FRINGE; X tflag = TRUE; X } X } X X if( tflag ) Target = TRUE; X if( _sv_setdir ) FREE(_sv_setdir); X _sv_rules = NIL(STRING); X _sv_crule = NIL(STRING); X _sv_targets = NIL(CELL); X _sv_glb_prq = NIL(LINK); X _sv_edgel = NIL(CELL); X _sp_target = FALSE; X _sv_globprq_only = 0; X X DB_VOID_RETURN; } X X X PUBLIC int Set_group_attributes( list )/* ============================== X Scan list looking for the standard @ and - (as in recipe line defs) X and set the flags accordingly so that they apply when we bind the X rules to the appropriate targets. */ char *list; { X int res = (*_strspn(list,"@-%+ \t") == '['); X if( res ) _sv_attr |= Rcp_attribute(list); X return(res); } X X static void _do_special( special, op, attr, set_dir, target, prereq, state )/* ================================================================== X Process a special target. So far the only special targets we have X are those recognized by the _is_special function. X X target is always only a single special target. X X NOTE: For the cases of .IMPORT, and .INCLUDE, the cells created by the X parser are never freed. This is due to the fact that it is too much X trouble to get them out of the hash table once they are defined, and X if they are per chance used again it will be ok, anyway, since the X cell is not really used by the code below. */ X int special; int op; t_attr attr; char *set_dir; CELLPTR target; CELLPTR prereq; int *state; { X HASHPTR hp; /* pointer to macro def cell */ X CELLPTR cp; /* temporary pointer into cells list */ X CELLPTR dp; /* pointer to directory dir cell */ X LINKPTR lp; /* pointer at prerequisite list */ X char *dir; /* current dir to prepend */ X char *path; /* resulting path to try to read */ X char *name; /* File name for processing a .INCLUDE */ X char *tmp; /* temporary string pointer */ X FILE *fil; /* File descriptor returned by Openfile */ X X DB_ENTER( "_do_special" ); X X target->ce_flag = F_SPECIAL; /* mark the target as special */ X X switch( special ) { X case ST_EXPORT: X for( ; prereq != NIL(CELL); prereq = prereq->ce_link ) { X DB_PRINT( "par", ("Exporting [%s]", prereq->CE_NAME) ); X hp = GET_MACRO( prereq->CE_NAME ); X X if( hp != NIL(HASH) ) { X char *tmpstr = hp->ht_value; X X if( tmpstr == NIL(char) ) tmpstr = ""; X X if( Write_env_string( prereq->CE_NAME, tmpstr ) != 0 ) X Warning( "Could not export %s", prereq->CE_NAME ); X } X } X break; X X case ST_IMPORT: X for( ; prereq != NIL(CELL); prereq = prereq->ce_link ) { X char *tmpstr; X X DB_PRINT( "par", ("Importing [%s]", prereq->CE_NAME) ); X X if( strcmp(prereq->CE_NAME, ".EVERYTHING") == 0 ) { X t_attr sattr = Glob_attr; X Glob_attr |= A_SILENT; X X ReadEnvironment(); X X Glob_attr = sattr; X } X else { X tmpstr = Read_env_string( prereq->CE_NAME ); X X if( tmpstr != NIL(char) ) X Def_macro( prereq->CE_NAME,tmpstr,M_EXPANDED | M_LITERAL); X else X if( !((Glob_attr | attr) & A_IGNORE) ) X Fatal("Imported macro `%s' not found",prereq->CE_NAME); X } X } X X attr &= ~A_IGNORE; X break; X X case ST_INCLUDE: X { X int ignore = (((Glob_attr | attr) & A_IGNORE) != 0); X int pushed = FALSE; X LINKPTR prqlnk = NIL(LINK); X LINKPTR prqlst = NIL(LINK); X X if( prereq == NIL(CELL) ) Fatal( "No .INCLUDE file(s) specified" ); X X dp = Def_cell( ".INCLUDEDIRS" ); X X if( (attr & A_SETDIR) && *(dir = strchr(set_dir, '=')+1) ) X pushed = Push_dir( dir, ".INCLUDE", ignore ); X X for( cp=prereq; cp != NIL(CELL); cp = cp->ce_link ) { X LINKPTR ltmp; X TALLOC(ltmp, 1, LINK); X ltmp->cl_prq = cp; X X if( prqlnk == NIL(LINK) ) X prqlst = ltmp; X else X prqlnk->cl_next = ltmp; X X prqlnk = ltmp; X } X X for( ; prqlst != NIL(LINK); FREE(prqlst), prqlst=prqlnk ) { X prqlnk = prqlst->cl_next; X cp = prqlst->cl_prq; X name = cp->CE_NAME; X X if( *name == '<' ) { X /* We have a file name enclosed in <....> X * so get rid of the <> arround the file name */ X X name++; X if( (tmp = strrchr( name, '>' )) != NIL( char ) ) X *tmp = 0; X X if( If_root_path( name ) ) X fil = Openfile( name, FALSE, FALSE ); X else X fil = NIL(FILE); X } X else X fil = Openfile( name, FALSE, FALSE ); X X if( fil == NIL(FILE) ) { /*if true ==> not found in current dir*/ X /* Now we must scan the list of prerequisites for .INCLUDEDIRS X * looking for the file in each of the specified directories. X * if we don't find it then we issue an error. The error X * message is suppressed if the .IGNORE attribute of attr is X * set. If a file is found we call Parse on the file to X * perform the parse and then continue on from where we left X * off. */ X X for(lp=dp->CE_PRQ; lp && fil == NIL(FILE); lp=lp->cl_next) { X dir = lp->cl_prq->CE_NAME; X if( strchr(dir, '$') ) dir = Expand(dir); X path = Build_path( dir, name ); X X DB_PRINT( "par", ("Trying to include [%s]", path) ); X X fil = Openfile( path, FALSE, FALSE ); X if( dir != lp->cl_prq->CE_NAME ) FREE(dir); X } X } X X if( fil != NIL(FILE) ) X Parse( fil ); X else if( !((Glob_attr | attr) & A_IGNORE) ) X Fatal( "Include file %s, not found", name ); X } X X if( pushed ) Pop_dir(FALSE); X attr &= ~(A_IGNORE|A_SETDIR); X } X break; X X case ST_SOURCE: X /* case ST_SUFFIXES: */ X if( prereq != NIL(CELL) ) X _do_targets( op & (R_OP_CL | R_OP_MI | R_OP_UP), attr, set_dir, X target, prereq ); X else { X /* The old semantics of .SOURCE were that an empty list of X * prerequisites clears the .SOURCE list. So we must implement X * that here as a clearout prerequisite operation. Since this is X * a standard operation with the :- opcode we can simply call the X * proper routine with the target cell and it should do the trick X */ X X if( op == R_OP_CL || (op & R_OP_MI) ) X Clear_prerequisites( target ); X } X X op &= ~(R_OP_MI | R_OP_UP); X break; X X case ST_KEEP: X if( Keep_state != NIL(char) ) break; X Def_macro( ".KEEP_STATE", "_state.mk", M_EXPANDED ); X break; X X case ST_REST: X /* The rest of the special targets can all take rules, as such they X * must be able to affect the state of the parser. */ X X { X int s_targ = Target; X X Target = TRUE; X _sp_target = TRUE; X *state = _do_targets( op, attr, set_dir, target, prereq ); X Target = s_targ; X X target->ce_flag |= F_TARGET; X X attr = A_DEFAULT; X op = R_OP_CL; X } X break; X X default:break; X } X X if( op != R_OP_CL ) Warning( "Modifier(s) for operator ignored" ); X if( attr != A_DEFAULT ) Warning( "Extra attributes ignored" ); X X DB_VOID_RETURN; } X X X static int _do_targets( op, attr, set_dir, targets, prereq )/* ================================================= */ int op; t_attr attr; char *set_dir; CELLPTR targets; CELLPTR prereq; { X CELLPTR tg1; /* temporary target pointer */ X CELLPTR tp1; /* temporary prerequisite pointer */ X char *p; /* temporary char pointer */ X CELLPTR prev_cell; /* pointer for .UPDATEALL processing */ X CELLPTR first_cell; /* pointer for .UPDATEALL processing */ X int update; /* A_UPDATEALL attribute flag */ X int smagic = 0; /* collective amount of magic :-) */ X X DB_ENTER( "_do_targets" ); X X if( update = ((attr & A_UPDATEALL) != 0) ) X if( targets == NIL(CELL) ) X Fatal( ".UPDATEALL attribute requires non-empty list of targets" ); X X first_cell = prev_cell = NIL(CELL); X for( tg1 = targets; tg1 != NIL(CELL); tg1 = tg1->ce_link ) { X /* Check each target. Check for inconsistencies between :: and : rule X * sets. :: may follow either : or :: but not the reverse. X * X * Any targets that contain :: rules are represented by a prerequisite X * list hanging off the main target cell where each of the prerequisites X * is a copy of the target cell but is not entered into the hash table. X */ X int magic = (tg1->ce_flag & F_PERCENT) && !(tg1->ce_flag & F_MAGIC); X smagic |= magic; X X if( !(op & R_OP_DCL ) && (tg1->ce_flag & F_MULTI) && !magic ) X Fatal( "Inconsistency in inference rules for %s", tg1->CE_NAME ); X X if( magic ) X do { X _build_graph( op, tg1, prereq ); X if( prereq != NIL(CELL) ) prereq = prereq->ce_link; X } while( prereq != NIL(CELL) ); X else if( !(tg1->ce_flag & F_SPECIAL) && X (prereq == NIL(CELL)) && X (p = _is_magic( tg1->CE_NAME )) != NIL(char)) X smagic |= _do_magic( op, p, tg1, prereq, attr, set_dir ); X else if( op & R_OP_DCL ) { X CELLPTR tmp_cell = _make_multi(tg1); X targets = _replace_cell( targets, tg1, tmp_cell ); X tg1 = tmp_cell; X } X X if( !magic ) _set_attributes( attr, set_dir, tg1 ); X X if( update ) { X if( smagic ) Fatal( ".UPDATEALL attribute not legal in meta rule" ); X X /* Check this as it would break another circular .UPATEALL list if X * we blindly assign it and it is part of another list already. */ X if( tg1->ce_all != NIL(CELL) ) X Fatal( "Target [%s] appears on multiple .UPDATEALL lists" ); X X tg1->ce_all = prev_cell; X if( prev_cell == NIL(CELL) ) first_cell = tg1; X prev_cell = tg1; X } X X /* Build the proper prerequisite list of the target. If the `-', X * modifier was used clear the prerequisite list before adding any X * new prerequisites. Else add them to the head/tail as appropriate. X * X * If the target has F_PERCENT set then no prerequisites are used. */ X X if( !(tg1->ce_flag & F_PERCENT) ) { X if( op & R_OP_MI ) Clear_prerequisites( tg1 ); X X if( (op & R_OP_UP) && (tg1->ce_prq != NIL(LINK)) ) X _stick_at_head( tg1, prereq ); X else for( tp1=prereq; tp1 != NIL(CELL); tp1 = tp1->ce_link ) X Add_prerequisite( tg1, tp1, FALSE, FALSE ); X } X else if( op & (R_OP_MI | R_OP_UP) ) X Warning( "Modifier(s) `^!' for %-meta target ignored" ); X } X X if( first_cell != NIL(CELL) ) first_cell->ce_all = prev_cell; X X X /* Check to see if we have NO targets but some attributes. IF so then X * apply all of the attributes to the complete list of prerequisites. X * Cannot happen for F_PERCENT targets. (ie. in that case targets is always X * not NIL) */ X X if( (targets == NIL(CELL)) && attr ) X if( prereq != NIL(CELL) ) X for( tp1=prereq; tp1 != NIL(CELL); tp1 = tp1->ce_link ) X _set_attributes( attr, set_dir, tp1 ); X else X _set_global_attr( attr ); X X /* Fix up the ce_link pointers so that when we go to attach a recipe in X * Bind_targets to rules we get the right thing if it's an .UPDATEALL :: X * recipe */ X if( update ) { X for( tp1=NIL(CELL),tg1=prev_cell; tg1!=first_cell; tg1=tg1->ce_all ) { X tg1->ce_link = tp1; X tp1 = tg1; X } X tg1->ce_link = tp1; X targets = first_cell; X } X X /* Now that we have built the lists of targets, the parser must parse the X * rules if there are any. However we must start the rule list with the X * rule specified as via the ; kludge, if there is one */ X _sv_targets = targets; X _sv_attr = _sv_attro = attr; X _sv_flag = ((op & R_OP_BG) ? F_SINGLE : F_DEFAULT); X X DB_RETURN( RULE_SCAN ); } X X static int _do_magic( op, dot, target, prereq, attr, set_dir )/* ===================================================== X This function takes a magic target of the form .<chars>.<chars> or X .<chars> and builds the appropriate % rules for that target. X X The function builds the % rule, `%.o : %.c' from .c.o, and X `%.a :' from .a */ X int op; char *dot; CELLPTR target; CELLPTR prereq; t_attr attr; char *set_dir; { X CELLPTR tg; X CELLPTR prq; X char *tmp, *tmp2; X X DB_ENTER( "_do_magic" ); X X if( prereq != NIL(CELL) ) X Warning( "Ignoring prerequisites of old style meta-target" ); X X if( dot == target->CE_NAME ) { /* its of the form .a */ X tg = Def_cell( "%" ); /* ==> no prerequisite */ X tmp = _build_meta( target->CE_NAME ); X prq = Def_cell( tmp ); X FREE( tmp ); X X _build_graph( op, tg, prq ); X } X else { X tmp = _build_meta( dot ); X tg = Def_cell( tmp ); X FREE( tmp ); X X tmp = _build_meta( tmp2 = _substr( target->CE_NAME, dot ) ); X prq = Def_cell( tmp ); X FREE( tmp ); X FREE( tmp2 ); X X _build_graph( op, tg, prq ); X } X X tg->ce_flag |= F_PERCENT; X target->ce_flag |= (F_MAGIC|F_PERCENT); X X _set_attributes( attr, set_dir, tg ); X X DB_RETURN(1); } X X static CELLPTR _replace_cell( lst, cell, rep ) CELLPTR lst; CELLPTR cell; CELLPTR rep; { X register CELLPTR tp; X X if( lst == cell ) { X rep->ce_link = lst->ce_link; X lst = rep; X } X else { X for( tp=lst; tp->ce_link != cell; tp=tp->ce_link ); X rep->ce_link = tp->ce_link->ce_link; X tp->ce_link = rep; X } X X return(lst); } X X static char * _build_meta( name )/* ===================== X Check to see if the name is of the form .c~ if so and if Augmake X translation is enabled then return s.%.c, else return %.suff, where if the X suffix ends in '~' then leave it be.*/ char *name; { X char *tmp; X int test = Augmake ? name[strlen(name)-1] == '~' : 0; X X tmp = _strjoin( test ? "s.%" : "%", name, -1, FALSE); X if( test ) tmp[ strlen(tmp)-1 ] = '\0'; X X return(tmp); } X X X static void _build_graph( op, target, prereq )/* ==================================== X This function is called to build the graph for the % rule given by X target : prereq cell combination. This function assumes that target X is a % target and that prereq is a single % prerequisite. R_OP_CL X rules replace existing rules if any, only R_OP_CL works for meta-rules. X %.o :: %.c is meaningless. X X It also assumes that target cell has F_PERCENT set already. */ int op; CELLPTR target; CELLPTR prereq; { X LINKPTR edl; X CELLPTR edge; X int match; X X DB_ENTER( "_build_graph" ); X DB_PRINT( "%", ("Building graph for [%s : %s]", target->CE_NAME, X (prereq == NIL(CELL)) ? "" : prereq->CE_NAME) ); X X if( prereq != NIL(CELL) ) { X char *name = prereq->CE_NAME; X int len = strlen(name); X X if( *name == '\'' && name[len-1]=='\'' ){ X _add_global_prereq( prereq ); X name[len-1] = '\0'; X strcpy(name, name+1); X DB_VOID_RETURN; X } X } X X /* Search the list of prerequisites for the current target and see if X * any of them match the current %-meta : prereq pair. NOTE that %-metas X * are built as if they were F_MULTI targets. */ X X match = FALSE; X for( edl=target->ce_prq; edl != NIL(LINK); edl=edl->cl_next ) { X edge = edl->cl_prq; X X DB_PRINT( "%", ("Trying to match [%s]", edge?edge->CE_NAME:"(nil)") ); X X if( (!edge->ce_prq && !prereq) X || ( edge->ce_prq->cl_prq == prereq X && ( edge->ce_dir == _sv_setdir X || ( edge->ce_dir X && _sv_setdir X && !strcmp(edge->ce_dir,strchr(_sv_setdir,'=')+1) X ) X ) X ) X ) { X match = TRUE; X break; X } X } X X if( match ) { X /* match is TRUE hence, we found an edge joining the target and the X * prerequisite so reset the new edge's how values to reflect the new X * recipe etc. */ X DB_PRINT( "%", ("It's an old edge") ); X X edge->ce_dir = NIL(char); X edge->ce_flag &= (F_PERCENT|F_MAGIC|F_DFA); X edge->ce_attr &= A_NOINFER; X } X else { X DB_PRINT( "%", ("Adding a new edge") ); X X if( !(target->ce_flag & F_DFA) ) { X Add_nfa( target->CE_NAME ); X target->ce_flag |= F_DFA; X } X edge = _make_multi(target); X if( prereq ) Add_prerequisite(edge, prereq, FALSE, TRUE); X } X X if( op & R_OP_DCL ) X Warning( "'::' operator for meta-target '%s' ignored, ':' operator assumed.", X target->CE_NAME ); X X edge->ce_link = _sv_edgel; X _sv_edgel = edge; X _sv_globprq_only = 0; X X DB_VOID_RETURN; } X X static CELLPTR _make_multi( tg ) CELLPTR tg; { X CELLPTR cp; X X /* This first handle the case when a : foo ; exists prior to seeing X * a :: fee; */ X if( !(tg->ce_flag & F_MULTI) && (tg->ce_prq || tg->ce_recipe) ) { X TALLOC(cp, 1, CELL); X *cp = *tg; X X tg->ce_prq = NIL(LINK); X tg->ce_flag |= F_RULES|F_MULTI|F_TARGET; X tg->ce_attr |= A_SEQ; X tg->ce_recipe = NIL(STRING); X tg->ce_dir = NIL(char); X cp->ce_count = ++tg->ce_index; X X Add_prerequisite(tg, cp, FALSE, TRUE); X } X X TALLOC(cp, 1, CELL); X *cp = *tg; X X if( !(tg->ce_flag & F_MULTI) ) { X tg->ce_prq = NIL(LINK); X tg->ce_flag |= F_RULES|F_MULTI|F_TARGET; X tg->ce_attr |= A_SEQ; X tg->ce_recipe = NIL(STRING); X tg->ce_dir = NIL(char); X } X else { X cp->ce_flag &= ~(F_RULES|F_MULTI); X cp->ce_attr &= ~A_SEQ; X cp->ce_prq = NIL(LINK); X cp->ce_index = 0; X } X cp->ce_count = ++tg->ce_index; X cp->ce_flag |= F_TARGET; X X Add_prerequisite(tg, cp, FALSE, TRUE); X return(cp); } X X static void _add_global_prereq( pq )/* ========================== X Prerequisite is a non-% prerequisite for a %-rule target, add it to X the target's list of global prerequsites to add on match */ CELLPTR pq; { X register LINKPTR ln; X X TALLOC( ln, 1, LINK ); X ln->cl_next = _sv_glb_prq; X ln->cl_prq = pq; X _sv_glb_prq = ln; } X X X static void _set_attributes( attr, set_dir, cp )/* ====================================== X Set the appropriate attributes for a cell */ t_attr attr; char *set_dir; CELLPTR cp; { X char *dir; X X DB_ENTER( "_set_attributes" ); X X /* If .SETDIR attribute is set then we have at least .SETDIR= in the X * set_dir string. So go and fishout what is at the end of the =. X * If not set and not NULL then propagate it to the target cell. */ X X if( attr & A_SETDIR ) { X dir = strchr( set_dir, '=' ) + 1; X X if( cp->ce_dir ) X Warning( "Multiple .SETDIR for %s ignored", cp->CE_NAME ); X else X if( *dir ) cp->ce_dir = _strdup(dir); X } X cp->ce_attr |= attr; /* set rest of attributes for target */ X X DB_VOID_RETURN; } X X X static void _set_global_attr( attr )/* ========================== X Handle the setting of the global attribute functions based on X The attribute flags set in attr. */ t_attr attr; { X int flag; X X /* Some compilers can't handle a switch on a long, and t_attr is now a long X * integer on some systems. foey! */ X for( flag = MAX_ATTR; flag; flag >>= 1 ) X if( flag & attr ) X if( flag == A_PRECIOUS) Def_macro(".PRECIOUS", "y", M_EXPANDED); X else if( flag == A_SILENT) Def_macro(".SILENT", "y", M_EXPANDED); X else if( flag == A_IGNORE ) Def_macro(".IGNORE", "y", M_EXPANDED); X else if( flag == A_EPILOG ) Def_macro(".EPILOG", "y", M_EXPANDED); X else if( flag == A_PROLOG ) Def_macro(".PROLOG", "y", M_EXPANDED); X else if( flag == A_NOINFER ) Def_macro(".NOINFER", "y", M_EXPANDED); X else if( flag == A_SEQ ) Def_macro(".SEQUENTIAL","y", M_EXPANDED); X else if( flag == A_SHELL ) Def_macro(".USESHELL", "y", M_EXPANDED); X else if( flag == A_MKSARGS ) Def_macro(".MKSARGS", "y", M_EXPANDED); X else if( flag == A_SWAP ) Def_macro(".SWAP", "y", M_EXPANDED); X X attr &= ~A_GLOB; X if( attr ) Warning( "Non global attribute(s) ignored" ); } X X X static void _stick_at_head( cp, pq )/* ========================== X Add the prerequisite list to the head of the existing prerequisite X list */ X CELLPTR cp; /* cell for target node */ CELLPTR pq; /* list of prerequisites to add */ { X DB_ENTER( "_stick_at_head" ); X X if( pq->ce_link != NIL(CELL) ) _stick_at_head( cp, pq->ce_link ); X Add_prerequisite( cp, pq, TRUE, FALSE ); X X DB_VOID_RETURN; } X X X static t_attr _is_attribute( name )/* ======================= X Check the passed name against the list of valid attributes and return the X attribute index if it is, else return 0, indicating the name is not a valid X attribute. The present attributes are defined in dmake.h as A_xxx #defines, X with the corresponding makefile specification: (note they must be named X exactly as defined below) X X Valid attributes are: .IGNORE, .SETDIR=, .SILENT, .PRECIOUS, .LIBRARY, X .EPILOG, .PROLOG, .LIBRARYM, .SYMBOL, .UPDATEALL, X .USESHELL, .NOINFER, .PHONY, .SWAP, .SEQUENTIAL X .NOSTATE, .MKSARGS X X NOTE: The strcmp's are OK since at most three are ever executed for any X one attribute check, and that happens only when we can be fairly X certain we have an attribute. */ char *name; { X t_attr attr = 0; X X DB_ENTER( "_is_attribute" ); X X if( *name++ == '.' ) X switch( *name ) X { X case 'E': attr = (strcmp(name, "EPILOG")) ? 0 : A_EPILOG; break; X case 'I': attr = (strcmp(name, "IGNORE")) ? 0 : A_IGNORE; break; X case 'L': attr = (strcmp(name, "LIBRARY")) ? 0 : A_LIBRARY; break; X case 'M': attr = (strcmp(name, "MKSARGS")) ? 0 : A_MKSARGS; break; X X case 'N': X if( !strcmp(name, "NOINFER") ) attr = A_NOINFER; X else if( !strcmp(name, "NOSTATE")) attr = A_NOSTATE; X else attr = 0; X break; X X case 'U': X if( !strcmp(name, "UPDATEALL") ) attr = A_UPDATEALL; X else if( !strcmp(name, "USESHELL")) attr = A_SHELL; X else attr = 0; X break; X X case 'P': X if( !strcmp(name, "PRECIOUS") ) attr = A_PRECIOUS; X else if( !strcmp(name, "PROLOG") ) attr = A_PROLOG; X else if( !strcmp(name, "PHONY") ) attr = A_PHONY; X else attr = 0; X break; X X case 'S': X if( !strncmp(name, "SETDIR=", 7) ) attr = A_SETDIR; X else if( !strcmp(name, "SILENT") ) attr = A_SILENT; X else if( !strcmp(name, "SYMBOL") ) attr = A_SYMBOL; X else if( !strcmp(name, "SEQUENTIAL")) attr = A_SEQ; X else if( !strcmp(name, "SWAP")) attr = A_SWAP; X else attr = 0; X break; X } X X DB_RETURN( attr ); } X X X static int _is_special( tg )/* =================== X This function returns TRUE if the name passed in represents a special X target, otherwise it returns false. A special target is one that has X a special meaning to dmake, and may require processing at the time that X it is parsed. X X Current Special targets are: X .GROUPPROLOG .GROUPEPILOG .INCLUDE .IMPORT X .EXPORT .SOURCE .SUFFIXES .ERROR X .INCLUDEDIRS .MAKEFILES .REMOVE .KEEP_STATE */ char *tg; { X DB_ENTER( "_is_special" ); X X if( *tg++ != '.' ) DB_RETURN( 0 ); X X switch( *tg ) X { X case 'I': X if( !strcmp( tg, "IMPORT" ) ) DB_RETURN( ST_IMPORT ); X else if( !strcmp( tg, "INCLUDE" ) ) DB_RETURN( ST_INCLUDE ); X else if( !strcmp( tg, "INCLUDEDIRS" )) DB_RETURN( ST_REST ); X break; X X case 'M': X if( !strcmp( tg, "MAKEFILES" ) ) DB_RETURN( ST_REST ); X break; X X case 'E': X if( !strcmp( tg, "ERROR" ) ) DB_RETURN( ST_REST ); X else if( !strcmp( tg, "EXPORT" ) ) DB_RETURN( ST_EXPORT ); X break; X X case 'G': X if( !strcmp( tg, "GROUPPROLOG" )) DB_RETURN( ST_REST ); X else if( !strcmp( tg, "GROUPEPILOG" )) DB_RETURN( ST_REST ); X break; X X case 'K': X if( !strcmp( tg, "KEEP_STATE" ) ) DB_RETURN( ST_KEEP ); X break; X X case 'R': X if( !strcmp( tg, "REMOVE" ) ) DB_RETURN( ST_REST ); X break; X X case 'S': X if( !strncmp( tg, "SOURCE", 6 ) ) DB_RETURN( ST_SOURCE ); X else if( !strncmp(tg, "SUFFIXES", 8 )) DB_RETURN( ST_SOURCE ); X break; X } X X DB_RETURN( 0 ); } X X X static int _is_percent( np )/* =================== X return TRUE if np points at a string containing a % sign */ char *np; { X return( (strchr(np,'%') && (*np != '\'' && np[strlen(np)-1] != '\'')) ? X TRUE : FALSE ); } X X static char * _is_magic( np )/* ================= X return TRUE if np points at a string of the form X .<chars>.<chars> or .<chars> X where chars are only alpha characters. X X NOTE: reject target if it begins with ./ or ../ */ char *np; { X register char *n; X X n = np; X if( *n != '.' ) return( NIL(char) ); X if (strchr(DirBrkStr, *(n+1))!=NULL || *(n+1) == '.' ) X return (NIL(char)); X X for( n++; isgraph(*n) && (*n != '.'); n++ ); X X if( *n != '\0' ) { X if( *n != '.' ) return( NIL(char) ); X for( np = n++; isgraph( *n ) && (*n != '.'); n++ ); X if( *n != '\0' ) return( NIL(char) ); X } X else if( !Augmake ) X return( NIL(char) ); X X /* np points at the second . of .<chars>.<chars> string. X * if the special target is of the form .<chars> then np points at the X * first . in the token. */ X X return( np ); } X SHAR_EOF chmod 0640 dmake/rulparse.c || echo 'restore of dmake/rulparse.c failed' Wc_c="`wc -c < 'dmake/rulparse.c'`" test 39277 -eq "$Wc_c" || echo 'dmake/rulparse.c: original size 39277, current size' "$Wc_c" rm -f _shar_wnt_.tmp fi # ============= dmake/stat.c ============== if test -f 'dmake/stat.c' -a X"$1" != X"-c"; then echo 'x - skipping dmake/stat.c (File already exists)' rm -f _shar_wnt_.tmp else > _shar_wnt_.tmp sed 's/^X//' << 'SHAR_EOF' > 'dmake/stat.c' && /* RCS -- $Header: /u2/dvadura/src/generic/dmake/src/RCS/stat.c,v 1.1 91/05/06 15:23:29 dvadura Exp $ -- SYNOPSIS -- bind a target name to a file. -- -- DESCRIPTION -- This file contains the code to go and stat a target. The stat rules -- follow a predefined order defined in the comment for Stat_target. -- -- 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: stat.c,v $ X * Revision 1.1 91/05/06 15:23:29 dvadura X * dmake Release Version 3.7 X * */ X #include "extern.h" X X static int _check_dir_list ANSI((CELLPTR, CELLPTR, int)); X #ifdef DBUG X /* Just a little ditty for debugging this thing */ X static time_t X _do_stat( name, lib, sym ) X char *name; X char *lib; X char **sym; X { X time_t res; X DB_ENTER( "_do_stat" ); X X res = Do_stat(name, lib, sym); X DB_PRINT( "stat", ("Statted [%s,%s,%d,%ld]", name, lib, sym, res) ); X X DB_RETURN( res ); X } #define DO_STAT(A,B,C) _do_stat(A,B,C) #else #define DO_STAT(A,B,C) Do_stat(A,B,C) #endif X static char *_first; /* local storage of first attempted path */ SHAR_EOF true || echo 'restore of dmake/stat.c failed' fi echo 'End of part 29, continue with part 30' echo 30 > _shar_seq_.tmp exit 0 exit 0 # Just in case... -- Kent Landfield INTERNET: kent@sparky.IMD.Sterling.COM Sterling Software, IMD UUCP: uunet!sparky!kent Phone: (402) 291-8300 FAX: (402) 291-4362 Please send comp.sources.misc-related mail to kent@uunet.uu.net.