/* LPCON.C

  Copyright (C) Psion PLC 1994
  Started by : DavidW
  Started on:  1/1/94
*/

#include <p_std.h>
#include <p_gen.h>
#include <p_file.h>
#include <epoc.h>
#include <ats.h>
#include <wskeys.h>
#include <wlib.h>
#include <lpc.g>
#include <gate.g>
#include <hwimman.g>
#include <ipc.g>
#include "lpcdat.h"

#ifndef W_KEY_DIAMOND
#define W_KEY_DIAMOND 0x124
#endif

GLREF_D PR_GATE *DatGate;
GLREF_D VOID *DatCommandPtr;
GLREF_D VOID *DatUsedPathNamePtr;
GLREF_D VOID *DatStatusNamePtr;
GLREF_D VOID *DatProcessNamePtr;
GLREF_D VOID *DatCommandPtr;
GLREF_D UWORD DatLocked;
GLREF_D PR_APPMAN *w_am;
GLREF_D PR_WSERV *w_ws;
GLREF_D PR_DLGCHAIN *DatDialogPtr;

GLREF_C VOID WservName(VOID);
GLREF_C VOID OplComline(VOID);
GLREF_C VOID Sys$prgoName(VOID);

LOCAL_C VOID SignalCompletion(PR_LPCON *self,WORD completion)
    {
    *self->lpcon.pMstat=completion;
    p_iosignal();           /* force completion */
    }

LOCAL_C INT SendMessage(PR_LPCON *self,INT type,ATS_MESS_BODY *pu)
/*
Can be called within WaitHandler so never leaves in that case
*/
    {
    INT ret;

    if (type==TY_ATS_RECORD)
        return(p_msendreceivew(self->lpcon.pid,type,pu));
    ret=p_msendreceivea(self->lpcon.pid,type,pu,self->lpcon.pMstat);
    if (ret)
        SignalCompletion(self,ret);     /* simulate completion */
    if (self->lpcon.pUstat || self->lpcon.pstr)
        return(0);
    p_waitstat(self->lpcon.pMstat);
    return(self->lpcon.mstat);
    }

LOCAL_C VOID ResetPMstat(PR_LPCON *self)
/*
Reset to use user's stat, if specified, or else internal one
*/
    {
    self->lpcon.pMstat=
            (self->lpcon.pUstat? self->lpcon.pUstat: &self->lpcon.mstat);
    }

LOCAL_C VOID CancelStringState(PR_LPCON *self)
    {
    self->lpcon.pstr=NULL;
    ResetPMstat(self);
    }

LOCAL_C INT CurrentDriveLetter(VOID)
    {
    INT i;

    i=(*(((UBYTE *)DatCommandPtr)+P_FSYSNAMESIZE));
    if (i=='M')
        i='I';
    return(i);
    }

LOCAL_C INT LookUp(INT key)
    {
    switch (key)
        {
    case 'C':
        return(W_KEY_RETURN);
    case 'D':
        return(W_KEY_DOWN);
    case 'E':
        return(W_KEY_END);
    case 'H':
        return(W_KEY_HOME);
    case 'L':
        return(W_KEY_LEFT);
    case 'M':
        return(W_KEY_MENU);
    case 'P':
        return(W_KEY_PAGE_UP);
    case 'Q':
        return(W_KEY_PAGE_DOWN);
    case 'R':
        return(W_KEY_RIGHT);
    case 'T':
        return(W_KEY_TAB);
    case 'U':
        return(W_KEY_UP);
    case 'X':
        return(W_KEY_ESCAPE);
    case '+':
        return(W_KEY_DIAMOND);
    case '-':
        return(W_KEY_DELETE_LEFT);
    case '?':
        return(CurrentDriveLetter());
        }
    return(key);
    }

LOCAL_C VOID DoPause(PR_LPCON *self,INT delay)
/*
This will never leave since is called only within WaitHandler during string
handling, when messaging always takes place asynchronously
*/
    {
    p_send3(self,O_LPC_PAUSE,delay);
    }

LOCAL_C VOID ProcessNextCharacterInString(PR_LPCON *self)
    {
    INT chr;
    INT mods;

    mods=0;
Again:
    chr=(*self->lpcon.pstr++);
    if (!chr)
        {
        SignalCompletion(self,-1);
        return;
        }
    if (chr=='@')
        {
        chr=(*self->lpcon.pstr++);
        if (!chr)
            {
            SignalCompletion(self,-1);
            return;
            }
        if (chr>='0' && chr<='9')
            {
            mods=chr-'0';
            if (mods&1)
                {
                mods&=(~1);
                mods|=W_PSION_MODIFIER;
                }
            goto Again;
            }
        if (chr!='@')
            {
            mods=(p_isupper(chr)? W_PSION_MODIFIER|W_SHIFT_MODIFIER:
                                                    W_PSION_MODIFIER);
            chr+=W_SPECIAL_KEY;
            }
        }
    else if (chr=='^')
        {
        chr=(*self->lpcon.pstr++);
        if (!chr)
            {
            SignalCompletion(self,-1);
            return;
            }
        if (chr>='0' && chr<='9')
            {
            DoPause(self,10*(chr-'0'));
            return;
            }
        if (chr=='%')
            {
            DoPause(self,5);
            return;
            }
        if (chr=='Y')
            {
            p_send2(self,O_LPC_YIELD);
            SignalCompletion(self,0);
            return;
            }
        chr=LookUp(chr);
        }
    p_send4(self,O_LPC_KEY,chr,mods);
    }

LOCAL_C VOID SendEscapeKey(PR_LPCON *self)
    {
    p_send4(self,O_LPC_KEY,W_KEY_ESCAPE,0);
    }

#define wws ((PR_WSERV *)self->lpcon.pstr)

LOCAL_C VOID NextGroundStep(PR_LPCON *self)
    {
    INT ret;
    VOID *win;

    if (self->lpcon.strCount++==1)
        {
        ret=p_pcpyfr(self->lpcon.pid,&wws->wserv.bar,&win,2);
        if (ret)
            {
            SignalCompletion(self,ret);
            return;
            }
        if (win)
            {
            SendEscapeKey(self);
            return;
            }
        }
    if (self->lpcon.strCount==32)
        {
        SignalCompletion(self,E_GEN_TOOMANY);
        return;
        }
    ret=p_pcpyfr(self->lpcon.pid,&wws->wserv.dial,&win,2);
    if (!ret && !win)
        ret=(-1);
    if (ret)
        {
        SignalCompletion(self,ret);
        return;
        }
    SendEscapeKey(self);
    }

#undef wws

LOCAL_C INT WaitHandler(PR_LPCON *self)
    {
    if (self->lpcon.pid && self->lpcon.lstat!=E_FILE_PENDING)
        {       /* attached process has terminated */
        self->lpcon.pid=0;
        if (self->lpcon.pstr)
            {           /* cancel any string processing */
            if (self->lpcon.sstat!=E_FILE_PENDING)
                p_iowait();         /* use up the string signal */
            CancelStringState(self);
            self->lpcon.cancel=FALSE;
            }
        if (*self->lpcon.pMstat==E_FILE_PENDING)
            SignalCompletion(self,E_GEN_RECEIVER);
        return(P_SIGNAL_DISABLE);
        }
    if (!self->lpcon.pstr || self->lpcon.sstat==E_FILE_PENDING)
        return(P_SIGNAL_UNUSED);
    if (self->lpcon.cancel)
        {
        self->lpcon.cancel=FALSE;
        self->lpcon.sstat=E_FILE_CANCEL;
        }
    if (self->lpcon.sstat<0)
        {
        CancelStringState(self);
        if (self->lpcon.sstat==(-1))
            self->lpcon.sstat=0;
        SignalCompletion(self,self->lpcon.sstat);
        }
    else if (!self->lpcon.strCount)
        ProcessNextCharacterInString(self);
    else
        NextGroundStep(self);
    return(P_SIGNAL_ENABLE);
    }

LOCAL_C VOID LogOff(PR_LPCON *self)
    {
    if (self->lpcon.pid)
        {
        p_sveccall(self->lpcon.whandler,FALSE);
        if (!p_logoffa(self->lpcon.pid));
            p_waitstat(&self->lpcon.lstat);
        self->lpcon.pid=0;
        }
    }

LOCAL_C VOID LogOn(PR_LPCON *self)
    {
    INT ret;

    ret=p_logona(self->lpcon.pid,&self->lpcon.lstat);
    if (ret)
        {
        self->lpcon.pid=0;
        f_leave(ret);
        }
    p_sveccall(self->lpcon.whandler,TRUE);
    }

