Decompressing ZIP Files on Mainframe

· klm's blog


Original post is here: eklausmeier.goip.de

Unfortunately classical mainframe has no builtin decompressing software for ZIP files. What you could do: Transfer ZIP file to USS, decompress and unpack there, then copy back to MVS. To do it directly on MVS you need to purchase a separate utility to do this. One utility is FLAM, Frankenstein Limes Access Method. FLAM is a product of limes datentechnik.

Given a ZIP file which contains 10 files.

 1$ unzip -l fulwola.zip
 2Archive:  fulwola.zip
 3  Length      Date    Time    Name
 4---------  ---------- -----   ----
 5   709692  2022-06-08 22:09   20220609_WOLA_EXP_WOLA_ADRESSE_CZSWADTB.csv
 6      179  2022-06-08 22:09   20220609_WOLA_EXP_WOLA_ADRESSE_CZSWADTB.info
 7      674  2022-06-08 22:09   20220609_WOLA_EXP_WOLA_GESCHAEFTS_VM_CZSWGVTB.csv
 8      177  2022-06-08 22:09   20220609_WOLA_EXP_WOLA_GESCHAEFTS_VM_CZSWGVTB.info
 9  1754956  2022-06-08 22:09   20220609_WOLA_EXP_WOLA_PARTYINFOS_CZSWPTTB.csv
10      179  2022-06-08 22:09   20220609_WOLA_EXP_WOLA_PARTYINFOS_CZSWPTTB.info
11     1004  2022-06-08 22:09   20220609_WOLA_EXP_WOLA_PARTY_BEZ_CZSWPBTB.csv
12      177  2022-06-08 22:09   20220609_WOLA_EXP_WOLA_PARTY_BEZ_CZSWPBTB.info
13    15254  2022-06-08 22:09   20220609_WOLA_EXP_WOLA_VERFUEGUNGEN_CZSWVFTB.csv
14      178  2022-06-08 22:09   20220609_WOLA_EXP_WOLA_VERFUEGUNGEN_CZSWVFTB.info
15---------                     -------
16  2482470                     10 files

They can be decompressed to below PO files on mainframe:

 1EH2KLRQ.ZIPWOLA.CZSWADTC
 2EH2KLRQ.ZIPWOLA.CZSWADTI
 3EH2KLRQ.ZIPWOLA.CZSWGVTC
 4EH2KLRQ.ZIPWOLA.CZSWGVTI
 5EH2KLRQ.ZIPWOLA.CZSWPBTC
 6EH2KLRQ.ZIPWOLA.CZSWPBTI
 7EH2KLRQ.ZIPWOLA.CZSWPTTC
 8EH2KLRQ.ZIPWOLA.CZSWPTTI
 9EH2KLRQ.ZIPWOLA.CZSWVFTC
10EH2KLRQ.ZIPWOLA.CZSWVFTI

Relevant part in JCL is:

//FLCLCONV EXEC PGM=FLCL,REGION=0M,PARM='CONV'
//STEPLIB  DD DISP=SHR,DSN=SYS3.FLAM.LOAD
//SYSOUT   DD SYSOUT=*
//SYSPRINT DD SYSOUT=*
//STDENV   DD *
LANG=de_DE.IBM1141
FLM_SAF_LOG=OFF
//ARCHIN   DD DISP=SHR,DSN=EH2KLRQ.FULWOLA.ZIP
//FLAMPAR  DD *
  read.text(file=DD:ARCHIN/?*)
  write.record(
      file='EH2KLRQ.ZIPWOLA.[_-1-.1|cut7][ext|cut1]'
      ccsid=1141
      falloc(recformat=FB,space(primary=200,secondary=300))
      ccsid=1141
  )
//

See 1027: Need example to unzip ZIP file with multiple files in it.

Three things are noteworthy:

  1. All members of ZIP archive are processed via /?*
  2. Output files are dynamically created using [AX-BY], then using first 7 characters, adding first character from extension
  3. Target CCSID is specified

From FLCLBOOK.txt:

This is the list of supported processing rules:

  • [path] - Extracts the string before the last slash or backslash, i.e. the path without trailing (back)slash
  • [name] - Extracts the part after the last (back)slash, i.e. the filename
  • [base] - Extracts the part between the last (back)slash and the last dot, i.e. the filename without extension
  • [ext] - Extracts the part after the last dot, i.e. the file extension
  • [member] - Extracts a string enclosed in round brackets from the filename part of the string, e.g. a PDS member name on z/OS
  • [copy] - Simply copies the input string (useful if you only want to add an extension)
  • [upper] - Converts all characters to upper case
  • [lower] - Converts all characters to lower case
  • [title] - Converts the first character to upper case, all others to lower case
  • [cutX] - Cuts off the string after the X-th character, where X is a positive or negative number indicating how many characters to keep, counting from the front (positive) or back (negative)
  • [indN] - Inserts the current index as decimal number of at least length N (padded with preceding zeros) N is any number between 0 and 9, where 0 results in variable length numbers with their shortest possible representation (no preceding zeros)
  • [rndN] - Inserts random numbers of length N (with preceding zeros) N is any number between 0 and 9, where 0 results in variable length numbers (no preceding zeros)
  • [AX] - A is a place holder for a single-byte character, X is a positive or negative number; Extracts the string part between the X-th occurrence of the specified character and the next occurrence of that same character. If X is negative, search starts at the back and in reverse. If X is 0, the part before the first occurrence of A is extracted.
  • [AX-] - A is a placeholder for a single-byte character, X is a positive or negative number; Extracts the string part after the X-th occurrence of the specified character. If X is negative, search starts at the back and in reverse. If X is 0, the input string is copied.
  • [AX-BY] - A and B are placeholders for a single-byte character, X and Y are positive or negative numbers; Extracts the string part between the X-th occurrence of A and (relative to that occurrence) the Y-th occurrence of B. Like above, negative numbers mean counting from the back.
  • [search=replace] - Replaces the first occurrence of the string 'search' with the string 'replace'
  • [search=*replace] - Replaces all occurrences of the string 'search' with the string 'replace'
  • [table] - Evaluates to the current table name if end of table handling is activated (only when reading tables)

Examples:

 1------------------------------------------------------------------------
 2 infile='/path/to/file.ext'   pattern='text_without_tokens' outfile='text_without_tokens'
 3 infile='/path/to/file.ext'   pattern='text^[path^]'        outfile='text[path]'
 4 infile='/path/to/file.ext'   pattern='[PATH]'              outfile='/path/to'
 5 infile='/path/to/file.ext'   pattern='[Name]'              outfile='file.ext'
 6 infile='/path/to/file.ext'   pattern='[ext]'               outfile='ext'
 7 infile='/path/to/file.ext'   pattern='[UPPER]'             outfile='/PATH/TO/FILE.EXT'
 8 infile='/path/to/file.ext'   pattern='[cut5]'              outfile='/path'
 9 infile='/path/to/file.ext',  pattern='FILE[ind04]'         outfile='FILE0001'
10 infile='/path/to/file.ext',  pattern='FILE[rnd04]'         outfile='FILE0815'
11 infile='/path/to/file.ext'   pattern='[/2]'                outfile='to'
12 infile='/path/to/file.ext'   pattern='[/-1]'               outfile='file.ext'
13 infile='/path/to/file.ext'   pattern='[/2-]'               outfile='to/file.ext'
14 infile='/path/to/file.ext'   pattern='[/3-.1]'             outfile='file'
15 infile='/path/to/file.ext'   pattern='[path=directory]'    outfile='/directory/to/file.ext'
16 infile='USER.DATA.PDS(MYMEMBER)' pattern='[member]'        outfile='MYMEMBER'
17 infile='USER.DATA.PDS(MYMEMBER)' pattern='[.0|lower]/[.1|lower]/[member|lower].[ext|(0|lower]' outfile='user/data/mymember.pds'
18 infile='/verylongpath/to/longfilename.ext' pattern='[path|verylong=|/=*.|.1-|upper].[base|cut-8|upper]' outfile='PATH.TO.FILENAME'
19 infile='/path/to/*.txt'      pattern='<SYSUID>.GDG(+[ind0])' outfile='USER.GDG(+n)' whereby n starts with 1 and is incremented for each file
20------------------------------------------------------------------------