#include <exec/types.h>
#include <exec/memory.h>
#include <intuition/intuition.h>
#include <intuition/gadgetclass.h>
#include <libraries/gadtools.h>
#include <dos/dos.h>
#include <dos/dostags.h>
#include <dos/dosextens.h>
#include <dos/exall.h>
#include <dos/datetime.h>
#include <graphics/gfx.h>

#include <clib/alib_protos.h>
#include <clib/intuition_protos.h>
#include <clib/graphics_protos.h>
#include <clib/dos_protos.h>
#include <clib/gadtools_protos.h>
#include <clib/exec_protos.h>

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

#include <reaction/reaction.h>
#include <gadgets/listbrowser.h>
#include <pragmas/listbrowser_pragmas.h>

#define GID_10_LB1 0
#define GIDS_10    GID_10_LB1

MODULE void updatefiles(void);
MODULE void stage1(void);
MODULE void files_work(void);                                 // for stage1()
MODULE void stage2(void);
MODULE ABOOL __inline quickcomp(STRPTR first, STRPTR second); // for stage2()
MODULE void dobuffer(ABOOL item, ULONG whichpen);             // for stage2()
MODULE void stage3(void);

IMPORT ABOOL                fillwindows;
IMPORT TEXT                 IOBuffer[LONGESTFIELD + 1],
                            aslresult[MEDFIELD + 1];
IMPORT SBYTE                page;
IMPORT ULONG                wbval;
IMPORT struct SharedStruct  shared;
IMPORT struct Screen*       ScreenPtr;
IMPORT struct Library*      ListBrowserBase;
IMPORT struct ExAllData*    EADataPtr;
IMPORT struct VisualInfo*   VisualInfoPtr;
IMPORT struct NewGadget     Gadget;
IMPORT struct List          EmptyList;

MODULE struct Gadget       *gadgets[GIDS_10 + 1];
MODULE struct List          DirList, FileList;
MODULE struct List          ResultList;
MODULE ABOOL                ResultNodes = FALSE,
                            stop;
MODULE struct
{   ABOOL show[8];
    TEXT  showlabel[8][17 + 1];
    TEXT  path[VLONGFIELD + 1];
    ULONG entries;
    UWORD osversion;
} files =
{   TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, FALSE,
    "Very important",
    "Important",
    "Semi-important",
    "Unimportant",
    "Obsolete",
    "3rd-party",
    "Missing normal",
    "Missing optional",
    "", 0
};
MODULE  BPTR           LogFileHandle       = NULL;
MODULE  TEXT           stringholder1[VLONGFIELD + 1],
                       stringholder2[VLONGFIELD + 1],
                       lockstring[VLONGFIELD + 1];
MODULE  struct Gadget *CY101_OSVersion     = NULL,
                      *TE101_Status        = NULL,
                      *TE101_ShowText      = NULL,
                      *CB101_Show[8] =
                      {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};

#define OSVERSIONS 5 // counting from 0
MODULE  STRPTR OSOptions[OSVERSIONS + 2] =
{   "OS3.1",
    "OS3.5",
    "OS3.5 & Boing Bag(s)",
    "OS3.9",
    "OS3.9 & Genesis Update",
    "OS3.9 & Boing Bag 1",
    NULL
};
MODULE struct
{   ULONG red, green, blue, pennumber;
} penn[8] =
{     {0xFFFFFFFF, 0x22222222, 0x22222222, -1}, // red
      {0xFFFFFFFF, 0x88888888, 0x00000000, -1}, // orange
      {0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, -1}, // yellow
      {0x55555555, 0xFFFFFFFF, 0x55555555, -1}, // green
      {0x55555555, 0x55555555, 0xFFFFFFFF, -1}, // blue (obsolete)
      {0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, -1}, // cyan (3rd-party)
      {0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, -1}, // white (missing normal)
      {0xFFFFFFFF, 0x99999999, 0x99999999, -1}  // pink (missing optional)
};

// from rp.c
IMPORT SBYTE               page;
IMPORT struct Gadget      *BU99_Right,
                          *ST99_Output,
                          *BU99_OutputASL,
                          *BU99_Update,
                          *BU99_Stop,
                          *CB99_Log,
                          *PrevGadPtr;
IMPORT struct Window*      MainWindowPtr;
IMPORT TEXT                weekdaystring[LEN_DATSTRING],
                           datestring[LEN_DATSTRING],
                           timestring[LEN_DATSTRING];

