/* tar.c - Tape ARchive utility program (main function) * Author: T.V.Shaporev * Creation date: 14 Dec 1990 * * The program works in a same fashion under UNIX (most clones) and MS-DOS. * The main idear was to develop a tool for file transferring via diskette * between different operating systems - such as all UNIX clones, MS-DOS, * RSX, VAX/VMS - and all others which support tar format on a diskette. * * First step on this way (made in 1989) lies in adapting common UNIX tar * program to MS-DOS. * * On the second step * - some bugs were fixed (especially in DOS-applied codes) and some * optimization were done; * - nonstandard (under DOS) diskette formats were added (i.e. * DEC Rainbow and 80 tracks & 9 sectors format) * - the possibility for compress encoding were included * (this compressor has the best ratio among all others which * I know, but don't ask me about its speed). Compressed-file * format is compatible with the common versions of tar, so You * can extract the compressed image from the archive by the * common program, but I doubt You could uncompress them at last. * * On the fird step the program was totally (newly) overwritten to bypass * any copyright exclamations. In fact, it must be considered new program * but I prefer to continue version enumeration (I hope, nobody cares). * * I think, this program must be called Tar (with capital first letter) * to distinguish it from regular UNIX tar. * * The program's behaviour is analogous to usual tar, and I hope, its * internal help will be enough to understand the differences. * * The program doesn't perform any text file conversion - it passes * strict binary image of each file. If You has a problems with reading * DOS text files under UNIX (or UNIX files under DOS) please use my * dostext program. * * The program must be compiled by Turbo C 2.0 compiler (in a compact * model) under MS-DOS. Please don't replace dynamic arrays back to * static and automatic - MS-DOS compilers dislike them. * * tim tim@ecsc.mipt.su 14 Dec 1990 */ /* Version 3.01 * Handling of the 'l' option corrected */ /* Version 3.02 31 Dec 1990 * - great deal of minor corrections * - u option expanded to extracting * - j option added (comment storying files) * - wildcards * and ? are now processed in archive; this may be * suppressed by s option * - d option added to replace previous occurencies of files * in archive on storying, or deleting files from archive when * whithout a
parameter - this is for file archives only! */ /* Version 3.03 22 Feb 1991 * - an error corrected in mismatch() (file extract.c) * - decreased stack requirements for tree processing * (see store() in store.c and putfiles() in tar.c) * - added codes to prevent archive file self-storying * (not quite reliable for MS-DOS) * - bincall() invocations changed by calls to rmdir() and mkdir() * this is done automatically for 386/ix and may be switched by * RMKDIR macro */ /* Version 3.04 29 Jul 1991 * - a direct intialization of static global variables inserted into * lzencode() and lzdecode() - see file lzpack.c */ /* Version 3.04b 03 Oct 1991 * - a minor correction of bincall() * - added default block number = sectors on track (MS-DOS) */ /* Version 3.05 11 Nov 1991 * - block factor for diskette writing is set to 1 for most BIOS * compatibility * - scantape() is slightly optimized */ /* Version 3.06 17 Dec 1991 * - n option applied to all actions (see inarg() in extract.c) * - command-line-extension-file option (responce file) added * for vak (vak@kiae.su) request (see append() in tar.c) * - p option added to save directories permissions (UNIX) */ /* Version 3.06b 22 Dec 1991 * - UNIX to DOS renaming algorithm (dot elimination) slightly * changed (see extract.c) * - most of output redirected to stdout (vs stderr) */ /* Version 3.07 28 Dec 1992 * - all unary apostrofs in string constants preserved by backslashes * - reading file list from stdin allowed (see append() in tar.c) * - input redirected to /dev/tty under UNIX * - support for traditional UNIX compression algorithm * - few changes in converting UNIX names for DOS */ /* Version 3.07b 20 Jan 1993 * - gethead() does not return FALSE while errors, * scantape() looks for tape reading errors */ /* Version 3.08 22 Feb 1993 * - method-dependent comression indicator masks (see percent.c) * - 'z' option applied to catalog printing (see catalog()) * - compatibility corrections in directory structure checking * - st.st_size == codesize - means file unpacked! (see extract.c) */ /* Version 3.09 14 Mar 1993 * - a bug fixed wich prevents archiving unpacked files at all * (ha-ha!) - see store.c * - changed header description to support new features * see define.h * - support for P1003 and GNU file types - 't' option only! - * see extract.c * - small changes in #ifdef-s to distinguish SCO UNIXes from * XENIXes - see store.c * - regular file processing extracted into separate source * files (see savefile.c & restore.c) * - support for devices and FIFOs added * - 'l' option added for DOS (copy linked files) * - support for System V extents - see extract.c and restore.c * to read archives only, extents may not be unpacked on the * fly - alas, that's all for now * - an error corrected in roll.c */ /* Version 3.10 28 Jun 1993 * - a bug fixed in old compression code (see lzpack.c) * - added possibility to run through compress (',' comma option) * see tar.c, tape.c and compress.c * - comments will not be printed unless 'j' is given (extract.c) * - separated compress-related codes */ /* Version 3.11 14 Jul 1993 * - support for QIC-02 streamers (first version!) * devices supported: fastape, everex */ /* Version 3.12 29 Sen 1993 * - slack area in archive is filled by nulls to improve compression * - added support for Wangtek (QIC-02) device * - a bug fixed in memory release ('pk_out' variable - see _done()) * - program support for QIC-02 drive number and tape format * selection * - experimental (!) support for appending QIC-02 tapes * (see qback() in qicface.c) * - LZW support splitted into compressor and extractor and * the letter included in official release (see unlzw.c etc.) * - get default file name from TAPE environment variable * - 'o' flag for DOS means prevent file overwriting */ /* Version 3.12b 10 Nov 1993 * - an error corrected in QIC device selection (see qicface.c) * - eliminated idle rewindings around QIC tape initialisation */ /* Version 3.13 26 Dec 1993 * - online inflatter (unzip) and corresponding '.' (dot) option */ /* Version 3.14 19 Feb 1994 * - online deflatter (zip); compilation model changed to large */ /* Version 3.15 - general bugfix 03 Apr 1994 * - strerror() missed in some UNIXes, so psyserr() function added * into tape.c * - extended local header signature inserted in deflated output and * unzclose() changed to uderstand both formats * (see zipdefs.h, zippipe.c and diszip.c) * - pkflush() output is aligned to pksize boundary if output is not * regular file or DOS floppy to avoid block device alignment error * (see tape.c) * - "total blocks" number is reported accordingly to real archive * size (see tape.c, extract.c, tar.c) */ /* Version 3.15b - bugfix 15 Jun 1994 * - uname() (see restore.c) bug fixed; * - bi_reverse() cleaned (__emit__ed code cause BC 3.1 error) * moved to trees.c and renamed; * - getlg() changed to look for NEEDBITS buffer; * - diszip.c cleaned a bit. */ /* Version 3.16 ?? Jul 1994 * - got rid of __emit__() - completely; * - got rid of "#pragma pack()" - see define.h; * - error corrected in ct_free() (see trees.c); * - default (-e) compression changed to deflation, * keep support for old-style decompression; * - exclude file(s) specification ('#' option) - up to 16 patterns * (see fmatch.c, store.c, extract.c); * - autodetection of compressed or (g)zipped archives * (pktest() from extract.c etc.) */ /* Version 3.17 04 Nov 1994 * - changed foloppy calibrating logic (see disk.c), added diskspec() * function (see pclevel.asm) and support for 2.88M floppies (?); * - corrected missing IBEGIN updating into diszip.c; * - added 'drop online' op. into streamer() and qend(); * - restored handling of ':' option in savefile(); * - restored "idle rewindings" while starting tape since * they arrear to improve reliability; * - use conditional XOFF flag in ct_iobyte() (see streamer.c); * - added explicit call to tzset() in tar.c; * - implemented 'add' and 'skip' device parameters; * - unzipping stored file (see diszip.c), * gmtime() changed to localtime() in zippipe.c; * - GNU-like command line syntax, environment configuration; * - cascade EOI changed to specific in cthandle.asm. */ /* Version 3.18 24 Dec 1994 - ASPI support (first version!); - bug fixed in blocksize reading (see readblk() in readopt.c); - bug fixed in pattern comparing (see mismatch() in fmatch.c) */ #include "sysup.h" #include "modern.h" #include "zippipe.h" #include "lzwbits.h" #include "lzwhead.h" #include "compress.h" static char note[] = "\n\ Tape ARchive utility v3.20c%c (C) 1990-94 Tim V.Shaporev\n"; #ifdef USE_COMPRESS # define SIGN_VERSION '+' #else # define SIGN_VERSION '-' #endif #ifdef UNIX static char help[] = "\n\ Usage: tar - [tapefile] [blocksize] [disksize] file ...\n\n\ Options are: s - no wildcards for archive\n\ c - put files to new archive i - ignore read errors\n\ a,r - add files to archive m - forget files origin date/time\n\ y - move files to archive o - forget files owner\n\ x - extract files from archive l - type missed links\n\ t - show archive catalog p - save directories & permissions\n\ d - delete files in archive n - no proceed with dir nesting\n\ u - update files / - omit left \'/\' in file names\n\ v - verbose 0...7 - number of tape device\n\ w - acknowledge operations j - comment storying files\n\ e - compress encode files f - next arg is an archive file\n\ z - old-fashion compression b - next arg is a blocking factor\n\ , - run through compressor @ - next arg is a responce file\n\ . - run through (g)zip # - exclude file(s) specification\n\ "; #endif #ifdef MSDOS static char help[] = "\n\ Usage: tar - [tapefile] [blocksize] [disksize] file ...\n\n\ Options are:\n\ c - put files to new archive s - no wildcards for archive\n\ a,r - add files to archive m - forget files date/time\n\ y - move files to archive n - no proceed with dir nesting\n\ x - extract files from archive l - copy linked files\n\ t - show archive catalog o - prevent files overwriting\n\ d - delete files in archive \\ - omit left \'\\\' in file names\n\ u - update files : - omit DOS drive name\n\ v - verbose 0...3 - number of storage device\n\ w - acknowledge operations j - comment storying files\n\ i - ignore read errors f - next arg is an archive file\n\ e - compress encode files b - next arg is a blocking factor\n\ z - old-fashion compression k - next arg is K diskette size\n\ , - run through compressor @ - next arg is a responce file\n\ . - run through (g)zip # - exclude file(s) specification\n\ \n\ Most of options may be combined. Wildcards * and ? are o\'k\n\ "; static char devlist[] = "\n\ The following \"file names\" will be treated as diskette format/size:\n\ \tfd048ss8 - 160K\tfd048ds8 - 320K\tfd135ds9 - 720K\n\ \tfd048ss9 - 180K\tfd048ds9 - 360K\tfd135ds18 - 1.4M\n\ \tfd096ds9 - 720K\tfd096ds15 - 1.2M\trainbow\n\ \n\ Streamer \"file name\" syntax (full form) is:\n\ \t:base:=h,dma:=[,irq:=][,norewind]\n\ Streamer device clones supported are:\n\ \tarchive,\teverex,\t\twangtek\n\ \n\ The following \"file names\" are aliases for ASPI driven SCSI streamer:\n\ \taspitape,\taspimgr$,\taspi\n\ Full form is:\n\ \taspi[:target:=[,lun:=][,adapter:=][,density:=]]\n\ "; #endif #include #include #ifdef MSDOS # include # include # include # include # include # include #else char *strcpy(), *strcat(), *strncpy(); char *getenv(), *malloc(), *realloc(); int open(), read(), close(), link(), unlink(), isatty(); int strlen(), strncmp(), atoi(); void exit(), free(); long lseek(); #endif #define __ALLOCEXT__ #include "define.h" #include "lzpack.h" #include "roll.h" #ifdef UNIX # ifdef MAXNAMSIZ # define MAXPATH MAXNAMSIZ+1 # else # define MAXPATH 512+1 # endif #endif #ifdef UNIX # ifndef RMKDIR int bincall(name, args) char *name, *args; { extern int fork(), execl(), wait(); int i; register k; char b[24]; if (fork() == 0) { (void)execl(strcat(strcpy(b, "/bin/"), name), name, args, 0); (void)execl(strcat(strcpy(b, "/usr/bin/"), name), name, args, 0); k = -1; } else { (void)wait(&i); k = i>>8; } return k; } # endif #endif #ifdef MSDOS void takename(dst, src) register char *dst, *src; { do { *(dst++) = *src >= 'A' && *src <= 'Z' ? *src + ('z'-'Z') : *src == '\\' ? '/' : *src; } while (*(src++)); } #endif short headsum(h) header *h; { register short i, j; for (i=0, j=0; i= MAXTNAME+3*8+2*12 && i < MAXTNAME+3*8+2*12+8 ? ' ' : *((unsigned char *)h + i); } #if ~0 != 0177777 return j & 0177777; #else return j; #endif } node *finditem(fname, prev, head) char *fname; node **prev, *head; { register node *this; register i; *prev = this = head; i = 1; while (this && i>0) { if ((i = strcmp(fname, this->name)) > 0) { *prev = this; this = this->next; } } return i ? NONE : this; } node *additem(fname, prev, head) char *fname; node *prev, **head; { register node *this; register i; i = sizeof(node) - (MINTNAME-1) + strlen(fname); if ((this = (node *)malloc(i)) == NULL) return NONE; (void)strcpy(this->name, fname); this->prev = prev; if (prev != NONE) { if ((this->next = prev->next) != NONE) this->next->prev = this; prev->next = this; } else {/* initialise the list */ this->next = NONE; (*head) = this; } return this; } void delitem(this, head) node *this, **head; { if (this == *head) {/* head of the list */ if (this->next != NONE) { this->next->prev = this->prev != this ? this->prev : this->next; } this = this->next; free(*head); *head = this; } else { if (this->next != NONE) this->next->prev = this->prev; this->prev->next = this->next; free(this); } } static void _done __ARGS__(( void )) { register node *p, *q; #ifdef MSDOS extern void qend __ARGS__((void)), aspiend __ARGS__((void)); if (devtype == DEV_QIC2) qend(); else if (devtype == DEV_ASPI) aspiend(); if (archname) free(archname); #endif if (responce) free(responce); if (argvector) free((char*)argvector); if (tarcmd) free(tarcmd); p = timehead; while (p) { q = p->next; free(p); p = q; } #ifdef UNIX p = linkhead; while (p) { q = p->next; free(p); p = q; } #endif if (hwrite >= 0 && hwrite != handle) { (void)close(hwrite); (void)unlink(scratch); } if (io_2nd && io_2nd!=io_buf) free(io_2nd); if (io_buf) free(io_buf); if (pk_out && pk_out!=pk_inp) free(pk_out); if (pk_inp) free(pk_inp); zipfree(); unzfree(); #ifdef USE_COMPRESS z_reltab(); #endif z_relmem(); delroll(); } void done(n) int n; { _done(); exit(n); } void outmem(f) FILE *f; { (void)fprintf(f, "Tar: not enough memory\n"); done(ESMALL); } char *salloc(n) int n; { register char *p; if ((p = malloc(n)) == NULL) outmem(stderr); return p; } int yes_no(d) char d; { register int c; #ifdef MSDOS extern int getkey __ARGS__((void)); do { c = getkey(); } while (c!='y' && c!='Y' && c!='n' && c!='N' && c!='q' && c!='Q' && c!='\r' && c!='\n' && c!=27 && c!=3); if (c == 3) { cbreak = TRUE; } else if (c >= ' ') { (void)fprintf(stderr, "%c", c); } else if (d) { (void)fprintf(stderr, "%c", d); } (void)fprintf(stderr, "\n"); #else if ((c = getc(myinp)) == '\n') { c = d; } else { while (getc(myinp) != '\n') ; } #endif if (c == 'q' || c == 'Q') done(EXIT); return c == 'y' || c == 'Y'; } void prmode(c, m) char c; int m; { (void)fprintf(myout, "%c%c%c%c%c%c%c%c%c%c", c, (m & S_IREAD ? 'r' : '-'), (m & S_IWRITE? 'w' : '-'), (m & S_ISUID ? 's' : m & S_IEXEC ? 'x' : '-'), (m & 00040 ? 'r' : '-'), (m & 00020 ? 'w' : '-'), (m & S_ISGID ? 's' : m & 00010 ? 'x' : '-'), (m & 00004 ? 'r' : '-'), (m & 00002 ? 'w' : '-'), (m & S_ISVTX ? 't' : m & 00001 ? 'x' : '-')); } int okwork(c, p, s, n) char c, p, *n; struct stat *s; { register m; (void)fprintf(stderr, "%c ", c); if (v_flag) { if (p == ' ' && (m = s->st_mode & S_IFMT) != 0) { switch (m) { case S_IFREG: break; case S_IFDIR: p = 'd'; break; case S_IFIFO: p = 'p'; break; case S_IFCHR: p = 'c'; break; case S_IFBLK: p = 'b'; break; #ifdef S_IFLNK case S_IFLNK: p = 'l'; break; #endif default: p = '?'; } } prmode(p, (int)(s->st_mode)); (void)fprintf(stderr," %3d/%1d %7ld ",s->st_uid,s->st_gid,s->st_size); } (void)fprintf(stderr, "%s : ", n); return YES_NO(); } void onintr() { (void)signal(SIGINT, SIG_IGN); cbreak = TRUE; } #ifdef UNIX void onquit() { (void)signal(SIGQUIT, SIG_IGN); cbreak = TRUE; } void onhup() { (void)signal(SIGHUP, SIG_IGN); cbreak = TRUE; } #endif static void set_sig __ARGS__(( void )) { if (signal(SIGINT, SIG_IGN) != SIG_IGN) (void)signal(SIGINT, onintr); #ifdef UNIX if (signal(SIGHUP, SIG_IGN) != SIG_IGN) (void)signal(SIGHUP, onhup ); if (signal(SIGQUIT,SIG_IGN) != SIG_IGN) (void)signal(SIGQUIT,onquit); #endif } static void delfile __ARGS__(( void )) { if (v_flag) (void)fprintf(myout, "d %s, %ld bytes, %ld tape blocks\n", hblock->m.name, st.st_size, (st.st_size + (BLKSIZE-1))/BLKSIZE); if (usize()) skipfile(); } static void putfiles __ARGS__(( int, char ** )); static void putfiles(argc, argv) int argc; char *argv[]; { register i; char fnmbuf[MAXPATH]; for (i=0; i MAXTNAME) { (void)fprintf(myout, "Tar: \'%s\' name too long\n", argv[i]); continue; } takename(fnmbuf, argv[i]); store(fnmbuf); } } static void stdhelp __ARGS__((void)) { (void)fprintf(stdout, note, SIGN_VERSION); (void)fprintf(stdout, help); (void)fflush (stdout); #ifdef MSDOS if (ioctl(fileno(stdout),0) & 0x80) {/* not a disk file */ (void)fprintf(stderr,"\nDo you want to see device list? "); (void)fflush (stderr); if (!YES_NO()) return; } (void)fprintf(stdout, devlist); #endif } int pkalloc() { register k; if (pktype == PKpLZW || pktype == PKZIP) {/* Pipe compression */ pksize = BLKSIZE; k = (d_flag || x_flag || t_flag) && (pk_inp=malloc(pksize))==NULL || (d_flag || a_flag) && (pk_out=malloc(pksize))==NULL; } else {/* Individual file(s) compression */ pksize = PKSIZE; k = (d_flag || x_flag || (t_flag && pktype == PKfLZW)) && (pk_out=malloc(pksize))==NULL || (d_flag || a_flag) && (pk_inp=malloc(pksize))==NULL; } return k; } int main(argc, argv) int argc; char *argv[]; { register i, k; register char *p; int cenv; char **earg; if (argc < 2) { stdhelp(); return ERRARG; } #ifdef MSDOS setdrive = FALSE; filemask = FA_SYSTEM+FA_HIDDEN+FA_RDONLY+FA_DIREC+FA_ARCH; #endif pktype = PKNONE; pklock = FALSE; cblock = 0; tapename = NULL; myout = stdout; xcnt = 0; lzwbits = BITS; ziplevel = 6; /* Skip the program name */ --argc; ++argv; appname = NULL; if ((cenv = envbuild(0, &earg)) > 0) { i = readopt(&cenv, &earg, OPTFLAG); if (i < cenv) { revector(argc, &argv, cenv-i); /* append new arguments */ while(i= argc) { (void)fprintf(stderr, "Tar: no files specified\n"); return ERRARG; } #ifndef USE_COMPRESS if (pktype == PKfLZW || pktype == PKpLZW) { (void)fprintf(stderr, "Tar: this restricted version does not support LZW compression\n"); return ERRARG; } #endif } if ((k = initape(tapename)) != CORRECT) done(k); if ((io_buf=getbuf(cblock ? cblock*BLKSIZE : MAXBLOCK*BLKSIZE)) == NULL) done(ESMALL); io_2nd = io_buf; if (pktype != PKNONE) { if (pkalloc()) {/* Memory lack */ if (!w_flag) { outmem(stderr); } else { (void)fprintf(stderr, "No memory for [de]compression. Continue? "); k = YES_NO(); (void)fprintf(stderr, "\n"); if (!k) done(ESMALL); if (pk_inp) { free(pk_inp); pk_inp = NULL; } pktype = PKNONE; } } } cbreak = FALSE; if ((k = runtape()) != CORRECT) done(k); #ifdef UNIX myinp = stdin; if (!isatty(/* stdin */ 0) && (myinp = fopen("/dev/tty", "r")) == NULL) { (void)fprintf(myout, "Tar: warning: can\'t open terminal device, may be problems\n"); myinp = stdin; } #endif #ifdef MSDOS if (a_flag && isfile) { p = tapename; if ((p[0]>='A' && p[0]<='Z' || p[0]>='a' && p[0]<='z') && p[1]==':') { k = p[0] < 'a' ? p[0] - ('A'-1) : p[0] - ('a'-1); p += 2; } else { k = 0; } if (p[0] == '/' || p[0] == '\\') { takename((archname = salloc(strlen(p) + 1)), p); } else { archname = salloc(strlen(p) + MAXPATH); *(int *)archname = '/'; getcurdir(k, archname+1); takename(archname, archname); k = strlen(archname); archname[k++] = '/'; takename(archname+k, p); } } /* Turbo C 2.0 stat() does not call tzset(), so invoke it explicitly (for non-file archives) */ tzset(); #endif argc -= i; argv += i; if (d_flag) { register header *h; register long l; register m; if (d_flag && !isfile) { (void)fprintf(stderr, "Tar: delete option is for file archives only\n"); return ERRARG; } duptape(tapename); m = FALSE; do { if ((k = gethead()) == ERROR) done(ERREAD); if (!k) continue; if (a_flag) { struct stat s; if (inargs(argc, argv, hblock->m.name)) { if (u_flag) uplist(); if (stat(hblock->m.name, &s)!=0) { if (v_flag) { (void)fprintf(myout, "Tar: can\'t access \'%s\'\n", hblock->m.name); } } else if (!u_flag || s.st_mtime > st.st_mtime) { delfile(); continue; } } } else {/* pure delete */ if (inargs(argc, argv, hblock->m.name) || ((hblock->m.filetype==TF_LNK || hblock->m.filetype==TF_SYM) && inargs(argc, argv, hblock->m.linkname))) { m = TRUE; delfile(); continue; } } /* move file to output archive */ for (h=steptape(), i=0; i 0) { if ((hblock = readtape()) == NULL) done(ERREAD); for (h=steptape(), i=0; i l; } if (m) {/* archive was modified */ endtape(); if (unlink(tapename)) { (void)fprintf(myout, "Tar: can\'t delete \'%s\'\n", tapename); done(EWRITE); } #ifdef UNIX if (link(scratch, tapename)) { (void)fprintf(myout, "Tar: can\'t link \'%s\' - data stay in \'%s\'\n", tapename, scratch); done(EWRITE); } if (unlink(scratch)) { (void)fprintf(myout, "Tar: can\'t delete scratch file\n"); } #endif #ifdef MSDOS if (rename(scratch, tapename)) { (void)fprintf(myout, "Tar: can\'t rename \'%s\' to \'%s\'\n", scratch, tapename); done(EWRITE); } #endif #ifdef UNIX if (a_flag && l_flag) { register node *this; for (this=linkhead; this; this=this->next) { (void)fprintf(myout, "Tar: missed %d link(s) to \'%s\'\n", this->info.data.count, this->name); } } #endif } else { if (v_flag) (void)fprintf(myout, "Tar: archive unchanged\n"); (void)close(hwrite); if (unlink(scratch)) { (void)fprintf(myout, "Tar: can\'t delete scratch file\n"); done(EWRITE); } } } else if (a_flag) { #ifdef MSDOS if (w_flag && c_flag && devtype == DEV_FLOP) { fprintf(stderr, "\007Data on drive %c: would be destroyed. Continue ? ", ndrive + 'A'); if (!YES_NO()) done(ERRARG); } #endif if (a_flag && !c_flag) { scantape(argc, argv, acctime); backtape(); } set_sig(); putfiles(argc, argv); endtape(); #ifdef UNIX if (l_flag) { register node *this; for (this=linkhead; this; this=this->next) { (void)fprintf(myout, "Tar: missed %d link(s) to \'%s\'\n", this->info.data.count, this->name); } } #endif } else if (x_flag) { #ifdef UNIX (void)umask(0); #endif scantape(argc, argv, extract); } else {/* if (t_flag) */ allbytes = 0; allfiles = 0; scantape(argc, argv, catalog); if (v_flag) { (void)fprintf(myout, "\tTotal %u file(s) for %lu bytes in %lu tape blocks\n", allfiles, allbytes, allblock); } } _done(); return 0; }