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/gunicode.h>
43 : :
44 : : #include <string.h>
45 : :
46 : : /**
47 : : * GCompletion:
48 : : * @items: list of target items (strings or data structures).
49 : : * @func: function which is called to get the string associated with a
50 : : * target item. It is %NULL if the target items are strings.
51 : : * @prefix: the last prefix passed to g_completion_complete() or
52 : : * g_completion_complete_utf8().
53 : : * @cache: the list of items which begin with @prefix.
54 : : * @strncmp_func: The function to use when comparing strings. Use
55 : : * g_completion_set_compare() to modify this function.
56 : : *
57 : : * `GCompletion` provides support for automatic completion of a string
58 : : * using any group of target strings. It is typically used for file
59 : : * name completion as is common in many UNIX shells.
60 : : *
61 : : * A `GCompletion` is created using [func@GLib.Completion.new]. Target items are
62 : : * added and removed with [method@GLib.Completion.add_items],
63 : : * [method@GLib.Completion.remove_items] and
64 : : * [method@GLib.Completion.clear_items]. A completion attempt is requested with
65 : : * [method@GLib.Completion.complete] or [method@GLib.Completion.complete_utf8].
66 : : * When no longer needed, the `GCompletion` is freed with
67 : : * [method@GLib.Completion.free].
68 : : *
69 : : * Items in the completion can be simple strings (e.g. filenames), or
70 : : * pointers to arbitrary data structures. If data structures are used
71 : : * you must provide a [type@GLib.CompletionFunc] in [func@GLib.Completion.new],
72 : : * which retrieves the item’s string from the data structure. You can change
73 : : * the way in which strings are compared by setting a different
74 : : * [type@GLib.CompletionStrncmpFunc] in [method@GLib.Completion.set_compare].
75 : : *
76 : : * `GCompletion` has been marked as deprecated, since this API is rarely
77 : : * used and not very actively maintained.
78 : : *
79 : : * Deprecated: 2.26: Rarely used API
80 : : **/
81 : :
82 : : /**
83 : : * GCompletionFunc:
84 : : * @item: the completion item.
85 : : *
86 : : * Specifies the type of the function passed to g_completion_new(). It
87 : : * should return the string corresponding to the given target item.
88 : : * This is used when you use data structures as #GCompletion items.
89 : : *
90 : : * Returns: the string corresponding to the item.
91 : : * Deprecated: 2.26: Rarely used API
92 : : **/
93 : :
94 : : /**
95 : : * GCompletionStrncmpFunc:
96 : : * @s1: string to compare with @s2.
97 : : * @s2: string to compare with @s1.
98 : : * @n: maximal number of bytes to compare.
99 : : *
100 : : * Specifies the type of the function passed to
101 : : * g_completion_set_compare(). This is used when you use strings as
102 : : * #GCompletion items.
103 : : *
104 : : * Returns: an integer less than, equal to, or greater than zero if
105 : : * the first @n bytes of @s1 is found, respectively, to be
106 : : * less than, to match, or to be greater than the first @n
107 : : * bytes of @s2.
108 : : * Deprecated: 2.26: Rarely used API
109 : : **/
110 : :
111 : : static void completion_check_cache (GCompletion* cmp,
112 : : gchar** new_prefix);
113 : :
114 : : /**
115 : : * g_completion_new:
116 : : * @func: the function to be called to return the string representing
117 : : * an item in the #GCompletion, or %NULL if strings are going to
118 : : * be used as the #GCompletion items.
119 : : *
120 : : * Creates a new #GCompletion.
121 : : *
122 : : * Returns: the new #GCompletion.
123 : : * Deprecated: 2.26: Rarely used API
124 : : **/
125 : : GCompletion*
126 : 1 : g_completion_new (GCompletionFunc func)
127 : : {
128 : : GCompletion* gcomp;
129 : :
130 : 1 : gcomp = g_new (GCompletion, 1);
131 : 1 : gcomp->items = NULL;
132 : 1 : gcomp->cache = NULL;
133 : 1 : gcomp->prefix = NULL;
134 : 1 : gcomp->func = func;
135 : 1 : gcomp->strncmp_func = strncmp;
136 : :
137 : 1 : return gcomp;
138 : : }
139 : :
140 : : /**
141 : : * g_completion_add_items:
142 : : * @cmp: the #GCompletion.
143 : : * @items: (transfer none): the list of items to add.
144 : : *
145 : : * Adds items to the #GCompletion.
146 : : *
147 : : * Deprecated: 2.26: Rarely used API
148 : : **/
149 : : void
150 : 1 : g_completion_add_items (GCompletion* cmp,
151 : : GList* items)
152 : : {
153 : : GList* it;
154 : :
155 : 1 : g_return_if_fail (cmp != NULL);
156 : :
157 : : /* optimize adding to cache? */
158 [ - + ]: 1 : if (cmp->cache)
159 : : {
160 : 0 : g_list_free (cmp->cache);
161 : 0 : cmp->cache = NULL;
162 : : }
163 : :
164 [ - + ]: 1 : if (cmp->prefix)
165 : : {
166 : 0 : g_free (cmp->prefix);
167 : 0 : cmp->prefix = NULL;
168 : : }
169 : :
170 : 1 : it = items;
171 [ + + ]: 5 : while (it)
172 : : {
173 : 4 : cmp->items = g_list_prepend (cmp->items, it->data);
174 : 4 : it = it->next;
175 : : }
176 : : }
177 : :
178 : : /**
179 : : * g_completion_remove_items:
180 : : * @cmp: the #GCompletion.
181 : : * @items: (transfer none): the items to remove.
182 : : *
183 : : * Removes items from a #GCompletion. The items are not freed, so if the memory
184 : : * was dynamically allocated, free @items with g_list_free_full() after calling
185 : : * this function.
186 : : *
187 : : * Deprecated: 2.26: Rarely used API
188 : : **/
189 : : void
190 : 1 : g_completion_remove_items (GCompletion* cmp,
191 : : GList* items)
192 : : {
193 : : GList* it;
194 : :
195 : 1 : g_return_if_fail (cmp != NULL);
196 : :
197 : 1 : it = items;
198 [ + - + + ]: 2 : while (cmp->items && it)
199 : : {
200 : 1 : cmp->items = g_list_remove (cmp->items, it->data);
201 : 1 : it = it->next;
202 : : }
203 : :
204 : 1 : it = items;
205 [ + - + + ]: 2 : while (cmp->cache && it)
206 : : {
207 : 1 : cmp->cache = g_list_remove(cmp->cache, it->data);
208 : 1 : it = it->next;
209 : : }
210 : : }
211 : :
212 : : /**
213 : : * g_completion_clear_items:
214 : : * @cmp: the #GCompletion.
215 : : *
216 : : * Removes all items from the #GCompletion. The items are not freed, so if the
217 : : * memory was dynamically allocated, it should be freed after calling this
218 : : * function.
219 : : *
220 : : * Deprecated: 2.26: Rarely used API
221 : : **/
222 : : void
223 : 1 : g_completion_clear_items (GCompletion* cmp)
224 : : {
225 : 1 : g_return_if_fail (cmp != NULL);
226 : :
227 : 1 : g_list_free (cmp->items);
228 : 1 : cmp->items = NULL;
229 : 1 : g_list_free (cmp->cache);
230 : 1 : cmp->cache = NULL;
231 : 1 : g_free (cmp->prefix);
232 : 1 : cmp->prefix = NULL;
233 : : }
234 : :
235 : : static void
236 : 7 : completion_check_cache (GCompletion* cmp,
237 : : gchar** new_prefix)
238 : : {
239 : : GList* list;
240 : : gsize len;
241 : : gsize i;
242 : : gsize plen;
243 : : gchar* postfix;
244 : : gchar* s;
245 : :
246 [ + + ]: 7 : if (!new_prefix)
247 : 2 : return;
248 [ - + ]: 5 : if (!cmp->cache)
249 : : {
250 : 0 : *new_prefix = NULL;
251 : 0 : return;
252 : : }
253 : :
254 : 5 : len = strlen(cmp->prefix);
255 : 5 : list = cmp->cache;
256 [ - + ]: 5 : s = cmp->func ? cmp->func (list->data) : (gchar*) list->data;
257 : 5 : postfix = s + len;
258 : 5 : plen = strlen (postfix);
259 : 5 : list = list->next;
260 : :
261 [ + + + - ]: 9 : while (list && plen)
262 : : {
263 [ - + ]: 4 : s = cmp->func ? cmp->func (list->data) : (gchar*) list->data;
264 : 4 : s += len;
265 [ + - ]: 6 : for (i = 0; i < plen; ++i)
266 : : {
267 [ + + ]: 6 : if (postfix[i] != s[i])
268 : 4 : break;
269 : : }
270 : 4 : plen = i;
271 : 4 : list = list->next;
272 : : }
273 : :
274 : 5 : *new_prefix = g_new0 (gchar, len + plen + 1);
275 : 5 : strncpy (*new_prefix, cmp->prefix, len);
276 : 5 : strncpy (*new_prefix + len, postfix, plen);
277 : : }
278 : :
279 : : /**
280 : : * g_completion_complete_utf8:
281 : : * @cmp: the #GCompletion
282 : : * @prefix: the prefix string, typically used by the user, which is compared
283 : : * with each of the items
284 : : * @new_prefix: if non-%NULL, returns the longest prefix which is common to all
285 : : * items that matched @prefix, or %NULL if no items matched @prefix.
286 : : * This string should be freed when no longer needed.
287 : : *
288 : : * Attempts to complete the string @prefix using the #GCompletion target items.
289 : : * In contrast to g_completion_complete(), this function returns the largest common
290 : : * prefix that is a valid UTF-8 string, omitting a possible common partial
291 : : * character.
292 : : *
293 : : * You should use this function instead of g_completion_complete() if your
294 : : * items are UTF-8 strings.
295 : : *
296 : : * Returns: (element-type utf8) (transfer none): the list of items whose strings begin with @prefix. This should
297 : : * not be changed.
298 : : *
299 : : * Since: 2.4
300 : : *
301 : : * Deprecated: 2.26: Rarely used API
302 : : **/
303 : : GList*
304 : 4 : g_completion_complete_utf8 (GCompletion *cmp,
305 : : const gchar *prefix,
306 : : gchar **new_prefix)
307 : : {
308 : : GList *list;
309 : : gchar *p, *q;
310 : :
311 : 4 : list = g_completion_complete (cmp, prefix, new_prefix);
312 : :
313 [ + + - + ]: 4 : if (new_prefix && *new_prefix)
314 : : {
315 : 3 : p = *new_prefix + strlen (*new_prefix);
316 : 3 : q = g_utf8_find_prev_char (*new_prefix, p);
317 : :
318 [ + + ]: 3 : switch (g_utf8_get_char_validated (q, p - q))
319 : : {
320 : 1 : case (gunichar)-2:
321 : : case (gunichar)-1:
322 : 1 : *q = 0;
323 : 1 : break;
324 : 1 : default: ;
325 : : }
326 : :
327 : : }
328 : :
329 : 4 : return list;
330 : : }
331 : :
332 : : /**
333 : : * g_completion_complete:
334 : : * @cmp: the #GCompletion.
335 : : * @prefix: the prefix string, typically typed by the user, which is
336 : : * compared with each of the items.
337 : : * @new_prefix: if non-%NULL, returns the longest prefix which is
338 : : * common to all items that matched @prefix, or %NULL if
339 : : * no items matched @prefix. This string should be freed
340 : : * when no longer needed.
341 : : *
342 : : * Attempts to complete the string @prefix using the #GCompletion
343 : : * target items.
344 : : *
345 : : * Returns: (transfer none): the list of items whose strings begin with
346 : : * @prefix. This should not be changed.
347 : : *
348 : : * Deprecated: 2.26: Rarely used API
349 : : **/
350 : : GList*
351 : 7 : g_completion_complete (GCompletion* cmp,
352 : : const gchar* prefix,
353 : : gchar** new_prefix)
354 : : {
355 : : gsize plen, len;
356 : 7 : gboolean done = FALSE;
357 : : GList* list;
358 : :
359 : 7 : g_return_val_if_fail (cmp != NULL, NULL);
360 : 7 : g_return_val_if_fail (prefix != NULL, NULL);
361 : :
362 : 7 : len = strlen (prefix);
363 [ + + + - ]: 7 : if (cmp->prefix && cmp->cache)
364 : : {
365 : 6 : plen = strlen (cmp->prefix);
366 [ + - + + ]: 6 : if (plen <= len && ! cmp->strncmp_func (prefix, cmp->prefix, plen))
367 : : {
368 : : /* use the cache */
369 : 3 : list = cmp->cache;
370 [ + + ]: 9 : while (list)
371 : : {
372 : 6 : GList *next = list->next;
373 : :
374 [ - + ]: 6 : if (cmp->strncmp_func (prefix,
375 [ - + ]: 6 : cmp->func ? cmp->func (list->data) : (gchar*) list->data,
376 : : len))
377 : 0 : cmp->cache = g_list_delete_link (cmp->cache, list);
378 : :
379 : 6 : list = next;
380 : : }
381 : 3 : done = TRUE;
382 : : }
383 : : }
384 : :
385 [ + + ]: 7 : if (!done)
386 : : {
387 : : /* normal code */
388 : 4 : g_list_free (cmp->cache);
389 : 4 : cmp->cache = NULL;
390 : 4 : list = cmp->items;
391 [ + - + + ]: 19 : while (*prefix && list)
392 : : {
393 [ + + ]: 15 : if (!cmp->strncmp_func (prefix,
394 [ - + ]: 15 : cmp->func ? cmp->func (list->data) : (gchar*) list->data,
395 : : len))
396 : 7 : cmp->cache = g_list_prepend (cmp->cache, list->data);
397 : 15 : list = list->next;
398 : : }
399 : : }
400 [ + + ]: 7 : if (cmp->prefix)
401 : : {
402 : 6 : g_free (cmp->prefix);
403 : 6 : cmp->prefix = NULL;
404 : : }
405 [ + - ]: 7 : if (cmp->cache)
406 : 7 : cmp->prefix = g_strdup (prefix);
407 : 7 : completion_check_cache (cmp, new_prefix);
408 : :
409 [ + - ]: 7 : return *prefix ? cmp->cache : cmp->items;
410 : : }
411 : :
412 : : /**
413 : : * g_completion_free:
414 : : * @cmp: the #GCompletion.
415 : : *
416 : : * Frees all memory used by the #GCompletion. The items are not freed, so if
417 : : * the memory was dynamically allocated, it should be freed after calling this
418 : : * function.
419 : : *
420 : : * Deprecated: 2.26: Rarely used API
421 : : **/
422 : : void
423 : 1 : g_completion_free (GCompletion* cmp)
424 : : {
425 : 1 : g_return_if_fail (cmp != NULL);
426 : :
427 : 1 : g_completion_clear_items (cmp);
428 : 1 : g_free (cmp);
429 : : }
430 : :
431 : : /**
432 : : * g_completion_set_compare:
433 : : * @cmp: a #GCompletion.
434 : : * @strncmp_func: the string comparison function.
435 : : *
436 : : * Sets the function to use for string comparisons. The default string
437 : : * comparison function is strncmp().
438 : : *
439 : : * Deprecated: 2.26: Rarely used API
440 : : **/
441 : : void
442 : 1 : g_completion_set_compare(GCompletion *cmp,
443 : : GCompletionStrncmpFunc strncmp_func)
444 : : {
445 : 1 : cmp->strncmp_func = strncmp_func;
446 : 1 : }
447 : :
448 : : #ifdef TEST_COMPLETION
449 : : #include <stdio.h>
450 : : int
451 : : main (int argc,
452 : : char* argv[])
453 : : {
454 : : FILE *file;
455 : : gchar buf[1024];
456 : : GList *list;
457 : : GList *result;
458 : : GList *tmp;
459 : : GCompletion *cmp;
460 : : gint i;
461 : : gchar *longp = NULL;
462 : :
463 : : if (argc < 3)
464 : : {
465 : : g_warning ("Usage: %s filename prefix1 [prefix2 ...]",
466 : : (argc > 0) ? argv[0] : "gcompletion");
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
|