Skip to content

Commit 834ecba

Browse files
authored
Support POSIX.1-2024 read built-in behavior (except byte-wise field splitting) (#123)
2 parents 18fc747 + 8c691c5 commit 834ecba

File tree

13 files changed

+355
-212
lines changed

13 files changed

+355
-212
lines changed

NEWS

+4
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
## Yash 2.59 (Unreleased)
44

55
- Improved POSIX.1-2024 support:
6+
- The `read` built-in now returns a more specific exit status
7+
depending on the cause of the error.
8+
- The `read` built-in now supports the `-d` option, which can be
9+
used to specify a delimiter character.
610
- The `trap` built-in now supports the `-p` option in the POSIXly-
711
correct mode. When the `-p` option is used, the built-in prints
812
all traps without filtering out traps that are not set.

NEWS.ja

+3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
## Yash 2.59 (未リリース)
44

55
- POSIX.1-2024 のサポートを強化:
6+
- `read` 組込みは結果に応じてより細分化された終了ステータスを返す
7+
ようになった
8+
- `read` 組込みに行区切り文字を指定する `-d` オプションを追加
69
- POSIX 準拠モードで `trap` 組込みに `-p` オプションを使えるように
710
なった。 `-p` オプションを使用する場合、設定が変更されていないもの
811
も含めて全てのトラップを表示する。

doc/_read.txt

+21-7
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ The dfn:[read built-in] reads a line from the standard input.
88
[[syntax]]
99
== Syntax
1010

11-
- +read [-Aer] [-P|-p] {{variable}}...+
11+
- +read [-Aer] [-d {{delimiter}}] [-P|-p {{prompt}}] {{variable}}...+
1212

1313
[[description]]
1414
== Description
@@ -27,6 +27,11 @@ When the built-in reads the next line, the link:params.html#sv-ps2[+PS2+
2727
variable] is used as a prompt if the shell is link:interact.html[interactive]
2828
and the standard input is a terminal.
2929

30+
By default, the input line is read up to a newline character.
31+
If the +-d+ (+--delimiter+) option is specified, the input line is read up to
32+
the specified {{delimiter}} character instead.
33+
The {{delimiter}} character must be a single character.
34+
3035
The input line is subject to link:expand.html#split[field splitting].
3136
The resulting words are assigned to {{variable}}s in order.
3237
If there are more words than {{variable}}s, the last variable is assigned all
@@ -43,6 +48,10 @@ Make the last {{variable}} an link:params.html#arrays[array].
4348
Instead of assigning a concatenation of the remaining words to a normal
4449
variable, the words are assigned to an array.
4550

51+
+-d {{delimiter}}+::
52+
+--delimiter={{delimiter}}+::
53+
Read up to the specified {{delimiter}} character instead of a newline.
54+
4655
+-e+::
4756
+--line-editing+::
4857
Use link:lineedit.html[line-editing] to read the line.
@@ -53,6 +62,7 @@ To use line-editing, all of the following conditions must also be met:
5362
- The link:_set.html#so-vi[vi] or link:_set.html#so-emacs[emacs] option is
5463
enabled.
5564
- The standard input and standard error are connected to a terminal.
65+
- The {{delimiter}} is a newline.
5666

5767
+-P+::
5868
+--ps1+::
@@ -78,18 +88,22 @@ Names of variables to which input words are assigned.
7888
[[exitstatus]]
7989
== Exit status
8090

81-
The exit status of the read built-in is zero unless there is any error.
82-
83-
Note that the exit status is non-zero if an end of input is encountered before
84-
reading the entire line.
91+
The exit status is 0 if the read built-in successfully reads a line.
92+
If the end of the input is reached, the exit status is 1.
93+
The variables are assigned the words read so far.
94+
If a variable is read-only, the exit status is 2.
95+
If the standard input is unreadable, the exit status is 3.
96+
In case of a syntax error in the command line arguments, the exit status is 4.
8597

8698
[[notes]]
8799
== Notes
88100

89101
The read built-in is a link:builtin.html#types[mandatory built-in].
90102

91-
The POSIX standard defines the +-r+ option only:
92-
other options cannot be used in the link:posix.html[POSIXly-correct mode].
103+
The POSIX standard defines the +-d+ and +-r+ options only.
104+
Other options cannot be used in the link:posix.html[POSIXly-correct mode].
105+
Only the exit status 0 and 1 are portable; for other error conditions, the
106+
exit status may be different in other shells.
93107

