04
Mon, Nov
1 New Articles

ILE RPG 1996 & Beyond: Part 2

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

In Part 1 of this series (MC, December 1995), I explained what the planned enhancements to ILE RPG will allow programmers to do: use a free-form syntax to call programs and procedures, call procedures that return values, call procedures with parameters passed by value, and prototype calls to ensure correct parameter passing. If you haven't read Part 1, I encourage you to do so.

This month, I'll explain what the new RPG subprocedure feature will allow programmers to do: define procedures that can be called like RPG built-in functions, declare local variables, declare automatic storage, and create cycle-free RPG modules.

In addition to what you've already seen, there are some more-practical benefits you can realize with the planned enhancements. For example, if an application currently has 10 ILE RPG programs, each program contains all the code that the RPG cycle requires. You may have used the /COPY compiler directive extensively to try to control how you use files or how you call your programs. You probably have the same F specs coded in most of your programs; if so, your compiled programs each contain all the I specs and O specs needed for processing your externally described files. With the new enhancements, you'll be able to eliminate the redundant code. Let me explain.

In V3R1 ILE RPG, a module could only contain one procedure. That one procedure would always have the code required for the RPG cycle. Think of this procedure as a main procedure. With the enhancements, the single procedure per module restriction will be removed, and you'll be able to create a subprocedure, a new type of procedure that offers several advantages, one being the elimination of the RPG cycle code. If you change your application so that what was formerly ten modules is now one module with ten procedures, you will have only one copy of the RPG cycle. Furthermore, you will have only one copy of your F specs, I specs, and O specs. This considerably reduces your application's storage consumption and total compile time.

There may also be some performance benefits. The performance of the actual calls is no different, but a subprocedure should run a bit faster than a main procedure since it doesn't have to go through the cycle.

Let's take a closer look at this concept of subprocedures.

I have to talk about ILE for a moment to explain what a subprocedure really is. For more information about ILE, see IBM's Integrated Language Environment Concepts manual; "V3R1 Announcement Follow-up: An Introduction to Program Binding" (MC, August 1994); or "The Integrated Language Environment (ILE)" (MC, May 1993). An ILE program consists of one or more modules; each module contains one or more procedures. 1 illustrates the elements of an ILE program. There are several types of procedures:

I have to talk about ILE for a moment to explain what a subprocedure really is. For more information about ILE, see IBM's Integrated Language Environment Concepts manual; "V3R1 Announcement Follow-up: An Introduction to Program Binding" (MC, August 1994); or "The Integrated Language Environment (ILE)" (MC, May 1993). An ILE program consists of one or more modules; each module contains one or more procedures. Figure 1 illustrates the elements of an ILE program. There are several types of procedures:

Main procedure: A main procedure is one that is eligible to be the first procedure called when a program is called. The module's main procedure is always exported from the module.

In ILE RPG, the main procedure consists of everything after the H spec, up to the first subprocedure. The main procedure always has the same name as the created module. In other languages, this is not necessarily true. For example, C requires that its main procedures be called main, and they can be coded anywhere in the module.

Prior to the multiple-procedure enhancements, ILE RPG modules had only one procedure, and it was always a main procedure. With the multiple procedure enhancements, ILE RPG modules can have zero or one main procedure and possibly several subprocedures. We'll look at how to code a module with no main procedure later.

User entry procedure: The user entry procedure is the first procedure called when the program is called.

In any program, there is exactly one user entry procedure, regardless of how many modules have a main procedure. If your program has several modules that have a main procedure, you select the user entry procedure using the Program entry procedure module (ENTMOD) parameter of the Create Program (CRTPGM) command.

You may already be familiar with this if you create programs using several ILE RPG modules.

Program entry procedure (PEP): This is a special procedure added by the compiler. When a program is called, the system calls the PEP, which in turn calls the user entry procedure for the program. You do not need to be concerned with this procedure, but it shows up in the list of procedures when you use the Display Module (DSPMOD) command.

Exported subprocedure: This subprocedure is a nonmain procedure in a module; it can be called by any procedure in the program, either in the same module or other modules.

