Annotation of src/usr.bin/mg/yank.c, Revision 1.13
1.13 ! bcallah 1: /* $OpenBSD: yank.c,v 1.12 2015/03/16 13:47:48 bcallah Exp $ */
1.1 kjell 2:
3: /* This file is in the public domain. */
4:
5: /*
6: * kill ring functions
7: */
8:
1.13 ! bcallah 9: #include <sys/queue.h>
! 10: #include <signal.h>
! 11: #include <stdio.h>
! 12: #include <stdlib.h>
! 13: #include <string.h>
! 14:
1.1 kjell 15: #include "def.h"
16:
1.13 ! bcallah 17: #define KBLOCK 8192 /* Kill grow. */
1.1 kjell 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) {
1.2 kjell 35: free(kbufp);
1.1 kjell 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.
1.6 deraadt 47: * This call is ignored if KNONE is set.
1.1 kjell 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 */
1.11 lum 79: dobeep();
1.1 kjell 80: ewprintf("Kill buffer size at maximum");
81: return (FALSE);
82: }
83: if ((nbufp = malloc((unsigned)(ksize + KBLOCK))) == NULL) {
1.11 lum 84: dobeep();
1.1 kjell 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: if (kbufp != NULL)
1.2 kjell 91: free(kbufp);
1.1 kjell 92: kbufp = nbufp;
93: ksize += KBLOCK;
94: kused = kused - kstart + nstart;
95: kstart = nstart;
96: return (TRUE);
97: }
98:
99: /*
100: * This function gets characters from the kill buffer. If the character
101: * index "n" is off the end, it returns "-1". This lets the caller just
102: * scan along until it gets a "-1" back.
103: */
104: int
105: kremove(int n)
106: {
107: if (n < 0 || n + kstart >= kused)
108: return (-1);
109: return (CHARMASK(kbufp[n + kstart]));
110: }
111:
112: /*
113: * Copy a string into the kill buffer. kflag gives direction.
114: * if KNONE, do nothing.
115: */
116: int
117: kchunk(char *cp1, RSIZE chunk, int kflag)
118: {
119: /*
120: * HACK - doesn't matter, and fixes back-over-nl bug for empty
121: * kill buffers.
122: */
123: if (kused == kstart)
124: kflag = KFORW;
125:
1.9 kjell 126: if (kflag & KFORW) {
1.1 kjell 127: while (ksize - kused < chunk)
128: if (kgrow(kflag) == FALSE)
129: return (FALSE);
130: bcopy(cp1, &(kbufp[kused]), (int)chunk);
131: kused += chunk;
1.9 kjell 132: } else if (kflag & KBACK) {
1.1 kjell 133: while (kstart < chunk)
134: if (kgrow(kflag) == FALSE)
135: return (FALSE);
136: bcopy(cp1, &(kbufp[kstart - chunk]), (int)chunk);
137: kstart -= chunk;
1.9 kjell 138: }
1.1 kjell 139:
140: return (TRUE);
141: }
142:
143: /*
144: * Kill line. If called without an argument, it kills from dot to the end
145: * of the line, unless it is at the end of the line, when it kills the
146: * newline. If called with an argument of 0, it kills from the start of the
147: * line to dot. If called with a positive argument, it kills from dot
148: * forward over that number of newlines. If called with a negative argument
149: * it kills any text before dot on the current line, then it kills back
150: * abs(arg) lines.
151: */
152: /* ARGSUSED */
153: int
154: killline(int f, int n)
155: {
156: struct line *nextp;
157: RSIZE chunk;
158: int i, c;
159:
160: /* clear kill buffer if last wasn't a kill */
161: if ((lastflag & CFKILL) == 0)
162: kdelete();
163: thisflag |= CFKILL;
164: if (!(f & FFARG)) {
165: for (i = curwp->w_doto; i < llength(curwp->w_dotp); ++i)
166: if ((c = lgetc(curwp->w_dotp, i)) != ' ' && c != '\t')
167: break;
168: if (i == llength(curwp->w_dotp))
169: chunk = llength(curwp->w_dotp) - curwp->w_doto + 1;
170: else {
171: chunk = llength(curwp->w_dotp) - curwp->w_doto;
172: if (chunk == 0)
173: chunk = 1;
174: }
175: } else if (n > 0) {
1.10 deraadt 176: chunk = llength(curwp->w_dotp) - curwp->w_doto;
1.1 kjell 177: nextp = lforw(curwp->w_dotp);
1.10 deraadt 178: if (nextp != curbp->b_headp)
179: chunk++; /* newline */
180: if (nextp == curbp->b_headp)
181: goto done; /* EOL */
1.1 kjell 182: i = n;
183: while (--i) {
1.10 deraadt 184: chunk += llength(nextp);
185: nextp = lforw(nextp);
186: if (nextp != curbp->b_headp)
187: chunk++; /* newline */
1.4 kjell 188: if (nextp == curbp->b_headp)
1.10 deraadt 189: break; /* EOL */
1.1 kjell 190: }
191: } else {
192: /* n <= 0 */
193: chunk = curwp->w_doto;
194: curwp->w_doto = 0;
195: i = n;
196: while (i++) {
1.10 deraadt 197: if (lforw(curwp->w_dotp))
198: chunk++;
1.1 kjell 199: curwp->w_dotp = lback(curwp->w_dotp);
1.8 kjell 200: curwp->w_rflag |= WFMOVE;
1.10 deraadt 201: chunk += llength(curwp->w_dotp);
1.1 kjell 202: }
203: }
204: /*
205: * KFORW here is a bug. Should be KBACK/KFORW, but we need to
206: * rewrite the ldelete code (later)?
207: */
1.10 deraadt 208: done:
209: if (chunk)
210: return (ldelete(chunk, KFORW));
211: return (TRUE);
1.1 kjell 212: }
213:
214: /*
215: * Yank text back from the kill buffer. This is really easy. All of the work
216: * is done by the standard insert routines. All you do is run the loop, and
217: * check for errors. The blank lines are inserted with a call to "newline"
218: * instead of a call to "lnewline" so that the magic stuff that happens when
219: * you type a carriage return also happens when a carriage return is yanked
220: * back from the kill buffer. An attempt has been made to fix the cosmetic
221: * bug associated with a yank when dot is on the top line of the window
222: * (nothing moves, because all of the new text landed off screen).
223: */
224: /* ARGSUSED */
225: int
226: yank(int f, int n)
227: {
228: struct line *lp;
229: int c, i, nline;
230:
231: if (n < 0)
232: return (FALSE);
233:
234: /* newline counting */
235: nline = 0;
236:
1.7 kjell 237: undo_boundary_enable(FFRAND, 0);
1.1 kjell 238: while (n--) {
239: /* mark around last yank */
240: isetmark();
241: i = 0;
242: while ((c = kremove(i)) >= 0) {
243: if (c == '\n') {
1.12 bcallah 244: if (enewline(FFRAND, 1) == FALSE)
1.1 kjell 245: return (FALSE);
246: ++nline;
247: } else {
248: if (linsert(1, c) == FALSE)
249: return (FALSE);
250: }
251: ++i;
252: }
253: }
254: /* cosmetic adjustment */
255: lp = curwp->w_linep;
256:
257: /* if offscreen insert */
258: if (curwp->w_dotp == lp) {
1.4 kjell 259: while (nline-- && lback(lp) != curbp->b_headp)
1.1 kjell 260: lp = lback(lp);
261: /* adjust framing */
262: curwp->w_linep = lp;
1.8 kjell 263: curwp->w_rflag |= WFFULL;
1.1 kjell 264: }
1.7 kjell 265: undo_boundary_enable(FFRAND, 1);
1.1 kjell 266: return (TRUE);
267: }
268: