#include "config.h"
/*
 Copyright (c) 1986, 1990 by The Trustees of Columbia University in
 the City of New York.  Permission is granted to any individual or
 institution to use, copy, or redistribute this software so long as it
 is not sold for profit, provided this copyright notice is retained.

 Author: Howie Kaye
*/

/*
 * cmpara
 * parse 'chunks' of text, with some nice actions defined.
 *
 * texti style data input.
 */

/*
 * This file contains the code to parse chunks of text, simlilarly to the
 * way MM parses a message on the DEC-20
 *
 * Accepts a char *, which is:
 *  a buffer of initialized text -- so that the function can be
 *	interrupted, and then continued.
 *  and a list of action characters.  These are only in place when inside
 *  the actual paragraph parse.  When an action char is typed.
 *
 * This is pretty hairy.  What it does, is to change the action routines
 * being used, to allow the default/user defined actions to work.  It also
 * has to manage it's own set of cmd buffers and work buffers, so that
 * calls to ccmd insertion routines (cmsti) do not overflow the buffer
 * space which ccmd has been given (cmbufs call).  The routines here are
 * meant to be called after a confirm has been given, and no reparses into
 * previous data will be necessary.  It can be used to parse large chunks
 * of text, in a manner similar to the TEXTI jsys under TOPS-20.
 *
 * In order to make ccmd appear to be in a normal state whenever we leave
 * the paragraph environment, we have to remember the old environment, and
 * 'context switch' the old environment in (and out again) whenever an
 * action character is typed (the action routine may make asumptions about
 * the ccmd environment, and try to parse something).  To do this, all
 * action character force a call to the break_hndlr() routine.  This then
 * saves the environment, calls the real handler, and then restores the
 * environment.  It also fixes the cmd internal buffers to reflect the new
 * state.
 *
 * When a user supplied (or default) action routine is called, it is
 * passed the text it is acting on (This buffer is malloc'ed to the correct
 * size, and should not be written to if the write will go past the bounds
 * of the current buffer).  The routine is also passed two flags.  If the
 * buffer is modified, then the modified flag should be set.  If the
 * parse should end (eof,abort,etc), then the ret flags should be set.  In
 * all cases, a pointer to the new text should be returned.
 */

					/* allocate cmd stuff here */
#define PARAERR

					/* system structures/defines */
#include "ccmdlib.h"
#include "cmfncs.h"
					/* incremental buffer size */
#define BSIZ 1000
					/* minimum space left before buffer */
					/* expansion */
#define MINSIZE 133

#define beep() cmputc(BELL,cmcsb._cmoj)

extern csb cmcsb;

PASSEDSTATIC int paracomplete ARGS((char *text, int textlen, fdb *fdbp,
	 int full, const char **cplt, int *cpltlen));
PASSEDSTATIC int parahelp ARGS((char *text, int textlen, fdb *fdbp,
	 int cust, int lines));
PASSEDSTATIC int paraparse ARGS((char *text, int textlen, fdb *fdbp,
	 int *parselen, pval *value));
static char **get_path ARGS((void));
static char *abortact ARGS((char *text, int *modified, int *ret));
static char *autowrapact ARGS((char *text, int *modified, int *ret, int c));
static char *autowrapnl ARGS((char *text, int *modified, int *ret));
static char *autowrapspace ARGS((char *text, int *modified, int *ret));
static char *autowraptab ARGS((char *text, int *modified, int *ret));

#define begact Pbegact	/* avoid conflict in this file with global begact() */
static char *begact ARGS((char *text, int *modified, int *ret));

#define delact Pdelact	/* avoid conflict in this file with global delact() */
static char *delact ARGS((char *text, int *modified, int *ret));

static char *dispact ARGS((char *text, int *modified, int *ret));
static char *editact ARGS((char *text, int *modified, int *ret));
static char *eofact ARGS((char *text, int *modified, int *ret));

#if 0					/* no longer used */
static char *eolact ARGS((char *text, int *modified, int *ret));
#endif

static char *executeact ARGS((char *text, int *modified, int *ret));
static char *filteract ARGS((char *text, int *modified, int *ret));

#define fixact Pfixact	/* avoid conflict in this file with global fixact() */
static char *fixact ARGS((char *text, int *modified, int *ret));

static char *insact ARGS((char *text, int *modified, int *ret));

#ifdef MSDOS
#define mktemp Pmktemp	/* avoid conflict in this file with global mktemp() */
static char *mktemp ARGS((char *dir, char *prefix));
#endif

static char *parse_cmdline ARGS((const char *p));
static char *parse_cmdline1 ARGS((void));

#define quoact Pquoact	/* avoid conflict in this file with global quoact() */
static char *quoact ARGS((char *text, int *modified, int *ret));

static char *redispact ARGS((char *text, int *modified, int *ret));
static char *self_ins_nl ARGS((char *text, int *modified, int *ret));

#define wrdact Pwrdact	/* avoid conflict in this file with global wrdact() */
static char *wrdact ARGS((char *text, int *modified, int *ret));

static int break_hndlr ARGS((HANDLER_ARGS));
static int lastlinelen ARGS((char *str));
static int parareset ARGS((int code));
static int tablen ARGS((char *str));
static void install_actions ARGS((para_actions *actions));
static void install_nl_action ARGS((void));
static void null_actions ARGS((void));

/*
 * paragraph break mask
 */
static brktab parabrk = {
  {					/* all print characters */
					/* except tab and space */
    0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
  },
  {
    0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
  }
};

#if 0					/* no longer used */
static brktab pipeparabrk = {
  {					/* just newline is a brk char */
					/* except tab and space */
    0x00, 0x24, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
  },
  {
    0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
  }
};
#endif

/*
 * The behavior of this parser is different from the standard
 * ccmd interface, so we must set up our own action table.
 */

/*
 * default paragraph actions
 */
static para_actions def_actions[] = {
  { '\002', insact },			/* ^B insert file */
  { '\004', eofact },			/* ^D end of file */
  { '\005', editact },			/* ^E invoke editor on this buffer */
  { '\006', filteract },		/* ^F run buffer through filter */
  { '\b', delact },			/* back space */
  { '\t', autowraptab },		/* autowrap on tab */
  { '\n', autowrapnl },			/* \n end of line wrap */
  { '\r', autowrapnl },			/* CR or LF. */
  { '\013', dispact },			/* ^K display whole buffer */
  { '\f', redispact },			/* ^L redisplay screen */
  { '\016', abortact },			/* ^N abort this insertion */
  { '\020', executeact },		/* ^P execute command and insert */
  { '\022', fixact },			/* ^R redisplay line */
  { '\025', begact },			/* ^U delete line */
  { '\026', quoact },			/* ^V quota character */
  { '\027', wrdact },			/* ^W delete backwards word */
  { ' ', autowrapspace },		/* SPC autowrap on space */
  { '\177', delact },			/* DEL delete previous char */
  { '\000', NULL },			/* end the table */
};

