#include "bbscdef.h"

struct heads {				/* archive entry header format */
	char		name[13];	/* file name */
	int		size;		/* size of file, in bytes */
	unsigned short	date;		/* creation date */
	unsigned short	time;		/* creation time */
	unsigned short		crc;		/* cyclic redundancy check */
	int		length;		/* true file length */
};

#define ARCMARK		26	/* special archive marker */
#define ARCVER		9	/* archive header version code */
#define ARCFNLEN	13	/* file name length */

char	hdrver;			/* header version */
FILE	*arc;			/* the old archive */
FILE	*inps;

listarc(filename, port_id)
char	*filename, *port_id;
{
	struct heads	hdr;			/* header data */
	long		tnum, tlen, tsize;	/* totals */
	strcpy(buf128, "/tmp/arclst.");
	strcat(buf128, port_id);
	inps = fopen(buf128, "w");

	strcpy(buf128, filename);

	fprintf(inps, "\nArchive:  %s\n\n", buf128);

	tnum = tlen = tsize = 0;	/* reset totals */

	fprintf(inps, "Name          Length    Stowage    SF   Size now  ");
	fprintf(inps, "Date       Time    CRC\n");
	fprintf(inps, "============  ========  ========  ====  ========  ");
	fprintf(inps, "=========  ======  ====\n");
	
	if (!(arc = fopen(buf128, "rb"))) {	/* open archive for reading */
		fprintf(inps, "Cannot read archive: %s\n", buf128);
		return;
	}

	while (readhdr(&hdr, arc)) {	/* else report on all files */
		lstfile(&hdr);
		tnum++; 		/* update totals */
		tlen += hdr.length;
		tsize += hdr.size;
		fseek(arc, hdr.size, 1);/* skip to next header */
	}

	fclose(arc);			/* close archive after reading */

	fprintf(inps, "        ====  ========            ====  ========\n");
	fprintf(inps, "Total %6ld  %8ld            %3ld%%  %8ld  \n\n",
	   tnum, tlen, tlen ? 100L - (100L * tsize) / tlen : 0, tsize);
	fclose(inps);
}

static lstfile(hdr)		       /* tell about a file */
struct heads *hdr;		       /* pointer to header data */
{
	int	yr, mo, dy;		       /* parts of a date */
	int	hh, mm, ss;		       /* parts of a time */

	static char	*mon[] = {	       /* month abbreviations */
		"Jan", "Feb", "Mar", "Apr",
		"May", "Jun", "Jul", "Aug",
		"Sep", "Oct", "Nov", "Dec"
	};

	yr = (hdr -> date >> 9) & 0x7f;		/* dissect the date */
	mo = (hdr -> date >> 5) & 0x0f;
	dy = hdr -> date & 0x1f;

	hh = (hdr -> time >> 11) & 0x1f;	/* dissect the time */
	mm = (hdr -> time >> 5) & 0x3f;
	ss = (hdr -> time & 0x1f) * 2;

	fprintf(inps, "%-12s  %8ld  ", hdr -> name, hdr -> length);

	switch (hdrver) {
	case 1:
	case 2:
		fprintf(inps, "   --   ");
		break;
	case 3:
		fprintf(inps, " Packed ");
		break;
	case 4:
		fprintf(inps, "Squeezed");
		break;
	case 5:
	case 6:
	case 7:
		fprintf(inps, "crunched");
		break;
	case 8:
		fprintf(inps, "Crunched");
		break;
	case 9: fprintf(inps, "Squashed");
		break;
	default:
		fprintf(inps, "Unknown!");
	}

	fprintf(inps, "  %3ld%%  %8ld  %2d %3s %02d  %2d:%02d%c  %04X\n",
	    100L - (100L * hdr -> size) / hdr -> length,
	    hdr -> size,
	    dy, mon[mo-1], (yr + 80) % 100,
	    (hh > 12 ? hh - 12 : hh), mm, (hh > 12 ? 'p' : 'a'), hdr -> crc);
}

int readhdr(hdr, f)			/* read a header from an archive */
struct heads	*hdr;			/* storage for header */
FILE		*f;			/* archive to read header from */
{
	char		name[ARCFNLEN];	/* filename buffer */
	int		try = 0;	/* retry counter */
	static int	first = 1;	/* true only on first read */

	if (!f)				/* if archive didn't open */
		return 0;		/* then pretend it's the end */
	if (feof(f))			/* if no more data */
		return 0;		/* then signal end of archive */

	if (fgetc(f) != ARCMARK) {	/* check archive validity */
		fprintf(inps, "An entry in %s has a bad header.\n", buf128);

		while(!feof(f)) {
			try++;
			if (fgetc(f) == ARCMARK) {
				ungetc(hdrver = fgetc(f), f);
				if (hdrver >= 0 && hdrver <= ARCVER)
					break;
			}
		}

		if (feof(f) && first) {
			fprintf(inps,"%s is not an archive.\n", buf128);
			return 0;
		}

		fprintf(inps, "  %d bytes skipped.\n", try);

		if (feof(f))
			return 0;
	}

	hdrver = fgetc(f);		/* get header version */
	if (hdrver < 0) {
		fprintf(inps, "Invalid header in archive %s\n", buf128);
		return 0;
	}
	if (hdrver == 0)
		return 0;		/* note our end of archive marker */
	if (hdrver > ARCVER) {
		fread(name, sizeof(char), ARCFNLEN, f);
		fprintf(inps, "I don't know how to handle file %s in archive %s\n",
		   name, buf128);
		fprintf(inps, "I think you need a newer version of ARC.\n");
	        return 0;
	}

	/* amount to read depends on header type */

	if (hdrver == 1) {			/* old style is shorter */
		fread(hdr, sizeof (struct heads) - sizeof (long int), 1, f);
		hdrver = 2;           		/* convert header to new format */
		hdr -> length = hdr -> size;	/* size is same when not packed */
	} else
		fread(hdr, sizeof (struct heads), 1, f);
	first = 0;
	return 1;				/* we read something */
}
