src/dd.c - coreutils-8.23
Global variables defined
Data types defined
Functions defined
Macros defined
Source code
#include <config.h>
#define SWAB_ALIGN_OFFSET 2
#include <assert.h>
#include <sys/types.h>
#include <signal.h>
#include <getopt.h>
#include "system.h"
#include "close-stream.h"
#include "error.h"
#include "fd-reopen.h"
#include "gethrxtime.h"
#include "human.h"
#include "long-options.h"
#include "quote.h"
#include "quotearg.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
#if ! HAVE_FDATASYNC
# define fdatasync(fd) (errno = ENOSYS, -1)
#endif
#define output_char(c) \
do \
{ \
obuf[oc++] = (c); \
if (oc >= output_blocksize) \
write_output (); \
} \
while (0)
#define DEFAULT_BLOCKSIZE 512
#define INPUT_BLOCK_SLOP (2 * SWAB_ALIGN_OFFSET + 2 * page_size - 1)
#define OUTPUT_BLOCK_SLOP (page_size - 1)
#define MAX_BLOCKSIZE(slop) MIN (SIZE_MAX - (slop), MIN (SSIZE_MAX, OFF_T_MAX))
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_NOXFER = 01,
STATUS_NONE = 02
};
static char const *input_file = NULL;
static char const *output_file = NULL;
static size_t page_size;
static size_t input_blocksize = 0;
static size_t output_blocksize = 0;
static size_t conversion_blocksize = 0;
static uintmax_t skip_records = 0;
static size_t skip_bytes = 0;
static uintmax_t seek_records = 0;
static uintmax_t seek_bytes = 0;
static bool final_op_was_seek;
static uintmax_t max_records = (uintmax_t) -1;
static size_t max_bytes = 0;
static int conversions_mask = 0;
static int input_flags = 0;
static int output_flags = 0;
static int status_flags = 0;
static bool translation_needed = false;
static uintmax_t w_partial = 0;
static uintmax_t w_full = 0;
static uintmax_t r_partial = 0;
static uintmax_t r_full = 0;
static uintmax_t w_bytes = 0;
static xtime_t start_time;
static bool input_seekable;
static int input_seek_errno;
static uintmax_t input_offset;
static bool input_offset_overflow;
static bool warn_partial_read;
static uintmax_t r_truncate = 0;
static char newline_character = '\n';
static char space_character = ' ';
static char *ibuf;
static char *obuf;
static size_t oc = 0;
static size_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 ssize_t (*iread_fnc) (int fd, char *buf, size_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_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)
};
verify (O_FULLBLOCK != 0);
verify (O_NOCACHE != 0);
verify (O_COUNT_BYTES != 0);
verify (O_SKIP_BYTES != 0);
verify (O_SEEK_BYTES != 0);
#define MULTIPLE_BITS_SET(i) (((i) & ((i) - 1)) != 0)
verify ( ! MULTIPLE_BITS_SET (O_FULLBLOCK));
verify ( ! MULTIPLE_BITS_SET (O_NOCACHE));
verify ( ! MULTIPLE_BITS_SET (O_COUNT_BYTES));
verify ( ! MULTIPLE_BITS_SET (O_SKIP_BYTES));
verify ( ! 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[] =
{
{"noxfer", STATUS_NOXFER},
{"none", STATUS_NONE},
{"", 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);
}
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\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 stdin\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 stdout\n\
oflag=FLAGS write as per the comma separated symbol list\n\
seek=N skip N obs-sized blocks at start of output\n\
skip=N skip N ibs-sized blocks at start of input\n\
status=WHICH WHICH info to suppress outputting to stderr;\n\
'noxfer' suppresses transfer stats, 'none' suppresses all\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.\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 the output for NUL input 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);
if (O_DIRECTORY)
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 discard cached data\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);
if (O_COUNT_BYTES)
fputs (_(" count_bytes treat 'count=N' as a byte count (iflag only)\n\
"), stdout);
if (O_SKIP_BYTES)
fputs (_(" skip_bytes treat 'skip=N' as a byte count (iflag only)\n\
"), stdout);
if (O_SEEK_BYTES)
fputs (_(" seek_bytes treat 'seek=N' as a byte count (oflag only)\n\
"), stdout);
{
char const *siginfo_name = (SIGINFO == SIGUSR1 ? "USR1" : "INFO");
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\
$ dd if=/dev/zero of=/dev/null& pid=$!\n\
$ kill -%s $pid; sleep 1; kill $pid\n\
18335302+0 records in\n\
18335302+0 records out\n\
9387674624 bytes (9.4 GB) copied, 34.6279 seconds, 271 MB/s\n\
\n\
Options are:\n\
\n\
"),
siginfo_name, siginfo_name);
}
fputs (HELP_OPTION_DESCRIPTION, stdout);
fputs (VERSION_OPTION_DESCRIPTION, stdout);
emit_ancillary_info ();
}
exit (status);
}
static char *
human_size (size_t n)
{
static char hbuf[LONGEST_HUMAN_READABLE + 1];
int human_opts =
(human_autoscale | human_round_to_nearest | human_base_1024
| human_space_before_unit | human_SI | human_B);
return human_readable (n, hbuf, human_opts, 1, 1);
}
static void
alloc_ibuf (void)
{
if (ibuf)
return;
char *real_buf = malloc (input_blocksize + INPUT_BLOCK_SLOP);
if (!real_buf)
error (EXIT_FAILURE, 0,
_("memory exhausted by input buffer of size %zu bytes (%s)"),
input_blocksize, human_size (input_blocksize));
real_buf += SWAB_ALIGN_OFFSET;
ibuf = ptr_align (real_buf, page_size);
}
static void
alloc_obuf (void)
{
if (obuf)
return;
if (conversions_mask & C_TWOBUFS)
{
char *real_obuf = malloc (output_blocksize + OUTPUT_BLOCK_SLOP);
if (!real_obuf)
error (EXIT_FAILURE, 0,
_("memory exhausted by output buffer of size %zu bytes (%s)"),
output_blocksize, human_size (output_blocksize));
obuf = ptr_align (real_obuf, page_size);
}
else
{
alloc_ibuf ();
obuf = ibuf;
}
assert (sizeof (uintptr_t) <= OUTPUT_BLOCK_SLOP);
memset (obuf + output_blocksize, 1, sizeof (uintptr_t));
}
static void
translate_charset (char const *new_trans)
{
int i;
for (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 void
print_stats (void)
{
char hbuf[LONGEST_HUMAN_READABLE + 1];
int human_opts =
(human_autoscale | human_round_to_nearest
| human_space_before_unit | human_SI | human_B);
double delta_s;
char const *bytes_per_second;
if (status_flags & STATUS_NONE)
return;
fprintf (stderr,
_("%"PRIuMAX"+%"PRIuMAX" records in\n"
"%"PRIuMAX"+%"PRIuMAX" records out\n"),
r_full, r_partial, w_full, w_partial);
if (r_truncate != 0)
fprintf (stderr,
ngettext ("%"PRIuMAX" truncated record\n",
"%"PRIuMAX" truncated records\n",
select_plural (r_truncate)),
r_truncate);
if (status_flags & STATUS_NOXFER)
return;
fprintf (stderr,
ngettext ("%"PRIuMAX" byte (%s) copied",
"%"PRIuMAX" bytes (%s) copied",
select_plural (w_bytes)),
w_bytes,
human_readable (w_bytes, hbuf, human_opts, 1, 1));
xtime_t now = gethrxtime ();
if (start_time < now)
{
double XTIME_PRECISIONe0 = XTIME_PRECISION;
uintmax_t delta_xtime = now;
delta_xtime -= start_time;
delta_s = delta_xtime / XTIME_PRECISIONe0;
bytes_per_second = human_readable (w_bytes, hbuf, human_opts,
XTIME_PRECISION, delta_xtime);
}
else
{
delta_s = 0;
bytes_per_second = _("Infinity B");
}
fprintf (stderr, _(", %g s, %s/s\n"), delta_s, bytes_per_second);
}
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)
{
sigaction (SIGINFO, NULL, &act);
if (act.sa_handler != SIG_IGN)
sigaddset (&caught_signals, SIGINFO);
}
sigaction (SIGINT, NULL, &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, NULL);
}
if (sigismember (&caught_signals, SIGINT))
{
act.sa_handler = interrupt_handler;
act.sa_flags = SA_NODEFER | SA_RESETHAND;
sigaction (SIGINT, &act, NULL);
}
#else
if (catch_siginfo && signal (SIGINFO, SIG_IGN) != SIG_IGN)
{
signal (SIGINFO, siginfo_handler);
siginterrupt (SIGINFO, 1);
}
if (signal (SIGINT, SIG_IGN) != SIG_IGN)
{
signal (SIGINT, interrupt_handler);
siginterrupt (SIGINT, 1);
}
#endif
}
static void
cleanup (void)
{
if (close (STDIN_FILENO) < 0)
error (EXIT_FAILURE, errno,
_("closing input file %s"), quote (input_file));
if (close (STDOUT_FILENO) < 0)
error (EXIT_FAILURE, errno,
_("closing output file %s"), quote (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, NULL);
if (interrupt)
cleanup ();
print_stats ();
if (interrupt)
raise (interrupt);
}
}
static void ATTRIBUTE_NORETURN
quit (int code)
{
cleanup ();
print_stats ();
process_signals ();
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)
{
off_t c_pending = *pending + len;
*pending = c_pending % page_size;
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 clen = cache_round (fd, len);
if (len && !clen)
return true; if (!len && !clen && max_records)
return true; off_t pending = len ? cache_round (fd, 0) : 0;
if (fd == STDIN_FILENO)
{
if (input_seekable)
{
#if HAVE_POSIX_FADVISE
adv_ret = posix_fadvise (fd, input_offset - clen - pending, clen,
POSIX_FADV_DONTNEED);
#else
errno = ENOTSUP;
#endif
}
else
errno = ESPIPE;
}
else if (fd == STDOUT_FILENO)
{
static off_t output_offset = -2;
if (output_offset != -1)
{
if (0 > output_offset)
{
output_offset = lseek (fd, 0, SEEK_CUR);
output_offset -= clen + pending;
}
if (0 <= output_offset)
{
#if HAVE_POSIX_FADVISE
adv_ret = posix_fadvise (fd, output_offset, clen,
POSIX_FADV_DONTNEED);
#else
errno = ENOTSUP;
#endif
output_offset += clen + pending;
}
}
}
return adv_ret != -1 ? true : false;
}
static ssize_t
iread (int fd, char *buf, size_t size)
{
ssize_t nread;
do
{
process_signals ();
nread = read (fd, buf, size);
}
while (nread < 0 && errno == EINTR);
if (0 < nread && warn_partial_read)
{
static ssize_t prev_nread;
if (0 < prev_nread && prev_nread < size)
{
uintmax_t prev = prev_nread;
if (!(status_flags & STATUS_NONE))
error (0, 0, ngettext (("warning: partial read (%"PRIuMAX" byte); "
"suggest iflag=fullblock"),
("warning: partial read (%"PRIuMAX" 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, size_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 size_t
iwrite (int fd, char const *buf, size_t size)
{
size_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_flags & STATUS_NONE))
error (0, errno, _("failed to turn off O_DIRECT: %s"),
quote (output_file));
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)
{
size_t nwritten = iwrite (STDOUT_FILENO, obuf, output_blocksize);
w_bytes += nwritten;
if (nwritten != output_blocksize)
{
error (0, errno, _("writing to %s"), quote (output_file));
if (nwritten != 0)
w_partial++;
quit (EXIT_FAILURE);
}
else
w_full++;
oc = 0;
}
static bool _GL_ATTRIBUTE_PURE
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,
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])
{
size_t slen = strcomma ? strcomma - str : strlen (str);
error (0, 0, "%s: %s", _(error_msgid),
quotearg_n_style_mem (0, locale_quoting_style, str, slen));
usage (EXIT_FAILURE);
}
}
value |= entry->value;
if (!strcomma)
break;
str = strcomma + 1;
}
return value;
}
static uintmax_t
parse_integer (const char *str, bool *invalid)
{
uintmax_t n;
char *suffix;
enum strtol_error e = xstrtoumax (str, &suffix, 10, &n, "bcEGkKMPTwYZ0");
if (e == LONGINT_INVALID_SUFFIX_CHAR && *suffix == 'x')
{
uintmax_t multiplier = parse_integer (suffix + 1, invalid);
if (multiplier != 0 && n * multiplier / multiplier != n)
{
*invalid = true;
return 0;
}
n *= multiplier;
}
else if (e != LONGINT_OK)
{
*invalid = true;
return 0;
}
return n;
}
static bool _GL_ATTRIBUTE_PURE
operand_is (char const *operand, char const *name)
{
return operand_matches (operand, name, '=');
}
static void
scanargs (int argc, char *const *argv)
{
int i;
size_t blocksize = 0;
uintmax_t count = (uintmax_t) -1;
uintmax_t skip = 0;
uintmax_t seek = 0;
for (i = optind; i < argc; i++)
{
char const *name = argv[i];
char const *val = strchr (name, '=');
if (val == NULL)
{
error (0, 0, _("unrecognized operand %s"), quote (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,
N_("invalid conversion"));
else if (operand_is (name, "iflag"))
input_flags |= parse_symbols (val, flags,
N_("invalid input flag"));
else if (operand_is (name, "oflag"))
output_flags |= parse_symbols (val, flags,
N_("invalid output flag"));
else if (operand_is (name, "status"))
status_flags |= parse_symbols (val, statuses,
N_("invalid status flag"));
else
{
bool invalid = false;
uintmax_t n = parse_integer (val, &invalid);
if (operand_is (name, "ibs"))
{
invalid |= ! (0 < n && n <= MAX_BLOCKSIZE (INPUT_BLOCK_SLOP));
input_blocksize = n;
}
else if (operand_is (name, "obs"))
{
invalid |= ! (0 < n && n <= MAX_BLOCKSIZE (OUTPUT_BLOCK_SLOP));
output_blocksize = n;
}
else if (operand_is (name, "bs"))
{
invalid |= ! (0 < n && n <= MAX_BLOCKSIZE (INPUT_BLOCK_SLOP));
blocksize = n;
}
else if (operand_is (name, "cbs"))
{
invalid |= ! (0 < n && n <= SIZE_MAX);
conversion_blocksize = n;
}
else if (operand_is (name, "skip"))
skip = n;
else if (operand_is (name, "seek"))
seek = n;
else if (operand_is (name, "count"))
count = n;
else
{
error (0, 0, _("unrecognized operand %s"), quote (name));
usage (EXIT_FAILURE);
}
if (invalid)
error (EXIT_FAILURE, 0, _("invalid number %s"), quote (val));
}
}
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)
{
error (0, 0, "%s: %s", _("invalid output flag"), "'fullblock'");
usage (EXIT_FAILURE);
}
if (input_flags & O_SEEK_BYTES)
{
error (0, 0, "%s: %s", _("invalid input flag"), "'seek_bytes'");
usage (EXIT_FAILURE);
}
if (output_flags & (O_COUNT_BYTES | O_SKIP_BYTES))
{
error (0, 0, "%s: %s", _("invalid output flag"),
output_flags & O_COUNT_BYTES ? "'count_bytes'" : "'skip_bytes'");
usage (EXIT_FAILURE);
}
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 (input_flags & O_COUNT_BYTES && count != (uintmax_t) -1)
{
max_records = count / input_blocksize;
max_bytes = count % input_blocksize;
}
else if (count != (uintmax_t) -1)
max_records = count;
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 < (uintmax_t) -1)
|| (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;
input_flags &= ~O_NOCACHE;
}
if (output_flags & O_NOCACHE)
{
o_nocache = true;
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, size_t nread)
{
char *cp;
size_t i;
for (i = nread, cp = buf; i; i--, cp++)
*cp = trans_table[to_uchar (*cp)];
}
static bool char_is_saved = false;
static char saved_char;
static char *
swab_buffer (char *buf, size_t *nread)
{
char *bufstart = buf;
char *cp;
size_t i;
if (char_is_saved)
{
*--bufstart = saved_char;
(*nread)++;
char_is_saved = false;
}
if (*nread & 1)
{
saved_char = bufstart[--*nread];
char_is_saved = true;
}
cp = bufstart + *nread; for (i = *nread / 2; i; i--, cp -= 2)
*cp = *(cp - 2);
return ++bufstart;
}
static void
advance_input_offset (uintmax_t offset)
{
input_offset += offset;
if (input_offset < offset)
input_offset_overflow = true;
}
#ifdef __linux__
# include <sys/mtio.h>
# define MT_SAME_POSITION(P, Q) \
((P).mt_resid == (Q).mt_resid \
&& (P).mt_fileno == (Q).mt_fileno \
&& (P).mt_blkno == (Q).mt_blkno)
static off_t
skip_via_lseek (char const *filename, int fdesc, off_t offset, int whence)
{
struct mtget s1;
struct mtget s2;
bool got_original_tape_position = (ioctl (fdesc, MTIOCGET, &s1) == 0);
off_t new_position = lseek (fdesc, offset, whence);
if (0 <= new_position
&& got_original_tape_position
&& ioctl (fdesc, MTIOCGET, &s2) == 0
&& MT_SAME_POSITION (s1, s2))
{
if (!(status_flags & STATUS_NONE))
error (0, 0, _("warning: working around lseek kernel bug for file "
"(%s)\n of mt_type=0x%0lx -- "
"see <sys/mtio.h> for the list of types"),
filename, s2.mt_type);
errno = 0;
new_position = -1;
}
return new_position;
}
#else
# define skip_via_lseek(Filename, Fd, Offset, Whence) lseek (Fd, Offset, Whence)
#endif
static uintmax_t
skip (int fdesc, char const *file, uintmax_t records, size_t blocksize,
size_t *bytes)
{
uintmax_t offset = records * blocksize + *bytes;
errno = 0;
if (records <= OFF_T_MAX / blocksize
&& 0 <= skip_via_lseek (file, fdesc, offset, SEEK_CUR))
{
if (fdesc == STDIN_FILENO)
{
struct stat st;
if (fstat (STDIN_FILENO, &st) != 0)
error (EXIT_FAILURE, errno, _("cannot fstat %s"), quote (file));
if (usable_st_size (&st) && 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 ((skip_via_lseek (file, fdesc, 0, SEEK_END)) >= 0)
{
if (!lseek_errno)
{
lseek_errno = EOVERFLOW;
}
if (fdesc == STDIN_FILENO)
error (0, lseek_errno, _("%s: cannot skip"), quote (file));
else
error (0, lseek_errno, _("%s: cannot seek"), quote (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)
{
error (0, errno, _("error reading %s"), quote (file));
if (conversions_mask & C_NOERROR)
print_stats ();
}
else
error (0, lseek_errno, _("%s: cannot seek"), quote (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 (size_t nbytes)
{
if (! input_seekable)
{
if (input_seek_errno == ESPIPE)
return true;
errno = input_seek_errno;
}
else
{
off_t offset;
advance_input_offset (nbytes);
input_offset_overflow |= (OFF_T_MAX < input_offset);
if (input_offset_overflow)
{
error (0, 0, _("offset overflow while reading file %s"),
quote (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_flags & STATUS_NONE))
error (0, 0, _("warning: invalid file offset after failed read"));
if (0 <= skip_via_lseek (input_file, STDIN_FILENO, diff, SEEK_CUR))
return true;
if (errno == 0)
error (0, 0, _("cannot work around kernel bug after all"));
}
}
error (0, errno, _("%s: cannot seek"), quote (input_file));
return false;
}
static void
copy_simple (char const *buf, size_t nread)
{
const char *start = buf;
do
{
size_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, size_t nread)
{
size_t i;
for (i = nread; i; i--, buf++)
{
if (*buf == newline_character)
{
if (col < conversion_blocksize)
{
size_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, size_t nread)
{
size_t i;
char c;
static size_t pending_spaces = 0;
for (i = 0; i < nread; i++)
{
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 (fstat (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"), quote (name));
}
}
static int
dd_copy (void)
{
char *bufstart; ssize_t nread;
size_t partread = 0;
int exit_status = EXIT_SUCCESS;
size_t n_bytes_read;
if (skip_records != 0 || skip_bytes != 0)
{
uintmax_t us_bytes = input_offset + (skip_records * input_blocksize)
+ skip_bytes;
uintmax_t us_blocks = skip (STDIN_FILENO, input_file,
skip_records, input_blocksize, &skip_bytes);
us_bytes -= input_offset;
if ((us_blocks || (!input_offset_overflow && us_bytes))
&& !(status_flags & STATUS_NONE))
{
error (0, 0,
_("%s: cannot skip to specified offset"), quote (input_file));
}
}
if (seek_records != 0 || seek_bytes != 0)
{
size_t bytes = seek_bytes;
uintmax_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
{
size_t size = write_records ? output_blocksize : bytes;
if (iwrite (STDOUT_FILENO, obuf, size) != size)
{
error (0, errno, _("writing to %s"), quote (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 ();
while (1)
{
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 && i_nocache)
invalidate_cache (STDIN_FILENO, nread);
if (nread == 0)
break;
if (nread < 0)
{
if (!(conversions_mask & C_NOERROR) || !(status_flags & STATUS_NONE))
error (0, errno, _("error reading %s"), quote (input_file));
if (conversions_mask & C_NOERROR)
{
print_stats ();
size_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;
advance_input_offset (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) {
size_t nwritten = iwrite (STDOUT_FILENO, obuf, n_bytes_read);
w_bytes += nwritten;
if (nwritten != n_bytes_read)
{
error (0, errno, _("error writing %s"), quote (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);
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 (char_is_saved)
{
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)
{
size_t i;
for (i = col; i < conversion_blocksize; i++)
output_char (space_character);
}
if (col && (conversions_mask & C_UNBLOCK))
{
output_char (newline_character);
}
if (oc != 0)
{
size_t nwritten = iwrite (STDOUT_FILENO, obuf, oc);
w_bytes += nwritten;
if (nwritten != 0)
w_partial++;
if (nwritten != oc)
{
error (0, errno, _("error writing %s"), quote (output_file));
return EXIT_FAILURE;
}
}
if (final_op_was_seek)
{
struct stat stdout_stat;
if (fstat (STDOUT_FILENO, &stdout_stat) != 0)
{
error (0, errno, _("cannot fstat %s"), quote (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 (output_offset > stdout_stat.st_size)
{
if (ftruncate (STDOUT_FILENO, output_offset) != 0)
{
error (0, errno,
_("failed to truncate to %" PRIdMAX " bytes"
" in output file %s"),
(intmax_t) output_offset, quote (output_file));
return EXIT_FAILURE;
}
}
}
}
if ((conversions_mask & C_FDATASYNC) && fdatasync (STDOUT_FILENO) != 0)
{
if (errno != ENOSYS && errno != EINVAL)
{
error (0, errno, _("fdatasync failed for %s"), quote (output_file));
exit_status = EXIT_FAILURE;
}
conversions_mask |= C_FSYNC;
}
if (conversions_mask & C_FSYNC)
while (fsync (STDOUT_FILENO) != 0)
if (errno != EINTR)
{
error (0, errno, _("fsync failed for %s"), quote (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_long_options (argc, argv, PROGRAM_NAME, PACKAGE, Version,
usage, AUTHORS, (char const *) NULL);
close_stdout_required = false;
if (getopt_long (argc, argv, "", NULL, NULL) != -1)
usage (EXIT_FAILURE);
for (i = 0; i < 256; i++)
trans_table[i] = i;
scanargs (argc, argv);
apply_translations ();
if (input_file == NULL)
{
input_file = _("standard input");
set_fd_flags (STDIN_FILENO, input_flags, input_file);
}
else
{
if (fd_reopen (STDIN_FILENO, input_file, O_RDONLY | input_flags, 0) < 0)
error (EXIT_FAILURE, errno, _("failed to open %s"), quote (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 == NULL)
{
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));
if ((! seek_records
|| fd_reopen (STDOUT_FILENO, output_file, O_RDWR | opts, perms) < 0)
&& (fd_reopen (STDOUT_FILENO, output_file, O_WRONLY | opts, perms)
< 0))
error (EXIT_FAILURE, errno, _("failed to open %s"),
quote (output_file));
if (seek_records != 0 && !(conversions_mask & C_NOTRUNC))
{
uintmax_t size = seek_records * output_blocksize + seek_bytes;
unsigned long int obs = output_blocksize;
if (OFF_T_MAX / output_blocksize < seek_records)
error (EXIT_FAILURE, 0,
_("offset too large: "
"cannot truncate to a length of seek=%"PRIuMAX""
" (%lu-byte) blocks"),
seek_records, obs);
if (ftruncate (STDOUT_FILENO, size) != 0)
{
int ftruncate_errno = errno;
struct stat stdout_stat;
if (fstat (STDOUT_FILENO, &stdout_stat) != 0)
error (EXIT_FAILURE, errno, _("cannot fstat %s"),
quote (output_file));
if (S_ISREG (stdout_stat.st_mode)
|| S_ISDIR (stdout_stat.st_mode)
|| S_TYPEISSHM (&stdout_stat))
error (EXIT_FAILURE, ftruncate_errno,
_("failed to truncate to %"PRIuMAX" bytes"
" in output file %s"),
size, quote (output_file));
}
}
}
start_time = gethrxtime ();
exit_status = dd_copy ();
if (max_records == 0 && max_bytes == 0)
{
if (i_nocache && !invalidate_cache (STDIN_FILENO, 0))
{
error (0, errno, _("failed to discard cache for: %s"),
quote (input_file));
exit_status = EXIT_FAILURE;
}
if (o_nocache && !invalidate_cache (STDOUT_FILENO, 0))
{
error (0, errno, _("failed to discard cache for: %s"),
quote (output_file));
exit_status = EXIT_FAILURE;
}
}
else if (max_records != (uintmax_t) -1)
{
if (i_nocache)
invalidate_cache (STDIN_FILENO, 0);
if (o_nocache)
invalidate_cache (STDOUT_FILENO, 0);
}
quit (exit_status);
}