Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Guardian procedure call to return number of records for an Enscribe file

706 views
Skip to first unread message

paradiso

unread,
Sep 1, 2015, 10:44:03 PM9/1/15
to
Hey, Tandem SME,

Got a quick question about what Guardian Procedure call a COBOL program can use to return the number of records for an Enscribe file.

I have a cobol program written to compare 2 Enscribe files with the same file attributes when first created. Our application platform received a file from another platform on a daily basis, and we switched the current processed file with the newly received file as long as the EOF difference between 2 files are within a pre-set acceptable percentage. The goal is to ensure both current and new files are "identical" if not way too much different. To achieve the goal, I was using Guardian procedure call, FILE_GETINFOLISTBYNAME, to return EOF value of the files (item code 191).

A recent issue occurred on a situation where both files had the same number of records, but their EOF was more than 50% off (and exceeding the default compare percentage). The EOF difference was caused by the slack space. That makes me realize, instead of comparing the EOF b/t 2 files, I should really compare the number of records b/t those 2 Enscribe files.

My question is, is there any Guardian procedure call I can use for a cobol program to compare the number of records between 2 Enscribe files? I look through all item codes for FILE_GETINFOLISTBYNAME, but did not see any item code that returns the number of records.

Any insight into this question will be greatly appreciated!!

Keith Dick

unread,
Sep 2, 2015, 1:51:52 AM9/2/15
to
No, there is no Guardian procedure that returns the number of records in an Enscribe file.

You could read the file from beginning to end and count the records, or you could have the COBOL program run FUP, have it use the "INFO <filename>,STAT" command to get a number of statistics about the file, one of which is the number of records. The FUP approach would run quicker, but if you must do it from within a COBOL program, it probably would take you a lot longer to implement. If the files are relatively small, reading the file and counting the records probably would be the better approach.

The FUP INFO STAT implementation makes use of knowledge of the layout of the data in the file to run faster. It still reads the file and counts the records, but it reads the file via an unstructured open, and counts the records in each block of the file. In principle, you could do the same, but I wouldn't recommend it.

If you are free to get the record counts in some TACL code rather than doing it in a COBOL program, using a FUP INFO STAT command probably would be the best approach. It would be very easy to write some TACL code to examine the output of a FUP INFO STAT command and pick out the record count (it is the third field in the line whose first field is "DATA"). If you aren't sure how to do that, ask for suggestions.

If you must have the record counts in the COBOL program to use them for something in that program, perhaps you could write a TACL routine to get the record counts of the two files, then have the TACL routine run your COBOL program, and have the TACL pass the record counts as an input line to your program via INV or INLINE. The COBOL program could read those counts via an ACCEPT statement.

If, for some reason, passing the record counts via the standard input is not suitable, you could have the TACL put each record count into a PARAM and read the values of the PARAMs in the COBOL program using the Saved Message Utility procedures.

Any of those approaches that use some TACL code to get the record counts by running FUP would require modifying the COBOL program a bit, but the modifications would be much easier than running FUP from within the COBOL program, capturing the output of FUP, and parsing that output to find the record count. If you don't know how to use the Saved Message Utility procedures to get the value of a PARAM, they are documented in the COBOL manual. You can ask about them in this newsgroup if you have trouble using them.

wbreidbach

unread,
Sep 2, 2015, 5:44:14 AM9/2/15
to
What is the real goal, comparing 2 files if they are identical or just comparing the number of records? If both files contain the same number of records, the contents could be completely different.
We have a compare program that is able to compare 2 files with a bunch of different options. The program ends with a returncode, in case any differences are found, a warning is issued, in case the number of differences exceeds a given value, an error is issued.
This program is available, of course without any warranty.
So a way would be: Start this program from your COBOL program and if the program returns no error, the files are identical.
Just drop an email, if you would like to have the program. The program is written in TAL and can be compiled using the native compilers like EPTAL. The package contains the source and objects in code 100 and 800. In addition it contains 2 routines retrieving the binder timestamp of the actual object.

paradiso

unread,
Sep 2, 2015, 8:02:45 PM9/2/15
to
Hey, Keith,

First, let me express my sincere appreciation for good Samaritans like you and wbreidbach. Because of you - Tandem gurus, who are willing to share your expertise and experience with those who are in need, our work and life are just getting easier.

I have been a reader for this newsgroup. Occasionally, I read the posts and found some useful knowledge I can apply at work. Just wanna let you know how much it means to me to have people like you willing to take time to answer questions on this group.

Great mind thinks alike. Yes, your suggestion is one of the options I consider. We have a Tech Support team running NDM on CA utility to facilitate file transfers between HPNS and another platform. Upon receiving the new file, it also captured the number of records of the new file. So, by taking your advice, all we need is to have Tech Support team to add one two new params on the CA jobs to capture the new file's record counts and another param to house the current file record counts by invoking FUP INFO STAT command on the current file. Our Tech Support team has been implementing TACL command within the CA job, so they are familiar with adding new params onto the job.

When I posted this question, I was suspecting I might overlook the item code from FILE_GETINFOLISTBYNAME that might actually return the number of records. Since our cobol program has been coded to use this procedure call, I was wondering if I can simply use the item code to achieve my goal.

With our Tech Support team adding new params to capture both new & current file records counts, I'll be able to use SMU to retrieve the param values passed from our CA jobs.

Thanks again, Keith, for taking time to write up your recommendation.

Nella Fantasia

Paradiso

paradiso

unread,
Sep 2, 2015, 8:14:59 PM9/2/15
to
On Tuesday, September 1, 2015 at 10:44:03 PM UTC-4, paradiso wrote:
Hey, wbreidbach,

Thanks for your response. I look at the timestamp of your post - 5:44 am EDT. Wow, you must be an early bird or living in Hawaii.... Anyway, the goal of my compare program is not to validate data integrity between new/current files. It's just a precaution before our Tech Support CA jobs continue the next step for more tasks. The goal is to make sure the difference (either in record counts or EOF) between current/new files are under the acceptable differentiate rate. The new file came over our HPNS after another platform rebuilds a fresh file after some posting process.

Anyhow, I'd like to have a copy of your free gift if you're willing. Although my company does not allow (we have very strict internal auditing policy) us to install any unauthorized 3rd party object code, I'll still love to pass your program (source code) to another team to review and see if there are any good ideas they can adopt. My e-mail address is nellafan...@gmail.com.

Thanks for your help!!

Nella Fantasia,

Paradiso

wbreidbach

unread,
Sep 3, 2015, 3:23:23 AM9/3/15
to
I am located in Europe, at this time we have 9:20am.
I know this object code problem, the file I sent you contains the sourcecode as well, nothing magic with that.
The program has a parameter that limits the number of allowed differences between the files, if this limit is exceeded, an error is produced.

Andre White

unread,
Nov 8, 2015, 12:47:52 AM11/8/15
to
Hi Paradiso, this the example to calculate number of record in file TYPE K and file format 2

?SECTION READ^AND^CAL^NUM^OF^REC
?PAGE "READ^AND^CAL^NUM^OF^REC"
INT PROC READ^AND^CAL^NUM^OF^REC;
BEGIN
INT16 IFILEINFO_ITEM_LIST[ 0:4 ];
INT16 IFILEINFO_ITEM_LIST_NUM := 0;
INT16 IFILEINFO_RESULT_LGTH := 0;
INT16 IFILEINFO_RESULT_MAX_LGTH := 0;
INT16 IREC_PERBLOCK;
INT16 ILOGLENGTH;
INT32 IELECTIONS := 1D;
INT32 IF64[ 0:1 ] := [0D,0D];
FIXED FF64 = IF64;
STRUCT IFILEINFO_RESULT;
BEGIN
FIXED FEOF;
INT IFILE_TYP;
INT IREC_LGTH;
INT IBLK_LGTH;
FIXED FCUR_REC_ADDR_64BIT;
END;

FIXED FNUM_BLOCKS := 0F;
FIXED FNUM_RECORDS:= 0F;

STRING .STEMP;
STRING .SILEINFO_RESULT_S;

IFILEINFO_ITEM_LIST_NUM := 5;
IFILEINFO_ITEM_LIST[ 0 ] := 191;
IFILEINFO_ITEM_LIST[ 1 ] := 41;
IFILEINFO_ITEM_LIST[ 2 ] := 43;
IFILEINFO_ITEM_LIST[ 3 ] := 44;
IFILEINFO_ITEM_LIST[ 4 ] := 202;
IFILEINFO_RESULT_LGTH := 0;
IFILEINFO_RESULT_MAX_LGTH := 191 + 41 + 43 + 44 + 8;

IF ( SERR.IERROR := FILENAME_RESOLVE_( STLFMENU.SLOCATIONLOGFILE:STLFMENU.ICOUNTLOG,
STLFMENU.SLOCATIONLOGFILE:36,
ILOGLENGTH )
) THEN
RETURN 1;

IF ( SERR.IERROR := FILE_OPEN_( STLFMENU.SLOCATIONLOGFILE:ILOGLENGTH,
ILOCATIONNUM,
1,
!EXLUSION!,
!NOWAIT DEPTH!,
1,
0,
!SEQ BLOCK BUFFER-ID!,
!SEQ BLOCK BUFFER-LEN!,
!PRIMARY PROCESSHANDLE!,
IELECTIONS
)
) THEN
RETURN 2;

IF ILOCATIONNUM = -1 THEN
RETURN 3;

IF ( SERR.IERROR := FILE_GETINFOLIST_( ILOCATIONNUM,
IFILEINFO_ITEM_LIST,
IFILEINFO_ITEM_LIST_NUM,
IFILEINFO_RESULT,
IFILEINFO_RESULT_MAX_LGTH,
IFILEINFO_RESULT_LGTH
)
) THEN
RETURN 4;

IF IFILEINFO_RESULT.IFILE_TYP <> LFILETYPE2 THEN
RETURN 5;

IF NOT STLFMENU.ISCANFLAG THEN
BEGIN
! ( IFILEINFO_RESULT.IBLK_LGTH - 22 ) / (IFILEINFO_RESULT.IREC_LGTH + 2 ) --> FOR FORMAT 1 FILES !
! THIS IS FOR FORMAT 2 FILES !
IREC_PERBLOCK := ( IFILEINFO_RESULT.IBLK_LGTH - 44 ) /
( IFILEINFO_RESULT.IREC_LGTH + 4 );

! CALCULATE THE NUMBER OF BLOCKS !
FNUM_BLOCKS := IFILEINFO_RESULT.FEOF + $FIX( IFILEINFO_RESULT.IBLK_LGTH ) - 1F;
FNUM_BLOCKS := FNUM_BLOCKS / $FIX( IFILEINFO_RESULT.IBLK_LGTH );

IF64[0] := $FIXD( FNUM_BLOCKS );
IF64[1] := $DBL( IREC_PERBLOCK );

! CALCULATE THE NUMBER OF RECORDS !
FNUM_RECORDS := $FIX( IREC_PERBLOCK ) * FNUM_BLOCKS;
END;


CALL FILE_SETPOSITION_( ILOCATIONNUM,
FF64
);

