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