1/*  Completion.c is generated from completion.rb by the program rbgen
2    (cf. http://libredblack.sourceforge.net/)
3
4    completion.rb: maintaining the completion list, my_completion_function()
5    (callback for readline lib)
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License , or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with this program; see the file COPYING.  If not, write to
19    the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
20
21    You may contact the author by:
22       e-mail:  hanslub42@gmail.com
23*/
24
25#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
26#pragma GCC diagnostic ignored "-Wunused-function"
27
28
29#include "rlwrap.h"
30
31#ifdef assert
32#undef assert
33#endif
34
35
36int completion_is_case_sensitive = 1;
37
38int
39compare(const char *string1, const char *string2)
40{
41  const char *p1;
42  const char *p2;
43  int count;
44
45  for (p1 = string1, p2 = string2, count = 0;
46       *p1 && *p2 && count < BUFFSIZE; p1++, p2++, count++) {
47    char c1 = completion_is_case_sensitive ? *p1 : tolower(*p1);
48    char c2 = completion_is_case_sensitive ? *p2 : tolower(*p2);
49
50    if (c1 != c2)
51      return (c1 < c2 ? -1 : 1);
52  }
53  if ((*p1 && *p2) || (!*p1 && !*p2))
54    return 0;
55  return (*p1 ? 1 : -1);
56}
57
58
59
60
61
62#ifdef malloc
63#  undef malloc
64#endif
65#define malloc(x) mymalloc(x) /* This is a bit evil, but there is no other way to make libredblack use mymalloc() */
66
67
68
69/* This file has to be processed by the program rbgen  */
70
71%%rbgen
72%type char
73%cmp compare
74%access pointer
75%static
76%omit
77%%rbgen
78
79
80
81
82/* forward declarations */
83static struct rbtree *completion_tree;
84
85
86static char *my_history_completion_function(char *prefix, int state);
87static void print_list(void);
88
89
90static void
91my_rbdestroy(struct rbtree *rb)
92{				/* destroy rb tree, freeing the keys first */
93  const char *key, *lastkey;
94
95  for (key = rbmin(rb);
96       key;
97       lastkey = key, key =
98       rblookup(RB_LUGREAT, key, rb), free((void *)lastkey))
99    rbdelete(key, rb);
100  rbdestroy(rb);
101}
102
103
104static void
105print_list()
106{
107  const char *word;
108  RBLIST *completion_list = rbopenlist(completion_tree);	/* uses mymalloc() internally, so no chance of getting a NULL pointer back */
109
110  printf("Completions:\n");
111  while ((word = rbreadlist(completion_list)))
112    printf("%s\n", word);
113  rbcloselist(completion_list);
114}
115
116static char *rbtree_to_string(const struct rbtree *rb, int max_items) {
117   const char *word;
118   char  *result = NULL;
119   int i;
120   RBLIST *list = rbopenlist(rb);
121   for (i = 0; (word = rbreadlist(list)) && (i < max_items); i++) {
122      if (i > 0)
123         result = append_and_free_old(result, ", ");
124      result = append_and_free_old(result, word);
125   }
126   if (i >= max_items)
127      result = append_and_free_old(result, "...");
128   rbcloselist(list);
129   return result;
130}
131
132void
133init_completer()
134{
135  completion_tree = rbinit();
136}
137
138
139void
140add_word_to_completions(const char *word)
141{
142  rbsearch(mysavestring(word), completion_tree);	/* the tree stores *pointers* to the words, we have to allocate copies of them ourselves
143							   freeing the tree will call free on the pointers to the words
144							   valgrind reports the copies as lost, I don't understand this.' */
145}
146
147
148void
149remove_word_from_completions(const char *word)
150{
151  free((char *) rbdelete(word, completion_tree));  /* why does rbdelete return a const *? I want to be able to free it! */
152}
153
154void
155feed_line_into_completion_list(const char *line)
156{
157
158  char **words = split_with(line, rl_basic_word_break_characters);
159  char **plist, *word;
160  for(plist = words;(word = *plist); plist++)
161    add_word_to_completions(word);
162  free_splitlist(words);
163}
164
165void
166feed_file_into_completion_list(const char *completions_file)
167{
168  FILE *compl_fp;
169  char buffer[BUFFSIZE];
170
171  if ((compl_fp = fopen(completions_file, "r")) == NULL)
172    myerror(FATAL|USE_ERRNO, "Could not open %s", completions_file);
173  while (fgets(buffer, BUFFSIZE - 1, compl_fp) != NULL) {
174    buffer[BUFFSIZE - 1] = '\0';	/* make sure buffer is properly terminated (it should be anyway, according to ANSI) */
175    feed_line_into_completion_list(buffer);
176  }
177  if (! feof(compl_fp) && ferror(compl_fp))   /* at least in GNU libc, errno will be set in this case. If not, no harm is done */
178    myerror(FATAL|USE_ERRNO, "Couldn't read completions from %s", completions_file);
179
180  fclose(compl_fp);
181  /* print_list(); */
182}
183
184
185#define COMPLETE_FILENAMES 1
186#define COMPLETE_FROM_LIST 2
187#define COMPLETE_USERNAMES 4
188#define FILTER_COMPLETIONS 8
189#define COMPLETE_PARANORMALLY 16 /* read user's thoughts */
190
191
192
193int
194get_completion_type()
195{				/* some day, this function will inspect the current line and make rlwrap complete
196				   differently according to the word *preceding* the one we're completing ' */
197  return (COMPLETE_FROM_LIST | (complete_filenames ? COMPLETE_FILENAMES : 0) | (filter_pid ? FILTER_COMPLETIONS : 0));
198}
199
200
201/* helper function for my_completion_function */
202static int
203is_prefix(const char *s0, const char *s1)
204{				/* s0 is prefix of s1 */
205  const char *p0, *p1;
206  int count;
207
208  for (count = 0, p0 = s0, p1 = s1; *p0; count++, p0++, p1++) {
209    char c0 = completion_is_case_sensitive ? *p0 : tolower(*p0);
210    char c1 = completion_is_case_sensitive ? *p1 : tolower(*p1);
211
212    if (c0 != c1 || count == BUFFSIZE)
213      return FALSE;
214  }
215  return TRUE;
216}
217
218/* See readline doumentation: this function is called by readline whenever a completion is needed. The first time state == 0,
219   whwnever the user presses TAB to cycle through the list, my_completion_function() is called again, but then with state != 0
220   It should return the completion, which then will be freed by readline (so we'll hand back a copy instead of the real thing) ' */
221
222
223char *
224my_completion_function(char *prefix, int state)
225{
226  static struct rbtree *scratch_tree = NULL;
227  static RBLIST *scratch_list = NULL;	/* should remain unchanged between invocations */
228  int completion_type, count;
229  const char *word;
230  const char *completion;
231
232  rl_completion_append_character = *extra_char_after_completion;
233
234  /* if (*prefix == '!')
235    return my_history_completion_function(prefix + 1, state); */
236
237  if (state == 0) {		/* first time we're called for this prefix ' */
238
239    if (scratch_list)
240      rbcloselist(scratch_list);
241    if (scratch_tree)
242      my_rbdestroy(scratch_tree);
243    scratch_tree = rbinit();	/* allocate scratch_tree. We will use this to get a sorted list of completions */
244    /* now find all possible completions: */
245    completion_type = get_completion_type();
246    DPRINTF2(DEBUG_ALL, "completion_type: %d, filter_pid: %d", completion_type, filter_pid);
247    if (completion_type & COMPLETE_FROM_LIST) {
248      for (word = rblookup(RB_LUGTEQ, prefix, completion_tree);	/* start with first word >= prefix */
249	   word && is_prefix(prefix, word);	/* as long as prefix is really prefix of word */
250	   word = rblookup(RB_LUGREAT, word, completion_tree)) {	/* find next word in list */
251	rbsearch(mysavestring(word), scratch_tree);	/* insert fresh copy of the word */
252	/* DPRINTF1(DEBUG_COMPLETION, "Adding %s to completion list ", word); */
253      }
254    }
255    if (completion_type & COMPLETE_FILENAMES) {
256      change_working_directory();
257      DPRINTF1(DEBUG_COMPLETION, "Starting milking of rl_filename_completion_function, prefix = <%s> ", prefix);
258      for (count = 0;
259	   (word = copy_and_free_string_for_malloc_debug(rl_filename_completion_function(prefix, count)));
260	   count++) {	/* using rl_filename_completion_function means
261			   that completing filenames will always be case-sensitive */
262        DPRINTF1(DEBUG_COMPLETION, "Adding <%s> to completion list ", word);
263	rbsearch(word, scratch_tree);
264      }
265    }
266
267    scratch_list = rbopenlist(scratch_tree); /* OK, we now have our list with completions. We may have to filter it ... */
268    if (completion_type & FILTER_COMPLETIONS) {
269      char *filter_food = NULL;
270      char *filtered, **filtered_components, **plist;
271      int count;
272
273      /* build the "filter food" (input for the filter) as a field list
274         <rl_line_buffer><prefix><completion1><completion2>....                    */
275      filter_food = append_field_and_free_old(filter_food, rl_line_buffer);
276      filter_food = append_field_and_free_old(filter_food, prefix);
277      while((completion = rbreadlist(scratch_list)))
278        filter_food = append_field_and_free_old(filter_food, completion);
279
280      filtered = pass_through_filter(TAG_COMPLETION, filter_food);
281      free(filter_food);
282      rbcloselist(scratch_list);
283      DPRINTF1(DEBUG_ALL, "Filtered: %s", mangle_string_for_debug_log(filtered, 40));
284
285      filtered_components = split_filter_message(filtered, &count);
286      free(filtered);
287
288      if ( count <2 || strcmp(filtered_components[0], rl_line_buffer) ||  strcmp(filtered_components[1], prefix))
289	  myerror(FATAL|NOERRNO, "filter has illegally messed with completion message\n"); /* it should ONLY have changed the completion word list  */
290
291
292      my_rbdestroy(scratch_tree); /* burn the old scratch tree (but leave the completion tree alone)  */
293      scratch_tree = rbinit();    /* now grow a new one */
294
295      for(plist = filtered_components + 2; *plist; plist++) {
296        if (!**plist)
297          continue; /* empty space at beginning or end of the word list results in an empty word, ignore those now */
298        rbsearch(mysavestring(*plist), scratch_tree); /* add the filtered completions to the new scratch tree */
299        DPRINTF1(DEBUG_COMPLETION, "Adding %s to completion list ", *plist);
300      }
301      free_splitlist(filtered_components);
302      scratch_list = rbopenlist(scratch_tree);      /* flatten the tree into a new list */
303      DPRINTF1(DEBUG_COMPLETION, "scratch list: %s", rbtree_to_string(scratch_tree, 6));
304    } /* if (completion_type & FILTER_COMPLETIONS) */
305  } /* if state ==  0 */
306
307  /* we get here each time the user presses TAB to cycle through the list */
308  assert(scratch_tree != NULL);
309  assert(scratch_list != NULL);
310  if ((completion = rbreadlist(scratch_list))) {	/* read next possible completion */
311    struct stat buf;
312    char *copy_for_readline = malloc_foreign(strlen(completion)+1);
313    strcpy(copy_for_readline, completion);
314
315
316    rl_filename_completion_desired = rl_filename_quoting_desired = (stat(completion, &buf) ? FALSE : TRUE);
317
318    DPRINTF1(DEBUG_COMPLETION, "Returning completion to readline: <%s>", copy_for_readline);
319    return copy_for_readline;	/* we cannot just return the original as  readline will free it (and make rlwrap explode) */
320  } else {
321    return NULL;
322  }
323}
324
325
326
327
328static char *
329my_history_completion_function(char *prefix, int state)
330{
331  while (next_history());
332  if (state || history_search_prefix(prefix, -1) < 0)
333    return NULL;
334  return mysavestring(current_history()->line);
335}
336
337
338
339
340
341
342
343/* The following sets edit modes for GNU EMACS
344   Local Variables:
345   mode:c
346   End:  */
347