AGLOBAL void files1(void)
{   ULONG i;

    verynewwindow
    (   FILES1WIDTH, FILES1HEIGHT,
        "Report+: System File Report",
        BUTTONIDCMP | STRINGIDCMP | CYCLEIDCMP | CHECKBOXIDCMP
    );
    if (fillwindows)
    {   SetAPen(MainWindowPtr->RPort, 0);
        RectFill(MainWindowPtr->RPort, 446, 20, 446 + 72, 20 + 12); // TE101_Status
    }

    if (!(gadgets[GID_10_LB1] = (struct Gadget *) NewObject
    (   LISTBROWSER_GetClass(),   NULL,
        GA_ID,                    GID_10_LB1,
        GA_Left,                  10,
        GA_Top,                   144,
        GA_Width,                 FILES1WIDTH - 20,
        GA_Height,                84,
        GA_ReadOnly,              TRUE,
        GA_TextAttr,              Gadget.ng_TextAttr,
        LISTBROWSER_ScrollRaster, TRUE,
        LISTBROWSER_Labels,       (ULONG) &EmptyList,
        TAG_END
    )))
    {   rq("Can't create ReAction gadgets!");
    }
    AddGList(MainWindowPtr, gadgets[GID_10_LB1], -1, -1, NULL);
    RefreshGList(gadgets[GID_10_LB1], MainWindowPtr, NULL, -1);

    setgadget(244, 119, 244, 12, "AmigaOS _Version", PLACETEXT_ABOVE);
    CY101_OSVersion = PrevGadPtr = (struct Gadget *) CreateGadget
    (   CYCLE_KIND,
        PrevGadPtr,
        &Gadget,
        GTCY_Labels, OSOptions,
        GTCY_Active, files.osversion,
        GT_Underscore, '_',
        TAG_DONE
    );

    // show
    setgadget(10, 35, 40, 0, "Show:", PLACETEXT_ABOVE);
    TE101_ShowText = PrevGadPtr = (struct Gadget *) CreateGadget
    (   TEXT_KIND,
        PrevGadPtr,
        &Gadget,
        TAG_DONE
    );

    SetDrMd(MainWindowPtr->RPort, JAM2);
    SetAPen(MainWindowPtr->RPort, BLACK);
    for (i = 0; i <= 7; i++)
    {   setgadget(16, 35 + (i * 12), 0, 0, NULL, PLACETEXT_RIGHT);
        Move(MainWindowPtr->RPort, 49, 43 + (i * 12));
        SetBPen(MainWindowPtr->RPort, penn[i].pennumber);
        Text(MainWindowPtr->RPort, files.showlabel[i], strlen(files.showlabel[i]));
        CB101_Show[i] = PrevGadPtr = (struct Gadget *) CreateGadget
        (   CHECKBOX_KIND,
            PrevGadPtr,
            &Gadget,
            GTCB_Checked, files.show[i],
            GT_Underscore, '_',
            TAG_DONE
        );
    }
    SetDrMd(MainWindowPtr->RPort, JAM1);
    Move(MainWindowPtr->RPort, 49 + (8 * 1),  43);
    Text(MainWindowPtr->RPort, "_", 1);
    Move(MainWindowPtr->RPort, 49 + (8 * 0),  55);
    Text(MainWindowPtr->RPort, "_", 1);
    Move(MainWindowPtr->RPort, 49 + (8 * 0),  67);
    Text(MainWindowPtr->RPort, "_", 1);
    Move(MainWindowPtr->RPort, 49 + (8 * 1),  79);
    Text(MainWindowPtr->RPort, "_", 1);
    Move(MainWindowPtr->RPort, 49 + (8 * 1),  91);
    Text(MainWindowPtr->RPort, "_", 1);
    Move(MainWindowPtr->RPort, 49 + (8 * 0), 103);
    Text(MainWindowPtr->RPort, "_", 1);
    Move(MainWindowPtr->RPort, 49 + (8 * 6), 115);
    Text(MainWindowPtr->RPort, "_", 1);
    Move(MainWindowPtr->RPort, 49 + (8 * 9), 127);
    Text(MainWindowPtr->RPort, "_", 1);

    /* update */
    setgadget(244, 20, 60, 12, "_Update", NULL);
    BU99_Update = PrevGadPtr = (struct Gadget *) CreateGadget
    (   BUTTON_KIND,
        PrevGadPtr,
        &Gadget,
        GT_Underscore, '_',
        TAG_DONE
    );
    /* stop */
    setgadget(304, 20, 60, 12, "Stop", NULL);
    BU99_Stop = PrevGadPtr = (struct Gadget *) CreateGadget
    (   BUTTON_KIND,
        PrevGadPtr,
        &Gadget,
        GA_Disabled, TRUE,
        TAG_DONE
    );

    if (wbval >= 44)
        shared.log = FALSE;
    else shared.log = TRUE;
    // log to file?
    setgadget(244, 45, 0, 0, "_Log to file?", PLACETEXT_RIGHT);
    CB99_Log = PrevGadPtr = (struct Gadget *) CreateGadget
    (   CHECKBOX_KIND,
        PrevGadPtr,
        &Gadget,
        GTCB_Checked, shared.log,
        GT_Underscore, '_',
        TAG_DONE
    );

    /* output */
    if (!(shared.output[0]))
        strcpy(shared.output, "RAM:Report.txt");
    setgadget(244, 72, 244, 12, "_Output pathname:", PLACETEXT_ABOVE);
    ST99_Output = PrevGadPtr = (struct Gadget *) CreateGadget
    (   STRING_KIND,
        PrevGadPtr,
        &Gadget,
        GTST_String, &(shared.output),
        GTST_MaxChars, LONGFIELD,
        GA_TabCycle, TRUE,
        GT_Underscore, '_',
        TAG_DONE
    );
    /* output... */
    setgadget(490, 72, 28, 12, "_...", NULL);
    BU99_OutputASL = PrevGadPtr = (struct Gadget *) CreateGadget
    (   BUTTON_KIND,
        PrevGadPtr,
        &Gadget,
        GT_Underscore, '_',
        TAG_DONE
    );

    // status
    setgadget(446, 20, 72, 12, "Status:", NULL);
    TE101_Status = PrevGadPtr = (struct Gadget *) CreateGadget
    (   TEXT_KIND,
        PrevGadPtr,
        &Gadget,
        GTTX_Text, "Ready",
        GTTX_Border, TRUE,
        TAG_DONE
    );

    drawgadgets((UWORD) ~0);

    if (wbval < 44)
    {   GT_SetGadgetAttrs
        (   CB99_Log,
            MainWindowPtr, NULL,
            GA_Disabled, TRUE,
            TAG_DONE
        );
    }
    if (!shared.log)
    {   GT_SetGadgetAttrs
        (   ST99_Output,
            MainWindowPtr, NULL,
            GA_Disabled, TRUE,
            TAG_DONE
        );
        GT_SetGadgetAttrs
        (   BU99_OutputASL,
            MainWindowPtr, NULL,
            GA_Disabled, TRUE,
            TAG_DONE
        );
    }
    loop();
    if (ResultNodes)
    {   clearreactionlist(&ResultList);
        ResultNodes = FALSE;
    }
    closewindow();
}

