WRITE AFTER ADVANCING followed by WRITE causes 2 lines to appear in the first line - Why? - cobol

I got this result from the code below using a COBOL PC Compiler on Windows.
REC 1
FFREC 2-AFTER PAGE
REC 3 AFTER 3REC 4
I can understand the FF at the beginning of the first line.
However, the last output line puts REC 4 at the SAME LINE as record 3!
My question is, does MICRO FOCUS COBOL or IBM ENTERPRISE COBOL or Fujitsu COBOL produce the same result? If yes, how to work around this to have a result file one can view on Windows?
Thanks.
EDIT-1:
I discovered that if I write:
MOVE "REC 4" TO FD-PRINT-REC
WRITE FD-PRINT-REC AFTER 1
Then the text "REC 4" appears on a separate line.
I assume that this is not the ANSI Standard behavior, I hope you could verify what your compiler does.
EDIT-2:
Another option proposed at this thread, the "ASSIGN TO LINE ADVANCING" is probably not something that would work on ENTERPRISE COBOL (not sure):
GNU COBOL Forum-Related Thread
The code:
IDENTIFICATION DIVISION.
PROGRAM-ID. CALLER.
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT PRINT-FILE ASSIGN TO "X.TXT"
ORGANIZATION IS LINE SEQUENTIAL.
*> --------------------------------------------------------------
DATA DIVISION.
FILE SECTION.
FD PRINT-FILE.
01 FD-PRINT-REC PIC X(132).
*>---------------------------------------------------------------
WORKING-STORAGE SECTION.
PROCEDURE DIVISION.
OPEN OUTPUT PRINT-FILE
MOVE "REC 1" TO FD-PRINT-REC
WRITE FD-PRINT-REC
MOVE "REC 2-AFTER PAGE" TO FD-PRINT-REC
WRITE FD-PRINT-REC AFTER PAGE
MOVE "REC 3 AFTER 3" TO FD-PRINT-REC
WRITE FD-PRINT-REC AFTER 3
MOVE "REC 4" TO FD-PRINT-REC
WRITE FD-PRINT-REC
CLOSE PRINT-FILE.
STOP RUN
.

If none of the files has a LINAGE clause, then if any of the programs use the WRITE statement with the ADVANCING phrase, all of the programs in the run unit that have a WRITE statement must use the WRITE statement with the ADVANCING phrase. IBM WRITE statement
IDENTIFICATION DIVISION.
PROGRAM-ID. CALLER.
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT PRINT-FILE ASSIGN TO "X.TXT"
ORGANIZATION IS LINE SEQUENTIAL.
*> --------------------------------------------------------------
DATA DIVISION.
FILE SECTION.
FD PRINT-FILE.
01 FD-PRINT-REC PIC X(132).
*>---------------------------------------------------------------
WORKING-STORAGE SECTION.
PROCEDURE DIVISION.
OPEN OUTPUT PRINT-FILE
MOVE "REC 1" TO FD-PRINT-REC
WRITE FD-PRINT-REC END-WRITE
MOVE "REC 2-AFTER PAGE" TO FD-PRINT-REC
WRITE FD-PRINT-REC AFTER PAGE END-WRITE
MOVE "REC 3 AFTER 3" TO FD-PRINT-REC
WRITE FD-PRINT-REC AFTER 3 END-WRITE
MOVE "REC 4" TO FD-PRINT-REC
WRITE FD-PRINT-REC END-WRITE
MOVE "REC 5" TO FD-PRINT-REC
WRITE FD-PRINT-REC AFTER 3 END-WRITE
MOVE "REC 6" TO FD-PRINT-REC
WRITE FD-PRINT-REC END-WRITE
MOVE "REC 7" TO FD-PRINT-REC
WRITE FD-PRINT-REC END-WRITE
CLOSE PRINT-FILE.
STOP RUN.
Result:
$ cat X.TXT
REC 1
REC 2-AFTER PAGE
REC 3 AFTER 3REC 4
REC 5REC 6
REC 7

Related

How to display a scrollable list using Cobol Screens

