Why are my file definitions not working? [closed] - cobol

Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 8 years ago.
Improve this question
I am trying to figure out why my file definitions are incorrect. From what I can tell this is the same way that they were defined in my last program and all will be fixed block records
Code:
FILE-CONTROL.
SELECT INFILE1 ASSIGN TO UT-S-INPUT1.
SELECT INFILE2 ASSIGN TO UT-S-INPUT2.
SELECT OUTPUT-FILE1 ASSIGN TO UT-S-OUTPUT1.
SELECT OUTPUT-FILE2 ASSIGN TO UT-S-OUTPUT2.
DATA DIVISION.
FILE SECTION.
FD INFILE1.
BLOCK CONTAINS 0 RECORDS
RECORDING MODE IS F
LABEL RECORDS ARE STANDARD.
01 INF-LINE1.
05 STATE-1 PIC X(02).
05 KEY-1 PIC X(10).
05 TRANSID1 PIC X(10).
05 FILLER PIC X(58).
FD INFILE2.
BLOCK CONTAINS 0 RECORDS
RECORDING MODE IS F
LABEL RECORDS ARE STANDARD.
01 INF-LINE2.
05 KEY-2 PIC X(10).
05 TRANSID2 PIC X(10).
05 FILLER PIC X(60).
FD OUTPUT-FILE1
BLOCK CONTAINS 0 RECORDS
RECORDING MODE IS F
LABEL RECORDS ARE STANDARD.
01 OUTLINE1 PIC X(80).
FD OUTPUT-FILE2
BLOCK CONTAINS 0 RECORDS
RECORDING MODE IS F
LABEL RECORDS ARE STANDARD.
01 OUTLINE2 PIC X(80).
Error Code: Some of these error codes do not pertain to the file errors but I copied all of them and placed in this section.
20 IGYGR1216-I A "RECORDING MODE" of "F" was assumed for file "INFILE1".
21 IGYDS1089-S "BLOCK" was invalid. Scanning was resumed at the next area "A" item, level-number, or the start of the next
clause.
Same message on line: 31
30 IGYGR1216-I A "RECORDING MODE" of "F" was assumed for file "INFILE2".
283 IGYPA3043-E Data-item "INF-LINE1 (GROUP)" and record "INF-LINE1 (GROUP)" had overlapping storage. Movement of data may
not occur at execution time.
293 IGYPA3043-E Data-item "INF-LINE2 (GROUP)" and record "INF-LINE2 (GROUP)" had overlapping storage. Movement of data may
not occur at execution time.
304 IGYPS2015-I The paragraph or section prior to paragraph or section "X-COMP-FILE" did not contain any statements.
321 IGYPS2121-S "STUB4" was not defined as a data-name. The statement was discarded.

If you remove the period after the file name on our definition statement it should work without compiler errors.
Code:
FD INFILE1
BLOCK CONTAINS 0 RECORDS
RECORDING MODE IS F
LABEL RECORDS ARE STANDARD.
01 INF-LINE1.
05 STATE-1 PIC X(02).
05 KEY-1 PIC X(10).
05 TRANSID1 PIC X(10).
05 FILLER PIC X(58).
FD INFILE2
BLOCK CONTAINS 0 RECORDS
RECORDING MODE IS F
LABEL RECORDS ARE STANDARD.
01 INF-LINE2.
05 KEY-2 PIC X(10).
05 TRANSID2 PIC X(10).
05 FILLER PIC X(60).
This is a common mistake that happens when you look at a green screen all day long. I would recompile this and see if the other errors go away. It is always a good idea to recompile a program and then start fresh. Do you print the compile listing to scan through every-now-and-then. I would I think. The compile listing gives you all of the memory locations and the size of each record. It is very handy.
Code:
30 IGYGR1216-I A "RECORDING MODE" of "F" was assumed for file "INFILE2".
I am not certain on this error but I bet that you have the F a V for fixed block files. I would check this and if that is the case fix it. Do you start from scratch on programs or do you take a working program and then gut out what you do not need and then start working from that perspective. If this is the case you want to make certain that you have the recording mode set correctly for fixed block and variable block files.
321 IGYPS2121-S "STUB4" was not defined as a data-name. The statement was discarded.
The above error is simple that there is no working storage or variable named STUB4.
Hope this helps.

Related

Adding two integers giving unwanted result in cobol

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.

Is it possible to MOVE to a variable name created during runtime?

