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