/*
 * This program is in public domain; written by Dave G. Conroy.
 * This file contains the main driving routine, and some keyboard processing
 * code, for the MicroEMACS screen editor.
 *
 * REVISION HISTORY:
 *
 * 1.0  Steve Wilhite, 30-Nov-85
 *      - Removed the old LK201 and VT100 logic. Added code to support the
 *        DEC Rainbow keyboard (which is a LK201 layout) using the the Level
 *        1 Console In ROM INT. See "rainbow.h" for the function key definitions.
 *
 * 2.0  George Jones, 12-Dec-85
 *      - Ported to Amiga.
 *
 * m2emacs 1.0 ms, 3-Jun-87
 *      Supports Amiga Modula-2 Error Handling, german and english versions
 */

#include <stdio.h>
#include <exec/types.h>
#include <libraries/dos.h>
#include <clib/dos_protos.h>
#include <clib/exec_protos.h>
#include <clib/intuition_protos.h>
#include <clib/dos_pragmas.h>
#include <clib/exec_pragmas.h>
#include <clib/intuition_pragmas.h>
#include "ed.h"

extern char *calloc(), *title1, *title2, *version, *usage1, *usage2;

#ifndef GOOD
#define GOOD    0
#endif

long    newLock;                        /* used if started from WB      */
short   currow;                         /* Working cursor row           */
short   curcol;                         /* Working cursor column        */
short   autoindent;                     /* Auto indentation flag        */
short   casesens;                       /* Compare case senitive        */
short   icon;                           /* Create icons for output      */
short   keepBackup;                     /* Create backup copy           */
short   stripline;                      /* Clean trailing ' ' & '\t'    */
short   requester;                      /* Use FileRequester for input  */
short   displaytab;                     /* Display Tab as '»' in text   */
short   doCD;
long    origDir;
int     thisflag;                       /* Flags, this command          */
int     lastflag;                       /* Flags, last command          */
int     curgoal;                        /* Goal column                  */
BUFFER  *curbp;                         /* Current buffer               */
WINDOW  *curwp;                         /* Current window               */
BUFFER  *bheadp;                        /* BUFFER listhead              */
WINDOW  *wheadp;                        /* WINDOW listhead              */
BUFFER  *blistp;                        /* Buffer list BUFFER           */
BUFFER  *prev_bp;                       /* Previous Buffer              */
short   kbdm[NKBDM] = {CTLX|')'};       /* Macro                        */
short   *kbdmip;                        /* Input  for above             */
short   *kbdmop;                        /* Output for above             */
char    pat[NPAT];                      /* Pattern                      */
char    reppat[NPAT];                   /* replace pattern              */
short   interlace;

typedef struct  {
        short   k_code;                 /* Key code                     */
        int     (*k_fp)();              /* Routine to handle it         */
}       KEYTAB;

extern  int     delline();              /* delete line                  */
extern  int     ctrlg();                /* Abort out of things          */
extern  int     quit();                 /* Quit                         */
extern  int     ctlxlp();               /* Begin macro                  */
extern  int     ctlxrp();               /* End macro                    */
extern  int     ctlxe();                /* Execute macro                */
extern  int     fileread();             /* Get a file, read only        */
extern  int     filevisit();            /* Get a file, read write       */
extern  int     filewrite();            /* Write a file                 */
extern  int     filesave();             /* Save current file            */
extern  int     filename();             /* Adjust file name             */
extern  int     fileyank();             /* Yank buffer to a file        */
extern  int     fileinsert();           /* Get a file, insert before dot*/
extern  int     getccol();              /* Get current column           */
extern  int     gotobol();              /* Move to start of line        */
extern  int     forwchar();             /* Move forward by characters   */
extern  int     gotoeol();              /* Move to end of line          */
extern  int     backchar();             /* Move backward by characters  */
extern  int     forwline();             /* Move forward by lines        */
extern  int     backline();             /* Move backward by lines       */
extern  int     forwpage();             /* Move forward by pages        */
extern  int     backpage();             /* Move backward by pages       */
extern  int     gotobob();              /* Move to start of buffer      */
extern  int     gotoeob();              /* Move to end of buffer        */
extern  int     toggleautoindent();     /* Toggle auto indentation      */
extern  int     togglecasesens();       /* Toggle Case Sens             */
extern  int     toggleicon();           /* Toggle icon generation       */
extern  int     togglebackup();         /* Toggle keeping backups       */
extern  int     togglestripline();      /* Toggle del of trailing ' '   */
extern  int     toggledisplaytab();     /* Toggle display of tabs as '»'*/
extern  int     setmark();              /* Set mark                     */
extern  int     swapmark();             /* Swap "." and mark            */
extern  int     forwsearch();           /* Search forward               */
extern  int     backsearch();           /* Search backwards             */
extern  int     qreplace();             /* Query Replace                */
extern  int     showcpos();             /* Show the cursor position     */
extern  int     nextwind();             /* Move to the next window      */
extern  int     prevwind();             /* Move to the previous window  */
extern  int     onlywind();             /* Make current window only one */
extern  int     splitwind();            /* Split current window         */
extern  int     closewind();            /* Close current window         */
extern  int     mvdnwind();             /* Move window down             */
extern  int     mvupwind();             /* Move window up               */
extern  int     moveline();             /* Move to a line number        */
extern  int     enlargewind();          /* Enlarge display window.      */
extern  int     shrinkwind();           /* Shrink window.               */
extern  int     listbuffers();          /* Display list of buffers      */
extern  int     usebuffer();            /* Switch a window to a buffer  */
extern  int     killbuffer();           /* Make a buffer go away.       */
extern  int     reposition();           /* Reposition window            */
extern  int     refresh();              /* Refresh the screen           */
extern  int     twiddle();              /* Twiddle characters           */
extern  int     tab();                  /* Insert tab                   */
/*extern  int     newline();               Insert CR-LF                 */
extern  int     indent();               /* Insert CR-LF, then indent    */
extern  int     openline();             /* Open up a blank line         */
extern  int     deblank();              /* Delete blank lines           */
extern  int     quote();                /* Insert literal               */
extern  int     backword();             /* Backup by words              */
extern  int     forwword();             /* Advance by words             */
extern  int     forwdel();              /* Forward delete               */
extern  int     backdel();              /* Backward delete              */
extern  int     kill();                 /* Kill forward                 */
extern  int     kill0();                /* Kill from start to dot       */
extern  int     yank();                 /* Yank back from killbuffer.   */
extern  int     upperword();            /* Upper case word.             */
extern  int     lowerword();            /* Lower case word.             */
extern  int     upperregion();          /* Upper case region.           */
extern  int     lowerregion();          /* Lower case region.           */
extern  int     capword();              /* Initial capitalize word.     */
extern  int     delfword();             /* Delete forward word.         */
extern  int     delbword();             /* Delete backward word.        */
extern  int     killregion();           /* Kill region.                 */
extern  int     copyregion();           /* Copy region to kill buffer.  */
extern  int     eraseregion();          /* Erase region                 */
extern  int     spawncli();             /* Run CLI in a subjob.         */
extern  int     spawn();                /* Run a command in a subjob.   */
extern  int     quickexit();            /* low keystroke style exit.    */
extern  int     ReadErrDat();           /* Modula-2 Error File Reader   */
extern  int     ReadErrMsg();           /* Modula-2 Error Msgs Reader   */
extern  int     NextM2Error();          /* Display next Modula-2 Error  */
extern  int     join();                 /* Join two lines */
extern  int     export();
extern  int     changedir();
extern  int     call_ohm();
extern  int     ohm_project();

/*
 * Command table.
 * This table  is *roughly* in ASCII order, left to right across the
 * characters of the command. This expains the funny location of the
 * control-X commands.
 */
