/*->c.dir */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <ctype.h>
#include <time.h>

#include "h.os"
#include "h.wimp"
#include "h.bbc"
#include "h.flex"



#include "h.DrawLevel0"


#include "h.def"
#include "h.wos"
#include "h.strdef"
#include "h.main"
#include "h.mym"
#include "h.serial"
#include "h.ram"
#include "h.file"
#include "h.pr"
#include "h.key"
#include "h.script"
#include "h.state"
#include "h.crctab"
#include "h.timex"
#include "h.sched"
#include "h.group"
#include "h.xext"
#include "h.trans"
#include "h.csv"
#include "h.replay"
#include "h.newfax"
#include "h.band"
#include "h.config"
#include "h.ser"
#include "h.con"

#include "h.dir"

#include "h.txfax"





/*****************************************************************************/



char  prefixstring[PREFIXLEN];
static char  passwordstring[PASSWLEN];

static int   passcrc=0;
static int   unlocked=0;



/*****************************************************************************/

                        /* step between each macro entry in window */
#define TDIRDY   56
                        /* step between top of window and first macro */
#define TDIRFY   12
                        /* height of macro definitions */
#define TDIRHY   48
                        /* x step between entries */
#define TDIRDX   8
#define TDIRPX   8


#define TDIRFW   13
#define TDIRSW   24
#define TDIRTW   8
#define TDIRGW   9
#define TDIRHW   3


#define TDIRFXW   TDIRFW*16+2*TDIRPX
#define TDIRSXW   TDIRSW*16+2*TDIRPX
#define TDIRTXW   TDIRTW*16+2*TDIRPX
#define TDIRGXW   TDIRGW*16+2*TDIRPX
#define TDIRHXW   TDIRHW*16+2*TDIRPX


#define TDIRFX    8
#define TDIRSX    TDIRFXW+TDIRFX+TDIRDX
#define TDIRTX    TDIRSXW+TDIRSX+TDIRDX+2*TDIRDX
#define TDIRGX    TDIRTXW+TDIRTX+TDIRDX
#define TDIRHX    TDIRGXW+TDIRGX+TDIRDX
/* #define TDIRWIDTH TDIRHXW+TDIRHX+TDIRDX */
#define TDIRWIDTH TDIRTX-TDIRDX



#define TDNAME     1
#define TDNUMBER   5

#define TDNOPW     8
#define TDPREFIX   10
#define TDSHOW     2

#define TDOK       7
#define TDCANCEL   9


#define TDVOICE    12   /* voice number */
#define TDLINE1    16   /* full name */
#define TDLINE2    13   /* address 1 */
#define TDLINE3    17   /* address 2 */
#define TDLINE4    19   /* address 3 */
#define TDLINE5    22   /* address 4 */
#define TDLINE6    23   /* address 5 */
#define TDLINE7    3    /* email     */
#define TDCOMMENT  25   /* comment   */
#define TDLINE8    32   /* address 6 */
#define TDLINE9    33   /* address 7 */
#define TDLINE10   34   /* address 8 */

#define TDVOICED    28
#define TDFAXD      27

#define TDBAR       36
#define TDBLOCK     37
#define TDCHECK     66

#define TDMORE      39
#define TDOK2       40
#define TDCANCEL2   41
#define TDPREV      42
#define TDNAME2     57
#define TDAFAX      44
#define TDADATA     45
#define TDAVOICE    46
#define TDANSWER    47
#define TDORIGINATE 48
#define TDSERVER    43
#define TDSNAME     50
#define TDSNAMEGR   51
#define TDO1        54
#define TDO2        55

#define TDPRIORITY  59
#define TDPRIORITYGR 60
#define TDBAND      62
#define TDBANDGR    63

#define TDREMOTEID    64
#define TDREMOTEIDOPT 65



#define DIR101


#ifdef DIR101

#define NSTRING    24
#define OLDNSTRING 12

#else

#define NSTRING    12
#define OLDNSTRING 12

#endif





typedef struct
{
 char name[24];
 char  mark[8];
 char copy[64];
 int  passcrc;
 char prefix[PREFIXLEN];

 char stuff[160];
} dheader;



typedef struct fixedbit
{
 char          name[SYSNAMELEN];
 char          number[NUMLEN];

 int           txrate;
 int           rxrate;

 unsigned int  data:4;
 unsigned int  parity:4;
 unsigned int  stop:4;
 unsigned int  xband:8;
 unsigned int  term:8;

 unsigned int  config:1;
 unsigned int  show:1;            /* show on menu */
 unsigned int  nopw:1;            /* password needed to dial */
 unsigned int  prefix:1;          /* use prefix ? */
 unsigned int  selected:2;
 unsigned int  lastcall:1;
 unsigned int  call:1;
 unsigned int  bar:1;
 unsigned int  block:1;
 unsigned int  answer:1;
 unsigned int  afax:1;
 unsigned int  adata:1;
 unsigned int  avoice:1;
 unsigned int  server:1;
 unsigned int  priority:4;
 unsigned int  remoteid:1;
 unsigned int  check:1;


 int           id;                /* unique number */
 int           group;             /* group id      */

 int           spare[8];
} fixedbit;



typedef struct entry 
{
 fixedbit fix;

 char     strings[NSTRING][64];             
 char     oldname[SYSNAMELEN];

 int      handle;
 int      inuse;
 int      page;

} entry;



typedef struct packedentry 
{
 fixedbit fix;

 int      size;                        /* size of string entry   */
 int      offset;                      /* offset of string entry */
} packedentry;


static packedentry * dir;
static char        * dirs;        /* directory string space */


       int dirtot;                /* total number of directory entries */
static int dirnext;               /* id count */
static int dirmodded;


#define MAXEW 2


static int    entrywindows;

static entry ewind[MAXEW];

static int    sentry;

static void decodeaddentry(int sn,int n,newbitstr * new);


/*****************************************************************************/


static void lockpackedentry(int n,int lock)
{
 int    offset;
 char * p;
 int    i;
 int    len=strlen(passwordstring);
 int    size;

 if(passcrc)
 {
  offset=dir[n].offset;
  size=dir[n].size;
  p=dirs+offset;
  i=0;

  while(size--)
  {
   *p=*p ^ passwordstring[i];
   *p=*p ^ 0xCC;
   p++;
   if(++i>=len) i=0;
  }
 }
 lock=0;
}



/* take fixed length prefix string, and make it unreadable */


static int rotate(int byte,int shift)
{
 if(shift<0) shift=8+shift;

 byte=(byte<<shift);
 byte=(byte+(byte>>8)) & 0xFF;
 return(byte ^ 0xFF);
}


static void scrunch(int doit)
{
 char temp[PREFIXLEN];
 int  i;
 int  j;
 int  sum;

 if(doit)
 {
  for(i=0;i<PREFIXLEN;i++)
  {
   sum=0;
   for(j=0;j<8;j++) sum+=prefixstring[(i+j) % PREFIXLEN] & (1<<j);
   temp[i]=rotate(sum,i % 8);
  }
 }
 else
 {
  for(i=0;i<PREFIXLEN;i++) prefixstring[i]=rotate(prefixstring[i],-(i % 8));

  for(i=0;i<PREFIXLEN;i++)
  {
   sum=0;
   for(j=0;j<8;j++)
   {
    if((i-j)<0)  sum+=prefixstring[PREFIXLEN+(i-j)] & (1<<j);
    else         sum+=prefixstring[(i-j)] & (1<<j);
   }
   temp[i]=sum;
  }
 }

 memcpy(prefixstring,temp,PREFIXLEN);
}




static int passwordcrc(char * pw)
{
 int crc;
 crc=0;

 while(*pw)
 {
  if(*pw!=13) crc=updcrc((*pw++),crc);
  else        pw++;
 }
 return(crc);
}


static int passflag;


void passwordicon(void)
{
 if(icon==2 || icon==3)
 {
  passflag=icon-2;
 }
}



void passwordkey(int * key)
{
 if(*key==27) passflag=-1;
 else
 if(*key==13) passflag=1;
 else
 return;
 *key=-1;
}



/* returns 1==OK 0==NO -1==FORGET IT */

