Source Code

Project File EOE.PR

#system epoc img
#set epocinit=iplib
#model small jpi
#compile eoe.c
#compile tcom.c
#compile reply.c
#pragma link(hwif.lib)
#pragma link(misc.lib)
#link eoe
#include <hwif.h>
#include <p_file.h>
#include <p_gen.h>
#include <epoc.h>
#include <p_serial.h>
#include <p_date.h>
//#include <p_object.h> ///
//#include <rscfile.xg> ///

// This application uses Binary File IO with Random Access & Updates
#define bNew P_FSTREAM | P_FUPDATE | P_FRANDOM | P_FCREATE
#define bOld P_FSTREAM | P_FUPDATE | P_FRANDOM | P_FOPEN

#define A_MASK_HideSWin 0x01  // B0: Hide Status Window
#define A_MASK_Edit 0x02      // B1: Edit Mode (TAB pressed)
#define A_MASK_Connected 0x80 // B7: connected to host

#define C_MASK_AckOdd 0x01    // B0: Ack Odd/Even
#define C_MASK_RVI 0x02       // B1: request RVI (Receive state)
#define C_MASK_StopTx 0x04    // B2: supress Transmit state

#define L_MASK_ToFollow 1     // B0: Request To Follow if no stock
#define L_MASK_Cases 2        // B1: Quantity is in Cases

#define O_MASK_NoToFollow 1   // B0: No "To Follow" facility
#define O_MASK_CasesOnly 2    // B1: Case orders only

#define dX      7 // pixels between characters (cursor.width)
#define dY      8 // pixels between lines (font.height)
#define iMax    8 // max chars in Item Code
#define qMax    5 // max digits in Qty

#define pip 1,150
#define beep 6,320
#define rasp 3,5000
#define LinesPerBlock 10

// Record Layouts
typedef struct {
  UINT oLine1; // bytes offset to first Line Record (=128)
  UINT max; // maximum number of lines
  UINT lines; // current number of lines
  UINT state;
  ULONG stamp; // time sent
  UINT iblk; // length of RVI block
  UBYTE oblk; // length of outcome block
  TEXT fill1;
  TEXT cref[9]; // (BCS) customer's Order Reference
  TEXT host[31]; // (BCS) wholesaler's name
  UINT acode; // Access Code
  TEXT pword[6]; // (BCS) Password
  TEXT fill2[34];
  UINT undo;
  TEXT fill3;
  UBYTE opts; // wholesaler options (see O_MASKs)
  UINT unsend;
  unsigned int signal: 4; // 0=ring, 1=chime, 2=mute
  unsigned int zlead: 4; // zero lead-in for PIP Code
  UBYTE phone; // last dial selection
  UBYTE flags; // default LINE.flags
  UBYTE modem;
  UBYTE bps;
  TEXT dial[3][17]; // 3x (BCS) Host Phone No
} HEADER;

typedef struct {
  UINT qty; // quantity ordered
  UBYTE flags; // see L_MASKs
  UBYTE rblk; // length of reply block
  TEXT item[iMax]; // Item Code
} LINE;

typedef struct {
  TEXT ver[2];
  unsigned int class: 6;
  unsigned int type: 2; // 0=direct, 1=generic, 2=exclusive, 3=(spare)
  UWORD key;
} USER;

typedef struct {
  USER user;
  UWORD acode;
  UBYTE pword[5];
  TEXT cref[9];
} TxHEAD;

typedef struct {
  UWORD qty; // quantity ordered
  UBYTE bcd[4]; // item code & flags
} TxLINE;

typedef struct { // reply progress frame
  UWORD line; // line number in source
  TEXT flag;
  UWORD qshort;
  UBYTE description[44]; // max length including NUL
} RxLINE;

typedef struct { // reply trailer
  UWORD shorts; // lines short+1 (0=Stock not checked)
  UBYTE flag; // [EOT]
  UBYTE outcome[24];
} RxTAIL;

typedef struct {
  VOID *ccb;
  VOID *timer;
  VOID *dir;
  WORD rxstate; // Rx completion status
  WORD txstate; // Tx completion status
  UBYTE state; // see C_MASKs
  HEADER host;
  union {
    UBYTE data[256];
    P_CRC crc;
    TxHEAD head;
    TxLINE line[LinesPerBlock];
    RxLINE progress;
    RxTAIL tail;
  } frame;
} ASYNC;

typedef struct {
  UWORD state; // Application Status flags (see A_MASKs)
  LINE *pLine1;
  VOID *fcb;
  UINT MainW; // Id of Main Window
  W_CURSOR spot;
  INT Normal, Bold; // Graphics contexts
  P_RECT boxB; // Main Border
  P_RECT boxS; // ShowState window
  TEXT *pHost; // -> BCS Host
  LONG SeekTo;
  VOID (*HotKey)(INT);
} CTLBLK;

// Global references defined in libraries
GLREF_D TEXT *DatCommandPtr;
GLREF_D TEXT *DatUsedPathNamePtr; // points to dsname[0]
GLREF_D TEXT *DatStatusNamePtr;
GLREF_D UWORD DatLocked;
GLREF_D WSERV_SPEC *wserv_channel;
#pragma save, call(reg_param=>(bx,cx,dx,di,si),\
                   reg_saved=>(bx,cx,dx,si,di,EPOC_SAVE))
extern void wsScreenExt(P_EXTENT *); // missing from wlib.h
#pragma call(reg_param =>(di,bx,cx),reg_saved =>(bx,cx,dx,si,di,EPOC_SAVE))
extern void PackLn(void *,void *);
#pragma call(reg_param =>(di,si,cx),reg_saved =>(bx,cx,dx,si,di,EPOC_SAVE))
extern void CRC16(unsigned short int *,unsigned char *,unsigned int);
extern void MovePw(unsigned char *,unsigned char *);
extern int FormItm(TEXT *,LINE *); // format Item Code
extern int FormTxt(TEXT *,TEXT *); // format Compressed ZTS
#pragma restore

// Global references defined in EOE
GLREF_D CTLBLK m;
GLREF_D ASYNC *com;
GLREF_D HEADER head;
GLREF_D WMSG_KEY key; // keyboard event structure
GLREF_C INT SwitchIf(INT, TEXT *); // prepare for possible file switch
GLREF_C INT SwitchFile(INT); // execute file switch & layout screen
GLREF_C VOID AgeFile(VOID); // rename file & prepare next
GLREF_C VOID Char(VOID); // keyboard event handler
GLREF_C VOID Wait(VOID); // wait for event
GLREF_C VOID StowAll(VOID); //rewrite all lines
GLREF_C VOID ShowDone(TEXT *msg); // show Done Msg
GLREF_C VOID ShowState(TEXT *); // show Send State
GLREF_C VOID TileWindow(INT tile); // size & clear window
GLREF_C VOID Flash(INT); // debug utility

// Global references defined in TCOM
#pragma save, ENTER_CALL
GLREF_C INT Call(TEXT *);
#pragma restore
GLREF_C VOID Connect(VOID); // wait for connect to host

// Global references defined in REPLY
GLREF_C VOID ShowReply(VOID); // display Reply Window

C Module EOE.C

#include "oe.h"
//#include "eoe.h" ///
// V1.2  1 Apr 93: Reject Send if <6000 bytes memory available.
// V1.3 11 May 93: Reject [Enter] on unmodified edits.
// V1.4 14 Oct 94: Display correct free memory when >64K.
//      16 Nov 94: Terminate other apps if memory too low for Send.
#define vMax    6 // max order lines in viewer window

// S3 Screen Layout pixel coords
#define ColL    4 // x for start of Line column
#define ColV1  32 // x for first vertical line
#define ColV2  99 // x for second vertical line
#define ColQ0 101 // x for Quantity prefix
#define ColQ1 109 // x for Quantity start
#define ColQ2 144 // x for Quantity end
#define ColQ3 152 // x for Case start
#define ColQ4 186 // x for Case end
#define XEsc    0 // x for Send Escape msg
#define YEsc   75 // y for Send Escape msg baseline
#define XSend 144 // x for Send Status window split
#define YSend  18 // y for Send Status
#define Head    7 // y for Header row
#define Foot   77 // y for Footer row

// Global variables defined here
GLDEF_D TEXT ** _cmds;
GLDEF_D H_MENU_DATA * _mdata;
GLDEF_D CTLBLK m = {0,NULL,NULL};
GLDEF_D ASYNC *com = NULL;
GLDEF_D HEADER head;
GLDEF_D WMSG_KEY key; // keyboard event structure

// Static variables local to this module
LOCAL_D INT cmd1, cmd2, enable; // workspace
LOCAL_D INT y1; // bottom y for 1st View row (base+1)
LOCAL_D TEXT dsname[P_FNAMESIZE+1]; // current file spec in ZTS form
LOCAL_D VOID *newfcb, *scb = NULL, *rcb;
LOCAL_D WORD soundstate, keystate, keybusy=FALSE;
LOCAL_D P_RECT boxD = {XEsc+36+26+6,YEsc+1-dY,0,YEsc+1}; //Disconnect msg
LOCAL_D P_RECT boxE = {XEsc+36,YEsc-9,XEsc+36+26,YEsc-9+14}; // Send Esc
LOCAL_D P_RECT boxI = {ColV1+8,Foot+1-dY,ColV2-4,Foot+1} ; // Item Code
LOCAL_D P_RECT boxN = {ColL,Foot+1-dY,ColV1-1,Foot+1} ; // Next Line
LOCAL_D P_RECT boxQ = {ColQ1,Foot+1-dY,ColQ2,Foot+1} ; // Quantity
LOCAL_D P_RECT boxR = {68,Head+1-dY,68+64,Head+1}; // Customer Reference
LOCAL_D LINE *pThis, *pNext;
LOCAL_D INT base, this, next;
LOCAL_D UBYTE weight[iMax-1] = {3,5,4,6,8,0,0};
LOCAL_D TEXT tws[32]; // transient work space
LOCAL_D TEXT exit1[] = "Done with phone";
LOCAL_D TEXT exit2[] = "Nothing to send!";
LOCAL_D TEXT *exit[3] = {exit1, exit1, exit2};
LOCAL_D H_MENU_DATA mCard[]= { // Main Menu
  "File",6,"Edit",4,"Format",2,NULL
};
LOCAL_D TEXT *mList[]= { // Main Menu Presentation
  "sSend...","vView reply...","oOpen...","nNew...","fFree space","xExit",
  "rorder Ref...","aAccess...","dDelete all items...","",
  "zZero lead-in...","tauto To Follow...",
  NULL
};
LOCAL_D H_MENU_DATA rCard[]= { // Reply Menu
  "File",5,"Edit",1,NULL
};
LOCAL_D TEXT *rList[]= { // Reply Menu Presentation
  "oOpen...","nNew...","vView reply...","fFree space","xExit",
  "uUndo send...",
  NULL
};

