/* Read a TAR format tape or file , move files into VMS directories */
/* Copyright 1986, Sid Penstone, 
*  Department of Electrical Engineering,
*  Queen's University,
*  Kingston, Ontario, Canada K7L3N6
* (613)-545-5925
* BITNET: 	   PENSTONE@QUCDNEE1  (Preferred)
*	or  PENSTONE@QUCDN
*
* Version 2.3, Jan.9,1987
* mods:	- corrected header size (thanks to Eric Gisin, U .of Waterloo)
*	- No more of the dreaded QIO's ( "  "  " )
*	- tried to sort out link flag format
*	- uses a tape or a file as input
*	- NOTE: default is NO conversion to vms standard text format (cr) 
* 2.1	- trapped commas in file names, converted to '_'
* 2.2	- reported translations of names
*	- continued after error in opening output file
*	- exit correctly after error opening input file
* 2.3	- fixed bug in make_new on top level file (thanks to Ursula Natrup,
*					natrup@vax.hmi.dfn )
*	- reject "@" in filenames
*/


/* The input data is in record format, length 512, blocks of 10240 bytes;
 */


#include stdio
#include time
#include ssdef
#include iodef
#include descrip
#include ctype

#define ERROR1 -1
#define BUFFSIZE 512
#define ISDIRE 1
#define ISFILE 0
#define NAMSIZE 100
#define SIZE 10240		/* Block size */
#define DSIZE 512		/* Data block size */

struct 			/* A Tar header */
    {
    char title[NAMSIZE];
    char protection[8];
    char field1[8];		/* this is the user id */
    char field2[8];		/*  this is the group id */
    char count[12];		/*  was 11 in error */
    char time[12];		/* UNIX format date  */
    char chksum[8];		/* Header Checksum (ignored) */
    char linkcount;		/* hope this is right */
    char linkname[NAMSIZE]; /* Space for the name of the link */
    char dummy[255];	/* and the rest */
    } header;

static char buffer[DSIZE];	/* BUFFER for a record */

/* Function flags, options:*/
int extract,		/* x option (default) */
    list,			/* t option : list tape contents */
    verbose,		/* v option, report actions */
    wait;

/* Miscellaneous globals, etc. */

char *tarfile = "tape",	/* Input file name  */
    pathname[NAMSIZE],	/* File name as found on tape (UNIX) */
    directory[NAMSIZE],	/* Current directory */
    new_directory[NAMSIZE],	/* Directory of current file */
    top[NAMSIZE],		/* Top level or root */
    newfile[NAMSIZE],	/* VMS format of file name */
    outfile[NAMSIZE],	/* Complete output file specification */
    temp[256],		/* Scratch */
    creation[NAMSIZE],	/* Date as extracted from the TAR file */
    *ctime(),		/* System function */
    linkname[NAMSIZE];	/* Linked file name  */

int bytecount,	mode, uic1, uic2, linktype;/* Data from header */
int tarfd;			/* The input file descriptor */