I am creating a COBOL application using the screen section (Library where you can lend books).
How I can create a scrollable list using Screen Section? Can it be implemented so that you can scroll through that list using certain keys?
In my application I have screens where lists are displayed but there I am displaying a limited number of items and then a key has to be pressed to see further items.
But now I have a more complex situation.
This is the code I would like to "convert" into the screen section mode:
IDENTIFICATION DIVISION.
PROGRAM-ID. RETBOOK.
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
COPY "SLCUST.cbl".
COPY "SLBOOK.cbl".
COPY "SLLOAN.cbl".
DATA DIVISION.
FILE SECTION.
COPY "FDCUST.cbl".
COPY "FDBOOK.cbl".
COPY "FDLOAN.cbl".
WORKING-STORAGE SECTION.
COPY "WSALPHA".
77 A-PAUSE PIC X.
77 LOAN-FILE-AT-END PIC X.
77 THE-CUSTOMER-ID PIC 9(5).
77 THE-CUSTOMER-LAST-NAME PIC X(20).
77 THE-CUSTOMER-FIRST-NAME PIC X(20).
77 THE-BOOK-TO-RETURN-ID PIC 9(5).
77 LOAN-FOUND PIC X.
PROCEDURE DIVISION.
PROGRAM-BEGIN.
PERFORM DO-RETURN.
PROGRAM-EXIT.
EXIT PROGRAM.
PROGRAM-DONE.
STOP RUN.
OPEN-FILES.
OPEN I-O CUSTOMER-FILE.
OPEN I-O BOOK-FILE.
OPEN I-O LOAN-FILE.
CLOSE-FILES.
CLOSE CUSTOMER-FILE.
CLOSE BOOK-FILE.
CLOSE LOAN-FILE.
DO-RETURN.
PERFORM OPEN-FILES.
PERFORM FIND-CUSTOMER.
PERFORM DISPLAY-LOANED-BOOK-IDS.
IF LOAN-FOUND = "Y"
PERFORM RETURN-BOOK
ELSE
DISPLAY "No loans available".
PERFORM CLOSE-FILES.
FIND-CUSTOMER.
PERFORM ENTER-CUSTOMER-ID.
PERFORM GET-CUSTOMER-BY-ID.
ENTER-CUSTOMER-ID.
DISPLAY "Enter id of customer to find.".
ACCEPT THE-CUSTOMER-ID.
GET-CUSTOMER-BY-ID.
CALL "CUSTBYID" USING THE-CUSTOMER-ID.
DISPLAY "Press any key to continue.".
ACCEPT A-PAUSE.
DISPLAY-LOANED-BOOK-IDS.
PERFORM FIND-LENT-BOOKS-OF-CUSTOMER.
FIND-LENT-BOOKS-OF-CUSTOMER.
PERFORM READ-CUSTOMER-DATA.
PERFORM DISPLAY-CUSTOMER-DATA.
PERFORM INIT-DATA.
MOVE CUSTOMER-ID TO LOAN-CUSTOMER.
PERFORM READ-LOAN-DATA.
INIT-DATA.
MOVE SPACES TO LOAN-RECORD.
READ-CUSTOMER-DATA.
MOVE THE-CUSTOMER-ID TO CUSTOMER-ID.
READ CUSTOMER-FILE RECORD
INVALID KEY
DISPLAY "CUSTOMER NOT FOUND".
READ-LOAN-DATA.
MOVE "Y" TO LOAN-FOUND
START LOAN-FILE
KEY = LOAN-CUSTOMER
INVALID KEY
DISPLAY "No lent books."
MOVE "N" TO LOAN-FOUND
MOVE "Y" TO LOAN-FILE-AT-END
END-START.
PERFORM READ-NEXT-LOAN-DATA
UNTIL LOAN-FILE-AT-END = "Y".
READ-NEXT-LOAN-DATA.
IF LOAN-CUSTOMER = CUSTOMER-ID
READ LOAN-FILE NEXT RECORD
AT END
MOVE "Y" TO LOAN-FILE-AT-END
PERFORM DISPLAY-LOAN-DATA
ELSE
MOVE "Y" TO LOAN-FILE-AT-END.
DISPLAY-CUSTOMER-DATA.
DISPLAY "Lent books of".
DISPLAY CUSTOMER-FIRST-NAME.
DISPLAY CUSTOMER-LAST-NAME.
DISPLAY-LOAN-DATA.
DISPLAY "Loan id : " LOAN-ID.
DISPLAY "Loan Book : " LOAN-BOOK.
RETURN-BOOK.
CALL "LOANDEL".
Code of "LOANDEL"
IDENTIFICATION DIVISION.
PROGRAM-ID. LOANDEL.
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
COPY "SLBOOK.cbl".
COPY "SLLOAN.cbl".
DATA DIVISION.
FILE SECTION.
COPY "FDBOOK.cbl".
COPY "FDLOAN.cbl".
WORKING-STORAGE SECTION.
77 THE-BOOK-ID PIC 9(5).
77 LOAN-FOUND PIC X.
77 BOOK-FOUND PIC X.
77 LOAN-KEY PIC 9(5).
PROCEDURE DIVISION.
PROGRAM-BEGIN.
OPEN I-O LOAN-FILE.
OPEN I-O BOOK-FILE.
PERFORM MAIN-PROCESS.
CLOSE LOAN-FILE.
CLOSE BOOK-FILE.
PROGRAM-EXIT.
EXIT PROGRAM.
PROGRAM-DONE.
STOP RUN.
MAIN-PROCESS.
PERFORM ENTER-BOOK-ID-TO-RETURN.
PERFORM DISPLAY-BOOK.
PERFORM FIND-LOAN.
IF LOAN-FOUND = "Y"
PERFORM DISPLAY-LOAN
PERFORM DELETE-LOAN
PERFORM MAKE-BOOK-AVAILABLE
ELSE
DISPLAY "Loan not found".
ENTER-BOOK-ID-TO-RETURN.
DISPLAY "Enter book id to return".
ACCEPT THE-BOOK-ID.
MOVE THE-BOOK-ID TO LOAN-BOOK.
MOVE THE-BOOK-ID TO BOOK-ID.
DISPLAY-BOOK.
CALL "BOOKBYID" USING THE-BOOK-ID.
FIND-LOAN.
MOVE "Y" TO LOAN-FOUND.
START LOAN-FILE
KEY = LOAN-BOOK
INVALID KEY
MOVE "N" TO LOAN-FOUND
DISPLAY "This book is not lent by the customer".
READ LOAN-FILE NEXT RECORD.
DELETE-LOAN.
DELETE LOAN-FILE RECORD
INVALID KEY
DISPLAY "Error deleting loan".
DISPLAY-LOAN.
DISPLAY "Loan to delete:".
DISPLAY "Loan id : " LOAN-ID.
DISPLAY "Loan customer : " LOAN-CUSTOMER.
DISPLAY "Loan book : " LOAN-BOOK.
DISPLAY "Loan date : " LOAN-DATE.
DISPLAY "Loan return date : " LOAN-RETURN-DATE.
MAKE-BOOK-AVAILABLE.
PERFORM FIND-BOOK.
MOVE "N" TO BOOK-LENT.
MOVE "N" TO BOOK-OVERDUE.
PERFORM SAVE-CHANGES.
FIND-BOOK.
MOVE "Y" TO BOOK-FOUND.
READ BOOK-FILE RECORD
INVALID KEY
MOVE "N" TO BOOK-FOUND.
SAVE-CHANGES.
REWRITE BOOK-RECORD
INVALID KEY
DISPLAY "Error rewriting book record".
Code of "BOOKBYID"
IDENTIFICATION DIVISION.
PROGRAM-ID. BOOKBYID.
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
COPY "SLBOOK.cbl".
DATA DIVISION.
FILE SECTION.
COPY "FDBOOK.cbl".
WORKING-STORAGE SECTION.
77 BOOK-FOUND PIC X.
LINKAGE SECTION.
77 THE-BOOK-ID PIC 9(5).
PROCEDURE DIVISION
USING THE-BOOK-ID.
PROGRAM-BEGIN.
PERFORM OPEN-FILE.
PERFORM GET-BOOK-BY-ID.
PERFORM CLOSE-FILE.
PROGRAM-EXIT.
EXIT PROGRAM.
PROGRAM-DONE.
STOP RUN.
OPEN-FILE.
OPEN I-O BOOK-FILE.
CLOSE-FILE.
CLOSE BOOK-FILE.
GET-BOOK-BY-ID.
MOVE THE-BOOK-ID TO BOOK-ID.
PERFORM READ-BOOK.
IF BOOK-FOUND = "Y"
PERFORM DISPLAY-BOOK
ELSE
DISPLAY "Book not found".
READ-BOOK.
MOVE "Y" TO BOOK-FOUND.
READ BOOK-FILE RECORD
INVALID KEY
MOVE "N" TO BOOK-FOUND.
DISPLAY-BOOK.
DISPLAY "Title : " BOOK-TITLE.
DISPLAY "Author : " BOOK-AUTHOR.
I am using SCREEN SECTION in my other programs and DISPLAY/ACCEPT and DISPLAY BLANK SCREEN (wrapped in a paragraph) to clear the screen. Navigation using arrows, TAB and ENTER is not sufficient in this situation because I also would like to navigate through the scrollable list using other keys for example F3/F4.How can I achieve this here ?
Answer to the question "How to create a scrollable list of records in SCREEEN SECTION":
The most portable way is to define a CRT STATUS (goes to SPECIAL NAMES paragraph) which can be queried after each ACCEPT. The actual values you receive differ between implementations with the biggest difference you'll see is between X/Open COBOL definition (3-byte status, partial binary) and COBOL 2002+ (4byte status), some COBOL implementations may even accept and handle both (like GnuCOBOL 3.2+ ;-).
Implementations commonly ship a copybook with values you can test against, as there was no specification for the COBOL environment used: GnuCOBOL as an example would have copy screenio. with 4-byte definitions as constants (level 78 constants, not all COBOL implementations have those, the standard would use 01 ... CONSTANT) and an implied 4-byte status, allowing checks like IF COB-CRT-STATUS = COB-SCR-PAGE-DOWN and similar.
If you specify the CRT STATUS clause then query that, if it is a 3 byte one then you'd normally check the different parts.

