#include <exec/types.h>
#include <exec/memory.h>
#include <proto/exec.h>
#include <intuition/intuition.h>
#include <intuition/gadgetclass.h>
#include <proto/intuition.h>
#include <libraries/gadtools.h>
#include <proto/gadtools.h>
#include <dos/dos.h>
#include <dos/exall.h>
#include <dos/dostags.h>
#include <proto/dos.h>
#include <graphics/gfx.h>
#include <clib/graphics_protos.h>
#include <clib/alib_protos.h>
#include <libraries/asl.h>
#include <clib/asl_protos.h>

#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include "rp.h"

#define ALL_REACTION_CLASSES
#include <reaction/reaction.h>

#define LIMIT_DIRS        8196
#define MAX_FILES (128 * 1024)
#define TRUNCATE            39
#define GRANULARITY_DIRS    10
#define GRANULARITY_FILES   10

/* Limitations:
   LIMIT_DIRS (top-level list entries).
   MAX_FILES (total files/dirs in path). */

MODULE void pop(void);
MODULE void push(STRPTR name, ULONG theindex);
MODULE void subdir(ULONG theindex);
MODULE void parent(void);
MODULE void root(void);
MODULE void pathasl(void);
MODULE void showduplicates(void);
MODULE void addduplicate(STRPTR path, STRPTR filename);
MODULE void writeline(void);
MODULE void killduplist(void);
MODULE void killsizelist(void);
MODULE void ghost(void);
MODULE void unghost(void);
MODULE void scale(ULONG bytes);
MODULE void __inline progressbar(ULONG level);

#define UNITOPTIONS 3 // counting from 0
MODULE STRPTR UnitOptions[UNITOPTIONS + 1] =
{   "Bytes",
    "Kilobytes",
    "Megabytes",
    "Blocks"
};

MODULE struct ColumnInfo SizeColumnInfo[] =
{ { 75,            // WORD   ci_Width
    "Pathname",    // STRPTR ci_Title
    0,             // ULONG  ci_Flags
  },
  { 25,
    "Size",
    0, /* Last column must not have CIF_DRAGGABLE set (CIF_DRAGGABLE applies
          to the right border of the relevant column). */
  },
  { -1, (STRPTR) ~0, -1
} },
DupColumnInfo[] =
{ { 40,            // WORD   ci_Width
    "Pathname",    // STRPTR ci_Title
    0,             // ULONG  ci_Flags
  },
  { 10,
    "Byte size",
    0,
  },
  { 10,
    "Date",
    0,
  },
  { 10,
    "Time",
    0,
  },
  { 30,
    "Version",
    0, /* Last column must not have CIF_DRAGGABLE set (CIF_DRAGGABLE applies
          to the right border of the relevant column). */
  },
  { -1, (STRPTR) ~0, -1
} };

IMPORT struct TextAttr     Topaz8;
IMPORT SBYTE               page;
IMPORT TEXT                globalname[PATHNAMEFIELD + 1],
                           weekdaystring[10],
                           datestring[10],
                           timestring[9],
                           asldir[PATHNAMEFIELD + 1],
                           aslresult[PATHNAMEFIELD + 1],
                           IOBuffer[LONGESTFIELD + 1];
IMPORT struct SharedStruct shared;
IMPORT struct Screen*      ScreenPtr;
IMPORT struct Window*      MainWindowPtr;
IMPORT struct ExAllData*   EADataPtr;
IMPORT struct Menu*        MenuPtr;
IMPORT Object*             WinObject[FUNCTIONS + 1];
IMPORT ULONG               increment;

AGLOBAL struct Gadget*     size_gadgets[GIDS_8 + 1];

MODULE ABOOL               DupNodes       = FALSE,
                           SizeNodes      = FALSE,
                           EmptyDupNodes  = FALSE,
                           EmptySizeNodes = FALSE,
                           ram            = FALSE;
MODULE struct List         SizeList,
                           DupList,
                           EmptySizeList,
                           EmptyDupList;
MODULE STRPTR*             PathnamePtr;
MODULE STRPTR*             FilenamePtr;
MODULE STRPTR*             StackPtr;
MODULE STRPTR*             DirNamePtr;
MODULE ABOOL*              IsDup;
MODULE BPTR                TempHandle     = NULL,
                           DirHandle      = NULL,
                           LogFileHandle  = NULL;
MODULE TEXT                logstring[PATHNAMEFIELD + 1],
                           liststring[5][PATHNAMEFIELD + 1];
MODULE UBYTE               commastring[13 + 1],
                           pathlength;
MODULE ULONG               DirNamesAllocated  = 0,
                           FileNamesAllocated = 0,
                           files              = 0, // how many files have been found. Counting from 1.
                           status             = STATUS_READY;

MODULE struct
{   ULONG entries, total, free, capacity, stackentries,
          blocksize, view, numfiles, numdirs,
          log, finddup, slack; // really booleans
    TEXT  oldpath[PATHNAMEFIELD + 1], path[PATHNAMEFIELD + 1], logfile[PATHNAMEFIELD + 1];
    LONG  type[LIMIT_DIRS];
    ULONG bytes[LIMIT_DIRS], topdir[LIMIT_DIRS];
} size =
{   0, 0, 0, 0, 0,
    0, 0, 0, 0,
    (ULONG) TRUE, (ULONG) FALSE, (ULONG) FALSE,
    "RAM:", "RAM:", "RAM:ReportPlus.txt"
};

AGLOBAL void size_init(void)
{   struct Node* ListBrowserNodePtr;

    NewList(&SizeList);
    NewList(&DupList);

    /* Create a 2-column listbrowser list with only one node containing
    only column tags. */
    NewList(&EmptySizeList);
    if (!(ListBrowserNodePtr = AllocListBrowserNode
    (   2,                  // columns
        /* LBNCA_ tags are those that apply to the specific column. */
        LBNA_Column,        0,
        LBNA_Column,        1,
        TAG_END
    )))
    {   rq("Can't create ReAction listbrowser.gadget node(s)!");
    }
    AddTail(&EmptySizeList, ListBrowserNodePtr); // AddTail() has no return code
    EmptySizeNodes = TRUE;

    /* Create a 5-column listbrowser list with only one node containing
    only column tags. */
    NewList(&EmptyDupList);
    if (!(ListBrowserNodePtr = AllocListBrowserNode
    (   5,                  // columns
        /* LBNCA_ tags are those that apply to the specific column. */
        LBNA_Column,        0,
        LBNA_Column,        1,
        LBNA_Column,        2,
        LBNA_Column,        3,
        LBNA_Column,        4,
        TAG_END
    )))
    {   rq("Can't create ReAction listbrowser.gadget node(s)!");
    }
    AddTail(&EmptyDupList, ListBrowserNodePtr); // AddTail() has no return code
    EmptyDupNodes = TRUE;
}