MODULE void updatefiles(void)
{   ULONG            i;

    /* Gadgets are: BU99_Right ('menu')
                    BU99_Update
                    CB99_Log, ST99_Output, BU99_OutputASL
                    CY101_OSVersion
                    CB101_Show[]
    All are ghosted during operation. Then their ghosting status returns
    to normal (not necessarily unghosted).

    Disable the `Menu' (BU), `Update' (BU). `Log to file?' (CB),
    `Pathname' (ST), `...' (BU), `Show' (CB), `OS version' (MX), `Stop'
    (BU) gadgets. Status (TE) is changed.

        0: Gadget handling.
        1: Set up lists, do the directory examination.
           (For each `source' directory in the `queue', before doing it
           we check for a break. `Break opportunity 1'.)
    At this point we have an empty DirList, and a full FileList, and an
    empty ResultList.
        2: Create the ResultList.
           (For each `source' file in the `queue', before doing it we
           check for a break. `Break opportunity 2'.)
        3: Show results.

       DirList: an Exec list of directories found, awaiting examination.
      FileList: an Exec list of files found, awaiting processing.
    ResultList: a listbrowser list of files found, for display. */

    strcpy
    (   shared.output,
        ((struct StringInfo *) ST99_Output->SpecialInfo)->Buffer
    );

    stop = FALSE;
    GT_SetGadgetAttrs
    (   BU99_Right,
        MainWindowPtr, NULL,
        GA_Disabled, TRUE,
        TAG_DONE
    );
    GT_SetGadgetAttrs
    (   BU99_Update,
        MainWindowPtr, NULL,
        GA_Disabled, TRUE,
        TAG_DONE
    );
    GT_SetGadgetAttrs
    (   CB99_Log,
        MainWindowPtr, NULL,
        GA_Disabled, TRUE,
        TAG_DONE
    );
    GT_SetGadgetAttrs
    (   ST99_Output,
        MainWindowPtr, NULL,
        GA_Disabled, TRUE,
        TAG_DONE
    );
    GT_SetGadgetAttrs
    (   BU99_OutputASL,
        MainWindowPtr, NULL,
        GA_Disabled, TRUE,
        TAG_DONE
    );
    GT_SetGadgetAttrs
    (   CY101_OSVersion,
        MainWindowPtr, NULL,
        GA_Disabled, TRUE,
        TAG_DONE
    );
    for (i = 0; i <= 7; i++)
    {   GT_SetGadgetAttrs
        (   CB101_Show[i],
            MainWindowPtr, NULL,
            GA_Disabled, TRUE,
            TAG_DONE
        );
    }
    GT_SetGadgetAttrs
    (   BU99_Stop,
        MainWindowPtr, NULL,
        GA_Disabled, FALSE,
        TAG_DONE
    );
    GT_SetGadgetAttrs
    (   TE101_Status,
        MainWindowPtr, NULL,
        GTTX_Text, "Busy",
        TAG_DONE
    );

    SetGadgetAttrs
    (   gadgets[GID_10_LB1], MainWindowPtr, NULL,
        LISTBROWSER_Labels, NULL,
        TAG_END
    );
    SetGadgetAttrs
    (   gadgets[GID_10_LB1], MainWindowPtr, NULL,
        LISTBROWSER_Labels, (ULONG) &EmptyList,
        TAG_END
    );
    if (ResultNodes)
    {   clearreactionlist(&ResultList);
        ResultNodes = FALSE;
    }

    stage1();
    if (!stop)
    {   stage2();
    }
    FreeNameNodes(&FileList);
    if (!stop)
    {   stage3();
    }

    GT_SetGadgetAttrs
    (   BU99_Right,
        MainWindowPtr, NULL,
        GA_Disabled, FALSE,
        TAG_DONE
    );
    GT_SetGadgetAttrs
    (   BU99_Update,
        MainWindowPtr, NULL,
        GA_Disabled, FALSE,
        TAG_DONE
    );
    if (wbval >= 44)
    {   GT_SetGadgetAttrs
        (   CB99_Log,
            MainWindowPtr, NULL,
            GA_Disabled, FALSE,
            TAG_DONE
        );
    }
    if (shared.log)
    {   GT_SetGadgetAttrs
        (   ST99_Output,
            MainWindowPtr, NULL,
            GA_Disabled, FALSE,
            TAG_DONE
        );
        GT_SetGadgetAttrs
        (   BU99_OutputASL,
            MainWindowPtr, NULL,
            GA_Disabled, FALSE,
            TAG_DONE
        );
    }
    GT_SetGadgetAttrs
    (   CY101_OSVersion,
        MainWindowPtr, NULL,
        GA_Disabled, FALSE,
        TAG_DONE
    );
    for (i = 0; i <= 7; i++)
    {   GT_SetGadgetAttrs
        (   CB101_Show[i],
            MainWindowPtr, NULL,
            GA_Disabled, FALSE,
            TAG_DONE
        );
    }
    GT_SetGadgetAttrs
    (   BU99_Stop,
        MainWindowPtr, NULL,
        GA_Disabled, TRUE,
        TAG_DONE
    );
    GT_SetGadgetAttrs
    (   TE101_Status,
        MainWindowPtr, NULL,
        GTTX_Text, "Ready",
        TAG_DONE
    );
}

