Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP
Path: utzoo!watmath!clyde!cbatt!ihnp4!chinet!megabyte
From: megabyte@chinet.UUCP
Newsgroups: comp.sys.m6809
Subject: AR archive program for OS9
Message-ID: <1005@chinet.UUCP>
Date: Thu, 8-Jan-87 20:53:17 EST
Article-I.D.: chinet.1005
Posted: Thu Jan 8 20:53:17 1987
Date-Received: Fri, 9-Jan-87 23:05:00 EST
Reply-To: megabyte@chinet.UUCP (Dr. Megabyte)
Organization: Chinet - Public Access Unix
Lines: 1558
Summary: Simular but not compatable with MS-DOS}i ARC program
This is the 'ar' archive program written by Carl Krieder
and now being used to pack the user group library on CIS and
on DELPHI. It provides a means to pack sever files into one
file while at the same time compressing them.
This is being posted to the net with Carl's Permission.
--------------------------------------------------------------
#! /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:
# ar.c
# ar.doc
# ar.h
# ar.man
# arsup.c
# delz1.c
# dir.c
# lz1.c
# lz1.h
# lz1glob.c
# lz1init.c
# makefile.09
# makefile.68
# This archive created: Thu Jan 8 19:39:49 1987
# By: Dr. Megabyte ()
export PATH; PATH=/bin:/usr/bin:$PATH
if test -f 'ar.c'
then
echo shar: "will not over-write existing file 'ar.c'"
else
cat << \SHAR_EOF > 'ar.c'
/*
** file archive utility loosely modeled after Kernigan & Plauger
*/
#include
#include
#include "ar.h"
FN *fnhead = (FN *)NULL;
char *hid = HID,
*suf = SUF;
char *mod, /* pointer to module name */
*archfile; /* name of archive file */
int all = FALSE, /* true to access old versions */
supflag = FALSE, /* true to supress compression */
zflag = FALSE; /* true if names come from stdin */
char *emalloc();
main(argc, argv)
int argc;
char **argv;
{
char command, *p;
int n, updating;
FILE *afp;
pflinit();
mod = *argv++; /* save program name */
if ((argc < 3) || (*(p = *argv++) != '-'))
help();
command = tolower(*++p);
proc_opt(++p); /* process command modifiers */
archfile = *argv++;
n = strlen(archfile);
if ((strcmp(archfile + n - SUFSIZ, suf)) != 0)
archfile = strcat(strcpy(emalloc(n + SUFSIZ + 1), archfile), suf);
if (get_names(argc -= 3, argv, command == 'u') == 0)
if (command == 'u')
fatal(0, "none of the targets found\n");
if (command == 'u')
{
if ((afp = fopen(archfile, "r+")) == NULL) /* try old first */
afp = fopen(archfile, "w+"); /* not there - create it */
}
else
{
if ((afp = fopen(archfile, "r")) == NULL) /* save file date */
fatal(errno, "can't find %s\n", archfile);
}
proc_cmd(command, afp); /* process a command */
}
/*page*/
/*
** process command modifiers
*/
proc_opt(p)
char *p;
{
while (*p)
switch (tolower(*p++))
{
case 'a' : /* all - even old ones */
all = TRUE;
break;
case 's' : /* supress compression */
supflag = TRUE;
break;
case 'z' : /* get names from stdin */
zflag = TRUE;
break;
default :
help();
}
}
/*
** process the command
*/
proc_cmd(command, afp)
char command;
FILE *afp;
{
switch (command)
{
case 'd' :
break;
case 't' : /* table of contents */
table(afp);
break;
case 'u' : /* update or add */
update(afp);
break;
case 'p' : /* print member(s) */
extract(afp, 0);
break;
case 'x' : /* extract member(s) */
extract(afp, 1);
break;
default :
help();
}
}
/*page*/
/*
** extract file(s) from the archive
** copy a file from archive and restore it's origional attrs
*/
extract(afp, flag)
FILE *afp;
int flag; /* 0 = listing, 1 = writing to file */
{
FILE *ofp; /* assume just listing */
HEADER header;
FN *fnp;
FILE *spl_open();
if (fnhead == (FN *) NULL)
stash_name("*"); /* fake for special case */
while ((get_header(afp, &header)) != EOF)
{
for (fnp = fnhead; fnp; fnp = fnp->fn_link)
if (patmatch(fnp->fn_name, header.a_name, TRUE) == TRUE
|| (header.a_stat != 0 && all == TRUE))
break;
if (fnp == 0)
fseek(afp, header.a_size, 1); /* file not found */
else
{
if (!flag)
copy_from(afp, stdout, &header);
else
{
printf("extracting <%s>\n", header.a_name);
ofp = spl_open(&header);
copy_from(afp, ofp, &header);
set_fstat(ofp->_fd, &header.a_attr);
fclose(ofp);
}
}
}
}
/*page*/
/*
** list a table of contents for the archive file
** only show files matching the search mask which are current
** unless the all flag is set, whereupon we will show old ones too
*/
table(fp)
FILE *fp;
{
HEADER header;
FN *fnp;
long n, c4tol();
if (fnhead == (FN *) NULL)
stash_name("*"); /* fake for special case */
printf(" file stored\n");
printf(" file name ver file date size size\n");
printf("----------------------------- --- -------------- ----- -----\n");
while ((get_header(fp, &header)) != EOF)
for (fnp = fnhead; fnp; fnp = fnp->fn_link)
{
if (patmatch(fnp->fn_name, header.a_name, TRUE) == TRUE
&& (header.a_stat == 0 || all == TRUE))
printf("%-29s %2d %02d/%02d/%02d %02d:%02d %7ld %7ld\n",
header.a_name, header.a_stat, header.a_attr.fd_date[0],
header.a_attr.fd_date[1], header.a_attr.fd_date[2],
header.a_attr.fd_date[3], header.a_attr.fd_date[4],
c4tol(header.a_attr.fd_fsize), header.a_size);
fseek(fp, header.a_size, 1);
}
}
/*page*/
/*
** add new files or replace existing files
*/
update(afp)
FILE *afp;
{
FILE *ifp;
HEADER header;
FN *fnp;
int synch, n;
long bytes, head_pos, tail_pos, copy_to(), c4tol();
while ((get_header(afp, &header)) != EOF)
{
for (fnp = fnhead; fnp; fnp = fnp->fn_link)
if ((n = patmatch(fnp->fn_name, header.a_name, TRUE)) == TRUE)
{
++header.a_stat; /* mark it older */
fseek(afp, (long) -sizeof(HEADER), 1);
if ((fwrite(&header, sizeof(HEADER), 1, afp)) == NULL)
fatal(errno, "write failure on delete\n");
}
fseek(afp, header.a_size, 1);
}
for (fnp = fnhead; fnp; fnp = fnp->fn_link)
{
if ((ifp = fopen(fnp->fn_name, "r")) == NULL)
if (errno == 214)
continue; /* a directory, we presume */
else
fatal(errno, "can't find %s\n", fnp->fn_name);
printf("archiving <%s>\n", fnp->fn_name);
if (supflag || ((synch = getw(ifp)) == MSYNC))
header.a_type = PLAIN;
else
header.a_type = COMP1;
strcpy(header.a_hid, hid);
setmem(header.a_name, FNSIZ + 1, ' ');
strcpy(header.a_name, fnp->fn_name);
get_fstat(ifp->_fd, &header.a_attr);
header.a_stat = '\0';
rewind(ifp);
head_pos = ftell(afp); /* save for update */
fwrite(&header, sizeof(HEADER), 1, afp); /* skip ahead */
bytes = head_pos + c4tol(header.a_attr.fd_fsize) + sizeof(HEADER);
set_fsize(afp->_fd, bytes);
header.a_size = copy_to(afp, ifp, &header);
tail_pos = ftell(afp);
fclose(ifp);
fseek(afp, head_pos, 0); /* back up to header pos */
if ((fwrite(&header, sizeof(HEADER), 1, afp)) == NULL)
fatal(errno, "write error on header for %s\n", fnp->fn_name);
fseek(afp, tail_pos, 0); /* go to end of file */
}
set_fsize(afp->_fd, tail_pos); /* now set real file size */
}
/*page*/
/*
** gather file names from command line or std in
** use linked list to avoid finite limit on number of names
*/
get_names(ac, av, updating)
int ac;
char **av;
int updating; /* TRUE if command is update */
{
char *p, *q, *r, buf[80], *strhcpy();
int dirfd, found = 0;
DIRENT *dirp, *nextdir();
while (ac--)
if (!updating || !(index(*av, '?') || index(*av, '*')))
found += stash_name(*av++);
else
{
*(p = buf) = '\0';
if (q = rindex((r = *av++), '/'))
{
strcpy(p, r); /* copy all to buffer */
*(r = rindex(p, '/')) = '\0'; /* break in two */
++q; /* pointer to pattern */
}
else
{
q = r;
r = p; /* swap pointers */
}
dirfd = opndir(*p ? p : ".");
if (*p)
*r++ = '/'; /* set up for append */
while ((dirp = nextdir(dirfd)) != -1)
if (patmatch(q, strhcpy(r, dirp->dir_name), TRUE))
if ((strucmp(p, archfile)) != 0) /* not self */
found += stash_name(p);
}
if (zflag)
while (gets(buf))
if (buf[0] != '\0')
found += stash_name(buf);
return (found);
}
/*page*/
/*
** squirrel a name away in the linked list of targets
*/
stash_name(p)
char *p;
{
static FN *fnp;
char *q;
if (*p == '/')
fatal(1, "absolute path illegal <%s>\n", p);
q = emalloc(sizeof(FN) + strlen(p));
if (fnhead == (FN *)NULL)
fnhead = fnp = q;
else
fnp = fnp->fn_link = q;
strcpy(fnp->fn_name, p);
fnp->fn_link = (FN *)NULL;
return (1); /* we saved one name */
}
/*page*/
/*
** get the next header from the file, perhaps skipping the body
*/
get_header(fp, hp)
FILE *fp;
HEADER *hp;
{
if ((fread(hp, sizeof(HEADER), 1, fp)) == NULL)
return (EOF);
if (strncmp(hp->a_hid, hid, HIDSIZ) != 0)
fatal(1, "file not archive or damaged\n");
return (0);
}
/*
** here we will recreate a tree that was collapsed into the archive file
*/
FILE *
spl_open(hp)
HEADER *hp;
{
char buf[FNSIZ+3];
FILE *ofp;
char *p;
long c4tol();
p = hp->a_name;
while (p = index(p, '/'))
{
*p = '\0'; /* truncate temporarily */
if (access(hp->a_name, DIRCHK) == -1)
if (mknod(hp->a_name, DIRMAK) == -1) /* try to make it */
fatal(errno, "can't make <%s>\n", hp->a_name);
*p++ = '/'; /* put back the delim */
}
strcpy(buf, hp->a_name);
if (hp->a_stat)
sprintf(&buf[strlen(buf)], ".%d", hp->a_stat); /* make unique */
if ((ofp = fopen(buf, "w")) == NULL)
fatal(errno, "create failure on %s\n", buf);
set_fsize(ofp->_fd, c4tol(hp->a_attr.fd_fsize));
return (ofp);
}
/*page*/
/*
** copy an archived file from an archive
*/
copy_from(ifp, ofp, hp)
FILE *ifp, *ofp;
HEADER *hp;
{
long bytes = hp->a_size;
int byt;
switch (hp->a_type)
{
case PLAIN :
while (bytes--)
{
if ((byt = getc(ifp)) == ERROR)
fatal(errno, "read error while copying\n");
if (putc(byt, ofp) == ERROR)
fatal(errno, "write error while copying\n");
}
break;
case COMP1 :
if ((byt = de_LZ_1(ifp, ofp, bytes)) == -2)
fatal(1, "archive damaged or file not compressed\n");
break;
default :
fatal(1, "unknown compression algo\n");
}
}
/*page*/
/*
** copy an file to an archive
*/
long
copy_to(ofp, ifp, hp)
FILE *ofp, *ifp;
HEADER *hp;
{
long bytes = 0;
int byt;
switch (hp->a_type)
{
case PLAIN :
while ((byt = getc(ifp)) != ERROR)
if (putc(byt, ofp) == ERROR)
fatal(errno, "write error while copying\n");
else
++bytes;
if (ferror(ifp))
fatal(errno, "read error while copying\n");
break;
case COMP1 :
if ((byt = LZ_1(ifp, ofp, &bytes)) == -3)
fatal(1, "string table overflow on compression\n");
break;
default :
fatal(1, "unknown compression algo\n");
}
return (bytes);
}
/*page*/
/*
** get memory from the system or die trying
*/
char *
emalloc(n)
int n;
{
char *p;
if ((p = malloc(n)) == NULL)
fatal(errno, "Can't get memory\n");
return (p);
}
/*
** help for the user
*/
help()
{
fprintf(stderr, "Ar V1.2 - archive file manager\n");
fprintf(stderr, "Usage: Ar -[] [file .. ]\n");
fprintf(stderr, " is one of the following:\n");
fprintf(stderr, " t show table of contents for archive\n");
fprintf(stderr, " u update/add file(s) to the archive\n");
fprintf(stderr, " p print file(s) from the archive\n");
fprintf(stderr, " x extract file(s) from the archive\n");
fprintf(stderr, " is one of the following:\n");
fprintf(stderr, " a all versions (for extract)\n");
fprintf(stderr, " s supress file compression\n");
fprintf(stderr, " z read names for from std in\n");
fprintf(stderr, "\n File names can include the meta chars ");
fprintf(stderr, "* and ?, or path lists.\n");
exit (1);
}
/*
** print a fatal error message and exit
*/
fatal(code, msg, arg1, arg2)
int code;
char *msg;
int arg1, arg2;
{
fprintf(stderr, "%s: ", mod);
fprintf(stderr, msg, arg1, arg2);
exit (code);
}
SHAR_EOF
fi
if test -f 'ar.doc'
then
echo shar: "will not over-write existing file 'ar.doc'"
else
cat << \SHAR_EOF > 'ar.doc'
AR Version 1.2
Ar is a utility modeled loosely after the archive utility in the
Kernigan & Plauger book, Software Tools. It's purpose is to gather
together files into a common file, in order to save space, to keep
related files together, to ease the transmission of files by
telephone, and other similar uses. It uses Lempel-Zev compression on
text files, so substantial space savings can result. Several versions
of the same file can be kept in one archive without file name
contention. This version compiles and runs on OS9 LI and LII, and on
OSK.
The command line syntax is:
Ar <-command>[options] [file_specification(s)]
The items enclosed in <> are required and those in [] are optional.
The commands available operate on the archive in some way:
-p print a file(s) to standard output
-t print a table of contents for the archive
-u update/add file(s) to the archive
-x extract file(s) from the archive
The options modify the behavior of a command:
-a causes all old files to be included in the command
-s supresses compress on storage, no effect on retrieval
-z names of files to process are read on standard input
Most of these commands and modifiers are obvious, but not so the
detail operation. A version number is kept as part of the header
record associated with each file in the archive. The current file has
a version number of '0'. Older versions of files with the same names
have succedingly greater numbers. Commands normally operate only on
current copies, or those with a version of '0'. The 'a' option will
cause all to be selected. To avoid name conflicts on extraction, the
version number for all but the current version will be appended to the
name of the created file.
A file spec can include the meta characters '?' (matches any
character) and '*' (matches any string of characters). Thus the
following examples are valid file specifications:
*.c matches ar.c, dir.c, but not dir.h or dir.cc
ar.? matches ar.c, ar.h, but not ar.ch
a*z matches az, abz, abcz, abcdz, etc.
*.* matches anything with a dot in it
* matches anything and everything
Since the OS9 (6809) shell does not expand any meta characters, I have
provided internal expansion of same. When run on OSK this is
transparent for update since 'ar' also accepts a list of files on the
command line. In the case of extraction and printing, we do not want
the shell generating filenames from the current directory. In this
case, any file specification containing a meta character must be
quoted to protect it from the shell.
Meta characters can be used for files both bound for the archive and
for files to be extracted. The p, t, and x commands default to '*' if
no explicit file list is given. The u command MUST have a file spec.
The file specification can include a path list if it is not absolute.
That is, '/d0/work/*.c' will not be accepted, but 'work/*.c' will be.
During extraction, if the directory in the path list of the archived
file cannot be found, it will be created. Here are some sample
command lines:
ar -pa archfile print all files, even old versions
ar -p archfile print all current files
ar -p archfile *.c print all current files with '.c' suffix
ar -p archfile file.c print only 'file.c'
The x and t commands would work the same way, but the u command is
slightly different. Sample command lines follow:
ar -us archfile *.c add all files with '.c' suffix without
compression
ar -u archfile * add ALL files using compression unless binary
ar -u archfile file.c add only 'file.c'
If a file with the same name as the one to be added exists in the
archive already, the version number of the existing one is incremented
and the new version is added to the end of the archive. The atribute
section of the file descriptor is saved in the archive and restored to
the file (as much as is possible) on extraction. OS9 limits this to
owner, creation date, and last modified date. Size of course will be
the same, but the file permissions will default to some value. It is
possible (I do it in 'arc') to preserve permissions, but that requires
that the disk be opened raw and written raw. I chose not to do that
here.
Pre-extension is used during extraction to avoid segment table
overflow problems, and will be added to update as soon as the
mechanism becomes clear.
There is no way in this version of 'ar' to recover space of obsoleted
or updated files, or to delete files. This does not seem important,
given the design goal. This can be simulated with a burst/re-archive
operation, which is, in principle, how 'ar' will have to do it.
The concatenation of two '.ar' files is still an archive, with certain
limitations. If a duplicate file with duplicate version appears in
both archives prior to concatenation, only the last one physically in
the archive file will be saved on extraction. One could, of course,
double the size of 'ar' with all manner of prompting, perusing, and
choosing in cases like that but I prefer to rely on the inate ability
of the user deal with such problems outside of 'ar'.
The archive file is opened in update only if necessary, so the last
modified date of the archive is not disturbed by extraction or
perusal. This should go a long way toward making rational backup of
archives possible.
I would like to maintain control of the evolution of this utility,
primarily since it is used to generate and burst most of the files in
the new User Group Data Base on CIS. It is important to maintain
backwards compatibility. I will be open to suggestions for
improvements, additions, and (of course) bug fixes. The first likely
addition will be the newer LZ compression to improve the compaction.
Carl Kreider 71076,76
SHAR_EOF
fi
if test -f 'ar.h'
then
echo shar: "will not over-write existing file 'ar.h'"
else
cat << \SHAR_EOF > 'ar.h'
#include
#define DIRMAK S_IFDIR+S_IOEXEC+S_IOWRITE+S_IOREAD+S_IEXEC+S_IWRITE+S_IREAD
#define DIRCHK S_IFDIR+S_IREAD
#ifdef OSK
#define MSYNC 0x4AFC
#else
#define MSYNC 0x87CD
#endif
#define FALSE (0)
#define TRUE (!FALSE)
#define ERROR (-1)
#define HID "+AR0.0+"
#define HIDSIZ 7
#define SUF ".ar"
#define SUFSIZ 3
#define FNSIZ 65
#define MAXLINE 256
#define PLAIN 0 /* plain text or object */
#define SQ 1 /* old fashion CPM squeeze */
#define COMP1 2 /* LZ compression type 1 */
#define COMP2 3 /* LZ comperssion type 2 */
typedef struct {
char dir_name[28];
long dir_addr; /* assumes that 29th char is null */
} DIRENT;
typedef struct { /* obvious definitions */
char fd_att;
char fd_own[2];
char fd_date[5];
char fd_link;
char fd_fsize[4];
char fd_dcr[3];
} FILDES;
/* NOTE that a_size is on an even byte boundary */
typedef struct {
char a_hid[HIDSIZ+1]; /* header id string */
char a_name[FNSIZ+1]; /* name of the archived file */
long a_size; /* size of archive (not virgin) file */
char a_type; /* archive type - virg, packed, etc */
char a_stat; /* status of file - good, deleted, .. */
FILDES a_attr; /* attributes of the archived file */
} HEADER;
typedef struct fn {
struct fn *fn_link; /* link to next file name */
char fn_name[1]; /* the name itself */
} FN;
SHAR_EOF
fi
if test -f 'ar.man'
then
echo shar: "will not over-write existing file 'ar.man'"
else
cat << \SHAR_EOF > 'ar.man'
AR Version 1.2
Ar is a utility modeled loosely after the archive utility in the
Kernigan & Plauger book, Software Tools. It's purpose is to gather
together files into a common file, in order to save space, to keep
related files together, to ease the transmission of files by
telephone, and other similar uses. It uses Lempel-Zev compression on
text files, so substantial space savings can result. Several versions
of the same file can be kept in one archive without file name
contention. This version compiles and runs on OS9 LI and LII, and on
OSK.
The command line syntax is:
Ar <-command>[options] [file_specification(s)]
The items enclosed in <> are required and those in [] are optional.
The commands available operate on the archive in some way:
-p print a file(s) to standard output
-t print a table of contents for the archive
-u update/add file(s) to the archive
-x extract file(s) from the archive
The options modify the behavior of a command:
-a causes all old files to be included in the command
-s supresses compress on storage, no effect on retrieval
-z names of files to process are read on standard input
Most of these commands and modifiers are obvious, but not so the
detail operation. A version number is kept as part of the header
record associated with each file in the archive. The current file has
a version number of '0'. Older versions of files with the same names
have succedingly greater numbers. Commands normally operate only on
current copies, or those with a version of '0'. The 'a' option will
cause all to be selected. To avoid name conflicts on extraction, the
version number for all but the current version will be appended to the
name of the created file.
A file spec can include the meta characters '?' (matches any
character) and '*' (matches any string of characters). Thus the
following examples are valid file specifications:
*.c matches ar.c, dir.c, but not dir.h or dir.cc
ar.? matches ar.c, ar.h, but not ar.ch
a*z matches az, abz, abcz, abcdz, etc.
*.* matches anything with a dot in it
* matches anything and everything
Since the OS9 (6809) shell does not expand any meta characters, I have
provided internal expansion of same. When run on OSK this is
transparent for update since 'ar' also accepts a list of files on the
command line. In the case of extraction and printing, we do not want
the shell generating filenames from the current directory. In this
case, any file specification containing a meta character must be
quoted to protect it from the shell.
Meta characters can be used for files both bound for the archive and
for files to be extracted. The p, t, and x commands default to '*' if
no explicit file list is given. The u command MUST have a file spec.
The file specification can include a path list if it is not absolute.
That is, '/d0/work/*.c' will not be accepted, but 'work/*.c' will be.
During extraction, if the directory in the path list of the archived
file cannot be found, it will be created. Here are some sample
command lines:
ar -pa archfile print all files, even old versions
ar -p archfile print all current files
ar -p archfile *.c print all current files with '.c' suffix
ar -p archfile file.c print only 'file.c'
The x and t commands would work the same way, but the u command is
slightly different. Sample command lines follow:
ar -us archfile *.c add all files with '.c' suffix without
compression
ar -u archfile * add ALL files using compression unless binary
ar -u archfile file.c add only 'file.c'
If a file with the same name as the one to be added exists in the
archive already, the version number of the existing one is incremented
and the new version is added to the end of the archive. The atribute
section of the file descriptor is saved in the archive and restored to
the file (as much as is possible) on extraction. OS9 limits this to
owner, creation date, and last modified date. Size of course will be
the same, but the file permissions will default to some value. It is
possible (I do it in 'arc') to preserve permissions, but that requires
that the disk be opened raw and written raw. I chose not to do that
here.
Pre-extension is used during extraction to avoid segment table
overflow problems, and will be added to update as soon as the
mechanism becomes clear.
There is no way in this version of 'ar' to recover space of obsoleted
or updated files, or to delete files. This does not seem important,
given the design goal. This can be simulated with a burst/re-archive
operation, which is, in principle, how 'ar' will have to do it.
The concatenation of two '.ar' files is still an archive, with certain
limitations. If a duplicate file with duplicate version appears in
both archives prior to concatenation, only the last one physically in
the archive file will be saved on extraction. One could, of course,
double the size of 'ar' with all manner of prompting, perusing, and
choosing in cases like that but I prefer to rely on the inate ability
of the user deal with such problems outside of 'ar'.
The archive file is opened in update only if necessary, so the last
modified date of the archive is not disturbed by extraction or
perusal. This should go a long way toward making rational backup of
archives possible.
I would like to maintain control of the evolution of this utility,
primarily since it is used to generate and burst most of the files in
the new User Group Data Base on CIS. It is important to maintain
backwards compatibility. I will be open to suggestions for
improvements, additions, and (of course) bug fixes. The first likely
addition will be the newer LZ compression to improve the compaction.
Carl Kreider 71076,76
SHAR_EOF
fi
if test -f 'arsup.c'
then
echo shar: "will not over-write existing file 'arsup.c'"
else
cat << \SHAR_EOF > 'arsup.c'
/*
** some lowlevel routines that are also in my library.
** this file contains the C versions for those who don't
** have access to my library
*/
#ifndef OSK
#include
#endif
#include "ar.h"
#include
#include
/*
** get file stats using _os9 for portability
*/
get_fstat(pn, fs)
int pn;
FILDES *fs;
{
#ifdef OSK
_gs_gfd(pn, fs, sizeof(FILDES));
#else
struct registers regs;
regs.rg_a = pn;
regs.rg_b = SS_FD;
regs.rg_x = fs;
regs.rg_y = sizeof(FILDES);
_os9(I_GETSTT, ®s);
#endif
}
/*
** set file attributes
*/
set_fstat(pn, fs)
int pn;
FILDES *fs;
{
#ifdef OSK
_ss_pfd(pn, fs, sizeof(FILDES));
#else
struct registers regs;
regs.rg_a = pn;
regs.rg_b = SS_FD;
regs.rg_x = fs;
regs.rg_y = sizeof(FILDES);
_os9(I_SETSTT, ®s);
#endif
}
/*page*/
/*
** get the file size
*/
long
get_fsize(pn)
int pn;
{
long size;
#ifdef OSK
size = _gs_size(pn);
#else
getstat(SS_SIZE, pn, &size);
#endif
return (size);
}
/*
** change the file size
*/
set_fsize(pn, size)
int pn;
long size;
{
#ifdef OSK
_ss_size(pn, size);
#else
setstat(SS_SIZE, pn, size);
#endif
}
/*
** convert a long read from disk as an array of char
** back to a long.
*/
long
c4tol(s)
char *s;
{
long x = 0;
x = (x + (*s++ &0xff)) << 8;
x = (x + (*s++ &0xff)) << 8;
x = (x + (*s++ &0xff)) << 8;
x = (x + (*s &0xff));
return (x);
}
/*page*/
/*
** Returns true if string s matches pattern p.
*/
patmatch(p, s, f)
char *p; /* pattern */
register char *s; /* string to match */
char f; /* flag for case force */
{
char pc; /* a single character from pattern */
while (pc = (f ? toupper(*p++) : *p++))
{
if (pc == '*')
{
do { /* look for match till s exhausted */
if (patmatch (p, s, f))
return (1);
} while (*s++);
return (0);
}
else
if (*s == 0)
return (0); /* s exhausted, p not */
else
if (pc == '?')
s++; /* matches all, just bump */
else
if (pc != (f ? toupper(*s++) : *s++))
return (0);
}
return (!*s); /* p exhausted, ret true if s exhausted */
}
/*
** initialize memory (variables) of string type
*/
setmem(p, q, v)
char *p; /* string */
int q; /* count */
char v; /* value */
{
while (q--)
*p++ = v;
}
#include
/*
** special strcmp to ignore case
*/
strucmp(s1, s2)
char *s1;
register char *s2;
{
while (toupper(*s1) == toupper(*s2))
{
if (*s2++ == 0)
return (0);
s1++;
}
return (toupper(*s1) - toupper(*s2));
}
SHAR_EOF
fi
if test -f 'delz1.c'
then
echo shar: "will not over-write existing file 'delz1.c'"
else
cat << \SHAR_EOF > 'delz1.c'
/*
* Decompress the input file.
*/
#include
#include "lz1.h"
WORD
de_LZ_1(infile, outfile, bytes)
FILE *infile, *outfile;
long bytes;
{
WORD tag, finchar, code, oldcode, incode, stack_top = MAXSTACK;
WORD getcode();
char stack[MAXSTACK];
if (fread(&tag, sizeof(tag), 1, infile) < 1 || tag != TAG)
return (-2);
lz1_init();
lz_bytes = bytes - sizeof(tag);
for (free_ent = 0; free_ent < 256; free_ent++)
{
Code[free_ent].next = Code[free_ent].chain = NULL;
Code[free_ent].prefix = NULL;
Code[free_ent].suffix = free_ent;
}
finchar = oldcode = getcode(infile);
putc((char)finchar, outfile); /* first code must be 8 bits */
while ((code = getcode(infile)) != -1)
{
incode = code;
if (code >= free_ent) /* Special case for KwKwK string. */
{
stack[--stack_top] = finchar;
code = oldcode;
}
while (code >= 256) /* Generate characters in reverse order */
{
stack[--stack_top] = Code[code].suffix;
code = Code[code].prefix;
}
stack[--stack_top] = finchar = Code[code].suffix;
/* And put them out in forward order */
fwrite(&stack[stack_top], 1, MAXSTACK - stack_top, outfile);
stack_top = MAXSTACK;
if ((code = free_ent) < maxmaxcode) /* Generate new entry */
{
Code[code].prefix = oldcode;
Code[code].suffix = finchar;
free_ent++;
}
oldcode = incode; /* Remember previous code. */
}
return (code);
}
/*page*/
/*
* Read one code from the input file. If EOF, return -1.
*/
WORD
getcode(infile)
FILE *infile;
{
WORD code;
static WORD size = 0;
if (offset >= size || free_ent > maxcode)
{
if (free_ent > maxcode)
{
n_bits++; /* new entry too big, increase size */
maxcode = (n_bits == maxbits) ? maxmaxcode : (1 << n_bits) - 1;
}
if (lz_bytes <= 0)
return (-1); /* "eof" */
size = (lz_bytes > (long) n_bits) ? n_bits : (int) lz_bytes;
size = fread(buf, 1, size, infile); /* read new buffer too */
if (size <= 0)
return (-1);
lz_bytes -= size;
offset = 0;
/*
* Convert size to bits, and round down so that stray bits
* at the end aren't treated as data
*/
size = BytesToBits(size) - (n_bits - 1);
}
code = fetch();
offset += n_bits;
return(code);
}
fetch()
{
WORD w_offset, word, size2;
word = offset >> LOG2WSIZE;
w_offset = LowOrder(LOG2WSIZE) & offset;
if (w_offset + n_bits <= WSIZE)
{
return((buf[word] >> ((WSIZE - n_bits) - w_offset))
& LowOrder(n_bits));
}
else
{
size2 = n_bits - (WSIZE - w_offset);
return(((buf[word] << size2) |
(buf[word + 1] >> (WSIZE - size2)))
& LowOrder(n_bits));
}
}
SHAR_EOF
fi
if test -f 'dir.c'
then
echo shar: "will not over-write existing file 'dir.c'"
else
cat << \SHAR_EOF > 'dir.c'
#include "ar.h"
/*
** open a directory, returning an fd or -1
*/
int
opndir(s)
char *s;
{
return (open(s, 0x81));
}
/*
** return a pointer to the next directory entry
*/
static DIRENT dent;
DIRENT *
nextdir(fd)
int fd;
{
char c;
do {
if (read(fd, &dent, sizeof(dent)) <= 0)
return (-1);
} while ((c = dent.dir_name[0]) == 0 || (c & 0x7f) == '.');
return (&dent);
}
SHAR_EOF
fi
if test -f 'lz1.c'
then
echo shar: "will not over-write existing file 'lz1.c'"
else
cat << \SHAR_EOF > 'lz1.c'
/*
* Writes compressed file to outfile.
*/
#include
#include "lz1.h"
LZ_1(infile, outfile, bytes)
FILE *infile, *outfile;
long *bytes;
{
WORD c, ent, n_ent, reslt, tag = TAG;
for (free_ent = 0; free_ent < 256; free_ent++)
{
Code[free_ent].next = Code[free_ent].chain = NULL;
Code[free_ent].suffix = free_ent;
}
lz1_init();
lz_bytes = sizeof(tag);
fwrite(&tag, 1, sizeof(tag), outfile); /* mark as LZ */
ent = getc(infile);
while (!feof(infile) && (c = getc(infile)) != EOF)
{
/*
** Find the entry corresponding to the current entry suffixed
** with c. Since the entries are sorted, suffix > c is as
** good as a null to indicate the need to create a new entry.
*/
n_ent = Code[ent].chain;
for (;;)
{
if (!n_ent || Code[n_ent].suffix > c)
{
output(ent, outfile);
if (reslt = addentry(c, ent))
/* return (reslt) */;
ent = c;
break;
}
else
if (Code[n_ent].suffix == c)
{
ent = n_ent;
break;
}
else
n_ent = Code[n_ent].next;
}
}
output(ent, outfile); /* put out final code */
output(-1, outfile); /* and -1 to flush and finish */
*bytes = lz_bytes;
return (0);
}
/*page*/
addentry(c, ent)
WORD c, ent;
{
register WORD p_ent;
/* if the table is full, there's nothing we can do. */
if (free_ent >= maxmaxcode)
return (-3);
Code[free_ent].chain = NULL;
Code[free_ent].suffix = c;
if (!(p_ent = Code[ent].chain) || c < Code[p_ent].suffix)
{
Code[free_ent].next = p_ent;
Code[ent].chain = free_ent;
}
else
{
while ((ent = Code[p_ent].next) && c >= Code[ent].suffix)
p_ent = ent;
Code[free_ent].next = ent;
Code[p_ent].next = free_ent;
}
free_ent++;
return (0);
}
/*page*/
/*
* Output the given code.
*/
output(code, ofp)
WORD code;
FILE *ofp;
{
WORD n;
if (code < 0)
{
/* at EOF--flush buffers and pack up */
if (offset > 0)
{
fwrite(buf, 1, n = (offset + 7) / 8, ofp);
lz_bytes += n;
offset = 0;
}
fflush(ofp);
return (0);
}
insert_bit(code);
if ((offset += n_bits) == BytesToBits(n_bits))
{
fwrite(buf, 1, n_bits, ofp);
lz_bytes += n_bits;
offset = 0;
}
/*
* If the next entry is going to be too big for the code size,
* then increase it, if possible.
*/
if (free_ent <= maxcode)
return;
/*
* Write the whole buffer, because the input side won't
* discover the size increase until after it has read it.
*/
if (offset > 0)
{
fwrite(buf, 1, n_bits, ofp);
lz_bytes += n_bits;
offset = 0;
}
n_bits++;
if (n_bits == maxbits)
maxcode = maxmaxcode;
else
maxcode = (1 << n_bits) - 1;
}
/* insert a value of "n_bits" bits at "offset" bits into buf */
insert_bit(value)
WORD value;
{
WORD w_offset, word, shift, size1, size2;
word = offset >> LOG2WSIZE;
w_offset = LowOrder(LOG2WSIZE) & offset;
if (w_offset + n_bits <= WSIZE)
{
shift = (WSIZE - n_bits) - w_offset;
buf[word] = (buf[word] & ~(LowOrder(n_bits) << shift)) |
(value << shift);
}
else
{
size1 = WSIZE - w_offset;
size2 = n_bits - size1;
buf[word] = (buf[word] & HighOrder(size1)) |
((unsigned) value >> size2);
shift = WSIZE - size2;
buf[word + 1] = (buf[word + 1] & ~(LowOrder(size2) << shift)) |
(value << shift);
}
}
SHAR_EOF
fi
if test -f 'lz1.h'
then
echo shar: "will not over-write existing file 'lz1.h'"
else
cat << \SHAR_EOF > 'lz1.h'
/*
* header file for LZ compression routines
*/
#ifndef WORD
# ifdef OSK
# define WORD short
# define UWORD unsigned short
#else
# define WORD int
# define UWORD unsigned int
# endif
#endif
#define BITS 11 /* maximum number of bits/code */
#define INIT_BITS 9 /* initial number of bits/code */
/*
* One code can represent 1 << BITS characters, but to get a code of
* length N requires an input string of at least N * (N - 1) / 2
* characters. To overflow the decompress stack, an input file would
* have to have at least MAXSTACK * (MAXSTACK - 1) / 2 consecutive
* occurrences of a particular character, which is unlikely for the
* value used here. (Do keep the initial advice in mind, though.)
*/
#define MAXSTACK 2000 /* size of output stack */
#define TAG 2995 /* suggested by M. Meyer */
/*
* The following should be changed to fit your machine and the type
* you choose for the elements of the array buf.
* (If you avoid insert_bit() and fetch(), never mind.)
*/
#define LOG2WSIZE 4 /* log2(size of base type of buf) */
#define WSIZE 16 /* size of base type of buf */
#define BytesToBits(b) ((b) << 3)
#define LowOrder(n) (~(~0 << (n))) /* thanks to K & R */
#define HighOrder(n) (~0 << (n))
typedef struct {
unsigned WORD next, /* chain of entries with same prefix */
chain, /* chain prefixed with this entry */
prefix, /* prefix code for this entry */
suffix; /* last char in this entry */
} CodeTable;
extern WORD maxbits, /* user settable max # bits/code */
n_bits, /* initial number of bits/code */
maxmaxcode, /* max permissible maxcode value */
/* (i.e. 2 ** BITS - 1) */
maxcode, /* 2 ** n_bits - 1 */
free_ent, /* first unused entry */
offset, /* cursor into buf (units of bits) */
stats_flg; /* should we print statistics? */
extern WORD verbose,
debug;
extern long lz_bytes;
extern UWORD buf[BITS];
extern CodeTable Code[1 << BITS];
SHAR_EOF
fi
if test -f 'lz1glob.c'
then
echo shar: "will not over-write existing file 'lz1glob.c'"
else
cat << \SHAR_EOF > 'lz1glob.c'
/* compressglob -- globals for compress */
#include "lz1.h"
WORD maxbits,
n_bits,
maxmaxcode,
maxcode,
free_ent,
offset,
stats_flg,
verbose,
debug;
long lz_bytes;
UWORD buf[BITS];
CodeTable Code[1 << BITS];
SHAR_EOF
fi
if test -f 'lz1init.c'
then
echo shar: "will not over-write existing file 'lz1init.c'"
else
cat << \SHAR_EOF > 'lz1init.c'
/*
** routines to initialize the Lempel-Zev version one routines
*/
#include "lz1.h"
lz1_init()
{
maxbits = BITS;
n_bits = INIT_BITS;
maxcode = (1 << n_bits) - 1;
maxmaxcode = (1 << maxbits) - 1;
}
SHAR_EOF
fi
if test -f 'makefile.09'
then
echo shar: "will not over-write existing file 'makefile.09'"
else
cat << \SHAR_EOF > 'makefile.09'
/d0/cmds/ar: ar.r arsup.r dir.r lz1.r delz1.r lz1glob.r lz1init.r
=d;cc -m=8 -f=Ar -MS ar.r arsup.r dir.r lz1.r delz1.r lz1glob.r lz1init.r >map
lz1init.r: lz1init.c lz1.h
=cc -r lz1init.c
lz1glob.r: lz1glob.c lz1.h
=cc -r lz1glob.c
delz1.r: delz1.c lz1glob.c lz1.h
=cc -r delz1.c
lz1.r: lz1.c lz1glob.c lz1.h
=cc -r lz1.c
dir.r: dir.c
=cc -r dir.c
arsup.r: arsup.c ar.h
=cc -r arsup.c
ar.r: ar.c ar.h
=cc -r ar.c
SHAR_EOF
fi
if test -f 'makefile.68'
then
echo shar: "will not over-write existing file 'makefile.68'"
else
cat << \SHAR_EOF > 'makefile.68'
/d0/cmds/ar: ar.r arsup.r dir.r lz1.r delz1.r lz1glob.r lz1init.r
cc -m=8 -f=Ar ar.r arsup.r dir.r lz1.r delz1.r lz1glob.r lz1init.r
lz1.r delz1.r lz1init.r lz1glob.r: lz1.h
ar.r arsup.r dir.c: ar.h
SHAR_EOF
fi
exit 0
# End of shell archive
--
UUCP: (1) seismo!why_not!scsnet!sunder Mark E. Sunderlin
(2) ihnp4!chinet!megabyte aka Dr. Megabyte
CIS: 74026,3235 (202) 634-2529
Mail: IRS PM:PFR:D:NO 1111 Constitution Ave. NW Washington,DC 20224