ccdv.c 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  1. /* ccdv.c
  2. *
  3. * Copyright (C) 2002-2003, by Mike Gleason, NcFTP Software.
  4. * All Rights Reserved.
  5. *
  6. * Licensed under the GNU Public License.
  7. */
  8. #include <unistd.h>
  9. #include <sys/types.h>
  10. #include <sys/time.h>
  11. #include <sys/wait.h>
  12. #include <fcntl.h>
  13. #include <stdio.h>
  14. #include <string.h>
  15. #include <stdlib.h>
  16. #include <errno.h>
  17. #define SETCOLOR_SUCCESS (gANSIEscapes ? "\033\1331;32m" : "")
  18. #define SETCOLOR_FAILURE (gANSIEscapes ? "\033\1331;31m" : "")
  19. #define SETCOLOR_WARNING (gANSIEscapes ? "\033\1331;33m" : "")
  20. #define SETCOLOR_NORMAL (gANSIEscapes ? "\033\1330;39m" : "")
  21. #define TEXT_BLOCK_SIZE 8192
  22. #define INDENT 2
  23. #define TERMS "vt100:vt102:vt220:vt320:xterm:xterm-color:ansi:linux:scoterm:scoansi:dtterm:cons25:cygwin"
  24. size_t gNBufUsed = 0, gNBufAllocated = 0;
  25. char *gBuf = NULL;
  26. int gCCPID;
  27. char gAction[200] = "";
  28. char gTarget[200] = "";
  29. char gAr[32] = "";
  30. char gArLibraryTarget[64] = "";
  31. int gDumpCmdArgs = 0;
  32. char gArgsStr[1000];
  33. int gColumns = 80;
  34. int gANSIEscapes = 0;
  35. int gExitStatus = 95;
  36. static void DumpFormattedOutput(void)
  37. {
  38. char *cp;
  39. char spaces[8 + 1] = " ";
  40. char *saved;
  41. int curcol;
  42. int i;
  43. curcol = 0;
  44. saved = NULL;
  45. for (cp = gBuf + ((gDumpCmdArgs == 0) ? strlen(gArgsStr) : 0); ; cp++) {
  46. if (*cp == '\0') {
  47. if (saved != NULL) {
  48. cp = saved;
  49. saved = NULL;
  50. } else break;
  51. }
  52. if (*cp == '\r')
  53. continue;
  54. if (*cp == '\t') {
  55. saved = cp + 1;
  56. cp = spaces + 8 - (8 - ((curcol - INDENT - 1) % 8));
  57. }
  58. if (curcol == 0) {
  59. for (i = INDENT; --i >= 0; )
  60. putchar(' ');
  61. curcol = INDENT;
  62. }
  63. putchar(*cp);
  64. if (++curcol == (gColumns - 1)) {
  65. putchar('\n');
  66. curcol = 0;
  67. } else if (*cp == '\n')
  68. curcol = 0;
  69. }
  70. free(gBuf);
  71. } /* DumpFormattedOutput */
  72. /* Difftime(), only for timeval structures. */
  73. static void TimeValSubtract(struct timeval *tdiff, struct timeval *t1, struct timeval *t0)
  74. {
  75. tdiff->tv_sec = t1->tv_sec - t0->tv_sec;
  76. tdiff->tv_usec = t1->tv_usec - t0->tv_usec;
  77. if (tdiff->tv_usec < 0) {
  78. tdiff->tv_sec--;
  79. tdiff->tv_usec += 1000000;
  80. }
  81. } /* TimeValSubtract */
  82. static void Wait(void)
  83. {
  84. int pid2, status;
  85. do {
  86. status = 0;
  87. pid2 = (int) waitpid(gCCPID, &status, 0);
  88. } while (((pid2 >= 0) && (! WIFEXITED(status))) || ((pid2 < 0) && (errno == EINTR)));
  89. if (WIFEXITED(status))
  90. gExitStatus = WEXITSTATUS(status);
  91. } /* Wait */
  92. static int SlurpProgress(int fd)
  93. {
  94. char s1[71];
  95. char *newbuf;
  96. int nready;
  97. size_t ntoread;
  98. ssize_t nread;
  99. struct timeval now, tnext, tleft;
  100. fd_set ss;
  101. fd_set ss2;
  102. const char *trail = "/-\\|", *trailcp;
  103. trailcp = trail;
  104. snprintf(s1, sizeof(s1), "%s%s%s... ", gAction, gTarget[0] ? " " : "", gTarget);
  105. printf("\r%-70s%-9s", s1, "");
  106. fflush(stdout);
  107. gettimeofday(&now, NULL);
  108. tnext = now;
  109. tnext.tv_sec++;
  110. tleft.tv_sec = 1;
  111. tleft.tv_usec = 0;
  112. FD_ZERO(&ss2);
  113. FD_SET(fd, &ss2);
  114. for(;;) {
  115. if (gNBufUsed == (gNBufAllocated - 1)) {
  116. if ((newbuf = (char *) realloc(gBuf, gNBufAllocated + TEXT_BLOCK_SIZE)) == NULL) {
  117. perror("ccdv: realloc");
  118. return (-1);
  119. }
  120. gNBufAllocated += TEXT_BLOCK_SIZE;
  121. gBuf = newbuf;
  122. }
  123. for (;;) {
  124. ss = ss2;
  125. nready = select(fd + 1, &ss, NULL, NULL, &tleft);
  126. if (nready == 1)
  127. break;
  128. if (nready < 0) {
  129. if (errno != EINTR) {
  130. perror("ccdv: select");
  131. return (-1);
  132. }
  133. continue;
  134. }
  135. gettimeofday(&now, NULL);
  136. if ((now.tv_sec > tnext.tv_sec) || ((now.tv_sec == tnext.tv_sec) && (now.tv_usec >= tnext.tv_usec))) {
  137. tnext = now;
  138. tnext.tv_sec++;
  139. tleft.tv_sec = 1;
  140. tleft.tv_usec = 0;
  141. printf("\r%-71s%c%-7s", s1, *trailcp, "");
  142. fflush(stdout);
  143. if (*++trailcp == '\0')
  144. trailcp = trail;
  145. } else {
  146. TimeValSubtract(&tleft, &tnext, &now);
  147. }
  148. }
  149. ntoread = (gNBufAllocated - gNBufUsed - 1);
  150. nread = read(fd, gBuf + gNBufUsed, ntoread);
  151. if (nread < 0) {
  152. if (errno == EINTR)
  153. continue;
  154. perror("ccdv: read");
  155. return (-1);
  156. } else if (nread == 0) {
  157. break;
  158. }
  159. gNBufUsed += nread;
  160. gBuf[gNBufUsed] = '\0';
  161. }
  162. snprintf(s1, sizeof(s1), "%s%s%s: ", gAction, gTarget[0] ? " " : "", gTarget);
  163. Wait();
  164. if (gExitStatus == 0) {
  165. printf("\r%-70s", s1);
  166. printf("[%s%s%s]", ((gNBufUsed - strlen(gArgsStr)) < 4) ? SETCOLOR_SUCCESS : SETCOLOR_WARNING, "OK", SETCOLOR_NORMAL);
  167. printf("%-5s\n", " ");
  168. } else {
  169. printf("\r%-70s", s1);
  170. printf("[%s%s%s]", SETCOLOR_FAILURE, "ERROR", SETCOLOR_NORMAL);
  171. printf("%-2s\n", " ");
  172. gDumpCmdArgs = 1; /* print cmd when there are errors */
  173. }
  174. fflush(stdout);
  175. return (0);
  176. } /* SlurpProgress */
  177. static int SlurpAll(int fd)
  178. {
  179. char *newbuf;
  180. size_t ntoread;
  181. ssize_t nread;
  182. printf("%s%s%s.\n", gAction, gTarget[0] ? " " : "", gTarget);
  183. fflush(stdout);
  184. for(;;) {
  185. if (gNBufUsed == (gNBufAllocated - 1)) {
  186. if ((newbuf = (char *) realloc(gBuf, gNBufAllocated + TEXT_BLOCK_SIZE)) == NULL) {
  187. perror("ccdv: realloc");
  188. return (-1);
  189. }
  190. gNBufAllocated += TEXT_BLOCK_SIZE;
  191. gBuf = newbuf;
  192. }
  193. ntoread = (gNBufAllocated - gNBufUsed - 1);
  194. nread = read(fd, gBuf + gNBufUsed, ntoread);
  195. if (nread < 0) {
  196. if (errno == EINTR)
  197. continue;
  198. perror("ccdv: read");
  199. return (-1);
  200. } else if (nread == 0) {
  201. break;
  202. }
  203. gNBufUsed += nread;
  204. gBuf[gNBufUsed] = '\0';
  205. }
  206. Wait();
  207. gDumpCmdArgs = (gExitStatus != 0); /* print cmd when there are errors */
  208. return (0);
  209. } /* SlurpAll */
  210. static const char *Basename(const char *path)
  211. {
  212. const char *cp;
  213. cp = strrchr(path, '/');
  214. if (cp == NULL)
  215. return (path);
  216. return (cp + 1);
  217. } /* Basename */
  218. static const char * Extension(const char *path)
  219. {
  220. const char *cp = path;
  221. cp = strrchr(path, '.');
  222. if (cp == NULL)
  223. return ("");
  224. // printf("Extension='%s'\n", cp);
  225. return (cp);
  226. } /* Extension */
  227. static void Usage(void)
  228. {
  229. fprintf(stderr, "Usage: ccdv /path/to/cc CFLAGS...\n\n");
  230. fprintf(stderr, "I wrote this to reduce the deluge Make output to make finding actual problems\n");
  231. fprintf(stderr, "easier. It is intended to be invoked from Makefiles, like this. Instead of:\n\n");
  232. fprintf(stderr, "\t.c.o:\n");
  233. fprintf(stderr, "\t\t$(CC) $(CFLAGS) $(DEFS) $(CPPFLAGS) $< -c\n");
  234. fprintf(stderr, "\nRewrite your rule so it looks like:\n\n");
  235. fprintf(stderr, "\t.c.o:\n");
  236. fprintf(stderr, "\t\t@ccdv $(CC) $(CFLAGS) $(DEFS) $(CPPFLAGS) $< -c\n\n");
  237. fprintf(stderr, "ccdv 1.1.0 is Free under the GNU Public License. Enjoy!\n");
  238. fprintf(stderr, " -- Mike Gleason, NcFTP Software <http://www.ncftp.com>\n");
  239. exit(96);
  240. } /* Usage */
  241. int main(int argc, char **argv)
  242. {
  243. int pipe1[2];
  244. int devnull;
  245. char emerg[256];
  246. int fd;
  247. int nread;
  248. int i;
  249. int cc = 0, pch = 0;
  250. const char *quote;
  251. if (argc < 2)
  252. Usage();
  253. snprintf(gAction, sizeof(gAction), "Running %s", Basename(argv[1]));
  254. memset(gArgsStr, 0, sizeof(gArgsStr));
  255. for (i = 1; i < argc; i++) {
  256. // printf("argv[%d]='%s'\n", i, argv[i]);
  257. quote = (strchr(argv[i], ' ') != NULL) ? "\"" : "";
  258. snprintf(gArgsStr + strlen(gArgsStr), sizeof(gArgsStr) - strlen(gArgsStr), "%s%s%s%s%s", (i == 1) ? "" : " ", quote, argv[i], quote, (i == (argc - 1)) ? "\n" : "");
  259. if ((strcmp(argv[i], "-o") == 0) && ((i + 1) < argc)) {
  260. if (strcasecmp(Extension(argv[i + 1]), ".o") != 0) {
  261. strcpy(gAction, "Linking");
  262. snprintf(gTarget, sizeof(gTarget), "%s", Basename(argv[i + 1]));
  263. }
  264. } else if (strchr("-+", (int) argv[i][0]) != NULL) {
  265. continue;
  266. } else if (strncasecmp(Extension(argv[i]), ".c", 2) == 0) {
  267. cc++;
  268. snprintf(gTarget, sizeof(gTarget), "%s", Basename(argv[i]));
  269. // printf("gTarget='%s'\n", gTarget);
  270. } else if ((strncasecmp(Extension(argv[i]), ".h", 2) == 0) && (cc == 0)) {
  271. pch++;
  272. snprintf(gTarget, sizeof(gTarget), "%s", Basename(argv[i]));
  273. } else if ((i == 1) && (strcmp(Basename(argv[i]), "ar") == 0)) {
  274. snprintf(gAr, sizeof(gAr), "%s", Basename(argv[i]));
  275. } else if ((gArLibraryTarget[0] == '\0') && (strcasecmp(Extension(argv[i]), ".a") == 0)) {
  276. snprintf(gArLibraryTarget, sizeof(gArLibraryTarget), "%s", Basename(argv[i]));
  277. }
  278. }
  279. if ((gAr[0] != '\0') && (gArLibraryTarget[0] != '\0')) {
  280. strcpy(gAction, "Creating library");
  281. snprintf(gTarget, sizeof(gTarget), "%s", gArLibraryTarget);
  282. } else if (pch > 0) {
  283. strcpy(gAction, "Precompiling");
  284. } else if (cc > 0) {
  285. strcpy(gAction, "Compiling");
  286. }
  287. if (pipe(pipe1) < 0) {
  288. perror("ccdv: pipe");
  289. exit(97);
  290. }
  291. (void) close(0);
  292. devnull = open("/dev/null", O_RDWR, 00666);
  293. if ((devnull != 0) && (dup2(devnull, 0) == 0))
  294. close(devnull);
  295. gCCPID = (int) fork();
  296. if (gCCPID < 0) {
  297. (void) close(pipe1[0]);
  298. (void) close(pipe1[1]);
  299. perror("ccdv: fork");
  300. exit(98);
  301. } else if (gCCPID == 0) {
  302. /* Child */
  303. (void) close(pipe1[0]); /* close read end */
  304. if (pipe1[1] != 1) { /* use write end on stdout */
  305. (void) dup2(pipe1[1], 1);
  306. (void) close(pipe1[1]);
  307. }
  308. (void) dup2(1, 2); /* use write end on stderr */
  309. execvp(argv[1], argv + 1);
  310. perror(argv[1]);
  311. exit(99);
  312. }
  313. /* parent */
  314. (void) close(pipe1[1]); /* close write end */
  315. fd = pipe1[0]; /* use read end */
  316. gColumns = (getenv("COLUMNS") != NULL) ? atoi(getenv("COLUMNS")) : 80;
  317. gANSIEscapes = (getenv("TERM") != NULL) && (strstr(TERMS, getenv("TERM")) != NULL);
  318. gBuf = (char *) malloc(TEXT_BLOCK_SIZE);
  319. if (gBuf == NULL)
  320. goto panic;
  321. gNBufUsed = 0;
  322. gNBufAllocated = TEXT_BLOCK_SIZE;
  323. if (strlen(gArgsStr) < (gNBufAllocated - 1)) {
  324. strcpy(gBuf, gArgsStr);
  325. gNBufUsed = strlen(gArgsStr);
  326. }
  327. if (isatty(1)) {
  328. if (SlurpProgress(fd) < 0)
  329. goto panic;
  330. } else {
  331. if (SlurpAll(fd) < 0)
  332. goto panic;
  333. }
  334. DumpFormattedOutput();
  335. exit(gExitStatus);
  336. panic:
  337. gDumpCmdArgs = 1; /* print cmd when there are errors */
  338. DumpFormattedOutput();
  339. while ((nread = read(fd, emerg, (size_t) sizeof(emerg))) > 0)
  340. (void) write(2, emerg, (size_t) nread);
  341. Wait();
  342. exit(gExitStatus);
  343. } /* main */