home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Simtel MSDOS - Coast to Coast
/
simteldosarchivecoasttocoast2.iso
/
c
/
maek.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-03-04
|
18KB
|
609 lines
/* make - do minimum work to update a file */
#include <stdio.h>
/*---------------------------------------------------------------------------*/
#ifdef NEVER
#define DEBUG 1 /* include for debug diagnostics in make() */
#endif
#define MAXLINE (80*10) /* maximum input line length */
#define MAXBLOCK 64 /* max number of lines in an action */
#define MAXDEP 32 /* max number of dependancies */
#define COMMENT '#' /* delimits a comment */
#define MAKEFILE "mkfile" /* name of makefile */
#define OPEN 0x3d /* dos function call to open a file */
#define CLOSE 0x3e /* dos function call to close a file */
#define DATETIME 0x57 /* " to get/set file's date & time */
#define DEFTIME 0x0 /* the default time returned by
gtime when a file doesn't exist */
/*---------------------------------------------------------------------------
iswhite(c) evaluates true if c is white space.
skipwhite(s) skips the character pointer s past any white space
skipnonwhite(s) skips s past any non-white characters. */
#define iswhite(c) ((c)==' '||(c)=='\t')
#define skipwhite(s) while(iswhite(*s) ) ++s;
#define skipnonwhite(s) while(*s&& !iswhite(*s) ) ++s;
/*---------------------------------------------------------------------------
The entire makefile is read into memory before it's processed.
It's stored in a binary tree composed of the following structures:
depends_on and do_this are argv-like arrays of pointers to
character pointers. The arrays are null terminated so no count is
required. The time field is a 32 bit long consisting of the date
and time fields returned from a DOS 0x57 call. The date and time
are concataneted with the date in the most significant 16 bits and
the time in the least significant. This way they can be compared
as a single number.
*/
typedef struct _tn
{struct _tn *lnode; /* pointer to left sub-tree */
struct _tn *rnode; /* pointer to right sub-tree */
char *being_made; /* name of file being made */
char **depends_on; /* names of dependant files */
char **do_this; /* Actions to be done to make file */
long time; /* time & date last modified */
} TNODE;
/*---------------------------------------------------------------------------*/
static TNODE *Root =0 ; /* root of file-name tree */
static FILE *Makefile ; /* pointer to opened makefile */
static int Inputline =0 ; /* current input line number */
static char *First ="" ; /* default file to make */
extern char *malloc() ; /* from standard library */
extern char **getblock() ; /* declared in this module */
extern char *getline() ; /* declared in this module */
extern TNODE *find() ; /* declared in this module */
/*---------------------------------------------------------------------------*/
char *gmem( numbytes )
{
/* Get numbytes from malloc. Print an error message and abort if
malloc fails, otherwise return a pointer to the memory. */
extern char *calloc();
char *p;
if( !( p=calloc(1,numbytes) ))
err("Out of memory");
return p;
}
/*---------------------------------------------------------------------------*/
char **stov(str, maxvect )
char *str;
{
/* "Str" is a string of words separated from each other by white
space. Stov returns an argv-like array of pointers to
character pointers, one to each word in the original string.
The white-space in the original string is replaced with nulls.
The array of pointers is null-terminated. "Maxvect" is the
number of vectors in the returned array. The program is
aborted if it can't get memory.
*/
char **vect, **vp;
vp = vect = (char **) gmem( (maxvect + 1) * sizeof(str) );
while( *str && --maxvect >= 0 )
{skipwhite(str);
*vp++ = str;
skipnonwhite(str);
if( *str ) *str++=0;
}
*vp=0;
return (vect);
}
/*---------------------------------------------------------------------------*/
long gtime( file ) char *file;
{
/* Return the time and date for a file.
The DOS time and date are concatenated to form one large
number. Note that the high bit of this number will be set to 1
for all dates after 2043, which will cause the date comparisons
done in make() to fail. THIS ROUTINE IS NOT PORTABLE (because
it assumes a 32 bit long).
*/
extern unsigned _rax,_rbx,_rcx,_rdx,_rsi,_rdi,_res,_rds,_doint();
extern char _carryf,_zerof;
short handle = 0 ; /* place to remember file handle */
long time ;
_rds=-1;
_rax=(OPEN<<8)|0; /* open the file */
_rdx=(short) file;
_doint(0x21);
if(_carryf) return DEFTIME; /* file doesn't exist */
handle=_rbx=_rax;
_rax=(DATETIME<<8)|0; /* get the time */
_doint(0x21);
if(_carryf) err("DOS returned error from date/time request");
time=(((long)(_rdx))<<16) | ((long)(_rcx)&0xffffL);
_rax=CLOSE<<8; /* close the file */
_doint(0x21);
if(_carryf) err("DOS returned error from file close request");
return time;
}
/*--------------------------------------------------------------------------*/
TNODE *makenode()
{
/* Create a TNODE, filling it from the makefile and returning a
pointer to it. Return NULL if there are no more objects in the
makefile. */
char *line, *lp;
TNODE *nodep;
unsigned date, time;
/* First, skip past any blank lines or comment lines.
Return NULL if we reach end of file. */
do
{if( (line=getline(MAXLINE,Makefile)) == NULL) return (NULL);
} while ( *line == 0 || *line == COMMENT );
/* At this point we've gotten what should be the dependancy
line. Position lp to point at the colon. */
for( lp = line; *lp && *lp != ':' ; lp++) {}
/* If we find the colon position, adjust lp to point at the first
non-white character following the colon. */
if( *lp != ':' ) {err( "missing ':'" );} /* This will abort the program */
else for( *lp++ = 0; iswhite(*lp) ; lp++) {}
/* Allocate and initialize the TNODE */
nodep = (TNODE *) gmem( sizeof(TNODE) );
nodep->being_made = line;
nodep->time = gtime( line );
nodep->depends_on = stov( lp, MAXDEP );
nodep->do_this = getblock( Makefile );
#ifdef DEBUG
printf("making...\n"); pnode(nodep); getchar();
#endif
return (nodep);
}
/*--------------------------------------------------------------------------*/
dependancies()
{
/* Manufacture the binary tree of objects to make. First is a
pointer to the first target file listed in the makefile (i.e.
the one to make if one isn't explicitly given on the command
line. Root is the tree's root pointer. */
TNODE *node;
if( node = makenode() )
{First = node->being_made ;
if( !tree(node, &Root) )
err("Can't insert first node into tree !!!\n");
while( node = makenode() )
if( !tree( node, &Root ) )
free( node );
return 1;
}
return 0;
}
/*---------------------------------------------------------------------------*/
char *getline( maxline, fp )
FILE *fp;
{
/* Get a line from the stream pointed to by fp. "Maxline" is the
maximum input line size (including the terminating null). A \
at the end of line is recognized as a line continuation, (the
lines are concatenated). Buffer space is gotten from malloc.
If a line is longer than maxline, it is truncated (i.e. all
characters from the maxlineth until a \n or EOF is encountered
are discarded. */
static char *buf ;
register char *bp ;
register int c, lastc;
/* Two buffers are used. Here, we are getting a worst-case buffer
that will hold the longest possible line. Later on we'll copy
the string into a buffer that's the correct size. */
if( !(bp = buf = malloc(maxline)) )
return NULL;
while(1)
{/* Get the line from fp. Terminate after maxline characters
and ignore \n following a \ */
Inputline++; /* update input line number */
for( lastc=0; (c = filter(fp)) != EOF && c!='\n'; lastc = c)
if( --maxline > 0)
*bp++ = c;
if( !( c == '\n' && lastc == '\\' ) )
break;
else if( maxline > 0) /* erase the \ */
--bp;
}
*bp=0;
if( (c == EOF && bp == buf) || !(bp = malloc((bp-buf)+1)) )
{
/* If EOF was the first character on the line or
malloc fails when we try to get a buffer, quit. */
free(buf);
return (NULL);
}
strcpy ( bp, buf ); /* Copy the worst-case buffer to the one
that is the correct size and... */
free ( buf ); /* free the original, worst-case buffer, */
#ifdef DEBUG
/* printf("line %d: \n",Inputline); */
#endif
return ( bp ); /* returning a pointer to the copy. */
}
/* With DeSmet C, the program hiccups unless CRs are filtered out */
filter(fp) FILE *fp;
{ int c;
while((c=fgetc(fp))=='\015') {}
return c;
}
/*---------------------------------------------------------------------------*/
char **getblock( fp )
FILE *fp;
{
/* Get a block from standard input. A block is a sequence of lines
terminated by a blank line. The block is returned as an array
of pointers to strings. At most MAXBLOCK lines can be in a
block. Leading white space is stripped. */
char *p, *lines[MAXBLOCK], **blockv=lines ;
int blockc = 0;
do {if( !( p = getline(MAXLINE,Makefile) ))
break;
skipwhite(p);
if( ++blockc <= MAXBLOCK )
*blockv++ = p;
else
err("action too long (max = %d lines)", MAXBLOCK);
} while ( *p );
/* Copy the blockv array into a safe place. Since the array
returned by getblock is NULL terminated, we need to increment
blockc first. */
blockv = (char **) gmem( (blockc + 1) * sizeof(blockv[0]) );
movmem( lines, blockv, blockc * sizeof(blockv[0]) );
blockv[blockc]=NULL;
return blockv;
}
/* move n bytes from s to t */
movmem(s,t,n) char *s,*t; int n;
{ while(n--) *t++=*s++;
}
/*---------------------------------------------------------------------------*/
err( msg, param)
char *msg;
{
/* Print the error message and exit the program. */
fprintf(stderr,"Mk (%s line %d): ",MAKEFILE, Inputline );
fprintf(stderr, msg, param );
exit(1);
}
serr( msg, param )
char *msg, *param;
{
/* Same as err() except the parameter is a string pointer
instead of an int. */
fprintf(stderr,"Mk (%s line %d): ", MAKEFILE, Inputline );
fprintf(stderr, msg, param );
exit(1);
}
/*---------------------------------------------------------------------------*/
make(what)
char *what;
{
/* Actually do the make. The dependancy tree is descended
recursively and if required, the dependancies are adjusted.
Return 1 if anything was done, 0 otherwise. */
TNODE *snode ; /* source file node pointer */
TNODE *dnode ; /* dependant file node pointer */
int doaction = 0 ; /* if true do the action */
static char *zero = (char *) 0 ;
char **linev = &zero ;
#ifdef DEBUG
static int recurlev = 0 ; /* recursion level */
printf("make (lev %d): making <%s>\n",recurlev, what);
#endif
if( !(snode = find(what, Root)) )
serr("Don't know how to make source <%s>\n", what );
if( !*(linev = snode->depends_on)) /* If no dependancies */
doaction++; /* always do the action */
for( ; *linev ; linev++ ) /* Process each dependancy */
{
#ifdef DEBUG
recurlev++;
#endif
make( *linev );
#ifdef DEBUG
recurlev--;
#endif
if( !(dnode = find(*linev, Root)) )
serr("Don't know how to make dependent <%s>\n", *linev );
#ifdef DEBUG
printf("make (lev %d): source file ",recurlev);
ptime( what, snode->time);
printf("make (lev %d): dependant file ",recurlev);
ptime( *linev, dnode->time );
#endif
if( snode->time <= dnode->time )
{
/* If source node is older than (time is less than)
dependant node, do something. If the times are
equal, assume that neither file exists but that
the action will create them, and do the action */
#ifdef DEBUG
printf("make (lev %d): %s older than %s\n",
recurlev, what, *linev );
#endif
doaction++;
}
#ifdef DEBUG
else printf("make (lev %d): %s younger than %s\n",
recurlev, what, *linev);
#endif
}
if (doaction )
{
#ifdef DEBUG
printf("make (lev %d): doing action:\n",
recurlev, *linev, what);
#endif
for( linev = snode->do_this; *linev; linev++ )
{printf("%s\n", *linev); /* echo action to screen */
if( system(*linev) )
serr("Can't process <%s>\n", *linev );
/* Change the source file's time to
reflect any modification */
snode->time = gtime( snode->being_made );
}
}
#ifdef DEBUG
printf("make (lev %d): exiting\n", recurlev );
#endif
return doaction;
}
/* system() isn't supplied in the DeSmet library.
This implementation by J. R. Van Zandt */
static char *(extension[])={".COM",".EXE",".BAT"};
system(s) char *s;
{ int i;
char program[40],directory[30],path[200];
char *tail,*dp,*pp;
FILE file;
tail=s;
skipnonwhite(tail);
if(*tail)
{*tail++=0;
envsearch("path",path);
pp=path;
dp=directory;
while(1)
{*dp=0;
/* We don't permit i=2 because exec() can't execute a .BAT file */
for (i=0; i<2; i++)
{program[0]=0;
if(directory[0])
{strcat(program,directory);
strcat(program,"\\");
}
strcat(program,s);
strcat(program,extension[i]);
if(file=fopen(program,"r")) break;
}
if(file)break;
if( !(*pp) ) return (-1);
dp=directory;
while(*pp && *pp!=';') *dp++=*pp++; /* copy next directory in path */
if(*pp) pp++;
}
fclose(file);
return exec(program,tail);
}
else return 0;
}
/* envsearch - search environment for given string
usage...
char buf[25];
envsearch("ALPHA",buf); puts value of the environment
variable ALPHA into buf
*/
envsearch(target,value) char *target,*value;
{ char buf[100],*s,t[25],*env;
int nt,offset;
s=t;
while(*target) *s++=toupper(*target++);
*s++= '='; *s=0;
nt = strlen(t);
offset=0;
/* DeSmet C sets up cs register to point 100H past the Program Segment
Prefix. The word at offset 44 in the PSP points to the segment with
the environment */
_lmove(2,44,_showcs()-0x10,&env,_showds()); /* get env. pointer */
while(1)
{_lmove(100,offset,env,buf,_showds()); /* get (part of) env. */
s=buf;
if(*s)
{/* printf("examining entry: %s \n",s); getchar(); */
if (strncmp(t,s,nt)==0) return (strcpy(value,s+nt));
}
else
{*value=0; /* no value found */
return;
}
offset+=strlen(buf)+1;
}
}
/*--------------------------------------------------------------------------*/
/* Tree routines: */
TNODE *find( key, root )
char *key;
TNODE *root;
{
/* If key is in the tree pointed to by root, return
a pointer to it, else return 0. */
register int notequal ;
register TNODE *rval ;
if( !root )
return 0;
if( !(notequal = strcmp(root->being_made,key)) )
return( root );
return( find( key, (notequal > 0) ? root->lnode : root->rnode) );
}
/*---------------------------------------------------------------------------*/
tree( node, rootp )
TNODE *node, **rootp ;
{
/* If node's key is in the tree pointed to by rootp, return 0
else put it into the tree and return 1. */
register int notequal ;
register TNODE *rval ;
if( *rootp == NULL )
{*rootp = node;
return 1;
}
if( !(notequal = strcmp( (*rootp)->being_made, node->being_made)))
return 0;
return( tree( node, notequal > 0 ? &(*rootp)->lnode
: &(*rootp)->rnode ) );
}
/*--------------------------------------------------------------------------*/
main( argc, argv )
int argc;
char **argv;
{ /* A stupid version of the Unix make facility */
if( !(Makefile = fopen(MAKEFILE, "r")) )
err("can't open %s\n", MAKEFILE );
if( !dependancies() )
err("Nothing to make");
else
make( argc > 1 ? argv[1] : First );
}
#ifdef DEBUG
/*--------------------------------------------------------------------------*/
/* Misc. debugging routines */
ptime( file, t )
char *file;
long t;
{
/* Print out the time and date field of a TNODE as
"mm-dd-yy hh:mm:ss"
File is the file name. */
int date, time;
date = (t >> 16) & 0xffffL ;
time = t & 0xffffL ;
printf("%s: file: ",file);
printf("%02d-%02d-%02d, ", (date >> 5 ) & 0x0f, date & 0x1f,
80 + ((date >> 9) & 0x7f) );
printf ("%02d:%02d:%02d, ", (time >> 11) & 0x1f, (time >> 5) & 0x3f,
(time << 1) & 0x3d );
printf("\n");
}
/*--------------------------------------------------------------------------*/
pnode( node )
TNODE *node;
{
/* Print out the tree node pointed to by "node" */
char **linev;
printf("+-----------------------------\n" );
printf("| node at 0x%x\n", node );
printf("+-----------------------------\n" );
printf("| lnode = 0x%x, rnode = 0x%x\n" ,node->lnode,node->rnode);
printf("| time = 0x%lx =\n" , node->time );
ptime( "", node->time );
printf("| target = <%s>\n" , node->being_made );
printf("| dependancies:\n" );
for( linev = node->depends_on; *linev; printf("|\t<%s>\n", *linev++)) {}
printf("| actions:\n" );
for( linev = node->do_this; *linev; printf("|\t<%s>\n", *linev++)) {}
printf("+-----------------------------\n" );
}
/*--------------------------------------------------------------------------*/
trav( root )
TNODE *root;
{
/* Do an in-order traversal of the tree, printing the node's
contents as you go */
if( root == NULL )
return;
trav( root->lnode );
pnode( root );
trav( root->rnode );
}
#endif