LOCAL_C INT fSendMessage(PR_LPCON *self,INT type,ATS_MESS_BODY *pu)
    {
    return(f_leave(SendMessage(self,type,pu)));
    }

LOCAL_C VOID SendEscapeKeyWait(PR_LPCON *self)
    {
    ATS_MESS_BODY u;
    INT ret;

    u.k.mod=0;
    u.k.key=W_KEY_ESCAPE;
    self->lpcon.pMstat=(&self->lpcon.mstat);
    ret=SendMessage(self,TY_ATS_KEY,&u);
    if (self->lpcon.pUstat)
        {
        p_waitstat(self->lpcon.pMstat);
        ret=self->lpcon.mstat;
        }   /* in !pUstat case, waits inside SendMessage */
    ResetPMstat(self);
    f_leave(ret);
    }

LOCAL_C VOID CopyTo(PR_LPCON *self,VOID *trg,VOID *src,UINT len)
    {
    f_leave(p_pcpyto(self->lpcon.pid,trg,src,len));
    }

LOCAL_C VOID CopyFrom(PR_LPCON *self,VOID *src,VOID *trg,UINT len)
    {
    f_leave(p_pcpyfr(self->lpcon.pid,src,trg,len));
    }

LOCAL_C INT ExecImg(PR_LPCON *self,TEXT *pb)
    {
    return(f_leave(p_execc(pb,self->lpcon.pStore,self->lpcon.storeLen)));
    }

LOCAL_C VOID DoAttach(PR_LPCON *self,INT pid)
    {
    p_send3(self,O_LPC_TEST_PID,pid);
    p_send3(self,O_LPC_SET_PID,pid);
    }

LOCAL_C INT GetTwoValues(TEXT *pb,WORD *pval)
    {
    WORD val;

    val=0;
    *pval=0;
    p_stoi(&pb,&val);
    if (*pb)
        {
        pb++;
        pb=p_skipwh(pb);
        p_stoi(&pb,pval);
        }
    return(val);
    }

LOCAL_C INT Action(PR_LPCON *self,INT method,VOID *p1,VOID *p2)
    {
    INT ret;

    ret=p_entersend4(self,method,p1,p2);
    if (self->lpcon.pUstat)
        {
        *self->lpcon.pUstat=ret;
        p_iosignal();
        return(0);
        }
    return(ret);
    }

LOCAL_C INT IoSemCount(PR_LPCON *self)
    {
    E_PROC epc;
    WORD sem[3];

    p_getosd(&epc,(VOID *)(E_PIDMASK&(self->lpcon.pid)),sizeof(E_PROC));
    p_getosd(&sem[0],(VOID *)epc.semaphore,6);
    return(sem[2]);
    }

LOCAL_C INT AmStartCount(PR_LPCON *self)
    {
    PR_APPMAN *wam;
    WORD start;

    CopyFrom(self,&w_am,&wam,2);
    CopyFrom(self,&wam->appman.stop,&start,2);
    return(start);
    }

LOCAL_C INT StartStringProcessing(PR_LPCON *self,UBYTE *pstr,INT count)
    {
    *self->lpcon.pMstat=E_FILE_PENDING;
    self->lpcon.pMstat=(&self->lpcon.sstat);
    self->lpcon.strCount=count;
    self->lpcon.pstr=pstr;
    SignalCompletion(self,0);   /* kick string processing into life */
    if (!self->lpcon.pUstat)
        {
        p_waitstat(&self->lpcon.mstat);
        return(f_leave(self->lpcon.mstat));
        }
    return(0);
    }

LOCAL_C INT WaitIfSynchronous(PR_LPCON *self)
    {
    if (self->lpcon.pUstat)
        return(0);
    p_waitstat(self->lpcon.pMstat);
    return(f_leave(self->lpcon.mstat));
    }

LOCAL_C VOID fCheckExist(TEXT *pb)
    {
    P_INFO junk;

    f_leave(p_finfo(pb,&junk));
    }

LOCAL_C INT ForegroundClientPid(VOID)
    {
    UWORD pids[WS_MAX_CLIENTS+1];

    wGetProcessList(&pids[0]);
    return(pids[0]);
    }

#pragma ENTER_CALL

LOCAL_C INT TestUnlockedGetWws(PR_LPCON *self,UWORD *pwws)
    {
    WORD count;

    CopyFrom(self,&DatLocked,&count,2);
    if (count)
        p_leave(E_GEN_INUSE);
    CopyFrom(self,&w_ws,pwws,2);
    return(0);
    }

