119304Speter/*- 219304Speter * Copyright (c) 1992, 1993, 1994 319304Speter * The Regents of the University of California. All rights reserved. 419304Speter * Copyright (c) 1992, 1993, 1994, 1995, 1996 519304Speter * Keith Bostic. All rights reserved. 619304Speter * 719304Speter * See the LICENSE file for redistribution information. 819304Speter */ 919304Speter 1019304Speter#include "config.h" 1119304Speter 1219304Speter#ifndef lint 1319304Speterstatic const char sccsid[] = "@(#)v_ch.c 10.8 (Berkeley) 3/6/96"; 1419304Speter#endif /* not lint */ 1519304Speter 1619304Speter#include <sys/types.h> 1719304Speter#include <sys/queue.h> 1819304Speter#include <sys/time.h> 1919304Speter 2019304Speter#include <bitstring.h> 2119304Speter#include <limits.h> 2219304Speter#include <stdio.h> 2319304Speter#include <stdlib.h> 2419304Speter 2519304Speter#include "../common/common.h" 2619304Speter#include "vi.h" 2719304Speter 2819304Speterstatic void notfound __P((SCR *, ARG_CHAR_T)); 2919304Speterstatic void noprev __P((SCR *)); 3019304Speter 3119304Speter/* 3219304Speter * v_chrepeat -- [count]; 3319304Speter * Repeat the last F, f, T or t search. 3419304Speter * 3519304Speter * PUBLIC: int v_chrepeat __P((SCR *, VICMD *)); 3619304Speter */ 3719304Speterint 3819304Speterv_chrepeat(sp, vp) 3919304Speter SCR *sp; 4019304Speter VICMD *vp; 4119304Speter{ 4219304Speter vp->character = VIP(sp)->lastckey; 4319304Speter 4419304Speter switch (VIP(sp)->csearchdir) { 4519304Speter case CNOTSET: 4619304Speter noprev(sp); 4719304Speter return (1); 4819304Speter case FSEARCH: 4919304Speter return (v_chF(sp, vp)); 5019304Speter case fSEARCH: 5119304Speter return (v_chf(sp, vp)); 5219304Speter case TSEARCH: 5319304Speter return (v_chT(sp, vp)); 5419304Speter case tSEARCH: 5519304Speter return (v_cht(sp, vp)); 5619304Speter default: 5719304Speter abort(); 5819304Speter } 5919304Speter /* NOTREACHED */ 6019304Speter} 6119304Speter 6219304Speter/* 6319304Speter * v_chrrepeat -- [count], 6419304Speter * Repeat the last F, f, T or t search in the reverse direction. 6519304Speter * 6619304Speter * PUBLIC: int v_chrrepeat __P((SCR *, VICMD *)); 6719304Speter */ 6819304Speterint 6919304Speterv_chrrepeat(sp, vp) 7019304Speter SCR *sp; 7119304Speter VICMD *vp; 7219304Speter{ 7319304Speter cdir_t savedir; 7419304Speter int rval; 7519304Speter 7619304Speter vp->character = VIP(sp)->lastckey; 7719304Speter savedir = VIP(sp)->csearchdir; 7819304Speter 7919304Speter switch (VIP(sp)->csearchdir) { 8019304Speter case CNOTSET: 8119304Speter noprev(sp); 8219304Speter return (1); 8319304Speter case FSEARCH: 8419304Speter rval = v_chf(sp, vp); 8519304Speter break; 8619304Speter case fSEARCH: 8719304Speter rval = v_chF(sp, vp); 8819304Speter break; 8919304Speter case TSEARCH: 9019304Speter rval = v_cht(sp, vp); 9119304Speter break; 9219304Speter case tSEARCH: 9319304Speter rval = v_chT(sp, vp); 9419304Speter break; 9519304Speter default: 9619304Speter abort(); 9719304Speter } 9819304Speter VIP(sp)->csearchdir = savedir; 9919304Speter return (rval); 10019304Speter} 10119304Speter 10219304Speter/* 10319304Speter * v_cht -- [count]tc 10419304Speter * Search forward in the line for the character before the next 10519304Speter * occurrence of the specified character. 10619304Speter * 10719304Speter * PUBLIC: int v_cht __P((SCR *, VICMD *)); 10819304Speter */ 10919304Speterint 11019304Speterv_cht(sp, vp) 11119304Speter SCR *sp; 11219304Speter VICMD *vp; 11319304Speter{ 11419304Speter if (v_chf(sp, vp)) 11519304Speter return (1); 11619304Speter 11719304Speter /* 11819304Speter * v_chf places the cursor on the character, where the 't' 11919304Speter * command wants it to its left. We know this is safe since 12019304Speter * we had to move right for v_chf() to have succeeded. 12119304Speter */ 12219304Speter --vp->m_stop.cno; 12319304Speter 12419304Speter /* 12519304Speter * Make any necessary correction to the motion decision made 12619304Speter * by the v_chf routine. 12719304Speter */ 12819304Speter if (!ISMOTION(vp)) 12919304Speter vp->m_final = vp->m_stop; 13019304Speter 13119304Speter VIP(sp)->csearchdir = tSEARCH; 13219304Speter return (0); 13319304Speter} 13419304Speter 13519304Speter/* 13619304Speter * v_chf -- [count]fc 13719304Speter * Search forward in the line for the next occurrence of the 13819304Speter * specified character. 13919304Speter * 14019304Speter * PUBLIC: int v_chf __P((SCR *, VICMD *)); 14119304Speter */ 14219304Speterint 14319304Speterv_chf(sp, vp) 14419304Speter SCR *sp; 14519304Speter VICMD *vp; 14619304Speter{ 14719304Speter size_t len; 14819304Speter u_long cnt; 14919304Speter int isempty, key; 15019304Speter char *endp, *p, *startp; 15119304Speter 15219304Speter /* 15319304Speter * !!! 15419304Speter * If it's a dot command, it doesn't reset the key for which we're 15519304Speter * searching, e.g. in "df1|f2|.|;", the ';' searches for a '2'. 15619304Speter */ 15719304Speter key = vp->character; 15819304Speter if (!F_ISSET(vp, VC_ISDOT)) 15919304Speter VIP(sp)->lastckey = key; 16019304Speter VIP(sp)->csearchdir = fSEARCH; 16119304Speter 16219304Speter if (db_eget(sp, vp->m_start.lno, &p, &len, &isempty)) { 16319304Speter if (isempty) 16419304Speter goto empty; 16519304Speter return (1); 16619304Speter } 16719304Speter 16819304Speter if (len == 0) { 16919304Speterempty: notfound(sp, key); 17019304Speter return (1); 17119304Speter } 17219304Speter 17319304Speter endp = (startp = p) + len; 17419304Speter p += vp->m_start.cno; 17519304Speter for (cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; cnt--;) { 17619304Speter while (++p < endp && *p != key); 17719304Speter if (p == endp) { 17819304Speter notfound(sp, key); 17919304Speter return (1); 18019304Speter } 18119304Speter } 18219304Speter 18319304Speter vp->m_stop.cno = p - startp; 18419304Speter 18519304Speter /* 18619304Speter * Non-motion commands move to the end of the range. 18719304Speter * Delete and yank stay at the start, ignore others. 18819304Speter */ 18919304Speter vp->m_final = ISMOTION(vp) ? vp->m_start : vp->m_stop; 19019304Speter return (0); 19119304Speter} 19219304Speter 19319304Speter/* 19419304Speter * v_chT -- [count]Tc 19519304Speter * Search backward in the line for the character after the next 19619304Speter * occurrence of the specified character. 19719304Speter * 19819304Speter * PUBLIC: int v_chT __P((SCR *, VICMD *)); 19919304Speter */ 20019304Speterint 20119304Speterv_chT(sp, vp) 20219304Speter SCR *sp; 20319304Speter VICMD *vp; 20419304Speter{ 20519304Speter if (v_chF(sp, vp)) 20619304Speter return (1); 20719304Speter 20819304Speter /* 20919304Speter * v_chF places the cursor on the character, where the 'T' 21019304Speter * command wants it to its right. We know this is safe since 21119304Speter * we had to move left for v_chF() to have succeeded. 21219304Speter */ 21319304Speter ++vp->m_stop.cno; 21419304Speter vp->m_final = vp->m_stop; 21519304Speter 21619304Speter VIP(sp)->csearchdir = TSEARCH; 21719304Speter return (0); 21819304Speter} 21919304Speter 22019304Speter/* 22119304Speter * v_chF -- [count]Fc 22219304Speter * Search backward in the line for the next occurrence of the 22319304Speter * specified character. 22419304Speter * 22519304Speter * PUBLIC: int v_chF __P((SCR *, VICMD *)); 22619304Speter */ 22719304Speterint 22819304Speterv_chF(sp, vp) 22919304Speter SCR *sp; 23019304Speter VICMD *vp; 23119304Speter{ 23219304Speter size_t len; 23319304Speter u_long cnt; 23419304Speter int isempty, key; 23519304Speter char *endp, *p; 23619304Speter 23719304Speter /* 23819304Speter * !!! 23919304Speter * If it's a dot command, it doesn't reset the key for which 24019304Speter * we're searching, e.g. in "df1|f2|.|;", the ';' searches 24119304Speter * for a '2'. 24219304Speter */ 24319304Speter key = vp->character; 24419304Speter if (!F_ISSET(vp, VC_ISDOT)) 24519304Speter VIP(sp)->lastckey = key; 24619304Speter VIP(sp)->csearchdir = FSEARCH; 24719304Speter 24819304Speter if (db_eget(sp, vp->m_start.lno, &p, &len, &isempty)) { 24919304Speter if (isempty) 25019304Speter goto empty; 25119304Speter return (1); 25219304Speter } 25319304Speter 25419304Speter if (len == 0) { 25519304Speterempty: notfound(sp, key); 25619304Speter return (1); 25719304Speter } 25819304Speter 25919304Speter endp = p - 1; 26019304Speter p += vp->m_start.cno; 26119304Speter for (cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; cnt--;) { 26219304Speter while (--p > endp && *p != key); 26319304Speter if (p == endp) { 26419304Speter notfound(sp, key); 26519304Speter return (1); 26619304Speter } 26719304Speter } 26819304Speter 26919304Speter vp->m_stop.cno = (p - endp) - 1; 27019304Speter 27119304Speter /* 27219304Speter * All commands move to the end of the range. Motion commands 27319304Speter * adjust the starting point to the character before the current 27419304Speter * one. 27519304Speter */ 27619304Speter vp->m_final = vp->m_stop; 27719304Speter if (ISMOTION(vp)) 27819304Speter --vp->m_start.cno; 27919304Speter return (0); 28019304Speter} 28119304Speter 28219304Speterstatic void 28319304Speternoprev(sp) 28419304Speter SCR *sp; 28519304Speter{ 28619304Speter msgq(sp, M_BERR, "178|No previous F, f, T or t search"); 28719304Speter} 28819304Speter 28919304Speterstatic void 29019304Speternotfound(sp, ch) 29119304Speter SCR *sp; 29219304Speter ARG_CHAR_T ch; 29319304Speter{ 29419304Speter msgq(sp, M_BERR, "179|%s not found", KEY_NAME(sp, ch)); 29519304Speter} 296