United States

Compaq C

# Compaq C
User's Guide for OpenVMS Systems

Previous | Contents | Index

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).
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:

1. A series of `do` loops controls the input of information. For each field in the record, a prompt is displayed. The response is buffered and the field is copied to the structure.
2. When all fields have been entered, the `pad_record` function pads each field with blanks.
3. Three members in the record access block are initialized before writing the record. The record access member (rab$b_rac) is initialized for keyed access. The record buffer and size members (rab$l_rbf and rab$w_rsz) are initialized with the address and size of the record to be written.
4. The RMS sys$put function writes the record to the file.
5. The `rms_status` variable is checked. If the return status is normal, or if the record has a duplicate key value and duplicates are allowed, the function prints a message stating that the record was added to the file. Any other return value is treated as a fatal error causing `error_exit` to be called.

Example 2-6 shows the function that deletes records. This function is called when ' `d` ' or ' `D` ' is entered in response to the menu.

Example 2-6 Utility Function: Deleting Records

    /* This segment of RMSEXP.C contains the function that    *
     * deletes a record from the file.                        */

    void delete_employee(void)
    {
       int i;
    (1)   do
          {
             printf("(DELETE) Enter Social Security Number   ");
             gets(response);
             i =  strlen(response);
          }
       while(i == 0);

    (2)   while(i < SIZE_SSN)
          response[i++] = ' ';

    (3)   rab.rab$b_krf =  0;
       rab.rab$l_kbf =  response;
       rab.rab$b_ksz =  SIZE_SSN;
       rab.rab$b_rac =  RAB$C_KEY;

    (4)   rms_status =  sys$find(&rab);

    (5)   if (rms_status != RMS$_NORMAL && rms_status != RMS$_RNF)
          error_exit("$FIND");
       else
          if (rms_status == RMS$_RNF)
             printf("RMSEXP - specified employee does not \
    exist.\n");

          else
             {
    (6)            rms_status = sys$delete(&rab);
                if (rms_status != RMS$_NORMAL)
                   error_exit("$DELETE");
             }
    }

Key to Example 2-6:

1. A `do` loop prompts you to type a social security number at the terminal and places the response in the response buffer.
2. The social security number is padded with blanks.
3. Some members in the record access block must be initialized before the program can locate the record. Here, the key of reference (0 specifies the primary key), the location and size of the search string (this is the address of the response buffer and its size), and the type of record access (in this case, keyed access) are given.
4. The RMS sys$find function locates the record specified by the social security number entered from the terminal.
5. The program checks the `rms_status` variable for the values RMS$_NORMAL and RMS$_RNF (record not found). A message is displayed if the record cannot be found. Any other error is a fatal error.
6. The RMS sys$delete function deletes the record. The return status is checked only for success.

Example 2-7 shows the function that displays the employee file at the terminal. This function is called from the `main` function when ' `t` ' or ' `T` ' is entered in response to the menu.

Example 2-7 Utility Function: Typing the File

    /* This segment of RMSEXP.C contains the function that    *
     * displays a single record at the terminal.              */

    void type_employees(void)
    {
    (1)   int  number_employees;

    (2)   rab.rab$b_krf =  1;

    (3)   rms_status =  sys$rewind(&rab);
       if (rms_status != RMS$_NORMAL)
          error_exit("$REWIND");

    (4)   printf("\n\nEmployees (Sorted by Last Name)\n\n");
       printf("Last Name      First Name          SSN      \
              Comments\n");

       printf("---------      ----------          ---------\
              --------\n\n");
    (5)   rab.rab$b_rac =  RAB$C_SEQ;
       rab.rab$l_ubf =  (char *) &record;
       rab.rab$w_usz =  RECORD_SIZE;

    (6)   for(number_employees = 0; ; number_employees++)
          {
             rms_status =  sys$get(&rab);
             if (rms_status != RMS$_NORMAL && rms_status !=
                                              RMS$_EOF)
                error_exit("$GET");
             else
                if (rms_status == RMS$_EOF)
                   break;

             printf("%.*s%.*s%.*s%.*s\n",
                     SIZE_LNAME, record.last_name,
                     SIZE_FNAME, record.first_name,
                     SIZE_SSN, record.ssn,
                     SIZE_COMMENTS, record.comments);
          }

    (7)   if (number_employees)
          printf("\nTotal number of employees = %d.\n",
                    number_employees);
       else
          printf("[Data file is empty.]\n");
    }

Key to Example 2-7:

1. A running total of the number of records in the file is kept in the `number_employees` variable.
2. The key of reference is changed to the alternate key so that the employees are displayed in alphabetical order by last name.
3. The file is positioned to the beginning of the first record according to the new key of reference, and the return status of the sys$rewind function is checked for success.
4. A heading is displayed.
5. Sequential record access is specified, and the location and size of the record is given.
6. A `for` loop controls the following operations:
   - Incrementing the `number_employees` counter
   - Locating a record and placing it in the record structure, using the RMS sys$get function
   - Checking the return status of the RMS sys$get function
   - Displaying the record at the terminal
7. This `if` statement checks for records in the file. The result is a display of the number of records or a message indicating that the file is empty.

Example 2-8 shows the function that prints the file on the printer. This function is called by the `main` function when ' `p` ' or ' `P` ' is entered in response to the menu.

Example 2-8 Utility Function: Printing the File

    /* This segment of RMSEXP.C contains the function that    *
     * prints the file.                                       */

    void print_employees(void)
    {
       int  number_employees;
       FILE *fp;

    (1)   fp = fopen("personnel.lis", "w", "rat=cr",
                           "rfm=var", "fop=spl");
       if (fp == NULL)
          {
             perror("RMSEXP - failed opening listing \
    file");

             exit(SS$_NORMAL);
          }

    (2)   rab.rab$b_krf =  0;

    (3)   rms_status =  sys$rewind(&rab);
       if (rms_status != RMS$_NORMAL)
          error_exit("$REWIND");

    (4)   fprintf(fp,"\n\nEmployees (Sorted by SSN)\n\n");
       fprintf(fp,"Last Name      First Name       SSN      \
         Comments\n");

       fprintf(fp,"---------      ----------       ---------\
         --------\n\n");

    (5)   rab.rab$b_rac =  RAB$C_SEQ;
       rab.rab$l_ubf =  (char *) &record;
       rab.rab$w_usz =  RECORD_SIZE;

    (6)   for(number_employees = 0; ; number_employees++)
          {
             rms_status =  sys$get(&rab);
             if (rms_status != RMS$_NORMAL &&
                 rms_status != RMS$_EOF)
                error_exit("$GET");
             else
                if (rms_status == RMS$_EOF)
                   break;

             fprintf(fp, "%.*s%.*s%.*s%.*s",
                     SIZE_LNAME,record.last_name,
                     SIZE_FNAME,record.first_name,
                     SIZE_SSN,record.ssn,
                     SIZE_COMMENTS,record.comments);
          }
    (7)   if (number_employees)
          fprintf(fp, "Total number of employees = %d.\n",
                  number_employees);
       else
          fprintf(fp,"[Data file is empty.]\n");

    (8)   fclose(fp);
          printf("[Listing file\"personnel.lis\"spooled to \
    SYS$PRINT.]\n");
    }

Key to Example 2-8:

1. This function creates a sequential file with carriage-return carriage-control, variable-length records. It spools the file to the printer when the file is closed. The file is created using the standard I/O library function `fopen` , which associates the file with the file pointer, `fp` .
2. The key of reference for the indexed file is the primary key.
3. The RMS sys$rewind function positions the file at the first record. The return status is checked for success.
4. A heading is written to the sequential file using the standard I/O library function `fprintf` .
5. The record access, user buffer address, and user buffer size members of the record access block are initialized for keyed access to the record located in the record structure.
6. A `for` loop controls the following operations:
   - Initializing the running total and then incrementing the total at each iteration of the loop
   - Locating the records and placing them in the record structure with the RMS sys$get function, one record at a time
   - Checking the `rms_status` information for success and end-of-file
   - Writing the record to the sequential file
7. The `number_employees` counter is checked. If it is 0, a message is printed indicating that the file is empty. If it is not 0, the total is printed at the bottom of the listing.
8. The sequential file is closed. Since it has the spl record attribute, the file is automatically spooled to the printer. The function displays a message at the terminal stating that the file was successfully spooled.

Example 2-9 shows the function that updates the file. This function is called by the `main` function when ' `u` ' or ' `U` ' is entered in response to the menu.

Example 2-9 Utility Function: Updating the File

    /* This segment of RMSEXP.C contains the function that    *
     * updates the file.                                      */

    void update_employee(void)
    {
       int i;
    (1)   do
          {
             printf("(UPDATE) Enter Social Security Number\
          ");

             gets(response);
             i =  strlen(response);
          }
       while(i == 0);

    (2)   while(i < SIZE_SSN)
          response[i++] = ' ';

    (3)   rab.rab$b_krf =  0;
       rab.rab$l_kbf =  response;
       rab.rab$b_ksz =  SIZE_SSN;
       rab.rab$b_rac =  RAB$C_KEY;
       rab.rab$l_ubf =  (char *) &record;
       rab.rab$w_usz =  RECORD_SIZE;

    (4)   rms_status =  sys$get(&rab);

       if (rms_status != RMS$_NORMAL && rms_status != RMS$_RNF)
          error_exit("$GET");
       else
          if (rms_status == RMS$_RNF)
             printf("RMSEXP - specified employee does not \
    exist.\n");

    (5)      else
             {
                printf("Enter the new data or RETURN to leave \
    data unmodified.\n\n");

                printf("Last Name:");
                gets(response);
                if (strlen(response))
                   strncpy(record.last_name, response,
                           SIZE_LNAME);

                printf("First Name:");
                gets(response);
                if (strlen(response))
                   strncpy(record.first_name, response,
                           SIZE_FNAME);

                printf("Comments:");
                gets(response);
                if (strlen(response))
                   strncpy(record.comments, response,
                           SIZE_COMMENTS);

    (6)            pad_record();

    (7)            rms_status =  sys$update(&rab);
                if (rms_status != RMS$_NORMAL)
                   error_exit("$UPDATE");

                printf("[Record has been successfully \
    updated.]\n");
             }
    }

Key to Example 2-9:

1. A `do` loop prompts for the social security number and places the response in the response buffer.
2. The response is padded with blanks so that it will correspond to the field in the file.
3. Some of the members in the record access block are initialized for the operation. The primary key is specified as the key of reference, the location and size of the key value are given, keyed access is specified, and the location and size of the record are given.
4. The RMS sys$get function locates the record and places it in the record structure. The function checks the `rms_status` value for RMS$_NORMAL and RMS$_RNF (record not found). If the record is not found, a message is displayed. If the record is found, the program prints instructions for updating the record.
5. If you press the Return key, the record is placed in the record structure unchanged. If you make a change to the record, the new information is placed in the record structure.
6. The fields in the record are padded with blanks.
7. The RMS sys$update function rewrites the record. The program then checks that the update operation was successful. Any error causes the program to call the fatal error-handling routine.

Previous | Next | Contents | Index

privacy and legal statement