/* -----------------------------------------------------------------------------

 GoldED API client. ©1995 Dietmar Eilert. All Rights Reserved.
 
 Dice:

 dcc main.c cx.a sprintf.a -// -mRR -proto -2.0 -l tmsr.lib -l reqtoolssr.lib -o ram:dock

 Note: Compiling this code requires ToolManger includes & ToolManager linker
       libraries. ToolManager is ©1990-1995 Stefan Becker.

  ------------------------------------------------------------------------------
*/

/// "includes"

#define Prototype extern

#include <exec/exec.h>
#include <ctype.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <dos/dos.h>
#include <dos/rdargs.h>
#include <dos/dostags.h>
#include <intuition/intuition.h>
#include <workbench/startup.h>
#include <rexx/errors.h>
#include <rexx/rxslib.h>
#include <rexx/storage.h>
#include <libraries/reqtools.h>
#include <utility/tagitem.h>
#include <clib/toolmanager_protos.h>
#include <clib/exec_protos.h>
#include <clib/alib_protos.h>
#include <clib/dos_protos.h>
#include <clib/intuition_protos.h>
#include <clib/utility_protos.h>
#include <clib/rexxsyslib_protos.h>
#include <clib/reqtools_protos.h>
#include <clib/commodities_protos.h>
#include "golded:api/include/golded.h"

#define SIG_KILL         SIGBREAKF_CTRL_C
#define SIG_HANDSHAKE    SIGF_SINGLE
#define TMET_Hotkey      10
#define TMET_GoldED      11

///
/// "definitions"

// ToolManager objects are a combination of action/image/sound:

struct Tool {

    struct Node     Node;
    APTR            Exec;
    APTR            Icon;
    APTR            Sound;
};

// parser array: commands, templates, handlers

struct Parser { 

    UBYTE          *Command;
    APTR            Server;
    UBYTE          *Template;
};

// linked list of all PROG objects (CLI/AREXX/HOTKEY/GOLDED) found in the preset file:

struct Event {

    struct Node     Node;
    UWORD           Type;
    UBYTE          *Command;
    UBYTE          *Dir;
    UBYTE          *Key;
    UBYTE          *Output;
    BOOL            Hotkey;
    BOOL            Args;
    BOOL            Activate;
};

// linked list of all docks found in the preset file:

struct Dock {

    struct Node     Node;
    struct TagItem *Tags;
    BOOL            Visible;
    UWORD           Elements;
};

///
/// "prototypes"

Prototype void   StopServer(struct Task **);
Prototype void   InsertEvent(UBYTE *);
Prototype void   PutIEvents(struct InputEvent *);
Prototype int    main(int, char **);
Prototype UBYTE *xsprintf(UBYTE *, UBYTE *);
Prototype void   AddProg (ULONG *, struct List *);
Prototype void   AddIcon (ULONG *, struct List *);
Prototype void   AddSound(ULONG *, struct List *);
Prototype void   AddTool (ULONG *, struct List *);
Prototype void   AddDock (ULONG *, struct List *);
Prototype void   HandleAPI(struct Task **);
Prototype void   ReadConfig(UBYTE *);
Prototype void   CloseRexxPort(struct MsgPort *, UWORD, BOOL);
Prototype ULONG *SendRexxCommand(UBYTE *, UBYTE *, struct MsgPort *, BOOL, UWORD *, UBYTE *);
Prototype struct Node *SearchName(struct List *, UBYTE *);
Prototype void   FreeRexxCommand(struct RexxMsg *);
Prototype UWORD  CountList(struct List *);
Prototype void   Puts(UBYTE *);
Prototype UBYTE *myprintf(UBYTE *, UBYTE *, ...);
Prototype void   ARexxServer(void);
Prototype void   ShowDocks(struct Dock *);
Prototype void   HideDocks(void);
Prototype void   DoExecute(UBYTE *, BOOL, UBYTE *, UBYTE *, UWORD, WORD);
Prototype UBYTE *stristr(UBYTE *, UBYTE *);
Prototype void   Replace(UBYTE *, UBYTE *, UBYTE *);
Prototype void   CommandDispatch(struct RDArgs *, struct APIMessage *);
Prototype LONG   ModifyDock(ULONG *, struct APIMessage *);

Prototype __stkargs struct InputEvent *MyInvertString(UBYTE *, APTR);
Prototype __stkargs void               MyFreeIEvents(struct InputEvent *);

///
/// "globals"

UBYTE Version = "$VER: NextStep 1.6 (" __COMMODORE_DATE__ ")";

UWORD            ARexxReplies;
struct Library  *ReqToolsBase;
struct Library  *CxBase;
struct Task     *MainTask;
struct List      EventList, DockList;
void            *TMHandle;
UBYTE            Screen[255], Host[255];

// parser definitions used to scan preset file

struct Parser ConfigParser[] = {

