1 /*
2 * The functions in this file are a general set of line management utilities.
3 * They are the only routines that touch the text. They also touch the buffer
4 * and window structures, to make sure that the necessary updating gets done.
5 * There are routines in this file that handle the kill buffer too. It isn't
6 * here for any good reason.
7 *
8 * Note that this code only updates the dot and mark values in the window list.
9 * Since all the code acts on the current window, the buffer that we are
10 * editing must be being displayed, which means that "b_nwnd" is non zero,
11 * which means that the dot and mark values in the buffer headers are nonsense.
12 */
13
14 #include <stdio.h>
15 #include "estruct.h"
16 #include "eproto.h"
17 #include "edef.h"
18 #include "elang.h"
19
20 #define BSIZE(a) (a + NBLOCK - 1) & (~(NBLOCK - 1))
21
22 static long last_size = -1L; /* last # of bytes yanked */
23
24 /*
25 * This routine allocates a block of memory large enough to hold a LINE
26 * containing "used" characters. Return a pointer to the new block, or
27 * NULL if there isn't any memory left. Print a message in the message
28 * line if no space.
29 */
30
lalloc(used)31 LINE *PASCAL NEAR lalloc(used)
32
33 register int used;
34
35 {
36 register LINE *lp;
37
38 if ((lp = (LINE *)room(sizeof(LINE)+used)) == NULL) {
39 mlabort(TEXT94);
40 /* "%%Out of memory" */
41 return(NULL);
42 }
43 lp->l_size = used;
44 lp->l_used = used;
45 #if WINDOW_MSWIN
46 {
47 static int o = 0;
48 if (--o < 0) {
49 longop(TRUE);
50 o = 10; /* to lower overhead, only 10% calls to longop */
51 }
52 }
53 #endif
54 return(lp);
55 }
56
57 /*
58 * Delete line "lp". Fix all of the links that might point at it (they are
59 * moved to offset 0 of the next line. Unlink the line from whatever buffer it
60 * might be in. Release the memory. The buffers are updated too; the magic
61 * conditions described in the above comments don't hold here.
62 */
lfree(lp)63 PASCAL NEAR lfree(lp)
64 register LINE *lp;
65 {
66 register BUFFER *bp;
67 SCREEN *scrp; /* screen to fix pointers in */
68 register EWINDOW *wp;
69 register int cmark; /* current mark */
70
71 /* in all screens.... */
72 scrp = first_screen;
73 while (scrp) {
74 wp = scrp->s_first_window;
75 while (wp != NULL) {
76 if (wp->w_linep == lp)
77 wp->w_linep = lforw(lp);
78 if (wp->w_dotp == lp) {
79 wp->w_dotp = lforw(lp);
80 wp->w_doto = 0;
81 }
82 for (cmark = 0; cmark < NMARKS; cmark++) {
83 if (wp->w_markp[cmark] == lp) {
84 wp->w_markp[cmark] = lforw(lp);
85 wp->w_marko[cmark] = 0;
86 }
87 }
88 wp = wp->w_wndp;
89 }
90
91 /* next screen! */
92 scrp = scrp->s_next_screen;
93 }
94
95 bp = bheadp;
96 while (bp != NULL) {
97 if (bp->b_nwnd == 0) {
98 if (bp->b_dotp == lp) {
99 bp->b_dotp = lforw(lp);
100 bp->b_doto = 0;
101 }
102 for (cmark = 0; cmark < NMARKS; cmark++) {
103 if (bp->b_markp[cmark] == lp) {
104 bp->b_markp[cmark] = lforw(lp);
105 bp->b_marko[cmark] = 0;
106 }
107 }
108 }
109 bp = bp->b_bufp;
110 }
111 lp->l_bp->l_fp = lp->l_fp;
112 lp->l_fp->l_bp = lp->l_bp;
113 free((char *) lp);
114 #if WINDOW_MSWIN
115 {
116 static int o = 0;
117 if (--o < 0) {
118 longop(TRUE);
119 o = 10; /* to lower overhead, only 10% calls to longop */
120 }
121 }
122 #endif
123 }
124
125 /*
126 * This routine gets called when a character is changed in place in the current
127 * buffer. It updates all of the required flags in the buffer and window
128 * system. The flag used is passed as an argument; if the buffer is being
129 * displayed in more than 1 window we change EDIT t HARD. Set MODE if the
130 * mode line needs to be updated (the "*" has to be set).
131 */
lchange(flag)132 PASCAL NEAR lchange(flag)
133 register int flag;
134 {
135 register EWINDOW *wp;
136 SCREEN *scrp; /* screen to fix pointers in */
137
138 if (curbp->b_nwnd != 1) /* Ensure hard. */
139 flag = WFHARD;
140 if ((curbp->b_flag&BFCHG) == 0) { /* First change, so */
141 flag |= WFMODE; /* update mode lines. */
142 curbp->b_flag |= BFCHG;
143 }
144
145 /* in all screens.... */
146 scrp = first_screen;
147 while (scrp) {
148 /* make sure all the needed windows get this flag */
149 wp = scrp->s_first_window;
150 while (wp != NULL) {
151 if (wp->w_bufp == curbp)
152 wp->w_flag |= flag;
153 wp = wp->w_wndp;
154 }
155
156 /* next screen! */
157 scrp = scrp->s_next_screen;
158 }
159 }
160
insspace(f,n)161 PASCAL NEAR insspace(f, n) /* insert spaces forward into text */
162
163 int f, n; /* default flag and numeric argument */
164
165 {
166 register int status;
167
168 if ((status = linsert(n, ' ')) == TRUE)
169 status = backchar(f, n);
170 return status;
171 }
172
173 /*
174 * linstr -- Insert a string at the current point
175 */
176
177 #if PROTO
linstr(char * instr)178 int PASCAL NEAR linstr(char *instr)
179 #else
180 int PASCAL NEAR linstr( instr)
181 char *instr;
182 #endif
183 {
184 register int status;
185 register int saved_undo; /* saved undo flag */
186
187 status = TRUE;
188 if (instr != NULL && *instr != '\0') {
189 /* record the insertion for the undo stack.... */
190 undo_insert(OP_ISTR, (long)strlen(instr), obj);
191
192 /* insert the string one character at a time */
193 saved_undo = undoing;
194 undoing = TRUE;
195 do {
196 status = ((*instr == '\r') ? lnewline(): linsert(1, *instr));
197
198 /* Insertion error? */
199 if (status != TRUE) {
200 mlwrite(TEXT168);
201 /* "%%Can not insert string" */
202 break;
203 }
204 instr++;
205 } while (*instr);
206 undoing = saved_undo;
207 }
208
209 return(status);
210 }
211
212 /*
213 * Insert "n" copies of the character "c" at the current location of dot. In
214 * the easy case all that happens is the text is stored in the line. In the
215 * hard case, the line has to be reallocated. When the window list is updated,
216 * take special care; I screwed it up once. You always update dot in the
217 * current window. You update mark, and a dot in another window, if it is
218 * greater than the place where you did the insert. Return TRUE if all is
219 * well, and FALSE on errors.
220 */
221
222 #if PROTO
linsert(int n,char c)223 PASCAL NEAR linsert(int n, char c)
224 #else
225 PASCAL NEAR linsert(n, c)
226
227 int n;
228 char c;
229 #endif
230
231 {
232 register char *cp1;
233 register char *cp2;
234 register LINE *lp1;
235 register LINE *lp2;
236 register LINE *lp3;
237 register int doto;
238 register int i;
239 register EWINDOW *wp;
240 SCREEN *scrp; /* screen to fix pointers in */
241 int cmark; /* current mark */
242
243 if (curbp->b_mode&MDVIEW) /* don't allow this command if */
244 return(rdonly()); /* we are in read only mode */
245
246 /* a zero insert means do nothing! */
247 if (n == 0)
248 return(TRUE);
249
250 /* Negative numbers of inserted characters are right out! */
251 if (n < 1)
252 return(FALSE);
253
254 /* remember we did this! */
255 obj.obj_char = c;
256 undo_insert(OP_INSC, (long)n, obj);
257
258 /* mark the current window's buffer as changed */
259 lchange(WFEDIT);
260
261 lp1 = curwp->w_dotp; /* Current line */
262 if (lp1 == curbp->b_linep) { /* At the end: special */
263 if (curwp->w_doto != 0) {
264 mlwrite(TEXT170);
265 /* "bug: linsert" */
266 return(FALSE);
267 }
268 if ((lp2=lalloc(BSIZE(n))) == NULL) /* Allocate new line */
269 return(FALSE);
270 lp2->l_used = n;
271 lp3 = lp1->l_bp; /* Previous line */
272 lp3->l_fp = lp2; /* Link in */
273 lp2->l_fp = lp1;
274 lp1->l_bp = lp2;
275 lp2->l_bp = lp3;
276 for (i=0; i<n; ++i)
277 lp2->l_text[i] = c;
278 curwp->w_dotp = lp2;
279 curwp->w_doto = n;
280 return(TRUE);
281 }
282 doto = curwp->w_doto; /* Save for later. */
283 if (lp1->l_used+n > lp1->l_size) { /* Hard: reallocate */
284 if ((lp2=lalloc(BSIZE(lp1->l_used+n))) == NULL)
285 return(FALSE);
286 lp2->l_used = lp1->l_used+n;
287 cp1 = &lp1->l_text[0];
288 cp2 = &lp2->l_text[0];
289 while (cp1 != &lp1->l_text[doto])
290 *cp2++ = *cp1++;
291 cp2 += n;
292 while (cp1 != &lp1->l_text[lp1->l_used])
293 *cp2++ = *cp1++;
294 lp1->l_bp->l_fp = lp2;
295 lp2->l_fp = lp1->l_fp;
296 lp1->l_fp->l_bp = lp2;
297 lp2->l_bp = lp1->l_bp;
298 free((char *) lp1);
299 } else { /* Easy: in place */
300 lp2 = lp1; /* Pretend new line */
301 lp2->l_used += n;
302 cp2 = &lp1->l_text[lp1->l_used];
303 cp1 = cp2-n;
304 while (cp1 != &lp1->l_text[doto])
305 *--cp2 = *--cp1;
306 }
307 for (i=0; i<n; ++i) /* Add the characters */
308 lp2->l_text[doto+i] = c;
309 /* in all screens.... */
310 scrp = first_screen;
311 while (scrp) {
312
313 wp = scrp->s_first_window;
314 while (wp != NULL) {
315 if (wp->w_linep == lp1)
316 wp->w_linep = lp2;
317 if (wp->w_dotp == lp1) {
318 wp->w_dotp = lp2;
319 if (wp==curwp || wp->w_doto>doto)
320 wp->w_doto += n;
321 }
322 for (cmark = 0; cmark < NMARKS; cmark++) {
323 if (wp->w_markp[cmark] == lp1) {
324 wp->w_markp[cmark] = lp2;
325 if (wp->w_marko[cmark] > doto)
326 wp->w_marko[cmark] += n;
327 }
328 }
329 wp = wp->w_wndp;
330 }
331
332 /* next screen! */
333 scrp = scrp->s_next_screen;
334 }
335 return(TRUE);
336 }
337
338 /*
339 * Overwrite a character into the current line at the current position
340 *
341 */
342
343 #if PROTO
lowrite(char c)344 PASCAL NEAR lowrite(char c)
345 #else
346 PASCAL NEAR lowrite(c)
347
348 char c; /* character to overwrite on current position */
349 #endif
350
351 {
352 if (curwp->w_doto < curwp->w_dotp->l_used &&
353 ((lgetc(curwp->w_dotp, curwp->w_doto) != '\t' || tabsize == 0) ||
354 (curwp->w_doto) % tabsize == tabsize -1))
355 ldelete(1L, FALSE);
356 return(linsert(1, c));
357 }
358
359 /*
360 * lover -- Overwrite a string at the current point
361 */
362
lover(ostr)363 int PASCAL NEAR lover(ostr)
364
365 char *ostr;
366
367 {
368 register int status = TRUE;
369
370 if (ostr != NULL)
371 while (*ostr && status == TRUE) {
372 status = ((*ostr == '\r') ? lnewline(): lowrite(*ostr));
373
374 /* Insertion error? */
375 if (status != TRUE) {
376 mlwrite(TEXT172);
377 /* "%%Out of memory while overwriting" */
378 break;
379 }
380 ostr++;
381 }
382 return(status);
383 }
384
385 /*
386 * Insert a newline into the buffer at the current location of dot in the
387 * current window. The funny ass-backwards way it does things is not a botch;
388 * it just makes the last line in the file not a special case. Return TRUE if
389 * everything works out and FALSE on error (memory allocation failure). The
390 * update of dot and mark is a bit easier then in the above case, because the
391 * split forces more updating.
392 */
lnewline()393 int PASCAL NEAR lnewline()
394 {
395 register char *cp1;
396 register char *cp2;
397 register LINE *lp1;
398 register LINE *lp2;
399 register int doto;
400 register EWINDOW *wp;
401 SCREEN *scrp; /* screen to fix pointers in */
402 int cmark; /* current mark */
403
404 if (curbp->b_mode&MDVIEW) /* don't allow this command if */
405 return(rdonly()); /* we are in read only mode */
406
407 /* remember we did this! */
408 obj.obj_char = 13;
409 undo_insert(OP_INSC, 1L, obj);
410
411 lchange(WFHARD);
412 lp1 = curwp->w_dotp; /* Get the address and */
413 doto = curwp->w_doto; /* offset of "." */
414 if ((lp2=lalloc(doto)) == NULL) /* New first half line */
415 return(FALSE);
416 cp1 = &lp1->l_text[0]; /* Shuffle text around */
417 cp2 = &lp2->l_text[0];
418 while (cp1 != &lp1->l_text[doto])
419 *cp2++ = *cp1++;
420 cp2 = &lp1->l_text[0];
421 while (cp1 != &lp1->l_text[lp1->l_used])
422 *cp2++ = *cp1++;
423 lp1->l_used -= doto;
424 lp2->l_bp = lp1->l_bp;
425 lp1->l_bp = lp2;
426 lp2->l_bp->l_fp = lp2;
427 lp2->l_fp = lp1;
428
429 /* in all screens.... */
430 scrp = first_screen;
431 while (scrp) {
432
433 wp = scrp->s_first_window;
434 while (wp != NULL) {
435 if (wp->w_linep == lp1)
436 wp->w_linep = lp2;
437 if (wp->w_dotp == lp1) {
438 if (wp->w_doto < doto)
439 wp->w_dotp = lp2;
440 else
441 wp->w_doto -= doto;
442 }
443 for (cmark = 0; cmark < NMARKS; cmark++) {
444 if (wp->w_markp[cmark] == lp1) {
445 if (wp->w_marko[cmark] < doto)
446 wp->w_markp[cmark] = lp2;
447 else
448 wp->w_marko[cmark] -= doto;
449 }
450 }
451 wp = wp->w_wndp;
452 }
453
454 /* next screen! */
455 scrp = scrp->s_next_screen;
456 }
457 return(TRUE);
458 }
459
460 /*
461
462 LDELETE:
463
464 This function deletes "n" bytes, starting at dot. Positive n
465 deletes forward, negative n deletes backwords. It understands how to
466 deal with end of lines, and with two byte characters. It returns TRUE
467 if all of the characters were deleted, and FALSE if they were not
468 (because dot ran into the buffer end). The "kflag" is TRUE if the text
469 should be put in the kill buffer.
470
471 */
472
ldelete(n,kflag)473 PASCAL NEAR ldelete(n, kflag)
474
475 long n; /* # of chars to delete */
476 int kflag; /* put killed text in kill buffer flag */
477
478 {
479 register char *cp1;
480 register char *cp2;
481 register LINE *dotp;
482 register int doto;
483 register int chunk;
484 register EWINDOW *wp;
485 int cmark; /* current mark */
486
487 if (curbp->b_mode&MDVIEW) /* don't allow this command if */
488 return(rdonly()); /* we are in read only mode */
489
490 /* going Forward? */
491 if (n >= 0) {
492
493 while (n > 0) {
494 #if DBCS
495 /* never start forward on a 2 byte char */
496 if (curwp->w_doto > 0 && is2byte(curwp->w_dotp->l_text,
497 &curwp->w_dotp->l_text[curwp->w_doto - 1])) {
498 curwp->w_doto--;
499 n++;
500 }
501 #endif
502 /* record the current point */
503 dotp = curwp->w_dotp;
504 doto = curwp->w_doto;
505
506 /* can't delete past the end of the buffer */
507 if (dotp == curbp->b_linep)
508 return(FALSE);
509
510 /* find out how many chars to delete on this line */
511 chunk = dotp->l_used-doto; /* Size of chunk. */
512 if (chunk > n)
513 chunk = n;
514
515 /* if at the end of a line, merge with the next */
516 if (chunk == 0) {
517
518 /* flag that we are making a hard change */
519 lchange(WFHARD);
520 if (ldelnewline() == FALSE ||
521 (kflag != FALSE &&
522 kinsert(FORWARD, '\r')==FALSE))
523 return(FALSE);
524 --n;
525 continue;
526 }
527
528 /* flag the fact we are changing the current line */
529 lchange(WFEDIT);
530
531 /* find the limits of the kill */
532 cp1 = &dotp->l_text[doto];
533 cp2 = cp1 + chunk;
534 #if DBCS
535 /* never leave half a character */
536 if (is2byte(dotp->l_text, cp2 - 1)) {
537 ++chunk;
538 ++cp2;
539 ++n;
540 }
541 #endif
542
543 /* save deleted characters for an undo... */
544 if (undoflag == TRUE) {
545 obj.obj_sptr = cp1;
546 undo_insert(OP_DSTR, (long)chunk, obj);
547 }
548
549 /* save the text to the kill buffer */
550 if (kflag != FALSE) {
551 while (cp1 != cp2) {
552 if (kinsert(FORWARD, *cp1) == FALSE)
553 return(FALSE);
554 ++cp1;
555 }
556 cp1 = &dotp->l_text[doto];
557 }
558
559 /* copy what is left of the line upward */
560 while (cp2 != &dotp->l_text[dotp->l_used])
561 *cp1++ = *cp2++;
562 dotp->l_used -= chunk;
563
564 /* fix any other windows with the same text displayed */
565 wp = wheadp;
566 while (wp != NULL) {
567
568 /* reset the dot if needed */
569 if (wp->w_dotp==dotp && wp->w_doto>=doto) {
570 wp->w_doto -= chunk;
571 if (wp->w_doto < doto)
572 wp->w_doto = doto;
573 }
574
575 /* reset any marks if needed */
576 for (cmark = 0; cmark < NMARKS; cmark++) {
577 if (wp->w_markp[cmark]==dotp && wp->w_marko[cmark]>=doto) {
578 wp->w_marko[cmark] -= chunk;
579 if (wp->w_marko[cmark] < doto)
580 wp->w_marko[cmark] = doto;
581 }
582 }
583
584 /* onward to the next window */
585 wp = wp->w_wndp;
586 }
587
588 /* indicate we have deleted chunk characters */
589 n -= chunk;
590 }
591 } else {
592 while (n < 0) {
593 #if DBCS
594 /* never start backwards on the
595 1st of a 2 byte character */
596 if (curwp->w_doto > 1 && is2byte(curwp->w_dotp->l_text,
597 &curwp->w_dotp->l_text[curwp->w_doto-1])) {
598 curwp->w_doto++;
599 n--;
600 }
601 #endif
602 /* record the current point */
603 dotp = curwp->w_dotp;
604 doto = curwp->w_doto;
605
606 /* can't delete past the beginning of the buffer */
607 if (dotp == lforw(curbp->b_linep) && (doto == 0))
608 return(FALSE);
609
610 /* find out how many chars to delete on this line */
611 chunk = doto; /* Size of chunk. */
612 if (chunk > -n)
613 chunk = -n;
614
615 /* if at the beginning of a line, merge with the last */
616 if (chunk == 0) {
617
618 /* flag that we are making a hard change */
619 lchange(WFHARD);
620 backchar(TRUE, 1);
621 if (ldelnewline() == FALSE ||
622 (kflag != FALSE &&
623 kinsert(REVERSE, '\r')==FALSE))
624 return(FALSE);
625 ++n;
626 continue;
627 }
628
629 /* flag the fact we are changing the current line */
630 lchange(WFEDIT);
631
632 /* find the limits of the kill */
633 cp1 = &dotp->l_text[doto];
634 cp2 = cp1 - chunk;
635 #if DBCS
636 if (is2byte(dotp->l_text, cp2 - 1)) {
637 ++chunk;
638 --cp2;
639 ++n;
640 }
641 #endif
642
643 /* save deleted characters for an undo... */
644 if (undoflag == TRUE) {
645 curwp->w_doto -= chunk;
646 obj.obj_sptr = cp2;
647 undo_insert(OP_DSTR, (long)chunk, obj);
648 curwp->w_doto += chunk;
649 }
650
651 /* save the text to the kill buffer */
652 if (kflag != FALSE) {
653 while (cp1 > cp2) {
654 if (kinsert(REVERSE, *(--cp1)) == FALSE)
655 return(FALSE);
656 }
657 cp1 = &dotp->l_text[doto];
658 }
659
660 /* copy what is left of the line downward */
661 while (cp1 != &dotp->l_text[dotp->l_used])
662 *cp2++ = *cp1++;
663 dotp->l_used -= chunk;
664 curwp->w_doto -= chunk;
665
666 /* fix any other windows with the same text displayed */
667 wp = wheadp;
668 while (wp != NULL) {
669
670 /* reset the dot if needed */
671 if (wp->w_dotp==dotp && wp->w_doto>=doto) {
672 wp->w_doto -= chunk;
673 if (wp->w_doto < doto)
674 wp->w_doto = doto;
675 }
676
677 /* reset any marks if needed */
678 for (cmark = 0; cmark < NMARKS; cmark++) {
679 if (wp->w_markp[cmark]==dotp && wp->w_marko[cmark]>=doto) {
680 wp->w_marko[cmark] -= chunk;
681 if (wp->w_marko[cmark] < doto)
682 wp->w_marko[cmark] = doto;
683 }
684 }
685
686 /* onward to the next window */
687 wp = wp->w_wndp;
688 }
689
690 /* indicate we have deleted chunk characters */
691 n += chunk;
692 }
693 }
694 return(TRUE);
695 }
696
697 /* getctext: grab and return a string with the text of
698 the current line
699 */
700
701 #if PROTO
getctext(char * rline)702 char *PASCAL NEAR getctext(char *rline)
703 #else
704 char *PASCAL NEAR getctext( rline)
705 char *rline;
706 #endif
707
708 {
709 register LINE *lp; /* line to copy */
710 register int size; /* length of line to return */
711 register char *sp; /* string pointer into line */
712 register char *dp; /* string pointer into returned line */
713
714 /* find the contents of the current line and its length */
715 lp = curwp->w_dotp;
716 sp = ltext(lp);
717 size = lused(lp);
718 if (size >= NSTRING)
719 size = NSTRING - 1;
720
721 /* copy it across */
722 dp = rline;
723 while (size--)
724 *dp++ = *sp++;
725 *dp = 0;
726 return(rline);
727 }
728
729 /* putctext: replace the current line with the passed in text */
730
putctext(iline)731 PASCAL NEAR putctext(iline)
732
733 char *iline; /* contents of new line */
734
735 {
736 register int status;
737
738 /* delete the current line */
739 curwp->w_doto = 0; /* starting at the beginning of the line */
740 if ((status = killtext(TRUE, 1)) != TRUE)
741 return(status);
742
743 /* insert the new line */
744 if ((status = linstr(iline)) != TRUE)
745 return(status);
746 status = lnewline();
747 backline(TRUE, 1);
748 return(status);
749 }
750
751 /*
752 * Delete a newline. Join the current line with the next line. If the next line
753 * is the magic header line always return TRUE; merging the last line with the
754 * header line can be thought of as always being a successful operation, even
755 * if nothing is done, and this makes the kill buffer work "right". Easy cases
756 * can be done by shuffling data around. Hard cases require that lines be moved
757 * about in memory. Return FALSE on error and TRUE if all looks ok. Called by
758 * "ldelete" only.
759 */
ldelnewline()760 int PASCAL NEAR ldelnewline()
761 {
762 register char *cp1;
763 register char *cp2;
764 register LINE *lp1;
765 register LINE *lp2;
766 register LINE *lp3;
767 register EWINDOW *wp;
768 SCREEN *scrp; /* screen to fix pointers in */
769 int cmark; /* current mark */
770
771 if (curbp->b_mode&MDVIEW) /* don't allow this command if */
772 return(rdonly()); /* we are in read only mode */
773
774 /* remember we did this! */
775 obj.obj_char = 13;
776 undo_insert(OP_DELC, 1L, obj);
777
778 lp1 = curwp->w_dotp;
779 lp2 = lp1->l_fp;
780 if (lp2 == curbp->b_linep) { /* At the buffer end. */
781 if (lp1->l_used == 0) /* Blank line. */
782 lfree(lp1);
783 return(TRUE);
784 }
785 if (lp2->l_used <= lp1->l_size-lp1->l_used) {
786 cp1 = &lp1->l_text[lp1->l_used];
787 cp2 = &lp2->l_text[0];
788 while (cp2 != &lp2->l_text[lp2->l_used])
789 *cp1++ = *cp2++;
790
791 /* in all screens.... */
792 scrp = first_screen;
793 while (scrp) {
794
795 wp = scrp->s_first_window;
796 while (wp != NULL) {
797 if (wp->w_linep == lp2)
798 wp->w_linep = lp1;
799 if (wp->w_dotp == lp2) {
800 wp->w_dotp = lp1;
801 wp->w_doto += lp1->l_used;
802 }
803 for (cmark = 0; cmark < NMARKS; cmark++) {
804 if (wp->w_markp[cmark] == lp2) {
805 wp->w_markp[cmark] = lp1;
806 wp->w_marko[cmark] += lp1->l_used;
807 }
808 }
809 wp = wp->w_wndp;
810 }
811
812 /* next screen! */
813 scrp = scrp->s_next_screen;
814 }
815
816 lp1->l_used += lp2->l_used;
817 lp1->l_fp = lp2->l_fp;
818 lp2->l_fp->l_bp = lp1;
819 free((char *) lp2);
820 return(TRUE);
821 }
822 if ((lp3=lalloc(lp1->l_used+lp2->l_used)) == NULL)
823 return(FALSE);
824 cp1 = &lp1->l_text[0];
825 cp2 = &lp3->l_text[0];
826 while (cp1 != &lp1->l_text[lp1->l_used])
827 *cp2++ = *cp1++;
828 cp1 = &lp2->l_text[0];
829 while (cp1 != &lp2->l_text[lp2->l_used])
830 *cp2++ = *cp1++;
831 lp1->l_bp->l_fp = lp3;
832 lp3->l_fp = lp2->l_fp;
833 lp2->l_fp->l_bp = lp3;
834 lp3->l_bp = lp1->l_bp;
835
836 /* in all screens.... */
837 scrp = first_screen;
838 while (scrp) {
839
840 wp = scrp->s_first_window;
841 while (wp != NULL) {
842 if (wp->w_linep==lp1 || wp->w_linep==lp2)
843 wp->w_linep = lp3;
844 if (wp->w_dotp == lp1)
845 wp->w_dotp = lp3;
846 else if (wp->w_dotp == lp2) {
847 wp->w_dotp = lp3;
848 wp->w_doto += lp1->l_used;
849 }
850 for (cmark = 0; cmark < NMARKS; cmark++) {
851 if (wp->w_markp[cmark] == lp1)
852 wp->w_markp[cmark] = lp3;
853 else if (wp->w_markp[cmark] == lp2) {
854 wp->w_markp[cmark] = lp3;
855 wp->w_marko[cmark] += lp1->l_used;
856 }
857 }
858 wp = wp->w_wndp;
859 }
860
861 /* next screen! */
862 scrp = scrp->s_next_screen;
863 }
864
865 free((char *) lp1);
866 free((char *) lp2);
867 return(TRUE);
868 }
869
870 /* Add a new line to the end of the indicated buffer.
871 return FALSE if we run out of memory
872 note that this works on non-displayed buffers as well!
873 */
874
875 #if PROTO
addline(BUFFER * bp,char * text)876 int PASCAL NEAR addline(BUFFER *bp, char *text)
877 #else
878 int PASCAL NEAR addline(bp, text)
879
880 BUFFER *bp; /* buffer to add text to */
881 char *text; /* line to add */
882 #endif
883 {
884 register LINE *lp;
885 register int i;
886 register int ntext;
887
888 /* allocate the memory to hold the line */
889 ntext = strlen(text);
890 if ((lp=lalloc(ntext)) == NULL)
891 return(FALSE);
892
893 /* copy the text into the new line */
894 for (i=0; i<ntext; ++i)
895 lputc(lp, i, text[i]);
896
897 /* add the new line to the end of the buffer */
898 bp->b_linep->l_bp->l_fp = lp;
899 lp->l_bp = bp->b_linep->l_bp;
900 bp->b_linep->l_bp = lp;
901 lp->l_fp = bp->b_linep;
902
903 /* if the point was at the end of the buffer,
904 move it to the beginning of the new line */
905 if (bp->b_dotp == bp->b_linep)
906 bp->b_dotp = lp;
907 return(TRUE);
908 }
909
910 /*
911 * Delete all of the text saved in the kill buffer. Called by commands when a
912 * new kill context is being created. The kill buffer array is released, just
913 * in case the buffer has grown to immense size. No errors.
914 */
915
kdelete()916 VOID PASCAL NEAR kdelete()
917
918 {
919 KILL *kp; /* ptr to scan kill buffer chunk list */
920
921 if (kbufh[kill_index] != NULL) {
922
923 /* first, delete all the chunks */
924 kbufp[kill_index] = kbufh[kill_index];
925 while (kbufp[kill_index] != NULL) {
926 kp = kbufp[kill_index]->d_next;
927 free((char *)kbufp[kill_index]);
928 kbufp[kill_index] = kp;
929 #if WINDOW_MSWIN
930 {
931 static int o = 0;
932 if (--o < 0) {
933 longop(TRUE);
934 o = 10; /* to lower overhead,
935 only 10% calls to longop */
936 }
937 }
938 #endif
939 }
940
941 /* and reset all the kill buffer pointers */
942 kbufh[kill_index] = kbufp[kill_index] = NULL;
943 kskip[kill_index] = 0;
944 kused[kill_index] = KBLOCK;
945 }
946 }
947
948 /* next_kill: advance to the next position in the kill ring,
949 pushing the current kill buffer and clearing
950 what will be the new kill buffer
951 */
952
next_kill()953 VOID PASCAL NEAR next_kill()
954
955 {
956 /* advance to the next kill ring entry */
957 kill_index++;
958 if (kill_index == NRING)
959 kill_index = 0;
960
961 /* and clear it, so it is ready for use */
962 kdelete();
963 }
964
965 /*
966 * Insert a character to the kill buffer, allocating new chunks as needed.
967 * Return TRUE if all is well, and FALSE on errors.
968 */
969
970 #if PROTO
kinsert(int direct,char c)971 int PASCAL NEAR kinsert(int direct, char c)
972 #else
973 int PASCAL NEAR kinsert(direct, c)
974
975 int direct; /* direction (FORWARD/REVERSE) to insert characters */
976 char c; /* character to insert in the kill buffer */
977 #endif
978
979 {
980 KILL *nchunk; /* ptr to newly roomed chunk */
981
982 if (direct == FORWARD) {
983
984 /* check to see if we need a new chunk */
985 if (kused[kill_index] >= KBLOCK) {
986 if ((nchunk = (KILL *)room(sizeof(KILL))) == NULL) {
987 mlwrite(TEXT94);
988 /* "%%Out of memory" */
989 return(FALSE);
990 }
991 if (kbufh[kill_index] == NULL) /* set head ptr if first time */
992 kbufh[kill_index] = nchunk;
993 if (kbufp[kill_index] != NULL) /* point the current to this new one */
994 kbufp[kill_index]->d_next = nchunk;
995 kbufp[kill_index] = nchunk;
996 kbufp[kill_index]->d_next = NULL;
997 kused[kill_index] = 0;
998 #if WINDOW_MSWIN
999 {
1000 static int o = 0;
1001 if (--o < 0) {
1002 longop(TRUE);
1003 o = 10; /* to lower overhead,
1004 only 10% calls to longop */
1005 }
1006 }
1007 #endif
1008 }
1009
1010 /* and now insert the character */
1011 kbufp[kill_index]->d_chunk[kused[kill_index]++] = c;
1012 } else {
1013 /* REVERSE */
1014 /* check to see if we need a new chunk */
1015 if (kskip[kill_index] == 0) {
1016 if ((nchunk = (KILL *)room(sizeof(KILL))) == NULL) {
1017 mlwrite(TEXT94);
1018 /* "%%Out of memory" */
1019 return(FALSE);
1020 }
1021 if (kbufh[kill_index] == NULL) { /* set head ptr if first time */
1022 kbufh[kill_index] = nchunk;
1023 kbufp[kill_index] = nchunk;
1024 kskip[kill_index] = KBLOCK;
1025 kused[kill_index] = KBLOCK;
1026 nchunk->d_next = (KILL *)NULL;
1027 } else {
1028 nchunk->d_next = kbufh[kill_index];
1029 kbufh[kill_index] = nchunk;
1030 kskip[kill_index] = KBLOCK;
1031 }
1032 #if WINDOW_MSWIN
1033 {
1034 static int o = 0;
1035 if (--o < 0) {
1036 longop(TRUE);
1037 o = 10; /* to lower overhead,
1038 only 10% calls to longop */
1039 }
1040 }
1041 #endif
1042 }
1043
1044 /* and now insert the character */
1045 kbufh[kill_index]->d_chunk[--kskip[kill_index]] = c;
1046 }
1047 return(TRUE);
1048 }
1049
1050 /*
1051 * Yank text back from the kill buffer. This is really easy. All of the work
1052 * is done by the standard insert routines. All you do is run the loop, and
1053 * check for errors. Bound to "C-Y".
1054 */
1055
1056 #define Char_insert(a) (a == '\r' ? lnewline() : linsert(1, a))
1057
yank(f,n)1058 int PASCAL NEAR yank(f, n)
1059
1060 int f,n; /* prefix flag and argument */
1061
1062 {
1063 register int counter; /* counter into kill buffer data */
1064 register char *sp; /* pointer into string to insert */
1065 short int curoff; /* storage for line before yanking */
1066 LINE *curline;
1067 KILL *kptr; /* pointer into kill buffer */
1068
1069 if (curbp->b_mode&MDVIEW) /* don't allow this command if */
1070 return(rdonly()); /* we are in read only mode */
1071 if (n < 0)
1072 return(FALSE);
1073
1074 /* make sure there is something to yank */
1075 if (kbufh[kill_index] == NULL) {
1076 last_size = 0L;
1077 return(TRUE); /* not an error, just nothing */
1078 }
1079
1080 /*
1081 * Save the local pointers to hold global ".".
1082 */
1083 if (yankflag) {
1084 /* Find the *previous* line, since the line we are on
1085 * may disappear due to re-allocation. This works even
1086 * if we are on the first line of the file.
1087 */
1088 curline = lback(curwp->w_dotp);
1089 curoff = curwp->w_doto;
1090 }
1091
1092 /* for each time.... */
1093 while (n--) {
1094 last_size = 0L;
1095 if (kskip[kill_index] > 0) {
1096 kptr = kbufh[kill_index];
1097 sp = &(kptr->d_chunk[kskip[kill_index]]);
1098 counter = kskip[kill_index];
1099 while (counter++ < KBLOCK) {
1100 Char_insert(*sp);
1101 last_size++;
1102 ++sp;
1103 }
1104 kptr = kptr->d_next;
1105 } else {
1106 kptr = kbufh[kill_index];
1107 }
1108
1109 if (kptr != (KILL *)NULL) {
1110 while (kptr != kbufp[kill_index]) {
1111 sp = kptr->d_chunk;
1112 for(counter = 0; counter < KBLOCK; counter++) {
1113 Char_insert(*sp);
1114 last_size++;
1115 ++sp;
1116 }
1117 kptr = kptr->d_next;
1118 }
1119 counter = kused[kill_index];
1120 sp = kptr->d_chunk;
1121 while (counter--) {
1122 Char_insert(*sp);
1123 last_size++;
1124 ++sp;
1125 }
1126 }
1127 }
1128
1129 /* If requested, set global "." back to the
1130 * beginning of the yanked text.
1131 */
1132 if (yankflag) {
1133 curwp->w_dotp = lforw(curline);
1134 curwp->w_doto = curoff;
1135 }
1136 thisflag |= CFYANK;
1137 return(TRUE);
1138 }
1139
cycle_ring(f,n)1140 int PASCAL NEAR cycle_ring(f, n)
1141
1142 int f,n; /* prefix flag and argument */
1143
1144 {
1145 register int orig_index; /* original kill_index */
1146
1147 /* if there is an argument, cycle the kill index */
1148 if (f) {
1149 while (n) {
1150 orig_index = kill_index;
1151 do {
1152 kill_index--;
1153 if (kill_index < 0)
1154 kill_index = NRING - 1;
1155 } while ((orig_index != kill_index) &&
1156 (kbufh[kill_index] == (KILL *)NULL));
1157 n--;
1158 }
1159 }
1160 return TRUE;
1161 }
1162
yank_pop(f,n)1163 int PASCAL NEAR yank_pop(f, n)
1164
1165 int f,n; /* prefix flag and argument */
1166
1167 {
1168 /* defaulted non first call will cycle by 1 */
1169 if ((lastflag & CFYANK) && (f == FALSE)) {
1170 f = TRUE;
1171 n = 1;
1172 }
1173
1174 /* cycle the kill ring appropriately */
1175 cycle_ring(f, n);
1176
1177 /* if not the first consectutive time, delete the last yank */
1178 if ((lastflag & CFYANK))
1179 ldelete(-last_size, FALSE);
1180
1181 /* and insert the current kill buffer */
1182 return(yank(FALSE, 1));
1183 }
1184
clear_ring(f,n)1185 int PASCAL NEAR clear_ring(f, n)
1186
1187 int f,n; /* prefix flag and argument */
1188
1189 {
1190 register int index;
1191
1192 for (index = 0; index < NRING; index++)
1193 next_kill();
1194 mlwrite(TEXT228);
1195 /* "[Kill ring cleared]" */
1196 return(TRUE);
1197 }
1198
1199 #if 0
1200 dispkill()
1201
1202 {
1203 KILL *kptr;
1204 int index;
1205 char *sp;
1206 int counter;
1207
1208 if (kbufh[kill_index] == (KILL *)NULL) {
1209 printf("<EMPTY>\n");
1210 return;
1211 }
1212
1213 index = 1;
1214 if (kskip[kill_index] > 0) {
1215 kptr = kbufh[kill_index];
1216 printf("kskip[kill_index] = %d\nBLOCK %d <", kskip[kill_index], index++);
1217 sp = &(kptr->d_chunk[kskip[kill_index]]);
1218 counter = kskip[kill_index];
1219 while (counter++ < KBLOCK) {
1220 putchar(*sp++);
1221 }
1222 printf(">\n");
1223 kptr = kptr->d_next;
1224 } else {
1225 kptr = kbufh[kill_index];
1226 }
1227
1228 if (kptr != (KILL *)NULL) {
1229 while (kptr != kbufp[kill_index]) {
1230 printf("BLOCK %d <%255s>\n", index++, kptr->d_chunk);
1231 kptr = kptr->d_next;
1232 }
1233 printf("BLOCK %d <", index++);
1234 counter = kused[kill_index];
1235 sp = kptr->d_chunk;
1236 while (counter--) {
1237 putchar(*sp++);
1238 }
1239 printf(">\nkused[kill_index] = %d\n", kused[kill_index]);
1240 }
1241
1242 }
1243 #endif
1244