/*
 * ostape.c: reads OS standard labeled tapes, using dd to do the real work.
 *
 * Usage: ostape
 *
 * Christine Gianone, CUCCA, December 1986
 *
 */

/* Preprocessor Stuff */

#include <stdio.h>
#include <ctype.h>

#define MAXLEN 90			/* Max length for a label line */
#define MAXFN  17			/* Max length for filename */

#define XVOL1 0				/* Symbols for label identifiers */
#define XEOV1 1 
#define XHDR1 2
#define XHDR2 3
#define XEOF1 4
#define XEOF2 5
#define XUVL1 6
#define XUHL1 7
#define XUTL1 8

/* Global Declarations */

char buff[MAXLEN];			/* Input line buffer */
FILE *fp, *fopen();			/* File pointer & open function */

struct lbl {				/* Label ids and case indexes */
    char *id;
    int code;
};

struct lbl tbl[10] = {
    "VOL1", XVOL1,			/* Volume label */
    "EOV1", XEOV1,			/* End-of-Volume */
    "HDR1", XHDR1,			/* Header 1 */
    "HDR2", XHDR2,			/* Header 2 */
    "EOF1", XEOF1,			/* End of file 1 */
    "EOF2", XEOF2,			/*  and 2 */
    "UVL1", XUVL1,			/* User labels */
    "UHL1", XUHL1,			/*  are */
    "UTL1", XUTL1			/*  ignored... */
};

int nids = (sizeof(tbl) / sizeof(struct lbl));

extern int errno;			/* System error stuff */
extern char *sys_errlist[];

char vn[6];				/* Volume name */
char fn[MAXFN];				/* File name */
int bs;					/* Blocksize */
int rl;					/* Record length */
int bc;					/* Block count in EOF1 */
char rf;				/* Record format */

char *ddhdr = "dd if=/dev/rmt12 of=hdr.tmp ibs=800 cbs=80 conv=unblock,ascii";

/* Main function */

main(argc,argv)
    int argc;				/* Command line arguments */
    char *argv[];			/* (not used) */
    {
	int x;				/* Declare local variables */
	int files = 0;			/* Files processed */
	int skip = 0;			/* Files skipped */
	char ddcmd[200];

	for (files = 0 ; ; )
	  {
	      unlink("hdr.tmp");	/* Delete temp header file */
	      system(ddhdr);

	      if ((x = phdr()) < 0)	/* Process the header */
		break;
	      else if (x == 0)
		{
		    printf("dd cannot read %s in %c format", fn, rf);
		    system("mt fsf 2");	/* Skip forward 2 files */
		    skip++;		/* to next tape header. */
		    continue;
		}
	      sprintf(ddcmd, 
                     "dd if=/dev/rmt12 of=%s ibs=%d cbs=%d conv=unblock,ascii",
		      fn,bs,rl);
	      system(ddcmd);		/* Run the dd command above */
	      sprintf(ddcmd, "ls -l %s", fn); /* to read the tape file */
	      files++;			/* Count the file. */
	      system(ddcmd);		/* Run the list command above */

	      unlink("eof.tmp");	/* Delete old temp trailer file */
	      system(ddhdr);

	      if ((x = peof()) < 0)	/* Process trailer labels */
		break;
	  }
	unlink("hdr.tmp");
	unlink("eof.tmp");
	printf("All done! Files Read: %d -- Files Skipped: %d\n", files, skip);

    }

/* Process a header ... */
/* Returns 1 valid file header with recfm F, */
/*  0 if valid file header with some other recfm (which dd can't handle), or */
/* -1 otherwise - fatal error or end of tape */

