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