// Procedure Prototypes local to this module
#pragma save, ENTER_CALL
LOCAL_C INT EOE(VOID);
LOCAL_C INT StartUp(VOID); // application initialization
#pragma restore
LOCAL_C INT ItemCode(VOID); // get Item Code
LOCAL_C INT CheckDigit(VOID); // verify Check Digit
LOCAL_C INT CheckLetter(INT); // verify Check Letter
LOCAL_C INT Qty(VOID); // get Quantity
LOCAL_C INT EditQty(VOID); // edit Quantity
LOCAL_C VOID InfoTF(LINE *); // inform about To Follow flag
LOCAL_C VOID View(VOID); // display order lines
LOCAL_C VOID ShowHost(VOID); // display current wholesaler's name
LOCAL_C VOID ShowTF(INT col, LINE *pLine); // display To Follow flag
LOCAL_C VOID ShowCase(INT col, LINE *pLine); // display Case flag
LOCAL_C VOID HotKeyM(INT); // Main Menu selection & HotKey handler
LOCAL_C VOID HotKeyR(INT); // Reply Menu selection & HotKey handler
LOCAL_C VOID Access(VOID); // define access to host
LOCAL_C VOID Void(VOID); // delete all lines
LOCAL_C VOID ChooseFile(INT); // File switching dialog handler
LOCAL_C VOID CRef(VOID); // edit Customer Reference
LOCAL_C VOID Send(VOID); // send order(s)
LOCAL_C VOID SwitchNewTF(VOID); // switch To Follow flag default
LOCAL_C VOID UnSend(VOID); // Undo Send
LOCAL_C VOID UnDo(VOID); // Undo last action
LOCAL_C VOID ZLeadIn(VOID); // set Zero lead-in for PIP Code
LOCAL_C VOID ButtonDance(P_RECT *, TEXT *);
LOCAL_C VOID User1(VOID); // Kybd exit proc (ItemCode & Qty)
LOCAL_C INT User2(VOID); // Kybd exit proc (EditQty)
LOCAL_C VOID Help(VOID); // Help system
LOCAL_C VOID ExecSysCmd(VOID); // S3 System Command handler
LOCAL_C VOID StowThis(VOID); // rewrite/append line
LOCAL_C VOID StowHead(VOID); // rewrite Header Record
LOCAL_C VOID UpCase(TEXT *); // convert BCS to upper case
LOCAL_C VOID Jingle(UWORD tune);
LOCAL_C ULONG FlashFree(VOID); // display free bytes on drive
//****************************************************************

GLDEF_C VOID main(VOID) {
INT outcome;

  for (outcome=p_enter1(StartUp); outcome==1; )
    outcome=p_enter1(EOE);
  p_exit(outcome);
} //******************************** END main

LOCAL_C INT EOE(VOID) {

  while (SwitchFile(head.state)) {ShowReply();}
  base=0; this=head.lines; next=head.lines+1; View();
  while(1) {
    pNext=m.pLine1+head.lines;
    pNext->flags=head.flags; pNext->rblk=0;
    gPrintBoxText(&boxN,m.spot.ascent,G_TEXT_ALIGN_RIGHT,0,
      tws,p_itob(tws,next)); // Line Number
    if (ItemCode() && !EditQty() && Qty()) { // add new line
      if ((p_toupper(key.keycode)=='C') || (head.opts & O_MASK_CasesOnly))
        pNext->flags|=L_MASK_Cases;
      head.lines=this=next++; StowThis(); View();
      m.pLine1=f_realloc(m.pLine1,next*sizeof(LINE));
    }
  }
} //******************************** END EOE

LOCAL_C INT ItemCode(VOID) {
INT i,len,zlead;

  if (m.state & A_MASK_Edit) return TRUE; // edit Qty
  len=0; m.spot.pos.y=Foot; m.spot.pos.x=boxI.tl.x;
  gClrRect(&boxI,G_TRMODE_CLR); // clear Item Code
  ShowTF(Foot+1,pNext);
  zlead=head.zlead; key.keycode='0';
  while(1) {
    if (len>=zlead) {zlead=0; Char();}
    if (key.keycode==W_KEY_DELETE_LEFT) { // backsace
      if (len) {
        len--; m.spot.pos.x-=dX;
        gPrintText(m.spot.pos.x,m.spot.pos.y,"  ",2);
        if (len==2) {
          m.spot.pos.x-=dX-1;
          gPrintText(m.spot.pos.x,m.spot.pos.y,"  ",2);
        }
        continue;
      }
    }
    else if (key.keycode==W_KEY_ESCAPE) return FALSE; // restart line
    else if ((key.keycode==W_KEY_TAB) && head.lines) {
      m.state|=A_MASK_Edit; return TRUE; // edit Qty
    }
    else if (len==7); // discard
    else if ((key.keycode>='0') && (key.keycode<='9')) { // insert
      gPrintText(m.spot.pos.x,m.spot.pos.y,(TEXT *)&key.keycode,1);
      len++; *(pNext->item+len)=key.keycode;
      m.spot.pos.x+=dX;
      if (len==3) {
        gPrintText(m.spot.pos.x,m.spot.pos.y,"-",1); m.spot.pos.x+=dX-1;
      }
      if (len<7) continue;
      *(pNext->item)='0';
      if(CheckDigit()==0) return TRUE;
    }
    else if (p_isalpha(key.keycode) && CheckLetter(len)) {
      len++; *(pNext->item+len)=p_toupper(key.keycode);
      gPrintBoxText(&boxI,m.spot.ascent,G_TEXT_ALIGN_RIGHT,0,
        pNext->item+1,len);
      i=iMax;
      do { // right justify Item Code
        *(pNext->item+(--i))=(len ? *(pNext->item+(len--)) : '0');
      } while(i);
      return TRUE;
    }
    User1();
  }
} //******************************** END ItemCode

LOCAL_C INT CheckDigit(VOID) {
INT i, j, w;

  i=j=w=0;
  do {
    if (j) {
      j++; w=(*(pNext->item+j)&0xf)<<1;
      if (w>9) w-=9;
    }
    j++; i+=w+(*(pNext->item+j)&0xf);
  } while (j<7);
  return i % 10;
} //******************************** END CheckDigit

LOCAL_C INT CheckLetter(INT j) {
INT i,w;

  for (i=w=0;j;j--,i++) w+=weight[i] * (*(pNext->item+j)&0xf);
  if (w && ((w % 26)+65 == p_toupper(key.keycode))) return TRUE;
  return FALSE;
} //******************************** END CheckLetter

LOCAL_C INT Qty(VOID) {
INT len, view;
TEXT sQty[qMax+1], *psQty;

  if (next>head.max) {
    wInfoMsg("Maximum number of lines reached");
    p_sound(beep); return 0;
  }
  len=0; *sQty='1';
  while(1) {
    m.spot.pos.x=ColQ2; view=len;
    if (!len) {m.spot.pos.x-=dX; view=1;}
    gPrintBoxText(&boxQ,m.spot.ascent,G_TEXT_ALIGN_RIGHT,0,sQty,view);
    Char();
    if (key.keycode==W_KEY_DELETE_LEFT) { // backsace
      if (len) len--;
      if (!len) *sQty='0';
      continue;
    }
    else if ((key.keycode==W_KEY_RETURN)
             || (p_toupper(key.keycode)=='C')) { // enter line
      *(sQty+view)=0; psQty=sQty;
      if (!p_stog(&psQty,&(UWORD)(pNext->qty),10)) break;
      len=0; *sQty='0'; // discard (qty too large)
    }
    else if (key.keycode==W_KEY_ESCAPE) { // restart line
      pNext->qty=0; break;
    }
    else if (len==qMax); // discard
    else if ((key.keycode>='0') && (key.keycode<='9')) { // insert
      *(sQty+len)=key.keycode;
      if (len || (key.keycode!='0')) len++;
      continue;
    }
    User1();
  }
  gClrRect(&boxQ,G_TRMODE_CLR); // clear Qty
  return pNext->qty;
} //******************************** END Qty

LOCAL_C INT EditQty(VOID) {
INT len, view;
P_RECT box;
TEXT sQty[qMax+1], *psQty;

  if (m.state & A_MASK_Edit) m.state&=~A_MASK_Edit; // edit 'this'
  else { // search for matching Item Code
    for (this=1, pThis=m.pLine1; this<next; this++, pThis++)
      if (!p_bcmp(pThis->item,iMax,pNext->item,iMax)) break;
    if (this>=next) return FALSE; // no match (new item code)
  }
  View(); gClrRect(&boxI,G_TRMODE_CLR); // clear Item Code
  pThis=m.pLine1-1+this;
  box.br.y=y1+(this-base-1)*dY; box.tl.y=box.br.y-dY;
  len=p_gtob(sQty,pThis->qty,10);
  m.spot.pos.y=box.br.y-1;
  enable=FALSE;
  while(1) {
    m.spot.pos.x=ColQ2; view=len;
    if (!len) {m.spot.pos.x-=dX; view=1;}
    box.tl.x=ColQ1; box.br.x=ColQ2;
    gPrintBoxText(&box,m.spot.ascent,G_TEXT_ALIGN_RIGHT,0,sQty,view);
    Char();
    switch (key.keycode) {
    case W_KEY_DELETE_LEFT: // backsace
      if (len) len--;
      enable=TRUE; break;
    case W_KEY_ESCAPE: // cancel
      View(); return TRUE;
    case '-':
      if ((!enable) && (pThis->qty>1)) {
        pThis->qty--;
        StowThis(); m.state|=A_MASK_Edit; return TRUE;
      }
      User2(); break;
    case '+':
      if ((!enable) && (pThis->qty<0xffff)) {
        pThis->qty++;
        StowThis(); m.state|=A_MASK_Edit; return TRUE;
      }
      User2(); break;
    default:
      if (enable && (len<qMax)
      && (key.keycode>='0') && (key.keycode<='9')) { // insert
        *(sQty+len)=key.keycode;
        if (len || (key.keycode!='0')) len++;
        break;
      }
      if (!User2()) break; // discard keystroke
      if (enable) { // update Qty
        *(sQty+view)=0; psQty=sQty;
        if (p_stog(&psQty,&(UWORD)(pThis->qty),10)) { // Qty too large
          len=p_gtob(sQty,pThis->qty,10);
          enable=FALSE; p_sound(beep); break; // discard
        }
        if (pThis->qty) StowThis();
        else { // delete line
          m.pLine1=p_adjust(m.pLine1,(this-1)*sizeof(LINE),-sizeof(LINE));
          if (m.pLine1==NULL) p_leave(E_GEN_NOMEMORY);
          --next; --head.lines; head.undo=head.unsend=0; StowAll();
          p_sound(pip); wInfoMsg("Line erased");
        }
      }
      if (!head.lines) m.state&=~A_MASK_Edit;
      if (m.state & A_MASK_Edit) {this=cmd1; base+=cmd2;}
      else View();
      return TRUE;
    }
  }
} //******************************** END EditQty