static int getpassword(void)
{
 char buff[PASSWLEN];
 char * p;
 char * q;
 char * pw;



 if(unlocked) return(1);
 else
 if(passcrc==0)
 {
  unlocked=1;
  return(1);
 }

 if(!createwindow(PASSWORD)) return(-1);
 passflag=-2;

 menuwindow(whandle[PASSWORD]);

 while(passflag==-2)
 {
  getw(whandle[PASSWORD]);
  if(!(wflags & 0x10000))
  {
   passflag=-1;
   break;
  }
  poll(0);
 }

 p=pw=iconaddr(whandle[PASSWORD],0);
 q=buff;

 while((*q++=tolower(*p++))!=0);
 *q=0;
 p=buff;

 if(passflag==1)       /* attempt at saying OK */
 {
  if(passwordcrc(p)!=passcrc) passflag=0;
  else                        
  {
   q=passwordstring;
   do
   {
    if(*p!=13) *q++=*p;
   } while(*p++);

   unlocked=1;
  }
 }

 memset(pw,0,PASSWLEN);

 zapmenu();
 closedownt(PASSWORD);

 return(passflag);
}



static void changepassword(char * newpassword)
{
 int i;
 char * p;

 for(i=0;i<dirtot;i++)
  if(!dir[i].fix.nopw) lockpackedentry(i,0);

 strcpy(passwordstring,newpassword);
 p=passwordstring;
 while(*p) *p++=tolower(*p);
 passcrc=passwordcrc(passwordstring);

 for(i=0;i<dirtot;i++)
  if(!dir[i].fix.nopw) lockpackedentry(i,1);
}







int findname(char * name)
{
 int i;
 for(i=0;i<dirtot;i++)
 {
  if(!cstrcmp(name,dir[i].fix.name)) return(i);
 }
 return(-1);
}


int finddirid(char * name)
{
 int dirn;

 dirn=findname(name);

 if(dirn<0) return(dirn);
 else       return(dir[dirn].fix.id);
}



char * diridname(int id)
{
 int i;

 for(i=0;i<dirtot;i++)
  if(dir[i].fix.id==id) return(dir[i].fix.name);

 return(NULL);
}




void dirtogglegroup(int group)
{
 int i;

 for(i=0;i<dirtot;i++) 
 {
  if(dir[i].fix.group==group)
  {
   dir[i].fix.show^=1;
   moddir();
   break;
  }
 }
}


int dirgroupshow(int group)
{
 int i;

 for(i=0;i<dirtot;i++) 
 {
  if(dir[i].fix.group==group) return(dir[i].fix.show);
 }

 return(0);
}


static char * dnthstring(int i,int n)
{
 char * p;
 int    j;

 p=dirs+dir[i].offset;
 for(j=0;j<n;j++)
 {
  p+=strlen(p)+1;
 }

 return(p);
}





/* prefix this number ? */

static int prefixnumber(char * number)
{
 int r0;

 if(xexec3("prefixnumber",number,&r0))
 {
  return(r0);
 }
 else return(1);
}



/* input a number or string, return 0 OK, 1 fail with prefixed number */
/* map - text is a directory name/number */
/* prefix, do prefix stuff */

int getnumber(char * text,char * number,int map,int prefix)
{
 char temp[128];
 int  n;

 *number=0;

 if(map)
 {
  if(isdigit(*text))         strcpy(temp,text);
  else
  if((n=findname(text))!=-1) strcpy(temp,dir[n].fix.number);
  else  
  {
   errorbox("{DIR04}");
   return(1);
  }
 }
 else strcpy(temp,text);

 if(prefix && prefixnumber(temp))
 {
  scrunch(0);
  strcpy(number,prefixstring);
  scrunch(1);
 }

 strcat(number,temp);

 return(0);
}


void diridnumber(int id,char * p,int doprefix)
{
 int i;

 for(i=0;i<dirtot;i++) if(dir[i].fix.id==id) break;

 if(i<dirtot)
 {
  getnumber(dir[i].fix.number,p,0,doprefix);
 }
}


static int createentry(void)
{
 int i;
 i=dirtot++;
 if(!flex_extende((flex_ptr)&dir,dirtot*sizeof(packedentry)))
 {
  dir[i].size=0;
  dir[i].offset=i?dir[i-1].offset+dir[i-1].size:0;
  dir[i].fix.id=dirnext++;

/* dprintf(1,"dirtot=%d i=%d offset=%d",dirtot,i,dir[i-1].offset); */

  return(i);
 }
 else
  return(-1);
}



static void dirextent(void)
{
 int sety;
 int n;
 if(dirtot<3) n=3;
 else         n=dirtot;
 sety=TDIRFY+TDIRDY*n+TDIRFY;

 if(whandle[TDIR])
 {
  extent(whandle[TDIR],0,-sety,TDIRWIDTH+TDIRDX,0);
 }
}



static void dirtoentrysub(entry * ep,int i)
{
 char *  p;
 int     j;

 ep->fix=dir[i].fix;

 strcpy(ep->oldname,dir[i].fix.name);

 if(!ep->fix.nopw) lockpackedentry(i,0);

 p=dirs+dir[i].offset;

 for(j=0;j<NSTRING;j++)
 {
  strcpy(ep->strings[j],p);
  p+=strlen(p)+1;
 }

 if(!ep->fix.nopw) lockpackedentry(i,0);
}


static void dirtoentry(int en,int i)
{
 dirtoentrysub(&ewind[en],i);
}


static int setdirnstring(int i,int size)
{
 int offset;
 int oldsize;
 int delta;
 int j;
 
 offset=dir[i].offset;
 oldsize=dir[i].size;
 delta=size-oldsize;

 if(!flex_midextende((flex_ptr)&dirs,offset+oldsize,delta))
 {
  for(j=0;j<dirtot;j++) if(dir[j].offset>=offset && i!=j) dir[j].offset+=delta;
  dir[i].size=size;
  return(1);
 }
 else
  return(0);
}



static void entrytodirsub(int i,entry * ep)
{
 char buff[1024];
 char * p;
 int    len;
 int    tot;
 int    j;


 dir[i].fix=ep->fix;

 p=buff;
 tot=0;

 for(j=0;j<NSTRING;j++)
 {
  strcpy(p,ep->strings[j]);
  len=strlen(p)+1;
  p+=len;
  tot+=len;
 }

 tot=(tot+0x7) & (~0x7);

 if(setdirnstring(i,tot))
 {
  p=dirs+dir[i].offset;
  memcpy(p,buff,tot);
  if(!ep->fix.nopw) lockpackedentry(i,1);
 }
}

static void entrytodir(int i,int en)
{
 entrytodirsub(i,&ewind[en]);
}


void moddir(void)
{
 dirmodded=1;
}



/* deletes entry i from directory */

static void deleteentry(int i)
{
 if(dir[i].fix.group) delgrouphi(dir[i].fix.group);
 else                 deletegrouplines(dir[i].fix.id);

 deleteschedulelines(dir[i].fix.id);
 setdirnstring(i,0);
 flex_midextend((flex_ptr)&dir,(i+1)*sizeof(packedentry),-sizeof(packedentry));
 dirtot--;
 moddir();
}



static int addentry(int en)
{
 int i;

 i=findname(ewind[en].fix.name);

 if(i<0) 
 {
  i=createentry();
  ewind[en].fix.id=dir[i].fix.id;
 }

 if(i>=0)
 {
  entrytodir(i,en);
  dirextent();
  refreshdirentry(i);
  return(1);
 }
 else
  return(0);
}





/* returns number of entries selected, and number of first */

static int selected(int * k)
{
 int i;
 int n;

 n=0;

 for(i=0;i<dirtot;i++)
 {
  if(dir[i].fix.selected)
  {
   if(!n) *k=i;
   n++;
  }
 }

 return(n);
}



/* see if there is an dir entry with the same name as the name
   in entry struct ci */

static int dduplicate(int ci)
{
 int i;

 for(i=0;i<dirtot;i++)
 {
  if(!cstrcmp(dir[i].fix.name,ewind[ci].fix.name)) return(i);
 }

 return(-1);
}




