The example program in this section uses RMS functions to maintain a simple employee file. The file is an indexed file with two keys: social security number and last name. The fields in the record are character strings defined in a structure with the tag record.
The records have the carriage-return attribute. Individual fields in each record are padded with blanks for two reasons. First, because RMS requires that the key fields be a fixed length and occur in a fixed position in each record, key fields must be padded in some way. The example program pads short fields; its use of the space character for padding is arbitrary. Second, the choice of blank padding (as opposed to null padding) allows the file to be printed or typed without conversion. Note that both the position and size of the key are attributes of the file, not of each I/O that gets done.
The program does not perform range or bounds checking. Only the error checking that shows the mapping of DEC C to RMS is performed. Any other errors are considered fatal.
The program is divided into the following sections:
To run this program, perform the following steps:
$ CC RMSEXP<Return>
For more information about the compiling process, see Chapter 1.
$ LINK RMSEXP <Return>
For more information about the linking process, see Chapter 1.
$ RMSEXP :== $device:[directory]RMSEXP<Return>
The identifier device is the logical or physical name of the device containing your directory; the identifier directory is the name of your directory. The device name must be preceded by the dollar sign ($) to be recognized as a foreign command by the DCL interpreter.
$ RMSEXP filename<Return>
The complete listing of the sample program follows. The listing is broken into sections and shown in Examples 2-1 through 2-9. Notes on each section are keyed to the numbers in the listing.
Example 2-1 shows the external data declarations and definitions.
/* This segment of RMSEXP.C contains external data * * definitions. */ #include <rms.h> #include <stdio.h> #include <ssdef.h> #include <string.h> #include <stdlib.h> #include <starlet.h> #define DEFAULT_FILE_EXT ".dat" #define RECORD_SIZE (sizeof record) #define SIZE_SSN 15 #define SIZE_LNAME 25 #define SIZE_FNAME 25 #define SIZE_COMMENTS 15 #define KEY_SIZE \ (SIZE_SSN > SIZE_LNAME ? SIZE_SSN: SIZE_LNAME) struct FAB fab; struct RAB rab; struct XABKEY primary_key,alternate_key; struct { char ssn[SIZE_SSN], last_name[SIZE_LNAME]; char first_name[SIZE_FNAME], comments[SIZE_COMMENTS]; } record; char response[BUFSIZ],*filename; int rms_status; void open_file(void); void type_options(void); void pad_record(void); void error_exit(char *); void add_employee(void); void delete_employee(void); void type_employees(void); void print_employees(void); void update_employee(void); void initialize(char *);
Key to Example 2-1:
The sizes of the fields in the record are also defined. Some (such as the social security number field) are given a constant length. Others (such as the record size) are defined as macros; the size of the field is determined with the sizeof operator. DEC C evaluates constant expressions, such as KEY_SIZE, at compile time. No special code is necessary to calculate this value.
The main function, shown in Example 2-2, controls the general flow of the program.
/* This segment of RMSEXP.C contains the main function * * and controls the flow of the program. */ main(int argc, char **argv) { if (argc < 1 || argc > 2) printf("RMSEXP - incorrect number of arguments"); else { printf("RMSEXP - Personnel Database \ Manipulation Example\n"); filename = (argc == 2 ? *++argv : "personnel.dat"); initialize(filename); open_file(); for(;;) { printf("\nEnter option (A,D,P,T,U) or \ ? for help :"); gets(response); if (feof(stdin)) break; printf("\n\n"); switch(response[0]) { case 'a': case 'A': add_employee(); break; case 'd': case 'D': delete_employee(); break; case 'p': case 'P': print_employees(); break; case 't': case 'T': type_employees(); break; case 'u': case 'U': update_employee(); break; default: printf("RMSEXP - \ Unknown Operation.\n"); case '?': case '\0': type_options(); } } rms_status = sys$close(&fab); if (rms_status != RMS$_NORMAL) error_exit("$CLOSE"); } }
Key to Example 2-2:
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.
/* This segment of RMSEXP.C contains the function that * * initializes the RMS data structures. */ void initialize(char *fn) { 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); fab.fab$l_fop = FAB$M_CIF; fab.fab$w_mrs = RECORD_SIZE; fab.fab$b_org = FAB$C_IDX; 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; rab = cc$rms_rab; /* Initialize RAB */ rab.rab$l_fab = &fab; primary_key = cc$rms_xabkey; /* Initialize Primary * * Key XAB */ primary_key.xab$b_dtp = XAB$C_STG; primary_key.xab$b_flg = 0; 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 "; alternate_key = cc$rms_xabkey; /* Initialize Alternate * * Key XAB */ alternate_key.xab$b_dtp = XAB$C_STG; 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; alternate_key.xab$l_knm = "Employee Last Name \ "; }
Key to Example 2-3:
Example 2-4 shows the internal functions for the program.
/* This segment of RMSEXP.C contains the functions that * * control the data manipulation of the program. */ void open_file(void) { 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"); rms_status = sys$connect(&rab); if (rms_status != RMS$_NORMAL) error_exit("$CONNECT"); } 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"); } 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. */ void error_exit(char *operation) { printf("RMSEXP - file %s failed (%s)\n", operation, filename); exit(rms_status); }
Key to Example 2-4:
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.
/* This segment of RMSEXP.C contains the function that * * adds a record to the file. */ void add_employee(void) { 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); pad_record(); rab.rab$b_rac = RAB$C_KEY; rab.rab$l_rbf = (char *) &record; rab.rab$w_rsz = RECORD_SIZE; rms_status = sys$put(&rab); 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:
Example 2-6 shows the function that deletes records. This function is called when 'd' or 'D' is entered in response to the menu.
/* This segment of RMSEXP.C contains the function that * * deletes a record from the file. */ void delete_employee(void) { int i; do { printf("(DELETE) Enter Social Security Number "); gets(response); i = strlen(response); } while(i == 0); while(i < SIZE_SSN) response[i++] = ' '; rab.rab$b_krf = 0; rab.rab$l_kbf = response; rab.rab$b_ksz = SIZE_SSN; rab.rab$b_rac = RAB$C_KEY; rms_status = sys$find(&rab); 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 { rms_status = sys$delete(&rab); if (rms_status != RMS$_NORMAL) error_exit("$DELETE"); } }
Key to Example 2-6:
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.
/* This segment of RMSEXP.C contains the function that * * displays a single record at the terminal. */ void type_employees(void) { int number_employees; rab.rab$b_krf = 1; rms_status = sys$rewind(&rab); if (rms_status != RMS$_NORMAL) error_exit("$REWIND"); printf("\n\nEmployees (Sorted by Last Name)\n\n"); printf("Last Name First Name SSN \ Comments\n"); printf("--------- ---------- ---------\ --------\n\n"); rab.rab$b_rac = RAB$C_SEQ; rab.rab$l_ubf = (char *) &record; rab.rab$w_usz = RECORD_SIZE; 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); } if (number_employees) printf("\nTotal number of employees = %d.\n", number_employees); else printf("[Data file is empty.]\n"); }
Key to Example 2-7:
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.
/* This segment of RMSEXP.C contains the function that * * prints the file. */ void print_employees(void) { int number_employees; FILE *fp; fp = fopen("personnel.lis", "w", "rat=cr", "rfm=var", "fop=spl"); if (fp == NULL) { perror("RMSEXP - failed opening listing \ file"); exit(SS$_NORMAL); } rab.rab$b_krf = 0; rms_status = sys$rewind(&rab); if (rms_status != RMS$_NORMAL) error_exit("$REWIND"); fprintf(fp,"\n\nEmployees (Sorted by SSN)\n\n"); fprintf(fp,"Last Name First Name SSN \ Comments\n"); fprintf(fp,"--------- ---------- ---------\ --------\n\n"); rab.rab$b_rac = RAB$C_SEQ; rab.rab$l_ubf = (char *) &record; rab.rab$w_usz = RECORD_SIZE; 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); } if (number_employees) fprintf(fp, "Total number of employees = %d.\n", number_employees); else fprintf(fp,"[Data file is empty.]\n"); fclose(fp); printf("[Listing file\"personnel.lis\"spooled to \ SYS$PRINT.]\n"); }
Key to Example 2-8:
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.
/* This segment of RMSEXP.C contains the function that * * updates the file. */ void update_employee(void) { int i; do { printf("(UPDATE) Enter Social Security Number\ "); gets(response); i = strlen(response); } while(i == 0); while(i < SIZE_SSN) response[i++] = ' '; 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; 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"); 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); pad_record(); 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: