7-Zip Running on i

Article ID: 58301

7-Zip is open-source software for Windows. It's a tool for working with file archives in many different formats, including .ZIP, .TAR, .GZ, .BZ2, and its own native format, .7Z. In addition to zipping and unzipping files into these formats, it can also encrypt the files using strong AES encryption. All for free!

Would you like to have the same thing in IBM i? If so, read this article.

There's another open-source project, p7zip, in which developers have taken the command-line portion of the Windows 7-Zip utility and ported it to run on Unix systems. This new tool is also free and can do almost anything that the Windows software can do, albeit, without the GUI interface.

After a little bit of work, I managed to get p7zip to compile in PASE running on i. Now that I've done that, I can use p7zip from QShell or PASE command lines. I can use it to create files that will be sent and unpacked on Windows or Unix systems. It's pretty cool!

Online Help

Once 7-Zip has been installed, you can run the 7z command to run it. Typing 7z by itself shows the command's help output. Here's what that help screen looks like:

7-Zip 4.65  Copyright (c) 1999-2009 Igor Pavlov  2009-02-03
p7zip Version 4.65 (locale=en_US,Utf16=on,HugeFiles=on,1 CPU)

Usage: 7z <command> [<switches>...] <archive_name> [<file_names>...]
       [<@listfiles...>]

<Commands>
  a: Add files to archive
  b: Benchmark
  d: Delete files from archive
  e: Extract files from archive (without using directory names)
  l: List contents of archive
  t: Test integrity of archive
  u: Update files to archive
  x: eXtract files with full paths
<Switches>
  -ai[r[-|0]]{@listfile|!wildcard}: Include archives
  -ax[r[-|0]]{@listfile|!wildcard}: eXclude archives
  -bd: Disable percentage indicator
  -i[r[-|0]]{@listfile|!wildcard}: Include filenames
  -m{Parameters}: set compression Method (see the manual)
  -l: don't store symlinks; store the files/directories they point to
  CAUTION : the scanning stage can never end because of symlinks like '..'
            (ex:  ln -s .. ldir)
  -o{Directory}: set Output directory
  -p{Password}: set Password
  -r[-|0]: Recurse subdirectories
  (CAUTION: this flag does not do what you think, avoid using it)
  -sfx[{name}]: Create SFX archive
  -si[{name}]: read data from stdin
  -slt: show technical information for l (List) command
  -so: write data to stdout (e.g.,: 7z a dummy -tgzip -so Doc.txt > archive.gz)
  -ssc[-]: set sensitive case mode
  -t{Type}: Set type of archive
  -v{Size}[b|k|m|g]: Create volumes
  -u[-][p#][q#][r#][x#][y#][z#][!newArchiveName]: Update options
  -w[path]: assign Work directory. Empty path means a temporary directory
  -x[r[-|0]]]{@listfile|!wildcard}: eXclude filenames
  -y: assume Yes on all queries

Examples

For example, let's say you've downloaded a file that was compressed with GNU Zip (by default, GNU Zip files end in .gz or .tgz). Here's how you'd uncompress that zipped file from a PASE command line:

7z x coolpgm_3.2.4.tar.gz

The 7z command tells PASE to run the 7-Zip program. The command character (x in this example) must be listed next, so I've given x=extract. Finally, I give it the filename of the gzipped file. 7z figures out that the file is in .gz format and unzips it according to the GNU zip rules.

For another example, let's say I wanted to create an archive of my /home/klemscot/logs folder in the IFS. I want it to be in ZIP format so that it can be easily opened on a Windows PC. No problem—I just do the following:

7z a logs.zip /home/klemscot/logs

This creates a .ZIP file suitable for sending to a Windows colleague.

By default, 7z figures out what sort of archive you are working with based on the extension. In the preceding example, it created a .ZIP archive because my file name ended with the letters ".zip." If for some reason you prefer to specify the format separately, you can use the -t switch. For example:

7z a -tzip logs.zip /home/klemscot/logs

Utilizing 7z from CL

As with most PASE tools, 7-Zip can be run from within a CL program via the QP2SHELL API. For example, I could do the following:

PGM

    DCL VAR(&CMD) TYPE(*CHAR) LEN(1000)

    CHGVAR VAR(&CMD) VALUE('7z a logs.zip /home/klemscot/logs')
    CALL PGM(QP2SHELL) PARM('/QOpenSys/usr/bin/sh' '-c' &CMD)

ENDPGM

Note: Although the name "QP2SHELL" looks very similar to QShell, they are actually two different things. QP2SHELL is part of PASE and launches a PASE shell, whereas QShell is a native Unix-like environment, not part of PASE, and QShell is launched via the STRQSH command.

There are some problems with the preceding CL program, however.

  • If the 7z command fails, no error is reported to my CL program!
  • 7z normally prints some output to the screen . In the CL version, however, that output goes to a spooled file, which can clutter up my spool or cause unnecessary printing.

