|
Odds and Ends
Dear Readers:
Response
to the first Odds and Ends column was
overwhelmingly positive, so I am going to
continue this feature. In each Odds and Ends
edition, I will feature answers to questions
that, in my opinion, aren't worth featuring
as Midrange Guru tips by themselves.
-- Ted
Question:
How can I make my iSeries run an FTP session
in batch mode?
Answer:
Put the FTP commands in a source physical
file member. For example, you might call the
member FTPIN and put it in file MYLIB/MYFILE.
Create another member for FTP output.
Use the Override with Database File (OVRDBF)
command to override INPUT and OUTPUT to these
members.
For example:
OVRDBF INPUT TOFILE(MYLIB/MYFILE) MBR(FTPIN)
OVRDBF OUTPUT TOFILE(MYLIB/MYFILE) MBR(FTPOUT)
FTP HostNameOrIP#
DLTOVR (INPUT OUTPUT)
Question:
Since I started programming, which was over 20
years ago, I have heard from time to time that
I/O operations generate a lot of machine code.
I was under the impression that the following
code was not as good as another example:
Fpaywork uf e disk
C read PayRec
C dow not %eof(PayWork)
C update PayRec
C read PayRec
C enddo
C*
C eval *inLR = *on
This is the other example I was referring to:
Fpaywork uf e disk
C exsr Read
C dow not %eof(PayWork)
C update PayRec
C exsr Read
C enddo
C*
C eval *inLR = *on
C*
C Read begsr
C read PayRec
C endsr
I was told that the reason for this is due
to the fact that the machine code for the read
is only generated once in the first example but
it is generated twice in the second example.
What's the truth? Does it really matter if I
code an I/O twice, as in the first example, or
just once, as in the second?
Answer:
I had to ask the RPG compiler development team
in Toronto to get an answer to this one. Here
is what Barbara Morris had to say:
The RPG compilers already generate
their own internal subroutines for the I/O.
Also, for RPG IVat least, part of the I/O is
done in the RPG runtime. The setup for calling
the internal subroutine will be greater than
the setup for an EXSR, but most of the duplication
has already been taken care of for you.
The experiments I've done indicate that each
read adds about 1,500 bytes and each EXSR adds
about 150 bytes. That's probably not enough
difference to warrant putting the read in the
subroutine just for the sake of reducing the
size of the program, especially just for the
case of two, as in your loop.
I guess I'd say having two READs is better
than having EXSRs, just because it's easier
to see what's going on. Although, if there
was a strict standard for subroutine naming,
and that subroutine was named read_payrec,
the EXSRs wouldn't be too bad.
Question:
MHCPDT is an alphanumeric field read from a
file. We want to reference it and parts of
it as numerics, so we created the following
data structure:
I DS
I 1 6 MHCPDT
I 1 60DSCPDT
I 1 20MHCPYR
I 3 60MHCPKY
I 3 40MHCPMO
I 5 60MHCPDA
When we reference DSCPDT in an IFEQ
statement, it gives a decimal data error.
When we remove MHCPDT from the data structure
and add a MOVE calculation to copy MHCPDT to
DSCPDT, it works fine. We're confused. Could
you shed some light on this?
Answer:
MHCPDT probably has some blanks. When you
use the MOVE opcode to copy an alpha value
to a numeric one, RPG converts the data to
numeric if necessary. For example, a blank
gets converted to a zero. That's why the MOVE
works. However, when the system copies a value
into a data structure no conversion is done
to other subfields that occupy the same
positions.
Question:
How can I make SDA sort the fields in a record
format in a display file by position?
Answer:
From the Work with Display Records panel, use
option 12 (Design Image) to select the format.
Press F4 to display the fields, then F6 to
sort them.
Question:
Can I use SQL to change only the rightmost
three positions of a six-character field?
Answer:
Yes. Use the substring function to extract the
portions you want to keep intact. Concatenate
them to the new value:
update mytable
set class = substring(class,1,3) concat 'XYZ'
The first three characters of class remain the
same. The last three are changed to XYZ.
Question:
We typically produce RPG IV programs that use
subfile error handling. We have run into a
case where we just wanted to use a CL program
that uses a regular screen format, a message
subfile format, and a message subfile control
format. Do you know if or how we can do that?
Answer:
Here's an example that will do exactly what
you want. Compile JKL001D as a display file
and JKL001C as a CLP or CLLE:
A* member JKL001D
A DSPSIZ(24 80 *DS3)
A R FORMAT01
A CA03(03)
A OVERLAY
A 1 31'Generate Some Report'
A 1 72DATE
A EDTCDE(Y)
A 2 72TIME
A 5 8'Enter range of dates in MMDDYY for-
A mat. You may leave ending date'
A 6 8'blank if the report is to be run f-
A or only one day.'
A 8 8'Beginning date ...................-
A ...........:'
A SBGNDATE 6Y 0B 8 55EDTCDE(4)
A 9 8'Ending date ......................-
A ...........:'
A SENDDATE 6Y 0B 9 55EDTCDE(4)
A 22 6'F3=Cancel request'
A 22 27'Enter=Generate report'
A
A R MSGSFL SFL
A SFLMSGRCD(24)
A MSGKEY SFLMSGKEY
A PGMNAM SFLPGMQ
A
A R MSGCTL SFLCTL(MSGSFL)
A SFLSIZ(0010)
A SFLPAG(0001)
A OVERLAY
A SFLDSP
A SFLDSPCTL
A SFLINZ
A N87 SFLEND
A PGMNAM SFLPGMQ(10)
/* member JKL001C */
PGM
DCLF FILE(JKL001D)
DCL VAR(&NBGNDATE) TYPE(*CHAR) LEN(6)
DCL VAR(&NENDDATE) TYPE(*CHAR) LEN(6)
DCL VAR(&WBGNDATE) TYPE(*CHAR) LEN(8)
DCL VAR(&WENDDATE) TYPE(*CHAR) LEN(8)
DCL VAR(&DATEWDW8) TYPE(*CHAR) LEN(8)
DCL VAR(&DATEWDW6) TYPE(*CHAR) LEN(8)
DCL VAR(&MSGTXT) TYPE(*CHAR) LEN(78)
/* Variables needed to determine program name */
DCL VAR(&MSGKEY) TYPE(*CHAR) LEN(4)
DCL VAR(&SENDER) TYPE(*CHAR) LEN(80)
MONMSG MSGID(CPF0000) EXEC(GOTO CMDLBL(ERROR))
Prelims:
/* Determine the name of the program */
SNDPGMMSG MSG('Dummy message') TOPGMQ(*SAME) +
MSGTYPE(*INFO) KEYVAR(&MSGKEY)
RCVMSG PGMQ(*SAME) MSGTYPE(*INFO) MSGKEY(&MSGKEY) +
RMV(*YES) SENDER(&SENDER)
CHGVAR VAR(&PGMNAM) VALUE(%SST(&SENDER 27 10))
/* Get earliest acceptable date */
CHGVAR VAR(&DATEWDW8) VALUE('20020101') /* yymd */
CVTDAT DATE(&DATEWDW8) TOVAR(&DATEWDW6) +
FROMFMT(*YYMD) TOFMT(*JOB) TOSEP(*JOB) /* +
job date format */
/* Create the msgf for demo purposes. Normally this msgf */
/* would already exist in a production library */
CRTMSGF MSGF(QTEMP/JKLMSG)
MONMSG MSGID(CPF2112) EXEC(GOTO BEGIN)
ADDMSGD MSGID(JKL1001) MSGF(QTEMP/JKLMSG) +
MSG('Ending date must not be before +
beginning date')
ADDMSGD MSGID(JKL1002) MSGF(QTEMP/JKLMSG) MSG('Dates +
must not be before &1.') FMT((*CHAR 8))
Begin:
RMVMSG CLEAR(*ALL)
/* loop until F3 pressed or data is valid */
GetInput:
SNDF RCDFMT(MSGCTL)
SNDRCVF RCDFMT(FORMAT01)
IF COND(&IN03) THEN(GOTO EndPgm)
RMVMSG CLEAR(*ALL)
CHGVAR &NBGNDATE &SBGNDATE
CVTDAT DATE(&NBGNDATE) TOVAR(&WBGNDATE) +
FROMFMT(*JOB) TOFMT(*YYMD) TOSEP(*NONE)
CHGVAR &NENDDATE &SENDDATE
CVTDAT DATE(&NENDDATE) TOVAR(&WENDDATE) +
FROMFMT(*JOB) TOFMT(*YYMD) TOSEP(*NONE)
IF COND(&WENDDATE *LT &WBGNDATE) THEN(DO)
SNDPGMMSG MSGID(JKL1001) MSGF(QTEMP/JKLMSG) TOPGMQ(*SAME)
GOTO CMDLBL(GETINPUT)
ENDDO
IF COND((&WBGNDATE *LT &DATEWDW8) *OR +
(&WENDDATE *LT &DATEWDW8)) THEN(DO)
SNDPGMMSG MSGID(JKL1002) MSGF(QTEMP/JKLMSG) +
MSGDTA(&DATEWDW6) TOPGMQ(*SAME)
GOTO CMDLBL(GETINPUT)
ENDDO
EndInput:
/* Insert commands to process input here */
RETURN
Error:
RCVMSG MSGQ(*PGMQ) MSGTYPE(*EXCP) MSG(&MSGTXT)
MONMSG MSGID(CPF0000)
SNDPGMMSG MSG(&MSGTXT) TOPGMQ(*SAME)
MONMSG MSGID(CPF0000)
GOTO CMDLBL(GETINPUT)
EndPgm:
ENDPGM
This short program allows a user to enter
two dates that will be used in building a
report of some sort. It checks for several
errors--invalid dates, second date before first
date, and date not in current year. When it
finds an error, it sends it to the error subfile.
|