/*==================================================================*/
/*																	*/
/*							  noDesk								*/
/*																	*/
/*						    Version 3.0								*/
/*																	*/
/* 						written 1989-1994 by						*/
/*		  Andreas Fahrig   Ralph Mertens    Christian Stamm			*/
/*                                                                  */
/*         (c)1989-93 by DesaSoft GbR, all Rights reserved			*/
/*         (c)1994-   by no|Soft GmbH, all Rights reserved			*/
/*                                                                  */
/*==================================================================*/
/*																	*/
/*				Archiv-Treiber fr LHARC-Dateien					*/
/*																	*/
/*==================================================================*/
#include <types.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <ext.h>
#include <tos.h>

#include "warc.h"


/*-------------------------------------------------------------------
	Konstanten und Makros
--------------------------------------------------------------------*/
/*
#define DR_DEBUG
*/

#ifdef DR_DEBUG
# define DebugAlert(SS) {printf("\033H\n\n%s\n", SS); Cnecin(); }
#else
# define DebugAlert(SS)
#endif


/*-------------------------------------------------------------------
	Typen und Strukturen
--------------------------------------------------------------------*/
union stamp {						/* time stamp */
	struct ftime	s;
	unsigned long	u;
};

struct Lzh_Head {					/* file header */
	unsigned char	head_chk;
	unsigned char	head_id[5];
	unsigned long	pac_siz;
	unsigned long	org_siz;
	union stamp		file_time;
	unsigned word	file_attr;
	unsigned char	file_name[MAXPATHLEN];
} Hdr1;


/*-------------------------------------------------------------------
	Variablen
--------------------------------------------------------------------*/
char		arcProg[MAXPATHLEN],
			tmpFile[MAXPATHLEN];

Warc_File	*fileBuf;

int			maxFiles,
			fileAnz;
char		*envBuff;

uchar 		HeadSiz;
uchar		buf2[2048];
char		filename[MAXPATHLEN];


/*-------------------------------------------------------------------
	Funktionen
--------------------------------------------------------------------*/
/*
 * calculate check-sum of header
 */
uchar _CalcCRC(struct Lzh_Head *h, uchar headsize) {
	uchar *p, *q;
	uchar i;

	p = (uchar *)h + 1;
	q = p + headsize;
	for (i = 0; p < q; p++)
		i += *p;
	return i;
} /* _CalcCRC */


/*
 * open an old archive
 */
FILE *_OpenArc1(char *fname) {
	uchar	*p, *q;
	FILE	*fP;

	if ((fP = fopen(fname, "rb")) == NULL)
		return NULL;
		
	q = buf2 - 5 + fread(buf2, 1, 2048, fP);
	for (p = buf2; p < q; p++) {
		if (p[0] == '-' && p[1] == 'l' && p[4] == '-') break;
	}
	if (p >= q) {
		return NULL;
	}
	fseek(fP, (long)(p - buf2 - 2), SEEK_SET);
	return(fP);
} /* _OpenArc1 */


void _Swap(void *vP, int size) {
	int		i;
	char	buf[4];
	
	for(i = 0; i < size; i++) buf[size - i - 1] = *((char *)vP + i);
	if (size == 4) *(ulong *)vP = *(ulong *)buf;
	if (size == 2) *(uword *)vP = *(uword *)buf;
} /* _Swap */


/*
 * get a file header
 */
uchar *_GetHdr(FILE *fP, struct Lzh_Head *h) {
	uchar 	*q, i;
	
	if (((HeadSiz = getc(fP)) <= 0) ||
	     (HeadSiz > sizeof(struct Lzh_Head) - 3) ||
	     (fread(h, HeadSiz + 1, 1, fP) == 0))
	{								/* read file header */
		return NULL;
	}

	if (_CalcCRC(h, HeadSiz) != h->head_chk)	/* if sum is wrong */
		return NULL;

	_Swap(&h->org_siz, 4);
	_Swap(&h->pac_siz, 4);
	_Swap((int *)&h->file_time.u, 2);
	_Swap((int *)&h->file_time.u + 1, 2);
	_Swap(&h->file_attr, 2);
	i = h->file_name[0];
	strncpy(filename, (char *)&h->file_name[1], i);
	*(filename + i) = '\0';
	if ((q = (uchar *)strrchr(filename, '\\')) == NULL &&
	    (q = (uchar *)strchr(filename, ':')) == NULL)
		q = (uchar *)filename;
	else
		q++;
	return q;					/* return the portion of file name */
} /* _GetHdr */


/*
 * _Ratio * 1000
 */
static uword _Ratio(ulong a, ulong b) {
	int i;

	if (!b) return 0;			/* if diviser == 0 */
	for (i = 0; i < 3 && a < 0x19999999UL; i++) {
		a *= 10;				/* while not overflow */
	}							/* upto 1000 times */
	for (; i < 3; i++) {		/* the case of overflow */
		b /= 10;
	}
	a += b / 2;					/* for round up */
	return ((uword)(a / b));	/* return (a * 1000 / b) */
} /* _Ratio */


static void _FillLE(Warc_File *wfP) {
	wfP->orig_len	= Hdr1.org_siz;
	wfP->packed_len = Hdr1.pac_siz;
/* AFA 7.12.94: ----------------------
	wfP->t_d.td.jar = (Hdr1.file_time.s.ft_year + 80) % 100;
------------------------------------*/
	wfP->t_d.td.jar = Hdr1.file_time.s.ft_year;
	wfP->t_d.td.mon = Hdr1.file_time.s.ft_month;
	wfP->t_d.td.tag = Hdr1.file_time.s.ft_day;
	wfP->t_d.td.std = Hdr1.file_time.s.ft_hour;
	wfP->t_d.td.min = Hdr1.file_time.s.ft_min;
	wfP->t_d.td.sek = Hdr1.file_time.s.ft_tsec * 2;
	wfP->attrib		= Hdr1.file_attr;
} /* _FillLE */


static Warc_File *_AddLEDir(uchar level, int ref_no, char *dirname) {
	static Warc_File *_AddLE(uchar, int, char *, Warc_File *);
	Warc_File		wf;

	wf.orig_len		= 0L;
	wf.packed_len	= 0L;
	wf.t_d.td.jar 	= 0;
	wf.t_d.td.mon 	= 1;
	wf.t_d.td.tag 	= 1;
	wf.t_d.td.std	= 0;
	wf.t_d.td.min	= 0;
	wf.t_d.td.sek	= 0;
	wf.attrib		= FA_SUBDIR;

	return(_AddLE(level, ref_no, dirname, &wf));
} /* _AddLEDir */


static Warc_File *_AddLE(uchar level, int ref_no, char *fname, Warc_File *newwfP) {
	char		*cP;
	Warc_File	*wfP;
	int			i;

	if (fileAnz >= maxFiles) {
		DebugAlert("too much");
		return(NULL);
	}

	cP = strchr(fname, '\\');
	if (cP) {
		*cP = '\0';

		for (i=0, wfP = fileBuf; i<fileAnz; i++, wfP++) {
			if (!(wfP->attrib & FA_SUBDIR)) continue;
			if (wfP->level!=level || wfP->ref_no!=ref_no) continue;
			if (!strcmp(wfP->fname, fname)) break;
		}
		if (i >= fileAnz) wfP = _AddLEDir(level, ref_no, fname);
		*cP = '\\';
		if (!wfP) return(NULL);
		fname = cP+1;
		return (_AddLE(level+1, wfP->physcnt, fname, newwfP));
	}

	if (newwfP->attrib & FA_SUBDIR) {
		for (i=0, wfP = fileBuf; i<fileAnz; i++, wfP++) {
			if (!(wfP->attrib & FA_SUBDIR)) continue;
			if (wfP->level!=level || wfP->ref_no!=ref_no) continue;
			if (!strcmp(wfP->fname, fname)) return(wfP);
		}
	}

	wfP = &fileBuf[fileAnz++];
	wfP->physcnt		= fileAnz;
	wfP->ref_no			= ref_no;
	wfP->level			= level;
	wfP->attrib			= newwfP->attrib;
	wfP->t_d.ul			= newwfP->t_d.ul;
	wfP->orig_len		= newwfP->orig_len;
	wfP->packed_len		= newwfP->packed_len;
	strncpy(wfP->fname, fname, 13);
	wfP->fname[13]		= '\0';

	while (level > 0) {
		if (fileBuf[ref_no].t_d.ul >= wfP->t_d.ul) break;
		fileBuf[ref_no].t_d.ul = wfP->t_d.ul;
		wfP = &fileBuf[ref_no];
		level--;
	}
	return(wfP);
} /* _AddLE */