AGLOBAL void size1(void)
{   struct Hook         Hook8Struct;
    struct List         ChooserList;
    struct ChooserNode* ChooserNodePtr[UNITOPTIONS + 1];
    ULONG               i;

    NewList(&ChooserList);
    for (i = 0; i <= UNITOPTIONS; i++)
    {   if (!(ChooserNodePtr[i] = (struct ChooserNode *) AllocChooserNode(CNA_Text, UnitOptions[i], TAG_DONE)))
        {   rq("Can't allocate chooser node!");
        }
        AddTail(&ChooserList, (struct Node *) ChooserNodePtr[i]);
    } // automatically freed by ReAction at DisposeObject() time

    InitHook(&Hook8Struct, Hook8Func, NULL);

    /* Create the window object. */
    lockscreen();

    /* We can't just use EmptyList for an empty columnar list, we
    actually need a node (with the LBNA_Column tags) to be present.

    LISTBROWSER_ScrollRaster seems not to be usable with
    multi-column lists. */

    if (!(WinObject[8] =          NewObject(WINDOW_GetClass(), NULL,
    // window
    WA_PubScreen,                 ScreenPtr,
    WA_ScreenTitle,               "Report+",
    WA_Title,                     "Report+: Path Size Report",
    WA_Activate,                  TRUE,
    WA_DepthGadget,               TRUE,
    WA_DragBar,                   TRUE,
    WA_CloseGadget,               TRUE,
    WA_SizeGadget,                TRUE,
    WA_IDCMP,                     IDCMP_RAWKEY,
    WINDOW_IDCMPHook,             &Hook8Struct,
    WINDOW_IDCMPHookBits,         IDCMP_RAWKEY,
    WINDOW_MenuStrip,             MenuPtr,
    WINDOW_Position,              WPOS_CENTERSCREEN,
    WINDOW_ParentGroup,           size_gadgets[GID_8_LY1] =
    NewObject
    (       LAYOUT_GetClass(), NULL,
            // root-layout
            LAYOUT_Orientation,        LAYOUT_ORIENT_VERT,
            LAYOUT_SpaceOuter,         TRUE,
            LAYOUT_DeferLayout,        TRUE,
            LAYOUT_AddChild,
            NewObject
            (   LAYOUT_GetClass(),     NULL,
                LAYOUT_Orientation,    LAYOUT_ORIENT_HORIZ,
                LAYOUT_SpaceOuter,     TRUE,
                LAYOUT_VertAlignment,  LALIGN_CENTER,
                LAYOUT_HorizAlignment, LALIGN_CENTER,
                LAYOUT_BevelStyle,     BVS_NONE,
                LAYOUT_AddImage,
                NewObject
                (   LABEL_GetClass(),  NULL,
                    LABEL_Text,        "_Path:",
                    LABEL_Justification,LJ_LEFT,
                TAG_END),
                CHILD_WeightedWidth,   0,
                LAYOUT_AddChild,       size_gadgets[GID_8_ST1] =
                NewObject
                (   STRING_GetClass(), NULL,
                    GA_ID,             GID_8_ST1,
                    GA_RelVerify,      TRUE,
                    STRINGA_TextVal,   size.path,
                    STRINGA_MinVisible,20,
                TAG_END),
                LAYOUT_AddChild,       size_gadgets[GID_8_BU1] =
                NewObject(NULL,        "button.gadget",
                    GA_ID,             GID_8_BU1,
                    GA_RelVerify,      TRUE,
                    BUTTON_AutoButton, BAG_POPFILE,
                TAG_END),
                CHILD_WeightedWidth,   0,
            TAG_END),
            CHILD_WeightedHeight,      0,
            LAYOUT_AddChild,           NewObject(LAYOUT_GetClass(), NULL,
                LAYOUT_Orientation,    LAYOUT_ORIENT_HORIZ,
                LAYOUT_SpaceOuter,     TRUE,
                LAYOUT_VertAlignment,  LALIGN_CENTER,
                LAYOUT_HorizAlignment, LALIGN_CENTER,
                LAYOUT_BevelStyle,     BVS_NONE,
                LAYOUT_ShrinkWrap,     TRUE,
                LAYOUT_AddChild,       size_gadgets[GID_8_CB1] =
                NewObject
                (   CHECKBOX_GetClass(),NULL,
                    // checkbox
                    GA_ID,             GID_8_CB1,
                    GA_RelVerify,      TRUE,
                    GA_Text,           "_Log to:",
                    GA_Selected,       (BOOL) size.log,
                    TAG_END
                ),
                CHILD_WeightedWidth,   0,
                LAYOUT_AddChild,       size_gadgets[GID_8_ST2] =
                                       NewObject(STRING_GetClass(), NULL,
                    // string        
                    GA_ID,             GID_8_ST2,
                    GA_RelVerify,      TRUE,
                    STRINGA_TextVal,   size.logfile,
                    STRINGA_MinVisible,20,
                    GA_Disabled,       !size.log,
                    TAG_END
                ),
                LAYOUT_AddChild,       size_gadgets[GID_8_BU3] =
                NewObject
                (   NULL,              "button.gadget",
                    // button
                    GA_ID,             GID_8_BU3,
                    GA_RelVerify,      TRUE,
                    BUTTON_AutoButton, BAG_POPFILE,
                    GA_Disabled,       !size.log,
                    TAG_END
                ),
                CHILD_WeightedWidth,   0,
                TAG_END
            ),
            CHILD_WeightedHeight,      0,
            LAYOUT_AddChild,
            NewObject
            (   LAYOUT_GetClass(),     NULL,
                LAYOUT_Orientation,    LAYOUT_ORIENT_VERT,
                LAYOUT_SpaceOuter,     TRUE,
                LAYOUT_VertAlignment,  LALIGN_CENTER,
                LAYOUT_HorizAlignment, LALIGN_CENTER,
                LAYOUT_BevelStyle,     BVS_FIELD,
                LAYOUT_AddChild,
                NewObject
                (   LAYOUT_GetClass(),     NULL,
                    LAYOUT_Orientation,    LAYOUT_ORIENT_HORIZ,
                    LAYOUT_SpaceOuter,     TRUE,
                    LAYOUT_VertAlignment,  LALIGN_CENTER,
                    LAYOUT_HorizAlignment, LALIGN_CENTER,
                    LAYOUT_BevelStyle,     BVS_NONE,
                    LAYOUT_AddChild,       size_gadgets[GID_8_BU6] =
                    NewObject
                    (   NULL,              "button.gadget",
                        GA_ID,             GID_8_BU6,
                        GA_RelVerify,      TRUE,
                        GA_Text,           "_Root",
                        TAG_END
                    ),
                    CHILD_WeightedWidth,   50,
                    LAYOUT_AddChild,       size_gadgets[GID_8_BU7] =
                    NewObject             
                    (   NULL,              "button.gadget",
                        GA_ID,             GID_8_BU7,
                        GA_RelVerify,      TRUE,
                        GA_Text,           "P_arent",
                        TAG_END
                    ),
                    CHILD_WeightedWidth,   50,
                TAG_END),
                CHILD_WeightedHeight,      0,
                LAYOUT_AddChild,           size_gadgets[GID_8_LB2] =
                NewObject
                (   LISTBROWSER_GetClass(),NULL,
                    GA_ID,                 GID_8_LB2,
                    GA_RelVerify,          TRUE,
                    GA_TextAttr,           &Topaz8,
                    LISTBROWSER_Labels,    (ULONG) &EmptySizeList,
                    LISTBROWSER_ColumnInfo,(ULONG) &SizeColumnInfo,
                    LISTBROWSER_ColumnTitles,TRUE,
                    LISTBROWSER_MinVisible,6,
                TAG_END),
                CHILD_WeightedHeight,      100,
                CHILD_MinHeight,           80,
                LAYOUT_AddChild,
                NewObject
                (   LAYOUT_GetClass(),     NULL,
                    LAYOUT_Orientation,    LAYOUT_ORIENT_HORIZ,
                    LAYOUT_SpaceOuter,     TRUE,
                    LAYOUT_VertAlignment,  LALIGN_LEFT,
                    LAYOUT_HorizAlignment, LALIGN_RIGHT,
                    LAYOUT_BevelStyle,     BVS_NONE,
                    LAYOUT_AddChild,
                    NewObject
                    (   LAYOUT_GetClass(),     NULL,
                        LAYOUT_Orientation,    LAYOUT_ORIENT_VERT,
                        LAYOUT_SpaceOuter,     TRUE,
                        LAYOUT_VertAlignment,  LALIGN_CENTER,
                        LAYOUT_HorizAlignment, LALIGN_RIGHT,
                        LAYOUT_BevelStyle,     BVS_NONE,
                        LAYOUT_AddImage,
                        NewObject
                        (   LABEL_GetClass(),  NULL,
                            LABEL_Text,        "Files:",
                            LABEL_Justification,LJ_CENTRE,
                        TAG_END),
                        CHILD_WeightedHeight,  0,
                        LAYOUT_AddImage,
                        NewObject
                        (   LABEL_GetClass(),  NULL,
                            LABEL_Text,        "Directories:",
                            LABEL_Justification,LJ_CENTRE,
                        TAG_END),
                        CHILD_WeightedHeight,  0,
                        LAYOUT_AddImage,
                        NewObject
                        (   LABEL_GetClass(),  NULL,
                            LABEL_Text,        "Block size:",
                            LABEL_Justification,LJ_CENTRE,
                        TAG_END),
                        CHILD_WeightedHeight,  0,
                    TAG_END),
                    CHILD_WeightedWidth,       0,
                    LAYOUT_AddChild,
                    NewObject
                    (   LAYOUT_GetClass(),     NULL,
                        LAYOUT_Orientation,    LAYOUT_ORIENT_VERT,
                        LAYOUT_SpaceOuter,     TRUE,
                        LAYOUT_VertAlignment,  LALIGN_CENTER,
                        LAYOUT_HorizAlignment, LALIGN_RIGHT,
                        LAYOUT_BevelStyle,     BVS_NONE,
                        LAYOUT_AddChild,       size_gadgets[GID_8_ST6] =
                        NewObject
                        (   STRING_GetClass(), NULL,
                            GA_ID,             GID_8_ST6,
                            GA_ReadOnly,       TRUE,
                            STRINGA_TextVal,   "-",
                            STRINGA_MinVisible,13,
                            STRINGA_Justification,GACT_STRINGRIGHT,
                            GA_TextAttr,       &Topaz8,
                        TAG_END),
                        CHILD_WeightedHeight,  0,
                        LAYOUT_AddChild,       size_gadgets[GID_8_ST7] =
                        NewObject
                        (   STRING_GetClass(), NULL,
                            GA_ID,             GID_8_ST7,
                            GA_ReadOnly,       TRUE,
                            STRINGA_TextVal,   "-",
                            STRINGA_MinVisible,13,
                            STRINGA_Justification,GACT_STRINGRIGHT,
                            GA_TextAttr,       &Topaz8,
                        TAG_END),
                        CHILD_WeightedHeight,  0,
                        LAYOUT_AddChild,       size_gadgets[GID_8_ST8] =
                        NewObject
                        (   STRING_GetClass(), NULL,
                            GA_ID,             GID_8_ST8,
                            GA_ReadOnly,       TRUE,
                            STRINGA_TextVal,   "-",
                            STRINGA_MinVisible,13,
                            STRINGA_Justification,GACT_STRINGRIGHT,
                            GA_TextAttr,       &Topaz8,
                        TAG_END),
                        CHILD_WeightedHeight,  0,
                    TAG_END),
                    CHILD_WeightedWidth,       0,
                    LAYOUT_AddImage,           NewObject
                    (   LABEL_GetClass(),      NULL,
                        LABEL_Text,            "",
                        TAG_END
                    ),
                    CHILD_WeightedWidth,       100,
                    LAYOUT_AddChild,
                    NewObject
                    (   LAYOUT_GetClass(),     NULL,
                        LAYOUT_Orientation,    LAYOUT_ORIENT_VERT,
                        LAYOUT_SpaceOuter,     TRUE,
                        LAYOUT_VertAlignment,  LALIGN_CENTER,
                        LAYOUT_HorizAlignment, LALIGN_RIGHT,
                        LAYOUT_BevelStyle,     BVS_NONE,
                        LAYOUT_AddImage,
                        NewObject
                        (   LABEL_GetClass(),  NULL,
                            LABEL_Text,        "Used in path:",
                            LABEL_Justification,LJ_RIGHT,
                        TAG_END),
                        CHILD_WeightedHeight,  0,
                        LAYOUT_AddImage,
                        NewObject
                        (   LABEL_GetClass(),  NULL,
                            LABEL_Text,        "Free on volume:",
                            LABEL_Justification,LJ_RIGHT,
                        TAG_END),
                        CHILD_WeightedHeight,  0,
                        LAYOUT_AddImage,
                        NewObject
                        (   LABEL_GetClass(),  NULL,
                            LABEL_Text,        "Volume capacity:",
                            LABEL_Justification,LJ_RIGHT,
                        TAG_END),
                        CHILD_WeightedHeight,  0,
                    TAG_END),
                    CHILD_WeightedWidth,       0,
                    LAYOUT_AddChild,
                    NewObject
                    (   LAYOUT_GetClass(),     NULL,
                        LAYOUT_Orientation,    LAYOUT_ORIENT_VERT,
                        LAYOUT_SpaceOuter,     TRUE,
                        LAYOUT_VertAlignment,  LALIGN_CENTER,
                        LAYOUT_HorizAlignment, LALIGN_RIGHT,
                        LAYOUT_BevelStyle,     BVS_NONE,
                        LAYOUT_AddChild,       size_gadgets[GID_8_ST4] =
                        NewObject
                        (   STRING_GetClass(), NULL,
                            GA_ID,             GID_8_ST4,
                            GA_ReadOnly,       TRUE,
                            STRINGA_TextVal,   "-",
                            STRINGA_MinVisible,13,
                            STRINGA_Justification,GACT_STRINGRIGHT,
                            GA_TextAttr,       &Topaz8,
                        TAG_END),
                        CHILD_WeightedHeight,  0,
                        LAYOUT_AddChild,       size_gadgets[GID_8_ST5] =
                        NewObject
                        (   STRING_GetClass(), NULL,
                            GA_ID,             GID_8_ST5,
                            GA_ReadOnly,       TRUE,
                            STRINGA_TextVal,   "-",
                            STRINGA_MinVisible,13,
                            STRINGA_Justification,GACT_STRINGRIGHT,
                            GA_TextAttr,       &Topaz8,
                        TAG_END),
                        CHILD_WeightedHeight,  0,
                        LAYOUT_AddChild,       size_gadgets[GID_8_ST3] =
                        NewObject
                        (   STRING_GetClass(), NULL,
                            GA_ID,             GID_8_ST3,
                            GA_ReadOnly,       TRUE,
                            STRINGA_TextVal,   "-",
                            STRINGA_MinVisible,13,
                            STRINGA_Justification,GACT_STRINGRIGHT,
                            GA_TextAttr,       &Topaz8,
                        TAG_END),
                        CHILD_WeightedHeight,  0,
                    TAG_END),
                    CHILD_WeightedWidth,       0,
                TAG_END),
                CHILD_WeightedHeight,          0,
            TAG_END),
            CHILD_WeightedHeight,              70,
            LAYOUT_AddChild,
            NewObject
            (   LAYOUT_GetClass(),     NULL,
                LAYOUT_Orientation,    LAYOUT_ORIENT_HORIZ,
                LAYOUT_SpaceOuter,     TRUE,
                LAYOUT_VertAlignment,  LALIGN_CENTER,
                LAYOUT_HorizAlignment, LALIGN_CENTER,
                LAYOUT_BevelStyle,     BVS_NONE,
                LAYOUT_AddImage,
                NewObject
                (   LABEL_GetClass(),  NULL,
                    // label
                    LABEL_Text,        "_View as:",
                    LABEL_Justification,LJ_RIGHT,
                TAG_END),
                CHILD_WeightedWidth,   0,
                LAYOUT_AddChild,       size_gadgets[GID_8_CH1] =
                NewObject
                (   CHOOSER_GetClass(),NULL,
                    GA_ID,             GID_8_CH1,
                    GA_RelVerify,      TRUE,
                    CHOOSER_PopUp,     TRUE,
                    CHOOSER_Labels,    &ChooserList,
                    CHOOSER_Selected,  size.view,
                    TAG_END
                ),
                CHILD_WeightedWidth,   0,
                LAYOUT_AddImage,       NewObject
                (   LABEL_GetClass(),  NULL,
                    LABEL_Text,        "",
                    TAG_END
                ),
                CHILD_WeightedWidth,   100,
                LAYOUT_AddChild,       size_gadgets[GID_8_CB2] =
                NewObject
                (   CHECKBOX_GetClass(),NULL,
                    // checkbox
                    GA_ID,             GID_8_CB2,
                    GA_RelVerify,      TRUE,
                    GA_Text,           "Include _slack?",
                    GA_Selected,       (BOOL) size.slack,
                TAG_END),
                CHILD_WeightedWidth,   0,
                LAYOUT_AddChild,       size_gadgets[GID_8_CB3] =
                NewObject
                (   CHECKBOX_GetClass(),NULL,
                    // checkbox
                    GA_ID,             GID_8_CB3,
                    GA_RelVerify,      TRUE,
                    GA_Text,           "Find _duplicates?",
                    GA_Selected,       (BOOL) size.finddup,
                    TAG_END
                ),
                CHILD_WeightedWidth,   0,
            TAG_END),
            CHILD_WeightedHeight,     0,
            LAYOUT_AddChild,          size_gadgets[GID_8_LB1] =
            NewObject
            (   LISTBROWSER_GetClass(),NULL,
                GA_ID,                GID_8_LB1,
                GA_ReadOnly,          TRUE,
                GA_TextAttr,          &Topaz8,
                GA_Disabled,          !size.finddup,
                LISTBROWSER_Labels,   (ULONG) &EmptyDupList,
                LISTBROWSER_ColumnInfo,(ULONG) &DupColumnInfo,
                LISTBROWSER_ColumnTitles,TRUE,
                LISTBROWSER_HorizontalProp,TRUE,
                LISTBROWSER_VirtualWidth,620 + 155,
                LISTBROWSER_MinVisible,6,
            TAG_END),
            CHILD_WeightedHeight,     30,
            CHILD_MinHeight,          80,
            LAYOUT_AddChild,          size_gadgets[GID_8_FG1] =
            NewObject
            (   FUELGAUGE_GetClass(), NULL,
                GA_ID,                GID_8_FG1,
                GA_Text,              "Ready.",
                FUELGAUGE_Level,      0,
                FUELGAUGE_Percent,    FALSE,
                FUELGAUGE_Justification,FGJ_CENTER,
            TAG_END),
            CHILD_WeightedHeight,     0,
            LAYOUT_AddChild,
            NewObject
            (   LAYOUT_GetClass(),    NULL,
                LAYOUT_Orientation,   LAYOUT_ORIENT_HORIZ,
                LAYOUT_SpaceOuter,    TRUE,
                LAYOUT_VertAlignment, LALIGN_CENTER,
                LAYOUT_HorizAlignment,LALIGN_CENTER,
                LAYOUT_BevelStyle,    BVS_NONE,
                LAYOUT_AddChild,      size_gadgets[GID_8_BU4] =
                NewObject
                (   NULL,             "button.gadget",
                    GA_ID,            GID_8_BU4,
                    GA_RelVerify,     TRUE,
                    GA_Text,          "_Update",
                TAG_END),
                LAYOUT_AddChild,      size_gadgets[GID_8_BU5] =
                NewObject
                (   NULL,             "button.gadget",
                    GA_ID,            GID_8_BU5,
                    GA_RelVerify,     TRUE,
                    GA_Text,          "Stop",
                    GA_Disabled,      TRUE,
                TAG_END),
            TAG_END),
            CHILD_WeightedHeight,     0,
    TAG_END)
    )))
    {   rq("Can't create ReAction objects!");
    }
    unlockscreen();
    openwindow();
    ActivateLayoutGadget(size_gadgets[GID_8_LY1], MainWindowPtr, NULL, (Object) size_gadgets[GID_8_ST1]);

    loop();
    closewindow();
    size_exit();
}

