Changeset View
Changeset View
Standalone View
Standalone View
contrib/mg/yank.c
- This file was added.
| /* $OpenBSD: yank.c,v 1.15 2021/03/01 10:51:14 lum Exp $ */ | |||||
| /* This file is in the public domain. */ | |||||
| /* | |||||
| * kill ring functions | |||||
| */ | |||||
| #include <sys/queue.h> | |||||
| #include <signal.h> | |||||
| #include <stdio.h> | |||||
| #include <stdlib.h> | |||||
| #include <string.h> | |||||
| #include "def.h" | |||||
| #define KBLOCK 8192 /* Kill grow. */ | |||||
| static char *kbufp = NULL; /* Kill buffer data. */ | |||||
| static RSIZE kused = 0; /* # of bytes used in KB. */ | |||||
| static RSIZE ksize = 0; /* # of bytes allocated in KB. */ | |||||
| static RSIZE kstart = 0; /* # of first used byte in KB. */ | |||||
| static int kgrow(int); | |||||
| /* | |||||
| * Delete all of the text saved in the kill buffer. Called by commands when | |||||
| * a new kill context is created. The kill buffer array is released, just in | |||||
| * case the buffer has grown to an immense size. No errors. | |||||
| */ | |||||
| void | |||||
| kdelete(void) | |||||
| { | |||||
| if (kbufp != NULL) { | |||||
| free(kbufp); | |||||
| kbufp = NULL; | |||||
| kstart = kused = ksize = 0; | |||||
| } | |||||
| } | |||||
| /* | |||||
| * Insert a character to the kill buffer, enlarging the buffer if there | |||||
| * isn't any room. Always grow the buffer in chunks, on the assumption | |||||
| * that if you put something in the kill buffer you are going to put more | |||||
| * stuff there too later. Return TRUE if all is well, and FALSE on errors. | |||||
| * Print a message on errors. Dir says whether to put it at back or front. | |||||
| * This call is ignored if KNONE is set. | |||||
| */ | |||||
| int | |||||
| kinsert(int c, int dir) | |||||
| { | |||||
| if (dir == KNONE) | |||||
| return (TRUE); | |||||
| if (kused == ksize && dir == KFORW && kgrow(dir) == FALSE) | |||||
| return (FALSE); | |||||
| if (kstart == 0 && dir == KBACK && kgrow(dir) == FALSE) | |||||
| return (FALSE); | |||||
| if (dir == KFORW) | |||||
| kbufp[kused++] = c; | |||||
| else if (dir == KBACK) | |||||
| kbufp[--kstart] = c; | |||||
| else | |||||
| panic("broken kinsert call"); /* Oh shit! */ | |||||
| return (TRUE); | |||||
| } | |||||
| /* | |||||
| * kgrow - just get more kill buffer for the callee. If dir = KBACK | |||||
| * we are trying to get space at the beginning of the kill buffer. | |||||
| */ | |||||
| static int | |||||
| kgrow(int dir) | |||||
| { | |||||
| int nstart; | |||||
| char *nbufp; | |||||
| if ((unsigned)(ksize + KBLOCK) <= (unsigned)ksize) { | |||||
| /* probably 16 bit unsigned */ | |||||
| dobeep(); | |||||
| ewprintf("Kill buffer size at maximum"); | |||||
| return (FALSE); | |||||
| } | |||||
| if ((nbufp = malloc((unsigned)(ksize + KBLOCK))) == NULL) { | |||||
| dobeep(); | |||||
| ewprintf("Can't get %ld bytes", (long)(ksize + KBLOCK)); | |||||
| return (FALSE); | |||||
| } | |||||
| nstart = (dir == KBACK) ? (kstart + KBLOCK) : (KBLOCK / 4); | |||||
| bcopy(&(kbufp[kstart]), &(nbufp[nstart]), (int)(kused - kstart)); | |||||
| free(kbufp); | |||||
| kbufp = nbufp; | |||||
| ksize += KBLOCK; | |||||
| kused = kused - kstart + nstart; | |||||
| kstart = nstart; | |||||
| return (TRUE); | |||||
| } | |||||
| /* | |||||
| * This function gets characters from the kill buffer. If the character | |||||
| * index "n" is off the end, it returns "-1". This lets the caller just | |||||
| * scan along until it gets a "-1" back. | |||||
| */ | |||||
| int | |||||
| kremove(int n) | |||||
| { | |||||
| if (n < 0 || n + kstart >= kused) | |||||
| return (-1); | |||||
| return (CHARMASK(kbufp[n + kstart])); | |||||
| } | |||||
| /* | |||||
| * Copy a string into the kill buffer. kflag gives direction. | |||||
| * if KNONE, do nothing. | |||||
| */ | |||||
| int | |||||
| kchunk(char *cp1, RSIZE chunk, int kflag) | |||||
| { | |||||
| /* | |||||
| * HACK - doesn't matter, and fixes back-over-nl bug for empty | |||||
| * kill buffers. | |||||
| */ | |||||
| if (kused == kstart) | |||||
| kflag = KFORW; | |||||
| if (kflag & KFORW) { | |||||
| while (ksize - kused < chunk) | |||||
| if (kgrow(kflag) == FALSE) | |||||
| return (FALSE); | |||||
| bcopy(cp1, &(kbufp[kused]), (int)chunk); | |||||
| kused += chunk; | |||||
| } else if (kflag & KBACK) { | |||||
| while (kstart < chunk) | |||||
| if (kgrow(kflag) == FALSE) | |||||
| return (FALSE); | |||||
| bcopy(cp1, &(kbufp[kstart - chunk]), (int)chunk); | |||||
| kstart -= chunk; | |||||
| } | |||||
| return (TRUE); | |||||
| } | |||||
| /* | |||||
| * Kill line. If called without an argument, it kills from dot to the end | |||||
| * of the line, unless it is at the end of the line, when it kills the | |||||
| * newline. If called with an argument of 0, it kills from the start of the | |||||
| * line to dot. If called with a positive argument, it kills from dot | |||||
| * forward over that number of newlines. If called with a negative argument | |||||
| * it kills any text before dot on the current line, then it kills back | |||||
| * abs(arg) lines. | |||||
| */ | |||||
| /* ARGSUSED */ | |||||
| int | |||||
| killline(int f, int n) | |||||
| { | |||||
| struct line *nextp; | |||||
| RSIZE chunk; | |||||
| int i, c; | |||||
| /* clear kill buffer if last wasn't a kill */ | |||||
| if ((lastflag & CFKILL) == 0) | |||||
| kdelete(); | |||||
| thisflag |= CFKILL; | |||||
| if (!(f & FFARG)) { | |||||
| for (i = curwp->w_doto; i < llength(curwp->w_dotp); ++i) | |||||
| if ((c = lgetc(curwp->w_dotp, i)) != ' ' && c != '\t') | |||||
| break; | |||||
| if (i == llength(curwp->w_dotp)) | |||||
| chunk = llength(curwp->w_dotp) - curwp->w_doto + 1; | |||||
| else { | |||||
| chunk = llength(curwp->w_dotp) - curwp->w_doto; | |||||
| if (chunk == 0) | |||||
| chunk = 1; | |||||
| } | |||||
| } else if (n > 0) { | |||||
| chunk = llength(curwp->w_dotp) - curwp->w_doto; | |||||
| nextp = lforw(curwp->w_dotp); | |||||
| if (nextp != curbp->b_headp) | |||||
| chunk++; /* newline */ | |||||
| if (nextp == curbp->b_headp) | |||||
| goto done; /* EOL */ | |||||
| i = n; | |||||
| while (--i) { | |||||
| chunk += llength(nextp); | |||||
| nextp = lforw(nextp); | |||||
| if (nextp != curbp->b_headp) | |||||
| chunk++; /* newline */ | |||||
| if (nextp == curbp->b_headp) | |||||
| break; /* EOL */ | |||||
| } | |||||
| } else { | |||||
| /* n <= 0 */ | |||||
| chunk = curwp->w_doto; | |||||
| curwp->w_doto = 0; | |||||
| i = n; | |||||
| while (i++) { | |||||
| if (lforw(curwp->w_dotp)) | |||||
| chunk++; | |||||
| curwp->w_dotp = lback(curwp->w_dotp); | |||||
| curwp->w_rflag |= WFMOVE; | |||||
| chunk += llength(curwp->w_dotp); | |||||
| } | |||||
| } | |||||
| /* | |||||
| * KFORW here is a bug. Should be KBACK/KFORW, but we need to | |||||
| * rewrite the ldelete code (later)? | |||||
| */ | |||||
| done: | |||||
| if (chunk) | |||||
| return (ldelete(chunk, KFORW)); | |||||
| return (TRUE); | |||||
| } | |||||
| /* | |||||
| * Yank text back from the kill buffer. This is really easy. All of the work | |||||
| * is done by the standard insert routines. All you do is run the loop, and | |||||
| * check for errors. The blank lines are inserted with a call to "newline" | |||||
| * instead of a call to "lnewline" so that the magic stuff that happens when | |||||
| * you type a carriage return also happens when a carriage return is yanked | |||||
| * back from the kill buffer. An attempt has been made to fix the cosmetic | |||||
| * bug associated with a yank when dot is on the top line of the window | |||||
| * (nothing moves, because all of the new text landed off screen). | |||||
| */ | |||||
| /* ARGSUSED */ | |||||
| int | |||||
| yank(int f, int n) | |||||
| { | |||||
| struct line *lp; | |||||
| int c, i, nline; | |||||
| if (n < 0) | |||||
| return (FALSE); | |||||
| /* newline counting */ | |||||
| nline = 0; | |||||
| undo_boundary_enable(FFRAND, 0); | |||||
| while (n--) { | |||||
| /* mark around last yank */ | |||||
| isetmark(); | |||||
| i = 0; | |||||
| while ((c = kremove(i)) >= 0) { | |||||
| if (c == *curbp->b_nlchr) { | |||||
| if (enewline(FFRAND, 1) == FALSE) | |||||
| return (FALSE); | |||||
| ++nline; | |||||
| } else { | |||||
| if (linsert(1, c) == FALSE) | |||||
| return (FALSE); | |||||
| } | |||||
| ++i; | |||||
| } | |||||
| } | |||||
| /* cosmetic adjustment */ | |||||
| lp = curwp->w_linep; | |||||
| /* if offscreen insert */ | |||||
| if (curwp->w_dotp == lp) { | |||||
| while (nline-- && lback(lp) != curbp->b_headp) | |||||
| lp = lback(lp); | |||||
| /* adjust framing */ | |||||
| curwp->w_linep = lp; | |||||
| curwp->w_rflag |= WFFULL; | |||||
| } | |||||
| undo_boundary_enable(FFRAND, 1); | |||||
| return (TRUE); | |||||
| } | |||||