/* KMACC.C

  Copyright (C) Psion PLC 1994
  Started by:  DavidW
  Started on:  03/07/93
*/

#include <p_std.h>
#include <p_gen.h>
#include <p_file.h>
#include <epoc.h>
#include <wlib.h>
#include <lpc.g>
#include <appman.g>
#include <kmac.g>
#include <kmac.rsg>
#include "kmpanic.h"

#ifndef W_KEY_DIAMOND
#define W_KEY_DIAMOND 0x124
#endif

#define KMAC_RECORD_KEY     0       /* on Sibo, Control-Space = 0 */
#define KMAC_RECORD_KEY_2   32      /* for PC */
#define KMAC_PLAYBACK_KEY   W_KEY_DIAMOND
#define KMAC_ACCL_KEY       W_KEY_MENU
#define KMAC_HELP_KEY       W_KEY_HELP
#define KMAC_INTERRUPT_KEY  W_KEY_ESCAPE

#define SHIFT_CTRL  (W_SHIFT_MODIFIER|W_CTRL_MODIFIER)
#define ALL_MODS    (SHIFT_CTRL|W_PSION_MODIFIER)

GLREF_D VOID *DatCommandPtr;
GLREF_D TEXT *DatProcessNamePtr;
GLREF_D TEXT *DatUsedPathNamePtr;
GLREF_D TEXT *DatStatusNamePtr;
GLREF_D WORD DatLocked;

GLREF_D VOID *DatApp1;  /* for lpcon */
GLREF_D VOID *DatApp2;  /* for comman */
GLREF_D VOID *DatApp3;  /* for macros */
GLREF_D VOID *DatApp4;  /* for fcb */
GLREF_D VOID *DatApp5;  /* for mactive */
GLREF_D VOID *DatApp6;  /* for pbs */
GLREF_D HANDLE DatApp7; /* for DYL to p_unloadlib */

LOCAL_D WORD commandByte;
LOCAL_D TEXT *pUsedFileName;    /* only alloced if needed */

LOCAL_D HANDLE lpc;
LOCAL_D VOID *lpcon;
LOCAL_D VOID *comman;
LOCAL_D VOID *rscfile;

LOCAL_D TEXT fileBuf[100];

LOCAL_D PR_VAXVAR *macros;
LOCAL_D WORD macrosChanged;

LOCAL_D UBYTE capt[26];     /* which alphabetic keys are captured */

LOCAL_D VOID *fcb;

#define KMAC_FILE_SIG       "KMAC 1.00"
#define KMAC_FILE_SIG_LEN   9

LOCAL_D WORD mstat;     /* messaging status word */
LOCAL_D UWORD mactive;  /* messaging active */
LOCAL_D WS_EV wevent;   /* window server event */
LOCAL_D UWORD wactive;  /* window server read active */

LOCAL_D WORD inDialog;
LOCAL_D UWORD revise;

#define MAC_FLG_BACK  0x01  /* do NOT start by attaching to foreground */
#define MAC_FLG_QUIET 0x02  /* no "Play back successful" at end of playback */

#define MACRO_NAME_LEN   24         /* includes zero terminator */

typedef struct
    {
    TEXT name[MACRO_NAME_LEN];
    UBYTE accl;
    UBYTE flags;
    UWORD nsteps;
    } MACRO_HEADER;

#define MACRO_SEG_TYPE_KEYS         0
#define MACRO_SEG_TYPE_COMMANDS     1
    
typedef struct
    {
    UBYTE type;
    UWORD nsteps;
    } MACRO_SEGMENT_HEADER;
    
typedef struct
    {
    UWORD key;
    UBYTE mods;
    } SHORT_KEY;

#define MAX_NUM_KEYS    127      /* maximum number of keys to record */
#define MAX_SEG_LEN     (MAX_NUM_KEYS*sizeof(SHORT_KEY))

typedef union
    {
    SHORT_KEY keys[MAX_NUM_KEYS];
    UBYTE buf[MAX_SEG_LEN];
    } MACRO_SEG_CONTENTS;
    
typedef struct
    {
    MACRO_HEADER h;
    MACRO_SEGMENT_HEADER f;
    MACRO_SEG_CONTENTS u;
    } MACRO;

LOCAL_D MACRO recStore;
LOCAL_D TEXT *pRecname;
LOCAL_D WORD defAccl;
LOCAL_D WORD allQuiet;

#define RECORD_STATE_CHECKED_NAME   0x01
#define RECORD_STATE_FETCHING_KEYS  0x02

LOCAL_D WORD record;    /* flags giving state of recording */
LOCAL_D WORD nameMatch; /* index of macro matching name now being defined */

typedef union
    {
    SHORT_KEY *pk;
    TEXT *pl;
    MACRO_SEGMENT_HEADER *psh;
    } MACRO_UNIT;

typedef struct fcb_q
    {
    struct fcb_q *prev;
    VOID *fcb;
    } FCB_Q;

typedef struct
    {
    MACRO *mac;
    FCB_Q *script;
    TEXT *pb;
    UWORD nsteps;
    UWORD nsegsteps;
    UWORD segtype;
    MACRO_UNIT u;
    TEXT *lastStep;
    UBYTE cancelled;
    UBYTE errorHandling;
    UBYTE singleStep;
    UBYTE skipping;
    UWORD dValue;
    } PLAYBACK_STATE;

LOCAL_D PLAYBACK_STATE pbs;

#define SINGLE_STEPPING_OFF     0
#define SINGLE_STEPPING_ON      1
#define SINGLE_STEPPING_NOTIFY  2
#define SINGLE_STEPPING_NUDGE   3

LOCAL_D TEXT msgBuf[80];   /* preserve buffer after routine returns */
LOCAL_D TEXT errScript[P_FNAMESIZE];

LOCAL_D VOID *free1;    /* buffer to free when messaging returns */
LOCAL_D VOID *free2;    /* buffer to free when messaging returns */

LOCAL_C VOID FreeCells(VOID)
    {
    p_free(free1);
    p_free(free2);
    free1=NULL;
    free2=NULL;
    }

LOCAL_C INT fFree(INT ret)
    {
    if (ret<0)
        {
        FreeCells();
        p_leave(ret);
        }
    return(ret);
    }

LOCAL_C VOID NotifyErr(TEXT *pb,INT err)
    {
    if (err==E_GEN_NSUP)
        p_notify(pb,"App isn't supporting keyboard macros",0,0,0);
    else
        p_notifyerr(err,pb,0,0,0);
    }

LOCAL_C VOID NotifyPlaybackErr(INT ret)
    {
    TEXT buf[140];

    if (!pbs.lastStep)
        NotifyErr("Macro play back interrupted",ret);
    else
        {
        p_atos(&buf[0],"Play back interrupted, line \"%s\"",pbs.lastStep);
        NotifyErr(&buf[0],ret);
        }
    }