AGLOBAL void updatesize(void)
{   BOOL                  more; // note that it is BOOL, not ABOOL
    TEXT                  tempstring[PATHNAMEFIELD + 1],
                          tempname[LONGFIELD + 1];
    ULONG                 i, j, tempbytes, where;
    LONG                  length, temptype; // these both MUST be signed
    ABOOL                 failed      = FALSE;
    struct FileInfoBlock* FIBPtr      = NULL;
    struct InfoData*      InfoDataPtr;
    struct ExAllControl*  eac;
    struct ExAllData*     ead;
    struct Node*          ListBrowserNodePtr;

/* 0: Ready
   1: Reading directory...
 ! 2: Reading subdirectories...
   3: Sorting contents...
 ![4: Matching duplicates...
  [5: Condensing duplicates...
  [6: Sorting duplicates...
 ![7: Examining duplicates...

[ = only for duplicates
! = can be aborted by user
 */

    if (SizeNodes)
    {   clearlist(&SizeList);
        SizeNodes = FALSE;
    }
    if (!DirNamePtr)
    {   if (!(DirNamePtr = AllocVec(LIMIT_DIRS * sizeof(STRPTR), NULL)))
        {   rq("Out of memory!");
    }   }

    // 1: Ghost relevant gadgets, for duration of the operation.

    ghost();
    SetGadgetAttrs
    (   size_gadgets[GID_8_FG1], MainWindowPtr, NULL,
        FUELGAUGE_Min,   0,
        FUELGAUGE_Max,   100,
        FUELGAUGE_Level, 25,
        GA_Text,         "Reading directory...",
        TAG_END
    ); // we don't know how many files in advance...

    // 2: Now get a Lock() on the path.

    size.stackentries = size.entries = size.numdirs = size.numfiles = 0;
    /* From RKRM I&A, p. 65: */
    if
    (   !(DirHandle = (BPTR) Lock(size.path, ACCESS_READ))
     || !(FIBPtr    = AllocDosObject(DOS_FIB, NULL))
     || !(            Examine(DirHandle, FIBPtr))
     ||  (FIBPtr->fib_DirEntryType <= 0) // if not a directory
    )
    {   failed = TRUE;
    }

    strcpy(size.oldpath, size.path);
    if (FIBPtr)
    {   FreeDosObject(DOS_FIB, FIBPtr);
        // FIBPtr = NULL;
    }
    if (failed)
    {   DisplayBeep(ScreenPtr);
        unghost();

        strcpy(size.path, size.oldpath);
        SetGadgetAttrs
        (   size_gadgets[GID_8_ST1], MainWindowPtr, NULL,
            STRINGA_TextVal, size.path,
            TAG_END
        );
        SetGadgetAttrs
        (   size_gadgets[GID_8_FG1], MainWindowPtr, NULL,
            FUELGAUGE_Level, 0,
            GA_Text,         "Failed!",
            TAG_END
        );
        return;
    }
    SetGadgetAttrs
    (   size_gadgets[GID_8_ST1], MainWindowPtr, NULL,
        STRINGA_TextVal, size.path,
        TAG_END
    );

    /* 3: Now we call Info() to ascertain the free bytes.
          InfoDataPtr must be longword-aligned. */

    if (!(InfoDataPtr = AllocVec(sizeof(struct InfoData), MEMF_CLEAR)))
    {   rq("Out of memory!");
    }
    if (Info(DirHandle, InfoDataPtr))
    {   size.blocksize = InfoDataPtr->id_BytesPerBlock;
        size.free      =
        (InfoDataPtr->id_NumBlocks - InfoDataPtr->id_NumBlocksUsed) *
         InfoDataPtr->id_BytesPerBlock;
        size.capacity  =
         InfoDataPtr->id_NumBlocks *
         InfoDataPtr->id_BytesPerBlock;
    } else
    {   rq("Can't get volume info!");
    }
    FreeVec(InfoDataPtr);
    // InfoDataPtr = NULL;

    comma(size.blocksize, commastring);
    SetGadgetAttrs // "dirs"
    (   size_gadgets[GID_8_ST8], MainWindowPtr, NULL,
        STRINGA_TextVal, commastring,
        TAG_END
    );

    /* 4: Now we are ready to begin the main part of the operation.

    size.topdir[]     = the 'owning' top-level directory for this subdir.
                        Top-level dirs are owned by themselves.
    size.bytes[]      = the size of this top-level directory/file.
    size.entries      = how many top-level dirs, ie. how many names in the
                        listview gadget.
    size.path         = the path (drive, partition, etc.) the user is doing
                        the report on.
    size.stackentries = the current size of the stack.
    DirNamesAllocated = the number of allocated chunks for the
                        topdir names. */

    if (!(        StackPtr = AllocVec(LIMIT_DIRS * sizeof(STRPTR), NULL)))
    {   rq("Out of memory!");
    }

    files = size.total = 0;
    if (size.finddup)
    {   if (!( PathnamePtr = AllocVec(MAX_FILES * sizeof(STRPTR), NULL)))
        {   rq("Out of memory!");
        }
        if (!( FilenamePtr = AllocVec(MAX_FILES * sizeof(STRPTR), NULL)))
        {   rq("Out of memory!");
        }
        if (!(       IsDup = AllocVec(MAX_FILES * sizeof(ABOOL) , NULL)))
        {   rq("Out of memory!");
    }   }

    // This must be allocated with AllocDosObject()
    if (!(eac = AllocDosObject(DOS_EXALLCONTROL, NULL)))
    {   rq("Can't allocate DOS object!");
    }

    eac->eac_LastKey = 0;
    do
    {   more = ExAll(DirHandle, (struct ExAllData *) EADataPtr, 4096, ED_SIZE, eac);
        if (!more && IoErr() != ERROR_NO_MORE_ENTRIES)
        {   FreeDosObject(DOS_EXALLCONTROL, eac);
            eac = NULL;
            rq("Can't examine path!");
        }
        if (eac->eac_Entries == 0)
        {   ; /* ExAll() failed normally with no entries */
            continue; /* more is USUALLY zero */
        }

        // OK, ExAll() has generated up to 2K of data.
        ead = (struct ExAllData *) EADataPtr;
        do
        {   /* use ead here */

            if (ead->ed_Type == -3 || ead->ed_Type == 2) // if a normal file or normal directory (ie. not a link)
            {   size.entries++;
                if (size.entries == LIMIT_DIRS)
                {   FreeDosObject(DOS_EXALLCONTROL, eac);
                    eac = NULL;
                    rq("Overflow!");
                }
                size.type[size.entries] = ead->ed_Type;
                if (size.entries > DirNamesAllocated)
                {   if (!(DirNamePtr[DirNamesAllocated + 1] = AllocMem(PATHNAMEFIELD, MEMF_PUBLIC)))
                    {   FreeDosObject(DOS_EXALLCONTROL, eac);
                        eac = NULL;
                        rq("Out of memory!");
                    }
                    DirNamesAllocated++;
                }
                strcpy(DirNamePtr[size.entries], ead->ed_Name);
                if (ead->ed_Type == 2) /* +2 is dir, +3 is softlink, -3 is file */
                {   size.numdirs++;
                    if (!(size.numdirs % GRANULARITY_DIRS))
                    {   comma(size.numdirs, commastring);
                        SetGadgetAttrs
                        (   size_gadgets[GID_8_ST7], MainWindowPtr, NULL,
                            STRINGA_TextVal, commastring,
                            TAG_END
                        );
                    }
                    size.bytes[size.entries] = 0;
                    strcpy(tempstring, size.path);
                    if (!AddPart(tempstring, ead->ed_Name, PATHNAMEFIELD))
                    {   FreeDosObject(DOS_EXALLCONTROL, eac);
                        eac = NULL;
                        rq("Can't add filename to pathname!");
                    }
                    push(tempstring, size.entries);
                } else
                {   size.numfiles++;
                    if (!(size.numfiles % GRANULARITY_FILES))
                    {   comma(size.numfiles, commastring);
                        SetGadgetAttrs
                        (   size_gadgets[GID_8_ST6], MainWindowPtr, NULL,
                            STRINGA_TextVal, commastring,
                            TAG_END
                        );
                    }
                    addduplicate("", ead->ed_Name);
                    if (size.slack)
                    {   if (ead->ed_Size % size.blocksize == 0)
                        {   size.bytes[size.entries] = (ead->ed_Size / size.blocksize) * size.blocksize;
                            size.total              += (ead->ed_Size / size.blocksize) * size.blocksize;
                        } else
                        {   size.bytes[size.entries] = ((ead->ed_Size / size.blocksize) + 1) * size.blocksize;
                            size.total              += ((ead->ed_Size / size.blocksize) + 1) * size.blocksize;
                    }   }
                    else
                    {   size.bytes[size.entries]     = ead->ed_Size;
                        size.total                  += ead->ed_Size;
            }   }   }

            /* get next ead */
            ead = ead->ed_Next;
        } while(ead);
    } while(more);

    FreeDosObject(DOS_EXALLCONTROL, eac);
    // eac = NULL;
    UnLock(DirHandle);
    DirHandle = NULL;

    SetGadgetAttrs
    (   size_gadgets[GID_8_FG1], MainWindowPtr, NULL,
        FUELGAUGE_Min,   0,
        FUELGAUGE_Max,   100,
        FUELGAUGE_Level, 50,
        GA_Text,         "Reading subdirectories...",
        TAG_END
    );
    pop();

    // Now handle the list.
    killsizelist();
    if (status == STATUS_BUSY)
    {   /* size.entries is the actual number of entries. The array elements are
        from 1 to size.entries.

        bubble sort routine */

        SetGadgetAttrs
        (   size_gadgets[GID_8_FG1], MainWindowPtr, NULL,
            FUELGAUGE_Min,   0,
            FUELGAUGE_Max,   size.entries - 2,
            GA_Text,         "Sorting contents...",
            TAG_END
        );
        setbar(size.entries - 2);

        for (i = size.entries; i >= 2; i--)
        {   progressbar(size.entries - 1);

            for (j = 2; j <= i; j++)
            {   if (size.bytes[j - 1] < size.bytes[j]) 
                {   // swap them
                    tempbytes = size.bytes[j - 1];
                    size.bytes[j - 1] = size.bytes[j]; 
                    size.bytes[j] = tempbytes;
                    temptype = size.type[j - 1];
                    size.type[j - 1] = size.type[j];
                    size.type[j] = temptype;
                    strcpy(tempname, DirNamePtr[j - 1]);
                    strcpy(DirNamePtr[j - 1], DirNamePtr[j]);
                    strcpy(DirNamePtr[j], tempname);
        }   }   }

        getdate();
        if (size.log)
        {   strcpy(logstring, "Path: \"");
            strcat(logstring, size.path);
            strcat(logstring, "\" at ");
            strcat(logstring, timestring);
            strcat(logstring, " on ");
            strcat(logstring, weekdaystring);
            strcat(logstring, " ");
            strcat(logstring, datestring);
            strcat(logstring, ":\n");
            if (!(LogFileHandle = (BPTR) Open(size.logfile, MODE_READWRITE)))
                rq("Can't open file for appending!");
            Seek(LogFileHandle, 0, OFFSET_END);
            writeline();
        }

        for (i = 1; i <= size.entries; i++)
        {   scale(size.bytes[i]);

            if (size.log)
            {   if (size.type[i] == 2)
                {   strcpy(logstring, "*");
                } else
                {   strcpy(logstring, " ");
                }
                strncat(logstring, DirNamePtr[i], TRUNCATE);
                if (strlen(DirNamePtr[i]) < TRUNCATE)
                {   length = TRUNCATE - strlen(DirNamePtr[i]);
                    for (j = 1; j <= length; j++)
                    {   strcat(logstring, " ");
                }   }
                strcat(logstring, " ");
                strcat(logstring, commastring);
                strcat(logstring, "\n");
                writeline();
            }

            if (size.type[i] == 2)
            {   strcpy(liststring[0], "*");
            } else
            {   strcpy(liststring[0], " ");
            }
            strcat(liststring[0], DirNamePtr[i]);
            for (j = 0; j <= 13; j++)
            {   if (commastring[j] != ' ')
                {   where = j; // where is index of first non-space character
                    break;
            }   }
            strcpy(liststring[1], &commastring[where]);

            if (!(ListBrowserNodePtr = AllocListBrowserNode
            (   2,                       // columns,
                LBNA_Column,             0,
                    LBNCA_CopyText,      TRUE,
                    LBNCA_Text,          liststring[0],
                LBNA_Column,             1,
                    LBNCA_CopyText,      TRUE,
                    LBNCA_Text,          &commastring[where],
                    LBNCA_Justification, LCJ_RIGHT,
                TAG_END
            )))
            {   rq("Can't create ReAction listbrowser.gadget node(s)!");
            }

            AddTail(&SizeList, ListBrowserNodePtr); // AddTail() has no return code
            SizeNodes = TRUE;
        }

        scale(size.total);
        SetGadgetAttrs // "used in path"
        (   size_gadgets[GID_8_ST4], MainWindowPtr, NULL,
            STRINGA_TextVal, commastring,
            TAG_END
        );
        scale(size.free);
        SetGadgetAttrs // "free on volume"
        (   size_gadgets[GID_8_ST5], MainWindowPtr, NULL,
            STRINGA_TextVal, commastring,
            TAG_END
        );
        scale(size.capacity);
        SetGadgetAttrs // "volume capacity"
        (   size_gadgets[GID_8_ST3], MainWindowPtr, NULL,
            STRINGA_TextVal, commastring,
            TAG_END
        );
        comma(size.numfiles, commastring);
        SetGadgetAttrs // "files"
        (   size_gadgets[GID_8_ST6], MainWindowPtr, NULL,
            STRINGA_TextVal, commastring,
            TAG_END
        );
        comma(size.numdirs, commastring);
        SetGadgetAttrs // "dirs"
        (   size_gadgets[GID_8_ST7], MainWindowPtr, NULL,
            STRINGA_TextVal, commastring,
            TAG_END
        );

        if (size.log)
        {   strcpy(logstring, "\nUsed in path:                            ");
            scale(size.total);
            strcat(logstring, commastring);
            strcat(logstring, "\nFree on volume:                          "); // these must be adjusted according to TRUNCATE
            scale(size.free);
            strcat(logstring, commastring);
            strcat(logstring, "\nVolume capacity:                         "); // these must be adjusted according to TRUNCATE
            scale(size.capacity);
            strcat(logstring, commastring);
            strcat(logstring, "\nFiles:                                   "); // these must be adjusted according to TRUNCATE
            comma(size.numfiles, commastring);
            strcat(logstring, commastring);
            strcat(logstring, "\nDirectories:                             "); // these must be adjusted according to TRUNCATE
            comma(size.numdirs, commastring);
            strcat(logstring, commastring);
            strcat(logstring, "\nBlock size:                              "); // these must be adjusted according to TRUNCATE
            comma(size.blocksize, commastring);
            strcat(logstring, commastring);
            strcat(logstring, "\n\n");
            writeline();
        }

        SetGadgetAttrs
        (   size_gadgets[GID_8_LB2], MainWindowPtr, NULL,
            LISTBROWSER_Labels, &SizeList,
            TAG_END
        );
        showduplicates();
    } else
    {   SetGadgetAttrs // "used in path"
        (   size_gadgets[GID_8_ST4], MainWindowPtr, NULL,
            STRINGA_TextVal, "-",
            TAG_END
        );
        SetGadgetAttrs // "free on volume"
        (   size_gadgets[GID_8_ST5], MainWindowPtr, NULL,
            STRINGA_TextVal, "-",
            TAG_END
        );
        SetGadgetAttrs // "volume capacity"
        (   size_gadgets[GID_8_ST3], MainWindowPtr, NULL,
            STRINGA_TextVal, "-",
            TAG_END
        );
        SetGadgetAttrs // "files"
        (   size_gadgets[GID_8_ST6], MainWindowPtr, NULL,
            STRINGA_TextVal, "-",
            TAG_END
        );
        SetGadgetAttrs // "directories"
        (   size_gadgets[GID_8_ST7], MainWindowPtr, NULL,
            STRINGA_TextVal, "-",
            TAG_END
        );
        SetGadgetAttrs // "block size"
        (   size_gadgets[GID_8_ST8], MainWindowPtr, NULL,
            STRINGA_TextVal, "-",
            TAG_END
        );
        killsizelist();
        killduplist();
    }

    unghost();
    SetGadgetAttrs
    (   size_gadgets[GID_8_FG1], MainWindowPtr, NULL,
        FUELGAUGE_Min,     0,
        FUELGAUGE_Max,   100,
        FUELGAUGE_Level, 100,
        GA_Text,         "Done",
        TAG_END
    );

    if (LogFileHandle)
    {   Close(LogFileHandle);
        LogFileHandle = NULL;
    }
    while (FileNamesAllocated)
    {   FreeMem(StackPtr[FileNamesAllocated], PATHNAMEFIELD);
        StackPtr[FileNamesAllocated--] = NULL;
    }
    FreeVec(StackPtr);
    StackPtr = NULL;

    // We need the DirNamePtr[] array to remain valid, because
    // the user can click on the listview gadget.
}

