• R/O
  • SSH
  • HTTPS

yash: Commit


Commit MetaInfo

Revision3929 (tree)
Time2018-11-30 00:05:40
Authormagicant

Log Message

Merge double_bracket branch

Change Summary

Incremental Difference

--- yash/trunk/INSTALL (revision 3928)
+++ yash/trunk/INSTALL (revision 3929)
@@ -86,6 +86,10 @@
8686 --enable-dirstack --disable-dirstack
8787 If disabled, the `dirs', `pushd', and `popd' built-in commands are
8888 not available.
89+ --enable-double-bracket --disable-double-bracket
90+ If disabled, the double-bracket command (the [[ ... ]] syntax) is
91+ not available. When this feature is enabled, the `test' built-in
92+ must also be enabled.
8993 --enable-help --disable-help
9094 If disabled, the `help' built-in command is not available.
9195 --enable-history --disable-history
--- yash/trunk/NEWS (revision 3928)
+++ yash/trunk/NEWS (revision 3929)
@@ -10,6 +10,7 @@
1010 ----------------------------------------------------------------------
1111 Yash 2.48
1212
13+ + The double-bracket command (the [[ ... ]] syntax)
1314 + The prompt string now can be defined with the $YASH_PS...
1415 variables.
1516 = The default value of $PS1 has been changed.
--- yash/trunk/builtins/test.c (revision 3928)
+++ yash/trunk/builtins/test.c (revision 3929)
@@ -1,6 +1,6 @@
11 /* Yash: yet another shell */
22 /* test.c: test builtin */
3-/* (C) 2007-2012 magicant */
3+/* (C) 2007-2018 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
@@ -27,7 +27,13 @@
2727 #include <unistd.h>
2828 #include <wchar.h>
2929 #include <wctype.h>
30+#if YASH_ENABLE_DOUBLE_BRACKET
31+# include "../expand.h"
32+#endif
3033 #include "../option.h"
34+#if YASH_ENABLE_DOUBLE_BRACKET
35+# include "../parser.h"
36+#endif
3137 #include "../path.h"
3238 #include "../plist.h"
3339 #include "../strbuf.h"
@@ -59,10 +65,6 @@
5965 __attribute__((nonnull));
6066 static bool test_long_term(struct test_state *state)
6167 __attribute__((nonnull));
62-static bool is_unary_primary(const wchar_t *word)
63- __attribute__((nonnull,pure));
64-static bool is_binary_primary(const wchar_t *word)
65- __attribute__((nonnull,pure));
6668 static bool is_term_delimiter(const wchar_t *word)
6769 __attribute__((nonnull,pure));
6870 static int compare_integers(const wchar_t *left, const wchar_t *right)
@@ -72,7 +74,22 @@
7274 static enum filecmp compare_files(const wchar_t *left, const wchar_t *right)
7375 __attribute__((nonnull));
7476
77+#if YASH_ENABLE_DOUBLE_BRACKET
78+static int eval_dbexp(const dbexp_T *e)
79+ __attribute__((nonnull));
80+static inline wchar_t *expand_double_bracket_operand(const wordunit_T *w)
81+ __attribute__((nonnull,malloc,warn_unused_result));
82+static wchar_t *expand_and_unescape_double_bracket_operand(const wordunit_T *w)
83+ __attribute__((nonnull,malloc,warn_unused_result));
84+static bool test_triple_db(
85+ const wchar_t *lhs, const wchar_t *op, const wchar_t *rhs_escaped)
86+ __attribute__((nonnull));
87+static bool test_triple_args(
88+ const wchar_t *left, const wchar_t *op, const wchar_t *right)
89+ __attribute__((nonnull));
90+#endif
7591
92+
7693 /* The "test" ("[") built-in. */
7794 int test_builtin(int argc, void **argv)
7895 {
@@ -687,5 +704,134 @@
687704 );
688705 #endif
689706
707+#if YASH_ENABLE_DOUBLE_BRACKET
690708
709+/* Executes the double-bracket command, evaluating the conditional expression.
710+ * Returns the exit status. Prints an error message on error. */
711+int exec_double_bracket(const command_T *c)
712+{
713+ assert(c->c_type == CT_BRACKET);
714+
715+ yash_error_message_count = 0;
716+ return eval_dbexp(c->c_dbexp);
717+}
718+
719+/* Evaluates the expression of a double-bracket command.
720+ * You must reset `yash_error_message_count' before calling this function.
721+ * Returns the exit status. Prints an error message on error. */
722+int eval_dbexp(const dbexp_T *e)
723+{
724+ int lhs_result;
725+ bool result;
726+ wchar_t *lhs = NULL, *rhs = NULL;
727+
728+ switch (e->type) {
729+ case DBE_OR:
730+ lhs_result = eval_dbexp(e->lhs.subexp);
731+ if (lhs_result != Exit_FALSE)
732+ return lhs_result;
733+ return eval_dbexp(e->rhs.subexp);
734+ case DBE_AND:
735+ lhs_result = eval_dbexp(e->lhs.subexp);
736+ if (lhs_result != Exit_TRUE)
737+ return lhs_result;
738+ return eval_dbexp(e->rhs.subexp);
739+ case DBE_NOT:
740+ switch (eval_dbexp(e->rhs.subexp)) {
741+ case Exit_TRUE: return Exit_FALSE;
742+ case Exit_FALSE: return Exit_TRUE;
743+ default: return Exit_TESTERROR;
744+ }
745+
746+ case DBE_UNARY:
747+ rhs = expand_and_unescape_double_bracket_operand(e->rhs.word);
748+ if (rhs == NULL)
749+ return Exit_TESTERROR;
750+ result = test_double((void *[]) { e->operator, rhs });
751+ break;
752+ case DBE_BINARY:
753+ lhs = expand_and_unescape_double_bracket_operand(e->lhs.word);
754+ if (lhs == NULL)
755+ return Exit_TESTERROR;
756+ rhs = expand_double_bracket_operand(e->rhs.word);
757+ if (rhs == NULL)
758+ return Exit_TESTERROR;
759+ result = test_triple_db(lhs, e->operator, rhs);
760+ break;
761+ case DBE_STRING:
762+ rhs = expand_and_unescape_double_bracket_operand(e->rhs.word);
763+ if (rhs == NULL)
764+ return Exit_TESTERROR;
765+ result = test_single((void *[]) { rhs });
766+ break;
767+
768+ default:
769+ assert(false);
770+ }
771+
772+ free(lhs);
773+ free(rhs);
774+ if (yash_error_message_count > 0)
775+ return Exit_TESTERROR;
776+ return result ? Exit_TRUE : Exit_FALSE;
777+}
778+
779+/* Expands the operand of a primary.
780+ * The result may contain backslash escapes. */
781+wchar_t *expand_double_bracket_operand(const wordunit_T *w)
782+{
783+ return expand_single(w, TT_SINGLE, true, false);
784+}
785+
786+/* Expands the operand of a primary.
787+ * The result is literal (does not contain backslash escapes). */
788+wchar_t *expand_and_unescape_double_bracket_operand(const wordunit_T *w)
789+{
790+ wchar_t *e = expand_double_bracket_operand(w);
791+ if (e == NULL)
792+ return NULL;
793+ return unescapefree(e);
794+}
795+
796+/* Tests the specified three-token (binary) primary in the double-bracket
797+ * command. The left-hand-side must be given literal while the right-hand-side
798+ * backslash-escaped. */
799+bool test_triple_db(
800+ const wchar_t *lhs, const wchar_t *op, const wchar_t *rhs_escaped)
801+{
802+ /* Some string comparison primaries in the double-bracket command are
803+ * different from those in the test built-in. */
804+ switch (op[0]) {
805+ case L'=':
806+ if (op[1] == L'~') {
807+ assert(op[2] == L'\0');
808+ return test_triple_args(lhs, op, rhs_escaped);
809+ }
810+ if (op[1] == L'\0' || (op[1] == L'=' && op[2] == L'\0'))
811+ return match_pattern(lhs, rhs_escaped);
812+ break;
813+ case L'!':
814+ assert(op[1] == L'=');
815+ if (op[2] == L'\0')
816+ return !match_pattern(lhs, rhs_escaped);
817+ break;
818+ }
819+
820+ wchar_t *rhs = unescape(rhs_escaped);
821+ bool result = test_triple_args(lhs, op, rhs);
822+ free(rhs);
823+ return result;
824+}
825+
826+/* Tests the specified three-token expression. */
827+bool test_triple_args(
828+ const wchar_t *left, const wchar_t *op, const wchar_t *right)
829+{
830+ void *args[] = { (void *) left, (void *) op, (void *) right, };
831+ return test_triple(args);
832+}
833+
834+#endif /* YASH_ENABLE_DOUBLE_BRACKET */
835+
836+
691837 /* vim: set ts=8 sts=4 sw=4 noet tw=80: */
--- yash/trunk/builtins/test.h (revision 3928)
+++ yash/trunk/builtins/test.h (revision 3929)
@@ -1,6 +1,6 @@
11 /* Yash: yet another shell */
22 /* test.h: test builtin */
3-/* (C) 2007-2012 magicant */
3+/* (C) 2007-2018 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
@@ -20,6 +20,8 @@
2020 #define YASH_TEST_H
2121
2222
23+#include <stddef.h>
24+
2325 extern int test_builtin(int argc, void **argv)
2426 __attribute__((nonnull));
2527 #if YASH_ENABLE_HELP
@@ -26,7 +28,18 @@
2628 extern const char test_help[], test_syntax[];
2729 #endif
2830
31+extern _Bool is_unary_primary(const wchar_t *word)
32+ __attribute__((nonnull,pure));
33+extern _Bool is_binary_primary(const wchar_t *word)
34+ __attribute__((nonnull,pure));
2935
36+#if YASH_ENABLE_DOUBLE_BRACKET
37+struct command_T;
38+extern int exec_double_bracket(const struct command_T *c)
39+ __attribute__((nonnull));
40+#endif
41+
42+
3043 #endif /* YASH_TEST_H */
3144
3245
--- yash/trunk/configure (revision 3928)
+++ yash/trunk/configure (revision 3929)
@@ -44,6 +44,7 @@
4444 debug="false"
4545 enable_array="true"
4646 enable_dirstack="true"
47+enable_double_bracket="true"
4748 enable_nls="true"
4849 enable_help="true"
4950 enable_history="true"
@@ -95,17 +96,18 @@
9596 opt="${1#--*able-}"
9697 opt="${opt%%=*}"
9798 case "$opt" in
98- array) enable_array=$val ;;
99- dirstack) enable_dirstack=$val ;;
100- help) enable_help=$val ;;
101- history) enable_history=$val ;;
102- lineedit) enable_lineedit=$val ;;
103- nls) enable_nls=$val ;;
104- printf) enable_printf=$val ;;
105- socket) enable_socket=$val ;;
106- test) enable_test=$val ;;
107- ulimit) enable_ulimit=$val ;;
108- *) echo "$0: $1: invalid option" >&2; exit 2 ;;
99+ array) enable_array=$val ;;
100+ dirstack) enable_dirstack=$val ;;
101+ double-bracket) enable_double_bracket=$val ;;
102+ help) enable_help=$val ;;
103+ history) enable_history=$val ;;
104+ lineedit) enable_lineedit=$val ;;
105+ nls) enable_nls=$val ;;
106+ printf) enable_printf=$val ;;
107+ socket) enable_socket=$val ;;
108+ test) enable_test=$val ;;
109+ ulimit) enable_ulimit=$val ;;
110+ *) echo "$0: $1: invalid option" >&2; exit 2 ;;
109111 esac
110112 }
111113
@@ -184,19 +186,20 @@
184186 --htmldir=DIR html documentation [DOCDIR]
185187
186188 Optional features:
187- --enable-FEATURE[=ARG] enable or disable FEATURE [ARG=yes]
188- --disable-FEATURE disable FEATURE (same as --enable-FEATURE=no)
189- --enable-array enable the array builtin
190- --enable-dirstack enable the directory stack (pushd, popd, dirs)
191- --enable-help enable the help builtin
192- --enable-history enable history
193- --enable-lineedit enable command line editing
194- --enable-nls enable native language support
195- --enable-printf enable the echo/printf builtins
196- --enable-socket enable socket redirection by /dev/tcp, /dev/udp
197- --enable-test enable the test builtin
198- --enable-ulimit enable the ulimit builtin
199- --default-loadpath=DIR specify the default \$YASH_LOADPATH value
189+ --enable-FEATURE[=ARG] enable or disable FEATURE [ARG=yes]
190+ --disable-FEATURE disable FEATURE (same as --enable-FEATURE=no)
191+ --enable-array enable the array builtin
192+ --enable-dirstack enable the directory stack (pushd, popd, dirs)
193+ --enable-double-bracket enable the double-bracket command
194+ --enable-help enable the help builtin
195+ --enable-history enable history
196+ --enable-lineedit enable command line editing
197+ --enable-nls enable native language support
198+ --enable-printf enable the echo/printf builtins
199+ --enable-socket enable socket redirection by /dev/tcp, /dev/udp
200+ --enable-test enable the test builtin
201+ --enable-ulimit enable the ulimit builtin
202+ --default-loadpath=DIR specify the default \$YASH_LOADPATH value
200203
201204 Optional packages:
202205 --with-intl-lib=LIBS search space-separated LIBS for NLS
@@ -1498,6 +1501,12 @@
14981501 defconfigh "YASH_ENABLE_ARRAY"
14991502 fi
15001503
1504+# enable/disable the double-bracket command
1505+if ${enable_double_bracket}
1506+then
1507+ defconfigh "YASH_ENABLE_DOUBLE_BRACKET"
1508+fi
1509+
15011510 # enable/disable the directory stack
15021511 if ${enable_dirstack}
15031512 then
@@ -1536,6 +1545,15 @@
15361545 then
15371546 defconfigh "YASH_ENABLE_TEST"
15381547 builtin_objs="$builtin_objs "'$(TEST_OBJS)'
1548+else
1549+ if ${enable_double_bracket}
1550+ then
1551+ cat >&2 <<END
1552+The test built-in is required for the double-bracket command, but disabled.
1553+Add the "--disable-double-bracket" option and try again.
1554+END
1555+ fail
1556+ fi
15391557 fi
15401558
15411559 # check for getrlimit and setrlimit
--- yash/trunk/doc/fgrammar.txt (revision 3928)
+++ yash/trunk/doc/fgrammar.txt (revision 3929)
@@ -202,6 +202,7 @@
202202 <<d-for-command,ForCommand>> | +
203203 <<d-while-command,WhileCommand>> | +
204204 <<d-case-command,CaseCommand>> | +
205+<<d-double-bracket-command,DoubleBracketCommand>> | +
205206 <<d-function-command,FunctionCommand>>
206207
207208 [[d-subshell]]Subshell::
@@ -232,6 +233,28 @@
232233 [[d-case-item]]CaseItem::
233234 +(+? <<d-word,Word>> (+|+ Word)* +)+ <<d-complete-program,CompleteProgram>>
234235
236+[[d-double-bracket-command]]DoubleBracketCommand::
237++[[+ <<d-ors,Ors>> +]]+
238+
239+[[d-ors]]Ors::
240+<<d-ands,Ands>> (+||+ Ands)*
241+
242+[[d-ands]]Ands::
243+<<d-nots,Nots>> (+&&+ Nots)*
244+
245+[[d-nots]]Nots::
246+++!++* <<d-primary,Primary>>
247+
248+[[d-primary]]Primary::
249+(+-b+ | +-c+ | +-d+ | +-e+ | +-f+ | +-G+ | +-g+ | +-h+ | +-k+ | +-L+ | +-N+ |
250++-n+ | +-O+ | +-o+ | +-p+ | +-r+ | +-S+ | +-s+ | +-t+ | +-u+ | +-w+ | +-x+ |
251++-z+) <<d-word,Word>> | +
252+Word (+-ef+ | +-eq+ | +-ge+ | +-gt+ | +-le+ | +-lt+ | +-ne+ | +-nt+ | +-ot+ |
253++-veq+ | +-vge+ | +-vgt+ | +-vle+ | +-vlt+ | +-vne+ | +=+ | +==+ | +===+ |
254++=~+ | +!=+ | +!==+ | +<+ | +>+) Word | +
255++(+ <<d-ors,Ors>> +)+ | +
256+Word
257+
235258 [[d-function-command]]FunctionCommand::
236259 +function+ <<d-word,Word>> (+(+ +)+)? <<d-nl,NL>>*
237260 <<d-compound-command,CompoundCommand>> <<d-redirection,Redirection>>*
@@ -261,6 +284,10 @@
261284 [[d-nl]]NL::
262285 <newline>
263286
287+In the rule for <<d-primary,Primary>>, <<d-word,Word>> tokens must not be
288++]]+. Additionally, if a Primary starts with a Word, it must not be any of the
289+possible unary operators allowed in the rule.
290+
264291 In the rule for <<d-simple-command,SimpleCommand>>, a <<d-word,Word>> token is
265292 accepted only when the token cannot be parsed as the first token of an
266293 <<d-assignment,Assignment>>.
--- yash/trunk/doc/ja/fgrammar.txt (revision 3928)
+++ yash/trunk/doc/ja/fgrammar.txt (revision 3929)
@@ -176,6 +176,7 @@
176176 <<d-for-command,ForCommand>> | +
177177 <<d-while-command,WhileCommand>> | +
178178 <<d-case-command,CaseCommand>> | +
179+<<d-double-bracket-command,DoubleBracketCommand>> | +
179180 <<d-function-command,FunctionCommand>>
180181
181182 [[d-subshell]]Subshell::
@@ -206,6 +207,28 @@
206207 [[d-case-item]]CaseItem::
207208 +(+? <<d-word,Word>> (+|+ Word)* +)+ <<d-complete-program,CompleteProgram>>
208209
210+[[d-double-bracket-command]]DoubleBracketCommand::
211++[[+ <<d-ors,Ors>> +]]+
212+
213+[[d-ors]]Ors::
214+<<d-ands,Ands>> (+||+ Ands)*
215+
216+[[d-ands]]Ands::
217+<<d-nots,Nots>> (+&&+ Nots)*
218+
219+[[d-nots]]Nots::
220+++!++* <<d-primary,Primary>>
221+
222+[[d-primary]]Primary::
223+(+-b+ | +-c+ | +-d+ | +-e+ | +-f+ | +-G+ | +-g+ | +-h+ | +-k+ | +-L+ | +-N+ |
224++-n+ | +-O+ | +-o+ | +-p+ | +-r+ | +-S+ | +-s+ | +-t+ | +-u+ | +-w+ | +-x+ |
225++-z+) <<d-word,Word>> | +
226+Word (+-ef+ | +-eq+ | +-ge+ | +-gt+ | +-le+ | +-lt+ | +-ne+ | +-nt+ | +-ot+ |
227++-veq+ | +-vge+ | +-vgt+ | +-vle+ | +-vlt+ | +-vne+ | +=+ | +==+ | +===+ |
228++=~+ | +!=+ | +!==+ | +<+ | +>+) Word | +
229++(+ <<d-ors,Ors>> +)+ | +
230+Word
231+
209232 [[d-function-command]]FunctionCommand::
210233 +function+ <<d-word,Word>> (+(+ +)+)? <<d-nl,NL>>*
211234 <<d-compound-command,CompoundCommand>> <<d-redirection,Redirection>>*
@@ -235,6 +258,8 @@
235258 [[d-nl]]NL::
236259 <改行>
237260
261+ルール <<d-primary,Primary>> では、<<d-word,Word>> トークンは +]]+ に一致してはなりません。また、Primary が Word トークンで始まる場合にはその Word は同ルールで認められている単項演算子に一致してはなりません。
262+
238263 ルール <<d-simple-command,SimpleCommand>> では、<<d-word,Word>> トークンはそれが <<d-assignment,Assignment>> の始まりとは解釈できない場合にのみ採用されます。
239264
240265 ルール <<d-assignment,Assignment>> では、<<d-assignment-prefix,AssignmentPrefix>> と ++(++ の間に空白を置くことはできません。
--- yash/trunk/doc/ja/posix.txt (revision 3928)
+++ yash/trunk/doc/ja/posix.txt (revision 3929)
@@ -17,6 +17,7 @@
1717 - link:syntax.html#for[For ループ]で展開した単語はグローバル変数として代入します。変数名はポータブルな (すなわち ASCII の範囲内の) 文字しか使えません。
1818 - link:syntax.html#case[Case 文]の最初のパターンを +esac+ にすることはできません。
1919 - 予約語 +!+ の直後に空白を置かずに +(+ を置くことはできません。
20+- link:syntax.html#double-bracket[二重ブラケットコマンド]は使えません。
2021 - 予約語 +function+ を用いる形式の{zwsp}link:syntax.html#funcdef[関数定義]構文は使えません。関数名はポータブルな (すなわち ASCII の範囲内の) 文字しか使えません。
2122 - link:syntax.html#simple[単純コマンド]での{zwsp}link:params.html#arrays[配列]の代入はできません。
2223 - シェル実行中に link:params.html#sv-lc_ctype[+LC_CTYPE+ 変数]の値が変わっても、それをシェルのロケール情報に反映しません。
--- yash/trunk/doc/ja/syntax.txt (revision 3928)
+++ yash/trunk/doc/ja/syntax.txt (revision 3929)
@@ -23,7 +23,7 @@
2323
2424 以下のトークンは特定の場面においてdfn:[予約語]と見なされます。予約語は複合コマンドなどを構成する一部となります。
2525
26- ! { } case do done elif else esac fi
26+ ! { } [[ case do done elif else esac fi
2727 for function if in then until while
2828
2929 これらのトークンは以下の場面において予約語となります。
@@ -232,6 +232,62 @@
232232
233233 link:posix.html[POSIX 準拠モード]では、(+|+ で区切られた最初の) {{パターン}}トークンを +esac+ にすることはできません。
234234
235+[[double-bracket]]
236+=== 二重ブラケットコマンド
237+
238+dfn:[二重ブラケットコマンド]は link:_test.html[test コマンド]に近い動作をする構文です。ブラケットに囲まれた式を展開して評価します。
239+
240+二重ブラケットコマンド構文::
241+ +[[ {{式}} ]]+
242+
243+{{式}}は単一の原子式とすることも原子式や演算子の組み合わせとすることもできます。式の構文解析は、コマンドの実行時ではなく構文解析時に行われます。演算子 (原子式の演算子もそうでないものも) は{zwsp}link:#quotes[クォート]してはなりません。クォートすると通常の単語と見なされます。
244+
245+コマンドが実行されるとき、被演算子となる単語は{zwsp}link:expand.html[四種展開]されます。ブレース展開・単語分割・パス名展開は行われません。
246+
247+二重ブラケットコマンドでは test コマンドと同様に以下の原子式が使用できます:
248+
249+単項原子式::
250+ +-b+, +-c+, +-d+, +-e+, +-f+, +-G+, +-g+, +-h+, +-k+, +-L+, +-N+, +-n+,
251+ +-O+, +-o+, +-p+, +-r+, +-S+, +-s+, +-t+, +-u+, +-w+, +-x+, +-z+
252+
253+二項原子式::
254+ +-ef+, +-eq+, +-ge+, +-gt+, +-le+, +-lt+, +-ne+, +-nt+, +-ot+, +-veq+,
255+ +-vge+, +-vgt+, +-vle+, +-vlt+, +-vne+, +===+, +!==+, +=~+, +<+, +>+
256+
257+さらに、文字列を比較するための三つの二項原子式が使用できますが、test コマンドとは動作が異なります:
258++=+ および +==+ 原子式は右辺を{zwsp}link:pattern.html[パターン]として扱い、左辺がそれにマッチするかどうかを判定します。
259++!=+ 原子式は同様の判定を行い、逆の結果を返します。
260+
261+原子式の被演算子となる単語が +]]+ であるか他の演算子と紛らわしい場合は、クォートする必要があります。
262+
263+[NOTE]
264+将来新しい種類の原子式が導入される可能性があります。ハイフンで始まる単語は全てクォートすることをお勧めします。
265+
266+[NOTE]
267+`<=` および `>=` 二項原子式は二重ブラケットコマンド内においては正しく構文解析できないため使用できません。
268+
269+以下の演算子を使用して原子式を組み合わせることができます (ここでは演算子の結合順位が高い順に示します):
270+
271++( {{式}} )+::
272+ 式を括弧で囲むと演算子の優先順位を変更できます。
273+
274++! {{式}}+::
275+ 感嘆符は式の結果を反転します。
276+
277++{{expression}} && {{expression}}+::
278+ 二重アンパサンドは連言 (論理積) を表します。両辺が共に真である時、全体も真となります。左辺が先に展開・判定されます。左辺が真である場合のみ右辺が展開・判定されます。
279+
280++{{expression}} || {{expression}}+::
281+ 二重縦棒は選言 (論理和) を表します。両辺が共に偽である時、全体も偽となります。左辺が先に展開・判定されます。左辺が偽である場合のみ右辺が展開・判定されます。
282+
283+[NOTE]
284+二重ブラケットコマンドでは、test コマンドの様に +-a+ および +-o+ を連言・選言演算子として使用することはできません。
285+
286+二重ブラケットコマンドの終了ステータスは、{{式}}が真ならば 0、偽ならば 1、展開エラーやその他の理由で判定が行えない場合は 2 です。
287+
288+[NOTE]
289+二重ブラケットコマンドは bash, ksh, mksh, zsh にもありますが、POSIX にはない拡張機能です。シェルによって多少動作が異なります。移植性を高めるには二重ブラケットコマンドよりも test コマンドを使用することをお勧めします。
290+
235291 [[funcdef]]
236292 == 関数定義
237293
--- yash/trunk/doc/posix.txt (revision 3928)
+++ yash/trunk/doc/posix.txt (revision 3929)
@@ -41,6 +41,7 @@
4141 - The first pattern in a link:syntax.html#case[case command] cannot be +esac+.
4242 - The +!+ keyword cannot be followed by +(+ without any whitespaces
4343 in-between.
44+- The link:syntax.html#double-bracket[double-bracket command] cannot be used.
4445 - The +function+ keyword cannot be used for link:syntax.html#funcdef[function
4546 definition]. The function must have a portable (ASCII-only) name.
4647 - link:syntax.html#simple[Simple commands] cannot assign to
--- yash/trunk/doc/syntax.txt (revision 3928)
+++ yash/trunk/doc/syntax.txt (revision 3929)
@@ -31,7 +31,7 @@
3131 The following tokens are treated as dfn:[keywords] depending on the context in
3232 which they appear:
3333
34- ! { } case do done elif else esac fi
34+ ! { } [[ case do done elif else esac fi
3535 for function if in then until while
3636
3737 A token is treated as a keyword when:
@@ -398,6 +398,95 @@
398398 In the link:posix.html[POSIXly-correct mode], the first pattern in a case item
399399 cannot be +esac+ (even if you do not omit the +(+ token).
400400
401+[[double-bracket]]
402+=== Double-bracket command
403+
404+The dfn:[double-bracket command] is a syntactic construct that works similarly
405+to the link:_test.html[test built-in].
406+It expands and evaluates the words between the brackets.
407+
408+Double-bracket command syntax::
409+ +[[ {{expression}} ]]+
410+
411+The {{expression}} can be a single primary or combination of primaries and
412+operators.
413+The expression syntax is parsed when the command is parsed, not executed.
414+Operators (either primary or non-primary) must not be link:#quotes[quoted], or
415+it will be parsed as a normal word.
416+
417+When the command is executed, operand words are subjected to the
418+link:expand.html[four expansions], but not brace expansion, field splitting,
419+or pathname expansion.
420+
421+In the double-bracket command, the following primaries from the test built-in
422+can be used:
423+
424+Unary primaries::
425+ +-b+, +-c+, +-d+, +-e+, +-f+, +-G+, +-g+, +-h+, +-k+, +-L+, +-N+, +-n+,
426+ +-O+, +-o+, +-p+, +-r+, +-S+, +-s+, +-t+, +-u+, +-w+, +-x+, +-z+
427+
428+Binary primaries::
429+ +-ef+, +-eq+, +-ge+, +-gt+, +-le+, +-lt+, +-ne+, +-nt+, +-ot+, +-veq+,
430+ +-vge+, +-vgt+, +-vle+, +-vlt+, +-vne+, +===+, +!==+, +=~+, +<+, +>+
431+
432+Additionally, some binary primaries can be used to compare strings, which
433+works slightly differently from those for the test built-in:
434+The +=+ primary treats the right-hand-side operand word as a
435+link:pattern.html[pattern] and tests if it matches the left-hand-side operand
436+word.
437+The +==+ primary is the same as +=+.
438+The +!=+ primary is negation of the +=+ primary (reverse result).
439+
440+The operand word of a primary must be quoted if it is +]]+ or can be confused
441+with another primary operator.
442+
443+[NOTE]
444+More primaries may be added in future versions of the shell.
445+You should quote any words that start with a hyphen.
446+
447+[NOTE]
448+The `<=` and `>=` binary primaries cannot be used in the double-bracket
449+command because it cannot be parsed correctly in the shell grammar.
450+
451+The following operands (listed in the descending order of precedence) can be
452+used to combine primaries:
453+
454++( {{expression}} )+::
455+ A pair of parentheses change operator precedence.
456+
457++! {{expression}}+::
458+ An exclamation mark negates (reverses) the result.
459+
460++{{expression}} && {{expression}}+::
461+ A double ampersand represents logical conjugation (the ``and'' operation).
462+ The entire expression is true if and only if the operand expressions are
463+ both true.
464+ The left-hand-side expression is first expanded and tested.
465+ The right-hand-side is expanded only if the left-hand-side is true.
466+
467++{{expression}} || {{expression}}+::
468+ A double vertical line represents logical conjugation (the ``or''
469+ operation).
470+ The entire expression is false if and only if the operand expressions are
471+ both false.
472+ The left-hand-side expression is first expanded and tested.
473+ The right-hand-side is expanded only if the left-hand-side is false.
474+
475+[NOTE]
476+Unlike the test built-in, neither +-a+ nor +-o+ can be used as a binary
477+operator in the double-bracket command.
478+
479+The exit status of the double-bracket command is 0 if {{expression}} is true,
480+1 if false, and 2 if it cannot be evaluated because of expansion error or any
481+other reasons.
482+
483+[NOTE]
484+The double-bracket command is also supported in bash, ksh, mksh, and zsh, but
485+not defined in the POSIX standard.
486+The behavior slightly differs between the shells.
487+The test built-in should be preferred over the double-bracket command for
488+maximum portability.
489+
401490 [[funcdef]]
402491 == Function definition
403492
--- yash/trunk/exec.c (revision 3928)
+++ yash/trunk/exec.c (revision 3929)
@@ -57,6 +57,9 @@
5757 #include "variable.h"
5858 #include "xfnmatch.h"
5959 #include "yash.h"
60+#if YASH_ENABLE_DOUBLE_BRACKET
61+# include "builtins/test.h"
62+#endif
6063 #if YASH_ENABLE_LINEEDIT
6164 # include "lineedit/complete.h"
6265 # include "lineedit/lineedit.h"
@@ -516,14 +519,8 @@
516519 if (pattern == NULL)
517520 goto fail;
518521
519- xfnmatch_T *xfnm = xfnm_compile(
520- pattern, XFNM_HEADONLY | XFNM_TAILONLY);
522+ bool match = match_pattern(word, pattern);
521523 free(pattern);
522- if (xfnm == NULL)
523- continue;
524-
525- bool match = (xfnm_wmatch(xfnm, word).start != (size_t) -1);
526- xfnm_free(xfnm);
527524 if (match) {
528525 if (ci->ci_commands != NULL) {
529526 exec_and_or_lists(ci->ci_commands, finally_exit);
@@ -708,6 +705,9 @@
708705 switch (c->c_type) {
709706 case CT_SIMPLE:
710707 case CT_SUBSHELL:
708+#if YASH_ENABLE_DOUBLE_BRACKET
709+ case CT_BRACKET:
710+#endif
711711 case CT_FUNCDEF:
712712 return true;
713713 case CT_GROUP:
@@ -1189,6 +1189,13 @@
11891189 case CT_CASE:
11901190 exec_case(c, finally_exit);
11911191 break;
1192+#if YASH_ENABLE_DOUBLE_BRACKET
1193+ case CT_BRACKET:
1194+ laststatus = exec_double_bracket(c);
1195+ if (finally_exit)
1196+ exit_shell();
1197+ break;
1198+#endif /* YASH_ENABLE_DOUBLE_BRACKET */
11921199 case CT_FUNCDEF:
11931200 exec_funcdef(c, finally_exit);
11941201 break;
--- yash/trunk/parser.c (revision 3928)
+++ yash/trunk/parser.c (revision 3929)
@@ -39,6 +39,9 @@
3939 #include "plist.h"
4040 #include "strbuf.h"
4141 #include "util.h"
42+#if YASH_ENABLE_DOUBLE_BRACKET
43+# include "builtins/test.h"
44+#endif
4245 #if YASH_ENABLE_LINEEDIT
4346 # include "lineedit/lineedit.h"
4447 #endif
@@ -49,6 +52,9 @@
4952 static void pipesfree(pipeline_T *p);
5053 static void ifcmdsfree(ifcommand_T *i);
5154 static void caseitemsfree(caseitem_T *i);
55+#if YASH_ENABLE_DOUBLE_BRACKET
56+static void dbexpfree(dbexp_T *e);
57+#endif
5258 static void wordunitfree(wordunit_T *wu)
5359 __attribute__((nonnull));
5460 static void wordfree_vp(void *w);
@@ -110,6 +116,11 @@
110116 wordfree(c->c_casword);
111117 caseitemsfree(c->c_casitems);
112118 break;
119+#if YASH_ENABLE_DOUBLE_BRACKET
120+ case CT_BRACKET:
121+ dbexpfree(c->c_dbexp);
122+ break;
123+#endif /* YASH_ENABLE_DOUBLE_BRACKET */
113124 case CT_FUNCDEF:
114125 wordfree(c->c_funcname);
115126 comsfree(c->c_funcbody);
@@ -146,6 +157,31 @@
146157 }
147158 }
148159
160+#if YASH_ENABLE_DOUBLE_BRACKET
161+void dbexpfree(dbexp_T *e)
162+{
163+ if (e == NULL)
164+ return;
165+
166+ free(e->operator);
167+ switch (e->type) {
168+ case DBE_OR:
169+ case DBE_AND:
170+ case DBE_NOT:
171+ dbexpfree(e->lhs.subexp);
172+ dbexpfree(e->rhs.subexp);
173+ break;
174+ case DBE_UNARY:
175+ case DBE_BINARY:
176+ case DBE_STRING:
177+ wordfree(e->lhs.word);
178+ wordfree(e->rhs.word);
179+ break;
180+ }
181+ free(e);
182+}
183+#endif /* YASH_ENABLE_DOUBLE_BRACKET */
184+
149185 void wordunitfree(wordunit_T *wu)
150186 {
151187 switch (wu->wu_type) {
@@ -264,6 +300,9 @@
264300 TT_IF, TT_THEN, TT_ELSE, TT_ELIF, TT_FI, TT_DO, TT_DONE, TT_CASE, TT_ESAC,
265301 TT_WHILE, TT_UNTIL, TT_FOR, TT_LBRACE, TT_RBRACE, TT_BANG, TT_IN,
266302 TT_FUNCTION,
303+#if YASH_ENABLE_DOUBLE_BRACKET
304+ TT_DOUBLE_LBRACKET,
305+#endif
267306 } tokentype_T;
268307
269308 static wchar_t *skip_name(const wchar_t *s, bool predicate(wchar_t))
@@ -353,9 +392,9 @@
353392 {
354393 /* List of keywords:
355394 * case do done elif else esac fi for function if in then until while
356- * { } !
395+ * { } [[ !
357396 * The following words are currently not keywords:
358- * select [[ ]] */
397+ * select ]] */
359398 switch (s[0]) {
360399 case L'c':
361400 if (s[1] == L'a' && s[2] == L's' && s[3] == L'e' && s[4]== L'\0')
@@ -417,6 +456,12 @@
417456 if (s[1] == L'\0')
418457 return TT_RBRACE;
419458 break;
459+#if YASH_ENABLE_DOUBLE_BRACKET
460+ case L'[':
461+ if (s[1] == L'[' && s[2] == L'\0')
462+ return TT_DOUBLE_LBRACKET;
463+ break;
464+#endif /* YASH_ENABLE_DOUBLE_BRACKET */
420465 case L'!':
421466 if (s[1] == L'\0')
422467 return TT_BANG;
@@ -519,6 +564,8 @@
519564
520565 static void serror(parsestate_T *restrict ps, const char *restrict format, ...)
521566 __attribute__((nonnull(1,2),format(printf,2,3)));
567+static void print_errmsg_token(parsestate_T *ps, const char *message)
568+ __attribute__((nonnull));
522569 static const char *get_errmsg_unexpected_tokentype(tokentype_T tokentype)
523570 __attribute__((const));
524571 static void print_errmsg_token_unexpected(parsestate_T *ps)
@@ -620,6 +667,20 @@
620667 __attribute__((nonnull,malloc,warn_unused_result));
621668 static void **parse_case_patterns(parsestate_T *ps)
622669 __attribute__((nonnull,malloc,warn_unused_result));
670+#if YASH_ENABLE_DOUBLE_BRACKET
671+static command_T *parse_double_bracket(parsestate_T *ps)
672+ __attribute__((nonnull,malloc,warn_unused_result));
673+static dbexp_T *parse_double_bracket_ors(parsestate_T *ps)
674+ __attribute__((nonnull,malloc,warn_unused_result));
675+static dbexp_T *parse_double_bracket_ands(parsestate_T *ps)
676+ __attribute__((nonnull,malloc,warn_unused_result));
677+static dbexp_T *parse_double_bracket_nots(parsestate_T *ps)
678+ __attribute__((nonnull,malloc,warn_unused_result));
679+static dbexp_T *parse_double_bracket_primary(parsestate_T *ps)
680+ __attribute__((nonnull,malloc,warn_unused_result));
681+static wordunit_T *parse_double_bracket_operand(parsestate_T *ps)
682+ __attribute__((nonnull,malloc,warn_unused_result));
683+#endif /* YASH_ENABLE_DOUBLE_BRACKET */
623684 static command_T *parse_function(parsestate_T *ps)
624685 __attribute__((nonnull,malloc,warn_unused_result));
625686 static command_T *try_reparse_as_function(parsestate_T *ps, command_T *c)
@@ -797,6 +858,16 @@
797858 ps->error = true;
798859 }
799860
861+void print_errmsg_token(parsestate_T *ps, const char *message)
862+{
863+ assert(ps->index <= ps->next_index);
864+ size_t length = ps->next_index - ps->index;
865+ wchar_t token[length + 1];
866+ wcsncpy(token, &ps->src.contents[ps->index], length);
867+ token[length] = L'\0';
868+ serror(ps, message, token);
869+}
870+
800871 const char *get_errmsg_unexpected_tokentype(tokentype_T tokentype)
801872 {
802873 switch (tokentype) {
@@ -833,14 +904,7 @@
833904
834905 void print_errmsg_token_unexpected(parsestate_T *ps)
835906 {
836- assert(ps->index <= ps->next_index);
837- size_t length = ps->next_index - ps->index;
838- wchar_t token[length + 1];
839- wcsncpy(token, &ps->src.contents[ps->index], length);
840- token[length] = L'\0';
841-
842- const char *message = get_errmsg_unexpected_tokentype(ps->tokentype);
843- serror(ps, message, token);
907+ print_errmsg_token(ps, get_errmsg_unexpected_tokentype(ps->tokentype));
844908 }
845909
846910 void print_errmsg_token_missing(parsestate_T *ps, const wchar_t *t)
@@ -2304,6 +2368,11 @@
23042368 case TT_CASE:
23052369 result = parse_case(ps);
23062370 break;
2371+#if YASH_ENABLE_DOUBLE_BRACKET
2372+ case TT_DOUBLE_LBRACKET:
2373+ result = parse_double_bracket(ps);
2374+ break;
2375+#endif
23072376 default:
23082377 return NULL;
23092378 }
@@ -2658,6 +2727,195 @@
26582727 return pl_toary(&wordlist);
26592728 }
26602729
2730+#if YASH_ENABLE_DOUBLE_BRACKET
2731+
2732+/* Parses a double-bracket command.
2733+ * The current token must be the starting "[[".
2734+ * Never returns NULL. The resultant `c_dbexp' may contain NULL sub-expression
2735+ * in case of syntax error. */
2736+command_T *parse_double_bracket(parsestate_T *ps)
2737+{
2738+ if (posixly_correct)
2739+ serror(ps, Ngt("The [[ ... ]] syntax is not supported "
2740+ "in the POSIXly-correct mode"));
2741+
2742+ assert(ps->tokentype == TT_DOUBLE_LBRACKET);
2743+ next_token(ps);
2744+ psubstitute_alias_recursive(ps, 0);
2745+
2746+ command_T *result = xmalloc(sizeof *result);
2747+ result->next = NULL;
2748+ result->refcount = 1;
2749+ result->c_type = CT_BRACKET;
2750+ result->c_lineno = ps->info->lineno;
2751+ result->c_redirs = NULL;
2752+ result->c_dbexp = parse_double_bracket_ors(ps);
2753+
2754+ if (is_single_string_word(ps->token) &&
2755+ wcscmp(ps->token->wu_string, L"]]") == 0) {
2756+ next_token(ps);
2757+ psubstitute_alias_recursive(ps, 0);
2758+ } else if (!ps->error) {
2759+ if (ps->tokentype == TT_NEWLINE)
2760+ serror(ps, Ngt("`%ls' is missing"), L"]]");
2761+ else
2762+ print_errmsg_token(ps,
2763+ Ngt("invalid word `%ls' between `[[' and `]]'"));
2764+ }
2765+
2766+ return result;
2767+}
2768+
2769+/* Parses one or more "and" expressions separated by "||"s in the double-bracket
2770+ * command. May return NULL on error. */
2771+dbexp_T *parse_double_bracket_ors(parsestate_T *ps)
2772+{
2773+ dbexp_T *lhs = parse_double_bracket_ands(ps);
2774+ if (lhs == NULL || ps->tokentype != TT_PIPEPIPE)
2775+ return lhs;
2776+ next_token(ps);
2777+ psubstitute_alias_recursive(ps, 0);
2778+
2779+ dbexp_T *result = xmalloc(sizeof *result);
2780+ result->type = DBE_OR;
2781+ result->operator = NULL;
2782+ result->lhs.subexp = lhs;
2783+ result->rhs.subexp = parse_double_bracket_ors(ps);
2784+ return result;
2785+}
2786+
2787+/* Parses one or more "!" expressions separated by "&&"s in the double-bracket
2788+ * command. May return NULL on error. */
2789+dbexp_T *parse_double_bracket_ands(parsestate_T *ps)
2790+{
2791+ dbexp_T *lhs = parse_double_bracket_nots(ps);
2792+ if (lhs == NULL || ps->tokentype != TT_AMPAMP)
2793+ return lhs;
2794+ next_token(ps);
2795+ psubstitute_alias_recursive(ps, 0);
2796+
2797+ dbexp_T *result = xmalloc(sizeof *result);
2798+ result->type = DBE_AND;
2799+ result->operator = NULL;
2800+ result->lhs.subexp = lhs;
2801+ result->rhs.subexp = parse_double_bracket_ands(ps);
2802+ return result;
2803+}
2804+
2805+/* Parses a primary expression optionally prefixed by any number of "!"s in the
2806+ * double-bracket command. May return NULL on error. */
2807+dbexp_T *parse_double_bracket_nots(parsestate_T *ps)
2808+{
2809+ if (ps->tokentype != TT_BANG)
2810+ return parse_double_bracket_primary(ps);
2811+ next_token(ps);
2812+ psubstitute_alias_recursive(ps, 0);
2813+
2814+ dbexp_T *result = xmalloc(sizeof *result);
2815+ result->type = DBE_NOT;
2816+ result->operator = NULL;
2817+ result->lhs.subexp = NULL;
2818+ result->rhs.subexp = parse_double_bracket_nots(ps);
2819+ return result;
2820+}
2821+
2822+/* Parses a primary expression in the double-bracket command. May return NULL on
2823+ * error. The "(...)" operator is considered as a primary in this function. */
2824+dbexp_T *parse_double_bracket_primary(parsestate_T *ps)
2825+{
2826+ if (ps->tokentype == TT_LPAREN) {
2827+ /* parse "(...)" */
2828+ next_token(ps);
2829+ psubstitute_alias_recursive(ps, 0);
2830+
2831+ dbexp_T *subexp = parse_double_bracket_ors(ps);
2832+ if (ps->tokentype == TT_RPAREN) {
2833+ next_token(ps);
2834+ psubstitute_alias_recursive(ps, 0);
2835+ } else if (!ps->error) {
2836+ if (ps->tokentype == TT_NEWLINE ||
2837+ (is_single_string_word(ps->token) &&
2838+ wcscmp(ps->token->wu_string, L"]]") == 0))
2839+ serror(ps, Ngt("`%ls' is missing"), L")");
2840+ else
2841+ print_errmsg_token(ps,
2842+ Ngt("invalid word `%ls' between `[[' and `]]'"));
2843+ }
2844+ return subexp;
2845+ }
2846+
2847+ dbexptype_T type;
2848+ wchar_t *op;
2849+ wordunit_T *lhs, *rhs;
2850+
2851+ if (is_single_string_word(ps->token) &&
2852+ is_unary_primary(ps->token->wu_string)) {
2853+ type = DBE_UNARY;
2854+ lhs = NULL;
2855+ goto parse_primary_operator;
2856+ }
2857+
2858+ lhs = parse_double_bracket_operand(ps);
2859+ if (lhs == NULL)
2860+ return NULL;
2861+
2862+ if (ps->tokentype == TT_LESS || ps->tokentype == TT_GREATER) {
2863+ type = DBE_BINARY;
2864+ op = xwcsndup(&ps->src.contents[ps->index], ps->next_index - ps->index);
2865+ } else if (is_single_string_word(ps->token) &&
2866+ is_binary_primary(ps->token->wu_string)) {
2867+ type = DBE_BINARY;
2868+parse_primary_operator:
2869+ op = ps->token->wu_string, ps->token->wu_string = NULL;
2870+ } else {
2871+ type = DBE_STRING;
2872+ op = NULL;
2873+ rhs = lhs, lhs = NULL;
2874+ goto return_result;
2875+ }
2876+ next_token(ps);
2877+ psubstitute_alias_recursive(ps, 0);
2878+
2879+ rhs = parse_double_bracket_operand(ps);
2880+
2881+return_result:;
2882+ dbexp_T *result = xmalloc(sizeof *result);
2883+ result->type = type;
2884+ result->operator = op;
2885+ result->lhs.word = lhs;
2886+ result->rhs.word = rhs;
2887+ return result;
2888+}
2889+
2890+/* Parses a operand token of a primary conditional expression in the double-
2891+ * bracket command. Returns NULL on error. */
2892+wordunit_T *parse_double_bracket_operand(parsestate_T *ps)
2893+{
2894+ if (is_single_string_word(ps->token) &&
2895+ wcscmp(ps->token->wu_string, L"]]") == 0) {
2896+ serror(ps, Ngt("conditional expression "
2897+ "is missing or incomplete between `[[' and `]]'"));
2898+ return NULL;
2899+ }
2900+ if (ps->token == NULL) {
2901+ if (ps->tokentype == TT_NEWLINE)
2902+ serror(ps, Ngt("unexpected linebreak "
2903+ "in the middle of the [[ ... ]] command"));
2904+ else
2905+ print_errmsg_token(ps, Ngt("`%ls' is not a valid operand "
2906+ "in the conditional expression"));
2907+ return NULL;
2908+ }
2909+
2910+ wordunit_T *result = ps->token;
2911+ ps->token = NULL;
2912+ next_token(ps);
2913+ psubstitute_alias_recursive(ps, 0);
2914+ return result;
2915+}
2916+
2917+#endif /* YASH_ENABLE_DOUBLE_BRACKET */
2918+
26612919 /* Parses a function definition that starts with the "function" keyword.
26622920 * The current token must be "function". Never returns NULL. */
26632921 command_T *parse_function(parsestate_T *ps)
@@ -3011,6 +3269,15 @@
30113269 struct print *restrict pr, const caseitem_T *restrict caseitems,
30123270 unsigned indent)
30133271 __attribute__((nonnull(1)));
3272+#if YASH_ENABLE_DOUBLE_BRACKET
3273+static void print_double_bracket(
3274+ struct print *restrict pr, const command_T *restrict c, unsigned indent)
3275+ __attribute__((nonnull));
3276+static void print_double_bracket_expression(
3277+ struct print *restrict pr, const dbexp_T *restrict e,
3278+ dbexptype_T context, unsigned indent)
3279+ __attribute__((nonnull));
3280+#endif
30143281 static void print_function_definition(
30153282 struct print *restrict pr, const command_T *restrict command,
30163283 unsigned indent)
@@ -3156,6 +3423,11 @@
31563423 case CT_CASE:
31573424 print_case(pr, c, indent);
31583425 break;
3426+#if YASH_ENABLE_DOUBLE_BRACKET
3427+ case CT_BRACKET:
3428+ print_double_bracket(pr, c, indent);
3429+ break;
3430+#endif /* YASH_ENABLE_DOUBLE_BRACKET */
31593431 case CT_FUNCDEF:
31603432 print_function_definition(pr, c, indent);
31613433 assert(c->c_redirs == NULL);
@@ -3325,6 +3597,64 @@
33253597 }
33263598 }
33273599
3600+#if YASH_ENABLE_DOUBLE_BRACKET
3601+
3602+void print_double_bracket(
3603+ struct print *restrict pr, const command_T *restrict c, unsigned indent)
3604+{
3605+ assert(c->c_type == CT_BRACKET);
3606+
3607+ wb_cat(&pr->buffer, L"[[ ");
3608+ print_double_bracket_expression(pr, c->c_dbexp, DBE_OR, indent);
3609+ wb_cat(&pr->buffer, L"]] ");
3610+}
3611+
3612+void print_double_bracket_expression(
3613+ struct print *restrict pr, const dbexp_T *restrict e,
3614+ dbexptype_T context, unsigned indent)
3615+{
3616+ assert(context == DBE_OR || context == DBE_AND || context == DBE_NOT);
3617+
3618+ switch (e->type) {
3619+ case DBE_OR:
3620+ if (context != DBE_OR)
3621+ wb_cat(&pr->buffer, L"( ");
3622+ print_double_bracket_expression(pr, e->lhs.subexp, DBE_OR, indent);
3623+ wb_cat(&pr->buffer, L"|| ");
3624+ print_double_bracket_expression(pr, e->rhs.subexp, DBE_OR, indent);
3625+ if (context != DBE_OR)
3626+ wb_cat(&pr->buffer, L") ");
3627+ break;
3628+ case DBE_AND:
3629+ if (context == DBE_NOT)
3630+ wb_cat(&pr->buffer, L"( ");
3631+ print_double_bracket_expression(pr, e->lhs.subexp, DBE_AND, indent);
3632+ wb_cat(&pr->buffer, L"&& ");
3633+ print_double_bracket_expression(pr, e->rhs.subexp, DBE_AND, indent);
3634+ if (context == DBE_NOT)
3635+ wb_cat(&pr->buffer, L") ");
3636+ break;
3637+ case DBE_NOT:
3638+ wb_cat(&pr->buffer, L"! ");
3639+ print_double_bracket_expression(pr, e->rhs.subexp, DBE_NOT, indent);
3640+ break;
3641+ case DBE_BINARY:
3642+ print_word(pr, e->lhs.word, indent);
3643+ wb_wccat(&pr->buffer, L' ');
3644+ /* falls thru! */
3645+ case DBE_UNARY:
3646+ wb_cat(&pr->buffer, e->operator);
3647+ wb_wccat(&pr->buffer, L' ');
3648+ /* falls thru! */
3649+ case DBE_STRING:
3650+ print_word(pr, e->rhs.word, indent);
3651+ wb_wccat(&pr->buffer, L' ');
3652+ break;
3653+ }
3654+}
3655+
3656+#endif
3657+
33283658 void print_function_definition(
33293659 struct print *restrict pr, const command_T *restrict c, unsigned indent)
33303660 {
--- yash/trunk/parser.h (revision 3928)
+++ yash/trunk/parser.h (revision 3929)
@@ -1,6 +1,6 @@
11 /* Yash: yet another shell */
22 /* parser.h: syntax parser */
3-/* (C) 2007-2015 magicant */
3+/* (C) 2007-2018 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
@@ -62,6 +62,9 @@
6262 CT_FOR, /* for command */
6363 CT_WHILE, /* while/until command */
6464 CT_CASE, /* case command */
65+#if YASH_ENABLE_DOUBLE_BRACKET
66+ CT_BRACKET, /* double-bracket command */
67+#endif
6568 CT_FUNCDEF, /* function definition */
6669 } commandtype_T;
6770
@@ -93,6 +96,7 @@
9396 struct wordunit_T *casword; /* word compared to case patterns */
9497 struct caseitem_T *casitems; /* pairs of patterns and commands */
9598 } casecommand;
99+ struct dbexp_T *dbexp; /* double-bracket command expression */
96100 struct {
97101 struct wordunit_T *funcname; /* name of function */
98102 struct command_T *funcbody; /* body of function */
@@ -111,6 +115,7 @@
111115 #define c_whlcmds c_content.whileloop.whlcmds
112116 #define c_casword c_content.casecommand.casword
113117 #define c_casitems c_content.casecommand.casitems
118+#define c_dbexp c_content.dbexp
114119 #define c_funcname c_content.funcdef.funcname
115120 #define c_funcbody c_content.funcdef.funcbody
116121 /* `c_words' and `c_forwords' are NULL-terminated arrays of pointers to
@@ -135,6 +140,31 @@
135140 /* `ci_patterns' is a NULL-terminated array of pointers to `wordunit_T' that are
136141 * cast to `void *'. */
137142
143+/* type of dbexp_T */
144+typedef enum {
145+ DBE_OR, /* the "||" operator, two operand expressions */
146+ DBE_AND, /* the "&&" operator, two operand expressions */
147+ DBE_NOT, /* the "!" operator, one operand expression */
148+ DBE_UNARY, /* -f, -n, etc., one operand word */
149+ DBE_BINARY, /* -eq, =, etc., two operand words */
150+ DBE_STRING, /* single string primary, one operand word */
151+} dbexptype_T;
152+
153+/* operand of expression in double-bracket command */
154+typedef union dboperand_T {
155+ struct dbexp_T *subexp;
156+ struct wordunit_T *word;
157+} dboperand_T;
158+
159+/* expression in double-bracket command */
160+typedef struct dbexp_T {
161+ dbexptype_T type;
162+ wchar_t *operator;
163+ dboperand_T lhs, rhs;
164+} dbexp_T;
165+/* `operator' is NULL for non-primary expressions */
166+/* `lhs' is NULL for one-operand expressions */
167+
138168 /* embedded command */
139169 typedef struct embedcmd_T {
140170 _Bool is_preparsed;
--- yash/trunk/po/ja.po (revision 3928)
+++ yash/trunk/po/ja.po (revision 3929)
@@ -8,8 +8,8 @@
88 msgstr ""
99 "Project-Id-Version: yash 2.48\n"
1010 "Report-Msgid-Bugs-To: http://osdn.jp/projects/yash/forums/\n"
11-"POT-Creation-Date: 2018-10-06 15:04+0900\n"
12-"PO-Revision-Date: 2018-10-06 15:19+0900\n"
11+"POT-Creation-Date: 2018-11-21 00:15+0900\n"
12+"PO-Revision-Date: 2018-11-21 00:20+0900\n"
1313 "Last-Translator: WATANABE Yuki <magicant@users.osdn.me>\n"
1414 "Language-Team: Japanese\n"
1515 "Language: ja\n"
@@ -217,9 +217,9 @@
217217 msgid "`%ls' is not a valid number"
218218 msgstr "「%ls」は有効な数値ではありません"
219219
220-#: builtins/printf.c:693 builtins/test.c:556 builtins/test.c:562
221-#: builtins/ulimit.c:229 exec.c:1725 exec.c:1798 history.c:1714 sig.c:1395
222-#: variable.c:2102 variable.c:2162 variable.c:2201 variable.c:2385 yash.c:633
220+#: builtins/printf.c:693 builtins/test.c:573 builtins/test.c:579
221+#: builtins/ulimit.c:229 exec.c:1732 exec.c:1805 history.c:1714 sig.c:1395
222+#: variable.c:2105 variable.c:2165 variable.c:2204 variable.c:2388 yash.c:633
223223 #, c-format
224224 msgid "`%ls' is not a valid integer"
225225 msgstr "「%ls」は有効な整数ではありません"
@@ -232,44 +232,44 @@
232232 msgid "\tprintf format [value...]\n"
233233 msgstr "\tprintf 書式 [値...]\n"
234234
235-#: builtins/test.c:82 builtins/test.c:425 parser.c:852 parser.c:1349
236-#: parser.c:1437 parser.c:1462 parser.c:1594 parser.c:1633 parser.c:2142
237-#: parser.c:2482 parser.c:2570 parser.c:2650
235+#: builtins/test.c:99 builtins/test.c:442 parser.c:916 parser.c:1413
236+#: parser.c:1501 parser.c:1526 parser.c:1658 parser.c:1697 parser.c:2206
237+#: parser.c:2551 parser.c:2639 parser.c:2719 parser.c:2760 parser.c:2839
238238 #, c-format
239239 msgid "`%ls' is missing"
240240 msgstr "「%ls」が抜けています"
241241
242-#: builtins/test.c:113
242+#: builtins/test.c:130
243243 #, c-format
244244 msgid "`%ls' is not a valid operator"
245245 msgstr "「%ls」は有効な演算子ではありません"
246246
247-#: builtins/test.c:138
247+#: builtins/test.c:155
248248 #, c-format
249249 msgid "`%ls' is not a unary operator"
250250 msgstr "「%ls」は単項演算子ではありません"
251251
252-#: builtins/test.c:159 builtins/test.c:627 builtins/test.c:635 exec.c:1934
253-#: exec.c:2078 exec.c:2087 history.c:1492 lineedit/keymap.c:476 path.c:1106
252+#: builtins/test.c:176 builtins/test.c:644 builtins/test.c:652 exec.c:1941
253+#: exec.c:2085 exec.c:2094 history.c:1492 lineedit/keymap.c:476 path.c:1106
254254 #: path.c:1173 strbuf.c:573 strbuf.c:596
255255 msgid "unexpected error"
256256 msgstr "想定外のエラーです"
257257
258-#: builtins/test.c:364
258+#: builtins/test.c:381
259259 #, c-format
260260 msgid "`%ls' is not a binary operator"
261261 msgstr "「%ls」は二項演算子ではありません"
262262
263-#: builtins/test.c:416
263+#: builtins/test.c:433
264264 #, c-format
265265 msgid "an expression is missing after `%ls'"
266266 msgstr "「%ls」の後で式が抜けています"
267267
268-#: builtins/test.c:682
268+#: builtins/test.c:699
269269 msgid "evaluate a conditional expression"
270270 msgstr "条件式を評価する"
271271
272-#: builtins/test.c:685
272+#: builtins/test.c:702
273273 msgid ""
274274 "\ttest expression\n"
275275 "\t[ expression ]\n"
@@ -379,64 +379,64 @@
379379 msgid "cannot make a child process"
380380 msgstr "子プロセスを生成できません"
381381
382-#: exec.c:1208 exec.c:2102 exec.c:2329
382+#: exec.c:1215 exec.c:2109 exec.c:2336
383383 #, c-format
384384 msgid "no such command `%s'"
385385 msgstr "「%s」というようなコマンドはありません"
386386
387-#: exec.c:1262
387+#: exec.c:1269
388388 #, c-format
389389 msgid "cannot execute command `%s'"
390390 msgstr "コマンド「%s」を実行できません"
391391
392-#: exec.c:1263
392+#: exec.c:1270
393393 #, c-format
394394 msgid "cannot execute command `%s' (%s)"
395395 msgstr "コマンド「%s」(%s) を実行できません"
396396
397-#: exec.c:1366
397+#: exec.c:1373
398398 #, c-format
399399 msgid "cannot invoke a new shell to execute script `%s'"
400400 msgstr "スクリプト「%s」を実行するための新しいシェルを起動できません"
401401
402-#: exec.c:1425 exec.c:1447
402+#: exec.c:1432 exec.c:1454
403403 msgid "cannot open a pipe for the command substitution"
404404 msgstr "コマンド置換のためのパイプを開けません"
405405
406-#: exec.c:1484
406+#: exec.c:1491
407407 msgid "command substitution"
408408 msgstr "コマンド置換"
409409
410-#: exec.c:1735
410+#: exec.c:1742
411411 msgid "cannot be used in the interactive mode"
412412 msgstr "対話モードでは使えません"
413413
414-#: exec.c:1745
414+#: exec.c:1752
415415 msgid "return from a function or script"
416416 msgstr "関数やスクリプトから抜ける"
417417
418-#: exec.c:1748
418+#: exec.c:1755
419419 msgid "\treturn [-n] [exit_status]\n"
420420 msgstr "\treturn [-n] [終了ステータス]\n"
421421
422-#: exec.c:1780
422+#: exec.c:1787
423423 msgid "not in an iteration"
424424 msgstr "反復実行の途中ではありません"
425425
426-#: exec.c:1801
426+#: exec.c:1808
427427 #, c-format
428428 msgid "%u is not a positive integer"
429429 msgstr "%u は正の整数ではありません"
430430
431-#: exec.c:1811
431+#: exec.c:1818
432432 msgid "not in a loop"
433433 msgstr "ループの途中ではありません"
434434
435-#: exec.c:1830
435+#: exec.c:1837
436436 msgid "exit a loop"
437437 msgstr "ループを抜ける"
438438
439-#: exec.c:1833
439+#: exec.c:1840
440440 msgid ""
441441 "\tbreak [count]\n"
442442 "\tbreak -i\n"
@@ -444,11 +444,11 @@
444444 "\tbreak [深さ]\n"
445445 "\tbreak -i\n"
446446
447-#: exec.c:1838
447+#: exec.c:1845
448448 msgid "continue a loop"
449449 msgstr "ループの先頭に戻る"
450450
451-#: exec.c:1841
451+#: exec.c:1848
452452 msgid ""
453453 "\tcontinue [count]\n"
454454 "\tcontinue -i\n"
@@ -456,104 +456,104 @@
456456 "\tcontinue [深さ]\n"
457457 "\tcontinue -i\n"
458458
459-#: exec.c:1881
459+#: exec.c:1888
460460 msgid "evaluate arguments as a command"
461461 msgstr "引数をコマンドとして実行する"
462462
463-#: exec.c:1884
463+#: exec.c:1891
464464 msgid "\teval [-i] [argument...]\n"
465465 msgstr "\teval [-i] [引数...]\n"
466466
467-#: exec.c:1943
467+#: exec.c:1950
468468 #, c-format
469469 msgid "file `%s' was not found in $YASH_LOADPATH"
470470 msgstr "ファイル「%s」は $YASH_LOADPATH 内に見つかりませんでした"
471471
472-#: exec.c:1953
472+#: exec.c:1960
473473 #, c-format
474474 msgid "file `%s' was not found in $PATH"
475475 msgstr "ファイル「%s」は $PATH 内に見つかりませんでした"
476476
477-#: exec.c:1965 redir.c:279 yash.c:206
477+#: exec.c:1972 redir.c:279 yash.c:206
478478 #, c-format
479479 msgid "cannot open file `%s'"
480480 msgstr "ファイル「%s」を開けません"
481481
482-#: exec.c:2004
482+#: exec.c:2011
483483 msgid "read a file and execute commands"
484484 msgstr "ファイルをスクリプトとして実行する"
485485
486-#: exec.c:2007
486+#: exec.c:2014
487487 msgid "\t. [-AL] file [argument...]\n"
488488 msgstr "\t. [-AL] ファイル [引数...]\n"
489489
490-#: exec.c:2055 yash.c:620
490+#: exec.c:2062 yash.c:620
491491 #, c-format
492492 msgid "You have a stopped job!"
493493 msgid_plural "You have %zu stopped jobs!"
494494 msgstr[0] "停止中のジョブが %zu 個あります!"
495495
496-#: exec.c:2059
496+#: exec.c:2066
497497 msgid " Use the -f option to exec anyway.\n"
498498 msgstr " それでも exec するには -f オプションを付けてください。\n"
499499
500-#: exec.c:2149
500+#: exec.c:2156
501501 msgid "replace the shell process with an external command"
502502 msgstr "シェルのプロセスを外部コマンドに変える"
503503
504-#: exec.c:2152
504+#: exec.c:2159
505505 msgid "\texec [-cf] [-a name] [command [argument...]]\n"
506506 msgstr "\texec [-cf] [-a 名前] [コマンド [引数...]]\n"
507507
508-#: exec.c:2212
508+#: exec.c:2219
509509 msgid "the -a or -k option must be used with the -v option"
510510 msgstr "-a および -k オプションは -v オプションと一緒にしか使えません"
511511
512-#: exec.c:2306
512+#: exec.c:2313
513513 #, c-format
514514 msgid "%ls: a shell keyword\n"
515515 msgstr "%ls: シェルの予約語\n"
516516
517-#: exec.c:2335
517+#: exec.c:2342
518518 #, c-format
519519 msgid "%s: a special built-in\n"
520520 msgstr "%s: 特殊組込みコマンド\n"
521521
522-#: exec.c:2339
522+#: exec.c:2346
523523 #, c-format
524524 msgid "%s: a semi-special built-in\n"
525525 msgstr "%s: 準特殊組込みコマンド\n"
526526
527-#: exec.c:2351
527+#: exec.c:2358
528528 #, c-format
529529 msgid "%s: a regular built-in (not found in $PATH)\n"
530530 msgstr "%s: 通常の組込みコマンド ($PATH 内に存在せず)\n"
531531
532-#: exec.c:2352
532+#: exec.c:2359
533533 #, c-format
534534 msgid "%s: a regular built-in at %s\n"
535535 msgstr "%s: 通常の組込みコマンド (%s)\n"
536536
537-#: exec.c:2359
537+#: exec.c:2366
538538 #, c-format
539539 msgid "%s: a function\n"
540540 msgstr "%s: 関数\n"
541541
542-#: exec.c:2375
542+#: exec.c:2382
543543 #, c-format
544544 msgid "%s: an external command at %s\n"
545545 msgstr "%s: 外部コマンド (%s)\n"
546546
547-#: exec.c:2397
547+#: exec.c:2404
548548 #, c-format
549549 msgid "%s: an external command at %s/%s\n"
550550 msgstr "%s: 外部コマンド (%s/%s)\n"
551551
552-#: exec.c:2406
552+#: exec.c:2413
553553 msgid "execute or identify a command"
554554 msgstr "コマンドを実行または特定する"
555555
556-#: exec.c:2409
556+#: exec.c:2416
557557 msgid ""
558558 "\tcommand [-befp] command [argument...]\n"
559559 "\tcommand -v|-V [-abefkp] command...\n"
@@ -561,23 +561,23 @@
561561 "\tcommand [-befp] コマンド [引数...]\n"
562562 "\tcommand -v|-V [-abefkp] コマンド...\n"
563563
564-#: exec.c:2414
564+#: exec.c:2421
565565 msgid "identify a command"
566566 msgstr "コマンドを特定する"
567567
568-#: exec.c:2417
568+#: exec.c:2424
569569 msgid "\ttype command...\n"
570570 msgstr "\ttype コマンド...\n"
571571
572-#: exec.c:2455
572+#: exec.c:2462
573573 msgid "cannot get the time data"
574574 msgstr "時間情報を取得できません"
575575
576-#: exec.c:2472
576+#: exec.c:2479
577577 msgid "print CPU time usage"
578578 msgstr "消費 CPU 時間を表示する"
579579
580-#: exec.c:2475
580+#: exec.c:2482
581581 msgid "\ttimes\n"
582582 msgstr "\ttimes\n"
583583
@@ -985,204 +985,226 @@
985985 "\tset [オプション...] [--] [新しい位置パラメータ...]\n"
986986 "\tset -o|+o # 現在の設定を表示する\n"
987987
988-#: parser.c:790
988+#: parser.c:851
989989 msgid "syntax error: "
990990 msgstr "構文エラー: "
991991
992-#: parser.c:804
992+#: parser.c:875
993993 #, c-format
994994 msgid "encountered `%ls' without a matching `('"
995995 msgstr "「%ls」に対応する「(」がありません"
996996
997-#: parser.c:806
997+#: parser.c:877
998998 #, c-format
999999 msgid "encountered `%ls' without a matching `{'"
10001000 msgstr "「%ls」に対応する「{」がありません"
10011001
1002-#: parser.c:808
1002+#: parser.c:879
10031003 #, c-format
10041004 msgid "`%ls' is used outside `case'"
10051005 msgstr "「%ls」が case コマンドの外で使われています"
10061006
1007-#: parser.c:810 parser.c:812 parser.c:2666
1007+#: parser.c:881 parser.c:883 parser.c:2924
10081008 #, c-format
10091009 msgid "`%ls' cannot be used as a command name"
10101010 msgstr "「%ls」はコマンド名として使えません"
10111011
1012-#: parser.c:814 parser.c:827
1012+#: parser.c:885 parser.c:898
10131013 #, c-format
10141014 msgid "encountered `%ls' without a matching `if' and/or `then'"
10151015 msgstr "「%ls」に対応する「if」または「then」がありません"
10161016
1017-#: parser.c:817
1017+#: parser.c:888
10181018 #, c-format
10191019 msgid "encountered `%ls' without a matching `if' or `elif'"
10201020 msgstr "「%ls」に対応する「if」または「elif」がありません"
10211021
1022-#: parser.c:819
1022+#: parser.c:890
10231023 #, c-format
10241024 msgid "encountered `%ls' without a matching `for', `while', or `until'"
10251025 msgstr "「%ls」に対応する「for」「while」または「until」がありません"
10261026
1027-#: parser.c:822
1027+#: parser.c:893
10281028 #, c-format
10291029 msgid "encountered `%ls' without a matching `do'"
10301030 msgstr "「%ls」に対応する「do」がありません"
10311031
1032-#: parser.c:824
1032+#: parser.c:895
10331033 #, c-format
10341034 msgid "encountered `%ls' without a matching `case'"
10351035 msgstr "「%ls」に対応する「case」がありません"
10361036
1037-#: parser.c:850 parser.c:2324
1037+#: parser.c:914 parser.c:2393
10381038 #, c-format
10391039 msgid "(maybe you missed `%ls'?)"
10401040 msgstr "(「%ls」を忘れていませんか?)"
10411041
1042-#: parser.c:1153
1042+#: parser.c:1217
10431043 msgid "the double quotation is not closed"
10441044 msgstr "二重引用符が閉じられていません"
10451045
1046-#: parser.c:1170
1046+#: parser.c:1234
10471047 msgid "the single quotation is not closed"
10481048 msgstr "単一引用符が閉じられていません"
10491049
1050-#: parser.c:1327
1050+#: parser.c:1391
10511051 msgid "the parameter name is missing or invalid"
10521052 msgstr "パラメータ名が抜けているか不正です"
10531053
1054-#: parser.c:1339 parser.c:1344
1054+#: parser.c:1403 parser.c:1408
10551055 msgid "the index is missing"
10561056 msgstr "インデックスが抜けています"
10571057
1058-#: parser.c:1371 parser.c:1384
1058+#: parser.c:1435 parser.c:1448
10591059 #, c-format
10601060 msgid "invalid character `%lc' in parameter expansion"
10611061 msgstr "パラメータ展開に無効な文字「%lc」が混じっています"
10621062
1063-#: parser.c:1380 parser.c:1395 parser.c:1439
1063+#: parser.c:1444 parser.c:1459 parser.c:1503
10641064 #, c-format
10651065 msgid "invalid use of `%lc' in parameter expansion"
10661066 msgstr "パラメータ展開において「%lc」の使い方が正しくありません"
10671067
1068-#: parser.c:1538
1068+#: parser.c:1602
10691069 msgid "the backquoted command substitution is not closed"
10701070 msgstr "「`」によるコマンド置換が閉じられていません"
10711071
1072-#: parser.c:1789
1072+#: parser.c:1853
10731073 msgid "`;' or `&' is missing"
10741074 msgstr "「;」または「&」が抜けています"
10751075
1076-#: parser.c:1898
1076+#: parser.c:1962
10771077 msgid "ksh-like extended glob pattern `!(...)' is not supported"
10781078 msgstr "ksh の拡張パターン「!(...)」は利用できません"
10791079
1080-#: parser.c:1999
1080+#: parser.c:2063
10811081 msgid "a command is missing at the end of input"
10821082 msgstr "入力の最後でコマンドが抜けています"
10831083
1084-#: parser.c:2001
1084+#: parser.c:2065
10851085 #, c-format
10861086 msgid "a command is missing before `%lc'"
10871087 msgstr "「%lc」の前にコマンドがありません"
10881088
1089-#: parser.c:2203
1089+#: parser.c:2267
10901090 msgid "pipe redirection is not supported in the POSIXly-correct mode"
10911091 msgstr "パイプリダイレクトは POSIX 準拠モードでは使えません"
10921092
1093-#: parser.c:2221
1093+#: parser.c:2285
10941094 msgid "here-string is not supported in the POSIXly-correct mode"
10951095 msgstr "ヒアストリングは POSIX 準拠モードでは使えません"
10961096
1097-#: parser.c:2236
1097+#: parser.c:2300
10981098 msgid "the redirection target is missing"
10991099 msgstr "リダイレクトの対象が抜けています"
11001100
1101-#: parser.c:2246
1101+#: parser.c:2310
11021102 msgid "the end-of-here-document indicator is missing"
11031103 msgstr "ヒアドキュメントの終端子が抜けています"
11041104
1105-#: parser.c:2255
1105+#: parser.c:2319
11061106 msgid "process redirection is not supported in the POSIXly-correct mode"
11071107 msgstr "プロセスリダイレクトは POSIX 準拠モードでは使えません"
11081108
1109-#: parser.c:2261
1109+#: parser.c:2325
11101110 msgid "unclosed process redirection"
11111111 msgstr "プロセスリダイレクトが閉じられていません"
11121112
1113-#: parser.c:2272
1113+#: parser.c:2336
11141114 #, c-format
11151115 msgid "put a space between `%lc' and `%lc' for disambiguation"
11161116 msgstr "「%lc」と「%lc」の間に空白が必要です"
11171117
1118-#: parser.c:2323
1118+#: parser.c:2392
11191119 msgid "unexpected word after redirection"
11201120 msgstr "リダイレクトの直後に不正な単語があります"
11211121
1122-#: parser.c:2358 parser.c:2400 parser.c:2487 parser.c:2531
1122+#: parser.c:2427 parser.c:2469 parser.c:2556 parser.c:2600
11231123 #, c-format
11241124 msgid "commands are missing between `%ls' and `%ls'"
11251125 msgstr "「%ls」と「%ls」の間にコマンドがありません"
11261126
1127-#: parser.c:2412 parser.c:2521
1127+#: parser.c:2481 parser.c:2590
11281128 #, c-format
11291129 msgid "commands are missing after `%ls'"
11301130 msgstr "「%ls」の後にコマンドがありません"
11311131
1132-#: parser.c:2449
1132+#: parser.c:2518
11331133 msgid "an identifier is required after `for'"
11341134 msgstr "「for」の後には識別子が必要です"
11351135
1136-#: parser.c:2451
1136+#: parser.c:2520
11371137 #, c-format
11381138 msgid "`%ls' is not a valid identifier"
11391139 msgstr "「%ls」は有効な識別子ではありません"
11401140
1141-#: parser.c:2471
1141+#: parser.c:2540
11421142 msgid "`;' cannot appear on a new line"
11431143 msgstr "「;」は行頭に置けません"
11441144
1145-#: parser.c:2560 parser.c:2633 parser.c:2680
1145+#: parser.c:2629 parser.c:2702 parser.c:2938
11461146 #, c-format
11471147 msgid "a word is required after `%ls'"
11481148 msgstr "「%ls」の後には単語が必要です"
11491149
1150-#: parser.c:2623
1150+#: parser.c:2692
11511151 msgid "an unquoted `esac' cannot be the first case pattern"
11521152 msgstr "クォートしていない「esac」を最初のパターンにすることはできません"
11531153
1154-#: parser.c:2636
1154+#: parser.c:2705
11551155 #, c-format
11561156 msgid "encountered an invalid character `%lc' in the case pattern"
11571157 msgstr "case のパターン内に不正な文字「%lc」があります"
11581158
1159-#: parser.c:2712 parser.c:2766
1159+#: parser.c:2739
1160+msgid "The [[ ... ]] syntax is not supported in the POSIXly-correct mode"
1161+msgstr "[[ ... ]] 構文は POSIX 準拠モードでは使えません"
1162+
1163+#: parser.c:2763 parser.c:2842
1164+#, c-format
1165+msgid "invalid word `%ls' between `[[' and `]]'"
1166+msgstr "[[ ... ]] の中に不正な単語「%ls」があります"
1167+
1168+#: parser.c:2896
1169+msgid "conditional expression is missing or incomplete between `[[' and `]]'"
1170+msgstr "[[ ... ]] の中の条件式が不完全です"
1171+
1172+#: parser.c:2902
1173+msgid "unexpected linebreak in the middle of the [[ ... ]] command"
1174+msgstr "[[ ... ]] の途中に改行があります"
1175+
1176+#: parser.c:2905
1177+#, c-format
1178+msgid "`%ls' is not a valid operand in the conditional expression"
1179+msgstr "「%ls」は条件式における有効な被演算子ではありません"
1180+
1181+#: parser.c:2970 parser.c:3024
11601182 msgid "a function body must be a compound command"
11611183 msgstr "関数の本体は複合コマンドでなければなりません"
11621184
1163-#: parser.c:2734
1185+#: parser.c:2992
11641186 #, c-format
11651187 msgid "invalid use of `%lc'"
11661188 msgstr "「%lc」の使い方が正しくありません"
11671189
1168-#: parser.c:2741
1190+#: parser.c:2999
11691191 msgid "invalid function name"
11701192 msgstr "無効な関数名です"
11711193
1172-#: parser.c:2751
1194+#: parser.c:3009
11731195 msgid "`(' must be followed by `)' in a function definition"
11741196 msgstr "関数定義では「(」の直後に「)」が必要です"
11751197
1176-#: parser.c:2779
1198+#: parser.c:3037
11771199 msgid "the end-of-here-document indicator contains a newline"
11781200 msgstr "ヒアドキュメントの終端子に改行が入っています"
11791201
1180-#: parser.c:2806 parser.c:2840
1202+#: parser.c:3064 parser.c:3098
11811203 #, c-format
11821204 msgid "the here-document content is not closed by `%ls'"
11831205 msgstr "ヒアドキュメントが「%ls」で閉じられていません"
11841206
1185-#: parser.c:2892
1207+#: parser.c:3150
11861208 #, c-format
11871209 msgid "here-document content for %s%ls is missing"
11881210 msgstr "%s%ls に対応するヒアドキュメントの内容がありません"
@@ -1191,7 +1213,7 @@
11911213 msgid "$HOME is not set"
11921214 msgstr "$HOME が設定されていません"
11931215
1194-#: path.c:1030 variable.c:3043
1216+#: path.c:1030 variable.c:3046
11951217 msgid "$OLDPWD is not set"
11961218 msgstr "$OLDPWD が設定されていません"
11971219
@@ -1440,8 +1462,8 @@
14401462 msgid "no such array $%ls"
14411463 msgstr "$%ls というような配列はありません"
14421464
1443-#: variable.c:404 variable.c:606 variable.c:1722 variable.c:2331
1444-#: variable.c:3089
1465+#: variable.c:404 variable.c:606 variable.c:1725 variable.c:2334
1466+#: variable.c:3092
14451467 #, c-format
14461468 msgid "$%ls is read-only"
14471469 msgstr "$%ls は読み込み専用です"
@@ -1466,72 +1488,72 @@
14661488 msgid "function `%ls' cannot be redefined because it is read-only"
14671489 msgstr "関数「%ls」は読み込み専用なので再定義できません"
14681490
1469-#: variable.c:1426 variable.c:3175
1491+#: variable.c:1429 variable.c:3178
14701492 msgid "the directory stack is empty"
14711493 msgstr "ディレクトリスタックは空です"
14721494
1473-#: variable.c:1464 variable.c:3027 variable.c:3271
1495+#: variable.c:1467 variable.c:3030 variable.c:3274
14741496 msgid "$PWD is not set"
14751497 msgstr "$PWD が設定されていません"
14761498
1477-#: variable.c:1476
1499+#: variable.c:1479
14781500 #, c-format
14791501 msgid "index %ls is out of range"
14801502 msgstr "インデックス %ls は範囲外です"
14811503
1482-#: variable.c:1746
1504+#: variable.c:1749
14831505 #, c-format
14841506 msgid "no such variable $%ls"
14851507 msgstr "$%ls というような変数はありません"
14861508
1487-#: variable.c:1761
1509+#: variable.c:1764
14881510 #, c-format
14891511 msgid "no such function `%ls'"
14901512 msgstr "「%ls」というような関数はありません"
14911513
1492-#: variable.c:1951
1514+#: variable.c:1954
14931515 msgid "set or print variables"
14941516 msgstr "変数を設定または表示する"
14951517
1496-#: variable.c:1954
1518+#: variable.c:1957
14971519 msgid "\ttypeset [-fgprxX] [name[=value]...]\n"
14981520 msgstr "\ttypeset [-fgprxX] [名前[=値]...]\n"
14991521
1500-#: variable.c:1957
1522+#: variable.c:1960
15011523 msgid "export variables as environment variables"
15021524 msgstr "変数を環境変数としてエクスポートする"
15031525
1504-#: variable.c:1960
1526+#: variable.c:1963
15051527 msgid "\texport [-prX] [name[=value]...]\n"
15061528 msgstr "\texport [-prX] [名前[=値]...]\n"
15071529
1508-#: variable.c:1963
1530+#: variable.c:1966
15091531 msgid "make variables read-only"
15101532 msgstr "変数を読み込み専用にする"
15111533
1512-#: variable.c:1966
1534+#: variable.c:1969
15131535 msgid "\treadonly [-fpxX] [name[=value]...]\n"
15141536 msgstr "\treadonly [-fpxX] [名前[=値]...]\n"
15151537
1516-#: variable.c:2014
1538+#: variable.c:2017
15171539 msgid "more than one option cannot be used at once"
15181540 msgstr "二つ以上のオプションを同時に使うことはできません"
15191541
1520-#: variable.c:2033
1542+#: variable.c:2036
15211543 #, c-format
15221544 msgid "`%ls' is not a valid array name"
15231545 msgstr "「%ls」は有効な配列名ではありません"
15241546
1525-#: variable.c:2225
1547+#: variable.c:2228
15261548 #, c-format
15271549 msgid "index %ls is out of range (the actual size of array $%ls is %zu)"
15281550 msgstr "インデックス %ls は範囲外です (配列 $%ls のサイズは %zu です)"
15291551
1530-#: variable.c:2232
1552+#: variable.c:2235
15311553 msgid "manipulate an array"
15321554 msgstr "配列を操作する"
15331555
1534-#: variable.c:2235
1556+#: variable.c:2238
15351557 msgid ""
15361558 "\tarray # print arrays\n"
15371559 "\tarray name [value...] # set array values\n"
@@ -1545,30 +1567,30 @@
15451567 "\tarray -i 名前 インデックス [値...]\n"
15461568 "\tarray -s 名前 インデックス 値\n"
15471569
1548-#: variable.c:2306
1570+#: variable.c:2309
15491571 #, c-format
15501572 msgid "function `%ls' is read-only"
15511573 msgstr "関数「%ls」は読み込み専用です"
15521574
1553-#: variable.c:2342
1575+#: variable.c:2345
15541576 msgid "remove variables or functions"
15551577 msgstr "変数または関数を削除する"
15561578
1557-#: variable.c:2345
1579+#: variable.c:2348
15581580 msgid "\tunset [-fv] [name...]\n"
15591581 msgstr "\tunset [-fv] 名前...\n"
15601582
1561-#: variable.c:2388
1583+#: variable.c:2391
15621584 #, c-format
15631585 msgid "%ls: the operand value must not be negative"
15641586 msgstr "%ls: 負でないオペランドの値を指定してください"
15651587
1566-#: variable.c:2404 variable.c:3086
1588+#: variable.c:2407 variable.c:3089
15671589 #, c-format
15681590 msgid "$%ls is not an array"
15691591 msgstr "$%ls は配列ではありません"
15701592
1571-#: variable.c:2421
1593+#: variable.c:2424
15721594 #, c-format
15731595 msgid "%ld: cannot shift so many (there is only one positional parameter)"
15741596 msgid_plural ""
@@ -1576,86 +1598,86 @@
15761598 msgstr[0] ""
15771599 "%ld: シフトする数が多すぎます (位置パラメータは %zu 個しかありません)"
15781600
1579-#: variable.c:2427
1601+#: variable.c:2430
15801602 #, c-format
15811603 msgid "%ld: cannot shift so many (there is only one array element)"
15821604 msgid_plural "%ld: cannot shift so many (there are only %zu array elements)"
15831605 msgstr[0] "%ld: シフトする数が多すぎます (配列の要素は %zu 個しかありません)"
15841606
1585-#: variable.c:2451
1607+#: variable.c:2454
15861608 msgid "remove some positional parameters or array elements"
15871609 msgstr "位置パラメータまたは配列の要素の一部を削除する"
15881610
1589-#: variable.c:2454
1611+#: variable.c:2457
15901612 msgid "\tshift [-A array_name] [count]\n"
15911613 msgstr "\tshift [-A 配列名] [個数]\n"
15921614
1593-#: variable.c:2485 variable.c:2715
1615+#: variable.c:2488 variable.c:2718
15941616 #, c-format
15951617 msgid "`%ls' is not a valid variable name"
15961618 msgstr "「%ls」は有効な変数名ではありません"
15971619
1598-#: variable.c:2488
1620+#: variable.c:2491
15991621 #, c-format
16001622 msgid "`%ls' is not a valid option specification"
16011623 msgstr "「%ls」は有効なオプション指定文字列ではありません"
16021624
1603-#: variable.c:2546
1625+#: variable.c:2549
16041626 #, c-format
16051627 msgid "%ls: `-%lc' is not a valid option\n"
16061628 msgstr "%ls: 「%lc」は有効なオプションではありません\n"
16071629
1608-#: variable.c:2569
1630+#: variable.c:2572
16091631 #, c-format
16101632 msgid "%ls: the -%lc option's argument is missing\n"
16111633 msgstr "%ls: -%lc オプションの引数が抜けています\n"
16121634
1613-#: variable.c:2592
1635+#: variable.c:2595
16141636 msgid "$OPTIND has an invalid value"
16151637 msgstr "$OPTIND の値が正しくありません"
16161638
1617-#: variable.c:2647
1639+#: variable.c:2650
16181640 msgid "parse command options"
16191641 msgstr "コマンドのオプションを解析する"
16201642
1621-#: variable.c:2650
1643+#: variable.c:2653
16221644 msgid "\tgetopts options variable [argument...]\n"
16231645 msgstr "\tgetopts オプション 変数名 [引数...]\n"
16241646
1625-#: variable.c:2958
1647+#: variable.c:2961
16261648 msgid "read a line from the standard input"
16271649 msgstr "標準入力から行を読み込む"
16281650
1629-#: variable.c:2961
1651+#: variable.c:2964
16301652 msgid "\tread [-Aer] [-P|-p] variable...\n"
16311653 msgstr "\tread [-Aer] [-P|-p] 変数名...\n"
16321654
1633-#: variable.c:3141
1655+#: variable.c:3144
16341656 msgid "push a directory into the directory stack"
16351657 msgstr "ディレクトリスタックにディレクトリを追加する"
16361658
1637-#: variable.c:3144
1659+#: variable.c:3147
16381660 msgid "\tpushd [-L|-P] [directory]\n"
16391661 msgstr "\tpushd [-L|-P] [ディレクトリ]\n"
16401662
1641-#: variable.c:3184 variable.c:3260
1663+#: variable.c:3187 variable.c:3263
16421664 #, c-format
16431665 msgid "`%ls' is not a valid index"
16441666 msgstr "「%ls」は有効なインデックスではありません"
16451667
1646-#: variable.c:3207
1668+#: variable.c:3210
16471669 msgid "pop a directory from the directory stack"
16481670 msgstr "ディレクトリスタックからディレクトリを削除する"
16491671
1650-#: variable.c:3210
1672+#: variable.c:3213
16511673 msgid "\tpopd [index]\n"
16521674 msgstr "\tpopd [インデックス]\n"
16531675
1654-#: variable.c:3298
1676+#: variable.c:3301
16551677 msgid "print the directory stack"
16561678 msgstr "ディレクトリスタックを表示する"
16571679
1658-#: variable.c:3301
1680+#: variable.c:3304
16591681 msgid "\tdirs [-cv] [index...]\n"
16601682 msgstr "\tdirs [-cv] [インデックス...]\n"
16611683
--- yash/trunk/tests/Makefile.in (revision 3928)
+++ yash/trunk/tests/Makefile.in (revision 3929)
@@ -1,5 +1,5 @@
11 # Makefile.in for test of yash
2-# (C) 2007-2017 magicant
2+# (C) 2007-2018 magicant
33 #
44 # This program is free software: you can redistribute it and/or modify
55 # it under the terms of the GNU General Public License as published by
@@ -28,7 +28,7 @@
2828 LDLIBS = @LDLIBS@
2929 SOURCES = checkfg.c resetsig.c
3030 POSIX_TEST_SOURCES = alias-p.tst andor-p.tst arith-p.tst async-p.tst bg-p.tst break-p.tst builtins-p.tst case-p.tst cd-p.tst cmdsub-p.tst command-p.tst comment-p.tst continue-p.tst dot-p.tst errexit-p.tst error-p.tst eval-p.tst exec-p.tst exit-p.tst export-p.tst fg-p.tst fnmatch-p.tst for-p.tst fsplit-p.tst function-p.tst getopts-p.tst grouping-p.tst if-p.tst input-p.tst job-p.tst kill1-p.tst kill2-p.tst kill3-p.tst kill4-p.tst lineno-p.tst nop-p.tst option-p.tst param-p.tst path-p.tst pipeline-p.tst ppid-p.tst quote-p.tst read-p.tst readonly-p.tst redir-p.tst return-p.tst set-p.tst shift-p.tst signal-p.tst simple-p.tst test-p.tst testtty-p.tst tilde-p.tst trap-p.tst umask-p.tst unset-p.tst until-p.tst wait-p.tst while-p.tst
31-YASH_TEST_SOURCES = alias-y.tst andor-y.tst arith-y.tst array-y.tst async-y.tst bg-y.tst bindkey-y.tst brace-y.tst break-y.tst builtins-y.tst case-y.tst cd-y.tst cmdsub-y.tst command-y.tst complete-y.tst continue-y.tst dirstack-y.tst disown-y.tst dot-y.tst echo-y.tst errexit-y.tst error-y.tst errretur-y.tst eval-y.tst exec-y.tst exit-y.tst export-y.tst fc-y.tst fg-y.tst for-y.tst fsplit-y.tst function-y.tst getopts-y.tst grouping-y.tst hash-y.tst help-y.tst history-y.tst historyx-y.tst if-y.tst job-y.tst jobs-y.tst kill-y.tst lineno-y.tst option-y.tst param-y.tst pipeline-y.tst printf-y.tst prompt-y.tst pwd-y.tst quote-y.tst random-y.tst read-y.tst readonly-y.tst redir-y.tst return-y.tst set-y.tst settty-y.tst shift-y.tst signal1-y.tst signal2-y.tst simple-y.tst startup-y.tst suspend-y.tst test-y.tst tilde-y.tst times-y.tst trap-y.tst typeset-y.tst ulimit-y.tst umask-y.tst unset-y.tst until-y.tst wait-y.tst while-y.tst
31+YASH_TEST_SOURCES = alias-y.tst andor-y.tst arith-y.tst array-y.tst async-y.tst bg-y.tst bindkey-y.tst brace-y.tst bracket-y.tst break-y.tst builtins-y.tst case-y.tst cd-y.tst cmdsub-y.tst command-y.tst complete-y.tst continue-y.tst dirstack-y.tst disown-y.tst dot-y.tst echo-y.tst errexit-y.tst error-y.tst errretur-y.tst eval-y.tst exec-y.tst exit-y.tst export-y.tst fc-y.tst fg-y.tst for-y.tst fsplit-y.tst function-y.tst getopts-y.tst grouping-y.tst hash-y.tst help-y.tst history-y.tst historyx-y.tst if-y.tst job-y.tst jobs-y.tst kill-y.tst lineno-y.tst option-y.tst param-y.tst pipeline-y.tst printf-y.tst prompt-y.tst pwd-y.tst quote-y.tst random-y.tst read-y.tst readonly-y.tst redir-y.tst return-y.tst set-y.tst settty-y.tst shift-y.tst signal1-y.tst signal2-y.tst simple-y.tst startup-y.tst suspend-y.tst test-y.tst tilde-y.tst times-y.tst trap-y.tst typeset-y.tst ulimit-y.tst umask-y.tst unset-y.tst until-y.tst wait-y.tst while-y.tst
3232 TEST_SOURCES = $(POSIX_TEST_SOURCES) $(YASH_TEST_SOURCES)
3333 TEST_RESULTS = $(TEST_SOURCES:.tst=.trs)
3434 RECHECK_LOGS = $(TEST_RESULTS)
--- yash/trunk/tests/fulltest.sh (revision 3928)
+++ yash/trunk/tests/fulltest.sh (revision 3929)
@@ -1,5 +1,5 @@
11 # fulltest.sh: runs tests for many combinations of configuration options
2-# (C) 2010-2015 magicant
2+# (C) 2010-2018 magicant
33
44 do_test () {
55 if [ -r Makefile ]; then
@@ -15,33 +15,24 @@
1515
1616 echo "$0: using '${MAKE:=make}' as make"
1717
18-a0='' a1='--disable-lineedit' a2='--disable-history --disable-lineedit' a3=''
19-b0='' b1='--disable-array'
20-c0='' c1='--disable-dirstack'
21-d0='' d1='--disable-help'
22-e0='' e1='--disable-nls'
23-f0='' f1='--disable-printf'
24-g0='' g1='--disable-socket'
25-h0='' h1='--disable-test'
26-i0='' i1='--disable-ulimit'
27-j0='' j1='--debug'
28-k0='' k1=''
29-l0='' l1=''
30-m0='' m1=''
18+a0='' a1='--disable-lineedit' a2='--disable-history --disable-lineedit'
19+b0='' b1='--disable-double-bracket' b2='--disable-double-bracket --disable-test'
20+c0='' c1='--disable-array'
21+d0='' d1='--disable-dirstack'
22+e0='' e1='--disable-help'
23+f0='' f1='--disable-nls'
24+g0='' g1='--disable-printf'
25+h0='' h1='--disable-socket'
26+i0='' i1='--disable-ulimit'
27+j0='' j1='--debug'
3128
32-do_test $a0 $b0 $c0 $d0 $e0 $f0 $g0 $h0 $i0 $j0 $k0 $l0 $m0 "$@"
33-do_test $a0 $b0 $c0 $d0 $e0 $f1 $g1 $h1 $i1 $j1 $k1 $l1 $m1 "$@"
34-do_test $a0 $b1 $c1 $d1 $e1 $f0 $g0 $h0 $i0 $j1 $k1 $l1 $m1 "$@"
35-do_test $a0 $b1 $c1 $d1 $e1 $f1 $g1 $h1 $i1 $j0 $k0 $l0 $m0 "$@"
36-do_test $a1 $b0 $c0 $d1 $e1 $f0 $g0 $h1 $i1 $j0 $k0 $l1 $m1 "$@"
37-do_test $a1 $b0 $c0 $d1 $e1 $f1 $g1 $h0 $i0 $j1 $k1 $l0 $m0 "$@"
38-do_test $a1 $b1 $c1 $d0 $e0 $f0 $g0 $h1 $i1 $j1 $k1 $l0 $m0 "$@"
39-do_test $a1 $b1 $c1 $d0 $e0 $f1 $g1 $h0 $i0 $j0 $k0 $l1 $m1 "$@"
40-do_test $a2 $b0 $c1 $d0 $e1 $f0 $g1 $h0 $i1 $j0 $k1 $l0 $m1 "$@"
41-do_test $a2 $b0 $c1 $d0 $e1 $f1 $g0 $h1 $i0 $j1 $k0 $l1 $m0 "$@"
42-do_test $a2 $b1 $c0 $d1 $e0 $f0 $g1 $h0 $i1 $j1 $k0 $l1 $m0 "$@"
43-do_test $a2 $b1 $c0 $d1 $e0 $f1 $g0 $h1 $i0 $j0 $k1 $l0 $m1 "$@"
44-do_test $a3 $b0 $c1 $d1 $e0 $f0 $g1 $h1 $i0 $j0 $k1 $l1 $m0 "$@"
45-do_test $a3 $b0 $c1 $d1 $e0 $f1 $g0 $h0 $i1 $j1 $k0 $l0 $m1 "$@"
46-do_test $a3 $b1 $c0 $d0 $e1 $f0 $g1 $h1 $i0 $j1 $k0 $l0 $m1 "$@"
47-do_test $a3 $b1 $c0 $d0 $e1 $f1 $g0 $h0 $i1 $j0 $k1 $l1 $m0 "$@"
29+do_test $a0 $b0 $c0 $d0 $e1 $f1 $g0 $h1 $i0 $j0 "$@"
30+do_test $a0 $b1 $c1 $d1 $e1 $f0 $g1 $h0 $i1 $j1 "$@"
31+do_test $a0 $b2 $c0 $d1 $e0 $f0 $g1 $h1 $i0 $j1 "$@"
32+do_test $a1 $b0 $c0 $d0 $e0 $f0 $g1 $h1 $i1 $j1 "$@"
33+do_test $a1 $b1 $c1 $d0 $e1 $f0 $g0 $h0 $i1 $j0 "$@"
34+do_test $a1 $b2 $c1 $d1 $e1 $f1 $g1 $h0 $i0 $j0 "$@"
35+do_test $a2 $b0 $c1 $d1 $e0 $f1 $g1 $h0 $i1 $j0 "$@"
36+do_test $a2 $b1 $c0 $d1 $e0 $f1 $g1 $h1 $i0 $j1 "$@"
37+do_test $a2 $b2 $c0 $d1 $e0 $f0 $g0 $h0 $i1 $j1 "$@"
38+do_test $a2 $b2 $c1 $d0 $e1 $f0 $g1 $h1 $i0 $j0 "$@"
--- yash/trunk/variable.c (revision 3928)
+++ yash/trunk/variable.c (revision 3929)
@@ -1295,6 +1295,9 @@
12951295 case CT_CASE:
12961296 hash_all_commands_in_case(c->c_casitems);
12971297 break;
1298+#if YASH_ENABLE_DOUBLE_BRACKET
1299+ case CT_BRACKET:
1300+#endif
12981301 case CT_FUNCDEF:
12991302 break;
13001303 }
--- yash/trunk/xfnmatch.c (revision 3928)
+++ yash/trunk/xfnmatch.c (revision 3929)
@@ -1,6 +1,6 @@
11 /* Yash: yet another shell */
22 /* xfnmatch.c: regex matching wrapper as a replacement for fnmatch */
3-/* (C) 2007-2012 magicant */
3+/* (C) 2007-2018 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
@@ -732,6 +732,16 @@
732732 }
733733 }
734734
735+/* Tests if pattern matching expression `pattern' matches string `s'. */
736+bool match_pattern(const wchar_t *s, const wchar_t *pattern)
737+{
738+ xfnmatch_T *xfnm = xfnm_compile(pattern, XFNM_HEADONLY | XFNM_TAILONLY);
739+ if (xfnm == NULL)
740+ return false;
741+ bool match = (xfnm_wmatch(xfnm, s).start != (size_t) -1);
742+ xfnm_free(xfnm);
743+ return match;
744+}
735745
736746 #if YASH_ENABLE_TEST
737747
--- yash/trunk/xfnmatch.h (revision 3928)
+++ yash/trunk/xfnmatch.h (revision 3929)
@@ -1,6 +1,6 @@
11 /* Yash: yet another shell */
22 /* xfnmatch.h: regex matching wrapper as a replacement for fnmatch */
3-/* (C) 2007-2012 magicant */
3+/* (C) 2007-2018 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
@@ -55,6 +55,8 @@
5555 __attribute__((malloc,warn_unused_result,nonnull));
5656 extern void xfnm_free(xfnmatch_T *xfnm);
5757
58+extern _Bool match_pattern(const wchar_t *s, const wchar_t *pattern)
59+ __attribute__((nonnull));
5860 #if YASH_ENABLE_TEST
5961 extern _Bool match_regex(const wchar_t *s, const wchar_t *regex)
6062 __attribute__((nonnull));
Show on old repository browser