LOCAL_C INT LoadResource(INT rid,VOID **ppb)
    {
    return(p_entersend4(rscfile,O_RS_READ,rid,ppb));
    }

LOCAL_C MACRO *Macro(INT n)
    {
    return((MACRO *)p_send3(macros,O_VA_PBUF,n));
    }

LOCAL_C VOID TryConnectToForeground(VOID)
    {
    INT val;

    val=p_entersend2(lpcon,O_LPC_ATTACH_FORE);
    if (val==E_GEN_NSUP)
        {
        NotifyErr("Can't attach to foreground app",val);
        val=E_GEN_FAIL;
        }
    f_leave(val);
    }

LOCAL_C VOID DoMessage(VOID)
    {
    TryConnectToForeground();
    p_send3(lpcon,O_LPC_MESSAGE,&msgBuf[0]);
    mactive=O_KM_MESSAGE;
    }

LOCAL_C VOID Message(TEXT *pb,...)
    {
    msgBuf[p_atob(&msgBuf[0],pb,&pb+1)]=0;
    DoMessage();
    }

LOCAL_C VOID Write(TEXT *pb)
    {
    f_leave(p_send4(lpcon,O_LPC_ACTION,*pb,pb+1));
    }

LOCAL_C VOID NotifyStep(VOID)
    {
    p_notify("About to play back",pbs.lastStep,0,0,0);
    }

LOCAL_C VOID NotifyKey(SHORT_KEY *pkey)
    {
    TEXT buf[30];

    p_atos(&buf[0],"Keycode %d, modifiers %d",pkey->key,pkey->mods);
    p_notify("About to play back",&buf[0],0,0,0);
    }

LOCAL_C VOID WriteKeyWithNotify(SHORT_KEY *pkey)
    {
    if (pbs.singleStep==SINGLE_STEPPING_NOTIFY)
        NotifyKey(pkey);
    f_leave(p_send4(lpcon,O_LPC_KEY,pkey->key,pkey->mods));
    }

LOCAL_C INT RemoteDialog(LPC_DIALOG_DATA *pd)
    {
    inDialog=TRUE;
    DatLocked++;
    return(p_entersend3(lpcon,O_LPC_DIALOG,pd));
    }

LOCAL_C INT RemoteDialogF(LPC_DIALOG_DATA *pd)
    {
    TryConnectToForeground();
    return(RemoteDialog(pd));
    }

LOCAL_C VOID DoubleAllocDialog(INT mainrid,INT extrarid)
    {
    LPC_DIALOG_DATA d;

    d.main_len=f_leave(LoadResource(mainrid,&d.main));
    d.extra_val=LoadResource(extrarid,&d.extra);
    if (d.extra_val<0)
        {
        p_free(d.main);
        p_leave(d.extra_val);
        }
    free1=d.main;
    free2=d.extra;
    fFree(RemoteDialogF(&d));
    }

LOCAL_C INT FindMacroByAccl(UINT key)
    {
    INT n;
    INT i;

    n=macros->varoot.nrec;
    for (i=0; i<n; i++)
        {
        if (Macro(i)->h.accl==key)
            return(i);
        }
    return(-1);
    }

LOCAL_C INT FindMacroByName(TEXT *name)
    {
    INT i;
    INT n;

    n=macros->varoot.nrec;
    for (i=0; i<n; i++)
        {
        if (!p_scmpi(&Macro(i)->h.name[0],name))
            return(i);
        }
    return(-1);
    }

LOCAL_C VOID RemoveLeadingSpacesInName(VOID)
    {
    TEXT *pb1;
    TEXT *pb2;

    pb1=(&recStore.h.name[0]);
    pb2=p_skipwh(pb1);
    if (pb1==pb2)
        return;
    p_scpy(pb1,pb2);
    }

LOCAL_C VOID CheckForDuplicateName(VOID)
    {
    nameMatch=FindMacroByName(&recStore.h.name[0]);
    if (nameMatch<0)
        return;
    DoubleAllocDialog(KMAC_DL_CONFIRM,KMAC_AC_CONFIRM);
    mactive=O_KM_NAME_CHECK;
    p_leave(-1);
    }

LOCAL_C INT LoadMacroNameList(VOID **pext)
    {
    TEXT *pb;
    TEXT *pb2;
    TEXT *new_pb;
    INT i;
    INT size;
    INT this_size;
    MACRO *pmac;

    pb=p_alloc(1);
    if (!pb)
        return(0);
    size=1;
    i=macros->varoot.nrec;
    *pb=i;
    while (i--)
        {
        pmac=Macro(i);
        this_size=p_slen(&pmac->h.name[0])+2;   /* include LBC and ZTS */
        if (pmac->h.accl)
            this_size+=4;
        new_pb=p_realloc(pb,size+this_size);
        if (!new_pb)
            {
            p_free(pb);
            return(0);
            }
        pb=new_pb;
        *(pb+size)=this_size-1;             /* leading byte count */
        pb2=p_scpy(pb+size+1,&pmac->h.name[0]);
        size+=this_size;
        if (pmac->h.accl)
            {
            p_scpy(pb2," [x]");
            *(pb2+2)=pmac->h.accl+('A'-1);
            }
        }
    *pext=pb;
    return(size);
    }

LOCAL_C VOID FileWrite(TEXT *pb)
    {
    f_write(fcb,pb,p_slen(pb));
    }

LOCAL_C INT FileRead(VOID)
    {
    INT ret;

    ret=p_read(fcb,&fileBuf[0],99);
    if (ret==E_FILE_EOF)
        return(TRUE);
    f_leave(ret);
    fileBuf[ret]=0;
    return(0);
    }

LOCAL_C TEXT *StartNextWord(TEXT *pb)
    {
    return(p_skipwh(p_skipch(pb)));
    }

LOCAL_C VOID AppendMacro(VOID *end)
    {
    RC_VAXVAR rec;

    rec.buf=(UBYTE *)(&recStore);
    rec.len=((UBYTE *)end)-rec.buf;
    p_send3(macros,O_VA_APPEND,&rec);
    }

LOCAL_C VOID PresentReviseDialog(VOID)
    {
    DoubleAllocDialog(KMAC_DL_REVISING,KMAC_MN_REVISING);
    mactive=O_KM_DO_REVISE;
    }

LOCAL_C VOID CaptureKey(INT keycode)
    {
    wCaptureKey(keycode,SHIFT_CTRL,ALL_MODS);
    }

LOCAL_C VOID CancelCaptureKey(INT keycode)
    {
    wCancelCaptureKey(keycode,SHIFT_CTRL,ALL_MODS);
    }

LOCAL_C VOID CancelCaptureAllKeys(VOID)
    {
    INT i;
    UBYTE *p;

    p=(&capt[0]);
    i=1;
    while (i<=26)
        {
        if (*p)
            {
            CancelCaptureKey(i);
            *p=FALSE;
            }
        p++;
        i++;
        }
    }

