Annotation of src/usr.bin/mg/yank.c, Revision 1.15
1.15 ! lum 1: /* $OpenBSD: yank.c,v 1.14 2015/12/11 20:21:23 mmcc 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));
1.14 mmcc 90: free(kbufp);
1.1 kjell 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:
1.9 kjell 125: if (kflag & KFORW) {
1.1 kjell 126: while (ksize - kused < chunk)
127: if (kgrow(kflag) == FALSE)
128: return (FALSE);
129: bcopy(cp1, &(kbufp[kused]), (int)chunk);
130: kused += chunk;
1.9 kjell 131: } else if (kflag & KBACK) {
1.1 kjell 132: while (kstart < chunk)
133: if (kgrow(kflag) == FALSE)
134: return (FALSE);
135: bcopy(cp1, &(kbufp[kstart - chunk]), (int)chunk);
136: kstart -= chunk;
1.9 kjell 137: }
1.1 kjell 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) {
1.10 deraadt 175: chunk = llength(curwp->w_dotp) - curwp->w_doto;
1.1 kjell 176: nextp = lforw(curwp->w_dotp);
1.10 deraadt 177: if (nextp != curbp->b_headp)
178: chunk++; /* newline */
179: if (nextp == curbp->b_headp)
180: goto done; /* EOL */
1.1 kjell 181: i = n;
182: while (--i) {
1.10 deraadt 183: chunk += llength(nextp);
184: nextp = lforw(nextp);
185: if (nextp != curbp->b_headp)
186: chunk++; /* newline */
1.4 kjell 187: if (nextp == curbp->b_headp)
1.10 deraadt 188: break; /* EOL */
1.1 kjell 189: }
190: } else {
191: /* n <= 0 */
192: chunk = curwp->w_doto;
193: curwp->w_doto = 0;
194: i = n;
195: while (i++) {
1.10 deraadt 196: if (lforw(curwp->w_dotp))
197: chunk++;
1.1 kjell 198: curwp->w_dotp = lback(curwp->w_dotp);
1.8 kjell 199: curwp->w_rflag |= WFMOVE;
1.10 deraadt 200: chunk += llength(curwp->w_dotp);
1.1 kjell 201: }
202: }
203: /*
204: * KFORW here is a bug. Should be KBACK/KFORW, but we need to
205: * rewrite the ldelete code (later)?
206: */
1.10 deraadt 207: done:
208: if (chunk)
209: return (ldelete(chunk, KFORW));
210: return (TRUE);
1.1 kjell 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:
1.7 kjell 236: undo_boundary_enable(FFRAND, 0);
1.1 kjell 237: while (n--) {
238: /* mark around last yank */
239: isetmark();
240: i = 0;
241: while ((c = kremove(i)) >= 0) {
1.15 ! lum 242: if (c == *curbp->b_nlchr) {
1.12 bcallah 243: if (enewline(FFRAND, 1) == FALSE)
1.1 kjell 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) {
1.4 kjell 258: while (nline-- && lback(lp) != curbp->b_headp)
1.1 kjell 259: lp = lback(lp);
260: /* adjust framing */
261: curwp->w_linep = lp;
1.8 kjell 262: curwp->w_rflag |= WFFULL;
1.1 kjell 263: }
1.7 kjell 264: undo_boundary_enable(FFRAND, 1);
1.1 kjell 265: return (TRUE);
266: }
267: