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