LOCAL_C VOID DeleteMacro(INT n)
    {
    INT accl;

    accl=Macro(n)->h.accl;
    p_send3(macros,O_VA_DELETE,nameMatch);
    if (FindMacroByAccl(accl)<0)
        CancelCaptureKey(accl);
    }

LOCAL_C VOID CheckGrow(WORD *pLength,INT growBy)
    {
    if ((*pLength+=growBy)>MAX_SEG_LEN)
        p_leave(E_GEN_TOOMANY);
    }

LOCAL_C VOID GetDefAccl(TEXT *pb)
    {
    WORD val;

    pb=StartNextWord(pb);
    val=p_toupper(*pb);
    if (val=='?')
        defAccl=(-1);
    else if (val=='-')
        defAccl=0;
    else if (val>='A' && val<='Z')
        defAccl=val-('A'-1);
    else
        p_leave(E_FILE_INVALID);
    }

LOCAL_C VOID GetMacroName(VOID)
    {
    TEXT *pb;
    WORD val;

    FOREVER
        {
        if (FileRead())
            p_leave(0);
        pb=p_skipwh(&fileBuf[0]);
        val=(*pb);
        if (val=='M')
            break;
        if (val=='A')
            GetDefAccl(pb);
        if (val=='Q')
            allQuiet=TRUE;
        }
    pb=StartNextWord(pb);
    val=p_slen(pb);
    if (!val || val>=MACRO_NAME_LEN)
        p_leave(E_FILE_INVALID);
    p_scpy(&recStore.h.name[0],pb);
    }

LOCAL_C VOID AddScriptFile(TEXT *pname)
    {
    FCB_Q *new;
    TEXT buf[P_FNAMESIZE];

    if (!pbs.script)
        {    /* step not actually processed yet - so "put back" */
        pbs.nsteps++;       /* NB only put it back ONCE */
        pbs.nsegsteps++;
        }
    new=f_alloc(sizeof(FCB_Q));
    new->prev=pbs.script;
    new->fcb=NULL;
    pbs.script=new;
    f_fparse(pname,DatUsedPathNamePtr,&buf[0],NULL);
    f_open(&new->fcb,&buf[0],P_FTEXT|P_FSHARE|P_FRANDOM);
    fcb=new->fcb;
    }

LOCAL_C VOID TryAddScriptFile(TEXT *pfmt)
    {
    TEXT buf[P_FNAMESIZE];
    P_INFO junk;

    if (p_ssubi(pfmt,"%d"))
        p_atos(&buf[0],pfmt,pbs.dValue);
    else
        p_send4(lpcon,O_LPC_SENSE_NAME,&buf[0],pfmt);
    f_fparse(&buf[0],DatUsedPathNamePtr,&buf[0],NULL);
    if (!p_finfo(&buf[0],&junk))
        AddScriptFile(&buf[0]);
    }

LOCAL_C VOID CloseScriptFile(VOID)
    {
    FCB_Q *prev;

    p_close(pbs.script->fcb);
    prev=pbs.script->prev;
    p_free(pbs.script);
    pbs.script=prev;
    if (prev)
        fcb=prev->fcb;
    }

LOCAL_C VOID ClearPlayBackState(VOID)
    {
    while (pbs.script)
        CloseScriptFile();
    pbs.mac=NULL;
    pbs.singleStep=SINGLE_STEPPING_OFF;
    DatLocked--;
    }

LOCAL_C VOID fstoi(TEXT **ppb,WORD *pval)
    {
    INT ret;

    ret=p_stoi(ppb,pval);
    if (ret==E_GEN_FAIL)
        ret=E_GEN_ARG;  /* otherwise no error report given later */
    f_leave(ret);
    }

LOCAL_C VOID GetKeyFromBuffer(TEXT **ppb,SHORT_KEY *pk)
    {
    WORD val;
    TEXT *pb;

    pb=(*ppb);
    fstoi(&pb,&val);
    pk->key=val;
    pk->mods=0;
    pb=p_skipwh(pb);
    val=(*pb);
    if (val==',')
        pb++;
    else if (val)
        {
        fstoi(&pb,&val);
        pk->mods=val;
        }
    pb=p_skipwh(pb);
    if (*pb==',')
        pb=p_skipwh(pb+1);
    *ppb=pb;
    }

LOCAL_C VOID InitPlayBackState(MACRO *mac)
    {
    p_bfil(&pbs,sizeof(pbs),0);
    pbs.nsteps=mac->h.nsteps;
    pbs.nsegsteps=mac->f.nsteps;
    pbs.segtype=mac->f.type;
    pbs.u.pk=(&mac->u.keys[0]);
    }

LOCAL_C VOID StartPlayback(INT nsel)
    {
    MACRO *tmp;

    p_sleep(1);  /* in case attached app needs time to unlock itself */
    tmp=Macro(nsel);
    InitPlayBackState(tmp);
    if (!(tmp->h.flags&MAC_FLG_BACK))
        TryConnectToForeground();
    pbs.mac=tmp;
    DatLocked++;
    }

LOCAL_C VOID Rewind(VOID)
    {
    LONG junk;

    if (pbs.script)
        f_seek(fcb,P_FREWIND,&junk);
    else
        InitPlayBackState(pbs.mac);
    }

LOCAL_C VOID SetSkipping(INT skipping)
    {
    pbs.skipping=skipping;
    if (!skipping)
        Rewind();
    }

LOCAL_C VOID CheckForSkipping(VOID)
    {
    WORD skipping;
    WORD val;
    INT reverse;
    TEXT *pb;
    WORD ret;

    skipping=1;
    pb=pbs.lastStep+1;
    fstoi(&pb,&val);
    if (*pb)
        {
        pb=p_skipwh(pb+1);
        fstoi(&pb,&skipping);
        }
    reverse=FALSE;
    if (val<0)
        {
        reverse=TRUE;
        val=(-val);
        }
    if (*pbs.lastStep=='J')
        {
        ret=p_send2(lpcon,O_LPC_PROCESS_INFO);
        ret&=val;
        if (!reverse && !ret || reverse && ret)
            return;
        }
    else
        {
        ret=p_send2(lpcon,O_LPC_COUNT_DIALOGS);
        if (!reverse && ret<=val || reverse && ret>=val)
            return;
        }
    SetSkipping(skipping);
    }

LOCAL_C VOID SkipSteps(VOID)
    {
    WORD skipping;
    TEXT *pb;

    skipping=1;
    pb=pbs.lastStep+1;
    if (*pb)
        fstoi(&pb,&skipping);
    SetSkipping(skipping);
    }

LOCAL_C VOID ReadSingleStepValue(VOID)
    {
    WORD ss;
    TEXT *pb;

    ss=0;
    pb=pbs.lastStep+1;
    if (*pb)
        fstoi(&pb,&ss);
    if (ss>SINGLE_STEPPING_NUDGE || ss<0)
        p_leave(E_GEN_ARG);
    pbs.singleStep=ss;
    }

