27
Fri, Dec
0 New Articles

Fun with Binding Directories

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

What do binding directories and binder language have in common? Answer: The use of the word "bind" in their titles. Other than that, they really have nothing else to do with one another.

Binder language is stored in source file members. It's easily accessible and modifiable, and since you can use a source editor, you have all the copy/paste/find functions of the SEU or WDSc environment.

Binding directories, on the other hand, are a joke. They are unique object types (space-based objects) that cannot be easily modified, searched, or—most often requested—reordered. I remember that 11 or 12 years ago people complained about binding directories being a new object instead of a source member, but those complaints obviously did no good.

So here it is, a decade later, and people are finally using binding directories. Suddenly, all of the shortcomings we noticed in the mid-1990s are coming to light. "How do I reorder or move entries in my binding directory?" Short answer: You can't. "How do I update a binding directory entry (to change the library name, for example)?" Again, you can't. "How do I insert a new entry between two existing entries?" You can't use WRKBNDDIRE; you have to use ADDBNDDIRE instead.

Binding directories clearly should have been stored in source members to give us the ability to rename, reorder, and add and remove entries with ease. Certainly, this kind of work is not done every day. In fact, it is rarely done. But nonetheless, it is one more thing that should be there to make things easier.

To that end, I started looking for an API that would give me access to binding directories. I wanted to be able to list the entries, display them in a subfile, and allow those entries to be moved, deleted, added, and even sorted. Alas, no binding directory APIs exist. Of course, being an old System/38 hack, I dumped a binding directory object and discovered that it is a simple user-space type of object that contains a header followed by a list of entries. After a few minutes of looking at the dump, I had the structure figured out.

The bad news: User-state programs can't play at this level with binding directories. So if you have security level 40 or higher, you wouldn't be able to use any new utility I might create.

