[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

  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.


Chapter 3
Using HP C in the Common Language Environment

This chapter discusses the following topics:

The HP C compiler is part of the OpenVMS common language environment. This environment defines certain calling procedures and guidelines that allow you to call routines written in different languages from HP C programs, to call HP C functions from programs written in other languages, or to call prewritten system routines from HP C programs. You can call any one of the following routine types from HP C:

  • Routines written in other OpenVMS languages
  • OpenVMS RTL routines
  • OpenVMS system services
  • OpenVMS utility routines

The terms routine, procedure, and function are used throughout this chapter. A routine is a closed, ordered set of instructions that performs one or more specific tasks. Every routine has an entry point (the routine name), and optionally an argument list. Procedures and functions are specific types of routines: a procedure is a routine that does not return a value; a function is a routine that returns a value by assigning that value to the function's identifier.

System routines are prewritten OpenVMS routines that perform common tasks, such as finding the square root of a number or allocating virtual memory. You can call any system routine from your program, provided that HP C supports the data structures required to call the routine. The system routines used most often are OpenVMS RTL routines and system services. System routines, which are discussed later in this chapter, are documented in detail in the VMS Run-Time Library Routines Volume and the HP OpenVMS System Services Reference Manual.

3.1 Basic Calling Standard Conventions

The HP OpenVMS Calling Standard describes the concepts used by all OpenVMS languages to invoke routines and pass data between them. It also describes the differences between the VAX and Alpha parameter-passing mechanisms. The OpenVMS calling standard specifies the following attributes:

  • Register usage
  • Stack usage
  • Function return value
  • Argument list

The following sections discuss these attributes in more detail for OpenVMS VAX systems. For more detail on OpenVMS Alpha systems, see the HP OpenVMS Calling Standard.

The calling standard also defines such attributes as the calling sequence, the argument data types and descriptor formats, condition handling, and stack unwinding. These attributes are discussed in detail in the OpenVMS Programming Interfaces: Calling a System Routine.

3.1.1 Register and Stack Usage

The calling standard defines several registers and their uses, as listed in Table 3-1 for VAX systems and Table 3-2 for Alpha systems.

Table 3-1 VAX Register Usage
Register Use
PC Program counter
SP Stack pointer
FP Current stack frame pointer
AP Argument pointer
R1 Environment value (when necessary)
R0, R1 Function return value registers

Table 3-2 Alpha Register Usage
Register Use
PC Program counter
SP Stack pointer
FP Frame pointer for current procedure
R25 Argument information register
R16 to R21,
F16 to F21
Argument list registers
R0 Function return value register

By definition, any called routine can use registers R2 through R11 for computation, and the AP register as a temporary register.

In the calling standard, a stack is defined as a last-in/first-out (LIFO) temporary storage area that the system allocates for every user process. The system keeps information about each routine call in the current image on the call stack. Then, each time you call a routine, the system creates a structure on this call stack, known as the call frame. The call frame for each active process contains the following data:

  • A pointer to the call frame of the previous routine call. This pointer corresponds to the frame pointer (FP).
  • The argument pointer (AP) of the previous routine call.
  • The storage address of the point at which the routine was called; that is, the address of the instruction following the call to the current routine. This address is called the program counter (PC).
  • The contents of other general registers. Based on a mask specified in the control information, the system restores the saved contents of these registers to the calling routine when control returns to it.

When a routine completes execution, the system uses the frame pointer in the call frame of the current routine to locate the frame of the previous routine. The system then removes the call frame of the current routine from the stack.

Figure 3-1 shows the call stack and several call frames for VAX processors. Function A calls function B, which calls function C. When a function reaches a return statement or when control reaches the end of the function, the system uses the frame pointer in the call frame of the current function to locate the frame of the previous function. It then removes the call frame of the current function from the stack.

Figure 3-1 The Call Stack


3.1.2 Return of the Function Value

A function is a routine that returns a single value to the calling routine. The function value represents the value of the expression in the return statement. According to the calling standard, a function value may be returned as either an actual value or a condition value that indicates success or failure.

3.1.3 The Argument List

The HP OpenVMS Calling Standard also defines a data structure called the argument list. You use an argument list to pass information to a routine and receive results.

On OpenVMS Alpha systems, an argument list is formed using registers R16 to R21 or F16 to F21, and a collection of quadwords in memory (depending on the number and type of the arguments).

On OpenVMS VAX systems, an argument list is a collection of longwords in memory that represents a routine parameter list and possibly includes a function value. Figure 3-2 shows the structure of a typical OpenVMS VAX argument list.

Figure 3-2 Structure of an OpenVMS VAX Argument List


The first longword must be present; this longword stores the number of arguments (the argument count: n) as an unsigned integer value in the low byte of the longword with a maximum of 255 arguments. The remaining 24 bits of the first longword are reserved for use by HP and should be 0. The longwords labeled arg1 through argn are the actual parameters, which can be any of the following addresses or value:

  • An uninterpreted 32-bit value that is passed by value
  • An address that is passed by reference
  • An address of a descriptor that is passed by descriptor

The argument list contains the parameters that are passed to the routine. Depending on the passing mechanisms for these parameters, the forms of the arguments contained in the argument list vary. For example, if you pass three arguments, the first by value, the second by reference, and the third by descriptor, the argument list would contain the value of the first argument, the address of the second, and the address of the descriptor of the third. Figure 3-3 shows this argument list.

Figure 3-3 Example of an OpenVMS VAX Argument List


For additional information on the OpenVMS calling standard, see the HP OpenVMS Calling Standard.


Previous Next Contents Index