    "PROG",  (APTR)AddProg,    "NAME/A,COMMAND/K/A,AREXX/S,CLI/S,HOTKEY/S,WB/S,DIR/K,OUTPUT/K,ARGS/S,KEY/K,GOLDED/S,ACTIVATE/S",
    "SOUND", (APTR)AddSound,   "NAME/A,COMMAND/K/A,PORT/K/A",
    "ICON",  (APTR)AddIcon,    "NAME/A,FILE/A",
    "DTOOL", (APTR)AddTool,    "PROG/K/A,ICON/K,SOUND/K",
    "DOCK",  (APTR)AddDock,    "NAME/A,X/N,Y/N,HORIZONTAL/S,COLUMNS/N,HOTKEY/K,TITLE/K,CENTER/S,POPUP/S,HIDDEN/S,TEXT/S,STICKY/S,FRONTMOST/S,PATTERN/S,VERTICAL/S,LINEAR/S",
     NULL
};

// parser definitions used to process commands sent by GoldED (API interface):

struct Parser CommandParser[] = {

    "DOCK",  (APTR)ModifyDock, "NAME/A,X/N,Y/N,SHOW/S,HIDE/S,TOGGLE/S,HORIZONTAL/S,VERTICAL/S,ROTATE/S,NEWLOOK/S,LINEAR/S",
     NULL
};

///
/// "main"

/* ----------------------------------- main ------------------------------------

 Start ToolManager, start background server (server performs all dock-related
 actions, e.g. the server will run commands), read config file, call API loop.

*/

int
main(int argc, char **argv)
{
    if (FindPort("DOCKSERVER"))

        Puts("NextStep: Can't run dock twice");

    else if (CxBase = OpenLibrary("commodities.library", 37)) {

        struct RDArgs *rdArgs;

        ULONG args[] = { 0, 0 };

        MainTask = FindTask(0);

        if (rdArgs = ReadArgs("CONFIG/K,HOST/K/A", args, NULL)) {

            UWORD  try;

            for (try = 20; !(TMHandle = AllocTMHandle()) && try; try--)
                Delay(10);

            if (TMHandle) {

                struct Task *server;

                if (server = (struct Task *)CreateNewProcTags(NP_Entry, ARexxServer, NP_Name, "DOCKSERVER", NP_Priority, 1, TAG_DONE)) {

                    NewList(&DockList );
                    NewList(&EventList);

                    strcpy(Host, (UBYTE *)args[1]);

                    ReadConfig(args[0] ? (char *)args[0] : "progdir:dock.prefs");

                    if (DockList.lh_Head->ln_Succ)
                        HandleAPI(&server);

                    FreeTMHandle(TMHandle);

                    StopServer(&server);
                }
            }
            else
                Puts("got no ToolManager handle ?!");

            FreeArgs(rdArgs);
        }
        else
            Puts("syntax error");

        CloseLibrary(CxBase);
    }

    exit(0);
}

/* ---------------------------------- wbmain -----------------------------------

 Workbench entry point; exit gracefully

*/

int
wbmain(struct WBStartup *startup)
{
    if (ReqToolsBase = OpenLibrary("reqtools.library", 37)) {

        rtEZRequestTags("No executable - to be used as GoldED API client", "OK", NULL, NULL, TAG_DONE);

        CloseLibrary(ReqToolsBase);
    }

    exit(0);
}


///
/// "read config"

/* -------------------------------- ReadConfig ---------------------------------

 Read preset file. Build linked list of  dock  definitions  (DockList).  PROG
 definitions  (AREXX/CLI/HOTKEY/GOLDED)  are  appended  to the EventList; the
 contents of the EventList are examined if ToolManger tells us  that  a  dock
 icon  has  been  activated  by the user. DTOOL definitions (command + icon +
 sound) are appended to the <toolList>. The <tooList> is appended to the next
 dock created by a DOCK line.

*/

void
ReadConfig(prefs)

