I am using OpenCOBOLIDE and trying to just simply call a subroutine. I am following along in a TutorialPoint document. I followed all the instructions but cannot seem to figure out why when I call the subroutine it still cannot find the module I am trying to link to.
Here is my main file:
IDENTIFICATION DIVISION.
PROGRAM-ID. MAIN.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-STUDENT-ID PIC 9(4) VALUE 1000.
01 WS-STUDENT-NAME PIC A(15) VALUE 'Tim'.
PROCEDURE DIVISION.
CALL 'UTIL' USING WS-STUDENT-ID, WS-STUDENT-NAME.
DISPLAY 'Student Id : ' WS-STUDENT-ID
DISPLAY 'Student Name : ' WS-STUDENT-NAME
STOP RUN.
And here is my subroutine:
IDENTIFICATION DIVISION.
PROGRAM-ID. UTIL.
DATA DIVISION.
LINKAGE SECTION.
01 LS-STUDENT-ID PIC 9(4).
01 LS-STUDENT-NAME PIC A(15).
PROCEDURE DIVISION USING LS-STUDENT-ID, LS-STUDENT-NAME.
DISPLAY 'In Called Program'.
MOVE 1111 TO LS-STUDENT-ID.
EXIT PROGRAM.
I am using OpenCobolIDE-4.6.5 if that helps.
The message I get is:
MAIN.cbl:16: libcob: Cannot find module 'UTIL'
If you look at the COBOL option in the menu-bar at the top of the screen select Program type you'll see two options: Executable; Module.
For the program which is started from the OS, your MAIN, you need that to be set to Executable. For the CALLed program, UTIL, you need that to be set to Module.
You should also try to install GnuCOBOL, which is the new name for OpenCOBOL. GnuCOBOL is actively maintained. The GnuCOBOL site is currently at SourceForge.Net. There is a recent discussion in the Help getting started as to exactly how to do that (for Ubuntu, anyway, if you are using something more different, post a question and you'll get assistance).
Disclosure: I am a Moderator there.
You should be aware that the concept of "main" does not actually exist in COBOL itself, not in the way of other languages you may know. Exactly how the initial program operates is down to implementation and operating system.
On Linux/Unix/Windows the initial program is compiled differently, so it is more like a "main". On an IBM Mainframe it is not compiled differently.
Also note that a subroutine can CALL another subroutine. You would need to compile both as Module in your case. It is not COBOL dictating that, but the OS and the implementation.
If learning COBOL, be modern about it. In the PROCEDURE DIVISION do not attach a full-stop/period to a line of code, and only use them where they are necessary, not where they are optional. In earlier COBOL Standards the full-stop/period was the only scope-terminator available, and for backwards-compatibility it still acts as a "super-scope-terminator" which is usually not what you want, and if it were to be what you want, it is bad practice as any other reader would consider it an error and wonder what you had really intended.
A full-stop/period is needed: to terminate the PROCEDURE DIVISION statement; to terminate a paragraph or SECTION label; to terminate a paragraph or SECTION itself; to terminate a program.
If using a COPY or REPLACE compiler-directive, you also need to terminate those with a full-stop/period.
Your subroutine does not contain a GOBACK, RETURN or EXIT PROGRAM. Even if that were to work, it would be non-Standard, non-portable and would not be a good way to learn COBOL.
In the real-world of COBOL programming you won't/may never find a PIC A used. It is considered of little benefit, as all it does is prevent you using that field as a source and a numeric field as a target in the same statement. Using a PIC X instead of PIC A is what you will normally see, so you may as well start now (despite what any tutorial may say).
That's an opinion, and you've no need to follow that advice, but in practice that is the way it is.
Using commas (or any other non-relevant separator) in COBOL statements only clouds a program. They have no genuine value. This is valid:
PROCEDURE DIVISION USING LS-STUDENT-ID, , , , , LS-STUDENT-NAME.
So what would be the point?
There is no benefit in defining a field as numeric simply because it contains a number. You are never going to do a calculation with the student-id, so it is much better for it to be PIC X not PIC 9.
independantly of Bill's excellent remarks, I did face the same problem with OpenCobolIDE today. And found a solution.
You have to parameter the paths of the .dll created so that the caller programm can find the called module. When you compile [F8] your module, you have a message that tells you where the dll has gone :
Compilation succeeded (output: C:\Users\Mariah Flaim\bin\MyModule.dll)
Now, you have to go to the parameters [F2], to the compiler tab, and add a link to this path in the "Library path" list. And, how miracle, the call happens!!!
A very simple example, just to try, I've made work after this manoeuver, starting from their templates(would need more work to be clean, but it works).
Calling program :
IDENTIFICATION DIVISION.
*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
PROGRAM-ID. YOUR-PROGRAM-NAME.
ENVIRONMENT DIVISION.
*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
CONFIGURATION SECTION.
*-----------------------
INPUT-OUTPUT SECTION.
*-----------------------
DATA DIVISION.
*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
FILE SECTION.
*-----------------------
WORKING-STORAGE SECTION.
01 PARAMETRES.
02 PA-RETURN-CODE PIC 99 VALUE 0.
*-----------------------
PROCEDURE DIVISION.
*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
MAIN-PROCEDURE.
**
* The main procedure of the program
**
CALL "MYMODULE"
USING PARAMETRES
DISPLAY "Hello world"
STOP RUN.
** add other procedures here
END PROGRAM YOUR-PROGRAM-NAME.
Called Module :
IDENTIFICATION DIVISION.
*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
PROGRAM-ID. MYMODULE.
ENVIRONMENT DIVISION.
*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
CONFIGURATION SECTION.
*-----------------------
INPUT-OUTPUT SECTION.
*-----------------------
DATA DIVISION.
*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
FILE SECTION.
*-----------------------
WORKING-STORAGE SECTION.
*-----------------------
LINKAGE SECTION.
**-*-*-*-*-*-*-*-*-*-*-*-*-*
01 PARAMETRES.
02 PA-RETURN-CODE PIC 99 VALUE 0.
PROCEDURE DIVISION USING PARAMETRES.
*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
MAIN-PROCEDURE.
**
* The main procedure of the program
**
DISPLAY "Glu"
MOVE 0 TO PA-RETURN-CODE
GOBACK.
** add other procedures here
END PROGRAM MYMODULE.
Output :
Glu
Hello world
Related
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
I'm very new to COBOL. I'm following the tutorials that came with Micro Focus and I can't seem to get the example to work right. I'm trying to print -123.45 and I keep getting the following,
I looked up a number of posts on here and they don't address my problem. I'm using Micro Focus' Visual COBOL in Eclipse. Here's my code,
program-id. tictac as "tictac".
environment division.
configuration section.
data division.
working-storage section.
01 WS-NUM3 PIC S9(3)V9(2) VALUE -123.45.
procedure division.
Display WS-NUM3.
goback.
end program tictac.
The V in your picture clause is an implied decimal point. I think you want a field with PIC -999.99, where the . is an explicit decimal point, for its picture clause.
You might think of this as COBOL making a distinction between how a variable is defined and how it is shown. Sort of like a format string in printf is just specifying how to show a variable, not how it is defined. Though in both cases the definition and how it is shown have to match up to a certain extent.
Choosing the right picture clause for a numeric field is important; if you're doing calculations it can have a significant performance impact.
So it's common to have a field with a definition such as...
PIC 9(4)V99 COMP-3
...and a corresponding field for output purposes such as...
PIC ZZZ9.99
...so that computations can be done on the first, and when displaying the field is necessary one uses a MOVE statement to copy the contents of the first to the second.
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.
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.