Suppose you have a customer file that you want to make available to all NetWare users, and that file contains fields that need conversion from packed decimal format. Example: Copying an iSeries™ database file shows you how to copy and convert that file with a series of integrated file system commands. You can also copy and convert the necessary field by using an ILE C for iSeries program. Before you use integrated file system APIs on the QNetWare file system, you should be aware of some considerations.
This sample program is not restricted to the QNetWare file system. You can use it to copy an iSeries database file into other file systems that the integrated file system supports. For example, you could change the hard-coded name of the stream file path to be the current directory: ./CUST.DAT. Before you run the program, use the CHGCURDIR command to change to the directory in which you want to create the stream file. However, you cannot copy this file into the QSYS.LIB file system because CUST.DAT is not a valid name for the QSYS.LIB file system.
The file in this example is in library DATALIB and has the name CUSTCDT. You can refer to these example displays:
The following ILE C for iSeries program, NWCUST, uses the external description of CUSTCDT. To convert the data from EBCDIC to ASCII, it uses the system APIs from the service program QTQICONV. This service program provides APIs to convert a buffer of characters from one coded character set identifier (CCSID) into another CCSID. To create the stream file CUST.DAT, it uses the open(), write() and close() integrated file system APIs.
To create the ILE C for iSeries module and bind it to the QTQICONV service program to create the NWCUST program, you would use these commands:
CRTCMOD MODULE(NWCUST) SRCFILE(QCSRC) OUTPUT(*PRINT) OPTION(*SHOWUSR) CRTPGM PGM(NWCUST) BNDSRVPGM(QTQICONV)
This function opens the stream file for write only (O_WRONLY flag). If the file does not exist, it is created (O_CREAT flag). If it exists, it is truncated to 0 bytes length (O_TRUNC flag). This ensures that the stream file is replaced every time the program runs.
S_IRWXU sets file authority bits.
The open() API requires authority bits to be set when the O_CREAT flag is set.
S_IRWU is used to let you read and write to the file. NetWare file mode support has information about which attributes NetWare sets for owner permission.
The sprintf() function converts all packed decimal data fields from the database file into characters and formats them correctly with the format-string, for example: "D(6.2)".
Function iconv() converts the stream file buffer to ASCII code page 850. Use the code page of your NetWare server. To determine the code page of the server, use the DSPMFSINF command. For example, to obtain the code page of directory SYSTEM in volume SYS of server NTWSERV1, enter:
DSPMFSINF OBJ('/QNetWare/NTWSERV1.SVR/SYS/SYSTEM')
Here is the source listing for the example program:
* * * * * S O U R C E * * * * * Line STMT SEQNBR INCNO *...+.....1.....+.....2.....+.....3.....+.....4.....+.....5.....+.....6.....+.....7.....+.....8.....+.....9.....+..... 1 |/* This program copies the AS/400 database file CUSTCDT to a */ | 1 2 |/* stream file in the QNetWare file system. It uses Integrated File System APIs to */ | 2 3 |/* create and write the file. It converts the data from CCSID 37 */ | 3 4 |/* to ASCII codepage 850 using the QTQICONV system API. */ | 4 5 | | 5 6 |#include <stdio.h> | 6 7 |#include <recio.h> | 7 8 |#include <stdlib.h> | 8 9 |#include <string.h> | 9 10 |#include <decimal.h> | 10 11 |#include <fcntl.h> | 11 12 |#include <unistd.h> | 12 13 |#include <qtqiconv.h> | 13 14 | | 14 15 |/* Include the external definition of the database file CUSTCDT. */ | 15 16 | | 16 17 |#pragma mapinc("custmf","CUSTCDT(cusrec)","input","_P") | 17 18 |#include "custmf" | 18 1 |/* ------------------------------------------------------------------------*/ | 19 19 2 |/* PHYSICAL FILE: DATALIB/CUSTCDT */ | 20 19 3 |/* FILE CREATION DATE: 94/08/22 */ | 21 19 4 |/* RECORD FORMAT: CUSREC */ | 22 19 5 |/* FORMAT LEVEL IDENTIFIER: 39C301DBCB10B */ | 23 19 6 |/* ------------------------------------------------------------------------*/ | 24 19 7 |typedef _Packed struct { | 25 19 8 | decimal( 6, 0) CUSNUM; /* Customer number */ | 26 19 9 | /* PACKED SPECIFIED IN DDS */ | 27 19 10 | char LSTNAM[8]; /* Last name */ | 28 19 11 | char INIT[3]; /* First/middle initial */ | 29 19 12 | char STREET[13]; /* Street address */ | 30 19 13 | char CITY[6]; /* City */ | 31 19 14 | char STATE[2]; /* State abbreviation */ | 32 19 15 | decimal( 5, 0) ZIPCOD; /* Zip code */ | 33 19 16 | /* PACKED SPECIFIED IN DDS */ | 34 19 17 | decimal( 4, 0) CDTLMT; /* Credit limit */ | 35 19 18 | /* PACKED SPECIFIED IN DDS */ | 36 19 19 | decimal( 1, 0) CHGCOD; /* Charge code */ | 37 19 20 | /* PACKED SPECIFIED IN DDS */ | 38 19 21 | decimal( 6, 2) BALDUE; /* Balance due */ | 39 19 22 | /* PACKED SPECIFIED IN DDS */ | 40 19 23 | decimal( 6, 2) CDTDUE; /* Credit due */ | 41 19 24 | /* PACKED SPECIFIED IN DDS */ | 42 19 25 |}DATALIB_CUSTCDT_CUSREC_i_t; | 43 19 26 | | 44 19 19 |#define _RCDLEN 100 | 45 20 | | 46 21 |int main(void) | 47 22 |{ | 48 23 | /* Declare rcd, dta of data structure type DATALIB_CUSTCDT_CUSREC_i_t. */ | 49 24 | /* The data structure type was defined from the DDS specified. */ | 50 25 | | 51 26 | DATALIB_CUSTCDT_CUSREC_i_t rcd, *dta = &rcd; | 52 27 | _RFILE *in; | 53 28 | _RIOFB_T *fb; | 54 29 | char dbrcd[_RCDLEN]; | 55 30 | char stmrcd[_RCDLEN]; | 56 31 | char *dbfile = "CUSTCDT"; | 57 32 | char *stmfile = "QNetWare/SERVER.SVR/VOLUME/DATA/CUST.DAT"; | 58 33 | int fildes, stmlen; | 59 34 | | 60
35 | /* Declare necessary variables for code conversion CCSID 37 to 850. */ | 61 36 | | 62 37 | char fromcode[32] = "IBMCCSID000370000000"; | 63 38 | char tocode[32] = "IBMCCSID00850"; | 64 39 | char *ibuf, *obuf; | 65 40 | iconv_t cd; | 66 41 | size_t ilen, olen; | 67 42 | | 68 43 | /* Open and create the stream file using the open() Integrated File System API. */ | 69 44 | | 70 45 1 | fildes = open( stmfile, O_WRONLY|O_CREAT|O_TRUNC, S_IRWXU ); 1 | 71 46 2 | if (fildes == -1) | 72 47 | { | 73 48 3 | printf ( "Open to stream file %s failed\n", stmfile ); | 74 49 4 | exit ( 1 ); | 75 50 | } | 76 51 | else | 77 52 | { | 78 53 | /* Open the database file for processing in arrival sequence. */ | 79 54 | | 80 55 5 | if (( in = _Ropen( dbfile, "rr, arrseq=Y" )) == NULL ) | 81 56 | { | 82 57 6 | printf ( "Open to database file %s failed\n", dbfile ); | 83 58 7 | exit ( 1 ); | 84 59 | } | 85 60 | else | 86 61 | { | 87 62 | /* Open a code conversion descriptor for CCSID 37 to 850 */ | 88 63 | /* conversion. */ | 89 64 | | 90 65 8 | cd = iconv_open( tocode, fromcode ); | 91 66 | | 92 67 9 | fb = _Rreadn( in, dta, in->buf_length, __DFT ); | 93 68 10 | while ( fb->num_bytes != EOF ) | 94 69 | { | 95 70 | /* Create the buffer for the stream file. Each field will be */ | 96 71 | /* separated by a comma. */ | 97 72 | | 98 73 11 | ilen = sprintf( dbrcd, "%D(6,0),", rcd.CUSNUM ); 2 | 99 74 12 | ilen += sprintf( dbrcd+ilen, "%8.8s,", rcd.LSTNAM ); | 100 75 13 | ilen += sprintf( dbrcd+ilen, "%3.3s,", rcd.INIT ); | 101 76 14 | ilen += sprintf( dbrcd+ilen, "%13.13s,", rcd.STREET ); | 102 77 15 | ilen += sprintf( dbrcd+ilen, "%6.6s,", rcd.CITY ); | 103 78 16 | ilen += sprintf( dbrcd+ilen, "%2.2s,", rcd.STATE ); | 104 79 17 | ilen += sprintf( dbrcd+ilen, "%D(5,0),", rcd.ZIPCOD ); | 105 80 18 | ilen += sprintf( dbrcd+ilen, "%D(4,0),", rcd.CDTLMT ); | 106 81 19 | ilen += sprintf( dbrcd+ilen, "%D(1,0),", rcd.CHGCOD ); | 107 82 20 | ilen += sprintf( dbrcd+ilen, "%D(6,2),", rcd.BALDUE ); | 108 83 21 | ilen += sprintf( dbrcd+ilen, "%D(6,2)", rcd.CDTDUE ); | 109 84 | | 110 85 | /* Set pointers for CCSID 37 to CCSID 850 conversion. */ | 111 86 | /* Use the iconv() function to convert the data. */ | 112 87 | | 113 88 22 | stmlen = olen = ilen; | 114 89 23 | ibuf = &dbrcd[0]; | 115 90 24 | obuf = &stmrcd[0]; | 116 91 25 | iconv( cd, &ibuf, &ilen, &obuf, &olen ); 3 | 117 92 | | 118 93 | /* Add ASCII carriage return and line feed. */ | 119 94 | | 120 95 26 | stmlen += sprintf( stmrcd+stmlen, "\15\12" ); 4 | 121 96 | | 122
97 | /* Write the buffer to the stream file using the Integrated File System write() API.*/ | 123
98 | | 124
99 27 | if (( write( fildes, stmrcd, stmlen )) == -1 ) 5 | 125
100 | { | 126
101 28 | printf ( "Write to stream file %s failed\n", stmfile ); | 127
102 29 | exit ( 1 ); | 128
103 30 | }; | 129
104 31 | fb = _Rreadn( in, dta, in->buf_length, __DFT ); | 130
105 32 | }; | 131
106 33 | iconv_close( cd ); /* Close data conversion descriptor. */ | 132
107 34 | _Rclose( in ); /* Close the database file. */ | 133
108 35 | }; | 134
109 36 | close( fildes ); /* Close the streamfile. */ | 135
110 37 | }; | 136
111 |} | 137
* * * * * E N D O F S O U R C E * * * * *
Figure 3 shows the contents of the stream file CUST.DAT after the NWCUST program has run and created it.
[C:\]type \\J:cust.dat 938472,Henning ,G K,4859 Elm Ave ,Dallas,TX,75217,5000,3,37.00,0.00 839283,Jones ,B D,21B NW 135 St,Clay ,NY,13041,400,1,100.00,0.00 392859,Vine ,S S,PO Box 79 ,Broton,VT,5046,700,1,439.00,0.00 938485,Johnson ,J A,3 Alpine Way ,Helen ,GA,30545,9999,2,3987.50,33.50 397267,Tyron ,W E,13 Myrtle Dr ,Hector,NY,14841,1000,1,0.00,0.00 389572,Stevens ,K L,208 Snow Pass,Denver,CO,80226,400,1,58.75,1.50 846283,Alison ,J S,787 Lake Dr ,Isle ,MN,56342,5000,3,10.00,0.00 475938,Doe ,J W,59 Archer Rd ,Sutter,CA,95685,700,2,250.00,100.00 693829,Thomas ,A N,3 Dove Circle,Casper,WY,82609,9999,2,0.00,0.00 593029,Williams,E D,485 SE 2 Ave ,Dallas,TX,75218,200,1,25.00,0.00 192837,Lee ,F L,5963 Oak St ,Hector,NY,14841,700,2,489.50,0.50 583990,Abraham ,M T,392 Mill St ,Isle ,MN,56342,9999,3,500.00,0.00 [C:\]
In this example, the J: drive was mapped to SERVER:VOLUME\DATA using the NetWare client.
Because the fields in CUST.DAT are separated by a comma, it is very easy to incorporate the file into a spreadsheet application.