home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Simtel MSDOS 1992 September
/
Simtel20_Sept92.cdr
/
msdos
/
ddjmag
/
ddj8911.arc
/
STREICH.LST
< prev
Wrap
File List
|
1989-10-04
|
49KB
|
1,540 lines
_A PARALLEL MAKE WITH DESQVIEW_
by Mark Steich
[LISTING ONE]
/*---------------------------------------------------------------------
* DVMAKE, adapted by Mark Streich
* Original Mk by Allen Holub, Doctor Dobb's Journal, August 1985
*
* Some functions may be specific to Borland Int'l. Turbo C 2.0.
* DESQview 2.01 (or later), and API C Library required.
*
* Compile with byte alignment ON.
*
* Run from within a non-swappable DOS window with at least 64K
* of memory, 15K of system memory, runs in the background and
* shares the cpu when in the foreground. On non-386 systems,
* you will have to say that it does not write directly to the screen.
*/
#include <stdio.h> /* for printf(), sprintf(), etc. */
#include <io.h> /* for open(), close(), getftime() */
#include <fcntl.h> /* for O_RDONLY used in open() */
#include <string.h> /* for various string functions */
#include <stdarg.h> /* for variable argument err() */
#include "dvapi.h" /* provided by DESQview API */
/*----------------------------------------------- DEFINES -----------------*/
#define MAXLINE 127 /* Maximum DOS command line length */
#define MAXBLOCK 16 /* Max number of lines in an action */
#define MAXDEP 32 /* Max number of dependencies */
#define MAXFNM 64 /* Max length of file name/dir */
#define COMMENT '#' /* Delimits a comment */
#define MAKEFILE "mkfile" /* Name of makefile */
#define OLDTIME 0x0 /* the Beginning of Time (very old) */
#define NEWTIME 0xFFFFFFFFL /* the End of Time (very young) */
#define DV_VER 0x201 /* DESQview version required: 2.01 */
#define STKSIZE 512 /* size of tasks' local stack */
#define NORMAL 0 /* normal status state */
#define ACCESSDV 1 /* access DESQview, so no new tasks */
#define ABORT 2 /* kill of all processes */
/*----------------------------------------------------------------------
* 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.
* waitwhile(event) gives up task's time slice while event is true.
*/
#define iswhite(c) ((c)==' ' || (c)=='\t')
#define skipwhite(s) while( iswhite(*s) ) ++s;
#define skipnonwhite(s) while( *s && !iswhite(*s) ) ++s;
#define waitwhile(event) while( event ) api_pause();
/*----------------------------------------------- TYPEDEFS ----------------
* The entire mkfile 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 ulong consisting of the date and time
* fields returned from DOS. The date and time are concatanated with
* the date in the most significant bits and the time in the least
* significant. This way they can be compared as a single number.
*/
typedef struct _tn /* node for dependencies */
{
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 dependent files */
char **do_this; /* Actions to be done to make file */
ulong time; /* time & date last modified */
ulong apphan; /* what app is making this item */
char made; /* flag indicating made or not */
int tsknum; /* what task number was assigned */
}
TNODE;
typedef struct _qn /* queue of items */
{
void *item; /* item in queue */
struct _qn *next; /* next item in the queue */
}
QNODE;
typedef struct /* definition of Program Information File (PIF) */
{
char reserved1[2];
char prog_title[30]; /* blank filled */
uint maxmem; /* max memory size in k-bytes */
uint minmem;
char program[64]; /* command to start program, 0-terminated */
char def_drive; /* 'A', 'B', ..., or blank */
char def_dir[64]; /* default directory, 0-terminated */
char params[64]; /* parameters, 0-terminated */
byte init_screen; /* screen mode (0-7) */
byte text_pages; /* # of text pages used */
byte first_intr; /* # of first interrupt vector to save */
byte last_intr; /* # of last interrupt */
byte logical_rows; /* logical size of window buffer */
byte logical_cols;
byte init_row; /* initial row to display window */
byte init_col;
uint system_mem; /* system memory in k-bytes */
char shared_prog[64]; /* shared program file name, 0-terminated */
char shared_data[64]; /* shared program data, 0-terminated */
byte control_byte1; /* control byte 1, encoded as follows:
80H - writes direct to screen
40H - foreground only
20H - uses math coprocessor
10H - accesses system keyboard buffer
01H - swappable */
byte control_byte2; /* control byte 2, encoded as follows:
40H - uses command line parameters
20H - swaps interrupt vectors */
char open_keys[2]; /* keys to use for Open Window menu */
uint script_size; /* size of script buffer in bytes */
uint auto_pause; /* pause after this many tests for input
during one clock tick (normally 0) */
byte color_mapping; /* non-zero to disable auto color mapping */
byte swappable; /* non-zero if application is swappable */
char reserved2[3]; /* should be zero */
byte auto_close; /* non-zero to automatically close on exit */
byte disk_reqd; /* non-zero if diskette required */
byte reserved3; /* MUST HAVE VALUE OF 1 */
byte shared_mem; /* non-zero if program uses shared system mem
*/
byte physical_rows; /* initial size of physical window */
byte physical_cols;
uint max_expanded_mem; /* max amount of expanded mem avail to app */
byte control_byte3; /* control byte 3, encoded as follows:
80H - automatically assigns position
20H - honor maximum memory value
10H - disallow Close command
08H - foreground-only when in graphics
04H - don't virtualize */
byte key_conflict; /* keyboard conflict (0-4, usually 0) */
byte graphics_pages; /* # graphics pages used */
uint system_mem2; /* system memory - overrides system_mem */
byte init_mode; /* initial screen mode, normally 0FFH */
char reserved4[22];
} PIF; /* note that the sizeof(PIF) MUST be 416, or else we've made a typo */???
/*----------------------------------------------- GLOBAL VARIABLES --------*/
static TNODE *Root = NULL; /* Root of file-name tree */
static FILE *Makefile; /* Pointer to opened makefile */
static int Inputline = 1; /* current input line number */
static char *First = NULL; /* Default file to make */
static char ShowWin = 0; /* Display tasks? */
static char Parallel = 0; /* Are we multitasking yet? */
static char Status = NORMAL; /* processing status */
static char CurDir[MAXFNM]; /* Directory called from */
static char Error[MAXLINE] = "";/* Error saved for later printing */
static int RunCnt = 0; /* how many tasks running */
static int MemSize = 256; /* default task memory size */
static int ReDirLen = 0; /* length of redirection file */
static QNODE *MkQueue = NULL; /* queue of items to make */
static QNODE *TskQueue = NULL; /* queue of tasks to run */
static QNODE *OutQueue = NULL; /* queue of files to output */
static ulong TskQueueLock; /* semaphore for TskQueue */
static ulong OutQueueLock; /* semaphore for OutQueue */
static ulong AllocLock; /* semaphore for malloc/free */
static ulong MainWin; /* handle of Main Window */
static ulong MenuTsk; /* handle of Menu Task */
static ulong MakeTsk; /* handle of Make Task */
static PIF Pif; /* default Prog Info File */
static int Lpif; /* length of the PIF */
/*----------------------------------------------- ERROR ROUTINES ----------*/
void err( char *msg, ... )
{
/* print the message and optional parameter and either
* stop immediately if we haven't started up parallel tasks,
* or just set our status to ABORT and stop later
*/
static char temp[MAXLINE];
va_list argptr;
/* Print the error message, if we haven't already, and abort */
if (!strlen(Error))
{
/* print the location of the error in the mkfile */
sprintf(Error,"Mk (%s line %d): ", MAKEFILE, Inputline );
va_start(argptr,msg);
vsprintf(temp,msg,argptr); /* print the error message */
va_end(argptr);
strcat(Error,temp); /* and concatenate the two */
}
if (Parallel) /* are we multitasking? */
{
/* notify everyone that there are problems, but don't stop yet */
api_beginc();
Status = ABORT;
api_endc();
}
else /* not multitasking, so we can STOP */
{
fprintf(stderr,"%s",Error); /* print the error message */
mal_free(TskQueueLock); /* get rid of our semaphores */
mal_free(OutQueueLock);
mal_free(AllocLock);
api_exit(); /* tell DESQview we're done */
exit(1); /* and STOP */
}
}
/*----------------------------------------------- MEMORY ROUTINES ---------*/
void *gmem( int numbytes )
{
/* Get numbytes from malloc. Print an error message and
* abort if malloc fails, otherwise return a pointer to
* the memory. Uses semaphores because malloc() not re-entrant.
*/
void *p;
extern void *malloc();
mal_lock(AllocLock); /* get access to heap */
p = malloc(numbytes); /* grab some memory */
mal_unlock(AllocLock); /* free access to heap */
if (p == NULL) /* were we successful */
err("Out of memory");
return( p );
}
void fmem( void *ptr )
{
/* Frees memory pointed to by ptr. */
extern void free();
mal_lock(AllocLock); /* get access to heap */
if (ptr) free(ptr); /* free the memory if ptr not NULL */
mal_unlock(AllocLock); /* free access to heap */
}
/*----------------------------------------------- QUEUE ROUTINES ----------*/
void enqueue(QNODE **queue,void *item) /* add item to the queue */
{
QNODE *qptr;
/* get memory for new node */
if ( (qptr = (QNODE *) gmem(sizeof(QNODE))) == NULL )
err("Out of memory");
else
{
qptr->item = item; /* add the item */
qptr->next = *queue; /* point to the next item in the queue, if any
*/
*queue = qptr; /* point to the new front of the queue */
}
}
void *dequeue(QNODE **queue) /* return an item from the queue */
{
QNODE *qptr,*qptr2 = NULL;
void *iptr;
if (*queue == NULL) /* is the queue empty? */
return( NULL );
else
{
/* find the end of the queue */
for (qptr = *queue; qptr->next != NULL; qptr = qptr->next)
qptr2 = qptr; /* qptr2 points to qptr's predecessor */
iptr = qptr->item; /* get the item to return */
fmem( qptr ); /* remove the last item from the queue */
if (qptr2 != NULL)
qptr2->next = NULL;
else
*queue = NULL; /* nothing left in queue */
return( iptr ); /* return a pointer to the item removed */
}
}
int inMkQueue(char *being_made)
{
/* see if "being_made" item is already in the MkQueue */
QNODE *qptr;
if (MkQueue != NULL) /* is the queue empty? */
for (qptr = MkQueue; qptr != NULL; qptr = qptr->next)
if (strcmp(((TNODE *)qptr->item)->being_made,being_made) == 0)
return(1); /* being_made is in the queue */
return( 0 ); /* MkQueue is empty or being_made not in it */
}
/*----------------------------------------------- INITIALIZE PIF ----------*/
void init_pif(PIF *pif,int *lenpif,char *title,int rows,int cols)
/* Initialize the Program Information File to start the new application.
* By default it is just a non-swappable dos window with 256K.
*/
{
*lenpif = sizeof(PIF);
/* set defaults now, and particulars later */
memset(pif->reserved1,0,2);
memset(pif->prog_title,' ',30); /* blank filled */
strncpy(pif->prog_title,title,strlen(title));
pif->maxmem = MemSize; /* memory required for app in k-bytes */
pif->minmem = MemSize;
strcpy(pif->program,""); /* command to start program, 0-terminated
*/
pif->def_drive = CurDir[0]; /* ' ', 'A', 'B', ... */
strcpy(pif->def_dir,CurDir+2); /* default directory, 0-terminated */
strcpy(pif->params,""); /* parameters, 0-terminated */
pif->init_screen = 0x7F; /* screen mode (0-7) (??) */
pif->text_pages = 1; /* # of text pages used */
pif->first_intr = 0; /* # of first interrupt vector to save */
pif->last_intr = 255; /* # of last interrupt */
pif->logical_rows = rows; /* logical size of window buffer */
pif->logical_cols = cols;
pif->init_row = 0; /* initial row to display window */
pif->init_col = 0;
pif->system_mem = 0; /* system memory in k-bytes */
pif->shared_prog[0] = 0; /* shared program file name, 0-terminated
*/
pif->shared_data[0] = 0; /* shared program data, 0-terminated */
pif->control_byte1 = 0x20; /* control byte 1, encoded as follows:
80H - writes direct to screen
40H - foreground onlay
20H - uses math coprocessor
10H - accesses system keyboard buffer
01H - swappable */
pif->control_byte2 = 0x40|0x20; /* control byte 2, encoded as follows:
40H - uses command line parameters
20H - swaps interrupt vectors */
memset(pif->open_keys,' ',2); /* keys to use for Open Window menu */
pif->script_size = 256; /* size of script buffer in bytes */
pif->auto_pause = 0; /* pause after this many tests for input
during one clock tick (normally 0) */
pif->color_mapping = 0; /* non-zero to disable color mapping */
pif->swappable = 0; /* non-zero if application is swappable */
memset(pif->reserved2,0,3); /* should be zero */
pif->auto_close = 0; /* non-zero to close on program exit */
pif->disk_reqd = 0; /* non-zero if diskette required */
pif->reserved3 = 1; /* MUST HAVE VALUE OF 1 */
pif->shared_mem = 0; /* non-zero if prog uses shared memory */
pif->physical_rows = 0; /* initial size of physical window */
pif->physical_cols = 0; /* 0's allow DV to set */
pif->max_expanded_mem = 65535u; /* max amt of expanded mem avail to app */
pif->control_byte3 = 0x80|0x10; /* control byte 3, encoded as follows:
80H - automatically assigns position
20H - honor maximum memory value
10H - disallow Close command
08H - foreground-only when in
graphics
04H - don't virtualize */
pif->key_conflict = 0; /* keyboard conflict (0-4, usually 0) */
pif->graphics_pages = 0; /* # graphics pages used */
pif->system_mem2 = 0; /* system memory - overrides system_mem */
pif->init_mode = 0xFF; /* initial screen mode, normally 0FFH */
memset(pif->reserved4,0,22);
}
/*----------------------------------------------- GENERATE TEMP FILE NAME --*/
char *gen_name(int tsknum,int cmdnum)
{
/* Generate a new output file name, d:\dir\DVMKxxyy.$$$, where
d:\dir\ is the directory in which the "dvmake" was started,
xx = task number, and yy = command number (both in hex).
Returns a pointer to the newly allocated file name.
*/
char *new_name; /* generated name */
char tsk_cmd[5]; /* task/command number string */
if ( (new_name = (char *) gmem(MAXFNM)) == NULL )
err("Out of memory");
else
{
strcpy(new_name,CurDir); /* directory name */
if (CurDir[strlen(CurDir)-1] != '\\')
strcat(new_name,"\\"); /* add backslash to dir */
strcat(new_name,"DVMK"); /* first 4 chars of name */
sprintf(tsk_cmd,"%02x%02x",tsknum,cmdnum);
strcat(new_name,tsk_cmd); /* last 4 chars of name */
strcat(new_name,".$$$"); /* add an extension */
}
return( new_name );
}
/*----------------------------------------------- MENU TASK ---------------*/
int dvmenu( void )
{
/* display a menu to control the status of the make, and don't
* quit until someone wakes me up with tsk_post(MenuTsk)
*/
ulong kbd,win; /* handles for keyboard, window */
ulong whichobj; /* handle of object that has input */
char *kbuf; /* message buffer */
int klen, /* message length */
state; /* state of selected item */
/* this string defines the contents of the menu */
static char mkmenu[] = "\
Access DV A \
Quit Q ";
static char mkmenutbl[] =
{
ftab(2,FTH_KEYSELECT+FTH_MODIFIED+FTH_AUTORESET,0,0,9,2),
0,0,0,13,FTE_SELECT,'A',1,0,
1,0,1,13,FTE_SELECT,'Q',1,0,
};
win = win_new("DVMAKE",6,2,14); /* get a new window */
win_logattr(win,1); /* and set its logical attributes */
win_attr(win,1);
win_disallow(win,ALW_HSIZE); /* do not allow resizing menu */
win_disallow(win,ALW_VSIZE);
win_swrite(win,mkmenu); /* write the contents and */
win_stream(win,mkmenutbl); /* field table to the menu window */
fld_marker(win,175); /* set the selected field marker */
kbd = key_new(); /* get a keyboard for the menu */
key_open(kbd,win);
key_addto(kbd,KBF_FIELD); /* and put it into field mode */
/* put and display the menu in the top right corner of the main window */
win_poswin(win,MainWin,PSW_LEFT,0,PSW_RIGHT,0,0);
win_unhide(win);
win_top (win); /* make sure it's the one on top */
/* go until someone wakes me up with tsk_post(MenuTsk) */
for (whichobj = 0; whichobj != tsk_me(); )
{
/* wait for something to show up in our object queue */
if ((whichobj = obq_read()) == kbd)
{
key_read(kbd,&kbuf,&klen);/* see what field was selected */
state = qry_type(win,(int) *kbuf); /* is it ON or OFF */
if ((int) *kbuf == 1 && /* was "Access DV" toggled? */
Status != ABORT)
{
api_beginc(); /* make sure err() hasn't aborted */
if (Status != ABORT) /* in the interim */
Status = (state == FLT_SELECT ? ACCESSDV : NORMAL);
api_endc();
}
else /* selected "Quit" */
{
Status = ABORT;
fld_reset(win); /* show only Quit as selected */
fld_type(win,2,FLT_SELECT);
}
}
}
/* get rid of menu window, keyboard */
key_free(kbd);
win_free(win);
}
/*----------------------------------------------- GET TIME ROUTINE --------*/
ulong gtime( char *file )
{
/* Return the time and date for file, or if the file
* does not exist, assume it is very old
*
* The DOS time and date are concatanated to form one
* large number.
* THIS ROUTINE IS NOT PORTABLE (because it assumes a 32
* bit ulong to provide for the time functions).
*/
short handle; /* Place to remember file handle */
struct ftime time; /* date/time structure */
ulong utime = 0; /* use to convert time to ulong */
char xtern *searchpath(); /* search PATH for the file, */
/* defined in TURBO C's dir.h */
if ((handle = open(searchpath(file),O_RDONLY)) == -1)
{
/* File doesn't exist. Return a very old date & time */
return( OLDTIME );
}
else
{
/* File exists, so get the time */
if ( getftime(handle,&time) )
err("DOS returned error from date/time request");
if ( close(handle) )
err("DOS returned error from file close request");
/* pack the time into an unsigned long for comparisons */
utime |= (ulong) time.ft_year << 25;
utime |= (ulong) time.ft_month << 21;
utime |= (ulong) time.ft_day << 16;
utime |= (ulong) time.ft_hour << 11;
utime |= (ulong) time.ft_min << 5;
utime |= (ulong) time.ft_tsec;
return( utime );
}
}
/*----------------------------------------------- CHAR STORAGE ------------*/
char **stov( char *str, int maxvect )
{
/* "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 );
}
/*----------------------------------------------------------------------*/
char *getline( int maxline, 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 concatanated). Buffer space is gotten from gmem().
* 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.
*
* Returns: NULL on a malloc failure or end of file.
* A pointer to the malloced buffer on success.
*/
static char *buf;
char *bp;
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 = (char *) gmem(maxline)) == NULL )
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 = fgetc(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 = (char *) gmem((int) (bp-buf)+1)) == NULL )
{
/* If EOF was the first character on the line or
* malloc fails when we try to get a buffer, quit/
*/
fmem(buf);
return( NULL );
}
strcpy( bp, buf ); /* Copy the worst-case buffer to the one */
/* that is the correct size and ... */
fmem( buf ); /* free the original, worst-case buffer, */
return( bp ); /* returning a pointer to the copy. */
}
/*----------------------------------------------------------------------*/
char **getblock( 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,fp)) == NULL)
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 );
}
/*----------------------------------------------------------------------*/
TNODE *makenode( void )
{
/* Create a TNODE, filling it from the mkfile, and return a
* pointer to it. Return NULL if there are no more objects
* in the makefile.
*/
char *line, *lp;
TNODE *nodep;
/* 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 dependency
* line. Position lp to point at the colon.
*/
for ( lp = line; *lp && *lp != ':'; lp++ )
;
/* If we find the colon position, 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->lnode = NULL;
nodep->rnode = NULL;
nodep->being_made = line;
nodep->time = gtime( line );
nodep->depends_on = stov( lp, MAXDEP );
nodep->do_this = getblock( Makefile );
nodep->made = 1; /* assume has already been made, but later change
*/
nodep->apphan = 0;
nodep->tsknum = 0;
return( nodep );
}
/*----------------------------------------------- TREE ROUTINES -----------*/
TNODE *find( char *key, TNODE *root )
{
/* If key is in the tree pointed to by root, return a pointer
* to it, else return 0.
*/
int notequal;
if ( !root )
return( 0 );
if ( (notequal = strcmp(root->being_made,key)) == 0 )
return( root );
return( find( key, (notequal > 0) ? root->lnode : root->rnode) );
}
/*----------------------------------------------------------------------*/
int tree( TNODE *node, TNODE **rootp )
{
/* If node's key is in the tree pointed to by rootp, return 0
* else put it into the tree and return 1.
*/
int notequal;
if ( *rootp == NULL )
{
*rootp = node;
return( 1 );
}
if ( (notequal = strcmp( (*rootp)->being_made, node->being_made)) == 0 )
return( 0 );
return( tree( node, notequal > 0 ? &(*rootp)->lnode : &(*rootp)->rnode)
);
}
/*----------------------------------------------------------------------*/
int dependencies( void )
{
/* Manufacture the binary tree of objects to make. First
* is a pointer to the first target file listed in the
* makefile (ie. 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()) != NULL )
{
/* has First been assigned a value yet? */
if (First == NULL)
First = node->being_made;
if ( !tree(node, &Root) )
err("Can't insert first node into tree !!!\n");
while ( (node = makenode()) != NULL )
if ( !tree( node, &Root ) )
fmem( node );
return( 1 );
}
return( 0 );
}
/*----------------------------------------------- CREATE MAKE QUEUE -------*/
void make_queue( char *what )
{
/* Simulate a sequential make, building up a queue of items to make.
* The dependency tree is descended recursively.
*/
TNODE *snode; /* Source file node pointer */
TNODE *dnode; /* dependent file node pointer */
int doaction = 0; /* If true do the action */
static char *zero = (char *)0;
char **linev = &zero;
if ( (snode = find(what, Root)) == NULL )
err("Don't know how to make <%s>\n", what );
if ( !*(linev = snode->depends_on)) /* If no dependencies */
++doaction; /* always do the action */
for ( ; *linev; linev++ ) /* Process each dependency */
{
make_queue( *linev );
if ( (dnode = find(*linev, Root)) == NULL )
err("Don't know how to make <%s>\n", *linev );
if ( snode->time <= dnode->time )
{
/* If dependent node is more recent (time is greater)
* than the source node, do something. If the times
* are equal, assume that neither file exists but that
* the action will create them, and do the action.
*/
++doaction;
}
}
if ( doaction ) /* are we going to do anything */
{
/* are there any commands, and is this node not in MkQueue */
if ( snode->do_this && *snode->do_this && **snode->do_this &&
!inMkQueue(snode->being_made) )
{
snode->time = NEWTIME; /* Assume the time will change */
snode->made = 0; /* This item has not been made yet */
enqueue(&MkQueue,snode); /* Add to list of things to make */
}
}
}
/*----------------------------------------------- DISPATCH TASK -----------*/
int dispatch( void )
{
/* Grab a job from the TskQueue, send all of the commands to that
* application through its keyboard, close the app, and return.
*/
TNODE *snode; /* Source file node pointer */
char **linev; /* Command to execute */
char *outfnm; /* file to redirect output to */
int cmdcnt; /* how many commands were redirected */
ulong keyhan; /* handle for keyboard */
static int taskcnt = 0; /* give each task a unique identifier */
void send_keys(ulong keyhan, char *keys);
mal_lock(TskQueueLock); /* grab the task queue */
snode = (TNODE *) dequeue(&TskQueue); /* get a task from the queue */
mal_unlock(TskQueueLock); /* let go of the task queue */
keyhan = key_of(snode->apphan); /* get keyboard handle */
api_beginc();
snode->tsknum = taskcnt++; /* assign a new task number */
api_endc();
/* get window to "escape out of" any commands that creeped in at startup
*/
send_keys(keyhan,"\x1B"); /* "\x1B" is the Escape key character */
for (linev = snode->do_this, cmdcnt = 0;
*linev && **linev && Status != ABORT; linev++)
{
send_keys(keyhan,*linev); /* send the command */
/* if the command doesn't already redirect output, do so */
if (strchr(*linev,'>') == NULL && strlen(*linev)+ReDirLen < MAXLINE)
{
/* get a new output file */
if ((outfnm = gen_name((int) snode->tsknum,cmdcnt++)) != NULL)
{
send_keys(keyhan," > "); /* send a redirect command */
send_keys(keyhan,outfnm);
fmem(outfnm); /* free up the temp file name */
}
else
--cmdcnt; /* we couldn't redirect anything */
}
send_keys(keyhan,"\r"); /* send return key to run the command */
}
send_keys(keyhan,"exit\r"); /* make window exit itself when done */
waitwhile(api_isobj(snode->apphan)); /* and wait for task to go away */
snode->made = 1; /* show that this item was made */
if (cmdcnt > 0) /* were any commands redirected? */
{
mal_lock(OutQueueLock); /* get access to output queue */
enqueue(&OutQueue,snode); /* add to output list */
mal_unlock(OutQueueLock); /* free access to queue */
}
api_beginc(); /* this task is done, so */
--RunCnt; /* update # of running tasks */
api_endc();
}
void send_keys(ulong keyhan, char *keys)
{
/* send string to keyboard, pausing every 5 characters to avoid
* overflowing any buffers. You could try different values, but
* 5 seems to be a reasonable compromise between speed and making
* sure no keys are lost.
*/
int keyctr;
if (keys != NULL)
while (*keys)
{
waitwhile(key_sizeof(keyhan)); /* wait for keyboard space */
for (keyctr = 0; *keys && keyctr < 5; keyctr++)
key_write(keyhan,keys++,1,0); /* send a single keystroke */
}
}
/*----------------------------------------------- MAKE TASK ---------------*/
int make( void )
{
/* Actually execute the commands. Items are removed from the
* MkQueue queue, and placed back on the queue if its dependents
* haven't been made yet. This task runs in parallel with others.
*/
TNODE *snode; /* Source file node pointer */
char **linev; /* Command to execute */
char doaction; /* Should we do anything? */
char *stack; /* stack for dispatch() tasks */
/* while there are still items to make */
while ( (snode = (TNODE *) dequeue(&MkQueue)) != NULL && Status != ABORT )
{
/* make sure all dependents have been made before acting */
for (linev = snode->depends_on, doaction = 1; *linev; linev++ )
if (!(find(*linev,Root)->made))
doaction = 0;
if (!doaction)
{
enqueue(&MkQueue,snode); /* put the item back on the queue */
api_pause(); /* and give up our time slice */
}
else
{
/* put the item on the task queue, and start up its dispatch */
mal_lock(TskQueueLock); /* grab the task queue */
enqueue(&TskQueue,snode); /* put a task on the queue */
mal_unlock(TskQueueLock); /* let go of the task queue */
/* get stack space for the command dispatcher */
if ( (stack = (char *) gmem(STKSIZE)) == NULL )
err("Out of memory");
/* keep trying to start a new application
* unless user aborts or wants to access DESQview menu
*/
while ( Status != NORMAL ||
(snode->apphan = app_start((char *) &Pif,Lpif)) == 0 )
{
if (Status == ABORT) /* get out now! */
break;
else if (RunCnt == 0 && /* can't we even get 1 running? */
Status == NORMAL && snode->apphan == 0)
err("Cannot start a single process");
else
api_pause(); /* just give up our time slice */
}
if (snode->apphan != 0)
{
/* either hide or put the application in the background */
if (ShowWin)
app_goback(snode->apphan);
else
app_hide(snode->apphan);
/* start up another command dispatcher, with no window */
tsk_new(dispatch,stack,STKSIZE,"",0,0,0);
api_beginc();
++RunCnt; /* we've started another task */
api_endc();
}
}
}
waitwhile(RunCnt > 0); /* wait for all tasks to finish */
}
/*----------------------------------------------- OUTPUT TASK -------------*/
void output( void )
{
/* Send files (created by redirecting output to DVMKxxyy.$$$) to
* standard output in same order they were created, keeping all
* output for a given dependency together.
*/
TNODE *onode; /* Output node pointer */
FILE *infile; /* file to read input from */
char *infnm; /* file name of input */
char **linev; /* pointer to commands */
int ch,counter;
/* loop until everything is finished */
while ((api_isobj(MakeTsk) || OutQueue != NULL) && Status != ABORT)
{
mal_lock(OutQueueLock); /* get access to output queue */
if ( (onode = (TNODE *) dequeue(&OutQueue)) == NULL )
{
mal_unlock(OutQueueLock); /* free access to queue */
api_pause(); /* and give up our time slice */
} /* because there's nothing to output */
else
{
mal_unlock(OutQueueLock); /* free access to queue */
for ( linev = onode->do_this, counter = 0;
*linev && **linev; linev++ )
{
printf("\n%s\n",*linev); /* print the command executed */
/* make sure dvmake was able to redirect output */
if (strchr(*linev,'>') == NULL &&
strlen(*linev)+ReDirLen < MAXLINE)
{
/* get the file name and open the file */
infnm = gen_name((int) onode->tsknum,counter++);
/* lock access to memory (fopen() gets memory) */
mal_lock(AllocLock);
if (infnm == NULL || (infile = fopen(infnm,"r")) == NULL)
{
mal_unlock(AllocLock); /* free access to memory */
/* not a drastic error, but tell user */
printf("Can't open %s\n",infnm);
}
else /* we successfully opened the temporary file */
{
mal_unlock(AllocLock); /* free access to memory */
/* copy the file to stdout */
while ((ch = fgetc(infile)) != EOF)
putchar(ch);
/* get access to memory (fclose() releases memory) */
mal_lock(AllocLock);
fclose(infile); /* close, and */
remove(infnm); /* erase the temporary file */
mal_unlock(AllocLock); /* free access to memory */
}
fmem(infnm);
}
}
}
}
/* if user aborts, get rid of remaining temporary files */
if (Status == ABORT)
{
waitwhile(api_isobj(MakeTsk));/* wait for make() to stop first */
mal_lock(OutQueueLock); /* gain access to queue */
while ( (onode = (TNODE *) dequeue(&OutQueue)) != NULL )
for ( linev = onode->do_this, counter = 0;
*linev && **linev; linev++ )
if (strchr(*linev,'>') == NULL && /* could we redirect? */
strlen(*linev)+ReDirLen < MAXLINE)
if ((infnm = gen_name((int) onode->tsknum,counter++)) !=
NULL)
{
remove(infnm); /* erase the temporary file */
fmem(infnm); /* and free up the memory */
}
mal_unlock(OutQueueLock); /* free access to queue */
}
}
/*----------------------------------------------- INITIALIZE ROUTINES -----*/
int controlbrk( void ) /* handles control-break interrupts */
{
return( 1 ); /* return non-zero to continue running */
}
/*-------------------------------------------------------------------------*/
void getoptions(int argc,char *argv[])
{
int i;
char *getcwd(); /* defined in dir.h */
void ctrlbrk(); /* defined in dos.h */
TskQueueLock = mal_new(); /* semaphore for TskQueue */
OutQueueLock = mal_new(); /* semaphore for OutQueue */
AllocLock = mal_new(); /* semaphore for malloc/free */
/* get the current directory, used to save output files */
if (getcwd(CurDir,MAXFNM) == NULL)
err("Cannot get current directory");
ReDirLen = strlen(CurDir)+16; /* length of redirection file name */
/* initialize the control-break handler to call controlbrk() */
ctrlbrk(controlbrk);
for (i = 1; i < argc; i++) /* get the command line switches */
{
/* windows switch */
if (strcmp(argv[i],"-w") == 0 || strcmp(argv[i],"-W") == 0)
ShowWin = 1;
/* memory size switch */
else if (strcmp(argv[i],"-k") == 0 || strcmp(argv[i],"-K") == 0)
{
if (i < argc - 1)
{
if ((MemSize = atoi(argv[++i])) <= 0)
err("Invalid memory size parameter for -k switch");
}
else
err("Missing memory size parameter for -k switch");
}
/* help switch */
else if (strcmp(argv[i],"-h") == 0 || strcmp(argv[i],"-H") == 0)
{
printf("dvmake [-w] [-k nnn] [-h] [target]\n");
printf(" -w switch displays windows \n");
printf(" -k switch sets task memory to nnn K-bytes \n");
printf(" -h switch displays help message \n");
}
/* anything else with - or / must be mistake */
else if (argv[i][0] == '-' || argv[i][0] == '/')
err("Invalid command switch: %s",argv[i]);
/* anything else must be the first item to make */
else
{
if ( (First = (char *) gmem(strlen(argv[i])+1)) == NULL)
err("Out of memory");
strcpy(First,argv[i]);
}
}
}
/*----------------------------------------------- START PARALLEL TASKS ----*/
void startup( void )
{
char *mkstack, /* stacks for various tasks */
*menustack;
MainWin = win_me(); /* get handle of main window */
/* initialize a PIF buffer with appropriate window size */
if (ShowWin)
init_pif(&Pif,&Lpif," DVMAKE TASK ",25,80);
else
init_pif(&Pif,&Lpif," DVMAKE TASK ",1,1);
/* get stack space for the parallel tasks */
if ( (mkstack = (char *) gmem(STKSIZE)) == NULL ||
(menustack = (char *) gmem(STKSIZE)) == NULL )
err("Out of memory");
/* we are now in Parallel mode */
Parallel = 1;
/* start a task to track a menu, with no title or window */
MenuTsk = tsk_new(dvmenu,menustack,STKSIZE,"",0,0,0);
/* start a task to make the items, with no title or window */
MakeTsk = tsk_new(make,mkstack,STKSIZE,"",0,0,0);
}
/*----------------------------------------------- STOP PARALLEL TASKS -----*/
void finishup( void )
{
/* get dvmenu() to stop by posting its Object Queue */
tsk_post(MenuTsk);
/* wait for everything to really finish */
waitwhile(api_isobj(MakeTsk) || api_isobj(MenuTsk));
}
/*----------------------------------------------- MAIN PROGRAM ------------*/
main( int argc, char *argv[] )
{
/* if DESQview is not running or version is too low, display a message */
if (api_init() < DV_VER)
{
printf ("dvmake requires DESQview version %d.%02d or later\n",
DV_VER/256,DV_VER%256);
return( 1 );
}
else
{
api_level(DV_VER); /* tell DV what extensions to enable */
getoptions( argc, argv ); /* get command line arguments */
if ( (Makefile = fopen(MAKEFILE, "r")) == NULL )
err("can't open %s\n", MAKEFILE );
if ( !dependencies() ) /* is there anything in the mkfile */
{
fclose(Makefile);
err("Nothing to make");
}
else
{
fclose(Makefile);
make_queue( First ); /* simulate the sequential make */
if (MkQueue != NULL) /* does anything need to be made? */
{
startup(); /* start parallel tasks */
output(); /* display output from tasks */
finishup(); /* stop parallel tasks */
}
mal_free(TskQueueLock); /* get rid of our semaphores */
mal_free(OutQueueLock);
mal_free(AllocLock);
if (Status == ABORT) /* print the error message */
fprintf(stderr,"%s",Error);
api_exit(); /* tell DESQview we're done */
return( Status == ABORT ? 1 : 0 );
}
}
}