
/*
**
**  $VER: classbase.c 1.8 (10.11.97)
**  directory.datatype 1.8
**
**  Library routines for a DataTypes class
**
**  Written 1996/1997 by Roland 'Gizzy' Mainz
**  Original example source from David N. Junod
**
*/


/* main includes */
#include "classbase.h"


/****** directory.datatype/--datasheed-- *************************************
*
*   NAME
*       directory.datatype -- data type for directories
*
*   SUPERCLASS
*       text.datatype
*
*   FUNCTION
*       The directory data type, a sub-class of the text.datatype, is used
*       to load directories.
*
*   METHODS
*       OM_NEW -- Create a new text object from a directory file.  The
*           source may only be a file.
*
*       OM_GET -- The DTA_Methods attribute is created from local context.
*
*       OM_NOTIFY -- When notifying TDTA_WordSelected, directory.datatype
*           looks if the word selected matches a filename given.
*           If TRUE, the word will be expanded to full path and a following
*           /MAIN (example: word "ENV" in "Ram Disk:" would be
*           "Ram Disk:ENV/MAIN". This causes amigaguide.datatype to load
*           the specified node.
*
*       GM_GOACTIVE
*       GM_GOINACTIVE
*       DTM_CLEARSELECTED -- These methods have been patched to fix a bug in
*           text.datatype/DTM_CLEARSELECTED. This allows DTM_SELECT to work
*           properly.
*
*       DTM_ASYNCLAYOUT -- Fill in the line nodes with the contents of
*           the directory. Note that this method creates a notify task
*           which updates the contents each time changed.
*
*   ATTRIBUTES
*       DTA_Methods -- Set to the supported set of methods, based
*           on the set got from the superclass, extended with own methods.
*
*       DTA_Progressive -- If set to TRUE at OM_NEW time, the object
*           does progresive loading.
*
*       DTA_Data -- Pointer to the object itself. This is NULL when the
*           object is still loading.
*
*       DTA_CurrentLevel -- Current fill level of the object.
*
*   BUGS
*       A possible 0xA0 char in a filename causes TDTA_WordSelect to cut
*       the filename at this point (because 0xA0 is used as filename
*       terminator).
*       May or may not be fixed.
*
*       DTM_COPY is known to be BUGGY (copying without a DTM_ASYNCLAYOUT
*       causes Enforcer hits (read from 0UL)) because TDTA_Buffer is NULL.
*       Not fixed yet.
*       Will be fixed in the V2.0 release.
*
*       If any memory is allocated during mark operation or DTM_SELECT,
*       it will NOT be freed by my code (possible memory loss).
*       Not fixable.
*
*       The NOICONS preferences switch does match all "#?.info" files instead
*       of the "real" icon files.
*
*   HISTORY
*       V1.3
*         First public release.
*
*       V1.4
*         - Now supports DTM_COPY, DTM_WRITE, DTM_PRINT.
*
*         - Supports DTM_SELECT and DTM_CLEARSELECTED
*           The idea was born as I found the bug in
*           text.datatype/DTM_CLEARSELECTED which causes Enforcer hits.
*           I replaced it with my own code (see dispatch.c/Dispatch)).
*           I also introduced GM_GOACTIVE, GM_HANDLEINPUT and GM_GOINACTIVE
*           to clear the marked area at the begin of marking and redraws
*           the object after marking.
*           This may cause (small) memory loss (not serious to me,
*           my "smallest" system has 16MB). See "markabletextdtclass" in the
*           NOTES section downstairs.
*
*       V1.5
*         - At low mem time, the BOOPSI object migth be created (allocated
*           at OM_NEW time), but not the memory pool. This case causes havoc,
*           because some functions assumed to see a valid pool handle.
*           Never happed, but fixed.
*
*         - At OM_DISPOSE time, the text buffer is freed BEFORE the superclass
*           gets OM_SET, { TDTA_Buffer, NULL, TDTA_BufferLen, 0UL }.
*           Never happed, but fixed.
*
*         - The search used in OM_NOTIFY to find the line node which matches
*           the given word (TDTA_WordSelect) is now case sensitive (uses
*           strncmp, saving copying the filename => speed-up). This fixes
*           possible problems with case-sensitive filesystems (two files/dirs
*           have the same name with different case).
*
*         - After update by the notify task, the text buffer (TDTA_Buffer) is
*           not freed. It will be freed at DeletePool time, but this bug
*           wastes memory in object's live time.
*
*         - The notify task now waits until no notification occurs within a
*           half second, then it updates the data (previous behaviour was to
*           wait a half second and refresh the data for EACH notification).
*           Very busy directories are updated at least after 2 seconds.
*
*         - The file date being printed was the current system date.
*           Thanks to Rudi Chiarito (chiarito@cli.di.unipi.it) for reporting
*           and fixing the bug.
*
*           (I used a function from my SwitchWindow project which formats the
*           CURRENT time and date for the LEARN (logging) command).
*           Fixed.
*
*         - If something goes wrong inside the ExNext loop, memory loss was
*           caused because the temp text buffers (for line nodes) aren't
*           freed.
*           Fixed.
*
*         - The notify task now sends a OM_NOTIFY, { GA_ID, ...,
*           DTA_Title, "Updating", DTA_Busy, TRUE } msg to signal that
*           something will be changed.
*
*         - The nominal size (DTA_NominalHoriz) has been increased from 80 to
*           120 chars.
*
*       V1.6
*         - Now uses the new intuition/classes.(h|i) include files which
*           includes the ClassLibrary structure.
*
*         - Fixed the longstanding bug that a empty dir causes a
*           ERROR_NO_FREE_STORE error (AllocPooled( pool, 0UL ) returns NULL).
*           Now a blank char is used.
*
*         - Fixed the longstanding bug that unused space at the end of the
*           allocated text buffer was put into the clipboard (DTM_COPY) or
*           written to a file (DTM_WRITE).
*           This was done using new text buffer size calculations (old
*           calculations left 20%-40% of the text buffer unused).
*           Fixed.
*
*         - Fixed the longstanding bug: Low mem at OM_NEW time causes that
*           DTA_Methods did dot contain the correct set of supported methods.
*           Now the OM_NEW method correctly aborts.
*           Fixed.
*
*         - The nominal size (DTA_NominalHoriz) has been increased from 120 to
*           140 chars (should be enougth for now).
*           The V2.0 release will introduce dynamic calculations.
*
*         - Internal pool "page" size has been increased from 1024 to 4096
*           bytes.
*
*         - Any future version of text.datatype may reimplement and/or
*           redefine DTM_SELECT/DTM_CLEARSELECTED. Then, directory.datatype
*           would cause serious problems (No problems known with
*           "text.datatype 40.3 (07.12.93)") with the "mark" workaround.
*           If a text.datatype V41 or higher is detected, the own
*           DTM_CLEARSELECTED code is disabled. This affects also the
*           DTA_Methods attribute.
*
*       V1.7
*         I've made some changes and cleanup to match my own "DataTypes"
*         proposal. The following is NOT implemented, but requested by the
*         proposal:
*         * DTA_Obj#? and TDTA_Buffer#? attributes are not set at OM_NEW
*           time.
*
*         * This release is not tested under various conditions (like low mem
*           etc.)
*
*         * The autodoc does not contain all of the information requested by
*           proposal.
*
*        Changes (many are forced by the proposal):
*         - DTA_Name is used for the directory name instead of DTA_ObjName.
*
*         - Now allows DTST_RAM as DTA_SourceType. DTA_Name will contain the
*           directory name. This will allow tools to use this datatype as
*           "easy-to-use" directory viewer.
*           Changing the DTA_Name attribute will now update the object
*           to show the new directory.
*
*         - Now directory.datatype rejects any attempts to create subclasses
*           of directory.datatype (not implemented yet) by rejecting the
*           OM_NEW method.
*
*         - Now uses proper ObtainSemaphore/ReleaseSemaphore for instance
*           data (including DTSpecialInfo), except for those instance data 
*           elements which are protected by other instances (e.g. used only by
*           by one task like (did -> did_InMarkMode).
*
*         - Now used datatypes SetDTAttrs/GetDTAttrs instead of
*           SetAttrs/GetAttr (they were used in cases when no GI context
*           was submitted).
*
*         - Misc autodoc cleanup and update. The autodoc has now a TOC
*           (Table Of Contents).
*
*         - Now introduces a preferences file. At object creation time
*           (OM_NEW), the datatype looks for a
*           "Classes/DataTypes/directory.prefs" prefs file or local env
*           variable. See directory.datatype/preferences autodoc section
*           for details.
*           Changes:
*           Notification, user id, group id and amigaguide link support is
*           now turned off for default and can be turned on by the
*           prefs file. This matches the future directory.datatype V2.x
*           behaviour.
*
*         - Different ways of mattching a file have been implemented:
*           NOICONS        -- Reject all wb icons (uses a custom #?.info
*                             check)
*           PATTERN        -- Match pattern (like CBM List)
*           ACCEPTPATTERN  -- Like CBM RequestFile
*           REJECTPATTERN  -- Like CBM RequestFile
*
*           See autodoc preferences section for more details.
*
*         - Implemented UID/GID to "real" name conversion. If you turns
*           the GROUPS/USERS flags on, directory.datatype sends packets
*           to the filesystem to convert fs object name -> "real" user/
*           group name.
*           This is __EXPERIMENTAL__ and may be removed in a later version.
*           The format and function of the envoy fs packets 20002 (uid2uname)
*           and 20003 (gid2gname) was discovered with SnoopDOS and other
*           debugging utilities. This packets are currenty envoy ds PRIVATE.
*
*           The implementation may not be correct. If you want to include
*           the code in your projects, CONTACT ME (because then I may have
*           more information.)
*
*           The current implementation is not very nice. Many time is wasted
*           by the code ((Create|Delete)MsgPort, (Alloc|Free)DOSObject etc.),
*           any error is ignored.
*
*       V1.8
*         - Added stack swapping code which avoids crashes because of low 
*           stack.
*
*         - Now the notify process has a stack size of 16384 bytes.
*
*         - How uses the lock got from DTA_Handle. Saves one dos.library/Lock
*           and one UnLock
*
*         - Removed the ugly "feature" that someone can set DTA_Name and
*           that this updates the data.
*           The feature was incompatible to the normal datatypes behaviour.
*
*         - Now some datatypes.library V45 calls are used. This saves some
*           bytes of code in this datatype :-)
*
*         - Cleaned up the startup code.
*
*         - Fixed a bug in the startup code, which causes that one ROM lib
*           wasn't CloseLibrary'ed.
*
*         - If a datatype of a directory entry cannot be obtained, the field
*           now shows the cause (error string).
*
*         - OM_DISPOSE now preserves Result2 (IoErr()) value to avoid that it
*           gets lost.
*
*         - Implemented progressive loading of data (datatypes.library V45
*           progressive support), including
*           DTA_Progressive, DTA_Data and DTA_CurrentLevel
*           THIS SUPPORT IS CURRENTLY EXPERIMENTAL
*           datatypes.library V45.4 is recommented
*
*   NOTES
*       The DTM_CLEARSELECTED patch has also been put into a seperate project
*       called "markabletextdtclass".
*
*   SEE ALSO
*       text.datatype, amigaguide.datatype,
*       markabletextdtclass
*
*******************************************************************************
*
*/