COBOL: How to differentiate between end of file and junk value when both have return code 10?

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!

Opening file for reading in COBOL

I'm using OpenCobolIDE 4.7.4 (it's based on GnuCOBOL) on Windows 10 and trying to compile this program opening a file for reading:
IDENTIFICATION DIVISION.
PROGRAM-ID. HELLO.
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT STUDENT ASSIGN TO 'input.txt'
ORGANIZATION IS LINE SEQUENTIAL.
DATA DIVISION.
FILE SECTION.
FD STUDENT.
01 STUDENT-FILE.
05 STUDENT-ID PIC 9(5).
05 NAME PIC A(25).
WORKING-STORAGE SECTION.
01 WS-STUDENT.
05 WS-STUDENT-ID PIC 9(5).
05 WS-NAME PIC A(25).
01 WS-EOF PIC A(1).
PROCEDURE DIVISION.
OPEN INPUT STUDENT.
PERFORM UNTIL WS-EOF='Y'
READ STUDENT INTO WS-STUDENT
AT END MOVE 'Y' TO WS-EOF
NOT AT END DISPLAY WS-STUDENT
END-READ
END-PERFORM.
CLOSE STUDENT.
STOP RUN.
The input.txt is in the same directory as the source coude, yet I'm still getting the following error:
Main.cob:24: libcob: File does not exist (STATUS = 35) File : 'input.txt'
What am I doing wrong?
OCIDE has a setting for the output directory, the default is "bin" (relative to the source file). Effectively it just passes this setting to the compiler cobc source.cob -o bin\source.exe
You can change this behaviour in settings Menu Preferences -> Compiler:
Output directory
This option let you chose where to put the binaries, by default binaries will be placed into a bin folder next to the source file. You can define another relative or absolute directory if you want.
In any case you can set the actual name in the environment, check GC FAQ - How to map a file name to an external name.
As an alternative you can set the data directory with the environment var COB_FILE_PATH.
Both environment options can be set in settings Menu Preferences -> Run.
Most IDE for other languages happen to run the executable from another directory (where it is built for example).
A simple test is to write a test program, opening a file for writing.
You'll quickly see what happens.
Perhaps better will be to write the full path in the select clause.
select STUDENT ASSIGN TO '/xpto/folder1/input.txt'

