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| 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
1031 if let Some(block_id) = decl.block_id() {
1032 let block = engine_state.get_block(block_id);
1034
1035 check_input_types(&input, &block.signature, head)?;
1037
1038 let mut callee_stack = caller_stack.gather_captures(engine_state, &block.captures);
1040
1041 gather_arguments(
1042 engine_state,
1043 block,
1044 &mut caller_stack,
1045 &mut callee_stack,
1046 *args_base,
1047 args_len,
1048 head,
1049 )?;
1050
1051 callee_stack.recursion_count += 1;
1054
1055 result = 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 } else {
1062 check_input_types(&input, &decl.signature(), head)?;
1063
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 result = decl.run(engine_state, &mut caller_stack, &(&call).into(), input);
1085 };
1086
1087 drop(caller_stack);
1088
1089 ctx.stack.arguments.leave_frame(ctx.args_base);
1091 ctx.redirect_out = None;
1092 ctx.redirect_err = None;
1093
1094 result
1095}
1096
1097fn find_named_var_id(
1098 sig: &Signature,
1099 name: &[u8],
1100 short: &[u8],
1101 span: Span,
1102) -> Result<VarId, ShellError> {
1103 sig.named
1104 .iter()
1105 .find(|n| {
1106 if !n.long.is_empty() {
1107 n.long.as_bytes() == name
1108 } else {
1109 n.short
1111 .is_some_and(|s| s.encode_utf8(&mut [0; 4]).as_bytes() == short)
1112 }
1113 })
1114 .ok_or_else(|| ShellError::IrEvalError {
1115 msg: format!(
1116 "block does not have an argument named `{}`",
1117 String::from_utf8_lossy(name)
1118 ),
1119 span: Some(span),
1120 })
1121 .and_then(|flag| expect_named_var_id(flag, span))
1122}
1123
1124fn expect_named_var_id(arg: &Flag, span: Span) -> Result<VarId, ShellError> {
1125 arg.var_id.ok_or_else(|| ShellError::IrEvalError {
1126 msg: format!(
1127 "block signature is missing var id for named arg `{}`",
1128 arg.long
1129 ),
1130 span: Some(span),
1131 })
1132}
1133
1134fn expect_positional_var_id(arg: &PositionalArg, span: Span) -> Result<VarId, ShellError> {
1135 arg.var_id.ok_or_else(|| ShellError::IrEvalError {
1136 msg: format!(
1137 "block signature is missing var id for positional arg `{}`",
1138 arg.name
1139 ),
1140 span: Some(span),
1141 })
1142}
1143
1144fn gather_arguments(
1146 engine_state: &EngineState,
1147 block: &Block,
1148 caller_stack: &mut Stack,
1149 callee_stack: &mut Stack,
1150 args_base: usize,
1151 args_len: usize,
1152 call_head: Span,
1153) -> Result<(), ShellError> {
1154 let mut positional_iter = block
1155 .signature
1156 .required_positional
1157 .iter()
1158 .map(|p| (p, true))
1159 .chain(
1160 block
1161 .signature
1162 .optional_positional
1163 .iter()
1164 .map(|p| (p, false)),
1165 );
1166
1167 let mut rest = vec![];
1169
1170 let mut always_spread = false;
1172
1173 for arg in caller_stack.arguments.drain_args(args_base, args_len) {
1174 match arg {
1175 Argument::Positional { span, val, .. } => {
1176 let next = (!always_spread).then(|| positional_iter.next()).flatten();
1178 if let Some((positional_arg, required)) = next {
1179 let var_id = expect_positional_var_id(positional_arg, span)?;
1180 if required {
1181 let variable = engine_state.get_var(var_id);
1184 check_type(&val, &variable.ty)?;
1185 }
1186 callee_stack.add_var(var_id, val);
1187 } else {
1188 rest.push(val);
1189 }
1190 }
1191 Argument::Spread { vals, .. } => {
1192 if let Value::List { vals, .. } = vals {
1193 rest.extend(vals);
1194 always_spread = true;
1196 } else if let Value::Error { error, .. } = vals {
1197 return Err(*error);
1198 } else {
1199 return Err(ShellError::CannotSpreadAsList { span: vals.span() });
1200 }
1201 }
1202 Argument::Flag {
1203 data,
1204 name,
1205 short,
1206 span,
1207 } => {
1208 let var_id = find_named_var_id(&block.signature, &data[name], &data[short], span)?;
1209 callee_stack.add_var(var_id, Value::bool(true, span))
1210 }
1211 Argument::Named {
1212 data,
1213 name,
1214 short,
1215 span,
1216 val,
1217 ..
1218 } => {
1219 let var_id = find_named_var_id(&block.signature, &data[name], &data[short], span)?;
1220 callee_stack.add_var(var_id, val)
1221 }
1222 Argument::ParserInfo { .. } => (),
1223 }
1224 }
1225
1226 if let Some(rest_arg) = &block.signature.rest_positional {
1228 let rest_span = rest.first().map(|v| v.span()).unwrap_or(call_head);
1229 let var_id = expect_positional_var_id(rest_arg, rest_span)?;
1230 callee_stack.add_var(var_id, Value::list(rest, rest_span));
1231 }
1232
1233 for (positional_arg, _) in positional_iter {
1235 let var_id = expect_positional_var_id(positional_arg, call_head)?;
1236 callee_stack.add_var(
1237 var_id,
1238 positional_arg
1239 .default_value
1240 .clone()
1241 .unwrap_or(Value::nothing(call_head)),
1242 );
1243 }
1244
1245 for named_arg in &block.signature.named {
1246 if let Some(var_id) = named_arg.var_id {
1247 if !callee_stack.vars.iter().any(|(id, _)| *id == var_id) {
1251 let val = if named_arg.arg.is_none() {
1252 Value::bool(false, call_head)
1253 } else if let Some(value) = &named_arg.default_value {
1254 value.clone()
1255 } else {
1256 Value::nothing(call_head)
1257 };
1258 callee_stack.add_var(var_id, val);
1259 }
1260 }
1261 }
1262
1263 Ok(())
1264}
1265
1266fn check_type(val: &Value, ty: &Type) -> Result<(), ShellError> {
1268 match val {
1269 Value::Error { error, .. } => Err(*error.clone()),
1270 _ if val.is_subtype_of(ty) => Ok(()),
1271 _ => Err(ShellError::CantConvert {
1272 to_type: ty.to_string(),
1273 from_type: val.get_type().to_string(),
1274 span: val.span(),
1275 help: None,
1276 }),
1277 }
1278}
1279
1280fn check_input_types(
1282 input: &PipelineData,
1283 signature: &Signature,
1284 head: Span,
1285) -> Result<(), ShellError> {
1286 let io_types = &signature.input_output_types;
1287
1288 if io_types.is_empty() {
1290 return Ok(());
1291 }
1292
1293 if io_types.iter().all(|(intype, _)| intype == &Type::Nothing) {
1295 return Ok(());
1296 }
1297
1298 match input {
1299 PipelineData::Value(Value::Error { error, .. }, ..) => return Err(*error.clone()),
1301 PipelineData::Value(Value::Custom { .. }, ..) => return Ok(()),
1303 _ => (),
1304 }
1305
1306 if io_types
1308 .iter()
1309 .any(|(command_type, _)| input.is_subtype_of(command_type))
1310 {
1311 return Ok(());
1312 }
1313
1314 let mut input_types = io_types
1315 .iter()
1316 .map(|(input, _)| input.to_string())
1317 .collect::<Vec<String>>();
1318
1319 let expected_string = match input_types.len() {
1320 0 => {
1321 return Err(ShellError::NushellFailed {
1322 msg: "Command input type strings is empty, despite being non-zero earlier"
1323 .to_string(),
1324 })
1325 }
1326 1 => input_types.swap_remove(0),
1327 2 => input_types.join(" and "),
1328 _ => {
1329 input_types
1330 .last_mut()
1331 .expect("Vector with length >2 has no elements")
1332 .insert_str(0, "and ");
1333 input_types.join(", ")
1334 }
1335 };
1336
1337 match input {
1338 PipelineData::Empty => Err(ShellError::PipelineEmpty { dst_span: head }),
1339 _ => Err(ShellError::OnlySupportsThisInputType {
1340 exp_input_type: expected_string,
1341 wrong_type: input.get_type().to_string(),
1342 dst_span: head,
1343 src_span: input.span().unwrap_or(Span::unknown()),
1344 }),
1345 }
1346}
1347
1348fn get_var(ctx: &EvalContext<'_>, var_id: VarId, span: Span) -> Result<Value, ShellError> {
1350 match var_id {
1351 ENV_VARIABLE_ID => {
1353 let env_vars = ctx.stack.get_env_vars(ctx.engine_state);
1354 let env_columns = env_vars.keys();
1355 let env_values = env_vars.values();
1356
1357 let mut pairs = env_columns
1358 .map(|x| x.to_string())
1359 .zip(env_values.cloned())
1360 .collect::<Vec<(String, Value)>>();
1361
1362 pairs.sort_by(|a, b| a.0.cmp(&b.0));
1363
1364 Ok(Value::record(pairs.into_iter().collect(), span))
1365 }
1366 _ => ctx.stack.get_var(var_id, span).or_else(|err| {
1367 if let Some(const_val) = ctx.engine_state.get_constant(var_id).cloned() {
1369 Ok(const_val.with_span(span))
1370 } else {
1371 Err(err)
1372 }
1373 }),
1374 }
1375}
1376
1377fn get_env_var_case_insensitive<'a>(ctx: &'a mut EvalContext<'_>, key: &str) -> Option<&'a Value> {
1379 for overlays in ctx
1381 .stack
1382 .env_vars
1383 .iter()
1384 .rev()
1385 .chain(std::iter::once(&ctx.engine_state.env_vars))
1386 {
1387 for overlay_name in ctx.stack.active_overlays.iter().rev() {
1389 let Some(map) = overlays.get(overlay_name) else {
1390 continue;
1392 };
1393 let hidden = ctx.stack.env_hidden.get(overlay_name);
1394 let is_hidden = |key: &str| hidden.is_some_and(|hidden| hidden.contains(key));
1395
1396 if let Some(val) = map
1397 .get(key)
1399 .filter(|_| !is_hidden(key))
1401 .or_else(|| {
1402 map.iter().find_map(|(k, v)| {
1404 (k.eq_ignore_case(key) && !is_hidden(k)).then_some(v)
1406 })
1407 })
1408 {
1409 return Some(val);
1410 }
1411 }
1412 }
1413 None
1415}
1416
1417fn get_env_var_name_case_insensitive<'a>(ctx: &mut EvalContext<'_>, key: &'a str) -> Cow<'a, str> {
1421 ctx.stack
1423 .env_vars
1424 .iter()
1425 .rev()
1426 .chain(std::iter::once(&ctx.engine_state.env_vars))
1427 .flat_map(|overlays| {
1428 ctx.stack
1430 .active_overlays
1431 .iter()
1432 .rev()
1433 .filter_map(|name| overlays.get(name))
1434 })
1435 .find_map(|map| {
1436 if map.contains_key(key) {
1438 Some(Cow::Borrowed(key))
1439 } else {
1440 map.keys().find(|k| k.eq_ignore_case(key)).map(|k| {
1441 Cow::Owned(k.to_owned())
1443 })
1444 }
1445 })
1446 .unwrap_or(Cow::Borrowed(key))
1448}
1449
1450fn collect(data: PipelineData, fallback_span: Span) -> Result<PipelineData, ShellError> {
1454 let span = data.span().unwrap_or(fallback_span);
1455 let metadata = match data.metadata() {
1456 Some(PipelineMetadata {
1459 data_source: DataSource::FilePath(_),
1460 content_type: None,
1461 }) => None,
1462 other => other,
1463 };
1464 let value = data.into_value(span)?;
1465 Ok(PipelineData::Value(value, metadata))
1466}
1467
1468fn drain(ctx: &mut EvalContext<'_>, data: PipelineData) -> Result<InstructionResult, ShellError> {
1470 use self::InstructionResult::*;
1471 match data {
1472 PipelineData::ByteStream(stream, ..) => {
1473 let span = stream.span();
1474 let callback_spans = stream.get_caller_spans().clone();
1475 if let Err(mut err) = stream.drain() {
1476 ctx.stack.set_last_error(&err);
1477 if callback_spans.is_empty() {
1478 return Err(err);
1479 } else {
1480 for s in callback_spans {
1481 err = ShellError::EvalBlockWithInput {
1482 span: s,
1483 sources: vec![err],
1484 }
1485 }
1486 return Err(err);
1487 }
1488 } else {
1489 ctx.stack.set_last_exit_code(0, span);
1490 }
1491 }
1492 PipelineData::ListStream(stream, ..) => {
1493 let callback_spans = stream.get_caller_spans().clone();
1494 if let Err(mut err) = stream.drain() {
1495 if callback_spans.is_empty() {
1496 return Err(err);
1497 } else {
1498 for s in callback_spans {
1499 err = ShellError::EvalBlockWithInput {
1500 span: s,
1501 sources: vec![err],
1502 }
1503 }
1504 return Err(err);
1505 }
1506 }
1507 }
1508 PipelineData::Value(..) | PipelineData::Empty => {}
1509 }
1510 Ok(Continue)
1511}
1512
1513enum RedirectionStream {
1514 Out,
1515 Err,
1516}
1517
1518fn open_file(ctx: &EvalContext<'_>, path: &Value, append: bool) -> Result<Arc<File>, ShellError> {
1520 let path_expanded =
1521 expand_path_with(path.as_str()?, ctx.engine_state.cwd(Some(ctx.stack))?, true);
1522 let mut options = File::options();
1523 if append {
1524 options.append(true);
1525 } else {
1526 options.write(true).truncate(true);
1527 }
1528 let file = options
1529 .create(true)
1530 .open(&path_expanded)
1531 .map_err(|err| IoError::new(err.kind(), path.span(), path_expanded))?;
1532 Ok(Arc::new(file))
1533}
1534
1535fn eval_redirection(
1537 ctx: &mut EvalContext<'_>,
1538 mode: &RedirectMode,
1539 span: Span,
1540 which: RedirectionStream,
1541) -> Result<Option<Redirection>, ShellError> {
1542 match mode {
1543 RedirectMode::Pipe => Ok(Some(Redirection::Pipe(OutDest::Pipe))),
1544 RedirectMode::PipeSeparate => Ok(Some(Redirection::Pipe(OutDest::PipeSeparate))),
1545 RedirectMode::Value => Ok(Some(Redirection::Pipe(OutDest::Value))),
1546 RedirectMode::Null => Ok(Some(Redirection::Pipe(OutDest::Null))),
1547 RedirectMode::Inherit => Ok(Some(Redirection::Pipe(OutDest::Inherit))),
1548 RedirectMode::Print => Ok(Some(Redirection::Pipe(OutDest::Print))),
1549 RedirectMode::File { file_num } => {
1550 let file = ctx
1551 .files
1552 .get(*file_num as usize)
1553 .cloned()
1554 .flatten()
1555 .ok_or_else(|| ShellError::IrEvalError {
1556 msg: format!("Tried to redirect to file #{file_num}, but it is not open"),
1557 span: Some(span),
1558 })?;
1559 Ok(Some(Redirection::File(file)))
1560 }
1561 RedirectMode::Caller => Ok(match which {
1562 RedirectionStream::Out => ctx.stack.pipe_stdout().cloned().map(Redirection::Pipe),
1563 RedirectionStream::Err => ctx.stack.pipe_stderr().cloned().map(Redirection::Pipe),
1564 }),
1565 }
1566}
1567
1568fn eval_iterate(
1570 ctx: &mut EvalContext<'_>,
1571 dst: RegId,
1572 stream: RegId,
1573 end_index: usize,
1574) -> Result<InstructionResult, ShellError> {
1575 let mut data = ctx.take_reg(stream);
1576 if let PipelineData::ListStream(list_stream, _) = &mut data {
1577 if let Some(val) = list_stream.next_value() {
1579 ctx.put_reg(dst, val.into_pipeline_data());
1580 ctx.put_reg(stream, data); Ok(InstructionResult::Continue)
1582 } else {
1583 ctx.put_reg(dst, PipelineData::Empty);
1584 Ok(InstructionResult::Branch(end_index))
1585 }
1586 } else {
1587 let metadata = data.metadata();
1590 let span = data.span().unwrap_or(Span::unknown());
1591 ctx.put_reg(
1592 stream,
1593 PipelineData::ListStream(
1594 ListStream::new(data.into_iter(), span, Signals::EMPTY),
1595 metadata,
1596 ),
1597 );
1598 eval_iterate(ctx, dst, stream, end_index)
1599 }
1600}
1601
1602fn redirect_env(engine_state: &EngineState, caller_stack: &mut Stack, callee_stack: &Stack) {
1604 let caller_env_vars = caller_stack.get_env_var_names(engine_state);
1607
1608 for var in caller_env_vars.iter() {
1611 if !callee_stack.has_env_var(engine_state, var) {
1612 caller_stack.remove_env_var(engine_state, var);
1613 }
1614 }
1615
1616 for (var, value) in callee_stack.get_stack_env_vars() {
1618 caller_stack.add_env_var(var, value);
1619 }
1620
1621 caller_stack.config.clone_from(&callee_stack.config);
1623}