I am writing a program that changes the colour of a field if is it a duplicate record. To do this I am using a nested perform to compare each item to each other. When it finds a duplicate I would like to MOVE DFHRED to that specific field for example CRS1AC. My issue is that I don't know how to reference the field I am trying to change the colour of once I've found that is it a duplicate, how do I do this? Here are the fields which are in the MAP file that I am trying to move the colour to if a duplicate exists...
01 CRS1AC PIC X.
01 CRS1BC PIC X.
01 CRS2AC PIC X.
01 CRS2BC PIC X.
01 CRS3AC PIC X.
01 CRS3BC PIC X.
01 CRS4AC PIC X.
01 CRS4BC PIC X.
01 CRS5AC PIC X.
01 CRS5BC PIC X.
here is my table setup...
01 TABLES.
05 TBL-CRS-ENTRIES PIC S9(3) COMP-3 VALUE 5.
05 TBL-CRS-VALUES PIC X(4) OCCURS 10 TIMES.
05 CRS-TBL REDEFINES TBL-CRS-VALUES
PIC X(8) OCCURS 5 TIMES.
05 SUB-1 PIC S9(3) COMP-3 VALUE 1.
05 SUB-2 PIC S9(3) COMP-3 VALUE 1.
& here is the code that checks for duplicates
PERFORM VARYING SUB-1 FROM 1 BY 1 UNTIL SUB-1 > TBL-CRS-ENTRIES
PERFORM VARYING SUB-2 FROM 1 BY 1 UNTIL SUB-2 > SUB-1 - 1
IF CRS-TBL(SUB-1) = CRS-TBL(SUB-2)
*if there is a match it should change the colour to red.
* for example a match at CRS1AC & CRS1BC match CRS3AC & CRS3BC
*this is my attempt at trying make the variable name.
MOVE DFHRED TO CRS(SUB-1)AC
MOVE DFHRED TO CRS(SUB-1)BC
MOVE DFHRED TO CRS(SUB-2)AC
MOVE DFHRED TO CRS(SUB-2)BC
PERFORM 999-DUPLICATE-RECORD
END-IF
END-PERFORM
END-PERFORM.
GOBACK.
So, if 'PSYC 1000' = 'PSYC 1000' & the name of those fields are..
'CRS1AC+CRS1BC = CRS3AC+CRS3BC' <--- these are the fields I want to change the color of.
I've researched this heavily & still cannot find a solution.
Hope this makes sense I know its all over the place, for further clarification please ask & if complete program code is required I can provide it.
No, it is not possible to do what you want how you want.
COBOL is a compiled language. What is relevant about that is that you cannot make up the names of identifiers (variables) at run-time. Once a COBOL program is compiled, that is it, no more changes to the source for that executable version.
Where to go then?
You say this is part of your MAP.
01 CRS1AC PIC X.
01 CRS1BC PIC X.
01 CRS2AC PIC X.
01 CRS2BC PIC X.
01 CRS3AC PIC X.
01 CRS3BC PIC X.
01 CRS4AC PIC X.
01 CRS4BC PIC X.
01 CRS5AC PIC X.
01 CRS5BC PIC X.
But, because of those level-numbers of 01 those aren't part of anything, except the SECTION they belong to within the DATA DIVISION.
So you need to show your full, actual, MAP.
There is not an especially cute way, so what you have to do is REDEFINES the entire MAP, with an OCCURS which represents each screen-line for this portion of the screen (or entire screen, depends on the design) and define those attribute bytes within the OCCURS item.
What is really exciting about this is getting it all to line-up, by hand.
Now your program.
Your loops are wrong. You execute the inner-loop five times for each iteration of the outer-loop. Which means you are doing far more comparisons than necessary.
This is unwieldy:
SUB-2 > SUB-1 - 1
For that line of code, this would be better:
SUB-2 EQUAL TO SUB-1
The fifth outer-iteration is not required at all, because there are no values to compare to other than those which have already been compared (so the result will be the same, so why bother?).
Consider changing the definition of the identifiers (variables) you are using as subscripts and your total of entries to BINARY or COMP or COMP-4 (they all mean the same thing).
Consider giving them more meaningful names than SUB-1, SUB-2. It will make the code "read" better, which always helps.
Here's your data-defintion:
01 TABLES.
05 TBL-CRS-ENTRIES PIC S9(3) COMP-3 VALUE 5.
05 TBL-CRS-VALUES PIC X(4) OCCURS 10 TIMES.
05 CRS-TBL REDEFINES TBL-CRS-VALUES
PIC X(8) OCCURS 5 TIMES.
05 SUB-1 PIC S9(3) COMP-3 VALUE 1.
05 SUB-2 PIC S9(3) COMP-3 VALUE 1.
Some other things to make things easier for yourself:
Don't code identifiers (variables) after an OCCURS. It doesn't greatly matter in itself when there is no DEPENDING ON on the OCCURS, but since it does matter for that, why not be consistent (consistency is great, because when you are looking for a problem and you find inconsistency, it tells something about the writer of that particular bit of code).
Don't REDEFINES items which are defined with OCCURS. Even if you are using an old compiler (either you've put incorrect code here, or you are doing that) that gives you at least a Caution message. Try an experiment. Show that definition to a colleague, and ask them "what does this actually do?" Then show to another. And a third. If they all immediately reply, accurately, what it does, then you have a site with outstanding knowledge. You haven't, so don't do that.
Don't make identifiers signed, unless they can contain negative values. Then the reader can look at the definition and know it can't contain a negative value. Don't fall for "oh, we do it for performance". Name the compiler and show the compile options, and I'll show you what it is "gaining", or losing.
Don't use VALUE clauses for initial values of identifiers used as subscripts. Set them to their initial value before you use them. Unless you have tortuous code setting the value at the end of the current processing so it is ready for the next processing, all you are doing is confusing the human-reader. If there is a VALUE clause, it should be necessary.
Do give everything a good, descriptive, name, if it is being used. If something does not need a name, use FILLER to define it (often).
Your definition could be done like this:
01 some-meaningful-name
05 TBL-CRS-ENTRIES BINARY PIC 9(4) VALUE 5.
05 also-meaningful BINARY PIC 9(4).
05 meaningful-also BINARY PIC 9(4).
05 another-meaningful-name.
10 FILLER OCCURS 10 TIMES.
15 TBL-CRS-VALUES PIC X(4).
05 FILLER REDEFINES another-meaningful-name.
10 FILLER OCCURS 5 TIMES.
15 CRS-TBL PIC X(8).
Source code is for two things: for the compiler to convert into executable code; for the human reader to understand what has been coded. The compiler does not care about formatting the code. So format the code for the human reader. Even little things help:
IF CRS-TBL(SUB-1) = CRS-TBL(SUB-2)
Vs
IF CRS-TBL ( W-ORIGINAL-ITEM )
EQUAL TO CRS-TBL ( W-NEW-ITEM )

