1/* parens.c -- implementation of matching parentheses feature. */ 2 3/* Copyright (C) 1987, 1989, 1992-2015, 2017 Free Software Foundation, Inc. 4 5 This file is part of the GNU Readline Library (Readline), a library 6 for reading lines of text with interactive input and history editing. 7 8 Readline is free software: you can redistribute it and/or modify 9 it under the terms of the GNU General Public License as published by 10 the Free Software Foundation, either version 3 of the License, or 11 (at your option) any later version. 12 13 Readline is distributed in the hope that it will be useful, 14 but WITHOUT ANY WARRANTY; without even the implied warranty of 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 GNU General Public License for more details. 17 18 You should have received a copy of the GNU General Public License 19 along with Readline. If not, see <http://www.gnu.org/licenses/>. 20*/ 21 22#define READLINE_LIBRARY 23 24#if defined (__TANDEM) 25# include <floss.h> 26#endif 27 28#include "rlconf.h" 29 30#if defined (HAVE_CONFIG_H) 31# include <config.h> 32#endif 33 34#include <stdio.h> 35#include <sys/types.h> 36 37#if defined (HAVE_UNISTD_H) 38# include <unistd.h> 39#endif 40 41#include "posixselect.h" 42 43#if defined (HAVE_STRING_H) 44# include <string.h> 45#else /* !HAVE_STRING_H */ 46# include <strings.h> 47#endif /* !HAVE_STRING_H */ 48 49#if !defined (strchr) && !defined (__STDC__) 50extern char *strchr (), *strrchr (); 51#endif /* !strchr && !__STDC__ */ 52 53#include "readline.h" 54#include "rlprivate.h" 55 56static int find_matching_open PARAMS((char *, int, int)); 57 58/* Non-zero means try to blink the matching open parenthesis when the 59 close parenthesis is inserted. */ 60int rl_blink_matching_paren = 0; 61 62static int _paren_blink_usec = 500000; 63 64/* Change emacs_standard_keymap to have bindings for paren matching when 65 ON_OR_OFF is 1, change them back to self_insert when ON_OR_OFF == 0. */ 66void 67_rl_enable_paren_matching (int on_or_off) 68{ 69 if (on_or_off) 70 { 71 /* ([{ */ 72 rl_bind_key_in_map (')', rl_insert_close, emacs_standard_keymap); 73 rl_bind_key_in_map (']', rl_insert_close, emacs_standard_keymap); 74 rl_bind_key_in_map ('}', rl_insert_close, emacs_standard_keymap); 75 76#if defined (VI_MODE) 77 /* ([{ */ 78 rl_bind_key_in_map (')', rl_insert_close, vi_insertion_keymap); 79 rl_bind_key_in_map (']', rl_insert_close, vi_insertion_keymap); 80 rl_bind_key_in_map ('}', rl_insert_close, vi_insertion_keymap); 81#endif 82 } 83 else 84 { 85 /* ([{ */ 86 rl_bind_key_in_map (')', rl_insert, emacs_standard_keymap); 87 rl_bind_key_in_map (']', rl_insert, emacs_standard_keymap); 88 rl_bind_key_in_map ('}', rl_insert, emacs_standard_keymap); 89 90#if defined (VI_MODE) 91 /* ([{ */ 92 rl_bind_key_in_map (')', rl_insert, vi_insertion_keymap); 93 rl_bind_key_in_map (']', rl_insert, vi_insertion_keymap); 94 rl_bind_key_in_map ('}', rl_insert, vi_insertion_keymap); 95#endif 96 } 97} 98 99int 100rl_set_paren_blink_timeout (int u) 101{ 102 int o; 103 104 o = _paren_blink_usec; 105 if (u > 0) 106 _paren_blink_usec = u; 107 return (o); 108} 109 110int 111rl_insert_close (int count, int invoking_key) 112{ 113 if (rl_explicit_arg || !rl_blink_matching_paren) 114 _rl_insert_char (count, invoking_key); 115 else 116 { 117#if defined (HAVE_SELECT) 118 int orig_point, match_point, ready; 119 struct timeval timer; 120 fd_set readfds; 121 122 _rl_insert_char (1, invoking_key); 123 (*rl_redisplay_function) (); 124 match_point = 125 find_matching_open (rl_line_buffer, rl_point - 2, invoking_key); 126 127 /* Emacs might message or ring the bell here, but I don't. */ 128 if (match_point < 0) 129 return 1; 130 131 FD_ZERO (&readfds); 132 FD_SET (fileno (rl_instream), &readfds); 133 USEC_TO_TIMEVAL (_paren_blink_usec, timer); 134 135 orig_point = rl_point; 136 rl_point = match_point; 137 (*rl_redisplay_function) (); 138 ready = select (1, &readfds, (fd_set *)NULL, (fd_set *)NULL, &timer); 139 rl_point = orig_point; 140#else /* !HAVE_SELECT */ 141 _rl_insert_char (count, invoking_key); 142#endif /* !HAVE_SELECT */ 143 } 144 return 0; 145} 146 147static int 148find_matching_open (char *string, int from, int closer) 149{ 150 register int i; 151 int opener, level, delimiter; 152 153 switch (closer) 154 { 155 case ']': opener = '['; break; 156 case '}': opener = '{'; break; 157 case ')': opener = '('; break; 158 default: 159 return (-1); 160 } 161 162 level = 1; /* The closer passed in counts as 1. */ 163 delimiter = 0; /* Delimited state unknown. */ 164 165 for (i = from; i > -1; i--) 166 { 167 if (delimiter && (string[i] == delimiter)) 168 delimiter = 0; 169 else if (rl_basic_quote_characters && strchr (rl_basic_quote_characters, string[i])) 170 delimiter = string[i]; 171 else if (!delimiter && (string[i] == closer)) 172 level++; 173 else if (!delimiter && (string[i] == opener)) 174 level--; 175 176 if (!level) 177 break; 178 } 179 return (i); 180} 181