/*

    FreeSpace - graphically displays free space for each mounted volume
    Copyright 1993 Barry McConnell
    bmccnnll@unix1.tcd.ie

*/


#include <exec/execbase.h>
#include <exec/memory.h>
#include <dos/dos.h>
#include <graphics/gfxbase.h>
#include <intuition/intuition.h>
#include <intuition/screens.h>
#include <graphics/text.h> 
#include <libraries/gadtools.h>
#include <workbench/startup.h>
#include <workbench/workbench.h>

#include <proto/dos.h>
#include <proto/exec.h>
#include <proto/intuition.h>
#include <proto/graphics.h>
#include <proto/gadtools.h>
#include <proto/icon.h>

#include <stdio.h>
#include <string.h>

#define TEXTLENGTH 36  /* max length of volume statistics */

struct volume  /* to store volume data */
    {
    struct volume *next;
    struct volume *prev;
    char *name;  /* name of volume */
    char *text;  /* statistics in ASCII */
    LONG free, used, percentage;
    };

static UBYTE *version = "$VER: FreeSpace 1.0 (27.08.93)";
extern struct ExecBase *SysBase;
UWORD osver = 0;  /* Kickstart version */

UWORD __chip waitPointer[] =
    {
    0x0000, 0x0000,
    0x0400, 0x07c0,
    0x0000, 0x07c0,
    0x0100, 0x0380,
    0x0000, 0x07e0,
    0x07c0, 0x1ff8,
    0x1ff0, 0x3fec,
    0x3ff8, 0x7fde,
    0x3ff8, 0x7fbe,
    0x7ffc, 0xff7f,
    0x7efc, 0xffff,
    0x7ffc, 0xffff,
    0x3ff8, 0x7ffe,
    0x3ff8, 0x7ffe,
    0x1ff0, 0x3ffc,
    0x07c0, 0x1ff8,
    0x0000, 0x07e0,
    0x0000, 0x0000,
    };


/*
    Display a busy pointer in the current window, and prevent user from
    clicking on any gadgets.
*/

void busyPointer(struct Window *myWindow, struct Requester *myReq)
{
InitRequester(myReq);
Request(myReq, myWindow);
if (osver < 39)
    SetPointer(myWindow, waitPointer, 16, 16, -6, 0);
else
    SetWindowPointer(myWindow,  WA_BusyPointer,     TRUE,
                                WA_PointerDelay,    TRUE,
                                TAG_END);
}


/*
    Restore the pointer to normal.
*/

void normalPointer(struct Window *myWindow, struct Requester *myReq)
{
ClearPointer(myWindow);
EndRequest(myReq, myWindow);
}


/*
    Compare two strings. Like "strcmp" but case-insensitive.
*/

int Compare(char *s, char *t)
{
for (; ((*s & ~32) == (*t & ~32)) && *s; s++, t++)  /* sorry, K&R ;-) */
    ;
return (*s & ~32) - (*t & ~32);
}


/*
    Alphabetically insert "node" into "list".
*/

void Sort(struct List *list, struct volume *node)
{
struct volume *temp = (struct volume *)list -> lh_Head;

while (temp -> next && Compare(temp -> name, node -> name) < 0)
    temp = temp -> next;

Insert(list, (struct Node *)node, (struct Node *)temp -> prev);
}


