Cobol - End terminator for write? - cobol

In Cobol, Is it required that we put at end-write after a write statement. For example consider the following:
write something from something after advancing 1 lines.
end-write.
Do we need to do it like the above? Or is just this fine:
write something from something after advancing 1 lines.
I don't get any compiler warnings when I compile without the end-write unlike when I don't use end-read or end-if I get compiler warnings.
Could someone help me understand this?

The END-WRITE is only required when INVALID KEY or NOT INVALID KEY are used.
In no other circumstance do you need it. Although you can always use it if you want to.
I don't use INVALID KEY/NOT INVALID KEY, and never use END-WRITE (or END-READ, END-OPEN, END-CLOSE, END-START). Instead I use the FILE STATUS clause on the SELECT statement, and test the FILE STATUS field after each IO operation.
If you are getting used to writing modern COBOL (to the 1985 Standard) then you should avoid as many full-stops/periods in the PROCEDURE DIVISION as you can. After the PROCEDURE DIVISION header. After a paragraph/SECTION label. Before a paragraph/SECTION label. At the end of the program if not already used for another reason.
For the one before a paragraph/SECTION label, I include it full-stop/period on a line of its own, in column 12.

Related

Getting current line number in Cobol

Is it possible to get and display the current line number in the Cobol program?
For example, C allows do it by the next way:
...
printf("Current line = %d\n", __LINE__);
...
Short answer: No.
There is no portable COBOL way in doing this, especially not in all places like __LINE__ does.
Long answer with potential alternatives:
COBOL 2002 added intrinsic functions for exception handling. Using these you can get the location where the last error happened, which checks are activated.
You could hack something by raising a non-fatal exception and ideally in the same line use that function...
From the standard:
The EXCEPTION-LOCATION function returns an alphanumeric character string, part of which is the implementor-defined location of the statement associated with the last exception status.
So this may provide you with the line number, as the returned value depends on the implementation, additional it seems that - at the time of writing - neither IBM nor MicroFocus nor Fujitsu compilers support that intrinsic function at all.
The GnuCOBOL implementation returns a semicolon-separated list with the last entry being the line number.
The upcoming COBOL standard added the MODULE-NAME intrinsic function - but this will only give the name, not the line reference.
If you are free to choose which implementation you use, then an addition of an extra register COB_SOURCE_LINE / COB_SOURCE_FILE in GnuCOBOL should be relative easy to add...
If the intend is a tracing of some kind: many compilers have an extension READY TRACE/ RESET TRACE. With those two statements (and possibly compiler directives / options) they will at least show the name of sections and paragraphs reached, some may also show the line number. Often this could be redirected to a file and will otherwise go to the default error stream.
If you use GnuCOBOL and compile with -ftrace-all you can also use that for line or statement tracing with self-defined format as specified in COB_TRACE_FORMAT [which can also be adjusted within the COBOL program and limited to the line number].
Q: Is it possible to get and display the current line number in the Cobol program?
There was a feature through COBOL 85 called the DEBUG module. The feature was made obsolete in COBOL 85 and subsequently removed in COBOL 2002. While DEBUG lines were available in the 2002 standard, the DEBUG module was removed from the standard.
NOTE: The DEBUG module may still be available in current compilers.
The feature requires debugging mode in the source-computer paragraph. If the line is removed, source lines with a D or d in column 7 are treated as comments.
Declaratives must be added to access debug-line which is the standard name for the source line number.
I have coded the source such that the source line number of wherever I place perform show-line will be displayed. Notice that show-line doesn't do anything.
Source:
program-id. dbug.
environment division.
source-computer. computer-name debugging mode.
object-computer. computer-name.
data division.
working-storage section.
01 char pic x.
procedure division.
declaratives.
ddebug section.
duse for debugging show-line.
d display "Source-line: " debug-line.
end declaratives.
main-line.
begin.
display "Before"
d perform show-line
display "After"
accept char
stop run.
dshow-line.
end program dbug.
Each implementor has their own means for activating the feature. For the system I use, it's a switch parameter (+D) on the command line. Without the switch parameter the line number will not show. (For GnuCOBOL 3.2 it is, apparently, the environment variable COB_SET_DEBUG with a value of 'Y', 'y' or '1'. ;-))
Command line:
dbug (+D)
Display:
Before
Source-line: 17
After

Multiple exit causing compiler warnings

I have a program where under several conditions I would want to exit early, rather than continuing through the flow, and rather than having to check for that exit-early condition in calling paragraphs.
To do this, I have a paragraph "EXIT-FAILURE", that checks to make sure that the general return flag field is not ok (0), logs a message (DISPLAY), and finally has the statement GOBACK.
However, doing this is giving me a compiler warning on every PERFORM that calls this "EXIT-FAILURE" paragraph:
IGYCB7310-W The "PERFORM" statement at "PERFORM (line [line-number])" cannot reach its exit.
Is there any way to have this logic (which is basically multiple-exit/early-exit rather than single-exit), without having a compiler warning?
Is this idea entirely contrary to the COBOL way of doing things (my experience is more in Java, where this would be entirely normal in the context of guard statements or exceptions)?
EDIT: Adding minimal program requested by Simon:
IDENTIFICATION DIVISION.
PROGRAM-ID. SOQUEST.
ENVIRONMENT DIVISION.
DATA DIVISION.
PROCEDURE DIVISION.
PERFORM A100-INITIALIZE
PERFORM A200-VALIDATE
PERFORM B200-PROCESS-STEP-1
GOBACK
.
A100-INITIALIZE.
DISPLAY "INITIALIZED"
.
A200-VALIDATE.
PERFORM Z900-EXIT-FAILURE
.
B200-PROCESS-STEP-1.
DISPLAY "COMPLETED STEP 1"
.
Z900-EXIT-FAILURE.
GOBACK
.
Results in these two warnings related to my question:
IGYCB7310-W The "PERFORM" statement at "PERFORM (line 58.1)" cannot reach its exit.
IGYCB7310-W The "PERFORM" statement at "PERFORM (line 68.1)" cannot reach its exit.
(line 58.1 maps to the line "PERFORM A200-VALIDATE"; line 68.1 maps to the line "PERFORM Z900-EXIT-FAILURE")
As seen in the compiler warning and the additional explanation from the compiler manual, the issue is that you PERFORM something and PERFORM says "do this and then come back".
If Enterprise COBOL for z/OS adds support for RAISE exception-name (and ideally user-defined exceptions) this would be the way to go (both being "COBOL" as requested in the question and "exceptional" like in java) and you'd place the paragraph into DECLARATIVES as EXIT-FAILURE SECTION. USE AFTER EXCEPTION CONDITION exception-name. Until then [= maybe forever]:
If there's no rule against this on-site: use GO TO EXIT-FAILURE - this COBOL verb says "go there" (and likely don't come back, especially with a well named paragraph as in your case).
If there's a rule against GO TO - go with the approach from #cschneid - add a comment in the header about this warning and reference this comment directly where it happens with another comment.
Side-note: I personally would still try to put the paragraph into DECLARATIVES (unchanged, as it is now, just move it "up" to DECLARATIVES) to stretch the point "this is only called if something goes wrong" even more. But your compiler may raise another warning or even error in this case (at least "standard"-COBOL requires a use-statement there).
My reaction to this compiler warning would be to add a comment in the source indicating that the warning is expected. IBM Enterprise COBOL 6.3 (the latest release as of this date) does not support the RAISE statement.
It's not unlike PERFORMing a paragraph that does an EXEC CICS RETURN.
#SimonSobisch knows more about COBOL than I ever will, and will hopefully supply an example of how to solve this more in keeping with the "COBOL way" which will be useful to future seekers of knowledge here.
Using EXIT PARAGRAPH can help to avoid Go TO, compilation warnings, and comments ...
IDENTIFICATION DIVISION.
PROGRAM-ID. SOQUEST.
ENVIRONMENT DIVISION.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 FLAGS.
03 WS-VALIDATION PIC 9 VALUE ZERO.
88 WS-VALIDATION-OK VALUE 0.
88 WS-VALIDATION-ERROR VALUE 1.
PROCEDURE DIVISION.
MAIN.
PERFORM A100-INITIALIZE
PERFORM A200-VALIDATE
IF WS-VALIDATION-ERROR
EXIT PARAGRAPH
END-IF
PERFORM B200-PROCESS-STEP-1
.
CLOSE-FILES.
CLOSE XXXX
CLOSE YYY
.
EXIT-PROGRAM.
GOBACK
.
A100-INITIALIZE.
DISPLAY "INITIALIZED"
.
A200-VALIDATE.
* Do next when validation fails ...
MOVE 1 TO WS-VALIDATION
ANY-VALIDATION-HERE
IF ERROR-FOUND
EXIT PARAGRAPH
END-IF
CONTINUE-WITH-OTHER-VALIDATIONS
IF ERROR-FOUND
EXIT PARAGRAPH
END-IF
* Arriving here .. MEANS VALIDATION IS ok
MOVE O TO WS-VALIDATION
.
B200-PROCESS-STEP-1.
DISPLAY "COMPLETED STEP 1"
.
Try the following:
Z900-EXIT-FAILURE.
IF <some condition that is always true>
GOBACK
END-IF
.
As long as the compiler optimizer cannot determine the fact that the IF condition is always true, it won't raise the warning message.
A response to comment below and a few other suggestions...
Compiler optimization in some future release may determine the condition to be always true and remove it:
This would cause the warning message to return. Face that problem when it occurs. For the present,
something like: IF FUNCTION WHEN-COMPILED <= FUNCTION CURRENT-DATE
will not be optimized away, and probably won't be for many years to come.
May produce less efficient code:
The whole point of this paragraph is to exit the program. The
extra instructions needed for the IF test should not have a measurable affect
on performance.
Disable the diagnostic:
This can be done using a compiler exit to catch and
nullify the message, see: https://www.ibm.com/support/knowledgecenter/en/SS6SG3_6.3.0/pg/ref/rpext10.html .
I would caution against this because valid warnings may also be suppressed, some of which
probably should not be ignored.
Put a comment in the code indicating the warning is acceptable:
Not all programmers are so diligent as to continue reviewing compiler warnings once
they find out they are sometimes acceptable. Chances are good that valid warnings will be missed
in the future.
Using DECLARATIVES:
The IBM Enterprise COBOL compiler supports DECLARATIVES for I/O related errors
and debugging only making their usage fairly restrictive. Furthermore, there are a number of additional restrictions as to whether a STOP RUNor GOBACK
may be issued while a DECLARATIVE procedure is active. I would hesitate to advise using DECLARATIVES.
Language Environment provides facilities to
establish user defined condition handling but this is a fairly advanced topic.
see: https://www.ibm.com/support/knowledgecenter/en/SSLTBW_2.1.0/com.ibm.zos.v2r1.ceea800/ceea824.htm
Use GO TO Z900-EXIT-FAILURE:
GO is recognized by the compiler as a go-there-and-don't-come-back transfer of control
and will not issue message IGYCB7310-W provided the GO TO is conditionally
executed (e.g. contained within an IF or other conditional statement). This is probably the best solution provided
local coding standards permit
usage of GO TO in these circumstances. Some places have an unjustified pathological fear of GO TO
and won't allow it under any circumstances.

How to negate a variable?

I have the below Working-storage variable in my program.
01 W-WRK.
02 W-MNTH-THRSHLD PIC S9(04).
I am using the below COMPUTE function to negate the value of W-MNTH-THRSHLD.
COMPUTE W-MNTH-THRSHLD OF W-WRK =
W-MNTH-THRSHLD OF W-WRK * -1.
I want to know if this approach is right or is there any alternative for the same?
Firstly, why are you using qualification (the OF)? That is only required if you have defined duplicate names. Why define duplicate names in the WORKING-STORAGE?
Secondly, unless you are using a very old COBOL compiler, you should only use the minimum required full-stops/periods in the PROCEDURE DIVISION. That is, one to terminate the paragraph/SECTION label, one to terminate a paragraph/SECTION. One to terminate the PROCEDURE DIVISION header. One to terminate a program (if a full-stop/period is not already there. Keeping extra full-stops/periods around makes it more difficult to copy code around. Put the full-stop/period on a line of its own, so no line of code has one, then you can't accidentally terminate a scope by copying a line of code with a full-stop/period to within a scope.
With those in mind, your code becomes:
COMPUTE W-MNTH-THRSHLD = W-MNTH-THRSHLD
* -1
Multiplication is slower than subtraction. So as Bruce Martin suggested:
COMPUTE W-MNTH-THRSHLD = 0
- W-MNTH-THRSHLD
I do it like this:
SUBTRACT W-MNTH-THRSHLD FROM 0
GIVING W-MNTH-THRSHLD-REV-SIGN
I dislike "destroying" a value just for the heck of it. If the program fails, I know what W-MNTH-THRSHLD contained, plus the meaningful name for the target field explains what the line does.
You could also DIVIDE (or / in COMPUTE), but that is even slower than MULTIPLY (or *).
Also bear in mind that conversions may be required, because you are doing arithmetic with a USAGE DISPLAY field. If you define your field as BINARY or PACKED-DECIMAL conversion is less likely for arithmetic. You won't lose by doing that, unless your compiler can deal with a USAGE DISPLAY in arithmetic without requiring conversion.
Note also, COMPUTE is not a function. COMPUTE is a verb, just a part of the language. "I am using COMPUTE" is sufficient, and not even necessary, as we can see that from the code.

Cobol Read statement format. Can it be redone a different way?

On this read statement below couldn't it be made into the form of the following? I think personaly it might be easier to understand and read.
Basically the read statement is simply reading through a file and making decision on which paragraph performs should be executed. It probably is difficult to see exaclty what the program does but what I was really interested to know was if you can change the read to a different format as listed below.
READ DATA-FILE
AT END
...do some code
NOT AT END
...do code that is below
END-READ.
Class:
INIT-READ.
READ C AT END
GO TO EOJ.
IF C-ID = ' ' AND C-S = 'P'
GO TO I-R
END-IF.
IF CID = ' ' AND C-S = 'D'
IF F = 'Y'
MOVE 'N' TO F
MOVE 'your text here' TO RPT-ID
MOVE A TO H6
MOVE B TO H7
PERFORM PA THRU H-A-X
END-IF
PERFORM WD-CLAIM THRU W-X
GO TO I-R
END-IF.
PERFORM N-V THRU N-V-X.
At the bottom of a discussion thread, captured in the GNU Cobol FAQ, http://opencobol.add1tocobol.com/gnucobol/#performing-forever Roger While mentioned a pretty nice idiom for READ control without paragraphs.
One thing that I saw on earlier posts to
the newsgroup cobol was
What is the need/justification for an
empty inline perform group.
ie.
PERFORM
...
END-PERFORM
None of the discussions then realized that
there is a -
EXIT PERFORM [CYCLE]
Therefore, it is a method to to
define an exit condition without having paragraphs.
ie. (very simply)
PERFORM
READ xxx
AT END
EXIT PERFORM
END-READ
MOVE something TO somewhere
END-PERFORM
.. test xxx status and somewhere
There are, of course, other variations.
Basically, it means that you code without
using section/paragraphs.
(Recommended, if only from performance point of view)
Note that the CYCLE option offers interesting possibilities.
Roger
Have a look here. COBOL read/store in table. for a way of avoiding GO TO, and even the AT END/NOT AT END. No END-READ needed either.
To avoid the AT END/NOT AT END, simply use the File Status, which you should already be using anyway to check that all IO operations were successful. For an input file, READ will give a file status of 10 when end-of-file is detected.
Use an 88 on your file status. So you can say things like END-OF-PAYMENTS-TRANSACTIONS.
Then, to process your file, you use a "priming read". This is a read outside the loop.
priming read
processing-loop until END-OF-PAYMENTS-TRANSACTIONS
do the processing
read
EXIT PERFORM (and some other EXIT options) is not available in all COBOLs currently. Be aware that if you are part of a large team and put an EXIT PERFORM in your program you will likely find several EXIT PERFORMs in the same block of code within a couple of years. So they may have well been GO TOs all along. The new EXIT options are just a way of have a GO TO which is spelled differently. OK, a little tongue-in-cheek, but there we go.
Of course, the priming read and read above both PERFORM a single paragraph to do the actual read and check the validity of the io (file status zero or 10 is OK, else a problem).
Be careful about considering avoiding PERFORM paragraph/SECTION for "performance". Write for clarity unless performance is critical. With IBM's Enterprise COBOL, using OPT, PERFORM code can be "inlined" anyway.

How do I prevent a Cobol program from going into an infinite loop if no match found on 2 flat files?

A Cobol program reads a record from a first flat file and compares it to the first record on a second flat file. However, because the first record from the first flat file does not match any records on the second flat file, the Cobol program goes into an infinite loop. How do I fix it?
Smells like a logic error somewhere in the program. Hard to say
what that might be. But I do have a few ideas...
Possible causes of an infinite loop:
Failure to check end-of-file conditions
Not reacting to the end-of-file properly
Testing for end-of-file but assuming all other conditions are 'successful' reads
End of file is sometimes determined by testing the File Status after each
I/O operation. File Status is an optional 2 character data item associated with the file being
read/written. It is specified
in the FILE-CONTROL paragraph of your program. For example:
SELECT file-name ASSIGN TO dd-name FILE STATUS fstatus
where: file-name is the name you refer to in OPEN/READ/WRITE/CLOSE statements. dd-name
is the external file name (the DDNAME from your JCL). fstatus is a two character data item declared
under WORKING-STORAGE.
The File Status is set on every file I/O operation. For example:
READ file-name
sets the fstatus to end-of-file if there were no more records to read. Note that the File Status
variable is not actually referenced on the READ, but it is set.
File Status values are two characters and are defined in the ISO COBOL standard, they should
be the same for all COBOL implementations. The exception being File Status values where the first
character is a '9', these are implementation dependant. Here is a link to the IBM Enterprise
COBOL File Status values
The value for end-of-file is: '10' - which should be the same for all COBOL implementations.
It is my guess that your program has a File Status for each of the input files, but is not checking it or reacting to it
appropriately. For example, your program may only check for end-of-file but not other conditions:
IF fstatus = '10'
PERFORM END-OF-FILE-LOGIC
ELSE
PERFORM NORMAL-LOGIC
END-IF
The problem with this approach is that it treats normal returns (fstatus = '00') and all non-end-of-file error
conditions as if the READ was successful. Better to have something like:
EVALUATE fstatus
WHEN '10'
PERFORM END-OF-FILE-LOGIC
WHEN '00'
PERFORM NORMAL-LOGIC
WHEN OTHER
PERFORM UNEXPECTED-ERROR
END-EVALUATE
There is an imperative form of the READ statement that specifies what to do when the end of file
is reached. It goes something like:
READ file-name AT END PERFORM END-OF-FILE-LOGIC END-READ
Again, if a File Status was specified in the FILE-CONTROL section for file-name and a non-end-of-file
error occurred, your program would attempt to continue with 'normal' logic - exactly the wrong thing to be
doing.
At end-of-file, the last record read stays in the record area. So if you don't check for an end-of-file situation, your program will not stop running by itself.
For a typical sequential file match at the end of each loop you need to read one of more records.
If first key >= second key then read from the second file.
if second key > first key then read from the first file.
There are many variations depending on the one to n relationship between the two files. However you must read something at the end of the loop!

Resources