/* * simple make program * * make [options] [targets] * * options are * -i ignore non-zero program returns * -f file makefile name (default is "makefile") * -n don't execute any commands * -t just touch the targets, don't build * -r ignore built-in rules * -d debug flag (noisy output) * -h hold the screen * * this program reads a makefile to build the named targets * a makefile contains rules, macro definitions, or dependencies * an example makefile might be: * * # this is a sample makefile for a spreadsheet * OBJ = main.s io.s calc.s * ss: $(OBJ) ss.h * d:as.ttp d:ttp.s $(OBJ) -L d:lib.a * * there are built-in rules to convert .c to .s, .c to .ttp, and .c to .prg */ #include #define MAXLINE 160 #define MAXSYM 100 #define MAXRULE 30 #define MAXARG 75 #define TIME 0x2C #define DATE 0x2A #define EXEC 0x4B #define RENAME 0x56 #define GSDTOF 0x57 #define SETDRV 0x0E #define GETDRV 0x19 #define GETDSK 0x36 #define MALLOC 0x48 typedef struct x0 { /* structure for command lists */ char *cmd; /* pointer to the command line */ struct x0 *next; /* pointer to the next one */ } Command; typedef struct x1 { /* structure for dependencies */ int dep; /* name of a parent */ struct x1 *next; /* pointer to next parent */ } Depend; typedef struct { /* structure for rules */ int from; /* the extension of the parent */ int to; /* the extension of the child */ Command *cmd; /* the commands to make the child */ } Rule; FILE *in; /* makefile input file handle */ int eof; /* eof flag for makefile input stream */ int no_exec = 0; /* don't execute the commands */ int debug = 0; /* debug output flag */ int ignore = 0; /* ignore non-zero returns */ int norules = 0; /* no built-in rules */ int hold = 1; /* hold the screen (for GEM users */ char line[MAXLINE]; /* buffer containing current input line */ char extra[MAXLINE]; /* extra buffer for macro expansion */ char name[MAXLINE]; /* space for making up names */ char *macro[MAXSYM]; /* pointer to the macros */ char *sym[MAXSYM]; /* pointer to the symbol names */ Depend *depend[MAXSYM]; /* pointer to dependencies */ Command *command[MAXSYM]; /* index into commands */ Rule rules[MAXRULE]; /* rules */ int lrule = 0; /* last rule used */ int firstsym = -1; /* what's first dependency declared */ char base[MAXLINE]; /* buffer for '*' macro */ char target[MAXLINE]; /* buffer for '@' macro */ char *save(); /* save a string */ char *alloc(); /* allocate space */ Command *cmdlist(); /* make up a command list */ Command *add_cmd(); /* add a command to a command list */ long date(); /* get the date of a file */ char *av[MAXARG]; /* argv for built-in's */ int ac; /* argc for built-in's */ FILE *xin; /* stdin for built-in commands */ FILE *xout; /* stdout for built-in commands */ main(argc, argv) char *argv[]; { int i, err, tch; char *name; i = 1; name = "makefile"; tch = err = 0; for (i = 1; i < argc && *argv[i] == '-'; i++) { switch (argv[i][1]) { case 'f': case 'F': i++; name = argv[i]; break; case 'i': case 'I': ignore = 1; break; case 'd': case 'D': debug = 1; break; case 't': case 'T': tch = 1; break; case 'n': case 'N': no_exec = 1; break; case 'r': case 'R': norules = 1; break; case 'h': case 'H': hold = !hold; break; default: printf("unknown option: %s\n", argv[i]); err = 1; break; } } if (err) error("usage: make [-i -n -t -r -d -h -f file] [targets]\n"); if (tch) { while (i < argc) touch(argv[i++]); } else { init(); input(name); if (i == argc && firstsym >= 0) make(firstsym); else { for ( ; i < argc; i++) if (!assign(argv[i])) make(lookup(argv[i])); } } bye(0); } /* initialize the symbol table and add built-in rules */ init() { int i; Command *cp; rules[0].cmd = NULL; for (i = 0; i < MAXSYM; i++) sym[i] = NULL; if (norules) return; /* built-in rules */ cp = NULL; cp = add_cmd(cp, " d:\\cc.ttp $*.c"); cp = add_cmd(cp, " d:\\as.ttp -o $*.ttp d:\\ttp.s yc.out -L d:\\lib.a"); cp = add_cmd(cp, " rm yc.out"); add_rule(".c", ".ttp", cp); cp = NULL; cp = add_cmd(cp, " d:\\cc.ttp $*.c"); cp = add_cmd(cp, " d:\\as.ttp -o $*.prg d:\\prg.s yc.out -L d:\\gem.a d:\\lib.a"); cp = add_cmd(cp, " rm yc.out"); add_rule(".c", ".prg", cp); cp = NULL; cp = add_cmd(cp, " d:\\cc.ttp -o $*.s $*.c"); add_rule(".c", ".s", cp); } /* read in and parse the makefile */ input(name) char *name; { if (!findfile(name)) return; getline(); while (!eof) { if (*line == '#') getline(); else if (macdef()) getline(); else if (*line == '.') rule(); else if (*line > ' ') dependency(); else getline(); } fclose(in); } /* search high and low for a makefile */ findfile(name) char *name; { int i, drv, r; /* someday, turn a full pathname into a chdir */ if (in = fopen(name, "r")) /* try current directory first */ return 1; drv = trap(1, GETDRV); for (i = 0; i < 4; i++) { /* try A:, B:, C:, D: */ r = trap(1, SETDRV, i); if (in = fopen(name, "r")) { printf(" run from %c:\n", i + 'A'); return 1; } } trap(1, SETDRV, drv); return 0; } /* read in a line from the makefile */ getline() { int c; char *l; l = line; c = getch(); while (c != EOF && c != '\n') { if (c == '\\') { if ((c = getch()) == '\n') c = ' '; else *l++ = '\\'; } *l++ = c; if (c <= ' ') { /* skip spaces */ while (c != EOF && c != '\n' && c <= ' ') c = getch(); } else c = getch(); } *l = 0; eof = (c == EOF); } /* get a character from the file, strip out '\r' */ getch() { int c; while ((c = getc(in)) == '\r') ; return c; } /* expand all the macro's in the current line */ expand() { int done; char *e, *l, *m; while (1) { e = extra; l = line; done = 1; while (*e++ = *l++) if (*l == '$') done = 0; if (done) break; e = extra; l = line; while (*e) { if (*e == '$') { e++; m = name; if (*e == '(') { /* multi letter macro */ e++; while (*e != ')') *m++ = *e++; e++; } else *m++ = *e++; /* one letter macro */ *m = 0; if (m = macro[lookup(name)]) { while (*m) *l++ = *m++; } else error("$(%s) not defined", name); } else *l++ = *e++; } *l = 0; } } /* read in a rule, ".from.to:" followed by command lines */ rule() { int i, j; char to[10], from[10], *l; l = line; i = j = 0; if (*l != '.') error("bad rule entry"); do { from[j++] = *l++; } while (*l && *l != '.'); from[j] = 0; if (*l != '.') error("bad rule entry"); j = 0; do { to[j++] = *l++; } while (*l && *l != ':'); to[j] = 0; add_rule(from, to, cmdlist()); } /* build a list of commands, return pointer to them */ Command * cmdlist() { Command *r; r = NULL; getline(); while (*line && *line <= ' ') { r = add_cmd(r, save(line)); getline(); } return r; } /* check for a command line macro def and process if there */ assign(s) char *s; { char *p; for (p = s; *p && *p != '='; p++) ; if (*p == '=') { *p++ = 0; add_mac(s, save(p)); return 1; } else return 0; } /* check for a macro def and process if there */ macdef() { char *l, *b, *e; l = line; while (*l && *l <= ' ') l++; b = l; while (*l && *l > ' ') l++; e = l; while (*l && *l <= ' ') l++; if (*l++ == '=') { /* got a macro */ *e = 0; add_mac(b, save(l)); return 1; } else return 0; } /* parse a dependency, "child [children]*: [parents]*" */ dependency() { int i, j, target[20], depend[20]; Command *cmd; expand(); i = namelist(target, 0); if (line[i] != ':') error("bad dependency rule"); i = namelist(depend, i+1); cmd = cmdlist(); for (i = 0; target[i]; i++) { for (j = 0; depend[j]; j++) add_dep(target[i], depend[j]); set_cmd(target[i], cmd); } if (firstsym < 0) firstsym = target[0]; } /* gather up a list of names in the input line */ namelist(list, i) int *list; { int t, j; t = 0; while (line[i] && line[i] != ':') { while (line[i] && line[i] <= ' ') i++; j = 0; if (line[i]) { while (line[i] && line[i] > ' ' && line[i] != ':') name[j++] = line[i++]; name[j] = 0; list[t++] = lookup(name); } } list[t] = 0; return i; } /* add a new command to the end of a command list */ Command * add_cmd(cp, cmd) Command *cp; char *cmd; { Command *p, *r; p = alloc((short)sizeof(Command)); p->cmd = cmd; p->next = NULL; if (cp == NULL) r = p; else { r = cp; while (cp->next) cp = cp->next; cp->next = p; } return r; } /* add a parent (target) to a child (dependency) */ add_dep(target, dep) { Depend *p; if (debug) printf("add_dep(%s,%s)\n", sym[target], sym[dep]); p = alloc((short)sizeof(Depend)); p->dep = dep; p->next = depend[target]; depend[target] = p; } /* add a macro */ add_mac(name, str) char *name, *str; { if (debug) printf("add_mac(%s,%s)\n", name, str); macro[lookup(name)] = str; } /* add a command list to a target */ set_cmd(target, cp) Command *cp; { if (debug) printf("set_cmd(%s,%lx)\n", sym[target], cp); command[target] = cp; } /* add a rule */ add_rule(from, to, cp) char *from, *to; Command *cp; { if (debug) printf("add_rule(%s,%s,%lx)\n", from, to, cp); rules[lrule].from = lookup(from); rules[lrule].to = lookup(to); rules[lrule].cmd = cp; if (++lrule >= MAXRULE) error("too many rules"); rules[lrule].cmd = NULL; } /* build a child by first building the parents */ make(child) { int mkflag; long chdate; Command *cp; Depend *dp; if (debug) printf("make(%s)\n", sym[child]); chdate = date(sym[child]); mkflag = 0; if (dp = depend[child]) { while (dp) { make(dp->dep); if (check(dp->dep, chdate)) mkflag = 1; dp = dp->next; } } else mkflag = 1; if (mkflag) { strcpy(target, sym[child]); add_mac("@", target); if (cp = command[child]) { unlink(target); execute(cp); } else chkrule(child, chdate); } } /* set the date/time of the named file to the present */ touch(name) char *name; { int fd, r; long dt; if ((fd = open(name, 2)) >= 0) { dt = (trap(1, DATE) && 0xFFFF) | (trap(1, TIME) << 16); if (r = trap(1, GSDTOF, &dt, fd, 1)) perror(r, name); close(fd); } else error("cannot open %s (%d)\n", name, fd); } /* get the date/time of the named file */ long date(name) char *name; { int fd; unsigned long dt; if ((fd = open(name, 0)) >= 0) { trap(1, GSDTOF, &dt, fd, 0); dt = ((dt >> 16) & 0xFFFFL) | (dt << 16); /* swap words */ close(fd); } else dt = 0L; if (debug) printf("date of %s is %lx\n", name, dt); return dt; } /* compare the date/time of the named file against the child date */ check(parent, chdt) long chdt; { long pardt; pardt = date(sym[parent]); return (pardt > chdt); } /* see if there are any rules that we can use to build the child */ chkrule(child, chdate) long chdate; { Rule *r; char *s, *e; int i, j, ext, parent, parbase; s = sym[child]; for (j = i = 0; base[i] = s[i]; i++) if (s[i] == '.') j = i; if (j == 0) return 0; e = &s[j]; base[j] = 0; parbase = lookup(base); ext = lookup(e); for (i = lrule; --i >= 0; ) { r = &rules[i]; if (r->to == ext) { strcpy(extra, base); strcat(extra, sym[r->from]); parent = lookup(extra); make(parent); add_mac("*", sym[parbase]); if (check(parent, chdate)) { unlink(sym[child]); execute(r->cmd); return; } } } } /* execute a list of commands */ execute(cp) Command *cp; { int r; while (cp) { strcpy(line, cp->cmd); expand(); printf(" %s\n", line); if (!no_exec && (r = system(line)) && !ignore) perror(r, "command failed"); cp = cp->next; } } /* do something, either built-in (rm, mv) or exec a program */ args(s) char *s; { /* built and argc, argv for the built-in's */ char *strtok(); ac = 1; s = strtok(s, " \t"); xin = stdin; xout = stdout; while (ac < MAXARG && s != NULL) { if (*s == '>') { if (s[1] == '>') xout = fopen(s+2, "a"); else xout = fopen(s+1, "w"); } else if (*s == '<') xin = fopen(s+1, "r"); else av[ac++] = s; s = strtok(NULL, " \t"); } av[ac] = NULL; } rm() { /* remove a file */ int i, r, rr; rr = 0; for (i = 1; i < ac; i++) { if ((r = unlink(av[i])) != 0 && r != -33) { /* not deleted but was found */ perror(r, av[i]); rr = r; } } return rr; } mv() { /* rename a file */ int r; if (ac == 3) { unlink(av[2]); if (r = trap(1, RENAME, 0, av[1], av[2])) { printf("rename didn't work (%d), try copy\n", r); if ((r = cp()) == 0) unlink(av[1]); } } return r; } cp() { /* copy a file */ int r; FILE *in, *out; r = 1; if (ac == 3) { if (in = fopen(av[1], "rb")) { if (out = fopen(av[2], "wb")) { xcat(in, out); r = 0; fclose(in); fclose(out); } else error("cannot create %s\n", av[2]); fclose(in); } else error("cannot copy %s\n", av[1]); } return r; } cat() { /* cat a file */ int i, r; FILE *in; r = 0; for (i = 1; i < ac; i++) { if ((in = fopen(av[i], "r")) == NULL) { r = 1; error("cannot cat %s", av[i]); } else xcat(in, xout); } return r; } xcat(i, o) FILE *i, *o; { /* do the real work of cat */ int c; while ((c = getc(i)) != EOF) putc(c, o); fclose(i); } grep() { int i; char *p, *fgets(); FILE *in, *fopen(); strlower(p = av[1]); for (i = 2; i < ac; i++) { if (in = fopen(av[i], "r")) { while (fgets(extra, MAXLINE, in)) { strlower(extra); if (match(p, extra)) printf("%s:\t%s", av[i], extra); } fclose(in); } else printf("cannot open %s\n", av[i]); } return 0; } match(pat, line) char *pat, *line; { char *p, *l; while (*line) { p = pat; l = line++; while (*p++ == *l++) if (*p == 0) return 1; } return 0; } xfree() { long m, dsz[4]; int c, drv; if (ac > 1 && (c = *av[1]) >= 'A' && c <= 'P') drv = c - 'A'; else drv = trap(1, GETDRV); trap(1, GETDSK, dsz, drv + 1); m = trap(1, MALLOC, -1L); printf("%ld free bytes in memory, ", m); printf("%ld free bytes on %c:\n", dsz[0]*dsz[2]*dsz[3], drv+'A'); return 0; } struct bltin { char *name; int (*func)(); } btbl[] = { { "rm", rm }, { "mv", mv }, { "cp", cp }, { "cat", cat }, { "grep", grep }, { "free", xfree }, { NULL, NULL } }; system(s) char *s; { int i, r; char *p; while (*s && *s <= ' ') s++; for (p = extra; *s && *s > ' '; ) *p++ = *s++; *p = 0; for (i = 0; btbl[i].name != NULL; i++) { if (strcmp(extra, btbl[i].name) == 0) { args(s); r = (*btbl[i].func)(); if (xin != stdin) fclose(xin); if (xout != stdout) fclose(xout); return r; } } if ((r = strlen(s+1)) >= 128) error("command line too long"); else { *s = r; r = trap(1, EXEC, 0, extra, s, ""); } return r; } /* symbol table lookup */ lookup(s) char *s; { int i, start; upper(s); /* too bad TOS filenames are always uppercase */ start = i = *s; while (sym[i]) { if (strcmp(s, sym[i]) == 0) return i; if (++i >= MAXSYM) i = 0; if (i == start) error("too many symbols"); } sym[i] = save(s); command[i] = macro[i] = depend[i] = 0L; return i; } /* convert a string to all uppercase in place */ upper(s) char *s; { register int c; for ( ; c = *s; s++) { if (c >= 'a' && c <= 'z') *s = c - 'a' + 'A'; } } /* save a string */ char * save(s) char *s; { char *r, *alloc(); r = alloc(strlen(s)+1); strcpy(r, s); return r; } /* allocate some space */ char * alloc(n) { char *r, *malloc(); if ((r = malloc(n)) == NULL) error("out of free space"); return r; } bye(n) { if (hold) { printf("(press any char)\n"); getchar(); } exit(n); } perror(r, s) char *s; { char *p; switch (r) { case -32: p = "invalid function number"; break; case -33: p = "file not found"; break; case -34: p = "pathname not found"; break; case -35: p = "too many open files"; break; case -36: p = "access not possible"; break; case -37: p = "invalid handle"; break; case -39: p = "not enough memory"; break; case -46: p = "invalid drive spec"; break; case -49: p = "no more files"; break; default: sprintf(p = extra, "error %d", r); } error("%s: %s", p, s); } /* complain and get out */ error(s, a, b, c, d) char *s; long a, b, c, d; { printf("** "); printf(s, a, b, c, d); printf("\n"); bye(1); }