LOCAL_C VOID InfoTF(LINE *pLine) {

  if (pLine->flags & L_MASK_ToFollow) wInfoMsg("To follow if no stock");
  else wInfoMsg("Only if in stock");
} //******************************** END InfoTF

GLDEF_C INT SwitchIf(INT mode, TEXT *newname) {
INT outcome, ret;
HEADER newhead;
TEXT name[P_FNAMESIZE+1];

  do {
    if (mode=='S') { // send
      p_close(m.fcb); m.fcb=NULL;
      if (com->dir==NULL) f_open(&com->dir,newname,P_FDIR);
      outcome=p_iow(com->dir,P_FREAD,name,NULL);
      if (outcome==E_FILE_EOF) return outcome; // no more files
      f_leave(outcome);
      f_fparse(name,newname,name,NULL);
    }
    else p_scpy(name,newname);
    hEnsurePath(name);
    outcome=p_open(&newfcb,name,mode=='C'? bNew: bOld);
    if (!outcome) { // newfcb is open
      ret=p_read(newfcb,&newhead,sizeof(HEADER));
      if ((ret==E_FILE_EOF) && (mode=='C')) { // end of (empty) file
        newhead=head;
        newhead.oLine1=sizeof(HEADER);
        newhead.lines=newhead.state=newhead.iblk=*newhead.cref=0;
        if (!newhead.max) newhead.max=200;
        f_write(newfcb,&newhead,sizeof(HEADER));
        ret=sizeof(HEADER);
      }
      if ((ret!=sizeof(HEADER))
      || (newhead.oLine1!=sizeof(HEADER))
      || ((mode=='S') && ((newhead.lines==0) || newhead.state))) {
        outcome=E_FILE_INVALID; p_close(newfcb);
      }
      else { // (file switch is now committed)
        head=newhead;
        p_scpy(dsname,name);
        p_close(m.fcb); m.fcb=newfcb;
        return outcome;
      }
    }
  } while (mode=='S'); // skip invalid files during Send
  return (mode=='M')? outcome: uErrorValue(outcome);
} //******************************** END SwitchIf

GLDEF_C INT SwitchFile(INT layout) {
UINT mem;
TEXT string[32];

  p_free(m.pLine1); m.pLine1=NULL;
  mem=head.lines*sizeof(LINE);
  hSetUpStatusNames(dsname); // notify S3 that file has changed
  wsUpdate(WS_UPDATE_NAME); // update name in Status Window
  switch (layout) {
  case -1: // layout Send Window & connect to host
    if (!(m.state & A_MASK_Connected)) {
      m.boxS.tl.x=m.boxB.tl.x; m.boxS.br.x=m.boxB.br.x;
      m.boxS.tl.y=YSend-dY; m.boxS.br.y=YSend;
      Connect();
    }
    p_atos(string,"File %s (%u lines):",DatStatusNamePtr,head.lines);
    m.boxS.tl.x=m.boxB.tl.x; m.boxS.br.x=XSend; ShowState(string);
    m.boxS.tl.x=XSend; m.boxS.br.x=m.boxB.br.x; ShowState("Trying to log on");
    break;
  case 0: // layout Main Window
    m.HotKey=HotKeyM; _cmds=mList; _mdata=mCard;
    mCard[2].nlines=(head.opts & O_MASK_NoToFollow)? 1: 2;
    mem+=sizeof(LINE);
    gSetGC0(m.Bold);
    TileWindow(TRUE); // clear Tile Window
    m.boxB.tl.y=Head+1;
    gBorderRect(&m.boxB,W_BORD_SHADOW_S | W_BORD_SHADOW_ON);
    gPrintText(ColL,Head,"Order Ref:",10);
    gPrintText(ColL,17,"Line   Item Code         Qty",28);
    gDrawLine(1,18,m.boxB.br.x,18);
    gDrawLine(1,Foot-dY-1,m.boxB.br.x,Foot-dY-1);
    gDrawLine(ColV1,Head+2,ColV1,m.boxB.br.y);
    gDrawLine(ColV2,Head+2,ColV2,m.boxB.br.y);
    gPrintText(68,Head,head.cref+1,*head.cref); // Customer reference
    break;
  default: // layout Reply Window
    m.HotKey=HotKeyR; _cmds=rList; _mdata=rCard;
    mem+=head.iblk+head.oblk+sizeof(UWORD); // (UWORD for shorts & ZTS)
    gSetGC0(m.Normal);
    TileWindow(FALSE); // clear Full Window
    m.boxB.tl.y=Head+1+dY+2; m.boxB.br.y-=dY+1;
    gBorderRect(&m.boxB,W_BORD_SHADOW_S | W_BORD_SHADOW_ON);
    gDrawLine(1,30,m.boxB.br.x,30);
  }
  m.pLine1=f_alloc(mem);
  if (head.lines) { // fetch all lines +iblk +(maybe)oblk
    m.SeekTo=head.oLine1; f_seek(m.fcb,P_FABS,&m.SeekTo);
    f_read(m.fcb,m.pLine1,mem);
  }
  m.pHost=(head.state)? (TEXT *)(m.pLine1+head.lines): head.host;
  ShowHost();
  return head.state;
} //******************************** END SwitchFile

GLDEF_C VOID AgeFile(VOID) {
TEXT newname[P_FNAMESIZE+1];

  StowAll();
  p_close(m.fcb); m.fcb=NULL;
  if (!head.state) return;
  f_fparse(".BAK",dsname,newname,NULL);
  if (p_scmp(dsname,newname)) {
    head.state=0; // forestall recall if ageing fails
    p_close(com->dir); com->dir=NULL;
    p_delete(newname);
    f_leave(p_rename(dsname,newname));
    f_leave(SwitchIf('C',dsname));
  }
} //******************************** END AgeFile

LOCAL_C VOID View(VOID) {
INT rows,i;
LINE *pLine;
P_RECT box; // Next Line
TEXT flag, string[iMax];

  if (this < 1) this=1;
  if (this > head.lines) this=head.lines;
  rows=head.lines;
  if (rows > vMax) { // full screen
    if (base < 0) base=0;
    else if (base > rows-vMax) base=rows-vMax;
    rows=vMax;
    if (this <= base) base=this-1;
    else if (this > base+vMax) base=this-vMax;
  }
  else { // partial screen
    base=0;
    if (rows<vMax) {
      box.br.y=Foot-1-(rows+1)*dY; box.tl.y=Foot-1-(vMax+1)*dY;
      box.br.x=boxN.br.x; box.tl.x=boxN.tl.x; gClrRect(&box,G_TRMODE_CLR);
      box.br.x=boxI.br.x; box.tl.x=boxI.tl.x; gClrRect(&box,G_TRMODE_CLR);
      box.br.x=ColQ4; box.tl.x=ColQ0; gClrRect(&box,G_TRMODE_CLR);
    }
  }
  y1=Foot-1-rows*dY;
  for (i=base, box.br.y=y1; rows; rows--, i++, box.br.y+=dY) {
    box.tl.y=box.br.y-dY;
    box.tl.x=boxN.tl.x; box.br.x=boxN.br.x;
    gPrintBoxText(&box,m.spot.ascent,G_TEXT_ALIGN_RIGHT,0,
      tws,p_itob(tws,i+1)); // Line number
    pLine=m.pLine1+i;
    box.tl.x=boxI.tl.x; box.br.x=boxI.br.x;
    gPrintBoxText(&box,m.spot.ascent,G_TEXT_ALIGN_RIGHT,0,
      string,FormItm(string,pLine)); // Item Code
    flag=(pLine->flags & L_MASK_ToFollow)? '*': ' ';
    box.tl.x=ColQ0; box.br.x=ColQ1;
    gPrintBoxText(&box,m.spot.ascent,G_TEXT_ALIGN_LEFT,0,&flag,1); // Flag
    box.tl.x=ColQ1; box.br.x=ColQ2;
    gPrintBoxText(&box,m.spot.ascent,G_TEXT_ALIGN_RIGHT,0,
      tws,p_gtob(tws,pLine->qty,10)); // Quantity
    ShowCase(box.br.y,pLine); // Case
  }
} //******************************** END View

LOCAL_C VOID ShowHost(VOID) {
TEXT string[40];

  *p_bcpy(p_scpy(string,(head.state)? "From: ": "To: "),
    m.pHost+1,*m.pHost)=0;
  wInfoMsg(string);
} //******************************** END ShowHost

LOCAL_C VOID ShowTF(INT col, LINE *pLine) {
TEXT flag;
P_RECT box;

  flag=(pLine->flags & L_MASK_ToFollow)? '*': ' ';
  box.br.y=col; box.tl.y=col-dY;
  box.tl.x=ColQ0; box.br.x=ColQ1;
  gPrintBoxText(&box,m.spot.ascent,G_TEXT_ALIGN_LEFT,0,&flag,1);
} //******************************** END ShowTF

LOCAL_C VOID ShowCase(INT col, LINE *pLine) {
UINT len=0;
TEXT *pCase="";
P_RECT box;

  if (pLine->flags & L_MASK_Cases)
    if (pLine->qty > 1) {pCase="Cases"; len=5;}
    else {pCase="Case"; len=4;}
  box.br.y=col; box.tl.y=col-dY;
  box.tl.x=ColQ3; box.br.x=ColQ4;
  gPrintBoxText(&box,m.spot.ascent,G_TEXT_ALIGN_RIGHT,0,pCase,len);
} //******************************** END ShowCase

