source: trunk/third/nvi/common/put.c @ 14302

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