1: /*
2: *
3: * Title:
4: * Backup
5: *
6: * Decription:
7: * Program to read VMS backup tape
8: *
9: * Author:
10: * John Douglas CAREY.
11: * Sven-Ove Westberg (version 3.0)
12: *
13: * Net-addess:
14: * john%monu1.oz@seismo.ARPA
15: * luthcad!sow@enea.UUCP
16: *
17: * History:
18: * Version 1.0 - September 1984
19: * Can only read variable length records
20: * Version 1.1
21: * Cleaned up the program from the original hack
22: * Can now read stream files
23: * Version 1.2
24: * Now convert filename from VMS to UNIX
25: * and creates sub-directories
26: * Version 1.3
27: * Works on the Pyramid if SWAP is defined
28: * Version 1.4
29: * Reads files spanning multiple tape blocks
30: * Version 1.5
31: * Always reset reclen = 0 on file open
32: * Now output fixed length records
33: *
34: * Version 2.0 - July 1985
35: * VMS Version 4.0 causes a rethink !!
36: * Now use mtio operations instead of opening and closing file
37: * Blocksize now grabed from the label
38: *
39: * Version 2.1 - September 1985
40: * Handle variable length records of zero length.
41: *
42: * Version 2.2 - July 1986
43: * Handle FORTRAN records of zero length.
44: * Inserted exit(0) at end of program.
45: * Distributed program in aus.sources
46: *
47: * Version 2.3 - August 1986
48: * Handle FORTRAN records with record length fields
49: * at the end of a block
50: * Put debug output to a file.
51: * Distributed program in net.sources
52: *
53: * Version 3.0 - December 1986
54: * Handle multiple saveset
55: * Remote tape
56: * Interactive mode
57: * File name selection with meta-characters
58: * Convert ; to : in VMS filenames
59: * Flag for usage of VMS directory structure
60: * Flag for "useless" files eg. *.exe
61: * Flag for use VMS version in file names
62: * Flag for verbose mode
63: * Flag to list the contents of the tape
64: * Distributed to mod.sources
65: *
66: *
67: * Installation:
68: *
69: * Computer Centre
70: * Monash University
71: * Wellington Road
72: * Clayton
73: * Victoria 3168
74: * AUSTRALIA
75: *
76: */
77: #include <stdio.h>
78: #include <ctype.h>
79:
80: #include <sys/ioctl.h>
81: #include <sys/types.h>
82: #ifdef REMOTE
83: #include <local/rmt.h>
84: #include <sys/stat.h>
85: #endif
86: #include <sys/mtio.h>
87: #include <sys/file.h>
88:
89: #ifdef pyr
90: #define SWAP
91: #endif pyr
92:
93: #ifdef sun
94: #define SWAP
95: #endif
96:
97: #ifdef pdp11
98: #define bbh_dol_w_size _bdwopsize
99: #define bbh_dol_w_opsys _bdwopsys
100: #define bbh_dol_w_subsys _bdwsubsys
101: #define bbh_dol_w_applic _bdwapplic
102: #define bbh_dol_l_number _bdlnumber
103: #define bbh_dol_t_spare_1 _bdtsp1
104: #define bbh_dol_w_struclev _bdwstruclev
105: #define bbh_dol_w_volnum _bdwvolnum
106: #define bbh_dol_l_crc _bdlcrc
107: #define bbh_dol_l_blocksize _bdlblocksize
108: #define bbh_dol_l_flags _bdlflags
109: #define bbh_dol_t_ssname _bdtssname
110: #define bbh_dol_w_fid _bdwfid
111: #define bbh_dol_w_did _bdwdid
112: #define bbh_dol_t_filename _bdtfilename
113: #define bbh_dol_b_rtype _bdbrtype
114: #define bbh_dol_b_rattrib _bdbrattrib
115: #define bbh_dol_w_rsize _bdwrsize
116: #define bbh_dol_b_bktsize _bdbbktsize
117: #define bbh_dol_b_vfcsize _bdbvfcsize
118: #define bbh_dol_w_maxrec _bdwmaxrec
119: #define bbh_dol_l_filesize _bdlfilesize
120: #define bbh_dol_t_spare_2 _bdtsp2
121: #define bbh_dol_w_checksum _bdwchecksum
122:
123: #define brh_dol_w_rsize _bhdwrsize
124: #define brh_dol_w_rtype _bhwrtyppe
125: #define brh_dol_l_flags _bhlflags
126: #define brh_dol_l_address _bhladdress
127: #define brh_dol_l_spare _bhlspare
128:
129: #define bsa_dol_w_size _bawsize
130: #define bsa_dol_w_type _bawtype
131: #define bsa_dol_t_text _battext
132:
133: #define process_file __procf
134: #define process_vbn __procv
135: #endif
136:
137: struct bbh {
138: short bbh_dol_w_size;
139: short bbh_dol_w_opsys;
140: short bbh_dol_w_subsys;
141: short bbh_dol_w_applic;
142: long bbh_dol_l_number;
143: char bbh_dol_t_spare_1[20];
144: short bbh_dol_w_struclev;
145: short bbh_dol_w_volnum;
146: long bbh_dol_l_crc;
147: long bbh_dol_l_blocksize;
148: long bbh_dol_l_flags;
149: char bbh_dol_t_ssname[32];
150: short bbh_dol_w_fid[3];
151: short bbh_dol_w_did[3];
152: char bbh_dol_t_filename[128];
153: char bbh_dol_b_rtype;
154: char bbh_dol_b_rattrib;
155: short bbh_dol_w_rsize;
156: char bbh_dol_b_bktsize;
157: char bbh_dol_b_vfcsize;
158: short bbh_dol_w_maxrec;
159: long bbh_dol_l_filesize;
160: char bbh_dol_t_spare_2[22];
161: short bbh_dol_w_checksum;
162: } *;
163:
164: struct brh {
165: short brh_dol_w_rsize;
166: short brh_dol_w_rtype;
167: long brh_dol_l_flags;
168: long brh_dol_l_address;
169: long brh_dol_l_spare;
170: } *;
171:
172: /* define record types */
173:
174: #define brh_dol_k_null 0
175: #define brh_dol_k_summary 1
176: #define brh_dol_k_volume 2
177: #define brh_dol_k_file 3
178: #define brh_dol_k_vbn 4
179: #define brh_dol_k_physvol 5
180: #define brh_dol_k_lbn 6
181: #define brh_dol_k_fid 7
182:
183: struct bsa {
184: short bsa_dol_w_size;
185: short bsa_dol_w_type;
186: char bsa_dol_t_text[1];
187: } *data_item;
188:
189: #ifdef STREAM
190: char *def_tapefile = "/dev/rts8";
191: #else
192: char *def_tapefile = "/dev/rmt8";
193: #endif
194: char *tapefile;
195:
196: char filename[128];
197: int filesize;
198:
199: char recfmt; /* record format */
200:
201: #define FAB_dol_C_UDF 0 /* undefined */
202: #define FAB_dol_C_FIX 1 /* fixed-length record */
203: #define FAB_dol_C_VAR 2 /* variable-length record */
204: #define FAB_dol_C_VFC 3 /* variable-length with fixed-length control record */
205: #define FAB_dol_C_STM 4 /* RMS-11 stream record (valid only for sequential org) */
206: #define FAB_dol_C_STMLF 5 /* stream record delimited by LF (sequential org only) */
207: #define FAB_dol_C_STMCR 6 /* stream record delimited by CR (sequential org only) */
208: #define FAB_dol_C_MAXRFM 6 /* maximum rfm supported */
209:
210: char recatt; /* record attributes */
211:
212: #define FAB_dol_V_FTN 0 /* FORTRAN carriage control character */
213: #define FAB_dol_V_CR 1 /* line feed - record -carriage return */
214: #define FAB_dol_V_PRN 2 /* print-file carriage control */
215: #define FAB_dol_V_BLK 3 /* records don't cross block boundaries */
216:
217: #define FANO 20
218:
219: #ifdef pyr
220: static struct bsa *file_table[FANO];
221: #else
222: struct bsa *file_table[FANO];
223: #endif
224:
225: FILE *f = NULL;
226: int file_count;
227: short reclen;
228: short fix;
229: short recsize;
230: int vfcsize;
231:
232: #ifdef NEWD
233: FILE *lf;
234: #endif NEWD
235:
236: int fd; /* tape file descriptor */
237: int cflag, dflag, eflag, iflag, sflag, tflag, vflag, wflag, xflag, nflag;
238: int setnr;
239: char **gargv;
240: int goptind, gargc;
241:
242: #define LABEL_SIZE 80
243: char label[LABEL_SIZE];
244:
245: char *block;
246: int blocksize;
247:
248: struct mtop op;
249:
250: FILE *
251: openfile(fn)
252: char *fn;
253: {
254: char ufn[256];
255: char ans[80];
256: char *p, *q, s, *ext;
257: int procf;
258:
259: procf = 1;
260: /* copy fn to ufn and convert to lower case */
261: p = fn;
262: q = ufn;
263: while (*p) {
264: if (isupper(*p))
265: *q = *p - 'A' + 'a';
266: else
267: *q = *p;
268: p++;
269: q++;
270: }
271: *q = '\0';
272:
273: /* convert the VMS to UNIX and make the directory path */
274: p = ufn;
275: q = ++p;
276: while (*q) {
277: if (*q == '.' || *q == ']') {
278: s = *q;
279: *q = '\0';
280: if(procf && dflag) mkdir(p, 0777);
281: *q = '/';
282: if (s == ']')
283: break;
284: }
285: *q++;
286: }
287: *q++;
288: if(!dflag) p=q;
289: /* strip off the version number */
290: while (*q && *q != ';') {
291: if( *q == '.') ext = q;
292: q++;
293: }
294: if (cflag) {
295: *q = ':';
296: }
297: else {
298: *q = '\0';
299: }
300: if(!eflag && procf) procf = typecmp(++ext);
301: if(procf && wflag) {
302: printf("extract %s [ny]",filename);
303: fflush(stdout);
304: gets(ans);
305: if(*ans != 'y') procf = NULL;
306: }
307: if(procf)
308: /* open the file for writing */
309: return(fopen(p, "w"));
310: else
311: return(NULL);
312: }
313:
314: typecmp(str) /* Compare the filename type in str with our list
315: of file type to be ignored. Return 0 if the
316: file is to be ignored, return 1 if the
317: file is not in our list and should not be ignored. */
318: register char *str;
319: {
320: static char *type[] = {
321: "exe", /* vms executable image */
322: "lib", /* vms object library */
323: "obj", /* rsx object file */
324: "odl", /* rsx overlay description file */
325: "olb", /* rsx object library */
326: "pmd", /* rsx post mortem dump */
327: "stb", /* rsx symbol table */
328: "sys", /* rsx bootable system image */
329: "tsk", /* rsx executable image */
330: "dir",
331: "upd",
332: "tlo",
333: "tlb",
334: "" /* null string terminates list */
335: };
336: register int i;
337:
338: i = -1;
339: while (*type[++i])
340: if (strncmp(str, type[i],3) == 0)
341: return(0); /* found a match, file to be ignored */
342: return(1); /* no match found */
343: }
344:
345: process_file(buffer)
346: char *buffer;
347: {
348: int i, n;
349: char *p, *q;
350: short dsize, nblk, lnch;
351:
352: int c;
353: short *s;
354:
355: int procf;
356:
357: s = (short *) buffer;
358:
359: /* check the header word */
360: if (*s != 257) {
361: printf("Snark: invalid data header\n");
362: exit(1);
363: }
364:
365: c = 2;
366: for (i = 0; i < FANO; i++) {
367: file_table[i] = (struct bsa *) &buffer[c];
368: #ifndef SWAP
369: dsize = file_table[i]->bsa_dol_w_size;
370: #else
371: swap(&file_table[i]->bsa_dol_w_size, &dsize, sizeof(short));
372: #endif
373: c += dsize + 4;
374: }
375:
376: /* extract file name */
377: #ifndef SWAP
378: dsize = file_table[0]->bsa_dol_w_size;
379: #else
380: swap(&file_table[0]->bsa_dol_w_size, &dsize, sizeof(short));
381: #endif
382: p = file_table[0]->bsa_dol_t_text;
383: q = filename;
384: for (i = 0; i < dsize; i++)
385: *q++ = *p++;
386: *q = '\0';
387:
388: /* extract file's record attributes */
389: #ifndef SWAP
390: dsize = file_table[5]->bsa_dol_w_size;
391: #else
392: swap(&file_table[5]->bsa_dol_w_size, &dsize, sizeof(short));
393: #endif
394: p = file_table[5]->bsa_dol_t_text;
395: recfmt = p[0];
396: recatt = p[1];
397: #ifndef SWAP
398: bcopy(&p[2], &recsize, sizeof(short));
399: #else
400: swap(&p[2], &recsize, sizeof(short));
401: #endif
402: vfcsize = p[15];
403: if (vfcsize == 0)
404: vfcsize = 2;
405: #ifdef DEBUG
406: printf("recfmt = %d\n", recfmt);
407: printf("recatt = %d\n", recatt);
408: printf("reclen = %d\n", recsize);
409: printf("vfcsize = %d\n", vfcsize);
410: #endif
411: #ifndef SWAP
412: bcopy(&p[10], &nblk, sizeof(short));
413: bcopy(&p[12], &lnch, sizeof(short));
414: #else
415: swap(&p[10], &nblk, sizeof(short));
416: swap(&p[12], &lnch, sizeof(short));
417: #endif
418: filesize = (nblk-1)*512 + lnch;
419: #ifdef DEBUG
420: printf("nbk = %d, lnch = %d\n", nblk, lnch);
421: printf("filesize = 0x%x\n", filesize);
422: #endif
423:
424: /* open the file */
425: if (f != NULL) {
426: fclose(f);
427: file_count = 0;
428: reclen = 0;
429: }
430: procf = 0;
431: if (goptind < gargc)
432: for(i=goptind; i < gargc; i++) {
433: procf |= match(filename,gargv[i]);
434: }
435: else
436: procf = 1;
437: if (tflag && procf)
438: printf( " %-35s %8d \n",filename,filesize);
439: if (xflag && procf) {
440: /* open file */
441: f = openfile(filename);
442: if(f != NULL && vflag) printf("extracting %s\n", filename);
443: }
444: }
445: /*
446: *
447: * process a virtual block record (file record)
448: *
449: */
450: process_vbn(buffer, rsize)
451: char *buffer;
452: unsigned short rsize;
453: {
454: int c, i;
455:
456: if (f == NULL) {
457: return;
458: }
459: i = 0;
460: while (file_count+i < filesize && i < rsize) {
461: switch (recfmt) {
462: case FAB_dol_C_FIX:
463: if (reclen == 0) {
464: reclen = recsize;
465: }
466: fputc(buffer[i], f);
467: i++;
468: reclen--;
469: break;
470:
471: case FAB_dol_C_VAR:
472: case FAB_dol_C_VFC:
473: if (reclen == 0) {
474: reclen = *((short *) &buffer[i]);
475: #ifdef SWAP
476: swap(&reclen, &reclen, sizeof(short));
477: #endif
478: #ifdef NEWD
479: fprintf(lf, "---\n");
480: fprintf(lf, "reclen = %d\n", reclen);
481: fprintf(lf, "i = %d\n", i);
482: fprintf(lf, "rsize = %d\n", rsize);
483: #endif NEWD
484: fix = reclen;
485: i += 2;
486: if (recfmt == FAB_dol_C_VFC) {
487: i += vfcsize;
488: reclen -= vfcsize;
489: }
490: } else if (reclen == fix
491: && recatt == (1 << FAB_dol_V_FTN)) {
492: /****
493: if (buffer[i] == '0')
494: fputc('\n', f);
495: else if (buffer[i] == '1')
496: fputc('\f', f);
497: *** sow ***/
498: fputc(buffer[i],f); /** sow **/
499: i++;
500: reclen--;
501: } else {
502: fputc(buffer[i], f);
503: i++;
504: reclen--;
505: }
506: if (reclen == 0) {
507: if (!nflag)
508: fputc('\n', f);
509: if (i & 1)
510: i++;
511: }
512: break;
513:
514: case FAB_dol_C_STM:
515: case FAB_dol_C_STMLF:
516: if (reclen < 0) {
517: printf("SCREAM\n");
518: }
519: if (reclen == 0) {
520: reclen = 512;
521: }
522: c = buffer[i++];
523: reclen--;
524: if (c == '\n') {
525: reclen = 0;
526: }
527: fputc(c, f);
528: break;
529:
530: case FAB_dol_C_STMCR:
531: c = buffer[i++];
532: if (c == '\r')
533: fputc('\n', f);
534: else
535: fputc(c, f);
536: break;
537:
538: default:
539: fclose(f);
540: unlink(filename);
541: fprintf(stderr, "Invalid record format = %d\n", recfmt);
542: return;
543: }
544: }
545: file_count += i;
546: }
547: #ifdef SWAP
548: /*
549: *
550: * do swapping for Motorola type architectures
551: *
552: */
553: swap(from, to, nbytes)
554: char *from, *to;
555: int nbytes;
556: {
557: int i, j;
558: char temp[100];
559:
560: for (i = 0; i < nbytes; i++)
561: temp[i] = from[i];
562: for (i = 0, j = nbytes-1; i < nbytes; i++, j--)
563: to[i] = temp[j];
564: }
565: #endif
566: /*
567: *
568: * process a backup block
569: *
570: */
571: process_block(block, blocksize)
572: char *block;
573: int blocksize;
574: {
575:
576: unsigned short bhsize, rsize, rtype;
577: u_long bsize, i;
578:
579: i = 0;
580:
581: /* read the backup block header */
582: block_header = (struct bbh *) &block[i];
583: i += sizeof(struct bbh);
584:
585: bhsize = block_header->bbh_dol_w_size;
586: bsize = block_header->bbh_dol_l_blocksize;
587: #ifdef SWAP
588: swap(&bhsize, &bhsize, sizeof(short));
589: swap(&bsize, &bsize, sizeof(long));
590: #endif
591:
592: /* check the validity of the header block */
593: if (bhsize != sizeof(struct bbh)) {
594: fprintf(stderr, "Snark: Invalid header block size\n");
595: exit(1);
596: }
597: if (bsize != 0 && bsize != blocksize) {
598: fprintf(stderr, "Snark: Invalid block size\n");
599: exit(1);
600: }
601: #ifdef DEBUG
602: printf("new block: i = %d, bsize = %d\n", i, bsize);
603: #endif
604:
605: /* read the records */
606: while (i < bsize) {
607: /* read the backup record header */
608: record_header = (struct brh *) &block[i];
609: i += sizeof(struct brh);
610:
611: rtype = record_header->brh_dol_w_rtype;
612: rsize = record_header->brh_dol_w_rsize;
613: #ifdef SWAP
614: swap(&rtype, &rtype, sizeof(short));
615: swap(&rsize, &rsize, sizeof(short));
616: #endif
617: #ifdef DEBUG
618: printf("rtype = %d\n", rtype);
619: printf("rsize = %d\n", rsize);
620: printf("flags = 0x%x\n", record_header->brh_dol_l_flags);
621: printf("addr = 0x%x\n", record_header->brh_dol_l_address);
622: printf("i = %d\n", i);
623: #endif
624:
625: switch (rtype) {
626:
627: case brh_dol_k_null:
628: #ifdef DEBUG
629: printf("rtype = null\n");
630: #endif
631: break;
632:
633: case brh_dol_k_summary:
634: #ifdef DEBUG
635: printf("rtype = summary\n");
636: #endif
637: break;
638:
639: case brh_dol_k_file:
640: #ifdef DEBUG
641: printf("rtype = file\n");
642: #endif
643: process_file(&block[i]);
644: break;
645:
646: case brh_dol_k_vbn:
647: #ifdef DEBUG
648: printf("rtype = vbn\n");
649: #endif
650: process_vbn(&block[i], rsize);
651: break;
652:
653: case brh_dol_k_physvol:
654: #ifdef DEBUG
655: printf("rtype = physvol\n");
656: #endif
657: break;
658:
659: case brh_dol_k_lbn:
660: #ifdef DEBUG
661: printf("rtype = lbn\n");
662: #endif
663: break;
664:
665: case brh_dol_k_fid:
666: #ifdef DEBUG
667: printf("rtype = fid\n");
668: #endif
669: break;
670:
671: default:
672: fprintf(stderr, " Snark: invalid record type\n");
673: fprintf(stderr, " record type = %d\n", rtype);
674: exit(1);
675: }
676: #ifdef pyr
677: i = i + rsize;
678: #else
679: i += rsize;
680: #endif
681: }
682: }
683:
684: rdhead()
685: {
686: int i, nfound;
687: char name[80];
688: nfound = 1;
689: /* read the tape label - 4 records of 80 bytes */
690: while ((i = read(fd, label, LABEL_SIZE)) != 0) {
691: if (i != LABEL_SIZE) {
692: fprintf(stderr, "Snark: bad label record\n");
693: exit(1);
694: }
695: if (strncmp(label, "VOL1",4) == 0) {
696: sscanf(label+4, "%14s", name);
697: if(vflag || tflag) printf("Volume: %s\n",name);
698: }
699: if (strncmp(label, "HDR1",4) == 0) {
700: sscanf(label+4, "%14s", name);
701: sscanf(label+31, "%4d", &setnr);
702: }
703: /* get the block size */
704: if (strncmp(label, "HDR2", 4) == 0) {
705: nfound = 0;
706: sscanf(label+5, "%5d", &blocksize);
707: #ifdef DEBUG
708: printf("blocksize = %d\n", blocksize);
709: #endif
710: }
711: }
712: if((vflag || tflag) && !nfound)
713: printf("Saveset name: %s number: %d\n",name,setnr);
714: /* get the block buffer */
715: block = (char *) malloc(blocksize);
716: if (block == (char *) 0) {
717: fprintf(stderr, "memory allocation for block failed\n");
718: exit(1);
719: }
720: return(nfound);
721: }
722:
723: rdtail()
724: {
725: int i;
726: char name[80];
727: /* read the tape label - 4 records of 80 bytes */
728: while ((i = read(fd, label, LABEL_SIZE)) != 0) {
729: if (i != LABEL_SIZE) {
730: fprintf(stderr, "Snark: bad label record\n");
731: exit(1);
732: }
733: if (strncmp(label, "EOF1",4) == 0) {
734: sscanf(label+4, "%14s", name);
735: if(vflag || tflag)
736: printf("End of saveset: %s\n\n\n",name);
737: }
738: }
739: }
740:
741: usage(progname)
742: char *progname;
743: {
744: fprintf(stderr,
745: "Usage: %s -{tx}[cdenvw][-s setnumber][-f tapefile]\n",progname);
746: }
747: main(argc, argv)
748: int argc;
749: char *argv[];
750: {
751:
752: char *progname;
753: int c, i, eoffl;
754: int selset;
755: extern int optind;
756: extern char *optarg;
757:
758: progname = argv[0];
759: if(argc < 2){
760: usage(progname);
761: exit(1);
762: }
763: gargv = argv;
764: gargc = argc;
765: tapefile = def_tapefile;
766: cflag=dflag=eflag=iflag=nflag=sflag=tflag=vflag=wflag=xflag=0;
767: while((c=getopt(argc,argv,"cdef:ins:tvwx")) != EOF)
768: switch(c){
769: case 'c':
770: cflag++;
771: break;
772: case 'd':
773: dflag++;
774: break;
775: case 'e':
776: eflag++;
777: break;
778: case 'f':
779: tapefile = optarg;
780: break;
781: case 'i':
782: iflag++;
783: break;
784: case 'n':
785: nflag++;
786: break;
787: case 's':
788: sflag++;
789: sscanf(optarg,"%d",&selset);
790: break;
791: case 't':
792: tflag++;
793: break;
794: case 'v':
795: vflag++;
796: break;
797: case 'w':
798: wflag++;
799: break;
800: case 'x':
801: xflag++;
802: break;
803: case '?':
804: usage(progname);
805: exit(1);
806: break;
807: };
808: if(!tflag && !xflag) {
809: usage(progname);
810: exit(1);
811: }
812: goptind = optind;
813:
814: #ifdef NEWD
815: /* open debug file */
816: lf = fopen("log", "w");
817: if (lf == NULL) {
818: perror("log");
819: exit(1);
820: }
821: #endif
822:
823: /* open the tape file */
824: fd = open(tapefile, O_RDONLY);
825: if (fd < 0) {
826: perror(tapefile);
827: exit(1);
828: }
829:
830: /* rewind the tape */
831: op.mt_op = MTREW;
832: op.mt_count = 1;
833: i = ioctl(fd, MTIOCTOP, &op);
834: if (i < 0) {
835: perror(tapefile);
836: exit(1);
837: }
838:
839: eoffl = rdhead();
840: /* read the backup tape blocks until end of tape */
841: while (!eoffl) {
842: if(sflag && setnr != selset) {
843: op.mt_op = MTFSF;
844: op.mt_count = 1;
845: i = ioctl(fd, MTIOCTOP, &op);
846: if (i < 0) {
847: perror(tapefile);
848: exit(1);
849: }
850: i = 0;
851: }
852: else
853: i = read(fd, block, blocksize);
854: if(i == 0) {
855: rdtail();
856: eoffl=rdhead();
857: }
858: else if (i != blocksize) {
859: fprintf(stderr, "bad block read i = %d\n", i);
860: if (!iflag)
861: exit(1);
862: else if (f != NULL) {
863: printf("File '%s' truncated - bad blocks\n",
864: filename);
865: fclose(f);
866: f = NULL;
867: file_count = 0;
868: reclen = 0;
869: }
870: }
871: else{
872: eoffl = 0;
873: process_block(block, blocksize);
874: }
875: }
876: if(vflag || tflag) printf("End of tape\n");
877:
878: /* close the tape */
879: close(fd);
880:
881: #ifdef NEWD
882: /* close debug file */
883: fclose(lf);
884: #endif NEWD
885:
886: /* exit cleanly */
887: exit(0);
888: }