1 /* $OpenBSD: ex_move.c,v 1.11 2016/01/06 22:28:52 millert 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 #include <sys/types.h>
15 #include <sys/queue.h>
16
17 #include <bitstring.h>
18 #include <limits.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22
23 #include "../common/common.h"
24
25 /*
26 * ex_copy -- :[line [,line]] co[py] line [flags]
27 * Copy selected lines.
28 *
29 * PUBLIC: int ex_copy(SCR *, EXCMD *);
30 */
31 int
ex_copy(SCR * sp,EXCMD * cmdp)32 ex_copy(SCR *sp, EXCMD *cmdp)
33 {
34 CB cb;
35 MARK fm1, fm2, m, tm;
36 recno_t cnt;
37 int rval;
38
39 rval = 0;
40
41 NEEDFILE(sp, cmdp);
42
43 /*
44 * It's possible to copy things into the area that's being
45 * copied, e.g. "2,5copy3" is legitimate. Save the text to
46 * a cut buffer.
47 */
48 fm1 = cmdp->addr1;
49 fm2 = cmdp->addr2;
50 memset(&cb, 0, sizeof(cb));
51 TAILQ_INIT(&cb.textq);
52 for (cnt = fm1.lno; cnt <= fm2.lno; ++cnt)
53 if (cut_line(sp, cnt, 0, CUT_LINE_TO_EOL, &cb)) {
54 rval = 1;
55 goto err;
56 }
57 cb.flags |= CB_LMODE;
58
59 /* Put the text into place. */
60 tm.lno = cmdp->lineno;
61 tm.cno = 0;
62 if (put(sp, &cb, NULL, &tm, &m, 1))
63 rval = 1;
64 else {
65 /*
66 * Copy puts the cursor on the last line copied. The cursor
67 * returned by the put routine is the first line put, not the
68 * last, because that's the historic semantic of vi.
69 */
70 cnt = (fm2.lno - fm1.lno) + 1;
71 sp->lno = m.lno + (cnt - 1);
72 sp->cno = 0;
73 }
74 err: text_lfree(&cb.textq);
75 return (rval);
76 }
77
78 /*
79 * ex_move -- :[line [,line]] mo[ve] line
80 * Move selected lines.
81 *
82 * PUBLIC: int ex_move(SCR *, EXCMD *);
83 */
84 int
ex_move(SCR * sp,EXCMD * cmdp)85 ex_move(SCR *sp, EXCMD *cmdp)
86 {
87 LMARK *lmp;
88 MARK fm1, fm2;
89 recno_t cnt, diff, fl, tl, mfl, mtl;
90 size_t blen, len;
91 int mark_reset;
92 char *bp, *p;
93
94 NEEDFILE(sp, cmdp);
95
96 /*
97 * It's not possible to move things into the area that's being
98 * moved.
99 */
100 fm1 = cmdp->addr1;
101 fm2 = cmdp->addr2;
102 if (cmdp->lineno >= fm1.lno && cmdp->lineno <= fm2.lno) {
103 msgq(sp, M_ERR, "Destination line is inside move range");
104 return (1);
105 }
106
107 /*
108 * Log the positions of any marks in the to-be-deleted lines. This
109 * has to work with the logging code. What happens is that we log
110 * the old mark positions, make the changes, then log the new mark
111 * positions. Then the marks end up in the right positions no matter
112 * which way the log is traversed.
113 *
114 * XXX
115 * Reset the MARK_USERSET flag so that the log can undo the mark.
116 * This isn't very clean, and should probably be fixed.
117 */
118 fl = fm1.lno;
119 tl = cmdp->lineno;
120
121 /* Log the old positions of the marks. */
122 mark_reset = 0;
123 LIST_FOREACH(lmp, &sp->ep->marks, q)
124 if (lmp->name != ABSMARK1 &&
125 lmp->lno >= fl && lmp->lno <= tl) {
126 mark_reset = 1;
127 F_CLR(lmp, MARK_USERSET);
128 (void)log_mark(sp, lmp);
129 }
130
131 /* Get memory for the copy. */
132 GET_SPACE_RET(sp, bp, blen, 256);
133
134 /* Move the lines. */
135 diff = (fm2.lno - fm1.lno) + 1;
136 if (tl > fl) { /* Destination > source. */
137 mfl = tl - diff;
138 mtl = tl;
139 for (cnt = diff; cnt--;) {
140 if (db_get(sp, fl, DBG_FATAL, &p, &len))
141 return (1);
142 BINC_RET(sp, bp, blen, len);
143 memcpy(bp, p, len);
144 if (db_append(sp, 1, tl, bp, len))
145 return (1);
146 if (mark_reset)
147 LIST_FOREACH(lmp, &sp->ep->marks, q)
148 if (lmp->name != ABSMARK1 &&
149 lmp->lno == fl)
150 lmp->lno = tl + 1;
151 if (db_delete(sp, fl))
152 return (1);
153 }
154 } else { /* Destination < source. */
155 mfl = tl;
156 mtl = tl + diff;
157 for (cnt = diff; cnt--;) {
158 if (db_get(sp, fl, DBG_FATAL, &p, &len))
159 return (1);
160 BINC_RET(sp, bp, blen, len);
161 memcpy(bp, p, len);
162 if (db_append(sp, 1, tl++, bp, len))
163 return (1);
164 if (mark_reset)
165 LIST_FOREACH(lmp, &sp->ep->marks, q)
166 if (lmp->name != ABSMARK1 &&
167 lmp->lno == fl)
168 lmp->lno = tl;
169 ++fl;
170 if (db_delete(sp, fl))
171 return (1);
172 }
173 }
174 FREE_SPACE(sp, bp, blen);
175
176 sp->lno = tl; /* Last line moved. */
177 sp->cno = 0;
178
179 /* Log the new positions of the marks. */
180 if (mark_reset)
181 LIST_FOREACH(lmp, &sp->ep->marks, q)
182 if (lmp->name != ABSMARK1 &&
183 lmp->lno >= mfl && lmp->lno <= mtl)
184 (void)log_mark(sp, lmp);
185
186
187 sp->rptlines[L_MOVED] += diff;
188 return (0);
189 }
190