diff --git a/util/make/.distr b/util/make/.distr new file mode 100644 index 000000000..97e95bfb6 --- /dev/null +++ b/util/make/.distr @@ -0,0 +1,10 @@ +Makefile +README +check.c +h.h +input.c +macro.c +main.c +make.c +reader.c +rules.c diff --git a/util/make/Makefile b/util/make/Makefile new file mode 100644 index 000000000..1e98ebcae --- /dev/null +++ b/util/make/Makefile @@ -0,0 +1,29 @@ +# Makefile for make! +EMHOME=../.. + +OBJS = check.o input.o macro.o main.o \ + make.o reader.o rules.o +# unix not defined under Xenix ??? +CFLAGS = -Dunix + +all: make + +install: make + cp make $(EMHOME)/bin/make + +cmp: make + cmp make $(EMHOME)/bin/make + +clean: + rm -f *.o make + +pr: + @pr README Makefile h.h main.c check.c input.c macro.c make.c reader.c rules.c + +opr: + make pr ^ opr + +make: $(OBJS) + $(CC) -i -o make $(OBJS) + +$(OBJS): h.h diff --git a/util/make/README b/util/make/README new file mode 100644 index 000000000..2c8d12f1b --- /dev/null +++ b/util/make/README @@ -0,0 +1,42 @@ +Following is a repost of the public domain 'make' that I posted +to net.sources a couple of months ago. I have fixed a few bugs, and +added some more features, and the resulting changes amounted to +about as much text as the whole program (hence the repost). + +For those that missed the net.sources posting, this is a public domain +re-implementation of the UNIX make program. There is no manual included; +for documentation, refer to a UNIX manual, or the source. + +Here is a list of the changes made: + +i) If '-' (ignore) or '@' (silent) where used at the start + of a command, their effect was not turned off for the following + commands. +ii) A special target (.SUFFIXES, .PRECIOUS) or a rule (.c.o, .a.o), + if first in the file would be taken as the default target. + This resulted in error messages like "Don't know how to + make .c", because things like .SUFFIXES were being made. + This was further complicated by --- +iii) Special target lines with no dependents (ie. .SUFFIXES:\n) + were not clearing out the existing dependents like + they should. +iv) Default rules could not be redefined because of the error + checking for commands being defined twice. Now you are + allowed to define a target beinging with '.', having + no dependents with commands. +v) The -q option didn't do the time comparison correctly, + or clear the variable used to keep track of this. Thus + it didn't work very well. +vi) The syntax ${..} for macro's supported by UNIX make was + not supported. +vii) There wuz a couple of spelling errors. +viii) When make checked for implicit rules on targets without + a suffix, there were problems. (Note: The ~ feature of + UNIX make wasn't and still isn't supported) +ix) The -n option did not print @ lines like it was supposed to. +x) :: added. (See UNIX manual) +xi) $? added. (see UNIX manual) + +Hacked further by Ceriel Jacobs to make it work better. Use this "make" to +install ACK under Microsoft Xenix V3.2. Some of the makefiles are just too +big for the Xenix "make". Strange, they work on a PDP-11 ... diff --git a/util/make/check.c b/util/make/check.c new file mode 100644 index 000000000..2951c4096 --- /dev/null +++ b/util/make/check.c @@ -0,0 +1,117 @@ +/* + * Check structures for make. + * + * $Header$ + */ + +#include +#include "h.h" + + +/* + * Prints out the structures as defined in memory. Good for check + * that you make file does what you want (and for debugging make). + */ +void +prt() +{ + register struct name * np; + register struct depend * dp; + register struct line * lp; + register struct cmd * cp; + register struct macro * mp; + + + for (mp = macrohead; mp; mp = mp->m_next) + fprintf(stderr, "%s = %s\n", mp->m_name, mp->m_val); + + fputc('\n', stderr); + + for (np = namehead.n_next; np; np = np->n_next) + { + if (np->n_flag & N_DOUBLE) + fprintf(stderr, "%s::\n", np->n_name); + else + fprintf(stderr, "%s:\n", np->n_name); + if (np == firstname) + fprintf(stderr, "(MAIN NAME)\n"); + for (lp = np->n_line; lp; lp = lp->l_next) + { + fputc(':', stderr); + for (dp = lp->l_dep; dp; dp = dp->d_next) + fprintf(stderr, " %s", dp->d_name->n_name); + fputc('\n', stderr); + + for (cp = lp->l_cmd; cp; cp = cp->c_next) +#ifdef os9 + fprintf(stderr, "- %s\n", cp->c_cmd); +#else + fprintf(stderr, "-\t%s\n", cp->c_cmd); +#endif + fputc('\n', stderr); + } + fputc('\n', stderr); + } +} + + +/* + * Recursive routine that does the actual checking. + */ +void +check(np) +struct name * np; +{ + register struct depend * dp; + register struct line * lp; + + + if (np->n_flag & N_MARK) + fatal("Circular dependency from %s", np->n_name); + + np->n_flag |= N_MARK; + + for (lp = np->n_line; lp; lp = lp->l_next) + for (dp = lp->l_dep; dp; dp = dp->d_next) + check(dp->d_name); + + np->n_flag &= ~N_MARK; +} + + +/* + * Look for circular dependancies. + * ie. + * a: b + * b: a + * is a circular dep + */ +void +circh() +{ + register struct name * np; + + + for (np = namehead.n_next; np; np = np->n_next) + check(np); +} + + +/* + * Check the target .PRECIOUS, and mark its dependentd as precious + */ +void +precious() +{ + register struct depend * dp; + register struct line * lp; + register struct name * np; + + + if (!((np = newname(".PRECIOUS"))->n_flag & N_TARG)) + return; + + for (lp = np->n_line; lp; lp = lp->l_next) + for (dp = lp->l_dep; dp; dp = dp->d_next) + dp->d_name->n_flag |= N_PREC; +} diff --git a/util/make/h.h b/util/make/h.h new file mode 100644 index 000000000..1dcab8998 --- /dev/null +++ b/util/make/h.h @@ -0,0 +1,145 @@ +/* + * Include header for make + * + * $Header$ + */ + + +#ifndef uchar +#ifdef os9 +#define uchar char +#define void int +#define fputc putc +#else +#define uchar unsigned char +#endif +#endif + +#define bool int +#define time_t long +#define TRUE (1) +#define FALSE (0) +#define max(a,b) ((a)>(b)?(a):(b)) + +#define DEFN1 "makefile" /* Default names */ +#ifdef unix +#define DEFN2 "Makefile" +#endif +#ifdef eon +#define DEFN2 "Makefile" +#endif +/* os9 is case insensitive */ + +#define LZ (2048) /* Line size */ + + + +/* + * A name. This represents a file, either to be made, or existant + */ + +struct name +{ + struct name * n_next; /* Next in the list of names */ + char * n_name; /* Called */ + struct line * n_line; /* Dependencies */ + time_t n_time; /* Modify time of this name */ + uchar n_flag; /* Info about the name */ +}; + +#define N_MARK 0x01 /* For cycle check */ +#define N_DONE 0x02 /* Name looked at */ +#define N_TARG 0x04 /* Name is a target */ +#define N_PREC 0x08 /* Target is precious */ +#define N_DOUBLE 0x10 /* Double colon target */ + +/* + * Definition of a target line. + */ +struct line +{ + struct line * l_next; /* Next line (for ::) */ + struct depend * l_dep; /* Dependents for this line */ + struct cmd * l_cmd; /* Commands for this line */ +}; + + +/* + * List of dependents for a line + */ +struct depend +{ + struct depend * d_next; /* Next dependent */ + struct name * d_name; /* Name of dependent */ +}; + + +/* + * Commands for a line + */ +struct cmd +{ + struct cmd * c_next; /* Next command line */ + char * c_cmd; /* Command line */ +}; + + +/* + * Macro storage + */ +struct macro +{ + struct macro * m_next; /* Next variable */ + char * m_name; /* Called ... */ + char * m_val; /* Its value */ + uchar m_flag; /* Infinite loop check */ + uchar m_prio; /* 5 levels: + - 0 for internal ($(CC), etc) + - 1 (reserved for environment) + - 2 for makefile + - 3 for command line + - 4 for special ($*,$<, etc) + */ +}; + +extern char * myname; +extern struct name namehead; +extern struct macro * macrohead; +extern struct name * firstname; +extern bool silent; +extern bool ignore; +extern bool rules; +extern bool dotouch; +extern bool quest; +extern bool domake; +extern char str1[]; +extern char str2[]; +extern int lineno; + +char * fgets(); +char * index(); +char * rindex(); +char * malloc(); +char * strcpy(); +char * strcat(); +extern int errno; + +void circh(); +char * getmacro(); +struct macro * setmacro(); +void input(); +void error(); +void expand(); +void fatal(); +int make(); +void modtime(); +struct name * newname(); +struct depend * newdep(); +struct cmd * newcmd(); +void newline(); +void prt(); +char * suffix(); +void touch(); +void makerules(); +char * gettok(); +void precious(); diff --git a/util/make/input.c b/util/make/input.c new file mode 100644 index 000000000..3e4b88e51 --- /dev/null +++ b/util/make/input.c @@ -0,0 +1,342 @@ +/* + * Parse a makefile + * + * $Header$ + */ + + +#include +#include +#include "h.h" + + +struct name namehead; +struct name * firstname; + +char str1[LZ]; /* General store */ +char str2[LZ]; + + +/* + * Intern a name. Return a pointer to the name struct + */ +struct name * +newname(name) +char * name; +{ + register struct name * rp; + register struct name * rrp; + register char * cp; + + + for + ( + rp = namehead.n_next, rrp = &namehead; + rp; + rp = rp->n_next, rrp = rrp->n_next + ) + if (strcmp(name, rp->n_name) == 0) + return rp; + + if ((rp = (struct name *)malloc(sizeof (struct name))) + == (struct name *)0) + fatal("No memory for name"); + rrp->n_next = rp; + rp->n_next = (struct name *)0; + if ((cp = malloc((unsigned)(strlen(name)+1))) == (char *)0) + fatal("No memory for name"); + strcpy(cp, name); + rp->n_name = cp; + rp->n_line = (struct line *)0; + rp->n_time = (time_t)0; + rp->n_flag = 0; + + return rp; +} + + +/* + * Add a dependant to the end of the supplied list of dependants. + * Return the new head pointer for that list. + */ +struct depend * +newdep(np, dp) +struct name * np; +struct depend * dp; +{ + register struct depend * rp; + register struct depend * rrp; + + + if ((rp = (struct depend *)malloc(sizeof (struct depend))) + == (struct depend *)0) + fatal("No memory for dependant"); + rp->d_next = (struct depend *)0; + rp->d_name = np; + + if (dp == (struct depend *)0) + return rp; + + for (rrp = dp; rrp->d_next; rrp = rrp->d_next) + ; + + rrp->d_next = rp; + + return dp; +} + + +/* + * Add a command to the end of the supplied list of commands. + * Return the new head pointer for that list. + */ +struct cmd * +newcmd(str, cp) +char * str; +struct cmd * cp; +{ + register struct cmd * rp; + register struct cmd * rrp; + register char * rcp; + + + if (rcp = rindex(str, '\n')) + *rcp = '\0'; /* Loose newline */ + + while (isspace(*str)) + str++; + + if (*str == '\0') /* If nothing left, the exit */ + return 0; + + if ((rp = (struct cmd *)malloc(sizeof (struct cmd))) + == (struct cmd *)0) + fatal("No memory for command"); + rp->c_next = (struct cmd *)0; + if ((rcp = malloc((unsigned)(strlen(str)+1))) == (char *)0) + fatal("No memory for command"); + strcpy(rcp, str); + rp->c_cmd = rcp; + + if (cp == (struct cmd *)0) + return rp; + + for (rrp = cp; rrp->c_next; rrp = rrp->c_next) + ; + + rrp->c_next = rp; + + return cp; +} + + +/* + * Add a new 'line' of stuff to a target. This check to see + * if commands already exist for the target. If flag is set, + * the line is a double colon target. + * + * Kludges: + * i) If the new name begins with a '.', and there are no dependents, + * then the target must cease to be a target. This is for .SUFFIXES. + * ii) If the new name begins with a '.', with no dependents and has + * commands, then replace the current commands. This is for + * redefining commands for a default rule. + * Neither of these free the space used by dependents or commands, + * since they could be used by another target. + */ +void +newline(np, dp, cp, flag) +struct name * np; +struct depend * dp; +struct cmd * cp; +{ + bool hascmds = FALSE; /* Target has commands */ + register struct line * rp; + register struct line * rrp; + + + /* Handle the .SUFFIXES case */ + if (! strcmp(np->n_name, ".SUFFIXES") && !dp && !cp) + { + for (rp = np->n_line; rp; rp = rrp) + { + rrp = rp->l_next; + free((char *)rp); + } + np->n_line = (struct line *)0; + np->n_flag &= ~N_TARG; + return; + } + + /* This loop must happen since rrp is used later. */ + for + ( + rp = np->n_line, rrp = (struct line *)0; + rp; + rrp = rp, rp = rp->l_next + ) + if (rp->l_cmd) + hascmds = TRUE; + + if (hascmds && cp && !(np->n_flag & N_DOUBLE)) + /* Handle the implicit rules redefinition case */ + if (np->n_name[0] == '.' && dp == (struct depend *)0) + { + np->n_line->l_cmd = cp; + return; + } + else + error("Commands defined twice for target %s", np->n_name); + if (np->n_flag & N_TARG) + if (!(np->n_flag & N_DOUBLE) != !flag) /* like xor */ + error("Inconsistent rules for target %s", np->n_name); + + if ((rp = (struct line *)malloc(sizeof (struct line))) + == (struct line *)0) + fatal("No memory for line"); + rp->l_next = (struct line *)0; + rp->l_dep = dp; + rp->l_cmd = cp; + + if (rrp) + rrp->l_next = rp; + else + np->n_line = rp; + + np->n_flag |= N_TARG; + if (flag) + np->n_flag |= N_DOUBLE; +} + + +/* + * Parse input from the makefile, and construct a tree structure + * of it. + */ +void +input(fd) +FILE * fd; +{ + char * p; /* General */ + char * q; + struct name * np; + struct depend * dp; + struct cmd * cp; + bool dbl; + + + if (getline(str1, fd)) /* Read the first line */ + return; + + for(;;) + { +#ifdef os9 + if (*str1 == ' ') /* Rules without targets */ +#else + if (*str1 == '\t') /* Rules without targets */ +#endif + error("Rules not allowed here"); + + p = str1; + + while (isspace(*p)) /* Find first target */ + p++; + + while (((q = index(p, '=')) != (char *)0) && + (p != q) && (q[-1] == '\\')) /* Find value */ + { + register char * a; + + a = q - 1; /* Del \ chr; move rest back */ + p = q; + while(*a++ = *q++) + ; + } + + if (q != (char *)0) + { + register char * a; + + *q++ = '\0'; /* Separate name and val */ + while (isspace(*q)) + q++; + if (p = rindex(q, '\n')) + *p = '\0'; + + p = str1; + if ((a = gettok(&p)) == (char *)0) + error("No macro name"); + + setmacro(a, q, 2); + + if (getline(str1, fd)) + return; + continue; + } + + expand(str1); + p = str1; + + while (((q = index(p, ':')) != (char *)0) && + (p != q) && (q[-1] == '\\')) /* Find dependents */ + { + register char * a; + + a = q - 1; /* Del \ chr; move rest back */ + p = q; + while(*a++ = *q++) + ; + } + + if (q == (char *)0) + error("No targets provided"); + + *q++ = '\0'; /* Separate targets and dependents */ + + if (*q == ':') /* Double colon */ + { + dbl = 1; + q++; + } + else + dbl = 0; + + for (dp = (struct depend *)0; ((p = gettok(&q)) != (char *)0);) + /* get list of dep's */ + { + np = newname(p); /* Intern name */ + dp = newdep(np, dp); /* Add to dep list */ + } + + *((q = str1) + strlen(str1) + 1) = '\0'; + /* Need two nulls for gettok (Remember separation) */ + + cp = (struct cmd *)0; + if (getline(str2, fd) == FALSE) /* Get commands */ + { +#ifdef os9 + while (*str2 == ' ') +#else + while (*str2 == '\t') +#endif + { + cp = newcmd(&str2[0], cp); + if (getline(str2, fd)) + break; + } + } + + while ((p = gettok(&q)) != (char *)0) /* Get list of targ's */ + { + np = newname(p); /* Intern name */ + newline(np, dp, cp, dbl); + if (!firstname && p[0] != '.') + firstname = np; + } + + if (feof(fd)) /* EOF? */ + return; + + strcpy(str1, str2); + } +} diff --git a/util/make/macro.c b/util/make/macro.c new file mode 100644 index 000000000..5c4b76de9 --- /dev/null +++ b/util/make/macro.c @@ -0,0 +1,167 @@ +/* + * Macro control for make + * + * $Header$ + */ + + +#include "h.h" + + +struct macro * macrohead; + + +struct macro * +getmp(name) +char * name; +{ + register struct macro * rp; + + for (rp = macrohead; rp; rp = rp->m_next) + if (strcmp(name, rp->m_name) == 0) + return rp; + return (struct macro *)0; +} + + +char * +getmacro(name) +char * name; +{ + struct macro * mp; + + if (mp = getmp(name)) + return mp->m_val; + else + return ""; +} + + +struct macro * +setmacro(name, val, prio) +char * name; +char * val; +{ + register struct macro * rp; + register char * cp; + + + /* Replace macro definition if it exists */ + for (rp = macrohead; rp; rp = rp->m_next) + if (strcmp(name, rp->m_name) == 0) + { + if (prio < rp->m_prio) + return rp; + free(rp->m_val); /* Free space from old */ + break; + } + + if (!rp) /* If not defined, allocate space for new */ + { + if ((rp = (struct macro *)malloc(sizeof (struct macro))) + == (struct macro *)0) + fatal("No memory for macro"); + + rp->m_next = macrohead; + macrohead = rp; + rp->m_flag = FALSE; + + if ((cp = malloc((unsigned)(strlen(name)+1))) == (char *)0) + fatal("No memory for macro"); + strcpy(cp, name); + rp->m_name = cp; + } + + if ((cp = malloc((unsigned)(strlen(val)+1))) == (char *)0) + fatal("No memory for macro"); + strcpy(cp, val); /* Copy in new value */ + rp->m_val = cp; + rp->m_prio = prio; + + return rp; +} + +#define MBUFSIZ 128 + +/* + * Do the dirty work for expand + */ +void +doexp(to, from, len, buf) +char ** to; +char * from; +int * len; +char * buf; +{ + register char * rp; + register char * p; + register char * q; + register struct macro * mp; + + + rp = from; + p = *to; + while (*rp) + { + if (*rp != '$') + { + *p++ = *rp++; + (*len)--; + } + else + { + q = buf; + if (*++rp == '{') + while (*++rp && *rp != '}') { + if (q < &buf[MBUFSIZ-1]) *q++ = *rp; + } + else if (*rp == '(') + while (*++rp && *rp != ')') { + if (q < &buf[MBUFSIZ-1]) *q++ = *rp; + } + else if (!*rp) + { + *p++ = '$'; + break; + } + else + *q++ = *rp; + *q = '\0'; + if (*rp) + rp++; + if (!(mp = getmp(buf))) + mp = setmacro(buf, "", 2); + if (mp->m_flag) + fatal("Infinitely recursive macro %s", mp->m_name); + mp->m_flag = TRUE; + *to = p; + doexp(to, mp->m_val, len, buf); + p = *to; + mp->m_flag = FALSE; + } + if (*len <= 0) + error("Expanded line too line"); + } + *p = '\0'; + *to = p; +} + + +/* + * Expand any macros in str. + */ +void +expand(str) +char * str; +{ + char *a; + static char b[MBUFSIZ]; /* temp storage for macroname */ + char * p = str; + int len = LZ-1; + + a = malloc((unsigned)(strlen(str)+1)); + if (!a) fatal("No memory for expand"); + strcpy(a, str); + doexp(&p, a, &len, b); + free(a); +} diff --git a/util/make/main.c b/util/make/main.c new file mode 100644 index 000000000..68a31c481 --- /dev/null +++ b/util/make/main.c @@ -0,0 +1,261 @@ +/* + * make [-f makefile] [-ins] [target(s) ...] + * + * (Better than EON mk but not quite as good as UNIX make) + * + * -f makefile name + * -i ignore exit status + * -n Pretend to make + * -p Print all macros & targets + * -q Question up-to-dateness of target. Return exit status 1 if not + * -r Don't not use inbuilt rules + * -s Make silently + * -t Touch files instead of making them + * -m Change memory requirements (EON only) + * -k For the time being: accept but ignore + * + * $Header$ + */ + +#include +#include "h.h" + +#ifdef unix +#include +#endif +#ifdef eon +#include +#endif +#ifdef os9 +#include +#endif + + +#ifdef eon +#define MEMSPACE (16384) +#endif + + +char * myname; +char * makefile; /* The make file */ +#ifdef eon +unsigned memspace = MEMSPACE; +#endif + +FILE * ifd; /* Input file desciptor */ +bool domake = TRUE; /* Go through the motions option */ +bool ignore = FALSE; /* Ignore exit status option */ +bool silent = FALSE; /* Silent option */ +bool print = FALSE; /* Print debuging information */ +bool rules = TRUE; /* Use inbuilt rules */ +bool dotouch = FALSE;/* Touch files instead of making */ +bool quest = FALSE; /* Question up-to-dateness of file */ + + +void +main(argc, argv) +int argc; +char ** argv; +{ + register char * p; /* For argument processing */ + int estat = 0; /* For question */ + register struct name * np; + int nargc = 0; + char **nargv; + int fflag = 0; + + + myname = (argc-- < 1) ? "make" : *argv++; + nargv = argv; + + while (argc > 0) + { + argc--; /* One less to process */ + p = *argv++; /* Now processing this one */ + + if (*p == '-') while (*++p != '\0') + { + switch(*p) + { + case 'f': /* Alternate file name */ + fflag = 1; + break; +#ifdef eon + case 'm': /* Change space requirements */ + if (*++p == '\0') + { + if (argc-- <= 0) + usage(); + p = *argv++; + } + memspace = atoi(p); + goto end_of_args; +#endif + case 'n': /* Pretend mode */ + domake = FALSE; + break; + case 'i': /* Ignore fault mode */ + ignore = TRUE; + break; + case 's': /* Silent about commands */ + silent = TRUE; + break; + case 'p': + print = TRUE; + break; + case 'r': + rules = FALSE; + break; + case 't': + dotouch = TRUE; + break; + case 'q': + quest = TRUE; + break; + case 'k': + break; + default: /* Wrong option */ + usage(); + } + } + else { + if (fflag) { + if (argc <= 0) usage(); + makefile = p; + fflag = 0; + } + else { + nargc++; + *nargv++ = p; + } + } + end_of_args:; + } + argv = nargv - nargc; + argc = nargc; + +#ifdef eon + if (initalloc(memspace) == 0xffff) /* Must get memory for alloc */ + fatal("Cannot initalloc memory"); +#endif + + if (makefile && strcmp(makefile, "-") == 0) /* Can use stdin as makefile */ + ifd = stdin; + else + if (!makefile) /* If no file, then use default */ + { + if ((ifd = fopen(DEFN1, "r")) == (FILE *)0) +#ifdef eon + if (errno != ER_NOTF) + fatal("Can't open %s; error %02x", DEFN1, errno); +#endif +#ifdef unix + if (errno != ENOENT) + fatal("Can't open %s; error %02x", DEFN1, errno); +#endif +#ifndef os9 + if ((ifd == (FILE *)0) + && ((ifd = fopen(DEFN2, "r")) == (FILE *)0)) + fatal("Can't open %s", DEFN2); +#else + fatal("Can't open %s", DEFN1); +#endif + } + else + if ((ifd = fopen(makefile, "r")) == (FILE *)0) + fatal("Can't open %s", makefile); + + makerules(); + + setmacro("$", "$", 4); + + while (argc && (p = index(*argv, '='))) + { + char c; + + c = *p; + *p = '\0'; + setmacro(*argv, p+1, 3); + *p = c; + + argv++; + argc--; + } + + input(ifd); /* Input all the gunga */ + fclose(ifd); /* Finished with makefile */ + lineno = 0; /* Any calls to error now print no line number */ + + if (print) + prt(); /* Print out structures */ + + np = newname(".SILENT"); + if (np->n_flag & N_TARG) + silent = TRUE; + + np = newname(".IGNORE"); + if (np->n_flag & N_TARG) + ignore = TRUE; + + precious(); + + if (!firstname) + fatal("No targets defined"); + + circh(); /* Check circles in target definitions */ + + if (!argc) + estat = make(firstname, 0); + else while (argc--) + { + if (!print && !silent && strcmp(*argv, "love") == 0) + printf("Not war!\n"); + estat |= make(newname(*argv++), 0); + } + + if (quest) + exit(estat); + else + exit(0); +} + + +usage() +{ + fprintf(stderr, "Usage: %s [-f makefile] [-inpqrst] [macro=val ...] [target(s) ...]\n", myname); + exit(1); +} + + +/*VARARGS1*/ +void +fatal(msg, a1, a2, a3, a4, a5, a6) +char *msg; +{ + fprintf(stderr, "%s: ", myname); + fprintf(stderr, msg, a1, a2, a3, a4, a5, a6); + fputc('\n', stderr); + exit(1); +} + +char * +index(s, c) + register char *s, c; +{ + while (*s) + if (*s++ == c) + return --s; + return (char *)0; +} + +char * +rindex(str, chr) + register char *str, chr; +{ + register char *retptr = 0; + + while (*str) + if (*str++ == chr) + retptr = &str[-1]; + return retptr; +} diff --git a/util/make/make.c b/util/make/make.c new file mode 100644 index 000000000..c28520b15 --- /dev/null +++ b/util/make/make.c @@ -0,0 +1,482 @@ +/* + * Do the actual making for make + * + * $Header$ + */ + +#include +#ifdef unix +#include +#include +#include +#endif +#ifdef eon +#include +#include +#endif +#ifdef os9 +#include +#include +#include +#include +#include +#endif +#include "h.h" + + + +/* + * Exec a shell that returns exit status correctly (/bin/esh). + * The standard EON shell returns the process number of the last + * async command, used by the debugger (ugg). + * [exec on eon is like a fork+exec on unix] + */ +int +dosh(string, shell) +char * string; +char * shell; +{ + int number; + +#ifdef unix + return system(string); +#endif +#ifdef eon + return ((number = execl(shell, shell,"-c", string, 0)) == -1) ? + -1: /* couldn't start the shell */ + wait(number); /* return its exit status */ +#endif +#ifdef os9 + int status, pid; + + strcat(string, "\n"); + if ((number = os9fork(shell, strlen(string), string, 0, 0, 0)) == -1) + return -1; /* Couldn't start a shell */ + do + { + if ((pid = wait(&status)) == -1) + return -1; /* child already died!?!? */ + } while (pid != number); + + return status; +#endif +} + + +/* + * Do commands to make a target + */ +void +docmds1(np, lp) +struct name * np; +struct line * lp; +{ + bool ssilent; + bool signore; + int estat; + register char * q; + register char * p; + char * shell; + register struct cmd * cp; + + + if (*(shell = getmacro("SHELL")) == '\0') +#ifdef eon + shell = ":bin/esh"; +#endif +#ifdef unix + shell = "/bin/sh"; +#endif +#ifdef os9 + shell = "shell"; +#endif + + for (cp = lp->l_cmd; cp; cp = cp->c_next) + { + strcpy(str1, cp->c_cmd); + expand(str1); + q = str1; + ssilent = silent; + signore = ignore; + while ((*q == '@') || (*q == '-')) + { + if (*q == '@') /* Specific silent */ + ssilent = TRUE; + else /* Specific ignore */ + signore = TRUE; + q++; /* Not part of the command */ + } + + if (!domake) + ssilent = 0; + + if (!ssilent) + fputs(" ", stdout); + + for (p=q; *p; p++) + { + if (*p == '\n' && p[1] != '\0') + { + *p = ' '; + if (!ssilent) + fputs("\\\n", stdout); + } + else if (!ssilent) + putchar(*p); + } + if (!ssilent) { + putchar('\n'); + fflush(stdout); + } + + if (domake) + { /* Get the shell to execute it */ + if ((estat = dosh(q, shell)) != 0) + { + if (estat == -1) + fatal("Couldn't execute %s", shell); + else + { + printf("%s: Error code %d", myname, estat); + if (signore) + fputs(" (Ignored)\n", stdout); + else + { + putchar('\n'); + if (!(np->n_flag & N_PREC)) + if (unlink(np->n_name) == 0) + printf("%s: '%s' removed.\n", myname, np->n_name); + exit(1); + } + } + fflush(stdout); + } + } + } +} + + +docmds(np) +struct name * np; +{ + register struct line * lp; + + + for (lp = np->n_line; lp; lp = lp->l_next) + docmds1(np, lp); +} + + +#ifdef os9 +/* + * Some stuffing around to get the modified time of a file + * in an os9 file system + */ +getmdate(fd, tbp) +struct sgtbuf * tbp; +{ + struct registers regs; + static struct fildes fdbuf; + + + regs.rg_a = fd; + regs.rg_b = SS_FD; + regs.rg_x = &fdbuf; + regs.rg_y = sizeof (fdbuf); + + if (_os9(I_GETSTT, ®s) == -1) + { + errno = regs.rg_b & 0xff; + return -1; + } + if (tbp) + { + _strass(tbp, fdbuf.fd_date, sizeof (fdbuf.fd_date)); + tbp->t_second = 0; /* Files are only acurate to mins */ + } + return 0; +} + + +/* + * Kludge routine to return an aproximation of how many + * seconds since 1980. Dates will be in order, but will not + * be lineer + */ +time_t +cnvtime(tbp) +struct sgtbuf *tbp; +{ + long acc; + + + acc = tbp->t_year - 80; /* Baseyear is 1980 */ + acc = acc * 12 + tbp->t_month; + acc = acc * 31 + tbp->t_day; + acc = acc * 24 + tbp->t_hour; + acc = acc * 60 + tbp->t_minute; + acc = acc * 60 + tbp->t_second; + + return acc; +} + + +/* + * Get the current time in the internal format + */ +time(tp) +time_t * tp; +{ + struct sgtbuf tbuf; + + + if (getime(&tbuf) < 0) + return -1; + + if (tp) + *tp = cnvtime(&tbuf); + + return 0; +} +#endif + + +/* + * Get the modification time of a file. If the first + * doesn't exist, it's modtime is set to 0. + */ +void +modtime(np) +struct name * np; +{ +#ifdef unix + struct stat info; + + + if (stat(np->n_name, &info) < 0) + { + if (errno != ENOENT) + fatal("Can't open %s; error %d", np->n_name, errno); + + np->n_time = 0L; + } + else + np->n_time = info.st_mtime; +#endif +#ifdef eon + struct stat info; + int fd; + + + if ((fd = open(np->n_name, 0)) < 0) + { + if (errno != ER_NOTF) + fatal("Can't open %s; error %02x", np->n_name, errno); + + np->n_time = 0L; + } + else if (getstat(fd, &info) < 0) + fatal("Can't getstat %s; error %02x", np->n_name, errno); + else + np->n_time = info.st_mod; + + close(fd); +#endif +#ifdef os9 + struct sgtbuf info; + int fd; + + + if ((fd = open(np->n_name, 0)) < 0) + { + if (errno != E_PNNF) + fatal("Can't open %s; error %02x", np->n_name, errno); + + np->n_time = 0L; + } + else if (getmdate(fd, &info) < 0) + fatal("Can't getstat %s; error %02x", np->n_name, errno); + else + np->n_time = cnvtime(&info); + + close(fd); +#endif +} + + +/* + * Update the mod time of a file to now. + */ +void +touch(np) +struct name * np; +{ + char c; + int fd; + + + if (!domake || !silent) + printf(" touch(%s)\n", np->n_name); + + if (domake) + { +#ifdef unix + long a[2]; + long time(); + + a[0] = a[1] = time((long *)0); + if (utime(np->n_name, &a[0]) < 0) + printf("%s: '%s' not touched - non-existant\n", + myname, np->n_name); +#endif +#ifdef eon + if ((fd = open(np->n_name, 0)) < 0) + printf("%s: '%s' not touched - non-existant\n", + myname, np->n_name); + else + { + uread(fd, &c, 1, 0); + uwrite(fd, &c, 1); + } + close(fd); +#endif +#ifdef os9 + /* + * Strange that something almost as totally useless + * as this is easy to do in os9! + */ + if ((fd = open(np->n_name, S_IWRITE)) < 0) + printf("%s: '%s' not touched - non-existant\n", + myname, np->n_name); + close(fd); +#endif + } +} + + +/* + * Recursive routine to make a target. + */ +int +make(np, level) +struct name * np; +int level; +{ + register struct depend * dp; + register struct line * lp; + register struct depend * qdp; + time_t dtime = 1; + bool didsomething = 0; + int dynamic = 0; + + + if (np->n_flag & N_DONE) + return 0; + + if (!np->n_time) + modtime(np); /* Gets modtime of this file */ + + if (rules) + { + for (lp = np->n_line; lp; lp = lp->l_next) + if (lp->l_cmd) + break; + if (!lp) { + dyndep(np); + dynamic = 1; + } + } + + if (!(np->n_flag & N_TARG) && np->n_time == 0L) + fatal("Don't know how to make %s", np->n_name); + + for (qdp = (struct depend *)0, lp = np->n_line; lp; lp = lp->l_next) + { + for (dp = lp->l_dep; dp; dp = dp->d_next) + { + char *sv = 0; + if (dynamic) { + char *s = getmacro("<"); + + if (s) { + sv = malloc((unsigned)(strlen(s)+1)); + if (!sv) { + fatal("no space for saved $<"); + } + strcpy(sv, s); + } + } + make(dp->d_name, level+1); + if (dynamic && sv) { + setmacro("<", sv, 4); + free(sv); + } + if (np->n_time < dp->d_name->n_time) + qdp = newdep(dp->d_name, qdp); + dtime = max(dtime, dp->d_name->n_time); + } + if (!quest && (np->n_flag & N_DOUBLE) && (np->n_time < dtime)) + { + make1(np, lp, qdp); /* free()'s qdp */ + dtime = 1; + qdp = (struct depend *)0; + didsomething++; + } + } + + np->n_flag |= N_DONE; + + if (quest) + { + long t; + + t = np->n_time; + time(&np->n_time); + return t < dtime; + } + else if (np->n_time < dtime && !(np->n_flag & N_DOUBLE)) + { + make1(np, (struct line *)0, qdp); /* free()'s qdp */ + time(&np->n_time); + } + else if (level == 0 && !didsomething) + printf("%s: '%s' is up to date\n", myname, np->n_name); + return 0; +} + + +make1(np, lp, qdp) +register struct depend * qdp; +struct line * lp; +struct name * np; +{ + register struct depend * dp; + register char *p; + char *rindex(); + + + if (dotouch) + touch(np); + else + { + strcpy(str1, ""); + for (dp = qdp; dp; dp = qdp) + { + if (strlen(str1)) + strcat(str1, " "); + strcat(str1, dp->d_name->n_name); + qdp = dp->d_next; + free((char *)dp); + } + setmacro("?", str1, 4); + setmacro("@", np->n_name, 4); + p = rindex(np->n_name, '.'); + if (p) *p = 0; + setmacro("*", np->n_name, 4); + if (p) *p = '.'; + if (lp) /* lp set if doing a :: rule */ + docmds1(np, lp); + else + docmds(np); + } +} diff --git a/util/make/reader.c b/util/make/reader.c new file mode 100644 index 000000000..f015442ce --- /dev/null +++ b/util/make/reader.c @@ -0,0 +1,119 @@ +/* + * Read in makefile + * + * $Header$ + */ + + +#include +#include +#include "h.h" + + +int lineno; + + +/* + * Syntax error handler. Print message, with line number, and exits. + */ +/*VARARGS1*/ +void +error(msg, a1, a2, a3) +char * msg; +{ + fprintf(stderr, "%s: ", myname); + fprintf(stderr, msg, a1, a2, a3); + if (lineno) + fprintf(stderr, " near line %d", lineno); + fputc('\n', stderr); + exit(1); +} + + +/* + * Read a line into the supplied string of length LZ. Remove + * comments, ignore blank lines. Deal with quoted (\) #, and + * quoted newlines. If EOF return TRUE. + */ +bool +getline(str, fd) +char * str; +FILE * fd; +{ + register char * p; + char * q; + int pos = 0; + + + for (;;) + { + if (fgets(str+pos, LZ-pos, fd) == (char *)0) + return TRUE; /* EOF */ + + lineno++; + + if ((p = index(str+pos, '\n')) == (char *)0) + error("Line too long"); + + if (p[-1] == '\\') + { + p[-1] = '\n'; + pos = p - str; + continue; + } + + p = str; + while (((q = index(p, '#')) != (char *)0) && + (p != q) && (q[-1] == '\\')) + { + char *a; + + a = q - 1; /* Del \ chr; move rest back */ + p = q; + while (*a++ = *q++) + ; + } + if (q != (char *)0) + { + q[0] = '\n'; + q[1] = '\0'; + } + + p = str; + while (isspace(*p)) /* Checking for blank */ + p++; + + if (*p != '\0') + return FALSE; + pos = 0; + } +} + + +/* + * Get a word from the current line, surounded by white space. + * return a pointer to it. String returned has no white spaces + * in it. + */ +char * +gettok(ptr) +char **ptr; +{ + register char * p; + + + while (isspace(**ptr)) /* Skip spaces */ + (*ptr)++; + + if (**ptr == '\0') /* Nothing after spaces */ + return NULL; + + p = *ptr; /* word starts here */ + + while ((**ptr != '\0') && (!isspace(**ptr))) + (*ptr)++; /* Find end of word */ + + *(*ptr)++ = '\0'; /* Terminate it */ + + return(p); +} diff --git a/util/make/rules.c b/util/make/rules.c new file mode 100644 index 000000000..e2d7dfacb --- /dev/null +++ b/util/make/rules.c @@ -0,0 +1,257 @@ +/* + * Control of the implicit suffix rules + * + * $Header$ + */ + + +#include "h.h" + + +/* + * Return a pointer to the suffix of a name + */ +char * +suffix(name) +char * name; +{ + return rindex(name, '.'); +} + + +/* + * Dynamic dependency. This routine applies the suffis rules + * to try and find a source and a set of rules for a missing + * target. If found, np is made into a target with the implicit + * source name, and rules. Returns TRUE if np was made into + * a target. + */ +bool +dyndep(np) +struct name * np; +{ + register char * p; + register char * q; + register char * suff; /* Old suffix */ + register char * basename; /* Name without suffix */ + struct name * op; /* New dependent */ + struct name * sp; /* Suffix */ + struct line * lp; + struct depend * dp; + char * newsuff; + + + p = str1; + q = np->n_name; + if (!(suff = suffix(q))) + return FALSE; /* No suffix */ + while (q < suff) + *p++ = *q++; + *p = '\0'; + basename = setmacro("*", str1, 4)->m_val; + + if (!((sp = newname(".SUFFIXES"))->n_flag & N_TARG)) + return FALSE; + + for (lp = sp->n_line; lp; lp = lp->l_next) + for (dp = lp->l_dep; dp; dp = dp->d_next) + { + newsuff = dp->d_name->n_name; + if (strlen(suff)+strlen(newsuff)+1 >= LZ) + fatal("Suffix rule too long"); + p = str1; + q = newsuff; + while (*p++ = *q++) + ; + p--; + q = suff; + while (*p++ = *q++) + ; + sp = newname(str1); + if (sp->n_flag & N_TARG) + { + p = str1; + q = basename; + if (strlen(basename) + strlen(newsuff)+1 >= LZ) + fatal("Implicit name too long"); + while (*p++ = *q++) + ; + p--; + q = newsuff; + while (*p++ = *q++) + ; + op = newname(str1); + if (!op->n_time) + modtime(op); + if (op->n_time || (op->n_flag & N_TARG)) + { + dp = newdep(op, (struct depend *)0); + newline(np, dp, sp->n_line->l_cmd, 0); + setmacro("<", op->n_name, 4); + return TRUE; + } + } + } + return FALSE; +} + + +/* + * Make the default rules + */ +void +makerules() +{ + struct cmd * cp; + struct name * np; + struct depend * dp; + + +#ifdef eon + setmacro("BDSCC", "asm", 0); + /* setmacro("BDSCFLAGS", "", 0); */ + cp = newcmd("$(BDSCC) $(BDSCFLAGS) -n $<", (struct cmd *)0); + np = newname(".c.o"); + newline(np, (struct depend *)0, cp, 0); + + setmacro("CC", "c", 0); + setmacro("CFLAGS", "-O", 0); + cp = newcmd("$(CC) $(CFLAGS) -c $<", (struct cmd *)0); + np = newname(".c.obj"); + newline(np, (struct depend *)0, cp, 0); + + setmacro("M80", "asm -n", 0); + /* setmacro("M80FLAGS", "", 0); */ + cp = newcmd("$(M80) $(M80FLAGS) $<", (struct cmd *)0); + np = newname(".mac.o"); + newline(np, (struct depend *)0, cp, 0); + + setmacro("AS", "zas", 0); + /* setmacro("ASFLAGS", "", 0); */ + cp = newcmd("$(ZAS) $(ASFLAGS) -o $@ $<", (struct cmd *)0); + np = newname(".as.obj"); + newline(np, (struct depend *)0, cp, 0); + + np = newname(".as"); + dp = newdep(np, (struct depend *)0); + np = newname(".obj"); + dp = newdep(np, dp); + np = newname(".c"); + dp = newdep(np, dp); + np = newname(".o"); + dp = newdep(np, dp); + np = newname(".mac"); + dp = newdep(np, dp); + np = newname(".SUFFIXES"); + newline(np, dp, (struct cmd *)0, 0); +#endif + +/* + * Some of the UNIX implicit rules + */ +#ifdef unix + setmacro("CC", "cc", 0); + setmacro("CFLAGS", "-O", 0); +#ifdef MINIX + cp = newcmd("$(CC) $(CFLAGS) -S $<", (struct cmd *)0); + np = newname(".c.s"); +#else + cp = newcmd("$(CC) $(CFLAGS) -c $<", (struct cmd *)0); + np = newname(".c.o"); +#endif MINIX + newline(np, (struct depend *)0, cp, 0); + + setmacro("AS", "as", 0); + cp = newcmd("$(AS) -o $@ $<", (struct cmd *)0); + np = newname(".s.o"); + newline(np, (struct depend *)0, cp, 0); + + setmacro("YACC", "yacc", 0); + /* setmacro("YFLAGS", "", 0); */ + cp = newcmd("$(YACC) $(YFLAGS) $<", (struct cmd *)0); + cp = newcmd("mv y.tab.c $@", cp); + np = newname(".y.c"); + newline(np, (struct depend *)0, cp, 0); + + cp = newcmd("$(YACC) $(YFLAGS) $<", (struct cmd *)0); + cp = newcmd("$(CC) $(CFLAGS) -c y.tab.c", cp); + cp = newcmd("rm y.tab.c", cp); + cp = newcmd("mv y.tab.o $@", cp); + np = newname(".y.o"); + newline(np, (struct depend *)0, cp, 0); + + setmacro("LEX", "lex", 0); + /* setmacro("LFLAGS", "", 0); */ + cp = newcmd("$(LEX) $(LFLAGS) $<", (struct cmd *)0); + cp = newcmd("mv lex.yy.c $@", cp); + np = newname(".l.c"); + newline(np, (struct depend *)0, cp, 0); + + cp = newcmd("$(LEX) $(LFLAGS) $<", (struct cmd *)0); + cp = newcmd("$(CC) $(CFLAGS) -c lex.yy.c", cp); + cp = newcmd("rm lex.yy.c", cp); + cp = newcmd("mv lex.yy.o $@", cp); + np = newname(".l.o"); + newline(np, (struct depend *)0, cp, 0); + + np = newname(".s"); + dp = newdep(np, (struct depend *)0); + np = newname(".o"); + dp = newdep(np, dp); + np = newname(".c"); + dp = newdep(np, dp); + np = newname(".y"); + dp = newdep(np, dp); + np = newname(".l"); + dp = newdep(np, dp); + np = newname(".SUFFIXES"); + newline(np, dp, (struct cmd *)0, 0); +#endif +#ifdef os9 +/* + * Fairlight use an enhanced version of the C sub-system. + * They have a specialised macro pre-processor. + */ + setmacro("CC", "cc", 0); + setmacro("CFLAGS", "-z", 0); + cp = newcmd("$(CC) $(CFLAGS) -r $<", (struct cmd *)0); + + np = newname(".c.r"); + newline(np, (struct depend *)0, cp, 0); + np = newname(".ca.r"); + newline(np, (struct depend *)0, cp, 0); + np = newname(".a.r"); + newline(np, (struct depend *)0, cp, 0); + np = newname(".o.r"); + newline(np, (struct depend *)0, cp, 0); + np = newname(".mc.r"); + newline(np, (struct depend *)0, cp, 0); + np = newname(".mca.r"); + newline(np, (struct depend *)0, cp, 0); + np = newname(".ma.r"); + newline(np, (struct depend *)0, cp, 0); + np = newname(".mo.r"); + newline(np, (struct depend *)0, cp, 0); + + np = newname(".r"); + dp = newdep(np, (struct depend *)0); + np = newname(".mc"); + dp = newdep(np, dp); + np = newname(".mca"); + dp = newdep(np, dp); + np = newname(".c"); + dp = newdep(np, dp); + np = newname(".ca"); + dp = newdep(np, dp); + np = newname(".ma"); + dp = newdep(np, dp); + np = newname(".mo"); + dp = newdep(np, dp); + np = newname(".o"); + dp = newdep(np, dp); + np = newname(".a"); + dp = newdep(np, dp); + np = newname(".SUFFIXES"); + newline(np, dp, (struct cmd *)0, 0); +#endif +}