Document revision date: 30 March 2001

## OpenVMS Record Management Services Reference Manual

Previous | Contents | Index

### 2.4.6 Types of Errors

RMS completion status error codes generally fall into one of four groups:

- Programming errors
- Program design errors
- System environment errors
- Operator/user errors

> #### Programming Errors

> These errors are caused by incorrect programming and are usually detected during the early stages of developing and debugging a program that uses RMS. Typical examples are missing values for required fields, referring to a RAB instead of a FAB, invalid address for a buffer, and so forth. This type of error is generally self-explanatory and usually requires only a minor change to the program.

> #### Program Design Errors

> These errors are caused by more subtle errors that may rarely occur, particularly if asynchronous record I/O, multistreamed sharing, or shared files are involved. These errors may occur long after a program has been in use and could require either a major program revision or the addition of substantial error recovery code to handle the error conditions. Record-lock errors, resource-exhaustion errors, and record-stream-currently-active errors are typical program design errors.

> #### System Environment Errors

> These errors include hardware errors and RMS or other system software errors that are not caused by your program. You may need to add substantial defensive error-handling code or you may be able to run the program again without error.

> #### Operator/User Errors

> These errors include errors by the user of the program, such as not mounting a device before running the program, or typing an invalid file specification. As with system environment errors, you may need to add substantial defensive error-handling code or simply reprompt the user for the correct information or user action.

There are conditions in which completion status codes may not be returned as expected:

- The completion status codes that apply to the Close service do not include errors introduced by the FAB$V_SCF option and the FAB$V_SPL option. If the request is serviced successfully, then a success completion code is returned, even if the request is found to be in error by the job controller process.
- The Wait service has unique errors. This service can return any status code of the awaited operation.
- Errors associated with output operations may not necessarily be reported as the status of a particular operation because modified I/O buffers are not always written out immediately. Such errors are reported as the status of a subsequent operation, which may be an input, output, or control operation.

When you submit a problem report, you should also provide a magnetic tape copy of the file causing the error.

When using the debugger, use the debug command EXAMINE/CONDITION to view the message corresponding to the value in R0 or the STS field or the STV field. For example, you can view the error codes in the STS and STV fields of the FAB at symbolic address (label) MYFAB when debugging a VAX MACRO program by entering the following commands:

    DBG> EXAMINE/CONDITION MYFAB+FAB$L_STS
    DBG> EXAMINE/CONDITION MYFAB+FAB$L_STV

For additional information about the debugger, see the OpenVMS Debugger Manual.

## 2.5 Allowable Program Execution Modes

Generally, RMS executes in either executive mode or executive AST mode. When an operation is initiated, processing begins in executive mode. If device I/O is necessary to process the request, the $QIO system service is called. RMS specifies an executive-mode AST to signal completion. At this point, RMS exits from executive mode. If the operation is being performed asynchronously, control is returned to the caller; if the operation is synchronous, RMS waits for an event flag in the access mode of the caller. When the I/O is complete, RMS continues processing in executive AST mode. Thus, user-mode ASTs can be serviced while a synchronous operation called from user mode is awaiting I/O completion. However, processing in user mode during an asynchronous operation is interrupted by RMS processing in executive AST mode when I/O completes.

RMS should not be called from kernel mode, from executive AST mode, or from executive mode when executive-mode ASTs are disabled.

## 2.6 Access-Mode Protected Memory

RMS protects the following data structures and their associated I/O buffers at EW (execute read/write):

- RMS-controlled data structures
- Process-permanent data structures
- Image-activated data structures

Previously, the data structures were protected at UREW (user read, executive write).

The following memory protection exceptions apply to USER-mode accessors of RMS and are protected at UREW:

- Internal RMS I/O buffers to facilitate RAB$V_LOC mode
- RMS buffers containing collated tables used for indexed files

## 2.7 Reserved Event Flags

RMS uses system-reserved event flags to synchronize its internal operations. RMS reserves event flags 27, 28, 29, and 30 for possible use; in addition, event flag 31 is used to specify a "do not care" event flag for asynchronous processing.

## 2.8 DEC Multinational Character Set

You can use any character in the DEC Multinational character set in RMS records, including the key value of an indexed file. Keys are collated according to their corresponding character code value.

