Created
November 10, 2012 16:56
-
-
Save RavuAlHemio/4051694 to your computer and use it in GitHub Desktop.
fork-and-pipe debugging utility
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* @file pipes-are-evil.c | |
* @author Ondřej Hošek <[email protected]> | |
*/ | |
#include <ctype.h> | |
#include <errno.h> | |
#include <signal.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <unistd.h> | |
enum | |
{ | |
/** Number of bytes for the output filename buffer. */ | |
FNBUFSIZE = 128, | |
/** Length of one hex line. */ | |
HEXLINELEN = 16 | |
}; | |
/** Name of the program. */ | |
static const char *progname = "pipes-are-evil"; | |
/** | |
* Outputs an error message (taken from errno) and terminates the program. | |
* | |
* This function does not return. | |
* | |
* @param func Name of the function which failed. | |
*/ | |
__attribute__((noreturn)) | |
static void bail_out(const char *func) | |
{ | |
(void)fprintf(stderr, "%s: %s: [%d] %s\n", progname, func, errno, strerror(errno)); | |
exit(EXIT_FAILURE); | |
} | |
/** The file being written to. */ | |
static FILE *f = NULL; | |
/** | |
* Outputs a hexadecimal dump of the given line at the given file. | |
* | |
* @param f File into which to dump. | |
* @param pos Byte position of this line within the file. The first byte is at | |
* 00000000. | |
* @param line The line to hex-dump. | |
* @param linelen The length of the line to hex-dump. | |
* @param maxlinelen The maximum length of a line, for formatting purposes. | |
*/ | |
static void dump_line(FILE *f, size_t pos, const char *line, size_t linelen, size_t maxlinelen) | |
{ | |
size_t i; | |
/* output position */ | |
(void)fprintf(f, "%08zx ", pos); | |
/* output hexed bytes */ | |
for (i = 0; i < linelen; ++i) | |
{ | |
(void)fprintf(f, "%02x ", line[i]); | |
} | |
/* output padding */ | |
for (i = linelen; i < maxlinelen; ++i) | |
{ | |
(void)fputs(" ", f); | |
} | |
(void)fputc('|', f); | |
/* output characters */ | |
for (i = 0; i < linelen; ++i) | |
{ | |
(void)fputc(isprint(line[i]) ? line[i] : '.', f); | |
} | |
/* traditionally no padding here */ | |
/* end of line */ | |
(void)fputs("|\n", f); | |
/* flush */ | |
(void)fflush(f); | |
} | |
/** | |
* Signal handler. Tries to close f before exiting the program. | |
* @param signum Number of the signal that was raised. | |
* @param info Assorted information about the signal. | |
* @param ctx Assorted information about the machine context. | |
*/ | |
static void sighandler(int signum, siginfo_t *info, void *ctx) | |
{ | |
if (f != NULL) | |
{ | |
(void)fclose(f); | |
} | |
exit(EXIT_FAILURE); | |
} | |
/** | |
* The main entry point of the program. | |
* @param argc Number of arguments. | |
* @param argv Array of argument strings. | |
* @return 0 on success, non-zero on failure. | |
*/ | |
int main(int argc, char **argv) | |
{ | |
size_t i; | |
char outfn[FNBUFSIZE]; | |
char buf[HEXLINELEN]; | |
if (argc > 0) | |
{ | |
progname = argv[0]; | |
} | |
/* try handling signals */ | |
struct sigaction act; | |
sigset_t mask; | |
(void)sigemptyset(&mask); | |
act.sa_sigaction = sighandler; | |
act.sa_flags = SA_SIGINFO; | |
act.sa_mask = mask; | |
if (sigaction(SIGINT, &act, NULL) == -1) | |
{ | |
(void)fprintf(stderr, "%s: sigaction: [%d] %s\n", progname, errno, strerror(errno)); | |
/* continue */ | |
} | |
/* construct output file name */ | |
(void)snprintf(outfn, sizeof(outfn), "pae-%u.txt", getpid()); | |
/* open output file */ | |
f = fopen(outfn, "a"); | |
if (f == NULL) | |
{ | |
bail_out("fopen"); | |
} | |
/* output arguments */ | |
for (i = 0; i < argc; ++i) | |
{ | |
size_t l; | |
size_t pos = 0; | |
const char *arg = argv[i]; | |
(void)fprintf(f, "Argument %zu:\n", i); | |
while ((l = strlen(arg)) > HEXLINELEN) | |
{ | |
dump_line(f, pos, arg, HEXLINELEN, HEXLINELEN); | |
pos += HEXLINELEN; | |
arg += HEXLINELEN; | |
} | |
dump_line(f, pos, arg, strlen(arg), HEXLINELEN); | |
(void)fputc('\n', f); | |
(void)fflush(f); | |
} | |
/* read and output stdin */ | |
i = 0; | |
(void)fputs("stdin:\n", f); | |
for (;;) | |
{ | |
/* read */ | |
int locerr; | |
size_t got = fread(buf, sizeof(char), HEXLINELEN, stdin); | |
/* check for error */ | |
if (got < HEXLINELEN && ferror(stdin)) | |
{ | |
/* remember for later */ | |
locerr = errno; | |
} | |
/* output */ | |
dump_line(f, i, buf, got, HEXLINELEN); | |
/* check for trouble */ | |
if (got < HEXLINELEN) | |
{ | |
if (ferror(stdin)) | |
{ | |
/* alright then */ | |
(void)fprintf(f, "stdin raised error: [%d] %s\n", locerr, strerror(locerr)); | |
} | |
else if (ferror(stdin)) | |
{ | |
/* no worries */ | |
(void)fputs("stdin reached EOF\n", f); | |
} | |
break; | |
} | |
i += got; | |
} | |
/* that is quite enough */ | |
if (fclose(f) == EOF) | |
{ | |
bail_out("fclose"); | |
} | |
/* aaaaaaand we're done. */ | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment