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