#include <stdio.h>

/*
 * \usr\decusc\lib\wild.c
 *
 *	This module provides wilcard file manipulation through
 *	the following functions:
 *
 *		FILE *fwild (char *pattern, char *access)
 *		FILE *fnext (FILE *stream)
 *		char *fgetname (FILE *stream)
 */

extern FILE
    *fopen ();

extern unsigned char
    *dos_error ();

#define DOS_ERROR(n) { fputs (dos_error (n), stderr); exit (); }
#define DOS(n) { srv.ax = n << 8;\
		 status = sysint (0x21, &srv, &rrv);\
		 if (status & 1) DOS_ERROR (rrv.ax); };

/* These lengths all include the trailing null */

#define MAX_FILENAME_LENGTH 9
#define MAX_FILETYPE_LENGTH 4
#define MAX_PREFIX_LENGTH 64

#define MAX_FILESPEC_LENGTH (MAX_PREFIX_LENGTH + \
			     MAX_FILENAME_LENGTH + \
			     MAX_FILETYPE_LENGTH + 1)
#define MAX_WILD_CHANNELS 4

static unsigned int
    status;

struct regval {int ax, bx, cx, dx, si, di, ds, es;};
struct segval {int scs, sss, sds, ses;};

static struct regval srv, rrv;
static struct segval seg;

static struct
    {
    unsigned char reserved[21];
    unsigned char attr;
    unsigned time;
    unsigned date;
    unsigned size_l;
    unsigned size_h;
    unsigned char pname[13];
    } find_buf;

static struct wild_file
    {
    unsigned char pattern[MAX_FILESPEC_LENGTH];
    unsigned char name[MAX_FILESPEC_LENGTH];
    unsigned char prefix[MAX_PREFIX_LENGTH];
    unsigned char access[4];
    unsigned int first_flag;
    FILE *stream;
    } files[MAX_WILD_CHANNELS] =
    {
	{"", "", "", 0, NULL},	/* MAX_WILD_CHANNELS = 4 */
	{"", "", "", 0, NULL},
	{"", "", "", 0, NULL},
	{"", "", "", 0, NULL}
    };


FILE *fwild (pattern, access)
unsigned char *pattern, *access;
{
unsigned int
    i,
    findex;

FILE
    *fd;

struct wild_file
    *wfile;

char
    c,
    *sp,
    *terminator;

if (strlen (pattern) >= MAX_FILESPEC_LENGTH)
    error ("fwild called with filespec too long");
if (strlen (access) > 3)
    error ("fwild called with access string too long");
wfile = NULL;
for (findex = 0; findex < MAX_WILD_CHANNELS; findex++)
    if (files[findex].stream == NULL)
	{
	wfile = &files[findex];
	break;
	};
if (wfile == NULL)
    error ("fwild out of channels");

sp = pattern;
terminator = NULL;
while (c = *sp++)
    if (c == ':' || c == '\\' || c == '/')
	terminator = sp;	/* find last prefix terminator if one exists */
if (terminator == NULL)
    wfile->prefix[0] = 0;
else
    {				/* copy drive and directory */
    i = 0;
    for (sp = pattern; sp != terminator; sp++)
	wfile->prefix[i++] = *sp;
    wfile->prefix[i] = 0;
    };

segread (&seg);
srv.ds = seg.sds;
srv.dx = &find_buf;		/* set DMA to GNJFN block */
srv.ax = 0x1A00;
sysint (0x21, &srv, &rrv);

srv.dx = pattern;		/* pathname */
srv.cx = 0;			/* attributes */
srv.ax = 0x4E00;		/* GTJFN */
status = sysint (0x21, &srv, &rrv);
if (status & 1)
    if (rrv.ax == 18)
	{
	wfile->stream = NULL;
	return (NULL);
	}
    else
	DOS_ERROR (rrv.ax);

strcpy (wfile->pattern, pattern);	/* copy pattern */
strcpy (wfile->name, wfile->prefix);
strcat (wfile->name, find_buf.pname);	/* copy first name */
strcpy (wfile->access, access);		/* copy access string */
if ((fd = fopen (wfile->name, access)) == NULL)
    {
    wfile->stream = NULL;
    return (NULL);
    };
wfile->stream = fd;
wfile->first_flag = 1;
return (fd);
}

FILE *fnext (stream)
FILE *stream;
{
unsigned int
    findex;

FILE
    *fd;

struct wild_file
    *wfile;

wfile = NULL;
for (findex = 0; findex < MAX_WILD_CHANNELS; findex++)
    if (files[findex].stream == stream)
	{
	wfile = &files[findex];
	break;
	};
if (wfile == NULL)
    error ("fnext called for invalid stream");

if (wfile->first_flag)
    {
    wfile->first_flag = 0;
    return (wfile->stream);
    };
if (fclose (wfile->stream) != NULL)
    error ("implicit fclose failed in fnext");
srv.ax = 0x4F00;			/* GNJFN */
status = sysint (0x21, &srv, &rrv);
if (status & 1)
    if (rrv.ax != 18)
	DOS_ERROR (rrv.ax)
    else
	{
	wfile->stream = NULL;
	return (NULL);
	};
strcpy (wfile->name, wfile->prefix);
strcat (wfile->name, find_buf.pname);
if ((fd = fopen (wfile->name, wfile->access)) == NULL)
    {
    wfile->stream = NULL;
    return (NULL);
    };
wfile->stream = fd;
return (fd);
}

fgetname (stream, name)
FILE *stream;
unsigned char *name;
{
unsigned int
    findex;

struct wild_file
    *wfile;

wfile = NULL;
for (findex = 0; findex < MAX_WILD_CHANNELS; findex++)
    if (files[findex].stream == stream)
	{
	wfile = &files[findex];
	break;
	};
if (wfile == NULL)
    error ("fgetname called for invalid stream");
strcpy (name, wfile->name);
}

fclean (stream)
FILE *stream;
{
unsigned int
    findex;

struct wild_file
    *wfile;

if (stream == NULL) return;
wfile = NULL;
for (findex = 0; findex < MAX_WILD_CHANNELS; findex++)
    if (files[findex].stream == stream)
	{
	wfile = &files[findex];
	break;
	};
if (wfile == NULL)
    error ("fclean called for invalid stream");
fclose (stream);
wfile->stream = NULL;
}