MODULE void subdir(ULONG topdir)
{   BOOL                 more;
    TEXT                 tempstring[PATHNAMEFIELD + 1];
    struct ExAllControl* eac;
    struct ExAllData*    ead;

    /* Instead of passing a pointer to the path, we use a global string.
    There are memory corruption problems if you pass pointers to variables
    which are really local to other functions.

    This is dynamically allocated as it must be at least word-aligned;
    also remember not to allocate large arrays on the stack. */

    if (!(DirHandle = (BPTR) Lock(globalname, ACCESS_READ)))
    {   DisplayBeep(ScreenPtr);
        return;
    }
    if (!(eac = AllocDosObject(DOS_EXALLCONTROL, NULL)))
    {   UnLock(DirHandle);
        DirHandle = NULL;
        rq("Can't allocate DOS object!");
    }

    eac->eac_LastKey = 0;
    do
    {   more = ExAll(DirHandle, (struct ExAllData *) EADataPtr, 4096, ED_SIZE, eac);
        if ((!more) && (IoErr() != ERROR_NO_MORE_ENTRIES))
        {   FreeDosObject(DOS_EXALLCONTROL, eac);
            eac = NULL;
            UnLock(DirHandle);
            DirHandle = NULL;
            rq("Can't examine path!");
        }
        if (eac->eac_Entries == 0)
        {   ; /* ExAll() failed normally with no entries */
            continue; /* more is USUALLY zero */
        }
        ead = (struct ExAllData *) EADataPtr;

        do
        {   /* use ead here */

            if (ead->ed_Type == 2) /* +2 is dir, +3 is softlink, -3 is file */
            {   size.numdirs++;    
                if (!(size.numdirs % GRANULARITY_DIRS))
                {   comma(size.numdirs, commastring);
                    SetGadgetAttrs
                    (   size_gadgets[GID_8_ST7], MainWindowPtr, NULL,
                        STRINGA_TextVal, commastring,
                        TAG_END
                    );
                }
                strcpy(tempstring, globalname);
                if (!AddPart(tempstring, ead->ed_Name, PATHNAMEFIELD))
                {   FreeDosObject(DOS_EXALLCONTROL, eac);
                    eac = NULL;
                    UnLock(DirHandle);
                    DirHandle = NULL;
                    rq("Can't add filename to pathname!");
                }
                push(tempstring, topdir);
            } elif (ead->ed_Type == -3) // if it's a file
            {   size.numfiles++;
                if (!(size.numfiles % GRANULARITY_FILES))
                {   comma(size.numfiles, commastring);
                    SetGadgetAttrs
                    (   size_gadgets[GID_8_ST6], MainWindowPtr, NULL,
                        STRINGA_TextVal, commastring,
                        TAG_END
                    );
                }
                addduplicate(&globalname[pathlength], ead->ed_Name);
                if (size.slack)
                {   if (ead->ed_Size % size.blocksize == 0)
                    {   size.bytes[topdir] += (ead->ed_Size / size.blocksize) * size.blocksize;
                        size.total         += (ead->ed_Size / size.blocksize) * size.blocksize;
                    } else
                    {   size.bytes[topdir] += ((ead->ed_Size / size.blocksize) + 1) * size.blocksize;
                        size.total         += ((ead->ed_Size / size.blocksize) + 1) * size.blocksize;
                }   }
                else
                {   size.bytes[topdir]     += ead->ed_Size;
                    size.total             += ead->ed_Size;
            }   }

            /* get next ead */
            ead = ead->ed_Next;
        } while(ead);
    } while(more);

    FreeDosObject(DOS_EXALLCONTROL, eac);
    // eac = NULL;
    UnLock(DirHandle);
    DirHandle = NULL;
    // don't call pop() yourself!
}