MODULE void stage1(void)
{   struct NameNode* NodePtr;

    NewList(&FileList);
    NewList(&DirList);
    NewList(&ResultList);
    AddNameToTail(&DirList, "");
    // pop all the directories from the work stack, and send them one
    // at a time to files_work() for processing.
    while ((DirList.lh_Head)->ln_Succ) // while the list is non-empty
    {   if (checkbreak() == 1) // we don't yet support completely quitting
        {   stop = TRUE;
            GT_SetGadgetAttrs
            (   TE101_Status,
                MainWindowPtr, NULL,
                GTTX_Text, "Stopping",
                TAG_DONE
            );
            GT_SetGadgetAttrs
            (   BU99_Stop,
                MainWindowPtr, NULL,
                GA_Disabled, TRUE,
                TAG_DONE
            );
            break;
        }

        /* remove the dir-node from the list, copy its path to
           stringholder1, call files_work, then free the dir-node. */

        if (!(NodePtr = (struct NameNode *) RemTail(&DirList)))
        {   rq("RemTail() failed (list is empty!)"); // this should never happen
        }
        strcpy(stringholder1, NodePtr->nn_Data);
        files_work();
        FreeMem(NodePtr, sizeof(struct NameNode));
    }
    FreeNameNodes(&DirList); // Not required unless stopping early (although harmless).
}