KEYTAB  keytab[] = {
        CTRL|'@',               &setmark,
        CTRL|'A',               &gotobol,
        CTRL|'B',               &backchar,
        CTRL|'C',               &spawncli,      /* Run CLI in subjob.   */
        CTRL|'D',               &forwdel,
        CTRL|'E',               &gotoeol,
        CTRL|'F',               &forwchar,
        CTRL|'G',               &ctrlg,
        CTRL|'H',               &backdel,
        CTRL|'I',               &tab,
        CTRL|'J',               &indent,
        CTRL|'K',               &kill,
        CTRL|'L',               &refresh,
        CTRL|'M',               &indent,
        CTRL|'N',               &forwline,
        CTRL|'O',               &openline,
        CTRL|'P',               &backline,
        CTRL|'Q',               &quote,         /* Often unreachable    */
        CTRL|'R',               &backsearch,
        CTRL|'S',               &forwsearch,    /* Often unreachable    */
        CTRL|'T',               &twiddle,
        CTRL|'V',               &forwpage,
        CTRL|'W',               &killregion,
        CTRL|'Y',               &yank,
        CTRL|'Z',               &quickexit,     /* quick save and exit  */
        CTRL|'\\',              &export,
        CTLX|CTRL|'B',          &listbuffers,
        CTLX|CTRL|'C',          &quit,          /* Hard quit.           */
        CTLX|CTRL|'D',          &delline,       /* delete line          */
        CTLX|CTRL|'E',          &eraseregion,
        CTLX|CTRL|'F',          &filename,
        CTLX|CTRL|'I',          &fileinsert,
        CTLX|CTRL|'J',          &join,
        CTLX|CTRL|'K',          &kill0,
        CTLX|CTRL|'L',          &lowerregion,
        CTLX|CTRL|'M',          &NextM2Error,
        CTLX|CTRL|'N',          &mvdnwind,
        CTLX|CTRL|'P',          &mvupwind,
        CTLX|CTRL|'R',          &fileread,
        CTLX|CTRL|'S',          &filesave,      /* Often unreachable    */
        CTLX|CTRL|'T',          &toggledisplaytab,
        CTLX|CTRL|'U',          &upperregion,
        CTLX|CTRL|'V',          &filevisit,
        CTLX|CTRL|'W',          &filewrite,
        CTLX|CTRL|'X',          &swapmark,
        CTLX|CTRL|'Z',          &shrinkwind,
        CTLX|'!',               &spawn,         /* Run 1 command.       */
        CTLX|'=',               &showcpos,
        CTLX|'(',               &ctlxlp,
        CTLX|')',               &ctlxrp,
        CTLX|'0',               &closewind,
        CTLX|'1',               &onlywind,
        CTLX|'2',               &splitwind,
        CTLX|'B',               &usebuffer,
        CTLX|'C',               &togglecasesens,
        CTLX|'D',               &changedir,
        CTLX|'E',               &ctlxe,
        CTLX|'H',               &call_ohm,
        CTLX|'I',               &toggleautoindent,
        CTLX|'K',               &killbuffer,
        CTLX|'M',               &ReadErrDat,
        CTLX|'N',               &nextwind,
        CTLX|'P',               &prevwind,
        CTLX|'O',               &ohm_project,
        CTLX|'R',               &togglerequester,
        CTLX|'S',               &togglebackup,
        CTLX|'T',               &togglestripline,
        CTLX|'W',               &toggleicon,
        CTLX|'Z',               &enlargewind,
        META|CTRL|'H',          &delbword,
        META|'!',               &reposition,
        META|'.',               &setmark,
        META|'>',               &gotoeob,
        META|'<',               &gotobob,
        META|'B',               &backword,
        META|'C',               &capword,
        META|'D',               &delfword,
        META|'F',               &forwword,
        META|'H',               &reposition,
        META|'L',               &lowerword,
        META|'M',               &moveline,
        META|'U',               &upperword,
        META|'V',               &backpage,
        META|'W',               &copyregion,
        META|'Y',               &fileyank,
        META|'%',               &qreplace,
        META|0x7F,              &delbword,
        0x7F,                   &backdel
};

#define NKEYTAB (sizeof(keytab)/sizeof(keytab[0]))

#define CSI 0x9B
#include <exec/types.h>
#include <workbench/startup.h>

/*
 * -------------------------------------------------------------- *
 *     As long as we cannot depend on 2.0 being available
 *     we have to implement the 2.0 functionality ourselves.
 * -------------------------------------------------------------- *
 */
