    
/* *** fileio.c *************************************************************
 *
 * File IO Suite  --  Primary FileIO Requester Routines
 *     from Book 1 of the Amiga Programmers' Suite by RJ Mical
 *
 * Copyright (C) 1986, 1987, Robert J. Mical
 * All Rights Reserved.
 *
 * Created for Amiga developers.
 * Any or all of this code can be used in any program as long as this
 * entire copyright notice is retained, ok?  Thanks.
 *
 * HISTORY      NAME            DESCRIPTION
 * -----------  --------------  --------------------------------------------
 * 4 Feb 87     RJ              Real release
 * 12 Aug 86    RJ >:-{)*       Prepare (clean house) for release
 * 3 May 86     =RJ Mical=      Fix prop gadget for both 1.1 and 1.2
 * 1 Feb 86     =RJ Mical=      Created this file.
 *
 * *********************************************************************** */


#define FILEIO_SOURCEFILE
#include "fileio.h"


/* These routines can be found in filesupp.c */
extern HandleGadget();
extern StartOpenRequester();
extern DiskInserted();
extern PropMouseMoves();



/* *** GetFileIOSupport() ***************************************************
 * 
 * NAME
 *     GetFileIOSupport  --  Allocate and initialize a FileIOSupport structure
 * 
 * 
 * SYNOPSIS
 *     struct FileIOSupport *GetFileIOSupport();
 * 
 * 
 * FUNCTION
 *     Allocates and initializes a FileIOSupport structure for use with
 *     calls to GetFileIOName().
 * 
 *     You may want to further initialize the structure before calling
 *     GetFileIOName().  Refer to the FileIO documentation for more
 *     information.
 * 
 *     When you're done with the structure, call ReleaseFileIO().
 * 
 * 
 * INPUTS
 *     None
 * 
 * 
 * RESULT
 *     If all goes well, returns the address of a FileIOSupport structure.
 *     If anything goes wrong (usually out of memory), returns NULL.
 * 
 * 
 * EXAMPLE
 *     struct FileIOSupport *fileio;
 *     fileio = GetFileIOSupport();
 *     GetFileIOName(fileio, window);
 *     ReleaseFileIO(fileio);
 * 
 * 
 * BUGS
 *     None known
 * 
 * 
 * SEE ALSO
 *     GetFileIOName(), ReleaseFileIO()
 */
struct FileIOSupport *GetFileIOSupport()
{
    struct FileIOSupport *fileio;

    if (fileio = (struct FileIOSupport *)AllocMem(
            sizeof(struct FileIOSupport), MEMF_CLEAR))
        {
        /* Anything special to initialize? */
        SetFlag(fileio->Flags, USE_VOLUME_NAMES);
        }
    return(fileio);
}



/* *** GetFileIOName() ******************************************************
 * 
 * NAME
 *     GetFileIOName  --  Gets a file name for input/output from the user
 * 
 * 
 * SYNOPSIS
 *     BOOL GetFileIOName(FileIO, Window);
 * 
 * 
 * FUNCTION
 *     This routine creates a filename requester which allows the user
 *     to browse through the AmigaDOS filesystem and select one of
 *     the filenames found there.
 * 
 *     The FileIO argument is a pointer to a FileIOSupport structure,
 *     which is allocated and initialized for you via a call to 
 *     GetFileIOSupport().
 *     You may preset the FileIO parameters before calling this routine, 
 *     or you may leave them set at their default values.  See the FileIO
 *     documentation for complete details.
 * 
 *     The Window argument is the pointer to the window structure returned
 *     by a call to Intuition's OpenWindow() function.  As this routine
 *     opens a requester and requesters open in windows, you must have
 *     already opened a window before calling this routine, even if it's
 *     a window opened for no other purpose than to call this routine.
 * 
 *     This routine returns a BOOL value of TRUE or FALSE, depending on
 *     whether the user chose to accept or cancel the filename selection 
 *     operation.  If TRUE, the filename selected by the user can be
 *     found in the FileIO structure FileName[] field.  This filename
 *     will have all leading and trailing blanks removed (in case the
 *     user typed in a filename with extraneous spaces).  Likewise,
 *     the pathname to the disk and drawer can be found in the text
 *     fields DiskName[] and DrawerName[].  You can construct 
 *     the pathname using these text strings.  Also, you can call 
 *     BuildFileIOPathname() to build the pathname automatically.
 * 
 *     There's a *lot* more to be said about this function.  Please 
 *     read the documentation.
 * 
 *     NOTE:  This routine is not re-entrant.  What this means 
 *     is that if you have created a program that has more than one task,
 *     this routine cannot be called by more than one task at a time.
 *     This is not a problem for the grand majority of programs.
 *     But if you have some application that would require calling this 
 *     routine asynchronously from multiple tasks, you'll have to 
 *     implement some quick semaphore arrangement to avoid collisions.
 *     No big deal, actually.  See Exec semaphores for everything you need.
 * 
 * 
 * INPUTS
 *     FileIO = pointer to a FileIOSupport structure, as allocated
 *         via a call to GetFileIOSupport()
 *     Window = pointer to a Window structure, as created via a call
 *         to Intuition's OpenWindow()
 * 
 * 
 * RESULT
 *     TRUE if the user decided that the filename selection was successful,
 *     FALSE if the user chose to cancel the operation
 * 
 * 
 * EXAMPLE
 *     if (GetFileIOName(fileio, window))
 *         ProcessFileName(&fileio->FileName[0]);
 * 
 * 
 * BUGS
 *     None known, though there could be some, and the disk selection
 *     subsystem logic is not perfectly polished (though it's believed 
 *     to be bug-free).
 * 
 * 
 * SEE ALSO
 *     BuildFileIOPathname(), GetFileIOSupport(), ReleaseFileIO()
 */
