1 /*
2 *       Text line handling.
3 * The functions in this file
4 * are a general set of line management
5 * utilities. They are the only routines that
6 * touch the text. They also touch the buffer
7 * and window structures, to make sure that the
8 * necessary updating gets done. There are routines
9 * in this file that handle the kill buffer too.
10 * It isn't here for any good reason.
11 *
12 * Note that this code only updates the dot and
13 * mark values in the window list. Since all the code
14 * acts on the current window, the buffer that we
15 * are editing must be being displayed, which means
16 * that "b_nwnd" is non zero, which means that the
17 * dot and mark values in the buffer headers are
18 * nonsense.
19 */
20 
21 #include    <stdlib.h>
22 #include    "def.h"
23 
24 void l_fix_up ();
25 
26 extern char MSG_cnt_alloc[];
27 #if RUNCHK
28 extern char ERR_no_alloc[];
29 extern char ERR_db_dalloc[];
30 extern char ERR_lock[];
31 extern char ERR_lock_del[];
32 #endif
33 
34 extern LINE *cur_pat;
35 extern LINE *cur_mask;
36 extern bool read_pat_mode;
37 extern BUFFER sav_buf;
38 
39 /*
40 * This routine allocates a block
41 * of memory large enough to hold a LINE
42 * containing "size" characters. Return a pointer
43 * to the new block, or NULL if there isn't
44 * any memory left. Print a message in the
45 * message line if no space.
46 */
47 LINE *
lalloc(size)48 lalloc (size)
49     register int size;
50 {
51     register LINE *lp;
52     char buf[NCOL], buf1[NCOL];
53 #if RUNCHK
54     if (read_pat_mode)
55 	printf (ERR_no_alloc);
56 #endif
57 
58     if ((lp = (LINE *) malloc (sizeof (LINE) + size)) == NULL)
59     {
60 	sprintf (buf1, MSG_cnt_alloc, R_POS_FMT (curwp));
61 	sprintf (buf, buf1, (A32) size);
62 	err_echo (buf);
63 	curbp->b_flag |= BFBAD;	/* may be trashed */
64 	curwp->w_flag |= WFMODE;
65 	update ();
66 	return (NULL);
67     }
68     lp->l_size = size;
69     lp->l_used = 0;
70     lp->l_file_offset = 0;	/* set resonable initial value */
71     return (lp);
72 }
73 
74 /*
75 * Delete line "lp". Fix all of the
76 * links that might point at it (they are
77 * moved to offset 0 of the next line.
78 * Unlink the line from whatever buffer it
79 * might be in. Release the memory. The
80 * buffers are updated too; the magic conditions
81 * described in the above comments don't hold
82 * here.
83 */
84 
85 void
lfree(lp)86 lfree (lp)
87     register LINE *lp;
88 {
89     register BUFFER *bp;
90     register WINDOW *wp;
91 
92 #if RUNCHK
93     if (read_pat_mode)
94 	printf (ERR_db_dalloc);
95 #endif
96 
97     wp = wheadp;
98     while (wp != NULL)
99     {
100 	if (wp->w_linep == lp)
101 	{
102 	    wp->w_linep = lp->l_fp;
103 	    wp->w_loff = 0;
104 	}
105 
106 	if (wp->w_dotp == lp)
107 	{
108 	    wp->w_dotp = lp->l_fp;
109 	    wp->w_doto = 0;
110 	}
111 
112 	if (wp->w_markp == lp)
113 	{
114 	    wp->w_markp = lp->l_fp;
115 	    wp->w_marko = 0;
116 	}
117 
118 	wp = wp->w_wndp;
119     }
120 
121     bp = bheadp;
122     while (bp != NULL)
123     {
124 	if (bp->b_nwnd == 0)
125 	{
126 	    if (bp->b_dotp == lp)
127 	    {
128 		bp->b_dotp = lp->l_fp;
129 		bp->b_doto = 0;
130 	    }
131 
132 	    if (bp->b_markp == lp)
133 	    {
134 		bp->b_markp = lp->l_fp;
135 		bp->b_marko = 0;
136 	    }
137 	}
138 	bp = bp->b_bufp;
139     }
140 
141     lp->l_bp->l_fp = lp->l_fp;
142     lp->l_fp->l_bp = lp->l_bp;
143     free ((char *) lp);
144 }
145 
146 /*
147 * This routine gets called when
148 * a character is changed in place in the
149 * current buffer. It updates all of the required
150 * flags in the buffer and window system. The flag
151 * used is passed as an argument; if the buffer is being
152 * displayed in more than 1 window we change EDIT to
153 * HARD. Set MODE if the mode line needs to be
154 * updated (the "*" has to be set).
155 */
156 void
lchange(flag)157 lchange (flag)
158     register int flag;
159 {
160     register WINDOW *wp;
161 
162     if (curbp->b_nwnd != 1)	/* Ensure hard.     */
163 	flag = WFHARD;
164     if ((curbp->b_flag & BFCHG) == 0)
165     {
166 	/* First change, so     */
167 	flag |= WFMODE;		/* update mode lines.   */
168 	curbp->b_flag |= BFCHG;
169     }
170 
171     wp = wheadp;
172     while (wp != NULL)
173     {
174 	if (wp->w_bufp == curbp)
175 	    wp->w_flag |= flag;
176 	wp = wp->w_wndp;
177     }
178 }
179 
180 /*
181  *  Break the line "dotp" in two at the position "doto."
182  */
183 
184 LINE *
l_break_in_two(lp,lo,extra)185 l_break_in_two (lp, lo, extra)
186     register LINE *lp;
187     register LPOS lo, extra;
188 {
189     register LINE *new_lp;
190     register D8 *cp1;
191     register D8 *cp2;
192     LPOS cnt, i;
193 
194     i = 0;
195     cnt = lp->l_used - lo;
196     if ((new_lp = lalloc (cnt + extra)) == NULL)
197 	return (NULL);
198 
199     cp1 = &lp->l_text[lo];	/* starting location, source */
200     cp2 = &new_lp->l_text[0];	/* starting location, destination */
201 
202     /* kill bytes in the current line */
203     while (i++ < cnt)
204     {
205 	*cp2++ = *cp1++;
206     }
207     lp->l_used -= cnt;
208     new_lp->l_used = cnt;
209     new_lp->l_file_offset = new_lp->l_file_offset + lo;
210 
211     /* insert into chain */
212     new_lp->l_fp = lp->l_fp;
213     lp->l_fp = new_lp;
214     new_lp->l_bp = lp;
215     new_lp->l_fp->l_bp = new_lp;
216     return (new_lp);
217 }
218 
219 /*
220 * Insert "n" copies of the character "c"
221 * at the current location of dot. In the easy case
222 * all that happens is the text is stored in the line.
223 * Always allocate some extra space in line so that edit
224 * will be faster next time but will save space in the general case.
225 * In the hard case, the line has to be reallocated.
226 * When the window list is updated, take special
227 * care; I screwed it up once. You always update dot
228 * in the current window. You update mark, and a
229 * dot in another window, if it is greater than
230 * the place where you did the insert. Return TRUE
231 * if all is well, and FALSE on errors.
232 */
233 bool
linsert(n,c)234 linsert (n, c)
235     uchar c;
236     int n;
237 {
238     register D8 *cp1;
239     register D8 *cp2;
240     register LINE *lp1;
241     register LINE *lp2;
242     register short doto;
243     register int i;
244     register WINDOW *wp;
245 
246 #if RUNCHK
247     /* check that buffer size can be changed */
248     if (curbp->b_flag & BFSLOCK)
249     {
250 	writ_echo (ERR_lock);
251 	return (FALSE);
252     }
253 #endif
254 
255     lchange (WFMOVE);
256     lp1 = curwp->w_dotp;	/* Current line     */
257     if (lp1 == curbp->b_linep)
258     {
259 	/* At the end: special  */
260 	/* break the current line at the end */
261 	if ((lp2 = l_break_in_two (lp1, lp1->l_used, (LPOS) n + NBLOCK)) == NULL)
262 	    return (FALSE);
263 	for (i = 0; i < n; ++i)	/* Add the characters   */
264 	    lp2->l_text[i] = c;
265 	lp2->l_used = n;
266 	curwp->w_dotp = lp2;
267 	curwp->w_doto = n;
268 	return (TRUE);
269     }
270 
271     doto = curwp->w_doto;	/* Save for later.  */
272     if (lp1->l_used + n > lp1->l_size)
273     {
274 	/* break the current line and let the normal insert do it */
275 	if ((lp2 = l_break_in_two (lp1, doto, (LPOS) n + NBLOCK)) == NULL)
276 	    return (FALSE);
277 	lp1->l_text[doto] = c;
278 	lp1->l_used++;
279 	curwp->w_doto++;
280 	if (curwp->w_doto >= lp1->l_used)
281 	{
282 	    curwp->w_dotp = lp2;
283 	    curwp->w_doto = 0;
284 	}
285 	if (n > 1)
286 	    return (linsert (n - 1, c));	/* handle the rest in normal maner */
287     }
288     else
289     {
290 	/* Easy: in place   */
291 	lp2 = lp1;		/* Pretend new line */
292 	lp2->l_used += n;
293 	cp2 = &lp1->l_text[lp1->l_used];
294 	cp1 = cp2 - n;
295 	while (cp1 != &lp1->l_text[doto])
296 	    *--cp2 = *--cp1;
297 	for (i = 0; i < n; ++i)	/* Add the characters   */
298 	    lp2->l_text[doto + i] = c;
299 	move_ptr (curwp, (A32) n, TRUE, TRUE, TRUE);
300     }
301 
302     wp = wheadp;		/* Update windows   */
303     while (wp != NULL)
304     {
305 	if ((wp->w_linep == lp1) && (wp->w_loff >= lp1->l_used))
306 	{
307 	    wp->w_linep = lp2;
308 	    wp->w_loff -= lp1->l_used;
309 	}
310 
311 	/* move dot to next line but not to head line */
312 	if ((wp->w_dotp == lp1) && (wp->w_doto >= lp1->l_used) &&
313 	    (wp->w_dotp->l_fp->l_size != 0))
314 	{
315 	    wp->w_dotp = lp2;
316 	    wp->w_doto -= (lp1->l_used - 1);
317 	}
318 
319 	if ((wp->w_markp == lp1) && (wp->w_marko >= lp1->l_used))
320 	{
321 	    wp->w_markp = lp2;
322 	    wp->w_marko -= (lp1->l_used - 1);
323 	}
324 
325 	wp = wp->w_wndp;
326     }
327     l_fix_up (lp1);		/* re-adjust file offsets */
328     return (TRUE);
329 }
330 
331 /*
332 * This function deletes n_bytes,
333 * starting at dot. It understands how to deal
334 * with end of lines, etc. It returns TRUE if all
335 * of the characters were deleted, and FALSE if
336 * they were not (because dot ran into the end of
337 * the buffer). The "kflag" is TRUE if the text
338 * should be put in the kill buffer.
339 */
340 bool
ldelete(n_bytes,kflag)341 ldelete (n_bytes, kflag)
342     A32 n_bytes;
343     int kflag;
344 {
345     register LINE *dotp, *lp, *lp_prev, *lp_next;
346     register LPOS doto, l_cnt;
347     register WINDOW *wp;
348     D8 *cp1, *cp2;
349     D32 dot_pos;
350     uint n_byt;
351 
352 #if RUNCHK
353     /* check that buffer size can be changed */
354     if (curbp->b_flag & BFSLOCK)
355     {
356 	writ_echo (ERR_lock_del);
357 	return (FALSE);
358     }
359 #endif
360     lchange (WFMOVE);
361     doto = curwp->w_doto;
362     dotp = curwp->w_dotp;
363     lp_prev = dotp->l_bp;
364     dot_pos = DOT_POS (curwp);
365 
366     /* if at the end of the buffer then delete nothing */
367     if (dot_pos >= BUF_SIZE (curwp))
368     {
369 	l_fix_up (dotp);	/* re-adjust file offsets */
370 	return (TRUE);
371     }
372 
373     /* save dot and mark positions for later restore */
374     wp = wheadp;
375     while (wp != NULL)
376     {
377 	wp->w_dot_temp = DOT_POS (wp);
378 	if (wp->w_markp != NULL)/* mark may not be set */
379 	    wp->w_mark_temp = MARK_POS (wp);
380 	wp->w_wind_temp = WIND_POS (wp);
381 	wp = wp->w_wndp;
382     }
383 
384     /* is delete wholy within one line? */
385     if ((doto + n_bytes) <= dotp->l_used)
386     {
387 	cp1 = &dotp->l_text[doto];	/* Scrunch text.    */
388 	cp2 = cp1 + n_bytes;
389 
390 	/* put stuff to delete into the kill buffer */
391 	if (kflag != FALSE)
392 	{
393 	    /* Kill?        */
394 	    while (cp1 != cp2)
395 	    {
396 		if (b_append_c (&sav_buf, *cp1) == FALSE)
397 		    return (FALSE);
398 		++cp1;
399 	    }
400 	    cp1 = &dotp->l_text[doto];
401 	}
402 	/* kill bytes in the current line */
403 	while (cp2 < &dotp->l_text[dotp->l_used])
404 	    *cp1++ = *cp2++;
405 
406 	dotp->l_used -= n_bytes;
407     }
408     else
409     {				/* wholesale delete by moving lines to save buffer */
410 	if (doto != 0)
411 	{
412 	    if ((lp = l_break_in_two (dotp, doto, 0)) == NULL)
413 		return (FALSE);
414 	}
415 	else
416 	    lp = dotp;
417 
418 	n_byt = n_bytes;
419 	/* now handle whole lines if necessary */
420 	while (n_byt > 0)
421 	{
422 	    lp_next = lp->l_fp;
423 
424 	    if (n_byt < lp->l_used)
425 	    {
426 		/* get last piece of a line */
427 		lp_next = l_break_in_two (lp, n_byt, 0);
428 	    }
429 	    n_byt -= lp->l_used;
430 	    if (kflag)
431 	    {
432 		/* remove form linked list */
433 		lp->l_bp->l_fp = lp->l_fp;
434 		lp->l_fp->l_bp = lp->l_bp;
435 		/* append it to the save buffer */
436 		b_append_l (&sav_buf, lp);
437 	    }
438 	    else
439 		/* if we don't want it, free it */
440 		lfree (lp);
441 	    lp = lp_next;
442 	}
443     }
444     l_fix_up (lp_prev);		/* re-adjust file offsets */
445 
446     /* adjust dot and marks in other windows */
447     /* this should be ok because the save buffer dosn't disturb l_file_offset */
448     wp = wheadp;		/* Fix windows      */
449     while (wp != NULL)
450     {
451 	if (curbp == wp->w_bufp)
452 	{
453 	    A32 temp;
454 
455 	    /* if dot is before delete position, do nothing */
456 	    if (dot_pos <= (temp = wp->w_dot_temp))
457 	    {
458 		/* step back to the previous line */
459 		wp->w_doto = 0;
460 		wp->w_dotp = lp_prev;
461 
462 		/* if dot is in deleted range, set to dot position */
463 		if (temp > dot_pos + n_bytes)
464 		    /* if after deleted range, move back deleted ammount */
465 		    move_ptr (wp, temp - n_bytes, TRUE, TRUE, FALSE);
466 		else
467 		    /* if in the deleted range, move to curwp dot position */
468 		    move_ptr (wp, dot_pos, TRUE, TRUE, FALSE);
469 	    }
470 	    /* mark may not be set in some windows */
471 	    if (wp->w_markp != NULL)
472 	    {
473 		/* do the same for mark */
474 		if (dot_pos <= (temp = wp->w_mark_temp))
475 		{
476 		    /* if in or after the deleted range, move to curwp dot position */
477 		    wp->w_marko = curwp->w_doto;
478 		    wp->w_markp = curwp->w_dotp;
479 
480 		    /* if mark after deleted range */
481 		    if (temp > dot_pos + n_bytes)
482 		    {
483 			/* if after deleted range, move back deleted ammount */
484 			/* move dot then swap with mark to produce result */
485 			move_ptr (wp, temp - n_bytes, TRUE, TRUE, FALSE);
486 			lp_next = wp->w_dotp;
487 			wp->w_dotp = wp->w_markp;
488 			wp->w_markp = lp_next;
489 			l_cnt = wp->w_doto;
490 			wp->w_doto = wp->w_marko;
491 			wp->w_marko = l_cnt;
492 		    }
493 		}
494 	    }
495 	    /* if window position is before delete position, do nothing */
496 	    if (dot_pos <= (temp = wp->w_wind_temp))
497 	    {
498 		/* set window position to dot position */
499 		wp->w_loff = 0;
500 		wp->w_linep = wp->w_dotp;
501 		wind_on_dot (wp);
502 	    }
503 	}
504 	wp = wp->w_wndp;
505     }
506     /* update buffer display */
507     if ((blistp->b_nwnd != 0) &&
508 	(blistp->b_type == BTLIST))
509 	listbuffers ();
510     return (TRUE);
511 }
512 
513 /*
514 *   Replace character at dot position.
515 */
516 void
lreplace(n,c)517 lreplace (n, c)
518     int n;
519     char c;
520 {
521     lchange (WFEDIT);
522     while (n--)
523     {
524 	DOT_CHAR (curwp) = c & 0xff;
525 	move_ptr (curwp, 1L, TRUE, FALSE, TRUE);
526     }
527 }
528 
529 /*
530 * Replace plen characters before dot with argument string.
531 */
532 bool
lrepl_str(plen,rstr,mstr)533 lrepl_str (plen, rstr, mstr)
534 
535     register int plen;		/* length to remove     */
536     register LINE *rstr;	/* replace string       */
537     register LINE *mstr;	/* mask string       */
538 {
539     register int i;		/* used for random characters   */
540     register A32 dot_pos;	/* dot offset into buffer     */
541     register int rlen;		/* rplace string length */
542     register char c;		/* temp storage for char */
543     register char mask;		/* temp storage for mask */
544 
545     /*
546   * make the string lengths match (either pad the line
547   * so that it will fit, or scrunch out the excess).
548   * be careful with dot's offset.
549   */
550     /* get offset from begining of buffer */
551     dot_pos = DOT_POS (curwp);
552     rlen = rstr->l_used;
553     if (plen > rlen)
554     {
555 	ldelete ((A32) (plen - rlen), FALSE);
556     }
557     else if (plen < rlen)
558     {
559 	if (linsert (rlen - plen, ' ') == FALSE)
560 	    return (FALSE);
561     }
562     /* must use move_ptr because delete may advance to next line */
563     move_ptr (curwp, dot_pos, TRUE, FALSE, FALSE);
564 
565     /* do the replacement. */
566     for (i = 0; i < rlen; i++)
567     {
568 	c = DOT_CHAR (curwp);
569 	mask = mstr->l_text[i];
570 	DOT_CHAR (curwp) = (c & mask) | (rstr->l_text[i] & ~mask);
571 	move_ptr (curwp, 1L, TRUE, FALSE, TRUE);
572     }
573     move_ptr (curwp, dot_pos, TRUE, FALSE, FALSE);
574     lchange (WFHARD);
575     return (TRUE);
576 }
577 
578 /*
579 *   Line fixup.
580 *   This fixes the 'l_file_offset' variable in
581 *   each line structure.
582 *   This is necessary after every change in the size
583 *   of the buffer.
584 */
585 void
l_fix_up(line)586 l_fix_up (line)
587 
588     LINE *line;			/* points to buffer header line */
589 
590 {
591     long offset;
592 
593     offset = line->l_file_offset;	/* starting offset */
594     offset += line->l_used;
595     for (;;)
596     {
597 	line = line->l_fp;
598 	if (line->l_size == 0)
599 	    return;
600 	line->l_file_offset = offset;
601 	offset += line->l_used;
602     }
603 }
604