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