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:
34
check.pl
34
check.pl
@ -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
316
check.t
@ -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 we’re 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 we’re 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
|
||||
---
|
||||
|
50
dot.mkshrc
50
dot.mkshrc
@ -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
73
edit.c
@ -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
33
exec.c
@ -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
487
funcs.c
@ -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
6
lex.c
@ -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
43
misc.c
@ -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
188
mksh.1
@ -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
18
sh.h
@ -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
20
syn.c
@ -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
9
tree.c
@ -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
13
var.c
@ -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;
|
||||
|
Reference in New Issue
Block a user