// file/buffer manipulation routines

#include "Include.h"

#define MAX_QUOTE_COL 78

static unsigned char *xnext;

// internally used functions
char *STD gtfilename(char *, int);
STD getfile(char *, int );
STD real_readin(char *, int, int);
STD writeout(char *, char *);
static void xenoprep(char *);
static int REG xenogetline(void);
REG emacs2dlg(short);


/// wrapreadin()
/*
** char  fname[];    name of file to read
** int   lockfl;     check for file locks?
 ***********************************************************************/
STD wrapreadin(char fname[], int lockfl)
{
   return real_readin(fname, lockfl, TRUE);
}
//-

/// quotemsg()
/*
** Read quoted message into current buffer if not already done.
 ***********************************************************************/
STD quotemsg(int f, int n)
{
   static char already = 0;
   char *xenotext = NULL;
   register LINE *lp1;
   register LINE *lp2;
   register WINDOW *wp;
   register BUFFER *bp;
   register int nbytes;
   register int nline;
   register int cmark; /* current mark */
   int skipinitblanks = 1;
   BPTR rh = NULL;
   
   if (already) return TRUE;
   ++already;

   // read the message body into one big buffer ala xenolink
   if (dlg_quote)
   {
      ULONG sz;
      UBYTE skip_header;
      
      if (hack_quote)
      {
         // do special quote for hack&slash

         if (0 == FileSize(hack_quote,&sz))
         {
            if (sz > 0)
            {
               xenotext = malloc(sz+1);
               rh = Open(hack_quote, MODE_OLDFILE);
               skip_header = 0;
            }
         }
      }
      else
      {
         if (0 == FileSize(dlg_reply,&sz))
         {
            sz = sz - sizeof(struct Msg_Header);

            if (sz > 0)
            {
               xenotext = malloc(sz+1);
               rh = Open(dlg_reply, MODE_OLDFILE);
               skip_header = 1;
            }
         }
      }

      if (xenotext)
      {
         *xenotext = 0;

         if (rh)
         {
            if (skip_header)
            {
               Seek(rh, sizeof(struct Msg_Header), OFFSET_BEGINNING);
            }

            Read(rh, xenotext, sz);
            xenotext[sz] = 0;
         }
      }

      if (rh)  Close(rh);
   }
   
   bp = curbp;            /* Cheap.      */

   /* turn off ALL keyboard translation in case we get a dos error */
   TTkclose();


   nline = 0;
   xenoprep(xenotext);
   while (xenogetline())
   {

      // NOTE: not reusable if we ever reread the quote file
      if (hack_ignore)
      {
         // ignore leading lines
         --hack_ignore;
         continue;
      }
      
      
      if (*fline == 1)
      {
         /* hide FIDONET kludge lines */
         /* probably not necessary in z2b */
         /* but z2b is inserting blank lines here, it seems */
         continue;
      }

      nbytes = strlen(fline);
      if (nbytes == 0 && skipinitblanks)
      {
         /* remove all blank lines at beginning of message */
         continue;
      }
      skipinitblanks = 0;

      if ((lp1=lalloc(nbytes)) == NULL)
      {
         break;
      }
      lp2 = lback(bp->b_linep);
      lp2->l_fp = lp1;
      lp1->l_fp = bp->b_linep;
      lp1->l_bp = lp2;
      bp->b_linep->l_bp = lp1;
      memcpy(lp1->l_text, fline, nbytes);
      ++nline;
   }
   out:

   TTkopen(); /* open the keyboard again */
   for (wp=wheadp; wp!=NULL; wp=wp->w_wndp)
   {
      if (wp->w_bufp == bp)
      {
         wp->w_linep = lforw(bp->b_linep);
         wp->w_dotp  = lforw(bp->b_linep);
         wp->w_doto  = 0;
         for (cmark = 0; cmark < NMARKS; cmark++)
         {
            wp->w_markp[cmark] = NULL;
            wp->w_marko[cmark] = 0;
         }
         wp->w_flag |= WFMODE|WFHARD;
      }
   }
   return(TRUE);
}
//-

/// fileread()
/*
** Read a file into the current buffer. This is really easy; all you do is
** find the name of the file, and call the standard
** "read a file into the current buffer" code.
** Bound to "C-X C-R".
 ***********************************************************************/
STD fileread(int f, int n)
{
   char *fname;   /* file name to read */

   if (restflag)     /* don't allow this command if restricted */
   return(resterr());

   if ((fname = gtfilename(TEXT131, 0)) == NULL)
   /*                              "Read file" */
   return(FALSE);
   return(readin(fname, TRUE));
}
//-

/// insfile()
/*
** Insert a file into the current buffer. This is really easy; all you do it
** find the name of the file, and call the standard
** "insert a file into the current buffer" code.
** Bound to "C-X C-I".
 ***********************************************************************/
STD insfile(int f, int n)
{
   register int   s;
   char *fname;   /* file name */
   LINE *curline;
   char userbuf[NSTRING];
   int wrt;
   int uuans;
   
   // if (restflag || xl_access < xl_acc_read)  /* don't allow this command if restricted */
   //    return(resterr());
   if (restflag)  /* don't allow this command if restricted */
   return(resterr());
   if (curbp->b_mode&MDVIEW)      /* don't allow this command if  */
   return(rdonly()); /* we are in read only mode   */

   // use requester if on local port and user is sysop level.
   // non sysop cannot enter / and : in pathnames
   if (xl_access < xl_acc_read) {
      wrt = -1;
   }
   else {
      wrt = 0;
   }
   if ((fname = gtfilename(TEXT132, wrt)) == NULL) 
   /*                              "Insert file" */
   return(FALSE);

   // Check user access level.  If they do not have read access,
   // insert file from their private directory only.
   if (xl_access < xl_acc_read) {
      char *p = fname;
      while (*p) {
         if (*p == '/' || *p == ':') {
            return(resterr());
         }
         lowercase(p);
         ++p;
      }
      if (strcmp(fname, "user.data") == 0) {
         return(resterr());
      }
      strcpy(userbuf, "user:");
      strcat(userbuf, dlg_uname);
      strcat(userbuf, "/");
      strcat(userbuf, fname);
      fname = userbuf;
   }
   
   /*
    * Save the local pointers to hold global ".", in case
    * $yankflag is set to 1.  Insert-file always places the
    * starting offset point at 0.  Hold *previous* line
    * position, since the current line may be re-allocated.
    */
   // XENO_ always puts cursor at the start
   curline = lback(curwp->w_dotp);

   // check if binary file and uuencode if needed
   if (uunecessary(fname)) {
      uuans = xenoyesno("File contains binary characters, uuencode it", *xl_yes, 'q', "quit");
      if (uuans == ABORT) {
         return FALSE;
      }
      else if (uuans == TRUE) {
         char uu[NSTRING];
         strcpy(uu, "user:");
         strcat(uu, dlg_uname);
         strcat(uu, "/uu.out");
         mlwrite("uuencoding...");
         if (uuencode(fname, uu) != 0) {
            DeleteFile(uu);
            return FALSE;
         }
         s = ifile(uu);
         DeleteFile(uu);
      }
      else {
         s = ifile(fname);
      }
   }
   else {

      s = ifile(fname);
   }

   curwp->w_dotp = lforw(curline);

   return (s);
}
//-

/// filefind()
/*
** Select a file for editing. Look around to see if you can find the
** fine in another buffer; if you can find it just switch to the buffer.
** If you cannot find the file, create a new buffer, read in the
** text, and switch to the new buffer. Bound to C-X C-F.
 ***********************************************************************/
STD filefind(int f, int n)
{
   char *fname;   /* file user wishes to find */   /* file name */

   if (restflag)     /* don't allow this command if restricted */
   return(resterr());

   if ((fname = gtfilename(TEXT133, 0)) == NULL) 
   /*                              "Find file" */
   return(FALSE);
   return(getfile(fname, TRUE));
}
//-

/// viewfile()
/* visit a file in VIEW mode */
STD viewfile(int f, int n)
{
   char *fname;   /* file user wishes to find */   /* file name */
   register int s;   /* status return */

   if (restflag)     /* don't allow this command if restricted */
   return(resterr());

   if ((fname = gtfilename(TEXT134, 0)) == NULL) 
   /*                              "View file" */
   return(FALSE);
   s = getfile(fname, FALSE);
   if (s) {
      /* if we succeed, put it in view mode */
      curwp->w_bufp->b_mode |= MDVIEW;
      upmode();
   }
   return(s);
}
//-

