// buffer manipulation routines

#include "Include.h"

// internally used functions

BUFFER *STD getdefb(void);

/// anycb()
/*
** Look through the list of buffers. Return TRUE if there
** are any changed buffers. Buffers that hold magic internal stuff are
** not considered; who cares if the list of buffer names is hacked.
** Return FALSE if no buffers have been changed.
 ***********************************************************************/
STD anycb(void)
{
   register BUFFER *bp;

   bp = bheadp;
   while (bp != NULL) {
      if ((bp->b_flag&BFINVS)==0 && (bp->b_flag&BFCHG)!=0)
      return(TRUE);
      bp = bp->b_bufp;
   }
   return(FALSE);
}
//-

/// usebuffer()
/*
** Attach a buffer to a window. The
** values of dot and mark come from the buffer
** if the use count is 0. Otherwise, they come
** from some other window.
 ***********************************************************************/
STD usebuffer(int f, int n)
{
   register BUFFER *bp; /* temporary buffer pointer */

   /* get the buffer name to switch to */
   bp = getdefb();
   bp = getcbuf(TEXT24, bp ? bp->b_bname : mainbuf, TRUE);
   /*           "Use buffer" */
   if (!bp)
   return(ABORT);

   /* make it invisable if there is an argument */
   if (f == TRUE)
   bp->b_flag |= BFINVS;

   /* switch to it in any case */
   return(swbuffer(bp));
}
//-

/// nextbuffer
/* switch to the next buffer in the buffer list */
STD nextbuffer(int f, int n)
{
   register BUFFER *bp; /* current eligable buffer */
   register int status;

   /* make sure the arg is legit */
   if (f == FALSE)
   n = 1;
   if (n < 1)
   return(FALSE);

   /* cycle thru buffers until n runs out */
   while (n-- > 0) {
      bp = getdefb();
      if (bp == NULL)
      return(FALSE);
      status = swbuffer(bp);
      if (status != TRUE)
      return(status);
   }
   return(status);
}
//-

