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::declaratives;
11#[cfg(test)]
12mod edit;
13pub use rt::files;
14pub use compile::layout;
15pub mod le;
16pub use rt::lir;
17pub mod loader;
18pub mod lower;
19pub mod machine;
20pub use rt::module;
21pub mod oo;
22pub use compile::picture;
23pub mod printer;
24pub mod report;
25use compile::sort;
26pub mod sql;
27pub mod terminal;
28#[cfg(test)]
29mod testing;
30pub use rt::tn3270;
31pub mod unit;
32pub mod vm;
33
34pub use compile::{Compiled, compile, compile_at, compile_time, entry_points, read_lengths, variable_records};
35pub(crate) use compile::{procedure, procedure_from, section_end};
36pub use machine::{Abend, Ending};
37
38use abend::AbendCode;
39use unit::AddProgram;
40use std::io::{BufRead, Write};
41use syntax::Pos;
42
43pub trait Execute {
45 fn run(&self, out: &mut dyn Write, err: &mut dyn Write) -> Result<Ending, Abend>;
46
47 fn run_with(&self, dds: files::Dds, out: &mut dyn Write, err: &mut dyn Write) -> Result<Ending, Abend>;
49
50 fn execute<'w>(
53 &self,
54 library: unit::Library,
55 dds: files::Dds,
56 sysin: Option<Box<dyn BufRead + 'w>>,
57 clock: unit::Clock,
58 out: &'w mut dyn Write,
59 err: &'w mut dyn Write,
60 ) -> Result<(Ending, i16), Abend>;
61
62 #[allow(clippy::too_many_arguments)]
64 fn execute_with<'w>(
65 &self,
66 library: unit::Library,
67 dds: files::Dds,
68 sysin: Option<Box<dyn BufRead + 'w>>,
69 clock: unit::Clock,
70 database: Option<&'w mut (dyn sql::Database + '_)>,
71 out: &'w mut dyn Write,
72 err: &'w mut dyn Write,
73 ) -> Result<(Ending, i16), Abend>;
74
75 #[allow(clippy::too_many_arguments)]
78 fn execute_observed<'w>(
79 &self,
80 library: unit::Library,
81 dds: files::Dds,
82 sysin: Option<Box<dyn BufRead + 'w>>,
83 clock: unit::Clock,
84 database: Option<&'w mut (dyn sql::Database + '_)>,
85 out: &'w mut dyn Write,
86 err: &'w mut dyn Write,
87 observer: Option<unit::Observer<'w>>,
88 ) -> Result<(Ending, i16), Abend>;
89
90 #[allow(clippy::too_many_arguments)]
94 fn execute_main<'w>(
95 &self,
96 library: unit::Library,
97 dds: files::Dds,
98 sysin: Option<Box<dyn BufRead + 'w>>,
99 clock: unit::Clock,
100 database: Option<&'w mut (dyn sql::Database + '_)>,
101 out: &'w mut dyn Write,
102 err: &'w mut dyn Write,
103 observer: Option<unit::Observer<'w>>,
104 parm: &str,
105 ) -> Result<(Ending, i16), Abend>;
106
107 #[allow(clippy::too_many_arguments)]
111 fn execute_with_arguments<'w>(
112 &self,
113 library: unit::Library,
114 dds: files::Dds,
115 sysin: Option<Box<dyn BufRead + 'w>>,
116 clock: unit::Clock,
117 database: Option<&'w mut (dyn sql::Database + '_)>,
118 out: &'w mut dyn Write,
119 err: &'w mut dyn Write,
120 observer: Option<unit::Observer<'w>>,
121 arguments: &[Option<Vec<u8>>],
122 ) -> Result<(Ending, i16), Abend>;
123
124 #[allow(clippy::too_many_arguments)]
127 fn execute_kept<'w>(
128 &self,
129 library: unit::Library,
130 dds: files::Dds,
131 sysin: Option<Box<dyn BufRead + 'w>>,
132 clock: unit::Clock,
133 database: Option<&'w mut (dyn sql::Database + '_)>,
134 out: &'w mut dyn Write,
135 err: &'w mut dyn Write,
136 observer: Option<unit::Observer<'w>>,
137 kept: &mut Option<unit::Remains>,
138 ) -> Result<(Ending, i16), Abend>;
139
140 fn execute_cics<'w>(
144 &self,
145 library: unit::Library,
146 dds: files::Dds,
147 task: cics::Task,
148 clock: unit::Clock,
149 out: &'w mut dyn Write,
150 err: &'w mut dyn Write,
151 ) -> Result<(Ending, cics::Task), Abend>;
152
153 #[allow(clippy::too_many_arguments)]
157 fn execute_cics_with<'w>(
158 &self,
159 library: unit::Library,
160 dds: files::Dds,
161 task: cics::Task,
162 clock: unit::Clock,
163 database: Option<&'w mut (dyn sql::Database + '_)>,
164 out: &'w mut dyn Write,
165 err: &'w mut dyn Write,
166 ) -> Result<(Ending, cics::Task), Abend>;
167
168 #[allow(clippy::too_many_arguments)]
171 fn execute_cics_observed<'w>(
172 &self,
173 library: unit::Library,
174 dds: files::Dds,
175 task: cics::Task,
176 clock: unit::Clock,
177 database: Option<&'w mut (dyn sql::Database + '_)>,
178 out: &'w mut dyn Write,
179 err: &'w mut dyn Write,
180 observer: Option<unit::Observer<'w>>,
181 ) -> Result<(Ending, cics::Task), Abend>;
182}
183
184impl Execute for Compiled {
185 fn run(&self, out: &mut dyn Write, err: &mut dyn Write) -> Result<Ending, Abend> {
186 self.run_with(files::Dds::default(), out, err)
187 }
188
189 fn run_with(&self, dds: files::Dds, out: &mut dyn Write, err: &mut dyn Write) -> Result<Ending, Abend> {
190 self.execute(unit::Library::default(), dds, None, unit::Clock::System, out, err).map(|(ending, _)| ending)
191 }
192
193 fn execute<'w>(
194 &self,
195 library: unit::Library,
196 dds: files::Dds,
197 sysin: Option<Box<dyn BufRead + 'w>>,
198 clock: unit::Clock,
199 out: &'w mut dyn Write,
200 err: &'w mut dyn Write,
201 ) -> Result<(Ending, i16), Abend> {
202 self.execute_with(library, dds, sysin, clock, None, out, err)
203 }
204
205 fn execute_with<'w>(
206 &self,
207 library: unit::Library,
208 dds: files::Dds,
209 sysin: Option<Box<dyn BufRead + 'w>>,
210 clock: unit::Clock,
211 database: Option<&'w mut (dyn sql::Database + '_)>,
212 out: &'w mut dyn Write,
213 err: &'w mut dyn Write,
214 ) -> Result<(Ending, i16), Abend> {
215 self.execute_observed(library, dds, sysin, clock, database, out, err, None)
216 }
217
218 fn execute_observed<'w>(
219 &self,
220 library: unit::Library,
221 dds: files::Dds,
222 sysin: Option<Box<dyn BufRead + 'w>>,
223 clock: unit::Clock,
224 database: Option<&'w mut (dyn sql::Database + '_)>,
225 out: &'w mut dyn Write,
226 err: &'w mut dyn Write,
227 observer: Option<unit::Observer<'w>>,
228 ) -> Result<(Ending, i16), Abend> {
229 run_main(self, library, dds, sysin, clock, database, out, err, observer, Passed::Nothing, &mut None)
230 }
231
232 fn execute_main<'w>(
233 &self,
234 library: unit::Library,
235 dds: files::Dds,
236 sysin: Option<Box<dyn BufRead + 'w>>,
237 clock: unit::Clock,
238 database: Option<&'w mut (dyn sql::Database + '_)>,
239 out: &'w mut dyn Write,
240 err: &'w mut dyn Write,
241 observer: Option<unit::Observer<'w>>,
242 parm: &str,
243 ) -> Result<(Ending, i16), Abend> {
244 run_main(self, library, dds, sysin, clock, database, out, err, observer, Passed::Parm(parm), &mut None)
245 }
246
247 fn execute_with_arguments<'w>(
248 &self,
249 library: unit::Library,
250 dds: files::Dds,
251 sysin: Option<Box<dyn BufRead + 'w>>,
252 clock: unit::Clock,
253 database: Option<&'w mut (dyn sql::Database + '_)>,
254 out: &'w mut dyn Write,
255 err: &'w mut dyn Write,
256 observer: Option<unit::Observer<'w>>,
257 arguments: &[Option<Vec<u8>>],
258 ) -> Result<(Ending, i16), Abend> {
259 run_main(self, library, dds, sysin, clock, database, out, err, observer, Passed::Arguments(arguments), &mut None)
260 }
261
262 fn execute_kept<'w>(
263 &self,
264 library: unit::Library,
265 dds: files::Dds,
266 sysin: Option<Box<dyn BufRead + 'w>>,
267 clock: unit::Clock,
268 database: Option<&'w mut (dyn sql::Database + '_)>,
269 out: &'w mut dyn Write,
270 err: &'w mut dyn Write,
271 observer: Option<unit::Observer<'w>>,
272 kept: &mut Option<unit::Remains>,
273 ) -> Result<(Ending, i16), Abend> {
274 run_main(self, library, dds, sysin, clock, database, out, err, observer, Passed::Nothing, kept)
275 }
276
277 fn execute_cics<'w>(
278 &self,
279 library: unit::Library,
280 dds: files::Dds,
281 task: cics::Task,
282 clock: unit::Clock,
283 out: &'w mut dyn Write,
284 err: &'w mut dyn Write,
285 ) -> Result<(Ending, cics::Task), Abend> {
286 self.execute_cics_with(library, dds, task, clock, None, out, err)
287 }
288
289 fn execute_cics_with<'w>(
290 &self,
291 library: unit::Library,
292 dds: files::Dds,
293 task: cics::Task,
294 clock: unit::Clock,
295 database: Option<&'w mut (dyn sql::Database + '_)>,
296 out: &'w mut dyn Write,
297 err: &'w mut dyn Write,
298 ) -> Result<(Ending, cics::Task), Abend> {
299 self.execute_cics_observed(library, dds, task, clock, database, out, err, None)
300 }
301
302 fn execute_cics_observed<'w>(
303 &self,
304 library: unit::Library,
305 dds: files::Dds,
306 task: cics::Task,
307 clock: unit::Clock,
308 database: Option<&'w mut (dyn sql::Database + '_)>,
309 out: &'w mut dyn Write,
310 err: &'w mut dyn Write,
311 observer: Option<unit::Observer<'w>>,
312 ) -> Result<(Ending, cics::Task), Abend> {
313 oo::refuse_to_run(&self.program)?;
314 let (ending, task) = execute_task(self, library, dds, task, clock, database, out, err, observer, &mut None);
315 ending.map(|e| (e, task))
316 }
317}
318
319#[allow(clippy::too_many_arguments)]
323pub(crate) fn execute_task<'w>(
324 compiled: &Compiled,
325 library: unit::Library,
326 dds: files::Dds,
327 task: cics::Task,
328 clock: unit::Clock,
329 database: Option<&'w mut (dyn sql::Database + '_)>,
330 out: &'w mut dyn Write,
331 err: &'w mut dyn Write,
332 observer: Option<unit::Observer<'w>>,
333 kept: &mut Option<unit::Remains>,
334) -> (Result<Ending, Abend>, cics::Task) {
335 let (statements, taint) = (library.trace_statements.clone(), library.trace_input.then(rt::taint::Taint::default));
336 let limit = library.statement_limit;
337 let mut run_unit = unit::RunUnit::new(library, dds, None, clock, out, err);
338 run_unit.observer = observer;
339 run_unit.statements = statements;
340 run_unit.taint = taint;
341 run_unit.statement_limit = limit;
342 run_unit.sql = database.map(sql::Session::new);
343 let (ending, ended, task) = run_task(First::of(compiled), run_unit, task, kept, |unit, me, commarea, length| {
344 machine::Machine::activation(compiled, me, unit, true).and_then(|mut m| {
345 m.begin_task(commarea, length);
346 m.run_level()
347 })
348 });
349 (ending.map_err(asra).and_then(|e| ended.map(|()| e)), task)
350}
351
352pub(crate) fn run_task<'w, H: Clone, L: unit::Loader<H>, E>(
357 first: First<'_>,
358 mut run_unit: rt::unit::RunUnit<'w, H, L>,
359 mut task: cics::Task,
360 kept: &mut Option<unit::Remains>,
361 run: impl FnOnce(&mut rt::unit::RunUnit<'w, H, L>, usize, Option<usize>, usize) -> Result<Ending, E>,
362) -> (Result<Ending, E>, Result<(), Abend>, cics::Task) {
363 let me = run_unit.add_named(None, first.name, first.files, first.size);
364 run_unit.programs[me].source = first.source;
365 run_unit.eib = run_unit.push_temporary(&[0; cics::EIB_LEN]);
366 let commarea = task.commarea.take();
367 let length = commarea.as_ref().map_or(0, Vec::len);
368 let commarea = commarea.map(|c| {
369 let at = run_unit.push_temporary(&c);
370 run_unit.mark_input(at, c.len(), true);
371 at
372 });
373 run_unit.cics = Some(task);
374 let ending = run(&mut run_unit, me, commarea, length);
375 let settled = run_unit.sql.as_mut().map_or(Ok(()), |s| s.end_task(first.id, ending.is_ok()).map(drop));
376 let mut closed = run_unit.close_all(false);
377 for (name, f) in run_unit.cics_files.drain() {
378 if let Err(e) = f.close() {
379 closed = closed.and(Err(format!("closing CICS file {name}: {e}")));
380 }
381 }
382 *kept = Some(unit::Remains::of(&run_unit));
383 let mut task = run_unit.cics.take().unwrap_or_default();
384 if let Err(e) = task.flush_td(first.page) {
385 closed = closed.and(Err(format!("writing transient data: {e}")));
386 }
387 let settled = settled.map_err(|a| Abend { code: a.code.into(), message: a.message, pos: Pos::default(), file: None });
388 let closed = closed.map_err(|m| Abend { code: AbendCode::Ironwork, message: m, pos: Pos::default(), file: None });
389 (ending, settled.and(closed), task)
390}
391
392pub(crate) struct First<'a> {
396 pub id: &'a str,
397 pub name: String,
398 pub files: usize,
399 pub size: usize,
400 pub page: &'static zarch::ebcdic::CodePage,
401 pub source: Option<std::path::PathBuf>,
402}
403
404impl<'a> First<'a> {
405 pub(crate) fn of(compiled: &'a Compiled) -> Self {
406 let p = &compiled.program;
407 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 }
408 }
409}
410
411pub(crate) fn asra(a: Abend) -> Abend {
413 match a.code {
414 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 },
415 _ => a,
416 }
417}
418
419#[derive(Clone, Copy, Debug, Default)]
421pub enum Passed<'a> {
422 #[default]
424 Nothing,
425 Parm(&'a str),
427 Arguments(&'a [Option<Vec<u8>>]),
430}
431
432impl Passed<'_> {
433 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>> {
435 match self {
436 Passed::Nothing => Vec::new(),
437 Passed::Parm(parm) => vec![Some(push_parm(run_unit, page, parm))],
438 Passed::Arguments(arguments) => arguments.iter().map(|a| a.as_deref().map(|bytes| push_input(run_unit, bytes))).collect(),
439 }
440 }
441
442 pub(crate) fn main(self) -> bool {
445 !matches!(self, Passed::Arguments(_))
446 }
447
448 pub(crate) fn apply_parm<H: Clone, L: rt::unit::Loader<H>>(self, run_unit: &mut rt::unit::RunUnit<'_, H, L>) {
452 let Passed::Parm(parm) = self else { return };
453 run_unit.arguments = rt::le::parm::Arguments::of(parm);
454 match rt::le::parm::upsi(parm) {
455 Some(Ok(on)) => run_unit.set_switches(on),
456 Some(Err(m)) => {
457 let _ = writeln!(run_unit.err, "ironwork: {m}");
458 }
459 None => {}
460 }
461 }
462}
463
464#[allow(clippy::too_many_arguments)]
467fn run_main<'w>(
468 compiled: &Compiled,
469 library: unit::Library,
470 dds: files::Dds,
471 sysin: Option<Box<dyn BufRead + 'w>>,
472 clock: unit::Clock,
473 database: Option<&'w mut (dyn sql::Database + '_)>,
474 out: &'w mut dyn Write,
475 err: &'w mut dyn Write,
476 observer: Option<unit::Observer<'w>>,
477 passed: Passed<'_>,
478 kept: &mut Option<unit::Remains>,
479) -> Result<(Ending, i16), Abend> {
480 oo::refuse_to_run(&compiled.program)?;
481 let (statements, taint) = (library.trace_statements.clone(), library.trace_input.then(rt::taint::Taint::default));
482 let limit = library.statement_limit;
483 let mut run_unit = unit::RunUnit::new(library, dds, sysin, clock, out, err);
484 run_unit.observer = observer;
485 run_unit.statements = statements;
486 run_unit.taint = taint;
487 run_unit.statement_limit = limit;
488 run_unit.sql = database.map(sql::Session::new);
489 let me = run_unit.add(None, &compiled.program, compiled.layout.size as usize);
490 let trap_off = matches!(passed, Passed::Parm(p) if rt::le::parm::trap_off(p));
491 passed.apply_parm(&mut run_unit);
492 let addresses = passed.addresses(&mut run_unit, compiled.options.code_page());
493 let ending = machine::Machine::activation(compiled, me, &mut run_unit, passed.main()).and_then(|mut m| {
494 if !addresses.is_empty() {
495 m.bind(&addresses);
496 }
497 m.run_procedure()
498 });
499 let settled = run_unit.sql.as_mut().map_or(Ok(()), |s| s.settle(&compiled.program.id, ending.is_ok()).map(drop));
500 let closed = run_unit.close_all(trap_off && ending.as_ref().is_err_and(|a| a.code.bypasses_trap_off()));
501 *kept = Some(unit::Remains::of(&run_unit));
502 let ending = ending?;
503 settled.map_err(|a| Abend { code: a.code.into(), message: a.message, pos: Pos::default(), file: None })?;
504 closed.map_err(|m| Abend { code: AbendCode::Ironwork, message: m, pos: Pos::default(), file: None })?;
505 Ok((ending, run_unit.return_code()))
506}
507
508pub(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 {
510 let area = rt::le::parm::parameter_area(rt::le::parm::program_arguments(parm), page);
511 push_input(run_unit, &area)
512}
513
514fn push_input<H: Clone, L: rt::unit::Loader<H>>(run_unit: &mut rt::unit::RunUnit<'_, H, L>, bytes: &[u8]) -> usize {
516 let at = run_unit.push_temporary(bytes);
517 run_unit.mark_input(at, bytes.len(), true);
518 at
519}
520
521#[cfg(test)]
522mod tests;