/*****************************************************************************/

DISPATCHERFLAGS
struct IClass *ObtainDirectoryEngine( REGA6 struct ClassBase *cb )
{
    return( (cb -> cb_Lib . cl_Class) );
}

/*****************************************************************************/

DISPATCHERFLAGS
struct Library *LibInit( REGD0 struct ClassBase *cb, REGA0 BPTR seglist, REGA6 struct ExecBase *sysbase )
{
    cb -> cb_SegList = seglist;
    cb -> cb_SysBase = sysbase;

    InitSemaphore( (&(cb -> cb_Lock)) );

    if( (cb -> cb_SysBase -> LibNode . lib_Version) >= 39UL )
    {
      /* Obtain ROM libs */
      if( cb -> cb_UtilityBase = OpenLibrary( "utility.library", 39UL ) )
      {
        if( cb -> cb_DOSBase = OpenLibrary( "dos.library", 39UL ) )
        {
          if( cb -> cb_GfxBase = OpenLibrary( "graphics.library",  39UL ) )
          {
            if( cb -> cb_IntuitionBase = OpenLibrary( "intuition.library", 39UL ) )
            {
              return( (&(cb -> cb_Lib . cl_Lib)) );

#ifdef COMMENTED_OUT
              CloseLibrary( IntuitionBase );
#endif /* COMMENTED_OUT */
            }

            CloseLibrary( GfxBase );
          }

          CloseLibrary( DOSBase );
        }

        CloseLibrary( UtilityBase );
      }
    }

