Compaq C
Compaq C User's Guide for OpenVMS Systems
 
 
2.4 RMS Functions
RMS provides a number of functions that create and manipulate files. 
These functions use RMS data structures to define the characteristics 
of a file and its records. The data structures are used as indirect 
arguments to the function call.
 
The RMS data structures are grouped into four main categories, as 
follows:
 
  - File Access Block (FAB)---Defines the file's characteristics, such 
  as file organization and record format.
  
 - Record Access Block (RAB)---Defines the way in which records are 
  processed, such as the record access mode.
  
 - Extended Attribute Block (XAB)---Various kinds of extended 
  attribute blocks contain additional file characteristics, such as the 
  definition of keys in an indexed file. Extended attribute blocks are 
  optional.
  
 - Name Block (NAM)---Defines all or part of a file specification to 
  be used when an incomplete file specification is given in an OPEN or 
  CREATE operation. Name blocks are optional.
  
RMS uses these data structures to perform file and record operations. 
Table 2-1 lists some of the common functions.
 
 
  Table 2-1 Common RMS Run-Time Processing Functions
  
    | Category  | 
    Function  | 
    Description  | 
   
  
    
      File
        Processing
     | 
    
      sys$create
     | 
    
      Creates and opens a new file of any organization.
     | 
   
  
    | 
       
     | 
    
      sys$open
     | 
    
      Opens an existing file and initiates file processing.
     | 
   
  
    | 
       
     | 
    
      sys$close
     | 
    
      Terminates file processing and closes the file.
     | 
   
  
    | 
       
     | 
    
      sys$erase
     | 
    
      Deletes a file.
     | 
   
  
    
      Record
        Processing
     | 
    
      sys$connect
     | 
    
      Associates a file access block with a record access block to establish 
      a record access stream; a call to this function is required before any 
      other record-processing function can be used.
     | 
   
  
    | 
       
     | 
    
      sys$get
     | 
    
      Retrieves a record from a file.
     | 
   
  
    | 
       
     | 
    
      sys$put
     | 
    
      Writes a new record to a file.
     | 
   
  
    | 
       
     | 
    
      sys$update
     | 
    
      Rewrites an existing record to a file.
     | 
   
  
    | 
       
     | 
    
      sys$delete
     | 
    
      Deletes a record from a file.
     | 
   
  
    | 
       
     | 
    
      sys$rewind
     | 
    
      Positions the record pointer to the first record in the file.
     | 
   
  
    | 
       
     | 
    
      sys$disconnect
     | 
    
      Disconnects a record access stream.
     | 
   
 
All RMS functions are directly accessible from Compaq C programs. 
The syntax for any RMS function has the following form:
 
  
    
       
      
int  sys$name(struct rms_structure *pointer); 
     | 
   
 
 
In this syntax, name is the name of the RMS function (such as 
OPEN or CREATE); rms_structure is the name of the structure 
being used by the function.
 
The file-processing functions require a pointer to a file access block 
as an argument; the record-processing functions require a pointer to a 
record access block as an argument. Since sys$create is a 
file-processing function, its syntax is as follows:
 
  
    
       
      
int  sys$create(struct FAB *fab); 
     | 
   
 
 
These syntax descriptions do not show all the options available when 
you invoke an RMS function. For a complete description of the RMS 
calling sequence, see the OpenVMS Record Management Services  Reference Manual.
 
Finally, all the RMS functions return an integer status value. The 
format of RMS status values follows the standard format described in 
Chapter 3. Since RMS functions return a 32-bit integer, you do not 
need to declare the type of an RMS function return before you use it.
2.5 Writing Compaq C Programs Using RMS
 
The Compaq C Run-Time  Library (RTL) supplies a number of header files that describe the 
RMS data structures and status codes. Table 2-2 describes these 
header files.
 
 
  Table 2-2 Compaq C RMS Header Files
  
    | Header File  | 
    Structure Tag(s)  | 
    Description  | 
   
  
    | 
      
      <fab.h>
      
     | 
    
      FAB
     | 
    
      Defines the file access block structure.
     | 
   
  
    | 
      
      <rab.h>
      
     | 
    
      RAB
     | 
    
      Defines the record access block structure.
     | 
   
  
    | 
      
      <nam.h>
      
     | 
    
      NAM
     | 
    
      Defines the name block structure.
     | 
   
  
    | 
      
      <xab.h>
      
     | 
    
      XAB
     | 
    
      Defines all the extended attribute block structures.
     | 
   
  
    | 
      
      <rmsdef.h>
      
     | 
    
      --
     | 
    
      Defines the completion status codes that RMS returns after every file- 
      or record-processing operation.
     | 
   
  
    | 
      
      <rms.h>
      
     | 
    
      all tags
     | 
    
      Includes all the previous header files.
     | 
   
 
Most Compaq C programmers include the
<rms.h>
header file, which includes all the other header files.
 
These header files define all the data structures as structure tag 
names. However, they perform no allocation or initialization of the 
structures; these header files describe only a template for the 
structures. To use the structures, you must create storage for them and 
initialize all the structure members as required by RMS. Note that 
these include files are part of Compaq C for OpenVMS 
systems. RMS is part of the OpenVMS environment and may 
contain other included header files not described here.
 
To assist in the initialization process, the Compaq C RTL provides 
initialized RMS data structure variables. You can copy these variables 
to your uninitialized structure definitions with a structure 
assignment. You can choose to take the default values for each of the 
structure members, or you can tailor the contents of the structures to 
fit your requirements. In either case, you must use the structure types 
to allocate storage for the structure and to define the members of the 
structure.
 
The initialized variables supply the RMS default values for each member 
in the structure; they specify none of the optional parameters. To 
determine what default values are supplied by the initialized 
variables, see the OpenVMS Record Management Services  Reference Manual.
 
Table 2-3 lists the initialized RMS data structure variables and 
the structures that they initialize.
 
 
  Table 2-3 RMS Data Structures
  
    | Variable  | 
    Structure Type  | 
    Initialize Structure  | 
   
  
    | 
      cc$rms_fab
     | 
    
      struct FAB
     | 
    
      File access block
     | 
   
  
    | 
      cc$rms_rab
     | 
    
      struct RAB
     | 
    
      Record access block
     | 
   
  
    | 
      cc$rms_nam
     | 
    
      struct NAM
     | 
    
      Name block
     | 
   
  
    | 
      cc$rms_xaball
     | 
    
      struct XABALL
     | 
    
      Allocation extended attribute block
     | 
   
  
    | 
      cc$rms_xabdat
     | 
    
      struct XABDAT
     | 
    
      Date and time extended attribute block
     | 
   
  
    | 
      cc$rms_xabfhc
     | 
    
      struct XABFHC
     | 
    
      File header characteristics extended attribute block
     | 
   
  
    | 
      cc$rms_xabkey
     | 
    
      struct XABKEY
     | 
    
      Indexed file key extended attribute block
     | 
   
  
    | 
      cc$rms_xabpro
     | 
    
      struct XABPRO
     | 
    
      Protection extended attribute block
     | 
   
  
    | 
      cc$rms_xabrdt
     | 
    
      struct XABRDT
     | 
    
      Revision date and time extended attribute block
     | 
   
  
    | 
      cc$rms_xabsum
     | 
    
      struct XABSUM
     | 
    
      Summary extended attribute block
     | 
   
  
    | 
      cc$rms_xabtrm
     | 
    
      struct XABTRM
     | 
    
      Terminal extended attribute block
     | 
   
 
The declarations of these structures are contained in the appropriate 
header file.
 
The names of the structure members conform to the following RMS naming 
convention:
 
 
The identifier typ is the abbreviation for the structure, the 
letter s is the size of the member (such as l for longword or 
b for byte), and the identifier fld is the member name, such 
as sts for the completion status code. The dollar sign ($) is a 
character used in OpenVMS system logical names. See the 
OpenVMS Record Management Services  Reference Manual for a description of the members in each structure.
2.5.1 Initializing File Access Blocks
 
The file access block defines the attributes of the file. To initialize 
a file access block, assign the values in the initialized data 
structure cc$rms_fab to the address of the file access block defined in 
your program. Consider the following example:
 
 
  
    
       
      
/*  This example shows how to initialize a file access block.   */ 
 
#include <rms.h>               /*  Declare all RMS data structs */ 
 
struct  FAB   fblock;          /*  Define a file access block   */ 
 
main() 
{ 
   fblock = cc$rms_fab;        /*  Initialize the structure     */ 
      . 
      . 
      . 
} 
 |   
Any of these RMS structures may be dynamically allocated. For example, 
another way to allocate a file access block is as follows:
 
 
  
    
       
      
/* This program shows how to dynamically allocate RMS structures. */ 
 
#include <rms.h>                 /*  Declare all RMS data structs */ 
 
main() 
{ 
                                 /*  Allocate dynamic storage     */ 
   struct  FAB     *fptr = malloc(sizeof (struct FAB)); 
   *fptr = cc$rms_fab;           /*  Initialize the structure     */ 
      . 
      . 
      . 
} 
 |   
To change the default values supplied by a data structure variable, you 
must reinitialize the members of the structure individually. You 
initialize a member by giving the offset of the member and assigning a 
value to it. Consider the following example:
 
 
  
    
       
      
fblock.fab$l_xab = &primary_key; 
 
 |   
This statement assigns the address of the extended attribute block named
primary_key
 to the
fab$l_xab
 member of the file access block named
fblock
.
2.5.2 Initializing Record Access Blocks
 
The record access block specifies how records are processed. You 
initialize a record access block the same way you initialize a file 
access block. For example:
 
 
  
    
       
      
/*  This example shows how to initialize a file access block.  */ 
 
#include <rms.h> 
struct  FAB  fblock; 
 
struct  RAB  rblock;          /*  Define a record access block */ 
 
main() 
{ 
   fblock = cc$rms_fab;       /*  Initialize the structure     */ 
   rblock = cc$rms_rab; 
 
                              /*  Initialize the FAB member    */ 
   rblock.rab$l_fab  =  &fblock; 
      . 
      . 
      . 
} 
 |   
2.5.3 Initializing Extended Attribute Blocks
There is only one extended attribute block structure (XAB), but there 
are seven ways to initialize it. The extended attribute blocks define 
additional file attributes that are not defined elsewhere. For example, 
the key extended attribute block is used to define the keys of an 
indexed file.
 
All extended attribute blocks are chained off a file access block in 
the following manner:
 
  - In a file access block, you initialize the fab$l_xab field with the 
  address of the first extended attribute block.
  
 - You designate the next extended attribute block in the chain in the 
  xab$l_nxt field of any subsequent extended attribute blocks. You chain 
  each subsequent extended attribute block in order by the key of 
  reference (first the primary key, then the first alternate key, then 
  the second alternate key, and so forth).
  
 - You initialize the xab$l_nxt member of the last extended attribute 
  block in the chain with the value 0 (the default) to indicate the end 
  of the chain.
  
You go through the same steps to declare extended attribute blocks as 
you would to declare the other RMS data structures:
 
  - Define the structures by including the appropriate header file.
  
 - Assign a specific data structure variable to the structure in your 
  program.
  
 - Initialize the members of the structure with the desired values.
  
The following example declares two extended attribute block structures. 
They are initialized as key extended attribute blocks with the 
cc$rms_xabkey data structure variable. The xab$l_nxt member of the 
primary key is initialized with the address of the alternate_key 
extended attribute block.
 
 
  
    
       
      
/*  This example shows how to initialize the extended          * 
 *  attribute block.                                           */ 
 
#include <rms.h> 
struct  XABKEY  primary_key,alternate_key; 
 
main() 
{ 
   primary_key           = cc$rms_xabkey; 
   alternate_key         = cc$rms_xabkey; 
   primary_key.xab$l_nxt = &alternate_key; 
      . 
      . 
      . 
} 
 |   
