[comp.sources.amiga] filerequestor example/template

peter@sugar.UUCP (Peter DaSilva) (06/29/87)

    Here is a real nice file requestor example made by Peter DaSilva.
Note that his makefile didn't work immediately for the following
reasons, so I included one of my own called "makefile.manx".  This can
be found in this and in the lander program also written by him...
    -Craig Norborg
    comp.sources.amiga moderator

>My makefile didn't work because I put "delete" in ram:c. make doesn't
>do a path search for commands: they all have to be in c:. So my call
>to delete to trash the old version of the file before making the new
>one (to cut down on disk space during the make) would have failed on
>you. I also supplied my own default rules for CC because I wasn't sure
>I trusted Manx'. I have been burned with a make on the PC that had bad
>default rules.

#	This is a shell archive.
#	Remove everything above and including the cut line.
#	Then run the rest of the file through sh.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# Xshar: Extended Shell Archiver.
# This archive created: Sun Jun 28 16:15:52 1987
# By: Craig Norborg (Purdue University Computing Center)
#	Run the following text with /bin/sh to create:
#	makefile
#	makefile.manx
#	patmatch.c
#	readme
#	stdfile.c
#	test.c
#	vollist.c
cat << \SHAR_EOF > makefile
CFLAGS= -DTEST
OFILES= stdfile.o PatMatch.o VolList.o
CFILES= stdfile.c PatMatch.c VolList.c
OTHERS= Makefile test readme

SUFFIXES: .c .o .h

c.o:
	@-ram:c/del $*.o
	cc $(CFLAGS) +P -S -B -DAMIGA $*.c

test: stdfile.o PatMatch.o VolList.o
	@-ram:c/del test
	ln -o test stdfile.o PatMatch.o VolList.o -lcl32

stdfile.arc: $(CFILES) $(OTHERS)
	@-ram:c/del stdfile.arc
	arc a stdfile $(CFILES) $(OTHERS)

print: $(CFILES) $(OTHERS)
	:pr $(CFILES) $(OTHERS)
SHAR_EOF
cat << \SHAR_EOF > makefile.manx
CFLAGS= -DTEST +P -S -B -DAMIGA
OBJ= stdfile.o PatMatch.o VolList.o test.o
SRC= stdfile.c PatMatch.c VolList.c test.c
OTHERS= Makefile readme test

test: $(OBJ)
	ln -o test $(OBJ) -lcl32

stdfile.arc: ${CFILES} $(OTHERS)
	arc a stdfile $(CFILES) $(OTHERS)
SHAR_EOF
cat << \SHAR_EOF > patmatch.c
/* PatMatch.c - Implements AmigaDos Regular Expression Pattern Matching.
**
**  This program will test whether a string is an AmigaDos  regular expression
**  It may be used to implement wild expressions such as:
**
**    "copy #?.c to <dir>" to copy any file ending in .c
**
**  The program has two entry points: CmplPat, and Match.
**
**    CmplPat - takes a pattern and returns an auxilliary integer vector
**              which is used by Match.  The pattern is not modified in
**              any way.  CmplPat returns 1 if no errors were detected
**              while compiling the pattern; otherwise it returns 0;
**
**    Match   - takes the pattern, the auxilliary vector, and the string
**              to be matched.  It returns 1 if the string matches the
**              pattern; otherwise it returns 0;
**
**  Translated from BCPL by:
**              Jeff Lydiatt
**              Richmond B.C. Canada
**              16 May 1986.
**
**  Source: "A Compact Function for Regular Expression Pattern Matching",
**           Software - Practice and Experience, September 1979.
**
**  Useage:
**     To test if "file.c" matches the regular expression "#?.c"
**     char *Pat = "#?.c";
**     char *Str = "file.c";
**     WORD Aux[128];
**
**     if ( CmplPat( Pat, Aux ) == 0 )
**        {
**           printf("Bad Wildcard Expression\n");
**           exit(1);
**        }
**     if ( Match( Pat, Aux, Str ) == 1 )
**        printf("String matches the pattern\n");
**     else
**        printf("String does NOT match the pattern\n");
**/

/*--- Included files ----*/

#include <stdio.h>
#include <exec/types.h>
#include <ctype.h>

#define  EOS '\0'

/*--- Global Variables  ---*/

static char     Ch;      /* The current character in Pattern */
static char     *Pat;    /* Pointer to the Pattern */
static int      *Aux;    /* Pointer to returned auxilliary vector */
static int      PatP;    /* Current position in Pat */
static int      Patlen;  /* strlen(pat) */
static BOOL     Errflag; /* TRUE if error */
static int      *Work;   /* Pointer to Active work area */
static int      Wp;      /* Current position in work */
static BOOL     Succflag;/* True if "str" matches "pat" */

/*----------------------------------------------------------------*/
/*                   The Interpreter                              */
/*----------------------------------------------------------------*/

static void Put(N)
int N;
{
   register int *ip;
   register int *to;

   if ( N == 0 )
      Succflag = TRUE;
   else
      {
	for ( ip = &Work[ 1 ], to = &Work[ Wp ]; ip <= to; ip++)
	   if ( *ip == N )
	      return;
	Work[ ++Wp ] = N;
      }
}

int Match( Pat, Aux, Str )
char Pat[];
int  Aux[];
char Str[];
{
   int W[ 128 ];
   int  S = 0;
   int  I, N, Q, P, Strlength;
   char K;
   int  strlen();
   void Put();

   Work = W;
   Wp = 0;
   Succflag = FALSE;
   Strlength = strlen( Str );
   Put( 1 );

   if ( Aux[ 0 ] != 0 )
      Put( Aux[ 0 ] );

   for(;;)
      {
        /* First complete the closure */
        for( N=1; N <= Wp; N++ )
          {
	     P = Work[ N ];
	     K = Pat[ P-1 ];
	     Q = Aux[ P ];
	     switch( K )
	   	{
		  case '#': Put( P + 1 );
		  case '%': Put( Q );
		  default : break;
		  case '(':
		  case '|': Put( P + 1);
			    if ( Q != 0 )
			       Put( Q );
		}
	   }

	if ( S >= Strlength )
	   return Succflag;
	if ( Wp == 0 )
	   return FALSE;
	Ch = Str[ S++ ];

	/* Now deal with the match items */

	N = Wp;
	Wp = 0;
	Succflag = FALSE;

	for ( I = 1; I <= N; I++)
	  {
	     P = Work[ I ];
	     K = Pat[ P - 1 ];
	     switch( K )
	       {
		 case '#':
		 case '|':
		 case '%':
		 case '(': break;
		 case '\'': K = Pat[ P ];
		 default : if ( _toupper( Ch ) != _toupper( K ) )
			      break;
		 case '?': /* Successful match */
		 	   Put ( Aux[ P ] );
		} /* End Switch */
	  } /* End For I */
     } /* End for(;;) */
}


/*----------------------------------------------------------------*/
/*                     The Compiler                               */
/*----------------------------------------------------------------*/

static void  Rch() /* Read next character from Pat */
{
   if ( PatP >= Patlen )
      Ch = EOS;
   else
      Ch = Pat[ PatP++ ];
}

static void Nextitem() /* Get next char from Pat; recognize the ' escape char */
{
   if ( Ch == '\'' )
      Rch();
   Rch();
}

static void Setexits( List, Val )
int List;
int Val;
{
   int A;

   do
     {
	A = Aux[ List ];
	Aux[ List ] = Val;
	List = A;
     }
	while ( List != 0 );
}

static int Join( A, B )
int A, B;
{
    int T = A;

    if ( A == 0 )
	return B;
    while ( Aux[ A ] != 0 )
	A = Aux[ A ];
    Aux[ A ] = B;
    return T;
}

static int Prim()      /* Parse a Prim symbol */
{
   int   A = PatP;
   char Op = Ch;
   int  Exp();
   void Setexits(), Nextitem();

   Nextitem();
   switch( Op )
     {
        case EOS:
        case ')':
        case '|': Errflag = TRUE;
        default : return A;
        case '#': Setexits( Prim(), A ); return A;
        case '(': A = Exp( A );
		  if ( Ch != ')' )
		    {
			Errflag = TRUE;
		    }
		  Nextitem();
		  return A;
     }
}

static int Exp( AltP )    /* Parse an expression */
int AltP;
{
   int Exits = 0;
   int A;
   int Prim(), Exits(), Join();
   void Nextitem(), Setexits();

   for (;;)
	{
	   A = Prim();
	   if ( Ch == '|' || Ch == ')' || Ch == EOS )
	      {
		Exits = Join( Exits, A );
		if ( Ch != '|' )
		   return Exits;
		Aux[ AltP ] = PatP;
		AltP = PatP;
		Nextitem();
	      }
	   else
	      Setexits( A, PatP );
	}
}

int CmplPat( Pattern, CmplPattern)
char Pattern[];
int  CmplPattern[];
{
   int i, strlen();
   void Rch(), Setexits();

   Pat = Pattern;
   Aux = CmplPattern;
   PatP = 0;
   Patlen = strlen( Pat );
   Errflag = FALSE;

   for ( i = 0; i <= Patlen; i++ )
      Aux[ i ] = 0;
   Rch();
   Setexits( Exp(0), 0 );
   return (!Errflag);
}
SHAR_EOF
cat << \SHAR_EOF > readme
STDFILE is a module that can be linked with any Intuition based program
to provide a standard file requestor. Documentation is provided in the
source.

The module "PatMatch" is a fast pattern matching routine written
by Jeff Lydiatt of Richmond, British Columbia. He didn't provide a more
precise address than that, I'm sorry.

