• The Four Hundred
  • Subscribe
  • Media Kit
  • Contributors
  • About Us
  • Contact
Menu
  • The Four Hundred
  • Subscribe
  • Media Kit
  • Contributors
  • About Us
  • Contact
  • Guru: Quick And Handy RPG Output, Take 2

    October 25, 2021 Ted Holt

    I am pleased today to revisit a topic I wrote about just over seven and a half years ago. I do so for two reasons. First, I’ve made a slight improvement to my routine. Second, I’d like to provide more examples of this routine in action. My previous article suffered from a paucity of examples. I can’t believe I let that happen.

    I’m talking about the writeln subprocedure, a handy routine that I use to write unstructured text to a spooled file. I derived the inspiration for this routine from Pascal, a programming language I used heavily when I was working on my computer science degree. It’s not a substitute for record output (think DDS and O specs), but a different tool for different situations. If you have not read my first article on this subject, please do so before continuing.

    First, the improvement. Sometimes I like to indent certain lines of output for purposes of legibility, e.g. to align data in columns or to present the output in the form of an outline. When I found myself prepending inordinate numbers of blanks to the output string, I decided to add a second, optional parameter to indicate the starting position of the string in the output line. The default value is 1.

    Now, the examples. One of the ways I most use my writeln routine is for batch debugging. “Batch debugging?” I hear you ask. “What’s that?” Very simply this: Rather than running a program interactively under the control of a debugger, endlessly clicking a button or pressing some command key to step through the program, and endlessly repeating some other action to examine the values of variables, I make the program build a spooled file of the same sort of information. When the program finishes, I can read the spooled file to determine how the program behaved and why the program behaved as it did. Batch debugging does not replace the interactive debugger. I use each one as it makes sense.

    Let’s see an example. I’ve thrown together a short piece of nonsensical code that you can run on your own system if you like. Just make sure that library QIWS is in your library list.

    **free
    ctl-opt option(*srcstmt: *nodebugio) actgrp(*new) main(WL01R);
    
    //undefine debugging
    /define debugging
    
    dcl-f  qcustcdt  usage(*input) qualified usropn;
    
    /if defined(debugging)
    dcl-f qsysprt printer(132)  usropn;
    /endif
    
    dcl-proc  WL01R;
    
       dcl-pi *n;
          inState         char  ( 2   )    const;
          ouTotalDue      packed( 7: 2);
       end-pi;
    
       dcl-ds  CustomerRec    likerec(qcustcdt.cusrec);
       dcl-s   Name           varchar(24);
    
    /if defined(debugging)
       open qsysprt;
       writeln ('Enter ' + %proc() + ' ' + %char(%timestamp));
       writeln ('> Selected state =/' + inState + '/');
    /endif
    
       clear ouTotalDue;
    
       open qcustcdt;
    
       dow '1';
          read  qcustcdt.CusRec  CustomerRec;
          if %eof();
             leave;
          endif;
    
          if CustomerRec.State = inState;
             ouTotalDue += CustomerRec.BalDue;
             Name = ReformatName (CustomerRec.LstNam: CustomerRec.Init);
    /if defined(debugging)
             writeln ('Selected customer ' +
                       %editc(CustomerRec.CusNum: '4') +
                       '  Balance = ' +
                       %editc(CustomerRec.BalDue: 'L') : 3);
             writeln ('Name=/' + Name + '/': 3);
    /endif
          endif;
    
    
       enddo;
    
    /if defined(debugging)
       writeln ('> Total due = ' + %editc(ouTotalDue: 'L'));
       writeln ('Leave ' + %proc() + ' ' + %char(%timestamp));
    /endif
    
       close *all;
       return;
    end-proc  WL01R;
    
    dcl-proc  ReformatName;
    
       dcl-pi *n          varchar(24);
          inLastName      varchar(16)    const;
          inInitials      varchar( 3)    const;
       end-pi;
    
    /if defined(debugging)
       writeln ('Enter ' + %proc() : 3);
       writeln ('> Last=/' + inLastName +
                '/ Init=/' + inInitials + '/' : 3);
    /endif
    
       return %subst(inInitials: 1: 1) + '. ' +
              %subst(inInitials: 3: 1) + '. ' +
              %trim(inLastName);
    
    end-proc  ReformatName;
    
    /if defined(debugging)
    dcl-proc writeln;
       dcl-pi *n;
          inString   varchar(132)   const;
          inPosition uns(3)         const   options(*nopass);
       end-pi;
    
       dcl-ds   ReportLine   len(132)   end-ds;
       dcl-s    Position     uns(3);
    
       if %parms() >= %ParmNum(inPosition);
          Position = inPosition;
       else;
          Position = 1;
       endif;
    
       %subst(ReportLine: Position) = inString;
       write qsysprt ReportLine;
    
    end-proc writeln;
    /endif
    

    Notice the code that is conditioned to the debugging compiler condition. When I am working on the program, I /define the debugging condition. When the debugging condition is defined, I get a report like this one:

    Enter WL01R 2021-10-25-10.46.45.798914
    > Selected state =/NY/
      Enter REFORMATNAME
      > Last=/Jones   / Init=/B D/
      Selected customer 839283  Balance =  100.00
      Name=/B. D. Jones/
      Enter REFORMATNAME
      > Last=/Tyron   / Init=/W E/
      Selected customer 397267  Balance =     .00
      Name=/W. E. Tyron/
      Enter REFORMATNAME
      > Last=/Lee     / Init=/F L/
      Selected customer 192837  Balance =  489.50
      Name=/F. L. Lee/
    > Total due =   589.50
    Leave WL01R 2021-10-25-10.46.45.799561
    

    What you print is up to you. I find myself adding, removing, and modifying writeln commands as I work on the program.

    When I am ready to put the program into production, I /undefine the debugging condition. The debugging code is still there for the next time I work on the program if I should need it.

    Another way I commonly use the writeln routine is to log the progress of a program as it works through step after step. I’ll use the same illogical program to illustrate.

    **free
    ctl-opt option(*srcstmt: *nodebugio) actgrp(*new) main(WL02R);
    
    dcl-f  qcustcdt  usage(*input) qualified usropn;
    
    dcl-f qsysprt printer(132)  usropn;
    
    dcl-proc  WL02R;
    
       dcl-pi *n;
          inState     char  (  2   )  const;
          ouTotalDue  packed(  7: 2);
          inOptions   char  ( 80   )  const options(*nopass);
       end-pi;
    
       dcl-s   Logging        ind;
       dcl-ds  CustomerRec    likerec(qcustcdt.cusrec);
       dcl-s   Name           varchar(24);
    
       if %parms() >= %parmnum(inOptions)
       and %scan('NOLOG': inOptions) = *zero
       and %scan('LOG'  : inOptions) > *zero;
          Logging = *on;
       endif;
    
       if Logging;
          open qsysprt;
          writeln ('Enter ' + %proc() + ' ' + %char(%timestamp));
          writeln ('> Selected state =/' + inState + '/');
       endif;
    
       clear ouTotalDue;
    
       open qcustcdt;
    
       dow '1';
          read  qcustcdt.CusRec  CustomerRec;
          if %eof();
             leave;
          endif;
    
          if CustomerRec.State = inState;
             ouTotalDue += CustomerRec.BalDue;
             Name = ReformatName (CustomerRec.LstNam: CustomerRec.Init:
                                  Logging);
             if Logging;
                writeln ('Selected customer ' +
                          %editc(CustomerRec.CusNum: '4') +
                          '  Balance = ' +
                          %editc(CustomerRec.BalDue: 'L') : 3);
                writeln ('Name=/' + Name + '/': 3);
             endif;
          endif;
    
       enddo;
    
       if Logging;
          writeln ('> Total due = ' + %editc(ouTotalDue: 'L'));
          writeln ('Leave ' + %proc() + ' ' + %char(%timestamp));
       endif;
    
       close *all;
       return;
    end-proc  WL02R;
    
    dcl-proc  ReformatName;
    
       dcl-pi *n          varchar(24);
          inLastName      varchar(16)    const;
          inInitials      varchar( 3)    const;
          inLogging       ind            const;
       end-pi;
    
       if inLogging;
          writeln ('Enter ' + %proc() : 3);
          writeln ('> Last=/' + inLastName +
                   '/ Init=/' + inInitials + '/' : 3);
       endif;
    
       return %subst(inInitials: 1: 1) + '. ' +
              %subst(inInitials: 3: 1) + '. ' +
              %trim(inLastName);
    
    end-proc  ReformatName;
    
    dcl-proc writeln;
       dcl-pi *n;
          inString   varchar(132)   const;
          inPosition uns(3)         const   options(*nopass);
       end-pi;
    
       dcl-ds   ReportLine   len(132)   end-ds;
       dcl-s    Position     uns(3);
    
       if %parms() >= %ParmNum(inPosition);
          Position = inPosition;
       else;
          Position = 1;
       endif;
    
       %subst(ReportLine: Position) = inString;
       write qsysprt ReportLine;
    
    end-proc writeln;
    

    The debugging compiler condition is gone. The printer file, the writeln routine, all the writeln calls, and the printer file open are compiled into the object code and are controlled by a variable named LOGGING in the main subprocedure. Whether the report is produced or not is controlled through an optional options parameter.

    I can put the word LOG or NOLOG in the options parameter when I call the program. If I specify LOG, the program produces the same report shown above. I can easily enable and disable the report by changing the command string in the job scheduler.

    The way I interpret the options parameter in this example is not foolproof, but it’s robust enough for my purposes, as I know that I will not put a value like FLOG or APOLOGY or HYDROBIOLOGICAL into the parameter string.

    What we do now is no different from what people have been doing since the earliest days of business computing. We process input to create output. Output can be formal or informal. Both types have their place. My writeln routine has been invaluable to me in producing both types of output. If you can improve my routine, please do so and let me know about it.

    RELATED STORY

    Quick And Handy RPG Output

    Share this:

    • Reddit
    • Facebook
    • LinkedIn
    • Twitter
    • Email

    Tags: Tags: 400guru, FHG, Four Hundred Guru, IBM i, RPG

    Sponsored by
    ARCAD Software

    Embrace VS Code for IBM i Development

    The IBM i development landscape is evolving with modern tools that enhance efficiency and collaboration. Ready to make the move to VS Code for IBM i?

    Watch this webinar where we showcase how VS Code can serve as a powerful editor for native IBM i code and explore the essential extensions that make it possible.

    In this session, you’ll discover:

    • How ARCAD’s integration with VS Code provides deep metadata insights, allowing developers to assess the impact of their changes upfront.
    • The role of Git in enabling seamless collaboration between developers using tools like SEU, RDi, and VS Code.
    • Powerful extensions for code quality, security, impact analysis, smart build, and automated RPG conversion to Free Form.
    • How non-IBM i developers can now contribute to IBM i projects without prior knowledge of its specifics, while ensuring full control over their changes.

    The future of IBM i development is here. Let ARCAD be your guide!

    Watch the replay now!

    Share this:

    • Reddit
    • Facebook
    • LinkedIn
    • Twitter
    • Email

    HelpSystems Goes Phishing For Cyber Threat Hunter Talking Power Systems Deals With The Boss

    2 thoughts on “Guru: Quick And Handy RPG Output, Take 2”

    • Dale Janus says:
      October 25, 2021 at 11:42 am

      how should WriteLn be compiled? I get error no open, among other errors

      Reply
    • Ted Holt says:
      November 3, 2021 at 11:18 am

      Hi, Dale. You’ll have to open the printer file yourself.

      I include writeln in every source member where I use it. Someday I may turn it into a standalone module that you’ll just link to, and if I do, the module will handle the open on the first call to writeln.

      Reply

    Leave a Reply Cancel reply

