root/src/noit_console_complete.c

Revision 5fd1894ad04542945430bed21cc582a937f8edcb, 9.2 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 2 years ago)

a variety of memory leaks in console commands.

  • 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(cmd && 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, *rv = NULL;
100   int len, 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   cmds[0] = NULL;
110   (void) noit_tokenize(curstr, cmds, &cnt);
111
112   el_get(el, EL_USERDATA, (void *)&ncct);
113
114   if(!strlen(text)) {
115     cmds[cnt++] = strdup("");
116   }
117   if(cnt != 32)
118     rv = noit_console_opt_delegate(ncct, ncct->state_stack,
119                                    ncct->state_stack->state,
120                                    cnt, cmds, state);
121   if(cmds[0]) while(cnt > 0) free(cmds[--cnt]);
122   return rv;
123 }
124 static int
125 _edit_qsort_string_compare(i1, i2)
126         const void *i1, *i2;
127 {
128         /*LINTED const castaway*/
129         const char *s1 = ((const char **)i1)[0];
130         /*LINTED const castaway*/
131         const char *s2 = ((const char **)i2)[0];
132
133         return strcasecmp(s1, s2);
134 }
135 static char **
136 completion_matches(EditLine *el, const char *text, NCPFunction *genfunc) {
137   char **match_list = NULL, *retstr, *prevstr;
138   size_t match_list_len, max_equal, which, i;
139   int matches;
140
141   matches = 0;
142   match_list_len = 1;
143   while ((retstr = (*genfunc) (el, text, matches)) != NULL) {
144     if(matches + 1 >= match_list_len) {
145       match_list_len <<= 1;
146       match_list = realloc(match_list,
147                            match_list_len * sizeof(char *));
148     }
149     match_list[++matches] = retstr;
150   }
151
152   if(!match_list)
153     return (char **) NULL;  /* nothing found */
154
155   /* find least denominator and insert it to match_list[0] */
156   which = 2;
157   prevstr = match_list[1];
158   max_equal = strlen(prevstr);
159   for(; which <= matches; which++) {
160     for(i = 0; i < max_equal && prevstr[i] == match_list[which][i]; i++)
161       continue;
162     max_equal = i;
163   }
164
165   retstr = malloc(max_equal + 1);
166   (void) strncpy(retstr, match_list[1], max_equal);
167   retstr[max_equal] = '\0';
168   match_list[0] = retstr;
169
170   /* add NULL as last pointer to the array */
171   if(matches + 1 >= match_list_len)
172     match_list = realloc(match_list,
173                          (match_list_len + 1) * sizeof(char *));
174   match_list[matches + 1] = (char *) NULL;
175
176   return (match_list);
177 }
178
179 static void
180 noit_edit_display_match_list (EditLine *el, char **matches, int len, int max)
181 {
182   int i, idx, limit, count;
183   int screenwidth = el->el_term.t_size.h;
184
185   /*
186    * Find out how many entries can be put on one line, count
187    * with two spaces between strings.
188    */
189   limit = screenwidth / (max + 2);
190   if(limit == 0)
191     limit = 1;
192
193   /* how many lines of output */
194   count = len / limit;
195   if(count * limit < len)
196     count++;
197
198   /* Sort the items if they are not already sorted. */
199   qsort(&matches[1], (size_t)(len - 1), sizeof(char *),
200         _edit_qsort_string_compare);
201
202   idx = 1;
203   for(; count > 0; count--) {
204     for(i=0; i < limit && matches[idx]; i++, idx++)
205       el->el_std_printf(el, "%-*s  ", max, matches[idx]);
206     el->el_std_printf(el, "\r\n");
207   }
208 }
209
210 unsigned char
211 noit_edit_complete(EditLine *el, int invoking_key) {
212   static const char *rl_basic_word_break_characters = " \t\n\"\\'@$><=;|&{(";
213   static const char *rl_special_prefixes = NULL;
214   static const int   rl_completion_append_character = ' ';
215   noit_console_closure_t ncct;
216   const LineInfo *li;
217   char *temp, **matches;
218   const char *ctemp;
219   int method = '\t';
220   size_t len;
221
222   el_get(el, EL_USERDATA, (void *)&ncct);
223
224   if(el->el_state.lastcmd == ncct->noit_edit_complete_cmdnum)
225     method = '?';
226
227   /* We now look backwards for the start of a filename/variable word */
228   li = el_line(el);
229   ctemp = (const char *) li->cursor;
230   while (ctemp > li->buffer
231       && !strchr(rl_basic_word_break_characters, ctemp[-1])
232       && (!rl_special_prefixes
233       || !strchr(rl_special_prefixes, ctemp[-1]) ) )
234     ctemp--;
235
236   len = li->cursor - ctemp;
237   temp = alloca(len + 1);
238   (void) strncpy(temp, ctemp, len);
239   temp[len] = '\0';
240
241   /* these can be used by function called in completion_matches() */
242   /* or (*rl_attempted_completion_function)() */
243   ncct->rl_point = li->cursor - li->buffer;
244   ncct->rl_end = li->lastchar - li->buffer;
245
246   matches = completion_matches(el, temp, noitedit_completion_function);
247
248   if (matches) {
249     int i, retval = CC_REFRESH;
250     int matches_num, maxlen, match_len;
251
252     /*
253      * Only replace the completed string with common part of
254      * possible matches if there is possible completion.
255      */
256     if (matches[0][0] != '\0') {
257       el_deletestr(el, (int) len);
258       el_insertstr(el, matches[0]);
259     }
260
261     if (method == '?')
262       goto display_matches;
263
264     if (matches[2] == NULL && strcmp(matches[0], matches[1]) == 0) {
265       /*
266        * We found exact match. Add a space after
267        * it, unless we do filename completition and the
268        * object is a directory.
269        */
270       size_t alen = strlen(matches[0]);
271       if ((alen > 0 && (matches[0])[alen - 1] != '/')) {
272         char buf[2];
273         buf[0] = rl_completion_append_character;
274         buf[1] = '\0';
275         el_insertstr(el, buf);
276       }
277     } else if (method == '!') {
278     display_matches:
279       /*
280        * More than one match and requested to list possible
281        * matches.
282        */
283
284       for(i=1, maxlen=0; matches[i]; i++) {
285         match_len = strlen(matches[i]);
286         if (match_len > maxlen)
287           maxlen = match_len;
288       }
289       matches_num = i - 1;
290        
291       /* newline to get on next line from command line */
292       el->el_std_printf(el, "\r\n");
293
294       noit_edit_display_match_list(el, matches, matches_num, maxlen);
295       retval = CC_REDISPLAY;
296     } else if (matches[0][0]) {
297       /*
298        * There was some common match, but the name was
299        * not complete enough. Next tab will print possible
300        * completions.
301        */
302       el_beep(el);
303     } else {
304       /* lcd is not a valid object - further specification */
305       /* is needed */
306       el_beep(el);
307       retval = CC_NORM;
308     }
309
310     /* free elements of array and the array itself */
311     for (i = 0; matches[i]; i++)
312       free(matches[i]);
313     free(matches), matches = NULL;
314
315     return (retval);
316   }
317   return (CC_NORM);
318 }
Note: See TracBrowser for help on using the browser.