mirror of
https://github.com/ZDoom/qzdoom.git
synced 2024-11-06 13:01:14 +00:00
464 lines
10 KiB
C
464 lines
10 KiB
C
|
/* ccdv.c */
|
||
|
#include <unistd.h>
|
||
|
#include <sys/types.h>
|
||
|
#include <sys/time.h>
|
||
|
#include <sys/wait.h>
|
||
|
#include <fcntl.h>
|
||
|
#include <stdio.h>
|
||
|
#include <string.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <errno.h>
|
||
|
|
||
|
#define SETCOLOR_SUCCESS (gANSIEscapes ? "\033\1331;32m" : "")
|
||
|
#define SETCOLOR_FAILURE (gANSIEscapes ? "\033\1331;31m" : "")
|
||
|
#define SETCOLOR_WARNING (gANSIEscapes ? "\033\1331;33m" : "")
|
||
|
#define SETCOLOR_COMMAND (gANSIEscapes ? "\033\1331;35m" : "")
|
||
|
#define SETCOLOR_ERROROUTPUT (gANSIEscapes ? "\033\1331;31m" : "")
|
||
|
#define SETCOLOR_WARNINGOUTPUT (gANSIEscapes ? "\033\1331;39m" : "")
|
||
|
#define SETCOLOR_NORMAL (gANSIEscapes ? "\033\1330;39m" : "")
|
||
|
|
||
|
#define TEXT_BLOCK_SIZE 8192
|
||
|
#define INDENT 2
|
||
|
|
||
|
size_t gNBufUsed = 0, gNBufAllocated = 0;
|
||
|
char *gBuf = NULL;
|
||
|
int gCCPID;
|
||
|
char gAction[64] = "";
|
||
|
char gTarget[64] = "";
|
||
|
char gAr[32] = "";
|
||
|
char gArLibraryTarget[64] = "";
|
||
|
int gDumpCmdArgs = 0;
|
||
|
char gArgsStr[32768];
|
||
|
int gColumns = 80;
|
||
|
int gANSIEscapes = 0;
|
||
|
int gExitStatus = 95;
|
||
|
|
||
|
static void DumpFormattedOutput(void)
|
||
|
{
|
||
|
char *cp;
|
||
|
char spaces[8 + 1] = " ";
|
||
|
char *saved;
|
||
|
int curcol;
|
||
|
int i;
|
||
|
|
||
|
curcol = 0;
|
||
|
saved = NULL;
|
||
|
if(gDumpCmdArgs)
|
||
|
{
|
||
|
printf("%s%s%s", SETCOLOR_COMMAND, gArgsStr, SETCOLOR_NORMAL);
|
||
|
for(i = 0; i < gColumns; ++i)
|
||
|
{
|
||
|
putchar('=');
|
||
|
}
|
||
|
}
|
||
|
printf("%s%s%s", gDumpCmdArgs ? SETCOLOR_ERROROUTPUT : SETCOLOR_WARNINGOUTPUT,
|
||
|
gBuf + strlen(gArgsStr), SETCOLOR_NORMAL);
|
||
|
free(gBuf);
|
||
|
} /* DumpFormattedOutput */
|
||
|
|
||
|
/* Difftime(), only for timeval structures. */
|
||
|
static void TimeValSubtract(struct timeval *tdiff, struct timeval *t1,
|
||
|
struct timeval *t0)
|
||
|
{
|
||
|
tdiff->tv_sec = t1->tv_sec - t0->tv_sec;
|
||
|
tdiff->tv_usec = t1->tv_usec - t0->tv_usec;
|
||
|
if(tdiff->tv_usec < 0)
|
||
|
{
|
||
|
tdiff->tv_sec--;
|
||
|
tdiff->tv_usec += 1000000;
|
||
|
}
|
||
|
} /* TimeValSubtract */
|
||
|
|
||
|
static void Wait(void)
|
||
|
{
|
||
|
int pid2, status;
|
||
|
|
||
|
do
|
||
|
{
|
||
|
status = 0;
|
||
|
pid2 = (int) waitpid(gCCPID, &status, 0);
|
||
|
}
|
||
|
while(((pid2 >= 0) && (!WIFEXITED(status)))
|
||
|
|| ((pid2 < 0) && (errno == EINTR)));
|
||
|
if(WIFEXITED(status))
|
||
|
gExitStatus = WEXITSTATUS(status);
|
||
|
} /* Wait */
|
||
|
|
||
|
static int SlurpProgress(int fd)
|
||
|
{
|
||
|
char s1[71];
|
||
|
char *newbuf;
|
||
|
int nready;
|
||
|
size_t ntoread;
|
||
|
ssize_t nread;
|
||
|
struct timeval now, tnext, tleft;
|
||
|
fd_set ss;
|
||
|
fd_set ss2;
|
||
|
const char *trail = "/-\\|", *trailcp;
|
||
|
|
||
|
trailcp = trail;
|
||
|
snprintf(s1, sizeof(s1), "%s%s%s... ", gAction, gTarget[0] ? " " : "",
|
||
|
gTarget);
|
||
|
printf("\r%-70s%-9s", s1, "");
|
||
|
fflush(stdout);
|
||
|
|
||
|
gettimeofday(&now, NULL);
|
||
|
tnext = now;
|
||
|
tnext.tv_sec++;
|
||
|
tleft.tv_sec = 1;
|
||
|
tleft.tv_usec = 0;
|
||
|
FD_ZERO(&ss2);
|
||
|
FD_SET(fd, &ss2);
|
||
|
for(;;)
|
||
|
{
|
||
|
if(gNBufUsed == (gNBufAllocated - 1))
|
||
|
{
|
||
|
if((newbuf =
|
||
|
(char *) realloc(gBuf,
|
||
|
gNBufAllocated + TEXT_BLOCK_SIZE)) == NULL)
|
||
|
{
|
||
|
perror("ccdv: realloc");
|
||
|
return (-1);
|
||
|
}
|
||
|
gNBufAllocated += TEXT_BLOCK_SIZE;
|
||
|
gBuf = newbuf;
|
||
|
}
|
||
|
for(;;)
|
||
|
{
|
||
|
ss = ss2;
|
||
|
nready = select(fd + 1, &ss, NULL, NULL, &tleft);
|
||
|
if(nready == 1)
|
||
|
break;
|
||
|
if(nready < 0)
|
||
|
{
|
||
|
if(errno != EINTR)
|
||
|
{
|
||
|
perror("ccdv: select");
|
||
|
return (-1);
|
||
|
}
|
||
|
continue;
|
||
|
}
|
||
|
gettimeofday(&now, NULL);
|
||
|
if((now.tv_sec > tnext.tv_sec)
|
||
|
|| ((now.tv_sec == tnext.tv_sec)
|
||
|
&& (now.tv_usec >= tnext.tv_usec)))
|
||
|
{
|
||
|
tnext = now;
|
||
|
tnext.tv_sec++;
|
||
|
tleft.tv_sec = 1;
|
||
|
tleft.tv_usec = 0;
|
||
|
printf("\r%-71s%c%-7s", s1, *trailcp, "");
|
||
|
fflush(stdout);
|
||
|
if(*++trailcp == '\0')
|
||
|
trailcp = trail;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
TimeValSubtract(&tleft, &tnext, &now);
|
||
|
}
|
||
|
}
|
||
|
ntoread = (gNBufAllocated - gNBufUsed - 1);
|
||
|
nread = read(fd, gBuf + gNBufUsed, ntoread);
|
||
|
if(nread < 0)
|
||
|
{
|
||
|
if(errno == EINTR)
|
||
|
continue;
|
||
|
perror("ccdv: read");
|
||
|
return (-1);
|
||
|
}
|
||
|
else if(nread == 0)
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
gNBufUsed += nread;
|
||
|
gBuf[gNBufUsed] = '\0';
|
||
|
}
|
||
|
snprintf(s1, sizeof(s1), "%s%s%s: ", gAction, gTarget[0] ? " " : "",
|
||
|
gTarget);
|
||
|
Wait();
|
||
|
if(gExitStatus == 0)
|
||
|
{
|
||
|
printf("\r%-70s", s1);
|
||
|
printf("[%s%s%s]",
|
||
|
((gNBufUsed - strlen(gArgsStr)) <
|
||
|
4) ? SETCOLOR_SUCCESS : SETCOLOR_WARNING, "OK",
|
||
|
SETCOLOR_NORMAL);
|
||
|
printf("%-5s\n", " ");
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
printf("\r%-70s", s1);
|
||
|
printf("[%s%s%s]", SETCOLOR_FAILURE, "ERROR", SETCOLOR_NORMAL);
|
||
|
printf("%-2s\n", " ");
|
||
|
gDumpCmdArgs = 1; /* print cmd when there are errors */
|
||
|
}
|
||
|
fflush(stdout);
|
||
|
return (0);
|
||
|
} /* SlurpProgress */
|
||
|
|
||
|
static int SlurpAll(int fd)
|
||
|
{
|
||
|
char *newbuf;
|
||
|
size_t ntoread;
|
||
|
ssize_t nread;
|
||
|
|
||
|
printf("%s%s%s.\n", gAction, gTarget[0] ? " " : "", gTarget);
|
||
|
fflush(stdout);
|
||
|
|
||
|
for(;;)
|
||
|
{
|
||
|
if(gNBufUsed == (gNBufAllocated - 1))
|
||
|
{
|
||
|
if((newbuf =
|
||
|
(char *) realloc(gBuf,
|
||
|
gNBufAllocated + TEXT_BLOCK_SIZE)) == NULL)
|
||
|
{
|
||
|
perror("ccdv: realloc");
|
||
|
return (-1);
|
||
|
}
|
||
|
gNBufAllocated += TEXT_BLOCK_SIZE;
|
||
|
gBuf = newbuf;
|
||
|
}
|
||
|
ntoread = (gNBufAllocated - gNBufUsed - 1);
|
||
|
nread = read(fd, gBuf + gNBufUsed, ntoread);
|
||
|
if(nread < 0)
|
||
|
{
|
||
|
if(errno == EINTR)
|
||
|
continue;
|
||
|
perror("ccdv: read");
|
||
|
return (-1);
|
||
|
}
|
||
|
else if(nread == 0)
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
gNBufUsed += nread;
|
||
|
gBuf[gNBufUsed] = '\0';
|
||
|
}
|
||
|
Wait();
|
||
|
gDumpCmdArgs = (gExitStatus != 0); /* print cmd when there are errors */
|
||
|
return (0);
|
||
|
} /* SlurpAll */
|
||
|
|
||
|
static const char *Basename(const char *path)
|
||
|
{
|
||
|
const char *cp;
|
||
|
cp = strrchr(path, '/');
|
||
|
if(cp == NULL)
|
||
|
return (path);
|
||
|
return (cp + 1);
|
||
|
} /* Basename */
|
||
|
|
||
|
static const char *Extension(const char *path)
|
||
|
{
|
||
|
const char *cp = path;
|
||
|
cp = strrchr(path, '.');
|
||
|
if(cp == NULL)
|
||
|
return ("");
|
||
|
return (cp);
|
||
|
} /* Extension */
|
||
|
|
||
|
static void Usage(void)
|
||
|
{
|
||
|
char *sUsage = "\
|
||
|
Usage: ccdv /path/to/cc CFLAGS...\n\
|
||
|
\n\
|
||
|
I wrote this to reduce the deluge Make output to make finding actual problems\n\
|
||
|
easier. It is intended to be invoked from Makefiles, like this. Instead of:\n\
|
||
|
\n\
|
||
|
.c.o:\n\
|
||
|
$(CC) $(CFLAGS) $(DEFS) $(CPPFLAGS) $< -c\n\
|
||
|
\n\
|
||
|
Rewrite your rule so it looks like:\n\
|
||
|
\n\
|
||
|
.c.o:\n\
|
||
|
@ccdv $(CC) $(CFLAGS) $(DEFS) $(CPPFLAGS) $< -c\n\
|
||
|
.cpp.o:\n\
|
||
|
@ccdv $(CXX) $(CFLAGS) $(DEFS) $(CPPFLAGS) $< -c\n\
|
||
|
\n\
|
||
|
ccdv 1.1.0 is Free under the GNU Public License. Enjoy!\n\
|
||
|
-- Mike Gleason, NcFTP Software <http://www.ncftp.com>\n\
|
||
|
-- John F Meinel Jr, <http://ccdv.sourceforge.net>\n\
|
||
|
";
|
||
|
fprintf(stderr, sUsage);
|
||
|
exit(96);
|
||
|
} /* Usage */
|
||
|
|
||
|
int main(int argc, char **argv)
|
||
|
{
|
||
|
int pipe1[2];
|
||
|
int devnull;
|
||
|
char emerg[256];
|
||
|
int fd;
|
||
|
int nread;
|
||
|
int i;
|
||
|
int cc = 0;
|
||
|
int yy = 0;
|
||
|
int gcc = 0;
|
||
|
int lemon = 0;
|
||
|
const char *quote;
|
||
|
|
||
|
if(argc < 2)
|
||
|
Usage();
|
||
|
|
||
|
snprintf(gAction, sizeof(gAction), "Running %s", Basename(argv[1]));
|
||
|
memset(gArgsStr, 0, sizeof(gArgsStr));
|
||
|
|
||
|
if(strcmp(gAction+8, "ar") == 0)
|
||
|
{
|
||
|
snprintf(gTarget, sizeof(gTarget), "%s", Basename(argv[i + 1]));
|
||
|
}
|
||
|
else if(strcmp(gAction+8, "cc") == 0 ||
|
||
|
strcmp(gAction+8, "ld") == 0 ||
|
||
|
strcmp(gAction+8, "gcc") == 0 ||
|
||
|
strcmp(gAction+8, "g++") == 0 ||
|
||
|
strcmp(gAction+8, "c++") == 0)
|
||
|
{
|
||
|
gcc = 1;
|
||
|
}
|
||
|
else if(strcmp(gAction+8, "lemon") == 0)
|
||
|
{
|
||
|
lemon = 1;
|
||
|
}
|
||
|
|
||
|
for(i = 1; i < argc; i++)
|
||
|
{
|
||
|
quote = (strchr(argv[i], ' ') != NULL) ? "\"" : "";
|
||
|
snprintf(gArgsStr + strlen(gArgsStr),
|
||
|
sizeof(gArgsStr) - strlen(gArgsStr), "%s%s%s%s%s",
|
||
|
(i == 1) ? "" : " ", quote, argv[i], quote,
|
||
|
(i == (argc - 1)) ? "\n" : "");
|
||
|
if(gcc && (strcmp(argv[i], "-o") == 0) && ((i + 1) < argc))
|
||
|
{
|
||
|
if(strcasecmp(Extension(argv[i + 1]), ".o") != 0)
|
||
|
{
|
||
|
strcpy(gAction, "Linking");
|
||
|
snprintf(gTarget, sizeof(gTarget), "%s",
|
||
|
Basename(argv[i + 1]));
|
||
|
}
|
||
|
}
|
||
|
else if(strchr("-+/", (int) argv[i][0]) != NULL)
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
else if(strncasecmp(Extension(argv[i]), ".c", 2) == 0)
|
||
|
{
|
||
|
cc++;
|
||
|
snprintf(gTarget, sizeof(gTarget), "%s", Basename(argv[i]));
|
||
|
}
|
||
|
else if(strncasecmp(Extension(argv[i]), ".y", 2) == 0)
|
||
|
{
|
||
|
if(lemon)
|
||
|
{
|
||
|
strcpy(gAction, "Generating");
|
||
|
snprintf(gTarget, sizeof(gTarget), "%s", Basename(argv[i]));
|
||
|
}
|
||
|
yy++;
|
||
|
}
|
||
|
else if(strncasecmp(Extension(argv[i]), ".nas", 3) == 0 ||
|
||
|
strncasecmp(Extension(argv[i]), ".asm", 3) == 0 ||
|
||
|
strcasecmp(Extension(argv[i]), ".s") == 0)
|
||
|
{
|
||
|
strcpy(gAction, "Assembling");
|
||
|
snprintf(gTarget, sizeof(gTarget), "%s", Basename(argv[i]));
|
||
|
}
|
||
|
else if((i == 1) && (strcmp(Basename(argv[i]), "ar") == 0))
|
||
|
{
|
||
|
snprintf(gAr, sizeof(gAr), "%s", Basename(argv[i]));
|
||
|
}
|
||
|
else if((gArLibraryTarget[0] == '\0')
|
||
|
&& (strcasecmp(Extension(argv[i]), ".a") == 0))
|
||
|
{
|
||
|
snprintf(gArLibraryTarget, sizeof(gArLibraryTarget), "%s",
|
||
|
Basename(argv[i]));
|
||
|
}
|
||
|
}
|
||
|
if((gAr[0] != '\0') && (gArLibraryTarget[0] != '\0'))
|
||
|
{
|
||
|
strcpy(gAction, "Creating library");
|
||
|
snprintf(gTarget, sizeof(gTarget), "%s", gArLibraryTarget);
|
||
|
}
|
||
|
else if(cc > 0)
|
||
|
{
|
||
|
strcpy(gAction, yy == 0 ? "Compiling" : "Generating");
|
||
|
}
|
||
|
|
||
|
if(pipe(pipe1) < 0)
|
||
|
{
|
||
|
perror("ccdv: pipe");
|
||
|
exit(97);
|
||
|
}
|
||
|
|
||
|
(void) close(0);
|
||
|
devnull = open("/dev/null", O_RDWR, 00666);
|
||
|
if((devnull != 0) && (dup2(devnull, 0) == 0))
|
||
|
close(devnull);
|
||
|
|
||
|
gCCPID = (int) fork();
|
||
|
if(gCCPID < 0)
|
||
|
{
|
||
|
(void) close(pipe1[0]);
|
||
|
(void) close(pipe1[1]);
|
||
|
perror("ccdv: fork");
|
||
|
exit(98);
|
||
|
}
|
||
|
else if(gCCPID == 0)
|
||
|
{
|
||
|
/* Child */
|
||
|
(void) close(pipe1[0]); /* close read end */
|
||
|
if(pipe1[1] != 1)
|
||
|
{ /* use write end on stdout */
|
||
|
(void) dup2(pipe1[1], 1);
|
||
|
(void) close(pipe1[1]);
|
||
|
}
|
||
|
(void) dup2(1, 2); /* use write end on stderr */
|
||
|
execvp(argv[1], argv + 1);
|
||
|
perror(argv[1]);
|
||
|
exit(99);
|
||
|
}
|
||
|
|
||
|
/* parent */
|
||
|
(void) close(pipe1[1]); /* close write end */
|
||
|
fd = pipe1[0]; /* use read end */
|
||
|
|
||
|
gColumns = (getenv("COLUMNS") != NULL) ? atoi(getenv("COLUMNS")) : 80;
|
||
|
gANSIEscapes = (getenv("TERM") != NULL)
|
||
|
&&
|
||
|
(strstr
|
||
|
("vt100:vt102:vt220:vt320:xterm:ansi:linux:scoterm:scoansi:dtterm:cons25:cygwin",
|
||
|
getenv("TERM")) != NULL);
|
||
|
gBuf = (char *) malloc(TEXT_BLOCK_SIZE);
|
||
|
if(gBuf == NULL)
|
||
|
goto panic;
|
||
|
gNBufUsed = 0;
|
||
|
gNBufAllocated = TEXT_BLOCK_SIZE;
|
||
|
if(strlen(gArgsStr) < (gNBufAllocated - 1))
|
||
|
{
|
||
|
strcpy(gBuf, gArgsStr);
|
||
|
gNBufUsed = strlen(gArgsStr);
|
||
|
}
|
||
|
|
||
|
if(isatty(1))
|
||
|
{
|
||
|
if(SlurpProgress(fd) < 0)
|
||
|
goto panic;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if(SlurpAll(fd) < 0)
|
||
|
goto panic;
|
||
|
}
|
||
|
DumpFormattedOutput();
|
||
|
exit(gExitStatus);
|
||
|
|
||
|
panic:
|
||
|
gDumpCmdArgs = 1; /* print cmd when there are errors */
|
||
|
DumpFormattedOutput();
|
||
|
while((nread = read(fd, emerg, (size_t) sizeof(emerg))) > 0)
|
||
|
(void) write(2, emerg, (size_t) nread);
|
||
|
Wait();
|
||
|
exit(gExitStatus);
|
||
|
} /* main */
|
||
|
|
||
|
/* eof ccdv.c */
|