Converting PDF Files to Text
Q I have been tasked with finding the best way (if there is one) to convert a PDF file back to a text file that our IBM i can read. I can open the file in Adobe and save it as a text file, so I know the process is possible, but I must automate it entirely on the IBM i, as the PDF files are in the IFS directory. Someone here said that they thought FTP might do the conversion, but that sounds strange to me.
A FTP will not do the conversion, although if you don't specify BINARY file transfer mode, FTP will try to convert the data, but you'll still get garbage. You can use the open source Java library iText (iTextPDF.com) to read the PDF and parse out the data. Another open source Java library is the Apache Software Foundation’s PDFBox (PDFBox.Apache.org). You could also try porting the C Poppler Utilities package (poppler.freedesktop.org), which includes the command line utility pdftotext. The Poppler utilities also ship as binary code with RedHat and other Linux distributions, so you could perform the conversion using a Linux LPAR, if you’re running one.
—Tommy Holden, TK, and Mel Beckman, from the iPro Developer Forums
SRVPGM Binding Terminology
Q I've read a lot of material on the subject of SRVPGM binding, but I still have questions. For example, consider a binder source MYBNDSRC1 that exports formatPhoneNumber and a service program MYSRVPGM1 with exported procedure formatPhoneNumber. Service program MYSRVPGM1 uses MYBNDSRC1 as the binder source. The binding directory MYBNDDIR1 has an entry of *LIBL/MYSRVPGM1 (the *LIBL is important for my question). Finally, the program MYPGM1 uses formatPhoneNumber and binds to binding directory MYBNDDIR1.
If I changed my *LIBL to include a library that contains another copy of MYSRVPGM1, and that copy of MYSRVPGM1 uses a period to separate the phone number instead of a dash, when MYPGM1 calls formatPhoneNumber, should I get a dash or a period as my separator?
Where I am heading with this is a deeper understanding of the verbiage "bound," especially in the context of "dynamic" and "static." From my understanding, using the term "dynamically bound" is a misnomer when it comes to talking about binding and service programs. Is my understanding correct?
A Here is a summary of the terminology:
- Dynamic Binding: A call to a program (it's possible, but rather unusual, to make *SRVPGM calls dynamic with APIs).
- Static Binding: The binding is done when the program is created. This is how both modules and service programs are bound.
- Bind by Copy: A type of static binding where the contents of a module are copied into the caller's program object. Because the called procedures are inside the same disk object, nothing needs to be resolved at run time (which happens when you bind modules together directly).
- Bind by Reference: A type of static binding where exports/imports are resolved when a program is created, but they may refer to other disk objects that will be located at run time (which happens when you bind a program to a *SRVPGM or a service program to another *SRVPGM).
The procedure in the service program is "dynamically bound" because the service program is located at run time. This is true for both *LIBL and for hard-coded libraries. The service program can be changed after the program is created without making any changes to the program.
A procedure in one of the program modules is "statically bound" because it is permanently part of the program. The module with the called procedure can't be changed without also changing the program.
For a statically-bound call, you can think of the link between the call statement and the called procedure as being a permanent solid line. For a dynamically-bound call, you can think of the link a dotted line that doesn't become solid until the program is called and the service program located. The line becomes dotted once again when the program's activation group is reclaimed.
It might seem that using a named library in your binding directory means you can use a solid line to connect the call with the procedure, but the service program isn't attached to the program; the service program can be deleted and recreated, or changed, as long as the service program is there when the program is called. So a service program is dynamically bound whether or not you have *LIBL in your binding directory.
If you use *LIBL to locate the service program, and one version of the service program uses a dot to separate the phone number while the other uses a hyphen, then your program will pick up whichever SRVPGM is in the library list.
But you have to be careful when testing this. The service program is only bound once, when the program is activated into the activation group (although binding can be delayed, binding is only done once per activation of the program). If you call the program with HYPHEN/SRVPGM in the library list, then change the library list so that DOT/SRVPGM is first in the LIBL, the program will continue to use HYPHEN/SRVPGM until the program's activation group is reclaimed.
If the program is running in the default activation group (due to being in *CALLER say called from the command line), then you won't be able to reclaim the activation group, and you'll never be able to get it to use the other SRVPGM in that job. Every call will use the service program that was found when the program was first called.
If the program is running in a named activation group, then you can RCLACTGRP to reclaim the program's activation group; on the next call, it will pick up the version of the SRVPGM in the library list.
If the program uses a *NEW activation group, it will pick up the SRVPGM in the library list every time you call the program.
If you use *LIBL on the BNDSRVPGM parameter or in a binding directory entry, then the library list will be used to locate the *SRVPGM, which means the *SRVPGM can change at run time. This is indeed considered "static binding" and, specifically, "bind by reference." It's worth considering that the subprocedure (the SRVPGM export) has already been resolved to a number, so if you insert another SRVPGM in its place (which uses periods instead of dashes, in your example), that SRVPGM must be careful to use the same numbered subprocedures and the same signature, or it won't work. But, indeed, if the same export number is the same procedure, and both SRVPGMs have the same signature, you can switch out the SRVPGM based on *LIBL and get different results.
However, if you want to change either the name of the SRVPGM being called or the export number of the procedure you're calling, you need to re-bind them. In this case, the bind is still considered "static," because those details are fixed at bind time (program creation time).
—Barbara Morris and Scott Klement, from the iPro Developer Forums
Zoned Decimals in CL
Q I read a file in my CLLE program with one field as numeric data (6S 0), then I pass this data as parameters to my RPGLE program, like so:
D DATA_ALPHA 20
D DATA_NUMP 6S 0
D MYPGM Pi
D DATA_ALPHA 20
D DATA_NUMP 6S 0
Why doesn't it work?
A CL doesn't support zoned decimal except as a character field. If your goal is to pass parameters from CL, consider making them packed (6p 0), because that's what CL uses for its *DEC data type. Alternatively, you can pass zoned numeric values as CL character strings to RPG and have RPG receive them as zoned numeric fields. This only works if the value is a positive integer (i.e., zero decimal positions). For example:
dcl &zonenbr *char 7
chgvar &zonenbr &nbr
call myrpg (&zonenbr)
—Scott Klement and Lynne Noll, from the iPro Developer Forums
List Contents of a Directory File
Q I have a folder in the IFS containing several thousand PDF files. Using Windows Explorer, I can see them all, but WRKLNK and DSPLNK on my IBM i don’t seem to show all the PDFs. Instead, they show a few and then skip one or two and continue. The PDFs are not hidden when I look at the properties. I do see a CPDA092 message on the screen.
A The text for the CPDA092 message is “Cannot display all of the objects requested.” The recovery process given for this message is the somewhat unhelpful “Specify a pattern with more characters to match fewer objects.” IBM says the reason is a 16MB limit on the total size of the directory listing returned from a WRKLNK or DSPLNK query.
A simple way to get a list of the contents of a directory into a file is to build an “ls” command, which doesn’t have this limitation, and send it to QSH:
ADDENVVAR ENVVAR(QIBM_QSH_CMD_ESCAPE_MSG) VALUE('Y') +
CRTPF FILE(QTEMP/MYFILE) RCDLEN(640)
OVRDBF FILE(STDOUT) TOFILE(QTEMP/MYFILE)
CHGVAR VAR(&QSHCMD) VALUE('ls /yourpathname')
—Brian Rusch and Mel Beckman, from the iPro Developer Forums