04
Mon, Nov
1 New Articles

Changing Case with RPG IV Subprocedures

RPG
Typography
  • Smaller Small Medium Big Bigger
  • Default Helvetica Segoe Georgia Times

In all my years of programming, I've rarely seen AS/400 data stored in anything but uppercase.
However, there have been many times when I've needed to present AS/400 data in mixed or
lowercase as well. For example, I recently got an assignment to extract some customer
information from a set of AS/400 database files. As usual, the data on the AS/400 was stored in
uppercase. The specifications called for the name and address to be converted to mixed case.
(That is, the first character of each word in uppercase, with all other characters in lowercase).
Furthermore, the customer's email address needed to be converted to lowercase.
I've written RPG III routines that performed this type of conversion in the past. Generally,
however, these routines weren't reusable by other programs, and the ones that were tended to not
perform very well. This time, I wanted to do it right. By taking advantage of a relatively new
enhancement to RPG IV called subprocedures (see "RPG IV Subprocedures," MC, August
1996), I was able to write routines that are both reusable and efficient.
In this article, I'll share with you the results of those efforts. You may find these routines useful
in your shop if you need to convert data between upper-, lower-, and mixed case. Even if you
don't perform these types of conversions, I encourage you to keep reading. The techniques used
here could be applied to many other situations in which code reuse and good performance are
important.

I'll start out by giving you a brief overview of subprocedures and why they're so useful. If you've
ever used any of the RPG IV built-in function-such as %SIZE, %SUBST, or %TRIM-then you
already know how much easier they are to use than other comparable methods that perform the
same task. Now, imagine that you can create your own built-in functions! That's the idea behind
subprocedures. You can think of them as user-written functions as opposed to RPG IV's built-in
functions.

There are many benefits to using subprocedures in your applications instead of using older
methods such as executing subroutines or calling external programs. What I like most about using subprocedures is that they can be embedded into expressions just like RPG IV's built-in
functions. This simplifies the code, making it easier to maintain. These expressions are normally
coded on an EVAL statement but can also be used with IF, DOW, DOU, and WHEN statements.
I'm going to introduce you to three subprocedures-UpperCase, LowerCase, and MixedCase.
Figure 1 shows an example of a program that uses these three subprocedures. As you can see in
the highlighted Eval statements, converting data to mixed case is as easy as coding MIXED =
MixedCase(DATA). In this example, the field DATA contains the data you want to convert, and
the field MIXED is used to store the results of the conversion.

To allow for a high degree of code reuse, I've placed these three subprocedures into a service
program called CASESRV. This eliminates the need to have to copy this code into each program
that wants to use it, as you would if they were coded as subroutines. This also allows the routines
to execute more efficiently than if they were coded into standalone programs.

To use this code in your applications, you need to perform three steps. Step 1 is to create the
/COPY member shown in Figure 2. This member contains code that is used by both the service
program and the program using the service program. Step 2 is to create the service program
shown in Figure 3. This service program contains the three subprocedures: UpperCase,
LowerCase, and MixedCase. Step 3 is to add code to one of your application programs to
execute one or more of the subprocedures. An example of this is shown in Figure 1. The only
changes you need to make are to the /COPY statements in the service program and the program
that uses it. Just be sure the /COPY statements point to the library, source file, and member
containing the /COPY member shown in Figure 2.

Let's take a closer look at each of the components of this example. The /COPY member shown in
Figure 2 contains three procedure prototypes. A procedure prototype describes the parameters
that are passed to the subprocedure and the value that the subprocedure passes back to the
program. The parameters and the returned value are known as the interface to the subprocedure.
The compiler uses the procedure prototypes to validate the interface. It's important to note that
this validation takes place at compile time, whereas parameters passed to externally called
programs are validated at runtime. The reason for placing these prototypes in a /COPY member
is that they are required in both the program containing the subprocedures and the program
calling the subprocedures. Since they must be identical in both places, a common practice is to
put them into a /COPY member rather than duplicate the code.

