Commit | Line | Data |
---|---|---|
15637ed4 RG |
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 | } |