1
0
Fork 0
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:
Martijn Dekker 2022-02-16 07:19:37 +00:00
parent 95d695cb5a
commit d55e9686d7
6 changed files with 149 additions and 108 deletions

8
NEWS
View file

@ -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',

View file

@ -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;

View file

@ -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"

View file

@ -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. */

View file

@ -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:

View file

@ -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))