  working with COBOL VFC files? (take II) 
 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 &gt; 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