RETURN LFALSE;
END;

Keith Dick

unread,
Nov 8, 2015, 2:09:01 PM11/8/15
to
Andre White wrote:
> On Wednesday, September 2, 2015 at 10:44:03 AM UTC+8, paradiso wrote:
>
>>Hey, Tandem SME,
>>
>>Got a quick question about what Guardian Procedure call a COBOL program can use to return the number of records for an Enscribe file.
>>
>>I have a cobol program written to compare 2 Enscribe files with the same file attributes when first created. Our application platform received a file from another platform on a daily basis, and we switched the current processed file with the newly received file as long as the EOF difference between 2 files are within a pre-set acceptable percentage. The goal is to ensure both current and new files are "identical" if not way too much different. To achieve the goal, I was using Guardian procedure call, FILE_GETINFOLISTBYNAME, to return EOF value of the files (item code 191).
>>
>>A recent issue occurred on a situation where both files had the same number of records, but their EOF was more than 50% off (and exceeding the default compare percentage). The EOF difference was caused by the slack space. That makes me realize, instead of comparing the EOF b/t 2 files, I should really compare the number of records b/t those 2 Enscribe files.
>>
>>My question is, is there any Guardian procedure call I can use for a cobol program to compare the number of records between 2 Enscribe files? I look through all item codes for FILE_GETINFOLISTBYNAME, but did not see any item code that returns the number of records.
>>
>>Any insight into this question will be greatly appreciated!!
>
>
> Hi Paradiso, this the example to calculate number of record in file TYPE K and file format 2

<program removed>

No, unless I'm misunderstanding something, that program does not compute the number of records in the file. It gives an upper bound on the number of records that could be in the file, under the assumption that all records are the maximum record length specified for the file.

It is only an upper bound, not an exact count, because:

- It ignores the presence of index block and bitmap blocks in the file
- It ignores the fact that most data blocks in a file do not contain the maximum number of records that the block could hold

If the records are variable in length, the actual number of records could even be larger than this program calculates.

Although it does not affect the execution of this program, the code incorrectly computes the value of IFILEINFO_RESULT_MAX_LGTH. It should be 22, not 327. It is to be the length in bytes of the result argument. The too-high value allows FILE_GETINFOLIST_ to function, but if you had accidently made IFILEINFO_RESULT too short, the mistake would not be caught and FILE_GETINFOLIST_ would store past the end of IFILEINFO_RESULT.

I'm not sure the business near the end where you call FILE_SETPOSITION_ is valid, since FILE_SETPOSITION_ is documented only to work for relative, entry-sequenced, and unstructured files. I do not see an explicit statement that FILE_SETPOSITION_ does not work with key-sequenced files, but I would expect it to return an error code when used on a key-sequenced file. This does not affect the calculation of the number of records, but I noticed it and wanted to alert you to the possible problem, in case you actually are counting on that positioning to work in some program.

There is no way to get the exact count of the records in a key-sequenced file except by reading the file and counting the records. That is what FUP INFO STAT does, though it uses unstructured access to read the file in large chunks to do the reading and counting more efficiently.

If you only need an approximation of the number of records in a file, something like the calcuations in this program, or just dividing the EOF by the record length, is a reasonable thing to do, but that is not the problem paradiso said he had to solve.

Andre White

unread,
Nov 8, 2015, 9:46:56 PM11/8/15
to
Hi keith, thanks. I mean this program for entry sequence not for key sequence. One more for CALL FILE_SETPOSITION, i try to read to last record.

Fix :
IFILEINFO_ITEM_LIST_NUM := 6
IFILEINFO_ITEM_LIST[0] := 191
IFILEINFO_ITEM_LIST[1] := 41
IFILEINFO_ITEM_LIST[2] := 43
IFILEINFO_ITEM_LIST[3] := 44
IFILEINFO_ITEM_LIST[4] := 195
IFILEINFO_ITEM_LIST[5] := 202

IFILEINFO_RESULT_MAX_LGTH := 191+41+43+44+195+8

thanks.

wbreidbach

unread,
Nov 9, 2015, 4:37:43 AM11/9/15
to
As Keith already wrote, this computation might give you completely unusable results.
It will work under the following conditions:

The file only contains fixed length records
No block contains a slack greater than the recordsize
All blocks are full

The chance to meet all these requirements is pretty small.

Just to give you an idea, here an example using one of our tools:


Statistics
INDEX TOTAL TOTAL AVRG# AVRG AVRG%
LEVEL BLOCKS RECS RECS SLACK SLACK
2 1 3 3.0 3853 94
1 3 33 11.0 3006 73
DATA 33 287 8.6 2141 52
FREE 391
BITMAP 1

Recsize/Blocksize : 270/ 4096
actual/maximum EOF : 1757184/ 20111360
primary/secondary extent : 20/ 200
allocated/maximum extents: 6/ 50
% full : 8.7%

Fragmentation
Chainlength no. chains
1 11
2 4
3 2
8 1

Maximum chainlength: 8
Average chainlength: 1
Total number of blocks: 33
Blocks in short chains: 11
Total fragmentation: 33%

Last reload:
Reload started : 22 Oct 2015, 15:54:14
Reload ended : 22 Oct 2015, 15:54:15
Dslack : 15%
Islack : 15%
Rate : 100%
Percent completed : 100%
Deallocate : Yes
Share : No
Compact : No
Reclaim : No

File last modified : 09 Nov 2015, 10:07:22
File last opened : 09 Nov 2015, 09:55:59

A reload with slack <= 52 should save some diskspace
Regarding current fragmentation: Reload is recommended