LOCAL_C VOID EnsurePath(VOID)
    {
    P_FPARSE crk;
    TEXT buf[P_FNAMESIZE];

    f_fparse(DatUsedPathNamePtr,NULL,&buf[0],&crk);
    buf[crk.system+crk.device+crk.path]=0;
    p_mkdir(&buf[0]);
    }

LOCAL_C VOID SaveMacrosToFile(PR_KCOMMAN *self)
    {
    INT ret;

    if (!macrosChanged)
        return;
    EnsurePath();
    f_open(&fcb,DatUsedPathNamePtr,P_FTEXT|P_FREPLACE|P_FUPDATE);
    ret=p_entersend2(self,O_KM_ENTERABLE_SAVE);
    p_close(fcb);
    if (ret)
        {
        p_delete(fcb);
        p_leave(ret);
        }
    macrosChanged=FALSE;
    }

LOCAL_C VOID SetUpStatusNames(TEXT *pb)
    {
    TEXT buf[P_FNAMESIZE];
    P_FPARSE crk;

    DatUsedPathNamePtr=pb;
    f_fparse(pb,0,&buf[0],&crk);
    DatStatusNamePtr=pb+P_FSYSNAMESIZE+crk.device+crk.path;
    }

LOCAL_C VOID CheckGoodKmacFile(TEXT *pb)
    {
    VOID *tfcb;
    INT ret;
    TEXT tmpBuf[KMAC_FILE_SIG_LEN];

    f_open(&tfcb,pb,P_FTEXT|P_FSHARE);
    ret=p_read(tfcb,&tmpBuf[0],KMAC_FILE_SIG_LEN);
    if (ret==KMAC_FILE_SIG_LEN)
        ret=p_bcmp(KMAC_FILE_SIG,KMAC_FILE_SIG_LEN,
                                            &tmpBuf[0],KMAC_FILE_SIG_LEN);
    p_close(tfcb);
    if (ret)
        {
        NotifyErr("Cannot open specified file",E_FILE_INVALID);
        p_leave(E_GEN_FAIL);
        }
    }

LOCAL_C VOID ResetEnvironment(VOID)
    {
    defAccl='M'-('A'-1);
    allQuiet=FALSE;
    }

LOCAL_C VOID HookDylToKmac(TEXT *pb)
    {
    HANDLE catHand;
    VOID *objHand;

    f_leave(p_loadlib(pb,&catHand,TRUE));
    objHand=p_newlibh(catHand,0);
    if (objHand)
        {
        if (!p_entersend3(objHand,1,catHand))
            return;
        p_send2(objHand,O_DESTROY);
        }
    p_unloadlib(catHand);
    if (!objHand)
        p_leave(E_GEN_NOMEMORY);
    }

LOCAL_C INT CheckCondition(TEXT *pb)
    {
    TEXT buf[32];
    TEXT *pfmt;
    WORD val;

    pb=p_skipwh(pb);
    val=p_toupper(*pb++);
    switch (val)
        {
    case 0:
        return(TRUE);
    case 'D':
        fstoi(&pb,&val);
        return(pbs.dValue<=val);
    default:
        pfmt="%f";
        *(pfmt+1)=val;
        p_send4(lpcon,O_LPC_SENSE_NAME,&buf[0],pfmt);
        return(p_scmpi(pb,&buf[0]));
        }
    }

LOCAL_C VOID *fAdjust(VOID *p,INT offset,INT adjust)
    {
    VOID *newp;

    newp=p_adjust(p,offset,adjust);
    if (newp<0)
        {
        p_free(p);
        p_leave(E_GEN_NOMEMORY);
        }
    return(newp);
    }

#define SZ_DLG_FLAGS    2
#define SZ_TXT_HEADER   6
#define SZ_TXT_BODY     2
#define SZ_TXT_ITEM     (SZ_TXT_HEADER+SZ_TXT_BODY)
#define SZ_LEN_WORD     2

LOCAL_C VOID DoCustomisableDialog(TEXT *pb,INT extraID)
    {
    INT ind;
    INT titLen;
    INT secLen;
    INT adjust;
    INT offset;
    UBYTE *ptxt;
    UBYTE *pcnt;
    LPC_DIALOG_DATA d;

    titLen=p_slen(pb);
    ind=p_sloc(pb,',');
    if (ind<0)
        secLen=0;
    else
        {
        secLen=titLen-ind-1;
        titLen=ind;
        }
    d.main_len=f_leave(LoadResource(KMAC_DL_NOTIFY,&d.main));
    adjust=titLen-1;
    d.main=fAdjust(d.main,SZ_DLG_FLAGS,adjust);
    d.main_len+=adjust;
    ptxt=p_bcpy((UBYTE *)d.main+SZ_DLG_FLAGS,pb,titLen);
    *ptxt++=0;
    pcnt=ptxt++;
    if (!secLen)
        {
        (*pcnt)--;
        p_adjust(d.main,ptxt-(UBYTE *)d.main,-SZ_TXT_ITEM-SZ_LEN_WORD);
        d.main_len-=SZ_TXT_ITEM+SZ_LEN_WORD;
        }
    else
        {
        adjust=secLen-1;
        offset=ptxt-(UBYTE *)d.main+SZ_LEN_WORD+SZ_TXT_HEADER;
        d.main=fAdjust(d.main,offset,adjust);
        d.main_len+=adjust;
        ptxt=(UBYTE *)d.main+offset;
        (*(UWORD *)(ptxt-SZ_LEN_WORD-SZ_TXT_HEADER))+=adjust;
        p_bcpy(ptxt,pb+titLen+1,secLen+1);
        }
    d.extra_val=LoadResource(extraID,&d.extra);
    if (d.extra_val<0)
        {
        p_free(d.main);
        p_leave(d.extra_val);
        }
    free1=d.main;
    free2=d.extra;
    fFree(RemoteDialog(&d));
    }

LOCAL_C VOID DoNotifyDialog(TEXT *pb)
    {
    DoCustomisableDialog(pb,KMAC_AC_NOTIFY);
    mactive=O_KM_DO_PLAYBACK;
    }

LOCAL_C VOID DoQueryDialog(TEXT *pb)
    {
    DoCustomisableDialog(pb,KMAC_AC_QUERY);
    mactive=O_KM_AFT_QUERY;
    }

LOCAL_C VOID QueryIfCouldAttach(VOID)
    {
    pbs.dValue=(p_entersend3(lpcon,O_LPC_TEST_PID,0)<0? 0: 1);
    }

#pragma save,METHOD_CALL

METHOD INT kcomman_km_name_check(PR_KCOMMAN *self)
    {
    if (!mstat)
        return(0);      /* user cancelled dialog */
    DeleteMacro(nameMatch);
    macrosChanged=TRUE;
    if (mstat==1)       /* overwrite */
        {
        record=RECORD_STATE_CHECKED_NAME;
        DatLocked++;
        return(p_send2(self,O_KM_RECORD_STEP));
        }
    return(0);
    }

