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 #include <sys/types.h> 13 #include <sys/queue.h> 14 #include <sys/time.h> 15 16 #include <bitstring.h> 17 #include <limits.h> 18 #include <stdio.h> 19 #include <stdlib.h> 20 21 #include "../common/common.h" 22 #include "vi.h" 23 24 static void notfound(SCR *, ARG_CHAR_T); 25 static void noprev(SCR *); 26 27 /* 28 * v_chrepeat -- [count]; 29 * Repeat the last F, f, T or t search. 30 * 31 * PUBLIC: int v_chrepeat(SCR *, VICMD *); 32 */ 33 int 34 v_chrepeat(SCR *sp, VICMD *vp) 35 { 36 vp->character = VIP(sp)->lastckey; 37 38 switch (VIP(sp)->csearchdir) { 39 case CNOTSET: 40 noprev(sp); 41 return (1); 42 case FSEARCH: 43 return (v_chF(sp, vp)); 44 case fSEARCH: 45 return (v_chf(sp, vp)); 46 case TSEARCH: 47 return (v_chT(sp, vp)); 48 case tSEARCH: 49 return (v_cht(sp, vp)); 50 default: 51 abort(); 52 } 53 /* NOTREACHED */ 54 } 55 56 /* 57 * v_chrrepeat -- [count], 58 * Repeat the last F, f, T or t search in the reverse direction. 59 * 60 * PUBLIC: int v_chrrepeat(SCR *, VICMD *); 61 */ 62 int 63 v_chrrepeat(SCR *sp, VICMD *vp) 64 { 65 cdir_t savedir; 66 int rval; 67 68 vp->character = VIP(sp)->lastckey; 69 savedir = VIP(sp)->csearchdir; 70 71 switch (VIP(sp)->csearchdir) { 72 case CNOTSET: 73 noprev(sp); 74 return (1); 75 case FSEARCH: 76 rval = v_chf(sp, vp); 77 break; 78 case fSEARCH: 79 rval = v_chF(sp, vp); 80 break; 81 case TSEARCH: 82 rval = v_cht(sp, vp); 83 break; 84 case tSEARCH: 85 rval = v_chT(sp, vp); 86 break; 87 default: 88 abort(); 89 } 90 VIP(sp)->csearchdir = savedir; 91 return (rval); 92 } 93 94 /* 95 * v_cht -- [count]tc 96 * Search forward in the line for the character before the next 97 * occurrence of the specified character. 98 * 99 * PUBLIC: int v_cht(SCR *, VICMD *); 100 */ 101 int 102 v_cht(SCR *sp, VICMD *vp) 103 { 104 if (v_chf(sp, vp)) 105 return (1); 106 107 /* 108 * v_chf places the cursor on the character, where the 't' 109 * command wants it to its left. We know this is safe since 110 * we had to move right for v_chf() to have succeeded. 111 */ 112 --vp->m_stop.cno; 113 114 /* 115 * Make any necessary correction to the motion decision made 116 * by the v_chf routine. 117 */ 118 if (!ISMOTION(vp)) 119 vp->m_final = vp->m_stop; 120 121 VIP(sp)->csearchdir = tSEARCH; 122 return (0); 123 } 124 125 /* 126 * v_chf -- [count]fc 127 * Search forward in the line for the next occurrence of the 128 * specified character. 129 * 130 * PUBLIC: int v_chf(SCR *, VICMD *); 131 */ 132 int 133 v_chf(SCR *sp, VICMD *vp) 134 { 135 size_t len; 136 u_long cnt; 137 int isempty; 138 ARG_CHAR_T key; 139 CHAR_T *endp, *p, *startp; 140 141 /* 142 * !!! 143 * If it's a dot command, it doesn't reset the key for which we're 144 * searching, e.g. in "df1|f2|.|;", the ';' searches for a '2'. 145 */ 146 key = vp->character; 147 if (!F_ISSET(vp, VC_ISDOT)) 148 VIP(sp)->lastckey = key; 149 VIP(sp)->csearchdir = fSEARCH; 150 151 if (db_eget(sp, vp->m_start.lno, &p, &len, &isempty)) { 152 if (isempty) 153 goto empty; 154 return (1); 155 } 156 157 if (len == 0) { 158 empty: notfound(sp, key); 159 return (1); 160 } 161 162 endp = (startp = p) + len; 163 p += vp->m_start.cno; 164 for (cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; cnt--;) { 165 while (++p < endp && *p != key); 166 if (p == endp) { 167 notfound(sp, key); 168 return (1); 169 } 170 } 171 172 vp->m_stop.cno = p - startp; 173 174 /* 175 * Non-motion commands move to the end of the range. 176 * Delete and yank stay at the start, ignore others. 177 */ 178 vp->m_final = ISMOTION(vp) ? vp->m_start : vp->m_stop; 179 return (0); 180 } 181 182 /* 183 * v_chT -- [count]Tc 184 * Search backward in the line for the character after the next 185 * occurrence of the specified character. 186 * 187 * PUBLIC: int v_chT(SCR *, VICMD *); 188 */ 189 int 190 v_chT(SCR *sp, VICMD *vp) 191 { 192 if (v_chF(sp, vp)) 193 return (1); 194 195 /* 196 * Check whether the matching character is to the immediate left 197 * of the original cursor position, offset adjusted for a motion 198 * command. If so, no movement is required. 199 */ 200 if (vp->m_start.cno == vp->m_stop.cno) { 201 return (1); 202 } 203 204 /* 205 * v_chF places the cursor on the character, where the 'T' 206 * command wants it to its right. We know this is safe since 207 * we had to move left for v_chF() to have succeeded. 208 */ 209 ++vp->m_stop.cno; 210 vp->m_final = vp->m_stop; 211 212 VIP(sp)->csearchdir = TSEARCH; 213 return (0); 214 } 215 216 /* 217 * v_chF -- [count]Fc 218 * Search backward in the line for the next occurrence of the 219 * specified character. 220 * 221 * PUBLIC: int v_chF(SCR *, VICMD *); 222 */ 223 int 224 v_chF(SCR *sp, VICMD *vp) 225 { 226 size_t len; 227 u_long cnt; 228 int isempty; 229 ARG_CHAR_T key; 230 CHAR_T *endp, *p; 231 232 /* 233 * !!! 234 * If it's a dot command, it doesn't reset the key for which 235 * we're searching, e.g. in "df1|f2|.|;", the ';' searches 236 * for a '2'. 237 */ 238 key = vp->character; 239 if (!F_ISSET(vp, VC_ISDOT)) 240 VIP(sp)->lastckey = key; 241 VIP(sp)->csearchdir = FSEARCH; 242 243 if (db_eget(sp, vp->m_start.lno, &p, &len, &isempty)) { 244 if (isempty) 245 goto empty; 246 return (1); 247 } 248 249 if (len == 0) { 250 empty: notfound(sp, key); 251 return (1); 252 } 253 254 endp = p - 1; 255 p += vp->m_start.cno; 256 for (cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; cnt--;) { 257 while (--p > endp && *p != key); 258 if (p == endp) { 259 notfound(sp, key); 260 return (1); 261 } 262 } 263 264 vp->m_stop.cno = (p - endp) - 1; 265 266 /* 267 * All commands move to the end of the range. Motion commands 268 * adjust the starting point to the character before the current 269 * one. 270 */ 271 vp->m_final = vp->m_stop; 272 if (ISMOTION(vp)) 273 --vp->m_start.cno; 274 return (0); 275 } 276 277 static void 278 noprev(SCR *sp) 279 { 280 msgq(sp, M_BERR, "178|No previous F, f, T or t search"); 281 } 282 283 static void 284 notfound(SCR *sp, ARG_CHAR_T ch) 285 { 286 msgq(sp, M_BERR, "179|%s not found", KEY_NAME(sp, ch)); 287 } 288