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