There are no copyright notices in his code, so I presume it's public
domain. Mine, of course, has the usual selfserving "freeware" notice.
If you want to deviate from the rather loose restrictions I impose,
please give me a call... I'm sure I can be talked out of them. If you
think my code is totally gross and you can do better, be my guest. I
do the same thing myself (frex, this code doesn't contain a single line
of code from anyone else's file requestors).

	--	Peter da Silva
		Houston, Texas  1987

		Voice: Home (713) 497-4372
		Data: Sugarland Fido (713) 933-2440
SHAR_EOF
cat << \SHAR_EOF > stdfile.c
/* STDFILE -- Standard File Requestor. Version 2.0a 15 June 1987
 *
 * AUTHOR -- Peter da Silva      US (713) 497-4372
 *
 * Copyright (c) 1987 Peter da Silva, all rights reserved.
 *
 *	This module may be freely used in any product, commercial or
 *	otherwise, provided credit is given for this module and
 *	and provided this notice remains intact in the source. The
 *	intent of this module is to provide a standard file requestor
 *	such as is available on the Macintosh, in GEM on the IBM-PC
 *	and Atari ST, and in the Microsoft Windows software on the
 *	IBM-PC. The advantage this module has over other requestors
 *	is that it minimises disk accesses: an important consideration
 *	given the structure of AmigaDos directories. If you need to
 *	modify it for your needs, by all means go ahead... but please
 *	conform to the intent of this program as stated above. If you
 *	have suggestions for improvements, by all means call me at
 *	the number listed above.
 *
 * Enhancements in the current version:
 *
 *	Gadgets now boxed. Display generally cleaned up.
 *
 *	True "dictionary order" for searches.
 *
 *	Default pattern can now be specified. Default file name now
 *	specified in a single argument.
 *
 *	Directories always match.
 *
 *	Null pattern converted to "#?" universal wildcard.
 *
 *	If you attempt to build a file name longer than 128 characters the
 *	screen will flash and the operation will be aborted.
 *
 *	"Volumes" gadget, using the device list code in "mounted". This
 *	gadget brings up a list of all currently mounted volumes for
 *	selection. Volumes leaves the directory specification intact, so
 *	you can quickly return to where you left off.
 *
 *	With these enhancements it is now possible to select any file on
 *	any device without touching the keyboard. This is now release 2.0,
 *	as it is significantly better than 1.0.
 *
 * Acknowledgements:
 *
 *	Thanks to Jeff Lydiatt for the pattern matching code in PatMatch.c
 *	Thanks to Jay Miner, =RJ= and the whole Amiga team for the Amiga
 *	itself.
 *
 * Environment:
 *
 *	IntuitionBase and GfxBase must be open. dos.library must be open
 *	under the name "DosLibrary". Link with PatMatch.o and VolList.o.
 *
 * Usage:
 *
 *	#define MAXFILENAME 128
 *
 *	int stdfile(title, default_file, default_pat, name);
 *	char *title;
 *	char *default_file;
 *	char *default_pattern;
 *	char name[MAXFILENAME];
 *
 *	struct Screen *stdscreen;
 *
 *	STDFILE puts up a file requestor (actually, it's a plain window)
 *	in stdscreen. If stdscreen is NULL, the workbench screen is used.
 *	The requestor looks like this (allowing for the limitations of
 *	text):
 *
 *	+-----------------------------------+
 *	|o| Title ------------------- |  |  | title parameter, or "File Name"
 *	|-----------------------------------|
 *	| Directory: [                    ] | Directory parameter, or current.
 *	| File name: [                    ] | Default parameter, or empty.
 *	| Pattern:   [                    ] | Initially empty, if the user
 *	| +-------------------------------+ | enters anything here it will
 *	| | [Filename]                 |  | | be used to select files. The
 *	| | [Filename]                 |  | | file display will also be empty
 *	| | [Filename]                 |@@| | to start with to avoid excess
 *	| | [Filename]                 |@@| | disk I/O. If the user selects
 *	| |                            |@@| | here the directory will be
 *	| |                            |@@| | scanned looking for files
 *	| |                            |  | | matching the specified pattern,
 *	| |                            |  | | or "#?" if no pattern is given.
 *	| |                            |  | |
 *	| +-------------------------------+ | ACCEPT returns 1. CANCEL
 *	| [ACCEPT]    [VOLUMES]    [CANCEL] | or the close gadget return 0.
 *	+-----------------------------------+ VOLUMES displays volume names.
 *
 *	The number of filenames displayed is specified at compile time in the
 *	constant MAXFILES. The maximum size of a filename is specified in the
 *	constant MAXNAME. The parameter "Default file" will be broken into
 *	directory and file parts.
 */
char *Copyright =
"stdfile V2.0a. Copyright (c) 1987 Peter da Silva. All rights reserved.";
#include <intuition/intuitionbase.h>
#include <intuition/intuition.h>
#include <libraries/dos.h>
#include <exec/memory.h>

char *malloc();

#define MAXFILES 8
#define MAXNAME 32
#define MAXFULL (MAXNAME*4)

/* SIZING PARAMS */
#define Z NULL
#define INDENT 6
#define LEFTMAR (INDENT-1)
#define BORDER 3
#define CHSIZ 8
#define HT CHSIZ
#define BASELINE 6

/* GADGET BORDERS */
#define IN1 LEFTMAR+10*CHSIZ
#define IN3 LEFTMAR+3
#define IN4 -(INDENT+6*CHSIZ+1)
#define IN5 -(INDENT+CHSIZ*2)
#define IN6 ((WINWD-WD6)/2)
#define WD1 -(INDENT+IN1)
#define WD3 (6*CHSIZ)
#define WD4 (6*CHSIZ)
#define WD5 (CHSIZ*2+2)
#define WD6 (7*CHSIZ)
#define TP1 (CHSIZ+BORDER)
#define TP2 (TP1+HT+1)
#define TP3 (TP2+HT+1)
#define TP4 -(BORDER+HT4-1)
#define TP5 (TP3+HT+BORDER)
#define HT4 (HT+1)
#define HT5 CHSIZ*MAXFILES+INDENT

#define WINHT (TP5 + HT5 + (-TP4) + BORDER)
#define WINWD (INDENT*4 + (MAXNAME+2)*CHSIZ)
#define WININ (640-WINWD)/2
#define WINTP (200-WINHT)/2

#define HOMEX (INDENT+LEFTMAR)
#define HOMEY (TP5+BORDER)
#define LASTX (HOMEX+MAXNAME*CHSIZ)
#define LASTY (HOMEY+MAXFILES*CHSIZ)

#define BTP TP5
#define BIN LEFTMAR
#define BWD (WINWD-INDENT-BIN)
#define BHT (WINHT-BTP-(-TP4+BORDER+1))

#define SF GADGHCOMP|GRELWIDTH
#define SEL SELECTED
#define BF1 GADGHCOMP|GRELBOTTOM
#define BF2 GADGHCOMP|GRELBOTTOM|GRELRIGHT
#define PF GRELRIGHT

#define SA RELVERIFY
#define CEN STRINGCENTER
#define BA RELVERIFY
#define PA RELVERIFY

#define SI(n) (APTR)&STD_String[n]
#define G(n) &STD_Gadget[n]
#define IMAG (APTR)&STD_Image
#define PROP (APTR)&STD_Prop

#define SG STRGADGET
#define BG BOOLGADGET
#define PG PROPGADGET

#define FP AUTOBACKPEN
#define BP AUTOFRONTPEN

#define OKTEXT &STD_OK
#define NOTEXT &STD_CANCEL
#define VLTEXT &STD_VOLUME

static int DoneFlag;

#define DirName SBuffer[0]
#define FileName SBuffer[1]
#define PatName SBuffer[2]
#define STRINGS 3

static UBYTE SBuffer[STRINGS][MAXFULL];
static UBYTE Undo[MAXFULL];

static struct StringInfo STD_String[STRINGS] = {
	{SBuffer[0],Undo,0,MAXFULL,0},
	{SBuffer[1],Undo,0,MAXFULL,0},
	{SBuffer[2],Undo,0,MAXFULL,0}
};

static struct PropInfo STD_Prop = { AUTOKNOB|FREEVERT, 0, 0, 0, 0 };

static struct IntuiText STD_OK =
	{ FP, BP, JAM2, 0, 1, Z, (UBYTE *)"ACCEPT", Z };
static struct IntuiText STD_CANCEL =
	{ FP, BP, JAM2, 0, 1, Z, (UBYTE *)"CANCEL", Z };
static struct IntuiText STD_VOLUME =
	{ FP, BP, JAM2, 0, 1, Z, (UBYTE *)"VOLUMES", Z };

#define BUTTONS 3
#define BUTVEC 8

static SHORT butvecs[BUTTONS][BUTVEC*2] = {
	{
		-2, HT4,
		-2, -1,
		WD3+1,-1,
		WD3+1,HT4,
		-3, HT4,
		-3,-1,
		WD3+2,-1,
		WD3+2, HT4
	},
	{
		-2, HT4,
		-2, -1,
		WD4+1,-1,
		WD4+1,HT4,
		-3, HT4,
		-3,-1,
		WD4+2,-1,
		WD4+2, HT4
	},
	{
		-2, HT4,
		-2, -1,
		WD6+1,-1,
		WD6+1,HT4,
		-3, HT4,
		-3,-1,
		WD6+2,-1,
		WD6+2, HT4
	}
};
static struct Border ButBorder[BUTTONS] = {
	{0, 0, FP, BP, JAM1, BUTVEC, butvecs[0], NULL},
	{0, 0, FP, BP, JAM1, BUTVEC, butvecs[1], NULL},
	{0, 0, FP, BP, JAM1, BUTVEC, butvecs[2], NULL}
};
#define BB(n) &ButBorder[n]

static struct Image STD_Image;

#define DIRID 0
#define FILID 1
#define PATID 2
#define YESID 3
#define CANID 4
#define VOLID 5
#define BARID 6
#define GADGETS 7

static struct Gadget STD_Gadget[GADGETS] = {
/*NEXT, LFT, TP,WDTH, H, FLAG,  ACT, TYP, REND, Z, TXT, Z, SPEC, ID, Z */
{ G(1), IN1,TP1, WD1,HT, SF,     SA,  SG,    Z, Z,   Z, Z, SI(0), 0, 0 },
{ G(2), IN1,TP2, WD1,HT, SF|SEL, SA,  SG,    Z, Z,   Z, Z, SI(1), 1, 0 },
{ G(3), IN1,TP3, WD1,HT, SF,     SA,  SG,    Z, Z,   Z, Z, SI(2), 2, 0 },
{ G(4), IN3,TP4, WD3,HT4,BF1,    BA,  BG,BB(0), Z, OKTEXT, Z,  Z, 3, 0 },
{ G(5), IN4,TP4, WD4,HT4,BF2,    BA,  BG,BB(1), Z, NOTEXT, Z,  Z, 4, 0 },
{ G(6), IN6,TP4, WD6,HT4,BF1,    BA,  BG,BB(2), Z, VLTEXT, Z,  Z, 5, 0 },
{ NULL, IN5,TP5, WD5,HT5,PF,     PA,  PG, IMAG, Z,   Z, Z,  PROP, 6, 0 }
};

static struct NewWindow STD_NewWindow = {
	WININ, WINTP, WINWD, WINHT, -1, -1,
	REFRESHWINDOW|MOUSEBUTTONS|GADGETUP|CLOSEWINDOW,
	WINDOWDRAG|WINDOWDEPTH|WINDOWCLOSE|SIMPLE_REFRESH|ACTIVATE,
	G(0), NULL, "File Name Requestor",
	NULL, NULL, 0, 0, 0, 0, WBENCHSCREEN
};
static struct Window *STD_Window;

#define NVEC 6

static SHORT Vectors[NVEC*2] = {
	BIN+1, BTP,
	BIN+1, BTP+BHT,
	BIN+BWD, BTP+BHT,
	BIN+BWD, BTP,
	BIN, BTP,
	BIN, BTP+BHT
};

static struct Border STD_FileBox = {
	0, 0, FP, BP, JAM1, NVEC, Vectors, NULL
};

static struct IntuiText STD_Text[3] = {
	{ FP, BP, JAM2, 0, 0, NULL, (UBYTE *)"Directory:", NULL },
	{ FP, BP, JAM2, 0, 0, NULL, (UBYTE *)"File Name:", NULL },
	{ FP, BP, JAM2, 0, 0, NULL, (UBYTE *)"Pattern:", NULL }
};

static OpenFileWindow()
{
	extern struct IntuitionBase *IntuitionBase;
	int i;

	/* Rebuild gadget list */
	STD_NewWindow.FirstGadget = &STD_Gadget[0];
	for(i = 0; i < GADGETS; i++) {
		STD_Gadget[i].NextGadget = (i==GADGETS-1)?(0):(&STD_Gadget[i+1]);
	}
	for(i = 0; i < STRINGS; i++) {
		STD_String[i].BufferPos = strlen(SBuffer[i]);
		STD_String[i].DispPos = 0;
	}
	STD_Prop.VertBody = 0xFFFF;
	STD_Prop.VertPot = 0;

	if(!(STD_Window = OpenWindow(&STD_NewWindow))) {
		return 0;
	}

	/* This optional line will activate a string gadget	*/
	if ( IntuitionBase->lib_Version > 32 )
	{
		ActivateGadget(G(1),STD_Window,0L);
	}

	CalcPropGadget();
	PaintFileWindow();
	return 1;
}

static CloseFileWindow()
{
	STD_NewWindow.LeftEdge = STD_Window->LeftEdge;
	STD_NewWindow.TopEdge = STD_Window->TopEdge;
	if(STD_Window)
		CloseWindow(STD_Window);
}

static int State;

#define INITIAL 0
#define DIRECTORY 1

static PaintFileWindow()
{
	DrawBorder(STD_Window->RPort, &STD_FileBox, 0, 0);
	PrintIText(STD_Window->RPort, &STD_Text[0], LEFTMAR, TP1);
	PrintIText(STD_Window->RPort, &STD_Text[1], LEFTMAR, TP2);
	PrintIText(STD_Window->RPort, &STD_Text[2], LEFTMAR, TP3);
	if(State == DIRECTORY) PrintFileNames();
}

static int FirstFile;
static int Selected;
static int NumFiles;
static struct dirent {
	struct dirent *nextfile;
	SHORT filetype;
	char *filename;
} *NameList, **NameTable;

#define FILETYPE 0
#define DIRTYPE 1
#define VOLTYPE 2

static PrintFileNames()
{
	int i;

	for(i = 0; i < MAXFILES; i++) {
		SetBPen(STD_Window->RPort, BP);
		SetAPen(STD_Window->RPort, BP);
		RectFill(STD_Window->RPort,
			HOMEX, HOMEY+i*CHSIZ,
			LASTX, HOMEY+(i+1)*CHSIZ);
		if(i+FirstFile < NumFiles)
			PrintName(i+FirstFile, i+FirstFile==Selected);
	}
}

static PrintName(file, hilite)
int file;
int hilite;
{
	int i;

	i = file - FirstFile;

	Move(STD_Window->RPort, HOMEX, HOMEY+i*CHSIZ+BASELINE);
	if(hilite == 0) {
		SetBPen(STD_Window->RPort, BP);
		if(NameTable[file]->filetype == FILETYPE)
			SetAPen(STD_Window->RPort, FP);
		else
			SetAPen(STD_Window->RPort, 3);
	} else {
		SetAPen(STD_Window->RPort, BP);
		if(NameTable[file]->filetype == FILETYPE)
			SetBPen(STD_Window->RPort, FP);
		else
			SetBPen(STD_Window->RPort, 3);
	}
	Text(STD_Window->RPort,
		NameTable[file]->filename,
		strlen(NameTable[file]->filename));
}

static CalcPropGadget()
{
	int VertPot, VertBody;

	if(State == INITIAL) return;

	if(NumFiles<=MAXFILES) {
		VertBody = 0xFFFF;
		VertPot = 0;
		FirstFile = 0;
	} else {
		VertBody = ((MAXFILES<<16)-1) / NumFiles;
		VertPot = 0;
		FirstFile = 0;
	}

	ModifyProp(&STD_Gadget[BARID], STD_Window, NULL,
		STD_Prop.Flags, 0, VertPot, 0, VertBody);
}

static CalcFilePosition()
{
	int old_pos;

	if(State == INITIAL) return;

	old_pos = FirstFile;
	if(NumFiles<=MAXFILES)
		FirstFile = 0;
	else {
		int VertPot = STD_Prop.VertPot;

		FirstFile = ((VertPot+1)*(NumFiles-MAXFILES))>>16;
	}
	if(old_pos != FirstFile)
		PrintFileNames();
}

FreeList(list)
struct dirent *list;
{
	struct dirent *ptr;

	while(list) {
		ptr = list->nextfile;
		if(list->filename) free(list->filename);
		free(list);
		list = ptr;
	}
}

static ReadNewDir()
{
	struct dirent *NewList, **NewTable, *ptr;
	int NewCount;
	struct FileInfoBlock *FIB;
	BPTR dirlock;

	if(State != DIRECTORY) {
		NameTable = 0;
		NameList = 0;
	}
	if(DirName[0])
		dirlock = Lock(DirName, ACCESS_READ);
	else {
		BPTR ram;
		ram = Lock("RAM:", ACCESS_READ);
		dirlock = CurrentDir(ram);
		CurrentDir(dirlock);
		UnLock(ram);
	}
	if(dirlock==0)
		return 0;

	/* FIB must be long word aligned, and aztec doesn't guarantee this, so: */
	if((FIB = AllocMem(sizeof(struct FileInfoBlock), MEMF_PUBLIC)) == 0) {
		UnLock(dirlock);
		return 0;
	}
	if(!Examine(dirlock, FIB)) {
		UnLock(dirlock);
		FreeMem(FIB, sizeof(struct FileInfoBlock));
		return 0;
	}
	if(FIB->fib_DirEntryType < 0) {
		UnLock(dirlock);
		FreeMem(FIB, sizeof(struct FileInfoBlock));
		return 0;
	}
	NewList = 0;
	NewCount = 0;
	while(ExNext(dirlock, FIB)) {
		NewCount += 1;
		ptr = (struct dirent *)malloc(sizeof(struct dirent));
		if(ptr==0) {
			FreeList(NewList);
			UnLock(dirlock);
			FreeMem(FIB, sizeof(struct FileInfoBlock));
			return 0;
		}
		ptr->nextfile = NewList;
		ptr->filetype = (FIB->fib_DirEntryType<0)?FILETYPE:DIRTYPE;
		ptr->filename = malloc(strlen(FIB->fib_FileName)+1);
		if(ptr->filename == 0) {
			FreeList(ptr);
			UnLock(dirlock);
			FreeMem(FIB, sizeof(struct FileInfoBlock));
			return 0;
		}
		strcpy(ptr->filename, FIB->fib_FileName);
		NewList = ptr;
	}
	FreeMem(FIB, sizeof(struct FileInfoBlock));
	if(DirName[0]) {
		UnLock(dirlock);
	}
	NewTable = malloc(sizeof(struct dirent *)*NewCount);
	if(NewTable==0) {
		FreeList(NewList);
		return 0;
	}
	FreeList(NameList);
	NameList = NewList;
	if(NameTable) free(NameTable);
	NameTable = NewTable;

	if(PatName[0]==0)
		SetPatName("#?");

	State = DIRECTORY;
	Selected = -1;

	ReCalcPattern();
}

static ReadVol()
{
	struct dirent *NewList, **NewTable, *ptr;
	int NewCount;
	char name[MAXNAME];

	if(State != DIRECTORY) {
		NameTable = 0;
		NameList = 0;
	}
	OpenVolList();
	NewList = 0;
	NewCount = 0;
	while(ReadVolList(name)) {
		NewCount += 1;
		ptr = (struct dirent *)malloc(sizeof(struct dirent));
		if(ptr==0) {
			FreeList(NewList);
			return 0;
		}
		ptr->nextfile = NewList;
		ptr->filetype = VOLTYPE;
		ptr->filename = malloc(strlen(name)+1);
		if(ptr->filename == 0) {
			FreeList(ptr);
			return 0;
		}
		strcpy(ptr->filename, name);
		NewList = ptr;
	}
	CloseVolList();
	NewTable = malloc(sizeof(struct dirent *)*NewCount);
	if(NewTable==0) {
		FreeList(NewList);
		return 0;
	}
	FreeList(NameList);
	NameList = NewList;
	if(NameTable) free(NameTable);
	NameTable = NewTable;

	if(PatName[0]==0)
		SetPatName("#?");

	State = DIRECTORY;
	Selected = -1;

	ReCalcPattern();
}

static WORD PatCode[128];

static patcomp()
{
	/* This is a judgement call: that no pattern should be equivalent
	   to "#?". Perhaps it should do this invisibly, by adding a
	   pointer to the real pattern name and making it PatName or "#?"
	   as appropriate. */

	if(!PatName[0])
		SetPatName("#?");
	return CmplPat(PatName, PatCode);
}

static patmatch(name)
{
	return Match(PatName, PatCode, name);
}

/* this routine does a true dictionary search:
 *
 *		Devs < devs but Devs > devices
 */
static table_compare(p1, p2)
struct dirent **p1, **p2;
{
	char *s1, *s2;
	char c1, c2;
	char firstdiff;

	s1 = (*p1)->filename;
	s2 = (*p2)->filename;
	firstdiff = 0;

	while(*s1 && *s2) {
		c1 = *s1++;
		c2 = *s2++;
		if(firstdiff==0)
			firstdiff = c1 - c2;
		if(c1>='A' && c1<='Z') c1 = c1+'@';
		if(c2>='A' && c2<='Z') c2 = c2+'@';
		if(c1 != c2)
			return c1 - c2;
	}
	return firstdiff;
}

static sort_table()
{
	qsort(NameTable, NumFiles, sizeof(struct dirent *), table_compare);
	return 1;
}

static ReCalcPattern()
{
	if(State != DIRECTORY)
		ReadNewDir();
	else {
		struct dirent *ptr;
		patcomp();

		NumFiles = 0;
		for(ptr = NameList; ptr; ptr=ptr->nextfile) {
			/* Directories always match. Is this good? */
			if(ptr->filetype == DIRTYPE ||
			   ptr->filetype == VOLTYPE ||
			   patmatch(ptr->filename)) {
				NameTable[NumFiles] = ptr;
				NumFiles++;
			}
		}
		sort_table();
		CalcPropGadget();
		Selected = -1;
		PrintFileNames();
	}
}

static SetGadgetText(id, text)
int id;
char *text;
{
	int position;

	position = RemoveGadget(STD_Window, G(id));
	if(position != -1) {
		strcpy(SBuffer[id], text);
		STD_String[id].BufferPos = strlen(text);
		position = AddGadget(STD_Window, G(id), -1);
		if(position != -1)
			RefreshGadgets(G(id), STD_Window, NULL);
	}
}

static SetDirName(name)
char *name;
{
	char buffer[MAXFULL+1], *ptr;
	int index;
	char lastchar;

	/* Can't enter a file name too long. */
	if(strlen(DirName) + strlen(name) + 1 > MAXFULL) {
		DisplayBeep();
		return 0;
	}
	index = 0;
	lastchar = 0;
	for(ptr = DirName; *ptr; ptr++)
		buffer[index++] = lastchar = *ptr;
	if(lastchar!=':' && lastchar!=0)
		buffer[index++] = '/';
	strcpy(&buffer[index], name);
	SetGadgetText(DIRID, buffer);
	SetGadgetText(FILID, "");
	return 1;
}

static SetFileName(name)
char *name;
{
	/* Can't enter a file name too long. */
	if(strlen(DirName) + strlen(name) + 1 > MAXFULL) {
		DisplayBeep();
		return 0;
	}
	SetGadgetText(FILID, name);
	return 1;
}

static SetPatName(name)
char *name;
{
	SetGadgetText(PATID, name);
}

static ProcessGadget(id)
int id;
{
	switch(id) {
		case DIRID: ReadNewDir(); break;
		case FILID: DoneFlag = 1; break;
		case PATID: ReCalcPattern(); break;
		case BARID: CalcFilePosition(); break;
		case YESID: DoneFlag = 1; break;
		case CANID: DoneFlag = -1; break;
		case VOLID: ReadVol(); break;
	}
}

static ProcessMouse(x, y, code, seconds, micros)
int x, y, code;
{
	int NewSelected;
	static int oseconds = 0, omicros = 0;

	if(x<HOMEX || y<HOMEY || x>=LASTX || y>=LASTY)
		return;
	if((code&SELECTUP) == SELECTUP)
		return;
	if(State != DIRECTORY) {
		ReadNewDir();
		return;
	}
	NewSelected = (y-HOMEY)/CHSIZ + FirstFile;
	if(NewSelected == Selected) {
		if(Selected != -1) {
			if(DoubleClick(oseconds, omicros, seconds, micros)) {
				if(NameTable[Selected]->filetype == DIRTYPE) {
					if(SetDirName(NameTable[Selected]->filename))
						ReadNewDir();
				} else if(NameTable[Selected]->filetype == VOLTYPE) {
					SetGadgetText(DIRID, NameTable[Selected]->filename);
					SetGadgetText(FILID, "");
					ReadNewDir();
				} else {
					if(!SetFileName(NameTable[Selected]->filename))
						Selected = -1;
					DoneFlag = 1;
				}
			}
		}
	} else {
		if(Selected != -1 &&
		   Selected>=FirstFile && Selected<FirstFile+MAXFILES)
			PrintName(Selected, 0);
		Selected = NewSelected;
		if(Selected>=NumFiles)
			Selected = -1;
		else {
			if(SetFileName(NameTable[Selected]->filename))
				PrintName(Selected, 1);
			else
				Selected = -1;
		}
	}
	oseconds = seconds;
	omicros = micros;
}

stdfile(title, deffile, defpat, name)
char *title, *deffile, *defpat, *name;
{
	if(title)
		STD_NewWindow.Title = (UBYTE *)title;
	else
		STD_NewWindow.Title = (UBYTE *)"Enter File Name";
	if(deffile) {
		int i;
		for(i = strlen(deffile)-1; i>=0; i--) {
			if(deffile[i]==':' || deffile[i]=='/') {
				int hold;
				strcpy(FileName, &deffile[i+1]);
				if(deffile[i]==':')
					i++;
				hold = deffile[i];
				deffile[i] = 0;
				strcpy(DirName, deffile);
				deffile[i] = hold;
				break;
			}
		}
		if(i<0) {
			strcpy(FileName, deffile);
			DirName[0] = 0;
		}
	} else {
		DirName[0] = 0;
		FileName[0] = 0;
	}
	if(defpat)
		strcpy(PatName, defpat);
	else
		PatName[0] = 0;

	State = INITIAL;
	NameTable = 0;
	NameList = 0;

	if(OpenFileWindow()) {
		struct IntuiMessage *msg;
		DoneFlag = 0;
		while(!DoneFlag) {
			Wait(1<<STD_Window->UserPort->mp_SigBit);
			while(msg = GetMsg(STD_Window->UserPort)) {
				switch(msg->Class) {
					case CLOSEWINDOW:
						DoneFlag = -1;
						break;
					case MOUSEBUTTONS:
						ProcessMouse(msg->MouseX, msg->MouseY,
							msg->Code,
							msg->Seconds, msg->Micros);
						break;
					case GADGETUP:
						ProcessGadget(
							((struct Gadget *)msg->IAddress)->GadgetID
						);
						break;
					case REFRESHWINDOW:
						BeginRefresh(STD_Window);
						PaintFileWindow();
						EndRefresh(STD_Window, 1);
						break;
				}
				ReplyMsg(msg);
			}
		}

		CloseFileWindow();
	}
	else return 0;

	FreeList(NameList);
	if(NameTable) free(NameTable);

	if(DoneFlag==1) {
		int len;

		strcpy(name, DirName);
		if(FileName[0]) {
			if(len = strlen(name))
				if(name[len-1]!=':')
					strcat(name, "/");
			strcat(name, FileName);
			return 1;
		}

		/* Here the user has accepted the name without providing a file
		   name. I return true, but false may be more appropriate. What
		   do you think? */
		return 1;
	}
	return 0;
}
SHAR_EOF
cat << \SHAR_EOF > test.c
#include <intuition/intuitionbase.h>
#include <intuition/intuition.h>
#include <libraries/dos.h>
#include <exec/memory.h>
#include <stdio.h>

struct IntuitionBase *IntuitionBase;
struct GfxBase *GfxBase;
struct DosLibrary *DosLibrary;

#define SECSPERDAY (60*60*24)
#define SECSPERMIN 60
#define TICKSPERSEC TICKS_PER_SECOND

#define MAXNAME 128

char *stdfile();
extern char *Copyright;

main()
{
	char name[MAXNAME];

	if(!(IntuitionBase = OpenLibrary("intuition.library", 1))) {
		printf("Couldn't open intuition.library.\n");
		exit(20);
	}
	if(!(GfxBase = OpenLibrary("graphics.library", 1))) {
		printf("Couldn't open graphics.library.\n");
		CloseLibrary(IntuitionBase);
		exit(20);
	}
	if(!(DosLibrary = OpenLibrary("dos.library", 0))) {
		printf("Can't open dos.library\n");
		CloseLibrary(IntuitionBase);
		CloseLibrary(GfxBase);
		exit(20);
	}

	printf("Testing STDFILE standard file requestor.\n%s\n", Copyright);

	name[0] = 0;
	while(stdfile("Display file", name, 0, name)) {
		BPTR stdlock;
		struct FileInfoBlock *stdfib;
		FILE *fp;
		long datestamp;
		int c;

		stdfib = AllocMem(sizeof(struct FileInfoBlock), MEMF_PUBLIC);
		if(!stdfib) {
			printf("Out of memory!\n");
			break;
		}

		if(!(stdlock = Lock(name, ACCESS_READ))) {
			printf("Can't obtain lock for %s\n", name);
			FreeMem(stdfib, sizeof(struct FileInfoBlock));
			continue;
		}
		if(!(Examine(stdlock, stdfib))) {
			printf("Can't examine %s\n", name);
			UnLock(stdlock);
			FreeMem(stdfib, sizeof(struct FileInfoBlock));
			continue;
		}
		UnLock(stdlock);

		if(stdfib->fib_DirEntryType >= 0) {
			printf("%s is a directory.\n", name);
			FreeMem(stdfib, sizeof(struct FileInfoBlock));
			continue;
		}
		if(!(fp = fopen(name, "r"))) {
			perror(name);
			FreeMem(stdfib, sizeof(struct FileInfoBlock));
			continue;
		}

		datestamp = stdfib->fib_Date.ds_Days*SECSPERDAY +
			      stdfib->fib_Date.ds_Minute*SECSPERMIN +
				  stdfib->fib_Date.ds_Tick/TICKSPERSEC;
		printf("\n%s, %s\n", stdfib->fib_FileName,
			ctime(&datestamp));
		while((c = getc(fp)) != EOF)
			putchar(c);
		fclose(fp);
		FreeMem(stdfib, sizeof(struct FileInfoBlock));
	}

	CloseLibrary(IntuitionBase);
	CloseLibrary(GfxBase);
	CloseLibrary(DosLibrary);
}
SHAR_EOF
cat << \SHAR_EOF > vollist.c
#include <libraries/dosextens.h>

#define toAPTR(b) ((b)<<2)
#define toBPTR(a) ((a)>>2)

struct DeviceList *list;

OpenVolList()
{
	extern struct DosLibrary *DosLibrary;
	struct RootNode *root;
	struct DosInfo *info;

	root = DosLibrary -> dl_Root;
	info = toAPTR(root->rn_Info);
	list = toAPTR(info->di_DevInfo);
}

ReadVolList(name)
char name[32];
{
	struct DeviceList *next;

	while(list) {
		next = toAPTR(list->dl_Next);
		if(list->dl_Type == DLT_VOLUME) {
			char *ptr;
			int count;
			ptr = toAPTR((BPTR)list->dl_Name);
			count = *ptr++;
			if(count > 30)
				count = 30;
			strncpy(name, ptr, count);
			name[count++] = ':';
			name[count] = 0;
			list = next;
			return 1;
		}
		list = next;
	}
	return 0;
}

CloseVolList()
{
S][P, JA *D