/// readin()
/*
** Read file "fname" into the current buffer, blowing away any text
** found there.  Called by both the read and find commands.  Return
** the final status of the read.  Also called by the mainline, to
** read in a file specified on the command line as an argument.
** The command in $readhook is called after the buffer is set up
** and before it is read.
**
** char  fname[];    name of file to read
** int   lockfl;     check for file locks?
 ***********************************************************************/
STD readin(char fname[], int lockfl)
{
   return real_readin(fname, lockfl, FALSE);
}
//-

/// real_readin()
/*
** char  fname[];    name of file to read
** int   lockfl;     check for file locks?
** int   wrapflag;   auto word-wrap while reading?  DLG
 **********************************************************************/
STD real_readin(char fname[], int lockfl, int wrapflag)
{
   register LINE *lp1;
   register LINE *lp2;
   register WINDOW *wp;
   register BUFFER *bp;
   register int s;
   register int nbytes;
   register int nline;
   register int cmark;  /* current mark */
   int i;
   char mesg[NSTRING];
   short silent;

   /* XENO: don't show loading message for startup file */
   // oh this is a bug in EmacsDLG and EmacsXL
   // if (strcmp(fname+strlen(fname)-strlen(pathname[0]), pathname[0]) == 0 ||
   //     strcmp(fname+strlen(fname)-strlen(pathname[1]), pathname[1]) == 0)
   if (strcmp(fname+strlen(fname)-strlen(pathname[0]), pathname[0]) == 0)
   silent = 1;
   else
   silent = 0;


   bp = curbp;          /* Cheap.      */
   if ((s=bclear(bp)) != TRUE)      /* Might be old.  */
   return(s);
   bp->b_flag &= ~(BFINVS|BFCHG);
   strcpy(bp->b_fname, fname);

   /* let a user macro get hold of things...if he wants */
   execkey(&readhook, FALSE, 1);


   /* turn off ALL keyboard translation in case we get a dos error */
   TTkclose();

   if ((s=ffropen(fname)) == FIOERR)   /* Hard file open.   */
   goto out;

   if (s == FIOFNF) {
      /* File not found.   */
      if (!silent) mlwrite(TEXT138);
      /*                      "[New file]" */
      goto out;
   }

   /* read the file in */
   if (!silent) mlwrite(TEXT139);
   /*              "[Reading file]" */
   nline = 0;
   while ((s=ffgetline()) == FIOSUC) {
      nbytes = strlen(fline);
      if (wrapflag) {
         // i bet this is really really slow
         extern void h_insert(char);
         for (i=0; i<nbytes; ++i) {
            // DLG stores signatures with ^M but not ^J
            // we want to ignore the ^M on input, ignore ^J on output
            if (fline[i] != 13) {
               h_insert(fline[i]);
            }
         }
         lnewline();
         ++nline; // not really accurate
      }
      else {
         if ((lp1=lalloc(nbytes)) == NULL) {
            s = FIOMEM;    /* Keep message on the  */
            break;         /* display.    */
         }
         lp2 = lback(curbp->b_linep);
         lp2->l_fp = lp1;
         lp1->l_fp = curbp->b_linep;
         lp1->l_bp = lp2;
         curbp->b_linep->l_bp = lp1;
         //    for (i=0; i<nbytes; ++i)
         //       lputc(lp1, i, fline[i]);
         memcpy(lp1->l_text, fline, nbytes);
         ++nline;
      }
   }
   ffclose();           /* Ignore errors. */
   //   dlg_debug("read existing file");
   strcpy(mesg, "[");
   if (s==FIOERR) {
      strcat(mesg, TEXT141);
      /*                           "I/O ERROR, " */
      curbp->b_flag |= BFTRUNC;
   }
   if (s == FIOMEM) {
      strcat(mesg, TEXT142);
      /*                           "OUT OF MEMORY, " */
      curbp->b_flag |= BFTRUNC;
   }
   strcat(mesg, TEXT140);
   /*                   "Read " */
   strcat(mesg, int_asc(nline));
   strcat(mesg, TEXT143);
   /*                   " line" */
   if (nline > 1)
   strcat(mesg, "s");
   strcat(mesg, "]");
   if (!silent) mlwrite(mesg);

   out:
   TTkopen();  /* open the keyboard again */
   for (wp=wheadp; wp!=NULL; wp=wp->w_wndp) {
      if (wp->w_bufp == curbp) {
         wp->w_linep = lforw(curbp->b_linep);
         wp->w_dotp  = lforw(curbp->b_linep);
         wp->w_doto  = 0;
         for (cmark = 0; cmark < NMARKS; cmark++) {
            wp->w_markp[cmark] = NULL;
            wp->w_marko[cmark] = 0;
         }
         wp->w_flag |= WFMODE|WFHARD;
      }
   }
   if (s == FIOERR || s == FIOFNF)  /* False if error.   */
   return(FALSE);
   return(TRUE);
}
//-

/// makename
/*
** Take a file name, and from it fabricate a buffer name. This routine knows
** about the syntax of file names on the target system. I suppose that this
** information could be put in a better place than a line of code.
** Returns a pointer into fname indicating the end of the file path; i.e.,
** 1 character BEYOND the path name.
****************************************/
char *STD makename(char *bname, char *fname)
{
   register char *cp1;
   register char *cp2;
   register char *pathp;

   cp1 = &fname[0];
   while (*cp1 != 0)
   ++cp1;

   while (cp1!=&fname[0] && cp1[-1]!=':' && cp1[-1]!='/')
   --cp1;
   /* cp1 is pointing to the first real filename char */
   pathp = cp1;

   cp2 = &bname[0];
   while (cp2!=&bname[NBUFN-1] && *cp1!=0 && *cp1!=';')
   *cp2++ = *cp1++;
   *cp2 = 0;

   return(pathp);
}
//-

/// filewrite()
/*
** Ask for a file name, and write the contents of the current buffer to that file.
** Update the remembered file name and clear the
** buffer changed flag. This handling of file names
** is different from the earlier versions, and
** is more compatable with Gosling EMACS than with ITS EMACS. Bound to "C-X C-W"
** for writing and ^X^A for appending.
 ***********************************************************************/
STD filewrite(int f, int n)
{
   register int s;
   char *fname;
   if (restflag || xl_access < xl_acc_write) /* don't allow this command if restricted */
   return(resterr());
   fname = gtfilename(TEXT144, 1);
   if (!fname)
   return FALSE;
   if ((s=writeout(fname, "w")) == TRUE) {
      strcpy(curbp->b_fname, fname);
      curbp->b_flag &= ~BFCHG;
      /* Update mode lines.   */
      upmode();
   }
   return(s);
}
//-

/// filesave()
/*
** Save the contents of the current buffer in its associatd file. Do nothing
** if nothing has changed (this may be a bug, not a
** feature). Error if there is no remembered file
** name for the buffer. Bound to "C-X C-S". May
** get called by "C-Z".
 ***********************************************************************/
STD filesave(int f, int n)
{
   if (curbp->b_mode&MDVIEW)  /* don't allow this command if   */
   return(rdonly()); /* we are in read only mode   */
   // allow it to work after write-file
   // if ((curbp->b_flag&BFCHG) == 0)  /* Return, no changes.  */
   //    return(TRUE);

   // if saving a message, store fido newline convetions.
   // otherwise store ^M for line terminators.
   emacs2dlg(dlg_hdr_style == STYLE_MESSAGE ? FALSE : TRUE);

   curbp->b_flag &= ~BFCHG;
   /* Update mode lines.   */
   upmode();

   return TRUE;
}
//-

/// ifile()
/*
** Insert file "fname" into the current
** buffer, Called by insert file command. Return the final
** status of the read.
 ***********************************************************************/
