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