rh2y+@andrew.cmu.edu (Russell E. Hoffman, II) (09/26/90)
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 <stdio.h> #include <direct.h> #include <modes.h> #include <sgstat.h> #include <time.h> #ifndef OSK # include <utime.h> #else # include <types.h> # include <errno.h> # include <strings.h> #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
jeffr@bcs800.UUCP (Jeff Riegel) (09/27/90)
In <kazu22S00WB389MkU0@andrew.cmu.edu> rh2y+@andrew.cmu.edu (Russell E. Hoffman, II) writes: >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! Correct assumption... Thanks for the source Jeffr
krischan@strange.informatik.rwth-aachen.de (Christian Engel) (09/28/90)
STOP!!! rh2y+@andrew.cmu.edu (Russell E. Hoffman, II) writes: > Here is the source for the un*x 'tar' utility. [...] I've got the same tar but I've improved it at several points. The source is modified that much that there is no sense of a diff for patching. Here is the complete tar V1.9 again: ---- Cut here ---- Cut here ---- Cut here ---- Cut here ---- Cut here ---- #! /bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #! /bin/sh line. # 2. Save the resulting text in a file. # 3. Execute the file with /bin/sh (not csh) to create: # tar.c # makefile # tar.doc # tar.hlp # This archive created: Fri Sep 28 10:55:09 1990 export PATH; PATH=/bin:/usr/bin:$PATH echo shar: "extracting 'tar.c'" '(28037 characters)' cat << \SHAR_EOF > 'tar.c' char scid[]="$Header: tar.c,v 1.9 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 * * 07/30/90 V1.9 Revised the source code completely and made it more readable. * Added -z option for reading file names from file or stdin. * Placed versatile write error messages (tar kept silent * though writing on a full device!). * (Christian Engel, krischan@strange.informatik.rwth-aachen.de) * * 04/13/90 V1.8 problems in extraction of subdirectories fixed * change invalid filenames * LF <-> CR conversion 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][bdfmuvz] 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 <stdio.h> #include <direct.h> #include <modes.h> #include <sgstat.h> #include <time.h> #include <ctype.h> #ifndef OSK # include <utime.h> #else # include <types.h> # include <errno.h> # include <strings.h> #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) extern char *malloc (), *realloc (); #ifdef OSK extern char *_prgname (); extern char *index (); #else #define _prgname() prgname char *prgname; #define index strchr #endif char *getname (); BOOL show_fl, /* t option: list contents of archive */ creat_fl, /* c option: create archive */ ext_fl, /* x option: extract form archive */ verbose_fl, /* v modifier: verbose all actions */ modtime_fl, /* m modifier: do not restore date */ convert_fl, /* u modifier: convert LF <-> CR */ disk_fl; /* d modifier: backup to disks */ char *listfilename = NULL; /* z modifier: read file names from file */ char modestr[11], stbuf[132]; struct sgbuf opts; struct fildes ofdbuf; #ifdef OSK char *dummy = NULL; #endif int tar_fd; char *tarfilename; int Argc; char **Argv; /* 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 } /* VARARGS2 */ usage(exitcode, format, p1, p2) int exitcode; char *format, *p1, *p2; { static char *text [] = { "Syntax : %s [ctxb][mfvu] [blocks] tarfile [file(s)...]\n", "Function: Builds and extracts from Unix tape archive files", " if tarfile is `-' stdin/stdout will be used", "Options: Modifiers:", " c : create archive b : set copy buffer size", " t : list contents d : backup floppy disks", " x : extract from archive m : do not restore date", " u : convert LF <-> CR", " v : verbose mode", " z : read file names from named file, file", " name - means stdin", 0 }; char **pp; if (format) { fprintf (stderr, format); putc ('\n', stderr); } fprintf (stderr, text [0], _prgname ()); putc ('\n', stderr); for (pp = text; *pp; pp++) fprintf (stderr, "%s\n", *pp); exit(exitcode); } main(argc, argv) int argc; register char *argv[]; { register char *ptr; int i, argind; char *fname; #ifndef OSK prgname = argv [0]; #endif pflinit(); if (argc < 3) usage(1, "Too few arguments"); argind = 2; ptr = argv[1]; #ifdef F_MODIFIER_ACTIVE tarfilename = NULL; #else tarfilename = argv [argind++]; #endif while (*ptr) { switch (*ptr++) { case 'c' : creat_fl = TRUE; break; case 't' : show_fl = TRUE; break; case 'x' : ext_fl = TRUE; break; /* ** Scan Modifiers */ case 'b' : /* set blocksize (01/16/90 stp) */ if (argind >= argc) usage (1, "No argument for b modifier"); blks_fl = TRUE; if (sscanf(argv[argind],"%d",&blocking) == -1) usage (1, "%s: bad parameter for blocking", argv[argind]); blocking = (blocking+1)>>1; argind++; break; case 'd' : /* muliple disk backup (04/14/90 stp) */ disk_fl = TRUE; break; case 'f': #ifdef F_MODIFIER_ACTIVE if (tarfilename != NULL) usage (1, "Just one tar file!"); if (argind >= argc) usage (1, "No argument for f modifier"); tarfilename = argv [argind++]; #endif break; case 'm': modtime_fl = TRUE; break; case 'v': verbose_fl = TRUE; break; case 'u' : /* convert flag (04/10/90 stp) */ convert_fl = TRUE; break; case 'z': /* file list input flag (07/28/90 ce) */ if (argind >= argc) usage (1, "No argument for z modifier"); listfilename = argv [argind++]; break; case '-' : if (ptr == argv [1] + 1) /* leading '-' */ continue; case '?' : usage (0, NULL); default : ptr--; usage (1, "Unknown %s '%c'", ptr == argv [1] ? "option" : "modifier", *ptr); } } #ifdef F_MODIFIER_ACTIVE if (tarfilename == NULL) usage (1, "No tar file specified. Use f modifier"); #endif if (creat_fl + show_fl + ext_fl != 1) usage(1, "Use one of the c, x, and t options"); if (strcmp(listfilename, "-")==0 && strcmp(tarfilename, "-")==0 && ext_fl) usage (1, "tar file and list file for z modifier are both stdin"); if ((io_buffer = malloc(BLOCK_SIZE*blocking)) == NULL) error("allocation of io_buffer failed",""); if ((w_buffer = malloc(BLOCK_SIZE*blocking)) == NULL) error("allocation of w_buffer failed",""); bsize = (long) BLOCK_SIZE*blocking; if (strcmp (tarfilename, "-") == 0) tar_fd = creat_fl ? STDOUT : STDIN; else tar_fd = creat_fl ? creat(tarfilename,3) : open(tarfilename, 1); if (tar_fd < 0) { #ifdef OSK exit (_errmsg (errno, "cannot open archive '%s'", tarfilename)); #else error ("Cannot open archive", tarfilename); #endif } Argc = argc; Argv = argv; if (creat_fl) { do { if (getname (argind) != NULL) exit (99); /* fatal error!!! */ while ((fname = getname (0)) != NULL) add_file (fname); 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 (argind); flush (); exit (0); } /* ** read a header from the tar file */ 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 (argind) int argind; { register char *ptr; register char *mem_name; char *atime (); int i; char *fname; register char *pp; struct files f0, *fptr=&f0; /* create 'files' structure from file list */ if (getname (argind) != NULL) exit (99); /* fatal error */ while ((fname = getname (0)) != NULL) { fptr->name = (char *) malloc(strlen(fname)); strcpy(fptr->name,fname); fptr->next = (struct files*) malloc(sizeof(struct files)); fptr = fptr->next; } fptr->name = NULL; _gs_opt(tar_fd, &opts); while (get_header ()) { mem_name = header.member.m_name; if (ext_fl) { if (f0.name) { /* extract entire directories (08/05/89 stp) */ if (mem_name[strlen(mem_name)-1] == '/') { mem_name[strlen(mem_name)-1] = '\0'; if (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; ---> Hmmm?!?!? (ce) }*/ } 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(); } } #define NCHUNK 128 /* ** This function catches arguments as file names from the list file (if any) ** and from the command line. ** If 0 is passed by <argind> the next file name will be fetched. If ** the global variable <listfilename> isn't NULL this name will be read ** from the listfile. If EOF is reached or if there isn't any listfile ** the next name will be fetched from the command line. In this case and ** if the last command line argument has been reached already NULL ** will be returned to indicate the end of the file name list. ** If a value not equal 0 is passed by <argind> the getname() initializes ** anything. The list file will be opened (if any) or rewound. The ** index into the command line arguments <fnameind> pointing to ** the next command line argument to be passed will be reset to <argind>. */ char *getname (argind) int argind; { static char *buf = NULL; static int buflen = 0; static int fnameind; register int c; register char *p; static FILE *file = NULL; if (argind) { /* initialize! */ if (listfilename != NULL) { /* z option is given */ if (file == NULL) { /* list file hasn't been opened */ if (strcmp (listfilename, "-") == 0) file = stdin; else if ((file = fopen (listfilename, "r")) == NULL) #ifdef OSK exit (_errmsg (errno, "can't open list file '%s'\n", listfilename)); #else error ("Can't open list file ", listfilename); #endif buflen = NCHUNK; if ((buf = malloc (buflen)) == NULL) #ifdef OSK exit(_errmsg(errno, "Can't allocate file name buffer\n")); #else error("Can't allocate file name buffer\n", NIL_PTR); #endif } else rewind (file); } fnameind = argind; return (NULL); } if (file != NULL && !feof (file)) { p = buf; /* now we try to get the next name from the list file */ { /* skip white space */ c = getc (file); } while (isspace (c)); while (c != EOF && !isspace (c)) { /* get name */ if (p == buf + buflen - 2) { /* buffer exhausted */ buflen += NCHUNK; /* enlage chunk */ if ((buf = realloc (buf, buflen)) == NULL) #ifdef OSK exit (_errmsg (errno, "Can't reallocate file name buffer\n")); #else error ("Can't reallocate file name buffer\n", NIL_PTR); #endif p = buf + buflen - 2 - NCHUNK; } *p++ = c; c = getc (file); } if (c != EOF || p > buf) { *p = '\0'; return (buf); } } /* there was no list file (no z option) or the file is exhausted => continue with catching file names from the command line */ if (fnameind >= Argc) /*there aren't any names left in the command line*/ return (NULL); return (Argv [fnameind++]); } /* ** skip the next entry in the tar file */ 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 (!(isalnum(*c) || *c == '_' || *c == '$' || *c == '.' || *c == '/')) *c = '_'; c++; } } extract(file) register char *file; { register int fd; if (header.member.m_linked == '1') { #ifdef OSK fprintf(stderr,"Cannot link %s (hard links not supported)\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; while (s = index(pp,'/')) { 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 <bytes> bytes from the file with path descriptor <from> to the ** file with path descriptor <to> ** upon verbose use the string pointed to by <file> as the file name */ 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); if (write(fd,w_buffer,bsize) != bsize) { #ifdef OSK exit(_errmsg(errno,"write error. ")); #else error("tar: write error.", NIL_PTR); #endif } address += (bsize-fill); bytes -= (bsize-fill); fill = 0; bptr = w_buffer; } _strass(bptr,address,bytes); bptr += bytes; fill += bytes; if (flag) { if (write(fd,w_buffer,fill) != fill) #ifdef OSK exit(_errmsg(errno,"write error. ")); #else error("tar: write error.", NIL_PTR); #endif 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 ind = 0; if (str == NIL_PTR) { if (write(2, output, ind) != ind) #ifdef OSK exit(_errmsg(errno,"write error. ")); #else error("tar: write error.", NIL_PTR); #endif ind = 0; return(0); } while (*str) { output[ind++] = *str++; if (ind == BLOCK_SIZE) { if (write(2, output, BLOCK_SIZE) != BLOCK_SIZE) #ifdef OSK exit(_errmsg(errno,"write error. ")); #else error("tar: write error.", NIL_PTR); #endif ind = 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); } /* ** convert OS-9 to UNIX mode flags */ 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); } /* ** convert UNIX to OS-9 mode flags */ 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 SHAR_EOF chmod 644 'tar.c' echo shar: "extracting 'makefile'" '(963 characters)' cat << \SHAR_EOF > 'makefile' # # makefile for OS-9 tar V1.9 Mon Jul 30 09:48:59 1990 # # ``make'' generates tar in $(ODIR) # ``make debug'' generates debugging version _tar in $(DBG_ODIR) # ``make tags'' generates tags file # ``make lint'' generates lint protocol # # The following compile flags can be used (see DEFS macro): # -DF_MODIFIER_ACTIVE makes the f modifier active like under UNIX # EDITION = 19 ODIR = /h0/CMDS/LOCAL DBG_ODIR= /h1/CMDS RDIR = RELS DEFS = F_MODIFIER_ACTIVE G = CFLAGS = -qixt=/r0/TMP $G LFLAGS = -qixt=/r0/TMP $G -e=$(EDITION) CC = cf TARGET = tar $(TARGET): tar.r $(CC) $(LFLAGS) -f=$(ODIR)/$(TARGET) $(RDIR)/tar.r @attr $(ODIR)/$(TARGET) -epe debug: debug.r @echo debug made debug.r: tar.c make TARGET=_tar ODIR=$(DBG_ODIR) RDIR=DBG_RELS G=-g ########################################################## tags: tags.r @echo tags made tags.r: tar.c ctags -t tar.c lint: lint.r @echo lint made lint.r: tar.c lint -bachx tar.c >-tar.lint SHAR_EOF chmod 644 'makefile' echo shar: "extracting 'tar.doc'" '(4119 characters)' cat << \SHAR_EOF > 'tar.doc' USAGE tar <function> <tarfile> [<file> . . .] DESCRIPTION Builds and extracts from Unix tape archive files. The <function> parameter consists of a one-letter function specifier with one or more option characters. <tarfile> is the file to be used for the archive. If <tarfile> is specified as -, then Stdin or Stdout will be used, as appropriate. <file> is one or more filenames to be used by the extract or create operations. FUNCTION SPECIFIERS t Output a table of all files in the archive. Recognized Options: v Verbose mode. Display file permissions, sizes, and modification dates and times. x Extract the named files from the archive. Wildcard characters * and ? may be used in filename specifications. Recognized Options: v Verbose mode. Display a message for each file extracted. m Do not restore modification dates. Instead, use current date and time as modification date for each extracted file. u convert LF -> CR (for UNIX text files) b Set copy buffer size (number of tape blocks). The number is specified directly after the options. z First read the names of the files to be extracted from a list file, then continue with the names given on the command line. The name of the list file is given after the tar file name. If a ``-'' is specified stdin will be used. This feature is not possible if the tarfile is stdin, too. c Create a new archive file and include the named files. A directory name means to recursively include all files in that directory. Recognized Options: v Verbose mode. Display a message for each file added to the archive. u convert CR -> LF (for UNIX text files) b Set copy buffer size (number of tape blocks). The number is specified directly after the options. d copy contents of several floopy disks to one tar archive z First read the names of the files to be tared from a list file, then continue with the names given on the command line. The name of the list file is given after the tar file name. If a ``-'' is specified stdin will be used. All of the functions will also accept an 'f' option, which is ignored and included only for compatibility with the Unix 'tar' program. EXAMPLES 1) tar c - /d0 >/t2 Archive all of the files on drive /d0, and send the resultant archive to stdout, which is redirected to /t2. 2) tar tv sample.tar Will produce a verbose listing of the files in the archive. 3) tar xmb 100 sample.tar a*.c text Will extract 'text' and all files matching the pattern a*.c from the archive sample.tar, ignoring the stored modification dates. Use a buffer of 100 tape blocks. 4) tar xvb 50 sample.tar subdir Will extract all files from the directory 'subdir'. If 'subdir' doesn't exist it will be created. Buffer size is set to 50 blocks. 5) tar xu /mt0 test.c Will extract the file test.c from a tape written on a UNIX machine, all LF characters will be converted to CRs. 6) tar cu /mt0 test.c Will write the file test.c to a tape streamer converting all CR to LF for usage on a UNIX system. 7) tar cfbz sample.tar 100 - Will create the tarfile sample.tar with block size 100. The names of the files to be tared are read from stdin. BUGS This program is much simpler than the standard Unix tar program, and lacks much of its functionality. It cannot add files to a pre-existing archive. It cannot extract anything but the last occurence when more than one copy of a file appears in the archive. There may be problems with very long file names. AUTHOR Michael Huisjes Ported to OS9/6809 by Simmy Turner(simmy@nu.cs.fsu.edu) Ported to OS9/68k & improved by Stephan Paschedag (stp@ethz.uucp ..!mcvax!cernvax!chx400!ethz!stp) Improved by Christian Engel (krischan&informatik.rwth-aachen.de ..!mcvax!unido!strange!krischan) SHAR_EOF chmod 644 'tar.doc' echo shar: "extracting 'tar.hlp'" '(4195 characters)' cat << \SHAR_EOF > 'tar.hlp' @H @T0 USAGE tar <function> <tarfile> [<file> . . .] DESCRIPTION Builds and extracts from Unix tape archive files. The <function> parameter consists of a one-letter function specifier with one or more option characters. <tarfile> is the file to be used for the archive. If <tarfile> is specified as -, then Stdin or Stdout will be used, as appropriate. <file> is one or more filenames to be used by the extract or create operations. @D1 Function_Specifiers : 2 Examples : 3 Bugs_and_Info : 4 @T2 FUNCTION SPECIFIERS t Output a table of all files in the archive. Recognized Options: v Verbose mode. Display file permissions, sizes, and modification dates and times. x Extract the named files from the archive. Wildcard characters * and ? may be used in filename specifications. Recognized Options: v Verbose mode. Display a message for each file extracted. m Do not restore modification dates. Instead, use current date and time as modification date for each extracted file. u convert LF -> CR (for UNIX text files) b Set copy buffer size (number of tape blocks). The number is specified directly after the options. z First read the names of the files to be extracted from a list file, then continue with the names given on the command line. The name of the list file is given after the tar file name. If a ``-'' is specified stdin will be used. This feature is not possible if the tarfile is stdin, too. c Create a new archive file and include the named files. A directory name means to recursively include all files in that directory. Recognized Options: v Verbose mode. Display a message for each file added to the archive. u convert CR -> LF (for UNIX text files) b Set copy buffer size (number of tape blocks). The number is specified directly after the options. d copy contents of several floopy disks to one tar archive z First read the names of the files to be tared from a list file, then continue with the names given on the command line. The name of the list file is given after the tar file name. If a ``-'' is specified stdin will be used. All of the functions will also accept an 'f' option, which is ignored and included only for compatibility with the Unix 'tar' program. @T3 EXAMPLES 1) tar c - /d0 >/t2 Archive all of the files on drive /d0, and send the resultant archive to stdout, which is redirected to /t2. 2) tar tv sample.tar Will produce a verbose listing of the files in the archive. 3) tar xmb 100 sample.tar a*.c text Will extract 'text' and all files matching the pattern a*.c from the archive sample.tar, ignoring the stored modification dates. Use a buffer of 100 tape blocks. 4) tar xvb 50 sample.tar subdir Will extract all files from the directory 'subdir'. If 'subdir' doesn't exist it will be created. Buffer size is set to 50 blocks. 5) tar xu /mt0 test.c Will extract the file test.c from a tape written on a UNIX machine, all LF characters will be converted to CRs. 6) tar cu /mt0 test.c Will write the file test.c to a tape streamer converting all CR to LF for usage on a UNIX system. 7) tar cfbz sample.tar 100 - Will create the tarfile sample.tar with block size 100. The names of the files to be tared are read from stdin. @T4 BUGS This program is much simpler than the standard Unix tar program, and lacks much of its functionality. It cannot add files to a pre-existing archive. It cannot extract anything but the last occurence when more than one copy of a file appears in the archive. There may be problems with very long file names. AUTHOR Michael Huisjes Ported to OS9/6809 by Simmy Turner(simmy@nu.cs.fsu.edu) Ported to OS9/68k & improved by Stephan Paschedag (stp@ethz.uucp ..!mcvax!cernvax!chx400!ethz!stp) Improved by Christian Engel (krischan&informatik.rwth-aachen.de ..!mcvax!unido!strange!krischan) SHAR_EOF chmod 644 'tar.hlp' exit 0 # End of shell archive ---- Cut here ---- Cut here ---- Cut here ---- Cut here ---- Cut here ---- Bye, Krischan. -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- krischan@informatik.rwth-aachen.de *** mcvax!unido!rwthinf!strange!krischan Christian Engel, Lehrstuhl fuer Informatik I, RWTH Aachen Ahornstr. 55, D-5100 Aachen, W. Germany