mksh R40 Release Candidate 1

Testsuite:
• add new need-pass: {yes|no} attribute, default yes
• exit with 1 if a need-pass test failed unexpectedly
  idea by Kacper Kornet <draenog@pld-linux.org>
• mark utf8bom-2 as need-pass: no
Infrstructure:
• add housekeeping function for making a tty raw
• switch functions with unused results to void
• struct op: u.charflag contains last char of ;; in TPAT
• var.c:arraysearch is now a global function
Language:
• add ;& (fall through) and ;| (examine next) delimiters
  in addition to ;; (end case) as zsh extensions, because
  POSIX standardised on ;& already
• add -A (read into array), -N (read exactly n bytes),
  -n (read up to n bytes), -t (timeout) flags for read
  from ksh93
• allow read -N -1 or -n -1 to slurp the entire input
• add -a (read into array the input characters) extension
  specific to mksh to read, idea by David Korn
• add -e (exit with error if PWD was not set correctly
  after a physical cd) to cd builtin, mandated by next
  POSIX, and change error codes accordingly
Rewrites:
• full rewrite of read builtin and its manpage section
• add regression tetss for most of the new functionality
• duplicate hexdump demo tests for use of read -a
• use read -raN-1 in dot.mkshrc to get NUL safe base64,
  DJB cdb hash and Jenkins one-at-a-time hash functions
This commit is contained in:
tg
2011-05-29 02:18:57 +00:00
parent 9fe3ba48d2
commit 2cfc3e5c3d
13 changed files with 971 additions and 319 deletions

View File

