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)]
110 fn execute_kept<'w>(
111 &self,
112 library: unit::Library,
113 dds: files::Dds,
114 sysin: Option<Box<dyn BufRead + 'w>>,
115 clock: unit::Clock,
116 database: Option<&'w mut (dyn sql::Database + '_)>,
117 out: &'w mut dyn Write,
118 err: &'w mut dyn Write,
119 observer: Option<unit::Observer<'w>>,
120 kept: &mut Option<unit::Remains>,
121 ) -> Result<(Ending, i16), Abend>;
122
123 fn execute_cics<'w>(
127 &self,
128 library: unit::Library,
129 dds: files::Dds,
130 task: cics::Task,
131 clock: unit::Clock,
132 out: &'w mut dyn Write,
133 err: &'w mut dyn Write,
134 ) -> Result<(Ending, cics::Task), Abend>;
135
136 #[allow(clippy::too_many_arguments)]
140 fn execute_cics_with<'w>(
141 &self,
142 library: unit::Library,
143 dds: files::Dds,
144 task: cics::Task,
145 clock: unit::Clock,
146 database: Option<&'w mut (dyn sql::Database + '_)>,
147 out: &'w mut dyn Write,
148 err: &'w mut dyn Write,
149 ) -> Result<(Ending, cics::Task), Abend>;
150
151 #[allow(clippy::too_many_arguments)]
154 fn execute_cics_observed<'w>(
155 &self,
156 library: unit::Library,
157 dds: files::Dds,
158 task: cics::Task,
159 clock: unit::Clock,
160 database: Option<&'w mut (dyn sql::Database + '_)>,
161 out: &'w mut dyn Write,
162 err: &'w mut dyn Write,
163 observer: Option<unit::Observer<'w>>,
164 ) -> Result<(Ending, cics::Task), Abend>;
165}
166
167impl Execute for Compiled {
168 fn run(&self, out: &mut dyn Write, err: &mut dyn Write) -> Result<Ending, Abend> {
169 self.run_with(files::Dds::default(), out, err)
170 }
171
172 fn run_with(&self, dds: files::Dds, out: &mut dyn Write, err: &mut dyn Write) -> Result<Ending, Abend> {
173 self.execute(unit::Library::default(), dds, None, unit::Clock::System, out, err).map(|(ending, _)| ending)
174 }
175
176 fn execute<'w>(
177 &self,
178 library: unit::Library,
179 dds: files::Dds,
180 sysin: Option<Box<dyn BufRead + 'w>>,
181 clock: unit::Clock,
182 out: &'w mut dyn Write,
183 err: &'w mut dyn Write,
184 ) -> Result<(Ending, i16), Abend> {
185 self.execute_with(library, dds, sysin, clock, None, out, err)
186 }
187
188 fn execute_with<'w>(
189 &self,
190 library: unit::Library,
191 dds: files::Dds,
192 sysin: Option<Box<dyn BufRead + 'w>>,
193 clock: unit::Clock,
194 database: Option<&'w mut (dyn sql::Database + '_)>,
195 out: &'w mut dyn Write,
196 err: &'w mut dyn Write,
197 ) -> Result<(Ending, i16), Abend> {
198 self.execute_observed(library, dds, sysin, clock, database, out, err, None)
199 }
200
201 fn execute_observed<'w>(
202 &self,
203 library: unit::Library,
204 dds: files::Dds,
205 sysin: Option<Box<dyn BufRead + 'w>>,
206 clock: unit::Clock,
207 database: Option<&'w mut (dyn sql::Database + '_)>,
208 out: &'w mut dyn Write,
209 err: &'w mut dyn Write,
210 observer: Option<unit::Observer<'w>>,
211 ) -> Result<(Ending, i16), Abend> {
212 run_main(self, library, dds, sysin, clock, database, out, err, observer, None, &mut None)
213 }
214
215 fn execute_main<'w>(
216 &self,
217 library: unit::Library,
218 dds: files::Dds,
219 sysin: Option<Box<dyn BufRead + 'w>>,
220 clock: unit::Clock,
221 database: Option<&'w mut (dyn sql::Database + '_)>,
222 out: &'w mut dyn Write,
223 err: &'w mut dyn Write,
224 observer: Option<unit::Observer<'w>>,
225 parm: &str,
226 ) -> Result<(Ending, i16), Abend> {
227 run_main(self, library, dds, sysin, clock, database, out, err, observer, Some(parm), &mut None)
228 }
229
230 fn execute_kept<'w>(
231 &self,
232 library: unit::Library,
233 dds: files::Dds,
234 sysin: Option<Box<dyn BufRead + 'w>>,
235 clock: unit::Clock,
236 database: Option<&'w mut (dyn sql::Database + '_)>,
237 out: &'w mut dyn Write,
238 err: &'w mut dyn Write,
239 observer: Option<unit::Observer<'w>>,
240 kept: &mut Option<unit::Remains>,
241 ) -> Result<(Ending, i16), Abend> {
242 run_main(self, library, dds, sysin, clock, database, out, err, observer, None, kept)
243 }
244
245 fn execute_cics<'w>(
246 &self,
247 library: unit::Library,
248 dds: files::Dds,
249 task: cics::Task,
250 clock: unit::Clock,
251 out: &'w mut dyn Write,
252 err: &'w mut dyn Write,
253 ) -> Result<(Ending, cics::Task), Abend> {
254 self.execute_cics_with(library, dds, task, clock, None, out, err)
255 }
256
257 fn execute_cics_with<'w>(
258 &self,
259 library: unit::Library,
260 dds: files::Dds,
261 task: cics::Task,
262 clock: unit::Clock,
263 database: Option<&'w mut (dyn sql::Database + '_)>,
264 out: &'w mut dyn Write,
265 err: &'w mut dyn Write,
266 ) -> Result<(Ending, cics::Task), Abend> {
267 self.execute_cics_observed(library, dds, task, clock, database, out, err, None)
268 }
269
270 fn execute_cics_observed<'w>(
271 &self,
272 library: unit::Library,
273 dds: files::Dds,
274 task: cics::Task,
275 clock: unit::Clock,
276 database: Option<&'w mut (dyn sql::Database + '_)>,
277 out: &'w mut dyn Write,
278 err: &'w mut dyn Write,
279 observer: Option<unit::Observer<'w>>,
280 ) -> Result<(Ending, cics::Task), Abend> {
281 oo::refuse_to_run(&self.program)?;
282 let (ending, task) = execute_task(self, library, dds, task, clock, database, out, err, observer, &mut None);
283 ending.map(|e| (e, task))
284 }
285}
286
287#[allow(clippy::too_many_arguments)]
291pub(crate) fn execute_task<'w>(
292 compiled: &Compiled,
293 library: unit::Library,
294 dds: files::Dds,
295 task: cics::Task,
296 clock: unit::Clock,
297 database: Option<&'w mut (dyn sql::Database + '_)>,
298 out: &'w mut dyn Write,
299 err: &'w mut dyn Write,
300 observer: Option<unit::Observer<'w>>,
301 kept: &mut Option<unit::Remains>,
302) -> (Result<Ending, Abend>, cics::Task) {
303 let (statements, taint) = (library.trace_statements.clone(), library.trace_input.then(rt::taint::Taint::default));
304 let limit = library.statement_limit;
305 let mut run_unit = unit::RunUnit::new(library, dds, None, clock, out, err);
306 run_unit.observer = observer;
307 run_unit.statements = statements;
308 run_unit.taint = taint;
309 run_unit.statement_limit = limit;
310 run_unit.sql = database.map(sql::Session::new);
311 let (ending, ended, task) = run_task(compiled, run_unit, task, kept, |unit, me, commarea, length| {
312 machine::Machine::activation(compiled, me, unit, true).and_then(|mut m| {
313 m.begin_task(commarea, length);
314 m.run_level()
315 })
316 });
317 (ending.map_err(asra).and_then(|e| ended.map(|()| e)), task)
318}
319
320pub(crate) fn run_task<'w, H: Clone, L: unit::Loader<H>, E>(
325 compiled: &Compiled,
326 mut run_unit: rt::unit::RunUnit<'w, H, L>,
327 mut task: cics::Task,
328 kept: &mut Option<unit::Remains>,
329 run: impl FnOnce(&mut rt::unit::RunUnit<'w, H, L>, usize, Option<usize>, usize) -> Result<Ending, E>,
330) -> (Result<Ending, E>, Result<(), Abend>, cics::Task) {
331 let me = run_unit.add_named(None, compiled.program.id.to_ascii_uppercase(), compiled.program.files.len(), compiled.layout.size as usize);
332 run_unit.eib = run_unit.push_temporary(&[0; cics::EIB_LEN]);
333 let commarea = task.commarea.take();
334 let length = commarea.as_ref().map_or(0, Vec::len);
335 let commarea = commarea.map(|c| {
336 let at = run_unit.push_temporary(&c);
337 run_unit.mark_input(at, c.len(), true);
338 at
339 });
340 run_unit.cics = Some(task);
341 let ending = run(&mut run_unit, me, commarea, length);
342 let settled = run_unit.sql.as_mut().map_or(Ok(()), |s| s.end_task(&compiled.program.id, ending.is_ok()).map(drop));
343 let mut closed = run_unit.close_all(false);
344 for (name, f) in run_unit.cics_files.drain() {
345 if let Err(e) = f.close() {
346 closed = closed.and(Err(format!("closing CICS file {name}: {e}")));
347 }
348 }
349 *kept = Some(unit::Remains::of(&run_unit));
350 let mut task = run_unit.cics.take().unwrap_or_default();
351 if let Err(e) = task.flush_td(compiled.options.code_page()) {
352 closed = closed.and(Err(format!("writing transient data: {e}")));
353 }
354 let settled = settled.map_err(|a| Abend { code: a.code.into(), message: a.message, pos: Pos::default(), file: None });
355 let closed = closed.map_err(|m| Abend { code: AbendCode::Ironwork, message: m, pos: Pos::default(), file: None });
356 (ending, settled.and(closed), task)
357}
358
359pub(crate) fn asra(a: Abend) -> Abend {
361 match a.code {
362 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 },
363 _ => a,
364 }
365}
366
367#[allow(clippy::too_many_arguments)]
370fn run_main<'w>(
371 compiled: &Compiled,
372 library: unit::Library,
373 dds: files::Dds,
374 sysin: Option<Box<dyn BufRead + 'w>>,
375 clock: unit::Clock,
376 database: Option<&'w mut (dyn sql::Database + '_)>,
377 out: &'w mut dyn Write,
378 err: &'w mut dyn Write,
379 observer: Option<unit::Observer<'w>>,
380 parm: Option<&str>,
381 kept: &mut Option<unit::Remains>,
382) -> Result<(Ending, i16), Abend> {
383 oo::refuse_to_run(&compiled.program)?;
384 let (statements, taint) = (library.trace_statements.clone(), library.trace_input.then(rt::taint::Taint::default));
385 let limit = library.statement_limit;
386 let mut run_unit = unit::RunUnit::new(library, dds, sysin, clock, out, err);
387 run_unit.observer = observer;
388 run_unit.statements = statements;
389 run_unit.taint = taint;
390 run_unit.statement_limit = limit;
391 run_unit.sql = database.map(sql::Session::new);
392 let me = run_unit.add(None, &compiled.program, compiled.layout.size as usize);
393 let trap_off = parm.is_some_and(rt::le::parm::trap_off);
394 let parm = parm.map(|p| push_parm(&mut run_unit, compiled.options.code_page(), p));
395 let ending = machine::Machine::activation(compiled, me, &mut run_unit, true).and_then(|mut m| {
396 if parm.is_some() {
397 m.bind(&[parm]);
398 }
399 m.run_procedure()
400 });
401 let settled = run_unit.sql.as_mut().map_or(Ok(()), |s| s.settle(&compiled.program.id, ending.is_ok()).map(drop));
402 let closed = run_unit.close_all(trap_off && ending.as_ref().is_err_and(|a| a.code.bypasses_trap_off()));
403 *kept = Some(unit::Remains::of(&run_unit));
404 let ending = ending?;
405 settled.map_err(|a| Abend { code: a.code.into(), message: a.message, pos: Pos::default(), file: None })?;
406 closed.map_err(|m| Abend { code: AbendCode::Ironwork, message: m, pos: Pos::default(), file: None })?;
407 Ok((ending, run_unit.return_code()))
408}
409
410pub(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 {
412 let area = rt::le::parm::parameter_area(rt::le::parm::program_arguments(parm), page);
413 let at = run_unit.push_temporary(&area);
414 run_unit.mark_input(at, area.len(), true);
415 at
416}
417
418#[cfg(test)]
419mod tests;