    return( NULL );
}

/*****************************************************************************/

DISPATCHERFLAGS
LONG LibOpen( REGA6 struct ClassBase *cb )
{
    LONG retval = (LONG)cb;
    BOOL success = TRUE;

    ObtainSemaphore( (&(cb -> cb_Lock)) );

    /* Use an internal use counter */
    cb -> cb_Lib . cl_Lib . lib_OpenCnt++;
    cb -> cb_Lib . cl_Lib . lib_Flags &= ~LIBF_DELEXP;

    if( (cb -> cb_Lib . cl_Lib . lib_OpenCnt) == 1U )
    {
      if( (cb -> cb_Lib . cl_Class) == NULL )
      {
        success = FALSE;

        if( cb -> cb_DataTypesBase = OpenLibrary( "datatypes.library", 45UL ) )
        {
          if( cb -> cb_SuperClassBase = OpenLibrary( "datatypes/text.datatype", 39UL ) )
          {
            if( cb -> cb_Lib . cl_Class = initClass( cb ) )
            {
              success = TRUE;
            }
          }
        }
      }
    }

    if( !success )
    {
      CloseLibrary( (cb -> cb_SuperClassBase) );
      CloseLibrary( (cb -> cb_DataTypesBase) );

      cb -> cb_DataTypesBase = cb -> cb_SuperClassBase = NULL;

      (cb -> cb_Lib . cl_Lib . lib_OpenCnt)--;

      retval = 0L;
    }

    ReleaseSemaphore( (&(cb -> cb_Lock)) );

    return( retval );
}

