Midrange News for the IBM i Community

Posted by: Bob Cozzi
Rogue Programmer
Cozzi Productions, Inc.
RPG Report 2012
has no ratings.
Published: 03 Apr 2012
Revised: 30 Sep 2014 - 2921 days ago
Last viewed on: 26 Sep 2022 (4222 views) 

Using IBM i? Need to create Excel, CSV, HTML, JSON, PDF, SPOOL reports? Learn more about the fastest and least expensive tool for the job: SQL iQuery.

RPG Report - 03 Apr 2012 Published by: Bob Cozzi on 03 Apr 2012 view comments

© Robert Cozzi, Jr. All rights reserved. Reproduction/Redistribution prohibited.
A midrangeNews.com Publication

A True "Copy Spool File" Command

Add COPYSPLF To Your Tool Library

As I continue to complete my upcoming COZTOOLS (Cozzi Tools) library, more and more I realize how poorly designed most IBM i APIs and Exit Points have been. It really seems like whomever designed these things did not actually have any history in using the system. Sure a great Engineer could build an API or design an Exit Point accurately, but what about its user interface? Oh my, we really do have some awkward designs on this system.

It is now nearly 35 years since the original CPF now call IBM i was shipped. In those 35 years, you would have thought IBM had enough time and profit margins to build in a few things that were missing, initially. One such feature that is still missing (unless I'm missing something) is the ability to actually copy a spool file. Now before you send me emails about the CPYSPLF command, I don't mean the CPYSPLF command which is really a "DSPSPLF OUTPUT(*OUTFILE)" command more so than a "CPYSPLF" command. What I want is the ability to actually copy a spool file from one output queue to another, with all its attributes.

At the client's that I support, many have long since moved on to PDFs and email for report distribution, but my fastest growing client base, is small systems shops. Those shops still use outqs and printers to distribute reports to end-users. To move a spool file from one output queue to another, the CHGSPLF (Change Spool File) command, or Option 2 on the WRKOUTQ and WRKSPLF commands are used. The problem is this: once a SPOOL file is "changed" it is moved to the target outq, and removed from the original outq. Where is the desired Copy option?

Sponsored by: BCD Software

Recently I was pushed up against the wall with a request to make a "copy spool file" command work. A client had just invested nearly $100,000 in a new model 720 with v7.1 and is about to order another 4 or 5 of them. They asked how they could copy their daily Reports to another outq. By "copy" that actually meant "copy" and not "move". They have a nightly run that produces sales reports into an outq; I'll call it outq "NIGHTLY". Then either that night or the next morning, someone will use WRKOUTQ NIGHTLY and copy those SPOOL files to an outq associated with a real physical printer, for example OUTQ(HP3000) and print everything. The reports are printed and the HP3000 outq is now empty. Certainly SAVE(*YES) could keep them in the HP3000 outq, but they don't want all that clutter.

What they want is for the NIGHLY outq to retain the SPOOL files when they are "copied" to the HP3000 outq. Then once printed from HP3000 they're gone from it, but still over in NIGHTLY "just in case". So where is option 3=Copy on WRKOUTQ?  There is no "copy spool file" command on this system; and that is very disappointing.

Normally I am an application developer and trainer. I don't do much in the way of Systems Admin activities, but I have been doing a lot with Remote Output Queues (RMTOUTQ) and DDM lately. It suddenly occurred to me that perhaps using some networking or systems admin ninja tactics there might be a way to fake-out the system into doing a real COPY SPOOL FILE; and I was right.


I've been using the SNDTCPSPLF command to send a report to from one location to another pretty often. I never use SNDNETSPLF because that command require enrollment in something I haven't needed or used for more than 20 years. With SNDTCPSPLF I thought, what if I sent the SPOOL File to the local system? Most systems have a Host Table or DNS entry for the local system. In the Host table its normally the entry named LOOPBACK. I wondered what would happen if I did a SNDTCPSPLF RMTSYS(LOOPBACK)...

I wrote down the SPPOL file info for a compile listing I had in my own output queue, and typed in the following command:


The SPOOL file was in my own COZZI outq, and after running the above CL command, it was copied (technically it was sent) to the TESTQ in QUSRSYS on my local system.

Bam! I have a CPYSPLF command that actually copies a SPOOL file!

Cozzi Tools COPYSPLF Command

To make the command a bit simpler, I created a COPYSPLF command that wraps the SNDTCPSPLF command and simplifies its parameters. Here's the Command Definition Source:

 COPYSPLF:   CMD        PROMPT('COZZI - Copy Spool File')               
             PARM       KWD(SPLFNAME) TYPE(*NAME) LEN(10) MIN(1) +      
                          EXPR(*YES) PROMPT('Spooled file name')        
             PARM       KWD(OUTQ) TYPE(OUTQUAL) MIN(1) PROMPT('Copy +   
                          to OUTQ')                                     
 OUTQUAL:    QUAL       TYPE(*NAME) LEN(10) MIN(1) EXPR(*YES)           
             QUAL       TYPE(*NAME) LEN(10) DFT(*LIBL) SPCVAL((*LIBL)) +
                          EXPR(*YES) PROMPT('OUTQ Library')             
             PARM       KWD(SPLNBR) TYPE(*CHAR) LEN(6) DFT(*ONLY) +     
                          RANGE(000000 999999) SPCVAL((*ONLY *N) +      
                          (*LAST *N) (*ANY *N)) EXPR(*YES) +            
                          PROMPT('Spooled file number')                 
             PARM       KWD(JOB) TYPE(JOBQUAL) DFT(*) SNGVAL((*)) +     
                          PROMPT('Job name')                            
 JOBQUAL:    QUAL       TYPE(*NAME) LEN(10) EXPR(*YES)                  
             QUAL       TYPE(*NAME) LEN(10) EXPR(*YES) PROMPT('User')   
             QUAL       TYPE(*CHAR) LEN(6) RANGE(000000 999999) +       
                          FULL(*YES) EXPR(*YES) CHOICE('000000 - +      
                          999999') PROMPT('Number')                     

The only required parameters are the SPLFNAME and new OUTQ parameter. The remaining parameters have the usual default values. Here is the CL source that runs when the COPYSPLF command is entered:

             DCL        VAR(&SPLFNAME) TYPE(*CHAR) LEN(10)            
             DCL        VAR(&OUTQ) TYPE(*CHAR) LEN(20)                
             DCL        VAR(&SPLFNBR) TYPE(*CHAR) LEN(6)              
             DCL        VAR(&JOB) TYPE(*CHAR) LEN(26)                 
             DCL        VAR(&JOBNAME) TYPE(*CHAR) STG(*DEFINED) +     
                          LEN(10) DEFVAR(&JOB 1)                      
             DCL        VAR(&JOBUSER) TYPE(*CHAR) STG(*DEFINED) +     
                          LEN(10) DEFVAR(&JOB 11)                     
             DCL        VAR(&JOBNBR) TYPE(*CHAR) STG(*DEFINED) +      
                          LEN(6) DEFVAR(&JOB 21)                      
             DCL        VAR(&PRTQ) TYPE(*CHAR) LEN(21)                
             MONMSG     MSGID(CPF0000)                                
             CHGVAR     VAR(&PRTQ) VALUE(%SST(&OUTQ 11 10) *TCAT '/' +
                          *CAT %SST(&OUTQ 01 10))                     
                          JOB(&JOBNBR/&JOBUSER/&JOBNAME) + 
                          SPLNBR(&SPLFNBR) DESTTYP(*AS400) +         
             MONMSG     MSGID(TCP0000) EXEC(DO)                      
                          failed on SNDTCPSPLF command. The *LPD +   
                          service is required to copy spool files. + 
                          Make sure that STRTCPSVR SERVER(*LPD) has +
                          been run and *LPD is started. See joblog + 
                          for details.')                             
 ENDPGM:     ENDPGM                                                  

The command simply routes the COPY SPOOL File request to the SNDTCPSPLF command with RMTSYS(LOOPBACK) as the target. Certainly if you want to bypass the Cozzi Tools COPYSPLF command, you can simply enter the parameters into SNDTCPSPLF on the Command Line of the WRKOUTQ and WRKSPLF commands.

Note that in order to make this work, your local system must have the *LPD TCP/IP server started. Most shops already have this started, as it is required for Remote Output Queues to function properly. To start the service, run the following command:



If you're environment has the requirement to copy several SPOOL files to another outq, you can insert an EXIT POINT (aka "Exit Program") into the system that maps to my COPYSPLF command. It actually creates and exit program used by the IBM -supplied WRKSPLF, WRKOUTQ and WRKJOB commands. By installing this option, from a WRKSPLF or WRKOUTQ command, type a "C" next to the SPOOL file you want to Copy, and the COPYSPLF command is prompted with all the ugly SPOOL attributes already filled in.

To install this Exit point on your IBM i system, run the following Command:

             FORMAT(LASP0100) PGMNBR(*HIGH) 
             TEXT('Enable true COPY SPOOL FILE to 
             another OutQ') CRTEXITPNT(*YES) 
             PGMDTA(*JOB 1 'C') 

Exit programs are installed permanently and are syste-wide. So you'd need to remove it using the RMVEXITPGM command in order to get rid of it.

The exit point I'm using here is QIBM_QSP_SPLF_LSTACT and is connected to the WRKOUTQ, WRKSPLF and WRKJOB panels. It allows you to insert your own user-defined options for those panels. In this example, I added option "C" to these panels/screens.

As is the case with most API-related components, Exit Programs/Exit Points are only partially useful. Sure it will call your program, but it will not pass the Command Line values to your program. So if you typed in option C next to 7 different SPOOL files, and then typed "OUTQ(TESTQ) on the Command line, you might assume it would behave just like option 2 does and add that parameter to the command be performed. Of course it doesn't, that would be intuitive.

Instead, you have force the prompter and type in the target outq for everyone SPOOL you want to copy. Pretty dumb design, isn't it?

In the example above, I add a program named COZSPLEXIT (Cozzi Tools SPOOL File Exit Program) to the entry point. It is evoked when the end-user types in option "C" next to a SPOOL file. Here's the COZSPLEXIT source code:

             DCL        VAR(&EXITPOINT) TYPE(*CHAR) LEN(20)         
             DCL        VAR(&FORMAT) TYPE(*CHAR) LEN(8)             
             DCL        VAR(&OPTION) TYPE(*CHAR) LEN(1)             
             DCL        VAR(&SPLFINFO) TYPE(*CHAR) LEN(128)         
             DCL        VAR(&SPLFIDLEN) TYPE(*INT) LEN(4)           
             DCL        VAR(&SPJOBNAME) TYPE(*CHAR) STG(*DEFINED) + 
                          LEN(10) DEFVAR(&SPLFINFO 01)              
             DCL        VAR(&SPJOBUSR) TYPE(*CHAR) STG(*DEFINED) +  
                          LEN(10) DEFVAR(&SPLFINFO 11)              
             DCL        VAR(&SPJOBNBR) TYPE(*CHAR) STG(*DEFINED) +  
                          LEN(6) DEFVAR(&SPLFINFO 21)               
             DCL        VAR(&SPSPLFNAME) TYPE(*CHAR) STG(*DEFINED) +
                          LEN(10) DEFVAR(&SPLFINFO 27)              
             DCL        VAR(&SPSPLFNBR) TYPE(*INT) STG(*DEFINED) +  
                          LEN(4) DEFVAR(&SPLFINFO 37)               
             DCL        VAR(&SPSYSJOB) TYPE(*CHAR) STG(*DEFINED) +  
                          LEN(8) DEFVAR(&SPLFINFO 41)              
             DCL        VAR(&SPCRTDATE) TYPE(*CHAR) STG(*DEFINED) +
                          LEN(7) DEFVAR(&SPLFINFO 49)              
             DCL        VAR(&SPCRTTIM) TYPE(*CHAR) STG(*DEFINED) + 
                          LEN(6) DEFVAR(&SPLFINFO 56)              
             DCL        VAR(&SPOUTQ) TYPE(*CHAR) STG(*DEFINED) +   
                          LEN(10) DEFVAR(&SPLFINFO 62)             
             DCL        VAR(&SPOUTQLIB) TYPE(*CHAR) STG(*DEFINED) +
                          LEN(10) DEFVAR(&SPLFINFO 72)             
             DCL        VAR(&SPLFNAME) TYPE(*CHAR) LEN(10)         
             DCL        VAR(&OUTQ) TYPE(*CHAR) LEN(20)             
             DCL        VAR(&SPLFNBR) TYPE(*CHAR) LEN(6)           
             DCL        VAR(&JOB) TYPE(*CHAR) LEN(26)              
             DCL        VAR(&JOBNAME) TYPE(*CHAR) STG(*DEFINED) +  
                          LEN(10) DEFVAR(&JOB 1)                   
             DCL        VAR(&JOBUSER) TYPE(*CHAR) STG(*DEFINED) +  
                          LEN(10) DEFVAR(&JOB 11)                  
             DCL        VAR(&JOBNBR) TYPE(*CHAR) STG(*DEFINED) + 
                          LEN(6) DEFVAR(&JOB 21)                             
             DCL        VAR(&PRTQ) TYPE(*CHAR) LEN(21)                       
             MONMSG     MSGID(CPF0000)                                       
             CHGVAR     VAR(&SPLFNBR) VALUE(&SPSPLFNBR)                      
 COPYSPLF:   ?COPYSPLF SPLFNAME(&SPSPLFNAME) ??OUTQ() +                      
                         SPLNBR(&SPLFNBR) +                                 
             RETURN  /* RETURN TO CALLER */
             /* To register this EXIT Program, run the following command.  */
             ADDEXITPGM EXITPNT(QIBM_QSP_SPLF_LSTACT) +                      
                          FORMAT(LASP0100) PGMNBR(*HIGH) +                   
                          PGM(COZTOOLS/COZSPLEXIT) THDSAFE(*YES) +           
                          TEXT('Enable true COPY SPOOL FILE to +             
                          another OutQ') CRTEXITPNT(*YES) +                  
                          PGMDTA(*JOB 1 'C') /* The PGMDTA parm +            
                          identifies the WRKSPLF, WRKOUTQ option +           
                           that evokes this Exit program 'C' in our +   
                          example */                                   

Exit points require Exit Programs with a defined parameter list. This program incudes the proper parameter list for the QIBM_QSP_SPLF_LSTACT exit.

Note that on the line containing the CL Label COPYSPLF: that the question mark is inserted along with the OUTQ to evoke Selective Prompting. This causes the COPYSPLF command to be prompted and also request that the end-user type in the name of the OUTQ.  Pressing F4 or not pressing F4 on the Exit Program has no effect in your Exit Program.

While I am complaining a lot about the design of the Exit Point interface on the system, it is much easier to use it, than re-inventing the wheel or in this case, the WRKSPLF or WRKOUTQ commands. On my system, when an end-user wants to copy a printout, they can actually do it.

Cozzi Tools 2012 - Beta

The COPYSPLF and related code are included in the upcoming Cozzi Tools. The new Cozzi Tools package contains some very exciting new utilities and tools that I've been building as well as the consolidation of some of the more productive tools I've built over the years. The initial beta is scheduled to be published on May 1, 2012. Watch midrangeNews.com for details, including download links. The beta period will end shortly before the final version is shipped, which is planned for August 7, 2012.

Call Me

Bob Cozzi is a technical advisor to IBM i clients around the world. His specialty is solving difficult problems for his clients, training their programming staffs, and performing system migration/upgrades for small shops. His consulting rates are available online. To contact Bob, send an email to: bob at rpgworld.com

You can subscribe to RPG Report (we call it "follow") by visiting the RPG Report page on midrangeNews.com and then click the FOLLOW link in the table of contents for that page. To unsubscribe, simply click that same link. You must be signed up and signed in to midrangeNews.com to start following RPG Report.

Follow Bob Cozzi on Twitter

Return to midrangenews.com home page.
Sort Ascend | Descend