DataMuseum.dk

Presents historical artifacts from the history of:

Commodore CBM-900

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about Commodore CBM-900

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - download

⟦eb2033105⟧ TextFile

    Length: 17663 (0x44ff)
    Types: TextFile
    Notes: UNIX file
    Names: »ar.c«

Derivation

└─⟦f27320a65⟧ Bits:30001972 Commodore 900 hard disk image with partial source code
    └─⟦f4b8d8c84⟧ UNIX V7 Filesystem
        └─ ⟦this⟧ »cmd/ar.c« 

TextFile

/*
 * 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