HP OpenVMS Systemsask the wizard |
The Question is:
In July 1998, you answered a user's question relating to conversion of VFC
format
files to sequential variable files. Your answer read:
``COBOL uses VFC for files used with BEFORE or AFTER ADVANCING keywords,
for files created by the report writer, and for files marked with
GLOBAL
or EXTERNAL. You might also be able to use FDL$CREATE to create the
desired attributes, and then use OPEN EXTEND.
If you have a software support contract, check on DSNlink or with the
customer support center folks for a copy of the article "Example-COBOL
Converting VFC Files To Seq. Variable On OpenVMS AXP".''
I do have a support contract, and I have searched DSNLink, and the CSC folks
have
searched their databases to no avail. Can you please e-mail me this article
or give
me a steer to a web link, etc. where I can obtain it? I have for many years
used a
COBOL program, CONVERT-VFC on VAXen to deal with this problem;
unfortunately, it is
broken on an Alpha machine - it aborts with an ACCVIO dump.
Please help!
The Answer is :
The OpenVMS Wizard finds that other tools are typically better suited to
this file format conversion task -- use of C or Macro32 code, or code
that uses the DCL CONVERT utility, would likely be rather easier.
The following (untested) example is likely close to what you need.
This is DEC COBOL code -- the Wizard would not expect this to
operate on VAX COBOL.
$ CREATE FDLDEF.MAR
.TITLE FDLDEF
$FDLDEF GLOBAL
.END
$ MACRO FDLDEF
$ COBOL SYS$INPUT/OBJECT=CONVERT_VFC
PROGRAM:
identification division.
*******************************************************************************
* This program requires a MACRO program in order to link. The MACRO
* program can be created to look like this (you can call it FDLDEF.MAR):
* .TITLE FDLDEF
* $FDLDEF GLOBAL
* .END
* The following commands can be used to compile and link the programs:
* $ MACRO FDLDEF
* $ COBOL CONVERT_VFC
* $ LINK CONVERT_VFC,FDLDEF
************************
* Please note that this is a fairly detailed example. In an effort to
* simplify things as much as possible, error checking and the anticipation
* of contingencies has been kept to a minimum. Feel free to enhance this
* aspect of this program once you understand the underlying concepts.
************************
* Also note that, with many devices, there is an implied line feed
* pending prior to the *first* record being written. If you notice
* that printed output from the converted file starts one line farther
* down the page than expected, this could be the explanation. This
* program converts the VFC information without giving consideration
* to such external factors.
* If you wish to suppress a line feed in the first record, evaluate
* the prefix byte of the first record to see if there is a line feed
* that is not necessary. If so, you can remove it. If not, there is
* not much else you can do (at least from the perspective of this
* program).
*******************************************************************************
program-id. convert_vfc.
environment division.
configuration section.
input-output section.
file-control.
select in_file
assign to in_file
organization sequential.
select optional out_file
assign to out_file
organization sequential.
* By default, RMS ignores (i.e. discard) the VFC bytes when reading
* a VFC-formatted file. To override this, it is necessary to instruct
* RMS to do otherwise. In COBOL, this can be accomplished by applying
* print-control to the input file. This will cause RMS to reserve a
* field in memory to hold the VFC bytes. The address of that field
* will be placed into the RHB field of the RAB.
i-o-control.
apply print-control on in_file.
data division.
file section.
fd in_file
value of id is in_filename
record varying from 1 to 255 depending on in_size.
01 in_rec.
03 in_data pic x(255).
* Note that the size of the output record is set up to be greater
* than the size of the input record. This is because the VFC
* (carriage control) information is embedded into the output
* record (thus increasing its size).
fd out_file
value of id is out_filename
record varying from 1 to 765 depending on out_size.
01 out_rec.
03 out_data pic x(765).
working-storage section.
01 variables.
05 stat pic s9(9) comp.
05 rab_address pic 9(9) comp.
05 rhb_address pic 9(9) comp.
05 vfc_address pic 9(9) comp.
05 in_filename pic x(255).
05 out_filename pic x(255).
05 vfc_switch pic x(7).
05 vfc_bytes.
10 prefix_byte pic x.
10 postfix_byte pic x.
05 record_number pic 9(9) comp.
05 work_string pic x(255).
05 work_string_len pic s9(4) comp.
05 total_size pic s9(4) comp.
05 bit7 pic s9(9) comp.
05 bit6 pic s9(9) comp.
05 bit5 pic s9(9) comp.
05 bit4 pic s9(9) comp.
05 bits0-6 pic s9(9) comp.
05 bits0-4 pic s9(9) comp.
05 in_size pic 9(4) comp.
05 out_size pic 9(4) comp.
05 prefix_data pic x(255).
05 prefix_size pic 9(4) comp.
05 postfix_data pic x(255).
05 postfix_size pic 9(4) comp.
** Constructs to emulate byte-sized integers...
05 work_num_long pic 9(9) comp.
05 work_num_bytes redefines work_num_long.
10 work_num_byte1 pic x.
10 filler pic x(3).
05 ext_size_word pic s9(4) comp.
05 ext_size_bytes redefines ext_size_word.
10 ext_size_byte1 pic x.
10 filler pic x.
05 ascii_c0_int pic s9(9) comp.
05 ascii_c0_txt redefines ascii_c0_int.
10 c0_char pic x.
10 filler pic x(3).
01 constants.
05 out_bufsize pic s9(4) comp value 765.
05 cr_char pic x value x"0D".
05 lf_char pic x value x"0A".
05 two pic 9(4) comp value 2.
05 four_w pic 9(4) comp value 4.
05 four pic 9(9) comp value 4.
05 five pic 9(9) comp value 5.
05 six pic 9(9) comp value 6.
05 seven pic 9(9) comp value 7.
05 pos0-4 pic 9(9) comp value 0.
05 pos0-6 pic 9(9) comp value 0.
01 flags.
05 eof_flag pic x.
88 eof value high-values.
01 external_constants.
05 rab$l_rhb pic s9(9) comp value external rab$l_rhb.
05 fdl$m_fdl_string pic 9(9) comp value external fdl$m_fdl_string.
01 fdl_string.
05 filler pic x(7) value "SYSTEM;".
05 filler pic x(32) value "SOURCE OpenVMS;".
05 filler pic x(5) value "FILE;".
05 filler pic x(35) value "ORGANIZATION sequential;".
05 filler pic x(7) value "RECORD;".
05 filler pic x(40) value "CARRIAGE_CONTROL none;".
05 filler pic x(33) value "FORMAT variable;".
05 filler pic x(26) value "SIZE 0;".
procedure division.
0000-main.
* Have the user enter the name of the input file and then open it...
display "What is the name of the file to be converted? " no advancing.
accept in_filename.
open input in_file.
* In order to evaluate the VFC bytes for each record, it is necessary
* to determine where RMS puts them. The RAB$L_RHB field contains the
* address of the field RMS sets up to hold the VFC byte values. So,
* the next step is to isolate the RHB field and obtain the address it
* contains.
perform 0100-get-rhb-contents.
* See what the user wants to name the output file...
display "What do you want to name the output file? " no advancing.
accept out_filename.
* By default, COBOL-created files are typically of a VFC format.
* Since we are converting *from* VFC, it would seem pointless to put
* the results back into a VFC file. So, this program uses the
* callable interface to FDL to pre-create a file with sequential,
* variable attributes. This also provides a way to create the file
* with no implied carriage control (since all the carriage control
* information will be embedded in each record). We can then open
* that existing, empty file and write the converted information to it.
call "fdl$create"
using
by descriptor fdl_string
out_filename
omitted
omitted
omitted
by reference fdl$m_fdl_string
giving
stat.
if stat is failure call "lib$stop" using by value stat.
* At this point an empty file exists (with the characteristics we want),
* so open it EXTEND.
open extend out_file.
* Now that the files are open, read each record from the input file,
* convert the VFC bytes, and write the converted result to the
* output file. Note that the VFC information is translated and then
* embedded into the record that is written to the file.
perform 0200-read-infile.
perform until eof
move "PREFIX " to vfc_switch
perform 0300-process-vfc
move work_string (1:work_string_len) to prefix_data
move work_string_len to prefix_size
move "POSTFIX" to vfc_switch
perform 0300-process-vfc
move work_string (1:work_string_len) to postfix_data
move work_string_len to postfix_size
* At this point, the prefix information, the input record data,
* and the postfix information have been formulated. All that
* is left to do is piece them together in the output field and
* write the resulting record to the output file. The only check
* made at this point is to make sure the resulting string is not
* too large for the output buffer.
compute
total_size = prefix_size + in_size + postfix_size
end-compute
if total_size > out_bufsize
display " "
display "Calculated record size greater than output buffer size..."
display "...Record: " record_number with conversion
display "...Data portion of record will be truncated to fit."
compute
in_size = in_size - (total_size - out_bufsize)
end-compute
end-if
string
prefix_data(1:prefix_size) delimited by size
in_data(1:in_size) delimited by size
postfix_data(1:postfix_size) delimited by size
into
out_data
end-string
compute
out_size = prefix_size + in_size + postfix_size
end-compute
write out_rec
perform 0200-read-infile
end-perform.
close out_file.
close in_file.
display " ".
display "Records processed: " record_number with conversion.
display " ".
stop run.
0100-get-rhb-contents.
* Determine where the RHB field in the RAB is and place the address
* it contains into vfc_address.
* The process of isolating the RHB field can be started by calling
* DCOB$RMS_CURRENT_RAB. This returns the starting address of the
* RAB. The RHB field can then be accessed by indexing into the RAB
* structure (starting from the address retrieved) the appropriate
* number of bytes. The symbolic constant RAB$L_RHB is used in this
* example to represent that offset value.
* Get the starting address of the RAB...
call "dcob$rms_current_rab"
giving
rab_address.
* Add the RHB offset value to the starting address to derive the
* address of the RHB field...
add rab$l_rhb to rab_address giving rhb_address.
* Use LIB$MOVC3 to extract the address contained in the RHB field.
* This is the address where the VFC information is written after
* each read.
call "lib$movc3"
using
by reference four_w
by value rhb_address
by reference vfc_address.
0200-read-infile.
move spaces to in_rec out_rec prefix_data postfix_data vfc_bytes.
move 0 to in_size out_size prefix_size postfix_size.
read in_file
at end
set eof to true
end-read.
* Move the VFC bytes from the holding area (the address of which
* we obtained from the RHB field of the RAB earlier) to a place
* we can work with.
if not eof
add 1 to record_number
call "lib$movc3"
using
by reference two
by value vfc_address
by reference vfc_bytes
end-if.
0300-process-vfc.
* Following is a table taken from the OpenVMS Record Management
* Services Reference Manual. This table describes what the
* different bit patterns of VFC bytes represent. Please note that
* the prefix and postfix VFC bytes must be processed individually.
* ---------------------------------------------------------------------------
* Bit 7 Bit 6 Bit 5 Bit 4 Meaning
* ---------------------------------------------------------------------------
* 0 0 0 0 To specify no carriage control (NULL),
* set bits 3 through 0 to 0.
* 0 x x x Use bits 6 through 0 to specify a
* count of new lines (line feed followed
* by carriage return).
* 1 0 0 x Output the ASCII C0 control character
* specified by the configuration of bits
* 4 through 0.
* 1 0 1 x Reserved.
* 1 1 0 0 Skip to the vertical format unit (VFU)
* channel (1-16) specified by bits 3
* through 0. Devices that do not have
* hardware VFUs translate these codes as
* a 1-line advance.
* 1 1 0 1 Reserved.
* 1 1 1 0 Reserved.
* Evaluate vfc_switch to determine if the prefix or postfix byte is to
* be processed.
move spaces to work_string.
move 0 to work_string_len work_num_long.
if vfc_switch equal "PREFIX "
move prefix_byte to work_num_byte1
else
move postfix_byte to work_num_byte1
end-if.
* Evaluate the VFC byte of interest to determine what it means.
* Load the appropriate information into the work area based on this
* evaluation. In order to evaluate the meaning of the VFC byte, it
* is necessary to examine it at a binary level. LIB$EXTZV is used
* to help isolate the bits that need to be evaluated. Please note
* that this program treats VFU references as a 1-line advance.
* Reserved bit patterns are treated as Null carriage control. An
* informational message is displayed if any of these bit patterns
* are detected (the record *is* written).
move 1 to ext_size_word.
call "lib$extzv"
using
by reference seven
by reference ext_size_byte1
by reference work_num_long
giving
bit7.
call "lib$extzv"
using
by reference six
by reference ext_size_byte1
by reference work_num_long
giving
bit6.
call "lib$extzv"
using
by reference five
by reference ext_size_byte1
by reference work_num_long
giving
bit5.
call "lib$extzv"
using
by reference four
by reference ext_size_byte1
by reference work_num_long
giving
bit4.
move 5 to ext_size_word.
call "lib$extzv"
using
by reference pos0-4
by reference ext_size_byte1
by reference work_num_long
giving
bits0-4.
move 7 to ext_size_word.
call "lib$extzv"
using
by reference pos0-6
by reference ext_size_byte1
by reference work_num_long
giving
bits0-6.
evaluate bit7
when 0
evaluate bits0-6
when 0
* Null carriage control (00000000).
move spaces to work_string
move 0 to work_string_len
when other
* Bits 0-6 equal number of lf/cr's (0xxxyyyy).
perform bits0-6 times
move lf_char to work_string (work_string_len + 1 : 1)
add 1 to work_string_len
move cr_char to work_string (work_string_len + 1 : 1)
add 1 to work_string_len
end-perform
end-evaluate
when other
evaluate bit6
when 0
evaluate bit5
when 0
* Bits 0-4 represent an ASCII C0 value (100xyyyy).
move bits0-4 to ascii_c0_int
move c0_char to work_string
move 1 to work_string_len
when other
* Reserved (101xyyyy).
display " "
display "Reserved bit pattern detected (101x)..."
display " ...Record: "
record_number with conversion
display " ...VFC byte: " vfc_switch
display " ...Record will be written, but that "
"VFC byte will be ignored."
move spaces to work_string
move 0 to work_string_len
end-evaluate
when other
evaluate bit5
when 0
evaluate bit4
when 0
* VFU reference (1100yyyy). This program
* treats these as a 1-line advance.
display " "
display "VFU bit pattern detected "
"(1100)..."
display " ...Record: "
record_number with conversion
display " ...VFC byte: " vfc_switch
display " ...Record will be written, but "
"that VFC byte will be treated "
"as LF/CR."
move lf_char to work_string
(work_string_len + 1 : 1)
add 1 to work_string_len
move cr_char to work_string
(work_string_len + 1 : 1)
add 1 to work_string_len
when other
* Reserved (1101yyyy).
display " "
display "Reserved bit pattern "
"detected (1101)..."
display " ...Record: "
record_number with conversion
display " ...VFC byte: " vfc_switch
display " ...Record will be written, but "
"that VFC byte will be ignored."
move spaces to work_string
move 0 to work_string_len
end-evaluate
when other
* Reserved (111xyyyy).
display " "
display "Reserved bit pattern detected (111x)..."
display " ...Record: "
record_number with conversion
display " ...VFC byte: " vfc_switch
display " ...Record will be written, but that "
"VFC byte will be ignored."
move spaces to work_string
move 0 to work_string_len
end-evaluate
end-evaluate
end-evaluate.
end program convert_vfc.
$ LINK CONVERT_VFC,FDLDEF
|