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