diff options
author | Jim Meyering <jim@meyering.net> | 1997-11-30 10:24:58 +0000 |
---|---|---|
committer | Jim Meyering <jim@meyering.net> | 1997-11-30 10:24:58 +0000 |
commit | 72bc3b818f6c7e9e190afee12c133275c88fe59f (patch) | |
tree | d28849cd51f1478c4738c6a9164320349ced7223 /src/dd.c | |
parent | a5fe3a240b7bf15077b30bba1a205a8af27c5d2b (diff) | |
download | coreutils-72bc3b818f6c7e9e190afee12c133275c88fe59f.tar.xz |
(<inttypes.h>): Include if HAVE_INTTYPES_H.
("human.h"): Include.
(input_blocksize, output_blocksize, conversion_blocksize):
Now size_t instead of long. 0 means unset.
(skip_records, seek_record, max_records): Now uintmax_t, not long.
(w_partial, w_full, r_partial, r_full, r_truncate):
Now uintmax_t instead of unsigned.
(print_stats): Print counts as uintmax_t, not unsigned.
(main, skip): Check for overflow when computing file offsets.
(skip): Records count arg is uintmax_t, not long; blocksize arg is
size_t, not long. Try lseek even on non regular files, as per comment.
(oc, col): Now size_t, not int.
(copy): No need to check max_records >= 0 any more, as the
default value is now effectively infinity.
Cast lseek arg to off_t.
(copy, copy_with_block): conversion_blocksize - col can never
be negative now, since it's unsigned, so rewrite loops to
avoid problems with unsigned.
(scanargs): Parse numeric args using uintmax_t, not int.
Check for overflow when converting block size args to size_t.
Blocksize options are now unsigned, and are now 0 when not set yet.
(parse_integer): Return uintmax_t, not int; accept new int * arg
to store error indicator, since all returned values are now valid.
Check for overflow when scanning integer.
Diffstat (limited to 'src/dd.c')
-rw-r--r-- | src/dd.c | 215 |
1 files changed, 125 insertions, 90 deletions
@@ -58,10 +58,14 @@ #define SWAB_ALIGN_OFFSET 2 +#if HAVE_INTTYPES_H +# include <inttypes.h> +#endif #include <sys/types.h> #include <signal.h> #include <getopt.h> +#include "human.h" #include "system.h" #include "error.h" @@ -98,7 +102,7 @@ int full_write (); static RETSIGTYPE interrupt_handler __P ((int)); static int bit_count __P ((register unsigned int i)); -static int parse_integer __P ((char *str)); +static uintmax_t parse_integer __P ((char *str, int *)); static void apply_translations __P ((void)); static void copy __P ((void)); static void copy_simple __P ((unsigned char *buf, int nread)); @@ -108,8 +112,8 @@ static void parse_conversion __P ((char *str)); static void translate_charset __P ((const unsigned char *new_trans)); static void quit __P ((int code)); static void scanargs __P ((int argc, char **argv)); -static void skip __P ((int fdesc, char *file, long int records, - long int blocksize, unsigned char *buf)); +static void skip __P ((int fdesc, char *file, uintmax_t records, + size_t blocksize, unsigned char *buf)); static void usage __P ((int status)); static void write_output __P ((void)); @@ -129,22 +133,22 @@ static char *output_file = NULL; static int output_fd = 1; /* The number of bytes in which atomic reads are done. */ -static long input_blocksize = -1; +static size_t input_blocksize = 0; /* The number of bytes in which atomic writes are done. */ -static long output_blocksize = -1; +static size_t output_blocksize = 0; /* Conversion buffer size, in bytes. 0 prevents conversions. */ -static long conversion_blocksize = 0; +static size_t conversion_blocksize = 0; /* Skip this many records of `input_blocksize' bytes before input. */ -static long skip_records = 0; +static uintmax_t skip_records = 0; /* Skip this many records of `output_blocksize' bytes before output. */ -static long seek_record = 0; +static uintmax_t seek_record = 0; -/* Copy only this many records. <0 means no limit. */ -static int max_records = -1; +/* Copy only this many records. The default is effectively infinity. */ +static uintmax_t max_records = (uintmax_t) -1; /* Bit vector of conversions to apply. */ static int conversions_mask = 0; @@ -153,19 +157,19 @@ static int conversions_mask = 0; static int translation_needed = 0; /* Number of partial blocks written. */ -static unsigned w_partial = 0; +static uintmax_t w_partial = 0; /* Number of full blocks written. */ -static unsigned w_full = 0; +static uintmax_t w_full = 0; /* Number of partial blocks read. */ -static unsigned r_partial = 0; +static uintmax_t r_partial = 0; /* Number of full blocks read. */ -static unsigned r_full = 0; +static uintmax_t r_full = 0; /* Records truncated by conv=block. */ -static unsigned r_truncate = 0; +static uintmax_t r_truncate = 0; /* Output representation of newline and space characters. They change if we're converting to EBCDIC. */ @@ -321,10 +325,16 @@ static struct option const long_options[] = static void print_stats (void) { - fprintf (stderr, _("%u+%u records in\n"), r_full, r_partial); - fprintf (stderr, _("%u+%u records out\n"), w_full, w_partial); + char buf[2][LONGEST_HUMAN_READABLE + 1]; + fprintf (stderr, _("%s+%s records in\n"), + human_readable (r_full, buf[0], 1, 1, 0), + human_readable (r_partial, buf[1], 1, 1, 0)); + fprintf (stderr, _("%s+%s records out\n"), + human_readable (w_full, buf[0], 1, 1, 0), + human_readable (w_partial, buf[1], 1, 1, 0)); if (r_truncate > 0) - fprintf (stderr, "%u %s\n", r_truncate, + fprintf (stderr, "%s %s\n", + human_readable (r_truncate, buf[0], 1, 1, 0), (r_truncate == 1 ? _("truncated record") : _("truncated records"))); @@ -409,9 +419,12 @@ main (int argc, char **argv) if (output_fd < 0) error (1, errno, "%s", output_file); #ifdef HAVE_FTRUNCATE - if (seek_record > 0 && !(conversions_mask & C_NOTRUNC)) + if (seek_record != 0 && !(conversions_mask & C_NOTRUNC)) { - if (ftruncate (output_fd, seek_record * output_blocksize) < 0) + off_t o = seek_record * output_blocksize; + if (o / output_blocksize != seek_record) + error (1, 0, _("file offset out of range")); + if (ftruncate (output_fd, o) < 0) error (0, errno, "%s", output_file); } #endif @@ -434,10 +447,11 @@ main (int argc, char **argv) bytes of the data at a time in BUF, if necessary. */ static void -skip (int fdesc, char *file, long int records, long int blocksize, +skip (int fdesc, char *file, uintmax_t records, size_t blocksize, unsigned char *buf) { struct stat stats; + off_t o; /* Use fstat instead of checking for errno == ESPIPE because lseek doesn't work on some special files but doesn't return an @@ -449,18 +463,11 @@ skip (int fdesc, char *file, long int records, long int blocksize, quit (1); } - /* FIXME: why use lseek only on regular files? - Better: try lseek and if an error indicates it was an inappropriate + /* Try lseek and if an error indicates it was an inappropriate operation, fall back on using read. */ - if (S_ISREG (stats.st_mode)) - { - if (lseek (fdesc, records * blocksize, SEEK_SET) < 0) - { - error (0, errno, "%s", file); - quit (1); - } - } - else + o = records * blocksize; + if (o / blocksize != records + || lseek (fdesc, o, SEEK_SET) < 0) { while (records-- > 0) { @@ -542,10 +549,10 @@ swab_buffer (unsigned char *buf, int *nread) static unsigned char *obuf; /* Current index into `obuf'. */ -static int oc = 0; +static size_t oc = 0; /* Index into current line, for `conv=block' and `conv=unblock'. */ -static int col = 0; +static size_t col = 0; /* The main loop. */ @@ -569,10 +576,10 @@ copy (void) else obuf = ibuf; - if (skip_records > 0) + if (skip_records != 0) skip (input_fd, input_file, skip_records, input_blocksize, ibuf); - if (seek_record > 0) + if (seek_record != 0) { /* FIXME: this loses for % ./dd if=dd seek=1 |: @@ -589,7 +596,7 @@ copy (void) while (1) { - if (max_records >= 0 && r_partial + r_full >= max_records) + if (r_partial + r_full >= max_records) break; /* Zero the buffer before reading, so that if we get a read error, @@ -610,7 +617,7 @@ copy (void) { print_stats (); /* Seek past the bad block if possible. */ - lseek (input_fd, input_blocksize, SEEK_CUR); + lseek (input_fd, (off_t) input_blocksize, SEEK_CUR); if (conversions_mask & C_SYNC) /* Replace the missing input with null bytes and proceed normally. */ @@ -688,12 +695,9 @@ copy (void) { /* If the final input line didn't end with a '\n', pad the output block to `conversion_blocksize' chars. */ - int pending_spaces = max (0, conversion_blocksize - col); - while (pending_spaces) - { - output_char (space_character); - --pending_spaces; - } + size_t i; + for (i = col; i < conversion_blocksize; i++) + output_char (space_character); } if ((conversions_mask & C_UNBLOCK) && col == conversion_blocksize) @@ -702,7 +706,7 @@ copy (void) output_char (newline_character); /* Write out the last block. */ - if (oc > 0) + if (oc != 0) { int nwritten = full_write (output_fd, obuf, oc); if (nwritten > 0) @@ -759,11 +763,11 @@ copy_with_block (unsigned char *buf, int nread) { if (*buf == newline_character) { - int pending_spaces = max (0, conversion_blocksize - col); - while (pending_spaces) + if (col < conversion_blocksize) { - output_char (space_character); - --pending_spaces; + size_t j; + for (j = col; j < conversion_blocksize; j++) + output_char (space_character); } col = 0; } @@ -836,7 +840,7 @@ write_output (void) static void scanargs (int argc, char **argv) { - int i, n; + int i; int c; while ((c = getopt_long (argc, argv, "", long_options, NULL)) != -1) @@ -872,24 +876,32 @@ scanargs (int argc, char **argv) parse_conversion (val); else { - n = parse_integer (val); - if (n < 0) - error (1, 0, _("invalid number `%s'"), val); + int invalid = 0; + uintmax_t n = parse_integer (val, &invalid); if (STREQ (name, "ibs")) { input_blocksize = n; + invalid |= input_blocksize != n || input_blocksize == 0; conversions_mask |= C_TWOBUFS; } else if (STREQ (name, "obs")) { output_blocksize = n; + invalid |= output_blocksize != n || output_blocksize == 0; conversions_mask |= C_TWOBUFS; } else if (STREQ (name, "bs")) - output_blocksize = input_blocksize = n; + { + output_blocksize = input_blocksize = n; + invalid |= output_blocksize != n || output_blocksize == 0; + } else if (STREQ (name, "cbs")) - conversion_blocksize = n; + { + conversion_blocksize = n; + invalid |= (conversion_blocksize != n + || conversion_blocksize == 0); + } else if (STREQ (name, "skip")) skip_records = n; else if (STREQ (name, "seek")) @@ -901,17 +913,20 @@ scanargs (int argc, char **argv) error (0, 0, _("unrecognized option `%s=%s'"), name, val); usage (1); } + + if (invalid) + error (1, 0, _("invalid number `%s'"), val); } } /* If bs= was given, both `input_blocksize' and `output_blocksize' will - have been set to non-negative values. If either has not been set, + have been set to positive values. If either has not been set, bs= was not given, so make sure two buffers are used. */ - if (input_blocksize == -1 || output_blocksize == -1) + if (input_blocksize == 0 || output_blocksize == 0) conversions_mask |= C_TWOBUFS; - if (input_blocksize == -1) + if (input_blocksize == 0) input_blocksize = DEFAULT_BLOCKSIZE; - if (output_blocksize == -1) + if (output_blocksize == 0) output_blocksize = DEFAULT_BLOCKSIZE; if (conversion_blocksize == 0) conversions_mask &= ~(C_BLOCK | C_UNBLOCK); @@ -919,48 +934,68 @@ scanargs (int argc, char **argv) /* Return the value of STR, interpreted as a non-negative decimal integer, optionally multiplied by various values. - Return -1 if STR does not represent a number in this format. */ + Assign nonzero to *INVALID if STR does not represent a number in + this format. */ -/* FIXME: use xstrtou?l */ +/* FIXME: use xstrtou?[lq] */ -static int -parse_integer (char *str) +static uintmax_t +parse_integer (char *str, int *invalid) { - register int n = 0; - register int temp; + register uintmax_t n = 0; register char *p = str; while (ISDIGIT (*p)) { - n = n * 10 + *p - '0'; + uintmax_t n10 = n * 10; + int digit = *p - '0'; + if (! (n10 / 10 == n && n10 <= n10 + digit)) + { + *invalid = 1; + return 0; + } + n = n10 + digit; p++; } -loop: - switch (*p++) + + for (;;) { - case '\0': - return n; - case 'b': - n *= 512; - goto loop; - case 'c': - goto loop; - case 'k': - n *= 1024; - goto loop; - case 'w': - n *= 2; - goto loop; - case 'x': - temp = parse_integer (p); - if (temp == -1) - return -1; - n *= temp; - break; - default: - return -1; + uintmax_t multiplier; + + switch (*p++) + { + case '\0': + return n; + case 'b': + multiplier = 512; + break; + case 'c': + continue; + case 'k': + multiplier = 1024; + break; + case 'w': + multiplier = 2; + break; + case 'x': + multiplier = parse_integer (p, invalid); + p = ""; + break; + default: + { + *invalid = 1; + return 0; + } + } + + if (multiplier != 0 && n * multiplier / multiplier != n) + { + *invalid = 1; + return 0; + } + + n *= multiplier; } - return n; } /* Interpret one "conv=..." option. */ |