Each procedure prototype in Figure 2 contains two D-specs. The first D-spec has several
columns of information, starting with the name of the subprocedure. The next column contains
the declaration type PR, which identifies this as a procedure prototype. The length of 32767 tells
the compiler that the subprocedure returns a character string of up to 32,767 bytes. The OPDESC
keyword tells the compiler to pass the operational descriptor to the subprocedure. The
operational descriptor contains additional information about the parameters passed to the
subprocedure (more on operational descriptors in a moment).

The second line of code for each prototype describes the parameter that's passed to the
subprocedure. The name (in this example, String) is optional and is coded here for
documentation purposes only. The field itself is not defined, and no storage is allocated for it.
The number 32767 describes the maximum length of the parameter. The keyword
Single user only. Do not copy. No LAN, WAN or Intranet uses Copyright IIR Publications
OPTIONS(*VARSIZE) specifies that this is a variable-length parameter. That is, the length of
the parameter passed to the subprocedure can be anywhere from 1 to 32,767 bytes long. Later,
you'll see how the subprocedure queries the operational descriptor to determine the actual length
of the parameter that's passed at runtime.

Near the top of Figure 1, you'll see a /COPY statement that copies in the procedure prototypes
shown in Figure 2. The highlighted code in Figure 1 shows the statements that execute the
UpperCase, LowerCase, and MixedCase functions. In each case, the DATA variable is passed to
the function. The result is stored in the UPPER, LOWER, and MIXED fields.
Figure 3 shows the code for the service program CASESRV. This program contains the code for
the three subprocedures. The first thing to notice is that the H-spec contains the NOMAIN
keyword. This tells the compiler that the program doesn't contain a main procedure; it contains
only one or more subprocedures. The NOMAIN keyword also instructs the compiler to omit the
code for the RPG cycle.

The next statement in Figure 3 contains another /COPY statement identical to the one in Figure
1. This statement again copies the procedure prototypes shown in Figure 2 into the program. As I
stated earlier, the compiler uses these procedure prototypes to validate the interface to the
subprocedures at compile time. No field definition or storage allocation takes place.

After the /COPY statement in Figure 3, you'll see a set of D-specs. These D-specs define the
global variables for the module. These variables are available to all subprocedures in the module.
Following the global variables are the three subprocedures. Each procedure begins and ends with
a P-spec. These P-specs identify the beginning and ending of the procedures. The beginning Pspec
contains the name of the procedure followed by a B (begin procedure) or an E (end
procedure) in column 24. The EXPORT keywords on the begin procedure statements indicate
that the procedure is to be exported. That is, it can be called by a procedure outside the current
module. In this example, the procedure is defined in service program CASESRV, but it is called
from program CASETST. If the EXPORT keyword were omitted, this type of call would not be
allowed.

The next pair of statements in each of the procedures constitutes what's known as the procedure
interface. A procedure interface is similar to a procedure prototype, with a few exceptions. First,
the definition type is PI (for procedure interface) instead of PR. Second, the name of the
parameter, in this case Input, is not optional as it is with the procedure prototype. In addition,
unlike in the procedure prototype, the field for the parameter is defined, storage is allocated for
it, and it can be accessed within the procedure. The fields defined in the procedure interface will
contain the values passed to the procedure from the calling procedure. In this respect, the
procedure interface serves much the same purpose as the *ENTRY PLIST and PARM statements
used to accept parameters in a traditional OPM RPG program.

In the MixedCase procedure, you'll notice that there are some additional D-Specs following the
procedure interface. These are local variables. That is, they're available only to the MixedCase
procedure.

Earlier, I mentioned that this procedure accepts a variable-length string (1 to 32,767 bytes) as its
parameter. To accommodate this type of parameter, the procedure needs to know the exact length
Single user only. Do not copy. No LAN, WAN or Intranet uses Copyright IIR Publications
of the string passed to it at runtime. It determines this by querying the operational descriptor. The
operational descriptor provides descriptive information to the called procedure regarding a
parameter. In this example, the operational descriptor can be used to determine the length of the
string. This is accomplished by calling the Retrieve Operational Descriptor Information
(CEEDOD) API. This API retrieves the operational descriptor that contains information about
the parameter passed to the procedure. The first parameter on the call to the API (ParmPos) is
initialized to 1, which tells the API to retrieve information about the first parameter passed to the
procedure. The last parameter (DataLen) on the API call will contain the actual length of the
parameter that's passed to the procedure at runtime.

