1use std::{borrow::Cow, fs::File, sync::Arc};
2
3use nu_path::{expand_path, expand_path_with};
4use nu_protocol::{
5 DataSource, DeclId, ENV_VARIABLE_ID, Flag, IntoPipelineData, IntoSpanned, ListStream, OutDest,
6 PipelineData, PipelineMetadata, PositionalArg, Range, Record, RegId, ShellError, Signals,
7 Signature, Span, Spanned, Type, Value, VarId,
8 ast::{Bits, Block, Boolean, CellPath, Comparison, Math, Operator},
9 debugger::DebugContext,
10 engine::{
11 Argument, Closure, EngineState, ErrorHandler, Matcher, Redirection, Stack, StateWorkingSet,
12 },
13 ir::{Call, DataSlice, Instruction, IrAstRef, IrBlock, Literal, RedirectMode},
14 shell_error::io::IoError,
15};
16use nu_utils::IgnoreCaseExt;
17
18use crate::{
19 ENV_CONVERSIONS, convert_env_vars, eval::is_automatic_env_var, eval_block_with_early_return,
20};
21
22pub fn eval_ir_block<D: DebugContext>(
24 engine_state: &EngineState,
25 stack: &mut Stack,
26 block: &Block,
27 input: PipelineData,
28) -> Result<PipelineData, ShellError> {
29 let maximum_call_stack_depth: u64 = engine_state.config.recursion_limit as u64;
34 if stack.recursion_count > maximum_call_stack_depth {
35 return Err(ShellError::RecursionLimitReached {
36 recursion_limit: maximum_call_stack_depth,
37 span: block.span,
38 });
39 }
40
41 if let Some(ir_block) = &block.ir_block {
42 D::enter_block(engine_state, block);
43
44 let args_base = stack.arguments.get_base();
45 let error_handler_base = stack.error_handlers.get_base();
46
47 let mut registers = Vec::with_capacity(ir_block.register_count as usize);
50 for _ in 0..ir_block.register_count {
51 registers.push(PipelineData::Empty);
52 }
53
54 let mut files = vec![None; ir_block.file_count as usize];
56
57 let result = eval_ir_block_impl::<D>(
58 &mut EvalContext {
59 engine_state,
60 stack,
61 data: &ir_block.data,
62 block_span: &block.span,
63 args_base,
64 error_handler_base,
65 redirect_out: None,
66 redirect_err: None,
67 matches: vec![],
68 registers: &mut registers[..],
69 files: &mut files[..],
70 },
71 ir_block,
72 input,
73 );
74
75 stack.error_handlers.leave_frame(error_handler_base);
76 stack.arguments.leave_frame(args_base);
77
78 D::leave_block(engine_state, block);
79
80 result
81 } else {
82 Err(ShellError::GenericError {
84 error: "Can't evaluate block in IR mode".into(),
85 msg: "block is missing compiled representation".into(),
86 span: block.span,
87 help: Some("the IrBlock is probably missing due to a compilation error".into()),
88 inner: vec![],
89 })
90 }
91}
92
93struct EvalContext<'a> {
95 engine_state: &'a EngineState,
96 stack: &'a mut Stack,
97 data: &'a Arc<[u8]>,
98 block_span: &'a Option<Span>,
100 args_base: usize,
102 error_handler_base: usize,
104 redirect_out: Option<Redirection>,
106 redirect_err: Option<Redirection>,
108 matches: Vec<(VarId, Value)>,
110 registers: &'a mut [PipelineData],
112 files: &'a mut [Option<Arc<File>>],
114}
115
116impl<'a> EvalContext<'a> {
117 #[inline]
119 fn put_reg(&mut self, reg_id: RegId, new_value: PipelineData) {
120 self.registers[reg_id.get() as usize] = new_value;
122 }
123
124 #[inline]
126 fn borrow_reg(&self, reg_id: RegId) -> &PipelineData {
127 &self.registers[reg_id.get() as usize]
128 }
129
130 #[inline]
132 fn take_reg(&mut self, reg_id: RegId) -> PipelineData {
133 std::mem::replace(
135 &mut self.registers[reg_id.get() as usize],
136 PipelineData::Empty,
137 )
138 }
139
140 fn clone_reg(&mut self, reg_id: RegId, error_span: Span) -> Result<PipelineData, ShellError> {
142 match &self.registers[reg_id.get() as usize] {
143 PipelineData::Empty => Ok(PipelineData::Empty),
144 PipelineData::Value(val, meta) => Ok(PipelineData::Value(val.clone(), meta.clone())),
145 _ => Err(ShellError::IrEvalError {
146 msg: "Must collect to value before using instruction that clones from a register"
147 .into(),
148 span: Some(error_span),
149 }),
150 }
151 }
152
153 fn clone_reg_value(&mut self, reg_id: RegId, fallback_span: Span) -> Result<Value, ShellError> {
155 match self.clone_reg(reg_id, fallback_span)? {
156 PipelineData::Empty => Ok(Value::nothing(fallback_span)),
157 PipelineData::Value(val, _) => Ok(val),
158 _ => unreachable!("clone_reg should never return stream data"),
159 }
160 }
161
162 fn collect_reg(&mut self, reg_id: RegId, fallback_span: Span) -> Result<Value, ShellError> {
164 let data = self.take_reg(reg_id);
165 let span = data.span().unwrap_or(fallback_span);
166 data.into_value(span)
167 }
168
169 fn get_str(&self, slice: DataSlice, error_span: Span) -> Result<&'a str, ShellError> {
171 std::str::from_utf8(&self.data[slice]).map_err(|_| ShellError::IrEvalError {
172 msg: format!("data slice does not refer to valid UTF-8: {slice:?}"),
173 span: Some(error_span),
174 })
175 }
176}
177
178fn eval_ir_block_impl<D: DebugContext>(
180 ctx: &mut EvalContext<'_>,
181 ir_block: &IrBlock,
182 input: PipelineData,
183) -> Result<PipelineData, ShellError> {
184 if !ctx.registers.is_empty() {
185 ctx.registers[0] = input;
186 }
187
188 let mut pc = 0;
190 let need_backtrace = ctx.engine_state.get_env_var("NU_BACKTRACE").is_some();
191
192 while pc < ir_block.instructions.len() {
193 let instruction = &ir_block.instructions[pc];
194 let span = &ir_block.spans[pc];
195 let ast = &ir_block.ast[pc];
196
197 D::enter_instruction(ctx.engine_state, ir_block, pc, ctx.registers);
198
199 let result = eval_instruction::<D>(ctx, instruction, span, ast, need_backtrace);
200
201 D::leave_instruction(
202 ctx.engine_state,
203 ir_block,
204 pc,
205 ctx.registers,
206 result.as_ref().err(),
207 );
208
209 match result {
210 Ok(InstructionResult::Continue) => {
211 pc += 1;
212 }
213 Ok(InstructionResult::Branch(next_pc)) => {
214 pc = next_pc;
215 }
216 Ok(InstructionResult::Return(reg_id)) => {
217 return Ok(ctx.take_reg(reg_id));
218 }
219 Err(
220 err @ (ShellError::Return { .. }
221 | ShellError::Continue { .. }
222 | ShellError::Break { .. }),
223 ) => {
224 return Err(err);
226 }
227 Err(err) => {
228 if let Some(error_handler) = ctx.stack.error_handlers.pop(ctx.error_handler_base) {
229 prepare_error_handler(ctx, error_handler, Some(err.into_spanned(*span)));
231 pc = error_handler.handler_index;
232 } else if need_backtrace {
233 let err = ShellError::into_chainned(err, *span);
234 return Err(err);
235 } else {
236 return Err(err);
237 }
238 }
239 }
240 }
241
242 Err(ShellError::IrEvalError {
244 msg: format!(
245 "Program counter out of range (pc={pc}, len={len})",
246 len = ir_block.instructions.len(),
247 ),
248 span: *ctx.block_span,
249 })
250}
251
252fn prepare_error_handler(
254 ctx: &mut EvalContext<'_>,
255 error_handler: ErrorHandler,
256 error: Option<Spanned<ShellError>>,
257) {
258 if let Some(reg_id) = error_handler.error_register {
259 if let Some(error) = error {
260 ctx.stack.set_last_error(&error.item);
262 ctx.put_reg(
264 reg_id,
265 error
266 .item
267 .into_value(&StateWorkingSet::new(ctx.engine_state), error.span)
268 .into_pipeline_data(),
269 );
270 } else {
271 ctx.put_reg(reg_id, PipelineData::Empty);
273 }
274 }
275}
276
277#[derive(Debug)]
279enum InstructionResult {
280 Continue,
281 Branch(usize),
282 Return(RegId),
283}
284
285fn eval_instruction<D: DebugContext>(
287 ctx: &mut EvalContext<'_>,
288 instruction: &Instruction,
289 span: &Span,
290 ast: &Option<IrAstRef>,
291 need_backtrace: bool,
292) -> Result<InstructionResult, ShellError> {
293 use self::InstructionResult::*;
294
295 instruction.check_interrupt(ctx.engine_state, span)?;
297
298 match instruction {
301 Instruction::Unreachable => Err(ShellError::IrEvalError {
302 msg: "Reached unreachable code".into(),
303 span: Some(*span),
304 }),
305 Instruction::LoadLiteral { dst, lit } => load_literal(ctx, *dst, lit, *span),
306 Instruction::LoadValue { dst, val } => {
307 ctx.put_reg(*dst, Value::clone(val).into_pipeline_data());
308 Ok(Continue)
309 }
310 Instruction::Move { dst, src } => {
311 let val = ctx.take_reg(*src);
312 ctx.put_reg(*dst, val);
313 Ok(Continue)
314 }
315 Instruction::Clone { dst, src } => {
316 let data = ctx.clone_reg(*src, *span)?;
317 ctx.put_reg(*dst, data);
318 Ok(Continue)
319 }
320 Instruction::Collect { src_dst } => {
321 let data = ctx.take_reg(*src_dst);
322 let value = collect(data, *span)?;
323 ctx.put_reg(*src_dst, value);
324 Ok(Continue)
325 }
326 Instruction::Span { src_dst } => {
327 let data = ctx.take_reg(*src_dst);
328 let spanned = data.with_span(*span);
329 ctx.put_reg(*src_dst, spanned);
330 Ok(Continue)
331 }
332 Instruction::Drop { src } => {
333 ctx.take_reg(*src);
334 Ok(Continue)
335 }
336 Instruction::Drain { src } => {
337 let data = ctx.take_reg(*src);
338 drain(ctx, data)
339 }
340 Instruction::DrainIfEnd { src } => {
341 let data = ctx.take_reg(*src);
342 let res = {
343 let stack = &mut ctx
344 .stack
345 .push_redirection(ctx.redirect_out.clone(), ctx.redirect_err.clone());
346 data.drain_to_out_dests(ctx.engine_state, stack)?
347 };
348 ctx.put_reg(*src, res);
349 Ok(Continue)
350 }
351 Instruction::LoadVariable { dst, var_id } => {
352 let value = get_var(ctx, *var_id, *span)?;
353 ctx.put_reg(*dst, value.into_pipeline_data());
354 Ok(Continue)
355 }
356 Instruction::StoreVariable { var_id, src } => {
357 let value = ctx.collect_reg(*src, *span)?;
358 ctx.stack.add_var(*var_id, value);
359 Ok(Continue)
360 }
361 Instruction::DropVariable { var_id } => {
362 ctx.stack.remove_var(*var_id);
363 Ok(Continue)
364 }
365 Instruction::LoadEnv { dst, key } => {
366 let key = ctx.get_str(*key, *span)?;
367 if let Some(value) = get_env_var_case_insensitive(ctx, key) {
368 let new_value = value.clone().into_pipeline_data();
369 ctx.put_reg(*dst, new_value);
370 Ok(Continue)
371 } else {
372 Err(ShellError::CantFindColumn {
375 col_name: key.into(),
376 span: Some(*span),
377 src_span: *span,
378 })
379 }
380 }
381 Instruction::LoadEnvOpt { dst, key } => {
382 let key = ctx.get_str(*key, *span)?;
383 let value = get_env_var_case_insensitive(ctx, key)
384 .cloned()
385 .unwrap_or(Value::nothing(*span));
386 ctx.put_reg(*dst, value.into_pipeline_data());
387 Ok(Continue)
388 }
389 Instruction::StoreEnv { key, src } => {
390 let key = ctx.get_str(*key, *span)?;
391 let value = ctx.collect_reg(*src, *span)?;
392
393 let key = get_env_var_name_case_insensitive(ctx, key);
394
395 if !is_automatic_env_var(&key) {
396 let is_config = key == "config";
397 let update_conversions = key == ENV_CONVERSIONS;
398
399 ctx.stack.add_env_var(key.into_owned(), value.clone());
400
401 if is_config {
402 ctx.stack.update_config(ctx.engine_state)?;
403 }
404 if update_conversions {
405 convert_env_vars(ctx.stack, ctx.engine_state, &value)?;
406 }
407 Ok(Continue)
408 } else {
409 Err(ShellError::AutomaticEnvVarSetManually {
410 envvar_name: key.into(),
411 span: *span,
412 })
413 }
414 }
415 Instruction::PushPositional { src } => {
416 let val = ctx.collect_reg(*src, *span)?.with_span(*span);
417 ctx.stack.arguments.push(Argument::Positional {
418 span: *span,
419 val,
420 ast: ast.clone().map(|ast_ref| ast_ref.0),
421 });
422 Ok(Continue)
423 }
424 Instruction::AppendRest { src } => {
425 let vals = ctx.collect_reg(*src, *span)?.with_span(*span);
426 ctx.stack.arguments.push(Argument::Spread {
427 span: *span,
428 vals,
429 ast: ast.clone().map(|ast_ref| ast_ref.0),
430 });
431 Ok(Continue)
432 }
433 Instruction::PushFlag { name } => {
434 let data = ctx.data.clone();
435 ctx.stack.arguments.push(Argument::Flag {
436 data,
437 name: *name,
438 short: DataSlice::empty(),
439 span: *span,
440 });
441 Ok(Continue)
442 }
443 Instruction::PushShortFlag { short } => {
444 let data = ctx.data.clone();
445 ctx.stack.arguments.push(Argument::Flag {
446 data,
447 name: DataSlice::empty(),
448 short: *short,
449 span: *span,
450 });
451 Ok(Continue)
452 }
453 Instruction::PushNamed { name, src } => {
454 let val = ctx.collect_reg(*src, *span)?.with_span(*span);
455 let data = ctx.data.clone();
456 ctx.stack.arguments.push(Argument::Named {
457 data,
458 name: *name,
459 short: DataSlice::empty(),
460 span: *span,
461 val,
462 ast: ast.clone().map(|ast_ref| ast_ref.0),
463 });
464 Ok(Continue)
465 }
466 Instruction::PushShortNamed { short, src } => {
467 let val = ctx.collect_reg(*src, *span)?.with_span(*span);
468 let data = ctx.data.clone();
469 ctx.stack.arguments.push(Argument::Named {
470 data,
471 name: DataSlice::empty(),
472 short: *short,
473 span: *span,
474 val,
475 ast: ast.clone().map(|ast_ref| ast_ref.0),
476 });
477 Ok(Continue)
478 }
479 Instruction::PushParserInfo { name, info } => {
480 let data = ctx.data.clone();
481 ctx.stack.arguments.push(Argument::ParserInfo {
482 data,
483 name: *name,
484 info: info.clone(),
485 });
486 Ok(Continue)
487 }
488 Instruction::RedirectOut { mode } => {
489 ctx.redirect_out = eval_redirection(ctx, mode, *span, RedirectionStream::Out)?;
490 Ok(Continue)
491 }
492 Instruction::RedirectErr { mode } => {
493 ctx.redirect_err = eval_redirection(ctx, mode, *span, RedirectionStream::Err)?;
494 Ok(Continue)
495 }
496 Instruction::CheckErrRedirected { src } => match ctx.borrow_reg(*src) {
497 #[cfg(feature = "os")]
498 PipelineData::ByteStream(stream, _)
499 if matches!(stream.source(), nu_protocol::ByteStreamSource::Child(_)) =>
500 {
501 Ok(Continue)
502 }
503 _ => Err(ShellError::GenericError {
504 error: "Can't redirect stderr of internal command output".into(),
505 msg: "piping stderr only works on external commands".into(),
506 span: Some(*span),
507 help: None,
508 inner: vec![],
509 }),
510 },
511 Instruction::OpenFile {
512 file_num,
513 path,
514 append,
515 } => {
516 let path = ctx.collect_reg(*path, *span)?;
517 let file = open_file(ctx, &path, *append)?;
518 ctx.files[*file_num as usize] = Some(file);
519 Ok(Continue)
520 }
521 Instruction::WriteFile { file_num, src } => {
522 let src = ctx.take_reg(*src);
523 let file = ctx
524 .files
525 .get(*file_num as usize)
526 .cloned()
527 .flatten()
528 .ok_or_else(|| ShellError::IrEvalError {
529 msg: format!("Tried to write to file #{file_num}, but it is not open"),
530 span: Some(*span),
531 })?;
532 let is_external = if let PipelineData::ByteStream(stream, ..) = &src {
533 stream.source().is_external()
534 } else {
535 false
536 };
537 if let Err(err) = src.write_to(file.as_ref()) {
538 if is_external {
539 ctx.stack.set_last_error(&err);
540 }
541 Err(err)?
542 } else {
543 Ok(Continue)
544 }
545 }
546 Instruction::CloseFile { file_num } => {
547 if ctx.files[*file_num as usize].take().is_some() {
548 Ok(Continue)
549 } else {
550 Err(ShellError::IrEvalError {
551 msg: format!("Tried to close file #{file_num}, but it is not open"),
552 span: Some(*span),
553 })
554 }
555 }
556 Instruction::Call { decl_id, src_dst } => {
557 let input = ctx.take_reg(*src_dst);
558 let mut result = eval_call::<D>(ctx, *decl_id, *span, input)?;
559 if need_backtrace {
560 match &mut result {
561 PipelineData::ByteStream(s, ..) => s.push_caller_span(*span),
562 PipelineData::ListStream(s, ..) => s.push_caller_span(*span),
563 _ => (),
564 };
565 }
566 ctx.put_reg(*src_dst, result);
567 Ok(Continue)
568 }
569 Instruction::StringAppend { src_dst, val } => {
570 let string_value = ctx.collect_reg(*src_dst, *span)?;
571 let operand_value = ctx.collect_reg(*val, *span)?;
572 let string_span = string_value.span();
573
574 let mut string = string_value.into_string()?;
575 let operand = if let Value::String { val, .. } = operand_value {
576 val
578 } else {
579 operand_value.to_expanded_string(", ", ctx.engine_state.get_config())
580 };
581 string.push_str(&operand);
582
583 let new_string_value = Value::string(string, string_span);
584 ctx.put_reg(*src_dst, new_string_value.into_pipeline_data());
585 Ok(Continue)
586 }
587 Instruction::GlobFrom { src_dst, no_expand } => {
588 let string_value = ctx.collect_reg(*src_dst, *span)?;
589 let glob_value = if matches!(string_value, Value::Glob { .. }) {
590 string_value
592 } else {
593 let string = string_value.into_string()?;
595 Value::glob(string, *no_expand, *span)
596 };
597 ctx.put_reg(*src_dst, glob_value.into_pipeline_data());
598 Ok(Continue)
599 }
600 Instruction::ListPush { src_dst, item } => {
601 let list_value = ctx.collect_reg(*src_dst, *span)?;
602 let item = ctx.collect_reg(*item, *span)?;
603 let list_span = list_value.span();
604 let mut list = list_value.into_list()?;
605 list.push(item);
606 ctx.put_reg(*src_dst, Value::list(list, list_span).into_pipeline_data());
607 Ok(Continue)
608 }
609 Instruction::ListSpread { src_dst, items } => {
610 let list_value = ctx.collect_reg(*src_dst, *span)?;
611 let items = ctx.collect_reg(*items, *span)?;
612 let list_span = list_value.span();
613 let items_span = items.span();
614 let mut list = list_value.into_list()?;
615 list.extend(
616 items
617 .into_list()
618 .map_err(|_| ShellError::CannotSpreadAsList { span: items_span })?,
619 );
620 ctx.put_reg(*src_dst, Value::list(list, list_span).into_pipeline_data());
621 Ok(Continue)
622 }
623 Instruction::RecordInsert { src_dst, key, val } => {
624 let record_value = ctx.collect_reg(*src_dst, *span)?;
625 let key = ctx.collect_reg(*key, *span)?;
626 let val = ctx.collect_reg(*val, *span)?;
627 let record_span = record_value.span();
628 let mut record = record_value.into_record()?;
629
630 let key = key.coerce_into_string()?;
631 if let Some(old_value) = record.insert(&key, val) {
632 return Err(ShellError::ColumnDefinedTwice {
633 col_name: key,
634 second_use: *span,
635 first_use: old_value.span(),
636 });
637 }
638
639 ctx.put_reg(
640 *src_dst,
641 Value::record(record, record_span).into_pipeline_data(),
642 );
643 Ok(Continue)
644 }
645 Instruction::RecordSpread { src_dst, items } => {
646 let record_value = ctx.collect_reg(*src_dst, *span)?;
647 let items = ctx.collect_reg(*items, *span)?;
648 let record_span = record_value.span();
649 let items_span = items.span();
650 let mut record = record_value.into_record()?;
651 for (key, val) in items
653 .into_record()
654 .map_err(|_| ShellError::CannotSpreadAsRecord { span: items_span })?
655 {
656 if let Some(first_value) = record.insert(&key, val) {
657 return Err(ShellError::ColumnDefinedTwice {
658 col_name: key,
659 second_use: *span,
660 first_use: first_value.span(),
661 });
662 }
663 }
664 ctx.put_reg(
665 *src_dst,
666 Value::record(record, record_span).into_pipeline_data(),
667 );
668 Ok(Continue)
669 }
670 Instruction::Not { src_dst } => {
671 let bool = ctx.collect_reg(*src_dst, *span)?;
672 let negated = !bool.as_bool()?;
673 ctx.put_reg(
674 *src_dst,
675 Value::bool(negated, bool.span()).into_pipeline_data(),
676 );
677 Ok(Continue)
678 }
679 Instruction::BinaryOp { lhs_dst, op, rhs } => binary_op(ctx, *lhs_dst, op, *rhs, *span),
680 Instruction::FollowCellPath { src_dst, path } => {
681 let data = ctx.take_reg(*src_dst);
682 let path = ctx.take_reg(*path);
683 if let PipelineData::Value(Value::CellPath { val: path, .. }, _) = path {
684 let value = data.follow_cell_path(&path.members, *span)?;
685 ctx.put_reg(*src_dst, value.into_pipeline_data());
686 Ok(Continue)
687 } else if let PipelineData::Value(Value::Error { error, .. }, _) = path {
688 Err(*error)
689 } else {
690 Err(ShellError::TypeMismatch {
691 err_message: "expected cell path".into(),
692 span: path.span().unwrap_or(*span),
693 })
694 }
695 }
696 Instruction::CloneCellPath { dst, src, path } => {
697 let value = ctx.clone_reg_value(*src, *span)?;
698 let path = ctx.take_reg(*path);
699 if let PipelineData::Value(Value::CellPath { val: path, .. }, _) = path {
700 let value = value.follow_cell_path(&path.members)?;
701 ctx.put_reg(*dst, value.into_owned().into_pipeline_data());
702 Ok(Continue)
703 } else if let PipelineData::Value(Value::Error { error, .. }, _) = path {
704 Err(*error)
705 } else {
706 Err(ShellError::TypeMismatch {
707 err_message: "expected cell path".into(),
708 span: path.span().unwrap_or(*span),
709 })
710 }
711 }
712 Instruction::UpsertCellPath {
713 src_dst,
714 path,
715 new_value,
716 } => {
717 let data = ctx.take_reg(*src_dst);
718 let metadata = data.metadata();
719 let mut value = data.into_value(*span)?;
721 let path = ctx.take_reg(*path);
722 let new_value = ctx.collect_reg(*new_value, *span)?;
723 if let PipelineData::Value(Value::CellPath { val: path, .. }, _) = path {
724 value.upsert_data_at_cell_path(&path.members, new_value)?;
725 ctx.put_reg(*src_dst, value.into_pipeline_data_with_metadata(metadata));
726 Ok(Continue)
727 } else if let PipelineData::Value(Value::Error { error, .. }, _) = path {
728 Err(*error)
729 } else {
730 Err(ShellError::TypeMismatch {
731 err_message: "expected cell path".into(),
732 span: path.span().unwrap_or(*span),
733 })
734 }
735 }
736 Instruction::Jump { index } => Ok(Branch(*index)),
737 Instruction::BranchIf { cond, index } => {
738 let data = ctx.take_reg(*cond);
739 let data_span = data.span();
740 let val = match data {
741 PipelineData::Value(Value::Bool { val, .. }, _) => val,
742 PipelineData::Value(Value::Error { error, .. }, _) => {
743 return Err(*error);
744 }
745 _ => {
746 return Err(ShellError::TypeMismatch {
747 err_message: "expected bool".into(),
748 span: data_span.unwrap_or(*span),
749 });
750 }
751 };
752 if val {
753 Ok(Branch(*index))
754 } else {
755 Ok(Continue)
756 }
757 }
758 Instruction::BranchIfEmpty { src, index } => {
759 let is_empty = matches!(
760 ctx.borrow_reg(*src),
761 PipelineData::Empty | PipelineData::Value(Value::Nothing { .. }, _)
762 );
763
764 if is_empty {
765 Ok(Branch(*index))
766 } else {
767 Ok(Continue)
768 }
769 }
770 Instruction::Match {
771 pattern,
772 src,
773 index,
774 } => {
775 let value = ctx.clone_reg_value(*src, *span)?;
776 ctx.matches.clear();
777 if pattern.match_value(&value, &mut ctx.matches) {
778 for (var_id, match_value) in ctx.matches.drain(..) {
780 ctx.stack.add_var(var_id, match_value);
781 }
782 Ok(Branch(*index))
783 } else {
784 ctx.matches.clear();
786 Ok(Continue)
787 }
788 }
789 Instruction::CheckMatchGuard { src } => {
790 if matches!(
791 ctx.borrow_reg(*src),
792 PipelineData::Value(Value::Bool { .. }, _)
793 ) {
794 Ok(Continue)
795 } else {
796 Err(ShellError::MatchGuardNotBool { span: *span })
797 }
798 }
799 Instruction::Iterate {
800 dst,
801 stream,
802 end_index,
803 } => eval_iterate(ctx, *dst, *stream, *end_index),
804 Instruction::OnError { index } => {
805 ctx.stack.error_handlers.push(ErrorHandler {
806 handler_index: *index,
807 error_register: None,
808 });
809 Ok(Continue)
810 }
811 Instruction::OnErrorInto { index, dst } => {
812 ctx.stack.error_handlers.push(ErrorHandler {
813 handler_index: *index,
814 error_register: Some(*dst),
815 });
816 Ok(Continue)
817 }
818 Instruction::PopErrorHandler => {
819 ctx.stack.error_handlers.pop(ctx.error_handler_base);
820 Ok(Continue)
821 }
822 Instruction::ReturnEarly { src } => {
823 let val = ctx.collect_reg(*src, *span)?;
824 Err(ShellError::Return {
825 span: *span,
826 value: Box::new(val),
827 })
828 }
829 Instruction::Return { src } => Ok(Return(*src)),
830 }
831}
832
833fn load_literal(
835 ctx: &mut EvalContext<'_>,
836 dst: RegId,
837 lit: &Literal,
838 span: Span,
839) -> Result<InstructionResult, ShellError> {
840 let value = literal_value(ctx, lit, span)?;
841 ctx.put_reg(dst, PipelineData::Value(value, None));
842 Ok(InstructionResult::Continue)
843}
844
845fn literal_value(
846 ctx: &mut EvalContext<'_>,
847 lit: &Literal,
848 span: Span,
849) -> Result<Value, ShellError> {
850 Ok(match lit {
851 Literal::Bool(b) => Value::bool(*b, span),
852 Literal::Int(i) => Value::int(*i, span),
853 Literal::Float(f) => Value::float(*f, span),
854 Literal::Filesize(q) => Value::filesize(*q, span),
855 Literal::Duration(q) => Value::duration(*q, span),
856 Literal::Binary(bin) => Value::binary(&ctx.data[*bin], span),
857 Literal::Block(block_id) | Literal::RowCondition(block_id) | Literal::Closure(block_id) => {
858 let block = ctx.engine_state.get_block(*block_id);
859 let captures = block
860 .captures
861 .iter()
862 .map(|(var_id, span)| get_var(ctx, *var_id, *span).map(|val| (*var_id, val)))
863 .collect::<Result<Vec<_>, ShellError>>()?;
864 Value::closure(
865 Closure {
866 block_id: *block_id,
867 captures,
868 },
869 span,
870 )
871 }
872 Literal::Range {
873 start,
874 step,
875 end,
876 inclusion,
877 } => {
878 let start = ctx.collect_reg(*start, span)?;
879 let step = ctx.collect_reg(*step, span)?;
880 let end = ctx.collect_reg(*end, span)?;
881 let range = Range::new(start, step, end, *inclusion, span)?;
882 Value::range(range, span)
883 }
884 Literal::List { capacity } => Value::list(Vec::with_capacity(*capacity), span),
885 Literal::Record { capacity } => Value::record(Record::with_capacity(*capacity), span),
886 Literal::Filepath {
887 val: path,
888 no_expand,
889 } => {
890 let path = ctx.get_str(*path, span)?;
891 if *no_expand {
892 Value::string(path, span)
893 } else {
894 let path = expand_path(path, true);
895 Value::string(path.to_string_lossy(), span)
896 }
897 }
898 Literal::Directory {
899 val: path,
900 no_expand,
901 } => {
902 let path = ctx.get_str(*path, span)?;
903 if path == "-" {
904 Value::string("-", span)
905 } else if *no_expand {
906 Value::string(path, span)
907 } else {
908 let path = expand_path(path, true);
909 Value::string(path.to_string_lossy(), span)
910 }
911 }
912 Literal::GlobPattern { val, no_expand } => {
913 Value::glob(ctx.get_str(*val, span)?, *no_expand, span)
914 }
915 Literal::String(s) => Value::string(ctx.get_str(*s, span)?, span),
916 Literal::RawString(s) => Value::string(ctx.get_str(*s, span)?, span),
917 Literal::CellPath(path) => Value::cell_path(CellPath::clone(path), span),
918 Literal::Date(dt) => Value::date(**dt, span),
919 Literal::Nothing => Value::nothing(span),
920 })
921}
922
923fn binary_op(
924 ctx: &mut EvalContext<'_>,
925 lhs_dst: RegId,
926 op: &Operator,
927 rhs: RegId,
928 span: Span,
929) -> Result<InstructionResult, ShellError> {
930 let lhs_val = ctx.collect_reg(lhs_dst, span)?;
931 let rhs_val = ctx.collect_reg(rhs, span)?;
932
933 if let Value::Error { error, .. } = lhs_val {
935 return Err(*error);
936 }
937 if let Value::Error { error, .. } = rhs_val {
938 return Err(*error);
939 }
940
941 let op_span = span;
944
945 let result = match op {
946 Operator::Comparison(cmp) => match cmp {
947 Comparison::Equal => lhs_val.eq(op_span, &rhs_val, span)?,
948 Comparison::NotEqual => lhs_val.ne(op_span, &rhs_val, span)?,
949 Comparison::LessThan => lhs_val.lt(op_span, &rhs_val, span)?,
950 Comparison::GreaterThan => lhs_val.gt(op_span, &rhs_val, span)?,
951 Comparison::LessThanOrEqual => lhs_val.lte(op_span, &rhs_val, span)?,
952 Comparison::GreaterThanOrEqual => lhs_val.gte(op_span, &rhs_val, span)?,
953 Comparison::RegexMatch => {
954 lhs_val.regex_match(ctx.engine_state, op_span, &rhs_val, false, span)?
955 }
956 Comparison::NotRegexMatch => {
957 lhs_val.regex_match(ctx.engine_state, op_span, &rhs_val, true, span)?
958 }
959 Comparison::In => lhs_val.r#in(op_span, &rhs_val, span)?,
960 Comparison::NotIn => lhs_val.not_in(op_span, &rhs_val, span)?,
961 Comparison::Has => lhs_val.has(op_span, &rhs_val, span)?,
962 Comparison::NotHas => lhs_val.not_has(op_span, &rhs_val, span)?,
963 Comparison::StartsWith => lhs_val.starts_with(op_span, &rhs_val, span)?,
964 Comparison::EndsWith => lhs_val.ends_with(op_span, &rhs_val, span)?,
965 },
966 Operator::Math(mat) => match mat {
967 Math::Add => lhs_val.add(op_span, &rhs_val, span)?,
968 Math::Subtract => lhs_val.sub(op_span, &rhs_val, span)?,
969 Math::Multiply => lhs_val.mul(op_span, &rhs_val, span)?,
970 Math::Divide => lhs_val.div(op_span, &rhs_val, span)?,
971 Math::FloorDivide => lhs_val.floor_div(op_span, &rhs_val, span)?,
972 Math::Modulo => lhs_val.modulo(op_span, &rhs_val, span)?,
973 Math::Pow => lhs_val.pow(op_span, &rhs_val, span)?,
974 Math::Concatenate => lhs_val.concat(op_span, &rhs_val, span)?,
975 },
976 Operator::Boolean(bl) => match bl {
977 Boolean::Or => lhs_val.or(op_span, &rhs_val, span)?,
978 Boolean::Xor => lhs_val.xor(op_span, &rhs_val, span)?,
979 Boolean::And => lhs_val.and(op_span, &rhs_val, span)?,
980 },
981 Operator::Bits(bit) => match bit {
982 Bits::BitOr => lhs_val.bit_or(op_span, &rhs_val, span)?,
983 Bits::BitXor => lhs_val.bit_xor(op_span, &rhs_val, span)?,
984 Bits::BitAnd => lhs_val.bit_and(op_span, &rhs_val, span)?,
985 Bits::ShiftLeft => lhs_val.bit_shl(op_span, &rhs_val, span)?,
986 Bits::ShiftRight => lhs_val.bit_shr(op_span, &rhs_val, span)?,
987 },
988 Operator::Assignment(_asg) => {
989 return Err(ShellError::IrEvalError {
990 msg: "can't eval assignment with the `binary-op` instruction".into(),
991 span: Some(span),
992 });
993 }
994 };
995
996 ctx.put_reg(lhs_dst, PipelineData::Value(result, None));
997
998 Ok(InstructionResult::Continue)
999}
1000
1001fn eval_call<D: DebugContext>(
1003 ctx: &mut EvalContext<'_>,
1004 decl_id: DeclId,
1005 head: Span,
1006 input: PipelineData,
1007) -> Result<PipelineData, ShellError> {
1008 let EvalContext {
1009 engine_state,
1010 stack: caller_stack,
1011 args_base,
1012 redirect_out,
1013 redirect_err,
1014 ..
1015 } = ctx;
1016
1017 let args_len = caller_stack.arguments.get_len(*args_base);
1018 let decl = engine_state.get_decl(decl_id);
1019
1020 let mut caller_stack = caller_stack.push_redirection(redirect_out.take(), redirect_err.take());
1022
1023 let result = (|| {
1024 if let Some(block_id) = decl.block_id() {
1025 let block = engine_state.get_block(block_id);
1027
1028 check_input_types(&input, &block.signature, head)?;
1030
1031 let mut callee_stack = caller_stack.gather_captures(engine_state, &block.captures);
1033
1034 gather_arguments(
1035 engine_state,
1036 block,
1037 &mut caller_stack,
1038 &mut callee_stack,
1039 *args_base,
1040 args_len,
1041 head,
1042 )?;
1043
1044 callee_stack.recursion_count += 1;
1047
1048 let result =
1049 eval_block_with_early_return::<D>(engine_state, &mut callee_stack, block, input);
1050
1051 if block.redirect_env {
1053 redirect_env(engine_state, &mut caller_stack, &callee_stack);
1054 }
1055
1056 result
1057 } else {
1058 check_input_types(&input, &decl.signature(), head)?;
1059 let span = Span::merge_many(
1061 std::iter::once(head).chain(
1062 caller_stack
1063 .arguments
1064 .get_args(*args_base, args_len)
1065 .iter()
1066 .flat_map(|arg| arg.span()),
1067 ),
1068 );
1069
1070 let call = Call {
1071 decl_id,
1072 head,
1073 span,
1074 args_base: *args_base,
1075 args_len,
1076 };
1077
1078 decl.run(engine_state, &mut caller_stack, &(&call).into(), input)
1080 }
1081 })();
1082
1083 drop(caller_stack);
1084
1085 ctx.stack.arguments.leave_frame(ctx.args_base);
1087 ctx.redirect_out = None;
1088 ctx.redirect_err = None;
1089
1090 result
1091}
1092
1093fn find_named_var_id(
1094 sig: &Signature,
1095 name: &[u8],
1096 short: &[u8],
1097 span: Span,
1098) -> Result<VarId, ShellError> {
1099 sig.named
1100 .iter()
1101 .find(|n| {
1102 if !n.long.is_empty() {
1103 n.long.as_bytes() == name
1104 } else {
1105 n.short
1107 .is_some_and(|s| s.encode_utf8(&mut [0; 4]).as_bytes() == short)
1108 }
1109 })
1110 .ok_or_else(|| ShellError::IrEvalError {
1111 msg: format!(
1112 "block does not have an argument named `{}`",
1113 String::from_utf8_lossy(name)
1114 ),
1115 span: Some(span),
1116 })
1117 .and_then(|flag| expect_named_var_id(flag, span))
1118}
1119
1120fn expect_named_var_id(arg: &Flag, span: Span) -> Result<VarId, ShellError> {
1121 arg.var_id.ok_or_else(|| ShellError::IrEvalError {
1122 msg: format!(
1123 "block signature is missing var id for named arg `{}`",
1124 arg.long
1125 ),
1126 span: Some(span),
1127 })
1128}
1129
1130fn expect_positional_var_id(arg: &PositionalArg, span: Span) -> Result<VarId, ShellError> {
1131 arg.var_id.ok_or_else(|| ShellError::IrEvalError {
1132 msg: format!(
1133 "block signature is missing var id for positional arg `{}`",
1134 arg.name
1135 ),
1136 span: Some(span),
1137 })
1138}
1139
1140fn gather_arguments(
1142 engine_state: &EngineState,
1143 block: &Block,
1144 caller_stack: &mut Stack,
1145 callee_stack: &mut Stack,
1146 args_base: usize,
1147 args_len: usize,
1148 call_head: Span,
1149) -> Result<(), ShellError> {
1150 let mut positional_iter = block
1151 .signature
1152 .required_positional
1153 .iter()
1154 .map(|p| (p, true))
1155 .chain(
1156 block
1157 .signature
1158 .optional_positional
1159 .iter()
1160 .map(|p| (p, false)),
1161 );
1162
1163 let mut rest = vec![];
1165 let mut rest_span: Option<Span> = None;
1166
1167 let mut always_spread = false;
1169
1170 for arg in caller_stack.arguments.drain_args(args_base, args_len) {
1171 match arg {
1172 Argument::Positional { span, val, .. } => {
1173 let next = (!always_spread).then(|| positional_iter.next()).flatten();
1175 if let Some((positional_arg, required)) = next {
1176 let var_id = expect_positional_var_id(positional_arg, span)?;
1177 if required {
1178 let variable = engine_state.get_var(var_id);
1181 check_type(&val, &variable.ty)?;
1182 }
1183 callee_stack.add_var(var_id, val);
1184 } else {
1185 rest_span = Some(rest_span.map_or(val.span(), |s| s.append(val.span())));
1186 rest.push(val);
1187 }
1188 }
1189 Argument::Spread {
1190 vals,
1191 span: spread_span,
1192 ..
1193 } => {
1194 if let Value::List { vals, .. } = vals {
1195 rest.extend(vals);
1196 rest_span = Some(rest_span.map_or(spread_span, |s| s.append(spread_span)));
1198 always_spread = true;
1200 } else if let Value::Error { error, .. } = vals {
1201 return Err(*error);
1202 } else {
1203 return Err(ShellError::CannotSpreadAsList { span: vals.span() });
1204 }
1205 }
1206 Argument::Flag {
1207 data,
1208 name,
1209 short,
1210 span,
1211 } => {
1212 let var_id = find_named_var_id(&block.signature, &data[name], &data[short], span)?;
1213 callee_stack.add_var(var_id, Value::bool(true, span))
1214 }
1215 Argument::Named {
1216 data,
1217 name,
1218 short,
1219 span,
1220 val,
1221 ..
1222 } => {
1223 let var_id = find_named_var_id(&block.signature, &data[name], &data[short], span)?;
1224 callee_stack.add_var(var_id, val)
1225 }
1226 Argument::ParserInfo { .. } => (),
1227 }
1228 }
1229
1230 if let Some(rest_arg) = &block.signature.rest_positional {
1232 let rest_span = rest_span.unwrap_or(call_head);
1233 let var_id = expect_positional_var_id(rest_arg, rest_span)?;
1234 callee_stack.add_var(var_id, Value::list(rest, rest_span));
1235 }
1236
1237 for (positional_arg, _) in positional_iter {
1239 let var_id = expect_positional_var_id(positional_arg, call_head)?;
1240 callee_stack.add_var(
1241 var_id,
1242 positional_arg
1243 .default_value
1244 .clone()
1245 .unwrap_or(Value::nothing(call_head)),
1246 );
1247 }
1248
1249 for named_arg in &block.signature.named {
1250 if let Some(var_id) = named_arg.var_id {
1251 if !callee_stack.vars.iter().any(|(id, _)| *id == var_id) {
1255 let val = if named_arg.arg.is_none() {
1256 Value::bool(false, call_head)
1257 } else if let Some(value) = &named_arg.default_value {
1258 value.clone()
1259 } else {
1260 Value::nothing(call_head)
1261 };
1262 callee_stack.add_var(var_id, val);
1263 }
1264 }
1265 }
1266
1267 Ok(())
1268}
1269
1270fn check_type(val: &Value, ty: &Type) -> Result<(), ShellError> {
1272 match val {
1273 Value::Error { error, .. } => Err(*error.clone()),
1274 _ if val.is_subtype_of(ty) => Ok(()),
1275 _ => Err(ShellError::CantConvert {
1276 to_type: ty.to_string(),
1277 from_type: val.get_type().to_string(),
1278 span: val.span(),
1279 help: None,
1280 }),
1281 }
1282}
1283
1284fn check_input_types(
1286 input: &PipelineData,
1287 signature: &Signature,
1288 head: Span,
1289) -> Result<(), ShellError> {
1290 let io_types = &signature.input_output_types;
1291
1292 if io_types.is_empty() {
1294 return Ok(());
1295 }
1296
1297 if io_types.iter().all(|(intype, _)| intype == &Type::Nothing) {
1299 return Ok(());
1300 }
1301
1302 match input {
1303 PipelineData::Value(Value::Error { error, .. }, ..) => return Err(*error.clone()),
1305 PipelineData::Value(Value::Custom { .. }, ..) => return Ok(()),
1307 _ => (),
1308 }
1309
1310 if io_types
1312 .iter()
1313 .any(|(command_type, _)| input.is_subtype_of(command_type))
1314 {
1315 return Ok(());
1316 }
1317
1318 let mut input_types = io_types
1319 .iter()
1320 .map(|(input, _)| input.to_string())
1321 .collect::<Vec<String>>();
1322
1323 let expected_string = match input_types.len() {
1324 0 => {
1325 return Err(ShellError::NushellFailed {
1326 msg: "Command input type strings is empty, despite being non-zero earlier"
1327 .to_string(),
1328 });
1329 }
1330 1 => input_types.swap_remove(0),
1331 2 => input_types.join(" and "),
1332 _ => {
1333 input_types
1334 .last_mut()
1335 .expect("Vector with length >2 has no elements")
1336 .insert_str(0, "and ");
1337 input_types.join(", ")
1338 }
1339 };
1340
1341 match input {
1342 PipelineData::Empty => Err(ShellError::PipelineEmpty { dst_span: head }),
1343 _ => Err(ShellError::OnlySupportsThisInputType {
1344 exp_input_type: expected_string,
1345 wrong_type: input.get_type().to_string(),
1346 dst_span: head,
1347 src_span: input.span().unwrap_or(Span::unknown()),
1348 }),
1349 }
1350}
1351
1352fn get_var(ctx: &EvalContext<'_>, var_id: VarId, span: Span) -> Result<Value, ShellError> {
1354 match var_id {
1355 ENV_VARIABLE_ID => {
1357 let env_vars = ctx.stack.get_env_vars(ctx.engine_state);
1358 let env_columns = env_vars.keys();
1359 let env_values = env_vars.values();
1360
1361 let mut pairs = env_columns
1362 .map(|x| x.to_string())
1363 .zip(env_values.cloned())
1364 .collect::<Vec<(String, Value)>>();
1365
1366 pairs.sort_by(|a, b| a.0.cmp(&b.0));
1367
1368 Ok(Value::record(pairs.into_iter().collect(), span))
1369 }
1370 _ => ctx.stack.get_var(var_id, span).or_else(|err| {
1371 if let Some(const_val) = ctx.engine_state.get_constant(var_id).cloned() {
1373 Ok(const_val.with_span(span))
1374 } else {
1375 Err(err)
1376 }
1377 }),
1378 }
1379}
1380
1381fn get_env_var_case_insensitive<'a>(ctx: &'a mut EvalContext<'_>, key: &str) -> Option<&'a Value> {
1383 for overlays in ctx
1385 .stack
1386 .env_vars
1387 .iter()
1388 .rev()
1389 .chain(std::iter::once(&ctx.engine_state.env_vars))
1390 {
1391 for overlay_name in ctx.stack.active_overlays.iter().rev() {
1393 let Some(map) = overlays.get(overlay_name) else {
1394 continue;
1396 };
1397 let hidden = ctx.stack.env_hidden.get(overlay_name);
1398 let is_hidden = |key: &str| hidden.is_some_and(|hidden| hidden.contains(key));
1399
1400 if let Some(val) = map
1401 .get(key)
1403 .filter(|_| !is_hidden(key))
1405 .or_else(|| {
1406 map.iter().find_map(|(k, v)| {
1408 (k.eq_ignore_case(key) && !is_hidden(k)).then_some(v)
1410 })
1411 })
1412 {
1413 return Some(val);
1414 }
1415 }
1416 }
1417 None
1419}
1420
1421fn get_env_var_name_case_insensitive<'a>(ctx: &mut EvalContext<'_>, key: &'a str) -> Cow<'a, str> {
1425 ctx.stack
1427 .env_vars
1428 .iter()
1429 .rev()
1430 .chain(std::iter::once(&ctx.engine_state.env_vars))
1431 .flat_map(|overlays| {
1432 ctx.stack
1434 .active_overlays
1435 .iter()
1436 .rev()
1437 .filter_map(|name| overlays.get(name))
1438 })
1439 .find_map(|map| {
1440 if map.contains_key(key) {
1442 Some(Cow::Borrowed(key))
1443 } else {
1444 map.keys().find(|k| k.eq_ignore_case(key)).map(|k| {
1445 Cow::Owned(k.to_owned())
1447 })
1448 }
1449 })
1450 .unwrap_or(Cow::Borrowed(key))
1452}
1453
1454fn collect(data: PipelineData, fallback_span: Span) -> Result<PipelineData, ShellError> {
1458 let span = data.span().unwrap_or(fallback_span);
1459 let metadata = match data.metadata() {
1460 Some(PipelineMetadata {
1463 data_source: DataSource::FilePath(_),
1464 content_type: None,
1465 }) => None,
1466 other => other,
1467 };
1468 let value = data.into_value(span)?;
1469 Ok(PipelineData::Value(value, metadata))
1470}
1471
1472fn drain(ctx: &mut EvalContext<'_>, data: PipelineData) -> Result<InstructionResult, ShellError> {
1474 use self::InstructionResult::*;
1475 match data {
1476 PipelineData::ByteStream(stream, ..) => {
1477 let span = stream.span();
1478 let callback_spans = stream.get_caller_spans().clone();
1479 if let Err(mut err) = stream.drain() {
1480 ctx.stack.set_last_error(&err);
1481 if callback_spans.is_empty() {
1482 return Err(err);
1483 } else {
1484 for s in callback_spans {
1485 err = ShellError::EvalBlockWithInput {
1486 span: s,
1487 sources: vec![err],
1488 }
1489 }
1490 return Err(err);
1491 }
1492 } else {
1493 ctx.stack.set_last_exit_code(0, span);
1494 }
1495 }
1496 PipelineData::ListStream(stream, ..) => {
1497 let callback_spans = stream.get_caller_spans().clone();
1498 if let Err(mut err) = stream.drain() {
1499 if callback_spans.is_empty() {
1500 return Err(err);
1501 } else {
1502 for s in callback_spans {
1503 err = ShellError::EvalBlockWithInput {
1504 span: s,
1505 sources: vec![err],
1506 }
1507 }
1508 return Err(err);
1509 }
1510 }
1511 }
1512 PipelineData::Value(..) | PipelineData::Empty => {}
1513 }
1514 Ok(Continue)
1515}
1516
1517enum RedirectionStream {
1518 Out,
1519 Err,
1520}
1521
1522fn open_file(ctx: &EvalContext<'_>, path: &Value, append: bool) -> Result<Arc<File>, ShellError> {
1524 let path_expanded =
1525 expand_path_with(path.as_str()?, ctx.engine_state.cwd(Some(ctx.stack))?, true);
1526 let mut options = File::options();
1527 if append {
1528 options.append(true);
1529 } else {
1530 options.write(true).truncate(true);
1531 }
1532 let file = options
1533 .create(true)
1534 .open(&path_expanded)
1535 .map_err(|err| IoError::new(err, path.span(), path_expanded))?;
1536 Ok(Arc::new(file))
1537}
1538
1539fn eval_redirection(
1541 ctx: &mut EvalContext<'_>,
1542 mode: &RedirectMode,
1543 span: Span,
1544 which: RedirectionStream,
1545) -> Result<Option<Redirection>, ShellError> {
1546 match mode {
1547 RedirectMode::Pipe => Ok(Some(Redirection::Pipe(OutDest::Pipe))),
1548 RedirectMode::PipeSeparate => Ok(Some(Redirection::Pipe(OutDest::PipeSeparate))),
1549 RedirectMode::Value => Ok(Some(Redirection::Pipe(OutDest::Value))),
1550 RedirectMode::Null => Ok(Some(Redirection::Pipe(OutDest::Null))),
1551 RedirectMode::Inherit => Ok(Some(Redirection::Pipe(OutDest::Inherit))),
1552 RedirectMode::Print => Ok(Some(Redirection::Pipe(OutDest::Print))),
1553 RedirectMode::File { file_num } => {
1554 let file = ctx
1555 .files
1556 .get(*file_num as usize)
1557 .cloned()
1558 .flatten()
1559 .ok_or_else(|| ShellError::IrEvalError {
1560 msg: format!("Tried to redirect to file #{file_num}, but it is not open"),
1561 span: Some(span),
1562 })?;
1563 Ok(Some(Redirection::File(file)))
1564 }
1565 RedirectMode::Caller => Ok(match which {
1566 RedirectionStream::Out => ctx.stack.pipe_stdout().cloned().map(Redirection::Pipe),
1567 RedirectionStream::Err => ctx.stack.pipe_stderr().cloned().map(Redirection::Pipe),
1568 }),
1569 }
1570}
1571
1572fn eval_iterate(
1574 ctx: &mut EvalContext<'_>,
1575 dst: RegId,
1576 stream: RegId,
1577 end_index: usize,
1578) -> Result<InstructionResult, ShellError> {
1579 let mut data = ctx.take_reg(stream);
1580 if let PipelineData::ListStream(list_stream, _) = &mut data {
1581 if let Some(val) = list_stream.next_value() {
1583 ctx.put_reg(dst, val.into_pipeline_data());
1584 ctx.put_reg(stream, data); Ok(InstructionResult::Continue)
1586 } else {
1587 ctx.put_reg(dst, PipelineData::Empty);
1588 Ok(InstructionResult::Branch(end_index))
1589 }
1590 } else {
1591 let metadata = data.metadata();
1594 let span = data.span().unwrap_or(Span::unknown());
1595 ctx.put_reg(
1596 stream,
1597 PipelineData::ListStream(
1598 ListStream::new(data.into_iter(), span, Signals::EMPTY),
1599 metadata,
1600 ),
1601 );
1602 eval_iterate(ctx, dst, stream, end_index)
1603 }
1604}
1605
1606fn redirect_env(engine_state: &EngineState, caller_stack: &mut Stack, callee_stack: &Stack) {
1608 let caller_env_vars = caller_stack.get_env_var_names(engine_state);
1611
1612 for var in caller_env_vars.iter() {
1615 if !callee_stack.has_env_var(engine_state, var) {
1616 caller_stack.remove_env_var(engine_state, var);
1617 }
1618 }
1619
1620 for (var, value) in callee_stack.get_stack_env_vars() {
1622 caller_stack.add_env_var(var, value);
1623 }
1624
1625 caller_stack.config.clone_from(&callee_stack.config);
1627}