#pragma METHOD_CALL

GLREF_C VOID root_destroy(VOID *self);

METHOD VOID lpcon_lpc_destroy(PR_LPCON *self)
    {
    LogOff(self);
    if (self->lpcon.whandler)
        p_svecrem(self->lpcon.whandler);
    p_free(self->lpcon.pStore);
    root_destroy(self);
    }

METHOD INT lpcon_lpc_init(PR_LPCON *self,WORD *pUstat)
    {
    self->lpcon.pUstat=pUstat;
    ResetPMstat(self);
    self->lpcon.whandler=f_leave(p_svecadd((VOID *)WaitHandler,self));
    return(0);
    }

METHOD INT lpcon_lpc_beep(PR_LPCON *self,INT v1,INT v2)
    {
    p_sound(v1,v2);
    return(0);
    }

METHOD INT lpcon_lpc_set_pid(PR_LPCON *self,INT pid)
    {
    if (pid!=self->lpcon.pid)
        {
        LogOff(self);
        self->lpcon.pid=pid;
        LogOn(self);
        }
    return(0);
    }

METHOD INT lpcon_lpc_sense_pid(PR_LPCON *self)
    {
    return(self->lpcon.pid);
    }

METHOD INT lpcon_lpc_sense_name(PR_LPCON *self,TEXT *pb,TEXT *pfmt)
    {
    TEXT *pfmt2;
    TEXT *pb2;
    INT ind;
    INT val;
    VOID **par;
    TEXT buf[P_FNAMESIZE];
    P_FPARSE crk;

    buf[0]=0;
    pb2=(&buf[0]);
    pfmt2=pfmt;
    FOREVER
        {
        ind=p_sloc(pfmt2,'%');
        if (ind<0)
            p_leave(E_GEN_ARG);
        pfmt2+=ind+1;
        val=p_toupper(*pfmt2);
        if (val=='S')
            par=(&DatUsedPathNamePtr);
        else if (val=='F')
            par=(&DatProcessNamePtr);
        else if (val=='P')
            par=(&DatCommandPtr);
        else if (val==0)
            p_leave(E_GEN_ARG);
        else
            continue;
        pb=(TEXT *)p_bcpy(pb,pfmt,pfmt2-pfmt-1);
        f_leave(p_piscpyfr(self->lpcon.pid,par,pb2,P_FNAMESIZE));
        f_fparse(pb2,NULL,pb2,&crk);
        pb2+=crk.system+crk.device+crk.path;
        pb=(TEXT *)p_bcpy(pb,pb2,crk.name);
        p_scpy(pb,pfmt2+1);
        break;
        }
    return(0);
    }

METHOD INT lpcon_lpc_store_pid(PR_LPCON *self)
    {
    self->lpcon.vpid=self->lpcon.pid;
    return(0);
    }

METHOD INT lpcon_lpc_restore_pid(PR_LPCON *self)
    {
    return(p_send3(self,O_LPC_SET_PID,self->lpcon.vpid));
    }

METHOD INT lpcon_lpc_test_pid(PR_LPCON *self,INT pid)
    {
    PR_GATE *gate;
    UWORD atson;

    if (!pid)
        pid=ForegroundClientPid();
    f_leave(p_pcpyfr(pid,&DatGate,&gate,2));
    if (!gate)
        p_leave(E_GEN_NSUP);
    f_leave(p_pcpyfr(pid,&gate->gate.atson,&atson,2));
    if (atson!=1)
        p_leave(E_GEN_NSUP);
    return(0);
    }

METHOD INT lpcon_lpc_attach(PR_LPCON *self,TEXT *pb)
    {
    INT pid;

    pid=f_leave(p_pidfind(pb));
    DoAttach(self,pid);
    return(pid);
    }

METHOD INT lpcon_lpc_attach_fname(PR_LPCON *self,TEXT *pb)
    {
    TEXT buf[P_FNAMESIZE];
    TEXT remBuf[P_FNAMESIZE];
    TEXT bb[E_MAX_NAME+2];
    TEXT star[2];
    P_FPARSE crk;
    INT pid;
    INT off;
    VOID **par;
    TEXT *pb2;

    pb2=(&buf[0]);
    f_fparse(pb,NULL,pb2,&crk);
    off=p_sloc(pb,':');
    if (off<0)
        off=p_sloc(pb,'\\');
    par=(&DatUsedPathNamePtr);
    if (off<0)
        {   /* "short form" of name */
        par=(&DatStatusNamePtr);
        pb2+=crk.system+crk.device+crk.path;
        }
    pid=0;
    *(UWORD *)(&star[0])='*';
    FOREVER
        {
        pid=f_leave(p_pfind(pid,&star[0],&bb[0]));
        if (p_piscpyfr(pid,par,&remBuf[0],P_FNAMESIZE)<0)
            continue;
        if (!p_scmpi(&remBuf[0],pb2))
            break;
        }
    DoAttach(self,pid);
    return(pid);
    }

METHOD INT lpcon_lpc_kill(PR_LPCON *self,INT reason)
    {
    INT pid;

    pid=self->lpcon.pid;
    LogOff(self);
    return(f_leave(p_pkill(pid,reason)));
    }

METHOD INT lpcon_lpc_terminate(PR_LPCON *self,INT reason)
    {
    INT pid;

    pid=self->lpcon.pid;
    LogOff(self);
    return(f_leave(p_pterminate(pid,reason)));
    }

METHOD INT lpcon_lpc_zap(PR_LPCON *self,TEXT *pb)
    {
    TEXT bb[E_MAX_NAME+2];
    HANDLE h;

    FOREVER
        {
        h=p_pfind(0,pb,&bb[0]);
        if (h<0)
            break;
        p_pkill(h,0);
        }
    return(0);
    }

METHOD INT lpcon_lpc_dialog(PR_LPCON *self,LPC_DIALOG_DATA *data)
    {
    ATS_MESS_BODY u;

    p_bcpy(&u.d,data,sizeof(ATS_DIAL_DEF));
    return(fSendMessage(self,TY_ATS_DIALOG,&u));
    }

METHOD INT lpcon_lpc_client_pos(PR_LPCON *self,INT pos)
    {
    ATS_MESS_BODY u;

    u.position=pos;
    return(fSendMessage(self,TY_ATS_CLIENT_POS,&u));
    }

METHOD INT lpcon_lpc_pause(PR_LPCON *self,INT delay)
    {
    ATS_MESS_BODY u;

    u.delay=delay;
    return(fSendMessage(self,TY_ATS_PAUSE,&u));
    }

METHOD INT lpcon_lpc_message(PR_LPCON *self,TEXT *pb)
    {
    ATS_MESS_BODY u;

    u.offs=pb;
    return(fSendMessage(self,TY_ATS_MESSAGE,&u));
    }

METHOD INT lpcon_lpc_allcount(PR_LPCON *self)
    {
    ATS_MESS_BODY u;

    return(fSendMessage(self,TY_ATS_ALLCOUNT,&u));
    }

METHOD INT lpcon_lpc_self_check(PR_LPCON *self,INT par)
    {
    ATS_MESS_BODY u;

    u.par=par;
    return(fSendMessage(self,TY_ATS_SELF_CHECK,&u));
    }

METHOD INT lpcon_lpc_key(PR_LPCON *self,INT key,INT mod)
    {
    ATS_MESS_BODY u;

    u.k.key=key;
    u.k.mod=mod;
    return(fSendMessage(self,TY_ATS_KEY,&u));
    }

METHOD INT lpcon_lpc_record(PR_LPCON *self,INT state)
    {
    ATS_MESS_BODY u;

    u.par=state;
    return(fSendMessage(self,TY_ATS_RECORD,&u));
    }

METHOD INT lpcon_lpc_get_key(PR_LPCON *self,LPC_GET_KEY *pkey)
    {
    ATS_MESS_BODY u;
    
    u.offs=pkey;
    return(fSendMessage(self,TY_ATS_GET_KEY,&u));
    }

METHOD INT lpcon_lpc_string(PR_LPCON *self,TEXT *pb)
    {
    return(StartStringProcessing(self,(UBYTE *)pb,0));
    }

METHOD INT lpcon_lpc_store(PR_LPCON *self,TEXT *pb)
    {
    UWORD len;

    len=p_slen(pb);
    self->lpcon.pStore=f_realloc(self->lpcon.pStore,len);
    self->lpcon.storeLen=len;
    p_bcpy(self->lpcon.pStore,pb,len);
    return(0);
    }

