freebsd-dev/contrib/nvi/vi/v_mark.c

235 lines
5.5 KiB
C
Raw Normal View History

/*-
* 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[] = "@(#)v_mark.c 10.8 (Berkeley) 9/20/96";
#endif /* not lint */
#include <sys/types.h>
#include <sys/queue.h>
#include <sys/time.h>
#include <bitstring.h>
#include <limits.h>
#include <stdlib.h>
#include <stdio.h>
#include "../common/common.h"
#include "vi.h"
/*
* v_mark -- m[a-z]
* Set a mark.
*
* PUBLIC: int v_mark __P((SCR *, VICMD *));
*/
int
v_mark(sp, vp)
SCR *sp;
VICMD *vp;
{
return (mark_set(sp, vp->character, &vp->m_start, 1));
}
enum which {BQMARK, FQMARK};
static int mark __P((SCR *, VICMD *, enum which));
/*
* v_bmark -- `['`a-z]
* Move to a mark.
*
* Moves to a mark, setting both row and column.
*
* !!!
* Although not commonly known, the "'`" and "'`" forms are historically
* valid. The behavior is determined by the first character, so "`'" is
* the same as "``". Remember this fact -- you'll be amazed at how many
* people don't know it and will be delighted that you are able to tell
* them.
*
* PUBLIC: int v_bmark __P((SCR *, VICMD *));
*/
int
v_bmark(sp, vp)
SCR *sp;
VICMD *vp;
{
return (mark(sp, vp, BQMARK));
}
/*
* v_fmark -- '['`a-z]
* Move to a mark.
*
* Move to the first nonblank character of the line containing the mark.
*
* PUBLIC: int v_fmark __P((SCR *, VICMD *));
*/
int
v_fmark(sp, vp)
SCR *sp;
VICMD *vp;
{
return (mark(sp, vp, FQMARK));
}
/*
* mark --
* Mark commands.
*/
static int
mark(sp, vp, cmd)
SCR *sp;
VICMD *vp;
enum which cmd;
{
dir_t dir;
MARK m;
size_t len;
if (mark_get(sp, vp->character, &vp->m_stop, M_BERR))
return (1);
/*
* !!!
* Historically, BQMARKS for character positions that no longer
* existed acted as FQMARKS.
*
* FQMARKS move to the first non-blank.
*/
switch (cmd) {
case BQMARK:
if (db_get(sp, vp->m_stop.lno, DBG_FATAL, NULL, &len))
return (1);
if (vp->m_stop.cno < len ||
vp->m_stop.cno == len && len == 0)
break;
if (ISMOTION(vp))
F_SET(vp, VM_LMODE);
cmd = FQMARK;
/* FALLTHROUGH */
case FQMARK:
vp->m_stop.cno = 0;
if (nonblank(sp, vp->m_stop.lno, &vp->m_stop.cno))
return (1);
break;
default:
abort();
}
/* Non-motion commands move to the end of the range. */
if (!ISMOTION(vp)) {
vp->m_final = vp->m_stop;
return (0);
}
/*
* !!!
* If a motion component to a BQMARK, the cursor has to move.
*/
if (cmd == BQMARK &&
vp->m_stop.lno == vp->m_start.lno &&
vp->m_stop.cno == vp->m_start.cno) {
v_nomove(sp);
return (1);
}
/*
* If the motion is in the reverse direction, switch the start and
* stop MARK's so that it's in a forward direction. (There's no
* reason for this other than to make the tests below easier. The
* code in vi.c:vi() would have done the switch.) Both forward
* and backward motions can happen for any kind of search command.
*/
if (vp->m_start.lno > vp->m_stop.lno ||
vp->m_start.lno == vp->m_stop.lno &&
vp->m_start.cno > vp->m_stop.cno) {
m = vp->m_start;
vp->m_start = vp->m_stop;
vp->m_stop = m;
dir = BACKWARD;
} else
dir = FORWARD;
/*
* Yank cursor motion, when associated with marks as motion commands,
* historically behaved as follows:
*
* ` motion ' motion
* Line change? Line change?
* Y N Y N
* -------------- ---------------
* FORWARD: | NM NM | NM NM
* | |
* BACKWARD: | M M | M NM(1)
*
* where NM means the cursor didn't move, and M means the cursor
* moved to the mark.
*
* As the cursor was usually moved for yank commands associated
* with backward motions, this implementation regularizes it by
* changing the NM at position (1) to be an M. This makes mark
* motions match search motions, which is probably A Good Thing.
*
* Delete cursor motion was always to the start of the text region,
* regardless. Ignore other motion commands.
*/
#ifdef HISTORICAL_PRACTICE
if (ISCMD(vp->rkp, 'y')) {
if ((cmd == BQMARK ||
cmd == FQMARK && vp->m_start.lno != vp->m_stop.lno) &&
(vp->m_start.lno > vp->m_stop.lno ||
vp->m_start.lno == vp->m_stop.lno &&
vp->m_start.cno > vp->m_stop.cno))
vp->m_final = vp->m_stop;
} else if (ISCMD(vp->rkp, 'd'))
if (vp->m_start.lno > vp->m_stop.lno ||
vp->m_start.lno == vp->m_stop.lno &&
vp->m_start.cno > vp->m_stop.cno)
vp->m_final = vp->m_stop;
#else
vp->m_final = vp->m_start;
#endif
/*
* Forward marks are always line oriented, and it's set in the
* vcmd.c table.
*/
if (cmd == FQMARK)
return (0);
/*
* BQMARK'S moving backward and starting at column 0, and ones moving
* forward and ending at column 0 are corrected to the last column of
* the previous line. Otherwise, adjust the starting/ending point to
* the character before the current one (this is safe because we know
* the search had to move to succeed).
*
* Mark motions become line mode opertions if they start at the first
* nonblank and end at column 0 of another line.
*/
if (vp->m_start.lno < vp->m_stop.lno && vp->m_stop.cno == 0) {
if (db_get(sp, --vp->m_stop.lno, DBG_FATAL, NULL, &len))
return (1);
vp->m_stop.cno = len ? len - 1 : 0;
len = 0;
if (nonblank(sp, vp->m_start.lno, &len))
return (1);
if (vp->m_start.cno <= len)
F_SET(vp, VM_LMODE);
} else
--vp->m_stop.cno;
return (0);
}