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

  ED 3.4 - 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 <exec/exec.h>
#include <string.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <intuition/intuition.h>
#include <dos/dos.h>
#include <dos/dosextens.h>
#include <dos/rdargs.h>
#include <dos/dostags.h>
#include <workbench/startup.h>
#include <workbench/workbench.h>
#include <rexx/errors.h>
#include <rexx/rxslib.h>

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

#define Prototype        extern
#define MAX_LEN          120
#define MAX_LENPLUSONE   121
#define ARGBUFFER_SIZE   8192
#define ARGBUFFER_LIMIT  8000

///
/// "prototypes"

Prototype void    main(ULONG, UBYTE **);
Prototype int     wbmain(struct WBStartup *);
Prototype void    Action(UBYTE *, UBYTE *, BOOL, BOOL, ULONG *, UBYTE *, UBYTE *);
Prototype UBYTE  *StartGED(UBYTE *, BOOL, UBYTE *, UBYTE *, UBYTE *);
Prototype ULONG  *SendRexxCommand(UBYTE *, UBYTE *, struct MsgPort *);
Prototype UBYTE  *LookForGED(UBYTE *);
Prototype UBYTE  *myprintf(UBYTE *, UBYTE *, ...);
Prototype UBYTE  *xstrcpy (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.4 (" __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;
    UWORD  error;

    error = 0;

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

        struct RDArgs *rdArgs;

        ULONG args[] = { 0, 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,SESSION/K", args, NULL)) {

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

                UBYTE **nextFile, path[MAX_LENPLUSONE];
                BPTR    lock;

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

                    strcpy(path, *nextFile);

                    // expand file name

                    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, "\42");

                    strcat(argBuffer, path);

                    strcat(argBuffer, "\42 ");

                    // too many files ?

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

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

            FreeArgs(rdArgs);
        }
        else
            error = 20;

        FreeVec(argBuffer);
    }

    exit(error);
}


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

 Workbench entry point. Read tooltypes of text icon(s) to decide wether user
 prefers a special configuration.

*/

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

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

        struct DiskObject *diskObject = NULL;

        UBYTE  *filetype;
        UBYTE  *arexx;
        UBYTE  *session;
        BOOL    hide;

        filetype = NULL;
        arexx    = NULL;
        session  = NULL;

        hide = FALSE;

        if (--wbs->sm_NumArgs) {

            struct WBArg *wbArg = wbs->sm_ArgList;

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

                UBYTE file[MAX_LENPLUSONE];

                ++wbArg;

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

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

                // options not yet read ?

                if (diskObject == NULL) {

                    if (diskObject = GetDiskObject(file)) {

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

                        if (FindToolType(diskObject->do_ToolTypes, "HIDE"))

                            hide = TRUE;
                    }
                }

                strcat(argBuffer, "\42");

                strcat(argBuffer, file);

                strcat(argBuffer, "\42 ");

                // too many files ?

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

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

        if (diskObject)

            FreeDiskObject(diskObject);

        FreeVec(argBuffer);
    }

    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). 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, session)

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

    if (host = LookForGED(arexx)) {                  // GoldED found ?

         struct MsgPort *replyPort;

         if (replyPort = CreateMsgPort()) {

            if (session)

                SendRexxCommand(host, xsprintf("SESSION LOAD CONFIG=\42%s\42", session), replyPort);

            else 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, session)) {

        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);
            }
        }
    }
}

///
/// "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.

*/

UBYTE *
LookForGED(host)

UBYTE *host;
{
    UBYTE *name;

    Forbid();

    if (host && FindPort(host))

        name = host;

    else {

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

        for (name = NULL; try[7] != '9'; ++try[7]) {

            if (FindPort(try)) {

                name = try;

                break;
            }
        }
    }

    Permit();

    return(name);
}


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

 Launch a new GoldED task. Look for "GOLDED:" assign. Create assign if none is
 found (assign[] is set by the GoldED installer script). Return pointer to host
 name (or NULL).

*/

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

UBYTE *arexx;
UBYTE *filetype;
UBYTE *files;
UBYTE *session;
BOOL   hide;
{
    UBYTE assign [MAX_LENPLUSONE] = "$GOLDED";
    UBYTE command[MAX_LENPLUSONE];

    struct MsgPort *port;
    UBYTE          *host;
    UBYTE          *cmd;

    port = NULL;

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

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

    cmd = (files) ? files : command;

    if (session)

        strcpy(cmd, xsprintf("SESSION=\42%s\42", session));

    if (filetype)

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

    if (host = arexx)

        strins(cmd, xsprintf("AREXX=\42%s\42 ", arexx));

    if (host == NULL)

        host = "GOLDED.1";

    if (hide)

        strins(cmd, "HIDE ");

    strins(cmd, "GOLDED:GOLDED ");

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

        UWORD try;

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

            Forbid();

            port = FindPort(host);

            Permit();

            if (port)
                break;
        }
    }

    if (port)
        return(host);
    else
        return(NULL);
}


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

 sprintf frontend (returns pointer to static buffer)

*/

UBYTE *
xsprintf(template, data)

UBYTE *template;
APTR  data;
{
    static UBYTE buffer[MAX_LENPLUSONE];

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


/* ---------------------------------- xstrcpy ----------------------------------

 strcpy() frontend (handles NULL argument)

*/

UBYTE *
xstrcpy(dest, src)

UBYTE *dest, *src;
{
    if (src)
        strcpy(dest, src);
    else
        *dest = 0;

    return(dest);
}

///
/// "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);
}

///
