119304Speter/*-
219304Speter * Copyright (c) 1992, 1993, 1994
319304Speter *	The Regents of the University of California.  All rights reserved.
419304Speter * Copyright (c) 1992, 1993, 1994, 1995, 1996
519304Speter *	Keith Bostic.  All rights reserved.
619304Speter *
719304Speter * See the LICENSE file for redistribution information.
819304Speter */
919304Speter
1019304Speter#include "config.h"
1119304Speter
1219304Speter#ifndef lint
13254225Speterstatic const char sccsid[] = "$Id: put.c,v 10.19 04/07/11 17:00:24 zy Exp $";
1419304Speter#endif /* not lint */
1519304Speter
1619304Speter#include <sys/types.h>
1719304Speter#include <sys/queue.h>
18254225Speter#include <sys/time.h>
1919304Speter
2019304Speter#include <bitstring.h>
2119304Speter#include <ctype.h>
2219304Speter#include <limits.h>
2319304Speter#include <stdio.h>
2419304Speter#include <stdlib.h>
2519304Speter#include <string.h>
2619304Speter
2719304Speter#include "common.h"
2819304Speter
2919304Speter/*
3019304Speter * put --
3119304Speter *	Put text buffer contents into the file.
3219304Speter *
3319304Speter * PUBLIC: int put __P((SCR *, CB *, CHAR_T *, MARK *, MARK *, int));
3419304Speter */
3519304Speterint
36254225Speterput(
37254225Speter	SCR *sp,
38254225Speter	CB *cbp,
39254225Speter	CHAR_T *namep,
40254225Speter	MARK *cp,
41254225Speter	MARK *rp,
42254225Speter	int append)
4319304Speter{
4419304Speter	CHAR_T name;
4519304Speter	TEXT *ltp, *tp;
4619304Speter	recno_t lno;
4719304Speter	size_t blen, clen, len;
4819304Speter	int rval;
49254225Speter	CHAR_T *bp, *t;
50254225Speter	CHAR_T *p;
5119304Speter
5219304Speter	if (cbp == NULL)
5319304Speter		if (namep == NULL) {
5419304Speter			cbp = sp->gp->dcbp;
5519304Speter			if (cbp == NULL) {
5619304Speter				msgq(sp, M_ERR,
5719304Speter				    "053|The default buffer is empty");
5819304Speter				return (1);
5919304Speter			}
6019304Speter		} else {
6119304Speter			name = *namep;
6219304Speter			CBNAME(sp, cbp, name);
6319304Speter			if (cbp == NULL) {
6419304Speter				msgq(sp, M_ERR, "054|Buffer %s is empty",
6519304Speter				    KEY_NAME(sp, name));
6619304Speter				return (1);
6719304Speter			}
6819304Speter		}
69254225Speter	tp = TAILQ_FIRST(cbp->textq);
7019304Speter
7119304Speter	/*
7219304Speter	 * It's possible to do a put into an empty file, meaning that the cut
7319304Speter	 * buffer simply becomes the file.  It's a special case so that we can
7419304Speter	 * ignore it in general.
7519304Speter	 *
7619304Speter	 * !!!
7719304Speter	 * Historically, pasting into a file with no lines in vi would preserve
7819304Speter	 * the single blank line.  This is surely a result of the fact that the
7919304Speter	 * historic vi couldn't deal with a file that had no lines in it.  This
8019304Speter	 * implementation treats that as a bug, and does not retain the blank
8119304Speter	 * line.
8219304Speter	 *
8319304Speter	 * Historical practice is that the cursor ends at the first character
8419304Speter	 * in the file.
8519304Speter	 */
8619304Speter	if (cp->lno == 1) {
8719304Speter		if (db_last(sp, &lno))
8819304Speter			return (1);
8919304Speter		if (lno == 0) {
90254225Speter			for (; tp != NULL;
91254225Speter			    ++lno, ++sp->rptlines[L_ADDED], tp = TAILQ_NEXT(tp, q))
9219304Speter				if (db_append(sp, 1, lno, tp->lb, tp->len))
9319304Speter					return (1);
9419304Speter			rp->lno = 1;
9519304Speter			rp->cno = 0;
9619304Speter			return (0);
9719304Speter		}
9819304Speter	}
9919304Speter
10019304Speter	/* If a line mode buffer, append each new line into the file. */
10119304Speter	if (F_ISSET(cbp, CB_LMODE)) {
10219304Speter		lno = append ? cp->lno : cp->lno - 1;
10319304Speter		rp->lno = lno + 1;
104254225Speter		for (; tp != NULL;
105254225Speter		    ++lno, ++sp->rptlines[L_ADDED], tp = TAILQ_NEXT(tp, q))
10619304Speter			if (db_append(sp, 1, lno, tp->lb, tp->len))
10719304Speter				return (1);
10819304Speter		rp->cno = 0;
10919304Speter		(void)nonblank(sp, rp->lno, &rp->cno);
11019304Speter		return (0);
11119304Speter	}
11219304Speter
11319304Speter	/*
11419304Speter	 * If buffer was cut in character mode, replace the current line with
11519304Speter	 * one built from the portion of the first line to the left of the
11619304Speter	 * split plus the first line in the CB.  Append each intermediate line
11719304Speter	 * in the CB.  Append a line built from the portion of the first line
11819304Speter	 * to the right of the split plus the last line in the CB.
11919304Speter	 *
12019304Speter	 * Get the first line.
12119304Speter	 */
12219304Speter	lno = cp->lno;
12319304Speter	if (db_get(sp, lno, DBG_FATAL, &p, &len))
12419304Speter		return (1);
12519304Speter
126254225Speter	GET_SPACE_RETW(sp, bp, blen, tp->len + len + 1);
12719304Speter	t = bp;
12819304Speter
12919304Speter	/* Original line, left of the split. */
13019304Speter	if (len > 0 && (clen = cp->cno + (append ? 1 : 0)) > 0) {
131254225Speter		MEMCPY(bp, p, clen);
13219304Speter		p += clen;
13319304Speter		t += clen;
13419304Speter	}
13519304Speter
13619304Speter	/* First line from the CB. */
13719304Speter	if (tp->len != 0) {
138254225Speter		MEMCPY(t, tp->lb, tp->len);
13919304Speter		t += tp->len;
14019304Speter	}
14119304Speter
14219304Speter	/* Calculate length left in the original line. */
14319304Speter	clen = len == 0 ? 0 : len - (cp->cno + (append ? 1 : 0));
14419304Speter
14519304Speter	/*
14619304Speter	 * !!!
14719304Speter	 * In the historical 4BSD version of vi, character mode puts within
14819304Speter	 * a single line have two cursor behaviors: if the put is from the
14919304Speter	 * unnamed buffer, the cursor moves to the character inserted which
15019304Speter	 * appears last in the file.  If the put is from a named buffer,
15119304Speter	 * the cursor moves to the character inserted which appears first
15219304Speter	 * in the file.  In System III/V, it was changed at some point and
15319304Speter	 * the cursor always moves to the first character.  In both versions
15419304Speter	 * of vi, character mode puts that cross line boundaries leave the
15519304Speter	 * cursor on the first character.  Nvi implements the System III/V
15619304Speter	 * behavior, and expect POSIX.2 to do so as well.
15719304Speter	 */
15819304Speter	rp->lno = lno;
15919304Speter	rp->cno = len == 0 ? 0 : sp->cno + (append && tp->len ? 1 : 0);
16019304Speter
16119304Speter	/*
16219304Speter	 * If no more lines in the CB, append the rest of the original
16319304Speter	 * line and quit.  Otherwise, build the last line before doing
16419304Speter	 * the intermediate lines, because the line changes will lose
16519304Speter	 * the cached line.
16619304Speter	 */
167254225Speter	if (TAILQ_NEXT(tp, q) == NULL) {
16819304Speter		if (clen > 0) {
169254225Speter			MEMCPY(t, p, clen);
17019304Speter			t += clen;
17119304Speter		}
17219304Speter		if (db_set(sp, lno, bp, t - bp))
17319304Speter			goto err;
17419304Speter		if (sp->rptlchange != lno) {
17519304Speter			sp->rptlchange = lno;
17619304Speter			++sp->rptlines[L_CHANGED];
17719304Speter		}
17819304Speter	} else {
17919304Speter		/*
18019304Speter		 * Have to build both the first and last lines of the
18119304Speter		 * put before doing any sets or we'll lose the cached
18219304Speter		 * line.  Build both the first and last lines in the
18319304Speter		 * same buffer, so we don't have to have another buffer
18419304Speter		 * floating around.
18519304Speter		 *
18619304Speter		 * Last part of original line; check for space, reset
18719304Speter		 * the pointer into the buffer.
18819304Speter		 */
189254225Speter		ltp = TAILQ_LAST(cbp->textq, _texth);
19019304Speter		len = t - bp;
191254225Speter		ADD_SPACE_RETW(sp, bp, blen, ltp->len + clen);
19219304Speter		t = bp + len;
19319304Speter
19419304Speter		/* Add in last part of the CB. */
195254225Speter		MEMCPY(t, ltp->lb, ltp->len);
19619304Speter		if (clen)
197254225Speter			MEMCPY(t + ltp->len, p, clen);
19819304Speter		clen += ltp->len;
19919304Speter
20019304Speter		/*
20119304Speter		 * Now: bp points to the first character of the first
20219304Speter		 * line, t points to the last character of the last
20319304Speter		 * line, t - bp is the length of the first line, and
20419304Speter		 * clen is the length of the last.  Just figured you'd
20519304Speter		 * want to know.
20619304Speter		 *
20719304Speter		 * Output the line replacing the original line.
20819304Speter		 */
20919304Speter		if (db_set(sp, lno, bp, t - bp))
21019304Speter			goto err;
21119304Speter		if (sp->rptlchange != lno) {
21219304Speter			sp->rptlchange = lno;
21319304Speter			++sp->rptlines[L_CHANGED];
21419304Speter		}
21519304Speter
21619304Speter		/* Output any intermediate lines in the CB. */
217254225Speter		for (tp = TAILQ_NEXT(tp, q); TAILQ_NEXT(tp, q) != NULL;
218254225Speter		    ++lno, ++sp->rptlines[L_ADDED], tp = TAILQ_NEXT(tp, q))
21919304Speter			if (db_append(sp, 1, lno, tp->lb, tp->len))
22019304Speter				goto err;
22119304Speter
22219304Speter		if (db_append(sp, 1, lno, t, clen))
22319304Speter			goto err;
22419304Speter		++sp->rptlines[L_ADDED];
22519304Speter	}
22619304Speter	rval = 0;
22719304Speter
22819304Speter	if (0)
22919304Spetererr:		rval = 1;
23019304Speter
231254225Speter	FREE_SPACEW(sp, bp, blen);
23219304Speter	return (rval);
23319304Speter}
234