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