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