TFH Volume: 31 Issue: 70

This Issue Sponsored By

  • Fresche Solutions
  • UCG Technologies
  • Connectria
  • Raz-Lee Security
  • WorksRight Software

Table of Contents

  • A Proper Accounting Of The Power Business
  • Talking Power Systems Deals With The Boss
  • Guru: Quick And Handy RPG Output, Take 2
  • HelpSystems Goes Phishing For Cyber Threat Hunter
  • Various Power Systems Updates And Tweaks

Content archive

  • The Four Hundred
  • Four Hundred Stuff
  • Four Hundred Guru

Recent Posts

  • POWERUp 2025 –Your Source For IBM i 7.6 Information
  • Maxava Consulting Services Does More Than HA/DR Project Management – A Lot More
  • Guru: Creating An SQL Stored Procedure That Returns A Result Set
  • As I See It: At Any Cost
  • IBM i PTF Guide, Volume 27, Number 19
  • IBM Unveils Manzan, A New Open Source Event Monitor For IBM i
  • Say Goodbye To Downtime: Update Your Database Without Taking Your Business Offline
  • i-Rays Brings Observability To IBM i Performance Problems
  • Another Non-TR “Technology Refresh” Happens With IBM i TR6
  • IBM i PTF Guide, Volume 27, Number 18

Subscribe

To get news from IT Jungle sent to your inbox every week, subscribe to our newsletter.

Pages

  • About Us
  • Contact
  • Contributors
  • Four Hundred Monitor
  • IBM i PTF Guide
  • Media Kit
  • Subscribe

Search

Copyright © 2025 IT Jungle