So I had to resort to 20-year-old programming techniques. I had to use an OUTFILE. While doing so did make things more complicated and gave me a sense of humiliation (I'm a firm believer in APIs, not OUTFILEs), I was able to make it work.

The first thing I had to do was to realize that the DSPBNDDIR command has an OUTPUT(*OUTFILE) parameter option. Being accustomed to using WRKxxxx (Work With...) commands, I sometimes don't even consider that there might be a corresponding DSPxxxx command; in this case, there was.

Since the new IBM V5R3 Info Center seems like it was designed by a hurricane (compared to prior releases), finding things is a bit bewildering. So rather than attempt to look up the record layout of the output from DSPBNDDIR, I decided to do it the old-fashioned way—create the output file and read the format from the outfile using the DSPFFD command.

The following is the record format of the output file generated by the DSPBNDDIR command:

Record Format: QBNDSPBD
BNDCEN
Char(1)
Display Century
BNDDAT
Char(6)
Display Date: format—Job
BNDTIM
Char(6)
Display Time
BNOLNM
Char(10)
Library Name
BNOBNM
Char(10)
Object Name
BNOBTP
Char(7)
Object Type
BNOCEN
Char(1)
Object Create Century
BNODAT
Char(6)
Object Create Date
BNOTIM
Char(6)
Object Create Time
BNDRLB
Char(10)
Binding Directory Library
BNDRNM
Char(10)
Binding Directory
BNMOSY
Char(8)
System Name

This output file has what we need to write our own Work With panel. The sad thing is that we have to write it ourselves. So rather than build a rarely used WRKBD command, I decided to create something much more simple—a Retrieve Binder Directory Source (RTVBDSRC) CL command. There is already a Retrieve Binder Language Source (RTVBNDSRC) so why not RTVBDSRC?

This is the kind of tool I used to build back in the old System/38 days (when I wrote, edited, and published the "Q38" newsletter (anybody remember that?)

The command looks like this:

RTVBDSRC BNDDIR( [library/] [bnddir] ) 
          SRCFILE( [library/] [QCLSRC] ) 
          SRCMBR( [*BNDDIR name ] ) +
          REPLACE( [*NO | *YES ] )  

The source for the command definition object is listed below. When you compile it with CRTCMD, be sure to specify the name of the CL program, also named RTVBDSRC, as the command processing program.

 RTVBDSRC:   CMD        PROMPT('Retrieve Binding Dir Source')
             /*         Command processing program is: RTVBDSRC  */
             PARM       KWD(BNDDIR) TYPE(BNDDIR) MIN(1) +
                          PROMPT('Binding Directory')
 BNDDIR:     QUAL       TYPE(*NAME) MIN(1) EXPR(*YES)
             QUAL       TYPE(*NAME) DFT(*LIBL) SPCVAL((*LIBL) +
                          (*CURLIB)) EXPR(*YES) PROMPT('Library')
             PARM       KWD(SRCFILE) TYPE(SRCF) PROMPT('Source +
                          File')
 SRCF:       QUAL       TYPE(*NAME) DFT(QCLSRC) SPCVAL((QCLSRC) +
                          (QCLLESRC)) EXPR(*YES)
             QUAL       TYPE(*NAME) DFT(*LIBL) SPCVAL((*LIBL) +
                          (*CURLIB)) EXPR(*YES) PROMPT('Library')
             PARM       KWD(SRCMBR) TYPE(*NAME) DFT(*BNDDIR) +
                          SPCVAL((*BNDDIR)) EXPR(*YES) +
                          PROMPT('Source member')
             PARM       KWD(REPLACE) TYPE(*LGL) DFT(*NO) +
                          SPCVAL((*YES '1') (*NO '0')) EXPR(*YES) +
                          CHOICE('*YES, *NO') PROMPT('Replace +
                          source member')

The CL program that this RTVBDSRC command runs to do the actual work is also named RTVBDSRC. The program does some routine verification and then dumps the named binder directory to an output file in QTEMP. Then, within the same CL program, it reads the outfile and creates a source member that includes an ADDBNDDIRE CL command for each entry in the binding directory.

That source member can then be edited with SEU or WDSc and modified to your satisfaction. Compile the source member created by RTVBDSRC and run it to recreate the binding directory. Note that the old binding directory will be moved to QTEMP (which you could probably change to QRPLOBJ if necessary).

Here is the CL source for RTVBDSRC:

 RTVBDSRC:   PGM        PARM(&BNDDIR &SRCFILE &SRCMBR &REPLACE)
             DCL        VAR(&BNDDIR) TYPE(*CHAR) LEN(20)
             DCL        VAR(&SRCFILE) TYPE(*CHAR) LEN(20)
             DCL        VAR(&SRCMBR) TYPE(*CHAR) LEN(10)
             DCL        VAR(&REPLACE) TYPE(*LGL)
             DCL        VAR(&FOUND) TYPE(*LGL) VALUE('1')
             DCL        VAR(&RTNLIB) TYPE(*CHAR) LEN(10)
             DCL        VAR(&RTNMBR) TYPE(*CHAR) LEN(10)
             DCLF       FILE(QABNDBND)
             MONMSG     MSGID(CPF0000)

   /*  Check for existing binding directory  */
             CHKOBJ     OBJ(%SST(&BNDDIR 11 10)/%SST(&BNDDIR 01 10)) +
                          OBJTYPE(*BNDDIR)
             MONMSG MSGID(CPF9800) EXEC(DO)
               SNDPGMMSG  MSGID(CPF9897) MSGF(QCPFMSG) MSGDTA('Binding +
                            directory not found. See joblog for +
                            low-level messages.') MSGTYPE(*ESCAPE)
                RETURN
             ENDDO

    /*  Get the bnddir's library name for "hard" qualifier.  */
             RTVOBJD    OBJ(%SST(&BNDDIR 11 10)/%SST(&BNDDIR 01 10)) +
                          OBJTYPE(*BNDDIR) RTNLIB(&RTNLIB)
             IF         COND(&RTNLIB *NE ' ') THEN(DO)
               CHGVAR     VAR(%SST(&BNDDIR 11 10)) VALUE(&RTNLIB)
               CHGVAR     VAR(&RTNLIB) VALUE(' ')
             ENDDO

    /*  Translate src mbr name to bnddir name, if requested.  */
             IF         COND(&SRCMBR = '*BNDDIR') THEN(DO)
               CHGVAR     VAR(&SRCMBR) VALUE(%SST(&BNDDIR 01 10))
             ENDDO

    /*  Get BD library and member names  */
             RTVMBRD    FILE(%SST(&SRCFILE 11 10)/%SST(&SRCFILE 01 +
                          10)) MBR(&SRCMBR) RTNLIB(&RTNLIB) +
                          RTNMBR(&RTNMBR)
             MONMSG     MSGID(CPF9815) EXEC(DO)
               ADDPFM     FILE(%SST(&SRCFILE 11 10)/%SST(&SRCFILE 01 +
                            10)) MBR(&SRCMBR) TEXT('Retrieve Binding +
                            Directory Source created member')
               CHGVAR     VAR(&FOUND) VALUE('0')
             ENDDO
             IF         COND(&RTNMBR *NE ' ') THEN(DO)
                CHGVAR     VAR(&SRCMBR) VALUE(&RTNMBR)
             ENDDO

  /*  If member already exists, replace it, if REPLACE(*YES)  */
             IF         COND(&FOUND = '1') THEN(DO)
               IF         COND(&REPLACE = '1') THEN(DO)
               CLRPFM     FILE(%SST(&SRCFILE 11 10)/%SST(&SRCFILE 01 +
                            10)) MBR(&SRCMBR)
               ENDDO
               ELSE  DO
                 SNDPGMMSG  MSGID(CPF9897) MSGF(QCPFMSG) +
                            MSGDTA('Member' *BCAT &SRCMBR +
                              *BCAT 'already exists. Specify +
                              a different source member name +
                              or REPLACE(*YES) to replace the +
                              existing member.') MSGTYPE(*ESCAPE)
                  RETURN
               ENDDO
             ENDDO

  DSPBD:    DSPBNDDIR  BNDDIR(%SST(&BNDDIR 11 10)/%SST(&BNDDIR 01 +
                          10)) OUTPUT(*OUTFILE) +
                          OUTFILE(QTEMP/BNDDIR) OUTMBR(%SST(&BNDDIR +
                          01 10))
             MONMSG     MSGID(CPF9800 CPF5D15) EXEC(DO)
               SNDPGMMSG  MSGID(CPF9897) MSGF(QCPFMSG) MSGDTA('Cannot +
                            create binding directory source for' +
                            *BCAT %SST(&BNDDIR 01 10) *BCAT 'in' +
                            *BCAT %SST(&BNDDIR 11 10) *TCAT '. See +
                            joblog messages for details.') +
                            MSGTYPE(*ESCAPE)
             ENDDO
             OVRDBF     FILE(QABNDBND) TOFILE(QTEMP/BNDDIR) +
                          MBR(%SST(&BNDDIR 01 10))

 READNEXT:   RCVF       RCDFMT(QBNDSPBD)
             MONMSG     MSGID(CPF0864) EXEC(DO)
             GOTO       ENDPGM
             ENDDO
 WRTSRC:     WRTSRCREC  SRCFILE(%SST(&SRCFILE 11 10)/%SST(&SRCFILE +
                          01 10)) SRCMBR(&SRCMBR) DATA('ADDBNDDIRE +
                          BNDDIR(' *TCAT %SST(&BNDDIR 11 10) *TCAT +
                          '/' *TCAT %SST(&BNDDIR 01 10) *TCAT ') +
                          OBJ((' *TCAT &BNOLNM *TCAT '/' *TCAT +
                          &BNOBNM *BCAT &BNOBTP *TCAT '))')
             GOTO       READNEXT
 ENDPGM:     ENDPGM

The output from DSPBNDDIR is generated at the statement tagged with the DSPBD label. This creates in QTEMP the output file of all the entries in the specified binding directory.

The statement tagged with the READNEXT label reads each record out of the output file. Then, at the statement tagged with WRTSRC, an ADDBNDDIRE command is written to the user-specified source file member.

WRTSRCREC is a simple output routine I slapped together to write to a known source file member from within CL programs. The source member must already exist, and the name must be correct. It does no error checking. The WRTSRCREC CL command and the RPG IV command processing program is listed below.

Command Source: WRTSRCREC

 WRTSRCREC:  CMD        PROMPT('Write Source Record')
             /*         Command processing program is WRTSRCREC  */
             PARM       KWD(SRCFILE) TYPE(SRCF) MIN(1) +
                          PROMPT('Source file')
 SRCF:       QUAL       TYPE(*NAME) DFT(QCLSRC) SPCVAL((QCLSRC) +
                          (QCLLESRC QRPGLESRC)) EXPR(*YES)
             QUAL       TYPE(*NAME) DFT(*LIBL) SPCVAL((*LIBL) +
                          (*CURLIB)) EXPR(*YES) PROMPT('Library')
             PARM       KWD(SRCMBR) TYPE(*NAME) SPCVAL((*FIRST) +
                          (*LAST)) MIN(1) EXPR(*YES) PROMPT('Source +
                          member')
             PARM       KWD(DATA) TYPE(*CHAR) LEN(250) +
                          SPCVAL((*BLANKS ' ')) EXPR(*YES) +
                          VARY(*YES) PROMPT('Source data')

RPG IV Source: WRTSRCREC

     H DFTACTGRP(*NO)   BNDDIR('QC2LE')
     H OPTION(*NODEBUGIO : *SRCSTMT)
     FQSRC      O  A F  266        DISK    USROPN INFDS(INFDS)

     D WRTSRCREC       PR                  Extpgm('WRTSRCREC')
     D  SRCFILE                      20A   CONST
     D  SRCMBR                       10A   CONST
     D  inData                      252A

     D WRTSRCREC       PI
     D  SRCFILE                      20A   CONST
     D  SRCMBR                       10A   CONST
     D  inData                      252A

     D Data            DS                  Based(pData)
     D  nInDataLen                    5I 0
     D  szData                      250A

     D INFDS           DS
     D  szSrcFileName         83     92A
     D  szSrcFileLib          93    102A
     D  szSrcFileMbr         129    138A
     D  nSrcRecLen           125    126I 0
     D  nSrcRecCnt           156    159I 0

     D PSDS           SDS
     D  JobName                      10A   Overlay(PSDS:244)
     D  JobUser                      10A   Overlay(PSDS:254)
     D  JobNbr                        6A   Overlay(PSDS:264)

     D system          PR            10I 0 ExtProc('system')
     D   szCmd                         *   Value OPTIONS(*STRING)

     D szOvr           S            256A   Varying
     D today           S               D   Inz(*SYS)
     D SRCSEQ          S              6S 2
     D SRCDATE         S              6S 0
     D SRCDATA         S            250A

     C                   eval      *INLR  = *ON

     C                   eval      szOvr = 'OVRDBF FILE(QSRC) TOFILE('     +
     C                                 %TrimR(%SUBST(SRCFILE:11:10)) + '/' +
     C                                 %TrimR(%SUBST(SRCFILE:01:10)) + ')' +
     C                                 ' MBR(' + %TrimR(srcmbr) + ')' +
     C                                 ' SECURE(*YES)'

     C                   callp     system(szOvr)
     C                   open      QSRC
     C                   if        NOT %OPEN(QSRC)
     C                   return
     C                   endif

     C                   eval      pData = %addr(inData)
     C                   if        nInDataLen > nSrcRecLen
     C                   eval      srcData = %subst(szData:1:nSrcRecLen)
     C                   eval      %Subst(srcData : nSrcRecLen : 1) = '-'
     C                   eval      srcseq = nSrcRecCnt + 1
     C                   except    OUTPUT
     C                   eval      srcData = %subst(szData:nSrcRecLen)
     C                   eval      srcseq = nSrcRecCnt + 1
     C                   except    OUTPUT
     C                   else
     C                   eval      srcseq = nSrcRecCnt + 1
     C                   eval      srcData = %subst(szData:1:nInDataLen)
     C                   except    OUTPUT
     C                   endif
     C                   CLOSE     QSRC
     C                   return
     OQSRC      EADD         OUTPUT
     O                       SRCSEQ               6
     O                       SRCDATE             12
     O                       SRCDATA            266

This program writes out a line to a source file member. If the input line length is longer than the source member record length, it breaks the line into two parts and writes out two lines. Obviously, if you want it to paginate CL commands, you'll have to add your own logic.

As always, the source code for this command is available for free download at www.rpgiv.com/downloads. Just click on the RPG Developer downloads link at the top of the page.

While binding directories are not necessarily something that we need to maintain on a frequent basis, having the basic tools necessary to maintain them is important. RTVBDSRC is one small step toward making things a bit less frustrating.

Bob Cozzi is a programmer/consultant, writer/author, and software developer of the RPG xTools, a popular add-on subprocedure library for RPG IV. His book The Modern RPG Language has been the most widely used RPG programming book for nearly two decades. He, along with others, speaks at and runs the highly-popular RPG World conference for RPG programmers.

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: