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

char *Copyright =
"stdfile V2.0a. Copyright (c) 1987 Peter da Silva. All rights reserved.";
#include <intuition/intuitionbase.h>
#include <intuition/intuition.h>
#include <libraries/dos.h>
#include <exec/memory.h>
#include <clib/intuition_protos.h>
#include <clib/graphics_protos.h>
#include <clib/exec_protos.h>
#include <clib/dos_protos.h>
#include <clib/intuition_pragmas.h>
#include <clib/graphics_pragmas.h>
#include <clib/exec_pragmas.h>
#include <clib/dos_pragmas.h>
#include "ed.h"

#undef DEUTSCH

struct Screen *stdscreen;

#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
#define BUTWID (6*CHSIZ+INDENT*2)

/* GADGET BORDERS */
#define IN1 LEFTMAR+7*CHSIZ
#define IN3 LEFTMAR+3
#ifdef DEUTSCH
#define IN4 -(INDENT+7*CHSIZ+1)
#else
#define IN4 -(INDENT+6*CHSIZ+1)
#endif
#define IN5 -(INDENT+CHSIZ*2)
#define IN6 ((WINWD-BUTWID)/3+INDENT)
#define IN7 (((WINWD-BUTWID)*2)/3+INDENT)
#define WD1 -(INDENT+IN1)
#ifdef DEUTSCH
#define WD3 (5*CHSIZ)
#define WD4 (5*CHSIZ)
#define WD6 (6*CHSIZ)
#define WD7 (7*CHSIZ)
#else
#define WD3 (6*CHSIZ)
#define WD4 (6*CHSIZ)
#define WD6 (6*CHSIZ)
#define WD7 (6*CHSIZ)
#endif
#define WD5 (CHSIZ*2+2)
#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
#define PRTEXT &STD_PARENT

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 };

#ifdef DEUTSCH
static struct IntuiText STD_OK =
    { FP, BP, JAM2, 0, 1, Z, (UBYTE *)" OK ", Z };
static struct IntuiText STD_CANCEL =
    { FP, BP, JAM2, 0, 1, Z, (UBYTE *)"ABBRUCH", Z };
static struct IntuiText STD_VOLUME =
    { FP, BP, JAM2, 0, 1, Z, (UBYTE *)"GERÄT", Z };
static struct IntuiText STD_PARENT =
    { FP, BP, JAM2, 0, 1, Z, (UBYTE *)"VATER", Z };
#else
static struct IntuiText STD_OK =
    { FP, BP, JAM2, 0, 1, Z, (UBYTE *)"  OK  ", 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 *)"Disks", Z };
static struct IntuiText STD_PARENT =
    { FP, BP, JAM2, 0, 1, Z, (UBYTE *)"Parent", Z };
#endif

#define BUTTONS 4
#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
    },
    {
        -2, HT4,
        -2, -1,
        WD7+1,-1,
        WD7+1,HT4,
        -3, HT4,
        -3,-1,
        WD7+2,-1,
        WD7+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},
    {0, 0, FP, BP, JAM1, BUTVEC, butvecs[3], NULL}
};
#define BB(n) &ButBorder[n]

static struct Image STD_Image;

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

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,TP3, WD1,HT, SF|SEL, SA,  SG,    Z, Z,   Z, Z, SI(1), 1, 0 },
{ G(3), IN1,TP2, 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 },
{ G(7), IN7,TP4, WD7,HT4,BF1,    BA,  BG,BB(3), Z, PRTEXT, Z,  Z, 6, 0 },
{ NULL, IN5,TP5, WD5,HT5,PF,     PA,  PG, IMAG, Z,   Z, Z,  PROP, 7, 0 }
};

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

#define NVEC 6

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

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

static struct IntuiText STD_Text[3] = {
#ifdef DEUTSCH
    { FP, BP, JAM2, 0, 0, NULL, (UBYTE *)"Verzeichnis:", NULL },
    { FP, BP, JAM2, 0, 0, NULL, (UBYTE *)"Datei-Name:", NULL },
    { FP, BP, JAM2, 0, 0, NULL, (UBYTE *)"Pattern:", NULL }
#else
    { FP, BP, JAM2, 0, 0, NULL, (UBYTE *)"Drawer", NULL },
    { FP, BP, JAM2, 0, 0, NULL, (UBYTE *)" Match", NULL },
    { FP, BP, JAM2, 0, 0, NULL, (UBYTE *)"  File", NULL }
#endif
};

static OpenFileWindow()
{
    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 (stdscreen) {
        STD_NewWindow.Screen = stdscreen;
        STD_NewWindow.Type = CUSTOMSCREEN;
    }

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

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

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

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

static int State;

#define INITIAL 0
#define DIRECTORY 1

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

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

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

static PrintFileNames()
{
    int i;

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

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

    i = file - FirstFile;

    Move(STD_Window->RPort, HOMEX, HOMEY+i*CHSIZ+BASELINE);
    SetAPen(STD_Window->RPort, (hilite) ? BP : FP);
    SetBPen(STD_Window->RPort, (hilite) ? FP : BP);
    Text(STD_Window->RPort,
        NameTable[file]->filename,
        strlen(NameTable[file]->filename));
/*    if (NameTable[file]->filetype == DIRTYPE)
 *      Text(STD_Window->RPort,"/",1);
 */
}

static CalcPropGadget()
{
    int VertPot, VertBody;

    if(State == INITIAL) return;

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

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

static CalcFilePosition()
{
    int old_pos;

    if(State == INITIAL) return;

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

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

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

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

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

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

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

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

    State = DIRECTORY;
    Selected = -1;

    ReCalcPattern();
}

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

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

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

    State = DIRECTORY;
    Selected = -1;

    ReCalcPattern();
}

static WORD PatCode[128];

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

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

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

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

    if ( (*p1)->filetype == (*p2)->filetype ) {
      s1 = (*p1)->filename;
      s2 = (*p2)->filename;

      while(*s1 && *s2) {
        c1 = cap(*s1++);
        c2 = cap(*s2++);
        if(c1 != c2)
          return c1 - c2;
      }
      return 0;
    }
    else
      return (*p2)->filetype - (*p1)->filetype;
}

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

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

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

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

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

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

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

static ReadParDir()
{
    int i;
    int ptr;

    ptr = -1;
    for(i = 0; DirName[i]; i++)
        if(DirName[i]==':' || (DirName[i]=='/') && DirName[i+1] )
            ptr = i;
    if(ptr>=0) {
        SetGadgetText(FILID, &DirName[ptr+1]);
        if(ptr==0 || DirName[ptr]==':')
            ptr++;
        DirName[ptr] = 0;
        SetGadgetText(DIRID, DirName);
    } else {
        SetGadgetText(FILID, DirName);
        SetGadgetText(DIRID, (i) ? "" : "/");
    }
    ReadNewDir();
    return 1;
}

static SetFileName(name)
char *name;
{
    /* Can't enter a file name too long. */
    if(strlen(DirName) + strlen(name) + 1 > MAXFULL) {
        DisplayBeep(NULL);
        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: if (FileName[0]) {
                        DoneFlag = 1;
                        break;
                    }
        case PATID: ReCalcPattern(); break;
        case BARID: CalcFilePosition(); break;
        case YESID: DoneFlag = 1; break;
        case CANID: DoneFlag = -1; break;
        case VOLID: ReadVol(); break;
        case PARID: ReadParDir(); break;
    }
}

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

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

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

/*
 *  3.9.89/ms
 *
 *  if(defpat)
 *      strcpy(PatName, defpat);
 *  else
 *      PatName[0] = 0;
 */
    State = INITIAL;
    NameTable = 0;
    NameList = 0;

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

        CloseFileWindow();
    }
    else return 0;

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

    if(DoneFlag==1) {
        int len;

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

        /* Here the user has accepted the name without providing a file
           name. I return true, but false may be more appropriate. What
           do you think? */
        return 1;
    }
    return 0;
}
