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