| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | |
| |
|
| | #include <config.h> |
| |
|
| | #include <ctype.h> |
| | #include <sys/types.h> |
| | #include <signal.h> |
| |
|
| | #include "system.h" |
| | #include "alignalloc.h" |
| | #include "close-stream.h" |
| | #include "fd-reopen.h" |
| | #include "gethrxtime.h" |
| | #include "human.h" |
| | #include "ioblksize.h" |
| | #include "long-options.h" |
| | #include "quote.h" |
| | #include "xstrtol.h" |
| | #include "xtime.h" |
| |
|
| | |
| | #define PROGRAM_NAME "dd" |
| |
|
| | #define AUTHORS \ |
| | proper_name ("Paul Rubin"), \ |
| | proper_name ("David MacKenzie"), \ |
| | proper_name ("Stuart Kemp") |
| |
|
| | |
| | |
| | #ifndef SA_NOCLDSTOP |
| | # define SA_NOCLDSTOP 0 |
| | # define sigprocmask(How, Set, Oset) |
| | # define sigset_t int |
| | # if ! HAVE_SIGINTERRUPT |
| | # define siginterrupt(sig, flag) |
| | # endif |
| | #endif |
| |
|
| | |
| | #ifndef SA_RESETHAND |
| | # define SA_RESETHAND 0 |
| | #endif |
| |
|
| | #ifndef SIGINFO |
| | # define SIGINFO SIGUSR1 |
| | #endif |
| |
|
| | |
| | |
| | #ifndef O_CIO |
| | # define O_CIO 0 |
| | #endif |
| |
|
| | |
| | |
| | #undef O_NOCACHE |
| |
|
| | #define output_char(c) \ |
| | do \ |
| | { \ |
| | obuf[oc++] = (c); \ |
| | if (oc >= output_blocksize) \ |
| | write_output (); \ |
| | } \ |
| | while (0) |
| |
|
| | |
| | #define DEFAULT_BLOCKSIZE 512 |
| |
|
| | |
| | enum |
| | { |
| | C_ASCII = 01, |
| |
|
| | C_EBCDIC = 02, |
| | C_IBM = 04, |
| | C_BLOCK = 010, |
| | C_UNBLOCK = 020, |
| | C_LCASE = 040, |
| | C_UCASE = 0100, |
| | C_SWAB = 0200, |
| | C_NOERROR = 0400, |
| | C_NOTRUNC = 01000, |
| | C_SYNC = 02000, |
| |
|
| | |
| | |
| | C_TWOBUFS = 04000, |
| |
|
| | C_NOCREAT = 010000, |
| | C_EXCL = 020000, |
| | C_FDATASYNC = 040000, |
| | C_FSYNC = 0100000, |
| |
|
| | C_SPARSE = 0200000 |
| | }; |
| |
|
| | |
| | enum |
| | { |
| | STATUS_NONE = 1, |
| | STATUS_NOXFER = 2, |
| | STATUS_DEFAULT = 3, |
| | STATUS_PROGRESS = 4 |
| | }; |
| |
|
| | |
| | static char const *input_file = nullptr; |
| |
|
| | |
| | static char const *output_file = nullptr; |
| |
|
| | |
| | static idx_t page_size; |
| |
|
| | |
| | static idx_t input_blocksize = 0; |
| |
|
| | |
| | static idx_t output_blocksize = 0; |
| |
|
| | |
| | static idx_t conversion_blocksize = 0; |
| |
|
| | |
| | static intmax_t skip_records = 0; |
| |
|
| | |
| | |
| | static idx_t skip_bytes = 0; |
| |
|
| | |
| | static intmax_t seek_records = 0; |
| |
|
| | |
| | |
| | static intmax_t seek_bytes = 0; |
| |
|
| | |
| | static bool final_op_was_seek; |
| |
|
| | |
| | static intmax_t max_records = INTMAX_MAX; |
| |
|
| | |
| | static idx_t max_bytes = 0; |
| |
|
| | |
| | static int conversions_mask = 0; |
| |
|
| | |
| | static int input_flags = 0; |
| | static int output_flags = 0; |
| |
|
| | |
| | static int status_level = STATUS_DEFAULT; |
| |
|
| | |
| | static bool translation_needed = false; |
| |
|
| | |
| | static intmax_t w_partial = 0; |
| |
|
| | |
| | static intmax_t w_full = 0; |
| |
|
| | |
| | static intmax_t r_partial = 0; |
| |
|
| | |
| | static intmax_t r_full = 0; |
| |
|
| | |
| | static intmax_t w_bytes = 0; |
| |
|
| | |
| | static intmax_t reported_w_bytes = -1; |
| |
|
| | |
| | static xtime_t start_time; |
| |
|
| | |
| | static xtime_t next_time; |
| |
|
| | |
| | static int progress_len; |
| |
|
| | |
| | static bool input_seekable; |
| |
|
| | |
| | |
| | static int input_seek_errno; |
| |
|
| | |
| | static off_t input_offset; |
| |
|
| | |
| | static bool warn_partial_read; |
| |
|
| | |
| | static intmax_t r_truncate = 0; |
| |
|
| | |
| | |
| | static char newline_character = '\n'; |
| | static char space_character = ' '; |
| |
|
| | |
| | static char *ibuf; |
| | static char *obuf; |
| |
|
| | |
| | static idx_t oc = 0; |
| |
|
| | |
| | static idx_t col = 0; |
| |
|
| | |
| | static sigset_t caught_signals; |
| |
|
| | |
| | static sig_atomic_t volatile interrupt_signal; |
| |
|
| | |
| | static sig_atomic_t volatile info_signal_count; |
| |
|
| | |
| | static bool i_nocache, o_nocache; |
| |
|
| | |
| | static bool i_nocache_eof, o_nocache_eof; |
| |
|
| | |
| | static ssize_t (*iread_fnc) (int fd, char *buf, idx_t size); |
| |
|
| | |
| | #define LONGEST_SYMBOL "count_bytes" |
| |
|
| | |
| | struct symbol_value |
| | { |
| | char symbol[sizeof LONGEST_SYMBOL]; |
| | int value; |
| | }; |
| |
|
| | |
| | static struct symbol_value const conversions[] = |
| | { |
| | {"ascii", C_ASCII | C_UNBLOCK | C_TWOBUFS}, |
| | {"ebcdic", C_EBCDIC | C_BLOCK | C_TWOBUFS}, |
| | {"ibm", C_IBM | C_BLOCK | C_TWOBUFS}, |
| | {"block", C_BLOCK | C_TWOBUFS}, |
| | {"unblock", C_UNBLOCK | C_TWOBUFS}, |
| | {"lcase", C_LCASE | C_TWOBUFS}, |
| | {"ucase", C_UCASE | C_TWOBUFS}, |
| | {"sparse", C_SPARSE}, |
| | {"swab", C_SWAB | C_TWOBUFS}, |
| | {"noerror", C_NOERROR}, |
| | {"nocreat", C_NOCREAT}, |
| | {"excl", C_EXCL}, |
| | {"notrunc", C_NOTRUNC}, |
| | {"sync", C_SYNC}, |
| | {"fdatasync", C_FDATASYNC}, |
| | {"fsync", C_FSYNC}, |
| | {"", 0} |
| | }; |
| |
|
| | #define FFS_MASK(x) ((x) ^ ((x) & ((x) - 1))) |
| | enum |
| | { |
| | |
| | |
| | v = ~(0 |
| | | O_APPEND |
| | | O_BINARY |
| | | O_CIO |
| | | O_DIRECT |
| | | O_DIRECTORY |
| | | O_DSYNC |
| | | O_EXCL |
| | | O_NOATIME |
| | | O_NOCTTY |
| | | O_NOFOLLOW |
| | | O_NOLINKS |
| | | O_NONBLOCK |
| | | O_SYNC |
| | | O_TEXT |
| | ), |
| |
|
| | |
| | O_FULLBLOCK = FFS_MASK (v), |
| | v2 = v ^ O_FULLBLOCK, |
| |
|
| | O_NOCACHE = FFS_MASK (v2), |
| | v3 = v2 ^ O_NOCACHE, |
| |
|
| | O_COUNT_BYTES = FFS_MASK (v3), |
| | v4 = v3 ^ O_COUNT_BYTES, |
| |
|
| | O_SKIP_BYTES = FFS_MASK (v4), |
| | v5 = v4 ^ O_SKIP_BYTES, |
| |
|
| | O_SEEK_BYTES = FFS_MASK (v5) |
| | }; |
| |
|
| | |
| | static_assert (O_FULLBLOCK != 0); |
| | static_assert (O_NOCACHE != 0); |
| | static_assert (O_COUNT_BYTES != 0); |
| | static_assert (O_SKIP_BYTES != 0); |
| | static_assert (O_SEEK_BYTES != 0); |
| |
|
| | #define MULTIPLE_BITS_SET(i) (((i) & ((i) - 1)) != 0) |
| |
|
| | |
| | static_assert ( ! MULTIPLE_BITS_SET (O_FULLBLOCK)); |
| | static_assert ( ! MULTIPLE_BITS_SET (O_NOCACHE)); |
| | static_assert ( ! MULTIPLE_BITS_SET (O_COUNT_BYTES)); |
| | static_assert ( ! MULTIPLE_BITS_SET (O_SKIP_BYTES)); |
| | static_assert ( ! MULTIPLE_BITS_SET (O_SEEK_BYTES)); |
| |
|
| | |
| | static struct symbol_value const flags[] = |
| | { |
| | {"append", O_APPEND}, |
| | {"binary", O_BINARY}, |
| | {"cio", O_CIO}, |
| | {"direct", O_DIRECT}, |
| | {"directory", O_DIRECTORY}, |
| | {"dsync", O_DSYNC}, |
| | {"noatime", O_NOATIME}, |
| | {"nocache", O_NOCACHE}, |
| | {"noctty", O_NOCTTY}, |
| | {"nofollow", HAVE_WORKING_O_NOFOLLOW ? O_NOFOLLOW : 0}, |
| | {"nolinks", O_NOLINKS}, |
| | {"nonblock", O_NONBLOCK}, |
| | {"sync", O_SYNC}, |
| | {"text", O_TEXT}, |
| | {"fullblock", O_FULLBLOCK}, |
| | {"count_bytes", O_COUNT_BYTES}, |
| | {"skip_bytes", O_SKIP_BYTES}, |
| | {"seek_bytes", O_SEEK_BYTES}, |
| | {"", 0} |
| | }; |
| |
|
| | |
| | static struct symbol_value const statuses[] = |
| | { |
| | {"none", STATUS_NONE}, |
| | {"noxfer", STATUS_NOXFER}, |
| | {"progress", STATUS_PROGRESS}, |
| | {"", 0} |
| | }; |
| |
|
| | |
| | static unsigned char trans_table[256]; |
| |
|
| | |
| | |
| | |
| | |
| |
|
| | static char const ascii_to_ebcdic[] = |
| | { |
| | '\000', '\001', '\002', '\003', '\067', '\055', '\056', '\057', |
| | '\026', '\005', '\045', '\013', '\014', '\015', '\016', '\017', |
| | '\020', '\021', '\022', '\023', '\074', '\075', '\062', '\046', |
| | '\030', '\031', '\077', '\047', '\034', '\035', '\036', '\037', |
| | '\100', '\132', '\177', '\173', '\133', '\154', '\120', '\175', |
| | '\115', '\135', '\134', '\116', '\153', '\140', '\113', '\141', |
| | '\360', '\361', '\362', '\363', '\364', '\365', '\366', '\367', |
| | '\370', '\371', '\172', '\136', '\114', '\176', '\156', '\157', |
| | '\174', '\301', '\302', '\303', '\304', '\305', '\306', '\307', |
| | '\310', '\311', '\321', '\322', '\323', '\324', '\325', '\326', |
| | '\327', '\330', '\331', '\342', '\343', '\344', '\345', '\346', |
| | '\347', '\350', '\351', '\255', '\340', '\275', '\232', '\155', |
| | '\171', '\201', '\202', '\203', '\204', '\205', '\206', '\207', |
| | '\210', '\211', '\221', '\222', '\223', '\224', '\225', '\226', |
| | '\227', '\230', '\231', '\242', '\243', '\244', '\245', '\246', |
| | '\247', '\250', '\251', '\300', '\117', '\320', '\137', '\007', |
| | '\040', '\041', '\042', '\043', '\044', '\025', '\006', '\027', |
| | '\050', '\051', '\052', '\053', '\054', '\011', '\012', '\033', |
| | '\060', '\061', '\032', '\063', '\064', '\065', '\066', '\010', |
| | '\070', '\071', '\072', '\073', '\004', '\024', '\076', '\341', |
| | '\101', '\102', '\103', '\104', '\105', '\106', '\107', '\110', |
| | '\111', '\121', '\122', '\123', '\124', '\125', '\126', '\127', |
| | '\130', '\131', '\142', '\143', '\144', '\145', '\146', '\147', |
| | '\150', '\151', '\160', '\161', '\162', '\163', '\164', '\165', |
| | '\166', '\167', '\170', '\200', '\212', '\213', '\214', '\215', |
| | '\216', '\217', '\220', '\152', '\233', '\234', '\235', '\236', |
| | '\237', '\240', '\252', '\253', '\254', '\112', '\256', '\257', |
| | '\260', '\261', '\262', '\263', '\264', '\265', '\266', '\267', |
| | '\270', '\271', '\272', '\273', '\274', '\241', '\276', '\277', |
| | '\312', '\313', '\314', '\315', '\316', '\317', '\332', '\333', |
| | '\334', '\335', '\336', '\337', '\352', '\353', '\354', '\355', |
| | '\356', '\357', '\372', '\373', '\374', '\375', '\376', '\377' |
| | }; |
| |
|
| | static char const ascii_to_ibm[] = |
| | { |
| | '\000', '\001', '\002', '\003', '\067', '\055', '\056', '\057', |
| | '\026', '\005', '\045', '\013', '\014', '\015', '\016', '\017', |
| | '\020', '\021', '\022', '\023', '\074', '\075', '\062', '\046', |
| | '\030', '\031', '\077', '\047', '\034', '\035', '\036', '\037', |
| | '\100', '\132', '\177', '\173', '\133', '\154', '\120', '\175', |
| | '\115', '\135', '\134', '\116', '\153', '\140', '\113', '\141', |
| | '\360', '\361', '\362', '\363', '\364', '\365', '\366', '\367', |
| | '\370', '\371', '\172', '\136', '\114', '\176', '\156', '\157', |
| | '\174', '\301', '\302', '\303', '\304', '\305', '\306', '\307', |
| | '\310', '\311', '\321', '\322', '\323', '\324', '\325', '\326', |
| | '\327', '\330', '\331', '\342', '\343', '\344', '\345', '\346', |
| | '\347', '\350', '\351', '\255', '\340', '\275', '\137', '\155', |
| | '\171', '\201', '\202', '\203', '\204', '\205', '\206', '\207', |
| | '\210', '\211', '\221', '\222', '\223', '\224', '\225', '\226', |
| | '\227', '\230', '\231', '\242', '\243', '\244', '\245', '\246', |
| | '\247', '\250', '\251', '\300', '\117', '\320', '\241', '\007', |
| | '\040', '\041', '\042', '\043', '\044', '\025', '\006', '\027', |
| | '\050', '\051', '\052', '\053', '\054', '\011', '\012', '\033', |
| | '\060', '\061', '\032', '\063', '\064', '\065', '\066', '\010', |
| | '\070', '\071', '\072', '\073', '\004', '\024', '\076', '\341', |
| | '\101', '\102', '\103', '\104', '\105', '\106', '\107', '\110', |
| | '\111', '\121', '\122', '\123', '\124', '\125', '\126', '\127', |
| | '\130', '\131', '\142', '\143', '\144', '\145', '\146', '\147', |
| | '\150', '\151', '\160', '\161', '\162', '\163', '\164', '\165', |
| | '\166', '\167', '\170', '\200', '\212', '\213', '\214', '\215', |
| | '\216', '\217', '\220', '\232', '\233', '\234', '\235', '\236', |
| | '\237', '\240', '\252', '\253', '\254', '\255', '\256', '\257', |
| | '\260', '\261', '\262', '\263', '\264', '\265', '\266', '\267', |
| | '\270', '\271', '\272', '\273', '\274', '\275', '\276', '\277', |
| | '\312', '\313', '\314', '\315', '\316', '\317', '\332', '\333', |
| | '\334', '\335', '\336', '\337', '\352', '\353', '\354', '\355', |
| | '\356', '\357', '\372', '\373', '\374', '\375', '\376', '\377' |
| | }; |
| |
|
| | static char const ebcdic_to_ascii[] = |
| | { |
| | '\000', '\001', '\002', '\003', '\234', '\011', '\206', '\177', |
| | '\227', '\215', '\216', '\013', '\014', '\015', '\016', '\017', |
| | '\020', '\021', '\022', '\023', '\235', '\205', '\010', '\207', |
| | '\030', '\031', '\222', '\217', '\034', '\035', '\036', '\037', |
| | '\200', '\201', '\202', '\203', '\204', '\012', '\027', '\033', |
| | '\210', '\211', '\212', '\213', '\214', '\005', '\006', '\007', |
| | '\220', '\221', '\026', '\223', '\224', '\225', '\226', '\004', |
| | '\230', '\231', '\232', '\233', '\024', '\025', '\236', '\032', |
| | '\040', '\240', '\241', '\242', '\243', '\244', '\245', '\246', |
| | '\247', '\250', '\325', '\056', '\074', '\050', '\053', '\174', |
| | '\046', '\251', '\252', '\253', '\254', '\255', '\256', '\257', |
| | '\260', '\261', '\041', '\044', '\052', '\051', '\073', '\176', |
| | '\055', '\057', '\262', '\263', '\264', '\265', '\266', '\267', |
| | '\270', '\271', '\313', '\054', '\045', '\137', '\076', '\077', |
| | '\272', '\273', '\274', '\275', '\276', '\277', '\300', '\301', |
| | '\302', '\140', '\072', '\043', '\100', '\047', '\075', '\042', |
| | '\303', '\141', '\142', '\143', '\144', '\145', '\146', '\147', |
| | '\150', '\151', '\304', '\305', '\306', '\307', '\310', '\311', |
| | '\312', '\152', '\153', '\154', '\155', '\156', '\157', '\160', |
| | '\161', '\162', '\136', '\314', '\315', '\316', '\317', '\320', |
| | '\321', '\345', '\163', '\164', '\165', '\166', '\167', '\170', |
| | '\171', '\172', '\322', '\323', '\324', '\133', '\326', '\327', |
| | '\330', '\331', '\332', '\333', '\334', '\335', '\336', '\337', |
| | '\340', '\341', '\342', '\343', '\344', '\135', '\346', '\347', |
| | '\173', '\101', '\102', '\103', '\104', '\105', '\106', '\107', |
| | '\110', '\111', '\350', '\351', '\352', '\353', '\354', '\355', |
| | '\175', '\112', '\113', '\114', '\115', '\116', '\117', '\120', |
| | '\121', '\122', '\356', '\357', '\360', '\361', '\362', '\363', |
| | '\134', '\237', '\123', '\124', '\125', '\126', '\127', '\130', |
| | '\131', '\132', '\364', '\365', '\366', '\367', '\370', '\371', |
| | '\060', '\061', '\062', '\063', '\064', '\065', '\066', '\067', |
| | '\070', '\071', '\372', '\373', '\374', '\375', '\376', '\377' |
| | }; |
| |
|
| | |
| | static bool close_stdout_required = true; |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | static void |
| | maybe_close_stdout (void) |
| | { |
| | if (close_stdout_required) |
| | close_stdout (); |
| | else if (close_stream (stderr) != 0) |
| | _exit (EXIT_FAILURE); |
| | } |
| |
|
| | |
| | |
| |
|
| | ATTRIBUTE_FORMAT ((__printf__, 2, 3)) |
| | static void |
| | diagnose (int errnum, char const *fmt, ...) |
| | { |
| | if (0 < progress_len) |
| | { |
| | fputc ('\n', stderr); |
| | progress_len = 0; |
| | } |
| |
|
| | va_list ap; |
| | va_start (ap, fmt); |
| | verror (0, errnum, fmt, ap); |
| | va_end (ap); |
| | } |
| |
|
| | void |
| | usage (int status) |
| | { |
| | if (status != EXIT_SUCCESS) |
| | emit_try_help (); |
| | else |
| | { |
| | printf (_("\ |
| | Usage: %s [OPERAND]...\n\ |
| | or: %s OPTION\n\ |
| | "), |
| | program_name, program_name); |
| | fputs (_("\ |
| | Copy a file, converting and formatting according to the operands.\n\ |
| | \n\ |
| | bs=BYTES read and write up to BYTES bytes at a time (default: 512);\n\ |
| | overrides ibs and obs\n\ |
| | cbs=BYTES convert BYTES bytes at a time\n\ |
| | conv=CONVS convert the file as per the comma separated symbol list\n\ |
| | count=N copy only N input blocks\n\ |
| | ibs=BYTES read up to BYTES bytes at a time (default: 512)\n\ |
| | "), stdout); |
| | fputs (_("\ |
| | if=FILE read from FILE instead of standard input\n\ |
| | iflag=FLAGS read as per the comma separated symbol list\n\ |
| | obs=BYTES write BYTES bytes at a time (default: 512)\n\ |
| | of=FILE write to FILE instead of standard output\n\ |
| | oflag=FLAGS write as per the comma separated symbol list\n\ |
| | seek=N (or oseek=N) skip N obs-sized output blocks\n\ |
| | skip=N (or iseek=N) skip N ibs-sized input blocks\n\ |
| | status=LEVEL The LEVEL of information to print to standard error;\n\ |
| | 'none' suppresses everything but error messages,\n\ |
| | 'noxfer' suppresses the final transfer statistics,\n\ |
| | 'progress' shows periodic transfer statistics\n\ |
| | "), stdout); |
| | fputs (_("\ |
| | \n\ |
| | N and BYTES may be followed by the following multiplicative suffixes:\n\ |
| | c=1, w=2, b=512, kB=1000, K=1024, MB=1000*1000, M=1024*1024, xM=M,\n\ |
| | GB=1000*1000*1000, G=1024*1024*1024, and so on for T, P, E, Z, Y, R, Q.\n\ |
| | Binary prefixes can be used, too: KiB=K, MiB=M, and so on.\n\ |
| | If N ends in 'B', it counts bytes not blocks.\n\ |
| | \n\ |
| | Each CONV symbol may be:\n\ |
| | \n\ |
| | "), stdout); |
| | fputs (_("\ |
| | ascii from EBCDIC to ASCII\n\ |
| | ebcdic from ASCII to EBCDIC\n\ |
| | ibm from ASCII to alternate EBCDIC\n\ |
| | block pad newline-terminated records with spaces to cbs-size\n\ |
| | unblock replace trailing spaces in cbs-size records with newline\n\ |
| | lcase change upper case to lower case\n\ |
| | ucase change lower case to upper case\n\ |
| | sparse try to seek rather than write all-NUL output blocks\n\ |
| | swab swap every pair of input bytes\n\ |
| | sync pad every input block with NULs to ibs-size; when used\n\ |
| | with block or unblock, pad with spaces rather than NULs\n\ |
| | "), stdout); |
| | fputs (_("\ |
| | excl fail if the output file already exists\n\ |
| | nocreat do not create the output file\n\ |
| | notrunc do not truncate the output file\n\ |
| | noerror continue after read errors\n\ |
| | fdatasync physically write output file data before finishing\n\ |
| | fsync likewise, but also write metadata\n\ |
| | "), stdout); |
| | fputs (_("\ |
| | \n\ |
| | Each FLAG symbol may be:\n\ |
| | \n\ |
| | append append mode (makes sense only for output; conv=notrunc suggested)\n\ |
| | "), stdout); |
| | if (O_CIO) |
| | fputs (_(" cio use concurrent I/O for data\n"), stdout); |
| | if (O_DIRECT) |
| | fputs (_(" direct use direct I/O for data\n"), stdout); |
| | fputs (_(" directory fail unless a directory\n"), stdout); |
| | if (O_DSYNC) |
| | fputs (_(" dsync use synchronized I/O for data\n"), stdout); |
| | if (O_SYNC) |
| | fputs (_(" sync likewise, but also for metadata\n"), stdout); |
| | fputs (_(" fullblock accumulate full blocks of input (iflag only)\n"), |
| | stdout); |
| | if (O_NONBLOCK) |
| | fputs (_(" nonblock use non-blocking I/O\n"), stdout); |
| | if (O_NOATIME) |
| | fputs (_(" noatime do not update access time\n"), stdout); |
| | #if HAVE_POSIX_FADVISE |
| | if (O_NOCACHE) |
| | fputs (_(" nocache Request to drop cache. See also oflag=sync\n"), |
| | stdout); |
| | #endif |
| | if (O_NOCTTY) |
| | fputs (_(" noctty do not assign controlling terminal from file\n"), |
| | stdout); |
| | if (HAVE_WORKING_O_NOFOLLOW) |
| | fputs (_(" nofollow do not follow symlinks\n"), stdout); |
| | if (O_NOLINKS) |
| | fputs (_(" nolinks fail if multiply-linked\n"), stdout); |
| | if (O_BINARY) |
| | fputs (_(" binary use binary I/O for data\n"), stdout); |
| | if (O_TEXT) |
| | fputs (_(" text use text I/O for data\n"), stdout); |
| |
|
| | { |
| | printf (_("\ |
| | \n\ |
| | Sending a %s signal to a running 'dd' process makes it\n\ |
| | print I/O statistics to standard error and then resume copying.\n\ |
| | \n\ |
| | Options are:\n\ |
| | \n\ |
| | "), SIGINFO == SIGUSR1 ? "USR1" : "INFO"); |
| | } |
| |
|
| | fputs (HELP_OPTION_DESCRIPTION, stdout); |
| | fputs (VERSION_OPTION_DESCRIPTION, stdout); |
| | emit_ancillary_info (PROGRAM_NAME); |
| | } |
| | exit (status); |
| | } |
| |
|
| | |
| |
|
| | enum { human_opts = (human_autoscale | human_round_to_nearest |
| | | human_space_before_unit | human_SI | human_B) }; |
| |
|
| | |
| |
|
| | static void |
| | alloc_ibuf (void) |
| | { |
| | if (ibuf) |
| | return; |
| |
|
| | bool extra_byte_for_swab = !!(conversions_mask & C_SWAB); |
| | ibuf = alignalloc (page_size, input_blocksize + extra_byte_for_swab); |
| | if (!ibuf) |
| | { |
| | char hbuf[LONGEST_HUMAN_READABLE + 1]; |
| | error (EXIT_FAILURE, 0, |
| | _("memory exhausted by input buffer of size %td bytes (%s)"), |
| | input_blocksize, |
| | human_readable (input_blocksize, hbuf, |
| | human_opts | human_base_1024, 1, 1)); |
| | } |
| | } |
| |
|
| | |
| |
|
| | static void |
| | alloc_obuf (void) |
| | { |
| | if (obuf) |
| | return; |
| |
|
| | if (conversions_mask & C_TWOBUFS) |
| | { |
| | obuf = alignalloc (page_size, output_blocksize); |
| | if (!obuf) |
| | { |
| | char hbuf[LONGEST_HUMAN_READABLE + 1]; |
| | error (EXIT_FAILURE, 0, |
| | _("memory exhausted by output buffer of size %td" |
| | " bytes (%s)"), |
| | output_blocksize, |
| | human_readable (output_blocksize, hbuf, |
| | human_opts | human_base_1024, 1, 1)); |
| | } |
| | } |
| | else |
| | { |
| | alloc_ibuf (); |
| | obuf = ibuf; |
| | } |
| | } |
| |
|
| | static void |
| | translate_charset (char const *new_trans) |
| | { |
| | for (int i = 0; i < 256; i++) |
| | trans_table[i] = new_trans[trans_table[i]]; |
| | translation_needed = true; |
| | } |
| |
|
| | |
| |
|
| | static inline bool |
| | multiple_bits_set (int i) |
| | { |
| | return MULTIPLE_BITS_SET (i); |
| | } |
| |
|
| | static bool |
| | abbreviation_lacks_prefix (char const *message) |
| | { |
| | return message[strlen (message) - 2] == ' '; |
| | } |
| |
|
| | |
| |
|
| | static void |
| | print_xfer_stats (xtime_t progress_time) |
| | { |
| | xtime_t now = progress_time ? progress_time : gethrxtime (); |
| | static char const slash_s[] = "/s"; |
| | char hbuf[3][LONGEST_HUMAN_READABLE + sizeof slash_s]; |
| | double delta_s; |
| | char const *bytes_per_second; |
| | char const *si = human_readable (w_bytes, hbuf[0], human_opts, 1, 1); |
| | char const *iec = human_readable (w_bytes, hbuf[1], |
| | human_opts | human_base_1024, 1, 1); |
| |
|
| | |
| | |
| | char *bpsbuf = hbuf[2]; |
| | int bpsbufsize = sizeof hbuf[2]; |
| | if (start_time < now) |
| | { |
| | double XTIME_PRECISIONe0 = XTIME_PRECISION; |
| | xtime_t delta_xtime = now - start_time; |
| | delta_s = delta_xtime / XTIME_PRECISIONe0; |
| | bytes_per_second = human_readable (w_bytes, bpsbuf, human_opts, |
| | XTIME_PRECISION, delta_xtime); |
| | strcat (bytes_per_second - bpsbuf + bpsbuf, slash_s); |
| | } |
| | else |
| | { |
| | delta_s = 0; |
| | snprintf (bpsbuf, bpsbufsize, "%s B/s", _("Infinity")); |
| | bytes_per_second = bpsbuf; |
| | } |
| |
|
| | if (progress_time) |
| | fputc ('\r', stderr); |
| |
|
| | |
| | |
| | |
| | |
| | |
| | char delta_s_buf[24]; |
| | snprintf (delta_s_buf, sizeof delta_s_buf, |
| | progress_time ? "%.0f s" : "%g s", delta_s); |
| |
|
| | int stats_len |
| | = (abbreviation_lacks_prefix (si) |
| | ? fprintf (stderr, |
| | ngettext ("%jd byte copied, %s, %s", |
| | "%jd bytes copied, %s, %s", |
| | select_plural (w_bytes)), |
| | w_bytes, delta_s_buf, bytes_per_second) |
| | : abbreviation_lacks_prefix (iec) |
| | ? fprintf (stderr, |
| | _("%jd bytes (%s) copied, %s, %s"), |
| | w_bytes, si, delta_s_buf, bytes_per_second) |
| | : fprintf (stderr, |
| | _("%jd bytes (%s, %s) copied, %s, %s"), |
| | w_bytes, si, iec, delta_s_buf, bytes_per_second)); |
| |
|
| | if (progress_time) |
| | { |
| | |
| | |
| | |
| | |
| | |
| | |
| | if (0 <= stats_len && stats_len < progress_len) |
| | fprintf (stderr, "%*s", progress_len - stats_len, ""); |
| | progress_len = stats_len; |
| | } |
| | else |
| | fputc ('\n', stderr); |
| |
|
| | reported_w_bytes = w_bytes; |
| | } |
| |
|
| | static void |
| | print_stats (void) |
| | { |
| | if (status_level == STATUS_NONE) |
| | return; |
| |
|
| | if (0 < progress_len) |
| | { |
| | fputc ('\n', stderr); |
| | progress_len = 0; |
| | } |
| |
|
| | fprintf (stderr, |
| | _("%jd+%jd records in\n" |
| | "%jd+%jd records out\n"), |
| | r_full, r_partial, w_full, w_partial); |
| |
|
| | if (r_truncate != 0) |
| | fprintf (stderr, |
| | ngettext ("%jd truncated record\n", |
| | "%jd truncated records\n", |
| | select_plural (r_truncate)), |
| | r_truncate); |
| |
|
| | if (status_level == STATUS_NOXFER) |
| | return; |
| |
|
| | print_xfer_stats (0); |
| | } |
| |
|
| | |
| |
|
| | static void |
| | interrupt_handler (int sig) |
| | { |
| | if (! SA_RESETHAND) |
| | signal (sig, SIG_DFL); |
| | interrupt_signal = sig; |
| | } |
| |
|
| | |
| |
|
| | static void |
| | siginfo_handler (int sig) |
| | { |
| | if (! SA_NOCLDSTOP) |
| | signal (sig, siginfo_handler); |
| | info_signal_count++; |
| | } |
| |
|
| | |
| |
|
| | static void |
| | install_signal_handlers (void) |
| | { |
| | bool catch_siginfo = ! (SIGINFO == SIGUSR1 && getenv ("POSIXLY_CORRECT")); |
| |
|
| | #if SA_NOCLDSTOP |
| |
|
| | struct sigaction act; |
| | sigemptyset (&caught_signals); |
| | if (catch_siginfo) |
| | sigaddset (&caught_signals, SIGINFO); |
| | sigaction (SIGINT, nullptr, &act); |
| | if (act.sa_handler != SIG_IGN) |
| | sigaddset (&caught_signals, SIGINT); |
| | act.sa_mask = caught_signals; |
| |
|
| | if (sigismember (&caught_signals, SIGINFO)) |
| | { |
| | act.sa_handler = siginfo_handler; |
| | |
| | |
| | |
| | act.sa_flags = 0; |
| | sigaction (SIGINFO, &act, nullptr); |
| | } |
| |
|
| | if (sigismember (&caught_signals, SIGINT)) |
| | { |
| | act.sa_handler = interrupt_handler; |
| | act.sa_flags = SA_NODEFER | SA_RESETHAND; |
| | sigaction (SIGINT, &act, nullptr); |
| | } |
| |
|
| | #else |
| |
|
| | if (catch_siginfo) |
| | { |
| | signal (SIGINFO, siginfo_handler); |
| | siginterrupt (SIGINFO, 1); |
| | } |
| | if (signal (SIGINT, SIG_IGN) != SIG_IGN) |
| | { |
| | signal (SIGINT, interrupt_handler); |
| | siginterrupt (SIGINT, 1); |
| | } |
| | #endif |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | static int |
| | iclose (int fd) |
| | { |
| | if (close (fd) != 0) |
| | do |
| | if (errno != EINTR) |
| | return -1; |
| | while (close (fd) != 0 && errno != EBADF); |
| |
|
| | return 0; |
| | } |
| |
|
| | static int synchronize_output (void); |
| |
|
| | static void |
| | cleanup (void) |
| | { |
| | if (!interrupt_signal) |
| | { |
| | int sync_status = synchronize_output (); |
| | if (sync_status) |
| | exit (sync_status); |
| | } |
| |
|
| | if (iclose (STDIN_FILENO) != 0) |
| | error (EXIT_FAILURE, errno, _("closing input file %s"), |
| | quoteaf (input_file)); |
| |
|
| | |
| | |
| | |
| | if (iclose (STDOUT_FILENO) != 0) |
| | error (EXIT_FAILURE, errno, |
| | _("closing output file %s"), quoteaf (output_file)); |
| | } |
| |
|
| | |
| | |
| | |
| |
|
| | static void |
| | process_signals (void) |
| | { |
| | while (interrupt_signal || info_signal_count) |
| | { |
| | int interrupt; |
| | int infos; |
| | sigset_t oldset; |
| |
|
| | sigprocmask (SIG_BLOCK, &caught_signals, &oldset); |
| |
|
| | |
| | |
| | interrupt = interrupt_signal; |
| | infos = info_signal_count; |
| |
|
| | if (infos) |
| | info_signal_count = infos - 1; |
| |
|
| | sigprocmask (SIG_SETMASK, &oldset, nullptr); |
| |
|
| | if (interrupt) |
| | cleanup (); |
| | print_stats (); |
| | if (interrupt) |
| | raise (interrupt); |
| | } |
| | } |
| |
|
| | static void |
| | finish_up (void) |
| | { |
| | |
| | process_signals (); |
| | cleanup (); |
| | print_stats (); |
| | } |
| |
|
| | static void |
| | quit (int code) |
| | { |
| | finish_up (); |
| | exit (code); |
| | } |
| |
|
| | |
| | |
| | |
| | |
| |
|
| | static off_t |
| | cache_round (int fd, off_t len) |
| | { |
| | static off_t i_pending, o_pending; |
| | off_t *pending = (fd == STDIN_FILENO ? &i_pending : &o_pending); |
| |
|
| | if (len) |
| | { |
| | intmax_t c_pending; |
| | if (ckd_add (&c_pending, *pending, len)) |
| | c_pending = INTMAX_MAX; |
| | *pending = c_pending % IO_BUFSIZE; |
| | if (c_pending > *pending) |
| | len = c_pending - *pending; |
| | else |
| | len = 0; |
| | } |
| | else |
| | len = *pending; |
| |
|
| | return len; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| |
|
| | static bool |
| | invalidate_cache (int fd, off_t len) |
| | { |
| | int adv_ret = -1; |
| | off_t offset; |
| | bool nocache_eof = (fd == STDIN_FILENO ? i_nocache_eof : o_nocache_eof); |
| |
|
| | |
| | off_t clen = cache_round (fd, len); |
| | if (len && !clen) |
| | return true; |
| | else if (! len && ! clen && ! nocache_eof) |
| | return true; |
| | off_t pending = len ? cache_round (fd, 0) : 0; |
| |
|
| | if (fd == STDIN_FILENO) |
| | { |
| | if (input_seekable) |
| | offset = input_offset; |
| | else |
| | { |
| | offset = -1; |
| | errno = ESPIPE; |
| | } |
| | } |
| | else |
| | { |
| | static off_t output_offset = -2; |
| |
|
| | if (output_offset != -1) |
| | { |
| | if (output_offset < 0) |
| | output_offset = lseek (fd, 0, SEEK_CUR); |
| | else if (len) |
| | output_offset += clen + pending; |
| | } |
| |
|
| | offset = output_offset; |
| | } |
| |
|
| | if (0 <= offset) |
| | { |
| | if (! len && clen && nocache_eof) |
| | { |
| | pending = clen; |
| | clen = 0; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | #if HAVE_POSIX_FADVISE |
| | offset = offset - clen - pending; |
| | |
| | if (clen == 0) |
| | offset -= offset % page_size; |
| | adv_ret = posix_fadvise (fd, offset, clen, POSIX_FADV_DONTNEED); |
| | errno = adv_ret; |
| | #else |
| | errno = ENOTSUP; |
| | #endif |
| | } |
| |
|
| | return adv_ret == 0; |
| | } |
| |
|
| | |
| | |
| | |
| |
|
| | static ssize_t |
| | iread (int fd, char *buf, idx_t size) |
| | { |
| | ssize_t nread; |
| | static ssize_t prev_nread; |
| |
|
| | do |
| | { |
| | process_signals (); |
| | nread = read (fd, buf, size); |
| | |
| | |
| | if (nread == -1 && errno == EINVAL |
| | && 0 < prev_nread && prev_nread < size |
| | && (input_flags & O_DIRECT)) |
| | { |
| | errno = 0; |
| | nread = 0; |
| | } |
| | } |
| | while (nread < 0 && errno == EINTR); |
| |
|
| | |
| | if (0 < nread && nread < size) |
| | process_signals (); |
| |
|
| | if (0 < nread && warn_partial_read) |
| | { |
| | if (0 < prev_nread && prev_nread < size) |
| | { |
| | idx_t prev = prev_nread; |
| | if (status_level != STATUS_NONE) |
| | diagnose (0, ngettext (("warning: partial read (%td byte); " |
| | "suggest iflag=fullblock"), |
| | ("warning: partial read (%td bytes); " |
| | "suggest iflag=fullblock"), |
| | select_plural (prev)), |
| | prev); |
| | warn_partial_read = false; |
| | } |
| | } |
| |
|
| | prev_nread = nread; |
| | return nread; |
| | } |
| |
|
| | |
| | static ssize_t |
| | iread_fullblock (int fd, char *buf, idx_t size) |
| | { |
| | ssize_t nread = 0; |
| |
|
| | while (0 < size) |
| | { |
| | ssize_t ncurr = iread (fd, buf, size); |
| | if (ncurr < 0) |
| | return ncurr; |
| | if (ncurr == 0) |
| | break; |
| | nread += ncurr; |
| | buf += ncurr; |
| | size -= ncurr; |
| | } |
| |
|
| | return nread; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| |
|
| | static idx_t |
| | iwrite (int fd, char const *buf, idx_t size) |
| | { |
| | idx_t total_written = 0; |
| |
|
| | if ((output_flags & O_DIRECT) && size < output_blocksize) |
| | { |
| | int old_flags = fcntl (STDOUT_FILENO, F_GETFL); |
| | if (fcntl (STDOUT_FILENO, F_SETFL, old_flags & ~O_DIRECT) != 0 |
| | && status_level != STATUS_NONE) |
| | diagnose (errno, _("failed to turn off O_DIRECT: %s"), |
| | quotef (output_file)); |
| |
|
| | |
| | |
| |
|
| | |
| | |
| | o_nocache_eof = true; |
| | invalidate_cache (STDOUT_FILENO, 0); |
| |
|
| | |
| | |
| | conversions_mask |= C_FSYNC; |
| |
|
| | |
| | |
| | } |
| |
|
| | while (total_written < size) |
| | { |
| | ssize_t nwritten = 0; |
| | process_signals (); |
| |
|
| | |
| | final_op_was_seek = false; |
| | if ((conversions_mask & C_SPARSE) && is_nul (buf, size)) |
| | { |
| | if (lseek (fd, size, SEEK_CUR) < 0) |
| | { |
| | conversions_mask &= ~C_SPARSE; |
| | |
| | } |
| | else |
| | { |
| | final_op_was_seek = true; |
| | nwritten = size; |
| | } |
| | } |
| |
|
| | if (!nwritten) |
| | nwritten = write (fd, buf + total_written, size - total_written); |
| |
|
| | if (nwritten < 0) |
| | { |
| | if (errno != EINTR) |
| | break; |
| | } |
| | else if (nwritten == 0) |
| | { |
| | |
| | |
| | |
| | errno = ENOSPC; |
| | break; |
| | } |
| | else |
| | total_written += nwritten; |
| | } |
| |
|
| | if (o_nocache && total_written) |
| | invalidate_cache (fd, total_written); |
| |
|
| | return total_written; |
| | } |
| |
|
| | |
| |
|
| | static void |
| | write_output (void) |
| | { |
| | idx_t nwritten = iwrite (STDOUT_FILENO, obuf, output_blocksize); |
| | w_bytes += nwritten; |
| | if (nwritten != output_blocksize) |
| | { |
| | diagnose (errno, _("writing to %s"), quoteaf (output_file)); |
| | if (nwritten != 0) |
| | w_partial++; |
| | quit (EXIT_FAILURE); |
| | } |
| | else |
| | w_full++; |
| | oc = 0; |
| | } |
| |
|
| | |
| |
|
| | static int |
| | ifdatasync (int fd) |
| | { |
| | int ret; |
| |
|
| | do |
| | { |
| | process_signals (); |
| | ret = fdatasync (fd); |
| | } |
| | while (ret < 0 && errno == EINTR); |
| |
|
| | return ret; |
| | } |
| |
|
| | |
| |
|
| | static int |
| | ifd_reopen (int desired_fd, char const *file, int flag, mode_t mode) |
| | { |
| | int ret; |
| |
|
| | do |
| | { |
| | process_signals (); |
| | ret = fd_reopen (desired_fd, file, flag, mode); |
| | } |
| | while (ret < 0 && errno == EINTR); |
| |
|
| | return ret; |
| | } |
| |
|
| | |
| |
|
| | static int |
| | ifstat (int fd, struct stat *st) |
| | { |
| | int ret; |
| |
|
| | do |
| | { |
| | process_signals (); |
| | ret = fstat (fd, st); |
| | } |
| | while (ret < 0 && errno == EINTR); |
| |
|
| | return ret; |
| | } |
| |
|
| | |
| |
|
| | static int |
| | ifsync (int fd) |
| | { |
| | int ret; |
| |
|
| | do |
| | { |
| | process_signals (); |
| | ret = fsync (fd); |
| | } |
| | while (ret < 0 && errno == EINTR); |
| |
|
| | return ret; |
| | } |
| |
|
| | |
| |
|
| | static int |
| | iftruncate (int fd, off_t length) |
| | { |
| | int ret; |
| |
|
| | do |
| | { |
| | process_signals (); |
| | ret = ftruncate (fd, length); |
| | } |
| | while (ret < 0 && errno == EINTR); |
| |
|
| | return ret; |
| | } |
| |
|
| | |
| |
|
| | ATTRIBUTE_PURE |
| | static bool |
| | operand_matches (char const *str, char const *pattern, char delim) |
| | { |
| | while (*pattern) |
| | if (*str++ != *pattern++) |
| | return false; |
| | return !*str || *str == delim; |
| | } |
| |
|
| | |
| | |
| | |
| |
|
| | static int |
| | parse_symbols (char const *str, struct symbol_value const *table, |
| | bool exclusive, char const *error_msgid) |
| | { |
| | int value = 0; |
| |
|
| | while (true) |
| | { |
| | char const *strcomma = strchr (str, ','); |
| | struct symbol_value const *entry; |
| |
|
| | for (entry = table; |
| | ! (operand_matches (str, entry->symbol, ',') && entry->value); |
| | entry++) |
| | { |
| | if (! entry->symbol[0]) |
| | { |
| | idx_t slen = strcomma ? strcomma - str : strlen (str); |
| | diagnose (0, "%s: %s", _(error_msgid), |
| | quotearg_n_style_mem (0, locale_quoting_style, |
| | str, slen)); |
| | usage (EXIT_FAILURE); |
| | } |
| | } |
| |
|
| | if (exclusive) |
| | value = entry->value; |
| | else |
| | value |= entry->value; |
| | if (!strcomma) |
| | break; |
| | str = strcomma + 1; |
| | } |
| |
|
| | return value; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| |
|
| | static intmax_t |
| | parse_integer (char const *str, strtol_error *invalid) |
| | { |
| | |
| | |
| | |
| | |
| | int indeterminate = 0; |
| | uintmax_t n = indeterminate; |
| | char *suffix; |
| | static char const suffixes[] = "bcEGkKMPQRTwYZ0"; |
| | strtol_error e = xstrtoumax (str, &suffix, 10, &n, suffixes); |
| | intmax_t result; |
| |
|
| | if ((e & ~LONGINT_OVERFLOW) == LONGINT_INVALID_SUFFIX_CHAR |
| | && *suffix == 'B' && str < suffix && suffix[-1] != 'B') |
| | { |
| | suffix++; |
| | if (!*suffix) |
| | e &= ~LONGINT_INVALID_SUFFIX_CHAR; |
| | } |
| |
|
| | if ((e & ~LONGINT_OVERFLOW) == LONGINT_INVALID_SUFFIX_CHAR |
| | && *suffix == 'x') |
| | { |
| | strtol_error f = LONGINT_OK; |
| | intmax_t o = parse_integer (suffix + 1, &f); |
| | if ((f & ~LONGINT_OVERFLOW) != LONGINT_OK) |
| | { |
| | e = f; |
| | result = indeterminate; |
| | } |
| | else if (ckd_mul (&result, n, o) |
| | || (result != 0 && ((e | f) & LONGINT_OVERFLOW))) |
| | { |
| | e = LONGINT_OVERFLOW; |
| | result = INTMAX_MAX; |
| | } |
| | else |
| | { |
| | if (result == 0 && STRPREFIX (str, "0x")) |
| | diagnose (0, _("warning: %s is a zero multiplier; " |
| | "use %s if that is intended"), |
| | quote_n (0, "0x"), quote_n (1, "00x")); |
| | e = LONGINT_OK; |
| | } |
| | } |
| | else if (n <= INTMAX_MAX) |
| | result = n; |
| | else |
| | { |
| | e = LONGINT_OVERFLOW; |
| | result = INTMAX_MAX; |
| | } |
| |
|
| | *invalid = e; |
| | return result; |
| | } |
| |
|
| | |
| |
|
| | ATTRIBUTE_PURE |
| | static bool |
| | operand_is (char const *operand, char const *name) |
| | { |
| | return operand_matches (operand, name, '='); |
| | } |
| |
|
| | static void |
| | scanargs (int argc, char *const *argv) |
| | { |
| | idx_t blocksize = 0; |
| | intmax_t count = INTMAX_MAX; |
| | intmax_t skip = 0; |
| | intmax_t seek = 0; |
| | bool count_B = false, skip_B = false, seek_B = false; |
| |
|
| | for (int i = optind; i < argc; i++) |
| | { |
| | char const *name = argv[i]; |
| | char const *val = strchr (name, '='); |
| |
|
| | if (val == nullptr) |
| | { |
| | diagnose (0, _("unrecognized operand %s"), quoteaf (name)); |
| | usage (EXIT_FAILURE); |
| | } |
| | val++; |
| |
|
| | if (operand_is (name, "if")) |
| | input_file = val; |
| | else if (operand_is (name, "of")) |
| | output_file = val; |
| | else if (operand_is (name, "conv")) |
| | conversions_mask |= parse_symbols (val, conversions, false, |
| | N_("invalid conversion")); |
| | else if (operand_is (name, "iflag")) |
| | input_flags |= parse_symbols (val, flags, false, |
| | N_("invalid input flag")); |
| | else if (operand_is (name, "oflag")) |
| | output_flags |= parse_symbols (val, flags, false, |
| | N_("invalid output flag")); |
| | else if (operand_is (name, "status")) |
| | status_level = parse_symbols (val, statuses, true, |
| | N_("invalid status level")); |
| | else |
| | { |
| | strtol_error invalid = LONGINT_OK; |
| | intmax_t n = parse_integer (val, &invalid); |
| | bool has_B = !!strchr (val, 'B'); |
| | intmax_t n_min = 0; |
| | intmax_t n_max = INTMAX_MAX; |
| | idx_t *converted_idx = nullptr; |
| |
|
| | |
| | |
| | |
| | |
| | |
| | idx_t max_blocksize = MIN (IDX_MAX - 1, MIN (SSIZE_MAX, OFF_T_MAX)); |
| |
|
| | if (operand_is (name, "ibs")) |
| | { |
| | n_min = 1; |
| | n_max = max_blocksize; |
| | converted_idx = &input_blocksize; |
| | } |
| | else if (operand_is (name, "obs")) |
| | { |
| | n_min = 1; |
| | n_max = max_blocksize; |
| | converted_idx = &output_blocksize; |
| | } |
| | else if (operand_is (name, "bs")) |
| | { |
| | n_min = 1; |
| | n_max = max_blocksize; |
| | converted_idx = &blocksize; |
| | } |
| | else if (operand_is (name, "cbs")) |
| | { |
| | n_min = 1; |
| | n_max = MIN (SIZE_MAX, IDX_MAX); |
| | converted_idx = &conversion_blocksize; |
| | } |
| | else if (operand_is (name, "skip") || operand_is (name, "iseek")) |
| | { |
| | skip = n; |
| | skip_B = has_B; |
| | } |
| | else if (operand_is (name + (*name == 'o'), "seek")) |
| | { |
| | seek = n; |
| | seek_B = has_B; |
| | } |
| | else if (operand_is (name, "count")) |
| | { |
| | count = n; |
| | count_B = has_B; |
| | } |
| | else |
| | { |
| | diagnose (0, _("unrecognized operand %s"), quoteaf (name)); |
| | usage (EXIT_FAILURE); |
| | } |
| |
|
| | if (n < n_min) |
| | invalid = LONGINT_INVALID; |
| | else if (n_max < n) |
| | invalid = LONGINT_OVERFLOW; |
| |
|
| | if (invalid != LONGINT_OK) |
| | error (EXIT_FAILURE, invalid == LONGINT_OVERFLOW ? EOVERFLOW : 0, |
| | "%s: %s", _("invalid number"), quoteaf (val)); |
| | else if (converted_idx) |
| | *converted_idx = n; |
| | } |
| | } |
| |
|
| | if (blocksize) |
| | input_blocksize = output_blocksize = blocksize; |
| | else |
| | { |
| | |
| | |
| | conversions_mask |= C_TWOBUFS; |
| | } |
| |
|
| | if (input_blocksize == 0) |
| | input_blocksize = DEFAULT_BLOCKSIZE; |
| | if (output_blocksize == 0) |
| | output_blocksize = DEFAULT_BLOCKSIZE; |
| | if (conversion_blocksize == 0) |
| | conversions_mask &= ~(C_BLOCK | C_UNBLOCK); |
| |
|
| | if (input_flags & (O_DSYNC | O_SYNC)) |
| | input_flags |= O_RSYNC; |
| |
|
| | if (output_flags & O_FULLBLOCK) |
| | { |
| | diagnose (0, "%s: %s", _("invalid output flag"), quote ("fullblock")); |
| | usage (EXIT_FAILURE); |
| | } |
| |
|
| | if (skip_B) |
| | input_flags |= O_SKIP_BYTES; |
| | if (input_flags & O_SKIP_BYTES && skip != 0) |
| | { |
| | skip_records = skip / input_blocksize; |
| | skip_bytes = skip % input_blocksize; |
| | } |
| | else if (skip != 0) |
| | skip_records = skip; |
| |
|
| | if (count_B) |
| | input_flags |= O_COUNT_BYTES; |
| | if (input_flags & O_COUNT_BYTES && count != INTMAX_MAX) |
| | { |
| | max_records = count / input_blocksize; |
| | max_bytes = count % input_blocksize; |
| | } |
| | else if (count != INTMAX_MAX) |
| | max_records = count; |
| |
|
| | if (seek_B) |
| | output_flags |= O_SEEK_BYTES; |
| | if (output_flags & O_SEEK_BYTES && seek != 0) |
| | { |
| | seek_records = seek / output_blocksize; |
| | seek_bytes = seek % output_blocksize; |
| | } |
| | else if (seek != 0) |
| | seek_records = seek; |
| |
|
| | |
| | |
| | |
| | |
| | warn_partial_read = |
| | (! (conversions_mask & C_TWOBUFS) && ! (input_flags & O_FULLBLOCK) |
| | && (skip_records |
| | || (0 < max_records && max_records < INTMAX_MAX) |
| | || (input_flags | output_flags) & O_DIRECT)); |
| |
|
| | iread_fnc = ((input_flags & O_FULLBLOCK) |
| | ? iread_fullblock |
| | : iread); |
| | input_flags &= ~O_FULLBLOCK; |
| |
|
| | if (multiple_bits_set (conversions_mask & (C_ASCII | C_EBCDIC | C_IBM))) |
| | error (EXIT_FAILURE, 0, _("cannot combine any two of {ascii,ebcdic,ibm}")); |
| | if (multiple_bits_set (conversions_mask & (C_BLOCK | C_UNBLOCK))) |
| | error (EXIT_FAILURE, 0, _("cannot combine block and unblock")); |
| | if (multiple_bits_set (conversions_mask & (C_LCASE | C_UCASE))) |
| | error (EXIT_FAILURE, 0, _("cannot combine lcase and ucase")); |
| | if (multiple_bits_set (conversions_mask & (C_EXCL | C_NOCREAT))) |
| | error (EXIT_FAILURE, 0, _("cannot combine excl and nocreat")); |
| | if (multiple_bits_set (input_flags & (O_DIRECT | O_NOCACHE)) |
| | || multiple_bits_set (output_flags & (O_DIRECT | O_NOCACHE))) |
| | error (EXIT_FAILURE, 0, _("cannot combine direct and nocache")); |
| |
|
| | if (input_flags & O_NOCACHE) |
| | { |
| | i_nocache = true; |
| | i_nocache_eof = (max_records == 0 && max_bytes == 0); |
| | input_flags &= ~O_NOCACHE; |
| | } |
| | if (output_flags & O_NOCACHE) |
| | { |
| | o_nocache = true; |
| | o_nocache_eof = (max_records == 0 && max_bytes == 0); |
| | output_flags &= ~O_NOCACHE; |
| | } |
| | } |
| |
|
| | |
| |
|
| | static void |
| | apply_translations (void) |
| | { |
| | int i; |
| |
|
| | if (conversions_mask & C_ASCII) |
| | translate_charset (ebcdic_to_ascii); |
| |
|
| | if (conversions_mask & C_UCASE) |
| | { |
| | for (i = 0; i < 256; i++) |
| | trans_table[i] = toupper (trans_table[i]); |
| | translation_needed = true; |
| | } |
| | else if (conversions_mask & C_LCASE) |
| | { |
| | for (i = 0; i < 256; i++) |
| | trans_table[i] = tolower (trans_table[i]); |
| | translation_needed = true; |
| | } |
| |
|
| | if (conversions_mask & C_EBCDIC) |
| | { |
| | translate_charset (ascii_to_ebcdic); |
| | newline_character = ascii_to_ebcdic['\n']; |
| | space_character = ascii_to_ebcdic[' ']; |
| | } |
| | else if (conversions_mask & C_IBM) |
| | { |
| | translate_charset (ascii_to_ibm); |
| | newline_character = ascii_to_ibm['\n']; |
| | space_character = ascii_to_ibm[' ']; |
| | } |
| | } |
| |
|
| | |
| | |
| |
|
| | static void |
| | translate_buffer (char *buf, idx_t nread) |
| | { |
| | idx_t i; |
| | char *cp; |
| | for (i = nread, cp = buf; i; i--, cp++) |
| | *cp = trans_table[to_uchar (*cp)]; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | static char * |
| | swab_buffer (char *buf, idx_t *nread, int *saved_byte) |
| | { |
| | if (*nread == 0) |
| | return buf; |
| |
|
| | |
| | int prev_saved = *saved_byte; |
| | if ((prev_saved < 0) == (*nread & 1)) |
| | { |
| | unsigned char c = buf[--*nread]; |
| | *saved_byte = c; |
| | } |
| | else |
| | *saved_byte = -1; |
| |
|
| | |
| | |
| | |
| | for (idx_t i = *nread; 1 < i; i -= 2) |
| | buf[i] = buf[i - 2]; |
| |
|
| | if (prev_saved < 0) |
| | return buf + 1; |
| |
|
| | buf[1] = prev_saved; |
| | ++*nread; |
| | return buf; |
| | } |
| |
|
| | |
| | |
| |
|
| | static void |
| | advance_input_offset (intmax_t offset) |
| | { |
| | if (0 <= input_offset && ckd_add (&input_offset, input_offset, offset)) |
| | input_offset = -1; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | static intmax_t |
| | skip (int fdesc, char const *file, intmax_t records, idx_t blocksize, |
| | idx_t *bytes) |
| | { |
| | |
| | |
| | |
| |
|
| | errno = 0; |
| | off_t offset; |
| | if (! ckd_mul (&offset, records, blocksize) |
| | && ! ckd_add (&offset, offset, *bytes) |
| | && 0 <= lseek (fdesc, offset, SEEK_CUR)) |
| | { |
| | if (fdesc == STDIN_FILENO) |
| | { |
| | struct stat st; |
| | if (ifstat (STDIN_FILENO, &st) != 0) |
| | error (EXIT_FAILURE, errno, _("cannot fstat %s"), quoteaf (file)); |
| | if (usable_st_size (&st) && 0 < st.st_size && 0 <= input_offset |
| | && st.st_size - input_offset < offset) |
| | { |
| | |
| | |
| | |
| | records = ( offset - st.st_size ) / blocksize; |
| | offset = st.st_size - input_offset; |
| | } |
| | else |
| | records = 0; |
| | advance_input_offset (offset); |
| | } |
| | else |
| | { |
| | records = 0; |
| | *bytes = 0; |
| | } |
| | return records; |
| | } |
| | else |
| | { |
| | int lseek_errno = errno; |
| |
|
| | |
| | |
| | |
| | |
| | |
| | if (lseek (fdesc, 0, SEEK_END) >= 0) |
| | { |
| | |
| | |
| |
|
| | if (!lseek_errno) |
| | { |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | lseek_errno = EOVERFLOW; |
| | } |
| |
|
| | diagnose (lseek_errno, |
| | gettext (fdesc == STDIN_FILENO |
| | ? N_("%s: cannot skip") |
| | : N_("%s: cannot seek")), |
| | quotef (file)); |
| | |
| | |
| | quit (EXIT_FAILURE); |
| | } |
| | |
| |
|
| | char *buf; |
| | if (fdesc == STDIN_FILENO) |
| | { |
| | alloc_ibuf (); |
| | buf = ibuf; |
| | } |
| | else |
| | { |
| | alloc_obuf (); |
| | buf = obuf; |
| | } |
| |
|
| | do |
| | { |
| | ssize_t nread = iread_fnc (fdesc, buf, records ? blocksize : *bytes); |
| | if (nread < 0) |
| | { |
| | if (fdesc == STDIN_FILENO) |
| | { |
| | diagnose (errno, _("error reading %s"), quoteaf (file)); |
| | if (conversions_mask & C_NOERROR) |
| | print_stats (); |
| | } |
| | else |
| | diagnose (lseek_errno, _("%s: cannot seek"), quotef (file)); |
| | quit (EXIT_FAILURE); |
| | } |
| | else if (nread == 0) |
| | break; |
| | else if (fdesc == STDIN_FILENO) |
| | advance_input_offset (nread); |
| |
|
| | if (records != 0) |
| | records--; |
| | else |
| | *bytes = 0; |
| | } |
| | while (records || *bytes); |
| |
|
| | return records; |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| |
|
| | static bool |
| | advance_input_after_read_error (idx_t nbytes) |
| | { |
| | if (! input_seekable) |
| | { |
| | if (input_seek_errno == ESPIPE) |
| | return true; |
| | errno = input_seek_errno; |
| | } |
| | else |
| | { |
| | off_t offset; |
| | advance_input_offset (nbytes); |
| | if (input_offset < 0) |
| | { |
| | diagnose (0, _("offset overflow while reading file %s"), |
| | quoteaf (input_file)); |
| | return false; |
| | } |
| | offset = lseek (STDIN_FILENO, 0, SEEK_CUR); |
| | if (0 <= offset) |
| | { |
| | off_t diff; |
| | if (offset == input_offset) |
| | return true; |
| | diff = input_offset - offset; |
| | if (! (0 <= diff && diff <= nbytes) && status_level != STATUS_NONE) |
| | diagnose (0, _("warning: invalid file offset after failed read")); |
| | if (0 <= lseek (STDIN_FILENO, diff, SEEK_CUR)) |
| | return true; |
| | if (errno == 0) |
| | diagnose (0, _("cannot work around kernel bug after all")); |
| | } |
| | } |
| |
|
| | diagnose (errno, _("%s: cannot seek"), quotef (input_file)); |
| | return false; |
| | } |
| |
|
| | |
| |
|
| | static void |
| | copy_simple (char const *buf, idx_t nread) |
| | { |
| | char const *start = buf; |
| |
|
| | do |
| | { |
| | idx_t nfree = MIN (nread, output_blocksize - oc); |
| |
|
| | memcpy (obuf + oc, start, nfree); |
| |
|
| | nread -= nfree; |
| | start += nfree; |
| | oc += nfree; |
| | if (oc >= output_blocksize) |
| | write_output (); |
| | } |
| | while (nread != 0); |
| | } |
| |
|
| | |
| | |
| | |
| |
|
| | static void |
| | copy_with_block (char const *buf, idx_t nread) |
| | { |
| | for (idx_t i = nread; i; i--, buf++) |
| | { |
| | if (*buf == newline_character) |
| | { |
| | if (col < conversion_blocksize) |
| | { |
| | idx_t j; |
| | for (j = col; j < conversion_blocksize; j++) |
| | output_char (space_character); |
| | } |
| | col = 0; |
| | } |
| | else |
| | { |
| | if (col == conversion_blocksize) |
| | r_truncate++; |
| | else if (col < conversion_blocksize) |
| | output_char (*buf); |
| | col++; |
| | } |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| |
|
| | static void |
| | copy_with_unblock (char const *buf, idx_t nread) |
| | { |
| | static idx_t pending_spaces = 0; |
| |
|
| | for (idx_t i = 0; i < nread; i++) |
| | { |
| | char c = buf[i]; |
| |
|
| | if (col++ >= conversion_blocksize) |
| | { |
| | col = pending_spaces = 0; |
| | i--; |
| | output_char (newline_character); |
| | } |
| | else if (c == space_character) |
| | pending_spaces++; |
| | else |
| | { |
| | |
| | |
| | while (pending_spaces) |
| | { |
| | output_char (space_character); |
| | --pending_spaces; |
| | } |
| | output_char (c); |
| | } |
| | } |
| | } |
| |
|
| | |
| | |
| |
|
| | static void |
| | set_fd_flags (int fd, int add_flags, char const *name) |
| | { |
| | |
| | add_flags &= ~ (O_NOCTTY | O_NOFOLLOW); |
| |
|
| | if (add_flags) |
| | { |
| | int old_flags = fcntl (fd, F_GETFL); |
| | int new_flags = old_flags | add_flags; |
| | bool ok = true; |
| | if (old_flags < 0) |
| | ok = false; |
| | else if (old_flags != new_flags) |
| | { |
| | if (new_flags & (O_DIRECTORY | O_NOLINKS)) |
| | { |
| | |
| | |
| | struct stat st; |
| | if (ifstat (fd, &st) != 0) |
| | ok = false; |
| | else if ((new_flags & O_DIRECTORY) && ! S_ISDIR (st.st_mode)) |
| | { |
| | errno = ENOTDIR; |
| | ok = false; |
| | } |
| | else if ((new_flags & O_NOLINKS) && 1 < st.st_nlink) |
| | { |
| | errno = EMLINK; |
| | ok = false; |
| | } |
| | new_flags &= ~ (O_DIRECTORY | O_NOLINKS); |
| | } |
| |
|
| | if (ok && old_flags != new_flags |
| | && fcntl (fd, F_SETFL, new_flags) == -1) |
| | ok = false; |
| | } |
| |
|
| | if (!ok) |
| | error (EXIT_FAILURE, errno, _("setting flags for %s"), quoteaf (name)); |
| | } |
| | } |
| |
|
| | |
| |
|
| | static int |
| | dd_copy (void) |
| | { |
| | char *bufstart; |
| | ssize_t nread; |
| |
|
| | |
| | |
| | idx_t partread = 0; |
| |
|
| | int exit_status = EXIT_SUCCESS; |
| | idx_t n_bytes_read; |
| |
|
| | if (skip_records != 0 || skip_bytes != 0) |
| | { |
| | intmax_t us_bytes; |
| | bool us_bytes_overflow = |
| | (ckd_mul (&us_bytes, skip_records, input_blocksize) |
| | || ckd_add (&us_bytes, skip_bytes, us_bytes)); |
| | off_t input_offset0 = input_offset; |
| | intmax_t us_blocks = skip (STDIN_FILENO, input_file, |
| | skip_records, input_blocksize, &skip_bytes); |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | if ((us_blocks |
| | || (0 <= input_offset |
| | && (us_bytes_overflow |
| | || us_bytes != input_offset - input_offset0))) |
| | && status_level != STATUS_NONE) |
| | { |
| | diagnose (0, _("%s: cannot skip to specified offset"), |
| | quotef (input_file)); |
| | } |
| | } |
| |
|
| | if (seek_records != 0 || seek_bytes != 0) |
| | { |
| | idx_t bytes = seek_bytes; |
| | intmax_t write_records = skip (STDOUT_FILENO, output_file, |
| | seek_records, output_blocksize, &bytes); |
| |
|
| | if (write_records != 0 || bytes != 0) |
| | { |
| | memset (obuf, 0, write_records ? output_blocksize : bytes); |
| |
|
| | do |
| | { |
| | idx_t size = write_records ? output_blocksize : bytes; |
| | if (iwrite (STDOUT_FILENO, obuf, size) != size) |
| | { |
| | diagnose (errno, _("writing to %s"), quoteaf (output_file)); |
| | quit (EXIT_FAILURE); |
| | } |
| |
|
| | if (write_records != 0) |
| | write_records--; |
| | else |
| | bytes = 0; |
| | } |
| | while (write_records || bytes); |
| | } |
| | } |
| |
|
| | if (max_records == 0 && max_bytes == 0) |
| | return exit_status; |
| |
|
| | alloc_ibuf (); |
| | alloc_obuf (); |
| | int saved_byte = -1; |
| |
|
| | while (true) |
| | { |
| | if (status_level == STATUS_PROGRESS) |
| | { |
| | xtime_t progress_time = gethrxtime (); |
| | if (next_time <= progress_time) |
| | { |
| | print_xfer_stats (progress_time); |
| | next_time += XTIME_PRECISION; |
| | } |
| | } |
| |
|
| | if (r_partial + r_full >= max_records + !!max_bytes) |
| | break; |
| |
|
| | |
| | |
| | |
| | if ((conversions_mask & C_SYNC) && (conversions_mask & C_NOERROR)) |
| | memset (ibuf, |
| | (conversions_mask & (C_BLOCK | C_UNBLOCK)) ? ' ' : '\0', |
| | input_blocksize); |
| |
|
| | if (r_partial + r_full >= max_records) |
| | nread = iread_fnc (STDIN_FILENO, ibuf, max_bytes); |
| | else |
| | nread = iread_fnc (STDIN_FILENO, ibuf, input_blocksize); |
| |
|
| | if (nread > 0) |
| | { |
| | advance_input_offset (nread); |
| | if (i_nocache) |
| | invalidate_cache (STDIN_FILENO, nread); |
| | } |
| | else if (nread == 0) |
| | { |
| | i_nocache_eof |= i_nocache; |
| | o_nocache_eof |= o_nocache && ! (conversions_mask & C_NOTRUNC); |
| | break; |
| | } |
| | else |
| | { |
| | if (!(conversions_mask & C_NOERROR) || status_level != STATUS_NONE) |
| | diagnose (errno, _("error reading %s"), quoteaf (input_file)); |
| |
|
| | if (conversions_mask & C_NOERROR) |
| | { |
| | print_stats (); |
| | idx_t bad_portion = input_blocksize - partread; |
| |
|
| | |
| | |
| | invalidate_cache (STDIN_FILENO, bad_portion); |
| |
|
| | |
| | if (!advance_input_after_read_error (bad_portion)) |
| | { |
| | exit_status = EXIT_FAILURE; |
| |
|
| | |
| | input_seekable = false; |
| | input_seek_errno = ESPIPE; |
| | } |
| | if ((conversions_mask & C_SYNC) && !partread) |
| | |
| | |
| | nread = 0; |
| | else |
| | continue; |
| | } |
| | else |
| | { |
| | |
| | exit_status = EXIT_FAILURE; |
| | break; |
| | } |
| | } |
| |
|
| | n_bytes_read = nread; |
| |
|
| | if (n_bytes_read < input_blocksize) |
| | { |
| | r_partial++; |
| | partread = n_bytes_read; |
| | if (conversions_mask & C_SYNC) |
| | { |
| | if (!(conversions_mask & C_NOERROR)) |
| | |
| | memset (ibuf + n_bytes_read, |
| | (conversions_mask & (C_BLOCK | C_UNBLOCK)) ? ' ' : '\0', |
| | input_blocksize - n_bytes_read); |
| | n_bytes_read = input_blocksize; |
| | } |
| | } |
| | else |
| | { |
| | r_full++; |
| | partread = 0; |
| | } |
| |
|
| | if (ibuf == obuf) |
| | { |
| | idx_t nwritten = iwrite (STDOUT_FILENO, obuf, n_bytes_read); |
| | w_bytes += nwritten; |
| | if (nwritten != n_bytes_read) |
| | { |
| | diagnose (errno, _("error writing %s"), quoteaf (output_file)); |
| | return EXIT_FAILURE; |
| | } |
| | else if (n_bytes_read == input_blocksize) |
| | w_full++; |
| | else |
| | w_partial++; |
| | continue; |
| | } |
| |
|
| | |
| |
|
| | if (translation_needed) |
| | translate_buffer (ibuf, n_bytes_read); |
| |
|
| | if (conversions_mask & C_SWAB) |
| | bufstart = swab_buffer (ibuf, &n_bytes_read, &saved_byte); |
| | else |
| | bufstart = ibuf; |
| |
|
| | if (conversions_mask & C_BLOCK) |
| | copy_with_block (bufstart, n_bytes_read); |
| | else if (conversions_mask & C_UNBLOCK) |
| | copy_with_unblock (bufstart, n_bytes_read); |
| | else |
| | copy_simple (bufstart, n_bytes_read); |
| | } |
| |
|
| | |
| | if (0 <= saved_byte) |
| | { |
| | char saved_char = saved_byte; |
| | if (conversions_mask & C_BLOCK) |
| | copy_with_block (&saved_char, 1); |
| | else if (conversions_mask & C_UNBLOCK) |
| | copy_with_unblock (&saved_char, 1); |
| | else |
| | output_char (saved_char); |
| | } |
| |
|
| | if ((conversions_mask & C_BLOCK) && col > 0) |
| | { |
| | |
| | |
| | for (idx_t i = col; i < conversion_blocksize; i++) |
| | output_char (space_character); |
| | } |
| |
|
| | if (col && (conversions_mask & C_UNBLOCK)) |
| | { |
| | |
| | output_char (newline_character); |
| | } |
| |
|
| | |
| | if (oc != 0) |
| | { |
| | idx_t nwritten = iwrite (STDOUT_FILENO, obuf, oc); |
| | w_bytes += nwritten; |
| | if (nwritten != 0) |
| | w_partial++; |
| | if (nwritten != oc) |
| | { |
| | diagnose (errno, _("error writing %s"), quoteaf (output_file)); |
| | return EXIT_FAILURE; |
| | } |
| | } |
| |
|
| | |
| | |
| | if (final_op_was_seek) |
| | { |
| | struct stat stdout_stat; |
| | if (ifstat (STDOUT_FILENO, &stdout_stat) != 0) |
| | { |
| | diagnose (errno, _("cannot fstat %s"), quoteaf (output_file)); |
| | return EXIT_FAILURE; |
| | } |
| | if (S_ISREG (stdout_stat.st_mode) || S_TYPEISSHM (&stdout_stat)) |
| | { |
| | off_t output_offset = lseek (STDOUT_FILENO, 0, SEEK_CUR); |
| | if (0 <= output_offset && stdout_stat.st_size < output_offset) |
| | { |
| | if (iftruncate (STDOUT_FILENO, output_offset) != 0) |
| | { |
| | diagnose (errno, _("failed to truncate to %jd bytes" |
| | " in output file %s"), |
| | (intmax_t) output_offset, quoteaf (output_file)); |
| | return EXIT_FAILURE; |
| | } |
| | } |
| | } |
| | } |
| |
|
| | |
| | |
| | if (conversions_mask & (C_FDATASYNC | C_FSYNC) |
| | && status_level == STATUS_PROGRESS |
| | && 0 <= reported_w_bytes && reported_w_bytes < w_bytes) |
| | print_xfer_stats (0); |
| |
|
| | return exit_status; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| |
|
| | static int |
| | synchronize_output (void) |
| | { |
| | int exit_status = 0; |
| | int mask = conversions_mask; |
| | conversions_mask &= ~ (C_FDATASYNC | C_FSYNC); |
| |
|
| | if ((mask & C_FDATASYNC) && ifdatasync (STDOUT_FILENO) != 0) |
| | { |
| | if (errno != ENOSYS && errno != EINVAL) |
| | { |
| | diagnose (errno, _("fdatasync failed for %s"), quoteaf (output_file)); |
| | exit_status = EXIT_FAILURE; |
| | } |
| | mask |= C_FSYNC; |
| | } |
| |
|
| | if ((mask & C_FSYNC) && ifsync (STDOUT_FILENO) != 0) |
| | { |
| | diagnose (errno, _("fsync failed for %s"), quoteaf (output_file)); |
| | return EXIT_FAILURE; |
| | } |
| |
|
| | return exit_status; |
| | } |
| |
|
| | int |
| | main (int argc, char **argv) |
| | { |
| | int i; |
| | int exit_status; |
| | off_t offset; |
| |
|
| | install_signal_handlers (); |
| |
|
| | initialize_main (&argc, &argv); |
| | set_program_name (argv[0]); |
| | setlocale (LC_ALL, ""); |
| | bindtextdomain (PACKAGE, LOCALEDIR); |
| | textdomain (PACKAGE); |
| |
|
| | |
| | atexit (maybe_close_stdout); |
| |
|
| | page_size = getpagesize (); |
| |
|
| | parse_gnu_standard_options_only (argc, argv, PROGRAM_NAME, PACKAGE, Version, |
| | true, usage, AUTHORS, |
| | (char const *) nullptr); |
| | close_stdout_required = false; |
| |
|
| | |
| | for (i = 0; i < 256; i++) |
| | trans_table[i] = i; |
| |
|
| | |
| | scanargs (argc, argv); |
| |
|
| | apply_translations (); |
| |
|
| | if (input_file == nullptr) |
| | { |
| | input_file = _("standard input"); |
| | set_fd_flags (STDIN_FILENO, input_flags, input_file); |
| | } |
| | else |
| | { |
| | if (ifd_reopen (STDIN_FILENO, input_file, O_RDONLY | input_flags, 0) < 0) |
| | error (EXIT_FAILURE, errno, _("failed to open %s"), |
| | quoteaf (input_file)); |
| | } |
| |
|
| | offset = lseek (STDIN_FILENO, 0, SEEK_CUR); |
| | input_seekable = (0 <= offset); |
| | input_offset = MAX (0, offset); |
| | input_seek_errno = errno; |
| |
|
| | if (output_file == nullptr) |
| | { |
| | output_file = _("standard output"); |
| | set_fd_flags (STDOUT_FILENO, output_flags, output_file); |
| | } |
| | else |
| | { |
| | mode_t perms = MODE_RW_UGO; |
| | int opts |
| | = (output_flags |
| | | (conversions_mask & C_NOCREAT ? 0 : O_CREAT) |
| | | (conversions_mask & C_EXCL ? O_EXCL : 0) |
| | | (seek_records || (conversions_mask & C_NOTRUNC) ? 0 : O_TRUNC)); |
| |
|
| | off_t size; |
| | if ((ckd_mul (&size, seek_records, output_blocksize) |
| | || ckd_add (&size, seek_bytes, size)) |
| | && !(conversions_mask & C_NOTRUNC)) |
| | error (EXIT_FAILURE, 0, |
| | _("offset too large: " |
| | "cannot truncate to a length of seek=%jd" |
| | " (%td-byte) blocks"), |
| | seek_records, output_blocksize); |
| |
|
| | |
| | |
| | |
| | if ((! seek_records |
| | || ifd_reopen (STDOUT_FILENO, output_file, O_RDWR | opts, perms) < 0) |
| | && (ifd_reopen (STDOUT_FILENO, output_file, O_WRONLY | opts, perms) |
| | < 0)) |
| | error (EXIT_FAILURE, errno, _("failed to open %s"), |
| | quoteaf (output_file)); |
| |
|
| | if (seek_records != 0 && !(conversions_mask & C_NOTRUNC)) |
| | { |
| | if (iftruncate (STDOUT_FILENO, size) != 0) |
| | { |
| | |
| | |
| | |
| | |
| | |
| | int ftruncate_errno = errno; |
| | struct stat stdout_stat; |
| | if (ifstat (STDOUT_FILENO, &stdout_stat) != 0) |
| | { |
| | diagnose (errno, _("cannot fstat %s"), quoteaf (output_file)); |
| | exit_status = EXIT_FAILURE; |
| | } |
| | else if (S_ISREG (stdout_stat.st_mode) |
| | || S_ISDIR (stdout_stat.st_mode) |
| | || S_TYPEISSHM (&stdout_stat)) |
| | { |
| | intmax_t isize = size; |
| | diagnose (ftruncate_errno, |
| | _("failed to truncate to %jd bytes" |
| | " in output file %s"), |
| | isize, quoteaf (output_file)); |
| | exit_status = EXIT_FAILURE; |
| | } |
| | } |
| | } |
| | } |
| |
|
| | start_time = gethrxtime (); |
| | next_time = start_time + XTIME_PRECISION; |
| |
|
| | exit_status = dd_copy (); |
| |
|
| | int sync_status = synchronize_output (); |
| | if (sync_status) |
| | exit_status = sync_status; |
| |
|
| | if (max_records == 0 && max_bytes == 0) |
| | { |
| | |
| | if (i_nocache && !invalidate_cache (STDIN_FILENO, 0)) |
| | { |
| | diagnose (errno, _("failed to discard cache for: %s"), |
| | quotef (input_file)); |
| | exit_status = EXIT_FAILURE; |
| | } |
| | if (o_nocache && !invalidate_cache (STDOUT_FILENO, 0)) |
| | { |
| | diagnose (errno, _("failed to discard cache for: %s"), |
| | quotef (output_file)); |
| | exit_status = EXIT_FAILURE; |
| | } |
| | } |
| | else |
| | { |
| | |
| | if (i_nocache || i_nocache_eof) |
| | invalidate_cache (STDIN_FILENO, 0); |
| | if (o_nocache || o_nocache_eof) |
| | invalidate_cache (STDOUT_FILENO, 0); |
| | } |
| |
|
| | finish_up (); |
| | main_exit (exit_status); |
| | } |
| |
|