[an error occurred while processing this directive]

HP OpenVMS Systems

C Programming Language
Content starts here HP C

HP C
User's Guide for OpenVMS Systems


Previous Contents Index

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:

  1. In a file access block, you initialize the fab$l_xab field with the address of the first extended attribute block.
  2. 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).
  3. 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:

  1. Define the structures by including the appropriate header file.
  2. Assign a specific data structure variable to the structure in your program.
  3. 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 HP 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:

  1. 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.
  2. Compile the source file with the following command:


    $ CC  RMSEXP[Return]
    

    For more information about the compiling process, see Chapter 1.
  3. Link the program with the following command:


    $ LINK RMSEXP [Return]
    

    For more information about the linking process, see Chapter 1.
  4. 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.
  5. 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:

  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.
  2. 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. HP C evaluates constant expressions, such as KEY_SIZE, at compile time. No special code is necessary to calculate this value.
  3. 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.
  4. The records in the file are defined using a structure with four fields of character arrays.
  5. 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 .
  6. 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:

  1. The main function is entered with two parameters. The first is the number of arguments used to call the program; the second is a pointer to the first argument (file name).
  2. This statement checks that you used the correct number of arguments when invoking the program.
  3. If a file name is included in the command line to execute the program, that file name is used. If a file extension is not given, .DAT is the file extension. If no file name is specified, then the file name is PERSONNEL.DAT.
  4. The file access block, record access block, and extended attribute blocks are initialized.
  5. The file is opened using the RMS sys$open function.
  6. The program displays a menu and checks for end-of-file (the character Ctrl/Z).
  7. A switch statement and a set of case statements control the function to be called, which is determined by the response from the terminal.
  8. The program ends when Ctrl/Z is entered in response to the menu. At that time, the RMS sys$close function closes the employee file.
  9. The rms_status variable is checked for a return status of RMS$_NORMAL. If the file is not closed successfully, then the error-handling function terminates the program.

Example 2-3 shows the function that initializes the RMS data structures. See the RMS documentation for more information about the file access block, record access block, and extended attribute block structure members.

Example 2-3 Function Initializing RMS Data Structures

/* This segment of RMSEXP.C contains the function that    * 
 * initializes the RMS data structures.                   */ 
 
void initialize(char *fn) 
{ 
 
(1)   fab =  cc$rms_fab;               /* Initialize FAB     */ 
   fab.fab$b_bks =  4; 
   fab.fab$l_dna =  DEFAULT_FILE_EXT; 
   fab.fab$b_dns =  sizeof DEFAULT_FILE_EXT -1; 
   fab.fab$b_fac =  FAB$M_DEL | FAB$M_GET | 
                    FAB$M_PUT | FAB$M_UPD; 
   fab.fab$l_fna =  fn; 
   fab.fab$b_fns =  strlen(fn); 
(2)   fab.fab$l_fop =  FAB$M_CIF; 
   fab.fab$w_mrs =  RECORD_SIZE; 
   fab.fab$b_org =  FAB$C_IDX; 
(3)   fab.fab$b_rat =  FAB$M_CR; 
   fab.fab$b_rfm =  FAB$C_FIX; 
   fab.fab$b_shr =  FAB$M_NIL; 
   fab.fab$l_xab =  &primary_key; 
 
(4)   rab =  cc$rms_rab;              /* Initialize RAB      */ 
 
   rab.rab$l_fab =  &fab; 
 
(5)   primary_key =  cc$rms_xabkey;   /* Initialize Primary  * 
                                    *  Key XAB            */ 
   primary_key.xab$b_dtp = XAB$C_STG; 
   primary_key.xab$b_flg = 0; 
(6)   primary_key.xab$w_pos0 =  (char *) &record.ssn - 
                             (char *) &record; 
   primary_key.xab$b_ref =  0; 
   primary_key.xab$b_siz0 = SIZE_SSN; 
   primary_key.xab$l_nxt =  &alternate_key; 
   primary_key.xab$l_knm =  "Employee Social Security \
Number       "; 
 
(7)   alternate_key = cc$rms_xabkey; /* Initialize Alternate * 
                                   *  Key XAB             */ 
   alternate_key.xab$b_dtp = XAB$C_STG; 
(8)   alternate_key.xab$b_flg =  XAB$M_DUP | XAB$M_CHG; 
   alternate_key.xab$w_pos0 =  (char *) &record.last_name - 
                               (char *) &record; 
   alternate_key.xab$b_ref =  1; 
   alternate_key.xab$b_siz0 =  SIZE_LNAME; 
(9)   alternate_key.xab$l_knm =   "Employee Last Name \
               "; 
} 

Key to Example 2-3:

  1. The data structure variable cc$rms_fab initializes the file access block with default values. Some members have no default values; they must be initialized. Such members include the file-name string address and size. Other members can be initialized to override the default values.
  2. This statement initializes the file-processing options member with the create-if option. A file is created if one does not exist.
  3. This statement initializes the record attributes member with the carriage-return control attribute. Records are terminated with a carriage return/line feed when they are printed on the printer or displayed at the terminal.
  4. The data structure variable cc$rms_rab initializes the record access block with the default values. In this case, the only member that must be initialized is the rab$l_fab member, which associates a file access block with a record access block.
  5. The data structure variable cc$rms_xabkey initializes an extended attribute block for one key of an indexed file.
  6. The position of the key is specified by subtracting the offset of the member from the base of the structure.
  7. A separate extended attribute block is initialized for the alternate key.
  8. This statement specifies that more than one alternate key can contain the same value (XAB$M_DUP), and that the value of the alternate key can be changed (XAB$M_CHG).

    Note

    RMS constants shown here are in the form xxx$M_yyy (for example, RAB$M_FIX) or xxx$C_yyy (for example, RAB$C_FIX). The OpenVMS RMS documentation cites the constants in the form xxx$V_yyy (for example, rab$v_fix), the difference being:
    • The $M type constant signifies a bit mask, and should be OR'ed to an existing value.
    • The $V type constant represents the bit position of a constant, and a shift operation is necessary for setting the appropriate bit.


    Using a $V type constant the same way as a $M type constant is a common problem.
  9. The key-name member is padded with blanks because it is a fixed-length, 32-character field.

Example 2-4 shows the internal functions for the program.

Example 2-4 Internal Functions

/* This segment of RMSEXP.C contains the functions that   * 
 * control the data manipulation of the program.          */ 
 
void open_file(void) 
{ 
(1)   rms_status =  sys$create(&fab); 
   if (rms_status != RMS$_NORMAL && 
       rms_status != RMS$_CREATED) 
      error_exit("$OPEN"); 
 
   if (rms_status == RMS$_CREATED) 
      printf("[Created new data file.]\n"); 
 
(2)   rms_status =  sys$connect(&rab); 
   if (rms_status != RMS$_NORMAL) 
      error_exit("$CONNECT"); 
} 
 
 
(3)void type_options(void) 
{ 
   printf("Enter one of the following:\n\n"); 
   printf("A     Add an employee.\n"); 
   printf("D     Delete an employee specified by SSN.\n"); 
   printf("P     Print employee(s) by ascending SSN on \
line printer.\n"); 
 
   printf("T     Type employee(s) by ascending last name \
on terminal.\n"); 
   printf("U     Update employee specified by SSN.\n\n"); 
   printf("?     Type this text.\n"); 
   printf("^Z    Exit this program.\n\n"); 
} 
 
 
(4)void pad_record(void) 
{ 
   int      i; 
 
   for(i = strlen(record.ssn); i < SIZE_SSN; i++) 
      record.ssn[i] = ' '; 
   for(i = strlen(record.last_name); i < SIZE_LNAME; i++) 
      record.last_name[i] = ' '; 
   for(i = strlen(record.first_name); i < SIZE_FNAME; i++) 
      record.first_name[i] = ' '; 
   for(i = strlen(record.comments);i < SIZE_COMMENTS; i++) 
      record.comments[i] = ' '; 
} 
 
/* This subroutine is the fatal error-handling routine.   */ 
 
(5)void error_exit(char *operation) 
{ 
   printf("RMSEXP - file %s failed (%s)\n", 
           operation, filename); 
   exit(rms_status); 
} 

Key to Example 2-4:

  1. The open_file function uses the RMS sys$create function to create the file, giving the address of the file access block as an argument. The function returns status information to the rms_status variable.
  2. The RMS sys$connect function associates the record access block with the file access block.
  3. The type_options function, called from the main function, prints help information. Once the help information is displayed, control returns to the main function, which processes the response that is typed at the terminal.
  4. For each field in the record, the pad_record function fills the remaining bytes in the field with blanks.
  5. This function handles fatal errors. It prints the function that caused the error, returns an OpenVMS error code (if appropriate), and exits the program.

Example 2-5 shows the function that adds a record to the file. This function is called when ' a ' or ' A ' is entered in response to the menu.

Example 2-5 Utility Function: Adding Records

/* This segment of RMSEXP.C contains the function that    * 
 * adds a record to the file.                             */ 
 
void add_employee(void) 
{ 
(1)   do 
      { 
         printf("(ADD)   Enter Social Security Number:"); 
 
         gets(response); 
 
      } 
   while(strlen(response) == 0); 
 
   strncpy(record.ssn,response,SIZE_SSN); 
 
   do 
      { 
         printf("(ADD)    Enter Last Name:"); 
 
         gets(response); 
      } 
   while(strlen(response) == 0); 
 
   strncpy(record.last_name,response,SIZE_LNAME); 
 
   do 
      { 
         printf("(ADD)    Enter First Name:"); 
 
         gets(response); 
      } 
   while(strlen(response) == 0); 
 
   strncpy(record.first_name,response,SIZE_FNAME); 
 
   do 
      { 
         printf("(ADD)    Enter Comments:"); 
 
         gets(response); 
      } 
   while(strlen(response) == 0); 
 
   strncpy(record.comments,response,SIZE_COMMENTS); 
 
(2)   pad_record(); 
 
(3)   rab.rab$b_rac =  RAB$C_KEY; 
   rab.rab$l_rbf =  (char *) &record; 
   rab.rab$w_rsz =  RECORD_SIZE; 
 
(4)   rms_status =  sys$put(&rab); 
(5)   if (rms_status != RMS$_NORMAL && rms_status != 
                     RMS$_DUP && rms_status != RMS$_OK_DUP) 
      error_exit("$PUT"); 
   else 
      if (rms_status == RMS$_NORMAL || rms_status == 
                                                 RMS$_OK_DUP) 
         printf("[Record added successfully.]\n"); 
      else 
         printf("RMSEXP - Existing employee with same SSN, \
not added.\n"); 
} 

Key to Example 2-5:


Previous Next Contents Index