/* 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 "utf8.h" #include "util.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", "Exact", "View", "Over", "Magic", "Asave", "Utf-8", "Dos" } ; int gmode = 0 ; /* global editor mode */ 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 status ; char *bufn ; /* Get buffer name */ status = newmlarg( &bufn, "Use buffer: ", sizeof( bname_t)) ; if( status != TRUE) return status ; /* Find buffer in list */ bp = bfind( bufn, TRUE, 0) ; free( bufn) ; if( bp == NULL) return FALSE ; /* Switch to buffer */ 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 status ; char *bufn ; /* Get buffer name */ status = newmlarg( &bufn, "Kill buffer: ", sizeof( bname_t)) ; if( status != TRUE) return status ; /* Find buffer in list */ bp = bfind( bufn, FALSE, 0) ; free( bufn) ; if( bp == 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 */ int status ; char *bufn ; /* buffer to hold buffer name */ /* prompt for and get the new buffer name */ ask: status = newmlarg( &bufn, "Change buffer name to: ", sizeof( bname_t)) ; if( status != TRUE) return status ; /* and check for duplicates */ bp = bheadp ; while( bp != NULL) { if( bp != curbp) { /* retry if the names are the same */ if( strcmp( bufn, bp->b_bname) == 0) { free( bufn) ; goto ask ; /* try again */ } } bp = bp->b_bufp ; /* onward */ } /* copy buffer name to structure */ mystrscpy( curbp->b_bname, bufn, sizeof( bname_t)) ; free( bufn) ; 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; int s; if ((s = makelist(f)) != TRUE) return s; if (blistp->b_nwnd == 0) { /* Not on screen yet. */ struct buffer *bp ; 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 */ /* Layout: "ACT MODES Size Buffer File" AAA MMMMMMMMMSSSSSSSSSS BBBBBBBBBBBBBBB FFF... FNAMSTART ---------------------------------------^ */ #define FNAMSTART (3 + 1 + NUMMODES + 10 + 1 + (sizeof( bname_t) - 1) + 1) static void do_layout( char *line, int mode) { int i ; /* build line to report global mode settings */ strcpy( line, " WCEVOMAUD Global Modes") ; /* output the mode codes */ for( i = 0 ; i < NUMMODES ; i++) if( 0 == (mode & (1 << i))) line[ 4 + i] = '.' ; } static unsigned int utf8_disp_len( const char *s) { unsigned int len = 0 ; while( *s) { unicode_t c ; s += utf8_to_unicode( s, 0, 4, &c) ; len += utf8_width( c) ; } return len ; } static int makelist( int iflag) { struct buffer *bp; int s; char line[ FNAMSTART + sizeof( fname_t)] ; blistp->b_flag &= ~BFCHG; /* Don't complain! Mute bclear() */ if ((s = bclear(blistp)) != TRUE) /* Blow old text away */ return s; blistp->b_fname[ 0] = 0 ; /* in case of user override */ if( addline("ACT MODES Size Buffer File") == FALSE || addline("‾‾‾ ‾‾‾‾‾ ‾‾‾‾ ‾‾‾‾‾‾ ‾‾‾‾") == FALSE) return FALSE; /* report global mode settings */ do_layout( line, gmode) ; if( addline( line) == FALSE) return FALSE ; /* output the list of buffers */ for( bp = bheadp ; bp != NULL ; bp = bp->b_bufp) { /* For all buffers */ char *cp1, *cp2 ; int c ; struct line *lp ; long nbytes ; /* # of bytes in current buffer */ long nlines ; /* # of lines in current buffer */ /* skip invisible buffers if iflag is false */ if (((bp->b_flag & BFINVS) != 0) && (iflag != TRUE)) continue; do_layout( line, bp->b_mode) ; cp1 = line ; /* Start at left edge */ /* output status of ACTIVE flag ('@' when the file has been read in) */ *cp1++ = (bp->b_active == TRUE) ? '@' : ' ' ; /* report if the file is truncated */ *cp1++ = ((bp->b_flag & BFTRUNC) != 0) ? '#' : ' ' ; /* output status of changed flag ('*' when the buffer is changed) */ *cp1 = ((bp->b_flag & BFCHG) != 0) ? '*' : ' ' ; /* Buffer size */ nbytes = 0L; /* Count bytes in buf. */ nlines = 0 ; for( lp = lforw( bp->b_linep) ; lp != bp->b_linep ; lp = lforw( lp)) { nbytes += (long) llength(lp) + 1L; nlines += 1 ; } if( bp->b_mode & MDDOS) nbytes += nlines ; l_to_a( &line[ 13], 10 + 1, nbytes) ; /* "%10d" formatted numbers */ cp1 = &line[ 23] ; *cp1++ = ' ' ; /* Display buffer name */ cp2 = &bp->b_bname[ 0] ; while ((c = *cp2++) != 0) *cp1++ = c; /* Pad with spaces to max buffer name length */ int len = sizeof bp->b_bname ; len -= utf8_disp_len( bp->b_bname) ; while( len--) *cp1++ = ' ' ; /* Display filename if any */ if( bp->b_fname[ 0] != 0) mystrscpy( cp1, bp->b_fname, &line[ sizeof line] - cp1) ; else *cp1 = 0 ; /* Terminate string */ if( addline( line) == FALSE) /* Add to the buffer. */ return FALSE ; } 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 line *lp; for( bp = bheadp ; bp != NULL ; bp = bp->b_bufp) if( strcmp( bname, bp->b_bname) == 0) return bp ; 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 { struct buffer *sb; /* buffer to insert after */ for( sb = bheadp ; sb->b_bufp != NULL ; sb = sb->b_bufp) if( strcmp( sb->b_bufp->b_bname, bname) > 0) break ; /* 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' ; mystrscpy( bp->b_bname, bname, sizeof( bname_t)) ; 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; }