scpinker@pikes.Colorado.EDU (Scott C. Pinkerton) (08/24/90)
Hello, I have recently received a 9-Track tape from some people working on a VAX. I am using an HP9000/375 running HP-UX 7.0, with a HP7980XC. I've tried a few simple approaches to read the data (ascii) off of the tape - with little success. I have tried frecover, tar, and cpio to read the tape but no luck so far. The tape included a data sheet with the following info: Save set: GCU.SAV Written by: BASCHMITT UIC: [000135,000001] Date: 6-AUG-1990 08:41:49.32 Command: BACKUP/REWIND/LOG/IGNORE=LABEL filenames $7$MUA0:GCU.SAV Operating System: VAX/VMS Version V5.3 BACKUP Version: V5.3 CPU ID register: 05903B14 Node name: _FLA2B:: Written on: _$7$MUA0: Block Size: 8192 Group Size: 10 Buffer count: 16 Total of 2 files, 638 blocks End of save set Does this provide enough info to allow some knowledgable sole to help read this data ?? How are other people trading data between VAXes on 9-Track. If there is a better command to use on the VAX which allows easier reading on the HP side that's ok too. Does the VAX have an equivalent tar command ?? Thanks for any help! scott pinkerton scpinker@pikes.colorado.edu
milburn@me10.lbl.gov (John Milburn) (08/24/90)
In article <4180@pikes.Colorado.EDU> scpinker@pikes.Colorado.EDU (Scott C. Pinkerton) writes: >Hello, I have recently received a 9-Track tape from some people working on a >VAX. I am using an HP9000/375 running HP-UX 7.0, with a HP7980XC. I've tried >a few simple approaches to read the data (ascii) off of the tape - with little >success. I have tried frecover, tar, and cpio to read the tape but no luck >so far. The tape included a data sheet with the following info: [info deleted] >Does this provide enough info to allow some knowledgable sole to help read this >data ?? How are other people trading data between VAXes on 9-Track. If there >is a better command to use on the VAX which allows easier reading on the HP >side that's ok too. Does the VAX have an equivalent tar command ?? The tape was made using VMS' BACKUP utility. You have pretty much no hope of reading this with your hpux machine, as it contains all kinds of VMS specific file information. Since the data you wish to read is ascii, the sender needs to find some VMS utility to write an ascii tape. There is a utility called "twrite" on our VMS systems, which allows one to write either EBCDIC or ascii tapes. If you need to investigate this further, I can ask our VMS folks about how this utility can be obtained. -jem -- JEMilburn@lbl.gov ...!ucbvax!lbl.gov!JEMilburn
mark@comp.vuw.ac.nz (Mark Davies) (08/24/90)
In article <4180@pikes.Colorado.EDU> scpinker@pikes.Colorado.EDU (Scott C. Pinkerton) writes: >Hello, I have recently received a 9-Track tape from some people working on a >VAX. I am using an HP9000/375 running HP-UX 7.0, with a HP7980XC. I've tried >a few simple approaches to read the data (ascii) off of the tape - with little >success. I have tried frecover, tar, and cpio to read the tape but no luck >so far. The tape included a data sheet with the following info: >Save set: GCU.SAV >Command: BACKUP/REWIND/LOG/IGNORE=LABEL filenames $7$MUA0:GCU.SAV >Operating System: VAX/VMS Version V5.3 >Does this provide enough info to allow some knowledgable sole to help read this >data ?? Yes. The tape is written in VMS backup format. A program to read this format was posted to one of the sources groups a few years back. You could try uunet or some other archive site for it. If you have problems locating a copy get back to me and I'll dig around and try to find what we did with our copy. I've never actually built it on an HP-UX system but I wouldn't anticipate any problems. > How are other people trading data between VAXes on 9-Track. If there >is a better command to use on the VAX which allows easier reading on the HP >side that's ok too. Does the VAX have an equivalent tar command ?? There are several tar programs available for VMS but I don't know any details. Sorry, its been several years since we've had to ship files between VMS and UNIX boxes other than over the network. cheers mark
mat@emcard.UUCP (W Mat Waites) (08/25/90)
In article <4180@pikes.Colorado.EDU> scpinker@pikes.Colorado.EDU (Scott C. Pinkerton) writes: >Hello, I have recently received a 9-Track tape from some people working on a >VAX. I am using an HP9000/375 running HP-UX 7.0, with a HP7980XC. I've tried >a few simple approaches to read the data (ascii) off of the tape - with little >success. I have tried frecover, tar, and cpio to read the tape but no luck >so far. ... >Does this provide enough info to allow some knowledgable sole to help read this >data ?? How are other people trading data between VAXes on 9-Track. If there >is a better command to use on the VAX which allows easier reading on the HP >side that's ok too. Does the VAX have an equivalent tar command ?? >scott pinkerton >scpinker@pikes.colorado.edu Here is a package called vmstape. We have successfully used it to load VMS "backup" tapes. Sorry if this is too long a posting, but I thought others might be interested. We're using it on a 9000/835. Enjoy, Mat #!/bin/sh # shar: Shell Archiver (v1.22) # # Run the following text with /bin/sh to create: # Makefile # READ_ME # field.c # header3.c # skiptm.c # vmstape.c # vt_append.c # vt_extract.c # vt_list.c # vt_write.c # vmstape.h # vmstape.1 # echo "x - extracting Makefile (Text)" sed 's/^X//' << 'SHAR_EOF' > Makefile && XCFILES = field.c header3.c skiptm.c vmstape.c vt_append.c \ X vt_extract.c vt_list.c vt_write.c X XOBJECTS = field.o header3.o skiptm.o vmstape.o vt_append.o \ X vt_extract.o vt_list.o vt_write.o X XCMD = vmstape XDESTIN = /usr/local/bin XCFLAGS = -O $(DEFS) XDEFS = -DNEWDIR X# to compile for ver 4.2 BSD OR any version that CANNOT emulate X# the 4.2 directory structure with the files ndir.h and libndir.a: X# define in DEFS '-Dnewdir'. X# do not define '-lndir' in LIBS. X# to compile for any version of unix WITH the 4.2 directory EMULATION package: X# do not define '-Dnewdir' in DEFS. X# define '-lndir' in LIBS. XLIBS = XOWNER = XMODE = 0755 X X$(CMD) : $(OBJECTS) /lib/libc.a X ld /lib/crt0.o $(OBJECTS) -o $(CMD) $(LIBS) -lc X X# ld /lib/crt0.o $(OBJECTS) -o $(CMD) $(LIBS) -lc -lg X Xinstall : $(DESTIN)/$(CMD) X X$(DESTIN)/$(CMD) : $(CMD) X cp $(CMD) $(DESTIN)/$(CMD) X strip $(DESTIN)/$(CMD) X chmod $(MODE) $(DESTIN)/$(CMD) X Xdepend : X @makedepend $(CFILES) X Xclean : X -rm $(OBJECTS) $(CMD) X Xprint : X print -h $(CFILES) X Xlint : X lint -h $(CFILES) $(DEFS) X X# DO NOT DELETE THIS LINE -- make depends on it X Xheader3.o : vmstape.h X Xskiptm.o : /usr/include/sys/types.h /usr/include/sys/mtio.h \ X vmstape.h /usr/include/sys/ioctl.h X Xvmstape.o : /usr/include/stdio.h vmstape.h X Xvt_append.o : /usr/include/stdio.h /usr/include/sys/types.h \ X /usr/include/sys/stat.h /usr/include/sys/mtio.h vmstape.h \ X /usr/include/sys/ioctl.h X Xvt_extract.o : /usr/include/stdio.h /usr/include/ctype.h \ X /usr/include/sys/types.h /usr/include/sys/stat.h vmstape.h X Xvt_list.o : vmstape.h X Xvt_write.o : /usr/include/stdio.h /usr/include/sys/types.h \ X /usr/include/sys/stat.h /usr/include/sys/dir.h vmstape.h SHAR_EOF chmod 0664 Makefile || echo "restore of Makefile fails" echo "x - extracting READ_ME (Text)" sed 's/^X//' << 'SHAR_EOF' > READ_ME && XBy default, the makefile will compile the vmstape assuming X the new 4.2 directory structure emulation by libndir.a and ndir.h. X XIf these files do not exist, the makefile must be changed (as noted in the file) X to add and remove definitions for the variables DEFS and LIBS. X X XAuthors: Glen Dudek and Steve Kaufer X Harvard University Science Center SHAR_EOF chmod 0664 READ_ME || echo "restore of READ_ME fails" echo "x - extracting field.c (Text)" sed 's/^X//' << 'SHAR_EOF' > field.c && X#define SIZE 80 X Xchar * Xfield(p, begin, end) X char *p; X int begin; X int end; X{ X register int i; X register int j; X static char buffer[SIZE]; X X j = 0; X for(i = begin ; i <= end ; i++) X buffer[j++] = p[i]; X buffer[j] = '\0'; X X if( j >= SIZE ) X printf("field: buffer overflow. j = %d\n", j); X X return(buffer); X} SHAR_EOF chmod 0664 field.c || echo "restore of field.c fails" echo "x - extracting header3.c (Text)" sed 's/^X//' << 'SHAR_EOF' > header3.c && X#include "vmstape.h" X X/* these functions really simulate r_record, but with these names X * make reading the code easier X */ X X/* is there a Header 3 area on the tape? X */ X Xheader3() X{ X char buf[RECSIZE]; X X if( read(magtape, buf, RECSIZE) != 0 ) X return(1); X return(0); X} X Xtrailer3() X{ X char buf[RECSIZE]; X X if( read(magtape, buf, RECSIZE) != 0 ) X return(1); X return(0); X} SHAR_EOF chmod 0664 header3.c || echo "restore of header3.c fails" echo "x - extracting skiptm.c (Text)" sed 's/^X//' << 'SHAR_EOF' > skiptm.c && X#include <sys/types.h> X#include <sys/mtio.h> X#include <sys/ioctl.h> X#include "vmstape.h" X Xskiptm(n) X int n; X{ X struct mtop m; X X m.mt_count = n; X m.mt_op = MTFSF; X X ioctl(magtape, MTIOCTOP, &m); X} Xw_tapemark() X{ X struct mtop m; X X m.mt_count = 1; X m.mt_op = MTWEOF; X X ioctl(magtape, MTIOCTOP, &m); X} SHAR_EOF chmod 0664 skiptm.c || echo "restore of skiptm.c fails" echo "x - extracting vmstape.c (Text)" sed 's/^X//' << 'SHAR_EOF' > vmstape.c && X#include <stdio.h> X#include "vmstape.h" X X/* NOTE -- FILES MUST BE TEXTUAL (NOT CONTAINING A NULL BYTE) */ X X/* arguments to open system call X */ X#define READ 0 X#define WRITE 1 X#define RDWT 2 X X#define CREATE 1 X#define LIST 2 X#define EXTRACT 3 X#define APPEND 4 X Xint open_file(); X Xmain(argc, argv) X int argc; X char **argv; X{ X register char *p; X int function; X int func_count; X int mode; X char device[100]; X X if(argc < 2) { X fprintf(stderr, "Usage (H = HELP): vmstape [crtxvfdbFRVH] [ filename ]\n"); X exit(FAILURE); X } X X strcpy(device, MAGTAPE); X strcpy(vol_label, VOL_LABEL); X blocksz = BLOCKSZ ; /* default */ X fixreclen = FIXRECLEN; /* default */ X func_count = 0; X for(p = argv[1]; *p != '\0' ; p++) X switch(*p) { X case 'H': X fprintf(stdout,"Help option specified. All others ignored.\n"); X fprintf(stdout,"OPTIONS: c = create tape\n"); X fprintf(stdout," r = append to tape\n"); X fprintf(stdout," t = list tape\n"); X fprintf(stdout," x = extract from tape specified file name\n"); X fprintf(stdout," v = verbose messages\n"); X fprintf(stdout," f = specify new device\n"); X fprintf(stdout," d = if appending, make new section\n"); X fprintf(stdout," b = specify new blocksize.(default:2048b/block.)\n"); X fprintf(stdout," F = write fixed length records (non text files).\n"); X fprintf(stdout," R = specify new fixed record length.(default:128b/rec.)\n"); X fprintf(stdout," V = specify new volume label\n"); X fprintf(stdout," H = see this help screen\n"); X exit(FAILURE); X case 'R': X fixreclen = atoi(argv[2]); X if (fixreclen > MAXFIXRECLEN) X { X fprintf(stderr,"ERROR:Fixed record length cannot be greater than %d\n",MAXFIXRECLEN); X exit(FAILURE); X } X argv++;argc--; X /* Fall through to writing fixed length recs */; X case 'F': X fixed_length_flag = 1; X break; X case 'b': X blocksz = atoi(argv[2]); X if (blocksz > MAXBLOCKSZ) X { X fprintf(stderr,"ERROR:Blocksz cannot be greater than %d\n",MAXBLOCKSZ); X exit(FAILURE); X } X argv++;argc--; X break; X case 'c': X function = CREATE; X func_count++; X mode = WRITE; X break; X X case 't': X function = LIST; X func_count++; X mode = READ; X break; X X case 'x': X function = EXTRACT; X func_count++; X mode = READ; X break; X X case 'r': X function = APPEND; X func_count++; X mode = RDWT; X break; X X case 'd': X /* appending , but starting the file offset count X all over again so that a VMS dir command will split X the tape directory into sections. */ X section_flag = 1; X break; X case 'f': X strcpy(device, argv[2]); X argv++ ; argc-- ; X break; X X#ifdef OLD X/* could use these for writing tapes. X */ X case 'B': X binflag = TRUE; X break; X X case 'R': X rawflag = TRUE; X break; X#endif X X case 'V': X strcpy(vol_label, argv[2]); X argv++ ; argc--; X break; X X case 'v': X verbose = TRUE; X break; X X X default: X printf("Unknown key: %c\n", *p); X exit(FAILURE); X } X X if (blocksz % fixreclen ) { X fprintf(stderr,"ERROR:The blocksize must be a multiple of the fixed record length.\n"); X exit (FAILURE); X } X if(func_count == 0) { X fprintf(stderr,"No function specified\n"); X exit(FAILURE); X } X if(func_count > 1) { X fprintf(stderr,"Too many functions specified\n"); X exit(FAILURE); X } X#ifdef OLD X magtape = open(device, mode); X if(magtape < 0) { X fprintf(stderr, "Cannot open %s\n", device); X exit(FAILURE); X } X#endif X switch(function) { X case CREATE: X vt_create(&argv[2], device, mode); X break; X X case LIST: X vt_list(device, mode); X break; X X case EXTRACT: X vt_extract(&argv[2], device, mode); X break; X X case APPEND: X vt_append(&argv[2], device, mode); X break; X X default: X printf("This can never happen!\n"); X break; X } X X close(magtape); X exit(SUCCESS); X} X Xmake_pad_record() X{ Xint i; Xfor (i=0;i<MAXFIXRECLEN;i++) pad_record[i] = '^'; X} X Xint open_file (device, mode) X Xchar device[]; Xint mode; X X{ X int descrip; X X descrip = open(device, mode); X if(descrip < 0) { X fprintf(stderr, "Cannot open %s\n", device); X exit(FAILURE); X } X return (descrip); X} SHAR_EOF chmod 0664 vmstape.c || echo "restore of vmstape.c fails" echo "x - extracting vt_append.c (Text)" sed 's/^X//' << 'SHAR_EOF' > vt_append.c && X#include <stdio.h> X#include <sys/types.h> X#include <sys/stat.h> X#include <sys/mtio.h> X#include <sys/ioctl.h> X#include "vmstape.h" X Xint open_file(); X Xvt_append(argv, device, mode) X Xchar **argv, *device; Xint mode; X X{ X char buf[RECSIZE]; X int i,offset; X int nblocks; X X if ( !(*argv) ) { X printf("r: no arguments??\n"); X return; X } X X /* open tape file */ X X magtape = open_file (device, mode); X X /* read the volume label */ X r_record(buf); X X offset = 1; X while (r_record(buf)) X { X ++offset; /* counting files */ X skiptm(3); X } X X if (section_flag) offset = 1; /* if we want to make sections ... */ X /* back up one tape mark */ X backtm(); X X for(i = 0 ; argv[i] ; i++) { X if(verbose) X printf("r %s\n", argv[i]); X if( subdir(argv[i]) ) X continue; X if( size0(argv[i]) ) X continue; X if ( isdir(argv[i]) ) { X printf("'%s' is a directory...writing all files within\n", argv[i]); X w_dir(argv[i], &offset); X } X else { X w_file(argv[i], offset); X ++offset; X } X } X w_tapemark(); X} X Xbacktm() X{ X int i; X struct mtop m; X X m.mt_count = 1; X m.mt_op = MTBSF; X X i = ioctl(magtape, MTIOCTOP, &m); X} SHAR_EOF chmod 0664 vt_append.c || echo "restore of vt_append.c fails" echo "x - extracting vt_extract.c (Text)" sed 's/^X//' << 'SHAR_EOF' > vt_extract.c && X#include <stdio.h> X#include <ctype.h> X#include <sys/types.h> X#include <sys/stat.h> X#include "vmstape.h" X Xstatic int nomore = 0; Xint open_file(); X Xvt_extract(files, device, mode) X Xchar **files, *device; Xint mode; X X{ X register int blocksize; X int nblocks; X int i; X char buf[RECSIZE]; X char filename[RECSIZE]; X char recformat; X char formc; X /* open tape file */ X X magtape = open_file (device, mode); X X /* get volume label */ X r_record(buf); X X while( r_record(buf) ){ X rawflag = 0; X binflag = 0; X X strcpy(filename, field(buf, H1_FNAME)); X strip(filename); X r_record(buf); X blocksize = atoi(field(buf, H2_BSZ)); X X /* set flags for extraction of the file. X * X * variable length crlf form control --> no flags set X * this is your normal text file on VMS X * X * |length|record| --> |record|'\n' X * X * fixed length --> binflag set X * file records extracted as is. no interpretation X * done on blocks of file. (ie, file is full of records X * and no information about record length). This type X * of file is an array of records which have no control X * (record length) info). X * X * |record| --> |record| X * X * variable length no form control --> raw flag set X * X * |length|record| --> |length|record| X */ X recformat = *field(buf, H2_FMT); X fixreclen = atoi(field(buf, H2_RECLEN)); X if( recformat == 'F' ) /* is fixed length */ X binflag = 1; X else { X if( recformat != 'D' ){ X printf("UNKNOWN RECORD FORMAT <%c>\n", X recformat); X exit(FAILURE); X } X /* if recformat == 'D', then X * is variable length. set no flags */ X } X X formc = *field(buf, H2_FORMC); X if( formc == 'M') X rawflag = 1; /* stuff record onto disk with X * its control area defining its X * length. X */ X else { X if( formc != ' '){ X printf("UNKNOWN FORM CONTROL <%c>\n", X formc); X exit(FAILURE); X } X /* is the default whith crlf X * stuff done at the end of each record. X */ X } X X if( header3() ) X r_tapemark(); X X if( isarg(filename, files) ) X r_data(blocksize, filename, files); X else{ X /* skip tape mark at end of data area and X * after Trailers. X */ X skiptm(2); X continue; X } X X if( nomore ) /* got all files wanted */ X break; X X /* grap Trailer areas and tape mark separating files X */ X r_record(buf); X r_record(buf); X if( trailer3() ) X r_tapemark(); X } X X for( i=0; files[i]; i++) X if (files[i] != (char *)(-1)) X printf("'%s' not found on tape\n", files[i]); X X} X Xr_data(blksize, filename, argv) Xregister int blksize; X char *filename; X char **argv; X{ X register int i; X FILE *outfile; X char *pathname; X X if(blksize > MAXBLOCKSZ) { X fprintf("BUFFER SIZE EXCEEDED\n"); X exit(FAILURE); X } X X if(exists(filename)) { X fprintf(stderr, "x: %s already exists. Not extracted.\n", X filename); X while( read(magtape, databuf, blksize) > 0) X continue; X return; X } X X outfile = fopen(filename, "w"); X if(outfile == NULL) { X fprintf(stderr, "x: cannot create %s\n", filename); X while( read(magtape, databuf, blksize) > 0) X continue; X return; X } X X if (verbose) printf("Extracting %s\n", filename); X X while( (i = read(magtape, databuf, blksize)) > 0 ) X ext(databuf, i, outfile); X X fclose(outfile); X} X Xexists(filename) X char *filename; X{ X struct stat statbuf; X X if( stat(filename, &statbuf) < 0 ) X return(FALSE); X X return(TRUE); X} X Xext(p, blksize, outfile) Xregister char *p; X int blksize; Xregister FILE *outfile; X{ X register int i; X register int seen; X int thisline; X char nbuf[5]; X char outbuf[MAXFIXRECLEN]; X int filler = 1; X X/* if binflag, then global fixreclen is set to record size */ X if(binflag) { X while (blksize>0) { X for(i = 0 ; i < fixreclen ; i++) { X outbuf[i] = *p; X if (*p++ != FILLCHAR) filler = 0; X } X if (filler) return; X filler = 1; X for(i = 0;i<fixreclen;i++) { X putc(outbuf[i], outfile); X } X blksize -= fixreclen; X } X } X else X for(seen = 0 ; seen < blksize ; ) { X for(i = 0 ; i < 4 ; i++) X nbuf[i] = *p++; X nbuf[4] = '\0'; X seen += 4; X if(!isdigit(nbuf[0])) X break; X thisline = atoi(nbuf) - 4; X X if(rawflag) X putw(thisline, outfile); X X for(i = 0 ; i < thisline ; i++) X fputc(*p++, outfile); X X seen += thisline; X X if(!rawflag) X putc('\n', outfile); X } X} X Xr_record(p) X char *p; X{ X int n_read; X X n_read = read(magtape, p, RECSIZE); X return(n_read); X} X Xisarg(str, argv) X char *str; X char **argv; X{ X int i; X int count; /* of number left */ X int retval; X X /* argv vector is null terminated. X * if no files listed, then extract all X */ X if( !(*argv) ) X return(1); X X count = 0; X retval = 0; X X for(i = 0 ; argv[i] ; i++){ X if( argv[i] == ((char *)(-1)) ) X continue; X if( streq(str, argv[i]) ) { X argv[i] = ((char *)(-1)); X retval = 1; X } X count++; X } X X if( (!count) && (!retval) ) X nomore = 1; /* all done */ X return(retval); X} X Xstrip(p) Xregister char *p; X{ X if(*p == '\0') X return; X while(*p != '\0') X p++; X p--; X while(*p == ' ') X p--; X *++p = '\0'; X} X Xr_tapemark() X{ X if(read(magtape, databuf, MAXBLOCKSZ) != 0) { X fprintf(stderr, "MISSING TAPE MARK??\n"); X exit(FAILURE); X } X} SHAR_EOF chmod 0664 vt_extract.c || echo "restore of vt_extract.c fails" echo "x - extracting vt_list.c (Text)" sed 's/^X//' << 'SHAR_EOF' > vt_list.c && X#include "vmstape.h" X Xint open_file(); X Xvt_list(device, mode) X Xchar *device; Xint mode; X X{ X register int blocksize; X register int recformat; X register int formcntrl; X int nblocks; X char buf[RECSIZE]; X char filename[RECSIZE]; X char format[40]; X char formc[40]; X int n_read; X int reclen; X /* open tape file */ X X magtape = open_file (device, mode); X X /* get volume label */ X X r_record(buf); X printf("\nVolume Label: %s\n\n", field(buf, VLABEL)); X if( verbose ) X printf("%-20s%-15s %5s %6s %7s %7s\n", X "filename", "record format", "bsize", X "reclen", "formc", "nblocks" X ); X else X printf("%-20s\n", X "filename" X ); X X while( r_record(buf) ){ X X strcpy(filename, field(buf, H1_FNAME)); X strip(filename); X X r_record(buf); X blocksize = atoi(field(buf, H2_BSZ)); X if( blocksize > MAXBLOCKSZ ){ X printf("blocksize %d too large for program !\n", X blocksize); X exit(-1); X } X X recformat = *field(buf, H2_FMT); X if( recformat == 'D' ) X strcpy(format,"var length"); X else if( recformat == 'F' ) X strcpy(format,"fixed length"); X else X strcpy(format,"unknown"); X X reclen = atoi(field(buf, H2_RECLEN)); X if( recformat == 'D' ) X reclen -= 4; /* 4 = size of control area X * of for variable length records X * X * the control area defines the length X * of the record, including the control X * area. X */ X X formcntrl = *field(buf, H2_FORMC); X if( formcntrl == 'A' ) X strcpy(formc, "fortran"); X else if( formcntrl == 'M' ) X strcpy(formc, "none"); X else if( formcntrl == ' ' ) X strcpy(formc, "crlf"); /* to be put between records */ X else X strcpy(formc,"unknown"); X X X if( header3() ) X r_tapemark(); X X if( verbose ) X printf("%-20s%-15s %-5d %-6d %7s ", X filename, format, blocksize, reclen, formc); X else X printf("%-20s\n", X filename); X nblocks = 0; X while( read(magtape, databuf, blocksize) > 0 ) X nblocks++; X X/* X if (recformat == 'F') nblocks = (nblocks % (blocksize/reclen)) X ? nblocks/(blocksize/reclen)+1 X : nblocks/(blocksize/reclen); X*/ X /* Because when we were reading in X blocksize chunks of data, we X were only getting reclen size peices. */ X X if( verbose ) X printf("%-7d\n", nblocks); X X /* the condition that causes the exit from the while loop X * reads in the tape mark after the data area X */ X r_record(buf); X r_record(buf); X if( trailer3() ) X r_tapemark(); X } X} SHAR_EOF chmod 0664 vt_list.c || echo "restore of vt_list.c fails" echo "x - extracting vt_write.c (Text)" sed 's/^X//' << 'SHAR_EOF' > vt_write.c && X#include <stdio.h> X#include <sys/types.h> X#include <sys/stat.h> X X /* for either the old directory struct or new, but not emulation*/ X X#include <sys/dir.h> X X#include "vmstape.h" X Xstruct dirlis { X char *names; /* newline sep'd list of files */ X int n; /* number of files there */ X}; X Xstruct carray { X char *block; /* block w/text stored in it */ X char **array; /* block of pointers into above */ X int num; /* number of 'valid' pointers */ X}; X Xint open_file(); X X/* makerec assumes normal textual files X * note the check for zero byte means such is not valid to have X * in textual files. All material following the zero byte is X * lost. X * X * fgets has filled in the variable "in" with an asciz string X * and so the variable "in" is null-terminated. X */ X Xmakerec(in, out, max) X char *in; X char *out; X{ X register char *p; X register int count; X X count = 0; X for(p = in ; *p != '\n' ; p++){ X count++; X if( count + 4> max ){ /* we need the 4 to have control info*/ X printf("GASP! More than %d characters in the file not seperated by a newline.\nGASP! File Incomplete.\n",max-4); X return 0; /* error condition */ X } X if( !(*p) ) X break; X } X X *p = '\0'; X sprintf(out, "%04d%s", strlen(in) + 4, in); X} X Xbflush(seen) Xregister int seen; X{ X if(seen == 0) X return; X for( ; seen < blocksz ; seen++) X databuf[seen] = FILLCHAR; X if(write(magtape, databuf, blocksz) != blocksz) { X fprintf(stderr, "FATAL WRITE ERROR\n"); X exit(FAILURE); X } X databuf[0] = '\0'; /* for strcat() */ X} X Xflush_blk(buf,charcount) Xchar *buf; Xlong charcount; X{ X int i; X if (charcount) X for (i = charcount;i<blocksz;i++) buf[i] = FILLCHAR; X if (write(magtape,buf,blocksz) != blocksz) X { X fprintf(stderr,"FATAL WRITE ERROR\n"); X exit (FAILURE); X } X} X Xvt_create(argv, device, mode) X Xchar **argv, *device; Xint mode; X X{ X int i; X int offset; /* offset for filenumbers if any arguments */ X /* are directory names instead of just files */ X X if( !(*argv) ){ X printf("c: no args?\n"); X return; X }; X X /* open tape file */ X X magtape = open_file (device, mode); X X w_label(); /* volume label */ X X offset = 1; /* first file sequence number */ X X for(i = 0 ; argv[i] ; i++) { X if(verbose) X printf("c %s\n", argv[i]); X if( subdir(argv[i]) ) X continue; X if( size0(argv[i]) ) X continue; X if ( isdir(argv[i]) ) { X printf("'%s' is a directory...writing all files within\n", argv[i]); X w_dir(argv[i], &offset); X } X else { X w_file(argv[i], offset); X ++offset; X } X } X w_tapemark(); X} X Xw_dir(dirname, offset) Xchar *dirname; Xint *offset; X{ X struct carray files, gath(); X char *p, *malloc(); X int i; X X p = malloc(256); X X files = gath(dirname); X X for(i = 0; i < files.num; i++) { X if(*files.array[i] == '.') X continue; X X if(verbose) X printf("%s: %s\n", dirname, files.array[i]); X X strcpy(p, dirname); X strcat(p, "/"); X strcat(p, files.array[i]); X X if ( isdir(p) ) { X printf("%s is a subdirectory. Not added to tape\n", p); X continue; X } X if( size0(p) ) X continue; X w_file(p, *offset ); X ++*offset; /* inc file sequence number */ X } X} X Xint w_data(filename) Xchar *filename; X{ X FILE *ioptr; X char c,buf[MAXBLOCKSZ+1]; X struct stat filestat; X long totalchar=0,charcount=0; X long nrecs; X int nblocks = 0; X int length,outcount; X char rec[MAXBLOCKSZ+1]; X X ioptr = fopen(filename, "r"); X if(ioptr == NULL) { X fprintf(stderr, "Cannot open %s\n", filename); X } X else { X X if (fixed_length_flag) { X stat(filename,&filestat); X nrecs = (filestat.st_size%fixreclen ) X ? filestat.st_size/fixreclen +1 X : filestat.st_size/fixreclen ; X totalchar = fixreclen * nrecs; /*this will be an even rec size*/ X while (charcount < totalchar) X { X c = getc(ioptr); X buf[charcount++ % blocksz] = c; X if ((charcount % blocksz) == 0 ) flush_blk(buf,0); X } X if ((charcount % blocksz) != 0 ) X flush_blk(buf,charcount % blocksz); X fclose(ioptr); X nblocks = nrecs/(blocksz/fixreclen); X if (nrecs % (blocksz/fixreclen)) nblocks++; X } X else { /* variable length records */ X X outcount = 0; X for(;;){ X length = (int)fgets(buf, blocksz, ioptr); X /* scratch variable for the moment */ X X if(length == NULL) X break; X X if (makerec(buf, rec, blocksz) == 0) X { X nblocks = 0; X break; /* error */ X } X length = strlen(rec); X if(outcount + length > blocksz) { X bflush(outcount); X nblocks++; X outcount = 0; X } X strcat(databuf, rec); X outcount += length; X } X X if( outcount ){ X bflush(outcount); X nblocks++; X } X X fclose(ioptr); X } X} X return(nblocks); X} X X Xw_file(path, number) Xchar *path; Xint number; X{ X int nblocks; X char *file, *malloc(); X X file = malloc(256); X strcpy(file, path); X X while(*file != '\0' && *file !='/') file++; X if (*file == '\0') file = path; X else file++; X w_hdr1(file, number); X w_hdr2(); X w_tapemark(); X nblocks = w_data(path); X w_tapemark(); X w_eof1(file, number, nblocks); X w_eof2(); X w_tapemark(); X if( nblocks == 0 ){ X fprintf(stderr,"%s caused no data area to be written on tape??\n", X file); X w_tapemark(); /* make greaceful crash (all other files are okay )*/ X exit(FAILURE); X } X} X Xw_eof1(filename, filenum, blkcount) Xchar *filename; Xint filenum; Xint blkcount; X{ X char buf[81]; X X sprintf(buf, X "EOF1%-17s%-6s0001%04d000101%-6s%-6s %06dDECFILE11A ", X filename, vol_label, filenum, CREATION, EXPIRATION, blkcount); X X if(write(magtape, buf, 80) != 80) { X fprintf(stderr, "WRITE -- FATAL ERROR\n"); X exit(FAILURE); X } X} X Xw_eof2() X{ X char buf[82]; X sprintf(buf, X "EOF2%c%05d%05d %c 00 ",(fixed_length_flag)?'F':'D', X blocksz,(fixed_length_flag)?fixreclen:blocksz-4, X (fixed_length_flag) ? 'M' : ' '); X if(write(magtape, buf, 80) != 80) { X fprintf(stderr, "WRITE -- FATAL ERROR\n"); X exit(FAILURE); X } X} X Xw_hdr1(filename, filenum) X char *filename; X int filenum; X{ X char buf[81]; X X sprintf(buf, X "HDR1%-17s%-6s0001%04d000101%-6s%-6s 000000DECFILE11A ", X filename, vol_label, filenum, CREATION, EXPIRATION); X X if(write(magtape, buf, 80) != 80) { X fprintf(stderr, "WRITE -- FATAL ERROR\n"); X exit(FAILURE); X } X} X Xw_hdr2() X{ X char hdr2_label[RECSIZE]; X int i; X X sprintf( hdr2_label, "HDR2%c%05d%05d",(fixed_length_flag)?'F':'D',blocksz,(fixed_length_flag) ? fixreclen: MAXFIXRECLEN); X i = strlen(hdr2_label); X for( ; i<RECSIZE ; i++){ X X hdr2_label[i] = ' '; /* By default */ X X /* Form control info */ X if (i == 36 && (fixed_length_flag)) hdr2_label[i] = 'M'; X X /* buffer offset = "00" */ X if( i == 50 || i == 51 ) hdr2_label[i] = '0'; X X } X X if(write(magtape, hdr2_label, RECSIZE) != RECSIZE) { X fprintf(stderr, "WRITE -- FATAL ERROR\n"); X exit(FAILURE); X } X} X Xw_label() X{ X char buf[RECSIZE]; X int i; X X sprintf(buf,"VOL1%-6s", vol_label); X X i = strlen(buf); X for( ; i<RECSIZE ; i++){ X X /* DIGITAL standard version */ X if( i == 50 ){ X buf[i] = '1'; X continue; X } X X /* label standard version */ X if( i == 79 ){ X buf[i] = '3'; X continue; X } X X buf[i] = ' '; X } X X if(write(magtape, buf, RECSIZE) != RECSIZE) { X fprintf(stderr, "WRITE -- FATAL ERROR\n"); X exit(FAILURE); X } X} Xsize0(filename) X char *filename; X{ X struct stat statbuf; X X if( stat(filename, &statbuf) < 0 ){ X printf("'%s' does not exist...not added to tape\n", filename); X return(1); X } X if( statbuf.st_size == 0 ){ X printf("'%s' is of 0 size...skipped\n", filename); X return(1); X } X X return(0); X} X Xsubdir(filename) Xchar *filename; X{ X register char *p; X X for(p = filename; *p != '\0'; p++) X if (*p == '/') { X printf("File '%s' from a subdirectory...not added to tape\n", filename); X return(1); X } X return(0); X} X Xisdir(file) Xchar *file; X{ X struct stat sb; X register int t; X X stat(file, &sb); X t = sb.st_mode & S_IFMT; X X return(t == S_IFDIR); X} X X/* DIRSIZ, to return the size of the (directory) passed it. X * used as an estimate for the amount of space to keep for X * matches. ERROR if longer than 16 bit's worth. X * From Dave Brownell's help program X */ X Xint dirsiz(dir) char *dir; X{ X struct stat entry; X X if (stat(dir,&entry) == EOF) { X printf("Can't stat() %s\n", dir); X exit(1); X } X if (entry.st_size > 65000L) { /* lazy */ X printf("Directory exceeds 64K bytes in length\n"); X exit(1); X } X else return((int) entry.st_size); X} X X/* NAMGET, to get the names in a directory and put them in X * a string, separated by newlines. will also count the number X * of files. From Dave Brownell's help program. X * Modified to reflect 4.2 file changes as of 6/13/84. -- S.K. X */ X Xstruct dirlis namget(dir) char *dir; X{ X#ifdef OLD_DIR_STRUCT X struct direct entry; X#else X struct direct *entry; X DIR *dp; X#endif X struct dirlis ret; X register int i; X register char *t; X register FILE *file; X extern char *malloc(); X X ret.n = 0; X X t = ret.names = malloc(dirsiz(dir)); X#ifdef OLD_DIR_STRUCT X if ((file = fopen(dir,"r")) == NULL) return (ret); X while (read(file,&entry,sizeof(struct direct))) { X if (entry.d_ino != 0) { X for (i = 0 ; entry.d_name[i] != 0 && i < MAXNAMLEN ; i++) X *t++ = entry.d_name[i]; /* copy the name */ X *t++ = '\n'; X ret.n++; X } X } X *t++ = '\0'; X fclose(file); X return(ret); X} X#else X if ((dp = opendir(dir)) == NULL) return (ret); X while( entry = readdir(dp)) { X if (entry->d_ino != 0) { X for (i = 0 ; entry->d_name[i] != 0 && i < MAXNAMLEN ; i++) X *t++ = entry->d_name[i]; /* copy the name */ X *t++ = '\n'; X ret.n++; X } X X } X X *t++ = '\0'; X closedir(dp); X return(ret); X} X#endif X X/* GATH, to return an array of strings representing the names X * of the files in the directory. This is not sorted. X * Note that a 'struct carray' has two pointers to blocks which X * must be free()ed. X * The newlines in the block returned by namget() are changed X * to nulls. From Dave Brownell's help program. X */ X Xstruct carray gath(dir) char *dir; X{ X struct carray ret; X struct dirlis names; X char *calloc(); X X names = namget(dir); X ret.block = names.names; X ret.array = (char **) calloc(names.n + 1, sizeof(char *)); X for (ret.num = 0; ret.num < names.n; ret.num++) { X (ret.array)[ret.num] = names.names; X while (*(names.names) != '\n') (names.names)++; X *(names.names)++ = '\0'; X } X ret.array[ret.num] = NULL; X return(ret); X} SHAR_EOF chmod 0664 vt_write.c || echo "restore of vt_write.c fails" echo "x - extracting vmstape.h (Text)" sed 's/^X//' << 'SHAR_EOF' > vmstape.h && X#ifndef MAXNAMLEN /* maxnamlen is defined in the new directory package, but X not the old. This is our way of telling if we X are using the new package or not */ X#define MAXNAMLEN 14 /* This will set maxnamlen for old dir structs*/ X#define OLD_DIR_STRUCT 1 /* for compilation of proper subrs */ X#endif X X#define TRUE 1 X#define FALSE 0 X X#define streq(a,b) (!strcmp(a,b)) X X/* exit status codes for program X */ X#define SUCCESS 0 X#define FAILURE (-1) X X/* #define MAGTAPE "/dev/rmt8" */ X#define MAGTAPE "/dev/rmt/0m" X Xint blocksz; X#define BLOCKSZ 2048 X#define MAXBLOCKSZ 2048 /* random choice */ Xint fixreclen; X#define FIXRECLEN 128 /* should be multiple of BLOCKSZ */ X#define MAXFIXRECLEN MAXBLOCKSZ X X#define RECSIZE 80 /* size of Header and Trailer X * records X */ X X/* X * The ANSI Standard Magtape produced by a VAX looks like X * X * Volume Label 80 bytes X * -------------------------------- X * Header 1 80 bytes X * Header 2 80 bytes X * (Header 3) (80 bytes) ... optional X * TAPE MARK X * Data Block 2048 bytes X * .... X * TAPE MARK X * Trailer 1 80 bytes X * Trailer 2 80 bytes X * (Trailer 3) (80 bytes) ... optional X * TAPE MARK X * --------------------------------- X * TAPE MARK X * X * The information between the dashed lines is repeated for X * every file on the tape. See appendix B of VAX-11 RMS Reference X * for additional detailed information. X */ X X/* The definitions used here reflect the fact that in C, counting starts X * at 0 and not 1 X */ X X/* X * fields of Volume Label X */ X#define __XXX 0,2 /* alphabetic characters "VOL" */ X#define __YYY 3,3 /* numberic character "1" */ X#define VLABEL 4,9 X#define VPROT 10,10 /* protection on volume */ X#define VRES1 11,36 X#define VOWNER 37,49 X#define VDSV 50,50 /* DIGITAL standard version */ X#define VRES2 51,78 X#define VLSV 79,79 /* Label standard version */ X X/* X * fields of Header 1 X */ X#define __AAA 0,2 /* alphabetic characters "HDR" */ X#define __BBB 3,3 /* numeric character "1" */ X#define H1_FNAME 4, 20 /* filename */ X#define __CCC 21,26 /* file set identifier */ X#define __DDD 27,30 /* file section number */ X#define __EEE 31,34 /* file sequence number */ X#define __FFF 35,38 /* generation number */ X#define __GGG 39,40 /* generation version */ X#define H1_CDATE 41,46 /* creation date */ X#define H1_XDATE 47,52 /* expiration date */ X#define H1_ACCESS 53,53 /* access security */ X#define H1_BCOUNT 54,59 /* block count (always 0) */ X#define H1_SYSTEM 60,72 /* system that produced it */ X#define __HHH 73,79 /* reserved */ X X/* X * fields of Header 2 X */ X#define __III 0,2 /* alphabetic characters "HDR" */ X#define __JJJ 3,3 /* numeric character "2" */ X#define H2_FMT 4,4 /* record format */ X#define H2_BSZ 5,9 /* # characters per block */ X#define H2_RECLEN 10,14 /* record length for fixed length records */ X#define __KKK 15,35 /* system dependent info */ X#define H2_FORMC 36,36 /* form control X * 'A' : first byte of record contains X * fortran control characters X * 'M' : record contains all form control X * info X * ' ' : lf/cr to be inserted between X * records. X */ X#define __LLL 37,49 /* system dependent info */ X#define __MMM 50,51 /* buffer offset = "00" */ X#define __NNN 52,79 /* reserved */ X X/* X * fields of Header 3 X */ X#define __OOO 0,2 /* alphabetic characters "HDR" */ X#define __PPP 3,3 /* numeric character "3" */ X#define __QQQ 4,67 /* FILES-11 attributes if made on VMS system */ X#define __RRR 68,79 /* system dependent info. spaces for VMS */ X X/* X * DEFINITIONS FOR WRITING TAPES X */ X X#define VOL_LABEL "UNIX" /* default volume label */ X#define CREATION " 70001" /* Julian date: yyddd */ X#define EXPIRATION " 99365" /* Julian date: yyddd */ X#define FILLCHAR '^' X Xint magtape; X Xchar databuf[MAXBLOCKSZ+1]; Xchar vol_label[80]; X X X X X/* X * These flags represent which operation is to be performed X */ X Xint createflag; /* Create a new tape */ Xint listflag; /* List the tape contents */ Xint extractflag; /* Extract the files from tape */ X X/* X * These flags represent modifiers to the basic operations X */ X Xint rawflag; Xint binflag; Xint verbose; Xint fixed_length_flag; Xint section_flag; X Xchar *field(); X Xchar pad_record[MAXFIXRECLEN]; SHAR_EOF chmod 0664 vmstape.h || echo "restore of vmstape.h fails" echo "x - extracting vmstape.1 (Text)" sed 's/^X//' << 'SHAR_EOF' > vmstape.1 && X.TH VMSTAPE 1H X.SH NAME Xvmstape \- manipulate tapes in accordance with VAX-VMS standards X.SH SYNOPSIS Xvmstape [tcxrvfbdFRHV] [files] X.SH DESCRIPTION X.LP X.I Vmstape Xis a program that manipulates the tape format that is the standard for XVMS systems. The format is based on the ANSI standard for tapes. The Xswitches to the X.I vmstape Xprogram are as similar to the switches for X.I tar Xas possible. X.SH SWITCHES X.LP X.I c Xis used to X.I create Xa new tape with the named files on it. XAll old information on the tape is overwritten. X.LP X.I r Xappends the named files to the end of the tape. All old information Xis unchanged on the tape. X.LP XIf a directory name is passed as an argument to the X.I c Xor X.I r Xswitches, all the files in the directory will be written on the tape. X.LP X.I t Xreports a directory listing of the files on the tape. X.LP X.I x extracts Xthe named files from the tape. If no individual files are Xspecified, then all files are extracted from the tape. X.LP XAll other switches are modifiers to these basic commands. Not all modifiers Xare applicable to all the commands. X.LP X.I v Xcauses a X.I verbose Xoutput describing the programs actions on the tape. X.LP X.I f Xis used to specify an alternate magtape device. The default Xassumes the tape is 1600 bpi density and will rewind the tape when the Xprogram is done. X.LP X.I d Xdivides the tape into sections so that the VMS "directory" command will Xlist the tape in sections. This is useful when writing subdirectories in XUNIX. X.LP X.I b Xis used to change the default block size to the length specified. X.LP X.I V Xis used to specify an alternate volume label. X.LP X.I F Xis used to indicate that files are NOT textual, and should therefore be Xwritten in a fixed length format. X.LP X.I R Xis used to change the default fixed length record size to the length Xspecified. X.LP X.I H Xprovides a help screen. X X.SH SEE ALSO Xtar(1), tp(1), mtb(8), retrieve(8) X.SH DIAGNOSTICS X.LP XImproperly formatted tapes, not made in agreement with the VMS standard, Xmay generate missing tape mark errors. X.LP XProblems with the tape and/or magtape drive will generate messages to the Xeffect that the program was unable to read or write the tape. X.SH BUGS X.LP XAs DEC changes the standard for VAX-VMS tapes, Xthe program will need alterations. X.LP XIf the user attemps to write a non-textual file without the F (or R) switch, Xthe file will not be completely written. SHAR_EOF chmod 0664 vmstape.1 || echo "restore of vmstape.1 fails" exit 0 -- W Mat Waites | Unlike most of you, I am not a nut. {gatech,emory}!emcard!mat | -H. Simpson
donn@hpfcdc.HP.COM (Donn Terry) (08/25/90)
I'll provide a guess.... It's written in some form of VAX proprietary backup or exchange format. (I have no idea what.) Some suggestions at getting at it: 1) The VAX may have (I'm no VAX expert) a tar or cpio analog; if so, use that. 2) Copy the whole tape into a file, edit out the junk, and you may have what you want. dd if=<tape> of=<filename> ibs=8192 may do it. If the bytes seem right, but scrambled, try: dd if=<tape> of=<filename> ibs=8192 conv=swap If still scrambled, you may need to do a custom byte-swapping program. You may also have to do some fiddling with line breaks. 3) In situations like this, some form of LAN (specifically email) might be even easier. 4) If it's binary data, you will HAVE to write a custom conversion program. (Or convert to text and back.) 5) Write an ANSI tape; read it with ANSITAR (from the contrib tape). (Here I stick my neck out; you may not have ANSITAR and it may be harder to get than you'd (or I'd) like.) 6) Use some sort of copy utility to write it to the tape with no structuring information at all. Read it back with dd. Again, you may have to translate the line breaks or swap bytes. Donn Terry Speaking solely for myself, and no-one else.
rer@hpfcdc.HP.COM (Rob Robason) (08/28/90)
If the other suggestions don't pan out, you may wish to use the mtdump utility attached below to figure out the format of the tape, then use a combination of the mt and dd commands to retrieve what you need. mtdump is on the HP contrib tape. Rob Robason /* @(#) $Revision: 29.2 $ */ /* 1/2 inch mag tape dump utility .... Oct 25, 1984, Rob Robason */ /* Last modified 1/86 to use 8192 Byte buffer and /dev/rmt/0m */ /* Usage: mtdump [-t special_file] [-b max_rec_size ] */ /* Default maximum record size is BUFSIZE */ #include <fcntl.h> /* required for read(2) */ #include <stdio.h> #include <signal.h> #define BUFSIZE 8192 /*default buffer size used by malloc(3c) */ #define DFLT_TAPE "/dev/rmt/0m" #define WIDTH 16 /* width of data fields on output */ int done = 0; /* set if 'more' dies */ char *malloc(); main(argc,argv) int argc; char *argv[]; { int filedes, size, recno, pos; unsigned bufsize = BUFSIZE; char *tape_name = DFLT_TAPE; union { char *sign; unsigned char *unsign; } buf; /* data buffer for mag tape record */ unsigned char tmp[WIDTH]; /* display buffer for ascii */ FILE *pfp; int moredead(); int c; extern char *optarg; extern int optind; while ((c = getopt(argc, argv, "t:b:")) != EOF) switch (c) { case 't': tape_name = optarg; break; case 'b': if ((bufsize = atoi(optarg)) == 0) { perror(optarg); exit (-1); } break; default: fprintf(stderr,"Usage: %s [-t special_file] [-b max_rec_size ]\n",argv[0]); exit(-1); } if ((filedes = open(tape_name, O_RDONLY | O_NDELAY)) == -1) { perror(tape_name); exit(-1); } if ((buf.sign = malloc(bufsize)) == NULL) { perror("malloc"); exit(-1); } signal(SIGPIPE, moredead); pfp = popen("/usr/bin/more", "w"); fprintf(pfp,"device to be dumped: %s\n",tape_name); recno=1; /* read the file, record at a time */ while ((size = read(filedes,buf.unsign,bufsize)) && !done) { fprintf(pfp,"Record #%d, Size=%d bytes\n",recno,size); for (pos=0;pos<size;pos++){ /* print buf out in hex and ascii */ if ( !( pos % WIDTH )) /* print decimal pos in record at SOL*/ fprintf(pfp,"%.6d ",pos); fprintf(pfp," %.2X",buf.unsign[pos]); /* print hex char */ if(buf.unsign[pos] >= 32 & buf.unsign[pos] <= 127) /* put display-able char into display buffer */ tmp[pos % WIDTH] = buf.unsign[pos]; else /* put '.' into display buffer */ tmp[pos % WIDTH] = '.'; if (!((pos+1) % WIDTH)) /* print ascii buffer at EOL */ fprintf(pfp," %16s\n",tmp); } if ( size % WIDTH ) { /* print the last partial buffer */ tmp[pos % WIDTH] = 0; fprintf(pfp,"\r\t\t\t\t\t\t\t %s",tmp); } fprintf(pfp,"\n"); recno++; } fprintf(pfp,"end of file\n"); pclose(pfp); close(filedes); } moredead() { done++; exit(0); }