@ -1,4 +1,4 @@
# $MirOS: src/bin/mksh/check.pl,v 1.26 2011/03/28 21:58:05 tg Exp $
# $MirOS: src/bin/mksh/check.pl,v 1.27 2011/05/29 02:18:47 tg Exp $
# $OpenBSD: th,v 1.13 2006/05/18 21:27:23 miod Exp $
#-
# Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2011
@ -197,6 +197,7 @@ EOF
'expected-stderr-pattern', 'm',
'category', 'm',
'need-ctty', '',
'need-pass', '',
);
# Filled in by read_test()
%internal_test_fields = (
@ -217,6 +218,7 @@ $tempe = "/tmp/rte$$";
$tempdir = "/tmp/rtd$$";
$nfailed = 0;
$nifailed = 0;
$nxfailed = 0;
$npassed = 0;
$nxpassed = 0;
@ -304,12 +306,13 @@ if (-d $test_set) {
}
&cleanup_exit() if !defined $ret;
$tot_failed = $nfailed + $nxfailed;
$tot_failed = $nfailed + $nifailed + $nxfailed;
$tot_passed = $npassed + $nxpassed;
if ($tot_failed || $tot_passed) {
print "Total failed: $tot_failed";
print " ($nifailed ignored)" if $nifailed;
print " ($nxfailed unexpected)" if $nxfailed;
print " (as expected)" if $nfailed && !$nxfailed;
print " (as expected)" if $nfailed && !$nxfailed && !$nifailed;
print "\nTotal passed: $tot_passed";
print " ($nxpassed unexpected)" if $nxpassed;
print "\n";
@ -323,7 +326,11 @@ cleanup_exit
local($sig, $exitcode) = ('', 1);
if ($_[0] eq 'ok') {
$exitcode = 0;
unless ($nxfailed) {
$exitcode = 0;
} else {
$exitcode = 1;
}
} elsif ($_[0] ne '') {
$sig = $_[0];
}
@ -620,8 +627,13 @@ run_test
if ($failed) {
if (!$test{'expected-fail'}) {
print "FAIL $name\n";
$nxfailed++;
if ($test{'need-pass'}) {
print "FAIL $name\n";
$nxfailed++;
} else {
print "FAIL $name (ignored)\n";
$nifailed++;
}
} else {
print "fail $name (as expected)\n";
$nfailed++;
@ -1079,6 +1091,16 @@ read_test
} else {
$test{'need-ctty'} = 0;
}
if (defined $test{'need-pass'}) {
if ($test{'need-pass'} !~ /^(yes|no)$/) {
print STDERR
"$prog:$test{':long-name'}: bad value for need-pass field\n";
return undef;
}
$test{'need-pass'} = $1 eq 'yes';
} else {
$test{'need-pass'} = 1;
}
if (defined $test{'arguments'}) {
local($firstc) = substr($test{'arguments'}, 0, 1);

316
check.t
View File

@ -1,4 +1,4 @@
# $MirOS: src/bin/mksh/check.t,v 1.454 2011/05/07 02:02:45 tg Exp $
# $MirOS: src/bin/mksh/check.t,v 1.455 2011/05/29 02:18:47 tg Exp $
# $OpenBSD: bksl-nl.t,v 1.2 2001/01/28 23:04:56 niklas Exp $
# $OpenBSD: history.t,v 1.5 2001/01/28 23:04:56 niklas Exp $
# $OpenBSD: read.t,v 1.3 2003/03/10 03:48:16 david Exp $
@ -25,7 +25,7 @@
# http://www.research.att.com/~gsf/public/ifs.sh
expected-stdout:
@(#)MIRBSD KSH R39 2011/05/06
@(#)MIRBSD KSH R39 2011/05/28
description:
Check version of shell.
stdin:
@ -1005,6 +1005,43 @@ expected-stdout:
1 /bin
0 /tmp
---
name: cd-pe
description:
Check package for cd -Pe
need-pass: no
file-setup: file 644 "x"
mkdir noread noread/target noread/target/subdir
ln -s noread link
chmod 311 noread
cd -P$1 .
echo 0=$?
bwd=$PWD
cd -P$1 link/target
echo 1=$?,${PWD#$bwd/}
epwd=$($TSHELL -c pwd 2>/dev/null)
echo pwd=$?,$epwd
mv ../../noread ../../renamed
cd -P$1 subdir
echo 2=$?,${PWD#$bwd/}
cd $bwd
chmod 755 renamed
rm -rf noread link renamed
stdin:
export TSHELL="$__progname"
"$__progname" x
echo "now with -e:"
"$__progname" x e
expected-stdout:
0=0
1=0,noread/target
pwd=1,
2=0,noread/target/subdir
now with -e:
0=0
1=0,noread/target
pwd=1,
2=1,noread/target/subdir
---
name: env-prompt
description:
Check that prompt not printed when processing ENV
@ -3764,6 +3801,34 @@ expected-stdout:
<ch
myok m>
---
name: read-ext-1
description:
Check read with number of bytes specified, and -A
stdin:
print 'foo\nbar' >x1
print -n x >x2
print 'foo\\ bar baz' >x3
x1a=u; read x1a <x1
x1b=u; read -N-1 x1b <x1
x2a=u; read x2a <x2; r2a=$?
x2b=u; read -N2 x2c <x2; r2b=$?
x2c=u; read -n2 x2c <x2; r2c=$?
x3a=u; read -A x3a <x3
print -r "x1a=<$x1a>"
print -r "x1b=<$x1b>"
print -r "x2a=$r2a<$x2a>"
print -r "x2b=$r2b<$x2b>"
print -r "x2c=$r2c<$x2c>"
print -r "x3a=<${x3a[0]}|${x3a[1]}|${x3a[2]}>"
expected-stdout:
x1a=<foo>
x1b=<foo
bar>
x2a=1<x>
x2b=1<u>
x2c=0<x>
x3a=<foo bar|baz|>
---
name: regression-1
description:
Lex array code had problems with this.
@ -5722,6 +5787,7 @@ description:
XXX if the OS can already execute them, we lose
note: cygwin execve(2) doesn't return to us with ENOEXEC, we lose
note: Ultrix perl5 t4 returns 65280 (exit-code 255) and no text
need-pass: no
category: !os:cygwin,!os:uwin-nt,!os:ultrix,!smksh
env-setup: !FOO=BAR!
stdin:
@ -6822,7 +6888,7 @@ expected-stderr-pattern:
/1#<23><><EFBFBD>: unexpected '<27>'/
expected-exit: e != 0
---
name: integer-base-one-3A
name: integer-base-one-3As
description:
some sample code for hexdumping
stdin:
@ -6889,7 +6955,7 @@ expected-stdout:
00000110 EF F0 F1 F2 F3 F4 F5 F6 - F7 F8 F9 FA FB FC FD FE |................|
00000120 FF 0A - |..|
---
name: integer-base-one-3W
name: integer-base-one-3Ws
description:
some sample code for hexdumping Unicode
stdin:
@ -6992,6 +7058,172 @@ expected-stdout:
00000120 000A EFE0 EF80 EF80 - 000A FFFD EFEF EFBF |.<2E><><EFBFBD>.<2E><><EFBFBD>|
00000128 EFBE EFEF EFBF EFBF - 000A |<7C><><EFBFBD><EFBFBD>.|
---
name: integer-base-one-3Ar
description:
some sample code for hexdumping
stdin:
{
print 'Hello, World!\\\nこんにちは'
typeset -Uui16 i=0x100
# change that to 0xFF once we can handle embedded
# NUL characters in strings / here documents
while (( i++ < 0x1FF )); do
print -n "\x${i#16#1}"
done
print
} | {
typeset -Uui16 -Z11 pos=0
typeset -Uui16 -Z5 hv
dasc=
while read -ar line; do
typeset -i1 line
line[${#line[*]}]=10
i=0
while (( i < ${#line[*]} )); do
hv=${line[i++]}
if (( (pos & 15) == 0 )); then
(( pos )) && print "$dasc|"
print -n "${pos#16#} "
dasc=' |'
fi
print -n "${hv#16#} "
if (( (hv < 32) || (hv > 126) )); then
dasc=$dasc.
else
dasc=$dasc${line[i-1]#1#}
fi
(( (pos++ & 15) == 7 )) && print -n -- '- '
done
done
if (( (pos & 15) != 1 )); then
while (( pos & 15 )); do
print -n ' '
(( (pos++ & 15) == 7 )) && print -n -- '- '
done
print "$dasc|"
fi
}
expected-stdout:
00000000 48 65 6C 6C 6F 2C 20 57 - 6F 72 6C 64 21 5C 0A E3 |Hello, World!\..|
00000010 81 93 E3 82 93 E3 81 AB - E3 81 A1 E3 81 AF EF BC |................|
00000020 81 0A 01 02 03 04 05 06 - 07 08 09 0A 0B 0C 0D 0E |................|
00000030 0F 10 11 12 13 14 15 16 - 17 18 19 1A 1B 1C 1D 1E |................|
00000040 1F 20 21 22 23 24 25 26 - 27 28 29 2A 2B 2C 2D 2E |. !"#$%&'()*+,-.|
00000050 2F 30 31 32 33 34 35 36 - 37 38 39 3A 3B 3C 3D 3E |/0123456789:;<=>|
00000060 3F 40 41 42 43 44 45 46 - 47 48 49 4A 4B 4C 4D 4E |?@ABCDEFGHIJKLMN|
00000070 4F 50 51 52 53 54 55 56 - 57 58 59 5A 5B 5C 5D 5E |OPQRSTUVWXYZ[\]^|
00000080 5F 60 61 62 63 64 65 66 - 67 68 69 6A 6B 6C 6D 6E |_`abcdefghijklmn|
00000090 6F 70 71 72 73 74 75 76 - 77 78 79 7A 7B 7C 7D 7E |opqrstuvwxyz{|}~|
000000A0 7F 80 81 82 83 84 85 86 - 87 88 89 8A 8B 8C 8D 8E |................|
000000B0 8F 90 91 92 93 94 95 96 - 97 98 99 9A 9B 9C 9D 9E |................|
000000C0 9F A0 A1 A2 A3 A4 A5 A6 - A7 A8 A9 AA AB AC AD AE |................|
000000D0 AF B0 B1 B2 B3 B4 B5 B6 - B7 B8 B9 BA BB BC BD BE |................|
000000E0 BF C0 C1 C2 C3 C4 C5 C6 - C7 C8 C9 CA CB CC CD CE |................|
000000F0 CF D0 D1 D2 D3 D4 D5 D6 - D7 D8 D9 DA DB DC DD DE |................|
00000100 DF E0 E1 E2 E3 E4 E5 E6 - E7 E8 E9 EA EB EC ED EE |................|
00000110 EF F0 F1 F2 F3 F4 F5 F6 - F7 F8 F9 FA FB FC FD FE |................|
00000120 FF 0A - |..|
---
name: integer-base-one-3Wr
description:
some sample code for hexdumping Unicode
stdin:
set -U
{
print 'Hello, World!\\\nこんにちは'
typeset -Uui16 i=0x100
# change that to 0xFF once we can handle embedded
# NUL characters in strings / here documents
while (( i++ < 0x1FF )); do
print -n "\u${i#16#1}"
done
print
print \\xff # invalid utf-8
print \\xc2 # invalid 2-byte
print \\xef\\xbf\\xc0 # invalid 3-byte
print \\xc0\\x80 # non-minimalistic
print \\xe0\\x80\\x80 # non-minimalistic
print '<27>￾￿' # end of range
} | {
typeset -Uui16 -Z11 pos=0
typeset -Uui16 -Z7 hv
dasc=
while read -ar line; do
typeset -i1 line
line[${#line[*]}]=10
i=0
while (( i < ${#line[*]} )); do
hv=${line[i++]}
if (( (hv < 32) || \
((hv > 126) && (hv < 160)) )); then
dch=.
elif (( (hv & 0xFF80) == 0xEF80 )); then
dch=<EFBFBD>
else
dch=${line[i-1]#1#}
fi
if (( (pos & 7) == 7 )); then
dasc=$dasc$dch
dch=
elif (( (pos & 7) == 0 )); then
(( pos )) && print "$dasc|"
print -n "${pos#16#} "
dasc=' |'
fi
print -n "${hv#16#} "
(( (pos++ & 7) == 3 )) && \
print -n -- '- '
dasc=$dasc$dch
done
done
if (( pos & 7 )); then
while (( pos & 7 )); do
print -n ' '
(( (pos++ & 7) == 3 )) && print -n -- '- '
done
print "$dasc|"
fi
}
expected-stdout:
00000000 0048 0065 006C 006C - 006F 002C 0020 0057 |Hello, W|
00000008 006F 0072 006C 0064 - 0021 005C 000A 3053 |orld!\.|
00000010 3093 306B 3061 306F - FF01 000A 0001 0002 |んにちは...|
00000018 0003 0004 0005 0006 - 0007 0008 0009 000A |........|
00000020 000B 000C 000D 000E - 000F 0010 0011 0012 |........|
00000028 0013 0014 0015 0016 - 0017 0018 0019 001A |........|
00000030 001B 001C 001D 001E - 001F 0020 0021 0022 |..... !"|
00000038 0023 0024 0025 0026 - 0027 0028 0029 002A |#$%&'()*|
00000040 002B 002C 002D 002E - 002F 0030 0031 0032 |+,-./012|
00000048 0033 0034 0035 0036 - 0037 0038 0039 003A |3456789:|
00000050 003B 003C 003D 003E - 003F 0040 0041 0042 |;<=>?@AB|
00000058 0043 0044 0045 0046 - 0047 0048 0049 004A |CDEFGHIJ|
00000060 004B 004C 004D 004E - 004F 0050 0051 0052 |KLMNOPQR|
00000068 0053 0054 0055 0056 - 0057 0058 0059 005A |STUVWXYZ|
00000070 005B 005C 005D 005E - 005F 0060 0061 0062 |[\]^_`ab|
00000078 0063 0064 0065 0066 - 0067 0068 0069 006A |cdefghij|
00000080 006B 006C 006D 006E - 006F 0070 0071 0072 |klmnopqr|
00000088 0073 0074 0075 0076 - 0077 0078 0079 007A |stuvwxyz|
00000090 007B 007C 007D 007E - 007F 0080 0081 0082 |{|}~....|
00000098 0083 0084 0085 0086 - 0087 0088 0089 008A |........|
000000A0 008B 008C 008D 008E - 008F 0090 0091 0092 |........|
000000A8 0093 0094 0095 0096 - 0097 0098 0099 009A |........|
000000B0 009B 009C 009D 009E - 009F 00A0 00A1 00A2 |..... ¡¢|
000000B8 00A3 00A4 00A5 00A6 - 00A7 00A8 00A9 00AA |£¤¥¦§¨©ª|
000000C0 00AB 00AC 00AD 00AE - 00AF 00B0 00B1 00B2 |«¬­®¯°±²|
000000C8 00B3 00B4 00B5 00B6 - 00B7 00B8 00B9 00BA |³´µ¶·¸¹º|
000000D0 00BB 00BC 00BD 00BE - 00BF 00C0 00C1 00C2 |»¼½¾¿ÀÁÂ|
000000D8 00C3 00C4 00C5 00C6 - 00C7 00C8 00C9 00CA |ÃÄÅÆÇÈÉÊ|
000000E0 00CB 00CC 00CD 00CE - 00CF 00D0 00D1 00D2 |ËÌÍÎÏÐÑÒ|
000000E8 00D3 00D4 00D5 00D6 - 00D7 00D8 00D9 00DA |ÓÔÕÖרÙÚ|
000000F0 00DB 00DC 00DD 00DE - 00DF 00E0 00E1 00E2 |ÛÜÝÞßàáâ|
000000F8 00E3 00E4 00E5 00E6 - 00E7 00E8 00E9 00EA |ãäåæçèéê|
00000100 00EB 00EC 00ED 00EE - 00EF 00F0 00F1 00F2 |ëìíîïðñò|
00000108 00F3 00F4 00F5 00F6 - 00F7 00F8 00F9 00FA |óôõö÷øùú|
00000110 00FB 00FC 00FD 00FE - 00FF 000A EFFF 000A |ûüýþÿ.<2E>.|
00000118 EFC2 000A EFEF EFBF - EFC0 000A EFC0 EF80 |<7C>.<2E><><EFBFBD>.<2E><>|
00000120 000A EFE0 EF80 EF80 - 000A FFFD EFEF EFBF |.<2E><><EFBFBD>.<2E><><EFBFBD>|
00000128 EFBE EFEF EFBF EFBF - 000A |<7C><><EFBFBD><EFBFBD>.|
---
name: integer-base-one-4
description:
Check if ksh93-style base-one integers work
@ -7013,6 +7245,32 @@ expected-stdout:
5 97
6 97
---
name: integer-base-one-5A
description:
Check to see that were NUL and Unicode safe
stdin:
set +U
print 'a\0b\xfdz' >x
read -a y <x
set -U
typeset -Uui16 y
print ${y[*]} .
expected-stdout:
16#61 16#0 16#62 16#FD 16#7A .
---
name: integer-base-one-5W
description:
Check to see that were NUL and Unicode safe
stdin:
set -U
print 'a\0b€c' >x
read -a y <x
set +U
typeset -Uui16 y
print ${y[*]} .
expected-stdout:
16#61 16#0 16#62 16#20AC 16#63 .
---
name: ulimit-1
description:
Check if we can use a specific syntax idiom for ulimit
@ -7442,7 +7700,7 @@ stdin:
#TFOR_TTIME
for i in {1,2,3} ; do time echo $i ; done
#TCASE
case $foo in 1) echo eins;; 2) echo zwei ;; *) echo kann net bis drei zählen;; esac
case $foo in 1) echo eins;& 2) echo zwei ;| *) echo kann net bis drei zählen;; esac
#TIF_TBANG_TDBRACKET_TELIF
if ! [[ 1 = 1 ]] ; then echo eins; elif [[ 1 = 2 ]]; then echo zwei ;else echo drei; fi
#TWHILE
@ -7599,32 +7857,32 @@ expected-stdout:
x=$(( for i in {1,2,3} ; do time echo $i ; done ) | tr u x )
}
inline_TCASE() {
case $foo in 1) echo eins;; 2) echo zwei ;; *) echo kann net bis drei zählen;; esac
case $foo in 1) echo eins;& 2) echo zwei ;| *) echo kann net bis drei zählen;; esac
}
inline_TCASE() {
case $foo in
(1)
echo eins
;;
;&
(2)
echo zwei
;;
;|
(*)
echo kann net bis drei zählen
;;
esac
}
function comsub_TCASE { x=$(
case $foo in 1) echo eins;; 2) echo zwei ;; *) echo kann net bis drei zählen;; esac
case $foo in 1) echo eins;& 2) echo zwei ;| *) echo kann net bis drei zählen;; esac
); }
function comsub_TCASE {
x=$(case $foo in (1) echo eins ;; (2) echo zwei ;; (*) echo kann net bis drei zählen ;; esac )
x=$(case $foo in (1) echo eins ;& (2) echo zwei ;| (*) echo kann net bis drei zählen ;; esac )
}
function reread_TCASE { x=$((
case $foo in 1) echo eins;; 2) echo zwei ;; *) echo kann net bis drei zählen;; esac
case $foo in 1) echo eins;& 2) echo zwei ;| *) echo kann net bis drei zählen;; esac
)|tr u x); }
function reread_TCASE {
x=$(( case $foo in (1) echo eins ;; (2) echo zwei ;; (*) echo kann net bis drei zählen ;; esac ) | tr u x )
x=$(( case $foo in (1) echo eins ;& (2) echo zwei ;| (*) echo kann net bis drei zählen ;; esac ) | tr u x )
}
inline_TIF_TBANG_TDBRACKET_TELIF() {
if ! [[ 1 = 1 ]] ; then echo eins; elif [[ 1 = 2 ]]; then echo zwei ;else echo drei; fi
@ -8845,3 +9103,37 @@ stdin:
expected-stdout:
0
---
name: case-zsh
description:
Check that zsh case variants work
stdin:
case 'b' in
a) echo a ;;
b) echo b ;;
c) echo c ;;
*) echo x ;;
esac
echo =
case 'b' in
a) echo a ;&
b) echo b ;&
c) echo c ;&
*) echo x ;&
esac
echo =
case 'b' in
a) echo a ;|
b) echo b ;|
c) echo c ;|
*) echo x ;|
esac
expected-stdout:
b
=
b
c
x
=
b
x
---

