1
0
Fork 0
mirror of git://git.code.sf.net/p/cdesktopenv/code synced 2025-02-15 04:32:24 +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. 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: 2022-02-15:
- A bug was fixed in fast filescan loops (like 'while <file; do ...; done', - 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) int b_read(int argc,char *argv[], Shbltin_t *context)
{ {
Sfdouble_t sec; Sfdouble_t sec;
register char *name; char *prompt;
register int r, flags=0, fd=0; register int r, flags=0, fd=0;
ssize_t len=0; ssize_t len=0;
long timeout = 1000*sh.st.tmout; long timeout = 1000*sh.st.tmout;
@ -82,7 +82,7 @@ int b_read(int argc,char *argv[], Shbltin_t *context)
timeout = rp->timeout; timeout = rp->timeout;
fd = rp->fd; fd = rp->fd;
argv = rp->argv; argv = rp->argv;
name = rp->prompt; prompt = rp->prompt;
r = rp->plen; r = rp->plen;
goto bypass; goto bypass;
} }
@ -107,6 +107,7 @@ int b_read(int argc,char *argv[], Shbltin_t *context)
} }
break; break;
case 'p': case 'p':
coprocess:
if((fd = sh.cpipe[0])<=0) if((fd = sh.cpipe[0])<=0)
{ {
errormsg(SH_DICT,ERROR_exit(1),e_query); 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; flags |= SS_FLAG;
break; break;
case 'u': case 'u':
fd = (int)opt_info.num; if(opt_info.arg[0]=='p' && opt_info.arg[1]==0)
if(opt_info.num<0 || opt_info.num>INT_MAX || (fd>=sh.lim.open_max && !sh_iovalidfd(fd))) goto coprocess;
{ fd = (int)strtol(opt_info.arg,&opt_info.arg,10);
errormsg(SH_DICT,ERROR_exit(1),e_file,opt_info.arg); /* reject invalid file descriptors */ if(*opt_info.arg || !sh_iovalidfd(fd) || sh_inuse(fd))
UNREACHABLE();
}
if(sh_inuse(fd))
fd = -1; fd = -1;
break; break;
case 'v': case 'v':
@ -162,8 +160,8 @@ int b_read(int argc,char *argv[], Shbltin_t *context)
UNREACHABLE(); UNREACHABLE();
} }
/* look for prompt */ /* look for prompt */
if((name = *argv) && (name=strchr(name,'?')) && (r&IOTTY)) if((prompt = *argv) && (prompt=strchr(prompt,'?')) && (r&IOTTY))
r = strlen(name++); r = strlen(prompt++);
else else
r = 0; r = 0;
if(argc==fixargs) if(argc==fixargs)
@ -174,7 +172,7 @@ int b_read(int argc,char *argv[], Shbltin_t *context)
rp->flags = flags; rp->flags = flags;
rp->timeout = timeout; rp->timeout = timeout;
rp->argv = argv; rp->argv = argv;
rp->prompt = name; rp->prompt = prompt;
rp->plen = r; rp->plen = r;
rp->len = len; rp->len = len;
} }
@ -182,7 +180,7 @@ bypass:
sh.prompt = default_prompt; sh.prompt = default_prompt;
if(r && (sh.prompt=(char*)sfreserve(sfstderr,r,SF_LOCKR))) 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); sfwrite(sfstderr,sh.prompt,r-1);
} }
sh.timeout = 0; sh.timeout = 0;

View file

@ -1404,7 +1404,7 @@ const char sh_optpwd[] =
; ;
const char sh_optread[] = 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 "]" "[--catalog?" SH_DICT "]"
"[+NAME?read - read a line from standard input]" "[+NAME?read - read a line from standard input]"
"[+DESCRIPTION?\bread\b reads a line from standard input and breaks it " "[+DESCRIPTION?\bread\b reads a line from standard input and breaks it "
@ -1419,28 +1419,29 @@ const char sh_optread[] =
"\bREPLY\b is used.]" "\bREPLY\b is used.]"
"[+?When \avar\a has the binary attribute and \b-n\b or \b-N\b is specified, " "[+?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.]" "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 " "[+?If you append \b?\b\aprompt\a to the first \avar\a, then \bread\b "
"will display \aprompt\a on standard error when standard input " "will display \aprompt\a on standard error before reading "
"is a terminal or pipe.]" "if standard input is a terminal or pipe. "
"[+?If an end of file is encountered while reading a line the data is " "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.]" "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.]" "the line starting at index 0.]"
"[C?Unset \avar\a and read \avar\a as a compound variable.]" "[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.]" "[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 " "[n]#[count?Read at most \acount\a characters or (for binary fields) bytes.]"
"file causes \bread\b to disconnect the co-process so that another " "[N]#[count?Read exactly \acount\a characters or (for binary fields) bytes.]"
"can be created.]" "[p?Read from the current co-process instead of standard input. "
"[r?Do not treat \b\\\b specially when processing the input line.]" "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?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.]" "[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 " "[t]:[timeout?Specify a timeout \atimeout\a in seconds when reading from "
"a terminal or pipe.]" "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 " "[v?When reading from a terminal the value of the first variable is displayed "
"and used as a default value.]" "and used as a default value.]"
"\n" "\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_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_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 #define SH_RELEASE_CPYR "(c) 2020-2022 Contributors to ksh " SH_RELEASE_FORK
/* Scripts sometimes field-split ${.sh.version}, so don't change amount of whitespace. */ /* Scripts sometimes field-split ${.sh.version}, so don't change amount of whitespace. */