void savedir(void)
{
 FILE *  fp;
 int     size;
 dheader dh;

 if(dirmodded)
 {
  memset(&dh,0,sizeof(dheader));
  strcpy(dh.name,"ArcFax Tele-Dir");

#ifdef DIR101
  strcpy(dh.mark,"1.01");
#else
  strcpy(dh.mark,"1.00");
#endif

  strcpy(dh.copy,"(17-Jan-1994)");
  dh.passcrc=passcrc;
  memcpy(dh.prefix,prefixstring,PREFIXLEN);

  size=flex_size((flex_ptr)&dirs);

  fp=fopen(path(TDIRP),"wb");
  if(fp)
  {
   fwrite(&dh,1,sizeof(dheader),fp);

   fwrite(&dirtot,1,sizeof(int),fp);
   fwrite(&dirnext,1,sizeof(int),fp);
   fwrite(&size,1,sizeof(int),fp); 
   fwrite(dir,dirtot,sizeof(packedentry),fp);
   fwrite(dirs,1,size,fp);
   savegroup(fp);
   fclose(fp);
   setftype(path(TDIRP),TELEDIR);
   dirmodded=0;
  }
 }
}



#ifdef DIR101

/* directories of version 1.00 have 12 strings per entry         */
/*                   from 1.01 push this to 24 strings per entry */

static void checkversion(dheader * dh)
{
 int    i;
 int    j;
 int    oldsize;
 int    newsize;
 int    len;
 char * p;

 if(strcmp(dh->mark,"1.01")<0)
 {
/*  dprintf(0,"old directory"); */

  for(i=0;i<dirtot;i++)
  {
   oldsize=0;

   dir[i].fix.afax=1;

   p=dirs+dir[i].offset;
   for(j=0;j<OLDNSTRING;j++)
   {
    len=strlen(p)+1;
    oldsize+=len;
    p+=len;
   }
   newsize=(oldsize+NSTRING-OLDNSTRING+0x7) & (~0x7);

   if(setdirnstring(i,newsize))
   {
    memset(dirs+dir[i].offset+oldsize,0,NSTRING-OLDNSTRING);
   }
   else fatalerror("{DIR05}");
  }
 }
}

#endif



static void loaddir(void)
{
 FILE  * fp;
 int     i;
 int     size;
 dheader dh;

/* int time=clock(); */


 fp=fopen(path(TDIRP),"rb");
 if(fp)
 {
  fread(&dh,1,sizeof(dheader),fp);

  passcrc=dh.passcrc;
  memcpy(prefixstring,dh.prefix,PREFIXLEN);

  fread(&dirtot,1,sizeof(int),fp);
  fread(&dirnext,1,sizeof(int),fp);
  fread(&size,1,sizeof(int),fp);

  if(flex_extend((flex_ptr)&dir,dirtot*sizeof(packedentry)) &&
     flex_extend((flex_ptr)&dirs,size))
  {
   fread(dir,dirtot,sizeof(packedentry),fp);
   fread(dirs,1,size,fp);
   loadgroup(fp);
   fclose(fp);
   dirmodded=0;
   for(i=0;i<dirtot;i++) dir[i].fix.selected=0;

#ifdef DIR101
   checkversion(&dh);
#endif
  }
  else fatalerror("{DIR05}");
 }
 else
 {
  *prefixstring=0;
  scrunch(1);
  loadgroup(NULL);
 }

/* dprintf(0,"load time=%d",clock()-time); */


}



/*****************************************************************************/

/* maps a window handle into an entry number */
/* placed in sentry */

int getentry(int handle)
{
 int i;

 for(i=0;i<MAXEW;i++)
 {
  if(ewind[i].inuse && ewind[i].handle==handle)
  {
   sentry=i;
   return(1);
  }
 }

 return(0);
}


/* gets a new entry handle, return handle or -1 if fails */

static int getentryhandle(void)
{
 int i;
 for(i=0;i<MAXEW;i++) if(!ewind[i].inuse) return(i);
 return(-1);
}


/* trashes an entry */

static void loseentryhandle(int i)
{
 ewind[i].inuse=0;
 closedownhandle(&ewind[i].handle);
}


/* close an entry window */

static void closeentry(int i,int ok)
{
 if(ok)
 {
  if(strcmp(ewind[i].fix.name,ewind[i].oldname))
  {                                               /* the name has changed */
   if(dduplicate(i)>=0)
   {
    if(confirm(CONDC,
        "{DIR00}")!=1) ok=0;
   }
  }

  if(ok)
  {
   if(addentry(i)) moddir();
  }
 }

 loseentryhandle(i);
}

/*****************************************************************************/


static void entryclosesub(int i)
{
 closeentry(i,0);
}



void entryclose(void)
{
 entryclosesub(sentry);
}


/*****************************************************************************/

static entry * serentry;

static char * serverf(char * name)
{
 strcpy(serentry->strings[12],name);
 seti(serentry->handle,TDSNAME,0,0);
 return(serentry->strings[12]);
}


static int priorityf(int priority)
{
 serentry->fix.priority=priority;
 writeicon(serentry->handle,TDPRIORITY,priorityname(priority));
 return(priority);
}


static int bandf(int band)
{
 strcpy(serentry->strings[16],bandname(band));
 seti(serentry->handle,TDBAND,0,0);
 return(band);
}



static void entryiconsub(int i)
{
 entry * data=&ewind[i];
 int     handle=ewind[i].handle;

 switch(icon)
 {

    case TDSHOW:
                selectst(handle,TDSHOW,data->fix.show^=1);
                break;

  case TDPREFIX:
                selectst(handle,TDPREFIX,data->fix.prefix^=1);
                break;

     case TDBAR:
                selectst(handle,TDBAR,data->fix.bar^=1);
                break;

   case TDBLOCK:
                selectst(handle,TDBLOCK,data->fix.block^=1);
                break;

   case TDCHECK:
                selectst(handle,TDCHECK,data->fix.check^=1);
                break;

    case TDNOPW:
                selectst(handle,TDNOPW,data->fix.nopw^=1);
                break;

/*  case TDCONFIG:
                optst(handle,TDCONFIG,data->fix.config^=1);
                break; */

  case TDCANCEL:
      case TDOK:
     case TDOK2:
 case TDCANCEL2:
                closeentry(i,icon==TDOK || icon==TDOK2);
                break;

  case TDVOICED:
                voicedial(iconaddr(handle,TDVOICE),0,data->fix.prefix);
                break;

    case TDFAXD:
                voicedial(iconaddr(handle,TDNUMBER),0,data->fix.prefix);
                break;


#ifdef DIR101

    case TDMORE:
    case TDPREV:
                getw(handle);
                findcaret();

                if(icon==TDMORE)
                {
                 scy=(y0-y1);
                 data->page=1;
                 if(chandle==handle) iecarrot(handle,TDREMOTEID);
                }
                else
                {
                 scy=0;
                 data->page=0;
                 if(chandle==handle) iecarrot(handle,TDNAME);
                }

                openatscroll(handle,scx,scy);
                break;

    case TDAFAX:
                selectst(handle,TDAFAX,data->fix.afax^=1);
                break;

   case TDADATA:
                selectst(handle,TDADATA,data->fix.adata^=1);
                break;

  case TDAVOICE:
                selectst(handle,TDAVOICE,data->fix.avoice^=1);
                break;

  case TDSERVER:
                selectst(handle,TDSERVER,data->fix.server^=1);
                break;

 case TDSNAMEGR:
                serentry=data;
                popservermenu(serverf,data->strings[12]);
                break;

  case TDANSWER:
                if(!data->fix.answer)
                {
                 select(handle,TDANSWER);
                 deselect(handle,TDORIGINATE);
                 data->fix.answer=1;
                }
                break;

case TDORIGINATE:
                if(data->fix.answer)
                {
                 deselect(handle,TDANSWER);
                 select(handle,TDORIGINATE);
                 data->fix.answer=0;
                }
                break;

case TDREMOTEIDOPT:
                   selectst(handle,TDREMOTEIDOPT,data->fix.remoteid^=1);
                   break;

 case TDPRIORITYGR:
                   serentry=data;
                   poppriority(priorityf,data->fix.priority);
                   break;

 case     TDBANDGR:
                   serentry=data;
                   popband(bandf,bandnametoband(data->strings[16]));
                   break;

#endif

 }
}




