Home | History | Annotate | Line # | Download | only in vi
      1 /*	$NetBSD: v_mark.c,v 1.4 2014/01/26 21:43:45 christos Exp $ */
      2 /*-
      3  * Copyright (c) 1992, 1993, 1994
      4  *	The Regents of the University of California.  All rights reserved.
      5  * Copyright (c) 1992, 1993, 1994, 1995, 1996
      6  *	Keith Bostic.  All rights reserved.
      7  *
      8  * See the LICENSE file for redistribution information.
      9  */
     10 
     11 #include "config.h"
     12 
     13 #include <sys/cdefs.h>
     14 #if 0
     15 #ifndef lint
     16 static const char sccsid[] = "Id: v_mark.c,v 10.12 2001/06/25 15:19:32 skimo Exp  (Berkeley) Date: 2001/06/25 15:19:32 ";
     17 #endif /* not lint */
     18 #else
     19 __RCSID("$NetBSD: v_mark.c,v 1.4 2014/01/26 21:43:45 christos Exp $");
     20 #endif
     21 
     22 #include <sys/types.h>
     23 #include <sys/queue.h>
     24 #include <sys/time.h>
     25 
     26 #include <bitstring.h>
     27 #include <limits.h>
     28 #include <stdlib.h>
     29 #include <stdio.h>
     30 
     31 #include "../common/common.h"
     32 #include "vi.h"
     33 
     34 enum which {BQMARK, FQMARK};
     35 static int mark __P((SCR *, VICMD *, int, enum which));
     36 
     37 /*
     38  * v_mark -- m[a-z]
     39  *	Set a mark.
     40  *
     41  * PUBLIC: int v_mark __P((SCR *, VICMD *));
     42  */
     43 int
     44 v_mark(SCR *sp, VICMD *vp)
     45 {
     46 	return (mark_set(sp, vp->character, &vp->m_start, 1));
     47 }
     48 
     49 /*
     50  * v_bmark -- `['`a-z]
     51  *	Move to a mark.
     52  *
     53  * Moves to a mark, setting both row and column.
     54  *
     55  * !!!
     56  * Although not commonly known, the "'`" and "'`" forms are historically
     57  * valid.  The behavior is determined by the first character, so "`'" is
     58  * the same as "``".  Remember this fact -- you'll be amazed at how many
     59  * people don't know it and will be delighted that you are able to tell
     60  * them.
     61  *
     62  * PUBLIC: int v_bmark __P((SCR *, VICMD *));
     63  */
     64 int
     65 v_bmark(SCR *sp, VICMD *vp)
     66 {
     67 	return (mark(sp, vp, 1, BQMARK));
     68 }
     69 
     70 /*
     71  * v_fmark -- '['`a-z]
     72  *	Move to a mark.
     73  *
     74  * Move to the first nonblank character of the line containing the mark.
     75  *
     76  * PUBLIC: int v_fmark __P((SCR *, VICMD *));
     77  */
     78 int
     79 v_fmark(SCR *sp, VICMD *vp)
     80 {
     81 	return (mark(sp, vp, 1, FQMARK));
     82 }
     83 
     84 /*
     85  * v_emark -- <mouse click>
     86  *	Mouse mark.
     87  *
     88  * PUBLIC: int v_emark __P((SCR *, VICMD *));
     89  */
     90 int
     91 v_emark(SCR *sp, VICMD *vp)
     92 {
     93 	SMAP *smp;
     94 
     95 	smp = HMAP + vp->ev.e_lno;
     96 	if (smp > TMAP) {
     97 		msgq(sp, M_BERR, "320|Unknown cursor position.");
     98 		return (1);
     99 	}
    100 	vp->m_stop.lno = smp->lno;
    101 	vp->m_stop.cno =
    102 	    vs_colpos(sp, smp->lno, vp->ev.e_cno + (smp->soff - 1) * sp->cols);
    103 	return (mark(sp, vp, 0, BQMARK));
    104 }
    105 
    106 /*
    107  * mark --
    108  *	Mark commands.
    109  */
    110 static int
    111 mark(SCR *sp, VICMD *vp, int getmark, enum which cmd)
    112 {
    113 	MARK m;
    114 	size_t len;
    115 
    116 	if (getmark && mark_get(sp, vp->character, &vp->m_stop, M_BERR))
    117 		return (1);
    118 
    119 	/*
    120 	 * !!!
    121 	 * Historically, BQMARKS for character positions that no longer
    122 	 * existed acted as FQMARKS.
    123 	 *
    124 	 * FQMARKS move to the first non-blank.
    125 	 */
    126 	switch (cmd) {
    127 	case BQMARK:
    128 		if (db_get(sp, vp->m_stop.lno, DBG_FATAL, NULL, &len))
    129 			return (1);
    130 		if (vp->m_stop.cno < len ||
    131 		    (vp->m_stop.cno == len && len == 0))
    132 			break;
    133 
    134 		if (ISMOTION(vp))
    135 			F_SET(vp, VM_LMODE);
    136 		cmd = FQMARK;
    137 		/* FALLTHROUGH */
    138 	case FQMARK:
    139 		vp->m_stop.cno = 0;
    140 		if (nonblank(sp, vp->m_stop.lno, &vp->m_stop.cno))
    141 			return (1);
    142 		break;
    143 	default:
    144 		abort();
    145 	}
    146 
    147 	/* Non-motion commands move to the end of the range. */
    148 	if (!ISMOTION(vp)) {
    149 		vp->m_final = vp->m_stop;
    150 		return (0);
    151 	}
    152 
    153 	/*
    154 	 * !!!
    155 	 * If a motion component to a BQMARK, the cursor has to move.
    156 	 */
    157 	if (cmd == BQMARK &&
    158 	    vp->m_stop.lno == vp->m_start.lno &&
    159 	    vp->m_stop.cno == vp->m_start.cno) {
    160 		v_nomove(sp);
    161 		return (1);
    162 	}
    163 
    164 	/*
    165 	 * If the motion is in the reverse direction, switch the start and
    166 	 * stop MARK's so that it's in a forward direction.  (There's no
    167 	 * reason for this other than to make the tests below easier.  The
    168 	 * code in vi.c:vi() would have done the switch.)  Both forward
    169 	 * and backward motions can happen for any kind of search command.
    170 	 */
    171 	if (vp->m_start.lno > vp->m_stop.lno ||
    172 	    (vp->m_start.lno == vp->m_stop.lno &&
    173 	    vp->m_start.cno > vp->m_stop.cno)) {
    174 		m = vp->m_start;
    175 		vp->m_start = vp->m_stop;
    176 		vp->m_stop = m;
    177 	}
    178 
    179 	/*
    180 	 * Yank cursor motion, when associated with marks as motion commands,
    181 	 * historically behaved as follows:
    182 	 *
    183 	 * ` motion			' motion
    184 	 *		Line change?		Line change?
    185 	 *		Y	N		Y	N
    186 	 *	      --------------	      ---------------
    187 	 * FORWARD:  |	NM	NM	      | NM	NM
    188 	 *	     |			      |
    189 	 * BACKWARD: |	M	M	      | M	NM(1)
    190 	 *
    191 	 * where NM means the cursor didn't move, and M means the cursor
    192 	 * moved to the mark.
    193 	 *
    194 	 * As the cursor was usually moved for yank commands associated
    195 	 * with backward motions, this implementation regularizes it by
    196 	 * changing the NM at position (1) to be an M.  This makes mark
    197 	 * motions match search motions, which is probably A Good Thing.
    198 	 *
    199 	 * Delete cursor motion was always to the start of the text region,
    200 	 * regardless.  Ignore other motion commands.
    201 	 */
    202 #ifdef HISTORICAL_PRACTICE
    203 	if (ISCMD(vp->rkp, 'y')) {
    204 		if ((cmd == BQMARK ||
    205 		    cmd == FQMARK && vp->m_start.lno != vp->m_stop.lno) &&
    206 		    (vp->m_start.lno > vp->m_stop.lno ||
    207 		    vp->m_start.lno == vp->m_stop.lno &&
    208 		    vp->m_start.cno > vp->m_stop.cno))
    209 			vp->m_final = vp->m_stop;
    210 	} else if (ISCMD(vp->rkp, 'd'))
    211 		if (vp->m_start.lno > vp->m_stop.lno ||
    212 		    vp->m_start.lno == vp->m_stop.lno &&
    213 		    vp->m_start.cno > vp->m_stop.cno)
    214 			vp->m_final = vp->m_stop;
    215 #else
    216 	vp->m_final = vp->m_start;
    217 #endif
    218 
    219 	/*
    220 	 * Forward marks are always line oriented, and it's set in the
    221 	 * vcmd.c table.
    222 	 */
    223 	if (cmd == FQMARK)
    224 		return (0);
    225 
    226 	/*
    227 	 * BQMARK'S moving backward and starting at column 0, and ones moving
    228 	 * forward and ending at column 0 are corrected to the last column of
    229 	 * the previous line.  Otherwise, adjust the starting/ending point to
    230 	 * the character before the current one (this is safe because we know
    231 	 * the search had to move to succeed).
    232 	 *
    233 	 * Mark motions become line mode opertions if they start at the first
    234 	 * nonblank and end at column 0 of another line.
    235 	 */
    236 	if (vp->m_start.lno < vp->m_stop.lno && vp->m_stop.cno == 0) {
    237 		if (db_get(sp, --vp->m_stop.lno, DBG_FATAL, NULL, &len))
    238 			return (1);
    239 		vp->m_stop.cno = len ? len - 1 : 0;
    240 		len = 0;
    241 		if (nonblank(sp, vp->m_start.lno, &len))
    242 			return (1);
    243 		if (vp->m_start.cno <= len)
    244 			F_SET(vp, VM_LMODE);
    245 	} else
    246 		--vp->m_stop.cno;
    247 
    248 	return (0);
    249 }
    250