Branch data Line data Source code
1 : : /* GLIB - Library of useful routines for C programming
2 : : * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3 : : *
4 : : * SPDX-License-Identifier: LGPL-2.1-or-later
5 : : *
6 : : * This library is free software; you can redistribute it and/or
7 : : * modify it under the terms of the GNU Lesser General Public
8 : : * License as published by the Free Software Foundation; either
9 : : * version 2.1 of the License, or (at your option) any later version.
10 : : *
11 : : * This library is distributed in the hope that it will be useful,
12 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 : : * Lesser General Public License for more details.
15 : : *
16 : : * You should have received a copy of the GNU Lesser General Public
17 : : * License along with this library; if not, see <http://www.gnu.org/licenses/>.
18 : : */
19 : :
20 : : /*
21 : : * Modified by the GLib Team and others 1997-2000. See the AUTHORS
22 : : * file for a list of people on the GLib Team. See the ChangeLog
23 : : * files for a list of changes. These files are distributed with
24 : : * GLib at ftp://ftp.gtk.org/pub/gtk/.
25 : : */
26 : :
27 : : /*
28 : : * MT safe
29 : : */
30 : :
31 : : #include "config.h"
32 : :
33 : : /* we know we are deprecated here, no need for warnings */
34 : : #ifndef GLIB_DISABLE_DEPRECATION_WARNINGS
35 : : #define GLIB_DISABLE_DEPRECATION_WARNINGS
36 : : #endif
37 : :
38 : : #include "gcompletion.h"
39 : :
40 : : #include <glib/gstrfuncs.h>
41 : : #include <glib/gmessages.h>
42 : : #include <glib/gstdio.h>
43 : : #include <glib/gunicode.h>
44 : :
45 : : #include <string.h>
46 : :
47 : : /**
48 : : * GCompletion:
49 : : * @items: list of target items (strings or data structures).
50 : : * @func: function which is called to get the string associated with a
51 : : * target item. It is %NULL if the target items are strings.
52 : : * @prefix: the last prefix passed to g_completion_complete() or
53 : : * g_completion_complete_utf8().
54 : : * @cache: the list of items which begin with @prefix.
55 : : * @strncmp_func: The function to use when comparing strings. Use
56 : : * g_completion_set_compare() to modify this function.
57 : : *
58 : : * `GCompletion` provides support for automatic completion of a string
59 : : * using any group of target strings. It is typically used for file
60 : : * name completion as is common in many UNIX shells.
61 : : *
62 : : * A `GCompletion` is created using [func@GLib.Completion.new]. Target items are
63 : : * added and removed with [method@GLib.Completion.add_items],
64 : : * [method@GLib.Completion.remove_items] and
65 : : * [method@GLib.Completion.clear_items]. A completion attempt is requested with
66 : : * [method@GLib.Completion.complete] or [method@GLib.Completion.complete_utf8].
67 : : * When no longer needed, the `GCompletion` is freed with
68 : : * [method@GLib.Completion.free].
69 : : *
70 : : * Items in the completion can be simple strings (e.g. filenames), or
71 : : * pointers to arbitrary data structures. If data structures are used
72 : : * you must provide a [type@GLib.CompletionFunc] in [func@GLib.Completion.new],
73 : : * which retrieves the item’s string from the data structure. You can change
74 : : * the way in which strings are compared by setting a different
75 : : * [type@GLib.CompletionStrncmpFunc] in [method@GLib.Completion.set_compare].
76 : : *
77 : : * `GCompletion` has been marked as deprecated, since this API is rarely
78 : : * used and not very actively maintained.
79 : : *
80 : : * Deprecated: 2.26: Rarely used API
81 : : **/
82 : :
83 : : /**
84 : : * GCompletionFunc:
85 : : * @item: the completion item.
86 : : *
87 : : * Specifies the type of the function passed to g_completion_new(). It
88 : : * should return the string corresponding to the given target item.
89 : : * This is used when you use data structures as #GCompletion items.
90 : : *
91 : : * Returns: the string corresponding to the item.
92 : : * Deprecated: 2.26: Rarely used API
93 : : **/
94 : :
95 : : /**
96 : : * GCompletionStrncmpFunc:
97 : : * @s1: string to compare with @s2.
98 : : * @s2: string to compare with @s1.
99 : : * @n: maximal number of bytes to compare.
100 : : *
101 : : * Specifies the type of the function passed to
102 : : * g_completion_set_compare(). This is used when you use strings as
103 : : * #GCompletion items.
104 : : *
105 : : * Returns: an integer less than, equal to, or greater than zero if
106 : : * the first @n bytes of @s1 is found, respectively, to be
107 : : * less than, to match, or to be greater than the first @n
108 : : * bytes of @s2.
109 : : * Deprecated: 2.26: Rarely used API
110 : : **/
111 : :
112 : : static void completion_check_cache (GCompletion* cmp,
113 : : gchar** new_prefix);
114 : :
115 : : /**
116 : : * g_completion_new:
117 : : * @func: the function to be called to return the string representing
118 : : * an item in the #GCompletion, or %NULL if strings are going to
119 : : * be used as the #GCompletion items.
120 : : *
121 : : * Creates a new #GCompletion.
122 : : *
123 : : * Returns: the new #GCompletion.
124 : : * Deprecated: 2.26: Rarely used API
125 : : **/
126 : : GCompletion*
127 : 1 : g_completion_new (GCompletionFunc func)
128 : : {
129 : : GCompletion* gcomp;
130 : :
131 : 1 : gcomp = g_new (GCompletion, 1);
132 : 1 : gcomp->items = NULL;
133 : 1 : gcomp->cache = NULL;
134 : 1 : gcomp->prefix = NULL;
135 : 1 : gcomp->func = func;
136 : 1 : gcomp->strncmp_func = strncmp;
137 : :
138 : 1 : 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 : 1 : g_completion_add_items (GCompletion* cmp,
152 : : GList* items)
153 : : {
154 : : GList* it;
155 : :
156 : 1 : g_return_if_fail (cmp != NULL);
157 : :
158 : : /* optimize adding to cache? */
159 : 1 : if (cmp->cache)
160 : : {
161 : 0 : g_list_free (cmp->cache);
162 : 0 : cmp->cache = NULL;
163 : : }
164 : :
165 : 1 : if (cmp->prefix)
166 : : {
167 : 0 : g_free (cmp->prefix);
168 : 0 : cmp->prefix = NULL;
169 : : }
170 : :
171 : 1 : it = items;
172 : 5 : while (it)
173 : : {
174 : 4 : cmp->items = g_list_prepend (cmp->items, it->data);
175 : 4 : 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 : 1 : g_completion_remove_items (GCompletion* cmp,
192 : : GList* items)
193 : : {
194 : : GList* it;
195 : :
196 : 1 : g_return_if_fail (cmp != NULL);
197 : :
198 : 1 : it = items;
199 : 2 : while (cmp->items && it)
200 : : {
201 : 1 : cmp->items = g_list_remove (cmp->items, it->data);
202 : 1 : it = it->next;
203 : : }
204 : :
205 : 1 : it = items;
206 : 2 : while (cmp->cache && it)
207 : : {
208 : 1 : cmp->cache = g_list_remove(cmp->cache, it->data);
209 : 1 : 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 : 1 : g_completion_clear_items (GCompletion* cmp)
225 : : {
226 : 1 : g_return_if_fail (cmp != NULL);
227 : :
228 : 1 : g_list_free (cmp->items);
229 : 1 : cmp->items = NULL;
230 : 1 : g_list_free (cmp->cache);
231 : 1 : cmp->cache = NULL;
232 : 1 : g_free (cmp->prefix);
233 : 1 : cmp->prefix = NULL;
234 : : }
235 : :
236 : : static void
237 : 7 : 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 : 7 : if (!new_prefix)
248 : 2 : return;
249 : 5 : if (!cmp->cache)
250 : : {
251 : 0 : *new_prefix = NULL;
252 : 0 : return;
253 : : }
254 : :
255 : 5 : len = strlen(cmp->prefix);
256 : 5 : list = cmp->cache;
257 : 5 : s = cmp->func ? cmp->func (list->data) : (gchar*) list->data;
258 : 5 : postfix = s + len;
259 : 5 : plen = strlen (postfix);
260 : 5 : list = list->next;
261 : :
262 : 9 : while (list && plen)
263 : : {
264 : 4 : s = cmp->func ? cmp->func (list->data) : (gchar*) list->data;
265 : 4 : s += len;
266 : 6 : for (i = 0; i < plen; ++i)
267 : : {
268 : 6 : if (postfix[i] != s[i])
269 : 4 : break;
270 : : }
271 : 4 : plen = i;
272 : 4 : list = list->next;
273 : : }
274 : :
275 : 5 : *new_prefix = g_new0 (gchar, len + plen + 1);
276 : 5 : strncpy (*new_prefix, cmp->prefix, len);
277 : 5 : strncpy (*new_prefix + len, postfix, 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 : 4 : g_completion_complete_utf8 (GCompletion *cmp,
306 : : const gchar *prefix,
307 : : gchar **new_prefix)
308 : : {
309 : : GList *list;
310 : : gchar *p, *q;
311 : :
312 : 4 : list = g_completion_complete (cmp, prefix, new_prefix);
313 : :
314 : 4 : if (new_prefix && *new_prefix)
315 : : {
316 : 3 : p = *new_prefix + strlen (*new_prefix);
317 : 3 : q = g_utf8_find_prev_char (*new_prefix, p);
318 : :
319 : 3 : switch (g_utf8_get_char_validated (q, p - q))
320 : : {
321 : 1 : case (gunichar)-2:
322 : : case (gunichar)-1:
323 : 1 : *q = 0;
324 : 1 : break;
325 : 1 : default: ;
326 : : }
327 : :
328 : : }
329 : :
330 : 4 : 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 : 7 : g_completion_complete (GCompletion* cmp,
353 : : const gchar* prefix,
354 : : gchar** new_prefix)
355 : : {
356 : : gsize plen, len;
357 : 7 : gboolean done = FALSE;
358 : : GList* list;
359 : :
360 : 7 : g_return_val_if_fail (cmp != NULL, NULL);
361 : 7 : g_return_val_if_fail (prefix != NULL, NULL);
362 : :
363 : 7 : len = strlen (prefix);
364 : 7 : if (cmp->prefix && cmp->cache)
365 : : {
366 : 6 : plen = strlen (cmp->prefix);
367 : 6 : if (plen <= len && ! cmp->strncmp_func (prefix, cmp->prefix, plen))
368 : : {
369 : : /* use the cache */
370 : 3 : list = cmp->cache;
371 : 9 : while (list)
372 : : {
373 : 6 : GList *next = list->next;
374 : :
375 : 6 : if (cmp->strncmp_func (prefix,
376 : 6 : cmp->func ? cmp->func (list->data) : (gchar*) list->data,
377 : : len))
378 : 0 : cmp->cache = g_list_delete_link (cmp->cache, list);
379 : :
380 : 6 : list = next;
381 : : }
382 : 3 : done = TRUE;
383 : : }
384 : : }
385 : :
386 : 7 : if (!done)
387 : : {
388 : : /* normal code */
389 : 4 : g_list_free (cmp->cache);
390 : 4 : cmp->cache = NULL;
391 : 4 : list = cmp->items;
392 : 19 : while (*prefix && list)
393 : : {
394 : 15 : if (!cmp->strncmp_func (prefix,
395 : 15 : cmp->func ? cmp->func (list->data) : (gchar*) list->data,
396 : : len))
397 : 7 : cmp->cache = g_list_prepend (cmp->cache, list->data);
398 : 15 : list = list->next;
399 : : }
400 : : }
401 : 7 : if (cmp->prefix)
402 : : {
403 : 6 : g_free (cmp->prefix);
404 : 6 : cmp->prefix = NULL;
405 : : }
406 : 7 : if (cmp->cache)
407 : 7 : cmp->prefix = g_strdup (prefix);
408 : 7 : completion_check_cache (cmp, new_prefix);
409 : :
410 : 7 : 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 : 1 : g_completion_free (GCompletion* cmp)
425 : : {
426 : 1 : g_return_if_fail (cmp != NULL);
427 : :
428 : 1 : g_completion_clear_items (cmp);
429 : 1 : g_free (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 : 1 : g_completion_set_compare(GCompletion *cmp,
444 : : GCompletionStrncmpFunc strncmp_func)
445 : : {
446 : 1 : cmp->strncmp_func = strncmp_func;
447 : 1 : }
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 ...]",
467 : : (argc > 0) ? argv[0] : "gcompletion");
468 : : return 1;
469 : : }
470 : :
471 : : file = g_fopen (argv[1], "re");
472 : : if (!file)
473 : : {
474 : : g_warning ("Cannot open %s", argv[1]);
475 : : return 1;
476 : : }
477 : :
478 : : cmp = g_completion_new (NULL);
479 : : list = g_list_alloc ();
480 : : while (fgets (buf, 1024, file))
481 : : {
482 : : list->data = g_strdup (buf);
483 : : g_completion_add_items (cmp, list);
484 : : }
485 : : fclose (file);
486 : :
487 : : for (i = 2; i < argc; ++i)
488 : : {
489 : : printf ("COMPLETING: %s\n", argv[i]);
490 : : result = g_completion_complete (cmp, argv[i], &longp);
491 : : g_list_foreach (result, (GFunc) printf, NULL);
492 : : printf ("LONG MATCH: %s\n", longp);
493 : : g_free (longp);
494 : : longp = NULL;
495 : : }
496 : :
497 : : g_list_foreach (cmp->items, (GFunc) g_free, NULL);
498 : : g_completion_free (cmp);
499 : : g_list_free (list);
500 : :
501 : : return 0;
502 : : }
503 : : #endif
|