Local subprocedure: This subprocedure is a nonmain procedure in a module; it can only be called by other procedures in the same module.

If you're interested, you can see the procedures in a module using the DSPMOD command and specifying DETAIL(*EXPORT *PROCLIST).

In the following discussions, the term procedure refers to main procedures, user entry procedures, and subprocedures. The term subprocedure refers to both exported and local subprocedures, but does not include main procedures.

When a module has several procedures, the issue of name scope arises. The name scope defines the locations in the module where the name can be used. If you declare a variable in a subprocedure, that name can be used only within the calculations of that subprocedure. If you declare a variable in the main procedure, it is global to the module and can be used anywhere in the module.

In ILE RPG, any file, record format, variable, KLIST, or PLIST declared in the main procedure is global to the module. Any subroutine or TAG in the main procedure is local to the main procedure. Anything declared in a subprocedure is local to the subprocedure.

You do not have to ensure that local names do not conflict with global names. For example, if you declare a file called F1 in the main procedure, you could declare a data structure called F1 in a subprocedure. As long as you don't want to access file F1 by name in the subprocedure, hiding the global name F1 should not cause you any problems.

Previously, every RPG module contained exactly one procedure, and that procedure was designated as a possible main procedure. Now, you'll be able to define as many procedures as you like in a module. You'll even be able to say that you don't want the module to have a main procedure (more about this in a later section, "A No-cycle RPG Module").

An RPG subprocedure differs from a main procedure in the following ways:

o The main procedure doesn't have P specs; the subprocedures begin and end with a P spec.

o The calculations in the main procedure are controlled by the RPG cycle, so they are done repeatedly until terminated either by a RETURN operation or by setting on *INLR, *INRT, or a halt indicator. A subprocedure doesn't use the RPG cycle, so the calculations are done once only. The calculations begin at the first calculation of the subprocedure, and end at the last, although they can be terminated early by a RETURN operation.

Because the subprocedure doesn't use the cycle, exceptions are handled differently. The main difference in exception handling is that inquiry messages are not displayed for unhandled exceptions in a subprocedure; instead, the procedure ends immediately in error.

o A main procedure cannot be called recursively; a subprocedure can be called recursively. When this happens, any information about the old call is saved by the system until the new call is complete.

o The default storage type for a main procedure is static, and, for a subprocedure, it is automatic. For a variable in static storage,

there is one single storage location that is accessed by every call to the procedure. Even if a procedure returns, when the procedure is called again, the static storage still has its old value. For a variable in automatic storage, a new copy of the storage is created each time a procedure is called. If the procedure is called recursively, the old value is part of the information that is saved until the new call is complete.

Even though the default storage type for subprocedures is automatic, you can define variables in static storage using the STATIC keyword on the D spec for the variable.

o Anything you define in the main procedure, except subroutines and TAGs, is global to the module. Anything you define in a subprocedure is local to the subprocedure.

You code subprocedures after output specs and before compile-time data. Each subprocedure begins and ends with a P (Procedure) spec. The body of the subprocedure consists of D specs and C specs, including local subroutines. Each subprocedure can have its own *PSSR subroutine to handle errors in the subprocedure.

The C specs can refer to any definitions-including files, PLISTs, and KLISTs-from the main procedure as well as, of course, any of its definitions from its own D specs. The P spec is similar to the D spec. It contains the following components:

Name: (positions 7-21).This is the name of the prototype that describes this procedure.

Type: (position 24). The type is B for the P spec that begins a procedure or E for the P spec that ends a procedure.

Keywords: Currently, there is only one keyword-EXPORT. Coding the EXPORT keyword means that you want the procedure to be visible to other modules (as illustrated in 2).

Keywords: Currently, there is only one keyword-EXPORT. Coding the EXPORT keyword means that you want the procedure to be visible to other modules (as illustrated in Figure 2).

In last month's article, I showed how the compiler uses the prototype to ensure that a caller is passing the correct parameters and using the return value correctly. The compiler also uses the prototype to ensure that the procedure is expecting the correct parameters and returning the correct type of value.

The compiler does this by comparing the prototype to another new kind of definition: the procedure interface. The procedure interface looks very similar to a prototype, except that it has PI as the type rather than PR. The compiler does not allow any differences between the prototype and the procedure interface.

The procedure interface is also used to define the entry parameters for the subprocedure. The name you give to the parameter on the D spec is the name you use to refer to the parameter inside the subprocedure.

3 contains an example of a subprocedure in which a prototype and a procedure interface are defined. As you can see, the prototype and the procedure interface match.

Figure 3 contains an example of a subprocedure in which a prototype and a procedure interface are defined. As you can see, the prototype and the procedure interface match.

Notice all the places that contain the name MyProc. The name is required on the prototype and the begin-procedure specs. It's optional on the procedure-interface spec and the end-procedure spec.

4 contains another example of a subprocedure. Notice that there is no procedure interface. For procedures that don't return a value and have no parameters, the procedure interface is optional.

Figure 4 contains another example of a subprocedure. Notice that there is no procedure interface. For procedures that don't return a value and have no parameters, the procedure interface is optional.

In Part I of this series, you learned how to define a prototype for a procedure to return a value, allowing you to call the procedure in an expression.

To return a value from a subprocedure, you must code a new form of the RETURN op code within the body of the subprocedure. The expression in the extended factor 2 is the value that is returned from the subprocedure. The old form of RETURN, without factor 2, is still used to return from a main procedure or from a procedure without a return value.

When a subprocedure is prototyped to return a value, you must have a RETURN op code in the subprocedure. Additionally, you must ensure that the RETURN operation is executed at run time. If you have several places in your subprocedure that return a value, you will probably have some conditioning logic to determine which RETURN statement to use; if it is not guaranteed that a RETURN operation will be found on every path through this logic, you should code a default RETURN as the last statement in the subprocedure.

If a subprocedure that returns a value ends without returning a value, run-time exception RNX0224 will be issued to the caller of the subprocedure.

In 5 on page 103, you can see an example of a subprocedure that returns the square root of its parameter if the parameter is valid, or zero if the parameter is not valid.

In Figure 5 on page 103, you can see an example of a subprocedure that returns the square root of its parameter if the parameter is valid, or zero if the parameter is not valid.

1. Every subprocedure in the module must have a prototype. Normally, the prototypes for any exported subprocedures would be kept in a /COPY member, and the prototypes for local subprocedures would be in the source itself. The prototypes for all the subprocedures in a module must be defined in the D specs of the main procedure, so you would place the /COPY statement in the main D specs, along with any prototypes for local subprocedures.

2. You can't specify the external name of an RPG subprocedure using the EXTPROC keyword on the prototype for the subprocedure. The external name for a subprocedure is the name (keyed in upper- or lowercase) in positions 7-12 of the prototype. RPGsubprocedures are always exported in uppercase. For the procedure in 5, the name is MY_SQRT.

2. You can't specify the external name of an RPG subprocedure using the EXTPROC keyword on the prototype for the subprocedure. The external name for a subprocedure is the name (keyed in upper- or lowercase) in positions 7-12 of the prototype. RPGsubprocedures are always exported in uppercase. For the procedure in Figure 5, the name is MY_SQRT.

Defining the Procedure Interface for the Main Procedure

You may be wondering how you code the main procedure if it is prototyped. There are no P specs for the main procedure; it begins after the H spec and ends at the first subprocedure. You are still free to use the *ENTRY PLIST for your entry parameters, but, if the main procedure is prototyped, you should define the entry parameters using a procedure interface.

The procedure interface for the main procedure must have a name. This name must match a prototype that has already been defined.

A program cannot have a return value or parameters passed by value. Since the main procedure of any module may be selected as a program's user entry procedure, the main procedure must conform to these rules.

6 on page 103 contains an example in which the main procedure's entry parameters are defined with a prototype and procedure interface.

Figure 6 on page 103 contains an example in which the main procedure's entry parameters are defined with a prototype and procedure interface.

