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