METHOD INT lpcon_lpc_convert(PR_LPCON *self,INT hash)
    {
    UBYTE *pb;
    UBYTE *pbend;

    pb=self->lpcon.pStore;
    for (pbend=pb+self->lpcon.storeLen; pb<pbend; pb++)
        {
        if (*pb==hash)
            *pb=0;
        }
    return(0);
    }

METHOD INT lpcon_lpc_wait(PR_LPCON *self)
    {
    f_leave(p_logona(self->lpcon.ipid,self->lpcon.pMstat));
    return(WaitIfSynchronous(self));
    }

METHOD INT lpcon_lpc_exec_img(PR_LPCON *self,TEXT *pb)
    {
    TEXT buf[P_FNAMESIZE];

    f_fparse(pb,DatCommandPtr,&buf[0],NULL);
    self->lpcon.ipid=ExecImg(self,&buf[0]);
    return(f_leave(p_presume(self->lpcon.ipid)));
    }

METHOD INT lpcon_lpc_exec_opl(PR_LPCON *self,TEXT *progName)
    {
    INT len;
    TEXT comLine[128];
    TEXT opoName[P_FNAMESIZE];
    TEXT appName[LPCDAT_SYSPRGO_NAME_LEN];

    f_fparse(progName,DatCommandPtr,&opoName[0],NULL);
    fCheckExist(&opoName[0]);
    len=p_slen(&opoName[0])+1;
    if (len+LPCDAT_OPL_COMLINE_LEN>128)
        return(E_GEN_OVER);
    p_ccpy(&comLine[0],(UBYTE *)OplComline,LPCDAT_OPL_COMLINE_LEN);
    p_bcpy(&comLine[LPCDAT_OPL_COMLINE_LEN],&opoName[0],len);
    p_ccpy(&appName[0],(UBYTE *)Sys$prgoName,LPCDAT_SYSPRGO_NAME_LEN);
    self->lpcon.ipid=f_leave
            (p_execc(&appName[0],&comLine[0],len+LPCDAT_OPL_COMLINE_LEN));
    return(f_leave(p_presume(self->lpcon.ipid)));
    }

METHOD INT lpcon_lpc_exec(PR_LPCON *self,TEXT *pb)
    {
    INT pid;

    pid=ExecImg(self,pb);
    p_send3(self,O_LPC_SET_PID,pid);
    *self->lpcon.pMstat=E_FILE_PENDING;
    p_pcpyto(pid,&w_ws,&self->lpcon.pMstat,2);
    p_presume(pid);
    return(WaitIfSynchronous(self));
    }

METHOD INT lpcon_lpc_ground_state(PR_LPCON *self)
    {
    INT ret;
    UWORD wws;

    ret=p_enter3(TestUnlockedGetWws,self,&wws);
    if (ret)
        {
        if (self->lpcon.pUstat)
            {
            SignalCompletion(self,ret);
            return(0);
            }
        return(ret);
        }
    return(StartStringProcessing(self,(UBYTE *)wws,1));
    }

METHOD INT lpcon_lpc_process_info(PR_LPCON *self)
    {
    INT flags;
    WORD locked;
    PR_WSERV *wws;
    VOID *win;

    flags=0;
    CopyFrom(self,&DatLocked,&locked,2);
    if (locked)
        flags=LPC_APP_LOCKED;
    CopyFrom(self,&w_ws,&wws,2);
    CopyFrom(self,&wws->wserv.bar,&win,2);
    if (win)
        flags|=LPC_MENU_SHOWING;
    CopyFrom(self,&wws->wserv.dial,&win,2);
    if (win)
        flags|=LPC_DIALOG_SHOWING;
    CopyFrom(self,&wws->wserv.filter,&win,2);
    if (win)
        flags|=LPC_FILTER_USED;
    if (IoSemCount(self)>-1)
        flags|=LPC_IOSEM_ACTIVE;
    if (AmStartCount(self)>1)
        flags|=LPC_AM_STARTED;
    return(flags);
    }

METHOD INT lpcon_lpc_sense_iosem(PR_LPCON *self)
    {
    return(IoSemCount(self));
    }

METHOD INT lpcon_lpc_sense_amstart(PR_LPCON *self)
    {
    return(AmStartCount(self));
    }

