1pub mod abend;
5pub use rt::calendar;
6pub use rt::cics;
7pub use rt::digest;
8pub use rt::evidence;
9pub use compile::collating;
10pub use compile::constructs;
11pub use compile::declaratives;
12#[cfg(test)]
13mod edit;
14pub use rt::files;
15pub use compile::layout;
16pub mod le;
17pub use rt::lir;
18pub mod loader;
19pub use compile::lower;
20pub mod machine;
21pub use rt::module;
22pub use rt::refusal;
23pub mod oo;
24pub use compile::picture;
25pub mod printer;
26pub mod report;
27use compile::sort;
28pub mod sql;
29pub mod terminal;
30#[cfg(test)]
31mod testing;
32pub use rt::tn3270;
33pub mod unit;
34pub mod vm;
35
36pub use compile::{Compiled, compile, compile_at, compile_time, entry_points, read_lengths, variable_records};
37pub(crate) use compile::{procedure, procedure_from, section_end};
38pub use machine::{Abend, Ending};
39
40use abend::AbendCode;
41use unit::AddProgram;
42use std::io::{BufRead, Write};
43use syntax::Pos;
44
45pub trait Execute {
47 fn run(&self, out: &mut dyn Write, err: &mut dyn Write) -> Result<Ending, Abend>;
48
49 fn run_with(&self, dds: files::Dds, out: &mut dyn Write, err: &mut dyn Write) -> Result<Ending, Abend>;
51
52 fn execute<'w>(
55 &self,
56 library: unit::Library,
57 dds: files::Dds,
58 sysin: Option<Box<dyn BufRead + 'w>>,
59 clock: unit::Clock,
60 out: &'w mut dyn Write,
61 err: &'w mut dyn Write,
62 ) -> Result<(Ending, i16), Abend>;
63
64 #[allow(clippy::too_many_arguments)]
66 fn execute_with<'w>(
67 &self,
68 library: unit::Library,
69 dds: files::Dds,
70 sysin: Option<Box<dyn BufRead + 'w>>,
71 clock: unit::Clock,
72 database: Option<&'w mut (dyn sql::Database + '_)>,
73 out: &'w mut dyn Write,
74 err: &'w mut dyn Write,
75 ) -> Result<(Ending, i16), Abend>;
76
77 #[allow(clippy::too_many_arguments)]
80 fn execute_observed<'w>(
81 &self,
82 library: unit::Library,
83 dds: files::Dds,
84 sysin: Option<Box<dyn BufRead + 'w>>,
85 clock: unit::Clock,
86 database: Option<&'w mut (dyn sql::Database + '_)>,
87 out: &'w mut dyn Write,
88 err: &'w mut dyn Write,
89 observer: Option<unit::Observer<'w>>,
90 ) -> Result<(Ending, i16), Abend>;
91
92 #[allow(clippy::too_many_arguments)]
96 fn execute_main<'w>(
97 &self,
98 library: unit::Library,
99 dds: files::Dds,
100 sysin: Option<Box<dyn BufRead + 'w>>,
101 clock: unit::Clock,
102 database: Option<&'w mut (dyn sql::Database + '_)>,
103 out: &'w mut dyn Write,
104 err: &'w mut dyn Write,
105 observer: Option<unit::Observer<'w>>,
106 parm: &str,
107 ) -> Result<(Ending, i16), Abend>;
108
109 #[allow(clippy::too_many_arguments)]
115 fn execute_with_arguments<'w>(
116 &self,
117 library: unit::Library,
118 dds: files::Dds,
119 sysin: Option<Box<dyn BufRead + 'w>>,
120 clock: unit::Clock,
121 database: Option<&'w mut (dyn sql::Database + '_)>,
122 out: &'w mut dyn Write,
123 err: &'w mut dyn Write,
124 observer: Option<unit::Observer<'w>>,
125 arguments: &[Option<Vec<u8>>],
126 kept: &mut Option<unit::Remains>,
127 ) -> Result<(Ending, i16), Abend>;
128
129 #[allow(clippy::too_many_arguments)]
132 fn execute_kept<'w>(
133 &self,
134 library: unit::Library,
135 dds: files::Dds,
136 sysin: Option<Box<dyn BufRead + 'w>>,
137 clock: unit::Clock,
138 database: Option<&'w mut (dyn sql::Database + '_)>,
139 out: &'w mut dyn Write,
140 err: &'w mut dyn Write,
141 observer: Option<unit::Observer<'w>>,
142 kept: &mut Option<unit::Remains>,
143 ) -> Result<(Ending, i16), Abend>;
144
145 fn execute_cics<'w>(
149 &self,
150 library: unit::Library,
151 dds: files::Dds,
152 task: cics::Task,
153 clock: unit::Clock,
154 out: &'w mut dyn Write,
155 err: &'w mut dyn Write,
156 ) -> Result<(Ending, cics::Task), Abend>;
157
158 #[allow(clippy::too_many_arguments)]
162 fn execute_cics_with<'w>(
163 &self,
164 library: unit::Library,
165 dds: files::Dds,
166 task: cics::Task,
167 clock: unit::Clock,
168 database: Option<&'w mut (dyn sql::Database + '_)>,
169 out: &'w mut dyn Write,
170 err: &'w mut dyn Write,
171 ) -> Result<(Ending, cics::Task), Abend>;
172
173 #[allow(clippy::too_many_arguments)]
176 fn execute_cics_observed<'w>(
177 &self,
178 library: unit::Library,
179 dds: files::Dds,
180 task: cics::Task,
181 clock: unit::Clock,
182 database: Option<&'w mut (dyn sql::Database + '_)>,
183 out: &'w mut dyn Write,
184 err: &'w mut dyn Write,
185 observer: Option<unit::Observer<'w>>,
186 ) -> Result<(Ending, cics::Task), Abend>;
187}
188
189impl Execute for Compiled {
190 fn run(&self, out: &mut dyn Write, err: &mut dyn Write) -> Result<Ending, Abend> {
191 self.run_with(files::Dds::default(), out, err)
192 }
193
194 fn run_with(&self, dds: files::Dds, out: &mut dyn Write, err: &mut dyn Write) -> Result<Ending, Abend> {
195 self.execute(unit::Library::default(), dds, None, unit::Clock::System, out, err).map(|(ending, _)| ending)
196 }
197
198 fn execute<'w>(
199 &self,
200 library: unit::Library,
201 dds: files::Dds,
202 sysin: Option<Box<dyn BufRead + 'w>>,
203 clock: unit::Clock,
204 out: &'w mut dyn Write,
205 err: &'w mut dyn Write,
206 ) -> Result<(Ending, i16), Abend> {
207 self.execute_with(library, dds, sysin, clock, None, out, err)
208 }
209
210 fn execute_with<'w>(
211 &self,
212 library: unit::Library,
213 dds: files::Dds,
214 sysin: Option<Box<dyn BufRead + 'w>>,
215 clock: unit::Clock,
216 database: Option<&'w mut (dyn sql::Database + '_)>,
217 out: &'w mut dyn Write,
218 err: &'w mut dyn Write,
219 ) -> Result<(Ending, i16), Abend> {
220 self.execute_observed(library, dds, sysin, clock, database, out, err, None)
221 }
222
223 fn execute_observed<'w>(
224 &self,
225 library: unit::Library,
226 dds: files::Dds,
227 sysin: Option<Box<dyn BufRead + 'w>>,
228 clock: unit::Clock,
229 database: Option<&'w mut (dyn sql::Database + '_)>,
230 out: &'w mut dyn Write,
231 err: &'w mut dyn Write,
232 observer: Option<unit::Observer<'w>>,
233 ) -> Result<(Ending, i16), Abend> {
234 run_main(self, library, dds, sysin, clock, database, out, err, observer, Passed::Nothing, &mut None)
235 }
236
237 fn execute_main<'w>(
238 &self,
239 library: unit::Library,
240 dds: files::Dds,
241 sysin: Option<Box<dyn BufRead + 'w>>,
242 clock: unit::Clock,
243 database: Option<&'w mut (dyn sql::Database + '_)>,
244 out: &'w mut dyn Write,
245 err: &'w mut dyn Write,
246 observer: Option<unit::Observer<'w>>,
247 parm: &str,
248 ) -> Result<(Ending, i16), Abend> {
249 run_main(self, library, dds, sysin, clock, database, out, err, observer, Passed::Parm(parm), &mut None)
250 }
251
252 fn execute_with_arguments<'w>(
253 &self,
254 library: unit::Library,
255 dds: files::Dds,
256 sysin: Option<Box<dyn BufRead + 'w>>,
257 clock: unit::Clock,
258 database: Option<&'w mut (dyn sql::Database + '_)>,
259 out: &'w mut dyn Write,
260 err: &'w mut dyn Write,
261 observer: Option<unit::Observer<'w>>,
262 arguments: &[Option<Vec<u8>>],
263 kept: &mut Option<unit::Remains>,
264 ) -> Result<(Ending, i16), Abend> {
265 run_main(self, library, dds, sysin, clock, database, out, err, observer, Passed::Arguments(arguments), kept)
266 }
267
268 fn execute_kept<'w>(
269 &self,
270 library: unit::Library,
271 dds: files::Dds,
272 sysin: Option<Box<dyn BufRead + 'w>>,
273 clock: unit::Clock,
274 database: Option<&'w mut (dyn sql::Database + '_)>,
275 out: &'w mut dyn Write,
276 err: &'w mut dyn Write,
277 observer: Option<unit::Observer<'w>>,
278 kept: &mut Option<unit::Remains>,
279 ) -> Result<(Ending, i16), Abend> {
280 run_main(self, library, dds, sysin, clock, database, out, err, observer, Passed::Nothing, kept)
281 }
282
283 fn execute_cics<'w>(
284 &self,
285 library: unit::Library,
286 dds: files::Dds,
287 task: cics::Task,
288 clock: unit::Clock,
289 out: &'w mut dyn Write,
290 err: &'w mut dyn Write,
291 ) -> Result<(Ending, cics::Task), Abend> {
292 self.execute_cics_with(library, dds, task, clock, None, out, err)
293 }
294
295 fn execute_cics_with<'w>(
296 &self,
297 library: unit::Library,
298 dds: files::Dds,
299 task: cics::Task,
300 clock: unit::Clock,
301 database: Option<&'w mut (dyn sql::Database + '_)>,
302 out: &'w mut dyn Write,
303 err: &'w mut dyn Write,
304 ) -> Result<(Ending, cics::Task), Abend> {
305 self.execute_cics_observed(library, dds, task, clock, database, out, err, None)
306 }
307
308 fn execute_cics_observed<'w>(
309 &self,
310 library: unit::Library,
311 dds: files::Dds,
312 task: cics::Task,
313 clock: unit::Clock,
314 database: Option<&'w mut (dyn sql::Database + '_)>,
315 out: &'w mut dyn Write,
316 err: &'w mut dyn Write,
317 observer: Option<unit::Observer<'w>>,
318 ) -> Result<(Ending, cics::Task), Abend> {
319 oo::refuse_to_run(&self.program)?;
320 let (ending, task) = execute_task(self, library, dds, task, clock, database, out, err, observer, &mut None);
321 ending.map(|e| (e, task))
322 }
323}
324
325#[allow(clippy::too_many_arguments)]
329pub(crate) fn execute_task<'w>(
330 compiled: &Compiled,
331 library: unit::Library,
332 dds: files::Dds,
333 task: cics::Task,
334 clock: unit::Clock,
335 database: Option<&'w mut (dyn sql::Database + '_)>,
336 out: &'w mut dyn Write,
337 err: &'w mut dyn Write,
338 observer: Option<unit::Observer<'w>>,
339 kept: &mut Option<unit::Remains>,
340) -> (Result<Ending, Abend>, cics::Task) {
341 let (statements, taint) = (library.trace_statements.clone(), library.trace_input.then(rt::taint::Taint::default));
342 let limits = (library.statement_limit, library.time_limit, library.storage_limit);
343 let mut run_unit = unit::RunUnit::new(library, dds, None, clock, out, err);
344 run_unit.observer = observer;
345 run_unit.statements = statements;
346 run_unit.taint = taint;
347 run_unit.limit(limits.0, limits.1, limits.2);
348 run_unit.sql = database.map(sql::Session::new);
349 let (ending, ended, task) = run_task(First::of(compiled), run_unit, task, kept, |unit, me, commarea, length| {
350 machine::Machine::activation(compiled, me, unit, true).and_then(|mut m| {
351 m.begin_task(commarea, length);
352 m.run_level()
353 })
354 });
355 (ending.map_err(asra).and_then(|e| ended.map(|()| e)), task)
356}
357
358pub(crate) fn run_task<'w, H: Clone, L: unit::Loader<H>, E>(
363 first: First<'_>,
364 mut run_unit: rt::unit::RunUnit<'w, H, L>,
365 mut task: cics::Task,
366 kept: &mut Option<unit::Remains>,
367 run: impl FnOnce(&mut rt::unit::RunUnit<'w, H, L>, usize, Option<usize>, usize) -> Result<Ending, E>,
368) -> (Result<Ending, E>, Result<(), Abend>, cics::Task) {
369 let me = run_unit.add_named(None, first.name, first.files, first.size);
370 run_unit.programs[me].source = first.source;
371 run_unit.eib = run_unit.push_temporary(&[0; cics::EIB_LEN]);
372 let commarea = task.commarea.take();
373 let length = commarea.as_ref().map_or(0, Vec::len);
374 let commarea = commarea.map(|c| {
375 let at = run_unit.push_temporary(&c);
376 run_unit.mark_input(at, c.len(), true);
377 at
378 });
379 run_unit.cics = Some(task);
380 let ending = run(&mut run_unit, me, commarea, length);
381 let settled = run_unit.sql.as_mut().map_or(Ok(()), |s| s.end_task(first.id, ending.is_ok()).map(drop));
382 let mut closed = run_unit.close_all(false);
383 for (name, f) in run_unit.cics_files.drain() {
384 if let Err(e) = f.close() {
385 closed = closed.and(Err(format!("closing CICS file {name}: {e}")));
386 }
387 }
388 *kept = Some(unit::Remains::of(&run_unit));
389 let mut task = run_unit.cics.take().unwrap_or_default();
390 if let Err(e) = task.flush_td(first.page) {
391 closed = closed.and(Err(format!("writing transient data: {e}")));
392 }
393 let settled = settled.map_err(|a| Abend { code: a.code.into(), message: a.message, pos: Pos::default(), file: None });
394 let closed = closed.map_err(|m| Abend { code: AbendCode::Ironwork, message: m, pos: Pos::default(), file: None });
395 (ending, settled.and(closed), task)
396}
397
398pub(crate) struct First<'a> {
402 pub id: &'a str,
403 pub name: String,
404 pub files: usize,
405 pub size: usize,
406 pub page: &'static zarch::ebcdic::CodePage,
407 pub source: Option<std::path::PathBuf>,
408}
409
410impl<'a> First<'a> {
411 pub(crate) fn of(compiled: &'a Compiled) -> Self {
412 let p = &compiled.program;
413 Self { id: &p.id, name: p.id.to_ascii_uppercase(), files: p.files.len(), size: compiled.layout.size as usize, page: compiled.options.code_page(), source: None }
414 }
415}
416
417pub(crate) fn asra(a: Abend) -> Abend {
420 match a.code {
421 AbendCode::Check(_) | AbendCode::Protection => Abend { message: format!("{} ({}, which CICS reports as ASRA)", a.message, a.code), code: AbendCode::Cics("ASRA".into()), pos: a.pos, file: a.file },
422 ref u if *u == "U4038" => Abend { message: format!("{} (U4038, which CICS reports as transaction abend 4038)", a.message), code: AbendCode::Cics("4038".into()), pos: a.pos, file: a.file },
423 _ => a,
424 }
425}
426
427#[derive(Clone, Copy, Debug, Default)]
429pub enum Passed<'a> {
430 #[default]
432 Nothing,
433 Parm(&'a str),
435 Arguments(&'a [Option<Vec<u8>>]),
438}
439
440impl Passed<'_> {
441 pub(crate) fn addresses<H: Clone, L: rt::unit::Loader<H>>(self, run_unit: &mut rt::unit::RunUnit<'_, H, L>, page: &zarch::ebcdic::CodePage) -> Vec<Option<usize>> {
443 match self {
444 Passed::Nothing => Vec::new(),
445 Passed::Parm(parm) => vec![Some(push_parm(run_unit, page, parm))],
446 Passed::Arguments(arguments) => arguments.iter().map(|a| a.as_deref().map(|bytes| push_input(run_unit, bytes))).collect(),
447 }
448 }
449
450 pub(crate) fn main(self) -> bool {
453 !matches!(self, Passed::Arguments(_))
454 }
455
456 pub(crate) fn returned(self, addresses: &[Option<usize>], mem: &[u8]) -> Vec<Option<Vec<u8>>> {
459 let Passed::Arguments(arguments) = self else { return Vec::new() };
460 arguments
461 .iter()
462 .zip(addresses)
463 .map(|(a, at)| match (a, at) {
464 (Some(a), Some(at)) => mem.get(*at..at + a.len()).map(<[u8]>::to_vec),
465 _ => None,
466 })
467 .collect()
468 }
469
470 pub(crate) fn apply_parm<H: Clone, L: rt::unit::Loader<H>>(self, run_unit: &mut rt::unit::RunUnit<'_, H, L>) {
474 let Passed::Parm(parm) = self else { return };
475 run_unit.arguments = rt::le::parm::Arguments::of(parm);
476 match rt::le::parm::upsi(parm) {
477 Some(Ok(on)) => run_unit.set_switches(on),
478 Some(Err(m)) => {
479 let _ = writeln!(run_unit.err, "ironwork: {m}");
480 }
481 None => {}
482 }
483 }
484}
485
486#[allow(clippy::too_many_arguments)]
489fn run_main<'w>(
490 compiled: &Compiled,
491 library: unit::Library,
492 dds: files::Dds,
493 sysin: Option<Box<dyn BufRead + 'w>>,
494 clock: unit::Clock,
495 database: Option<&'w mut (dyn sql::Database + '_)>,
496 out: &'w mut dyn Write,
497 err: &'w mut dyn Write,
498 observer: Option<unit::Observer<'w>>,
499 passed: Passed<'_>,
500 kept: &mut Option<unit::Remains>,
501) -> Result<(Ending, i16), Abend> {
502 oo::refuse_to_run(&compiled.program)?;
503 let (statements, taint) = (library.trace_statements.clone(), library.trace_input.then(rt::taint::Taint::default));
504 let (limits, screen, environment) = ((library.statement_limit, library.time_limit, library.storage_limit), library.screen.clone(), library.environment.clone());
505 let mut run_unit = unit::RunUnit::new(library, dds, sysin, clock, out, err);
506 run_unit.crt = screen;
507 run_unit.environment = rt::environment::Environment::of(environment);
508 run_unit.observer = observer;
509 run_unit.statements = statements;
510 run_unit.taint = taint;
511 run_unit.limit(limits.0, limits.1, limits.2);
512 run_unit.sql = database.map(sql::Session::new);
513 let me = run_unit.add(None, &compiled.program, compiled.layout.size as usize);
514 let trap_off = matches!(passed, Passed::Parm(p) if rt::le::parm::trap_off(p));
515 passed.apply_parm(&mut run_unit);
516 let addresses = passed.addresses(&mut run_unit, compiled.options.code_page());
517 let ending = machine::Machine::activation(compiled, me, &mut run_unit, passed.main()).and_then(|mut m| {
518 if !addresses.is_empty() {
519 m.bind(&addresses);
520 }
521 m.run_procedure()
522 });
523 let last_paragraph = compiled.program.paragraphs.last().map(|p| p.pos);
524 let ending = ending.and_then(|e| past_the_end(e, passed.main(), &compiled.program.id, last_paragraph));
525 let settled = run_unit.sql.as_mut().map_or(Ok(()), |s| s.settle(&compiled.program.id, ending.is_ok()).map(drop));
526 let closed = run_unit.close_all(ending.as_ref().is_err_and(|a| a.code.skips_termination(trap_off)));
527 *kept = Some(unit::Remains { arguments: passed.returned(&addresses, &run_unit.mem), ..unit::Remains::of(&run_unit) });
528 let ending = ending?;
529 settled.map_err(|a| Abend { code: a.code.into(), message: a.message, pos: Pos::default(), file: None })?;
530 closed.map_err(|m| Abend { code: AbendCode::Ironwork, message: m, pos: Pos::default(), file: None })?;
531 Ok((ending, run_unit.return_code()))
532}
533
534pub(crate) fn past_the_end(ending: Ending, main: bool, program: &str, last_paragraph: Option<Pos>) -> Result<Ending, Abend> {
538 if main && ending == Ending::EndOfProgram {
539 let message = format!("IGZ0037S The flow of control in program {} proceeded beyond the last line of the program.", program.to_ascii_uppercase());
540 return Err(Abend { code: AbendCode::user(4038), message, pos: last_paragraph.unwrap_or_default(), file: None });
541 }
542 Ok(ending)
543}
544
545pub(crate) fn push_parm<H: Clone, L: rt::unit::Loader<H>>(run_unit: &mut rt::unit::RunUnit<'_, H, L>, page: &zarch::ebcdic::CodePage, parm: &str) -> usize {
547 let area = rt::le::parm::parameter_area(rt::le::parm::program_arguments(parm), page);
548 push_input(run_unit, &area)
549}
550
551fn push_input<H: Clone, L: rt::unit::Loader<H>>(run_unit: &mut rt::unit::RunUnit<'_, H, L>, bytes: &[u8]) -> usize {
553 let at = run_unit.push_temporary(bytes);
554 run_unit.mark_input(at, bytes.len(), true);
555 at
556}
557
558#[cfg(test)]
559mod tests;