MODULE void pop(void)
{   ULONG breakval;

    pathlength = strlen(size.path);
    while (size.stackentries && status == STATUS_BUSY)
    {   size.stackentries--;
        strcpy(globalname, StackPtr[size.stackentries + 1]);
        subdir(size.topdir[size.stackentries + 1]);
        breakval = ra_checkbreak();
        if (breakval == 2)
            cleanexit(EXIT_SUCCESS);
        elif (breakval == 1)
            status = STATUS_STOPPING;
}   }
MODULE void push(STRPTR name, ULONG theindex)
{   size.stackentries++;
    if (size.stackentries == LIMIT_DIRS)
        rq("Overflow!");
    if (size.stackentries > FileNamesAllocated)
    {   if (!(StackPtr[FileNamesAllocated + 1] = AllocMem(PATHNAMEFIELD, MEMF_PUBLIC)))
            rq("Out of memory!");
        FileNamesAllocated++;
    }
    strcpy(StackPtr[size.stackentries], name);
    size.topdir[size.stackentries] = theindex;
}

AGLOBAL void size_loop(ULONG gid)
{   ULONG  code;
    STRPTR stringptr;

    switch(gid)
    {
    case GID_8_BU1: // path ASL
        pathasl();
    break;
    case GID_8_BU3:
        asl("~(#?.info)");
        SetGadgetAttrs // `Log to file:' (string)
        (   size_gadgets[GID_8_ST2], MainWindowPtr, NULL,
            STRINGA_TextVal, size.logfile,
        TAG_END);
    break;
    case GID_8_BU4: // update
        updatesize();
    break;
    // GID_8_BU5 is stop
    case GID_8_BU6:
        root();
    break;
    case GID_8_BU7:
        parent();
    break;
    case GID_8_ST1: // path string
        if (!(GetAttr
        (   STRINGA_TextVal, size_gadgets[GID_8_ST1], (ULONG *) &stringptr
        )))
        {   rq("Unsupported inquiry!"); // should never happen
        }
        strcpy(size.path, stringptr);
        updatesize();
    break;
    case GID_8_ST2: // output string
        if (!(GetAttr
        (   STRINGA_TextVal, size_gadgets[GID_8_ST2], (ULONG *) &stringptr
        )))
        {   rq("Unsupported inquiry!"); // should never happen
        }
        strcpy(size.logfile, stringptr);
    break;
    case GID_8_CB1:
        if (!(GetAttr
        (   GA_Selected, size_gadgets[GID_8_CB1], &size.log
        )))
        {   rq("Unsupported inquiry!"); // should never happen
        }

        SetGadgetAttrs // `Log to file:' (string)
        (   size_gadgets[GID_8_ST2], MainWindowPtr, NULL,
            GA_Disabled, !size.log,
            TAG_END
        );
        /* For some reason, we need to explicitly refresh the string gadget
        visuals here */
        RefreshGadgets((struct Gadget *) size_gadgets[GID_8_ST2], MainWindowPtr, NULL);

        SetGadgetAttrs // `Log to file:' (ASL button)
        (   size_gadgets[GID_8_BU3], MainWindowPtr, NULL,
            GA_Disabled, !size.log,
            TAG_END
        );
        if (size.log)
        {   ActivateLayoutGadget(size_gadgets[GID_8_LY1], MainWindowPtr, NULL, (Object) size_gadgets[GID_8_ST2]);
        }
    break;
    case GID_8_CB2:
        if (!(GetAttr
        (   GA_Selected, size_gadgets[GID_8_CB2], (ULONG *) &size.slack
        )))
        {   rq("Unsupported inquiry!"); // should never happen
        }
    break;
    case GID_8_CB3:
        if (!(GetAttr
        (   GA_Selected, size_gadgets[GID_8_CB3], (ULONG *) &size.finddup
        )))
        {   rq("Unsupported inquiry!"); // should never happen
        }
        SetGadgetAttrs
        (   size_gadgets[GID_8_LB1], MainWindowPtr, NULL,
            GA_Disabled, !size.finddup,
            TAG_END
        );
    break;
    case GID_8_CH1:
        if (!(GetAttr
        (   CHOOSER_Selected, size_gadgets[GID_8_CH1], (ULONG *) &size.view
        )))
        {   rq("Unsupported inquiry!"); // should never happen
        }
    break;
    case GID_8_LB2:
        /* code is the position within the list, starting from 0. */

        if (!(GetAttr
        (   LISTBROWSER_Selected, size_gadgets[GID_8_LB2], (ULONG *) &code
        )))
        {   rq("Unsupported inquiry!"); // should never happen
        }
        code++;
        if (code <= size.entries && size.type[code] == 2)
        {   if (!(AddPart(size.path, DirNamePtr[code], PATHNAMEFIELD + 1)))
            {   rq("Can't add filename to pathname!");
            } else
            {   updatesize();
        }   }
    break;
    default:
    break;
}   }

AGLOBAL void size_exit(void)
{   if (TempHandle)
    {   UnLock(TempHandle);
        TempHandle = NULL;
    }
    if (DirHandle)
    {   UnLock(DirHandle);
        DirHandle = NULL;
    }
    if (LogFileHandle)
    {   Close(LogFileHandle);
        LogFileHandle = NULL;
    }

    // Only call this if the list gadget is detached or not present.
    // (ie. after closewindow()).
    if (SizeNodes)
    {   clearreactionlist(&SizeList);
        SizeNodes = FALSE;
    }
    if (DupNodes)
    {   clearreactionlist(&DupList);
        DupNodes = FALSE;
    }

    while (FileNamesAllocated)
    {   FreeMem(StackPtr[FileNamesAllocated], PATHNAMEFIELD);
        StackPtr[FileNamesAllocated--] = NULL;
    }
    if (StackPtr)
    {   FreeVec(StackPtr);
        StackPtr = NULL;
    }
    while ( DirNamesAllocated)
    {   FreeMem(DirNamePtr[DirNamesAllocated], PATHNAMEFIELD);
        DirNamePtr[DirNamesAllocated--] = NULL;
    }
    if (DirNamePtr)
    {   FreeVec(DirNamePtr);
        DirNamePtr = NULL;
}   }

AGLOBAL void size_die(void)
{   if (EmptySizeNodes)
    {   clearreactionlist(&EmptySizeList);
        EmptySizeNodes = FALSE;
    }
    if (EmptyDupNodes)
    {   clearreactionlist(&EmptyDupList);
        EmptyDupNodes = FALSE;
    }
    IOBuffer[0]  = (UBYTE) size.log;
    IOBuffer[6]  = (UBYTE) size.finddup;
    IOBuffer[21] = (UBYTE) size.slack;
    IOBuffer[22] = (UBYTE) size.view;
}

AGLOBAL void size_config(void)
{   size.log     = (ULONG) IOBuffer[0];
    size.finddup = (ULONG) IOBuffer[6];
    size.slack   = (ULONG) IOBuffer[21];
    size.view    = (ULONG) IOBuffer[22];
}

MODULE void parent(void)
{   BPTR DirHandle,
         ParentHandle;

    if (!(DirHandle = (BPTR) Lock(size.path, ACCESS_READ)))
        rq("Can't lock directory!");
    if (!(ParentHandle = ParentDir(DirHandle)))
    {   /* It returned a NULL lock; ie. a lock on SYS:
        That is not what we want. */
        ParentHandle = DirHandle;
    }
    if (!NameFromLock(ParentHandle, size.path, PATHNAMEFIELD))
        rq("Can't get name from lock!");
    UnLock(DirHandle);
    // DirHandle = NULL;

    updatesize();
}

MODULE void root(void)
{   BPTR  DirHandle,
          ParentHandle;
    ABOOL rootdone     = FALSE;

    if (!(DirHandle = (BPTR) Lock(size.path, ACCESS_READ)))
        rq("Can't lock directory!");
    do
    {   if (!(ParentHandle = ParentDir(DirHandle)))
        {   /* It returned a NULL lock; ie. a lock on SYS:
               That is not what we want. */
            ParentHandle = DirHandle;
            rootdone = TRUE;
        } else DirHandle = ParentHandle;
    } while (!rootdone);
    if (!NameFromLock(ParentHandle, size.path, PATHNAMEFIELD))
        rq("Can't get name from lock!");
    UnLock(DirHandle);
    // DirHandle = NULL;

    updatesize();
}

MODULE void pathasl(void)
{   strcpy(asldir, size.path);
    if (dirasl()) // path for ASL must be valid?
    {   strcpy(size.path, aslresult);
        SetGadgetAttrs
        (   size_gadgets[GID_8_ST1], MainWindowPtr, NULL,
            STRINGA_TextVal, size.path,
            TAG_END
        );
        updatesize();
}   }

MODULE void showduplicates(void)
{   ABOOL                 found = FALSE,
                          started = FALSE,
                          success;
    TEXT                  pathnamestring[PATHNAMEFIELD + 1],
                          commandstring[PATHNAMEFIELD + 1],
                          lastfilename[PATHNAMEFIELD + 1];
    ULONG                 breakval, i, j, k, where,
                          firstpen, secondpen, currentpen, dups;
    LONG                  length;
    STRPTR                tempPathnamePtr, tempFilenamePtr;
    struct DateTime       DateTime;
    struct FileInfoBlock* FIBPtr;
    struct Node*          ListBrowserNodePtr;

    DateTime.dat_Format  = FORMAT_DOS;
    DateTime.dat_Flags   = NULL;
    DateTime.dat_StrDay  = NULL;
    DateTime.dat_StrDate = datestring;
    DateTime.dat_StrTime = timestring;

    if (size.finddup)
    {   killduplist();

        lockscreen();
        firstpen = FindColor
        (   ScreenPtr->ViewPort.ColorMap,
            0xDDDDDDDD,
            0xDDDDDDDD,
            0xDDDDDDDD,
            -1
        );
        secondpen = FindColor
        (   ScreenPtr->ViewPort.ColorMap,
            0xAAAAAAAA,
            0xAAAAAAAA,
            0xAAAAAAAA,
           -1
        );
        unlockscreen();
        currentpen = firstpen;

        if (size.log)
        {   strcpy(logstring, "Duplicates found:\n");
            writeline();
        }

        /* Now we iterate through the complete file list (last entry index
        of files). For each file (i), we look at every other file (j). As
        soon as we find a matching file, we add the file (i) to the
        duplicate file list. */

        SetGadgetAttrs
        (   size_gadgets[GID_8_FG1], MainWindowPtr, NULL,
            FUELGAUGE_Min,   1,
            FUELGAUGE_Max,   files,
            GA_Text,         "Matching duplicates...",
            TAG_DONE
        );
        setbar(files);

        for (i = 1; i <= files; i++)
        {   progressbar(i);
            breakval = ra_checkbreak();
            if (breakval == 2)
            {   cleanexit(EXIT_SUCCESS);
            } elif (breakval == 1)
            {   status = STATUS_STOPPING;
                SetGadgetAttrs
                (   size_gadgets[GID_8_FG1], MainWindowPtr, NULL,
                    GA_Text, "Stopping...",
                    TAG_END
                );
                break;
            }
            // if the user doesn't want us to stop...
            for (j = 1; j <= files; j++)
            {   if (i != j && !stricmp(FilenamePtr[i], FilenamePtr[j]))
                {   found = TRUE; // we found (at least one) match
                    IsDup[i] = TRUE;
                    break;
        }   }   }

        if (status == STATUS_BUSY)
        {   SetGadgetAttrs
            (   size_gadgets[GID_8_FG1], MainWindowPtr, NULL,
                FUELGAUGE_Min,   1,
                FUELGAUGE_Max,   files,
                GA_Text,         "Condensing duplicates...",
                TAG_END
            );
            setbar(files);

            // condense list
            dups = 0;
            for (i = 1; i <= files; i++)
            {   progressbar(i);

                if (IsDup[i])
                {   dups++;
                    FilenamePtr[dups] = FilenamePtr[i];
                    PathnamePtr[dups] = PathnamePtr[i];
                    IsDup[dups]       = IsDup[i];
                } else
                {   FreeVec(PathnamePtr[i]);
                    FreeVec(FilenamePtr[i]);
            }   }

            SetGadgetAttrs
            (   size_gadgets[GID_8_FG1], MainWindowPtr, NULL,
                FUELGAUGE_Min,   2,
                FUELGAUGE_Max,   dups,
                GA_Text,         "Sorting duplicates...",
                TAG_END
            );
            setbar(dups);

            // bubble sort routine
            for (i = dups; i >= 2; i--)
            {   progressbar(dups - i);
                for (j = 2; j <= i; j++)
                {   if (stricmp(FilenamePtr[j - 1], FilenamePtr[j]) > 0)
                    {   // swap them
                        tempPathnamePtr     = PathnamePtr[j - 1];
                        PathnamePtr[j - 1]  = PathnamePtr[j];
                        PathnamePtr[j]      = tempPathnamePtr;
                        tempFilenamePtr     = FilenamePtr[j - 1];
                        FilenamePtr[j - 1]  = FilenamePtr[j];
                        FilenamePtr[j]      = tempFilenamePtr;
            }   }   }

            SetGadgetAttrs
            (   size_gadgets[GID_8_FG1], MainWindowPtr, NULL,
                FUELGAUGE_Min,   0,
                FUELGAUGE_Max,   dups,
                FUELGAUGE_Level, 0,
                GA_Text,         "Examining duplicates...",
                TAG_END
            );
            setbar(dups);

            NewList(&DupList);

            for (i = 1; i <= dups; i++)
            {   progressbar(i);
                breakval = ra_checkbreak();
                if (breakval == 2)
                {   cleanexit(EXIT_SUCCESS);
                } elif (breakval == 1)
                {   status = STATUS_STOPPING;
                    SetGadgetAttrs
                    (   size_gadgets[GID_8_FG1], MainWindowPtr, NULL,
                        GA_Text,         "Stopping...",
                        TAG_END
                    );
                    break;
                }

                // begin creating the line of text
                if (PathnamePtr[i][0] == '/')
                {   strcpy(liststring[0], &(PathnamePtr[i][1]));
                } else
                {   strcpy(liststring[0], PathnamePtr[i]);
                }
                strcpy(logstring, " ");
                strcat(logstring, liststring[0]);
                if (strlen(logstring) > TRUNCATE)
                {   *(logstring + TRUNCATE) = 0; // truncate
                }
                length = TRUNCATE + 2 - strlen(logstring);
                if (length >= 1)
                {   for (k = 1; k <= length; k++)
                    {   strcat(logstring, " ");
                }   }

                strcpy(pathnamestring, size.path);
                if (PathnamePtr[i][0] != '/')
                {   if (!AddPart(pathnamestring, PathnamePtr[i], 256))
                    {   rq("AddPart() failed!");
                }   }
                else
                {   if (!AddPart(pathnamestring, &PathnamePtr[i][1], 256))
                    {   rq("AddPart() failed!");
                }   }
                success = FALSE;

                // now get size and date of file
                if (DirHandle  = (BPTR) Lock(pathnamestring, ACCESS_READ))
                {   if (FIBPtr =        AllocDosObject(DOS_FIB, NULL))
                    {   success = TRUE;

                        if (Examine(DirHandle, FIBPtr))
                        {   comma(FIBPtr->fib_Size, commastring);
                            strcat(logstring, commastring);
                            for (j = 0; j <= 13; j++)
                            {   if (commastring[j] != ' ')
                                {   where = j; // where is index of first non-space character
                                    break;
                            }   }
                            strcpy(liststring[1], &commastring[where]);
                        } else
                        {   strcpy(liststring[1], "?");
                            strcat(logstring, "            ?");
                        }
                        strcat(logstring, " ");

                        DateTime.dat_Stamp   = FIBPtr->fib_Date;
                        if (DateToStr(&DateTime))
                        {   strcpy(liststring[2], datestring);
                            strcpy(liststring[3], timestring);
                        } else
                        {   strcpy(liststring[2], "        ?");
                            strcpy(liststring[3], "        ?");
                        }
                        strcat(logstring, liststring[2]);
                        strcat(logstring, " ");
                        strcat(logstring, liststring[3]);
                        strcat(logstring, " ");

                        FreeDosObject(DOS_FIB, FIBPtr);
                        // FIBPtr = NULL;
                        UnLock(DirHandle);
                        DirHandle = NULL;
                }   }

                if (!success)
                {   strcpy(liststring[1], "            ?");
                    strcpy(liststring[2], "            ?");
                    strcpy(liststring[3], "            ?");
                    strcat(logstring, liststring[1]);
                    strcat(logstring, " ");
                    strcat(logstring, liststring[2]);
                    strcat(logstring, " ");
                    strcat(logstring, liststring[3]);
                    strcat(logstring, " ");
                }

                getversion(pathnamestring, liststring[4]);
                strcat(logstring, liststring[4]);

                // now get the file version
                if (ram)
                {   strcpy(commandstring, "RAM:Version ");
                } else
                {   strcpy(commandstring, "Version ");
                }
                strcat(commandstring, pathnamestring);
                strcat(commandstring, " >T:ReportPlus.temp");
                if (!SystemTags(commandstring, SYS_Output, Open("NIL:", MODE_NEWFILE), TAG_DONE))
                {   readordie("T:ReportPlus.temp");
                    for (k = 0; k <= LONGESTFIELD; k++)
                    {   if (IOBuffer[k] == LF)
                        {   IOBuffer[k] = 0;
                            break;
                    }   }
                    strcpy(liststring[4], IOBuffer);
                } else
                {   strcpy(liststring[4], "?");
                }
                strcat(logstring, liststring[4]);

                if (started)
                {   if (stricmp(FilenamePtr[i], lastfilename))
                    {   if (currentpen == firstpen)
                        {   currentpen = secondpen;
                        } else currentpen = firstpen;
                }   }
                else started = TRUE;

                if (!(ListBrowserNodePtr = AllocListBrowserNode
                (   5,                       // columns,
                    LBNA_Column,             0,
                        LBNA_Flags,          LBFLG_CUSTOMPENS,
                        LBNCA_FGPen,         BLACK,
                        LBNCA_BGPen,         currentpen,
                        LBNCA_CopyText,      TRUE,
                        LBNCA_Text,          liststring[0],
                    LBNA_Column,             1,
                        LBNA_Flags,          LBFLG_CUSTOMPENS,
                        LBNCA_FGPen,         BLACK,
                        LBNCA_BGPen,         currentpen,
                        LBNCA_CopyText,      TRUE,
                        LBNCA_Text,          liststring[1],
                        LBNCA_Justification, LCJ_RIGHT,
                    LBNA_Column,             2,
                        LBNA_Flags,          LBFLG_CUSTOMPENS,
                        LBNCA_FGPen,         BLACK,
                        LBNCA_BGPen,         currentpen,
                        LBNCA_CopyText,      TRUE,
                        LBNCA_Text,          liststring[2],
                    LBNA_Column,             3,
                        LBNA_Flags,          LBFLG_CUSTOMPENS,
                        LBNCA_FGPen,         BLACK,
                        LBNCA_BGPen,         currentpen,
                        LBNCA_CopyText,      TRUE,
                        LBNCA_Text,          liststring[3],
                    LBNA_Column,             4,
                        LBNA_Flags,          LBFLG_CUSTOMPENS,
                        LBNCA_FGPen,         BLACK,
                        LBNCA_BGPen,         currentpen,
                        LBNCA_CopyText,      TRUE,
                        LBNCA_Text,          liststring[4],
                    TAG_END
                )))
                {   rq("Can't create ReAction listbrowser.gadget node(s)!");
                }
                AddTail(&DupList, ListBrowserNodePtr); // AddTail() has no return code
                DupNodes = TRUE;
                strcpy(lastfilename, FilenamePtr[i]);
                
                if (size.log) // write to logfile
                {   strcat(logstring, "\n");
                    writeline();
            }   }
            for (i = 1; i <= dups; i++)
            {   FreeVec(PathnamePtr[i]);
                FreeVec(FilenamePtr[i]);
        }   }
        else
        {   for (i = 1; i <= files; i++)
            {   FreeVec(PathnamePtr[i]);
                FreeVec(FilenamePtr[i]);
        }   }

        files = 0;
        FreeVec(IsDup);
        IsDup = NULL;
        FreeVec(PathnamePtr);
        PathnamePtr = NULL;
        FreeVec(FilenamePtr);
        FilenamePtr = NULL;
        DeleteFile("T:ReportPlus.temp"); /* returns FALSE for failure */

        if (size.log)
        {   if (!found)
            {   strcpy(logstring, " None\n");
            }
            strcat(logstring, "\n");
            writeline();
        }

        SetGadgetAttrs
        (   size_gadgets[GID_8_LB1], MainWindowPtr, NULL,
            LISTBROWSER_Labels, &DupList,
            TAG_END
        );
}   }