METHOD INT kcomman_km_record_start(VOID)
    {
    LPC_DIALOG_DATA d;

    p_bfil(&recStore,sizeof(MACRO),0);
    if (allQuiet)
        recStore.h.flags=MAC_FLG_QUIET;
    d.main_len=f_leave(LoadResource(KMAC_DL_MAC_NAME,&d.main));
    d.extra=(&pRecname);
    d.extra_val=(-2);   /* editor in position 1 in dialog */
    free1=d.main;
    fFree(RemoteDialogF(&d));
    mactive=O_KM_RECORD_STEP;
    return(0);
    }

METHOD INT kcomman_km_record_step(VOID)
    {
    SHORT_KEY *pkey;
    LOCAL_D LPC_GET_KEY tmp;

    if (!(record&RECORD_STATE_CHECKED_NAME))
        {
        RemoveLeadingSpacesInName();
        CheckForDuplicateName();    /* may leave */
        DatLocked++;
        record=RECORD_STATE_CHECKED_NAME;
        }
    if (!(record&RECORD_STATE_FETCHING_KEYS))
        {
        p_send3(lpcon,O_LPC_RECORD,TRUE);
        record=RECORD_STATE_FETCHING_KEYS|RECORD_STATE_CHECKED_NAME;
        }
    else
        {
        pkey=(&recStore.u.keys[recStore.h.nsteps++]);
        pkey->key=tmp.key;
        pkey->mods=tmp.mods;
        if (recStore.h.nsteps==MAX_NUM_KEYS)
            {
            Message("Maximum size of keyboard macro reached");
            p_sleep(20);
            mactive=O_KM_RECORD_STOP;
            return(0);
            }
        }
    p_send3(lpcon,O_LPC_GET_KEY,&tmp);
    mactive=O_KM_RECORD_STEP;
    return(0);
    }

METHOD INT kcomman_km_record_stop(VOID)
    {
    INT already;
    TEXT *pfmt;

    if (record)
        {
        p_send3(lpcon,O_LPC_RECORD,FALSE);
        record=FALSE;
        DatLocked--;
        mactive=O_KM_RECORD_STOP;
        return(0);
        }
    if (!recStore.h.nsteps)
        {
        Message("Macro \"%s\" cancelled",pRecname);
        return(0);
        }
    if (defAccl>0)
        {
        recStore.h.accl=defAccl;
        already=FindMacroByAccl(defAccl);
        }
    recStore.f.nsteps=recStore.h.nsteps;
    AppendMacro(&recStore.u.keys[recStore.h.nsteps]);
    macrosChanged=TRUE;
    if (defAccl>0)
        {
        if (already<0)
            CaptureKey(defAccl);
        else
            Macro(already)->h.accl=0;
        }
    pfmt=(recStore.h.nsteps>1? "Macro \"%s\" recorded (%d keys)":
                                        "Macro \"%s\" recorded (1 key)");
    Message(pfmt,pRecname,recStore.h.nsteps);
    if (defAccl<0)
        {
        revise=macros->varoot.nrec-1;
        mactive=O_KM_DEFINE_ACCL;
        }
    return(0);
    }

METHOD INT kcomman_km_define_accl(VOID)
    {
    PresentReviseDialog();
    return(0);
    }

METHOD INT kcomman_km_playback(VOID)
    {
    LPC_DIALOG_DATA d;

    if (!macros->varoot.nrec)
        {
        Message("No macros to play back");
        return(0);
        }
    d.main_len=f_leave(LoadResource(KMAC_DL_PLAYBACK,&d.main));
    d.extra_val=LoadMacroNameList(&d.extra);
    if (!d.extra_val)
        {
        p_free(d.main);
        p_leave(E_GEN_NOMEMORY);
        }
    free1=d.main;
    free2=d.extra;
    fFree(RemoteDialogF(&d));
    mactive=O_KM_DO_PLAYBACK;
    return(0);
    }

METHOD INT kcomman_km_command(PR_KCOMMAN *self)
    {
    TEXT buf[128];

    wGetCommand((UBYTE *)&buf[0]);
    commandByte=buf[0];
    if (commandByte=='L')
        {
        HookDylToKmac(&buf[1]);
        return(0);
        }
    SaveMacrosToFile(self);
    if (commandByte=='X')
        p_exit(0);
    if (commandByte=='O')
        CheckGoodKmacFile(&buf[1]);     /* before committing to it */
    if (!pUsedFileName)
        pUsedFileName=f_alloc(P_FNAMESIZE);
    p_scpy(pUsedFileName,&buf[1]);
    SetUpStatusNames(pUsedFileName);
    p_send2(macros,O_VA_RESET);
    CancelCaptureAllKeys();
    p_send2(self,O_KM_LOAD);
    return(0);
    }

METHOD INT kcomman_km_enterable_save(PR_KCOMMAN *self)
    {
    INT i;
    INT n;
    INT ltot;
    INT l;
    INT j;
    MACRO *m;
    TEXT *pb;
    MACRO_UNIT u;
    MACRO_SEGMENT_HEADER *psh;

    FileWrite(KMAC_FILE_SIG);
    pb=p_scpy(&fileBuf[0],"AUTO ");
    *pb++=(defAccl>0? 'A'-1+defAccl: defAccl? '?': '-');
    *pb=0;
    FileWrite(&fileBuf[0]);
    if (allQuiet)
        FileWrite("QUIET");
    n=macros->varoot.nrec;
    for (i=0; i<n; i++)
        {
        m=Macro(i);
        p_scpym(&fileBuf[0],"MACRO ",&m->h.name[0],NULL);
        FileWrite(&fileBuf[0]);
        if (m->h.accl)
            {
            pb=p_scpy(&fileBuf[0],"KEY ");
            *pb++='A'-1+m->h.accl;
            *pb=0;
            FileWrite(&fileBuf[0]);
            }
        if (m->h.flags&MAC_FLG_BACK)
            FileWrite("BACKGROUND");
        if (m->h.flags&MAC_FLG_QUIET)
            FileWrite("QUIET");
        FileWrite("CONTENTS");
        ltot=m->h.nsteps;
        psh=(&m->f);
        u.pk=(&m->u.keys[0]);
        FOREVER
            {
            l=psh->nsteps;
            ltot-=l;
            if (psh->type==MACRO_SEG_TYPE_COMMANDS)
                {
                while (l--)
                    {
                    j=p_slen(u.pl);
                    f_write(fcb,u.pl,j);
                    u.pl+=j+1;
                    }
                }
            else
                {
                j=0;
                pb=(&fileBuf[0]);
                while (l--)
                    {
                    if (j)
                        {
                        *pb++=',';
                        *pb++=' ';
                        }
                    pb+=p_itob(pb,u.pk->key);
                    if (u.pk->mods)
                        {
                        *pb++=' ';
                        pb+=p_itob(pb,u.pk->mods);
                        }
                    u.pk++;
                    if (++j==10)
                        {
                        *pb=0;
                        FileWrite(&fileBuf[0]);
                        j=0;
                        pb=(&fileBuf[0]);
                        }
                    }
                if (j)
                    {
                    *pb=0;
                    FileWrite(&fileBuf[0]);
                    }
                }
            if (!ltot)
                break;
            psh=u.psh;
            u.psh++;
            }
        FileWrite("");
        }
    return(0);
    }