For a list of characters in the DEC Multinational character set, see the OpenVMS User's Manual.

## Chapter 3
Implementing RMS from C Programs

This section includes C programming examples illustrating the implementation of RMS from a high-level programming interface. Each of the sample programs illustrates the implementation of a particular programming task in the OpenVMS environment.

From a high-level language program, you can create new files, process existing files, extend and delete files, and read, write, update, and delete records within files in an RMS environment.

To create and process RMS files, your program must contain calls to appropriate record management services from your language interface with RMS. Generally, you make these calls by using the service macros for run-time processing. When encountered at run time, the expanded code of these macros generates a call to the corresponding service. Each call represents a program request for a file service, a record service, or a block I/O transfer operation.

## 3.1 Creating, Accessing, and Deaccessing a File

You can create, access, and deaccess a file using either the Create service or the Open service. The Create service constructs a new file structured to the attributes you specify in the FAB for the file, whereas the Open service makes an existing file available for processing by your program. Both of these services allocate the system resources needed to establish an access path to a file. You must open or create a file to perform most file operations and any record operations on that file. Where applicable, you must declare the type of shared access when you create or open a file. You do this through your program interface with RMS by selecting file access control options.

RMS provides several file-processing options for the Create service. The create-if option requests that the file be created only if it does not exist in the specified directory. If the file does exist in the specified directory, the existing file is opened. The Open and Create services both establish access to the desired file, but the Create service also allocates disk space and performs the functions related to allocation.

When you are finished processing a file, you invoke the Close service to close the file, disconnect all record streams associated with the file, and free all resources allocated to the file. If you do not explicitly invoke the Close service when the program image exits, RMS attempts an implicit close. All resources associated with open files are returned when the files are deaccessed at image rundown time. However, process permanent files are not implicitly closed when an image exits.

### 3.1.1 Example of Copying Records from One File to Another File

Example 3-1 illustrates the use of various services to access and copy records from one file to another.

