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.
Related
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"==
.
So let's assume that I have a file which consists of 10 students with 3 fields: Name, Gender, Age. So, theoretically, I would want to create a 10 by 3 array.
But when it comes to COBOL, two-dimensional tables are created by this example:
01 WS-TABLE.
05 WS-A OCCURS 10 TIMES.
10 WS-B PIC A(10).
10 WS-C OCCURS 5 TIMES.
15 WS-D PIC X(6).
In this example, I can not understand what WS-B and WS-D are. If I want to create an array like the one I mentioned (10 by 3), how may I do so?
Thanks
First of all COBOL doesn't have arrays per-se it has tables. There is no way to make a 2-dimensional table. The example you have give is actually the closest you can get (a nested table). If I was faced with the problem you do (a field of 10 student with Name, Gender and Age) I would structure my data like this:
01 WS-TABLE.
05 WS-STUDENT OCCURS 10 TIMES.
10 WS-NAME PIC X(10).
10 WS-GENDER PIC X.
10 WS-AGE PIC 9(3).
In this example I would use a subscript to access the fields I have created for student. So this is what a loop to display them all would look like:
PERFORM VARYING WS-X
FROM 1 BY 1
UNTIL WS-X > 10
DISPLAY "NAME: " WS-NAME(WS-X) " GENDER: " WS-GENDER(WS-X) " AGE: " WS-AGE(WS-X)
END-PERFORM
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.
I'm trying to study NTLMv2 response, Eric Glass's work.
but stuck at HMAC-MD5 section. (using function like this)
I got the right NTLM hash, and unicode username & domain.
(he only emphasize the "USERDOMAIN" but no username, so I suppose it's "USER")
unsigned char v1hash[16]; // "0xcd06ca7c7e10c99b1d33b7485a2ed808"
unsigned short udata[14];
// concated unicode USER+USERDOMAIN "0x550053004500520044004f004d00410049004e00"
unsigned char v2hash[16];
int iLen;
HMAC(EVP_md5(), v1hash, 16, udata, sizeof(udata), v2hash, &iLen);
but the result is:
v2hash(16): 23 d2 3c a4 dd 1a 20 81 35 cf 3a 42 1c e1 5a 17
which should be "0x04b8e0ba74289cc540826bab1dee63ae"
am I doing something wrong here?
I'm not a C programmer but...
From my reading of that article USERDOMAIN should be just that - NOT USER+USERDOMAIN ie the uppercase username = "USER" concat with the target = "DOMAIN" to give USERDOMAIN which gives the unicode bytes beginning 55005300...
Then it would appear your udata array is the wrong size. You're only processing 20 bytes of information so don't set the array to 28 otherwise the HMAC/MD5 functions will be processing extra random data at the end of the input array.
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.