2.5.4 Initializing Name Blocks
The name block contains default file name values, such as the directory 
or device specification, file name, or file type. If you do not specify 
one of the parts of the file specification when you open the file, RMS 
uses the values in the name block to complete the file specification 
and places the complete file specification in an array.
 
You create and initialize name blocks in the same manner used to 
initialize the other RMS data structures. Consider the following 
example:
 
 
  
    
       
      
/*  This example shows how to initialize a name block.         */ 
 
#include <rms.h> 
 
struct  NAM  nam; 
struct  FAB  fab; 
 
main() 
{ 
   fab = cc$rms_fab; 
   nam = cc$rms_nam; 
 
                              /*  Define an array for the      * 
                               *   expanded file specification */ 
   char expanded_name[NAM$C_MAXRSS]; 
 
                              /*  Initialize the appropriate   * 
                               *   members                     */ 
   fab.fab$l_nam = &nam; 
   nam.nam$l_esa = &expanded_name; 
   nam.nam$b_ess =  sizeof  expanded_name; 
      . 
      . 
      . 
} 
 |   
2.6 RMS Example Program
The example program in this section uses RMS functions to maintain a 
simple employee file. The file is an indexed file with two keys: social 
security number and last name. The fields in the record are character 
strings defined in a structure with the tag record.
 
The records have the carriage-return attribute. Individual fields in 
each record are padded with blanks for two reasons. First, because RMS 
requires that the key fields be a fixed length and occur in a fixed 
position in each record, key fields must be padded in some way. The 
example program pads short fields; its use of the space character for 
padding is arbitrary. Second, the choice of blank padding (as opposed 
to null padding) allows the file to be printed or typed without 
conversion. Note that both the position and size of the key are 
attributes of the file, not of each I/O that gets done.
 
The program does not perform range or bounds checking. Only the error 
checking that shows the mapping of Compaq C to RMS is performed. 
Any other errors are considered fatal.
 
The program is divided into the following sections:
 
  - External data declarations and definitions
  
 - Main program section
  
 - Function to initialize the RMS data structures
  
 - Internal functions to open the file, display HELP information, pad 
  the records, and process fatal errors
  
 - Utility functions
  
    - ADD
    
 - DELETE
    
 - TYPE
    
 - PRINT
    
 - UPDATE
  
  
  
To run this program, perform the following steps:
 
  - Create a source file. The name of the source file in this example 
  is RMSEXP.C. For more information about creating source files, see 
  Chapter 1.
  
 - Compile the source file with the following command:
 
     For more information about the compiling process, see Chapter 1.
   - Link the program with the following command:
 
     For more information about the linking process, see Chapter 1.
   - Because the program expects command-line arguments, it must be 
  defined as a foreign command. You can do this with the following 
  command line:
 
  
    
       
      
$ RMSEXP :== $device:[directory]RMSEXP[Return]
 
 |   
     The identifier device is the logical or physical name of the device 
    containing your directory; the identifier directory is the name of your 
    directory. The device name must be preceded by the dollar sign ($) to 
    be recognized as a foreign command by the DCL interpreter.
   - Run the program using the following foreign command:
 
  
    
       
      
$ RMSEXP  filename[Return]
 
 |   
  
The complete listing of the sample program follows. The listing is 
broken into sections and shown in Examples 2-1 through 
2-9. Notes on each section are keyed to the numbers in the 
listing.
 
Example 2-1 shows the external data declarations and definitions.
 
 
  
    | Example 2-1 External Data Declarations and 
    Definitions | 
   
  
    
       
      
/* This segment of RMSEXP.C contains external data        * 
 * definitions.                                           */ 
 
(1)#include <rms.h> 
#include <stdio.h> 
#include <ssdef.h> 
#include <string.h> 
#include <stdlib.h> 
#include <starlet.h> 
 
(2)#define  DEFAULT_FILE_EXT        ".dat" 
 
#define RECORD_SIZE              (sizeof record) 
#define SIZE_SSN                 15 
#define SIZE_LNAME               25 
#define SIZE_FNAME               25 
#define SIZE_COMMENTS            15 
#define KEY_SIZE                 \
(SIZE_SSN > SIZE_LNAME ? SIZE_SSN: SIZE_LNAME) 
 
(3)struct  FAB fab; 
struct  RAB rab; 
struct  XABKEY primary_key,alternate_key; 
 
(4)struct 
   { 
      char     ssn[SIZE_SSN], last_name[SIZE_LNAME]; 
      char     first_name[SIZE_FNAME], 
               comments[SIZE_COMMENTS]; 
   }  record; 
 
(5)char  response[BUFSIZ],*filename; 
 
(6)int  rms_status; 
 
void open_file(void); 
void type_options(void); 
void pad_record(void); 
void error_exit(char *); 
void add_employee(void); 
void delete_employee(void); 
void type_employees(void); 
void print_employees(void); 
void update_employee(void); 
void initialize(char *); 
 |   
Key to Example 2-1:
 
  - The
<rms.h>
 header file defines the RMS data structures. The
<stdio.h>
 header file contains the Standard I/O definitions. The
<ssdef.h>
 header file contains the system services definitions.
  
 - Preprocessor variables and macros are defined. 
  A default file extension .DAT is defined. 
 The sizes of the fields 
  in the record are also defined. Some (such as the social security 
  number field) are given a constant length. Others (such as the record 
  size) are defined as macros; the size of the field is determined with 
  the
sizeof
 operator. Compaq C evaluates constant expressions, such as
KEY_SIZE,
 at compile time. No special code is necessary to calculate this value.
   - Static storage for the RMS data structures is 
  declared. The file access block, record access block, and extended 
  attribute block types are defined by the
<rms.h>
 header file. One extended attribute block is defined for the primary 
 key and one is defined for the alternate key.
  
 - The records in the file are defined using a 
  structure with four fields of character arrays.
  
 - The
BUFSIZ
 constant is used to define the size of the array that will be used to 
 buffer input from the terminal. The file-name variable is defined as a 
 pointer to
char
.
  
 - The variable
rms_status
 is used to receive RMS return status information. After each function 
 call, RMS returns status information as an integer. This return status 
 is used to check for specific errors, end-of-file, or successful 
 program execution.
  
The
main
 function, shown in Example 2-2, controls the general flow of the 
 program.
 
 
  
    | Example 2-2 Main Program Section | 
   
  
    
       
      
/* This segment of RMSEXP.C contains the main function    * 
 * and controls the flow of the program.                  */ 
 
(1)main(int argc, char **argv) 
{ 
(2)   if (argc < 1 || argc > 2) 
      printf("RMSEXP - incorrect number of arguments"); 
   else 
      { 
 
         printf("RMSEXP - Personnel Database \
                 Manipulation Example\n"); 
 
(3)         filename = (argc == 2 ? *++argv : "personnel.dat"); 
(4)         initialize(filename); 
(5)         open_file(); 
 
         for(;;) 
            { 
(6)               printf("\nEnter option (A,D,P,T,U) or \
? for help :"); 
 
               gets(response); 
               if (feof(stdin)) 
                  break; 
               printf("\n\n"); 
 
(7)               switch(response[0]) 
                  { 
                     case 'a': case 'A':  add_employee(); 
                                          break; 
 
                     case 'd': case 'D':  delete_employee(); 
                                          break; 
 
                     case 'p': case 'P':  print_employees(); 
                                          break; 
 
                     case 't': case 'T':  type_employees(); 
                                          break; 
 
                     case 'u': case 'U':  update_employee(); 
                                          break; 
 
                     default:             printf("RMSEXP - \
                                                  Unknown Operation.\n"); 
 
                     case '?': case '\0': 
                                          type_options(); 
                  } 
            } 
 
(8)         rms_status = sys$close(&fab); 
 
(9)         if (rms_status != RMS$_NORMAL) 
                     error_exit("$CLOSE"); 
      } 
} 
 |   
Key to Example 2-2:
 
  
        
	
	  | 
	 
		 
		
		
	 
	 |