Example 3-1 Use of the Create, Open, and Close Services

    /*
    ** COPYFILE.C     This program copies the input file to the output file.
    ** It is made to resemble the MACRO example in the RMS Reference Manual.
    */
    #define REC_SIZE 132
    #define INPUT_NAME "INFILE"
    #define OUTPUT_NAME "OUTFILE"
    #define DEFAULT_NAME ".DAT"

    #include        <rms>                /* defines for rabs and fabs    */
    #include        <stdio>              /* defins printf...             */
    #include        <starlet>    /* defines sys$open et al       */

    COPYFILE ()
    {
    struct FAB      infab, outfab, *fab;    /* Allocate fabs and a pointer to fab */
    struct RAB      inrab, outrab, *rab;    /* Allocate fabs and a pointer to fab */
    int             lib$signal();
    int             stat;
    char            rec_buff[REC_SIZE];     /* maximum record size                */

    infab = cc$rms_fab;                     /* Make this a real FAB (bid and bln) */
    infab.fab$l_fna = (char *) &INPUT_NAME; /* Primary file name: (logical) name..*/
    infab.fab$b_fns = sizeof INPUT_NAME -1; /* .. and its size                    */
    infab.fab$l_dna = (char *) &DEFAULT_NAME; /* Default name: here file type..     */
    infab.fab$b_dns = sizeof DEFAULT_NAME -1; /* .. and its size                  */

    inrab = cc$rms_rab;                     /* Make this a real RAB (bid and bln) */
    inrab.rab$l_fab = &infab;               /* Point to FAB for $CONNECT          */
    inrab.rab$v_rah = 1;                    /* Set bitVield to request read-ahead */
    inrab.rab$l_ubf = rec_buff;             /* Point to buffer area..             */
    inrab.rab$w_usz = REC_SIZE;             /* and indicate its size              */

    outfab = cc$rms_fab;                    /* Make this a real FAB (bid and bln) */
    outfab.fab$v_ctg = 1;                   /* Allocate contigeously              */
    outfab.fab$v_put = 1;                   /* Write access (default on create)   */
    outfab.fab$v_nil = 1;                   /* No sharing (default on create)     */
    outfab.fab$b_rat = FAB$M_CR;            /* Set option using bitMask           */
    outfab.fab$w_mrs = REC_SIZE;
    outfab.fab$l_fna = (char *) &OUTPUT_NAME;
    outfab.fab$b_fns = sizeof OUTPUT_NAME -1;
    outfab.fab$l_dna = (char *) &DEFAULT_NAME;
    outfab.fab$b_dns = sizeof DEFAULT_NAME -1;

    outrab = cc$rms_rab;
    outrab.rab$l_fab = &outfab;
    outrab.rab$v_wbh = 1;                   /* Write Ahead                        */
    outrab.rab$l_rbf = rec_buff;            /* Same buffer address as before      */

    fab = &infab;                           /* for error handling               */
    stat = sys$open ( fab );                /* Actual open (could use &infab)   */
    if (stat & 1)                           /* $OPEN Success ? */
    {
        outfab.fab$l_alq = infab.fab$l_alq; /* Set proper size for output       */
        fab = &outfab;                      /* for error handling               */
        stat = sys$create ( fab );          /* Try to create the file           */
    }

    if (stat & 1)                           /* Both open & create success ?     */
    {
        rab = &outrab;                      /* for error handling               */
        stat = sys$connect ( rab );         /* get some rms internal buffers    */
        if (stat & 1)                       /* output $CONNECT Success ?        */
        {
            rab = &inrab;                   /* for error handling               */
            stat = sys$connect ( rab );     /* input $CONNECT Success ?         */
        }
        if (stat & 1)                       /* CONNECTs succes? then prime loop */
            stat = sys$get ( rab );         /* setting stat for while           */

        while (stat & 1)                    /* success on record operation ? */
        {
            /*
            ** Main Code. Opened and connected files and buffer
            ** First $GET done and inrab is current. Copy records.
            */
            outrab.rab$w_rsz = inrab.rab$w_rsz;     /* set correct size     */
            rab = &outrab;                          /* error handler        */
            stat = sys$put ( rab );                 /* Actual copy          */
            if (stat & 1)                           /* $PUT success?        */
            {
                rab = &inrab;                       /* for error handling   */
                stat = sys$get ( rab );             /* $GET next , set stat */
            }
        } /* while */

        /*
        ** Fallen through while. stat must be EOF if copy was succesful.
        ** if not, signal error from connect, get or put. Using stat instead
        ** of rab->rab$l_sts to handle (programming) error providing RAB.
        */

        if (stat != RMS$_EOF)
            stat = lib$signal( stat, rab->rab$l_stv );

        stat = sys$close ( &infab );
        stat = sys$close ( &outfab );
        }
    else
    {
        /* Failed to open input or output file */
        stat = lib$signal( stat, fab->fab$l_stv );
    }

    return stat;                    /* Using output close stat to return */
    }

This example illustrates how you can use the sequential file organization to create a new file by copying records from an existing file. The newly created file and the source file have variable-length records.

This example assumes that an external program has identified the input file as a search list logical name using the equivalent of the following DCL command:

    $ ASSIGN [INV]30JUN93,[INV.OLD]30JUN93 INFILE

This command directs RMS to look for the input file in directory [INV] first, and, if it does not find the file, to look in directory [INV.OLD].

The program also specifies the default file type .INV for the input file using this statement:

    infab.fab$l_dna = &DEFAULT_NAME;        /* Default name: here file type..     */
    infab.fab$b_dns = sizeof DEFAULT_NAME;  /* .. and its size                    */

Next the program configures the RAB used for the input file. The first argument links the RAB to the associated FAB. This is the only required argument to a RAB. The rest of the arguments specify the read-ahead option (described in later text) and the record buffer for the input file. The Get service uses the user record buffer address (UBF) field and the user record buffer size (USZ) field as inputs to specify the record buffer and the record size, respectively.

Note When you invoke the GET service, RMS takes control of the record buffer and may modify it. RMS returns the record size and only guarantees the contents from where it accessed the record to the completion of the record.

The program then configures the FAB for the output file. The first argument equates the file name to the externally defined logical name. After the program specifies the default file type for the output file, it specifies three additional FAB fields.