The computed number of records would be 6435, but in fact there are only 287 records in the file.

So if you want to know the exact number of records you have to count them using a tool like FUP (or the one above).

Keith Dick

unread,
Nov 9, 2015, 1:06:51 PM11/9/15
to
Well, in your earlier post, you said it was for key-sequenced.

Your calculation assumes the records in the entry-sequenced file are all the maximum record length specified when the file was created. If the files you use with the program do meet that condition, then maybe you do get the correct number of records. But if the records vary in length, I expect the number of records you calculate will be lower than the actual number of records in the file.

The FILE_SETPOSITION_ call might be correct for an entry-sequenced file. I have not looked at the code to see whether it is correct for that.

Your calculation of IFILEINFO_RESULT_MAX_LGTH is still wrong. I don't know why you think you are supposed to add the value of the item codes, except for the last one. The fifth argument to FILE_GETINFOLIST_ is supposed to be the length in bytes of the fourth argument. That is not what you are computing for IFILEINFO_RESULT_MAX_LGTH. As I explained previously, this will not prevent your program from working, because you are telling FILE_GETINFOLIST_ that IFILEINFO_RESULT is much larger than it actually is, so it will believe that it has much more room in IFILEINFO_RESULT than it actually needs. However, if you accidently declare IFILEINFO_RESULT to be shorter than the size necessary to hold all the items you requested, FILE_GETINFOLIST_ will store past the end of IFILEINFO_RESULT rather than giving you the error return to tell you that you did not supply enough space for all the items you requested.

Andre White

unread,
Nov 10, 2015, 12:15:17 AM11/10/15
to
Hi keith,

Thanks for your input, sorry I forgot to put in my earlier post. So the IFILEINFO_RESULT actualy like this:

STRUCT IFILEINFO_RESULT;
BEGIN
FIXED FEOF;
INT IFILE_TYP;
INT IREC_LGTH;
INT IBLK_LGTH;
INT IFILE_FORMAT;
FIXED FCUR_REC_ADDR_64BIT;
END;

I need to know the file format is type 1 or 2. Because i need open the file use parameter "IELECTIONS" if the file format is type 2.

Currently my condition like this :

1. CALL FILE_OPEN_ USE PARAMETER IELECTION.
2. CALL FILE_GETINFOLIST_
3. CHECK IF FILE FORMAT = 2 GOTO STEP 4 ELSE GOTO STEP 5
4. CALCULATE NUMBER OF BLOCK AND RECORDS GOTO STEP 8
5. CALCULATE NUMBER OF BLOCK AND RECORDS
6. CLOSE FILE
7. CALL FILE_OPEN_ WITHOUT PARAMETER IELECTION.
8. CALL FILE_SETPOSITION_
9. RETURN

One more guys, i need ask you about technical and your input.

Yesterday, me and my friend debate about changes in source code, he dosen't like if i put condition like this.

MY CHANGES
==========
PROC A;
BEGIN

int itemp := atm.ncr.txn_stat; ! my changes !

atm.ncr.txn_stat := expect_tran_l;

case atm.ncr.txn_resp_cde of
begin
shutdown_atm_rc ->
begin
.....
end;
tran_cancel_shutdown_atm_rc ->
begin
if atm_is_faulted then
begin
atm.ncr.txn_stat : itemp;
call send_faulty_screen;
return;
end;
..........
end;
....................
end;
END

He reason because in my changes I add new variable and I assign the value in my variable. I don't know, he know basic programming or not. But my reason in he code because, i don't like if call redundancy proc in same time.

HE WANT MAKE LIKE THIS
===================
PROC A;
BEGIN


if not ( atm_is_faulted or tran_cancel_shutdown_atm_rc) then
atm.ncr.txn_stat := expect_tran_l;

case atm.ncr.txn_resp_cde of
begin
shutdown_atm_rc ->
begin
.....
end;
tran_cancel_shutdown_atm_rc ->
begin
if atm_is_faulted then
begin
call send_faulty_screen;
return;
end;
..........
end;
....................
end;
END


CURRENT :
========
PROC A;
BEGIN

atm.ncr.txn_stat := expect_tran_l;

case atm.ncr.txn_resp_cde of
begin
shutdown_atm_rc ->
begin
.....
end;
tran_cancel_shutdown_atm_rc ->
begin
if atm_is_faulted then
begin
call send_faulty_screen;
return;
end;
..........
end;
....................
end;
END;

noted : DEFINE am_is_faulted = call proc X and call proc Y and call proc Z #;

Thanks keith and wolfgang for your input. I still need learn tandem from you.

wbreidbach

unread,
Nov 10, 2015, 4:59:30 AM11/10/15
to
Just for you information: There is no need for different opens for format 1 and format 2, you can always use 64-bit opens (elections set to 1d).

Andre White

unread,
Nov 10, 2015, 5:36:10 AM11/10/15
to
Thanks Wolfgang

Keith Dick

unread,
Nov 10, 2015, 5:39:37 AM11/10/15
to
Hi Andre,

I had guessed that you had added the additional field to the IFILEINFO_RESULT structure. That wasn't the point I was trying to make, and your further explanation does not make it clear whether you understood my point or did not understand it.

My point is that the value of IFILEINFO_RESULT_MAX_LGTH is supposed to be the number of bytes in IFILEINFO_RESULT, which for the modified structure is 24.

191+41+43+44+195+8, which is 522, is far more than 24, so your program is telling FILE_GETINFOLIST_ that IFILEINFO_RESULT is 522 bytes long, which is not true.

