Carlos, hace algun tiempo Alex publico en recursos un documento en donde explica como crear archivos de PC desde RPG IV:
http://www.recursos-as400.com/como67.shtml Igualmente en su momento, Alex me envio un articulo que me fue de gran utilidad para comprender algunas cosas, te lo copio a continuacion: _____________________________________________________________________________ RPG IV and the IFS by Ted Holt You don't have to get a C compiler to write programs that access the Integrated File System (IFS) and the Novell Network File System (NFS). RPG IV programs can bind to the same APIs that C uses. You can read and write IFS files, and it's probably easier than you think! I attended some very interesting sessions at COMMON in San Antonio this past September, among them Ray Bills' session, "Access the Integrated File System from RPG." For some time, I had wanted to write RPG programs that would directly read from and write to the IFS, but I had assumed it wasn't yet possible--or at least not feasible. Boy, was I wrong! Not only is it possible, it's (relatively) easy! Ray, who works for IBM Rochester, had thought the same, until Massimo Marasco of IBM Italy contacted him for some debugging help. Ray asked Massimo to send him a copy of his C source, and Massimo replied that his program was written in RPG, not C. A new world opened to Ray, and he was kind enough to share it with us at COMMON. So, I stand on the shoulders of giants. Having attempted to give credit where it is due, I move to the subject at hand. What Is the IFS? Most computers have one way of storing programs and data. On PCs and UNIX systems, for example, we store everything in files, which we organize into directories. On the AS/400, we store most types of objects (e.g., database files, programs, data areas, and data queues) in libraries. We store certain other types, like documents and PC files, in a hierarchical filing system of folders. These typical AS/400 filing systems work well for the data processing and office functions AS/400 users have traditionally needed, but they don't work well for all the types of data modern computers are being asked to store. IBM created the Integrated File System (IFS) as a way of allowing more filing systems to exist on the AS/400 so that we would be able to put any type of object into the AS/400's disk storage. To learn more about the IFS, see "V3R1 Announcement Follow-up: The Integrated File System," MC, July 1994. So if you need a PC-type filing system on the AS/400, you've got it. If you need a secure server for your LAN, it's there. If you need to store graphic images (TIFF, BMP, etc.), multimedia presentations, and such on your AS/400, the IFS is ready for the task. Many AS/400 shops have not taken advantage of the IFS. For the most part, they don't need it. Database is still the lifeblood of businesses, and databases are still stored in libraries. Another reason people haven't used the IFS is that it is not directly addressable from RPG programs. That is, you can't declare an IFS file in an F-spec. Instead, you have to use something like C or Perl. However, with a client like the Windows 95 Explorer, you can access an IFS file. One of the beauties of the AS/400, and especially ILE, is that if one language can do something, all of them can (to a degree, at least.) Since C can access the IFS, so can RPG. The APIs C uses are described in the OS/400 UNIX-type APIs manual (see the references at the end of this article). Now, I'm going to give you two example RPG programs that access the IFS: one to read from the IFS, the other to write to it. I have tried to keep these very short and simple so you can concentrate on understanding how this works. You can find more examples, in C, COBOL, and RPG, in Appendix B of the System API Programming manual. Reading from the IFS Before you can read from a file, you have to open it. The UNIX-type API that opens files, believe it or not, is named open(). To read data from an IFS file, use the read() API. When you're finished with the file, close() it. You can call these APIs using the callp (Call a Prototyped Procedure or Program) op code, but since they return a value, you really should use them the way you use built-in functions, like %SUBST and %TRIM. That is, you'll use them in Eval statements. In any event, you'll have to prototype them in your RPG programs. IBM supplies header files that include the C prototypes, but it hasn't yet released copybooks with RPG prototypes. You can look up the function definitions in the API manual or look at the appropriate header file in the QSYSINC library. Or you can just use the IFSPROTOS member in Figure 1. I got some of this from Ray Bills, and I added other things myself as I needed to. It's not complete, so you may have to add more to it. The UNIX-type APIs manual will tell you which header file contains a function's prototype and show you how the function is defined. You can look at the header file in library QSYSINC for more information. Be aware that any constant that begins with a zero (but not a zero followed by an X) is an octal number, not a decimal number. Once you've got this copybook on your system, you'll just need to /COPY it into your programs that use the IFS, as I have done in the programs in Figures 2 and 3. Now, look at the RPG program in Figure 2. It opens an ASCII text file of variable length records and prints them to QSYSPRT. I created the ASCII file on my PC using a spreadsheet, saved it in comma-delimited format, and FTPed it to my play directory on MC's RISC machine. The AS/400 created the file in the IFS with code page 819, one of the ASCII code pages. The first D-spec copies the prototypes and constants the APIs need. The rest of the D-specs define constants and working variables. You might want to add some of these constants to the IFSPROTOS member so you wouldn't have to define them in the programs that need them. Now, look at the C-specs. To open the file, I call the open() API. This API needs to know what file I want to open and how I want to open it. To tell it the name of the file to open, I have to pass it a pointer to (the address of) a null-terminated string. The first Eval strips leading and trailing blanks from the file name and appends a null. The second Eval sets the open flag to the attributes read-only and text-data. (These attributes are defined in IFSPROTOS.) "Read-only" means I'm opening it for input, of course. "Text-only" means that the file is a text file, not a binary file. This causes the system to translate the data to the CCSID of the job. In this case, the ASCII data gets translated to EBCDIC. The result of the open(), the value it returns, is assigned to the variable fp. If the open() fails, it returns a negative number, and I can take appropriate action. If it succeeds, it gives me a file pointer number that I refer to when I read the file. Once the file is open, I can read it. Variable-length records are separated by the carriage return-line feed combination, which comes into this program as X'0D25'. Since I don't know how long each record will be, I read the file a character at a time, building a record until I hit a carriage return. I ignore line feed characters. Look at the GetChar subroutine. It first checks to see if all characters previously read have been processed. (This will prove true the first time through, as zero characters have been processed, and zero characters were last read.) If so, it blanks the input variable, calls the read() API to get another block of 256 characters, and resets the index into the input variable. The read() API requires three pieces of information: the file pointer returned by open() so it knows which file to read, the address of the variable into which to store the retrieved data, and the number of bytes of data it should read. It will return the number of bytes read. A positive number means it retrieved data. Zero means there was no more data to read. A negative number indicates that an error occurred on the read. I told it to get 256 bytes of data at a time. It will get as many as it can. If there are 259 bytes of data in the file, it will get 256 the first time, 3 the second time, and zero the third time. The GetChar subroutine continues by extracting the next character from the buffer into CurChar (current character) so the main routine can process it. When the main routine finds a carriage return, it prints a line. It ignores line feed characters. It copies anything else into the next position of the output buffer. Eventually, this program runs out of data, and I'm ready to close the file. The close() function also returns a value, so I should use Eval to assign the function result to a return code. However, I wanted to show you that you can use a callp op code to run these functions if you want to. The RPG compiler generates an RNF5409 error ("The prototyped call returns a value which is lost when CALLP is used"). In other words, you can ignore the return value if you want to. Take a look at the compilation instructions at the top of the program. You have to include the QC2LE binding directory so the system can find these APIs during program creation. This program is not robust. For example, it assumes that no input record will have more than 80 characters of data in it. My first inclination was to reprint the CSV001RG program I published last February, making it read from the IFS. (See "Extracting Problem Data from PC Files," MC, February 1997.) That would have been more realistic, but much too long for publication. So I opted for this short example instead. I hope you can integrate the portions of this code that you need into your applications. Writing to the IFS The program in Figure 3 reads data from a physical file and writes it, in comma-delimited format, to an IFS file. Let's see how it works. The D-specs are more or less like those in Figure 2. Let's start with the C-specs. The first step is to create the file in the IFS. The creat() API creates a new file, but I'm going to use open() instead, because, as Ray Bills showed me, open() allows me to specify a code page, but creat() does not. Since FTP creates the files I transfer to the AS/400 with code page 819, I decided to use code page 819 for this example as well. Keep in mind that code page 819 is an ASCII code page, not EBCDIC. The first Eval sets the attributes for the open. In this case, the attributes say that I am creating a file, that I am specifying a code page parameter on the open, and that the file is to have read-write attributes. The second Eval sets the proper value for the authority to the file, which is expressed in the mode parameter. The value S_IRWXU says the owner is allowed *RWX (read-write-execute) authorities, and S_IROTH means *PUBLIC is allowed only to read it. The third Eval builds a null-terminated string of the file name. The fourth Eval executes the open() function to create the file. The open function has four arguments here: the address of (a pointer to) the file name, the attributes in the oflag, the authorities, and the code page to be used. It returns a value to the fp variable. If the open fails, this value will be negative, and I can do some appropriate error processing. If all went well, the file is open, but not for translation between code pages. To make the system translate data from one code page to another, I have to close the file and open it again. Before executing the second open (), I set the attributes to write-only and tell it to translate from the job's code page (EBCDIC) to the IFS file's code page (ASCII). To summarize, the file needs to be opened twice-the first time to create the file, the second time to force data translation as the program writes to the files. Now that the IFS file is open, I need to load it with data. I start a loop that reads database file StoreData and writes each record, in comma-delimited format, to the IFS file. The first Eval inside the loop builds the comma-delimited variable, and the second Eval writes it. (Perhaps you're wondering what happens if, in building this variable, you concatenate an alphanumeric field that contains an embedded comma. Embedded commas are OK inside quoted strings. Embedded quotes, however, should be doubled-but I don't do that in this example.) This is generally not a robust way to build a comma-delimited file, because it doesn't double quotation marks inside character values. It is adequate for this illustration, because the input data does not contain quotation marks. Notice the write() function. The arguments are the file pointer returned by open(), the address of the data being written, and the number of bytes to be written. The function returns the number of bytes it writes into variable rc. I don't check for an error here, but I could easily do so by making sure rc has a positive value after the write() executed. Ideally, rc should return the same value as the write function's third parameter. When I'm finished copying the data, I close the file. Notice I used Eval, not callp, this time. Again, I don't check for an error, but if the close doesn't work correctly, rc will be negative. Potential for Service I've been discussing the IFS, but these APIs work with the NFS as well. I haven't tried this yet, but I'm told that you can use these APIs to read and write to other devices on a network. For example, a user might use a spreadsheet or database to build a CSV file every day on his PC's hard drive. At night, the AS/400 could read that file directly from his PC, process it, and create a new file on his hard drive that he could open and begin to work with the next morning. All of this would take place without file transfer. It seems to me, then, that these APIs give us more or better ways to serve the people who use our AS/400s. I've enjoyed learning how to use these APIs, and plan to use them soon in production work for my consulting firm. I hope you find a lot of good uses for these APIs too. Ted Holt is a technical editor for Midrange Computing and a consultant living in Corinth, Mississippi. You can contact him by email at [EMAIL PROTECTED] REFERENCES AND RELATED MATERIALS OS/400 UNIX-type APIs (SC41-4875, CD-ROM QBKAM401) System API Programming (SC41-4800, CD-ROM QBKAVC00) FIGURE 1: This RPG copybook contains some of the prototypes and data declarations the UNIX-type APIs need. .BOF---- * Prototypes and definitions for working with the IFS * * Warning: this file may be incomplete or contain errors! * * open -- open an IFS file * D open pr 10i 0 ExtProc('open') D filename * value D openflags 10i 0 value D mode 10u 0 value options(*nopass) D codepage 10u 0 value options(*nopass) * = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = * read -- read an IFS file * D read pr 10i 0 ExtProc('read') D filehandle 10i 0 value D datareceived * value D nbytes 10u 0 value * = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = * write -- write to an IFS file * D write pr 10i 0 ExtProc('write') D filehandle 10i 0 value D datatowrite * value D nbytes 10u 0 value * = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = * close- close an IFS file * D close pr 10i 0 ExtProc('close') D filehandle 10i 0 value * = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = * values for oflag parameter, used by open() * from QSYSINC/H, member FCNTL D O_APPEND s 10i 0 inz(256) D O_CODEPAGE s 10i 0 inz(8388608) D O_CREAT s 10i 0 inz(8) D O_EXCL s 10i 0 inz(16) D O_RDONLY s 10i 0 inz(1) D O_RDWR s 10i 0 inz(4) D O_TEXTDATA s 10i 0 inz(16777216) D O_TRUNC s 10i 0 inz(64) D O_WRONLY s 10i 0 inz(2) * = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = * user authorities for omode parameter, used by open() * from QSYSINC/SYS, member STAT D S_IRUSR s 10i 0 inz(256) D S_IWUSR s 10i 0 inz(128) D S_IXUSR s 10i 0 inz( 64) D S_IRWXU s 10i 0 inz(448) * group authorities D S_IRGRP s 10i 0 inz( 32) D S_IWGRP s 10i 0 inz( 16) D S_IXGRP s 10i 0 inz( 8) D S_IRWXG s 10i 0 inz( 56) * other authorities D S_IROTH s 10i 0 inz( 4) D S_IWOTH s 10i 0 inz( 2) D S_IXOTH s 10i 0 inz( 1) D S_IRWXO s 10i 0 inz( 7) .EOF--- FIGURE 2: This RPG program reads ASCII data from the IFS and prints it. .BOF--- * =============================================================== * * Read data from the Integrated File System * * =============================================================== * * To compile: * * * * CRTBNDRPG PGM(XXX/CSV002RG) DFTACTGRP(*NO) BNDDIR(QC2LE) * * * * =============================================================== * Fqsysprt o f 132 printer oflind(*inof) D/copy xxx/qrpglesrc,ifsprotos D CharsRead s 10i 0 D CurChar s 1 D CR c const(x'0D') D EOF c const(x'00') D LF c const(x'25') D NULL c const(x'00') D FileName s 80 inz('/home/holt/store2.csv') D fp s 10i 0 D Input s 256 D ix s 5 0 D LogicalRec s 80 D oflag s 10i 0 D ox s 5 0 * Open the IFS file. C eval FileName = %trim(FileName) + NULL C eval oflag = O_RDONLY + O_TEXTDATA C eval fp = open(%addr(FileName): oflag) C if fp < 0 * The following line should do an error routine. C except openerror C endif C exsr GetChar C dow CurChar <> EOF C select C when CurChar = CR C except dtl C eval ox = *zero C eval LogicalRec = *blanks C when CurChar = LF * ignore line feed C other C eval ox = ox + 1 C eval %subst(LogicalRec: ox: 1) = CurChar C endsl C exsr GetChar C enddo C CallP close(fp) C eval *inLR = *on ******************* C GetChar begsr * If input buffer is empty, or all characters have been * processed, refill the input buffer. C if ix = CharsRead C eval Input = *blanks C eval CharsRead = read(fp: %addr(Input): 256) C eval ix = *zero C endif * Get the next character in the input buffer. C if CharsRead <= 0 C eval CurChar = EOF C else C eval ix = ix + 1 C eval CurChar = %subst(Input: ix: 1) C endif C C endsr Oqsysprt e dtl 1 O '(' O LogicalRec O ')#' Oqsysprt e openerror 1 O 'open error fp(' O fp O ')' .EOF--- FIGURE 3: This program builds an ASCII CSV file from a database file. .BOF--- * =============================================================== * * Write data to the Integrated File System * * =============================================================== * * To compile: * * * * CRTBNDRPG PGM(XXX/CSV003RG) DFTACTGRP(*NO) BNDDIR(QC2LE) * * * * =============================================================== * FStoreData if e k disk D/copy xxx/qrpglesrc,ifsprotos D CodePage s 10u 0 inz(819) D Comma c const(',') D NULL c const(x'00') D EndOfData s 1 D EOL c const(x'0d25') D FileName s 80 inz('/home/holt/storedta.csv') D fp s 10i 0 D oflag s 10i 0 D omode s 10u 0 D OutRec s 256 D Quote c const('"') D rc s 10i 0 * Create the IFS file. C eval oflag = O_CREAT + O_CODEPAGE + O_RDWR C eval omode = S_IRWXU + S_IROTH C eval FileName = %trim(FileName) + NULL C eval fp = open(%addr(FileName): oflag: C omode: CodePage) C if fp < 0 * Insert error handler here. C endif * Close the file so we can reopen it for output with translation C eval rc = close(fp) * Open the IFS file for output. C eval oflag = O_WRONLY + O_TEXTDATA C eval fp = open(%addr(FileName): oflag) C if fp < 0 * Insert error handler here. C endif C read StoreRec 51 C eval EndOfData = *in51 C dow EndOfData = '0' C eval OutRec = Quote + %trim(StoreID) + C Quote + Comma + C %triml(%editc(CYMTD: 'P')) + Comma + C %triml(%editc(CYYTD: 'P')) + EOL C eval rc = write(fp: %addr(OutRec): C %len(%trimr(OutRec))) C read StoreRec 51 C eval EndOfData = *in51 C enddo * Close the IFS file. C eval rc = close(fp) C eval *inLR = *on .EOF--- _____________________________________________________________________________ Saludos. Jes�s Humberto Olague Alcal� L�der de Proyectos Envases y Tapas Modelo, S.A. de C.V. e-mail: [EMAIL PROTECTED] Tel: (478) 985 4100 Ext. 247 Fax: (478) 985 4100 Ext. 249 _____________________________________________________ Forum.HELP400 es un servicio m�s de NEWS/400. � Publicaciones Help400, S.L. - Todos los derechos reservados http://www.help400.es _____________________________________________________ Para darte de baja, env�a el mensaje resultante de pulsar mailto:[EMAIL PROTECTED]
