mirror of
git://git.code.sf.net/p/cdesktopenv/code
synced 2025-02-15 04:32:24 +00:00
The commit that backported read -a did not add a case label for it to read.c. This was under the assumption that AST optget(3) would always convert -a to -A. However, that was only done for first use. The cause is the short-form options caching mechanism in optget(3). On the first run, the pre-caching result would be returned, but the equivalent option (-a) would be cached as if it is its own option, so on the second and subsequent runs, optget returned 'a' instead of 'A'. This only happens if no long-form equivalent is present. Reproducer: $ read -A foo <<< 'foo bar baz' $ unset foo $ read -a foo <<< 'foo bar baz' $ echo ${foo[0]} foo bar baz Expected: foo src/lib/libast/misc/optget.c, src/lib/libast/misc/optlib.h: - [by Martijn Dekker] Implement caching for short-option equivalents. If a short-form equivalent is found, instead of caching it as a separate option, cache the equivalent in a new equiv[] array. Check this when reading the cache and substitute the main option for the equivalent if one is cached. src/lib/libcmd/cp.c: - Fix cp -r/cp -R symlink handling. The -r and -R options sometimes ignored -P, -L and -H. - The -r and -R options no longer follow symlinks by default. src/cmd/ksh93/bltins/whence.c, src/lib/libcmd/*.c: - Remove case labels that are redundant now that the optget(3) caching bug is fixed. src/cmd/ksh93/tests/libcmd.sh: - Added. This is the new script for the /opt/ast/bin path-bound built-ins from libcmd. Other relevant tests are moved into here. Co-authored-by: Martijn Dekker <martijn@inlv.org>
This commit is contained in:
parent
aa1f8244e6
commit
bea4fd56e8
12 changed files with 400 additions and 210 deletions
10
NEWS
10
NEWS
|
@ -2,6 +2,16 @@ This documents significant changes in the 1.0 branch of ksh 93u+m.
|
||||||
For full details, see the git log at: https://github.com/ksh93/ksh/tree/1.0
|
For full details, see the git log at: https://github.com/ksh93/ksh/tree/1.0
|
||||||
Uppercase BUG_* IDs are shell bug IDs as used by the Modernish shell library.
|
Uppercase BUG_* IDs are shell bug IDs as used by the Modernish shell library.
|
||||||
|
|
||||||
|
2022-08-20:
|
||||||
|
|
||||||
|
- Fixed a bug in command line options processing that caused short-form
|
||||||
|
option equivalents on some built-in commands to be ignored after one use,
|
||||||
|
e.g., the new read -a equivalent of read -A (introduced on 2022-02-16).
|
||||||
|
|
||||||
|
- Fixed a bug in the /opt/ast/bin/cp built-in command that caused the -r and
|
||||||
|
-R options to sometimes ignore -P, -L and -H. Additionally, the -r and -R
|
||||||
|
options no longer follow symlinks by default.
|
||||||
|
|
||||||
2022-08-16:
|
2022-08-16:
|
||||||
|
|
||||||
- Fixed an old bug in history expansion (set -H) where any use of the history
|
- Fixed an old bug in history expansion (set -H) where any use of the history
|
||||||
|
|
|
@ -124,7 +124,6 @@ int b_whence(int argc,char *argv[],Shbltin_t *context)
|
||||||
case 'f':
|
case 'f':
|
||||||
flags |= F_FLAG;
|
flags |= F_FLAG;
|
||||||
break;
|
break;
|
||||||
case 'P':
|
|
||||||
case 'p':
|
case 'p':
|
||||||
flags |= P_FLAG;
|
flags |= P_FLAG;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
|
|
||||||
#define SH_RELEASE_FORK "93u+m" /* only change if you develop a new ksh93 fork */
|
#define SH_RELEASE_FORK "93u+m" /* only change if you develop a new ksh93 fork */
|
||||||
#define SH_RELEASE_SVER "1.0.3-alpha" /* semantic version number: https://semver.org */
|
#define SH_RELEASE_SVER "1.0.3-alpha" /* semantic version number: https://semver.org */
|
||||||
#define SH_RELEASE_DATE "2022-08-19" /* must be in this format for $((.sh.version)) */
|
#define SH_RELEASE_DATE "2022-08-20" /* must be in this format for $((.sh.version)) */
|
||||||
#define SH_RELEASE_CPYR "(c) 2020-2022 Contributors to ksh " SH_RELEASE_FORK
|
#define SH_RELEASE_CPYR "(c) 2020-2022 Contributors to ksh " SH_RELEASE_FORK
|
||||||
|
|
||||||
/* Scripts sometimes field-split ${.sh.version}, so don't change amount of whitespace. */
|
/* Scripts sometimes field-split ${.sh.version}, so don't change amount of whitespace. */
|
||||||
|
|
|
@ -1,92 +0,0 @@
|
||||||
########################################################################
|
|
||||||
# #
|
|
||||||
# This software is part of the ast package #
|
|
||||||
# Copyright (c) 2019-2020 Contributors to ksh2020 #
|
|
||||||
# Copyright (c) 2022 Contributors to ksh 93u+m #
|
|
||||||
# and is licensed under the #
|
|
||||||
# Eclipse Public License, Version 2.0 #
|
|
||||||
# #
|
|
||||||
# A copy of the License is available at #
|
|
||||||
# https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html #
|
|
||||||
# (with md5 checksum 84283fa8859daf213bdda5a9f8d1be1d) #
|
|
||||||
# #
|
|
||||||
# Kurtis Rader <krader@skepticism.us> #
|
|
||||||
# Johnothan King <johnothanking@protonmail.com> #
|
|
||||||
# #
|
|
||||||
########################################################################
|
|
||||||
|
|
||||||
# Tests for `head` builtin
|
|
||||||
|
|
||||||
. "${SHTESTS_COMMON:-${0%/*}/_common}"
|
|
||||||
if ! builtin head 2> /dev/null; then
|
|
||||||
warning 'Could not detect head builtin; skipping tests'
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
cat > "$tmp/file1" <<EOF
|
|
||||||
This is line 1 in file1
|
|
||||||
This is line 2 in file1
|
|
||||||
This is line 3 in file1
|
|
||||||
This is line 4 in file1
|
|
||||||
This is line 5 in file1
|
|
||||||
This is line 6 in file1
|
|
||||||
This is line 7 in file1
|
|
||||||
This is line 8 in file1
|
|
||||||
This is line 9 in file1
|
|
||||||
This is line 10 in file1
|
|
||||||
This is line 11 in file1
|
|
||||||
This is line 12 in file1
|
|
||||||
EOF
|
|
||||||
|
|
||||||
cat > "$tmp/file2" <<EOF2
|
|
||||||
This is line 1 in file2
|
|
||||||
This is line 2 in file2
|
|
||||||
This is line 3 in file2
|
|
||||||
This is line 4 in file2
|
|
||||||
This is line 5 in file2
|
|
||||||
EOF2
|
|
||||||
|
|
||||||
# ==========
|
|
||||||
# -*n*; i.e., an integer presented as a flag.
|
|
||||||
#
|
|
||||||
# The `awk` invocation is to strip whitespace around the output of `wc` since it might pad the
|
|
||||||
# value.
|
|
||||||
exp=11
|
|
||||||
got=$(head -11 < "$tmp/file1" | wc -l | awk '{ print $1 }')
|
|
||||||
[[ "$got" = "$exp" ]] || err_exit "'head -n' failed" "(expected $(printf %q "$exp"), got $(printf %q "$got"))"
|
|
||||||
|
|
||||||
# ==========
|
|
||||||
# -n, --lines=lines
|
|
||||||
# Copy lines lines from each file. The default value is 10.
|
|
||||||
got=$(head -n 3 "$tmp/file1")
|
|
||||||
exp=$'This is line 1 in file1\nThis is line 2 in file1\nThis is line 3 in file1'
|
|
||||||
[[ "$got" = "$exp" ]] || err_exit "'head -n' failed" "(expected $(printf %q "$exp"), got $(printf %q "$got"))"
|
|
||||||
|
|
||||||
# ==========
|
|
||||||
# -c, --bytes=chars
|
|
||||||
# Copy chars bytes from each file.
|
|
||||||
got=$(head -c 14 "$tmp/file1")
|
|
||||||
exp=$'This is line 1'
|
|
||||||
[[ "$got" = "$exp" ]] || err_exit "'head -c' failed" "(expected $(printf %q "$exp"), got $(printf %q "$got"))"
|
|
||||||
|
|
||||||
# ==========
|
|
||||||
# -q, --quiet|silent
|
|
||||||
# Never output filename headers.
|
|
||||||
got=$(head -q -n 3 "$tmp/file1" "$tmp/file2")
|
|
||||||
exp=$'This is line 1 in file1\nThis is line 2 in file1\nThis is line 3 in file1\nThis is line 1 in file2\nThis is line 2 in file2\nThis is line 3 in file2'
|
|
||||||
[[ "$got" = "$exp" ]] || err_exit "'head -q' failed" "(expected $(printf %q "$exp"), got $(printf %q "$got"))"
|
|
||||||
|
|
||||||
# ==========
|
|
||||||
# -s, --skip=char Skip char characters or lines from each file before copying.
|
|
||||||
got=$(head -s 5 -c 18 "$tmp/file1")
|
|
||||||
exp=$'is line 1 in file1'
|
|
||||||
[[ "$got" = "$exp" ]] || err_exit "'head -s' failed" "(expected $(printf %q "$exp"), got $(printf %q "$got"))"
|
|
||||||
|
|
||||||
# ==========
|
|
||||||
# -v, --verbose Always output filename headers.
|
|
||||||
got=$(head -v -n 3 "$tmp/file1")
|
|
||||||
exp=$'file1 <==\nThis is line 1 in file1\nThis is line 2 in file1\nThis is line 3 in file1'
|
|
||||||
[[ "$got" =~ "$exp" ]] || err_exit "'head -v' failed" "(expected $(printf %q "$exp"), got $(printf %q "$got"))"
|
|
||||||
|
|
||||||
# ======
|
|
||||||
exit $((Errors<125?Errors:125))
|
|
|
@ -16,6 +16,9 @@
|
||||||
# #
|
# #
|
||||||
########################################################################
|
########################################################################
|
||||||
|
|
||||||
|
# Tests for special and regular built-in commands (except those for
|
||||||
|
# libcmd path-bound built-ins; they should go into libcmd.sh instead)
|
||||||
|
|
||||||
. "${SHTESTS_COMMON:-${0%/*}/_common}"
|
. "${SHTESTS_COMMON:-${0%/*}/_common}"
|
||||||
|
|
||||||
bincat=$(whence -p cat)
|
bincat=$(whence -p cat)
|
||||||
|
@ -1212,19 +1215,6 @@ got=$($SHELL -c 't=good; t=bad command -@; print $t' 2>/dev/null)
|
||||||
[[ $exp == $got ]] || err_exit "temp var assignment with 'command'" \
|
[[ $exp == $got ]] || err_exit "temp var assignment with 'command'" \
|
||||||
"(expected $(printf %q "$expect"), got $(printf %q "$actual"))"
|
"(expected $(printf %q "$expect"), got $(printf %q "$actual"))"
|
||||||
|
|
||||||
# ======
|
|
||||||
# Regression test for https://github.com/att/ast/issues/949
|
|
||||||
if (builtin chmod) 2>/dev/null
|
|
||||||
then foo_script='#!/bin/sh
|
|
||||||
exit 0'
|
|
||||||
echo "$foo_script" > "$tmp/foo1.sh"
|
|
||||||
echo "$foo_script" > "$tmp/foo2.sh"
|
|
||||||
builtin chmod
|
|
||||||
chmod +x "$tmp/foo1.sh" "$tmp/foo2.sh"
|
|
||||||
$SHELL "$tmp/foo1.sh" || err_exit "builtin 'chmod +x' doesn't work on first script"
|
|
||||||
$SHELL "$tmp/foo2.sh" || err_exit "builtin 'chmod +x' doesn't work on second script"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# ======
|
# ======
|
||||||
# In ksh93v- 2013-10-10 alpha cd doesn't fail on directories without execute permission.
|
# In ksh93v- 2013-10-10 alpha cd doesn't fail on directories without execute permission.
|
||||||
# Additionally, ksh93v- added a regression test for attempting to use cd on a file.
|
# Additionally, ksh93v- added a regression test for attempting to use cd on a file.
|
||||||
|
@ -1343,26 +1333,6 @@ then builtin uname
|
||||||
"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
|
"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# ======
|
|
||||||
# https://github.com/ksh93/ksh/issues/138
|
|
||||||
builtin -d cat
|
|
||||||
if [[ $'\n'${ builtin; }$'\n' == *$'\n/opt/ast/bin/cat\n'* ]]
|
|
||||||
then exp=' version cat (*) ????-??-??'
|
|
||||||
got=$(/opt/ast/bin/cat --version 2>&1)
|
|
||||||
[[ $got == $exp ]] || err_exit "path-bound builtin not executable by literal canonical path" \
|
|
||||||
"(expected match of $(printf %q "$exp"), got $(printf %q "$got"))"
|
|
||||||
got=$(PATH=/opt/ast/bin:$PATH; "${ whence -p cat; }" --version 2>&1)
|
|
||||||
[[ $got == $exp ]] || err_exit "path-bound builtin not executable by canonical path resulting from expansion" \
|
|
||||||
"(expected match of $(printf %q "$exp"), got $(printf %q "$got"))"
|
|
||||||
got=$(PATH=/opt/ast/bin:$PATH; "$SHELL" -o restricted -c 'cat --version' 2>&1)
|
|
||||||
[[ $got == $exp ]] || err_exit "restricted shells do not recognize path-bound builtins" \
|
|
||||||
"(expected match of $(printf %q "$exp"), got $(printf %q "$got"))"
|
|
||||||
got=$(set +x; PATH=/opt/ast/bin cat --version 2>&1)
|
|
||||||
[[ $got == $exp ]] || err_exit "path-bound builtin not found on PATH in preceding assignment" \
|
|
||||||
"(expected match of $(printf %q "$exp"), got $(printf %q "$got"))"
|
|
||||||
else warning 'skipping path-bound builtin tests: builtin /opt/ast/bin/cat not found'
|
|
||||||
fi
|
|
||||||
|
|
||||||
# ======
|
# ======
|
||||||
# part of https://github.com/ksh93/ksh/issues/153
|
# part of https://github.com/ksh93/ksh/issues/153
|
||||||
mkdir "$tmp/deleted"
|
mkdir "$tmp/deleted"
|
||||||
|
@ -1439,55 +1409,6 @@ printf -v 'got[1][two][3]' 'ok\f%012d\n' $ver 2>/dev/null
|
||||||
unset got ver
|
unset got ver
|
||||||
}
|
}
|
||||||
|
|
||||||
# ======
|
|
||||||
# The rm builtin's -d option should remove files and empty directories without
|
|
||||||
# removing non-empty directories (unless the -r option is also passed).
|
|
||||||
# https://www.austingroupbugs.net/view.php?id=802
|
|
||||||
if builtin rm 2> /dev/null; then
|
|
||||||
echo foo > "$tmp/bar"
|
|
||||||
mkdir "$tmp/emptydir"
|
|
||||||
mkdir -p "$tmp/nonemptydir1/subfolder"
|
|
||||||
mkdir "$tmp/nonemptydir2"
|
|
||||||
echo dummyfile > "$tmp/nonemptydir2/shouldexist"
|
|
||||||
|
|
||||||
# Tests for lone -d option
|
|
||||||
got=$(rm -d "$tmp/emptydir" 2>&1)
|
|
||||||
[[ $? == 0 ]] || err_exit 'rm builtin fails to remove empty directory with -d option' \
|
|
||||||
"(got $(printf %q "$got"))"
|
|
||||||
[[ -d $tmp/emptydir ]] && err_exit 'rm builtin fails to remove empty directory with -d option'
|
|
||||||
got=$(rm -d $tmp/bar 2>&1)
|
|
||||||
[[ $? == 0 ]] || err_exit 'rm builtin fails to remove files with -d option' \
|
|
||||||
"(got $(printf %q "$got"))"
|
|
||||||
[[ -f $tmp/bar ]] && err_exit 'rm builtin fails to remove files with -d option'
|
|
||||||
rm -d "$tmp/nonemptydir1" 2> /dev/null
|
|
||||||
[[ ! -d $tmp/nonemptydir1/subfolder ]] && err_exit 'rm builtin has unwanted recursion with -d option on folder containing folder'
|
|
||||||
rm -d "$tmp/nonemptydir2" 2> /dev/null
|
|
||||||
[[ ! -f $tmp/nonemptydir2/shouldexist ]] && err_exit 'rm builtin has unwanted recursion with -d option on folder containing file'
|
|
||||||
|
|
||||||
# Recreate non-empty directories in case the above tests failed
|
|
||||||
mkdir -p "$tmp/nonemptydir1/subfolder"
|
|
||||||
mkdir -p "$tmp/nonemptydir2"
|
|
||||||
echo dummyfile > "$tmp/nonemptydir2/shouldexist"
|
|
||||||
|
|
||||||
# Tests combining -d with -r
|
|
||||||
got=$(rm -rd "$tmp/nonemptydir1" 2>&1)
|
|
||||||
[[ $? == 0 ]] || err_exit 'rm builtin fails to remove non-empty directory and subdirectory with -rd options' \
|
|
||||||
"(got $(printf %q "$got"))"
|
|
||||||
[[ -d $tmp/nonemptydir1/subfolder || -d $tmp/nonemptydir1 ]] && err_exit 'rm builtin fails to remove all folders with -rd options'
|
|
||||||
got=$(rm -rd "$tmp/nonemptydir2" 2>&1)
|
|
||||||
[[ $? == 0 ]] || err_exit 'rm builtin fails to remove non-empty directory and file with -rd options' \
|
|
||||||
"(got $(printf %q "$got"))"
|
|
||||||
[[ -f $tmp/nonemptydir2/shouldexist || -d $tmp/nonemptydir2 ]] && err_exit 'rm builtin fails to remove all folders and files with -rd options'
|
|
||||||
|
|
||||||
# Additional test: 'rm -f' without additional arguments should act
|
|
||||||
# as a no-op command. This bug was fixed in ksh93u+ 2012-02-14.
|
|
||||||
got=$(rm -f 2>&1)
|
|
||||||
if (($? != 0)) || [[ ! -z $got ]]
|
|
||||||
then err_exit 'rm -f without additional arguments does not work correctly' \
|
|
||||||
"(got $(printf %q "$got"))"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# ======
|
# ======
|
||||||
# These are regression tests for the cd command's -e and -P flags
|
# These are regression tests for the cd command's -e and -P flags
|
||||||
if ((.sh.version >= 20211205))
|
if ((.sh.version >= 20211205))
|
||||||
|
@ -1544,23 +1465,6 @@ then
|
||||||
(( got == exp )) || err_exit "cd -eP to empty string has wrong exit status (expected $exp, got $got)"
|
(( got == exp )) || err_exit "cd -eP to empty string has wrong exit status (expected $exp, got $got)"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# ======
|
|
||||||
# The head and tail builtins should work on files without newlines
|
|
||||||
if builtin head 2> /dev/null; then
|
|
||||||
print -n nonewline > "$tmp/nonewline"
|
|
||||||
exp=nonewline
|
|
||||||
got=$(head -1 "$tmp/nonewline")
|
|
||||||
[[ $got == $exp ]] || err_exit "head builtin fails to correctly handle files without an ending newline" \
|
|
||||||
"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
|
|
||||||
fi
|
|
||||||
if builtin tail 2> /dev/null; then
|
|
||||||
print -n 'newline\nnonewline' > "$tmp/nonewline"
|
|
||||||
exp=nonewline
|
|
||||||
got=$(tail -1 "$tmp/nonewline")
|
|
||||||
[[ $got == $exp ]] || err_exit "tail builtin fails to correctly handle files without an ending newline" \
|
|
||||||
"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# ======
|
# ======
|
||||||
# ksh93v- accidentally broke the sleep builtin's support for
|
# ksh93v- accidentally broke the sleep builtin's support for
|
||||||
# using microseconds in the form of <num>U.
|
# using microseconds in the form of <num>U.
|
||||||
|
@ -1597,5 +1501,43 @@ if ((SHOPT_BRACEPAT)); then
|
||||||
"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
|
"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# The read builtin's -a and -A flags should function identically
|
||||||
|
read_a_test=$tmp/read_a_test.sh
|
||||||
|
cat > "$read_a_test" << 'EOF'
|
||||||
|
. "${SHTESTS_COMMON}"
|
||||||
|
exp=foo
|
||||||
|
exp1=bar
|
||||||
|
exp2=baz
|
||||||
|
read -a foo_a <<< 'foo bar baz'
|
||||||
|
if [[ ${foo_a[0]} != ${exp} ]] || [[ ${foo_a[1]} != ${exp1} ]] || [[ ${foo_a[2]} != ${exp2} ]]
|
||||||
|
then
|
||||||
|
err_exit "read -a fails to create array with first use" \
|
||||||
|
"(foo_a[0] is $(printf %q "${foo_a[0]}"), foo_a[1] is $(printf %q "${foo_a[1]}"), foo_a[2] is $(printf %q "${foo_a[2]}"))"
|
||||||
|
fi
|
||||||
|
unset foo_a
|
||||||
|
read -a foo_a <<< 'foo bar baz'
|
||||||
|
if [[ ${foo_a[0]} != ${exp} ]] || [[ ${foo_a[1]} != ${exp1} ]] || [[ ${foo_a[2]} != ${exp2} ]]
|
||||||
|
then
|
||||||
|
err_exit "read -a fails to create array with second use" \
|
||||||
|
"(foo_a[0] is $(printf %q "${foo_a[0]}"), foo_a[1] is $(printf %q "${foo_a[1]}"), foo_a[2] is $(printf %q "${foo_a[2]}"))"
|
||||||
|
fi
|
||||||
|
read -A foo_A <<< 'foo bar baz'
|
||||||
|
if [[ ${foo_A[0]} != ${exp} ]] || [[ ${foo_A[1]} != ${exp1} ]] || [[ ${foo_A[2]} != ${exp2} ]]
|
||||||
|
then
|
||||||
|
err_exit "read -A fails to create array with first use" \
|
||||||
|
"(foo_A[0] is $(printf %q "${foo_A[0]}"), foo_A[1] is $(printf %q "${foo_A[1]}"), foo_A[2] is $(printf %q "${foo_A[2]}"))"
|
||||||
|
fi
|
||||||
|
unset foo_A
|
||||||
|
read -A foo_A <<< 'foo bar baz'
|
||||||
|
if [[ ${foo_A[0]} != ${exp} ]] || [[ ${foo_A[1]} != ${exp1} ]] || [[ ${foo_A[2]} != ${exp2} ]]
|
||||||
|
then
|
||||||
|
err_exit "read -A fails to create array with second use" \
|
||||||
|
"(foo_A[0] is $(printf %q "${foo_A[0]}"), foo_A[1] is $(printf %q "${foo_A[1]}"), foo_A[2] is $(printf %q "${foo_A[2]}"))"
|
||||||
|
fi
|
||||||
|
exit $Errors
|
||||||
|
EOF
|
||||||
|
"$SHELL" "$read_a_test"
|
||||||
|
let Errors+=$?
|
||||||
|
|
||||||
# ======
|
# ======
|
||||||
exit $((Errors<125?Errors:125))
|
exit $((Errors<125?Errors:125))
|
||||||
|
|
327
src/cmd/ksh93/tests/libcmd.sh
Executable file
327
src/cmd/ksh93/tests/libcmd.sh
Executable file
|
@ -0,0 +1,327 @@
|
||||||
|
########################################################################
|
||||||
|
# #
|
||||||
|
# This software is part of the ast package #
|
||||||
|
# Copyright (c) 2019-2020 Contributors to ksh2020 #
|
||||||
|
# Copyright (c) 2022 Contributors to ksh 93u+m #
|
||||||
|
# and is licensed under the #
|
||||||
|
# Eclipse Public License, Version 2.0 #
|
||||||
|
# #
|
||||||
|
# A copy of the License is available at #
|
||||||
|
# https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html #
|
||||||
|
# (with md5 checksum 84283fa8859daf213bdda5a9f8d1be1d) #
|
||||||
|
# #
|
||||||
|
# Siteshwar Vashisht <svashisht@redhat.com> #
|
||||||
|
# Kurtis Rader <krader@skepticism.us> #
|
||||||
|
# Johnothan King <johnothanking@protonmail.com> #
|
||||||
|
# Martijn Dekker <martijn@inlv.org> #
|
||||||
|
# #
|
||||||
|
########################################################################
|
||||||
|
|
||||||
|
# Tests for path-bound built-ins from src/lib/libcmd
|
||||||
|
|
||||||
|
. "${SHTESTS_COMMON:-${0%/*}/_common}"
|
||||||
|
|
||||||
|
# ======
|
||||||
|
# Tests for the cp builtin
|
||||||
|
if builtin cp 2> /dev/null; then
|
||||||
|
# The cp builtin's -r/-R flag should not interfere with the -L, -P and -H flags
|
||||||
|
echo 'test file' > "$tmp/cp_testfile"
|
||||||
|
ln -s "$tmp/cp_testfile" "$tmp/symlink1"
|
||||||
|
cp -r "$tmp/symlink1" "$tmp/symlink2"
|
||||||
|
{ test -f "$tmp/symlink2" && test -L "$tmp/symlink2"; } || err_exit "default behavior of 'cp -r' follows symlinks"
|
||||||
|
rm "$tmp/symlink2"
|
||||||
|
cp -R "$tmp/symlink1" "$tmp/symlink2"
|
||||||
|
{ test -f "$tmp/symlink2" && test -L "$tmp/symlink2"; } || err_exit "default behavior of 'cp -R' follows symlinks"
|
||||||
|
rm "$tmp/symlink2"
|
||||||
|
cp -Pr "$tmp/symlink1" "$tmp/symlink2"
|
||||||
|
{ test -f "$tmp/symlink2" && test -L "$tmp/symlink2"; } || err_exit "'cp -Pr' follows symlinks"
|
||||||
|
rm "$tmp/symlink2"
|
||||||
|
cp -PR "$tmp/symlink1" "$tmp/symlink2"
|
||||||
|
{ test -f "$tmp/symlink2" && test -L "$tmp/symlink2"; } || err_exit "'cp -PR' follows symlinks"
|
||||||
|
rm "$tmp/symlink2"
|
||||||
|
cp -rP "$tmp/symlink1" "$tmp/symlink2"
|
||||||
|
{ test -f "$tmp/symlink2" && test -L "$tmp/symlink2"; } || err_exit "'cp -rP' follows symlinks"
|
||||||
|
rm "$tmp/symlink2"
|
||||||
|
cp -RP "$tmp/symlink1" "$tmp/symlink2"
|
||||||
|
{ test -f "$tmp/symlink2" && test -L "$tmp/symlink2"; } || err_exit "'cp -RP' follows symlinks"
|
||||||
|
rm "$tmp/symlink2"
|
||||||
|
cp -Lr "$tmp/symlink1" "$tmp/testfile2"
|
||||||
|
{ test -f "$tmp/testfile2" && ! test -L "$tmp/testfile2"; } || err_exit "'cp -Lr' doesn't follow symlinks"
|
||||||
|
rm "$tmp/testfile2"
|
||||||
|
cp -LR "$tmp/symlink1" "$tmp/testfile2"
|
||||||
|
{ test -f "$tmp/testfile2" && ! test -L "$tmp/testfile2"; } || err_exit "'cp -LR' doesn't follow symlinks"
|
||||||
|
rm "$tmp/testfile2"
|
||||||
|
cp -rL "$tmp/symlink1" "$tmp/testfile2"
|
||||||
|
{ test -f "$tmp/testfile2" && ! test -L "$tmp/testfile2"; } || err_exit "'cp -rL' doesn't follow symlinks"
|
||||||
|
rm "$tmp/testfile2"
|
||||||
|
cp -RL "$tmp/symlink1" "$tmp/testfile2"
|
||||||
|
{ test -f "$tmp/testfile2" && ! test -L "$tmp/testfile2"; } || err_exit "'cp -RL' doesn't follow symlinks"
|
||||||
|
mkdir -p "$tmp/cp_testdir/dir1"
|
||||||
|
ln -s "$tmp/cp_testdir" "$tmp/testdir_symlink"
|
||||||
|
ln -s "$tmp/testfile2" "$tmp/cp_testdir/testfile2_sym"
|
||||||
|
cp -RH "$tmp/testdir_symlink" "$tmp/result"
|
||||||
|
{ test -d "$tmp/result" && ! test -L "$tmp/result"; } || err_exit "'cp -RH' didn't follow the given symlink"
|
||||||
|
{ test -f "$tmp/result/testfile2_sym" && test -L "$tmp/result/testfile2_sym"; } || err_exit "'cp -RH' follows symlinks not given on the command line"
|
||||||
|
rm -r "$tmp/result"
|
||||||
|
cp -rH "$tmp/testdir_symlink" "$tmp/result"
|
||||||
|
{ test -d "$tmp/result" && ! test -L "$tmp/result"; } || err_exit "'cp -rH' didn't follow the given symlink"
|
||||||
|
{ test -f "$tmp/result/testfile2_sym" && test -L "$tmp/result/testfile2_sym"; } || err_exit "'cp -rH' follows symlinks not given on the command line"
|
||||||
|
rm -r "$tmp/result"
|
||||||
|
cp -Hr "$tmp/testdir_symlink" "$tmp/result"
|
||||||
|
{ test -d "$tmp/result" && ! test -L "$tmp/result"; } || err_exit "'cp -Hr' didn't follow the given symlink"
|
||||||
|
{ test -f "$tmp/result/testfile2_sym" && test -L "$tmp/result/testfile2_sym"; } || err_exit "'cp -Hr' follows symlinks not given on the command line"
|
||||||
|
rm -r "$tmp/result"
|
||||||
|
cp -HR "$tmp/testdir_symlink" "$tmp/result"
|
||||||
|
{ test -d "$tmp/result" && ! test -L "$tmp/result"; } || err_exit "'cp -HR' didn't follow the given symlink"
|
||||||
|
{ test -f "$tmp/result/testfile2_sym" && test -L "$tmp/result/testfile2_sym"; } || err_exit "'cp -HR' follows symlinks not given on the command line"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ======
|
||||||
|
# Tests for the head builtin
|
||||||
|
if builtin head 2> /dev/null; then
|
||||||
|
cat > "$tmp/file1" <<-EOF
|
||||||
|
This is line 1 in file1
|
||||||
|
This is line 2 in file1
|
||||||
|
This is line 3 in file1
|
||||||
|
This is line 4 in file1
|
||||||
|
This is line 5 in file1
|
||||||
|
This is line 6 in file1
|
||||||
|
This is line 7 in file1
|
||||||
|
This is line 8 in file1
|
||||||
|
This is line 9 in file1
|
||||||
|
This is line 10 in file1
|
||||||
|
This is line 11 in file1
|
||||||
|
This is line 12 in file1
|
||||||
|
EOF
|
||||||
|
|
||||||
|
cat > "$tmp/file2" <<-EOF2
|
||||||
|
This is line 1 in file2
|
||||||
|
This is line 2 in file2
|
||||||
|
This is line 3 in file2
|
||||||
|
This is line 4 in file2
|
||||||
|
This is line 5 in file2
|
||||||
|
EOF2
|
||||||
|
|
||||||
|
# -*n*; i.e., an integer presented as a flag.
|
||||||
|
#
|
||||||
|
# The `awk` invocation is to strip whitespace around the output of `wc` since it might pad the
|
||||||
|
# value.
|
||||||
|
exp=11
|
||||||
|
got=$(head -11 < "$tmp/file1" | wc -l | awk '{ print $1 }')
|
||||||
|
[[ $got == "$exp" ]] || err_exit "'head -n' failed (expected $(printf %q "$exp"), got $(printf %q "$got"))"
|
||||||
|
|
||||||
|
# -n, --lines=lines
|
||||||
|
# Copy lines lines from each file. The default value is 10.
|
||||||
|
got=$(head -n 3 "$tmp/file1")
|
||||||
|
exp=$'This is line 1 in file1\nThis is line 2 in file1\nThis is line 3 in file1'
|
||||||
|
[[ $got == "$exp" ]] || err_exit "'head -n' failed (expected $(printf %q "$exp"), got $(printf %q "$got"))"
|
||||||
|
|
||||||
|
# -c, --bytes=chars
|
||||||
|
# Copy chars bytes from each file.
|
||||||
|
got=$(head -c 14 "$tmp/file1")
|
||||||
|
exp=$'This is line 1'
|
||||||
|
[[ $got == "$exp" ]] || err_exit "'head -c' failed (expected $(printf %q "$exp"), got $(printf %q "$got"))"
|
||||||
|
|
||||||
|
# -q, --quiet|silent
|
||||||
|
# Never output filename headers.
|
||||||
|
got=$(head -q -n 3 "$tmp/file1" "$tmp/file2")
|
||||||
|
exp=$'This is line 1 in file1\nThis is line 2 in file1\nThis is line 3 in file1\nThis is line 1 in file2\nThis is line 2 in file2\nThis is line 3 in file2'
|
||||||
|
[[ $got == "$exp" ]] || err_exit "'head -q' failed (expected $(printf %q "$exp"), got $(printf %q "$got"))"
|
||||||
|
|
||||||
|
# -s, --skip=char Skip char characters or lines from each file before copying.
|
||||||
|
got=$(head -s 5 -c 18 "$tmp/file1")
|
||||||
|
exp=$'is line 1 in file1'
|
||||||
|
[[ $got == "$exp" ]] || err_exit "'head -s' failed (expected $(printf %q "$exp"), got $(printf %q "$got"))"
|
||||||
|
|
||||||
|
# -v, --verbose Always output filename headers.
|
||||||
|
got=$(head -v -n 3 "$tmp/file1")
|
||||||
|
exp=$'file1 <==\nThis is line 1 in file1\nThis is line 2 in file1\nThis is line 3 in file1'
|
||||||
|
[[ $got =~ "$exp" ]] || err_exit "'head -v' failed (expected $(printf %q "$exp"), got $(printf %q "$got"))"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ======
|
||||||
|
# Tests for the wc builtin
|
||||||
|
# wc - print the number of bytes, words, and lines in files
|
||||||
|
if builtin wc 2> /dev/null; then
|
||||||
|
cat > "$tmp/file1" <<-EOF
|
||||||
|
This is line 1 in file1
|
||||||
|
This is line 2 in file1
|
||||||
|
This is line 3 in file1
|
||||||
|
This is line 4 in file1
|
||||||
|
This is line 5 in file1
|
||||||
|
EOF
|
||||||
|
|
||||||
|
cat > "$tmp/file2" <<-EOF
|
||||||
|
This is line 1 in file2
|
||||||
|
This is line 2 in file2
|
||||||
|
This is line 3 in file2
|
||||||
|
This is line 4 in file2
|
||||||
|
This is line 5 in file2
|
||||||
|
This is the longest line in file2
|
||||||
|
神
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# -l, --lines List the line counts.
|
||||||
|
got=$(wc -l "$tmp/file1")
|
||||||
|
exp=$" 5 $tmp/file1"
|
||||||
|
[[ $got == "$exp" ]] || err_exit "'wc -l' failed (expected $(printf %q "$exp"), got $(printf %q "$got"))"
|
||||||
|
|
||||||
|
# -w, --words List the word counts.
|
||||||
|
got=$(wc -w "$tmp/file1")
|
||||||
|
exp=$" 30 $tmp/file1"
|
||||||
|
[[ $got == "$exp" ]] || err_exit "'wc -w' failed (expected $(printf %q "$exp"), got $(printf %q "$got"))"
|
||||||
|
|
||||||
|
# -c, --bytes|chars
|
||||||
|
# List the byte counts.
|
||||||
|
got=$(wc -c "$tmp/file1")
|
||||||
|
exp=$" 120 $tmp/file1"
|
||||||
|
[[ $got == "$exp" ]] || err_exit "'wc -c' failed (expected $(printf %q "$exp"), got $(printf %q "$got"))"
|
||||||
|
|
||||||
|
if ((SHOPT_MULTIBYTE)) && [[ ${LC_ALL:-${LC_CTYPE:-${LANG:-}}} =~ [Uu][Tt][Ff]-?8 ]]; then
|
||||||
|
# -m|C, --multibyte-chars
|
||||||
|
# List the character counts.
|
||||||
|
got=$(wc -m "$tmp/file2")
|
||||||
|
exp=$" 156 $tmp/file2"
|
||||||
|
[[ $got == "$exp" ]] || err_exit "'wc -m' failed (expected $(printf %q "$exp"), got $(printf %q "$got"))"
|
||||||
|
got=$(wc -C "$tmp/file2")
|
||||||
|
exp=$" 156 $tmp/file2"
|
||||||
|
[[ $got == "$exp" ]] || err_exit "'wc -C' failed (expected $(printf %q "$exp"), got $(printf %q "$got"))"
|
||||||
|
|
||||||
|
# -q, --quiet Suppress invalid multibyte character warnings.
|
||||||
|
got=$(wc -q -m "$tmp/file2")
|
||||||
|
exp=$" 156 $tmp/file2"
|
||||||
|
[[ $got == "$exp" ]] || err_exit "'wc -q -m' failed (expected $(printf %q "$exp"), got $(printf %q "$got"))"
|
||||||
|
got=$(wc -q -C "$tmp/file2")
|
||||||
|
exp=$" 156 $tmp/file2"
|
||||||
|
[[ $got == "$exp" ]] || err_exit "'wc -q -C' failed (expected $(printf %q "$exp"), got $(printf %q "$got"))"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# -L, --longest-line|max-line-length
|
||||||
|
# List the longest line length; the newline,if any, is not
|
||||||
|
# counted in the length.
|
||||||
|
got=$(wc -L "$tmp/file2")
|
||||||
|
exp=$" 33 $tmp/file2"
|
||||||
|
[[ $got == "$exp" ]] || err_exit "'wc -l' failed (expected $(printf %q "$exp"), got $(printf %q "$got"))"
|
||||||
|
|
||||||
|
# -N, --utf8 For UTF-8 locales --noutf8 disables UTF-8 optimzations and
|
||||||
|
# relies on the native mbtowc(3). On by default; -N means
|
||||||
|
# --noutf8.
|
||||||
|
got=$(wc -N "$tmp/file2")
|
||||||
|
exp=" 7 38 158 $tmp/file2"
|
||||||
|
[[ $got == "$exp" ]] || err_exit "'wc -N' failed (expected $(printf %q "$exp"), got $(printf %q "$got"))"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ======
|
||||||
|
# The rm builtin's -d option should remove files and empty directories without
|
||||||
|
# removing non-empty directories (unless the -r option is also passed).
|
||||||
|
# https://www.austingroupbugs.net/view.php?id=802
|
||||||
|
if builtin rm 2> /dev/null; then
|
||||||
|
echo foo > "$tmp/bar"
|
||||||
|
mkdir "$tmp/emptydir"
|
||||||
|
mkdir -p "$tmp/nonemptydir1/subfolder"
|
||||||
|
mkdir "$tmp/nonemptydir2"
|
||||||
|
echo dummyfile > "$tmp/nonemptydir2/shouldexist"
|
||||||
|
|
||||||
|
# Tests for lone -d option
|
||||||
|
got=$(rm -d "$tmp/emptydir" 2>&1) || err_exit 'rm builtin fails to remove empty directory with -d option' \
|
||||||
|
"(got $(printf %q "$got"))"
|
||||||
|
[[ -d $tmp/emptydir ]] && err_exit 'rm builtin fails to remove empty directory with -d option'
|
||||||
|
got=$(rm -d $tmp/bar 2>&1) || err_exit 'rm builtin fails to remove files with -d option' \
|
||||||
|
"(got $(printf %q "$got"))"
|
||||||
|
[[ -f $tmp/bar ]] && err_exit 'rm builtin fails to remove files with -d option'
|
||||||
|
rm -d "$tmp/nonemptydir1" 2> /dev/null
|
||||||
|
[[ ! -d $tmp/nonemptydir1/subfolder ]] && err_exit 'rm builtin has unwanted recursion with -d option on folder containing folder'
|
||||||
|
rm -d "$tmp/nonemptydir2" 2> /dev/null
|
||||||
|
[[ ! -f $tmp/nonemptydir2/shouldexist ]] && err_exit 'rm builtin has unwanted recursion with -d option on folder containing file'
|
||||||
|
|
||||||
|
# Recreate non-empty directories in case the above tests failed
|
||||||
|
mkdir -p "$tmp/nonemptydir1/subfolder"
|
||||||
|
mkdir -p "$tmp/nonemptydir2"
|
||||||
|
echo dummyfile > "$tmp/nonemptydir2/shouldexist"
|
||||||
|
|
||||||
|
# Tests combining -d with -r
|
||||||
|
got=$(rm -rd "$tmp/nonemptydir1" 2>&1) \
|
||||||
|
|| err_exit 'rm builtin fails to remove non-empty directory and subdirectory with -rd options' \
|
||||||
|
"(got $(printf %q "$got"))"
|
||||||
|
[[ -d $tmp/nonemptydir1/subfolder || -d $tmp/nonemptydir1 ]] \
|
||||||
|
&& err_exit 'rm builtin fails to remove all folders with -rd options'
|
||||||
|
got=$(rm -rd "$tmp/nonemptydir2" 2>&1) \
|
||||||
|
|| err_exit 'rm builtin fails to remove non-empty directory and file with -rd options' \
|
||||||
|
"(got $(printf %q "$got"))"
|
||||||
|
[[ -f $tmp/nonemptydir2/shouldexist || -d $tmp/nonemptydir2 ]] \
|
||||||
|
&& err_exit 'rm builtin fails to remove all folders and files with -rd options'
|
||||||
|
|
||||||
|
# Repeat the above tests with -R instead of -r (because of possible optget bugs)
|
||||||
|
mkdir -p "$tmp/nonemptydir1/subfolder"
|
||||||
|
mkdir -p "$tmp/nonemptydir2"
|
||||||
|
echo dummyfile > "$tmp/nonemptydir2/shouldexist"
|
||||||
|
got=$(rm -Rd "$tmp/nonemptydir1" 2>&1) \
|
||||||
|
|| err_exit 'rm builtin fails to remove non-empty directory and subdirectory with -Rd options' \
|
||||||
|
"(got $(printf %q "$got"))"
|
||||||
|
[[ -d $tmp/nonemptydir1/subfolder || -d $tmp/nonemptydir1 ]] \
|
||||||
|
&& err_exit 'rm builtin fails to remove all folders with -Rd options'
|
||||||
|
got=$(rm -Rd "$tmp/nonemptydir2" 2>&1) \
|
||||||
|
|| err_exit 'rm builtin fails to remove non-empty directory and file with -Rd options' \
|
||||||
|
"(got $(printf %q "$got"))"
|
||||||
|
[[ -f $tmp/nonemptydir2/shouldexist || -d $tmp/nonemptydir2 ]] \
|
||||||
|
&& err_exit 'rm builtin fails to remove all folders and files with -Rd options'
|
||||||
|
|
||||||
|
# Additional test: 'rm -f' without additional arguments should act
|
||||||
|
# as a no-op command. This bug was fixed in ksh93u+ 2012-02-14.
|
||||||
|
got=$(rm -f 2>&1)
|
||||||
|
if (($? != 0)) || [[ ! -z $got ]]
|
||||||
|
then err_exit 'rm -f without additional arguments does not work correctly' \
|
||||||
|
"(got $(printf %q "$got"))"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ======
|
||||||
|
# Regression test for https://github.com/att/ast/issues/949
|
||||||
|
if builtin chmod 2>/dev/null; then
|
||||||
|
foo_script='exit 0'
|
||||||
|
echo "$foo_script" > "$tmp/foo1.sh"
|
||||||
|
echo "$foo_script" > "$tmp/foo2.sh"
|
||||||
|
chmod +x "$tmp/foo1.sh" "$tmp/foo2.sh"
|
||||||
|
"$tmp/foo1.sh" || err_exit "builtin 'chmod +x' doesn't work on first script"
|
||||||
|
"$tmp/foo2.sh" || err_exit "builtin 'chmod +x' doesn't work on second script"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ======
|
||||||
|
# https://github.com/ksh93/ksh/issues/138
|
||||||
|
builtin -d cat
|
||||||
|
if [[ $'\n'${ builtin; }$'\n' == *$'\n/opt/ast/bin/cat\n'* ]]
|
||||||
|
then exp=' version cat (*) ????-??-??'
|
||||||
|
got=$(/opt/ast/bin/cat --version 2>&1)
|
||||||
|
[[ $got == $exp ]] || err_exit "path-bound builtin not executable by literal canonical path" \
|
||||||
|
"(expected match of $(printf %q "$exp"), got $(printf %q "$got"))"
|
||||||
|
got=$(PATH=/opt/ast/bin:$PATH; "${ whence -p cat; }" --version 2>&1)
|
||||||
|
[[ $got == $exp ]] || err_exit "path-bound builtin not executable by canonical path resulting from expansion" \
|
||||||
|
"(expected match of $(printf %q "$exp"), got $(printf %q "$got"))"
|
||||||
|
got=$(PATH=/opt/ast/bin:$PATH; "$SHELL" -o restricted -c 'cat --version' 2>&1)
|
||||||
|
[[ $got == $exp ]] || err_exit "restricted shells do not recognize path-bound builtins" \
|
||||||
|
"(expected match of $(printf %q "$exp"), got $(printf %q "$got"))"
|
||||||
|
got=$(set +x; PATH=/opt/ast/bin cat --version 2>&1)
|
||||||
|
[[ $got == $exp ]] || err_exit "path-bound builtin not found on PATH in preceding assignment" \
|
||||||
|
"(expected match of $(printf %q "$exp"), got $(printf %q "$got"))"
|
||||||
|
else warning 'skipping path-bound builtin tests: builtin /opt/ast/bin/cat not found'
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ======
|
||||||
|
# The head and tail builtins should work on files without newlines
|
||||||
|
if builtin head 2> /dev/null; then
|
||||||
|
print -n nonewline > "$tmp/nonewline"
|
||||||
|
exp=nonewline
|
||||||
|
got=$(head -1 "$tmp/nonewline")
|
||||||
|
[[ $got == $exp ]] || err_exit "head builtin fails to correctly handle files without an ending newline" \
|
||||||
|
"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
|
||||||
|
fi
|
||||||
|
if builtin tail 2> /dev/null; then
|
||||||
|
print -n 'newline\nnonewline' > "$tmp/nonewline"
|
||||||
|
exp=nonewline
|
||||||
|
got=$(tail -1 "$tmp/nonewline")
|
||||||
|
[[ $got == $exp ]] || err_exit "tail builtin fails to correctly handle files without an ending newline" \
|
||||||
|
"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ======
|
||||||
|
exit $((Errors<125?Errors:125))
|
|
@ -4549,6 +4549,8 @@ optget(register char** argv, const char* oopts)
|
||||||
{
|
{
|
||||||
if (cache)
|
if (cache)
|
||||||
{
|
{
|
||||||
|
if (c >= 0 && c < sizeof(map) && map[c] && cache->equiv[map[c]])
|
||||||
|
c = cache->equiv[map[c]];
|
||||||
if (k = cache->flags[map[c]])
|
if (k = cache->flags[map[c]])
|
||||||
{
|
{
|
||||||
opt_info.arg = 0;
|
opt_info.arg = 0;
|
||||||
|
@ -4979,6 +4981,7 @@ optget(register char** argv, const char* oopts)
|
||||||
v = f;
|
v = f;
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
|
char eqv;
|
||||||
if (isdigit(*f) && isdigit(*(f + 1)))
|
if (isdigit(*f) && isdigit(*(f + 1)))
|
||||||
while (isdigit(*(f + 1)))
|
while (isdigit(*(f + 1)))
|
||||||
f++;
|
f++;
|
||||||
|
@ -4987,12 +4990,17 @@ optget(register char** argv, const char* oopts)
|
||||||
else
|
else
|
||||||
cache->flags[map[*f]] = m;
|
cache->flags[map[*f]] = m;
|
||||||
j = 0;
|
j = 0;
|
||||||
|
/*
|
||||||
|
* parse and cache short option equivalents,
|
||||||
|
* e.g. x|y|z means -y and -z yield -x
|
||||||
|
*/
|
||||||
|
eqv = *f;
|
||||||
while (*(f + 1) == '|')
|
while (*(f + 1) == '|')
|
||||||
{
|
{
|
||||||
f += 2;
|
f += 2;
|
||||||
if (!(j = *f) || j == '!' || j == '=' || j == ':' || j == '?' || j == ']')
|
if (!(j = *f) || j == '!' || j == '=' || j == ':' || j == '?' || j == ']')
|
||||||
break;
|
break;
|
||||||
cache->flags[map[j]] = m;
|
cache->equiv[map[j]] = eqv;
|
||||||
}
|
}
|
||||||
if (j != '!' || (m & OPT_cache_invert))
|
if (j != '!' || (m & OPT_cache_invert))
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -71,6 +71,7 @@ typedef struct Optcache_s
|
||||||
Optpass_t pass;
|
Optpass_t pass;
|
||||||
int caching;
|
int caching;
|
||||||
unsigned char flags[sizeof(OPT_FLAGS)];
|
unsigned char flags[sizeof(OPT_FLAGS)];
|
||||||
|
char equiv[sizeof(OPT_FLAGS)]; /* short option equivalents */
|
||||||
} Optcache_t;
|
} Optcache_t;
|
||||||
|
|
||||||
typedef struct Optstate_s
|
typedef struct Optstate_s
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static const char usage_head[] =
|
static const char usage_head[] =
|
||||||
"[-?@(#)$Id: cp (AT&T Research) 2012-04-20 $\n]"
|
"[-?@(#)$Id: cp (ksh 93u+m) 2022-08-20 $\n]"
|
||||||
"[--catalog?" ERROR_CATALOG "]"
|
"[--catalog?" ERROR_CATALOG "]"
|
||||||
;
|
;
|
||||||
|
|
||||||
|
@ -806,8 +806,12 @@ b_cp(int argc, register char** argv, Shbltin_t* context)
|
||||||
continue;
|
continue;
|
||||||
case 'r':
|
case 'r':
|
||||||
state->recursive = 1;
|
state->recursive = 1;
|
||||||
if (path_resolve < 0)
|
if (path_resolve < 1)
|
||||||
path_resolve = 0;
|
{
|
||||||
|
state->flags &= ~FTS_META;
|
||||||
|
state->flags |= FTS_PHYSICAL;
|
||||||
|
path_resolve = 1;
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
case 's':
|
case 's':
|
||||||
state->op = LN;
|
state->op = LN;
|
||||||
|
@ -847,12 +851,6 @@ b_cp(int argc, register char** argv, Shbltin_t* context)
|
||||||
state->flags |= FTS_PHYSICAL;
|
state->flags |= FTS_PHYSICAL;
|
||||||
path_resolve = 1;
|
path_resolve = 1;
|
||||||
continue;
|
continue;
|
||||||
case 'R':
|
|
||||||
state->recursive = 1;
|
|
||||||
state->flags &= ~FTS_META;
|
|
||||||
state->flags |= FTS_PHYSICAL;
|
|
||||||
path_resolve = 1;
|
|
||||||
continue;
|
|
||||||
case 'S':
|
case 'S':
|
||||||
state->suffix = opt_info.arg;
|
state->suffix = opt_info.arg;
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static const char usage[] =
|
static const char usage[] =
|
||||||
"[-?\n@(#)$Id: cut (AT&T Research) 2010-08-11 $\n]"
|
"[-?\n@(#)$Id: cut (ksh 93u_m) 2022-08-20 $\n]"
|
||||||
"[--catalog?" ERROR_CATALOG "]"
|
"[--catalog?" ERROR_CATALOG "]"
|
||||||
"[+NAME?cut - cut out selected columns or fields of each line of a file]"
|
"[+NAME?cut - cut out selected columns or fields of each line of a file]"
|
||||||
"[+DESCRIPTION?\bcut\b bytes, characters, or character-delimited fields "
|
"[+DESCRIPTION?\bcut\b bytes, characters, or character-delimited fields "
|
||||||
|
@ -655,7 +655,6 @@ b_cut(int argc, char** argv, Shbltin_t* context)
|
||||||
mode |= C_NONEWLINE;
|
mode |= C_NONEWLINE;
|
||||||
continue;
|
continue;
|
||||||
case 'R':
|
case 'R':
|
||||||
case 'r':
|
|
||||||
if(opt_info.num>0)
|
if(opt_info.num>0)
|
||||||
reclen = opt_info.num;
|
reclen = opt_info.num;
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static const char usage[] =
|
static const char usage[] =
|
||||||
"[-?\n@(#)$Id: rm (AT&T Research) 2013-12-01 $\n]"
|
"[-?\n@(#)$Id: rm (ksh 93u+m) 2022-08-20 $\n]"
|
||||||
"[--catalog?" ERROR_CATALOG "]"
|
"[--catalog?" ERROR_CATALOG "]"
|
||||||
"[+NAME?rm - remove files]"
|
"[+NAME?rm - remove files]"
|
||||||
"[+DESCRIPTION?\brm\b removes the named \afile\a arguments. By default it"
|
"[+DESCRIPTION?\brm\b removes the named \afile\a arguments. By default it"
|
||||||
|
@ -347,10 +347,9 @@ b_rm(int argc, register char** argv, Shbltin_t* context)
|
||||||
state.force = 0;
|
state.force = 0;
|
||||||
continue;
|
continue;
|
||||||
case 'r':
|
case 'r':
|
||||||
case 'R':
|
|
||||||
state.recursive = 1;
|
state.recursive = 1;
|
||||||
continue;
|
continue;
|
||||||
case 'F':
|
case 'c':
|
||||||
#if _lib_fsync
|
#if _lib_fsync
|
||||||
state.clobber = 1;
|
state.clobber = 1;
|
||||||
#else
|
#else
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static const char usage[] =
|
static const char usage[] =
|
||||||
"[-?\n@(#)$Id: wc (AT&T Research) 2009-11-28 $\n]"
|
"[-?\n@(#)$Id: wc (ksh 93u+m) 2022-08-20 $\n]"
|
||||||
"[--catalog?" ERROR_CATALOG "]"
|
"[--catalog?" ERROR_CATALOG "]"
|
||||||
"[+NAME?wc - print the number of bytes, words, and lines in files]"
|
"[+NAME?wc - print the number of bytes, words, and lines in files]"
|
||||||
"[+DESCRIPTION?\bwc\b reads one or more input files and, by default, "
|
"[+DESCRIPTION?\bwc\b reads one or more input files and, by default, "
|
||||||
|
@ -111,7 +111,6 @@ b_wc(int argc,register char **argv, Shbltin_t* context)
|
||||||
mode |= WC_NOUTF8;
|
mode |= WC_NOUTF8;
|
||||||
continue;
|
continue;
|
||||||
case 'm':
|
case 'm':
|
||||||
case 'C':
|
|
||||||
mode |= WC_MBYTE;
|
mode |= WC_MBYTE;
|
||||||
continue;
|
continue;
|
||||||
case 'q':
|
case 'q':
|
||||||
|
|
Loading…
Reference in a new issue