root/src/noit_console_complete.c

Revision 88a71780101cbf23034aa0cb840f9f0368fda2dd, 9.2 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 6 years ago)

fixes #126

  • Property mode set to 100644
Line 
1 /*
2  * Copyright (c) 2007, OmniTI Computer Consulting, Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  *     * Redistributions of source code must retain the above copyright
10  *       notice, this list of conditions and the following disclaimer.
11  *     * Redistributions in binary form must reproduce the above
12  *       copyright notice, this list of conditions and the following
13  *       disclaimer in the documentation and/or other materials provided
14  *       with the distribution.
15  *     * Neither the name OmniTI Computer Consulting, Inc. nor the names
16  *       of its contributors may be used to endorse or promote products
17  *       derived from this software without specific prior written
18  *       permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32
33 #include "noit_defines.h"
34
35 #include "eventer/eventer.h"
36 #include "utils/noit_log.h"
37 #include "noit_listener.h"
38 #include "noit_console.h"
39 #include "noit_tokenizer.h"
40
41 #include "noitedit/sys.h"
42 #include "noitedit/el.h"
43 #include "noitedit/fcns.h"
44 #include "noitedit/map.h"
45
46 typedef char *NCPFunction(EditLine *, const char *, int);
47
48 char *
49 noit_console_opt_delegate(noit_console_closure_t ncct,
50                           noit_console_state_stack_t *stack,
51                           noit_console_state_t *state,
52                           int argc, char **argv,
53                           int idx) {
54   int i;
55   noit_skiplist_node *next, *curr;
56   cmd_info_t *cmd;
57
58   if(state == NULL) return NULL;
59   i = 0;
60   if(argc == 0) {
61     for(next = noit_skiplist_getlist(&state->cmds); next;
62         noit_skiplist_next(&state->cmds,&next)) {
63       cmd = next->data;
64       if(idx == i) return strdup(cmd->name);
65       i++;
66     }
67   }
68
69   cmd = noit_skiplist_find_neighbors(&state->cmds, argv[0],
70                                      NULL, &curr, &next);
71   if(cmd) {
72     if(argc != 1) {
73       if(!cmd->autocomplete) return NULL;
74       return cmd->autocomplete(ncct, stack, cmd->dstate, argc-1, argv+1, idx);
75     }
76     next = curr;
77     goto multiples;
78   }
79
80  multiples:
81   if(!next) return NULL;
82   i = 0;
83   while(next) {
84     cmd = next->data;
85     if(strncasecmp(cmd->name, argv[0], strlen(argv[0])) == 0) {
86       if(idx == i) return strdup(cmd->name);
87       i++;
88     }
89     noit_skiplist_next(&state->cmds, &next);
90   }
91   return NULL;
92 }
93
94
95 static char *
96 noitedit_completion_function(EditLine *el, const char *text, int state) {
97   noit_console_closure_t ncct;
98   const LineInfo *li;
99   char **cmds, *curstr;
100   int len, i, cnt = 32;
101
102   li = el_line(el);
103   len = li->cursor - li->buffer;
104   curstr = alloca(len + 1);
105   memcpy(curstr, li->buffer, len);
106   curstr[len] = '\0';
107
108   cmds = alloca(32 * sizeof(*cmds));
109   i = noit_tokenize(curstr, cmds, &cnt);
110
111   el_get(el, EL_USERDATA, (void *)&ncct);
112
113   if(!strlen(text)) {
114     cmds[cnt++] = "";
115   }
116   if(cnt == 32) return NULL;
117   return noit_console_opt_delegate(ncct, ncct->state_stack,
118                                    ncct->state_stack->state,
119                                    cnt, cmds, state);
120 }
121 static int
122 _edit_qsort_string_compare(i1, i2)
123         const void *i1, *i2;
124 {
125         /*LINTED const castaway*/
126         const char *s1 = ((const char **)i1)[0];
127         /*LINTED const castaway*/
128         const char *s2 = ((const char **)i2)[0];
129
130         return strcasecmp(s1, s2);
131 }
132 static char **
133 completion_matches(EditLine *el, const char *text, NCPFunction *genfunc) {
134   char **match_list = NULL, *retstr, *prevstr;
135   size_t match_list_len, max_equal, which, i;
136   int matches;
137
138   matches = 0;
139   match_list_len = 1;
140   while ((retstr = (*genfunc) (el, text, matches)) != NULL) {
141     if(matches + 1 >= match_list_len) {
142       match_list_len <<= 1;
143       match_list = realloc(match_list,
144                            match_list_len * sizeof(char *));
145     }
146     match_list[++matches] = retstr;
147   }
148
149   if(!match_list)
150     return (char **) NULL;  /* nothing found */
151
152   /* find least denominator and insert it to match_list[0] */
153   which = 2;
154   prevstr = match_list[1];
155   max_equal = strlen(prevstr);
156   for(; which <= matches; which++) {
157     for(i = 0; i < max_equal && prevstr[i] == match_list[which][i]; i++)
158       continue;
159     max_equal = i;
160   }
161
162   retstr = malloc(max_equal + 1);
163   (void) strncpy(retstr, match_list[1], max_equal);
164   retstr[max_equal] = '\0';
165   match_list[0] = retstr;
166
167   /* add NULL as last pointer to the array */
168   if(matches + 1 >= match_list_len)
169     match_list = realloc(match_list,
170                          (match_list_len + 1) * sizeof(char *));
171   match_list[matches + 1] = (char *) NULL;
172
173   return (match_list);
174 }
175
176 static void
177 noit_edit_display_match_list (EditLine *el, char **matches, int len, int max)
178 {
179   int i, idx, limit, count;
180   int screenwidth = el->el_term.t_size.h;
181
182   /*
183    * Find out how many entries can be put on one line, count
184    * with two spaces between strings.
185    */
186   limit = screenwidth / (max + 2);
187   if(limit == 0)
188     limit = 1;
189
190   /* how many lines of output */
191   count = len / limit;
192   if(count * limit < len)
193     count++;
194
195   /* Sort the items if they are not already sorted. */
196   qsort(&matches[1], (size_t)(len - 1), sizeof(char *),
197         _edit_qsort_string_compare);
198
199   idx = 1;
200   for(; count > 0; count--) {
201     for(i=0; i < limit && matches[idx]; i++, idx++)
202       el->el_std_printf(el, "%-*s  ", max, matches[idx]);
203     el->el_std_printf(el, "\r\n");
204   }
205 }
206
207 unsigned char
208 noit_edit_complete(EditLine *el, int invoking_key) {
209   static const char *rl_basic_word_break_characters = " \t\n\"\\'@$><=;|&{(";
210   static const char *rl_special_prefixes = NULL;
211   static const int   rl_completion_append_character = ' ';
212   noit_console_closure_t ncct;
213   const LineInfo *li;
214   char *temp, **matches;
215   const char *ctemp;
216   int method = '\t';
217   size_t len;
218
219   el_get(el, EL_USERDATA, (void *)&ncct);
220
221   if(el->el_state.lastcmd == ncct->noit_edit_complete_cmdnum)
222     method = '?';
223
224   /* We now look backwards for the start of a filename/variable word */
225   li = el_line(el);
226   ctemp = (const char *) li->cursor;
227   while (ctemp > li->buffer
228       && !strchr(rl_basic_word_break_characters, ctemp[-1])
229       && (!rl_special_prefixes
230       || !strchr(rl_special_prefixes, ctemp[-1]) ) )
231     ctemp--;
232
233   len = li->cursor - ctemp;
234   temp = alloca(len + 1);
235   (void) strncpy(temp, ctemp, len);
236   temp[len] = '\0';
237
238   /* these can be used by function called in completion_matches() */
239   /* or (*rl_attempted_completion_function)() */
240   ncct->rl_point = li->cursor - li->buffer;
241   ncct->rl_end = li->lastchar - li->buffer;
242
243   matches = completion_matches(el, temp, noitedit_completion_function);
244
245   if (matches) {
246     int i, retval = CC_REFRESH;
247     int matches_num, maxlen, match_len;
248
249     /*
250      * Only replace the completed string with common part of
251      * possible matches if there is possible completion.
252      */
253     if (matches[0][0] != '\0') {
254       el_deletestr(el, (int) len);
255       el_insertstr(el, matches[0]);
256     }
257
258     if (method == '?')
259       goto display_matches;
260
261     if (matches[2] == NULL && strcmp(matches[0], matches[1]) == 0) {
262       /*
263        * We found exact match. Add a space after
264        * it, unless we do filename completition and the
265        * object is a directory.
266        */
267       size_t alen = strlen(matches[0]);
268       if ((alen > 0 && (matches[0])[alen - 1] != '/')
269           && rl_completion_append_character) {
270         char buf[2];
271         buf[0] = rl_completion_append_character;
272         buf[1] = '\0';
273         el_insertstr(el, buf);
274       }
275     } else if (method == '!') {
276     display_matches:
277       /*
278        * More than one match and requested to list possible
279        * matches.
280        */
281
282       for(i=1, maxlen=0; matches[i]; i++) {
283         match_len = strlen(matches[i]);
284         if (match_len > maxlen)
285           maxlen = match_len;
286       }
287       matches_num = i - 1;
288        
289       /* newline to get on next line from command line */
290       el->el_std_printf(el, "\r\n");
291
292       noit_edit_display_match_list(el, matches, matches_num, maxlen);
293       retval = CC_REDISPLAY;
294     } else if (matches[0][0]) {
295       /*
296        * There was some common match, but the name was
297        * not complete enough. Next tab will print possible
298        * completions.
299        */
300       el_beep(el);
301     } else {
302       /* lcd is not a valid object - further specification */
303       /* is needed */
304       el_beep(el);
305       retval = CC_NORM;
306     }
307
308     /* free elements of array and the array itself */
309     for (i = 0; matches[i]; i++)
310       free(matches[i]);
311     free(matches), matches = NULL;
312
313     return (retval);
314   }
315   return (CC_NORM);
316 }
Note: See TracBrowser for help on using the browser.