static int (**oldact) ARGS((HANDLER_ARGS)); /* old action table */

					/* clean paragraph action table */
static int (*(paraact[128])) ARGS((HANDLER_ARGS)) = {
    NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
    NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
    NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
    NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
    NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
    NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
    NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
    NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
    NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
    NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
    NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
    NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
    NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
    NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
    NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
    NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL
};

					/* clean user defined action table */
static char * (*(paraact1[128])) ARGS((char *, int *, int *)) = {
    NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
    NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
    NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
    NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
    NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
    NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
    NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
    NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
    NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
    NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
    NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
    NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
    NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
    NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
    NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
    NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL
};

/*
 * global state variables
 */

#define abort Abort		/* avoid conflict with function in stdlib.h */
static int eof=FALSE;
static int abort = FALSE;
static csb savecsb;
static int *cmdbuf=NULL;
static int set=FALSE;			/* first call to parse, or an */
static jmp_buf ojmp;

					/* handler structure */
ftspec ft_para = { paraparse, parahelp, paracomplete, 0, &parabrk };

static int (*oerrh) ARGS((int, ...));

static int
#if HAVE_STDC
parareset(int code)
#else /* K&R style */
parareset(code)
int code;
#endif /* HAVE_STDC */
{
  bcopy(ojmp, cmerjb, sizeof (jmp_buf));
					/* put back old error handler */
  null_actions();			/* restore action block */
  cmact(oldact);			/* put back the original action table*/
  set = FALSE;				/* uninitialized...for next time */
  if (cmcsb._cmwbp)
      free(cmcsb._cmwbp);
  if (cmcsb._cmbfp)
      free(cmcsb._cmbfp);
  cmcsb = savecsb;
  cmcsb._cmerh = oerrh;
  return((*cmcsb._cmerh)(code));
}

/*
 * parse routine.
 * parse until eof or abort get's set.
 * returns buffer which has been typed, or NULL on abort
 */
PASSEDSTATIC int
#if HAVE_STDC
paraparse(char *text, int textlen, fdb *fdbp, int *parselen, pval *value)
#else /* K&R style */
paraparse(text,textlen,fdbp,parselen,value)
char *text;
int textlen;
fdb *fdbp;
int *parselen;
pval *value;
#endif /* HAVE_STDC */
{
  int i;				/* action call? */
  static char *buf;

  if (cmcsb._cmflg2 & CM_EOF) {
      cmcsb._cmflg2 &= ~CM_EOF;
      eof = TRUE;
  }

  if (!set) {				/* first time through, set things up */
    bcopy(cmerjb, ojmp, sizeof (jmp_buf));

    savecsb = cmcsb;			/* save stae for later */
    oerrh = cmcsb._cmerh;
    if (cmdbuf != NULL)			/* get rid of old buffer */
      free(cmdbuf);
    if ((cmdbuf = (int *)calloc(1,BSIZ*sizeof(*cmdbuf))) == NULL)
      return(PARAxNM);
    if ((cmcsb._cmwbp =
	 (char *)calloc(1,BSIZ*sizeof(*cmcsb._cmwbp))) == NULL) {
      free(cmdbuf);
      cmdbuf = NULL;
      return(PARAxNM);
    }
    cmcsb._cmwbc = BSIZ;		/* set up. */
    cmcsb._cmcnt = BSIZ;
    cmcsb._cmptr = cmdbuf;
    cmcsb._cmbfp = cmdbuf;
    cmcsb._cmcur = cmdbuf;
    cmcsb._cminc = 0;
    cmcsb._cmflg &= ~CM_NAC;
    cmcsb._cmerh = (int(*) ARGS((int, ...)))parareset;

    if (fdbp->_cmdat != NULL) {		/* insert specified starting text */
      para_data *dat = (para_data *) fdbp->_cmdat;
      if (dat->buf != NULL) {
	int len = strlen(dat->buf);
	if (len >= BSIZ) {		/* do we have enough space? */
					/* no!  get more */
	  if ((cmdbuf=(int *)cmrealloc((char*)cmdbuf,
				       (len+BSIZ)*sizeof(int)))==NULL){
	    return(PARAxNM);
	  }
	  cmcsb._cmcur = cmcsb._cmptr = cmdbuf + (cmcsb._cmptr - cmcsb._cmbfp);
	  cmcsb._cmbfp = cmdbuf;
	  cmcsb._cmcnt = len + BSIZ;

	  if ((cmcsb._cmwbp =
	       (char *)cmrealloc(cmcsb._cmwbp,
				 sizeof(*cmcsb._cmwbp)*(len+BSIZ))) == NULL){
	    return(PARAxNM);
	  }
	  cmcsb._cmwbc = len + BSIZ;
	}
	cmsti(dat->buf,CC_NEC|CC_HID|CC_QUO); /* insert text, no echoing */
	for (i = 0; i < cmcsb._cminc; i++) { /* turn off the flags */
	  cmcsb._cmptr[i] &= ~(CC_HID|CC_NEC|CC_QUO);
	}
      }
      null_actions();			/* zero out the action table */
      if (cmcsb._cmflg & CM_ITTY) {
	if (dat->actions != NULL) {	/* install user/default actions */
	  if (fdbp->_cmffl & PARA_DEF)
	    install_actions(def_actions);
	  install_actions(dat->actions);
	}
	else {
	  install_actions(def_actions);
	}
      }
      else {
	install_nl_action();
      }
    }
    oldact = cmcsb._cmact;		/* save old action table */
    cmact(paraact);			/* now use our own action characters */
    eof = FALSE;			/* not done yet */
    abort = FALSE;			/* not aborting */
    set = TRUE;				/* but we are initialized */
  }

  if (cmcsb._cmcnt < MINSIZE) {		/* running out of buffer space? */
					/* then get more space */
    if ((cmdbuf=(int *)cmrealloc((char*)cmdbuf,(cmcsb._cmwbc + BSIZ)
				 *sizeof(int)))==NULL){
      return(PARAxNM);
    }
    cmcsb._cmptr = cmdbuf + (cmcsb._cmptr - cmcsb._cmbfp);
    cmcsb._cmcur = cmdbuf + (cmcsb._cmcur - cmcsb._cmbfp);
    cmcsb._cmbfp = cmdbuf;		/* and install it into the CSB */

    cmcsb._cmcnt += BSIZ;		/* count it */

    if ((cmcsb._cmwbp = (char *)cmrealloc(cmcsb._cmwbp,cmcsb._cmwbc+BSIZ))
	== NULL) {
      return(PARAxNM);
    }
    cmcsb._cmwbc += BSIZ;
  }

  if (eof) {				/* done! */
    if (buf != NULL)			/* free previous buffer */
      free(buf);

    if ((buf = (char *)malloc(textlen+1)) == NULL) { /* get space */
      return(PARAxNM);
    }
    strncpy(buf,text,textlen);		/* copy text into buffer */
    buf[textlen] = '\0';		/* NULL terminate it */
    for (i = 0; i < textlen; i++)
      buf[i] &= 0x7f;			/* strip those quota characters out. */
    value->_pvpara = buf;		/* point return value at it */
    *parselen = textlen;		/* set the length properly */
    null_actions();			/* restore action block */
    bcopy(cmerjb, ojmp, sizeof (jmp_buf));
					/* put back old error handler */
    cmact(oldact);			/* put back the original action table*/
    set = FALSE;			/* initialized...for next time */
    if (cmcsb._cmcol != 0)
	cmxnl();
    free(cmcsb._cmwbp);
    free(cmcsb._cmbfp);
    cmdbuf = NULL;			/* cmdbuf = cmcsb._cmbfp */
    cmcsb = savecsb;			/* restore ccmd's buffers */
    return(CMxOK);			/* return done */
  }
  else
    if (abort) {
      bcopy(ojmp, cmerjb, sizeof (jmp_buf));
					/* put back old error handler */
      null_actions();			/* restore action block */
      cmact(oldact);			/* put back the original action table*/
      set = FALSE;			/* uninitialized...for next time */
      value->_pvpara = NULL;
      *parselen = textlen;
      if (cmcsb._cmcol != 0)
	  cmxnl();
      free(cmcsb._cmwbp);		/* restore ccmd's buffers */
      cmcsb = savecsb;
      return(CMxOK);
    }
  return(CMxINC);			/* nothing here...return incomplete */
}

/*
 * scan through para_action table, and install actions in
 * the real actions tables.
 * terminates in NULL function.
 */
static void
#if HAVE_STDC
install_actions(para_actions *actions)
#else /* K&R style */
install_actions(actions)
para_actions *actions;
#endif /* HAVE_STDC */
{
  for(; !(actions->actionfunc==NULL && actions->actionchar=='\0');actions++) {
    paraact[actions->actionchar] = break_hndlr;
    paraact1[actions->actionchar] = actions->actionfunc;
  }
}

/*
 * zero out the action tables
 */
static void
null_actions(VOID) {
    bzero(paraact, sizeof(paraact));
    bzero(paraact1, sizeof(paraact));
}

/*
 * parse completion.
 * does nothing.
 */
PASSEDSTATIC int
#if HAVE_STDC
paracomplete(char *text, int textlen, fdb *fdbp, int full, const char **cplt, int *cpltlen)
#else /* K&R style */
paracomplete(text,textlen,fdbp,full,cplt,cpltlen)
char *text;
const char **cplt;
int textlen,full,*cpltlen;
fdb *fdbp;
#endif /* HAVE_STDC */
{
  *cplt = NULL;
  return(CMP_BEL);		/* beep at them */
}

/*
 * helpless
 */

PASSEDSTATIC int
#if HAVE_STDC
parahelp(char *text, int textlen, fdb *fdbp, int cust, int lines)
#else /* K&R style */
parahelp(text,textlen,fdbp,cust,lines)
char *text;
int textlen,cust;
fdb *fdbp;
int lines;
#endif /* HAVE_STDC */
{
  return(lines);
}

/*
 * Action routines
 */

/*
 * eof action.
 */
static char *
#if HAVE_STDC
eofact(char *text, int *modified, int *ret) {
#else /* K&R style */
eofact(text,modified,ret) char *text; int *modified,*ret; {
#endif /* HAVE_STDC */
  *ret = TRUE;
  *modified = FALSE;
  return(text);
}


/*
 * redisplay the last screenful of text
 */

static char *
#if HAVE_STDC
redispact(char *text, int *modified, int *ret) {
#else /* K&R style */
redispact(text,modified,ret) char *text; int *modified, *ret;{
#endif /* HAVE_STDC */
  int i, li, co, cols, ov=FALSE;
  char c;

  *ret = FALSE;
  *modified = FALSE;
  cmcls();
  li = cmcsb._cmrmx;			/* get number of lines */
  co = cmcsb._cmcmx;			/* and number of columns */

  for (i = strlen(text)-1, cols = 0; i >= 0; i--) { /* figure out what */
    c = text[i] & 0x7f;			/*   we can display */
    if (c == '\t')
	cols = ((cols + 8) / 8) * 8;
    else
	cols++;				/* incr column count */
    if (iscntrl(c) && !isspace(c))	/* control char takes two chars */
      cols++;				/*   to display, count the ^ */
    if (cols > co) {
      --li;				/* we overflowed the line */
      cols = 0;				/* reset column count */
      ov = TRUE;			/* remember */
    }
    if (c == '\n') {			/* another line */
      cols = 0;				/* reset the column count */
      li--;
      ov = FALSE;
    }
    if (li == 0)			/* can't display any more lines */
      break;
  }
  if (ov && li == 0) {			/* top line doesn't fit on display */
    int p;
    p = i;
    while ((c = text[p] & 0x7f) != '\n' &&  p > 0) /* find beginning of line */
      p--;
    p += ((i - p)/co + 1) * co;
    i = p-1;				/* skip over the \n */
  }

  cmcsb._cmcol = 0;
  for(i++; i < cmcsb._cminc; i++) {	/* display the screenful */
    c = text[i] & 0x7f;
    if (iscntrl(c) && !isspace(c)) {
      cmxputc('^');			/* display control chars as */
      cmxputc(c | 0100);		/*   caret and uppercased */
    }
    else cmechx((int)(c));
  }
  return(text);
}

/*
 * insact
 * insert a file at the end of the buffer
 */
static char *
#if HAVE_STDC
insact(char *text, int *modified, int *ret)
#else /* K&R style */
insact(text, modified, ret)
char *text;
int *modified, *ret;
#endif /* HAVE_STDC */
{
					/* filename fdb */
  static fdb filfdb = { _CMFIL ,FIL_TYPE|FIL_NODIR, NULL, NULL, NULL, NULL,
			    NULL };
					/* confirm fdb */
  static fdb filfdb1 = { _CMCFM, CM_SDH, &filfdb, NULL, "Confirm to cancel",
			 NULL, NULL };
  static fdb cfmfdb = { _CMCFM, 0, NULL, NULL, NULL, NULL, NULL };
  char *fname= NULL;
  pval parseval;
  fdb *used;
  int fd;
  struct stat buf;
  char *ntext = NULL;
  size_t len;
  static int cmdbuf[200];
  static char atmbuf[200],wrkbuf[200];

  cmbufs(cmdbuf,200,atmbuf,200,wrkbuf,200);
  cmseter();				/* errors come back to here */
  prompt("Insert file: ");		/* prmpt for a filename */
  cmsetrp();				/* reparses come back here. */
  parse(&filfdb1, &parseval, &used);	/* parse the filename */
  if (used == &filfdb1) {
    cmxputs("...No file inserted\n");
    *modified = FALSE;
    return(text);
  }
  if (fname != NULL)			/* free up old filename if */
    free(fname);			/* necessary */
  if ((fname = (char *)malloc(strlen(parseval._pvfil[0])+1)) == NULL) {
    cmxputs("?Out of memory\n");
    return(text);
  }
  strcpy(fname,parseval._pvfil[0]);	/* copy the data */
  parse(&cfmfdb,&parseval,&used);	/* get confirmation */

  fd = open(fname,O_RDONLY,0);		/* open up the file */
  if (fd == -1) {			/* couldn't open it... */
    cmxputs("?permission denied\n");	/* complain */
    *modified = FALSE;			/* no modification done */
    free(fname);
    return(text);
  }
  stat(fname,&buf);			/* get length of file */
  len = strlen(text) + (size_t)buf.st_size;
  if ((ntext = (char *)malloc(len+1)) == NULL) { /* make space */
    cmxputs("?Out of memory\n");
    close(fd);
    free(fname);
    return(text);
  }
  strcpy(ntext,text);			/* copy old text */
					/* and add in the file */
  len = strlen(text) + read(fd,&ntext[strlen(text)],(size_t)buf.st_size);
  close(fd);				/* close the file */
  cmxputs("[OK]\n");
  ntext[len] = '\0';			/* null terminate */
  *modified = TRUE;			/* we changed things */
  *ret = FALSE;
  free(fname);
  return(ntext);			/* return new text */
}

/*
 * display the whole buffer
 */
static char *
#if HAVE_STDC
dispact(char *text, int *modified, int *ret) {
#else /* K&R style */
dispact(text, modified, ret) char *text; int *modified, *ret; {
#endif /* HAVE_STDC */
  if (cmcsb._cmcol != 0)
    cmxnl();
  cmxputs(text);
  return(text);
}

/*
 * invoke the editor on the buffer
 */
static char *
#if HAVE_STDC
editact(char *text, int *modified, int *ret)
#else /* K&R style */
editact(text, modified, ret)
char *text;
int *modified, *ret;
#endif /* HAVE_STDC */
{
  char fname[40];
  const char *e;
  int fd;
  char buf[100];
  static char *ntext=NULL;
  struct stat sbuf;
  int i;
  size_t len;

  cmxputs("Invoking editor...\n");	/* announce our intentions */
  e = getenv("EDITOR");			/* check editor variable */
  if (e == NULL || *e == '\0')		/* no editor variable? */
    e = DEF_EDITOR;			/* use default editor */

#if defined(HAVE_MKSTEMP)
    strcpy(fname,"/tmp/cmdXXXXXX");	/* get a file name */
    fd = mkstemp(fname);
    if ((fd >= 0) && (fchmod(fd, 0700) < 0))
	fd = -1;
#else
#if unix
    strcpy(fname,"/tmp/cmdXXXXXX");	/* get a file name */
    mktemp(fname);
#else
  strcpy(fname,mktemp(getenv("TMP"),"cmd")); /* get a file name */
#endif
  fd = open(fname,O_WRONLY|O_CREAT,0700); /* open the tmp file */
#endif /* defined(HAVE_MKSTEMP) */
  if (fd < 0) {
    cmxputs("?Could not create temporary file.\n");
    perror(fname);
    return(text);
  }
  if (write(fd,text,strlen(text)) != (int)strlen(text)) {
    cmxputs("?Could not write temporary file.\n");
    perror(fname);
    close(fd);
    unlink(fname);
    return(text);
  }
  close(fd);
  strcpy(buf,e);
#ifndef MSDOS
  strcat(buf," +1000000");
#endif
  strcat(buf," ");
  strcat(buf,fname);
  cmtend();				/* put the tty back */
  if (system(buf) != 0) {		/* execute it */
    cmtset();
    cmxputs("?Edit failed.\n");		/* failure? */
    perror(fname);
    unlink(fname);			/* throw up hands in disgust */
    return(text);
  }
  cmtset();
  if (stat(fname,&sbuf) == -1) {	/* get length of file */
    cmxputs("?Temporary file disappeared\n");
    return(text);
  }
  len = (size_t)sbuf.st_size;
  fd = open(fname,O_RDONLY,0);		/* open the file again */
  if (fd < 0) {
    cmxputs("?Could not open temporary file for read.\n");
    perror(fname);
    unlink(fname);
    return(text);
  }
  if (ntext != NULL)			/* free up last buffer */
    free(ntext);
  if ((ntext = (char *)malloc(len+1)) == NULL) { /* make space */
    cmxputs("?Out of memory\n");
    return(text);
  }
#ifdef MSDOS
  if ((i = read(fd,ntext,len)) < 0)  /* read in the file */
#else
  if ((size_t)(i = read(fd,ntext,len)) != len) /* read in the file */
#endif
  {
    cmxputs("?Could not read temporary file.\n");
    perror(fname);
    unlink(fname);
    return(text);
  }
  close(fd);				/* close the file */
  cmcls();
  ntext[len] = '\0';			/* null terminate */
  *modified = TRUE;
  unlink(fname);
  cmxputs("...Back again\n");
  return(ntext);
}

/*
 * abort the current buffer
 */
static char *
#if HAVE_STDC
abortact(char *text, int *modified, int *ret) {
#else /* K&R style */
abortact(text, modified, ret) char *text; int *modified, *ret; {
#endif /* HAVE_STDC */
  static fdb cfmfdb = { _CMCFM, CM_SDH, NULL, NULL, "Carriage Return to abort",
			NULL, NULL };
  char cmdbuf[200],atmbuf[200],wrkbuf[200];
  pval parseval;
  fdb *used;

  cmcsb._cmerr = CMxOK;
  cmbufs((int*)cmdbuf,200,atmbuf,200,wrkbuf,200);
  cmseter();				/* errors come back to here */
  if (cmcsb._cmerr == CFMxNOC) {
    cmcsb._cmflg &= ~CM_ACT;		/* no pending actions now */
    return(text);
  }
  prompt("[Confirm to Abort]");		/* prompt for a confirm */
  cmsetrp();				/* reparses come back here. */
  parse(&cfmfdb, &parseval, &used);	/* parse the confirm */
  abort = TRUE;
  return(text);
}

#if 0					/* no longer used */
/*
 * just make us wake up on eol, to check on buffer space...
 */

static char *
eolact(text, modified, ret) char *text; int *modified, *ret; {
  static char *ntext=NULL;

  cmechx('\n');				/* do it again */
  *modified = TRUE;			/* we modified it */
  *ret = FALSE;				/* don't return yet */
  strcat(text,"\n");
  return(ntext);
}
#endif


static char *
#if HAVE_STDC
executeact(char *text, int *modified, int *ret)
#else /* K&R style */
executeact(text, modified, ret)
char *text;
int *modified, *ret;
#endif /* HAVE_STDC */
{
  char fname[40];
  char *output;
  char *cmd;
  struct stat sbuf;
  int fd;
  int len;
  int total;

  if ((cmd = parse_cmdline("Command: ")) == NULL) {
    *modified = FALSE;
    return (text);
  }

#if defined(HAVE_MKSTEMP)
    strcpy(fname,"/tmp/cmdXXXXXX");	/* get a file name */
    fd = mkstemp(fname);
    if ((fd >= 0) && (fchmod(fd, 0700) < 0))
	fd = -1;
    if (fd >= 0)
	(void)close(fd);
#else
#if unix
  strcpy(fname,"/tmp/cmdXXXXXX");	/* get a file name */
  mktemp(fname);
#else
  strcpy(fname,(char*)mktemp(getenv("TMP"),"cmd")); /* get a file name */
#endif
#endif /* defined(HAVE_MKSTEMP) */


  cmd = (char *) cmrealloc (cmd, (int)(strlen(cmd)+strlen(fname)+strlen(" > ")+1));
  strcat (cmd, " > ");
  strcat (cmd, fname);

  cmsystem (cmd);
  free (cmd);

  if (stat(fname, &sbuf) == -1) {
    cmxputs ("?Temporary file missing\n");
    return (text);
  }
  len = (size_t)sbuf.st_size;
  total = strlen(text)+len;
  if ((output = (char *) malloc (total+1)) == NULL) {
    cmxputs ("?Out of memory\n");
    *modified = FALSE;
    return (text);
  }
  strcpy (output, text);
  if ((fd = open(fname, O_RDONLY,0)) < 0) {
    cmxputs("?Could not open temporary file for read.\n");
    perror(fname);
    unlink(fname);
    free (output);
    return(text);
  }
#ifdef MSDOS
  if (read(fd,output+strlen(text),len) < 0)
#else
  if (read(fd,output+strlen(text),len) != len)
#endif
  {
    cmxputs("?Could not read temporary file.\n");
    perror(fname);
    unlink(fname);
    free (output);
    return(text);
  }
  close(fd);				/* close the file */
  output[total] = '\0';
  unlink (fname);
  *modified = TRUE;
  *ret = FALSE;
  cmxputs("[Done]\n");
  return (output);
}


static brktab fieldbrk = {
  {					/* print chars except ?, space, tab */
    0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x01,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01
  },
  {					/* print chars except ?, space, tab */
    0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x01,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01
  }
};
static filblk efilblk = { NULL, NULL, NULL };
static fdb efilefdb = { _CMFIL, FIL_OLD|FIL_EXEC|CM_SDH, NULL,
			  (pdat) &efilblk, NULL, NULL, NULL };
static fdb filefdb = { _CMFIL, CM_SDH, NULL,
			 (pdat) NULL, NULL, NULL, NULL };
static fdb fieldfdb = { _CMFLD, CM_SDH, NULL, NULL, NULL, NULL, &fieldbrk };
static fdb conffdb = { _CMCFM, CM_SDH, NULL, NULL,
			 "command line", NULL, NULL };
static char *
#if HAVE_STDC
parse_cmdline (const char *p)
#else /* K&R style */
parse_cmdline (p)
const char *p;
#endif /* HAVE_STDC */
{
  const char **path = NULL;
  char *cmd;
  char *cmdend;
  pval parseval;
  fdb *used;
  static int cmdbuf[200];
  static char atmbuf[200],wrkbuf[200];

  cmbufs(cmdbuf,200,atmbuf,200,wrkbuf,200);

  path = (const char**)get_path();
  efilblk.pathv = path;

  cmseter();
  prompt (p);
  cmsetrp();
  parse (fdbchn(&conffdb, &efilefdb, &filefdb, &fieldfdb, NULL),
	 &parseval, &used);

  if (used == &conffdb) {
    cmxputs ("Aborting\n");
    return (NULL);
  }

  if (used == &efilefdb || used == &filefdb) {
    cmd = (char *) malloc (strlen (parseval._pvfil[0]) + 1);
    strcpy (cmd, parseval._pvfil[0]);
  }
  else if (used == &fieldfdb) {
    cmd = (char *) malloc (strlen (parseval._pvstr) + 1);
    strcpy (cmd, parseval._pvstr);
  }

  if ((cmdend = parse_cmdline1(), cmdend)) {
      cmd = (char *) realloc (cmd, strlen (cmd)+strlen(cmdend)+2);
      strcat (cmd, " ");
      strcat (cmd, cmdend);
      free (cmdend);
  }
  return (cmd);
}

static char *
parse_cmdline1 (VOID) {
  char *cmd;
  char *cmdend;
  pval parseval;
  fdb *used;

  parse (fdbchn(&conffdb, &filefdb, &fieldfdb, NULL),
	 &parseval, &used);

  if (used == &conffdb)
    return (NULL);

  if (used == &filefdb) {
    cmd = (char *) malloc (strlen (parseval._pvfil[0]) + 1);
    strcpy (cmd, parseval._pvfil[0]);
  }
  else if (used == &fieldfdb) {
    cmd = (char *) malloc (strlen (parseval._pvstr) + 1);
    strcpy (cmd, parseval._pvstr);
  }

  if ((cmdend = parse_cmdline1(), cmdend)) {
      cmd = (char *) realloc (cmd, strlen (cmd)+strlen(cmdend)+2);
      strcat (cmd, " ");
      strcat (cmd, cmdend);
      free (cmdend);
  }
  return (cmd);
}


static char **
get_path(VOID) {
  char **path = NULL;
  int num = 0;
  char *PATH, *cp;
  char *p;

  if ((PATH = getenv ("PATH")) == NULL)
    return (NULL);
  else {
    cp = (char *) malloc (strlen(PATH)+1); /* space for private copy */
    strcpy (cp, PATH);
  }

  while ((p = index (cp, ':')) != NULL) { /* while we have dirs */
    *p = '\0';				/* tie of with a NULL */
    if (strlen(cp) != 0) {
      if (num == 0) 			/* first path */
	path = (char **) malloc (sizeof (char *)); /* space for 1 */
      else				/* need more space */
	path = (char **) realloc (path, (num+1)*sizeof(char *));
      path[num] = (char *) malloc (strlen(cp)+1); /* space for path */
      strcpy (path[num], cp);		/* copy path into array */
      num++;				/* increment count */
    }
    cp = ++p;				/* move on */
  }
  /* the last path element */
  path = (char **) realloc (path, (num+2)*sizeof(char *));
  path[num] = (char *) malloc (strlen(cp)+1);
  strcpy (path[num], cp);
  num++;
  path[num] = NULL;			/* tie off with null path */
  return (path);
}

static char *
#if HAVE_STDC
autowrapspace (char *text, int *modified, int *ret)
#else /* K&R style */
autowrapspace (text, modified, ret)
char *text;
int *modified, *ret;
#endif /* HAVE_STDC */
{
  return (autowrapact (text, modified, ret, ' '));
}

static char *
#if HAVE_STDC
autowrapnl (char *text, int *modified, int *ret)
#else /* K&R style */
autowrapnl (text, modified, ret)
char *text;
int *modified, *ret;
#endif /* HAVE_STDC */
{
  return (autowrapact (text, modified, ret, '\n'));
}


static char *
#if HAVE_STDC
autowraptab (char *text, int *modified, int *ret)
#else /* K&R style */
autowraptab (text, modified, ret)
char *text;
int *modified, *ret;
#endif /* HAVE_STDC */
{
  return (autowrapact (text, modified, ret, '\t'));
}

static char *
#if HAVE_STDC
autowrapact(char *text, int *modified, int *ret, int c)
#else /* K&R style */
autowrapact(text, modified, ret, c)
char *text;
int *modified, *ret;
int c;
#endif /* HAVE_STDC */
{
    int i;
    int wordbeg, spacebeg;
    int maxcol;

    maxcol = cmcsb._cmwrp >= 0 ? cmcsb._cmwrp : cmcsb._cmcmx + cmcsb._cmwrp;
    if (cmcsb._cmwrp != 0 && lastlinelen(text) >= maxcol) {
	for(i = strlen(text)-1; i >= 0; i--) {
	    if (text[i] == ' ' || text[i] == '\t') {
		wordbeg = i+1;
		i--;
#ifdef undef
		while((text[i] == ' ' || text[i] == '\t') && i >= 0) i--;
#endif
		spacebeg = i+1;
		break;
	    }
	    else {
		if (text[i] == '\n' || i == 0 ||
		    (int)strlen(text) - i >= maxcol) {
		    strcat(text,"\n");
		    cmechx('\n');
		    *ret = FALSE;
		    *modified = TRUE;
		    return(text);
		}
	    }
	}
	if (cmcsb._cmcol != (int)strlen(text) - wordbeg) {
	    delcurrent((int)strlen(text) - wordbeg);
	    cmechx('\n');
	    cmxputs(&text[wordbeg]);
	}
	cmechx(c);
	text[spacebeg] = '\n';
	text[strlen(text)+1] = '\0';
	text[strlen(text)] = c;
	*ret = FALSE;
	*modified = TRUE;
	return(text);
    }
    text[strlen(text)+1] = '\0';
    text[strlen(text)] = c;
    cmechx(c);
    *ret = FALSE;
    *modified = TRUE;
    return(text);
}

static int
#if HAVE_STDC
lastlinelen(char *str)
#else /* K&R style */
lastlinelen(str)
char *str;
#endif /* HAVE_STDC */
{
    register int i,j=strlen(str);
    for(i = j - 1; i >= 0; i--)
	if (str[i] == '\n')
	    return(tablen(str + i + 1));
    return(tablen(str));
}

/*
 * calculate length of a line, even if there are tabs in it.
 */

static int
#if HAVE_STDC
tablen(char *str)
#else /* K&R style */
tablen(str)
char *str;
#endif /* HAVE_STDC */
{
    register char *cp = str;
    register int len=0;
    while(*cp) {
	if (*cp == '\t') {
	    len = ((len + 8) / 8) * 8;
	}
	else
	    len++;
	cp++;
    }
    return(len);
}


static char *
#if HAVE_STDC
filteract (char *text, int *modified, int *ret)
#else /* K&R style */
filteract (text, modified, ret)
char *text;
int *modified, *ret;
#endif /* HAVE_STDC */
{
  char fname1[40];
  char fname2[40];
  char *output;
  char *cmd;
  struct stat sbuf;
  int fd;
  int len;
  FILE *pp;

  if ((cmd = parse_cmdline("Filter: ")) == NULL) {
    *modified = FALSE;
    return (text);
  }

#if defined(HAVE_MKSTEMP)
    strcpy(fname1,"/tmp/cmd1XXXXXX");	/* get a file name */
    fd = mkstemp(fname1);
    if ((fd >= 0) && (fchmod(fd, 0700) < 0))
	fd = -1;
    strcpy(fname2,"/tmp/cmd2XXXXXX");	/* get a file name */
    fd = mkstemp(fname2);
    if ((fd >= 0) && (fchmod(fd, 0700) < 0))
	fd = -1;
#else
#if unix
  strcpy(fname1,"/tmp/cmd1XXXXXX");	/* get a file name */
  mktemp(fname1);
  strcpy(fname2,"/tmp/cmd2XXXXXX");	/* get a file name */
  mktemp(fname2);
#else
  strcpy(fname1,mktemp(getenv("TMP"),"cmd1")); /* get a file name */
  strcpy(fname2,mktemp(getenv("TMP"),"cmd2")); /* get a file name */
#endif
#endif /* defined(HAVE_MKSTEMP) */

  cmd = (char *) realloc (cmd, strlen(cmd)+strlen(fname1)+strlen(" < ")+
			  strlen(" > ") + strlen(fname2) +1);
  strcat (cmd, " < ");
  strcat (cmd, fname1);
  strcat (cmd, " > ");
  strcat (cmd, fname2);
  if ((pp = fopen (fname1, "w")) == NULL) {
    cmxputs ("Could not open temp file to execute command\n");
    *modified = FALSE;
    free (cmd);
    return (text);
  }
  fprintf (pp, "%s", text);
  fclose (pp);
  system(cmd);
  free (cmd);
  unlink(fname1);

  if (stat(fname2, &sbuf) == -1) {
    cmxputs ("?Temporary file missing\n");
    return (text);
  }
  len = (size_t)sbuf.st_size;
  if ((output = (char *) malloc (len+1)) == NULL) {
    cmxputs ("?Out of memory\n");
    *modified = FALSE;
    unlink(fname2);
    return (text);
  }
  if ((fd = open(fname2, O_RDONLY,0)) < 0) {
    cmxputs("?Could not open temporary file for read.\n");
    perror(fname2);
    unlink(fname2);
    free (output);
    return(text);
  }
#ifdef MSDOS
  if (read(fd,output,len) < 0)
#else
  if (read(fd,output,len) != len)
#endif
  {
    cmxputs("?Could not read temporary file.\n");
    perror(fname2);
    unlink(fname2);
    free (output);
    return(text);
  }
  close(fd);				/* close the file */
  output[len] = '\0';
  unlink (fname2);
  *modified = TRUE;
  *ret = FALSE;
  cmxputs("[Done]\n");
  return (output);
}



#ifdef MSDOS
static char *
mktemp(dir,prefix) char *dir, *prefix; {
  static int inited=FALSE;
  int x;
  char name[100];
  struct stat sbuf;
  if (!inited++)
    srand(1);
  x = rand();
  if (dir == NULL)
    dir = "./";
  strcpy(name,dir);
  strcat(name,"\\");
  strcat(name,prefix);
  strcat(name,atoi(x));
  if (stat(name,&sbuf) == 0)
    return(mktemp(dir,prefix));
  return(name);
}
#endif /*  MSDOS */

void
cm_para_abort(VOID) {
  abort = TRUE;
}

void
cm_para_eof(VOID) {
  eof = TRUE;
}


/*
 * delete to beginning of line.   mostly taken from begact() in stdact.c
 */
static char *
#if HAVE_STDC
begact(char *text, int *modified, int *ret)
#else /* K&R style */
begact(text,modified,ret) char *text; int *modified, *ret;
#endif /* HAVE_STDC */
{
  char *cp;				/* pointer to deletion site */
  int eralen;				/* # of chars erased */

  *modified = FALSE;
  *ret = FALSE;
  if (strlen(text) == 0) return(text);
  cp = text + strlen(text) - 1; 	/* point to end of buffer */
  while (cp-- != text) {		/* search for nonhidden newline */
    if (*cp == NEWLINE)			/* newline? */
      break;				/* yup, stop looking */
  }
  cp++;					/* point to char after break */
  eralen = (text - cp) + strlen(text); /* get # of chars erased */
  if (eralen <= 0) return(text);
  if (cmcsb._cmflg2 & CM_CRT)
    delcurrent(eralen);			/* erase the characters */
  else {
    cmxputs("^U");			/* signal line kill on hardcopy */
    cmxnl();				/* move to a new line */
  }
  *modified = TRUE;
  text[strlen(text)-eralen] = '\0';
  return(text);
}

/*
 * wrdact:
 * delete backwards one word.  If at the beginning of a line,
 * and previous line is empty, just deletes to beginning of
 * previous line.
 */
static char *
#if HAVE_STDC
wrdact(char *text, int *modified, int *ret)
#else /* K&R style */
wrdact(text,modified,ret) char *text; int *modified, *ret;
#endif /* HAVE_STDC */
{
  char *cp;				/* pointer to deletion site */
  char c;
  int eralen;				/* # of chars erased */

  *modified = FALSE;
  *ret = FALSE;
  if (strlen(text) == 0) {
    beep();
    return(text);
  }
  cp = text + strlen(text) - 1; 	/* point to end of buffer */
  while (cp-- != text) {		/* search for a character which is */
    c = *cp;
    if ((c < '0') ||			/* not a letter or digit? */
	 ((c > '9') && (c < 'A')) ||
	 ((c > 'Z') && (c < 'a')) ||
	 (c > 'z')
	)
      break;				/* yup, stop looking */
  }
  cp++;					/* point to char after break */
  eralen = (text - cp) + cmcsb._cminc; /* get # of chars erased */
  if (eralen <= 0) return(text);
  if (cmcsb._cmflg2 & CM_CRT)
    delcurrent(eralen);			/* erase the characters */
  else {
    cmxputs("^W");			/* signal line kill on hardcopy */
  }
  *modified = TRUE;
  text[strlen(text)-eralen] = '\0';	/* modify in place */
  return(text);
}

/* fixact
**
** Purpose:
**   Refresh the display of the current line of text, back to the
**   last unhidden newline character.  If the last character in the
**   buffer is a newline, the previous line is refreshed.
**/

static char *
#if HAVE_STDC
fixact(char *text, int *modified, int *ret)
#else /* K&R style */
fixact(text,modified,ret)
char *text;
int *modified;
int *ret;
#endif /* HAVE_STDC */
{
  char *cp;				/* pointer into buffer */
  char cc;				/* character under examination */
  char c;
  int reflen;				/* # of chars refreshed */

  *ret = FALSE;
  *modified = FALSE;
  if (strlen(text) == 0) return(text);
  cp = text + strlen(text) -1;		/* point to end of buffer */

  while (cp-- != text) {		/* search for nonhidden newline */
    if (*cp == NEWLINE)			/* newline? */
      break;				/* yup, stop looking */
  }
  cp++;					/* point to char after break */

  reflen = (text - cp) + strlen(text); /* get # of chars to refresh */

  if (cmcsb._cmflg2 & CM_CRT)
    delcurrent(reflen);			/* erase the characters from screen */
  else {
    cmxputs("^R");			/* signal line kill on hardcopy */
    cmxnl();				/* move to a new line */
  }
  while (reflen-- > 0) {		/* retype buffer contents */
    c = (cc = *cp++) & CC_CHR;		/* get next char */
    cmechx((int)(c));			/* do it again */
  }
  return(text);
}

/* quoact
**
** Purpose:
**   Enter the next character into the buffer with its quote flag
**   turned on, so it will be treated as a normal character regardless
**   of any handling it would normally receive.
**/
static char *
#if HAVE_STDC
quoact(char *text, int *modified, int *ret)
#else /* K&R style */
quoact(text,modified,ret)
char *text;
int *modified;
int *ret;
#endif /* HAVE_STDC */
{
  int c;				/* quoted character */

  cmgetc(&c,cmcsb._cmij);		/* get another character */
  cmechx(c);				/* do it again */
  *modified = TRUE;			/* we modified it */
  *ret = FALSE;				/* don't return yet */
  text[strlen(text)+1] = '\0';
  text[strlen(text)] = c;   /* | CC_QUO; */
  return(text);
}


/* delact
**
** Purpose:
**   Erase back to and including the last non-hidden character in the
**   command buffer.
**/
static char *
#if HAVE_STDC
delact(char *text, int *modified, int *ret)
#else /* K&R style */
delact(text,modified,ret)
char *text;
int *modified, *ret;
#endif /* HAVE_STDC */
{
  int eralen;				/* # of chars erased */

  *modified = FALSE;
  *ret = FALSE;
  if (strlen(text) == 0) {
    beep();
    return(text);
  }
  eralen = 1;
  if (cmcsb._cmflg2 & CM_CRT)
    delcurrent(eralen);			/* erase the characters */
  else {
    cmxputs("\\");			/* signal line kill on hardcopy */
  }
  *modified = TRUE;
  text[strlen(text)-eralen] = '\0';	/* modify in place */
  return(text);
}



/*
 * paragraph action char handler.
 * calls user defined handlers, fixes up text, and returns
 * CMxGO
 * Must perform context switch on ccmd's buffers, and maintain enough
 * buffer space for the current buffer
 */
static int
#if HAVE_STDC
break_hndlr(fdb *fdblist, int brk, int deferred)
#else /* K&R style */
break_hndlr(fdblist,brk,deferred)
fdb *fdblist;
int brk;
int deferred;
#endif /* HAVE_STDC */
{
  csb savecsb;
  jmp_buf rpbuf, erbuf;
  int col;
  int len, ret, modified;
  char *text, *ntext;
  int i;

  if (paraact[brk] == NULL) {
    cmsti1(brk,0);
    return(CMxGO);
  }

  if ((text = (char *)malloc(cmcsb._cminc+3)) == NULL) { /* space for buffer */
    return(PARAxNM);			/* and a few added chars */
  }

  for (i = 0; i < cmcsb._cminc; i++) {
    text[i] = cmcsb._cmptr[i] & 0x7f;
    if (text[i] == '\0') text[i] |= 0x80;
  }
  text[cmcsb._cminc] = '\0';

  savecsb = cmcsb;			/* save old state */
  cmcsb._cmrty = "";			/* no prompt here. */
  cmact(oldact);
  bcopy(cmrpjb,rpbuf,sizeof(jmp_buf));
  bcopy(cmerjb,erbuf,sizeof(jmp_buf));

  modified = ret = FALSE;		/* default values */

  if (paraact1[brk] != NULL) {		/* call handler with text */
    ntext = (*paraact1[brk])(text,&modified,&ret);
  }
  else
      ntext = NULL;
  col = cmcsb._cmcol;			/* need to remember where we were on */
					/* the screen.   this is independent */
					/* of the rest of the csb state. */

  cmcsb = savecsb;			/* restore old state */
  cmcsb._cmcol = col;
  bcopy(rpbuf,cmrpjb,sizeof(jmp_buf));
  bcopy(erbuf,cmerjb,sizeof(jmp_buf));
  cmact(paraact);

  if (text != ntext) {			/* new buffer. */
    free(text);				/* get rid of old one */
    text = NULL;
  }

  if (modified) {
    if ((int)strlen(ntext) >=
	cmcsb._cmcnt + cmcsb._cminc) { /* out of space? */
					/* then get some more space */
      cmcsb._cmcnt = strlen(ntext);
      cmcsb._cminc = 0;
      if ((cmdbuf =
	(int *)cmrealloc((char*)cmdbuf,
			 (cmcsb._cmcnt + BSIZ) * sizeof(int)))==NULL) {
	return(PARAxNM);
      }
      cmcsb._cmcnt += BSIZ;		/* count it */
      if ((cmcsb._cmwbp =
	   (char *)cmrealloc(cmcsb._cmwbp,
			     sizeof(*cmcsb._cmwbp)*cmcsb._cmcnt))==NULL){
	return(PARAxNM);
      }
      cmcsb._cmwbc = cmcsb._cmcnt;	/* and install it into the CSB */
      cmcsb._cmptr = cmdbuf;
      cmcsb._cmbfp = cmdbuf;
      cmcsb._cmcur = cmdbuf;
    }
    else {
      cmcsb._cmptr = cmcsb._cmcur = cmcsb._cmbfp;
      cmcsb._cmcnt += cmcsb._cminc;
      cmcsb._cminc = 0;
      cmcsb._cmwbc = cmcsb._cmcnt;
    }
    len = strlen(ntext);
    for (i = 0; i < len; i++)		/* unquote everything */
      ntext[i] &= 0x7f;
    cmsti(ntext,CC_NEC|CC_HID|CC_QUO);		/* insert text into buffer */
    for (i = 0; i < cmcsb._cminc; i++)
      cmcsb._cmptr[i] &= ~(CC_HID|CC_NEC); /* mark it unhidden */
  }
  eof = ret;
  if (ntext)
    free(ntext);
  return(CMxGO);
}

static char *
#if HAVE_STDC
self_ins_nl(char *text, int *modified, int *ret)
#else /* K&R style */
self_ins_nl(text, modified, ret)
char *text;
int *modified, *ret;
#endif /* HAVE_STDC */
{
  strcat(text,"\n");
  cmechx('\n');
  *modified = TRUE;
  return(text);
}

static void
install_nl_action(VOID)
{
  paraact['\n'] = break_hndlr;
  paraact1['\n'] = self_ins_nl;
}
