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