home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ftp.update.uu.se
/
ftp.update.uu.se.2014.03.zip
/
ftp.update.uu.se
/
pub
/
rainbow
/
msdos
/
misc
/
make.lzh
/
MAKE.C
< prev
next >
Wrap
Text File
|
1986-01-19
|
18KB
|
774 lines
/*
* \usr\c\make.c
*
* An imitation of the Unix MAKE facility
*
* Copyright (C) 1984 by Larry Campbell, 73 Concord St., Maynard, Mass.
*
* This software may be freely copied and disseminated for
* noncommercial purposes, if and only if this entire copyright
* statement and notice is included intact. This software, and
* the information contained herein, may not be used for commercial
* purposes without my prior written permission.
*
* This program runs a new shell (COMMAND.COM) for each command
* specified in the makefile. This, I recommend that you put
* a copy of the shell in ramdisk (if you have one). Assuming
* your ramdisk is called, say, drive F:, you would:
*
* COPY A:\COMMAND.COM F:
* SET COMSPEC=F:\COMMAND.COM
*
*/
/*
* Revision history
*
* 1.11 Make default makefilename be MAKEFILE.
* 1.10 Add -n (trace) switch.
* 1.09 Allow multiple targets before colon.
* 1.08 Cleanup and speedup of makefile parsing.
* 1.07 Add continuation lines (hyphen as last char of line)
* 1.06 Don't need to make dummy FCBs, zero seems to work OK.
* 1.05 Fix bug finding COMSPEC when it's not first env variable
* 1.04 Wordier usage text, including copyright notice.
* Remove printf's (except when DEBUG on) to shrink EXE
* 1.03 Don't uppercase shell command and fix datetime bug
* 1.02 Random cleanup
* 1.01 The beginning
*/
#define VERSION "MAKE version 1.11 Copyright (C) 1984 by Larry Campbell, Maynard Mass."
#include <stdio.h>
#define ABORT(s) { puts (s); exit (); }
#define DOS_ERROR(n) { fputs (dos_error (n), stderr); exit (); }
#define DOS(n) { srv.ax = n << 8;\
status = sysint (0x21, &srv, &rrv);\
if (status & 1) DOS_ERROR (rrv.ax); };
#define PREREQ_MAGIC 123
#define FILE_MAGIC 543
#define SHELL_MAGIC 678
#define TARG_MAGIC 987
#define MAXTARGETS 12
extern char
*dos_error ();
unsigned long
getdatetime ();
struct target_node
*lookup_target ();
struct prereq_node
*new_prereq_node ();
struct file_node
*new_file_node ();
struct regval {int ax, bx, cx, dx, si, di, ds, es;};
struct segval {int scs, sss, sds, ses;};
struct regval srv, rrv;
struct segval seg;
struct
{
char reserved[21];
char attr;
unsigned time;
unsigned date;
unsigned size_l;
unsigned size_h;
char pname[13];
} find_buf;
/*
* MAKE parses the make file and constructs a somewhat tangled
* directed graph describing dependencies. There is a list of
* target_node structures, each of which points to a list of
* prereq_node structures. These both point to file_node structures,
* which contain information about files such as date-time of write
* and the filename. To keep things simple, MAKE insures that only
* one file_node exists for any given file.
*/
struct file_node
{
int magic;
char *fname;
unsigned long fdatetime;
struct file_node *chain;
};
typedef struct file_node *fileptr;
struct target_node
{
int magic;
struct target_node *next;
fileptr file;
struct prereq_node *prereq_list;
struct shell_node *shell_list;
};
struct prereq_node
{
int magic;
struct prereq_node *next;
fileptr file;
};
struct shell_node
{
int magic;
struct shell_node *next;
char *command;
};
static struct target_node
*target_list;
static struct file_node
*file_node_list;
static char
*makefilename,
comspec[132];
static int
status,
trace_flag;
usage ()
{
puts (VERSION);
puts ("This program may be copied freely for noncommercial purposes. It may");
puts ("not be copied for commercial use without my prior written permission.\n");
puts ("This program is an imitation of the MAKE program supplied with Unix.");
puts ("It works exactly like Unix MAKE (and VAX/VMS MMS) except that it has");
puts ("no default rules and no macro facility.\n");
puts ("Usage: make [-f makefile] [options] file ...");
puts ("Options:");
puts (" -f filename specify makefile, default is MAKEFILE");
puts (" -n trace and print, but don't execute commands");
exit ();
}
main (argc, argv)
int argc;
char **argv;
{
int
argi,
targi;
char
*targname[MAXTARGETS];
if (argc < 2)
usage ();
makefilename = "MAKEFILE";
trace_flag = 0;
target_list = 0;
file_node_list = 0;
targi = 0;
random_inits (); /* init random stuff */
puts (VERSION);
for (argi = 1; argi < argc; argi++)
{
if (argv[argi][0] == '-') /* switch */
switch (argv[argi][1]) /* switch character */
{
case 'f':
if (argi < (argc - 1))
makefilename = argv[++argi];
else
usage ();
break;
case 'n':
trace_flag = 1;
break;
default:
usage ();
}
else
{ /* target name */
if (targi + 1 == MAXTARGETS)
ABORT ("too many target files");
targname[targi] = malloc (strlen (argv[argi]) + 1);
if (targname[targi] == NULL)
ABORT ("no memory");
strcpy (targname[targi], argv[argi]);
targi++;
};
};
parse (makefilename);
for (argi = 0; argi < targi; argi++)
make (targname[argi]);
puts ("MAKE complete.");
}
/*
* Init random stuff. This routine frees all memory we're not using,
* and finds out where the shell lives.
*/
random_inits ()
{
unsigned int
segsize,
envseg;
segread (&seg); /* read segment registers */
srv.es = seg.scs;
segsize = peek (0x2, seg.scs); /* get top of memory address */
segsize = segsize - seg.sss; /* compute size of data segment */
if (segsize > 0x1000) segsize = 0x1000; /* max CI-C86 version 1.33 uses */
srv.bx = (seg.sss + segsize) - seg.scs; /* free memory we'll never use */
DOS (0x4A);
envseg = peek (0x2C, seg.scs); /* get environment segment */
if (! env_fetch ("COMSPEC", envseg, comspec))
ABORT ("Can't find COMSPEC environment string");
}
parse (makefilename)
char *makefilename;
{
FILE
*fd;
#define LINESIZE 400 /* max length of input line */
#define MAXCOMMON 8 /* max no. of targets with common prereqs */
#define WORDSIZE 80
int
targi,
i,
line_length;
char
c,
*sp,
*dp,
input[LINESIZE],
cont_line[LINESIZE],
word[WORDSIZE];
struct target_node
*targ[MAXTARGETS];
fd = fopen (makefilename, "r");
if (fd == NULL) ABORT ("can't open makefile");
targi = 0;
while (fgets (input, LINESIZE, fd) != 0)
{
line_length = strlen (input);
if (input[line_length - 1] == '\n') /* stomp on trailing newline */
input[--line_length] = 0;
while (input[line_length - 1] == '-') /* continuation line coming? */
{
input[--line_length] = 0; /* stomp on trailing hyphen */
fgets (cont_line, LINESIZE, fd); /* read next line */
line_length += strlen (cont_line); /* and append to input */
strcat (input, cont_line);
if (input[line_length - 1] == '\n') /* stomp on trailing newline */
input[--line_length] = 0;
};
#ifdef DEBUG
printf ("Makefile Input: \"%s\"\n", input);
#endif
sp = input;
c = *sp;
if (c == '!' || c == '#') continue; /* ignore comment lines */
if (isspace (c)) /* target or shell line? */
{ /* leading whitespace - shell */
while (isspace (*sp)) sp++; /* skip leading whitespace */
if (targi == 0) ABORT ("Target line must come before shell lines");
for (i = 0; i < targi; i++)
link_new_shell_line (targ[i], sp);
continue;
};
targi = 0;
dp = word; /* where to put target name */
while (c = *sp++)
if (c == ':' || isspace (c)) /* space or colon ends target name */
{
char colonflag;
*dp = 0; /* ASCIZ */
colonflag = (c == ':'); /* "colon present" flag */
while ((c = *sp) && isspace (c)) sp++; /* skip whitespace */
if (c == ':') /* if whitespace followed by colon */
{
colonflag = 1; /* remember colon present */
sp++; /* skip colon and */
while ((c = *sp) && isspace (c)) sp++; /* trailing spaces */
};
targ[targi++] = new_target (word);
if (colonflag)
break;
if (targi > MAXTARGETS) ABORT ("too many targets in one line");
dp = word;
}
else
*dp++ = c; /* accumulate target name */
if (targi == 0)
{
fputs ("No target found in this line:\n \"");
fputs (input, stdout);
puts ("\"");
ABORT ("bad makefile");
};
while ((c = *sp) && isspace (c)) sp++;
dp = word; /* where to accumulate prereq names */
while (1) /* pick off prerequisite names */
{
c = *sp++;
if (c == 0 || isspace (c)) /* space terminates a name */
{
if (dp == word) break; /* if nothing found, line has ended */
*dp = 0;
for (i = 0; i < targi; i++)
link_new_prereq (targ[i], word);
while ((c = *sp) && isspace (c)) sp++;
dp = word; /* reset destination pointer */
}
else
*dp++ = c;
};/* end while */
};/* end while */
fclose (fd);
}
/*
* new_target
*
* We think we have a new target; this routine scans the
* target list and if we've already seen this name returns
* the node we already built. Otherwise, a new node is
* allocated and linked.
*/
new_target (name)
char *name;
{
struct target_node
*targ,
*last_targ;
#ifdef DEBUG
printf ("new_target (\"%s\")\n", name);
#endif
targ = target_list;
while (targ != 0)
{
if (targ->magic != TARG_MAGIC) ABORT ("bad target_list");
if (strcmp (targ->file->fname, name) == 0)
return (targ);
last_targ = targ;
targ = targ->next;
};
targ = malloc (sizeof (struct target_node));
if (targ == NULL) ABORT ("no memory");
targ->magic = TARG_MAGIC;
targ->file = new_file_node (name);
targ->next = 0;
targ->shell_list = 0;
targ->prereq_list = 0;
if (target_list == 0)
target_list = targ;
else
last_targ->next = targ;
return (targ);
}
/*
* link_new_shell_line
*
* Add a shell command to the list of commands needed to update
* a target file
*/
link_new_shell_line (targ, line)
struct target_node *targ;
char *line;
{
struct shell_node
*next,
*snode,
*new;
#ifdef DEBUG
printf ("link_new_shell_line (%x, \"%s\")\n", targ, line);
#endif
new = malloc (sizeof (struct shell_node));
if (new == NULL) ABORT ("no memory");
new->next = 0;
new->magic = SHELL_MAGIC;
new->command = malloc (strlen (line) + 1);
if (new->command == NULL) ABORT ("no memory");
strcpy (new->command, line);
if (snode = targ->shell_list)
{
if (snode->magic != SHELL_MAGIC) ABORT ("bad shell chain");
while (next = snode->next) snode = next;
snode->next = new;
}
else
targ->shell_list = new;
}
/*
* link_new_prereq (targ, name)
*
* Link a new prerequisite file onto prereq list for a target.
* We first scan the list to make sure this isn't a duplicate
* (if duplicate, we just ignore it).
*/
link_new_prereq (targ, name)
struct target_node *targ;
char *name;
{
struct prereq_node
*prereq,
*last;
fileptr
fnode;
#ifdef DEBUG
printf ("link_new_prereq (%x, \"%s\")\n", targ, name);
#endif
prereq = targ->prereq_list;
if (prereq == 0)
{
targ->prereq_list = new_prereq_node (new_file_node (name));
#ifdef DEBUG
printf (" 1st prereq linked is at %x\n", targ->prereq_list);
#endif
return;
};
while (prereq)
{
if (prereq->magic != PREREQ_MAGIC) ABORT ("bad prerequisite chain");
if (strcmp (name, prereq->file->fname) == 0)
return;
last = prereq;
prereq = prereq->next;
};
last->next = new_prereq_node (new_file_node (name));
}
/*
* new_prereq_node
*
* Allocate and return a new prerequisite node
*/
struct prereq_node *new_prereq_node (fnode)
struct file_node *fnode;
{
struct prereq_node
*new;
#ifdef DEBUG
printf ("new_prereq_node (struct file_node *%x)\n", fnode);
#endif
new = malloc (sizeof (struct prereq_node));
if (new == NULL) ABORT ("no memory");
new->next = 0;
new->magic = PREREQ_MAGIC;
new->file = fnode;
return (new);
}
/*
* new_file_node
*
* Return file_node pointer for a file; returns pointer to
* existing file_node if this file already seen, else allocates
* and inits a new node
*/
struct file_node *new_file_node (name)
char *name;
{
struct file_node
*last,
*newnode,
*fnode;
#ifdef DEBUG
printf ("new_file_node (\"%s\")\n", name);
#endif
fnode = file_node_list;
last = 0;
while (fnode)
{
if (fnode->magic != FILE_MAGIC) ABORT ("bad file_node_list chain");
if (strcmp (name, fnode->fname) == 0)
{
#ifdef DEBUG
printf ("new_file_node returning existing node at %x\n", fnode);
#endif
return (fnode);
};
last = fnode;
fnode = fnode->chain;
};
newnode = malloc (sizeof (struct file_node));
if (newnode == NULL) ABORT ("no memory");
init_fnode (newnode, name);
if (last == 0)
file_node_list = newnode;
else
last->chain = newnode;
return (newnode);
}
/*
* init_fnode
*
* Init a file_node with name and date/time of a file
*/
init_fnode (fnode, name)
char *name;
struct file_node *fnode;
{
#ifdef DEBUG
printf ("init_fnode (%x, \"%s\")\n", fnode, name);
#endif
fnode->fname = malloc (strlen (name) + 1);
if (fnode->fname == NULL) ABORT ("no memory");
strcpy (fnode->fname, name);
fnode->fdatetime = getdatetime (name);
fnode->chain = 0;
fnode->magic = FILE_MAGIC;
}
/*
* getdatetime
*
* Return date-time of a file squished into a long so compares
* are easy
*/
unsigned long getdatetime (name)
char *name;
{
unsigned long
datetime;
int
dma_off,
dma_seg;
srv.ax = 0x2F00; /* get current DMA address */
sysint (0x21, &srv, &rrv); /* .. */
dma_off = rrv.bx; /* and save for later restoration */
dma_seg = rrv.es;
srv.ds = seg.sds;
srv.dx = &find_buf; /* set DMA to GNJFN block */
srv.ax = 0x1A00;
sysint (0x21, &srv, &rrv);
srv.ds = seg.sds;
srv.dx = name; /* pathname */
srv.cx = 0; /* attributes */
srv.ax = 0x4E00; /* GTJFN */
status = sysint (0x21, &srv, &rrv);
if (status & 1)
{
#ifdef DEBUG
printf ("MAKE: warning -- can't find file \"%s\", continuing...\n", name);
#endif
return (0L);
};
srv.ds = dma_seg; /* restore DMA address */
srv.dx = dma_off;
srv.ax = 0x1A00;
sysint (0x21, &srv, &rrv);
#ifdef DEBUG
printf ("filespec = \"%s\", date = %u, time = %u, sizel = %d\n",
&find_buf.pname, find_buf.date, find_buf.time, find_buf.size_l);
#endif
datetime = (unsigned long) find_buf.date;
datetime = datetime << 16;
datetime = datetime + ((unsigned long) find_buf.time);
return (datetime);
}
/*
* make (name)
*
* This routine actually does the work. It scans the list of
* targets parsed from the makefile, and checks the target's
* prerequisites date/time values against the target's. If
* the prerequisite is itself a target (present in target_list),
* we call make recursively to check it. Then, if any of our
* prerequisites are newer than we are, we execute all our shell
* commands.
*/
make (targname)
char *targname;
{
struct target_node
*targ;
struct prereq_node
*prereq;
unsigned long
newest_prereq;
#ifdef DEBUG
printf ("Making %s\n", targname);
#endif
newest_prereq = 0;
if ((targ = lookup_target (targname)) == 0)
{
puts ("Target not found");
ABORT ("make failed");
};
if (prereq = targ->prereq_list) /* check each prerequisite file */
while (prereq)
{
if (lookup_target (prereq->file->fname)) /* if prereq known, */
make (prereq->file->fname); /* recursively make */
if (prereq->file->fdatetime > newest_prereq) /* now check age */
newest_prereq = prereq->file->fdatetime;
prereq = prereq->next;
};
#ifdef DEBUG
printf ("Target \"%s\" datetime is %ld, newest prereq is %ld\n",
targ->file->fname, targ->file->fdatetime, newest_prereq);
#endif
if (targ->file->fdatetime < newest_prereq)
build (targ);
}
/*
* build
*
* Invoke shell commands to build a target file
*/
build (targ)
struct target_node *targ;
{
struct shell_node
*snode;
char
*cmd;
#ifdef DEBUG
printf ("MAKE: Building \"%s\"\n", targ->file->fname);
#endif
snode = targ->shell_list;
if (snode == 0) ABORT ("No shell commands specified");
while (snode)
{
if (snode->magic != SHELL_MAGIC) ABORT ("bad shell node");
cmd = snode->command;
fputs ("MAKE: ", stdout);
fputs (cmd, stdout);
if (trace_flag)
puts (""); /* EXEC does newline, so must be faked */
else /* here if just tracing */
system (cmd);
snode = snode->next;
};
targ->file->fdatetime = getdatetime (targ->file->fname);
}
/*
* system
*
* Execute a shell command
*/
static struct /* execute program parameter block */
{
int environment;
int cmdoff;
int cmdseg;
int fcb5off;
int fcb5seg;
int fcb6off;
int fcb6seg;
} params;
static char
*sp,
c,
cmdstring[132];
system (cmd)
char *cmd;
{
int
i;
sp = "/C"; /* magic switch for shell to say "here's a command" */
i = 1;
while (c = *sp++)
cmdstring[i++] = c;
sp = cmd; /* append user's shell command */
while (c = *sp++)
cmdstring[i++] = c;
cmdstring[i++] = 0x0D;
cmdstring[i] = 0;
cmdstring[0] = i - 2;
params.environment = peek (0x2c, seg.scs); /* inherit our environment */
params.cmdseg = seg.sss;
params.cmdoff = cmdstring;
params.fcb5seg = params.fcb6seg = 0;
params.fcb5off = params.fcb6off = 0;
status = exec (seg.sds, comspec, ¶ms);
if (status)
{
fputs ("\nMAKE error: ", stdout);
puts (dos_error (status));
ABORT ("EXEC failed");
};
}
struct target_node *lookup_target (name)
char *name;
{
struct target_node
*targ;
targ = target_list;
while (targ)
{
if (strcmp (name, targ->file->fname) == 0)
return (targ);
targ = targ->next;
};
return (0);
}