1use std::{borrow::Cow, fs::File, sync::Arc};
2
3use nu_path::{AbsolutePathBuf, 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 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)?;
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)?;
698 ctx.put_reg(*dst, value.into_owned().into_pipeline_data());
699 Ok(Continue)
700 } else if let PipelineData::Value(Value::Error { error, .. }, _) = path {
701 Err(*error)
702 } else {
703 Err(ShellError::TypeMismatch {
704 err_message: "expected cell path".into(),
705 span: path.span().unwrap_or(*span),
706 })
707 }
708 }
709 Instruction::UpsertCellPath {
710 src_dst,
711 path,
712 new_value,
713 } => {
714 let data = ctx.take_reg(*src_dst);
715 let metadata = data.metadata();
716 let mut value = data.into_value(*span)?;
718 let path = ctx.take_reg(*path);
719 let new_value = ctx.collect_reg(*new_value, *span)?;
720 if let PipelineData::Value(Value::CellPath { val: path, .. }, _) = path {
721 value.upsert_data_at_cell_path(&path.members, new_value)?;
722 ctx.put_reg(*src_dst, value.into_pipeline_data_with_metadata(metadata));
723 Ok(Continue)
724 } else if let PipelineData::Value(Value::Error { error, .. }, _) = path {
725 Err(*error)
726 } else {
727 Err(ShellError::TypeMismatch {
728 err_message: "expected cell path".into(),
729 span: path.span().unwrap_or(*span),
730 })
731 }
732 }
733 Instruction::Jump { index } => Ok(Branch(*index)),
734 Instruction::BranchIf { cond, index } => {
735 let data = ctx.take_reg(*cond);
736 let data_span = data.span();
737 let val = match data {
738 PipelineData::Value(Value::Bool { val, .. }, _) => val,
739 PipelineData::Value(Value::Error { error, .. }, _) => {
740 return Err(*error);
741 }
742 _ => {
743 return Err(ShellError::TypeMismatch {
744 err_message: "expected bool".into(),
745 span: data_span.unwrap_or(*span),
746 });
747 }
748 };
749 if val {
750 Ok(Branch(*index))
751 } else {
752 Ok(Continue)
753 }
754 }
755 Instruction::BranchIfEmpty { src, index } => {
756 let is_empty = matches!(
757 ctx.borrow_reg(*src),
758 PipelineData::Empty | PipelineData::Value(Value::Nothing { .. }, _)
759 );
760
761 if is_empty {
762 Ok(Branch(*index))
763 } else {
764 Ok(Continue)
765 }
766 }
767 Instruction::Match {
768 pattern,
769 src,
770 index,
771 } => {
772 let value = ctx.clone_reg_value(*src, *span)?;
773 ctx.matches.clear();
774 if pattern.match_value(&value, &mut ctx.matches) {
775 for (var_id, match_value) in ctx.matches.drain(..) {
777 ctx.stack.add_var(var_id, match_value);
778 }
779 Ok(Branch(*index))
780 } else {
781 ctx.matches.clear();
783 Ok(Continue)
784 }
785 }
786 Instruction::CheckMatchGuard { src } => {
787 if matches!(
788 ctx.borrow_reg(*src),
789 PipelineData::Value(Value::Bool { .. }, _)
790 ) {
791 Ok(Continue)
792 } else {
793 Err(ShellError::MatchGuardNotBool { span: *span })
794 }
795 }
796 Instruction::Iterate {
797 dst,
798 stream,
799 end_index,
800 } => eval_iterate(ctx, *dst, *stream, *end_index),
801 Instruction::OnError { index } => {
802 ctx.stack.error_handlers.push(ErrorHandler {
803 handler_index: *index,
804 error_register: None,
805 });
806 Ok(Continue)
807 }
808 Instruction::OnErrorInto { index, dst } => {
809 ctx.stack.error_handlers.push(ErrorHandler {
810 handler_index: *index,
811 error_register: Some(*dst),
812 });
813 Ok(Continue)
814 }
815 Instruction::PopErrorHandler => {
816 ctx.stack.error_handlers.pop(ctx.error_handler_base);
817 Ok(Continue)
818 }
819 Instruction::ReturnEarly { src } => {
820 let val = ctx.collect_reg(*src, *span)?;
821 Err(ShellError::Return {
822 span: *span,
823 value: Box::new(val),
824 })
825 }
826 Instruction::Return { src } => Ok(Return(*src)),
827 }
828}
829
830fn load_literal(
832 ctx: &mut EvalContext<'_>,
833 dst: RegId,
834 lit: &Literal,
835 span: Span,
836) -> Result<InstructionResult, ShellError> {
837 let value = literal_value(ctx, lit, span)?;
838 ctx.put_reg(dst, PipelineData::Value(value, None));
839 Ok(InstructionResult::Continue)
840}
841
842fn literal_value(
843 ctx: &mut EvalContext<'_>,
844 lit: &Literal,
845 span: Span,
846) -> Result<Value, ShellError> {
847 Ok(match lit {
848 Literal::Bool(b) => Value::bool(*b, span),
849 Literal::Int(i) => Value::int(*i, span),
850 Literal::Float(f) => Value::float(*f, span),
851 Literal::Filesize(q) => Value::filesize(*q, span),
852 Literal::Duration(q) => Value::duration(*q, span),
853 Literal::Binary(bin) => Value::binary(&ctx.data[*bin], span),
854 Literal::Block(block_id) | Literal::RowCondition(block_id) | Literal::Closure(block_id) => {
855 let block = ctx.engine_state.get_block(*block_id);
856 let captures = block
857 .captures
858 .iter()
859 .map(|(var_id, span)| get_var(ctx, *var_id, *span).map(|val| (*var_id, val)))
860 .collect::<Result<Vec<_>, ShellError>>()?;
861 Value::closure(
862 Closure {
863 block_id: *block_id,
864 captures,
865 },
866 span,
867 )
868 }
869 Literal::Range {
870 start,
871 step,
872 end,
873 inclusion,
874 } => {
875 let start = ctx.collect_reg(*start, span)?;
876 let step = ctx.collect_reg(*step, span)?;
877 let end = ctx.collect_reg(*end, span)?;
878 let range = Range::new(start, step, end, *inclusion, span)?;
879 Value::range(range, span)
880 }
881 Literal::List { capacity } => Value::list(Vec::with_capacity(*capacity), span),
882 Literal::Record { capacity } => Value::record(Record::with_capacity(*capacity), span),
883 Literal::Filepath {
884 val: path,
885 no_expand,
886 } => {
887 let path = ctx.get_str(*path, span)?;
888 if *no_expand {
889 Value::string(path, span)
890 } else {
891 let cwd = ctx.engine_state.cwd(Some(ctx.stack))?;
892 let path = expand_path_with(path, cwd, true);
893
894 Value::string(path.to_string_lossy(), span)
895 }
896 }
897 Literal::Directory {
898 val: path,
899 no_expand,
900 } => {
901 let path = ctx.get_str(*path, span)?;
902 if path == "-" {
903 Value::string("-", span)
904 } else if *no_expand {
905 Value::string(path, span)
906 } else {
907 let cwd = ctx
908 .engine_state
909 .cwd(Some(ctx.stack))
910 .map(AbsolutePathBuf::into_std_path_buf)
911 .unwrap_or_default();
912 let path = expand_path_with(path, cwd, true);
913
914 Value::string(path.to_string_lossy(), span)
915 }
916 }
917 Literal::GlobPattern { val, no_expand } => {
918 Value::glob(ctx.get_str(*val, span)?, *no_expand, span)
919 }
920 Literal::String(s) => Value::string(ctx.get_str(*s, span)?, span),
921 Literal::RawString(s) => Value::string(ctx.get_str(*s, span)?, span),
922 Literal::CellPath(path) => Value::cell_path(CellPath::clone(path), span),
923 Literal::Date(dt) => Value::date(**dt, span),
924 Literal::Nothing => Value::nothing(span),
925 })
926}
927
928fn binary_op(
929 ctx: &mut EvalContext<'_>,
930 lhs_dst: RegId,
931 op: &Operator,
932 rhs: RegId,
933 span: Span,
934) -> Result<InstructionResult, ShellError> {
935 let lhs_val = ctx.collect_reg(lhs_dst, span)?;
936 let rhs_val = ctx.collect_reg(rhs, span)?;
937
938 if let Value::Error { error, .. } = lhs_val {
940 return Err(*error);
941 }
942 if let Value::Error { error, .. } = rhs_val {
943 return Err(*error);
944 }
945
946 let op_span = span;
949
950 let result = match op {
951 Operator::Comparison(cmp) => match cmp {
952 Comparison::Equal => lhs_val.eq(op_span, &rhs_val, span)?,
953 Comparison::NotEqual => lhs_val.ne(op_span, &rhs_val, span)?,
954 Comparison::LessThan => lhs_val.lt(op_span, &rhs_val, span)?,
955 Comparison::GreaterThan => lhs_val.gt(op_span, &rhs_val, span)?,
956 Comparison::LessThanOrEqual => lhs_val.lte(op_span, &rhs_val, span)?,
957 Comparison::GreaterThanOrEqual => lhs_val.gte(op_span, &rhs_val, span)?,
958 Comparison::RegexMatch => {
959 lhs_val.regex_match(ctx.engine_state, op_span, &rhs_val, false, span)?
960 }
961 Comparison::NotRegexMatch => {
962 lhs_val.regex_match(ctx.engine_state, op_span, &rhs_val, true, span)?
963 }
964 Comparison::In => lhs_val.r#in(op_span, &rhs_val, span)?,
965 Comparison::NotIn => lhs_val.not_in(op_span, &rhs_val, span)?,
966 Comparison::Has => lhs_val.has(op_span, &rhs_val, span)?,
967 Comparison::NotHas => lhs_val.not_has(op_span, &rhs_val, span)?,
968 Comparison::StartsWith => lhs_val.starts_with(op_span, &rhs_val, span)?,
969 Comparison::EndsWith => lhs_val.ends_with(op_span, &rhs_val, span)?,
970 },
971 Operator::Math(mat) => match mat {
972 Math::Add => lhs_val.add(op_span, &rhs_val, span)?,
973 Math::Subtract => lhs_val.sub(op_span, &rhs_val, span)?,
974 Math::Multiply => lhs_val.mul(op_span, &rhs_val, span)?,
975 Math::Divide => lhs_val.div(op_span, &rhs_val, span)?,
976 Math::FloorDivide => lhs_val.floor_div(op_span, &rhs_val, span)?,
977 Math::Modulo => lhs_val.modulo(op_span, &rhs_val, span)?,
978 Math::Pow => lhs_val.pow(op_span, &rhs_val, span)?,
979 Math::Concatenate => lhs_val.concat(op_span, &rhs_val, span)?,
980 },
981 Operator::Boolean(bl) => match bl {
982 Boolean::Or => lhs_val.or(op_span, &rhs_val, span)?,
983 Boolean::Xor => lhs_val.xor(op_span, &rhs_val, span)?,
984 Boolean::And => lhs_val.and(op_span, &rhs_val, span)?,
985 },
986 Operator::Bits(bit) => match bit {
987 Bits::BitOr => lhs_val.bit_or(op_span, &rhs_val, span)?,
988 Bits::BitXor => lhs_val.bit_xor(op_span, &rhs_val, span)?,
989 Bits::BitAnd => lhs_val.bit_and(op_span, &rhs_val, span)?,
990 Bits::ShiftLeft => lhs_val.bit_shl(op_span, &rhs_val, span)?,
991 Bits::ShiftRight => lhs_val.bit_shr(op_span, &rhs_val, span)?,
992 },
993 Operator::Assignment(_asg) => {
994 return Err(ShellError::IrEvalError {
995 msg: "can't eval assignment with the `binary-op` instruction".into(),
996 span: Some(span),
997 });
998 }
999 };
1000
1001 ctx.put_reg(lhs_dst, PipelineData::Value(result, None));
1002
1003 Ok(InstructionResult::Continue)
1004}
1005
1006fn eval_call<D: DebugContext>(
1008 ctx: &mut EvalContext<'_>,
1009 decl_id: DeclId,
1010 head: Span,
1011 input: PipelineData,
1012) -> Result<PipelineData, ShellError> {
1013 let EvalContext {
1014 engine_state,
1015 stack: caller_stack,
1016 args_base,
1017 redirect_out,
1018 redirect_err,
1019 ..
1020 } = ctx;
1021
1022 let args_len = caller_stack.arguments.get_len(*args_base);
1023 let decl = engine_state.get_decl(decl_id);
1024
1025 let mut caller_stack = caller_stack.push_redirection(redirect_out.take(), redirect_err.take());
1027
1028 let result = (|| {
1029 if let Some(block_id) = decl.block_id() {
1030 let block = engine_state.get_block(block_id);
1032
1033 check_input_types(&input, &block.signature, head)?;
1035
1036 let mut callee_stack = caller_stack.gather_captures(engine_state, &block.captures);
1038
1039 gather_arguments(
1040 engine_state,
1041 block,
1042 &mut caller_stack,
1043 &mut callee_stack,
1044 *args_base,
1045 args_len,
1046 head,
1047 )?;
1048
1049 callee_stack.recursion_count += 1;
1052
1053 let result =
1054 eval_block_with_early_return::<D>(engine_state, &mut callee_stack, block, input);
1055
1056 if block.redirect_env {
1058 redirect_env(engine_state, &mut caller_stack, &callee_stack);
1059 }
1060
1061 result
1062 } else {
1063 check_input_types(&input, &decl.signature(), head)?;
1064 let span = Span::merge_many(
1066 std::iter::once(head).chain(
1067 caller_stack
1068 .arguments
1069 .get_args(*args_base, args_len)
1070 .iter()
1071 .flat_map(|arg| arg.span()),
1072 ),
1073 );
1074
1075 let call = Call {
1076 decl_id,
1077 head,
1078 span,
1079 args_base: *args_base,
1080 args_len,
1081 };
1082
1083 decl.run(engine_state, &mut caller_stack, &(&call).into(), input)
1085 }
1086 })();
1087
1088 drop(caller_stack);
1089
1090 ctx.stack.arguments.leave_frame(ctx.args_base);
1092 ctx.redirect_out = None;
1093 ctx.redirect_err = None;
1094
1095 result
1096}
1097
1098fn find_named_var_id(
1099 sig: &Signature,
1100 name: &[u8],
1101 short: &[u8],
1102 span: Span,
1103) -> Result<VarId, ShellError> {
1104 sig.named
1105 .iter()
1106 .find(|n| {
1107 if !n.long.is_empty() {
1108 n.long.as_bytes() == name
1109 } else {
1110 n.short
1112 .is_some_and(|s| s.encode_utf8(&mut [0; 4]).as_bytes() == short)
1113 }
1114 })
1115 .ok_or_else(|| ShellError::IrEvalError {
1116 msg: format!(
1117 "block does not have an argument named `{}`",
1118 String::from_utf8_lossy(name)
1119 ),
1120 span: Some(span),
1121 })
1122 .and_then(|flag| expect_named_var_id(flag, span))
1123}
1124
1125fn expect_named_var_id(arg: &Flag, span: Span) -> Result<VarId, ShellError> {
1126 arg.var_id.ok_or_else(|| ShellError::IrEvalError {
1127 msg: format!(
1128 "block signature is missing var id for named arg `{}`",
1129 arg.long
1130 ),
1131 span: Some(span),
1132 })
1133}
1134
1135fn expect_positional_var_id(arg: &PositionalArg, span: Span) -> Result<VarId, ShellError> {
1136 arg.var_id.ok_or_else(|| ShellError::IrEvalError {
1137 msg: format!(
1138 "block signature is missing var id for positional arg `{}`",
1139 arg.name
1140 ),
1141 span: Some(span),
1142 })
1143}
1144
1145fn gather_arguments(
1147 engine_state: &EngineState,
1148 block: &Block,
1149 caller_stack: &mut Stack,
1150 callee_stack: &mut Stack,
1151 args_base: usize,
1152 args_len: usize,
1153 call_head: Span,
1154) -> Result<(), ShellError> {
1155 let mut positional_iter = block
1156 .signature
1157 .required_positional
1158 .iter()
1159 .map(|p| (p, true))
1160 .chain(
1161 block
1162 .signature
1163 .optional_positional
1164 .iter()
1165 .map(|p| (p, false)),
1166 );
1167
1168 let mut rest = vec![];
1170
1171 let mut always_spread = false;
1173
1174 for arg in caller_stack.arguments.drain_args(args_base, args_len) {
1175 match arg {
1176 Argument::Positional { span, val, .. } => {
1177 let next = (!always_spread).then(|| positional_iter.next()).flatten();
1179 if let Some((positional_arg, required)) = next {
1180 let var_id = expect_positional_var_id(positional_arg, span)?;
1181 if required {
1182 let variable = engine_state.get_var(var_id);
1185 check_type(&val, &variable.ty)?;
1186 }
1187 callee_stack.add_var(var_id, val);
1188 } else {
1189 rest.push(val);
1190 }
1191 }
1192 Argument::Spread { vals, .. } => {
1193 if let Value::List { vals, .. } = vals {
1194 rest.extend(vals);
1195 always_spread = true;
1197 } else if let Value::Error { error, .. } = vals {
1198 return Err(*error);
1199 } else {
1200 return Err(ShellError::CannotSpreadAsList { span: vals.span() });
1201 }
1202 }
1203 Argument::Flag {
1204 data,
1205 name,
1206 short,
1207 span,
1208 } => {
1209 let var_id = find_named_var_id(&block.signature, &data[name], &data[short], span)?;
1210 callee_stack.add_var(var_id, Value::bool(true, span))
1211 }
1212 Argument::Named {
1213 data,
1214 name,
1215 short,
1216 span,
1217 val,
1218 ..
1219 } => {
1220 let var_id = find_named_var_id(&block.signature, &data[name], &data[short], span)?;
1221 callee_stack.add_var(var_id, val)
1222 }
1223 Argument::ParserInfo { .. } => (),
1224 }
1225 }
1226
1227 if let Some(rest_arg) = &block.signature.rest_positional {
1229 let rest_span = rest.first().map(|v| v.span()).unwrap_or(call_head);
1230 let var_id = expect_positional_var_id(rest_arg, rest_span)?;
1231 callee_stack.add_var(var_id, Value::list(rest, rest_span));
1232 }
1233
1234 for (positional_arg, _) in positional_iter {
1236 let var_id = expect_positional_var_id(positional_arg, call_head)?;
1237 callee_stack.add_var(
1238 var_id,
1239 positional_arg
1240 .default_value
1241 .clone()
1242 .unwrap_or(Value::nothing(call_head)),
1243 );
1244 }
1245
1246 for named_arg in &block.signature.named {
1247 if let Some(var_id) = named_arg.var_id {
1248 if !callee_stack.vars.iter().any(|(id, _)| *id == var_id) {
1252 let val = if named_arg.arg.is_none() {
1253 Value::bool(false, call_head)
1254 } else if let Some(value) = &named_arg.default_value {
1255 value.clone()
1256 } else {
1257 Value::nothing(call_head)
1258 };
1259 callee_stack.add_var(var_id, val);
1260 }
1261 }
1262 }
1263
1264 Ok(())
1265}
1266
1267fn check_type(val: &Value, ty: &Type) -> Result<(), ShellError> {
1269 match val {
1270 Value::Error { error, .. } => Err(*error.clone()),
1271 _ if val.is_subtype_of(ty) => Ok(()),
1272 _ => Err(ShellError::CantConvert {
1273 to_type: ty.to_string(),
1274 from_type: val.get_type().to_string(),
1275 span: val.span(),
1276 help: None,
1277 }),
1278 }
1279}
1280
1281fn check_input_types(
1283 input: &PipelineData,
1284 signature: &Signature,
1285 head: Span,
1286) -> Result<(), ShellError> {
1287 let io_types = &signature.input_output_types;
1288
1289 if io_types.is_empty() {
1291 return Ok(());
1292 }
1293
1294 if io_types.iter().all(|(intype, _)| intype == &Type::Nothing) {
1296 return Ok(());
1297 }
1298
1299 match input {
1300 PipelineData::Value(Value::Error { error, .. }, ..) => return Err(*error.clone()),
1302 PipelineData::Value(Value::Custom { .. }, ..) => return Ok(()),
1304 _ => (),
1305 }
1306
1307 if io_types
1309 .iter()
1310 .any(|(command_type, _)| input.is_subtype_of(command_type))
1311 {
1312 return Ok(());
1313 }
1314
1315 let mut input_types = io_types
1316 .iter()
1317 .map(|(input, _)| input.to_string())
1318 .collect::<Vec<String>>();
1319
1320 let expected_string = match input_types.len() {
1321 0 => {
1322 return Err(ShellError::NushellFailed {
1323 msg: "Command input type strings is empty, despite being non-zero earlier"
1324 .to_string(),
1325 });
1326 }
1327 1 => input_types.swap_remove(0),
1328 2 => input_types.join(" and "),
1329 _ => {
1330 input_types
1331 .last_mut()
1332 .expect("Vector with length >2 has no elements")
1333 .insert_str(0, "and ");
1334 input_types.join(", ")
1335 }
1336 };
1337
1338 match input {
1339 PipelineData::Empty => Err(ShellError::PipelineEmpty { dst_span: head }),
1340 _ => Err(ShellError::OnlySupportsThisInputType {
1341 exp_input_type: expected_string,
1342 wrong_type: input.get_type().to_string(),
1343 dst_span: head,
1344 src_span: input.span().unwrap_or(Span::unknown()),
1345 }),
1346 }
1347}
1348
1349fn get_var(ctx: &EvalContext<'_>, var_id: VarId, span: Span) -> Result<Value, ShellError> {
1351 match var_id {
1352 ENV_VARIABLE_ID => {
1354 let env_vars = ctx.stack.get_env_vars(ctx.engine_state);
1355 let env_columns = env_vars.keys();
1356 let env_values = env_vars.values();
1357
1358 let mut pairs = env_columns
1359 .map(|x| x.to_string())
1360 .zip(env_values.cloned())
1361 .collect::<Vec<(String, Value)>>();
1362
1363 pairs.sort_by(|a, b| a.0.cmp(&b.0));
1364
1365 Ok(Value::record(pairs.into_iter().collect(), span))
1366 }
1367 _ => ctx.stack.get_var(var_id, span).or_else(|err| {
1368 if let Some(const_val) = ctx.engine_state.get_constant(var_id).cloned() {
1370 Ok(const_val.with_span(span))
1371 } else {
1372 Err(err)
1373 }
1374 }),
1375 }
1376}
1377
1378fn get_env_var_case_insensitive<'a>(ctx: &'a mut EvalContext<'_>, key: &str) -> Option<&'a Value> {
1380 for overlays in ctx
1382 .stack
1383 .env_vars
1384 .iter()
1385 .rev()
1386 .chain(std::iter::once(&ctx.engine_state.env_vars))
1387 {
1388 for overlay_name in ctx.stack.active_overlays.iter().rev() {
1390 let Some(map) = overlays.get(overlay_name) else {
1391 continue;
1393 };
1394 let hidden = ctx.stack.env_hidden.get(overlay_name);
1395 let is_hidden = |key: &str| hidden.is_some_and(|hidden| hidden.contains(key));
1396
1397 if let Some(val) = map
1398 .get(key)
1400 .filter(|_| !is_hidden(key))
1402 .or_else(|| {
1403 map.iter().find_map(|(k, v)| {
1405 (k.eq_ignore_case(key) && !is_hidden(k)).then_some(v)
1407 })
1408 })
1409 {
1410 return Some(val);
1411 }
1412 }
1413 }
1414 None
1416}
1417
1418fn get_env_var_name_case_insensitive<'a>(ctx: &mut EvalContext<'_>, key: &'a str) -> Cow<'a, str> {
1422 ctx.stack
1424 .env_vars
1425 .iter()
1426 .rev()
1427 .chain(std::iter::once(&ctx.engine_state.env_vars))
1428 .flat_map(|overlays| {
1429 ctx.stack
1431 .active_overlays
1432 .iter()
1433 .rev()
1434 .filter_map(|name| overlays.get(name))
1435 })
1436 .find_map(|map| {
1437 if map.contains_key(key) {
1439 Some(Cow::Borrowed(key))
1440 } else {
1441 map.keys().find(|k| k.eq_ignore_case(key)).map(|k| {
1442 Cow::Owned(k.to_owned())
1444 })
1445 }
1446 })
1447 .unwrap_or(Cow::Borrowed(key))
1449}
1450
1451fn collect(data: PipelineData, fallback_span: Span) -> Result<PipelineData, ShellError> {
1455 let span = data.span().unwrap_or(fallback_span);
1456 let metadata = match data.metadata() {
1457 Some(PipelineMetadata {
1460 data_source: DataSource::FilePath(_),
1461 content_type: None,
1462 }) => None,
1463 other => other,
1464 };
1465 let value = data.into_value(span)?;
1466 Ok(PipelineData::Value(value, metadata))
1467}
1468
1469fn drain(ctx: &mut EvalContext<'_>, data: PipelineData) -> Result<InstructionResult, ShellError> {
1471 use self::InstructionResult::*;
1472 match data {
1473 PipelineData::ByteStream(stream, ..) => {
1474 let span = stream.span();
1475 let callback_spans = stream.get_caller_spans().clone();
1476 if let Err(mut err) = stream.drain() {
1477 ctx.stack.set_last_error(&err);
1478 if callback_spans.is_empty() {
1479 return Err(err);
1480 } else {
1481 for s in callback_spans {
1482 err = ShellError::EvalBlockWithInput {
1483 span: s,
1484 sources: vec![err],
1485 }
1486 }
1487 return Err(err);
1488 }
1489 } else {
1490 ctx.stack.set_last_exit_code(0, span);
1491 }
1492 }
1493 PipelineData::ListStream(stream, ..) => {
1494 let callback_spans = stream.get_caller_spans().clone();
1495 if let Err(mut err) = stream.drain() {
1496 if callback_spans.is_empty() {
1497 return Err(err);
1498 } else {
1499 for s in callback_spans {
1500 err = ShellError::EvalBlockWithInput {
1501 span: s,
1502 sources: vec![err],
1503 }
1504 }
1505 return Err(err);
1506 }
1507 }
1508 }
1509 PipelineData::Value(..) | PipelineData::Empty => {}
1510 }
1511 Ok(Continue)
1512}
1513
1514enum RedirectionStream {
1515 Out,
1516 Err,
1517}
1518
1519fn open_file(ctx: &EvalContext<'_>, path: &Value, append: bool) -> Result<Arc<File>, ShellError> {
1521 let path_expanded =
1522 expand_path_with(path.as_str()?, ctx.engine_state.cwd(Some(ctx.stack))?, true);
1523 let mut options = File::options();
1524 if append {
1525 options.append(true);
1526 } else {
1527 options.write(true).truncate(true);
1528 }
1529 let file = options
1530 .create(true)
1531 .open(&path_expanded)
1532 .map_err(|err| IoError::new(err, path.span(), path_expanded))?;
1533 Ok(Arc::new(file))
1534}
1535
1536fn eval_redirection(
1538 ctx: &mut EvalContext<'_>,
1539 mode: &RedirectMode,
1540 span: Span,
1541 which: RedirectionStream,
1542) -> Result<Option<Redirection>, ShellError> {
1543 match mode {
1544 RedirectMode::Pipe => Ok(Some(Redirection::Pipe(OutDest::Pipe))),
1545 RedirectMode::PipeSeparate => Ok(Some(Redirection::Pipe(OutDest::PipeSeparate))),
1546 RedirectMode::Value => Ok(Some(Redirection::Pipe(OutDest::Value))),
1547 RedirectMode::Null => Ok(Some(Redirection::Pipe(OutDest::Null))),
1548 RedirectMode::Inherit => Ok(Some(Redirection::Pipe(OutDest::Inherit))),
1549 RedirectMode::Print => Ok(Some(Redirection::Pipe(OutDest::Print))),
1550 RedirectMode::File { file_num } => {
1551 let file = ctx
1552 .files
1553 .get(*file_num as usize)
1554 .cloned()
1555 .flatten()
1556 .ok_or_else(|| ShellError::IrEvalError {
1557 msg: format!("Tried to redirect to file #{file_num}, but it is not open"),
1558 span: Some(span),
1559 })?;
1560 Ok(Some(Redirection::File(file)))
1561 }
1562 RedirectMode::Caller => Ok(match which {
1563 RedirectionStream::Out => ctx.stack.pipe_stdout().cloned().map(Redirection::Pipe),
1564 RedirectionStream::Err => ctx.stack.pipe_stderr().cloned().map(Redirection::Pipe),
1565 }),
1566 }
1567}
1568
1569fn eval_iterate(
1571 ctx: &mut EvalContext<'_>,
1572 dst: RegId,
1573 stream: RegId,
1574 end_index: usize,
1575) -> Result<InstructionResult, ShellError> {
1576 let mut data = ctx.take_reg(stream);
1577 if let PipelineData::ListStream(list_stream, _) = &mut data {
1578 if let Some(val) = list_stream.next_value() {
1580 ctx.put_reg(dst, val.into_pipeline_data());
1581 ctx.put_reg(stream, data); Ok(InstructionResult::Continue)
1583 } else {
1584 ctx.put_reg(dst, PipelineData::Empty);
1585 Ok(InstructionResult::Branch(end_index))
1586 }
1587 } else {
1588 let metadata = data.metadata();
1591 let span = data.span().unwrap_or(Span::unknown());
1592 ctx.put_reg(
1593 stream,
1594 PipelineData::ListStream(
1595 ListStream::new(data.into_iter(), span, Signals::EMPTY),
1596 metadata,
1597 ),
1598 );
1599 eval_iterate(ctx, dst, stream, end_index)
1600 }
1601}
1602
1603fn redirect_env(engine_state: &EngineState, caller_stack: &mut Stack, callee_stack: &Stack) {
1605 let caller_env_vars = caller_stack.get_env_var_names(engine_state);
1608
1609 for var in caller_env_vars.iter() {
1612 if !callee_stack.has_env_var(engine_state, var) {
1613 caller_stack.remove_env_var(engine_state, var);
1614 }
1615 }
1616
1617 for (var, value) in callee_stack.get_stack_env_vars() {
1619 caller_stack.add_env_var(var, value);
1620 }
1621
1622 caller_stack.config.clone_from(&callee_stack.config);
1624}