/***********************************************************
	extract.c -- extract file from archive
***********************************************************/
#include <stdio.h>
#include <string.h>
#include <farstr.h>
#include <stdlib.h>
#include <io.h>
#include <dos.h>
#include <direct.h>
#include "lh.h"
#include "intrface.h"
#include "errmes.h"
#include "disp.h"

static char methods[10][5] = {
	"-lh0-", "-lh1-", "-lh2-", "-lh3-", "-lh4-", 
	"-lh5-", "-lzs-", "-lz5-", "-lz4-", "\0\0\0\0\0"
};

/*******************************
	test the file name which
	should be melted
*******************************/
static char tstdir(char *name)
{
	char *p, *q, yn;
	int absent;
	struct find_t srchbuf;

	p = name;
	if (*p && p[1] == ':')				/* skip a drive name */
		p += 2;
	if (*p == DELIM)					/* skip a root mark('\') */
		p++;
	yn = flg_m ? 'Y' : 'N';
	q = p;
	while ((p = strchr(p, DELIM)) != NULL) {	/* skip to next '\' */
		if (*q != '.') {
			*p = '\0';
			absent = _dos_findfirst(name, 0x17, &srchbuf);
			if (absent) {	
				if (yn == 'N') {
					*p = DELIM;
					eprintf("'%s' : %s", name, MKDIR);
					*p = '\0';
					yn = getyn();
				}
				if (yn == 'N') {
					return 'S';
				} else {
					if (makedir(name)) {			/* make directory */
						error(MKDIRERR, name);
					}
				}
			} else {
				if ((srchbuf.attrib & 0x10) == 0) {
					error(MKDIRERR, name);	/* if the name isn't directory */
				}
			}
			*p = DELIM;
		}
		q = ++p;
	}
	if (! _dos_findfirst(name, 0x17, &srchbuf)) {
		/* already exists */
		if (flg_c == 0 && 
		    dos2unix((struct ftime *)&srchbuf.wr_time) >= hpb.utc) yn = 'S';
		switch (flg_m) {
		case 0:
			if (yn != 'S') {
				eprintf("'%s' %s", name, OVERWT);
				yn = (getyn() == 'Y') ? 'O' : 'S';
				break;
			}
		case 1:
			if (yn == 'S') {
				skipdisp(NEWFILE);
				break;
			}
			yn = 'O';
			break;
		case 2:
			yn = 'R';
			break;
		}
		if (yn != 'O') {
			return yn;
		}
		if (srchbuf.attrib & 0x01 && 
			srchbuf.attrib != hpb.attr && !flg_a) {
        						/* if the file is read-only, */
								/* attributes must match */
			skipdisp(RDONLY);
			return 'S';
		}
		if (srchbuf.attrib & 0x10) {
			skipdisp(SAMEDIR);
			return 'S';
		}
		if (srchbuf.attrib & 0x07)
			_dos_setfileattr(name, 0x20);	/* reset attributes */
	}
	return 'O';
}

static int rename_ext(char *name)
{
	int i;
	char *p, *q;
	struct find_t srchbuf;

	p = strrchr(name, '.');
	q = strrchr(name, DELIM);
	if (p <= q) p = name + strlen(name);
	for (i = 0; i <= 999; i++) {
		sprintf(p, ".%03d", i);
		if (_dos_findfirst(name, 0x17, &srchbuf)) return 1;
	}
	return 0;
}

void extract(char far *bdir)
{
	char *path;
	int method;

	memcpy(methods[9], hpb.method, 5);
	for (method = 0; memcmp(hpb.method, methods[method], 5); method++);
	if (method == 9) {
		skipdisp(METHODERR);
		return;
	}

	switch (cmd) {
	case 'E':
		if ((hpb.attr & 0x06) && flg_a == 0) {
			skipdisp(SPECIALATTR);
			return;
		}
		form_path(hpb.pathname);
		hpb.filename = getfilename(hpb.pathname);
		path = (flg_x) ? hpb.pathname : hpb.filename;
		far_strcpy(filename3, bdir);
		if (path[1] == ':') path += 2;
		if (*(unsigned char *)path == DELIM) {
			if (filename3[1] == ':') {
				filename3[2] = '\0';
			} else {
				*filename3 = '\0';
			}
		}
		strcat(filename3, path);

		switch (tstdir(filename3)) {
		case 'R':
			if (rename_ext(filename3)) break;
			skipdisp(NOMOREEXT);
		case 'S':
			return;
		}
		if (diskspace(filename3) < hpb.original) {
			skipdisp(DISKFULL);
			return;
		}
		file3 = mywopen(filename3, MKFILEERR);
		regdisp("Melting ", filename3);
		break;
	case 'P':
		file3 = fdopen(dup(1), "wb");
		regdisp("Melting", NULL);
		fprintf(file3, "\r\n<< %s >>\r\n\r\n", hpb.filename);
		fflush(file3);
		break;
	case 'T':
		if (hpb.level < 0) {
			skipdisp(NOCRC);
			return;
		}
		regdisp("Testing", NULL);
		file3 = NULL;
		break;
	}

	interface.method = method;
	interface.dicbit = 13; /* method + 8; */
	interface.infile = file1;
	interface.outfile = file3;
	interface.original = hpb.original;
	interface.packed = hpb.packed;

	switch (method) {
	case 8:
		method = 0;
		break;
	case 6:
		interface.dicbit = 11;
		break;
	case 1:
	case 4:
	case 7:
		interface.dicbit = 12;
		break;
	}

	disp(cmd == 'P' && !outredir, cmd != 'P' && outredir);
	initdisp();

	if (method) {
		decode(&interface);
	} else {
		copyfile(file1, file3, hpb.original, 1);
	}

	if (file3) {
#if 0
		if (fflush(file3)) error(WTERR, filename3);
#else
		fflush(file3);
#endif
		setfiletime(file3, hpb.utc);
		fclose(file3);
		file3 = NULL;
	}
	if (hpb.level >= 0 && crc != hpb.filecrc) {
		errorlevel = 1;
		eprintf("\rCRC err\n");
		if (cmd == 'E') {
			if (flg_m == 0) {
				eprintf(MAYDELETE);
				if (getyn() == 'Y')
					remove(filename3);
			} else {
				remove(filename3);
			}
		}
		if (flg_m == 0) {
			eprintf(MAYCONT);
			if (getyn() != 'Y')
				error(CTRLBRK, NULL);
		}
	} else {
		if (cmd == 'T') {
			enddisp("Test OK");
		} else {
			if (cmd != 'P' || outredir) enddisp("Melted ");
		}
		if (cmd == 'E' && flg_a) {
			_dos_setfileattr(filename3, hpb.attr);
		}
	}
}

void extract_internal(FILE *f, char *p)
{
	interface.method = 5;
	interface.dicbit = 13;
	interface.infile = NULL;
	interface.outfile = f ? fdopen(dup(fileno(f)), "wb") : f;
	interface.original = *(ushort *)(p + 2);
	interface.packed = *(ushort *)p;
	interface.blkcnt = -1;
	interface.internal = (char *)(p + 4);
	disp(1, 0);
	initdisp();
	decode(&interface);
}
