dynamic-memory-allocationcobolsubscriptgnucobol

COBOL Subscript Variable Length String


I'm learning COBOL and am attempting to read an unformatted file with C style file i/o (reading x bytes at a time). I've so far succeeded in reading the file one byte at a time and displaying the individual characters immediately, but I've been unable to store the contents of the file in a variable.

The code that I've tried is this:

        IDENTIFICATION DIVISION.
        PROGRAM-ID. FILEREADER.

        ENVIRONMENT DIVISION.
        INPUT-OUTPUT SECTION.
        FILE-CONTROL.
            SELECT FILE-TO-READ
            ASSIGN USING INPUT-FILE-NAME 
            ORGANIZATION IS SEQUENTIAL
            ACCESS IS SEQUENTIAL.

        DATA DIVISION.
        FILE SECTION.
        FD FILE-TO-READ.
            01 CHAR PIC X(1).

        WORKING-STORAGE SECTION.
        01 FILE-DATA-POINTER USAGE IS POINTER.
        01 FILE-DATA-HOLDER PIC X BASED.
        01 INPUT-DATA.
            05 INPUT-FILE-NAME PIC X(100). 
            05 INPUT-FILE-CHARACTERS PIC 9(18).
        01 MISC-DATA.
           05 EOF PIC X(1).
           05 STRING-INDEX PIC 9(18) USAGE IS COMP.
        PROCEDURE DIVISION.
        MAIN.
            PERFORM GET-INPUT.
            PERFORM ALLOCATE-STORAGE.
            PERFORM READ-FILE.
            PERFORM DISPLAY-FILE.
            PERFORM CLEAN-UP.
            STOP RUN.

        GET-INPUT.
            DISPLAY "Please enter the name of the file (maximum 100 char
      -     "acters)."
            ACCEPT INPUT-FILE-NAME.

            DISPLAY "Please enter the number of characters in your file
      -     "(the number of bytes)."
            ACCEPT INPUT-FILE-CHARACTERS.

        ALLOCATE-STORAGE.
            ALLOCATE INPUT-FILE-CHARACTERS CHARACTERS RETURNING
            FILE-DATA-POINTER SET ADDRESS OF FILE-DATA-HOLDER TO
            FILE-DATA-POINTER.
            
            IF ADDRESS OF FILE-DATA-POINTER = NULL
                DISPLAY "System error when allocating RAM. Exiting..."
                STOP RUN
            END-IF.

        READ-FILE.
            INITIALIZE STRING-INDEX.
            MOVE 'N' TO EOF.
            MOVE 1 TO STRING-INDEX.
            OPEN INPUT FILE-TO-READ.
                PERFORM UNTIL EOF = 'Y'
                    READ FILE-TO-READ 
                        AT END MOVE 'Y' TO EOF
                        NOT AT END MOVE CHAR TO FILE-DATA-HOLDER
                        (STRING-INDEX:1)

                        ADD 1 TO STRING-INDEX    
                    END-READ
                END-PERFORM.
           CLOSE FILE-TO-READ.
       
        DISPLAY-FILE.
            DISPLAY FILE-DATA-HOLDER.
       
        CLEAN-UP.
            FREE FILE-DATA-POINTER.

When compiling this program I get the warning:

file_reader.cbl:64: warning: suspicious reference-modification: always using max. length [-Wothers]
   62 |                     READ FILE-TO-READ
   63 |                         AT END MOVE 'Y' TO EOF
   64 >                         NOT AT END MOVE CHAR TO FILE-DATA-HOLDER
   65 |                         (STRING-INDEX:1)

The program outputs the following:

Please enter the name of the file (maximum 100 characters).
test.txt         <------ User input
Please enter the number of characters in your file (the number of bytes).
4                <------ Also user input
L

In this example, the text file contains the phrase "Lorem Ipsum" ASCII encoded.

After some research I figured out that this is because strings can't be subscripted/indexed like in C or Python. I then tried to use an array (table) of characters instead of a string, but this didn't work because you apparently can't dynamically allocate tables in COBOL, so now I'm back at square one.

To summarize, I need to find a way to store an entire file in a dynamically allocated variable in standard COBOL.

Any help is appreciated.

EDIT: I've figured out that the problem is not with the subscripting, but rather that the string only has a length of 1 character.


Solution

  • I've figured out what I was doing wrong. The picture for the variable FILE-DATA-HOLDER was limiting the number of characters that could be contained in the variable (obvious now that I think about it). All I had to do was change the picture of the aforementioned variable and slightly tweak the DISPLAY statement at the end.

    Here is my updated code:

            IDENTIFICATION DIVISION.
            PROGRAM-ID. FILEREADER.
    
            ENVIRONMENT DIVISION.
            INPUT-OUTPUT SECTION.
            FILE-CONTROL.
                SELECT FILE-TO-READ
                ASSIGN USING INPUT-FILE-NAME 
                ORGANIZATION IS SEQUENTIAL
                ACCESS IS SEQUENTIAL.
    
            DATA DIVISION.
            FILE SECTION.
            FD FILE-TO-READ.
                01 CHAR PIC X(1).
    
            WORKING-STORAGE SECTION.
            01 FILE-DATA-POINTER USAGE IS POINTER.
            01 FILE-DATA-HOLDER PIC X(2147483646) BASED.
            01 INPUT-DATA.
                05 INPUT-FILE-NAME PIC X(100). 
                05 INPUT-FILE-CHARACTERS PIC 9(10).
            01 MISC-DATA.
               05 EOF PIC X(1).
               05 STRING-INDEX PIC 9(18) USAGE IS COMP. 
            PROCEDURE DIVISION.
            MAIN.
                PERFORM GET-INPUT.
                PERFORM ALLOCATE-STORAGE.
                PERFORM READ-FILE.
                PERFORM DISPLAY-FILE.
                PERFORM CLEAN-UP.
                STOP RUN.
    
            GET-INPUT.
                DISPLAY "Please enter the name of the file (maximum 100 char
          -     "acters)."
                ACCEPT INPUT-FILE-NAME.
    
                DISPLAY "Please enter the number of characters in your file
          -     "(the number of bytes)."
                ACCEPT INPUT-FILE-CHARACTERS.
    
            ALLOCATE-STORAGE.
                ALLOCATE INPUT-FILE-CHARACTERS CHARACTERS RETURNING
                FILE-DATA-POINTER SET ADDRESS OF FILE-DATA-HOLDER TO
                FILE-DATA-POINTER.
                
                IF ADDRESS OF FILE-DATA-POINTER = NULL
                    DISPLAY "System error when allocating RAM. Exiting..."
                    STOP RUN
                END-IF.
    
            READ-FILE.
                INITIALIZE STRING-INDEX.
                MOVE 'N' TO EOF.
                MOVE 1 TO STRING-INDEX.
                OPEN INPUT FILE-TO-READ.
                    PERFORM UNTIL EOF = 'Y'
                        READ FILE-TO-READ 
                            AT END MOVE 'Y' TO EOF
                            NOT AT END MOVE CHAR TO FILE-DATA-HOLDER
                            (STRING-INDEX:1)
    
                            ADD 1 TO STRING-INDEX    
                        END-READ
                    END-PERFORM.
               CLOSE FILE-TO-READ.
           
            DISPLAY-FILE.
                DISPLAY FILE-DATA-HOLDER (1:STRING-INDEX).
    
           
            CLEAN-UP.
                FREE FILE-DATA-POINTER.