use super::*;
use super::cics_bind::operand;
use crate::cics::{self, CicsHost};
use rt::bms::Mapset;
use std::rc::Rc;
use super::cics_bind::has;
pub(super) use crate::cics::Handlers;
fn flow(f: cics::Flow) -> Flow {
match f {
cics::Flow::Next => Flow::Next,
cics::Flow::GoTo(p) => Flow::GoTo(p as usize),
cics::Flow::End(e) => Flow::End(e),
}
}
impl<'p> Machine<'p, '_, '_> {
pub(super) fn cics(&mut self, block: &'p ExecBlock) -> R<Flow> {
cics::in_task(self.unit, &block.command, block.pos)?;
if self.unit.observed() {
self.cics_sinks(block);
}
let command = self.bind_cics(block)?;
cics::run(self, &command, block.pos).map(flow)
}
fn cics_sinks(&mut self, block: &ExecBlock) {
let command = block.command.as_str();
let queue = matches!(command, "WRITEQ" | "READQ" | "DELETEQ") || command.starts_with("WRITEQ ") || command.starts_with("READQ ") || command.starts_with("DELETEQ ");
let mut sinks: Vec<(&str, &'static str)> = Vec::new();
match command {
"LINK" | "XCTL" => sinks.push(("PROGRAM", "cics-dynamic-transfer")),
"START" | "START TRANSID" => sinks.push(("TRANSID", "cics-dynamic-transfer")),
"READ" | "STARTBR" | "RESETBR" => sinks.push(("RIDFLD", "record-key")),
"DELETE" => sinks.push(("RIDFLD", "record-update")),
"WRITEQ TD" => sinks.push(("FROM", "log")),
"WRITE" if has(block, "OPERATOR") => sinks.push(("TEXT", "log")),
"WRITE" if has(block, "JOURNALNAME") || has(block, "JOURNALNUM") => sinks.push(("FROM", "log")),
"SEND TEXT" | "SEND MAP" | "SEND" => sinks.push(("FROM", "screen")),
"WEB SEND" => sinks.push(("FROM", "web-response")),
"WEB WRITE" => sinks.push(("VALUE", "http-header")),
"WEB OPEN" => sinks.extend([("HOST", "outbound-host"), ("URL", "outbound-host")]),
"WEB CONVERSE" => sinks.extend([("PATH", "outbound-host"), ("FROM", "outbound-http")]),
_ => {}
}
if queue {
sinks.extend([("QUEUE", "queue-name"), ("QNAME", "queue-name")]);
}
sinks.push(("SYSID", "cics-sysid"));
for (option, kind) in sinks {
let Some(Operand::Ref(r)) = operand(block, option) else { continue };
if let Ok(loc) = self.locate(r) {
let text = self.page.decode(self.bytes(loc));
self.sink(kind, block.pos, &text);
}
}
}
pub(crate) fn begin_task(&mut self, commarea: Option<usize>, length: usize) {
cics::begin_task(self.unit, self.page, length);
let eib = self.unit.eib;
self.bind(&[Some(eib), commarea]);
}
fn named(name: &str, pos: Pos) -> Ref {
Ref { name: name.to_owned(), qualifiers: Vec::new(), subscripts: Vec::new(), refmod: None, pos }
}
}
impl<'a, 'w> CicsHost<'w, &'a Ref, &'a Operand, &'a str> for Machine<'_, '_, 'w> {
type Program = Rc<Compiled>;
type Loader = crate::unit::Library;
fn unit(&mut self) -> &mut RunUnit<'w> {
self.unit
}
fn handlers(&mut self) -> &mut Handlers {
&mut self.cics_handlers
}
fn content(&mut self, operand: &&'a Operand, pos: Pos) -> R<Vec<u8>> {
self.content_argument(operand, pos)
}
fn integer_of(&mut self, operand: &&'a Operand, pos: Pos) -> R<i64> {
Machine::integer(self, &Expr::Operand((*operand).clone()), pos)
}
fn text(&self, text: &&'a str) -> String {
(*text).to_owned()
}
fn main(&self) -> bool {
self.main
}
fn program_id(&self) -> String {
self.program.id.clone()
}
fn commarea(&self) -> Option<usize> {
let ordinal = self.layout.linkage_roots.iter().position(|&i| self.layout.items[i].name.as_deref() == Some("DFHCOMMAREA"));
ordinal.and_then(|o| self.linkage[o])
}
fn mapset(&mut self, name: &str) -> Option<Result<Mapset, String>> {
syntax::bms::find_mapset(&self.unit.library.copy, name).map(|found| found.map_err(|e| e.message))
}
fn item_named(&mut self, name: &str, pos: Pos) -> R<Option<Loc>> {
let r = Self::named(name, pos);
match self.resolve(&r) {
Ok(Resolved::Item(_)) => self.locate(&r).map(Some),
_ => Ok(None),
}
}
fn locate_named(&mut self, name: &str, pos: Pos) -> R<Loc> {
self.locate(&Self::named(name, pos))
}
fn run_program(&mut self, program: Rc<Compiled>, index: usize, commarea: Option<usize>, xctl: bool) -> R<Ending> {
let ending = Machine::activation(&program, index, &mut *self.unit, self.main && xctl).and_then(|mut callee| {
let eib = callee.unit.eib;
callee.bind(&[Some(eib), commarea]);
callee.run_procedure()
});
ending.map_err(|a| self.in_loaded(index, &program, a))
}
}
#[cfg(test)]
mod tests {
use crate::cics::{EIBAID, EIBCALEN, EIBCPOSN, EIBDATE, EIBFN, EIBRESP, EIBRESP2, EIBRSRCE, EIBTASKN, EIBTIME, EIBTRMID, EIBTRNID};
#[test]
fn eib_offsets_match_the_dfheiblk_layout() {
let source = " IDENTIFICATION DIVISION.\n PROGRAM-ID. EIBT.\n PROCEDURE DIVISION.\n EXEC CICS RETURN END-EXEC.\n";
let program = syntax::parse_all_with(source, &syntax::copy::Libraries::default()).unwrap().remove(0);
let compiled = crate::compile(program, &[]).unwrap_or_else(|e| panic!("{e:?}"));
let offset = |name: &str| compiled.layout.items.iter().find(|i| i.name.as_deref() == Some(name)).map(|i| i.offset as usize);
let fields = [
("EIBTIME", EIBTIME),
("EIBDATE", EIBDATE),
("EIBTRNID", EIBTRNID),
("EIBTASKN", EIBTASKN),
("EIBTRMID", EIBTRMID),
("EIBCPOSN", EIBCPOSN),
("EIBCALEN", EIBCALEN),
("EIBAID", EIBAID),
("EIBFN", EIBFN),
("EIBRSRCE", EIBRSRCE),
("EIBRESP", EIBRESP),
("EIBRESP2", EIBRESP2),
];
for (name, at) in fields {
assert_eq!(offset(name), Some(at), "{name}");
}
}
}