From:	CRDGW2::CRDGW2::MRGATE::"SMTP::CRVAX.SRI.COM::RELAY-INFO-VAX" 17-MAY-1989 17:46
To:	MRGATE::"ARISIA::EVERHART"
Subj:	tar2vms.c (VMS TAR Part 3 of 4)

Received: From KL.SRI.COM by CRVAX.SRI.COM with TCP; Wed, 17 MAY 89 08:09:36 PDT
Received: from ucbvax.Berkeley.EDU by KL.SRI.COM with TCP; Wed, 17 May 89 07:48:09 PDT
Received: by ucbvax.Berkeley.EDU (5.61/1.36)
	id AA01658; Wed, 17 May 89 07:30:17 -0700
Received: from USENET by ucbvax.Berkeley.EDU with netnews
	for info-vax@kl.sri.com (info-vax@kl.sri.com)
	(contact usenet@ucbvax.Berkeley.EDU if you have questions)
Date: 16 May 89 14:04:58 GMT
From: att!alberta!calgary!.UCalgary.CA!cliff@ucbvax.Berkeley.EDU
Organization: U. of Calgary, Calgary, Alberta, Canada
Subject: tar2vms.c (VMS TAR Part 3 of 4)
Message-Id: <1387@cs-spool.calgary.UUCP>
Sender: info-vax-request@kl.sri.com
To: info-vax@kl.sri.com

/**************************** CUT HERE ****************************** */
/* Read a TAR format tape or file , move files into VMS directories */
/* This source program can be distributed freely provided that the
*  following 4 lines are included:
  Copyright 1986, Sid Penstone,
  Department of Electrical Engineering,
  Queen's University,
  Kingston, Ontario, Canada K7L3N6  (613)-545-5925
*/
/*
* Addresses:
*                 penstone@queensu.ca
*                  PENSTONE@QUCDNEE1.BITNET
*
* Version 2.3C, February 22, 1989
* 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
* 2.3A  - permit hyphens in filename
* 2.3B  - reject '~' in filenames
* 2.3C  - extract/list only those files listed on the command line
*         ("tar absolute" paths, no wildcards, sigh.  This would
*         make the code a pig in the namelist parse/construction phase)
*       - changed modes printout from octal to "rwxrwxrwx" form
*         Cliff Marcellus, U. of Calgary
*/
 
 
/* 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 */
#define M_EXE 01
#define M_WR  02
#define M_RD  04
 
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  */

char modestr[10];
int mask[3]={ 0700, 0070, 0007 };
 
int bytecount,  mode, uic1, uic2, linktype;/* Data from header */
int tarfd;                      /* The input file descriptor */

/* name singly linked-list stuff */
typedef struct nlist {
           char value[NAMSIZE];
           struct nlist *next;
        } Nnode;

#define Create_Nnode	((Nnode *) calloc(1, sizeof(Nnode)))

Nnode *head=NULL,*last=NULL,*Ntmp=NULL;
 
main(argc,argv)
int argc;
char *argv[];
{
int isterm,status,file_type,j,c, flag;
char *make_directory(), *cp;
char *Modes();
 
/* Decode the options and parameters: */
 
    if(argc == 1)
        {
        list = 1;            /* Default for now */
        verbose = 1;
        wait = 0;               /* Don't wait for prompt */
        }
    else {
        --argc;
        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);
            }
       }
   }
   
   if (--argc) {            
      head = Create_Nnode;
      last = Create_Nnode;
      head->next = NULL;
      last = head;
      argv++;
      while (argc--) {
         Ntmp = Create_Nnode;
         last->next = Ntmp;
         last = Ntmp;
         Ntmp->next = NULL;
         strcpy(Ntmp->value, *(++argv));
      };
   };

   if (verbose && head) {
      if (extract) printf("Extracting :\n");
      else printf("Searching for :\n");
      Ntmp = head->next;
      while (Ntmp != NULL) {
         printf("\t%s\n",Ntmp->value);
         Ntmp = Ntmp->next;
      };
      putchar('\n');
   };
      
/* 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 head is != NULL, see if this file is one we should extract/list */
            if (head) {
               Ntmp = head->next;
               while (Ntmp != NULL) {
                  if (strcmp(Ntmp->value, header.title) == 0) break;
                  Ntmp = Ntmp->next;
               };
               if (Ntmp == NULL) { 
                  tarskip(bytecount);
                  continue;
               };
            };
            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("%9s %6d %s %s\n",
                    Modes(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  */

/* free up names linked list */
    if (head != NULL) {
       Ntmp = head->next;
       while (Ntmp->next != NULL) {
          cfree(head);  
          head = Ntmp;
          Ntmp = Ntmp->next;
       };
       cfree(head);
    };
       
    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  for 4.3 */
            case ',':           /* No commas in file names  */
            case '@':           /* No '@' allowed in a name  */
            case '~':           /*  no tildes */
                string[i]= '_';
                flag++;         /* Record if any changes were made */
                break;
            default:
                break;
            }
        }
    return(flag);
}

char *
Modes(mode)
int mode;
{
   int tmp;
   int i;

   for (i=0; i<3; ++i) {
      tmp = mode & mask[i];
      tmp = tmp >> (3*(2 - i));
      if (tmp & M_EXE) modestr[(i*3)+2] = 'x'; else modestr[(i*3)+2] = '-';
      if (tmp & M_WR)  modestr[(i*3)+1] = 'w'; else modestr[(i*3)+1] = '-';
      if (tmp & M_RD)  modestr[i*3] = 'r'; else modestr[i*3] = '-';
   };
 
   modestr[9] = NULL;

   return(modestr);
}
Cliff Marcellus                UUCP     : {any backbone}!calgary!ssg-vax-a!cliff
Institute for Space Research   DOMAIN   : cliff@ssg-vax-a.phys.UCalgary.CA
Dept of Physics and Astronomy  SPAN     : CANCAL::CLIFF
The University of Calgary                 "If it's not fun, don't do it!"

