]> Pileus Git - ~andy/gtk/blob - gtk/gtksearchenginesimple.c
label: Fix memleak
[~andy/gtk] / gtk / gtksearchenginesimple.c
1 /*
2  * Copyright (C) 2005 Red Hat, Inc
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, see <http://www.gnu.org/licenses/>.
16  *
17  * Author: Alexander Larsson <alexl@redhat.com>
18  *
19  * Based on nautilus-search-engine-simple.c
20  */
21
22 #include "config.h"
23
24 /* these must be defined even when HAVE_GNU_FTW is not defined
25  * because (really) old versions of GNU libc have ftw.h but do
26  * export ftw() and friends only if _XOPEN_SOURCE and _GNU_SOURCE
27  * are defined. see bug #444097.
28  */
29 #define _XOPEN_SOURCE 600
30 #define _GNU_SOURCE
31
32 #ifdef HAVE_FTW_H
33 #include <ftw.h>
34 #endif
35
36 #include <gdk/gdk.h>
37
38 #include "gtksearchenginesimple.h"
39 #include "gtkprivate.h"
40
41 #include <string.h>
42
43 #define BATCH_SIZE 500
44
45 typedef struct 
46 {
47   GtkSearchEngineSimple *engine;
48   
49   gchar *path;
50   gchar **words;
51   GList *found_list;
52   
53   gint n_processed_files;
54   GList *uri_hits;
55   
56   /* accessed on both threads: */
57   volatile gboolean cancelled;
58 } SearchThreadData;
59
60
61 struct _GtkSearchEngineSimplePrivate 
62 {
63   GtkQuery *query;
64   
65   SearchThreadData *active_search;
66   
67   gboolean query_finished;
68 };
69
70
71 G_DEFINE_TYPE (GtkSearchEngineSimple, _gtk_search_engine_simple, GTK_TYPE_SEARCH_ENGINE);
72
73 static void
74 gtk_search_engine_simple_dispose (GObject *object)
75 {
76   GtkSearchEngineSimple *simple;
77   GtkSearchEngineSimplePrivate *priv;
78   
79   simple = GTK_SEARCH_ENGINE_SIMPLE (object);
80   priv = simple->priv;
81   
82   if (priv->query) 
83     {
84       g_object_unref (priv->query);
85       priv->query = NULL;
86     }
87
88   if (priv->active_search)
89     {
90       priv->active_search->cancelled = TRUE;
91       priv->active_search = NULL;
92     }
93   
94   G_OBJECT_CLASS (_gtk_search_engine_simple_parent_class)->dispose (object);
95 }
96
97 static SearchThreadData *
98 search_thread_data_new (GtkSearchEngineSimple *engine,
99                         GtkQuery              *query)
100 {
101   SearchThreadData *data;
102   char *text, *lower, *uri;
103   
104   data = g_new0 (SearchThreadData, 1);
105   
106   data->engine = g_object_ref (engine);
107   uri = _gtk_query_get_location (query);
108   if (uri != NULL) 
109     {
110       data->path = g_filename_from_uri (uri, NULL, NULL);
111       g_free (uri);
112     }
113   if (data->path == NULL)
114     data->path = g_strdup (g_get_home_dir ());
115         
116   text = _gtk_query_get_text (query);
117   lower = g_ascii_strdown (text, -1);
118   data->words = g_strsplit (lower, " ", -1);
119   g_free (text);
120   g_free (lower);
121   
122   return data;
123 }
124
125 static void 
126 search_thread_data_free (SearchThreadData *data)
127 {
128   g_object_unref (data->engine);
129   g_free (data->path);
130   g_strfreev (data->words);
131   g_free (data);
132 }
133
134 static gboolean
135 search_thread_done_idle (gpointer user_data)
136 {
137   SearchThreadData *data;
138
139   data = user_data;
140   
141   if (!data->cancelled)
142     _gtk_search_engine_finished (GTK_SEARCH_ENGINE (data->engine));
143      
144   data->engine->priv->active_search = NULL;
145   search_thread_data_free (data);
146   
147   return FALSE;
148 }
149
150 typedef struct 
151 {
152   GList *uris;
153   SearchThreadData *thread_data;
154 } SearchHits;
155
156
157 static gboolean
158 search_thread_add_hits_idle (gpointer user_data)
159 {
160   SearchHits *hits;
161
162   hits = user_data;
163
164   if (!hits->thread_data->cancelled) 
165     {
166       _gtk_search_engine_hits_added (GTK_SEARCH_ENGINE (hits->thread_data->engine),
167                                     hits->uris);
168     }
169
170   g_list_free_full (hits->uris, g_free);
171   g_free (hits);
172         
173   return FALSE;
174 }
175
176 static void
177 send_batch (SearchThreadData *data)
178 {
179   SearchHits *hits;
180   
181   data->n_processed_files = 0;
182   
183   if (data->uri_hits) 
184     {
185       hits = g_new (SearchHits, 1);
186       hits->uris = data->uri_hits;
187       hits->thread_data = data;
188       
189       gdk_threads_add_idle (search_thread_add_hits_idle, hits);
190     }
191
192   data->uri_hits = NULL;
193 }
194
195 static GPrivate search_thread_data;
196
197 #ifdef HAVE_FTW_H
198 static int
199 search_visit_func (const char        *fpath,
200                    const struct stat *sb,
201                    int                typeflag,
202                    struct FTW        *ftwbuf)
203 {
204   SearchThreadData *data;
205   gint i;
206   const gchar *name;
207   gchar *lower_name;
208   gchar *uri;
209   gboolean hit;
210   gboolean is_hidden;
211
212   data = (SearchThreadData*)g_private_get (&search_thread_data);
213
214   if (data->cancelled)
215 #ifdef HAVE_GNU_FTW
216     return FTW_STOP;
217 #else
218     return 1;
219 #endif /* HAVE_GNU_FTW */
220
221   name = strrchr (fpath, '/');
222   if (name)
223     name++;
224   else
225     name = fpath;
226
227   is_hidden = *name == '.';
228         
229   hit = FALSE;
230   
231   if (!is_hidden) 
232     {
233       lower_name = g_ascii_strdown (name, -1);
234       
235       hit = TRUE;
236       for (i = 0; data->words[i] != NULL; i++) 
237         {
238           if (strstr (lower_name, data->words[i]) == NULL) 
239             {
240               hit = FALSE;
241               break;
242             }
243         }
244       g_free (lower_name);
245     }
246
247   if (hit) 
248     {
249       uri = g_filename_to_uri (fpath, NULL, NULL);
250       data->uri_hits = g_list_prepend (data->uri_hits, uri);
251     }
252
253   data->n_processed_files++;
254   
255   if (data->n_processed_files > BATCH_SIZE)
256     send_batch (data);
257
258 #ifdef HAVE_GNU_FTW
259   if (is_hidden)
260     return FTW_SKIP_SUBTREE;
261   else
262     return FTW_CONTINUE;
263 #else
264   return 0;
265 #endif /* HAVE_GNU_FTW */
266 }
267 #endif /* HAVE_FTW_H */
268
269 static gpointer 
270 search_thread_func (gpointer user_data)
271 {
272 #ifdef HAVE_FTW_H
273   SearchThreadData *data;
274   
275   data = user_data;
276   
277   g_private_set (&search_thread_data, data);
278
279   nftw (data->path, search_visit_func, 20,
280 #ifdef HAVE_GNU_FTW
281         FTW_ACTIONRETVAL |
282 #endif
283         FTW_PHYS);
284
285   send_batch (data);
286   
287   gdk_threads_add_idle (search_thread_done_idle, data);
288 #endif /* HAVE_FTW_H */
289   
290   return NULL;
291 }
292
293 static void
294 gtk_search_engine_simple_start (GtkSearchEngine *engine)
295 {
296   GtkSearchEngineSimple *simple;
297   SearchThreadData *data;
298   
299   simple = GTK_SEARCH_ENGINE_SIMPLE (engine);
300   
301   if (simple->priv->active_search != NULL)
302     return;
303   
304   if (simple->priv->query == NULL)
305     return;
306         
307   data = search_thread_data_new (simple, simple->priv->query);
308   
309   g_thread_unref (g_thread_new ("file-search", search_thread_func, data));
310   
311   simple->priv->active_search = data;
312 }
313
314 static void
315 gtk_search_engine_simple_stop (GtkSearchEngine *engine)
316 {
317   GtkSearchEngineSimple *simple;
318   
319   simple = GTK_SEARCH_ENGINE_SIMPLE (engine);
320   
321   if (simple->priv->active_search != NULL) 
322     {
323       simple->priv->active_search->cancelled = TRUE;
324       simple->priv->active_search = NULL;
325     }
326 }
327
328 static gboolean
329 gtk_search_engine_simple_is_indexed (GtkSearchEngine *engine)
330 {
331   return FALSE;
332 }
333
334 static void
335 gtk_search_engine_simple_set_query (GtkSearchEngine *engine, 
336                                     GtkQuery        *query)
337 {
338   GtkSearchEngineSimple *simple;
339   
340   simple = GTK_SEARCH_ENGINE_SIMPLE (engine);
341   
342   if (query)
343     g_object_ref (query);
344
345   if (simple->priv->query) 
346     g_object_unref (simple->priv->query);
347
348   simple->priv->query = query;
349 }
350
351 static void
352 _gtk_search_engine_simple_class_init (GtkSearchEngineSimpleClass *class)
353 {
354   GObjectClass *gobject_class;
355   GtkSearchEngineClass *engine_class;
356   
357   gobject_class = G_OBJECT_CLASS (class);
358   gobject_class->dispose = gtk_search_engine_simple_dispose;
359   
360   engine_class = GTK_SEARCH_ENGINE_CLASS (class);
361   engine_class->set_query = gtk_search_engine_simple_set_query;
362   engine_class->start = gtk_search_engine_simple_start;
363   engine_class->stop = gtk_search_engine_simple_stop;
364   engine_class->is_indexed = gtk_search_engine_simple_is_indexed;
365
366   g_type_class_add_private (gobject_class, sizeof (GtkSearchEngineSimplePrivate));
367 }
368
369 static void
370 _gtk_search_engine_simple_init (GtkSearchEngineSimple *engine)
371 {
372   engine->priv = G_TYPE_INSTANCE_GET_PRIVATE (engine, GTK_TYPE_SEARCH_ENGINE_SIMPLE, GtkSearchEngineSimplePrivate);
373 }
374
375 GtkSearchEngine *
376 _gtk_search_engine_simple_new (void)
377 {
378 #ifdef HAVE_FTW_H
379   return g_object_new (GTK_TYPE_SEARCH_ENGINE_SIMPLE, NULL);
380 #else
381   return NULL;
382 #endif
383 }