You could use an *ENTRY PLIST instead of a prototype and procedure interface for this program. If you did, it would look like the illustration in 7.

You could use an *ENTRY PLIST instead of a prototype and procedure interface for this program. If you did, it would look like the illustration in Figure 7.

If you want a module that only contains subprocedures, you can code NOMAIN on the H spec for the module. A NOMAIN module does not have a PEP, so you can't create a program using only this module. You can use a NOMAIN module in a service program, or you can bind the module directly to other modules as part of a program. If the procedures are very specific to one program, binding the module is the best choice, but if the procedures in the modules will be used by several programs, it's best to use the module in a service program. That way, you can reduce the amount of storage used by your module, since you need only one copy of the service program on your system.

The part of the code that precedes the subprocedures is called the main source section. If there is a main procedure, it is part of the main source section. In a NOMAIN module, the main source section contains only global definitions.

You can define files, data areas, KLISTs, PLISTs, and variables in the global definitions. Your files that would normally get opened as part of *INIT get opened when the first subprocedure is called (during module initialization). They never get closed unless you explicitly close them. The same applies to UDS data areas-they get locked during module initialization, but they never get unlocked unless you explicitly unlock them.

Let's look at a meaningful example. I'll develop a module that has two general-purpose procedures, and then I'll use those procedures to write a more specific subprocedure for another module.

The general-purpose procedures are DayOfWeek and FormatDate. DayOfWeek gives the name (e.g., Monday, Tuesday) for a given date. FormatDate gives a readable form of a given date (e.g., Monday, June 5, 1985).

One of the most important tasks is defining the prototypes for these procedures. This is the caller's view of the procedures; ideally, it should never change except in an upward compatible way.

Both these procedures have at least one input parameter: the date. I have a choice for the value the procedure is calculating; I can return that value in a parameter passed by reference, or it can be the return value from the procedure. For DayOfWeek, I'll have the procedure return a value, and, for FormatDate, I'll use a parameter passed by reference. For convenience, I should allow this parameter to be any length, so I'll use the *VARSIZE option. I'll use another parameter to indicate the passed length of this parameter.

For exported procedures, the prototypes should be in a /COPY member. I'll call the module FMTDATE and the /COPY member FMTDATEI (see 8 on page 104).

For exported procedures, the prototypes should be in a /COPY member. I'll call the module FMTDATE and the /COPY member FMTDATEI (see Figure 8 on page 104).

Now, I'm ready to code the FMTDATE module that exports these two subprocedures, FormatDate and DayOfWeek (see 9 on page 104).

Now, I'm ready to code the FMTDATE module that exports these two subprocedures, FormatDate and DayOfWeek (see Figure 9 on page 104).

Notice these points:

o There is nothing for a main procedure to do, so I've made this a NOMAIN module.

o The module is copyrighted.

o The source for the module copies in the prototypes for the module. The compiler will ensure that the exported procedures match the prototypes.

o The local procedure AbsVal uses the new data types I (integer) and U (unsigned integer). Since the input parameter can be either positive or negative, it cannot be unsigned, but the return value is never negative, so unsigned integer is a good choice. (The next section contains more information about these new data types.)

10 contains the source code for a module that uses these procedures. This module calls a program called REPORTS that writes a report to printer file QSYSPRT.

Figure 10 contains the source code for a module that uses these procedures. This module calls a program called REPORTS that writes a report to printer file QSYSPRT.

Note these things about this example:

o Even though the EXCEPT op code is used in the subprocedure, the output specs are part of the main procedure. This means that the names used in the output specs are the global names. If the name PrtLine was defined locally in the DateMsg subprocedure, the EXCEPT operation would not work as expected. The value printed out would be whatever value PrtLine had before the call to DateMsg.

o The new %PARMS built-in function is used to determine how many parameters were passed to the subprocedure.

o For the Override with Printer File (OVRPRTF) to work as expected, the program REPORTS must be in the same activation group as PRTRPT.

Other enhancements to RPG in the next release include new numeric data types: integer and unsigned integer. They are available in two lengths: 10 and 5 digits.

