mirror of
				git://git.code.sf.net/p/cdesktopenv/code
				synced 2025-03-09 15:50:02 +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…
	
	Add table
		Add a link
		
	
		Reference in a new issue