[comp.sources.misc] v14i015: dmake version 3.5 part 5/21

dvadura@watdragon.waterloo.edu (Dennis Vadura) (07/27/90)

Posting-number: Volume 14, Issue 15
Submitted-by: dvadura@watdragon.waterloo.edu (Dennis Vadura)
Archive-name: dmake/part05

#!/bin/sh
# this is part 5 of a multipart archive
# do not concatenate these parts, unpack them in order with /bin/sh
# file rulparse.c continued
#
CurArch=5
if test ! -r s2_seq_.tmp
then echo "Please unpack part 1 first!"
     exit 1; fi
( read Scheck
  if test "$Scheck" != $CurArch
  then echo "Please unpack part $Scheck next!"
       exit 1;
  else exit 0; fi
) < s2_seq_.tmp || exit 1
echo "x - Continuing file rulparse.c"
sed 's/^X//' << 'SHAR_EOF' >> rulparse.c
X	 for( cp=prereq; cp != NIL(CELL); cp = cp->ce_link ) {
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 );
X	       else
X		  fil = NIL(FILE);
X	    }
X	    else
X	       fil = Openfile( name, 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	       if( (dp->CE_HOW != NIL(HOW)) &&
X		   ((lp = dp->CE_HOW->hw_prq) != NIL(LINK)) )
X		  for(; lp != NIL(LINK) && 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 );
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->CE_HOW );
X	 }
X
X	 op &= ~(R_OP_MI | R_OP_UP);
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	    set_dir = NIL( char );
X	    attr    = A_DEFAULT;
X	    op      = R_OP_CL;
X	 }
X	 break;
X
X      default:break;
X   }
X      
X   if( set_dir != NIL(char) ) FREE( set_dir );
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
X
Xstatic int
X_do_targets( op, attr, set_dir, targets, prereq )/*
X================================================= */
Xint	op;
Xint	attr;
Xchar	*set_dir;
XCELLPTR targets;
XCELLPTR prereq;
X{
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   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) )
X      if( targets == NIL(CELL) )
X	    Fatal( ".UPDATEALL attribute requires non-empty list of targets" );
X
X   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.  We allocate
X       * a HOW cell for each target that we see, if it already does not have
X       * one.  If it has a HOW cell then we use it, unless the current
X       * operator is ::, in which case we must allocate a new one. */
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		(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	 HOWPTR hp;
X
X	 TALLOC( hp, 1, HOW );
X
X	 hp->hw_next   = tg1->CE_HOW;
X	 tg1->CE_HOW   = hp;
X	 tg1->ce_flag |= F_MULTI;
X      }
X      else if( tg1->CE_HOW == NIL(HOW) )
X	 TALLOC( tg1->CE_HOW, 1, HOW );
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 cirlcular .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	 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( tg1 == targets || !update ) {
X	    register HOWPTR  how  = tg1->CE_HOW;
X
X	    if( op & R_OP_MI ) Clear_prerequisites( how );
X
X	    if( (op & R_OP_UP) && (how->hw_prq != NIL(LINK)) )
X	       _stick_at_head( how, prereq );
X	    else
X	       for( tp1=prereq; tp1 != NIL(CELL); tp1 = tp1->ce_link )
X		  Add_prerequisite( how, tp1, FALSE );
X	 }
X      else
X         if( op & (R_OP_MI | R_OP_UP) )
X	    Warning( "Modifier(s) `^!' for ':' operator ignored" );
X   }
X
X   if( targets != NIL(CELL) ) targets->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	    if( tp1->CE_HOW == NIL(HOW) ) TALLOC( tp1->CE_HOW, 1, HOW );
X	    _set_attributes( attr, set_dir, tp1 );
X	 }
X      else
X	 _set_global_attr( attr, set_dir );
X
X   /* Fix up the HOW pointers for the A_UPDATEALL case, they should all point
X    * to the same cell (targets->CE_HOW), if the .UPDATEALL attribute is given
X    */
X   if( update && targets != NIL(CELL) )
X      for( tg1=targets->ce_link; tg1 != NIL(CELL); tg1 = tg1->ce_link ) {
X	 FREE(tg1->CE_HOW);
X	 tg1->CE_HOW = targets->CE_HOW;
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
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
X
Xstatic int
X_do_magic( op, dot, target, prereq, attr, set_dir )/*
X=====================================================
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
Xint	op;
Xchar    *dot;
XCELLPTR target;
XCELLPTR prereq;
Xint     attr;
Xchar    *set_dir;
X{
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   op &= (R_OP_CL | R_OP_DCL);
X
X   if( dot == target->CE_NAME )	{		/* its of the form .a	*/
X      tg  = Def_cell( "%", NIL(CELL) );		/* ==> no prerequisite  */
X      tmp = _build_meta( target->CE_NAME );
X      prq = Def_cell( tmp, NIL(CELL) );
X      FREE( tmp );
X
X      _build_graph( op, tg, prq );
X   }
X   else {
X      tmp = _build_meta( dot );
X      tg  = Def_cell( tmp, NIL(CELL) );
X      FREE( tmp );
X
X      tmp = _build_meta( tmp2 = _substr( target->CE_NAME, dot ) );
X      prq = Def_cell( tmp, NIL(CELL) );
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  |= (tg->ce_flag & (F_MULTI | F_PERCENT)) | F_MAGIC;
X   target->CE_EDGES  = tg->CE_EDGES;
X
X   _set_attributes( attr, set_dir, tg );
X
X   DB_RETURN(1);
X}
X
X
X
Xstatic char *
X_build_meta( name )/*
X=====================
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.*/
Xchar *name;
X{
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
X
Xstatic void
X_build_graph( op, target, prereq )/*
X====================================
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.  op can be
X   either R_OP_CL or R_OP_DCL, all other operations are ignored.
X   
X   It also assumes that target cell has F_PERCENT set already. */
Xint op;
XCELLPTR target;
XCELLPTR prereq;
X{
X   int      match;
X   EDGEPTR  edge;
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   /* The list of edges is kept as a circular list.  Thus we must find the
X    * last edge if we are to add a new edge.  Also we must check the list to
X    * find out if a new edge needs to be added. */
X
X   match = FALSE;
X   if( (edge = target->CE_EDGES) != NIL(EDGE) ) {
X      EDGEPTR start;
X
X      start = edge;
X      do {
X	 DB_PRINT( "%", ("Trying to match [%s]", edge->ed_prq->CE_NAME) );
X
X         if( edge->ed_prq == prereq )
X	    match = TRUE;
X	 else
X	    edge  = edge->ed_next;
X      }
X      while ( !match && start != edge );
X   }
X
X   if( match ) {
X      /* match is TRUE hence, we found an edge joining the target and the
X       * prerequisite so set the current target's CE_EDGES pointer to point
X       * at the edge we found and make sure the new edge has a valid HOW
X       * pointer. */
X
X      DB_PRINT( "%", ("It's an old edge") );
X
X      target->CE_EDGES = edge;
X
X      if( op & R_OP_DCL ) {
X         HOWPTR hp;
X
X	 TALLOC( hp, 1, HOW );
X
X	 hp->hw_next   = edge->ed_how;
X	 edge->ed_how  = hp;
X      }
X      else {
X	 HOWPTR hp = edge->ed_how;
X
X	 hp->hw_flag = F_DEFAULT;
X	 hp->hw_attr = A_DEFAULT;
X	 target->ce_dir    = NIL(char);
X	 target->ce_flag  &= (F_PERCENT|F_MAGIC);
X	 target->ce_attr  &= A_NOINFER;
X      }
X	
X   }
X   else {
X      EDGEPTR tedge;
X
X      TALLOC( tedge, 1, EDGE );
X
X      if( edge == NIL(EDGE) ) {
X	 DB_PRINT( "%", ("It's a new edge") );
X	 edge = tedge;
X	 target->CE_EDGES = edge->ed_next = edge;
X      }
X      else {
X	 DB_PRINT( "%", ("It's a new edge (non-empty edge list)") );
X	 tedge->ed_next   = edge->ed_next;
X	 edge->ed_next    = tedge;
X	 target->CE_EDGES = edge = tedge;
X      }
X
X      /* This was a new edge so we must point it's prerequisite pointer at the
X       * prerequisite, and add the first HOW cell.
X       * Since this is also the first time we have seen the % target we
X       * add it to the NFA we are building of % rule targets. */
X
X      TALLOC( edge->ed_how, 1, HOW );
X
X      edge->ed_prq = prereq;
X      edge->ed_tg  = target;
X
X      if( !(target->ce_flag & F_DFA) ) {
X	 Add_dfa( target->CE_NAME );
X	 target->ce_flag |= F_DFA;
X      }
X
X      if( op & R_OP_DCL ) target->ce_flag |= F_MULTI;
X   }
X
X   edge->ed_link = _sv_edgel;
X   _sv_edgel = edge;
X   _sv_globprq_only = 0;
X
X   DB_VOID_RETURN;
X}
X
X
X
Xstatic void
X_add_global_prereq( pq )/*
X==========================
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 */
XCELLPTR pq;
X{
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
X
Xstatic void
X_set_attributes( attr, set_dir, cell )/*
X=============================================
X	Set the appropriate attributes for a cell */
Xint	attr;
Xchar	*set_dir;
XCELLPTR cell;
X{
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( cell->ce_attr & A_SETDIR )
X	 Warning( "Multiple .SETDIR for %s ignored", cell->CE_NAME );
X      else
X	 if( *dir ) cell->ce_dir = dir;
X   }
X   cell->ce_attr |= attr;		/* set rest of attributes for target */
X
X   DB_VOID_RETURN;
X}
X
X
X
Xstatic void
X_set_global_attr( attr, dir )/*
X===============================
X	Handle the setting of the global attribute functions based on
X	The attribute flags set in attr.  Note we set the dir path name
X	to be the value of Start_dir.  If Start_dir is initially set
X	Make will CD to that directory before making any targets. */
Xint  attr;
Xchar *dir;
X{
X   int flag;
X
X   for( flag = MAX_ATTR; flag; flag >>= 1 )
X      switch( flag & attr )
X      {
X	 case A_PRECIOUS: Def_macro(".PRECIOUS",  "y", M_EXPANDED); break;
X	 case A_SILENT:   Def_macro(".SILENT",    "y", M_EXPANDED); break;
X	 case A_IGNORE:   Def_macro(".IGNORE",    "y", M_EXPANDED); break;
X	 case A_EPILOG:   Def_macro(".EPILOG",    "y", M_EXPANDED); break;
X	 case A_PROLOG:   Def_macro(".PROLOG",    "y", M_EXPANDED); break;
X	 case A_NOINFER:  Def_macro(".NOINFER",   "y", M_EXPANDED); break;
X	 case A_SEQ:	  Def_macro(".SEQUENTIAL","y",M_EXPANDED); break;
X
X	 case A_SETDIR:
X	    dir = strchr( dir, '=' ) + 1;
X	    if( *dir ) Start_dir.ce_dir = dir;
X	    break;
X      }
X   
X   attr &= ~(A_PRECIOUS | A_SETDIR | A_SILENT | A_IGNORE |
X             A_EPILOG | A_PROLOG | A_NOINFER | A_SEQ);
X
X   if( attr )
X      Warning( "Non global attribute(s) ignored" );
X}
X
X
X
Xstatic void
X_stick_at_head( how, pq )/*
X===========================
X	Add the prerequisite list to the head of the existing prerequisite
X	list */
X
XHOWPTR  how;		/* HOW cell for target node	*/
XCELLPTR pq;		/* list of prerequisites to add */
X{
X   DB_ENTER( "_stick_at_head" );
X
X   if( pq->ce_link != NIL(CELL) ) _stick_at_head( how, pq->ce_link );
X   Add_prerequisite( how, pq, TRUE );
X
X   DB_VOID_RETURN;
X}
X
X
X
Xstatic int
X_is_attribute( name )/*
X=======================
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			  .NOINFER
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.  */
Xchar *name;
X{
X   int 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 'N': attr = (strcmp(name, "NOINFER"))  ? 0 : A_NOINFER; break;
X         case 'U': attr = (strcmp(name, "UPDATEALL"))? 0 : A_UPDATEALL;break;
X
X         case 'P':
X            if( !strcmp(name, "PRECIOUS") )     attr = A_PRECIOUS;
X            else if( !strcmp(name, "PROLOG") )  attr = A_PROLOG;
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 attr = 0;
X            break;
X      }
X
X   DB_RETURN( attr );
X}
X
X
X
Xstatic int
X_is_special( tg )/*
X===================
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
X*/
Xchar *tg;
X{
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 '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
X
Xstatic int
X_is_percent( np )/*
X===================
X	return TRUE if np points at a string containing a % sign */
Xchar *np;
X{
X   return( (strchr(np,'%') && (*np != '\'' && np[strlen(np)-1] != '\'')) ?
X	   TRUE : FALSE );
X}
X
X
Xstatic char *
X_is_magic( np )/*
X=================
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 ../ */
Xchar *np;
X{
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}
X
SHAR_EOF
echo "File rulparse.c is complete"
chmod 0440 rulparse.c || echo "restore of rulparse.c fails"
echo "x - extracting quit.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > quit.c &&
X/* RCS      -- $Header: /u2/dvadura/src/generic/dmake/src/RCS/quit.c,v 1.1 90/07/19 13:53:29 dvadura Exp $
X-- SYNOPSIS -- end the dmake session.
X-- 
X-- DESCRIPTION
X-- 	Handles dmake termination.
X--
X-- AUTHOR
X--      Dennis Vadura, dvadura@watdragon.uwaterloo.ca
X--      CS DEPT, University of Waterloo, Waterloo, Ont., Canada
X--
X-- COPYRIGHT
X--      Copyright (c) 1990 by Dennis Vadura.  All rights reserved.
X-- 
X--      This program is free software; you can redistribute it and/or
X--      modify it under the terms of the GNU General Public License
X--      (version 1), as published by the Free Software Foundation, and
X--      found in the file 'LICENSE' included with this distribution.
X-- 
X--      This program is distributed in the hope that it will be useful,
X--      but WITHOUT ANY WARRANTY; without even the implied warrant of
X--      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
X--      GNU General Public License for more details.
X-- 
X--      You should have received a copy of the GNU General Public License
X--      along with this program;  if not, write to the Free Software
X--      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
X--
X-- LOG
X--     $Log:	quit.c,v $
X * Revision 1.1  90/07/19  13:53:29  dvadura
X * Initial Revision of Version 3.5
X * 
X*/
X
X#include "extern.h"
X
Xstatic	void	_handle_quit ANSI((char*));
Xstatic	int	_dont_quit = 0;
X
X
Xvoid
XQuit()/*
X======== Error or quit */
X{
X   if( _dont_quit ) return;
X
X   while( Closefile() != NIL( FILE ) );
X   Clean_up_processes();
X
X   if( Current_target != NIL(HOW) )
X      Unlink_temp_files(Current_target);
X
X   if( _dont_quit == 0 ) _handle_quit( ".ERROR" );
X
X   Set_dir( Makedir );		/* No Error message if we can't do it */
X   Epilog( ERROR_EXIT_VALUE );
X}
X
X
Xstatic void
X_handle_quit( err_target )/*
X============================
X   Called by quit and the others to handle the execution of termination code
X   from within make */
Xchar *err_target;
X{
X   HASHPTR hp;
X   CELLPTR cp;
X
X   if( (hp = Get_name(err_target, Defs, FALSE, NIL(CELL))) != NIL(HASH) ) {
X      cp = hp->CP_OWNR;
X      Glob_attr |= A_IGNORE;
X
X      _dont_quit = 1;
X      cp->ce_flag |= F_TARGET;
X      Make( cp, cp->CE_HOW, NIL(CELL) );
X   }
X}
SHAR_EOF
chmod 0440 quit.c || echo "restore of quit.c fails"
echo "x - extracting percent.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > percent.c &&
X/* RCS      -- $Header: /u2/dvadura/src/generic/dmake/src/RCS/percent.c,v 1.1 90/07/19 13:53:25 dvadura Exp $
X-- SYNOPSIS -- handle building or %-rule meta-target nfa.
X-- 
X-- DESCRIPTION
X--	Builds the NFA used by dmake to match targets against %-meta
X--	rule constructs.  The NFA is built as a set of DFA's.
X-- 
X-- AUTHOR
X--      Dennis Vadura, dvadura@watdragon.uwaterloo.ca
X--      CS DEPT, University of Waterloo, Waterloo, Ont., Canada
X--
X-- COPYRIGHT
X--      Copyright (c) 1990 by Dennis Vadura.  All rights reserved.
X-- 
X--      This program is free software; you can redistribute it and/or
X--      modify it under the terms of the GNU General Public License
X--      (version 1), as published by the Free Software Foundation, and
X--      found in the file 'LICENSE' included with this distribution.
X-- 
X--      This program is distributed in the hope that it will be useful,
X--      but WITHOUT ANY WARRANTY; without even the implied warrant of
X--      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
X--      GNU General Public License for more details.
X-- 
X--      You should have received a copy of the GNU General Public License
X--      along with this program;  if not, write to the Free Software
X--      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
X--
X-- LOG
X--     $Log:	percent.c,v $
X * Revision 1.1  90/07/19  13:53:25  dvadura
X * Initial Revision of Version 3.5
X * 
X*/
X
X#include "extern.h"
X#include "alloc.h"
X#include "db.h"
X
X#define NO_ACTION	0
X#define START_PERCENT	1
X#define END_PERCENT	2
X#define ACCEPT		4
X#define EPSILON		8
X#define FAIL		-1
X
Xstatic NFAPTR _nfa = NIL( NFA );
X
X
XDFALINKPTR
XMatch_dfa( buf )/*
X==================
X   This routines runs all DFA's in parrallel and selects the one that best
X   matches the string.  If no match then it returns NIL( DFA ) */
Xchar *buf;
X{
X   register NFAPTR nfa;
X   int  	   adv;
X   DFALINKPTR	   dfa_list = NIL(DFALINK);
X
X   DB_ENTER( "Match_dfa" );
X   DB_PRINT( "dfa", ("Matching %s", buf) );
X
X   /* Run each of the DFA's on the input string in parallel, we terminate
X    * when all DFA's have either failed or ACCEPTED, if more than one DFA
X    * accepts we build a list of all accepting DFA's sorted on states with
X    * those matching in a higher numbered state heading the list. */
X
X   do {
X      adv = FALSE;
X
X      for( nfa = _nfa; nfa != NIL( NFA ); nfa = nfa->next )
X	 if( nfa->status != (char) FAIL && nfa->status != (char) ACCEPT ) {
X	    adv++;
X	    nfa->status = Advance_dfa( nfa->dfa, buf );
X
X	    /* Construct the list of matching DFA's */
X	    if( nfa->status == (char) ACCEPT ) {
X	       DFALINKPTR dl;
X
X	       TALLOC( dl, 1, DFALINK );
X	       dl->dl_meta  = nfa->dfa->node;
X	       dl->dl_per   = _substr( nfa->dfa->pstart, nfa->dfa->pend );
X	       dl->dl_state = nfa->dfa->n_states;
X
X	       if( dfa_list == NIL(DFALINK) )
X	          dfa_list = dl;
X	       else {
X		  DFALINKPTR tdli = dfa_list;
X		  DFALINKPTR tdlp = NIL(DFALINK);
X
X		  for( ; tdli != NIL(DFALINK); tdli = tdli->dl_next ) {
X		     if( dl->dl_state >= tdli->dl_state )
X			break;
X		     tdlp = tdli;
X		  }
X
X		  if( tdli != NIL(DFALINK) ) {
X		     tdli->dl_prev = dl;
X		     dl->dl_next   = tdli;
X		  }
X
X		  if( tdlp != NIL(DFALINK) ) {
X		     tdlp->dl_next = dl;
X		     dl->dl_prev   = tdlp;
X		  }
X		  else
X		     dfa_list = dl;
X	       }
X
X	       DB_PRINT( "dfa", ("Matched [%s]", dl->dl_meta->CE_NAME) );
X	    }
X	 }
X
X      buf++;
X   }
X   while ( adv );
X
X   for( nfa = _nfa; nfa != NIL( NFA ); nfa = nfa->next )
X      nfa->status = nfa->dfa->c_state = 0;
X
X   DB_RETURN( dfa_list );
X}
X
X
X
Xvoid
XAdd_dfa( buf )/*
X================
X   This function adds a DFA to the string of DFA's that form the NFA.
X   The DFA is added at the head of the list because ordering is unimportant,
X   when we do a match all DFA are run in parallel.  */
Xchar *buf;
X{
X   NFAPTR nfa;
X
X   DB_ENTER( "Add_dfa" );
X   DB_PRINT( "dfa", ("Adding dfa [%s]", buf) );
X
X   TALLOC( nfa, 1, NFA );
X   nfa->dfa = Construct_dfa( buf );
X
X   if( _nfa != NIL(NFA) )
X      nfa->next = _nfa;
X
X   _nfa = nfa;
X
X   DB_VOID_RETURN;
X}
X
X
X
XDFAPTR
XConstruct_dfa( pat )/*
X======================
X   This routine takes a string pattern which possibly contains a single %
X   and returns a dfa which will recognize the string, treating the % as
X   a token which accepts any char upto the next following char.  So for
X   example:
X	
X		%.o  ==> accepts f.o  % = f, fred.o % = fred, fred.c FAILS
X	       f%.o  ==> accepts f.o  % =  , fred.o % = red,  fred.c FAILS
X
X   and so on.  The function constructs the DFA and returns a pointer to the
X   DFA struct. */
X
Xchar *pat;
X{
X   register int 	i;
X   register DFAPTR 	dfa;
X   register STATEPTR	state;
X   int			l = strlen( pat ) + 1;
X   int			n;
X   int			no_match = -1;
X   int			pend = 0;
X   char			*_strdup();
X
X   DB_ENTER( "Construct_dfa" );
X   DB_PRINT( "dfa", ("Constructing dfa for %s", pat) );
X
X   n = l + 2;
X
X   TALLOC( dfa, 1, DFA );
X   TALLOC( dfa->states, n, STATE );
X
X   dfa->n_states = n;
X   dfa->node     = Def_cell( pat, NIL(CELL) );
X
X   for( i=0; i<l; i++, pat++ ) {
X      state = dfa->states + i;
X
X      if( pend == 1 ) {
X         state->action |= END_PERCENT;
X	 pend++;
X      }
X
X      switch( *pat ) {
X         case '%':				/* % start state	*/
X	    if( pend > 0 )
X	       Error( "Only a single %% allowed in a target pattern" );
X	    pend++;
X	    state->symbol    = 0;
X	    state->next      = i+1;
X	    state->no_match  = no_match;
X	    state->action   |= START_PERCENT | EPSILON;
X	    no_match = i+1;
X	    break;
X
X	 case '\0': 				/* termination state	*/
X	    state->symbol    = *pat;
X	    state->next      = -1;
X	    state->no_match  = no_match;
X	    state->action   |= ACCEPT;
X	    break;
X	    
X	 default:				/* generate a new state	*/
X	    state->symbol   = *pat;
X	    state->next     = i+1;
X	    state->no_match = no_match;
X	    break;
X      }
X   }
X
X   DB_RETURN( dfa );
X}
X
X
X
Xint
XAdvance_dfa( dfa, pch )/*
X=========================
X   This function takes the DFA that is provided and advances it to the next
X   states based on the value of *pch.  The function returns either 0 for
X   everything ok, -1 for fail, and ACCEPT if the dfa accepts the string. */
XDFAPTR dfa;
Xchar   *pch;
X{
X   STATEPTR s;
X   char     ch = *pch;
X   int      done = FALSE;
X   int      ret  = 0;
X
X   DB_ENTER( "Advance_dfa" );
X   DB_PRINT( "dfa", ("Advancing [%s] by %c", dfa->node->CE_NAME, *pch) );
X
X   while( !done ) {
X#ifdef DEBUG
X      if( dfa->c_state < 0 || dfa->c_state >= dfa->n_states )
X	 Fatal( "Internal, bad current dfa state %d in [%s]",
X		dfa->c_state, dfa->node->CE_NAME );
X#endif
X
X      s = dfa->states + dfa->c_state;
X
X      if( ch == '\0' && s->symbol != '\0' ) {
X	 done = TRUE;			/* FAIL string terminated first */
X	 ret  = -1;
X      }
X      else if( s->action & EPSILON ) {	/* set the start of % string	*/
X	 if( s->action & START_PERCENT ) dfa->pstart = pch;
X	 dfa->c_state = s->next;
X      }
X      else if( s->symbol == ch ) {	/* normal shift action		*/
X	 ret = s->action & ACCEPT;
X
X	 if( s->action & END_PERCENT ) dfa->pend = pch;
X	 dfa->c_state = s->next;
X	 done = TRUE;
X      }
X      else {				/* non-match shift action	*/
X	 if( s->no_match == dfa->c_state || s->no_match == -1 ) {
X	    done = TRUE;
X	    if( s->no_match == -1 ) ret = -1;	/* FAIL */
X	 }
X
X	 dfa->c_state = s->no_match;
X      }
X   }
X
X   DB_RETURN( ret );
X}
X
X
Xvoid
XCheck_circle_dfa()/*
X====================
X   This function is called to test for circularities in the DFA lists
X   constructed from %-meta targets. */
X{
X   register NFAPTR nfa;
X
X   for( nfa = _nfa; nfa != NIL(NFA); nfa = nfa->next )
X      if( Test_circle( nfa->dfa->node, TRUE ) )
X	 Fatal( "Detected circular dependency in inference graph at [%s]",
X		nfa->dfa->node->CE_NAME );
X}
SHAR_EOF
chmod 0440 percent.c || echo "restore of percent.c fails"
echo "x - extracting path.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > path.c &&
X/* RCS      -- $Header: /u2/dvadura/src/generic/dmake/src/RCS/path.c,v 1.1 90/07/19 13:53:25 dvadura Exp $
X-- SYNOPSIS -- pathname manipulation code
X-- 
X-- DESCRIPTION
X--	Pathname routines to handle building and pulling appart
X--	pathnames.
X-- 
X-- AUTHOR
X--      Dennis Vadura, dvadura@watdragon.uwaterloo.ca
X--      CS DEPT, University of Waterloo, Waterloo, Ont., Canada
X--
X-- COPYRIGHT
X--      Copyright (c) 1990 by Dennis Vadura.  All rights reserved.
X-- 
X--      This program is free software; you can redistribute it and/or
X--      modify it under the terms of the GNU General Public License
X--      (version 1), as published by the Free Software Foundation, and
X--      found in the file 'LICENSE' included with this distribution.
X-- 
X--      This program is distributed in the hope that it will be useful,
X--      but WITHOUT ANY WARRANTY; without even the implied warrant of
X--      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
X--      GNU General Public License for more details.
X-- 
X--      You should have received a copy of the GNU General Public License
X--      along with this program;  if not, write to the Free Software
X--      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
X--
X-- LOG
X--     $Log:	path.c,v $
X * Revision 1.1  90/07/19  13:53:25  dvadura
X * Initial Revision of Version 3.5
X * 
X*/
X
X#include "extern.h"
X#include "alloc.h"
X#include <string.h>
X
X/*
X** Return the suffix portion of a filename, assumed to begin with a `.'.
X*/
Xchar *
XGet_suffix(name)
Xchar *name;
X{
X   char *suff;
X
X   if(name == NIL(char)  || (suff = strrchr(name, '.')) == NIL(char))
X      suff = ".NULL";
X
X   return (suff);
X}
X
X
X
X/*
X** Take dir and name, and return a path which has dir as the directory
X** and name afterwards.
X**
X** N.B. Assumes that the dir separator string is in DirSepStr.
X**      Return path is built in a static buffer, if you need to use it
X**      again you must _strdup the result returned by Build_path.
X*/
Xchar *
XBuild_path(dir, name)
Xchar *dir;
Xchar *name;
X{
X   static char     *path  = NIL(char);
X   static unsigned buflen = 0;
X   int  plen = 0;
X   int  dlen = 0;
X   int  len;
X
X   if( dir  != NIL(char) ) dlen = strlen( dir  );
X   if( name != NIL(char) ) plen = strlen( name );
X   len = plen+dlen+strlen(DirSepStr)+1;
X
X   if( len > buflen ) {
X      buflen = (len+16) & ~0xf;		/* buf is always multiple of 16 */
X
X      if( path == NIL(char) )
X         path = MALLOC( buflen, char );
X      else
X         path = realloc( path, (unsigned) (buflen*sizeof(char)) );
X   }
X   
X   *path = '\0';
X
X   if( dlen ) {
X      strcpy( path, dir );
X
X      if( strchr(DirSepStr, dir[dlen-1]) == NIL(char) )
X	 strcat( path, DirSepStr );
X   }
X
X   strcat( path, name );
X   return( path );
X}
SHAR_EOF
chmod 0440 path.c || echo "restore of path.c fails"
echo "x - extracting patchlvl.h (Text)"
sed 's/^X//' << 'SHAR_EOF' > patchlvl.h &&
X/* dmake patch level, reset to 1 for each new version release. */
X
X#define PATCHLEVEL 1
SHAR_EOF
chmod 0440 patchlvl.h || echo "restore of patchlvl.h fails"
echo "x - extracting parse.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > parse.c &&
X/* RCS      -- $Header: /u2/dvadura/src/generic/dmake/src/RCS/parse.c,v 1.1 90/07/19 13:53:23 dvadura Exp $
X-- SYNOPSIS -- parse the input, and perform semantic analysis
X-- 
X-- DESCRIPTION
X-- 	This file contains the routines that parse the input makefile and
X--	call the appropriate routines to perform the semantic analysis and
X--	build the internal dag.
X--
X-- AUTHOR
X--      Dennis Vadura, dvadura@watdragon.uwaterloo.ca
X--      CS DEPT, University of Waterloo, Waterloo, Ont., Canada
X--
X-- COPYRIGHT
X--      Copyright (c) 1990 by Dennis Vadura.  All rights reserved.
X-- 
X--      This program is free software; you can redistribute it and/or
X--      modify it under the terms of the GNU General Public License
X--      (version 1), as published by the Free Software Foundation, and
X--      found in the file 'LICENSE' included with this distribution.
X-- 
X--      This program is distributed in the hope that it will be useful,
X--      but WITHOUT ANY WARRANTY; without even the implied warrant of
X--      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
X--      GNU General Public License for more details.
X-- 
X--      You should have received a copy of the GNU General Public License
X--      along with this program;  if not, write to the Free Software
X--      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
X--
X-- LOG
X--     $Log:	parse.c,v $
X * Revision 1.1  90/07/19  13:53:23  dvadura
X * Initial Revision of Version 3.5
X * 
X*/
X
X#include <ctype.h>
X#include "extern.h"
X#include "alloc.h"
X#include "db.h"
X
X
Xvoid
XParse( fil )/*
X==============  Parse the makefile input */
XFILE *fil;
X{
X   int	state = NORMAL_SCAN;           /* indicates current parser state */
X   int  group = FALSE;                 /* true if scanning a group rcpe  */
X   int  rule  = FALSE;                 /* have seen a recipe line        */
X   char *p;			       /* termporary pointer into Buffer */
X
X   DB_ENTER( "Parse" );
X
X   while( TRUE ) {
X      if( Get_line( Buffer, fil ) ) {
X	 if( fil != NIL( FILE ) )               /* end of parsable input */
X	    Closefile();
X
X	 Bind_rules_to_targets( F_DEFAULT );
X         if( group )  Fatal( "Incomplete rule recipe group detected" );
X
X	 DB_VOID_RETURN;
X      }
X      else
X         switch( state ) {
X	    case RULE_SCAN:
X
X	       /* Check for the `[' that starts off a group rule definition.  It
X	        * must appear as the first non-white space
X		* character in the line. */
X
X	       p = _strspn( Buffer, " \t" );
X               if( Set_group_attributes( p ) ) {
X                  if( rule  && group )
X                     Fatal( "Cannot mix single and group recipe lines" );
X                  else
X                     group = TRUE;
X
X                  rule = TRUE;
X
X                  break;                     /* ignore the group start  */
X               }
X
X               if( group ) {
X                  if( *p != ']' ) {
X                     Add_recipe_to_list( Buffer, TRUE, TRUE );
X                     rule = TRUE;
X                  }
X                  else
X                     state = NORMAL_SCAN;
X               }
X               else {
X                  if( *Buffer == '\t' ) {
X                     Add_recipe_to_list( Buffer, FALSE, FALSE );
X                     rule = TRUE;
X                  }
X                  else if( *p == ']' )
X                     Fatal( "Found unmatched ']'" );
X                  else if( *Buffer )
X		     state = NORMAL_SCAN;
X               }
X 
X               if( state == RULE_SCAN ) break;     /* ie. keep going    */
X               
X	       Bind_rules_to_targets( (group) ? F_GROUP: F_DEFAULT );
X
X               rule = FALSE;
X               if( group ) {
X                  group = FALSE;
X                  break;
X               }
X	       /*FALLTRHOUGH*/
X
X               /* In this case we broke out of the rule scan because we do not
X                * have a recipe line that begins with a <TAB>, so lets
X		* try to scan the thing as a macro or rule definition. */
X               
X
X	    case NORMAL_SCAN:
X	       if( !*Buffer ) continue;         /* we have null input line */
X
X	       /* STUPID AUGMAKE uses "include" at the start of a line as
X	        * a signal to include a new file, so let's look for it.
X		* if we see it replace it by .INCLUDE: and stick this back
X		* into the buffer. */
X	       if( !strncmp( "include", Buffer, 7 ) &&
X		   (Buffer[7] == ' ' || Buffer[7] == '\t') )
X	       {
X		  char *tmp;
X
X		  tmp = _strjoin( ".INCLUDE:", Buffer+7, -1, FALSE );
X		  strcpy( Buffer, tmp );
X		  FREE( tmp );
X	       }
X
X               /* look for a macro definition, they all contain an = sign
X	        * if we fail to recognize it as a legal macro op then try to
X		* parse the same line as a rule definition, it's one or the
X		* other */
X		
X	       if( Parse_macro(Buffer, M_DEFAULT) ) break;/* it's a macro def */
X	       if( Parse_rule_def( &state ) ) 	    break;/* it's a rule def  */
X
X	       /* if just blank line then ignore it */
SHAR_EOF
echo "End of part 5"
echo "File parse.c is continued in part 6"
echo "6" > s2_seq_.tmp
exit 0