1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
|
/*-
* Copyright (c) 1992, 1993, 1994
* The Regents of the University of California. All rights reserved.
* Copyright (c) 1992, 1993, 1994, 1995, 1996
* Keith Bostic. All rights reserved.
*
* See the LICENSE file for redistribution information.
*/
#include "config.h"
#ifndef lint
static const char sccsid[] = "@(#)ex_shift.c 10.11 (Berkeley) 9/15/96";
#endif /* not lint */
#include <sys/types.h>
#include <sys/queue.h>
#include <bitstring.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "../common/common.h"
enum which {LEFT, RIGHT};
static int shift __P((SCR *, EXCMD *, enum which));
/*
* ex_shiftl -- :<[<...]
*
*
* PUBLIC: int ex_shiftl __P((SCR *, EXCMD *));
*/
int
ex_shiftl(sp, cmdp)
SCR *sp;
EXCMD *cmdp;
{
return (shift(sp, cmdp, LEFT));
}
/*
* ex_shiftr -- :>[>...]
*
* PUBLIC: int ex_shiftr __P((SCR *, EXCMD *));
*/
int
ex_shiftr(sp, cmdp)
SCR *sp;
EXCMD *cmdp;
{
return (shift(sp, cmdp, RIGHT));
}
/*
* shift --
* Ex shift support.
*/
static int
shift(sp, cmdp, rl)
SCR *sp;
EXCMD *cmdp;
enum which rl;
{
recno_t from, to;
size_t blen, len, newcol, newidx, oldcol, oldidx, sw;
int curset;
char *p, *bp, *tbp;
NEEDFILE(sp, cmdp);
if (O_VAL(sp, O_SHIFTWIDTH) == 0) {
msgq(sp, M_INFO, "152|shiftwidth option set to 0");
return (0);
}
/* Copy the lines being shifted into the unnamed buffer. */
if (cut(sp, NULL, &cmdp->addr1, &cmdp->addr2, CUT_LINEMODE))
return (1);
/*
* The historic version of vi permitted the user to string any number
* of '>' or '<' characters together, resulting in an indent of the
* appropriate levels. There's a special hack in ex_cmd() so that
* cmdp->argv[0] points to the string of '>' or '<' characters.
*
* Q: What's the difference between the people adding features
* to vi and the Girl Scouts?
* A: The Girl Scouts have mint cookies and adult supervision.
*/
for (p = cmdp->argv[0]->bp, sw = 0; *p == '>' || *p == '<'; ++p)
sw += O_VAL(sp, O_SHIFTWIDTH);
GET_SPACE_RET(sp, bp, blen, 256);
curset = 0;
for (from = cmdp->addr1.lno, to = cmdp->addr2.lno; from <= to; ++from) {
if (db_get(sp, from, DBG_FATAL, &p, &len))
goto err;
if (!len) {
if (sp->lno == from)
curset = 1;
continue;
}
/*
* Calculate the old indent amount and the number of
* characters it used.
*/
for (oldidx = 0, oldcol = 0; oldidx < len; ++oldidx)
if (p[oldidx] == ' ')
++oldcol;
else if (p[oldidx] == '\t')
oldcol += O_VAL(sp, O_TABSTOP) -
oldcol % O_VAL(sp, O_TABSTOP);
else
break;
/* Calculate the new indent amount. */
if (rl == RIGHT)
newcol = oldcol + sw;
else {
newcol = oldcol < sw ? 0 : oldcol - sw;
if (newcol == oldcol) {
if (sp->lno == from)
curset = 1;
continue;
}
}
/* Get a buffer that will hold the new line. */
ADD_SPACE_RET(sp, bp, blen, newcol + len);
/*
* Build a new indent string and count the number of
* characters it uses.
*/
for (tbp = bp, newidx = 0;
newcol >= O_VAL(sp, O_TABSTOP); ++newidx) {
*tbp++ = '\t';
newcol -= O_VAL(sp, O_TABSTOP);
}
for (; newcol > 0; --newcol, ++newidx)
*tbp++ = ' ';
/* Add the original line. */
memcpy(tbp, p + oldidx, len - oldidx);
/* Set the replacement line. */
if (db_set(sp, from, bp, (tbp + (len - oldidx)) - bp)) {
err: FREE_SPACE(sp, bp, blen);
return (1);
}
/*
* !!!
* The shift command in historic vi had the usual bizarre
* collection of cursor semantics. If called from vi, the
* cursor was repositioned to the first non-blank character
* of the lowest numbered line shifted. If called from ex,
* the cursor was repositioned to the first non-blank of the
* highest numbered line shifted. Here, if the cursor isn't
* part of the set of lines that are moved, move it to the
* first non-blank of the last line shifted. (This makes
* ":3>>" in vi work reasonably.) If the cursor is part of
* the shifted lines, it doesn't get moved at all. This
* permits shifting of marked areas, i.e. ">'a." shifts the
* marked area twice, something that couldn't be done with
* historic vi.
*/
if (sp->lno == from) {
curset = 1;
if (newidx > oldidx)
sp->cno += newidx - oldidx;
else if (sp->cno >= oldidx - newidx)
sp->cno -= oldidx - newidx;
}
}
if (!curset) {
sp->lno = to;
sp->cno = 0;
(void)nonblank(sp, to, &sp->cno);
}
FREE_SPACE(sp, bp, blen);
sp->rptlines[L_SHIFT] += cmdp->addr2.lno - cmdp->addr1.lno + 1;
return (0);
}
|