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