int DoConvertArc(Warc_Info *aiP) {
	FILE			*fP;
	Warc_File		wf;
	char			*cP;
	int				err = DE_DRERROR;
	
	if ((fP = _OpenArc1(aiP->arc_name)) == NULL) { 	/* open archive */
		err = DE_BADFILE;
		goto arc_read_err;
	}

	maxFiles	= aiP->buf_size;
	fileBuf		= aiP->buf;
	fileAnz		= 0;

	while (_GetHdr(fP, &Hdr1) != NULL) {
		filename[MAXPATHLEN-1] = '\0';
		if (!filename[0]) continue;
		for (cP = filename; *cP; cP++);
		cP--;
		if (*cP == '\\') *cP = '\0';
		if (!filename[0]) continue;
		if (filename[1] == ':') {
			sprintf((char *)aiP->buf,
								"Das Archiv enth„lt absolute\n"
								"Pfade incl. Laufwerksangaben!");
			err = DE_TXTERROR;
			goto arc_read_err;
		}

		_FillLE(&wf);
		if (!_AddLE(0, 0, filename, &wf)) break;

		if (fseek(fP, Hdr1.pac_siz, 1) || feof(fP))
			break;
	}

	fclose(fP);
	aiP->buf_size = fileAnz;
	return(DE_OK);

arc_read_err:
	if (fP) fclose(fP);
	if (err == DE_DRERROR) {
		if (fileAnz >= maxFiles) err = DE_TOOMUCH;
		else {
			sprintf((char *)aiP->buf,
								"Fehler beim Lesen von\n%s",aiP->arc_name);
			err = DE_TXTERROR;
		}
	}
	fileAnz = 0;
	return (err);
} /* DoConvertArc */


int ReadArchive(Warc_Info *aiP) {
	int			ret;

	if (Fattrib(aiP->arc_name, 0, 0) < 0) {
		DebugAlert("Archiv");
		strcpy((char *)aiP->buf, "Das Archiv ist vom\n"
								"Treiber nicht zu finden!");
		return(DE_TXTERROR);
	}

	if ((ret = DoConvertArc(aiP)) != DE_OK)
		goto bad_work;

	return(ret);
bad_work:
	strcpy((char *)aiP->buf, "Die Ausfhrung des Archiv-\n"
							"Programms erfolgte fehlerhaft!");
	return(DE_TXTERROR);
} /* ReadArchive */


static Warc_Info *_ScanArg(char *arg) {
	int					i = 0;
	long				l = 0L;
	Warc_Info			*aiP;

	while ((i < 8) && isxdigit(*arg)) {
		if ((*arg >= '0')&&(*arg <= '9')) l = (l << 4) + (*arg - '0');
		else if ((*arg >= 'A')&&(*arg <= 'F')) l = (l << 4) + (*arg - 'A'+10);
		else 							l = (l << 4) + (*arg - 'a'+10);
		arg++;
		i++;
	}

	if ((i != 8) || (*arg != 0)) return(NULL);

	aiP = (Warc_Info *)l;

	if (strncmp(aiP->magic, WARC_MAGIC, 8)) return(NULL);

	return(aiP);
} /* _ScanArg */


int main(int ac, char **av, char **env) {
	Warc_Info		*aiP;

	envBuff = *env;
	if (ac != 2) return(DE_DRERROR);

	if ((aiP = _ScanArg(av[1])) == NULL) {
		DebugAlert("Arguments");
		return(DE_DRERROR);
	}
	return(ReadArchive(aiP));
} /* main */