First, it specifies that RMS should allocate contiguous space for the output file by setting the CTG bit in the FAB$L_FOP field of the FAB.

Next, the program uses a program-defined variable to store the value 132 in the MRS field:

    #define REC_SIZE 132
    outfab.fab$w_mrs = REC_SIZE;

The program then specifies that each record is to be preceded by a line feed and followed by a carriage return whenever the record is output to a line printer or terminal:

    outfab.fab$b_rat = FAB$M_CR;            /* Set option using bitMask           */

Because the program alternately reads and then writes each record, the input file and the output file may share the same buffer. However, because the Put service does not have access to the UBF and UBZ fields, the output RAB defines the buffer using the RBF and the RSZ fields.

Note that the UBF, USZ, and RBF values are set prior to run time, but that the RSZ value is set at run time, just prior to invocation of the Put service. This is done because the input file contains variable-length records and the Put service relies on the Get service to supply each record's size by way of the RSZ field, an INRAB output field.

The following statement from the sample program illustrates this feature:

    outrab.rab$w_rsz = inrab.rab$w_rsz;     /* set correct size     */

The run-time processing macros for the input file consist of a $OPEN, a $CONNECT, a $GET, and a $CLOSE macro. Because the input file already exists, the program accesses it with a $OPEN macro. The sole argument to this macro identifies the FAB to the Open service:

    stat = SYS$OPEN ( fab );                /* Actual open (could use &infab)   */

Next, the program connects a record stream to the input file by calling the Connect service and specifying INRAB as the appropriate RAB:

    stat = SYS$CONNECT ( rab );             /* get some rms internal buffers    */

Note that upon completion of each service call, the program tests the condition value in STAT returned by the service before proceeding to the next call. If the call fails, the program exits with the appropriate control block address in rab or fab.

After creating the output file and establishing its record stream, the program begins a processing loop in which the Get service reads a record from the input file and the Put service writes the record to the output file. When all file records are copied, as indicated by the detection of the end of the file, the main while loop terminates.

The Close service disconnects the record stream for all RABs connected to the specified FAB. In a multistream environment (more than one RAB can be connected to a single FAB), a program may disconnect individual record streams using the Disconnect service.

## 3.2 Program to Illustrate Record Operations

The record-processing services provide the following record operations:

- Get
- Find
- Put
- Update
- Delete

This section illustrates the use of RMS record operations from a C program as shown in Example 3-2.