LOCAL_C VOID HotKeyM(INT keycode) {

  switch (keycode) {
  case 'a': // Access
    Access(); break;
  case 't': // To follow flag
    SwitchNewTF(); break;
  case 'd': // Delete all items
    Void(); break;
  case 'f':
    FlashFree(); break;
  case 'n': // New
    ChooseFile('C'); break;
  case 'o': // Open
    ChooseFile('O'); break;
  case 'r': // order Reference
    CRef(); break;
  case 's': // Send
    Send(); break;
  case 'u': // Undo
    UnDo(); break;
  case W_KEY_MODE:
  case 'v': // View reply
    ChooseFile('V'); break;
  case 'x':
    p_leave(0);
  case 'z':
    ZLeadIn(); break;
  }
} //******************************** END HotKeyM

LOCAL_C VOID HotKeyR(INT keycode) {

  switch (keycode) {
  case 'f':
    FlashFree(); break;
  case W_KEY_MODE:
  case 'o': // Open
    ChooseFile('O'); break;
  case 'n': // New
    ChooseFile('C'); break;
  case 'u': // Undo send
    UnSend(); break;
  case 'v': // View reply
    ChooseFile('V'); break;
  case 'x':
    p_leave(0);
  }
} //******************************** END HotKeyR

LOCAL_C VOID Access(VOID) {
INT i;
UWORD modem, bps, backord, cases;
LONG Max, ACode;
TEXT title[10+sizeof(head.host)], PWord[9];
H_DI_EDIT phone[3], host;
H_DI_TEXT etc;
H_DI_NUMBER max, acode;
H_DI_XINPUT pword;

  *p_bcpy(p_bcpy(title,"Access to ",10),head.host+1,*head.host)=0;
  if (uOpenDialog(title)) return;
  modem=head.modem+1; bps=head.bps+1;
  for (i=0;i<3;i++) {
    phone[i].str=head.dial[i]; phone[i].len=sizeof(head.dial[0])-1;
    if (uAddDialogItem(H_DIALOG_EDIT,"Phone No",phone+i)) return;
  }
  etc.type=H_DTEXT_SELECTABLE; etc.str="\xAPIN etc...";
  if (uAddChoiceList("Modem",&modem,
    "Hayes (Tone dial)",
    "Hayes (Pulse dial)",
    "Manual",
    "VX-543",
    NULL)
  || uAddChoiceList("Speed (bps)",&bps,
    "V21(300)",
    "V22(1200)",
    "V22bis(2400)",
    NULL)
  || uAddDialogItem(H_DIALOG_TEXT,NULL,&etc)) return;
  i=uRunDialog();
  if (i>0) { // did not escape or fail
    head.modem=modem-1; head.bps=bps-1;
    Max=head.max; ACode=head.acode;
    backord=(head.opts & O_MASK_NoToFollow)? 1: 2;
    cases=(head.opts & O_MASK_CasesOnly)? 2: 1;
    if (i==7) { // continue to second dialog screen
      host.str=head.host; host.len=sizeof(head.host)-1;
      max.value=&Max; max.low=1; max.high=254;
      acode.value=&ACode; acode.low=0L; acode.high=99999L;
      pword.str=PWord; *PWord=0;
      if (uOpenDialog("EOE \xB8 1994 J A Templeman. Version 1.4")
      || uAddDialogItem(H_DIALOG_EDIT,"Wholesaler",&host)
      || uAddDialogItem(H_DIALOG_NUMBER,"Max Lines",&max)
      || uAddChoiceList("Backorders",&backord,"No","Yes",NULL)
      || uAddChoiceList("Cases only",&cases,"No","Yes",NULL)
      || uAddDialogItem(H_DIALOG_NUMBER,"Access Code",&acode)
      || uAddDialogItem(H_DIALOG_XINPUT,"Password",&pword)) return;
      uRunDialog();
      if (*PWord==(sizeof(head.pword)-1)) MovePw(head.pword,PWord);
    }
    head.max=(UINT)Max; head.acode=(UINT)ACode;
    head.opts&=~(O_MASK_NoToFollow | O_MASK_CasesOnly);
    mCard[2].nlines=2;
    if (backord==1) {
      head.opts|=O_MASK_NoToFollow; mCard[2].nlines=1;
    }
    if (cases==2) head.opts|=O_MASK_CasesOnly;
    StowHead();
  }
} //******************************** END Access

LOCAL_C VOID Void(VOID) {
INT i;
TEXT string[32];
H_DI_TEXT note1;

  if (!head.lines) return;
  *p_bcpy(string+10+p_itob(p_bcpy(string,
    "Erase all ",10),head.lines)," lines?",7)=0;
  note1.type=H_DTEXT_ALIGN_CENTRE | H_DTEXT_UNDERLINE;
  note1.str="\x25(to erase one line, set its Qty to 0)";
  if (uOpenDialog(string)
  || uAddDialogItem(H_DIALOG_TEXT,NULL,&note1)
  || uAddButtonList("No",'n',"Yes",W_KEY_DELETE_LEFT,NULL)) return;
  i=uRunDialog();
  if ((i<=0) || (i!=W_KEY_DELETE_LEFT)) return;
  head.undo=head.lines; head.lines=0; StowHead(); p_leave(1);
} //******************************** END Void

LOCAL_C VOID ChooseFile(INT mode) {
TEXT dsname[P_FNAMESIZE+2];
H_DI_FSEL open;
H_DI_TEXT name;

  open.fname=dsname;
  f_fparse((mode=='V')? ".BAK": ".EOE",DatUsedPathNamePtr,dsname+1,NULL);
  *dsname=p_slen(dsname+1);
  if (key.keycode==W_KEY_MODE) {
    if (!SwitchIf('M',dsname+1)) p_leave(1);
    wInfoMsg("File not found"); return;
  }
  do {
    if (mode=='C') {
      if (uOpenDialog("Create new order file")) return;
      if (*head.host) {
        name.type=H_DTEXT_ALIGN_CENTRE | H_DTEXT_UNDERLINE;
        name.str=head.host;
        if (uAddDialogItem(H_DIALOG_TEXT,NULL,&name)) return;
      }
      open.flags=H_FILE_NEW_EDITOR |
        H_FILE_NO_AUTOQUERY | H_FILE_SET_DEFEXT;
    }
    else {
      if (uOpenDialog((mode=='V')?
        "View wholesaler's reply": "Reopen existing order file")) return;
      open.flags=H_FILE_PICK_SELECTOR |
        H_FILE_RESTRICT_LIST | H_FILE_SET_DEFEXT;
    }
    if (uAddDialogItem(H_DIALOG_FSEL,"File:",&open)
    || uRunDialog()<=0) return;
    dsname[1+*dsname]=0;
  } while(SwitchIf(mode,dsname+1));
  p_leave(1);  // switch files
} //******************************** END ChooseFile

LOCAL_C VOID CRef(VOID) {
H_DI_EDIT ref;

  ref.str=head.cref; ref.len=sizeof(head.cref)-1;
  if (uOpenDialog(NULL)
  || uAddDialogItem(H_DIALOG_EDIT,"This order reference",&ref)
  || uRunDialog()<=0) return;
  UpCase(head.cref); StowHead();
  gPrintBoxText(&boxR,m.spot.ascent,G_TEXT_ALIGN_LEFT,0,head.cref+1,*head.cref);
} //******************************** END CRef

LOCAL_C VOID Send(VOID) {
HANDLE h;
INT outcome;
UWORD phone, signal;
H_DI_TEXT note1;
H_DI_FSEL spec;
union {
  TEXT pName[E_MAX_NAME+2];
  TEXT title[8+sizeof(head.host)];
  TEXT oldname[P_FNAMESIZE+1];
} u1;
union {
  TEXT spec[P_FNAMESIZE+2];
  TEXT string[E_MAX_ERROR_TEXT_SIZE];
} u2;

  if (*head.pword!=(sizeof(head.pword)-1)) {
    wInfoMsg("Please complete setup"); Access(); return;
  }
  while (FlashFree()<6000L) {
    note1.type=H_DTEXT_ALIGN_CENTRE | H_DTEXT_UNDERLINE;
    note1.str="\35Close all other applications?";
    if (uOpenDialog("Send needs more system memory.")
    || uAddDialogItem(H_DIALOG_TEXT,NULL,&note1)
    || uAddButtonList("No",'n',"Yes",'y',NULL)
    || (uRunDialog()!='y')) return;
    for (h=0; (h=p_pfind(h,"*",u1.pName))>=0;) {
      if (p_bcmp(u1.pName,4,"SYS$",4) && p_bcmp(u1.pName,4,"TIME",4)
          && (h!=p_getpid())) {
        p_atos(u2.string,"Closing %s",u1.pName);
        wInfoMsg(u2.string);
        if (!p_pterminate(h,0)) h=0; // reset to 1st process
      }
    }
  }
  note1.type=H_DTEXT_ALIGN_CENTRE | H_DTEXT_BOLD;
  note1.str="\26Press Enter to connect";
  phone=head.phone+1; signal=head.signal+1;
  p_bcpy(u2.spec+1,dsname,*u2.spec=p_slen(dsname));
  spec.fname=u2.spec;
  spec.flags=H_FILE_NEW_EDITOR | H_FILE_SET_DEFEXT
    | H_FILE_CAN_WILDCARD | H_FILE_NO_AUTOQUERY;
  *p_bcpy(p_bcpy(u1.title,"Send to ",8),head.host+1,*head.host)=0;
  if (uOpenDialog(u1.title)
  || uAddDialogItem(H_DIALOG_TEXT,NULL,&note1)
  || uAddChoiceList("Dial",&phone,
    "1st \5 line","2nd \5 line","3rd \5 line","no autodial",NULL)
  || uAddDialogItem(H_DIALOG_FSEL,"File(s):",&spec)
  || uAddChoiceList("Signal at end",&signal,"Ring","Chime","Mute",NULL)
  || (uRunDialog()<=0)) return;
  u2.spec[1+*u2.spec]=0;
  p_scpy(u1.oldname,dsname); // save active file name
  head.phone=--phone; head.signal=--signal; StowHead();
  p_free(m.pLine1); m.pLine1=NULL;
  TileWindow(FALSE); // clear Full Window
  m.boxB.br.y=Head+1; boxD.br.x=m.boxB.br.x;
  gPrintBoxText(&m.boxB,m.spot.ascent,G_TEXT_ALIGN_CENTRE,0,
    head.host+1,*head.host);
  m.boxB.tl.y=YSend+1; m.boxB.br.y=YEsc-10;
  gBorderRect(&m.boxB,W_BORD_SHADOW_S | W_BORD_SHADOW_ON);
  gPrintText(XEsc,YEsc,"Press",5);
  wDrawButton(&boxE,"Esc",FALSE); ShowDone("to cancel order");

  outcome=p_enter2(Call,u2.spec+1);
  if (com==NULL) p_leave(outcome); // (out of memory)
  p_close(com->ccb); p_close(com->timer); wCancelBusyMsg();
  AgeFile();
  p_close(com->dir); p_free(com); com=NULL;

  f_leave(SwitchIf('O',u1.oldname)); // restore original active file
  if ((outcome==1) && !(m.state & A_MASK_Connected)) p_leave(1);
  m.state&=~A_MASK_Connected;
  if (outcome<0) p_errs(u2.string,outcome);
  else p_scpy(u2.string,exit[outcome]);
  gSetGC0(m.Bold); ShowDone(u2.string);
  switch (outcome) {
  case 1: // user escaped
  case 2: // nothing to send
  case E_FILE_LOCKED: // in use
  case E_FILE_DEVICE: // not plugged in
    break; // skip jingle
  default:
    Jingle(signal);
  }
  wFlush();
  while(1) {
    Wait();
    if (soundstate!=E_FILE_PENDING) {p_close(scb); scb=NULL;}
  }
} //******************************** END Send

