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_left.c,v 10.9 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 <stdio.h> 23 24 #include "../common/common.h" 25 #include "vi.h" 26 27 /* 28 * v_left -- [count]^H, [count]h 29 * Move left by columns. 30 * 31 * PUBLIC: int v_left(SCR *, VICMD *); 32 */ 33 int 34 v_left(SCR *sp, VICMD *vp) 35 { 36 recno_t cnt; 37 38 /* 39 * !!! 40 * The ^H and h commands always failed in the first column. 41 */ 42 if (vp->m_start.cno == 0) { 43 v_sol(sp); 44 return (1); 45 } 46 47 /* Find the end of the range. */ 48 cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; 49 if (vp->m_start.cno > cnt) 50 vp->m_stop.cno = vp->m_start.cno - cnt; 51 else 52 vp->m_stop.cno = 0; 53 54 /* 55 * All commands move to the end of the range. Motion commands 56 * adjust the starting point to the character before the current 57 * one. 58 */ 59 if (ISMOTION(vp)) 60 --vp->m_start.cno; 61 vp->m_final = vp->m_stop; 62 return (0); 63 } 64 65 /* 66 * v_cfirst -- [count]_ 67 * Move to the first non-blank character in a line. 68 * 69 * PUBLIC: int v_cfirst(SCR *, VICMD *); 70 */ 71 int 72 v_cfirst(SCR *sp, VICMD *vp) 73 { 74 recno_t cnt, lno; 75 76 /* 77 * !!! 78 * If the _ is a motion component, it makes the command a line motion 79 * e.g. "d_" deletes the line. It also means that the cursor doesn't 80 * move. 81 * 82 * The _ command never failed in the first column. 83 */ 84 if (ISMOTION(vp)) 85 F_SET(vp, VM_LMODE); 86 /* 87 * !!! 88 * Historically a specified count makes _ move down count - 1 89 * rows, so, "3_" is the same as "2j_". 90 */ 91 cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; 92 if (cnt != 1) { 93 --vp->count; 94 return (v_down(sp, vp)); 95 } 96 97 /* 98 * Move to the first non-blank. 99 * 100 * Can't just use RCM_SET_FNB, in case _ is used as the motion 101 * component of another command. 102 */ 103 vp->m_stop.cno = 0; 104 if (nonblank(sp, vp->m_stop.lno, &vp->m_stop.cno)) 105 return (1); 106 107 /* 108 * !!! 109 * The _ command has to fail if the file is empty and we're doing 110 * a delete. If deleting line 1, and 0 is the first nonblank, 111 * make the check. 112 */ 113 if (vp->m_stop.lno == 1 && 114 vp->m_stop.cno == 0 && ISCMD(vp->rkp, 'd')) { 115 if (db_last(sp, &lno)) 116 return (1); 117 if (lno == 0) { 118 v_sol(sp); 119 return (1); 120 } 121 } 122 123 /* 124 * Delete and non-motion commands move to the end of the range, 125 * yank stays at the start. Ignore others. 126 */ 127 vp->m_final = 128 ISMOTION(vp) && ISCMD(vp->rkp, 'y') ? vp->m_start : vp->m_stop; 129 return (0); 130 } 131 132 /* 133 * v_first -- ^ 134 * Move to the first non-blank character in this line. 135 * 136 * PUBLIC: int v_first(SCR *, VICMD *); 137 */ 138 int 139 v_first(SCR *sp, VICMD *vp) 140 { 141 /* 142 * !!! 143 * Yielding to none in our quest for compatibility with every 144 * historical blemish of vi, no matter how strange it might be, 145 * we permit the user to enter a count and then ignore it. 146 */ 147 148 /* 149 * Move to the first non-blank. 150 * 151 * Can't just use RCM_SET_FNB, in case ^ is used as the motion 152 * component of another command. 153 */ 154 vp->m_stop.cno = 0; 155 if (nonblank(sp, vp->m_stop.lno, &vp->m_stop.cno)) 156 return (1); 157 158 /* 159 * !!! 160 * The ^ command succeeded if used as a command when the cursor was 161 * on the first non-blank in the line, but failed if used as a motion 162 * component in the same situation. 163 */ 164 if (ISMOTION(vp) && vp->m_start.cno == vp->m_stop.cno) { 165 v_sol(sp); 166 return (1); 167 } 168 169 /* 170 * If moving right, non-motion commands move to the end of the range. 171 * Delete and yank stay at the start. Motion commands adjust the 172 * ending point to the character before the current ending charcter. 173 * 174 * If moving left, all commands move to the end of the range. Motion 175 * commands adjust the starting point to the character before the 176 * current starting character. 177 */ 178 if (vp->m_start.cno < vp->m_stop.cno) 179 if (ISMOTION(vp)) { 180 --vp->m_stop.cno; 181 vp->m_final = vp->m_start; 182 } else 183 vp->m_final = vp->m_stop; 184 else { 185 if (ISMOTION(vp)) 186 --vp->m_start.cno; 187 vp->m_final = vp->m_stop; 188 } 189 return (0); 190 } 191 192 /* 193 * v_ncol -- [count]| 194 * Move to column count or the first column on this line. If the 195 * requested column is past EOL, move to EOL. The nasty part is 196 * that we have to know character column widths to make this work. 197 * 198 * PUBLIC: int v_ncol(SCR *, VICMD *); 199 */ 200 int 201 v_ncol(SCR *sp, VICMD *vp) 202 { 203 if (F_ISSET(vp, VC_C1SET) && vp->count > 1) { 204 --vp->count; 205 vp->m_stop.cno = 206 vs_colpos(sp, vp->m_start.lno, (size_t)vp->count); 207 /* 208 * !!! 209 * The | command succeeded if used as a command and the cursor 210 * didn't move, but failed if used as a motion component in the 211 * same situation. 212 */ 213 if (ISMOTION(vp) && vp->m_stop.cno == vp->m_start.cno) { 214 v_nomove(sp); 215 return (1); 216 } 217 } else { 218 /* 219 * !!! 220 * The | command succeeded if used as a command in column 0 221 * without a count, but failed if used as a motion component 222 * in the same situation. 223 */ 224 if (ISMOTION(vp) && vp->m_start.cno == 0) { 225 v_sol(sp); 226 return (1); 227 } 228 vp->m_stop.cno = 0; 229 } 230 231 /* 232 * If moving right, non-motion commands move to the end of the range. 233 * Delete and yank stay at the start. Motion commands adjust the 234 * ending point to the character before the current ending charcter. 235 * 236 * If moving left, all commands move to the end of the range. Motion 237 * commands adjust the starting point to the character before the 238 * current starting character. 239 */ 240 if (vp->m_start.cno < vp->m_stop.cno) 241 if (ISMOTION(vp)) { 242 --vp->m_stop.cno; 243 vp->m_final = vp->m_start; 244 } else 245 vp->m_final = vp->m_stop; 246 else { 247 if (ISMOTION(vp)) 248 --vp->m_start.cno; 249 vp->m_final = vp->m_stop; 250 } 251 return (0); 252 } 253 254 /* 255 * v_zero -- 0 256 * Move to the first column on this line. 257 * 258 * PUBLIC: int v_zero(SCR *, VICMD *); 259 */ 260 int 261 v_zero(SCR *sp, VICMD *vp) 262 { 263 /* 264 * !!! 265 * The 0 command succeeded if used as a command in the first column 266 * but failed if used as a motion component in the same situation. 267 */ 268 if (ISMOTION(vp) && vp->m_start.cno == 0) { 269 v_sol(sp); 270 return (1); 271 } 272 273 /* 274 * All commands move to the end of the range. Motion commands 275 * adjust the starting point to the character before the current 276 * one. 277 */ 278 vp->m_stop.cno = 0; 279 if (ISMOTION(vp)) 280 --vp->m_start.cno; 281 vp->m_final = vp->m_stop; 282 return (0); 283 } 284