This length value is used to determine how much of the variable needs to be converted to upper-,
lower-, or mixed case. That is, instead of converting all 32,767 bytes of the variable Input, the
procedures convert only the amount of data passed into the procedure. This results in more
efficient code, while still permitting the flexibility of allowing a variable-length parameter to be
passed to the procedure.

In each of the three procedures, the data in the Input variable is converted, and results are placed
in the Output variable. The Output variable is then used on the RETURN statement to pass the
converted data back to the calling procedure. Following the RETURN statement is the ending Pspec
to signify the end of the procedure. Unlike the beginning P-spec, the ending P-spec doesn't
require a procedure name. I chose to code the procedure names in this example for clarity.
The next time you find yourself needing to convert text between upper-, lower-, and mixed case,
I hope you'll consider using the subprocedures described in this article. If you've never used
subprocedures before, these will serve as good examples to help get you started. If you've
already discovered the benefits of subprocedures, you now have three more you can add to your
collection. In my opinion, the goal of subprocedures is to encourage modularity and code reuse
while maintaining efficiency and maintainability. Using subprocedures in your applications will
ultimately serve to improve the quality of the software you write. And that's a goal we should all
embrace.

Robin Klima is a freelance writer for Midrange Computing. He can be reached by email at
This email address is being protected from spambots. You need JavaScript enabled to view it..

ILE RPG/400 Programmer's Guide (SC09-2074, CD-ROM QBJAQD01)
ILE RPG/400 Reference (SC09-2077, CD-ROM QBJAQE01)

