Annotation of src/usr.bin/mg/yank.c, Revision 1.3
1.3 ! kjell 1: /* $OpenBSD: yank.c,v 1.2 2005/12/20 06:17:36 kjell 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.
45: * This call is ignored if KNONE is set.
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 */
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)
1.2 kjell 87: free(kbufp);
1.1 kjell 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: */
100: int
101: kremove(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: */
112: int
113: kchunk(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 */
150: int
151: killline(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_linep)
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_linep)
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 */
213: int
214: yank(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_linep)
249: lp = lback(lp);
250: /* adjust framing */
251: curwp->w_linep = lp;
1.3 ! kjell 252: curwp->w_flag |= WFFULL;
1.1 kjell 253: }
254: undo_no_boundary(FALSE);
255: undo_add_boundary();
256: return (TRUE);
257: }
258: