3.5. Retrieving Stream File Stats

Sometimes its useful to be able to look up information about a stream file. Information such as the file's size, access permissions, and the time it was last modified are all available using the stat() API.

Here is the C-language prototype for the stat() API:

      int(1) stat(const char *path(2), struct stat *buf(3))

The return value is an integer. Possible values are 0 which indicate success, or -1 if an error occurred.
This is the path of the IFS object that you wish to get information about. This argument is a C-style string, so we use the options(*string) keyword in RPG so that the system will convert it to C's format for us.
Here it wants a pointer to a stat data structure. This will be easy code in the prototype: we just define it as a pointer. However, we will also need to create a data structure in the same format as the stat data structure in C, which I'll explain below.

Here is the corresponding RPG prototype:

     D stat            PR            10I 0 ExtProc('stat')                   
     D   path                          *   value options(*string)            
     D   buf                           *   value                             

In C, when you define a data structure, you first define the layout of the data structure. In this layout you list the subfields and their data types. Then, you declare variables that use this layout.

So, in that C-language prototype above, it tells us that buf is a pointer to any variable which uses the "stat layout".

Here is the definition for "struct stat" (the "stat layout") from the C language header member:

        struct stat {                                                        
         mode_t         st_mode;       /* File mode                       */ 
         ino_t          st_ino;        /* File serial number              */ 
         nlink_t        st_nlink;      /* Number of links                 */ 
         uid_t          st_uid;        /* User ID of the owner of file    */ 
         gid_t          st_gid;        /* Group ID of the group of file   */ 
         off_t          st_size;       /* For regular files, the file        
                                        * size in bytes                   */ 
         time_t         st_atime;      /* Time of last access             */ 
         time_t         st_mtime;      /* Time of last data modification  */ 
         time_t         st_ctime;      /* Time of last file status change */ 
         dev_t          st_dev;        /* ID of device containing file    */ 
         size_t         st_blksize;    /* Size of a block of the file     */ 
         unsigned long  st_allocsize;  /* Allocation size of the file     */ 
         qp0l_objtype_t st_objtype;    /* AS/400 object type              */ 
         unsigned short st_codepage;   /* Object data codepage            */ 
         char           st_reserved1[62]; /* Reserved                     */ 
         unsigned int   st_ino_gen_id  /* file serial number generation id */

The first line simply tells us that this is a structure definition called "stat".

The remaining lines, except for the last one, are a simple list of subfields, and their data types. For example, the last subfield in this data structure is called "st_ino_gen_id" and it is an unsigned integer.

To duplicate this in RPG, what we'll do is create a normal RPG data structure. But then, we'll base that structure on a pointer. Then, when we want to use the structure in our programs, we'll simply use the LIKE() keyword to declare character strings of the same size, and move the pointer so that we can reference the subfields. (Don't worry, if that's not clear to you, yet. I'll give you an example shortly that will help you understand.)

Also, you may have noticed that both the structure definition and the API are called "stat". That's not a problem in C since structure definitions use a separate name space from procedure calls. However, it is a problem in RPG. So, we'll call our data structure "statds" instead of "stat". That way, the name won't conflict with the name of the API.

Here is the RPG definition of the stat data structure:

     D p_statds        S               *                                     
     D statds          DS                  BASED(p_statds)                   
     D  st_mode                      10U 0                                   
     D  st_ino                       10U 0                                   
     D  st_nlink                      5U 0                                   
     D  st_pad                        2A                                     
     D  st_uid                       10U 0                                   
     D  st_gid                       10U 0                                   
     D  st_size                      10I 0                                   
     D  st_atime                     10I 0                                   
     D  st_mtime                     10I 0                                   
     D  st_ctime                     10I 0                                   
     D  st_dev                       10U 0                                   
     D  st_blksize                   10U 0                                   
     D  st_allocsize                 10U 0                                   
     D  st_objtype                   12A                                     
     D  st_codepage                   5U 0                                   
     D  st_reserved1                 62A                                     
     D  st_ino_gen_id                10U 0                                   

Now, when we call stat() in RPG, we'll do something like this:

     D MyStat          S                   like(statds)
     D MySize          S             10I 0

      * get stat info into "MyStat":
     c                   if        stat('/path/to/file.txt': 
     c                                   %addr(mystat)) < 0     
     c                   callp     EscErrno(errno)
     c                   endif                                               

      * move structure to overlay the "mystat" info:
     c                   eval      p_statds = %addr(mystat)

      * read the file's size into MySize:
     c                   eval      MySize = st_size