Figure 1: This CASETST test program uses three subprocedures to convert
data to mixed case.

 
  • ===============================================================
  • To compile:
  • CRTRPGMOD MODULE(XXX/CASETST) SRCFILE(XXX/QRPGLESRC)
  • CRTPGM PGM(XXX/CASETST) BNDSRVPGM(XXX/CASESRV)
  • =============================================================== /COPY XXX/QRPGLESRC,CASECPY D DATA S 16A D UPPER S LIKE(DATA) Single user only. Do not copy. No LAN, WAN or Intranet uses Copyright IIR Publications D LOWER S LIKE(DATA) D MIXED S LIKE(DATA) C Eval DATA = 'SAMPLE Test data' C Eval UPPER = UpperCase(DATA) C Eval LOWER = LowerCase(DATA) C Eval MIXED = MixedCase(DATA)
  • Field UPPER now contains the value 'SAMPLE TEST DATA'
  • Field LOWER now contains the value 'sample test data'
  • Field MIXED now contains the value 'Sample Test Data' C Eval *INLR = *On

  • Figure 2: The CASECPY copy member contains three
    subprocedure prototypes.

     
  • ===============================================================
  • CASECPY /COPY member
  • =============================================================== D UpperCase PR 32767 OPDESC D String 32767 OPTIONS(*VARSIZE) D LowerCase PR 32767 OPDESC D String 32767 OPTIONS(*VARSIZE) D MixedCase PR 32767 OPDESC D String 32767 OPTIONS(*VARSIZE)
  • Figure 3: The CASESRV service program contains three subprocedures.

     
  • ===============================================================
  • To compile:
  • CRTRPGMOD MODULE(XXX/CASESRV) SRCFILE(XXX/QRPGLESRC)
  • CRTSRVPGM SRVPGM(XXX/CASESRV) SRCFILE(XXX/QRPGLESRC) +
  • EXPORT(*ALL)
  • =============================================================== H NOMAIN /COPY XXX/QRPGLESRC,CASECPY D ParmPos S 10I 0 D DescType S 10I 0 D DataType S 10I 0 D DescInf1 S 10I 0 D DescInf2 S 10I 0 D DataLen S 10I 0 D X S 5 0 D Char S 1 D Output S 32767 D Lower C 'abcdefghijklmnopqrstuvwxyz' D Upper C 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
  • ===============================================================
  • Upper case procedure
  • =============================================================== P UpperCase B EXPORT D UpperCase PI 32767 OPDESC D Input 32767 OPTIONS(*VARSIZE)
  • Retrieve operational descriptor C Callb 'CEEDOD' C Parm 1 ParmPos C Parm DescType C Parm DataType C Parm DescInf1 C Parm DescInf2 C Parm DataLen

    C Eval Output = %Subst(Input:1:DataLen)

  • Convert string to upper case C Do DataLen X C Eval Char = %Subst(Output:X:1) C Lower:Upper Xlate Char Char C Eval %Subst(Output:X:1) = Char C Enddo
  • Return upper case string C Return Output P UpperCase E
  • ===============================================================
  • Lower case procedure
  • =============================================================== P LowerCase B EXPORT D LowerCase PI 32767 OPDESC D Input 32767 OPTIONS(*VARSIZE)
  • Retrieve operational descriptor C Callb 'CEEDOD' C Parm 1 ParmPos C Parm DescType C Parm DataType C Parm DescInf1 C Parm DescInf2 C Parm DataLen
  • Extract the data from the string C Eval Output = %Subst(Input:1:DataLen)
  • Convert string to lower case C Do DataLen X C Eval Char = %Subst(Output:X:1) C Upper:Lower Xlate Char Char C Eval %Subst(Output:X:1) = Char C Enddo
  • Return lower case string C Return Output P LowerCase E
  • ===============================================================
  • Mixed case procedure
  • =============================================================== P MixedCase B EXPORT D MixedCase PI 32767 OPDESC D Input 32767 OPTIONS(*VARSIZE) D PrevChar S 1 D Char3 S 3 D Char4 S 4
  • Retrieve operational descriptor C Callb 'CEEDOD' C Parm 1 ParmPos C Parm DescType C Parm DataType C Parm DescInf1 C Parm DescInf2 C Parm DataLen
  • Extract the data from the string C Eval Output = %Subst(Input:1:DataLen)
  • Convert string to mixed case C Do DataLen X C Eval Char = %Subst(Output:X:1) C If X = 1 C Lower:Upper Xlate Char Char C Else C Eval PrevChar = %Subst(Output:X-1:1) C Lower:Upper Xlate PrevChar PrevChar

    C If %Scan(PrevChar:Upper) = *Zero C Lower:Upper Xlate Char Char C Else C Upper:Lower Xlate Char Char C Endif C Endif C Eval %Subst(Output:X:1) = Char C Enddo

  • Return mixed case string C Return Output P MixedCase E
  • BLOG COMMENTS POWERED BY DISQUS

    LATEST COMMENTS

    Support MC Press Online

    $

    Book Reviews

    Resource Center

    • SB Profound WC 5536 Have you been wondering about Node.js? Our free Node.js Webinar Series takes you from total beginner to creating a fully-functional IBM i Node.js business application. You can find Part 1 here. In Part 2 of our free Node.js Webinar Series, Brian May teaches you the different tooling options available for writing code, debugging, and using Git for version control. Brian will briefly discuss the different tools available, and demonstrate his preferred setup for Node development on IBM i or any platform. Attend this webinar to learn:

    • SB Profound WP 5539More than ever, there is a demand for IT to deliver innovation. Your IBM i has been an essential part of your business operations for years. However, your organization may struggle to maintain the current system and implement new projects. The thousands of customers we've worked with and surveyed state that expectations regarding the digital footprint and vision of the company are not aligned with the current IT environment.

    • SB HelpSystems ROBOT Generic IBM announced the E1080 servers using the latest Power10 processor in September 2021. The most powerful processor from IBM to date, Power10 is designed to handle the demands of doing business in today’s high-tech atmosphere, including running cloud applications, supporting big data, and managing AI workloads. But what does Power10 mean for your data center? In this recorded webinar, IBMers Dan Sundt and Dylan Boday join IBM Power Champion Tom Huntington for a discussion on why Power10 technology is the right strategic investment if you run IBM i, AIX, or Linux. In this action-packed hour, Tom will share trends from the IBM i and AIX user communities while Dan and Dylan dive into the tech specs for key hardware, including:

    • Magic MarkTRY the one package that solves all your document design and printing challenges on all your platforms. Produce bar code labels, electronic forms, ad hoc reports, and RFID tags – without programming! MarkMagic is the only document design and print solution that combines report writing, WYSIWYG label and forms design, and conditional printing in one integrated product. Make sure your data survives when catastrophe hits. Request your trial now!  Request Now.

    • SB HelpSystems ROBOT GenericForms of ransomware has been around for over 30 years, and with more and more organizations suffering attacks each year, it continues to endure. What has made ransomware such a durable threat and what is the best way to combat it? In order to prevent ransomware, organizations must first understand how it works.

    • SB HelpSystems ROBOT GenericIT security is a top priority for businesses around the world, but most IBM i pros don’t know where to begin—and most cybersecurity experts don’t know IBM i. In this session, Robin Tatam explores the business impact of lax IBM i security, the top vulnerabilities putting IBM i at risk, and the steps you can take to protect your organization. If you’re looking to avoid unexpected downtime or corrupted data, you don’t want to miss this session.

    • SB HelpSystems ROBOT GenericCan you trust all of your users all of the time? A typical end user receives 16 malicious emails each month, but only 17 percent of these phishing campaigns are reported to IT. Once an attack is underway, most organizations won’t discover the breach until six months later. A staggering amount of damage can occur in that time. Despite these risks, 93 percent of organizations are leaving their IBM i systems vulnerable to cybercrime. In this on-demand webinar, IBM i security experts Robin Tatam and Sandi Moore will reveal:

    • FORTRA Disaster protection is vital to every business. Yet, it often consists of patched together procedures that are prone to error. From automatic backups to data encryption to media management, Robot automates the routine (yet often complex) tasks of iSeries backup and recovery, saving you time and money and making the process safer and more reliable. Automate your backups with the Robot Backup and Recovery Solution. Key features include:

    • FORTRAManaging messages on your IBM i can be more than a full-time job if you have to do it manually. Messages need a response and resources must be monitored—often over multiple systems and across platforms. How can you be sure you won’t miss important system events? Automate your message center with the Robot Message Management Solution. Key features include:

    • FORTRAThe thought of printing, distributing, and storing iSeries reports manually may reduce you to tears. Paper and labor costs associated with report generation can spiral out of control. Mountains of paper threaten to swamp your files. Robot automates report bursting, distribution, bundling, and archiving, and offers secure, selective online report viewing. Manage your reports with the Robot Report Management Solution. Key features include:

    • FORTRAFor over 30 years, Robot has been a leader in systems management for IBM i. With batch job creation and scheduling at its core, the Robot Job Scheduling Solution reduces the opportunity for human error and helps you maintain service levels, automating even the biggest, most complex runbooks. Manage your job schedule with the Robot Job Scheduling Solution. Key features include:

    • LANSA Business users want new applications now. Market and regulatory pressures require faster application updates and delivery into production. Your IBM i developers may be approaching retirement, and you see no sure way to fill their positions with experienced developers. In addition, you may be caught between maintaining your existing applications and the uncertainty of moving to something new.

    • LANSAWhen it comes to creating your business applications, there are hundreds of coding platforms and programming languages to choose from. These options range from very complex traditional programming languages to Low-Code platforms where sometimes no traditional coding experience is needed. Download our whitepaper, The Power of Writing Code in a Low-Code Solution, and:

    • LANSASupply Chain is becoming increasingly complex and unpredictable. From raw materials for manufacturing to food supply chains, the journey from source to production to delivery to consumers is marred with inefficiencies, manual processes, shortages, recalls, counterfeits, and scandals. In this webinar, we discuss how:

    • The MC Resource Centers bring you the widest selection of white papers, trial software, and on-demand webcasts for you to choose from. >> Review the list of White Papers, Trial Software or On-Demand Webcast at the MC Press Resource Center. >> Add the items to yru Cart and complet he checkout process and submit

    • Profound Logic Have you been wondering about Node.js? Our free Node.js Webinar Series takes you from total beginner to creating a fully-functional IBM i Node.js business application.

    • SB Profound WC 5536Join us for this hour-long webcast that will explore:

    • Fortra IT managers hoping to find new IBM i talent are discovering that the pool of experienced RPG programmers and operators or administrators with intimate knowledge of the operating system and the applications that run on it is small. This begs the question: How will you manage the platform that supports such a big part of your business? This guide offers strategies and software suggestions to help you plan IT staffing and resources and smooth the transition after your AS/400 talent retires. Read on to learn: