#include "Include.h"

NOSHARE char *pathname[] =
{
   // EmacsDLG:
   // pathname[0] is config file
   // pathname[1] is currently unused
   // pathname[2] is set to the config directory
   // pathname[3] is the current directory
   // pathname[3] gets filled in later with emacsxl config directory
   // actually, it's pathname[2] for emacsdlg -- not using ram anymore
   // "EmacsXL.CFG", "EmacsXL.RC",  "RAM:",  NULL, "",
   "ScreenEdit.CFG",
   NULL,
   "DLGconfig:Misc/",
   ""
};


#define  NPNAMES  (sizeof(pathname)/sizeof(char *))


// internal functions

char *STD namval(int);
int  (STD *STD getname(char *))(int, int);
int   STD ostring(char *);
int   STD unbindchar(int);
int       doxenorc(void);
unsigned int STD getckey(int);


/// macrotokey()
/* macrotokey: Bind a key to a macro in the key binding table
 ***********************************************************************/
STD macrotokey(f, n)
{
   register unsigned int c;/* command key to bind */
   register BUFFER *kmacro;/* ptr to buffer of macro to bind to key */
   register KEYTAB *ktp;   /* pointer into the command table */
   register int found;  /* matched command flag */
   register int status; /* error return */
   char outseq[80];  /* output buffer for keystroke sequence */
   char bufn[NBUFN]; /* buffer to hold macro name */

   /* get the buffer name to use */
   if ((status=mlreply(TEXT215, &bufn[1], NBUFN-2)) != TRUE)
/*    ": macro-to-key " */
      return(status);

   /* build the responce string for later */
   strcpy(outseq, TEXT215);
/*       ": macro-to-key " */
   strcat(outseq, &bufn[1]);

   /* translate it to a buffer pointer */
   bufn[0] = '[';
   strcat(bufn, "]");
   if ((kmacro=bfind(bufn, FALSE, 0)) == NULL) {
      mlwrite(TEXT130);
/*    "Macro not defined"*/
      return(FALSE);
   }

   strcat(outseq, " ");
   mlwrite(outseq);

   /* get the command sequence to bind */
   c = getckey(FALSE);

   /* change it to something we can print as well */
   cmdstr(c, &outseq[0]);

   /* and dump it out */
   ostring(outseq);

   /* search the table to see if it exists */
   ktp = &keytab[0];
   found = FALSE;
   while (ktp->k_type != BINDNUL) {
      if (ktp->k_code == c) {
         found = TRUE;
         break;
      }
      ++ktp;
   }

   if (found) {   /* it exists, just change it then */
      ktp->k_ptr.buf = kmacro;
      ktp->k_type = BINDBUF;
   } else { /* otherwise we need to add it to the end */
      /* if we run out of binding room, bitch */
      if (ktp >= &keytab[NBINDS]) {
         mlwrite(TEXT17);
/*          "Binding table FULL!" */
         return(FALSE);
      }

      ktp->k_code = c;  /* add keycode */
      ktp->k_ptr.buf = kmacro;   /* and the function pointer */
      ktp->k_type = BINDBUF;  /* and the binding type */
      ++ktp;         /* and make sure the next is null */
      ktp->k_code = 0;
      ktp->k_type = BINDNUL;
      ktp->k_ptr.fp = NULL;
   }

   return(TRUE);
}
//-

/// unbindkey()
/*
** unbindkey:  delete a key from the key binding table
 ***********************************************************************/
STD unbindkey(f, n)
{
   register int c;   /* command key to unbind */
   char outseq[80];  /* output buffer for keystroke sequence */

   /* prompt the user to type in a key to unbind */
   mlwrite(TEXT18);
/*    ": unbind-key " */

   /* get the command sequence to unbind */
   c = getckey(FALSE);     /* get a command sequence */

   /* change it to something we can print as well */
   cmdstr(c, &outseq[0]);

   /* and dump it out */
   ostring(outseq);

   /* if it isn't bound, bitch */
   if (unbindchar(c) == FALSE) {
      mlwrite(TEXT19);
/*       "[Key not bound]" */
      return(FALSE);
   }
   return(TRUE);
}
//-