94108
The link:params.html#sv-ps1r[+PS1R+] and link:params.html#sv-ps1s[+PS1S+]
95109
variables affect the behavior of line-editing if the +PS1+ prompt is used. The

doc/ja/_read.txt

+15-5
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ dfn:[Read 組込みコマンド]は標準入力から行を読み込み変数に
88
[[syntax]]
99
== 構文
1010

11-
- +read [-Aer] [-P|-p] {{変数名}}...+
11+
- +read [-Aer] [-d {{行区切り}}] [-P|-p {{プロンプト}}] {{変数名}}...+
1212

1313
[[description]]
1414
== 説明
@@ -19,6 +19,8 @@ Read コマンドは標準入力から一行の文字列を読み込み、それ
1919

2020
+-r+ (+--raw-mode+) オプションを付けない場合、読み込んだ文字列の中のバックスラッシュ (+\+) は{zwsp}link:syntax.html#quotes[引用符]として働きます。バックスラッシュが行末にあるときは行の連結を行います。{zwsp}link:interact.html[対話モード]のシェルが 2 行目以降を読み込むとき、標準入力が端末ならば link:params.html#sv-ps2[+PS2+ 変数]の値がプロンプトとして出力されます。
2121

22+
通常、入力行は改行文字まで読み込まれます。+-d+ (+--delimiter+) オプションを指定すると、改行文字の代わりに指定された{{行区切り}}文字まで読み込みます。{{行区切り}}文字は 1 文字である必要があります。
23+
2224
読み込んだ文字列は、{zwsp}link:expand.html#split[単語分割]によって分割します。分割後の各文字列が、それぞれオペランドで指定された変数の値に順に設定されます。指定された変数の数より分割結果のほうが多い場合は、最後の変数に残りの分割結果の全てが入ります。分割結果の数より指定された変数のほうが多い場合は、余った変数には空文字列が入ります。
2325

2426
[[options]]
@@ -28,6 +30,10 @@ Read コマンドは標準入力から一行の文字列を読み込み、それ
2830
+--array+::
2931
最後に指定した変数を{zwsp}link:params.html#arrays[配列]にします。分割後の各文字列が配列の要素として設定されます。
3032

33+
+-d {{行区切り}}+::
34+
+--delimiter={{行区切り}}+::
35+
改行文字の代わりに指定された{{行区切り}}文字まで読み込みます。
36+
3137
+-e+::
3238
+--line-editing+::
3339
読み込みに{zwsp}link:lineedit.html[行編集]を使用します。
@@ -37,6 +43,7 @@ Read コマンドは標準入力から一行の文字列を読み込み、それ
3743
- シェルが{zwsp}link:interact.html[対話モード]である。
3844
- link:_set.html#so-vi[vi] または link:_set.html#so-emacs[emacs] オプションが有効になっている。
3945
- 標準入力と標準エラーが端末である。
46+
- {{行区切り}}が改行文字である。
4047

4148
+-P+::
4249
+--ps1+::
@@ -59,16 +66,19 @@ Read コマンドは標準入力から一行の文字列を読み込み、それ
5966
[[exitstatus]]
6067
== 終了ステータス
6168

62-
エラーがない限り read コマンドの終了ステータスは 0 です。
63-
64-
なお、行を完全に読み込む前に入力が終端に達した時は終了ステータスは非 0 になります。
69+
行を正常に読み込んだ場合は終了ステータスは 0 です。
70+
入力の終わりに達した場合は 1 です。この場合はそれまでに読み込んだ文字列が変数に代入されます。
71+
変数が読み取り専用の場合は 2 です。
72+
標準入力が読み込めない場合は 3 です。
73+
コマンドライン引数に構文エラーがある場合は 4 です。
6574

6675
[[notes]]
6776
== 補足
6877

6978
Read コマンドは{zwsp}link:builtin.html#types[必須組込みコマンド]です。
7079

71-
POSIX では +-r+ オプションのみが規定されています。他のオプションは link:posix.html[POSIX 準拠モード]では使えません。
80+
POSIX では +-d+ と +-r+ オプションのみが規定されています。他のオプションは link:posix.html[POSIX 準拠モード]では使えません。
81+
終了ステータスは 0 と 1 だけが POSIX で固定されています。他のエラーの場合はシェルによって終了ステータスが異なります。
7282