void entryicon(void)
{
 entryiconsub(sentry);
}



#define P0KEYS 14
#define P1KEYS 4


              /* N L R D U */

static char entryiclst[P0KEYS][5]=
{
     TDNAME,          0,           0,    TDNUMBER,    TDCOMMENT,
   TDNUMBER,          0,           0,     TDVOICE,       TDNAME,
    TDVOICE,          0,           0,     TDLINE1,     TDNUMBER,
    TDLINE1,          0,           0,     TDLINE2,      TDVOICE,
    TDLINE2,          0,           0,     TDLINE3,      TDLINE1,
    TDLINE3,          0,           0,     TDLINE4,      TDLINE2,
    TDLINE4,          0,           0,     TDLINE5,      TDLINE3,
    TDLINE5,          0,           0,     TDLINE6,      TDLINE4,
    TDLINE6,          0,           0,     TDLINE8,      TDLINE5,
    TDLINE8,          0,           0,     TDLINE9,      TDLINE6,
    TDLINE9,          0,           0,    TDLINE10,      TDLINE8,
    TDLINE10,         0,           0,     TDLINE7,      TDLINE9,
    TDLINE7,          0,           0,   TDCOMMENT,     TDLINE10,
  TDCOMMENT,          0,           0,      TDNAME,      TDLINE7
};



static char entryiclst2[P1KEYS][5]=
{
 TDREMOTEID,          0,           0,    TDO1,      0,
 TDO1,                0,           0,    TDO2,      TDREMOTEID,
 TDO2,                0,           0,    0,         TDO1,
};


static void entrykeysub(int i,int * key)
{
 int cicon;
 int j;
 int ch;

 ch=*key;

 switch(ch)
 {
       case 27:
               closeentry(i,ch==CR);
               break;

       case CR:
      case TAB:
               ch=CDOWN;

    case 0x18E:
    case 0x18F:
    case 0x19C:
    case 0x19D:
    case 0x19E:
    case 0x19F:
               ch&=0x18F;

               if(ewind[i].page)
               {
                for(j=0;j<P1KEYS;j++)
                 if(entryiclst2[j][0]==icon) break;
                cicon=entryiclst2[j][(ch-0x18B)];
               }
               else
               {
                for(j=0;j<P0KEYS;j++) 
                 if(entryiclst[j][0]==icon) break;
                cicon=entryiclst[j][(ch-0x18B)];
               }

               if(cicon) iecarrot(ewind[i].handle,cicon);
               break;

    default:return;
 }
 *key=-1;
}





void entrykey(int * key)
{
 entrykeysub(sentry,key);
}




static void openentrywindow(int i)
{
 int     handle;
 entry * ep=&ewind[i];

 handle=createwindow(ENTRY);
 whandle[ENTRY]=0;

 ep->handle=handle;
 ep->inuse=1;
 ep->page=0;

 setindirect(handle,TDNAME,13,ep->fix.name);
 setindirect(handle,TDNUMBER,25,ep->fix.number);
 setindirect(handle,TDVOICE,31,ep->strings[0]);
 setindirect(handle,TDLINE1,63,ep->strings[1]);
 setindirect(handle,TDLINE2,63,ep->strings[2]);
 setindirect(handle,TDLINE3,63,ep->strings[3]);
 setindirect(handle,TDLINE4,63,ep->strings[4]);
 setindirect(handle,TDLINE5,63,ep->strings[5]);
 setindirect(handle,TDLINE6,63,ep->strings[6]);
 setindirect(handle,TDLINE7,63,ep->strings[7]);
 setindirect(handle,TDCOMMENT,63,ep->strings[8]);
 setindirect(handle,TDLINE8,63,ep->strings[9]);
 setindirect(handle,TDLINE9,63,ep->strings[10]);
 setindirect(handle,TDLINE10,63,ep->strings[11]);

 selectst(handle,TDNOPW,ep->fix.nopw);
 selectst(handle,TDPREFIX,ep->fix.prefix);
 selectst(handle,TDBAR,ep->fix.bar);
 selectst(handle,TDBLOCK,ep->fix.block);
 selectst(handle,TDCHECK,ep->fix.check);
 selectst(handle,TDSHOW,ep->fix.show);


#ifdef DIR101
 setindirect(handle,TDNAME2,13,ep->fix.name);
 setindirect(handle,TDSNAME,63,ep->strings[12]);
 setindirect(handle,TDO1,63,ep->strings[13]);
 setindirect(handle,TDO2,63,ep->strings[14]);

 setindirect(handle,TDREMOTEID,63,ep->strings[15]);

 setindirect(handle,TDBAND,63,ep->strings[16]);
 if(!ep->strings[16][0]) strcpy(ep->strings[16],bandname(-1));


 selectst(handle,TDAFAX,ep->fix.afax);
 selectst(handle,TDADATA,ep->fix.adata);
 selectst(handle,TDAVOICE,ep->fix.avoice);
 selectst(handle,TDSERVER,ep->fix.server);
 selectst(handle,TDANSWER,ep->fix.answer);
 selectst(handle,TDORIGINATE,!ep->fix.answer);
 selectst(handle,TDREMOTEIDOPT,ep->fix.remoteid);

 writeicon(handle,TDPRIORITY,priorityname(ep->fix.priority));

#endif


 popup(handle,i);
 iecarrot(handle,TDNAME);
}



/* zeros fields to new values */

static void blankentrysub(entry * ep)
{
 memset(ep,0,sizeof(entry));

 ep->fix.show=ep->fix.prefix=1;
 ep->fix.config=1;
}

static void blankentry(int i)
{
 blankentrysub(&ewind[i]);
}


static void newentry(void)
{
 int i;
 int k;
 int n;
 int j;

 n=selected(&k);
 i=getentryhandle();
 if(i<0) return;
 if(n!=1 || dir[k].fix.group) blankentry(i);
 else
 {
  dirtoentry(i,k);
  *ewind[i].fix.name=0;
  *ewind[i].oldname=0;
  *ewind[i].fix.number=0;
  for(j=0;j<NSTRING;j++) *ewind[i].strings[j]=0;
 }

 openentrywindow(i);
}



void diraddnewentry(char * name)
{
 int i;
 int n;

 i=getentryhandle();
 if(i<0) return;
 blankentry(i);

 if(isdigit(*name)) strcpy(ewind[i].fix.number,name);
 else
 {
  n=findname(name);
  if(n>=0) dirtoentry(i,n);
  else     strcpy(ewind[i].fix.name,name);
 }

 openentrywindow(i);
}


static void newgroup(char * name)
{
 int i;
 int en;

 i=findname(name);
 if(i<0) 
 {
  en=getentryhandle();
  if(en==-1) closeentry(MAXEW-1,0);
  en=getentryhandle();
  blankentry(en);
  i=createentry();
  ewind[en].fix.id=dir[i].fix.id;
  ewind[en].fix.show=1;
  addgroup(&ewind[en].fix.group);
  strcpy(ewind[en].fix.name,name);
  entrytodir(i,en);
  dirextent();
  refreshdirentry(i);

  for(i=0;i<dirtot;i++)
  {
   if(dir[i].fix.selected && !dir[i].fix.group)
   {
    addgroupline(ewind[en].fix.group,dir[i].fix.id);
   }
  }

  moddir();
 }
}



static void editentry(void)
{
 int i;
 int k;
 int n;

 n=selected(&k);

 if(dir[k].fix.group)
 {
  opengroup(dir[k].fix.id,dir[k].fix.group);
 }
 else
 {
  i=getentryhandle();
  if(i<0) return;
  dirtoentry(i,k);
  openentrywindow(i);
 }
}


/*****************************************************************************/
/* code for handling macro windows */

/* redraw directory */



static void dirredrawsub(wimp_redrawstr * redrawstr,int more)
{
 int oy;
 int ox;
 int ylo;
 int yhi;
 int n;
 int y;
 int yp;
 int yp2;
 int xp;
 int col;

 fixedbit * fixp;

 wimpfontstart();

 while(more)
 {
  ox=redrawstr->box.x0-redrawstr->scx;
  oy=redrawstr->box.y1-redrawstr->scy;

  ylo=-(redrawstr->g.y1-oy);            /* small number, top of window */
  yhi=-(redrawstr->g.y0-oy);            /* bigger, bottom of window */

  n=(ylo-TDIRFY)/TDIRDY;
  if(n<0) n=0;
  y=n*TDIRDY+TDIRFY;

  while(y<=yhi && n<dirtot)
  {
   yp=oy-y-4;
   yp2=yp-8;
   xp=ox+4;

   fixp=&dir[n].fix;

   if(fixp->selected) col=(6<<8)|0;
   else               col=(1<<8)|7;

   plinthtext(ox+TDIRFX,yp,TDIRFXW,TDIRHY,fixp->name,col);

   if(fixp->group) plinthtext(ox+TDIRSX,yp,TDIRSXW,TDIRHY,
                                                  transtoken("Group"),col);
   else            plinthtext(ox+TDIRSX,yp,TDIRSXW,TDIRHY,fixp->number,col);

   y+=TDIRDY;
   n++;
  } 

  wimp_get_rectangle(redrawstr,&more);
 }

 wimpfontend();

}



void dirredraw(void)
{
 wimp_redrawstr redrawstr;
 int            more;
 redrawstr.w=ewindow;
 wimp_redraw_wind(&redrawstr,&more);
 dirredrawsub(&redrawstr,more);
}





void refreshdirentry(int n)
{
 wimp_redrawstr r;
 int handle=whandle[TDIR];
 int more;

 if(handle)
 {
  r.box.x0=0;
  r.box.x1=TDIRWIDTH;
  r.box.y1=-TDIRFY-n*TDIRDY;
  r.box.y0=r.box.y1-TDIRHY-4;
  r.w=handle;

  wimp_update_wind(&r,&more);
  dirredrawsub(&r,more);
 }
}



/* scroll window to show entry */

static void displayentry(int n)
{
 int yhi=-TDIRFY-n*TDIRDY;
 int ylo=-TDIRFY-(n+1)*TDIRDY;
 int wlo;
 int whi;

 getw(whandle[TDIR]);

 whi=by-y1;
 wlo=by-y0;

 if(ylo<wlo || yhi>whi)
   openatscroll(whandle[TDIR],0,-TDIRFY-n*TDIRDY-TDIRDY/2+(y1-y0)/2);
}




void dirclose(void)
{
 int i;
 for(i=0;i<MAXEW;i++) if(ewind[i].inuse) entryclosesub(i);
 closedownt(TDIR);
 saveconfig();
}



static void dirclearselect(void)
{
 int i;
 for(i=0;i<dirtot;i++)
 {
  if(dir[i].fix.selected)
  {
   dir[i].fix.selected=0;
   refreshdirentry(i);
  }
 }
}



static void dirselectall(void)
{
 int i;

 for(i=0;i<dirtot;i++)
 {
  dir[i].fix.selected=1;
 }
 refreshwindow(whandle[TDIR]);
}


/* return 1 to bar call */

int callbar(int fp)
{
 int    i;
 char   string[128];

 stripid(stringptr(stack[fp]),string);

 for(i=0;i<dirtot;i++)
 {
  if(dir[i].fix.bar)
  {
   if(!cstrcmp(dir[i].fix.number,string)) return(1);
  }
 }
 return(0);
}




int txidoks(char * p)
{
 char string[IDLEN*2];
 int  i;

 stripid(p,string);

 if(txdirn)
 {
  for(i=0;i<dirtot;i++)
  {
   if(dir[i].fix.id==txdirn)
   {
    if(dir[i].fix.remoteid && dir[i].fix.check)
    {
     if(cstrcmp(string,dnthstring(i,15)))
     {
   /*   dprintf(0,"string=%s nth=%s",string,dnthstring(i,15)); */

      return(0);
     }
    }
    break;
   }
  }
 }
 return(1);
}



int txidok(int fp)
{
 char * p;

 if(txdirn)
 {
  p=stringptr(stack[fp]);
  return(txidoks(p));
 }
 return(1);
}




int rxidoks(char * p)
{
 char   string[128];
 int i;

 stripid(p,string);

 for(i=0;i<dirtot;i++)
 {
  if(dir[i].fix.block && dir[i].fix.remoteid)
  {
   if(!cstrcmp(string,dnthstring(i,15))) return(0);
  }
 }
 return(1);
}


int rxidok(int fp)
{
 char * p;
 p=stringptr(stack[fp]);
 return(rxidoks(p));
}



char * idtoname(char * id)
{
 int i;

 for(i=0;i<dirtot;i++)
 {
  if(dir[i].fix.remoteid)
  {
   if(!cstrcmp(id,dnthstring(i,15))) return(dir[i].fix.name);
  }
 }

 return(NULL);
}




static void deleteselection(void)
{
 int i;
 int k;
 int n;

 n=selected(&k);

 if(n)
 {
  if(confirm(CONDC,"%s: {DIR03}",n==1?"{DIR01}":"{DIR02}")==1)
  {
   for(i=0;i<dirtot;i++)
   {
    if(dir[i].fix.selected) deleteentry(i--);
   }
  }
 }

 dirextent();
 reopenw(whandle[TDIR]);
 refreshwindow(whandle[TDIR]);
}



static int dircmp(const void * p1,const void * p2)
{
 return(strcmp(((entry*)p1)->fix.name,((entry*)p2)->fix.name));
}



/* sort dir entries on name */

static void dirsort(void)
{
 qsort(dir,dirtot,sizeof(packedentry),dircmp);
 refreshwindow(whandle[TDIR]);
 moddir();
}


static void dirsearch(void)
{
 char   lstring[SYSNAMELEN];
 char * p=menuaddr(tdirsearch_menu,0);
 char * q=lstring;
 char * estring;
 int    entry;
 int    length;
 int    i;
 int    j;
 int    from;
 int    n;

 n=selected(&from);
 if(!n) from=0;
 else   from++;

 dirclearselect();

 while((*q++=toupper(*p++))!=0);

 for(entry=from;entry<dirtot;entry++)
 { 
  estring=dir[entry].fix.name;
  if((length=strlen(estring))==0) continue;

  for(i=0,j=0;i<=length;i++)
  {
   if((lstring[j]=='*') && (lstring[j+1]!=0) && i==length) break;
   else
   if(lstring[j]==toupper(estring[i])) j++;
   else
   if(lstring[j]=='*')
   {
    if(lstring[j+1]==toupper(estring[i])) j+=2;
   }
   else
    break;
  }

  if(i==(length+1)) break;
 }

 if(entry<dirtot)
 {
  dir[entry].fix.selected=1;
  refreshdirentry(entry);
  displayentry(entry);
 }
}



static void dirdragselect(void)
{
 wimp_dragstr drg;
 int i;
 int min;
 int max;
 int hit;
 int handle;

 min=max=0; /* keep compiler happy */

 hit=0;

 for(i=0;i<dirtot;i++)
 {
  if(dir[i].fix.selected)
  {
   if(hit) max=i;
   else
   {
    min=max=i;
    hit=1;
   }
  }
 }


 if(hit)
 {
  handle=whandle[TDIR];
  getw(handle);
  drg.window=handle;
  drg.type=5;
  drg.box.x0=bx+TDIRFX;
  drg.box.y0=by-max*TDIRDY-TDIRFY-TDIRHY;
  drg.box.x1=bx+TDIRSX+TDIRSXW;
  drg.box.y1=by-min*TDIRDY-TDIRFY;
  drg.parent.x0=drg.box.x0-mousex;
  drg.parent.y0=drg.box.y0-mousey;
  drg.parent.x1=drg.box.x1-mousex+screenx;
  drg.parent.y1=drg.box.y1-mousey+screeny;

  wimp_drag_box(&drg);
  startdrag(DIRDRAG,handle);
 }
}