METHOD INT kcomman_km_accl(VOID)
    {
    LPC_DIALOG_DATA d;

    if (!macros->varoot.nrec)
        {
        Message("No macros to review");
        return(0);
        }
    d.main_len=f_leave(LoadResource(KMAC_DL_REVISE,&d.main));
    d.extra_val=LoadMacroNameList(&d.extra);
    if (!d.extra_val)
        {
        p_free(d.main);
        p_leave(E_GEN_NOMEMORY);
        }
    free1=d.main;
    free2=d.extra;
    fFree(RemoteDialogF(&d));
    mactive=O_KM_REVISE_ACCL;
    return(0);
    }

METHOD INT kcomman_km_revise_accl(VOID)
    {
    if (mstat<0)
        return(0);
    revise=macros->varoot.nrec-1-mstat;
    PresentReviseDialog();
    }

METHOD INT kcomman_km_do_revise(VOID)
    {
    MACRO *m;
    INT thisAcc;
    INT already;

    if (mstat<0)
        return(0);
    m=Macro(revise);
    thisAcc=m->h.accl;
    if (mstat==thisAcc)
        return(0);
    macrosChanged=TRUE;
    if (thisAcc)
        CancelCaptureKey(thisAcc);
    if (mstat)
        already=FindMacroByAccl(mstat);
    m->h.accl=mstat;
    if (!mstat)
        return(0);
    if (already>=0)
        Macro(already)->h.accl=0;
    else
        CaptureKey(mstat);
    return(0);
    }

METHOD INT kcomman_km_help(VOID)
    {
    LPC_DIALOG_DATA d;

    d.main_len=f_leave(LoadResource(KMAC_DL_HELP,&d.main));
    d.extra_val=0;
    free1=d.main;
    fFree(RemoteDialogF(&d));
    mactive=O_KM_HELP;
    return(0);
    }

METHOD INT kcomman_km_interrupt(PR_KCOMMAN *self)
    {
    pbs.cancelled=TRUE;
    if (!mactive)           /* must be single stepping */
        p_send2(self,O_KM_DO_PLAYBACK);
    else
        p_send2(lpcon,O_LPC_CANCEL);
    return(0);
    }

METHOD INT kcomman_km_do_playback_err(PR_KCOMMAN *self)
    {
    pbs.errorHandling=FALSE;
    AddScriptFile(&errScript[0]);
    p_send2(self,O_KM_DO_PLAYBACK);
    return(0);
    }

METHOD INT kcomman_km_do_playback(PR_KCOMMAN *self)
    {
    MACRO *tmp;
    SHORT_KEY k;

    pbs.errorHandling=FALSE;
    if (pbs.singleStep==SINGLE_STEPPING_NUDGE)
        pbs.singleStep=SINGLE_STEPPING_OFF;
    if (!pbs.mac)
        {
        if (mstat<0)
            return(0); /* user cancelled "Select macro to playback" dialog */
        StartPlayback(macros->varoot.nrec-1-mstat);
        }
NextStep:
    if (pbs.cancelled || !pbs.nsteps)
        {
    ExitPlayback:
        tmp=pbs.mac;
        ClearPlayBackState();
        if (pbs.cancelled)
            Message("Play back cancelled");
        else if (!(tmp->h.flags&MAC_FLG_QUIET))
            Message("Play back successful");
        return(0);
        }
    while (pbs.script)
        {
        if (pbs.pb)
            {
        GetKey:
            GetKeyFromBuffer(&pbs.pb,&k);
            if (!*pbs.pb)
                pbs.pb=NULL;
            WriteKeyWithNotify(&k);
            goto DoReturn;
            }
        if (FileRead())
            {       /* EOF */
        ExitScript:
            CloseScriptFile();
            if (!pbs.script)
                {
                pbs.nsteps--;
                pbs.nsegsteps--;
                goto NextStep;
                }
            }
        else
            {
            if (pbs.skipping)
                {
                pbs.skipping--;
                goto NextStep;
                }
            pbs.lastStep=p_skipwh(&fileBuf[0]);
            if (p_isdigit(*pbs.lastStep))
                {
                pbs.pb=pbs.lastStep;
                goto GetKey;
                }
            goto ProcessStep;
            }
        }
    pbs.nsteps--;
    if (!pbs.nsegsteps)
        {
        pbs.nsegsteps=pbs.u.psh->nsteps;
        pbs.segtype=pbs.u.psh->type;
        pbs.u.psh++;
        pbs.lastStep=NULL;
        }
    pbs.nsegsteps--;
    if (pbs.segtype==MACRO_SEG_TYPE_KEYS)
        {
        if (pbs.skipping)
            {
            pbs.u.pk++;
            pbs.skipping--;
            goto NextStep;
            }
        WriteKeyWithNotify(pbs.u.pk);
        pbs.u.pk++;
        }
    else
        {
        pbs.lastStep=pbs.u.pl;
        pbs.u.pl+=p_slen(pbs.u.pl)+1;
        if (pbs.skipping)
            {
            pbs.skipping--;
            goto NextStep;
            }
    ProcessStep:
        if (pbs.singleStep==SINGLE_STEPPING_NOTIFY)
            NotifyStep();
        switch (*pbs.lastStep)
            {
        case 'N':       /* Notify style dialog */
            DoNotifyDialog(pbs.lastStep+1);
            return(0);
        case 'Q':       /* Query dialog */
            DoQueryDialog(pbs.lastStep+1);
            return(0);
        case 'j':       /* conditional jump on number of dialogs */
        case 'J':       /* conditional jump on specified process info */
            CheckForSkipping();
            goto NextStep;
        case 'd':       /* divert to script if process name matches */
            TryAddScriptFile(pbs.lastStep+1);
            goto NextStep;
        case 'e':       /* set error script */
            p_scpy(&errScript[0],pbs.lastStep+1);
            pbs.errorHandling=TRUE;
            goto NextStep;
        case 'h':       /* hook DYL */
            HookDylToKmac(pbs.lastStep+1);
            goto NextStep;
        case 'l':       /* load script unconditionally */
            AddScriptFile(pbs.lastStep+1);
            goto NextStep;
        case 'm':       /* move (unconditional jump) */
            SkipSteps();
        case 0:         /* empty line */
        case '!':       /* comment line */
            goto NextStep;
        case 'n':       /* nudge (wait for single step key) */
            pbs.singleStep=SINGLE_STEPPING_NUDGE;
            return(0);
        case 'q':       /* query if could attach to foreground */
            QueryIfCouldAttach();
            goto NextStep;
        case 'r':       /* return one level of script */
            if (CheckCondition(pbs.lastStep+1))
                {
                if (pbs.script)
                    goto ExitScript;
                goto ExitPlayback;
                }
            goto NextStep;
        case 's':       /* set single stepping state */
            ReadSingleStepValue();
            if (!pbs.singleStep)
                goto NextStep;
            return(0);
        case 'x':       /* exit playback */
            if (CheckCondition(pbs.lastStep+1))
                goto ExitPlayback;
            goto NextStep;
        default:        /* pass through to LPC */
            Write(pbs.lastStep);
            }
        }
DoReturn:
    mactive=O_KM_DO_PLAYBACK;
    return(0);
    }