Is there a way to automatically fill in array after getting user input?

I have an array of 5 elements and each of the elements holds a character. I want to accept user input in one line. For example: ABCDE. And I intend element 1 of the array to have A and element 2 of the array to have B and so on. Could someone help with this? I have attached the relevant portions of the code below:
environment division.
input-output section.
file-control.
select standard-input assign to keyboard.
select standard-output assign to display.
data division.
file section.
fd standard-input.
01 stdin-record pic x(80).
fd standard-output.
01 stdout-record pic x(80).
working-storage section.
01 input-area.
02 inputCharacters pic x(1) occurs 5 times.
01 print-line.
02 inputCharacters pic x(1) occurs 5 times.
procedure division.
open input standard-input, output standard-output.
read standard-input into input-area
at end
close standard-input, standard-output
end-read.
write
stdout-record from print-line after advancing 5 line
end-write
stop run.
MOVE input-area TO print-line
For the code you have, you could also do this:
write
stdout-record from input-area after advancing 5 line
end-write
If you don't need two copies of a table (COBOL doesn't really have "arrays" in the way you're probably used to) then don't have two copies.
If you do have two tables, I'd suggest making the item names different. If you don't, you're making things tougher by having to use "qualification" to make the references unique.
MOVE inputCharacters ( 1 ) OF input-area
TO inputCharacters ( 1 ) OF print-line
Instead of
MOVE inputCharacters ( 1 )
TO outputCharacters ( 1 )
If you don't mind qualification yourself, you may find that colleagues or future maintainers hate it.
I'm not quite sure what you want to happen with this:
read standard-input into input-area
at end
close standard-input, standard-output
end-read.
You only do one read, you you'll only get at end when there is no data (whatever that means with keyboard). In that case, you don't have data to do anything with.
You should look at how to use FILE STATUS for each file. Check the file-status field after each IO, and I'd also recommend using the file-status field for end-of-file checking, rather than the cumbersome AT END.
However, as I said, I don't know what that means with keyboard... so perhaps that won't work :-)

