ansi tape read utility in C

notes at isucs1.UUCP notes at isucs1.UUCP
Wed Oct 3 08:00:39 AEST 1984


  Here is a simple quick and dirty program to read and write ansi labeled tapes.
It doesn't have a lot of flexibility, such as user control over record and
block sizes and so forth, but it does allow files to be transferred.

  I hope it is of some help to you.  And if someone else has already responded 
with something better I could probably use it too.

							Quent Johnson
-------------------------->>>>CUT HERE <<<-------------------------------------

/*  <ansitar.c>

 ******************************************************************************
 * March 26,1984							      *
 *									      *
 *									      *
 *  AUTHORS:      Steve Christiansen -- ISU Computation Center		      *
 *                Quentin Johnson    -- ISU Computer Science Department	      *
 ******************************************************************************

		A simple archiver for `ansi standard' tapes.

     New tapes can be created or files appended to existing volumes.
   Files can be extracted or the names of files on the tape can be listed.
   Fixed and variable record length files can be read; both the 'A` and
   'M` carriage control formats are interpreted when read. 

     For now only multi-file, single-volume tapes with record and block
   size determined by program constants can be written.  Since this program
   is meant to be only a quick and dirty method of file transfer between the
   various mainframes it does not allow users control of format.  A space is
   written in the carriage control format field.  The record format is `F'
   for fixed length.

     The record and block length are set for 12 80 character records per
   block since the major use will be for transferring text files to be
   edited on an 80 column screen.

     The VAX/VMS manual: "Magnetic Tape Users Guide" was used as a reference
   for the tape format used by this program. 
*/

#include <stdio.h>
#include <sys/types.h>			/* needed by mtio.h */
#include <sys/ioctl.h>
#include <sys/mtio.h>			/* magnetic tape io control */

#define TAPE		"/dev/rmt8"	/* 1600 bpi raw tape device */
#define VOLNAME		"CSUNIX"	/* volume name for tapes written */
#define TRUE		1
#define FALSE		0
#define RD		0		/* modes for low level i/o */
#define RW		2

#define LABBUFSIZ	128	/* max. tape label record size when reading */
#define DATBUFSIZ	8192	/* read data buffer size */
#define TBLKSIZ		960	/* tape data write block size */
				/* set up for typical text files
				   with 80 col. lines; 12/block */
#define RECSIZ		80	/* default record size for writing */

/* indices for label name list */
#define VOL1	0
#define HDR1	1
#define HDR2	2
#define HDR3	3
#define EOF1	4
#define EOF2	5
#define EOF3	6
#define EOV1	7
#define EOV2	8
#define EOV3	9
#define NLABIDS	10

/* standard tape label names */
char  *labelids[NLABIDS] =
       { "VOL1",	/* contains volume name */
         "HDR1",	/* contains file name,#blocks, creation date, etc. */
         "HDR2",	/* contains record size, block size, record format */
         "HDR3",	/* optional */
         "EOF1",
         "EOF2",	/* optional */
         "EOF3",	/* optional */
         "EOV1",
         "EOV2",	/* optional */
         "EOV3" };	/* optional */

int	file,		/* sequence number of file in volume */
	recsz,		/* record size */
	blksz,		/* blocksize */
	nblks,		/* number of blocks in tape file */
	creatn,		/* creation, expiration dates */
	expir,
	section,	/* file section within volume */
	gen,		/* file generation & version */
	ver;
char	recfm,		/* tape file record format (fixed,variable) */
	form,		/* carriage control format (A or M ) */
	name[18],		/* current tape-file-name */
	volume[7],	/* tape volume name */
	set[7],		/* tape volume set name */
	genstr[5],	/* file generation  & version */
	verstr[3];

int	hdr1, hdr2, hdr3, eov, vol1; /* flags: if !=0 this label was read */

int	tapefd,		/* for tape i/o */
	diskfd;		/* for copying disk files to tape */
FILE	*output;	/* disk output file for extracting tape files */

int	noproc,		/* if == 1, don't process carriage control */
	verbose;	/* if == 1, echo file names when encountered */
char	function;	/* x,t,a,or c - what to do with tape */

char	labelbuf[LABBUFSIZ],	/* tape label record buffer */
	databuf[DATBUFSIZ],	/* buffer for reading */
	tapebuf[TBLKSIZ];	/* buffer for writing to tape */

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

quit(msg, parm)		/* Fatal error handler */
char	*msg;
int	parm;
{
	printf("In file %s:  ", (hdr1? name : "??????"));
	printf(msg, parm);
	printf("\n");
	exit(1);
}


readlabel()
{
	register	len;

	if ((len = read(tapefd, labelbuf, LABBUFSIZ)) < 0)
		quit("Label read error.");
	if (len == 0) return(FALSE); /* hit tape mark */
	if (len != 80)
		quit("Wrong length label record.");
	return(TRUE);
}


readheader()
{
	register	id;
	register	char *p;

	hdr1 = hdr2 = hdr3 = FALSE;
	if (readlabel() == FALSE) return(FALSE);
	do {
		for (id = 0 ; id < NLABIDS ; id++)
			if (strncmp(labelids[id], labelbuf, 4) == 0)  break;
		switch (id) {
		  case HDR1:
			hdr1 = TRUE;
			if (sscanf(labelbuf, "%*4c%17c%6c%4d%4d%4c%2c%6d%6d%*1c%6d",
			  name, set, &section, &file, genstr, verstr, &creatn, &expir, &nblks) != 9)
				quit("Bad format in HDR1 label.");
			for (p = &name[16] ; p > name ; p--)  if (*p != ' ') {
				*++p = 0;
				break;
			}
			if (sscanf(genstr, "%d", &gen) != 1) gen = 0;
			if (sscanf(verstr, "%d", &ver) != 1) ver = 0;
			break;

		  case HDR2:
			hdr2 = TRUE;
			if (sscanf(labelbuf, "%*4c%1c%5d%5d%*21c%1c",
			  &recfm, &blksz, &recsz, &form) != 4)
				quit("Bad format in HDR2 label.");
			break;

		  case HDR3:
			hdr3 = TRUE;
			break;

		  case VOL1:
			vol1 = TRUE;
			if (sscanf(labelbuf, "%*4c%6c", volume) != 1)
				quit("Bad format in VOL1 label.");
			break;

		  case EOF1:
		  case EOF2:
		  case EOF3:
		  case EOV1:
		  case EOV2:
		  case EOV3:
			quit("Unexpected label (%s) in file header.", labelids[id]);
		}
	} while (readlabel());
	if (hdr1 == FALSE)
		quit("Missing HDR1 label in file header.");
	if (hdr2 == FALSE)  recfm = blksz = recsz = 0;
	return(TRUE);
}


readtrailer()
{
	register	id, eof;

	eov = eof = FALSE;
	if (readlabel() == FALSE)  return(FALSE);
	do {
		for (id = 0 ; id < NLABIDS ; id++)
			if (strncmp(labelids[id], labelbuf, 4) == 0)  break;
		switch (id) {
		  case EOV1:
			eov = TRUE;
		  case EOF1:
			eof = TRUE;
			if (sscanf(&labelbuf[55], "%6d", &nblks) != 1) {
				printf("In file %s:  bad format in EOF1 label.\n", name);
				nblks = 0;
			}
			break;

		  case VOL1:
		  case HDR1:
		  case HDR2:
		  case HDR3:
			quit("Unexpected label (%s) in file trailer.", labelids[id]);
		}
	} while (readlabel());
	if (eof == FALSE) {
		printf("In file %s: missing EOF1 or EOV1 label in file trailer.\n", name);
		nblks = 0;
	}
	return(TRUE);
}


printheading(volname)	/* for 't' function */
char	*volname;
{
	printf("Volume: %s\n\n", (vol1? volname : "(missing volume label.)"));
	printf(" set   file recfm recsz blksz nblks creat expir gen ver name\n");
}


printdata()		/* for 't' function */
{
	printf("%6s %4d ", set, file);
	printf(" %1c %1c  ", recfm, form);
	printf("%5d %5d%6d %5d %5d", recsz, blksz, nblks, creatn, expir);
	printf("%4d %3d %s", gen, ver, name);
	if (eov  ||  section != 1)  printf(" (section %d)", section);
	printf("\n");
}


processblock(blklen)		/* 'x' function */
int	blklen;
{
	int		bytcnt;
        register	nextrec, allpad, i;
        register char	 *recptr;

        nextrec = 0;
        while (nextrec < blklen) {
                recptr = &databuf[nextrec];
                if (recfm == 'D') {
                        if (sscanf(recptr, "%4d", &bytcnt) != 1) return;
                        nextrec += bytcnt;
                        if (form != 'M'  &&  noproc == 0) {
                                bytcnt -= 4;
                                recptr += 4;
                        }
                }
                else {
                        allpad = 1;
                        bytcnt = 0;
                        for (i = 1 ; i <= recsz ; i++) {
                                if (*recptr != ' ') bytcnt = i;
                                if (*recptr++ != '^') allpad = 0;
                        }
                        if (allpad) return;
                        recptr = &databuf[nextrec];
                        nextrec += recsz;
                        if (form == 'M'  ||  noproc) bytcnt = recsz;
                }
                if (form == 'A') {
                        bytcnt--;
                        switch (*recptr++) {
                          case '1':
				putc('\n', output);
				putc('\f', output);
                                break;
                          case '+':
				putc('\r', output);
                                break;
                          case '-':
				putc('\n', output);
                          case '0':
				putc('\n', output);
                          default:
				putc('\n', output);
                        }
                }
		while (--bytcnt >= 0) putc(*recptr++, output);
                if (form != 'A'  &&  form != 'M'  &&  noproc == 0)
			putc('\n', output);
        }
}


