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 begin_task(&mut self, commarea: Option<usize>, length: usize) {
70 cics::begin_task(self.unit, self.page, length);
71 let eib = self.unit.eib;
72 self.bind(&[Some(eib), commarea]);
73 }
74
75 fn named(name: &str, pos: Pos) -> Ref {
77 Ref { name: name.to_owned(), qualifiers: Vec::new(), subscripts: Vec::new(), refmod: None, pos }
78 }
79}
80
81impl<'a, 'w> CicsHost<'w, &'a Ref, &'a Operand, &'a str> for Machine<'_, '_, 'w> {
82 type Program = Rc<Compiled>;
83 type Loader = crate::unit::Library;
84
85 fn unit(&mut self) -> &mut RunUnit<'w> {
86 self.unit
87 }
88
89 fn handlers(&mut self) -> &mut Handlers {
90 &mut self.cics_handlers
91 }
92
93 fn content(&mut self, operand: &&'a Operand, pos: Pos) -> R<Vec<u8>> {
94 self.content_argument(operand, pos)
95 }
96
97 fn integer_of(&mut self, operand: &&'a Operand, pos: Pos) -> R<i64> {
98 Machine::integer(self, &Expr::Operand((*operand).clone()), pos)
99 }
100
101 fn text(&self, text: &&'a str) -> String {
102 (*text).to_owned()
103 }
104
105 fn main(&self) -> bool {
106 self.main
107 }
108
109 fn program_id(&self) -> String {
110 self.program.id.clone()
111 }
112
113 fn commarea(&self) -> Option<usize> {
114 let ordinal = self.layout.linkage_roots.iter().position(|&i| self.layout.items[i].name.as_deref() == Some("DFHCOMMAREA"));
115 ordinal.and_then(|o| self.linkage[o])
116 }
117
118 fn mapset(&mut self, name: &str) -> Option<Result<Mapset, String>> {
119 syntax::bms::find_mapset(&self.unit.library.copy, name).map(|found| found.map_err(|e| e.message))
120 }
121
122 fn item_named(&mut self, name: &str, pos: Pos) -> R<Option<Loc>> {
123 let r = Self::named(name, pos);
124 match self.resolve(&r) {
125 Ok(Resolved::Item(_)) => self.locate(&r).map(Some),
126 _ => Ok(None),
127 }
128 }
129
130 fn locate_named(&mut self, name: &str, pos: Pos) -> R<Loc> {
131 self.locate(&Self::named(name, pos))
132 }
133
134 fn run_program(&mut self, program: Rc<Compiled>, index: usize, commarea: Option<usize>, xctl: bool) -> R<Ending> {
135 let ending = Machine::activation(&program, index, &mut *self.unit, self.main && xctl).and_then(|mut callee| {
136 let eib = callee.unit.eib;
137 callee.bind(&[Some(eib), commarea]);
138 callee.run_procedure()
139 });
140 ending.map_err(|a| self.in_loaded(index, &program, a))
141 }
142}
143
144#[cfg(test)]
145mod tests {
146 use crate::cics::{EIBAID, EIBCALEN, EIBCPOSN, EIBDATE, EIBFN, EIBRESP, EIBRESP2, EIBRSRCE, EIBTASKN, EIBTIME, EIBTRMID, EIBTRNID};
147
148 #[test]
149 fn eib_offsets_match_the_dfheiblk_layout() {
150 let source = " IDENTIFICATION DIVISION.\n PROGRAM-ID. EIBT.\n PROCEDURE DIVISION.\n EXEC CICS RETURN END-EXEC.\n";
151 let program = syntax::parse_all_with(source, &syntax::copy::Libraries::default()).unwrap().remove(0);
152 let compiled = crate::compile(program, &[]).unwrap_or_else(|e| panic!("{e:?}"));
153 let offset = |name: &str| compiled.layout.items.iter().find(|i| i.name.as_deref() == Some(name)).map(|i| i.offset as usize);
154 let fields = [
155 ("EIBTIME", EIBTIME),
156 ("EIBDATE", EIBDATE),
157 ("EIBTRNID", EIBTRNID),
158 ("EIBTASKN", EIBTASKN),
159 ("EIBTRMID", EIBTRMID),
160 ("EIBCPOSN", EIBCPOSN),
161 ("EIBCALEN", EIBCALEN),
162 ("EIBAID", EIBAID),
163 ("EIBFN", EIBFN),
164 ("EIBRSRCE", EIBRSRCE),
165 ("EIBRESP", EIBRESP),
166 ("EIBRESP2", EIBRESP2),
167 ];
168 for (name, at) in fields {
169 assert_eq!(offset(name), Some(at), "{name}");
170 }
171 }
172}