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