METHOD INT kcomman_km_aft_intro(PR_KCOMMAN *self)
    {
    if (mstat==1)
        p_send2(self,O_KM_HELP);
    return(0);
    }

METHOD INT kcomman_km_aft_query(PR_KCOMMAN *self)
    {
    pbs.dValue=mstat;
    p_send2(self,O_KM_DO_PLAYBACK);
    return(0);
    }

METHOD INT kcomman_km_enterable_load(VOID)
    {
    TEXT *pb;
    WORD val;
    MACRO_UNIT u;
    WORD macLength;
    MACRO_SEGMENT_HEADER *psh;

    p_bfil(&capt[0],26,0);
    FileRead();
    if (p_scmp(&fileBuf[0],KMAC_FILE_SIG))
        return(E_FILE_INVALID);
    FOREVER
        {
        p_bfil(&recStore,sizeof(MACRO),0);
        GetMacroName();     /* calls p_leave(0) at EOF */
        FOREVER
            {
            if (FileRead())
                return(E_FILE_EOF);
            pb=p_skipwh(&fileBuf[0]);
            switch (*pb)
                {
            case 'C':
                goto StartContents;
            case 'K':
                pb=StartNextWord(pb);
                val=p_toupper(*pb);
                if (val<'A' || val>'Z')
                    return(E_FILE_INVALID);
                recStore.h.accl=val-('A'-1);
                break;
            case 'B':
                recStore.h.flags|=MAC_FLG_BACK;
                break;
            case 'Q':
                recStore.h.flags|=MAC_FLG_QUIET;
                break;
                }
            }
    StartContents:
        psh=(&recStore.f);
        macLength=0;
        u.pk=(&recStore.u.keys[0]);
        pb+=p_slen(pb);     /* force to zero terminator */
        FOREVER
            {
            if (!*pb)
                {
                if (FileRead())
                    break;
                pb=p_skipwh(&fileBuf[0]);
                if (!*pb)
                    break;
                }
            if (p_isdigit(*pb))
                {
                if (psh->type!=MACRO_SEG_TYPE_KEYS)
                    {
                    CheckGrow(&macLength,sizeof(MACRO_SEGMENT_HEADER));
                    psh=u.psh;
                    (u.psh)++;
                    psh->type=MACRO_SEG_TYPE_KEYS;
                    }
                CheckGrow(&macLength,sizeof(SHORT_KEY));
                GetKeyFromBuffer(&pb,u.pk);
                (u.pk)++;
                }
            else
                {
                if (psh->type!=MACRO_SEG_TYPE_COMMANDS)
                    {
                    if (recStore.h.nsteps)
                        {
                        CheckGrow(&macLength,sizeof(MACRO_SEGMENT_HEADER));
                        psh=u.psh;
                        (u.psh)++;
                        }
                    psh->type=MACRO_SEG_TYPE_COMMANDS;
                    }
                val=p_slen(pb);
                CheckGrow(&macLength,val+1);
                u.pl=(TEXT *)p_bcpy(u.pl,pb,val+1);
                pb+=val;
                }
            recStore.h.nsteps++;
            psh->nsteps++;
            }
        AppendMacro(u.pl);
        val=recStore.h.accl;
        if (val && !capt[val-1])
            {
            capt[val-1]=TRUE;
            CaptureKey(val);
            }
        }
    return(0);
    }

METHOD VOID kcomman_km_load(PR_KCOMMAN *self)
    {
    INT ret;

    if (commandByte=='C')
        {       /* create file, not open one */
        macrosChanged=TRUE;     /* force file save */
        SaveMacrosToFile(self);
        return;             
        }
    ResetEnvironment();
    ret=p_open(&fcb,DatUsedPathNamePtr,P_FTEXT|P_FSHARE);
    if (!ret)
        {
        ret=p_entersend2(self,O_KM_ENTERABLE_LOAD);
        p_close(fcb);
        }
    if (!ret)
        return;
    if (ret==E_GEN_TOOMANY)
        p_notify("Macro too long",&recStore.h.name[0],0,0,0);
    else
        NotifyErr("Error reading macro file",ret);
    p_exit(0);
    }

METHOD INT kcomman_km_message(VOID)
    {
    DoMessage();
    return(0);
    }

#pragma restore