They are similar to the binary data type in the way the numbers are stored, but, unlike the binary data type, when the value is used, it is not necessarily converted to a packed value first. Even though a two-byte binary field can hold values up to 32,767, RPG does not allow two-byte binary fields to be defined to have more than four digits. If a four-digit RPG binary field has a value larger than 9,999, the value is truncated whenever you try to use the value.

Integer and unsigned integer fields do not have this problem. Integer fields can have both positive and negative values. Unsigned integer fields can have only positive values, but they can have larger positive values than an integer field.

The *CYMD date format enhancement is only available for the MOVE, MOVEL, and TEST op codes. It allows you to move or test a character or numeric date in the format CYYMMDD.

The next release of RPG even allows you to copyright the modules and programs that you send to your customers by using a new H spec keyword, COPYRIGHT. You can see the copyright value of an ILE module, program, or service program by using the DETAIL(*COPYRIGHT) parameter of the DSPMOD, Display Program (DSPPGM), and Display Service Program (DSPSRVPGM) commands.

These enhancements give you much more freedom when designing an application in RPG. You will be able to write with a greater degree of modularity and, at the same time, gain more control over the interaction of the modules and probably realize better performance. If you find this freedom overwhelming, look to the principles of software engineering to guide you. There are many excellent books on this topic. Some topics you will find useful to read about include information hiding, module interfaces, data abstraction, and functional abstraction. (See references listed below). Applying those principles was possible using RPG before, but now it is much easier.

Barbara Morris is a staff development analyst for RPG at the IBM Laboratory in Toronto, Canada.

REFERENCES

Integrated Language Environment Concepts (SC41-3606).

Bell, Doug; Ian Morrey, and John R. Pugh. Software Engineering: A Programming Approach. Hemel-Hempstead, Hertfordshire, 1992.

Lamb, David Alex. Software Engineering: Planning for Change. Prentice-Hall, 1988.

Pressman, Roger S. Software Engineering: A Practitioner's Approach. New York: McGraw-Hill, 1992.

ILE RPG 1996 & Beyond: Part 2

Figure 1: The Elements of an ILE Program



ILE RPG 1996 & Beyond: Part 2

Figure 2: Defining a Subprocedure

 
 PName+++++++++++..B...................Keywords+++++++++++++++++++++++++ 
 P MyProc          B                   EXPORT                            
 D ....                                                                  
 C ....                                                                  
 P                 E                                             

ILE RPG 1996 & Beyond: Part 2

Figure 3: Subprocedure with Procedure Interface

 
 DName+++++++++++ETDsFrom+++To/L+++IDc.Keywords+++++++++++++++++++++++++ 
  * The prototype for the subprocedure                                    
 D   MyProc        PR                                                     
 D                                5P 0                                    
 D                               10A   VALUE                              
 D                                 D   DATFMT(*MDY/)                      
                                                                          
  * The subprocedure                                                      
 P   MyProc        B                                                      
  * The procedure interface                                               
 D   MyProc        PI                                                     
 D    num1                        5P 0                                    
 D    charparm                   10A   VALUE                              
 D    date                         D   DATFMT(*MDY/)                      
                                                                          
  * The subprocedure body                                                 
 C                   IF        num1 > 14                                  
 C      *MDY/        MOVEL     charparm          date                     
 C                   ENDIF                                                
                                                                          
 P   MyProc        E  
  

ILE RPG 1996 & Beyond: Part 2

Figure 4: Subprocedure with No Procedure Interface

 
 DName+++++++++++ETDsFrom+++To/L+++IDc.Keywords+++++++++++++++++++++++++ 
  * The prototype for the procedure                                       
 D   OpenFile      PR                                                     
                                                                          
  * The procedure                                                         
 P   OpenFile      B                                                      
 C                   OPEN      CUSTFILE                                   
 P                 E                                             
  

ILE RPG 1996 & Beyond: Part 2

