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