7383
+PS1+ 変数をプロンプトとして表示する際、{zwsp}link:params.html#sv-ps1r[+PS1R+] および link:params.html#sv-ps1s[+PS1S+] 変数も使用されます。 +PS2+ についても同様です。
7484

input.c

+16-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/* Yash: yet another shell */
22
/* input.c: functions for input of command line */
3-
/* (C) 2007-2024 magicant */
3+
/* (C) 2007-2025 magicant */
44

55
/* This program is free software: you can redistribute it and/or modify
66
* it under the terms of the GNU General Public License as published by
@@ -54,7 +54,8 @@
5454

5555
static bool is_seekable_file(int fd);
5656
static inputresult_T optimized_read_input(
57-
struct xwcsbuf_T *buf, struct input_file_info_T *info, _Bool trap)
57+
struct xwcsbuf_T *buf, struct input_file_info_T *info, bool trap,
58+
wchar_t delimiter)
5859
__attribute__((nonnull));
5960
static wchar_t *expand_prompt_variable(wchar_t num, wchar_t suffix)
6061
__attribute__((malloc,warn_unused_result));
@@ -110,9 +111,17 @@ inputresult_T input_file(struct xwcsbuf_T *buf, void *inputinfo)
110111
* INPUT_ERROR if an error occurred before reading any characters */
111112
inputresult_T read_input(
112113
xwcsbuf_T *buf, struct input_file_info_T *info, bool trap)
114+
{
115+
return read_input_delimited(buf, info, trap, L'\n');
116+
}
117+
118+
/* Like `read_input', but reads input until a delimiter character is found. */
119+
inputresult_T read_input_delimited(
120+
xwcsbuf_T *buf, struct input_file_info_T *info, bool trap,
121+
wchar_t delimiter)
113122
{
114123
if (info->bufsize == 1 && is_seekable_file(info->fd))
115-
return optimized_read_input(buf, info, trap);
124+
return optimized_read_input(buf, info, trap, delimiter);
116125

117126
size_t initlen = buf->length;
118127
inputresult_T status = INPUT_EOF;
@@ -170,7 +179,7 @@ inputresult_T read_input(
170179
default:
171180
info->bufpos += convcount;
172181
buf->contents[++buf->length] = L'\0';
173-
if (buf->contents[buf->length - 1] == L'\n')
182+
if (buf->contents[buf->length - 1] == delimiter)
174183
goto end;
175184
break;
176185
}
@@ -200,7 +209,8 @@ bool is_seekable_file(int fd)
200209
* once even if `info->bufsize' is 1. The input file descriptor must be
201210
* seekable. */
202211
inputresult_T optimized_read_input(
203-
struct xwcsbuf_T *buf, struct input_file_info_T *info, _Bool trap)
212+
struct xwcsbuf_T *buf, struct input_file_info_T *info, bool trap,
213+
wchar_t delimiter)
204214
{
205215
struct input_file_info_T *tmpinfo =
206216
xmallocs(sizeof *tmpinfo, BUFSIZ, sizeof *tmpinfo->buf);
@@ -212,7 +222,7 @@ inputresult_T optimized_read_input(
212222
while (info->bufpos < info->bufmax)
213223
tmpinfo->buf[tmpinfo->bufmax++] = info->buf[info->bufpos++];
214224

215-
inputresult_T result = read_input(buf, tmpinfo, trap);
225+
inputresult_T result = read_input_delimited(buf, tmpinfo, trap, delimiter);
216226

217227
if (tmpinfo->bufpos < tmpinfo->bufmax) {
218228
/* rewind the FD to pretend we're not buffering */

input.h

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/* Yash: yet another shell */
22
/* input.h: functions for input of command line */
3-
/* (C) 2007-2018 magicant */
3+
/* (C) 2007-2025 magicant */
44

55
/* This program is free software: you can redistribute it and/or modify
66
* it under the terms of the GNU General Public License as published by
@@ -59,6 +59,10 @@ struct input_file_info_T;
5959
extern inputresult_T read_input(
6060
struct xwcsbuf_T *buf, struct input_file_info_T *info, _Bool trap)
6161
__attribute__((nonnull));
62+
extern inputresult_T read_input_delimited(
63+
struct xwcsbuf_T *buf, struct input_file_info_T *info, _Bool trap,
64+
wchar_t delimiter)
65+
__attribute__((nonnull));
6266

6367
/* The type of input functions.
6468
* An input function reads input and appends it to buffer `buf'.

0 commit comments

Comments
 (0)