Is there any way that can let me cancel the effect of a "GO TO" in cobol
Example:
PERFORM PROCEDURE1 THRU E--PROCEDURE1
display "PERFORM PROCEDURE2 THRU E--PROCEDURE2"
PERFORM PROCEDURE2 THRU E--PROCEDURE2
GOBACK.
PROCEDURE1 SECTION.
display "BEGIN============PROC PROCEDURE1"
PERFORM LECTURE THRU E--LECTURE
PERFORM ENDD THRU E--ENDD
display " ENDD============PROC PROCEDURE1"
CONTINUE.
EXIT.
E--PROCEDURE1.
EXIT.
LECTURE.
display "I AM LABEL LECTURE"
GO TO ENDD
CONTINUE.
E--LECTURE.
EXIT.
ENDD.
DISPLAY "I AM LABEL ENDD"
CONTINUE.
E--ENDD.
EXIT.
PROCEDURE2 SECTION.
display "BEGIN============PROC PROCEDURE2"
DISPLAY "I AM LABEL PROCEDURE2"
display " ENDD============PROC PROCEDURE2"
CONTINUE.
E--PROCEDURE2.
EXIT.
When this code is executed, i won't see the display "PERFORM PROCEDURE2 THRU E--PROCEDURE2", beacause when i made a GO TO the paragraph ENDD, the control is permantely transfered into that paragraph and i think there is no way that i can use to goback to the end of proc1 ? am-i wrong ?
#Bill
This is the program that i am trying to translate:
GOTO5POW : PROC OPTIONS(MAIN);
DCL FILE001 FILE INPUT RECORD ENV(RECSIZE(15));
DCL CTR1 PIC'99' INIT('0');
DCL CARTE CHAR(15);
CALL PROCEDURE1;
PUT SKIP LIST("PERFORM PROCEDURE2 THRU E--PROCEDURE2");
CALL PROCEDURE2;
PROCEDURE1: PROC;
PUT SKIP LIST("BEGIN============PROC PROCEDURE1");
ON ENDFILE(FILE001) GOTO ENDD;
LECTURE:
PUT SKIP LIST("I AM LABEL LECTURE");
READ FILE(FILE001) INTO(CARTE);
CTR1 = CTR1 + 1;
PUT SKIP LIST("Record "||CTR1||"=/" || CARTE ||"/");
IF CARTE="AAAAAAAAAAAAAAA" THEN GOTO ENDD;
ENDD:
PUT SKIP LIST("I AM LABEL ENDD");
PUT SKIP LIST(" ENDD============PROC PROCEDURE1");
END PROCEDURE1;
PROCEDURE2 : PROC;
PUT SKIP LIST("BEGIN============PROC PROCEDURE2");
PUT SKIP LIST("I AM LABEL PROCEDURE2");
PUT SKIP LIST(" ENDD============PROC PROCEDURE2");
END PROCEDURE2;
END GOTO5POW ;
The output is:
BEGIN============PROC PROCEDURE1
I AM LABEL LECTURE
Record 01=/AAAAAAAAAAAAAAA/
I AM LABEL ENDD
ENDD============PROC PROCEDURE1
PERFORM PROCEDURE2 THRU E--PROCEDURE2
BEGIN============PROC PROCEDURE2
I AM LABEL PROCEDURE2
ENDD============PROC PROCEDURE2
But with cobol, this the generated output:
BEGIN============PROC PROCEDURE1
I AM LABEL LECTURE
Record 01=/AAAAAAAAAAAAAAA/
I AM LABEL ENDD
BEGIN============PROC PROCEDURE2
I AM LABEL PROCEDURE2
ENDD============PROC PROCEDURE2
As bill said, do not use thru. Also use either Procedures or sections; not both. The ON ENDFILE(FILE001) sets up exception handler for end-of file
This roughly what your program should be:
03 pic x value 'N'.
88 File001-EOF value 'Y'.
88 File001-has-data value 'N'.
PERFORM PROCEDURE1
display "PERFORM PROCEDURE2 THRU E--PROCEDURE2"
PERFORM PROCEDURE2
GOBACK.
PROCEDURE1 SECTION.
display "BEGIN============PROC PROCEDURE1"
display "I AM LABEL LECTURE"
Read File001
at end set File001-EOF to true
end-read
if File001-has-data
compute CTR1 = CTR1 + 1;
end-if
DISPLAY "I AM LABEL ENDD"
display " ENDD============PROC PROCEDURE1"
EXIT.
E--PROCEDURE1.
EXIT.
PROCEDURE2 SECTION.
display "BEGIN============PROC PROCEDURE2"
DISPLAY "I AM LABEL PROCEDURE2"
display " ENDD============PROC PROCEDURE2"
CONTINUE.
E--PROCEDURE2.
EXIT.
For people more familiar with java / c# the
ON ENDFILE(FILE001) GOTO ENDD;
setups up an exception or error handler for the End-Of-File condition. In java it would be like:
try {
....
} catch (endOfFile e) {
Goto ENDD;
}
Related
I am working on Fujitsu COBOL and there is a situation where I have to read the file and if there is a junk value then I want to abend the job.
When there is a junk value the return code is 10 but the catch is that when there is EOF (end of file) even then the return code is 10.
Please help me out on how can I differentiate between these two events based on the return code?
On very early PCs (MS-DOS 1.0), a Ctrl+Z (X"1A") was used as end-of-file for text files. Your compiler is likely detecting that character and treating it as end-if-file instead of allowing the remainder of the file to be processed.
In the OEM/DOS character set, X"1A" appears as a right-pointing arrow. (You mentioned an arrow in a comment.) I created a short file with four records for testing.
The original file:
Record1
Record2
Record3
Record4
I replaced the "3" with X"1A". In a hex editor, it appears as,
Notice that the x"1A" character appears as a right-pointing arrow.
A wrote a program (not shown) to read the file displaying the output of the modified file.
Record1
Record2
Record
0003 lines read
End of file occurred at the X"1A".
The following program may be used to check for X"1A" and "identify the corrupt record occurrence ... so that [you] can correct the file".
program-id. ctrl-z.
environment division.
input-output section.
file-control.
select in-file assign "ctrl-z.txt"
organization sequential
.
data division.
file section.
fd in-file.
1 in-char pic x.
working-storage section.
1 line-count binary pic 9(4) value 0.
1 msg-txt pic x(25) value "lines read".
88 error-msg value "lines read before cntrl-z".
1 eof-in-file-flag pic 9 value 0.
88 eof-in-file value 1.
procedure division.
begin.
open input in-file
read in-file
at end set eof-in-file to true
end-read
perform until eof-in-file
evaluate in-char
when x"0A"
add 1 to line-count
when x"1A"
set error-msg to true
set eof-in-file to true
exit perform
when other
continue
end-evaluate
read in-file
at end set eof-in-file to true
end-read
end-perform
close in-file
display line-count space msg-txt
stop run
.
end program ctrl-z.
The result was:
0002 lines read before cntrl-z
In the following code, I've just shown a method of differentiating end of file and junk value when both have File Status code of 10.
In the program, a file is being read. FILE STATUS for the input file being read is WS-ST. Record layout is just 1 field named as NAME1. I'm not really sure how a junk value (in your case) is causing the FILE STATUS to have 10 as value. But, in a way to reproduce the error you're facing, I've moved 10 to WS-ST, whenever NAME1 field have SRINIVASAN as value. I've also broken the READ loop, when NAME1 has got SRINIVASAN.
After the READ loop, the FILE STATUS data item is being checked. If it holds 10, another READ is issued. Here, I just came across FILE STATUS 46.
46 - A sequential READ operation has been tried on a file open in the INPUT or I-O mode but no valid next record has been established.
Handling FILE STATUS 46 genuinely lets you know if the file has really reached its end. Going by this way, you can certainly differentiate EOF with junk value. Please go through the examples at the bottom of this answer and you may get some clarity.
ID DIVISION.
PROGRAM-ID. EOF1.
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT INDD ASSIGN TO INDD
FILE STATUS IS WS-ST.
DATA DIVISION.
FILE SECTION.
FD INDD.
01 NAME1 PIC X(10).
WORKING-STORAGE SECTION.
01 WS-ST PIC 9(2) VALUE 0.
01 WS-READ PIC 9(2) VALUE 0.
01 WS-SWITCHES.
05 INDD-END PIC X(1) VALUE 'N'.
88 INDD-EOF VALUE 'Y'.
PROCEDURE DIVISION.
OPEN INPUT INDD.
PERFORM 100-READ THRU 100-EXIT UNTIL INDD-EOF.
IF WS-ST EQUAL 10
READ INDD
DISPLAY 'READ FILE STATUS: ' WS-ST
IF WS-ST EQUAL 46
DISPLAY 'END OF FILE'
ELSE
DISPLAY 'RECORD' WS-READ ' IS ERRORED!'
MOVE 16 TO RETURN-CODE
CLOSE INDD
STOP RUN
END-IF
ELSE
ADD 1 TO WS-READ
END-IF.
CLOSE INDD.
STOP RUN.
100-READ.
READ INDD
AT END
SET INDD-EOF TO TRUE
GO TO 100-EXIT
END-READ.
ADD 1 TO WS-READ.
DISPLAY 'READ FILE STATUS: ' WS-ST.
DISPLAY 'RECORD' WS-READ ': ' NAME1.
IF NAME1 EQUAL 'SRINIVASAN'
MOVE 10 TO WS-ST
SET INDD-EOF TO TRUE
END-IF.
100-EXIT. EXIT.
Several samples of Input and output are shown below:
Example 1:
Input: 3rd record is errored.
***************************** Top of Data ******************************
BHUTAN
NEPAL
SRINIVASAN
**************************** Bottom of Data ****************************
Output: Program ended with RC 16. Program didn't read further.
********************************* TOP OF DATA **********************************
RECORD01: BHUTAN
RECORD02: NEPAL
RECORD03: SRINIVASAN
RECORD03 IS ERRORED!
******************************** BOTTOM OF DATA ********************************
Example 2:
Input: No error record this time.
***************************** Top of Data ******************************
BHUTAN
NEPAL
**************************** Bottom of Data ****************************
Output: Program ended normally. RC 00.
********************************* TOP OF DATA **********************************
RECORD01: BHUTAN
RECORD02: NEPAL
END OF FILE
******************************** BOTTOM OF DATA ********************************
Example 3:
Input: Error record SRINIVASAN is in between BHUTAN and NEPAL. Might be in the border between these 2 countries :) ?
***************************** Top of Data ******************************
BHUTAN
SRINIVASAN
NEPAL
**************************** Bottom of Data ****************************
Output: Program ends with RC 16. Program didn't read 3rd record as the 2nd one is errored.
********************************* TOP OF DATA **********************************
RECORD01: BHUTAN
RECORD02: SRINIVASAN
RECORD02 IS ERRORED!
******************************** BOTTOM OF DATA ********************************
Example 4:
Input: Empty Input file
Output: Program ends normally.
********************************* TOP OF DATA **********************************
END OF FILE
******************************** BOTTOM OF DATA ********************************
Hope this helps!
I have the following xml-structure that I want to parse in Cobol.
<LDO>
<OD>1</OD> //OD 1'st occurrence
<OLD>1</OLD> //OLD 1'st occurrence
<OLD>2</OLD> //OLD 2'nd occurrence
<OLD>3</OLD> //OLD 3'rd occurrence
<OD>2</OD> //OD 2'nd occurrence
<OLD>4</OLD> //OLD 4'th occurrence
</LDO>
As you guys can see there is several OLD tags after an OD tag. What I want to do is reading this xml file step by step and display it's attributes in the following way:
1
1
2
3
2
4
READ xml-stream.
START xml-stream KEY IS OD.
*>check status
START xml-stream KEY IS OLD.
*> check stream status
PERFORM UNTIL EXIT
READ xml-stream next key is
old
IF stream-status = -7
EXIT PERFORM
END-IF
*> check stream status less than 0
display od-value
display old-value
But the od-value doesn't change when i excecute the program. It return the following values
1
1
2
3
1
4
I want that the second occurrence to return the value of the second element OD not the first one.
I would like some help to achieve this.
You could use the "xml parse" syntax:
program-id. xp.
01 xdoc pic x(1024) value
" <LDO>" &
" <OD>1</OD>" &
" <OLD>1</OLD>" &
" <OLD>2</OLD>" &
" <OLD>3</OLD>" &
" <OD>2</OD>" &
" <OLD>4</OLD>" &
"</LDO>".
procedure division.
Xml parse xdoc processing procedure p
ON EXCEPTION
display 'XML document error 'XML-CODE
NOT ON EXCEPTION
display 'XML document successfully parsed'
END-XML
goback.
p.
Evaluate xml-event
When 'START-OF-ELEMENT'
When 'CONTENT-CHARACTERS'
exhibit named xml-text
When 'CONTENT-CHARACTER'
exhibit named xml-text
When 'END-OF-ELEMENT'
exhibit named xml-event
When other
exhibit named xml-event
End-evaluate
.
end program xp.
With this code, I get
16: Perform stmnt not terminated by end-perform
33: syntax error, unexpected end-perform
Why is it saying that I need an end-perform and also not need it?
identification division.
program-id. xxx.
* will accept and display a num until 0 is called then
* asks to go again
data division.
file section.
working-storage section.
01 num pic 9(4).
01 hold pic 9(4).
01 another pic x.
procedure division.
perform until another = 'N' (line 16)
Display "Another Session (Y/N)? "
with no advancing
if another = 'Y'
Display "Enter a 4-digit unsigned number (0 to stop): "
with no advancing
accept num
move num to hold
perform until num = 0
Display "Enter a 4-digit unsigned number (0 to stop): "
with no advancing
accept num
if num <> 0
move num to hold
end-perform.
display space
Display "The last number entered: "hold
End-perform. (Line 33)
stop run.
end-perform.
display space
Display "The last number entered: "hold
End-perform. (Line 33)
It's that full-stop/period (Line 30) which is the killer.
Although since the 1985 Standard COBOL is much more relaxed about full-stops/periods, a single one will bring all current scopes screaming to a halt. You could have nesting 50 levels deep, and one single full-stop/period would end them all, in one fell swoop.
My advice is to use the absolute minimum of full-stop/periods in the PROCEDURE DIVISION.
That is: one to terminate the PROCEDURE DIVISION header; one to terminate each paragraph/SECTION label; one to terminate a paragrpah/SECTION; one to terminate a program (for a program with no paragraphs/SECTIONS). Also, if you have PROCEDURE DIVISION COPY or REPLACE statements, you'll need full-stops/periods to terminate those.
Except for the termination of the labels I put each full-stop/period on a line of its own, never attached to any code. I can then move code around and insert code without worrying about whether I need to add/remove a full-stop/period.
As to why you need END-PERFORM, it is an "inline PERFORM". Syntactically, an inline PERFORM requires an END-PERFORM, but your use of the full-stop/period caused termination of the PERFORM scope before the END-PERFORM was located, so the error on line 16. Subsequently an END-PERFORM unconnected to a PERFORM was located, so the error on line 33.
It is important when putting error messages in your questions that you include the error message exactly as you see it. Copy/paste, don't re-trype, please. Include any message numbers, as well.
You absolutely can not mix the full stop "." scope terminator from Cobol-74 with the End-* scope terminators from Cobol-85.
The difference is that the full stop "." terminates ALL scopes.
The End-* terminates only the most recent scope, just like you might expect.
Putting a "." in the middle of code with End-* is kinda like dropping a nuclear bomb in the middle of it. As a rule, for compilers made in the last quarter century or so, a period should only occur in the procedure division at the end of a paragraph name, or at the end of a paragraph (and sections too, but those are useless in an age where segmentation and overlays are managed by the operating system). I like to use "EXIT." or "CONTINUE." just to highlight that I'm using one of the bad-nasty-best-avoided-periods in the procedure division.
I'm wondering if anybody can explain to me the dot ruling in nested IF statements in COBOL. Example:
*The first if statement*
IF SUCCESSFUL-STATUS
PERFORM 8300-REPL-LNNTBI00
THRU 8300-REPL-LNNTBI00-EXIT
*The second if statement*
IF SUCCESSFUL-STATUS
DISPLAY 'RECORD ALREADY UPDATED :' WS-INF-REC
ELSE
DISPLAY 'UPDATE ERROR : ' WS-INF-REC ' / '
WS-RETURN-STATUS
READ INFILE INTO WS-INF-REC.
Which if statement does the dot placed after "WS-INF-REC" belong to? The first IF or the second IF-ELSE? I know that in most programming, it should be for the last if statement but just to make sure, is it the same for COBOL?
AFAIR a period always closes ALL preceding statements - regardless wether they are IF, PERFORM or whatever - so in your case the first IF-statement is closed as well. And since periods are so small and easily overlooked I use the following rule:
Avoid using periods, they are evil!
Only put a period where it is strictly required by the syntax rules and nowhere else. Use explicit scope-terminators like END-IF or END-PERFORM. They make your code more readable and clearly structured while periods tend to generate confusion because of multiple closures and their habit of hiding in plain view.
The period character "." ends all if statements. Remember that spacing is ignored by the compiler so therefore the READ statement is part of the ELSE of the second IF statement.
Us humans want to see the indentation used logically. And, if it were me, I would make the end-if's be explicit. I tend to have one period per paragraph:
* The first if statement *
IF SUCCESSFUL-STATUS
PERFORM 8300-REPL-LNNTBI00
THRU 8300-REPL-LNNTBI00-EXIT
* The second if statement*
IF SUCCESSFUL-STATUS
DISPLAY 'RECORD ALREADY UPDATED :' WS-INF-REC
ELSE
DISPLAY 'UPDATE ERROR : ' WS-INF-REC ' / '
WS-RETURN-STATUS
READ INFILE INTO WS-INF-REC
END-IF
END-IF
.
This is really bad, very archaic Cobol, but how it behaves is like this:
*The first if statement*
IF SUCCESSFUL-STATUS
PERFORM 8300-REPL-LNNTBI00
THRU 8300-REPL-LNNTBI00-EXIT
*The second if statement*
IF SUCCESSFUL-STATUS
DISPLAY 'RECORD ALREADY UPDATED :' WS-INF-REC
ELSE
DISPLAY 'UPDATE ERROR : ' WS-INF-REC ' / ' WS-RETURN-STATUS
READ INFILE INTO WS-INF-REC
END-IF ## from period
END-IF ## from period
We are supposed to form an array of names that occur 108 times. We are supposed to have name 1-54 in a left column and names 55-108 in a right column. After there have been 108 names for one page, we initialize our array and start over again. The output for my code is showing names 1-54 printed and, instead of being on the same page and beside names 1-54, names 55-108 in the right column but after names 1-54. Any thoughts would be greatly appreciated.
Here is some of my code:
PERFORM UNTIL ARE-THERE-MORE-RECORDS = 'NO '
READ NAMELIST-FILE-IN
AT END
MOVE 'NO ' TO ARE-THERE-MORE-RECORDS
NOT AT END
PERFORM 200-PROCESS-ONE-RECORD
END-READ
END-PERFORM
CLOSE NAMELIST-FILE-IN
CLOSE NAMELIST-FILE-OUT
STOP RUN.
200-PROCESS-ONE-RECORD.
ADD 1 TO NAME-SUB
MOVE NAME-IN TO NAME-1 (NAME-SUB)
PERFORM 220-MOVE-RECORDS.
220-MOVE-RECORDS.
IF NAME-SUB <= 54
MOVE NAME-1 (NAME-SUB) TO LEFT-LABEL
MOVE SPACES TO RIGHT-LABEL
END-IF
IF NAME-SUB >= 55
MOVE NAME-1 (NAME-SUB) TO RIGHT-LABEL
MOVE SPACES TO LEFT-LABEL
END-IF
MOVE DETAIL-LINE TO NAMELIST-RECORD-OUT
WRITE NAMELIST-RECORD-OUT
AFTER ADVANCING 1 LINE
IF NAME-SUB >= 108
MOVE SPACES TO DETAIL-LINE
MOVE ZERO TO NAME-SUB
PERFORM 300-WRITE-HEADING
END-IF.
I have coded all the proper WORKING-STORAGE entries to accommodate the information. Do you know if there is something wrong with the way I am writing the detail-line or is it the way I am processing my data?
Your logic is wrong. Lets say (just to make things easy) you have 216 names, you will need to read in 108 of them and store them in your NAME-1 array.
Then you can loop over the 54 lines placing NAME-1[n] into LEFT-LABEL and NAME-1[n+54] into RIGHT-LABEL, Then move your detail-line and write to output; repeating for lines n = 1 <= 54
Now read in your next 108 lines and repeat. So two loops; Read 108 names, print 54 lines.
Obviously you will need to guard for your remainder, ie if you don't have exactly a multiple of 108 names, something like
if n <= name-sub
move NAME-1[n] to LEFT-LABEL
else
move spaces to LEFT-LABEL
endif
if n+54 <= name-sub
move NAME-1[n+54] to RIGHT-LABEL
else
move spaces to RIGHT-LABEL
endif
I realise you will have to set the variables properly (n+54 is not proper cobol) and sorry for the mix of case, but long time ago writing COBOL and used to lower case now. ;)
If I understand correctly, this should be close to what you want
220-MOVE-RECORDS.
IF NAME-SUB >= 108
perform varing i from 1 to 54
MOVE NAME-1 (NAME-SUB) TO LEFT-LABEL
compute ip54 = i + 54
MOVE NAME-1 (ip54) TO RIGHT-LABEL
WRITE NAMELIST-RECORD-OUT
from DETAIL-LINE
AFTER ADVANCING 1 LINE
end-perform
MOVE SPACES TO DETAIL-LINE
MOVE ZERO TO NAME-SUB
PERFORM 300-WRITE-HEADING
END-IF.
Note: many Cobol compilers allow lower case
You should always have error checking for all your IO.
A one-file-in-one-file-out can always look like this:
open input
check status
open output
check status
process file until end
close input
check status
close output
check status
process file
read input
check staus
do what is needed
write output
check status
Better is like this:
open input
check status
open output
check status
*priming read*
process file until end
close input
check status
close output
check status
process file
do what is needed
write output
check status
read input
check staus
The "priming read" deals with the first record on the file (if any). You can neatly handle an "empty file" without having to "confuse" your main logic or having to differentiate between two different types of "end of file" elsewhere. The read now at the end of "process file" removes the somewhat tortuous "AT END/NOT AT END".
For the example, you only need 54 elements in your table. When processing a record for the "right" side of the page, you can take the first from the "left" and do the line immediately.
Use 88s rather than literals for tests.
Don't do your "headings" at the end of a page, as if there are no more records to process, you will have a "blank page" following your headings.
If the write of your print line is in a paragraph, that paragraph can be used check whether a heading is needed, with a "line count" which has an initial value of 54.
With the 108-element approach where you are printing a page-at-a-time, do the headings there, at the top.
The is no need to set things to initial values if the data is never used before it is set to something else.
You've adopted the "minimal full-stop/period" approach to procedure code, which is good - how about putting that necessary final period on a line of its own?
PERFORM 220-MOVE-RECORDS.
becomes
PERFORM 220-MOVE-RECORDS
.
Only use >= or<= when the values can logically exceed the maximum. Yours never can, so use EQUAL TO. Yes, if it exceeds, you get a Big Fat Loop. But that is better than "working" when something unexpected has happened. If you want to test > for exceeding and then failing with a diagnostic message, that's OK. Some compilers allow "bounds checking" of table accesses, if you are using that, you'd not even need the extra check.
It would have been helpful to see your Working Storage definitions as well as the code. It is hard to
understand one without the other.
At any rate, what you are describing is a fairly "standard" sort of problem to which there are
several possible solutions. What follows is an
outline of one possible approach.
Start with a data structure... Working Storage:
01 WS-PAGE-BUFFER.
02 WS-LINE OCCURS 54 TIMES.
03 WS-NAME PIC X(40) OCCURS 2 TIMES.
The above working storage describes one page of output. The page contains 54 lines. Each line contains
two names. Next you need a few counters...
01.
02 WS-LINE-CNTR PIC S9(4) COMP.
02 WS-NAME-CNTR PIC S9(4) COMP.
Two problems to solve:
Filling the page in the proper sequence
Printing the page with appropriate headings/trailers
Something else to keep in mind when solving these problems is that you need to cover
several scenaios with respect to inputs: No input, input fits exactly to some number
of ouput pages and input partly fills an output page. So whatever you do, all of
these situations need to sort themselves out in a "natural" way.
Also, there is generally some sort of pre/post amble stuff
to work out (eg. initializations, open files, close files etc.).
One more thing... Always declare a FILE-STATUS for your Input/Output files to
capture errors and end-of-file conditions. The algorithm below assumes you have
done that (end-of-file status is generally '10')
Skeleton algorithm.
MAINLINE
PERFORM INITIALIZE-PAGE
Open input file (check status etc...)
Open output file (check status etc...)
Read first line from file (check for errors/end of file etc...)
PERFORM UNTIL INPUT-FILE-STATUS NOT = ZERO /* read until eof/error
IF WS-LINE-CNTR = 54 AND WS-NAME-CNT = 2 /* check for full page.
PERFORM OUTPUT-PAGE
END-IF
ADD +1 TO WS-LINE-CNTR
IF WS-LINE-CNTR > 54
MOVE +1 TO WS-LINE-CNTR /* Start next column...
ADD +1 TO WS-NAME-CNTR /* Increment column
END-IF
MOVE input-record TO WS-NAME (WS-LINE-CNTR, WS-NAME-CNTR)
Read next line from input file
END-PERFORM
IF INPUT-FILE-STATUS = '10' AND WS-LINE-CNTR > ZERO
PERFORM OUTPUT-PAGE /* force the last page to print
END-IF
close input file
close output file
GOBACK /* done
.
INITIALIZE-PAGE.
MOVE SPACE TO WS-PAGE-BUFFER /* Blank page (ie. SPACES)
MOVE ZERO TO WS-LINE-CNTR /* Top of page
MOVE +1 TO WS-NAME-CNTR /* First column of page
.
OUTPUT-PAGE.
Ouput page headers...
PERFORM VARYING WS-LINE-CNTR FROM 1 BY 1
UNTIL WS-LINE-CNTR > 54
write WS-LINE (WS-LINE-CNTR) to output file (check status etc...)
END-PERORM
Output page trailers...
PERFORM INITIALIZE-PAGE /* Start a fresh page...
.
I have left plenty of "blank spots" to be filled in and I will admit there are other more
elegant ways to accomplish what you are trying to do, but
this should get you started.