[comp.sources.amiga] v02i039: Instant application generator

peter@sugar.UUCP (Peter da Silva) (10/06/87)

: This archive contains the following files...
: 'pr.c'
: 'instant.c'
: 'PatMatch.c'
: 'VolList.c'
: 'menu.c'
: 'shar.c'
: 'Makefile'
: 'readme'
: To extract them, run the following through /bin/sh
echo x - pr.c
sed 's/^X//' > pr.c << '//END'
X#include <intuition/intuition.h>
X#include <exec/memory.h>
X#include <libraries/dos.h>
X#include <stdio.h>
X#include "instant.h"
X#define BPTR long
X
X#define SECSPERDAY (60*60*24)
X#define SECSPERMIN 60
X#define TICKSPERSEC TICKS_PER_SECOND
XFILE *stdprt;
XFILE *stdlog;
X
Xint select1(), ready();
X
Xstruct FuncTable ft[] = {
X	"Select File", select1,
X	"Print Files", ready,
X	0, 0
X};
X
Xstruct IntuitionBase *IntuitionBase;
Xstruct GfxBase *GfxBase;
X
Xmain(ac, av)
Xint ac;
Xchar **av;
X{
X	if(ac == 0) {
X		IntuitionBase = (struct IntuitionBase *)
X			OpenLibrary("intuition.library", 0);
X		GfxBase = (struct GfxBase *)
X			OpenLibrary("graphics.library", 0);
X		noneselected();
X		if(instant("Print Files", 0, 0, ft)) {
X			if(stdprt=fopen("PRT:", "w")) {
X				stdlog=fopen("CON:0/0/400/50/Printer log", "w");
X				printselected();
X				if(stdlog) fclose(stdlog);
X				fclose(stdprt);
X			}
X		} else
X			freeselected();
X		CloseLibrary(IntuitionBase);
X		CloseLibrary(GfxBase);
X		exit(0);
X	}
X	if(strcmp(av[1], "debug") == 0 ||
X	   strcmp(av[1], "DEBUG") == 0) {
X		stdprt = stdout;
X		av++;
X	} else if(!(stdprt=fopen("PRT:", "w"))) {
X		perror("PRT:");
X		return -1;
X	}
X	stdlog = stderr;
X	while(*++av) {
X		prfiles(*av);
X	}
X	if(stdprt != stdout)
X		fclose(stdprt);
X}
X
Xprfiles(f)
Xchar *f;
X{
X	struct FileInfoBlock *FIB;
X	char path[MAXFILENAME], name[MAXNAME];
X	static WORD PatCode[128];
X	char *p;
X	BPTR dirlock;
X
X	if((FIB = AllocMem(sizeof(struct FileInfoBlock), MEMF_PUBLIC)) == 0)
X		return 0;
X
X	if((p=strrchr(f, '/'))==0) {
X		if((p=strrchr(f, ':'))==0) {
X			strcpy(name, f);
X			path[0] = 0;
X		} else {
X			char c;
X
X			c = *p;
X			*p = 0;
X			strcpy(path, f);
X			*p = c;
X			strcpy(name, p);
X		}
X	} else {
X		*p++ = 0;
X		strcpy(path, f);
X		strcpy(name, p);
X	}
X
X	CmplPat(name, PatCode);
X
X	dirlock = Lock(path, ACCESS_READ);
X
X	p = &path[strlen(path)];
X	if(p > path && p[-1]!=':')
X		*p++ = '/';
X	*p = 0;
X
X	if(!dirlock) {
X		FreeMem(FIB, sizeof(struct FileInfoBlock));
X		return 0;
X	}
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	}
X	while(ExNext(dirlock, FIB)) {
X		if(Match(name, PatCode, FIB->fib_FileName)) {
X			strcpy(p, FIB->fib_FileName);
X			pr(path);
X		}
X	}
X	FreeMem(FIB, sizeof(struct FileInfoBlock));
X	UnLock(dirlock);
X	return 1;
X}
X
Xpr(file)
Xchar *file;
X{
X	FILE *fp;
X	int line, page;
X	char linebuf[81];
X	BPTR lock;
X	struct FileInfoBlock *fib;
X	unsigned long datestamp;
X	char *timestr;
X
X	if(!(lock = Lock(file, ACCESS_READ))) {
X		if(stdlog)
X			fprintf(stdlog, "Can't obtain lock for %s\n", file);
X		return;
X	}
X	if(!(fib = AllocMem(sizeof(struct FileInfoBlock), MEMF_PUBLIC))) {
X		UnLock(lock);
X		if(stdlog)
X			fprintf(stdlog, "Out of memory.\n");
X		fclose(stdprt);
X		exit(20);
X	}
X	if(!(Examine(lock, fib))) {
X		if(stdlog)
X			fprintf(stdlog, "Can't examine %s\n", file);
X		UnLock(lock);
X		FreeMem(fib, sizeof(struct FileInfoBlock));
X		return;
X	}
X	UnLock(lock);
X
X	if(fib->fib_DirEntryType >= 0) {
X		if(stdlog)
X			fprintf(stdlog, "%s is a directory.\n", file);
X		FreeMem(fib, sizeof(struct FileInfoBlock));
X		return;
X	}
X
X	if(!(fp = fopen(file, "r"))) {
X		perror(file);
X		FreeMem(fib, sizeof(struct FileInfoBlock));
X		return;
X	}
X
X	line = 61;
X	page = 0;
X	datestamp =
X		(unsigned long)fib->fib_Date.ds_Days*SECSPERDAY +
X		(unsigned long)fib->fib_Date.ds_Minute*SECSPERMIN +
X		(unsigned long)fib->fib_Date.ds_Tick/TICKSPERSEC;
X	timestr = ctime(&datestamp);
X	timestr[strlen(timestr)-1] = 0;
X
X	if(stdlog)
X		fprintf(stdlog, "Printing %s.\n", file);
X	while(getline(linebuf, 80, fp)) {
X		linebuf[80] = 0;
X		if(line++ > 55) {
X			page++;
X			fprintf(stdprt, "\f\r%-30s       %5d       %30s\n\n",
X				fib->fib_FileName, page, timestr);
X			if(stdlog)
X				fprintf(stdlog, " Page %d\r", page);
X			line = 0;
X		}
X		fputs(linebuf, stdprt);
X	}
X	
X	fclose(fp);
X	FreeMem(fib, sizeof(struct FileInfoBlock));
X}
X
Xgetline(buffer, max, fp)
XFILE *fp;
Xchar *buffer;
Xint max;
X{
X	int c;
X	int len;
X
X	len = 0;
X	while((c = getc(fp)) != '\n' && c != EOF && len<max) {
X		c = c & 0x7F;
X		buffer[len] = c;
X		if(c<' ') {
X			switch(c) {
X				case '\t':
X					do {
X						buffer[len] = ' ';
X						len++;
X					} while((len&0x7) && len<max);
X					break;
X				case '\b':
X					if(len>0) len--;
X					break;
X				case '\r':
X					len = 0;
X					break;
X			}
X		} else
X			len++;
X	}
X	if(c=='\n' && len<max)
X		buffer[len++] = c;
X	else if(!feof(fp))
X		ungetc(c, fp);
X	buffer[len] = 0;
X}
X
Xstruct SelectedFile {
X	struct SelectedFile *sf_next;
X	char *sf_name;
X} *filelist;
X
Xnoneselected()
X{
X	filelist = 0;
X}
X
Xprintselected()
X{
X	struct SelectedFile *next;
X
X	while(filelist) {
X		next = filelist->sf_next;
X		pr(filelist->sf_name);
X		free(filelist->sf_name);
X		free(filelist);
X		filelist = next;
X	}
X}
X
Xfreeselected()
X{
X	struct SelectedFile *next;
X
X
X	while(filelist) {
X		next = filelist->sf_next;
X		free(filelist->sf_name);
X		free(filelist);
X		filelist = next;
X	}
X}
X
Xselect1(lock, name, id)
XBPTR lock;
Xchar *name;
Xint id;
X{
X	struct SelectedFile *next, *ptr;
X
X	if(!name)
X		return 0;
X
X	ptr = malloc(sizeof(struct SelectedFile));
X	if(!ptr)
X		return -1;
X	ptr->sf_next = 0;
X	ptr->sf_name = malloc(strlen(name)+1);
X	if(!ptr->sf_name) {
X		free(ptr);
X		return -1;
X	}
X	strcpy(ptr->sf_name, name);
X	if(!filelist)
X		filelist = ptr;
X	else {
X		for(next = filelist; next->sf_next; next = next->sf_next)
X			if(strcmp(name, next->sf_name) == 0) { /* already selected */
X				free(ptr->sf_name);
X				free(ptr);
X				return 0;
X			}
X		next->sf_next = ptr;
X	}
X	return 0;
X}
X
Xready(lock, name, id)
XBPTR lock;
Xchar *name;
Xint id;
X{
X	if(name)
X		select1(lock, name, id);
X	return 1;
X}
X
Xperror(s)
Xchar *s;
X{
X	extern int errno;
X	extern char *sys_errlist[];
X
X	if(stdlog)
X		fprintf(stdlog, "%s: %s\n", s, sys_errlist[errno]);
X	else
X		DisplayBeep();
X}
//END
echo x - instant.c
sed 's/^X//' > instant.c << '//END'
X/* INSTANT -- Instant Workbench Application   Version 0.0  5 Sep 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 *	provided this notice remains intact in the source. The
X *	intent of this module is to provide a simple way to write
X *	a "standard" workbench application. This routine is passed
X *	an array of function names and associated routines, and puts
X *	up a list of files. Double-clicking a file name will pass
X *	it to the current routine.
X *
X *	The current routine is initially the first one in the list, and
X *	you can select a routine from the "Applications" menu. When a
X *	new routine is selected it will be called with 0 for the filename.
X *
X *	A routine should normally return 0... a non-zero return value will
X *	cause instant to close the window and terminate. If this isn't
X *	good enough... if you must blow out... call instant_cleanup()
X *	first.
X *
X *	Check all arguments to your routines. This version will pass
X *	a 0 for the lock parameter. Another version may pass a directory
X *	lock. Eventually I plan on having this sort of calling
X *	sequence:
X *
X *	application(argc, argv, functab);
X *
X *	If called from the cli (argc!=0), it will expand the filename args
X *	and call the appropriate parameter args based on the keywords in
X *	functab. If argc==0 it will look for WBArgs and pass them to the
X *	first routine in functab. If argc==0 and there are no WBArgs, it
X *	will call instant(). This way you'll end up with an AmigaDOS
X *	compatible calling sequence from the CLI, and it'll still work with
X *	shift-click and double-click from the workbench. It should also
X *	work as the tool for an application icon.
X *
X *	The index argument is to allow you to have the same function in
X *	more than one place in your table.
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 *	#include "instant.h"
X *
X *	int instant(title, default_file, default_pat, table);
X *	char *title;
X *	char *default_file;
X *	char *default_pattern;
X *	struct FuncTable {
X *		char *FT_name;
X *		int (*FT_func)(BPTR, char *, int);
X *	} *table;
X *
X *	INSTANT puts up a file requestor (actually, it's a plain window)
X *	in the workbench screen.
X *
X *	The requestor looks like this (allowing for the limitations of
X *	text):
X *
X *	+-----------------------------------+
X *	|o| Title ------------------- |  |  | Currently selected function.
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 *	| +-------------------------------+ |
X *	+-----------------------------------+
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 *
X * Example:
X *
X *	pepperoni(lock, file, index)
X *	BPTR lock;
X *	char *file;
X *	int index;
X *	{
X *		return 0;
X *	}
X *	...
X *
X *	struct FuncTable *ft = {
X *		"Pepperoni", pepperoni,
X *		"Black Olive", blackolive,
X *		"Mozzarella", mozzarella,
X *		0, 0
X *	};
X *	
X *	instant("Pizza Processor", 0, "#?.pizza", ft);
X */
Xchar *Copyright =
X"Instant Application V0.0. (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#include "instant.h"
X#include "menu.h"
X
Xchar *malloc(), *AllocMem();
Xstruct Window *OpenWindow();
X
X#define MAXFILES 8
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
X/* GADGET BORDERS */
X#define IN1 LEFTMAR+10*CHSIZ
X#define IN3 LEFTMAR+3
X#define IN5 -(INDENT+CHSIZ*2)
X#define WD1 -(INDENT+IN1)
X#define WD5 (CHSIZ*2+2)
X#define TP1 (CHSIZ+BORDER)
X#define TP2 (TP1+HT+1)
X#define TP3 (TP2+HT+1)
X#define TP5 (TP3+HT+BORDER)
X#define HT5 (CHSIZ*MAXFILES+BORDER*2)
X
X#define WINHT (TP5 + HT5 + BORDER-1)
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 SF GADGHCOMP|GRELWIDTH
X#define SEL SELECTED
X#define PF GRELRIGHT
X
X#define SA RELVERIFY
X#define CEN STRINGCENTER
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 PG PROPGADGET
X
X#define FP AUTOBACKPEN
X#define BP AUTOFRONTPEN
X
Xstatic int DoneFlag;
Xstatic int (*DefaultFunction)();
Xstatic int DefaultIndex;
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 Image STD_Image;
X
X#define DIRID 0
X#define FILID 1
X#define PATID 2
X#define BARID 3
X#define GADGETS 4
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{ NULL, IN5,TP5, WD5,HT5,PF,     PA,  PG, IMAG, Z,   Z, Z,  PROP, 3, 0 }
X};
X
X#define INSTANTMENU 0
X#define VOLITEM 0
X#define PARITEM 1
X#define CLOSITEM 2
X#define APPLICMENU 1
X
Xstatic struct NewWindow STD_NewWindow = {
X	WININ, WINTP, WINWD, WINHT, -1, -1,
X	REFRESHWINDOW|MENUPICK|MOUSEBUTTONS|GADGETUP|CLOSEWINDOW,
X	WINDOWDRAG|WINDOWDEPTH|WINDOWCLOSE|SIMPLE_REFRESH|ACTIVATE,
X	G(0), NULL, (UBYTE *)"Instant Application",
X	NULL, NULL, 0, 0, 0, 0, WBENCHSCREEN
X};
Xstatic struct Window *STD_Window;
X
X#define NVEC 6
X
X#define BIN LEFTMAR
X#define BTP TP5
X#define BWD (WINWD-INDENT-BIN)
X#define BHT (WINHT-BTP-BORDER)
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
Xstruct MenuPtr menuptr[1];
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->LibNode.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}
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 = (struct dirent *)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 = (struct dirent *)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)
XUBYTE *name;
X{
X	UBYTE buffer[MAXFULL+1], *ptr;
X	int index;
X	UBYTE 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: break;
X		case PATID: ReCalcPattern(); break;
X		case BARID: CalcFilePosition(); break;
X	}
X}
X
XSetApplication(functable, number)
Xstruct FuncTable *functable;
Xint number;
X{
X	SetWindowTitles(STD_Window, functable[number].FT_name, -1);
X	DefaultFunction = functable[number].FT_func;
X	DefaultIndex = number;
X	DoneFlag = (*DefaultFunction)(0, 0, number);
X}
X
Xstatic ProcessMenu(functable, menunumber)
Xstruct FuncTable *functable;
Xint menunumber;
X{
X	struct MenuItem *ItemAddress();
X
X	while(menunumber != MENUNULL && DoneFlag==0) {
X		switch(MENUNUM(menunumber)) {
X			case INSTANTMENU:
X				switch(ITEMNUM(menunumber)) {
X					case VOLITEM:
X						ReadVol(); break;
X						break;
X					case PARITEM:
X						ReadParDir(); break;
X						break;
X					case CLOSITEM:
X						DoneFlag = -1; break;
X						break;
X					default:
X						break;
X				}
X				break;
X			case APPLICMENU:
X				SetApplication(functable, ITEMNUM(menunumber));
X				break;
X			default:
X				break;
X		}
X		menunumber = ItemAddress(menuptr[0].MenuBar, menunumber)->NextSelect;
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(Selected != -1 &&
X					   Selected>=FirstFile && Selected<FirstFile+MAXFILES)
X						PrintName(Selected, 0);
X					SetFileName(NameTable[Selected]->filename);
X					Selected = -1;
X					if(DefaultFunction) {
X						char buffer[MAXFILENAME];
X
X						ExtractFileName(buffer);
X						DoneFlag = (*DefaultFunction)(0, buffer, DefaultIndex);
X					}
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
XCreateMenuBar(functable)
Xstruct FuncTable *functable;
X{
X	int i;
X
X	init_menus(menuptr);
X	if(add_item(menuptr, "Instant", "Volumes", 0) == MENUNULL ||
X	   add_item(menuptr, "Instant", "Parent", 0) == MENUNULL ||
X	   add_item(menuptr, "Instant", "Close", 0) == MENUNULL) {
X		trash_menu(menuptr);
X		return 0;
X	}
X	for(i = 0; functable[i].FT_name; i++)
X		if(add_item(menuptr, "Application",
X			functable[i].FT_name, 0) == MENUNULL
X		  ) {
X			trash_menu(menuptr);
X			return 0;
X		}
X}
X
XExtractFileName(name)
Xchar *name;
X{
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
Xinstant(title, deffile, defpat, functable)
Xchar *title;
Xchar *deffile, *defpat;
Xstruct FuncTable *functable;
X{
X	struct IntuiMsg *GetMsg();
X
X	if(!CreateMenuBar(functable))
X		return 0;
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
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
X		SetApplication(functable, 0);
X		SetMenuStrip(STD_Window, menuptr[0].MenuBar);
X		SetWindowTitles(STD_Window, -1, title?title:"Instant Application");
X
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 MENUPICK:
X						ProcessMenu(functable, msg->Code);
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		ClearMenuStrip(STD_Window);
X		CloseFileWindow();
X	} else
X		return 0;
X
X	FreeList(NameList);
X	if(NameTable) free(NameTable);
X	trash_menu(menuptr);
X
X	if(DoneFlag==1)
X		return 1;
X	else
X		return 0;
X}
X
Xinstant_cleanup()	/* Call this routine if you must blow out. */
X{
X	FreeList(NameList);
X	if(NameTable) free(NameTable);
X	ClearMenuStrip(STD_Window);
X	CloseFileWindow();
X	trash_menu(menuptr);
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 - 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 - menu.c
sed 's/^X//' > menu.c << '//END'
X/* easy menus: Copyright 1987 Peter da Silva, all rights reserved.
X *
X *	Permission is granted to use this in any application, so long as
X *	this notice is retained in the source. Permission is granted to
X *	modify the code as you like, so long as this notice (between the
X *	first line beginning "easy menus" and the end of this paragraph,
X *	inclusive) is retained intact.
X *
X * Usage:
X *
X *	#include "menu.h"
X *
X *	struct MenuPtr menudata;
X *	struct MenuPtr *menuptr = &menudata;
X *
X *	init_menus(menuptr);	/ * Just zero menu pointer out * /
X *
X *	for(each menu item) {
X *		add_menu(menuptr, menu, item, subitem);
X *	}
X *
X *	SetMenuStrip(yourwindow, menuptr->MenuBar);
X *
X *	...
X *
X *	ClearMenuStrip(yourwindow);
X *
X *	trash_menus(menuptr);
X *
X * Notes:
X *
X *	if you don't want any subitems, use zero for the subitem value.
X *
X *	subitem is always initialised as a CHECKIT item with all the other
X *	subitems mutually excluded.
X *
X *	it is intended that the menu be set up with all action items in
X *	the first level of the menu, and all toggles in the second level...
X *	this is a piece of blatant authoritarianism on my part. I've seen
X *	too many menus with no rhyme or reason. Look at AmigaTerm (the term
X *	program that comes with the Amiga modem) some time. Baud rate has
X *	an item all by itself, but word size is hidden off in a menu with
X *	things like bell sound.
X *
X *	the appearance of the menus produced by this is (in my humble
X *	opinion) good. I took some care making text centered in menu boxes,
X *	for example.
X */
X#include <exec/memory.h>
X#include <intuition/intuition.h>
X#include "menu.h"
X
X/*
Xstruct MenuPtr {
X	struct Menu *MenuBar;
X	struct Remember *MenuMemory;
X};
X*/
X
Xstatic struct Menu *new_menu();
Xstatic struct MenuItem *new_item(), *new_subitem();
X
X#define TOMENUNUM(i,j,k) (SHIFTMENU(i)|SHIFTITEM(j)|SHIFTSUB(k))
X#define TextLen(s) (strlen(s)*CHARWIDTH)
X
X#define CHARWIDTH 9		/* 8 for 80 column display, 9 for 60 or 80 */
X
Xtrash_menu(menuptr)
Xstruct MenuPtr *menuptr;
X{
X	FreeRemember(&menuptr->MenuMemory, 1);
X	menuptr->MenuMemory = 0;
X	menuptr->MenuBar = 0;
X}
X
Xinit_menus(menuptr)
Xstruct MenuPtr *menuptr;
X{
X	menuptr->MenuMemory = 0;
X	menuptr->MenuBar = 0;
X}
X
Xint add_item(menuptr, menuname, itemname, subitemname)
Xstruct MenuPtr *menuptr;
Xchar *menuname, *itemname, *subitemname;
X{
X	int i, j, k;
X	struct Menu *menu;
X	struct MenuItem *item;
X	struct MenuItem *subitem;
X
X	if(menuptr->MenuBar) {
X		for(i = 0, menu = menuptr->MenuBar;
X		    menu;
X			menu = menu->NextMenu, i++
X		   )
X			if(strcmp(menuname, menu->MenuName)==0)
X				break;
X		if(!menu)
X			menu = new_menu(menuptr, menuname);
X		if(!menu)
X			return MENUNULL;
X	} else {
X		i = 0;
X		menu = new_menu(menuptr, menuname);
X		if(!menu)
X			return MENUNULL;
X	}
X	for(j = 0, item = menu->FirstItem;
X		item;
X		item = item->NextItem, j++
X	   ) {
X		struct IntuiText *text;
X		text = item->ItemFill;
X		if(strcmp(itemname, text->IText) == 0)
X			break;
X	}
X	if(subitemname) {
X		if(!item)
X			item = new_item(menuptr, menu, itemname);
X		if(!item)
X			return MENUNULL;
X		for(k = 0, subitem = item->SubItem;
X			subitem;
X			subitem = subitem->NextItem, k++
X		   ) {
X			struct IntuiText *text;
X			text = subitem->ItemFill;
X			if(strcmp(subitemname, text->IText) == 0)
X				break;
X		}
X		if(!subitem)
X			subitem = new_subitem(menuptr, item, subitemname);
X		if(!subitem)
X			return MENUNULL;
X		return TOMENUNUM(i, j, k);
X	} else {
X		if(!item)
X			item = new_item(menuptr, menu, itemname);
X		if(!item)
X			return MENUNULL;
X		return TOMENUNUM(i, j, NOSUB);
X	}
X}
X
Xstatic struct Menu *
Xnew_menu(menuptr, name)
Xstruct MenuPtr *menuptr;
Xchar *name;
X{
X	struct Menu *menu;
X
X	menu = (struct Menu *)AllocRemember(
X		menuptr->MenuMemory,
X		sizeof(struct Menu),
X		MEMF_PUBLIC);
X	if(!menu)
X		return 0;
X	menu->NextMenu = NULL;
X	menu->LeftEdge = 0;
X	menu->TopEdge = 0;
X	menu->Width = TextLen(name)+CHARWIDTH;
X	menu->Height = 0;
X	menu->Flags = MENUENABLED;
X	menu->MenuName = name;
X	menu->FirstItem = 0;
X	if(menuptr->MenuBar) {
X		struct Menu *ptr, *prev;
X		for(ptr = menuptr->MenuBar; ptr; ptr=ptr->NextMenu) {
X			menu->LeftEdge += ptr->Width;
X			prev = ptr;
X		}
X		prev->NextMenu = menu;
X	} else {
X		menuptr->MenuBar = menu;
X	}
X
X	return menu;
X}
X
Xstatic struct item *
Xnew_item(menuptr, menu, name)
Xstruct MenuPtr *menuptr;
Xchar *name;
Xstruct Menu *menu;
X{
X	struct MenuItem *item;
X	struct IntuiText *text;
X
X	item = (struct MenuItem *)AllocRemember(
X		menuptr->MenuMemory,
X		sizeof(struct MenuItem),
X		MEMF_PUBLIC);
X	if(!item)
X		return 0;
X	text = (struct IntuiText *)AllocRemember(
X		&menuptr->MenuMemory,
X		sizeof(struct IntuiText),
X		MEMF_PUBLIC);
X	if(!text)
X		return 0;
X
X	text->FrontPen = AUTOFRONTPEN;
X	text->BackPen = AUTOBACKPEN;
X	text->DrawMode = JAM2;
X	text->LeftEdge = 1;
X	text->TopEdge = 1;
X	text->ITextFont = NULL;
X	text->IText = name;
X	text->NextText = NULL;
X
X	item->NextItem = NULL;
X	item->LeftEdge = 0;
X	item->TopEdge = 0;
X	item->Width = IntuiTextLength(text)+2;
X	if(item->Width <= menu->Width)
X		item->Width = menu->Width+1;
X	item->Height = 9;
X	item->Flags = ITEMTEXT|HIGHCOMP|ITEMENABLED;
X	item->MutualExclude = 0;
X	item->ItemFill = text;
X	item->SelectFill = NULL;
X	item->Command = 0;
X	item->SubItem = NULL;
X	item->NextSelect = NULL;
X
X	if(menu->FirstItem) {
X		struct MenuItem *ptr, *prev;
X		for(ptr = menu->FirstItem; ptr; ptr=ptr->NextItem) {
X			if(item->Width > ptr->Width) {
X				if(ptr->SubItem)
X					nudge(ptr->SubItem, item->Width-ptr->Width);
X				ptr->Width = item->Width;
X			} else if(ptr->Width>item->Width)
X				item->Width = ptr->Width;
X			prev = ptr;
X		}
X		item->TopEdge = prev->TopEdge + prev->Height;
X		prev->NextItem = item;
X	} else {
X		menu->FirstItem = item;
X	}
X
X	return item;
X}
X
Xstatic nudge(item, delta)
Xstruct MenuItem *item;
Xint delta;
X{
X	while(item) {
X		item->LeftEdge += delta;
X		item = item->NextItem;
X	}
X}
X
Xstatic struct item *
Xnew_subitem(menuptr, item, name)
Xstruct MenuPtr *menuptr;
Xchar *name;
Xstruct MenuItem *item;
X{
X	struct MenuItem *subitem;
X	struct IntuiText *text;
X
X	subitem = (struct MenuItem *)AllocRemember(
X		&menuptr->MenuMemory,
X		sizeof(struct MenuItem),
X		MEMF_PUBLIC);
X	if(!subitem)
X		return 0;
X	text = (struct IntuiText *)AllocRemember(
X		&menuptr->MenuMemory,
X		sizeof(struct IntuiText),
X		MEMF_PUBLIC);
X	if(!text)
X		return 0;
X
X	text->FrontPen = AUTOFRONTPEN;
X	text->BackPen = AUTOBACKPEN;
X	text->DrawMode = JAM2;
X	text->LeftEdge = CHECKWIDTH+1;
X	text->TopEdge = 1;
X	text->ITextFont = NULL;
X	text->IText = name;
X	text->NextText = NULL;
X
X	subitem->NextItem = NULL;
X	subitem->LeftEdge = item->Width;
X	subitem->TopEdge = 0;
X	subitem->Width = IntuiTextLength(text)+CHECKWIDTH+2;
X	subitem->Height = 9;
X	subitem->Flags = ITEMTEXT|ITEMENABLED|HIGHCOMP|CHECKIT;
X	subitem->MutualExclude = 0xFFFF;
X	subitem->ItemFill = text;
X	subitem->SelectFill = NULL;
X	subitem->Command = 0;
X	subitem->SubItem = NULL;
X	subitem->NextSelect = NULL;
X
X	if(item->SubItem) {
X		struct MenuItem *ptr, *prev;
X		int i;
X		for(i=0, ptr = item->SubItem; ptr; i++, ptr=ptr->NextItem) {
X			if(subitem->Width > ptr->Width)
X				ptr->Width = subitem->Width;
X			else if(ptr->Width>subitem->Width)
X				subitem->Width = ptr->Width;
X			prev = ptr;
X		}
X		subitem->TopEdge = prev->TopEdge + prev->Height;
X		subitem->MutualExclude &= ~(1<<i);
X		prev->NextItem = subitem;
X	} else {
X		item->SubItem = subitem;
X		subitem->MutualExclude &= ~1;
X		subitem->Flags |= CHECKED;
X	}
X
X	return subitem;
X}
//END
echo x - shar.c
sed 's/^X//' > shar.c << '//END'
X
X#include <stdio.h>
X
Xmain(ac, av)
Xint ac;
Xchar **av;
X{
X	int i;
X	FILE *fp, *sharfp;
X	char buffer[BUFSIZ];
X
X	if(av[1][0] == '>') {
X		sharfp = fopen(&av[1][1], "w");
X		if(!sharfp) {
X			perror(&av[1][1]);
X			exit(20);
X		}
X		av++, ac--;
X	} else
X		sharfp = stdout;
X	fprintf(sharfp, ": This archive contains the following files...\n");
X	for(i = 1; i < ac; i++)
X		fprintf(sharfp, ": '%s'\n", av[i]);
X	fprintf(sharfp, ": To extract them, run the following through /bin/sh\n");
X	for(i = 1; i < ac; i++)
X	{
X		if(!(fp = fopen(av[i], "r"))) {
X			perror(av[i]);
X			fprintf(sharfp, "echo '%s' not found.\n", av[i]);
X			continue;
X		}
X		fprintf(sharfp, "echo x - %s\n", av[i]);
X		fprintf(sharfp, "sed 's/^X//' > %s << '//END'\n", av[i]);
X		while(fgets(buffer, BUFSIZ, fp))
X			fprintf(sharfp, "X%s", buffer);
X		fclose(fp);
X		fprintf(sharfp, "//END\n");
X	}
X	fprintf(sharfp, ": end of archive.\n");
X	fprintf(sharfp, "exit 0\n");
X	if(sharfp != stdout)
X		fclose(sharfp);
X}
//END
echo x - Makefile
sed 's/^X//' > Makefile << '//END'
XOFILES= pr.o instant.o PatMatch.o VolList.o menu.o
XCFILES= pr.c instant.c PatMatch.c VolList.c menu.c shar.c
XTEXT= Makefile readme
XOTHERS= $(TEXT) pr
X
X.SUFFIXES: .c .o
X
X.c.o:
X	@-ram:c/del $*.o
X	cc +P -S -B -DAMIGA $*.c
X
Xall: pr
X
Xpr: $(OFILES)
X	@-ram:c/del pr
X	ln -o pr $(OFILES) -lcl32
X
Xinstant.arc: $(CFILES) $(OTHERS)
X	@-ram:c/del instant.arc
X	arc a instant $(CFILES) $(OTHERS)
X
Xshar: shar.o
X	@-ram:c/del shar
X	ln -o shar shar.o -lcl32
X
Xinstant.shar: shar $(CFILES) $(TEXT)
X	@-ram:c/del instant.shar
X	shar >instant.shar $(CFILES) $(TEXT)
X
Xprint: $(CFILES) $(TEXT)
X	pr $(CFILES) $(TEXT)
//END
echo x - readme
sed 's/^X//' > readme << '//END'
XINSTANT is a module to take most of the grunt work out of writing an
Xapplication. It puts up a window that contains a list of files. You can
Xselect commands from the menu and then pass file names to them from the
Xfile list. Like STDFILE, on which it is based, it never hits the disk
Xuntil you ask it to, and it's possible to get to any file on any device
Xjust using the mouse.
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.
X
X	--	Peter da Silva
X		Houston, Texas  1987
X
X		Voice: Home (713) 497-4372
X		Data: Wintermute (713) 933-2440
X		UUCP: ...!hoptoad!academ!uhnix1!sugar!peter
//END
: end of archive.
exit 0
-- 
-- Peter da Silva `-_-' ...!seismo!soma!uhnix1!sugar!peter
--                 'U`  <-- Public domain wolf.