]> pd.if.org Git - pd_readline/blob - mg/yank.c
Added mg from an OpenBSD mirror site. Many thanks to the OpenBSD team and the mg...
[pd_readline] / mg / yank.c
1 /*      $OpenBSD: yank.c,v 1.10 2011/07/15 16:50:52 deraadt Exp $       */
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) {
33                 free(kbufp);
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)
87                 free(kbufp);
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         }
135
136         return (TRUE);
137 }
138
139 /*
140  * Kill line.  If called without an argument, it kills from dot to the end
141  * of the line, unless it is at the end of the line, when it kills the
142  * newline.  If called with an argument of 0, it kills from the start of the
143  * line to dot.  If called with a positive argument, it kills from dot
144  * forward over that number of newlines.  If called with a negative argument
145  * it kills any text before dot on the current line, then it kills back
146  * abs(arg) lines.
147  */
148 /* ARGSUSED */
149 int
150 killline(int f, int n)
151 {
152         struct line     *nextp;
153         RSIZE    chunk;
154         int      i, c;
155
156         /* clear kill buffer if last wasn't a kill */
157         if ((lastflag & CFKILL) == 0)
158                 kdelete();
159         thisflag |= CFKILL;
160         if (!(f & FFARG)) {
161                 for (i = curwp->w_doto; i < llength(curwp->w_dotp); ++i)
162                         if ((c = lgetc(curwp->w_dotp, i)) != ' ' && c != '\t')
163                                 break;
164                 if (i == llength(curwp->w_dotp))
165                         chunk = llength(curwp->w_dotp) - curwp->w_doto + 1;
166                 else {
167                         chunk = llength(curwp->w_dotp) - curwp->w_doto;
168                         if (chunk == 0)
169                                 chunk = 1;
170                 }
171         } else if (n > 0) {
172                 chunk = llength(curwp->w_dotp) - curwp->w_doto;
173                 nextp = lforw(curwp->w_dotp);
174                 if (nextp != curbp->b_headp)
175                         chunk++;                /* newline */
176                 if (nextp == curbp->b_headp)
177                         goto done;              /* EOL */
178                 i = n;
179                 while (--i) {
180                         chunk += llength(nextp);
181                         nextp = lforw(nextp);
182                         if (nextp != curbp->b_headp)
183                                 chunk++;        /* newline */
184                         if (nextp == curbp->b_headp)
185                                 break;          /* EOL */
186                 }
187         } else {
188                 /* n <= 0 */
189                 chunk = curwp->w_doto;
190                 curwp->w_doto = 0;
191                 i = n;
192                 while (i++) {
193                         if (lforw(curwp->w_dotp))
194                                 chunk++;
195                         curwp->w_dotp = lback(curwp->w_dotp);
196                         curwp->w_rflag |= WFMOVE;
197                         chunk += llength(curwp->w_dotp);
198                 }
199         }
200         /*
201          * KFORW here is a bug.  Should be KBACK/KFORW, but we need to
202          * rewrite the ldelete code (later)?
203          */
204 done:
205         if (chunk)
206                 return (ldelete(chunk, KFORW));
207         return (TRUE);
208 }
209
210 /*
211  * Yank text back from the kill buffer.  This is really easy.  All of the work
212  * is done by the standard insert routines.  All you do is run the loop, and
213  * check for errors.  The blank lines are inserted with a call to "newline"
214  * instead of a call to "lnewline" so that the magic stuff that happens when
215  * you type a carriage return also happens when a carriage return is yanked
216  * back from the kill buffer.  An attempt has been made to fix the cosmetic
217  * bug associated with a yank when dot is on the top line of the window
218  * (nothing moves, because all of the new text landed off screen).
219  */
220 /* ARGSUSED */
221 int
222 yank(int f, int n)
223 {
224         struct line     *lp;
225         int      c, i, nline;
226
227         if (n < 0)
228                 return (FALSE);
229
230         /* newline counting */
231         nline = 0;
232
233         undo_boundary_enable(FFRAND, 0);
234         while (n--) {
235                 /* mark around last yank */
236                 isetmark();
237                 i = 0;
238                 while ((c = kremove(i)) >= 0) {
239                         if (c == '\n') {
240                                 if (newline(FFRAND, 1) == FALSE)
241                                         return (FALSE);
242                                 ++nline;
243                         } else {
244                                 if (linsert(1, c) == FALSE)
245                                         return (FALSE);
246                         }
247                         ++i;
248                 }
249         }
250         /* cosmetic adjustment */
251         lp = curwp->w_linep;
252
253         /* if offscreen insert */
254         if (curwp->w_dotp == lp) {
255                 while (nline-- && lback(lp) != curbp->b_headp)
256                         lp = lback(lp);
257                 /* adjust framing */
258                 curwp->w_linep = lp;
259                 curwp->w_rflag |= WFFULL;
260         }
261         undo_boundary_enable(FFRAND, 1);
262         return (TRUE);
263 }
264