void main(int argc, char *argv[])
{
struct DiskObject   *myIcon;
struct Screen       *myScreen;
struct Window       *myWindow;
struct Requester    tinyReq;
struct VisualInfo   *visInfo;
struct TextFont     *propFont;
struct TextExtent   fontSize;
struct RastPort     myRast, *winRast;
struct DosList      *dl;
struct InfoData     info;
struct Remember     *memoryKey;
struct List         volumeList;
struct IntuiMessage *message;
struct volume       *volume;
int                 maxNameLen = 0, maxTextLen = 0, count = 0, memOkay = TRUE, tempLen,
                    winLeft = 0, winTop = 0, winWidth, winHeight, pen = 3, title, gap, rectSize;
ULONG               class, code;
UWORD               height;
BYTE                i,j;
BPTR                lock;
char                **tools, *string, *myName, *name, *dest, freeSuffix, usedSuffix;

struct EasyStruct about =
    {
    sizeof(struct EasyStruct),
    0,
    "About FreeSpace",
    "FreeSpace v1.0 -- Freely Distributable\nCopyright 1993 Barry McConnell\nbmccnnll@unix1.tcd.ie\n\nDedicated to Niall Murphy :-)",
    "Okay"
    };

IntuitionBase = NULL;
DOSBase = NULL;
GfxBase = NULL;
GadToolsBase = NULL;
osver = SysBase -> LibNode.lib_Version;  /* running under 2.0, 3.0, ...? */

if (IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library", 37))
if (DOSBase = (struct DosLibrary *)OpenLibrary("dos.library", 37))
if (GfxBase = (struct GfxBase *)OpenLibrary("graphics.library", 37))
if (GadToolsBase = OpenLibrary("gadtools.library", 37))
if (IconBase = OpenLibrary("icon.library", 37))
    {
    if (!argc)  /* only look at ToolTypes if started from Workbench */
        {
        myName = ((struct WBStartup *)argv) -> sm_ArgList -> wa_Name;  /* find ourselves */
        if (myIcon = GetDiskObject(myName));  /* read in .info file */
            {
            tools = myIcon -> do_ToolTypes;
            if (string = FindToolType(tools, "LEFT"))
                sscanf(string, "%d", &winLeft);
            if (string = FindToolType(tools, "TOP"))
                sscanf(string, "%d", &winTop);
            if (string = FindToolType(tools, "PEN"))
                sscanf(string, "%d", &pen);
            FreeDiskObject(myIcon);
            }
        }

    myScreen = LockPubScreen(NULL);  /* get a lock on the Workbench screen */
    visInfo = GetVisualInfo(myScreen, TAG_END);  /* GadTools needs this */
    propFont = OpenFont(myScreen -> Font);
    FontExtent(propFont, &fontSize);  /* figure out pixel size of fonts */
    height = fontSize.te_Height;

    InitRastPort(&myRast);  /* dummy RastPort for getting string lengths */
    SetFont(&myRast, propFont);

    memoryKey = NULL;
    volumeList.lh_Head      = (struct Node *)&volumeList.lh_Tail;
    volumeList.lh_Tail      = NULL;
    volumeList.lh_TailPred  = (struct Node *)&volumeList.lh_Head;

    dl = LockDosList(LDF_VOLUMES | LDF_READ);
    while ((dl = NextDosEntry(dl, LDF_VOLUMES)) && memOkay)  /* walk through list of volumes */
        {
        if ((volume = AllocRemember(&memoryKey, sizeof(struct volume), MEMF_ANY))
            && (volume -> text = AllocRemember(&memoryKey, TEXTLENGTH, MEMF_ANY)))
            {
            name = (char *)BADDR(dl -> dol_Name);  /* BCPL string */
            i = *((BYTE *)name);  /* length */
            dest = volume -> name = AllocRemember(&memoryKey, i+2, MEMF_ANY);
            for (j = 1; i; i--)
                *(dest++) = name[j++];
            *(dest++) = ':';
            *(dest) = 0;

            lock = Lock(volume -> name, ACCESS_READ);  /* get a lock on this volume */
            *(--dest) = 0;  /* remove trailing colon */

            Info(lock, &info);
            freeSuffix = usedSuffix = 'K';  /* default */
            volume -> free = (info.id_NumBlocks - info.id_NumBlocksUsed) * info.id_BytesPerBlock / 1024;
            volume -> used = info.id_NumBlocksUsed * info.id_BytesPerBlock / 1024;
            volume -> percentage = info.id_NumBlocksUsed * 100 / info.id_NumBlocks;
            if (volume -> free > 9999)
                {
                volume -> free = volume -> free / 1024;  /* move up to MB */
                freeSuffix = 'M';
                }
            if (volume -> used > 9999)
                {
                volume -> used = volume -> used / 1024;
                usedSuffix = 'M';
                }
            sprintf(volume -> text, "%d%% full, %d%c free, %d%c in use", volume -> percentage, volume -> free, freeSuffix, volume -> used, usedSuffix);

            SetSoftStyle(&myRast, FSF_BOLD, FSF_BOLD);
            tempLen = TextLength(&myRast, volume -> name, j-1);
            if (maxNameLen < tempLen)
                maxNameLen = tempLen;
            SetSoftStyle(&myRast, NULL, FSF_BOLD);  /* turn off bold */
            tempLen = TextLength(&myRast, volume -> text, strlen(volume -> text));
            if (maxTextLen < tempLen)
                maxTextLen = tempLen;

            Sort(&volumeList, volume);
            UnLock(lock);
            count++;
            }
        else
            memOkay = FALSE;
        }
    UnLockDosList(LDF_VOLUMES | LDF_READ);

    if (memOkay)
        {
        title = (myScreen -> WBorTop) + height + 1 + INTERHEIGHT;
        gap = title + propFont -> tf_Baseline;
        winWidth = INTERWIDTH*2 + maxTextLen;
        winHeight = title + count * (height*2 + INTERHEIGHT*3) - INTERHEIGHT + 2;  /* allow a couple of pixels for the comma */

        if (myWindow = OpenWindowTags(NULL, WA_Title,       "FreeSpace",
                                            WA_Left,        winLeft,
                                            WA_Top,         winTop,
                                            WA_Width,       winWidth,
                                            WA_Height,      winHeight,
                                            WA_DragBar,     TRUE,
                                            WA_DepthGadget, TRUE,
                                            WA_CloseGadget, TRUE,
                                            WA_Activate,    TRUE,
                                            WA_RMBTrap,     TRUE,
                                            WA_IDCMP,       IDCMP_CLOSEWINDOW | RAWKEY,
                                            WA_PubScreen,   myScreen,
                                            TAG_END))
            {
            volume = (struct volume *)volumeList.lh_Head;
            winRast = myWindow -> RPort;
            SetFont(winRast, propFont);  /* defaults to GfxBase -> DefaultFont, which is not what we want */

            for (i = 0; i < count; volume = volume -> next, i++)
                {
                SetAPen(winRast, 1);  /* black */
                Move(winRast, INTERWIDTH, gap + i * (height*2 + INTERHEIGHT*3));
                SetSoftStyle(winRast, FSF_BOLD, FSF_BOLD);
                Text(winRast, volume -> name, strlen(volume -> name));
                Move(winRast, INTERWIDTH, gap + height + INTERHEIGHT + i * (height*2 + INTERHEIGHT*3));
                SetSoftStyle(winRast, NULL, FSF_BOLD);
                Text(winRast, volume -> text, strlen(volume -> text));
                DrawBevelBox(winRast, (INTERWIDTH*3) / 2 + maxNameLen, title + i * (height*2 + INTERHEIGHT*3),
                                winWidth - maxNameLen - (INTERWIDTH*5) / 2, height,
                                GTBB_Recessed,  TRUE,
                                GT_VisualInfo,  visInfo,
                                TAG_END);
                SetAPen(winRast, pen);  /* pen to fill with (user's choice) */
                rectSize = winWidth - maxNameLen - INTERWIDTH*3;
                rectSize = (rectSize * volume -> percentage) / 100;
                RectFill(winRast, (INTERWIDTH*3) / 2 + maxNameLen + 2, title + i * (height*2 + INTERHEIGHT*3) + 1,
                            (INTERWIDTH*3) / 2 + maxNameLen + 1 + rectSize, title + i * (height*2 + INTERHEIGHT*3) + height - 2);
                }

            do
                {
                WaitPort(myWindow -> UserPort);
                if (message = (struct IntuiMessage *)GetMsg(myWindow -> UserPort))
                    class = message -> Class, code = message -> Code;
                ReplyMsg((struct Message *)message);
                if (class == RAWKEY && code == 95)  /* Help key */
                    {
                    busyPointer(myWindow, &tinyReq);
                    EasyRequest(NULL, &about, NULL, NULL);
                    normalPointer(myWindow, &tinyReq);
                    }
                }
            while (!(class == IDCMP_CLOSEWINDOW));

            CloseWindow(myWindow);
            }
        }
    FreeRemember(&memoryKey, TRUE);
    CloseFont(propFont);
    FreeVisualInfo(visInfo);
    UnlockPubScreen(NULL, myScreen);
    }
CloseLibrary(GadToolsBase);  /* safe to pass NULL */
CloseLibrary((struct Library *)GfxBase);
CloseLibrary((struct Library *)DOSBase);
CloseLibrary((struct Library *)IntuitionBase);
}
