applied patch from Andreas Persenius <ndap@swipnet.se> that updates the
[platform/upstream/glib.git] / glib / gcompletion.c
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 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, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
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 "glib.h"
32 #include <string.h>
33
34 static void completion_check_cache (GCompletion* cmp,
35                                     gchar**      new_prefix);
36
37 GCompletion* 
38 g_completion_new (GCompletionFunc func)
39 {
40   GCompletion* gcomp;
41   
42   gcomp = g_new (GCompletion, 1);
43   gcomp->items = NULL;
44   gcomp->cache = NULL;
45   gcomp->prefix = NULL;
46   gcomp->func = func;
47
48   return gcomp;
49 }
50
51 void 
52 g_completion_add_items (GCompletion* cmp,
53                         GList*       items)
54 {
55   GList* it;
56   
57   g_return_if_fail (cmp != NULL);
58   g_return_if_fail (items != NULL);
59   
60   /* optimize adding to cache? */
61   if (cmp->cache)
62     {
63       g_list_free (cmp->cache);
64       cmp->cache = NULL;
65     }
66
67   if (cmp->prefix)
68     {
69       g_free (cmp->prefix);
70       cmp->prefix = NULL;
71     }
72   
73   it = items;
74   while (it)
75     {
76       cmp->items = g_list_prepend (cmp->items, it->data);
77       it = it->next;
78     }
79 }
80
81 void 
82 g_completion_remove_items (GCompletion* cmp,
83                            GList*       items)
84 {
85   GList* it;
86   
87   g_return_if_fail (cmp != NULL);
88   g_return_if_fail (items != NULL);
89   
90   it = items;
91   while (cmp->items && it)
92     {
93       cmp->items = g_list_remove (cmp->items, it->data);
94       it = it->next;
95     }
96
97   it = items;
98   while (cmp->cache && it)
99     {
100       cmp->cache = g_list_remove(cmp->cache, it->data);
101       it = it->next;
102     }
103 }
104
105 void 
106 g_completion_clear_items (GCompletion* cmp)
107 {
108   g_return_if_fail (cmp != NULL);
109   
110   g_list_free (cmp->items);
111   cmp->items = NULL;
112   g_list_free (cmp->cache);
113   cmp->cache = NULL;
114   g_free (cmp->prefix);
115   cmp->prefix = NULL;
116 }
117
118 static void   
119 completion_check_cache (GCompletion* cmp,
120                         gchar**      new_prefix)
121 {
122   register GList* list;
123   register gint len;
124   register gint i;
125   register gint plen;
126   gchar* postfix;
127   gchar* s;
128   
129   if (!new_prefix)
130     return;
131   if (!cmp->cache)
132     {
133       *new_prefix = NULL;
134       return;
135     }
136   
137   len = strlen(cmp->prefix);
138   list = cmp->cache;
139   s = cmp->func ? cmp->func (list->data) : (gchar*) list->data;
140   postfix = s + len;
141   plen = strlen (postfix);
142   list = list->next;
143   
144   while (list && plen)
145     {
146       s = cmp->func ? cmp->func (list->data) : (gchar*) list->data;
147       s += len;
148       for (i = 0; i < plen; ++i)
149         {
150           if (postfix[i] != s[i])
151             break;
152         }
153       plen = i;
154       list = list->next;
155     }
156   
157   *new_prefix = g_new0 (gchar, len + plen + 1);
158   strncpy (*new_prefix, cmp->prefix, len);
159   strncpy (*new_prefix + len, postfix, plen);
160 }
161
162 GList* 
163 g_completion_complete (GCompletion* cmp,
164                        gchar*       prefix,
165                        gchar**      new_prefix)
166 {
167   gint plen, len;
168   gint done = 0;
169   GList* list;
170   
171   g_return_val_if_fail (cmp != NULL, NULL);
172   g_return_val_if_fail (prefix != NULL, NULL);
173   
174   len = strlen (prefix);
175   if (cmp->prefix && cmp->cache)
176     {
177       plen = strlen (cmp->prefix);
178       if (plen <= len && !strncmp (prefix, cmp->prefix, plen))
179         { 
180           /* use the cache */
181           list = cmp->cache;
182           while (list)
183             {
184               if (strncmp (prefix,
185                            cmp->func ? cmp->func (list->data) : (gchar*) list->data,
186                            len))
187                 {
188                   list = g_list_remove_link (cmp->cache, list);
189                   if (list != cmp->cache)
190                     cmp->cache = list;
191                 }
192               else
193                 list = list->next;
194             }
195           done = 1;
196         }
197     }
198   
199   if (!done)
200     {
201       /* normal code */
202       g_list_free (cmp->cache);
203       cmp->cache = NULL;
204       list = cmp->items;
205       while (*prefix && list)
206         {
207           if (!strncmp (prefix,
208                         cmp->func ? cmp->func (list->data) : (gchar*) list->data,
209                         len))
210             cmp->cache = g_list_prepend (cmp->cache, list->data);
211           list = list->next;
212         }
213     }
214   if (cmp->prefix)
215     {
216       g_free (cmp->prefix);
217       cmp->prefix = NULL;
218     }
219   if (cmp->cache)
220     cmp->prefix = g_strdup (prefix);
221   completion_check_cache (cmp, new_prefix);
222   
223   return *prefix ? cmp->cache : cmp->items;
224 }
225
226 void 
227 g_completion_free (GCompletion* cmp)
228 {
229   g_return_if_fail (cmp != NULL);
230   
231   g_completion_clear_items (cmp);
232   g_free (cmp);
233 }
234
235 #ifdef TEST_COMPLETION
236 #include <stdio.h>
237 int
238 main (int   argc,
239       char* argv[])
240 {
241   FILE *file;
242   gchar buf[1024];
243   GList *list;
244   GList *result;
245   GList *tmp;
246   GCompletion *cmp;
247   gint i;
248   gchar *longp = NULL;
249   
250   if (argc < 3)
251     {
252       g_warning ("Usage: %s filename prefix1 [prefix2 ...]\n", argv[0]);
253       return 1;
254     }
255   
256   file = fopen (argv[1], "r");
257   if (!file)
258     {
259       g_warning ("Cannot open %s\n", argv[1]);
260       return 1;
261     }
262   
263   cmp = g_completion_new (NULL);
264   list = g_list_alloc ();
265   while (fgets (buf, 1024, file))
266     {
267       list->data = g_strdup (buf);
268       g_completion_add_items (cmp, list);
269     }
270   fclose (file);
271   
272   for (i = 2; i < argc; ++i)
273     {
274       printf ("COMPLETING: %s\n", argv[i]);
275       result = g_completion_complete (cmp, argv[i], &longp);
276       g_list_foreach (result, (GFunc) printf, NULL);
277       printf ("LONG MATCH: %s\n", longp);
278       g_free (longp);
279       longp = NULL;
280     }
281   
282   g_list_foreach (cmp->items, (GFunc) g_free, NULL);
283   g_completion_free (cmp);
284   g_list_free (list);
285   
286   return 0;
287 }
288 #endif