UBYTE *prefs;
{
    BPTR config;

    if (config = Open(prefs, MODE_OLDFILE)) {

        struct RDArgs *rdArgs;

        struct List toolList;

        NewList(&toolList);

        if (rdArgs = AllocDosObject(DOS_RDARGS, NULL)) {

            static UBYTE buffer[256];

            while (FGets(config, buffer, 255)) {

                UBYTE *cmd;

                for (strcat(buffer, "\12"), cmd = buffer; *cmd && (*cmd != ';'); ++cmd) {

                    if ((*cmd != ' ') && (*cmd != 10)) {

                        ULONG n, argArray[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

                        struct RDArgs *args;

                        for (n = 0; ConfigParser[n].Command; ++n) {

                            if (!memcmp(cmd, ConfigParser[n].Command, strlen(ConfigParser[n].Command))) {

                                cmd += strlen(ConfigParser[n].Command);

                                rdArgs->RDA_Source.CS_Buffer = cmd;
                                rdArgs->RDA_Source.CS_Length = strlen(cmd);
                                rdArgs->RDA_Source.CS_CurChr = 0;
                                rdArgs->RDA_DAList           = NULL;
                                rdArgs->RDA_Buffer           = NULL;

                                if (args = ReadArgs(ConfigParser[n].Template, argArray, rdArgs)) {

                                    (*((void (*)(ULONG *, struct List *))ConfigParser[n].Server))(argArray, &toolList);

                                    FreeArgs(args);
                                }
                                else {

                                    PrintFault(IoErr(), "syntax error");
                                    Puts(buffer);
                                }
                            }
                        }

                        break;
                    }
                }
            }

            FreeDosObject(DOS_RDARGS, rdArgs);
        }

        Close(config);
    }
    else
        Puts("dock preset file missing");
}

/* ---------------------------------- AddProg ----------------------------------

 Add a command object (exec/WB/ARexx). The command definition is  added  to  our
 internal EventList only. An ARexx object is added to ToolManagers pool, telling
 ToolManager to send us the object's name upon activation: Events are  performed
 by  us  (i.e.  the  background  task),  not  by  ToolManager. This gives us the
 opportunity to change command strings before execution (e.g. in order to insert
 GoldED's  current screen name). Event definitions are added to the <EventList>;
 we are going to scan this list if we recieve ARexx messages by  ToolManager  to
 determine approbiate action.

 template: NAME/A,COMMAND/K/A,AREXX/S,CLI/S,HOTKEY/S,WB/S,DIR/K,OUTPUT/K,ARGS/S,KEY/K,GOLDED/S,ACTIVATE/S,SHANGHAI/N",

*/

void
AddProg(args, toolList)

ULONG       *args;
struct List *toolList;
{
    struct Event *event;

    if (event = malloc(sizeof(struct Event))) {

        UBYTE *name = strdup((UBYTE *)args[0]);

        struct TagItem execTags[] = {

            TMOP_ExecType,  TMET_ARexx,
            TMOP_Command,   xsprintf("'address DOCKSERVER; \42%s\42'", name),
            TAG_DONE
        };

        event->Node.ln_Name = strdup((UBYTE *)args[0]);
        event->Command      = strdup((UBYTE *)args[1]);
        event->Dir          = args[6]  ? strdup((UBYTE *)args[6]) : NULL;
        event->Key          = args[9]  ? strdup((UBYTE *)args[9]) : NULL;
        event->Output       = args[7]  ? strdup((UBYTE *)args[7]) : NULL;
        event->Hotkey       = args[4];
        event->Args         = args[8];
        event->Activate     = args[11];
        event->Type         = TMET_CLI;

        if (args[2])                                 // AREXX/S
            event->Type = TMET_ARexx;

        if (args[5])                                 // WB/S
            event->Type = TMET_WB;

        if (args[4])                                 // HOTKEY/S
            event->Type = TMET_Hotkey;

        if (args[10])
            event->Type = TMET_GoldED;               // GOLDED/S

        AddTail(&EventList, &event->Node);

        CreateTMObjectTagList(TMHandle, name, TMOBJTYPE_EXEC, execTags);
    }
}

/* ---------------------------------- AddIcon ----------------------------------

 Add icon object to ToolManager's pool

 template: NAME/A,FILE/A

*/

void
AddIcon(args, toolList)

ULONG       *args;
struct List *toolList;
{
    struct TagItem iconTags[] = { 

        TMOP_File,  strdup((UBYTE *)args[1]),
        TMOP_Data,  NULL,
        TAG_DONE 
    };

    CreateTMObjectTagList(TMHandle, strdup((UBYTE *)args[0]), TMOBJTYPE_IMAGE, iconTags);
}


/* --------------------------------- AddSound ----------------------------------

 Add a sound object to ToolManagers pool

 template: NAME/A,COMMAND/K/A,PORT/K/A

*/

void
AddSound(args, toolList)

ULONG       *args;
struct List *toolList;
{
    struct TagItem soundTags[] = {

        TMOP_Command, strdup((UBYTE *)args[1]),
        TMOP_Port,    strdup((UBYTE *)args[2]),
        TAG_DONE 
    };

    CreateTMObjectTagList(TMHandle, strdup((UBYTE *)args[0]), TMOBJTYPE_SOUND, soundTags);
}

/* ---------------------------------- AddTool ----------------------------------

 Add a tool object (i.e. combination of command, icon, sound) to the list of
 tool objects. This list is a temporary list only; the list is appended to the
 next dock created by a DOCK line found in the preset file.

 template: PROG/K/A,ICON/K,SOUND/K

*/

void
AddTool(args, toolList)

ULONG       *args;
struct List *toolList;
{
    struct Tool *tool;

    if (tool = malloc(sizeof(struct Tool))) {

        tool->Exec  = args[0] ? strdup((UBYTE *)args[0]) : NULL;
        tool->Icon  = args[1] ? strdup((UBYTE *)args[1]) : NULL;
        tool->Sound = args[2] ? strdup((UBYTE *)args[2]) : NULL;

        AddTail(toolList, &tool->Node);
    }
}

/* ---------------------------------- AddDock ----------------------------------

 Add a dock description to the list  of  docks.  This  function  won't  actually
 create  a  dock on the screen. Instead, it will create a ready-to-use tag list.
 Passing this list to the ToolManager library (done by ShowDocks()) will  create
 the dock.
 
 template: NAME/A,X/N,Y/N,HORIZONTAL/S,COLUMNS/N,HOTKEY/K,TITLE/K,CENTER/S,POPUP/S,HIDDEN/S,TEXT/S,STICKY/S,FRONTMOST/S,PATTERN/S,VERTICAL/S,LINEAR/S

*/

void
AddDock(args, toolList)

ULONG       *args;
struct List *toolList;
{
    UWORD entries;

    if (entries = CountList(toolList)) {

        struct Dock *dock;

        if (dock = malloc(sizeof(struct Dock))) {

            if (dock->Tags = (struct TagItem *)calloc((entries + 14) * sizeof(struct TagItem), 1)) {

                struct Node *tool;
                UWORD  n;

                dock->Tags[0].ti_Tag   = TMOP_PubScreen;

                dock->Tags[1].ti_Tag   = TMOP_LeftEdge;
                dock->Tags[1].ti_Data  = args[1] ? *(ULONG *)args[1] : ~0;

                dock->Tags[2].ti_Tag   = TMOP_TopEdge;
                dock->Tags[2].ti_Data  = args[2] ? *(ULONG *)args[2] : ~0;

                dock->Tags[3].ti_Tag   = TMOP_Activated;
                dock->Tags[3].ti_Data  = !args[9];

                dock->Tags[4].ti_Tag   = TMOP_Centered;
                dock->Tags[4].ti_Data  = args[7];

                dock->Tags[5].ti_Tag   = TMOP_Columns;
                dock->Tags[5].ti_Data  = args[4] ? (*(ULONG *)args[4]) : (args[15] ? (args[14] ? 1 : entries) : 0);

                dock->Tags[6].ti_Tag   = TMOP_Vertical;
                dock->Tags[6].ti_Data  = !args[14];

                dock->Tags[7].ti_Tag   = TMOP_HotKey;
                dock->Tags[7].ti_Data  = args[5] ? strdup((UBYTE *)args[5]) : NULL;

                dock->Tags[8].ti_Tag   = TMOP_Title;
                dock->Tags[8].ti_Data  = args[6] ? strdup((UBYTE *)args[6]) : NULL;                                        // TITLE/K

                dock->Tags[9].ti_Tag   = TMOP_PopUp;
                dock->Tags[9].ti_Data  = args[8];                                        // TITLE/K

                dock->Tags[10].ti_Tag  = TMOP_Text;
                dock->Tags[10].ti_Data = args[10];                                        // TITLE/K

                dock->Tags[11].ti_Tag  = TMOP_FrontMost;
                dock->Tags[11].ti_Data = args[12];                                        // TITLE/K

                dock->Tags[12].ti_Tag  = TMOP_Pattern;
                dock->Tags[12].ti_Data = args[13];                                        // TITLE/K

                // add the tool description tags:

                for (n = 13, tool = toolList->lh_Head; tool->ln_Succ; ++n, tool = tool->ln_Succ) {

                    dock->Tags[n].ti_Tag  = TMOP_Tool;
                    dock->Tags[n].ti_Data = &((struct Tool *)tool)->Exec;
                }

                dock->Tags[n].ti_Tag  = TAG_DONE;
                dock->Tags[n].ti_Data = NULL;

                dock->Visible  = FALSE;
                dock->Elements = entries;

                dock->Node.ln_Name = strdup((UBYTE *)args[0]);

                AddTail(&DockList, &dock->Node);
            }
        }
    }
    else
        Puts("error: dock empty");

    NewList(toolList);
}

///
/// "API management"

/* --------------------------------- HandleAPI ---------------------------------

 Register with GoldED & handle incoming API messages.

*/

void
HandleAPI(server)

struct Task **server;
{
    struct MsgPort *rexxPort, *apiPort;

    if (rexxPort = CreateMsgPort()) {

        if (apiPort = CreateMsgPort()) {

            struct RDArgs *rdArgs;

            if (rdArgs = AllocDosObject(DOS_RDARGS, NULL)) {

                UBYTE  command[255];
                ULONG *result;

                myprintf(command, "API PORT=%ld CLASS=%ld", apiPort, API_CLASS_ROOT | API_CLASS_SCREEN | API_CLASS_REXX);

                if ((result = SendRexxCommand(Host, command, rexxPort, FALSE, NULL, NULL)) && (*result == RC_OK)) {

                    do {

                        struct APIMessage *apiMsg, *nextMsg;

                        while (!(apiMsg = (struct APIMessage *)GetMsg(apiPort)))

                            WaitPort(apiPort);

                        do {

                            for (nextMsg = apiMsg; nextMsg; nextMsg = nextMsg->api_Next) {

                                if (nextMsg->api_State == API_STATE_NOTIFY) {

                                    switch (nextMsg->api_Class) {

                                        case API_CLASS_ROOT:

                                            switch (nextMsg->api_Action) {

                                                case API_ACTION_DIE:

                                                    HideDocks();

                                                    StopServer(server);

                                                    server = NULL;

                                                    break;

                                                case API_ACTION_INTRODUCE:

                                                    static struct TagItem tags[] = {

                                                        API_Client_Name,      "NeXTStep",
                                                        API_Client_Copyright, "©1995 Dietmar Eilert",
                                                        API_Client_Purpose,   "Dock Management System",
                                                        API_Client_Template,  "DOCK NAME/A,X/N,Y/N,SHOW/S,HIDE/S,TOGGLE/S,HORIZONTAL/S,VERTICAL/S,ROTATE/S,NEWLOOK/S,LINEAR/S",
                                                        TAG_DONE
                                                    };

                                                    nextMsg->api_Data = tags;
                                                    break;

                                                default:

                                                    nextMsg->api_Error = API_ERROR_UNKNOWN;
                                            }

                                            break;

                                        case API_CLASS_REXX:

                                            switch (nextMsg->api_Action) {

                                                case API_ACTION_COMMAND:

                                                    CommandDispatch(rdArgs, nextMsg);
                                                    break;

                                                default:

                                                    nextMsg->api_Error = API_ERROR_UNKNOWN;
                                            }
                                            break;

                                        case API_CLASS_SCREEN:

                                            switch (nextMsg->api_Action) {

                                                case API_ACTION_HIDE:

                                                    HideDocks();
                                                    break;

                                                case API_ACTION_SHOW:

                                                    strcpy(Screen, nextMsg->api_Global->F_ScrnName);

                                                    ShowDocks(NULL);
                                                    break;

                                                default:

                                                    nextMsg->api_Error = API_ERROR_UNKNOWN;
                                            }
                                            break;

                                        default:

                                            nextMsg->api_Error = API_ERROR_UNKNOWN;
                                    }
                                }
                            }

                            ReplyMsg((struct Message *)apiMsg);

                        } while (apiMsg = (struct APIMessage *)GetMsg(apiPort));

                    } while (server);
                }

                FreeDosObject(DOS_RDARGS, rdArgs);
            }

            DeleteMsgPort(apiPort);
        }

        DeleteMsgPort(rexxPort);
    }
}

///
/// "command dispatcher"

/* ------------------------------ CommandDispatch ------------------------------

 Dispatch commands sent by GoldED via the API interface. Commands are uppercase.

*/

void
CommandDispatch(rdArgs, apiMsg)

struct RDArgs     *rdArgs;
struct APIMessage *apiMsg;
{
    struct APIRexxNotify *notify = (struct APIRexxNotify *)apiMsg->api_Data;

    UWORD n;

    for (n = 0; CommandParser[n].Command; ++n) {

        if (memcmp(notify->arn_Command, CommandParser[n].Command, strlen(CommandParser[n].Command)) == NULL) {

            static UBYTE buffer[1024];
            
            UBYTE *arguments = buffer + strlen(CommandParser[n].Command);

            strcpy(buffer, notify->arn_Command);
            strcat(buffer, "\12");

            rdArgs->RDA_Source.CS_Buffer = arguments;
            rdArgs->RDA_Source.CS_Length = strlen(arguments);
            rdArgs->RDA_Source.CS_CurChr = 0;
            rdArgs->RDA_DAList           = NULL;
            rdArgs->RDA_Buffer           = NULL;

            if (CommandParser[n].Template) {

                ULONG argArray[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

                if (ReadArgs(CommandParser[n].Template, argArray, rdArgs)) {

                    notify->arn_RC = (*(LONG (*)(ULONG *, struct APIMessage *))CommandParser[n].Server)(argArray, apiMsg);

                    FreeArgs(rdArgs);
                }
                else {

                    static char errorText[81];

                    Fault(IoErr(), "IoErr()", errorText, 80);

                    notify->arn_RC           = RC_WARN;
                    notify->arn_CommandError = errorText;
                }
            }
            else
                notify->arn_RC = (*(LONG (*)(ULONG *, struct APIMessage *))CommandParser[n].Server)(NULL, apiMsg);

            apiMsg->api_State = API_STATE_CONSUMED;
        }
    }
}

/* -------------------------------- ModifyDock ---------------------------------

 Modify look of a dock

 template: NAME/A,X/N,Y/N,SHOW/S,HIDE/S,TOGGLE/S,HORIZONTAL/S,VERTICAL/S,ROTATE/S,NEWLOOK/S,LINEAR/S

*/

LONG
ModifyDock(args, apiMsg)

ULONG  *args;
struct APIMessage *apiMsg;
{
    struct Dock *dock;

    if (dock = SearchName(&DockList, (UBYTE *)args[0])) {

        struct TagItem *tag;
        BOOL            visible;

        if (visible = dock->Visible)
            DeleteTMObject(TMHandle, dock->Node.ln_Name);

        dock->Visible = FALSE;

        if (args[1])                                 // X/N

            FindTagItem(TMOP_LeftEdge, dock->Tags)->ti_Data = *(ULONG *)args[1];

        if (args[2])                                 // Y/N

            FindTagItem(TMOP_TopEdge, dock->Tags)->ti_Data = *(ULONG *)args[2];

        tag = FindTagItem(TMOP_Vertical, dock->Tags);

        if (args[6])                                 // HORIZONTAL/S
            tag->ti_Data = TRUE;

        if (args[7])                                 // VERTICAL/S
            tag->ti_Data = FALSE;

        if (args[8])                                 // ROTATE/S
            tag->ti_Data = !tag->ti_Data;

        if (args[10])                                // LINEAR/S

            FindTagItem(TMOP_Columns, dock->Tags)->ti_Data = (tag->ti_Data) ? dock->Elements : 1;

        tag = FindTagItem(TMOP_Text, dock->Tags);

        if (args[9])                                 // NEWLOOK/S
            tag->ti_Data = !tag->ti_Data;

        if (args[3])                                 // SHOW/S
            visible = TRUE;

        if (args[4])                                 // HIDE/S
            visible = FALSE;

        if (args[5])                                 // TOGGLE/S
            visible = !visible;

        if (visible)
            ShowDocks(dock);
    }
    else
        Puts("Unknow dock");

    return(RC_OK);
}

///
/// "dock handling"

/* --------------------------------- ShowDocks ---------------------------------

 Show specified (hidden) dock or all hidden docks if <dock> is FALSE.

*/

void
ShowDocks(showDock)

struct Dock *showDock;
{
    struct Screen *scr;

    if (scr = LockPubScreen(Screen)) {

        struct Dock *dock;

        for (dock = (struct Dock *)DockList.lh_Head; dock->Node.ln_Succ; dock = (struct Dock *)dock->Node.ln_Succ) {

            if (!dock->Visible) {

                if ((dock == showDock) || (showDock == NULL)) {

                    struct TagItem *tag;

                    FindTagItem(TMOP_PubScreen, dock->Tags)->ti_Data = Screen;

                    if ((tag = FindTagItem(TMOP_LeftEdge, dock->Tags))->ti_Data == ~0)
                        tag->ti_Data = scr->Width;

                    if ((tag = FindTagItem(TMOP_TopEdge, dock->Tags))->ti_Data == ~0)
                        tag->ti_Data = scr->BarHeight + ((scr->BitMap.Depth == 1) ? 2 : 1);

                    if (CreateTMObjectTagList(TMHandle, dock->Node.ln_Name, TMOBJTYPE_DOCK, dock->Tags))
                        dock->Visible = TRUE;
                    else
                        Puts("Couldn't create dock object");
                }
            }
        }

        UnlockPubScreen(NULL, scr);
    }
}

/* --------------------------------- HideDocks ---------------------------------

 Hide all open docks

*/

void
HideDocks()
{
    struct Dock *dock;

    for (dock = (struct Dock *)DockList.lh_Head; dock->Node.ln_Succ; dock = (struct Dock *)dock->Node.ln_Succ) {

        if (dock->Visible) {

            DeleteTMObject(TMHandle, dock->Node.ln_Name);

            dock->Visible = FALSE;
        }
    }
}


///
/// "misc"


/* --------------------------------- StopServer --------------------------------

 Stop server task

*/

void
StopServer(server)

struct Task **server;
{
    if (*server) {

        SetSignal(0, SIG_HANDSHAKE);

        Signal(*server, SIG_KILL);

        Wait(SIG_HANDSHAKE);

        *server = NULL;
    }
}


/* -------------------------------- PutIEvents ---------------------------------

 Add keyboard events to global stream of input events. Works  recursively  to
 invert   the  given  linked  list  of  events  (which  is  inverted  due  to
 InvertString). Restores the links after usage  to  support  a  FreeIEvents()
 call later on.

*/

void
PutIEvents(inputEvent)

struct InputEvent *inputEvent;
{
    struct InputEvent *next;

    if (next = inputEvent->ie_NextEvent)
        PutIEvents(next);

    inputEvent->ie_NextEvent = NULL;

    AddIEvents(inputEvent);

    inputEvent->ie_NextEvent = next;
}

/* ---------------------------------- InsertEvent ------------------------------

 Insert string into input event loop. String is interpreted. Return FALSE on
 failure (interpretation error).

*/

void
InsertEvent(string)

UBYTE *string;
{
    struct InputEvent *inputEvent;

    if (inputEvent = MyInvertString(string, NULL)) {

        PutIEvents   (inputEvent);
        MyFreeIEvents(inputEvent);
    }
}


/* ---------------------------------- stristr ----------------------------------

 strstr replacement: case insensitive (recursive)

*/

UBYTE *
stristr(buffer, pattern)

UBYTE *buffer, *pattern;
{
    if (*pattern) {

        while (*buffer) {

            if (toupper(*buffer) == *pattern)
                if (stristr(buffer + 1, pattern + 1))
                    return(buffer);

            ++buffer;
        }

        return(NULL);
    }
    else
        return(buffer);
}

/* ---------------------------------- Replace ----------------------------------

 Replace given <pattern> within <buffer> by <new> (case insensitive)

*/

void
Replace(buffer, pattern, new)

UBYTE *buffer, *pattern, *new;
{
    UWORD newLen;
    
    for (newLen = strlen(new); buffer = stristr(buffer, pattern); buffer += newLen) {

        WORD diffLen = newLen - strlen(pattern);

        if (diffLen > 0)
            movmem(buffer, buffer + diffLen, strlen(buffer) + 1);

        if (diffLen < 0)
            strcpy(buffer, buffer - diffLen);

        movmem(new, buffer, newLen);
    }
}


/* -------------------------------- SearchName ---------------------------------

 Search for named node; case insensitive FindName() replacement

*/

struct Node *
SearchName(list, name)

struct List *list;
UBYTE       *name;
{
    struct Node *node;

    if (node = FindName(list, name))
        return(node);

    for (node = list->lh_Head; node->ln_Succ; node = node->ln_Succ)
        if (node->ln_Name && (stricmp(name, node->ln_Name) == 0))
            return(node);

    return(NULL);
}

/* ----------------------------------- Puts ------------------------------------

 puts() replacement; saves another 2K ;-)

*/

void
Puts(text)

UBYTE *text;
{
    Write(Output(), text, strlen(text));
    Write(Output(), "\n", 1);
}


/* ---------------------------------- CountList --------------------------------

 Count nodes of a list

*/

UWORD
CountList(list)

struct List *list;
{
    UWORD  count;
    struct Node *nextNode;

    for (count = 0, nextNode = list->lh_Head; nextNode->ln_Succ; nextNode = nextNode->ln_Succ)
        count++;

    return(count);
}


/* --------------------------------- xsprintf ----------------------------------

 sprintf frontend (malloc buffer); limited to string insertion into string

*/

UBYTE *
xsprintf(mask, var)

UBYTE *mask, *var;
{
    char *buffer;

    if (buffer = malloc(strlen(mask) + strlen(var)))
        myprintf(buffer, mask, var);

    return(buffer);
}

///
/// "ARexx"

/* ---------------------------------- SendRexxCommand -------------------------

 Send ARexx message & wait for answer. Don't wait for reply  in  asynchronous
 mode.  In  synchronous mode: return pointer to result or NULL. Result string
 is written to <buffer> if <buffer> is valid. In  asynchronous  mode:  return
 TRUE (message sent; increase <counter>) or NULL.

*/

ULONG *
SendRexxCommand(port, cmd, replyPort, async, counter, buffer)

UBYTE *cmd, *port, *buffer;
UWORD *counter;
BOOL   async;
struct MsgPort *replyPort;
{
    struct MsgPort *rexxport;

    Forbid();

    if (rexxport = FindPort(port)) {

        struct RexxMsg *rexxMsg;

        if (rexxMsg = CreateRexxMsg(replyPort, NULL, NULL)) {

            if (rexxMsg->rm_Args[0] = CreateArgstring(cmd, strlen(cmd))) {

                static ULONG result;

                struct RexxMsg *answer;

                rexxMsg->rm_Action = RXCOMM | RXFF_RESULT;

                PutMsg(rexxport, &rexxMsg->rm_Node);

                Permit();

                if (counter)
                    ++*counter;

                if (async)
                    return(TRUE);

                do {

                    WaitPort(replyPort);

                    if (answer = (struct RexxMsg *)GetMsg(replyPort)) {

                        if (answer->rm_Node.mn_Node.ln_Type == NT_REPLYMSG) {

                            if (counter)
                                --*counter;

                            if (answer == rexxMsg) {

                                if ((result = answer->rm_Result1) == RC_OK) {

                                    if (answer->rm_Result2 && buffer)
                                        strcpy(buffer, (char *)answer->rm_Result2);
                                }
                            }

                            FreeRexxCommand(answer);
                        }
                        else {

                            answer->rm_Result1 = RC_FATAL;

                            ReplyMsg((struct Message *)answer);
                        }
                    }

                } while (answer != rexxMsg);

                return(&result);
            }
        }
    }

    Permit();

    return(NULL);
}

/* ------------------------------- CloseRexxPort -------------------------------

 Close an ARexx  port.  Wait  until  all  outstanding  (i.e.  sent)  messages
 (<missing>)  have  been  replied.  Remove  port from list of public ports if
 <removePort> is TRUE.

*/

void
CloseRexxPort(port, missing, removePort)

struct MsgPort *port;
UWORD  missing;
BOOL   removePort;
{
    if (removePort)
        RemPort(port);

    Forbid();

    while (missing) {

        struct RexxMsg *rexxMsg;

        Wait(1L<<port->mp_SigBit);

        while (rexxMsg = (struct RexxMsg *)GetMsg(port)) {

            if (rexxMsg->rm_Node.mn_Node.ln_Type == NT_REPLYMSG) {

                FreeRexxCommand(rexxMsg);

                missing--;
            }
            else {

                rexxMsg->rm_Result1 = RC_FATAL;

                ReplyMsg((struct Message *)rexxMsg);
            }
        }
    }

    DeleteMsgPort(port);

    Permit();
}

/* ------------------------------- FreeRexxCommand ----------------------------

 Free memory/file handles related to message <rexxmessage> replied by the
 ARexx server.

*/

void
FreeRexxCommand(rexxMsg)

struct RexxMsg *rexxMsg;
{
    if (rexxMsg->rm_Result1 == RC_OK) 
        if (rexxMsg->rm_Result2)
            DeleteArgstring((char *)rexxMsg->rm_Result2);

    if (rexxMsg->rm_Stdin)
        Close(rexxMsg->rm_Stdin);

    if (rexxMsg->rm_Stdout && (rexxMsg->rm_Stdout != rexxMsg->rm_Stdin))
        Close(rexxMsg->rm_Stdout);

    DeleteArgstring((char *)ARG0(rexxMsg));

    DeleteRexxMsg(rexxMsg);
}

///
/// "execute programs"

/* -------------------------------- DoExecute ------------------------------------

 Execute a DOS command.

*/

void
DoExecute(cmd, async, output, directory, stack, prio)

UBYTE *cmd, *output, *directory;
UWORD stack;
WORD  prio;
BOOL  async;
{
    BPTR newCurrentDir;

    if (!directory)
        directory = "progdir:";

    if (!output)
        output = "NIL:";

    if (newCurrentDir = Lock(directory, SHARED_LOCK)) {

        BPTR inHandle, outHandle, oldCurrentDir = CurrentDir(newCurrentDir);

        if (outHandle = Open(output, MODE_NEWFILE)) {

            struct TagItem tagItems[] = {

                SYS_Output,      NULL,
                SYS_Input ,      NULL,
                NP_ConsoleTask,  NULL,
                SYS_UserShell,   TRUE,
                SYS_Asynch,      async,
                NP_StackSize,    stack,
                NP_Priority,     prio,
                TAG_DONE 
            };

            struct MsgPort *oldct, *newct;

            BOOL success = FALSE;

            if (IsInteractive(outHandle)) {

                newct = ((struct FileHandle *)BADDR(outHandle))->fh_Type;
                oldct = SetConsoleTask(newct);

                inHandle = Open("CONSOLE:", MODE_OLDFILE);

                SetConsoleTask(oldct);
            }
            else {

                inHandle = Open("NIL:", MODE_OLDFILE);
                newct    = NULL;
            }

            if (inHandle) {

                tagItems[0].ti_Data = outHandle;
                tagItems[1].ti_Data = inHandle;
                tagItems[2].ti_Data = newct;

                if (SystemTagList(cmd, tagItems) != -1L)
                    success = TRUE;

                if (!(async && success))
                    Close(inHandle);
            }

            if (!(async && success))
                Close(outHandle);
        }

        CurrentDir(oldCurrentDir);

        UnLock(newCurrentDir);
    }
}

///
/// "Server"

/* -------------------------------- ARexxServer --------------------------------

 ARexx server; used to process  ARexx  commands  sent  by  ToolManager.  This
 function  is  installed  as  background  task.  Run until a SIG_HANDSHAKE is
 recieved from the  main  task.  Rise  SIG_HANDSHAKE  on  exit  (multitasking
 disabled,  i.e.  if  the  main  task  detects  the signal, this task is gone
 already).

*/

__geta4 void
ARexxServer()
{
    struct MsgPort *port;

    Forbid();

    if (port = CreateMsgPort()) {

        ULONG mask;
        UWORD missing = 0;

        port->mp_Node.ln_Name = "DOCKSERVER";

        AddPort(port);

        do {

            struct RexxMsg *rexxMsg;

            mask = Wait(1L<<(port->mp_SigBit) | SIG_KILL);

            while (rexxMsg = (struct RexxMsg *)GetMsg(port)) {

                if (rexxMsg->rm_Node.mn_Node.ln_Type == NT_REPLYMSG) {

                    FreeRexxCommand(rexxMsg);

                    if (missing)
                        missing--;
                }
                else {

                    struct Event *event;

                    if (event = (struct Event *)SearchName(&EventList, ARG0(rexxMsg))) {

                        if (event->Command) {

                            static UBYTE buffer[1024];

                            strcpy(buffer, event->Command);

                            Replace(buffer, "%SCREEN", Screen);
                            Replace(buffer, "%GOLDED", Host  );

                            if (event->Activate)
                                SendRexxCommand(Host, "WINDOW FORCE", port, TRUE, &missing, NULL);

                            switch (event->Type) {

                                case TMET_ARexx:

                                    SendRexxCommand("AREXX", buffer, port, TRUE, &missing, NULL);
                                    break;

                                case TMET_GoldED:

                                    SendRexxCommand(Host, buffer, port, TRUE, &missing, NULL);
                                    break;

                                case TMET_CLI:
                                case TMET_WB:

                                    DoExecute(buffer, TRUE, event->Output, event->Dir, 8192, 0);
                                    break;

                                case TMET_Hotkey:

                                    InsertEvent(buffer);
                                    break;
                            }
                        }
                    }

                    rexxMsg->rm_Result1 = NULL;
                    rexxMsg->rm_Result2 = NULL;

                    ReplyMsg((struct Message *)rexxMsg);
                }
            }

        } while (!(mask & SIG_KILL));

        CloseRexxPort(port, missing, TRUE);
    }
    else
        Wait(SIG_KILL);

    Forbid();

    Signal(MainTask, SIG_HANDSHAKE);
}

///