Report with Report Writer duplicating last line

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
.

Error using OCCURS in a very simple COBOL program

This is my code where iIam declaring a variable using OCCURS.
IDENTIFICATION DIVISION.
PROGRAM-ID. ARRAYEX.
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT StudentFile ASSIGN TO "STUDENTS.DAT"
ORGANIZATION IS LINE SEQUENTIAL.
DATA DIVISION.
WORKING-STORAGE SECTION.
77 WS-FIRSTARR OCCURS 10 TIMES PIC 9(5).
PROCEDURE DIVISION.
DISPLAY "HELLO WORLD".
STOP RUN.
Error:
arrayex.cbl:12: Error: Level 77 item 'WS-FIRSTARR' cannot have OCCURS clause
Any reasons the compilation fails?
A 77 cannot have OCCURS. An 01 cannot have OCCURS. Levels 02-49 can have OCCURS.
Forget level 66 (used for the RENAMES clause) as your should not use it and are unlikely to ever see it.
An 88-level, a Condition Name, cannot have an occurs, but if the field which it is defined on is part of an OCCURS or subordinate to an OCCURS, the 88 will need subscripting like any 02- - 49-level which is also part of, or subordinate to, an OCCURS.
01 a-simple-array-structure.
05 the-data occurs 5 times pic 9(5).
Or you can get more complex.
01 b-structure.
95 the-key pic x(8).
05 some-data pic x(10).
05 some-more-data pic 9(7).
05 a-simple-array.
10 a-simple-array-item occurs 5 times
pic 9(5).
05 a-more-complex-array.
10 complex-entry occurs 10 times.
15 some-complex-data pic xx.
15 another-bit-of-complex-data
pic 9(5).
OCCURS can also be used to define multi-dimensional tables.
05 first-occurs occurs 5 times.
10 second-occurs occurs 5 times.
15 an-item pic xx.
This is still far from the full gamut of OCCURS, so start simple, practice, get it working, become more complex.
There is also OCCURS DEPENDING ON, a variable-length table. One thing at a time. Get a simple OCCURS working, not just the definition, but the use as well, with a field for a subscript, an index for a subscript and a literal for a subscript.
Then get more complex.
COBOL has several "magic" level numbers...
Level-66 items indicate a RENAMES clause is to be expected
Level-77 items are atomic, cannot be subdivided, cannot have an OCCURS clause.
Level-88 indicates a condition-name entry
See pages 5-4 and 5-5 of the programmer's guide.