View File

@ -1,5 +1,5 @@
# $Id$
# $MirOS: src/bin/mksh/dot.mkshrc,v 1.59 2011/02/09 19:32:35 tg Exp $
# $MirOS: src/bin/mksh/dot.mkshrc,v 1.60 2011/05/29 02:18:49 tg Exp $
#-
# Copyright (c) 2002, 2003, 2004, 2006, 2007, 2008, 2009, 2010, 2011
# Thorsten Glaser <tg@mirbsd.org>
@ -222,7 +222,7 @@ function smores {
print -r -- "$line"
done
}
# base64 encoder and decoder, RFC compliant, NUL safe
function Lb64decode {
[[ -o utf8-mode ]]; typeset u=$?
@ -262,18 +262,20 @@ set -A Lb64encode_code -- A B C D E F G H I J K L M N O P Q R S T U V W X Y Z \
a b c d e f g h i j k l m n o p q r s t u v w x y z 0 1 2 3 4 5 6 7 8 9 + /
function Lb64encode {
[[ -o utf8-mode ]]; typeset u=$?
set +U
typeset c s="$*" t
[[ -n $s ]] || { s=$(cat;print x); s=${s%x}; }
set +U
typeset c s t
if (( $# )); then
read -raN-1 s <<<"$*"
unset s[${#s[*]}-1]
else
read -raN-1 s
fi
typeset -i i=0 n=${#s[*]} j v
while (( i < n )); do
c=${s:(i++):1}
(( v = 1#$c << 16 ))
c=${s:(i++):1}
while (( i < n )); do
(( v = s[i++] << 16 ))
(( j = i < n ? s[i++] : 0 ))
(( v |= j << 8 ))
c=${s:(i++):1}
(( v |= j << 8 ))
(( j = i < n ? s[i++] : 0 ))
(( v |= j ))
t=$t${Lb64encode_code[v >> 18]}${Lb64encode_code[v >> 12 & 63]}
@ -297,12 +299,17 @@ typeset -Z11 -Uui16 Lcdbhash_result
typeset -Z11 -Uui16 Lcdbhash_result
function Lcdbhash_add {
[[ -o utf8-mode ]]; typeset u=$?
set +U
typeset s="$*"
[[ -n $s ]] || { s=$(cat;print x); s=${s%x}; }
set +U
typeset s
if (( $# )); then
read -raN-1 s <<<"$*"
unset s[${#s[*]}-1]
else
read -raN-1 s
fi
typeset -i i=0 n=${#s[*]}
while (( i < n )); do
while (( i < n )); do
((# Lcdbhash_result = (Lcdbhash_result * 33) ^ s[i++] ))
done
@ -318,12 +325,17 @@ typeset -Z11 -Uui16 Loaathash_result
typeset -Z11 -Uui16 Loaathash_result
function Loaathash_add {
[[ -o utf8-mode ]]; typeset u=$?
set +U
typeset s="$*"
[[ -n $s ]] || { s=$(cat;print x); s=${s%x}; }
set +U
typeset s
if (( $# )); then
read -raN-1 s <<<"$*"
unset s[${#s[*]}-1]
else
read -raN-1 s
fi
typeset -i i=0 n=${#s[*]}
while (( i < n )); do
while (( i < n )); do
((# Loaathash_result = (Loaathash_result + s[i++]) *
1025 ))
((# Loaathash_result ^= Loaathash_result >> 6 ))

73
edit.c
View File

@ -25,7 +25,7 @@
#include "sh.h"
__RCSID("$MirOS: src/bin/mksh/edit.c,v 1.211 2011/04/22 12:16:38 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/edit.c,v 1.212 2011/05/29 02:18:49 tg Exp $");
/*
* in later versions we might use libtermcap for this, but since external
@ -68,7 +68,7 @@ static char holdbuf[LINE]; /* place to hold last edit buffer */
static int x_getc(void);
static void x_putcf(int);
static bool x_mode(bool);
static void x_mode(bool);
static int x_do_comment(char *, int, int *);
static void x_print_expansions(int, char *const *, bool);
static int x_cf_glob(int *, const char *, int, int, int *, int *, char ***);
@ -3180,44 +3180,26 @@ x_lastcp(void)
return (xlp);
}
static bool
static void
x_mode(bool onoff)
{
static bool x_cur_mode;
bool prev;
if (x_cur_mode == onoff)
return (x_cur_mode);
prev = x_cur_mode;
return;
x_cur_mode = onoff;
if (onoff) {
struct termios cb;
x_mkraw(tty_fd, NULL, false);
cb = tty_state;
edchars.erase = cb.c_cc[VERASE];
edchars.kill = cb.c_cc[VKILL];
edchars.intr = cb.c_cc[VINTR];
edchars.quit = cb.c_cc[VQUIT];
edchars.eof = cb.c_cc[VEOF];
edchars.erase = tty_state.c_cc[VERASE];
edchars.kill = tty_state.c_cc[VKILL];
edchars.intr = tty_state.c_cc[VINTR];
edchars.quit = tty_state.c_cc[VQUIT];
edchars.eof = tty_state.c_cc[VEOF];
#ifdef VWERASE
edchars.werase = cb.c_cc[VWERASE];
edchars.werase = tty_state.c_cc[VWERASE];
#endif
cb.c_iflag &= ~(INLCR | ICRNL);
cb.c_lflag &= ~(ISIG | ICANON | ECHO);
#if defined(VLNEXT) && defined(_POSIX_VDISABLE)
/* OSF/1 processes lnext when ~icanon */
cb.c_cc[VLNEXT] = _POSIX_VDISABLE;
#endif
/* SunOS 4.1.x & OSF/1 processes discard(flush) when ~icanon */
#if defined(VDISCARD) && defined(_POSIX_VDISABLE)
cb.c_cc[VDISCARD] = _POSIX_VDISABLE;
#endif
cb.c_cc[VTIME] = 0;
cb.c_cc[VMIN] = 1;
tcsetattr(tty_fd, TCSADRAIN, &cb);
#ifdef _POSIX_VDISABLE
/* Convert unset values to internal 'unset' value */
@ -3249,8 +3231,6 @@ x_mode(bool onoff)
bind_if_not_bound(0, edchars.quit, XFUNC_noop);
} else
tcsetattr(tty_fd, TCSADRAIN, &tty_state);
return (prev);
}
#if !MKSH_S_NOVI
@ -5338,3 +5318,34 @@ vi_macro_reset(void)
}
}
#endif /* !MKSH_S_NOVI */
void
x_mkraw(int fd, struct termios *ocb, bool forread)
{
struct termios cb;
if (ocb)
tcgetattr(fd, ocb);
else
ocb = &tty_state;
cb = *ocb;
if (forread) {
cb.c_lflag &= ~(ICANON) | ECHO;
} else {
cb.c_iflag &= ~(INLCR | ICRNL);
cb.c_lflag &= ~(ISIG | ICANON | ECHO);
}
#if defined(VLNEXT) && defined(_POSIX_VDISABLE)
/* OSF/1 processes lnext when ~icanon */
cb.c_cc[VLNEXT] = _POSIX_VDISABLE;
#endif
/* SunOS 4.1.x & OSF/1 processes discard(flush) when ~icanon */
#if defined(VDISCARD) && defined(_POSIX_VDISABLE)
cb.c_cc[VDISCARD] = _POSIX_VDISABLE;
#endif
cb.c_cc[VTIME] = 0;
cb.c_cc[VMIN] = 1;
tcsetattr(fd, TCSADRAIN, &cb);
}

33
exec.c
View File

@ -22,7 +22,7 @@
#include "sh.h"
__RCSID("$MirOS: src/bin/mksh/exec.c,v 1.91 2011/05/07 00:51:11 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/exec.c,v 1.92 2011/05/29 02:18:51 tg Exp $");
#ifndef MKSH_DEFAULT_EXECSHELL
#define MKSH_DEFAULT_EXECSHELL "/bin/sh"
@ -405,15 +405,30 @@ execute(struct op * volatile t,
break;
case TCASE:
i = 0;
ccp = evalstr(t->str, DOTILDE);
for (t = t->left; t != NULL && t->type == TPAT; t = t->right)
for (ap = (const char **)t->vars; *ap; ap++)
if ((s = evalstr(*ap, DOTILDE|DOPAT)) &&
gmatchx(ccp, s, false))
goto Found;
break;
Found:
rv = execute(t->left, flags & XERROK, xerrok);
for (t = t->left; t != NULL && t->type == TPAT; t = t->right) {
for (ap = (const char **)t->vars; *ap; ap++) {
if (i || ((s = evalstr(*ap, DOTILDE|DOPAT)) &&
gmatchx(ccp, s, false))) {
rv = execute(t->left, flags & XERROK,
xerrok);
i = 0;
switch (t->u.charflag) {
case '&':
i = 1;
/* FALLTHROUGH */
case '|':
goto TCASE_next;
}
goto TCASE_out;
}
}
i = 0;
TCASE_next:
/* empty */;
}
TCASE_out:
break;
case TBRACE:

487
funcs.c
View File

@ -38,7 +38,7 @@
#endif
#endif
__RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.186 2011/05/06 15:41:23 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.187 2011/05/29 02:18:51 tg Exp $");
#if HAVE_KILLPG
/*
@ -1759,58 +1759,96 @@ c_wait(const char **wp)
int
c_read(const char **wp)
{
int c, ecode = 0, fd = 0, optc;
bool expande = true, historyr = false, expanding;
const char *cp, *emsg;
struct shf *shf;
XString cs, xs = { NULL, NULL, 0, NULL};
struct tbl *vp;
char *ccp, *xp = NULL, *wpalloc = NULL, delim = '\n';
#define is_ifsws(c) (ctype((c), C_IFS) && ctype((c), C_IFSWS))
static char REPLY[] = "REPLY";
int c, fd = 0, rv = 0;
bool savehist = false, intoarray = false, aschars = false;
bool rawmode = false, expanding = false, lastparm = false;
enum { LINES, BYTES, UPTO, READALL } readmode = LINES;
char delim = '\n';
size_t bytesleft = 128, bytesread;
struct tbl *vp /* FU gcc */ = NULL, *vq;
char *cp, *allocd = NULL, *xp;
const char *ccp;
XString xs;
struct termios tios;
bool restore_tios = false;
#if HAVE_SELECT
bool hastimeout = false;
struct timeval tv, tvlim;
#define c_read_opts "Aad:N:n:prst:u,"
#else
#define c_read_opts "Aad:N:n:prsu,"
#endif
while ((optc = ksh_getopt(wp, &builtin_opt, "d:prsu,")) != -1)
switch (optc) {
case 'd':
delim = builtin_opt.optarg[0];
break;
case 'p':
if ((fd = coproc_getfd(R_OK, &emsg)) < 0) {
bi_errorf("%s: %s", "-p", emsg);
return (1);
}
break;
case 'r':
expande = false;
break;
case 's':
historyr = true;
break;
case 'u':
if (!*(cp = builtin_opt.optarg))
fd = 0;
else if ((fd = check_fd(cp, R_OK, &emsg)) < 0) {
bi_errorf("%s: %s: %s", "-u", cp, emsg);
return (1);
}
break;
case '?':
return (1);
while ((c = ksh_getopt(wp, &builtin_opt, c_read_opts)) != -1)
switch (c) {
case 'a':
aschars = true;
/* FALLTHROUGH */
case 'A':
intoarray = true;
break;
case 'd':
delim = builtin_opt.optarg[0];
break;
case 'N':
case 'n':
readmode = c == 'N' ? BYTES : UPTO;
if (!bi_getn(builtin_opt.optarg, &c))
return (2);
if (c == -1) {
readmode = READALL;
bytesleft = 1024;
} else
bytesleft = (unsigned int)c;
break;
case 'p':
if ((fd = coproc_getfd(R_OK, &ccp)) < 0) {
bi_errorf("%s: %s", "-p", ccp);
return (2);
}
break;
case 'r':
rawmode = true;
break;
case 's':
savehist = true;
break;
#if HAVE_SELECT
case 't':
if (parse_usec(builtin_opt.optarg, &tv)) {
bi_errorf("%s: %s '%s'", T_synerr, strerror(errno),
builtin_opt.optarg);
return (2);
}
hastimeout = true;
break;
#endif
case 'u':
if (!builtin_opt.optarg[0])
fd = 0;
else if ((fd = check_fd(builtin_opt.optarg, R_OK, &ccp)) < 0) {
bi_errorf("%s: %s: %s", "-u", builtin_opt.optarg, ccp);
return (2);
}
break;
case '?':
return (2);
}
wp += builtin_opt.optind;
if (*wp == NULL)
*--wp = REPLY;
/*
* Since we can't necessarily seek backwards on non-regular files,
* don't buffer them so we can't read too much.
*/
shf = shf_reopen(fd, SHF_RD | SHF_INTERRUPT | can_seek(fd), shl_spare);
if (intoarray && wp[1] != NULL) {
bi_errorf("too many arguments");
return (2);
}
if ((cp = cstrchr(*wp, '?')) != NULL) {
strdupx(wpalloc, *wp, ATEMP);
wpalloc[cp - *wp] = '\0';
*wp = wpalloc;
if ((ccp = cstrchr(*wp, '?')) != NULL) {
strdupx(allocd, *wp, ATEMP);
allocd[ccp - *wp] = '\0';
*wp = allocd;
if (isatty(fd)) {
/*
* AT&T ksh says it prints prompt on fd if it's open
@ -1818,133 +1856,268 @@ c_read(const char **wp)
* (it also doesn't check the interactive flag,
* as is indicated in the Korn Shell book).
*/
shellf("%s", cp + 1);
shf_puts(ccp + 1, shl_out);
}
}
/*
* If we are reading from the co-process for the first time,
* make sure the other side of the pipe is closed first. This allows
* the detection of eof.
*
* This is not compatible with AT&T ksh... the fd is kept so another
* coproc can be started with same output, however, this means eof
* can't be detected... This is why it is closed here.
* If this call is removed, remove the eof check below, too.
* coproc_readw_close(fd);
Xinit(xs, xp, bytesleft, ATEMP);
if (readmode == LINES)
bytesleft = 1;
else if (isatty(fd)) {
x_mkraw(fd, &tios, true);
restore_tios = true;
}
#if HAVE_SELECT
if (hastimeout) {
gettimeofday(&tvlim, NULL);
timeradd(&tvlim, &tv, &tvlim);
}
#endif
c_read_readloop:
#if HAVE_SELECT
if (hastimeout) {
fd_set fdset;
FD_ZERO(&fdset);
FD_SET(fd, &fdset);
gettimeofday(&tv, NULL);
timersub(&tvlim, &tv, &tv);
if (tv.tv_sec < 0) {
/* timeout expired globally */
rv = 1;
goto c_read_out;
}
switch (select(fd + 1, &fdset, NULL, NULL, &tv)) {
case 1:
break;
case 0:
/* timeout expired for this call */
rv = 1;
goto c_read_out;
default:
bi_errorf("%s: %s", T_select, strerror(errno));
rv = 2;
goto c_read_out;
}
}
#endif
bytesread = blocking_read(fd, xp, bytesleft);
if (bytesread == (size_t)-1) {
/* interrupted */
if (errno == EINTR && fatal_trap_check()) {
/*
* Was the offending signal one that would
* normally kill a process? If so, pretend
* the read was killed.
*/
rv = 2;
goto c_read_out;
}
/* just ignore the signal */
goto c_read_readloop;
}
switch (readmode) {
case READALL:
if (bytesread == 0) {
/* end of file reached */
rv = 1;
goto c_read_readdone;
}
xp += bytesread;
XcheckN(xs, xp, bytesleft);
break;
case UPTO:
if (bytesread == 0)
/* end of file reached */
rv = 1;
xp += bytesread;
goto c_read_readdone;
case BYTES:
if (bytesread == 0) {
/* end of file reached */
rv = 1;
xp = Xstring(xs, xp);
goto c_read_readdone;
}
xp += bytesread;
if ((bytesleft -= bytesread) == 0)
goto c_read_readdone;
break;
case LINES:
if (bytesread == 0) {
/* end of file reached */
rv = 1;
goto c_read_readdone;
}
if ((c = *xp) == '\0' && !aschars && delim != '\0') {
/* skip any read NULs unless delimiter */
break;
}
if (expanding) {
expanding = false;
if (c == delim) {
if (Flag(FTALKING_I) && isatty(fd)) {
/*
* set prompt in case this is
* called from .profile or $ENV
*/
set_prompt(PS2, NULL);
pprompt(prompt, 0);
}
/* drop the backslash */
--xp;
/* and the delimiter */
break;
}
} else if (c == delim) {
goto c_read_readdone;
} else if (!rawmode && c == '\\') {
expanding = true;
}
Xcheck(xs, xp);
++xp;
break;
}
goto c_read_readloop;
c_read_readdone:
bytesread = Xlength(xs, xp);
Xput(xs, xp, '\0');
/*-
* state: we finished reading the input and NUL terminated it
* Xstring(xs, xp) -> xp-1 = input string without trailing delim
* rv = 1 if EOF, 0 otherwise (errors handled already)
*/
if (historyr)
Xinit(xs, xp, 128, ATEMP);
expanding = false;
Xinit(cs, ccp, 128, ATEMP);
/* initialise to something not EOF or delim or any character */
c = 0x100;
for (; *wp != NULL; wp++) {
for (ccp = Xstring(cs, ccp); ; ) {
if (c == delim || c == EOF)
break;
/* loop to read one character */
while (/* CONSTCOND */ 1) {
c = shf_getc(shf);
/* we break unless NUL or EOF, so... */
if (c == delim)
/* in case delim == NUL */
break;
if (c == '\0')
/* skip any read NUL byte */
continue;
if (c == EOF && shf_error(shf) &&
shf_errno(shf) == EINTR) {
/*
* Was the offending signal one that
* would normally kill a process?
* If so, pretend the read was killed.
*/
ecode = fatal_trap_check();
if (rv == 1) {
/* clean up coprocess if needed, on EOF */
coproc_read_close(fd);
if (readmode == READALL)
/* EOF is no error here */
rv = 0;
}
/* non fatal (eg, CHLD), carry on */
if (!ecode) {
shf_clearerr(shf);
continue;
}
}
break;
}
if (historyr) {
Xcheck(xs, xp);
Xput(xs, xp, c);
}
Xcheck(cs, ccp);
if (expanding) {
expanding = false;
if (c == delim) {
c = 0;
if (Flag(FTALKING_I) && isatty(fd)) {
/*
* set prompt in case this is
* called from .profile or $ENV
*/
set_prompt(PS2, NULL);
pprompt(prompt, 0);
}
} else if (c != EOF)
Xput(cs, ccp, c);
continue;
}
if (expande && c == '\\') {
expanding = true;
continue;
}
if (c == delim || c == EOF)
break;
if (ctype(c, C_IFS)) {
if (Xlength(cs, ccp) == 0 && ctype(c, C_IFSWS))
continue;
if (wp[1])
break;
}
Xput(cs, ccp, c);
}
/* strip trailing IFS white space from last variable */
if (!wp[1])
while (Xlength(cs, ccp) && ctype(ccp[-1], C_IFS) &&
ctype(ccp[-1], C_IFSWS))
ccp--;
Xput(cs, ccp, '\0');
if (savehist)
histsave(&source->line, Xstring(xs, xp), true, false);
ccp = cp = Xclose(xs, xp);
expanding = false;
XinitN(xs, 128, ATEMP);
if (intoarray) {
vp = global(*wp);
/* Must be done before setting export. */
if (vp->flag & RDONLY) {
shf_flush(shf);
c_read_splitro:
bi_errorf("%s: %s", *wp, "is read only");
afree(wpalloc, ATEMP);
return (2);
c_read_spliterr:
rv = 2;
afree(cp, ATEMP);
goto c_read_out;
}
/* exporting an array is currently pointless */
unset(vp, 1);
/* counter for array index */
c = 0;
}
c_read_splitloop:
xp = Xstring(xs, xp);
/* generate next word */
if (!bytesread) {
/* no more input */
if (intoarray)
goto c_read_splitdone;
/* zero out next parameters */
goto c_read_gotword;
}
if (aschars) {
Xput(xs, xp, '1');
Xput(xs, xp, '#');
bytesleft = utf_ptradj(ccp);
while (bytesleft && bytesread) {
*xp++ = *ccp++;
--bytesleft;
--bytesread;
}
if (xp[-1] == '\0') {
xp[-1] = '0';
xp[-3] = '2';
}
goto c_read_gotword;
}
if (!intoarray && wp[1] == NULL)
lastparm = true;
/* skip initial IFS whitespace */
while (is_ifsws(*ccp)) {
++ccp;
--bytesread;
}
/* copy until IFS character */
while (bytesread) {
char ch;
ch = *ccp++;
--bytesread;
if (expanding) {
expanding = false;
} else if (ctype(ch, C_IFS)) {
if (!lastparm)
break;
/* last parameter, copy all */
} else if (!rawmode && ch == '\\') {
expanding = true;
continue;
}
Xcheck(xs, xp);
Xput(xs, xp, ch);
}
if (lastparm) {
/* remove trailing IFS whitespace */
while (Xlength(xs, xp) && is_ifsws(xp[-1]))
--xp;
}
c_read_gotword:
Xput(xs, xp, '\0');
if (intoarray) {
vq = arraysearch(vp, c++);
} else {
vq = global(*wp);
/* must be checked before exporting */
if (vq->flag & RDONLY)
goto c_read_splitro;
if (Flag(FEXPORT))
typeset(*wp, EXPORT, 0, 0, 0);
if (!setstr(vp, Xstring(cs, ccp), KSH_RETURN_ERROR)) {
shf_flush(shf);
afree(wpalloc, ATEMP);
return (1);
}
}
shf_flush(shf);
if (historyr) {
Xput(xs, xp, '\0');
histsave(&source->line, Xstring(xs, xp), true, false);
Xfree(xs, xp);
if (!setstr(vq, Xstring(xs, xp), KSH_RETURN_ERROR))
goto c_read_spliterr;
if (aschars) {
setint_v(vq, vq, false);
/* protect from UTFMODE changes */
vq->type = 0;
}
/*
* if this is the co-process fd, close the file descriptor
* (can get eof if and only if all processes are have died,
* i.e. coproc.njobs is 0 and the pipe is closed).
*/
if (c == EOF && !ecode)
coproc_read_close(fd);
if (intoarray || *++wp != NULL)
goto c_read_splitloop;
afree(wpalloc, ATEMP);
return (ecode ? ecode : c == EOF);
c_read_splitdone:
/* free up */
afree(cp, ATEMP);
c_read_out:
afree(allocd, ATEMP);
Xfree(xs, xp);
if (restore_tios)
tcsetattr(fd, TCSADRAIN, &tios);
return (rv);
#undef is_ifsws
}
int

6
lex.c
View File

@ -22,7 +22,7 @@
#include "sh.h"
__RCSID("$MirOS: src/bin/mksh/lex.c,v 1.150 2011/05/07 00:51:12 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/lex.c,v 1.151 2011/05/29 02:18:52 tg Exp $");
/*
* states while lexing word
@ -1018,6 +1018,10 @@ yylex(int cf)
/* c == '(' ) */ MDPAREN;
else if (c == '|' && c2 == '&')
c = COPROC;
else if (c == ';' && c2 == '|')
c = BRKEV;
else if (c == ';' && c2 == '&')
c = BRKFT;
else
ungetsc(c2);
} else if (c == '\n') {

43
misc.c
View File

@ -29,7 +29,7 @@
#include <grp.h>
#endif
__RCSID("$MirOS: src/bin/mksh/misc.c,v 1.165 2011/05/04 23:16:02 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/misc.c,v 1.166 2011/05/29 02:18:53 tg Exp $");
/* type bits for unsigned char */
unsigned char chtypes[UCHAR_MAX + 1];
@ -1641,14 +1641,17 @@ c_cd(const char **wp)
bool physical = tobool(Flag(FPHYSICAL));
/* was a node from cdpath added in? */
int cdnode;
/* print where we cd'd? */
bool printpath = false;
/* show where we went?, error for $PWD */
bool printpath = false, eflag = false;
struct tbl *pwd_s, *oldpwd_s;
XString xs;
char *dir, *allocd = NULL, *tryp, *pwd, *cdpath;
while ((optc = ksh_getopt(wp, &builtin_opt, "LP")) != -1)
while ((optc = ksh_getopt(wp, &builtin_opt, "eLP")) != -1)
switch (optc) {
case 'e':
eflag = true;
break;
case 'L':
physical = false;
break;
@ -1656,13 +1659,13 @@ c_cd(const char **wp)
physical = true;
break;
case '?':
return (1);
return (2);
}
wp += builtin_opt.optind;
if (Flag(FRESTRICTED)) {
bi_errorf("restricted shell - can't cd");
return (1);
return (2);
}
pwd_s = global("PWD");
@ -1672,7 +1675,7 @@ c_cd(const char **wp)
/* No arguments - go home */
if ((dir = str_val(global("HOME"))) == null) {
bi_errorf("no home directory (HOME not set)");
return (1);
return (2);
}
} else if (!wp[1]) {
/* One argument: - or dir */
@ -1683,7 +1686,7 @@ c_cd(const char **wp)
dir = str_val(oldpwd_s);
if (dir == null) {
bi_errorf("no OLDPWD");
return (1);
return (2);
}
printpath = true;
}
@ -1694,7 +1697,7 @@ c_cd(const char **wp)
if (!current_wd[0]) {
bi_errorf("can't determine current directory");
return (1);
return (2);
}
/*
* substitute arg1 for arg2 in current path.
@ -1704,7 +1707,7 @@ c_cd(const char **wp)
*/
if ((cp = strstr(current_wd, wp[0])) == NULL) {
bi_errorf("bad substitution");
return (1);
return (2);
}
/*-
* ilen = part of current_wd before wp[0]
@ -1723,7 +1726,7 @@ c_cd(const char **wp)
printpath = true;
} else {
bi_errorf("too many arguments");
return (1);
return (2);
}
#ifdef NO_PATH_MAX
@ -1750,9 +1753,12 @@ c_cd(const char **wp)
else
bi_errorf("%s: %s", tryp, strerror(errno));
afree(allocd, ATEMP);
return (1);
Xfree(xs, xp);
return (2);
}
rv = 0;
/* allocd (above) => dir, which is no longer used */
afree(allocd, ATEMP);
allocd = NULL;
@ -1770,8 +1776,14 @@ c_cd(const char **wp)
if (Xstring(xs, xp)[0] != '/') {
pwd = NULL;
} else if (!physical || !(pwd = allocd = do_realpath(Xstring(xs, xp))))
} else if (!physical) {
goto norealpath_PWD;
} else if ((pwd = allocd = do_realpath(Xstring(xs, xp))) == NULL) {
if (eflag)
rv = 1;
norealpath_PWD:
pwd = Xstring(xs, xp);
}
/* Set PWD */
if (pwd) {
@ -1784,12 +1796,15 @@ c_cd(const char **wp)
set_current_wd(null);
pwd = Xstring(xs, xp);
/* XXX unset $PWD? */
if (eflag)
rv = 1;
}
if (printpath || cdnode)
shprintf("%s\n", pwd);
afree(allocd, ATEMP);
return (0);
Xfree(xs, xp);
return (rv);
}

188
mksh.1
View File

@ -1,4 +1,4 @@
.\" $MirOS: src/bin/mksh/mksh.1,v 1.260 2011/05/06 15:41:24 tg Exp $
.\" $MirOS: src/bin/mksh/mksh.1,v 1.261 2011/05/29 02:18:54 tg Exp $
.\" $OpenBSD: ksh.1,v 1.140 2011/04/23 10:14:59 sobrado Exp $
.\"-
.\" Copyright © 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
@ -72,7 +72,7 @@
.\" with -mandoc, it might implement .Mx itself, but we want to
.\" use our own definition. And .Dd must come *first*, always.
.\"
.Dd $Mdocdate: May 6 2011 $
.Dd $Mdocdate: May 29 2011 $
.\"
.\" Check which macro package we use
.\"
@ -391,8 +391,11 @@ is used to create asynchronous pipelines;
and
.Ql \*(Ba\*(Ba
are used to specify conditional execution;
.Ql ;;
is used in
.Ql ;; ,
.Ql ;&\&
and
.Ql ;\*(Ba\&
are used in
.Ic case
statements;
.Ql \&(( .. ))
@ -642,10 +645,12 @@ and
are reserved words, not meta-characters.
.It Xo case Ar word No in
.Oo Op \&(
.Ar \ pattern
.Op \*(Ba Ar pattern
.No ... No )
.Ar list No ;;\ \& Oc ... esac
.Ar pattern
.Op \*(Ba Ar pat
.No ... Ns )
.Ar list
.Op ;; \*(Ba ;&\& \*(Ba ;\*(Ba\ \&
.Oc ... esac
.Xc
The
.Ic case
@ -669,12 +674,22 @@ stripped; any space within a pattern must be quoted.
Both the word and the
patterns are subject to parameter, command, and arithmetic substitution, as
well as tilde substitution.
.Pp
For historical reasons, open and close braces may be used instead of
.Ic in
and
.Ic esac
e.g.\&
.Ic case $foo { *) echo bar; } .
.Ic case $foo { *) echo bar;; } .
.Pp
The list terminators are
.Ql ;;
(terminate after the list),
.Ql ;&\&
(fall through into the next list) and
.Ql ;\*(Ba\&
(evaluate the remaining pattern-list tuples).
.Pp
The exit status of a
.Ic case
statement is that of the executed
@ -3059,12 +3074,17 @@ option is supported as a no-op.
.Pp
.It Xo
.Ic cd
.Op Fl LP
.Op Fl L
.Op Ar dir
.Xc
.It Xo
.Ic cd
.Fl P Op Fl e
.Op Ar dir
.Xc
.It Xo
.Ic chdir
.Op Fl LP
.Op Fl eLP
.Op Ar dir
.Xc
Set the working directory to
@ -3120,15 +3140,21 @@ and
.Ev OLDPWD
parameters are updated to reflect the current and old working directory,
respectively.
If the
.Fl e
option is set for physical filesystem traversal, and
.Ev PWD
could not be set, the exit code is 1; greater than 1 if an
error occurred, 0 otherwise.
.Pp
.It Xo
.Ic cd
.Op Fl LP
.Op Fl eLP
.Ar old new
.Xc
.It Xo
.Ic chdir
.Op Fl LP
.Op Fl eLP
.Ar old new
.Xc
The string
@ -3627,36 +3653,96 @@ directories to the root directory) is printed.
.Pp
.It Xo
.Ic read
.Op Fl d Ar delimiter
.Op Fl prsu Ns Op Ar n
.Op Ar parameter ...
.Op Fl A | Fl a
.Op Fl d Ar x
.Oo Fl N Ar z \*(Ba
.Fl n Ar z Oc
.Oo Fl p \*(Ba
.Fl u Ns Op Ar n
.Oc Op Fl t Ar n
.Op Fl rs
.Op Ar p ...
.Xc
Reads a line of input from the standard input, separates the line into fields
using the
Reads a line of input, separates the input into fields using the
.Ev IFS
parameter (see
.Sx Substitution
above), and assigns each field to the specified parameters.
Lines are delimited by the first character of
.Ar delimiter ,
.Dv NUL
if empty, if
.Fl d
was used, a newline otherwise.
If there are more parameters than fields, the extra parameters are set to
.Dv NULL ,
or alternatively, if there are more fields than parameters, the last parameter
is assigned the remaining fields (inclusive of any separating spaces).
above), and assigns each field to the specified parameters
.Ar p .
If no parameters are specified, the
.Ev REPLY
parameter is used.
If the input line ends in a backslash and the
.Fl r
option was not used, the backslash and the newline are stripped and more input
is read.
If no input is read,
parameter is used to store the result.
With the
.Fl A
and
.Fl a
options, only no or one parameter is accepted.
If there are more parameters than fields, the extra parameters are set to
the empty string or 0; if there are more fields than parameters, the last
parameter is assigned the remaining fields (including the word separators).
.Pp
The options are as follows:
.Bl -tag -width XuXnX
.It Fl A
Store the result into the parameter
.Ar p
(or
.Ev REPLY )
as array of words.
.It Fl a
Store the result without word splitting into the parameter
.Ar p
(or
.Ev REPLY )
as array of characters (wide characters if the
.Ic utf8\-mode
option is enacted, octets otherwise).
.It Fl d Ar x
Use the first byte of
.Ar x ,
.Dv NUL
if empty, instead of the ASCII newline character as input line delimiter.
.It Fl N Ar z
Instead of reading till end-of-line, read exactly
.Ar z
bytes; less if EOF or a timeout occurs.
.It Fl n Ar z
Instead of reading till end-of-line, read up to
.Ar z
bytes but return as soon as any bytes are read, e.g.\& from a
slow terminal device, or if EOF or a timeout occurs.
.It Fl p
Read from the currently active co-process, see
.Sx Co-processes
above for details on this.
.It Fl u Ns Op Ar n
Read from the file descriptor
.Ar n
(defaults to 0, i.e.\& standard input).
The argument must immediately follow the option character.
.It Fl t Ar n
Interrupt reading after
.Ar n
seconds (specified as positive decimal value with an optional fractional part).
.It Fl r
Normally, the ASCII backslash character escapes the special
meaning of the following character and is stripped from the input;
.Ic read
exits with a non-zero status.
does not stop when encountering a backslash-newline sequence and
does not store that newline in the result.
This option enables raw mode, in which backslashes are not processed.
.It Fl s
The input line is saved to the history.
.El
.Pp
If the input is a terminal, both the
.Fl N
and
.Fl n
options set it into raw mode;
they read an entire file if \-1 is passed as
.Ar z
argument.
.Pp
The first parameter may have a question mark and a string appended to it, in
which case the string is used as a prompt (printed to standard error before
@ -3665,20 +3751,9 @@ any input is read) if the input is a
(e.g.\&
.Ic read nfoo?\*(aqnumber of foos: \*(aq ) .
.Pp
The
.Fl u Ns Ar n
and
.Fl p
options cause input to be read from file descriptor
.Ar n
.Pf ( Ar n
defaults to 0 if omitted)
or the current co-process (see
.Sx Co-processes
above for comments on this), respectively.
If the
.Fl s
option is used, input is saved to the history file.
If no input is read or a timeout occurred,
.Ic read
exits with a non-zero status.
.Pp
Another handy set of tricks:
If
@ -3689,6 +3764,17 @@ then leading whitespace will be removed (IFS) and backslashes processed.
You might want to use
.Ic while IFS= read \-r foo; do ...; done
for pristine I/O.
Similarily, when using the
.Fl a
option, use of the
.Fl r
option might be prudent; the same applies for:
.Bd -literal -offset indent
find . -type f -print0 \*(Ba \e
while IFS= read \-d \*(aq\*(aq \-r filename; do
print \-r \-\- "found <${filename#./}>"
done
.Ed
.Pp
The inner loop will be executed in a subshell and variable changes
cannot be propagated if executed in a pipeline:
@ -6153,7 +6239,7 @@ $ /bin/sleep 666 && echo fubar
.Ed
.Pp
This document attempts to describe
.Nm mksh\ R39c+CVS
.Nm mksh\ R40~rc
and up,
compiled without any options impacting functionality, such as
.Dv MKSH_SMALL ,

18
sh.h
View File

@ -151,9 +151,9 @@
#endif
#ifdef EXTERN
__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.469 2011/05/06 15:41:25 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.470 2011/05/29 02:18:55 tg Exp $");
#endif
#define MKSH_VERSION "R39 2011/05/06"
#define MKSH_VERSION "R39 2011/05/28"
#ifndef MKSH_INCLUDES_ONLY
@ -1111,10 +1111,14 @@ struct op {
*/
int lineno; /* TCOM/TFUNC: LINENO for this */
short type; /* operation type, see below */
/* WARNING: newtp(), tcopy() use evalflags = 0 to clear union */
union {
/* WARNING: newtp(), tcopy() use evalflags = 0 to clear union */
short evalflags; /* TCOM: arg expansion eval() flags */
short ksh_func; /* TFUNC: function x (vs x()) */
/* TCOM: arg expansion eval() flags */
short evalflags;
/* TFUNC: function x (vs x()) */
short ksh_func;
/* TPAT: termination character */
char charflag;
} u;
};
@ -1392,6 +1396,8 @@ typedef union {
#define BANG 278 /* ! */
#define DBRACKET 279 /* [[ .. ]] */
#define COPROC 280 /* |& */
#define BRKEV 281 /* ;| */
#define BRKFT 282 /* ;& */
#define YYERRCODE 300
/* flags to yylex */
@ -1484,6 +1490,7 @@ int x_bind(const char *, const char *, bool, bool);
int x_bind(const char *, const char *, bool);
#endif
void x_init(void);
void x_mkraw(int, struct termios *, bool);
int x_read(char *, size_t);
/* eval.c */
char *substitute(const char *, int);
@ -1779,6 +1786,7 @@ const char *skip_varname(const char *, int);
const char *skip_wdvarname(const char *, int);
int is_wdvarname(const char *, int);
int is_wdvarassign(const char *);
struct tbl *arraysearch(struct tbl *, uint32_t);
char **makenv(void);
void change_winsz(void);
int array_ref_len(const char *);

20
syn.c
View File

@ -22,7 +22,7 @@
#include "sh.h"
__RCSID("$MirOS: src/bin/mksh/syn.c,v 1.64 2011/05/07 00:51:12 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/syn.c,v 1.65 2011/05/29 02:18:57 tg Exp $");
extern short subshell_nesting_level;
@ -619,7 +619,17 @@ casepart(int endtok)
t->left = c_list(true);
/* Note: POSIX requires the ;; */
if ((tpeek(CONTIN|KEYWORD|ALIAS)) != endtok)
musthave(BREAK, CONTIN|KEYWORD|ALIAS);
switch (symbol) {
default:
syntaxerr(NULL);
case BREAK:
case BRKEV:
case BRKFT:
t->u.charflag =
(symbol == BRKEV) ? '|' :
(symbol == BRKFT) ? '&' : ';';
ACCEPT;
}
return (t);
}
@ -768,6 +778,8 @@ const struct tokeninfo {
{ "&&", LOGAND, false },
{ "||", LOGOR, false },
{ ";;", BREAK, false },
{ ";|", BRKEV, false },
{ ";&", BRKFT, false },
{ "((", MDPAREN, false },
{ "|&", COPROC, false },
/* and some special cases... */
@ -782,8 +794,8 @@ initkeywords(void)
struct tbl *p;
ktinit(&keywords, APERM,
/* must be 80% of 2^n (currently 20 keywords) */
32);
/* must be 80% of 2^n (currently 28 keywords) */
64);
for (tt = tokentab; tt->name; tt++) {
if (tt->reserved) {
p = ktenter(&keywords, tt->name, hash(tt->name));

9
tree.c
View File

@ -22,7 +22,7 @@
#include "sh.h"
__RCSID("$MirOS: src/bin/mksh/tree.c,v 1.48 2011/05/07 00:24:35 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/tree.c,v 1.49 2011/05/29 02:18:57 tg Exp $");
#define INDENT 8
@ -100,7 +100,7 @@ ptree(struct op *t, int indent, struct shf *shf)
case TSELECT:
case TFOR:
fptreef(shf, indent, "%s %s ",
(t->type == TFOR) ? "for" : "select", t->str);
(t->type == TFOR) ? "for" : T_select, t->str);
if (t->vars != NULL) {
shf_puts("in ", shf);
w = (const char **)t->vars;
@ -121,7 +121,8 @@ ptree(struct op *t, int indent, struct shf *shf)
(w[1] != NULL) ? '|' : ')');
++w;
}
fptreef(shf, indent + INDENT, "%N%T%N;;", t1->left);
fptreef(shf, indent + INDENT, "%N%T%N;%c", t1->left,
t1->u.charflag);
}
fptreef(shf, indent, "%Nesac ");
break;
@ -949,7 +950,7 @@ dumptree(struct shf *shf, struct op *t)
shf_putc(')', shf);
shf_putc('\n', shf);
dumptree(shf, t1->left);
shf_fprintf(shf, " /%d]", i++);
shf_fprintf(shf, " ;%c/%d]", t1->u.charflag, i++);
}
break;
OPEN(TWHILE)

13
var.c
View File

@ -26,7 +26,7 @@
#include <sys/sysctl.h>
#endif
__RCSID("$MirOS: src/bin/mksh/var.c,v 1.121 2011/05/07 02:02:47 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/var.c,v 1.122 2011/05/29 02:18:57 tg Exp $");
/*
* Variables
@ -50,7 +50,6 @@ static void setspec(struct tbl *);
static void unsetspec(struct tbl *);
static int getint(struct tbl *, mksh_ari_t *, bool);
static mksh_ari_t intval(struct tbl *);
static struct tbl *arraysearch(struct tbl *, uint32_t);
static const char *array_index_calc(const char *, bool *, uint32_t *);
uint8_t set_refflag = 0;
@ -348,6 +347,8 @@ str_val(struct tbl *vp)
n = (vp->val.i < 0) ? -vp->val.i : vp->val.i;
base = (vp->type == 0) ? 10 : vp->type;
if (base == 1 && n == 0)
base = 2;
if (base == 1) {
size_t sz = 1;
@ -478,8 +479,6 @@ getint(struct tbl *vp, mksh_ari_t *nump, bool arith)
return (vp->type);
}
s = vp->val.s + vp->type;
if (s == NULL) /* redundant given initial test */
s = null;
base = 10;
num = 0;
neg = 0;
@ -553,10 +552,12 @@ setint_v(struct tbl *vq, struct tbl *vp, bool arith)
return (NULL);
if (!(vq->flag & INTEGER) && (vq->flag & ALLOC)) {
vq->flag &= ~ALLOC;
vq->type = 0;
afree(vq->val.s, vq->areap);
}
vq->val.i = num;
if (vq->type == 0) /* default base */
if (vq->type == 0)
/* default base */
vq->type = base;
vq->flag |= ISSET|INTEGER;
if (vq->flag&SPECIAL)
@ -1240,7 +1241,7 @@ unsetspec(struct tbl *vp)
* Search for (and possibly create) a table entry starting with
* vp, indexed by val.
*/
static struct tbl *
struct tbl *
arraysearch(struct tbl *vp, uint32_t val)
{
struct tbl *prev, *curr, *news;