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