MODULE void stage2(void)
{   ABOOL            matched, infomatched;
    ULONG            i, length;
    UBYTE            theversion;
    struct NameNode* NodePtr;

    // Everything is already allocated. Don't deallocate in this routine.

    if (shared.log)
    {   if (!(LogFileHandle = (BPTR) Open(shared.output, MODE_READWRITE)))
            rq("Can't open file for appending!");
        Seek(LogFileHandle, 0, OFFSET_END);
    }

    // Now we have the list of files
    getdate();
    strcpy(IOBuffer, "AmigaOS 3.");
    if (files.osversion == 0)
    {   theversion = 31;
        strcat(IOBuffer, "1");
    } elif (files.osversion == 1)
    {   theversion = 35;
        strcat(IOBuffer, "5");
    } elif (files.osversion == 2)
    {   theversion = 36;
        strcat(IOBuffer, "5 + BB1/2");
    } elif (files.osversion == 3)
    {   theversion = 39;
        strcat(IOBuffer, "9");
    } elif (files.osversion == 5)
    {   theversion = 41;
        strcat(IOBuffer, "9 + BB1");
    } else
    {   // assert(files.osversion == 4);
        theversion = 40;
        strcat(IOBuffer, "9 + Genesis Update");
    }
    strcat(IOBuffer, " system files at ");
    strcat(IOBuffer, timestring);
    strcat(IOBuffer, " on ");
    strcat(IOBuffer, weekdaystring);
    strcat(IOBuffer, " ");
    strcat(IOBuffer, datestring);
    strcat(IOBuffer, ":\n"); // another \n is inserted automatically by dobuffer()
    dobuffer(FALSE, 0);

    for (i = 0; i <= FILES; i++)
        os[i].matched = os[i].infomatched = FALSE;

    // stop is always FALSE at this point, because this function is not
    // called otherwise.

    while ((FileList.lh_Head)->ln_Succ) // while the list is non-empty
    {   if (checkbreak() == 1)
        {   stop = TRUE;
            GT_SetGadgetAttrs
            (   TE101_Status,
                MainWindowPtr, NULL,
                GTTX_Text, "Stopping",
                TAG_DONE
            );
            GT_SetGadgetAttrs
            (   BU99_Stop,
                MainWindowPtr, NULL,
                GA_Disabled, TRUE,
                TAG_DONE
            );
            strcpy(IOBuffer, "Aborted by user!");
            dobuffer(FALSE, 0);
            break;
        }
        // assert(!stop);

        if (!(NodePtr = (struct NameNode *) RemTail(&FileList)))
        {   rq("RemTail() failed (list is empty!)"); // should never happen
        }
        strcpy(stringholder1, NodePtr->nn_Data);
        length = strlen(stringholder1);
        FreeMem(NodePtr, sizeof(struct NameNode));

        // now we have removed the file from the `queue', and copied its
        // name into stringholder1. Now we check it against each system
        // file.

        matched = infomatched = FALSE;
        for (i = 0; i <= FILES; i++)
        {   if (os[i].version <= theversion)
            {   if (!matched && quickcomp(stringholder1, os[i].pathname))
                {   // we have a match

                    strcpy(IOBuffer, os[i].pathname);
                    matched = os[i].matched = TRUE;

                    // check whether file is obsoleted by OS3.9 or by Genesis Update
                    if
                    (   (theversion >= 39 && (os[i].flags & NOT39))
                     || (theversion >= 40 && (os[i].flags & NOT39UPDATE))
                     || (os[i].flags & CODE_BLUE)
                    )
                    {   dobuffer(TRUE, 12);
                    } elif (os[i].flags & CODE_RED)
                    {   dobuffer(TRUE,  8);
                    } elif (os[i].flags & CODE_ORANGE)
                    {   dobuffer(TRUE,  9);
                    } elif (os[i].flags & CODE_YELLOW)
                    {   dobuffer(TRUE, 10);
                    } else
                    {   // assert(os[i].flags & CODE_GREEN);
                        dobuffer(TRUE, 11);
                }   }
                elif
                (   !infomatched
                 && (os[i].flags & INFO)
                 && (stringholder1[length - 5] == '.')
                ) // we probably have a match, let's confirm
                {   strcpy(IOBuffer, os[i].pathname);
                    strcat(IOBuffer, ".info");
                    if (quickcomp(stringholder1, IOBuffer))
                    {   // we have a match
                        infomatched = os[i].infomatched = TRUE;

                        // check whether file is obsoleted by OS3.9 or by Genesis Update
                        if
                        (   (theversion >= 39 && (os[i].flags & NOT39))
                         || (theversion >= 40 && (os[i].flags & NOT39UPDATE))
                        )
                        {   dobuffer(TRUE, 12); // obsolete
                        } else
                        {   dobuffer(TRUE, 11); // automatic .info files are always green
        }   }   }   }   }
        if (!matched && !infomatched) // third-party (cyan)
        {   strcpy(IOBuffer, stringholder1);
            dobuffer(TRUE, 13);
    }   }

    // missing files
    if (!stop)
    {   for (i = 0; i <= FILES; i++)
        {   if
            (   !(os[i].flags & CODE_BLUE) // missing obsolete are NEVER shown
             && os[i].version <= theversion
             && (theversion < 39 || !(os[i].flags & NOT39))
             && (theversion < 40 || !(os[i].flags & NOT39UPDATE))
            )
            if (files.show[6] && !(os[i].flags & OPTIONAL))
            {   if (!os[i].matched)
                {   strcpy(IOBuffer, os[i].pathname); // white (missing normal)
                    dobuffer(TRUE, 14);
                }
                if ((os[i].flags & INFO) && !os[i].infomatched)
                {   strcpy(IOBuffer, os[i].pathname); // white (missing normal)
                    strcat(IOBuffer, ".info");
                    dobuffer(TRUE, 14);
            }   }
            elif (files.show[7] &&  (os[i].flags & OPTIONAL))
            {   if (!os[i].matched)
                {   strcpy(IOBuffer, os[i].pathname); // pink (missing optional)
                    dobuffer(TRUE, 15);
                }
                if ((os[i].flags & INFO) && !os[i].infomatched)
                {   strcpy(IOBuffer, os[i].pathname); // pink (missing optional)
                    strcat(IOBuffer, ".info");
                    dobuffer(TRUE, 15);
    }   }   }   }

    if (LogFileHandle)
    {   Close(LogFileHandle); // Close() doesn't return an error code
        LogFileHandle = NULL;
}   }

MODULE void stage3(void)
{   if (wbval >= 44)
    {   /* pens go thusly:
        0 red
        1 orange
        2 yellow
        3 green
        4 blue
        5 cyan
        6 white
        7 pink */

        GT_SetGadgetAttrs
        (   TE101_Status,
            MainWindowPtr, NULL,
            GTTX_Text, "Ready",
            TAG_DONE
        );
        GT_SetGadgetAttrs
        (   BU99_Stop,
            MainWindowPtr, NULL,
            GA_Disabled, TRUE,
            TAG_DONE
        );

        SetGadgetAttrs
        (   gadgets[GID_10_LB1], MainWindowPtr, NULL,
            LISTBROWSER_Labels, NULL,
            TAG_END
        );
        SetGadgetAttrs
        (   gadgets[GID_10_LB1], MainWindowPtr, NULL,
            LISTBROWSER_Labels, (ULONG) &ResultList,
            TAG_END
        );
}   }

/* FileNamePtr[]: each of these is a pointer to the allocated memory
area holding the pathname of that file found (excepting "SYS:" portion).
FileNamesAllocated = number of elements of FileNamePtr[]. Or, another way
to think of it, the number of files on the SYS: partition. Element 0 of
the array is not used. */

MODULE void files_work(void)
{   BOOL                 more; // BOOL, not ABOOL
    BPTR                 DirHandle; // = NULL;
    struct ExAllControl* eac;       // = NULL;
    struct ExAllData*    ead;

    /* Service routine for stage1(). Each call of this routine
    handles one directory from the work stack. This routine is the one
    that actually makes the DOS calls. It pushes any subdirectories
    found onto the stack.

    stringholder1 contains the pathname of the directory to examine
        (without "SYS:").
    lockstring contains the pathname of the directory to examine
        (with "SYS:").
    stringholder2 will contain the pathname of each file/dir found
        (with "SYS:"). */

    strcpy(lockstring, "SYS:");
    strcat(lockstring, stringholder1);

    if (!(DirHandle = (BPTR) Lock(lockstring, ACCESS_READ)))
    {   // Printf("Can't lock %s!\n", lockstring);
        rq("Can't lock directory!");
    }
    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, 2048, 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!"); /* ExAll() failed abnormally */
        }
        if (eac->eac_Entries == 0)
        {   ; /* ExAll() failed normally with no entries */
            continue; /* more is USUALLY zero */
        }
        ead = (struct ExAllData *) EADataPtr;

        do
        {   /* use ead here */

            strcpy(stringholder2, lockstring);
            if (!AddPart(stringholder2, ead->ed_Name, VLONGFIELD))
            {   FreeDosObject(DOS_EXALLCONTROL, eac);
                eac = NULL;
                UnLock(DirHandle);
                DirHandle = NULL;
                rq("Can't add filename/dirname to pathname!");
            }
            if (ead->ed_Type == 2) /* +2 is dir, +3 is softlink, -3 is file */
            {   AddNameToTail(&DirList,  &(stringholder2[4]));
            } elif (ead->ed_Type == -3) // if it's a file
            {   AddNameToTail(&FileList, &(stringholder2[4]));
            }

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

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

MODULE ABOOL __inline quickcomp(STRPTR first, STRPTR second)
{   ULONG i = 0;

    while (*(first + i))
    {   if (*(first + i)      != *(second + i)
        &&  *(first + i)      != *(second + i) + 32
        &&  *(first + i) + 32 != *(second + i)
        &&  *(first + i) + 32 != *(second + i) + 32
        )
            return FALSE;
        i++;
    }
    return TRUE;
}

MODULE void dobuffer(ABOOL item, ULONG whichpen)
{   struct Node* ListBrowserNodePtr;
    TEXT         codestring[VLONGFIELD + 1];

    /* Service routine for stage2(). */

    if (item && files.show[whichpen - 8])
    {   if (!(ListBrowserNodePtr = AllocListBrowserNode
        (   1,              // columns,
            LBNA_Column,    0,
            LBNA_Flags,     LBFLG_CUSTOMPENS,
            LBNCA_FGPen,    BLACK,
            LBNCA_BGPen,    penn[whichpen - 8].pennumber,
            LBNCA_CopyText, TRUE,
            LBNCA_Text,     IOBuffer,
            TAG_END))
        )
        {   rq("Can't create ReAction listbrowser.gadget node(s)!");
        }
        AddTail(&ResultList, ListBrowserNodePtr); // AddTail() has no return code
        ResultNodes = TRUE;
    }
    if (shared.log)
    {   if (!item)
        {   strcpy(codestring, IOBuffer);
            strcat(codestring, "\n");
            if (Write(LogFileHandle, codestring, strlen(codestring)) == -1)
            {   rq("Can't append to file!");
        }   }
        elif (files.show[whichpen - 8])
        {   switch(whichpen)
            {
            case 8:
                strcpy(codestring, "! "); // red
            break;
            case 9:
                strcpy(codestring, "@ "); // orange
            break;
            case 10:
                strcpy(codestring, "# "); // yellow
            break;
            case 11:
                strcpy(codestring, "$ "); // green
            break;
            case 12:
                strcpy(codestring, "% "); // blue (obsolete)
            break;
            case 13:
                strcpy(codestring, "& "); // cyan (3rd-party)
            break;
            case 14:
                strcpy(codestring, "- "); // white (missing normal)
            break;
            case 15:
                strcpy(codestring, "= "); // pink (missing optional)
            break;
            default:
                // assert(0);
            break;
            }
            strcat(codestring, IOBuffer);
            strcat(codestring, "\n");
            if (Write(LogFileHandle, codestring, strlen(codestring)) == -1)
            {   rq("Can't append to file!");
}   }   }   }

AGLOBAL void files_loop(ULONG class, struct Gadget* addr, UWORD code, UWORD qual)
{   ULONG i, scroll;

    if (class == IDCMP_RAWKEY && wbval >= 44)
    {   if (code == SCAN_UP)
        {   if (qual & IEQUALIFIER_CONTROL)
            {   scroll = LBP_TOP;
            } elif (qual & IEQUALIFIER_LSHIFT || qual & IEQUALIFIER_RSHIFT)
            {   scroll = LBP_PAGEUP;
            } else scroll = LBP_LINEUP;
        } elif (code == SCAN_DOWN)
        {   if (qual & IEQUALIFIER_CONTROL)
            {   scroll = LBP_BOTTOM;
            } elif (qual & IEQUALIFIER_LSHIFT || qual & IEQUALIFIER_RSHIFT)
            {   scroll = LBP_PAGEDOWN;
            } else scroll = LBP_LINEDOWN;
        }
        if (code == SCAN_UP || code == SCAN_DOWN)
        {   SetGadgetAttrs
            (   gadgets[GID_10_LB1],          // pointer to gadget
                MainWindowPtr,                // pointer to window (not window object!)
                NULL,                         // pointer to requester
                LISTBROWSER_Position, scroll, // tags
                TAG_DONE                      // done
            );
    }   }
    elif (class == IDCMP_VANILLAKEY)
    {   code = toupper(code);
        if (code == 'V')
        {   if (!(qual & IEQUALIFIER_LSHIFT) && !(qual & IEQUALIFIER_RSHIFT))
            {   if (files.osversion < OSVERSIONS)
                    files.osversion++;
                else files.osversion = 0;
            } else
            {   if (files.osversion > 0)
                    files.osversion--;
                else files.osversion = OSVERSIONS;
            }
            GT_SetGadgetAttrs
            (   CY101_OSVersion,
                MainWindowPtr,
                NULL,
                GTCY_Active, files.osversion,
                TAG_DONE
            );
        } elif (code == 'L')
        {   fliplog(TRUE);
        } elif (code == 'U')
        {   updatefiles();
        } elif (code == 'E')
        {   if (CB101_Show[0]->Flags & GFLG_SELECTED)
                files.show[0] = FALSE;
            else files.show[0] = TRUE;
            GT_SetGadgetAttrs
            (   CB101_Show[0],
                MainWindowPtr, NULL,
                GTCB_Checked, files.show[0],
                TAG_DONE
            );
        } elif (code == 'I')
        {   if (CB101_Show[1]->Flags & GFLG_SELECTED)
                files.show[1] = FALSE;
            else files.show[1] = TRUE;
            GT_SetGadgetAttrs
            (   CB101_Show[1],
                MainWindowPtr, NULL,
                GTCB_Checked, files.show[1],
                TAG_DONE
            );
        } elif (code == 'S')
        {   if (CB101_Show[2]->Flags & GFLG_SELECTED)
                files.show[2] = FALSE;
            else files.show[2] = TRUE;
            GT_SetGadgetAttrs
            (   CB101_Show[2],
                MainWindowPtr, NULL,
                GTCB_Checked, files.show[2],
                TAG_DONE
            );
        } elif (code == 'N')
        {   if (CB101_Show[3]->Flags & GFLG_SELECTED)
                files.show[3] = FALSE;
            else files.show[3] = TRUE;
            GT_SetGadgetAttrs
            (   CB101_Show[3],
                MainWindowPtr, NULL,
                GTCB_Checked, files.show[3],
                TAG_DONE
            );
        } elif (code == 'B')
        {   if (CB101_Show[4]->Flags & GFLG_SELECTED)
                files.show[4] = FALSE;
            else files.show[4] = TRUE;
            GT_SetGadgetAttrs
            (   CB101_Show[4],
                MainWindowPtr, NULL,
                GTCB_Checked, files.show[4],
                TAG_DONE
            );
        } elif (code == '3')
        {   if (CB101_Show[5]->Flags & GFLG_SELECTED)
                files.show[5] = FALSE;
            else files.show[5] = TRUE;
            GT_SetGadgetAttrs
            (   CB101_Show[5],
                MainWindowPtr, NULL,
                GTCB_Checked, files.show[5],
                TAG_DONE
            );
        } elif (code == 'G')
        {   if (CB101_Show[6]->Flags & GFLG_SELECTED)
                files.show[6] = FALSE;
            else files.show[6] = TRUE;
            GT_SetGadgetAttrs
            (   CB101_Show[6],
                MainWindowPtr, NULL,
                GTCB_Checked, files.show[6],
                TAG_DONE
            );
        } elif (code == 'P')
        {   if (CB101_Show[7]->Flags & GFLG_SELECTED)
                files.show[7] = FALSE;
            else files.show[7] = TRUE;
            GT_SetGadgetAttrs
            (   CB101_Show[7],
                MainWindowPtr, NULL,
                GTCB_Checked, files.show[7],
                TAG_DONE
            );
        } elif (code == 'O')
        {   ActivateGadget(ST99_Output, MainWindowPtr, NULL);
        } elif (code == '.')
        {   if (asl())
            {   GT_SetGadgetAttrs
                (   ST99_Output,
                    MainWindowPtr,
                    NULL,
                    GTST_String, aslresult,
                    TAG_DONE
                );
        }   }
        elif (code == ESCAPE)
        {   page = 0;
    }   }
    elif (class == IDCMP_GADGETUP)
    {   /* IDCMP_GADGETUP is sent by the string gadget
           when the user presses RETURN, ENTER, Help, Tab
           or Shift-Tab inside the string gadget. */

        if (addr == BU99_Right)
        {   page = 0;
        } elif (addr == BU99_Update)
        {   updatefiles();
        } elif (addr == CB99_Log)
        {   fliplog(FALSE);
        } elif (addr == ST99_Output)
        {   outputstring();
        } elif (addr == BU99_OutputASL)
        {   outputasl();
        } elif (addr == CY101_OSVersion)
        {   files.osversion = code;
        } else
        {   for (i = 0; i <= 6; i++)
            {   if (addr == CB101_Show[i])
                {   if (CB101_Show[i]->Flags & GFLG_SELECTED)
                    {   files.show[i] = TRUE;
                    } else files.show[i] = FALSE;
                    GT_SetGadgetAttrs
                    (   CB101_Show[i],
                        MainWindowPtr, NULL,
                        GTCB_Checked, files.show[i],
                        TAG_DONE
                    );
                    break;
}   }   }   }   }

AGLOBAL void files_init(void)
{   ULONG i;

    if (wbval == 44)
    {   files.osversion = 2;  // OS3.5 + BB1/2
    } else files.osversion = 5; // OS3.9 + BB1

    lockscreen();
    for (i = 0; i <= 7; i++)
    {   penn[i].pennumber = FindColor
        (   ScreenPtr->ViewPort.ColorMap,
            penn[i].red,
            penn[i].green,
            penn[i].blue,
            -1
        );
    }
    unlockscreen();
}

AGLOBAL void files_exit(void)
{   // The window should be closed before calling this.

    if (ResultNodes)
    {   clearreactionlist(&ResultList);
        ResultNodes = FALSE;
    }
    if (LogFileHandle)
    {   Close(LogFileHandle); // Close() doesn't return an error code
        LogFileHandle = NULL;
}   }

AGLOBAL void files_die(void)
{   ULONG i;

    IOBuffer[7] = files.osversion;

    for (i = 0; i <= 7; i++)
    {   IOBuffer[8 + i] = files.show[i];
}   }

AGLOBAL void files_config(void)
{   ULONG i;

    files.osversion = IOBuffer[7];

    for (i = 0; i <= 7; i++)
    {   files.show[i] = IOBuffer[8 + i];
}   }