/// flook()
/* Look up the existance of a file along the normal or PATH
** environment variable.
**
** LOOKUP ORDER:
**
** if contains path:
**
**    absolute
**
** else
**
**    HOME environment directory
**    all directories along PATH environment
**    directories in table from EPATH.H
**
** char *fname;   base file name to search for
** int hflag;     Look in the HOME environment variable first?
 ***********************************************************************/
char *STD flook(char *fname, int hflag)
{
   register char *sp;   /* pointer into path spec */
   register int i;   /* index */
   static char fspec[NFILEN]; /* full path spec to search */
   char *getenv();

   /* if we have an absolute path.. check only there! */
   sp = fname;
   while (*sp) {
      if (*sp == ':' || *sp == '\\' || *sp == '/') {
         if (Exists(fname)) {
            return(fname);
         } else
            return(NULL);
      }
      ++sp;
   }


   /* look it up via the old table method */
   for (i=2; i < NPNAMES; i++) {
      strcpy(fspec, pathname[i]);
      strcat(fspec, fname);

      /* and try it out */
      if (Exists(fspec)) {
         return(fspec);
      }
   }
   return(NULL);  /* no such luck */
}
//-

/// cmdstr()
/* change a key command to a string we can print out
**
** int c;         sequence to translate
** char *seq;     destination string for sequence
 ******************************************************/
STD cmdstr(int c, char *seq)
{
   char *ptr;  /* pointer into current position in sequence */

   ptr = seq;

   /* apply ^X sequence if needed */
   if (c & CTLX) {
      *ptr++ = '^';
      *ptr++ = 'X';
   }


   /* apply meta sequence if needed */
   if (c & META) {
      *ptr++ = 'M';
      *ptr++ = '-';
   }

   /* apply control sequence if needed */
   if (c & CTRL) {
      *ptr++ = '^';
   }

   c = c & 255;   /* strip the prefixes */

   /* and output the final sequence */

   *ptr++ = c;
   *ptr = 0;   /* terminate the string */

   return(0);
}
//-

/// getbind()
/* This function looks a key binding up in the binding table
**
** register int c;      key to find what is bound to it
********************/
KEYTAB *getbind(register int c)
{
   register KEYTAB *ktp;

   /* scan through the binding table, looking for the key's entry */
   ktp = &keytab[0];
   while (ktp->k_type != BINDNUL) {
      if (ktp->k_code == c)
         return(ktp);
      ++ktp;
   }

   /* no such binding */
   return((KEYTAB *)NULL);
}
//-

/// getfname()
/* getfname:   This function takes a ptr to KEYTAB entry and gets the name
      associated with it

   KEYTAB *key;   key binding to return a name of
****************/
char *STD getfname(KEYTAB *key)
{
   int (* STD func)(); /* ptr to the requested function */
   register NBIND *nptr;   /* pointer into the name binding table */
   register BUFFER *bp; /* ptr to buffer to test */
   register BUFFER *kbuf;  /* ptr to requested buffer */

   /* if this isn't a valid key, it has no name */
   if (key == NULL)
      return(NULL);

   /* skim through the binding table, looking for a match */
   if (key->k_type == BINDFNC) {
      func = key->k_ptr.fp;
      nptr = &names[0];
      while (nptr->n_func != NULL) {
         if (nptr->n_func == func)
            return(nptr->n_name);
         ++nptr;
      }
      return(NULL);
   }

   /* skim through the buffer list looking for a match */
   kbuf = key->k_ptr.buf;
   bp = bheadp;
   while (bp) {
      if (bp == kbuf)
         return(bp->b_bname);
      bp = bp->b_bufp;
   }
   return(NULL);
}
//-

/// fncmatch()
/* fncmatch:   match fname to a function in the names table and return
**    any match or NULL if none
**
** char *fname;   name to attempt to match
*****************/
int (*STD fncmatch(char *fname))()
{
   int nval;

   if ((nval = binary(fname, namval, numfunc)) == -1)
      return(NULL);
   else
      return(names[nval].n_func);
}
//-

/// stock()
/*
** stock()  String key name TO Command Key
**
** A key binding consists of one or more prefix functions followed by
** a keystroke.  Allowable prefixes must be in the following order:
**
** ^X preceeding control-X
** A- similtaneous ALT key (on PCs mainly)
** S- shifted function key
** MS mouse generated keystroke
**  M- Preceding META key
** FN function key
** ^  control key
**
** Meta and ^X prefix of lower case letters are converted to upper
** case.  Real control characters are automatically converted to
** the ^A form.
**
** char *keyname;    name of key to translate to Command key form
 ***********************************************************************/
