mirror of
https://github.com/rfivet/uemacs.git
synced 2024-11-14 00:26:01 -05:00
590 lines
14 KiB
C
590 lines
14 KiB
C
/* buffer.c -- implements buffer.h */
|
|
#include "buffer.h"
|
|
|
|
/* buffer.c
|
|
*
|
|
* Buffer management.
|
|
* Some of the functions are internal,
|
|
* and some are actually attached to user
|
|
* keys. Like everyone else, they set hints
|
|
* for the display system
|
|
*
|
|
* modified by Petri Kutvonen
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "defines.h"
|
|
#include "estruct.h"
|
|
#include "file.h"
|
|
#include "input.h"
|
|
#include "mlout.h"
|
|
#include "window.h"
|
|
|
|
|
|
struct buffer *curbp ; /* Current buffer */
|
|
struct buffer *bheadp ; /* Head of list of buffers */
|
|
struct buffer *blistp ; /* Buffer for C-X C-B */
|
|
|
|
const char *modename[] = { /* name of modes */
|
|
"Wrap", "Cmode", "Spell", "Exact", "View", "Over",
|
|
"Magic",
|
|
#if CRYPT
|
|
"Crypt",
|
|
#else
|
|
"",
|
|
#endif
|
|
"Asave", "Utf-8", "Dos"
|
|
} ;
|
|
|
|
int gmode = 0 ; /* global editor mode */
|
|
|
|
static const char modecode[] = "WCSEVOMYAUD" ; /* letters to represent modes */
|
|
|
|
|
|
static int makelist( int iflag) ;
|
|
static int addline( char *text) ;
|
|
static void l_to_a( char *buf, int width, long num) ;
|
|
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
int usebuffer(int f, int n)
|
|
{
|
|
struct buffer *bp;
|
|
int s;
|
|
bname_t bufn ;
|
|
|
|
if ((s = mlreply("Use buffer: ", bufn, sizeof bufn)) != TRUE)
|
|
return s;
|
|
if ((bp = bfind(bufn, TRUE, 0)) == NULL)
|
|
return FALSE;
|
|
return swbuffer(bp);
|
|
}
|
|
|
|
/*
|
|
* switch to the next buffer in the buffer list
|
|
*
|
|
* int f, n; default flag, numeric argument
|
|
*/
|
|
int nextbuffer(int f, int n)
|
|
{
|
|
struct buffer *bp = NULL; /* eligable buffer to switch to */
|
|
struct buffer *bbp; /* eligable buffer to switch to */
|
|
|
|
/* make sure the arg is legit */
|
|
if (f == FALSE)
|
|
n = 1;
|
|
if (n < 1)
|
|
return FALSE;
|
|
|
|
bbp = curbp;
|
|
while (n-- > 0) {
|
|
/* advance to the next buffer */
|
|
bp = bbp->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 == bbp)
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
bbp = bp;
|
|
}
|
|
|
|
return swbuffer(bp);
|
|
}
|
|
|
|
/*
|
|
* make buffer BP current
|
|
*/
|
|
int swbuffer(struct buffer *bp)
|
|
{
|
|
struct window *wp;
|
|
|
|
if (--curbp->b_nwnd == 0) { /* Last use. */
|
|
curbp->b_dotp = curwp->w_dotp;
|
|
curbp->b_doto = curwp->w_doto;
|
|
curbp->b_markp = curwp->w_markp;
|
|
curbp->b_marko = curwp->w_marko;
|
|
}
|
|
curbp = bp; /* Switch. */
|
|
if (curbp->b_active != TRUE) { /* buffer not active yet */
|
|
/* read it in and activate it */
|
|
readin(curbp->b_fname, TRUE);
|
|
curbp->b_dotp = lforw(curbp->b_linep);
|
|
curbp->b_doto = 0;
|
|
curbp->b_active = TRUE;
|
|
curbp->b_mode |= gmode; /* P.K. */
|
|
}
|
|
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;
|
|
curwp->w_markp = bp->b_markp;
|
|
curwp->w_marko = bp->b_marko;
|
|
cknewwindow();
|
|
return TRUE;
|
|
}
|
|
wp = wheadp; /* Look for old. */
|
|
while (wp != NULL) {
|
|
if (wp != curwp && wp->w_bufp == bp) {
|
|
curwp->w_dotp = wp->w_dotp;
|
|
curwp->w_doto = wp->w_doto;
|
|
curwp->w_markp = wp->w_markp;
|
|
curwp->w_marko = wp->w_marko;
|
|
break;
|
|
}
|
|
wp = wp->w_wndp;
|
|
}
|
|
cknewwindow();
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* 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".
|
|
*/
|
|
int killbuffer(int f, int n)
|
|
{
|
|
struct buffer *bp;
|
|
int s;
|
|
bname_t bufn ;
|
|
|
|
if ((s = mlreply("Kill buffer: ", bufn, sizeof bufn)) != TRUE)
|
|
return s;
|
|
if ((bp = bfind(bufn, FALSE, 0)) == NULL) /* Easy if unknown. */
|
|
return TRUE;
|
|
if (bp->b_flag & BFINVS) /* Deal with special buffers */
|
|
return TRUE; /* by doing nothing. */
|
|
return zotbuf(bp);
|
|
}
|
|
|
|
/*
|
|
* kill the buffer pointed to by bp
|
|
*/
|
|
int zotbuf(struct buffer *bp)
|
|
{
|
|
struct buffer *bp1;
|
|
struct buffer *bp2;
|
|
int s;
|
|
|
|
if (bp->b_nwnd != 0) { /* Error if on screen. */
|
|
mloutstr("Buffer is being displayed");
|
|
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;
|
|
}
|
|
|
|
/*
|
|
* Rename the current buffer
|
|
*
|
|
* int f, n; default Flag & Numeric arg
|
|
*/
|
|
int namebuffer(int f, int n)
|
|
{
|
|
struct buffer *bp; /* pointer to scan through all buffers */
|
|
bname_t bufn ; /* buffer to hold buffer name */
|
|
|
|
/* prompt for and get the new buffer name */
|
|
ask:if (mlreply("Change buffer name to: ", bufn, sizeof bufn) !=
|
|
TRUE)
|
|
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 */
|
|
curwp->w_flag |= WFMODE; /* make mode line replot */
|
|
mloutstr( "") ; /* erase message line */
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* List all of the active buffers. First update the special
|
|
* buffer that holds the list. Next make sure at least 1
|
|
* window is displaying the buffer list, splitting the screen
|
|
* if this is what it takes. Lastly, repaint all of the
|
|
* windows that are displaying the list. Bound to "C-X C-B".
|
|
*
|
|
* A numeric argument forces it to list invisible buffers as
|
|
* well.
|
|
*/
|
|
int listbuffers(int f, int n)
|
|
{
|
|
struct window *wp;
|
|
struct buffer *bp;
|
|
int s;
|
|
|
|
if ((s = makelist(f)) != TRUE)
|
|
return s;
|
|
if (blistp->b_nwnd == 0) { /* Not on screen yet. */
|
|
if ((wp = wpopup()) == NULL)
|
|
return FALSE;
|
|
bp = wp->w_bufp;
|
|
if (--bp->b_nwnd == 0) {
|
|
bp->b_dotp = wp->w_dotp;
|
|
bp->b_doto = wp->w_doto;
|
|
bp->b_markp = wp->w_markp;
|
|
bp->b_marko = wp->w_marko;
|
|
}
|
|
wp->w_bufp = blistp;
|
|
++blistp->b_nwnd;
|
|
}
|
|
wp = wheadp;
|
|
while (wp != NULL) {
|
|
if (wp->w_bufp == blistp) {
|
|
wp->w_linep = lforw(blistp->b_linep);
|
|
wp->w_dotp = lforw(blistp->b_linep);
|
|
wp->w_doto = 0;
|
|
wp->w_markp = NULL;
|
|
wp->w_marko = 0;
|
|
wp->w_flag |= WFMODE | WFHARD;
|
|
}
|
|
wp = wp->w_wndp;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* This routine rebuilds the
|
|
* text in the special secret buffer
|
|
* that holds the buffer list. It is called
|
|
* by the list buffers command. Return TRUE
|
|
* if everything works. Return FALSE if there
|
|
* is an error (if there is no memory). Iflag
|
|
* indicates wether to list hidden buffers.
|
|
*
|
|
* int iflag; list hidden buffer flag
|
|
*/
|
|
#define MAXLINE MAXCOL
|
|
static int makelist( int iflag)
|
|
{
|
|
char *cp1;
|
|
char *cp2;
|
|
int c;
|
|
struct buffer *bp;
|
|
struct line *lp;
|
|
int s;
|
|
int i;
|
|
long nbytes; /* # of bytes in current buffer */
|
|
long nlines ; /* # of lines in current buffer */
|
|
char b[ 8 + 1] ;
|
|
char line[MAXLINE];
|
|
|
|
blistp->b_flag &= ~BFCHG; /* Don't complain! */
|
|
if ((s = bclear(blistp)) != TRUE) /* Blow old text away */
|
|
return s;
|
|
strcpy(blistp->b_fname, "");
|
|
if( addline("ACT MODES Size Buffer File") == FALSE
|
|
|| addline("‾‾‾ ‾‾‾‾‾ ‾‾‾‾ ‾‾‾‾‾‾ ‾‾‾‾") == FALSE)
|
|
return FALSE;
|
|
bp = bheadp; /* For all buffers */
|
|
|
|
/* build line to report global mode settings */
|
|
cp1 = &line[0];
|
|
*cp1++ = ' ';
|
|
*cp1++ = ' ';
|
|
*cp1++ = ' ';
|
|
*cp1++ = ' ';
|
|
|
|
/* output the mode codes */
|
|
for (i = 0; i < NUMMODES; i++)
|
|
if (gmode & (1 << i))
|
|
*cp1++ = modecode[i];
|
|
else
|
|
*cp1++ = '.';
|
|
strcpy(cp1, " Global Modes");
|
|
if (addline(line) == FALSE)
|
|
return FALSE;
|
|
|
|
/* output the list of buffers */
|
|
while (bp != NULL) {
|
|
/* skip invisable buffers if iflag is false */
|
|
if (((bp->b_flag & BFINVS) != 0) && (iflag != TRUE)) {
|
|
bp = bp->b_bufp;
|
|
continue;
|
|
}
|
|
cp1 = &line[0]; /* Start at left edge */
|
|
|
|
/* output status of ACTIVE flag (has the file been read in? */
|
|
if (bp->b_active == TRUE) /* "@" if activated */
|
|
*cp1++ = '@';
|
|
else
|
|
*cp1++ = ' ';
|
|
|
|
/* output status of changed flag */
|
|
if ((bp->b_flag & BFCHG) != 0) /* "*" if changed */
|
|
*cp1++ = '*';
|
|
else
|
|
*cp1++ = ' ';
|
|
|
|
/* report if the file is truncated */
|
|
if ((bp->b_flag & BFTRUNC) != 0)
|
|
*cp1++ = '#';
|
|
else
|
|
*cp1++ = ' ';
|
|
|
|
*cp1++ = ' '; /* space */
|
|
|
|
/* output the mode codes */
|
|
for (i = 0; i < NUMMODES; i++) {
|
|
if (bp->b_mode & (1 << i))
|
|
*cp1++ = modecode[i];
|
|
else
|
|
*cp1++ = '.';
|
|
}
|
|
|
|
/* No gap as buffer size if left padded with space */
|
|
|
|
/* Buffer size */
|
|
nbytes = 0L; /* Count bytes in buf. */
|
|
nlines = 0 ;
|
|
lp = lforw(bp->b_linep);
|
|
while (lp != bp->b_linep) {
|
|
nbytes += (long) llength(lp) + 1L;
|
|
nlines += 1 ;
|
|
lp = lforw(lp);
|
|
}
|
|
|
|
if( bp->b_mode & MDDOS)
|
|
nbytes += nlines ;
|
|
|
|
l_to_a( b, sizeof b, nbytes) ; /* 8 digits string buffer size. */
|
|
cp2 = &b[0];
|
|
while ((c = *cp2++) != 0)
|
|
*cp1++ = c;
|
|
|
|
*cp1++ = ' '; /* Gap. */
|
|
cp2 = &bp->b_bname[0]; /* Buffer name */
|
|
while ((c = *cp2++) != 0)
|
|
*cp1++ = c;
|
|
cp2 = &bp->b_fname[0]; /* File name */
|
|
if (*cp2 != 0) {
|
|
while( cp1 < &line[ 3 + 1 + NUMMODES + 8 + 1 + (NBUFN-1) + 1])
|
|
*cp1++ = ' ';
|
|
while ((c = *cp2++) != 0) {
|
|
if (cp1 < &line[MAXLINE - 1])
|
|
*cp1++ = c;
|
|
}
|
|
}
|
|
*cp1 = 0; /* Add to the buffer. */
|
|
if (addline(line) == FALSE)
|
|
return FALSE;
|
|
bp = bp->b_bufp;
|
|
}
|
|
return TRUE; /* All done */
|
|
}
|
|
|
|
static void l_to_a(char *buf, int width, long num)
|
|
{
|
|
buf[ --width] = 0 ; /* End of string. */
|
|
while (num >= 10) { /* Conditional digits. */
|
|
buf[--width] = (int) (num % 10L) + '0';
|
|
num /= 10L;
|
|
}
|
|
buf[--width] = (int) num + '0'; /* Always 1 digit. */
|
|
while( width > 0) /* Pad with blanks. */
|
|
buf[--width] = ' ';
|
|
}
|
|
|
|
/*
|
|
* The argument "text" points to
|
|
* a string. Append this line to the
|
|
* buffer list buffer. Handcraft the EOL
|
|
* on the end. Return TRUE if it worked and
|
|
* FALSE if you ran out of room.
|
|
*/
|
|
static int addline( char *text)
|
|
{
|
|
struct line *lp;
|
|
int i;
|
|
int ntext;
|
|
|
|
ntext = strlen(text);
|
|
if ((lp = lalloc(ntext)) == NULL)
|
|
return FALSE;
|
|
for (i = 0; i < ntext; ++i)
|
|
lputc(lp, i, text[i]);
|
|
blistp->b_linep->l_bp->l_fp = lp; /* Hook onto the end */
|
|
lp->l_bp = blistp->b_linep->l_bp;
|
|
blistp->b_linep->l_bp = lp;
|
|
lp->l_fp = blistp->b_linep;
|
|
if (blistp->b_dotp == blistp->b_linep) /* If "." is at the end */
|
|
blistp->b_dotp = lp; /* move it to new line */
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
int anycb(void)
|
|
{
|
|
struct 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;
|
|
}
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
struct buffer *bfind( const char *bname, int cflag, int bflag)
|
|
{
|
|
struct buffer *bp;
|
|
struct buffer *sb; /* buffer to insert after */
|
|
struct line *lp;
|
|
|
|
bp = bheadp;
|
|
while (bp != NULL) {
|
|
if (strcmp(bname, bp->b_bname) == 0)
|
|
return bp;
|
|
bp = bp->b_bufp;
|
|
}
|
|
if (cflag != FALSE) {
|
|
if ((bp = (struct buffer *)malloc(sizeof(struct 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_active = TRUE;
|
|
bp->b_dotp = lp;
|
|
bp->b_doto = 0;
|
|
bp->b_markp = NULL;
|
|
bp->b_marko = 0;
|
|
bp->b_flag = bflag;
|
|
bp->b_mode = gmode;
|
|
bp->b_nwnd = 0;
|
|
bp->b_linep = lp;
|
|
bp->b_fname[ 0] = '\0' ;
|
|
strncpy( bp->b_bname, bname, sizeof( bname_t) - 1) ;
|
|
bp->b_bname[ sizeof( bname_t) - 1] = '\0' ;
|
|
#if CRYPT
|
|
bp->b_key[0] = 0;
|
|
#endif
|
|
lp->l_fp = lp;
|
|
lp->l_bp = lp;
|
|
}
|
|
return bp;
|
|
}
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
int bclear(struct buffer *bp)
|
|
{
|
|
struct line *lp;
|
|
int s;
|
|
|
|
if ((bp->b_flag & BFINVS) == 0 /* Not scratch buffer. */
|
|
&& (bp->b_flag & BFCHG) != 0 /* Something changed */
|
|
&& (s = mlyesno("Discard changes")) != TRUE)
|
|
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;
|
|
bp->b_markp = NULL; /* Invalidate "mark" */
|
|
bp->b_marko = 0;
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* unmark the current buffers change flag
|
|
*
|
|
* int f, n; unused command arguments
|
|
*/
|
|
int unmark(int f, int n)
|
|
{
|
|
curbp->b_flag &= ~BFCHG;
|
|
curwp->w_flag |= WFMODE;
|
|
return TRUE;
|
|
}
|