From decwrl!ucbvax!tut.cis.ohio-state.edu!cs.utexas.edu!uunet!allbery Fri Nov 3 19:53:18 PST 1989 Article 1151 of comp.sources.misc: Path: decwrl!ucbvax!tut.cis.ohio-state.edu!cs.utexas.edu!uunet!allbery From: allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc) Newsgroups: comp.sources.misc Subject: v08i104: Make Version 1.5 (Part 1 of 3) Message-ID: <71230@uunet.UU.NET> Date: 3 Nov 89 01:28:14 GMT Sender: allbery@uunet.UU.NET Reply-To: greggy@etude.UUCP (Greg Yachuk) Lines: 1598 Approved: allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc) Posting-number: Volume 8, Issue 104 Submitted-by: greggy@etude.UUCP (Greg Yachuk) Archive-name: make.gy/part01 #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh 'MANIFEST' <<'END_OF_FILE' X File Name Archive # Description X----------------------------------------------------------- X MANIFEST 1 This shipping list X README 1 X build.c 1 X decl.h 3 X default.dos 3 X default.mk 3 X make.c 1 X make.doc 2 X make.h 3 X makefile 3 X makefile.dos 3 X parse.c 2 X tstring.c 1 X tstring.h 3 END_OF_FILE if test 548 -ne `wc -c <'MANIFEST'`; then echo shar: \"'MANIFEST'\" unpacked with wrong size! fi # end of 'MANIFEST' fi if test -f 'README' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'README'\" else echo shar: Extracting \"'README'\" \(4707 characters\) sed "s/^X//" >'README' <<'END_OF_FILE' XMake Version 1.5 89-10-30 X XThis is a work-alike program, very close to `make' as it exists on Sun Xsystems. It is compiled with Microsoft C (5.1) and runs on PCDOS 3.3. XIt also compiles and runs on BSD 4.2 Unix. X XMany thanks to Kirk Bailey (bailey@mist.cs.orst) X Brian Wilson (island!sun!grenada!dr_unix) X Jeff Fried (ames!culls!jeff) X Arend van den Brug (arend@philmds@philnl@mcvax) Xfor pointing out problems and improvements. X XI have included a makefile and a (rudimentary) default.mk for BSD Unix. XUpdates, additions and corrections are welcomed. The shar distribution Xhas the filenames reversed from the ZOO distribution: X X ZOO distribution shar distribution X ---------------------------- -------------------------- X makefile makefile.bsd makefile.dos makefile X default.mk default.bsd default.dos default.mk X X XThis is because I assume that the ZOO is used on DOS and the shar on XUnix. If what you got is not appropriate, rename the appropriate files Xand make. For example, if you got the DOS version on a Unix system: X X mv makefile makefile.dos X mv makefile.bsd makefile X mv default.mk default.dos X mv default.bsd default.mk X make X XAlternatively, you can (probably) just use this following command: X X make -r -f default.bsd -f makefile.bsd X XThis is version 1.5. There is a version 1.4 that was submitted to Xcomp.binaries.ibm.pc by Arend van den Brug. I have incorporated many of Xthe changes into my version and called it 1.5. These are the changes Xfrom 1.3 to 1.5: X support the -k, -S and -q options (see make.man). X X correctly support the $(MAKE) macro. X X allow target lines to end with a semi-colon and a command. X X corrected bugs when allocated strings are over-run. X X added "*" and "?" to the list of characters that force the use X of the shell on Unix X X XThese are the changes from 1.2 to 1.3: X don't append shell command lines when encountering multiple X targets of the same name. if this is a *special* target X (e.g. .c.obj), just override. otherwise exit with an error. X X flush output before executing the command, so the output comes X out in the right order when redirecting stdout to a file. X X if a file is supposedly built, but does not exists, use current time. X X exit with an error if a target line does not have a ':'. X X don't force a space after "include" in case TAB is used. X X D and F modifiers for Directory and Filename of $@, $<, $*. X X use a dependent file for an implicit rule, if possible. X X allow Makefile as well as makefile, for Unix. X X always print statements when using -n, even if they start with @. X X backquote (`) will force use of a shell, in Unix. X XThese are the changes from 1.1 to 1.2: X ensure command line macros override makefile macros, even as X makefiles are being read in. X X support time checks correctly on MSDOS directories. X X use MAKEFLAGS macro, and set it up for subordinate makes. X X import environment variables and support -e flag. X X handle `-f -' (i.e. makefile from stdin) correctly. X X clean up some potential NULL pointer dereferences. X X correct errors in handling nested makes. X X modify tokenizing routine to correctly handle trailing separators. X X the documentation has been re-written X XThese are the changes from 1.0 to 1.1: X modify prerequisite list handling to correctly allow a target to X appear on multiple target lines. X XThere is a short story which goes with this offering. Sometime early in X1988, someone (possibly Dan Grayson) posted copyrighted source for a X`make' to Usenet. I used it and modified it somewhat, and then lost my Xhard disk. Having found this program to be very useful, I set about Xrewriting it from my recollection of the source that I had seen. I have Xasked Rahul (moderator of comp.binaries.ibm.pc) if he could trace the Xoriginal submitter, and have also posted a note to the net in c.b.i.p.d, Xtrying to locate this person. So far, no trace has been found. I Xreally would like to show this source to him(and hopefully have him Xagree that it is not the same as his). X XI have based my algorithms on this previous source code. These are not Xcopyrightable, so I feel that I have not infringed upon anyone's rights. XAlso, I feel that I have acted in good faith trying to trace this Xperson. I hope that recipients of this code feel the same. I am Xreleasing this into the public domain. You may do anything you wish Xwith it, even copyright it yourself and try to sell it as your own. XGood luck, and have fun. X X -greg X XGreg Yachuk Informix Software Inc., Menlo Park, CA (415) 926-6300 X{uunet,pyramid}!infmx!greggy why yes, I DID choose that login myself END_OF_FILE if test 4707 -ne `wc -c <'README'`; then echo shar: \"'README'\" unpacked with wrong size! fi # end of 'README' fi if test -f 'build.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'build.c'\" else echo shar: Extracting \"'build.c'\" \(4850 characters\) sed "s/^X//" >'build.c' <<'END_OF_FILE' X/* X * build.c An imitation of the Unix MAKE facility X * X * 88-10-01 v1.0 created by greg yachuk, placed in the public domain X * 88-10-06 v1.1 changed prerequisite list handling X * 88-11-11 v1.2 fixed some bugs and added environment variables X * 89-07-12 v1.3 stop appending shell commands, and flush output X * 89-08-01 v1.4 AB lots of new options and code X * 89-10-30 v1.5 greggy -f -S -q options, took some changes from v1.4 X * X */ X X#include X#include X#ifdef MSDOS X#include X#include X#else X#include X#endif X X#include "make.h" X#include "tstring.h" X#include "decl.h" X Xchar *shell_cmds[] = { X#ifdef MSDOS X "dir", "type", "rem", "pause", "date", "time", "ren", "rename", X "ver", "vol", "break", "verify", "mkdir", "md", "exit", "ctty", X "echo", "if", "cls", "chdir", "cd", "rmdir", "rd", "copy", "del", X "erase", X#endif X NULL, X}; X X/* X * build - process the shell commands X */ Xbuild(shellp) XREGISTER shellptr *shellp; X{ X REGISTER char **argv; /* argified version of scmd */ X int runst; /* exec return status */ X char *scmd; /* command with symbols broken out */ X char *tcmd; /* copy of scmd for `tokenize' */ X char *errnum; /* error number in ascii */ X char *errmsg; /* error message */ X#ifdef MSDOS X char **shcp; /* pointer to shell command list */ X#endif X X if (shellp == NULL || opts.query) X return (0); X X for (; *shellp != NULL; X tfree(scmd), tfree(tcmd), tfree(argv), ++shellp) X { X /* breakout runtime symbols (e.g. $* $@ $<) */ X scmd = breakout((*shellp)->scmd); X X if (!opts.silent && (opts.noexec || !(*shellp)->s_silent)) X { X puts(scmd); X fflush(stdout); X } X X /* make a copy because tokenize litters '\0's */ X tcmd = tstrcpy(scmd); X argv = tokenize(tcmd); X X /* look for $(MAKE) */ X if (equal(argv[0], opts.make)) X { X /* call ourselves recursively */ X new_make(argv); X continue; X } X X if (opts.noexec) X continue; X X /* any SHELL meta-characters MUST be handled by the shell */ X if (!(*shellp)->s_shell && strpbrk(scmd, SHELL_METAS)) X (*shellp)->s_shell = 1; X#ifdef MSDOS X /* likewise, check for COMMAND.COM builtin commands */ X shcp = shell_cmds; X for (; !(*shellp)->s_shell && *shcp; ++shcp) X (*shellp)->s_shell = equal(*shcp, argv[0]); X#endif X /* run without COMMAND.COM if possible, 'cause it uses RAM */ X if ((*shellp)->s_shell) X runst = system(scmd); X else X runst = spawnvp(P_WAIT, argv[0], argv); X X if (runst == 0) X continue; X X /* uh-oh, an error */ X if (runst == -1) X perror("make"); X X errnum = talloc(18); X#ifdef MSDOS X errnum = itoa(runst, errnum, 10); X#else X sprintf(errnum, "%d", runst); X#endif X errmsg = (opts.keepon) ? "\007*** Ignoring Error code " X : "\007*** Error code "; X errmsg = tstrcat(errmsg, errnum); X terror(0, errmsg); X tfree(errmsg); X tfree(errnum); X X if (opts.keepon) X return (1); X X if (!opts.ignore && !(*shellp)->s_ignore) X exit(1); X } X X return (0); X} X X X/* X * new_make - save current environment X * - call make() recursively (actually main()) X * - clean up new environment X * - restore environment X */ Xnew_make(argv) Xchar **argv; X{ X targptr thead, tnext, tsuffix; X fileptr fhead, fnext; X symptr shead, snext; X shellptr shhead, shnext; X char **ttlist; X long tnow; X optnode topts; X int i; X X /* save all the globals */ X tsuffix = suffix_targ; X thead = target_list; X fhead = file_list; X shead = symbol_list; X shhead = shell_list; X ttlist = tlist; X tnow = now; X topts = opts; X X /* count the arguments */ X for (i = 0; argv[i]; ++i) X tunquote(argv[i]); X X /* call ourselves recursively; this inherits flags */ X ++make_level; X main(i, argv); X --make_level; X X /* we're back, so gotta clean up and dispose of a few things */ X while (target_list) X { X tnext = target_list->tnext; X if (target_list->tpreq) X tfree(target_list->tpreq); X if (target_list->tshell) X tfree(target_list->tshell); X tfree(target_list); X target_list = tnext; X } X X while (file_list) X { X fnext = file_list->fnext; X tfree(file_list->fname); X tfree(file_list); X file_list = fnext; X } X X /* don't drop all symbols, just the new ones */ X X while (symbol_list != shead) X { X snext = symbol_list->snext; X tfree(symbol_list->sname); X tfree(symbol_list->svalue); X tfree(symbol_list); X symbol_list = snext; X } X X while (shell_list) X { X shnext = shell_list->slink; X tfree(shell_list->scmd); X tfree(shell_list); X shell_list = shnext; X } X X /* restore our original globals */ X suffix_targ = tsuffix; X target_list = thead; X file_list = fhead; X symbol_list = shead; X shell_list = shhead; X tlist = ttlist; X now = tnow; X opts = topts; X} X X X#ifndef MSDOS Xint Xspawnvp(mode, path, args) Xint mode; Xchar *path; Xchar **args; X{ X int pid = 0; X union wait waitword; X X if (mode != P_OVERLAY) X pid = fork(); X X if (pid == 0) X execvp(path, args); X X wait(&waitword); X return (waitword.w_retcode); X} X#endif END_OF_FILE if test 4850 -ne `wc -c <'build.c'`; then echo shar: \"'build.c'\" unpacked with wrong size! fi # end of 'build.c' fi if test -f 'make.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'make.c'\" else echo shar: Extracting \"'make.c'\" \(17680 characters\) sed "s/^X//" >'make.c' <<'END_OF_FILE' X/* X * make.c An imitation of the Unix MAKE facility X * X * 88-10-01 v1.0 created by greg yachuk, placed in the public domain X * 88-10-06 v1.1 changed prerequisite list handling X * 88-11-11 v1.2 fixed some bugs and added environment variables X * 89-07-12 v1.3 stop appending shell commands, and flush output X * 89-08-01 v1.4 AB lots of new options and code X * 89-10-30 v1.5 greggy -f -S -q options, took some changes from v1.4 X * X */ X X#include X#include X#include X#include X#include X#include X#ifdef MSDOS X#include X#endif X X#include "make.h" X#include "tstring.h" X#include "decl.h" X X Xtargptr target_list = NULL; /* list of target nodes */ Xfileptr file_list = NULL; /* list of file nodes */ Xsymptr symbol_list = NULL; /* list of symbol nodes */ Xshellptr shell_list = NULL; /* list of shell nodes */ X Xint make_level = 0; /* for counting new_make()'s */ X Xtargptr first_targ = NULL; /* first target, in case nothing explicit */ Xtargptr suffix_targ = NULL; /* .SUFFIXES target pointer */ X Xchar **tlist = NULL; /* command line targets */ Xchar **flist = NULL; /* command line make files */ Xchar **mlist = NULL; /* command line macros */ X Xint tmax = 0; /* max size of tlist */ Xint fmax = 0; /* max size of flist */ Xint mmax = 0; /* max size of mlist */ X Xoptnode opts; /* all the options */ Xint readdef = 1; /* -r option */ Xint dispcount = 0; /* used for -D option */ X Xlong now; /* time at startup */ Xchar *makeflags; /* value to update the MAKEFLAGS macro with */ X X Xmain(argc, argv) Xint argc; Xchar **argv; X{ X REGISTER int i; X REGISTER targptr targp; X REGISTER int mk; X symptr symp; X char *envp; X char **envv; X X /* initialize the various global lists */ X X opts.depend = 0; X dispcount = 0; X X target_list = NULL; X file_list = NULL; X shell_list = NULL; X /* don't set symbol_list to NULL, or recursive makes won't work */ X X /* allocate space for command line targets, files and macros */ X X tlist = grow_list(NULL, &tmax); X flist = grow_list(NULL, &fmax); X mlist = grow_list(NULL, &mmax); X X /* process MAKEFLAGS environment variable, first */ X X symp = get_symbol("MAKEFLAGS", 0); X if (symp->svalue != NULL) X { X /* chop up the MAKEFLAGS and feed them to to make_args() */ X X envp = tstrcpy(symp->svalue); X envv = tokenize(envp); X for (i = 0; envv[i] != NULL; i++); X make_args(i, envv); X X /* free the vector of pointers, and the string itself, */ X /* since you cannot have macros, targets or makefiles */ X /* in the MAKEFLAGS macro. */ X X tfree(envv); X tfree(envp); X tfree(makeflags); /* ignore this, since we just read it */ X } X X make_args(--argc, ++argv); /* process command line options */ X X add_macro(makeflags, 0);/* update the MAKEFLAGS macro */ X tfree(makeflags); X X /* add command line macros, so they DON'T get overridden */ X X for (i = 0; mlist[i] != NULL; i++) X add_macro(mlist[i], 1); X X tfree(mlist); /* all done with macros */ X X if (opts.query) /* -q never executes anything */ X opts.noexec = 1; X X if (opts.noexec) X opts.touch = 0; /* -n never touches */ X X if (dispcount > 1) /* display `default.mk' on -DD */ X opts.display = 1; X X first_targ = NULL; /* used in parse() */ X X if (readdef) /* read in `default.mk' */ X parse(fopenp(MAKEINI, "r")); X X if (dispcount > 0) /* display makefile's on -D */ X opts.display = 1; X X first_targ = NULL; /* get first target in `makefile' */ X X /* parse the makefiles given on command line */ X for (i = 0; flist[i] != NULL; i++) X { X parse(equal(flist[i], "-") ? fdopen(dup(fileno(stdin)), "r") X : fopen(flist[i], "r")); X } X X /* no makefiles specified, so use "makefile" or "Makefile" */ X if (i == 0) X { X if (parse(fopen("makefile", "r")) == 0) X { X#ifndef MSDOS X parse(fopen("Makefile", "r")); X#endif X } X } X X tfree(flist); /* all done with makefile's */ X X /* find the current value of the $(MAKE) macro */ X symp = get_symbol("MAKE", 0); X opts.make = (symp->svalue == NULL) ? "make" : symp->svalue; X X if ((targp = get_target(".INIT")) != NULL) X build(targp->tshell); /* process the .INIT rule */ X X mk = 0; X X for (i = 0; tlist[i] != NULL; i++) X { X /* process command line arguments */ X mk |= (make(tlist[i], 1) > 0) ? 1 : 0; X } X X tfree(tlist); /* all done with targets */ X X /* if no targets specified, make the first one */ X if (i == 0 && first_targ) X mk |= (make(first_targ->tfile->fname, 1) > 0) ? 1 : 0; X X if ((targp = get_target(".DONE")) != NULL) X build(targp->tshell); /* process the .DONE rule */ X X return (mk & opts.query); /* not exit(); see new_make() */ X} X X X/* X * make_args - process the command line arguments X */ Xmake_args(argc, argv) Xint argc; Xchar **argv; X{ X REGISTER int tlen; X REGISTER int flen; X REGISTER int mlen; X REGISTER int no_k = 0; /* override the -k option */ X char *tmf; X int addflag; X X now = time(NULL); /* get current date & time */ X X makeflags = tstrcpy("MAKEFLAGS+="); X X tlen = flen = mlen = 0; X X for (; argc != 0; ++argv, --argc) X { X if (**argv != '-') X { X /* doesn't start with '-'; must be macro or target */ X X if (strchr(*argv, '=')) X { /* store as a macro */ X if (mlen == mmax) X mlist = grow_list(mlist, &mmax); X mlist[mlen++] = *argv; X } X else X { /* store as a target */ X if (tlen == tmax) X tlist = grow_list(tlist, &tmax); X tlist[tlen++] = *argv; X } X continue; X } X X /* must be an option */ X X tmf = tstrcat(makeflags, *argv); X X while (*argv && *++*argv) X { X addflag = 1; /* add to MAKEFLAGS */ X switch (**argv) X { X case 'd': /* show dependencies */ X addflag = 0; /* don't add to MAKEFLAGS */ X opts.depend++; X break; X X case 'D': /* display makefiles */ X dispcount++; X break; X X case 'e': /* don't override environment */ X opts.envirn = 1; X break; X X case 'f': /* new makefile name */ X addflag = 0; /* don't add to MAKEFLAGS */ X if (argc < 2) X usage(); X if (flen == fmax) X flist = grow_list(flist, &fmax); X ++argv, --argc; X flist[flen++] = *argv; X X *argv = NULL; X break; X X case 'i': /* ignore errors */ X opts.ignore = 1; X break; X X case 'k': /* give up on current target on error */ X opts.keepon = 1; X break; X X case 'n': /* don't execute commands */ X opts.noexec = 1; X break; X X case 'q': /* question mode */ X opts.query = 1; X break; X X case 'r': /* don't read default.mk */ X readdef = 0; X break; X X case 's': /* don't echo commands */ X opts.silent = 1; X break; X X case 'S': /* Undo -k option */ X no_k = 1; X break; X X case 't': /* touch files, don't build */ X opts.touch = 1; X break; X X default: X usage(); /* never returns */ X } X } X X if (addflag) X { X tfree(makeflags); X makeflags = tstrcat(tmf, " "); X } X X tfree(tmf); X } X X /* terminate all lists with a NULL pointer */ X X tlist[tlen] = NULL; X flist[flen] = NULL; X mlist[mlen] = NULL; X X /* check for -S over-riding -k option */ X if (no_k) X opts.keepon = 0; X X /* let the caller update the makeflags macro */ X} X X X/* X * grow_list - expand the list of pointers by a factor of two X */ Xchar ** Xgrow_list(list, len) Xchar **list; Xint *len; X{ X REGISTER int l; X X /* if list is NULL, start off with a default list */ X X if (list == NULL) X list = (char **) talloc(((l = 1) + 1) * sizeof(char *)); X else X { X l = *len; /* get current length */ X X list = (char **) trealloc((char *) list, X ((l <<= 1) + 1) * sizeof(char *)); X } X X if (list == NULL) X terror(1, "too many options"); X X /* if we are initially allocating it, set first pointer to NULL */ X X if (l == 1) X *list = NULL; X X *len = l; /* update current length */ X return (list); X} X X X/* X * fopenp - open file in current directory or along PATH X */ XFILE * Xfopenp(fname, type) Xchar *fname; Xchar *type; X{ X REGISTER int len; X REGISTER char *fpath; X FILE *fd; X char *path; X char *tp; X X /* try to open file relative to current directory */ X if ((fd = fopen(fname, type)) != NULL) X return (fd); X#ifndef MSDOS X /* didn't work, try home directory */ X if ((path = getenv("HOME")) != NULL) X { X fpath = talloc(strlen(path) + strlen(fname) + 2); X X strcpy(fpath, path); X len = strlen(fpath) - 1; X X /* make sure there is a separator between path and filename */ X X if (!strchr(FILE_SEPARATOR, fpath[len])) X fpath[++len] = '/'; X X strcpy(&fpath[len + 1], fname); /* attach the filename */ X fd = fopen(fpath, type); X tfree(fpath); X X if (fd != NULL) X return (fd); X } X#endif X /* didn't work, search along path */ X X if ((path = getenv("PATH")) == NULL) X return (NULL); X X path = tstrcpy(path); /* allocate string and copy */ X fpath = talloc(strlen(path) + strlen(fname) + 2); X X /* look for tokens separated by semi-colons (;) or colons (:) */ X X tp = token(path, PATH_SEPARATOR, NULL); X while (tp != NULL) X { X strcpy(fpath, tp); X len = strlen(fpath) - 1; X X /* make sure there is a separator between path and filename */ X X if (!strchr(FILE_SEPARATOR, fpath[len])) X fpath[++len] = '/'; X X strcpy(&fpath[len + 1], fname); /* attach the filename */ X if ((fd = fopen(fpath, type)) != NULL) X break; X X tp = token(NULL, PATH_SEPARATOR, NULL); X } X X tfree(path); X tfree(fpath); X X return (fd); X} X X X/* X * make - guts of the make command X * - make all pre-requisites, and if necessary, build target X * X * returns -1 target was already up to date w.r.t. pre-requisites X * 0 target has not been built X * 1 target is now built (and up to date) X */ Xmake(targname, worry) Xchar *targname; Xint worry; /* if set, it is an error to NOT build this */ X{ X REGISTER targptr targp; X REGISTER fileptr *preqp; X REGISTER int mk; X fileptr filep; X long targtime; X long preqtime; X X mk = 0; X X /* if recorded time of file is not default, we've already built it */ X filep = get_file(targname); X if (filep && filep->ftime != MAXNEGTIME) X return (1); X X targp = get_target(targname); /* find the target node */ X if (targp == NULL) X return (default_rule(targname, NULL, worry, 0)); X X /* keep actual time of current target */ X targtime = file_time(targname, 0); X X /* must build non-existant files, even with no pre-requisites */ X preqtime = MAXNEGTIME + 1; X X /* make all pre-requisites */ X preqp = targp->tpreq; X while (preqp && *preqp) X { X mk |= make((*preqp)->fname, worry); X X /* keep track of newest pre-requisite */ X if (preqtime < (*preqp)->ftime) X preqtime = (*preqp)->ftime; X X /* display as necessary */ X if (opts.depend > 1 || X (opts.depend && (*preqp)->ftime > targtime)) X { X display_prereq(targname, targtime, (*preqp)->fname, X (*preqp)->ftime); X } X X ++preqp; X } X X if (targp->tshell == NULL) /* try default rules anyway */ X { X if (default_rule(targname, targp, 0, preqtime > targtime)) X return (1); X return (mk); X } X else if (preqtime > targtime) X { X if (opts.touch) /* won't be set when `noexec' */ X touch_file(targname); X else X { X add_metas("", "", targname); X if (build(targp->tshell)) X return (0); X } X X targp->tfile->ftime = (opts.noexec) ? now X : file_time(targname, 1); X return (1); X } X X targp->tfile->ftime = targtime; X X return (mk); X} X X X/* X * default_rule - try the .SUFFIXES when we don't have an explicit target X * - if `worry' is set, it is an ERROR to NOT build this target X * - `mustbuild' is set if make() has out-of-date prereq's X * but no explicit shell rules X */ Xdefault_rule(targname, targetp, worry, mustbuild) Xchar *targname; Xtargptr targetp; Xint worry; Xint mustbuild; X{ X REGISTER targptr targp; X REGISTER fileptr *preqp; X fileptr filep; X char *ext; X char *basename; X char *preqname; X long targtime; X long preqtime; X int built; X char suffrule[80]; X X ext = strrchr(targname, '.'); /* find the extension */ X if (ext == NULL) X ext = targname + strlen(targname); X X basename = tstrncpy(targname, ext - targname); /* find the base name */ X X targtime = file_time(targname, 0); X X /* suffix_targ is used to (slightly) speed up this function */ X preqp = suffix_targ ? suffix_targ->tpreq : NULL; X built = 0; X X while (preqp && *preqp && !built) X { X /* look for a default rule from SUFFIX to `ext' */ X strcat(strcpy(suffrule, (*preqp)->fname), ext); X targp = get_target(suffrule); /* e.g. `.c.o' */ X X if (targp != NULL) X { X /* found a rule; see if file exists */ X preqname = get_preqname(targetp, (*preqp)->fname, X basename); X preqtime = file_time(preqname, 0); X X /* X * don't bother recursive makes unless necessary e.g. X * we have .c.o and .l.c, but also .l.o! we want to X * use .l.o if a .c file does not exist X */ X if (preqtime != MAXNEGTIME || mustbuild) X built = make(preqname, 0); X X /* check if pre-req file exists and is newer */ X preqtime = file_time(preqname, 0); X if (preqtime > targtime || (mustbuild && built)) X { X if (opts.depend) X { X display_prereq(targname, targtime, X preqname, preqtime); X } X X if (opts.touch) /* won't be set when `noexec' */ X touch_file(targname); X else X { X add_metas(basename, preqname, targname); X if (build(targp->tshell)) X return (0); X } X built = 1; X } X else if (opts.depend > 1 && preqtime != MAXNEGTIME) X { X display_prereq(targname, targtime, X preqname, preqtime); X } X X tfree(preqname); X } X X ++preqp; /* try next .SUFFIXES rule */ X } X X if (!built) X { X /* didn't find anything; try the default rule */ X targp = get_target(".DEFAULT"); X if (targp != NULL) X { X add_metas(basename, "", targname); X if (build(targp->tshell)) X return (0); X built = 1; X } X else if (targtime == MAXNEGTIME && worry) X terror(1, tstrcat("Don't know how to make ", targname)); X } X X tfree(basename); X X /* record the current file time */ X if ((filep = get_file(targname)) != NULL) X { X filep->ftime = (built == 1 && opts.noexec) ? now X : file_time(targname, 1); X } X X return (built ? built : ((targtime == MAXNEGTIME) ? 0 : 1)); X} X X X/* X * get_preqname - find prerequisite name from target and prerequisite suffix X */ Xchar * Xget_preqname(targp, suffix, basename) Xtargptr targp; Xchar *suffix; Xchar *basename; X{ X fileptr *preqp; X char *preqf; X char *basef; X int i; X X if (targp != NULL) X { X /* strip the directory name from the basename */ X basef = tsplit(basename, FILE_SEPARATOR, NULL); X X /* look through prerequisite list for file with right name */ X for (preqp = targp->tpreq; preqp && *preqp; ++preqp) X { X /* split the pre-requisite into dir and filenames */ X preqf = tsplit((*preqp)->fname, FILE_SEPARATOR, NULL); X X /* see if the filename part matches the target */ X for (i = 0; preqf[i] != '\0'; i++) X { X if (preqf[i] != basef[i]) X break; X } X X /* if we differed only on the suffix, we're okay */ X if (strcmp(preqf + i, suffix) == 0) X return (tstrcpy((*preqp)->fname)); X } X#ifdef ALL_PREQS X /* didn't find a matching basename + suffix in the preq-list. */ X /* look through prerequisite list for file with right suffix. */ X for (preqp = targp->tpreq; preqp && *preqp; ++preqp) X { X preqf = strrchr((*preqp)->fname, '.'); X if (preqf == NULL) X continue; X X /* take the first file which has right suffix */ X if (strcmp(suffix, preqf) == 0) X return (tstrcpy((*preqp)->fname)); X } X#endif /* ALL_PREQS */ X } X X /* didn't find one, so try forming one using basename + suffix */ X X return (tstrcat(basename, suffix)); X} X X X/* X * add_metas - add symbols for $*, $< and $@ X */ Xadd_metas(basename, preqname, targname) Xchar *basename; Xchar *preqname; Xchar *targname; X{ X /* $* is the basename */ X add_symbol("*", basename, 0); X split_meta("*", basename); X X add_symbol("<", preqname, 0); X split_meta("<", preqname); X X add_symbol("@", targname, 0); X split_meta("@", targname); X} X X X/* X * split_meta - split a metasymbol into Directory and File parts X */ Xsplit_meta(sym, name) Xchar *sym; Xchar *name; X{ X char *dname; X char *dsym; X char *fsym; X X /* construct the macro names (e.g. $(*D), $(@F)) */ X dsym = tstrcat(sym, "D"); X fsym = tstrcat(sym, "F"); X X add_symbol(fsym, tsplit(name, FILE_SEPARATOR, &dname), 0); X X if (dname == NULL) X add_symbol(dsym, ".", 0); X else X { X add_symbol(dsym, dname, 0); X tfree(dname); X } X X tfree(dsym); X tfree(fsym); X} X X X/* X * touch_file - set the MODIFICATION time of the file to NOW X */ Xtouch_file(targname) Xchar *targname; X{ X REGISTER int handle; X X#ifndef MSDOS X time_t timep[2]; X X time(&timep[0]); X timep[1] = timep[0]; X handle = utime(targname, timep); X#else X handle = utime(targname, NULL); X#endif X fputs("touch ", stdout); X puts(targname); X X if (handle == 0) X return; X X /* create the file, if it did not exist */ X if (errno == ENOENT) X { X handle = open(targname, O_CREAT | O_TRUNC, S_IWRITE); X if (handle != -1) X { X close(handle); X return; X } X } X X perror("touch"); X exit(1); X} X Xdisplay_prereq(targname, targtime, preqname, preqtime) Xchar *targname; Xlong targtime; Xchar *preqname; Xlong preqtime; X{ X#ifdef MSDOS X char chtime[10]; X X fputs(targname, stdout); X fputs(" (", stdout); X fputs(ltoa(targtime, chtime, 16), stdout); X fputs((targtime <= preqtime) ? ") older than " : ") newer than ", stdout); X fputs(preqname, stdout); X fputs(" (", stdout); X fputs(ltoa(preqtime, chtime, 16), stdout); X puts(")"); X#else X printf("%s (%08lx) %s than %s (%08lx)\n", X targname, targtime, X (targtime < preqtime) ? "older" : "newer", X preqname, preqtime); X#endif X} X X Xlong Xfile_time(fname, built) Xchar *fname; Xint built; X{ X struct stat sbuf; X X /* X * if the file is supposedly built, but still does not exists, just X * fake it by returning the current time. X */ X if (stat(fname, &sbuf) != 0) X return (built ? now : MAXNEGTIME); X return (sbuf.st_mtime); X} X X Xusage() X{ X puts("make [-f filename] [-dDiknqrsSt] [target ...] [macro=value ...]"); X exit(1); X} END_OF_FILE if test 17680 -ne `wc -c <'make.c'`; then echo shar: \"'make.c'\" unpacked with wrong size! fi # end of 'make.c' fi if test -f 'tstring.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'tstring.c'\" else echo shar: Extracting \"'tstring.c'\" \(5471 characters\) sed "s/^X//" >'tstring.c' <<'END_OF_FILE' X/* X * tstring.c X * X * 88-10-01 v1.0 created by greg yachuk, placed in the public domain X * 88-10-06 v1.1 changed prerequisite list handling X * 88-11-11 v1.2 fixed some bugs and added environment variables X * 89-07-12 v1.3 stop appending shell commands, and flush output X * 89-08-01 v1.4 AB lots of new options and code X * 89-10-30 v1.5 greggy -f -S -q options, took some changes from v1.4 X * X */ X#include X#include X#include X#include X X#include "tstring.h" X X Xchar * Xtalloc(n) Xint n; X{ X register char *s; X X s = malloc(n); X if (s == NULL) X terror(1, "no free memory"); X return (s); X} X X Xchar * Xtrealloc(s, n) Xregister char *s; Xint n; X{ X s = realloc(s, n); X if (s == NULL) X talloc(n); /* force an error */ X return (s); X} X X Xchar * Xtstrncpy(s, n) Xregister char *s; Xint n; X{ X s = strncpy(talloc(n + 1), s, n); X s[n] = '\0'; X return (s); X} X X Xterror(n, s) Xint n; Xchar *s; X{ X fputs("Make: ", stderr); X fputs(s, stderr); X putc('\n', stderr); X if (n) X exit(n); X} X X X/* X * tstrspan - move to the end of a quoted string, ignoring escaped quotes X */ Xchar * Xtstrspan(str) Xchar *str; X{ X char quote; X X if (*str != '\'' && *str != '"') X return (str + 1); X X quote = *str++; X X while (*str && *str != quote) X { X /* check for escaped quote */ X if (*str == '\\' && str[1] == quote) X ++str; X ++str; X } X X return (str); X} X X X/* X * tunquote - remove quotes from a string X */ Xchar * Xtunquote(str) Xchar *str; X{ X char *s; X char *d; X X d = s = str; X X while (*s) X { X while (*s && *s == '"') X ++s; X X while (*s && *s != '"') X *d++ = *s++; X } X X *d = '\0'; X X return (str); X} X X X/* X * tsplit - split a string into two components, normally a directory X * path and a filename. If a pointer to a directory is X * supplied, a string is allocated to contain the directory. X * The filename is returned as a pointer into the supplied X * string. X */ Xchar * Xtsplit(s, seps, dp) Xchar *s; Xchar *seps; Xchar **dp; X{ X char *d; /* directory portion */ X char *f; /* file portion */ X X d = s; X X /* find the final separator */ X while ((f = strpbrk(d, seps)) != NULL) X d = f + 1; X X /* back up to final component */ X f = d; X X /* if we are still at the beginning, there was no Directory */ X if (d == s || dp == NULL) X d = NULL; X else X { X int len; X X /* X * by the time we get here, d points to the final separator X * char. we can substitute a NULL for this sep-char. Thus, X * we don't need to add 1 in the following length X * calculation. X */ X len = d - s; X X d = talloc(len); X d[--len] = '\0'; X while (--len >= 0) X d[len] = s[len]; X } X X if (dp != NULL) X *dp = d; X X return (f); X} X X X/* X * token - take an input string and return a token each call X * - default token delimiter characters are `isspace()' X * - separator chars are in addition to `isspace()' X * - text between quotes (" and ') is a single token X * - if requested, the separator char is returned X * X * called as s = token(string, seps, &schar); X * or s = token(string, NULL, NULL); X * X * followed by s = token(NULL, seps, NULL); X * or s = token(NULL, NULL, &schar); X * X * returns NULL when no more tokens are available X */ Xchar * Xtoken(s, sep, schar) Xchar *s; Xchar *sep; Xchar *schar; X{ X static char *olds = NULL; X X if (s) X olds = s; /* we are starting all over again */ X X if (schar) X *schar = '\0'; X X if (!olds || !*olds) X return (NULL); /* no tokens left */ X X while (isspace(*olds) || (sep && strchr(sep, *olds))) X ++olds; /* skip leading spaces and sep's */ X X if (*olds == NULL) X return (NULL); /* remainder is all separator's */ X X s = olds; X X while (*olds) X { X if (isspace(*olds) || (sep && strchr(sep, *olds))) X { X if (schar) X *schar = *olds; X *olds++ = '\0'; /* delimit the token */ X return (s); X } X else if (*olds == '"' || *olds == '\'') X { X olds = tstrspan(olds); X if (*olds != '\0') X ++olds; /* didn't hit eos, so skip quote */ X } X else X ++olds; /* otherwise, pass over char */ X } X X olds = NULL; X return (s); /* return last token */ X} X X X/* X * tokenize - chop a string up into an array of (char *)'s X */ Xchar ** Xtokenize(input) Xchar *input; X{ X char **argv; X register int argc = 0; X register int alen; X X alen = 20; /* good initial guess */ X argv = (char **) talloc((alen + 1) * sizeof(char *)); X X input = token(input, NULL, NULL); /* use default separators */ X while (input) X { X if (alen == argc) X argv = (char **) trealloc((char *) argv, X (alen <<= 1) * sizeof(char *)); X argv[argc++] = input; X input = token(NULL, NULL, NULL); X } X X argv[argc] = NULL; /* mark end of array */ X X return (argv); X} X X X/* X * tgets - read input, swallowing escaped newlines as necessary X */ Xchar * Xtgets(fd) XFILE *fd; X{ X static char *input = NULL; X static int inlen = 0; X register char *ep; X int len; X X if (inlen == 0) X input = talloc(inlen = 162); X X input[inlen - 2] = '\n'; X ep = input - 1; X while ((fgets(input, inlen, fd)) != NULL) X { X for (;;) X { X while (input[inlen - 2] != '\n' && input[inlen - 2] != '\0') X { X len = inlen; X input = trealloc(input, inlen <<= 1); X ep = &input[len - 2]; X input[inlen - 2] = '\n'; X fgets(ep + 1, len + 1, fd); X } X X while (*++ep); X *--ep = '\0'; X do X { X --ep; X } while (ep >= input && isspace(*ep)); X X if (ep > input && *ep == '\\' && *--ep != '\\') X fgets(ep + 1, inlen - (ep - input) - 1, fd); X else X break; X } X X return (input); X } X X inlen = 0; X tfree(input); X input = NULL; X X return (NULL); X} END_OF_FILE if test 5471 -ne `wc -c <'tstring.c'`; then echo shar: \"'tstring.c'\" unpacked with wrong size! fi # end of 'tstring.c' fi echo shar: End of archive 1 \(of 3\). cp /dev/null ark1isdone MISSING="" for I in 1 2 3 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have unpacked all 3 archives. rm -f ark[1-9]isdone else echo You still need to unpack the following archives: echo " " ${MISSING} fi ## End of shell archive. exit 0