if havmode = 0 & ,
optc = "--auto" then do
havmode = 1; mode = "auto";
iarg += 1;
iterate
end
if havmode = 0 & ,
optc = "--text" then do
havmode = 1; mode = "text";
iarg += 1;
iterate
end
if havmode = 0 & ,
optc = "--binary" then do
havmode = 1; mode = "binary";
iarg += 1;
iterate
end
if havsuff = 0 & ,
optc = "--suffix" then do
if argv = 'ffffffff'x | ,
left(argv,1) = "-" then do
call _log "option : '"left(optc,12)"' requires an argument"
exit
end
havsuff = 1
suff = lower(argv)
iarg += 2
iterate
end
if havproc = 0 & ,
optc = "--repl" then do
havproc = 1; repl = 1 ;
iarg += 1
iterate
end
if havproc = 0 & ,
optc = "--info" then do
havproc = 1; info = 1; extr = 0;
iarg += 1
iterate
end
if havproc = 0 & ,
optc = "--dump" then do
havproc = 1; dump = 1; extr = 0;
iarg += 1
iterate
end
if havproc = 0 & ,
optc = "--list" then do
havproc = 1; list = 1; extr = 0;
iarg += 1
iterate
end
if havreal = 0 & ,
optc = "--real" then do
havreal = 1; real = 1;
iarg += 1
iterate
end
if optc = "-m" | ,
optc = "--mbr" then do
if argv = 'ffffffff'x | ,
left(argv,1) = "-" then do
call _log "option : '"left(optc,12)"' requires an argument"
exit
end
mbrs = mbrs lower(argv)
iarg += 2
iterate
end
args = args optc
iarg += 1
end
argc = words(args)
if argc = 0 then do
call _log "need an argument (file to be UNXMITted)! "
exit
end
call _Start
if dump then ,
call _log "'--dump' specified, all other options will be ignored"
else ,
if info then ,
call _log "'--info' specified, all other options will be ignored"
else ,
if list then ,
call _log "'--list' specified, all other options will be ignored"
inpf = ""
do iarg = 1 to argc
if inpf \= "" then ,
call _close inpf
argv = strip(word(args,iarg))
inpf = argv
if \_isFile(inpf) then do
inpf = argv || XMIsuffx
if \_isFile(inpf) then do
inpf = argv || XMIsuff2
if \_isFile(inpf) then do
call _log "file not found : '"argv"'"
iterate iarg
end
end
end
if \_open(inpf,"rb") then do
call _log "xerr opening : '"inpf"'"
iterate iarg
end
head = charin(inpf,2,7)
call _close inpf
if head \= XMIheadr then do
call _log "not an XMIT file : '"inpf"'"
iterate iarg
end
if \_open(inpf,"rb") then do
call _log "xerr on 2nd open of: '"inpf"'"
iterate iarg
end
call _log "Now processing : '"inpf"'"
if extr then do
dest = filespec("n",inpf)
parse var dest dest "." .
end
if '20'x = bitand(flag,'20'x) then do
/* process control record */
if outf \= "" then do
call _close outf
call _log left(memb,10)"records("right(outk,8)") file("outf")"
memb = ""
outf = ""
outk = 0
end
type = _e2a(substr(buff, 2, 6))
if dump then do
call _hexdump buff
iterate getk
end
if type = "INMR01" then do
inmr01c += 1
list = ""
buff = substr(buff, 8)
call Process_INMR01 inmr01c
iterate getk
end
if type = "INMR02" then do
inmr02c += 1
INMR02.inmr02c.inmfseq = c2d(substr(buff, 8, 4))
list = "INMR02."inmr02c".inmfseq"
buff = substr(buff, 12)
call Process_INMR02 inmr02c
if dsorg(INMR02.inmr02c.inmdsorg) = "?" then do
call _log "dsorg '"INMR02.inmr02c.inmdsorg"' not supported"
iterate iarg
end
if recfm(INMR02.inmr02c.inmrecfm) = "?" then do
call _log "recfm '"INMR02.inmr02c.inmrecfm"' not supported"
iterate iarg
end
if INMR02.inmr02c.inmutiln = "INMCOPY" & ,
dsorg(INMR02.inmr02c.inmdsorg) \= "PS" then do
call _log "xinit: mismatched utilid:" inmr02c INMR02.xfseq.inmutiln
call _log " dsorg :" inmr02c INMR02.xfseq.inmdsorg dsorg(INMR02.inmr02c.inmdsorg)
xerr = 1
end
if INMR02.inmr02c.inmutiln = "IEBCOPY" & ,
dsorg(INMR02.inmr02c.inmdsorg) \= "PO" then do
call _log "xinit: mismatched utilid:" inmr02c INMR02.xfseq.inmutiln
call _log " dsorg :" inmr02c INMR02.xfseq.inmdsorg dsorg(INMR02.inmr02c.inmdsorg)
xerr = 1
end
iterate getk
end
if type = "INMR03" then do
fseq += 1
if fseq \= INMR02.fseq.inmfseq then ,
signal logic_error
inmr03c += 1
list = ""
buff = substr(buff, 8)
call Process_INMR03 inmr03c
if extr then do
if real then ,
dest = INMR02.inmr03c.inmdsnam
call _log "Extracting to : '"dest"'"
if \_ispath(dest) then ,
"mkdir -p" dest
end
iebcrseq = 1
iterate getk
end
call process_INMRXX
if type = "INMR06" then ,
leave getk
end
if info then ,
iterate getk
if dump then do
call _hexdump buff
iterate getk
end
if dsorg(INMR02.inmr03c.inmdsorg) = "PS" then do
if outf = "" then do
if symbol("INMR02."inmr03c".inmterm") = "VAR" then ,
mail = ".mail"
else ,
mail = ""
if substr(buff,PSoffset+1,7) = XMIheadr then do
suff = XMIsuffx
asis = 1
end
else ,
call ftype substr(buff, PSoffset, 16)
file = filespec("n",inpf)
parse var file file "." .
outf = dest || "/" || file || mail || suff
if \_open(outf,"wr") then do
call _log "error opening" "'"outf"'"
call _log "* * * TERMINATING * * *"
exit
end
outk = 0
end
outk += 1
if asis then ,
call charout outf, substr(buff, PSoffset)
else ,
call lineout outf, _e2a(substr(buff, PSoffset))
iterate getk
end
else ,
if dsorg(INMR02.inmr03c.inmdsorg) = "PO" then do
if iebcrseq = 1 then do
/* IEBCOPY first control record */
if ( substr(buff, R1offset, 4) = PDSheadr ) then ,
iebcpdst = "PDS"
else ,
if ( substr(buff, R1offset, 4) = POEheadr ) then ,
iebcpdst = "PDSE"
else do
call _log "invalid IEBCOPY control info '"upper(c2x(substr(buff, R1offset, 4)))"'x "
exit
end
r1dsorg = c2x(substr(buff, R1offset+4 ,2))
r1blksz = c2d(substr(buff, R1offset+6 ,2))
r1lrecl = c2d(substr(buff, R1offset+8 ,2))
LRECL = c2d(substr(buff, R1offset+8 ,2))
r1recfm = c2x(substr(buff, R1offset+10,2))
r1tblksz = c2x(substr(buff, R1offset+14,2))
r1devtyp = c2x(substr(buff, R1offset+16,20))
TRKSXCYL = c2d(substr(buff, R1offset+26,2))
iebcrseq = 2
iterate getk
end
if iebcrseq = 2 then do
/* IEBCOPY second control record */
debp = R2offset
debt.0 = c2d(substr(buff, debp, 1))
relt = 0
do i = 1 to debt.0
debp = debp + 16
debf = c2d(substr(buff, debp+6 ,2)) * TRKSXCYL + ,
c2d(substr(buff, debp+8 ,2))
debl = c2d(substr(buff, debp+10,2)) * TRKSXCYL + ,
c2d(substr(buff, debp+12,2))
trks = c2d(substr(buff, debp+14,2))
size = debl - debf + 1
if size \= trks then ,
signal logic_error
debt.i.1 = debf
debt.i.2 = debl
debt.i.3 = relt
debt.i.4 = relt + trks
relt = relt + trks
end
iebcrseq = 3
iterate getk
end
if iebcrseq = 3 then do
/* IEBCOPY directory control record */
buff = substr(buff,R3offset)
do dirk = 1 while (buff \= "")
if substr(buff,1,1) = "88"x then do
iebcrseq = 0
iterate getk
end
parse var buff . 23 dirb 277 buff
do while ( dirb \= "" )
if length(dirb) < 12 then ,
iterate dirk
if substr(dirb,1,8) = "ffffffffffffffff"x then do
iebcrseq = 0
iterate getk
end
ttr = lower(c2x(substr(dirb, 9,3)))
flg = bitand(substr(dirb,12,1),'80'x)
udl = c2d(bitand(substr(dirb,12,1),'1F'x))
if flg = '00'x then do
MEMBER.ttr = strip(_e2a(substr(dirb,1,8)))
end
dirb = substr(dirb, 12 + udl * 2 + 1)
end
end
iterate getk
end
/* process members */
offs = PDoffset
do while offs < length(buff)
blksz = c2d(substr(buff, offs + 10, 2))
select
when iebcpdst = "PDS" then do
if blksz = 0 then do
if outf \= "" then do
call _close outf
call _log left(memb,10)"records("right(outk,8)") file("outf")"
end
memb = ""
outf = ""
outk = 0
iterate getk
end
end
when iebcpdst = "PDSE" then do
if substr(buff, offs, 1) \= "00"x then do
if outf \= "" then do
call _close outf
call _log left(memb,10)"records("right(outk,8)") file("outf")"
end
memb = ""
outf = ""
outk = 0
iterate getk
end
end
otherwise ,
signal logic_error
end
if memb = "" then do
membcc = c2d(substr(buff, offs+4,2))
membhh = c2d(substr(buff, offs+6,2))
membrr = c2x(substr(buff, offs+8,1))
trk = membcc * TRKSXCYL + membhh
do i = 1 to debt.0
if ( trk >= debt.i.1 ) & ( trk <= debt.i.2 ) then ,
leave
end
ttr = lower(right(d2x(trk - debt.i.1 + debt.i.3),4,'0') || membrr)
memb = lower(strip(member.ttr))
end
offs += 12
if recfm(INMR02.inmr03c.inmrecfm) = "F" then do
do ( blksz % LRECL )
if outf = "" then do
if substr(buff, offs+1, 7) = XMIheadr then do
suff = XMIsuffx
asis = 1
end
else ,
call ftype substr(buff, offs, 16)
outf = dest || "/" || memb || suff
if \_open(outf,"wr") then do
call _log "error opening" "'"outf"'"
call _log "* * * TERMINATING * * *"
exit
end
outk = 0
end
if asis then ,
call charout outf, substr(buff, offs, LRECL)
else ,
call lineout outf, _e2a(substr(buff, offs, LRECL))
outk += 1
offs += LRECL
end
end
else ,
if recfm(INMR02.inmr03c.inmrecfm) = "V" then do
limt = offs + c2d(substr(buff, offs, 2))
offs = offs + 4
do while ( offs < limt)
LRECL = c2d(substr(buff, offs, 2))
if outf = "" then do
file = filespec("n",inpf)
parse var file file "." .
outf = dest || "/" || memb || ".txt"
if \_open(outf,"wr") then do
call _log "error opening" "'"outf"'"
call _log "* * * TERMINATING * * *"
exit
end
outk = 0
end
call lineout outf, _e2a(substr(buff, offs+4, LRECL-4))
outk += 1
offs += LRECL
end
end
else ,
signal logic_error
do while '00'x = Bitand(flag,'40'x)
leng = charin(file,,1)
flag = charin(file,,1)
buff = buff || charin(file,,c2d(leng)-2)
end
return buff
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
process_INMR01:
process_INMR02:
process_INMR03:
parse arg inmrseq
leng = length(buff)
do while ( length(buff) > 0 )
next = getkey(buff)
nfnd = 1
select
when keyn = inmdsnam_ then do
dsnam = _e2a(keyv.1)
do k = 2 to keyv.0
dsnam = dsnam || "." || _e2a(keyv.k)
end
/* dsnam = lower(dsnam) */
call value type"."inmrseq".inmdsnam", dsnam
list = strip(list) || " " || type"."inmrseq".inmdsnam"
nfnd = 0
end
otherwise ,
do k = 1 to inmkeys
if keyn = inmkey.k then do
nfnd = 0
knam = type"."inmrseq"."inmdesc.k
list = strip(list) || " " || knam
if keyv.0 = 0 then ,
kval = ""
else,
if inmconv.k = "d" then ,
kval = c2d(keyv.1)
else ,
if inmconv.k = "x" then ,
kval = c2x(keyv.1)
else ,
kval = _e2a(keyv.1)
interpret knam "= '"kval"'"
leave
end
end
end
if nfnd then ,
call _log type "unsupported key" c2x(substr(buff, 3+1,16))
buff = substr(buff,next)
end
if info then do
call _log copies("- ",30)
call _log type inmrseq leng
do v = 1 to words(list)
knam = word(list,v)
kval = value(word(list,v))
call _log left(knam,20) || "=" kval
end
end
return
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
process_INMRXX:
if info then do
call _log copies("- ",30)
call _log type
end
return
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
getkey: procedure expose keyn keyv.
parse arg buff
parse var buff ,
keyn +2 ,
coun +2 ,
.
coun = c2d(coun)
keyv.0 = coun
next = 5
do k = 1 to coun
lenv = c2d(substr(buff, next ,2))
keyv.k = substr(buff, next+2, lenv)
next = next + lenv + 2
end
return next
Joined: 27 Dec 2012 Posts: 6 Location: United States
Here's where I found information about the various parts of an XMIT file.
TSO TRANSMIT data formats are documented in:
TSO/E Customization Guide, Chapter 37 Customizing TRANSMIT and RECEIVE, Format of Transmitted Data.
IEBCOPY Directory Format area is documented in:
DFP Using Datasets, "Chapter 27, Processing a Partitioned Data Set" section.
IEBCOPY COPYR1 area is documented in:
DFP utilities Guide, "Appendix B. Unload Partitioned Dataset Format" section.
IEBCOPY COPYR2 area is documented in:
DFP utilities Guide, "Appendix B. Unload Partitioned Dataset Format" section.
DEB is mapped by system macro SYS1.MACLIB(IEZDEB).
The DEBExtent01-16 fields are highly device dependent and are required to translate absolute DASD addresses (MBBCCHHR) in the member data records to relative addresses (TTR). For DASD devices, these areas are mapped by DSECT DEBDASD in SYS1.MACLIB(IEZDEB) macro.
The DEB control block is documented in
DFSMS DFP Advanced Services.
DEVTAB information is documented in:
DFSMS DFP System Data Administration.
PDS Program Data area is mapped by the IHAPDS macro, which is described in the "MVS Program Management Advanced Facilities" guide, Appendix E "Data Areas", paragraph "PDS directory entry format on entry to STOW".
Joined: 27 Dec 2012 Posts: 6 Location: United States
I am developing a freeware application that allows data to be extracted from MVS TSO XMIT files. I call it the "MVS TSO XMIT Analyzer".
I wrote this program for fun, as well as to give back to the CBT Tape community. I found the CBT Tapes to be an invaluable resource over the years.
Here are a few more details.
Features
■ supports multiple files within the XMIT file.
■ supports Sequential, PDS, and PDS/E embedded content.
■ supports fixed-block, variable-blocked, and undefined record formats.
■ supports Message data contained within the XMIT file.
■ supports exporting of PDS and PDS/E member statistics for ISPF Source / Program data.
■ data can be extracted as-is (ie EBCDIC) or converted to Text (ie PC ASCII) format.
■ user-specified codepage conversion options to allow custom data translation.
■ supports easy viewing of embedded files - ie XMIT, PKZIP, PDF, MS Word, etc.
■ easy-to-use Graphical User-Interface.
■ drag and drop support, to allow easy extraction of source code to individual files.
■ underlying .NET Assembly API allows you to write your own programs to process XMIT files.
Requirements
■ Windows 32/64 bit Operating System.
■ Microsoft .NET v2.0 Framework.