I'm reading a file into a table, note the first line is not part of the table.
1000
MS 1 - Join Grps Group Project 5 5
Four Programs Programming 15 9
Quiz 1 Quizzes 10 7
FORTRAN Programming 25 18
Quiz 2 Quizzes 10 9
HW 1 - Looplang Homework 20 15
In the code, the table is represented as follows:
01 GRADES.
05 GRADE OCCURS 1 TO 100 TIMES DEPENDING ON RECORD-COUNT INDEXED BY J.
10 ASSIGNMENT-NAME PIC X(20).
10 CATEGORY PIC X(20).
10 POINTS-POSSIBLE PIC 9(14).
10 POINTS-EARNED PIC 9(14).
I have a few other accumulator variables designated for calculating sums/percentages later on.
01 RECORD-COUNT PIC 9(8) VALUE 0.
01 TOTAL-EARNED-POINTS PIC 9(14).
01 TOTAL-POSSIBLE-POINTS PIC 9(14) VALUE 0.
My issue is, while I'm reading the records, line by line, I want to do the following:
ADD POINTS-EARNED(RECORD-COUNT) TO TOTAL-EARNED-POINTS
Where RECORD-COUNT is the current position in the iteration.
I expect the value of TOTAL-EARNED-POINTS after the first iteration to simply be 5, right?
However, when I DISPLAY the value of TOTAL-EARNED-POINTS, the console reads:
50000000000000
Is this 50 trillion? Or is it a funny looking representation of the number 5?
How can I write this so that I can do proper mathematics with it to print a proper grade report?
EDIT: I know it's likely that there's better ways of writing this program but I've never used cobol before attempting to write this program, and I probably won't use it ever again, or at least for a very long time. This is for a class, so as long as I can print my output properly, I'm good.
Full code, so far:
IDENTIFICATION DIVISION.
PROGRAM-ID. GRADEREPORT.
AUTHOR. JORDAN RENAUD.
DATE-WRITTEN. 09/18/2020.
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT GRADES-FILE ASSIGN TO "bill"
ORGANIZATION IS LINE SEQUENTIAL
ACCESS IS SEQUENTIAL.
DATA DIVISION.
FILE SECTION.
FD GRADES-FILE.
01 INPUT-TOTAL-POINTS PIC 9(4).
01 INPUT-GRADES.
05 INPUT-GRADE OCCURS 1 to 100 TIMES DEPENDING ON RECORD-COUNT INDEXED BY I.
10 INPUT-ASSIGNMENT-NAME PIC X(20).
10 INPUT-CATEGORY PIC X(20).
10 INPUT-POINTS-POSSIBLE PIC 9(14).
10 INPUT-POINTS-EARNED PIC 9(14).
WORKING-STORAGE SECTION.
77 GRADES-FILE-EOF PIC 9.
01 RECORD-COUNT PIC 9(8) VALUE 0.
01 TOTAL-EARNED-POINTS PIC 9(4) VALUE ZERO.
01 TOTAL-POSSIBLE-POINTS PIC 9(14) VALUE 5.
01 K PIC 9(14) VALUE 1.
01 TMP PIC 9(14).
01 CURRENT-CATEGORY PIC X(20).
01 CATEGORY-WEIGHT PIC X(3).
01 LAST-CATEGORY PIC X(20).
01 TOTAL-POINTS PIC 9(4).
01 GRADES.
05 GRADE OCCURS 1 TO 100 TIMES DEPENDING ON RECORD-COUNT INDEXED BY J.
10 ASSIGNMENT-NAME PIC X(20).
10 CATEGORY PIC X(20).
10 POINTS-POSSIBLE PIC 9(14).
10 POINTS-EARNED PIC 9(14).
PROCEDURE DIVISION.
OPEN INPUT GRADES-FILE.
READ GRADES-FILE INTO TOTAL-POINTS.
DISPLAY TOTAL-EARNED-POINTS
PERFORM UNTIL GRADES-FILE-EOF = 1
READ GRADES-FILE
AT END SET
GRADES-FILE-EOF TO 1
NOT AT END
ADD 1 TO RECORD-COUNT
MOVE INPUT-GRADES TO GRADE(RECORD-COUNT)
SET TOTAL-EARNED-POINTS UP BY POINTS-EARNED(RECORD-COUNT)
DISPLAY TOTAL-EARNED-POINTS
END-READ
END-PERFORM.
CLOSE GRADES-FILE.
DISPLAY TOTAL-EARNED-POINTS.
SORT GRADE ASCENDING CATEGORY.
MOVE CATEGORY(1) TO LAST-CATEGORY.
PERFORM RECORD-COUNT TIMES
MOVE CATEGORY(K) TO CURRENT-CATEGORY
IF CURRENT-CATEGORY = LAST-CATEGORY THEN
DISPLAY "SAME CATEGORY"
ELSE
DISPLAY "NEW CATEGORY"
MOVE LAST-CATEGORY TO CURRENT-CATEGORY
END-IF
SET K UP BY 1
END-PERFORM
DISPLAY GRADES.
STOP RUN.
Edit 2: Upon implementing the given answer to convert the input from the file to a numeric form, the FIRST ROW of the table reads fine, but from then on it's all blank values.
Here's the READ block's new code, I'm not sure if there's a more efficient way to read and convert specific fields in a group field but this is how I assumed it should be done.
PERFORM UNTIL GRADES-FILE-EOF = 1
READ GRADES-FILE
AT END SET
GRADES-FILE-EOF TO 1
NOT AT END
ADD 1 TO RECORD-COUNT
MOVE INPUT-ASSIGNMENT-NAME(RECORD-COUNT) TO ASSIGNMENT-NAME(RECORD-COUNT)
DISPLAY INPUT-ASSIGNMENT-NAME(RECORD-COUNT)
DISPLAY ASSIGNMENT-NAME(RECORD-COUNT)
MOVE INPUT-CATEGORY(RECORD-COUNT) TO CATEGORY(RECORD-COUNT)
DISPLAY INPUT-CATEGORY(RECORD-COUNT)
DISPLAY CATEGORY(RECORD-COUNT)
MOVE FUNCTION NUMVAL (INPUT-POINTS-POSSIBLE(RECORD-COUNT)) TO POINTS-POSSIBLE(RECORD-COUNT)
DISPLAY INPUT-POINTS-POSSIBLE(RECORD-COUNT)
DISPLAY POINTS-POSSIBLE(RECORD-COUNT)
MOVE FUNCTION NUMVAL (INPUT-POINTS-EARNED(RECORD-COUNT)) TO POINTS-EARNED(RECORD-COUNT)
DISPLAY INPUT-POINTS-EARNED(RECORD-COUNT)
DISPLAY POINTS-EARNED(RECORD-COUNT)
COMPUTE TOTAL-EARNED-POINTS = TOTAL-EARNED-POINTS + POINTS-EARNED(RECORD-COUNT)
DISPLAY TOTAL-EARNED-POINTS
END-READ
END-PERFORM.
is it a funny looking representation of the number 5?
No, it is an unchecked fatal exception: EC-DATA-INCOMPATIBLE.
The reason:
Your data definition and record-definition doesn't match:
10 POINTS-EARNED PIC 9(14).
would be
"00000000000005"
not
"5 "
which looks like the better definition for would be
10 SOME-POSSIBILY-NUMERIC-DATA PIC X(14).
If you use GnuCOBOL as the tags suggest, then add -debug to the compile command and you will see the fatal exception stopping the program (the COBOL standard defines that all exception checking is off by default, in my opinion: because of legacy and performance, but at least for developing and testing it is very reasonable to activate them [in most cases it is even more reasonable to let the program abend instead of doing wrong math when the test is over]).
As with any computer language you should be very sure to have valid data (never trust external data, no matter if it is part of a blockchain or a text file you read in).
How can I write this so that I can do proper mathematics with it to
print a proper grade report?
If you want to go with "bad data is just ignored" (which may be appropriate here) just convert it:
MOVE FUNCTION NUMVAL (SOME-POSSIBILY-NUMERIC-DATA)
TO POINTS-EARNED(RECORD-COUNT)
Otherwise do an explicit check (either of completely numeric [own check], or numeric with possible spaces to the left/right FUNCTION TEST-NUMVAL) and stop the program/skip the bad line with a DISPLAY ... UPON SYSERR or whatever is appropriate for you.
Related
Additional background: this is a follow-up to Adding two integers giving unwanted result in cobol.
As the input data consists of strings and integers are needed for calculating, this program bulk reads each row, then read each field individually from the file and convert the necessary fields to numbers when storing them in the working storage section table.
Now, for some reason, only the first record reads and stores properly. The rest of the records are being read as blanks or nulls, I guess, even though the file contents after the first record are obviously not null.
Here is my current code for the full program:
IDENTIFICATION DIVISION.
PROGRAM-ID. GRADEREPORT.
AUTHOR. JORDAN RENAUD.
DATE-WRITTEN. 09/18/2020.
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT GRADES-FILE ASSIGN TO "bill"
ORGANIZATION IS LINE SEQUENTIAL
ACCESS IS SEQUENTIAL.
DATA DIVISION.
FILE SECTION.
FD GRADES-FILE.
01 INPUT-TOTAL-POINTS PIC 9(4).
01 INPUT-GRADES.
05 INPUT-GRADE OCCURS 1 to 100 TIMES DEPENDING ON RECORD-COUNT.
10 INPUT-ASSIGNMENT-NAME PIC X(20).
10 INPUT-CATEGORY PIC X(20).
10 INPUT-POINTS-POSSIBLE PIC X(14).
10 INPUT-POINTS-EARNED PIC X(14).
WORKING-STORAGE SECTION.
77 GRADES-FILE-EOF PIC 9.
01 RECORD-COUNT PIC 9(8) VALUE 0.
01 TOTAL-EARNED-POINTS PIC 9(14) VALUE ZERO.
01 TOTAL-POSSIBLE-POINTS PIC 9(14) VALUE 5.
01 K PIC 9(14) VALUE 1.
01 TMP PIC 9(14).
01 CURRENT-CATEGORY PIC X(20).
01 CATEGORY-WEIGHT PIC X(3).
01 LAST-CATEGORY PIC X(20).
01 TOTAL-POINTS PIC 9(4).
01 GRADES.
05 GRADE OCCURS 1 TO 100 TIMES DEPENDING ON RECORD-COUNT.
10 ASSIGNMENT-NAME PIC X(20).
10 CATEGORY PIC X(20).
10 POINTS-POSSIBLE PIC 9(14).
10 POINTS-EARNED PIC 9(14).
PROCEDURE DIVISION.
OPEN INPUT GRADES-FILE.
READ GRADES-FILE INTO TOTAL-POINTS.
DISPLAY TOTAL-EARNED-POINTS
PERFORM UNTIL GRADES-FILE-EOF = 1
READ GRADES-FILE
AT END SET
GRADES-FILE-EOF TO 1
NOT AT END
ADD 1 TO RECORD-COUNT
MOVE INPUT-ASSIGNMENT-NAME(RECORD-COUNT) TO ASSIGNMENT-NAME(RECORD-COUNT)
DISPLAY INPUT-ASSIGNMENT-NAME(RECORD-COUNT)
DISPLAY ASSIGNMENT-NAME(RECORD-COUNT)
MOVE INPUT-CATEGORY(RECORD-COUNT) TO CATEGORY(RECORD-COUNT)
DISPLAY INPUT-CATEGORY(RECORD-COUNT)
DISPLAY CATEGORY(RECORD-COUNT)
MOVE FUNCTION NUMVAL (INPUT-POINTS-POSSIBLE(RECORD-COUNT)) TO POINTS-POSSIBLE(RECORD-COUNT)
DISPLAY INPUT-POINTS-POSSIBLE(RECORD-COUNT)
DISPLAY POINTS-POSSIBLE(RECORD-COUNT)
MOVE FUNCTION NUMVAL (INPUT-POINTS-EARNED(RECORD-COUNT)) TO POINTS-EARNED(RECORD-COUNT)
DISPLAY INPUT-POINTS-EARNED(RECORD-COUNT)
DISPLAY POINTS-EARNED(RECORD-COUNT)
COMPUTE TOTAL-EARNED-POINTS = TOTAL-EARNED-POINTS + POINTS-EARNED(RECORD-COUNT)
DISPLAY TOTAL-EARNED-POINTS
END-READ
END-PERFORM.
CLOSE GRADES-FILE.
DISPLAY TOTAL-EARNED-POINTS.
SORT GRADE ASCENDING CATEGORY.
MOVE CATEGORY(1) TO LAST-CATEGORY.
PERFORM RECORD-COUNT TIMES
MOVE CATEGORY(K) TO CURRENT-CATEGORY
IF CURRENT-CATEGORY = LAST-CATEGORY THEN
DISPLAY "SAME CATEGORY"
ELSE
DISPLAY "NEW CATEGORY"
MOVE LAST-CATEGORY TO CURRENT-CATEGORY
END-IF
SET K UP BY 1
END-PERFORM
DISPLAY GRADES.
STOP RUN.
and here is the input file, bill:
1000
MS 1 - Join Grps Group Project 5 5
Four Programs Programming 15 9
Quiz 1 Quizzes 10 7
FORTRAN Programming 25 18
Quiz 2 Quizzes 10 9
HW 1 - Looplang Homework 20 15
As per the code written, the first line read from the table section of the file(lines 2 and forward) has its individual parts DISPLAY 'ed as follows:
MS 1 - Join Grps
MS 1 - Join Grps
Group Project
Group Project
5
00000000000005
5
00000000000005
This is what I expect. Each item is repeated, the first iteration is the input file structure, and the second is the working storage section structure. The difference being that the input structures are read as all strings of 20 and 14 lengths, and the storage structures are formatted as two strings of 20 length, and two ints of 14 length. The numeric strings are converted to ints and stored in the working storage, as stated earlier.
The output of the second row's DISPLAYs show as this:
00000000000000
00000000000000
00000000000005
00000000000000
00000000000000
00000000000005
00000000000000
00000000000000
00000000000005
00000000000000
00000000000000
00000000000005
00000000000000
00000000000000
00000000000005
In this case, the 00000000000005 is the total of a summation accumulator variable, which is always 5 because the first row reads 5 for the earned points, and the rest of them are just evaluating to zero because they're being read as blanks.
How can I get my program to properly read the rest of the file?
Turns out, when reading a table from a file, the subscript to access the current line is always 1, so instead of reading RECORD-COUNT as the subscript of the INPUT-items, I just put 1 for all of them, and the program works as expected!
I find myself sorting an input file, and using a control break to compute some data. We need headers in the control break, the report writer is duplicating the header each time and I can not figure it out for the life of me. The write statement in the break paragraph is written twice, but if I use a DISPLAY it is only displayed once. Where am I going wrong with the Report Writer? The break itself is calculating the data correctly (but probably terribly)
environment division.
configuration section.
input-output section.
file-control.
SELECT corpranks
ASSIGN TO
"corpranks.txt"
ORGANIZATION IS LINE SEQUENTIAL.
SELECT out-file
ASSIGN TO
"report"
ORGANIZATION IS LINE SEQUENTIAL.
SELECT sortfile
ASSIGN TO
"SortFile".
data division.
file section.
FD corpranks
RECORD CONTAINS 80 CHARACTERS.
01 gf-rec.
05 first-initial PIC x.
05 middle-initial PIC x.
05 last-name PIC x(14).
05 rank-code PIC 9.
05 Filler PIC x(15).
05 rank PIC x(3).
05 salary PIC 9(6).
05 corporation PIC x(29) VALUE SPACE.
FD out-file
REPORT IS corp-report.
01 of-rec PIC x(80).
SD sortfile.
01 Sortrec.
05 PIC x(16).
05 SR-rank PIC xxx.
05 PIC x(22).
05 SR-corporation PIC x(29).
working-storage section.
77 EOF PIC x VALUE "N".
77 current-corp PIC x(29).
77 total-salary PIC 9(6) VALUE 0.
77 current-salary PIC 9(6).
77 converted-month PIC x(3).
77 concatenated-date PIC x(28).
77 formatted-date PIC x(80) JUSTIFIED RIGHT.
77 formatted-name PIC x(20).
77 tally-counter PIC 9.
77 inp-len PIC 9.
01 current-date.
05 YYYY PIC x(4).
05 MM PIC x(2).
05 DD PIC x(2).
01 corporation-header.
05 FILLER pic x(18) VALUE SPACES.
05 FILLER pic x(13) VALUE "Corporation: ".
05 ch-corp pic x(40).
01 corporation-subheader.
05 FILLER pic x(5) VALUE SPACES.
05 FILLER pic x(4) VALUE "RANK".
05 FILLER pic x(5) VALUE SPACES.
05 FILLER pic x(4) VALUE "NAME".
05 FILLER pic x(15) VALUE SPACES.
05 FILLER pic x(6) VALUE "SALARY".
77 csh-underline pic x(40) Value
"========================================".
01 main-header.
05 FILLER PIC x(5).
05 header-content PIC x(69) VALUE "Jacksonville Computer App
"lications Support Personnel Salaries".
report section.
RD corp-report.
01 REPORT-LINE
TYPE DETAIL
LINE PLUS 2.
05 COLUMN 6 PIC x(3) SOURCE rank.
05 COLUMN 12 PIC x(20) SOURCE formatted-name.
05 COLUMN 37 PIC 9(6) SOURCE salary.
procedure division.
0000-MAIN.
Sort Sortfile on ascending key SR-corporation
on ascending key SR-rank
Using corpranks
giving corpranks.
OPEN
INPUT corpranks
OUTPUT out-file
INITIATE corp-report.
WRITE of-rec FROM main-header.
ACCEPT current-date from DATE YYYYMMDD.
PERFORM 3000-CONVERT-MONTH.
STRING "As of: " DELIMITED BY SIZE
DD DELIMITED BY SIZE
SPACE
converted-month DELIMITED BY SIZE
SPACE
YYYY DELIMITED BY SIZE
INTO concatenated-date.
MOVE concatenated-date TO formatted-date.
WRITE of-rec FROM formatted-date.
PERFORM 2000-GENERATE-REPORT UNTIL EOF = 1.
TERMINATE corp-report.
stop run.
2000-GENERATE-REPORT.
PERFORM 3100-TRIM-FIELDS
GENERATE REPORT-LINE
READ corpranks
AT END
CLOSE corpranks
out-file
MOVE 1 TO eof
NOT AT END
IF current-corp = SPACE
MOVE corporation to current-corp
MOVE current-corp to ch-corp
WRITE of-rec FROM corporation-header
WRITE of-rec FROM corporation-subheader
WRITE of-rec FROM csh-underline
END-IF
IF current-corp NOT = corporation
PERFORM 2500-CONTROL-BREAK
END-IF
COMPUTE total-salary = total-salary + salary
MOVE corporation to current-corp
END-READ.
2500-CONTROL-BREAK.
WRITE of-rec FROM corporation
MOVE 0 to total-salary
.
3000-CONVERT-MONTH.
EVALUATE mm
WHEN "01" MOVE "JAN" TO converted-month
WHEN "02" MOVE "FEB" TO converted-month
WHEN "03" MOVE "MAR" TO converted-month
WHEN "04" MOVE "APR" TO converted-month
WHEN "05" MOVE "MAY" TO converted-month
WHEN "06" MOVE "JUN" TO converted-month
WHEN "07" MOVE "JUL" TO converted-month
WHEN "08" MOVE "AUG" TO converted-month
WHEN "09" MOVE "SEP" TO converted-month
WHEN "10" MOVE "OCT" TO converted-month
WHEN "11" MOVE "NOV" TO converted-month
WHEN "12" MOVE "DEC" TO converted-month
WHEN OTHER MOVE mm to converted-month
END-EVALUATE.
3100-TRIM-FIELDS.
INSPECT last-name TALLYING tally-counter FOR trailing
spaces.
COMPUTE inp-len = LENGTH OF last-name - tally-counter
MOVE last-name(1: inp-len) to formatted-name
STRING last-name(1: inp-len) DELIMITED BY SIZE
SPACE
first-initial DELIMITED BY SIZE
INTO formatted-name
MOVE 0 TO tally-counter
end program Program2.
Some report output: (at the beginning header, csh-underline is the last thing written, the === underline displays twice. At the corporation control breaks, the next corp name is the last thing written, and is written twice)
Jacksonville Computer Applications Support Personnel Salaries
As of: 18 FEB 2015
Corporation: Alltel Information Services
RANK NAME SALARY
========================================
========================================
EVP COLUMBUS C 100000
SVP ADAMS S 042500
VP REAGAN R 081000
VP FRANKLIN B 080000
A&P FORD G 060000
A&P HAYES R 050000
A&P JACKSON A 057600
A&P TYLER J 069000
A&P HARRISON B 052000
A&P TAFT W 070500
A&P HOOVER H 035000
A&P PIERCE F 044000
American Express
American Express
EVP JOHNSON L 098000
SVP CLINTON W 086000
VP ROOSEVELT F 072000
A&P HARDING W 040000
....
Here's a link to some Report Writer documentation from Micro Focus. It is not the only documentation they provide, but it is all that I have scanned through: http://documentation.microfocus.com/help/index.jsp?topic=%2Fcom.microfocus.eclipse.infocenter.studee60win%2FGUID-48E4E734-F1A4-41C4-BA30-38993C8FE100.html
If you loot at Report File under Enterprise > Micro Focus Studio Enterprise Edition 6.0 > General Reference > COBOL Language Reference > Part 3. Additional Topics > Report Writer you will see this:
Report File
A report file is an output file having sequential organization. A
report file has a file description entry containing a REPORT clause.
The content of a report file consists of records that are written
under control of the RWCS.
A report file is named by a file control entry and is described by a
file description entry containing a REPORT clause. A report file is
referred to and accessed by the OPEN, GENERATE, INITIATE, SUPPRESS,
TERMINATE, USE BEFORE REPORTING, and CLOSE statements.
Although this does not definitively say "Don't use your own WRITE statements and hope that they will work" I think it is clear that you should not. What happens when you do that is not defined, or is "undefined behaviour".
You are getting repeated lines before a break, and after a break, exactly where the Report Writer will be checking if there is anything it needs to do. Although I know nothing at all about the implementation of the Report Writer in Micro Focus COBOL, I am pretty certain that you have correctly identified that the repetition happens and is beyond your control. I think the above quote confirms that, and within other parts of Micro Focus's documentation this may be made more explicit.
You either need to use the Report Writer fully (if the task is to use the Report Writer) or not use it at all. You can't mix automatic and manual on the same report file, it seems, and that makes sense to me.
Remember, it does not matter that some of your WRITE statements seem to work, because this is a computer and you need them all to work.
Some general comments on your program:
In main-header you have a FILLER without a VALUE clause, which can cause problems when written to a file for printing. Whether that is way those five bytes don't show on your output or whether it is due to formatting in the posting here, I don't know.
Also in main-header you have a long literal, continued onto a second line. I can't see the continuation marker, and that may be a feature of how it is done in that Micro Focus COBOL, but it always makes things easier if literals are not continued. Define two smaller fields one after the other, with smaller literals which taken together make up the whole.
You have this:
COMPUTE total-salary = total-salary + salary
This, however, is considered clearer:
ADD salary TO total-salary
You are using STRING. You should be aware that the data-transfer from the sending fields ceases when the receiving field is filled, or when all the sending fields have been processed. In the latter case, automatic space-padding is not carried out, unlike the behaviour of a MOVE statement. You need to set your receiving field to an initial value before the STRING is executed, else you will retain data from the previous execution of STRING when the current execution of STRING has less actual data.
After the STRING you do this:
MOVE 0 TO tally-counter
This means your INSPECT, several statements earlier, but where tally-counter is used, is relying on a previous value for tally-counter for the code after that to work. This is not good practice. Make tally-counter an initial value before it is used in the INSPECT.
If you go with the Report Writer your PROCEDURE DIVISION code will be significantly reduced, because the definition of the report elements defines the automatic processing.
The Report Write feature of COBOL is very powerful. It allows you to define a complex report in the REPORT SECTION of a COBOL program, with headings, column headings, detail lines, control-break totals etc. In the PROCEDURE DIVISION you only need as little as make the source-data available (say with a READ) and then GENERATE the report, and COBOL does the rest for you.
However, you have defined a very simple report, and are attempting to do headings, totals etc yourself. I have never done this, and don't know if it works in general, or if it works for your compiler.
From your testing, it seems like there may be a problem with doing this, and it may be, erroneously, repeating the line you yourself have written. You need to check that that particular line is not output elsewhere in your program.
We need to see the outstanding answers to questions from comments, and, unless it is an excessive size, your entire program.
If your exercise is specifically to use the Report Writer, then I think you need to define a more "complex" report, which will produce, automatically from the definition, everything that you want.
If you do not have to use the Report Writer for this exercise, don't use it, just do the detail-line formatting yourself and WRITE it as you are already doing for headings and totals.
On the assumption (later proved false) that you were using the Report Writer to do everything you need, the problem would have been manually writing to the same output file that the Report Writer was using.
If using the full features of the Report Writer, simply make this change and remove any other WRITEs to that output file, and use the Report Writer features for everything:
2500-CONTROL-BREAK.
MOVE 0 to total-salary
.
I need help on some COBOL homework. I've made a few attempts and they don't seem to be working as I would hope.
I need to make a program that reads an input file with some student info, then output it to the terminal and an output file.
I also need to calculate the GPA based on the hours and quality points earned.
I am currently having issues with creating column headers, and also adding values to get the cumulative values to get the GPA, among some other things. I have the input file and the code I have so far attached.
IDENTIFICATION DIVISION.
PROGRAM-ID. TEST3.
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT StudentFile ASSIGN TO "P2In.dat"
ORGANIZATION IS LINE SEQUENTIAL.
SELECT OutputFile ASSIGN TO "Report.dat"
ORGANIZATION IS LINE SEQUENTIAL.
DATA DIVISION.
FILE SECTION.
FD StudentFile.
*>Student details will only be printed once
01 StudentDetails.
05 STUDENT-NAME PIC X(16).
05 STUDENT-ID PIC X(9).
*>Semester info that will be on one line and not repeated
01 SemesterDetails.
05 SEMESTER PIC X(9).
*> Details in the class that need to be seperate
01 ClassDetails.
05 CLASS-NAME PIC X(32).
05 GRADE PIC X(2).
05 HOURS PIC X(4).
05 POINTS PIC X(2).
*>values that need to be calculated
01 CalculatedValues.
05 CUMULATIVE-GPA-IN PIC 99v99 VALUE ZERO.
05 CUMULATIVE-QP-IN PIC 99v99 VALUE ZERO.
05 CUMULATIVE-HOURS-IN PIC 99v99 VALUE ZERO.
FD OutputFile.
01 PrintLine PIC X(70).
WORKING-STORAGE SECTION.
01 SWITCHES.
05 EOF-SWITCH PIC X VALUE "N".
01 COUNTERS.
05 REC-COUNTER PIC 9(3) VALUE 0.
01 CUMULATIVE.
05 CUMULATIVE-QP PIC ZZ.99.
PROCEDURE DIVISION.
*>main paragraph, everything starts here
Main.
PERFORM Begin.
PERFORM ProcessData.
PERFORM PrintLines
UNTIL EOF-SWITCH = "Y".
*>opening read
Begin.
OPEN INPUT StudentFile
OPEN OUTPUT OutputFile
READ StudentFile
AT END
MOVE "Y" TO EOF-SWITCH
NOT AT END
COMPUTE REC-COUNTER = REC-COUNTER + 1
END-READ.
ProcessData.
READ StudentFile
AT END
MOVE "Y" TO EOF-SWITCH
NOT AT END
IF GRADE = 'A'
COMPUTE CUMULATIVE-QP = CUMULATIVE-QP + 4
ELSE
IF GRADE = 'B'
COMPUTE CUMULATIVE-QP = CUMULATIVE-QP + 3
ELSE
IF GRADE = 'C'
COMPUTE CUMULATIVE-QP = CUMULATIVE-QP + 2
ELSE
IF GRADE = 'D'
COMPUTE CUMULATIVE-QP = CUMULATIVE-QP + 1
END-IF.
*>printing out our lines to terminal
PrintLines.
READ StudentFile
AT END
MOVE "Y" TO EOF-SWITCH
NOT AT END
DISPLAY CUMULATIVE-QP
END-READ.
And the input file looks like this
TERRY ETHELBERT W1234567 FALL2014 CMPS161 ALGORITHM DSGN/IMPLMNT A 3.00 12.00
TERRY ETHELBERT W1234567 FALL2014 CMPS280 ALGORITHM DSGN/IMPLMNTII B 3.00 9.00
TERRY ETHELBERT W1234567 FALL2014 CMPS431 OPERATING SYSTEMS C 3.00 6.00
TERRY ETHELBERT W1234567 FALL2014 ENG322 TECHNICAL WRITING A 3.00 12.00
TERRY ETHELBERT W1234567 SPNG2015 MATH380 STATISTICS B 3.00 9.00
TERRY ETHELBERT W1234567 SPNG2015 HIST202 HISTORY B 3.00 9.00
TERRY ETHELBERT W1234567 SPNG2015 BIOL152 GENERAL BIOLOGY A 3.00 12.00
TERRY ETHELBERT W1234567 SPNG2015 MATH200 CALCULUS I C 5.00 10.00
A place to start would be nice.
First issue is to get your input record correct. The FD must match the line layout, so it should be something like
01 StudentDetails.
05 STUDENT-NAME PIC X(16).
05 STUDENT-ID PIC X(9).
*> Details in the class that need to be seperate
*01 ClassDetails.
05 CLASS-NAME PIC X(32).
05 GRADE PIC X(1).
05 FILLER PIC X(1).
05 HOURS.
07 HOURS-9 PIC 9.99.
05 FILLER PIC X(2).
05 POINTS.
07 POINTS-X PIC X(1) OCCURS 5.
05 POINTS-9-99 REDEFINES POINTS.
07 POINTS-9-99 PIC 9.99.
05 POINTS-99-99 REDEFINES POINTS.
07 POINTS-99-99 PIC 99.99.
Note that GRADE is an X(1) and is followed by a FILLER also X(1) to represent the space that follows the grade-letter.
HOURS is implicitly a X(4); HOURS-9 allows that field to be read as a 9.99
Then there are 2 spaces - another filler
Finally, there are POINTS. This is a 5-character field with 2 layouts. We van determine which of the layouts to use (POINTS-9-99 or POINTS-99-99) by looking at POINTS-X(2) - a dot means use POINTS-9-99, otherwise use POINTS-99-99.
I've no idea what Semesterdetails are.
Your Calculatedvalues are supposed to be in WORKING-STORAGE; you can't have a VALUE clause in an FD.
Next, you should think through your process. Think Michael Jackson. Seriously. Oh - not the singer, the computer scientist.
Your process:
Start with a CURRENT-STUDENT containing SPACES.
Read each record. If the STUDENT-NAME is not equal to CURRENT-STUDENT, (and also AT END) then (produce a report line, zero your accumulators and store STUDENT-NAME into CURRENT-STUDENT.) and use the fields in the current record to accumulate the required data.
Note that producing your report line is simply a matter of building the various accumulated fields into the output record and doing a little mathematical gymnastics to calculate averages. Naturally, don't bother if the CURRENT-STUDENT contains SPACES.
So, the essentials are
READ studentfile
at end perform write-report-line
not at end
if student-name is not equal to current-student
perform write-report-line
end-if
perform accumulate-data.
and the write-report-line paragraph is
if current-student is not equal to spaces
calculate and move name, average, etc. to output-record
and write it
end-if
move student-name to current-student
move zero to rec-counter etc, etc.
As Magoo has pointed out, you need to get your record-definition straight. You defined separate records when you defined each logical block as a separate 01-level. This does not match your data (which for the moment we assume is correct). It is unclear what POINTS is, but your definition doesn't match the data.
01 RecordDetails.
03 StudentDetails.
05 STUDENT-NAME PIC X(16).
05 STUDENT-ID PIC X(9).
03 SemesterDetails.
05 SEMESTER PIC X(9).
03 ClassDetails.
05 CLASS-NAME PIC X(32).
05 GRADE PIC X(2).
05 HOURS PIC X(4).
05 POINTS PIC X(2).
This you've define subordinate to the FD, so it as a record on your file:
01 CalculatedValues.
05 CUMULATIVE-GPA-IN PIC 99v99 VALUE ZERO.
05 CUMULATIVE-QP-IN PIC 99v99 VALUE ZERO.
05 CUMULATIVE-HOURS-IN PIC 99v99 VALUE ZERO.
That is probably not what you want.
Look at the documentation and understand what using FILE STATUS on the SELECT gets you. Every IO should have it's (separate per file) FILE STATUS field checked. You can then use the FILE STATUS field (via an 88-level with a value of "10") to check for end-of-file, cutting the tortuous use of READ ... AT END ... NOT AT END ....
88's are good for your grades as well, rather than literals. Note that if adding "4" it is better to add a well-named field with a VALUE of 4, so that the reader knows what is being added (what the 4 means).
Unless you have a complex calculation, you may want to prefer ADD 1 TO field-name over COMPUTE field-name = field-name + 1.
If you have your grade tests, you'll find EVALUATE much clearer to use than nested- or sequential-IFs.
You don't have any output yet, either file or screen. Look around here and elsewhere for examples and see how that goes. Best to ask a new question if you get stuck with that, else the answers become too complex. One thing at a time.
I have an error code 18 in COBOL when I'm trying to write the output to a file. I'm using Micro Focus VS 2012. I have tried everything but it seem doesn't print the output correctly at this time.
...
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT GRADE-FILE ASSIGN TO 'Grades.txt'.
SELECT PRINT-FILE ASSIGN TO 'Output.txt'
ORGANIZATION IS LINE SEQUENTIAL.
DATA DIVISION.
FILE SECTION.
FD GRADE-FILE
LABEL RECORDS ARE STANDARD.
01 GRADE-RECORD.
05 I-STUDENT PIC X(14).
05 I-GRADE1 PIC 999.
05 I-GRADE2 PIC 999.
05 I-GRADE3 PIC 999.
05 I-GRADE4 PIC 999.
05 I-GRADE5 PIC 999.
05 I-GRADE6 PIC 999.
FD PRINT-FILE
LABEL RECORDS ARE STANDARD.
01 PRINT-RECORD PIC X(80).
WORKING-STORAGE SECTION.
01 PROGRAM-VARIABLES.
05 W-AVERAGE PIC 999V99.
05 W-EOF-FLAG PIC X VALUE 'N'.
01 PAGE-TITLE.
05 PIC X(46) VALUE
' S I X W E E K G R A D E R E P O R T'.
01 HEADING-LINE1.
05 PIC X(51) VALUE
' Student T e s t S c o r e s Average'.
01 HEADING-LINE2.
05 PIC X(51) VALUE
'--------------------------------------------------'.
01 DETAIL-LINE.
05 PIC X VALUE SPACE.
05 O-STUDENT PIC X(14).
05 PIC X VALUE SPACE.
05 O-GRADE1 PIC ZZ9.
05 PIC X VALUE SPACE.
05 O-GRADE2 PIC ZZ9.
05 PIC X VALUE SPACE.
05 O-GRADE3 PIC ZZ9.
05 PIC X VALUE SPACE.
05 O-GRADE4 PIC ZZ9.
05 PIC X VALUE SPACE.
05 O-GRADE5 PIC ZZ9.
05 PIC X VALUE SPACE.
05 O-GRADE6 PIC ZZ9.
05 PIC X(4) VALUE SPACE.
05 O-AVERAGE PIC ZZ9.99.
PROCEDURE DIVISION.
10-MAINLINE.
OPEN INPUT GRADE-FILE
OUTPUT PRINT-FILE
PERFORM 20-PRINT-HEADINGS
PERFORM 30-PROCESS-LOOP
CLOSE GRADE-FILE
PRINT-FILE
STOP RUN.
20-PRINT-HEADINGS.
MOVE PAGE-TITLE TO PRINT-RECORD
WRITE PRINT-RECORD AFTER ADVANCING 1 LINE
MOVE HEADING-LINE1 TO PRINT-RECORD
WRITE PRINT-RECORD AFTER ADVANCING 3 LINES
MOVE HEADING-LINE2 TO PRINT-RECORD
WRITE PRINT-RECORD AFTER ADVANCING 1 LINE.
30-PROCESS-LOOP.
* PERFORM 40-READ-RECORD
READ GRADE-FILE
PERFORM UNTIL W-EOF-FLAG = 'Y'
PERFORM 50-COMPUTE-GRADE-AVERAGE
PERFORM 60-PRINT-DETAIL-LINE
READ GRADE-FILE
* PERFORM 40-READ-RECORD
END-PERFORM.
*40-READ-RECORD.
* READ GRADE-FILE
* AT END MOVE 'Y' TO W-EOF-FLAG.
50-COMPUTE-GRADE-AVERAGE.
COMPUTE W-AVERAGE ROUNDED = (I-GRADE1 + I-GRADE2 + I-GRADE3 + I-GRADE4 + I-GRADE5 + I-GRADE6 ) / 6.
60-PRINT-DETAIL-LINE.
MOVE SPACES TO DETAIL-LINE
MOVE I-STUDENT TO O-STUDENT
MOVE I-GRADE1 TO O-GRADE1
MOVE I-GRADE2 TO O-GRADE2
MOVE I-GRADE3 TO O-GRADE3
MOVE I-GRADE4 TO O-GRADE4
MOVE I-GRADE5 TO O-GRADE5
MOVE I-GRADE6 TO O-GRADE6
MOVE W-AVERAGE TO O-AVERAGE
WRITE PRINT-RECORD FROM DETAIL-LINE AFTER ADVANCING 1 LINE.
end program "GradeReport.Program1"
S I X W E E K G R A D E R E P O R T
Student T e s t S c o r e s Average
--------------------------------------------------
KellyAntonetz0 700 500 980 800 650 852 747.00
obertCain09708 207 907 309 406 2;1 25> 400.67
Dehaven0810870 940 850 930 892 122 981 785.83
rmon0760770800 810 750 92; 142 9>1 <1> 816.33
g0990930890830 940 901 =1> 41= ?82 65 872.50
06707108408809 6=9 ;52 565 <<0 900 870 924.33
78052076089Woo 493 9>4 520 760 760 830 734.50
Something prior to your COBOL program has pickled your file by removing all the spaces and shuffling the data to the left.
Your first student shows as KellyAntonetz but likely should be Kelly Antonetz. Since only one space was removed, the grade data has moved only one place to the left, so the numbers are still recognizable and although the average is a factor of 10 out, it is approximately correct.
It is not actually correct (except for the power of 10) because of that 2 following the 85. Where did that 2 come from?
It came from the next record, where the first-name should be Robert but you show as obertCain09708. The ASCII code for the letter R is X'82'. When treated as a number by COBOL the 8 will be ignored (or will cause a crash when in the trailing byte of a number). Your compiler doesn't cause the code to crash, but does treat the R as the number 2.
obertCain is only 9 bytes out of the 14 you have for the name. The five spaces/blanks which have been "lost" this time cause the numerics to be pulled-left by five bytes. From that point onward, explaining how the output you show fits the presumed input becomes an academic exercise only.
Further support is a reference for what would be a FILE STATUS code of 18 from a Micro Focus compiler, here: http://www.simotime.com/vsmfsk01.htm
Which says, for 18:
Read part record error: EOF before EOR or file open in wrong mode
(Micro Focus).
Your final record would "finish" before expected, with end-of-file being detected before 32 bytes have been read.
Note that the error is on your input file, not your output file.
Losing the spaces in that way can be done in many ways, so I can't guess what you are doing to the file before it gets to the COBOL program, but neither COBOL itself nor your code is doing that.
Take note of Emmad Kareem's comments. Use the FILE STATUS. Check the file-status field (define one per file) after each IO, so that you know when a problem occurs, and what the problem is.
Testing the file-status field for 10 on a file you are reading sequentially gives cleaner code than the AT END on the READ.
Note also that if your program had not crashed there, it would either loop infinitely or crash shortly afterwards. Probably in trying to fix your problem, you have commented-out your use of the "read paragraph" and in that paragraph is the only place you are setting end-of-file.
If you use the file-status instead of AT END, you don't need to define a flag/switch you can use an 88 on the file-status field and have the COBOL run-time set it for you directly, without you having to code it.
Just a couple of points about your DETAIL-LINE.
There is no need to MOVE SPACE to it, as you MOVE to each named field, and the (un-named) FILLERs have VALUE SPACE.
You don't necessarily need the (un-named) FILLERS. Try this:
01 DETAIL-LINE.
05 O-STUDENT PIC BX(14).
05 O-GRADE1 PIC ZZZ9.
05 O-GRADE2 PIC ZZZ9.
05 O-GRADE3 PIC ZZZ9.
05 O-GRADE4 PIC ZZZ9.
05 O-GRADE5 PIC ZZZ9.
05 O-GRADE6 PIC ZZZ9.
05 O-AVERAGE PIC Z(6)9.99.
If you work with COBOL, you may see this type of thing, so it is good to know. With massive amounts of output there is probably a small performance penalty. You may find it more convenient for "lining-up" output to headings.
Ah. Putting together you non-use of LINE SEQUENTIAL for your input file, I predict you have a "script" running some time before the COBOL program which is supposed to remove the record-terminators (whatever those are on your OS) at the end of each logical record, but that you have accidentally removed all whitespace from all positions of your record instead.
With LINE SEQUENTIAL you can have records of fixed-length which also happen to be "terminated". Unless the exercise specifically includes the removal of the record terminators, just use LINE SEQUENTIAL.
If you are supposed to remove the terminators, don't do so for whitespace which covers too much (be specific) and also "anchor" the change to the end of the record.
OK so I'm doing assignment but then I found that I was asked to add page numbers and change pages for each 4 records. Since it's an online course and I don't think there is anything about page numbers in lecture videos. So the main problems are
To add a heading that contains date and page number,
Print 4 records per page, which means page needs to be changed after printing 4 records.
I really have no idea how to do this.
Here is the code I have finished:
ENVIRONMENT DIVISION.
FILE-CONTROL. SELECT STOCK-IN ASSIGN TO 'F:/CS201S13/PROJECT2.TXT'
ORGANIZATION IS LINE SEQUENTIAL.
SELECT STOCK-OUT ASSIGN TO 'F:/CS201S13/PROJECT2OUTPUT.TXT'
ORGANIZATION IS LINE SEQUENTIAL.
DATA DIVISION.
FILE SECTION.
FD STOCK-IN.
01 STOCK-RECORD.
05 ST-TRANSACTION-INFORMATION.
10 ST-TRANSACTION-SHARES PIC 9(3).
10 ST-TRANSACTION-STOCK PIC X(14).
05 ST-PURCHASE-INFORMATION.
10 ST-PURCHASE-PRICE PIC 9(5)V99.
10 ST-PURCHASE-DATE.
15 ST-PURCHASE-YEAR PIC 99.
15 ST-PURCHASE-MONTH PIC 99.
15 ST-PURCHASE-DAY PIC 99.
05 ST-SALE-INFORMATION.
10 ST-SALE-PRICE PIC 9(5)V99.
10 ST-SALE-DATE.
15 ST-SALE-YEAR PIC 99.
15 ST-SALE-MONTH PIC 99.
15 ST-SALE-DAY PIC 99.
FD STOCK-OUT.
01 STOCK-RECORD-OUT.
05 ST-TRANSACTION-INFORMATION-OUT.
10 ST-TRANSACTION-SHARES-OUT PIC 9(3).
10 ST-TRANSACTION-STOCK-OUT PIC X(14).
05 TOTAL-PURCHASE PIC 9(8)V99.
05 PIC X(4).
05 TOTAL-SALE PIC 9(8)V99.
05 PIC X(4).
05 TOTAL-PROFIT PIC 9(8)V99.
05 PIC X(4).
05 ST-PURCHASE-DATE-OUT.
10 ST-PURCHASE-YEAR-OUT PIC 99.
10 PIC X VALUE '/'.
10 ST-PURCHASE-MONTH-OUT PIC 99.
10 PIC X VALUE '/'.
10 ST-PURCHASE-DAY-OUT PIC 99.
05 PIC X(4).
05 ST-SALE-DATE-OUT.
10 ST-SALE-YEAR-OUT PIC 99.
10 PIC X VALUE '/'.
10 ST-SALE-MONTH-OUT PIC 99.
10 PIC X VALUE '/'.
10 ST-SALE-DAY-OUT PIC 99.
05 PIC X(4).
05 RECORD-OUT PIC 9 VALUE 0.
05 PAGE-OUT PIC 9.
WORKING-STORAGE SECTION.
01 ARE-THERE-MORE-RECORDS PIC XXX VALUE 'YES'.
01 IS-THIS-PAGE-FULL PIC XXX VALUE 'NO '.
PROCEDURE DIVISION.
100-MAIN-PROCESS.
OPEN INPUT STOCK-IN
OUTPUT STOCK-OUT
MOVE ST-TRANSACTION-INFORMATION TO ST-TRANSACTION-INFORMATION-OUT
PERFORM UNTIL ARE-THERE-MORE-RECORDS = 'NO '
READ STOCK-IN
AT END
MOVE 'NO ' TO ARE-THERE-MORE-RECORDS
NOT AT END
PERFORM 200-PROCEDURE-RTN
ADD 1 TO RECORD-OUT
END-READ
END-PERFORM
CLOSE STOCK-IN
STOCK-OUT
STOP RUN.
200-PROCEDURE-RTN.
IF RECORD-OUT = 4
MOVE 'YES' TO IS-THIS-PAGE-FULL
MOVE 0 TO RECORD-OUT
MOVE 'NO ' TO IS-THIS-PAGE-FULL
ADD 1 TO PAGE-OUT
END-IF
MULTIPLY ST-PURCHASE-PRICE BY ST-TRANSACTION-SHARES GIVING TOTAL-PURCHASE
MULTIPLY ST-SALE-PRICE BY ST-TRANSACTION-SHARES GIVING TOTAL-SALE
SUBTRACT TOTAL-PURCHASE FROM TOTAL-SALE GIVING TOTAL-PROFIT
WRITE STOCK-RECORD-OUT.
You are both close, and far away.
"Close" because you need a little bit of code in between setting IS-THIS-PAGE-FULL to YES and NO.
"Far away" as you have quite a lot to do rather than just "patch up" what you have.
Is the program writing an output file (STOCK-OUT) and a report, or is STOCK-OUT the report? If it is a report, change the names so that it is clear that it is a report, not an output file.
Don't worry if this seems a lot. You should be learning how to Program in Cobol, as well as learning Cobol. Doesn't happen overnight.
In no particular order:
Include FILE-STATUS checking for all IO operations on all files, always. At the moment, if your input fails to open and the system does not fail the program (even if yours does, you are presumably learning Cobol to be able to work with any system, not just the one you have) then no records will be read, your "end of file test" will never be YES and you'll have a BFL (Big Fat Loop). With the FILE-STATUS checking, produce useful messages, including key/reference/record number as appropriate for failed READ or WRITE.
You may feel that this is a lot of work. However, put together some "template" files with all the stuff in, and then paste (or even COPY) those into your program each time.
You have VALUE clause in the FD. These will not do what you think.
You have single digit for your page count, which is unlikely to have general application.
Why use YES and NO as literals? Look at the SET verb, in relation to "condition names", use 88's for tests and "flags/switches".
You have "MOVE ST-TRANSACTION-INFORMATION" after the input is opened but before a record is read, and only have one reference to it in the program. This is not going to work.
For reading files, have a look at the "priming read" approach.
read input
loop until end-of-file (88 on file-status)
process data
read input
end-loop
This avoids the AT END/NOT AT END, allows processing of headers (if present) and "empty files" without clogging-up the main logic. The code "expands" with headers/trailers (including the correct number of them), sequence-checking of keys, etc, but you only need to code it once then "template" it.
According to your VALUE clauses in your FD, you expect RECORD-OUT to be zero, so the test for 4 will actually get you five on the first page, and four thereafter.
You always assume there will be a "profit" (a positive amount), which is not realistic, yet you don't allow a signed value for the "profit".
Now, for the report.
For your report FD, just make it a simple thing, length of your print line.
In WORKING-STORAGE, define data for the headings and titles that you need. Define data for a print line. Since you're in the WORKING-STORAGE, put VALUEs for everything which will not have data MOVEd to it in the PROCEDURE DIVISION.
When you have written four items (or when your program tells you this) and you have a fifth, write the headings and titles, remembering to update the page number.
I say "or when your program tells you this" because you can set your original value of "records written" to 4. Comment it, so that it is clear that it is what you want, and why you want it. The reason is, you don't have to then deal with "first time" headings and othe things. For first time, or on a "contol break" (I guess you'll get to those soon) set the " done on a page already" to the maximum for a page, and the headings will pop out when you want.
Format the print line. PERFORM a para to print it (which is where the "page full" test will be).
Note: You can use VALUEs for your "/"s in the dates, or you can use the "/" editing character in the PICture, like this:
05 an-input-date PIC X(8) (can be other definitions).
...
05 date-to-print PIC X(4)/XX/XX.
...
MOVE an-input-date TO date-to-print
I like to see that you are using "minimal full-stops/periods". You can go a little further.
MOVE an-input-date TO date-to-print
.
Then you get your final full-stop/period in a paragraph, without having it "attached" to any particular line of code, which makes "tossing code around" easier, as you don't have to think "do I need/not need that full-stop/period there".
You could also look through some of the Cobol questions here, and get a handle on some general tips and advice.
This may or may not help, if LINAGE is not supported you'll have to do some explicit counting.
*****************************************************************
* Example of LINAGE File Descriptor
* Author: Brian Tiffin
* Date: 10-July-2008
* Tectonics: $ cobc -x linage.cob
* $ ./linage <filename ["linage.cob"]>
* $ cat -n mini-report
*****************************************************************
IDENTIFICATION DIVISION.
PROGRAM-ID. linage-demo.
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
select optional data-file assign to file-name
organization is line sequential
file status is data-file-status.
select mini-report assign to "mini-report".
DATA DIVISION.
FILE SECTION.
FD data-file.
01 data-record.
88 endofdata value high-values.
02 data-line pic x(80).
FD mini-report
linage is 16 lines
with footing at 15
lines at top 2
lines at bottom 2.
01 report-line pic x(80).
WORKING-STORAGE SECTION.
01 command-arguments pic x(1024).
01 file-name pic x(160).
01 data-file-status pic 99.
01 lc pic 99.
01 report-line-blank.
02 filler pic x(18) value all "*".
02 filler pic x(05) value spaces.
02 filler pic x(34)
VALUE "THIS PAGE INTENTIONALLY LEFT BLANK".
02 filler pic x(05) value spaces.
02 filler pic x(18) value all "*".
01 report-line-data.
02 body-tag pic 9(6).
02 line-3 pic x(74).
01 report-line-header.
02 filler pic x(6) VALUE "PAGE: ".
02 page-no pic 9999.
02 filler pic x(24).
02 filler pic x(5) VALUE " LC: ".
02 header-tag pic 9(6).
02 filler pic x(23).
02 filler pic x(6) VALUE "DATE: ".
02 page-date pic x(6).
01 page-count pic 9999.
PROCEDURE DIVISION.
accept command-arguments from command-line end-accept.
string
command-arguments delimited by space
into file-name
end-string.
if file-name equal spaces
move "linage.cob" to file-name
end-if.
open input data-file.
read data-file
at end
display
"File: " function trim(file-name)
" open error or empty"
end-display
go to early-exit
end-read.
open output mini-report.
write report-line
from report-line-blank
end-write.
move 1 to page-count.
accept page-date from date end-accept.
move page-count to page-no.
write report-line
from report-line-header
after advancing page
end-write.
perform readwrite-loop until endofdata.
display
"Normal termination, file name: "
function trim(file-name)
" ending status: "
data-file-status
end-display.
close mini-report.
* Goto considered harmful? Bah! :)
early-exit.
close data-file.
exit program.
stop run.
****************************************************************
readwrite-loop.
move data-record to report-line-data
move linage-counter to body-tag
write report-line from report-line-data
end-of-page
add 1 to page-count end-add
move page-count to page-no
move linage-counter to header-tag
write report-line from report-line-header
after advancing page
end-write
end-write
read data-file
at end set endofdata to true
end-read
.
*****************************************************************
* Commentary
* LINAGE is set at a 20 line logical page
* 16 body lines
* 2 top lines
* A footer line at 15 (inside the body count)
* 2 bottom lines
* Build with:
* $ cobc -x -Wall -Wtruncate linage.cob
* Evaluate with:
* $ ./linage
* This will read in linage.cob and produce a useless mini-report
* $ cat -n mini-report
*****************************************************************
END PROGRAM linage-demo.