diff --git a/.vscode/cSpell.json b/.vscode/cSpell.json index 9869923a4..498360139 100644 --- a/.vscode/cSpell.json +++ b/.vscode/cSpell.json @@ -11,7 +11,7 @@ { "name": "workspace", "path": "./cspell.dictionaries/workspace.wordlist.txt" } ], // ignorePaths - a list of globs to specify which files are to be ignored - "ignorePaths": ["Cargo.lock", "target/**", "tests/**/fixtures/**"], + "ignorePaths": ["Cargo.lock", "target/**", "tests/**/fixtures/**", "src/uu/dd/test-resources/**"], // ignoreWords - a list of words to be ignored (even if they are in the flagWords) "ignoreWords": [], // words - list of words to be always considered correct diff --git a/.vscode/cspell.dictionaries/jargon.wordlist.txt b/.vscode/cspell.dictionaries/jargon.wordlist.txt index c2e2c29f3..232266427 100644 --- a/.vscode/cspell.dictionaries/jargon.wordlist.txt +++ b/.vscode/cspell.dictionaries/jargon.wordlist.txt @@ -12,6 +12,7 @@ colorizable colorize coprime consts +conv cyclomatic dedup deduplication @@ -23,6 +24,7 @@ dev devs discoverability duplicative +dsync enqueue errored executable @@ -30,7 +32,10 @@ executables exponentiate eval falsey +fileio flamegraph +fullblock +gibi gibibytes glob globbing @@ -39,8 +44,12 @@ hardlink hardlinks hasher hashsums +infile +iflag +iflags kibi kibibytes +lcase mebi mebibytes mergeable @@ -49,8 +58,21 @@ microbenchmarks microbenchmarking multibyte multicall +noatime +nocache +nocreat +noctty +noerror +nofollow +nolinks +nonblock nonportable nonprinting +notrunc +noxfer +ofile +oflag +oflags peekable performant precompiled @@ -70,9 +92,12 @@ semiprime semiprimes shortcode shortcodes +siginfo +sigusr subcommand subexpression submodule +sync symlink symlinks syscall @@ -80,12 +105,14 @@ syscalls tokenize toolchain truthy +ucase unbuffered unescape unintuitive unprefixed unportable unsync +urand whitespace wordlist wordlists diff --git a/Cargo.lock b/Cargo.lock index 912955deb..a9ec6d726 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -167,6 +167,15 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "560c32574a12a89ecd91f5e742165893f86e3ab98d21f8ea548658eb9eef5f40" +[[package]] +name = "byte-unit" +version = "4.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "063197e6eb4b775b64160dedde7a0986bb2836cce140e9492e9e96f28e18bcd8" +dependencies = [ + "utf8-width", +] + [[package]] name = "byteorder" version = "1.4.3" @@ -175,9 +184,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "cc" -version = "1.0.68" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a72c244c1ff497a746a7e1fb3d14bd08420ecda70c8f25c7112f2781652d787" +checksum = "e70cc2f62c6ce1868963827bd677764c62d07c3d9a3e1fb1177ee1a9ab199eb2" [[package]] name = "cexpr" @@ -310,6 +319,7 @@ dependencies = [ "uu_csplit", "uu_cut", "uu_date", + "uu_dd", "uu_df", "uu_dircolors", "uu_dirname", @@ -510,9 +520,9 @@ dependencies = [ [[package]] name = "crossbeam-deque" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94af6efb46fef72616855b036a624cf27ba656ffc9be1b9a3c931cfc7749a9a9" +checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" dependencies = [ "cfg-if 1.0.0", "crossbeam-epoch", @@ -699,6 +709,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7" +[[package]] +name = "gcd" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c7cd301bf2ab11ae4e5bdfd79c221d97a25e46c089144a62ee9d09cb32d2b92" + [[package]] name = "generic-array" version = "0.8.4" @@ -1269,9 +1285,9 @@ checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" [[package]] name = "proc-macro2" -version = "1.0.27" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0d8caf72986c1a598726adc988bb5984792ef84f5ee5aa50209145ee8077038" +checksum = "5c7ed8b8c7b886ea3ed7dde405212185f423ab44682667c8c6dd14aa1d9f6612" dependencies = [ "unicode-xid 0.2.2", ] @@ -1491,9 +1507,9 @@ dependencies = [ [[package]] name = "reference-counted-singleton" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e03aec30be874d0786379a6ee483c653c58dd6863ad4ed873e697ed968f9358" +checksum = "ef445213a92fdddc4bc69d9111156d20ffd50704a86ad82b372aab701a0d3a9a" [[package]] name = "regex" @@ -1721,9 +1737,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.73" +version = "1.0.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f71489ff30030d2ae598524f61326b902466f72a0fb1a8564c001cc63425bcc7" +checksum = "1873d832550d4588c3dbc20f01361ab00bfe741048f71e3fecf145a7cc18b29c" dependencies = [ "proc-macro2", "quote 1.0.9", @@ -1899,6 +1915,12 @@ dependencies = [ "log", ] +[[package]] +name = "utf8-width" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cf7d77f457ef8dfa11e4cd5933c5ddb5dc52a94664071951219a97710f0a32b" + [[package]] name = "uu_arch" version = "0.0.7" @@ -2063,6 +2085,20 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "uu_dd" +version = "0.0.7" +dependencies = [ + "byte-unit", + "clap", + "gcd", + "libc", + "signal-hook", + "tempfile", + "uucore", + "uucore_procs", +] + [[package]] name = "uu_df" version = "0.0.7" diff --git a/Cargo.toml b/Cargo.toml index 04bfef500..eb3f99190 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,6 +45,7 @@ feat_common_core = [ "df", "dircolors", "dirname", + "dd", "du", "echo", "env", @@ -255,6 +256,7 @@ cp = { optional=true, version="0.0.7", package="uu_cp", path="src/uu/cp" } csplit = { optional=true, version="0.0.7", package="uu_csplit", path="src/uu/csplit" } cut = { optional=true, version="0.0.7", package="uu_cut", path="src/uu/cut" } date = { optional=true, version="0.0.7", package="uu_date", path="src/uu/date" } +dd = { optional=true, version="0.0.7", package="uu_dd", path="src/uu/dd" } df = { optional=true, version="0.0.7", package="uu_df", path="src/uu/df" } dircolors= { optional=true, version="0.0.7", package="uu_dircolors", path="src/uu/dircolors" } dirname = { optional=true, version="0.0.7", package="uu_dirname", path="src/uu/dirname" } diff --git a/README.md b/README.md index 43beafdba..23e4c8cd3 100644 --- a/README.md +++ b/README.md @@ -366,23 +366,23 @@ To contribute to uutils, please see [CONTRIBUTING](CONTRIBUTING.md). | Done | Semi-Done | To Do | |-----------|-----------|--------| | arch | cp | chcon | -| base32 | date | dd | -| base64 | df | runcon | -| basename | expr | stty | -| cat | install | | -| chgrp | join | | -| chmod | ls | | -| chown | more | | -| chroot | numfmt | | -| cksum | od (`--strings` and 128-bit data types missing) | | -| comm | pr | | -| csplit | printf | | -| cut | sort | | -| dircolors | split | | -| dirname | tac | | -| du | tail | | -| echo | test | | -| env | | | +| base32 | date | runcon | +| base64 | dd | stty | +| basename | df | | +| cat | expr | | +| chgrp | install | | +| chmod | join | | +| chown | ls | | +| chroot | more | | +| cksum | numfmt | | +| comm | od (`--strings` and 128-bit data types missing) | +| csplit | pr | | +| cut | printf | | +| dircolors | sort | | +| dirname | split | | +| du | tac | | +| echo | tail | | +| env | test | | | expand | | | | factor | | | | false | | | diff --git a/src/uu/dd/Cargo.toml b/src/uu/dd/Cargo.toml new file mode 100644 index 000000000..da0165245 --- /dev/null +++ b/src/uu/dd/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "uu_dd" +version = "0.0.7" +authors = ["uutils developers"] +license = "MIT" +description = "dd ~ (uutils) copy and convert files" + +homepage = "https://github.com/uutils/coreutils" +repository = "https://github.com/uutils/coreutils/tree/master/src/uu/dd" +keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] +categories = ["command-line-utilities"] +edition = "2018" + +[lib] +path = "src/dd.rs" + +[dependencies] +byte-unit = "4.0" +clap = { version = "2.33", features = [ "wrap_help" ] } +gcd = "2.0" +libc = "0.2" +signal-hook = "0.3.9" +uucore = { version=">=0.0.8", package="uucore", path="../../uucore" } +uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" } + +[dev-dependencies] +tempfile = "^3" + +[[bin]] +name = "dd" +path = "src/main.rs" diff --git a/src/uu/dd/src/conversion_tables.rs b/src/uu/dd/src/conversion_tables.rs new file mode 100644 index 000000000..aca2ef9bc --- /dev/null +++ b/src/uu/dd/src/conversion_tables.rs @@ -0,0 +1,221 @@ +// This file is part of the uutils coreutils package. +// +// (c) Tyler Steele +// +// For the full copyright and license information, please view the LICENSE +// file that was distributed with this source code. + +// Note: Conversion tables are just lookup tables. +// eg. The ASCII->EBCDIC table stores the EBCDIC code at the index +// obtained by treating the ASCII representation as a number. + +pub type ConversionTable = [u8; 256]; + +pub const ASCII_UCASE_TO_LCASE: ConversionTable = [ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, +]; + +pub const ASCII_LCASE_TO_UCASE: ConversionTable = [ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, + 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, +]; + +pub const ASCII_TO_EBCDIC: ConversionTable = [ + 0x00, 0x01, 0x02, 0x03, 0x37, 0x2d, 0x2e, 0x2f, 0x16, 0x05, 0x25, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x3c, 0x3d, 0x32, 0x26, 0x18, 0x19, 0x3f, 0x27, 0x1c, 0x1d, 0x1e, 0x1f, + 0x40, 0x5a, 0x7f, 0x7b, 0x5b, 0x6c, 0x50, 0x7d, 0x4d, 0x5d, 0x5c, 0x4e, 0x6b, 0x60, 0x4b, 0x61, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0x7a, 0x5e, 0x4c, 0x7e, 0x6e, 0x6f, + 0x7c, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, + 0xd7, 0xd8, 0xd9, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xad, 0xe0, 0xbd, 0x9a, 0x6d, + 0x79, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, + 0x97, 0x98, 0x99, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xc0, 0x4f, 0xd0, 0x5f, 0x07, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x15, 0x06, 0x17, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x09, 0x0a, 0x1b, + 0x30, 0x31, 0x1a, 0x33, 0x34, 0x35, 0x36, 0x08, 0x38, 0x39, 0x3a, 0x3b, 0x04, 0x14, 0x3e, 0xe1, + 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, + 0x76, 0x77, 0x78, 0x80, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x6a, 0x9b, 0x9c, 0x9d, 0x9e, + 0x9f, 0xa0, 0xaa, 0xab, 0xac, 0x4a, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xa1, 0xbe, 0xbf, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xda, 0xdb, + 0xdc, 0xdd, 0xde, 0xdf, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, +]; + +pub const ASCII_TO_EBCDIC_UCASE_TO_LCASE: ConversionTable = [ + 0x00, 0x01, 0x02, 0x03, 0x37, 0x2d, 0x2e, 0x2f, 0x16, 0x05, 0x25, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x3c, 0x3d, 0x32, 0x26, 0x18, 0x19, 0x3f, 0x27, 0x1c, 0x1d, 0x1e, 0x1f, + 0x40, 0x5a, 0x7f, 0x7b, 0x5b, 0x6c, 0x50, 0x7d, 0x4d, 0x5d, 0x5c, 0x4e, 0x6b, 0x60, 0x4b, 0x61, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0x7a, 0x5e, 0x4c, 0x7e, 0x6e, 0x6f, + 0x7c, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, + 0x97, 0x98, 0x99, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xad, 0xe0, 0xbd, 0x9a, 0x6d, + 0x79, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, + 0x97, 0x98, 0x99, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xc0, 0x4f, 0xd0, 0x5f, 0x07, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x15, 0x06, 0x17, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x09, 0x0a, 0x1b, + 0x30, 0x31, 0x1a, 0x33, 0x34, 0x35, 0x36, 0x08, 0x38, 0x39, 0x3a, 0x3b, 0x04, 0x14, 0x3e, 0xe1, + 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, + 0x76, 0x77, 0x78, 0x80, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x6a, 0x9b, 0x9c, 0x9d, 0x9e, + 0x9f, 0xa0, 0xaa, 0xab, 0xac, 0x4a, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xa1, 0xbe, 0xbf, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xda, 0xdb, + 0xdc, 0xdd, 0xde, 0xdf, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, +]; + +pub const ASCII_TO_EBCDIC_LCASE_TO_UCASE: ConversionTable = [ + 0x00, 0x01, 0x02, 0x03, 0x37, 0x2d, 0x2e, 0x2f, 0x16, 0x05, 0x25, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x3c, 0x3d, 0x32, 0x26, 0x18, 0x19, 0x3f, 0x27, 0x1c, 0x1d, 0x1e, 0x1f, + 0x40, 0x5a, 0x7f, 0x7b, 0x5b, 0x6c, 0x50, 0x7d, 0x4d, 0x5d, 0x5c, 0x4e, 0x6b, 0x60, 0x4b, 0x61, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0x7a, 0x5e, 0x4c, 0x7e, 0x6e, 0x6f, + 0x7c, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, + 0xd7, 0xd8, 0xd9, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xad, 0xe0, 0xbd, 0x9a, 0x6d, + 0x79, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, + 0xd7, 0xd8, 0xd9, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xc0, 0x4f, 0xd0, 0x5f, 0x07, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x15, 0x06, 0x17, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x09, 0x0a, 0x1b, + 0x30, 0x31, 0x1a, 0x33, 0x34, 0x35, 0x36, 0x08, 0x38, 0x39, 0x3a, 0x3b, 0x04, 0x14, 0x3e, 0xe1, + 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, + 0x76, 0x77, 0x78, 0x80, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x6a, 0x9b, 0x9c, 0x9d, 0x9e, + 0x9f, 0xa0, 0xaa, 0xab, 0xac, 0x4a, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xa1, 0xbe, 0xbf, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xda, 0xdb, + 0xdc, 0xdd, 0xde, 0xdf, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, +]; + +pub const ASCII_TO_IBM: ConversionTable = [ + 0x00, 0x01, 0x02, 0x03, 0x37, 0x2d, 0x2e, 0x2f, 0x16, 0x05, 0x25, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x3c, 0x3d, 0x32, 0x26, 0x18, 0x19, 0x3f, 0x27, 0x1c, 0x1d, 0x1e, 0x1f, + 0x40, 0x5a, 0x7f, 0x7b, 0x5b, 0x6c, 0x50, 0x7d, 0x4d, 0x5d, 0x5c, 0x4e, 0x6b, 0x60, 0x4b, 0x61, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0x7a, 0x5e, 0x4c, 0x7e, 0x6e, 0x6f, + 0x7c, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, + 0xd7, 0xd8, 0xd9, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xad, 0xe0, 0xbd, 0x5f, 0x6d, + 0x79, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, + 0x97, 0x98, 0x99, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xc0, 0x4f, 0xd0, 0xa1, 0x07, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x15, 0x06, 0x17, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x09, 0x0a, 0x1b, + 0x30, 0x31, 0x1a, 0x33, 0x34, 0x35, 0x36, 0x08, 0x38, 0x39, 0x3a, 0x3b, 0x04, 0x14, 0x3e, 0xe1, + 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, + 0x76, 0x77, 0x78, 0x80, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, + 0x9f, 0xa0, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xda, 0xdb, + 0xdc, 0xdd, 0xde, 0xdf, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, +]; + +pub const ASCII_TO_IBM_UCASE_TO_LCASE: ConversionTable = [ + 0x00, 0x01, 0x02, 0x03, 0x37, 0x2d, 0x2e, 0x2f, 0x16, 0x05, 0x25, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x3c, 0x3d, 0x32, 0x26, 0x18, 0x19, 0x3f, 0x27, 0x1c, 0x1d, 0x1e, 0x1f, + 0x40, 0x5a, 0x7f, 0x7b, 0x5b, 0x6c, 0x50, 0x7d, 0x4d, 0x5d, 0x5c, 0x4e, 0x6b, 0x60, 0x4b, 0x61, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0x7a, 0x5e, 0x4c, 0x7e, 0x6e, 0x6f, + 0x7c, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, + 0x97, 0x98, 0x99, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xad, 0xe0, 0xbd, 0x5f, 0x6d, + 0x79, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, + 0x97, 0x98, 0x99, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xc0, 0x4f, 0xd0, 0xa1, 0x07, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x15, 0x06, 0x17, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x09, 0x0a, 0x1b, + 0x30, 0x31, 0x1a, 0x33, 0x34, 0x35, 0x36, 0x08, 0x38, 0x39, 0x3a, 0x3b, 0x04, 0x14, 0x3e, 0xe1, + 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, + 0x76, 0x77, 0x78, 0x80, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, + 0x9f, 0xa0, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xda, 0xdb, + 0xdc, 0xdd, 0xde, 0xdf, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, +]; + +pub const ASCII_TO_IBM_LCASE_TO_UCASE: ConversionTable = [ + 0x00, 0x01, 0x02, 0x03, 0x37, 0x2d, 0x2e, 0x2f, 0x16, 0x05, 0x25, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x3c, 0x3d, 0x32, 0x26, 0x18, 0x19, 0x3f, 0x27, 0x1c, 0x1d, 0x1e, 0x1f, + 0x40, 0x5a, 0x7f, 0x7b, 0x5b, 0x6c, 0x50, 0x7d, 0x4d, 0x5d, 0x5c, 0x4e, 0x6b, 0x60, 0x4b, 0x61, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0x7a, 0x5e, 0x4c, 0x7e, 0x6e, 0x6f, + 0x7c, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, + 0xd7, 0xd8, 0xd9, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xad, 0xe0, 0xbd, 0x5f, 0x6d, + 0x79, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, + 0xd7, 0xd8, 0xd9, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xc0, 0x4f, 0xd0, 0xa1, 0x07, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x15, 0x06, 0x17, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x09, 0x0a, 0x1b, + 0x30, 0x31, 0x1a, 0x33, 0x34, 0x35, 0x36, 0x08, 0x38, 0x39, 0x3a, 0x3b, 0x04, 0x14, 0x3e, 0xe1, + 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, + 0x76, 0x77, 0x78, 0x80, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, + 0x9f, 0xa0, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xda, 0xdb, + 0xdc, 0xdd, 0xde, 0xdf, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, +]; + +pub const EBCDIC_TO_ASCII: ConversionTable = [ + 0x00, 0x01, 0x02, 0x03, 0x9c, 0x09, 0x86, 0x7f, 0x97, 0x8d, 0x8e, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x9d, 0x85, 0x08, 0x87, 0x18, 0x19, 0x92, 0x8f, 0x1c, 0x1d, 0x1e, 0x1f, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x0a, 0x17, 0x1b, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x05, 0x06, 0x07, + 0x90, 0x91, 0x16, 0x93, 0x94, 0x95, 0x96, 0x04, 0x98, 0x99, 0x9a, 0x9b, 0x14, 0x15, 0x9e, 0x1a, + 0x20, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xd5, 0x2e, 0x3c, 0x28, 0x2b, 0x7c, + 0x26, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0x21, 0x24, 0x2a, 0x29, 0x3b, 0x7e, + 0x2d, 0x2f, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xcb, 0x2c, 0x25, 0x5f, 0x3e, 0x3f, + 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0x60, 0x3a, 0x23, 0x40, 0x27, 0x3d, 0x22, + 0xc3, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, + 0xca, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x5e, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, + 0xd1, 0xe5, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0xd2, 0xd3, 0xd4, 0x5b, 0xd6, 0xd7, + 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0x5d, 0xe6, 0xe7, + 0x7b, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, + 0x7d, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, + 0x5c, 0x9f, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, +]; + +pub const EBCDIC_TO_ASCII_UCASE_TO_LCASE: ConversionTable = [ + 0x00, 0x01, 0x02, 0x03, 0x37, 0x2d, 0x2e, 0x2f, 0x16, 0x05, 0x25, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x3c, 0x3d, 0x32, 0x26, 0x18, 0x19, 0x3f, 0x27, 0x1c, 0x1d, 0x1e, 0x1f, + 0x40, 0x5a, 0x7f, 0x7b, 0x5b, 0x6c, 0x50, 0x7d, 0x4d, 0x5d, 0x5c, 0x4e, 0x6b, 0x60, 0x4b, 0x61, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0x7a, 0x5e, 0x4c, 0x7e, 0x6e, 0x6f, + 0x7c, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, + 0x97, 0x98, 0x99, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xad, 0xe0, 0xbd, 0x5f, 0x6d, + 0x79, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, + 0x97, 0x98, 0x99, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xc0, 0x4f, 0xd0, 0xa1, 0x07, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x15, 0x06, 0x17, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x09, 0x0a, 0x1b, + 0x30, 0x31, 0x1a, 0x33, 0x34, 0x35, 0x36, 0x08, 0x38, 0x39, 0x3a, 0x3b, 0x04, 0x14, 0x3e, 0xe1, + 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, + 0x76, 0x77, 0x78, 0x80, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, + 0x9f, 0xa0, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xda, 0xdb, + 0xdc, 0xdd, 0xde, 0xdf, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, +]; + +pub const EBCDIC_TO_ASCII_LCASE_TO_UCASE: ConversionTable = [ + 0x00, 0x01, 0x02, 0x03, 0x37, 0x2d, 0x2e, 0x2f, 0x16, 0x05, 0x25, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x3c, 0x3d, 0x32, 0x26, 0x18, 0x19, 0x3f, 0x27, 0x1c, 0x1d, 0x1e, 0x1f, + 0x40, 0x5a, 0x7f, 0x7b, 0x5b, 0x6c, 0x50, 0x7d, 0x4d, 0x5d, 0x5c, 0x4e, 0x6b, 0x60, 0x4b, 0x61, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0x7a, 0x5e, 0x4c, 0x7e, 0x6e, 0x6f, + 0x7c, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, + 0xd7, 0xd8, 0xd9, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xad, 0xe0, 0xbd, 0x5f, 0x6d, + 0x79, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, + 0xd7, 0xd8, 0xd9, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xc0, 0x4f, 0xd0, 0xa1, 0x07, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x15, 0x06, 0x17, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x09, 0x0a, 0x1b, + 0x30, 0x31, 0x1a, 0x33, 0x34, 0x35, 0x36, 0x08, 0x38, 0x39, 0x3a, 0x3b, 0x04, 0x14, 0x3e, 0xe1, + 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, + 0x76, 0x77, 0x78, 0x80, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, + 0x9f, 0xa0, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xda, 0xdb, + 0xdc, 0xdd, 0xde, 0xdf, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, +]; diff --git a/src/uu/dd/src/datastructures.rs b/src/uu/dd/src/datastructures.rs new file mode 100644 index 000000000..b4410d210 --- /dev/null +++ b/src/uu/dd/src/datastructures.rs @@ -0,0 +1,172 @@ +// This file is part of the uutils coreutils package. +// +// (c) Tyler Steele +// +// For the full copyright and license information, please view the LICENSE +// file that was distributed with this source code. +// spell-checker:ignore ctable, outfile + +use crate::conversion_tables::*; + +use std::error::Error; +use std::time; + +pub struct ProgUpdate { + pub read_stat: ReadStat, + pub write_stat: WriteStat, + pub duration: time::Duration, +} + +#[derive(Clone, Copy, Default)] +pub struct ReadStat { + pub reads_complete: u64, + pub reads_partial: u64, + pub records_truncated: u32, +} +impl std::ops::AddAssign for ReadStat { + fn add_assign(&mut self, other: Self) { + *self = Self { + reads_complete: self.reads_complete + other.reads_complete, + reads_partial: self.reads_partial + other.reads_partial, + records_truncated: self.records_truncated + other.records_truncated, + } + } +} + +#[derive(Clone, Copy)] +pub struct WriteStat { + pub writes_complete: u64, + pub writes_partial: u64, + pub bytes_total: u128, +} +impl std::ops::AddAssign for WriteStat { + fn add_assign(&mut self, other: Self) { + *self = Self { + writes_complete: self.writes_complete + other.writes_complete, + writes_partial: self.writes_partial + other.writes_partial, + bytes_total: self.bytes_total + other.bytes_total, + } + } +} + +type Cbs = usize; + +/// Stores all Conv Flags that apply to the input +#[derive(Debug, Default, PartialEq)] +pub struct IConvFlags { + pub ctable: Option<&'static ConversionTable>, + pub block: Option, + pub unblock: Option, + pub swab: bool, + pub sync: Option, + pub noerror: bool, +} + +/// Stores all Conv Flags that apply to the output +#[derive(Debug, Default, PartialEq)] +pub struct OConvFlags { + pub sparse: bool, + pub excl: bool, + pub nocreat: bool, + pub notrunc: bool, + pub fdatasync: bool, + pub fsync: bool, +} + +/// Stores all Flags that apply to the input +#[derive(Debug, Default, PartialEq)] +pub struct IFlags { + pub cio: bool, + pub direct: bool, + pub directory: bool, + pub dsync: bool, + pub sync: bool, + pub nocache: bool, + pub nonblock: bool, + pub noatime: bool, + pub noctty: bool, + pub nofollow: bool, + pub nolinks: bool, + pub binary: bool, + pub text: bool, + pub fullblock: bool, + pub count_bytes: bool, + pub skip_bytes: bool, +} + +/// Stores all Flags that apply to the output +#[derive(Debug, Default, PartialEq)] +pub struct OFlags { + pub append: bool, + pub cio: bool, + pub direct: bool, + pub directory: bool, + pub dsync: bool, + pub sync: bool, + pub nocache: bool, + pub nonblock: bool, + pub noatime: bool, + pub noctty: bool, + pub nofollow: bool, + pub nolinks: bool, + pub binary: bool, + pub text: bool, + pub seek_bytes: bool, +} + +/// The value of the status cl-option. +/// Controls printing of transfer stats +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum StatusLevel { + Progress, + Noxfer, + None, +} + +/// The value of count=N +/// Defaults to Reads(N) +/// if iflag=count_bytes +/// then becomes Bytes(N) +#[derive(Debug, PartialEq)] +pub enum CountType { + Reads(usize), + Bytes(usize), +} + +#[derive(Debug)] +pub enum InternalError { + WrongInputType, + WrongOutputType, + InvalidConvBlockUnblockCase, +} + +impl std::fmt::Display for InternalError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::WrongInputType | Self::WrongOutputType => { + write!(f, "Internal dd error: Wrong Input/Output data type") + } + Self::InvalidConvBlockUnblockCase => { + write!(f, "Invalid Conversion, Block, or Unblock data") + } + } + } +} + +impl Error for InternalError {} + +pub mod options { + pub const INFILE: &str = "if"; + pub const OUTFILE: &str = "of"; + pub const IBS: &str = "ibs"; + pub const OBS: &str = "obs"; + pub const BS: &str = "bs"; + pub const CBS: &str = "cbs"; + pub const COUNT: &str = "count"; + pub const SKIP: &str = "skip"; + pub const SEEK: &str = "seek"; + pub const STATUS: &str = "status"; + pub const CONV: &str = "conv"; + pub const IFLAG: &str = "iflag"; + pub const OFLAG: &str = "oflag"; +} diff --git a/src/uu/dd/src/dd.rs b/src/uu/dd/src/dd.rs new file mode 100644 index 000000000..b300dff2d --- /dev/null +++ b/src/uu/dd/src/dd.rs @@ -0,0 +1,1236 @@ +// This file is part of the uutils coreutils package. +// +// (c) Tyler Steele +// +// For the full copyright and license information, please view the LICENSE +// file that was distributed with this source code. + +// spell-checker:ignore fname, tname, fpath, specfile, testfile, unspec, ifile, ofile, outfile, fullblock, urand, fileio, atoe, atoibm, behaviour, bmax, bremain, btotal, cflags, creat, ctable, ctty, datastructures, doesnt, etoa, fileout, fname, gnudd, iconvflags, nocache, noctty, noerror, nofollow, nolinks, nonblock, oconvflags, outfile, parseargs, rlen, rmax, rposition, rremain, rsofar, rstat, sigusr, sigval, wlen, wstat + +#[macro_use] +extern crate uucore; +use uucore::InvalidEncodingHandling; + +#[cfg(test)] +mod dd_unit_tests; + +mod datastructures; +use datastructures::*; + +mod parseargs; +use parseargs::Matches; + +mod conversion_tables; +use conversion_tables::*; + +use byte_unit::Byte; +use clap::{self, crate_version}; +use gcd::Gcd; +#[cfg(target_os = "linux")] +use signal_hook::consts::signal; +use std::cmp; +use std::convert::TryInto; +use std::env; +use std::error::Error; +use std::fs::{File, OpenOptions}; +use std::io::{self, Read, Seek, Write}; +#[cfg(target_os = "linux")] +use std::os::unix::fs::OpenOptionsExt; +use std::path::Path; +use std::sync::mpsc; +#[cfg(target_os = "linux")] +use std::sync::{atomic::AtomicUsize, atomic::Ordering, Arc}; +use std::thread; +use std::time; + +const ABOUT: &str = "copy, and optionally convert, a file system resource"; +const BUF_INIT_BYTE: u8 = 0xDD; +const RTN_SUCCESS: i32 = 0; +const RTN_FAILURE: i32 = 1; +const NEWLINE: u8 = b'\n'; +const SPACE: u8 = b' '; + +struct Input { + src: R, + non_ascii: bool, + ibs: usize, + print_level: Option, + count: Option, + cflags: IConvFlags, + iflags: IFlags, +} + +impl Input { + fn new(matches: &Matches) -> Result> { + let ibs = parseargs::parse_ibs(matches)?; + let non_ascii = parseargs::parse_input_non_ascii(matches)?; + let print_level = parseargs::parse_status_level(matches)?; + let cflags = parseargs::parse_conv_flag_input(matches)?; + let iflags = parseargs::parse_iflags(matches)?; + let skip = parseargs::parse_skip_amt(&ibs, &iflags, matches)?; + let count = parseargs::parse_count(&iflags, matches)?; + + let mut i = Input { + src: io::stdin(), + non_ascii, + ibs, + print_level, + count, + cflags, + iflags, + }; + + if let Some(amt) = skip { + let mut buf = vec![BUF_INIT_BYTE; amt]; + + i.force_fill(&mut buf, amt)?; + } + + Ok(i) + } +} + +#[cfg(target_os = "linux")] +fn make_linux_iflags(iflags: &IFlags) -> Option { + let mut flag = 0; + + if iflags.direct { + flag |= libc::O_DIRECT; + } + if iflags.directory { + flag |= libc::O_DIRECTORY; + } + if iflags.dsync { + flag |= libc::O_DSYNC; + } + if iflags.noatime { + flag |= libc::O_NOATIME; + } + if iflags.noctty { + flag |= libc::O_NOCTTY; + } + if iflags.nofollow { + flag |= libc::O_NOFOLLOW; + } + if iflags.nonblock { + flag |= libc::O_NONBLOCK; + } + if iflags.sync { + flag |= libc::O_SYNC; + } + + if flag != 0 { + Some(flag) + } else { + None + } +} + +impl Input { + fn new(matches: &Matches) -> Result> { + let ibs = parseargs::parse_ibs(matches)?; + let non_ascii = parseargs::parse_input_non_ascii(matches)?; + let print_level = parseargs::parse_status_level(matches)?; + let cflags = parseargs::parse_conv_flag_input(matches)?; + let iflags = parseargs::parse_iflags(matches)?; + let skip = parseargs::parse_skip_amt(&ibs, &iflags, matches)?; + let count = parseargs::parse_count(&iflags, matches)?; + + if let Some(fname) = matches.value_of(options::INFILE) { + let mut src = { + let mut opts = OpenOptions::new(); + opts.read(true); + + #[cfg(target_os = "linux")] + if let Some(libc_flags) = make_linux_iflags(&iflags) { + opts.custom_flags(libc_flags); + } + + opts.open(fname)? + }; + + if let Some(amt) = skip { + let amt: u64 = amt.try_into()?; + src.seek(io::SeekFrom::Start(amt))?; + } + + let i = Input { + src, + non_ascii, + ibs, + print_level, + count, + cflags, + iflags, + }; + + Ok(i) + } else { + Err(Box::new(InternalError::WrongInputType)) + } + } +} + +impl Read for Input { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + let mut base_idx = 0; + let target_len = buf.len(); + loop { + match self.src.read(&mut buf[base_idx..]) { + Ok(0) => return Ok(base_idx), + Ok(rlen) if self.iflags.fullblock => { + base_idx += rlen; + + if base_idx >= target_len { + return Ok(target_len); + } + } + Ok(len) => return Ok(len), + Err(e) if e.kind() == io::ErrorKind::Interrupted => continue, + Err(_) if self.cflags.noerror => return Ok(base_idx), + Err(e) => return Err(e), + } + } + } +} + +impl Input { + /// Fills a given buffer. + /// Reads in increments of 'self.ibs'. + /// The start of each ibs-sized read follows the previous one. + fn fill_consecutive(&mut self, buf: &mut Vec) -> Result> { + let mut reads_complete = 0; + let mut reads_partial = 0; + let mut bytes_total = 0; + + for chunk in buf.chunks_mut(self.ibs) { + match self.read(chunk)? { + rlen if rlen == self.ibs => { + bytes_total += rlen; + reads_complete += 1; + } + rlen if rlen > 0 => { + bytes_total += rlen; + reads_partial += 1; + } + _ => break, + } + } + + buf.truncate(bytes_total); + Ok(ReadStat { + reads_complete, + reads_partial, + // Records are not truncated when filling. + records_truncated: 0, + }) + } + + /// Fills a given buffer. + /// Reads in increments of 'self.ibs'. + /// The start of each ibs-sized read is aligned to multiples of ibs; remaining space is filled with the 'pad' byte. + fn fill_blocks(&mut self, buf: &mut Vec, pad: u8) -> Result> { + let mut reads_complete = 0; + let mut reads_partial = 0; + let mut base_idx = 0; + + while base_idx < buf.len() { + let next_blk = cmp::min(base_idx + self.ibs, buf.len()); + let target_len = next_blk - base_idx; + + match self.read(&mut buf[base_idx..next_blk])? { + 0 => break, + rlen if rlen < target_len => { + reads_partial += 1; + let padding = vec![pad; target_len - rlen]; + buf.splice(base_idx + rlen..next_blk, padding.into_iter()); + } + _ => { + reads_complete += 1; + } + } + + base_idx += self.ibs; + } + + buf.truncate(base_idx); + Ok(ReadStat { + reads_complete, + reads_partial, + records_truncated: 0, + }) + } + + /// Force-fills a buffer, ignoring zero-length reads which would otherwise be + /// interpreted as EOF. + /// Note: This will not return unless the source (eventually) produces + /// enough bytes to meet target_len. + fn force_fill(&mut self, buf: &mut [u8], target_len: usize) -> Result> { + let mut base_idx = 0; + while base_idx < target_len { + base_idx += self.read(&mut buf[base_idx..target_len])?; + } + + Ok(base_idx) + } +} + +struct Output { + dst: W, + obs: usize, + cflags: OConvFlags, +} + +impl Output { + fn new(matches: &Matches) -> Result> { + let obs = parseargs::parse_obs(matches)?; + let cflags = parseargs::parse_conv_flag_output(matches)?; + + let dst = io::stdout(); + + Ok(Output { dst, obs, cflags }) + } + + fn fsync(&mut self) -> io::Result<()> { + self.dst.flush() + } + + fn fdatasync(&mut self) -> io::Result<()> { + self.dst.flush() + } +} + +#[cfg(target_os = "linux")] +fn make_linux_oflags(oflags: &OFlags) -> Option { + let mut flag = 0; + + // oflag=FLAG + if oflags.append { + flag |= libc::O_APPEND; + } + if oflags.direct { + flag |= libc::O_DIRECT; + } + if oflags.directory { + flag |= libc::O_DIRECTORY; + } + if oflags.dsync { + flag |= libc::O_DSYNC; + } + if oflags.noatime { + flag |= libc::O_NOATIME; + } + if oflags.noctty { + flag |= libc::O_NOCTTY; + } + if oflags.nofollow { + flag |= libc::O_NOFOLLOW; + } + if oflags.nonblock { + flag |= libc::O_NONBLOCK; + } + if oflags.sync { + flag |= libc::O_SYNC; + } + + if flag != 0 { + Some(flag) + } else { + None + } +} + +impl Output { + fn new(matches: &Matches) -> Result> { + fn open_dst(path: &Path, cflags: &OConvFlags, oflags: &OFlags) -> Result { + let mut opts = OpenOptions::new(); + opts.write(true) + .create(!cflags.nocreat) + .truncate(!cflags.notrunc) + .create_new(cflags.excl) + .append(oflags.append); + + #[cfg(target_os = "linux")] + if let Some(libc_flags) = make_linux_oflags(oflags) { + opts.custom_flags(libc_flags); + } + + opts.open(path) + } + let obs = parseargs::parse_obs(matches)?; + let cflags = parseargs::parse_conv_flag_output(matches)?; + let oflags = parseargs::parse_oflags(matches)?; + let seek = parseargs::parse_seek_amt(&obs, &oflags, matches)?; + + if let Some(fname) = matches.value_of(options::OUTFILE) { + let mut dst = open_dst(Path::new(&fname), &cflags, &oflags)?; + + if let Some(amt) = seek { + let amt: u64 = amt.try_into()?; + dst.seek(io::SeekFrom::Start(amt))?; + } + + Ok(Output { dst, obs, cflags }) + } else { + // The following error should only occur if someone + // mistakenly calls Output::::new() without checking + // if 'of' has been provided. In this case, + // Output::::new() is probably intended. + Err(Box::new(InternalError::WrongOutputType)) + } + } + + fn fsync(&mut self) -> io::Result<()> { + self.dst.flush()?; + self.dst.sync_all() + } + + fn fdatasync(&mut self) -> io::Result<()> { + self.dst.flush()?; + self.dst.sync_data() + } +} + +impl Seek for Output { + fn seek(&mut self, pos: io::SeekFrom) -> io::Result { + self.dst.seek(pos) + } +} + +impl Write for Output { + fn write(&mut self, buf: &[u8]) -> io::Result { + fn is_sparse(buf: &[u8]) -> bool { + buf.iter().all(|&e| e == 0u8) + } + // ----------------------------- + if self.cflags.sparse && is_sparse(buf) { + let seek_amt: i64 = buf + .len() + .try_into() + .expect("Internal dd Error: Seek amount greater than signed 64-bit integer"); + self.dst.seek(io::SeekFrom::Current(seek_amt))?; + Ok(buf.len()) + } else { + self.dst.write(buf) + } + } + + fn flush(&mut self) -> io::Result<()> { + self.dst.flush() + } +} + +impl Write for Output { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.dst.write(buf) + } + + fn flush(&mut self) -> io::Result<()> { + self.dst.flush() + } +} + +impl Output { + /// Write all data in the given buffer in writes of size obs. + fn write_blocks(&mut self, buf: Vec) -> io::Result { + let mut writes_complete = 0; + let mut writes_partial = 0; + let mut bytes_total = 0; + + for chunk in buf.chunks(self.obs) { + match self.write(chunk)? { + wlen if wlen < chunk.len() => { + writes_partial += 1; + bytes_total += wlen; + } + wlen => { + writes_complete += 1; + bytes_total += wlen; + } + } + } + + Ok(WriteStat { + writes_complete, + writes_partial, + bytes_total: bytes_total.try_into().unwrap_or(0u128), + }) + } +} + +impl Output { + /// Write all data in the given buffer in writes of size obs. + fn write_blocks(&mut self, buf: Vec) -> io::Result { + let mut writes_complete = 0; + let mut writes_partial = 0; + let mut bytes_total = 0; + + for chunk in buf.chunks(self.obs) { + match self.write(chunk)? { + wlen if wlen < chunk.len() => { + writes_partial += 1; + bytes_total += wlen; + } + wlen => { + writes_complete += 1; + bytes_total += wlen; + } + } + } + + Ok(WriteStat { + writes_complete, + writes_partial, + bytes_total: bytes_total.try_into().unwrap_or(0u128), + }) + } +} + +/// Splits the content of buf into cbs-length blocks +/// Appends padding as specified by conv=block and cbs=N +/// Expects ascii encoded data +fn block(buf: Vec, cbs: usize, rstat: &mut ReadStat) -> Vec> { + let mut blocks = buf + .split(|&e| e == NEWLINE) + .map(|split| split.to_vec()) + .fold(Vec::new(), |mut blocks, mut split| { + if split.len() > cbs { + rstat.records_truncated += 1; + } + split.resize(cbs, SPACE); + blocks.push(split); + + blocks + }); + + if let Some(last) = blocks.last() { + if last.iter().all(|&e| e == SPACE) { + blocks.pop(); + } + } + + blocks +} + +/// Trims padding from each cbs-length partition of buf +/// as specified by conv=unblock and cbs=N +/// Expects ascii encoded data +fn unblock(buf: Vec, cbs: usize) -> Vec { + buf.chunks(cbs).fold(Vec::new(), |mut acc, block| { + if let Some(last_char_idx) = block.iter().rposition(|&e| e != SPACE) { + // Include text up to last space. + acc.extend(&block[..=last_char_idx]); + } + + acc.push(NEWLINE); + acc + }) +} + +/// A helper for teasing out which options must be applied and in which order. +/// Some user options, such as the presence of conversion tables, will determine whether the input is assumed to be ascii. The parser sets the Input::non_ascii flag accordingly. +/// Examples: +/// - If conv=ebcdic or conv=ibm is specified then block, unblock or swab must be performed before the conversion happens since the source will start in ascii. +/// - If conv=ascii is specified then block, unblock or swab must be performed after the conversion since the source starts in ebcdic. +/// - If no conversion is specified then the source is assumed to be in ascii. +/// For more info see `info dd` +fn conv_block_unblock_helper( + mut buf: Vec, + i: &mut Input, + rstat: &mut ReadStat, +) -> Result, Box> { + // Local Predicate Fns ------------------------------------------------- + fn should_block_then_conv(i: &Input) -> bool { + !i.non_ascii && i.cflags.block.is_some() + } + fn should_conv_then_block(i: &Input) -> bool { + i.non_ascii && i.cflags.block.is_some() + } + fn should_unblock_then_conv(i: &Input) -> bool { + !i.non_ascii && i.cflags.unblock.is_some() + } + fn should_conv_then_unblock(i: &Input) -> bool { + i.non_ascii && i.cflags.unblock.is_some() + } + fn conv_only(i: &Input) -> bool { + i.cflags.ctable.is_some() && i.cflags.block.is_none() && i.cflags.unblock.is_none() + } + // Local Helper Fns ---------------------------------------------------- + fn apply_conversion(buf: &mut [u8], ct: &ConversionTable) { + for idx in 0..buf.len() { + buf[idx] = ct[buf[idx] as usize]; + } + } + // -------------------------------------------------------------------- + if conv_only(i) { + // no block/unblock + let ct = i.cflags.ctable.unwrap(); + apply_conversion(&mut buf, ct); + + Ok(buf) + } else if should_block_then_conv(i) { + // ascii input so perform the block first + let cbs = i.cflags.block.unwrap(); + + let mut blocks = block(buf, cbs, rstat); + + if let Some(ct) = i.cflags.ctable { + for buf in blocks.iter_mut() { + apply_conversion(buf, ct); + } + } + + let blocks = blocks.into_iter().flatten().collect(); + + Ok(blocks) + } else if should_conv_then_block(i) { + // Non-ascii so perform the conversion first + let cbs = i.cflags.block.unwrap(); + + if let Some(ct) = i.cflags.ctable { + apply_conversion(&mut buf, ct); + } + + let blocks = block(buf, cbs, rstat).into_iter().flatten().collect(); + + Ok(blocks) + } else if should_unblock_then_conv(i) { + // ascii input so perform the unblock first + let cbs = i.cflags.unblock.unwrap(); + + let mut buf = unblock(buf, cbs); + + if let Some(ct) = i.cflags.ctable { + apply_conversion(&mut buf, ct); + } + + Ok(buf) + } else if should_conv_then_unblock(i) { + // Non-ascii input so perform the conversion first + let cbs = i.cflags.unblock.unwrap(); + + if let Some(ct) = i.cflags.ctable { + apply_conversion(&mut buf, ct); + } + + let buf = unblock(buf, cbs); + + Ok(buf) + } else { + // The following error should not happen, as it results from + // insufficient command line data. This case should be caught + // by the parser before making it this far. + // Producing this error is an alternative to risking an unwrap call + // on 'cbs' if the required data is not provided. + Err(Box::new(InternalError::InvalidConvBlockUnblockCase)) + } +} + +/// Read helper performs read operations common to all dd reads, and dispatches the buffer to relevant helper functions as dictated by the operations requested by the user. +fn read_helper( + i: &mut Input, + bsize: usize, +) -> Result<(ReadStat, Vec), Box> { + // Local Predicate Fns ----------------------------------------------- + fn is_conv(i: &Input) -> bool { + i.cflags.ctable.is_some() + } + fn is_block(i: &Input) -> bool { + i.cflags.block.is_some() + } + fn is_unblock(i: &Input) -> bool { + i.cflags.unblock.is_some() + } + // Local Helper Fns ------------------------------------------------- + fn perform_swab(buf: &mut [u8]) { + for base in (1..buf.len()).step_by(2) { + buf.swap(base, base - 1); + } + } + // ------------------------------------------------------------------ + // Read + let mut buf = vec![BUF_INIT_BYTE; bsize]; + let mut rstat = match i.cflags.sync { + Some(ch) => i.fill_blocks(&mut buf, ch)?, + _ => i.fill_consecutive(&mut buf)?, + }; + // Return early if no data + if rstat.reads_complete == 0 && rstat.reads_partial == 0 { + return Ok((rstat, buf)); + } + + // Perform any conv=x[,x...] options + if i.cflags.swab { + perform_swab(&mut buf); + } + if is_conv(i) || is_block(i) || is_unblock(i) { + let buf = conv_block_unblock_helper(buf, i, &mut rstat)?; + Ok((rstat, buf)) + } else { + Ok((rstat, buf)) + } +} + +// Print io lines of a status update: +// + records in +// + records out +fn print_io_lines(update: &ProgUpdate) { + eprintln!( + "{}+{} records in", + update.read_stat.reads_complete, update.read_stat.reads_partial + ); + if update.read_stat.records_truncated > 0 { + eprintln!("{} truncated records", update.read_stat.records_truncated); + } + eprintln!( + "{}+{} records out", + update.write_stat.writes_complete, update.write_stat.writes_partial + ); +} +// Print the progress line of a status update: +// bytes (, ) copied,