I am trying to pass to a C function the variable names from a cobol program.
01 Message.
03 varA PIC X(32).
03 varB PIC X(32).
Consider the fact that this function will be used in many programs and the structure of variable Message will be different everytime , how can i pass to the C function the names of the variables?
I allready consider making another group data item to contain the variable names, but this is not a good solution for me.
I am using Microfocus Server Express v5.1 on AIX.
how can i pass to the C function the names of the variables?
It is not possible to pass a variable name, directly, with CALL USING; at least, not one that would be better than a group data item.
What I did here is to create a JSON-like object using a STRING statement with a REPLACE statement to pass a name/value pair. I chose this method because there are many open source libraries that may be called from C to decode JSON objects. It also very flexible with regard to text lengths and the number of variables to be passed.
[Ed. Changed code to place all STRING statement data items in the REPLACE statement. Changed some names to reflect "name/value" pairings. Removed string length code to nested program.]
program-id. call-c.
data division.
working-storage section.
1 msg.
2 varA pic x(32) value "Message 1".
2 varB pic x(32) value "Message 2".
1 lengths binary.
2 len-1 pic 9(4).
2 len-2 pic 9(4).
replace
==name-1== by =="varA"==
==value-1== by ==varA(1:len-1)==
==name-2== by =="varB"==
==value-2== by ==varB(1:len-2)==
==newline== by ==x"0a"==
.
1 json-string pic x(256).
procedure division.
begin.
compute len-1 = function length (varA)
call "rtrim-len" using varA len-1
compute len-2 = function length (varB)
call "rtrim-len" using varB len-2
string
"{" newline
quote name-1 quote ": "
quote value-1 quote "," newline
quote name-2 quote ": "
quote value-2 quote newline
"}" x"00"
delimited size into json-string
call "c_prog" using
by reference json-string
stop run
.
program-id. rtrim-len.
data division.
linkage section.
1 str pic x(256).
1 str-len binary pic 9(4).
procedure division using str str-len.
begin.
perform varying str-len from str-len by -1
until str-len < 1 or str (str-len:1) not = space
continue
end-perform
exit program
.
end program rtrim-len.
end program call-c.
A COBOL program to substitute for a called C program.
program-id. "c_prog".
data division.
working-storage section.
1 json-string-count binary pic 9(4).
1 json-string-pos binary pic 9(4).
1 json-text-count binary pic 9(4).
1 json-text pic x(64).
linkage section.
1 json-string pic x(256).
procedure division using json-string.
begin.
move 0 to json-string-count
inspect json-string tallying
json-string-count for characters before x"00"
move 1 to json-string-pos
perform until json-string-pos > json-string-count
unstring json-string delimited x"0a" or x"00"
into json-text count json-text-count
pointer json-string-pos
display json-text (1:json-text-count)
end-perform
exit program
.
end program "c_prog".
Output:
{
"varA": "Message 1",
"varB": "Message 2"
}
If a TRIM function is available, the length calculations, data items, and nested program are not needed and the REPLACE statement becomes,
replace
==name-1== by =="varA"==
==value-1== by ==trim(varA)==
==name-2== by =="varB"==
==value-2== by ==trim(varB)==
==newline== by ==x"0a"==
.
Related
Trying to solve this using arrays.
Here's the problem:
Problem I'm facing: Has to be an easier way to loop through the booking types. When I view the output, it shows the customer number, customer name, and address several times with the same input which it shouldn't be.
Any help would be appreciated.
Here is the program below:
Process Apost.
Identification Division.
Program-ID. BOOKINGARR.
*
* Page 554 No 3. ARRAYS.
* Data in sequence by Client No. Print the average cost of
* trip for each booking type. Use arrays.
*
Environment Division.
Configuration Section.
Source-Computer. IBM-AS400.
Object-Computer. IBM-AS400.
Input-Output Section.
File-Control.
Select Input-File Assign to Database-Bookingpf.
Select Output-File Assign to Printer-Qsysprt.
Data Division.
File Section.
FD Input-File.
01 Input-File-Rec.
Copy DDS-BookingR of Bookingpf.
FD Output-File.
01 Output-File-Rec Pic x(120).
Working-Storage Section.
01 END-OF-FILE PIC X VALUE 'N'.
01 WS-ARRAY.
05 WS-TABLE-ENTRIES OCCURS 4 TIMES.
10 WS-TOTAL-COST PIC 9(7)V99.
10 WS-TRIP-COUNT PIC 999.
10 WS-AVG-COST PIC 9(7)V99 VALUE ZERO.
10 WS-BOOKING-TYPE PIC 9.
01 ARRAY-INDEX PIC 99.
01 EMPTY-POINTER PIC 99.
01 ARRAY-EMPTY PIC XXX.
01 PROGRAM-HEADER.
05 PIC X(2) VALUE SPACES.
05 PIC X(10) VALUE 'CLIENT NO.'.
05 PIC X(3) VALUE SPACES.
05 PIC X(11) VALUE 'CLIENT NAME'.
05 PIC X(6) VALUE SPACES.
05 PIC X(14) VALUE 'CLIENT ADDRESS'.
05 PIC X(4) VALUE SPACES.
05 PIC X(9) VALUE 'BOOK TYPE'.
05 PIC X(4) VALUE SPACES.
05 PIC X(12) VALUE 'AVERAGE COST'.
01 REPORT-LINE.
05 PIC X(2) VALUE SPACES.
05 CLIENTNO-OUT PIC 999.
05 PIC X(10) VALUE SPACES.
05 CLIENTNA-OUT PIC X(16).
05 PIC X(1) VALUE SPACES.
05 CLIENTADD-OUT PIC X(19).
05 PIC X(3) VALUE SPACES.
05 BOOKTYPE-OUT PIC Z.
05 PIC X(8) VALUE SPACES.
05 AVGCOST-OUT PIC $Z,ZZ9.99.
05 PIC X(12) VALUE SPACES.
Procedure Division.
000-MAIN.
OPEN INPUT INPUT-FILE
OUTPUT OUTPUT-FILE.
PERFORM 100-MOVE.
PERFORM 1000-READ.
PERFORM 300-UPDATE-BOOKINGS
UNTIL END-OF-FILE = 'Y'.
WRITE OUTPUT-FILE-REC FROM PROGRAM-HEADER.
PERFORM 600-WRITE-TO-SCREEN
VARYING ARRAY-INDEX FROM 1 BY 1
UNTIL ARRAY-INDEX > 4.
CLOSE INPUT-FILE, OUTPUT-FILE.
STOP RUN.
100-MOVE.
MOVE 1 TO EMPTY-POINTER.
MOVE 'Y' TO ARRAY-EMPTY.
MOVE SPACES TO UPDATE-DONE.
PERFORM 150-ZERO-OUT-ARRAY
VARYING ARRAY-INDEX FROM 1 BY 1 UNTIL
ARRAY-INDEX > 4.
150-ZERO-OUT-ARRAY.
MOVE ZEROS TO WS-BOOKING-TYPE (ARRAY-INDEX).
MOVE ZEROS TO WS-TOTAL-COST (ARRAY-INDEX).
MOVE ZEROS TO WS-TRIP-COUNT (ARRAY-INDEX).
MOVE ZEROS TO WS-AVG-COST (ARRAY-INDEX).
1000-READ.
READ INPUT-FILE AT END MOVE 'Y' TO END-OF-FILE.
300-UPDATE-BOOKINGS.
IF ARRAY-EMPTY = 'Y'
PERFORM 400-ADD-1-TO-COUNT
MOVE 'N' TO ARRAY-EMPTY
ELSE
MOVE 'N' TO UPDATE-DONE
PERFORM 500-GET-BOOKING-AVERAGE
VARYING ARRAY-INDEX FROM 1 BY 1
UNTIL ARRAY-INDEX = EMPTY-POINTER
OR
UPDATE-DONE = 'Y'.
IF UPDATE-DONE = 'N'
PERFORM 400-ADD-1-TO-COUNT.
PERFORM 1000-READ.
400-ADD-1-TO-COUNT.
MOVE BOOKTYPE TO WS-BOOKING-TYPE (EMPTY-POINTER).
ADD 1 TO WS-TRIP-COUNT (EMPTY-POINTER).
MOVE COSTOFTRIP TO WS-TOTAL-COST (EMPTY-POINTER).
MOVE COSTOFTRIP TO WS-AVG-COST (EMPTY-POINTER).
ADD 1 TO EMPTY-POINTER.
500-GET-BOOKING-AVERAGE.
IF BOOKTYPE = WS-BOOKING-TYPE (ARRAY-INDEX)
ADD 1 TO WS-TRIP-COUNT (ARRAY-INDEX)
ADD COSTOFTRIP TO WS-TOTAL-COST (ARRAY-INDEX)
COMPUTE WS-AVG-COST (ARRAY-INDEX) =
WS-TOTAL-COST (ARRAY-INDEX) /
WS-TRIP-COUNT (ARRAY-INDEX)
MOVE 'Y' TO UPDATE-DONE.
600-WRITE-TO-SCREEN.
MOVE CLIENTNO TO CLIENTNO-OUT.
MOVE CLIENTNA TO CLIENTNA-OUT.
MOVE CLIENTADD TO CLIENTADD-OUT.
MOVE WS-BOOKING-TYPE (ARRAY-INDEX) TO BOOKTYPE-OUT.
MOVE WS-AVG-COST (ARRAY-INDEX) TO AVGCOST-OUT.
WRITE OUTPUT-FILE-REC FROM REPORT-LINE
AFTER ADVANCING 1 LINE.
I see you are using Stern & Stern.
The objective:
Print the average cost of a trip for each booking type. Use arrays.
means the output should contain only two columns and four rows, plus any header. For example,
Booking Type Average Cost
------------ ------------
Cruise ZZZZ9.99
Air-Independent ZZZZ9.99
Air-Tour ZZZZ9.99
Other ZZZZ9.99
To achieve that you will need to place the four descriptions in an array and accumulate the total cost and count, for each booking type, also in an array. After processing all the records, calculate the averages and print the results while looping through the array(s).
You tried to do much more than was requested!
The reason behind numbering COBOL paragraphs is so they are easier to find. In a program several thousand lines long it saves significant effort for the maintainer. You have located paragraph 1000 between paragraphs 150 and 300.
Modern COBOL programs usually have full stops at the end of a paragraph or section name and the end of a paragraph or section. Some people make the last line of a paragraph or section a CONTINUE or EXIT statement with a full stop instead of just a full stop on its own.
Modern COBOL programs use explicit scope terminators, particularly on IF statements, instead of full stops.
COBOL is criticized for being verbose; many COBOL programmers make this into a virtue by naming their paragraphs to indicate what is being done by the code contained therein. For example, 100-MOVE might be better named 100-INITIALIZE.
On Stack Overflow, I suggest you familiarize yourself with the "code sample" button, the "{}" rather than marking your paragraphs in bold.
If the point of the exercise is to compute the average cost of each type of booking, I suggest using the booking type as a subscript into an array. For each record you read, add the COSTOFTRIP to WS-TOTAL-COST(BOOKTYPE) and increment WS-TRIP-COUNT(BOOKTYPE). At end of file compute the average cost of a trip for each BOOKTYPE by using a COMPUTE for each element of the array inside a PERFORM VARYING loop.
For extra credit, verify BOOKTYPE is numeric before using it as a subscript and use an in-line PERFORM to compute the average.
Good afternoon.
I am writing a SAS program that will loop through several sets of time-series/ observations. For each set, there is one observation per month, with roughly 450 observations/months total. For simplicity's sake, the months start at 1 and move sequentially.
Now, for each set of observations I have an additional set of variables to be employed. I am importing an auxiliary data set that contains these variables for all of the sets, and using the &&var&i. structure to assign each observation's variables a unique macro variable to be called during the execution of the main loop. So, for example, all of the variables in the first observation have a "1" concatenated onto their variable name, second observation variables have a "2," and so on. When the main loop goes through it's first iteration and calls &&var&i., it will resolve to &var1 and pull in the value assigned from the first observation in the auxiliary data set. I have tested this, and it is working fine.
Important note: each observation in the auxiliary set has a series of variables called ratio_1, ratio_2, ... up to ratio_9. After passing through the macro assignment above, they would assume macro names of ratio_11, ratio_21... for the first set, and ratio_12, ratio_22,... and so on for subsequent sets.
My problem arises when I try to insert code that is only supposed to occur at very specific time intervals within each set. Each set has a variable initial_check that determines on which month this code should begin executing. This code should then execute on each observation that occurs in 12-month increments. So, for example, set 1 might have an initial_check value of 36, meaning that the code will only execute for the observation on month 35 (see code below), with subsequent executions on months 47, 59, 71, and so on.
The first line of code is meant to determine that the code that follows only executes at the aforementioned intervals (the rem_var checks for the remainder of the difference between the current month and the initial_check, over 12 - if there is no remainder, then a multiple of 12 months has passed) :
if mon >= %eval(&&initial_check&k -1) and rem_var = 0 and mon < &&term&k. then do ;
I have run that code in isolation to check that each of its parameters is doing what it should, and it appears to be working correctly. The following code comes next:
** Iterate ratios **
if mon = %eval(&&initial_check&k. -1) then call symput('j',1) ;
else if mon = %eval(&&initial_check&k. +11) then call symput('j',2) ;
else if mon = %eval(&&initial_check&k. +23) then call symput('j',3) ;
else if mon = %eval(&&initial_check&k. +35) then call symput('j',4) ;
else if mon = %eval(&&initial_check&k. +47) then call symput('j',5) ;
else if mon = %eval(&&initial_check&k. +59) then call symput('j',6) ;
else if mon = %eval(&&initial_check&k. +71) then call symput('j',7) ;
else if mon = %eval(&&initial_check&k. +83) then call symput('j',8) ;
else if mon = %eval(&&initial_check&k. +95) then call symput('j',9) ;
end ;
Again, I have tested this using non-macro language (that is, assigning the values to regular variable j), and this also appears to be working. Unfortunately, even with the "mprint" option on, I can't see if the macro variable is being properly assigned. Following that, I have additional code that is only supposed to execute if that first condition was met.
if &&ratio_&j&k ne 0 then do ;
And HERE is the issue: I'm getting a note that macro variable j is unresolved.
This code is only supposed to execute in an instance in which &j has been defined, so I can't figure out why it is unresolved. That &&ratio_&j&k is supposed to resolve to &ratio_11 in month 35, &ratio_21 in month 47, and so on for the first loop of the broader program.
I have tried experimenting with the macro versions of the conditional logic (%IF, %THEN, %DO), but have so far failed to get the results I want.
Would anyone happen to have any insight? I'm at my wit's end. I will be following this thread, so I can add details where necessary. And thank you in advance for taking the time to read this.
We need more information. You cannot include the last two blocks of code in the same data step since the data step will use the value of the macro variable J that exists when the data step is compiled and not the one generated by the call symput() function.
Why isn't J just a data step variable?
If it is a macro variable and you want to use the value that call symput() created then you need to use symget() (or symgetn()) to retrieve it at run time. You can then use its value to generate the name of the macro variable that you actually want to reference.
if symgetn(cats('ratio_',symgetn('j'),"&k")) ne 0 then do ;
I am making a program which got to split the phone-number apart, each part has been divided by a hyphen (or spaces, or '( )' or empty).
Exp: Input: 0xx-xxxx-xxxx or 0xxxxxxxxxx or (0xx)xxxx-xxxx
Output: code 1: 0xx
code 2: xxxx
code 3: xxxx
But my problem is: sometime "Code 1" is just 0x -> so "Code 2" must be xxxxx (1st part always have hyphen or a parenthesis when 2 digit long)
Anyone can give me a hand, It would be grateful.
According to your comments, the following regex will extract the information you need
^\(?(0\d{1,2})\)?[- ]?(\d{4,5})[- ]?(\d{4})$
Break down:
^\(?(0\d{1,2})\)? matches 0x, 0xx, (0xx) and (0x) at he beggining of the string
[- ]? as parenthesis can only be used for the first group, the only valid separators left are space and the hyphen. ? means 0 or 1 time.
(\d{4,5}) will match the second group. As the length of the 3rd group is fixed (4 digits), the regex will automatically calculate the length of the Group1 and 2.
(\d{4})$ matches the 4 digits at the end of the number.
See it in action
You can the extract data from capture group 1,2 and 3
Note: As mentionned in the comments of the OP, this only extracts data from correctly formed numbers. It will match some ill-formed numbers.
I have the following c code which calls a cobol program:
#include <stdio.h>
#include "libcob.h"
//#pragma linkage (verkoop, COBOL)
extern void VERKOOP(char *productid, char *aantal, char*resultaat);
main(int argc, char *argv[])
{
int return_status;
COB_RTD = cob_get_rtd();
char *productid = "20 ";
char *aantal = "000020";
char resultaat[30];
cob_init(rtd, 0, NULL);
printf("hallo");//prints
VERKOOP(productid, aantal, resultaat);
printf("hallo");//doesn't print
printf("resultaat:%s", resultaat);// doesn't print
cob_stop_run (rtd, return_status);
}
I'm using the printf to see if resultaat has been assigned correctly. However, both of the lines after VERKOOP don't print for some reason.
This is the COBOL code of VERKOOP(he does fill in LS-RESULTAAT correctly here, I tried it with DISPLAY and this part works):
*************************************************************
* VERKOOP
*************************************************************
IDENTIFICATION DIVISION.
PROGRAM-ID. VERKOOP.
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT PRODUCTEN ASSIGN TO "BESTANDEN/LIJSTPRODUCTEN"
ACCESS MODE IS RANDOM
ORGANIZATION IS INDEXED
RECORD KEY IS PRODUCTID
FILE STATUS IS WS-FILE-STATUS.
DATA DIVISION.
FILE SECTION.
FD PRODUCTEN BLOCK CONTAINS 10 RECORDS.
01 PRODUCT.
02 PRODUCTID PIC X(6).
02 LEVERANCIERID PIC X(6).
02 AANTAL PIC 9(6).
WORKING-STORAGE SECTION.
77 FOUT PIC X.
88 PRODUCT-NIET-GEVONDEN VALUE 1.
77 WS-PRODUCTID PIC X(6).
77 WS-AANTAL PIC 9(6).
77 WS-FILE-STATUS PIC XX.
77 WS-RESULTAAT PIC X(30).
LINKAGE SECTION.
01 LS-PRODUCTID PIC X(6).
01 LS-AANTAL PIC 9(6).
01 LS-RESULTAAT PIC X(30).
PROCEDURE DIVISION USING LS-PRODUCTID, LS-AANTAL, LS-RESULTAAT.
MAIN.
PERFORM INITIALISEER
PERFORM LEES-PRODUCT-IN
PERFORM LEES-BESTAND
PERFORM SLUIT-BESTAND
STOP RUN.
INITIALISEER.
OPEN I-O PRODUCTEN.
* DISPLAY WS-FILE-STATUS..
LEES-PRODUCT-IN.
MOVE LS-PRODUCTID TO WS-PRODUCTID
MOVE LS-AANTAL TO WS-AANTAL
MOVE 'OK' TO WS-RESULTAAT
* DISPLAY WS-RESULTAAT
MOVE WS-RESULTAAT TO LS-RESULTAAT.
* DISPLAY "GEEF PRODUCTID OP: "
* ACCEPT WS-PRODUCTID
* DISPLAY "GEEF AANTAL OP: "
* ACCEPT WS-AANTAL.
LEES-BESTAND.
* DISPLAY "LEES-BESTAND"
MOVE WS-PRODUCTID TO PRODUCTID
* DISPLAY PRODUCTID
* DISPLAY WS-FILE-STATUS
READ PRODUCTEN INVALID KEY SET PRODUCT-NIET-GEVONDEN TO TRUE
END-READ
IF PRODUCT-NIET-GEVONDEN PERFORM FOUTJE
ELSE
* MOVE WS-PRODUCTID TO PRODUCTID
SUBTRACT WS-AANTAL FROM AANTAL
PERFORM UPDATE-PRODUCT
END-IF.
UPDATE-PRODUCT.
REWRITE PRODUCT INVALID KEY PERFORM FOUTJE.
SLUIT-BESTAND.
* DISPLAY "SLUIT-BESTAND"
CLOSE PRODUCTEN.
FOUTJE.
DISPLAY "ER IS EEN FOUT OPGETREDEN"
DISPLAY WS-FILE-STATUS
STOP RUN.
UPDATE: I tried removing both "STOP RUN's", however now for some reason he substracts 40 instead of 20 and prints "ER IS EEN FOUT OPGETREDEN". So he is running the COBOL Program twice for some reason.
UPDATE: After replacing STOP RUN by GOBACK it works perfectly
Your STOP RUN is returning here, cob_stop_run (rtd, return_status), so your prior code after the invocation of the COBOL program does not run.
If EXIT PROGRAM is in a "main" program (at least in the case of the pragma-usage it seems that is what you have) then it is treated the same as STOP RUN.
GOBACK is returning control to where you expect it to.
If you use the COBOL-IT API, as has already been suggested, then perhaps the EXIT PROGRAM will behave differently. Perhaps not.
You are using an undocumented way to call a COBOL-IT program. Exactly how it behaves is not known, and cannot be known, to someone without COBOL-IT and the same operating system as you have, and the patience to do something in a non-obvious way.
If things are suggested which you then ignore, it is difficult to keep answering your questions.
Again, you have an Assignment. The Assignment expects you to use the API. You should use the API and get your programs working. If you have time later, you can look at the pragma-usage to effect the interoperability.
I am a new comer to programming in COBOL and I am having difficulty at something that probably should be trivial. I am wanting to find the min and max of what the user enters. When the user hits 0 the max, min, and avg should display. The avg is easy but the min and max had me wondering. If this was JAVA or another lang I'd just do some scenario with comparing MAX INT value. Unfortunately High-Value and Low-Value in COBOL are not Integer values???? So I decided to put the user's entries in a table and then use the intrinsic functions to do what I need. However as soon as I attempt to compute like this:
compute Min-Result = Function Min (Num-Field(ALL))
I get an error that says, "syntax error, unexpected all." I am totally confused at this point on what to do and why I get this error. I am using OpenCOBOL 1.1 Mingw. Here is my complete code. Any help would be greatly appreciated. Anything at all. I also made sure no lines were over 72.
identification division.
program-id. lab1a.
* no envionrment division since there are no files needed, etc.
data division.
working-storage section.
* declaring proper variables to store integer values
01 Max-Result PIC S9(5).
01 Min-Result PIC S9(5).
01 Count-Val PIC 9 Value 0.
01 Running-Tot PIC S9(10)v99.
01 First-Zero PIC 9 Value 1.
01 Final-Format-Avg PIC ZZZZZ9.9999.
01 Avg-Ent PIC S9(5)v9999.
01 Calc-Table.
03 Table-Record Occurs 1 to 500 times
depending on Entered-Num.
05 Num-Field PIC S9(5).
01 Entered-Num PIC S9(5).
procedure division.
000-Main.
perform with test after until Entered-Num = 0
display "Enter a 4-digit number (0 to stop): "
with no advancing
accept Entered-Num
add 1 to Count-Val
add Entered-Num to Running-Tot
display Running-Tot
display Count-Val
move Entered-Num to Num-Field(Count-Val)
* this way every time the user enters a non zero number it will be re-assigned
* to the variable Ending-Num. If they enter zero the if condition is skipped, the
* loop condition is tested at the top and is ended.
end-perform.
subtract 1 from Count-Val
display Count-Val
display " "
display " "
*WATCH FOR TRUNCATION ERROR.....
Divide Running-Tot By Count-Val Giving Avg-Ent
move Avg-Ent to Final-Format-Avg
*******WHY DOES THIS NOT WORK???????***********************
compute Min-Result = Function Min (Num-Field(ALL))
compute Max-Result = Function Max (Num-Field(ALL))
if First-Zero = 0
display "The first number you entered was zero.
& Next time enter a different one."
else
display "The lowest value entered: " Min-Result
display "The highest value entered: " Max-Result
display "The average value entered: "
Final-Format-Avg
end-if
stop run.
ALL is not currently supported for OpenCOBOL intrinsics, which is a feature that is on the the books for implementation.
You have "Entered-Num" as your Occurs Depending On field. Entered-Num by the time you use the function is zero. It should be Count-Val.
That's not the problem, but you asked.
Look at a 2009 OpenCobol Programmer's Guid, I can find no confirmation that ALL is supported.
It will be much simpler/faster to keep a "lowest value" and a "highest value" and compare to/replace as necessary with the entered number.