Merge pull request #2502 from hbina/hbina-tr-reimplement-expansion

`tr`: Expanding expansion module
This commit is contained in:
Terts Diepraam 2022-01-23 19:08:00 +01:00 committed by GitHub
commit d2fe245192
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 1606 additions and 510 deletions

246
Cargo.lock generated
View file

@ -110,21 +110,6 @@ dependencies = [
"which",
]
[[package]]
name = "bit-set"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e11e16035ea35e4e5997b393eacbf6f63983188f7a2ad25bfb13465f5ad59de"
dependencies = [
"bit-vec",
]
[[package]]
name = "bit-vec"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
[[package]]
name = "bitflags"
version = "1.3.2"
@ -258,9 +243,9 @@ dependencies = [
[[package]]
name = "clap"
version = "3.0.9"
version = "3.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c506244a13c87262f84bf16369740d0b7c3850901b6a642aa41b031a710c473"
checksum = "7a30c3bf9ff12dfe5dae53f0a96e0febcd18420d1c0e7fad77796d9d5c4b5375"
dependencies = [
"atty",
"bitflags",
@ -279,7 +264,7 @@ version = "3.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d044e9db8cd0f68191becdeb5246b7462e4cf0c069b19ae00d1bf3fa9889498d"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
]
[[package]]
@ -318,7 +303,7 @@ version = "0.0.12"
dependencies = [
"atty",
"chrono",
"clap 3.0.9",
"clap 3.0.10",
"clap_complete",
"conv",
"filetime",
@ -1004,9 +989,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
[[package]]
name = "libc"
version = "0.2.112"
version = "0.2.113"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125"
checksum = "eef78b64d87775463c549fbd80e19249ef436ea3bf1de2a1eb7e717ec7fab1e9"
[[package]]
name = "libloading"
@ -1785,18 +1770,18 @@ dependencies = [
[[package]]
name = "serde"
version = "1.0.133"
version = "1.0.134"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97565067517b60e2d1ea8b268e59ce036de907ac523ad83a0475da04e818989a"
checksum = "96b3c34c1690edf8174f5b289a336ab03f568a4460d8c6df75f2f3a692b3bc6a"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.133"
version = "1.0.134"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed201699328568d8d08208fdd080e3ff594e6c422e438b6705905da01005d537"
checksum = "784ed1fbfa13fe191077537b0d70ec8ad1e903cfe04831da608aa36457cb653d"
dependencies = [
"proc-macro2",
"quote 1.0.14",
@ -1893,9 +1878,9 @@ checksum = "f67ad224767faa3c7d8b6d91985b78e70a1324408abcb1cfcc2be4c06bc06043"
[[package]]
name = "socket2"
version = "0.4.2"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5dc90fe6c7be1a323296982db1836d1ea9e47b6839496dde9a541bc496df3516"
checksum = "0f82496b90c36d70af5fcd482edaa2e0bd16fade569de1330405fecbbdac736b"
dependencies = [
"libc",
"winapi 0.3.9",
@ -1939,9 +1924,9 @@ dependencies = [
[[package]]
name = "syn"
version = "1.0.85"
version = "1.0.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a684ac3dcd8913827e18cd09a68384ee66c1de24157e3c556c9ab16d85695fb7"
checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b"
dependencies = [
"proc-macro2",
"quote 1.0.14",
@ -2156,7 +2141,7 @@ checksum = "7cf7d77f457ef8dfa11e4cd5933c5ddb5dc52a94664071951219a97710f0a32b"
name = "uu_arch"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"platform-info",
"uucore",
"uucore_procs",
@ -2166,7 +2151,7 @@ dependencies = [
name = "uu_base32"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"uucore",
"uucore_procs",
]
@ -2184,7 +2169,7 @@ dependencies = [
name = "uu_basename"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"uucore",
"uucore_procs",
]
@ -2193,7 +2178,7 @@ dependencies = [
name = "uu_basenc"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"uu_base32",
"uucore",
"uucore_procs",
@ -2204,7 +2189,7 @@ name = "uu_cat"
version = "0.0.12"
dependencies = [
"atty",
"clap 3.0.9",
"clap 3.0.10",
"nix 0.23.1",
"thiserror",
"unix_socket",
@ -2217,7 +2202,7 @@ dependencies = [
name = "uu_chcon"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"fts-sys",
"libc",
"selinux",
@ -2230,7 +2215,7 @@ dependencies = [
name = "uu_chgrp"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"uucore",
"uucore_procs",
]
@ -2239,7 +2224,7 @@ dependencies = [
name = "uu_chmod"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"libc",
"uucore",
"uucore_procs",
@ -2250,7 +2235,7 @@ dependencies = [
name = "uu_chown"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"uucore",
"uucore_procs",
]
@ -2259,7 +2244,7 @@ dependencies = [
name = "uu_chroot"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"uucore",
"uucore_procs",
]
@ -2268,7 +2253,7 @@ dependencies = [
name = "uu_cksum"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"libc",
"uucore",
"uucore_procs",
@ -2278,7 +2263,7 @@ dependencies = [
name = "uu_comm"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"libc",
"uucore",
"uucore_procs",
@ -2288,7 +2273,7 @@ dependencies = [
name = "uu_cp"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"exacl",
"filetime",
"ioctl-sys",
@ -2306,7 +2291,7 @@ dependencies = [
name = "uu_csplit"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"regex",
"thiserror",
"uucore",
@ -2319,7 +2304,7 @@ version = "0.0.12"
dependencies = [
"atty",
"bstr",
"clap 3.0.9",
"clap 3.0.10",
"memchr 2.4.1",
"uucore",
"uucore_procs",
@ -2330,7 +2315,7 @@ name = "uu_date"
version = "0.0.12"
dependencies = [
"chrono",
"clap 3.0.9",
"clap 3.0.10",
"libc",
"uucore",
"uucore_procs",
@ -2342,7 +2327,7 @@ name = "uu_dd"
version = "0.0.12"
dependencies = [
"byte-unit",
"clap 3.0.9",
"clap 3.0.10",
"gcd",
"libc",
"signal-hook",
@ -2355,7 +2340,7 @@ dependencies = [
name = "uu_df"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"number_prefix",
"uucore",
"uucore_procs",
@ -2365,7 +2350,7 @@ dependencies = [
name = "uu_dircolors"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"glob",
"uucore",
"uucore_procs",
@ -2375,7 +2360,7 @@ dependencies = [
name = "uu_dirname"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"libc",
"uucore",
"uucore_procs",
@ -2386,7 +2371,7 @@ name = "uu_du"
version = "0.0.12"
dependencies = [
"chrono",
"clap 3.0.9",
"clap 3.0.10",
"uucore",
"uucore_procs",
"winapi 0.3.9",
@ -2396,7 +2381,7 @@ dependencies = [
name = "uu_echo"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"uucore",
"uucore_procs",
]
@ -2405,7 +2390,7 @@ dependencies = [
name = "uu_env"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"libc",
"rust-ini",
"uucore",
@ -2416,7 +2401,7 @@ dependencies = [
name = "uu_expand"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"unicode-width",
"uucore",
"uucore_procs",
@ -2426,7 +2411,7 @@ dependencies = [
name = "uu_expr"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"libc",
"num-bigint",
"num-traits",
@ -2439,7 +2424,7 @@ dependencies = [
name = "uu_factor"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"coz",
"num-traits",
"paste 0.1.18",
@ -2454,7 +2439,7 @@ dependencies = [
name = "uu_false"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"uucore",
"uucore_procs",
]
@ -2463,7 +2448,7 @@ dependencies = [
name = "uu_fmt"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"libc",
"unicode-width",
"uucore",
@ -2474,7 +2459,7 @@ dependencies = [
name = "uu_fold"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"uucore",
"uucore_procs",
]
@ -2483,7 +2468,7 @@ dependencies = [
name = "uu_groups"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"uucore",
"uucore_procs",
]
@ -2493,7 +2478,7 @@ name = "uu_hashsum"
version = "0.0.12"
dependencies = [
"blake2b_simd",
"clap 3.0.9",
"clap 3.0.10",
"digest",
"hex",
"libc",
@ -2512,7 +2497,7 @@ dependencies = [
name = "uu_head"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"memchr 2.4.1",
"uucore",
"uucore_procs",
@ -2522,7 +2507,7 @@ dependencies = [
name = "uu_hostid"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"libc",
"uucore",
"uucore_procs",
@ -2532,7 +2517,7 @@ dependencies = [
name = "uu_hostname"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"hostname",
"libc",
"uucore",
@ -2544,7 +2529,7 @@ dependencies = [
name = "uu_id"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"selinux",
"uucore",
"uucore_procs",
@ -2554,7 +2539,7 @@ dependencies = [
name = "uu_install"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"file_diff",
"filetime",
"libc",
@ -2567,7 +2552,7 @@ dependencies = [
name = "uu_join"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"uucore",
"uucore_procs",
]
@ -2576,7 +2561,7 @@ dependencies = [
name = "uu_kill"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"libc",
"uucore",
"uucore_procs",
@ -2586,7 +2571,7 @@ dependencies = [
name = "uu_link"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"libc",
"uucore",
"uucore_procs",
@ -2596,7 +2581,7 @@ dependencies = [
name = "uu_ln"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"libc",
"uucore",
"uucore_procs",
@ -2606,7 +2591,7 @@ dependencies = [
name = "uu_logname"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"libc",
"uucore",
"uucore_procs",
@ -2618,7 +2603,7 @@ version = "0.0.12"
dependencies = [
"atty",
"chrono",
"clap 3.0.9",
"clap 3.0.10",
"glob",
"lazy_static",
"lscolors",
@ -2636,7 +2621,7 @@ dependencies = [
name = "uu_mkdir"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"libc",
"uucore",
"uucore_procs",
@ -2646,7 +2631,7 @@ dependencies = [
name = "uu_mkfifo"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"libc",
"uucore",
"uucore_procs",
@ -2656,7 +2641,7 @@ dependencies = [
name = "uu_mknod"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"libc",
"uucore",
"uucore_procs",
@ -2666,7 +2651,7 @@ dependencies = [
name = "uu_mktemp"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"rand 0.5.6",
"tempfile",
"uucore",
@ -2678,7 +2663,7 @@ name = "uu_more"
version = "0.0.12"
dependencies = [
"atty",
"clap 3.0.9",
"clap 3.0.10",
"crossterm",
"nix 0.23.1",
"redox_syscall",
@ -2693,7 +2678,7 @@ dependencies = [
name = "uu_mv"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"fs_extra",
"uucore",
"uucore_procs",
@ -2703,7 +2688,7 @@ dependencies = [
name = "uu_nice"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"libc",
"nix 0.23.1",
"uucore",
@ -2715,7 +2700,7 @@ name = "uu_nl"
version = "0.0.12"
dependencies = [
"aho-corasick",
"clap 3.0.9",
"clap 3.0.10",
"libc",
"memchr 2.4.1",
"regex",
@ -2729,7 +2714,7 @@ name = "uu_nohup"
version = "0.0.12"
dependencies = [
"atty",
"clap 3.0.9",
"clap 3.0.10",
"libc",
"uucore",
"uucore_procs",
@ -2739,7 +2724,7 @@ dependencies = [
name = "uu_nproc"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"libc",
"num_cpus",
"uucore",
@ -2750,7 +2735,7 @@ dependencies = [
name = "uu_numfmt"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"uucore",
"uucore_procs",
]
@ -2760,7 +2745,7 @@ name = "uu_od"
version = "0.0.12"
dependencies = [
"byteorder",
"clap 3.0.9",
"clap 3.0.10",
"half",
"libc",
"uucore",
@ -2771,7 +2756,7 @@ dependencies = [
name = "uu_paste"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"uucore",
"uucore_procs",
]
@ -2780,7 +2765,7 @@ dependencies = [
name = "uu_pathchk"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"libc",
"uucore",
"uucore_procs",
@ -2790,7 +2775,7 @@ dependencies = [
name = "uu_pinky"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"uucore",
"uucore_procs",
]
@ -2800,7 +2785,7 @@ name = "uu_pr"
version = "0.0.12"
dependencies = [
"chrono",
"clap 3.0.9",
"clap 3.0.10",
"getopts",
"itertools 0.10.3",
"quick-error 2.0.1",
@ -2813,7 +2798,7 @@ dependencies = [
name = "uu_printenv"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"uucore",
"uucore_procs",
]
@ -2822,7 +2807,7 @@ dependencies = [
name = "uu_printf"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"itertools 0.8.2",
"uucore",
"uucore_procs",
@ -2833,7 +2818,7 @@ name = "uu_ptx"
version = "0.0.12"
dependencies = [
"aho-corasick",
"clap 3.0.9",
"clap 3.0.10",
"libc",
"memchr 2.4.1",
"regex",
@ -2846,7 +2831,7 @@ dependencies = [
name = "uu_pwd"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"uucore",
"uucore_procs",
]
@ -2855,7 +2840,7 @@ dependencies = [
name = "uu_readlink"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"libc",
"uucore",
"uucore_procs",
@ -2865,7 +2850,7 @@ dependencies = [
name = "uu_realpath"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"uucore",
"uucore_procs",
]
@ -2874,7 +2859,7 @@ dependencies = [
name = "uu_relpath"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"uucore",
"uucore_procs",
]
@ -2883,7 +2868,7 @@ dependencies = [
name = "uu_rm"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"remove_dir_all",
"uucore",
"uucore_procs",
@ -2895,7 +2880,7 @@ dependencies = [
name = "uu_rmdir"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"libc",
"uucore",
"uucore_procs",
@ -2905,7 +2890,7 @@ dependencies = [
name = "uu_runcon"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"fts-sys",
"libc",
"selinux",
@ -2919,7 +2904,7 @@ name = "uu_seq"
version = "0.0.12"
dependencies = [
"bigdecimal",
"clap 3.0.9",
"clap 3.0.10",
"num-bigint",
"num-traits",
"uucore",
@ -2930,7 +2915,7 @@ dependencies = [
name = "uu_shred"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"libc",
"rand 0.7.3",
"uucore",
@ -2941,7 +2926,7 @@ dependencies = [
name = "uu_shuf"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"rand 0.5.6",
"uucore",
"uucore_procs",
@ -2951,7 +2936,7 @@ dependencies = [
name = "uu_sleep"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"uucore",
"uucore_procs",
]
@ -2961,7 +2946,7 @@ name = "uu_sort"
version = "0.0.12"
dependencies = [
"binary-heap-plus",
"clap 3.0.9",
"clap 3.0.10",
"compare",
"ctrlc",
"fnv",
@ -2980,7 +2965,7 @@ dependencies = [
name = "uu_split"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"uucore",
"uucore_procs",
]
@ -2989,7 +2974,7 @@ dependencies = [
name = "uu_stat"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"uucore",
"uucore_procs",
]
@ -2998,7 +2983,7 @@ dependencies = [
name = "uu_stdbuf"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"tempfile",
"uu_stdbuf_libstdbuf",
"uucore",
@ -3020,7 +3005,7 @@ dependencies = [
name = "uu_sum"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"uucore",
"uucore_procs",
]
@ -3029,7 +3014,7 @@ dependencies = [
name = "uu_sync"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"libc",
"uucore",
"uucore_procs",
@ -3040,7 +3025,7 @@ dependencies = [
name = "uu_tac"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"memchr 2.4.1",
"memmap2",
"regex",
@ -3052,7 +3037,7 @@ dependencies = [
name = "uu_tail"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"libc",
"nix 0.23.1",
"redox_syscall",
@ -3065,7 +3050,7 @@ dependencies = [
name = "uu_tee"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"libc",
"retain_mut",
"uucore",
@ -3076,7 +3061,7 @@ dependencies = [
name = "uu_test"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"libc",
"redox_syscall",
"uucore",
@ -3087,7 +3072,7 @@ dependencies = [
name = "uu_timeout"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"libc",
"nix 0.23.1",
"uucore",
@ -3098,7 +3083,7 @@ dependencies = [
name = "uu_touch"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"filetime",
"time",
"uucore",
@ -3109,9 +3094,8 @@ dependencies = [
name = "uu_tr"
version = "0.0.12"
dependencies = [
"bit-set",
"clap 3.0.9",
"fnv",
"clap 3.0.10",
"nom",
"uucore",
"uucore_procs",
]
@ -3120,7 +3104,7 @@ dependencies = [
name = "uu_true"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"uucore",
"uucore_procs",
]
@ -3129,7 +3113,7 @@ dependencies = [
name = "uu_truncate"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"uucore",
"uucore_procs",
]
@ -3138,7 +3122,7 @@ dependencies = [
name = "uu_tsort"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"uucore",
"uucore_procs",
]
@ -3148,7 +3132,7 @@ name = "uu_tty"
version = "0.0.12"
dependencies = [
"atty",
"clap 3.0.9",
"clap 3.0.10",
"libc",
"uucore",
"uucore_procs",
@ -3158,7 +3142,7 @@ dependencies = [
name = "uu_uname"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"platform-info",
"uucore",
"uucore_procs",
@ -3168,7 +3152,7 @@ dependencies = [
name = "uu_unexpand"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"unicode-width",
"uucore",
"uucore_procs",
@ -3178,7 +3162,7 @@ dependencies = [
name = "uu_uniq"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"strum",
"strum_macros",
"uucore",
@ -3189,7 +3173,7 @@ dependencies = [
name = "uu_unlink"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"uucore",
"uucore_procs",
]
@ -3199,7 +3183,7 @@ name = "uu_uptime"
version = "0.0.12"
dependencies = [
"chrono",
"clap 3.0.9",
"clap 3.0.10",
"uucore",
"uucore_procs",
]
@ -3208,7 +3192,7 @@ dependencies = [
name = "uu_users"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"uucore",
"uucore_procs",
]
@ -3218,7 +3202,7 @@ name = "uu_wc"
version = "0.0.12"
dependencies = [
"bytecount",
"clap 3.0.9",
"clap 3.0.10",
"libc",
"nix 0.23.1",
"unicode-width",
@ -3231,7 +3215,7 @@ dependencies = [
name = "uu_who"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"uucore",
"uucore_procs",
]
@ -3240,7 +3224,7 @@ dependencies = [
name = "uu_whoami"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"libc",
"uucore",
"uucore_procs",
@ -3251,7 +3235,7 @@ dependencies = [
name = "uu_yes"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"nix 0.23.1",
"uucore",
"uucore_procs",
@ -3261,7 +3245,7 @@ dependencies = [
name = "uucore"
version = "0.0.12"
dependencies = [
"clap 3.0.9",
"clap 3.0.10",
"data-encoding",
"data-encoding-macro",
"dns-lookup",

View file

@ -15,8 +15,7 @@ edition = "2018"
path = "src/tr.rs"
[dependencies]
bit-set = "0.5.0"
fnv = "1.0.5"
nom = "7.1.0"
clap = { version = "3.0", features = ["wrap_help", "cargo"] }
uucore = { version=">=0.0.11", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.8", package="uucore_procs", path="../../uucore_procs" }

37
src/uu/tr/src/convert.rs Normal file
View file

@ -0,0 +1,37 @@
// * This file is part of the uutils coreutils package.
// *
// * For the full copyright and license information, please view the LICENSE
// * file that was distributed with this source code.
// spell-checker:ignore (strings) anychar combinator
use nom::{
branch::alt,
bytes::complete::tag,
character::complete::{anychar, one_of},
combinator::{map_opt, recognize},
multi::{many0, many_m_n},
sequence::preceded,
IResult,
};
fn parse_octal(input: &str) -> IResult<&str, char> {
map_opt(
preceded(tag("\\"), recognize(many_m_n(1, 3, one_of("01234567")))),
|out: &str| {
u32::from_str_radix(out, 8)
.map(std::char::from_u32)
.ok()
.flatten()
},
)(input)
}
pub fn reduce_octal_to_char(input: String) -> String {
let result = many0(alt((parse_octal, anychar)))(input.as_str())
.map(|(_, r)| r)
.unwrap()
.into_iter()
.collect();
result
}

View file

@ -1,146 +0,0 @@
// * This file is part of the uutils coreutils package.
// *
// * (c) Michael Gehring <mg@ebfe.org>
// * (c) kwantam <kwantam@gmail.com>
// * * 2015-04-28 ~ created `expand` module to eliminate most allocs during setup
// *
// * For the full copyright and license information, please view the LICENSE
// * file that was distributed with this source code.
// spell-checker:ignore (ToDO) allocs slen unesc
use std::char::from_u32;
use std::cmp::min;
use std::iter::Peekable;
use std::ops::RangeInclusive;
/// Parse a backslash escape sequence to the corresponding character. Assumes
/// the string starts from the character _after_ the `\` and is not empty.
///
/// Returns a tuple containing the character and the number of characters
/// consumed from the input. The alphabetic escape sequences consume 1
/// character; octal escape sequences consume 1 to 3 octal digits.
#[inline]
fn parse_sequence(s: &str) -> (char, usize) {
let mut s = s.chars();
let c = s.next().expect("invalid escape: empty string");
if ('0'..='7').contains(&c) {
let mut v = c.to_digit(8).unwrap();
let mut consumed = 1;
let bits_per_digit = 3;
for c in s.take(2) {
match c.to_digit(8) {
Some(c) => {
v = (v << bits_per_digit) | c;
consumed += 1;
}
None => break,
}
}
(from_u32(v).expect("invalid octal escape"), consumed)
} else {
(
match c {
'a' => 0x07u8 as char,
'b' => 0x08u8 as char,
'f' => 0x0cu8 as char,
'v' => 0x0bu8 as char,
'n' => '\n',
'r' => '\r',
't' => '\t',
c => c,
},
1,
)
}
}
struct Unescape<'a> {
string: &'a str,
}
impl<'a> Iterator for Unescape<'a> {
type Item = char;
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let slen = self.string.len();
(min(slen, 1), None)
}
#[inline]
fn next(&mut self) -> Option<Self::Item> {
if self.string.is_empty() {
return None;
}
// is the next character an escape?
let (ret, idx) = match self.string.chars().next().unwrap() {
'\\' if self.string.len() > 1 => {
// yes---it's \ and it's not the last char in a string
// we know that \ is 1 byte long so we can index into the string safely
let (c, consumed) = parse_sequence(&self.string[1..]);
(Some(c), 1 + consumed)
}
c => (Some(c), c.len_utf8()), // not an escape char
};
self.string = &self.string[idx..]; // advance the pointer to the next char
ret
}
}
pub struct ExpandSet<'a> {
range: RangeInclusive<u32>,
unesc: Peekable<Unescape<'a>>,
}
impl<'a> Iterator for ExpandSet<'a> {
type Item = char;
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.unesc.size_hint()
}
#[inline]
fn next(&mut self) -> Option<Self::Item> {
// while the Range has elements, try to return chars from it
// but make sure that they actually turn out to be Chars!
for n in &mut self.range {
if let Some(c) = from_u32(n) {
return Some(c);
}
}
if let Some(first) = self.unesc.next() {
// peek ahead
if self.unesc.peek() == Some(&'-') && self.unesc.size_hint().0 > 1 {
self.unesc.next(); // this is the '-'
let last = self.unesc.next().unwrap(); // this is the end of the range
{
self.range = first as u32 + 1..=last as u32;
}
}
return Some(first); // in any case, return the next char
}
None
}
}
impl<'a> ExpandSet<'a> {
#[inline]
pub fn new(s: &'a str) -> ExpandSet<'a> {
ExpandSet {
range: 0..=0,
unesc: Unescape { string: s }.peekable(),
}
}
}

556
src/uu/tr/src/operation.rs Normal file
View file

@ -0,0 +1,556 @@
// * This file is part of the uutils coreutils package.
// *
// * For the full copyright and license information, please view the LICENSE
// * file that was distributed with this source code.
// spell-checker:ignore (strings) anychar combinator Alnum Punct Xdigit alnum punct xdigit cntrl
use nom::{
branch::alt,
bytes::complete::tag,
character::complete::{anychar, digit1},
combinator::{map, peek, value},
multi::many0,
sequence::{delimited, preceded, separated_pair},
IResult,
};
use std::{
collections::{HashMap, HashSet},
error::Error,
fmt::{Debug, Display},
io::{BufRead, Write},
};
use uucore::error::UError;
use crate::unicode_table;
#[derive(Debug, Clone)]
pub enum BadSequence {
MissingCharClassName,
MissingEquivalentClassChar,
MultipleCharRepeatInSet2,
CharRepeatInSet1,
InvalidRepeatCount(String),
EmptySet2WhenNotTruncatingSet1,
}
impl Display for BadSequence {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
BadSequence::MissingCharClassName => writeln!(f, "missing character class name '[::]'"),
BadSequence::MissingEquivalentClassChar => {
writeln!(f, "missing equivalence class character '[==]'")
}
BadSequence::MultipleCharRepeatInSet2 => {
writeln!(f, "only one [c*] repeat construct may appear in string2")
}
BadSequence::CharRepeatInSet1 => {
writeln!(f, "the [c*] repeat construct may not appear in string1")
}
BadSequence::InvalidRepeatCount(count) => {
writeln!(f, "invalid repeat count '{}' in [c*n] construct", count)
}
BadSequence::EmptySet2WhenNotTruncatingSet1 => {
writeln!(f, "when not truncating set1, string2 must be non-empty")
}
}
}
}
impl Error for BadSequence {}
impl UError for BadSequence {}
#[derive(Debug, Clone, Copy)]
pub enum Sequence {
Char(char),
CharRange(u32, u32),
CharStar(char),
CharRepeat(char, usize),
Alnum,
Alpha,
Blank,
Control,
Digit,
Graph,
Lower,
Print,
Punct,
Space,
Upper,
Xdigit,
}
impl Sequence {
pub fn flatten(&self) -> Box<dyn Iterator<Item = char>> {
match self {
Sequence::Char(c) => Box::new(std::iter::once(*c)),
Sequence::CharRange(l, r) => Box::new((*l..=*r).flat_map(std::char::from_u32)),
Sequence::CharStar(c) => Box::new(std::iter::repeat(*c)),
Sequence::CharRepeat(c, n) => Box::new(std::iter::repeat(*c).take(*n)),
Sequence::Alnum => Box::new(('0'..='9').chain('A'..='Z').chain('a'..='z')),
Sequence::Alpha => Box::new(('A'..='Z').chain('a'..='z')),
Sequence::Blank => Box::new(unicode_table::BLANK.iter().cloned()),
Sequence::Control => Box::new(
(0..=31)
.chain(std::iter::once(127))
.flat_map(std::char::from_u32),
),
Sequence::Digit => Box::new('0'..='9'),
Sequence::Graph => Box::new(
(48..=57) // digit
.chain(65..=90) // uppercase
.chain(97..=122) // lowercase
// punctuations
.chain(33..=47)
.chain(58..=64)
.chain(91..=96)
.chain(123..=126)
.chain(std::iter::once(32)) // space
.flat_map(std::char::from_u32),
),
Sequence::Lower => Box::new('a'..='z'),
Sequence::Print => Box::new(
(48..=57) // digit
.chain(65..=90) // uppercase
.chain(97..=122) // lowercase
// punctuations
.chain(33..=47)
.chain(58..=64)
.chain(91..=96)
.chain(123..=126)
.flat_map(std::char::from_u32),
),
Sequence::Punct => Box::new(
(33..=47)
.chain(58..=64)
.chain(91..=96)
.chain(123..=126)
.flat_map(std::char::from_u32),
),
Sequence::Space => Box::new(unicode_table::SPACES.iter().cloned()),
Sequence::Upper => Box::new('A'..='Z'),
Sequence::Xdigit => Box::new(('0'..='9').chain('A'..='F').chain('a'..='f')),
}
}
// Hide all the nasty sh*t in here
// TODO: Make the 2 set lazily generate the character mapping as necessary.
pub fn solve_set_characters(
set1_str: &str,
set2_str: &str,
truncate_set1_flag: bool,
) -> Result<(Vec<char>, Vec<char>), BadSequence> {
let set1 = Sequence::from_str(set1_str)?;
let set2 = Sequence::from_str(set2_str)?;
let is_char_star = |s: &&Sequence| -> bool { matches!(s, Sequence::CharStar(_)) };
let set1_star_count = set1.iter().filter(is_char_star).count();
if set1_star_count == 0 {
let set2_star_count = set2.iter().filter(is_char_star).count();
if set2_star_count < 2 {
let char_star = set2.iter().find_map(|s| match s {
Sequence::CharStar(c) => Some(c),
_ => None,
});
let mut partition = set2
.as_slice()
.split(|s| matches!(s, Sequence::CharStar(_)));
let set1_len = set1.iter().flat_map(Sequence::flatten).count();
let set2_len = set2
.iter()
.filter_map(|s| match s {
Sequence::CharStar(_) => None,
r => Some(r),
})
.flat_map(Sequence::flatten)
.count();
let star_compensate_len = set1_len.saturating_sub(set2_len);
let (left, right) = (partition.next(), partition.next());
let set2_solved: Vec<char> = match (left, right) {
(None, None) => match char_star {
Some(c) => std::iter::repeat(*c).take(star_compensate_len).collect(),
None => std::iter::empty().collect(),
},
(None, Some(set2_b)) => {
if let Some(c) = char_star {
std::iter::repeat(*c)
.take(star_compensate_len)
.chain(set2_b.iter().flat_map(Sequence::flatten))
.collect()
} else {
set2_b.iter().flat_map(Sequence::flatten).collect()
}
}
(Some(set2_a), None) => match char_star {
Some(c) => set2_a
.iter()
.flat_map(Sequence::flatten)
.chain(std::iter::repeat(*c).take(star_compensate_len))
.collect(),
None => set2_a.iter().flat_map(Sequence::flatten).collect(),
},
(Some(set2_a), Some(set2_b)) => match char_star {
Some(c) => set2_a
.iter()
.flat_map(Sequence::flatten)
.chain(std::iter::repeat(*c).take(star_compensate_len))
.chain(set2_b.iter().flat_map(Sequence::flatten))
.collect(),
None => set2_a
.iter()
.chain(set2_b.iter())
.flat_map(Sequence::flatten)
.collect(),
},
};
let mut set1_solved: Vec<char> = set1.iter().flat_map(Sequence::flatten).collect();
if truncate_set1_flag {
set1_solved.truncate(set2_solved.len());
}
Ok((set1_solved, set2_solved))
} else {
Err(BadSequence::MultipleCharRepeatInSet2)
}
} else {
Err(BadSequence::CharRepeatInSet1)
}
}
}
impl Sequence {
pub fn from_str(input: &str) -> Result<Vec<Sequence>, BadSequence> {
many0(alt((
Sequence::parse_char_range,
Sequence::parse_char_star,
Sequence::parse_char_repeat,
Sequence::parse_class,
Sequence::parse_char_equal,
// NOTE: This must be the last one
map(Sequence::parse_backslash_or_char, |s| Ok(Sequence::Char(s))),
)))(input)
.map(|(_, r)| r)
.unwrap()
.into_iter()
.collect::<Result<Vec<_>, _>>()
}
fn parse_backslash(input: &str) -> IResult<&str, char> {
preceded(tag("\\"), anychar)(input).map(|(l, a)| {
let c = match a {
'a' => unicode_table::BEL,
'b' => unicode_table::BS,
'f' => unicode_table::FF,
'n' => unicode_table::LF,
'r' => unicode_table::CR,
't' => unicode_table::HT,
'v' => unicode_table::VT,
x => x,
};
(l, c)
})
}
fn parse_backslash_or_char(input: &str) -> IResult<&str, char> {
alt((Sequence::parse_backslash, anychar))(input)
}
fn parse_char_range(input: &str) -> IResult<&str, Result<Sequence, BadSequence>> {
separated_pair(
Sequence::parse_backslash_or_char,
tag("-"),
Sequence::parse_backslash_or_char,
)(input)
.map(|(l, (a, b))| {
(l, {
let (start, end) = (u32::from(a), u32::from(b));
Ok(Sequence::CharRange(start, end))
})
})
}
fn parse_char_star(input: &str) -> IResult<&str, Result<Sequence, BadSequence>> {
delimited(tag("["), Sequence::parse_backslash_or_char, tag("*]"))(input)
.map(|(l, a)| (l, Ok(Sequence::CharStar(a))))
}
fn parse_char_repeat(input: &str) -> IResult<&str, Result<Sequence, BadSequence>> {
delimited(
tag("["),
separated_pair(Sequence::parse_backslash_or_char, tag("*"), digit1),
tag("]"),
)(input)
.map(|(l, (c, str))| {
(
l,
match usize::from_str_radix(str, 8) {
Ok(0) => Ok(Sequence::CharStar(c)),
Ok(count) => Ok(Sequence::CharRepeat(c, count)),
Err(_) => Err(BadSequence::InvalidRepeatCount(str.to_string())),
},
)
})
}
fn parse_class(input: &str) -> IResult<&str, Result<Sequence, BadSequence>> {
delimited(
tag("[:"),
alt((
map(
alt((
value(Sequence::Alnum, tag("alnum")),
value(Sequence::Alpha, tag("alpha")),
value(Sequence::Blank, tag("blank")),
value(Sequence::Control, tag("cntrl")),
value(Sequence::Digit, tag("digit")),
value(Sequence::Graph, tag("graph")),
value(Sequence::Lower, tag("lower")),
value(Sequence::Print, tag("print")),
value(Sequence::Punct, tag("punct")),
value(Sequence::Space, tag("space")),
value(Sequence::Upper, tag("upper")),
value(Sequence::Xdigit, tag("xdigit")),
)),
Ok,
),
value(Err(BadSequence::MissingCharClassName), tag("")),
)),
tag(":]"),
)(input)
}
fn parse_char_equal(input: &str) -> IResult<&str, Result<Sequence, BadSequence>> {
delimited(
tag("[="),
alt((
value(
Err(BadSequence::MissingEquivalentClassChar),
peek(tag("=]")),
),
map(Sequence::parse_backslash_or_char, |c| Ok(Sequence::Char(c))),
)),
tag("=]"),
)(input)
}
}
pub trait SymbolTranslator {
fn translate(&mut self, current: char) -> Option<char>;
}
#[derive(Debug)]
pub struct DeleteOperation {
set: Vec<char>,
complement_flag: bool,
}
impl DeleteOperation {
pub fn new(set: Vec<char>, complement_flag: bool) -> DeleteOperation {
DeleteOperation {
set,
complement_flag,
}
}
}
impl SymbolTranslator for DeleteOperation {
fn translate(&mut self, current: char) -> Option<char> {
let found = self.set.iter().any(|sequence| sequence.eq(&current));
if self.complement_flag == found {
Some(current)
} else {
None
}
}
}
pub struct TranslateOperationComplement {
iter: u32,
set2_iter: usize,
set1: Vec<char>,
set2: Vec<char>,
translation_map: HashMap<char, char>,
}
impl TranslateOperationComplement {
fn new(set1: Vec<char>, set2: Vec<char>) -> TranslateOperationComplement {
TranslateOperationComplement {
iter: 0,
set2_iter: 0,
set1,
set2,
translation_map: HashMap::new(),
}
}
}
#[derive(Debug)]
pub struct TranslateOperationStandard {
translation_map: HashMap<char, char>,
}
impl TranslateOperationStandard {
fn new(set1: Vec<char>, set2: Vec<char>) -> Result<TranslateOperationStandard, BadSequence> {
if let Some(fallback) = set2.last().copied() {
Ok(TranslateOperationStandard {
translation_map: set1
.into_iter()
.zip(set2.into_iter().chain(std::iter::repeat(fallback)))
.collect::<HashMap<_, _>>(),
})
} else if set1.is_empty() && set2.is_empty() {
Ok(TranslateOperationStandard {
translation_map: HashMap::new(),
})
} else {
Err(BadSequence::EmptySet2WhenNotTruncatingSet1)
}
}
}
pub enum TranslateOperation {
Standard(TranslateOperationStandard),
Complement(TranslateOperationComplement),
}
impl TranslateOperation {
fn next_complement_char(iter: u32, ignore_list: &[char]) -> (u32, char) {
(iter..)
.filter_map(std::char::from_u32)
.filter(|c| !ignore_list.iter().any(|s| s.eq(c)))
.map(|c| (u32::from(c) + 1, c))
.next()
.expect("exhausted all possible characters")
}
}
impl TranslateOperation {
pub fn new(
set1: Vec<char>,
set2: Vec<char>,
complement: bool,
) -> Result<TranslateOperation, BadSequence> {
if complement {
Ok(TranslateOperation::Complement(
TranslateOperationComplement::new(set1, set2),
))
} else {
Ok(TranslateOperation::Standard(
TranslateOperationStandard::new(set1, set2)?,
))
}
}
}
impl SymbolTranslator for TranslateOperation {
fn translate(&mut self, current: char) -> Option<char> {
match self {
TranslateOperation::Standard(TranslateOperationStandard { translation_map }) => Some(
translation_map
.iter()
.find_map(|(l, r)| if l.eq(&current) { Some(*r) } else { None })
.unwrap_or(current),
),
TranslateOperation::Complement(TranslateOperationComplement {
iter,
set2_iter,
set1,
set2,
translation_map,
}) => {
// First, try to see if current char is already mapped
// If so, return the mapped char
// Else, pop from set2
// If we popped something, map the next complement character to this value
// If set2 is empty, we just map the current char directly to fallback --- to avoid looping unnecessarily
if let Some(c) = set1.iter().find(|c| c.eq(&&current)) {
Some(*c)
} else {
while translation_map.get(&current).is_none() {
if let Some(value) = set2.get(*set2_iter) {
let (next_iter, next_key) =
TranslateOperation::next_complement_char(*iter, &*set1);
*iter = next_iter;
*set2_iter = set2_iter.saturating_add(1);
translation_map.insert(next_key, *value);
} else {
translation_map.insert(current, *set2.last().unwrap());
}
}
Some(*translation_map.get(&current).unwrap())
}
}
}
}
}
#[derive(Debug, Clone)]
pub struct SqueezeOperation {
set1: HashSet<char>,
complement: bool,
previous: Option<char>,
}
impl SqueezeOperation {
pub fn new(set1: Vec<char>, complement: bool) -> SqueezeOperation {
SqueezeOperation {
set1: set1.into_iter().collect(),
complement,
previous: None,
}
}
}
impl SymbolTranslator for SqueezeOperation {
fn translate(&mut self, current: char) -> Option<char> {
if self.complement {
let next = if self.set1.contains(&current) {
Some(current)
} else {
match self.previous {
Some(v) => {
if v.eq(&current) {
None
} else {
Some(current)
}
}
None => Some(current),
}
};
self.previous = Some(current);
next
} else {
let next = if self.set1.contains(&current) {
match self.previous {
Some(v) if v == current => None,
_ => Some(current),
}
} else {
Some(current)
};
self.previous = Some(current);
next
}
}
}
pub fn translate_input<T, R, W>(input: &mut R, output: &mut W, mut translator: T)
where
T: SymbolTranslator,
R: BufRead,
W: Write,
{
let mut buf = String::new();
let mut output_buf = String::new();
while let Ok(length) = input.read_line(&mut buf) {
if length == 0 {
break;
} else {
let filtered = buf.chars().filter_map(|c| translator.translate(c));
output_buf.extend(filtered);
output.write_all(output_buf.as_bytes()).unwrap();
}
buf.clear();
output_buf.clear();
}
}

View file

@ -1,238 +1,43 @@
// * This file is part of the uutils coreutils package.
// *
// * (c) Michael Gehring <mg@ebfe.org>
// * (c) kwantam <kwantam@gmail.com>
// * * 2015-04-28 ~ created `expand` module to eliminate most allocs during setup
// * (c) Sergey "Shnatsel" Davidoff <shnatsel@gmail.com>
// *
// * For the full copyright and license information, please view the LICENSE
// * file that was distributed with this source code.
// spell-checker:ignore (ToDO) allocs bset dflag cflag sflag tflag
mod expand;
extern crate nom;
mod convert;
mod operation;
mod unicode_table;
use bit_set::BitSet;
use clap::{crate_version, App, Arg};
use fnv::FnvHashMap;
use std::io::{stdin, stdout, BufRead, BufWriter, Write};
use nom::AsBytes;
use operation::{translate_input, Sequence, SqueezeOperation, TranslateOperation};
use std::io::{stdin, stdout, BufReader, BufWriter};
use uucore::show;
use crate::expand::ExpandSet;
use uucore::error::{UResult, UUsageError};
use crate::operation::DeleteOperation;
use uucore::error::{UResult, USimpleError, UUsageError};
use uucore::{display::Quotable, InvalidEncodingHandling};
static ABOUT: &str = "translate or delete characters";
const BUFFER_LEN: usize = 1024;
mod options {
pub const COMPLEMENT: &str = "complement";
pub const DELETE: &str = "delete";
pub const SQUEEZE: &str = "squeeze-repeats";
pub const TRUNCATE: &str = "truncate";
pub const TRUNCATE_SET1: &str = "truncate-set1";
pub const SETS: &str = "sets";
}
trait SymbolTranslator {
fn translate(&self, c: char, prev_c: char) -> Option<char>;
}
struct DeleteOperation {
bset: BitSet,
complement: bool,
}
impl DeleteOperation {
fn new(set: ExpandSet, complement: bool) -> DeleteOperation {
DeleteOperation {
bset: set.map(|c| c as usize).collect(),
complement,
}
}
}
impl SymbolTranslator for DeleteOperation {
fn translate(&self, c: char, _prev_c: char) -> Option<char> {
let uc = c as usize;
if self.complement == self.bset.contains(uc) {
Some(c)
} else {
None
}
}
}
struct SqueezeOperation {
squeeze_set: BitSet,
complement: bool,
}
impl SqueezeOperation {
fn new(squeeze_set: ExpandSet, complement: bool) -> SqueezeOperation {
SqueezeOperation {
squeeze_set: squeeze_set.map(|c| c as usize).collect(),
complement,
}
}
}
impl SymbolTranslator for SqueezeOperation {
fn translate(&self, c: char, prev_c: char) -> Option<char> {
if prev_c == c && self.complement != self.squeeze_set.contains(c as usize) {
None
} else {
Some(c)
}
}
}
struct DeleteAndSqueezeOperation {
delete_set: BitSet,
squeeze_set: BitSet,
complement: bool,
}
impl DeleteAndSqueezeOperation {
fn new(
delete_set: ExpandSet,
squeeze_set: ExpandSet,
complement: bool,
) -> DeleteAndSqueezeOperation {
DeleteAndSqueezeOperation {
delete_set: delete_set.map(|c| c as usize).collect(),
squeeze_set: squeeze_set.map(|c| c as usize).collect(),
complement,
}
}
}
impl SymbolTranslator for DeleteAndSqueezeOperation {
fn translate(&self, c: char, prev_c: char) -> Option<char> {
if self.complement != self.delete_set.contains(c as usize)
|| prev_c == c && self.squeeze_set.contains(c as usize)
{
None
} else {
Some(c)
}
}
}
struct TranslateOperation {
translate_map: FnvHashMap<usize, char>,
complement: bool,
s2_last: char,
}
impl TranslateOperation {
fn new(
set1: ExpandSet,
set2: &mut ExpandSet,
truncate: bool,
complement: bool,
) -> TranslateOperation {
let mut map = FnvHashMap::default();
let mut s2_prev = '_';
for i in set1 {
let s2_next = set2.next();
if s2_next.is_none() && truncate {
map.insert(i as usize, i);
} else {
s2_prev = s2_next.unwrap_or(s2_prev);
map.insert(i as usize, s2_prev);
}
}
TranslateOperation {
translate_map: map,
complement,
s2_last: set2.last().unwrap_or(s2_prev),
}
}
}
impl SymbolTranslator for TranslateOperation {
fn translate(&self, c: char, _prev_c: char) -> Option<char> {
if self.complement {
Some(if self.translate_map.contains_key(&(c as usize)) {
c
} else {
self.s2_last
})
} else {
Some(*self.translate_map.get(&(c as usize)).unwrap_or(&c))
}
}
}
struct TranslateAndSqueezeOperation {
translate: TranslateOperation,
squeeze: SqueezeOperation,
}
impl TranslateAndSqueezeOperation {
fn new(sets: Vec<String>, truncate: bool, complement: bool) -> TranslateAndSqueezeOperation {
let set1 = ExpandSet::new(sets[0].as_ref());
let set1_ = ExpandSet::new(sets[0].as_ref());
let mut set2 = ExpandSet::new(sets[1].as_ref());
let set2_ = ExpandSet::new(sets[1].as_ref());
TranslateAndSqueezeOperation {
translate: TranslateOperation::new(set1, &mut set2, truncate, complement),
squeeze: SqueezeOperation::new(if complement { set1_ } else { set2_ }, complement),
}
}
}
impl SymbolTranslator for TranslateAndSqueezeOperation {
fn translate(&self, c: char, prev_c: char) -> Option<char> {
// `unwrap()` will never panic because `Translate.translate()`
// always returns `Some`.
self.squeeze
.translate(self.translate.translate(c, 0 as char).unwrap(), prev_c)
}
}
fn translate_input<T: SymbolTranslator>(
input: &mut dyn BufRead,
output: &mut dyn Write,
translator: T,
) {
let mut buf = String::with_capacity(BUFFER_LEN + 4);
let mut output_buf = String::with_capacity(BUFFER_LEN + 4);
while let Ok(length) = input.read_line(&mut buf) {
let mut prev_c = 0 as char;
if length == 0 {
break;
}
{
// isolation to make borrow checker happy
let filtered = buf.chars().filter_map(|c| {
let res = translator.translate(c, prev_c);
// Set `prev_c` to the post-translate character. This
// allows the squeeze operation to correctly function
// after the translate operation.
if let Some(rc) = res {
prev_c = rc;
}
res
});
output_buf.extend(filtered);
output.write_all(output_buf.as_bytes()).unwrap();
}
buf.clear();
output_buf.clear();
}
}
fn usage() -> String {
fn get_usage() -> String {
format!("{} [OPTION]... SET1 [SET2]", uucore::execution_phrase())
}
fn get_long_usage() -> String {
"Translate, squeeze, and/or delete characters from standard input,
writing to standard output."
"Translate, squeeze, and/or delete characters from standard input, \
writing to standard output."
.to_string()
}
@ -242,7 +47,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
.collect_str(InvalidEncodingHandling::ConvertLossy)
.accept_any();
let usage = usage();
let usage = get_usage();
let after_help = get_long_usage();
let matches = uu_app()
@ -253,51 +58,87 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let delete_flag = matches.is_present(options::DELETE);
let complement_flag = matches.is_present(options::COMPLEMENT) || matches.is_present("C");
let squeeze_flag = matches.is_present(options::SQUEEZE);
let truncate_flag = matches.is_present(options::TRUNCATE);
let truncate_set1_flag = matches.is_present(options::TRUNCATE_SET1);
let sets = matches
.values_of(options::SETS)
.map(|v| v.map(ToString::to_string).collect::<Vec<_>>())
.map(|v| {
v.map(ToString::to_string)
.map(convert::reduce_octal_to_char)
.collect::<Vec<_>>()
})
.unwrap_or_default();
let sets_len = sets.len();
if sets.is_empty() {
return Err(UUsageError::new(1, "missing operand"));
}
if !(delete_flag || squeeze_flag) && sets.len() < 2 {
if !(delete_flag || squeeze_flag) && sets_len < 2 {
return Err(UUsageError::new(
1,
format!("missing operand after {}", sets[0].quote()),
));
}
if let Some(first) = sets.get(0) {
if first.ends_with('\\') {
show!(USimpleError::new(
0,
"warning: an unescaped backslash at end of string is not portable"
));
}
}
let stdin = stdin();
let mut locked_stdin = stdin.lock();
let stdout = stdout();
let locked_stdout = stdout.lock();
let mut buffered_stdout = BufWriter::new(locked_stdout);
let set1 = ExpandSet::new(sets[0].as_ref());
let mut sets_iter = sets.iter().map(|c| c.as_str());
let (set1, set2) = Sequence::solve_set_characters(
sets_iter.next().unwrap_or_default(),
sets_iter.next().unwrap_or_default(),
truncate_set1_flag,
)?;
if delete_flag {
if squeeze_flag {
let set2 = ExpandSet::new(sets[1].as_ref());
let op = DeleteAndSqueezeOperation::new(set1, set2, complement_flag);
translate_input(&mut locked_stdin, &mut buffered_stdout, op);
let mut delete_buffer = vec![];
{
let mut delete_writer = BufWriter::new(&mut delete_buffer);
let delete_op = DeleteOperation::new(set1, complement_flag);
translate_input(&mut locked_stdin, &mut delete_writer, delete_op);
}
{
let mut squeeze_reader = BufReader::new(delete_buffer.as_bytes());
let op = SqueezeOperation::new(set2, complement_flag);
translate_input(&mut squeeze_reader, &mut buffered_stdout, op);
}
} else {
let op = DeleteOperation::new(set1, complement_flag);
translate_input(&mut locked_stdin, &mut buffered_stdout, op);
}
} else if squeeze_flag {
if sets.len() < 2 {
if sets_len < 2 {
let op = SqueezeOperation::new(set1, complement_flag);
translate_input(&mut locked_stdin, &mut buffered_stdout, op);
} else {
let op = TranslateAndSqueezeOperation::new(sets, truncate_flag, complement_flag);
translate_input(&mut locked_stdin, &mut buffered_stdout, op);
let mut translate_buffer = vec![];
{
let mut writer = BufWriter::new(&mut translate_buffer);
let op = TranslateOperation::new(set1, set2.clone(), complement_flag)?;
translate_input(&mut locked_stdin, &mut writer, op);
}
{
let mut reader = BufReader::new(translate_buffer.as_bytes());
let squeeze_op = SqueezeOperation::new(set2, false);
translate_input(&mut reader, &mut buffered_stdout, squeeze_op);
}
}
} else {
let mut set2 = ExpandSet::new(sets[1].as_ref());
let op = TranslateOperation::new(set1, &mut set2, truncate_flag, complement_flag);
let op = TranslateOperation::new(set1, set2, complement_flag)?;
translate_input(&mut locked_stdin, &mut buffered_stdout, op);
}
Ok(())
@ -330,14 +171,14 @@ pub fn uu_app<'a>() -> App<'a> {
.long(options::SQUEEZE)
.short('s')
.help(
"replace each sequence of a repeated character that is
listed in the last specified SET, with a single occurrence
of that character",
"replace each sequence of a repeated character that is \
listed in the last specified SET, with a single occurrence \
of that character",
),
)
.arg(
Arg::new(options::TRUNCATE)
.long(options::TRUNCATE)
Arg::new(options::TRUNCATE_SET1)
.long(options::TRUNCATE_SET1)
.short('t')
.help("first truncate SET1 to length of SET2"),
)

View file

@ -0,0 +1,15 @@
// * This file is part of the uutils coreutils package.
// *
// * For the full copyright and license information, please view the LICENSE
// * file that was distributed with this source code.
pub static BEL: char = '\u{0007}';
pub static BS: char = '\u{0008}';
pub static HT: char = '\u{0009}';
pub static LF: char = '\u{000A}';
pub static VT: char = '\u{000B}';
pub static FF: char = '\u{000C}';
pub static CR: char = '\u{000D}';
pub static SPACE: char = '\u{0020}';
pub static SPACES: &[char] = &[HT, LF, VT, FF, CR, SPACE];
pub static BLANK: &[char] = &[SPACE, HT];

View file

@ -1,3 +1,4 @@
// spell-checker:ignore aabbaa aabbcc aabc abbb abcc abcdefabcdef abcdefghijk abcdefghijklmn abcdefghijklmnop ABCDEFGHIJKLMNOPQRS abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ ABCDEFZZ abcxyz ABCXYZ abcxyzabcxyz ABCXYZABCXYZ acbdef alnum amzamz AMZXAMZ bbbd cclass cefgm cntrl compl dabcdef dncase Gzabcdefg PQRST upcase wxyzz xdigit xycde xyyye xyyz xyzzzzxyzzzz ZABCDEF Zamz
use crate::common::util::*;
#[test]
@ -98,12 +99,11 @@ fn test_complement4() {
}
#[test]
#[ignore = "fixme: GNU tr returns '0a1b2c3' instead of '0~1~2~3', see #2158"]
fn test_complement5() {
// $ echo '0x1y2z3' | tr -c '\0-@' '*-~'
// $ echo -n '0x1y2z3' | tr -c '\0-@' '*-~'
// 0a1b2c3
new_ucmd!()
.args(&["-c", "\\0-@", "*-~"])
.args(&["-c", r"\0-@", "*-~"])
.pipe_in("0x1y2z3")
.run()
.stdout_is("0a1b2c3");
@ -192,6 +192,7 @@ fn test_set1_shorter_than_set2() {
#[test]
fn test_truncate() {
// echo -n "abcde" | tr -t "abc" "xy"
new_ucmd!()
.args(&["-t", "abc", "xy"])
.pipe_in("abcde")
@ -289,3 +290,812 @@ fn test_interpret_backslash_at_eol_literally() {
fn test_more_than_2_sets() {
new_ucmd!().args(&["'abcdef'", "'a'", "'b'"]).fails();
}
#[test]
fn basic_translation_works() {
// echo -n "abcdefabcdef" | tr "dabcdef" "xyz"
new_ucmd!()
.args(&["abcdef", "xyz"])
.pipe_in("abcdefabcdef")
.succeeds()
.stdout_is("xyzzzzxyzzzz");
}
#[test]
fn alnum_overrides_translation_to_fallback_1() {
// echo -n "abcdefghijklmnopqrstuvwxyz" | tr "abc[:alpha:]" "xyz"
new_ucmd!()
.args(&["abc[:alpha:]", "xyz"])
.pipe_in("abcdefghijklmnopqrstuvwxyz")
.succeeds()
.stdout_is("zzzzzzzzzzzzzzzzzzzzzzzzzz");
}
#[test]
fn alnum_overrides_translation_to_fallback_2() {
// echo -n "abcdefghijklmnopqrstuvwxyz" | tr "[:alpha:]abc" "xyz"
new_ucmd!()
.args(&["[:alpha:]abc", "xyz"])
.pipe_in("abcdefghijklmnopqrstuvwxyz")
.succeeds()
.stdout_is("zzzzzzzzzzzzzzzzzzzzzzzzzz");
}
#[test]
fn overrides_translation_pair_if_repeats() {
// echo -n 'aaa' | tr "aaa" "xyz"
new_ucmd!()
.args(&["aaa", "xyz"])
.pipe_in("aaa")
.succeeds()
.stdout_is("zzz");
}
#[test]
fn uppercase_conversion_works_1() {
// echo -n 'abcdefghijklmnopqrstuvwxyz' | tr "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
new_ucmd!()
.args(&["abcdefghijklmnopqrstuvwxyz", "ABCDEFGHIJKLMNOPQRSTUVWXYZ"])
.pipe_in("abcdefghijklmnopqrstuvwxyz")
.succeeds()
.stdout_is("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
}
#[test]
fn uppercase_conversion_works_2() {
// echo -n 'abcdefghijklmnopqrstuvwxyz' | tr "a-z" "A-Z"
new_ucmd!()
.args(&["a-z", "A-Z"])
.pipe_in("abcdefghijklmnopqrstuvwxyz")
.succeeds()
.stdout_is("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
}
#[test]
fn uppercase_conversion_works_3() {
// echo -n 'abcdefghijklmnopqrstuvwxyz' | tr "[:lower:]" "[:upper:]"
new_ucmd!()
.args(&["[:lower:]", "[:upper:]"])
.pipe_in("abcdefghijklmnopqrstuvwxyz")
.succeeds()
.stdout_is("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
}
#[test]
fn translate_complement_set_in_order() {
// echo -n '01234' | tr -c '@-~' ' -^'
new_ucmd!()
.args(&["-c", "@-~", " -^"])
.pipe_in("01234")
.succeeds()
.stdout_is("PQRST");
}
#[test]
fn alpha_expands_uppercase_lowercase() {
// echo -n "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" | tr "[:alpha:]" " -_"
new_ucmd!()
.args(&["[:alpha:]", " -_"])
.pipe_in("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz")
.succeeds()
.stdout_is(r##" !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRS"##);
}
#[test]
fn alnum_expands_number_uppercase_lowercase() {
// echo -n "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" | tr "[:alnum:]" " -_"
new_ucmd!()
.args(&["[:alnum:]", " -_"])
.pipe_in("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz")
.succeeds()
.stdout_is(r##" !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]"##);
}
#[test]
fn check_against_gnu_tr_tests() {
// ['1', qw(abcd '[]*]'), {IN=>'abcd'}, {OUT=>']]]]'}],
new_ucmd!()
.args(&["abcd", "[]*]"])
.pipe_in("abcd")
.succeeds()
.stdout_is("]]]]");
}
#[test]
fn check_against_gnu_tr_tests_2() {
// ['2', qw(abc '[%*]xyz'), {IN=>'abc'}, {OUT=>'xyz'}],
new_ucmd!()
.args(&["abc", "[%*]xyz"])
.pipe_in("abc")
.succeeds()
.stdout_is("xyz");
}
#[test]
fn check_against_gnu_tr_tests_3() {
// ['3', qw('' '[.*]'), {IN=>'abc'}, {OUT=>'abc'}],
new_ucmd!()
.args(&["", "[.*]"])
.pipe_in("abc")
.succeeds()
.stdout_is("abc");
}
#[test]
fn check_against_gnu_tr_tests_4() {
// # Test --truncate-set1 behavior when string1 is longer than string2
// ['4', qw(-t abcd xy), {IN=>'abcde'}, {OUT=>'xycde'}],
new_ucmd!()
.args(&["-t", "abcd", "xy"])
.pipe_in("abcde")
.succeeds()
.stdout_is("xycde");
}
#[test]
fn check_against_gnu_tr_tests_5() {
// # Test bsd behavior (the default) when string1 is longer than string2
// ['5', qw(abcd xy), {IN=>'abcde'}, {OUT=>'xyyye'}],
new_ucmd!()
.args(&["abcd", "xy"])
.pipe_in("abcde")
.succeeds()
.stdout_is("xyyye");
}
#[test]
fn check_against_gnu_tr_tests_6() {
// # Do it the posix way
// ['6', qw(abcd 'x[y*]'), {IN=>'abcde'}, {OUT=>'xyyye'}],
new_ucmd!()
.args(&["abcd", "x[y*]"])
.pipe_in("abcde")
.succeeds()
.stdout_is("xyyye");
}
#[test]
fn check_against_gnu_tr_tests_7() {
// ['7', qw(-s a-p '%[.*]$'), {IN=>'abcdefghijklmnop'}, {OUT=>'%.$'}],
new_ucmd!()
.args(&["-s", "a-p", "%[.*]$"])
.pipe_in("abcdefghijklmnop")
.succeeds()
.stdout_is("%.$");
}
#[test]
fn check_against_gnu_tr_tests_8() {
// ['8', qw(-s a-p '[.*]$'), {IN=>'abcdefghijklmnop'}, {OUT=>'.$'}],
new_ucmd!()
.args(&["-s", "a-p", "[.*]$"])
.pipe_in("abcdefghijklmnop")
.succeeds()
.stdout_is(".$");
}
#[test]
fn check_against_gnu_tr_tests_9() {
// ['9', qw(-s a-p '%[.*]'), {IN=>'abcdefghijklmnop'}, {OUT=>'%.'}],
new_ucmd!()
.args(&["-s", "a-p", "%[.*]"])
.pipe_in("abcdefghijklmnop")
.succeeds()
.stdout_is("%.");
}
#[test]
fn check_against_gnu_tr_tests_a() {
// ['a', qw(-s '[a-z]'), {IN=>'aabbcc'}, {OUT=>'abc'}],
new_ucmd!()
.args(&["-s", "[a-z]"])
.pipe_in("aabbcc")
.succeeds()
.stdout_is("abc");
}
#[test]
fn check_against_gnu_tr_tests_b() {
// ['b', qw(-s '[a-c]'), {IN=>'aabbcc'}, {OUT=>'abc'}],
new_ucmd!()
.args(&["-s", "[a-c]"])
.pipe_in("aabbcc")
.succeeds()
.stdout_is("abc");
}
#[test]
fn check_against_gnu_tr_tests_c() {
// ['c', qw(-s '[a-b]'), {IN=>'aabbcc'}, {OUT=>'abcc'}],
new_ucmd!()
.args(&["-s", "[a-b]"])
.pipe_in("aabbcc")
.succeeds()
.stdout_is("abcc");
}
#[test]
fn check_against_gnu_tr_tests_d() {
// ['d', qw(-s '[b-c]'), {IN=>'aabbcc'}, {OUT=>'aabc'}],
new_ucmd!()
.args(&["-s", "[b-c]"])
.pipe_in("aabbcc")
.succeeds()
.stdout_is("aabc");
}
#[test]
#[ignore = "I cannot tell if this means that tr preserve the octal representation?"]
fn check_against_gnu_tr_tests_e() {
// ['e', qw(-s '[\0-\5]'), {IN=>"\0\0a\1\1b\2\2\2c\3\3\3d\4\4\4\4e\5\5"}, {OUT=>"\0a\1b\2c\3d\4e\5"}],
new_ucmd!()
.args(&["-s", r"[\0-\5]"])
.pipe_in(
"\u{0}\u{0}a\u{1}\u{1}b\u{2}\u{2}\u{2}c\u{3}\u{3}\u{3}d\u{4}\u{4}\u{4}\u{4}e\u{5}\u{5}",
)
.succeeds()
.stdout_is("\u{0}a\u{1}b\u{2}c\u{3}d\u{4}e\u{5}");
}
#[test]
fn check_against_gnu_tr_tests_f() {
// # tests of delete
// ['f', qw(-d '[=[=]'), {IN=>'[[[[[[[]]]]]]]]'}, {OUT=>']]]]]]]]'}],
new_ucmd!()
.args(&["-d", "[=[=]"])
.pipe_in("[[[[[[[]]]]]]]]")
.succeeds()
.stdout_is("]]]]]]]]");
}
#[test]
fn check_against_gnu_tr_tests_g() {
// ['g', qw(-d '[=]=]'), {IN=>'[[[[[[[]]]]]]]]'}, {OUT=>'[[[[[[['}],
new_ucmd!()
.args(&["-d", "[=]=]"])
.pipe_in("[[[[[[[]]]]]]]]")
.succeeds()
.stdout_is("[[[[[[[");
}
#[test]
fn check_against_gnu_tr_tests_h() {
// ['h', qw(-d '[:xdigit:]'), {IN=>'0123456789acbdefABCDEF'}, {OUT=>''}],
new_ucmd!()
.args(&["-d", "[:xdigit:]"])
.pipe_in("0123456789acbdefABCDEF")
.succeeds()
.stdout_is("");
}
#[test]
fn check_against_gnu_tr_tests_i() {
// ['i', qw(-d '[:xdigit:]'), {IN=>'w0x1y2z3456789acbdefABCDEFz'}, {OUT=>'wxyzz'}],
new_ucmd!()
.args(&["-d", "[:xdigit:]"])
.pipe_in("w0x1y2z3456789acbdefABCDEFz")
.succeeds()
.stdout_is("wxyzz");
}
#[test]
fn check_against_gnu_tr_tests_j() {
// ['j', qw(-d '[:digit:]'), {IN=>'0123456789'}, {OUT=>''}],
new_ucmd!()
.args(&["-d", "[:digit:]"])
.pipe_in("0123456789")
.succeeds()
.stdout_is("");
}
#[test]
fn check_against_gnu_tr_tests_k() {
// ['k', qw(-d '[:digit:]'), {IN=>'a0b1c2d3e4f5g6h7i8j9k'}, {OUT=>'abcdefghijk'}],
new_ucmd!()
.args(&["-d", "[:digit:]"])
.pipe_in("a0b1c2d3e4f5g6h7i8j9k")
.succeeds()
.stdout_is("abcdefghijk");
}
#[test]
fn check_against_gnu_tr_tests_l() {
// ['l', qw(-d '[:lower:]'), {IN=>'abcdefghijklmnopqrstuvwxyz'}, {OUT=>''}],
new_ucmd!()
.args(&["-d", "[:lower:]"])
.pipe_in("abcdefghijklmnopqrstuvwxyz")
.succeeds()
.stdout_is("");
}
#[test]
fn check_against_gnu_tr_tests_m() {
// ['m', qw(-d '[:upper:]'), {IN=>'ABCDEFGHIJKLMNOPQRSTUVWXYZ'}, {OUT=>''}],
new_ucmd!()
.args(&["-d", "[:upper:]"])
.pipe_in("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
.succeeds()
.stdout_is("");
}
#[test]
fn check_against_gnu_tr_tests_n() {
// ['n', qw(-d '[:lower:][:upper:]'), {IN=>'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'}, {OUT=>''}],
new_ucmd!()
.args(&["-d", "[:lower:][:upper:]"])
.pipe_in("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
.succeeds()
.stdout_is("");
}
#[test]
fn check_against_gnu_tr_tests_o() {
// ['o', qw(-d '[:alpha:]'), {IN=>'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'}, {OUT=>''}],
new_ucmd!()
.args(&["-d", "[:alpha:]"])
.pipe_in("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
.succeeds()
.stdout_is("");
}
#[test]
fn check_against_gnu_tr_tests_p() {
// ['p', qw(-d '[:alnum:]'), {IN=>'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'}, {OUT=>''}],
new_ucmd!()
.args(&["-d", "[:alnum:]"])
.pipe_in("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
.succeeds()
.stdout_is("");
}
#[test]
fn check_against_gnu_tr_tests_q() {
// ['q', qw(-d '[:alnum:]'), {IN=>'.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.'}, {OUT=>'..'}],
new_ucmd!()
.args(&["-d", "[:alnum:]"])
.pipe_in(".abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.")
.succeeds()
.stdout_is("..");
}
#[test]
fn check_against_gnu_tr_tests_r() {
// ['r', qw(-ds '[:alnum:]' .),
// {IN=>'.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.'},
// {OUT=>'.'}],
new_ucmd!()
.args(&["-ds", "[:alnum:]", "."])
.pipe_in(".abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.")
.succeeds()
.stdout_is(".");
}
#[test]
fn check_against_gnu_tr_tests_s() {
// # The classic example, with string2 BSD-style
// ['s', qw(-cs '[:alnum:]' '\n'),
// {IN=>'The big black fox jumped over the fence.'},
// {OUT=>"The\nbig\nblack\nfox\njumped\nover\nthe\nfence\n"}],
new_ucmd!()
.args(&["-cs", "[:alnum:]", "\n"])
.pipe_in("The big black fox jumped over the fence.")
.succeeds()
.stdout_is("The\nbig\nblack\nfox\njumped\nover\nthe\nfence\n");
}
#[test]
fn check_against_gnu_tr_tests_t() {
// # The classic example, POSIX-style
// ['t', qw(-cs '[:alnum:]' '[\n*]'),
// {IN=>'The big black fox jumped over the fence.'},
// {OUT=>"The\nbig\nblack\nfox\njumped\nover\nthe\nfence\n"}],
new_ucmd!()
.args(&["-cs", "[:alnum:]", "[\n*]"])
.pipe_in("The big black fox jumped over the fence.")
.succeeds()
.stdout_is("The\nbig\nblack\nfox\njumped\nover\nthe\nfence\n");
}
#[test]
fn check_against_gnu_tr_tests_u() {
// ['u', qw(-ds b a), {IN=>'aabbaa'}, {OUT=>'a'}],
new_ucmd!()
.args(&["-ds", "b", "a"])
.pipe_in("aabbaa")
.succeeds()
.stdout_is("a");
}
#[test]
fn check_against_gnu_tr_tests_v() {
// ['v', qw(-ds '[:xdigit:]' Z), {IN=>'ZZ0123456789acbdefABCDEFZZ'}, {OUT=>'Z'}],
new_ucmd!()
.args(&["-ds", "[:xdigit:]", "Z"])
.pipe_in("ZZ0123456789acbdefABCDEFZZ")
.succeeds()
.stdout_is("Z");
}
#[test]
fn check_against_gnu_tr_tests_w() {
// # Try some data with 8th bit set in case something is mistakenly
// # sign-extended.
// ['w', qw(-ds '\350' '\345'),
// {IN=>"\300\301\377\345\345\350\345"},
// {OUT=>"\300\301\377\345"}],
new_ucmd!()
.args(&["-ds", "\u{350}", "\u{345}"])
.pipe_in("\u{300}\u{301}\u{377}\u{345}\u{345}\u{350}\u{345}")
.succeeds()
.stdout_is("\u{300}\u{301}\u{377}\u{345}");
}
#[test]
fn check_against_gnu_tr_tests_x() {
// ['x', qw(-s abcdefghijklmn '[:*016]'),
// {IN=>'abcdefghijklmnop'}, {OUT=>':op'}],
new_ucmd!()
.args(&["-s", "abcdefghijklmn", "[:*016]"])
.pipe_in("abcdefghijklmnop")
.succeeds()
.stdout_is(":op");
}
#[test]
fn check_against_gnu_tr_tests_y() {
// ['y', qw(-d a-z), {IN=>'abc $code'}, {OUT=>' $'}],
new_ucmd!()
.args(&["-d", "a-z"])
.pipe_in("abc $code")
.succeeds()
.stdout_is(" $");
}
#[test]
fn check_against_gnu_tr_tests_z() {
// ['z', qw(-ds a-z '$.'), {IN=>'a.b.c $$$$code\\'}, {OUT=>'. $\\'}],
new_ucmd!()
.args(&["-ds", "a-z", "$."])
.pipe_in("a.b.c $$$$code\\")
.succeeds()
.stdout_is(". $\\");
}
#[test]
fn check_against_gnu_tr_tests_range_a_a() {
// # Make sure that a-a is accepted.
// ['range-a-a', qw(a-a z), {IN=>'abc'}, {OUT=>'zbc'}],
new_ucmd!()
.args(&["a-a", "z"])
.pipe_in("abc")
.succeeds()
.stdout_is("zbc");
}
#[test]
fn check_against_gnu_tr_tests_null() {
// ['null', qw(a ''), {IN=>''}, {OUT=>''}, {EXIT=>1},
// {ERR=>"$prog: when not truncating set1, string2 must be non-empty\n"}],
new_ucmd!()
.args(&["a", ""])
.pipe_in("")
.fails()
.stderr_is("tr: when not truncating set1, string2 must be non-empty\n");
}
#[test]
fn check_against_gnu_tr_tests_upcase() {
// ['upcase', qw('[:lower:]' '[:upper:]'),
// {IN=>'abcxyzABCXYZ'},
// {OUT=>'ABCXYZABCXYZ'}],
new_ucmd!()
.args(&["[:lower:]", "[:upper:]"])
.pipe_in("abcxyzABCXYZ")
.succeeds()
.stdout_is("ABCXYZABCXYZ");
}
#[test]
fn check_against_gnu_tr_tests_dncase() {
// ['dncase', qw('[:upper:]' '[:lower:]'),
// {IN=>'abcxyzABCXYZ'},
// {OUT=>'abcxyzabcxyz'}],
new_ucmd!()
.args(&["[:upper:]", "[:lower:]"])
.pipe_in("abcxyzABCXYZ")
.succeeds()
.stdout_is("abcxyzabcxyz");
}
#[test]
fn check_against_gnu_tr_tests_rep_cclass() {
// ['rep-cclass', qw('a[=*2][=c=]' xyyz), {IN=>'a=c'}, {OUT=>'xyz'}],
new_ucmd!()
.args(&["a[=*2][=c=]", "xyyz"])
.pipe_in("a=c")
.succeeds()
.stdout_is("xyz");
}
#[test]
fn check_against_gnu_tr_tests_rep_1() {
// ['rep-1', qw('[:*3][:digit:]' a-m), {IN=>':1239'}, {OUT=>'cefgm'}],
new_ucmd!()
.args(&["[:*3][:digit:]", "a-m"])
.pipe_in(":1239")
.succeeds()
.stdout_is("cefgm");
}
#[test]
fn check_against_gnu_tr_tests_rep_2() {
// ['rep-2', qw('a[b*512]c' '1[x*]2'), {IN=>'abc'}, {OUT=>'1x2'}],
new_ucmd!()
.args(&["a[b*512]c", "1[x*]2"])
.pipe_in("abc")
.succeeds()
.stdout_is("1x2");
}
#[test]
fn check_against_gnu_tr_tests_rep_3() {
// ['rep-3', qw('a[b*513]c' '1[x*]2'), {IN=>'abc'}, {OUT=>'1x2'}],
new_ucmd!()
.args(&["a[b*513]c", "1[x*]2"])
.pipe_in("abc")
.succeeds()
.stdout_is("1x2");
}
#[test]
fn check_against_gnu_tr_tests_o_rep_1() {
// # Another couple octal repeat count tests.
// ['o-rep-1', qw('[b*08]' '[x*]'), {IN=>''}, {OUT=>''}, {EXIT=>1},
// {ERR=>"$prog: invalid repeat count '08' in [c*n] construct\n"}],
new_ucmd!()
.args(&["[b*08]", "[x*]"])
.pipe_in("")
.fails()
.stderr_is("tr: invalid repeat count '08' in [c*n] construct\n");
}
#[test]
fn check_against_gnu_tr_tests_o_rep_2() {
// ['o-rep-2', qw('[b*010]cd' '[a*7]BC[x*]'), {IN=>'bcd'}, {OUT=>'BCx'}],
new_ucmd!()
.args(&["[b*010]cd", "[a*7]BC[x*]"])
.pipe_in("bcd")
.succeeds()
.stdout_is("BCx");
}
#[test]
fn check_against_gnu_tr_tests_esc() {
// ['esc', qw('a\-z' A-Z), {IN=>'abc-z'}, {OUT=>'AbcBC'}],
new_ucmd!()
.args(&[r"a\-z", "A-Z"])
.pipe_in("abc-z")
.succeeds()
.stdout_is("AbcBC");
}
#[test]
fn check_against_gnu_tr_tests_bs_055() {
// ['bs-055', qw('a\055b' def), {IN=>"a\055b"}, {OUT=>'def'}],
new_ucmd!()
.args(&["a\u{055}b", "def"])
.pipe_in("a\u{055}b")
.succeeds()
.stdout_is("def");
}
#[test]
#[ignore = "Failing in Windows because it will not separate '\' and 'x' as separate arguments"]
fn check_against_gnu_tr_tests_bs_at_end() {
// ['bs-at-end', qw('\\' x), {IN=>"\\"}, {OUT=>'x'},
// {ERR=>"$prog: warning: an unescaped backslash at end of "
// . "string is not portable\n"}],
new_ucmd!()
.args(&[r"\", "x"])
.pipe_in(r"\")
.succeeds()
.stdout_is("x")
.stderr_is("tr: warning: an unescaped backslash at end of string is not portable");
}
#[test]
#[ignore = "not sure why GNU bails here. `[Y*]` should be able to generate all the mapping"]
fn check_against_gnu_tr_tests_ross_0a() {
// # From Ross
// ['ross-0a', qw(-cs '[:upper:]' 'X[Y*]'), {IN=>''}, {OUT=>''}, {EXIT=>1},
// {ERR=>$map_all_to_1}],
new_ucmd!()
.args(&["-cs", "[:upper:]", "X[Y*]"])
.pipe_in("")
.fails()
.stderr_is("tr: when translating with complemented character classes,\nstring2 must map all characters in the domain to one");
}
#[test]
#[ignore = "not sure why GNU bails here. `[Y*]` should be able to generate all the mapping"]
fn check_against_gnu_tr_tests_ross_0b() {
// ['ross-0b', qw(-cs '[:cntrl:]' 'X[Y*]'), {IN=>''}, {OUT=>''}, {EXIT=>1},
// {ERR=>$map_all_to_1}],
new_ucmd!()
.args(&["-cs", "[:cntrl:]", "X[Y*]"])
.pipe_in("")
.fails()
.stderr_is("tr: when translating with complemented character classes,\nstring2 must map all characters in the domain to one");
}
#[test]
fn check_against_gnu_tr_tests_ross_1a() {
// ['ross-1a', qw(-cs '[:upper:]' '[X*]'),
// {IN=>'AMZamz123.-+AMZ'}, {OUT=>'AMZXAMZ'}],
new_ucmd!()
.args(&["-cs", "[:upper:]", "[X*]"])
.pipe_in("AMZamz123.-+AMZ")
.succeeds()
.stdout_is("AMZXAMZ");
}
#[test]
fn check_against_gnu_tr_tests_ross_1b() {
// ['ross-1b', qw(-cs '[:upper:][:digit:]' '[Z*]'), {IN=>''}, {OUT=>''}],
new_ucmd!()
.args(&["-cs", "[:upper:][:digit:]", "[Z*]"])
.pipe_in("")
.succeeds()
.stdout_is("");
}
#[test]
fn check_against_gnu_tr_tests_ross_2() {
// ['ross-2', qw(-dcs '[:lower:]' n-rs-z),
// {IN=>'amzAMZ123.-+amz'}, {OUT=>'amzamz'}],
new_ucmd!()
.args(&["-dcs", "[:lower:]", "n-rs-z"])
.pipe_in("amzAMZ123.-+amz")
.succeeds()
.stdout_is("amzamz");
}
#[test]
fn check_against_gnu_tr_tests_ross_3() {
// ['ross-3', qw(-ds '[:xdigit:]' '[:alnum:]'),
// {IN=>'.ZABCDEFGzabcdefg.0123456788899.GG'}, {OUT=>'.ZGzg..G'}],
new_ucmd!()
.args(&["-ds", "[:xdigit:]", "[:alnum:]"])
.pipe_in(".ZABCDEFGzabcdefg.0123456788899.GG")
.succeeds()
.stdout_is(".ZGzg..G");
}
#[test]
fn check_against_gnu_tr_tests_ross_4() {
// ['ross-4', qw(-dcs '[:alnum:]' '[:digit:]'), {IN=>''}, {OUT=>''}],
new_ucmd!()
.args(&["-dcs", "[:alnum:]", "[:digit:]"])
.pipe_in("")
.succeeds()
.stdout_is("");
}
#[test]
fn check_against_gnu_tr_tests_ross_5() {
// ['ross-5', qw(-dc '[:lower:]'), {IN=>''}, {OUT=>''}],
new_ucmd!()
.args(&["-dc", "[:lower:]"])
.pipe_in("")
.succeeds()
.stdout_is("");
}
#[test]
fn check_against_gnu_tr_tests_ross_6() {
// ['ross-6', qw(-dc '[:upper:]'), {IN=>''}, {OUT=>''}],
new_ucmd!()
.args(&["-dc", "[:upper:]"])
.pipe_in("")
.succeeds()
.stdout_is("");
}
#[test]
fn check_against_gnu_tr_tests_empty_eq() {
// # Ensure that these fail.
// # Prior to 2.0.20, each would evoke a failed assertion.
// ['empty-eq', qw('[==]' x), {IN=>''}, {OUT=>''}, {EXIT=>1},
// {ERR=>"$prog: missing equivalence class character '[==]'\n"}],
new_ucmd!()
.args(&["[==]", "x"])
.pipe_in("")
.fails()
.stderr_is("tr: missing equivalence class character '[==]'\n");
}
#[test]
fn check_against_gnu_tr_tests_empty_cc() {
// ['empty-cc', qw('[::]' x), {IN=>''}, {OUT=>''}, {EXIT=>1},
// {ERR=>"$prog: missing character class name '[::]'\n"}],
new_ucmd!()
.args(&["[::]", "x"])
.pipe_in("")
.fails()
.stderr_is("tr: missing character class name '[::]'\n");
}
#[test]
fn check_against_gnu_tr_tests_repeat_bs_9() {
// # Weird repeat counts.
// ['repeat-bs-9', qw(abc '[b*\9]'), {IN=>'abcd'}, {OUT=>'[b*d'}],
new_ucmd!()
.args(&["abc", r"[b*\9]"])
.pipe_in("abcd")
.succeeds()
.stdout_is("[b*d");
}
#[test]
fn check_against_gnu_tr_tests_repeat_0() {
// ['repeat-0', qw(abc '[b*0]'), {IN=>'abcd'}, {OUT=>'bbbd'}],
new_ucmd!()
.args(&["abc", "[b*0]"])
.pipe_in("abcd")
.succeeds()
.stdout_is("bbbd");
}
#[test]
fn check_against_gnu_tr_tests_repeat_zeros() {
// ['repeat-zeros', qw(abc '[b*00000000000000000000]'),
// {IN=>'abcd'}, {OUT=>'bbbd'}],
new_ucmd!()
.args(&["abc", "[b*00000000000000000000]"])
.pipe_in("abcd")
.succeeds()
.stdout_is("bbbd");
}
#[test]
fn check_against_gnu_tr_tests_repeat_compl() {
// ['repeat-compl', qw(-c '[a*65536]\n' '[b*]'), {IN=>'abcd'}, {OUT=>'abbb'}],
new_ucmd!()
.args(&["-c", "[a*65536]\n", "[b*]"])
.pipe_in("abcd")
.succeeds()
.stdout_is("abbb");
}
#[test]
fn check_against_gnu_tr_tests_repeat_x_c() {
// ['repeat-xC', qw(-C '[a*65536]\n' '[b*]'), {IN=>'abcd'}, {OUT=>'abbb'}],
new_ucmd!()
.args(&["-C", "[a*65536]\n", "[b*]"])
.pipe_in("abcd")
.succeeds()
.stdout_is("abbb");
}
#[test]
#[ignore = "I think either clap-rs or uutils is parsing the '-H' as an argument..."]
fn check_against_gnu_tr_tests_fowler_1() {
// # From Glenn Fowler.
// ['fowler-1', qw(ah -H), {IN=>'aha'}, {OUT=>'-H-'}],
new_ucmd!()
.args(&["ah", "-H"])
.pipe_in("aha")
.succeeds()
.stdout_is("-H-");
}
#[test]
fn check_against_gnu_tr_tests_no_abort_1() {
// # Up to coreutils-6.9, this would provoke a failed assertion.
// ['no-abort-1', qw(-c a '[b*256]'), {IN=>'abc'}, {OUT=>'abb'}],
new_ucmd!()
.args(&["-c", "a", "[b*256]"])
.pipe_in("abc")
.succeeds()
.stdout_is("abb");
}