By convention, you can detect the failure of a Unix command by checking its exit status. The exit status is a number that programmers can set in their programs. In this case, the developer who wrote 7-Zip told the program to set the exit status to 0 if the program was successful, or a positive number if unsuccessful, which is the usual convention for Unix programs. So if I want to know whether 7z succeeded, I can check that exit status. Unfortunately, the QP2SHELL API doesn't return it to the CL program. Instead, it sets the exit status as the ILE return code, in the job attributes. So to retrieve it, I need to use the Retrieve Job Information (QUSRJOBI) API.

PGM

    DCL VAR(&CMD)       TYPE(*CHAR) LEN(1000)
    DCL VAR(&RCVVAR)    TYPE(*CHAR) LEN(200)
    DCL VAR(&RCVVARLEN) TYPE(*CHAR) LEN(4)

    CHGVAR VAR(&CMD) VALUE('7z a logs.zip /home/klemscot/logs')
    CALL PGM(QP2SHELL) PARM('/QOpenSys/usr/bin/sh' '-c' &CMD)

    CHGVAR VAR(%BIN(&RCVVARLEN)) VALUE(200)   /* SIZE OF &RCVVAR */

    CALL PGM(QUSRJOBI) PARM(&RCVVAR    +
                            &RCVVARLEN +
                            'JOBI0600' +
                            '*'        +
                            ' '        )
    IF (%BIN(&RCVVAR 109 4) *NE 0) DO
       SNDPGMMSG MSGID(CPF9897) MSGTYPE(*ESCAPE) MSGF(QCPFMSG) +
                 MSGDTA('COMMAND FAILED')
    ENDDO

ENDPGM

All of a sudden, my once-simple CL program is not so simple anymore! And I still have output going to the spool.

Therefore, my recommendation is to use QShell instead of PASE to run the command. It may seem unintuitive at first, but QShell can run PASE programs, provided that the PASE program directories are in its path. So I can run the command from QShell as well, but I have to make sure the appropriate PASE tools are in my PATH:

PGM

    DCL VAR(&CMD) TYPE(*CHAR) LEN(1000)

    CHGVAR VAR(&CMD) VALUE('export PATH=$PATH:/QOpenSys/usr/bin+
                                             :/usr/local/bin && +
                            7z a logs.zip /home/klemscot/logs')
    STRQSH CMD(&CMD)

ENDPGM

The "export PATH" stuff adds the /QOpenSys/usr/bin folder, the default location for PASE software, into my PATH. It also adds /usr/local/bin into my PATH because that's where I usually like to put programs (e.g., 7z) that aren't from IBM. The PATH works similarly to a library list. When I run a command (7z in this example), the system searches all the directories in the PATH to find the program to run.

The preceding code works just like the PASE example: It zips up the files, and so forth, and like the PASE one it doesn't do any error handling. However, QShell has much better tools to control errors in my CL program. Here are the ones I use:

  • QIBM_QSH_CMD_ESCAPE_MSG tells QShell to send my CL program an *ESCAPE message if the exit status is nonzero. That way, my CL program gets errors when a command fails.
  • QIBM_QSH_CMD_OUTPUT controls where the screen from QShell commands gets directed. I can write the output to a file or turn it off completely.
PGM

    DCL VAR(&CMD) TYPE(*CHAR) LEN(1000)

    ADDENVVAR ENVVAR(QIBM_QSH_CMD_ESCAPE_MSG) VALUE(Y) +
              REPLACE(*YES)

    ADDENVVAR ENVVAR(QIBM_QSH_CMD_OUTPUT) +
              VALUE('FILE=/tmp/errorLog.txt') +
              REPLACE(*YES)

    CHGVAR VAR(&CMD) VALUE('export PATH=$PATH:/QOpenSys/usr/bin+
                                             :/usr/local/bin && +
                            7z a logs.zip /home/klemscot/logs')
    STRQSH CMD(&CMD)

ENDPGM

In this version of the program, any output from 7z gets written to a file named /tmp/errorLog.txt. The user won't see those messages, but if something fails and someone calls me asking for help, I can look at the error log. If there's an error, the CL program also gets an *ESCAPE message, causing it to fail. You could, of course, insert the MONMSG to catch the error if you want to handle it in the program.

As you can see, using QShell makes the error handling a lot simpler.

Download & Links

You can download the 7-Zip software from my website.

The official page for 7-Zip (oriented towards Windows) is 7-zip.org.

The official page of the Unix port of 7-Zip (which is what I discussed in this article) is sourceforge.net/projects/p7zip.

Jim, the JAR utility included with Java runs very slowly, in my experience. It does not support all of the features of the ZIP format, either (Encryption, for example). Furthermore, JAR only supports Zip format.

Brian, the zip/unzip utilities for AIX (the InfoZip software) are better than the JAR tool, but still only support ZIP format. The encryption that InfoZip supports is much weaker than what 7zip supports. 7zip's native format, although different from ZIP, proviides a better compression ratio. 7z supports many file formats, including gzip, bzip2, 7z, zip, tar, arj, rar, cab, msi... whereas zip/unzip supports only Zip format.

Scott, Is there any advantage to using this vs. using AIX zip binary available in AIX Toolbox ?
Scott, how would you say this utility is different and/or better than the "jar" tool we already have in the JDK on the System i?

ProVIP Sponsors

ProVIP Sponsors