FILE_GETINFOLIST_ uses the fifth argument to check to see whether there is enough room in the fourth argument to place all the result values that you requested with the item codes in the item list. This is to protect against storing past the end of the memory allocated to the fourth argument. When you give an incorrect value for the fifth argument, FILE_GETINFOLIST_ will be testing with a false value for the length of the fourth argument. In your program, the total size of the result values you requested is 24 bytes, so they do fit into IFILEINFO_RESULT, so the incorrect value of the fifth argument does not cause any problem. However, it still is wrong.

Suppose sometime later, you add another item to the list of items you are requesting, but forget to add the field to hold it into IFILEINFO_RESULT. Since you would still be telling FILE_GETINFOLIST_ that the fourth argument is more than 500 bytes long, FILE_GETINFOLIST_ would think that there is plenty of space for all the items you requested, but actually, IFILEINFO_RESULT would not be long enough to hold them all. The result is that FILE_GETINFOLIST_ will store some of the items beyond the end of the space allocated for IFILEINFO_RESULT, which will change the value of some other variable in your program, possibly causing the program to do something wrong. If you had computed the correct value for IFILEINFO_RESULT_MAX_LGTH, FILE_GETINFOLIST_ would have noticed that the size of the items you requested was larger than would fit into IFILEINFO_RESULT, and instead of storing past the end of IFILEINFO_RESULT, it would have returned an error code that told you that you request
ed more results than would fit.

Something I did not mention earlier is that it would be much safer for you to let the compiler determine the size of the structure by using the $LEN function rather than trying to add up the sizes you predict for each field of the structure. So rather than computing some value for IFILEINFO_RESULT_MAX_LGTH and passing IFILEINFO_RESULT_MAX_LGTH as the fifth argument to FILE_GETINFOLIST_, it would be much better to eliminate IFILEINFO_RESULT_MAX_LGTH and pass $LEN(IFILEINFO_RESULT) as the fifth argument to FILE_GETINFOLIST_.

(If you are trying to get the length of an array of objects, the expression should be $LEN(x)*$OCCURS(x), since $LEN(x) gives the length of one occurrence of whatever x is. You don't have to include $OCCURS in this program, since IFILEINFO_RESULT is not an array.)

I hope I've explained that point clearly enough now, but if you still do not understand why I'm making a big deal about it, keep asking about it until you do understand.

Now, on to the next point. In your nine-step outline of the program, you show how you have changed the organization of your program to make it notice whether the file is format 1 or format 2, and do the calculation of the number of records differently, because the details of the number of records per block is different for format 1 files vs. format 2 files. That looks good, except one point about it seems like it could cause problems later.

When the file is format 1, after you calculate the number of records, you close the file, and open it again without requesting 64-bit keys. That has two implications. One is that you must calculate the argument to FILE_SETPOSITION_ differently for the format 1 case than for the format 2 case. Perhaps you have done that, but just did not mention it in the outline. The other implication is that if the rest of the program outside this procedure uses the file number obtained for the file in this procedure to do any positioning in the file, the rest of the program would need a way to tell whether to calculate the position in the format 1 way or in the format 2 way. If you have arranged for that to be done, or if the rest of the program never does FILE_SETPOSITION_ for this file, then perhaps the difference in the way the file is opened will not lead to any problems.

A format 1 file can be positioned and read when it is opened for 64-bit keys, so it could simplify the program if you did not switch to 32-bit keys for format 1 files. You can do it either way, as long as the program always knows whether to do the positioning with a 64-bit key or a 32-bit key.

Finally, on to your last question.

My short answer is that I probably do not know enough about your program to say which of the two ways of changing the program would be better.

I assume that the problem with CURRENT that you want to fix is that the value of atm.ncr.txn_stat should not be changed on certain paths through the code.

There are at least three ways to approach making that happen:

- Don't change atm.ncr.txn_stat at the beginning, but only store a new value
for it in each path through the code when it has been determined that you
have a case for which a new value for atm.ncr.txn_stat is appropriate.

- Don't change atm.ncr.txn_stat at the beginning, create a temp variable that will
be the new value for atm.ncr.txn_stat at the end of the analysis, store new values
in the temp variable as you analyze the conditions, then copy the temp variable to
atm.ncr.txn_stat just before every exit from the code.

- Save the current value of atm.ncr.txn_stat, store the expected new value before
examining the conditions, and put atm.ncr.txn_stat back to the value you saved
if you discover that the current conditions require that.

There might be still other approaches, but those are the only ones I can think of right now.

The benefit of not changing the value of atm.ncr.txn_stat at the beginning, but only storing a new value in atm.ncr.txn_stat when the code reaches a point for which you definitely know what the new value should be is that you are never storing a value into atm.ncr.txn_stat that you are not certain is the correct value. The drawback to that approach is that you must not forget to store a new value on every path through the code that requires a new value for atm.ncr.txn_stat.

The benefit of the second approach of storing the new value for atm.ncr.txn_stat in a temp and copying the temp to atm.ncr.txn_stat at every exit is similar, but has a small advantage if the current value of atm.ncr.txn_stat is considered in the analysis of what the new value should be. You can reference atm.ncr.txn_stat when you need to test the current status, since it has not yet been changed. A drawback is that you must not forget to copy the temp variable into atm.ncr.txn_stat on every exit from the code.

If the correct new value for atm.ncr.txn_stat on most paths through the code is expect_tran_l, then setting atm.ncr.txn_stat to that value at the beginning gives some assurance that you won't forget to set it on some paths through the code, but then you must be sure to put it back to its original value on those paths for which it should not have been changed.

It is hard to know whether it is more likely that you would forget to store a value with the first approach or the second approach as compared to the third approach. Without knowing more about the program than you have shown, I would favor the approach you suggested of saving the value of atm.ncr.txn_stat at the beginning, unconditionally setting atm.ncr.txn_stat to expect_tran_l, then setting atm.ncr.txn_stat back to the value you saved if you find that the situation is one for which its value should not have been changed.

I advise against what your friend suggested -- having an extra test at the beginning to decide whether atm.ncr.txn_stat should be changed. I suggest that is not so good precisely because it is an extra test that I assume would have to be kept in step with any changes made in the code below that analyzes all the cases. Whenever you can consolidate tests rather than repeating them, it usually is better to consolidate them.

If you do end up choosing the approach your friend suggests, I think it has two errors that you need to correct. What you showed in your post is:

if not ( atm_is_faulted or tran_cancel_shutdown_atm_rc) then
atm.ncr.txn_stat := expect_tran_l;

I think that if you do adopt that approach, the code should be:

if not ( atm_is_faulted and atm.ncr.txn_resp_cde = tran_cancel_shutdown_atm_rc) then
atm.ncr.txn_stat := expect_tran_l;

The "or" should be an "and", and you should test that atm.ncr.txn_resp_cde has the value tran_cancel_shutdown_atm_rc. Just writing tran_cancel_shutdown_atm_rc as you showed is simply testing whether tran_cancel_shutdown_atm_rc is zero or nonzero, and I'm pretty sure that isn't what you want. This accidently illustrates my point that attempting to duplicate tests in the code is best avoided, since you can easily make mistakes that cause two separate tests not to be testing the same thing. (The other test is the combination of the case statement and the "if" inside the tran_cancel_shutdown_atm_rc case.)

Your note at the end to explain how atm_is_faulted was defined is good, but it is a little confusing. I assume atm_is_faulted was not defined as three CALL statements, but as some expression combining what is returned by calling three procs that return function results. I think the details are not important for what I wrote above, so we do not need to dig into that.



Message has been deleted

Andre White

unread,
Nov 10, 2015, 6:52:27 AM11/10/15
to
Thanks Keith for your explanation. :-)

