1 /* $NetBSD: v_match.c,v 1.2 2013/11/22 15:52:06 christos Exp $ */ 2 /*- 3 * Copyright (c) 1992, 1993, 1994 4 * The Regents of the University of California. All rights reserved. 5 * Copyright (c) 1992, 1993, 1994, 1995, 1996 6 * Keith Bostic. All rights reserved. 7 * 8 * See the LICENSE file for redistribution information. 9 */ 10 11 #include "config.h" 12 13 #ifndef lint 14 static const char sccsid[] = "Id: v_match.c,v 10.10 2001/06/25 15:19:32 skimo Exp (Berkeley) Date: 2001/06/25 15:19:32 "; 15 #endif /* not lint */ 16 17 #include <sys/types.h> 18 #include <sys/queue.h> 19 #include <sys/time.h> 20 21 #include <bitstring.h> 22 #include <limits.h> 23 #include <stdio.h> 24 #include <string.h> 25 26 #include "../common/common.h" 27 #include "vi.h" 28 29 /* 30 * v_match -- % 31 * Search to matching character. 32 * 33 * PUBLIC: int v_match __P((SCR *, VICMD *)); 34 */ 35 int 36 v_match(SCR *sp, VICMD *vp) 37 { 38 VCS cs; 39 MARK *mp; 40 size_t cno, len, off; 41 int cnt, isempty, matchc, startc, (*gc)__P((SCR *, VCS *)); 42 CHAR_T *p; 43 char *cp; 44 const char *match_chars; 45 46 static MARK match = { 0, 0 }; 47 static int match_dir; 48 49 /* 50 * Historically vi would match (), {} and [] however 51 * an update included <>. This is ok for editing HTML 52 * but a pain in the butt for C source. 53 * Making it an option lets the user decide what is 'right'. 54 * Also fixed to do something sensible with "". 55 */ 56 match_chars = O_STR(sp, O_MATCHCHARS); 57 58 /* 59 * !!! 60 * Historic practice; ignore the count. 61 * 62 * !!! 63 * Historical practice was to search for the initial character in the 64 * forward direction only. 65 */ 66 if (db_eget(sp, vp->m_start.lno, &p, &len, &isempty)) { 67 if (isempty) 68 goto nomatch; 69 return (1); 70 } 71 for (off = vp->m_start.cno;; ++off) { 72 if (off >= len) { 73 nomatch: msgq(sp, M_BERR, "184|No match character on this line"); 74 return (1); 75 } 76 startc = p[off]; 77 cp = strchr(match_chars, startc); 78 if (cp != NULL) 79 break; 80 } 81 cnt = cp - match_chars; 82 matchc = match_chars[cnt ^ 1]; 83 84 /* Alternate back-forward search if startc and matchc the same */ 85 if (startc == matchc) { 86 /* are we continuing from where last match finished? */ 87 if (match.lno == vp->m_start.lno && match.cno ==vp->m_start.cno) 88 /* yes - continue in sequence */ 89 match_dir++; 90 else 91 /* no - go forward, back, back, forward */ 92 match_dir = 1; 93 if (match_dir & 2) 94 cnt++; 95 } 96 gc = cnt & 1 ? cs_prev : cs_next; 97 98 cs.cs_lno = vp->m_start.lno; 99 cs.cs_cno = off; 100 if (cs_init(sp, &cs)) 101 return (1); 102 for (cnt = 1;;) { 103 if (gc(sp, &cs)) 104 return (1); 105 if (cs.cs_flags != 0) { 106 if (cs.cs_flags == CS_EOF || cs.cs_flags == CS_SOF) 107 break; 108 continue; 109 } 110 if (cs.cs_ch == matchc && --cnt == 0) 111 break; 112 if (cs.cs_ch == startc) 113 ++cnt; 114 } 115 if (cnt) { 116 msgq(sp, M_BERR, "185|Matching character not found"); 117 return (1); 118 } 119 120 vp->m_stop.lno = cs.cs_lno; 121 vp->m_stop.cno = cs.cs_cno; 122 123 /* 124 * If moving right, non-motion commands move to the end of the range. 125 * Delete and yank stay at the start. 126 * 127 * If moving left, all commands move to the end of the range. 128 * 129 * !!! 130 * Don't correct for leftward movement -- historic vi deleted the 131 * starting cursor position when deleting to a match. 132 */ 133 if (vp->m_start.lno < vp->m_stop.lno || 134 (vp->m_start.lno == vp->m_stop.lno && 135 vp->m_start.cno < vp->m_stop.cno)) 136 vp->m_final = ISMOTION(vp) ? vp->m_start : vp->m_stop; 137 else 138 vp->m_final = vp->m_stop; 139 140 match.lno = vp->m_final.lno; 141 match.cno = vp->m_final.cno; 142 143 /* 144 * !!! 145 * If the motion is across lines, and the earliest cursor position 146 * is at or before any non-blank characters in the line, i.e. the 147 * movement is cutting all of the line's text, and the later cursor 148 * position has nothing other than whitespace characters between it 149 * and the end of its line, the buffer is in line mode. 150 */ 151 if (!ISMOTION(vp) || vp->m_start.lno == vp->m_stop.lno) 152 return (0); 153 mp = vp->m_start.lno < vp->m_stop.lno ? &vp->m_start : &vp->m_stop; 154 if (mp->cno != 0) { 155 cno = 0; 156 if (nonblank(sp, mp->lno, &cno)) 157 return (1); 158 if (cno < mp->cno) 159 return (0); 160 } 161 mp = vp->m_start.lno < vp->m_stop.lno ? &vp->m_stop : &vp->m_start; 162 if (db_get(sp, mp->lno, DBG_FATAL, &p, &len)) 163 return (1); 164 for (p += mp->cno + 1, len -= mp->cno; --len; ++p) 165 if (!ISBLANK((UCHAR_T)*p)) 166 return (0); 167 F_SET(vp, VM_LMODE); 168 return (0); 169 } 170