/* make - do minimum work to update a file */ #include /*---------------------------------------------------------------------------*/ #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