1 /*
2  * This file is part of DGD, https://github.com/dworkin/dgd
3  * Copyright (C) 1993-2010 Dworkin B.V.
4  * Copyright (C) 2010 DGD Authors (see the commit log for details)
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU Affero General Public License as
8  * published by the Free Software Foundation, either version 3 of the
9  * License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU Affero General Public License for more details.
15  *
16  * You should have received a copy of the GNU Affero General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 # include "ed.h"
21 # include "buffer.h"
22 
23 /*
24  * This file defines the basic editing operations.
25  */
26 
27 /*
28  * NAME:	editbuf->new()
29  * DESCRIPTION:	create a new edit buffer
30  */
eb_new(char * tmpfile)31 editbuf *eb_new(char *tmpfile)
32 {
33     editbuf *eb;
34 
35     eb = ALLOC(editbuf, 1);
36     eb->lb = lb_new((linebuf *) NULL, tmpfile);
37     eb->buffer = (block) 0;
38     eb->lines = 0;
39 
40     return eb;
41 }
42 
43 /*
44  * NAME:	editbuf->del()
45  * DESCRIPTION:	delete an edit buffer
46  */
eb_del(editbuf * eb)47 void eb_del(editbuf *eb)
48 {
49     lb_del(eb->lb);
50     FREE(eb);
51 }
52 
53 /*
54  * NAME:	editbuf->clear()
55  * DESCRIPTION:	reinitialize an edit buffer
56  */
eb_clear(editbuf * eb)57 void eb_clear(editbuf *eb)
58 {
59     lb_new(eb->lb, (char *) NULL);
60     eb->buffer = (block) 0;
61     eb->lines = 0;
62 }
63 
64 /*
65  * NAME:	editbuf->add()
66  * DESCRIPTION:	add a new block of lines to the edit buffer after a given line.
67  *		If this line is 0 the block is inserted before the other lines
68  *		in the edit buffer.
69  */
eb_add(editbuf * eb,Int ln,char * (* getline)())70 void eb_add(editbuf *eb, Int ln, char *(*getline) ())
71 {
72     block b;
73 
74     b = bk_new(eb->lb, getline);
75     if (b != (block) 0) {
76 	Int size;
77 
78 	size = eb->lines + bk_size(eb->lb, b);
79 	if (size < 0) {
80 	    error("Too many lines");
81 	}
82 
83 	if (ln == 0) {
84 	    if (eb->lines == 0) {
85 		eb->buffer = b;
86 	    } else {
87 		eb->buffer = bk_cat(eb->lb, b, eb->buffer);
88 	    }
89 	} else if (ln == eb->lines) {
90 	    eb->buffer = bk_cat(eb->lb, eb->buffer, b);
91 	} else {
92 	    block head, tail;
93 
94 	    bk_split(eb->lb, eb->buffer, ln, &head, &tail);
95 	    eb->buffer = bk_cat(eb->lb, bk_cat(eb->lb, head, b), tail);
96 	}
97 
98 	eb->lines = size;
99     }
100 }
101 
102 /*
103  * NAME:	editbuf->delete()
104  * DESCRIPTION:	delete a subrange of lines in the edit buffer
105  */
eb_delete(editbuf * eb,Int first,Int last)106 block eb_delete(editbuf *eb, Int first, Int last)
107 {
108     block head, mid, tail;
109     Int size;
110 
111     size = last - first + 1;
112 
113     if (last < eb->lines) {
114 	bk_split(eb->lb, eb->buffer, last, &mid, &tail);
115 	if (first > 1) {
116 	    bk_split(eb->lb, mid, first - 1, &head, &mid);
117 	    eb->buffer = bk_cat(eb->lb, head, tail);
118 	} else {
119 	    eb->buffer = tail;
120 	}
121     } else {
122 	mid = eb->buffer;
123 	if (first > 1) {
124 	    bk_split(eb->lb, mid, first - 1, &head, &mid);
125 	    eb->buffer = head;
126 	} else {
127 	    eb->buffer = (block) 0;
128 	}
129     }
130     eb->lines -= size;
131 
132     return mid;
133 }
134 
135 /*
136  * NAME:	editbuf->change()
137  * DESCRIPTION:	change a subrange of lines in the edit buffer
138  */
eb_change(editbuf * eb,Int first,Int last,block b)139 void eb_change(editbuf *eb, Int first, Int last, block b)
140 {
141     Int size;
142     block head, tail;
143 
144     size = eb->lines - (last - first + 1);
145     if (b != (block) 0) {
146 	size += bk_size(eb->lb, b);
147 	if (size < 0) {
148 	    error("Too many lines");
149 	}
150     }
151 
152     if (last < eb->lines) {
153 	if (first > 1) {
154 	    bk_split(eb->lb, eb->buffer, first - 1, &head, (block *) NULL);
155 	    bk_split(eb->lb, eb->buffer, last, (block *) NULL, &tail);
156 	    if (b != (block) 0) {
157 		b = bk_cat(eb->lb, bk_cat(eb->lb, head, b), tail);
158 	    } else {
159 		b = bk_cat(eb->lb, head, tail);
160 	    }
161 	} else {
162 	    bk_split(eb->lb, eb->buffer, last, (block *) NULL, &tail);
163 	    if (b != (block) 0) {
164 		b = bk_cat(eb->lb, b, tail);
165 	    } else {
166 		b = tail;
167 	    }
168 	}
169     } else if (first > 1) {
170 	bk_split(eb->lb, eb->buffer, first - 1, &head, (block *) NULL);
171 	if (b != (block) 0) {
172 	    b = bk_cat(eb->lb, head, b);
173 	} else {
174 	    b = head;
175 	}
176     }
177     eb->buffer = b;
178     eb->lines = size;
179 }
180 
181 /*
182  * NAME:	editbuf->yank()
183  * DESCRIPTION:	return a subrange block of the edit buffer
184  */
eb_yank(editbuf * eb,Int first,Int last)185 block eb_yank(editbuf *eb, Int first, Int last)
186 {
187     block head, mid, tail;
188 
189     if (last < eb->lines) {
190 	bk_split(eb->lb, eb->buffer, last, &mid, &tail);
191     } else {
192 	mid = eb->buffer;
193     }
194     if (first > 1) {
195 	bk_split(eb->lb, mid, first - 1, &head, &mid);
196     }
197 
198     return mid;
199 }
200 
201 /*
202  * NAME:	editbuf->put()
203  * DESCRIPTION:	put a block after a line in the edit buffer. The block is
204  *		supplied immediately.
205  */
eb_put(editbuf * eb,Int ln,block b)206 void eb_put(editbuf *eb, Int ln, block b)
207 {
208     Int size;
209 
210     size = eb->lines + bk_size(eb->lb, b);
211     if (size < 0) {
212 	error("Too many lines");
213     }
214 
215     if (ln == 0) {
216 	if (eb->lines == 0) {
217 	    eb->buffer = b;
218 	} else {
219 	    eb->buffer = bk_cat(eb->lb, b, eb->buffer);
220 	}
221     } else if (ln == eb->lines) {
222 	eb->buffer = bk_cat(eb->lb, eb->buffer, b);
223     } else {
224 	block head, tail;
225 
226 	bk_split(eb->lb, eb->buffer, ln, &head, &tail);
227 	eb->buffer = bk_cat(eb->lb, bk_cat(eb->lb, head, b), tail);
228     }
229 
230     eb->lines = size;
231 }
232 
233 /*
234  * NAME:	editbuf->range()
235  * DESCRIPTION:	output a subrange of the edit buffer, without first making
236  *		a subrange block for it
237  */
eb_range(editbuf * eb,Int first,Int last,void (* putline)(char *),int reverse)238 void eb_range(editbuf *eb, Int first, Int last, void (*putline) (char*), int reverse)
239 {
240     bk_put(eb->lb, eb->buffer, first - 1, last - first + 1, putline, reverse);
241 }
242 
243 /*
244  * Routines to add lines to a block in pieces. It would be nice if bk_new could
245  * be used for this, but this is only possible if the editor functions as a
246  * stand-alone program.
247  * Lines are stored in a local buffer, which is flushed into a block when full.
248  */
249 
250 static editbuf *eeb;	/* editor buffer */
251 
252 /*
253  * NAME:	add_line()
254  * DESCRIPTION:	return the next line from the lines buffer
255  */
add_line()256 static char *add_line()
257 {
258     editbuf *eb;
259 
260     eb = eeb;
261     if (eb->szlines > 0) {
262 	char *p;
263 	int len;
264 
265 	len = strlen(p = eb->llines) + 1;
266 	eb->llines += len;
267 	eb->szlines -= len;
268 	return p;
269     }
270     return (char *) NULL;
271 }
272 
273 /*
274  * NAME:	flush_line()
275  * DESCRIPTION:	flush the lines buffer into a block
276  */
flush_line(editbuf * eb)277 static void flush_line(editbuf *eb)
278 {
279     block b;
280 
281     eb->llines = eb->llbuf;
282     eeb = eb;
283     b = bk_new(eb->lb, add_line);
284     if (eb->flines == (block) 0) {
285 	eb->flines = b;
286     } else {
287 	eb->flines = bk_cat(eb->lb, eb->flines, b);
288     }
289 }
290 
291 /*
292  * NAME:	editbuf->startblock()
293  * DESCRIPTION:	start a block of lines
294  */
eb_startblock(editbuf * eb)295 void eb_startblock(editbuf *eb)
296 {
297     eb->flines = (block) 0;
298     eb->szlines = 0;
299 }
300 
301 /*
302  * NAME:	editbuf->addblock()
303  * DESCRIPTION:	add a line to the current block of lines
304  */
eb_addblock(editbuf * eb,char * text)305 void eb_addblock(editbuf *eb, char *text)
306 {
307     int len;
308 
309     len = strlen(text) + 1;
310 
311     if (eb->szlines + len >= sizeof(eb->llines)) {
312 	flush_line(eb);
313     }
314     memcpy(eb->llbuf + eb->szlines, text, len);
315     eb->szlines += len;
316 }
317 
318 /*
319  * NAME:	editbuf->endblock()
320  * DESCRIPTION:	finish the current block
321  */
eb_endblock(editbuf * eb)322 void eb_endblock(editbuf *eb)
323 {
324     if (eb->szlines > 0) {
325 	flush_line(eb);
326     }
327 }
328