View file

@ -7152,8 +7152,7 @@ or
formats, separates groups of digits with the grouping delimiter formats, separates groups of digits with the grouping delimiter
.RB ( , .RB ( ,
on groups of 3 in the C locale). on groups of 3 in the C locale).
.PD .PP
.TP
The \f3\-v\fP option assigns the output directly to a variable instead of 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 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 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. name, optionally with one or more array subscripts in square brackets.
Note that square brackets should be quoted to avoid pathname expansion. Note that square brackets should be quoted to avoid pathname expansion.
.RE .RE
.TP .TP
\f3pwd\fP \*(OK \f3\-LP\fP \*(CK \f3pwd\fP \*(OK \f3\-LP\fP \*(CK
Outputs the value of the current working Outputs the value of the current working
@ -7179,7 +7179,7 @@ or
on the command line on the command line
determines which method is used. determines which method is used.
.TP .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. The shell input mechanism.
One line is read and One line is read and
is broken up into fields using the characters in is broken up into fields using the characters in
@ -7190,32 +7190,6 @@ The escape character,
.BR \e , .BR \e ,
is used to remove any special meaning for the next is used to remove any special meaning for the next
character and for line continuation. 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 The first
field is assigned to the first field is assigned to the first
.IR vname , .IR vname ,
@ -7224,6 +7198,13 @@ to the second
.IR vname , .IR vname ,
etc., with leftover fields assigned to the last etc., with leftover fields assigned to the last
.IR vname . .IR vname .
If
.I vname\^
is omitted, then
.SM
.B REPLY
is used as the default
.IR vname .
When When
.IR vname .IR vname
has the binary attribute and has the binary attribute and
@ -7232,78 +7213,113 @@ or
.B \-N .B \-N
is specified, the bytes that are read are stored directly is specified, the bytes that are read are stored directly
into the variable. into the variable.
If the If you append \f3?\fP\f2prompt\fP to the first
.B \-v .IR vname\^ ,
is specified, then the value of the first then
.I vname\^ .B read\^
will be used as a default value when reading from a terminal device. will display
The .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 .B \-A
option causes the variable Causes the variable
.I vname\^ .I vname\^
to be unset and each field that is read to be stored in to be unset and each field that is read to be stored in
successive elements of the indexed array successive elements of the indexed array
.IR vname. .IR vname.
The .TP 8
.B \-C .B \-C
option causes the variable Causes the variable
.I vname\^ .I vname\^
to be read as a compound variable. Blanks will be ignored when to be read as a compound variable. Blanks will be ignored when
finding the beginning open parenthesis. 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 .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 .B .csv
format file so that double quotes can be used to allow the delimiter format file so that double quotes can be used to allow the delimiter
character and the new-line character to appear within a field. 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 .B \-p
option causes the input line Input is read from the current co-process spawned by the shell using
to be taken from the input pipe .BR \(bv& .
of a process spawned by the shell An end-of-file causes
using .B read\^
.BR |& . to disconnect the co-process so that another can be created.
If the .TP 8
.B \-r
Raw mode. The
.B \e
character is not treated specially.
.TP 8
.B \-s .B \-s
option is present, The input will be saved as a command in the history file.
the input will be saved as a command in the history file. .TP 8
The option .B \-t
Used to specify a timeout in
seconds when reading from a terminal or pipe.
.TP 8
.B \-u .B \-u
can be used to specify a one digit file This option can be used to specify a one-digit file
descriptor unit descriptor unit
.I unit\^ .I unit\^
to read from. to read from.
The file descriptor can be opened with the The file descriptor can be opened with the
.B exec\^ .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 or
.B read .B redirect\^
has timed out. 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 .TP
\(dg\(dd \f3readonly\fP \*(OK \f3\-p\fP \*(CK \*(OK \f2vname\fP\*(OK\f3=\fP\f2value\^\fP\*(CK \*(CK .\|.\|. \(dg\(dd \f3readonly\fP \*(OK \f3\-p\fP \*(CK \*(OK \f2vname\fP\*(OK\f3=\fP\f2value\^\fP\*(CK \*(CK .\|.\|.
If If
@ -7332,6 +7348,7 @@ does not create a function-local scope and the given
are marked globally read-only by default. are marked globally read-only by default.
When defining a type, if the value of a read-only subvariable is not defined, When defining a type, if the value of a read-only subvariable is not defined,
the value is required when creating each instance. the value is required when creating each instance.
.TP .TP
\f3redirect\fP \f3redirect\fP
This command only accepts input/output redirections. 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 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) redirected to themselves as part of the invocation (e.g. \fB4>&4\fR)
or if the \fBposix\fR option is set. or if the \fBposix\fR option is set.
.TP .TP
\(dg \f3return\fP \*(OK \f2n\^\fP \*(CK \(dg \f3return\fP \*(OK \f2n\^\fP \*(CK
Causes a shell function, dot script (see \f3\|.\fP and \f3source\fP), 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, is invoked while not in a function, dot script, or profile script,
then it behaves the same as then it behaves the same as
.BR exit . .BR exit .
.TP .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 \(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: The options for this command have meaning as follows:

View file

@ -1529,5 +1529,20 @@ fi
# using microseconds in the form of <num>U. # 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"))" 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)) exit $((Errors<125?Errors:125))