1use super::*;
5use super::cics_bind::operand;
6use crate::cics::{self, CicsHost};
7use rt::bms::Mapset;
8use std::rc::Rc;
9
10use super::cics_bind::has;
11pub(super) use crate::cics::Handlers;
12
13fn flow(f: cics::Flow) -> Flow {
14 match f {
15 cics::Flow::Next => Flow::Next,
16 cics::Flow::GoTo(p) => Flow::GoTo(p as usize),
17 cics::Flow::End(e) => Flow::End(e),
18 }
19}
20
21impl<'p> Machine<'p, '_, '_> {
22 pub(super) fn cics(&mut self, block: &'p ExecBlock) -> R<Flow> {
23 cics::in_task(self.unit, &block.command, block.pos)?;
24 if self.unit.observed() {
25 self.cics_sinks(block);
26 }
27 let command = self.bind_cics(block)?;
28 cics::run(self, &command, block.pos).map(flow)
29 }
30
31 fn cics_sinks(&mut self, block: &ExecBlock) {
36 let command = block.command.as_str();
37 let queue = matches!(command, "WRITEQ" | "READQ" | "DELETEQ") || command.starts_with("WRITEQ ") || command.starts_with("READQ ") || command.starts_with("DELETEQ ");
38 let mut sinks: Vec<(&str, &'static str)> = Vec::new();
39 match command {
40 "LINK" | "XCTL" => sinks.push(("PROGRAM", "cics-dynamic-transfer")),
41 "START" | "START TRANSID" => sinks.push(("TRANSID", "cics-dynamic-transfer")),
42 "READ" | "STARTBR" | "RESETBR" => sinks.push(("RIDFLD", "record-key")),
43 "DELETE" => sinks.push(("RIDFLD", "record-update")),
44 "WRITEQ TD" => sinks.push(("FROM", "log")),
45 "WRITE" if has(block, "OPERATOR") => sinks.push(("TEXT", "log")),
46 "WRITE" if has(block, "JOURNALNAME") || has(block, "JOURNALNUM") => sinks.push(("FROM", "log")),
47 "SEND TEXT" | "SEND MAP" | "SEND" => sinks.push(("FROM", "screen")),
48 "WEB SEND" => sinks.push(("FROM", "web-response")),
49 "WEB WRITE" => sinks.push(("VALUE", "http-header")),
50 "WEB OPEN" => sinks.extend([("HOST", "outbound-host"), ("URL", "outbound-host")]),
51 "WEB CONVERSE" => sinks.extend([("PATH", "outbound-host"), ("FROM", "outbound-http")]),
52 _ => {}
53 }
54 if queue {
55 sinks.extend([("QUEUE", "queue-name"), ("QNAME", "queue-name")]);
56 }
57 sinks.push(("SYSID", "cics-sysid"));
58 for (option, kind) in sinks {
59 let Some(Operand::Ref(r)) = operand(block, option) else { continue };
60 if let Ok(loc) = self.locate(r) {
61 let text = self.page.decode(self.bytes(loc));
62 self.sink(kind, block.pos, &text);
63 }
64 }
65 }
66
67 pub(crate) fn run_level(&mut self) -> R<Ending> {
71 let mut start = None;
72 loop {
73 let abend = match self.run_from(start) {
74 Err(abend) => abend,
75 done => return done,
76 };
77 match cics::abend_exit(self.unit, &mut self.cics_handlers, &abend) {
78 None => return Err(abend),
79 Some(cics::ExitTarget::Label(p)) => start = Some((p as usize, 0)),
80 Some(cics::ExitTarget::Program(name)) => {
81 let ending = cics::enter_exit_program(self, &name, abend.pos)?;
82 return Ok(if ending == Ending::StopRun { ending } else { Ending::Goback });
83 }
84 }
85 }
86 }
87
88 pub(crate) fn begin_task(&mut self, commarea: Option<usize>, length: usize) {
91 cics::begin_task(self.unit, self.page, length);
92 let eib = self.unit.eib;
93 self.bind(&[Some(eib), commarea]);
94 }
95
96 fn named(name: &str, pos: Pos) -> Ref {
98 Ref { name: name.to_owned(), qualifiers: Vec::new(), subscripts: Vec::new(), refmod: None, pos }
99 }
100}
101
102impl<'a, 'w> CicsHost<'w, &'a Ref, &'a Operand, &'a str> for Machine<'_, '_, 'w> {
103 type Program = Rc<Compiled>;
104 type Loader = crate::unit::Library;
105
106 fn unit(&mut self) -> &mut RunUnit<'w> {
107 self.unit
108 }
109
110 fn handlers(&mut self) -> &mut Handlers {
111 &mut self.cics_handlers
112 }
113
114 fn content(&mut self, operand: &&'a Operand, pos: Pos) -> R<Vec<u8>> {
115 self.content_argument(operand, pos)
116 }
117
118 fn integer_of(&mut self, operand: &&'a Operand, pos: Pos) -> R<i64> {
119 Machine::integer(self, &Expr::Operand((*operand).clone()), pos)
120 }
121
122 fn text(&self, text: &&'a str) -> String {
123 (*text).to_owned()
124 }
125
126 fn main(&self) -> bool {
127 self.main
128 }
129
130 fn program_id(&self) -> String {
131 self.program.id.clone()
132 }
133
134 fn commarea(&self) -> Option<usize> {
135 let ordinal = self.layout.linkage_roots.iter().position(|&i| self.layout.items[i].name.as_deref() == Some("DFHCOMMAREA"));
136 ordinal.and_then(|o| self.linkage[o])
137 }
138
139 fn mapset(&mut self, name: &str) -> Option<Result<Mapset, String>> {
140 syntax::bms::find_mapset(&self.unit.library.copy, name).map(|found| found.map_err(|e| e.message))
141 }
142
143 fn item_named(&mut self, name: &str, pos: Pos) -> R<Option<Loc>> {
144 let r = Self::named(name, pos);
145 match self.resolve(&r) {
146 Ok(Resolved::Item(_)) => self.locate(&r).map(Some),
147 _ => Ok(None),
148 }
149 }
150
151 fn locate_named(&mut self, name: &str, pos: Pos) -> R<Loc> {
152 self.locate(&Self::named(name, pos))
153 }
154
155 fn run_program(&mut self, program: Rc<Compiled>, index: usize, commarea: Option<usize>, xctl: bool) -> R<Ending> {
156 let ending = Machine::activation(&program, index, &mut *self.unit, self.main && xctl).and_then(|mut callee| {
157 let eib = callee.unit.eib;
158 callee.bind(&[Some(eib), commarea]);
159 callee.run_level()
160 });
161 ending.map_err(|a| self.in_loaded(index, &program, a))
162 }
163}
164
165#[cfg(test)]
166mod tests {
167 use crate::cics::{EIBAID, EIBCALEN, EIBCPOSN, EIBDATE, EIBFN, EIBRESP, EIBRESP2, EIBRSRCE, EIBTASKN, EIBTIME, EIBTRMID, EIBTRNID};
168
169 #[test]
170 fn eib_offsets_match_the_dfheiblk_layout() {
171 let source = " IDENTIFICATION DIVISION.\n PROGRAM-ID. EIBT.\n PROCEDURE DIVISION.\n EXEC CICS RETURN END-EXEC.\n";
172 let program = syntax::parse_all_with(source, &syntax::copy::Libraries::default()).unwrap().remove(0);
173 let compiled = crate::compile(program, &[]).unwrap_or_else(|e| panic!("{e:?}"));
174 let offset = |name: &str| compiled.layout.items.iter().find(|i| i.name.as_deref() == Some(name)).map(|i| i.offset as usize);
175 let fields = [
176 ("EIBTIME", EIBTIME),
177 ("EIBDATE", EIBDATE),
178 ("EIBTRNID", EIBTRNID),
179 ("EIBTASKN", EIBTASKN),
180 ("EIBTRMID", EIBTRMID),
181 ("EIBCPOSN", EIBCPOSN),
182 ("EIBCALEN", EIBCALEN),
183 ("EIBAID", EIBAID),
184 ("EIBFN", EIBFN),
185 ("EIBRSRCE", EIBRSRCE),
186 ("EIBRESP", EIBRESP),
187 ("EIBRESP2", EIBRESP2),
188 ];
189 for (name, at) in fields {
190 assert_eq!(offset(name), Some(at), "{name}");
191 }
192 }
193}