11//! test builtin command ([ and test)
22
3+ use std:: collections:: HashMap ;
34use std:: path:: { Path , PathBuf } ;
45use std:: sync:: Arc ;
56
@@ -23,7 +24,7 @@ impl Builtin for Test {
2324
2425 let cwd = ctx. cwd . clone ( ) ;
2526 // Parse and evaluate the expression
26- let result = evaluate_expression ( ctx. args , & ctx. fs , & cwd) . await ;
27+ let result = evaluate_expression ( ctx. args , & ctx. fs , & cwd, ctx . variables ) . await ;
2728
2829 if result {
2930 Ok ( ExecResult :: ok ( String :: new ( ) ) )
@@ -54,7 +55,7 @@ impl Builtin for Bracket {
5455
5556 let cwd = ctx. cwd . clone ( ) ;
5657 // Parse and evaluate the expression
57- let result = evaluate_expression ( & args, & ctx. fs , & cwd) . await ;
58+ let result = evaluate_expression ( & args, & ctx. fs , & cwd, ctx . variables ) . await ;
5859
5960 if result {
6061 Ok ( ExecResult :: ok ( String :: new ( ) ) )
@@ -79,6 +80,7 @@ fn evaluate_expression<'a>(
7980 args : & ' a [ String ] ,
8081 fs : & ' a Arc < dyn FileSystem > ,
8182 cwd : & ' a Path ,
83+ variables : & ' a HashMap < String , String > ,
8284) -> std:: pin:: Pin < Box < dyn std:: future:: Future < Output = bool > + Send + ' a > > {
8385 Box :: pin ( async move {
8486 if args. is_empty ( ) {
@@ -87,26 +89,26 @@ fn evaluate_expression<'a>(
8789
8890 // Handle negation
8991 if args[ 0 ] == "!" {
90- return !evaluate_expression ( & args[ 1 ..] , fs, cwd) . await ;
92+ return !evaluate_expression ( & args[ 1 ..] , fs, cwd, variables ) . await ;
9193 }
9294
9395 // Handle parentheses (basic support)
9496 if args[ 0 ] == "(" && args. last ( ) . map ( |s| s. as_str ( ) ) == Some ( ")" ) {
95- return evaluate_expression ( & args[ 1 ..args. len ( ) - 1 ] , fs, cwd) . await ;
97+ return evaluate_expression ( & args[ 1 ..args. len ( ) - 1 ] , fs, cwd, variables ) . await ;
9698 }
9799
98100 // Look for logical operators: -o has lowest precedence, then -a.
99101 // Scan for -o first (split at lowest precedence first).
100102 for ( i, arg) in args. iter ( ) . enumerate ( ) {
101103 if arg == "-o" && i > 0 {
102- return evaluate_expression ( & args[ ..i] , fs, cwd) . await
103- || evaluate_expression ( & args[ i + 1 ..] , fs, cwd) . await ;
104+ return evaluate_expression ( & args[ ..i] , fs, cwd, variables ) . await
105+ || evaluate_expression ( & args[ i + 1 ..] , fs, cwd, variables ) . await ;
104106 }
105107 }
106108 for ( i, arg) in args. iter ( ) . enumerate ( ) {
107109 if arg == "-a" && i > 0 {
108- return evaluate_expression ( & args[ ..i] , fs, cwd) . await
109- && evaluate_expression ( & args[ i + 1 ..] , fs, cwd) . await ;
110+ return evaluate_expression ( & args[ ..i] , fs, cwd, variables ) . await
111+ && evaluate_expression ( & args[ i + 1 ..] , fs, cwd, variables ) . await ;
110112 }
111113 }
112114
@@ -118,7 +120,7 @@ fn evaluate_expression<'a>(
118120 }
119121 2 => {
120122 // Unary operators
121- evaluate_unary ( & args[ 0 ] , & args[ 1 ] , fs, cwd) . await
123+ evaluate_unary ( & args[ 0 ] , & args[ 1 ] , fs, cwd, variables ) . await
122124 }
123125 3 => {
124126 // Binary operators
@@ -130,7 +132,13 @@ fn evaluate_expression<'a>(
130132}
131133
132134/// Evaluate a unary test expression
133- async fn evaluate_unary ( op : & str , arg : & str , fs : & Arc < dyn FileSystem > , cwd : & Path ) -> bool {
135+ async fn evaluate_unary (
136+ op : & str ,
137+ arg : & str ,
138+ fs : & Arc < dyn FileSystem > ,
139+ cwd : & Path ,
140+ variables : & HashMap < String , String > ,
141+ ) -> bool {
134142 match op {
135143 // String tests
136144 "-z" => arg. is_empty ( ) ,
@@ -211,7 +219,13 @@ async fn evaluate_unary(op: &str, arg: &str, fs: &Arc<dyn FileSystem>, cwd: &Pat
211219 "-S" => false , // socket (not supported)
212220 "-b" => false , // block device (not supported)
213221 "-c" => false , // character device (not supported)
214- "-t" => false , // file descriptor is open and refers to a terminal (not supported)
222+ "-t" => {
223+ // file descriptor refers to a terminal
224+ // In VFS sandbox, defaults to false for all FDs.
225+ // Configurable via _TTY_N variables (e.g. _TTY_0=1 for stdin).
226+ let fd_key = format ! ( "_TTY_{}" , arg) ;
227+ variables. get ( & fd_key) . map ( |v| v == "1" ) . unwrap_or ( false )
228+ }
215229
216230 _ => false ,
217231 }
0 commit comments