LONG GetEnvM2(UBYTE *name, UBYTE *buffer, LONG size, ULONG flags)
{
  char   envName[64];
  BPTR   f;
  LONG   len;

  len = -1;
  strcpy(envName, "ENV:");
  strcat(envName, name);
  if ( (f = Open(envName, MODE_OLDFILE) ) != (BPTR)NULL) {
    len = Read(f, buffer, size-1);
    if (len < 0)
      *buffer = 0;
    else
      *(buffer+len) = 0;
    Close(f);
  }
  return len;
}

/*
 * -------------------------------------------------------------- *
 * copy a 'String' from the <from> array into the <to> array.
 * If <fromEnv> spaces are allowed only in quoted strings. From
 * the command line the quotes habe been removed by _main().
 * -------------------------------------------------------------- *
 */
static int GetString(char *to, char *from, int fromEnv)
{
  int cnt      = 0;
  int inString = FALSE;

  if (fromEnv == FALSE) {
   /*
    * Standard C string copy, with counting
    * of the characters for command line.
    */
    while (*to++ = *from++)
      cnt++;
  }
  else {
    while ((*from != '\0') && ((inString == TRUE) || (*from != ' ') )) {
#ifdef DEBUG
      printf("gs: %c %d\n", *from, (int)*from);
#endif
      if (*from == '\047') {
        inString = ((inString == TRUE) ? FALSE : TRUE);
      }
      else {
        *to++ = *from;
      }
      from++;
      cnt ++;
    }
    *to = '\0';
  }
  return cnt;
}

/*
 * -------------------------------------------------------------- *
 * Read the option string and try to figure out the callers
 * wishes. This routine needs to know wheter the data comes
 * from the environment or the command line.
 * -------------------------------------------------------------- *
 */
void ReadOptionString(char *optStr, int fromEnv)
{
  char  action    = '-';
  BOOL  cont      = TRUE;
  char  tmp[64], tmp2[64];

#ifdef DEUTSCH
#define ILL_OPT "Unbekannte Option %c"
#define ILL_DIR "<%s> nicht gefunden"
#else
#define ILL_OPT "Unknown Option %c"
#define ILL_DIR "<%s> not found"
#endif

  while (*optStr && cont) {

    if (*optStr == ' ') /* --- ignore spaces --- */;
    else if (*optStr  == '-' || *optStr == '+') { action = *optStr; }
    else if (cap(*optStr) == 'B') { keepBackup = (action == '+'); }
    else if (cap(*optStr) == 'C') { stripline  = (action == '+'); }
    else if (cap(*optStr) == 'D') { displaytab = (action == '+'); }
    else if (cap(*optStr) == 'G') {
      optStr++;
      optStr += GetString(tmp, optStr, fromEnv);
      origDir = (long)Lock(tmp, SHARED_LOCK);
      if (origDir) {
        origDir = (long)CurrentDir(origDir);
        doCD = TRUE;
      }
      else {
        sprintf(tmp2, ILL_DIR , tmp);
        cont = Error(tmp2, TRUE);
      }
      continue;
    }
    else if (cap(*optStr) == 'I') { icon       = (action == '+'); }
    else if (cap(*optStr) == 'L') { interlace  = (action == '+'); }
    else if (cap(*optStr) == 'P') {
      optStr++;
      optStr += GetString(ohmProject, optStr, fromEnv);
      continue;
    }
    else if (cap(*optStr) == 'R') { requester  = (action == '+'); }
    else if (cap(*optStr) == 'T') {
      tabsize = 0;
      for (;;) {
        optStr++;
        if ( isdigit(*optStr) )
          tabsize = 10*tabsize + (*optStr - '0');
        else
          break;
      }
      optStr--;
    }
    else {
      sprintf(tmp, ILL_OPT, *optStr);
      cont = Error(tmp, TRUE);
    }
    optStr++;
  }
  if (cont == FALSE) {
    if (doCD) UnLock(CurrentDir((BPTR)origDir));
    exit(1);
  }
}


main(argc, argv)
char    *argv[];
{
  register int      c, f, n, mflag;
  struct WBStartup  *WBenchMsg;
  struct WBArg      *arg;
  char              *infile, bname[NBUFN];
  char              optBuffer[128];

  autoindent = TRUE;		/* Auto indentation flag	*/
  casesens = TRUE;		/* Compare case senitive	*/
  icon = TRUE;			/* Create icons for output	*/
  keepBackup = TRUE;		/* Create backup copy		*/
  stripline = TRUE;		/* Clean trailing ' ' & '\t'	*/
  requester = TRUE;		/* Use FileRequester for input	*/
  displaytab = FALSE;		/* Display Tab as » in the text */
  doCD = FALSE;
  curbp = NULL;                 /* Current buffer               */
  curwp = NULL;                 /* Current window               */
  bheadp = NULL;                /* BUFFER listhead              */
  wheadp = NULL;                /* WINDOW listhead              */
  blistp = NULL;                /* Buffer list BUFFER           */
  prev_bp = NULL;               /* Previous Buffer              */
  interlace = FALSE;
  strcpy(ohmProject, "M2Amiga");

  IntuitionBase = OpenLibrary("intuition.library",33);

  if (GetEnvM2("m2emacs", optBuffer, sizeof(optBuffer), 0) > 0)
    ReadOptionString(optBuffer, TRUE);

  WBenchMsg = (struct WBStartup *)argv;
  strcpy(bname, "main");

  infile = NULL;

  if (argc > 1) {
    for (c=1; (c<argc); c++) {
      if (*argv[c] == '-' || *argv[c] == '+')
        ReadOptionString(argv[c], FALSE);
      else if (*argv[c] == '?') {
        char tmp[80];

        sprintf(tmp,
                "%s%s\nCreation Date & Time %s %s\n%s %s %s\n",
                title1,
                version,
                __DATE__,
                __TIME__,
                usage1,
                argv[0],
                usage2
               );
        if (Output())
          Write(Output(), &tmp, strlen(tmp));
        else
          Error(tmp, FALSE);
        return(FALSE);
      }
      else {
        infile = argv[c];
        break;
      }
    }
  }
  else if (argc == 0) {   /* called from workbench */
    if (WBenchMsg->sm_NumArgs > 1) {
      arg = WBenchMsg->sm_ArgList;
      arg++;
      newLock = arg->wa_Lock;
      origDir = CurrentDir( newLock );
      infile = arg->wa_Name;
    }
  }
  if ( infile )
    makename( bname, infile );

  vtinit();

  bheadp = NULL;
  edinit(bname);
  setoption(2,icon);
  setoption(3,keepBackup);
  setoption(4,stripline);
  setoption(5,requester);
  setoption(6,displaytab);
  setoption(7,interlace);

  if ( infile ) {
    update();         /* You have to update   */
    readin( infile );       /* in case "[New file]" */
  }
  lastflag = 0;         /* Fake last flags.   */

#ifdef DEBUG
  printf("vor  loop:\n");
  Delay(50);
#endif
loop:
  update();           /* Fix up the screen  */
#ifdef DEBUG
  printf("nach update():\n");
  Delay(50);
#endif
  c = getkey();
  if (mpresf != FALSE) {
    mlerase();
    update();
  }
  f = FALSE;
  n = 1;
  if (c == (CTRL|'U')) {      /* ^U, start argument   */
    f = TRUE;
    n = 4;        /* with argument of 4 */
    mflag = 0;        /* that can be discarded. */
#ifdef DEUTSCH
    mlwrite("Zähler: 4");
#else
    mlwrite("Repeat Count: 4");
#endif
    while (((c=getkey()) >='0' && c<='9') || c==(CTRL|'U')) {
      if (c == (CTRL|'U')) {
        if (n<0x20000000)
        n = n<<2;
      }
     /*
      * If first digit entered, replace previous argument
      * with digit and set sign.  Otherwise, append to arg.
      */
      else {
        if (!mflag) {
          n = 0;
          mflag = 1;
        }
        if (n<0x8000000)
          n = 10*n + c - '0';
      }
#ifdef DEUTSCH
      mlwrite("Zähler: %d", n);
#else
      mlwrite("Repeat Count: %d", n);
#endif
    }
  }
  /*else*/
  if (c == (CTRL|'X')) {      /* ^X is a prefix     */
    c = (CTLX | getctl());
  }
  else if( (c & 0xFF) == CSI) {
    mouse_handle_event( f, n);
    goto loop;
  }
  if (kbdmip != NULL) {       /* Save macro strokes.  */
    if ( c != (CTLX|')') && kbdmip > &kbdm[NKBDM-6] ) {
      ctrlg(FALSE, 0);
      goto loop;
    }
    if (f != FALSE) {
      *kbdmip++ = (CTRL|'U');
      *kbdmip++ = n;
    }
    *kbdmip++ = c;
  }
  execute(c, f, n);         /* Do it.       */
  goto loop;
}