STD ifile(char fname[])
{
   register LINE *lp0;
   register LINE *lp1;
   register LINE *lp2;
   register BUFFER *bp;
   register int s;
   register int nbytes;
   register int nline;
   int cmark;  /* current mark */
   char mesg[NSTRING];

   bp = curbp;          /* Cheap.      */
   bp->b_flag |= BFCHG;       /* we have changed   */
   bp->b_flag &= ~BFINVS;        /* and are not temporary*/
   if ((s=ffropen(fname)) == FIOERR)   /* Hard file open.   */
   goto out;
   if (s == FIOFNF) {
      /* File not found.   */
      mlwrite(TEXT152);
      /*                      "[No such file]" */
      return(FALSE);
   }
   mlwrite(TEXT153);
   /*              "[Inserting file]" */

   /* back up a line and save the mark here */
   curwp->w_dotp = lback(curwp->w_dotp);
   curwp->w_doto = 0;
   for (cmark = 0; cmark < NMARKS; cmark++) {
      curwp->w_markp[cmark] = curwp->w_dotp;
      curwp->w_marko[cmark] = 0;
   }

   nline = 0;
   while ((s=ffgetline()) == FIOSUC) {

      nbytes = strlen(fline);
      if ((lp1=lalloc(nbytes)) == NULL) {
         s = FIOMEM;    /* Keep message on the  */
         break;         /* display.    */
      }
      lp0 = curwp->w_dotp;  /* line previous to insert */
      lp2 = lp0->l_fp;  /* line after insert */

      /* re-link new line between lp0 and lp2 */
      lp2->l_bp = lp1;
      lp0->l_fp = lp1;
      lp1->l_bp = lp0;
      lp1->l_fp = lp2;

      /* and advance and write out the current line */
      curwp->w_dotp = lp1;
      //    for (i=0; i<nbytes; ++i)
      //       lputc(lp1, i, fline[i]);
      memcpy(lp1->l_text, fline, nbytes);
      ++nline;
   }
   ffclose();           /* Ignore errors. */
   curwp->w_markp[0] = lforw(curwp->w_markp[0]);
   strcpy(mesg, "[");
   if (s==FIOERR) {
      strcat(mesg, TEXT141);
      /*                           "I/O ERROR, " */
      curbp->b_flag |= BFTRUNC;
   }
   if (s == FIOMEM) {
      strcat(mesg, TEXT142);
      /*                           "OUT OF MEMORY, " */
      curbp->b_flag |= BFTRUNC;
   }
   strcat(mesg, TEXT154);
   /*                   "Inserted " */
   strcat(mesg, int_asc(nline));
   strcat(mesg, TEXT143);
   /*                   " line" */
   if (nline > 1)
   strcat(mesg, "s");
   strcat(mesg, "]");
   mlwrite(mesg);

   out:
   /* advance to the next line and mark the window for changes */
   curwp->w_dotp = lforw(curwp->w_dotp);
   curwp->w_flag |= WFHARD | WFMODE;

   /* copy window parameters back to the buffer structure */
   curbp->b_dotp = curwp->w_dotp;
   curbp->b_doto = curwp->w_doto;
   for (cmark = 0; cmark < NMARKS; cmark++) {
      curbp->b_markp[cmark] = curwp->w_markp[cmark];
      curbp->b_marko[cmark] = curwp->w_marko[cmark];
   }
   curbp->b_fcol = curwp->w_fcol;

   if (s == FIOERR)        /* False if error.   */
   return(FALSE);
   return(TRUE);
}
//-

// internally used functions

/// getfile()
/*
** char fname[];     file name to find
** int lockfl;       check the file for locks?
*******************/
STD getfile(char fname[], int lockfl)
{
   register BUFFER *bp;
   register LINE  *lp;
   register int   i;
   register int   s;
   register int cmark;  /* current mark */
   char bname[NBUFN];   /* buffer name to put file */

   for (bp=bheadp; bp!=NULL; bp=bp->b_bufp) {
      if ((bp->b_flag&BFINVS)==0 && strcmp(bp->b_fname, fname)==0) {
         swbuffer(bp);
         lp = curwp->w_dotp;
         i = curwp->w_ntrows/2;
         while (i-- && lback(lp)!=curbp->b_linep)
         lp = lback(lp);
         curwp->w_linep = lp;
         curwp->w_flag |= WFMODE|WFHARD;
         mlwrite(TEXT135);
         /*                              "[Old buffer]" */
         return(TRUE);
      }
   }
   makename(bname, fname);       /* New buffer name.  */

   while ((bp=bfind(bname, FALSE, 0)) != NULL) {
      /* old buffer name conflict code */
      s = mlreply(TEXT136, bname, NBUFN);
      /*                          "Buffer name: " */
      if (s == ABORT)      /* ^G to just quit   */
      return(s);
      if (s == FALSE) {
         /* CR to clobber it  */
         makename(bname, fname);
         break;
      }
   }
   if (bp==NULL && (bp=bfind(bname, TRUE, 0))==NULL) {
      mlwrite(TEXT137);
      /*                      "Cannot create buffer" */
      return(FALSE);
   }

   if (--curbp->b_nwnd == 0) {
      /* Undisplay.     */
      curbp->b_dotp = curwp->w_dotp;
      curbp->b_doto = curwp->w_doto;
      for (cmark = 0; cmark < NMARKS; cmark++) {
         curbp->b_markp[cmark] = curwp->w_markp[cmark];
         curbp->b_marko[cmark] = curwp->w_marko[cmark];
      }
      curbp->b_fcol = curwp->w_fcol;
   }
   curbp = bp;          /* Switch to it.  */
   curwp->w_bufp = bp;
   curbp->b_nwnd++;
   return(readin(fname, lockfl));      /* Read it in.    */
}
//-

