11022Sache/*-
21533Sjoerg * Copyright (c) 1992, 1993, 1994
31022Sache *	The Regents of the University of California.  All rights reserved.
41022Sache * Copyright (c) 1992, 1993, 1994, 1995, 1996
51022Sache *	Keith Bostic.  All rights reserved.
61022Sache *
71022Sache * See the LICENSE file for redistribution information.
81022Sache */
91022Sache
101022Sache#include "config.h"
111022Sache
121022Sache#ifndef lint
131022Sachestatic const char sccsid[] = "$Id: put.c,v 10.19 04/07/11 17:00:24 zy Exp $";
141533Sjoerg#endif /* not lint */
151533Sjoerg
161533Sjoerg#include <sys/types.h>
171533Sjoerg#include <sys/queue.h>
181533Sjoerg#include <sys/time.h>
191533Sjoerg
201533Sjoerg#include <bitstring.h>
211533Sjoerg#include <ctype.h>
221533Sjoerg#include <limits.h>
231533Sjoerg#include <stdio.h>
241533Sjoerg#include <stdlib.h>
2555541Skato#include <string.h>
2655541Skato
271022Sache#include "common.h"
281022Sache
291022Sache/*
301022Sache * put --
311022Sache *	Put text buffer contents into the file.
328857Srgrimes *
331022Sache * PUBLIC: int put __P((SCR *, CB *, CHAR_T *, MARK *, MARK *, int));
341022Sache */
351022Sacheint
361022Sacheput(
371022Sache	SCR *sp,
381022Sache	CB *cbp,
391022Sache	CHAR_T *namep,
401022Sache	MARK *cp,
411022Sache	MARK *rp,
421022Sache	int append)
4329531Scharnier{
4429531Scharnier	CHAR_T name;
4529531Scharnier	TEXT *ltp, *tp;
461022Sache	recno_t lno;
471022Sache	size_t blen, clen, len;
4829531Scharnier	int rval;
491022Sache	CHAR_T *bp, *t;
501022Sache	CHAR_T *p;
511022Sache
521022Sache	if (cbp == NULL)
531022Sache		if (namep == NULL) {
541022Sache			cbp = sp->gp->dcbp;
551137Sache			if (cbp == NULL) {
561022Sache				msgq(sp, M_ERR,
571022Sache				    "053|The default buffer is empty");
581137Sache				return (1);
591138Sache			}
601022Sache		} else {
611137Sache			name = *namep;
621137Sache			CBNAME(sp, cbp, name);
631137Sache			if (cbp == NULL) {
641137Sache				msgq(sp, M_ERR, "054|Buffer %s is empty",
651137Sache				    KEY_NAME(sp, name));
661137Sache				return (1);
671137Sache			}
681022Sache		}
691022Sache	tp = TAILQ_FIRST(cbp->textq);
701022Sache
711022Sache	/*
721022Sache	 * It's possible to do a put into an empty file, meaning that the cut
731022Sache	 * buffer simply becomes the file.  It's a special case so that we can
741022Sache	 * ignore it in general.
751022Sache	 *
761022Sache	 * !!!
771022Sache	 * Historically, pasting into a file with no lines in vi would preserve
781022Sache	 * the single blank line.  This is surely a result of the fact that the
791022Sache	 * historic vi couldn't deal with a file that had no lines in it.  This
801137Sache	 * implementation treats that as a bug, and does not retain the blank
811022Sache	 * line.
821022Sache	 *
8329531Scharnier	 * Historical practice is that the cursor ends at the first character
8429531Scharnier	 * in the file.
851022Sache	 */
861022Sache	if (cp->lno == 1) {
871022Sache		if (db_last(sp, &lno))
881022Sache			return (1);
891022Sache		if (lno == 0) {
901022Sache			for (; tp != NULL;
911022Sache			    ++lno, ++sp->rptlines[L_ADDED], tp = TAILQ_NEXT(tp, q))
921533Sjoerg				if (db_append(sp, 1, lno, tp->lb, tp->len))
931022Sache					return (1);
941533Sjoerg			rp->lno = 1;
9529531Scharnier			rp->cno = 0;
961533Sjoerg			return (0);
971533Sjoerg		}
981533Sjoerg	}
991533Sjoerg
1001533Sjoerg	/* If a line mode buffer, append each new line into the file. */
1018857Srgrimes	if (F_ISSET(cbp, CB_LMODE)) {
1021022Sache		lno = append ? cp->lno : cp->lno - 1;
1031022Sache		rp->lno = lno + 1;
1041022Sache		for (; tp != NULL;
1051022Sache		    ++lno, ++sp->rptlines[L_ADDED], tp = TAILQ_NEXT(tp, q))
1061022Sache			if (db_append(sp, 1, lno, tp->lb, tp->len))
1071022Sache				return (1);
1081022Sache		rp->cno = 0;
1091022Sache		(void)nonblank(sp, rp->lno, &rp->cno);
11029531Scharnier		return (0);
11129531Scharnier	}
1121022Sache
1131533Sjoerg	/*
1141533Sjoerg	 * If buffer was cut in character mode, replace the current line with
1151533Sjoerg	 * one built from the portion of the first line to the left of the
1161533Sjoerg	 * split plus the first line in the CB.  Append each intermediate line
1171533Sjoerg	 * in the CB.  Append a line built from the portion of the first line
1181533Sjoerg	 * to the right of the split plus the last line in the CB.
1191533Sjoerg	 *
1201533Sjoerg	 * Get the first line.
1211022Sache	 */
1221022Sache	lno = cp->lno;
1231022Sache	if (db_get(sp, lno, DBG_FATAL, &p, &len))
1241022Sache		return (1);
1251022Sache
12656447Sasmodai	GET_SPACE_RETW(sp, bp, blen, tp->len + len + 1);
1271022Sache	t = bp;
1281022Sache
1291022Sache	/* Original line, left of the split. */
1301022Sache	if (len > 0 && (clen = cp->cno + (append ? 1 : 0)) > 0) {
1311022Sache		MEMCPY(bp, p, clen);
1321022Sache		p += clen;
13356447Sasmodai		t += clen;
1341022Sache	}
1351022Sache
1361022Sache	/* First line from the CB. */
1371022Sache	if (tp->len != 0) {
1381022Sache		MEMCPY(t, tp->lb, tp->len);
1391022Sache		t += tp->len;
1401533Sjoerg	}
1411022Sache
14229531Scharnier	/* Calculate length left in the original line. */
14361154Sphk	clen = len == 0 ? 0 : len - (cp->cno + (append ? 1 : 0));
14429531Scharnier
1451022Sache	/*
1461022Sache	 * !!!
1471022Sache	 * In the historical 4BSD version of vi, character mode puts within
1481022Sache	 * a single line have two cursor behaviors: if the put is from the
1491533Sjoerg	 * unnamed buffer, the cursor moves to the character inserted which
1501022Sache	 * appears last in the file.  If the put is from a named buffer,
1511022Sache	 * the cursor moves to the character inserted which appears first
1521022Sache	 * in the file.  In System III/V, it was changed at some point and
1531022Sache	 * the cursor always moves to the first character.  In both versions
1541022Sache	 * of vi, character mode puts that cross line boundaries leave the
1551022Sache	 * cursor on the first character.  Nvi implements the System III/V
1561022Sache	 * behavior, and expect POSIX.2 to do so as well.
1571022Sache	 */
1581022Sache	rp->lno = lno;
1591022Sache	rp->cno = len == 0 ? 0 : sp->cno + (append && tp->len ? 1 : 0);
1601022Sache
1611022Sache	/*
1621022Sache	 * If no more lines in the CB, append the rest of the original
1631022Sache	 * line and quit.  Otherwise, build the last line before doing
1641022Sache	 * the intermediate lines, because the line changes will lose
1651022Sache	 * the cached line.
1661022Sache	 */
1671022Sache	if (TAILQ_NEXT(tp, q) == NULL) {
1681022Sache		if (clen > 0) {
1691022Sache			MEMCPY(t, p, clen);
1701022Sache			t += clen;
1711022Sache		}
1721022Sache		if (db_set(sp, lno, bp, t - bp))
17361154Sphk			goto err;
1741022Sache		if (sp->rptlchange != lno) {
1751022Sache			sp->rptlchange = lno;
1761022Sache			++sp->rptlines[L_CHANGED];
1771022Sache		}
17861154Sphk	} else {
1791022Sache		/*
1801022Sache		 * Have to build both the first and last lines of the
1811022Sache		 * put before doing any sets or we'll lose the cached
1821022Sache		 * line.  Build both the first and last lines in the
1831022Sache		 * same buffer, so we don't have to have another buffer
1841022Sache		 * floating around.
1851022Sache		 *
1861022Sache		 * Last part of original line; check for space, reset
1871022Sache		 * the pointer into the buffer.
1881022Sache		 */
1891022Sache		ltp = TAILQ_LAST(cbp->textq, _texth);
1901022Sache		len = t - bp;
1911022Sache		ADD_SPACE_RETW(sp, bp, blen, ltp->len + clen);
1921022Sache		t = bp + len;
1931022Sache
1941022Sache		/* Add in last part of the CB. */
1951022Sache		MEMCPY(t, ltp->lb, ltp->len);
1961022Sache		if (clen)
1971022Sache			MEMCPY(t + ltp->len, p, clen);
1981022Sache		clen += ltp->len;
1991022Sache
2001022Sache		/*
2011022Sache		 * Now: bp points to the first character of the first
2021022Sache		 * line, t points to the last character of the last
2031022Sache		 * line, t - bp is the length of the first line, and
2041022Sache		 * clen is the length of the last.  Just figured you'd
2051022Sache		 * want to know.
2061022Sache		 *
2071022Sache		 * Output the line replacing the original line.
2081022Sache		 */
2091022Sache		if (db_set(sp, lno, bp, t - bp))
2101022Sache			goto err;
2111022Sache		if (sp->rptlchange != lno) {
2121022Sache			sp->rptlchange = lno;
2131022Sache			++sp->rptlines[L_CHANGED];
2141022Sache		}
2151022Sache
2161022Sache		/* Output any intermediate lines in the CB. */
2171022Sache		for (tp = TAILQ_NEXT(tp, q); TAILQ_NEXT(tp, q) != NULL;
2181022Sache		    ++lno, ++sp->rptlines[L_ADDED], tp = TAILQ_NEXT(tp, q))
2191022Sache			if (db_append(sp, 1, lno, tp->lb, tp->len))
2201022Sache				goto err;
2211022Sache
2221022Sache		if (db_append(sp, 1, lno, t, clen))
2231022Sache			goto err;
22461154Sphk		++sp->rptlines[L_ADDED];
22561154Sphk	}
22661154Sphk	rval = 0;
22761154Sphk
2281022Sache	if (0)
2291022Sacheerr:		rval = 1;
2301022Sache
2311022Sache	FREE_SPACEW(sp, bp, blen);
2321022Sache	return (rval);
2331022Sache}
2341022Sache