1use std::{borrow::Cow, fs::File, sync::Arc};
2
3use nu_path::{expand_path_with, AbsolutePathBuf};
4use nu_protocol::{
5 ast::{Bits, Block, Boolean, CellPath, Comparison, Math, Operator},
6 debugger::DebugContext,
7 engine::{
8 Argument, Closure, EngineState, ErrorHandler, Matcher, Redirection, Stack, StateWorkingSet,
9 },
10 ir::{Call, DataSlice, Instruction, IrAstRef, IrBlock, Literal, RedirectMode},
11 shell_error::io::IoError,
12 DataSource, DeclId, Flag, IntoPipelineData, IntoSpanned, ListStream, OutDest, PipelineData,
13 PipelineMetadata, PositionalArg, Range, Record, RegId, ShellError, Signals, Signature, Span,
14 Spanned, Type, Value, VarId, ENV_VARIABLE_ID,
15};
16use nu_utils::IgnoreCaseExt;
17
18use crate::{
19 convert_env_vars, eval::is_automatic_env_var, eval_block_with_early_return, ENV_CONVERSIONS,
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 match instruction {
298 Instruction::Unreachable => Err(ShellError::IrEvalError {
299 msg: "Reached unreachable code".into(),
300 span: Some(*span),
301 }),
302 Instruction::LoadLiteral { dst, lit } => load_literal(ctx, *dst, lit, *span),
303 Instruction::LoadValue { dst, val } => {
304 ctx.put_reg(*dst, Value::clone(val).into_pipeline_data());
305 Ok(Continue)
306 }
307 Instruction::Move { dst, src } => {
308 let val = ctx.take_reg(*src);
309 ctx.put_reg(*dst, val);
310 Ok(Continue)
311 }
312 Instruction::Clone { dst, src } => {
313 let data = ctx.clone_reg(*src, *span)?;
314 ctx.put_reg(*dst, data);
315 Ok(Continue)
316 }
317 Instruction::Collect { src_dst } => {
318 let data = ctx.take_reg(*src_dst);
319 let value = collect(data, *span)?;
320 ctx.put_reg(*src_dst, value);
321 Ok(Continue)
322 }
323 Instruction::Span { src_dst } => {
324 let data = ctx.take_reg(*src_dst);
325 let spanned = data.with_span(*span);
326 ctx.put_reg(*src_dst, spanned);
327 Ok(Continue)
328 }
329 Instruction::Drop { src } => {
330 ctx.take_reg(*src);
331 Ok(Continue)
332 }
333 Instruction::Drain { src } => {
334 let data = ctx.take_reg(*src);
335 drain(ctx, data)
336 }
337 Instruction::DrainIfEnd { src } => {
338 let data = ctx.take_reg(*src);
339 let res = {
340 let stack = &mut ctx
341 .stack
342 .push_redirection(ctx.redirect_out.clone(), ctx.redirect_err.clone());
343 data.drain_to_out_dests(ctx.engine_state, stack)?
344 };
345 ctx.put_reg(*src, res);
346 Ok(Continue)
347 }
348 Instruction::LoadVariable { dst, var_id } => {
349 let value = get_var(ctx, *var_id, *span)?;
350 ctx.put_reg(*dst, value.into_pipeline_data());
351 Ok(Continue)
352 }
353 Instruction::StoreVariable { var_id, src } => {
354 let value = ctx.collect_reg(*src, *span)?;
355 ctx.stack.add_var(*var_id, value);
356 Ok(Continue)
357 }
358 Instruction::DropVariable { var_id } => {
359 ctx.stack.remove_var(*var_id);
360 Ok(Continue)
361 }
362 Instruction::LoadEnv { dst, key } => {
363 let key = ctx.get_str(*key, *span)?;
364 if let Some(value) = get_env_var_case_insensitive(ctx, key) {
365 let new_value = value.clone().into_pipeline_data();
366 ctx.put_reg(*dst, new_value);
367 Ok(Continue)
368 } else {
369 Err(ShellError::CantFindColumn {
372 col_name: key.into(),
373 span: Some(*span),
374 src_span: *span,
375 })
376 }
377 }
378 Instruction::LoadEnvOpt { dst, key } => {
379 let key = ctx.get_str(*key, *span)?;
380 let value = get_env_var_case_insensitive(ctx, key)
381 .cloned()
382 .unwrap_or(Value::nothing(*span));
383 ctx.put_reg(*dst, value.into_pipeline_data());
384 Ok(Continue)
385 }
386 Instruction::StoreEnv { key, src } => {
387 let key = ctx.get_str(*key, *span)?;
388 let value = ctx.collect_reg(*src, *span)?;
389
390 let key = get_env_var_name_case_insensitive(ctx, key);
391
392 if !is_automatic_env_var(&key) {
393 let is_config = key == "config";
394 let update_conversions = key == ENV_CONVERSIONS;
395
396 ctx.stack.add_env_var(key.into_owned(), value.clone());
397
398 if is_config {
399 ctx.stack.update_config(ctx.engine_state)?;
400 }
401 if update_conversions {
402 convert_env_vars(ctx.stack, ctx.engine_state, &value)?;
403 }
404 Ok(Continue)
405 } else {
406 Err(ShellError::AutomaticEnvVarSetManually {
407 envvar_name: key.into(),
408 span: *span,
409 })
410 }
411 }
412 Instruction::PushPositional { src } => {
413 let val = ctx.collect_reg(*src, *span)?.with_span(*span);
414 ctx.stack.arguments.push(Argument::Positional {
415 span: *span,
416 val,
417 ast: ast.clone().map(|ast_ref| ast_ref.0),
418 });
419 Ok(Continue)
420 }
421 Instruction::AppendRest { src } => {
422 let vals = ctx.collect_reg(*src, *span)?.with_span(*span);
423 ctx.stack.arguments.push(Argument::Spread {
424 span: *span,
425 vals,
426 ast: ast.clone().map(|ast_ref| ast_ref.0),
427 });
428 Ok(Continue)
429 }
430 Instruction::PushFlag { name } => {
431 let data = ctx.data.clone();
432 ctx.stack.arguments.push(Argument::Flag {
433 data,
434 name: *name,
435 short: DataSlice::empty(),
436 span: *span,
437 });
438 Ok(Continue)
439 }
440 Instruction::PushShortFlag { short } => {
441 let data = ctx.data.clone();
442 ctx.stack.arguments.push(Argument::Flag {
443 data,
444 name: DataSlice::empty(),
445 short: *short,
446 span: *span,
447 });
448 Ok(Continue)
449 }
450 Instruction::PushNamed { name, src } => {
451 let val = ctx.collect_reg(*src, *span)?.with_span(*span);
452 let data = ctx.data.clone();
453 ctx.stack.arguments.push(Argument::Named {
454 data,
455 name: *name,
456 short: DataSlice::empty(),
457 span: *span,
458 val,
459 ast: ast.clone().map(|ast_ref| ast_ref.0),
460 });
461 Ok(Continue)
462 }
463 Instruction::PushShortNamed { short, src } => {
464 let val = ctx.collect_reg(*src, *span)?.with_span(*span);
465 let data = ctx.data.clone();
466 ctx.stack.arguments.push(Argument::Named {
467 data,
468 name: DataSlice::empty(),
469 short: *short,
470 span: *span,
471 val,
472 ast: ast.clone().map(|ast_ref| ast_ref.0),
473 });
474 Ok(Continue)
475 }
476 Instruction::PushParserInfo { name, info } => {
477 let data = ctx.data.clone();
478 ctx.stack.arguments.push(Argument::ParserInfo {
479 data,
480 name: *name,
481 info: info.clone(),
482 });
483 Ok(Continue)
484 }
485 Instruction::RedirectOut { mode } => {
486 ctx.redirect_out = eval_redirection(ctx, mode, *span, RedirectionStream::Out)?;
487 Ok(Continue)
488 }
489 Instruction::RedirectErr { mode } => {
490 ctx.redirect_err = eval_redirection(ctx, mode, *span, RedirectionStream::Err)?;
491 Ok(Continue)
492 }
493 Instruction::CheckErrRedirected { src } => match ctx.borrow_reg(*src) {
494 #[cfg(feature = "os")]
495 PipelineData::ByteStream(stream, _)
496 if matches!(stream.source(), nu_protocol::ByteStreamSource::Child(_)) =>
497 {
498 Ok(Continue)
499 }
500 _ => Err(ShellError::GenericError {
501 error: "Can't redirect stderr of internal command output".into(),
502 msg: "piping stderr only works on external commands".into(),
503 span: Some(*span),
504 help: None,
505 inner: vec![],
506 }),
507 },
508 Instruction::OpenFile {
509 file_num,
510 path,
511 append,
512 } => {
513 let path = ctx.collect_reg(*path, *span)?;
514 let file = open_file(ctx, &path, *append)?;
515 ctx.files[*file_num as usize] = Some(file);
516 Ok(Continue)
517 }
518 Instruction::WriteFile { file_num, src } => {
519 let src = ctx.take_reg(*src);
520 let file = ctx
521 .files
522 .get(*file_num as usize)
523 .cloned()
524 .flatten()
525 .ok_or_else(|| ShellError::IrEvalError {
526 msg: format!("Tried to write to file #{file_num}, but it is not open"),
527 span: Some(*span),
528 })?;
529 let is_external = if let PipelineData::ByteStream(stream, ..) = &src {
530 stream.source().is_external()
531 } else {
532 false
533 };
534 if let Err(err) = src.write_to(file.as_ref()) {
535 if is_external {
536 ctx.stack.set_last_error(&err);
537 }
538 Err(err)?
539 } else {
540 Ok(Continue)
541 }
542 }
543 Instruction::CloseFile { file_num } => {
544 if ctx.files[*file_num as usize].take().is_some() {
545 Ok(Continue)
546 } else {
547 Err(ShellError::IrEvalError {
548 msg: format!("Tried to close file #{file_num}, but it is not open"),
549 span: Some(*span),
550 })
551 }
552 }
553 Instruction::Call { decl_id, src_dst } => {
554 let input = ctx.take_reg(*src_dst);
555 let mut result = eval_call::<D>(ctx, *decl_id, *span, input)?;
556 if need_backtrace {
557 match &mut result {
558 PipelineData::ByteStream(s, ..) => s.push_caller_span(*span),
559 PipelineData::ListStream(s, ..) => s.push_caller_span(*span),
560 _ => (),
561 };
562 }
563 ctx.put_reg(*src_dst, result);
564 Ok(Continue)
565 }
566 Instruction::StringAppend { src_dst, val } => {
567 let string_value = ctx.collect_reg(*src_dst, *span)?;
568 let operand_value = ctx.collect_reg(*val, *span)?;
569 let string_span = string_value.span();
570
571 let mut string = string_value.into_string()?;
572 let operand = if let Value::String { val, .. } = operand_value {
573 val
575 } else {
576 operand_value.to_expanded_string(", ", ctx.engine_state.get_config())
577 };
578 string.push_str(&operand);
579
580 let new_string_value = Value::string(string, string_span);
581 ctx.put_reg(*src_dst, new_string_value.into_pipeline_data());
582 Ok(Continue)
583 }
584 Instruction::GlobFrom { src_dst, no_expand } => {
585 let string_value = ctx.collect_reg(*src_dst, *span)?;
586 let glob_value = if matches!(string_value, Value::Glob { .. }) {
587 string_value
589 } else {
590 let string = string_value.into_string()?;
592 Value::glob(string, *no_expand, *span)
593 };
594 ctx.put_reg(*src_dst, glob_value.into_pipeline_data());
595 Ok(Continue)
596 }
597 Instruction::ListPush { src_dst, item } => {
598 let list_value = ctx.collect_reg(*src_dst, *span)?;
599 let item = ctx.collect_reg(*item, *span)?;
600 let list_span = list_value.span();
601 let mut list = list_value.into_list()?;
602 list.push(item);
603 ctx.put_reg(*src_dst, Value::list(list, list_span).into_pipeline_data());
604 Ok(Continue)
605 }
606 Instruction::ListSpread { src_dst, items } => {
607 let list_value = ctx.collect_reg(*src_dst, *span)?;
608 let items = ctx.collect_reg(*items, *span)?;
609 let list_span = list_value.span();
610 let items_span = items.span();
611 let mut list = list_value.into_list()?;
612 list.extend(
613 items
614 .into_list()
615 .map_err(|_| ShellError::CannotSpreadAsList { span: items_span })?,
616 );
617 ctx.put_reg(*src_dst, Value::list(list, list_span).into_pipeline_data());
618 Ok(Continue)
619 }
620 Instruction::RecordInsert { src_dst, key, val } => {
621 let record_value = ctx.collect_reg(*src_dst, *span)?;
622 let key = ctx.collect_reg(*key, *span)?;
623 let val = ctx.collect_reg(*val, *span)?;
624 let record_span = record_value.span();
625 let mut record = record_value.into_record()?;
626
627 let key = key.coerce_into_string()?;
628 if let Some(old_value) = record.insert(&key, val) {
629 return Err(ShellError::ColumnDefinedTwice {
630 col_name: key,
631 second_use: *span,
632 first_use: old_value.span(),
633 });
634 }
635
636 ctx.put_reg(
637 *src_dst,
638 Value::record(record, record_span).into_pipeline_data(),
639 );
640 Ok(Continue)
641 }
642 Instruction::RecordSpread { src_dst, items } => {
643 let record_value = ctx.collect_reg(*src_dst, *span)?;
644 let items = ctx.collect_reg(*items, *span)?;
645 let record_span = record_value.span();
646 let items_span = items.span();
647 let mut record = record_value.into_record()?;
648 for (key, val) in items
650 .into_record()
651 .map_err(|_| ShellError::CannotSpreadAsRecord { span: items_span })?
652 {
653 if let Some(first_value) = record.insert(&key, val) {
654 return Err(ShellError::ColumnDefinedTwice {
655 col_name: key,
656 second_use: *span,
657 first_use: first_value.span(),
658 });
659 }
660 }
661 ctx.put_reg(
662 *src_dst,
663 Value::record(record, record_span).into_pipeline_data(),
664 );
665 Ok(Continue)
666 }
667 Instruction::Not { src_dst } => {
668 let bool = ctx.collect_reg(*src_dst, *span)?;
669 let negated = !bool.as_bool()?;
670 ctx.put_reg(
671 *src_dst,
672 Value::bool(negated, bool.span()).into_pipeline_data(),
673 );
674 Ok(Continue)
675 }
676 Instruction::BinaryOp { lhs_dst, op, rhs } => binary_op(ctx, *lhs_dst, op, *rhs, *span),
677 Instruction::FollowCellPath { src_dst, path } => {
678 let data = ctx.take_reg(*src_dst);
679 let path = ctx.take_reg(*path);
680 if let PipelineData::Value(Value::CellPath { val: path, .. }, _) = path {
681 let value = data.follow_cell_path(&path.members, *span, true)?;
682 ctx.put_reg(*src_dst, value.into_pipeline_data());
683 Ok(Continue)
684 } else if let PipelineData::Value(Value::Error { error, .. }, _) = path {
685 Err(*error)
686 } else {
687 Err(ShellError::TypeMismatch {
688 err_message: "expected cell path".into(),
689 span: path.span().unwrap_or(*span),
690 })
691 }
692 }
693 Instruction::CloneCellPath { dst, src, path } => {
694 let value = ctx.clone_reg_value(*src, *span)?;
695 let path = ctx.take_reg(*path);
696 if let PipelineData::Value(Value::CellPath { val: path, .. }, _) = path {
697 let value = value.follow_cell_path(&path.members, true)?;
699 ctx.put_reg(*dst, value.into_pipeline_data());
700 Ok(Continue)
701 } else if let PipelineData::Value(Value::Error { error, .. }, _) = path {
702 Err(*error)
703 } else {
704 Err(ShellError::TypeMismatch {
705 err_message: "expected cell path".into(),
706 span: path.span().unwrap_or(*span),
707 })
708 }
709 }
710 Instruction::UpsertCellPath {
711 src_dst,
712 path,
713 new_value,
714 } => {
715 let data = ctx.take_reg(*src_dst);
716 let metadata = data.metadata();
717 let mut value = data.into_value(*span)?;
719 let path = ctx.take_reg(*path);
720 let new_value = ctx.collect_reg(*new_value, *span)?;
721 if let PipelineData::Value(Value::CellPath { val: path, .. }, _) = path {
722 value.upsert_data_at_cell_path(&path.members, new_value)?;
723 ctx.put_reg(*src_dst, value.into_pipeline_data_with_metadata(metadata));
724 Ok(Continue)
725 } else if let PipelineData::Value(Value::Error { error, .. }, _) = path {
726 Err(*error)
727 } else {
728 Err(ShellError::TypeMismatch {
729 err_message: "expected cell path".into(),
730 span: path.span().unwrap_or(*span),
731 })
732 }
733 }
734 Instruction::Jump { index } => Ok(Branch(*index)),
735 Instruction::BranchIf { cond, index } => {
736 let data = ctx.take_reg(*cond);
737 let data_span = data.span();
738 let val = match data {
739 PipelineData::Value(Value::Bool { val, .. }, _) => val,
740 PipelineData::Value(Value::Error { error, .. }, _) => {
741 return Err(*error);
742 }
743 _ => {
744 return Err(ShellError::TypeMismatch {
745 err_message: "expected bool".into(),
746 span: data_span.unwrap_or(*span),
747 });
748 }
749 };
750 if val {
751 Ok(Branch(*index))
752 } else {
753 Ok(Continue)
754 }
755 }
756 Instruction::BranchIfEmpty { src, index } => {
757 let is_empty = matches!(
758 ctx.borrow_reg(*src),
759 PipelineData::Empty | PipelineData::Value(Value::Nothing { .. }, _)
760 );
761
762 if is_empty {
763 Ok(Branch(*index))
764 } else {
765 Ok(Continue)
766 }
767 }
768 Instruction::Match {
769 pattern,
770 src,
771 index,
772 } => {
773 let value = ctx.clone_reg_value(*src, *span)?;
774 ctx.matches.clear();
775 if pattern.match_value(&value, &mut ctx.matches) {
776 for (var_id, match_value) in ctx.matches.drain(..) {
778 ctx.stack.add_var(var_id, match_value);
779 }
780 Ok(Branch(*index))
781 } else {
782 ctx.matches.clear();
784 Ok(Continue)
785 }
786 }
787 Instruction::CheckMatchGuard { src } => {
788 if matches!(
789 ctx.borrow_reg(*src),
790 PipelineData::Value(Value::Bool { .. }, _)
791 ) {
792 Ok(Continue)
793 } else {
794 Err(ShellError::MatchGuardNotBool { span: *span })
795 }
796 }
797 Instruction::Iterate {
798 dst,
799 stream,
800 end_index,
801 } => eval_iterate(ctx, *dst, *stream, *end_index),
802 Instruction::OnError { index } => {
803 ctx.stack.error_handlers.push(ErrorHandler {
804 handler_index: *index,
805 error_register: None,
806 });
807 Ok(Continue)
808 }
809 Instruction::OnErrorInto { index, dst } => {
810 ctx.stack.error_handlers.push(ErrorHandler {
811 handler_index: *index,
812 error_register: Some(*dst),
813 });
814 Ok(Continue)
815 }
816 Instruction::PopErrorHandler => {
817 ctx.stack.error_handlers.pop(ctx.error_handler_base);
818 Ok(Continue)
819 }
820 Instruction::ReturnEarly { src } => {
821 let val = ctx.collect_reg(*src, *span)?;
822 Err(ShellError::Return {
823 span: *span,
824 value: Box::new(val),
825 })
826 }
827 Instruction::Return { src } => Ok(Return(*src)),
828 }
829}
830
831fn load_literal(
833 ctx: &mut EvalContext<'_>,
834 dst: RegId,
835 lit: &Literal,
836 span: Span,
837) -> Result<InstructionResult, ShellError> {
838 let value = literal_value(ctx, lit, span)?;
839 ctx.put_reg(dst, PipelineData::Value(value, None));
840 Ok(InstructionResult::Continue)
841}
842
843fn literal_value(
844 ctx: &mut EvalContext<'_>,
845 lit: &Literal,
846 span: Span,
847) -> Result<Value, ShellError> {
848 Ok(match lit {
849 Literal::Bool(b) => Value::bool(*b, span),
850 Literal::Int(i) => Value::int(*i, span),
851 Literal::Float(f) => Value::float(*f, span),
852 Literal::Filesize(q) => Value::filesize(*q, span),
853 Literal::Duration(q) => Value::duration(*q, span),
854 Literal::Binary(bin) => Value::binary(&ctx.data[*bin], span),
855 Literal::Block(block_id) | Literal::RowCondition(block_id) | Literal::Closure(block_id) => {
856 let block = ctx.engine_state.get_block(*block_id);
857 let captures = block
858 .captures
859 .iter()
860 .map(|(var_id, span)| get_var(ctx, *var_id, *span).map(|val| (*var_id, val)))
861 .collect::<Result<Vec<_>, ShellError>>()?;
862 Value::closure(
863 Closure {
864 block_id: *block_id,
865 captures,
866 },
867 span,
868 )
869 }
870 Literal::Range {
871 start,
872 step,
873 end,
874 inclusion,
875 } => {
876 let start = ctx.collect_reg(*start, span)?;
877 let step = ctx.collect_reg(*step, span)?;
878 let end = ctx.collect_reg(*end, span)?;
879 let range = Range::new(start, step, end, *inclusion, span)?;
880 Value::range(range, span)
881 }
882 Literal::List { capacity } => Value::list(Vec::with_capacity(*capacity), span),
883 Literal::Record { capacity } => Value::record(Record::with_capacity(*capacity), span),
884 Literal::Filepath {
885 val: path,
886 no_expand,
887 } => {
888 let path = ctx.get_str(*path, span)?;
889 if *no_expand {
890 Value::string(path, span)
891 } else {
892 let cwd = ctx.engine_state.cwd(Some(ctx.stack))?;
893 let path = expand_path_with(path, cwd, true);
894
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 cwd = ctx
909 .engine_state
910 .cwd(Some(ctx.stack))
911 .map(AbsolutePathBuf::into_std_path_buf)
912 .unwrap_or_default();
913 let path = expand_path_with(path, cwd, true);
914
915 Value::string(path.to_string_lossy(), span)
916 }
917 }
918 Literal::GlobPattern { val, no_expand } => {
919 Value::glob(ctx.get_str(*val, span)?, *no_expand, span)
920 }
921 Literal::String(s) => Value::string(ctx.get_str(*s, span)?, span),
922 Literal::RawString(s) => Value::string(ctx.get_str(*s, span)?, span),
923 Literal::CellPath(path) => Value::cell_path(CellPath::clone(path), span),
924 Literal::Date(dt) => Value::date(**dt, span),
925 Literal::Nothing => Value::nothing(span),
926 })
927}
928
929fn binary_op(
930 ctx: &mut EvalContext<'_>,
931 lhs_dst: RegId,
932 op: &Operator,
933 rhs: RegId,
934 span: Span,
935) -> Result<InstructionResult, ShellError> {
936 let lhs_val = ctx.collect_reg(lhs_dst, span)?;
937 let rhs_val = ctx.collect_reg(rhs, span)?;
938
939 if let Value::Error { error, .. } = lhs_val {
941 return Err(*error);
942 }
943 if let Value::Error { error, .. } = rhs_val {
944 return Err(*error);
945 }
946
947 let op_span = span;
950
951 let result = match op {
952 Operator::Comparison(cmp) => match cmp {
953 Comparison::Equal => lhs_val.eq(op_span, &rhs_val, span)?,
954 Comparison::NotEqual => lhs_val.ne(op_span, &rhs_val, span)?,
955 Comparison::LessThan => lhs_val.lt(op_span, &rhs_val, span)?,
956 Comparison::GreaterThan => lhs_val.gt(op_span, &rhs_val, span)?,
957 Comparison::LessThanOrEqual => lhs_val.lte(op_span, &rhs_val, span)?,
958 Comparison::GreaterThanOrEqual => lhs_val.gte(op_span, &rhs_val, span)?,
959 Comparison::RegexMatch => {
960 lhs_val.regex_match(ctx.engine_state, op_span, &rhs_val, false, span)?
961 }
962 Comparison::NotRegexMatch => {
963 lhs_val.regex_match(ctx.engine_state, op_span, &rhs_val, true, span)?
964 }
965 Comparison::In => lhs_val.r#in(op_span, &rhs_val, span)?,
966 Comparison::NotIn => lhs_val.not_in(op_span, &rhs_val, span)?,
967 Comparison::Has => lhs_val.has(op_span, &rhs_val, span)?,
968 Comparison::NotHas => lhs_val.not_has(op_span, &rhs_val, span)?,
969 Comparison::StartsWith => lhs_val.starts_with(op_span, &rhs_val, span)?,
970 Comparison::EndsWith => lhs_val.ends_with(op_span, &rhs_val, span)?,
971 },
972 Operator::Math(mat) => match mat {
973 Math::Add => lhs_val.add(op_span, &rhs_val, span)?,
974 Math::Subtract => lhs_val.sub(op_span, &rhs_val, span)?,
975 Math::Multiply => lhs_val.mul(op_span, &rhs_val, span)?,
976 Math::Divide => lhs_val.div(op_span, &rhs_val, span)?,
977 Math::FloorDivide => lhs_val.floor_div(op_span, &rhs_val, span)?,
978 Math::Modulo => lhs_val.modulo(op_span, &rhs_val, span)?,
979 Math::Pow => lhs_val.pow(op_span, &rhs_val, span)?,
980 Math::Concatenate => lhs_val.concat(op_span, &rhs_val, span)?,
981 },
982 Operator::Boolean(bl) => match bl {
983 Boolean::Or => lhs_val.or(op_span, &rhs_val, span)?,
984 Boolean::Xor => lhs_val.xor(op_span, &rhs_val, span)?,
985 Boolean::And => lhs_val.and(op_span, &rhs_val, span)?,
986 },
987 Operator::Bits(bit) => match bit {
988 Bits::BitOr => lhs_val.bit_or(op_span, &rhs_val, span)?,
989 Bits::BitXor => lhs_val.bit_xor(op_span, &rhs_val, span)?,
990 Bits::BitAnd => lhs_val.bit_and(op_span, &rhs_val, span)?,
991 Bits::ShiftLeft => lhs_val.bit_shl(op_span, &rhs_val, span)?,
992 Bits::ShiftRight => lhs_val.bit_shr(op_span, &rhs_val, span)?,
993 },
994 Operator::Assignment(_asg) => {
995 return Err(ShellError::IrEvalError {
996 msg: "can't eval assignment with the `binary-op` instruction".into(),
997 span: Some(span),
998 })
999 }
1000 };
1001
1002 ctx.put_reg(lhs_dst, PipelineData::Value(result, None));
1003
1004 Ok(InstructionResult::Continue)
1005}
1006
1007fn eval_call<D: DebugContext>(
1009 ctx: &mut EvalContext<'_>,
1010 decl_id: DeclId,
1011 head: Span,
1012 input: PipelineData,
1013) -> Result<PipelineData, ShellError> {
1014 let EvalContext {
1015 engine_state,
1016 stack: caller_stack,
1017 args_base,
1018 redirect_out,
1019 redirect_err,
1020 ..
1021 } = ctx;
1022
1023 let args_len = caller_stack.arguments.get_len(*args_base);
1024 let decl = engine_state.get_decl(decl_id);
1025
1026 let mut caller_stack = caller_stack.push_redirection(redirect_out.take(), redirect_err.take());
1028
1029 let result = (|| {
1030 if let Some(block_id) = decl.block_id() {
1031 let block = engine_state.get_block(block_id);
1033
1034 check_input_types(&input, &block.signature, head)?;
1036
1037 let mut callee_stack = caller_stack.gather_captures(engine_state, &block.captures);
1039
1040 gather_arguments(
1041 engine_state,
1042 block,
1043 &mut caller_stack,
1044 &mut callee_stack,
1045 *args_base,
1046 args_len,
1047 head,
1048 )?;
1049
1050 callee_stack.recursion_count += 1;
1053
1054 let result =
1055 eval_block_with_early_return::<D>(engine_state, &mut callee_stack, block, input);
1056
1057 if block.redirect_env {
1059 redirect_env(engine_state, &mut caller_stack, &callee_stack);
1060 }
1061
1062 result
1063 } else {
1064 check_input_types(&input, &decl.signature(), head)?;
1065 let span = Span::merge_many(
1067 std::iter::once(head).chain(
1068 caller_stack
1069 .arguments
1070 .get_args(*args_base, args_len)
1071 .iter()
1072 .flat_map(|arg| arg.span()),
1073 ),
1074 );
1075
1076 let call = Call {
1077 decl_id,
1078 head,
1079 span,
1080 args_base: *args_base,
1081 args_len,
1082 };
1083
1084 decl.run(engine_state, &mut caller_stack, &(&call).into(), input)
1086 }
1087 })();
1088
1089 drop(caller_stack);
1090
1091 ctx.stack.arguments.leave_frame(ctx.args_base);
1093 ctx.redirect_out = None;
1094 ctx.redirect_err = None;
1095
1096 result
1097}
1098
1099fn find_named_var_id(
1100 sig: &Signature,
1101 name: &[u8],
1102 short: &[u8],
1103 span: Span,
1104) -> Result<VarId, ShellError> {
1105 sig.named
1106 .iter()
1107 .find(|n| {
1108 if !n.long.is_empty() {
1109 n.long.as_bytes() == name
1110 } else {
1111 n.short
1113 .is_some_and(|s| s.encode_utf8(&mut [0; 4]).as_bytes() == short)
1114 }
1115 })
1116 .ok_or_else(|| ShellError::IrEvalError {
1117 msg: format!(
1118 "block does not have an argument named `{}`",
1119 String::from_utf8_lossy(name)
1120 ),
1121 span: Some(span),
1122 })
1123 .and_then(|flag| expect_named_var_id(flag, span))
1124}
1125
1126fn expect_named_var_id(arg: &Flag, span: Span) -> Result<VarId, ShellError> {
1127 arg.var_id.ok_or_else(|| ShellError::IrEvalError {
1128 msg: format!(
1129 "block signature is missing var id for named arg `{}`",
1130 arg.long
1131 ),
1132 span: Some(span),
1133 })
1134}
1135
1136fn expect_positional_var_id(arg: &PositionalArg, span: Span) -> Result<VarId, ShellError> {
1137 arg.var_id.ok_or_else(|| ShellError::IrEvalError {
1138 msg: format!(
1139 "block signature is missing var id for positional arg `{}`",
1140 arg.name
1141 ),
1142 span: Some(span),
1143 })
1144}
1145
1146fn gather_arguments(
1148 engine_state: &EngineState,
1149 block: &Block,
1150 caller_stack: &mut Stack,
1151 callee_stack: &mut Stack,
1152 args_base: usize,
1153 args_len: usize,
1154 call_head: Span,
1155) -> Result<(), ShellError> {
1156 let mut positional_iter = block
1157 .signature
1158 .required_positional
1159 .iter()
1160 .map(|p| (p, true))
1161 .chain(
1162 block
1163 .signature
1164 .optional_positional
1165 .iter()
1166 .map(|p| (p, false)),
1167 );
1168
1169 let mut rest = vec![];
1171
1172 let mut always_spread = false;
1174
1175 for arg in caller_stack.arguments.drain_args(args_base, args_len) {
1176 match arg {
1177 Argument::Positional { span, val, .. } => {
1178 let next = (!always_spread).then(|| positional_iter.next()).flatten();
1180 if let Some((positional_arg, required)) = next {
1181 let var_id = expect_positional_var_id(positional_arg, span)?;
1182 if required {
1183 let variable = engine_state.get_var(var_id);
1186 check_type(&val, &variable.ty)?;
1187 }
1188 callee_stack.add_var(var_id, val);
1189 } else {
1190 rest.push(val);
1191 }
1192 }
1193 Argument::Spread { vals, .. } => {
1194 if let Value::List { vals, .. } = vals {
1195 rest.extend(vals);
1196 always_spread = true;
1198 } else if let Value::Error { error, .. } = vals {
1199 return Err(*error);
1200 } else {
1201 return Err(ShellError::CannotSpreadAsList { span: vals.span() });
1202 }
1203 }
1204 Argument::Flag {
1205 data,
1206 name,
1207 short,
1208 span,
1209 } => {
1210 let var_id = find_named_var_id(&block.signature, &data[name], &data[short], span)?;
1211 callee_stack.add_var(var_id, Value::bool(true, span))
1212 }
1213 Argument::Named {
1214 data,
1215 name,
1216 short,
1217 span,
1218 val,
1219 ..
1220 } => {
1221 let var_id = find_named_var_id(&block.signature, &data[name], &data[short], span)?;
1222 callee_stack.add_var(var_id, val)
1223 }
1224 Argument::ParserInfo { .. } => (),
1225 }
1226 }
1227
1228 if let Some(rest_arg) = &block.signature.rest_positional {
1230 let rest_span = rest.first().map(|v| v.span()).unwrap_or(call_head);
1231 let var_id = expect_positional_var_id(rest_arg, rest_span)?;
1232 callee_stack.add_var(var_id, Value::list(rest, rest_span));
1233 }
1234
1235 for (positional_arg, _) in positional_iter {
1237 let var_id = expect_positional_var_id(positional_arg, call_head)?;
1238 callee_stack.add_var(
1239 var_id,
1240 positional_arg
1241 .default_value
1242 .clone()
1243 .unwrap_or(Value::nothing(call_head)),
1244 );
1245 }
1246
1247 for named_arg in &block.signature.named {
1248 if let Some(var_id) = named_arg.var_id {
1249 if !callee_stack.vars.iter().any(|(id, _)| *id == var_id) {
1253 let val = if named_arg.arg.is_none() {
1254 Value::bool(false, call_head)
1255 } else if let Some(value) = &named_arg.default_value {
1256 value.clone()
1257 } else {
1258 Value::nothing(call_head)
1259 };
1260 callee_stack.add_var(var_id, val);
1261 }
1262 }
1263 }
1264
1265 Ok(())
1266}
1267
1268fn check_type(val: &Value, ty: &Type) -> Result<(), ShellError> {
1270 match val {
1271 Value::Error { error, .. } => Err(*error.clone()),
1272 _ if val.is_subtype_of(ty) => Ok(()),
1273 _ => Err(ShellError::CantConvert {
1274 to_type: ty.to_string(),
1275 from_type: val.get_type().to_string(),
1276 span: val.span(),
1277 help: None,
1278 }),
1279 }
1280}
1281
1282fn check_input_types(
1284 input: &PipelineData,
1285 signature: &Signature,
1286 head: Span,
1287) -> Result<(), ShellError> {
1288 let io_types = &signature.input_output_types;
1289
1290 if io_types.is_empty() {
1292 return Ok(());
1293 }
1294
1295 if io_types.iter().all(|(intype, _)| intype == &Type::Nothing) {
1297 return Ok(());
1298 }
1299
1300 match input {
1301 PipelineData::Value(Value::Error { error, .. }, ..) => return Err(*error.clone()),
1303 PipelineData::Value(Value::Custom { .. }, ..) => return Ok(()),
1305 _ => (),
1306 }
1307
1308 if io_types
1310 .iter()
1311 .any(|(command_type, _)| input.is_subtype_of(command_type))
1312 {
1313 return Ok(());
1314 }
1315
1316 let mut input_types = io_types
1317 .iter()
1318 .map(|(input, _)| input.to_string())
1319 .collect::<Vec<String>>();
1320
1321 let expected_string = match input_types.len() {
1322 0 => {
1323 return Err(ShellError::NushellFailed {
1324 msg: "Command input type strings is empty, despite being non-zero earlier"
1325 .to_string(),
1326 })
1327 }
1328 1 => input_types.swap_remove(0),
1329 2 => input_types.join(" and "),
1330 _ => {
1331 input_types
1332 .last_mut()
1333 .expect("Vector with length >2 has no elements")
1334 .insert_str(0, "and ");
1335 input_types.join(", ")
1336 }
1337 };
1338
1339 match input {
1340 PipelineData::Empty => Err(ShellError::PipelineEmpty { dst_span: head }),
1341 _ => Err(ShellError::OnlySupportsThisInputType {
1342 exp_input_type: expected_string,
1343 wrong_type: input.get_type().to_string(),
1344 dst_span: head,
1345 src_span: input.span().unwrap_or(Span::unknown()),
1346 }),
1347 }
1348}
1349
1350fn get_var(ctx: &EvalContext<'_>, var_id: VarId, span: Span) -> Result<Value, ShellError> {
1352 match var_id {
1353 ENV_VARIABLE_ID => {
1355 let env_vars = ctx.stack.get_env_vars(ctx.engine_state);
1356 let env_columns = env_vars.keys();
1357 let env_values = env_vars.values();
1358
1359 let mut pairs = env_columns
1360 .map(|x| x.to_string())
1361 .zip(env_values.cloned())
1362 .collect::<Vec<(String, Value)>>();
1363
1364 pairs.sort_by(|a, b| a.0.cmp(&b.0));
1365
1366 Ok(Value::record(pairs.into_iter().collect(), span))
1367 }
1368 _ => ctx.stack.get_var(var_id, span).or_else(|err| {
1369 if let Some(const_val) = ctx.engine_state.get_constant(var_id).cloned() {
1371 Ok(const_val.with_span(span))
1372 } else {
1373 Err(err)
1374 }
1375 }),
1376 }
1377}
1378
1379fn get_env_var_case_insensitive<'a>(ctx: &'a mut EvalContext<'_>, key: &str) -> Option<&'a Value> {
1381 for overlays in ctx
1383 .stack
1384 .env_vars
1385 .iter()
1386 .rev()
1387 .chain(std::iter::once(&ctx.engine_state.env_vars))
1388 {
1389 for overlay_name in ctx.stack.active_overlays.iter().rev() {
1391 let Some(map) = overlays.get(overlay_name) else {
1392 continue;
1394 };
1395 let hidden = ctx.stack.env_hidden.get(overlay_name);
1396 let is_hidden = |key: &str| hidden.is_some_and(|hidden| hidden.contains(key));
1397
1398 if let Some(val) = map
1399 .get(key)
1401 .filter(|_| !is_hidden(key))
1403 .or_else(|| {
1404 map.iter().find_map(|(k, v)| {
1406 (k.eq_ignore_case(key) && !is_hidden(k)).then_some(v)
1408 })
1409 })
1410 {
1411 return Some(val);
1412 }
1413 }
1414 }
1415 None
1417}
1418
1419fn get_env_var_name_case_insensitive<'a>(ctx: &mut EvalContext<'_>, key: &'a str) -> Cow<'a, str> {
1423 ctx.stack
1425 .env_vars
1426 .iter()
1427 .rev()
1428 .chain(std::iter::once(&ctx.engine_state.env_vars))
1429 .flat_map(|overlays| {
1430 ctx.stack
1432 .active_overlays
1433 .iter()
1434 .rev()
1435 .filter_map(|name| overlays.get(name))
1436 })
1437 .find_map(|map| {
1438 if map.contains_key(key) {
1440 Some(Cow::Borrowed(key))
1441 } else {
1442 map.keys().find(|k| k.eq_ignore_case(key)).map(|k| {
1443 Cow::Owned(k.to_owned())
1445 })
1446 }
1447 })
1448 .unwrap_or(Cow::Borrowed(key))
1450}
1451
1452fn collect(data: PipelineData, fallback_span: Span) -> Result<PipelineData, ShellError> {
1456 let span = data.span().unwrap_or(fallback_span);
1457 let metadata = match data.metadata() {
1458 Some(PipelineMetadata {
1461 data_source: DataSource::FilePath(_),
1462 content_type: None,
1463 }) => None,
1464 other => other,
1465 };
1466 let value = data.into_value(span)?;
1467 Ok(PipelineData::Value(value, metadata))
1468}
1469
1470fn drain(ctx: &mut EvalContext<'_>, data: PipelineData) -> Result<InstructionResult, ShellError> {
1472 use self::InstructionResult::*;
1473 match data {
1474 PipelineData::ByteStream(stream, ..) => {
1475 let span = stream.span();
1476 let callback_spans = stream.get_caller_spans().clone();
1477 if let Err(mut err) = stream.drain() {
1478 ctx.stack.set_last_error(&err);
1479 if callback_spans.is_empty() {
1480 return Err(err);
1481 } else {
1482 for s in callback_spans {
1483 err = ShellError::EvalBlockWithInput {
1484 span: s,
1485 sources: vec![err],
1486 }
1487 }
1488 return Err(err);
1489 }
1490 } else {
1491 ctx.stack.set_last_exit_code(0, span);
1492 }
1493 }
1494 PipelineData::ListStream(stream, ..) => {
1495 let callback_spans = stream.get_caller_spans().clone();
1496 if let Err(mut err) = stream.drain() {
1497 if callback_spans.is_empty() {
1498 return Err(err);
1499 } else {
1500 for s in callback_spans {
1501 err = ShellError::EvalBlockWithInput {
1502 span: s,
1503 sources: vec![err],
1504 }
1505 }
1506 return Err(err);
1507 }
1508 }
1509 }
1510 PipelineData::Value(..) | PipelineData::Empty => {}
1511 }
1512 Ok(Continue)
1513}
1514
1515enum RedirectionStream {
1516 Out,
1517 Err,
1518}
1519
1520fn open_file(ctx: &EvalContext<'_>, path: &Value, append: bool) -> Result<Arc<File>, ShellError> {
1522 let path_expanded =
1523 expand_path_with(path.as_str()?, ctx.engine_state.cwd(Some(ctx.stack))?, true);
1524 let mut options = File::options();
1525 if append {
1526 options.append(true);
1527 } else {
1528 options.write(true).truncate(true);
1529 }
1530 let file = options
1531 .create(true)
1532 .open(&path_expanded)
1533 .map_err(|err| IoError::new(err.kind(), path.span(), path_expanded))?;
1534 Ok(Arc::new(file))
1535}
1536
1537fn eval_redirection(
1539 ctx: &mut EvalContext<'_>,
1540 mode: &RedirectMode,
1541 span: Span,
1542 which: RedirectionStream,
1543) -> Result<Option<Redirection>, ShellError> {
1544 match mode {
1545 RedirectMode::Pipe => Ok(Some(Redirection::Pipe(OutDest::Pipe))),
1546 RedirectMode::PipeSeparate => Ok(Some(Redirection::Pipe(OutDest::PipeSeparate))),
1547 RedirectMode::Value => Ok(Some(Redirection::Pipe(OutDest::Value))),
1548 RedirectMode::Null => Ok(Some(Redirection::Pipe(OutDest::Null))),
1549 RedirectMode::Inherit => Ok(Some(Redirection::Pipe(OutDest::Inherit))),
1550 RedirectMode::Print => Ok(Some(Redirection::Pipe(OutDest::Print))),
1551 RedirectMode::File { file_num } => {
1552 let file = ctx
1553 .files
1554 .get(*file_num as usize)
1555 .cloned()
1556 .flatten()
1557 .ok_or_else(|| ShellError::IrEvalError {
1558 msg: format!("Tried to redirect to file #{file_num}, but it is not open"),
1559 span: Some(span),
1560 })?;
1561 Ok(Some(Redirection::File(file)))
1562 }
1563 RedirectMode::Caller => Ok(match which {
1564 RedirectionStream::Out => ctx.stack.pipe_stdout().cloned().map(Redirection::Pipe),
1565 RedirectionStream::Err => ctx.stack.pipe_stderr().cloned().map(Redirection::Pipe),
1566 }),
1567 }
1568}
1569
1570fn eval_iterate(
1572 ctx: &mut EvalContext<'_>,
1573 dst: RegId,
1574 stream: RegId,
1575 end_index: usize,
1576) -> Result<InstructionResult, ShellError> {
1577 let mut data = ctx.take_reg(stream);
1578 if let PipelineData::ListStream(list_stream, _) = &mut data {
1579 if let Some(val) = list_stream.next_value() {
1581 ctx.put_reg(dst, val.into_pipeline_data());
1582 ctx.put_reg(stream, data); Ok(InstructionResult::Continue)
1584 } else {
1585 ctx.put_reg(dst, PipelineData::Empty);
1586 Ok(InstructionResult::Branch(end_index))
1587 }
1588 } else {
1589 let metadata = data.metadata();
1592 let span = data.span().unwrap_or(Span::unknown());
1593 ctx.put_reg(
1594 stream,
1595 PipelineData::ListStream(
1596 ListStream::new(data.into_iter(), span, Signals::EMPTY),
1597 metadata,
1598 ),
1599 );
1600 eval_iterate(ctx, dst, stream, end_index)
1601 }
1602}
1603
1604fn redirect_env(engine_state: &EngineState, caller_stack: &mut Stack, callee_stack: &Stack) {
1606 let caller_env_vars = caller_stack.get_env_var_names(engine_state);
1609
1610 for var in caller_env_vars.iter() {
1613 if !callee_stack.has_env_var(engine_state, var) {
1614 caller_stack.remove_env_var(engine_state, var);
1615 }
1616 }
1617
1618 for (var, value) in callee_stack.get_stack_env_vars() {
1620 caller_stack.add_env_var(var, value);
1621 }
1622
1623 caller_stack.config.clone_from(&callee_stack.config);
1625}