unsigned int STD stock(char *keyname)
{
   register unsigned int c;   /* key sequence to return */

   /* parse it up */
   c = 0;

   /* Do ^X prefix */
   if(*keyname == '^' && *(keyname+1) == 'X') {
      if(*(keyname+2) != 0) { /* Key is not bare ^X */
          c |= CTLX;
          keyname += 2;
      }
   }

   /* then the META prefix */
   if (*keyname == 'M' && *(keyname+1) == '-') {
      c |= META;
      keyname += 2;
   }


   /* a control char?  (Always upper case) */
   if (*keyname == '^' && *(keyname+1) != 0) {
      c |= CTRL;
      ++keyname;
      uppercase(keyname);
   }

   /* A literal control character? (Boo, hiss) */
   if (*keyname < 32) {
      c |= CTRL;
      *keyname += '@';
   }

   /* make sure we are not lower case if used with ^X or M- */
   if(!(c & (MOUS|SPEC|ALTD|SHFT))) /* If not a special key */
       if( c & (CTLX|META))      /* If is a prefix */
      uppercase(keyname);     /* Then make sure it's upper case */

   /* the final sequence... */
   c |= *keyname;
   return(c);
}
//-

/// execkey()
/* execute a function bound to a key
**
** KEYTAB *key;      key to execute
** int f, n;         agruments to C function
******************/
int STD execkey(KEYTAB *key, int f, int n)
{
   register int status; /* error return */

   if (key->k_type == BINDFNC)
      return((*(key->k_ptr.fp))(f, n));
   if (key->k_type == BINDBUF) {
      while (n--) {
         status = dobuf(key->k_ptr.buf);
         if (status != TRUE)
            return(status);
      }
   }
   return(TRUE);
}
//-

/// bindtokey
/* bindtokey:  add a new key to the key binding table
**
 *****************************************************/
STD bindtokey(int f, int n)
{
   register unsigned int c;/* command key to bind */
   register int (* STD kfunc)();/* ptr to the requested function to bind to */
   register KEYTAB *ktp;   /* pointer into the command table */
   register int found;  /* matched command flag */
   char outseq[80];  /* output buffer for keystroke sequence */

   /* prompt the user to type in a key to bind */
   /* get the function name to bind it to */
   kfunc = getname(TEXT15);
/*       ": bind-to-key " */
   if (kfunc == NULL) {
      mlwrite(TEXT16);
/*       "[No such function]" */
      return(FALSE);
   }
   if (clexec == FALSE) {
      ostring(" ");
      TTflush();
   }

   /* get the command sequence to bind */
   c = getckey((kfunc == meta) || (kfunc == cex) || (kfunc == ctrlg));

   if (clexec == FALSE) {

      /* change it to something we can print as well */
      cmdstr(c, &outseq[0]);

      /* and dump it out */
      ostring(outseq);
   }

   /* if the function is a unique prefix key */
   if (kfunc == ctrlg || kfunc == quote) {

      /* search for an existing binding for the prefix key */
      ktp = &keytab[0];
      while (ktp->k_type != BINDNUL) {
         if (ktp->k_ptr.fp == kfunc)
            unbindchar(ktp->k_code);
         ++ktp;
      }

      /* reset the appropriate global prefix variable */
      if (kfunc == ctrlg)
         abortc = c;
      if (kfunc == quote)
         quotec = c;
   }

   /* search the table to see if it exists */
   ktp = &keytab[0];
   found = FALSE;
   while (ktp->k_type != BINDNUL) {
      if (ktp->k_code == c) {
         found = TRUE;
         break;
      }
      ++ktp;
   }

   if (found) {   /* it exists, just change it then */
      ktp->k_ptr.fp = kfunc;
      ktp->k_type = BINDFNC;
   } else { /* otherwise we need to add it to the end */
      /* if we run out of binding room, bitch */
      if (ktp >= &keytab[NBINDS]) {
         mlwrite(TEXT17);
/*          "Binding table FULL!" */
         return(FALSE);
      }

      ktp->k_code = c;  /* add keycode */
      ktp->k_ptr.fp = kfunc;  /* and the function pointer */
      ktp->k_type = BINDFNC;  /* and the binding type */
      ++ktp;         /* and make sure the next is null */
      ktp->k_code = 0;
      ktp->k_type = BINDNUL;
      ktp->k_ptr.fp = NULL;
   }

   /* if we have rebound the meta key, make the
      search terminator follow it         */
   if (kfunc == meta)
      sterm = c;

   return(TRUE);
}
//-

