|
|
DataMuseum.dkPresents historical artifacts from the history of: Commodore CBM-900 |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about Commodore CBM-900 Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - download
Length: 17663 (0x44ff)
Types: TextFile
Notes: UNIX file
Names: »ar.c«
└─⟦f27320a65⟧ Bits:30001972 Commodore 900 hard disk image with partial source code
└─⟦f4b8d8c84⟧ UNIX Filesystem
└─⟦this⟧ »cmd/ar.c«
/*
* Archive manager.
* Also manages libraries for the
* link editor.
*/
#define RUNRSX 0
#define FMTRSX 0 /* Achives in COHERENT format */
#include <stdio.h>
#include <ar.h>
#include <canon.h>
#if !RUNRSX
#include <stat.h>
#endif
#define RO 0
#define RW 1
#define NONE 0
#define BEFORE 1
#define AFTER 2
#define ALLOK 0
#define NOTALL 1
#define ERROR 2
#define aechk() { if(ferror(afp)) ioerr(anp); }
#define techk() { if(ferror(tfp)) ioerr(tnp); }
char nwork[] = "No work";
char found[] = "found";
char copen[] = "%s: cannot open";
char ccrea[] = "%s: cannot create";
char creop[] = "%s: cannot reopen";
FILE *afp;
FILE *tfp;
struct ar_hdr ahb;
int nname;
char **namep;
char *ctime();
int usage();
long fsize();
long ftell();
int cflag;
int lflag;
int uflag;
int vflag;
char *pnp;
char *anp;
char tnp[32];
int xstat = ALLOK;
int pos = NONE;
int (*ffp)() = usage;
#if RUNRSX
struct header fhb;
time_t fmdate();
time_t getmdate();
#else
char state[] = "%s: stat error";
#endif
main(argc, argv)
char *argv[];
{
register char *p;
register i;
int usage(), rfunc();
if (argc < 2)
usage();
key(argv[1]);
nname = argc-2;
namep = &argv[2];
for (i=2; i<argc; ++i) {
p = argv[i];
if (pos!=NONE && pnp==NULL) {
pnp = p;
++namep;
--nname;
} else if (anp == NULL) {
anp = p;
++namep;
--nname;
}
}
if (anp == NULL)
usage();
if (pos != NONE) {
if (pnp == NULL)
usage();
if (ffp == usage)
ffp = rfunc;
}
(*ffp)();
delexit(xstat);
}
/*
* Decode key.
* Save function name in `ffp'.
* Set `c', `l' and `v' flags.
*/
key(p)
register char *p;
{
register c;
int dfunc(), rfunc(), qfunc(), tfunc();
int mfunc(), xfunc(), pfunc();
while (c = *p++) {
switch (c) {
case 'd':
ffp = dfunc;
break;
case 'r':
ffp = rfunc;
break;
case 'q':
ffp = qfunc;
break;
case 't':
ffp = tfunc;
break;
case 'p':
ffp = pfunc;
break;
case 'm':
ffp = mfunc;
break;
case 'x':
ffp = xfunc;
break;
case 'c':
++cflag;
break;
case 'l':
++lflag;
break;
case 'u':
++uflag;
break;
case 'v':
++vflag;
break;
case 'a':
pos = AFTER;
break;
case 'b':
case 'i':
pos = BEFORE;
break;
default:
usage();
}
}
}
/*
* Replace.
* Eliminate dead stuff if `u'.
* Copy up to insert point.
* Copy in new files.
* Copy the rest of the file.
* Copy back.
*/
rfunc()
{
register char *qfn;
register i, nef;
FILE *qfp;
if (nname == 0)
diag(1, nwork);
aopen(RW);
if (uflag) {
update();
fseek(afp, (long)sizeof(int), 0);
}
topen();
while (nef = geth()) {
if (pos==BEFORE && eqh(pnp)) {
fseek(afp, (long)-sizeof(struct ar_hdr), 1);
break;
}
if (pos == NONE) {
for (i=0; i<nname; ++i) {
if ((qfn=namep[i])!=NULL && eqh(qfn))
break;
}
if (i != nname) {
fseek(afp, ahb.ar_size, 1);
namep[i] = NULL;
remove(i, qfn);
if ((qfp = fopen(qfn, "r")) == NULL)
diag(1, copen, qfn);
makeh(qfn, qfp);
if (vflag)
printf("%s: in place replace.\n", qfn);
puth();
ffcopy(tfp, tnp, qfp, qfn, fsize(qfp, qfn));
fclose(qfp);
continue;
}
}
mmove(0);
if (pos==AFTER && eqh(pnp))
break;
}
if (nef==0 && pos!=NONE)
diag(1, "%s: not in archive", pnp);
for (i=0; i<nname; ++i) {
if ((qfn=namep[i]) == NULL)
continue;
remove(i, qfn);
if ((qfp=fopen(qfn, "r")) == NULL)
diag(1, copen, qfn);
makeh(qfn, qfp);
if (vflag)
printf("%s: replaced.\n", qfn);
puth();
ffcopy(tfp, tnp, qfp, qfn, fsize(qfp, qfn));
fclose(qfp);
}
while (geth())
mmove(0);
tacopy();
}
/*
* Handle the `u' option.
* Read through the archive, comparing
* the dates in the headers to the last
* modification dates of the files in
* the command line. Make some files
* go away.
*/
update()
{
register char *p;
register i;
#if !RUNRSX
struct stat sb;
#endif
while (geth()) {
for (i=0; i<nname; ++i) {
p = namep[i];
if (p!=NULL && eqh(p)) {
#if RUNRSX
if (ahb.ar_date >= fmdate(p)) {
#else
if (stat(p, &sb) < 0)
diag(1, state, p);
if (ahb.ar_date >= sb.st_mtime) {
#endif
if (vflag)
printf("%s: no update.\n", p);
namep[i] = NULL;
remove(i, p);
}
}
}
fseek(afp, ahb.ar_size, 1);
}
for (i=0; i<nname && namep[i]==NULL; ++i)
;
if (i >= nname)
diag(1, nwork);
}
/*
* Move.
* Copy stuff before insert.
* Copy moved stuff.
* Copy remainder.
* Copy back to archive.
*/
mfunc()
{
register nef;
long s;
aopen(RW);
topen();
while (nef = geth()) {
if (pos==BEFORE && eqh(pnp))
break;
mmove(0);
if (pos==AFTER && eqh(pnp))
break;
}
if (nef==0 && pos!=NONE)
diag(1, "%s: not in archive", pnp);
s = ftell(afp);
if (pos == BEFORE)
s -= sizeof(struct ar_hdr);
fseek(afp, (long)sizeof(int), 0);
while (geth())
mmove(1);
if (nef) {
fseek(afp, s, 0);
while (geth())
mmove(0);
}
tacopy();
}
/*
* Conditional move to the
* temp file from the archive
* file. Used by move and replace
*/
mmove(f1)
register f1;
{
register f2, i;
register long size;
f2 = 0;
for (i=0; i<nname; ++i) {
if (eqh(namep[i])) {
++f2;
break;
}
}
if (f1 == f2) {
if (vflag)
amsg("copied");
size = ahb.ar_size;
puth();
ffcopy(tfp, tnp, afp, anp, size);
} else {
if (vflag)
amsg("skipped");
fseek(afp, ahb.ar_size, 1);
}
}
/*
* Print.
*/
pfunc()
{
aopen(RO);
while (geth()) {
if (nname==0 || match())
pfile();
else
fseek(afp, ahb.ar_size, 1);
}
if (nname != 0)
notdone(found);
}
pfile()
{
register char *rb;
register i;
long s;
int n;
if (vflag)
amsg(NULL);
#if FMTRSX
if ((rb = malloc(ahb.ar_ufat.f_rsiz)) == NULL)
diag(1, "Out of space");
s = ahb.ar_size;
while (s != 0) {
if (ahb.ar_ufat.f_rtyp == R_FIX)
n = ahb.ar_ufat.f_rsiz;
else {
fread(&n, sizeof(int), 1, afp);
s -= sizeof(int);
}
fread(rb, sizeof(char), n, afp);
for (i=0; i<n; ++i)
putchar(rb[i]);
if ((ahb.ar_ufat.f_ratt&FD_CR) != 0)
putchar('\n');
if ((n&01) != 0) {
++n;
fseek(afp, (long)1, 1);
}
s -= n;
}
free(rb);
#else
ffcopy(stdout, "Stdout", afp, anp, ahb.ar_size);
#endif
}
/*
* Delete.
* Copy archive to temp, deleting
* members along the way. If all of
* the files have been deleted then
* copy back.
*/
dfunc()
{
register long size;
if (nname == 0)
diag(1, nwork);
aopen(RW);
topen();
while (geth()) {
if (match()) {
if (vflag)
amsg("deleted");
fseek(afp, ahb.ar_size, 1);
continue;
}
if (vflag)
amsg("copied");
size = ahb.ar_size;
puth();
ffcopy(tfp, tnp, afp, anp, size);
}
if (notdone("deleted"))
delexit(ERROR);
tacopy();
}
/*
* Quick insert.
* Copy archive to temp file.
* Tack new files onto the end.
* If no errors, copy back.
*/
qfunc()
{
register char *qfn;
register FILE *qfp;
register i;
if (nname == 0)
diag(1, nwork);
aopen(RW);
fseek(afp, (long)0, 2);
for (i=0; i<nname; ++i) {
if ((qfn=namep[i]) == NULL)
continue;
remove(i, qfn);
if ((qfp=fopen(qfn, "r")) == NULL)
diag(1, copen, qfn);
makeh(qfn, qfp);
if (vflag)
printf("%s: quick insert.\n", qfn);
cantime(ahb.ar_date);
canshort(ahb.ar_gid);
canshort(ahb.ar_uid);
canshort(ahb.ar_mode);
cansize(ahb.ar_size);
fwrite(&ahb, sizeof(ahb), 1, afp);
aechk();
ffcopy(afp, anp, qfp, qfn, fsize(qfp, qfn));
fclose(qfp);
}
}
/*
* Table.
* Read through archive.
* If good member print its name.
* If verbose, print extra stuff.
*/
tfunc()
{
register char *p;
register c, n;
aopen(RO);
while (geth()) {
if (nname==0 || match()) {
n = 0;
p = ahb.ar_name;
while (n<DIRSIZ && (c=*p++)) {
putchar(c);
++n;
}
if (vflag) {
while (n++ < DIRSIZ+1)
putchar(' ');
#if FMTRSX
printf("[%03o,%03o] ", ahb.ar_gid, ahb.ar_uid);
printf("%06o ", ahb.ar_mode);
#else
printf("%5d %5d ", ahb.ar_gid, ahb.ar_uid);
printf("%03o ", ahb.ar_mode);
#endif
printf("%10ld ", ahb.ar_size);
printf("%s", ctime(&ahb.ar_date));
} else
putchar('\n');
}
fseek(afp, ahb.ar_size, 1);
}
if (nname != 0)
notdone(found);
}
/*
* Extract.
* Read through archive.
* Extract any files you find.
* At end, mutter about files
* that were not there.
*/
xfunc()
{
register char *p1, *p2;
register c;
char fnb[DIRSIZ+1];
FILE *xfp;
aopen(RO);
while (geth()) {
if (nname==0 || match()) {
p1 = ahb.ar_name;
p2 = fnb;
while (p1<&ahb.ar_name[DIRSIZ] && (c=*p1++))
*p2++ = c;
*p2 = 0;
if ((xfp=fopen(fnb, "w")) == NULL) {
diag(0, ccrea, fnb);
fseek(afp, ahb.ar_size, 1);
continue;
}
if (vflag)
amsg("extracting");
ffcopy(xfp, fnb, afp, anp, ahb.ar_size);
#if RUNRSX && FMTRSX
fseek(xfp, (long)0, 0);
xfp->v_rtyp = ahb.ar_ufat.f_rtyp;
xfp->v_ratt = ahb.ar_ufat.f_ratt;
xfp->v_rsiz = ahb.ar_ufat.f_rsiz;
xfp->v_hibk = ahb.ar_ufat.f_hibk;
xfp->v_efbk = ahb.ar_ufat.f_efbk;
xfp->v_ffby = ahb.ar_ufat.f_ffby;
#endif
#if !RUNRSX && !FMTRSX
chmod(fnb, ahb.ar_mode);
#endif
fclose(xfp);
} else
fseek(afp, ahb.ar_size, 1);
}
if (nname != 0)
notdone(found);
}
/*
* Make an archive header.
* Put it in the external archive
* header buffer `ahb'. The arg.
* `fn' is the file name. The file
* is open on `fp'.
*/
makeh(fn, fp)
char *fn;
FILE *fp;
{
register char *p1, *p2;
register c;
#if !RUNRSX
struct stat sb;
#endif
for (p1=fn; *p1++; )
;
while (p1 > fn) {
c = p1[-1];
#if RUNRSX
if (c==':' || c==']')
break;
#else
if (c == '/')
break;
#endif
--p1;
}
p2 = ahb.ar_name;
while (c = *p1++) {
if (p2 < &ahb.ar_name[DIRSIZ])
*p2++ = c;
}
while (p2 < &ahb.ar_name[DIRSIZ])
*p2++ = 0;
#if RUNRSX
if (ratt(fp, &fhb) == 0)
diag(1, "%s: ratt failed", fn);
ahb.ar_date = getmdate(&fhb);
ahb.ar_size = fsize(fp, fn);
#if FMTRSX
ahb.ar_uid = fhb.h_prog;
ahb.ar_gid = fhb.h_proj;
ahb.ar_mode = fhb.h_fpro;
ahb.ar_ufat.f_rtyp = fp->v_rtyp;
ahb.ar_ufat.f_ratt = fp->v_ratt;
ahb.ar_ufat.f_rsiz = fp->v_rsiz;
ahb.ar_ufat.f_hibk = fp->v_hibk;
ahb.ar_ufat.f_efbk = fp->v_efbk;
ahb.ar_ufat.f_ffby = fp->v_ffby;
#else
ahb.ar_uid = 0;
ahb.ar_gid = 0;
ahb.ar_mode = 0644;
#endif
#else
if (fstat(fileno(fp), &sb) < 0)
diag(1, state, fn);
time(&ahb.ar_date);
ahb.ar_uid = sb.st_uid;
ahb.ar_gid = sb.st_gid;
ahb.ar_mode = sb.st_mode&0777;
ahb.ar_size = sb.st_size;
#endif
}
/*
* Test if the member whose
* header is held in the archive
* header buffer is mentioned in
* the user's list of members.
* Return the number of matches.
* All matches are NULLed.
*/
match()
{
register char *p;
register i, n;
n = 0;
for (i=0; i<nname; ++i) {
if ((p=namep[i]) == NULL)
continue;
if (eqh(p)) {
++n;
namep[i] = NULL;
}
}
return (n);
}
/*
* Remove all instances of name
* `fn' from the list of names that
* is described by `namep' and
* `nname'.
* Start at index `i+1'.
*/
remove(i, fn)
register i;
register char *fn;
{
register char *p;
for (++i; i<nname; ++i) {
if ((p=namep[i]) == NULL)
continue;
if (strcmp(fn, p) == 0)
namep[i] = NULL;
}
}
/*
* This routine digs through the
* list of names described by `namep'
* and `nname' looking for names that
* have not been NULLed out. If any
* are found it prints a title and
* the names. The number of names that
* were found is returned.
*/
notdone(s)
char *s;
{
register char *p;
register i, n;
n = 0;
for (i=0; i<nname; ++i) {
p = namep[i];
if (p != NULL) {
if (n++ == 0)
fprintf(stderr, "Not %s:\n", s);
fprintf(stderr, "%s\n", p);
}
}
return (n);
}
/*
* File to file copy.
*/
ffcopy(tfp, tfn, ffp, ffn, s)
FILE *tfp, *ffp;
char *tfn, *ffn;
long s;
{
register n;
static char fb[BUFSIZ];
while (s != 0) {
n = (s>BUFSIZ) ? BUFSIZ : s;
if (fread (fb, sizeof(char), n, ffp) != n)
ioerr(ffn);
if (fwrite(fb, sizeof(char), n, tfp) != n)
ioerr(tfn);
s -= n;
}
}
/*
* Get the next archive header
* into `ahb'. Check for any I/O
* errors. Return true if a header
* was read and false on EOF.
*/
geth()
{
if (fread(&ahb, sizeof(ahb), 1, afp) != 1) {
aechk();
return (0);
}
cantime(ahb.ar_date);
canshort(ahb.ar_gid);
canshort(ahb.ar_uid);
canshort(ahb.ar_mode);
cansize(ahb.ar_size);
return (1);
}
/*
* Write the header in `ahb' to
* the temp file.
*/
puth()
{
cantime(ahb.ar_date);
canshort(ahb.ar_gid);
canshort(ahb.ar_uid);
canshort(ahb.ar_mode);
cansize(ahb.ar_size);
fwrite(&ahb, sizeof(ahb), 1, tfp);
techk();
}
/*
* Compare a string to the name
* of the member in the archive header
* buffer. True return if same.
*/
eqh(p)
char *p;
{
register char *p1, *p2;
register c;
if ((p1 = p) == NULL)
return (0);
while (*p1++)
;
while (p1 > p) {
c = p1[-1];
#if RUNRSX
if (c==':' || c==']')
break;
#else
if (c == '/')
break;
#endif
--p1;
}
p2 = ahb.ar_name;
c = DIRSIZ;
while (c && *p1 == *p2++) {
if (*p1++ == 0)
return (1);
--c;
}
if (c == 0)
return (1);
return (0);
}
/*
* Open archive.
* The argument `aam' is the
* access mode (RO or RW).
*/
aopen(aam)
{
int i;
if ((afp=fopen(anp, "r")) == NULL) {
if (aam == RO)
diag(1, copen, anp);
if ((afp=fopen(anp, "w"))==NULL
|| (afp=freopen(anp, "w+r", afp))==NULL)
diag(1, ccrea, anp);
if (cflag == 0)
printf("%s: new archive.\n", anp);
i = ARMAG;
canint(i);
fwrite(&i, sizeof(i), 1, afp);
aechk();
return;
}
if (aam != RO) {
fclose(afp);
if ((afp=fopen(anp, "r+w"))==NULL)
diag(1, copen, anp);
}
fread(&i, sizeof(i), 1, afp);
aechk();
canint(i);
if (i != ARMAG)
diag(1, "%s: not an archive", anp);
}
/*
* Open tempfile.
* Stash the name in `tnp' for
* the benefit of `delexit'.
* Honour the `l' option w.r.t.
* file placement.
*/
topen()
{
register char *p1, *p2;
int i;
p1 = tnp;
#if RUNRSX
p2 = "ar.tmp";
while (*p1++ = *p2++)
;
#else
if (lflag == 0) {
p2 = "/tmp/";
while (*p1++ = *p2++)
;
--p1;
}
p2 = "vxxxxxx";
while (*p1++ = *p2++)
;
mktemp(tnp);
#endif
if ((tfp=fopen(tnp, "w")) == NULL)
diag(1, ccrea, tnp);
i = ARMAG;
canint(i);
fwrite(&i, sizeof(i), 1, tfp);
techk();
}
/*
* Copy tempfile back to the
* archive.
*/
tacopy()
{
register FILE *xtp;
fclose(tfp);
tfp = NULL; /* Scare off delexit */
fclose(afp);
if ((xtp=fopen(tnp, "r")) == NULL)
diag(1, creop, tnp);
if ((afp=fopen(anp, "w")) == NULL)
diag(1, creop, tnp);
if (vflag)
printf("%s: copy back.\n", anp);
ffcopy(afp, anp, xtp, tnp, fsize(xtp, tnp));
tfp = xtp; /* Delete */
}
/*
* Write diagnostic.
* The flag `f' marks fatal errors.
*/
diag(f, a)
{
fprintf(stderr, "%r", &a);
fprintf(stderr, ".\n");
if (f)
delexit(ERROR);
xstat = NOTALL;
}
/*
* Print a message for a
* given archive member. The header
* is in the header buffer.
*/
amsg(s)
char *s;
{
register char *p;
register c;
p = ahb.ar_name;
while (p<&ahb.ar_name[DIRSIZ] && (c=*p++)!=0)
putchar(c);
putchar(':');
if (s != NULL)
printf(" %s.", s);
putchar('\n');
}
/*
* Exit.
* Delete the tempfile if
* present.
*/
delexit(s)
{
#if RUNRSX
if (tfp != NULL)
fmkdl(tfp);
#else
if (tfp != NULL)
unlink(tnp);
#endif
#ifdef DEBUG
if (s)
abort();
#endif
exit(s);
}
/*
* Mutter about an I/O error
* on file `s'.
*/
ioerr(s)
char *s;
{
diag(1, "%s: I/O error", s);
}
/*
* Print usage message.
*/
usage()
{
fprintf(stderr, "Usage: ar options [posname] afile [name ...].\n");
delexit(ERROR);
}
/*
* Compute the size of a file.
* In bytes.
* The file must not be seeked.
*/
long
fsize(fp, fn)
register FILE *fp;
char *fn;
{
#if RUNRSX
return (((fp->v_efbk-1)<<9) + fp->v_ffby);
#else
struct stat sb;
if (fstat(fileno(fp), &sb) < 0)
diag(1, state, fn);
return (sb.st_size);
#endif
}
#if RUNRSX
/*
* The following routines simulate
* some aspects of Coherent under the
* dreaded RSX.
*/
#define DAY (24L*60L*60L)
time_t
fmdate(fn)
char *fn;
{
register FILE *fp;
register s;
if ((fp=fopen(fn, "r")) == NULL)
diag(1, copen, fn);
s = ratt(fp, &fhb);
fclose(fp);
if (s == 0)
diag(1, "%s: ratt failed", fn);
return (getmdate(&fhb));
}
time_t
getmdate(fhp)
register struct header *fhp;
{
register i;
time_t date;
int rsxdate[8];
static char *mtab[] = {
"JAN", "FEB", "MAR", "APR", "MAY", "JUN",
"JUL", "AUG", "SEP", "OCT", "NOV", "DEC"
};
rsxdate[0] = (fhp->i_rvdt[5]-'0')*10+fhp->i_rvdt[6]-'0';
for (i=0; i<12; ++i) {
if (cmp3(&fhp->i_rvdt[2], mtab[i])) {
rsxdate[1] = i+1;
break;
}
}
rsxdate[2] = (fhp->i_rvdt[0]-'0')*10+fhp->i_rvdt[1]-'0';
rsxdate[3] = (fhp->i_rvti[0]-'0')*10+fhp->i_rvti[1]-'0';
rsxdate[4] = (fhp->i_rvti[2]-'0')*10+fhp->i_rvti[3]-'0';
rsxdate[5] = (fhp->i_rvti[4]-'0')*10+fhp->i_rvti[5]-'0';
cvttime(&date, rsxdate);
return (date);
}
cmp3(p1, p2)
register char *p1, *p2;
{
if (*p1++==*p2++ && *p1++==*p2++ && *p1++==*p2++)
return (1);
return (0);
}
/*
* Convert rsx date to Coherent
* format. This routine assumes that
* the timezone is CST.
* Courtesy of Tom Duff.
*/
cvttime(date, rsxdate)
register long *date;
int rsxdate[8];
{
register i;
*date = 0;
for (i=70; i!=rsxdate[0]; i++) {
if (i%4==0)
*date += 366L*DAY; else
*date += 365L*DAY;
}
for (i=1; i!=rsxdate[1]; i++)
switch (i){
case 9: /* Sep */
case 4: /* Apr */
case 6: /* Jun */
case 11: /* Nov */
*date += 30L*DAY;
break;
case 2: /* Feb */
if (rsxdate[0]%4 == 0)
*date += 29L*DAY; else
*date += 28L*DAY;
break;
default:
*date += 31L*DAY;
}
*date += (rsxdate[2]-1)*DAY;
*date += rsxdate[3]*60L*60L;
*date += rsxdate[4]*60L;
*date += rsxdate[5];
*date += 6L*60L*60L; /* adjust cst to gmt */
}
#endif