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