, 5 min read

Decompressing ZIP Files on Mainframe

Original post is here eklausmeier.goip.de/blog/2022/06-15-decompressing-zip-files-on-mainframe.


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.

$ unzip -l fulwola.zip
Archive:  fulwola.zip
  Length      Date    Time    Name
---------  ---------- -----   ----
   709692  2022-06-08 22:09   20220609_WOLA_EXP_WOLA_ADRESSE_CZSWADTB.csv
      179  2022-06-08 22:09   20220609_WOLA_EXP_WOLA_ADRESSE_CZSWADTB.info
      674  2022-06-08 22:09   20220609_WOLA_EXP_WOLA_GESCHAEFTS_VM_CZSWGVTB.csv
      177  2022-06-08 22:09   20220609_WOLA_EXP_WOLA_GESCHAEFTS_VM_CZSWGVTB.info
  1754956  2022-06-08 22:09   20220609_WOLA_EXP_WOLA_PARTYINFOS_CZSWPTTB.csv
      179  2022-06-08 22:09   20220609_WOLA_EXP_WOLA_PARTYINFOS_CZSWPTTB.info
     1004  2022-06-08 22:09   20220609_WOLA_EXP_WOLA_PARTY_BEZ_CZSWPBTB.csv
      177  2022-06-08 22:09   20220609_WOLA_EXP_WOLA_PARTY_BEZ_CZSWPBTB.info
    15254  2022-06-08 22:09   20220609_WOLA_EXP_WOLA_VERFUEGUNGEN_CZSWVFTB.csv
      178  2022-06-08 22:09   20220609_WOLA_EXP_WOLA_VERFUEGUNGEN_CZSWVFTB.info
---------                     -------
  2482470                     10 files

They can be decompressed to below PO files on mainframe:

EH2KLRQ.ZIPWOLA.CZSWADTC 
EH2KLRQ.ZIPWOLA.CZSWADTI 
EH2KLRQ.ZIPWOLA.CZSWGVTC 
EH2KLRQ.ZIPWOLA.CZSWGVTI 
EH2KLRQ.ZIPWOLA.CZSWPBTC 
EH2KLRQ.ZIPWOLA.CZSWPBTI 
EH2KLRQ.ZIPWOLA.CZSWPTTC 
EH2KLRQ.ZIPWOLA.CZSWPTTI 
EH2KLRQ.ZIPWOLA.CZSWVFTC 
EH2KLRQ.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:

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