dvadura@watdragon.waterloo.edu (Dennis Vadura) (07/27/90)
Posting-number: Volume 14, Issue 25 Submitted-by: dvadura@watdragon.waterloo.edu (Dennis Vadura) Archive-name: dmake/part15 #!/bin/sh # this is part 15 of a multipart archive # do not concatenate these parts, unpack them in order with /bin/sh # file make.c continued # CurArch=15 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 * 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))) 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%04x, 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; X X if( (tcp->ce_flag & F_VISITED) && !(tcp->ce_flag & F_MADE) ) X goto stop_making_it; X else if( tcp->ce_flag & F_MADE ) X continue; 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 ) 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 ) X goto stop_making_it; X else if( seq && (rval == 1) ) X goto stop_making_it; 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 register STRINGPTR rp; X char *cmnd; X char *groupfile; X char *ap; X char *hold = NIL(char); X FILE *tmpfile; X FILE *here_doc = NIL(FILE); X int do_it; X int 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 X /* Process commands in recipe. If in group, merely append to file. X * Otherwise, run them. If a text diversion indicated, expand X * that document as required into a temporary file */ X X for( rp = _recipes[RP_RECIPE]; rp != NIL(STRING); rp=rp->st_next,FREE(cmnd)){ X char *savecmnd; X int done = 0; X 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 savecmnd = cmnd = Expand( rp->st_string ); X X/******* TEXT DIVERSION PROCESSING START ********/ X /* Look for a text diversion surrounded by <+ and +>. Note that the X * diversion may be on a single line or span multiple lines. Note that X * if a diversion is started with <+ and never finished with +> an error X * is issued. */ X X /* Check for the start of a new diversion */ X do { X if( here_doc == NIL(FILE) && (ap = _strstr(cmnd, "<+")) != NIL(char)) { X char *t; X char *fname; X here_doc = Start_temp( "", cp, how, &fname ); X *ap = '\0'; X hold = _strjoin( hold, t = _strjoin(cmnd,fname,-1,FALSE),-1,TRUE); X FREE(t); X cmnd = ap+2; X } X X if( here_doc != NIL(FILE) ) { X if( (ap = _strstr(cmnd, "+>")) == NIL(char) ) { X _add_here(cmnd, here_doc, TRUE); X done = TRUE; X } X else { X *ap = '\0'; X if( *_strspn(cmnd, " \t") != '\0' ) X _add_here(cmnd, here_doc, FALSE); X X cmnd = ap+2; X Close_temp( how, here_doc ); X here_doc = NIL(FILE); X } X } X else if( hold != NIL(char) ) { X cmnd = _strjoin( hold, cmnd, -1, TRUE ); X done = TRUE; X hold = NIL(char); X FREE( savecmnd ); X } X else X done = TRUE; X } X while( *cmnd && !done ); X X if( here_doc != NIL(FILE) ) continue; X if( hold != NIL(char) ) { X FREE(savecmnd); X cmnd = hold; X } X/******* TEXT DIVERSION PROCESSING END ********/ X X X if( group ) X _append_line( cmnd, TRUE, tmpfile, cp->CE_NAME, trace ); 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), X rp->st_next == NIL(STRING) ); X } X } X X if( here_doc != NIL(FILE) ) X Fatal( "Unterminated text diversion while making `%s'", cp->CE_NAME ); 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, TRUE); X } X X Wait_for_completion = FALSE; X DB_RETURN( rval ); X} 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 ) printf( "%s\n", cmnd ); X X for( p=cmnd; (n = strchr(p, CONTINUATION_CHAR)) != NIL(char); p=n+1 ) X if(n[1] == '\n') { X DB_PRINT( "make", ("fixing [%s]", p) ); X strcpy( n, n+2 ); X } X else if( map ) X Map_esc( n ); 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 Xstatic void X_append_line( cmnd, newline, tmpfile, name, printit ) Xchar *cmnd; Xint newline; XFILE *tmpfile; Xchar *name; Xint printit; X{ X _print_cmnd( cmnd, printit, 0 ); 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 group file, for target `%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); X FREE(cmnd); X rp = rp->st_next; X } X} X X X Xstatic void X_add_here( line, file, newline ) Xchar *line; XFILE *file; Xint newline; X{ X while( *line == '\t' ) /* skip initial tabs */ X line++; X X _print_cmnd( line, FALSE, TRUE ); X fputs(line, file); X if( newline ) fputc('\n', file); X fflush(file); X X if( ferror(file) ) X Fatal("Write error on temporary file for text diversion"); 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 == mscdos goto makemsc Xif %1 == tccdos goto maketcc X Xrem label the possible DOS variations for dmake here. X:error Xecho ERROR: You must specify one of: Xecho tccdos - Turbo C 2.0, compile. Xecho mscdos - Microsoft C 4.0 or higher, compile. Xgoto end X Xrem This is the script that makes dmake using Microsoft C 4.0 or higher. X:makemsc Xmsdos\mscdos\make.bat Xgoto end X 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 msdos\tccdos\tccobj.rsp Xecho and msdos\tccdos\tcclib.rsp contain absolute paths to TURBO-C Xecho runtime startup objects, and to the standard libraries. You should Xecho check that these files point to the right places before proceeding. Xecho -- Xecho Continue if ok, or abort and edit the response files. Xpause Xmsdos\tccdos\make.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/07/19 13:53:20 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/07/19 13:53:20 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 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, "=+:", FALSE ); 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, "=+:", FALSE ); 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 X case M_OP_PL: X /* Add to an existing macro, if it is not defined, though, then X * fall through and define a new macro */ X X if( (hv = GET_MACRO(tok1)) != NIL(HASH) ) { X if( hv->ht_value != NIL(char) ) { X result = _stradd( hv->ht_value, tok2, FALSE ); X Def_macro( tok1, result, flag ); X FREE( result ); X } X else X Def_macro( tok1, tok2, flag ); X X if( operator == M_OP_PLCL ) FREE(tok2); X break; X } X /*FALLTRHOUGH*/ X X case M_OP_EQ: X Def_macro( tok1, tok2, flag ); X if( operator == M_OP_PLCL ) FREE(tok2); X break; 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 if( *op == '+' ) { ret = M_OP_PL; op++; } X if( *op == ':' ) { ret = M_OP_PLCL; 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/07/21 11:06:32 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/07/21 11:06:32 dvadura X * Initial Revision Version 3.5 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 |\ X A_LIBRARY | A_IGNORE | A_PROLOG) 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 X * and we should Define it and attach it to the CELL pointed at X * by cp. This recursive inference is performed only if X * Transitive closure is enabled. X */ X if( _trans ) { X int _save = _dmax; X if( !_dmax ) _dmax += _dmax_fix; X infcell = Infer_recipe( &iprq, iprq.CE_HOW, &top_dfa_stack, X sdir); X _dmax = _save; X X if( infcell != NIL(CELL) ) { X /* We found we can make the prerequisite, so make it into X * a real node. This means, mark it for possible X * removal, and when you make it into a node make sure X * you don't clobber the Def_cell name. X */ X infcell = Def_cell( iprqh.ht_name, setdirroot ); X thp = infcell->ce_name; X *infcell = iprq; X infcell->ce_name = thp; X infcell->ce_flag |= F_REMOVE; X } X } X X /* If we pushed a directory for the inferred prerequisite then X * pop it. X */ X if( ipush ) Pop_dir(FALSE); X Xtry_next_edge: X FREE( iprqh.ht_name ); X } X X edge = edge->ed_next; X } X while( infcell == NIL(CELL) && edge != meta->CE_EDGES ); X X X /* If none of the previous edges were any good, and there was an X * edge with no prerequsite, then use it. X */ X if( infcell == NIL(CELL) ) X if( edge_noprq != NIL(EDGE) ) X pedge = edge_noprq; X else X pedge = NIL(EDGE); X else { X /* We have a match, so make the edge's pointer for the corresponding X * %-meta target point at the matched prerequisite so that the next X * time we perform an inference we will check this edge first. X */ X meta->CE_EDGES = pedge; X if( !_trans ) infcell->ce_attr |= A_NOINFER; X } X X X /* If we pushed a dir when we treated this %-meta, then X * pop it. X */ X if( push ) Pop_dir(FALSE); X X X /* Need this so that pdfa does not get advanced, and remains pointing X * to the dfa, that just resulted in a successful match. X */ X if( pedge != NIL(EDGE) ) break; X } X X X /* If pedge is not NIL then we have found an edge and possibly inferred X * a prerequisite. In any case we should definitely attach the recipe to X * the HOW node of the cell pointed at by cp. If the CELL has no HOW node X * the we allocate one. X */ X if( pedge != NIL(EDGE) ) { X LINKPTR lp; X HOWPTR nhow, ihow; X X DB_PRINT("inf", ("taking edge [%s] to [%s]", pedge->ed_tg->CE_NAME, X (pedge->ed_prq == NIL(CELL)) ? "(none)":pedge->ed_prq->CE_NAME)); X X if( Verbose ) X printf("%s: Inferred recipe using edge from [%s] to [%s]\n", X Pname, pedge->ed_tg->CE_NAME, X (pedge->ed_prq == NIL(CELL)) ? "(none)":pedge->ed_prq->CE_NAME); X X if( how == NIL(HOW) ) { X /* Get a new HOW node, this should happen only for inferred X * cells, as such they have no prior HOW node */ X TALLOC( how, 1, HOW ); X cp->CE_HOW = how; X } X X X /* Attach the recipe to the HOW node. Note that if the %-meta recipe X * is a :: recipe then we will attach all of the HOW cells belonging to X * the %-meta :: rule that we matched to the CELL, and we will place X * them so that they preceed subsequent HOW cells in the list. Under SHAR_EOF echo "End of part 15" echo "File infer.c is continued in part 16" echo "16" > s2_seq_.tmp exit 0