/* ** COPYRIGHT (c) 1993 BY ** DIGITAL EQUIPMENT CORPORATION, MAYNARD, MASSACHUSETTS. ** ALL RIGHTS RESERVED. ** ** THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY BE USED AND COPIED ** ONLY IN ACCORDANCE OF THE TERMS OF SUCH LICENSE AND WITH THE ** INCLUSION OF THE ABOVE COPYRIGHT NOTICE. THIS SOFTWARE OR ANY OTHER ** COPIES THEREOF MAY NOT BE PROVIDED OR OTHERWISE MADE AVAILABLE TO ANY ** OTHER PERSON. NO TITLE TO AND OWNERSHIP OF THE SOFTWARE IS HEREBY ** TRANSFERRED. ** ** THE INFORMATION IN THIS SOFTWARE IS SUBJECT TO CHANGE WITHOUT NOTICE ** AND SHOULD NOT BE CONSTRUED AS A COMMITMENT BY DIGITAL EQUIPMENT ** CORPORATION. ** ** DIGITAL ASSUMES NO RESPONSIBILITY FOR THE USE OR RELIABILITY OF ITS ** SOFTWARE ON EQUIPMENT WHICH IS NOT SUPPLIED BY DIGITAL. */ #ifdef VAX #module DISK_DRIVER "V1.0-001" #else #pragma module DISK_DRIVER "V1.0-001" #endif /* **++ ** FACILITY: SYS$EXAMPLES ** ** MODULE DESCRIPTION: ** ** DISK_DRIVER -- Programming example to demonstrate use of ** OpenVMS disk drivers using RMS and QIO services. The module ** first uses RMS $CREATE to create a file called MYDATAFIL.DAT in ** the default directory and then writes one hundred 512-byte (1 disk ** block) records, each of which contains the record number ** repeated 512 times, using RMS $PUT. It then uses QIO services ** to access, randomly read and write, and close the file, swapping the ** contents of each pair of records. If any errors are detected, ** the program exits with the error status associated with the ** error. ** ** AUTHORS: ** ** Digital Equipment Corporation ** ** CREATION DATE: 5 February 1993 (based on DISK_DRIVER.MAR) ** ** ** MODIFICATION HISTORY: ** ** X-1 DCP001 05-Feb-1993 ** Conversion from DISK_DRIVER.MAR. **-- */ /* ** ** INCLUDE FILES ** */ #include /* Descriptor Structure and Constant Definitions */ #include /* File Information Block Definitions */ #include /* File identification Definitions */ #include /* I/O function code Definitions */ #include /* Library (LIB$) routine definitions */ #include /* All RMS Structure & Return Status Definitions */ #include /* System Service Return Status Value Definitions */ #include /* System routine definitions*/ /* ** ** MACRO DEFINITIONS ** */ #define NUM_RECS 100 /* ** The following macro defines a status check which ** occurs numerous times throughout the program. */ #define check_s(status) \ if ( ! (status & 1) ) \ {\ lib$signal (status);\ } /* Define references to FIB fields, for VAX only */ #ifdef VAX /* Use full structure/union names on VAX */ #define f_i_b_outer1 f_i_b.fib$r_fid_overlay.fib$r_fid_fields #define f_i_b_outer2 f_i_b_outer1.fib$r_fid_rvn_overlay #define f_i_b$w_fid_num f_i_b_outer1.fib$w_fid_num #define f_i_b$w_fid_seq f_i_b_outer1.fib$w_fid_seq #define f_i_b$w_fid_rvn f_i_b_outer2.fib$w_fid_rvn #define f_i_b$l_acctl f_i_b.fib$r_acctl_overlay.fib$l_acctl #else /* Use tags otherwise */ #define f_i_b$w_fid_num f_i_b.fib$w_fid_num #define f_i_b$w_fid_seq f_i_b.fib$w_fid_seq #define f_i_b$w_fid_rvn f_i_b.fib$w_fid_rvn #define f_i_b$l_acctl f_i_b.fib$l_acctl #endif /* Declare RMS control blocks */ struct FAB f_a_b; /* File Access Block */ struct fibdef f_i_b; /* File Information Block */ struct RAB r_a_b; /* Record Access BLock */ struct NAM n_b; /* Name Block */ /* Declare Bits and Pieces for File Access */ unsigned long int status; /* Status Return */ unsigned long int record_match = SS$_NORMAL; /* Record Number Match */ char file_name[] = "SYS$DISK:MYDATAFIL.DAT"; /* File Name */ unsigned long int file_size = sizeof(file_name); /* File Size */ $DESCRIPTOR (device_descr, "SYS$DISK"); /* Device descriptor */ struct { /* File Information Block Descriptor */ unsigned short int length, pad; struct fibdef * addr; } fib_descr = { FIB$K_LENGTH, 0, &f_i_b }; /* Declare Buffer Area */ struct { char buf1 [512], buf2 [512]; } block_buf; /* **++ ** FUNCTIONAL DESCRIPTION: ** ** This is the main routine which writes the file using RMS and ** re-writes the file using QIO services. ** ** FORMAL PARAMETERS: ** ** None ** ** RETURN VALUE: ** ** Final status from deassign, or error code via lib$signal if ** error occurs. ** ** SIDE EFFECTS: ** ** Creates file SYS$DISK:MYDATAFIL.DAT. ** ** PRECONDITIONS: ** ** Default directory must exist and be writeable; the device must ** have enough space (and user quota if enabled) to create a ** contiguous 100-block file. ** ** ** **-- */ main () { /* ** Declare local counters etc. */ unsigned long int device_channel; /* Channel Number */ unsigned long int record_count; /* Record Count */ unsigned long int byte_count; /* Byte Count within record */ unsigned long int io_status[2]; /* I/O status block */ /* ** In the first part of the program, we start off by building the file ** using RMS functions. */ /* ** Begin by creating and opening the file using a FAB and NAM block. */ /* ** First, fill the necessary fields in the FAB. */ f_a_b = cc$rms_fab; /* Begin by using the initialized */ /* template provided by the system */ /* f_a_b.fab$b_bid = FAB$C_BID; /* Identify this block as a FAB */ /* f_a_b.fab$b_bln = FAB$K_BLN; /* Insert length of FAB */ f_a_b.fab$l_alq = 100; /* Initial file size = 100 */ /* blocks */ f_a_b.fab$b_fac = FAB$M_PUT; /* Access type = output */ f_a_b.fab$l_fna = file_name; /* File name string address */ f_a_b.fab$b_fns = file_size; /* File name string size */ f_a_b.fab$l_fop = FAB$M_CTG; /* File is to be contiguous */ f_a_b.fab$w_mrs = 512; /* Maximum record size is 512 bytes */ f_a_b.fab$l_nam = &n_b; /* File name block address */ f_a_b.fab$b_org = FAB$C_SEQ; /* File org is sequential */ f_a_b.fab$b_rfm = FAB$C_FIX; /* Record fmt is fixed lgth */ /* ** Initialize the name block using the initialized template ** provided by the system. */ n_b = cc$rms_nam; /* n_b.nam$b_bid = NAM$C_BID; /* Block ID */ /* n_b.nam$b_bln = NAM$K_BLN; /* Block length */ /* ** Now, create the file and open it via RMS. */ status=sys$create ( &f_a_b, /* Address of FAB */ 0, /* no on-error completion routine */ 0); /* no on-success completion routine */ check_s(status) /* ** Next, connect up the RAB. First fill the necessary RAB ** fields. */ r_a_b = cc$rms_rab; /* Begin by using the initialized */ /* template provided by the system */ /* r_a_b.rab$b_bid = RAB$C_BID; /* Identify block as RAB */ /* r_a_b.rab$b_bln = RAB$K_BLN; /* Insert length of RAB */ r_a_b.rab$l_fab = &f_a_b; /* File access block address */ r_a_b.rab$b_rac = RAB$C_SEQ; /* Sequential Access */ r_a_b.rab$l_rbf = &block_buf.buf1[0]; /* Record buffer address */ r_a_b.rab$w_rsz = 512; /* Record buffer size is 512 bytes */ /* ** Do the connect. */ status = sys$connect ( &r_a_b, /* Address of RAB */ 0, /* no on-error completion routine */ 0) ; /* no on-success completion routine */ /* Close and exit if error */ check_s(status) /* ** Now loop through and write the file. For each record, fill ** the record with the record number, byte by byte, and write ** it out using $PUT. */ for (record_count = 1 ; record_count <= NUM_RECS ; record_count++ ) { /* Fill record with record number, byte by byte */ for (byte_count = 0; byte_count <=511; byte_count++ ) { block_buf.buf1[byte_count] = (unsigned char) record_count; } /* $PUT the record out to the file */ status = sys$put ( &r_a_b, /* Record Access Block */ 0, /* no on-error compl rtn */ 0); /* no on-succ compl rtn */ /* Close and exit if error */ check_s(status) if (!(status&1)) { lib$signal (status); } } /* ** All records are written now, so close the file. Before doing so, ** though, save the file ID information in a file information block ** (FIB) for Part 2 of the program. */ f_i_b$w_fid_num = n_b.nam$w_fid_num; /* File # */ f_i_b$w_fid_seq = n_b.nam$w_fid_seq; /* Seq # */ f_i_b$w_fid_rvn = n_b.nam$w_fid_rvn; /* Rel Vol # */ /* Now close the file */ status = sys$close ( &f_a_b, 0, /* no on-error completion routine */ 0); /* no on-success completion routine */ check_s(status) /* Exit if error */ /* ** In the second part of the program, we swap every other record, ** using QIO services, to demonstrate random read and write ** access to a file. */ /* ** We begin by using $ASSIGN to assign a channel to the file ** device. */ status = sys$assign ( &device_descr, /* Device descriptor */ &device_channel, /* Channel number */ 0, /* Default access mode */ 0); /* No mailbox */ check_s(status) /* Exit if error */ /* ** To open the file, we use $QIO, specifying the io$access ** function, with read & write access specified. We use the ** file ID information from the first part of the program to ** get access to the file we want. */ /* Set up access in FIB to be write(=>read), with no */ /* other writers allowed. */ f_i_b$l_acctl = FIB$M_NOWRITE|FIB$M_WRITE; status = sys$qiow ( 0, /* No event flag number */ device_channel, /* Channel */ IO$_ACCESS|IO$M_ACCESS, /* Access the file */ &io_status, /* IO status block */ 0, /* No AST completion routine */ 0, /* So no AST routine parameters */ &fib_descr, /* P1 = address of FIB descriptor*/ 0, /* No P2-P6 */ 0, 0, 0, 0); check_s(status) /* Close and exit if error */ /* ** Now that the file is open, we process the records a pair ** at a time, checking as we go to ensure the contents of the ** records are corrent before we swap the pairs. The ** swapping of the records in a pair is done by writing the ** contents of the second record to the first record, and ** vice versa. */ for (record_count = 1 ; record_count <= NUM_RECS ; record_count += 2) /* count goes up by 2 for pairs */ { /* ** Read two records in. Since they're contiguous, we get ** them both with one read virtual block. */ status = sys$qiow ( 0, /* No EFN */ device_channel, /* Channel */ IO$_READVBLK, /* Func = read virtual block */ &io_status, /* IOSB */ 0, /* No AST completion routine */ 0, /* or parameters */ &block_buf.buf1[0], /* P1 = addr of buffer */ 1024, /* P2 = length of transfer */ record_count, /* P3 = record number */ 0, /* No P4-P6 */ 0, 0) ; check_s(status) /* make sure read OK */ /* ** Verify that the contents of the two records are ** correct. If not, return a "dummy" status code of -4 to ** indicate record number mismatch. */ /* First record OK? */ for (byte_count = 0; byte_count < 512; byte_count++ ) { if (block_buf.buf1[byte_count] != record_count) { record_match = -4; /* Dummy error status value */ } } /* Second record OK? */ for (byte_count = 0; byte_count < 512; byte_count++ ) { if (block_buf.buf2[byte_count] != record_count+1) { record_match = -4; /* Dummy error status value */ } } /* ** If either record showed a mismatch, then close ** (de-access) file, deassign channel and signal error. */ if (record_match != SS$_NORMAL) { sys$qiow ( 0, /* No EFN */ device_channel, IO$_DEACCESS, /* Function is deaccess file */ 0, /* No IOSB, AST, P1-P6 */ 0, 0, 0, 0, 0, 0, 0, 0); sys$dassgn (device_channel); /* Deassign device channel */ lib$signal(record_match); /* Signal error */ } /* Both records OK. Write them back in reverse order. */ status = sys$qiow ( /* Write new even record */ 0, /* no EFN */ device_channel, /* Channel */ IO$_WRITEVBLK, /* func = write virtual block */ &io_status, /* IOSB */ 0, /* No AST completion routine */ 0, /* or parameters */ &block_buf.buf1[0], /* P1 = address of buffer */ 512, /* P2 = length of buffer */ record_count + 1, /* P3 = Record number */ 0, /* No P4-P6 */ 0, 0); check_s(status) /* make sure write OK */ status = sys$qiow ( /* Write new odd record */ 0, /* no EFN */ device_channel, /* Channel */ IO$_WRITEVBLK, /* func = write virtual block */ &io_status, /* IOSB */ 0, /* No AST completion routine */ 0, /* or parameters */ &block_buf.buf2[0], /* P1 = address of buffer */ 512, /* P2 = length of buffer */ record_count, /* P3 = Record number */ 0, /* No P4-P6 */ 0, 0); check_s(status) /* make sure write OK */ } /* ** All records have now been swapped pairwise. Close file ** using $QIO with IO$_DEACCESS and deassign the channel. ** Note that in this case we don't save the status return ** code. */ sys$qiow ( 0, /* No EFN */ device_channel, IO$_DEACCESS, /* Function is deaccess file */ 0, /* No IOSB, AST, P1-P6 */ 0, 0, 0, 0, 0, 0, 0, 0); sys$dassgn (device_channel); /* Deassign the device channel */ /* ** Return appropriate status */ return status; }