BOOL GetFileIOName(fileio, window)
struct FileIOSupport *fileio;
struct Window *window;
{
    UBYTE *newtext;

    if ((fileio == NULL) || (window == NULL)) return(FALSE);

    OpenSaveLock = CurrentDir(NULL);
    CurrentDir(OpenSaveLock);
    fileio->DOSLock = OpenSaveLock;

    /* Get easily-accessible copies of the values that are referenced 
     * most often
     */
    OpenReq = &OpenReqSupport.Requester;
    OpenReqWindow = OpenReqSupport.Window = window;
    OpenReqFileIO = fileio;

    /* Set up the DoRequest() handlers */
    OpenReqSupport.GadgetHandler = HandleGadget;
    OpenReqSupport.StartRequest = StartOpenRequester;
    OpenReqSupport.NewDiskHandler = DiskInserted;
    OpenReqSupport.MouseMoveHandler = PropMouseMoves;

    /* Init the string gadget buffers */
    OpenNameTextInfo.Buffer = &fileio->FileName[0];
    OpenDrawerTextInfo.Buffer = &fileio->DrawerName[0];
    OpenDiskTextInfo.Buffer = &fileio->DiskName[0];

    /* Initialize the requester title */
    if ((ReqTitleText.IText = fileio->ReqTitle) == NULL)
        ReqTitleText.IText = DefaultReqTitle;
    ReqTitleText.LeftEdge
        = (OPEN_WIDTH - IntuiTextLength(&ReqTitleText)) >> 1;

    /* If this fileio doesn't have valid filenames,
     * then refresh the whole thing
     */
    if (FlagIsClear(fileio->Flags, GOOD_FILENAMES))
        {
        ResetNameText(TRUE);
        ResetDrawerText(TRUE);
        BuildVolumeTable(fileio);
        CopyString(&OpenReqFileIO->DiskName[0], CurrentVolumeName());
        }

    ResetNameText(FALSE);
    ResetDrawerText(FALSE);
    ResetDiskText(FALSE);

    StuffSelectNames(0);
    InitOpenProp(TRUE);

    /* Reset the double-click time variables */
    OpenClickSeconds = OpenClickMicros = 0;


    /* And now, do that requester. */
    DoRequest(&OpenReqSupport);
    /* Back, eh?  Wasn't that easy? */


    if (FlagIsSet(fileio->Flags, LOCK_GOTTEN))
        {
        UnLock(fileio->DOSLock);
        ClearFlag(fileio->Flags, LOCK_GOTTEN);
        }

    CurrentDir(OpenSaveLock);
    OpenReqFileIO = NULL;

    /* Strip any excess leading and trailing blanks off the final name */
    newtext = StripOuterSpace(&fileio->FileName[0], " ");
    CopyString(&fileio->FileName[0], newtext);

    if (OpenReqSupport.SelectedGadgetID != OPENGADGET_CANCEL)
        return(TRUE);
    return(FALSE);
}



/* *** BuildFileIOPathname() ************************************************
 * 
 * NAME
 *     BuildFileIOPathname  --  Build a file pathname using a FileIO struct
 * 
 * 
 * SYNOPSIS
 *     BuildFileIOPathname(FileIOSupport, Buffer);
 * 
 * 
 * FUNCTION
 *     Builds the text for a pathname using the FileName[], DrawerName[] and
 *     DiskName[] fields of the specified FileIOSupport structure
 *     after the support structure has been used in a successful call
 *     to GetFileIOName().  Writes the text into the Buffer.
 * 
 * 
 * INPUTS
 *     FileIOSupport = the address of a FileIOSupport structure
 *     Buffer = address of the buffer to receive the file pathname
 * 
 * 
 * RESULT
 *     None
 * 
 * 
 * SEE ALSO
 *     GetFileIOName()
 */
VOID BuildFileIOPathname(fileio, buffer)
struct FileIOSupport *fileio;
UBYTE *buffer;
{
    StripOuterSpace(&fileio->DiskName[0], " ");
    StripOuterSpace(&fileio->DrawerName[0], " ");
    StripOuterSpace(&fileio->FileName[0], " ");

    CopyString(buffer, &fileio->DiskName[0]);
    if (StringLength(&fileio->DrawerName[0]))
        {
        ConcatString(buffer, &fileio->DrawerName[0]);
        ConcatString(buffer, "/");
        }
    ConcatString(buffer, &fileio->FileName[0]);
}



/* *** AddFileIOName() ******************************************************
 * 
 * NAME
 *     AddFileIOName  --  Add a file name to the names in a FileIOSupport
 * 
 * 
 * SYNOPSIS
 *     AddFileIOName(FileIOSupport, FileName);
 * 
 * 
 * FUNCTION
 *     This routine adds a file name to the list of file names currently 
 *     in the specified FileIOSupport structure.  The next time the 
 *     FileIOSupport structure is used for a call to GetFileIOName(), the 
 *     new file name will apppear alphabetized in with the other file names.  
 *     
 *     This routine will most often be used after a call to GetFileIOName() 
 *     or some other routine where the user is allowed to specify the name 
 *     of a file to be opened for output.  If the file is opened 
 *     successfully, this routine will make sure that the name of the file 
 *     is in the FileIOSupport structure.  This is important if the output 
 *     file has been newly created; otherwise, without calling this 
 *     routine, the next time the FileIOSupport structure is used the new 
 *     file name would not appear even though the file exists.  If the name 
 *     is already in the list when you call AddFileIOName() then nothing 
 *     happens.  This allows you to call AddFileIOName() without worrying 
 *     about duplicate name redundancy.
 *     
 *     Here's a typical sequence of events leading up to a call to 
 *     AddFileIOName():
 *     
 *         First, get a FileIOSupport structure:
 *             fileio = GetFileIOSupport(...);
 *     
 *         When the user wants to write data, use GetFileIOName()
 *         to provide a convenient and consistent interface to
 *         the filesystem:
 *             goodfile = GetFileIOName(...);
 *     
 *         If the user has selected a name for output (in this example,
 *         goodfile will equal TRUE if the user selected a name), then
 *         open the file (possibly creating it) and then call 
 *         AddFileIOName() to make sure the name is in the FileIOSupport
 *         structure's list:
 *             if (goodfile)
 *                 {
 *                 UBYTE filename[80];
 *                 
 *                 BuildFileIOPathname(fileio, &filename[0]);
 *                 ... open filename, write it, close it ...
 *                 if (filename opened successfully)
 *                     AddFileIOName(fileio, &filename[0]);
 *                 }
 * 
 * 
 * INPUTS
 *     FileIOSupport = the address of a FileIOSupport structure
 *     FileName = the address of null-terminated text that is
 *         either a simple file name or a valid AmigaDOS pathname.
 * 
 * 
 * RESULT
 *     None
 * 
 * 
 * SEE ALSO
 *     GetFileIOName()
 *     GetFileIOSupport()
 */
VOID AddFileIOName(fileio, filename)
struct FileIOSupport *fileio;
UBYTE *filename;
{
    SHORT index, i, length;
    struct Remember *remember, *oldremember;
    UBYTE *nextentry;
    struct Remember *namekey;

    /* Does the filename start with a volume name?  If so, skip over it */
    index = IndexString(filename, ":");
    if (index >= 0) filename += index + 1;

    /* Does the filename start with a directory name?  If so, skip over it */
    do 
        {
        index = IndexString(filename, "/");
        if (index >= 0) filename += index + 1;
        }
    while (index >= 0);

    if (*filename == 0) return;

    /* Here, filename points to what is presumed to be a valid file name.
     * If it's found among the FileIOSupport's names, exit.
     * If it's not found in the list, add it.
     *
     * The current file names are stored in the fileio's Remember list.
     */
    remember = fileio->NameKey;
    oldremember = NULL;
    while (remember)
        {
        i = CompareUpperStrings(filename, remember->Memory);
        if (i < 0) goto ADD_FILENAME;
        if (i == 0) return;
        oldremember = remember;
        remember = remember->NextRemember;
        }

ADD_FILENAME:
    /* Name not on list, so add it now */
    length = StringLength(filename);

    namekey = NULL;
    if (nextentry = AllocRemember(&namekey, length + 1 + 1, NULL))
        {
        CopyString(nextentry, filename);
        *(nextentry + length + 1) = 0;
        if (oldremember) oldremember->NextRemember = namekey;
        else fileio->NameKey = namekey;
        /* This assignment is valid whether or not remember is NULL */
        namekey->NextRemember = remember;
        fileio->NameCount++;
        }
}



/* *** ReleaseFileIO() ******************************************************
 * 
 * NAME
 *     ReleaseFileIO  --  Release the FileIO structure and all local memory
 * 
 * 
 * SYNOPSIS
 *     ReleaseFileIO(FileIO);
 * 
 * 
 * FUNCTION
 *     Releases the FileIO structure by freeing all local memory attached
 *     to the structure and then freeing the structure itself.
 * 
 * 
 * INPUTS
 *     FileIO = the address of a FileIO structure
 * 
 * 
 * RESULT
 *     None
 * 
 * 
 * SEE ALSO
 *     GetFileIOSupport()
 */
VOID ReleaseFileIO(fileio)
struct FileIOSupport *fileio;
{
    if (fileio)
        {
        FreeRemember(&fileio->VolumeKey, TRUE);
        FreeRemember(&fileio->NameKey, TRUE);
        FreeMem(fileio, sizeof(struct FileIOSupport));
        }
}


