• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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*
g_completion_new(GCompletionFunc func)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
g_completion_add_items(GCompletion * cmp,GList * items)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 (cmp->cache);
162       cmp->cache = NULL;
163     }
164 
165   if (cmp->prefix)
166     {
167       g_free (cmp->prefix);
168       cmp->prefix = NULL;
169     }
170 
171   it = items;
172   while (it)
173     {
174       cmp->items = g_list_prepend (cmp->items, 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
g_completion_remove_items(GCompletion * cmp,GList * items)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 (cmp->items, it->data);
202       it = it->next;
203     }
204 
205   it = items;
206   while (cmp->cache && it)
207     {
208       cmp->cache = g_list_remove(cmp->cache, 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
g_completion_clear_items(GCompletion * cmp)224 g_completion_clear_items (GCompletion* cmp)
225 {
226   g_return_if_fail (cmp != NULL);
227 
228   g_list_free (cmp->items);
229   cmp->items = NULL;
230   g_list_free (cmp->cache);
231   cmp->cache = NULL;
232   g_free (cmp->prefix);
233   cmp->prefix = NULL;
234 }
235 
236 static void
completion_check_cache(GCompletion * cmp,gchar ** new_prefix)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(cmp->prefix);
256   list = cmp->cache;
257   s = cmp->func ? cmp->func (list->data) : (gchar*) list->data;
258   postfix = s + len;
259   plen = strlen (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 (*new_prefix, cmp->prefix, len);
277   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*
g_completion_complete_utf8(GCompletion * cmp,const gchar * prefix,gchar ** new_prefix)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 (*new_prefix);
317       q = g_utf8_find_prev_char (*new_prefix, p);
318 
319       switch (g_utf8_get_char_validated (q, 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*
g_completion_complete(GCompletion * cmp,const gchar * prefix,gchar ** new_prefix)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 (prefix);
364   if (cmp->prefix && cmp->cache)
365     {
366       plen = strlen (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 (cmp->cache, list);
379 
380 	      list = next;
381 	    }
382 	  done = TRUE;
383 	}
384     }
385 
386   if (!done)
387     {
388       /* normal code */
389       g_list_free (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 (cmp->cache, list->data);
398 	  list = list->next;
399 	}
400     }
401   if (cmp->prefix)
402     {
403       g_free (cmp->prefix);
404       cmp->prefix = NULL;
405     }
406   if (cmp->cache)
407     cmp->prefix = g_strdup (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
g_completion_free(GCompletion * cmp)424 g_completion_free (GCompletion* cmp)
425 {
426   g_return_if_fail (cmp != NULL);
427 
428   g_completion_clear_items (cmp);
429   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
g_completion_set_compare(GCompletion * cmp,GCompletionStrncmpFunc strncmp_func)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
main(int argc,char * argv[])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