/* 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); }