IBM Mainframe Forum Index
 
Log In
 
IBM Mainframe Forum Index Mainframe: Search IBM Mainframe Forum: FAQ Register
 

PL/I: opening file w/ dynamically determined RECFM and LRECL


IBM Mainframe Forums -> PL/I & Assembler
Post new topic   Reply to topic
View previous topic :: View next topic  
Author Message
Sam Dodgers

New User


Joined: 26 Jul 2016
Posts: 4
Location: CZ

PostPosted: Wed Jul 27, 2016 4:05 pm
Reply with quote

Hi everyone,

for our test automation effort, I am writing a utility which is to concatenate datasets with various record lengths and write the concatenated contents to a single output file whose LRECL would be the same as the longest LRECL of the input datasets. (Reason is simple: IEBGENER abends when you concatenate datasets with differing LRECLs/RECFMs into it's SYSUT1 in the JCL.)

Since it's supposed to be universal ("because automation"), the utility needs to be very dynamic: any number of input datasets of FB or VB formats and any possible LRECL could be user-entered as input, and either FB or VB record format could be user-required as output.

I have a working prototype which determines the number of input files, the highest LRECL of all of them and expects user-specified RECFM. It does the actual copying/concatenating of any number of input files to the output file and detecting the highest LRECL encountered.

However, I've run into unsolvable problems as for trying to force the output file to have the desired LRECL and RECFM values which are being programatically auto-detected during runtime.

I've consulted the IBM PL/I Programming guide and Reference guide, and Joan Hughes's "PL/I structured programming", but to no avail.
So I was hoping you guys could give me some clue.


Here is what I've tried so far:

  1. The ENV parameter. When I started writing the utility, I had hoped I could use this and easily solve the problem. However, I found out ENV only accepts STATIC variables, i.e. pre-set upon the program initialization, and thus I cannot pass it the LRECL computated during the actual program execution.
  2. ENV(<RECFM> BLKSIZE(0). I tried using two variables for the output file, one for cases when user requires RECFM=FB and other for RECFM=VB, with both pointing to the same DDname using the TITLE option of the subsequent OPEN statement, knowing that Blocksize=0 should cause auto-detection of LRECL and Blocksize. However, this just results in S013/R=00000034 Abend when the JCL does't specify the DCB of outfile (and the utility cannot require the user to do that - it would beat it's purpose).
  3. Using EXPORT DD_<DDNAME> statement with LRECL passed as non-static variable. I couldn't get this to work, the compiler keeps saying the statement is invalid. I probably don't understand the usage, the manuals aren't very verbose about this.
  4. Using the OPEN statement's TITLE with the /filespec attribute: this looked promising, but unfortunately, the TITLE could contain EITHER alternate_ddname OR /filespec, and /filespec seems to require hard-coded DSN. I couldn't have that, for the DSN is to be user-defined in the JCL calling the utility, and I also need the TITLE to point to an "alternate_ddname", because the output file's DD needs to be always reffered to as "OUTFILE" in the JCL.
  5. Falling back on implicit declarations (i.e. without record length or format being specified in the output file's DCL or OPEN statements). This works without abends and copies the infiles to the OUTFILE, but the output file seems to always be RECFM=VB,LRECL=255 (when testing with longest LRECL of 137 and all the "infiles" being FB) regardless of the length of the CHAR(<lrecl>) variables I am using to write to the OUTFILE.


Now, I do know two things:

  1. such utility could be programmed, because they are commercially available
  2. opening a file to write with it's LRECL and RECFM dynamically determined during runtime is possible, because my coworker has an assambler code which does that (I, being Test Automation, don't know HLASM)

So this is just a matter of finding how to accomplish this feat in PL/I.

Or, if you all agree this really cannot be done, finding a way how to substitute the OPEN statement with calling assembler macro to which I could pass the LRECL and RECFM variables I have and then work with the opened dataset using the standard PL/I I/O statements. (My coworker could provide the HLASM functionality itself, I just don't know how to "interface it" with my PL/I variables.)

Any ideas, please?

Thanks
Sam
Back to top
View user's profile Send private message
enrico-sorichetti

Superior Member


Joined: 14 Mar 2007
Posts: 10873
Location: italy

PostPosted: Wed Jul 27, 2016 4:26 pm
Reply with quote

probably it would be much simpler to use - instead of a concatenation - a sequence of ddnames with a common prefix...

something like
Code:
//IN001 DD ...
//IN002 DD ...
//IN003 DD ...
...
//INxxx DD ...

pretty easy to scan the step ddnames to find out the record length and format of each dataset

determine the characteristics of the output and process accordingly
Back to top
View user's profile Send private message
Sam Dodgers

New User


Joined: 26 Jul 2016
Posts: 4
Location: CZ

PostPosted: Wed Jul 27, 2016 4:37 pm
Reply with quote

That's exactly what I am doing.

See my utility's sample/testing JCL:

Code:

//CATLRSN  EXEC PGM=CATLRSN3                                 
//STEPLIB  DD DISP=SHR,DSN=xxx.xxx.LINKLIB             
//SYSPRINT DD SYSOUT=A                                       
//LOGFILE  DD SYSOUT=A                                       
//OUTFILE  DD DISP=(,CATLG),DSN=&OUTFILE,SPACE=(TRK,(2,4))   
//INFILE01 DD DISP=SHR,DSN=xxx.xxx.SYSOUTS(D130703A)   
//INFILE02 DD DISP=SHR,DSN=xxx.xxx.JCL(CONDEXER)       
//INFILE03 DD DISP=SHR,DSN=xxx.xxx.TC11.xxx.GLDN#RPT   


INFILE01 has LRECL=137, the others have LRECL=80. All are FB.

My whole problem is making the OUTFILE LRECL=137 and RECFM=FB (or whatever they're found to be during program's runtime) without specifying that in the JCL (the idea being that the customer doesn't know the record lengths of all his INFILEs and the program would auto-detect that for him).

To avoid further misunderstandings, let me post relevant portions of the function in question (note there are redundant debug/hacking variables):

Code:

CPINOUT: PROC($maxFiles,$maxLrecl,$outRecFm,$outBlksz);           
/****************************************************************/
/* CPINOUT  procedure:                                          */
/* - input: total # of INFILE DDs, LRECL of the longes INFILE,  */
/*          desired RecFm of the OUTFILE, desired BlockSize of  */
/*          the OUTFILE                                         */
/*                                                              */
/* - purpose: procedure allocates OUTFILE with the same LRECL   */
/*            as the longest INFILE's LRECL, then copies every  */
/*            INFILE dataset's content to the OUTFILE dataset,  */
/*            concatenating all the INFILE datasets in OUTFILE  */
/*                                                              */
/****************************************************************/
 dcl $maxLrecl FIXED(7,0);                                         
 dcl $currLrecl FIXED(7,0);                                       
 dcl $padLrecl fixed(7,0) init(0);                                 
 dcl $maxFiles fixed(3);                                           
 dcl $curDsnNr fixed(3);                                           
 dcl $outRecFm char(4) var;       /* TODO */                       
 dcl $outBlksz fixed(9);          /* TODO */                       
 dcl $currFi file record;                                         
 dcl $outFiFB file record ENV(FB BLKSIZE(0));                     
 DCL $outFiVB FILE RECORD ENV(VB BLKSIZE(0));                     
 DCL $outFile FILE record;                                         
 dcl $fileTitl char(9) var;                                       
 dcl $tempVar char($maxLrecl) var;                                 
 dcl $tempFix char($maxLrecl);                                     
 dcl not_eof bit(1) init('1'b);   

(...)

<OUT OF IDEAS ON HOW TO OPEN $OUTFILE or $OUTFIFB WITH $MAXLRECL AND $OUTRECFM HERE>

(...)

DO $curDsnNr = 1 to $maxFiles by 1;                               
 $fileTitl = 'INFILE'||padif10(trim($curDsnNr));                   
 call logstr('Reading '||$fileTitl||' to concatenate');           
 open file($currFi) title($fileTitl) input;                       
 ON ENDFILE($currFi) NOT_EOF='0'B;                                 
                                                                   
(...)

 read file($currFi) into($tempFix);                     
 DO WHILE(NOT_EOF);                                   
  write file($outFiFB) from($tempFix);                 
  read file($currFi) into($tempFix);                   
 END; /* end looping through all $currFi lines */   
 close file($currFi);
 NOT_EOF='1'B;                                                                     
END;
Back to top
View user's profile Send private message
enrico-sorichetti

Superior Member


Joined: 14 Mar 2007
Posts: 10873
Location: italy

PostPosted: Wed Jul 27, 2016 4:52 pm
Reply with quote

would it be acceptable to pass the output dsname as a parm
and use BPXWDYN for the dynamic allocation
Back to top
View user's profile Send private message
Sam Dodgers

New User


Joined: 26 Jul 2016
Posts: 4
Location: CZ

PostPosted: Wed Jul 27, 2016 5:02 pm
Reply with quote

Probably not as a PARM, but I think it would be possible to parse the OUTPUT dataset's DSName using the FILEDDWORD function.

I haven't thought about BPXWDYN at all, sincere thanks for pointing me to it.

I'll now do some research to see if it's plausible and then write back with either more questions or "mission accomplished".

Thanks once more
Sam
Back to top
View user's profile Send private message
steve-myers

Active Member


Joined: 30 Nov 2013
Posts: 917
Location: The Universe

PostPosted: Wed Jul 27, 2016 8:59 pm
Reply with quote

Something like this is not terribly difficult to write in Assembler. I did something similar back in the 1970s or perhaps the early 1980s as a TSO command - TPRINT (dataset dataset ... dataset) ... to print several data sets. One issue I had that you do not have is integrating the now obsolete table reference character (specified with OPTCD=J) into the output.

As Mr. Sorichetti and Mr. Dodgers propose, rather than trying to force the language and the PL/I library to define DCB attributes on the fly, use BPXWDYN to allocate an output data set with the required DCB attributes. One thing you have not considered is how to print data sets with ANSI carriage control characters and "machine" carriage control together. This is not trivial! Fortunately, "machine" carriage control is quite rare any more

Another issue is detecting input DCB attributes using the language and the library. I do not know if this is relatively easy to do, or is even possible.

Rather than trying to produce an output data set with "optimal" DCB attributes based on the input, just use a data set with varying length records large enough for the maximum input length you plan to support. You can keep the output size reasonable by trimming any trailing blanks from input records.

Good luck!
Back to top
View user's profile Send private message
Sam Dodgers

New User


Joined: 26 Jul 2016
Posts: 4
Location: CZ

PostPosted: Wed Jul 27, 2016 11:47 pm
Reply with quote

Steve Myers:
thanks for your advice. The option of using RECFM=VB and long-enough LRECL is always on the table, because my prototype did just that in the "implicit allocation" scenario I wrote about in the first post, but I would consider it not-enough good job.



For a large part of the afternoon, I was playing with the BPXWDYN calls. I have never called it from PL/I before, and I run into all sorts of weird problems, probably caused by some trivial mistake in my coding.

This is what I tried, based on the sole example in BPXWDYN's documentation:

Code:

/*OVERRIDE*/ $outDsn = 'xxx.xxx.catalloc'; /* placeholder */
DCL PLIRETV BUILTIN;                                                   
DCL BPXWDYN EXTERNAL ENTRY OPTIONS(ASM INTER RETCODE);                 
FETCH BPXWDYN;                                                         
call BpxWDyn("alloc fi($outFi) new space(2,4) msg(2) "||               
              "da("||trim($outDsn)||") "||                             
              "lrecl("||trim($maxLrecl)||") "||                       
              "blksize("||trim($maxLrecl*10)||") "||                   
              "recfm("||trim($outRecFm)||")");                         


I then rewrote my OPEN FILE and WRITE INTO statements to reference the $outFi variable/ddname.

However, despite the file supposedly being allocated:
Code:
IGD101I SMS ALLOCATED TO DDNAME ($OUTFI  )                   
        DSN (xxx.xxx.CATALLOC                       )   
        STORCLAS (SCWRKD) MGMTCLAS (MCWRKD) DATACLAS (DCWRKD)
        VOL SER NOS= WRKD04     


I subsequently got
Code:
IBM0203S ONCODE=83  The UNDEFINEDFILE condition was raised because the BLOCKSIZE was not specified (FILE= $OUTFI). 
         From entry point CPINOUT at statement 230 at compile unit offset +0000082C at entry offset +0000082C at   
         address 3AB0276C.                                                                                         


Statement 230 being the

Code:
open file($outfi) output title('OUTFILE');   


I seriously cannot grasp this: I have supplied the $outFi with Blocksize (albeit maybe not the best, but for FB-formatted datasets, LRECL*10 seems to be legit), yet it complains I didn't.

And the "XXX.XXX.CATALLOC" dataset is deleted regardless of what I do, although I did add another BPXWDYN block with "free fi($OutFi)":
Code:
 "IGD105I XXX.XXX.CATALLOC   DELETED"

To the end of my code, instead of the classic PL/I "close file($OutFi)" statement. Other files, which are closed in the "close file()" fashion, are RETEINED.

Also, the IBM manual says BPXWDYN returns messages in a "E15", which I suspect to be an Assembler Register. However, how do you fetch an ASM register from PL/I? Again, this is Terra incognita for me.

Could you guys shed some more light on working with BPXWDYN for me, or point me to a manual more verbose about the PL/I-BPXWDYN interaction?

Thanks for your patience
Sam
Back to top
View user's profile Send private message
View previous topic :: :: View next topic  
Post new topic   Reply to topic View Bookmarks
All times are GMT + 6 Hours
Forum Index -> PL/I & Assembler

 


Similar Topics
Topic Forum Replies
No new posts FTP VB File from Mainframe retaining ... JCL & VSAM 1
No new posts Extract the file name from another fi... DFSORT/ICETOOL 6
No new posts How to split large record length file... DFSORT/ICETOOL 10
No new posts Extracting Variable decimal numbers f... DFSORT/ICETOOL 17
No new posts SFTP Issue - destination file record ... All Other Mainframe Topics 2
Search our Forums:

Back to Top