/// gtfilename
/*
** char *prompt;     prompt to user on command line
** int wrt;
**                   wrt == 0 for read prompt
**                   wrt == 1 for write prompt
**                   wrt == -1 to prevent use of the requester even if on local
******************/
char *STD gtfilename(char *prompt, int wrt)
{
   char *sp;   /* ptr to the returned string */

   sp = complete(prompt, NULL, CMP_FILENAME, NFILEN, wrt);

   if (sp == NULL)
   return(NULL);

   return(sp);
}
//-

/// writeout()
/*
** This function performs the details of file writing. It uses
** the file management routines in the "fileio.c" package. The
** number of lines written is displayed. Several errors are
** posible, and cause writeout to return a FALSE result. When
** $ssave is TRUE,  the buffer is written out to a temporary
** file, and then the old file is unlinked and the temporary
** renamed to the original name.  Before the file is written,
** a user specifyable routine (in $writehook) can be run.
**
** char *fn;      name of file to write current buffer to
** char *mode;    mode to open file (w = write a = append)
 ***********************************************************************/
STD writeout(char *fn, char *mode)
{
   register LINE *lp;   /* line to scan while writing */
   register char *sp;   /* temporary string pointer */
   register int nline;  /* number of lines written */
   int status;    /* return status */
   int sflag;     /* are we safe saving? */
   char tname[NSTRING]; /* temporary file name */
   char buf[NSTRING];   /* message buffer */

   /* let a user macro get hold of things...if he wants */
   execkey(&writehook, FALSE, 1);

   /* determine if we will use the save method */
   sflag = FALSE;
   if (ssave && Exists(fn) && *mode == 'w')
   sflag = TRUE;


   /* turn off ALL keyboard translation in case we get a dos error */
   TTkclose();

   /* Perform Safe Save..... */
   if (sflag) {
      /* duplicate original file name, and find where to trunc it */
      sp = tname + (makename(tname, fn) - fn) + 1;
      strcpy(tname, fn);

      /* create a unique name, using random numbers */
      do {
         *sp = 0;
         strcat(tname, int_asc(ernd() & 0xffff));
      }
      while(Exists(tname));

      /* open the temporary file */
      status = ffwopen(tname, "w");
   }
   else
   status = ffwopen(fn, mode);

   /* if the open failed.. clean up and abort */
   if (status != FIOSUC) {
      TTkopen();
      return(FALSE);
   }

   /* write the current buffer's lines to the open disk file */
   mlwrite(TEXT148); /* tell us that we're writing */
   /*              "[Writing...]" */
   lp = lforw(curbp->b_linep);   /* start at the first line.   */
   nline = 0;        /* track the Number of lines  */
   while (lp != curbp->b_linep) {
      if ((status = ffputline(&lp->l_text[0], llength(lp))) != FIOSUC)
      break;
      ++nline;
      lp = lforw(lp);
   }


   /* report on status of file write */
   *buf = 0;
   status |= ffclose();
   if (status == FIOSUC) {
      /* report on success (or lack therof) */
      strcpy(buf, TEXT149);
      /*                          "[Wrote " */
      strcat(buf, int_asc(nline));
      strcat(buf, TEXT143);
      /*                          " line" */
      if (nline > 1)
      strcat(buf, "s");

      if (sflag) {
         /* erase original file */
         /* rename temporary file to original name */
         unlink(fn);
         rename(tname, fn);
      }
      strcat(buf, "]");
      mlwrite(buf);
   }

   /* reopen the keyboard, and return our status */
   TTkopen();
   return(status == FIOSUC);
}
//-

/// xenoprep()
static void xenoprep(char *xt)
{
   xnext = xt;
}
//-

/// xenogetline
static int REG xenogetline(void)
{
   char *p;
   int n;
   char *p_last, *x_last;
   int add_initials;
   int all_blanks = 1;

   if (xnext == NULL || *xnext == 0)
   return FALSE;

   if (*xnext == 0x0d && *(xnext+1) == 0x0a) {
      xnext += 2;
      *fline = 0;
      return TRUE;
   }

   /* alan added this 7/14 for better blank line handling */
   if (*xnext == 0x0d) {
      ++xnext;
      *fline = 0;
      return TRUE;
   }
   add_initials = 1;
   if (isaquote(xnext)) {
      /* leave XXX> quotes alone */
      /* change > quotes to YYY> > */
      if (*xnext != '>') {
         add_initials = 0;
      }
   }

   /* no quotes for fidonet kludge lines -- will get filtered later */
   if (*xnext == 1) {
      add_initials = 0;
   }

   if (add_initials && dlg_initials) {
      *fline = ' ';
      strcpy(fline+1, dlg_initials);
      n = strlen(fline);
      fline[n++] = '>';
      fline[n++] = ' ';
   }
   else {
      n = 0;
   }
   p = fline + n;
   p_last = x_last = NULL;

   /* n is a column count w/tabs, not a string length */
   while (*xnext) {

      /* probably can remove soft returns here */

      // added 0x0a case when i saw a line terminated with ^J
      if (*xnext == 0x0d || *xnext == 0x0a ||
      (!add_initials && (*xnext&0xff) == 0x8d)) {
         if (all_blanks) {
            *fline = 0;
         }
         *p = 0;
         ++xnext;
         if (*xnext == 0x0a || ((*xnext&0xff) == 0x8d)) {
            ++xnext;
         }
         return TRUE;
      }
      if ((*xnext&0xff) == 0x8d) {
         ++xnext;
         continue;
      }
      if (*xnext == ' ') {
         x_last = xnext;
         p_last = p;
      }
      else if (*xnext == '\t') {
         x_last = xnext;
         p_last = p;
         n += -(n % tabsize) + (tabsize - 1);
      }
      else {
         all_blanks = 0;
      }
      *p++ = *xnext;
      ++n;
      if (add_initials && n >= MAX_QUOTE_COL) {
         /* force word wrap */
         if (x_last) {
            xnext = x_last + 1;
            *p_last = 0;
            return TRUE;
         }
         else {
            // 10/92 why was this else clause missing?
            fline[MAX_QUOTE_COL] = 0;
            while (*xnext && *xnext != 0x0d) ++xnext;
            if (*xnext == 0x0a) ++xnext;
            return TRUE;
         }
      }
      else if (!add_initials && n >= MAX_QUOTE_COL) {
         /* truncate long quoted quote */
         fline[MAX_QUOTE_COL] = 0;
         while (*xnext && *xnext != 0x0d) ++xnext;
         // 7/14/93 skip over the 0x0d
         if (*xnext) ++xnext;

         if (*xnext == 0x0a) ++xnext;
         return TRUE;
      }
      ++xnext;
   }

   if (all_blanks) {
      *fline = 0;
   }
   *p = 0;
   return TRUE;
}
//-

/// emacs2dlg()
REG emacs2dlg(short ignore_linefeeds)
{
   register LINE *lp;   /* line to scan while writing */
   register int nline;  /* number of lines written */
   char *xenotext;
   register unsigned char *xenoptr;
   char *stemp;
   char buf[80];
   int ntemp;
   int i;
   int quoted;

   ignore_linefeeds = TRUE; /* always!!!! */

   /* let a user macro get hold of things...if he wants */
   execkey(&writehook, FALSE, 1);

   // first append the fortune cookie if necessary
   do_fortune();

   /* turn off ALL keyboard translation in case we get a dos error */

   /* write the current buffer's lines to the open disk file */
   mlwrite(TEXT148); /* tell us that we're writing */
   /*              "[Writing...]" */

   /* first need to pre-allocate xeno message buffer */
   lp = lforw(curbp->b_linep);   /* start at the first line.   */
   ntemp = 0;
   while (lp != curbp->b_linep) {
      ntemp += llength(lp) + 2;  /* line + CR NL */
      lp = lforw(lp);
   }

   xenotext = malloc(ntemp+1);
   if (!xenotext) {
      mlwrite(TEXT142);
      /*                  "OUT OF MEMORY, " */
      return(FALSE);
   }

   lp = lforw(curbp->b_linep);   /* start at the first line.   */
   nline = 0;        /* track the Number of lines  */
   xenoptr = xenotext;
   while (lp != curbp->b_linep) {

      /* put hard CRNL on previous line if empty line */
      /* or quoted line or beginning of paragraph */
      quoted = isaquote(lp->l_text);
      ntemp = *lp->l_text;
      if (nline > 0 &&
      (llength(lp) == 0 || quoted ||
      (ntemp == ' ' || ntemp == '\t'))) {
         if (ignore_linefeeds) {
            *(xenoptr-1) = 0x0d;
         }
         else {
            // previous line may have \r\n or one blank
            // since DLG doesnt do double soft returns.
            if (*(xenoptr-1) == ' ') {
               *(xenoptr-1) = 0x0d;
               *xenoptr++ = 0x0a;
            }
         }
      }

      /* entirely blank line == hard CRNL */
      ntemp = 0;
      if (llength(lp) > 0) {
         for (stemp = lp->l_text, i=0; i < llength(lp); ++stemp, ++i) {
            if (*stemp != ' ' && *stemp != '\t') {
               ntemp = 1;
               break;
            }
         }
      }
      if (ntemp) {

         /* line is not entirely blank */
         bytecopy(xenoptr, lp->l_text, llength(lp));
         xenoptr += llength(lp);

         if (lp->l_xeno == L_HARD) {
            *xenoptr++ = 0x0d;
            if (!ignore_linefeeds) *xenoptr++ = 0x0a;
         }
         else {
            // SOFT RETURNS NOT USED FOR DLG, USE BLANK INSTEAD
            *xenoptr++ = ' ';
         }
      }
      else {
         /* line is entirely blank */
         if (nline > 0) {
            if (ignore_linefeeds) {
               *(xenoptr-1) = 0x0d;
            }
            else {
               // previous line may have \r\n or one blank
               // since DLG doesnt do double soft returns.
               if (*(xenoptr-1) == ' ') {
                  *(xenoptr-1) = 0x0d;
                  *xenoptr++ = 0x0a;
               }
               /* ALAN ADDED THIS 6/27/93 TO TRY TO FIX THE MISSING NEWLINE BUG */
               else if (*(xenoptr-1) == 0x0a &&
               *(xenoptr-2) != 0x0d) {
                  *(xenoptr-1) = 0x0d;
                  *xenoptr++ = 0x0a;
               }
               /* END OF 6/27/93 ADDITION */
            }
         }
         *xenoptr++ = 0x0d;
         if (!ignore_linefeeds) *xenoptr++ = 0x0a;
      }

      ++nline;
      lp = lforw(lp);
   }

   /* remove all trailing blank lines */
   if (ignore_linefeeds) {
      while (nline > 0) {
         if (*(xenoptr-1) == 0x0d) {
            *xenoptr-- = 0;
            --nline;
         }
         else {
            break;
         }
      }
   }
   else {
      while (nline > 0) {
         if (*(xenoptr-2) == 0x0d && *(xenoptr-1) == 0x0a) {
            *xenoptr-- = 0;
            *xenoptr-- = 0;
            --nline;
         }
         else {
            break;
         }
      }
   }

   /* end message with hard crnl */
   *xenoptr++ = 0x0d;
   if (!ignore_linefeeds) *xenoptr++ = 0x0a;
   *xenoptr = 0;

   i = dlgsave(xenotext);
   free(xenotext);

   /* report on success (or lack therof) */
   /* error already displayed if failure */
   if (i == TRUE) {
      strcpy(buf, TEXT149);
      /*                          "[Wrote " */
      strcat(buf, int_asc(nline));
      strcat(buf, TEXT143);
      /*                          " line" */
      if (nline > 1)
      strcat(buf, "s");
      strcat(buf, "]");
   }
   mlwrite(buf);

   /* reopen the keyboard, and return our status */
   return(TRUE);
}
//-