/* connect disk file name with tape file name */
openfile(filelist, nfiles, function)
char	**filelist;
int	nfiles;
char	function;
{
	char		filename[18],
			*afterslash,
			*rindex();
	register int	i;
	register char	ch, *cp, *p1, *p2;

	if (function == 'x') {
		p1 = name;	/* tape file name */
		p2 = filename;  /* disk file name */
		do {
			ch = *p1++;
			if (ch >= 'A'  &&  ch <= 'Z') ch += ('a' - 'A');
		} while (*p2++ = ch); /* filename = lcase(name) */
		if (nfiles > 0)
                	/* && if current tape file was an argument */
			while (strcmp(filelist[--nfiles], filename) != 0)
				if (nfiles == 0) return(FALSE);
		output = fopen(filename,"w");
		if (output != NULL) return(TRUE);
		printf("Can't open file %s.\n", filename);
		return(FALSE);
	}
	else { /* function is 'c' or 'a' */
		if ((afterslash = rindex(*filelist,'/')) == 0) 
			afterslash = *filelist;
		else afterslash++;	/* forget dir path name */
		strncpy(name,afterslash,17); /* create tape file name */
		name[17] = 0;
		for (i=0; i<17; i++) /* blank fill on right replacing nulls */
			if (name[i] == 0)  name[i] = ' ';
		cp = name;
		while (*cp) {	/* convert to upper case */
			if ((*cp >= 'a') && (*cp <= 'z'))
				*cp = *cp - 'a' + 'A';
			cp++;
		}
		diskfd = open(*filelist,RD);
		if (diskfd > 0) return(TRUE);
		printf("Can't open %s for reading.\n",*filelist);
		return(FALSE);
	}
}

mt(command,count)

/* execute mag tape command */
/* tapefd is assumed opened previously */

short command;
int	count;
{
	struct mtop	mag_op;

	mag_op.mt_op = command;
	mag_op.mt_count = count;
	return((ioctl(tapefd,MTIOCTOP,&mag_op) < 0) ? FALSE : TRUE);
}

tapemark()
{
	if (mt(MTWEOF,1) == FALSE)
		quit("Can't write tape mark on tape device %s.",TAPE);
}

mklabels(lab1,lab2) 	/* write header or trailer labels to tape */

char	*lab1,*lab2;
{
char	label[81];

	/* write HDR1 or EOF1 label */
	sprintf(label,"%s%s%s0001%04d000100 00000 00000 %06dDECDECFILE11A%-7s",
	  lab1,name,set,file,nblks," ");
	if (write(tapefd,label,80) != 80)
	   return(FALSE);

	/* write HDR2 or EOF2 label */
	sprintf(label,"%sF%05d%05d%-35s00%-28s",lab2,blksz,recsz," "," ");
	if (write(tapefd,label,80) != 80)
	   return(FALSE);
        tapemark();	/* mark end of header or trailer */
	return(TRUE);
}

tapewrite()	/* copy disk file to tape */

 /* i/o channels are already opened for tape & disk    */

{
register char	*t,*d;
register int	buflen,count;

	t = tapebuf;
	count = nblks = 0;
	while ((buflen = read(diskfd,databuf,DATBUFSIZ)) != 0) {
		if (buflen < 0)
			quit("Error reading disk file.");
		d = databuf;
		while (buflen --) {
			*t++ = *d++;
			if (++count == blksz) {
				if (write(tapefd,tapebuf,blksz) <= 0)
				   quit("Error writing to tape device %s.",TAPE);
				count = 0; nblks++;
				t = tapebuf;
			}
		}
	}
	if (count > 0) {
		while (count++ < blksz)
			*t++ = '^';
		if (write(tapefd,tapebuf,blksz) <= 0)
			quit("Error writing to tape device %s.",TAPE);
		nblks++;
	}
	tapemark();	/* mark end of file section */
}

volend()	/* position to end of tape volume for append function */
{
	int	oldfile = 0;
	vol1 = FALSE;
	if (readheader() == FALSE) {
		printf("Unexpected tape mark while reading header label.\n");
		exit(1);
	}
	do {
	   if (verbose) printf("Found: %s\n",name);
	   if (mt(MTFSF,1) == FALSE)
		quit("Error while skipping file on tape device %s.",TAPE);
	   if (readtrailer() == FALSE)
		quit("Missing file trailer.");
	   oldfile = file;	/* file sequence number */
	}  while (readheader());
	file = oldfile + 1	/* number of first file to be appended */;
	/* now back up one tape mark since last `readheader' put tape
	   after end of volume */
	if (mt(MTBSF,1) == FALSE)
		quit("Can't position to end of volume on tape device %s.",TAPE);
}

