/*
 *	arcdmp.c
 *
 *	V 1.1
 *	Original August 1987, Jim Kyle
 *	Modified January 1988, S. Sampson
 *
 *	This program splits big ARC files into floppy-sized chunks
 *
 *	Define ECO for ECO-C88 Compiler, Default is MSC V4.0
 */

/* Includes */

#include <stdio.h>
#include <string.h>
#ifdef ECO
#include <ctype.h>
#include <malloc.h>
#else
#include <dos.h>
#include <search.h>
#include <stdlib.h>
#endif

/* Defines */

#define WORKSPACE		16384
#ifndef ECO
#define	tempname		argv[1]
#endif

/* Globals */

struct {
	char	key;		/* always 26		*/
	char	type;		/* 0 for EOF		*/
	char	name[13];	/* filename.ext+NULL	*/
	long	asiz;		/* size of ARC member	*/
	int	date;		/* date in FCB format	*/
	int	time;		/* time in FCB format	*/
	int	CRC;		/* CRC value for entry	*/
	long	osiz;		/* original file size	*/
} header;			/* ARC element header	*/

struct	list  {
	long	size;		/* index to ARC file    */
	long	loc;
} f1[500], f2[500];

FILE	*infl,
	*otfl;

long	avl;

int	f1c,
	f2c,
	outctr = 0;

char	outbas[16],
	outnam[16],
	*outbfr;

/* Prototypes, Forward declarations */

void	doarc(void);
void	bldtbl(void);
void	post(long);
void	dump(struct list *, int);
int	cmpsiz(struct list *, struct list *);
int	cmploc(struct list *, struct list *);
long	fill(void);
void	copy(struct list *, struct list *);
void	pack(struct list *);


/* Program */

void main(argc, argv)
int	argc;
char	*argv[];
{
	register char	*p;
#ifdef ECO
	char	tempname[64];
#endif

	/* provide help */

	if (argc < 2)  {
		fprintf(stderr, "Usage: arcdmp archive[.ARC] prefix\n");
		exit(1);
	}

	/* convert the archive filename to uppercase */

	for (p = argv[1]; *p; p++)
		*p = (char)toupper(*p);

#ifdef ECO
	/* parse the many possibilities */

	if ((p = strrchr(argv[1], '.')) == (char *)NULL)
		goto add;
	else if (strcmp(p, ".ARC") == NULL)
		strcpy(tempname, argv[1]);
	else if (*(p+1) == '/' || *(p+1) == '\\')  {
add:		strcpy(tempname, argv[1]);
		strcat(tempname, ".ARC");
	} else  {
		fprintf(stderr, "File '%s' not an archive\n", argv[1]);
		exit(1);
	}
#endif

	/* open up the archive */

	if ((infl = fopen(tempname, "rb")) == (FILE *)NULL)  {
		fprintf(stderr, "Can't open '%s' archive\n", tempname);
		exit(1);
	}

	/* build the new filename with a given prefix or TMP */

	if (argc < 3)
		strcpy (outbas, "TMP");
	else if (strchr(argv[2], ':') == NULL)
		sprintf(outbas, "%.5s", argv[2]);
	else
		sprintf(outbas, "%.7s", argv[2]); /* 5 char plus drive spec */

	/* reserve some memory space */

	if ((outbfr = calloc(WORKSPACE, sizeof(char))) == NULL)  {
		fprintf(stderr, "Not enough memory\n");
		exit(1);
	}

	doarc();

	/* were done, so cleanup and leave */

	free(outbfr);
	fclose(infl);
	exit(0);
}


void doarc()
{
	int	cmploc(), cmpsiz();

	bldtbl();
	qsort((char *)&f1[0], (unsigned)f1c, sizeof f1[0], cmpsiz);

	while ( f1c )  {
		printf("  Floppy = %lu bytes (%s):\n", fill(), outnam);
		qsort((char *)&f2[0], (unsigned)f2c, sizeof f2[0], cmploc);
		dump(f2, f2c);
	}
}


void bldtbl()
{
	long	curloc;

	curloc = 0L;
	while (fread((char *)&header, 1, sizeof header, infl) > 2)  {
		if (header.type)
			post(curloc);

		if (fseek(infl, header.asiz, 1))  {
			fprintf(stderr, "Seek error\n");
			exit(1);
		}

		curloc = ftell(infl);
	}
}


/* post size and location in table */

void post(posn)
long	posn;
{
	f1[f1c].size  = header.asiz + sizeof header;
	f1[f1c++].loc = posn;
	f1[f1c].loc   = posn + header.asiz + sizeof header;
	f1[f1c].size  = 0L;
}


void dump(f, n)
struct	list *f;
int	n;
{
	if ((otfl = fopen(outnam, "wb")) == (FILE *)NULL)  {
		fprintf(stderr, "Can't produce '%s'\n", outnam);
		exit(1);
	}

	while (n--)  {
		if (fseek(infl, f->loc, 0))  {  /* position to member */
			fprintf(stderr, "Positioning error\n");
			exit(1);
		}

		fread((char *)&header, 1, sizeof header, infl);
		printf("%-14s%9lu%9lu\n", header.name, f->size, f->loc);
		fwrite((char *)&header, 1, sizeof header, otfl);

		while (header.asiz > 0L)  {
			int x;

			x = header.asiz > (long)WORKSPACE ? WORKSPACE : (int)header.asiz;
			x = fread( outbfr, 1, x, infl);     /* read a chunk */

			if (x != fwrite( outbfr, 1, x, otfl))  {
				fprintf(stderr, "Output write error\n");
				exit(1);
			}

			header.asiz -= (long)x;
		}

		f++;
	}

	printf("%-14s%9lu%9lu\n", "  Final-->", f->size, f->loc);
	fputc(26, otfl);
	fputc(0, otfl);

	fclose(otfl);
}


int cmpsiz(a1, a2)
struct list *a1, *a2; /* sort biggest ones first */
{
	return(a1->size > a2->size ? -1 : a1->size < a2->size);
}


int cmploc(a1, a2)
struct list *a1, *a2; /* restore to alpha sequence */
{
	return(a1->loc < a2->loc ? -1 : a1->loc > a2->loc);
}


long fill()                             /* fill up a small ARC table */
{
	int	c;
	long	tmp;

	sprintf(outnam, "%s%03d.ARC", outbas, ++outctr);

	avl = 1024L * 354L - 3L;	/* 360K floppy size */
	f2c = c = 0;
	copy(&f1[c], &f2[f2c++]);	/* always take first one */
	tmp = f1[c].size;
	f2[f2c].size  = 0L;
	pack(&f1[c]);			/* and remove from list */

	while ( c < f1c )  {
		while (( f1[c].size < avl ) && f1[c].size )  {
			copy(&f1[c], &f2[f2c++]);
			tmp += f1[c].size;
			f2[f2c].size  = 0L;
			pack(&f1[c]);
		}

		++c;
	}

	return tmp;
}


void copy(a1, a2)
struct list *a1, *a2;
{
	a2->size = a1->size;
	a2->loc  = a1->loc;
}


void pack(a1)
struct list *a1;
{
	avl -= a1->size;
	while (a1->size)  {
		copy (a1+1, a1);
		++a1;
	}

	--f1c;
}

/* EOF */