LOCAL_C VOID SwitchNewTF(VOID) {
INT i;
H_DI_TEXT note1;

  if (head.opts & O_MASK_NoToFollow) return;
  note1.type=H_DTEXT_ALIGN_CENTRE | H_DTEXT_UNDERLINE;
  note1.str="\x25if wholesaler has insufficient stock?";
  if (uOpenDialog("Prefer new lines to follow automatically")
  || uAddDialogItem(H_DIALOG_TEXT,NULL,&note1)
  || uAddButtonList("No",'n',"Yes",'y',NULL)) return;
  i=uRunDialog(); if (i<=0) return; // escaped or failed
  head.flags&=~L_MASK_ToFollow; pNext->flags&=~L_MASK_ToFollow;
  if (i=='y') {
    head.flags|=L_MASK_ToFollow; pNext->flags|=L_MASK_ToFollow;
  }
  StowHead(); ShowTF(Foot+1,pNext); InfoTF(pNext);
} //******************************** END SwitchNewTF

LOCAL_C VOID UnDo(VOID) {

  if (head.undo) {
    head.lines=head.undo; head.undo=0;
    StowHead(); p_leave(1);
  }
  if (head.unsend) {
    head.state=head.unsend; head.unsend=0;
    StowHead(); p_leave(1);
  }
} //******************************** END UnDo

LOCAL_C VOID UnSend(VOID) {
H_DI_TEXT note1;

  note1.type=H_DTEXT_ALIGN_CENTRE | H_DTEXT_UNDERLINE;
  note1.str="\40for retransmission to wholesaler";
  if (uOpenDialog("\"Undo send\" restores this order")
  || uAddDialogItem(H_DIALOG_TEXT,NULL,&note1)
  || uAddButtonList("STOP",'n',"Undo Send",'y',NULL)
  || (uRunDialog()!='y')) return;
  head.unsend=head.state; head.state=0; StowHead(); p_leave(1);
} //******************************** END UnSend

LOCAL_C VOID ZLeadIn(VOID) {
LONG zlead;
H_DI_NUMBER lead;

  zlead=head.zlead; lead.value=&zlead; lead.low=0L; lead.high=4L;
  if (uOpenDialog(NULL)
  || uAddDialogItem(H_DIALOG_NUMBER,"How many PIP Code 0's preset?",&lead)
  || uRunDialog()<=0) return;
  head.zlead=(unsigned int)zlead; StowHead(); p_leave(1);
} //******************************** END ZLeadIn

GLDEF_C VOID Char(VOID) {

  while(1) {
    wTextCursor(m.MainW,&m.spot); // Cursor ON
    DatLocked--; Wait(); DatLocked++;
    wEraseTextCursor(); // Cursor OFF
    if (key.keycode & W_EVENT_KEY) { // Not a keypress
      if (key.keycode==CONS_EVENT_COMMAND) ExecSysCmd();
      else if ((key.keycode==CONS_EVENT_FOREGROUND)
        || (key.keycode==CONS_EVENT_ON_OFF))
        ShowHost();
    }
    else if (key.keycode & W_SPECIAL_KEY)
      m.HotKey(key.keycode & (~W_SPECIAL_KEY));
    else {
      switch (key.keycode) {
      case W_KEY_MODE:
        m.HotKey(key.keycode); break;
      case W_KEY_MENU:
        if (key.modifiers & W_CTRL_MODIFIER) // switch Status Window
          if ((m.state^=A_MASK_HideSWin) & A_MASK_HideSWin) wsDisable();
          else wsEnable();
        else {
          mList[9]=(head.undo)? "uUndo delete":
                   (head.unsend)? "uUndo \"undo send\"": "u(can't Undo)";
          m.HotKey(uPresentMenus());
        }
        break;
      default: return;
      }
    }
  }
} //******************************** END Char

GLDEF_C VOID Wait(VOID) {

  while(1) {
    if (keybusy) wFlush();
    else {keybusy=TRUE; uGetKeyA(&keystate,&key);} // start keyboard
    p_iowait();
    if (keystate!=E_FILE_PENDING) { // keystroke
      keybusy=FALSE;
      if (!DatLocked) return; // process keystroke
      if (key.keycode==W_KEY_ESCAPE) break;
    }
    else if (DatLocked) return; // process event
  }
  if (scb!=NULL) {p_close(scb); scb=NULL;} // abort jingle
  p_sound(pip); gSetGC0(m.Bold); ButtonDance(&boxE,"Esc");
  p_leave(1); // Esc pressed
} //******************************** END Wait

LOCAL_C VOID ButtonDance(P_RECT *box, TEXT *name) {

  wDrawButton(box,name,TRUE);
  wFlush(); p_sleep(1);
  wDrawButton(box,name,FALSE);
} //******************************** END ButtonDance

LOCAL_C VOID User1(VOID) {

  if (uKeyPressOutstanding()) return;
  switch (key.keycode) {
  case W_KEY_UP:
    base--; this--; break;
  case W_KEY_DOWN:
    base++; this++; break;
  case W_KEY_HOME:
    this=0; break;
  case W_KEY_END:
    this=next; break;
  case W_KEY_PAGE_UP:
    base-=vMax; this-=vMax; break;
  case W_KEY_PAGE_DOWN:
    base+=vMax; this+=vMax; break;
  case W_KEY_HELP:
    Help(); return;
  default:
    if ((key.keycode=='*') && !(head.opts & O_MASK_NoToFollow)) {
      pNext->flags^=L_MASK_ToFollow;
      InfoTF(pNext); ShowTF(Foot+1,pNext); break;
    }
    if (key.keycode<W_KEY_MODE) p_sound(beep);
    return; // reject keystroke
  }
  View();
} //******************************** END User1

LOCAL_C INT User2(VOID) {

  if (uKeyPressOutstanding()) return FALSE;
  cmd1=this; cmd2=0;
  switch (key.keycode) {
  case W_KEY_UP:
    if (key.modifiers & W_SHIFT_MODIFIER) cmd2=-1; // Scroll Lock
    cmd1--; break;
  case W_KEY_DOWN:
    if (key.modifiers & W_SHIFT_MODIFIER) cmd2=1; // Scroll Lock
    cmd1++; break;
  case W_KEY_HOME:
    cmd1=0; break;
  case W_KEY_END:
    cmd1=next; break;
  case W_KEY_PAGE_UP:
    cmd1-=vMax; cmd2=-vMax; break;
  case W_KEY_PAGE_DOWN:
    cmd1+=vMax; cmd2=vMax; break;
  case 'c': // switch Case flag
  case 'C':
    if (!(head.opts & O_MASK_CasesOnly)) pThis->flags^=L_MASK_Cases;
    enable=TRUE; break;
  case W_KEY_HELP:
    Help(); return FALSE;
  case W_KEY_TAB: // update
    return TRUE;
  case W_KEY_RETURN: // update
    if (enable) return TRUE;
  default:
    if ((key.keycode=='*') && !(head.opts & O_MASK_NoToFollow)) {
      pThis->flags^=L_MASK_ToFollow;
      InfoTF(pThis); enable=TRUE; break;
    }
    if (key.keycode<W_KEY_MODE) {
      if (!enable) wInfoMsg("Use +, -, Delete, \30, \31, Tab or Esc");
      p_sound(beep);
    }
    return FALSE; // reject keystroke
  }
  m.state|=A_MASK_Edit; return TRUE;
} //******************************** END User2

GLDEF_C VOID ShowDone(TEXT *msg) {

  gPrintBoxText(&boxD,m.spot.ascent,G_TEXT_ALIGN_LEFT,0,msg,p_slen(msg));
} //******************************** END ShowDone

GLDEF_C VOID ShowState(TEXT *msg) {

  gPrintBoxText(&m.boxS,m.spot.ascent,G_TEXT_ALIGN_CENTRE,0,msg,p_slen(msg));
} //******************************** END ShowState

LOCAL_C VOID Help(VOID) {
INT choice;

  choice=uDialogMenu("\30 \31 for help on:",
    "Keyboard",
    "Item Code",
    "Quantity",
    "Dialog boxes",
    "Sounds",
    "Sending",
    NULL);
  switch (choice) {
  case 2:
    uDisplayText("[Delete] backs cursor one space.",
      "[Esc] clears or cancels current action.",
      "[Tab] jumps cursor between new/alter line.",
      "[Menu] then \30 \31 \32 \33 select tools and show",
      "tool \"Hot Keys\" (eg: [ \2 ]+[X] to exit).",
      "\30 \31 PgUp PgDn Home End scroll order view.",
      NULL);
    break;
  case 3:
    uDisplayText("Item code is PIP Code (7 digits)",
      "or Wholesaler Code (upto 5 digits+letter).",
      "Enter the code on the bottom line: if it's",
      "already ordered its line is shown and",
      "may be amended or removed (see Qty help).",
      "[*] alters action preferred if wholesaler",
      "has insufficient stock.",
      NULL);
    break;
  case 4:
    uDisplayText("To set qty in new lines: after Item Code",
      "enter UNITS required and press [Enter],",
      "or enter CASES required and press [C].",
      "To alter qty: Tab \30 \31 cursor onto line,",
      "press Delete, reenter, Tab or \30 \31 again.",
      "Line is erased by setting its qty to 0.",
      "[C] alters CASE/UNITS, [*] alters t/f",
      NULL);
    break;
  case 5:
    uDisplayText("Dialog boxes \"pop up\" from menu selection",
      "or (more directly) from a \"hot key\". Edit",
      "box contents using \30 \31 \32 \33 to move cursor.",
      "Don't press [Enter] until all changes done!",
      "[Esc] undoes all changes in the box.",
      "\21value\20 without cursor is a \"spin box\":",
      "press [Tab] to reveal all its values.",
      NULL);
    break;
  case 6:
    uDisplayText("[System][Menu]\"S\" sets all sound on/off.",
      "A \"pip\" confirms your action accepted.",
      "A \"beep\" means key pressed was discarded.",
      "A \"rasp\" (during Send) means delay due to",
      "phone line (e.g. an extension in use).",
      NULL);
    break;
  case 7:
    uDisplayText("Use mains adaptor to send, if possible.",
      "After successful send file is cleared, then",
      "[Menu]\"View\" or [Pgm button] shows reply.",
      "Cancelling reply never cancels order.",
      NULL);
    break;
  }
} //******************************** END Help