main(argc,argv)
int argc;
char *argv[];
{
int isterm,status,file_type,j,c, flag;
char *make_directory(), *cp;

/* Decode the options and parameters: */

    if(argc ==1)
    	{
    	extract = 1;		/* Default for now */
	verbose = 1;
	wait = 0;		/* Don't wait for prompt */
	}
    while(--argc > 0)
	{
	cp = argv[1];
	while(c = *cp++)
	    {
	    switch(c)
	    {
	    case 't':
		list=1;
		break;
	    case 'x':
		extract=1;
		break;
	    case 'v':
		verbose=1;
		break;
	    case 'w':
		wait=1;
		break;
	    default:
		printf("Option '%c' not recognized.\n",c);
	    }
       }
   }

       
/* Find if this is a terminal */
    isterm = isatty(0);

/* Set up directory names */
    strcpy(top,getenv("PATH"));

/* Start with the default as the top */
    strcpy(directory,top);

/* open the file for reading */
    if((tarfd = opentar()) <= 0)
	{
	printf("Error opening the Tar tape\n");
	exit(2);
	}
/* Now keep reading headers from this file, and decode the names, etc. */

    while((status=hdr_read(&header))==DSIZE)	/* 0 on end of file */
	{
	if(strlen(header.title)!=0)	/* Valid header */
	    {
	    decode_header();
	    if(extract)
		{
		file_type=scan_title(pathname,new_directory,newfile);
		if( make_new(new_directory)!=0)		
		    printf("Error creating %s\n",new_directory);
		if(file_type == ISDIRE)
		    {}
		if(file_type == ISFILE)
/*  Now move the data into the output file */
		    if(bytecount>0)
			{
			strcpy(outfile,new_directory);
			strcat(outfile,newfile);
			if((j=copyfile(outfile,bytecount))<0)		   
			    printf("Error writing file %s\n",outfile);
			}
		}
	    else			/* listing only */
		{
		printf("%o %6d %s %s\n",
		    mode,bytecount,creation+4,pathname);
		if(linktype == 0)
		    tarskip(bytecount);
		else		
		    printf("     *****( Linked to file: %s)\n",linkname); 
		}
	    }
	else			/* Empty header means the end!!! */
	    {
	    status = 1;
	    printf("End of Tar file found.\n");
	    break;
	    }

	}	/* end while  */
    if(status == 1)			/* Empty header */
	{
	printf("Do you wish to move past the EOF mark ? y/n\n");
	gets(temp);
	if(tolower(temp[0]) == 'y')
	    while((status=hdr_read(&header)) >0);
	else
	    exit(SS$_NORMAL);
	}
    if(status==0)			/* End of tar file  */
	{
	printf("End of file encountered\n");
	exit(SS$_NORMAL);
	}
    if(status<0)			/* An error  */
	{
	printf("Error reading input.\n");
	exit(2);
	}	
}


/* This function simply copies the file to the output, no conversion */

int copyfile(outfile,nbytes)
char outfile[];	/* name of output version */
int nbytes;

{
int inbytes, fil;
/*  Open the output file */
    if((fil=creat(outfile,0)) == ERROR1)
	{
	printf(" Creation error in opening %s \n",outfile);
	tarskip(bytecount);
	return(-2);
	}
    if(linktype !=0)
	{
	sprintf(buffer,"This file is linked to %s\n",linkname);
	write(fil,buffer,strlen(temp));
	}
    else
	{
	while(nbytes>0)
	    {
	    if((inbytes=read(tarfd,buffer,DSIZE)) > 0)
		{
		write(fil,buffer,(nbytes > DSIZE)? DSIZE:nbytes);
		nbytes -= inbytes;
		}
	    else
		{
		printf("End of input file detected\n");
		close(fil);
		return(-1);
		}
	    }
	}
/* Close the file */
    close(fil);
    if(verbose)
	{
	printf("CREATED: %s\n",outfile);
	if(linktype!=0)
	    printf(" *** REAL DATA IS IN: %s\n",linkname);	    	
	}
    return(0);
}

/* Decode a file name into the directory, and the name, return
* a value to indicate if this is a directory name, or another file
* We return the extracted directory string in "dire", and the
* filename (if it exists) in "fname". The full title is in "line"
* at input.
*/

int scan_title(line,dire,fname)
char line[],dire[],fname[];
{
char temp[NAMSIZE],*end1;
int len,len2,i,ind;
/* The format will be UNIX at input, so we have to scan for the
* UNIX directory separator '/'
* If the name ends with '/' then it is actually a directory name.
* If the directory consists only of '.', then don't add a subdirectory
* The output directory will be a complete file spec, based on the default
* directory.
*/
    strcpy(dire,top);			/* Start with the top level */
    if(strncmp(line,"./",2)==0)
	strcpy(line,line+2);		/* ignore "./" */
    strcpy(temp,line);			/* Start in local buffer */
    ind=vms_cleanup(temp);		/* Remove illegal vms characters */
    if((end1=strrchr(temp,'/'))==0)	/* No directory at all  ? */
	strcpy(fname,temp);		/* Only a file name */
    else
	{				/* End of directory name is '/' */
	*end1 = 0;			/* Terminate directory name */
	strcpy(fname,end1+1);		/* File name without directory */
	for (i=0;temp[i];i++)		/* Change '/' to '.' in directory */
	    if(temp[i]=='/')
		temp[i]='.';
	dire[strlen(dire)-1] = (temp[0]=='.')?0:'.' ;
		 /* "." to indicate a subdirectory (unless already there )*/
	strcat(dire,temp); 	/* Add on the new directory  */
	strcat(dire,"]") ;		/* And close with ']' */
	}
    if(strlen(fname)==0)	/* Could this cause problems ? */
	{
	return(ISDIRE);		
	}	
    else
	for(i=0,end1=fname;*end1;end1++) /* Replace multiple . */
	    if(*end1 == '.')
		if(i++)*end1 = '_'; /* After the first */
	if((i>1||ind)&& verbose )	/* Any translations ? */
	    printf("****RENAMED: %s \n         TO: %s\n",line,fname);
    return(ISFILE);	
}