Figure 5: Subprocedure that Returns a Value

 
 DName+++++++++++ETDsFrom+++To/L+++IDc.Keywords+++++++++++++++++++++++++ 
  * The prototype for the subprocedure                                    
 D   my_sqrt       PR            10P 0                                    
 D                               30P 0 VALUE                             
                                                                          
  * The subprocedure                                                      
 P   my_sqrt       B             30P 0 EXPORT                            
  * The procedure interface                                               
 D   my_sqrt       PI            10P 0                                    
 D      num                      30P 0 VALUE                             
                                                                          
  * The subprocedure body                                                 
 C                   IF        num <= 0                                   
 C                   RETURN    0                                          
 C                   ELSE                                                 
 C                   RETURN    num ** .5                                  
 C                   ENDIF                                                
                                                                          
 P   my_sqrt       E  
  

ILE RPG 1996 & Beyond: Part 2

Figure 6: A Main Procedure's Entry Parameters with a Prototype and Procedure Interface

 
 FMASTER    IF   E             DISK                                       
 FQSYSPRT   O        80        PRINTER                                    
  
 DName+++++++++++ETDsFrom+++To/L+++IDc.Keywords+++++++++++++++++++++++++ 
 * The prototype for the program (would be normally in a /COPY member)     
 D PrtRpt          PR                  EXTPGM('PRTRPT')                 
 D   Title                       80A   OPTIONS(*NOPASS)                 
                                                                          
  * The procedure interface, defining entry parameter list                
 D PrtRpt          PI                                                     
 D   RptTitle                    80A                                      
                                                                          
 CL0N01Factor1+++++++Opcode&ExtFactor2+++++++Result++++++++Len++D+HiLoEq 
  * The main procedure                                                    
 C                   IF        %PARMS = 1                                 
 C                   EXCEPT    Title                                      
 C                   ENDIF                                                
 C                   EXSR      Process                                    
 C                   RETURN                                               
                                                                          
 C     Process       BEGSR                                                
 C ....  print report                                                     
 C                   ENDSR                                                
                                                                          
 OQSYSPRT   E            Title                                            
 O                       RptTitle                                         
 OQSYSPRT   E            Line                                             
 O                       Company                                          
 O                       Balance 
  

ILE RPG 1996 & Beyond: Part 2

Figure 7: Using an Entry PLIST Instead of a Prototype and Procedure Interface

 
 CL0N01Factor1+++++++Opcode&ExtFactor2+++++++Result++++++++Len++D+HiLoEq 
 C     *ENTRY        PLIST                                               
 C                   PARM                    RptTitle          80     
  

ILE RPG 1996 & Beyond: Part 2

Figure 8: /COPY Source Member FMTDATEI

 
  *----------------------------------------------------------------- 
  * DayOfWeek  - return the name of the day for the input date       
  *              eg "Tuesday"                                        
  *----------------------------------------------------------------- 
 DName+++++++++++ETDsFrom+++To/L+++IDc.Keywords+++++++++++++++++++++++++ 
 D DayOfWeek       PR            10A                                 
 D   InDate                        D   VALUE DATFMT(*ISO)            
                                                                     
  *----------------------------------------------------------------- 
  * FormatDate - format the input date into a string of the form     
  *              "Monday, December 4, 1994"                          
  *----------------------------------------------------------------- 
 D FormatDate      PR                                                
 D   InDate                        D   VALUE DATFMT(*ISO)            
 D   OutString                  100A   OPTIONS(*VARSIZE)             
 D   OutLen                       5P 0 VALUE    
  

ILE RPG 1996 & Beyond: Part 2

