1 /* modify.c */
2
3 /* This file contains the low-level file modification functions:
4 * delete(frommark, tomark) - removes line or portions of lines
5 * add(frommark, text) - inserts new text
6 * change(frommark, tomark, text) - delete, then add
7 */
8
9 #include "config.h"
10 #include "vi.h"
11
12
13 /* delete a range of text from the file */
delete(frommark,tomark)14 delete(frommark, tomark)
15 MARK frommark; /* first char to be deleted */
16 MARK tomark; /* AFTER last char to be deleted */
17 {
18 int i; /* used to move thru logical blocks */
19 register char *scan; /* used to scan thru text of the blk */
20 register char *cpy; /* used when copying chars */
21 BLK *blk; /* a text block */
22 long l; /* a line number */
23 MARK m; /* a traveling version of frommark */
24
25 /* if not deleting anything, quit now */
26 if (frommark == tomark)
27 {
28 return;
29 }
30
31 /* This is a change */
32 changes++;
33
34 /* if this is a multi-line change, then we'll have to redraw */
35 if (markline(frommark) != markline(tomark))
36 {
37 mustredraw = TRUE;
38 redrawrange(markline(frommark), markline(tomark), markline(frommark));
39 }
40
41 /* adjust marks 'a through 'z and '' as needed */
42 l = markline(tomark);
43 for (i = 0; i < NMARKS; i++)
44 {
45 if (mark[i] < frommark)
46 {
47 continue;
48 }
49 else if (mark[i] < tomark)
50 {
51 mark[i] = MARK_UNSET;
52 }
53 else if (markline(mark[i]) == l)
54 {
55 if (markline(frommark) == l)
56 {
57 mark[i] -= markidx(tomark) - markidx(frommark);
58 }
59 else
60 {
61 mark[i] -= markidx(tomark);
62 }
63 }
64 else
65 {
66 mark[i] -= MARK_AT_LINE(l - markline(frommark));
67 }
68 }
69
70 /* Reporting... */
71 if (markidx(frommark) == 0 && markidx(tomark) == 0)
72 {
73 rptlines = markline(tomark) - markline(frommark);
74 rptlabel = "deleted";
75 }
76
77 /* find the block containing frommark */
78 l = markline(frommark);
79 for (i = 1; lnum[i] < l; i++)
80 {
81 }
82
83 /* process each affected block... */
84 for (m = frommark;
85 m < tomark && lnum[i] < INFINITY;
86 m = MARK_AT_LINE(lnum[i - 1] + 1))
87 {
88 /* fetch the block */
89 blk = blkget(i);
90
91 /* find the mark in the block */
92 scan = blk->c;
93 for (l = markline(m) - lnum[i - 1] - 1; l > 0; l--)
94 {
95 while (*scan++ != '\n')
96 {
97 }
98 }
99 scan += markidx(m);
100
101 /* figure out where the changes to this block end */
102 if (markline(tomark) > lnum[i])
103 {
104 cpy = blk->c + BLKSIZE;
105 }
106 else if (markline(tomark) == markline(m))
107 {
108 cpy = scan - markidx(m) + markidx(tomark);
109 }
110 else
111 {
112 cpy = scan;
113 for (l = markline(tomark) - markline(m);
114 l > 0;
115 l--)
116 {
117 while (*cpy++ != '\n')
118 {
119 }
120 }
121 cpy += markidx(tomark);
122 }
123
124 /* delete the stuff by moving chars within this block */
125 while (cpy < blk->c + BLKSIZE)
126 {
127 *scan++ = *cpy++;
128 }
129 while (scan < blk->c + BLKSIZE)
130 {
131 *scan++ = '\0';
132 }
133
134 /* adjust tomark to allow for lines deleted from this block */
135 tomark -= MARK_AT_LINE(lnum[i] + 1 - markline(m));
136
137 /* if this block isn't empty now, then advance i */
138 if (*blk->c)
139 {
140 i++;
141 }
142
143 /* the buffer has changed. Update hdr and lnum. */
144 blkdirty(blk);
145 }
146
147 /* must have at least 1 line */
148 if (nlines == 0)
149 {
150 blk = blkadd(1);
151 blk->c[0] = '\n';
152 blkdirty(blk);
153 cursor = MARK_FIRST;
154 }
155 }
156
157
158 /* add some text at a specific place in the file */
add(atmark,newtext)159 add(atmark, newtext)
160 MARK atmark; /* where to insert the new text */
161 char *newtext; /* NUL-terminated string to insert */
162 {
163 register char *scan; /* used to move through string */
164 register char *build; /* used while copying chars */
165 int addlines; /* number of lines we're adding */
166 int lastpart; /* size of last partial line */
167 BLK *blk; /* the block to be modified */
168 int blkno; /* the logical block# of (*blk) */
169 register char *newptr; /* where new text starts in blk */
170 BLK buf; /* holds chars from orig blk */
171 BLK linebuf; /* holds part of line that didn't fit */
172 BLK *following; /* the BLK following the last BLK */
173 int i;
174 long l;
175
176 /* if not adding anything, return now */
177 if (!*newtext)
178 {
179 return;
180 }
181
182 /* This is a change */
183 changes++;
184
185 /* count the number of lines in the new text */
186 for (scan = newtext, lastpart = addlines = 0; *scan; )
187 {
188 if (*scan++ == '\n')
189 {
190 addlines++;
191 lastpart = 0;
192 }
193 else
194 {
195 lastpart++;
196 }
197 }
198
199 /* Reporting... */
200 if (lastpart == 0 && markidx(atmark) == 0)
201 {
202 rptlines = addlines;
203 rptlabel = "added";
204 }
205
206 /* extract the line# from atmark */
207 l = markline(atmark);
208
209 /* if more than 0 lines, then we'll have to redraw the screen */
210 if (addlines > 0)
211 {
212 mustredraw = TRUE;
213 if (markidx(atmark) == 0)
214 {
215 redrawrange(l, l, l + addlines);
216 }
217 else
218 {
219 /* make sure the last line gets redrawn -- it was
220 * split, so its appearance has changed
221 */
222 redrawrange(l, l + 1L, l + addlines + 1L);
223 }
224 }
225
226 /* adjust marks 'a through 'z and '' as needed */
227 for (i = 0; i < NMARKS; i++)
228 {
229 if (mark[i] < atmark)
230 {
231 continue;
232 }
233 else if (markline(mark[i]) > l)
234 {
235 mark[i] += MARK_AT_LINE(addlines);
236 }
237 else
238 {
239 mark[i] += MARK_AT_LINE(addlines) + lastpart;
240 }
241 }
242
243 /* get the block to be modified */
244 for (blkno = 1; lnum[blkno] < l && lnum[blkno + 1] < INFINITY; blkno++)
245 {
246 }
247 blk = blkget(blkno);
248 buf = *blk;
249
250 /* figure out where the new text starts */
251 for (newptr = buf.c, l = markline(atmark) - lnum[blkno - 1] - 1;
252 l > 0;
253 l--)
254 {
255 while (*newptr++ != '\n')
256 {
257 }
258 }
259 newptr += markidx(atmark);
260
261 /* keep start of old block */
262 build = blk->c + (newptr - buf.c);
263
264 /* fill this block (or blocks) from the newtext string */
265 while (*newtext)
266 {
267 while (*newtext && build < blk->c + BLKSIZE - 1)
268 {
269 *build++ = *newtext++;
270 }
271 if (*newtext)
272 {
273 /* save the excess */
274 for (scan = linebuf.c + BLKSIZE;
275 build > blk->c && build[-1] != '\n';
276 )
277 {
278 *--scan = *--build;
279 }
280
281 /* write the block */
282 while (build < blk->c + BLKSIZE)
283 {
284 *build++ = '\0';
285 }
286 blkdirty(blk);
287
288 /* add another block */
289 blkno++;
290 blk = blkadd(blkno);
291
292 /* copy in the excess from last time */
293 for (build = blk->c; scan < linebuf.c + BLKSIZE; )
294 {
295 *build++ = *scan++;
296 }
297 }
298 }
299
300 /* fill this block(s) from remainder of orig block */
301 while (newptr < buf.c + BLKSIZE && *newptr)
302 {
303 while (newptr < buf.c + BLKSIZE
304 && *newptr
305 && build < blk->c + BLKSIZE - 1)
306 {
307 *build++ = *newptr++;
308 }
309 if (newptr < buf.c + BLKSIZE && *newptr)
310 {
311 /* save the excess */
312 for (scan = linebuf.c + BLKSIZE;
313 build > blk->c && build[-1] != '\n';
314 )
315 {
316 *--scan = *--build;
317 }
318
319 /* write the block */
320 while (build < blk->c + BLKSIZE)
321 {
322 *build++ = '\0';
323 }
324 blkdirty(blk);
325
326 /* add another block */
327 blkno++;
328 blk = blkadd(blkno);
329
330 /* copy in the excess from last time */
331 for (build = blk->c; scan < linebuf.c + BLKSIZE; )
332 {
333 *build++ = *scan++;
334 }
335 }
336 }
337
338 /* see if we can combine our last block with the following block */
339 if (lnum[blkno] < nlines && lnum[blkno + 1] - lnum[blkno] < (BLKSIZE >> 6))
340 {
341 /* hey, we probably can! Get the following block & see... */
342 following = blkget(blkno + 1);
343 if (strlen(following->c) + (build - blk->c) < BLKSIZE - 1)
344 {
345 /* we can! Copy text from following to blk */
346 for (scan = following->c; *scan; )
347 {
348 *build++ = *scan++;
349 }
350 while (build < blk->c + BLKSIZE)
351 {
352 *build++ = '\0';
353 }
354 blkdirty(blk);
355
356 /* pretend the following was the last blk */
357 blk = following;
358 build = blk->c;
359 }
360 }
361
362 /* that last block is dirty by now */
363 while (build < blk->c + BLKSIZE)
364 {
365 *build++ = '\0';
366 }
367 blkdirty(blk);
368 }
369
370
371 /* change the text of a file */
change(frommark,tomark,newtext)372 change(frommark, tomark, newtext)
373 MARK frommark, tomark;
374 char *newtext;
375 {
376 int i;
377 long l;
378 char *text;
379 BLK *blk;
380
381 /* optimize for single-character replacement */
382 if (frommark + 1 == tomark && newtext[0] && !newtext[1] && newtext[0] != '\n')
383 {
384 /* find the block containing frommark */
385 l = markline(frommark);
386 for (i = 1; lnum[i] < l; i++)
387 {
388 }
389
390 /* get the block */
391 blk = blkget(i);
392
393 /* find the line within the block */
394 for (text = blk->c, i = l - lnum[i - 1] - 1; i > 0; text++)
395 {
396 if (*text == '\n')
397 {
398 i--;
399 }
400 }
401
402 /* replace the char */
403 text += markidx(frommark);
404 if (*text == newtext[0])
405 {
406 /* no change was needed - same char */
407 return;
408 }
409 else if (*text != '\n')
410 {
411 /* This is a change */
412 changes++;
413 ChangeText
414 {
415 *text = newtext[0];
416 blkdirty(blk);
417 }
418 return;
419 }
420 /* else it is a complex change involving newline... */
421 }
422
423 /* couldn't optimize, so do delete & add */
424 ChangeText
425 {
426 delete(frommark, tomark);
427 add(frommark, newtext);
428 rptlabel = "changed";
429 }
430 }
431