mirror of
git://git.code.sf.net/p/cdesktopenv/code
synced 2025-02-12 19:22:41 +00:00
Backport 'read -a' and 'read -p' from ksh 93v-/2020
This backports two minor additions to the 'read' built-in from ksh 93v-: '-a' is now the same as '-A' and '-u p' is the same as '-p'. This is for compatibility with some 93v- or ksh2020 scripts. Note that their change to the '-p' option to support both prompts and reading from the coprocess was *not* backported because we found it to be broken and unfixable. Discussoin at: https://github.com/ksh93/ksh/issues/463 src/cmd/ksh93/bltins/read.c: b_read(): - Backport as described above. - Rename the misleadingly named 'name' variable to 'prompt'. It points to the prompt string, not to a variable name. src/cmd/ksh93/data/builtins.c: sh_optpwd[]: - Add -a as an alterative to -A. All that is needed is adding '|a' and optget(3) will automatically convert it to 'A'. - Change -u from a '#' (numeric) to ':' option to support 'p'. Note that b_read() now needs a corresponding strtol() to convert file descriptor strings to numbers where applicable. - Tweaks. src/cmd/ksh93/sh.1: - Update accordingly. - Tidy up the unreadable mess that was the 'read' documentation. The options are now shown in a list.
This commit is contained in:
parent
95d695cb5a
commit
d55e9686d7
6 changed files with 149 additions and 108 deletions
8
NEWS
8
NEWS
|
@ -3,6 +3,14 @@ For full details, see the git log at: https://github.com/ksh93/ksh/tree/1.0
|
|||
|
||||
Any uppercase BUG_* names are modernish shell bug IDs.
|
||||
|
||||
2022-01-16:
|
||||
|
||||
- Backported minor additions to the 'read' built-in command from ksh 93v-:
|
||||
'-a' is now the same as '-A' and '-u p' is the same as '-p'. This is for
|
||||
compatibility with some 93v- or ksh2020 scripts. (Note that their change
|
||||
to the '-p' option to support both prompts and coprocess reads was not
|
||||
backported because we found it to be broken and unfixable.)
|
||||
|
||||
2022-02-15:
|
||||
|
||||
- A bug was fixed in fast filescan loops (like 'while <file; do ...; done',
|
||||
|
|
|
@ -62,7 +62,7 @@ struct read_save
|
|||
int b_read(int argc,char *argv[], Shbltin_t *context)
|
||||
{
|
||||
Sfdouble_t sec;
|
||||
register char *name;
|
||||
char *prompt;
|
||||
register int r, flags=0, fd=0;
|
||||
ssize_t len=0;
|
||||
long timeout = 1000*sh.st.tmout;
|
||||
|
@ -82,7 +82,7 @@ int b_read(int argc,char *argv[], Shbltin_t *context)
|
|||
timeout = rp->timeout;
|
||||
fd = rp->fd;
|
||||
argv = rp->argv;
|
||||
name = rp->prompt;
|
||||
prompt = rp->prompt;
|
||||
r = rp->plen;
|
||||
goto bypass;
|
||||
}
|
||||
|
@ -107,6 +107,7 @@ int b_read(int argc,char *argv[], Shbltin_t *context)
|
|||
}
|
||||
break;
|
||||
case 'p':
|
||||
coprocess:
|
||||
if((fd = sh.cpipe[0])<=0)
|
||||
{
|
||||
errormsg(SH_DICT,ERROR_exit(1),e_query);
|
||||
|
@ -129,13 +130,10 @@ int b_read(int argc,char *argv[], Shbltin_t *context)
|
|||
flags |= SS_FLAG;
|
||||
break;
|
||||
case 'u':
|
||||
fd = (int)opt_info.num;
|
||||
if(opt_info.num<0 || opt_info.num>INT_MAX || (fd>=sh.lim.open_max && !sh_iovalidfd(fd)))
|
||||
{
|
||||
errormsg(SH_DICT,ERROR_exit(1),e_file,opt_info.arg); /* reject invalid file descriptors */
|
||||
UNREACHABLE();
|
||||
}
|
||||
if(sh_inuse(fd))
|
||||
if(opt_info.arg[0]=='p' && opt_info.arg[1]==0)
|
||||
goto coprocess;
|
||||
fd = (int)strtol(opt_info.arg,&opt_info.arg,10);
|
||||
if(*opt_info.arg || !sh_iovalidfd(fd) || sh_inuse(fd))
|
||||
fd = -1;
|
||||
break;
|
||||
case 'v':
|
||||
|
@ -162,8 +160,8 @@ int b_read(int argc,char *argv[], Shbltin_t *context)
|
|||
UNREACHABLE();
|
||||
}
|
||||
/* look for prompt */
|
||||
if((name = *argv) && (name=strchr(name,'?')) && (r&IOTTY))
|
||||
r = strlen(name++);
|
||||
if((prompt = *argv) && (prompt=strchr(prompt,'?')) && (r&IOTTY))
|
||||
r = strlen(prompt++);
|
||||
else
|
||||
r = 0;
|
||||
if(argc==fixargs)
|
||||
|
@ -174,7 +172,7 @@ int b_read(int argc,char *argv[], Shbltin_t *context)
|
|||
rp->flags = flags;
|
||||
rp->timeout = timeout;
|
||||
rp->argv = argv;
|
||||
rp->prompt = name;
|
||||
rp->prompt = prompt;
|
||||
rp->plen = r;
|
||||
rp->len = len;
|
||||
}
|
||||
|
@ -182,7 +180,7 @@ bypass:
|
|||
sh.prompt = default_prompt;
|
||||
if(r && (sh.prompt=(char*)sfreserve(sfstderr,r,SF_LOCKR)))
|
||||
{
|
||||
memcpy(sh.prompt,name,r);
|
||||
memcpy(sh.prompt,prompt,r);
|
||||
sfwrite(sfstderr,sh.prompt,r-1);
|
||||
}
|
||||
sh.timeout = 0;
|
||||
|
|
|
@ -1404,7 +1404,7 @@ const char sh_optpwd[] =
|
|||
;
|
||||
|
||||
const char sh_optread[] =
|
||||
"[-1c?\n@(#)$Id: read (AT&T Research) 2006-12-19 $\n]"
|
||||
"[-1c?\n@(#)$Id: read (ksh 93u+m) 2022-02-16 $\n]"
|
||||
"[--catalog?" SH_DICT "]"
|
||||
"[+NAME?read - read a line from standard input]"
|
||||
"[+DESCRIPTION?\bread\b reads a line from standard input and breaks it "
|
||||
|
@ -1419,28 +1419,29 @@ const char sh_optread[] =
|
|||
"\bREPLY\b is used.]"
|
||||
"[+?When \avar\a has the binary attribute and \b-n\b or \b-N\b is specified, "
|
||||
"the bytes that are read are stored directly into \bvar\b.]"
|
||||
"[+?If you specify \b?\b\aprompt\a after the first \avar\a, then \bread\b "
|
||||
"will display \aprompt\a on standard error when standard input "
|
||||
"is a terminal or pipe.]"
|
||||
"[+?If an end of file is encountered while reading a line the data is "
|
||||
"[+?If you append \b?\b\aprompt\a to the first \avar\a, then \bread\b "
|
||||
"will display \aprompt\a on standard error before reading "
|
||||
"if standard input is a terminal or pipe. "
|
||||
"The \b?\b should be quoted to protect it from pathname expansion.]"
|
||||
"[+?If an end-of file is encountered on a line "
|
||||
"that is not terminated by a newline control character, the data is "
|
||||
"read and processed but \bread\b returns with a non-zero exit status.]"
|
||||
"[A?Unset \avar\a and then create an indexed array containing each field in "
|
||||
"[A|a?Unset \avar\a and then create an indexed array containing each field in "
|
||||
"the line starting at index 0.]"
|
||||
"[C?Unset \avar\a and read \avar\a as a compound variable.]"
|
||||
"[d]:[delim?Read until delimiter \adelim\a instead of to the end of line.]"
|
||||
"[p?Read from the current co-process instead of standard input. An end of "
|
||||
"file causes \bread\b to disconnect the co-process so that another "
|
||||
"can be created.]"
|
||||
"[r?Do not treat \b\\\b specially when processing the input line.]"
|
||||
"[n]#[count?Read at most \acount\a characters or (for binary fields) bytes.]"
|
||||
"[N]#[count?Read exactly \acount\a characters or (for binary fields) bytes.]"
|
||||
"[p?Read from the current co-process instead of standard input. "
|
||||
"An end-of-file causes \bread\b to disconnect the co-process "
|
||||
"so that another can be created.]"
|
||||
"[r?Raw mode. Do not treat \b\\\b specially when processing the input line.]"
|
||||
"[s?Save a copy of the input as an entry in the shell history file.]"
|
||||
"[S?Treat the input as if it was saved from a spreadsheet in csv format.]"
|
||||
"[u]#[fd:=0?Read from file descriptor number \afd\a instead of standard input.]"
|
||||
"[u]:[fd:=0?Read from file descriptor number \afd\a instead of standard input. "
|
||||
"If \afd\a is \bp\b, read from the co-process; same as \b-p\b.]"
|
||||
"[t]:[timeout?Specify a timeout \atimeout\a in seconds when reading from "
|
||||
"a terminal or pipe.]"
|
||||
"[n]#[count?Read at most \acount\a characters. For binary fields \acount\a "
|
||||
"is the number of bytes.]"
|
||||
"[N]#[count?Read exactly \acount\a characters. For binary fields \acount\a "
|
||||
"is the number of bytes.]"
|
||||
"[v?When reading from a terminal the value of the first variable is displayed "
|
||||
"and used as a default value.]"
|
||||
"\n"
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
|
||||
#define SH_RELEASE_FORK "93u+m" /* only change if you develop a new ksh93 fork */
|
||||
#define SH_RELEASE_SVER "1.0.0-beta.2" /* semantic version number: https://semver.org */
|
||||
#define SH_RELEASE_DATE "2022-02-15" /* must be in this format for $((.sh.version)) */
|
||||
#define SH_RELEASE_DATE "2022-02-16" /* must be in this format for $((.sh.version)) */
|
||||
#define SH_RELEASE_CPYR "(c) 2020-2022 Contributors to ksh " SH_RELEASE_FORK
|
||||
|
||||
/* Scripts sometimes field-split ${.sh.version}, so don't change amount of whitespace. */
|
||||
|
|
|
@ -7152,8 +7152,7 @@ or
|
|||
formats, separates groups of digits with the grouping delimiter
|
||||
.RB ( ,
|
||||
on groups of 3 in the C locale).
|
||||
.PD
|
||||
.TP
|
||||
.PP
|
||||
The \f3\-v\fP option assigns the output directly to a variable instead of
|
||||
writing it to standard output. This is faster than capturing the output using a
|
||||
command substitution and avoids the latter's stripping of final linefeed
|
||||
|
@ -7161,6 +7160,7 @@ characters (\fB\\n\fR). The \f2vname\fP argument should be a valid variable
|
|||
name, optionally with one or more array subscripts in square brackets.
|
||||
Note that square brackets should be quoted to avoid pathname expansion.
|
||||
.RE
|
||||
|
||||
.TP
|
||||
\f3pwd\fP \*(OK \f3\-LP\fP \*(CK
|
||||
Outputs the value of the current working
|
||||
|
@ -7179,7 +7179,7 @@ or
|
|||
on the command line
|
||||
determines which method is used.
|
||||
.TP
|
||||
\f3read\fP \*(OK \f3\-ACSprsv\^\fP \*(CK \*(OK \f3\-d\fP \f2delim \^\fP\*(CK \*(OK \f3\-n\fP \f2n \^\fP\*(CK \*(OK \*(OK \f3\-N\fP \f2n \^\fP\*(CK \*(OK \f3\-t\fP \f2timeout \^\fP\*(CK \*(OK \f3\-u\fP \f2unit \^\fP\*(CK \*(OK \f2vname\f3?\f2prompt\^\f1 \*(CK \*(OK \f2vname\^\fP .\|.\|. \*(CK
|
||||
\f3read\fP \*(OK \f3\-ACSaprsv\^\fP \*(CK \*(OK \f3\-d\fP \f2delim \^\fP\*(CK \*(OK \f3\-n\fP \f2n \^\fP\*(CK \*(OK \f3\-N\fP \f2n \^\fP\*(CK \*(OK \f3\-t\fP \f2timeout \^\fP\*(CK \*(OK \f3\-u\fP \f2unit \^\fP\*(CK \*(OK \f2vname\f3?\f2prompt\^\f1 \*(CK \*(OK \f2vname\^\fP .\|.\|. \*(CK
|
||||
The shell input mechanism.
|
||||
One line is read and
|
||||
is broken up into fields using the characters in
|
||||
|
@ -7190,32 +7190,6 @@ The escape character,
|
|||
.BR \e ,
|
||||
is used to remove any special meaning for the next
|
||||
character and for line continuation.
|
||||
The
|
||||
.B \-d
|
||||
option
|
||||
causes the read to continue to the first character of
|
||||
.I delim\^
|
||||
rather than new-line.
|
||||
The
|
||||
.B \-n
|
||||
option causes at most
|
||||
.I n\^
|
||||
bytes to read rather a full line
|
||||
but will return when reading from a slow device
|
||||
as soon as any characters have been read.
|
||||
The
|
||||
.B \-N
|
||||
option causes exactly
|
||||
.I n\^
|
||||
to be read unless an end-of-file has been encountered or
|
||||
the read times out because of the
|
||||
.B \-t
|
||||
option.
|
||||
In raw mode,
|
||||
.B \-r,
|
||||
the
|
||||
.B \e
|
||||
character is not treated specially.
|
||||
The first
|
||||
field is assigned to the first
|
||||
.IR vname ,
|
||||
|
@ -7224,6 +7198,13 @@ to the second
|
|||
.IR vname ,
|
||||
etc., with leftover fields assigned to the last
|
||||
.IR vname .
|
||||
If
|
||||
.I vname\^
|
||||
is omitted, then
|
||||
.SM
|
||||
.B REPLY
|
||||
is used as the default
|
||||
.IR vname .
|
||||
When
|
||||
.IR vname
|
||||
has the binary attribute and
|
||||
|
@ -7232,78 +7213,113 @@ or
|
|||
.B \-N
|
||||
is specified, the bytes that are read are stored directly
|
||||
into the variable.
|
||||
If the
|
||||
.B \-v
|
||||
is specified, then the value of the first
|
||||
.I vname\^
|
||||
will be used as a default value when reading from a terminal device.
|
||||
The
|
||||
If you append \f3?\fP\f2prompt\fP to the first
|
||||
.IR vname\^ ,
|
||||
then
|
||||
.B read\^
|
||||
will display
|
||||
.I prompt\^
|
||||
on standard error before reading if standard input is a terminal or pipe; the
|
||||
.B ?\^
|
||||
should be quoted to protect it from pathname expansion.
|
||||
The exit status is 0 unless an end-of-file is encountered or
|
||||
.B read
|
||||
has timed out.
|
||||
The options for the
|
||||
.B read\^
|
||||
command have meaning as follows:
|
||||
.RS
|
||||
.PD 0
|
||||
.TP 8
|
||||
.B \-A
|
||||
option causes the variable
|
||||
Causes the variable
|
||||
.I vname\^
|
||||
to be unset and each field that is read to be stored in
|
||||
successive elements of the indexed array
|
||||
.IR vname.
|
||||
The
|
||||
.TP 8
|
||||
.B \-C
|
||||
option causes the variable
|
||||
Causes the variable
|
||||
.I vname\^
|
||||
to be read as a compound variable. Blanks will be ignored when
|
||||
finding the beginning open parenthesis.
|
||||
The
|
||||
.TP 8
|
||||
.B \-N
|
||||
Causes
|
||||
.I n\^
|
||||
bytes to be read unless an end-of-file has been encountered or
|
||||
the read times out because of the
|
||||
.B \-t
|
||||
option.
|
||||
.TP 8
|
||||
.B \-S
|
||||
option causes the line to be treated like a record in a
|
||||
Causes the line to be treated like a record in a
|
||||
.B .csv
|
||||
format file so that double quotes can be used to allow the delimiter
|
||||
character and the new-line character to appear within a field.
|
||||
The
|
||||
.TP 8
|
||||
.B \-a
|
||||
Same as
|
||||
.BR \-A .
|
||||
.TP 8
|
||||
.B \-d
|
||||
Causes the read to continue to the first character of
|
||||
.I delim\^
|
||||
instead of the newline control character.
|
||||
.TP 8
|
||||
.B \-n
|
||||
Causes at most
|
||||
.I n\^
|
||||
bytes to be read instead of a full line,
|
||||
but will return when reading from a slow device
|
||||
as soon as any characters have been read.
|
||||
.TP 8
|
||||
.B \-p
|
||||
option causes the input line
|
||||
to be taken from the input pipe
|
||||
of a process spawned by the shell
|
||||
using
|
||||
.BR |& .
|
||||
If the
|
||||
Input is read from the current co-process spawned by the shell using
|
||||
.BR \(bv& .
|
||||
An end-of-file causes
|
||||
.B read\^
|
||||
to disconnect the co-process so that another can be created.
|
||||
.TP 8
|
||||
.B \-r
|
||||
Raw mode. The
|
||||
.B \e
|
||||
character is not treated specially.
|
||||
.TP 8
|
||||
.B \-s
|
||||
option is present,
|
||||
the input will be saved as a command in the history file.
|
||||
The option
|
||||
The input will be saved as a command in the history file.
|
||||
.TP 8
|
||||
.B \-t
|
||||
Used to specify a timeout in
|
||||
seconds when reading from a terminal or pipe.
|
||||
.TP 8
|
||||
.B \-u
|
||||
can be used to specify a one digit file
|
||||
This option can be used to specify a one-digit file
|
||||
descriptor unit
|
||||
.I unit\^
|
||||
to read from.
|
||||
The file descriptor can be opened with the
|
||||
.B exec\^
|
||||
special built-in command.
|
||||
The default value of unit
|
||||
.I n\^
|
||||
is 0.
|
||||
The option
|
||||
.B \-t
|
||||
is used to specify a timeout in
|
||||
seconds when reading from a terminal or pipe.
|
||||
If
|
||||
.I vname\^
|
||||
is omitted, then
|
||||
.SM
|
||||
.B REPLY
|
||||
is used as the default
|
||||
.IR vname .
|
||||
An end-of-file with the
|
||||
.B \-p
|
||||
option causes cleanup for this process
|
||||
so that another can be spawned.
|
||||
If the first argument contains a
|
||||
.BR ? ,
|
||||
the remainder of this word is used as a
|
||||
.I prompt\^
|
||||
on standard error
|
||||
when the shell is interactive.
|
||||
The exit status is 0 unless an end-of-file is encountered
|
||||
or
|
||||
.B read
|
||||
has timed out.
|
||||
.B redirect\^
|
||||
built-in command.
|
||||
If
|
||||
.I unit\^
|
||||
is
|
||||
.BR p ,
|
||||
input is read from the current co-process as with the
|
||||
.B \-p
|
||||
option.
|
||||
The default value of
|
||||
.I unit\^
|
||||
is 0.
|
||||
.TP 8
|
||||
.B \-v
|
||||
The value of the first
|
||||
.I vname\^
|
||||
will be used as a default value when reading from a terminal device.
|
||||
.RE
|
||||
|
||||
.TP
|
||||
\(dg\(dd \f3readonly\fP \*(OK \f3\-p\fP \*(CK \*(OK \f2vname\fP\*(OK\f3=\fP\f2value\^\fP\*(CK \*(CK .\|.\|.
|
||||
If
|
||||
|
@ -7332,6 +7348,7 @@ does not create a function-local scope and the given
|
|||
are marked globally read-only by default.
|
||||
When defining a type, if the value of a read-only subvariable is not defined,
|
||||
the value is required when creating each instance.
|
||||
|
||||
.TP
|
||||
\f3redirect\fP
|
||||
This command only accepts input/output redirections.
|
||||
|
@ -7350,6 +7367,7 @@ When invoking another program, file descriptors greater than
|
|||
that were opened with this mechanism are only passed on if they are explicitly
|
||||
redirected to themselves as part of the invocation (e.g. \fB4>&4\fR)
|
||||
or if the \fBposix\fR option is set.
|
||||
|
||||
.TP
|
||||
\(dg \f3return\fP \*(OK \f2n\^\fP \*(CK
|
||||
Causes a shell function, dot script (see \f3\|.\fP and \f3source\fP),
|
||||
|
@ -7368,6 +7386,7 @@ If
|
|||
is invoked while not in a function, dot script, or profile script,
|
||||
then it behaves the same as
|
||||
.BR exit .
|
||||
|
||||
.TP
|
||||
\(dg \f3set\fP \*(OK \f3\(+-BCGHabefhkmnprstuvx\fP \*(CK \*(OK \f3\(+-o\fP \*(OK \f2option\^\fP \*(CK \*(CK .\|.\|. \*(OK \f3\(+-A\fP \f2vname\^\fP \*(CK \*(OK \f2arg\^\fP .\|.\|. \*(CK
|
||||
The options for this command have meaning as follows:
|
||||
|
|
|
@ -1529,5 +1529,20 @@ fi
|
|||
# using microseconds in the form of <num>U.
|
||||
got=$(sleep 1U 2>&1) || err_exit "sleep builtin cannot handle microseconds in the form of <num>U (got $(printf %q "$got"))"
|
||||
|
||||
# ======
|
||||
# Tests to avoid backporting an inherently broken 'read -p' compat hack from ksh 93v- or ksh2020
|
||||
# Discussion: https://github.com/ksh93/ksh/issues/463
|
||||
# ...It hangs in an infinite option parsing loop on encountering a stacked 'p' option
|
||||
(echo ok |& read -pt1 && [[ $REPLY == ok ]]) &
|
||||
test_pid=$!
|
||||
(sleep 1; kill -s KILL "$test_pid" 2>/dev/null) & # another bg job to kill frozen test job
|
||||
sleep_pid=$!
|
||||
{ wait "$test_pid"; } 2>/dev/null # get job's exit status, suppressing signal messages
|
||||
((!(e = $?))) || err_exit "'read -pt1' hangs (got status $e$( ((e>128)) && print -n /SIG && kill -l "$e"))"
|
||||
kill "$sleep_pid" 2>/dev/null
|
||||
# ...It failed to implement co-process compatibility for 'read -p .dot.varname' or 'read -p varname?prompt'
|
||||
(echo ok |& read -p .sh.foo"?dummy prompt: " && [[ ${.sh.foo} == ok ]]) </dev/null \
|
||||
|| err_exit "'read -p' co-process usage is not fully backward compatible with ksh 93u+"
|
||||
|
||||
# ======
|
||||
exit $((Errors<125?Errors:125))
|
||||
|
|
Loading…
Reference in a new issue