Field validation in COBOL/CICS

I'm editing some source code for my college Transaction Processing course. We're working with COBOL/CICS, and the program is a video tape rental system. We have a list of changes to make, and one item has me stuck (it's been since Fall semester of 2010 since I took the COBOL course, so unfortunately I'm far more rusty than I should be). There is a "customer maintenance" section, in which the user can add new customers. One of the items for a new customer is the zip code, and as it stands it will take any input as valid input, but we need to make it accept only numeric values (which I do know how to do) as well as a specific format: Either '12345', '123456789', or '12345-6789', and should only write to the record as '12345' or '12345-6789'. Anything else, such as '1234' or 12345-6' will result in an error. How do I check these fields for the proper format?
Since the valid data format is fixed, it is easy.
05 nice-name-for-zip-code pic x(10).
05 filler redefines nice-name-for-zip-code.
10 simple-zip-first-part pic x(5).
10 simple-zip-last-part pic x(5).
88 simple-zip-last-part-valid value space.
05 filler redefines nice-name-for-zip-code.
10 complex-zip-first-part pic x(5).
10 complex-zip-separator pic x.
88 complex-zip-separator value "-".
10 complex-zip-last-part pic x(4).
05 filler redefines nice-name-for-zip-code.
10 long-zip-first-part pic x(9).
10 long-zip-last-part pic x.
88 long-zip-last-part-valid value space.
if ( simple-zip-first-part numeric )
and ( simple-zip-last-part-valid )
....
if ( complex-zip-first-part numeric )
and ( complex-zip-separator-valid )
and ( complex-zip-last-part numeric )
....
if ( long-zip-first-part numeric )
and ( long-zip-last-part-valid )
....
If any of the IFs is true, you have a valid format. Otherwise, invalid.
A different approach might be to let CICS BMS support do most of the
validation and editing for you. This assumes you are using a 3270 type
terminal with CICS (which is probably the case)
Try setting the Zip Code up as a group field on the BMS map. This has the effect
of creating a single input field with multiple parts to it.
Your BMS Map definition would look something like:
ZIP1 DFHMDF POS=(2,1),LENGTH=5,GRPNAME=ZIP,ATTRB=(UNPROT,NUM)
SEP DFHMDF POS=(2,6),LENGTH=1,GRPNAME=ZIP,ATTRB=(ASKIP,NORM),INITIAL='-'
ZIP2 DFHMDF POS=(2,7),LENGTH=5,GRPNAME=ZIP,ATTRB=(UNPROT,NUM),JUSTIFY=(LEFT,BLANK)
The Zip code will appear at the beginning of line 2 (POS=(2..)). It will have a 5 digit input
field (ZIP1) for the first part of the Zip Code, followed by a hard coded input protected
dash (SEP) and another left justified 5
digit blank filled input field (ZIP2) for the last part of the Zip code.
From this point on, BMS will force the user to enter 5 digits into the first part of the Zip Code,
cannot touch the dash and optionally enter zero to 5 digits in the second part of
the input field. None of these fields will accept non-numeric data (except the SEP, which is input protected)
When you retrieve the data from the screen all you need to do is check to see
if ZIP2 is numeric to figure out if a long or short Zip code was entered. If
a long Zip, then store the whole thing, if short, only store ZIP1.
You could also use the CICS command BIF DEEDIT, which will remove non-numeric chars, the minus passes that test. After that, test for a length of 5 or 10.
Or, you could use an 88 like this:
01 Zip-Validation-Field.
02 filler pic x(5).
88 Zip-Valid value '00000' thru '99999'.
02 filler pic x(5).
88 Zip-plus-4-valid value '-0000' thru '-9999'.
And test with:
If Zip-Valid and Zip-plus-4-valid...
You can use MOVE CORR
01 TX-ZIPCODE PIC X(08) VALUE ' - '.
01 TX-ZIPCODE-R REDEFINES TX-ZIPCODE.
03 ZIPCODE-P1 PIC 9(04).
03 FILLER PIC X(01).
03 ZIPCODE-P2 PIC 9(03).
01 NUM-ZIPCODE PIC X(07).
01 NUM-ZIPCODE-R REDEFINES NUM-ZIPCODE.
03 ZIPCODE-P1 PIC 9(04).
03 ZIPCODE-P2 PIC 9(03).
MOVE CORR TX-ZIPCODE-R TO NUM-ZIPCODE-R.
IF NUM-ZIPCODE IS NOT NUMERIC
* ERRO
END-IF.
Hope I have help you! :)

Resources