yank.c revision 1.4
1/* $OpenBSD: yank.c,v 1.4 2006/07/25 08:22:32 kjell Exp $ */ 2 3/* This file is in the public domain. */ 4 5/* 6 * kill ring functions 7 */ 8 9#include "def.h" 10 11#include <string.h> 12 13#ifndef KBLOCK 14#define KBLOCK 256 /* Kill buffer block size. */ 15#endif 16 17static char *kbufp = NULL; /* Kill buffer data. */ 18static RSIZE kused = 0; /* # of bytes used in KB. */ 19static RSIZE ksize = 0; /* # of bytes allocated in KB. */ 20static RSIZE kstart = 0; /* # of first used byte in KB. */ 21 22static int kgrow(int); 23 24/* 25 * Delete all of the text saved in the kill buffer. Called by commands when 26 * a new kill context is created. The kill buffer array is released, just in 27 * case the buffer has grown to an immense size. No errors. 28 */ 29void 30kdelete(void) 31{ 32 if (kbufp != NULL) { 33 free(kbufp); 34 kbufp = NULL; 35 kstart = kused = ksize = 0; 36 } 37} 38 39/* 40 * Insert a character to the kill buffer, enlarging the buffer if there 41 * isn't any room. Always grow the buffer in chunks, on the assumption 42 * that if you put something in the kill buffer you are going to put more 43 * stuff there too later. Return TRUE if all is well, and FALSE on errors. 44 * Print a message on errors. Dir says whether to put it at back or front. 45 * This call is ignored if KNONE is set. 46 */ 47int 48kinsert(int c, int dir) 49{ 50 if (dir == KNONE) 51 return (TRUE); 52 if (kused == ksize && dir == KFORW && kgrow(dir) == FALSE) 53 return (FALSE); 54 if (kstart == 0 && dir == KBACK && kgrow(dir) == FALSE) 55 return (FALSE); 56 if (dir == KFORW) 57 kbufp[kused++] = c; 58 else if (dir == KBACK) 59 kbufp[--kstart] = c; 60 else 61 panic("broken kinsert call"); /* Oh shit! */ 62 return (TRUE); 63} 64 65/* 66 * kgrow - just get more kill buffer for the callee. If dir = KBACK 67 * we are trying to get space at the beginning of the kill buffer. 68 */ 69static int 70kgrow(int dir) 71{ 72 int nstart; 73 char *nbufp; 74 75 if ((unsigned)(ksize + KBLOCK) <= (unsigned)ksize) { 76 /* probably 16 bit unsigned */ 77 ewprintf("Kill buffer size at maximum"); 78 return (FALSE); 79 } 80 if ((nbufp = malloc((unsigned)(ksize + KBLOCK))) == NULL) { 81 ewprintf("Can't get %ld bytes", (long)(ksize + KBLOCK)); 82 return (FALSE); 83 } 84 nstart = (dir == KBACK) ? (kstart + KBLOCK) : (KBLOCK / 4); 85 bcopy(&(kbufp[kstart]), &(nbufp[nstart]), (int)(kused - kstart)); 86 if (kbufp != NULL) 87 free(kbufp); 88 kbufp = nbufp; 89 ksize += KBLOCK; 90 kused = kused - kstart + nstart; 91 kstart = nstart; 92 return (TRUE); 93} 94 95/* 96 * This function gets characters from the kill buffer. If the character 97 * index "n" is off the end, it returns "-1". This lets the caller just 98 * scan along until it gets a "-1" back. 99 */ 100int 101kremove(int n) 102{ 103 if (n < 0 || n + kstart >= kused) 104 return (-1); 105 return (CHARMASK(kbufp[n + kstart])); 106} 107 108/* 109 * Copy a string into the kill buffer. kflag gives direction. 110 * if KNONE, do nothing. 111 */ 112int 113kchunk(char *cp1, RSIZE chunk, int kflag) 114{ 115 /* 116 * HACK - doesn't matter, and fixes back-over-nl bug for empty 117 * kill buffers. 118 */ 119 if (kused == kstart) 120 kflag = KFORW; 121 122 if (kflag == KFORW) { 123 while (ksize - kused < chunk) 124 if (kgrow(kflag) == FALSE) 125 return (FALSE); 126 bcopy(cp1, &(kbufp[kused]), (int)chunk); 127 kused += chunk; 128 } else if (kflag == KBACK) { 129 while (kstart < chunk) 130 if (kgrow(kflag) == FALSE) 131 return (FALSE); 132 bcopy(cp1, &(kbufp[kstart - chunk]), (int)chunk); 133 kstart -= chunk; 134 } else if (kflag != KNONE) 135 panic("broken ldelete call"); 136 137 return (TRUE); 138} 139 140/* 141 * Kill line. If called without an argument, it kills from dot to the end 142 * of the line, unless it is at the end of the line, when it kills the 143 * newline. If called with an argument of 0, it kills from the start of the 144 * line to dot. If called with a positive argument, it kills from dot 145 * forward over that number of newlines. If called with a negative argument 146 * it kills any text before dot on the current line, then it kills back 147 * abs(arg) lines. 148 */ 149/* ARGSUSED */ 150int 151killline(int f, int n) 152{ 153 struct line *nextp; 154 RSIZE chunk; 155 int i, c; 156 157 /* clear kill buffer if last wasn't a kill */ 158 if ((lastflag & CFKILL) == 0) 159 kdelete(); 160 thisflag |= CFKILL; 161 if (!(f & FFARG)) { 162 for (i = curwp->w_doto; i < llength(curwp->w_dotp); ++i) 163 if ((c = lgetc(curwp->w_dotp, i)) != ' ' && c != '\t') 164 break; 165 if (i == llength(curwp->w_dotp)) 166 chunk = llength(curwp->w_dotp) - curwp->w_doto + 1; 167 else { 168 chunk = llength(curwp->w_dotp) - curwp->w_doto; 169 if (chunk == 0) 170 chunk = 1; 171 } 172 } else if (n > 0) { 173 chunk = llength(curwp->w_dotp) - curwp->w_doto + 1; 174 nextp = lforw(curwp->w_dotp); 175 i = n; 176 while (--i) { 177 if (nextp == curbp->b_headp) 178 break; 179 chunk += llength(nextp) + 1; 180 nextp = lforw(nextp); 181 } 182 } else { 183 /* n <= 0 */ 184 chunk = curwp->w_doto; 185 curwp->w_doto = 0; 186 i = n; 187 while (i++) { 188 if (lback(curwp->w_dotp) == curbp->b_headp) 189 break; 190 curwp->w_dotp = lback(curwp->w_dotp); 191 curwp->w_flag |= WFMOVE; 192 chunk += llength(curwp->w_dotp) + 1; 193 } 194 } 195 /* 196 * KFORW here is a bug. Should be KBACK/KFORW, but we need to 197 * rewrite the ldelete code (later)? 198 */ 199 return (ldelete(chunk, KFORW)); 200} 201 202/* 203 * Yank text back from the kill buffer. This is really easy. All of the work 204 * is done by the standard insert routines. All you do is run the loop, and 205 * check for errors. The blank lines are inserted with a call to "newline" 206 * instead of a call to "lnewline" so that the magic stuff that happens when 207 * you type a carriage return also happens when a carriage return is yanked 208 * back from the kill buffer. An attempt has been made to fix the cosmetic 209 * bug associated with a yank when dot is on the top line of the window 210 * (nothing moves, because all of the new text landed off screen). 211 */ 212/* ARGSUSED */ 213int 214yank(int f, int n) 215{ 216 struct line *lp; 217 int c, i, nline; 218 219 if (n < 0) 220 return (FALSE); 221 222 /* newline counting */ 223 nline = 0; 224 225 undo_add_boundary(); 226 undo_no_boundary(TRUE); 227 while (n--) { 228 /* mark around last yank */ 229 isetmark(); 230 i = 0; 231 while ((c = kremove(i)) >= 0) { 232 if (c == '\n') { 233 if (newline(FFRAND, 1) == FALSE) 234 return (FALSE); 235 ++nline; 236 } else { 237 if (linsert(1, c) == FALSE) 238 return (FALSE); 239 } 240 ++i; 241 } 242 } 243 /* cosmetic adjustment */ 244 lp = curwp->w_linep; 245 246 /* if offscreen insert */ 247 if (curwp->w_dotp == lp) { 248 while (nline-- && lback(lp) != curbp->b_headp) 249 lp = lback(lp); 250 /* adjust framing */ 251 curwp->w_linep = lp; 252 curwp->w_flag |= WFFULL; 253 } 254 undo_no_boundary(FALSE); 255 undo_add_boundary(); 256 return (TRUE); 257} 258 259