Andre White

unread,
Nov 10, 2015, 7:14:37 AM11/10/15
to

> Now, on to the next point. In your nine-step outline of the program, you show how you have changed the organization of your program to make it notice whether the file is format 1 or format 2, and do the calculation of the number of records differently, because the details of the number of records per block is different for format 1 files vs. format 2 files. That looks good, except one point about it seems like it could cause problems later.
>
> When the file is format 1, after you calculate the number of records, you close the file, and open it again without requesting 64-bit keys. That has two implications. One is that you must calculate the argument to FILE_SETPOSITION_ differently for the format 1 case than for the format 2 case. Perhaps you have done that, but just did not mention it in the outline. The other implication is that if the rest of the program outside this procedure uses the file number obtained for the file in this procedure to do any positioning in the file, the rest of the program would need a way to tell whether to calculate the position in the format 1 way or in the format 2 way. If you have arranged for that to be done, or if the rest of the program never does FILE_SETPOSITION_ for this file, then perhaps the difference in the way the file is opened will not lead to any problems.
>
> A format 1 file can be positioned and read when it is opened for 64-bit keys, so it could simplify the program if you did not switch to 32-bit keys for format 1 files. You can do it either way, as long as the program always knows whether to do the positioning with a 64-bit key or a 32-bit key.
>

Hi Keith,

If i try open file format 1 with parameter 64-bit keys after that i try to FILE_SETPOSITION_ with record specifier is last record. After I read, i get not last record. And then I try open the file without 64-bit keys, is ok.

Thanks Keith.

wbreidbach

unread,
Nov 10, 2015, 8:42:48 AM11/10/15
to
Please remind, file_setposition_ needs a 64bit field for the position and this is identical for format 1 and format 2.
First 32 but contain the blocknumber, second 32 bit contain the recordnumber within that block.

Keith Dick

unread,
Nov 10, 2015, 9:42:09 AM11/10/15
to
Andre White wrote:
> Hi Keith thanks for your explanation. For point number 1 clear already.
>
> if not ( atm_is_faulted and atm.ncr.txn_resp_cde = tran_cancel_shutdown_atm_rc) then
> atm.ncr.txn_stat := expect_tran_l;
>
> For point number 2 i'am not agree with you (Don't change atm.ncr.txn_stat at the beginning), so you mean I must put the same condition with in case tran_cancel_shutdown_atm_rc
>
> DEFINE atm_is_faulted = BNA_FAULTY and CASH_HANDLER_FAULTY and CARD_RDR_FAULY #;
>
> INT PROC BNA_FAULTY;
> BEGIN
> ...........
> ............
> ............
> RETURN TRUE
> END;
> ==================
> INT PROC CASH_HANDLER_FAULTY;
> BEGIN
> ...........
> ............
> ............
> RETURN TRUE
> END;
> =============
> INT PROC CARD_RDR_FAULTY;
> BEGIN
> ...........
> ............
> ............
> RETURN TRUE
> END;
>
>
> My point to create a new variable just save value atm.ncr.txn_stat and store back in case tran_cancel_shutdown_atm_rc and atm_is_faulted.
>
> if i have declaration like this :
>
>
> int a := c; ! c values 2!
>
> My Question ?
>
> 1. possible for value a is equal 1 or 3 or 4 after i assign 2 ? if possible give me example ?
> 2. First call atm_is_faulted
> |
> v
> if not ( atm_is_faulted and atm.ncr.txn_resp_cde = tran_cancel_shutdown_atm_rc) then
> atm.ncr.txn_stat := expect_tran_l;
>
> case atm.ncr.txn_resp_cde of
> begin
> shutdown_atm_rc ->
> begin
> .....
> end;
> tran_cancel_shutdown_atm_rc ->
> begin
> ! second call atm_is_faulted !
> ! | !
> ! v !
> if atm_is_faulted then
> begin
> call send_faulty_screen;
> return;
> end;
> ..........
> end;
> ....................
> end;
> END ;
>
> what the different if make like this :
>
> if ( atm_is_faulted and atm.ncr.txn_resp_cde = tran_cancel_shutdown_atm_rc) then
> begin
> call send_faulty_screen;
> return;
> end;
>
> atm.ncr.txn_stat := expect_tran_l;
> case atm.ncr.txn_resp_cde of
> begin
> shutdown_atm_rc ->
> begin
> .....
> end;
> tran_cancel_shutdown_atm_rc ->
> begin
> if atm_is_faulted then
> begin
> call send_faulty_screen;
> return;
> end;
> ..........
> end;
> ....................
> end;
> END ;
>
> Thanks Keith

Hi Andre,

Your most recent post is a little confusing to me. I guess English is not your first language, so maybe my explanations are a little difficult for you to understand, and maybe I confused you. It is hard enough to discuss programming options in natural language, even when the two people have the same natural language as their first language. Trying to understand a discussion in a second language must be very difficult.

Let me summarize what I was trying to say in my previous post, in case that helps you understand anything that was confusing.

I first explained the problem you still had with calculating the length of the IFILEINFO_RESULT structure.

I then pointed out possible problems with closing the file when you found it was format 1 and opening it again with 32-bit keys.

I then wrote about your question about how to deal with getting the right value stored into atm.ncr.txn_stat -- you had one idea about how to do it and your friend had a different idea about how to do it. I described three general approaches that might be used for situations like the one I think your code represents. I discussed a little bit about benefits and drawbacks of each of those approaches. Then I said that in this particular program, I thought that your idea about how to do it was probably the best choice. In other words, what you labeled "MY CHANGES" is what I believe probably is the best choice, given what you have told me about the program.

I then went on to say that although I advised against the approach your friend suggested, if you ended up using his approach, I saw two bugs in his code, and showed how to correct them. I do not suggest using your friend's approach, but if you do decide to use it, you need to make those corrections.

Finally, I thanked you for thinking of explaining how atm_is_faulted was defined. I pointed out that you probably did not mean to say it contained three CALL statements. And I said that we probably did not have to discuss the details of atm_is_faulted, since it probably did not affect the question you had asked about the best way to deal with updating the value of atm.ncr.txn_stat.


Okay, now let me try to respond to your most recent post.

I think that when you said that point number 1 is clear now, you mean that you understand my explanation of why your calculation of the size of the IFILEINFO_RESULT structure was wrong and how to do it safely with $LEN, so we don't have to talk about that any more.

Or by point 1, did you mean my correction of your friend's addition to the program? That correction was:

if not ( atm_is_faulted and atm.ncr.txn_resp_cde = tran_cancel_shutdown_atm_rc) then
atm.ncr.txn_stat := expect_tran_l;

If point 1 was this correction, do you still need to discuss the size of IFILEINFO_RESULT structure? Or do you now understand what I was saying about the size of the IFILEINFO_RESULT structure?


I am not sure what you mean when you start to talk about my point 2. I'm not sure what you feel is my point 2. I did not number my points, which is part of what makes me unsure what you mean. My suggestion was that your suggestion, what you labeled "MY CHANGES", probably is the best choice. You now seem to be asking about the approach your friend suggested. That approach could be used, but I believe it is not the best approach to use.

The reason I believe your friend's approach is not the best is that it requires that the condition in the "if" statement gets the same result as is implied by the case statement and the "if" statement within the tran_cancel_shutdown_atm_rc case. Duplicating logic in that way usually is a bad idea. The fact that your friend got that condition wrong illustrates one reason why it usually is a bad idea.

So, again, I think your approach is right: at the beginning save atm.ncr.txn_stat in a temp variable and set atm.ncr.txn_stat to expect_tran_l, then inside the case statement, set atm.ncr.txn_stat back to the value you saved in the temp variable. Just as you wrote it in your earlier post.


You then asked two questions.

1. possible for value a is equal 1 or 3 or 4 after i assign 2 ? if possible give me example ?

I'm not sure what motivates you to ask this question, but if I understand the question correctly, here is the answer: If the variable "a" is not intentionally changed as a side-effect of calling any of the other procs in your code, and if none of your code accidently stores outside the memory area of your other variables, then once you set "a" to 2, "a" will remain having the value 2 until you store some other value into "a".

2. First call atm_is_faulted
|
v
if not ( atm_is_faulted and atm.ncr.txn_resp_cde = tran_cancel_shutdown_atm_rc) then
atm.ncr.txn_stat := expect_tran_l;

case atm.ncr.txn_resp_cde of
begin
shutdown_atm_rc ->
begin
.....
end;
tran_cancel_shutdown_atm_rc ->
begin
! second call atm_is_faulted !
! | !
! v !
if atm_is_faulted then
begin
call send_faulty_screen;
return;
end;
..........
end;
....................
end;
END ;

what the different if make like this :

if ( atm_is_faulted and atm.ncr.txn_resp_cde = tran_cancel_shutdown_atm_rc) then
begin
call send_faulty_screen;
return;
end;

atm.ncr.txn_stat := expect_tran_l;
case atm.ncr.txn_resp_cde of
begin
shutdown_atm_rc ->
begin
.....
end;
tran_cancel_shutdown_atm_rc ->
begin
if atm_is_faulted then
begin
call send_faulty_screen; <----- will never be executed
return; <----- will never be executed
end;
..........
end;
....................
end;
END ;

