#include #include #include #include #include #include #include #include #include #define USAGE "usage: %s file [...]\n", name #define d(...) do { if (dflag > 0) \ fprintf(stderr, __VA_ARGS__); } while (0); #define dd(...) do { if (dflag > 1) \ fprintf(stderr, __VA_ARGS__); } while (0); #define ddd(...) do { if (dflag > 2) \ fprintf(stderr, __VA_ARGS__); } while (0); #define MAXBUF 1024 #define MAXCMDS 32 #define MAXCMD 1024 #define MAXDEP 1024 #define MAXTGT 64 extern char *optarg; extern int optind; void cleandep(char **); void cleantgt(char **); char *nextdep(char **); int main(int argc, char *argv[]) { char *b, buf[MAXBUF], *cmd[MAXCMDS], *d, *dep, *name, *tgt; FILE *fp; int c, dflag, fflag, i, icmd, j, s; struct stat sb, ssb; /* allocate memory */ for (i = 0; i < MAXCMDS; i++) { cmd[i] = malloc(MAXCMD*sizeof(char)); if (!cmd[i]) err(1, "malloc"); } dep = malloc(MAXDEP*sizeof(char)); if (!dep) err(1, "malloc"); tgt = malloc(MAXTGT*sizeof(char)); if (!tgt) err(1, "malloc"); tgt[0] = dep[0] = 0; /* process arguments */ name = argv[0]; dflag = fflag = 0; while ((c = getopt(argc, argv, "df")) != -1) switch (c) { case 'd': dflag++; break; case 'f': fflag = 1; break; default: fprintf(stderr, USAGE); return 1; } argc -= optind; argv += optind; if (argc == 0) { fprintf(stderr, USAGE); return 1; } /* process each file */ for (i = icmd = 0; i < argc; i++, icmd = 0) { fp = fopen(argv[i], "r"); if (!fp) err(1, "fopen"); /* read line by line */ for (j = 0; j < 20 && fgets(buf, MAXBUF, fp); j++) { buf[strcspn(buf, "\n")] = 0; /* parse line */ b = buf; for (; *b; b++) { /* command line */ if (strncmp(b, " $ ", 3) == 0 || strncmp(b, " $ ", 3) == 0) { strncpy(cmd[icmd++], b+3, MAXBUF-1); /* find target */ for (b = b+3; *b; b++) { if (!(*b+1)) continue; if (*b != '>') continue; strncpy(tgt, b+1, MAXTGT-1); } ddd("%s: command line '%s'\n", argv[i], buf); continue; } /* dependency line */ if (strncmp(b, " % ", 3) == 0 || strncmp(b, " % ", 3) == 0) { strncpy(dep, b+3, MAXDEP-1); ddd("%s: dependency line '%s'\n", argv[i], buf); continue; } } } if (!icmd) { fprintf(stderr, "%s: no command line found\n", argv[i]); goto next; } if (fflag || !*tgt) goto build; /* compare source > target */ cleantgt(&tgt); dd("%s: target '%s'\n", argv[i], tgt); if (stat(argv[i], &sb)) err(1, argv[i]); if (stat(tgt, &ssb)) { if (errno == ENOENT) goto build; err(1, tgt); } if (sb.st_mtime > ssb.st_mtime) { d("%s: %s is modified, building\n", argv[i], argv[i]); goto build; } if (!*dep) goto uptodate; /* compare dependencies > target */ cleandep(&dep); while (d = nextdep(&dep)) { dd("%s: depend '%s'\n", argv[i], d); if (stat(d, &sb)) { if (errno != ENOENT) err(1, d); fprintf(stderr, "%s: dependency %s does not " "exist\n", argv[i], d); continue; } if (sb.st_mtime > ssb.st_mtime) { d("%s: %s is modified, building\n", argv[i], d); free(d); goto build; } free(d); } uptodate: fprintf(stderr, "%s: already up-to-date\n", argv[i]); goto next; build: /* run commands */ buf[0] = 0; strcat(buf, "set -ex\n"); for (j = 0; j < icmd; j++) { strncat(buf, cmd[j], MAXBUF-1); strncat(buf, "\n", MAXBUF-1); } s = system(buf); if (s == 0) goto next; if (WIFEXITED(s)) { fprintf(stderr, "%s: exited with %d\n", argv[i], WEXITSTATUS(s)); exit(WEXITSTATUS(s)); } else if (WIFSIGNALED(s)) { fprintf(stderr, "%s: terminated by signal %d\n", argv[i], WTERMSIG(s)); exit(1); } next: fclose(fp); } } void cleandep(char **dep) { for (; **dep; (*dep)++) if (!isspace(**dep)) break; } void cleantgt(char **tgt) { char *t; for (; **tgt; (*tgt)++) if (!isspace(**tgt)) break; for (t = *tgt; *t; t++) if (isspace(*t) || *t == '|' || *t == '&' || *t == ';' || *t == ')') { *t = 0; break; } } char * nextdep(char **dep) { char *d; int i; for (i = 0; (*dep)[i]; i++) { if (isspace((*dep)[i]) || (*dep)[i+1] == 0) { d = strdup(*dep); d[i+((*dep)[i+1]==0)] = 0; for (; (*dep)[i]; i++) if (!isspace((*dep)[i])) break; (*dep) += i + ((*dep)[i+1]==0); break; } } return d; }