int savetextentry(char * filename)
{
 FILE * fp;
 int    i;
 int    j;
 char * p;
 int    list;
 int    last;

 list=0;

 fp=ropen(filename,"wb");
 if(fp)
 {
  for(i=0;i<dirtot;i++)
  {
   if(dir[i].fix.selected)
   {
    if(list) {rputc('\n',fp);rputc('\n',fp);}
    list=1;

    last=0;
    p=dirs+dir[i].offset;
    for(j=0;j<OLDNSTRING;j++)
    {
     if(j!=7 && j!=8) if(strlen(p)) last=j;
     p+=strlen(p)+1;
    }

    p=dirs+dir[i].offset;
    for(j=0;j<=last;j++)
    {
     if(j>0 && j!=7 && j!=8)
     {
      if(j<last) rfprintf(fp,"%s\n",p);
      else       rfprintf(fp,"%s",p);
     }
     p+=strlen(p)+1;
    }
   }
  }
  rclose(fp);
 }

 return(1);
}



void dirdragend(void)
{
 int i;
 int vxn;

 getpointer();

 if((vxn=getsn(mhandle))>=0)
 {
  for(i=0;i<dirtot;i++)
  {
   if(dir[i].fix.selected) decodeaddentry(vxn,i,&defnewbits);
  }
 }
 else
 if((vxn=getgn(mhandle))>=0)
 {
  for(i=0;i<dirtot;i++)
  {
   if(dir[i].fix.selected) addgrouphi(vxn,dir[i].fix.name);
  }
 }
 else
 if(mhandle==whandle[NEWFAX] || mhandle==whandle[CONTROL])
 {
  for(i=0;i<dirtot;i++)
  {
   if(dir[i].fix.selected) 
   {
    if(mhandle==whandle[NEWFAX]) newfn(dir[i].fix.name);
    else                         confn(dir[i].fix.name);
    break;
   }
  }
 }
 else
 {
  setsave(SAVEDIRENTRY);
  savedragend();
 }
}



/* click on dir window */

void diricon(void)
{
 int n;
 int goodn;
 int k;
 int ns;

 getw(whandle[TDIR]);

 n=(by-mousey-TDIRFY)/TDIRDY;
 goodn=(n>=0 && n<dirtot);

 if(buttons==2)
 {
  if(goodn)
  {
   ns=selected(&k);

   if(ns==1) 
   {
    if(dir[k].fix.selected==2 && k!=n)
    {
     dir[k].fix.selected=0;
     refreshdirentry(k);
     ns=0;
    }
   }
   if(!ns)
   {
    dir[n].fix.selected=2;
    refreshdirentry(n);
   }
  }
  dirpop();
 }
 else
 {
  if(buttons==0x400)
  {
   if(!goodn) dirclearselect();
   else
   if(!dir[n].fix.selected)
   {
    dirclearselect();
    dir[n].fix.selected=1;
    refreshdirentry(n);
   }
  }
  else
  if(goodn)
  {
   if(buttons==0x10 || buttons==0x40)
   {
    if(!dir[n].fix.selected)
    {
     dir[n].fix.selected=1;
     refreshdirentry(n);
    }
    dirdragselect();
   }
   else
   if(buttons==0x100)   /* adjust */
   {
    if(dir[n].fix.selected) dir[n].fix.selected=0;
    else                    dir[n].fix.selected=1;
    refreshdirentry(n);
   }
   else
   if(buttons==0x1 || buttons==0x4)   /* select + */
   {
    dirclearselect();
    dir[n].fix.selected=1;
    refreshdirentry(n);

    if(buttons==0x1)   /* double click with adjust */
    {
     editentry();
    }
    else
    if(buttons==0x4)   /* double click with select */ 
    {          
     /* major fiddle voice is string #0 */
     if(!dir[n].fix.nopw) lockpackedentry(n,0);
     voicedial(dirs+dir[n].offset,0,dir[n].fix.prefix);
     if(!dir[n].fix.nopw) lockpackedentry(n,1);
    }
   }
  }
 }
}




/* click on menu, display macro window */

void dirshow(void)
{
 int handle;

 if(getpassword()!=1) return;

 handle=createwindow(TDIR);
 if(!handle) return;
 dirextent();
 popup(handle,0);
}



/* boots up phone directory */

void dirboot(void)
{
 int i;

 dirtot=0;
 dirnext=1;
 flex_alloc((flex_ptr)&dir,0);
 flex_alloc((flex_ptr)&dirs,0);

 entrywindows=0;
 for(i=0;i<MAXEW;i++) ewind[i].inuse=0;

 loaddir();
}


/*****************************************************************************/
/* call placing logic */

int prefix;

int redial;
int attempts;
int ardelay;



/*****************************************************************************/

static void setpopdir(void)
{
 int n;
 int k;

 n=selected(&k);

 sprintf(menuaddr(tdir_menu,0),transtoken("DIR06"),n==1?dir[k].fix.name:"");

 if(n>1)
  strcpy(menuaddr(tdir_menu,1),transtoken("DIR07"));
 else
  sprintf(menuaddr(tdir_menu,1),transtoken("DIR08"),n==1?dir[k].fix.name:"");

 unshadest(tdir_menu,0,n==1);
 unshadest(tdir_menu,1,n!=0);
 unshadest(tdir_menu,3,n>1);
 unshadest(tdir_menu,4,dirtot);
 unshadest(tdir_menu,5,dirtot);
 unshadest(tdir_menu,7,n);

 tickst(tdir_menu,9,prefix);

 scrunch(0);
 strcpy(menuaddr(tdirprefix_menu,0),prefixstring);
 scrunch(1);
 strcpy(menuaddr(tdirpassword_menu,0),passwordstring);

}



void decodedir(int m2,int m3,int m4,int m5)
{

 switch(m2)
 {
  case 0: /* Edit */
         editentry();
         break;

  case 1: /* Delete */
         deleteselection();
         break;

  case 2: /* New entry */
         newentry();
         break;
 
  case 3: /* New group */
         if(m3==0) newgroup(menuaddr(tdirgroup_menu,0));
         break;

  case 4: /* Sort */
         dirsort();
         break;

  case 5: /* Search */
         dirsearch();
         break;

  case 6:
         dirselectall();
         break;

  case 7: /* Clear selection */
         dirclearselect();
         break;

  case 8: /* password */
         changepassword(menuaddr(tdirpassword_menu,0));
         memset(menuaddr(tdirpassword_menu,0),0,PASSWLEN);
         moddir();
         break;

  case 9: /* prefix */
         if(m3==-1) prefix^=1;
         else
         {
          strcpy(prefixstring,menuaddr(tdirprefix_menu,0));
          scrunch(1);
          memset(menuaddr(tdirprefix_menu,0),0,PREFIXLEN);
          moddir();
          prefix=1;
         }
         break;

 }

 m3=m4=m5;
 setpopdir();
}



void dirpop(void)
{
 setpopdir();
 popmenu(tdir_menu);
}



/*****************************************************************************/

/* max size of ram buffer in bytes */



char * strings; /* points to area to store indirected menu entries in */

/* adds an (indirected) item to a menu */
/* return 1 if no room left */

int writemitem(int ** pr,char * string,int bits,int shade,int * maxl)
{
 int * smp=*pr;
 int   len;
 int   xlen;

 if((strings-(char*)smp)<24) return(1);

 smp[0]=bits;
 smp[1]=-1;
 smp[4]=0;

 if((unsigned int)string<0x8000)
 {
  len=(int)string;
  xlen=len+1;
  if(((strings-(char*)smp)-xlen)<24) return(1);
  strings-=xlen;
  memset(strings,0,xlen); 
  smp[2]=0x7000121 | shade;
  smp[3]=(int)strings;
  smp[5]=xlen;
 }
 else
 if((len=strlen(string))>12)
 {
  xlen=len+1;
  if(((strings-(char*)smp)-xlen)<24) return(1);
  strings-=xlen;
  strcpy(strings,string); 
  smp[2]=0x7000121 | shade;
  smp[3]=(int)strings;
  smp[5]=xlen;
 }
 else
 {
  len=strlen(string);
  smp[2]=0x7000021 | shade;
  smp[5]=0;
  smp[3]=0;
  strcpy((char*)(&smp[3]),string);
 }

 *pr+=6;
 if(*maxl<len) *maxl=len;
 return(0);
}


