• The Four Hundred
  • Subscribe
  • Media Kit
  • Contributors
  • About Us
  • Contact
Menu
  • The Four Hundred
  • Subscribe
  • Media Kit
  • Contributors
  • About Us
  • Contact
  • Write Your Own SCANRPL Built-In Function

    November 28, 2012 Bob Cozzi

    Note: The code accompanying this article is available for download here.

    Until IBM i v7.1 has made a pervasive appearance on virtually all IBM systems, the cool new v7.1 updates to RPG IV are out of reach for many RPG developers. Fortunately, with IBM i v7.1, IBM has finally stopped “point release” updates to the RPG compiler, giving us the ability to use anything implemented on (for example) technology refresh 5 all the way back to TR1. Life is good for RPG developers today.

    If you’re a software developer or, like me, consulting with clients that have everything from v5r4 through the latest and greatest release of IBM i, you can only envy those who have nothing more than v7.1 to burden their development cycle. One of those cool v7.1 enhancements is the %SCANRPL (Scan and Replace) built-in function. For years we’ve had the %SCAN and %REPLACE built-in functions, but the %REPLACE really never caught on. Sure a few people used it, but it really didn’t make sense to those writing end-user business applications or doing program maintenance. Using two built-in functions to accomplish what seems like one task? Fahgettaboudit.

    To that end, back some years ago, I wrote a FINDREPLACE (Find and Replace) subprocedure in RPG IV that basically did what IBM introduced in v7.1, and more. But while the FINDREPLACE subprocedure is cool, it included too many find/replace options that I mimicked from Microsoft Word Find/Replace dialogue box, things like “Match Case” and “Whole Word Only.”

    With RPG IV at v7.1, we now have a design for a subprocedure that could work on v5r4 and later and then easily be ported to %SCANRPL, when everyone is on v7.1 or later.

    The RPG IV %SCANRPL built-in function has five parameters:

    %SCANRPL( scan-pattern   :   replacement-text   : scanned-variable 
      [: start-position [ : length ]] );
    

    1. Scan-pattern is the text you are searching for.
    2. Replacement-text is the text to insert into the scanned text in place of the scan-pattern.
    3. Scanned-variable is the text to be searched. This is normally a fixed-length field, but can also be a varying field.
    4. Start-position [optional] is the first character in the scanned variable to be searched for the scan pattern. The default start position is 1.
    5. Length [optional] is the number of bytes in the scanned variable to be searched for the scan pattern beginning with the start position.

    To construct an operating system level, agnostic version of SCANRPL, we need to carefully define the parameter list of a subprocedure; a subprocedure named SCANRPL (no leading percent sign).

    To do this, a quick look at the %SCANRPL parameters indicates that the first three are character parameters and the final two are integers. It should be relatively simple to define a prototype with these basic characteristics:

    D scanRPL         PR         65533A   Varying
    D                                     extProc('COZZI_scanAndReplace')
    D pattern                      256A   Const Varying
    D replacement                  256A   Const Varying
    D searchData                 65533A   Const Varying
    D searchStart                   10I 0 Const OPTIONS(*NOPASS)
    D searchLen                     10I 0 Const OPTIONS(*NOPASS)
    

    Since the IBM version of %SCANRPL does not modify any of the input parameters, I’ve included the CONST keyword for all of the parameters of the subprocedure. CONST makes it easier to pass values to subprocedures by doing some minimal conversion for you. In addition, since the scan-pattern and replacement-text is typically not that long, I’ve shortened these parameters to 256 characters and made them VARYING. Increase their lengths if you need to, but I’ve found this size to be good enough. The benefit of VARYING and CONST is that I can use the %LEN RPG built-in function to extract the length of the data passed on these parameters. This greatly simplifies things.

    The third parameter, searchData is the text that is searched for the pattern parameter. It, too, is CONST VARYING, but has a maximum length of 64k-ish. On v7.1, this limit is removed, and parameters may be declared with lengths of up to 16 MB, so this is one difference with SCANRPL (the subprocedure) versus %SCANRPL (the built-in function).

    The starting position and length (parameters 4 and 5) are both defined as 4-byte integers. RPG uses the interesting “10i 0” nomenclature to define 4-byte integers, and “5i 0” to define 2-byte integers. Again here, the CONST keyword allows the caller of this subprocedure to pass in an expression or a numeric literal or a variable. If that value isn’t identical to the parameter definition, CONST converts the value into a 4-byte integer that the subprocedure understands. Last, I’ve included the OPTIONS(*NOPASS) keyword. This tells the compiler that the caller does not need to specify either of these parameters. Only parameters that do not include OPTIONS(*NOPASS) are required.

    The last part of the SCANRPL subprocedure is the returned value. This is defined on the first line of code in a prototype of a subprocedure. It defines the data being sent back to the caller on a conditional statement, such as IF, or on an assignment statement, such as EVAL. In the context of our SCANRPL subprocedure, the returned value needs to have properties identical to that of the third parameter (searchData in our prototype).

    Without seeing the body or implementation of this subprocedure, this is how it would look in your code versus the native RPG IV %SCANRPL built-in function:

    Native RPG %SCANRPL Built-in Function:

    myName = %scanRPL('Bob' : 'Robert' : myName );
    

    Custom SCANRPL Subprocedure:

    myName = scanRPL('Bob' : 'Robert' : myName );
    

    The only difference is the percent sign for the built-in function versus no percent sign for the subprocedure. We could even take it on step further:

    /IF DEFINED(*V7R1M0) 
        myName = %scanRPL('Bob' : 'Robert' : myName ); 
    /ELSE
        myName = scanRPL('Bob' : 'Robert' : myName );
    /ENDIF
    

    In this example, if the compiler is targeting v7.1 it uses the native RPG %SCANRPL built-in function, otherwise it uses our custom subprocedure. This way when the code is finally compiled for v7.1 it automatically uses the %SCANRPL built-in function. Pretty cool, huh?

    What’s that? Oh, you want to see the body/implementation of the SCANRPL subprocedure? Okay, here’s the full source code, but note: The /INCLUDE at the top of the source is simply including the prototype for the SCANRPL subprocedure (which is listed earlier in this article), that way you can include this in a service program and use it in all your apps without duplicating it all over the place.

     /include qcpysrc,scanRPL
    
    *********************************************************
    **  ScanRPL - mimic the v7r1  %SCANRPL built-in function
    **  myName = %scanRPL('Bob' : 'Robert' : myName );
    **  myName = scanRPL('Bob' : 'Robert' : myName );
    *********************************************************
    D buffer          DS                  Qualified Inz
    D  data                      65533A   VARYING
    
    *********************************************************
    **  ScanRPL - mimic the v7r1  %SCANRPL built-in function
    *********************************************************
    P scanRpl         B                   Export
    D scanRpl         PI         65533A   Varying
    D pattern                      256A   Const Varying
    D replacement                  256A   Const Varying
    D searchedData               65533A   Const Varying
    D searchStart                   10I 0 Const OPTIONS(*NOPASS)
    D searchLen                     10I 0 Const OPTIONS(*NOPASS)
    
    D nPos            S             10I 0
    D findLen         S             10I 0
    D rplen           S             10I 0
    D start           S             10I 0
    D findStart       S             10I 0 Inz(1)
     /free
          findLen = %Len(searchedData);  // Length of data to be searched
          if (%Parms()>= 4 and %addr(searchStart) <> *NULL);
             if (searchStart > 0);
                findStart = searchStart;
             endif;
          endif;
          if (%Parms()>= 5 and %addr(searchLen) <> *NULL);
             if (searchLen > 0); // Overriding search length?
                findLen = searchLen;
             endif;
          endif;
    
          if (findLen = 0 or findLen > %len(searchedData));
              findLen = %len(searchedData);
          endif;
    
          buffer.data = searchedData;
          rplen = %len(replacement);
          nPos = %scan( pattern : buffer.data : findStart);
          dow (nPos > 0 and nPos <= %len(buffer.data));
             buffer.data =
                  %Replace(replacement:buffer.Data:nPos: %len(pattern));
             if ((nPos + rpLen) < %Len(buffer.data));
                nPos = %scan( pattern : buffer.data : nPos + rpLen);
             else;
                nPos = 0;
             endif;
          enddo;
          return buffer.Data;
     /end-free 
    P scanRpl         E
    

    I enjoy writing subprocedures that help me write applications faster. I have noticed that many RPG developers seem to prefer to embrace their legacy habits more than learning new techniques. For example, one developer told me, “I can use SEU and copy/paste my code faster than writing a subprocedure.” Another said, “I can’t see the /COPYs in my code, so I prefer using copy/paste in SEU.” Yikes! But more and more I see a new attitude among RPG developers, one that embraces new technologies, albeit at a bit slower adoption rate than I would prefer.

    Bob Cozzi is developer of COZTOOLS, the popular collection of CL and RPG functions. You can reach him at www.cozTools.com or via email at bob@coztools.com.



                         Post this story to del.icio.us
                   Post this story to Digg
        Post this story to Slashdot

    Share this:

    • Reddit
    • Facebook
    • LinkedIn
    • Twitter
    • Email

    Tags:

    Sponsored by
    Raz-Lee Security

    Start your Road to Zero Trust!

    Firewall Network security, controlling Exit Points, Open DB’s and SSH. Rule Wizards and graphical BI.

    Request Demo

    Share this:

    • Reddit
    • Facebook
    • LinkedIn
    • Twitter
    • Email

    Sponsored Links

    ASNA:  Create great mobile apps with ILE RPG. Period.
    looksoftware:  Achieving the impossible with RPG Open Access. Live webcast Dec 4 & 5.
    ITJ Bookstore:  Bookstore BLOWOUT!! Up to 50% off all titles! Everything must go! Shop NOW

    IT Jungle Store Top Book Picks

    Bookstore Blowout! Up to 50% off all titles!

    The iSeries Express Web Implementer's Guide: Save 50%, Sale Price $29.50
    The iSeries Pocket Database Guide: Save 50%, Sale Price $29.50
    Easy Steps to Internet Programming for the System i: Save 50%, Sale Price $24.97
    The iSeries Pocket WebFacing Primer: Save 50%, Sale Price $19.50
    Migrating to WebSphere Express for iSeries: Save 50%, Sale Price $24.50
    Getting Started with WebSphere Express for iSeries: Save 50%, Sale Price $24.50
    The All-Everything Operating System: Save 50%, Sale Price $17.50
    The Best Joomla! Tutorial Ever!: Save 50%, Sale Price $9.98

    Jinfonet Speeds In-Memory OLAP with ‘Push Down’ Technology Power Systems Cloud Builders Get Huge Discounts

    Leave a Reply Cancel reply

Volume 12, Number 28 -- November 28, 2012
THIS ISSUE SPONSORED BY:

WorksRight Software
ProData Computer Services
Sirius Computer Solutions

Table of Contents

  • Write Your Own SCANRPL Built-In Function
  • Glenn Wants To Know More Facts About Special Values
  • Admin Alert: Strategically Using Power Systems’ Processor Trial Capacity On Demand

Content archive

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

Recent Posts

  • Public Preview For Watson Code Assistant for i Available Soon
  • COMMON Youth Movement Continues at POWERUp 2025
  • IBM Preserves Memory Investments Across Power10 And Power11
  • Eradani Uses AI For New EDI And API Service
  • Picking Apart IBM’s $150 Billion In US Manufacturing And R&D
  • FAX/400 And CICS For i Are Dead. What Will IBM Kill Next?
  • Fresche Overhauls X-Analysis With Web UI, AI Smarts
  • Is It Time To Add The Rust Programming Language To IBM i?
  • Is IBM Going To Raise Prices On Power10 Expert Care?
  • IBM i PTF Guide, Volume 27, Number 20

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