simmy@ocean.ocean.fsu.edu (Simmule Turner) (07/31/89)
#! /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 the files: # Readme # tar.c # tar.hlp # This archive created: Sun Jul 30 18:50:06 1989 export PATH; PATH=/bin:$PATH if test -f 'Readme' then echo shar: will not over-write existing file "'Readme'" else cat << \SHAR_EOF > 'Readme' For compilation the equivalent of the Kreider c-library is needed. This is for OS-9/6809. Documentation provided by Tim Koonce. Direct bug reports, flames, comments, questions to: simmy@nu.cs.fsu.edu, or gatech!nu.cs.fsu.edu!simmy SHAR_EOF fi # end of overwriting check if test -f 'tar.c' then echo shar: will not over-write existing file "'tar.c'" else cat << \SHAR_EOF > 'tar.c' char scid[]="$Header: tar.c,v 1.4 07/17/88 SrT $"; /* * Modified for use under OS-9/6809 * Simmule Turner simmy@nu.cs.fsu.edu 70651,67 * * Compilation: 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][v] 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. */ #include <stdio.h> #include <direct.h> #include <modes.h> #include <sgstat.h> #include <time.h> #include <utime.h> #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; 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; char modestr[11], stbuf[132]; struct sgbuf opts; struct fildes ofdbuf; int tar_fd; char usage[] = "Usage: tar [ctx][mfv] tarfile [file(s)...]"; char io_buffer[BLOCK_SIZE]; int total_blocks; long convert(); block_size() { return ((int) ((convert(header.member.m_size, LONG_TYPE) + (long) BLOCK_SIZE - 1) / (long) BLOCK_SIZE)); } error(s1, s2) char *s1, *s2; { string_print(NIL_PTR, "%s %s\r\l", s1, s2 ? s2 : ""); flush(); exit(1); } main(argc, argv) int argc; register char *argv[]; { register char *ptr; char **p = "\0"; int i; pflinit(); if (argc < 3) error(usage, NIL_PTR); 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; if (argc > 3) p = &argv[3]; break; /* * Modifiers * SrT */ case 'f': break; case 'm': modtime_fl = TRUE; break; case 'v': verbose_fl = TRUE; break; default : error(usage, NIL_PTR); } } if (creat_fl + ext_fl + show_fl != 1) error(usage, NIL_PTR); if (argv[2][0] == '-') tar_fd = creat_fl ? STDOUT : STDIN; else tar_fd = creat_fl ? creat(argv[2],3) : open(argv[2],1); if (tar_fd < 0) error("Cannot open ", argv[2]); if (creat_fl) { for (i = 3; i < argc; i++) add_file(argv[i]); adjust_boundary(); } else tarfile (p); flush(); exit(0); } BOOL get_header() { register int check; 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 != 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; _gs_opt(tar_fd,&opts); while (get_header()) { mem_name = header.member.m_name; if (ext_fl) { if (*p) { if (strchr(mem_name,'/')) skip_entry(); else { if (match(p,mem_name)) extract(mem_name); else skip_entry(); } } else { if (is_dir(mem_name)) { for (ptr = mem_name; *ptr; ptr++) ; *(ptr - 1) = '\0'; mkdir(mem_name); } else extract(mem_name); } } else { if (!verbose_fl) string_print(NIL_PTR, "%s", mem_name); else { u29mode((int) convert(header.member.m_mode,INT_TYPE)); string_print(NIL_PTR, "%s ",modestr); sprintf(stbuf,"%3d/101 %8ld %s %s", (int) convert(header.member.m_uid, INT_TYPE), convert(header.member.m_size,LONG_TYPE), atime(convert(header.member.m_time,LONG_TYPE)), header.member.m_name); print(stbuf); } print("\r\l"); 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); } } extract(file) register char *file; { register int fd; if (header.member.m_linked == '1') { string_print(NIL_PTR,"Cannot link %s\r\l",header.member.m_link); return; } if ((fd = creat(file, 3)) < 0) { string_print(NIL_PTR, "Cannot create %s\r\l", file); return; } 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) if (_gs_gfd(fd,&ofdbuf,sizeof(ofdbuf)) != ERROR) { 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(); } copy(file, from, to, bytes) char *file; int from, to; register long bytes; { register int rest; int blocks = (int) ((bytes + (long) BLOCK_SIZE - 1) / (long) BLOCK_SIZE); if (verbose_fl) { if (to == tar_fd) sprintf(stbuf,"a %s, %d tape blocks\r\l",file,blocks); else sprintf(stbuf,"x %s, %ld bytes, %d tape blocks\r\l",file,bytes,blocks); print(stbuf); } while (blocks--) { read(from, io_buffer, BLOCK_SIZE); rest = (bytes > (long) BLOCK_SIZE) ? BLOCK_SIZE : (int) bytes; mwrite(to, io_buffer, (to == tar_fd) ? BLOCK_SIZE : rest); bytes -= (long) rest; } } 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; else { int fd; if ((fd = open(dir_name,0x83)) > 0) { _ss_attr(fd, 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("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; if ((fd = open(file,0x81)) < 0) if ((fd = open(file, 1)) < 0) { string_print(NIL_PTR, "Cannot open %s\r\l", file); return; } if (_gs_gfd(fd,&st,sizeof(st)) < 0) { string_print(NIL_PTR, "Cannot get file descriptor for %s\r\l",file); close(fd); return; } make_header(path_name(file), &st); mwrite(tar_fd, &header, sizeof(header)); if (!(st.fd_att & S_IFDIR)) copy(path_name(file), fd, tar_fd, st.fd_fsize); 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)) if (dir.dir_addr[0] || dir.dir_addr[1] || dir.dir_addr[2]) { strhcpy(dir.dir_name,dir.dir_name); add_file(dir.dir_name); } chdir(".."); add_path(NIL_PTR); } } else print(" Tar: unknown file type. Not added.\r\l"); close(fd); } make_header(file, st) char *file; register struct fildes *st; { register char *ptr = header.member.m_name; char tbuf[6]; clear_header(); while (*ptr++ = *file++) ; if (st->fd_att & S_IFDIR) { *(ptr - 1) = '/'; st->fd_fsize = 0L; } _strass(tbuf,st->fd_date,5); tbuf[5] = 0; string_print(header.member.m_mode, "%I ", o2umode(st->fd_att)); 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); 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)); while (total_blocks++ < BLOCK_BOUNDARY) mwrite(tar_fd, &header, sizeof(header)); close(tar_fd); } mread(fd, address, bytes) int fd, bytes; char *address; { if (read(fd, address, bytes) != bytes) error("Tar: read error.", NIL_PTR); } mwrite(fd, address, bytes) int fd, bytes; char *address; { if (write(fd, address, bytes) != bytes) error("Tar: write error.", NIL_PTR); 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; } 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); 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); } match(p,name) char **p, *name; { char **q = p; while (*q) if (patmatch(*q++,name,1)) return(1); return(0); } SHAR_EOF fi # end of overwriting check if test -f 'tar.hlp' then echo shar: will not over-write existing file "'tar.hlp'" else cat << \SHAR_EOF > 'tar.hlp' 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. 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. 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 xm 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. 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) SHAR_EOF fi # end of overwriting check # End of shell archive exit 0 -- Simmule Turner |Arpa: simmy@nu.cs.fsu.edu | "Wait until it is Florida State Univ |Uucp: gatech!nu.cs.fsu.edu!simmy | finished, it will 444 OSB | Cis: 70651,67 Genie:simmy | be great" Tallahassee, FL 32306 | Tel: +1 904 644 1573 | simmy