Wednesday, June 22, 2016

Write to a file in the IFS using RPG

fputs c api to help rpg write to ifs file

After writing about how to read an IFS file in a RPG program I received a message from Domenico asking me how to do the opposite.

Very interesting article, as always, the only suggestion is: since, surfing internet, it is possible to find many examples about using _c_ifs_open but not one (!!) of _c_ifs_write .. it would be very very interesting and useful for our community.

Before I get started explaining how to write to an IFS file using RPG, let me say I am not going to repeat what I said in my earlier post A better way to read a file in the IFS with RPG, so you might want to review that before continuing with this post.

My goal is to write several strings to a file in the IFS. The first time I want to create the file and then write to it, the second I want to append data to the existing file.

Let me start with the definitions.

01  **free
02  ctl-opt option(*srcstmt) dftactgrp(*no) ;

03  dcl-pr OpenFile pointer extproc('_C_IFS_fopen') ;
04    *n pointer value ;  // File name
05    *n pointer value ;  // File mode
06  end-pr ;

07  dcl-pr WriteFile pointer extproc('_C_IFS_fputs') ;
08    *n pointer value ;  // String to write
09    *n pointer value ;  // Open mode
10  end-pr ;

11  dcl-pr CloseFile extproc('_C_IFS_fclose') ;
12    *n pointer value ;  // Misc pointer
13  end-pr ;

14  dcl-s PathFile char(50) ;
15  dcl-s OpenMode char(100) ;
16  dcl-s FilePtr pointer inz ;
17  dcl-s SndData char(32767) ;
18  dcl-s i packed(3) ;

Line 1: As I am writing in totally free RPG I need this at the top of my program. If you are unfamiliar with totally free RPG go check out the different flavors of free form RPG.

Line 2: I need the DFTACTGRP(*NO) as I am using external procedures.

Lines 3 – 6: Procedure prototype for the fopen, open file, external procedure.

Lines 7 – 10: This is the procedure for the write. Notice how it is fputs, which will write a string to an IFS file. As with the other APIs used it starts with _C_IFS_ then the API name. It has two parameters, both pointers to the string I want to write to the file and the mode in which the file was opened.

Lines 11 – 13: Procedure prototype for the procedure to close the IFS file.

Lines 14 – 18: These are the variables I will be using in the program. I won't bother to describe what each is for here, and you can probably guess their function from their names.

I want to create the file in the IFS before I write to it. Below is the code to do that:

19  PathFile = '/Simon/test_write.txt' + x'00' ;
20  OpenMode = 'w, o_ccsid=1252' + x'00' ;

21  FilePtr = OpenFile(%addr(PathFile):%addr(OpenMode)) ;
22  if (FilePtr = *null) ;
23    dsply ('Unable to open file (1)') ;
24    return ;
25  endif ;

26  CloseFile(%addr(PathFile)) ;

Line 19: This is the folder in the IFS and the name of the file I want to create. I do need to "null terminate" the string with hex '00'.

Line 20: This is the mode in which I want to open the file. The "w" is lower case and means that the file must be opened in "write" mode. The o_cssid denotes the CCSID I want to create the file in. This is the part that took the longest for me to work out. On the IBM i I am using the "correct" CCSID is 1252, you may find that yours requires a different CCSID. Again I have to terminate the string with null.

Line 21: When the file is opened a new file is created, if there is one already the "w" indicates that the existing one will be overlaid with this new one. If there was an error opening the new file null is placed in FilePtr.

Lines 22 – 25: If there was an error a message is displayed and program is exited with a RETURN.

Line 26: I now want to close the file, as I do not want to write to it in CCSID 1265. I want to use my IBM i's default CCSID.

As I now have a file I am going to write to it.

27  OpenMode = 'w' + x'00' ;  // Clear file, then write to file

28  FilePtr = OpenFile(%addr(PathFile):%addr(OpenMode)) ;
29  if (FilePtr = *null) ;
30     dsply ('Unable to open file (2)') ;
31     return ;
32  endif ;

33  for i = 1 to 10 ;
34    SndData = 'Write No. ' + %char(i) ;
35    WriteFile(%addr(SndData):FilePtr) ;
36  endfor ;

37  CloseFile(%addr(PathFile)) ;

Line 27: My open mode is to be "write", "w", this means that if there is any data already within the file it will be deleted with the first write to the file. I have not given a CCSID here as I want to write to the file with the default CCSID.

Lines 28 – 32: Opens the file, and then handles the error if the file cannot be opened.

Lines 33 – 36: I perform this for loop ten times writing to the IFS file. The write has to use the address of a pointer to the variable SndData, and the pointer retrieved from the opening of the file, line 28.

Line 37: After I am done writing to the file I close it.

If I perform the section of code again it will not append records to the file, as I open it for "write" the file is cleared. To append to the file I need to open it as "append". All I need to do is to replace the "w" with "a" when I open the file.

27  OpenMode = 'a' + x'00' ;  // Append to the file

After opening the file as "append" if I perform the write to the file as I did before my IFS file will contain teo sets of records number 1 – 10.

I can even go and read the file using the program I created for the read an IFS file in a RPG program post to confirm that the data is good.

 

You can learn more about the fputs procedure from the IBM website here.

 

This article was written for IBM i 7.2, and should work for earlier releases too.

2 comments:

  1. I've occasionally wondered about the performance of the "C" APIs, vs the "Unix" APIs for creating, changing, and working with IFS stream files.

    ReplyDelete
  2. Simon it is greatly appreciated that you are sharing on how to utilize files on the IFS from RPG programs.

    ReplyDelete

To prevent "comment spam" all comments are moderated.
Learn about this website's comments policy here.

Some people have reported that they cannot post a comment using certain computers and browsers. If this is you feel free to use the Contact Form to send me the comment and I will post it for you, please include the title of the post so I know which one to post the comment to.