45 guint len_prefix; // length of text already typed by the user |
45 guint len_prefix; // length of text already typed by the user |
46 guint len_compl; // length of the last completion |
46 guint len_compl; // length of the last completion |
47 GSList *next; // pointer to next completion to try |
47 GSList *next; // pointer to next completion to try |
48 } compl; |
48 } compl; |
49 |
49 |
|
50 typedef GSList *(*compl_handler_t) (void); // XXX userdata? *dynlist? |
|
51 |
50 // Category structure |
52 // Category structure |
51 typedef struct { |
53 typedef struct { |
52 guint64 flag; |
54 guint flags; |
53 GSList *words; |
55 GSList *words; |
|
56 compl_handler_t dynamic; |
54 } category; |
57 } category; |
55 |
58 |
56 static GSList *Categories; |
59 #define COMPL_CAT_BUILTIN 0x01 |
|
60 #define COMPL_CAT_ACTIVE 0x02 |
|
61 #define COMPL_CAT_DYNAMIC 0x04 |
|
62 #define COMPL_CAT_REVERSE 0x10 |
|
63 #define COMPL_CAT_NOSORT 0x20 |
|
64 |
|
65 #define COMPL_CAT_USERFLAGS 0x30 |
|
66 |
57 static compl *InputCompl; |
67 static compl *InputCompl; |
58 |
68 static category *Categories; |
|
69 static guint num_categories; |
|
70 |
|
71 // Dynamic completions callbacks |
|
72 static GSList *compl_dyn_group (void) |
|
73 { |
|
74 return compl_list(ROSTER_TYPE_GROUP); |
|
75 } |
|
76 |
|
77 static GSList *compl_dyn_user (void) |
|
78 { |
|
79 return compl_list(ROSTER_TYPE_USER); |
|
80 } |
|
81 |
|
82 static GSList *compl_dyn_resource (void) |
|
83 { |
|
84 return buddy_getresources_locale(NULL); |
|
85 } |
|
86 |
|
87 static GSList *compl_dyn_events (void) |
|
88 { |
|
89 GSList *compl = evs_geteventslist(); |
|
90 GSList *cel; |
|
91 for (cel = compl; cel; cel = cel->next) |
|
92 cel->data = g_strdup(cel->data); |
|
93 compl = g_slist_append(compl, g_strdup("list")); |
|
94 return compl; |
|
95 } |
|
96 |
|
97 static inline void register_builtin_cat(guint c, compl_handler_t dynamic) { |
|
98 Categories[c-1].flags = COMPL_CAT_BUILTIN | COMPL_CAT_ACTIVE; |
|
99 Categories[c-1].words = NULL; |
|
100 Categories[c-1].dynamic = dynamic; |
|
101 if (dynamic != NULL) { |
|
102 Categories[c-1].flags |= COMPL_CAT_DYNAMIC; |
|
103 } |
|
104 } |
|
105 |
|
106 void compl_init_system(void) |
|
107 { |
|
108 num_categories = COMPL_MAX_ID; |
59 #ifdef MODULES_ENABLE |
109 #ifdef MODULES_ENABLE |
60 static guint64 registered_cats; |
110 num_categories = ((num_categories / 16) + 1) * 16; |
61 |
111 #endif |
62 static inline void register_builtin_cat(guint c) { |
112 Categories = g_new0(category, num_categories); |
63 registered_cats |= 1UL << (c-1); |
113 |
64 } |
|
65 |
|
66 void compl_init_system(void) |
|
67 { |
|
68 // Builtin completion categories: |
114 // Builtin completion categories: |
69 register_builtin_cat(COMPL_CMD); |
115 register_builtin_cat(COMPL_CMD, NULL); |
70 register_builtin_cat(COMPL_JID); |
116 register_builtin_cat(COMPL_JID, compl_dyn_user); |
71 register_builtin_cat(COMPL_URLJID); |
117 register_builtin_cat(COMPL_URLJID, NULL); |
72 register_builtin_cat(COMPL_NAME); |
118 register_builtin_cat(COMPL_NAME, NULL); |
73 register_builtin_cat(COMPL_STATUS); |
119 register_builtin_cat(COMPL_STATUS, NULL); |
74 register_builtin_cat(COMPL_FILENAME); |
120 register_builtin_cat(COMPL_FILENAME, NULL); |
75 register_builtin_cat(COMPL_ROSTER); |
121 register_builtin_cat(COMPL_ROSTER, NULL); |
76 register_builtin_cat(COMPL_BUFFER); |
122 register_builtin_cat(COMPL_BUFFER, NULL); |
77 register_builtin_cat(COMPL_GROUP); |
123 register_builtin_cat(COMPL_GROUP, NULL); |
78 register_builtin_cat(COMPL_GROUPNAME); |
124 register_builtin_cat(COMPL_GROUPNAME, compl_dyn_group); |
79 register_builtin_cat(COMPL_MULTILINE); |
125 register_builtin_cat(COMPL_MULTILINE, NULL); |
80 register_builtin_cat(COMPL_ROOM); |
126 register_builtin_cat(COMPL_ROOM, NULL); |
81 register_builtin_cat(COMPL_RESOURCE); |
127 register_builtin_cat(COMPL_RESOURCE, compl_dyn_resource); |
82 register_builtin_cat(COMPL_AUTH); |
128 register_builtin_cat(COMPL_AUTH, NULL); |
83 register_builtin_cat(COMPL_REQUEST); |
129 register_builtin_cat(COMPL_REQUEST, NULL); |
84 register_builtin_cat(COMPL_EVENTS); |
130 register_builtin_cat(COMPL_EVENTS, NULL); |
85 register_builtin_cat(COMPL_EVENTSID); |
131 register_builtin_cat(COMPL_EVENTSID, compl_dyn_events); |
86 register_builtin_cat(COMPL_PGP); |
132 register_builtin_cat(COMPL_PGP, NULL); |
87 register_builtin_cat(COMPL_COLOR); |
133 register_builtin_cat(COMPL_COLOR, NULL); |
88 register_builtin_cat(COMPL_OTR); |
134 register_builtin_cat(COMPL_OTR, NULL); |
89 register_builtin_cat(COMPL_OTRPOLICY); |
135 register_builtin_cat(COMPL_OTRPOLICY, NULL); |
90 register_builtin_cat(COMPL_MODULE); |
136 register_builtin_cat(COMPL_MODULE, NULL); |
91 } |
137 } |
92 |
138 |
93 // compl_new_category() |
139 #ifdef MODULES_ENABLE |
|
140 // compl_new_category(flags) |
94 // Reserves id for new completion category. |
141 // Reserves id for new completion category. |
|
142 // Flags determine word sorting order. |
95 // Returns 0, if no more categories can be allocated. |
143 // Returns 0, if no more categories can be allocated. |
96 // Note, that user should not make any assumptions about id nature, |
144 guint compl_new_category(guint flags) |
97 // as it is likely to change in future. |
145 { |
98 guint compl_new_category(void) |
146 guint i; |
99 { |
147 for (i = 0; i < num_categories; i++) |
100 const guint maxcat = 8 * sizeof (registered_cats); |
148 if (!(Categories[i].flags & COMPL_CAT_ACTIVE)) |
101 guint i = 0; |
149 break; |
102 while ((registered_cats >> i) & 1 && i < maxcat) |
150 if (i >= num_categories ) { |
103 i++; |
151 guint j; |
104 if (i >= maxcat) |
152 if (num_categories > G_MAXUINT - 16) { |
105 return 0; |
153 scr_log_print(LPRINT_LOGNORM, "Warning: Too many " |
106 else { |
154 "completion categories!"); |
107 guint64 id = 1 << i; |
155 return 0; |
108 registered_cats |= id; |
156 } |
109 return i+1; |
157 num_categories += 16; |
110 } |
158 Categories = g_renew(category, Categories, num_categories); |
|
159 for (j = i+1; j < num_categories; j++) |
|
160 Categories[j].flags = 0; |
|
161 } |
|
162 Categories[i].flags = COMPL_CAT_ACTIVE | (flags & COMPL_CAT_USERFLAGS); |
|
163 Categories[i].words = NULL; |
|
164 return i+1; |
111 } |
165 } |
112 |
166 |
113 // compl_del_category(id) |
167 // compl_del_category(id) |
114 // Frees reserved id for category. |
168 // Frees reserved id for category. |
115 // Note, that for now it not validates its input, so, be careful |
169 // Note, that for now it not validates its input, so, be careful |
116 // and specify exactly what you get from compl_new_category. |
170 // and specify exactly what you get from compl_new_category. |
117 void compl_del_category(guint id) |
171 void compl_del_category(guint compl) |
118 { |
172 { |
119 if (!id) { |
173 GSList *wel; |
120 scr_log_print(LPRINT_LOGNORM, "Error: compl_del_category() - " |
174 |
121 "Invalid category."); |
175 if (!compl) { |
122 return; |
176 scr_log_print(LPRINT_DEBUG, "Error: compl_del_category() - " |
123 } |
177 "Invalid category (0)."); |
124 id--; |
178 return; |
125 registered_cats &= ~(1<<id); |
179 } |
|
180 |
|
181 compl--; |
|
182 |
|
183 if ((compl >= num_categories) || |
|
184 (Categories[compl].flags & COMPL_CAT_BUILTIN)) { |
|
185 scr_log_print(LPRINT_DEBUG, "Error: compl_del_category() " |
|
186 "Invalid category."); |
|
187 return; |
|
188 } |
|
189 |
|
190 Categories[compl].flags = 0; |
|
191 for (wel = Categories[compl].words; wel; wel = g_slist_next (wel)) |
|
192 g_free (wel -> data); |
|
193 g_slist_free (Categories[compl].words); |
126 } |
194 } |
127 #endif |
195 #endif |
128 |
196 |
129 // new_completion(prefix, compl_cat, suffix) |
197 // new_completion(prefix, compl_cat, suffix) |
130 // . prefix = beginning of the word, typed by the user |
198 // . prefix = beginning of the word, typed by the user |
220 } |
292 } |
221 |
293 |
222 |
294 |
223 /* Categories functions */ |
295 /* Categories functions */ |
224 |
296 |
|
297 static gint compl_sort_forward(gconstpointer a, gconstpointer b) |
|
298 { |
|
299 return g_ascii_strcasecmp((const gchar *)a, (const gchar *)b); |
|
300 } |
|
301 |
|
302 static gint compl_sort_reverse(gconstpointer a, gconstpointer b) |
|
303 { |
|
304 return -g_ascii_strcasecmp((const gchar *)a, (const gchar *)b); |
|
305 } |
|
306 |
|
307 static gint compl_sort_append(gconstpointer a, gconstpointer b) |
|
308 { |
|
309 return 1; |
|
310 } |
|
311 |
|
312 static gint compl_sort_prepend(gconstpointer a, gconstpointer b) |
|
313 { |
|
314 return -1; |
|
315 } |
|
316 |
225 // compl_add_category_word(categ, command) |
317 // compl_add_category_word(categ, command) |
226 // Adds a keyword as a possible completion in category categ. |
318 // Adds a keyword as a possible completion in category categ. |
227 void compl_add_category_word(guint categ, const gchar *word) |
319 void compl_add_category_word(guint categ, const gchar *word) |
228 { |
320 { |
229 guint64 catv; |
|
230 GSList *sl_cat; |
|
231 category *cat; |
|
232 char *nword; |
321 char *nword; |
233 |
322 |
234 if (!categ) { |
323 if (!categ) { |
235 scr_log_print(LPRINT_LOGNORM, "Error: compl_add_category_word() - " |
324 scr_log_print(LPRINT_DEBUG, "Error: compl_add_category_word() - " |
236 "Invalid category."); |
325 "Invalid category (0)."); |
237 return; |
326 return; |
238 } |
327 } |
239 |
328 |
240 categ--; |
329 categ--; |
241 catv = 1UL << categ; |
330 |
242 |
331 if ((categ >= num_categories) || |
243 // Look for category |
332 !(Categories[categ].flags & COMPL_CAT_ACTIVE)) { |
244 for (sl_cat=Categories; sl_cat; sl_cat = g_slist_next(sl_cat)) { |
333 scr_log_print(LPRINT_DEBUG, "Error: compl_add_category_word() - " |
245 if (catv == ((category*)sl_cat->data)->flag) |
334 "Category does not exist."); |
246 break; |
335 return; |
247 } |
336 } |
248 if (!sl_cat) { // Category not found, let's create it |
|
249 cat = g_new0(category, 1); |
|
250 cat->flag = catv; |
|
251 Categories = g_slist_append(Categories, cat); |
|
252 } else |
|
253 cat = (category*)sl_cat->data; |
|
254 |
337 |
255 // If word is not space-terminated, we add one trailing space |
338 // If word is not space-terminated, we add one trailing space |
256 for (nword = (char*)word; *nword; nword++) |
339 for (nword = (char*)word; *nword; nword++) |
257 ; |
340 ; |
258 if (nword > word) nword--; |
341 if (nword > word) nword--; |
260 nword = g_strdup_printf("%s ", word); |
343 nword = g_strdup_printf("%s ", word); |
261 } else { // word is fine |
344 } else { // word is fine |
262 nword = g_strdup(word); |
345 nword = g_strdup(word); |
263 } |
346 } |
264 |
347 |
265 if (g_slist_find_custom(cat->words, nword, (GCompareFunc)g_strcmp0) != NULL) |
348 if (g_slist_find_custom(Categories[categ].words, nword, |
266 return; |
349 (GCompareFunc)g_strcmp0) == NULL) { |
267 |
350 guint flags = Categories[categ].flags; |
268 cat->words = g_slist_insert_sorted(cat->words, nword, |
351 GCompareFunc comparator = compl_sort_forward; |
269 (GCompareFunc)g_ascii_strcasecmp); |
352 if (flags & COMPL_CAT_NOSORT) { |
|
353 if (flags & COMPL_CAT_REVERSE) |
|
354 comparator = compl_sort_prepend; |
|
355 else |
|
356 comparator = compl_sort_append; |
|
357 } else if (flags & COMPL_CAT_REVERSE) |
|
358 comparator = compl_sort_reverse; |
|
359 |
|
360 Categories[categ].words = g_slist_insert_sorted |
|
361 (Categories[categ].words, nword, comparator); |
|
362 } |
270 } |
363 } |
271 |
364 |
272 // compl_del_category_word(categ, command) |
365 // compl_del_category_word(categ, command) |
273 // Removes a keyword from category categ in completion list. |
366 // Removes a keyword from category categ in completion list. |
274 void compl_del_category_word(guint categ, const gchar *word) |
367 void compl_del_category_word(guint categ, const gchar *word) |
275 { |
368 { |
276 guint64 catv; |
369 GSList *wel; |
277 GSList *sl_cat, *sl_elt; |
|
278 category *cat; |
|
279 char *nword; |
370 char *nword; |
280 |
371 |
281 if (!categ) { |
372 if (!categ) { |
282 scr_log_print(LPRINT_LOGNORM, "Error: compl_del_category_word() - " |
373 scr_log_print(LPRINT_DEBUG, "Error: compl_del_category_word() - " |
283 "Invalid category."); |
374 "Invalid category (0)."); |
284 return; |
375 return; |
285 } |
376 } |
286 |
377 |
287 categ--; |
378 categ--; |
288 catv = 1UL << categ; |
379 |
289 |
380 if ((categ >= num_categories) || |
290 // Look for category |
381 !(Categories[categ].flags & COMPL_CAT_ACTIVE)) { |
291 for (sl_cat=Categories; sl_cat; sl_cat = g_slist_next(sl_cat)) { |
382 scr_log_print(LPRINT_DEBUG, "Error: compl_del_category_word() - " |
292 if (catv == ((category*)sl_cat->data)->flag) |
383 "Category does not exist."); |
293 break; |
384 return; |
294 } |
385 } |
295 if (!sl_cat) return; // Category not found, finished! |
|
296 |
|
297 cat = (category*)sl_cat->data; |
|
298 |
386 |
299 // If word is not space-terminated, we add one trailing space |
387 // If word is not space-terminated, we add one trailing space |
300 for (nword = (char*)word; *nword; nword++) |
388 for (nword = (char*)word; *nword; nword++) |
301 ; |
389 ; |
302 if (nword > word) nword--; |
390 if (nword > word) nword--; |
303 if (*nword != ' ') { // Add a space |
391 if (*nword != ' ') // Add a space |
304 nword = g_strdup_printf("%s ", word); |
392 word = nword = g_strdup_printf("%s ", word); |
305 } else { // word is fine |
393 else |
306 nword = g_strdup(word); |
394 nword = NULL; |
307 } |
395 |
308 |
396 for (wel = Categories[categ].words; wel; wel = g_slist_next (wel)) { |
309 sl_elt = cat->words; |
397 if (!strcasecmp((char*)wel->data, word)) { |
310 while (sl_elt) { |
398 g_free(wel->data); |
311 if (!strcasecmp((char*)sl_elt->data, nword)) { |
399 Categories[categ].words = g_slist_delete_link |
312 g_free(sl_elt->data); |
400 (Categories[categ].words, wel); |
313 cat->words = g_slist_delete_link(cat->words, sl_elt); |
|
314 break; // Only remove first occurence |
401 break; // Only remove first occurence |
315 } |
402 } |
316 sl_elt = g_slist_next(sl_elt); |
403 } |
317 } |
404 |
|
405 g_free (nword); |
318 } |
406 } |
319 |
407 |
320 // compl_get_category_list() |
408 // compl_get_category_list() |
321 // Returns a slist of all words in the specified categorie. |
409 // Returns a slist of all words in the specified categorie. |
322 // Iff this function sets *dynlist to TRUE, then the caller must free the |
410 // Iff this function sets *dynlist to TRUE, then the caller must free the |
323 // whole list after use. |
411 // whole list after use. |
324 GSList *compl_get_category_list(guint categ, guint *dynlist) |
412 GSList *compl_get_category_list(guint categ, guint *dynlist) |
325 { |
413 { |
326 guint64 cat_flags; |
|
327 GSList *sl_cat; |
|
328 |
|
329 if (!categ) { |
414 if (!categ) { |
330 scr_log_print(LPRINT_LOGNORM, "Error: compl_get_category_list() - " |
415 scr_log_print(LPRINT_DEBUG, "Error: compl_get_category_list() - " |
331 "Invalid category."); |
416 "Invalid category (0)."); |
332 return NULL; |
417 return NULL; |
333 } |
418 } |
334 |
419 |
335 *dynlist = FALSE; |
420 categ --; |
336 cat_flags = 1UL << (categ - 1); |
421 |
337 |
422 if ((categ > num_categories) || |
338 // Look for the category |
423 !(Categories[categ].flags & COMPL_CAT_ACTIVE)) { |
339 for (sl_cat=Categories; sl_cat; sl_cat = g_slist_next(sl_cat)) { |
424 scr_log_print(LPRINT_DEBUG, "Error: compl_get_category_list() - " |
340 if (cat_flags == ((category*)sl_cat->data)->flag) |
425 "Category does not exist."); |
341 break; |
426 return NULL; |
342 } |
427 } |
343 if (sl_cat) // Category was found, easy... |
428 |
344 return ((category*)sl_cat->data)->words; |
429 if (Categories[categ].flags & COMPL_CAT_DYNAMIC) { |
345 |
430 *dynlist = TRUE; |
346 // Handle dynamic SLists |
431 return (*Categories[categ].dynamic) (); |
347 *dynlist = TRUE; |
432 } else { |
348 if (categ == COMPL_GROUPNAME) { |
433 *dynlist = FALSE; |
349 return compl_list(ROSTER_TYPE_GROUP); |
434 return Categories[categ].words; |
350 } |
435 } |
351 if (categ == COMPL_JID) { |
|
352 return compl_list(ROSTER_TYPE_USER); |
|
353 } |
|
354 if (categ == COMPL_RESOURCE) { |
|
355 return buddy_getresources_locale(NULL); |
|
356 } |
|
357 if (categ == COMPL_EVENTSID) { |
|
358 GSList *compl = evs_geteventslist(); |
|
359 GSList *cel; |
|
360 for (cel = compl; cel; cel = cel->next) |
|
361 cel->data = g_strdup(cel->data); |
|
362 compl = g_slist_append(compl, g_strdup("list")); |
|
363 return compl; |
|
364 } |
|
365 |
|
366 *dynlist = FALSE; |
|
367 return NULL; |
|
368 } |
436 } |
369 |
437 |
370 /* vim: set expandtab cindent cinoptions=>2\:2(0 sw=2 ts=2: For Vim users... */ |
438 /* vim: set expandtab cindent cinoptions=>2\:2(0 sw=2 ts=2: For Vim users... */ |