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