My answer: The second form of the code in this question will do the same as the first form, but is more complicated and contains some code that will never be executed (the two lines I marked "<----- will never be executed"). Those lines will never be executed because the return statement in the "if" statement at the beginning of the code will cause the code to exit at that point, so the conditions that would let the case statement get to the two lines I marked will never be true when the case statement executes.

Code like this is complicated to follow precisely because of interactions between parts of the code. In a situation like this, where you have a variable such as atm.ncr.txn_resp_cde that identifies several states in which you do different things, putting everything into a single case statement usually is much easier to follow when you read the code later than it is when you have multiple tests at different points through the code. Also, having early returns from a section of code is something to be avoided if possible, since there might be something done below that return statement that you will assume gets done, but it gets skipped because of the early return. I do not know enough about this program to tell whether you can eliminate the early return from the tran_cancel_shutdown_atm_rc case, but it would be desirable to eliminate it.

Just to be clear, I believe your original suggestion is better than either of the possibilities given above in this question. This is better:

PROC A;
BEGIN

int itemp := atm.ncr.txn_stat; ! my changes !

atm.ncr.txn_stat := expect_tran_l;

case atm.ncr.txn_resp_cde of
begin
shutdown_atm_rc ->
begin
.....
end;
tran_cancel_shutdown_atm_rc ->
begin
if atm_is_faulted then
begin
atm.ncr.txn_stat := itemp;
call send_faulty_screen;
return;
end;
..........
end;
....................
end;
END;

For most programs, the most important thing is to organize the program so that it is very easy to understand what the code does when you come back to look at the code months or years later. There are some programs where the organization that is easiest to understand is not the best choice because it performs much slower than a different, harder to understand organization performs. However, those cases are rare. Learning how to organize a program so it is easy to understand is a large part of what you must learn to become a good programmer. One of the principles to follow is to make procedures flow from the top to the bottom with no returns in the middle. Another principle to follow is to reduce the number of possible paths of control flow through a procedure. In this procedure, eliminating the "if" at the beginning of the procedure reduces the number of possible paths through the code, which usually makes it easier to understand. These are not rules that you can never
violate. As with any art, you should first learn the rules, then learn when it is justified to violate the rules.


Okay, one more thing. I said in my previous post that we probably did not need to discuss the details of the atm_is_faulted DEFINE. However, since you included the details in your post, I see something about it that does not seem to be correct, so let me mention that.

You showed it as:

DEFINE atm_is_faulted = BNA_FAULTY and CASH_HANDLER_FAULTY and CARD_RDR_FAULTY #;

I assume each of BNA_FAULTY, CASH_HANDLER_FAULTY, and CARD_RDR_FAULTY returns true when there is a problem with the aspect it is testing. I also assume that atm_is_faulted should be true when any problem exists. However, the way you have written the DEFINE, it will only be true when ALL of the conditions are true, so I am pretty sure that is not how it should be written. I think it should be:

DEFINE atm_is_faulted = BNA_FAULTY or CASH_HANDLER_FAULTY or CARD_RDR_FAULTY #;

If I am wrong and atm_is_faulted should be true only when all three errors have occurred, then I am wrong and you should ignore this part of my post.

If BNA_FAULTY, CASH_HANDLER_FAULTY, and CARD_RDR_FAULTY return true to indicate that there is NOT a problem with the aspect they are testing, then maybe the DEFINE should be:

DEFINE atm_is_faulted = not (BNA_FAULTY and CASH_HANDLER_FAULTY and CARD_RDR_FAULTY) #;

I think it is unlikely that this form of the DEFINE is the correct one, but I mention it just in case the return values from those three procs are in the opposite sense than their names imply to me.


Keith Dick

unread,
Nov 10, 2015, 9:48:29 AM11/10/15
to
If Wolfgang's answer (wbreidbach's answer) does not help you correct the problem, please post again to ask for more help.
The point is that when you have an entry sequenced file open with elections = 1, requesting use of 64-bit keys, you always give the key in the 64-bit format, both for format 1 and for format 2 files. If you still were computing the key for a format 1 file in the way you computed it when using 32-bit keys, you definitely would get the wrong position.

Andre White

unread,
Nov 10, 2015, 10:48:21 AM11/10/15
to
Thanks Keith and Wolfgang,

I understood already.

Thank you very much for your explanation about all. I think you a good programmer and analys
:-). Only in this group i can share my experience in technical. We are programmer can make the same output, but different logic.

for example.

1 + 1 = 2 or
1 + 1 -1 = 2 or
2 + 0 = 2, ....
the output still same 2.

I argue with my friend because I thinking i'm correct, but he thinking he correct. So just in this group I can share.

Thanks again for Keith and Wolfgang. :-)

comforte...@gmail.com

unread,
Nov 11, 2015, 6:01:50 AM11/11/15
to
Some late comments:

(1) How large are the files? Doing a read-only "read all records and count them" would be so much easier to do and probably will complete in reasonable time.

(2) Staying with the current approach (which involves plenty of logic): Why not use a (sorry!) modern scripting language for parsing of text output? Perl or Python come to mind - it is *so* much easier to parse complex output. I think both are already or soon fully supported products. Ideally, you could call NonStop system calls "natively" from either Python or Perl.

Benefit: the scripts will still be "NonStop specific" in parts - but the actual logic will be in a modern/common language and thus much easier to read by (sorry again) young folks and/or NonStop newbies.

Kind regards,

Thomas Burg, comForte, http://de.linkedin.com/in/thomasburg66
0 new messages