3 * Copyright (C) 2002-2003, by Mike Gleason, NcFTP Software.
6 * Licensed under the GNU Public License.
18 #define SETCOLOR_SUCCESS (gANSIEscapes ? "\033\1331;32m" : "")
19 #define SETCOLOR_FAILURE (gANSIEscapes ? "\033\1331;31m" : "")
20 #define SETCOLOR_WARNING (gANSIEscapes ? "\033\1331;33m" : "")
21 #define SETCOLOR_NORMAL (gANSIEscapes ? "\033\1330;39m" : "")
23 #define TEXT_BLOCK_SIZE 8192
26 #define TERMS "vt100:vt102:vt220:vt320:xterm:xterm-color:ansi:linux:scoterm:scoansi:dtterm:cons25:cygwin"
28 size_t gNBufUsed = 0, gNBufAllocated = 0;
31 char gAction[64] = "";
32 char gTarget[64] = "";
34 char gArLibraryTarget[64] = "";
41 static void DumpFormattedOutput(void)
44 char spaces[8 + 1] = " ";
51 for (cp = gBuf + ((gDumpCmdArgs == 0) ? strlen(gArgsStr) : 0); ; cp++) {
62 cp = spaces + 8 - (8 - ((curcol - INDENT - 1) % 8));
65 for (i = INDENT; --i >= 0; )
70 if (++curcol == (gColumns - 1)) {
73 } else if (*cp == '\n')
77 } /* DumpFormattedOutput */
81 /* Difftime(), only for timeval structures. */
82 static void TimeValSubtract(struct timeval *tdiff, struct timeval *t1, struct timeval *t0)
84 tdiff->tv_sec = t1->tv_sec - t0->tv_sec;
85 tdiff->tv_usec = t1->tv_usec - t0->tv_usec;
86 if (tdiff->tv_usec < 0) {
88 tdiff->tv_usec += 1000000;
90 } /* TimeValSubtract */
94 static void Wait(void)
100 pid2 = (int) waitpid(gCCPID, &status, 0);
101 } while (((pid2 >= 0) && (! WIFEXITED(status))) || ((pid2 < 0) && (errno == EINTR)));
102 if (WIFEXITED(status))
103 gExitStatus = WEXITSTATUS(status);
108 static int SlurpProgress(int fd)
115 struct timeval now, tnext, tleft;
118 const char *trail = "/-\\|", *trailcp;
121 snprintf(s1, sizeof(s1), "%s%s%s... ", gAction, gTarget[0] ? " " : "", gTarget);
122 printf("\r%-70s%-9s", s1, "");
125 gettimeofday(&now, NULL);
133 if (gNBufUsed == (gNBufAllocated - 1)) {
134 if ((newbuf = (char *) realloc(gBuf, gNBufAllocated + TEXT_BLOCK_SIZE)) == NULL) {
135 perror("ccdv: realloc");
138 gNBufAllocated += TEXT_BLOCK_SIZE;
143 nready = select(fd + 1, &ss, NULL, NULL, &tleft);
147 if (errno != EINTR) {
148 perror("ccdv: select");
153 gettimeofday(&now, NULL);
154 if ((now.tv_sec > tnext.tv_sec) || ((now.tv_sec == tnext.tv_sec) && (now.tv_usec >= tnext.tv_usec))) {
159 printf("\r%-71s%c%-7s", s1, *trailcp, "");
161 if (*++trailcp == '\0')
164 TimeValSubtract(&tleft, &tnext, &now);
167 ntoread = (gNBufAllocated - gNBufUsed - 1);
168 nread = read(fd, gBuf + gNBufUsed, ntoread);
172 perror("ccdv: read");
174 } else if (nread == 0) {
178 gBuf[gNBufUsed] = '\0';
180 snprintf(s1, sizeof(s1), "%s%s%s: ", gAction, gTarget[0] ? " " : "", gTarget);
182 if (gExitStatus == 0) {
183 printf("\r%-70s", s1);
184 printf("[%s%s%s]", ((gNBufUsed - strlen(gArgsStr)) < 4) ? SETCOLOR_SUCCESS : SETCOLOR_WARNING, "OK", SETCOLOR_NORMAL);
185 printf("%-5s\n", " ");
187 printf("\r%-70s", s1);
188 printf("[%s%s%s]", SETCOLOR_FAILURE, "ERROR", SETCOLOR_NORMAL);
189 printf("%-2s\n", " ");
190 gDumpCmdArgs = 1; /* print cmd when there are errors */
194 } /* SlurpProgress */
198 static int SlurpAll(int fd)
204 printf("%s%s%s.\n", gAction, gTarget[0] ? " " : "", gTarget);
208 if (gNBufUsed == (gNBufAllocated - 1)) {
209 if ((newbuf = (char *) realloc(gBuf, gNBufAllocated + TEXT_BLOCK_SIZE)) == NULL) {
210 perror("ccdv: realloc");
213 gNBufAllocated += TEXT_BLOCK_SIZE;
216 ntoread = (gNBufAllocated - gNBufUsed - 1);
217 nread = read(fd, gBuf + gNBufUsed, ntoread);
221 perror("ccdv: read");
223 } else if (nread == 0) {
227 gBuf[gNBufUsed] = '\0';
230 gDumpCmdArgs = (gExitStatus != 0); /* print cmd when there are errors */
236 static const char *Basename(const char *path)
239 cp = strrchr(path, '/');
247 static const char * Extension(const char *path)
249 const char *cp = path;
250 cp = strrchr(path, '.');
258 static void Usage(void)
260 fprintf(stderr, "Usage: ccdv /path/to/cc CFLAGS...\n\n");
261 fprintf(stderr, "I wrote this to reduce the deluge Make output to make finding actual problems\n");
262 fprintf(stderr, "easier. It is intended to be invoked from Makefiles, like this. Instead of:\n\n");
263 fprintf(stderr, "\t.c.o:\n");
264 fprintf(stderr, "\t\t$(CC) $(CFLAGS) $(DEFS) $(CPPFLAGS) $< -c\n");
265 fprintf(stderr, "\nRewrite your rule so it looks like:\n\n");
266 fprintf(stderr, "\t.c.o:\n");
267 fprintf(stderr, "\t\t@ccdv $(CC) $(CFLAGS) $(DEFS) $(CPPFLAGS) $< -c\n\n");
268 fprintf(stderr, "ccdv 1.1.0 is Free under the GNU Public License. Enjoy!\n");
269 fprintf(stderr, " -- Mike Gleason, NcFTP Software <http://www.ncftp.com>\n");
275 int main(int argc, char **argv)
289 snprintf(gAction, sizeof(gAction), "Running %s", Basename(argv[1]));
290 memset(gArgsStr, 0, sizeof(gArgsStr));
291 for (i = 1; i < argc; i++) {
292 quote = (strchr(argv[i], ' ') != NULL) ? "\"" : "";
293 snprintf(gArgsStr + strlen(gArgsStr), sizeof(gArgsStr) - strlen(gArgsStr), "%s%s%s%s%s", (i == 1) ? "" : " ", quote, argv[i], quote, (i == (argc - 1)) ? "\n" : "");
294 if ((strcmp(argv[i], "-o") == 0) && ((i + 1) < argc)) {
295 if (strcasecmp(Extension(argv[i + 1]), ".o") != 0) {
296 strcpy(gAction, "Linking");
297 snprintf(gTarget, sizeof(gTarget), "%s", Basename(argv[i + 1]));
299 } else if (strchr("-+/", (int) argv[i][0]) != NULL) {
301 } else if (strncasecmp(Extension(argv[i]), ".c", 2) == 0) {
303 snprintf(gTarget, sizeof(gTarget), "%s", Basename(argv[i]));
304 } else if ((strncasecmp(Extension(argv[i]), ".h", 2) == 0) && (cc == 0)) {
306 snprintf(gTarget, sizeof(gTarget), "%s", Basename(argv[i]));
307 } else if ((i == 1) && (strcmp(Basename(argv[i]), "ar") == 0)) {
308 snprintf(gAr, sizeof(gAr), "%s", Basename(argv[i]));
309 } else if ((gArLibraryTarget[0] == '\0') && (strcasecmp(Extension(argv[i]), ".a") == 0)) {
310 snprintf(gArLibraryTarget, sizeof(gArLibraryTarget), "%s", Basename(argv[i]));
313 if ((gAr[0] != '\0') && (gArLibraryTarget[0] != '\0')) {
314 strcpy(gAction, "Creating library");
315 snprintf(gTarget, sizeof(gTarget), "%s", gArLibraryTarget);
316 } else if (pch > 0) {
317 strcpy(gAction, "Precompiling");
319 strcpy(gAction, "Compiling");
322 if (pipe(pipe1) < 0) {
323 perror("ccdv: pipe");
328 devnull = open("/dev/null", O_RDWR, 00666);
329 if ((devnull != 0) && (dup2(devnull, 0) == 0))
332 gCCPID = (int) fork();
334 (void) close(pipe1[0]);
335 (void) close(pipe1[1]);
336 perror("ccdv: fork");
338 } else if (gCCPID == 0) {
340 (void) close(pipe1[0]); /* close read end */
341 if (pipe1[1] != 1) { /* use write end on stdout */
342 (void) dup2(pipe1[1], 1);
343 (void) close(pipe1[1]);
345 (void) dup2(1, 2); /* use write end on stderr */
346 execvp(argv[1], argv + 1);
352 (void) close(pipe1[1]); /* close write end */
353 fd = pipe1[0]; /* use read end */
355 gColumns = (getenv("COLUMNS") != NULL) ? atoi(getenv("COLUMNS")) : 80;
356 gANSIEscapes = (getenv("TERM") != NULL) && (strstr(TERMS, getenv("TERM")) != NULL);
357 gBuf = (char *) malloc(TEXT_BLOCK_SIZE);
361 gNBufAllocated = TEXT_BLOCK_SIZE;
362 if (strlen(gArgsStr) < (gNBufAllocated - 1)) {
363 strcpy(gBuf, gArgsStr);
364 gNBufUsed = strlen(gArgsStr);
368 if (SlurpProgress(fd) < 0)
371 if (SlurpAll(fd) < 0)
374 DumpFormattedOutput();
378 gDumpCmdArgs = 1; /* print cmd when there are errors */
379 DumpFormattedOutput();
380 while ((nread = read(fd, emerg, (size_t) sizeof(emerg))) > 0)
381 (void) write(2, emerg, (size_t) nread);