1 | /* GLIB - Library of useful routines for C programming |
2 | * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald |
3 | * |
4 | * This library is free software; you can redistribute it and/or |
5 | * modify it under the terms of the GNU Lesser General Public |
6 | * License as published by the Free Software Foundation; either |
7 | * version 2.1 of the License, or (at your option) any later version. |
8 | * |
9 | * This library is distributed in the hope that it will be useful, |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 | * Lesser General Public License for more details. |
13 | * |
14 | * You should have received a copy of the GNU Lesser General Public |
15 | * License along with this library; if not, see <http://www.gnu.org/licenses/>. |
16 | */ |
17 | |
18 | /* |
19 | * Modified by the GLib Team and others 1997-2000. See the AUTHORS |
20 | * file for a list of people on the GLib Team. See the ChangeLog |
21 | * files for a list of changes. These files are distributed with |
22 | * GLib at ftp://ftp.gtk.org/pub/gtk/. |
23 | */ |
24 | |
25 | /* |
26 | * MT safe |
27 | */ |
28 | |
29 | #include "config.h" |
30 | |
31 | /* we know we are deprecated here, no need for warnings */ |
32 | #ifndef GLIB_DISABLE_DEPRECATION_WARNINGS |
33 | #define GLIB_DISABLE_DEPRECATION_WARNINGS |
34 | #endif |
35 | |
36 | #include "gcompletion.h" |
37 | |
38 | #include <glib/gstrfuncs.h> |
39 | #include <glib/gmessages.h> |
40 | #include <glib/gunicode.h> |
41 | |
42 | #include <string.h> |
43 | |
44 | /** |
45 | * SECTION:completion |
46 | * @title: Automatic String Completion |
47 | * @short_description: support for automatic completion using a group |
48 | * of target strings |
49 | * |
50 | * #GCompletion provides support for automatic completion of a string |
51 | * using any group of target strings. It is typically used for file |
52 | * name completion as is common in many UNIX shells. |
53 | * |
54 | * A #GCompletion is created using g_completion_new(). Target items are |
55 | * added and removed with g_completion_add_items(), |
56 | * g_completion_remove_items() and g_completion_clear_items(). A |
57 | * completion attempt is requested with g_completion_complete() or |
58 | * g_completion_complete_utf8(). When no longer needed, the |
59 | * #GCompletion is freed with g_completion_free(). |
60 | * |
61 | * Items in the completion can be simple strings (e.g. filenames), or |
62 | * pointers to arbitrary data structures. If data structures are used |
63 | * you must provide a #GCompletionFunc in g_completion_new(), which |
64 | * retrieves the item's string from the data structure. You can change |
65 | * the way in which strings are compared by setting a different |
66 | * #GCompletionStrncmpFunc in g_completion_set_compare(). |
67 | * |
68 | * GCompletion has been marked as deprecated, since this API is rarely |
69 | * used and not very actively maintained. |
70 | **/ |
71 | |
72 | /** |
73 | * GCompletion: |
74 | * @items: list of target items (strings or data structures). |
75 | * @func: function which is called to get the string associated with a |
76 | * target item. It is %NULL if the target items are strings. |
77 | * @prefix: the last prefix passed to g_completion_complete() or |
78 | * g_completion_complete_utf8(). |
79 | * @cache: the list of items which begin with @prefix. |
80 | * @strncmp_func: The function to use when comparing strings. Use |
81 | * g_completion_set_compare() to modify this function. |
82 | * |
83 | * The data structure used for automatic completion. |
84 | **/ |
85 | |
86 | /** |
87 | * GCompletionFunc: |
88 | * @Param1: the completion item. |
89 | * |
90 | * Specifies the type of the function passed to g_completion_new(). It |
91 | * should return the string corresponding to the given target item. |
92 | * This is used when you use data structures as #GCompletion items. |
93 | * |
94 | * Returns: the string corresponding to the item. |
95 | **/ |
96 | |
97 | /** |
98 | * GCompletionStrncmpFunc: |
99 | * @s1: string to compare with @s2. |
100 | * @s2: string to compare with @s1. |
101 | * @n: maximal number of bytes to compare. |
102 | * |
103 | * Specifies the type of the function passed to |
104 | * g_completion_set_compare(). This is used when you use strings as |
105 | * #GCompletion items. |
106 | * |
107 | * Returns: an integer less than, equal to, or greater than zero if |
108 | * the first @n bytes of @s1 is found, respectively, to be |
109 | * less than, to match, or to be greater than the first @n |
110 | * bytes of @s2. |
111 | **/ |
112 | |
113 | static void completion_check_cache (GCompletion* cmp, |
114 | gchar** new_prefix); |
115 | |
116 | /** |
117 | * g_completion_new: |
118 | * @func: the function to be called to return the string representing |
119 | * an item in the #GCompletion, or %NULL if strings are going to |
120 | * be used as the #GCompletion items. |
121 | * |
122 | * Creates a new #GCompletion. |
123 | * |
124 | * Returns: the new #GCompletion. |
125 | **/ |
126 | GCompletion* |
127 | g_completion_new (GCompletionFunc func) |
128 | { |
129 | GCompletion* gcomp; |
130 | |
131 | gcomp = g_new (GCompletion, 1); |
132 | gcomp->items = NULL; |
133 | gcomp->cache = NULL; |
134 | gcomp->prefix = NULL; |
135 | gcomp->func = func; |
136 | gcomp->strncmp_func = strncmp; |
137 | |
138 | return gcomp; |
139 | } |
140 | |
141 | /** |
142 | * g_completion_add_items: |
143 | * @cmp: the #GCompletion. |
144 | * @items: (transfer none): the list of items to add. |
145 | * |
146 | * Adds items to the #GCompletion. |
147 | * |
148 | * Deprecated: 2.26: Rarely used API |
149 | **/ |
150 | void |
151 | g_completion_add_items (GCompletion* cmp, |
152 | GList* items) |
153 | { |
154 | GList* it; |
155 | |
156 | g_return_if_fail (cmp != NULL); |
157 | |
158 | /* optimize adding to cache? */ |
159 | if (cmp->cache) |
160 | { |
161 | g_list_free (list: cmp->cache); |
162 | cmp->cache = NULL; |
163 | } |
164 | |
165 | if (cmp->prefix) |
166 | { |
167 | g_free (mem: cmp->prefix); |
168 | cmp->prefix = NULL; |
169 | } |
170 | |
171 | it = items; |
172 | while (it) |
173 | { |
174 | cmp->items = g_list_prepend (list: cmp->items, data: it->data); |
175 | it = it->next; |
176 | } |
177 | } |
178 | |
179 | /** |
180 | * g_completion_remove_items: |
181 | * @cmp: the #GCompletion. |
182 | * @items: (transfer none): the items to remove. |
183 | * |
184 | * Removes items from a #GCompletion. The items are not freed, so if the memory |
185 | * was dynamically allocated, free @items with g_list_free_full() after calling |
186 | * this function. |
187 | * |
188 | * Deprecated: 2.26: Rarely used API |
189 | **/ |
190 | void |
191 | g_completion_remove_items (GCompletion* cmp, |
192 | GList* items) |
193 | { |
194 | GList* it; |
195 | |
196 | g_return_if_fail (cmp != NULL); |
197 | |
198 | it = items; |
199 | while (cmp->items && it) |
200 | { |
201 | cmp->items = g_list_remove (list: cmp->items, data: it->data); |
202 | it = it->next; |
203 | } |
204 | |
205 | it = items; |
206 | while (cmp->cache && it) |
207 | { |
208 | cmp->cache = g_list_remove(list: cmp->cache, data: it->data); |
209 | it = it->next; |
210 | } |
211 | } |
212 | |
213 | /** |
214 | * g_completion_clear_items: |
215 | * @cmp: the #GCompletion. |
216 | * |
217 | * Removes all items from the #GCompletion. The items are not freed, so if the |
218 | * memory was dynamically allocated, it should be freed after calling this |
219 | * function. |
220 | * |
221 | * Deprecated: 2.26: Rarely used API |
222 | **/ |
223 | void |
224 | g_completion_clear_items (GCompletion* cmp) |
225 | { |
226 | g_return_if_fail (cmp != NULL); |
227 | |
228 | g_list_free (list: cmp->items); |
229 | cmp->items = NULL; |
230 | g_list_free (list: cmp->cache); |
231 | cmp->cache = NULL; |
232 | g_free (mem: cmp->prefix); |
233 | cmp->prefix = NULL; |
234 | } |
235 | |
236 | static void |
237 | completion_check_cache (GCompletion* cmp, |
238 | gchar** new_prefix) |
239 | { |
240 | GList* list; |
241 | gsize len; |
242 | gsize i; |
243 | gsize plen; |
244 | gchar* postfix; |
245 | gchar* s; |
246 | |
247 | if (!new_prefix) |
248 | return; |
249 | if (!cmp->cache) |
250 | { |
251 | *new_prefix = NULL; |
252 | return; |
253 | } |
254 | |
255 | len = strlen(s: cmp->prefix); |
256 | list = cmp->cache; |
257 | s = cmp->func ? cmp->func (list->data) : (gchar*) list->data; |
258 | postfix = s + len; |
259 | plen = strlen (s: postfix); |
260 | list = list->next; |
261 | |
262 | while (list && plen) |
263 | { |
264 | s = cmp->func ? cmp->func (list->data) : (gchar*) list->data; |
265 | s += len; |
266 | for (i = 0; i < plen; ++i) |
267 | { |
268 | if (postfix[i] != s[i]) |
269 | break; |
270 | } |
271 | plen = i; |
272 | list = list->next; |
273 | } |
274 | |
275 | *new_prefix = g_new0 (gchar, len + plen + 1); |
276 | strncpy (dest: *new_prefix, src: cmp->prefix, n: len); |
277 | strncpy (dest: *new_prefix + len, src: postfix, n: plen); |
278 | } |
279 | |
280 | /** |
281 | * g_completion_complete_utf8: |
282 | * @cmp: the #GCompletion |
283 | * @prefix: the prefix string, typically used by the user, which is compared |
284 | * with each of the items |
285 | * @new_prefix: if non-%NULL, returns the longest prefix which is common to all |
286 | * items that matched @prefix, or %NULL if no items matched @prefix. |
287 | * This string should be freed when no longer needed. |
288 | * |
289 | * Attempts to complete the string @prefix using the #GCompletion target items. |
290 | * In contrast to g_completion_complete(), this function returns the largest common |
291 | * prefix that is a valid UTF-8 string, omitting a possible common partial |
292 | * character. |
293 | * |
294 | * You should use this function instead of g_completion_complete() if your |
295 | * items are UTF-8 strings. |
296 | * |
297 | * Returns: (element-type utf8) (transfer none): the list of items whose strings begin with @prefix. This should |
298 | * not be changed. |
299 | * |
300 | * Since: 2.4 |
301 | * |
302 | * Deprecated: 2.26: Rarely used API |
303 | **/ |
304 | GList* |
305 | g_completion_complete_utf8 (GCompletion *cmp, |
306 | const gchar *prefix, |
307 | gchar **new_prefix) |
308 | { |
309 | GList *list; |
310 | gchar *p, *q; |
311 | |
312 | list = g_completion_complete (cmp, prefix, new_prefix); |
313 | |
314 | if (new_prefix && *new_prefix) |
315 | { |
316 | p = *new_prefix + strlen (s: *new_prefix); |
317 | q = g_utf8_find_prev_char (str: *new_prefix, p); |
318 | |
319 | switch (g_utf8_get_char_validated (p: q, max_len: p - q)) |
320 | { |
321 | case (gunichar)-2: |
322 | case (gunichar)-1: |
323 | *q = 0; |
324 | break; |
325 | default: ; |
326 | } |
327 | |
328 | } |
329 | |
330 | return list; |
331 | } |
332 | |
333 | /** |
334 | * g_completion_complete: |
335 | * @cmp: the #GCompletion. |
336 | * @prefix: the prefix string, typically typed by the user, which is |
337 | * compared with each of the items. |
338 | * @new_prefix: if non-%NULL, returns the longest prefix which is |
339 | * common to all items that matched @prefix, or %NULL if |
340 | * no items matched @prefix. This string should be freed |
341 | * when no longer needed. |
342 | * |
343 | * Attempts to complete the string @prefix using the #GCompletion |
344 | * target items. |
345 | * |
346 | * Returns: (transfer none): the list of items whose strings begin with |
347 | * @prefix. This should not be changed. |
348 | * |
349 | * Deprecated: 2.26: Rarely used API |
350 | **/ |
351 | GList* |
352 | g_completion_complete (GCompletion* cmp, |
353 | const gchar* prefix, |
354 | gchar** new_prefix) |
355 | { |
356 | gsize plen, len; |
357 | gboolean done = FALSE; |
358 | GList* list; |
359 | |
360 | g_return_val_if_fail (cmp != NULL, NULL); |
361 | g_return_val_if_fail (prefix != NULL, NULL); |
362 | |
363 | len = strlen (s: prefix); |
364 | if (cmp->prefix && cmp->cache) |
365 | { |
366 | plen = strlen (s: cmp->prefix); |
367 | if (plen <= len && ! cmp->strncmp_func (prefix, cmp->prefix, plen)) |
368 | { |
369 | /* use the cache */ |
370 | list = cmp->cache; |
371 | while (list) |
372 | { |
373 | GList *next = list->next; |
374 | |
375 | if (cmp->strncmp_func (prefix, |
376 | cmp->func ? cmp->func (list->data) : (gchar*) list->data, |
377 | len)) |
378 | cmp->cache = g_list_delete_link (list: cmp->cache, link_: list); |
379 | |
380 | list = next; |
381 | } |
382 | done = TRUE; |
383 | } |
384 | } |
385 | |
386 | if (!done) |
387 | { |
388 | /* normal code */ |
389 | g_list_free (list: cmp->cache); |
390 | cmp->cache = NULL; |
391 | list = cmp->items; |
392 | while (*prefix && list) |
393 | { |
394 | if (!cmp->strncmp_func (prefix, |
395 | cmp->func ? cmp->func (list->data) : (gchar*) list->data, |
396 | len)) |
397 | cmp->cache = g_list_prepend (list: cmp->cache, data: list->data); |
398 | list = list->next; |
399 | } |
400 | } |
401 | if (cmp->prefix) |
402 | { |
403 | g_free (mem: cmp->prefix); |
404 | cmp->prefix = NULL; |
405 | } |
406 | if (cmp->cache) |
407 | cmp->prefix = g_strdup (str: prefix); |
408 | completion_check_cache (cmp, new_prefix); |
409 | |
410 | return *prefix ? cmp->cache : cmp->items; |
411 | } |
412 | |
413 | /** |
414 | * g_completion_free: |
415 | * @cmp: the #GCompletion. |
416 | * |
417 | * Frees all memory used by the #GCompletion. The items are not freed, so if |
418 | * the memory was dynamically allocated, it should be freed after calling this |
419 | * function. |
420 | * |
421 | * Deprecated: 2.26: Rarely used API |
422 | **/ |
423 | void |
424 | g_completion_free (GCompletion* cmp) |
425 | { |
426 | g_return_if_fail (cmp != NULL); |
427 | |
428 | g_completion_clear_items (cmp); |
429 | g_free (mem: cmp); |
430 | } |
431 | |
432 | /** |
433 | * g_completion_set_compare: |
434 | * @cmp: a #GCompletion. |
435 | * @strncmp_func: the string comparison function. |
436 | * |
437 | * Sets the function to use for string comparisons. The default string |
438 | * comparison function is strncmp(). |
439 | * |
440 | * Deprecated: 2.26: Rarely used API |
441 | **/ |
442 | void |
443 | g_completion_set_compare(GCompletion *cmp, |
444 | GCompletionStrncmpFunc strncmp_func) |
445 | { |
446 | cmp->strncmp_func = strncmp_func; |
447 | } |
448 | |
449 | #ifdef TEST_COMPLETION |
450 | #include <stdio.h> |
451 | int |
452 | main (int argc, |
453 | char* argv[]) |
454 | { |
455 | FILE *file; |
456 | gchar buf[1024]; |
457 | GList *list; |
458 | GList *result; |
459 | GList *tmp; |
460 | GCompletion *cmp; |
461 | gint i; |
462 | gchar *longp = NULL; |
463 | |
464 | if (argc < 3) |
465 | { |
466 | g_warning ("Usage: %s filename prefix1 [prefix2 ...]" , argv[0]); |
467 | return 1; |
468 | } |
469 | |
470 | file = fopen (argv[1], "r" ); |
471 | if (!file) |
472 | { |
473 | g_warning ("Cannot open %s" , argv[1]); |
474 | return 1; |
475 | } |
476 | |
477 | cmp = g_completion_new (NULL); |
478 | list = g_list_alloc (); |
479 | while (fgets (buf, 1024, file)) |
480 | { |
481 | list->data = g_strdup (buf); |
482 | g_completion_add_items (cmp, list); |
483 | } |
484 | fclose (file); |
485 | |
486 | for (i = 2; i < argc; ++i) |
487 | { |
488 | printf ("COMPLETING: %s\n" , argv[i]); |
489 | result = g_completion_complete (cmp, argv[i], &longp); |
490 | g_list_foreach (result, (GFunc) printf, NULL); |
491 | printf ("LONG MATCH: %s\n" , longp); |
492 | g_free (longp); |
493 | longp = NULL; |
494 | } |
495 | |
496 | g_list_foreach (cmp->items, (GFunc) g_free, NULL); |
497 | g_completion_free (cmp); |
498 | g_list_free (list); |
499 | |
500 | return 0; |
501 | } |
502 | #endif |
503 | |