Showing posts with label AS400 FAQs. Show all posts
Showing posts with label AS400 FAQs. Show all posts

Friday, July 10, 2009

(FAQ) How To Convert Numeric To Character Format In AS/400 RPG-ILE.

We can convert numeric to character format in two ways; using BIF %CHAR and %EDITC.

1. %CHAR(expression{:format})

%CHAR converts the value of the expression from graphic, UCS-2, numeric, date,time or timestamp data to type character. The converted value remains unchanged,
but is returned in a format that is compatible with character data.

Sample:






a. To format the time and date with the default formats, use this:

result = ’It is ’ + %CHAR(time) + ’ on ’ + %CHAR(date)

b. To format the time and date with specific formats, use this:

result = ’It is ’ + %CHAR(time : *hms:) + ’ on ’ + %CHAR(date : *iso)

c. You can use %subst with the %char result if you only want part of the result:

result = ’The time is now ’ + %SUBST (%CHAR(time):1:5) + ’.’

d. Use %CHAR to convert a graphic value to character so it can be concatenated with a character value:

result = ’The customer’’s name is ’ + %CHAR(Name) + ’.’

e. Use %CHAR to convert a number to character format:

result = ’You have ’ + %char(points) + ’ points.’


2. %EDITC(numeric : editcode {: *ASTFILL | *CURSYM | currency-symbol})

This function returns a character result representing the numeric value edited
according to the edit code. In general, the rules for the numeric value and edit
code are identical to those for editing numeric values in output specifications. The
third parameter is optional, and if specified, must be one of:

a. *ASTFILL
Indicates that asterisk protection is to be used. This means that leading
zeros are replaced with asterisks in the returned value. For example,
%EDITC(-0012.5 : ’K’ : *ASTFILL) returns ’***12.5-’.

b. *CURSYM
Indicates that a floating currency symbol is to be used. The actual symbol
will be the one specified on the control specification in the CURSYM
keyword, or the default, ’$’. When *CURSYM is specified, the currency
symbol is placed in the the result just before the first significant digit. For
example, %EDITC(0012.5 : ’K’ : *CURSYM) returns ’ $12.5 ’.

c. currency-symbol
Indicates that floating currency is to be used with the provided currency
symbol. It must be a 1-byte character constant (literal, named constant or
expression that can be evaluated at compile time). For example,
%EDITC(0012.5 : ’K’ : ’X’) returns ’ X12.5 ’.

List of Edit Codes:


X – (obsolete; but it does retain leading zeros!)
Y – Slash insertion for date
Z – Zero suppresses leading zeros (no decimals, negatives)


Sample:




Read More...

Monday, July 6, 2009

(FAQ) How To Know Which AS/400 RPG Programs Are Using Specific Objects

It is very important for us to know which programs are using specific objects. For example you have updated a file, added/deleted some fields or changed field lengths. We need to know which programs need to be recompiled. We can use DSPPGMREF (Display Program Reference) command to create a file that maps all objects to all programs.

Command:
DSPPGMREF PGM(*ALL) OUTPUT(*OUTFILE) OBJTYPE(*ALL) OUTFILE(MYLIB/MYFILE)

Now you have created a file MYFILE in library MYLIB that contains list of programs with objects referenced to them.

To know which program uses MYPF file we can use SQL to retrieve it.

Command:
STRSQL

SELECT * FORM MYLIB/MYFILE
WHERE WHFNAM = 'MYPF'

Note:
WHFNAM is the object referencing to the program.


Read More...

(FAQ) AS/400 Journal Restrictions

The Create Journal (CRTJRN) command creates a journal as a local journal with the specified attributes, and attaches the specified journal receiver to the journal. Once a journal is created, object changes can be journaled to it or user entries can be sent to it. The journal state of the created journal is *ACTIVE.

Restrictions:
• A journal cannot be created in the library QTEMP.
• The receiver specified must be created before issuing this command and it must be empty (that is, the receiver must not have been previously attached to a journal or have been in the process of being attached to a journal).
• This command cannot be used to create a remote journal. See the ADDRMTJRN (Add Remote Journal) command description or the Add Remote Journal (QjoAddRemoteJournal) API in the System API Reference information in the iSeries Information Center at http://www.ibm.com/eserver/iseries/infocenter.
• If RCVSIZOPT(*MAXOPT1 or *MAXOPT2 or *MAXOPT3) is not to be in effect for the journal, the maximum threshold value that can be specified for any journal receiver being attached is 1,919,999 kilobytes.
• If the library to contain the journal is on an independent ASP then the journal receiver specified must be located on an independent ASP that is in the same ASP group as the journal's library. Likewise, if the library to contain the journal is not on an independent ASP, then the journal receiver specified cannot be located on an independent ASP.
• If the library to contain the journal is on an independent ASP then ASP(*LIBASP) must be specified.
• RCVSIZOPT(*MINFIXLEN) and FIXLENDTA cannot be used for the system security audit journal QSYS/QAUDJRN. Journal entries in the security audit journal are required to contain all possible data that could be used for auditing purposes.


Read More...

(FAQ) How To Use Commitment Control In AS/400

With Commitment Control, you ensure one of two outcomes for the file operations: either all of the file operations are successful or none of the file operations has any effect. In this way, you process a group of operations as a unit.

To use Commitment Control, you do the following:

• Use the CL commands CRTJRN (Create Journal), CRTJRNRCV (Create Journal Receiver) and STRJRNPF (Journal Physical File) to prepare for using commitment control, and the CL commands STRCMTCTL (Start Commitment Control) and ENDCMTCTL (End Commitment Control) to notify the system when you want to start and end commitment control. See the CL Reference for information on these commands.
• Specify commitment control on the file-description specifications of the files you want under commitment control.
• Use the COMMIT operation code to apply a group of changes to files under commitment control, or use the ROLLBACK operation code to eliminate the pending group of changes to files under commitment control.

Starting and Ending Commitment Control

The CL command STRCMTCTL notifies the system that you want to process files under commitment control.
The LCKLVL (Lock Level) parameter allows you to select the level at which records are locked under commitment control.
When you complete a group of changes with a COMMIT operation, you can specify a label to identify the end of the group. In the event of an abnormal job end, this identification label is written to a file, message queue, or data area so that you know which group of changes is the last group to be completed successfully. You specify this file, message queue, or data area on the STRCMTCTL command.
Before you call any program that processes files specified for commitment control, issue the STRCMTCTL command. If you call a program that opens a file specified for commitment control before you issue the STRCMTCTL command, the opening of the file will fail.
The CL command ENDCMTCTL notifies the system that your routing step has finished processing files under commitment control.

Specifying Files for Commitment Control

When a program specifies commitment control for a file, the specification applies only to the input and output operations made by this program for this file. Commitment control does not apply to operations other than input and output operations. It does not apply to files that do not have commitment control specified in the program doing the input or output operation.
When more than one program accesses a file as a shared file, all or none of the programs must specify the file to be under commitment control.

Commitment Control Operations

The COMMIT operation tells the system that you have completed a group of changes to the files under commitment control. The ROLLBACK operation eliminates the current group of changes to the files under commitment control.
If the system fails, it implicitly issues a ROLLBACK operation. You can check the identity of the last successfully completed group of changes using the label you specify in F1 of the COMMIT operation code, and the notify-object you specify on the STRCMTCTL command.
At the end of a routing step, or when you issue the ENDCMTCTL command, the OS/400 system issues an implicit ROLLBACK, which eliminates any changes since the last ROLLBACK or COMMIT operation that you issued. To ensure that all your file operations have effect, issue a COMMIT operation before ending a routing step operating under commitment control.
The OPEN operation permits input and output operations to be made to a file and the CLOSE operation stops input and output operations from being made to a file. However, the OPEN and CLOSE operations do not affect the COMMIT and ROLLBACK operations. A COMMIT or ROLLBACK operation affects a file, even after the file has been closed. For example, your program may include the following steps:
1. Issue COMMIT (for files already opened under commitment control).
2. Open a file specified for commitment control.
3. Perform some input and output operations to this file.
4. Close the file.
5. Issue ROLLBACK.
The changes made at step 3 are rolled back by the ROLLBACK operation at step 5, even though the file has been closed at step 4. The ROLLBACK operation could be issued from another program in the same routing step.
A program does not have to operate all its files under commitment control, and to do so may adversely affect performance. The COMMIT and ROLLBACK operations have no effect on files that are not under commitment control.
When multiple devices are attached to an application program, and commitment control is in effect for the files this program uses, the COMMIT or ROLLBACK operations continue to work on a file basis and not by device. The database may be updated with partially completed COMMIT blocks or changes that other users have completed may be eliminated. It is your responsibility to ensure this does not happen.

Commitment Control Locks

On the STRCMTCTL command, you specify a level of locking, either LCKLVL (*ALL) or LCKLVL (*CHG). When your program is operating under commitment control and has processed an input or output operation on a record in a file under commitment control, the record is locked by commitment control as follows:
• Your program can access the record.
• Another program in your routing step, with this file under commitment control, can read the record. If the file is a shared file, the second program can also update the record.
• Another program in your routing step that does not have this file under commitment control cannot read or update the record.
• Another program in a separate routing step, with this file under commitment control, can read the record if you specified LCKLVL (*CHG), but it cannot read the record if you specified LCKLVL (*ALL). With either lock level, the next program cannot update the record.
• Another program that does not have this file under commitment control and that is not in your routing step can read but not update the record.
• Commitment control locks are different than normal locks, depend on the LCKLVL specified, and can only be released by the COMMIT and ROLLBACK operations.
• The COMMIT and ROLLBACK operations release the locks on the records. The UNLCK operation will not release records locked using commitment control. See the CL Reference for details on lock levels.
• The number of entries that can be locked under commitment control before the COMMIT or ROLLBACK operations are required may be limited.

Commitment Control in the Program Cycle

Commitment control is intended for full procedural files, where the input and output is under your control. Do not use commitment control with primary and secondary files, where input and output is under the control of the RPG/400 program cycle. The following are some of the reasons for this recommendation:
• You cannot issue a COMMIT operation for the last total output in your program.
• It is difficult to program within the cycle for recovery from a locked-record condition.
• Level indicators are not reset by the ROLLBACK operation.
• After a ROLLBACK operation, processing matching records may produce a sequence error.


Read More...

Friday, July 3, 2009

(FAQ) What are AS/400 Data Queue APIs?

Data queues are a type of system object that you can create, to which one high-level language (HLL) program can send data, and from which another HLL program can receive data. The receiving program can be waiting for the data, or can receive the data later.

Before using a data queue, you must first create it using the Create Data Queue (CRTDTAQ) command.

The data queue APIs are:

* Clear Data Queue (QCLRDTAQ) clears all entries from a data queue.


Required Parameter Group:

1 Data queue name Input Char(10)
2 Library name Input Char(10)

Optional Parameter Group:

3 Key order Input Char(2)
4 Length of key data Input Packed(3,0)
5 Key data Input Char(*)
6 Error code I/O Char(*)

Default Public Authority: *USE


* Receive Data Queue (QRCVDTAQ) receives data from the specified data queue.

Required Parameter Group:

1 Data queue name Input Char(10)
2 Library name Input Char(10)
3 Length of data Output Packed(5,0)
4 Data Output Char(*)
5 Wait time Input Packed(5,0)

Optional Parameter Group 1:

6 Key order Input Char(2)
7 Length of key data Input Packed(3,0)
8 Key data I/O Char(*)
9 Length of sender information Input Packed(3,0)
10 Sender information Output Char(*)

Optional Parameter Group 2:

11 Remove message Input Char(10)
12 Size of data receiver Input Packed(5,0)
13 Error code I/O Char(*)

Default Public Authority: *USE

* Retrieve Data Queue Description (QMHQRDQD) retrieves information about a data queue.

Required Parameter Group:

1 Receiver variable Output Char(*)
2 Length of receiver variable Input Binary(4)
3 Format name Input Char(8)
4 Qualified data queue name Input Char(20)

Default Public Authority: *USE

Threadsafe: Yes

* Retrieve Data Queue Message (QMHRDQM) retrieves an entry from a data queue without removing the entry.

Required Parameter Group:

1 Receiver variable Output Char(*)
2 Length of receiver variable Input Binary(4)
3 Format name Input Char(8)
4 Qualified data queue name Input Char(20)
5 Message selection information Input Char(*)
6 Length of message selection information Input Binary(4)
7 Message selection information format name Input Char(8)
8 Error code I/O Char(*)

Default Public Authority: *USE

Threadsafe: Yes

* Send to a Data Queue (QSNDDTAQ) sends data to the specified data queue.

Required Parameter Group:

1 Data queue name Input Char(10)
2 Library name Input Char(10)
3 Length of data Input Packed(5,0)
4 Data Input Char(*)

Optional Parameter Group 1:

5 Length of key data Input Packed(3,0)
6 Key data Input Char(*)

Optional Parameter Group 2:

7 Asynchronous request Input Char(10)

Optional Parameter Group 3:

8 Data is from a journal entry Input Char(10)

Default Public Authority: *USE

Threadsafe: Conditional

Read More...

(FAQ) How to DEBUG in Batch Mode.

To debug in batch mode, just follow these steps:

a) Submit your program to batch. The job MUST be held. You can either hold the job queue (HLDJOBQ) or hold the individual job (HLDJOB) or specify HOLD(*YES) on the SBMJOB command.

b) WRKSBMJOB/WRKUSRJOB/WRKACTJOB and find your submitted job. Note that the SBMJOB command gives you an informational message with the job name/number. What you need is the job name, user ID and job number - the fully qualified job name. Example: 123456/BUCK/MONTHEND

c) STRSRVJOB on the held batch job.

d) STRDBG on your program. Specify UPDPROD(*YES) if needed. You'll see the source listing if you compiled with DBGVIEW(*LIST) or *SOURCE.

e) Press F12 to exit - you cannot set a breakpoint yet.

f) Release the job so that it becomes STATUS(*ACTIVE).

g) You'll see a display asking if you want to debug or continue. Press F10 to debug.

h) DSPMODSRC to see the source listing again. Alternately, press F10 to step into the first instruction.

i) Now you can add your breakpoints.

j) Press F3 until you're back to the "debug or continue" display. Press Enter to run the program with your breakpoints set.

k) When you're done, do an ENDDBG and ENDSRVJOB.
Read More...

(FAQ) How To Transfer AS400 Objects to Another Machine

These steps will help us transfer objects from one machine to another.
1) Create save file. This save file will contain the objects that we are going to transfer. This must be created on both machines.

Command: CRTSAVF FILE(MYLIBRARY/MYSAVF) TEXT('My Save File')

2) Save Objects to save file (*SAVF). You can now save your desired object to your save file.

Command: SAVOBJ OBJ(MYSRCPF) LIB(MYLIB) DEV(*SAVF) SAVF(MYLIB/MYSAVF)

3) Now you can now transfer save file (*SAVF) to the other machine using FTP.

Command: FTP RMTSYS('111.11.11.11')
- User: UserName
- Password: Password
- Binary
- Put MyLib/MySavF MyLib/MySavf
- Quit

4) After completion of FTP, you can now restore your object (from the *SAVF) to machine 2.

Command: RSTOBJ OBJ(*ALL) SAVLIB(MYLIB) DEV(*SAVF) SAVF(MYLIB/MYSAVF)

Read More...

Thursday, July 2, 2009

(FAQ) Difference Between SKIPA/SPACEA and SKIPB/SPACEB

SKIPA (Skip After) keyword in printer files
Use this file-, record-, or field-level keyword to specify that the printer device is to skip to a specific line number after it prints one or more lines. The format of the keyword is: SKIPA(skip-after-line-number)

SPACEA (Space After) keyword in printer files
Use this record- or field-level keyword to specify that the printer device is to space some number of lines after it prints one or more lines. The format of the keyword is: SPACEA(space-after-value)

SKIPB (Skip Before) keyword in printer files
Use this file-, record-, or field-level keyword to specify that the printer device is to skip to a specific line number before it prints the next line(s). The format of the keyword is: SKIPB(skip-before-line-number)

SPACEB (Space Before) keyword in printer files
Use this record- or field-level keyword to specify that the printer device is to space some number of lines before it prints the next line or lines. The format of the keyword is: SPACEB(space-before-value)
Read More...

Wednesday, June 24, 2009

(FAQ) How To Use SQL's SELECT CASE Statement

SQL provides a mechanism for returning different values in a SELECT clause based on Boolean conditions: the CASE statement. This statement resembles Visual Basics Select Case statement.

The SQL CASE statement has WHEN, THEN, and ELSE clauses along with an END terminator. The syntax is:

CASE [expression]
WHEN [value | Boolean expression] THEN [return value]
[ELSE [return value]]
END

Sample:
SELECT
CASE @TestVal
WHEN 1 THEN 'First'
WHEN 2 THEN 'Second'
WHEN 3 THEN 'Third'
ELSE 'Other'
END


Read More...

(FAQ) How To Use OPNQRYF

The Open Query File (OPNQRYF) command opens a file to a set of database records that satisfies a database query request. Once opened, the file looks like a database file opened using the Open Database File (OPNDBF) command, and the records in the file are accessed by high-level language programs that share the open data path (ODP). The path is closed, and all query resources are deallocated, using the Close File (CLOF) command.

This command is used to do any combination of the following database functions:

* Join records from more than one file, member, and record format. The join may be either equal or non-equal in nature.
* Calculate new field values using numeric and character operations on field values and constants.
* Group records by like values of one or more fields, and calculate aggregate functions, such as minimum field value and average field value, for each group.
* Select a subset of the available records, with selection both before and after grouping the records.
* Arrange result records by the value of one or more key fields.

Restrictions:

1. The user can use overrides to change the file, library, and member names specified for the FILE parameter. Overrides are ignored for the file and library specified for the FORMAT parameter, unless FORMAT(*FILE) is specified. Parameter values specified on an override command, other than TOFILE, MBR, LVLCHK, WAITRCD, SEQONLY, or INHWRT and SHARE, are ignored by the OPNQRYF command.
2. The OPNQRYF command does not share an existing open data path (ODP) in the job or activation group. If an existing SHARE(*YES) ODP in the job or activation group has the same file, library, and member name as the open query file open data path (ODP), the query file does not open and an escape message is sent.
3. Each subsequent shared open operation must use the same open options (such as SEQONLY) that are in effect when the OPNQRYF command is run.
4. Some system functions (such as the Display Physical File Member (DSPPFM) and Copy File (CPYF) commands) do not share an existing open data path. The OPNQRYF command cannot be used with those functions.
5. The file opened with the OPNQRYF command cannot be used in programs written in BASIC because BASIC does not share an existing open data path.
6. This command is conditionally threadsafe. In multithreaded jobs, this command is not threadsafe for distributed files and fails for distributed files that use relational databases of type *SNA. This command is also not threadsafe and fails for Distributed Data Management (DDM) files of type *SNA.
7. Users of this command must have the following authorities:
* Execute (*EXECUTE) authority for any library that is needed to locate the files specified for the FILE and FORMAT parameters
* Object operational (*OBJOPR) authority for any physical or logical file specified for the FILE parameter, and one or more of the following data authorities for the physical file or based-on physical file members of a logical file member:
o Read (*READ) authority if the file is opened for input (using option *INP)
o Add (*ADD) authority if the file is opened for output (using option *OUT)
o Update (*UPD) authority if the file is opened for updates (using option *UPD)
o Delete (*DLT) authority if the file is opened for deletions (using option *DLT)
o *READ, *ADD, *UPD, and *DLT authority if the file is opened for all I/O operations (using option *ALL)
* *OBJOPR authority for any file specified for the FORMAT parameter
* Use (*USE) authority for any translate tables specified for the MAPFLD parameter (using option *USE)

Sample:
OVRDBF FILE(ARD) SHARE(*YES)

OPNQRYF FILE((ARD)) QRYSLT('ARDDEL *EQ ("' *CAT +
&DELCDE *CAT '") *AND ARPDCQ *NE ("' *CAT +
&ARDCQ *CAT '") *AND XDATE *LE ("' *CAT +
&TOYMD *CAT '")') KEYFLD(*FILE) +
MAPFLD((XPRN PRNCPL) (XDATE +
'%DIGITS(ARCRTD)'))
CALL PGM(ZPG308C) PARM(&PBFR &PBTO &COLDVF &COLDVT +
&PDTFR &PDTTO &USER &JOB &RDATE)

CLOF OPNID(ARD)

DLTOVR FILE(*ALL)


Read More...

(FAQ) How To Use Commitment Control

To use Commitment Control, you do the following:
• Use the CL commands CRTJRN (Create Journal), CRTJRNRCV (Create Journal Receiver) and STRJRNPF (Journal Physical File) to prepare for using commitment control, and the CL commands STRCMTCTL (Start Commitment Control) and ENDCMTCTL (End Commitment Control) to notify the system when you want to start and end commitment control. See the CL Reference for information on these commands.

• Specify commitment control on the file-description specifications of the files you want under commitment control.

• Use the COMMIT operation code to apply a group of changes to files under commitment control, or use the ROLLBACK operation code to eliminate the pending group of changes to files under commitment control.



Read More...

Tuesday, June 23, 2009

(FAQ) Here's a sample program that generates random number in AS/400 RPG ILE

Sample:

D C0Seed S 10I 0 Inz(0)
D C0RndNbr S 8F
D* Random Number Conversion
D RndNbr S 10I 0
D MaxNbr S 10I 0 Inz(9000000)
D MinNbr S 10I 0 Inz(1000000)
C*
C DoU RndNbr >= MinNbr
C*
C CallB 'CEERAN0'
C Parm C0Seed
C Parm C0RndNbr
C Parm *OMIT
C*
C Eval RndNbr = %DecH(C0RndNbr:30:29) * MaxNbr
C EndDo
C*
C Return


Read More...

Monday, June 15, 2009

(FAQ) How To Search String Using FNDSTRPDM

By using the FNDSTRPDM command, you can bypass the Work with Members Using
PDM display and the Work with Objects Using PDM display and proceed directly
with the search.

To search for the character string Invoice in library ATEST in file DDSSRC
from the Find String Using PDM (FNDSTRPDM) display:

1. Type FNDSTRPDM on any command line.

2. Press F4 (Prompt). The Find String Using PDM display appears.

3. Type the string of characters, surrounded by quotation marks, that you
want PDM to search for in the Find 'string' prompt. For this example,
type 'Invoice' in the Find 'string' prompt.

4. Type the library, file, and member to be searched. To search for
additional members, type + on the line beneath the member line, and
spaces for additional members appear. For this example, search for
*ALL members in the file DDSSRC in library ATEST.

5. Type the option of the function that you want to perform on the
members containing the string in the Option prompt. To display the
description, type *DSPD in this prompt.

6. Type *PROMPT in the Prompt field to show an entry display for the
command chosen in the Option prompt every time a match is found for
the Find string.

7. Press F10 (Additional parameters) to see the remaining Find string
prompts.

8. Type 1 in the From column prompt, *RCDLEN in the To column prompt, and
*IGNORE in the Kind of match prompt.

Press Page Down (Roll Up) to enter all the remaining values, and press
Enter.

When a match is found, the appropriate display for the option chosen
appears. For this example, when the string Invoice is found, the
Display Member Description display appears.
Read More...

Monday, June 8, 2009

(FAQ) How do I do FTP from/to my AS/400?

I) Manual method - Good for one shotters or for testing and learning.
A) Starting ftp from my PC to the 400
From your PC do the following:
Start
Run
cmd
From the command interface window:
ftp your400name
Enter your userid
Enter your password

For a list of commands type in ? and hit enter
By default you will be in the library system. To change to the directory
system then enter: quote site namefmt 1
To change to the library system: quote site namefmt 0
get is used to get a file from the remote to the local.
put is used to put a file to the remote from the local.
Here is a sample used to run a command on the 400:
quote rcmd crtpf mylib/myfile rcdlen(80)
quit is used to exit.
When transmitting a save file to the 400, the save file should already
exist. Also you should use the bin command to transmit the file in
binary.
B) Starting ftp from my 400 to another 400
Very similar to starting ftp from a pc to the 400.
Differences include:
While you still use ? to prompt for commands the prompting appears
different.
C) Starting ftp from my 400 to a PC.
You'll need to ensure that the PC has a ftp server loaded. Windows comes
with one, but normally you have to load that separately. There are
others available.

II) Automated, or scripting, method
A) Scripted ftp from the pc to the 400.
First create a .BAT file. Let's call ours FTPSCRIPT.BAT. In it we'll
have one line:
ftp -n -s:ftpscript.txt>ftpout.txt
The -n says not to open a system until we say so.
The -s says to used a script file and this is the name of the script file.
The >ftpout.txt will pipe the output to a text file. You can check this
file for transmission errors.
Now create another text file called FTPSCRIPT.TXT. In this place:
open myas400
user myuserid mypassword
cd mylib
quote rcmd crtpf mylib/myfile rcdlen(80)
put myfile.txt myfile
quote rcmd runpost
quit
Now when you type ftpscript it will run the ftpscript.bat file, which
will use the ftpscript.txt file. You can use notepad or some other
utility to check the ftpout.txt file for the status of the transmission.
An important thing to remember is that the command we used, runpost, will
have to set up the library list. It will not use any library list
defined by your user profile's job description.
B) Scripted ftp from 400 to another server.
First create a simple CL program. Let's call ours FTPSCRIPT. In it we'll
have these lines:
PGM PARM(&RMTSYSTEM)
DCL VAR(&RMTSYSTEM) TYPE(*CHAR) LEN(200)
OVRDBF FILE(INPUT) TOFILE(MYLIB/FTPSCRIPT)
OVRDBF FILE(OUTPUT) TOFILE(MYLIB/FTPOUTPUT)
FTP RMTSYS(&RMTSYSTEM)
DLTOVR FILE(INPUT)
DLTOVR FILE(OUTPUT)
CALL PGM(ANALYZER) /* Analyze FTP output */
ENDPGM
The file MYLIB/FTPSCRIPT should be a one field file. The record length
is pretty flexible.
The file MYLIB/FTPOUTPUT should also be a one field file. Again, the
record length is pretty flexible.
In the file MYLIB/FTPSCRIPT put the following
<- There should be one blank line first. This cancels
prompting for the user name and password. The user
statement will handle that.
user myuserid mypassword
cd mylib
quote rcmd crtpf mylib/myfile rcdlen(80)
put myfile.txt myfile
quote rcmd runpost
quit
Now when you type CALL FTPSCRIPT it will run the CL program FTPSCRIPT,
which will use the FTPSCRIPT file. You can use DSPPFM or some other
utility to check the FTPOUTPUT file for the status of the transmission.
The program analyzer would be written to check out the contents of
FTPOUTPUT and process accordingly.
An important thing to remember is that the command we used, runpost, will
have to set up the library list. It will not use any library list
defined by your user profile's job description
Read More...

Friday, June 5, 2009

(FAQ) Using OPNQRYF %WLDCRD

OPNQRYF %WLDCRD - same functionality with that of SQL LIKE keyword.
See sample code below:

Example:

PGM

OVRDBF FILE(PF001A) SHARE(*YES)

OPNQRYF FILE((PF001A)) +

QRYSLT('(PFRMKS=%WLDCRD("*Expired*"))') +

KEYFLD(*FILE)

CPYFRMQRYF FROMOPNID(PF001A) TOFILE(QTEMP/PF001A) +

MBROPT(*REPLACE) CRTFILE(*YES)

CLOF OPNID(PF001A)

DLTOVR FILE(*ALL)

Read More...

Thursday, June 4, 2009

(FAQ) Socket Programming Simple Client Program

Socket API Calls to Create Simple Client Program
(Source: Scott Klement's Socket Programming)

Topics:
1. The socket() API call
2. The connect() API call
3. The send() and recv() API calls
4. Translating from ASCII to EBCDIC
5. The close() API call
6. Simple client program


1. The socket() API call

The previous sections explained how to find out the port number for a service name, and how to get the IP address for a host name. This section will utilize that information to create a simple client program.

The socket() API is used to create a socket. You can think of a socket as being the virtual device that is used to read & write data from a network connection.

The IBM manual page that documents the socket API is at this link: http://publib.boulder.ibm.com/pubs/html/as400/v4r5/ic2924/info/apis/socket.htm

It lists the following as the prototype for the socket() API:

int socket(int address_family,
int type,
int protocol)


This tells us that the name of the procedure to call is 'socket', and that it accepts 3 parameters. Each one is an integer, passed by value. It also returns an integer. Therefore, the RPG prototype for the socket() API looks like this:

D socket PR 10I 0 ExtProc('socket')
D addr_family 10I 0 value
D type 10I 0 value
D protocol 10I 0 value


It's important to realize that the socket APIs can be used for other networking protocols besides TCP/IP. When we create a socket, we need to explain to the socket API that we wish to communicate using the IP protocol, and that we wish to use TCP on top of the IP protocol.

For address family, the manual tells us that we need to specify a value of 'AF_INET' if we wish to do network programming in the 'Internet domain'. Therefore, when we specify a value of 'AF_INET', what we're really telling the API is to 'use the IP protocol'.

Under the 'type' parameter, it allows us to give values of 'SOCK_DGRAM', 'SOCK_SEQPACKET', 'SOCK_STREAM' or 'SOCK_RAW'. The TCP protocol is the standard streaming protocol for use over IP. So, if we say 'SOCK_STREAM', we'll use the TCP protocol. As you might imagine, SOCK_DGRAM is used for the UDP protocol and SOCK_RAW is used for writing raw IP datagrams.

Finally, we specify which protocol we wish to use with our socket. Note that, again, we can specify IPPROTO_TCP for TCP, IPPROTO_UDP for UDP, etc. However, this isn't necessary! Because we already specified that we wanted a 'stream socket' over 'internet domain', it already knows that it should be using TCP. Therefore, we can specify 'IPPROTO_IP' if we want, and the API will use the default protocol for the socket type.

Now, we just have one problem: We don't know what integer values AF_INET, SOCK_STREAM and IPPPROTO_IP are! IBM is referencing named constants that they've defined in the appropriate header files for C programs, but we don't have these defined for us in RPG! But, if you do a bit of snooping into the 'System Openness Includes' library, you'll find that AF_INET is defined to be '2', SOCK_STREAM is defined to be '1', and IPPROTO_IP is defined as '0'. To make this easier for us, we'll make named constants that match these values, like so:

D AF_INET C CONST(2)
D SOCK_STREAM C CONST(1)
D IPPROTO_IP C CONST(0)


Now we can call the socket() API like so:

c eval s = socket(AF_INET:SOCK_STREAM:IPPROTO_IP)


2. The connect() API call

Once we have a socket to work with, we need to connect it to something. We do that using the connect() API, which is documented in IBM's manual at this location: http://publib.boulder.ibm.com/pubs/html/as400/v4r5/ic2924/info/apis/connec.htm

It tells us here that the prototype for the connect() API looks like this:

int connect(int socket_descriptor,
struct sockaddr *destination_address,
int address_length)


So, as you can see, the procedure is named 'connect', and it accepts 3 parameters. An integer, a pointer to a 'sockaddr' structure, and another integer. It also returns an integer. This means that the RPG prototype will look like this:

D connect PR 10I 0 ExtProc('connect')
D sock_desc 10I 0 value
D dest_addr * value
D addr_len 10I 0 value


Looking further down the manual, we see that the a 'sockaddr' structure is defined as follows:

struct sockaddr {
u_short sa_family;
char sa_data[14];
};


Remember, the purpose of this structure is to tell the API which IP address and port number to connect to. Why, then, doesn't it contain fields that we can put the address and port numbers into? Again, we have to remember that the socket APIs can work with many different network protocols. Each protocol has a completely different format for how addresses work. This 'sockaddr' structure is, therefore, a generic structure. It contains a place to put the identifying address family, along with a generic "data" field that the address can be placed in, regardless of the format of the address.

Although it's not documented on IBM's page for the connect() API, there is actually a different structure called 'sockaddr_in' which is designed especially for internet addresses. The C definition for sockaddr_in can be found in the file QSYSINC/NETINET, member IN, if you have the System Openness Includes loaded. It looks like this:

struct sockaddr_in { /* socket address (internet) */
short sin_family; /* address family (AF_INET) */
u_short sin_port; /* port number */
struct in_addr sin_addr; /* IP address */
char sin_zero[8]; /* reserved - must be 0x00's */
};


To make it easier to use these structures in RPG, I like to make them based in the same area of memory. This means that you can look at the data as a 'sockaddr', or the same data as a 'sockaddr_in' without moving the data around. Having said that, here's the definition that I use for the sockaddr & sockaddr_in structures:

D p_sockaddr S *
D sockaddr DS based(p_sockaddr)
D sa_family 5I 0
D sa_data 14A
D sockaddr_in DS based(p_sockaddr)
D sin_family 5I 0
D sin_port 5U 0
D sin_addr 10U 0
D sin_zero 8A


Before we can call the connect() API, we need to ask the operating system for some memory that we can store our sockaddr structure into. Then, we can populate the sockaddr_in structure, and actually call the connect() API. Like so:

DName+++++++++++ETDsFrom+++To/L+++IDc.Keywords+++++++++++++++++++
D p_connto S *
D addrlen S 10I 0

C* Ask the operating system for some memory to store our socket
C* address into:
C eval addrlen = %size(sockaddr)
C alloc addrlen p_connto

C* Point the socket address structure at the newly allocated
C* area of memory:
C eval p_sockaddr = p_connto

C* Populate the sockaddr_in structure
C* Note that IP is the ip address we previously looked up
C* using the inet_addr and/or gethostbyname APIs
C* and port is the port number that we looked up using the
C* getservbyname API.
C eval sin_family = AF_INET
C eval sin_addr = IP
C eval sin_port = PORT
C eval sin_zero = *ALLx'00'

C* Finally, we can connect to a server:
C if connect(s: p_connto: addrlen) < 0
C*** Connect failed, report error here
C endif


3. The send() and recv() API calls

Once we've made a connection, we'll want to use that connection to send and receive data across the network. We'll do that using the send() and recv() APIs.

IBM's manual page for the send() API can be found at this link: http://publib.boulder.ibm.com/pubs/html/as400/v4r5/ic2924/info/apis/send.htm

It tells us that the prototype for the send() API looks like this:

int send(int socket_descriptor,
char *buffer,
int buffer_length,
int flags)


Yes, the procedure is called 'send', and it accepts 4 parameters. Those parameters are an integer, a pointer, an integer and another integer. The send() API also returns an integer. Therefore, the RPG prototype for this API is:

D send PR 10I 0 ExtProc('send')
D sock_desc 10I 0 value
D buffer * value
D buffer_len 10I 0 value
D flags 10I 0 value


You may have noticed that for other 'char *' definitions, we put the 'options(*string)' keyword in our D-specs, but we didn't this time. Why? Because the send() API doesn't use a trailing null-character to determine the end of the data to send. Instead, it uses the buffer_length parameter to determine how much data to send.

That is a useful feature to us, because it means that we are able to transmit the null-character over the network connection as well as the rest of the string, if we so desire.

The flags parameter is used for 'out of band data', and for sending 'non-routed' data. You'll almost never use these flags. Why? Because 'out of band data' has never been widely adopted. Many TCP/IP stacks don't even implement it properly. In fact, for a long time, sending 'out-of-band' data to a Windows machine caused it to crash. The popular program called 'winnuke' does nothing more than send some out-of-band data to a Windows machine. The other flag, 'dont route' is really only used when writing routing applications. In all other situations, you want your packets to be routed! Therefore, it's very rare for us to specify anything but a 0 in the flags parameter.

The return value of the send() API will be the number of bytes sent, or a negative number if an error occurred.

Consequently, we typically call the send() API like this:

D miscdata S 25A
D rc S 10I 0

C eval miscdata = 'The data to send goes here'
C eval rc = send(s: %addr(miscdata): 25: 0)
c if rc < 25
C* for some reason we weren't able to send all 25 bytes!
C endif


The recv() API is used to receive data over the network. IBM has documented this API here: http://publib.boulder.ibm.com/pubs/html/as400/v4r5/ic2924/info/apis/recv.htm

recv() is very similar to send(). In fact, the prototype for recv is nearly identical to send(), the only difference is the name of the procedure that you call. The prototype looks like this:

int recv(int socket_descriptor,
char *buffer,
int buffer_length,
int flags)


And, just like send, the RPG prototype looks like this:

D recv PR 10I 0 ExtProc('recv')
D sock_desc 10I 0 value
D buffer * value
D buffer_len 10I 0 value
D flags 10I 0 value


The obvious difference between send() and recv() is what the system does with the memory pointed to by the 'buffer' parameter. When using send(), the data in the buffer is written out to the network. When using recv(), data is read from the network and is written to the buffer.

Another, less obvious, difference is how much data gets processed on each call to these APIs. By default, when you call the send() API, the API call won't return control to your program until the entire buffer has been written out to the network. By contrast, the recv() API will receive all of the data that's currently waiting for your application.

By default, recv() will always wait for at least one byte to be received. But, if there are more bytes, it will return them all, up to the length of the buffer that you've requested.

In the send() example above, 25 bytes are always written to the network unless an error has occurred. In the recv() example below, we can receive anywhere from 1 to 25 bytes of data. We have to check the return code of the recv() API to see how much we actually received.

Here's a quick example of calling recv():

D miscdata S 25A
D rc S 10I 0

C eval rc = recv(s: %addr(miscdata): 25: 0)
c if rc < 1
C* Something is wrong, we didnt receive anything.
C endif


4. Translating from ASCII to EBCDIC

Almost all network communications use the ASCII character set, but the AS/400 natively uses the EBCDIC character set. Clearly, once we're sending and receiving data over the network, we'll need to be able to translate between the two.

There are many different ways to translate between ASCII and EBCDIC. The API that we'll use to do this is called QDCXLATE, and you can find it in IBM's information center at the following link: http://publib.boulder.ibm.com/pubs/html/as400/v4r5/ic2924/info/apis/QDCXLATE.htm

There are other APIs that can be used to do these conversions. In particular, the iconv() set of APIs does really a good job, however, QDCXLATE is the easiest to use, and will work just fine for our purposes.

The QDCXLATE API takes the following parameters:
Parm# Description Usage Data Type
1 Length of data to convert Input Packed (5,0)
2 Data to convert I/O Char (*)
3 Conversion table Input Char (10)

And, since QDCXLATE is an OPM API, we actually call it as a program. Traditionally, you'd call an OPM API with the RPG 'CALL' statement, like this:

C CALL 'QDCXLATE'
C PARM 128 LENGTH 5 0
C PARM DATA 128
C PARM 'QTCPEBC' TABLE 10


However, I find it easier to code program calls using prototypes, just as I use for procedure calls. So, when I call QDCXLATE, I will use the following syntax:

D Translate PR ExtPgm('QDCXLATE')
D Length 5P 0 const
D Data 32766A options(*varsize)
D Table 10A const

C callp Translate(128: Data: 'QTCPEBC')


There are certain advantages to using the prototyped call. The first, and most obvious, is that each time we want to call the program, we can do it in one line of code. The next is that the 'const' keyword allows the compiler to automatically convert expressions or numeric variables to the data type required by the call. Finally, the prototype allows the compiler to do more thorough syntax checking when calling the procedure.

There are two tables that we will use in our examples, QTCPASC and QTCPEBC. These tables are easy to remember if we just keep in mind that the table name specifies the character set that we want to translate the data into. In other words 'QTCPEBC' is the IBM-supplied table for translating TCP to EBCDIC (from ASCII) and QTCPASC is the IBM supplied table for translating TCP data to ASCII (from EBCDIC).


5. The close() API call

This section documents the easiest, by far, socket API to call. The close() API. This API is used to disconnect a connected socket, and destroy the socket descriptor. (In other words, to use the socket again after calling close() you have to call socket() again).

Here's the IBM manual page that describes the close() API:< http://publib.boulder.ibm.com/pubs/html/as400/v4r5/ic2924/info/apis/close.htm

The manual tells us that the prototype for close() looks like this:

int close(int fildes)


So, the procedure's name is 'close' and it accepts one parameter, an integer. It also returns an integer. The RPG prototype looks like this:

D close PR 10I 0 ExtProc('close')
D sock_desc 10I 0 value


To call it, we can simply to:

C eval rc = close(s)
C if rc < 0
C*** Socket didn't close. Now what?
c endif


Or, more commonly (because there isn't much we can do if close() fails) we do something like this:

C callp close(s)


Too easy to be a UNIX-Type API, right? Well, never fear, there's one complication. The system uses the same close() API for closing sockets that it uses for closing files in the integrated file system.

This means that if you use both sockets and IFS reads/writes in your program, that you only need to define one prototype for close(). Handy, right? Unfortunately, most people put all of the definitions needed for socket APIs into one source member that they can /COPY into all of the programs that need it. Likewise, the IFS prototypes and other definitions are put into their own /COPY member.

When you try to use both /COPY members in the same program, you end up with a duplicate definition of the close() API, causing the compiler to become unhappy.

The solution is relatively simple... When we make a header file for either sockets or IFS, we use the RPG /define and /if defined directives to force it to only include the close() prototype once. So, our prototype will usually look like this:

D/if not defined(CLOSE_PROTOTYPE)
D close PR 10I 0 ExtProc('close')
D sock_desc 10I 0 value
D/define CLOSE_PROTOTYPE
D/endif


6. Our first client program

We've learned a lot of new API calls over the past few sections. It's time to put these new APIs to use with an example program.

This program is a very simple http client. It connects to a web server on the internet, and requests that a web page (or another file) on the server be sent back to it. It then receives the data that the web server returns and displays it on the screen.

It's important to understand that most data that is sent or received over the internet uses the concept of 'lines of text.' A line of text is a variable-length string of bytes, you can tell the end of a line by looking for the 'carriage-return' and 'line-feed' characters. When these characters appear in the text, it means that its time to start a new line.

In ASCII, the 'carriage-return' character (CR) is x'0D', and the 'line-feed' character is x'0A'. When translated to EBCDIC, these are x'0D' and x'25', respectively.

Therefore, the pseudocode for this client program looks like this:

1. Look up the port number for the HTTP service and store it into the variable 'port'.
2. Look up the IP address for the hostname that was passed as a parameter, and store it in the variable 'IP'.
3. Call the socket() API to create a socket that we can use for communicating with the HTTP server.
4. Create a socket address structure ('sockaddr') that will tell the connect() API which host & service to connect to.
5. Call the connect() API to connect to the HTTP server.
6. Place a two-line 'request' into a variable. The first line will contain the phrase "GET /pathname/filename HTTP/1.0" which tells the HTTP server that we wish to get a file from it, and also tells the HTTP server where that file is. The "HTTP/1.0" means that we're using version 1.0 of the HTTP specifications (more about that later) The second line of the request is blank, that's how we tell the server that we're done sending requests.
7. Translate our request to ASCII so the server will understand it.
8. Call the send() API to send our request to the server.
9. Call the recv() API to read back 1 byte of the server's reply.
10. If an error occurred (that is, the server disconnected us) then we're done receiving, jump ahead to step 13.
11. If the byte that we've read is not the 'end-of-line' character, and our receive buffer isn't full, then add the byte to the end of the receive buffer, and go to step 9.
12. Translate the receive buffer to EBCDIC so we can read it, and display the receive buffer. Then go back to step 9 to get the next line of data.
13. Close the connection.
14. Pause the screen so the user can see what we received before the program ends.

Without further ado, here's the sample program, utilizing all of the concepts from the past few sections of this tutorial:

File: SOCKTUT/QRPGLESRC, Member: CLIENTEX1

H DFTACTGRP(*NO) ACTGRP(*NEW)

D getservbyname PR * ExtProc('getservbyname')
D service_name * value options(*string)
D protocol_name * value options(*string)

D p_servent S *
D servent DS based(p_servent)
D s_name *
D s_aliases *
D s_port 10I 0
D s_proto *

D inet_addr PR 10U 0 ExtProc('inet_addr')
D address_str * value options(*string)

D INADDR_NONE C CONST(4294967295)

D inet_ntoa PR * ExtProc('inet_ntoa')
D internet_addr 10U 0 value

D p_hostent S *
D hostent DS Based(p_hostent)
D h_name *
D h_aliases *
D h_addrtype 10I 0
D h_length 10I 0
D h_addr_list *
D p_h_addr S * Based(h_addr_list)
D h_addr S 10U 0 Based(p_h_addr)

D gethostbyname PR * extproc('gethostbyname')
D host_name * value options(*string)

D socket PR 10I 0 ExtProc('socket')
D addr_family 10I 0 value
D type 10I 0 value
D protocol 10I 0 value

D AF_INET C CONST(2)
D SOCK_STREAM C CONST(1)
D IPPROTO_IP C CONST(0)

D connect PR 10I 0 ExtProc('connect')
D sock_desc 10I 0 value
D dest_addr * value
D addr_len 10I 0 value

D p_sockaddr S *
D sockaddr DS based(p_sockaddr)
D sa_family 5I 0
D sa_data 14A
D sockaddr_in DS based(p_sockaddr)
D sin_family 5I 0
D sin_port 5U 0
D sin_addr 10U 0
D sin_zero 8A

D send PR 10I 0 ExtProc('send')
D sock_desc 10I 0 value
D buffer * value
D buffer_len 10I 0 value
D flags 10I 0 value

D recv PR 10I 0 ExtProc('recv')
D sock_desc 10I 0 value
D buffer * value
D buffer_len 10I 0 value
D flags 10I 0 value

D close PR 10I 0 ExtProc('close')
D sock_desc 10I 0 value

D translate PR ExtPgm('QDCXLATE')
D length 5P 0 const
D data 32766A options(*varsize)
D table 10A const

D msg S 50A
D sock S 10I 0
D port S 5U 0
D addrlen S 10I 0
D ch S 1A
D host s 32A
D file s 32A
D IP s 10U 0
D p_Connto S *
D RC S 10I 0
D Request S 60A
D ReqLen S 10I 0
D RecBuf S 50A
D RecLen S 10I 0

C*************************************************
C* The user will supply a hostname and file
C* name as parameters to our program...
C*************************************************
c *entry plist
c parm host
c parm file

c eval *inlr = *on

C*************************************************
C* what port is the http service located on?
C*************************************************
c eval p_servent = getservbyname('http':'tcp')
c if p_servent = *NULL
c eval msg = 'Can''t find the http service!'
c dsply msg
c return
c endif

c eval port = s_port

C*************************************************
C* Get the 32-bit network IP address for the host
C* that was supplied by the user:
C*************************************************
c eval IP = inet_addr(%trim(host))
c if IP = INADDR_NONE
c eval p_hostent = gethostbyname(%trim(host))
c if p_hostent = *NULL
c eval msg = 'Unable to find that host!'
c dsply msg
c return
c endif
c eval IP = h_addr
c endif

C*************************************************
C* Create a socket
C*************************************************
c eval sock = socket(AF_INET: SOCK_STREAM:
c IPPROTO_IP)
c if sock < 0
c eval msg = 'Error calling socket()!'
c dsply msg
c return
c endif

C*************************************************
C* Create a socket address structure that
C* describes the host & port we wanted to
C* connect to
C*************************************************
c eval addrlen = %size(sockaddr)
c alloc addrlen p_connto

c eval p_sockaddr = p_connto
c eval sin_family = AF_INET
c eval sin_addr = IP
c eval sin_port = port
c eval sin_zero = *ALLx'00'

C*************************************************
C* Connect to the requested host
C*************************************************
C if connect(sock: p_connto: addrlen) < 0
c eval msg = 'unable to connect to server!'
c dsply msg
c callp close(sock)
c return
c endif

C*************************************************
C* Format a request for the file that we'd like
C* the http server to send us:
C*************************************************
c eval request = 'GET ' + %trim(file) +
c ' HTTP/1.0' + x'0D25' + x'0D25'
c eval reqlen = %len(%trim(request))
c callp Translate(reqlen: request: 'QTCPASC')

C*************************************************
c* Send the request to the http server
C*************************************************
c eval rc = send(sock: %addr(request): reqlen:0)
c if rc < reqlen
c eval Msg = 'Unable to send entire request!'
c dsply msg
c callp close(sock)
c return
c endif

C*************************************************
C* Get back the server's response
C*************************************************
c dou rc < 1
C exsr DsplyLine
c enddo

C*************************************************
C* We're done, so close the socket.
C* do a dsply with input to pause the display
C* and then end the program
C*************************************************
c callp close(sock)
c dsply pause 1
c return

C*===============================================================
C* This subroutine receives one line of text from a server and
C* displays it on the screen using the DSPLY op-code
C*===============================================================
CSR DsplyLine begsr
C*------------------------
C*************************************************
C* Receive one line of text from the HTTP server.
C* note that "lines of text" vary in length,
C* but always end with the ASCII values for CR
C* and LF. CR = x'0D' and LF = x'0A'
C*
C* The easiest way for us to work with this data
C* is to receive it one byte at a time until we
C* get the LF character. Each time we receive
C* a byte, we add it to our receive buffer.
C*************************************************
c eval reclen = 0
c eval recbuf = *blanks

c dou reclen = 50 or ch = x'0A'
c eval rc = recv(sock: %addr(ch): 1: 0)
c if rc < 1
c leave
c endif
c if ch<>x'0D' and ch<>x'0A'
c eval reclen = reclen + 1
c eval %subst(recbuf:reclen:1) = ch
c endif
c enddo

C*************************************************
C* translate the line of text into EBCDIC
C* (to make it readable) and display it
C*************************************************
c if reclen > 0
c callp Translate(reclen: recbuf: 'QTCPEBC')
c endif
c recbuf dsply
C*------------------------
Csr endsr


Compile this program with: CRTBNDRPG PGM(CLIENTEX1) SRCFILE(xxx/QRPGLESRC) DBGVIEW(*LIST)

Run the program by typing: CALL CLIENTEX1 PARM('ods.ods.net' '/index.html')

(You should be able to use this to retrieve just about any web page)


Read More...

(FAQ) How to Automate AS400 File to Excel

When attempting to automate the downloads to Excel, I ran into a problem with using .csv because many of the fields contain imbedded commas. I wanted the user to be able to just click on the file and have all the data formatted. The answer is to copy to a tab-delimited import file first, then copy to a stream file making sure to use the .xls file extension.


CPYTOIMPF FROMFILE(&LIB/&FILE) TOFILE(QTEMP/TEMPXLS) +
MBROPT(*ADD) RCDDLM(X'0D') DTAFMT(*DLM) +
STRDLM(*NONE) FLDDLM(X'05') DATFMT(*USA) +
TIMFMT(*USA)

Hex '05' translates to a tab. And then all I had to do was copy to a file with an .xls extension:

CPYTOSTMF +
FROMMBR('/QSYS.LIB/QTEMP.LIB/TEMPXLS.FILE/TEMPXLS.MBR') +
TOSTMF(&STMF) +
STMFOPT(*REPLACE) CVTDTA(*AUTO) +
DBFCCSID(37) STMFCODPAG(850) ENDLINFMT(*LF)

Where &STMF = 'q:homedownloadmyfile.xls'
Read More...

Wednesday, June 3, 2009

(FAQ) How To Add Colors To Source Code

Here's a tip many people may not be aware of. When we are coding the programs we put a lot of comments to explain logic or functionality, instead of putting these comments in green color, generally we will prefer to put them in different colors. For doing this some people will use tools some will do programmatically. Instead of doing so we can add some colors to our program by using simple Client Access keyboard mapping.

Let us see how we can do this..

- Click on the "MAP" button
- Click on any key, for example I want to map my ALT + R for red color
- So click on R and type APL 28 in the box provided against Alt and save the settings.
- Now go to the source line, which you want to make in RED. Put your cursor just before the first word of the line and press ALT + R and enter, the source line will be displayed red in color.

For different colors follow this table:

APL 20 - Green
APL 21 - Green RI
APL 22 - White
APL 23 - White RI
APL 24 - Green UL
APL 25 - Green RI UL
APL 26 - White UL
APL 27 - ND
APL 28 - Red
APL 29 - Red RI
APL 30 - Blue
APL 31 - Blue RI
APL 32 - Yellow
APL 33 - Yellow RI
APL 34 - Blue UL
APL 35 - Blue UL RI

By using this technique we can change the colors for member text too... take option 13 and F4 for member you want to change the color first, then put your cursor just before the first letter in the text and press corresponding color key and press enter. Now you can see your member text will be displayed in different color.
Read More...

Tuesday, June 2, 2009

(FAQ) How to send email from iSeries

I. Simple message

You will need to know the internet address(es) of the recipient(s). Use the SNDDST command to send the message.

Parameters:
1. TYPE: Use *LMSG (Long Message, 5000 characters maximum)
2. TOINTNET: The internet address of the recipient. Use the “+” for additional addresses.


Recipient Type is
*PRI for the TO addressee
*CC for CC: addressees
*BCC for BCC: addressees.

3. DSTD: This field contains the subject line of your message
4. LONGMSG: The text of your message.

Example:

SNDDST TYPE(*LMSG) TOINTNET((someone@SomeDomain.com) (someoneelse@hotmail.com *PRI))
DSTD(Here is your file!!) LONGMSG(Your test worked great!. Please call me if you have questions/comments. )



II. Send a file as an attachment

The file must exist in the QDLS directory structure. You need to know the name of the file.
You will need to know the internet address(es) of the recipient(s). Use the SNDDST command to send the message.

Parameters:
1. TYPE: Use *DOC (The document specified on the Document prompt (DOC parameter)
or the Document identifier prompt (DOCID parameter) is sent. The user must
have authority for the document before it can be sent).
2. TOINTNET: The internet address of the recipient. Use the “+” for additional addresses.

Recipient Type is
*PRI for the TO addressee
*CC for CC: addressees
*BCC for BCC: addressees.

3. DSTD: This field contains the subject line of your message
4. MSG: The text of your message (256 characters maximum).
5. SNDFMT: *NOCHG
6. DOC: The name of the document in 8.3 format, e.g., TESTMAIL.TXT, or whatever name exists in the QDLS folder.
7. FLR: The name of the folder in the QDLS directory, e.g., PCUPLOAD

Example:

SNDDST TYPE(*DOC) TOINTNET((someone@SomeDomain.com *PRI) (someoneelse@hotmail.com *PRI))
DSTD(Here is your file!!) MSG(File TESTMAIL.txt attached) SNDFMT(*NOCHG)
DOC(TESTMAIL.TXT) FLR(PCUPLOAD)

First of all, you have to understand that the AS/400 supported E-mail long before the Internet came around, and SMTP became popular. It used an e-mail system called SNADS that IBM developed specifically for their business machines.

That's why commands like SNDDST seem so complicated. They weren't designed for today's Internet e-mail, they were designed for SNADS and later adapted for SMTP.

Before explaining how to set up e-mail, I'm making the following assumptions:

* Your system is already configured for TCP/IP.
* You already have DNS lookups working from your iSeries to Internet domains as well as local domains.
* You have an Internet Service Provider (ISP) who has given you an outgoing mail server to relay your e-mail. Or you have an internal SMTP server that your company uses for e-mail.


The first thing you'll want to do is configure your iSeries SMTP server. This is used when sending mail as well as receiving it. Type the following command:

Code:

CHGSMTPA AUTOSTART(*YES) MAILROUTER('your.isp.smtp.server') FIREWALL(*YES) ALWRLY(*BOTH)

* AUTOSTART(*YES) tells the system you want this server to start automatically when TCP/IP is started.
* the MAILROUTER keyword tells where the iSeries should send outgoing e-mail to. This'll be the internal SMTP server that your company uses for mail, or the one that your ISP provided you.
* The FIREWALL(*YES) keyword tells the iSeries to always send mail though the MAILROUTER, never try to deliver it directly.
* ALWRLY(*BOTH) is used to configure who is allowed to send mail though this server. Do not set this to *ALL or spammers will be able to use your machine to send spam without your consent. With *BOTH, you'll have to use the ADDSMTPLE command to control which computers can use your SMTP server (more later).


You'll also want to make sure that e-mail messages are not split up, even if they're big. This has always seemed strange to me, but it's controlled by the CHGPOPA (Change POP3 Attributes) command.

Code:

CHGPOPA MSGSPLIT(*NOMAX)

As I mentioned above, you'll need to configure which IP addresses are allowed to send mail through your iSeries system. In my shop, all the computers who can send mail have IP addresses that begin with 192.168.5, so I run the following command:

Code:

ADDSMTPLE TYPE(*ACCEPT) INTNETADR('192.168.5.0') SUBNETMASK('255.255.255.0')

You can run this command more than once if you need to add more than one subnet.

The next thing you'll need to do is set up a gateway that allows SNADS tools like SNDDST to send Internet e-mail. To do that, type the following commands:

Code:

ADDDIRE USRID(INTERNET GATEWAY) +
USRD(‘Internet SMTP gateway’) SYSNAME(INTERNET) +
PREFADR(NETUSRID *IBM ATCONTXT)

CHGDSTA SMTPRTE(INTERNET GATEWAY)

After running this command, the SNDDST command will use the gateway so that e-mail will be sent to the local SMTP server.

Now that SMTP is configured on your system, so go ahead and start the SMTP server

Code:

STRTCPSVR SERVER(*SMTP)

(If it was already running, use ENDTCPSVR to end it. Then use WRKACTJOB SBS(QSYSWRK) to make sure that all of the QTSMTPxxxx jobs end, then use STRTCPSVR to start it up again.)

Each user who needs to send e-mail using SNDDST will need to have a directory entry and SMTP e-mail address configured on the iSeries.

Code:

ADDDIRE USRID(KLEMSCOT S10262BA) USRD('Scott Klement')

Code:

WRKNAMSMTP TBLTYPE(*SYSTEM)

1. Use option 1=Add
2. Type the same name/address that you used on the ADDDIRE command
3. Enter the SMTP e-mail address for this user.


Once all of that is done, it should be easy to send off a test e-mail with SNDDST. For example:

Code:

SNDDST TYPE(*LMSG) +
TOINTNET(('unclebob@example.com')) +
DSTD('Put E-mail Subject Here') +
LONGMSG('Hi Bob. This is an E-mail message sent from my iSeries system.')

You should also be able to configure tools like Outlook, Firebird, Eudora, etc to send mail using your iSeries SMTP server. For user-written e-mail, that's nicer than using SNDDST. Though, SNDDST is still nice for automatic messages generated by programs.

There are also lots of free e-mail tools on the Web that can use this SMTP server. They provide many more capabilities than SNDDST does. But, alas, this message is getting too long, so I'll stop there.
Read More...

Monday, June 1, 2009

(FAQ) How Can You Check Record Existence Without Using CHAIN or READ I/O Operation?

This is possible using File Information Data Structure (INFDS).
Here's the code:

Position Entry
6 F
7-52 Blank (if the information is specified on a separate continuation line)
53 K (indicates a continuation statement)
54-59 INFDS (identifies this data structure as a file information data structure)
60-65 Name of the file information data structure.

Also make the following entries on an input specification line:
Position Entry
6 I
7-12 Name of the file information data structure
13-18 Blank
19-20 DS
21-74 Blank.

For each subfield of the information data structure, make the following entries on an
input specification line:
Position Entry
6 I
7-43 Blank
44-51 *RECORD
52 Blank
53-58 Name of the subfield of the information data structure
59-74 Blank.

Read More...