/*****************************************************************************/

DISPATCHERFLAGS
LONG LibClose( REGA6 struct ClassBase *cb )
{
    LONG retval = 0L;

    ObtainSemaphore( (&(cb -> cb_Lock)) );

    if( cb -> cb_Lib . cl_Lib . lib_OpenCnt )
    {
      (cb -> cb_Lib . cl_Lib . lib_OpenCnt)--;
    }

    if( ((cb -> cb_Lib . cl_Lib . lib_OpenCnt) == 0U) && (cb -> cb_Lib . cl_Class) )
    {
      if( FreeClass( (cb -> cb_Lib . cl_Class) ) )
      {
        cb -> cb_Lib . cl_Class = NULL;

        CloseLibrary( (cb -> cb_SuperClassBase) );
        CloseLibrary( (cb -> cb_DataTypesBase) );
      }
      else
      {
        cb -> cb_Lib . cl_Lib . lib_Flags |= LIBF_DELEXP;
      }
    }

    if( cb -> cb_Lib . cl_Lib . lib_Flags & LIBF_DELEXP )
    {
      retval = LibExpunge( cb );
    }

    ReleaseSemaphore( (&(cb -> cb_Lock)) );

    return( retval );
}

/*****************************************************************************/

DISPATCHERFLAGS
LONG LibExpunge( REGA6 struct ClassBase *cb )
{
    BPTR seg = cb -> cb_SegList;

    Remove( (&(cb -> cb_Lib . cl_Lib . lib_Node)) );

    CloseLibrary( (cb -> cb_IntuitionBase) );
    CloseLibrary( (cb -> cb_GfxBase) );
    CloseLibrary( (cb -> cb_DOSBase) );
    CloseLibrary( (cb -> cb_UtilityBase) );

    FreeMem( (APTR)((ULONG)(cb) - (ULONG)(cb -> cb_Lib . cl_Lib . lib_NegSize)), (ULONG)((cb -> cb_Lib . cl_Lib . lib_NegSize) + (cb -> cb_Lib . cl_Lib . lib_PosSize)) );

    return( (LONG)seg );
}


