utaz: add case management sensitivity support

This commit is contained in:
2026-10-13 11:21:53 +02:00
parent 2cc767ecc2
commit 6bfc2804ab
5 changed files with 76 additions and 9 deletions
+3 -2
View File
@@ -215,11 +215,12 @@ change the default without having to pass flags every time.
| Key | Default | Description |
| --- | --- | --- |
| `TAZ_DEFAULT_FORMAT` | `tar.gz` | Archive format for `taz` (`tar.gz`, `tar.bz2`, `tar.xz`, `zip`, …) |
| `TAZ_DEFAULT_FORMAT` | `lz` | Archive format(s) for `taz`: `lz`, `xz`, `bz2`, `gz`, `lzo`, `tar`; comma-separated list keeps the smallest result |
| `TAZ_DEFAULT_THREADS` | `auto` | Compression threads (`auto` = runtime CPU count, or explicit positive integer) |
| `TAZ_DEFAULT_LEVEL` | `6` | Compression level (1–9) |
| `UTAZ_DEFAULT_DELETE` | `0` | Set to `1` to delete the source archive after extraction |
| `UTAZ_DEFAULT_DIR_MODE` | `0` | Set to `1` to always extract into a subdirectory |
| `UTAZ_DEFAULT_DIR_MODE` | `auto` | Host directory policy: `auto` (only when the archive has no single top-level directory), `always` or `never` |
| `UTAZ_DEFAULT_IGNORE_CASE` | `0` | Set to `1` to match archive extensions case-insensitively (`.ZIP`, `.Tar.GZ`). Per call: `utaz -i` / `utaz -s` |
**`[filefct]`** — file utilities
+23
View File
@@ -7,6 +7,29 @@ Versions follow `MAJOR.MINOR.PATCH-REVISION_STAGE_N` (e.g. `3.99.1-4_rc_1`).
---
## [Unreleased]
### Added
- `utaz -i` / `--ignore-case` and `-s` / `--case-sensitive` switches to match
archive extensions case-insensitively or not; the default is set by the new
`UTAZ_DEFAULT_IGNORE_CASE` key of the `[compress]` section (default `0`).
- `utaz` supports bare `.zst` files and `.tzst` archives; `taz -q` is now
effective.
### Fixed
- `taz`: a plain file with the `tar` format could be deleted instead of being
archived; existing `<item>.tar` files are no longer overwritten; trailing
slashes, `.`/`..` and names starting with `-` are handled; the `gzip`
fallback (no `pigz`) works again.
- `utaz`: single top-level directory flattening, `--no-dir` target location,
`foo.tar.gz` host directory name and `.tar.lz` extraction.
- `compress.sh`: option flags and loop variables no longer leak into the
interactive shell.
---
## [4.1.0] — 2026-05-07
### Added
+5
View File
@@ -49,6 +49,11 @@ PAGER=less
# never — always flatten into the current directory
#UTAZ_DEFAULT_DIR_MODE=auto
# utaz: Match archive extensions case-insensitively, so that FOO.ZIP or
# bar.Tar.GZ are recognised (0=no, 1=yes). Overridden per call with
# -i/--ignore-case or -s/--case-sensitive.
#UTAZ_DEFAULT_IGNORE_CASE=0
# ==============================================================================
[debug]
# (no configurable keys yet)
+5
View File
@@ -36,6 +36,11 @@ TERM=xterm-256color
# never — always flatten extraction into the current directory
#UTAZ_DEFAULT_DIR_MODE=auto
# utaz: Match archive extensions case-insensitively, so that FOO.ZIP or
# bar.Tar.GZ are recognised (0=no, 1=yes). Overridden per call with
# -i/--ignore-case or -s/--case-sensitive.
#UTAZ_DEFAULT_IGNORE_CASE=0
[debug]
# Section used by debug.sh (nothing yet)
+40 -7
View File
@@ -44,6 +44,9 @@ load_conf "compress"
# -d, --delete If decompression succeeded, delete the source file
# -c, --create-dir Always create a host directory
# -n, --no-dir Never create a host directory
# -i, --ignore-case Match archive extensions case-insensitively (.ZIP, .Tar.GZ)
# -s, --case-sensitive Match archive extensions case-sensitively (overrides the
# UTAZ_DEFAULT_IGNORE_CASE configuration key)
utaz()
{
# Extractor helpers. Every one takes the archive path as $1 and an existing
@@ -156,7 +159,11 @@ utaz()
{
# A tar.zst/.tzst is untarred; a bare .zst is a single compressed file
# and is simply decompressed into the target directory.
case $1 in
# Called from utaz: "icase" is utaz's local, visible here through
# dynamic scoping. Lowercase the name so the match follows the mode.
local key=$1
[[ -n $icase ]] && key=${key,,}
case $key in
*.tar.zst|*.tzst)
tar --zstd -xf "$1" -C "$2"
;;
@@ -236,7 +243,7 @@ utaz()
)
local PARSED
PARSED=$(getopt -o hdcn --long help,delete,create-dir,no-dir -n 'utaz' -- "$@")
PARSED=$(getopt -o hdcnis --long help,delete,create-dir,no-dir,ignore-case,case-sensitive -n 'utaz' -- "$@")
# shellcheck disable=SC2181 # getopt return code is checked immediately after
if [ $? -ne 0 ]; then
disp E "Invalid options, use \"utaz --help\" to display usage."
@@ -245,7 +252,7 @@ utaz()
# getopt emits a shell-quoted, normalised argument list ("--" terminates the
# options); re-evaluating it restores the arguments with spaces and quotes
# intact. Declared local so flags never leak in from the caller's environment.
local willrm="" createdir="" nodir=""
local willrm="" createdir="" nodir="" icase="" csens=""
eval set -- "$PARSED"
while true; do
case "$1" in
@@ -257,7 +264,9 @@ utaz()
printf "\t-h, --help\t\tDisplay that help screen\n"
printf "\t-d, --delete\t\tIf decompression succeeded, delete the source file\n"
printf "\t-c, --create-dir\tAlways create a host directory\n"
printf "\t-n, --no-dir\t\tNever create a host directory\n\n"
printf "\t-n, --no-dir\t\tNever create a host directory\n"
printf "\t-i, --ignore-case\tMatch archive extensions case-insensitively\n"
printf "\t-s, --case-sensitive\tMatch archive extensions case-sensitively (default)\n\n"
printf "Supported archive format:\n"
printf "\t- zip\n"
printf "\t- tar.gz, .tgz\n"
@@ -290,6 +299,14 @@ utaz()
nodir=1
shift
;;
-i|--ignore-case)
icase=1
shift
;;
-s|--case-sensitive)
csens=1
shift
;;
--)
shift
break
@@ -319,6 +336,16 @@ utaz()
return 1
}
# Extension matching: the flags win over the configured default. After this
# block "icase" is non-empty if and only if matching is case-insensitive.
[[ -n $icase && -n $csens ]] && {
disp E "The --ignore-case and --case-sensitive options are mutually exclusive."
return 1
}
if [[ -z $icase && -z $csens && ${UTAZ_DEFAULT_IGNORE_CASE:-0} -eq 1 ]]; then
icase=1
fi
# Loop variables are local: this function lives in the interactive shell.
local zitem f
for zitem in "${FILES[@]}"; do
@@ -343,13 +370,19 @@ utaz()
fi
for f in "${targets[@]}"; do
# "key" is the name the extension patterns are matched against:
# lowercased in ignore-case mode, untouched otherwise.
local key=$f
[[ -n $icase ]] && key=${f,,}
# Host directory name: archive name without its extension. A
# trailing ".tar" is dropped too, so foo.tar.gz gives "foo", not
# "foo.tar".
# "foo.tar". The ".tar" test uses "key" so it follows the case mode
# (the extension is 4 characters either way).
local dir="${f%.*}"
[[ $dir == *.tar ]] && dir="${dir%.tar}"
[[ ${key%.*} == *.tar ]] && dir="${dir%.*}"
local extractor=""
case "$f" in
case "$key" in
*.zip)
extractor="_ununzip"
;;