LOCAL_C VOID ExecSysCmd(VOID) {
TEXT command[P_FNAMESIZE+2];

  wGetCommand(command);
  if (*command=='X') p_leave(0);  // shutdown
  if (!SwitchIf(*command,command+1)) p_leave(1);  // switch files
} //******************************** END ExecSysCmd

LOCAL_C VOID StowThis(VOID) {

  m.SeekTo=head.oLine1+(this-1)*sizeof(LINE);
  f_seek(m.fcb,P_FABS,&m.SeekTo);
  f_write(m.fcb,m.pLine1+(this-1),sizeof(LINE));
  p_sound(pip); head.undo=head.unsend=0; StowHead();
} //******************************** END StowThis

LOCAL_C VOID StowHead(VOID) {

  m.SeekTo=0; f_seek(m.fcb,P_FABS,&m.SeekTo);
  f_write(m.fcb,&head,sizeof(HEADER));
} //******************************** END StowHead

GLDEF_C VOID StowAll(VOID) {

  if (m.fcb!=NULL) {
    StowHead();
    if (m.pLine1!=NULL) f_write(m.fcb,m.pLine1,head.lines*sizeof(LINE));
  }
} //******************************** END StowAll

LOCAL_C VOID UpCase(TEXT *source) {
UINT i=1;

  for (;i<=*source;i++) *(source+i)=p_toupper(*(source+i));
} //******************************** END UpCase

LOCAL_C VOID Jingle(UWORD tune) {

  if ((tune<2) && (!p_open(&scb,"SND:",-1)))
    p_ioc4(scb,E_FALARM,&soundstate,&tune);
} //******************************** END Jingle

GLDEF_C VOID TileWindow(INT tile) {
W_WINDATA wd = {0,0};

  if (tile) wsScreenExt(&wd.extent); // exclude Status Window
  else {
    wd.extent.width=wserv_channel->conn.info.pixels.x;
    wd.extent.height=wserv_channel->conn.info.pixels.y;
  }
  wSetWindow(m.MainW,W_WIN_EXTENT,&wd);
  if (!(m.state & A_MASK_HideSWin)) wsEnable(); // show Status Window
  m.boxB.tl=wd.extent.tl;
  m.boxB.br.x=wd.extent.width; m.boxB.br.y=wd.extent.height;
  gClrRect(&m.boxB,G_TRMODE_CLR); // clear Main Window
} //******************************** END TileWindow

LOCAL_C ULONG FlashFree(VOID) {
TEXT string[40];
P_DINFO info;

if (!p_dinfo(dsname,&info)) {
  p_atos(string,"%lu bytes free",info.free);
  wInfoMsg(string);
  return info.free;
  }
  return 0L;
} //******************************** END FlashFree

GLDEF_C VOID Flash(INT i) {
TEXT string[8];

*(string+p_itob(string,i))=0; wInfoMsg(string);
} //******************************** END Flash

LOCAL_C INT StartUp(VOID) {
//TEXT buf[64]; ///
G_GC gc;
G_FONT_INFO font;

  DatLocked=1; uCommonInit();
  m.MainW=uFindMainWid();
  gc.textmode=G_TRMODE_REPL; // replace text
  gc.font=WS_FONT_BASE; // S3 Normal font
  m.Normal=f_leave(gCreateGC(m.MainW,
    G_GC_MASK_TEXTMODE | G_GC_MASK_FONT,&gc));
  gc.font=WS_FONT_BASE+1; // S3 Fast Bold font
  m.Bold=f_leave(gCreateGC(m.MainW,
    G_GC_MASK_TEXTMODE | G_GC_MASK_FONT,&gc));
//rcb=f_new(1,C_RSCFILE); ///
//f_send3(rcb,O_RS_INIT,DatCommandPtr); ///
//f_send4(rcb,O_RS_READ_BUF,buf,R_HOST); ///
//wInfoMsg(buf); ///
  gFontInfo(gc.font,G_STY_NORMAL,&font);
  m.spot.height=dY;
  m.spot.ascent=font.ascent;
  m.spot.width=font.numeric_width;
  m.spot.flags=W_CURSOR_OBLOID;
  wInformOn(); // enable CONS_EVENT_ON_OFF
  uEscape(FALSE);  //disable PSION[Esc]
  f_leave(SwitchIf(hCrackCommandLine(),DatUsedPathNamePtr));
  return 1;
} //******************************** END StartUp

C Module TCOM.C

#include "oe.h"

//#define uNUM   1 // SPD
//#define uNUM  21 // MU
//#define uNUM  31 // PH
//#define uNUM  41 // SA

#ifdef uNUM
  #define uVER 122
  #define uTYPE 1 // Generic
  //#define uTYPE 2 // Exclusive
  #define uTAG (uNUM+uVER*256)
#else
  #define uTYPE 0 // Restricted until:
  #define uYY 97
  #define uMM  7
  #define uDD  1
  #define uTAG (uYY*512+uMM*32+uDD-47712U)
// GT=81 (17 May 93)
// JAT=31416 (24 Aug 54)
#endif

#define ASCII_EOT 4
#define rMax    5 // max lines in Reply Window
#define tMax    8 // max consecutive retries during Send

// Static variables local to this module
LOCAL_D INT rowR; // rows displayed in Reply Window
LOCAL_D P_RECT boxR; // Reply Border
LOCAL_D USER user = {"P1",1,uTYPE,uTAG};
LOCAL_D UBYTE bps[3] = {P_BAUD_300,P_BAUD_1200,P_BAUD_2400};
LOCAL_D P_CRC crc, ack[4] = {15708U, 49507U, 26277U, 26277U};

// Global Definitions

// Procedure Prototypes local to this module
LOCAL_C INT Rx(VOID); // receive frame
LOCAL_C INT GetFrame(ULONG timeout); // wait for Rx string
LOCAL_C INT Tx(UINT len); // send frame
LOCAL_C INT Reply(ULONG timeout); // wait for Red, Green or White
LOCAL_C VOID Put(VOID *, UINT); // Tx frame
LOCAL_C VOID Get(VOID); // Rx line
LOCAL_C VOID ShowLine(TEXT *); //line into Reply Window
LOCAL_C VOID Check(INT); // display any error text
//****************************************************************

GLDEF_C INT Call(TEXT *spec) {
UINT i, line, len;
HEADER *pHost;
LINE *pLine;
TxLINE *pOut;
TEXT string[80];

  com=f_alloc(sizeof(ASYNC));
  com->ccb=com->timer=com->dir=NULL;
  com->state=0; // enable Tx, Ack even
  com->host=head;
  wFlush(); gSetGC0(m.Normal); rowR=0;
  boxR.tl.x=m.boxB.tl.x+2; boxR.tl.y=m.boxB.tl.y+3+dY;
  boxR.br.x=m.boxB.br.x-3; boxR.br.y=m.boxB.tl.y+3+rMax*dY;

  while(!SwitchIf('S',spec)) { // send next order
    SwitchFile(-1);
    m.SeekTo=sizeof(HEADER)+head.lines*sizeof(LINE);
    f_seek(m.fcb,P_FABS,&m.SeekTo);
    head.oblk=0;
    f_write(m.fcb,com->host.host,head.iblk=(*com->host.host)+1);
    len=(*head.host<*com->host.host)? *head.host: *com->host.host;
    pHost=(p_bcmp(head.host+1,len,com->host.host+1,len))? &com->host: &head;
    com->frame.head.user=user;
    com->frame.head.acode=pHost->acode;
    p_bcpy(com->frame.head.pword,pHost->pword+1,5);
    len=p_bcpy(com->frame.head.cref,head.cref,(*head.cref)+1)
      -com->frame.data;
    if (Tx(len)) while((len=Rx())>0) { // accept RVI msg
      f_write(m.fcb,com->frame.data,++len); head.iblk+=len;
      ShowLine(com->frame.data); // RVI
    }
    pLine=m.pLine1; line=0;
    while(line<head.lines) { // pack, block & send lines
      p_atos(string,"Sending line %u",line+1); ShowState(string);
      for (i=LinesPerBlock, pOut=com->frame.line, len=0; i; i--) {
        pLine->rblk=0;
        PackLn(pOut++,pLine++); len+=sizeof(TxLINE);
        if (++line==head.lines) break;
      }
      Tx(len);
    }
    head.state=1; head.stamp=p_date(); ShowState("Awaiting reply");
    while((len=Rx())>0) { // process reply
      if (head.state==1) { // now Sent OK
        p_sound(pip);
        gSetGC0(m.Bold); ShowDone("to cancel reply"); gSetGC0(m.Normal);
      }
      if (com->frame.tail.flag==ASCII_EOT) { // trailer
        f_write(m.fcb,&com->frame.tail,len);
        head.oblk=len;
        ShowLine(com->frame.tail.outcome);
        ShowState("Sent OK, reply OK");
        continue;
      }
      p_atos(string,"Sent OK %u checked",(UINT) com->frame.progress.line);
      ShowState(string); head.state=2+com->frame.progress.line;
      if ((com->frame.progress.flag!=' ')
      && (com->frame.progress.line<=head.lines)
      && (len-=sizeof(UWORD))) { // report shortage
        pLine=m.pLine1-1+com->frame.progress.line;
        f_write(m.fcb,&com->frame.progress.flag,len); pLine->rblk=len;
        p_atos(string+FormItm(string,pLine),
          (len==1)? " (%u)Not on file": " (%u%c)%s",
          (len==1)? pLine->qty: com->frame.progress.qshort,
          com->frame.progress.flag,
          com->frame.progress.description);
        ShowLine(string);
      }
    }
    AgeFile();
  }
  if (m.state & A_MASK_Connected) return 0;
  else return 2; // found nothing to send
} //******************************** END Call