METHOD INT lpcon_lpc_count_dialogs(PR_LPCON *self)
    {
    INT count;
    PR_WSERV *wws;
    PR_DLGCHAIN *dlg;
    PR_DLGCHAIN **pdlg;

    CopyFrom(self,&w_ws,&wws,2);
    pdlg=(&wws->wserv.dial);
    count=0;
    FOREVER
        {
        CopyFrom(self,pdlg,&dlg,2);
        if (!dlg)
            break;
        count++;
        pdlg=(&(PR_DLGCHAIN *)dlg->dlgchain.next);
        }
    return(count);
    }

METHOD INT lpcon_lpc_disp_menu(PR_LPCON *self,INT val1,INT val2)
    {
    PR_WSERV *wws;
    WSERV_INFO *info;
    UWORD packed;

    CopyFrom(self,&w_ws,&wws,2);
    CopyFrom(self,&wws->wserv.info,&info,2);
    packed=val1+(val2<<8);
    CopyTo(self,info,&packed,2);
    return(p_send4(self,O_LPC_KEY,W_KEY_MENU,0));
    }

#define MAX_SEND_LENGTH     127

METHOD INT lpcon_lpc_hook_dyl(PR_LPCON *self,TEXT *pb)
    {
    TEXT buf[P_FNAMESIZE+2];

    f_fparse(pb,DatCommandPtr,&buf[1],NULL);
    if (p_slen(&buf[1])>MAX_SEND_LENGTH)
        p_leave(E_GEN_OVER);
    fCheckExist(&buf[1]);
    buf[0]='L';     /* H_COMMAND_LAUNCH_DYL */
    return(wSendCommand(self->lpcon.pid,(UBYTE *)&buf[0],MAX_SEND_LENGTH));
    }

METHOD INT lpcon_lpc_closedown(PR_LPCON *self)
    {
    UBYTE buf[2];
    INT pid;
    WORD count;

    CopyFrom(self,&DatLocked,&count,2);
    if (count)
        p_leave(E_GEN_INUSE);
    pid=self->lpcon.pid;
    LogOff(self);
    buf[0]='X';     /* H_COMMAND_EXIT */
    return(wSendCommand(pid,&buf[0],1));
    }

METHOD INT lpcon_lpc_attach_fore(PR_LPCON *self)
    {
    INT pid;

    pid=ForegroundClientPid();
    DoAttach(self,pid);
    return(pid);
    }

METHOD INT lpcon_lpc_cancel(PR_LPCON *self)
    {
    if (self->lpcon.pstr)
        self->lpcon.cancel=TRUE;
    if (self->lpcon.ipid)
        p_pterminate(self->lpcon.ipid,0);
    return(0);
    }

METHOD INT lpcon_lpc_bring(PR_LPCON *self,TEXT *pb,INT maxlen)
    {
    TEXT wsrvName[12];
    INT wsrvPid;
    INT srvPid;
    ULONG fmt;
    ULONG *pfmt;
    INT ret;
    UWORD args[2];

    *pb=0;
    p_ccpy(&wsrvName[0],(UBYTE *)WservName,LPCDAT_WSERV_NAME_LEN);
    wsrvPid=p_pidfind(&wsrvName[0]);
    pfmt=(&fmt);
    srvPid=p_msendreceivew(wsrvPid,O_SY_LINK_PASTE,&pfmt);
    if (srvPid>0 && fmt&(1<<DF_LINK_TEXT))
        {
        args[0]=DF_LINK_TEXT;
        f_leave(p_msendreceivew(srvPid,TY_LINKSV_STEP,&args[0]));
        args[0]=(UWORD)pb;
        args[1]=maxlen;
        ret=f_leave(p_msendreceivew(srvPid,TY_LINKSV_STEP,&args[0]));
        *(pb+ret)=0;
        args[0]=0;
        p_msendreceivew(srvPid,TY_LINKSV_STEP,&args[0]);
        }
    return(0);
    }

METHOD INT lpcon_lpc_bring_send(PR_LPCON *self,INT maxlen)
    {
    TEXT buf[130];  /* room for zero terminator too */

    if (maxlen>128)
        p_leave(E_GEN_OVER);
    p_send4(self,O_LPC_BRING,&buf[0],maxlen);
    p_send3(self,O_LPC_STRING,&buf[0]);
    return(0);
    }

METHOD INT lpcon_lpc_name_send(PR_LPCON *self,TEXT *pfmt)
    {
    TEXT buf[P_FNAMESIZE];

    p_send4(self,O_LPC_SENSE_NAME,&buf[0],pfmt);
    p_send3(self,O_LPC_STRING,&buf[0]);
    return(0);
    }

METHOD INT lpcon_lpc_yield(PR_LPCON *self)
    {
    INT pid;
    INT prio;

    pid=p_getpid();
    prio=p_getpri(pid);
    p_setpri(pid,E_PRIORITY_BACK-1);
    p_setpri(pid,prio);
    return(0);
    }

METHOD INT lpcon_lpc_action(PR_LPCON *self,INT command,TEXT *pb)
    {
    WORD val,val2;

    switch (command)
        {
    case 'A':       /* alloc count in attached process */
        return(p_entersend2(self,O_LPC_ALLCOUNT));
    case 'B':       /* go to Background */
        return(p_entersend3(self,O_LPC_CLIENT_POS,100));
    case 'C':       /* Closedown message (as from shell) */
        return(Action(self,O_LPC_CLOSEDOWN,NULL,NULL));
    case 'E':       /* send Expression based on file names of process */
        return(p_entersend3(self,O_LPC_NAME_SEND,pb));
    case 'F':       /* come to Foreground */
        return(p_entersend3(self,O_LPC_CLIENT_POS,0));
    case 'G':       /* bring application to Ground state */
        return(p_entersend2(self,O_LPC_GROUND_STATE));
    case 'H':       /* application should Hook Dyl */
        return(Action(self,O_LPC_HOOK_DYL,pb,NULL));
    case 'I':       /* Info message */
        return(p_entersend3(self,O_LPC_MESSAGE,pb));
    case 'K':       /* single Key */
        val=GetTwoValues(pb,&val2);
        return(p_entersend4(self,O_LPC_KEY,val,val2));
    case 'L':       /* Link paste (and send) */
        val=128;
        p_stoi(&pb,&val);
        return(p_entersend3(self,O_LPC_BRING_SEND,val));
    case 'M':       /* display Menu at given position */
        val=GetTwoValues(pb,&val2);
        return(p_entersend4(self,O_LPC_DISP_MENU,val,val2));
    case 'P':       /* Pause */
        val=0;
        p_stoi(&pb,&val);
        return(p_entersend3(self,O_LPC_PAUSE,val));
    case 'S':       /* String of keys */
        return(p_entersend3(self,O_LPC_STRING,pb));
    case 'a':       /* attach to process */
        return(Action(self,O_LPC_ATTACH,pb,NULL));
    case 'b':       /* beep */
        val=GetTwoValues(pb,&val2);
        return(Action(self,O_LPC_BEEP,(VOID *)val,(VOID *)val2));
    case 'c':       /* prepare command line (or other stored line) */
        val=p_entersend3(self,O_LPC_STORE,pb);
        if (val)
            return(val);
        return(Action(self,O_LPC_CONVERT,(VOID *)'#',NULL));
    case 'f':       /* attach to foreground process */
        return(Action(self,O_LPC_ATTACH_FORE,NULL,NULL));
    case 'g':       /* go to process specified by filename */
        return(Action(self,O_LPC_ATTACH_FNAME,pb,NULL));
    case 'i':       /* execute program (without attaching to it) */
        return(Action(self,O_LPC_EXEC_IMG,pb,NULL));
    case 'k':       /* kill attached process */
        return(Action(self,O_LPC_KILL,0,NULL));
    case 'o':       /* start Opl program */
        return(Action(self,O_LPC_EXEC_OPL,pb,NULL));
    case 'p':       /* execute program (and attach to it) */
        return(p_entersend3(self,O_LPC_EXEC,pb));
    case 't':       /* terminate attached process */
        return(Action(self,O_LPC_TERMINATE,0,NULL));
    case 'u':       /* use pid stored earlier */
        return(Action(self,O_LPC_RESTORE_PID,NULL,NULL));
    case 'v':       /* store pid for later use */
        return(Action(self,O_LPC_STORE_PID,NULL,NULL));
    case 'w':       /* wait for completion of last image started */
        return(p_entersend2(self,O_LPC_WAIT));
    case 'y':       /* Yield CPU */
        return(Action(self,O_LPC_YIELD,NULL,NULL));
    case 'z':       /* zap all instances of named process */
        return(Action(self,O_LPC_ZAP,pb,NULL));
    default:
        return(E_GEN_ARG);
        }
    }