/// startup()
/*
** execute the startup file
**
** char *sfname;     name of startup file (NULL if default)
**
 ***********************************************************************/
STD startup(char *sfname)
{
    char *fname;  /* resulting file name to execute */
/* XENO_EMACS changed this to do two startup files */
    int i;
    int result;

      for (i=0; i<1; ++i)
      {
       /* look up the startup file */
         fname = flook(pathname[i], TRUE);

         if (fname == NULL) break; // continue, then do xenorc.

          /* otherwise, execute the sucker */
         cfg_fname = fname; /* save filename for do_fortune parsing */
         result = dofile(fname);
         if (result != TRUE) return result;
      }
   // JUST FINISHED EmacsDLG.CFG, now do user EmacsDLG.OPT
      exec_user_opts();
    // do built-in rc file

    if (doxenorc() != TRUE) return FALSE;
    return TRUE;
}
//-


// internal functions

/// namval()
/*
** int index;  index of name to fetch out of the name table
**************/
char *STD namval(int index)
{
   return(names[index].n_name);
}
//-

/// getname()
/* get a command name from the command line. Command completion means
** that pressing a <SPACE> will attempt to complete an unfinished command
** name if it is unique.
**
** char *prompt;     string to prompt with
*******************/
int (*STD getname(char *prompt))()
{
   char *sp;   /* ptr to the returned string */

   sp = complete(prompt, NULL, CMP_COMMAND, NSTRING, 0);
   if (sp == NULL)
      return(NULL);

   return(fncmatch(sp));
}
//-

/// ostring()
/*
** output a string of output characters
**
** char *s;    string to output
 ***********************************************************************/
STD ostring(char *s)
{
   if (discmd)
      while (*s) mlout(*s++);

   return(1);
}
//-

/// unbindchar()
STD unbindchar(int c)
{
   register KEYTAB *ktp;   /* pointer into the command table */
   register KEYTAB *sktp;  /* saved pointer into the command table */
   register int found;  /* matched command flag */

   /* search the table to see if the key exists */
   ktp = &keytab[0];
   found = FALSE;
   while (ktp->k_type != BINDNUL) {
      if (ktp->k_code == c) {
         found = TRUE;
         break;
      }
      ++ktp;
   }

   /* if it isn't bound, bitch */
   if (!found)
      return(FALSE);

   /* save the pointer and scan to the end of the table */
   sktp = ktp;
   while (ktp->k_type != BINDNUL)
      ++ktp;
   --ktp;      /* backup to the last legit entry */

   /* copy the last entry to the current one */
   sktp->k_code = ktp->k_code;
   sktp->k_type = ktp->k_type;
   sktp->k_ptr.fp  = ktp->k_ptr.fp;

   /* null out the last one */
   ktp->k_code = 0;
   ktp->k_type = BINDNUL;
   ktp->k_ptr.fp = NULL;
   return(TRUE);
}
//-

/// getckey()
/*
** get a command key sequence from the keyboard
**
** int mflag;  going for a meta sequence?
 ***********************************************************************/
unsigned int STD getckey(int mflag)
{
   register unsigned int c;   /* character fetched */
   char tok[NSTRING];      /* command incoming */

   /* check to see if we are executing a command line */
   if (clexec) {
      macarg(tok);   /* get the next token */
      return(stock(tok));
   }

   /* or the normal way */
   if (mflag)
      c = getkey();
   else
      c = getcmd();
   return(c);
}
//-

/// doxenorc()
/* Execute the built-in EmacsXL.RC file.
 ***********************************************************************/
int doxenorc(void)
{
   BUFFER *bp;
   if ((bp = init_xenorc()) == NULL) return FALSE;
   return dobuf(bp);
}
//-



