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

  Quick starter, ©1998 Dietmar Eilert.

  This is the C source code of the quickstarter. It demonstrates how to address
  the editor from other applications.

  DICE (compile as resident/pure program):

  dcc ed.c rawdofmt.asm -// -proto -mRR -mi -pr -3.0 -o ed

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

/// "compiler"

#ifdef __SASC

#define __USE_SYSBASE
#define __geta4      __saveds
#define __stkargs    __stdargs

#define __A0         register __a0
#define __A1         register __a1
#define __A2         register __a2
#define __A3         register __a3
#define __A4         register __a4
#define __A5         register __a5
#define __A6         register __a6
#define __A7         register __a7
#define __D0         register __d0
#define __D1         register __d1
#define __D2         register __d2
#define __D3         register __d3
#define __D4         register __d4
#define __D5         register __d5
#define __D6         register __d6
#define __D7         register __d7

#endif

#ifdef _DCC

#define __asm

#endif

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

// prototypes

#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/rexxsyslib_protos.h>
#include <clib/utility_protos.h>
#include <clib/wb_protos.h>

// pragmas

#ifdef __SASC

#include <pragmas/dos_pragmas.h>
#include <pragmas/exec_sysbase_pragmas.h>
#include <pragmas/icon_pragmas.h>
#include <pragmas/intuition_pragmas.h>
#include <pragmas/rexxsyslib_pragmas.h>
#include <pragmas/utility_pragmas.h>
#include <pragmas/wb_pragmas.h>

#endif

///
/// "defines"

#define Prototype        extern
#define MAX_PATHLEN      512
#define MAX_CMDLEN       256
#define ARGBUFFER_SIZE   8192
#define ARGBUFFER_LIMIT  8000

///
/// "globals"

extern struct Library *IconBase;
extern struct Library *UtilityBase;
extern struct Library *RexxSysBase;
extern struct Library *DOSBase;
extern struct Library *SysBase;

///
/// "prototypes"

Prototype int              Action         (UBYTE *, UBYTE *, BOOL, BOOL, ULONG *, UBYTE *, UBYTE *);
Prototype UBYTE           *LookForEditor  (UBYTE *);
Prototype int              main           (ULONG, UBYTE **);
Prototype LONG            *SendRexxCommand(UBYTE *, UBYTE *, struct MsgPort *, LONG *);
Prototype UBYTE           *StartEditor    (UBYTE *, BOOL, UBYTE *, UBYTE *, UBYTE *);
Prototype int              wbmain         (struct WBStartup *);
Prototype __stkargs UBYTE *mysprintf      (UBYTE *, UBYTE *, ...);
Prototype __stkargs UBYTE *myrawdofmt     (UBYTE *, UBYTE *, APTR);

///
/// "globals"

#ifdef __SASC

UBYTE Version[] = "$VER: ed 3.8 " __AMIGADATE__ "\n\0";

#endif

#ifdef _DCC

UBYTE Version[] = "$VER: ed 3.8 (" __COMMODORE_DATE__ ")\n\0";

#endif

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

*/

int
main(argc, argv)

ULONG argc;
UBYTE *argv[];
{
    int error;

    if (argc == 0) {

        error = wbmain((struct WBStartup *)argv);
    }
    else {

        UBYTE *argBuffer;

        error = 0;

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

            struct RDArgs *rdArgs;

            LONG 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 *path;

                    if (path = (UBYTE *)AllocVec(MAX_PATHLEN, MEMF_PUBLIC | MEMF_CLEAR)) {

                        UBYTE **nextFile;
                        BPTR    lock;

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

                            strcpy(path, *nextFile);

                            // expand file name

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

                                NameFromLock(lock, path, MAX_PATHLEN);

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

                                GetCurrentDirName(path, MAX_PATHLEN);

                                AddPart(path, *nextFile, MAX_PATHLEN);
                            }

                            strcat(argBuffer, "\42");

                            strcat(argBuffer, path);

                            strcat(argBuffer, "\42 ");

                            // too many files ?

                            if (strlen(argBuffer) > ARGBUFFER_LIMIT) {

                                error = 5;

                                break;
                            }
                        }

                        FreeVec(path);
                    }
                }

                if (error == 0)

                    error = 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);
        }
        else
            error = 20;
    }

    return(error);
}


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

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

*/

int
wbmain(wbs)

struct WBStartup *wbs;
{
    int    error;
    UBYTE *argBuffer;

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

        struct DiskObject *diskObject = NULL;

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

        error = 0;

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

        hide = FALSE;

        if (--wbs->sm_NumArgs) {

            UBYTE *path;

            if (path = (UBYTE *)AllocVec(MAX_PATHLEN, MEMF_PUBLIC | MEMF_CLEAR)) {

                struct WBArg *wbArg = wbs->sm_ArgList;

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

                    ++wbArg;

                    NameFromLock(wbArg->wa_Lock, path, MAX_PATHLEN);

                    AddPart(path, wbArg->wa_Name, MAX_PATHLEN);

                    // options not yet read ?

                    if (diskObject == NULL) {

                        if (diskObject = GetDiskObject(path)) {

                            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, path);

                    strcat(argBuffer, "\42 ");

                    // too many files ?

                    if (strlen(argBuffer) > ARGBUFFER_LIMIT) {

                        error = 5;

                        break;
                    }
                }

                FreeVec(path);
            }
            else
                error = 20;
        }

        if (error == 0)

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

        if (diskObject)

            FreeDiskObject(diskObject);

        FreeVec(argBuffer);
    }
    else
        error = 20;

    return(error);
}

///
/// "main routine"

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

 Run editor if no running instance is found (note: running the editor will
 open a first window, i.e. no need to open a further one unless files are
 specified). Send LOCK message to running editor. 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 the editor as startup options
 if STICKY is not specified and if there is no running editor instance.

*/

int
Action(files, filetype, sticky, hide, line, arexx, session)

UBYTE  *files, *filetype, *arexx, *session;
ULONG  *line;
BOOL    hide, sticky;
{
    int    error;
    UBYTE *buffer;

    error = 0;

    if (buffer = (UBYTE *)AllocVec(MAX_PATHLEN, MEMF_PUBLIC | MEMF_CLEAR)) {

        UBYTE *host;
        LONG   result;

        if (host = LookForEditor(arexx)) {               // editor found ?

             struct MsgPort *replyPort;

             if (replyPort = CreateMsgPort()) {

                if (session) {

                    mysprintf(buffer, "SESSION LOAD CONFIG=\42%s\42", session);

                    SendRexxCommand(host, buffer, replyPort, &result);
                }
                else if (*files) {

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

                        if (result == RC_OK) {

                            if (*files) {

                                strins(files, "NAME ");

                                if (filetype) {

                                    mysprintf(buffer, "FILETYPE=\42%s\42 ", filetype);

                                    strins(files, buffer);
                                }

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

                            SendRexxCommand(host, files, replyPort, &result);

                            if (line) {

                                mysprintf(buffer, "GOTO LINE=%ld UNFOLD=TRUE", *(ULONG *)line);

                                SendRexxCommand(host, buffer, replyPort, &result);
                            }

                            SendRexxCommand(host, sticky ? "UNLOCK STICKY" : "UNLOCK", replyPort, &result);
                        }
                    }
                    else
                        error = 20;
                }
                else {

                    // editor might be iconified - send wake-up-command (any command will work)

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

                DeleteMsgPort(replyPort);
            }
            else
                error = 20;
        }
        else if (host = StartEditor(arexx, hide, filetype, files, session)) {

            if (line) {

                struct MsgPort *replyPort;

                if (replyPort = CreateMsgPort()) {

                    mysprintf(buffer, "GOTO LINE=%ld UNFOLD=TRUE", *(ULONG *)line);

                    SendRexxCommand(host, buffer, replyPort, &result);

                    DeleteMsgPort(replyPort);
                }
                else
                    error = 20;
            }

            if (sticky) {

                struct MsgPort *replyPort;

                if (replyPort = CreateMsgPort()) {

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

                        SendRexxCommand(host, "UNLOCK STICKY", replyPort, &result);
                    }
                    else
                        error = 20;

                    DeleteMsgPort(replyPort);
                }
                else
                    error = 20;
            }
        }
        else
            error = 20;

        FreeVec(buffer);
    }
    else
        error = 20;

    return(error);
}

///
/// "misc"

/* ----------------------------------- mysprintf -------------------------------

 sprintf replacement

*/

__stkargs UBYTE *
mysprintf(UBYTE *buffer, UBYTE *template, ...)
{
    myrawdofmt(buffer, template, (APTR)((ULONG)&template + sizeof(ULONG)));

    return(buffer);
}


/* -------------------------------- LookForEditor ------------------------------

 Look for running editor task.

*/

UBYTE *
LookForEditor(host)

UBYTE *host;
{
    UBYTE *name = NULL;

    Forbid();

    if (host && FindPort(host))

        name = host;

    else {

        const UBYTE *try[] = { "GOLDED.1", "GOLDED.2", "GOLDED.3", "GOLDED.4", "GOLDED.5", NULL };

        UBYTE **next;

        for (next = (UBYTE **)try; *next; ++next) {

            if (FindPort(*next)) {

                name = *next;

                break;
            }
        }
    }

    Permit();

    return(name);
}


/* ----------------------------------- StartEditor -----------------------------

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

*/

UBYTE *
StartEditor(rexxname, hide, filetype, files, session)

UBYTE *rexxname;
UBYTE *filetype;
UBYTE *files;
UBYTE *session;
BOOL   hide;
{
    UBYTE *buffer;
    UBYTE *host;

    host = NULL;

    if (buffer = (UBYTE *)AllocVec(MAX_PATHLEN, MEMF_PUBLIC | MEMF_CLEAR)) {

        UBYTE *command;

        if (command = (UBYTE *)AllocVec(MAX_CMDLEN, MEMF_PUBLIC | MEMF_CLEAR)) {

            struct MsgPort *port;
            struct Task    *task;
            UBYTE          *cmd;
            LONG            stack;

            task = FindTask(NULL);

            // determine current stack size (used as default for running editor)

            stack = (ULONG)task->tc_SPUpper - (ULONG)task->tc_SPLower;

            if (stack < 8192)                        // minimum 8 KB

                stack = 8192;
                                                     // maximum 128 KB
            if (stack > 131072)

                stack = 131072;

            if (session) {

                mysprintf(command, "SESSION=\42%s\42", session);

                cmd = command;
            }
            else if (files) {

                cmd = files;
            }
            else {

                cmd = command;
            }

            if (filetype) {

                mysprintf(buffer, "FILETYPE=\42%s\42 ", filetype);

                strins(cmd, buffer);
            }

            if (rexxname) {

                host = rexxname;

                mysprintf(buffer, "AREXX=\42%s\42 ", rexxname);

                strins(cmd, buffer);
            }
            else
                host = "GOLDED.1";

            if (hide)

                strins(cmd, "HIDE ");

            strins(cmd, "golded:demo ");

            port = NULL;

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

                UWORD           try;

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

                    Forbid();

                    port = FindPort(host);

                    Permit();

                    if (port)

                        break;
                }
            }

            if (port == NULL)

                host = NULL;

            FreeVec(command);
        }

        FreeVec(buffer);
    }

    return(host);
}

///
/// "rexx"

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

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

*/

LONG *
SendRexxCommand(port, cmd, replyPort, result)

struct MsgPort *replyPort;
UBYTE          *cmd;
UBYTE          *port;
LONG           *result;
{
    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))) {

                rexxMsg->rm_Action = RXCOMM | RXFF_RESULT;

                PutMsg(rexxport, &rexxMsg->rm_Node);

                *result = 0;

                do {
                    
                    WaitPort(replyPort);

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

                        *result = answer->rm_Result1;

                } while (answer == NULL);

                Permit();

                if (answer->rm_Result1 == RC_OK) 

                    if (answer->rm_Result2)

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

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

                DeleteRexxMsg(answer);

                return(result);
            }
            else
                DeleteRexxMsg(rexxMsg);
        }
    }

    Permit();

    return(NULL);
}

///