phdr()
{    
    int i, j, k, l = 0;
    char c;

    *fn=bs=rl=rf=0;			/* Initialize file variables */
    fp = fopen("hdr.tmp", "r");		/* Try to open header file */
    if (fp == NULL)			/* Check for errors */
      {
	  printf("%s\n",sys_errlist[errno]); /* Oops, can't... */
	  return(-1);
      }
    while (fgets(buff, MAXLEN, fp) != NULL) /* Read each line */
      {
	  switch (k = lookup()) {	/* Got line, look up label id */
	    case XVOL1:			/* VOL1 */
	      buff[10] = 0;
	      printf("VOL: '%s'\n", buff+4); /* Print volid */
	      break;

	    case XEOV1:			/* End of volume */
	      printf("End of volume\n");
	      return(-1);
	      
	    case XHDR1:			/* File header 1 */
	      for (i = 0; i < MAXFN; i++) /* Copy name */
		{
		    c = buff[i+4];	/* and convert to lower case */
		    fn[i] = isupper(c) ? tolower(c) : c;
		}
	      for (i = MAXFN - 1; i > 0; i--) /* Trim trailing blanks */
		{
		    if (fn[i] == ' ')
		      fn[i] = '\0';
		    else break;
		}
	      printf("Filename:  '%s'\n", fn); /* Print the name */
	      break;

	    case XHDR2:			/* File header 2 */
	      buff[15] = 0;
	      rl = atoi (buff + 10);	/* Record length */
	      buff[10] = 0;
	      bs = atoi (buff + 5);	/* Block size */
	      rf = buff[4];		/* Record format */
	      printf ("rl: %d, bs: %d, rf: %c\n", rl, bs, rf);
	      break;

	    case XUVL1:			/* User headers */
	    case XUHL1:			/* (ignored) */
	      break;

	    default:
	      printf("Unexpected ID header label:\n%s\n",buff);
	      if(++l > 3)		/* Give up if too many, */
		return(-1);		/* probably not real headers */
	      break;
	  }
      }
    if (fclose(fp) == EOF)		/* Close the file */
      {
	  printf("%s\n",sys_errlist[errno]);
	  return(-1);
      }
    if (*fn != '\0' )			/* If we have a filename */
      return((rf == 'F') ? 1 : 0);	/* return 1 or 0 based on recfm */
    else return(-1);			/* otherwise probably end of tape */
}

/* Process trailer labels... */
/*  Returns -1 if the trailer label is invalid, otherwise 0. */

peof()
{    
    int i, j, k, l = 0;			/* Local variables */
    char c;
    bc = 0;				/* Block count */

    fp = fopen("eof.tmp", "r");		/* Try to open trailer label file */
    if (fp == NULL)			/* Check for errors */
      {
	  printf("%s\n",sys_errlist[errno]);
	  return(-1);
      }
    while (fgets(buff, MAXLEN, fp) != NULL) /* Read each line */
      {
	  switch (k = lookup())		/* Got line, look up label id */
	    {
	      case XEOF1:		/* End of file 1 */
		buff[60] = 0;
		bc = atoi (buff + 54);	/* Get block count & print it */
		printf("EOF:  %d block%c\n", bc, (bc == 1) ? ' ' : 's');
		break;
		
	      case XEOV1:		/* End of volume */
		printf("End of volume");
		return(-1);

	      case XEOF2:		/* End of file 2 */
		break;

	      case XUTL1:		/* User trailer */
		break;			/*  (ignored) */

	      default:
		printf("Unexpected ID in trailer label:\n%s\n",buff);
		if (++l > 3)		/* If too many unknowns, */
		  return(-1);		/* probably not a real trailer. */
		break;
	    }
      }
    if (fclose(fp) == EOF)		/* Close the file */
      {
	  printf("%s\n",sys_errlist[errno]);
	  return(-1);
      }
    return(0);
}

/* Lookup */

lookup()				/* Look up label ID, */
{					/* return case index, or -1. */
    int i, j;

    for (j = 0; j < nids; j++)		/* For each ID in our list (row) */
      {
	  for (i = 0; i < 4; i++)	/* Compare each character (column) */
	    {
		if (buff[i] != tbl[j].id[i]) /* If chars differ, */
		  break;		/* then try next row, */
		else continue;		/* otherwise keep comparing. */
	    }
	  if (i == 4) return (tbl[j].code); /* i is 4 if all chars match. */
      }
    return (-1);			/* No match, return -1. */
}
