diff options
-rw-r--r-- | coreutils/dd.c | 204 |
1 files changed, 130 insertions, 74 deletions
diff --git a/coreutils/dd.c b/coreutils/dd.c index 4a094e8..518c5fe 100644 --- a/coreutils/dd.c +++ b/coreutils/dd.c @@ -17,7 +17,7 @@ static const struct suffix_mult dd_suffixes[] = { { "b", 512 }, { "kD", 1000 }, { "k", 1024 }, - { "K", 1024 }, // compat with coreutils dd + { "K", 1024 }, /* compat with coreutils dd */ { "MD", 1000000 }, { "M", 1048576 }, { "GD", 1000000000 }, @@ -25,18 +25,21 @@ static const struct suffix_mult dd_suffixes[] = { { NULL, 0 } }; -static off_t out_full, out_part, in_full, in_part; +struct globals { + off_t out_full, out_part, in_full, in_part; +}; +#define G (*(struct globals*)&bb_common_bufsiz1) static void dd_output_status(int ATTRIBUTE_UNUSED cur_signal) { fprintf(stderr, "%"OFF_FMT"d+%"OFF_FMT"d records in\n" "%"OFF_FMT"d+%"OFF_FMT"d records out\n", - in_full, in_part, - out_full, out_part); + G.in_full, G.in_part, + G.out_full, G.out_part); } static ssize_t full_write_or_warn(int fd, const void *buf, size_t len, - const char* filename) + const char * const filename) { ssize_t n = full_write(fd, buf, len); if (n < 0) @@ -44,6 +47,19 @@ static ssize_t full_write_or_warn(int fd, const void *buf, size_t len, return n; } +static bool write_and_stats(int fd, const void *buf, size_t len, size_t obs, + const char * const filename) +{ + ssize_t n = full_write_or_warn(fd, buf, len, filename); + if (n < 0) + return 1; + if (n == obs) + G.out_full++; + else if (n > 0) + G.out_part++; + return 0; +} + #if ENABLE_LFS #define XATOU_SFX xatoull_sfx #else @@ -59,11 +75,31 @@ int dd_main(int argc, char **argv) trunc_flag = 1 << 2, twobufs_flag = 1 << 3, }; + const char * const keywords[] = { + "bs=", "count=", "seek=", "skip=", "if=", "of=", +#if ENABLE_FEATURE_DD_IBS_OBS + "ibs=", "obs=", "conv=", "notrunc", "sync", "noerror", +#endif + NULL + }; +#define OP_bs 0 + 1 +#define OP_count OP_bs + 1 +#define OP_seek OP_count + 1 +#define OP_skip OP_seek + 1 +#define OP_if OP_skip + 1 +#define OP_of OP_if + 1 +#define OP_ibs OP_of + ENABLE_FEATURE_DD_IBS_OBS +#define OP_obs OP_ibs + ENABLE_FEATURE_DD_IBS_OBS +#define OP_conv OP_obs + ENABLE_FEATURE_DD_IBS_OBS +#define OP_conv_notrunc OP_conv + ENABLE_FEATURE_DD_IBS_OBS +#define OP_conv_sync OP_conv_notrunc + ENABLE_FEATURE_DD_IBS_OBS +#define OP_conv_noerror OP_conv_sync + ENABLE_FEATURE_DD_IBS_OBS + int flags = trunc_flag; size_t oc = 0, ibs = 512, obs = 512; ssize_t n, w; off_t seek = 0, skip = 0, count = OFF_T_MAX; - int oflag, ifd, ofd; + int ifd, ofd; const char *infile = NULL, *outfile = NULL; char *ibuf, *obuf; @@ -78,63 +114,96 @@ int dd_main(int argc, char **argv) } for (n = 1; n < argc; n++) { + smalluint key_len, what; + char *key; char *arg = argv[n]; + +//XXX:FIXME: we reject plain "dd --" This would cost ~20 bytes, so.. +//if (*arg == '-' && *++arg == '-' && !*++arg) continue; + key = strstr(arg, "="); + if (key == NULL) + bb_show_usage(); + key_len = key - arg + 1; + key = xstrndup(arg, key_len); + what = index_in_str_array(keywords, key) + 1; + if (ENABLE_FEATURE_CLEAN_UP) + free(key); + if (what == 0) + bb_show_usage(); + arg += key_len; /* Must fit into positive ssize_t */ - if (ENABLE_FEATURE_DD_IBS_OBS && !strncmp("ibs=", arg, 4)) - ibs = xatoul_range_sfx(arg+4, 0, ((size_t)-1L)/2, dd_suffixes); - else if (ENABLE_FEATURE_DD_IBS_OBS && !strncmp("obs=", arg, 4)) - obs = xatoul_range_sfx(arg+4, 0, ((size_t)-1L)/2, dd_suffixes); - else if (!strncmp("bs=", arg, 3)) - ibs = obs = xatoul_range_sfx(arg+3, 0, ((size_t)-1L)/2, dd_suffixes); - /* These can be large: */ - else if (!strncmp("count=", arg, 6)) - count = XATOU_SFX(arg+6, dd_suffixes); - else if (!strncmp("seek=", arg, 5)) - seek = XATOU_SFX(arg+5, dd_suffixes); - else if (!strncmp("skip=", arg, 5)) - skip = XATOU_SFX(arg+5, dd_suffixes); - - else if (!strncmp("if=", arg, 3)) - infile = arg+3; - else if (!strncmp("of=", arg, 3)) - outfile = arg+3; - else if (ENABLE_FEATURE_DD_IBS_OBS && !strncmp("conv=", arg, 5)) { - arg += 5; - while (1) { - if (!strncmp("notrunc", arg, 7)) { - flags &= ~trunc_flag; - arg += 7; - } else if (!strncmp("sync", arg, 4)) { - flags |= sync_flag; - arg += 4; - } else if (!strncmp("noerror", arg, 7)) { - flags |= noerror; - arg += 7; - } else { - bb_error_msg_and_die(bb_msg_invalid_arg, arg, "conv"); + if (ENABLE_FEATURE_DD_IBS_OBS) { + if (what == OP_ibs) { + ibs = xatoul_range_sfx(arg, 1, ((size_t)-1L)/2, dd_suffixes); + continue; + } + if (what == OP_obs) { + obs = xatoul_range_sfx(arg, 1, ((size_t)-1L)/2, dd_suffixes); + continue; + } + if (what == OP_conv) { + while (1) { + /* find ',', replace them with nil so we can use arg for + * index_in_str_array without copying. + * We rely on arg being non-null, else strstr would fault. + */ + key = strstr(arg, ","); + if (key) + *key = '\0'; + what = index_in_str_array(keywords, arg) + 1; + if (what < OP_conv_notrunc) + bb_error_msg_and_die(bb_msg_invalid_arg, arg, "conv"); + if (what == OP_conv_notrunc) + flags &= ~trunc_flag; + if (what == OP_conv_sync) + flags |= sync_flag; + if (what == OP_conv_noerror) + flags |= noerror; + if (!key) /* no ',' left, so this was the last specifier */ + break; + arg += key - arg + 1; /* skip this keyword plus ',' */ } - if (arg[0] == '\0') break; - if (*arg++ != ',') bb_show_usage(); + continue; } - } else - bb_show_usage(); + } + if (what == OP_bs) { + ibs = obs = xatoul_range_sfx(arg, 1, ((size_t)-1L)/2, dd_suffixes); + continue; + } + /* These can be large: */ + if (what == OP_count) { + count = XATOU_SFX(arg, dd_suffixes); + continue; + } + if (what == OP_seek) { + seek = XATOU_SFX(arg, dd_suffixes); + continue; + } + if (what == skip) { + skip = XATOU_SFX(arg, dd_suffixes); + continue; + } + if (what == OP_if) { + infile = arg; + continue; + } + if (what == OP_of) + outfile = arg; } - +//XXX:FIXME for huge ibs or obs, malloc'ing them isn't the brightest idea ever ibuf = obuf = xmalloc(ibs); if (ibs != obs) { flags |= twobufs_flag; obuf = xmalloc(obs); } - if (infile != NULL) ifd = xopen(infile, O_RDONLY); else { ifd = STDIN_FILENO; infile = bb_msg_standard_input; } - if (outfile != NULL) { - oflag = O_WRONLY | O_CREAT; + int oflag = O_WRONLY | O_CREAT; if (!seek && (flags & trunc_flag)) oflag |= O_TRUNC; @@ -154,30 +223,25 @@ int dd_main(int argc, char **argv) ofd = STDOUT_FILENO; outfile = bb_msg_standard_output; } - if (skip) { if (lseek(ifd, skip * ibs, SEEK_CUR) < 0) { while (skip-- > 0) { n = safe_read(ifd, ibuf, ibs); if (n < 0) - bb_perror_msg_and_die("%s", infile); + goto die_infile; if (n == 0) break; } } } - if (seek) { if (lseek(ofd, seek * obs, SEEK_CUR) < 0) goto die_outfile; } - while (in_full + in_part != count) { - if (flags & noerror) { - /* Pre-zero the buffer when doing the noerror thing */ + while (G.in_full + G.in_part != count) { + if (flags & noerror) /* Pre-zero the buffer when for noerror */ memset(ibuf, '\0', ibs); - } - n = safe_read(ifd, ibuf, ibs); if (n == 0) break; @@ -186,12 +250,12 @@ int dd_main(int argc, char **argv) n = ibs; bb_perror_msg("%s", infile); } else - bb_perror_msg_and_die("%s", infile); + goto die_infile; } if ((size_t)n == ibs) - in_full++; + G.in_full++; else { - in_part++; + G.in_part++; if (flags & sync_flag) { memset(ibuf + n, '\0', ibs - n); n = ibs; @@ -209,40 +273,32 @@ int dd_main(int argc, char **argv) tmp += d; oc += d; if (oc == obs) { - w = full_write_or_warn(ofd, obuf, obs, outfile); - if (w < 0) goto out_status; - if (w == obs) - out_full++; - else if (w > 0) - out_part++; + if (write_and_stats(ofd, obuf, obs, obs, outfile)) + goto out_status; oc = 0; } } - } else { - w = full_write_or_warn(ofd, ibuf, n, outfile); - if (w < 0) goto out_status; - if (w == obs) - out_full++; - else if (w > 0) - out_part++; - } + } else + if (write_and_stats(ofd, ibuf, n, obs, outfile)) + goto out_status; } if (ENABLE_FEATURE_DD_IBS_OBS && oc) { w = full_write_or_warn(ofd, obuf, oc, outfile); if (w < 0) goto out_status; if (w > 0) - out_part++; + G.out_part++; } if (close(ifd) < 0) { +die_infile: bb_perror_msg_and_die("%s", infile); } if (close(ofd) < 0) { - die_outfile: +die_outfile: bb_perror_msg_and_die("%s", outfile); } - out_status: +out_status: dd_output_status(0); return EXIT_SUCCESS; |