xref: /openbsd/usr.bin/vi/ex/ex_move.c (revision 8529ddd3)
1 /*	$OpenBSD: ex_move.c,v 1.10 2014/11/12 04:28:41 bentley 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
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
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, "139|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