void writemheader(int * menup,char * title,int maxwidth)
{
 strcpy((char *)menup,title);
 *(((char *)menup)+12)=7;
 *(((char *)menup)+13)=2;
 *(((char *)menup)+14)=7;
 *(((char *)menup)+15)=0;
 menup[4]=12+16*maxwidth;
 menup[5]=40;
 menup[6]=0;
}




static char dialnamebuff[36];   /* used to retain last name/number dialled */

/* set up pointer to parent menu, so adjust clicks work on dynamic */

int setupentrymenu(int writeable,int * menuhandle,int item)
{ 
 int  * fontp;
 int  * fontm;
 int    i;
 int    maxwidth=0;

 fontm=(int*)myrambuff;

 usedrambuff=1;
 strings=myrambuff+MYRMAX;

 fontp=fontm+7;

 if(writeable)
 {
  writemitem(&fontp,(char*)32,2+4,0,&maxwidth);
  maxwidth=12;
 }

 i=0;
 while(i<dirtot)
 {
  if(dir[i].fix.show)
  {
   if(writemitem(&fontp,dir[i].fix.name,0,0,&maxwidth)) break;
   if((strings-(char*)fontp)<0x80) break; /* leave some room for ustyles */
  }
  i++;
 }
 *(fontp-6)|=0x80;

 writemheader(fontm,transtoken("Entry"),maxwidth); /* Fonts */

 if(writeable) strcpy(menuaddr((int*)myrambuff,0),dialnamebuff);

 if(menuhandle) mwpoint(menuhandle,item,(int)myrambuff);

 clearusermenus();
 entry_menu=(int*)myrambuff;

 return((int)myrambuff);
}



int setupdrivermenu(void)
{
 int  * fontp;
 int  * fontm;
 int    maxwidth=0;
 fxstat f;

 fontm=(int*)myrambuff;

 usedrambuff=1;
 strings=myrambuff+MYRMAX;

 fontp=fontm+7;

 startscan();

 while(nextitem(path(DRVP),&f,NULL))
 {
  if(writemitem(&fontp,f.name,0,0,&maxwidth)) break;
  if((strings-(char*)fontp)<0x80) break; /* leave some room for ustyles */
 }
 *(fontp-6)|=0x80;

 writemheader(fontm,transtoken("Driver"),maxwidth); /* Fonts */

 setpopdriver();

 clearusermenus();
 driver_menu=(int*)myrambuff;

 return((int)myrambuff);
}



int setupservermenu(void)
{
 int  * fontp;
 int  * fontm;
 int    maxwidth=0;
 fxstat f;
 int    n;

 fontm=(int*)myrambuff;

 usedrambuff=1;
 strings=myrambuff+MYRMAX;

 fontp=fontm+7;

 startscan();
 n=0;

 while(nextitem(path(SRVP),&f,NULL))
 {
  if(writemitem(&fontp,f.name,0,0,&maxwidth)) break;
  if((strings-(char*)fontp)<0x80) break; /* leave some room for ustyles */
  n++;
 }
 *(fontp-6)|=0x80;

 if(n)
 {
  writemheader(fontm,transtoken("Server"),maxwidth); /* Fonts */

  clearusermenus();
  server_menu=(int*)myrambuff;

  return((int)myrambuff);
 }
 else
 {
  return(NULL);
 }
}




int setupvoicecompmenu(void)
{
 int  * fontp;
 int  * fontm;
 int    maxwidth=0;
 int    i;

 fontm=(int*)myrambuff;

 usedrambuff=1;
 strings=myrambuff+MYRMAX;

 fontp=fontm+7;

 for(i=0;i<voicenames;i++)
 {
  if(writemitem(&fontp,voicecompressionname[i],0,0,&maxwidth)) break;
  if((strings-(char*)fontp)<0x80) break; /* leave some room for ustyles */
 }
 *(fontp-6)|=0x80;

 writemheader(fontm,transtoken("Compress"),maxwidth); /* Fonts */

 clearusermenus();
 voice_menu=(int*)myrambuff;

 return((int)myrambuff);
}




int setupbandmenu(void)
{
 int  * fontp;
 int  * fontm;
 int    maxwidth=0;
 int    i;

 fontm=(int*)myrambuff;

 usedrambuff=1;
 strings=myrambuff+MYRMAX;

 fontp=fontm+7;

 for(i=-1;i<bands;i++)
 {
  if(writemitem(&fontp,bandname(i),0,0,&maxwidth)) break;
  if((strings-(char*)fontp)<0x80) break; /* leave some room for ustyles */
 }
 *(fontp-6)|=0x80;

 writemheader(fontm,transtoken("Band"),maxwidth); /* Fonts */

 clearusermenus();
 band_menu=(int*)myrambuff;

 return((int)myrambuff);
}



void clearusermenus(void)
{
 band_menu=font_menu=voice_menu=entry_menu=driver_menu=server_menu=NULL;
}


/*****************************************************************************/


/* try to find a dir name */


int findnamedynamic(char * name)
{

 int i;
 for(i=0;i<dirtot;i++)
 {
  if(!cstrcmp(name,dir[i].fix.name)) return(i);
 }
 return(-1);

}




void getdirbits(int n,newbitstr * new)
{
 char    * p;
 int       j;

 new->prefix=dir[n].fix.prefix;
 new->priority=dir[n].fix.priority;

 p=dirs+dir[n].offset;
 for(j=0;j<16;j++)
 {
  p+=strlen(p)+1;
 }

 new->band=bandnametoband(p);
}


int getdirid(int n)
{
 return(dir[n].fix.id);
}

void getdirbitsall(int n,newbitstr * new)
{
 getdirbits(n,new);

 new->fax=dir[n].fix.afax;
 new->data=dir[n].fix.adata;
 new->voice=dir[n].fix.avoice;
 new->answer=dir[n].fix.answer;
}


/* adds the nth directory entry to schedule sn, with new bits */

static void decodeaddentry(int sn,int n,newbitstr * new)
{
 int       entries;
 int       i;
 int       j;
 int       id;
 newbitstr localnew;



 if(dir[n].fix.group)
 {
  entries=grouplines(dir[n].fix.group);
  for(i=0;i<entries;i++)
  {
   id=grouplineid(dir[n].fix.group,i);

   for(j=0;j<dirtot;j++)
   {
    if(dir[j].fix.id==id)
    {
     decodeaddentry(sn,j,new);
    } 
   }
  }
 }
 else 
 {
  localnew=*new;

  if(!localnew.apply) getdirbits(n,&localnew);

  localnew.override=1;
  localnew.fax=dir[n].fix.afax;
  localnew.data=dir[n].fix.adata;
  localnew.voice=dir[n].fix.avoice;
  localnew.answer=dir[n].fix.answer;

  addschedhi(sn,dir[n].fix.id,dir[n].fix.name,&localnew);
 }
}


/* attempt to add name/number to schedule sn with newbits */

void decodeaddnamenumber(char * name,int sn,newbitstr * new)
{
 char       * p;
 int          n;

 if(name)
 {
  p=name;
  while(isspace(*p)) p++;
  strcpy(dialnamebuff,p);
 }

 if(isdigit(*dialnamebuff))
 {
  addschedhi(sn,0,dialnamebuff,new);
 }
 else
 if((n=findname(dialnamebuff))!=-1)
 {
  decodeaddentry(sn,n,new);
 }
 else 
  errorbox("{DIR04}");
}


void decodedial(int m3)
{
 char * p;
 int    n;
 int    i;

 if(m3==0 || m3<0)
 {
  /* if it is an int then dial it else set m3 to entry number */

  if(m3==0) p=menuaddr((int*)myrambuff,0);
  else      p=NULL;

  decodeaddnamenumber(p,sxmenu,&defnewbits);
 }
 else
 if(m3>0 && m3<=dirtot)
 {
  /* dial entry */

  m3--;
  i=0;

  for(n=0;n<dirtot;n++)
  {
   if(dir[n].fix.show)
   {
    if(i==m3)
    {
     strcpy(dialnamebuff,dir[n].fix.name);
     decodeaddentry(sxmenu,n,&defnewbits);
     break;
    }
    else
    {
     i++;
    }
   }
  }
 }
}