MODULE void addduplicate(STRPTR path, STRPTR filename)
{   TEXT tablestring[257];

    if (size.finddup)
    {   files++;
        if (files >= MAX_FILES)
        {   rq("Too many files!");
        }
        IsDup[files] = FALSE;

        if (!(FilenamePtr[files] = AllocVec(strlen(filename) + 1, NULL)))
        {   rq("Out of memory!");
        }
        strcpy(FilenamePtr[files], filename);

        strcpy(tablestring, path);
        if (!AddPart(tablestring, filename, 256))
        {   rq("AddPart() failed!");
        }
        if (!(PathnamePtr[files] = AllocVec(strlen(tablestring) + 1, NULL)))
        {   rq("Out of memory!");
        }
        strcpy(PathnamePtr[files], tablestring);

    /* size.path   path           filename    PathnamePtr[]           FilenamePtr[]
       "SYS:Tools" "Commodities/" "AutoPoint" "Commodities/AutoPoint" "AutoPoint" */
}   }

MODULE void writeline(void)
{   /* The file is opened just before the first call to writeline()
    (when the date, time, etc. is written). Then we keep the file open
    and do our scan. Then we write out the results a line at a time,
    then the free and total bytes.
      If we are also doing duplicates, we now do our thinking,
    write the results a line at a time, then close the file. */

    if (FPuts(LogFileHandle, logstring))
    {   Close(LogFileHandle);
        LogFileHandle = NULL;
        rq("Can't append to file!");
}   }

MODULE void killsizelist(void)
{   SetGadgetAttrs
    (   size_gadgets[GID_8_LB2], MainWindowPtr, NULL,
        LISTBROWSER_Labels, &EmptySizeList,
        TAG_END
    );
    if (SizeNodes)
    {   clearreactionlist(&SizeList);
        SizeNodes = FALSE;
}   }

MODULE void killduplist(void)
{   SetGadgetAttrs
    (   size_gadgets[GID_8_LB1], MainWindowPtr, NULL,
        LISTBROWSER_Labels, &EmptyDupList,
        TAG_END
    );
    if (DupNodes)
    {   clearreactionlist(&DupList);
        DupNodes = FALSE;
}   }

AGLOBAL ULONG Hook8Func(struct Hook *h, VOID *o, VOID *msg)
{   /* "When the hook is called, the data argument points to the
    window object and message argument to the IntuiMessage." */

    UWORD code, qual;
    ULONG scroll;

    geta4(); // wait till here before doing anything
    code = ((struct IntuiMessage *) msg)->Code;
    qual = ((struct IntuiMessage *) msg)->Qualifier;
    switch(code)
    {
    case SCAN_HELP:
        if (status == STATUS_READY)
        {   helpabout();
        }
    break;
    case SCAN_ESCAPE:
        if (status == STATUS_READY)
        {   if ((qual & IEQUALIFIER_LSHIFT) || (qual & IEQUALIFIER_RSHIFT))
            {   cleanexit(EXIT_SUCCESS);
            } else page = 0;
        } else
        {   // assert(status == STATUS_BUSY);
            status = STATUS_STOPPING;
        }
    break;
    case SCAN_P:
        ActivateLayoutGadget(size_gadgets[GID_8_LY1], MainWindowPtr, NULL, (Object) size_gadgets[GID_8_ST1]);
    break;
    case SCAN_V:
        if (status == STATUS_READY)
        {   if (!(qual & IEQUALIFIER_LSHIFT) && !(qual & IEQUALIFIER_RSHIFT))
            {   if (size.view < UNITOPTIONS)
                    size.view++;
                else size.view = 0;
            } else
            {   if (size.view > 0)
                    size.view--;
                else size.view = UNITOPTIONS;
            }
            SetGadgetAttrs
            (   size_gadgets[GID_8_CH1], MainWindowPtr, NULL,
                CHOOSER_Selected, size.view,
                TAG_END
            );
        }
    break;
    case SCAN_PERIOD:
        pathasl();
    break;
    case SCAN_UP:
        if (qual & IEQUALIFIER_CONTROL)
        {   scroll = LBP_TOP;
        } elif (qual & IEQUALIFIER_LSHIFT || qual & IEQUALIFIER_RSHIFT)
        {   scroll = LBP_PAGEUP;
        } else scroll = LBP_LINEUP;
    break;
    case SCAN_DOWN:
        if (qual & IEQUALIFIER_CONTROL)
        {   scroll = LBP_BOTTOM;
        } elif (qual & IEQUALIFIER_LSHIFT || qual & IEQUALIFIER_RSHIFT)
        {   scroll = LBP_PAGEDOWN;
        } else scroll = LBP_LINEDOWN;
    break;
    default:
    break;
    }

    if (code == SCAN_UP || code == SCAN_DOWN)
    {   SetGadgetAttrs
        (   size_gadgets[GID_8_LB2],      // pointer to gadget
            MainWindowPtr,                // pointer to window (not window object!)
            NULL,                         // pointer to requester
            LISTBROWSER_Position, scroll, // tags
            TAG_DONE                      // done
        );
    }

    return(1);
}

MODULE void ghost(void)
{   status = STATUS_BUSY;

    SetGadgetAttrs // "update"
    (   size_gadgets[GID_8_BU4], MainWindowPtr, NULL,
        GA_Disabled, TRUE,
        TAG_END
    );
    SetGadgetAttrs // "stop"
    (   size_gadgets[GID_8_BU5], MainWindowPtr, NULL,
        GA_Disabled, FALSE,
        TAG_END
    );
}

MODULE void unghost(void)
{   status = STATUS_READY;

    SetGadgetAttrs // "update"
    (   size_gadgets[GID_8_BU4], MainWindowPtr, NULL,
        GA_Disabled, FALSE,
        TAG_END
    );
    SetGadgetAttrs // "stop"
    (   size_gadgets[GID_8_BU5], MainWindowPtr, NULL,
        GA_Disabled, TRUE,
        TAG_END
    );
}

MODULE void scale(ULONG bytes)
{   if (size.view == 0) // bytes
    {   comma(bytes, commastring);
    } elif (size.view == 1) // kilobytes
    {   comma(bytes / 1024, commastring);
    } elif (size.view == 2) // megabytes
    {   comma(bytes / 1048576, commastring);
    } else
    {   // assert(size.view == 3); // blocks
        // assert(size.blocksize); // to avoid division by zero
        comma(bytes / size.blocksize, commastring);
        if (bytes % size.blocksize)
        {   comma((bytes / size.blocksize) + 1, commastring);
        } else // exactly on a block boundary
        {   comma(bytes / size.blocksize, commastring);
}   }   }

MODULE void __inline progressbar(ULONG level)
{   if (!(level % increment))
    {   SetGadgetAttrs
        (   size_gadgets[GID_8_FG1], MainWindowPtr, NULL,
            FUELGAUGE_Level, level,
            TAG_END
        );
}   }

