[comp.sources.amiga] Files - a disk contents database program

ahh@j.cc.purdue.edu (Brent L. Woods) (01/05/88)

Program Name:  Files
Submitted By:  dillon%cory.Berkeley.EDU@ucbvax.berkeley.edu (Matt Dillon)
Summary:  A floppy disk file name archiving/database program.
Poster Boy:  Brent Woods  (ahh@j.cc.purdue.edu)
Tested.

NOTES:  Number one of two.  I was unable to test compile this program due
        to an inability to duplicate the compiler environment that was
        required.



Brent Woods, Co-Moderator, comp.{sources,binaries}.amiga

USENET:  ...!j.cc.purdue.edu!ahh     ARPANET:  ahh@j.cc.purdue.edu
BITNET:  PODUM@PURCCVM               PHONE:  +1 (317) 743-8421
USNAIL:  320 Brown St., #406  /  West Lafayette, IN  47906

================================================================
#	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
# shar:	Shell Archiver
#	Run the following text with /bin/sh to create:
#	filereq.c
#	files.c
#	Makefile
#	README
# This archive created: Mon Jan  4 19:56:22 1988
# By:	Brent L. Woods (Co-Moderators Unlimited.)
cat << \SHAR_EOF > filereq.c

/*
 *  STDFILE -- Standard File Requestor. Version 2.0a 15 June 1987
 *
 *  AUTHOR -- Peter da Silva	  US (713) 497-4372
 *
 *  Reorganized by Matthew Dillon for use with * and ?.  Added:
 *	-device name in File string gadget transfered to directory
 *	 gadget without closing the window.
 *	-bug when requesting volume ""... current directory lock would
 *	 get unlocked!
 *	-additional intuitive features added
 *	-coding reorganized
 *
 *	Copyright (c) 1987 Peter da Silva, all rights reserved.
 *	Changes (c)Copyright 1987 Matthew Dillon, 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];
 *
 *	+-----------------------------------+
 *	|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 <libraries/dosextens.h>
#include <exec/memory.h>

typedef unsigned char  ubyte;
typedef unsigned short uword;
typedef unsigned long  ulong;
typedef struct FileInfoBlock FIB;
typedef struct DeviceList    DEVLIST;
typedef struct DosLibrary    DOSLIB;
typedef struct DosInfo	     DOSINFO;
typedef struct RootNode      ROOTNODE;
typedef struct IntuiMessage  IMESS;

extern void CalcPropGadget();
extern void ProcessFileName();

extern void *malloc();
extern void *GetMsg();
extern struct Window *OpenWindow();

#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) (APTR)&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, (ubyte *)"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->LibNode.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
void
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
void
CalcFilePosition()
{
    short 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;
    FIB *fib;
    BPTR dirlock;

    if (State != DIRECTORY) {
	NameTable = 0;
	NameList = 0;
    }
    if (DirName[0]) {
	    dirlock = Lock(DirName, ACCESS_READ);
    } else {
	BPTR ram;
	if (ram = Lock("RAM:", ACCESS_READ)) {
	    dirlock = CurrentDir(ram);
	    CurrentDir(dirlock);
	    dirlock = DupLock(dirlock); /*  added */
	    UnLock(ram);
	}
    }
    if (!dirlock)
	return(0);
    if ((fib = (FIB *)malloc(sizeof(FIB))) == NULL) {
	UnLock(dirlock);
	return 0;
    }
    if (!Examine(dirlock, fib)) {
	UnLock(dirlock);
	free(fib);
	return 0;
    }
    if (fib->fib_DirEntryType < 0) {
	UnLock(dirlock);
	free(fib);
	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);
	    free(fib);
	    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);
	    free(fib);
	    return(0);
	}
	strcpy(ptr->filename, fib->fib_FileName);
	NewList = ptr;
    }
    free(fib);
    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();
}

/* 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;

	if (!PatName[0])
	    SetPatName("*");
	NumFiles = 0;
	for (ptr = NameList; ptr; ptr=ptr->nextfile) {
	    /* Directories always match. Is this good? */
	    if (ptr->filetype == DIRTYPE || ptr->filetype == VOLTYPE || newwildcmp(PatName, 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 = (char *)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: ProcessFileName();  break;
    case PATID: ReCalcPattern();    break;
    case BARID: CalcFilePosition(); break;
    case YESID: DoneFlag = 1;	    break;
    case CANID: DoneFlag = -1;	    break;
    case VOLID: ReadVol();          break;
    }
}

/*
 *  ProcessFileName() added by Matthew Dillon.  If the requested file is
 *  actually a directory, do a ReadNewDir() instead of quiting.
 */

void
ProcessFileName()
{
    register char *ptr;
    register short len;
    BPTR fillock;
    char buf[128];
    FIB *fib = (FIB *)malloc(sizeof(FIB));

    if (fib == NULL) {
	DoneFlag = 1;
	return;
    }
    for (ptr = (char *)FileName; *ptr; ++ptr) {
	if (*ptr == ':') {
	    DirName[0] = '\0';
	    break;
	}
    }
    strcpy(buf, DirName);
    if (FileName[0]) {
	if (len = strlen(buf)) {
	    if (buf[len-1]!=':')
		strcat(buf, "/");
	}
	strcat(buf, FileName);
	if (fillock = Lock(buf, ACCESS_READ)) {
	    if (Examine(fillock, fib)) {
		if (fib->fib_DirEntryType > 0) {
		    SetGadgetText(DIRID, buf);
		    SetGadgetText(FILID, "");
		    ReadNewDir();
		    free(fib);
		    UnLock(fillock);
		    return;
		}
	    }
	    UnLock(fillock);
	}
    }
    free(fib);
    DoneFlag = 1;
}


static
ProcessMouse(x, y, code, seconds, micros)
{
    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;
	    if (IntuitionBase->LibNode.lib_Version > 32)
		ActivateGadget(G(1),STD_Window,0L);
	}
    }
    oseconds = seconds;
    omicros = micros;
}

stdfile(title, deffile, defpat, name)
char *title, *deffile, *defpat, *name;
{
    IMESS *im;

    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())
	return(0);
    DoneFlag = 0;
    while (!DoneFlag) {
	Wait(1<<STD_Window->UserPort->mp_SigBit);
	while(im = GetMsg(STD_Window->UserPort)) {
	    switch(im->Class) {
	    case CLOSEWINDOW:
		DoneFlag = -1;
		break;
	    case MOUSEBUTTONS:
		ProcessMouse(im->MouseX, im->MouseY, im->Code, im->Seconds, im->Micros);
		break;
	    case GADGETUP:
		ProcessGadget(((struct Gadget *)im->IAddress)->GadgetID);
		break;
	    case REFRESHWINDOW:
		BeginRefresh(STD_Window);
		PaintFileWindow();
		EndRefresh(STD_Window, 1);
		break;
	    }
	    ReplyMsg(im);
	}
    }
    CloseFileWindow();
    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);
	}
    }
    return(0);
}

/*
 *  VOLLIST.C
 */

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

struct DeviceList *list;

OpenVolList()
{
    extern DOSLIB *DOSBase;
    ROOTNODE *root;
    DOSINFO  *info;

    root =   (ROOTNODE *)DOSBase->dl_Root;
    info =   (DOSINFO  *)toAPTR(root->rn_Info);
    list = (DEVLIST *)toAPTR(info->di_DevInfo);
}

ReadVolList(name)
char *name;
{
    register DEVLIST *next;

    while(list) {
	next = (DEVLIST *)toAPTR(list->dl_Next);
	if (list->dl_Type == DLT_VOLUME) {
	    char *ptr;
	    int count;
	    ptr = (char *)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()
{
}


SHAR_EOF
cat << \SHAR_EOF > files.c

/*
 *  FILES.C
 *
 *  File Manager!
 *
 *  (c)Copyright 1987 Matthew Dillon, All Rights Reserved.
 */

#include "files.h"

typedef struct FileLock LOCK;

extern LOCK *Lock();
extern LOCK *ParentDir(), *CurrentDir();
extern void *malloc();
extern void addentry();

RECORD *Rbase;		/*  All entries 	     */
RECORD *DisplayTop;	/*  Entry at the display top */
RECORD *Highlighted;	/*  Highlighted entry	     */
short  MaxNameLen;
long   NumEntries;
long   NumSelected;
long   NumTop;
long   NewNumTop;
FIB    *Fib;
short  Modified;

short  Xs, Xe, Ys, Ye, Rows, Cols;

char Title[80];

redisplay(newtop)
{
    short i;
    short noscroll = 0;
    uword percent, fill;
    RECORD *rec;

    bigboxbounds(&Xs,&Xe,&Ys,&Ye);
    Rows = (Ye-Ys)/Rp->TxHeight;
    Cols = (Xe-Xs)/Rp->TxWidth;

    if (newtop) {
	long delta = NumTop - NewNumTop;
	if (delta < 0)
	    delta = -delta;
	if (delta >= Rows)
	    noscroll = 1;
    }
    while (DisplayTop && !(DisplayTop->flags & R_SELECTED))
	DisplayTop = DisplayTop->next;
    if (newtop) {
	while (NewNumTop != NumTop && DisplayTop) {
	    if (NewNumTop < NumTop) {
		do {
		    DisplayTop = DisplayTop->prev;
		} while (DisplayTop && !(DisplayTop->flags & R_SELECTED));
		--NumTop;
		if (DisplayTop && !noscroll) {
		    ScrollRaster(Rp, 0, -Rp->TxHeight, Xs, Ys, Xe-1, Ye-1);
		    displayat(DisplayTop, 0);
		}
	    } else {
		do {
		    DisplayTop = DisplayTop->next;
		} while (DisplayTop && !(DisplayTop->flags & R_SELECTED));
		++NumTop;
		if (DisplayTop && !noscroll) {
		    ScrollRaster(Rp, 0, Rp->TxHeight, Xs, Ys, Xe-1, Ye-1);
		    displayrow(Rows-1);
		}
	    }
	}
	if (DisplayTop && !noscroll)
	    return(0);
    }
    sprintf(Title, "%ld/%ld", NumSelected, NumEntries);
    title(Title);
    if (DisplayTop == NULL) {
	DisplayTop = Rbase;
	NumTop = 0;
    }
    if (!newtop) {
	percent = fill = 0xFFFF;
	if (NumSelected) {
	    if (NumTop+(Rows>>1) > NumSelected)
		percent = 0xFFFF;
	    else
		percent = (NumTop + (Rows>>1)) * 0xFFFF / NumSelected;
	    fill = Rows * 0xFFFF / NumSelected;
	    if (Rows > NumSelected)
		fill = 0xFFFF;
	}
	setslider(percent, fill);
    }
    SetAPen(Rp, 0);
    RectFill(Rp, Xs, Ys, Xe-1, Ye-1);
    SetAPen(Rp, 1);
    for (rec = DisplayTop, i = 0; rec && i < Rows; rec = rec->next) {
	if (!(rec->flags & R_SELECTED))
	    continue;
	displayat(rec, i);
	++i;
    }
}

redisplayone(rec)
RECORD *rec;
{
    RECORD *nrec;
    short i;

    for (i = 0, nrec = DisplayTop; nrec && i < Rows; nrec = nrec->next) {
	if (!(nrec->flags & R_SELECTED))
	    continue;
	if (nrec == rec)
	    break;
	++i;
    }
    if (i < Rows && nrec == rec) {
	SetAPen(Rp, 0);
	RectFill(Rp, Xs, Ys + (Rp->TxHeight*i), Xe-1, Ys + (Rp->TxHeight*i) + Rp->TxHeight - 1);
	SetAPen(Rp, 1);
	displayat(rec, i);
    }
}

displayrow(row)
{
    register RECORD *rec = DisplayTop;
    register short i = row;

    while (i && rec) {
	rec = rec->next;
	while (rec && !(rec->flags & R_SELECTED))
	    rec = rec->next;
	--i;
    }
    if (rec && row < Rows)
	displayat(rec, row);
}

displayat(rec, i)
RECORD *rec;
{
    short len;

    len = strlen(rec->name);
    if (len > Cols)
	len = Cols;
    if (rec == Highlighted) {
	SetAPen(Rp, 0);
	SetBPen(Rp, 1);
    }
    Move(Rp, Xs, Ys + (Rp->TxHeight*i) + Rp->TxBaseline);
    Text(Rp, rec->name, len);
    if (Cols > MaxNameLen && rec->comment) {
	len = strlen(rec->comment);
	if (len > Cols - MaxNameLen)
	    len = Cols - MaxNameLen;
	Move(Rp, Xs + MaxNameLen * Rp->TxWidth, Ys + (Rp->TxHeight * i) + Rp->TxBaseline);
	Text(Rp, rec->comment, len);
    }
    if (rec == Highlighted) {
	SetAPen(Rp, 1);
	SetBPen(Rp, 0);
    }
}

bigboxhit(y, up)
{
    RECORD *hl = Highlighted;
    short row;

    Highlighted = NULL;
    if (hl)
	redisplayone(hl);
    row = (y - Ys) / Rp->TxHeight;
    if (row >= Rows || Rows < 0)
	return(0);
    for (hl = DisplayTop; hl && !(hl->flags & R_SELECTED); hl = hl->next);
    while (hl && row) {
	if (hl->flags & R_SELECTED)
	    --row;
	hl = hl->next;
    }
    for (; hl && !(hl->flags & R_SELECTED); hl = hl->next);
    if (Highlighted = hl) {
	redisplayone(hl);
	setcomment((hl->comment) ? hl->comment : "");
	if (up)
	    activate_com();
    }
}

sliderhit()
{
    uword pos, fill;

    getsliderpos(&pos, &fill);
    NewNumTop = pos * (NumSelected - Rows + 1) / 0xFFFF;
    redisplay(1);
}

/*
 *  Add a disk volume to the list.  Determine
 *  the root and add entries beginning at the
 *  specification.
 */

void
add_volume(str)
char *str;
{
    LOCK *lock;
    char path[128];
    int len;

    Highlighted = NULL;
    Fib = malloc(sizeof(FIB));
    if (Fib == NULL)
	return;
    resetsort();                /* also cleans up the database  */
    path[0] = 0;
    if (lock = Lock(str, ACCESS_READ)) {
	if (Examine(lock, Fib)) {
	    buildpath(lock, path);
	    len = strlen(path);
	    if (path[len-1] == ':' || path[len-1] == '/') {
		path[len] = '*';
		path[len+1] = 0;
		select_pattern(path, 1);
		path[len] = 0;
	    }
	    Examine(lock, Fib);
	    if (Fib->fib_DirEntryType < 0) {
		addentry(path, Fib->fib_Comment, Fib->fib_Size);
	    } else {
		RECORD *base;

		/*  find start of killpatterns, if any	*/
		for (base = Rbase; base; base = base->next) {
		    if (strcmp(base->name, KILLNAME) < 0)
			continue;
		    break;
		}
		title("Wait.. Scanning");
		scandir(lock, path, base);
	    }
	    title("Wait.. Update");
	    rem_selected(NULL,   1);
	}
	UnLock(lock);
    }
    free(Fib);
    strcat(path, "*");
    title("Select path");
    select_pattern(path, 0);
}

buildpath(lock, path)
LOCK *lock;
char *path;
{
    LOCK *parent;
    short plen, nlen;

    plen = strlen(path);
    nlen = strlen(Fib->fib_FileName) + 1;
    if (nlen == 1) {    /*  RAM: */
	strcpy(Fib->fib_FileName, "ram");
	nlen = 4;
    }
    bmov(path, path + nlen, plen + 1);
    strcpy(path, Fib->fib_FileName);
    if (Fib->fib_DirEntryType >= 0)
	path[nlen-1] = '/';
    if (parent = ParentDir(lock)) {
	if (Examine(parent, Fib)) {
	    buildpath(parent, path);
	}
	UnLock(parent);
    } else {
	path[nlen-1] = ':';
    }
}

scandir(lock, path, base)
LOCK *lock;
char *path;
RECORD *base;
{
    short restorelen = strlen(path);
    short len = restorelen;
    LOCK *olddirlock;

    if (path[restorelen-1] != ':' && path[restorelen-1] != '/') {
	strcpy(path + restorelen, "/");
	++len;
    }
    olddirlock = CurrentDir(lock);
    if (notkilled(path, base))
	addentry(path, Fib->fib_Comment, Fib->fib_Size);
    while (ExNext(lock, Fib)) {
	strcpy(path+len, Fib->fib_FileName);
	if (Fib->fib_DirEntryType < 0) {
	    if (notkilled(path, base))
		addentry(path, Fib->fib_Comment, Fib->fib_Size);
	} else {
	    LOCK *lock = Lock(Fib->fib_FileName, ACCESS_READ);
	    if (lock) {
		FIB *oldfib = Fib;
		if (Fib = malloc(sizeof(FIB))) {
		    Examine(lock, Fib);
		    scandir(lock, path, base);
		    UnLock(lock);
		    free(Fib);
		}
		Fib = oldfib;
	    }
	}
    }
    CurrentDir(olddirlock);
    path[restorelen] = 0;
}

notkilled(path,base)
char *path;
RECORD *base;
{
    while (base && strcmp(base->name, KILLNAME) == 0) {
	if (base->comment && newwildcmp(base->comment, path))
	    return(0);
	base = base->next;
    }
    return(1);
}

load_database(fi)
FILE *fi;
{
    char name[132];
    char comm[132];
    char size[32];

    fgets(name, 128, fi);   /*  # entries per item  */
    Highlighted = NULL;
    resetsort();
    title("Wait... Loading");
    while (fgets(name, 128, fi) && fgets(comm, 128, fi) && fgets(size, 32, fi)) {
	name[strlen(name)-1] = 0;   /*  remove newlines */
	comm[strlen(comm)-1] = 0;
	addentry(name, comm, atoi(size));
    }
    rem_selected(NULL, 1);
    selectall();
    redisplay(0);
}

save_database(fi)
FILE *fi;
{
    RECORD *rec;
    char buf[32];

    cleanup();
    title("Saving...");
    fputs("3\n", fi);
    for (rec = Rbase; rec; rec = rec->next) {
	fwrite(rec->name, strlen(rec->name), 1, fi);
	putc('\n', fi);
	if (rec->comment)
	    fwrite(rec->comment, strlen(rec->comment), 1, fi);
	putc('\n', fi);
	sprintf(buf, "%ld\n", rec->bytes);
	fputs(buf, fi);
	if (ferror(fi))
	    break;
    }
}

selectall()
{
    RECORD *rec;
    short len;

    Highlighted = NULL;
    DisplayTop = NULL;
    MaxNameLen = 0;
    for (rec = Rbase; rec; rec = rec->next) {
	if (rec->flags & R_KILLPAT)
	    continue;
	len = strlen(rec->name);
	if (MaxNameLen <= len)
	    MaxNameLen = len + 1;
	rec->flags |= R_SELECTED;
    }
    NumSelected = NumEntries;
}

select_pattern(str, noref)
char *str;
{
    register RECORD *rec;
    register short len;
    short which = 0;

    if (*str == '+')    /*  ADD selected patterns   */
	++str, which = 1;
    if (*str == '-')    /*  REMOVE selected patterns*/
	++str, which = 2;
    DisplayTop = NULL;
    Highlighted= NULL;

    switch(which) {
    case 0:
	NumSelected = 0;
	MaxNameLen = 0;
	for (rec = Rbase; rec; rec = rec->next) {
	    rec->flags &= ~R_SELECTED;
	    if (rec->flags & R_KILLPAT)
		continue;
	    if (newwildcmp(str, rec->name) || (rec->comment && newwildcmp(str, rec->comment))) {
		if (noref) {
		    rec->flags |= R_UPDATE;
		} else {
		    rec->flags |= R_SELECTED;
		    ++NumSelected;
		    if ((len = strlen(rec->name)) >= MaxNameLen)
			MaxNameLen = len + 1;
		}
	    }
	}
	break;
    case 1:
	for (rec = Rbase; rec; rec = rec->next) {
	    if ((rec->flags & R_KILLPAT) || (rec->flags & R_SELECTED))
		continue;
	    if (newwildcmp(str, rec->name) || (rec->comment && newwildcmp(str, rec->comment))) {
		rec->flags |= R_SELECTED;
		++NumSelected;
		if ((len = strlen(rec->name)) >= MaxNameLen)
		    MaxNameLen = len + 1;
	    }
	}
	break;
    case 2:
	for (rec = Rbase; rec; rec = rec->next) {
	    if (!(rec->flags & R_SELECTED))
		continue;
	    if (newwildcmp(str, rec->name) || (rec->comment && newwildcmp(str, rec->comment))) {
		rec->flags &= ~R_SELECTED;
		--NumSelected;
	    }
	}
	break;
    }
    if (!noref)
	redisplay(0);
}

/*
 *  If onerec != NULL, remove the one record,
 *  else remove all SELECTED records.
 */

rem_selected(onerec, noref)
RECORD *onerec;
{
    register RECORD *rec;
    register long len, maxlen;

    Highlighted = NULL;
    cleanup();
    if (onerec) {
	if (onerec->flags & R_SELECTED) {
	    onerec->flags &= ~R_SELECTED;
	    --NumSelected;
	}
	onerec->flags |= R_KILLPAT;
	--NumEntries;
    } else {
	maxlen = 0;
	for (rec = Rbase; rec; rec = rec->next) {
	    if (noref) {
		if (rec->flags & R_UPDATE) {
		    rec->flags &= ~R_UPDATE;
		    rec->flags |= R_KILLPAT;
		    --NumEntries;
		}
	    } else {
		if (rec->flags & R_SELECTED) {
		    rec->flags &= ~R_SELECTED;
		    rec->flags |= R_KILLPAT;
		   --NumEntries;
		}
	    }
	    if (!(rec->flags & R_KILLPAT) && (len=strlen(rec->name)) > maxlen)
		maxlen = len;
	}
	if (!noref)
	    NumSelected = 0;
	MaxNameLen = maxlen+1;
    }
    if (noref)
	cleanup();
    else
	redisplay(0);
}

undo()
{
    RECORD *rec;

    Highlighted = NULL;
    for (rec = Rbase; rec; rec = rec->next) {
	if (rec->flags & R_KILLPAT) {
	    rec->flags &= ~R_KILLPAT;
	    rec->flags |= R_SELECTED;
	    ++NumSelected;
	    ++NumEntries;
	    if (strlen(rec->name) >= MaxNameLen)
		MaxNameLen = strlen(rec->name)+1;
	}
    }
    redisplay(0);
}

cleanup()
{
    RECORD *rec, *nrec;
    for (rec = Rbase; rec; rec = nrec) {
	nrec = rec->next;
	if (rec->flags & R_KILLPAT) {
	    if (rec == DisplayTop)
		DisplayTop = nrec;
	    rmrecord(rec);
	}
    }
}

rmrecord(rec)
RECORD *rec;
{
    if (rec->flags & R_SOFTERR) {
	puts("panic: soft error");
	exit(1);
    }
    if (rec->prev)
	rec->prev->next = rec->next;
    else
	Rbase = rec->next;
    if (rec->next)
	rec->next->prev = rec->prev;
    rec->flags |= R_SOFTERR;
    freestr(rec->name);
    freestr(rec->comment);
    freerecord(rec);
}

/*
 *  modify the comment field for the highlighted
 *  item.
 */

mod_comment(str)
char *str;
{
    if (Highlighted) {
	Modified = 1;
	freestr(Highlighted->comment);
	if (str[0]) {
	    Highlighted->comment = allocstr(str);
	    if (Highlighted->comment == NULL)
		title("OUT OF MEMORY!");
	} else {
	    Highlighted->comment = NULL;
	}
	redisplayone(Highlighted);
    }
}


static RECORD *Cache;

resetsort()
{
    cleanup();
    Cache = Rbase;
}

void
addentry(name, comm, size)
char *name;
char *comm;
long size;
{
    RECORD *rec;
    short n;

    Modified = 1;
    rec = allocrecord();
    if (rec == NULL) {
	title("OUT OF MEMORY!");
	return;
    }
    rec->name = allocstr(name);
    if (rec->name == NULL) {
	rmrecord(rec);
	title("OUT OF MEMORY!");
	return;
    }
    rec->comment = NULL;
    if (strlen(comm)) {
	rec->comment = allocstr(comm);
	if (rec->comment == NULL) {
	    freestr(rec->name);
	    rmrecord(rec);
	    title("OUT OF MEMORY!");
	    return;
	}
    }
    rec->bytes = size;

    if (Rbase == NULL) {
	Rbase = rec;
	rec->prev = NULL;
	rec->next = NULL;
    } else {
	short n = strcmp(name, Cache->name);
	if (n < 0) {                    /*  name < Cache, move backwards */
	    while ((Cache = Cache->prev) && (n=strcmp(name, Cache->name)) < 0);
	} else
	if (n > 0) {                    /*  name > Cache, move forwards  */
	    while (Cache->next && (n=strcmp(name, Cache->next->name)) > 0)
		Cache = Cache->next;
	    if (Cache->next && n == 0)
		Cache = Cache->next;
	}
	if (Cache) {
	    rec->next = Cache->next;	/*  insert after cache */
	    rec->prev = Cache;
	    Cache->next = rec;
	} else {			/*  or at beginning    */
	    rec->next = Rbase;
	    rec->prev = NULL;
	    Rbase = rec;
	}
	if (rec->next)
	    rec->next->prev = rec;
	if (n == 0) {                   /*  replace if exact   */
	    if (Cache->comment) {
		char *swap = Cache->comment;
		Cache->comment = rec->comment;
		Cache->flags |= R_UPDATE;
		rec->comment = swap;
	    }
	}
    }
    rec->flags = R_SELECTED;
    ++NumSelected;
    ++NumEntries;
    if (MaxNameLen <= strlen(rec->name))
	MaxNameLen = strlen(rec->name) + 1;
    Cache = rec;
}

SHAR_EOF
cat << \SHAR_EOF > Makefile

SRCS= gadget.c main.c menu.c files.c memory.c newwildcmp.c filereq.c
OBJS= gadget.o main.o menu.o files.o memory.o newwildcmp.o filereq.o
CFLAGS= +L +Ivd0:include/symbols.m

all: $(OBJS)
    ln +Q $(OBJS) -lsup32 -lc32 -o files

SHAR_EOF
cat << \SHAR_EOF > README


COMPILER NOTES:

    Be sure that you have working NewModifyProp() and AddGList() calls.
    Aztec 3.4a screwed up the library call sequence for these.

    The source requires my support library SUP32.LIB to compile, as well
    as a precompiled symbol table with all amiga includes (*/*.h), but not
    high level includes (*.h ... stdio, etc...).


SHAR_EOF
#	End of shell archive
exit 0

ahh@j.cc.purdue.edu (Brent L. Woods) (01/05/88)

Program Name:  Files
Submitted By:  dillon%cory.Berkeley.EDU@ucbvax.berkeley.edu (Matt Dillon)
Summary:  A floppy disk file name archiving/database program.
Poster Boy:  Brent Woods  (ahh@j.cc.purdue.edu)
Tested.

NOTES:  Number two of two.  I was unable to compile this program due
        to difficulties in duplicating the compiler environment
        that was needed (detailed in one of the files of the posting).



Brent Woods, Co-Moderator, comp.{sources,binaries}.amiga

USENET:  ...!j.cc.purdue.edu!ahh     ARPANET:  ahh@j.cc.purdue.edu
BITNET:  PODUM@PURCCVM               PHONE:  +1 (317) 743-8421
USNAIL:  320 Brown St., #406  /  West Lafayette, IN  47906

================================================================
#	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
# shar:	Shell Archiver
#	Run the following text with /bin/sh to create:
#	files.doc
#	files.h
#	gadget.c
#	head
#	main.c
#	memory.c
#	menu.c
#	newwildcmp.c
# This archive created: Mon Jan  4 19:57:24 1988
# By:	Brent L. Woods (Co-Moderators Unlimited.)
cat << \SHAR_EOF > files.doc


			   FILE CATALOG UTILITY

By Matthew Dillon.

	dillon@ucbvax.berkeley.edu	(ARPANET)
	..!ihnp4!ucbvax!dillon		(USENET)

	Matthew Dillon
	891 Regal Rd.
	Berkeley, California 94708

	USA


WINDOW TITLE:
    Holds operational status.  Normally holds two numbers SELECTED/ENTRIES
    Where SELECTED is the number of files currently selected for viewing.
    The ALL gadget selects all entries for viewing.

PATTERN:
    Entering a wildcard pattern here selects for viewing those entries
    (file name or comment) which matches the pattern.  "+pattern" adds
    entries to the currently selected list, and "-pattern" removes entries
    from the currently selected list. "pattern" alone is equivalent to
    "-*" "+pattern".  Note that if looking for a specific program you need
    to specify it like:  "*viacom.arc" with the "*" in front to handle any
    prefix to the file.

    '*' and '?' are acceptable wildcards.  Any combination is allowed.  The
    search is case independant.

VOLUME:
    Entering a disk volume name (e.g. "df1:") or directory path
    (e.g. "df1:src/files") causes that volume/directory to be searched
    and all files to be added to the database.	The FULL path name is
    determined, including the volume name, independant of the original
    specification.  This means you can say "df1:" instead of "mydisk:" and
    entries in the database will begin "mydisk:blah..".

    When I say 'volume' in the following description I mean either a
    directory or volume.

    This also serves to UPDATE entries in the database.  Specifically, if
    the volume already exists in the database new files are added and files
    which no longer exist (in the volume) are removed.  Any comments which
    you have added to the database are KEPT.  Note, however, that if a
    file contains a comment field and the database entry also has a comment
    field the database entry's comment field is used.

    Example:  To add a whole lot of floppies simply specify DF0:, hit
    return, then place new floppies in DF0: and hit return without having
    to retype DF0: or even reselect the string gadget.

    NOTE!!  Each disk should have a unique volume name or it will exclude
    other disks!  Now that isn't too much to ask for, is it?

COMMENT:
    After selecting a file with the left mouse button you may enter a
    comment for it.  The comment field is automatically activated after
    selection and any previous comment loaded into the field.

SLIDE BAR:
    The slide bar is used to move around in the database.  Normally the
    database is too large for much resolution on the slide bar (mine is
    3821 entries at the moment), in which case one normally selects a
    subsection of the database with the PATTERN field before moving around.

    The display area places the comment after the longest file path in
    the currently selected subset, so you might have to make the window
    wider to see the comments.

INFO:
    Program info.  My name in lights!

DEL:
    Delete the highlighted item or the currently selected subsection of
    the database if there is no highlighted item.  YOU CAN DELETE THE
    ENTIRE DATABASE THIS WAY!! BUT...

UNDO:
    Undo the last delete operation.  For best results hit immediately
    after a DEL.

ALL:
    Selects the entire database for viewing.

----------------------------------------------------------------------------
				MENUS

    SAVE	save database under previously loaded name
    SAVEAS	save database under a new name
    LOADDEF	load the default database (s:catalog.db or previously
		 loaded file).	Information is appended to the memory
		 image.
    LOAD	specify a filename to load as the database.  Information
		 is appended as in LOADDEF
    QUIT	Quit the program (for those who do not know how to use
		the close gadget)
    ADDKILLPAT	Create a kill entry in the database (@@KILLPAT).  The
		comment field of this entry is a pattern.  If the pattern
		matches an about-to-be-loaded path the path is not loaded.
		Any number of kill entries may exist in the database.

		Example:    set the comment to *.o, and no object files
			    will be added from new volumes.


    The filerequester code was provided by Peter Da Silva and hacked
    moderately.  All other code is my own.

----------------------------------------------------------------------------
			    LIMITATIONS

    -Volume Names may not begin with @@
    -Volume Names beginning with '+' and '-' are o.k., but difficult to
     specify in the PATTERN field due to the special meaning of '+' & '-'.
    -Full File Paths and comments must be smaller than 128 characters each
    -There is no limitation to the database size except for the amount of
     memory you have.


----------------------------------------------------------------------------
			    FILE FORMAT

The database is an ascii file.	The first line is a number (3) specifying
the number of lines per entry.	The next line starts the first entry, with
3 lines per entry (and no extra lines at the end of the database).  No tabs
are allowed.  The initial number is used for upward compatibility when/if
I add more information to the database.  The three lines are:

    1	-file name
    2	-file comment
    3	-file size (bytes)

Future additions will also include:
    4	-date






SHAR_EOF
cat << \SHAR_EOF > files.h

/*
 *  FILES.H
 *
 */

#include <stdio.h>
#include <typedefs.h>

#define GAD_INFO    1
#define GAD_DEL     2
#define GAD_UNDO    3
#define GAD_ALL     4

#define GAD_VOLUME  8
#define GAD_PATTERN 9
#define GAD_COMMENT 10
#define GAD_SLIDER  11
#define GAD_BIGBOX  12

#define MEN_SAVE    1
#define MEN_SAVEAS  2
#define MEN_LOAD    3
#define MEN_LOADEF  4
#define MEN_QUIT    5
#define MEN_KILLPAT 6

#define title(str)  SetWindowTitles(Win,(str),NULL)
#define KILLNAME    "@@KILLPAT"

#define RECORD	struct _RECORD

typedef unsigned long	ulong;
typedef unsigned short	uword;
typedef unsigned char	ubyte;

/*
 *  Each record is stored in the following structure.
 */

#define R_SELECTED  0x01    /*	A selected item 			    */
#define R_KILLPAT   0x02    /*	deleted item (undoable)                     */
#define R_UPDATE    0x04    /*	flag delete (used for updating a volume)    */
#define R_SOFTERR   0x08    /*	software error if ever encountered!	    */

RECORD {
    RECORD  *next;	/*  record list, doubly linked	    */
    RECORD  *prev;
    char    *comment;	/*  comment	     */
    char    *name;	/*  entire file name */
    long    bytes;	/*  file size	     */
    short   flags;
};

extern WIN *OpenWindow();
extern void *malloc();
extern void *GetMsg();
extern void *AllocMem();

extern RECORD *allocrecord();
extern char   *allocstr();

extern RECORD *Rbase;
extern RECORD *Highlighted;
extern WIN    *Win;
extern RP     *Rp;
extern long   NumEntries;
extern short  Modified;


SHAR_EOF
cat << \SHAR_EOF > gadget.c

/*
 *  GADGET.C
 *
 *  (c)Copyright 1987 Matthew Dillon, All Rights Reserved.
 *
 */

#include "files.h"

#define NI 0

static BORDER  Bdr[10];

static ITEXT   BText[] = {
    { 3,0,JAM2,1,0,NULL,(ubyte *)"info"},
    { 3,0,JAM2,1,0,NULL,(ubyte *)"del" },
    { 3,0,JAM2,1,0,NULL,(ubyte *)"undo"},
    { 3,0,JAM2,1,0,NULL,(ubyte *)"all" }
};

static ITEXT   SText[] = {
    { 3,0,JAM2,NI,-2,NULL,(ubyte *)"Volume:  " },
    { 3,0,JAM2,NI,-2,NULL,(ubyte *)"Pattern: " },
    { 3,0,JAM2,NI,-2,NULL,(ubyte *)"Comment: " }
};

ubyte Buf1_vol[128]; ubyte Buf2_vol[128];
ubyte Buf1_pat[128]; ubyte Buf2_pat[128];
ubyte Buf1_com[128]; ubyte Buf2_com[128];

STRINGINFO  Si_vol = { Buf1_vol,Buf2_vol,0,sizeof(Buf1_vol),0,0,0,0,0,0 };
STRINGINFO  Si_pat = { Buf1_pat,Buf2_pat,0,sizeof(Buf2_pat),0,0,0,0,0,0 };
STRINGINFO  Si_com = { Buf1_com,Buf2_com,0,sizeof(Buf2_com),0,0,0,0,0,0 };

GADGET	Gad_vol = { NULL	,  NI,	0, NI, NI, GADGHCOMP|GRELWIDTH,GADGIMMEDIATE|RELVERIFY,STRGADGET,NI,NULL,&SText[0],NULL,(APTR)&Si_vol };
GADGET	Gad_pat = { &Gad_vol	,  NI,	1, NI, NI, GADGHCOMP|GRELWIDTH,GADGIMMEDIATE|RELVERIFY,STRGADGET,NI,NULL,&SText[1],NULL,(APTR)&Si_pat };
GADGET	Gad_com = { &Gad_pat	,  NI,	2, NI, NI, GADGHCOMP|GRELWIDTH,GADGIMMEDIATE|RELVERIFY,STRGADGET,NI,NULL,&SText[2],NULL,(APTR)&Si_com };

GADGET	Gad_info= { &Gad_com	,  5 ,-15, NI, NI, GADGHCOMP|GRELBOTTOM,GADGIMMEDIATE|RELVERIFY,BOOLGADGET,NI,NULL,&BText[0] };
GADGET	Gad_del = { &Gad_info	,  50,-15, NI, NI, GADGHCOMP|GRELBOTTOM,GADGIMMEDIATE|RELVERIFY,BOOLGADGET,NI,NULL,&BText[1] };
GADGET	Gad_undo= { &Gad_del	,-110,-15, NI, NI,GADGHCOMP|GRELRIGHT|GRELBOTTOM,GADGIMMEDIATE|RELVERIFY,BOOLGADGET,NI,NULL,&BText[2] };
GADGET	Gad_all = { &Gad_undo	, -50,-15, NI, NI, GADGHCOMP|GRELRIGHT|GRELBOTTOM,GADGIMMEDIATE|RELVERIFY,BOOLGADGET,NI,NULL,&BText[3] };

GADGET	*FirstGadget = &Gad_all;

GADGET	Gad_box = { NULL	,  NI, NI, NI, NI, GADGHNONE|GRELWIDTH|GRELHEIGHT,GADGIMMEDIATE|RELVERIFY|FOLLOWMOUSE,BOOLGADGET,NULL,NULL,NULL };

PROPINFO Prop_slid = { AUTOKNOB|FREEVERT, 0, 0, 0x30, 0x30 };
IMAGE	 Imag_slid = { 0,0,2,1,1,NULL,1,0,NULL };

GADGET	 Gad_slid  = { NULL,  -20, 12, 20, -30, GADGHCOMP|GRELHEIGHT|GRELRIGHT,GADGIMMEDIATE|RELVERIFY|RIGHTBORDER|FOLLOWMOUSE,PROPGADGET,(APTR)&Imag_slid,NULL,NULL,0,(APTR)&Prop_slid };

initslider(nw)
NW *nw;
{
    nw->FirstGadget = &Gad_slid;
}

initstructs()
{
    register short i;
    register GADGET *gad;

    for (i = 0; i < sizeof(Bdr)/sizeof(Bdr[0]); ++i) {
	register BORDER *b = &Bdr[i];
	register short	*xy= malloc(5 * 2 * sizeof(short));
	/*b->LeftEdge = b->TopEdge = -2;*/
	b->FrontPen = 1;
	b->DrawMode = JAM2;
	b->Count = 5;
	b->XY = xy;
	bzero(xy, 5 * 2 * sizeof(short));
	xy[2] = i * Win->RPort->TxWidth + 4;
	xy[4] = xy[2];
	xy[5] = Win->RPort->TxHeight + 2;
	xy[7] = Win->RPort->TxHeight + 2;
    }
    for (i = 0; i < sizeof(SText)/sizeof(SText[0]); ++i) {
	register ITEXT *it = &SText[i];
	it->LeftEdge = -strlen(it->IText) * Win->RPort->TxWidth + 2;
	it->TopEdge += 2;
    }
    for (i = 0; i < sizeof(BText)/sizeof(BText[0]); ++i) {
	register ITEXT *it = &BText[i];
	it->LeftEdge+= 2;
	it->TopEdge += 2;
    }
    for (gad = FirstGadget; gad; gad = gad->NextGadget) {
	register ubyte *str = gad->GadgetText->IText;
	gad->Height = Win->RPort->TxHeight + 3;
	gad->Width  = strlen(str) * Win->RPort->TxWidth + 5;
	if (gad->GadgetType == STRGADGET) {
	    gad->LeftEdge = Win->BorderLeft + strlen(str) * Win->RPort->TxWidth + 2;
	    gad->Width	  = -gad->LeftEdge - Win->BorderRight;
	    gad->TopEdge = gad->TopEdge * (Win->RPort->TxHeight + 2) + Win->BorderTop;
	    gad->GadgetText->LeftEdge = -strlen(str) * Win->RPort->TxWidth;
	} else {
	    gad->GadgetRender = (APTR)&Bdr[strlen(str)];
	}
    }
    Gad_box.LeftEdge= Win->BorderLeft;
    Gad_box.TopEdge = Gad_com.TopEdge + Win->RPort->TxHeight + 2;
    Gad_box.Width   = -Gad_box.LeftEdge - Win->BorderRight;
    Gad_box.Height  = -Gad_box.TopEdge	+ Gad_undo.TopEdge - 2;
}

addgadgets()
{
    register GADGET *gad, *ngad;
    int i = 0;

    initstructs(Win);
    for (gad = FirstGadget; gad; gad = ngad) {
	ngad = gad->NextGadget;
	AddGadget(Win, gad, i);
	++i;
    }
    AddGadget(Win, &Gad_box, i);
    RefreshGList(FirstGadget, Win, NULL, -1);
}

remgadgets()
{

}

fixgadgets()
{

}

getgadget(im, sel, str)
IMESS *im;
short *sel;
ubyte **str;
{
    GADGET *gad = (GADGET *)im->IAddress;

    *sel = gad->Flags & SELECTED;

    if (gad == &Gad_info) return(GAD_INFO);
    if (gad == &Gad_del)  return(GAD_DEL);
    if (gad == &Gad_undo) return(GAD_UNDO);
    if (gad == &Gad_all)  return(GAD_ALL);
    if (gad == &Gad_slid) return(GAD_SLIDER);
    if (gad == &Gad_box)  return(GAD_BIGBOX);

    if (gad == &Gad_vol || gad == &Gad_pat || gad == &Gad_com) {
	*str = ((STRINGINFO *)gad->SpecialInfo)->Buffer;
	if (gad == &Gad_vol) return(GAD_VOLUME);
	if (gad == &Gad_pat) return(GAD_PATTERN);
	if (gad == &Gad_com) return(GAD_COMMENT);
    }
    return(0);
}

bigboxbounds(xs,xe,ys,ye)
short *xs,*xe,*ys,*ye;
{
    *xs = Gad_box.LeftEdge;
    *xe = *xs + Gad_box.Width + Win->Width;
    *ys = Gad_box.TopEdge;
    *ye = *ys + Gad_box.Height+ Win->Height;
    if (*xe <= *xs)
	*xe = *xs + 1;
    if (*ye <= *ys)
	*ye = *ys + 1;
}

getsliderpos(pot, full)
short *pot, *full;
{
    *pot = Prop_slid.VertPot;
    *full= Prop_slid.VertBody;
}

getboxpos(im)
IMESS *im;
{
    return(im->MouseY - Gad_box.TopEdge);
}

setslider(pos, fatness)
{
    NewModifyProp(&Gad_slid, Win, NULL, AUTOKNOB|FREEVERT,0,pos,0,fatness,1);
}

setcomment(str)
char *str;
{
    strcpy(Buf1_com, str);
    RefreshGList(&Gad_com, Win, NULL, 1);
}

activate_vol()
{
    ActivateGadget(&Gad_vol, Win, NULL);
}

activate_pat()
{
    ActivateGadget(&Gad_pat, Win, NULL);
}

activate_com()
{
    ActivateGadget(&Gad_com, Win, NULL);
}

SHAR_EOF
cat << \SHAR_EOF > head
Approved: ahh@j.cc.purdue.edu  (Brent Woods)

Program Name:  Files
Submitted By:  dillon%cory.Berkeley.EDU@ucbvax.berkeley.edu (Matt Dillon)
Summary:  A floppy disk file name archiving/database program.
Poster Boy:  Brent Woods  (ahh@j.cc.purdue.edu)
Tested.

NOTES:



Brent Woods, Co-Moderator, comp.{sources,binaries}.amiga

USENET:  ...!j.cc.purdue.edu!ahh     ARPANET:  ahh@j.cc.purdue.edu
BITNET:  PODUM@PURCCVM               PHONE:  +1 (317) 743-8421
USNAIL:  320 Brown St., #406  /  West Lafayette, IN  47906

================================================================
SHAR_EOF
cat << \SHAR_EOF > main.c

/*
 *  MAIN.C
 *
 *  FILES
 *
 *  A file catalog program by Matthew Dillon.
 *
 *  (c)Copyright 1987 Matthew Dillon, All Rights Reserved.
 *
 */

#include "files.h"
#include <xmisc.h>

NW Nw = {
    30, 30, 500, 150, -1, -1,
    NEWSIZE|MOUSEBUTTONS|MOUSEMOVE|GADGETDOWN|GADGETUP|MENUPICK|CLOSEWINDOW,
    WINDOWSIZING|WINDOWDRAG|WINDOWDEPTH|WINDOWCLOSE|NOCAREREFRESH|ACTIVATE,
    NULL, NULL, (ubyte *)"V1.1 by Matthew Dillon, Catalog Utility.", NULL,
    NULL, 182, 66, -1, -1, WBENCHSCREEN
};

WIN *Win;
RP  *Rp;

char FileName[128] = { "s:catalog.db" };

main()
{
    char resized = 0;
    char notdone = 1;
    char movebox = 0;
    char moveslider = 0;
    char addactive = 0;
    char noload = 1;
    char dontquit = 0;

    char mmove = 0;
    short mmy;

    IMESS *im;

    openlibs(INTUITION_LIB|GRAPHICS_LIB);
    initslider(&Nw);
    Win = OpenWindow(&Nw);
    failfalse(Win, "Unable to open window");
    Rp = Win->RPort;

    addmenus();
    addgadgets();
again:
    for (; notdone || dontquit;) {
	if (dontquit)
	    notdone = 1;
	dontquit = 0;
	WaitPort(Win->UserPort);
	while (im = GetMsg(Win->UserPort)) {
	    switch(im->Class) {
	    case NEWSIZE:
		resized = 1;
		break;
	    case MOUSEBUTTONS:
		break;
	    case MOUSEMOVE:
		mmove = 1;
		mmy = im->MouseY;
		break;
	    case GADGETDOWN:
		{
		    char *str = NULL;
		    short sel;
		    switch(getgadget(im, &sel, &str)) {
		    case GAD_SLIDER:
			moveslider = 1;
			movebox = 0;
			sliderhit();
			break;
		    case GAD_BIGBOX:
			moveslider = 0;
			movebox = 1;
			bigboxhit(im->MouseY, 0);
			break;
		    }
		}
		break;
	    case GADGETUP:
		{
		    char *str = NULL;
		    short sel;
		    switch(getgadget(im, &sel, &str)) {
		    case GAD_INFO:
			{
			    long fh = Open("raw:10/24/600/150/INFO", 1006);
			    if (fh) {
				char buf[8];
				fhprintf(fh, "\n\r\n\rVersion 1.1  Written by Matthew Dillon\n\r");
				fhprintf(fh, "(c)Copyright 1987 Matthew Dillon, All Rights Reserved.\n\n\r");
				fhprintf(fh, "    dillon@ucbvax.berkeley.edu     (ARPANET)\n\r");
				fhprintf(fh, "    ...!ihnp4!ucbvax!dillon        (USENET)\n\r");
				fhprintf(fh, "Matthew Dillon, 891 Regal Rd. Berkeley, Ca. 94708\n\n\r");
				fhprintf(fh, "This software is freely redistributable only.  This\n\r");
				fhprintf(fh, "software is NOT shareware\n\n\r");
				fhprintf(fh, "Thanks to Peter Da Silva for providing the filerequestor\n\r");
				fhprintf(fh, "\n\n    Any key to continue\n\r");
				Read(fh, buf, 1);
				Close(fh);
			    }
			}
			break;
		    case GAD_DEL:
			rem_selected(Highlighted, 0);
			break;
		    case GAD_UNDO:
			undo();
			break;
		    case GAD_ALL:
			selectall();
			redisplay(0);
			break;
		    case GAD_VOLUME:
			if (str[0])
			    add_volume(str);
			activate_vol();
			break;
		    case GAD_PATTERN:
			if (str[0]) {
			    title("Wait");
			    select_pattern(str, 0);
			}
			activate_pat();
			break;
		    case GAD_COMMENT:
			mod_comment(str);
			break;
		    case GAD_SLIDER:
			moveslider = 0;
			sliderhit();
			break;
		    case GAD_BIGBOX:
			movebox = 0;
			bigboxhit(im->MouseY, 1);
			break;
		    }
		}
		break;
	    case MENUPICK:
		switch(getmenu(im)) {
		case MEN_SAVE:
		    {
			FILE *fi;
			if (noload && NumEntries == 0) {
			    title("Did I just save you from making a mistake?");
			    break;
			}
			fi = fopen(FileName, "w");
			if (fi) {
			    save_database(fi);
			    if (ferror(fi)) {
				title("File Error");
				dontquit = 1;
			    } else {
				title("Saved ok");
				Modified = 0;
			    }
			    fclose(fi);
			} else {
			    title("Unable to open file for write");
			}
		    }
		    break;
		case MEN_SAVEAS:
		    {
			FILE *fi;
			if (stdfile("SAVE DATABASE", FileName, "", FileName)) {
			    noload = 0;
			    if (fi = fopen(FileName, "w")) {
				save_database(fi);
				if (ferror(fi)) {
				    title("File Error");
				    dontquit = 1;
				} else {
				    title("Saved ok");
				    Modified = 0;
				}
				fclose(fi);
			    } else {
				title("Unable to open file for write");
			    }
			}
		    }
		    break;
		case MEN_LOADEF:
		    {
			FILE *fi = fopen(FileName, "r");
			long entries = NumEntries;
			if (fi) {
			    noload = 0;
			    load_database(fi);
			    if (!entries)
				Modified = 0;
			    fclose(fi);
			} else {
			    title("Unable to open file for read");
			}
		    }
		    break;
		case MEN_LOAD:
		    {
			FILE *fi;
			long entries = NumEntries;
			if (stdfile("LOAD DATABASE", FileName, "", FileName)) {
			    noload = 0;
			    if (fi = fopen(FileName, "r")) {
				load_database(fi);
				if (!entries)
				    Modified = 0;
				fclose(fi);
			    } else {
				title("Unable to open file for read");
			    }
			}
		    }
		    break;
		case MEN_QUIT:
		    notdone = 0;
		    break;
		case MEN_KILLPAT:
		    resetsort();
		    addentry(KILLNAME, "", 0);
		    select_pattern(KILLNAME, 0);
		    break;
		}
		break;
	    case CLOSEWINDOW:
		notdone = 0;
		break;
	    }
	    ReplyMsg(im);
	    if (resized) {
		fixgadgets();
		redisplay(0);
	    }
	    resized = 0;
	}
	if (mmove) {
	    if (moveslider)
		sliderhit();
	    if (movebox)
		bigboxhit(mmy, 0);
	    mmove = 0;
	}
    }
    if (Modified) {
	title("MODIFICATIONS MADE!  HIT CLOSE-BOX AGAIN TO VERIFY");
	Modified = 0;
	notdone = 1;
	goto again;
    }
    failfalse(NULL, NULL);
}

failfalse(val, str)
long val;
char *str;
{
    if (val == NULL) {
	if (str)
	    puts(str);
	if (Win) {
	    remmenus();
	    remgadgets();
	    CloseWindow(Win);
	    Win = NULL;
	}
	closelibs(-1);
	exit((str)? 1 : 0);
    }
}

failtrue(val, str)
long val;
char *str;
{
    if (val)
	failfalse(NULL, str);
}

SHAR_EOF
cat << \SHAR_EOF > memory.c

/*
 *  MEMORY.C
 *
 *  (c)Copyright 1987 Matthew Dillon, All Rights Reserved.
 *
 */

#include "files.h"

#define BLKSIZE     256     /*	# of records to allocate at once	*/
#define STRBLKSIZE  4096
#define MAXSTRLEN   64	    /*	maximum string len for cached strings	*/

static RECORD *Freelist;
static RECORD *Blk;
static short  Bi;
static char   *Strs[MAXSTRLEN];
static char   *Sbuf;
static short  Si;

RECORD *
allocrecord()
{
    register RECORD *rec;
    if (rec = Freelist) {
	Freelist = Freelist->next;
	return(rec);
    }
    if (!Bi) {
	Blk = (RECORD *)malloc(sizeof(RECORD) * BLKSIZE);
	if (!Blk)
	    return(NULL);
	Bi = 256;
    }
    --Bi;
    return(Blk++);
}

void
freerecord(rec)
RECORD *rec;
{
    rec->next = Freelist;
    Freelist = rec;
}

char *
allocstr(str)
char *str;
{
    register short bytes = strlen(str) + 1;
    register char *ptr;

    if (bytes >= 4 && bytes < MAXSTRLEN) {
	if (Strs[bytes]) {
	    ptr = Strs[bytes];
	    Strs[bytes] = *(char **)ptr;
	} else {
	    bytes = (bytes + 1) & ~1;
	    if (Si < bytes) {
		Sbuf = malloc(STRBLKSIZE);
		if (!Sbuf)
		    return(NULL);
		Si = STRBLKSIZE;
	    }
	    ptr = Sbuf;
	    Sbuf += bytes;
	    Si -= bytes;
	}
    } else {
	ptr = malloc(bytes);
	if (!ptr)
	    return(NULL);
    }
    strcpy(ptr, str);
    return(ptr);
}

void
freestr(ptr)
char *ptr;
{
    register short len;

    if (ptr) {
	len = strlen(ptr) + 1;
	if (len >= 4 && len < MAXSTRLEN) {
	    *(char **)ptr = Strs[len];
	    Strs[len] = ptr;
	} else {
	    free(ptr);
	}
    }
}

SHAR_EOF
cat << \SHAR_EOF > menu.c

/*
 *  MENU.C
 *
 *  (c)Copyright 1987 Matthew Dillon, All Rights Reserved.
 *
 */

#include "files.h"

#define NI  0	    /*	means 'not initialized' */

static ITEXT IText[] = {
    { 0, 1, JAM2, 0, 0, NULL, (ubyte *)"Save"        },
    { 0, 1, JAM2, 0, 0, NULL, (ubyte *)"SaveAs"      },
    { 0, 1, JAM2, 0, 0, NULL, (ubyte *)"Load"        },
    { 0, 1, JAM2, 0, 0, NULL, (ubyte *)"LoadDefault" },
    { 0, 1, JAM2, 0, 0, NULL, (ubyte *)"Quit"        },
    { 0, 1, JAM2, 0, 0, NULL, (ubyte *)"AddKillPat"  },
};

static ITEM Item[] = {
    { &Item[1], 0, NI, NI, NI, 0, 0, (APTR)&IText[0], NULL },
    { &Item[2], 0, NI, NI, NI, 0, 0, (APTR)&IText[1], NULL },
    { &Item[3], 0, NI, NI, NI, 0, 0, (APTR)&IText[2], NULL },
    { &Item[4], 0, NI, NI, NI, 0, 0, (APTR)&IText[3], NULL },
    { NULL    , 0, NI, NI, NI, 0, 0, (APTR)&IText[4], NULL },
    { NULL    , 0, NI, NI, NI, 0, 0, (APTR)&IText[5], NULL },
};

static MENU Menu[] = {
    { &Menu[1], NI, 0, NI, NI, NI, "Project" , NI },
    { NULL    , NI, 0, NI, NI, NI, "Control", NI  }
};


addmenus()
{
    MENU *menu;
    ITEM *item, *nit;
    short left = 5;
    short height;
    short width;

    for (menu = Menu, item = Item; menu; menu = menu->NextMenu) {
	height = 0;
	width  = strlen(menu->MenuName);
	menu->FirstItem = item;
	for (item = menu->FirstItem; item; item = item->NextItem) {
	    if (width < strlen(((ITEXT *)item->ItemFill)->IText))
		width = strlen(((ITEXT *)item->ItemFill)->IText);
	}
	width *= Win->RPort->TxWidth;
	for (item = menu->FirstItem; item; nit = item, item = item->NextItem) {
	    item->Width   = width;
	    item->Height  = Win->RPort->TxHeight + 2;
	    item->TopEdge = height;
	    item->Flags  |= ITEMTEXT|ITEMENABLED|HIGHCOMP;
	    height += item->Height;
	}
	menu->LeftEdge = left;
	menu->Width    = width;
	menu->Height   = height;
	menu->Flags    = MENUENABLED;
	item = nit + 1;
	left += width + 4;
    }
    SetMenuStrip(Win, Menu);
}

remmenus()
{
    ClearMenuStrip(Win);
}

getmenu(im)
IMESS *im;
{
    register short mn = MENUNUM(im->Code);
    register short in = ITEMNUM(im->Code);

    switch(mn) {
    case 0:
	switch(in) {
	case 0:
	    return(MEN_SAVE);
	case 1:
	    return(MEN_SAVEAS);
	case 2:
	    return(MEN_LOAD);
	case 3:
	    return(MEN_LOADEF);
	case 4:
	    return(MEN_QUIT);
	}
	break;
    case 1:
	switch(in) {
	case 0:
	    return(MEN_KILLPAT);
	}
	break;
    }
    return(0);
}

SHAR_EOF
cat << \SHAR_EOF > newwildcmp.c

/*
 *  NEWWILDCMP.C
 *
 *  (c)Copyright 1987 Matthew Dillon, All Rights Reserved.
 *
 *  Compare a wild card name with a normal name
 *
 *  This function replaces wildcmp() in SUP32.LIB (as not everybody will
 *  have the latest SUP32.LIB).  The only difference is that this call
 *  is case insensitive.  Later releases of SUP32.LIB will fix the
 *  case-sensitive bug.
 */

#define MAXB   8

newwildcmp(wild, name)
char *wild, *name;
{
    register char *w = wild;
    register char *n = name;
    char *back[MAXB][2];
    register short bi = 0;
    register char c1, c2;

    while (*n || *w) {
	switch (*w) {
	case '*':
	    if (bi == MAXB) {
		puts ("Too many levels of '*'");
		return (0);
	    }
	    back[bi][0] = w;
	    back[bi][1] = n;
	    ++bi;
	    ++w;
	    continue;
goback:
	    --bi;
	    while (bi >= 0 && *back[bi][1] == '\0')
		--bi;
	    if (bi < 0)
		return (0);
	    w = back[bi][0] + 1;
	    n = ++back[bi][1];
	    ++bi;
	    continue;
	case '?':
	    if (!*n) {
		if (bi)
		    goto goback;
		return (0);
	    }
	    break;
	default:
	    c1 = *n;
	    c2 = *w;
	    if (c1 >= 'A' && c1 <= 'Z')     /*  to lower case */
		c1 |= 0x20;
	    if (c2 >= 'A' && c2 <= 'Z')
		c2 |= 0x20;
	    if (c1 != c2) {
		if (bi)
		    goto goback;
		return (0);
	    }
	    break;
	}
	if (*n)  ++n;
	if (*w)  ++w;
    }
    return (1);
}


SHAR_EOF
#	End of shell archive
exit 0