LOCAL_C VOID MainLoop(VOID)
    {
    INT ret;
    INT key;
    INT method;

    FOREVER
        {
        if (!wactive)
            {
            wGetEvent(&wevent);
            wactive=TRUE;
            }
        if (DatApp7)
            {       /* hooked DYL gets unloaded from main code segment */
            p_unloadlib(DatApp7);
            DatApp7=NULL;
            }
        p_iowait();
        if (mactive && mstat!=E_FILE_PENDING)
            {
            if (mactive>O_KM_AFT_QUERY)
                {       /* may be set like this by hooked DYL */
                mactive=O_KM_AFT_QUERY;
                mstat++;
                }
            method=mactive;
            mactive=FALSE;
            ret=mstat;
            if (free1)
                FreeCells();
            if (inDialog)
                {
                inDialog=FALSE;
                DatLocked--;
                if (ret==E_GEN_INUSE)
                    {
                    p_notify("Cannot display Kmac dialog",
                                        "Foreground app too busy",0,0,0);
                    continue;
                    }
                }
            if (method==O_KM_RECORD_STOP && ret==E_FILE_CANCEL)
                ret=0;
            if (method==O_KM_MESSAGE || method==O_KM_HELP)
                {
                if (ret==E_GEN_RECEIVER)
                    goto ProcessMethod; /* try again with new foreground */
                if (ret>=E_GEN_FAIL)
                    continue;
                }
            if (ret<0)
                {
                if (record)
                    {
                    record=FALSE;
                    DatLocked--;
                    if (ret==E_GEN_RECEIVER)
                        {
                        method=O_KM_RECORD_STOP;
                        goto ProcessMethod;
                        }
                    NotifyErr("Macro recording interrupted",ret);
                    continue;
                    }
                if (pbs.mac)
                    {
                    if (ret==E_FILE_CANCEL && pbs.cancelled)
                        {
                        method=O_KM_DO_PLAYBACK;
                        goto ProcessMethod;
                        }
                    if (pbs.errorHandling)
                        {
                        method=O_KM_DO_PLAYBACK_ERR;
                        goto ProcessMethod;
                        }
                    }
                goto ReportError;
                }
            if (method==O_KM_DO_PLAYBACK && pbs.singleStep)
                continue;
            if (method)
                goto ProcessMethod;
            continue;
            }
        if (wevent.type==E_FILE_PENDING)
            p_panic(KMPANIC_STRAY_SIGNAL);
        wactive=FALSE;
        if (wevent.type==WM_FOREGROUND)
            {
            wSystemModal(100);
            continue;
            }
        if (wevent.type==WM_COMMAND)
            {
            method=O_KM_COMMAND;
            goto ProcessMethod;
            }
        if (wevent.type!=WM_KEY)
            continue;
        key=wevent.p.key.keycode;
        if (pbs.mac)
            {
            if (key==KMAC_INTERRUPT_KEY)
                {
                method=O_KM_INTERRUPT;
                goto ProcessMethod;
                }
            if (pbs.singleStep)
                {
                if (key==KMAC_PLAYBACK_KEY)
                    {
                    method=O_KM_DO_PLAYBACK;
                    goto ProcessMethod;
                    }
                p_notify("Not allowed when single stepping",
                         "Press Ctrl-Sh-\04 to step or Ctrl-Sh-Esc to quit",
                            0,0,0);
                }
            continue;
            }
        if (inDialog)
            continue;
        if (key==KMAC_RECORD_KEY_2)
            key=KMAC_RECORD_KEY;
        if (record)
            {
            if (key==KMAC_RECORD_KEY)
                {
                method=O_KM_RECORD_STOP;
                goto ProcessMethod;
                }
            continue;
            }
        if (mactive)
            continue;
        switch (key)
            {
        case KMAC_RECORD_KEY:
            method=O_KM_RECORD_START;
            break;
        case KMAC_PLAYBACK_KEY:
            method=O_KM_PLAYBACK;
            break;
        case KMAC_ACCL_KEY:
            method=O_KM_ACCL;
            break;
        case KMAC_HELP_KEY:
            method=O_KM_HELP;
            break;
        default:
            mstat=FindMacroByAccl(key);
            if (mstat<0)
                continue;
            mstat=macros->varoot.nrec-1-mstat;
            method=O_KM_DO_PLAYBACK;
            }
    ProcessMethod:
        ret=p_entersend2(comman,method);
    ReportError:
        if (ret<E_GEN_FAIL)
            {
            if (!pbs.mac)
                NotifyErr("Kmac operation interrupted",ret);
            else
                {
                ClearPlayBackState();
                NotifyPlaybackErr(ret);
                }
            }
        }
    }

LOCAL_C INT FindLocally(TEXT *pb)
    {
    INT ret;
    INT firstret;
    INT i;
    INT orig;
    TEXT *pdrv;
    P_INFO junk;

    pdrv=pb+P_FSYSNAMESIZE;
    orig=(*pdrv);
    i=0;
    FOREVER
        {
        ret=p_finfo(pb,&junk);
        if (!i)
            firstret=ret;
        if (!ret)
            return(0);
        if (i==4)
            break;
        *pdrv=(i? i+'A'-1: 'M');
        i++;
        }
    *pdrv=orig;
    return(firstret);
    }

LOCAL_C VOID LoadLpc(VOID)
    {
    TEXT *pb;

    pb="LOC::M:\\DYL\\LPC.DYL";
    FindLocally(pb);
    f_leave(p_loadlib(pb,&lpc,TRUE));
    }

LOCAL_C VOID CreateLpcon(VOID)
    {
    lpcon=f_newlibhsend(lpc,C_LPCON,O_LPC_INIT,&mstat);
    }

LOCAL_C VOID CreateComman(VOID)
    {
    comman=f_newsend(CAT_KMAC_KMAC,C_KCOMMAN,O_KM_LOAD);
    }

LOCAL_C VOID ConnectToWindowServer(VOID)
    {
    wConnect(f_alloc(sizeof(WSERV_SPEC)),0,W_CONNECT_AT_BACK);
    wSystemModal(100);
    }

LOCAL_C VOID CaptureKeys(VOID)
    {
    CaptureKey(KMAC_RECORD_KEY);
    CaptureKey(KMAC_RECORD_KEY_2);
    CaptureKey(KMAC_PLAYBACK_KEY);
    CaptureKey(KMAC_ACCL_KEY);
    CaptureKey(KMAC_HELP_KEY);
    CaptureKey(KMAC_INTERRUPT_KEY);
    }

LOCAL_C VOID InitRscfile(VOID)
    {
    rscfile=f_newsend(CAT_KMAC_OLIB,C_RSCFILE,O_RS_INIT,DatCommandPtr);
    }

LOCAL_C VOID InitialiseMacros(VOID)
    {
    macros=f_newsend(CAT_KMAC_OLIB,C_VAXVAR,O_VA_INIT,4);
    pRecname=(&recStore.h.name[0]);
    ResetEnvironment();
    }

LOCAL_C VOID CrackCommandLine(VOID)
    {
    TEXT *pb;

    pb=DatCommandPtr;
    pb+=p_slen(pb)+1;
    if (!*pb++)
        p_leave(E_GEN_ARG);     /* no command line */
    commandByte=(*pb++);
    DatProcessNamePtr=pb;
    pb+=p_slen(pb)+1;           /* skip process name */
    pb+=p_slen(pb)+1;           /* skip alias information */
    if (!*pb)                   /* check for filename */
        p_leave(E_GEN_ARG);
    SetUpStatusNames(pb);
    }

LOCAL_C VOID InitialiseGlobals(VOID)
    {
    DatApp1=lpcon;
    DatApp2=comman;
    DatApp3=macros;
    DatApp4=(&fcb);
    DatApp5=(&mactive);
    DatApp6=(&pbs);
    }

LOCAL_C VOID IntroDialog(VOID)
    {
    DoubleAllocDialog(KMAC_DL_INTRO,KMAC_AC_INTRO);
    mactive=O_KM_AFT_INTRO;
    }

#pragma save,ENTER_CALL

LOCAL_C INT Initialise(VOID)
    {
    p_linklib(0);
    CrackCommandLine();
    InitRscfile();
    InitialiseMacros();
    LoadLpc();
    CreateLpcon();
    ConnectToWindowServer();
    CaptureKeys();
    CreateComman();     /* and load file specified on command line */
    InitialiseGlobals();
    IntroDialog();
    return(0);
    }

#pragma restore

GLDEF_C INT main(VOID)
    {
    INT ret;

    ret=p_enter1(Initialise);
    if (!ret)
        MainLoop();
    if (ret<E_GEN_FAIL)
        NotifyErr("Failed to start Kmac",ret);
    return(0);
    }
