/* ** 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 MAGNETIC_TAPE "V1.0-001" #else #pragma module MAGNETIC_TAPE "V1.0-001" #endif /* **++ ** FACILITY: SYS$EXAMPLES ** ** MODULE DESCRIPTION: ** ** MAGNETIC_TAPE: This program exercises a magnetic tape unit ** using QIO calls to create and write a file and then to read it ** in reverse. See the description under main() for more details. ** ** AUTHORS: ** ** Digital Equipment Corporation ** ** CREATION DATE: 23 March 1993 ** ** PORTABILITY ISSUES: ** ** At this writing, the OpenVMS VAX File Information Block (FIB) ** definition header file differs slightly from the corresponding ** file for OpenVMS AXP. The OpenVMS AXP version uses variant ** union / structure definitions to allow unambiguous but brief ** access to inner members of the FIB while the OpenVMS VAX ** version requires fully qualified FIB structure member names. ** This program uses conditionally compiled definitions of ** certain referenced fields to accommodate both implementations. ** ** ** MODIFICATION HISTORY: ** ** 23-Mar-1993 Conversion from MAGNETIC_TAPE.MAR **-- */ /* ** ** INCLUDE FILES ** */ #include /* Descriptor Structure and Constant Definitions */ #include /* File Information Block Definitions */ #include /* I/O function code Definitions */ #include /* Library (LIB$) routine definitions */ #include /* System Service Return Status Value Definitions */ #include /* System routine definitions*/ #include /* String processing routine definitions*/ /* ** Number of records to be written/read (one for each letter of ** alphabet). */ #define NUM_RECS 26 /* ** The following macro defines a status check which ** occurs numerous times throughout the program. */ #define check_s(status) \ if ( ! (status & SS$_NORMAL) ) \ {\ lib$signal (status);\ } /* ** Define references to file information block (FIB) fields for the ** FIB used in this program. These differ slightly between OpenVMS ** VAX and OpenVMS AXP for members not at the top level in the FIB ** structure. Since we use both "standard" view of the FIB and the ** ancillary control process (ACP) view of the FIB, the definitions ** also account for the union / overlay of these two structures. */ #ifdef VAX /* Use full structure/union names on VAX */ #define f_i_b$l_cntrlval f_i_b.x.fib$r_cntrlval_overlay.fib$l_cntrlval #define f_i_b$l_acctl f_i_b.y.fib$r_acctl_overlay.fib$l_acctl #else /* Use tags otherwise */ #define f_i_b$l_cntrlval f_i_b.x.fib$l_cntrlval #define f_i_b$l_acctl f_i_b.y.fib$l_acctl #endif /* ** Data structures used by the program. */ /* ** Tape name & descriptor ** The logical name TAPE must be defined in order for this to work. */ $DESCRIPTOR (tape_name, "TAPE"); /* ** Tape channel number. */ unsigned long int tape_chan; /* ** I/O Status Block for magnetic tape. */ struct iosb { unsigned short int status, byte_count; /* status, #bytes transferred */ unsigned long int dev_depend; /* Device-dependent information */ } io_stat; /* ** Data Buffer (256 bytes). */ char buffer [256]; /* ** File name string */ char file_name[] = {"MYDATA.DAT;1"}; /* ** File Information Block. The FIB will be shared between the this ** program and the system, and used both to specify access type and ** to specify ACP control functions. Hence, we specify a union which ** has both types. */ union { struct fibdef1 x; /* ACP control version */ struct fibdef y; /* Basic (all other uses) version */ } f_i_b; /* ** File Information Block Descriptor. ** */ struct { unsigned short int length, pad; struct fibdef * addr; } fib_descr = { FIB$K_LENGTH, 0, (struct fibdef *) &f_i_b.x }; /* ** File name & descriptor. ** Use standard string descriptor; type & class will be ignored anyway. */ $DESCRIPTOR (name_descr, file_name); /* ** Record match check. In this example, record_match will be set to ** -4 if a record read back has the wrong length, and set to -6 if ** the record read back has the right length but does not match what ** should have been written. More typically, error codes would be ** more formally defined and assigned using the MESSAGE utility. ** */ long int record_match = SS$_NORMAL; /* ** General purpose status check. In the spirit of optimism, we ** initialize it to "normal successful completion". */ long int status = SS$_NORMAL; /* **++ ** FUNCTIONAL DESCRIPTION: ** ** The program first assigns a channel to the magnetic tape ** unit and then performs an access function to create and ** access a file called MYDATA.DAT. Next, the program writes ** 26 blocks of data (the letters of the alphabet) to the tape. ** The first block contains all A's, the next, all B's, and so ** forth. The program starts by writing a block of 256 bytes, ** that is, the block of A's. Each subsequent block is reduced ** in size by two bytes so that by the time the block of Z's is ** written, the size is only 206 bytes. The magtape ACP does ** not allow the reading of a file that has been written until ** one of three events occurs: ** 1. The file is deaccessed. ** 2. The file is rewound. ** 3. The file is backspaced. ** In this example the file is backspaced zero blocks and then ** read in reverse (incrementing the block size every block); ** the data is checked against the data that is supposed to be ** there. If no data errors are detected, the file is ** deaccessed and the program exits. ** ** FORMAL PARAMETERS: ** ** None ** ** RETURN VALUE: ** ** SS$_NORMAL unless: ** ** an I/O error occurred, in which case the error returned by ** the QIO or ASSIGN or in the IOSB is returned. ** or ** there was a record mismatch; see error codes below. ** ** SIDE EFFECTS: ** ** The tape in the drive identified by the logical "TAPE" is ** written as described above. ** ** PRECONDITIONS: ** ** A tape must be mounted in the drive identified by the logical ** "TAPE" prior to running this program. The tape must be capable ** of reverse reads. ** ** ** ERROR CODES: ** ** -4: Record length was incorrect. ** -6: Contents of record were incorrect. ** ** **-- */ main () { char init_letter = 'A'; /* Initial letter fill */ int rec_cnt; /* Record count/number */ int buf_len; /* Buffer length */ int byte_off; /* Byte offset within record */ /* ** initialize structures as and if necessary */ /* ** Initialize File Information Block for File create. ** This will be zero except for access control; access will be ** write(=>read), with no other writers allowed. */ memset ((void *) fib_descr.addr, 0, fib_descr.length); f_i_b$l_acctl = FIB$M_NOWRITE|FIB$M_WRITE; /* ** Assign channel for tape unit */ status = sys$assign ( &tape_name, /* Tape Device Descriptor */ &tape_chan, /* Tape Channel Number */ 0, /* Default access mode */ 0); /* No mailbox */ check_s(status) /* Ensure assign went OK, exit if error */ /* ** Create tape file MYDATA.DAT using io$_create function, ** file info block, name descriptor. */ status = sys$qiow ( 0, /* Default event flag #, OK for synch I/O */ tape_chan, /* Tape channel */ IO$_CREATE|IO$M_ACCESS|IO$M_CREATE, /* Create, open file */ &io_stat, /* I/O Status Block */ 0, /* No AST completion routine */ 0, /* or parameters */ &fib_descr, /* P1 = address of FIB descriptor */ &name_descr, /* P2 = address of filename descriptor */ 0, /* No P3 -- don't want filename len back */ 0, /* No P4 -- don't want filename dsc back */ 0, /* No P5 -- don't want attribute dsc back */ 0); /* No P6 */ check_s(status) /* Make sure QIO service completed OK*/ check_s(io_stat.status) /* Make sure actual QIO completed OK */ /* ** Now go through the write loop. ** Write 256 A's in the first record, 254 B's in the second, and so on, ** finishing up with 206 Z's in the last (26th) record. */ for ( rec_cnt = 1; rec_cnt <= NUM_RECS; rec_cnt++) { buf_len = 256 - 2 * (rec_cnt - 1) ; /* 1st reclen=256, 2nd=254, ...*/ memset (&buffer[0], /* Fill buffer with A's the */ init_letter + rec_cnt - 1, /* first time, B's the second, */ buf_len); /* and so on ... */ /* ** Do the write using QIO write virtual block. */ status = sys$qiow ( 0, /* Default event flag */ tape_chan, /* Tape channel */ IO$_WRITEVBLK, /* Function code = write virt blk */ &io_stat, /* I/O status block */ 0, /* No AST routine */ 0, /* or parameters */ &buffer, /* P1 = buffer address */ buf_len, /* P2 = Byte count */ 0, 0, 0, 0); /* No P3-P6 */ check_s(status) /* Make sure service */ check_s(io_stat.status) /* and I/O completed OK */ } /* ** Set up FIB for a space zero blocks ACP function, which will ** allow read access. */ f_i_b.x.fib$w_cntrlfunc = FIB$C_SPACE; /* Set up FIB for space function, */ f_i_b$l_cntrlval = 0; /* and space zero blocks */ /* ** Do the space-zero QIO. */ status = sys$qiow ( 0, /* Default event flag */ tape_chan, /* Tape channel */ IO$_ACPCONTROL, /* Function = ACP control, no modifiers */ &io_stat, /* I/O status block */ 0, /* No AST routine */ 0, /* or parameters */ &fib_descr, /* P1 = Address of File Info Blk descr */ 0, 0, 0, 0, 0); /* P2-P6 not allowed for this */ check_s(status) /* Make sure service */ check_s(io_stat.status) /* and space-zero completed OK */ /* ** Now cycle back through the records. */ for (rec_cnt = NUM_RECS; rec_cnt >= 1; rec_cnt--) { buf_len = 256 - 2*(rec_cnt-1) ; /* Buffer length should be this */ /* ** Do the read using QIO read virtual block with the reverse modifier ** set. */ status = sys$qiow ( 0, /* Default event flag */ tape_chan, /* Tape Channel */ IO$_READVBLK| /* Function code = read virt blk */ IO$M_REVERSE, /* w/reverse modif */ &io_stat, /* I/O status block */ 0, /* no AST routine */ 0, /* or parameters */ &buffer, /* P1 = buffer address */ buf_len, /* P2 = byte count */ 0, 0, 0, 0); /* No P3-P6 */ check_s(status) /* Make sure service */ check_s(io_stat.status) /* And I/O operation were OK */ if (buf_len != io_stat.byte_count) /* Did we get right # */ { /* of bytes back? */ record_match = -4; /* No -- set error code */ check_s(record_match) /* and force error signal */ } else /* Length is OK. Make sure each */ { /* byte in the buffer is correct.*/ for (byte_off = 0; byte_off < buf_len; byte_off++) { if (buffer[byte_off] != init_letter + rec_cnt - 1 ) { record_match = -6; /* Found bad byte, so set err */ check_s(record_match) /* code and force err signal */ } } /* If we got this far, then the current record is OK */ } /* And if we got this far, then all the records are OK */ } /* ** All records have now been checked. Close the file using QIO ** with IO$_DEACCESS and deassign the magtape channel. In this ** particular case we don't bother to save the status return ** codes, although we would if we were doing further processing ** dependent upon the success of these actions. */ sys$qiow ( /* do the de-access */ 0, /* Default event flag */ tape_chan, /* Last use of tape channel */ IO$_DEACCESS, /* Function is deaccess file */ 0, 0, 0, 0, 0, 0, 0, 0, 0); /* No IOSB, AST, P1-P6 */ sys$dassgn ( tape_chan ); /* do the deassign */ /* ** All done -- return appropriate status */ return status; }