This example is intended more for the classic IBM Mainframe z/OS user. Many times, when libraries are backed-up or downloaded, the PDS or PDSE members are downloaded into a folder as separate files. While this might not be a big deal when dealing with the odd individual member from time to time, but when having to upload the entire folder or directory back into a PDS, dealing with FTP for each member might be a bit tedious. Not to mention, having to validate the contents of the source for tabs, possible truncation issues, and other cross-platform and cross-character set incompatibilities that might crop up.
While a simple shell script could be written to upload all of the members using FTP individually, I personally like to opt for a single upload in IEBUPDT format. IEBUPDT is a source update facility that dates back to the OS/360 days. It's mostly used today to populate empty PDS or PDSEs with members from an installation library or product library.
So this Regina Rexx exec will read all files with a specific suffix within the current working directory and place them into a single IEBUPDT input file ready to be uploaded to z/OS. This exec also replaces any tabs with spaces and validates the length of each line and spits out a message if the line length is greater than 80. Of course, this length can be easily changed, but most source on the mainframe that's not-C/C++, is usually limited to 80 bytes. The exec can be easily extended to check for character or Unicode-EBCDIC translation anomalies that is easily corrected when building the IEBUPDT input deck.
The exec can be easily extended to do a lot more. Remember, this is just an example! It also shows how to do file I/O and system calls from within Regina Rexx on Mac OS X 10.6. Works pretty much as one would expect, but then again, Darwin does have it's differences from other *nix derivatives out there.
One side item to note. When passing parameters to Rexx execs or rexx subroutines, the parameters are generally blank delimited, with each parameter being placed into each subsequent variable in the argument list. With the exception of the last variable, which receives what ever is left over in the parameter list, regardless of how many parameters are there. So, it's considered good form to include another variable at the end of the expected parameter list to accept the errant, unexpected parameters should they be there. Some people use a period, some the variable Z; I personally like to use Uebrig which is German for "Remaining". Kind of also reminds me of my time in Berlin. Walking around on Sunday morning, you'd see rather tired or strung out partiers left from the night before. Either on their way home or ... anyway. They were referred to as "die Uebriggebliebene" -- literally "The Ones Left Over" ... as in left over from the night before. Uebriggebliebene also refers to left over food too.
The exec can be used for more than simply recombining files within a folder back into a single file ready for z/OS. In principle, one could adapt it as an archiver; or a facility to create a snapshot of the contents of part of a directory. The LS command provides a lot of information regarding the file in question. With the parsing power of Rexx, I'm sure there's quite a number of uses the basic structure of this exec could lend itself to.
Back to the exec! So here it is. With comments. Please feel free to make suggestions for improvement or ask questions! Hope you old mainframers like this mixing of two computing worlds!
(The exec is available for download at files.me.com/bikerdiver/ljxem8 for the next 10 days).
#!/usr/bin/regina
/* (c) 2011 T. Alan Tranter -- Use anyway you wish, but at your own risk. */
/* */
/* Set global variables that might someday be better parameterized. */
maxllen = 80 /* max line length before warning. */
/* Parse input and open up output stream in the current working directory */
/* File type (ftype) indicates the file extension. If it's a period '.' */
/* then the user wishes to include all files. */
parse upper arg outfile ftype uebrig
if ftype = '.' then ftype =''
/* Rexx is a bit different when opening up files. The first parameter is */
/* a variable name that contains the information linking to the open file. */
/* Rexx does not use, file handles, per se, rather variable names. */
rc = stream(outfile,'C','OPEN WRITE REPLACE')
/* Obtain a directory listing of the current working directory and place */
/* the output into the stem variable dcont., where dcont.0 contains the */
/* the number of output lines and dcont.1 through dcont.x is each line. */
address SYSTEM "ls -le" with OUTPUT STEM dcont.
if .rc <> 0 then exit 8
/* Loop through each line. We start with 2 because the first line is */
/* always the "total" line. Might be better to simply check to see if */
/* the first line and first word in the first line is 'total' and skip it, */
/* but why introduct a compare for every single line. Check the output */
/* of your LS command and adjust if necessary! */
do e = 2 to dcont.0
if substr(dcont.e,1,1) = '-' then do /* - indicates a file. */
nextfile = word(dcont.e,words(dcont.e))
if nextfile = outfile then iterate /* skip the output file */
if ftype <> '' then do
if ftype <> translate(strip(word(translate(nextfile,' ','.'),2))) then iterate
end
call addfile nextfile
end
end
/* Directory listing has been processed. Now add the IEBUPDT control */
/* card indicating it's the end of the input deck. */
rec = './ ENDUP'
rc = lineout(outfile,rec)
/* Close output file. */
rc = stream(outfile,'C','CLOSE')
exit 0
/* addfiles subroutine to include the selected file into the IEBUPDAT */
/* output stream. Single parameter which is the name of the file. Must */
/* reside in the current working directory. */
addfile: parse arg ifile uebrig
rc = stream(ifile,'C','OPEN READ')
if rc <> 'READY:' then do
say 'Error opening input file' ifile ' stream OPEN returned' rc
say 'file being skipped'
return
end
/* Separate the file name from its extension and test if the extension */
/* matches the extension, if provided. */
member = translate(ifile,' ','.')
/* PDS member names can only be up to 8 characters in length. To make */
/* sure the file name, which will become the directory entry name, is */
/* no longer than 8 characters. Should probably check to see that the */
/* contents of the name is also valid for PDSs. Something for the reader */
/* to do! */
if length(word(member,1)) > 8 then do
say word(member,1) "name is too long - file is skipped"
rc = stream(ifile,'C','CLOSE')
end
/* Add the IEBUPDT control card indicating the start of a new PDS or PDSE */
/* member. Must be upper case, and must start with a ./. Check the z/OS */
/* Utilities manual for more information regarding IEBUPDT. */
member = translate(substr(word(member,1),1,8))
rec = './ ADD NAME='||member
rc = lineout(outfile,rec)
maxlen=0
ftabs = 'N'
/* The lines() function returns the number of lines left in the input */
/* stream - kind of. It's basically 1 is there are one or more lines and */
/* 0 if end of file has been reached. The parameter provided to lines() */
/* is the variable name provided stream() open time. */
do while lines(ifile)>0
rec=linein(ifile)
/* This check is to make sure the file being included doesn't itself */
/* have IEBUPDT control cards imbedded within the file. This would not*/
/* be good. */
if substr(rec,1,2) = './' then do
say '** WARNING ** CONTROL CARD FOUND --' rec
end
/* Test if there are any tabs, and expand them to 6 blanks if found. */
if countstr(d2x(9),rec) > 0 then do
if ftabs = 'N' then do
say 'Tabs found in' ifile
ftabs = 'Y'
end
rec=changestr(d2x(9),rec,' ')
end
/* Strip any trailing blanks and test the length. If greater than 80, */
/* than spit out a message indicating such. The hwm length for the file*/
/* is saved, but no message is issued until the entire file has been */
/* processed. */
rec=strip(rec,'T')
if maxlen < length(rec) then maxlen=length(rec)
rc=lineout(outfile,rec)
end
/* Close up the input file and issue warning if max length is greater than */
/* 80. */
rc = stream(ifile,'C','CLOSE')
if maxlen > maxllen then ,
say '** WARNING **' ifile "maximum record length is" maxlen
return
No comments:
Post a Comment