Is there a way to parameterize functions in COBOL?

I'm coding routines like:
READ-A.
READ FILE-A
AT END
MOVE 1 TO EOF-A
NOT AT END
ADD 1 TO CN-READ-A
END-READ.
F-READ-A. EXIT.
to read several files and I was wondering if there's a way to code a routine that is able to read the filename from a variable so I don't have to code the same thing for each file. Thanks!
One solution as said above is to use multiple programs or nested program, for which
I have included an example below, which is solution 1.
Another solution is to COBOL classes, which might not be to your liking but I like them, so I've included an example, which is solution 2.
Solution 1:
program-id. TestProgram.
working-storage section.
01 file-name pic x(128).
01 file-lines pic 9(9).
procedure division.
move 0 to file-lines
move "d:\rts_win32.txt" to file-name
call "program1" using file-name file-lines
display file-lines
stop run
end program TestProgram.
program-id. Program1.
file-control.
select file-a assign to myfile
organization is line sequential.
data division.
fd file-a.
01 file-a-line pic x(80).
working-storage section.
01 EOF-A pic 9 value 0.
linkage section.
01 lk-filename pic x(128).
01 CN-READ-A pic 9(9).
procedure division using lk-filename
CN-READ-A.
move lk-filename to myfile
open input file-a
perform READ-A until EOF-A equals 1
close file-a
goback.
READ-A.
READ FILE-A
AT END
MOVE 1 TO EOF-A
NOT AT END
ADD 1 TO CN-READ-A
END-READ.
F-READ-A.
EXIT.
end program Program1.
Solution 2
program-id. TestProgram.:
working-storage section.
01 file-counter type FileLineCounter.
procedure division.
set file-counter to new type FileLineCounter("d:\rts_win32.txt")
display file-counter::LineCount
stop run
end program TestProgram.
class-id FileLineCounter.
file-control.
select file-a assign to myfile
organization is line sequential.
data division.
fd file-a.
01 file-a-line pic x(80).
working-storage section.
01 cn-read-a binary-long property as "LineCount".
method-id New.
01 EOF-A pic 9 value 0.
procedure division using by value filename as string.
set myfile to filename
open input file-a
perform READ-A until EOF-A equals 1
close file-a
goback.
READ-A.
READ FILE-A
AT END
MOVE 1 TO EOF-A
NOT AT END
ADD 1 TO CN-READ-A
END-READ.
F-READ-A.
EXIT.
end method.
end class.
May not be "in the wild" yet with compiler support, but the current ISO Draft 20xx standard includes FUNCTION-ID in place of PROGRAM-ID. It adds a parameter friendly function call computing paradigm to COBOL.
Might not help today, but maybe in the near future. If I'm not mistaken, User Defined Functions are actually from the COBOL 2002 spec, but it seems compiler vendors are hit or miss on support for the feature.
FUNCTION-ID support is in closed trials for OpenCOBOL 2.0, but the timeline for the 2.0 release is undetermined and could be another year or more before it's made public.
The proper Cobol way to parameterize routines is via the nested subprogram.
You can do what you want, but it is dependant upon your compiler and environment, you can pass a file, or a file name, or a DDname.
What platform are you on?
Edit: On z/OS, you can change what FILE-A points to at runtime using putenv() to adjust the dataset name associated with the DDNAME that FILE-A uses.
See:
http://ibmmainframes.com/post-57281.html
http://cicswiki.org/cicswiki1/index.php?title=How_do_I_allocate_a_file_dynamically_using_COBOL%3F
You will need a OPEN-A and CLOSE-A paragraph as well between switching files.
It isn't exactly passing parameters to your read statement, but it lets you reuse your OPEN/READ/WRITE/CLOSE statements for different files. But only serially.
There was a way, under VS COBOL II, where you could pass an FD to a subprogram, that would look something like:
CALL MYREADPGM USING FILE-A
CALL MYREADPGM USING FILE-B
This possible with Enterprise Cobol but IIRC VisualAge does not support that.
I realize this is an old thread, but hopefully someone might find this useful in the future: IBM's Enterprise COBOL on z/OS 6.4 compiler supports user-defined functions (released May 2022). User-defined functions could be a useful alternative to the other suggestion for internal programs. In contrast to program calls, there are compile time checks for parameters to user-defined function invocations. Also, you can invoke the function in a lot of places where you couldn't call a program, like within a
n expression.
Here's an example based on passing a file name to a function. It might be possible to combine this with the PUTENV() suggestion above.
IDENTIFICATION DIVISION.
FUNCTION-ID. READ-FILE.
DATA DIVISION.
LINKAGE SECTION.
1 FILE-NAME PIC X(50).
1 RET PIC 9(9).
PROCEDURE DIVISION USING FILE-NAME RETURNING RET.
* DO STUFF WITH FILE-NAME
* ...
GOBACK
.
END FUNCTION READ-FILE.
IDENTIFICATION DIVISION.
PROGRAM-ID. MAINPROG.
DATA DIVISION.
WORKING-STORAGE SECTION.
1 READ-RESULT PIC 9(9).
PROCEDURE DIVISION.
COMPUTE READ-RESULT = FUNCTION READ-FILE('MYINPUTFILE')
GOBACK
.
END PROGRAM MAINPROG.
More examples can be found in the Programming Guide Chapter 32 Using user-defined functions.
https://www.ibm.com/support/pages/enterprise-cobol-zos-documentation-library#Table642
You could create a data file of filenames, treat each one as an individual record, and then read each file. In the "SELECT ...ASSIGN" you would need to use a working-storage variable for the filename and move the value from the 'file of filenames' into it.
As you are using VisualAge, I assume in UNIX, you might also be able to run the program from the shell (sh,ksh), with the filename as a parameter, and repeatedly run the program from the shell for each file name.

Resources