home *** CD-ROM | disk | FTP | other *** search
- /*
- * \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);
- }