LOCAL_C INT Rx(VOID) {
WORD len, one=1, try=tMax;

  p_iow2(com->ccb,P_FFLUSH); // flush LDD input buffer
  while(1) { // wait for EOF or good CRC
    p_ioc5(com->ccb,P_FREAD,&com->rxstate,com->frame.data,&one); // start Rx
    do { // send Red or White every 2 secs until string received
      if (!try--) p_leave(E_FILE_RETRAN); // too many tries
      Put(&ack[com->state & 3],sizeof(P_CRC));
    } while(!(len=GetFrame(20)));
    if (len>sizeof(P_CRC)) {
      CRC16(&crc.uw,com->frame.data,len); // compute CRC
      if (!crc.uw) { // good CRC
        com->state&=~(C_MASK_StopTx | C_MASK_RVI); // enable Tx, cancel RVI
        com->state^=C_MASK_AckOdd; // switch Ack
        len-=sizeof(P_CRC); *(com->frame.data+len)=0;
        break;
      }
    }
    if (com->state & C_MASK_StopTx) continue;
    if ((len==sizeof(P_CRC))
    && (ack[com->state & 1].uw==com->frame.crc.uw)) { // end of file
      len=(com->state & C_MASK_RVI)? -1: 0;
      com->state|=C_MASK_StopTx; // stop next Tx
      com->state&=~C_MASK_RVI; // cancel any RVI
      break;
    }
    p_sound(rasp); // error
  }
  return len;
} //******************************** END Rx

LOCAL_C INT GetFrame(ULONG timeout) {
WORD tstate, last, this=0;

  p_ioc4(com->timer,P_FRELATIVE,&tstate,&timeout); // start Timer
  while(1) {
    Wait();
    if (com->rxstate!=E_FILE_PENDING) break; // 1st byte received
    if (tstate!=E_FILE_PENDING) return 0; // timeout
  }
  p_iow2(com->timer,P_FCANCEL); p_waitstat(&tstate); // stop Timer
  Check(com->rxstate);
  do {
    p_sleept(5); last=this; p_iow3(com->ccb,P_FTEST,&this);
    if (this>sizeof(com->frame)) p_leave(E_GEN_OVER);
  } while(last!=this);
  if (this) Check(p_read(com->ccb,com->frame.data+1,this));
  return this+1; // done
} //******************************** END GetFrame

LOCAL_C INT Tx(UINT len) {
WORD outcome, rtry, try=tMax;

  CRC16(&crc.uw,com->frame.data,len); // compute CRC
  *(com->frame.data+len++)=crc.hilo.hi; *(com->frame.data+len++)=crc.hilo.lo;
  while(1) { // send frame & wait for Green ack
    if (!try--) p_leave(E_FILE_RETRAN); // too many tries
    Put(com->frame.data,len);
    for (rtry=tMax; (outcome=Reply(20))<0; ) {
      if (!--rtry) p_leave(E_FILE_INACT); // inactivity timeout
    }
    if (outcome==2) break; // RVI received
    outcome^=~com->state; outcome&=C_MASK_AckOdd;
    if (!outcome) break; // ack is Green
    p_sound(rasp); // error
  }
  com->state^=C_MASK_AckOdd; // switch Ack
  return outcome;
} //******************************** END Tx

LOCAL_C INT Reply(ULONG timeout) {
INT i;
WORD tstate, two=sizeof(P_CRC);
P_CRC crc;

  p_iow2(com->ccb,P_FFLUSH); // flush LDD input buffer
  p_ioc5(com->ccb,P_FREAD,&com->rxstate,&crc,&two); // start Rx
  p_ioc4(com->timer,P_FRELATIVE,&tstate,&timeout); // start Timer
  while(1) {
    Wait();
    if (com->rxstate!=E_FILE_PENDING) break; // 1st byte received
    if (tstate==E_FILE_PENDING) continue;
    p_iow2(com->ccb,P_FCANCEL); p_waitstat(&com->rxstate); // stop Rx
    return -1; // timeout
  }
  p_iow2(com->timer,P_FCANCEL); p_waitstat(&tstate); // stop Timer
  Check(com->rxstate);
  for (i=0; i<=2; i++) {if (ack[i].uw==crc.uw) return i;} // reply known
  return -1; // reply garbled
} //******************************** END Reply

GLDEF_C VOID Connect(VOID) {
P_SRCHAR config;
TEXT string[32];
TEXT *pout;

  wSetBusyMsg("S3 disc bays closed?",
    W_CORNER_BOTTOM_RIGHT | 1);
  f_open(&com->timer,"TIM:",-1); // Timer
  f_open(&com->ccb,"TTY:A",-1); // Serial Port
  wCancelBusyMsg();
  if (com->host.modem==2) { // manual modem
    ShowState("Press modem DATA on hearing far tone");
    if (com->host.phone < 3) {
      *p_bcpy(string,com->host.dial[com->host.phone]+1,
        *com->host.dial[com->host.phone])=0;
      hDTMFString(string);
    }
  }
  else ShowState("Waiting for local modem");
  p_iow3(com->ccb,P_FSENSE,&config);
  config.tbaud=config.rbaud=bps[com->host.bps];
  config.tmask=0x2400; // B13, B10
  if (com->host.modem>1) config.hand=P_OBEY_DSR; // Manual or VX543
  if (com->host.modem==3) { // configure VX543
    config.tbaud=config.rbaud=P_BAUD_1200;
    config.frame=P_DATA_7 | P_PARITY; config.parity=P_PAR_EVEN;
    f_leave(p_iow3(com->ccb,P_FSET,&config)); // set port parameters
    Put("\33F 8N10\15",8); // Format 8 data, No parity, 1 stop, V21
    p_sleep(5L); // half second
    config.tbaud=config.rbaud=P_BAUD_300;
    config.frame=P_DATA_8;
  }
  f_leave(p_iow3(com->ccb,P_FSET,&config)); // set port parameters
  Put(&crc,0); // null Tx (wait for CTS and maybe DSR)
  p_sleep(15L); // 1.5 sec
  config.hand|=P_FAIL_DSR; // ignored unless P_OBEY_DSR is set
  f_leave(p_iow3(com->ccb,P_FSET,&config)); // set port parameters
  ShowState("Trying to connect to wholesaler");
  if (com->host.modem<2) { // AT modem
    pout=p_scpy(com->frame.data,"ATE1V1Q0X1D"); // echo cmds, long replies
    if (com->host.phone < 3) {
      *(pout-2)='4';
      *(pout++)=(com->host.modem)? 'P': 'T';
      pout=p_bcpy(pout,com->host.dial[com->host.phone]+1,
        *com->host.dial[com->host.phone]);
    }
    *(pout++)=0x0D; *pout=0;
    Put(com->frame.data,p_slen(com->frame.data)); Get();
    if (p_bcmp(com->frame.data,7,"CONNECT",7)) p_leave(0); // no connection
    if (*(com->frame.data+7)==0) config.tbaud=config.rbaud=P_BAUD_300;
    else if (!p_bcmp(com->frame.data+8,4,"1200",4))
      config.tbaud=config.rbaud=P_BAUD_1200;
    else if (!p_bcmp(com->frame.data+8,4,"2400",4))
      config.tbaud=config.rbaud=P_BAUD_2400;
  }
  else if (com->host.modem==3) { // VX543 modem
    pout=p_scpy(com->frame.data,"\33O");
    if (com->host.phone < 3) {
      *(pout-1)='D'; *(pout++)=' ';
      pout=p_bcpy(pout,com->host.dial[com->host.phone]+1,
        *com->host.dial[com->host.phone]);
    }
    *(pout++)=0x0D; *pout=0;
    Put(com->frame.data,p_slen(com->frame.data)); Get();
  }
  config.tmask=0;
  f_leave(p_iow3(com->ccb,P_FSET,&config)); // set port parameters
  p_sleep(10L); // one second
  while(Rx()>0) {} // Tx Bid
  m.state|=A_MASK_Connected;
} //******************************** END Connect

LOCAL_C VOID Put(VOID *string, UINT len) {

  p_ioc5(com->ccb,P_FWRITE,&com->txstate,string,&len); // start Tx
  while(1) {
    Wait();
    if (com->txstate!=E_FILE_PENDING) break; // Tx done
  }
  Check(com->txstate);
} //******************************** END Put

LOCAL_C VOID Get(VOID) {
UWORD len;

  while(1) {
    len=sizeof(com->frame.data);
    p_ioc5(com->ccb,P_FREAD,&com->rxstate,com->frame.data,&len); // start Rx
    while(1) {
      Wait();
      if (com->rxstate!=E_FILE_PENDING) break;
    }
    Check(com->rxstate);
    if (com->host.modem==3) return; // VX543
    if (!(--len)) continue;
    *(com->frame.data+len)=0; ShowLine(com->frame.data);
    if (p_bcmp(com->frame.data,2,"AT",2)) return; // not an echo
  }
} //******************************** END Get

LOCAL_C VOID ShowLine(TEXT *string) {
TEXT line[80];
P_RECT box;
P_POINT up = {0,-dY};

  if (rowR++==rMax) { // scroll window
    rowR=rMax;
    wScrollRect(m.MainW,&boxR,&up);
  }
  box.tl.x=boxR.tl.x; box.br.x=boxR.br.x;
  box.br.y=boxR.tl.y+(rowR-1)*dY; box.tl.y=box.br.y-dY;
  gPrintBoxText(&box,m.spot.ascent,G_TEXT_ALIGN_LEFT,0,
    line,FormTxt(line,string));
} //******************************** END ShowLine

LOCAL_C VOID Check(INT outcome) {
TEXT string[E_MAX_ERROR_TEXT_SIZE];

  if (outcome<0) {
    if (outcome==E_FILE_LINE) p_leave(outcome);
    p_errs(string,outcome); wInfoMsg(string);
  }
} //******************************** END Check

C Module REPLY.C

#include "oe.h"

// S3 Screen Layout pixel coords
#define Head    8 // y for Header row
#define Foot   79 // y for Footer row
#define XBar   90 // x for Status window split

// Static variables local to this module
TEXT *reason[]= {
  "B(to follow on back order)",
  "Ttemporarily.",
  "M - manuf. cannot supply.",
  NULL
};

// Procedure Prototypes local to this module
LOCAL_C VOID ShowRow(TEXT *,TEXT *,INT); // show Reply Row
LOCAL_C TEXT *Clock(TEXT *, ULONG); // Time Stamp -> ASCII
//****************************************************************

