>From 8c3d2a3019f21efce441d58e8084959cfe7bd043 Mon Sep 17 00:00:00 2001 From: Daniel Santos Date: Thu, 20 Dec 2012 03:51:39 -0600 Subject: md5sum: pipe --- src/md5sum.c | 133 ++++++++++++++++++++++++++++++++++++++++++++++++--------- 1 files changed, 112 insertions(+), 21 deletions(-) diff --git a/src/md5sum.c b/src/md5sum.c index 1663c1e..1953e67 100644 --- a/src/md5sum.c +++ b/src/md5sum.c @@ -39,6 +39,7 @@ #include "fadvise.h" #include "stdio--.h" #include "xfreopen.h" +#include "close-stream.h" /* The official name of this program (e.g., no 'g' prefix). */ #if HASH_ALGO_MD5 @@ -129,6 +130,14 @@ static bool strict = false; /* Whether a BSD reversed format checksum is detected. */ static int bsd_reversed = -1; +static bool pipe_to_stdout = false; +static const char *out_filename = NULL; +FILE *outfile = NULL; + +#ifndef unlikely +#define unlikely(exp) (exp) +#endif + /* For long options that have no equivalent short option, use a non-character as a pseudo short option, starting with CHAR_MAX + 1. */ enum @@ -149,6 +158,9 @@ static struct option const long_options[] = { "warn", no_argument, NULL, 'w' }, { "strict", no_argument, NULL, STRICT_OPTION }, { "tag", no_argument, NULL, TAG_OPTION }, + { "outfile", required_argument, NULL, 'o' }, + { "append", no_argument, NULL, 'a' }, + { "pipe", no_argument, NULL, 'p' }, { GETOPT_HELP_OPTION_DECL }, { GETOPT_VERSION_OPTION_DECL }, { NULL, 0, NULL, 0 } @@ -203,6 +215,16 @@ The following three options are useful only when verifying checksums:\n\ fputs (_("\ --strict with --check, exit non-zero for any invalid input\n\ "), stdout); + fputs (_("\ + -o, --outfile file output results to the specified file instead of\n\ + standard out.\n\ +"), stdout); + fputs (_("\ + -a, --append append to output file (used with --output)\n\ +"), stdout); + fputs (_("\ + -p, --pipe pipe through md5sum (must be used with --outfile)\n\ +"), stdout); fputs (HELP_OPTION_DESCRIPTION, stdout); fputs (VERSION_OPTION_DESCRIPTION, stdout); printf (_("\ @@ -457,10 +479,15 @@ digest_file (const char *filename, int *binary, unsigned char *bin_result) fadvise (fp, FADVISE_SEQUENTIAL); - err = DIGEST_STREAM (fp, bin_result); - if (err) + err = DIGEST_STREAM (fp, pipe_to_stdout ? stdout : NULL, bin_result); + + if (unlikely(err)) { - error (0, errno, "%s", filename); + if (err == 1 /* DIGEST_STREAM_READ_ERROR */) + error (0, errno, "%s: %s", _("read error"), filename); + else + error (0, errno, "%s: stdout", _("write error")); + if (fp != stdin) fclose (fp); return false; @@ -667,21 +694,36 @@ print_filename (char const *file) switch (*file) { case '\n': - fputs ("\\n", stdout); + fputs ("\\n", outfile); break; case '\\': - fputs ("\\\\", stdout); + fputs ("\\\\", outfile); break; default: - putchar (*file); + putc (*file, outfile); break; } file++; } } +static void +md5sum_cleanup (void) +{ + if (pipe_to_stdout && fflush (stdout)) + error (0, errno, _("error flushing stdout")); + + if (outfile && outfile != stdout && close_stream (outfile)) + error (0, errno, _("error closing: %s"), out_filename); + + if (out_filename) + free(bad_cast (out_filename)); + + close_stdout(); +} + int main (int argc, char **argv) { @@ -693,6 +735,7 @@ main (int argc, char **argv) bool ok = true; int binary = -1; bool prefix_tag = false; + const char *outfile_mode = "w"; /* Setting values of global variables. */ initialize_main (&argc, &argv); @@ -701,13 +744,9 @@ main (int argc, char **argv) bindtextdomain (PACKAGE, LOCALEDIR); textdomain (PACKAGE); - atexit (close_stdout); + atexit (md5sum_cleanup); - /* Line buffer stdout to ensure lines are written atomically and immediately - so that processes running in parallel do not intersperse their output. */ - setvbuf (stdout, NULL, _IOLBF, 0); - - while ((opt = getopt_long (argc, argv, "bctw", long_options, NULL)) != -1) + while ((opt = getopt_long (argc, argv, "bctwo:pa", long_options, NULL)) != -1) switch (opt) { case 'b': @@ -741,6 +780,15 @@ main (int argc, char **argv) prefix_tag = true; binary = 1; break; + case 'p': + pipe_to_stdout = true; + break; + case 'o': + out_filename = strdup(optarg); + break; + case 'a': + outfile_mode = "a"; + break; case_GETOPT_HELP_CHAR; case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS); default: @@ -803,6 +851,49 @@ main (int argc, char **argv) usage (EXIT_FAILURE); } + if (pipe_to_stdout && !out_filename) + { + error (0, 0, _("--pipe must be accompanied by --outfile")); + usage (EXIT_FAILURE); + } + + /* I'm not sure how strict to be here. */ + if (pipe_to_stdout && isatty(STDOUT_FILENO) && binary) + { + error (0, 0, _("refusing to write binary data to a terminal")); + usage (EXIT_FAILURE); + } + + if (pipe_to_stdout && optind >= (argc + 1)) + { + error (0, 0, _("--pipe cannot be used on multiple files.")); + usage (EXIT_FAILURE); + } + + if (!outfile && *outfile_mode == 'a') + { + error (0, 0, + _("the --append option is meaningful only when accompanied by " + "--outfile")); + usage (EXIT_FAILURE); + } + + if (!out_filename) + outfile = stdout; + else + { + if (!(outfile = fopen(out_filename, outfile_mode))) + { + error (0, 0, _("Failed to open file for writing: %s"), + out_filename); + exit (EXIT_FAILURE); + } + } + + /* Line buffer stdout to ensure lines are written atomically and immediately + so that processes running in parallel do not intersperse their output. */ + setvbuf (outfile, NULL, _IOLBF, 0); + if (!O_BINARY && binary < 0) binary = 0; @@ -826,12 +917,12 @@ main (int argc, char **argv) if (prefix_tag) { if (strchr (file, '\n') || strchr (file, '\\')) - putchar ('\\'); + putc ('\\', outfile); - fputs (DIGEST_TYPE_STRING, stdout); - fputs (" (", stdout); + fputs (DIGEST_TYPE_STRING, outfile); + fputs (" (", outfile); print_filename (file); - fputs (") = ", stdout); + fputs (") = ", outfile); } size_t i; @@ -839,21 +930,21 @@ main (int argc, char **argv) /* Output a leading backslash if the file name contains a newline or backslash. */ if (!prefix_tag && (strchr (file, '\n') || strchr (file, '\\'))) - putchar ('\\'); + putc ('\\', outfile); for (i = 0; i < (digest_hex_bytes / 2); ++i) - printf ("%02x", bin_buffer[i]); + fprintf (outfile, "%02x", bin_buffer[i]); if (!prefix_tag) { - putchar (' '); + putc (' ', outfile); - putchar (file_is_binary ? '*' : ' '); + putc (file_is_binary ? '*' : ' ', outfile); print_filename (file); } - putchar ('\n'); + putc ('\n', outfile); } } } -- 1.7.8.6