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