DEC C
User's Guide for OpenVMS Systems


Previous Contents Index

Key to Example 2-7:

  1. (1) A running total of the number of records in the file is kept in the number_employees variable.
  2. (2) The key of reference is changed to the alternate key so that the employees are displayed in alphabetical order by last name.
  3. (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. (4) A heading is displayed.
  5. (5) Sequential record access is specified, and the location and size of the record is given.
  6. (6) A for loop controls the following operations:
  7. (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. (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. (1) A do loop prompts for the social security number and places the response in the response buffer.
      2. (2) The response is padded with blanks so that it will correspond to the field in the file.
      3. (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. (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. (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. (6) The fields in the record are padded with blanks.
      7. (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 DEC C in the Common Language Environment

      This chapter discusses the following topics:

      The DEC 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 DEC C programs, to call DEC C functions from programs written in other languages, or to call prewritten system routines from DEC C programs. You can call any one of the following routine types from DEC 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 DEC 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 OpenVMS System Services Reference Manual.

      3.1 Basic Calling Standard Conventions

      The 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 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 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 Digital 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 OpenVMS Calling Standard.

      3.2 Specifying Parameter-Passing Mechanisms

      When you pass data between routines that are not written in the same OpenVMS language, you have to specify how you want that data to be represented and interpreted. You do this by specifying a parameter-passing mechanism.

      The calling standard defines three ways to pass data in an argument list. When you code a reference to a non-DEC C procedure, you must know how to pass each argument and write the function reference accordingly.

      The following list describes the three argument-passing mechanisms:

      • By immediate value
        When an argument is passed by immediate value, the actual value of the argument is present in the argument list. This is the default argument-passing mechanism for all function references written in DEC C.
      • By reference
        When an argument is passed by reference, the address of the argument is present in the argument list. Use the C ampersand operator (&) to pass the address of an argument, or pass a pointer to the argument by value.
      • By descriptor
        When an argument is passed by descriptor, the address of a data structure describing the argument is present in the argument list. From a DEC C program, you pass a descriptor first by creating a structure ( struct ) that meets the descriptor requirements of the called procedure and then by passing the structure's address with the ampersand operator or by passing a pointer to that structure by value.

      The following sections outline each of these parameter-passing mechanisms in more detail.

      3.2.1 Passing Arguments by Immediate Value

      By default, all values or expressions in a DEC C function's argument list are passed by immediate value (except for X_FLOATING on OpenVMS Alpha systems, which is passed by reference). The expressions are evaluated and the results placed directly in the argument list of the CALL machine instruction.

      The following statement declares the entry point of the Set Event Flag SYS$SETEF system service, which is used to set a specific event flag to 1:


      /*  Declare the function as a function returning type int.      */ 
       
      int  SYS$SETEF(); 
      

      The SYS$SETEF system service call requires one argument---the number of the event flag to be set---to be passed by immediate value. DEC C for OpenVMS Systems converts linker-resolved variable names (such as the entry-point names of system service calls) to uppercase. You do not have to declare them in uppercase in your program (unless you compile your module with /NAMES=AS_IS). However, linker-resolved variable names must be declared and used with identical cases in each module. The documentation uses uppercase as a convention for referring to system service calls to highlight them in the text and examples.

      DEC C does not require you to declare a function or to specify the number or types of the function's arguments. However, if you call a function without declaring it or without providing argument information in the declaration, DEC C does not check the types of the arguments in a call to that function. If you declare a function prototype, the compiler does check the arguments in a call to make sure that they have the same type. (See the DEC C Language Reference Manual for more information on function prototypes.)

      Like all system services, SYS$SETEF returns an integer value (the return status of the service) in register 0. Most system services return an integer completion status; therefore, the system service does not always have to be declared before it is used. The examples in this chapter declare system services for completeness.

      In the OpenVMS System Services Reference Manual, you can find the specification of each service's arguments. SYS$SETEF, for example, takes one argument, an event flag number. It returns one of four status values, which are represented by the symbolic constants shown in Table 3-3.

      Table 3-3 Status Values of SYS$SETEF
      Returned Status Description
      SS$_WASCLR Success Flag was previously clear
      SS$_WASSET Success Flag was previously set
      SS$_ILLEFC Failure Illegal event flag number
      SS$_UNASEFC Failure Event flag not in associated cluster

      The system services manual also defines event flags as integers in the range 0 to 127, grouped in clusters of 32. Clusters 0 and 1, comprising flags 0 to 31 and 32 to 63, respectively, are local clusters available to any process, with the restriction that flags 24 to 31 are reserved for use by the OpenVMS system. There are many ways of passing valid event flag numbers from your DEC C program to SYS$SETEF. One way is to use enum to define a subset of integers, as follows:


      enum  cluster0  {completion, breakdown, beginning}  event; 
      

      After the flag numbers are defined, call the SYS$SETEF service with the following code:


         .
         .
         .
      int  status; 
      event = completion; 
         .
         .
         .
      status = SYS$SETEF(event);         /* Set event flag.          */ 
         .
         .
         .
      

      Figure 3-4 shows an argument being passed by immediate value; in this case, the event flag number passed to SYS$SETEF.

      Figure 3-4 Passing Arguments by Immediate Value


      Since argument lists consist of longwords, the calling standard dictates that immediate-value arguments be expressed in 32 bits. A single-precision, floating-point (F_floating) value is only 32 bits long, but the compiler promotes all arguments of type float to double (64 bits on a VAX processor) unless a function prototype declaration is used for the called function. This double-precision value is passed as two immediate values (two longwords).

      Note

      The passing of double-precision immediate values is a violation of the calling standard for OpenVMS VAX systems, but is an allowed exception for DEC C.

      On rare occasions, the float -to- double promotion requires some additional programming. For instance, the function OTS$POWRJ, in the VAX Common Run-Time Procedure Library, computes the value of a floating-point number raised to the power of a signed longword (in C terms, a float to the power of an int ). This function (and others like it) is called implicitly by high-level OpenVMS languages that have an exponentiation operator as part of the language. It requires that both its arguments be passed as immediate values, and it returns a single-precision ( float ) result. To pass a floating-point base to the procedure, you must use some method to avoid promoting float arguments. The recommended method is to declare the procedure using a function prototype declaration, as shown in Example 3-1.

      Example 3-1 Passing Floating-Point Arguments by Immediate Value

      /*  This program shows how to pass a floating-point value,     * 
       *  using prototypes to avoid promoting floating               * 
       *  arguments to arguments of type double.                     */ 
       
      #include <stdio.h> 
       
      /*  This declared function returns a value of type float.  It  * 
       *  should be called as follows:  OTS$POWRJ(base, power),      * 
       *  where base is of type float and power is of type int.      */ 
       
      float OTS$POWRJ(float, int); 
       
      main(void) 
      { 
                                         /*  To hold result of       * 
                                          *   OTS$POWRJ              */ 
         float  result; 
         int    power;                   /*  Power argument          */ 
       
         float base; 
       
         base =  3.145;                  /*  Assign constant to base */ 
         power  =  2; 
         result =  OTS$POWRJ(base, power); 
       
         printf("Result= %f\n", result); 
      } 
      


      Previous Next Contents Index