mkvol()	/* write VOL1 label */
{
char	label[81];

	sprintf(label,"VOL1%s%40s1%28s3",volume," "," ");
	if (write(tapefd,label,80) != 80)
	   return(FALSE);
	return(TRUE);
}

main(argc, argv)
int	argc;
char	**argv;
{
	int	len,		/* # bytes read into buffer */
		blkcnt,		/* # blocks read from tape */
		curfile,	/* current file argument  */
		dofile;		/* whether to process current tape file */

        if (argc < 2) {
                printf("Usage:  ansitar {txac[vn]} [files...]\n");
                exit(1);
        }
	if (*argv[1] == '-') argv[1]++;	/* ignore optional '-' in flags */
        while (*argv[1]) {
                switch (*argv[1]) {
                  case 't': /* table of contents */
                  case 'x': /* extract files from tape */
		  case 'c': /* create tape & possibly append files */
		  case 'a': /* append files to tape */
                        if (function != 0) {
                                printf("No multiple functions allowed.\n");
                                exit(1);
                        }
                        function = *argv[1];
                        break;

                  case 'v':
                        verbose++;
                        break;

                  case 'n': /* don't process carriage control */
                        noproc++;
                        break;

		  default:
			printf("%c -- unknown flag.\n", *argv[1]);
			exit(1);
                }
                argv[1]++;
        }
	if (function == 0) {
	   printf("Usage:  ansitar {txac[vn]} [files...]\n");
	   exit(1);
	}

	switch (function) {
	case 'x':
	case 't':
	   if ((tapefd = open(TAPE, RD)) < 0) {
		printf("Can't open tape device %s for reading.\n", TAPE);
		exit(1);
	   }

	   vol1 = FALSE;
	   if (readheader() == FALSE) {
		printf("Unexpected tape mark while reading header label.\n");
		exit(1);
	   }
	   if (function == 't') printheading(volume);
	   do {
		if (blksz > DATBUFSIZ)
			quit("%d byte block size is too big.", blksz);
		if (function == 'x')
		        dofile = openfile(&argv[2], argc-2, function);
		blkcnt = 0;
		while ((len = read(tapefd, databuf, DATBUFSIZ)) != 0) {
		   	if (len < 0) quit("Tape data read error.");
		   	if (hdr2 == FALSE  &&  blksz == 0)  blksz = recsz = len;
		   	blkcnt++;
		   	if (dofile) processblock(len);
	   	}
	   	if (dofile) fclose(output);
		if (readtrailer() == FALSE)
			quit("Missing file trailer.");
		if (function == 't') printdata();
		else if (verbose) {
			printf("%s", (dofile ? "extracted: " : "found:     "));
			printf("%s\n", name);
		}
		if (blkcnt != nblks)
		   printf("In file %s: block count mismatch - %d read.\n",name,blkcnt);
	   } while (readheader());
	   break;

	case 'c':
	case 'a':
	   if ((tapefd = open(TAPE,RW)) < 0) {
		printf("Can't open tape device %s for read/write.\n",TAPE);
		exit(1);
	   }
	   if (function == 'c') {
	      strcpy(volume,VOLNAME);
	      strcpy(set,VOLNAME);
	      file = 1;		/* file sequence in tape volume */
              /* create new tape */
	      if (mkvol() == FALSE) {
		 printf("Can't write VOL1 label on tape device %s.\n", TAPE);
		 exit(1);
	      }
	      if (verbose) 
		 printf("Volume: %s created.\n",volume);
	      function = 'a';
	   }
           else volend();	/* position to end of volume */
	   curfile = 2;		/* current file-name argument */
	   recsz = RECSIZ;
	   blksz = TBLKSIZ;
	   do { /* each file named by an argument */
		nblks = 0;
		if (openfile(&argv[curfile],1,function) == TRUE) {
		   if (mklabels(labelids[HDR1],labelids[HDR2]) == FALSE) 
		      quit("Can't write tape file header for %s.\n", argv[curfile]);
		   /* copy disk file to tape file */
		   tapewrite();

		   if (mklabels(labelids[EOF1],labelids[EOF2]) == FALSE) 
		      quit("Can't write tape file trailer for %s.\n", argv[curfile]);
		   if (verbose)
		      printf("%s written to tape as %s \n",argv[curfile],name);
		   file++;
		   close(diskfd);
		}
		else if (verbose)
		        printf("%s not written to tape.\n",argv[curfile]);
      	   } while (++curfile < argc);
	   tapemark();	/* mark end of tape */
	   break;
	default: ; /* nothing for now.... */
	} /* end case */ 
	exit(0);
} /* end main */



More information about the Comp.unix.wizards mailing list