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

  ED 3.1 - GoldED quick starter, ©1996 Dietmar Eilert.

  This is C source code of ED to give you an idea of how to address GoldED
  from other applications. Feel free to change this code. Dice:

  dcc ed.c sprintf.a -// -proto -mRR -mi -pr -2.0 -o ram:ED

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

/// "includes"

#include <amiga20/exec/exec.h>
#include <string.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <amiga20/intuition/intuition.h>
#include <amiga20/dos/dos.h>
#include <amiga20/dos/dosextens.h>
#include <amiga20/dos/rdargs.h>
#include <amiga20/dos/dostags.h>
#include <amiga20/workbench/startup.h>
#include <amiga20/workbench/workbench.h>
#include <amiga20/rexx/errors.h>
#include <amiga20/rexx/rxslib.h>

#include <amiga20/clib/alib_protos.h>
#include <amiga20/clib/dos_protos.h>
#include <amiga20/clib/exec_protos.h>
#include <amiga20/clib/icon_protos.h>
#include <amiga20/clib/intuition_protos.h>
#include <amiga20/clib/utility_protos.h>
#include <amiga20/clib/rexxsyslib_protos.h>
#include <amiga20/clib/wb_protos.h>

#define PRAGMAS

#ifdef PRAGMAS

#include "Pragmas/exec.h"
#include "Pragmas/disk.h"
#include "Pragmas/diskfont.h"
#include "Pragmas/dynamic.h"
#include "Pragmas/gadtools.h"
#include "Pragmas/keymap.h"
#include "Pragmas/graphics.h"
#include "Pragmas/icon.h"
#include "Pragmas/input.h"
#include "Pragmas/intuition.h"
#include "Pragmas/layers.h"
#include "Pragmas/locale.h"
#include "Pragmas/misc.h"
#include "Pragmas/timer.h"
#include "Pragmas/wb.h"
#include "Pragmas/xpkmaster.h"
#include "Pragmas/amigaguide.h"
#include "Pragmas/reqtools.h"

#endif

#define Prototype        extern
#define MAX_LEN          120
#define ARGBUFFER_SIZE   10255
#define ARGBUFFER_LIMIT  10000

///
/// "prototypes"

Prototype void   main(ULONG, UBYTE **);
Prototype int    wbmain(struct WBStartup *);
Prototype void   Action(UBYTE *, UBYTE *, BOOL, BOOL, ULONG *, UBYTE *);
Prototype UBYTE *StartGED(UBYTE *, BOOL, UBYTE *, UBYTE *);
Prototype ULONG *SendRexxCommand(UBYTE *, UBYTE *, struct MsgPort *);
Prototype UBYTE *LookForGED(UBYTE *);
Prototype UBYTE *myprintf(UBYTE *, UBYTE*, ...);
Prototype UBYTE *xsprintf(UBYTE *, APTR);
Prototype BOOL   FindAssign(UBYTE *);

extern struct Library *IconBase;
extern struct Library *DOSBase;
extern struct Library *SysBase;
extern struct Library *IntuitionBase;

// globals

UBYTE Version[] = "$VER: ED 3.1 (" __COMMODORE_DATE__ ")";

///
/// "entry points"

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

 CLI entry point. Parse command line - create a string <argBuffer> containing
 provided  file  names  (file names are made absolute). This string has to be
 FreeVec()'ed later on. Additionally, command line options are checked.

*/

void
main(argc, argv)

ULONG argc;
UBYTE *argv[];
{
    UBYTE *argBuffer;

    if (argBuffer = AllocVec(ARGBUFFER_SIZE, MEMF_PUBLIC | MEMF_CLEAR)) {

        struct RDArgs *rdArgs;

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

        if (rdArgs = ReadArgs("FILETYPE/K,Y=STICKY/S,F=FILE/M,HIDE/S,-STICKY/S,L=LINE/N,A=AREXX/K", args, NULL)) {

            if (args[2]) {                           // FILE/M

                UBYTE **nextFile, path[MAX_LEN + 1];
                BPTR    lock;

                for (nextFile = (UBYTE **)args[2]; *nextFile; ++nextFile) {

                    strcpy(path, *nextFile);

                    if (lock = Lock(path, ACCESS_READ)) {

                        NameFromLock(lock, path, MAX_LEN);
                        UnLock(lock);
                    }
                    else if (strchr(path, ':') == NULL) {

                        GetCurrentDirName(path, MAX_LEN);

                        AddPart(path, *nextFile, MAX_LEN);
                    }

                    strcat(argBuffer, xsprintf("\42%s\42", path));

                    if (strlen(argBuffer) > ARGBUFFER_LIMIT)
                        break;
                }
            }

            Action(argBuffer, (UBYTE *)args[0], args[1] || args[4], (BOOL)args[3], (ULONG *)args[5], (UBYTE *)args[6]);

            FreeArgs(rdArgs);
        }
        else
            exit(20);
    }
    exit(0);
}


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

 Workbench entry point. Read tooltypes of ED icon to decide wether user prefers
 a special configuration/public screen.