/* Create a new directory, finding out any higher levels that are missing */

/* We will parse the directory name into the next higher directory, and the
* desired directory as "desired.dir".
* Thus: "DEV:[top.sub1.sub2]" is made into "DEV:[top.sub1]sub2.dir" . If
* the directory does not exist , then create the original directory. There
* may be higher levels missing, so we can recurse until we reach the top
* level directory, then work our way back, creating directories at each 
* successive level.
* Bug fix: if the input file was at top level, we will not find a '.'
*	and 'name' will be garbage.
*/

int make_new(want)
char want[];
{
int i,len;
char a[NAMSIZE],parent[NAMSIZE],*end,name[NAMSIZE];
    strcpy(parent,want);
    len = strlen(parent);
    parent[len-1] =0 ;		/* Get rid of the "]" */
    end = strrchr(parent,'.');	/* Find the last '.' */
    if(end != NULL)
	{
	strcpy(a,end+1);	/* Get the last parent */
	strcat(a,".dir");	/* Add the extension */
	*end++ = ']' ;		/* Reduce the directory parent */
	*end = 0;		/* Terminate the directory */
	strcpy(name,parent);
	strcat(name,a);

	if(access(name,0) <0)	/* Does the directory exist ? */
	    {
		if(strcmp(parent,top)!=0) /* No, are we at the top? */
		    if(make_new(parent))   /*  No, look again */
			return(-1);	/* recurse */
		if(mkdir(want,0755,0,0,0)) /* make it */
		    return(-1);		/* Leave on error */
		else
		    if(verbose)
			printf("CREATED: %s\n",want);
		return(0);
	    }
	}
    return(0);
}

 /* Function to open and get data from the blocked input file */
int opentar()
{
int fd;
    fd = open(tarfile, 0, "rfm = fix","mrs = 512");
    if(fd < 0)	
	{
	printf("Can't open input file \n");
	return(0);
	}
    return(fd);
}

/* Get the next file header from the input file buffer. We will always
* move to the next 512 byte boundary.
*/
int hdr_read(buffer)
char *buffer;
{
int stat;
    stat = read(tarfd,buffer,DSIZE);	/* read the header */
    return(stat);				/* Catch them next read ? */
}


/* This is supposed to skip over data to get to the desired position */
/* Position is the number of bytes to skip. We should never have to use
* this during data transfers; just during listings. */
int tarskip(bytes)
int bytes;
{
int i=0;
    while(bytes > 0)
	{
	if((i=read(tarfd,buffer,DSIZE)) == 0)
	    {
	    printf("End of file encountered while skipping.\n");
	    return(-1);
	    }
	bytes -= i; 
	}
    return(0);
}

/* Decode the fields of the header */

int decode_header()
{
int idate, *bintim;
char ll;
bintim = &idate;
    linktype=0; strcpy(linkname,"");
    strcpy(pathname,header.title);		
    sscanf(header.time,"%o",bintim);
    strcpy(creation,ctime(bintim)); 	/* Work on this! */
    creation[24]=0;
    sscanf(header.count,"%o",&bytecount);
    sscanf(header.protection,"%o",&mode);
    sscanf(header.field1,"%o",&uic1);
    sscanf(header.field2,"%o",&uic2);
    /* We may have the link written as binary or as character:  */
    linktype = isdigit(header.linkcount)?
	    (header.linkcount - '0'):header.linkcount;
    if(linktype != 0)
	sscanf(header.linkname,"%s",linkname);
    return(0);
}


/* remove illegal characters from directory and file names; replace
* hyphens and commas with underscores.Returns number of translations
* that were made.
*/
vms_cleanup(string)
char string[];
{
int i,flag=0;
char c;
    for(i=0;c=string[i];i++)
	{
	switch (c)
	    {
	    case '-':		/* No hyphens in file names */
	    case ',':		/* No commas in file names  */
	    case '@':		/* No '@' allowed in a name  */
		string[i]= '_';
		flag++;		/* Record if any changes were made */
		break;
	    default:
		break;
	    }
	}
    return(flag);
}