/*
 * Initialize all of the buffers and windows. The buffer name is passed down
 * as an argument, because the main routine may have been told to read in a
 * file by default, and we want the buffer name to be right.
 */
edinit(bname)
char  bname[];
{
  register BUFFER *bp;
  register WINDOW *wp;

  bp = bfind(bname, TRUE, 0);     /* First buffer   */
  blistp = bfind("[List]", TRUE, BFTEMP); /* Buffer list buffer   */

  wp = (WINDOW *) calloc(1, sizeof(WINDOW)); /* First window   */
  if (bp==NULL || wp==NULL || blistp==NULL) {
    vttidy();
    exit(1);
  }

  curbp  = bp;        /* Make this current  */
  wheadp = wp;
  curwp  = wp;

  wp->w_wndp  = NULL;       /* Initialize window  */
  wp->w_bufp  = bp;
  bp->b_nwnd  = 1;      /* Displayed.     */
  wp->w_linep = bp->b_linep;
  wp->w_dotp  = bp->b_linep;
  wp->w_doto  = 0;
  wp->w_markp = NULL;
  wp->w_marko = 0;
  wp->w_toprow = 0;
  wp->w_ntrows = term.t_nrow-1;     /* "-1" for mode line.  */
  wp->w_force = 0;
  wp->w_flag  = WFMODE|WFHARD;    /* Full.    */
  wp->w_leftOff = 0;

  return TRUE;
}

/*
 * This is the general command execution routine. It handles the fake binding
 * of all the keys to "self-insert". It also clears out the "thisflag" word,
 * and arranges to move it to the "lastflag", so that the next command can
 * look at it. Return the status of command.
 */
execute(c, f, n)
{
  register KEYTAB *ktp;
  register int  status;

/* Self inserting.    */
  if ((c>=0x20 && c<=0x7E) || (c>=0xA0 && c<=0xFE)) {
    if (n <= 0) {
      lastflag = 0;
      return (n<0 ? FALSE : TRUE);
    }
    thisflag = 0;       /* For the future.    */
    status   = linsert(n, c);
    lastflag = thisflag;
    return (status);
  }

  ktp = &keytab[0];         /* Look in key table.   */
  while (ktp < &keytab[NKEYTAB]) {
    if (ktp->k_code == c) {
      thisflag = 0;
      status   = (*ktp->k_fp)(f, n);
      lastflag = thisflag;
      return (status);
    }
    ++ktp;
  }

  lastflag = 0;         /* Fake last flags.   */
  return (FALSE);
}

/*
 * Read in a key.
 * Do the standard keyboard preprocessing. Convert the keys to the internal
 * character set.
 */
getkey()
{
  int c;

#ifdef DEBUG
  printf("Anfang getkey()\n");
  Delay(50);
#endif
  c = GetChar();
  if (c == METACH) {        /* Apply M- prefix    */
    c = getctl();
    return (META | c);
  }
  if (0 <= c && c <= 0x1F)     /* C0 control -> C-   */
    c = (CTRL | (c+'@'));
  else
    c &= 0xFF;
#ifdef DEBUG
  printf("Ende getkey() = %04x\n", c);
  Delay(50);
#endif
  return (c);
}

/*
 * Get a key.
 * Apply control modifications to the read key.
 */
getctl()
{
  int c;

  c = GetChar();
  if (0 <= c && c <= 0x1F) {    /* C0 control -> C-   */
    c = (CTRL | (c + '@'));
  }
  else {
    c = cap(c);
  }
#ifdef DEBUG
  printf("Ende getctl() = %04x\n", c);
  Delay(50);
#endif

  return c;
}

/*
 * Fancy quit command, as implemented by Norm. If the current buffer has
 * changed do a write current buffer and exit emacs, otherwise simply exit.
 */
quickexit(f, n)
{
  if ((curbp->b_flag&BFCHG) != 0 && (curbp->b_flag&BFTEMP) == 0)
    filesave(f, n);

  quit(f, n);         /* conditionally quit   */
  return TRUE;
}

/*
 * Quit command. If an argument, always quit. Otherwise confirm if a buffer
 * has been changed and not written out. Normally bound to "C-X C-C".
 */
quit(f, n)
{
  register int  s;

  if (f != FALSE        /* Argument forces it.  */
     || anycb() == FALSE       /* All buffers clean.   */
#ifdef DEUTSCH
     || (s=mlyesno("Es hat veränderte Buffer.  Trotzdem aufhören")) == TRUE)
#else
     || (s=mlyesno("Modified buffers exist.  Quit")) == TRUE)
#endif
  {
     /* User says it's OK.   */
    vttidy();
    exit(GOOD);
  }
  return (s);
}

/*
 * Begin a keyboard macro.
 * Error if not at the top level in keyboard processing. Set up variables and
 * return.
 */
ctlxlp(f, n)
{
  if (kbdmip!=NULL || kbdmop!=NULL) {
#ifdef DEUTSCH
    mlwrite("Nicht jetzt!");
#else
    mlwrite("Not now");
#endif
    return (FALSE);
  }
#ifdef DEUTSCH
  mlwrite("[Beginne Makro Definition]");
#else
  mlwrite("[Start macro]");
#endif
  SetMacroTitle(TRUE);
  kbdmip = &kbdm[0];
  return (TRUE);
}

/*
 * End keyboard macro. Check for the same limit conditions as the above
 * routine. Set up the variables and return to the caller.
 */
ctlxrp(f, n)
{
  if (kbdmip == NULL) {
#ifdef DEUTSCH
    mlwrite("Nicht jetzt!");
#else
    mlwrite("Not now");
#endif
    return (FALSE);
  }
#ifdef DEUTSCH
  mlwrite("[Ende des Makros]");
#else
  mlwrite("[End macro]");
#endif
  SetMacroTitle(FALSE);
  kbdmip = NULL;
  return (TRUE);
}

/*
 * Execute a macro.
 * The command argument is the number of times to loop. Quit as soon as a
 * command gets an error. Return TRUE if all ok, else FALSE.
 */
ctlxe(f, n)
{
  register int  c;
  register int  af;
  register int  an;
  register int  s;

  if (kbdmip!=NULL || kbdmop!=NULL) {
#ifdef DEUTSCH
    mlwrite("Nicht jetzt");
#else
    mlwrite("Not now");
#endif
    return (FALSE);
  }
  if (n <= 0)
    return (TRUE);
  do {
    kbdmop = &kbdm[0];
    do {
      af = FALSE;
      an = 1;
      if ((c = *kbdmop++) == (CTRL|'U')) {
        af = TRUE;
        an = *kbdmop++;
        c  = *kbdmop++;
      }
      s = TRUE;
    } while (c!=(CTLX|')') && (s=execute(c, af, an))==TRUE);
    kbdmop = NULL;
  } while (s==TRUE && --n);
  return (s);
}

/*
 * Abort.
 * Beep the beeper. Kill off any keyboard macro, etc., that is in progress.
 * Sometimes called as a routine, to do general aborting of stuff.
 */
ctrlg(f, n)
{
  DisplayBeep(NULL);
  if (kbdmip != NULL) {
    kbdm[0] = (CTLX|')');
    kbdmip  = NULL;
    SetMacroTitle(FALSE);
  }
  return (ABORT);
}