/// swbuffer()
/* make buffer BP current */
STD swbuffer(BUFFER *bp)
{
   register WINDOW *wp;
   SCREEN *scrp;     /* screen to fix pointers in */
   register int cmark;     /* current mark */

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

   if (--curbp->b_nwnd == 0) {
      /* Last use.      */
      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.     */
   if (curbp->b_active != TRUE) {
      /* buffer not active yet*/
      /* read it in and activate it */
      readin(curbp->b_fname, ((curbp->b_mode&MDVIEW) == 0));
      curbp->b_dotp = lforw(curbp->b_linep);
      curbp->b_doto = 0;
      curbp->b_active = TRUE;
   }
   curwp->w_bufp  = bp;
   curwp->w_linep = bp->b_linep;    /* For macros, ignored. */
   curwp->w_flag |= WFMODE|WFFORCE|WFHARD; /* Quite nasty.  */
   if (bp->b_nwnd++ == 0) {
      /* First use.     */
      curwp->w_dotp  = bp->b_dotp;
      curwp->w_doto  = bp->b_doto;
      for (cmark = 0; cmark < NMARKS; cmark++) {
         curwp->w_markp[cmark] = bp->b_markp[cmark];
         curwp->w_marko[cmark] = bp->b_marko[cmark];
      }
      curwp->w_fcol  = bp->b_fcol;
   }
   else {
      /* in all screens.... */
      scrp = first_screen;
      wp = scrp->s_first_window;
      while (wp != NULL) {
         if (wp!=curwp && wp->w_bufp==bp) {
            curwp->w_dotp  = wp->w_dotp;
            curwp->w_doto  = wp->w_doto;
            for (cmark = 0; cmark < NMARKS; cmark++) {
               curwp->w_markp[cmark] = wp->w_markp[cmark];
               curwp->w_marko[cmark] = wp->w_marko[cmark];
            }
            curwp->w_fcol  = wp->w_fcol;
            break;
         }
         /* next window */
         wp = wp->w_wndp;
      }

   }

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

   return(TRUE);
}
//-

/// killbuffer()
/*
** Dispose of a buffer, by name.
** Ask for the name. Look it up (don't get too
** upset if it isn't there at all!). Get quite upset
** if the buffer is being displayed. Clear the buffer (ask
** if the buffer has been changed). Then free the header
** line and the buffer header. Bound to "C-X K".
 ***********************************************************************/
STD killbuffer(f, n)
{
   register BUFFER *bp; /* ptr to buffer to dump */

   /* get the buffer name to kill */
   bp = getdefb();
   bp = getcbuf(TEXT26, bp ? bp->b_bname : mainbuf, TRUE);
   /*         "Kill buffer" */
   if (bp == NULL)
   return(ABORT);

   return(zotbuf(bp));
}
//-

/// zotbuf()
/* kill the buffer pointed to by bp */
STD zotbuf(register BUFFER *bp)
{
   register BUFFER *bp1;
   register BUFFER *bp2;
   register int   s;

   /* we can not kill a displayed buffer */
   if (bp->b_nwnd != 0) {
      mlwrite(TEXT28);
      /*       "Buffer is being displayed" */
      return(FALSE);
   }

   /* we can not kill an executing buffer */
   if (bp->b_exec != 0) {
      mlwrite(TEXT226);
      /*       "%%Can not delete an executing buffer" */
      return(FALSE);
   }

   if ((s=bclear(bp)) != TRUE)      /* Blow text away.   */
   return(s);
   free((char *) bp->b_linep);      /* Release header line. */
   bp1 = NULL;          /* Find the header.  */
   bp2 = bheadp;
   while (bp2 != bp) {
      bp1 = bp2;
      bp2 = bp2->b_bufp;
   }
   bp2 = bp2->b_bufp;         /* Next one in chain.   */
   if (bp1 == NULL)        /* Unlink it.     */
   bheadp = bp2;
   else
   bp1->b_bufp = bp2;
   free((char *) bp);         /* Release buffer block */
   return(TRUE);
}
//-

/// namebuffer()
/* Rename the current buffer  */
STD namebuffer(f,n)
{
   register BUFFER *bp; /* pointer to scan through all buffers */
   char bufn[NBUFN]; /* buffer to hold buffer name */

   /* prompt for and get the new buffer name */
   ask:  if (mlreply(TEXT29, bufn, NBUFN) != TRUE)
   /*        "Change buffer name to: " */
   return(FALSE);

   /* and check for duplicates */
   bp = bheadp;
   while (bp != NULL) {
      if (bp != curbp) {
         /* if the names the same */
         if (strcmp(bufn, bp->b_bname) == 0)
         goto ask;  /* try again */
      }
      bp = bp->b_bufp;  /* onward */
   }

   strcpy(curbp->b_bname, bufn); /* copy buffer name to structure */
   upmode();         /* make all mode lines replot */
   mlerase();
   return(TRUE);
}
//-

/// bfind()
/*
** Find a buffer, by name. Return a pointer
** to the BUFFER structure associated with it.
** If the buffer is not found
** and the "cflag" is TRUE, create it. The "bflag" is
** the settings for the flags in in buffer.
 ***********************************************************************/
BUFFER *STD bfind(register char *bname, int cflag, int bflag)
{
   register BUFFER *bp;
   register BUFFER *sb; /* buffer to insert after */
   register LINE  *lp;
   int cmark;     /* current mark */

   bp = bheadp;

   while (bp != NULL)
   {
      if (strcmp(bname, bp->b_bname) == 0)
      return(bp);
      bp = bp->b_bufp;
   }

   /* no such buffer exists, create it? */
   if (cflag != FALSE)
   {
      /* allocate the needed memory */
      if ((bp=(BUFFER *)malloc(sizeof(BUFFER))) == NULL)
         return(NULL);

      if ((lp=lalloc(0)) == NULL)
      {
         free((char *) bp);
         return(NULL);
      }

      /* find the place in the list to insert this buffer */
      if (bheadp == NULL || strcmp(bheadp->b_bname, bname) > 0)
      {
         /* insert at the beginning */
         bp->b_bufp = bheadp;
         bheadp = bp;
      }
      else
      {
         sb = bheadp;
         while (sb->b_bufp != NULL)
         {
            if (strcmp(sb->b_bufp->b_bname, bname) > 0)
               break;
            sb = sb->b_bufp;
         }

         /* and insert it */
         bp->b_bufp = sb->b_bufp;
         sb->b_bufp = bp;
      }

      /* and set up the other buffer fields */
      bp->b_topline = NULL;
      bp->b_botline = NULL;
      bp->b_active = TRUE;
      bp->b_dotp  = lp;
      bp->b_doto  = 0;

      for (cmark = 0; cmark < NMARKS; cmark++)
      {
         bp->b_markp[cmark] = NULL;
         bp->b_marko[cmark] = 0;
      }

      bp->b_fcol  = 0;
      bp->b_flag  = bflag;
      bp->b_mode  = gmode;
      bp->b_nwnd  = 0;
      bp->b_exec  = 0;
      bp->b_linep = lp;
      strcpy(bp->b_fname, "");
      strcpy(bp->b_bname, bname);
      lp->l_fp = lp;
      lp->l_bp = lp;
   }
   return(bp);
}
//-

/// bclear()
/*
** This routine blows away all of the text in a buffer. If the buffer is marked
** as changed then we ask if it is ok to blow it away; this is
** to save the user the grief of losing text. The window chain is nearly always
** wrong if this gets called; the caller must arrange for the updates
** that are required. Return TRUE if everything looks good.
 ******************************************************************************/
STD bclear(register BUFFER *bp)
{
   register LINE  *lp;
   register int   s;
   int cmark;     /* current mark */

   if ((bp->b_flag&BFINVS) == 0     /* Not scratch buffer.  */
   && (bp->b_flag&BFCHG) != 0    /* Something changed */
   && (s=mlyesno(TEXT32)) != TRUE)
   /*          "Discard changes" */
   return(s);
   bp->b_flag  &= ~BFCHG;        /* Not changed    */

   while ((lp=lforw(bp->b_linep)) != bp->b_linep)
   lfree(lp);

   bp->b_dotp  = bp->b_linep;    /* Fix "."     */
   bp->b_doto  = 0;
   for (cmark = 0; cmark < NMARKS; cmark++) {
      bp->b_markp[cmark] = NULL;  /* Invalidate "mark"    */
      bp->b_marko[cmark] = 0;
   }
   bp->b_fcol = 0;

   return(TRUE);
}
//-

/// unmark()
/*
** unmark the current buffers change flag
 ***********************************************************************/
STD unmark(f, n)
{
   // register WINDOW *wp;

   /* unmark the buffer */
   curbp->b_flag &= ~BFCHG;

   /* unmark all windows as well */
   upmode();

   return(TRUE);
}
//-

// internally used functions

/// getdefb
/* get the default buffer for a use or kill */
BUFFER *STD getdefb(void)
{
   BUFFER *bp; /* default buffer */

   /* Find the next buffer, which will be the default */
   bp = curbp->b_bufp;

   /* cycle through the buffers to find an eligable one */
   while (bp == NULL || bp->b_flag & BFINVS) {
      if (bp == NULL)
      bp = bheadp;
      else
      bp = bp->b_bufp;

      /* don't get caught in an infinite loop! */
      if (bp == curbp) {
         bp = NULL;
         break;
      }
   }
      return(bp);
}
//-



