Path: utzoo!attcan!uunet!zephyr.ens.tek.com!uw-beaver!cornell!rochester!pt.cs.cmu.edu!o.gp.cs.cmu.edu!andrew.cmu.edu!rh2y+ From: rh2y+@andrew.cmu.edu (Russell E. Hoffman, II) Newsgroups: comp.os.os9 Subject: Tar source Message-ID: Date: 25 Sep 90 18:22:58 GMT Organization: Carnegie Mellon, Pittsburgh, PA Lines: 1069 Here is the source for the un*x 'tar' utility. I'd have posted executable as well, but then I'd have had to at least uuencode it, and judging by the fact that the requestor does not have tar, he is not likely to have uudecode either, so I thought I'd play it safe! -------cut here-------- char scid[]="$Header: tar.c,v 1.8 04/13/90 SrT/stp $"; /* * Modified for use under OS-9/68k (08/03/89) * Stephan Paschedag stp@ethz.uucp ..!mcvax!cernvax!chx400!ethz!stp * paschedag@strati.ethz.ch * * Compilation for OS-9/68k: cc -qi tar.c * * 04/13/90 V1.8 problems in extraction of subdirectories fixed * change invalid filenames * LF <-> CR converion added (u modifier) (stp) * * 01/28/90 V1.7 multi-file buffer implemented (stp) * * 01/16/90 V1.6 Copy buffer implemeted, results in much faster * (re-)storing times (stp) * * 08/06/89 V1.5 Allow extraction of entire directories, * allow extraction of single files in subdirectories, * creates automatically missing directories. * continues now correctly after some errors * handles directories from unix archives correctly (stp) * * Modified for use under OS-9/6809 * Simmule Turner simmy@nu.cs.fsu.edu 70651,67 * * Compilation for OS-9/6809: cc -m=3k tar.c * Use extra memory for recursive directory descents. * * 07/17/88 V1.4 Allow extraction of specified file(s).... * from archive named on the command line * with wildcard matching *,?. (SrT) * * 07/14/88 V1.3 Added verbose TOC ala unix. Cleaned up * printing routines, use decimal instead of * octal. Removed conditional compilation. (SrT) * * 07/13/88 V1.2 Tries to set the correct file permissions. * Send diagnostics to stderr. Use lseek's * for TOC generation when a rbf device. (SrT) * * 07/12/88 V1.1 Added option of reading/writing * archive to stdin/stdout (SrT) * * 07/12/88 V1.0 Initial port (SrT) * added dummy fv options * SrT */ /* tar - tape archiver Author: Michiel Huisjes */ /* Usage: tar [cxt][bdfmuv] tapefile [files] * * Bugs: * This tape archiver should only be used as a program to read or make * simple tape archives. Its basic goal is to read (or build) UNIX V7 tape * archives and extract the named files. It is not wise to use it on * raw magnetic tapes. It doesn't know anything about linked files, * except when the involved fields are filled in. * * Comment : It works fine on raw magnetic tapes ! (tar tv /mt0 .) */ #include #include #include #include #include #ifndef OSK # include #else # include # include # include #endif #define STDIN 0 #define STDOUT 1 typedef char BOOL; #define TRUE 1 #define FALSE 0 #define HEADER_SIZE 512 #define NAME_SIZE 100 #define BLOCK_BOUNDARY 20 typedef union { char hdr_block[HEADER_SIZE]; struct m { char m_name[NAME_SIZE]; char m_mode[8]; char m_uid[8]; char m_gid[8]; char m_size[12]; char m_time[12]; char m_checksum[8]; char m_linked; char m_link[NAME_SIZE]; } member; } HEADER; /* file match structure (04/13/90 stp) */ struct files { char *name; struct files *next; }; HEADER header; #define INT_TYPE (sizeof(header.member.m_uid)) #define LONG_TYPE (sizeof(header.member.m_size)) #define NIL_HEADER ((HEADER *) 0) #define NIL_PTR ((char *) 0) #define BLOCK_SIZE 512 #define flush() print(NIL_PTR) BOOL show_fl, creat_fl, ext_fl, verbose_fl, modtime_fl, convert_fl, disk_fl; char modestr[11], stbuf[132]; struct sgbuf opts; struct fildes ofdbuf; #ifdef OSK char *dummy = NULL; #endif int tar_fd; /* set blocksize (01/16/90 stp) */ BOOL blks_fl; int blocking = 1; char *io_buffer; char *w_buffer; long bsize; int total_blocks; long convert(); char **match(); block_size() { return ((int) ((convert(header.member.m_size, LONG_TYPE) + (long) BLOCK_SIZE - 1) / (long) BLOCK_SIZE)); } error(s1, s2) char *s1, *s2; { #ifdef OSK fprintf(stderr,"%s %s\n", s1, s2 ? s2 : ""); exit(1); #else string_print(NIL_PTR, "%s %s\r\l", s1, s2 ? s2 : ""); flush(); exit(1); #endif } usage() { fprintf(stderr,"Syntax : tar [ctxb][mfvu] [blocks] tarfile [file(s)...]\n"); fprintf(stderr,"Function: Builds and extracts from Unix tape archive files\n"); fprintf(stderr,"Options: Modifiers:\n"); fprintf(stderr," c : create archive b : set copy buffer size\n"); fprintf(stderr," t : list contents d : backup floppy disks\n"); fprintf(stderr," x : extract from archive m : do not restore date\n"); fprintf(stderr," u : convert LF <-> CR\n"); fprintf(stderr," v : verbose mode\n"); exit(0); } main(argc, argv) int argc; register char *argv[]; { register char *ptr; #ifdef OSK char **p = &dummy; #else char **p = "\0"; #endif int i; pflinit(); if (argc < 3) usage(); for (ptr = argv[1]; *ptr; ptr++) { switch (*ptr) { case 'c' : creat_fl = TRUE; break; case 't' : show_fl = TRUE; break; case 'x' : ext_fl = TRUE; break; /* * Modifiers * SrT */ /* set blocksize (01/16/90 stp) */ case 'b' : blks_fl = TRUE; break; /* muliple disk backup (04/14/90 stp) */ case 'd' : disk_fl = TRUE; break; case 'f': break; case 'm': modtime_fl = TRUE; break; case 'v': verbose_fl = TRUE; break; /* convert flag (04/10/90 stp) */ case 'u' : convert_fl = TRUE; break; default : usage(); } } if (blks_fl) { if (sscanf(argv[2],"%d",&blocking) == -1) error("bad parameter for blocking",argv[3]); blocking = (blocking+1)>>1; } if ((io_buffer = (char*) malloc(BLOCK_SIZE*blocking)) == 0) error("allocation of io_buffer failed",""); if ((w_buffer = (char *) malloc(BLOCK_SIZE*blocking)) == 0) error("allocation of w_buffer failed",""); bsize = (long) BLOCK_SIZE*blocking; if (ext_fl) { if (argc > (blks_fl ? 4 : 3)) p = &argv[blks_fl ? 4 : 3]; } if (creat_fl + ext_fl + show_fl != 1) usage(); if (argv[blks_fl ? 3 : 2][0] == '-') tar_fd = creat_fl ? STDOUT : STDIN; else tar_fd = creat_fl ? creat(argv[blks_fl ? 3 : 2],3) : open(argv[blks_fl ? 3 : 2],1); if (tar_fd < 0) { #ifdef OSK exit(_errmsg(errno,"cannot open archive. ")); #else error("Cannot open ", argv[blks_fl ? 3 : 2]); #endif } if (creat_fl) { do { for (i = (blks_fl ? 4 : 3); i < argc; i++) add_file(argv[i]); if (disk_fl) { do { printf("save another disk ?"); fflush(stdout); i = toupper(getchar()); } while ((i != 'Y') && (i != 'N')); disk_fl = (i == 'Y'); } } while(disk_fl); adjust_boundary(); } else tarfile (p); flush(); exit(0); } BOOL get_header() { register int check,c; mread(tar_fd, &header, sizeof(header)); if (header.member.m_name[0] == '\0') return FALSE; check = (int) convert(header.member.m_checksum, INT_TYPE); if (check != (c = checksum())) error("tar: header checksum error.", NIL_PTR); return TRUE; } tarfile(p) char **p; { register char *ptr; register char *mem_name; char *atime(); int i; register char **q; register char *pp; struct files f0,*fptr=&f0; /* create 'files' structure from 'p' */ q = p; while (*q) { fptr->name = (char *) malloc(strlen(*q)); strcpy(fptr->name,*q); fptr->next = (struct files*) malloc(sizeof(struct files)); fptr = fptr->next; q++; } fptr->name = 0; _gs_opt(tar_fd,&opts); while (get_header()) { mem_name = header.member.m_name; if (ext_fl) { if (*p) { /* extract entire directories (08/05/89 stp) */ if (mem_name[strlen(mem_name)-1] == '/') { mem_name[strlen(mem_name)-1] = '\0'; if (q=match(&f0,mem_name)) { fptr->name = (char *) malloc(strlen(mem_name)+3); strcpy(fptr->name,mem_name); strncat(fptr->name,"/*",3); fptr->next = (struct files*) malloc(sizeof(struct files)); fptr = fptr->next; fptr->name = 0; check_name(mem_name); mkdir(mem_name); } else { q = q; } } else { if (match(&f0,mem_name)) { extract(mem_name); } else skip_entry(); } } else { if (is_dir(mem_name)) { for (ptr = mem_name; *ptr != '/'; ptr++); *ptr = '\0'; check_name(mem_name); mkdir(mem_name); } else { extract(mem_name); } } } else { if (!verbose_fl) #ifdef OSK printf("%s",mem_name); #else string_print(NIL_PTR, "%s", mem_name); #endif else { u29mode((int) convert(header.member.m_mode,INT_TYPE)); #ifdef OSK printf("%s ",modestr); #else string_print(NIL_PTR, "%s ",modestr); #endif sprintf(stbuf,"%3d/%3d %8ld %s %s", (int) convert(header.member.m_uid, INT_TYPE), (int) convert(header.member.m_gid, INT_TYPE), convert(header.member.m_size,LONG_TYPE), atime(convert(header.member.m_time,LONG_TYPE)), header.member.m_name); #ifdef OSK printf("%s",stbuf); } printf("\n"); #else print(stbuf); } print("\r\l"); #endif skip_entry(); } flush(); } } skip_entry() { register int blocks = block_size(); if (opts.sg_class == 1) { long pos; pos = ((long) blocks) * BLOCK_SIZE; lseek(tar_fd,pos,1); } else { while (blocks--) read(tar_fd, io_buffer, BLOCK_SIZE); } } /* convert to valid filename */ check_name(c) register char *c; { while (*c) { if (((*c >= '0') && (*c <= '9')) || ((*c >= 'A') && (*c <= 'Z')) || ((*c >= 'a') && (*c <= 'z')) || (*c == '_') || (*c == '$') || (*c == '.') || (*c == '/')) { } else { *c = '_'; } c++; } } extract(file) register char *file; { register int fd; if (header.member.m_linked == '1') { #ifdef OSK fprintf(stderr,"Cannot link %s (symbolic links not supportet)\n",header.member.m_link); #else string_print(NIL_PTR,"Cannot link %s\r\l",header.member.m_link); #endif skip_entry(); return(0); } check_name(file); if ((fd = creat(file, 3)) < 0) { /* create missing directories (08/06/89) stp */ register char *s,*pp; pp = file; #ifdef OSK while (s = index(pp,'/')) { #else while (s = strchr(pp,'/')) { #endif pp = (char *) malloc(s-file+1); strncpy(pp,file,s-file); pp[s-file] = '\0'; mkdir(pp); free(pp); pp = s+1; } if ((fd = creat(file, 3)) < 0) { #ifdef OSK fprintf(stderr, "Cannot create %s : ", file); fflush(stderr); prerr(0,errno); #else string_print(NIL_PTR, "Cannot create %s\r\l", file); #endif skip_entry(); return(0); } } copy(file, tar_fd, fd, convert(header.member.m_size, LONG_TYPE)); _ss_attr(fd, u29mode((int) convert(header.member.m_mode, INT_TYPE))); if (!modtime_fl) #ifdef OSK if (_gs_gfd(fd,&ofdbuf,sizeof(ofdbuf)) != -1) { #else if (_gs_gfd(fd,&ofdbuf,sizeof(ofdbuf)) != ERROR) { #endif struct tm *utm; struct sgtbuf otm; long clock; clock = convert(header.member.m_time,LONG_TYPE); utm = localtime(&clock); u2otime(&otm,utm); _strass(&ofdbuf.fd_date[0], &otm, 5); _ss_pfd(fd,&ofdbuf); } close(fd); flush(); } /* convert one char from buffer to another char (04/10/90 stp) */ convert_buf(p,s,f,t) register char *p; /* *buffer */ register int s; /* size of buffer */ register f,t; /* from, to */ { while (s--) { if (*p == f) *p = t; p++; } } copy(file, from, to, bytes) char *file; int from, to; register long bytes; { register int rest,cnt; int blocks,blks_fl = (int) ((bytes + BLOCK_SIZE - 1) / BLOCK_SIZE); if (verbose_fl) { if (to == tar_fd) { #ifdef OSK printf("a %s, ",file); fflush(stdout); #else sprintf(stbuf,"a %s, %d tape blocks\r\l",file,blks_fl); #endif } else { #ifdef OSK printf("x %s, %ld bytes, ",file,bytes); fflush(stdout); } #else sprintf(stbuf,"x %s, %ld bytes, %d tape blocks\r\l",file,bytes,blks_fl); } print(stbuf); flush(); #endif } blocks = (int) ((bytes + bsize - 1) / bsize); while (blocks--) { cnt = rest = (bytes > bsize) ? bsize : (to == tar_fd ? (int) bytes : ((bytes + BLOCK_SIZE - 1) / BLOCK_SIZE)*BLOCK_SIZE); read(from, io_buffer, rest); rest = (bytes > bsize) ? bsize : (to == tar_fd ? ((bytes + BLOCK_SIZE - 1) / BLOCK_SIZE)*BLOCK_SIZE : bytes); if (convert_fl) if (to == tar_fd) { convert_buf(io_buffer,rest,0x0d,0x0a); } else { convert_buf(io_buffer,rest,0x0a,0x0d); } mwrite(to, io_buffer, rest,0); bytes -= (bytes > bsize ? bsize : bytes); } #ifdef OSK if (verbose_fl) printf("%d tape blocks\n",blks_fl); #endif } long convert(str, type) char str[]; int type; { register long ac = 0L; register int i; for (i = 0; i < type; i++) { if (str[i] >= '0' && str[i] <= '7') { ac <<= 3; ac += (long) (str[i] - '0'); } } return ac; } mkdir(dir_name) char *dir_name; { if (mknod(dir_name,3) < 0) { return(0); } else { int fd; if ((fd = open(dir_name,0x83)) > 0) { _ss_attr(fd, S_IFDIR | u29mode((int) convert(header.member.m_mode, INT_TYPE))); close(fd); } } } checksum() { register char *ptr = header.member.m_checksum; register int ac = 0; while (ptr < &header.member.m_checksum[INT_TYPE]) *ptr++ = ' '; ptr = header.hdr_block; while (ptr < &header.hdr_block[BLOCK_SIZE]) ac += *ptr++; return ac; } is_dir(file) register char *file; { while (*file++ != '\0') ; return (*(file - 2) == '/'); } char path[NAME_SIZE]; char pathname[NAME_SIZE]; char *path_name(file) register char *file; { string_print(pathname, "%s%s", path, file); return pathname; } add_path(name) register char *name; { register char *path_ptr = path; while (*path_ptr) path_ptr++; if (name == NIL_PTR) { while (*path_ptr-- != '/') ; while (*path_ptr != '/' && path_ptr != path) path_ptr--; if (*path_ptr == '/') path_ptr++; *path_ptr = '\0'; } else { while (*name) { if (path_ptr == &path[NAME_SIZE]) error("tar: Pathname too long", NIL_PTR); *path_ptr++ = *name++; } *path_ptr++ = '/'; *path_ptr = '\0'; } } add_file(file) register char *file; { struct fildes st; struct dirent dir; register int fd; #ifdef OSK unsigned long siz; u_char *sip = (u_char*) st.fd_fsize; #endif if ((fd = open(file,0x81)) < 0) if ((fd = open(file, 1)) < 0) { #ifdef OSK fprintf(stderr, "Cannot open '%s' ", file); fflush(stderr); prerr(0,errno); #else string_print(NIL_PTR, "Cannot open %s\r\l", file); #endif return(0); } if (_gs_gfd(fd,&st,sizeof(st)) < 0) { #ifdef OSK fprintf(stderr, "Cannot get file descriptor for %s",file); fflush(stderr); prerr(0,errno); #else string_print(NIL_PTR, "Cannot get file descriptor for %s\r\l",file); #endif close(fd); return(0); } siz = (((((sip[0] << 8) + sip[1]) << 8) + sip[2]) << 8) + sip[3]; make_header(path_name(file), &st); mwrite(tar_fd, &header, sizeof(header),0); if (!(st.fd_att & S_IFDIR)) #ifdef OSK copy(path_name(file), fd, tar_fd, siz); #else copy(path_name(file), fd, tar_fd, st.fd_fsize); #endif else if (st.fd_att & S_IFDIR) { if (chdir(file) < 0) string_print(NIL_PTR, "Cannot chdir to %s\n", file); else { add_path(file); mread(fd, &dir, sizeof(dir)); /* "." */ mread(fd, &dir, sizeof(dir)); /* ".." */ while (read(fd, &dir, sizeof(dir)) == sizeof(dir)) { #ifdef OSK if (dir.dir_addr) { #else if (dir.dir_addr[0] || dir.dir_addr[1] || dir.dir_addr[2]) { #endif strhcpy(dir.dir_name,dir.dir_name); if (*dir.dir_name) add_file(dir.dir_name); } } chdir(".."); add_path(NIL_PTR); } } else #ifdef OSK _errmsg("unknown file type. Not added. ",0); #else print("tar: unknown file type. Not added.\r\l"); #endif close(fd); } make_header(file, st) char *file; register struct fildes *st; { register char *ptr = header.member.m_name; char tbuf[6]; #ifdef OSK u_char *sip = (u_char*) st->fd_fsize; unsigned long siz = (((((sip[0] << 8) + sip[1]) << 8) + sip[2]) << 8) + sip[3]; u_char *owp = (u_char*) st->fd_own; unsigned short own = owp[1]; unsigned short group = owp[0]; #endif clear_header(); while (*ptr++ = *file++) ; if (st->fd_att & S_IFDIR) { *(ptr - 1) = '/'; #ifdef OSK siz = 0; #else st->fd_fsize = 0L; #endif } _strass(tbuf,st->fd_date,5); tbuf[5] = 0; string_print(header.member.m_mode, "%I ", o2umode(st->fd_att)); #ifdef OSK string_print(header.member.m_uid, "%I ", own); string_print(header.member.m_gid, "%I ", group); string_print(header.member.m_size, "%L ", siz); #else string_print(header.member.m_uid, "%I ", st->fd_own); string_print(header.member.m_gid, "%I ", 101); string_print(header.member.m_size, "%L ", st->fd_fsize); #endif string_print(header.member.m_time, "%L ", o2utime(tbuf)); header.member.m_linked = ' '; string_print(header.member.m_checksum, "%I", checksum()); } clear_header() { register char *ptr = header.hdr_block; while (ptr < &header.hdr_block[BLOCK_SIZE]) *ptr++ = '\0'; } adjust_boundary() { clear_header(); mwrite(tar_fd, &header, sizeof(header),1); while (total_blocks++ < BLOCK_BOUNDARY) mwrite(tar_fd, &header, sizeof(header),1); close(tar_fd); } mread(fd, address, bytes) int fd, bytes; char *address; { register int r; if ((r = read(fd, address, bytes)) != bytes) { #ifdef OSK if (r == 0) errno = E_EOF; exit(_errmsg(errno,"read error. ")); #else error("tar: read error.", NIL_PTR); #endif } } mwrite(fd, address, bytes, flag) int fd, bytes; char *address; short flag; { static char *bptr; static int fill = 0; if (fd == tar_fd) { if (fill == 0) bptr = w_buffer; while (fill + bytes > bsize) { _strass(bptr,address,bsize-fill); write(fd,w_buffer,bsize); address += (bsize-fill); bytes -= (bsize-fill); fill = 0; bptr = w_buffer; } _strass(bptr,address,bytes); bptr += bytes; fill += bytes; if (flag) { write(fd,w_buffer,fill); fill = 0; } } else { if (write(fd, address, bytes) != bytes) { #ifdef OSK exit(_errmsg(errno,"write error. ")); #else error("tar: write error.", NIL_PTR); #endif } } total_blocks++; } char output[BLOCK_SIZE]; print(str) register char *str; { static int index = 0; if (str == NIL_PTR) { write(2, output, index); index = 0; return(0); } while (*str) { output[index++] = *str++; if (index == BLOCK_SIZE) { write(2, output, BLOCK_SIZE); index = 0; } } } char *num_out(number) register long number; { static char num_buf[13]; char temp[13]; register int i; for (i = 0; i < 11; i++) { temp[i] = (number & 07) + '0'; number >>= 3; } for (i = 0; i < 11; i++) num_buf[i] = temp[10 - i]; return num_buf; } /* VARARGS */ string_print(buffer, fmt, args) char *buffer; register char *fmt; int args; { register char *buf_ptr; char *scan_ptr; char buf[NAME_SIZE]; int *argptr = &args; BOOL pr_fl, i; if ((pr_fl = (buffer == NIL_PTR))) buffer = buf; buf_ptr = buffer; while (*fmt) { if (*fmt == '%') { fmt++; switch (*fmt++) { case 's': scan_ptr = (char *) *argptr; break; case 'I': scan_ptr = num_out((long) *argptr) + 5; /* for (i = 0; i < 5; i++) scan_ptr++; */ break; case 'L': scan_ptr = num_out(*((long *) argptr)); argptr++; break; default: scan_ptr = ""; } while (*buf_ptr++ = *scan_ptr++) ; buf_ptr--; argptr++; } else *buf_ptr++ = *fmt++; } *buf_ptr = '\0'; if (pr_fl) print(buffer); } o2umode(mode) char mode; { int ret_mode=0; if (mode & S_IFDIR) ret_mode |= 040000; if (mode & S_IREAD) ret_mode |= 0400; if (mode & S_IWRITE) ret_mode |= 0200; if (mode & S_IEXEC) ret_mode |= 0100; if (mode & S_IOREAD) ret_mode |= 04; if (mode & S_IOWRITE) ret_mode |= 02; if (mode & S_IOEXEC) ret_mode |= 01; return(ret_mode); } u29mode(mode) int mode; { int ret_mode=0; strcpy(modestr,"-----------"); if (mode & 040000) { ret_mode |= S_IFDIR; modestr[0] = 'd'; } if (mode & 0400) { ret_mode |= S_IREAD; modestr[1] = 'r'; } if (mode & 0200) { ret_mode |= S_IWRITE; modestr[2] = 'w'; } if (mode & 0100) { ret_mode |= S_IEXEC; modestr[3] = 'x'; } if (mode & 04) { ret_mode |= S_IOREAD; modestr[7] = 'r'; } if (mode & 02) { ret_mode |= S_IOWRITE; modestr[8] = 'w'; } if (mode & 01) { ret_mode |= S_IOEXEC; modestr[9] = 'x'; } return(ret_mode); } char *atime(clock) long clock; { static char buf[26]; int i; strcpy(buf,ctime(&clock)); for (i=4; i< 16; i++) buf[i-4] = buf[i]; buf[12] = ' '; for (i=20; i<24; i++) buf[i-7] = buf[i]; buf[17] = 0; return(buf); } char **match(fpt,name) register struct files *fpt; register char *name; { while (fpt->name) { #ifdef OSK if (!_cmpnam(name,fpt->name,strlen(fpt->name))) { #else if (patmatch(fpt->name,name,1)) { #endif return(fpt->name); } fpt = fpt->next; } return(NULL); } #ifdef OSK u2otime(om,um) struct sgtbuf *om; struct tm *um; { om->t_year = um->tm_year; om->t_month = um->tm_mon+1; om->t_day = um->tm_mday; om->t_hour = um->tm_hour; om->t_minute = um->tm_min; om->t_second = um->tm_sec; } long o2utime(om) struct sgtbuf *om; { struct tm um; um.tm_year = om->t_year; um.tm_mon = om->t_month-1; um.tm_mday = om->t_day; um.tm_hour = om->t_hour; um.tm_min = om->t_minute; um.tm_sec = om->t_second; return mktime(&um); } #endif