/* LHX.C -- An LHARC archive tester/scanner
 *
 * version 1.0 by Mark Armbrust, 15 April 1985   (IRS? Just say NO!)
 *
 * The code in this module is public domain -- do whatever you want with it */

static char version[] = "@(#)lhx.c version 1.0 by Mark Armbrust";

#include <process.h>
#include <stdio.h>
#include <string.h>
#include <dos.h>
#include <stdlib.h>
#include <malloc.h>

#define MAIN
#include "lhx.h"

/* private routines */

PRIVATE void	Usage		(void);
PRIVATE int 	Match		(char *, char *);
PRIVATE int 	WC_Match	(char *, char *);



PUBLIC void		main	(argc, argv)

int  				argc;
char			*	argv[];
{
char			*	arcname,
				*	arcpath;
FILE			*	arcfile;
struct fname	*	filename,
				*	filenames;
char			*	p,
				*	q;
struct find_t		findbuf;
int					i;
int					exitcode;


if (argc < 2)
	Usage();

--argc;
++argv;

mode = LIST;					/* set default mode */

if (**argv == '-') {			/* get mode switch if present */
	switch (*++*argv) {

		case 'e':
		case 'E':
			mode = EXTRACT;
			break;

		case 'l':
		case 'L':
			mode = LIST;
			break;

		case 's':
		case 'S':
			mode = SCAN;
			break;

		case 't':
		case 'T':
			mode = TEST;
			break;

		default:
			Usage();

		}
	--argc;
	++argv;
	}

header_start = -1;				/* disable starting offset options */
file_start = -1;
file_size = -1;
file_type = -1;
file_name[0] = 0;

if (argc && **argv == '@') {	/* get starting offsets */
	if (mode & SCAN)
		fprintf (stderr, "lhx:  WARNING -- offsets ignored.\n");
	else {
		mode |= AT;
		i = sscanf (++*argv, "%ld,%ld,%ld,%d,%s", &header_start, &file_start, 
					&file_size, &file_type, file_name);
		if (!i)
			Usage();
		}
	--argc;
	++argv;
	}

if (argc) {						/* get archive name */
	arcname = *argv;
	--argc;
	++argv;
	}
else
	Usage();

if (argc == 0) {				/* get file names if present */
	filenames = malloc (sizeof (struct fname));
	filenames->name = "*.*";
	filenames->next = 0;		/* none specified, assume *.* */
	}
else {
	if (mode & SCAN)
		fprintf (stderr, "lhx:  WARNING -- filenames ignored.\n");
	else {
		filenames = 0;				/* use the rest of the names to build a */
		while (argc--) {			/* linked list of filenames to match.	*/
			filename = malloc (sizeof (struct fname));
			filename->name = *argv++;
			filename->next = filenames;
			filenames = filename;
			}
		}
	}

if (strpbrk (arcname, "\\/:")) {
	arcpath = strdup(arcname);		/* strip path from arcname */
	p = arcpath - 1;
	while (q = strpbrk (++p, "\\/:"))
		p = q;
	*p = '\0';
	}
else
	arcpath = "";

strcpy (name, arcname);

p = name;
if (q = strrchr (p, '\\')) 
	p = q + 1;
if (q = strrchr (p, '/')) 
	p = q + 1;
if ( ! strchr (p, '.'))
	strcat (name, ".lzh");

if (_dos_findfirst (name, _A_NORMAL | _A_RDONLY ,&findbuf)) {
	fprintf (stderr, "lhx:  ERROR -- could not find any archive files "
			"matching '%s'.\n", arcname);
	exit (-1);
	}

exitcode = 0;

do {
	strcpy (name, arcpath);
	strcat (name, findbuf.name);
	arcfile = fopen (name, "rb");
	if ( ! arcfile) {
		fprintf (stderr, "lzx:  ERROR -- could not open archive file '%s'.\n",
				name);
		exit (-1);
		}

	switch (mode & MODES) {

		case EXTRACT:
		case LIST:
		case TEST:
			exitcode += Process (arcfile, filenames);
			break;

		case SCAN:
			Scan (arcfile);
			break;
		}

	fclose (arcfile);
	} while ( !(mode & AT) && ! _dos_findnext (&findbuf) );

exit (exitcode);
}



PRIVATE void	Usage	()

{
printf ("\n");
printf ("usage: lhx [-elst] [@hdr,data,size,type,name] arcname [filename ...]\n\n");
printf ("           E => extract files\n");
printf (" (default) L => list archive contents\n");
printf ("           S => scan damaged archive\n");
printf ("           T => test archive\n\n");
printf ("           @hdr,data,size,type,name => where to start in a damaged archive.\n");
exit (1);
}



PUBLIC int		MatchAny (name, filenames)

char			*	name;
struct fname	*	filenames;
{

do {
	if (Match (name, filenames->name))
		return 1;
	filenames = filenames->next;
	}
while (filenames);

return 0;
}



PRIVATE int 	Match (file, pattern)

	char			*file;
	char			*pattern;
	{
	int				i;
	register char	*filename;
	register char	*s;


/* strip off the leading path and trailing modifiers (zoo ver. number, etc.) */

	filename = file;
	if (s = strrchr (filename, '/')) filename = s+1;
	if (s = strrchr (filename, '\\')) filename = s+1;
	filename = strdup (filename);
/*	if (!filename) Abort ("filename strdup failed in Match"); */
	if (s = strpbrk (filename, ";*, ")) *s = '\0';

	i = WC_Match (filename, pattern);
	free (filename);

	return i;
	}



/*	Returns TRUE if s1 and s2 match under modified MSDOS wildcard rules.
 *
 *	Two strings match if all their substrings between : . / and \ match
 *	using * and ? as wildcards in the substrings.
 *
 *	Note that MSDOS is a bit schitzo about matching strings that end in : and .
 *	This code will only match them 1-to-1.  For example 'DIR X' matches X.* or
 *	X\*.* depending on what X is.  We do, however, match 'X' to 'X.' and 'X.*'
 *
 */

PRIVATE int 	WC_Match (s1, s2)

	register	char	*s1;
	register	char	*s2;
	{

#define IS_SLANT(c)		((c) == '/' || (c) == '\\')
#define IS_FILE_SEP(c)	( (c) == ':' || (c) == '.' || IS_SLANT(c) )

	while (1) {

		if (*s1 == '*' || *s2 == '*') {
			while (*s1 && ! IS_FILE_SEP (*s1))
				++s1;
			while (*s2 && ! IS_FILE_SEP (*s2))
				++s2;
			continue;
			}

		if (*s1 == '?') {
			if (!*s2 || IS_FILE_SEP (*s2))
				return 0;
			++s1;
			++s2;
			continue;
			}

		if (*s2 == '?') {
			if (!*s1 || IS_FILE_SEP (*s1))
				return 0;
			++s1;
			++s2;
			continue;
			}

		if (*s1 == 0 && *s2 == '.') {
			++s2;
			continue;
			}

		if (*s2 == 0 && *s1 == '.') {
			++s1;
			continue;
			}

		if (toupper(*s1) != toupper(*s2))
			return 0;

		if (*s1 == 0)		/* then (*s2 == 0) as well! */
			break;

		++s1;
		++s2;
		}

	return 1;
	}

