Sometimes I code some WRITEs into my program to show the values of some fields during runtime. In some cases I additionally use EM=(HHHH) to do a kind of hexdump.
At the moment I got a very special problem. Now I want to do a write+hexdump of all fields - including GDA, LDAs, PDAs etc…
Any ideas to solve this without coding hundreds of writes? Any other ideas to simplify the coding? I only wrote some modules to hexdump fields of type C, D, T etc…
The problem is: I want to write many many dumps into a work file and compare the results with a unix-diff afterwards. There must be differences somewhere in the data, but I didn’t find one yet.
I thought about the following steps for a “coredump”:
find out all fields of a module
determine type/length for each field
write content into a workfile
write hexdump into a workfile
I solved steps 3 and 4.
Do I have to write a parser for 1 and 2? Or is there a way to use the xref data? Any other ideas?
Here is something to speed up the process, given several “maybe’s”
Suppose you have your data definitions “neatly” arranged, such as:
1 #a (a5)
1 #b (n3)
1 #c (a10)
I personally am not so neat, but perhaps your code is arranged this way. Then:
create a “dummy” line in an editor such as:
‘=’ (em=hhhhh) /
copy this line many times.
Then, you need to have an editor that will do “block selects”. For example, in Word, you use the alt key to do this. From the data definition area, select the field names only. Then copy them. Then do a block paste into the dummy lines. You will have something like:
A couple of notes. I used tabs to type the posting above. I will try spaces to demonstrate what I mean about neatly arranged data
1 #a (a5)
1 #b (n3)
1 #c (a3)
There should be about 5 blanks between the levels, the names, and the formats.
In the category of “not enough coffee yet”, WRITE WORK FILE ‘=’ #A simply writes the alpha string = followed by the value of #A, not what you want #A ABCDE
So, in the dummy line, you could type
’ ’ (em=hhhhh) /
with lots of spaces between the two apostrophes. Then you could block paste the names of the variables in two places; between the apostrophes, and after the second apostrophe.
First I did it the way you said. But I will get crazy with my hundreds of fields plus some arrays with many occurances …
I even wrote some copycodes to simlify the thing with the edit mask.
Example for dumping a logical field:
*
*
* - work file 30 (of type "unformatted") is already opened
* - an additional dynamic alpha field #a is needed
*
include hexdmpLc '#logical' '"#logical"'
*
And the copycode itself looks like this:
*
* write readable
*
compress &2& '(L)' into #a leaving no
if *LENGTH(#a) < 32
compress full #a ' '
into #a
reduce dynamic #a to 32
end-if
if &1&
write work file 30 variable #a ' = TRUE' H'0A' /* 0A = newline
else
write work file 30 variable #a ' = FALSE' H'0A'
end-if
*
* write hexdump
*
compress &2& '(L)' into #a leaving no
if *LENGTH(#a) < 32
compress full #a ' '
into #a
reduce dynamic #a to 32
end-if
write work file 30 variable #a ' = '
move edited &1& (EM=H) to #a
write work file 30 variable 'H''' #a '''' H'0A'
hmmm…the XML Toolkit might help a bit here - it generates COMPRESS statements to output structures, although it does wrap <> tags around the data elements.
At the moment I think there’s no way to distinguish between alpha, numerics and logicals automatically. So I have to use different copycodes for differnent formats…
BTW. For numeric, packed, integer and float fields, I use the following techique. I do this because it’s would be very time consuming to find out the field format of ddm-fields (since the format is not visible inside the program).
*
include hexdmpNc '#i4-field' '"#i4-field"'
include hexdmpNc '#n2-field' '"#n2-field"'
*
*
* Dump-Copycode for types F, I, N and P
*
/* to be sure that copycode is only used for F, I, N, P
if &1& = 0 ignore end-if /* would work for B, D, T also
move edited &1& (EM=Z) to +dump-iw-a /* would work for L also
*
compress numeric full &1& into #a
move edited &1& (EM=H(30)) to #a2
/* determine format of field
callnat 'hexdmpnn' #a #a2 #a2
compress &2& '(' #a2 ')' into #a2 leaving no
if *LENGTH(#a2) < 32
compress full #a2 ' ' into #a2
reduce dynamic #a2 to 32
end-if
compress numeric &1& into #a
write work file 30 variable #a2 ' = ' #a H'0A'
move edited &1& (EM=H(30)) to #a
write work file 30 variable #a2 ' = H''' #a '''' H'0A'
And here’s HEXDMPNN
*
* determine field type/length for numeric/packed/integers/floats
*
define data parameter
1 #input-parameter
2 numeric-full (A) DYNAMIC BY VALUE
2 hexdump (A) DYNAMIC BY VALUE
1 #output-parameter
2 format-length (A) DYNAMIC BY VALUE RESULT
local
1 #a (a) DYNAMIC
1 #i4 (I4)
1 #format (A1)
1 #int (I1)
1 #frac (I1)
end-define
*
reduce dynamic #output-parameter.format-length to 0
*
* Check for float
*
if #input-parameter.numeric-full = scan ('E')
decide on first value of *LENGTH(#input-parameter.numeric-full)
value 22
#output-parameter.format-length := "F8"
value 13
#output-parameter.format-length := "F4"
none value
#output-parameter.format-length := "X"
end-decide
escape module
end-if
*
* Check for Integer
*
#a := #input-parameter.numeric-full
examine #a for '-' delete
if *LENGTH(#input-parameter.hexdump) = 8 and *LENGTH(#a) = 10
#output-parameter.format-length := "I4"
escape module
end-if
if *LENGTH(#input-parameter.hexdump) = 4 and *LENGTH(#a) = 5
#output-parameter.format-length := "I2"
escape module
end-if
if *LENGTH(#input-parameter.hexdump) = 2 and *LENGTH(#a) = 3
#output-parameter.format-length := "I1"
escape module
end-if
*
* Check for decimal point
#int := *LENGTH(#a)
#frac := 0
for #i4 := 1 to *LENGTH(#a)
if substr(#a,#i4,1) = "0" or="1" or="2" or="3" or="4" or="5" or="6" or="7" or="8" or="9"
ignore
else
#int := #i4 - 1
#frac := *LENGTH(#a) - #i4
escape bottom
end-if
end-for
if *LENGTH(#input-parameter.hexdump) = 2 and #int = 1
if substr(#input-parameter.hexdump,2,1) = substr(#a,1,1)
#format := 'N'
else
if substr(#input-parameter.hexdump,1,1) = substr(#a,1,1)
#format := 'P'
else
#output-parameter.format-length := 'X'
escape module
end-if
end-if
end-if
if (#int + #frac) * 2 = *LENGTH(#input-parameter.hexdump)
compress numeric 'N' #int into #output-parameter.format-length leaving no
else
compress numeric 'P' #int into #output-parameter.format-length leaving no
end-if
if #frac ne 0
compress numeric #output-parameter.format-length "." #frac
into #output-parameter.format-length leaving no
escape module
end-if
*
end
You might consider using MOVE INDEXED. MOVE INDEXED doesn’t care about fields, so you can start at the beginning of the data area and dump whatever you want. All you have to know is when to end. Either count the total bytes in the data area or add a unique character string at the end that you can recognize. Below is a very simple sample. Taking it further, you could write this as a subprogram, pass the starting field to the subprogram and dump until you figure out how to stop. As for writing the the file, you might want to consider writing it as DEFINE WORK … UNFORMATTED. If you are MVS, RECFM=U works very nicely.
Here’s a sample of code. You can enhance this to if you find it useful.
MOVEIND1 Dump LDA using MOVE INDEXED
DEFINE DATA LOCAL
1 #A1(A1)
1 #A (A40000)
1 #B (A40000)
/* only dump to here
1 #I4(I4)
1 #I5(I4)
1 #X1(A1)
END-DEFINE
MOVE ‘ABC’ TO #A
MOVE ‘XYZ’ TO SUBSTR(#A,39998,3)
MOVE ‘123’ TO #B
/*Remember, #A1 must be skipped
**FOR #I4 = 2 80001 /*start at next byte
FOR #I4 = 39998 40005 /*Fields don’t matter to MOVE INDEXED
MOVE INDEXED #A1 TO #X1 #I5 := #I4 - 1
PRINT #X1#X1 (EM=H)
CLOSE LOOP
END
You might consider using MOVE INDEXED. MOVE INDEXED doesn’t care about fields, so you can start at the beginning of the data area and dump whatever you want. All you have to know is when to end. Either count the total bytes in the data area or add a unique character string at the end that you can recognize. Below is a very simple sample. Taking it further, you could write this as a subprogram, pass the starting field to the subprogram and dump until you figure out how to stop. As for writing the the file, you might want to consider writing it as DEFINE WORK … UNFORMATTED. If you are MVS, RECFM=U works very nicely.
Here’s a sample of code. You can enhance this to if you find it useful.
MOVEIND1 Dump LDA using MOVE INDEXED
DEFINE DATA LOCAL
1 #A1(A1)
1 #A (A40000)
1 #B (A40000)
/* only dump to here
1 #I4(I4)
1 #I5(I4)
1 #X1(A1)
END-DEFINE
MOVE ‘ABC’ TO #A
MOVE ‘XYZ’ TO SUBSTR(#A,39998,3)
MOVE ‘123’ TO #B
/*Remember, #A1 must be skipped
**FOR #I4 = 2 80001 /*start at next byte
FOR #I4 = 39998 40005 /*Fields don’t matter to MOVE INDEXED
MOVE INDEXED #A1 TO #X1 #I5 := #I4 - 1
PRINT #X1#X1 (EM=H)
CLOSE LOOP
END