*/

int
wbmain(struct WBStartup *wbs)
{
    UBYTE *argBuffer;

    if (argBuffer = AllocVec(ARGBUFFER_SIZE, MEMF_PUBLIC | MEMF_CLEAR)) {

        UBYTE progName[MAX_LEN + 1];

        struct DiskObject *diskObject;
        UBYTE             *filetype;
        UBYTE             *arexx;
        BOOL               hide;

        filetype = NULL;
        arexx    = NULL;
        hide     = FALSE;

        NameFromLock(GetProgramDir(), progName, MAX_LEN);

        AddPart(progName, wbs->sm_ArgList[0].wa_Name, MAX_LEN);

        if (diskObject = GetDiskObject(progName)) {

            filetype = FindToolType(diskObject->do_ToolTypes, "FILETYPE");
            arexx    = FindToolType(diskObject->do_ToolTypes, "AREXX"   );

            if (FindToolType(diskObject->do_ToolTypes, "HIDE"))
                hide = TRUE;
        }

        if (--wbs->sm_NumArgs) {

            UBYTE file[MAX_LEN + 1];

            struct WBArg *wbArg = wbs->sm_ArgList;

            while ((wbs->sm_NumArgs)--) {

                ++wbArg;

                NameFromLock( wbArg->wa_Lock, file, MAX_LEN);
                AddPart(file, wbArg->wa_Name, MAX_LEN);

                strcat(argBuffer, xsprintf("\42%s\42", file));

                if (strlen(argBuffer) > ARGBUFFER_LIMIT)
                    break;
            }
        }

        Action(argBuffer, filetype, FALSE, hide, NULL, arexx);

        if (diskObject)
            FreeDiskObject(diskObject);
    }

    exit(0);
}

///
/// "main routine"

/* ------------------------------------ Action ---------------------------------

 Run GoldED if no running instance of GED is found (note:  running  GED  will
 open  a  first  window,  i.e. no need to open a further one unless files are
 specified). Send LOCK message to running GoldED. Wait  for  positive  reply,
 pass  our  list of <files> to that editor, unlock editor (use delayed unlock
 if <sticky> is specified). List of files (<files>) is FreeVec'ed. Files are
 passed to GoldED as startup options if STICKY is not specifed and if there is
 no running GoldED instance.

*/

void
Action(files, filetype, sticky, hide, line, arexx)

UBYTE *files, *filetype, *arexx;
ULONG *line;
BOOL   sticky, hide;
{
    UBYTE *host;

    Forbid();

    host = LookForGED(arexx);                        // running GoldED found ?

    Permit();

    if (host) {

         struct MsgPort *replyPort;

         if (replyPort = CreateMsgPort()) {

            if (*files) {

                ULONG *result;

                if (result = SendRexxCommand(host, "LOCK CURRENT RELEASE=4", replyPort)) {

                    if (*result == RC_OK) {

                        if (*files) {

                            if (filetype)
                                strins(files, xsprintf("FILETYPE=\42%s\42 ", filetype));

                            strins(files, "OPEN SMART QUIET ");
                        }
                        else
                            strcpy(files, "MORE SMART");

                        SendRexxCommand(host, files, replyPort);

                        if (line)
                            SendRexxCommand(host, xsprintf("GOTO LINE=%ld UNFOLD=TRUE", (APTR)*line), replyPort);

                        SendRexxCommand(host, sticky ? "UNLOCK STICKY" : "UNLOCK", replyPort);
                    }
                }

            }
            else {

                // GoldED might be iconified - send wake-up-command (any command will do)

                SendRexxCommand(host, "SCREEN FRONT", replyPort);
            }
        }

        DeleteMsgPort(replyPort);
    }
    else if (host = StartGED(arexx, hide, filetype, files)) {

        if (line) {

            struct MsgPort *replyPort;

            if (replyPort = CreateMsgPort()) {

                SendRexxCommand(host, xsprintf("GOTO LINE=%ld UNFOLD=TRUE", (APTR)*line), replyPort);

                DeleteMsgPort(replyPort);
            }
        }

        if (sticky) {

            struct MsgPort *replyPort;

            if (replyPort = CreateMsgPort()) {

                if (SendRexxCommand(host, "LOCK CURRENT RELEASE=4", replyPort))

                    SendRexxCommand(host, sticky ? "UNLOCK STICKY" : "UNLOCK", replyPort);

                DeleteMsgPort(replyPort);
            }
        }
    }

    FreeVec(files);
}

///
/// "misc"

/* -------------------------------- FindAssign ---------------------------------

 Check whether assign exists without annoying 'insert drive' requester

*/

BOOL
FindAssign(assign)

UBYTE *assign;
{
    BOOL success = (FindDosEntry(LockDosList(LDF_ASSIGNS | LDF_READ), assign, LDF_ASSIGNS) != NULL);

    UnLockDosList(LDF_ASSIGNS | LDF_READ);

    return(success);
}


/* ----------------------------------- LookForGED ----------------------------

 Look for running GoldED task (check <host> and GOLDED.1 to GOLDED.9)

*/

UBYTE *
LookForGED(host)

UBYTE *host;
{
    if (host && FindPort(host))

        return(host);

    else {

        static UBYTE name[] = "GOLDED.1";

        while (name[7] <= '9') {

            if (FindPort(name))
                return(name);
            else
                ++name[7];
        } 

        return(NULL);
    }
}

/* ------------------------------------- StartGED -----------------------------

 Launch a new GoldED task. Look for "GOLDED:" assign. Add assign if  none  is
 found (defaultPath[] is set by the installer script). Return pointer to host
 name (or NULL). Screen/config keywords are considered.

*/

UBYTE *
StartGED(arexx, hide, filetype, files)

UBYTE *arexx;
BOOL   hide;
UBYTE *filetype;
UBYTE *files;
{
    static UBYTE host[255], defaultPath[255] = "$GOLDED";

    UBYTE command[MAX_LEN + 1];

    if (FindAssign("GOLDED") == FALSE)

        AssignLock("GOLDED", Lock(defaultPath, ACCESS_READ));

    if (arexx)
        strcpy(host, arexx);
    else
        strcpy(host, "GOLDED.1");

    strcpy(command, xsprintf("GOLDED:GOLDED AREXX=%s", host));

    if (hide)
        strcat(command, " HIDE");

    if (filetype)
        strcat(command, xsprintf(" FILETYPE=\42%s\42", filetype));

    if (files) {

        strcat(command, " ");

        strcat(command, files);
    }

    if (SystemTags(command, SYS_Asynch, TRUE, SYS_Input, NULL, SYS_Output, NULL, NP_StackSize, 8192, TAG_DONE) == 0) {

        UWORD try;

        for (try = 50; try; try--, Delay(10))

            if (FindPort(host))

                return(host);
    }

    return(NULL);
}


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

 sprintf frontend (returns pointer to static buffer)

*/

UBYTE *
xsprintf(template, data)

UBYTE *template;
APTR  data;
{
    static UBYTE buffer[MAX_LEN + 1];

    return(myprintf(buffer, template, data));
}


///
/// "ARexx"

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

 Send ARexx message & wait for answer. Return pointer to result or NULL.

*/

ULONG *
SendRexxCommand(port, cmd, replyPort)

struct MsgPort *replyPort;
UBYTE          *cmd, *port;
{
    struct MsgPort *rexxport;

    Forbid();

    if (rexxport = FindPort(port)) {

        struct RexxMsg *rexxMsg, *answer;

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

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

                static ULONG result;

                rexxMsg->rm_Action = RXCOMM | RXFF_RESULT;

                PutMsg(rexxport, &rexxMsg->rm_Node);

                do {
                    
                    WaitPort(replyPort);

                    if (answer = (struct RexxMsg *)GetMsg(replyPort))
                        result = answer->rm_Result1;

                } while (!answer);

                Permit();

                if (answer->rm_Result1 == RC_OK) 

                    if (answer->rm_Result2)

                        DeleteArgstring((UBYTE *)answer->rm_Result2);

                DeleteArgstring((UBYTE *)ARG0(answer));

                DeleteRexxMsg(answer);

                return(&result);
            }
        }
    }

    Permit();

    return(NULL);
}

///