GLDEF_C VOID ShowReply(VOID) {
INT base, i, new;
RxLINE line;
TEXT left[40], right[80];
LINE *pLine, *pNew;
RxTAIL *pTail;

  p_atos(right,"File %s sent ",DatStatusNamePtr);
  *Clock(right+p_slen(right),head.stamp)=0;
  m.boxS.br.y=Head; ShowRow(NULL,right,FALSE);
  pTail=(RxTAIL *)(m.pHost+head.iblk); pTail->shorts=0;
  if (head.oblk) {
    m.SeekTo=head.oLine1+head.lines*sizeof(LINE)+head.iblk;
    for (pLine=m.pLine1;(TEXT *)pLine!=m.pHost;pLine++)
      m.SeekTo+=pLine->rblk;
    f_seek(m.fcb,P_FABS,&m.SeekTo); f_read(m.fcb,pTail,head.oblk);
    *((TEXT *)pTail+head.oblk)=0;
    FormTxt(right,pTail->outcome);
  }
  else if (head.state>1)
    p_atos(right,"Reply cut off after line %u",head.state-2);
  else p_scpy(right,"Outcome unknown");
  ShowRow(NULL,right,TRUE);
  gPrintText(158,Foot,"[Help] available",16);
  if (*head.cref)
    gPrintText(0,Foot,right,
      p_bcpy(p_scpy(right,"Order ref:"),head.cref+1,*head.cref)-right);
  m.spot.pos.y=Foot; m.spot.pos.x=m.boxB.br.x; // cursor off screen
  m.SeekTo=sizeof(HEADER)+head.lines*sizeof(LINE)+head.iblk;
  for (base=0, pLine=m.pLine1; base<head.lines; base++, pLine++) {
    if (pLine->rblk) break; // 1st shortage
  }
  if (base==head.lines) {base=0; pLine=m.pLine1;} // 1st line
  while(1) {
    m.boxS.br.y=m.boxB.tl.y+dY+3;
    p_atos(left,"Line %u of %u.",base+1,head.lines);
    p_atos(right,(pTail->shorts)? "(%u lines report shortages)": "",
      pTail->shorts-1);
    ShowRow(left,right,FALSE); m.boxS.br.y+=3;

    *(right+FormItm(right,pLine))=0;
    ShowRow("Item Code:",right,TRUE);

    p_atos(right,(pLine->flags & L_MASK_Cases)? "%u Case(s)": "%u",
      pLine->qty);
    ShowRow("Qty ordered:",right,FALSE);

    if (pLine->rblk) {
      f_seek(m.fcb,P_FABS,&m.SeekTo);
      f_read(m.fcb,&line.flag,pLine->rblk);
      if (line.flag=='N') {
        ShowRow("Units short:","All - item is not on our file.",FALSE);
        *right=0;
      }
      else {
        *left=line.flag; *(left+1)=0;
        for (i=0; reason[i]!=NULL; i++) {
          if (*reason[i]==line.flag) {
            p_scpy(left,reason[i]+1); break;
          }
        }        
        p_atos(right,"%u %s",line.qshort,left);
        ShowRow("Units short:",right,FALSE);
        *(&line.flag+pLine->rblk)=0;
        FormTxt(right,line.description);
      }
    }
    else {
      p_scpy(right,((pTail->shorts) || (base+2<head.state))?
        "(expect full delivery of this item)": "");
      ShowRow(NULL,right,FALSE);
      *right=0;
    }
    ShowRow(NULL,right,FALSE);

    while(1) {
      Char();
      if (uKeyPressOutstanding()) continue;
      new=base; pNew=pLine;
      if (key.keycode==W_KEY_TAB)
        key.keycode=(key.modifiers & W_SHIFT_MODIFIER)?
          W_KEY_LEFT: W_KEY_RIGHT;
      if ((key.keycode==W_KEY_DOWN) && (++new<head.lines)) { // next line
        m.SeekTo+=(pNew++)->rblk; break;
      }
      if ((key.keycode==W_KEY_UP) && new--) { // previous line
        m.SeekTo-=(--pNew)->rblk; break;
      }
      if (key.keycode==W_KEY_RIGHT) { // next shortage
        while (++new<head.lines)
          if ((++pNew)->rblk) {m.SeekTo+=pLine->rblk; break;}
        if (new<head.lines) break;
      }
      if (key.keycode==W_KEY_LEFT) { // previous shortage
        while (new--)
          if ((--pNew)->rblk) {m.SeekTo-=pNew->rblk; break;}
        if (new>=0) break;
      }
      if (key.keycode==W_KEY_HELP) {
        uDisplayText("[Esc] or [Enter] removes this box, then:",
          "\31 shows next, \30 shows previous line.",
          "[Tab] or \32 shows next, [Shift]+[Tab] or \33",
          "shows previous shortage.",
          "[Menu] then \30 \31 \32 \33 select tools.",
          "[Application button] switches files quickly.",
          NULL);
      }
      else p_sound(beep);
    }
    base=new; pLine=pNew;
  }
} //******************************** END ShowReply

LOCAL_C VOID ShowRow(TEXT *left,TEXT *right,INT bold) {
UINT align=G_TEXT_ALIGN_CENTRE;

  m.boxS.tl.x=m.boxB.tl.x+2; m.boxS.tl.y=m.boxS.br.y-dY;
  if (left!=NULL) {
    m.boxS.br.x=XBar;
    gPrintBoxText(&m.boxS,m.spot.ascent,
      G_TEXT_ALIGN_RIGHT,dX+3,left,p_slen(left));
    align=G_TEXT_ALIGN_LEFT;
    m.boxS.tl.x=XBar;
  }
  m.boxS.br.x=m.boxB.br.x-3;
  if (bold) gSetGC0(m.Bold);
  gPrintBoxText(&m.boxS,m.spot.ascent,align,0,right,p_slen(right));
  if (bold) gSetGC0(m.Normal);
  m.boxS.br.y+=dY+1;
} //******************************** END ShowRow

LOCAL_C TEXT *Clock(TEXT *string, ULONG stamp) {
TEXT day[E_MAX_DAY_NAME], month[E_MAX_MONTH_NAME];
P_DAYSEC dstime;
P_DATE dt;

  p_sttods(&stamp,&dstime); p_dstodt(&dstime,&dt);
  p_nmday(day,p_wkday(dstime.day));
  p_nmmon(month,dt.month);
  p_atos(string,"%3s %u %3s %u %02u:%02u",
    day,dt.day+1,month,dt.year+1900,dt.hour,dt.minute);
  return string+p_slen(string);
} //******************************** END Clock

MASM Module MISC.ASM

	.MODEL	SMALL,C
	OPTION	NOKEYWORD:<ESC>

b0	=	01h
b1	=	02h
b2	=	04h
b3	=	08h
b4	=	10h
b5	=	20h
b6	=	40h
b7	=	80h
b8	=	0100h
b9	=	0200h
b10	=	0400h
b11	=	0800h
b12	=	1000h
b13	=	2000h
b14	=	4000h
b15	=	8000h

pp	MACRO	i,x
	FOR	y,<x>
	i	y
	ENDM
	ENDM

iMax	=	8	; max chars in Item Code

line	STRUC
qty	WORD	?	; Qty ordered
flags	BYTE	?	; B0: Request To Follow if no stock
			; B1: Qty is in cases
outcome	BYTE	?
item	BYTE	iMax DUP(?)	; Item Code
line	ENDS

TxLINE	STRUC
qty	WORD	?	; Qty ordered
bcd	BYTE	4 DUP(?)	; Item Code (BCD 7 digits), Flags:
			; B0: Back Order request
			; B1: Qty ordered is in cases
			; B3: Product Code (not PIP Code)
TxLINE	ENDS

	.CODE
; ****************************** CRC16 of CX bytes at DS:SI into DS:DI
CRC16	PROC	PUBLIC USES cx dx si di
	cld		; upwards
	xor	dx,dx
	jcxz	@F
 .Repeat
	push	cx
	mov	cx,8
	lodsb
  .Repeat
	mov	ah,al
	and	ah,80h
	xor	dh,ah
	shl	dx,1
   .If		CARRY?
	xor	dx,B15+B2+B0	; Generating Polynomial
   .Endif
	rol	al,1
  .Untilcxz
	pop	cx
 .Untilcxz
@@:	mov	[di],dx
	ret
CRC16	ENDP

; ****************************** Pack Order Line DS:BX into ES:DI
PackLn	PROC	PUBLIC USES cx si di
	cld		; upwards
	mov	cl,4
	mov	ax,[bx].line.qty
	stosw
	lea	si,[bx].line.item
	call	pack
	call	pack
	call	pack
	lodsb
	shl	al,cl
	mov	ah,[bx].line.flags
	and	ah,B0+B1
 .If		BYTE PTR[si]>'9'
	or	ah,B3	; B3: Product Code
 .EndIf
	or	al,ah
	stosb
	ret
PackLn	ENDP

pack	PROC	PRIVATE
	lodsw
	shl	al,cl
	and	ah,0fh
	or	al,ah
	stosb
	ret
pack	ENDP

; ****************************** Move BCS Password DS:SI into ES:DI
MovePw	PROC	PUBLIC USES cx si di
	cld		; upwards
	lodsb
	stosb
	mov	cl,al
	xor	ch,ch
 .Repeat
	lodsb
  .If		(al>='a') && (al<='z')
	sub	al,20h	; Upper Case
  .EndIf
	sub	al,1bh
	add	al,cl
	rol	al,cl
	stosb
 .Untilcxz
	ret
MovePw	ENDP

; ****************************** Compressed ZTS DS:SI into ZTS ES:DI
FormTxt	PROC	PUBLIC USES cx si di
	cld		; upwards
	xor	cx,cx
 .While		1
	lodsb
  .If		al < ' '
	.Break .If !al
	.Continue	; discard
  .EndIf
  .If		al & B7
	mov	al,' '
  .EndIf
  .If		al == '#'
	mov	al,'œ'
  .EndIf
	inc	cx
	stosb
 .Endw
	stosb
	mov	ax,cx
	ret
FormTxt	ENDP

; ****************************** Item Code of Line DS:SI into ES:DI
FormItm	PROC	PUBLIC USES cx si di
	cld		; upwards
	lea	si,[si].line.item+1
 .If		BYTE PTR[si+iMax-2] <= '9'	; PIP Code
	movsw
	movsb
	mov	al,'-'
	stosb
	movsw
	movsw
	mov	ax,8
 .Else			; Product Code
	mov	cx,iMax-1
	mov	al,'0'
	xchg	si,di
	repe scasb
	xchg	si,di
	inc	cx
	dec	si
	mov	ax,cx
	rep movsb
 .Endif
	ret
FormItm	ENDP

	END

The Psion Archive