1 /*-
2 * Copyright (c) 1992, 1993, 1994
3 * The Regents of the University of California. All rights reserved.
4 * Copyright (c) 1992, 1993, 1994, 1995, 1996
5 * Keith Bostic. All rights reserved.
6 *
7 * See the LICENSE file for redistribution information.
8 */
9
10 #include "config.h"
11
12 #include <sys/types.h>
13 #include <sys/queue.h>
14 #include <sys/time.h>
15
16 #include <bitstring.h>
17 #include <ctype.h>
18 #include <limits.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22
23 #include "common.h"
24
25 /*
26 * put --
27 * Put text buffer contents into the file.
28 *
29 * PUBLIC: int put(SCR *, CB *, CHAR_T *, MARK *, MARK *, int);
30 */
31 int
put(SCR * sp,CB * cbp,CHAR_T * namep,MARK * cp,MARK * rp,int append)32 put(SCR *sp, CB *cbp, CHAR_T *namep, MARK *cp, MARK *rp, int append)
33 {
34 CHAR_T name;
35 TEXT *ltp, *tp;
36 recno_t lno;
37 size_t blen, clen, len;
38 int rval;
39 CHAR_T *bp, *t;
40 CHAR_T *p;
41
42 if (cbp == NULL)
43 if (namep == NULL) {
44 cbp = sp->gp->dcbp;
45 if (cbp == NULL) {
46 msgq(sp, M_ERR,
47 "053|The default buffer is empty");
48 return (1);
49 }
50 } else {
51 name = *namep;
52 CBNAME(sp, cbp, name);
53 if (cbp == NULL) {
54 msgq(sp, M_ERR, "054|Buffer %s is empty",
55 KEY_NAME(sp, name));
56 return (1);
57 }
58 }
59 tp = TAILQ_FIRST(cbp->textq);
60
61 /*
62 * It's possible to do a put into an empty file, meaning that the cut
63 * buffer simply becomes the file. It's a special case so that we can
64 * ignore it in general.
65 *
66 * !!!
67 * Historically, pasting into a file with no lines in vi would preserve
68 * the single blank line. This is surely a result of the fact that the
69 * historic vi couldn't deal with a file that had no lines in it. This
70 * implementation treats that as a bug, and does not retain the blank
71 * line.
72 *
73 * Historical practice is that the cursor ends at the first character
74 * in the file.
75 */
76 if (cp->lno == 1) {
77 if (db_last(sp, &lno))
78 return (1);
79 if (lno == 0) {
80 for (; tp != NULL;
81 ++lno, ++sp->rptlines[L_ADDED], tp = TAILQ_NEXT(tp, q))
82 if (db_append(sp, 1, lno, tp->lb, tp->len))
83 return (1);
84 rp->lno = 1;
85 rp->cno = 0;
86 return (0);
87 }
88 }
89
90 /* If a line mode buffer, append each new line into the file. */
91 if (F_ISSET(cbp, CB_LMODE)) {
92 lno = append ? cp->lno : cp->lno - 1;
93 rp->lno = lno + 1;
94 for (; tp != NULL;
95 ++lno, ++sp->rptlines[L_ADDED], tp = TAILQ_NEXT(tp, q))
96 if (db_append(sp, 1, lno, tp->lb, tp->len))
97 return (1);
98 rp->cno = 0;
99 (void)nonblank(sp, rp->lno, &rp->cno);
100 return (0);
101 }
102
103 /*
104 * If buffer was cut in character mode, replace the current line with
105 * one built from the portion of the first line to the left of the
106 * split plus the first line in the CB. Append each intermediate line
107 * in the CB. Append a line built from the portion of the first line
108 * to the right of the split plus the last line in the CB.
109 *
110 * Get the first line.
111 */
112 lno = cp->lno;
113 if (db_get(sp, lno, DBG_FATAL, &p, &len))
114 return (1);
115
116 GET_SPACE_RETW(sp, bp, blen, tp->len + len + 1);
117 t = bp;
118
119 /* Original line, left of the split. */
120 if (len > 0 && (clen = cp->cno + (append ? 1 : 0)) > 0) {
121 MEMCPY(bp, p, clen);
122 p += clen;
123 t += clen;
124 }
125
126 /* First line from the CB. */
127 if (tp->len != 0) {
128 MEMCPY(t, tp->lb, tp->len);
129 t += tp->len;
130 }
131
132 /* Calculate length left in the original line. */
133 clen = len == 0 ? 0 : len - (cp->cno + (append ? 1 : 0));
134
135 /*
136 * !!!
137 * In the historical 4BSD version of vi, character mode puts within
138 * a single line have two cursor behaviors: if the put is from the
139 * unnamed buffer, the cursor moves to the character inserted which
140 * appears last in the file. If the put is from a named buffer,
141 * the cursor moves to the character inserted which appears first
142 * in the file. In System III/V, it was changed at some point and
143 * the cursor always moves to the first character. In both versions
144 * of vi, character mode puts that cross line boundaries leave the
145 * cursor on the first character. Nvi implements the System III/V
146 * behavior, and expect POSIX.2 to do so as well.
147 */
148 rp->lno = lno;
149 rp->cno = len == 0 ? 0 : sp->cno + (append && tp->len ? 1 : 0);
150
151 /*
152 * If no more lines in the CB, append the rest of the original
153 * line and quit. Otherwise, build the last line before doing
154 * the intermediate lines, because the line changes will lose
155 * the cached line.
156 */
157 if (TAILQ_NEXT(tp, q) == NULL) {
158 if (clen > 0) {
159 MEMCPY(t, p, clen);
160 t += clen;
161 }
162 if (db_set(sp, lno, bp, t - bp))
163 goto err;
164 if (sp->rptlchange != lno) {
165 sp->rptlchange = lno;
166 ++sp->rptlines[L_CHANGED];
167 }
168 } else {
169 /*
170 * Have to build both the first and last lines of the
171 * put before doing any sets or we'll lose the cached
172 * line. Build both the first and last lines in the
173 * same buffer, so we don't have to have another buffer
174 * floating around.
175 *
176 * Last part of original line; check for space, reset
177 * the pointer into the buffer.
178 */
179 ltp = TAILQ_LAST(cbp->textq, _texth);
180 len = t - bp;
181 ADD_SPACE_RETW(sp, bp, blen, ltp->len + clen);
182 t = bp + len;
183
184 /* Add in last part of the CB. */
185 MEMCPY(t, ltp->lb, ltp->len);
186 if (clen)
187 MEMCPY(t + ltp->len, p, clen);
188 clen += ltp->len;
189
190 /*
191 * Now: bp points to the first character of the first
192 * line, t points to the last character of the last
193 * line, t - bp is the length of the first line, and
194 * clen is the length of the last. Just figured you'd
195 * want to know.
196 *
197 * Output the line replacing the original line.
198 */
199 if (db_set(sp, lno, bp, t - bp))
200 goto err;
201 if (sp->rptlchange != lno) {
202 sp->rptlchange = lno;
203 ++sp->rptlines[L_CHANGED];
204 }
205
206 /* Output any intermediate lines in the CB. */
207 for (tp = TAILQ_NEXT(tp, q); TAILQ_NEXT(tp, q) != NULL;
208 ++lno, ++sp->rptlines[L_ADDED], tp = TAILQ_NEXT(tp, q))
209 if (db_append(sp, 1, lno, tp->lb, tp->len))
210 goto err;
211
212 if (db_append(sp, 1, lno, t, clen))
213 goto err;
214 ++sp->rptlines[L_ADDED];
215 }
216 rval = 0;
217
218 if (0)
219 err: rval = 1;
220
221 FREE_SPACEW(sp, bp, blen);
222 return (rval);
223 }
224