Figure 9: Source Member for Module FMTDATE

 
      H NOMAIN COPYRIGHT('Copyright: My Company 1995')                    
                                                                          
       *----------------------------------------------------------------- 
       * FMTDATE - procedures to format dates into readable strings       
       *                                                                  
       * FormatDate - format a date into a string of the form             
       *              "Monday, December 4, 1994"                          
       *----------------------------------------------------------------- 
                                                                          
      DName+++++++++++ETDsFrom+++To/L+++IDc.Keywords+++++++++++++++++++++ 
       * Global variables                                                 
      D DayNames        S             10A   DIM(7) PERRCD(7) CTDATA       
      D MonthNames      S             10A   DIM(12) PERRCD(6) CTDATA      
                                                                          
       * Local subprocedure to calculate the absolute value of a number   
      D AbsVal          PR            10U 0                               
      D    Val                        10I 0 VALUE                         
                                                                     
       /COPY FMTDATEI                                                     
                                                                     
       *----------------------------------------------------------------- 
       * FormatDate - format a date into a string of the form             
       *              "Monday, December 4, 1994"                          
       *                                                                  
       * Uses procedure DayOfWeek to get the "Monday" part                
       *----------------------------------------------------------------- 
      P FormatDate      B                   EXPORT                        
                                                                     
      DName+++++++++++ETDsFrom+++To/L+++IDc.Keywords+++++++++++++++++++++ 
       * Parameters                                                       
      D FormatDate      PI                                                
      D   InDate                        D   VALUE DATFMT(*ISO)            
      D   OutString                  100A   OPTIONS(*VARSIZE)             
      D   OutLen                       5P 0 VALUE                         
                                                                     
       * Local variables                                                  
      D MonthNum        S              5P 0                               
      D DayChar         DS                                                
      D   Day                          2S 0                               
      D   DayFirst                     1A   OVERLAY(Day)                  
      D YearChar        DS                                                
      D   Year                         4S 0                               
                                                                          
       * Calculate the month                                              
      C                   EXTRCT    InDate:*M     MonthNum                
                                                                          
       * Get day in char form with no leading zero                        
       * Will be in data structure DayChar                                
      C                   EXTRCT    InDate:*D     Day                     
      C                   IF        DayFirst = '0'                        
      C                   EVAL      DayFirst = ' '                        
      C                   ENDIF                                           
                                                                          
       * Get the year in character form, in data structure YearChar       
      C                   EXTRCT    InDate:*Y     Year                    
                                                                          
       * Calculate the output string, being sure only to change as        
       * much of the output parameter as we have access to.               
      C                   EVAL      %subst(OutString:1:OutLen) =          
      C                               %trim(DayOfWeek(InDate))  + ', '    
      C                             + %trim(MonthNames(MonthNum)) + ' '   
      C                             + %trim(DayChar) + ', ' + YearChar    
                                                                         
      P FormatDate      E                                                 
                                                                          
       *----------------------------------------------------------------- 
       * DayOfWeek  - return the name of the day for the input date       
       *              eg "Tuesday"                                        
       *----------------------------------------------------------------- 
      P DayOfWeek       B                   EXPORT                        
                                                                          
       * Parameters                                                       
      D DayOfWeek       PI            10A                                 
      D   InDate                        D   VALUE DATFMT(*ISO)            
                                                                          
       * Local variables                                                  
      D DayNum          S              5P 0                               
      D Sunday          C                   CONST(D'1994-10-02')          
      D Days            S             30P 0                               
                                                                          
       * Calculate the day of the week using a date known to be Sunday    
      C     InDate        SUBDUR    Sunday        Days:*D                 
      C     Days          DIV       7             DayNum                  
      C                   MVR                     DayNum                  
      C                   RETURN    DayNames(AbsVal(DayNum) + 1)          
                                                                          
      P DayOfWeek       E                                                 
                                                                          
       *----------------------------------------------------------------- 
       * AbsVal - return the absolute value of the input number           
       *              eg AbsVal(-5) = 5                                   
       *----------------------------------------------------------------- 
      P AbsVal          B                                                 
                                                                          
       * Parameters                                                       
      D AbsVal          PI            10U 0                               
      D   Val                         10I 0 VALUE                         
                                                                          
      C                   IF        Val < 0                               
      C                   RETURN    - Val                                 
      C                   ELSE                                            
      C                   RETURN    Val                                   
      C                   ENDIF                                           
      P AbsVal          E                                                 
                                                                          
 **CTDATA DayNames                                                        
 Sunday    Monday    Tuesday   Wednesday Thursday  Friday    Saturday     
 **CTDATA MonthNames                                                      
 January   February  March     April     May       June                   
 July      August    September October   November  December 
  

ILE RPG 1996 & Beyond: Part 2

Figure 10: Source Member for Module PRTRPT Using FMTDATE Procedures

 
 H                                                                   
                                                                     
 FQSYSPRT   O    F  100        PRINTER USROPN                        
                                                                     
  *---------------------------------------------------------         
  * Prototypes                                                       
  *---------------------------------------------------------         
  /COPY FMTDATEI                                                     
                                                                     
 DName+++++++++++ETDsFrom+++To/L+++IDc.Keywords+++++++++++++++++++++++++ 
 D QCMDEXC         PR                  EXTPGM('QCMDEXC')             
 D    Cmd                       100A   OPTIONS(*VARSIZE) CONST       
 D    Len                        15P 5 CONST                         
                                                                     
  * DateMsg - prints a message followed by a date, followed          
  *           optionally by another message                          
 D DateMsg         PR                                                
 D    Msg1                       50A   VALUE                         
 D    Date                         D   VALUE                         
 D    Msg2                       50A   VALUE OPTIONS(*NOPASS)        
                                                                     
  *---------------------------------------------------------         
  * Global definitions                                               
  *---------------------------------------------------------        
 D OvrPrtf         C                   'OVRPRTF QSYSPRT +            
 D                                      SHARE(*YES) OVRSCOPE(*JOB)'  
 D Today           S               D                                 
                                                                     
 D                 DS                                                
 D TimeVal                       12S 0                               
 D   CurDate                      6S 0  OVERLAY(TimeVal:7)           
                                                                     
 D PrtLine         S            100A                                 
                                                                     
                                                                     
  *---------------------------------------------------------         
  * Main procedure                                                   
  *---------------------------------------------------------         
                                                                     
  * Get today's date                                                 
 C                   TIME                    TimeVal                 
 C     *JOBRUN       MOVEL     CurDate       Today                   
                                                                     
  * Print the report header                                          
 C                   OPEN      QSYSPRT                               
 C                   CALLP     DateMsg('Report printed on'           
 C                                   : Today)                        
                                                                     
  * Override the printer file to be shared and call the program      
  * to print the report to QSYSPRT                                   
 C                   CALLP     QCMDEXC(OvrPrtf : %size(OvrPrtf))       
 C                   CALL      'REPORTS'                             
                                                                     
  * Print the report footer                                          
 C                   ADDDUR    7:*DAYS       Today                   
 C                   CALLP     DateMsg('Next report is due on '      
 C                                   : Today                         
 C                                   : 'at the earliest.')           
                                                                     
 C                   CLOSE     QSYSPRT                               
 C                   RETURN                                          
                                                                     
 OQSYSPRT   E                                                        
 O                       PrtLine                             
 * Subprocedure DateMsg                                             
  *---------------------------------------------------------         
 P DateMsg         B                                                 
  
 DName+++++++++++ETDsFrom+++To/L+++IDc.Keywords+++++++++++++++++++++++++ 
 D                 PI                                                
 D    Msg1                       50A   VALUE                        
 D    Date                         D   VALUE                        
 D    Msg2                       50A   VALUE OPTIONS(*NOPASS)       
                                                                    
 D DateString      S            100A                                 
 D*PrtLine         S            100A                                 
                                                                     
  * Build a string of Msg1 + the 'Monday, June 5, 1995' form of      
  * the date                                                         
 C                   CALLP     FormatDate(Date : DateString :        
 C                                        %size(DateString))         
 C                   EVAL      PrtLine = %TRIMR(Msg1) + ' '          
 C                                     + %TRIMR(DateString)          
                                                                     
  * If the third parameter was passed, add the second part of the    
  * message                                                          
 C                   IF        %PARMS = 3                            
 C                   EVAL      PrtLine = %TRIMR(PrtLine) + ' '       
 C                                     + %TRIMR(Msg2)                
 C                   ENDIF                                           
                                                                    
  * Print out the line                                               
 C                   EXCEPT                                          
                                                                     
 P DateMsg         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: