Simple question that I cannot find an answer to through web-searching.
Is it valid to have a field in a COBOL copybook without a name? i.e. is the following valid?
05 SUMMARY.
07 DETAILS OCCURS 3 TIMES
PIC X(10).
07 PIC X(100).
Is this the same as say using FILLER instead of the name? If not, what is the difference between a blank name and using FILLER?
Is this the same as say using FILLER instead of the name?
Yes it is. The requirement to use FILLER for an unreferenced data item was removed in COBOL85.
Using one or the other is mostly a "style" issue, commonly there will be a "shop/team rule" about what to use.
Note: this is not strictly related to a copybook, it is the same when used in any data description entry in the program's source.
For documentation (I assume you meant an IBM mainframe here) see the appropriate entry in the IBM COBOL Language Reference.
Related
I have been looking around to find information on COBOL directives that come with vanilla PeopleSoft COBOLs.
I can find information for directives during the unicode conversion process but nothing else.
Specifically I am interested in MVSRND, what does it do?
Here's an example use:
MVSRND 01 WK-HPREC.
MVSRND 02 WK-HIGH-PREC PIC S9(12)V9(6) COMP-3.
It looks like "MVSRND" (which in the sequence area), is a tag for MVS RANDOM support.
Which might be related to the use of the function random/CEERAN0 or some other api.
I have a fairly basic Cobol program I'm using to learn about record structures. I'm noticing strange behaviour with gnucobol when passing signed numeric values as part of the record in an ACCEPT statement.
The program is defined as follows:
IDENTIFICATION DIVISION.
PROGRAM-ID. TEST.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 Account.
02 Name PIC X(5) VALUE SPACES.
02 Balance PIC S9999V999 VALUE ZEROES.
PROCEDURE DIVISION.
MAIN.
DISPLAY "Enter account details:"
ACCEPT Account.
DISPLAY "Balance is:"
DISPLAY Balance.
STOP RUN.
The behaviour is as follows:
Enter account details:
AAAAA-123.456
Balance is:
+-123.045
I assume this is due to how the value is stored in raw memory.
Is this generally what most cobol compilers do? Is there a way to get cobol interpreting the signed value properly?
I assume this is due to how the value is stored in raw memory.
yes
Is this generally what most cobol compilers do?
a guess: yes (the actual display will vary, but I'm sure most COBOL environments won't do what you seem to want them, at least this way)
Is there a way to get cobol interpreting the signed value properly?
Yes, but there are "COBOL" things to do:
store data in internal format (like you did: signed value with implied 3 decimal positions), but for ACCEPT and likely also DISPLAY use a format that actually has the data as you want, for example a PIC +ZZZ9.999$, for some details look at this answer
never ACCEPT a record, either split into multiple ACCEPT or use a single one with accepting a screen-name, not a record name --> use the SCREEN SECTION for entering the data, this will provide you with two separate fields and with most COBOL environments reasonable input validation.
I'm attempting to output the following row using DISPLAY and am getting the correct result in Micro Focus COBOL in Visual Studio and the Tutorialspoint COBOL compiler, but something strange when running it on a z/OS Mainframe using IBM's Enterprise COBOL:
01 W05-OUTPUT-ROW.
05 W05-OFFICE-NAME PIC X(13).
05 W05-BENEFIT-ROW OCCURS 5 TIMES.
10 PIC X(2) VALUE SPACES.
10 W05-B-TOTAL PIC ZZ,ZZ9.99 VALUE ZEROS.
05 PIC X(2) VALUE SPACES.
05 W05-OFFICE-TOTAL PIC ZZ,ZZ9.99 VALUE ZEROS.
It appears in Enterprise COBOL that the spaces are being ignored, and is adding an extra zero-filled column even though the PERFORM VARYING and DISPLAY code is the exact same in both versions:
PERFORM VARYING W02-O-IDX FROM 1 BY 1
UNTIL W02-O-IDX > W12-OFFICE-COUNT
MOVE W02-OFFICE-NAME(W02-O-IDX) TO W05-OFFICE-NAME
PERFORM 310-CALC-TOTALS VARYING W02-B-IDX FROM 1 BY 1
UNTIL W02-B-IDX > W13-BENEFIT-COUNT
MOVE W02-O-TOTAL(W02-O-IDX) TO W05-OFFICE-TOTAL
DISPLAY W05-OUTPUT-ROW
END-PERFORM
W13-BENEFIT-COUNT is 5 and never changes in the program, so the 6th column is a mystery to me.
Correct output:
Strange output:
Edit: as requested, here is W02-OFFICE-TABLE:
01 W02-OFFICE-TABLE.
05 W02-OFFICE-ROW OCCURS 11 TIMES
ASCENDING KEY IS W02-OFFICE-NAME
INDEXED BY W02-O-IDX.
10 W02-OFFICE-CODE PIC X(6).
10 W02-OFFICE-NAME PIC X(13).
10 W02-BENEFIT-ROW OCCURS 5 TIMES
INDEXED BY W02-B-IDX.
15 W02-B-CODE PIC 9(1).
15 W02-B-TOTAL PIC 9(5)V99 VALUE ZERO.
10 W02-O-TOTAL PIC 9(5)V99 VALUE ZERO.
and W12-OFFICE-COUNT is always 11, never changes:
01 W12-OFFICE-COUNT PIC 99 VALUE 11.
The question is not so much "why does Enterprise COBOL do that?", because it is documented, as "why do those other two compilers generate programs that do what I want?", which is probably also documented.
Here's a quote from the draft of what became the 2014 COBOL Standard (the actual Standard costs money):
C.3.4.1 Subscripting using index-names
In order to facilitate such operations as table searching and
manipulating specific items, a technique called indexing is available.
To use this technique, the programmer assigns one or more index-names
to an item whose data description entry contains an OCCURS clause. An
index associated with an index-name acts as a subscript, and its value
corresponds to an occurrence number for the item to which the
index-name is associated.
The INDEXED BY phrase, by which the index-name is identified and
associated with its table, is an optional part of the OCCURS clause.
There is no separate entry to describe the index associated with
index-name since its definition is completely hardware oriented. At
runtime the contents of the index correspond to an occurrence number
for that specific dimension of the table with which the index is
associated; however, the manner of correspondence is determined by the
implementor. The initial value of an index at runtime is undefined,
and the index shall be initialized before use. The initial value of an
index is assigned with the PERFORM statement with the VARYING phrase,
the SEARCH statement with the ALL phrase, or the SET statement.
[...]
An index-name may be used to reference only the table to which it is
associated via the INDEXED BY phrase.
From the second paragraph, it is clear that how an index is implemented is down to the implementor of the compiler. Which means that what an index actually contains, and how it is manipulated internally, can vary from compiler to compiler, as long as the results are the same.
The last paragraph quoted indicates that, by the Standard, a specific index can only be used for the table which defines that specific index.
You have some code equivalent to this in 310-CALC-TOTALS: take a source data-item using the index from its table, and use that index from the "wrong" table to store a value derived from that in a different table.
This breaks the "An index-name may be used to reference only the table to which it is associated via the INDEXED BY phrase."
So you changed your code in 310-CALC-TOTALS to: take a source data-item using the index from its table, and use a data-name or index defined on the destination table to store a value derived from that in a different table.
So your code now works, and will give you the same result with each compiler.
Why did the Enterprise COBOL code compile, if the Standard (and this was the same for prior Standards) forbids that use?
IBM has a Language Extension. In fact two Extensions, which are applicable to your case (quoted from the Enterprise COBOL Language Reference in Appendix A):
Indexing and subscripting ... Referencing a table with an index-name
defined for a different table
and
OCCURS ... Reference to a table through indexing when no INDEXED BY
phrase is specified
Thus you get no compile error, as using an index from a different table and using an index when no index is defined on the table are both OK.
So, what does it do, when you use another index? Again from the Language Reference, this time on Subscripting using index-names (indexing)
An index-name can be used to reference any table. However, the element
length of the table being referenced and of the table that the
index-name is associated with should match. Otherwise, the reference
will not be to the same table element in each table, and you might get
runtime errors.
Which is exactly what happened to you. The difference in lengths of the items in the OCCURS is down to the "insertion editing" symbols in your PICture for the table you DISPLAY from. If the items in the two tables were the same length, you'd not have noticed a problem.
You gave a VALUE clause for your table items (unnecessary, as you would always put something in them before the are output) and this left your "sixth" column, the five previous columns were written as shorter items. Note the confusion caused when the editing is done to one length and the storing done with a different implicit length, you even overwrite the second decimal place.
IBM's implementation of INDEXED BY means that the length of the item(s) being indexed is intrinsic. Hence the unexpected results when the fields referenced are actually different lengths.
What about the other two compilers? You'd need to hit their documentation to be certain of what was happening (something as simple as the index being represented by an entry-number (so plain 1, 2, 3, etc), and the allowing of an index to reference another table would be enough). There should be two extensions: to allow an index to be used on a table which did not define that index; to allow an index to be used on a table where no index is defined. The two logically come as a pair, and both only need to be specific (the first would do otherwise) because the are specifically against the Standard.
Micro Focus do have a Language Extension whereby an index from one table may be used to reference data from another table. It is not explicit that this includes referencing a table with no indexes defined, but this is obviously so.
Tutorialspoint uses OpenCOBOL 1.1. OpenCOBOL is now GnuCOBOL. GnuCOBOL 1.1 is the current release, which is different and more up-to-date than OpenCOBOL 1.1. GnuCOBOL 2.0 is coming soon. I contribute to the discussion area for GnuCOBOL at SourceForge.Net and have raised the issue there. Simon Sobisch of the GnuCOBOL project has previously approached Ideaone and Tuturialspoint about their use of the out-dated OpenCOBOL 1.1. Ideaone have provided positive feedback, Tutorialspoint, who Simon has again contacted today, nothing yet.
As a side-issue, it looks like you are using SEARCH ALL to do a binary-search of your table. For "small" tables, it is likely that the overhead of the mechanics of the generalised binary-search provided by SEARCH ALL outweighs any expected savings in machine resources. If you were to be processing large amounts of data, it is likely that a plain SEARCH would be more efficient than the SEARCH ALL.
How small is "small" depends on your data. Five is likely to be small close to 100% of the time.
Better performance than SEARCH and SEARCH ALL functionality can be achieved by coding, but remember that SEARCH and SEARCH ALL don't make mistakes.
However, especially with SEARCH ALL, mistakes by the programmer are easy. If the data is out of sequence, SEARCH ALL will not operate correctly. Defining more data than is populated gets a table quickly out of sequence as well. If using SEARCH ALL with a variable number of items, consider using OCCURS DEPENDING ON for the table, or "padding" unused trailing entries with a value beyond the maximum key-value that can exist.
I'd be very hesitant about mixing VALUE with OCCURS and re-code the WS as
01 W05-OUTPUT-ROW.
05 W05-OFFICE-NAME PIC X(13).
05 W05-BENEFITS PIC X(55) VALUE SPACES.
05 FILLER REDEFINES W05-BENEFITS.
07 W05-BENEFIT-ROW OCCURS 5 TIMES.
10 FILLER PIC X(02).
10 W05-B-TOTAL PIC ZZ,ZZ9.99.
05 FILLER PIC X(02) VALUE SPACES.
05 W05-OFFICE-TOTAL PIC ZZ,ZZ9.99 VALUE ZEROS.
Perhaps it has something to do with the missing fieldname?
Ah! evil INDEXED. I'd make both ***-IDX variables simple 99s.
01 COUNTER.
03 DIGITS1 OCCURS 40 TIMES PIC 9.
03 STRING1 REDEFINES DIGITS1 pic X(40).
That compiles fine in Micro Focus Visual COBOL 2.3 in Visual Studio 2015. It gives an error in GnuCOBOL, viz
The original definition should not have OCCURS
Why the difference and what do I do to have an array of digits that can also be viewed as a string of digits?
It is either a COBOL Language Extension in Micro Focus Visual COBOL 2.3, or it is a bug there.
Indeed, locating some Micro Focus documentation reveals:
OSVS MF The data description for data-name-2 can contain an OCCURS
clause.
OSVS and MF indicate what the Language Extension relates to. MF is Micro Focus, OSVS I assume is for OS/VS COBOL on an IBM Mainframe. I used that a lot, but since I don't code like that I can't say whether it worked like that. OS/VS COBOL was to the 1974 Standard.
Locating an old (1975) manual for OS/VS COBOL, here is the defintion of REDEFINES:
level number data-name-l REDEFINES data-name-2
That also serves for the Micro Focus quote.
Here is the relevant part from that OS/VS COBOL manual:
The data description entry far data-name-2 cannot contain an OCCURS
clause
So I'm not so sure that OSVS in the Micro Focus document is OS/VS COBOL. I know the icons are listed somewhere...
From a draft of the 2015 Standard (it is expensive to obtain the actual Standard):
The data description entry for data-name-2 shall not contain an OCCURS
clause. However, data-name-2 may be subordinate to an item whose data
description entry contains an OCCURS clause. In this case, the
reference to data-name-2 in the REDEFINES clause shall not be
subscripted. Neither the original definition nor the redefinition
shall include an occurs-depending table.
The 1985 Standard as it relates to the REDEFINES of an OCCURS is the same.
This should do for you:
01 COUNTER.
03 DIGITS1 OCCURS 40 TIMES PIC 9.
Just use COUNTER instead of STRING1 (I hope those names are just for this example, not the real ones).
COUNTER is a group item, which is treated as an alpha-numeric item with a total length equal to the sum of the lengths of all its subordinate items.
You want STRING1 to be a 40-byte PIC X field, when you already have one: COUNTER.
I always code my tables like this:
01 FILLER.
05 FILLER PIC something.
05 FILLER.
10 FILLER OCCURS 40 TIMES.
15 FILLER PIC something.
05 FILLER PIC something.
OK, conceptually I do that. Then, for each level of an OCCURS that I actually require, I give it a name. If there are items outside the OCCURS structure I don't need, I delete them.
With this, if you need to REDEFINES the group which contains the OCCURS, no problem. If you need to REDEFINES the OCCURing item, no problem. You can't REDEFINES (or do much useful) with the item actually containing the OCCURS, so it remains a FILLER.
This is for "maintainability". The next person along will never have to alter the structure of the OCCURS, so never have to worry when making a change. With the short-hand structure, a future change may require a reorganisation, which then has to be considered for impact.
If your table had been coded that way, there would not have been a problem anyway.
Consult your Micro Focus documentation for the REDEFINES, they usually indicate Language Extensions directly there. If there is not a Language Extension that you can locate, raise an issue with Micro Focus. They will point you to the documentation of the Extension, or do something else constructive with it.
Micro Focus the company has consumed some "small system" commercial compilers, and provides a lot of support to allow a migration path from those compilers to straight Micro Focus ones. My guess would be that you'll find a Language Extension linked to that.
Incidentally, it is also non-Standard to be able to REDEFINES a smaller item as a larger item. DIGITS1 has a length of one. Your REDEFINES item a length of 40. However, the Micro Focus REDEFINES is allowing you to redefine the entire length of the OCCURS.
By error, do you mean the warning?
identification division.
program-id. redef.
data division.
working-storage section.
01 COUNTER.
03 DIGITS1 OCCURS 40 TIMES PIC 9.
03 STRING1 REDEFINES DIGITS1 pic X(40).
procedure division.
move 1 to DIGITS1(2)
display ":" STRING1 ":"
goback.
end program redef.
with
prompt$ cobc -xj redef.cob
redef.cob: 8: Warning: The original definition 'DIGITS1' should not have OCCURS
:0100000000000000000000000000000000000000:
prompt$ cobc --version
cobc (GNU Cobol) 2.0.0
Copyright (C) 2001,2002,2003,2004,2005,2006,2007 Keisuke Nishida
Copyright (C) 2006-2012 Roger While
Copyright (C) 2009,2010,2012,2014,2015 Simon Sobisch
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Built Nov 02 2015 05:58:19
Packaged Oct 25 2015 21:40:28 UTC
C version "4.9.2 20150212 (Red Hat 4.9.2-6)"
So, that's GnuCOBOL 2.0.
prompt$ cobc -x redef.cob
redef.cob:8: Warning: The original definition 'DIGITS1' should not have OCCURS
prompt$ cobc --version
cobc (GNU Cobol) 1.1.0
...
prompt$ ./redef
:0100000000000000000000000000000000000000:
Looks like it works with 1.1 as well.
I can't get rid of the warning though.
How do I create a table from a copybook in Cobol?
The copybook has several groups in it and only need one of them to be in a table.
Do I need to use the copybook multiple times replacing the start of variables so that the compiler views them as different things?
I can edit the copybook as long as the other programs using it will compile.
It is on an IBM Mainframe.
See my answer below for my final solution. Thanks to #Bruce Martin
You say that you can't change the copybook. Well, the copybook can be changed in such a way that it affects nothing else.
This is an example from the Enterprise COBOL Language Reference:
Example 3
If the following conventions are followed in library text, then parts of names (for
example the prefix portion of data names) can be changed with the REPLACING
phrase.
In this example, the library text PAYLIB consists of the following data division
entries:
01 :TAG:.
02 :TAG:-WEEK PIC S99.
02 :TAG:-GROSS-PAY PIC S9(5)V99.
02 :TAG:-HOURS PIC S999 OCCURS 1 TO 52 TIMES
DEPENDING ON :TAG:-WEEK OF :TAG:.
The programmer can use the COPY statement in the data division of a program as
follows:
COPY PAYLIB REPLACING ==:TAG:== BY ==Payroll==.
Usage Note: It is important to notice in this example the required use of colons or
parentheses as delimiters in the library text. Colons are recommended for clarity
because parentheses can be used for a subscript, for instance in referencing a table
element.
In this program, the library text is copied; the resulting text is treated as if it had
been written as follows:
01 PAYROLL.
02 PAYROLL-WEEK PIC S99.
02 PAYROLL-GROSS-PAY PIC S9(5)V99.
02 PAYROLL-HOURS PIC S999 OCCURS 1 TO 52 TIMES
DEPENDING ON PAYROLL-WEEK OF PAYROLL.
The changes shown are made only for this program. The text, as it appears in the
library, remains unchanged.
So, for that 01 within the existing copybook, you can replace the data definitions after the 01 with a COPY ... REPLACING ... to give the same prefix (assuming the data-names have prefixes....) and then create your new copybook with adjusted level-numbers if needed (for instance, the example show level-numbers 02, which is always silly, but this is probably not the only example of that in the world). The different level numbers will not affect any existing code (as the compiler normalises all level numbers anyway, so the compiler will always treat the lowest level number after the 01 as 02, the second-lowest as 03, etc).
Then you can use your new copybook in your table.
Be aware that you will have to use subscripting to reference any fields in the table.
If you really cannot change the copybook (some odd diktat, happens at times) then perhaps the best bet would be to make a new copybook which is the same, but with different prefixes, and without the 01-level, for flexibility.
We now need the purpose of storing the records.
It seems that you have something like this:
copy reclyout.
01 record-layout-1.
...
01 record-layout-2.
...
01 record-layout-3.
...
And with that, you want to store the record-layout-2 records, only, in a table.
As Bruce Martin asked, knowledge of which compiler and OS you are using would be useful. Some, not all, COBOL compilers support nested copy statements. You could replace the layout of your record in the original copybook with a copy statement for a new copybook which contained the layout.
You'd have a minor issue that you'd need the 01 itself to be outside the copybook, and you'd need to allow a sufficient gap in the level-numbers to allow for your table definition to include the new copybook.
01 The-Table.
05 some-name OCCURS 100 TIMES.
copy newlyout.
The highest-level data definition(s) in the copybook would have to begin with level-numbers greater than 05. This is not much of a problem to achieve. COBOL compilers will "normalise" the level-numbers anyway, and the chance of you making something less flexible by doing this is almost nil. It is your best solution if your compiler supports nested copy statements.
Yes, using COPY with REPLACING would be very useful. It is always ugly to have to use qualification of data-names (or labels).
If not, consider doing the same thing, but removing that particular layout from the existing copybook and simply including the new copybook after the original copy statement(s). Whether you are able to do this will depend on how much the copybook is used elsewhere. Take it to your analyst/boss.
If that is not possible, make a new copybook for the table, and use comments and other documentation available to you to establish a relationship between the two data-definitions. Not ideal, but a common way that it is done.
Another possibility is to simply define areas within the table, and use the record-layout, via a MOVE to the record-layout. This is another common way, which does need documentation and checks for the lengths in the table/record-layout and is an ungainly/inefficient way to do it. Again, you'll come across that way, probably.
If you cough on the compiler/OS there are some other ways as well.
The IBM's mainframe supports nested copybooks, so you can change your copybook to a
nested copybook
Also have a look at
"COPY" statement with "REPLACING" in COBOL
so
01 record-layout-1.
...
01 record-layout-2.
...
01 record-layout-3.
...
can be changed to
01 record-layout-1.
...
01 record-layout-2.
...
01 record-layout-3.
copy newCopy.
...
and in your program you can use the newCopy or what ever you call it. You will probably want to renumber copybook levels while you are at it.
So if the original copybook is
01 record-layout-3.
05 field-1 pic x(4).
05 field-2 ...
you would create the new copybook as
25 field-1 pic x(4).
25 field-2 ...
The actual level numbers are not important, they just need to be > 01 for the copybook. Using 25 will make it easy to embed in your working storage.
A few companies make it a standard- copybooks must not contain 01 levels so that copybooks can also be embedded in Working storage. This is rare though
My final solution.
I actually ended not having to create a table from copybooks but still needed the 01 groups variables to be their own copybooks to create multiple instances of variables with the same structure just with different 01 group names and variable names.
I ended up creating individual copybook books for the 01 groups variables, and then using a Replace block to make the nested copybook look like the original. See below for final result.
REPLACE ==(TAGEIGHT)== BY ==N==.
01 STD-NACHA-FILE-HEADER.
COPY ABRYNAFH.
01 STD-NACHA-BATCH-HEADER.
COPY ABRYNABH.
01 STD-NACHA-DETAIL-TRANS.
COPY ABRYNADT.
01 STD-NACHA-DETAIL-ADDENDA-REC.
COPY ABRYNADA.
01 STD-NACHA-BATCH-TOTAL-REC.
COPY ABRYNABT.
01 STD-NACHA-FILE-TOTAL-REC.
COPY ABRYNAFT.
01 STD-NACHA-FILE-PAD-REC.
COPY ABRYNAFP.
01 STD-NACHA-RETURN-REC.
COPY ABRYNARR.
REPLACE OFF.
I went here for how to use Replace within a copybook.