[comp.sources.misc] v15i071: dmake version 3.6

dvadura@watdragon.waterloo.edu (Dennis Vadura) (10/15/90)

Posting-number: Volume 15, Issue 71
Submitted-by: Dennis Vadura <dvadura@watdragon.waterloo.edu>
Archive-name: dmake-3.6/part19

#!/bin/sh
# this is part 19 of a multipart archive
# do not concatenate these parts, unpack them in order with /bin/sh
# file make.c continued
#
CurArch=19
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 make.c"
sed 's/^X//' << 'SHAR_EOF' >> make.c
X   Explode_prq( Fringe_hd, NIL(CELL), FALSE );
X
X   _set_recipe( ".GROUPPROLOG", RP_GPPROLOG );
X   _set_recipe( ".GROUPEPILOG", RP_GPEPILOG );
X
X   while( !done ) {
X      done = F_MADE;
X      for( lp = Fringe_hd; lp != NIL(LINK); lp = lp->cl_next ) {
X	 int rval = 0;
X
X	 Root = lp->cl_prq;
X	 if( (rval |= Make(Root, Root->CE_HOW, NIL(CELL))) == -1 )
X	    DB_RETURN(1);
X	 else
X	    done &= (Root->ce_flag & F_MADE);
X
X	 if( !rval && !done ) Wait_for_child( FALSE, -1 );
X      }
X   }
X
X   for( lp = Fringe_hd; lp != NIL(LINK); lp = lp->cl_next ) {
X      Root = lp->cl_prq;
X      if( !(Root->ce_attr & A_UPDATED) )
X	 printf( "`%s' is up to date\n", Root->CE_NAME );
X   }
X
X   DB_RETURN( 0 );
X}
X
X
X
Xint
XMake( cp, how, setdirroot )/*
X=============================  Make a specified target */
XCELLPTR cp;
XHOWPTR  how;
XCELLPTR setdirroot;
X{
X   register LINKPTR dp;
X   register CELLPTR tcp;
X   char		    *name, *lib;
X   HASHPTR	    m_at    = NIL(HASH);
X   char             *all    = NIL(char);
X   char             *inf    = NIL(char);
X   char             *outall = NIL(char);
X   char             *imm    = NIL(char);
X   int              rval    = 0;
X   int		    push    = 0;
X   time_t           otime   = (time_t) 1L;
X   time_t	    ttime   = (time_t) 1L;
X
X   DB_ENTER( "Make" );
X   DB_PRINT( "mem", ("%s:-> mem %ld", cp->CE_NAME, (long) coreleft()) );
X
X   /* NOTE:  The only time we are called with a NIL how pointer is for
X    *	     cells that are to be statted only or for cells that are destined
X    *	     to have a set of rules inferred for them. */
X
X   if( how == NIL(HOW) ) {
X      TALLOC( cp->CE_HOW, 1, HOW );
X      how = cp->CE_HOW;
X   }
X
X   /* Check to see if we have made the node already.  If so then don't do
X    * it again, except if the cell's ce_setdir field is set to something other
X    * than the value of setdirroot.  If they differ then, and we have made it
X    * already, then make it again and set the cell's stat bit to off so that
X    * we do it again. */
X
X   if( how->hw_flag & F_VISITED ) {
X      /* we may return if we made it already from the same setdir location,
X       * or if it is not a library member whose lib field is non NULL.  (if
X       * it is such a member then we have a line of the form:
X       *	lib1 lib2 .LIBRARY : member_list...
X       * and we have to make sure all members are up to date in both libs. */
X
X      if( cp->ce_setdir == setdirroot &&
X	  !((cp->ce_attr & A_LIBRARYM) && (cp->ce_lib != NIL(char))) )
X	 DB_RETURN( 0 );
X
X      /* We check to make sure that we are comming from a truly different
X       * directory, ie. ".SETDIR=joe : a.c b.c d.c" are all assumed to come
X       * from the same directory, even though setdirroot is different when
X       * making dependents of each of these targets. */
X
X      if( cp->ce_setdir != NIL(CELL) && setdirroot != NIL(CELL) &&
X          cp->ce_setdir->ce_dir == setdirroot->ce_dir )
X         DB_RETURN( 0 );
X
X      cp->ce_flag  &= ~(F_STAT|F_VISITED|F_MADE);
X      how->hw_flag &= ~(F_VISITED|F_MADE);
X   }
X
X   if( how->hw_next != NIL(HOW))
X      if( (rval = Make(cp, how->hw_next, setdirroot)) == -1 ||
X	  !(how->hw_next->hw_flag & F_MADE) )
X	 goto stop_making_it;
X
X   /* If we are supposed to change directories for this target then do so.
X    * If we do change dir, then modify the setdirroot variable to reflect
X    * that fact for all of the prerequisites that we will be making. */
X
X   cp->ce_setdir = setdirroot;
X
X   if( cp->ce_attr & A_SETDIR ) {
X      /* Change directory only if the previous .SETDIR is a different
X      /* directory from the current one.  ie. all cells with the same .SETDIR
X      /* attribute are assumed to come from the same directory. */
X
X      if( (setdirroot == NIL(CELL) || setdirroot->ce_dir != cp->ce_dir) &&
X          (push = Push_dir(cp, (((cp->ce_attr|Glob_attr)&A_IGNORE) != 0))) )
X	 setdirroot = cp;
X   }
X
X   DB_PRINT( "mem", ("%s:-A mem %ld", cp->CE_NAME, (long) coreleft()) );
X   if( (how->hw_recipe == NIL(STRING) && !(how->hw_flag & F_INFER)) ) {
X      HOWPTR thp;
X      
X      if( Verbose )
X	 printf( "%s:  Infering prerequisite(s) and recipe for [%s]\n", Pname,
X	 	 cp->CE_NAME );
X
X      thp = how->hw_next;
X      (void) Infer_recipe( cp, how, NIL(DFASET), setdirroot );
X
X      /* If we inferred a new set of prerequisites then make
X       * them before the current list represented by the current how pointer */
X      if( thp != how->hw_next )
X	 if( (rval = Make(cp, how->hw_next, cp->ce_setdir)) == -1 ||
X	     !(how->hw_next->hw_flag & F_MADE) )
X	    goto stop_making_it;
X   }
X
X   DB_PRINT( "mem", ("%s:-A mem %ld", cp->CE_NAME, (long) coreleft()) );
X   /* If we have not yet statted the target then do so. */
X   if( !(cp->ce_flag & F_STAT) ) {
X      time_t itime = cp->ce_time;
X      Stat_target( cp, TRUE );
X
X      if( cp->ce_time == (time_t)0L ) {
X         if( cp->ce_flag & F_INFER )
X	    cp->ce_time = itime;
X      }
X      else
X         cp->ce_attr |= A_PRECIOUS;  /* File exists so don't remove it later. */
X
X      if( Verbose )
X	 printf("%s:  Time stamp of [%s] is %ld\n",Pname,cp->CE_NAME,
X	        cp->ce_time);
X   }
X
X   DB_PRINT( "make", ("(%s, %ld, 0x%08x, 0x%04x)", cp->CE_NAME,
X			cp->ce_time, cp->ce_attr, cp->ce_flag) );
X
X   if( !(cp->ce_flag & F_TARGET) && (cp->ce_time == (time_t) 0L) )
X      if( Makemkf )
X	 DB_RETURN( -1 );
X      else
X	 Fatal( "`%s' not found, and can't be made", cp->CE_NAME );
X
X   DB_PRINT( "mem", ("%s:-A mem %ld", cp->CE_NAME, (long) coreleft()) );
X
X   /* set value of $* if we have not infered a recipe, in this case $* is
X    * the same as $(@:db), this allows us to be compatible with BSD make */
X   if( how->hw_per == NIL(char) ) how->hw_per = "$(@:db)";
X
X   for( dp = how->hw_prq; dp != NIL(LINK); dp = dp->cl_next ) {
X      int seq;
X
X      /* Make the prerequisite, note that if the current target has the
X       * .LIBRARY attribute set we pass on to the prerequisite the .LIBRARYM
X       * attribute and pass on the name of the current target as the library
X       * name, and we take it away when we are done.  */
X
X      tcp = dp->cl_prq;
X      seq = (((cp->ce_attr | how->hw_attr | Glob_attr) & A_SEQ) != 0);
X
X      if( tcp->ce_flag & F_VISITED )
X	 if( tcp->ce_flag & F_MADE ) {
X	    if( tcp->ce_time > ttime ) ttime = tcp->ce_time;
X	    continue;
X	 }
X	 else
X	    goto stop_making_it;
X
X      if( strchr(tcp->CE_NAME, '$') ) {
X	 /* Make this prerequisite link point at the real prerequisite we
X	  * are after, ie figure out what the dynamic one is and point at it. */
X
X	 m_at = Def_macro( "@", cp->ce_fname, M_MULTI );
X	 name = Expand( tcp->CE_NAME );
X
X	 tcp = dp->cl_prq = Def_cell( name, setdirroot );
X	 FREE( name );
X      }
X
X      if( cp->ce_attr & A_LIBRARY ) {
X         tcp->ce_attr |= A_LIBRARYM;
X	 tcp->ce_lib   = cp->ce_fname;
X      }
X
X      if( (tcp->ce_flag & (F_INFER|F_STAT))==F_INFER && cp->ce_time > ttime )
X	 tcp->ce_time = cp->ce_time;
X
X      /* Propagate parents F_REMOVE and F_INFER attribute to children.
X       * Make certain you do this AFTER propagating the time, since the
X       * time propagation test above uses the F_INFER flag to decide if
X       * it should do so. */
X      tcp->ce_flag |= cp->ce_flag & (F_REMOVE|F_INFER);
X
X      rval |= Make(tcp, tcp->CE_HOW, setdirroot);
X
X      if( cp->ce_attr & A_LIBRARY )
X         tcp->ce_attr ^= A_LIBRARYM;
X
X      if( rval == -1 || (seq && (rval == 1)) )
X	 goto stop_making_it;
X
X      if( tcp->ce_time > ttime ) ttime = tcp->ce_time;
X   }
X
X   /* Do the loop again.  We are most definitely going to make the current
X    * how cell now.  NOTE:  doing this loop here also results in a reduction
X    * in peak memory usage by the algorithm. */
X
X   for( dp = how->hw_prq; dp != NIL(LINK); dp = dp->cl_next ) {
X      char *dir;
X      int  tgflg;
X      tcp  = dp->cl_prq;
X      name = tcp->ce_fname;
X
X      /* make certain that all prerequisites are made prior to advancing. */
X      if( !(tcp->ce_flag & F_MADE) ) goto stop_making_it;
X
X      /* If the target is a library, then check to make certain that a member
X       * is newer than an object file sitting on disk.  If the disk version
X       * is newer then set the time stamps so that the archived member is
X       * replaced. */
X      if( cp->ce_attr & A_LIBRARY )
X	 if( tcp->ce_time < cp->ce_time ) {
X	    time_t mtime = Do_stat( tcp->ce_fname, tcp->ce_lib, NIL(char *) );
X	    if( mtime < tcp->ce_time ) tcp->ce_time = cp->ce_time+1L;
X	 }
X
X      if( tcp->ce_time > otime ) otime = tcp->ce_time;
X
X      /* Note:  If the prerequisite was made using a SETDIR
X       * 	directory then we will include the directory in the name
X       *	of the prerequisite when we build the $?, $&, $^ and $< lists
X       */
X
X      if( (dir = tcp->ce_dir ) != NIL(char) )
X	 name = Build_path( dir, name );
X
X      all  = _strapp( all, name );
X      if( tgflg = (dp->cl_flag & F_TARGET) ) inf = _strapp( inf, name );
X
X      if((cp->ce_time<tcp->ce_time) || ((tcp->ce_flag & F_TARGET) && Force)) {
X         outall = _strapp( outall, name );
X         if( tgflg ) imm = _strapp( imm, name );
X      }
X   }
X
X   DB_PRINT( "mem", ("%s:-C mem %ld", cp->CE_NAME, (long) coreleft()) );
X   DB_PRINT( "make", ("I make '%s' if %ld > %ld", cp->CE_NAME, otime,
X	      cp->ce_time) );
X
X   /* mark the how cell as visited since we are making it for certain now. */
X   how->hw_flag |= F_VISITED;
X   if( Verbose ) printf( "%s:  >>>> Making [%s]\n", Pname, cp->CE_NAME );
X
X   if( Check && (cp->ce_time < otime) ) {
X      /* Only checking so stop as soon as we determine we will make something */
X      rval = -1;
X      goto stop_making_it;
X   }
X
X   if( (cp->ce_time < otime) || ((cp->ce_flag & F_TARGET) && Force) ) {
X      if( Verbose )
X	 printf( "%s:  Updating [%s], (%ld > %ld)\n", Pname,
X		 cp->CE_NAME, otime, cp->ce_time );
X
X      cp->ce_attr |= A_UPDATED;
X
X      if( Touch ) {
X	 name = cp->ce_fname;
X	 lib  = cp->ce_lib;
X
X	 if( !(Glob_attr & A_SILENT) || !Trace )
X	    if( lib == NIL(char) )
X	       printf("touch(%s)", name );
X	    else if( cp->ce_attr & A_SYMBOL )
X	       printf("touch(%s((%s)))", lib, name );
X	    else
X	       printf("touch(%s(%s))", lib, name );
X
X	 if( !Trace )
X	    if( Do_touch( name, lib,
X		(cp->ce_attr & A_SYMBOL) ? &name : NIL(char *) ) != 0 )
X	       printf( "  not touched - non-existant" );
X
X	 printf( "\n" );
X	 Update_time_stamp( cp, how );
X      }
X      else if( how->hw_recipe != NIL(STRING) ) {
X	 HASHPTR m_q, m_b, m_g, m_l, m_bb, m_up;
X	 TKSTR   tk;
X
X	 m_at = Def_macro( "@", cp->ce_fname,M_MULTI );
X	 m_g  = Def_macro( ">", cp->ce_lib,  M_MULTI );
X	 m_q  = Def_macro( "?", outall,      M_MULTI );
X	 m_b  = Def_macro( "<", inf,         M_MULTI );
X	 m_l  = Def_macro( "&", all,         M_MULTI );
X	 m_bb = Def_macro( "*", how->hw_per, M_MULTI );
X	 m_up = Def_macro( "^", imm,         M_MULTI );
X
X	 _recipes[ RP_RECIPE ] = how->hw_recipe;
X
X	 if( !(how->hw_flag & F_SINGLE) )
X	    rval = _exec_commands( cp, how );
X	 else {
X	    _drop_mac( m_q );
X	    SET_TOKEN( &tk, outall );
X
X	    Doing_bang = TRUE;
X	    name = Get_token( &tk, "", FALSE );
X	    do {
X	       m_q->ht_value = name;
X
X	       Wait_for_completion = TRUE;	/* Reset in _exec_commands */
X	       rval = _exec_commands( cp, how );
X	       Unlink_temp_files(how);
X	    }
X	    while( *(name = Get_token( &tk, "", FALSE )) != '\0' );
X	    Doing_bang = FALSE;
X	    Update_time_stamp( cp, how );
X
X	    m_q->ht_value = NIL(char);
X	 }
X
X	 _drop_mac( m_g  );
X	 _drop_mac( m_q  );
X	 _drop_mac( m_b  );
X	 _drop_mac( m_l  );
X	 _drop_mac( m_bb );
X	 _drop_mac( m_up );
X      }
X      else if( !(cp->ce_flag & F_RULES) &&
X	       ((cp != Root) || !(cp->ce_flag & F_EXPLICIT)) )
X	 Fatal( "Don't know how to make `%s'",cp->CE_NAME );
X      else {
X         /* Empty recipe, set the how flag as MADE and update the time stamp */
X	 Update_time_stamp( cp, how );
X      }
X   }
X   else {
X      /* Make sure everyone gets remade if Force is set */
X      if( !(cp->ce_flag & F_TARGET) && Force ) cp->ce_time = Do_time();
X
X      /* Set how to MADE since it is done, it was not out of date */
X      how->hw_flag |= F_MADE;
X      if( cp->CE_HOW == how ) {
X	 tcp = cp;
X	 do {
X	    tcp->ce_flag |= F_MADE;
X	    tcp = tcp->ce_all;
X	 }
X	 while( tcp != NIL(CELL) && tcp != cp );
X      }
X   }
X
X   if( cp->CE_HOW == how ) cp->ce_flag |= F_VISITED;
X
Xstop_making_it:
X   if( m_at != NIL(HASH) ) _drop_mac( m_at );
X
X   if( push )  Pop_dir(FALSE);
X
X   if( inf    != NIL(char) ) FREE( inf    );
X   if( all    != NIL(char) ) FREE( all    );
X   if( imm    != NIL(char) ) FREE( imm    );
X   if( outall != NIL(char) ) FREE( outall );
X
X   DB_PRINT( "mem", ("%s:-< mem %ld", cp->CE_NAME, (long) coreleft()) );
X   DB_RETURN( rval );
X}
X
X
X
Xstatic void
X_drop_mac( hp )/*
X================ set a macro value to zero. */
XHASHPTR hp;
X{
X   if( hp->ht_value != NIL(char) ) {
X      FREE( hp->ht_value );
X      hp->ht_value = NIL(char);
X   }
X}
X
X
X
Xstatic int
X_exec_commands( cp, how )/*
X===========================
X  Execute the commands one at a time that are pointed to by the rules pointer
X  of the target cp. If a group is indicated, then the hw_attr determines
X  .IGNORE and .SILENT treatment for the group.
X  
X  The function returns 0, if the command is executed and has successfully
X  returned, and returns 1 if the command is executing but has not yet
X  returned (for parallel makes).
X  
X  The F_MADE bit in the how cell is guaranteed set when the command has
X  successfully completed.  */
XCELLPTR cp;
XHOWPTR  how;
X{
X   static HASHPTR hsh = NIL(HASH);
X   register STRINGPTR	rp;
X   char			*cmnd;
X   char			*groupfile;
X   FILE    		*tmpfile;
X   int			do_it;
X   t_attr		attr;
X   int			group;
X   int			trace;
X   int			rval  = 0;
X
X   DB_ENTER( "_exec_commands" );
X
X   Current_target = how;
X   attr  = Glob_attr | cp->ce_attr;
X   trace = Trace || !(attr & A_SILENT);
X   group = how->hw_flag & F_GROUP;
X
X   if( group ) {
X      attr  |= how->hw_attr;
X      trace  = Trace || !(attr & A_SILENT);
X
X      if( !Trace ) tmpfile = Start_temp( Grp_suff, cp, how, &groupfile );
X      if( trace )  fputs( "[\n", stdout );
X
X      /* Emit group prolog */
X      if( attr & A_PROLOG )
X         _append_file( _recipes[RP_GPPROLOG], tmpfile, cp->CE_NAME, trace );
X   }
X
X   if( hsh == NIL(HASH) )
X      hsh = Def_macro("USESHELL", NIL(char), M_MULTI|M_EXPANDED);
X
X   /* Process commands in recipe. If in group, merely append to file.
X    * Otherwise, run them.  */
X   for( rp = _recipes[RP_RECIPE]; rp != NIL(STRING); rp=rp->st_next,FREE(cmnd)){
X      t_attr a_attr = attr|how->hw_attr|rp->st_attr;
X      int    shell  = ((a_attr & A_SHELL) != 0);
X
X      Swap_on_exec  = ((a_attr & A_SWAP) != 0);	  /* Swapping for DOS only */
X      hsh->ht_value = (group||shell)?"yes":"no";
X      do_it = !Trace;
X
X      if( !group && Trace && _strstr(rp->st_string,"$(MAKE)") ) {
X	 Wait_for_completion |= Trace;
X	 do_it = TRUE;
X      }
X
X      /* Expand needs these set to properly handle some GNU style functions */
X      Recipe_cell = cp;
X      Recipe_how  = how;
X      cmnd = Expand( rp->st_string );
X
X      if( group )
X         Append_line( cmnd, TRUE, tmpfile, cp->CE_NAME, trace, 0 );
X      else {
X	 if( *_strspn(cmnd, " \t") != '\0' )
X	    _print_cmnd(cmnd, !(do_it && ((rp->st_attr | attr) & A_SILENT)), 0);
X	 else
X	    do_it = FALSE;
X
X	 rval=Do_cmnd(cmnd,FALSE,do_it,cp,how,((attr|rp->st_attr)&A_IGNORE)!=0,
X		      shell, rp->st_next == NIL(STRING) );
X      }
X   }
X
X   /* If it is a group then output the EPILOG if required and possibly
X    * execute the command */
X   if( group ) {
X      if( attr & A_EPILOG )	/* emit epilog */
X	 _append_file( _recipes[RP_GPEPILOG], tmpfile, cp->CE_NAME, trace );
X
X      if( trace ) fputs("]\n", stdout);
X
X      if( do_it = !Trace ) Close_temp( how, tmpfile );
X      rval = Do_cmnd(groupfile, TRUE, do_it, cp, how, (attr & A_IGNORE)!=0,
X		     TRUE, TRUE);
X   }
X
X   Wait_for_completion = FALSE;
X   DB_RETURN( rval );
X}
X
X
Xstatic void
X_print_cmnd( cmnd, echo, map )/*
X================================
X   This routine is called to print out the command to stdout.  If echo is
X   false the printing to stdout is supressed, but the new lines in the command
X   are still deleted. */
Xchar *cmnd;
Xint  echo;
Xint  map;
X{
X   register char *p;
X   register char *n;
X
X   DB_ENTER( "_print_cmnd" );
X
X   if( echo ) {
X      printf( "%s\n", cmnd  );
X      fflush(stdout);
X   }
X
X   for( p=cmnd; (n = strchr(p, CONTINUATION_CHAR)) != NIL(char); )
X      if(n[1] == '\n') {
X	 DB_PRINT( "make", ("fixing [%s]", p) );
X	 strcpy( n, n+2 );
X	 p = n;
X      }
X      else {
X	 if( map ) Map_esc( n );
X	 p = n+1;
X      }
X
X   DB_VOID_RETURN;
X}
X
X
X
X/* These routines are used to maintain a stack of directories when making
X * the targets.  If a target cd's to the directory then it is assumed that
X * it will undo it when it is finished making itself. */
X
Xstatic STRINGPTR dir_stack = NIL(STRING);
X
Xint
XPush_dir( cp, ignore )/*
X==========================
X   Change the current working directory to dir and save the current
X   working directory on the stack so that we can come back.
X   
X   If ignore is TRUE then do not complain about _ch_dir if not possible.*/
XCELLPTR cp;
Xint  ignore;
X{
X   STRINGPTR   new_dir;
X   char        *dir;
X
X   DB_ENTER( "Push_dir" );
X
X   if( (dir = cp->ce_dir) == NIL(char) ) dir = Pwd;
X   if( *dir == '\'' && dir[strlen(dir)-1] == '\'' ) {
X      dir = _strdup(dir+1);
X      dir[strlen(dir)-1]='\0';
X   }
X   else
X      dir = Expand(dir);
X
X   if( Set_dir(dir) ) {
X      if( !ignore )
X         Fatal( "Unable to change to directory `%s', target is [%s]",
X	        dir, cp->CE_NAME );
X      FREE(dir);
X      DB_RETURN( 0 );
X   }
X
X   DB_PRINT( "dir", ("Push: [%s]", dir) );
X   if( Verbose ) printf( "%s:  Changed to directory [%s]\n", Pname, dir  );
X
X   FREE( dir );
X   TALLOC( new_dir, 1, STRING );
X   new_dir->st_next   = dir_stack;
X   dir_stack          = new_dir;
X   new_dir->st_string = _strdup( Pwd );
X
X   Def_macro( "PWD", Get_current_dir(), M_MULTI | M_EXPANDED );
X   _set_tmd();
X
X   DB_RETURN( 1 );
X}
X
X
X
Xvoid
XPop_dir(ignore)/*
X=================
X   Change the current working directory to the previous saved dir. */
Xint ignore;
X{
X   STRINGPTR old_dir;
X   char      *dir;
X
X   DB_ENTER( "Pop_dir" );
X
X   if( dir_stack == NIL(STRING) )
X      if( ignore ) {
X         DB_VOID_RETURN;
X      }
X      else
X	 Error( "Directory stack empty for return from .SETDIR" );
X
X   if( Set_dir(dir = dir_stack->st_string) )
X      Fatal( "Could not change to directory `%s'", dir );
X
X   Def_macro( "PWD", dir, M_MULTI | M_EXPANDED );
X   DB_PRINT( "dir", ("Pop: [%s]", dir) );
X   if( Verbose ) printf( "%s:  Changed back to directory [%s]\n", Pname, dir);
X
X   old_dir   = dir_stack;
X   dir_stack = dir_stack->st_next;
X
X   FREE( old_dir->st_string );
X   FREE( old_dir );
X   _set_tmd();
X
X   DB_VOID_RETURN;
X}
X
X
X
Xstatic void
X_set_tmd()/*
X============
X   Set the TWD Macro */
X{
X   TKSTR md, pd;
X   char  *m, *p;
X   char  *tmd;
X   int   is_sep;
X   int   first = 1;
X
X   SET_TOKEN( &md, Makedir );
X   SET_TOKEN( &pd, Pwd );
X
X   m = Get_token( &md, DirBrkStr, FALSE );
X   (void) Get_token( &pd, DirBrkStr, FALSE );
X   is_sep = (strchr(DirBrkStr, *m) != NIL(char));
X   tmd = _strdup( "" );
X
X   do {
X      m = Get_token( &md, DirBrkStr, FALSE );
X      p = Get_token( &pd, DirBrkStr, FALSE );
X
X      if( !is_sep && strcmp(m, p) ) {	/* they differ */
X	 char *tmp;
X	 if( first ) {		/* They differ in the first component	*/
X	    tmd = Makedir;	/* In this case use the full path	*/
X	    break;
X	 }
X
X	 if( *p ) tmp = Build_path( "..", tmd );
X	 if( *m ) tmp = Build_path( tmd, m );
X	 FREE( tmd );
X	 tmd = _strdup( tmp );
X      }
X
X      is_sep = 1-is_sep;
X      first  = 0;
X   } while (*m || *p);
X
X   CLEAR_TOKEN( &md );
X   CLEAR_TOKEN( &pd );
X
X   Def_macro( "TMD", tmd, M_MULTI | M_EXPANDED );
X   if( tmd != Makedir ) FREE( tmd );
X}
X
X
Xstatic void
X_set_recipe( target, ind )/*
X============================
X   Set up the _recipes static variable so that the slot passed in points
X   at the rules corresponding to the target supplied. */
Xchar *target;
Xint  ind;
X{
X   CELLPTR cp;
X   HASHPTR hp;
X
X   if( (hp = Get_name( target, Defs, FALSE, NIL(CELL) )) != NIL(HASH) ) {
X      cp = hp->CP_OWNR;
X      if( cp->CE_HOW != NIL(HOW) ) _recipes[ ind ] = cp->CE_RECIPE;
X   }
X   else
X      _recipes[ ind ] = NIL(STRING);
X}
X
X
X
Xvoid
XAppend_line( cmnd, newline, tmpfile, name, printit, map )
Xchar *cmnd;
Xint  newline;
XFILE *tmpfile;
Xchar *name;
Xint  printit;
Xint  map;
X{
X   _print_cmnd( cmnd, printit, map );
X
X   if( Trace ) return;
X
X   fputs(cmnd, tmpfile);
X   if( newline ) fputc('\n', tmpfile);
X   fflush(tmpfile);
X
X   if( ferror(tmpfile) )
X      Fatal("Write error on temporary file, while processing `%s'", name);
X}
X
X
X
Xstatic void
X_append_file( rp, tmpfile, name, printit )
Xregister STRINGPTR rp;
XFILE 		   *tmpfile;
Xchar 		   *name;
Xint 		   printit;
X{
X   char *cmnd;
X
X   while( rp != NIL(STRING) ) {
X      Append_line(cmnd = Expand(rp->st_string), TRUE, tmpfile, name, printit,0);
X      FREE(cmnd);
X      rp = rp->st_next;
X   }
X}
SHAR_EOF
echo "File make.c is complete"
chmod 0440 make.c || echo "restore of make.c fails"
echo "x - extracting make.bat (Text)"
sed 's/^X//' << 'SHAR_EOF' > make.bat &&
Xecho off
Xrem  *** This is the make batchfile that is used under MSDOS to make the
Xrem  *** first version of dmake.  It isn't pretty but it does work, assuming
Xrem  *** the compilers have been correctly setup.  See the warning below
Xrem  *** concerning tlink, if you are making the Turbo C version.
Xrem
Xecho Running make.bat script to make a %1 copy of dmake.
X
Xif %0%1 == %0 goto error
Xif %1 == msc goto makemsc
Xif %1 == msc60 goto makemsc60
Xif %1 == mscswp goto makemscswp
Xif %1 == msc60swp goto makemsc60swp
Xif %1 == tcc goto maketcc
Xif %1 == tccswp goto maketccswp
X
Xrem label the possible DOS variations for dmake here.
X:error
Xecho INDEX:  You must specify one of:
Xecho    tcc      - Turbo C 2.0, compile.
Xecho    tccswp   - Turbo C 2.0 compile of swapping dmake.
Xecho    msc      - Microsoft C 4.0 to 5.1, compile.
Xecho    msc60    - Microsoft C 6.0 compile.
Xecho    mscswp   - Microsoft C 4.0 to 5.1, MASM 5.1 compile of swapping dmake.
Xecho    msc60swp - Microsoft C 6.0, MASM 5.1 compile of swapping dmake.
Xgoto end
X
Xrem This is the script that makes dmake using Microsoft C 4.0 or higher.
X:makemsc
Xmsdos\mscdos\mk.bat
Xgoto end
X
Xrem This is the script that makes dmake using Microsoft C 4.0 or higher.
X:makemsc60
Xmsdos\mscdos\mk60.bat
Xgoto end
X
X:makemscswp
Xmsdos\mscdos\mkswp.bat
Xgoto end
X
X:makemscswp
Xmsdos\mscdos\mk60swp.bat
Xgoto end
X
Xrem This is the script that makes dmake using Turbo C 2.0 or higher.
Xrem This is the script that makes dmake using Turbo C 2.0 or higher.
X:maketcc
Xcls
Xecho WARNING:
Xecho    The default response files:
Xecho        msdos\tccdos\obj.rsp
Xecho        msdos\tccdos\lib.rsp
Xecho    contain absolute paths to TURBO-C runtime startup objects, and to
Xecho    the standard libraries.  You should check that these files contain
Xecho    the correct path names for your installation of Turbo-C before
Xecho    proceeding further.
Xecho --
Xecho Continue if ok, or abort and edit the response files.
Xpause
Xmsdos\tccdos\mk.bat
Xgoto end
X
X:maketccswp
Xcls
Xecho WARNING:
Xecho    The default response files:
Xecho        msdos\tccdos\objswp.rsp
Xecho        msdos\tccdos\libswp.rsp
Xecho    contain absolute paths to TURBO-C runtime startup objects, and to
Xecho    the standard libraries.  You should check that these files contain
Xecho    the correct path names for your installation of Turbo-C before
Xecho    proceeding further.
Xecho --
Xecho Continue if ok, or abort and edit the response files.
Xpause
Xmsdos\tccdos\mkswp.bat
X
Xrem All done!
X:end
SHAR_EOF
chmod 0440 make.bat || echo "restore of make.bat fails"
echo "x - extracting macparse.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > macparse.c &&
X/* RCS      -- $Header: /u2/dvadura/src/generic/dmake/src/RCS/macparse.c,v 1.1 90/10/06 12:03:57 dvadura Exp $
X-- SYNOPSIS -- parse a macro definition
X-- 
X-- DESCRIPTION
X--	This file contains the code that parses a macro definition
X--	stored in a buffer.  If the string in buffer is not a valid
X--	macro definition the routie Parse_macro returns 0, otherwise it
X--	returns 1 to indicate success.
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:	macparse.c,v $
X * Revision 1.1  90/10/06  12:03:57  dvadura
X * dmake Release, Version 3.6
X * 
X*/
X
X#include <ctype.h>
X#include "extern.h"
X#include "alloc.h"
X#include "db.h"
X
Xint
XParse_macro( buffer, flag )/*
X=============================
X   Parse the string in buffer and define it as a macro if it is a valid macro.
X   Note especially the string .SETDIR= since it is an attribute, but looks a
X   lot like a macro definition.  This would not be a problem if make used
X   white space as token separators, since this is not the case we must do
X   something about it. */
Xchar *buffer;
Xint  flag;
X{
X   register char *tok1;		/* temporary place to keep a token */
X   register char *tok2;		/* temporary place to keep a token */
X   char		 *result;	/* temporary pointer for strings   */
X   TKSTR          input;        /* place to scan the buffer from   */
X   HASHPTR	  hv;		/* pointer to hash table value	   */
X   int		  operator;	/* what macro operator do we have  */
X
X   DB_ENTER( "Parse_macro" );
X
X   SET_TOKEN( &input, buffer );
X   tok1 = Get_token( &input, "=+:*", -1 );
X
X   if( Macro_op( tok1 ) ) {
X      Error( "No macro name" );
X      CLEAR_TOKEN( &input );
X      DB_RETURN( 1 );
X   }
X
X   tok1 = _strdup( tok1 );
X   tok2 = Get_token( &input, "=+:*", -1 );
X   if( !(operator = Macro_op( tok2 )) || !strcmp( tok1, ".SETDIR") )
X   {
X      CLEAR_TOKEN( &input );
X      FREE( tok1 );
X      DB_RETURN( 0 );
X   }
X
X   tok2 = Expand(tok1); FREE(tok1); tok1 = tok2;
X   tok2 = Get_token( &input, NIL( char ), FALSE );
X
X   switch( operator ) {
X      case M_OP_PLCL:
X	  tok2 = Expand( tok2 );
X	  /* Fall thru */
X
X      case M_OP_PL:
X	 /* Add to an existing macro, if it is not defined, though, then
X	  * just define a new macro */
X
X	 if( (hv = GET_MACRO(tok1)) == NIL(HASH) || hv->ht_value == NIL(char) )
X	    Def_macro( tok1, tok2, flag );
X	 else {
X	    result = _stradd( hv->ht_value, tok2, FALSE );
X	    Def_macro( tok1, result, flag );
X	    FREE( result );
X	 }
X	 if( operator == M_OP_PLCL ) FREE(tok2);
X	 break;
X
X      case M_OP_DF:
X 	 if( (hv = GET_MACRO(tok1)) != NIL(HASH) )
X	    break;
X	 /* else FALLTHRU */
X
X      case M_OP_EQ:
X	 Def_macro( tok1, tok2, flag );
X	 break;
X
X      case M_OP_DFCL:
X 	 if( (hv = GET_MACRO(tok1)) != NIL(HASH) )
X	    break;
X	 /* else FALLTHRU */
X
X      case M_OP_CL:
X	 /* If the macro we are assigning from is a single control
X	  * macro with nothing else, then we propagate the M_MULTI
X	  * flag to the macro we are assigning the value to so that
X	  * the same macro can be used to do this over and over. */
X	 If_multi = 0;
X	 tok2 = Expand( tok2 );
X	 Def_macro( tok1, tok2, M_EXPANDED | flag | If_multi );
X	 FREE( tok2 );
X	 break;
X   }
X	    
X   FREE( tok1 );
X
X   DB_RETURN( 1 );
X}
X
X
X
Xint
XMacro_op( op )/*
X================
X   Check the passed in op string and map it to one of the macro operators */
Xchar *op;
X{
X   int ret = 0;
X
X   DB_ENTER( "macro_op" );
X
X   switch( *op ) {
X      case '=': ret = M_OP_EQ; break;
X      case ':': ret = M_OP_CL; op++; break;
X
X      case '+':
X         ret = M_OP_PL; op++;
X         if( *op == ':' ) { ret = M_OP_PLCL; op++; }
X         break;
X
X      case '*':
X         ret = M_OP_DF; op++;
X         if( *op == ':' ) { ret = M_OP_DFCL; op++; }
X         break;
X   }
X
X   if( *op++ != '=' )
X      ret = 0;
X   else if( *op != '\0' )
X      ret = 0;
X
X   DB_RETURN( ret );
X}
X
SHAR_EOF
chmod 0440 macparse.c || echo "restore of macparse.c fails"
echo "x - extracting infer.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > infer.c &&
X/* RCS      -- $Header: /u2/dvadura/src/generic/dmake/src/RCS/infer.c,v 1.1 90/10/06 12:03:54 dvadura Exp $
X-- SYNOPSIS -- infer how to make a target.
X-- 
X-- DESCRIPTION
X--	This file contains the code to infer a recipe, and possibly some new
X--	prerequisites for a target which dmake does not know how to make, or
X--	has no explicit recipe.
X--
X--	The inference fails if no path through the inference graph can be
X--	found by which we can make the target.
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:	infer.c,v $
X * Revision 1.1  90/10/06  12:03:54  dvadura
X * dmake Release, Version 3.6
X * 
X*/
X
X#include "extern.h"
X#include "alloc.h"
X#include "db.h"
X
X/* attributes that get transfered from the % start cell to the inferred
X * cells. */
X
X#define A_TRANSFER  (A_EPILOG | A_PRECIOUS | A_SILENT | A_SHELL |\
X		     A_SEQ | A_LIBRARY | A_IGNORE | A_PROLOG | A_SWAP)
X
X/* Define local static functions */
Xstatic DFALINKPTR _dfa_subset  ANSI((DFALINKPTR, DFASETPTR));
Xstatic void 	  _free_dfas   ANSI((DFALINKPTR));
Xstatic int	  _count_dots  ANSI((char *));
Xstatic char *	  _build_name  ANSI((char *, char *, char *));
X
Xstatic int _prep = -1;	/* Integer value of Prep variable */
Xstatic int _dmax;
X
XCELLPTR
XInfer_recipe( cp, how, dfa_stack, setdirroot )/*
X================================================
X	Infer a set of rules for making a target.  We know we have a HOW
X	cell attached, but it's prerequisite list may be NIL as is its
X	recipe!  A NIL recipe is a prerequisite for calling this routine. */
XCELLPTR    cp;
XHOWPTR     how;
XDFASETPTR  dfa_stack;
XCELLPTR    setdirroot;
X{
X   DFALINKPTR	pdfa;
X   DFALINKPTR	dfas;
X   CELLPTR	infcell;
X   CELLPTR	meta;
X   DFASET	top_dfa_stack;
X   EDGEPTR      pedge;
X
X   DB_ENTER( "Infer_recipe" );
X   DB_PRINT( "inf", (">>> Infer for target [%s]", cp->CE_NAME) );
X   DB_PRINT( "mem", ("%s:-> mem %ld", cp->CE_NAME, (long)coreleft()));
X
X   /* If no further transitive closure on this cell then don't perform
X    * any more inferences */
X   if( cp->ce_attr & A_NOINFER ) DB_RETURN(NIL(CELL));
X   if( _prep == -1 ) _prep = atoi(Prep);  /* _dfa_subset needs _prep */
X
X   if( dfa_stack == NIL(DFASET) )
X      _dmax = _prep + _count_dots(cp->CE_NAME);
X
X   /* If none of the inference nodes match then forget about the inference.
X    * The user did not tell us how to make such a target.  We also stop the
X    * Inference if the new set of DFA's is a proper subset of a previous
X    * subset and it's PREP counts exceed the value of Prep.
X    */
X   dfas = _dfa_subset( Match_dfa(cp->CE_NAME), dfa_stack );
X
X#ifdef DBUG
X   { char      *tmp;
X     DFASETPTR ds;
X
X      tmp = _strdup("");
X      for( pdfa = dfas; pdfa != NIL(DFALINK); pdfa = pdfa->dl_next )
X         tmp = _strapp( tmp, pdfa->dl_meta->CE_NAME );
X
X      DB_PRINT( "inf", ("Working DFA subset [%s]", tmp) );
X      FREE( tmp );
X
X      tmp = _strdup( "{" );
X      for( ds = dfa_stack; ds != NIL(DFASET); ds = ds->df_next ) {
X	 tmp = _strapp( tmp, "[" );
X	 for( pdfa = ds->df_set; pdfa != NIL(DFALINK); pdfa = pdfa->dl_next )
X	    tmp = _strapp( tmp, pdfa->dl_meta->CE_NAME );
X	 tmp = _strapp( tmp, "]" );
X      }
X      tmp = _strapp( tmp, "}" );
X
X      DB_PRINT( "inf", ("DFA stack:  %s", tmp) );
X      FREE(tmp);
X   }
X#endif
X
X   if( dfas == NIL(DFALINK) ) {
X      DB_PRINT( "mem", ("%s:<- mem %ld",cp->CE_NAME, (long)coreleft()));
X      DB_PRINT( "inf", ("<<< Exit, no dfas, cp = %04x", NIL(CELL)) );
X      DB_RETURN( NIL(CELL) );
X   }
X
X
X   /* We have a set of %-meta's that have not previously been considered, or
X    * whose counts do not violate the Prep count.  So we should consider
X    * them, and put them on the top of the stack.
X    */
X   top_dfa_stack.df_set  = dfas;
X   top_dfa_stack.df_next = dfa_stack;
X
X
X   /* Run through the %-meta cells, build the prerequisite cells.  If we are
X    * performing transitive closure, then call Infer_recipe with the new
X    * prerequisite.
X    */
X   for( pdfa = dfas, infcell = NIL(CELL), pedge = NIL(EDGE);
X        pdfa != NIL(DFALINK);
X        pdfa = pdfa->dl_next ) {
X      int     push   = 0;
X      int     _trans = 0;
X      EDGEPTR edge;
X      EDGEPTR edge_noprq;	/* No prerequisite for this edge */
X
X      DB_PRINT( "inf", ("Using dfa:  [%s]", pdfa->dl_meta->CE_NAME) );
X      meta = pdfa->dl_meta;
X
X
X      /* Change to the required directory prior to checking the prerequisites
X       * Only if the meta has a directory and we haven't changed dir's
X       * for the CELL already.
X       *
X       * Ignore the %-meta if we had to change to a dir, and the dir
X       * did not exist.
X       */
X      if( !(cp->ce_attr & A_SETDIR) && meta->ce_dir != NIL(char) )
X	 if( (setdirroot == NIL(CELL) || setdirroot->ce_dir != meta->ce_dir) &&
X	     (push = Push_dir(meta, TRUE)) )
X	    setdirroot = cp;
X	 else {
X	    DB_PRINT( "inf", ("Failed PUSH of [%s]", meta->ce_dir) );
X	    continue;
X	 }
X
X
X      /* Now run through the list of prerequisite edge's for the %-meta.
X       * Build each prerequisite in turn, and then see if it needs to be
X       * inferred.  We treat the edge that has NO prerequisites last, as
X       * it gives a recipe for making a %-meta from no prerequisites, and
X       * we should really check all NON-NULL prerequisites first.
X       */
X      edge = meta->CE_EDGES;
X      edge_noprq = NIL(EDGE);
X
X      do {
X         pedge = edge;
X
X	 if( edge->ed_prq == NIL(CELL) )
X	    edge_noprq = edge;
X	 else {
X	    HASHPTR  thp;	/* temporary hash table pointer		*/
X	    HASH     iprqh;	/* hash cell for new prerequisite	*/
X	    CELL     iprq;	/* inferred prerequisite to look for	*/
X	    CELLPTR  sdir;	/* local setdir root for inference	*/
X	    STRINGPTR sp   = NIL(STRING); /* pointer to rcp of inferred target*/
X	    int      ipush = 0;		  /* flag for push on inferred prereq */
X	    char     *name = edge->ed_prq->CE_NAME;
X	    int	      _dmax_fix;
X
X	    DB_PRINT( "inf", ("Trying edge from [%s] to [%s] for [%s]",
X	    	      meta->CE_NAME, name, cp->CE_NAME) );
X
X	    _trans = Transitive &&
X		     !((Glob_attr | edge->ed_prq->ce_attr) & A_NOINFER);
X
X	    /* Set the temp CELL used for building prerequisite candidates to
X	     * all zero so that we don't have to keep initializing all the
X	     * fields. */
X	    {
X	       register char *s = (char *) &iprq;
X	       register int   n = sizeof(CELL);
X	       while( n ) { *s++ = '\0'; n--; }
X	    }
X
X
X	    /* Build the prerequisite name from the %-meta prerequisite given
X	     * for the %-meta rule. */
X	    iprqh.ht_name = _build_name( cp->CE_NAME, name, pdfa->dl_per );
X	    if( strcmp(cp->CE_NAME, iprqh.ht_name) == 0 )
X	       goto try_next_edge;
X	    if((_dmax_fix = (_count_dots(name)-_count_dots(meta->CE_NAME))) < 0)
X	       _dmax_fix = 0;
X	    if( _count_dots(iprqh.ht_name) > _dmax + _dmax_fix )
X	       goto try_next_edge;
X
X	    DB_PRINT( "inf", ("Checking prerequisite [%s]", iprqh.ht_name) );
X
X	    if( Verbose )
X	       printf( "%s:  Trying prerequisite [%s] for [%s]\n", Pname,
X		       iprqh.ht_name, cp->CE_NAME );
X
X
X	    /* See if the prerequisite CELL has been previously defined.  If
X	     * it has, then make a copy of it into iprq, and use it to try
X	     * the inference.  We make the copy so that we don't modify the
X	     * stat of the inferred cell if the inference fails.
X	     */
X	    thp = Get_name( iprqh.ht_name, Defs, FALSE, setdirroot );
X	    if(thp != NIL(HASH)) {
X	       iprq = *thp->CP_OWNR;
X	       if( iprq.CE_HOW != NIL(HOW) ) sp = iprq.CE_HOW->hw_recipe;
X	    }
X	    else
X	       iprq.ce_name  = &iprqh;
X
X
X	    /* If the prerequisite has the .SETDIR attr set, then we
X	     * should try to CD to the directory, if it is really different
X	     * from the one we are currently in.  If the CD fails we ignore
X	     * the edge and go try another edge.
X	     */
X	    sdir = setdirroot;
X	    if( iprq.ce_dir != NIL(char) )
X	       if( sdir==NIL(CELL) || sdir->ce_dir != iprq.ce_dir )
X	          if( ipush = Push_dir(&iprq, TRUE) )
X		     sdir = &iprq;
X		  else
X		     goto try_next_edge;
X
X
X	    /* Stat the inferred prerequisite.
X	     */
X	    if( !(iprq.ce_flag & F_STAT) ) Stat_target( &iprq, FALSE );
X
X
X	    /* If the STAT succeeded or if the prerequisite has a recipe for
X	     * making it, then infer it.  We may later try to perform a
X	     * second inference on this prerequisite when we actually go to
X	     * make it.
X	     */
X	    if( (iprq.ce_time != (time_t)0L) || (sp != NIL(STRING)) ) {
X	       infcell = Def_cell( iprqh.ht_name, setdirroot );
X	       infcell->ce_flag |= F_REMOVE;
X	    }
X	    else
X	       /* The STAT did not succeed, so call Infer_recipe recursively
X	        * to see if we know how to make the prerequisite.  If it
X		* returns not NULL, then we have an inferred prerequisite
SHAR_EOF
echo "End of part 19"
echo "File infer.c is continued in part 20"
echo "20" > s2_seq_.tmp
exit 0