[comp.sys.amiga] Standard File Requestor

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

# Cut here and feed the rest to shell.
# I tried posting this to comp.sources.amiga, and it got to the moderator,
# but someone is eating it. I am therefore reposting it on comp.sys.amiga
# and risking the flames of the net gods. I would send it to the right
# newsgroup, honest, if it wasn't a bit-bucket.
echo x - ReadMe
sed 's/^X//' > ReadMe << '//END'
XSTDFILE is a module that can be linked with any Intuition based program
Xto provide a standard file requestor. Documentation is provided in the
Xsource. It has the advantage over the usual requestors that it never hits
Xthe disk unless it's absolutely necessary, and it's possible to get to any
Xfile on any device using just the mouse. Strictly speaking, it's not a
Xrequestor... thus it requires less set-up on *your* part. Does anyone know
Xwhy (other than efficiency, I guess) requestors don't normally get their
Xown window?
X
XThe module "PatMatch" is a fast pattern matching routine written
Xby Jeff Lydiatt of Richmond, British Columbia. He didn't provide a more
Xprecise address than that, I'm sorry.
X
XThere are no copyright notices in his code, so I presume it's public
Xdomain. Mine, of course, has the usual selfserving "freeware" notice.
XIf you want to deviate from the rather loose restrictions I impose,
Xplease give me a call... I'm sure I can be talked out of them. If you
Xthink my code is totally gross and you can do better, be my guest. I
Xdo the same thing myself (frex, this code doesn't contain a single line
Xof code from anyone else's file requestors).
X
X	--	Peter da Silva
X		Houston, Texas  1987
X
X		Voice: Home (713) 497-4372
X		Data: Sugarland Fido (713) 933-2440
X		UUCP: ...!seismo!soma!uhnix1!sugar!peter
//END
echo x - Makefile
sed 's/^X//' > Makefile << '//END'
XOFILES= test.o stdfile.o PatMatch.o VolList.o
XCFILES= test.c stdfile.c PatMatch.c VolList.c
XTEXT= Makefile readme
XOTHERS= $(TEXT) test
X
X.SUFFIXES: .c .o .h
X
X.c.o:
X	@-ram:c/del $*.o
X	cc +P -S -B -DAMIGA $*.c
X
Xtest: $(OFILES)
X	@-ram:c/del test
X	ln -o test $(OFILES) -lcl32
X
Xstdfile.arc: $(CFILES) $(OTHERS)
X	@-ram:c/del stdfile.arc
X	arc a stdfile $(CFILES) $(OTHERS)
X
Xprint: $(CFILES) $(TEXT)
X	pr $(CFILES) $(TEXT)
//END
echo x - STDFile.c
sed 's/^X//' > STDFile.c << '//END'
X/* STDFILE -- Standard File Requestor. Version 2.0a 15 June 1987
X *
X * AUTHOR -- Peter da Silva      US (713) 497-4372
X *
X * Copyright (c) 1987 Peter da Silva, all rights reserved.
X *
X *	This module may be freely used in any product, commercial or
X *	otherwise, provided credit is given for this module and
X *	and provided this notice remains intact in the source. The
X *	intent of this module is to provide a standard file requestor
X *	such as is available on the Macintosh, in GEM on the IBM-PC
X *	and Atari ST, and in the Microsoft Windows software on the
X *	IBM-PC. The advantage this module has over other requestors
X *	is that it minimises disk accesses: an important consideration
X *	given the structure of AmigaDos directories. If you need to
X *	modify it for your needs, by all means go ahead... but please
X *	conform to the intent of this program as stated above. If you
X *	have suggestions for improvements, by all means call me at
X *	the number listed above.
X *
X * Enhancements in the current version:
X *
X *	Gadgets now boxed. Display generally cleaned up.
X *
X *	True "dictionary order" for searches.
X *
X *	Default pattern can now be specified. Default file name now
X *	specified in a single argument.
X *
X *	Directories always match.
X *
X *	Null pattern converted to "#?" universal wildcard.
X *
X *	If you attempt to build a file name longer than 128 characters the
X *	screen will flash and the operation will be aborted.
X *
X *	"Volumes" gadget, using the device list code in "mounted". This
X *	gadget brings up a list of all currently mounted volumes for
X *	selection. Volumes leaves the directory specification intact, so
X *	you can quickly return to where you left off.
X *
X *	"Parent" gadget, displays parent directory.
X *
X *	With these enhancements it is now possible to select any file on
X *	any device without touching the keyboard. This is now release 2.0,
X *	as it is significantly better than 1.0.
X *
X * Acknowledgements:
X *
X *	Thanks to Jeff Lydiatt for the pattern matching code in PatMatch.c
X *	Thanks to Jay Miner, =RJ= and the whole Amiga team for the Amiga
X *	itself.
X *
X * Environment:
X *
X *	IntuitionBase and GfxBase must be open. dos.library must be open
X *	under the name "DOSBase". Link with PatMatch.o and VolList.o.
X *
X * Usage:
X *
X *	#define MAXFILENAME 128
X *
X *	int stdfile(title, default_file, default_pat, name);
X *	char *title;
X *	char *default_file;
X *	char *default_pattern;
X *	char name[MAXFILENAME];
X *
X *	struct Screen *stdscreen;
X *
X *	STDFILE puts up a file requestor (actually, it's a plain window)
X *	in stdscreen. If stdscreen is NULL, the workbench screen is used.
X *	The requestor looks like this (allowing for the limitations of
X *	text):
X *
X *	+-----------------------------------+
X *	|o| Title ------------------- |  |  | title parameter, or "File Name"
X *	|-----------------------------------|
X *	| Directory: [                    ] | Directory parameter, or current.
X *	| File name: [                    ] | Default parameter, or empty.
X *	| Pattern:   [                    ] | Initially empty, if the user
X *	| +-------------------------------+ | enters anything here it will
X *	| | [Filename]                 |  | | be used to select files. The
X *	| | [Filename]                 |  | | file display will also be empty
X *	| | [Filename]                 |@@| | to start with to avoid excess
X *	| | [Filename]                 |@@| | disk I/O. If the user selects
X *	| |                            |@@| | here the directory will be
X *	| |                            |@@| | scanned looking for files
X *	| |                            |  | | matching the specified pattern,
X *	| |                            |  | | or "#?" if no pattern is given.
X *	| |                            |  | |
X *	| +-------------------------------+ | ACCEPT returns 1. CANCEL
X *	| [ACCEPT]    [VOLUMES]    [CANCEL] | or the close gadget return 0.
X *	+-----------------------------------+ VOLUMES displays volume names.
X *
X *	The number of filenames displayed is specified at compile time in the
X *	constant MAXFILES. The maximum size of a filename is specified in the
X *	constant MAXNAME. The parameter "Default file" will be broken into
X *	directory and file parts.
X */
Xchar *Copyright =
X"stdfile V2.0a. Copyright (c) 1987 Peter da Silva. All rights reserved.";
X#include <intuition/intuitionbase.h>
X#include <intuition/intuition.h>
X#include <libraries/dos.h>
X#include <exec/memory.h>
X
Xchar *malloc();
X
X#define MAXFILES 8
X#define MAXNAME 32
X#define MAXFULL (MAXNAME*4)
X
X/* SIZING PARAMS */
X#define Z NULL
X#define INDENT 6
X#define LEFTMAR (INDENT-1)
X#define BORDER 3
X#define CHSIZ 8
X#define HT CHSIZ
X#define BASELINE 6
X#define BUTWID (6*CHSIZ+INDENT*2)
X
X/* GADGET BORDERS */
X#define IN1 LEFTMAR+10*CHSIZ
X#define IN3 LEFTMAR+3
X#define IN4 -(INDENT+6*CHSIZ+1)
X#define IN5 -(INDENT+CHSIZ*2)
X#define IN6 ((WINWD-BUTWID)/3+INDENT)
X#define IN7 (((WINWD-BUTWID)*2)/3+INDENT)
X#define WD1 -(INDENT+IN1)
X#define WD3 (6*CHSIZ)
X#define WD4 (6*CHSIZ)
X#define WD5 (CHSIZ*2+2)
X#define WD6 (6*CHSIZ)
X#define WD7 (6*CHSIZ)
X#define TP1 (CHSIZ+BORDER)
X#define TP2 (TP1+HT+1)
X#define TP3 (TP2+HT+1)
X#define TP4 -(BORDER+HT4-1)
X#define TP5 (TP3+HT+BORDER)
X#define HT4 (HT+1)
X#define HT5 CHSIZ*MAXFILES+INDENT
X
X#define WINHT (TP5 + HT5 + (-TP4) + BORDER)
X#define WINWD (INDENT*4 + (MAXNAME+2)*CHSIZ)
X#define WININ (640-WINWD)/2
X#define WINTP (200-WINHT)/2
X
X#define HOMEX (INDENT+LEFTMAR)
X#define HOMEY (TP5+BORDER)
X#define LASTX (HOMEX+MAXNAME*CHSIZ)
X#define LASTY (HOMEY+MAXFILES*CHSIZ)
X
X#define BTP TP5
X#define BIN LEFTMAR
X#define BWD (WINWD-INDENT-BIN)
X#define BHT (WINHT-BTP-(-TP4+BORDER+1))
X
X#define SF GADGHCOMP|GRELWIDTH
X#define SEL SELECTED
X#define BF1 GADGHCOMP|GRELBOTTOM
X#define BF2 GADGHCOMP|GRELBOTTOM|GRELRIGHT
X#define PF GRELRIGHT
X
X#define SA RELVERIFY
X#define CEN STRINGCENTER
X#define BA RELVERIFY
X#define PA RELVERIFY
X
X#define SI(n) (APTR)&STD_String[n]
X#define G(n) &STD_Gadget[n]
X#define IMAG (APTR)&STD_Image
X#define PROP (APTR)&STD_Prop
X
X#define SG STRGADGET
X#define BG BOOLGADGET
X#define PG PROPGADGET
X
X#define FP AUTOBACKPEN
X#define BP AUTOFRONTPEN
X
X#define OKTEXT &STD_OK
X#define NOTEXT &STD_CANCEL
X#define VLTEXT &STD_VOLUME
X#define PRTEXT &STD_PARENT
X
Xstatic int DoneFlag;
X
X#define DirName SBuffer[0]
X#define FileName SBuffer[1]
X#define PatName SBuffer[2]
X#define STRINGS 3
X
Xstatic UBYTE SBuffer[STRINGS][MAXFULL];
Xstatic UBYTE Undo[MAXFULL];
X
Xstatic struct StringInfo STD_String[STRINGS] = {
X	{SBuffer[0],Undo,0,MAXFULL,0},
X	{SBuffer[1],Undo,0,MAXFULL,0},
X	{SBuffer[2],Undo,0,MAXFULL,0}
X};
X
Xstatic struct PropInfo STD_Prop = { AUTOKNOB|FREEVERT, 0, 0, 0, 0 };
X
Xstatic struct IntuiText STD_OK =
X	{ FP, BP, JAM2, 0, 1, Z, (UBYTE *)"ACCEPT", Z };
Xstatic struct IntuiText STD_CANCEL =
X	{ FP, BP, JAM2, 0, 1, Z, (UBYTE *)"CANCEL", Z };
Xstatic struct IntuiText STD_VOLUME =
X	{ FP, BP, JAM2, 0, 1, Z, (UBYTE *)"VOLUME", Z };
Xstatic struct IntuiText STD_PARENT =
X	{ FP, BP, JAM2, 0, 1, Z, (UBYTE *)"PARENT", Z };
X
X#define BUTTONS 4
X#define BUTVEC 8
X
Xstatic SHORT butvecs[BUTTONS][BUTVEC*2] = {
X	{
X		-2, HT4,
X		-2, -1,
X		WD3+1,-1,
X		WD3+1,HT4,
X		-3, HT4,
X		-3,-1,
X		WD3+2,-1,
X		WD3+2, HT4
X	},
X	{
X		-2, HT4,
X		-2, -1,
X		WD4+1,-1,
X		WD4+1,HT4,
X		-3, HT4,
X		-3,-1,
X		WD4+2,-1,
X		WD4+2, HT4
X	},
X	{
X		-2, HT4,
X		-2, -1,
X		WD6+1,-1,
X		WD6+1,HT4,
X		-3, HT4,
X		-3,-1,
X		WD6+2,-1,
X		WD6+2, HT4
X	},
X	{
X		-2, HT4,
X		-2, -1,
X		WD7+1,-1,
X		WD7+1,HT4,
X		-3, HT4,
X		-3,-1,
X		WD7+2,-1,
X		WD7+2, HT4
X	}
X};
Xstatic struct Border ButBorder[BUTTONS] = {
X	{0, 0, FP, BP, JAM1, BUTVEC, butvecs[0], NULL},
X	{0, 0, FP, BP, JAM1, BUTVEC, butvecs[1], NULL},
X	{0, 0, FP, BP, JAM1, BUTVEC, butvecs[2], NULL},
X	{0, 0, FP, BP, JAM1, BUTVEC, butvecs[3], NULL}
X};
X#define BB(n) &ButBorder[n]
X
Xstatic struct Image STD_Image;
X
X#define DIRID 0
X#define FILID 1
X#define PATID 2
X#define YESID 3
X#define CANID 4
X#define VOLID 5
X#define PARID 6
X#define BARID 7
X#define GADGETS 8
X
Xstatic struct Gadget STD_Gadget[GADGETS] = {
X/*NEXT, LFT, TP,WDTH, H, FLAG,  ACT, TYP, REND, Z, TXT, Z, SPEC, ID, Z */
X{ G(1), IN1,TP1, WD1,HT, SF,     SA,  SG,    Z, Z,   Z, Z, SI(0), 0, 0 },
X{ G(2), IN1,TP2, WD1,HT, SF|SEL, SA,  SG,    Z, Z,   Z, Z, SI(1), 1, 0 },
X{ G(3), IN1,TP3, WD1,HT, SF,     SA,  SG,    Z, Z,   Z, Z, SI(2), 2, 0 },
X{ G(4), IN3,TP4, WD3,HT4,BF1,    BA,  BG,BB(0), Z, OKTEXT, Z,  Z, 3, 0 },
X{ G(5), IN4,TP4, WD4,HT4,BF2,    BA,  BG,BB(1), Z, NOTEXT, Z,  Z, 4, 0 },
X{ G(6), IN6,TP4, WD6,HT4,BF1,    BA,  BG,BB(2), Z, VLTEXT, Z,  Z, 5, 0 },
X{ G(7), IN7,TP4, WD7,HT4,BF1,    BA,  BG,BB(3), Z, PRTEXT, Z,  Z, 6, 0 },
X{ NULL, IN5,TP5, WD5,HT5,PF,     PA,  PG, IMAG, Z,   Z, Z,  PROP, 7, 0 }
X};
X
Xstatic struct NewWindow STD_NewWindow = {
X	WININ, WINTP, WINWD, WINHT, -1, -1,
X	REFRESHWINDOW|MOUSEBUTTONS|GADGETUP|CLOSEWINDOW,
X	WINDOWDRAG|WINDOWDEPTH|WINDOWCLOSE|SIMPLE_REFRESH|ACTIVATE,
X	G(0), NULL, "File Name Requestor",
X	NULL, NULL, 0, 0, 0, 0, WBENCHSCREEN
X};
Xstatic struct Window *STD_Window;
X
X#define NVEC 6
X
Xstatic SHORT Vectors[NVEC*2] = {
X	BIN+1, BTP,
X	BIN+1, BTP+BHT,
X	BIN+BWD, BTP+BHT,
X	BIN+BWD, BTP,
X	BIN, BTP,
X	BIN, BTP+BHT
X};
X
Xstatic struct Border STD_FileBox = {
X	0, 0, FP, BP, JAM1, NVEC, Vectors, NULL
X};
X
Xstatic struct IntuiText STD_Text[3] = {
X	{ FP, BP, JAM2, 0, 0, NULL, (UBYTE *)"Directory:", NULL },
X	{ FP, BP, JAM2, 0, 0, NULL, (UBYTE *)"File Name:", NULL },
X	{ FP, BP, JAM2, 0, 0, NULL, (UBYTE *)"Pattern:", NULL }
X};
X
Xstatic OpenFileWindow()
X{
X	extern struct IntuitionBase *IntuitionBase;
X	int i;
X
X	/* Rebuild gadget list */
X	STD_NewWindow.FirstGadget = &STD_Gadget[0];
X	for(i = 0; i < GADGETS; i++) {
X		STD_Gadget[i].NextGadget = (i==GADGETS-1)?(0):(&STD_Gadget[i+1]);
X	}
X	for(i = 0; i < STRINGS; i++) {
X		STD_String[i].BufferPos = strlen(SBuffer[i]);
X		STD_String[i].DispPos = 0;
X	}
X	STD_Prop.VertBody = 0xFFFF;
X	STD_Prop.VertPot = 0;
X
X	if(!(STD_Window = OpenWindow(&STD_NewWindow))) {
X		return 0;
X	}
X
X	/* This optional line will activate a string gadget	*/
X	if ( IntuitionBase->lib_Version > 32 )
X	{
X		ActivateGadget(G(1),STD_Window,0L);
X	}
X
X	CalcPropGadget();
X	PaintFileWindow();
X	return 1;
X}
X
Xstatic CloseFileWindow()
X{
X	STD_NewWindow.LeftEdge = STD_Window->LeftEdge;
X	STD_NewWindow.TopEdge = STD_Window->TopEdge;
X	if(STD_Window)
X		CloseWindow(STD_Window);
X}
X
Xstatic int State;
X
X#define INITIAL 0
X#define DIRECTORY 1
X
Xstatic PaintFileWindow()
X{
X	DrawBorder(STD_Window->RPort, &STD_FileBox, 0, 0);
X	PrintIText(STD_Window->RPort, &STD_Text[0], LEFTMAR, TP1);
X	PrintIText(STD_Window->RPort, &STD_Text[1], LEFTMAR, TP2);
X	PrintIText(STD_Window->RPort, &STD_Text[2], LEFTMAR, TP3);
X	if(State == DIRECTORY) PrintFileNames();
X}
X
Xstatic int FirstFile;
Xstatic int Selected;
Xstatic int NumFiles;
Xstatic struct dirent {
X	struct dirent *nextfile;
X	SHORT filetype;
X	char *filename;
X} *NameList, **NameTable;
X
X#define FILETYPE 0
X#define DIRTYPE 1
X#define VOLTYPE 2
X
Xstatic PrintFileNames()
X{
X	int i;
X
X	for(i = 0; i < MAXFILES; i++) {
X		SetBPen(STD_Window->RPort, BP);
X		SetAPen(STD_Window->RPort, BP);
X		RectFill(STD_Window->RPort,
X			HOMEX, HOMEY+i*CHSIZ,
X			LASTX, HOMEY+(i+1)*CHSIZ);
X		if(i+FirstFile < NumFiles)
X			PrintName(i+FirstFile, i+FirstFile==Selected);
X	}
X}
X
Xstatic PrintName(file, hilite)
Xint file;
Xint hilite;
X{
X	int i;
X
X	i = file - FirstFile;
X
X	Move(STD_Window->RPort, HOMEX, HOMEY+i*CHSIZ+BASELINE);
X	if(hilite == 0) {
X		SetBPen(STD_Window->RPort, BP);
X		if(NameTable[file]->filetype == FILETYPE)
X			SetAPen(STD_Window->RPort, FP);
X		else
X			SetAPen(STD_Window->RPort, 3);
X	} else {
X		SetAPen(STD_Window->RPort, BP);
X		if(NameTable[file]->filetype == FILETYPE)
X			SetBPen(STD_Window->RPort, FP);
X		else
X			SetBPen(STD_Window->RPort, 3);
X	}
X	Text(STD_Window->RPort,
X		NameTable[file]->filename,
X		strlen(NameTable[file]->filename));
X}
X
Xstatic CalcPropGadget()
X{
X	int VertPot, VertBody;
X
X	if(State == INITIAL) return;
X
X	if(NumFiles<=MAXFILES) {
X		VertBody = 0xFFFF;
X		VertPot = 0;
X		FirstFile = 0;
X	} else {
X		VertBody = ((MAXFILES<<16)-1) / NumFiles;
X		VertPot = 0;
X		FirstFile = 0;
X	}
X
X	ModifyProp(&STD_Gadget[BARID], STD_Window, NULL,
X		STD_Prop.Flags, 0, VertPot, 0, VertBody);
X}
X
Xstatic CalcFilePosition()
X{
X	int old_pos;
X
X	if(State == INITIAL) return;
X
X	old_pos = FirstFile;
X	if(NumFiles<=MAXFILES)
X		FirstFile = 0;
X	else {
X		int VertPot = STD_Prop.VertPot;
X
X		FirstFile = ((VertPot+1)*(NumFiles-MAXFILES))>>16;
X	}
X	if(old_pos != FirstFile)
X		PrintFileNames();
X}
X
XFreeList(list)
Xstruct dirent *list;
X{
X	struct dirent *ptr;
X
X	while(list) {
X		ptr = list->nextfile;
X		if(list->filename) free(list->filename);
X		free(list);
X		list = ptr;
X	}
X}
X
Xstatic ReadNewDir()
X{
X	struct dirent *NewList, **NewTable, *ptr;
X	int NewCount;
X	struct FileInfoBlock *FIB;
X	BPTR dirlock;
X
X	if(State != DIRECTORY) {
X		NameTable = 0;
X		NameList = 0;
X	}
X	if(DirName[0])
X		dirlock = Lock(DirName, ACCESS_READ);
X	else {
X		BPTR ram;
X		ram = Lock("RAM:", ACCESS_READ);
X		dirlock = CurrentDir(ram);
X		CurrentDir(dirlock);
X		UnLock(ram);
X	}
X	if(dirlock==0)
X		return 0;
X
X	/* FIB must be long word aligned, and aztec doesn't guarantee this, so: */
X	if((FIB = AllocMem(sizeof(struct FileInfoBlock), MEMF_PUBLIC)) == 0) {
X		UnLock(dirlock);
X		return 0;
X	}
X	if(!Examine(dirlock, FIB)) {
X		UnLock(dirlock);
X		FreeMem(FIB, sizeof(struct FileInfoBlock));
X		return 0;
X	}
X	if(FIB->fib_DirEntryType < 0) {
X		UnLock(dirlock);
X		FreeMem(FIB, sizeof(struct FileInfoBlock));
X		return 0;
X	}
X	NewList = 0;
X	NewCount = 0;
X	while(ExNext(dirlock, FIB)) {
X		NewCount += 1;
X		ptr = (struct dirent *)malloc(sizeof(struct dirent));
X		if(ptr==0) {
X			FreeList(NewList);
X			UnLock(dirlock);
X			FreeMem(FIB, sizeof(struct FileInfoBlock));
X			return 0;
X		}
X		ptr->nextfile = NewList;
X		ptr->filetype = (FIB->fib_DirEntryType<0)?FILETYPE:DIRTYPE;
X		ptr->filename = malloc(strlen(FIB->fib_FileName)+1);
X		if(ptr->filename == 0) {
X			FreeList(ptr);
X			UnLock(dirlock);
X			FreeMem(FIB, sizeof(struct FileInfoBlock));
X			return 0;
X		}
X		strcpy(ptr->filename, FIB->fib_FileName);
X		NewList = ptr;
X	}
X	FreeMem(FIB, sizeof(struct FileInfoBlock));
X	if(DirName[0]) {
X		UnLock(dirlock);
X	}
X	NewTable = malloc(sizeof(struct dirent *)*NewCount);
X	if(NewTable==0) {
X		FreeList(NewList);
X		return 0;
X	}
X	FreeList(NameList);
X	NameList = NewList;
X	if(NameTable) free(NameTable);
X	NameTable = NewTable;
X
X	if(PatName[0]==0)
X		SetPatName("#?");
X
X	State = DIRECTORY;
X	Selected = -1;
X
X	ReCalcPattern();
X}
X
Xstatic ReadVol()
X{
X	struct dirent *NewList, **NewTable, *ptr;
X	int NewCount;
X	char name[MAXNAME];
X
X	if(State != DIRECTORY) {
X		NameTable = 0;
X		NameList = 0;
X	}
X	OpenVolList();
X	NewList = 0;
X	NewCount = 0;
X	while(ReadVolList(name)) {
X		NewCount += 1;
X		ptr = (struct dirent *)malloc(sizeof(struct dirent));
X		if(ptr==0) {
X			FreeList(NewList);
X			return 0;
X		}
X		ptr->nextfile = NewList;
X		ptr->filetype = VOLTYPE;
X		ptr->filename = malloc(strlen(name)+1);
X		if(ptr->filename == 0) {
X			FreeList(ptr);
X			return 0;
X		}
X		strcpy(ptr->filename, name);
X		NewList = ptr;
X	}
X	CloseVolList();
X	NewTable = malloc(sizeof(struct dirent *)*NewCount);
X	if(NewTable==0) {
X		FreeList(NewList);
X		return 0;
X	}
X	FreeList(NameList);
X	NameList = NewList;
X	if(NameTable) free(NameTable);
X	NameTable = NewTable;
X
X	if(PatName[0]==0)
X		SetPatName("#?");
X
X	State = DIRECTORY;
X	Selected = -1;
X
X	ReCalcPattern();
X}
X
Xstatic WORD PatCode[128];
X
Xstatic patcomp()
X{
X	/* This is a judgement call: that no pattern should be equivalent
X	   to "#?". Perhaps it should do this invisibly, by adding a
X	   pointer to the real pattern name and making it PatName or "#?"
X	   as appropriate. */
X
X	if(!PatName[0])
X		SetPatName("#?");
X	return CmplPat(PatName, PatCode);
X}
X
Xstatic patmatch(name)
X{
X	return Match(PatName, PatCode, name);
X}
X
X/* this routine does a true dictionary search:
X *
X *		Devs < devs but Devs > devices
X */
Xstatic table_compare(p1, p2)
Xstruct dirent **p1, **p2;
X{
X	char *s1, *s2;
X	char c1, c2;
X	char firstdiff;
X
X	s1 = (*p1)->filename;
X	s2 = (*p2)->filename;
X	firstdiff = 0;
X
X	while(*s1 && *s2) {
X		c1 = *s1++;
X		c2 = *s2++;
X		if(firstdiff==0)
X			firstdiff = c1 - c2;
X		if(c1>='A' && c1<='Z') c1 = c1+'@';
X		if(c2>='A' && c2<='Z') c2 = c2+'@';
X		if(c1 != c2)
X			return c1 - c2;
X	}
X	return firstdiff;
X}
X
Xstatic sort_table()
X{
X	qsort(NameTable, NumFiles, sizeof(struct dirent *), table_compare);
X	return 1;
X}
X
Xstatic ReCalcPattern()
X{
X	if(State != DIRECTORY)
X		ReadNewDir();
X	else {
X		struct dirent *ptr;
X		patcomp();
X
X		NumFiles = 0;
X		for(ptr = NameList; ptr; ptr=ptr->nextfile) {
X			/* Directories always match. Is this good? */
X			if(ptr->filetype == DIRTYPE ||
X			   ptr->filetype == VOLTYPE ||
X			   patmatch(ptr->filename)) {
X				NameTable[NumFiles] = ptr;
X				NumFiles++;
X			}
X		}
X		sort_table();
X		CalcPropGadget();
X		Selected = -1;
X		PrintFileNames();
X	}
X}
X
Xstatic SetGadgetText(id, text)
Xint id;
Xchar *text;
X{
X	int position;
X
X	position = RemoveGadget(STD_Window, G(id));
X	if(position != -1) {
X		strcpy(SBuffer[id], text);
X		STD_String[id].BufferPos = strlen(text);
X		position = AddGadget(STD_Window, G(id), -1);
X		if(position != -1)
X			RefreshGadgets(G(id), STD_Window, NULL);
X	}
X}
X
Xstatic SetDirName(name)
Xchar *name;
X{
X	char buffer[MAXFULL+1], *ptr;
X	int index;
X	char lastchar;
X
X	/* Can't enter a file name too long. */
X	if(strlen(DirName) + strlen(name) + 1 > MAXFULL) {
X		DisplayBeep();
X		return 0;
X	}
X	index = 0;
X	lastchar = 0;
X	for(ptr = DirName; *ptr; ptr++)
X		buffer[index++] = lastchar = *ptr;
X	if(lastchar!='/' && lastchar!=':' && lastchar!=0)
X		buffer[index++] = '/';
X	strcpy(&buffer[index], name);
X	SetGadgetText(DIRID, buffer);
X	SetGadgetText(FILID, "");
X	return 1;
X}
X
Xstatic ReadParDir()
X{
X	int i;
X	int ptr;
X
X	ptr = -1;
X	for(i = 0; DirName[i]; i++)
X		if(DirName[i]==':' || DirName[i]=='/')
X			ptr = i;
X	if(ptr>=0) {
X		SetGadgetText(FILID, &DirName[ptr+1]);
X		if(ptr==0 || DirName[ptr]==':')
X			ptr++;
X		DirName[ptr] = 0;
X		SetGadgetText(DIRID, DirName);
X	} else {
X		SetGadgetText(FILID, DirName);
X		if(i)
X			SetGadgetText(DIRID, "");
X		else
X			SetGadgetText(DIRID, "/");
X	}
X	ReadNewDir();
X	return 1;
X}
X
Xstatic SetFileName(name)
Xchar *name;
X{
X	/* Can't enter a file name too long. */
X	if(strlen(DirName) + strlen(name) + 1 > MAXFULL) {
X		DisplayBeep();
X		return 0;
X	}
X	SetGadgetText(FILID, name);
X	return 1;
X}
X
Xstatic SetPatName(name)
Xchar *name;
X{
X	SetGadgetText(PATID, name);
X}
X
Xstatic ProcessGadget(id)
Xint id;
X{
X	switch(id) {
X		case DIRID: ReadNewDir(); break;
X		case FILID: DoneFlag = 1; break;
X		case PATID: ReCalcPattern(); break;
X		case BARID: CalcFilePosition(); break;
X		case YESID: DoneFlag = 1; break;
X		case CANID: DoneFlag = -1; break;
X		case VOLID: ReadVol(); break;
X		case PARID: ReadParDir(); break;
X	}
X}
X
Xstatic ProcessMouse(x, y, code, seconds, micros)
Xint x, y, code;
X{
X	int NewSelected;
X	static int oseconds = 0, omicros = 0;
X
X	if(x<HOMEX || y<HOMEY || x>=LASTX || y>=LASTY)
X		return;
X	if((code&SELECTUP) == SELECTUP)
X		return;
X	if(State != DIRECTORY) {
X		ReadNewDir();
X		return;
X	}
X	NewSelected = (y-HOMEY)/CHSIZ + FirstFile;
X	if(NewSelected == Selected) {
X		if(Selected != -1) {
X			if(DoubleClick(oseconds, omicros, seconds, micros)) {
X				if(NameTable[Selected]->filetype == DIRTYPE) {
X					if(SetDirName(NameTable[Selected]->filename))
X						ReadNewDir();
X				} else if(NameTable[Selected]->filetype == VOLTYPE) {
X					SetGadgetText(DIRID, NameTable[Selected]->filename);
X					SetGadgetText(FILID, "");
X					ReadNewDir();
X				} else {
X					if(!SetFileName(NameTable[Selected]->filename))
X						Selected = -1;
X					DoneFlag = 1;
X				}
X			}
X		}
X	} else {
X		if(Selected != -1 &&
X		   Selected>=FirstFile && Selected<FirstFile+MAXFILES)
X			PrintName(Selected, 0);
X		Selected = NewSelected;
X		if(Selected>=NumFiles)
X			Selected = -1;
X		else {
X			if(SetFileName(NameTable[Selected]->filename))
X				PrintName(Selected, 1);
X			else
X				Selected = -1;
X		}
X	}
X	oseconds = seconds;
X	omicros = micros;
X}
X
Xstdfile(title, deffile, defpat, name)
Xchar *title, *deffile, *defpat, *name;
X{
X	if(title)
X		STD_NewWindow.Title = (UBYTE *)title;
X	else
X		STD_NewWindow.Title = (UBYTE *)"Enter File Name";
X	if(deffile) {
X		int i;
X		for(i = strlen(deffile)-1; i>=0; i--) {
X			if(deffile[i]==':' || deffile[i]=='/') {
X				int hold;
X				strcpy(FileName, &deffile[i+1]);
X				if(deffile[i]==':')
X					i++;
X				hold = deffile[i];
X				deffile[i] = 0;
X				strcpy(DirName, deffile);
X				deffile[i] = hold;
X				break;
X			}
X		}
X		if(i<0) {
X			strcpy(FileName, deffile);
X			DirName[0] = 0;
X		}
X	} else {
X		DirName[0] = 0;
X		FileName[0] = 0;
X	}
X	if(defpat)
X		strcpy(PatName, defpat);
X	else
X		PatName[0] = 0;
X
X	State = INITIAL;
X	NameTable = 0;
X	NameList = 0;
X
X	if(OpenFileWindow()) {
X		struct IntuiMessage *msg;
X		DoneFlag = 0;
X		while(!DoneFlag) {
X			Wait(1<<STD_Window->UserPort->mp_SigBit);
X			while(msg = GetMsg(STD_Window->UserPort)) {
X				switch(msg->Class) {
X					case CLOSEWINDOW:
X						DoneFlag = -1;
X						break;
X					case MOUSEBUTTONS:
X						ProcessMouse(msg->MouseX, msg->MouseY,
X							msg->Code,
X							msg->Seconds, msg->Micros);
X						break;
X					case GADGETUP:
X						ProcessGadget(
X							((struct Gadget *)msg->IAddress)->GadgetID
X						);
X						break;
X					case REFRESHWINDOW:
X						BeginRefresh(STD_Window);
X						PaintFileWindow();
X						EndRefresh(STD_Window, 1);
X						break;
X				}
X				ReplyMsg(msg);
X			}
X		}
X
X		CloseFileWindow();
X	}
X	else return 0;
X
X	FreeList(NameList);
X	if(NameTable) free(NameTable);
X
X	if(DoneFlag==1) {
X		int len;
X
X		strcpy(name, DirName);
X		if(FileName[0]) {
X			if(len = strlen(name))
X				if(name[len-1]!=':')
X					strcat(name, "/");
X			strcat(name, FileName);
X			return 1;
X		}
X
X		/* Here the user has accepted the name without providing a file
X		   name. I return true, but false may be more appropriate. What
X		   do you think? */
X		return 1;
X	}
X	return 0;
X}
//END
echo x - VolList.c
sed 's/^X//' > VolList.c << '//END'
X#include <libraries/dosextens.h>
X
X#define toAPTR(b) ((b)<<2)
X#define toBPTR(a) ((a)>>2)
X
Xstruct DeviceList *list;
X
XOpenVolList()
X{
X	extern struct DosLibrary *DOSBase;
X	struct RootNode *root;
X	struct DosInfo *info;
X
X	root = DOSBase -> dl_Root;
X	info = toAPTR(root->rn_Info);
X	list = toAPTR(info->di_DevInfo);
X}
X
XReadVolList(name)
Xchar name[32];
X{
X	struct DeviceList *next;
X
X	while(list) {
X		next = toAPTR(list->dl_Next);
X		if(list->dl_Type == DLT_VOLUME) {
X			char *ptr;
X			int count;
X			ptr = toAPTR((BPTR)list->dl_Name);
X			count = *ptr++;
X			if(count > 30)
X				count = 30;
X			strncpy(name, ptr, count);
X			name[count++] = ':';
X			name[count] = 0;
X			list = next;
X			return 1;
X		}
X		list = next;
X	}
X	return 0;
X}
X
XCloseVolList()
X{
X}
//END
echo x - PatMatch.c
sed 's/^X//' > PatMatch.c << '//END'
X/* PatMatch.c - Implements AmigaDos Regular Expression Pattern Matching.
X**
X**  This program will test whether a string is an AmigaDos  regular expression
X**  It may be used to implement wild expressions such as:
X**
X**    "copy #?.c to <dir>" to copy any file ending in .c
X**
X**  The program has two entry points: CmplPat, and Match.
X**
X**    CmplPat - takes a pattern and returns an auxilliary integer vector
X**              which is used by Match.  The pattern is not modified in
X**              any way.  CmplPat returns 1 if no errors were detected
X**              while compiling the pattern; otherwise it returns 0;
X**
X**    Match   - takes the pattern, the auxilliary vector, and the string
X**              to be matched.  It returns 1 if the string matches the
X**              pattern; otherwise it returns 0;
X**
X**  Translated from BCPL by:
X**              Jeff Lydiatt
X**              Richmond B.C. Canada
X**              16 May 1986.
X**
X**  Source: "A Compact Function for Regular Expression Pattern Matching",
X**           Software - Practice and Experience, September 1979.
X**
X**  Useage:
X**     To test if "file.c" matches the regular expression "#?.c"
X**     char *Pat = "#?.c";
X**     char *Str = "file.c";
X**     WORD Aux[128];
X**
X**     if ( CmplPat( Pat, Aux ) == 0 )
X**        {
X**           printf("Bad Wildcard Expression\n");
X**           exit(1);
X**        }
X**     if ( Match( Pat, Aux, Str ) == 1 )
X**        printf("String matches the pattern\n");
X**     else
X**        printf("String does NOT match the pattern\n");
X**/
X
X/*--- Included files ----*/
X
X#include <stdio.h>
X#include <exec/types.h>
X#include <ctype.h>
X
X#define  EOS '\0'
X
X/*--- Global Variables  ---*/
X
Xstatic char     Ch;      /* The current character in Pattern */
Xstatic char     *Pat;    /* Pointer to the Pattern */
Xstatic int      *Aux;    /* Pointer to returned auxilliary vector */
Xstatic int      PatP;    /* Current position in Pat */
Xstatic int      Patlen;  /* strlen(pat) */
Xstatic BOOL     Errflag; /* TRUE if error */
Xstatic int      *Work;   /* Pointer to Active work area */
Xstatic int      Wp;      /* Current position in work */
Xstatic BOOL     Succflag;/* True if "str" matches "pat" */
X
X/*----------------------------------------------------------------*/
X/*                   The Interpreter                              */
X/*----------------------------------------------------------------*/
X
Xstatic void Put(N)
Xint N;
X{
X   register int *ip;
X   register int *to;
X
X   if ( N == 0 )
X      Succflag = TRUE;
X   else
X      {
X	for ( ip = &Work[ 1 ], to = &Work[ Wp ]; ip <= to; ip++)
X	   if ( *ip == N )
X	      return;
X	Work[ ++Wp ] = N;
X      }
X}
X
Xint Match( Pat, Aux, Str )
Xchar Pat[];
Xint  Aux[];
Xchar Str[];
X{
X   int W[ 128 ];
X   int  S = 0;
X   int  I, N, Q, P, Strlength;
X   char K;
X   int  strlen();
X   void Put();
X
X   Work = W;
X   Wp = 0;
X   Succflag = FALSE;
X   Strlength = strlen( Str );
X   Put( 1 );
X
X   if ( Aux[ 0 ] != 0 )
X      Put( Aux[ 0 ] );
X
X   for(;;)
X      {
X        /* First complete the closure */
X        for( N=1; N <= Wp; N++ )
X          {
X	     P = Work[ N ];
X	     K = Pat[ P-1 ];
X	     Q = Aux[ P ];
X	     switch( K )
X	   	{
X		  case '#': Put( P + 1 );
X		  case '%': Put( Q );
X		  default : break;
X		  case '(':
X		  case '|': Put( P + 1);
X			    if ( Q != 0 )
X			       Put( Q );
X		}
X	   }
X
X	if ( S >= Strlength )
X	   return Succflag;
X	if ( Wp == 0 )
X	   return FALSE;
X	Ch = Str[ S++ ];
X
X	/* Now deal with the match items */
X
X	N = Wp;
X	Wp = 0;
X	Succflag = FALSE;
X
X	for ( I = 1; I <= N; I++)
X	  {
X	     P = Work[ I ];
X	     K = Pat[ P - 1 ];
X	     switch( K )
X	       {
X		 case '#':
X		 case '|':
X		 case '%':
X		 case '(': break;
X		 case '\'': K = Pat[ P ];
X		 default : if ( _toupper( Ch ) != _toupper( K ) )
X			      break;
X		 case '?': /* Successful match */
X		 	   Put ( Aux[ P ] );
X		} /* End Switch */
X	  } /* End For I */
X     } /* End for(;;) */
X}
X
X
X/*----------------------------------------------------------------*/
X/*                     The Compiler                               */
X/*----------------------------------------------------------------*/
X
Xstatic void  Rch() /* Read next character from Pat */
X{
X   if ( PatP >= Patlen )
X      Ch = EOS;
X   else
X      Ch = Pat[ PatP++ ];
X}
X
Xstatic void Nextitem() /* Get next char from Pat; recognize the ' escape char */
X{
X   if ( Ch == '\'' )
X      Rch();
X   Rch();
X}
X
Xstatic void Setexits( List, Val )
Xint List;
Xint Val;
X{
X   int A;
X
X   do
X     {
X	A = Aux[ List ];
X	Aux[ List ] = Val;
X	List = A;
X     }
X	while ( List != 0 );
X}
X
Xstatic int Join( A, B )
Xint A, B;
X{
X    int T = A;
X
X    if ( A == 0 )
X	return B;
X    while ( Aux[ A ] != 0 )
X	A = Aux[ A ];
X    Aux[ A ] = B;
X    return T;
X}
X
Xstatic int Prim()      /* Parse a Prim symbol */
X{
X   int   A = PatP;
X   char Op = Ch;
X   int  Exp();
X   void Setexits(), Nextitem();
X
X   Nextitem();
X   switch( Op )
X     {
X        case EOS:
X        case ')':
X        case '|': Errflag = TRUE;
X        default : return A;
X        case '#': Setexits( Prim(), A ); return A;
X        case '(': A = Exp( A );
X		  if ( Ch != ')' )
X		    {
X			Errflag = TRUE;
X		    }
X		  Nextitem();
X		  return A;
X     }
X}
X
Xstatic int Exp( AltP )    /* Parse an expression */
Xint AltP;
X{
X   int Exits = 0;
X   int A;
X   int Prim(), Exits(), Join();
X   void Nextitem(), Setexits();
X
X   for (;;)
X	{
X	   A = Prim();
X	   if ( Ch == '|' || Ch == ')' || Ch == EOS )
X	      {
X		Exits = Join( Exits, A );
X		if ( Ch != '|' )
X		   return Exits;
X		Aux[ AltP ] = PatP;
X		AltP = PatP;
X		Nextitem();
X	      }
X	   else
X	      Setexits( A, PatP );
X	}
X}
X
Xint CmplPat( Pattern, CmplPattern)
Xchar Pattern[];
Xint  CmplPattern[];
X{
X   int i, strlen();
X   void Rch(), Setexits();
X
X   Pat = Pattern;
X   Aux = CmplPattern;
X   PatP = 0;
X   Patlen = strlen( Pat );
X   Errflag = FALSE;
X
X   for ( i = 0; i <= Patlen; i++ )
X      Aux[ i ] = 0;
X   Rch();
X   Setexits( Exp(0), 0 );
X   return (!Errflag);
X}
//END
echo x - test.c
sed 's/^X//' > test.c << '//END'
X#include <intuition/intuitionbase.h>
X#include <intuition/intuition.h>
X#include <libraries/dos.h>
X#include <exec/memory.h>
X#include <stdio.h>
X
Xstruct IntuitionBase *IntuitionBase;
Xstruct GfxBase *GfxBase;
X
X#define SECSPERDAY (60*60*24)
X#define SECSPERMIN 60
X#define TICKSPERSEC TICKS_PER_SECOND
X
X#define MAXNAME 128
X
Xchar *stdfile();
Xextern char *Copyright;
X
Xmain()
X{
X	char name[MAXNAME];
X
X	if(!(IntuitionBase = OpenLibrary("intuition.library", 1))) {
X		printf("Couldn't open intuition.library.\n");
X		exit(20);
X	}
X	if(!(GfxBase = OpenLibrary("graphics.library", 1))) {
X		printf("Couldn't open graphics.library.\n");
X		CloseLibrary(IntuitionBase);
X		exit(20);
X	}
X
X	printf("Testing STDFILE standard file requestor.\n%s\n", Copyright);
X
X	name[0] = 0;
X	while(stdfile("Display file", name, 0, name)) {
X		BPTR stdlock;
X		struct FileInfoBlock *stdfib;
X		FILE *fp;
X		long datestamp;
X		int c;
X
X		stdfib = AllocMem(sizeof(struct FileInfoBlock), MEMF_PUBLIC);
X		if(!stdfib) {
X			printf("Out of memory!\n");
X			break;
X		}
X
X		if(!(stdlock = Lock(name, ACCESS_READ))) {
X			printf("Can't obtain lock for %s\n", name);
X			FreeMem(stdfib, sizeof(struct FileInfoBlock));
X			continue;
X		}
X		if(!(Examine(stdlock, stdfib))) {
X			printf("Can't examine %s\n", name);
X			UnLock(stdlock);
X			FreeMem(stdfib, sizeof(struct FileInfoBlock));
X			continue;
X		}
X		UnLock(stdlock);
X
X		if(stdfib->fib_DirEntryType >= 0) {
X			printf("%s is a directory.\n", name);
X			FreeMem(stdfib, sizeof(struct FileInfoBlock));
X			continue;
X		}
X		if(!(fp = fopen(name, "r"))) {
X			perror(name);
X			FreeMem(stdfib, sizeof(struct FileInfoBlock));
X			continue;
X		}
X
X		datestamp = stdfib->fib_Date.ds_Days*SECSPERDAY +
X			      stdfib->fib_Date.ds_Minute*SECSPERMIN +
X				  stdfib->fib_Date.ds_Tick/TICKSPERSEC;
X		printf("\n%s, %s\n", stdfib->fib_FileName,
X			ctime(&datestamp));
X		while((c = getc(fp)) != EOF)
X			putchar(c);
X		fclose(fp);
X		FreeMem(stdfib, sizeof(struct FileInfoBlock));
X	}
X
X	CloseLibrary(IntuitionBase);
X	CloseLibrary(GfxBase);
X}
//END
exit
-- 
-- Peter da Silva `-_-' ...!seismo!soma!uhnix1!sugar!peter (I said, NO PHOTOS!)