source: project/release/4/readline/trunk/interface.c @ 32692

Last change on this file since 32692 was 32692, checked in by Alexej Magura, 5 years ago

adds support for custom cflags

File size: 11.9 KB
Line 
1// (Readline is GPLed, so that makes this file GPLed too, because this
2// file will only run if it's linked against readline.)
3//
4// Copyright (c) 2002 Tony Garnock-Jones
5// Copyright (c) 2006 Heath Johns (paren bouncing and auto-completion code)
6// Copyright (c) 2014 Alexej Magura (added a lot of history functions and more)
7//
8// This program 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 2 of the License, or
11// (at your option) any later version.
12//
13// This program 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 this program; if not, write to the Free Software
20// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21
22#include <stdio.h>
23#include <stdlib.h>
24#include <stdbool.h>
25#include <string.h>
26#include <sys/poll.h>
27#include <errno.h>
28#include <alloca.h>
29#include <readline/readline.h>
30#include <readline/history.h>
31
32#include "foreign.h"
33#include "musthaves.h"
34
35static int gnu_readline_bounce_ms = 500;
36static int gnu_history_newlines = 0;
37static char *gnu_readline_buf = (void *)NULL;
38static bool rl_egg_clear = false;
39
40struct balance {
41     int paren[3];
42     int brace[3];
43     int quote;
44} balnc;
45
46static void clear_parbar(char token)
47{
48     int idx = 0;
49     for (; idx < 3; ++idx) {
50          if (token == '(' || token == ')')
51               balnc.paren[idx] = 0;
52          if (token == '[' || token == ']')
53               balnc.brace[idx] = 0;
54     }
55}
56
57static int parbar_in_string(char *str, char add_token)
58{
59     int *idxp = NULL;
60     char sub_token = '\0';
61     if (add_token == '(') {
62          idxp = balnc.paren;
63          sub_token = ')';
64     } else if (add_token == '[') {
65          idxp = balnc.brace;
66          sub_token = ']';
67     }
68
69     char *cp = strchr(str, '\0');
70
71     if (cp == str)
72          return 0;
73
74     do {
75          if (cp != str && cpeek(cp, str, false) == '\\')
76               continue;
77
78          RD_DBG("balnc.quote: %d\n", balnc.quote);
79          if (*cp == add_token && balnc.quote < 1) {
80               ++(*idxp); // increment total
81               ++(*(++idxp)); // move to open, and increment
82               --idxp; // move back to total
83          } else if (*cp == sub_token && balnc.quote < 1) {
84               --(*idxp); // deincrement total
85               ++idxp; // move to open
86               ++(*(++idxp)); // move to close and increment
87               --idxp; // move back to open
88               --idxp; // move back to total
89          }
90     } while (cp-- != str);
91     return *idxp;
92}
93
94static int quote_in_string(char *str)
95{
96  if (str == NULL)
97    return 0;
98
99  char *cp = strchr(str, '\0');
100
101  if (cp == str)
102    return 0;
103
104  do {
105    if (cp != str && cpeek(cp, str, false) == '\\')
106      continue;
107
108    if (*cp == '"') {
109      ++balnc.quote;
110    }
111  } while (cp-- != str);
112
113  if (balnc.quote == 0)
114    return -1;
115  RD_DBG("return balance.quote: %d\n", balnc.quote);
116  return balnc.quote % 2;
117}
118
119static HIST_ENTRY *last_history_entry(const bool del_current, const bool script)
120{
121     HIST_ENTRY *he;
122
123     using_history();
124#if RD_DEBUG
125     if ((he = current_history()) != 0)
126          RD_DBG("current history: `%s'\n", he->line);
127#endif
128     if (del_current && !(script)) {
129          if ((he = current_history()) != 0) {
130               RD_DBG("deleting: `%s'\n", he->line);
131               free_history_entry(remove_history(where_history()));
132          } else if ((he = previous_history()) != 0) {
133               RD_DBG("deleting: `%s'\n", he->line);
134               free_history_entry(remove_history(where_history()));
135          }
136     } else if (!script) {
137          previous_history();
138     }
139     he = previous_history();
140     using_history();
141     return he;
142}
143
144/* End of static functions */
145
146int gnu_readline_skip(int pos, char open_key, char close_key)
147{
148     while (--pos > -1) {
149          if (pos > 0 && rl_line_buffer[pos - 1] == '\\')
150               continue;
151          else if (rl_line_buffer[pos] == open_key)
152               return pos;
153          else if (rl_line_buffer[pos] == close_key)
154               pos = gnu_readline_skip(pos, open_key, close_key);
155          else if (rl_line_buffer[pos] == '"')
156               pos = gnu_readline_skip(pos, '"', '"');
157     }
158     return pos;
159}
160
161// Finds the matching paren (starting from just left of the cursor)
162int gnu_readline_find_match(char key)
163{
164     if (key == ')')
165          return gnu_readline_skip(rl_point - 1, '(', ')');
166     else if (key == ']')
167          return gnu_readline_skip(rl_point - 1, '[', ']');
168     return 0;
169}
170
171// Delays, but returns early if key press occurs
172void gnu_readline_timid_delay(int ms)
173{
174     struct pollfd pfd;
175
176     pfd.fd = fileno(rl_instream);
177     pfd.events = POLLIN | /* | */ POLLPRI;
178     pfd.revents = 0;
179     poll(&pfd, 1, ms);
180}
181/* *** END ***
182 * Inline Functions
183 */
184
185// Bounces the cursor to the matching paren for a while
186int gnu_readline_paren_bounce(int count, int key)
187{
188     int insert_success;
189     int old_point;
190     int matching;
191
192     if (gnu_readline_bounce_ms == 0)
193          return 0;
194
195     // Write the just entered paren out first
196     insert_success = rl_insert(count, key);
197     if (insert_success != 0)
198          return insert_success;
199     rl_redisplay();
200
201     // Need at least two chars to bounce...
202     if (rl_point < 2) // rl_point set to next char (implicit +1)
203          return 0;
204
205     // If it's an escaped paren, don't bounce...
206     if (rl_line_buffer[rl_point - 2] == '\\')
207          return 0;
208
209     // Bounce
210     old_point = rl_point;
211     matching = gnu_readline_find_match(key);
212     if (matching < 0)
213          return 0;
214     else
215          rl_point = matching;
216     rl_redisplay();
217     gnu_readline_timid_delay(gnu_readline_bounce_ms);
218     rl_point = old_point;
219     return 0;
220}
221
222#if 0
223void paren_match_highlights(char *match_color, char *no_match_color)
224{
225  paren_colors.with_match = (match_color[0] == '\0'
226      ? "\x1b[36m"
227      : match_color);
228  paren_colors.without_match = (no_match_color[0] == '\0'
229      ? "\x1b[35m"
230      : no_match_color);
231}
232#endif
233
234////\\\\//// Tab Completion ////\\\\////
235
236// Prototype for callback into scm
237#if !RD_TEST
238C_word gnu_readline_scm_complete(char *, int, int);
239
240// Gets called (repeatedly) when readline tries to do a completion
241// FIXME, I use _strncpy_, but I should probably use _strlcpy_ or _strncat_
242char *gnu_readline_tab_complete(const char *text, int status)
243{
244     C_word result;
245     char *str;
246     int len;
247     char *copied_str;
248
249  /* All of this is for older versions of chicken (< 2.3), which don't
250     reliably null-terminate strings */
251
252  // Get scheme string for possible completion via callback
253     result = gnu_readline_scm_complete((char *)text, strlen(text), status);
254
255     if (result == C_SCHEME_FALSE)
256          return NULL;
257
258     // Convert into C types
259     str = C_c_string(result);
260     len = C_num_to_int(C_i_string_length(result));
261
262     if (len == 0)
263          return NULL;
264
265     // Copy (note: the readline lib frees this copy)
266     copied_str = (char *)malloc(len + 1);
267     strncpy(copied_str, str, len); /* XXX this probably isn't the best, although it is safe
268                                       thanks to the next line, way to copy these strings. */
269     copied_str[len] = '\0';
270     return copied_str;
271}
272#endif
273
274// grants RO access to the gnu_history_newlines variable.
275int gnu_history_new_lines()
276{
277     return gnu_history_newlines;
278}
279
280int gnu_readline_append_history(char *filename)
281{
282     return append_history(gnu_history_newlines, filename);
283}
284
285#if 0
286void gnu_readline_highlight_matches()
287{
288  RD_DBG("match-position: %d\n", find_match(')'));
289}
290#endif
291#if 0
292char *gnu_make_arrow_code()
293{};
294#endif
295
296// Set everything up
297#if !RD_TEST
298void gnu_readline_init()
299{
300     using_history();
301     rl_bind_key(')', gnu_readline_paren_bounce);
302     rl_bind_key(']', gnu_readline_paren_bounce);
303#if 0
304     rl_bind_keyseq("[D", highlight_paren);
305#endif
306     rl_completion_entry_function = &gnu_readline_tab_complete;
307     rl_variable_bind("rl_catch_signals", 0);
308     rl_clear_signals();
309     rl_set_signals();
310     rl_completer_quote_characters = "\"";
311}
312
313inline void noop()
314{}
315
316#define RL_EGG_BUILDST(FAKE, RL_NAME, RL_VAR, RL_DO_1)                  \
317  FAKE ## RL_NAME (int set)                                             \
318  {                                                                     \
319    if (set == -1)                                                      \
320      goto check;                                                       \
321    else if (set == false)                                              \
322      RL_VAR && (RL_VAR = false);                                       \
323    else                                                                \
324      RL_VAR || (RL_VAR = true);                                        \
325  check:                                                                \
326    if (RL_VAR)                                                         \
327      RL_DO_1();                                                        \
328    return RL_VAR;                                                      \
329  }
330
331bool RL_EGG_BUILDST(, clear_hist, rl_egg_clear, clear_history);
332
333// Called from scheme to get user input
334char *gnu_readline_readline(char *prompt, char *prompt2, bool norec)
335{
336     HIST_ENTRY *entry;
337
338     if (gnu_readline_buf != NULL) {
339          free(gnu_readline_buf);
340          gnu_readline_buf = NULL;
341     }
342
343     if (!(balnc.quote || balnc.paren[0] || balnc.brace[0]))
344          gnu_readline_buf = readline(prompt);
345     else
346          gnu_readline_buf = readline(prompt2);
347
348     RD_DBG("norec: `%d'\n", norec);
349
350     if (norec)
351          goto skipped;
352
353     if (gnu_readline_buf != NULL && *gnu_readline_buf != '\0') {
354          entry = history_get(history_base + history_length - 1);
355          if (entry == NULL || strcmp(entry->line, gnu_readline_buf) != 0) {
356               add_history(gnu_readline_buf);
357               ++gnu_history_newlines;
358          }
359     }
360
361skipped:
362     if (rl_end > 0) {
363          int adx = quote_in_string(rl_line_buffer);
364          RD_DBG("quote_in_string: %d\n", adx);
365          int bdx = parbar_in_string(rl_line_buffer, '(');
366          int cdx = parbar_in_string(rl_line_buffer, '[');
367          balnc.quote = (adx == -1 ? 0 : adx);
368          if (bdx == -1)
369               clear_parbar('(');
370          if (cdx == -1)
371               clear_parbar('[');
372     }
373     clear_hist(-1);
374     return (gnu_readline_buf);
375}
376#endif
377
378bool int_to_bool(int boo)
379{
380     return (bool)boo;
381}
382
383void gnu_readline_signal_cleanup()
384{
385     clear_parbar('(');
386     clear_parbar('[');
387     free(gnu_readline_buf);
388     gnu_readline_buf = NULL;
389     rl_free_line_state();
390     rl_cleanup_after_signal();
391}
392
393char gnu_unclosed_exp()
394{
395     if (balnc.quote)
396          return 'q';
397     else if (balnc.brace[0])
398          return 'b';
399     else if (balnc.paren[0])
400          return 'p';
401     return '\0';
402}
403
404char *last_history_line(const bool del_current, const bool script)
405{
406     RD_DBG("del_current: `%d'\n", del_current);
407     RD_DBG("script: `%d'\n", script);
408     HIST_ENTRY *he;
409     if ((he = last_history_entry(del_current, script)) == 0)
410          return ((char *)NULL);
411     return he->line;
412}
413
414void insert_last_history_line(const bool del_current, const bool script, const bool add_eol)
415{
416     char *text = (add_eol
417                   ? cat(last_history_line(del_current, script), "\n")
418                   : last_history_line(del_current, script));
419     if (text != NULL) {
420          char *endp = strchr(text, '\0');
421          for (char *s = text; s != endp; ++s) {
422               rl_stuff_char(*s);
423          }
424     }
425     if (add_eol)
426          free(text);
427}
428
429char *current_history_line()
430{
431     HIST_ENTRY *he;
432     using_history();
433
434     if ((he = current_history()) == 0)
435          return ((char *)NULL);
436     return he->line;
437}
438
439bool gnu_histpmove(int pos)
440{
441     using_history();
442     if (pos == -1)
443          return (current_history() == NULL);
444     return (bool)history_set_pos(pos);
445}
446
447void run_last_history_line(const bool del_current, const bool script)
448{
449     insert_last_history_line(del_current, script, true);
450}
451
452void safely_remove_history(int pos)
453{
454     using_history();
455     free_history_entry(remove_history(pos));
456}
457
458/* safely concatenates the history_list's entry strings and returns them via a pointer */
459char *gnu_history_list() /* may look a bit messy, but it seems to work great ;D */
460{
461     HIST_ENTRY **hist_list = history_list();
462     int idx;
463
464     if (hist_list == NULL)
465          return NULL;
466
467     /* should be free'd by Chicken Scheme */
468     const size_t hsize = (history_total_bytes()+1);
469     char *rlist = malloc(hsize * (sizeof(*rlist)));
470     bzero(rlist, hsize);
471
472     catm(rlist, hsize-1, hist_list[0]->line, "\n");
473     RD_DBG("buf@%d: %s\n", __LINE__, rlist);
474
475     for (idx = 1; idx < history_length; ++idx) {
476          catm(rlist, hsize, rlist, hist_list[idx]->line, "\n");
477     }
478     return rlist;
479}
480
481int gnu_history_list_length()
482{
483     return history_length;
484}
Note: See TracBrowser for help on using the repository browser.