koreth%panarthea.ebay@sun.com (Steven Grimm) (07/20/89)
Submitted-by: 7103_300@uwovax.uwo.ca (Eric R. Smith) Posting-number: Volume 2, Issue 52 Archive-name: gnutar/part06 #!/bin/sh # this is part 6 of a multipart archive # do not concatenate these parts, unpack them in order with /bin/sh # file TAR.C continued # CurArch=6 if test ! -r s2_seq_.tmp then echo "Please unpack part 1 first!" exit 1; fi ( read Scheck if test "$Scheck" != $CurArch then echo "Please unpack part $Scheck next!" exit 1; else exit 0; fi ) < s2_seq_.tmp || exit 1 echo "x - Continuing file TAR.C" sed 's/^X//' << 'SHAR_EOF' >> TAR.C X add_exclude(optarg); X break; X X case 'z': /* Easy to type */ X case 'Z': /* Like the filename extension .Z */ X f_compress++; X break; X X case '?': X badopt: X describe(); X exit(EX_ARGSBAD); X X } X } X X blocksize = blocking * RECORDSIZE; X} X X X/* X * Print as much help as the user's gonna get. X * X * We have to sprinkle in the KLUDGE lines because too many compilers X * cannot handle character strings longer than about 512 bytes. Yuk! X * In particular, MSDOS MSC 4.0 (and 5.0) and PDP-11 V7 Unix have this X * problem. X */ Xvoid Xdescribe() X{ X fprintf(stderr,"%s: choose one of the following:\n",tar); X fputs("\ X-A Append tar files to an archive.\n\ X-c Create a new archive.\n\ X-d Find differences between archive and file system.\n\ X-D Delete from the archive (NOT for use on mag tapes!)\n\ X-r Append files to the end of an archive.\n\ X-t List the contents of an archive.\n\ X-u Only append files that are newer than the copy in an archive.\n\ X-x Extract files from an archive.\n",stderr); X X fputs("\ XOther options:\n\ X-b N blocking factor N (block size = Nx512 bytes)\n\ X-B reblock as we read (for reading 4.2BSD pipes)\n\ X-C dir change to directory DIR\n\ X", stderr); /* KLUDGE */ fputs("\ X-f F read/write archive from file or device F (or hostname:/ForD)\n\ X-G create/list/extract GNU-format backup\n\ X-h don't dump symbolic links; dump the files they point to\n\ X-i ignore blocks of zeros in the archive, which normally mean EOF\n\ X-k keep existing files, don't overwrite them from the archive\n\ X-K file begin at \"file\" in the archive\n\ X-l stay in the local file system (like dump(8)) when creating an archive\n\ X", stderr); /* KLUDGE */ fputs("\ X-m don't extract file modified time\n\ X-M create/list/extract multi-volume archive\n\ X-N date only store files newer than DATE\n\ X-o write an old V7 format archive, rather than ANSI [draft 6] format\n\ X-O extract files to standard output\n\ X-p do extract all protection information\n\ X-R dump record number within archive with each message\n\ X-s list of names to extract is sorted to match the archive\n\ X", stderr); /* KLUDGE */ fputs("\ X-T F get names to extract or create from file F\n\ X-v verbosely list what files we process\n\ X-V vnam create archive with volume name 'vnam'\n\ X-w ask for confirmation for every action\n\ X-W attempt to verify the archive after writing it\n\ X-X file eXclude files listed in \"file\"\n\ X-z or Z run the archive through compress(1)\n\ X-[0-7][lmh] specify drive and density\n\ X", stderr); X} X X X/* X * Set up to gather file names for tar. X * X * They can either come from stdin or from argv. X */ Xname_init(argc, argv) X int argc; X char **argv; X{ X X if (f_namefile) { X if (optind < argc) { X fprintf(stderr, "tar: too many args with -T option\n"); X exit(EX_ARGSBAD); X } X if (!strcmp(name_file, "-")) { X namef = stdin; X } else { X namef = fopen(name_file, "r"); X if (namef == NULL) { X msg_perror("can't open file %s",name_file); X exit(EX_BADFILE); X } X } X } else { X /* Get file names from argv, after options. */ X n_argc = argc; X n_argv = argv; X } X} X X/* X * Get the next name from argv or the name file. X * X * Result is in static storage and can't be relied upon across two calls. X */ X X/* C is non-zero if we should deal with -C */ Xchar * Xname_next(c) X{ X static char buffer[NAMSIZ+2]; /* Holding pattern */ X register char *p; X register char *q; X extern char *un_quote_string(); X X tryagain: X if (namef == NULL) { X /* Names come from argv, after options */ X if (optind < n_argc) { X /* JF trivial support for -C option. I don't know if X chdir'ing at this point is dangerous or not. X It seems to work, which is all I ask. */ X if(c && n_argv[optind][0]=='-' && n_argv[optind][1]=='C' && n_argv[optind][2]=='\0') { X optind++; X if(chdir(n_argv[optind])<0) { X msg_perror("Can't chdir to %s",n_argv[optind]); X } X optind++; X } X /* End of JF quick -C hack */ X X if(f_exclude && check_exclude(n_argv[optind])) { X optind++; X goto tryagain; X } X return un_quote_string(n_argv[optind++]); X } X return (char *)NULL; X } X while(p = fgets(buffer, NAMSIZ+1 /*nl*/, namef)) { X q = p+strlen(p)-1; /* Find the newline */ X if (q <= p) /* Ignore empty lines */ X continue; X *q-- = '\0'; /* Zap the newline */ X while (q > p && *q == '/') /* Zap trailing /s */ X *q-- = '\0'; X if(f_exclude && check_exclude(p)) X goto tryagain; X return un_quote_string(p); X } X return NULL; X} X X X/* X * Close the name file, if any. X */ Xname_close() X{ X X if (namef != NULL && namef != stdin) fclose(namef); X} X X X/* X * Gather names in a list for scanning. X * Could hash them later if we really care. X * X * If the names are already sorted to match the archive, we just X * read them one by one. name_gather reads the first one, and it X * is called by name_match as appropriate to read the next ones. X * At EOF, the last name read is just left in the buffer. X * This option lets users of small machines extract an arbitrary X * number of files by doing "tar t" and editing down the list of files. X */ Xname_gather() X{ X register char *p; X static struct name namebuf[1]; /* One-name buffer */ X static char *chdir_name; X X if (f_sorted_names) { X p = name_next(0); X if (p) { X if(*p=='-' && p[1]=='C' && p[2]=='\0') { X chdir_name=name_next(0); X p=name_next(0); X if(!p) { X fprintf(stderr,"Missing file name after -C\n"); X exit(EX_ARGSBAD); X } X namebuf[0].change_dir=chdir_name; X } X namebuf[0].length = strlen(p); X if (namebuf[0].length >= sizeof namebuf[0].name) { X fprintf(stderr, "Argument name too long: %s\n", X p); X namebuf[0].length = (sizeof namebuf[0].name) - 1; X } X strncpy(namebuf[0].name, p, namebuf[0].length); X namebuf[0].name[ namebuf[0].length ] = 0; X namebuf[0].next = (struct name *)NULL; X namebuf[0].found = 0; X namelist = namebuf; X namelast = namelist; X } X return; X } X X /* Non sorted names -- read them all in */ X while (p = name_next(0)) X addname(p); X} X X/* X * Add a name to the namelist. X */ Xaddname(name) X char *name; /* pointer to name */ X{ X register int i; /* Length of string */ X register struct name *p; /* Current struct pointer */ X static char *chdir_name; X char *new_name(); X#define MAXPATHLEN 1024 X X if(name[0]=='-' && name[1]=='C' && name[2]=='\0') { X chdir_name=name_next(0); X name=name_next(0); X if(!name) { X fprintf(stderr,"Missing file name after -C\n"); X exit(EX_ARGSBAD); X } X if(chdir_name[0]!='/') { X char path[MAXPATHLEN]; X#ifdef MSDOS X int getcwd(); X X if(!getcwd(path,MAXPATHLEN)) X fprintf(stderr,"Couldn't get current dir\n"); X exit(EX_SYSTEM); X#else X char *getwd(); X X if(!getwd(path)) { X fprintf(stderr,"Couldn't get current dir: %s\n",path); X exit(EX_SYSTEM); X } X#endif X chdir_name=new_name(path,chdir_name); X } X } X X i = strlen(name); X /*NOSTRICT*/ X p = (struct name *) X malloc((unsigned)(i + sizeof(struct name) - NAMSIZ)); X if (!p) { X fprintf(stderr,"tar: cannot allocate mem for name %s\n",name); X exit(EX_SYSTEM); X } X p->next = (struct name *)NULL; X p->length = i; X strncpy(p->name, name, i); X p->name[i] = '\0'; /* Null term */ X p->found = 0; X p->regexp = 0; /* Assume not a regular expression */ X p->firstch = 1; /* Assume first char is literal */ X p->change_dir=chdir_name; X p->dir_contents = 0; /* JF */ X if (index(name, '*') || index(name, '[') || index(name, '?')) { X p->regexp = 1; /* No, it's a regexp */ X if (name[0] == '*' || name[0] == '[' || name[0] == '?') X p->firstch = 0; /* Not even 1st char literal */ X } X X if (namelast) namelast->next = p; X namelast = p; X if (!namelist) namelist = p; X} X Xadd_dir_to_name(name,dirc) Xchar *name; Xchar *dirc; X{ X struct name *n; X X for(n=namelist;n;n=n->next) { X if(!strcmp(n->name,name)) { X n->dir_contents = dirc; X return; X } X } X} X X/* X * Match a name from an archive, p, with a name from the namelist. X */ Xname_match(p) X register char *p; X{ X register struct name *nlp; X register int len; X Xagain: X if (0 == (nlp = namelist)) /* Empty namelist is easy */ X return 1; X len = strlen(p); X for (; nlp != 0; nlp = nlp->next) { X /* If first chars don't match, quick skip */ X if (nlp->firstch && nlp->name[0] != p[0]) X continue; X X /* Regular expressions */ X if (nlp->regexp) { X if (wildmat(p, nlp->name)) { X nlp->found = 1; /* Remember it matched */ X if(f_startfile) { X free((void *)namelist); X namelist=0; X } X return 1; /* We got a match */ X } X continue; X } X X /* Plain Old Strings */ X if (nlp->length <= len /* Archive len >= specified */ X && (p[nlp->length] == '\0' || p[nlp->length] == '/') X /* Full match on file/dirname */ X && strncmp(p, nlp->name, nlp->length) == 0) /* Name compare */ X { X nlp->found = 1; /* Remember it matched */ X if(f_startfile) { X free((void *)namelist); X namelist = 0; X } X return 1; /* We got a match */ X } X } X X /* X * Filename from archive not found in namelist. X * If we have the whole namelist here, just return 0. X * Otherwise, read the next name in and compare it. X * If this was the last name, namelist->found will remain on. X * If not, we loop to compare the newly read name. X */ X if (f_sorted_names && namelist->found) { X name_gather(); /* Read one more */ X if (!namelist->found) goto again; X } X return 0; X} X X X/* X * Print the names of things in the namelist that were not matched. X */ Xnames_notfound() X{ X register struct name *nlp; X register char *p; X X for (nlp = namelist; nlp != 0; nlp = nlp->next) { X if (!nlp->found) { X fprintf(stderr, "tar: %s not found in archive\n", X nlp->name); X } X /* X * We could free() the list, but the process is about X * to die anyway, so save some CPU time. Amigas and X * other similarly broken software will need to waste X * the time, though. X */ X#ifndef unix X if (!f_sorted_names) X free(nlp); X#endif X } X namelist = (struct name *)NULL; X namelast = (struct name *)NULL; X X if (f_sorted_names) { X while (0 != (p = name_next(1))) X fprintf(stderr, "tar: %s not found in archive\n", p); X } X} X X/* These next routines were created by JF */ X Xname_expand() X{ X; X} X/* p is a directory. Add all the files in P to the namelist. If any of the X files is a directory, recurse on the subdirectory. . . */ Xstatic Xadd_dir_name(p,device) Xchar *p; Xint device; X{ X char *new_buf; X char *p_buf; X char *get_dir_contents(); X new_buf=get_dir_contents(p,device); X X add_dir_to_name(p,new_buf); X { X char namebuf[NAMSIZ+2]; X register int len; X X (void)strcpy(namebuf,p); X len=strlen(namebuf); X if(namebuf[len-1]!='/') { X namebuf[len++]='/'; X namebuf[len]='\0'; X } X for(p_buf=new_buf;*p_buf;p_buf+=strlen(p_buf)+1) { X if(*p_buf=='D') { X (void)strcpy(namebuf+len,p_buf+1); X addname(namebuf); X add_dir_name(namebuf,device); X } X } X } X} X Xchar * Xget_dir_contents(p,device) Xchar *p; Xint device; X{ X register DIR *dirp; X register struct direct *d; X char namebuf[NAMSIZ+2]; X register int len; X extern int errno; X X#ifdef __GNU__ X void *the_buffer; X#else X char *the_buffer; X#endif X char *buf,*p_buf; X char **vec,**p_vec; X int n_strs,n_size; X char *new_buf; X int dirent_cmp(); X X if(f_local_filesys && device<0) { X struct stat hs; X X if (0 != f_follow_links ? stat(p, &hs) : lstat(p, &hs)) X msg_perror("can't stat %s",namebuf); X else X device=hs.st_dev; X } X errno=0; X dirp=opendir(p); X if(!dirp) { X if(errno) X msg_perror("can't open directory %s",p); X else X msg("error opening directory %s",p); X return "\0\0\0\0"; X } X (void) strcpy(namebuf,p); X if(p[strlen(p)-1]!='/') X (void) strcat(namebuf,"/"); X len=strlen(namebuf); X X the_buffer=init_buffer(); X while(d=readdir(dirp)) { X struct stat hs; X X /* Skip . and .. */ X if(is_dot_or_dotdot(d->d_name)) X continue; X if(DP_NAMELEN(d) + len >=NAMSIZ) { X msg("%s%s name too long: skipped",namebuf,d->d_name); X continue; X } X (void) strcpy(namebuf+len,d->d_name); X if (0 != f_follow_links? stat(namebuf, &hs): lstat(namebuf, &hs)) { X msg_perror("can't stat %s",namebuf); X continue; X } X if((f_new_files && new_time>hs.st_mtime && X new_time>hs.st_ctime && (hs.st_mode&S_IFMT)!=S_IFDIR) X || (f_local_filesys && device>=0 && device!=hs.st_dev)) X add_buffer(the_buffer,"N",1); X else if((hs.st_mode&S_IFMT)==S_IFDIR) X add_buffer(the_buffer,"D",1); X else X add_buffer(the_buffer,"Y",1); X add_buffer(the_buffer,d->d_name,(int)(DP_NAMELEN(d)+1)); X } X add_buffer(the_buffer,"\000\000",2); X closedir(dirp); X X /* Well, we've read in the contents of the dir, now sort them */ X buf=get_buffer(the_buffer); X n_strs=0; X n_size=0; X for(p_buf=buf;*p_buf;) { X int tmp; X X tmp=strlen(p_buf)+1; X n_strs++; X n_size+=tmp; X p_buf+=tmp; X } X if(n_strs==0) { X flush_buffer(the_buffer); X return "\0\0\0\0"; X } X vec=(char **)malloc(sizeof(char *)*(n_strs+1)); X for(p_vec=vec,p_buf=buf;*p_buf;p_buf+=strlen(p_buf)+1) X *p_vec++= p_buf; X *p_vec= 0; X qsort((void *)vec,n_strs,sizeof(char *),dirent_cmp); X new_buf=(char *)malloc(n_size+2); X for(p_vec=vec,p_buf=new_buf;*p_vec;p_vec++) { X char *p_tmp; X X for(p_tmp= *p_vec;*p_buf++= *p_tmp++;) X ; X } X *p_buf++='\0'; X free(vec); X flush_buffer(the_buffer); X return new_buf; X} X Xint dirent_cmp(p1,p2) Xchar **p1,**p2; X{ X char *frst,*scnd; X X frst= (*p1)+1; X scnd= (*p2)+1; X X return strcmp(frst,scnd); X} X X/* This is like name_match(), except that it returns a pointer to the name X it matched, and doesn't set ->found The caller will have to do that X if it wants to. Oh, and if the namelist is empty, it returns 0, unlike X name_match(), which returns TRUE */ X Xstruct name * Xname_scan(p) Xregister char *p; X{ X register struct name *nlp; X register int len; X Xagain: X if (0 == (nlp = namelist)) /* Empty namelist is easy */ X return 0; X len = strlen(p); X for (; nlp != 0; nlp = nlp->next) { X /* If first chars don't match, quick skip */ X if (nlp->firstch && nlp->name[0] != p[0]) X continue; X X /* Regular expressions */ X if (nlp->regexp) { X if (wildmat(p, nlp->name)) X return nlp; /* We got a match */ X continue; X } X X /* Plain Old Strings */ X if (nlp->length <= len /* Archive len >= specified */ X && (p[nlp->length] == '\0' || p[nlp->length] == '/') X /* Full match on file/dirname */ X && strncmp(p, nlp->name, nlp->length) == 0) /* Name compare */ X return nlp; /* We got a match */ X } X X /* X * Filename from archive not found in namelist. X * If we have the whole namelist here, just return 0. X * Otherwise, read the next name in and compare it. X * If this was the last name, namelist->found will remain on. X * If not, we loop to compare the newly read name. X */ X if (f_sorted_names && namelist->found) { X name_gather(); /* Read one more */ X if (!namelist->found) goto again; X } X return (struct name *) 0; X} X X/* This returns a name from the namelist which doesn't have ->found set. X It sets ->found before returning, so successive calls will find and return X all the non-found names in the namelist */ X Xstruct name *gnu_list_name; X Xchar * Xname_from_list() X{ X if(!gnu_list_name) X gnu_list_name = namelist; X while(gnu_list_name && gnu_list_name->found) X gnu_list_name=gnu_list_name->next; X if(gnu_list_name) { X gnu_list_name->found++; X if(gnu_list_name->change_dir) X if(chdir(gnu_list_name->change_dir)<0) X msg_perror("can't chdir to %s",gnu_list_name->change_dir); X return gnu_list_name->name; X } X return (char *)0; X} X Xblank_name_list() X{ X struct name *n; X X gnu_list_name = 0; X for(n=namelist;n;n=n->next) X n->found = 0; X} X X/* Collect all the names from argv[] (or whatever), then expand them into X a directory tree, and put all the directories at the beginning. */ Xcollect_and_sort_names() X{ X struct name *n,*n_next; X int num_names; X int name_cmp(); X char *merge_sort(); X X name_gather(); X X if(!namelist) addname("."); X for(n=namelist;n;n=n_next) { X n_next=n->next; X if(n->found || n->dir_contents) X continue; X if(n->regexp) /* FIXME just skip regexps for now */ X continue; X if(n->change_dir) X if(chdir(n->change_dir)<0) { X msg_perror("can't chdir to %s",n->change_dir); X continue; X } X X if(is_a_directory(n->name)) { X n->found++; X add_dir_name(n->name,-1); X } X } X X num_names=0; X for(n=namelist;n;n=n->next) X num_names++; X namelist=(struct name *)merge_sort((void *)namelist,num_names,(char *)(&(namelist->next))-(char *)namelist,name_cmp); X X for(n=namelist;n;n=n->next) { X n->found=0; X } X} X Xint name_cmp(n1,n2) Xstruct name *n1,*n2; X{ X if(n1->found) { X if(n2->found) X return strcmp(n1->name,n2->name); X else X return -1; X } else if(n2->found) X return 1; X else X return strcmp(n1->name,n2->name); X} X Xchar *new_name(); X Xgnu_restore(name) Xchar *name; X{ X char *current_dir; X/* int current_dir_length; */ X X char *archive_dir; X/* int archive_dir_length; */ X#ifdef __GNU__ X void *the_buffer; X#else X char *the_buffer; X#endif X char *p; X DIR *dirp; X struct direct *d; X char *cur,*arc; X extern struct stat hstat; /* Stat struct corresponding */ X long size,copied; X char *from,*to; X X dirp=opendir(name); X X if(!dirp) { X /* The directory doesn't exist now. It'll be created. X In any case, we don't have to delete any files out X of it */ X return; X } X X the_buffer=init_buffer(); X while(d=readdir(dirp)) { X if(is_dot_or_dotdot(d->d_name)) X continue; X X add_buffer(the_buffer,d->d_name,(int)(DP_NAMELEN(d)+1)); X } X closedir(dirp); X add_buffer(the_buffer,"",1); X X current_dir=get_buffer(the_buffer); X archive_dir=(char *)malloc(hstat.st_size); X if(archive_dir==0) { X fprintf(stderr,"Can't allocate %d bytes for restore\n",hstat.st_size); X return; X } X to=archive_dir; X for(size=hstat.st_size;size>0;size-=copied) { X from=findrec()->charptr; X if(!from) { X msg("Unexpected EOF in archive\n"); X break; X } X copied=endofrecs()->charptr - from; X if(copied>size) X copied=size; X bcopy((void *)from,(void *)to,(int)copied); X to+=copied; X userec((union record *)(from+copied-1)); X } X X for(cur=current_dir;*cur;cur+=strlen(cur)+1) { X for(arc=archive_dir;*arc;arc+=strlen(arc)+1) { X arc++; X if(!strcmp(arc,cur)) X break; X } X if(*arc=='\0') { X p=new_name(name,cur); X if(f_confirm && !confirm("delete",p)) { X free(p); X continue; X } X if(f_verbose) X printf("%s: deleting %s\n",p); X if(recursively_delete(p)) { X msg("%s: Error while deleting\n",p); X } X free(p); X } X X } X flush_buffer(the_buffer); X free(archive_dir); X} X Xrecursively_delete(path) Xchar *path; X{ X struct stat sbuf; X DIR *dirp; X struct direct *dp; X char *path_buf; X /* int path_len; */ X X X if(lstat(path,&sbuf)<0) X return 1; X if((sbuf.st_mode &S_IFMT)==S_IFDIR) { X X /* path_len=strlen(path); */ X dirp=opendir(path); X if(dirp==0) X return 1; X while(dp=readdir(dirp)) { X if(is_dot_or_dotdot(dp->d_name)) X continue; X path_buf=new_name(path,dp->d_name); X if(recursively_delete(path_buf)) { X free(path_buf); X closedir(dirp); X return 1; X } X free(path_buf); X } X closedir(dirp); X X if(rmdir(path)<0) X return 1; X return 0; X } X if(unlink(path)<0) X return 1; X return 0; X} X Xchar * Xnew_name(path,name) Xchar *path,*name; X{ X char *path_buf; X X path_buf=(char *)malloc(strlen(path)+strlen(name)+2); X if(path_buf==0) { X fprintf(stderr,"Can't allocate memory for name '%s/%s\n",path,name); X exit(EX_SYSTEM); X } X (void) sprintf(path_buf,"%s/%s",path,name); X return path_buf; X} X X/* return non-zero if p is the name of a directory */ Xis_a_directory(p) Xchar *p; X{ X struct stat stbuf; X X if(lstat(p,&stbuf)<0) { X msg_perror("can't stat %s",p); X return 0; X } X if((stbuf.st_mode&S_IFMT)==S_IFDIR) X return 1; X return 0; X} X X/* Returns non-zero if p is . or .. This could be a macro for speed. */ Xis_dot_or_dotdot(p) Xchar *p; X{ X return (p[0]=='.' && (p[1]=='\0' || (p[1]=='.' && p[2]=='\0'))); X} X X/* returns non-zero if the luser typed 'y' or 'Y', zero otherwise. */ X Xint Xconfirm(action,file) Xchar *action, *file; X{ X int c,nl; X static FILE *confirm_file = 0; X extern FILE *msg_file; X X fprintf(msg_file,"%s %s?", action, file); X if(!confirm_file) { X#ifdef atarist X confirm_file = (archive == 0) ? fopen("CON:", 0) : stdin; X#else X confirm_file = (archive == 0) ? fopen("/dev/tty") : stdin; X#endif X if(!confirm_file) { X fprintf(stderr,"Can't read confirmation from user\n"); X exit(EX_SYSTEM); X } X } X c=getc(confirm_file); X for(nl = c; nl != '\n' && nl != EOF; nl = getc(confirm_file)) X ; X return (c=='y' || c=='Y'); X} X Xchar *x_buffer = 0; Xint size_x_buffer; Xint free_x_buffer; X Xchar **exclude = 0; Xint size_exclude = 0; Xint free_exclude = 0; X Xchar **re_exclude = 0; Xint size_re_exclude = 0; Xint free_re_exclude = 0; X Xadd_exclude(file) Xchar *file; X{ X FILE *fp; X char buf[1024]; X extern char *rindex(); X X fp=fopen(file,"r"); X if(!fp) { X msg_perror("can't open %s",file); X exit(2); X } X while(fgets(buf,1024,fp)) { X int size_buf; X char *end_str; X X end_str=rindex(buf,'\n'); X *end_str='\0'; X X un_quote_string(buf); X size_buf = strlen(buf); X X if(x_buffer==0) { X x_buffer = (char *)ck_malloc(size_buf+1024); X free_x_buffer=1024; X } else if(free_x_buffer<=size_buf) { X char *old_x_buffer; X char **tmp_ptr; X X old_x_buffer = x_buffer; X x_buffer = (char *)ck_realloc(x_buffer,size_x_buffer+1024); X free_x_buffer = 1024; X for(tmp_ptr=exclude;tmp_ptr<exclude+size_exclude;tmp_ptr++) X *tmp_ptr= x_buffer + ((*tmp_ptr) - old_x_buffer); X for(tmp_ptr=re_exclude;tmp_ptr<re_exclude+size_re_exclude;tmp_ptr++) X *tmp_ptr= x_buffer + ((*tmp_ptr) - old_x_buffer); X } X X if(is_regex(buf)) { X if(free_re_exclude==0) { X re_exclude= (char **)(re_exclude ? ck_realloc(re_exclude,(size_re_exclude+32)*sizeof(char *)) : ck_malloc(sizeof(char *)*32)); X free_re_exclude+=32; X } X re_exclude[size_re_exclude]=x_buffer+size_x_buffer; X size_re_exclude++; X free_re_exclude--; X } else { X if(free_exclude==0) { X exclude=(char **)(exclude ? ck_realloc(exclude,(size_exclude+32)*sizeof(char *)) : ck_malloc(sizeof(char *)*32)); X free_exclude+=32; X } X exclude[size_exclude]=x_buffer+size_x_buffer; X size_exclude++; X free_exclude--; X } X strcpy(x_buffer+size_x_buffer,buf); X size_x_buffer+=size_buf+1; X free_x_buffer-=size_buf+1; X } X fclose(fp); X} X Xint Xis_regex(str) Xchar *str; X{ X return index(str,'*') || index(str,'[') || index(str,'?'); X} X Xint Xcheck_exclude(name) Xchar *name; X{ X int n; X X for(n=0;n<size_re_exclude;n++) { X if(wildmat(name,re_exclude[n])) X return 1; X } X for(n=0;n<size_exclude;n++) { X if(strstr(name,exclude[n])) X return 1; X } X return 0; X} SHAR_EOF echo "File TAR.C is complete" chmod 0644 TAR.C || echo "restore of TAR.C fails" echo "x - extracting TAR.H (Text)" sed 's/^X//' << 'SHAR_EOF' > TAR.H && X/* X X Copyright (C) 1988 Free Software Foundation X XGNU tar is distributed in the hope that it will be useful, but WITHOUT ANY XWARRANTY. No author or distributor accepts responsibility to anyone Xfor the consequences of using it or for whether it serves any Xparticular purpose or works at all, unless he says so in writing. XRefer to the GNU tar General Public License for full details. X XEveryone is granted permission to copy, modify and redistribute GNU tar, Xbut only under the conditions described in the GNU tar General Public XLicense. A copy of this license is supposed to have been given to you Xalong with GNU tar so you can know your rights and responsibilities. It Xshould be in a file named COPYING. Among other things, the copyright Xnotice and this notice must be preserved on all copies. X XIn other words, go ahead and share GNU tar, but don't try to stop Xanyone else from sharing it farther. Help stamp out software hoarding! X*/ X X/* X * Header file for tar (tape archive) program. X * X * @(#)tar.h 1.24 87/11/06 X * X * Created 25 August 1985 by John Gilmore, ihnp4!hoptoad!gnu. X */ X X/* X * Kludge for handling systems that can't cope with multiple X * external definitions of a variable. In ONE routine (tar.c), X * we #define TAR_EXTERN to null; here, we set it to "extern" if X * it is not already set. X */ X#ifndef TAR_EXTERN X#define TAR_EXTERN extern X#endif X X/* X * Header block on tape. X * X * I'm going to use traditional DP naming conventions here. X * A "block" is a big chunk of stuff that we do I/O on. X * A "record" is a piece of info that we care about. X * Typically many "record"s fit into a "block". X */ X#define RECORDSIZE 512 X#define NAMSIZ 100 X#define TUNMLEN 32 X#define TGNMLEN 32 X Xunion record { X char charptr[RECORDSIZE]; X struct header { X char name[NAMSIZ]; X char mode[8]; X char uid[8]; X char gid[8]; X char size[12]; X char mtime[12]; X char chksum[8]; X char linkflag; X char linkname[NAMSIZ]; X char magic[8]; X char uname[TUNMLEN]; X char gname[TGNMLEN]; X char devmajor[8]; X char devminor[8]; X /* these following fields were added by JF for gnu */ X /* and are NOT standard */ X char atime[12]; X char ctime[12]; X char offset[12]; X char longnames[4]; X } header; X}; X X/* The checksum field is filled with this while the checksum is computed. */ X#define CHKBLANKS " " /* 8 blanks, no null */ X X/* The magic field is filled with this if uname and gname are valid. */ X#define TMAGIC "ustar " /* 7 chars and a null */ X X/* The linkflag defines the type of file */ X#define LF_OLDNORMAL '\0' /* Normal disk file, Unix compat */ X#define LF_NORMAL '0' /* Normal disk file */ X#define LF_LINK '1' /* Link to previously dumped file */ X#define LF_SYMLINK '2' /* Symbolic link */ X#define LF_CHR '3' /* Character special file */ X#define LF_BLK '4' /* Block special file */ X#define LF_DIR '5' /* Directory */ X#define LF_FIFO '6' /* FIFO special file */ X#define LF_CONTIG '7' /* Contiguous file */ X/* Further link types may be defined later. */ X X#define LF_DUMPDIR 'D' /* This is a dir entry that contains X the names of files that were in X the dir at the time the dump X was made */ X#define LF_MULTIVOL 'M' /* This is the continuation X of a file that began on another X volume */ X#define LF_VOLHDR 'V' /* This file is a tape/volume header */ X /* Ignore it on extraction */ X X/* X * Exit codes from the "tar" program X */ X#define EX_SUCCESS 0 /* success! */ X#define EX_ARGSBAD 1 /* invalid args */ X#define EX_BADFILE 2 /* invalid filename */ X#define EX_BADARCH 3 /* bad archive */ X#define EX_SYSTEM 4 /* system gave unexpected error */ X X X/* X * Global variables X */ XTAR_EXTERN union record *ar_block; /* Start of block of archive */ XTAR_EXTERN union record *ar_record; /* Current record of archive */ XTAR_EXTERN union record *ar_last; /* Last+1 record of archive block */ XTAR_EXTERN char ar_reading; /* 0 writing, !0 reading archive */ XTAR_EXTERN int blocking; /* Size of each block, in records */ XTAR_EXTERN int blocksize; /* Size of each block, in bytes */ XTAR_EXTERN char *ar_file; /* File containing archive */ XTAR_EXTERN char *name_file; /* File containing names to work on */ XTAR_EXTERN char *tar; /* Name of this program */ X X/* X * Flags from the command line X */ XTAR_EXTERN int cmd_mode; X#define CMD_NONE 0 X#define CMD_CAT 1 /* -A */ X#define CMD_CREATE 2 /* -c */ X#define CMD_DIFF 3 /* -d */ X#define CMD_APPEND 4 /* -r */ X#define CMD_LIST 5 /* -t */ X#define CMD_UPDATE 6 /* -u */ X#define CMD_EXTRACT 7 /* -x */ X#define CMD_DELETE 8 /* -D */ X X /* -[0-9][lmh] */ X /* CMD_CAT -A */ X /* -b */ XTAR_EXTERN char f_reblock; /* -B */ X /* CMD_CREATE -c */ X /* -C */ X /* CMD_DIFF -d */ X/* TAR_EXTERN char f_dironly; /* -D */ X /* -f */ XTAR_EXTERN char f_gnudump; /* -G */ XTAR_EXTERN char f_follow_links; /* -h */ XTAR_EXTERN char f_ignorez; /* -i */ X /* CMD_DELETE -J */ XTAR_EXTERN char f_keep; /* -k */ XTAR_EXTERN char f_startfile; /* -K */ XTAR_EXTERN char f_local_filesys; /* -l */ XTAR_EXTERN char f_modified; /* -m */ XTAR_EXTERN char f_multivol; /* -M */ XTAR_EXTERN char f_new_files; /* -N */ XTAR_EXTERN char f_oldarch; /* -o */ XTAR_EXTERN char f_exstdout; /* -O */ XTAR_EXTERN char f_use_protection; /* -p */ XTAR_EXTERN char f_sayblock; /* -R */ XTAR_EXTERN char f_sorted_names; /* -s */ XTAR_EXTERN char f_namefile; /* -T */ X /* CMD_UPDATE -u */ XTAR_EXTERN char f_verbose; /* -v */ XTAR_EXTERN char *f_volhdr; /* -V */ XTAR_EXTERN char f_confirm; /* -w */ XTAR_EXTERN char f_verify; /* -W */ X /* CMD_EXTRACT -x */ XTAR_EXTERN char f_exclude; /* -X */ XTAR_EXTERN char f_compress; /* -z */ X /* -Z */ X X/* X * We now default to Unix Standard format rather than 4.2BSD tar format. X * The code can actually produce all three: X * f_standard ANSI standard X * f_oldarch V7 X * neither 4.2BSD X * but we don't bother, since 4.2BSD can read ANSI standard format anyway. X * The only advantage to the "neither" option is that we can cmp(1) our X * output to the output of 4.2BSD tar, for debugging. X */ X#define f_standard (!f_oldarch) X X/* X * Structure for keeping track of filenames and lists thereof. X */ Xstruct name { X struct name *next; X short length; /* cached strlen(name) */ X char found; /* A matching file has been found */ X char firstch; /* First char is literally matched */ X char regexp; /* This name is a regexp, not literal */ X char *change_dir; /* JF set with the -C option */ X char *dir_contents; /* JF for f_gnudump */ X char name[NAMSIZ+1]; X}; X XTAR_EXTERN struct name *namelist; /* Points to first name in list */ XTAR_EXTERN struct name *namelast; /* Points to last name in list */ X XTAR_EXTERN int archive; /* File descriptor for archive file */ XTAR_EXTERN int errors; /* # of files in error */ X X/* X * X * Due to the next struct declaration, each routine that includes X * "tar.h" must also include <sys/types.h>. I tried to make it automatic, X * but System V has no defines in <sys/types.h>, so there is no way of X * knowing when it has been included. In addition, it cannot be included X * twice, but must be included exactly once. Argghh! X * X * Thanks, typedef. Thanks, USG. X */ Xstruct link { X struct link *next; X dev_t dev; X ino_t ino; X short linkcount; X char name[NAMSIZ+1]; X}; X XTAR_EXTERN struct link *linklist; /* Points to first link in list */ X X X/* X * Error recovery stuff X */ XTAR_EXTERN char read_error_flag; X X X/* X * Declarations of functions available to the world. X */ Xunion record *findrec(); Xvoid userec(); Xunion record *endofrecs(); Xvoid anno(); Xvoid msg(); Xvoid msg_perror(); X/* #define annorec(stream, msg) anno(stream, msg, 0) /* Cur rec */ X/* #define annofile(stream, msg) anno(stream, msg, 1) /* Saved rec */ SHAR_EOF chmod 0644 TAR.H || echo "restore of TAR.H fails" echo "x - extracting TAR.TEX (Text)" sed 's/^X//' << 'SHAR_EOF' > TAR.TEX && X\input texinfo @c -*-texinfo-*- X X@setfilename tar X@settitle tar X@ifinfo XThis file documents the tape archive of the GNU system. X XCopyright (C) 1988 Free Software Foundation, Inc. X XPermission is granted to make and distribute verbatim copies of this Xmanual provided the copyright notice and this permission notice are Xpreserved on all copies. X X@ignore XPermission is granted to process this file through TeX and print the Xresults, provided the printed document carries copying permission Xnotice identical to this one except for the removal of this paragraph X(this paragraph not being relevant to the printed manual). X X@end ignore XPermission is granted to copy and distribute modified versions of Xthis manual under the conditions for verbatim copying, provided that Xthe entire resulting derived work is distributed under the terms of Xa permission notice identical to this one. X XPermission is granted to copy and distribute translations of this Xmanual into another language, under the above conditions for Xmodified versions. X@end ifinfo X X@iftex X@finalout X@end iftex X X@titlepage X@center @titlefont{tar} X@sp 1 X@center The GNU tape archive X@sp 2 X@center Jay Fenlason X@sp 3 XThis manual describes the GNU tape archiver, @code{tar}, and how you Xcan use it to store copies of a file or a group of files in an X@dfn{archive}. This archive may be written directly to a magnetic Xtape or other storage medium, stored as a file, or sent through a Xpipe to another program. @code{tar} can also be used to add files Xto an already existing archive, list the files in an archive, or Xextract the files in the archive. X XGNU @code{tar} was written by John Gilmore, and modified by many Xpeople. The GNU enhancements were written by Jay Fenlason. X@page X@vskip 0pt plus 1filll XCopyright @copyright{} 1988 Free Software Foundation, Inc. X XPermission is granted to make and distribute verbatim copies of Xthis manual provided the copyright notice and this permission notice Xare preserved on all copies. X X@ignore XPermission is granted to process this file through Tex and print the Xresults, provided the printed document carries copying permission Xnotice identical to this one except for the removal of this paragraph X(this paragraph not being relevant to the printed manual). X X@end ignore XPermission is granted to copy and distribute modified versions of this Xmanual under the conditions for verbatim copying, provided that the entire Xresulting derived work is distributed under the terms of a permission Xnotice identical to this one. X XPermission is granted to copy and distribute translations of this manual Xinto another language, under the same conditions as for modified versions. X X@end titlepage X X@ifinfo X@node Top, Why, , (dir) X@ichapter Using the Tape Archiver X@menu X* Why:: What @code{tar} archives are good for. X* Commands:: How to tell @code{tar} what to do. X* Options:: Options that change the way @code{tar} behaves. X* Format:: The format of a @code{tar} archive. X@end menu X@end ifinfo X X@node Why, Commands, Top, Top X@chapter The Uses of Tape Archives X XThe tape archiver @code{tar} allows you to store many files in an X@dfn{archive file} or @dfn{tar file} which describes the names and contents Xof the constituent files. Later you can extract some or all of these files Xfrom the archive. X XTar files are not restricted to magnetic tapes. The @code{tar} program Xcan equally well use an ordinary file, or a pipe, or any device, as the Xarchive. But they were originally designed for use with magnetic tapes, Xand that is how the name ``tar'' came about. X XArchive files can be used for transporting a group of files from one system Xto another: put all relevant files into an archive on one computer system, Xtransfer the archive to another, and extract the contents there. The basic Xtransfer medium might be magnetic tape, Internet FTP, or even electronic Xmail (though you must encode the archive with @code{uuencode} in order to Xtransport it properly by mail). Both machines do not have to use the same Xoperating system, as long as they both support the @code{tar} program. X XA magnetic tape can store several files in sequence, but has no names for Xthem, just relative position on the tape. A tar file or something like it Xis the only way to store several files on one tape and retain their names. XEven when the basic transfer mechanism can keep track of names, as FTP can, Xthe nuisance of handling multiple files, directories, and multiple links, Xmay make a tar file a much easier method. X XArchive files are also used for long-term storage, which you can think Xof as transportation from one time to another. X XPiping one @code{tar} to another is an easy way to copy a directory's Xcontents from one disk to another, while preserving the dates, modes, owners Xand link-structure of all the files therein. X XThe GNU version of @code{tar} has special features that allow it to be Xused to make incremental and full dumps of all the files in a Xfilesystem. X X@node Commands, Options, Why, Top X@chapter The Different Operations @code{tar} Can Perform X XOne program, @code{tar}, is used to create an archive, to extract files Xfrom an archive, to modify an archive, or to list the contents. Each Xtime you run @code{tar}, you must give a @dfn{command} to specify which Xone of these things you want to do. X XThe command must always be in the first argument to @code{tar}. This Xargument can also contain options (@pxref{Options}). For compatibility Xwith Unix @code{tar}, the first argument is always treated as containing Xcommand and option letters even if it doesn't start with @samp{-}. Thus, X@samp{tar c} is equivalent to @samp{tar -c}: both of them specify the X@samp{-c} command to create an archive. X XThe remaining arguments to @code{tar} are either options, if they start Xwith @samp{-}, or files to operate on. X XThe file names that you give as argument are the files that @code{tar} will Xact on--for example, they are the files to put in the archive, or the files Xto extract from it. If you don't give any file name arguments, the default Xdepends on which command you used. Some commands use all relevant files; Xsome commands have no default and will report an error if you don't specify Xfiles. X XIf a file name argument actually names a directory, then that directory Xand all files and subdirectories in it are used. X XHere is a list of all the @code{tar} commands: X X@table @samp X@item -c XThe @samp{-c} command tells @code{tar} to create a new archive that Xcontains the file(s) specified on the command line. If you don't Xspecify files, all the files in the current directory are used. X XIf the archive file already exists, it is overwritten; the old contents Xare lost. X X@item -d XThe @samp{-d} command causes @code{tar} to compare the archive with Xthe files in the file system. It will report differences in file Xsize, mode, owner, and contents. If a file exists in the archive, but Xnot in the file system, @code{tar} will report this. X XIf you specify file names, those files are compared with the tape and Xthey must all exist in the archive. If you don't specify files, all Xthe files in the archive are compared. X X@item -r XThe @samp{-r} command causes @code{tar} to add the specified file(s) Xto the end of the archive. This assumes that the archive file already Xexists and is in the proper format (which probably means it was Xcreated previously with the @code{tar} program). If the archive is Xnot in a format that @code{tar} understands, the results will be Xunpredictable. X XYou must specify the files to be used; there is no default. X X@item -t XThe @samp{-t} command causes @code{tar} to display a list of the files Xin the archive. If you specify file names, only the files Xthat you specify will be mentioned (but each of them is mentioned only Xif it appears in the archive). X X@item -u XThe @samp{-u} command causes @code{tar} to add the specified files to Xthe end of the archive, like @w{@samp{-r}}, but only when a file doesn't Xalready exist in the archive or is newer than the version in the Xarchive (last-modification time is compared). This command can be Xvery slow. X XYou must specify the files to be used; there is no default. X X@item -x XThe @samp{-x} command causes @code{tar} to extract the specified files Xfrom the archive. If no file names are given, all the files in the Xarchive will be extracted. X X@item -A XThe @samp{-A} command is used for concatenating several archive files Xinto one big archive file. The files to operate on should all be Xarchive files. They are all appended to the end of @emph{the} archive Xfile which @code{tar} works on. (The other files are not changed). X XYou might be tempted to use @code{cat} for this, but it won't Xordinarily work. A @code{tar} archive contains data which indicates Xthe end of the archive, so more material added to the end with X@code{cat} would be ignored. The @samp{tar -A} command works because Xit removes the end-of-archive markers from the middle of the result. X X@item -D XThe @samp{-D} command causes @code{tar} to delete the specified files Xfrom the archive. This command is extremely slow. Warning: Use of Xthis command on archives stored on magnetic tape may result in a Xscrambled archive. There is no safe way (except for completely Xre-writing the archive) to delete files from a magnetic tape. X@end table X X@node Options, General Options, Commands, Top X@chapter Options That Change How @code{tar} Works X XOptions may be specified as individual arguments starting with @samp{-}. XIn this case, if the option wants an argument (as does, for example, X@samp{-f}) then the argument should come after the option, separated Xfrom it by a space. XAll options are optional. Some options make sense with any command, while Xothers are meaningful only with particular commands.@refill X X@menu X* General Options:: X* Creation Options:: X* Extraction Options:: X* Option Syntax:: X@end menu X X@node General Options, Creation Options, Options, Options X@section Options That Are Always Meaningful X X@table @code X@item -b @var{number} XThis option is used to specify a @dfn{blocking factor} for the Xarchive. When reading or writing the archive, @code{tar}, will do Xreads and writes of the archive in blocks of @var{number}*512 bytes. X XThe default blocking factor is set when @code{tar} is compiled, and is Xtypically 20. X XBlocking factors larger than 20 cannot be read by very old versions of X@code{tar}, or by some newer versions of @code{tar} running on old machines Xwith small address spaces. X XWith a magnetic tape, larger blocks give faster throughput and fit Xmore data on a tape (because there are fewer inter-record gaps). If Xthe archive is in a disk file or a pipe, you may want to specify a Xsmaller blocking factor, since a large one will result in a large Xnumber of null bytes at the end of the archive. X XWhen writing cartridge or other streaming tapes, a much larger Xblocking factor (say 126 or more) will greatly increase performance. XHowever, you must specify the same blocking factor when reading or Xupdating the archive. X XWith GNU @code{tar} the blocking factor is limited only by the maximum Xblock size of the device containing the archive, or by the amount of Xavailable virtual memory. X X@item -f @var{filename} XThis option is used to specify the file name of the archive @code{tar} Xworks on. X XIf no @samp{-f} option is given, but the environment variable X@code{TAPE} exists, its value is used; otherwise, a default archive Xname (which was picked when @code{tar} was compiled) is used. The Xdefault is normally set up to be the ``first'' tape drive or other Xtransportable I/O medium on the system. X XIf the filename is @samp{-}, @code{tar} reads the archive from Xstandard input (when listing or extracting), or writes it to standard Xoutput (when creating). If the @samp{-} filename is given when Xupdating an archive, @code{tar} will read the original archive from Xits standard input, and will write the entire new archive to its Xstandard output. X XIf the filename contains @samp{:/dev/}, it is interpreted as X@samp{hostname:filename}. If the @var{hostname} contains an ampersand X(@samp{@@}), it is treated as @samp{user@@hostname:filename}. XIn either case, @code{tar} will invoke the command @code{rsh} X(or @code{remsh}) to start Xup an @code{/etc/rmt} on the remote machine. If you give an alternate login Xname, it will be given to the @code{rsh}. Naturally, the remote machine must Xhave a copy of @file{/etc/rmt}. @code{/etc/rmt} is free software Xfrom the University of California, and a copy of the source code can be found Xwith the sources for @code{tar}. @code{/etc/rmt} will have to be modified to Xrun on non-BSD4.3 systems.@refill SHAR_EOF echo "End of part 6" echo "File TAR.TEX is continued in part 7" echo "7" > s2_seq_.tmp exit 0