Example 3-2 Record Operations

    /*
                    RECORD OPERATIONS

            This program looks up records by key and then

               *  copies the record to an output file,
               *  deletes the record, or
               *  updates the record
    */

    #include        <rms>                /* defines for rabs and fabs    */
    #include        <stdio>              /* defins printf...             */
    #include        <string>     /* defines strlen               */
    #include        <stdlib>     /* defines exit                 */
    #include        <starlet>    /* defines sys$open et al       */

    #define INPUT_NAME      "INFILE:"
    #define OUTPUT_NAME     "OUTFILE:"
    #define RECORD_SIZE     132
    #define TYPING_SIZE     100

    struct  FAB     infab, outfab;
    struct  RAB     inrab, outrab;

    error_exit (code, value)
    long    code;
    long    value;
    {
            void lib$signal();
            lib$signal (code, value);
            exit (0);
    }
    main ()
    {
            char    record [RECORD_SIZE];           /* record buffer */
            char    key [RECORD_SIZE];              /* key buffer */
            char    choice [TYPING_SIZE];           /* typing buffer */
            long    status;

            /* set up input fab */
            infab = cc$rms_fab;
            infab.fab$b_fac = FAB$M_GET | FAB$M_PUT | FAB$M_UPD | FAB$M_DEL;
            infab.fab$b_shr = FAB$M_SHRGET | FAB$M_SHRPUT | FAB$M_SHRUPD
                              | FAB$M_SHRDEL;       /* read/write sharing */
            infab.fab$l_fna = (char *) &INPUT_NAME; /* logical name INFILE */
            infab.fab$b_fns = sizeof INPUT_NAME - 1;

            /* set up output fab */
            outfab = cc$rms_fab;
            outfab.fab$b_fac = FAB$M_PUT;
            outfab.fab$l_fna = (char *) &OUTPUT_NAME; /* logical name OUTFILE */
            outfab.fab$b_fns = sizeof OUTPUT_NAME - 1;
            outfab.fab$w_mrs = RECORD_SIZE;         /* record size */
            outfab.fab$b_org = FAB$C_REL;           /* relative file */
            outfab.fab$b_rat = FAB$M_CR;            /* implied carriage return */

            /* set up input rab */
            inrab = cc$rms_rab;
            inrab.rab$l_fab = &infab;
            inrab.rab$b_rac = RAB$C_KEY;            /* key access */
            inrab.rab$b_krf = 0;                    /* access by primary key */
            inrab.rab$l_kbf = key;                  /* key buffer */
            inrab.rab$l_ubf = record;               /* record buffer */
            inrab.rab$w_usz = RECORD_SIZE;          /* maximum record size */

            /* set up output rab */
            outrab = cc$rms_rab;
            outrab.rab$l_fab = &outfab;
            outrab.rab$l_rbf = record;              /* record buffer */

            /* open files and connect streams */
            status = sys$open (&infab);
            if (! (status & 1))
                    error_exit (status, infab.fab$l_stv);
            status = sys$connect (&inrab);
            if (! (status & 1))
                    error_exit (status, inrab.rab$l_stv);
            status = sys$create (&outfab);
            if (! (status & 1))
                    error_exit (status, outfab.fab$l_stv);
            status = sys$connect (&outrab);
            if (! (status & 1))
                    error_exit (status, outrab.rab$l_stv);

            while (1)
            {
                    /* get a key and a record */
                    printf ("Please input key value: ");
                    gets (key);                     /* get key from user */
                    if (feof (stdin))               /* stop on ctrl-Z */
                            break;
                    inrab.rab$b_ksz = strlen (key); /* set key length */
                    status = sys$get (&inrab);
                    if (! (status & 1))
                            error_exit (status, inrab.rab$l_stv);

                    /* display the record */
                    record[inrab.rab$w_rsz] = '\0';
                    printf ("Record: {%s}\n", record);

                    /* choose what to do */
                    printf ("Please choose C(opy), D(elete), or U(pdate):");
                    gets (choice);                  /* get choice from user */
                    if (feof (stdin))               /* stop on ctrl-Z */
                            break;
                    switch (choice[0])
                    {
                            case 'c':
                            case 'C':
                                    /* copy the record */
                                    outrab.rab$w_rsz = inrab.rab$w_rsz;
                                                    /* out length = in length */
                                    status = sys$put (&outrab);
                                    if (! (status & 1))
                                            error_exit (status, outrab.rab$l_stv);
                                    break;
                            case 'd':
                            case 'D':
                                    /* delete */
                                    status = sys$delete (&inrab);
                                    if (! (status & 1))
                                            error_exit (status, inrab.rab$l_stv);
                                    break;
                            case 'u':
                            case 'U':
                                    /* get a new record */
                                    printf ("Please input record value: ");
                                    gets (record);  /* get record from user */
                                    inrab.rab$w_rsz = strlen (record);
                                                    /* set record length */
                                    status = sys$update (&inrab);
                                    if (! (status & 1))
                                            error_exit (status, inrab.rab$l_stv);
                                    break;
                            default:
                                    /* do nothing */
                                    break;
                    }
            }

            /* close files */
            status = sys$close (&infab);
            if (! (status & 1))
                    error_exit (status, infab.fab$l_stv);
            status = sys$close (&outfab);
            if (! (status & 1))
                    error_exit (status, outfab.fab$l_stv);
    }

The program requires access to RMS.H and STDIO.H in SYS$LIBRARY to provide RMS structure definitions and standard C input/output. The program sets up four FABs: a FAB and a RAB for the input file and a FAB and a RAB for the output file. The program then opens and connects the input file and creates and connects the output file.

The main loop prompts the user for a key and then retrieves the record with that key. It then prompts the user for a decision of what to do with the record:

- Copy the record to the output file
- Delete the record
- Update the record using new user data

The program handles all errors by signaling the error and then exiting.

Previous | Next | Contents | Index

| privacy and legal statement |
4523PRO_002.HTML