/***************************************************************************/


void gdecodedial(int m3)
{
 char * p;
 int    n;
 int    i;

 if(m3==0 || m3<0)
 {
  /* if it is an int then dial it else set m3 to entry number */
  if(m3==0)
  {
   p=menuaddr((int*)myrambuff,0);
   while(isspace(*p)) p++;
   strcpy(dialnamebuff,p);
  }
  if((n=findname(dialnamebuff))!=-1)
  {
   if(!dir[n].fix.group) addgrouphimenu(dir[n].fix.name);
  }
  else                   errorbox("{DIR04}");
 }
 else
 if(m3>0 && m3<=dirtot)
 {
  m3--;
  i=0;
  for(n=0;n<dirtot;n++)
  {
   if(dir[n].fix.show)
   {
    if(i==m3)
    {
     strcpy(dialnamebuff,dir[n].fix.name);
     if(!dir[n].fix.group) addgrouphimenu(dir[n].fix.name);
     break;
    }
    else
    {
     i++;
    }
   }
  }
 }
}

/***************************************************************************/

static entryfn entryf;


void decodeentrymenu(int m3)
{
 int    n;
 int    i;

 if(m3>=0 && m3<=dirtot)
 {
  /* dial entry */

  i=0;

  for(n=0;n<dirtot;n++)
  {
   if(dir[n].fix.show)
   {
    if(i==m3)
    {
     entryf(dir[n].fix.name);
     break;
    }
    else
    {
     i++;
    }
   }
  }
 }
}



void openentrymenu(entryfn ex)
{
 if(dirtot)
 {
  entryf=ex;
  clearusermenus();
  setupentrymenu(0,NULL,0);
  popmenu(entry_menu);
 }
}


/**************************************************************************/

int loadcsv(char * name)
{
 FILE   * fp;
 csvstr * csv;
 entry    e;
 int      i;
 int      j;

 fp=ropen(name,"rb");
 if(fp)
 {
  while(!csvgetrecord(fp,&csv))
  {
   if(!csv) break;

   blankentrysub(&e);

   if(csv->records>0) strcpy(e.fix.name,csv->record[0]);
   if(csv->records>1) strcpy(e.fix.number,csv->record[1]);

   for(j=0;j<NSTRING;j++)
   {
    if(csv->records>(j+2)) strcpy(e.strings[j],csv->record[2+j]);
   }

   if(csv->records>(2+NSTRING+0))
       e.fix.show=!cstrcmp("Menu",csv->record[2+NSTRING+0]);
   if(csv->records>(2+NSTRING+1))
       e.fix.prefix=!cstrcmp("Prefix",csv->record[2+NSTRING+1]);
   if(csv->records>(2+NSTRING+2))
       e.fix.nopw=!cstrcmp("NoPassword",csv->record[2+NSTRING+2]);
   if(csv->records>(2+NSTRING+3))
       e.fix.bar=!cstrcmp("Bar",csv->record[2+NSTRING+3]);
   if(csv->records>(2+NSTRING+4))
       e.fix.block=!cstrcmp("Block",csv->record[2+NSTRING+4]);
   if(csv->records>(2+NSTRING+5))
       e.fix.remoteid=!cstrcmp("RemoteID",csv->record[2+NSTRING+5]);
   if(csv->records>(2+NSTRING+6))
       e.fix.avoice=!cstrcmp("Voice",csv->record[2+NSTRING+6]);
   if(csv->records>(2+NSTRING+7))
       e.fix.afax=!cstrcmp("Fax",csv->record[2+NSTRING+7]);
   if(csv->records>(2+NSTRING+8))
       e.fix.adata=!cstrcmp("Data",csv->record[2+NSTRING+8]);
   if(csv->records>(2+NSTRING+9))
       e.fix.answer=!cstrcmp("Answer",csv->record[2+NSTRING+9]);
   if(csv->records>(2+NSTRING+10))
       e.fix.server=!cstrcmp("Server",csv->record[2+NSTRING+10]);
   if(csv->records>(2+NSTRING+11))
       e.fix.priority=prioritynametopriority(csv->record[2+NSTRING+11]);
   if(csv->records>(2+NSTRING+12))
       e.fix.check=!cstrcmp("Check",csv->record[2+NSTRING+12]);

   i=findname(e.fix.name);

   if(i<0) 
   {
    i=createentry();
    e.fix.id=dir[i].fix.id;
   }

   if(i>=0)
   {
    entrytodirsub(i,&e);
    dirextent();
    refreshdirentry(i);
   }

   flex_free((flex_ptr)&csv);
  }
  rclose(fp);
  moddir();
 }
 return(1);
}


#define TDRECORDS (2+NSTRING+13)

int savecsv(char * name)
{
 FILE   * fp;
 int      i;
 int      j;
 entry    e;
 csvstr * csv;
 char     temp[sizeof(csvstr)+(TDRECORDS-1)*sizeof(char*)];

 char     prefix[12];
 char     menu[12];
 char     pw[12];

 char     bar[12];
 char     block[12];
 char     rid[12];
 char     voice[12];
 char     fax[12];
 char     data[12];
 char     answer[12];
 char     server[12];
 char     priority[16];
 char     check[12];

 csv=(csvstr*)temp;

 fp=csvopen(name,"wb");
 if(fp)
 {
  for(i=0;i<dirtot;i++)
  {
   if(!dir[i].fix.group)
   {
    dirtoentrysub(&e,i);

    csv->records=TDRECORDS;
    csv->record[0]=e.fix.name;
    csv->record[1]=e.fix.number;
    for(j=0;j<NSTRING;j++) csv->record[2+j]=e.strings[j];

    csv->record[2+NSTRING+0]=menu;
    csv->record[2+NSTRING+1]=prefix;
    csv->record[2+NSTRING+2]=pw;

    csv->record[2+NSTRING+3]=bar;
    csv->record[2+NSTRING+4]=block;
    csv->record[2+NSTRING+5]=rid;
    csv->record[2+NSTRING+6]=voice;
    csv->record[2+NSTRING+7]=fax;
    csv->record[2+NSTRING+8]=data;
    csv->record[2+NSTRING+9]=answer;
    csv->record[2+NSTRING+10]=server;
    csv->record[2+NSTRING+11]=priority;
    csv->record[2+NSTRING+12]=check;

    sprintf(menu,"%s",e.fix.show?"Menu":"");
    sprintf(prefix,"%s",e.fix.prefix?"Prefix":"");
    sprintf(pw,"%s",e.fix.nopw?"NoPassword":"Password");

    sprintf(bar,"%s",e.fix.bar?"Bar":"");
    sprintf(block,"%s",e.fix.bar?"Block":"");
    sprintf(rid,"%s",e.fix.remoteid?"RemoteID":"");
    sprintf(voice,"%s",e.fix.avoice?"Voice":"");
    sprintf(fax,"%s",e.fix.afax?"Fax":"");
    sprintf(data,"%s",e.fix.adata?"Data":"");
    sprintf(answer,"%s",e.fix.answer?"Answer":"Originate");
    sprintf(server,"%s",e.fix.server?"Server":"");
    strcpy(priority,priorityname(e.fix.priority));
    sprintf(check,"%s",e.fix.check?"Check":"");

    csvputrecord(fp,csv);
   }
  }
  csvclose(fp,name,1);
 }
 return(1);
}


/*****************************************************************************/

/* retun 1 if server handles call */

int txserver(int mode,int id,int * code)
{
 int    i;
 char   com1[SCOMMANDLEN];
 char   com2[SCOMMANDLEN];
 char   name[SNAMELEN];
 char * p;


 if(id)
 {
  for(i=0;i<dirtot;i++) if(dir[i].fix.id==id) break;
  if(i<dirtot)
  {
   if(dir[i].fix.server)
   {
    p=dnthstring(i,12);

    strcpy(name,p);
    p+=strlen(p)+1;
    strcpy(com1,p);
    p+=strlen(p)+1;
    strcpy(com2,p);

    *code=runserver(mode,name,com1,com2); 
    return(1);
   }
  }
 }
 return(0);
}

