[comp.os.os9] tar for OS-9/6809

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