Compaq C
Compaq C User's Guide for OpenVMS Systems
- 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).
- This statement checks that you used the
correct number of arguments when invoking the program.
- 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.
- The file access block, record access block,
and extended attribute blocks are initialized.
- The file is opened using the RMS sys$open
function.
- The program displays a menu and checks for
end-of-file (the character Ctrl/Z).
- A
switch
statement and a set of
case
statements control the function to be called, which is determined by
the response from the terminal.
- 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.
- 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:
- 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.
- This statement initializes the
file-processing options member with the create-if option. A file is
created if one does not exist.
- 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.
- 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.
- The data structure variable cc$rms_xabkey
initializes an extended attribute block for one key of an indexed file.
- The position of the key is specified by
subtracting the offset of the member from the base of the structure.
- A separate extended attribute block is
initialized for the alternate key.
- 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).
- 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:
- 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.
- The RMS sys$connect function associates the
record access block with the file access block.
- 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.
- For each field in the record, the
pad_record
function fills the remaining bytes in the field with blanks.
- 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:
- 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.
- When all fields have been entered, the
pad_record
function pads each field with blanks.
- 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.
- The RMS sys$put function writes the record to
the file.
- 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:
- A
do
loop prompts you to type a social security number at the terminal and
places the response in the response buffer.
- The social security number is padded with
blanks.
- 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.
- The RMS sys$find function locates the record
specified by the social security number entered from the terminal.
- 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.
- 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:
- A running total of the number of records in
the file is kept in the
number_employees
variable.
- The key of reference is changed to the
alternate key so that the employees are displayed in alphabetical order
by last name.
- 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.
- A heading is displayed.
- Sequential record access is specified, and
the location and size of the record is given.
- 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
- 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:
- 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
.
- The key of reference for the indexed file is
the primary key.
- The RMS sys$rewind function positions the
file at the first record. The return status is checked for success.
- A heading is written to the sequential file
using the standard I/O library function
fprintf
.
- 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.
- 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
- 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.
- 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:
- A
do
loop prompts for the social security number and places the response in
the response buffer.
- The response is padded with blanks so that it
will correspond to the field in the file.
- 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.
- 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.
- 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.
- The fields in the record are padded with
blanks.
- 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.
|
|