gnome-shell-3.6.3.1/src/hotplug-sniffer/shell-mime-sniffer.c

No issues found

Incomplete coverage

Tool Failure ID Location Function Message Data
clang-analyzer no-output-found hotplug-sniffer/shell-mime-sniffer.c Message(text='Unable to locate XML output from invoke-clang-analyzer') None
Failure running clang-analyzer ('no-output-found')
Message
Unable to locate XML output from invoke-clang-analyzer
  1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
  2 /*
  3  * Copyright (C) 1999, 2000, 2001 Eazel, Inc.
  4  * Copyright (C) 2011 Red Hat, Inc.
  5  *
  6  * This program is free software; you can redistribute it and/or
  7  * modify it under the terms of the GNU General Public License as
  8  * published by the Free Software Foundation; either version 2 of the
  9  * License, or (at your option) any later version.
 10  *
 11  * This program is distributed in the hope that it will be useful, but
 12  * WITHOUT ANY WARRANTY; without even the implied warranty of
 13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 14  * General Public License for more details.
 15  *
 16  * You should have received a copy of the GNU General Public License
 17  * along with this program; if not, write to the Free Software
 18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 19  * 02111-1307, USA.
 20  *
 21  * Author: Cosimo Cecchi <cosimoc@redhat.com>
 22  *
 23  * The code for crawling the directory hierarchy is based on
 24  * nautilus/libnautilus-private/nautilus-directory-async.c, with
 25  * the following copyright and author:
 26  *
 27  * Copyright (C) 1999, 2000, 2001 Eazel, Inc.
 28  * Author: Darin Adler <darin@bentspoon.com>
 29  *
 30  */
 31 
 32 #include "shell-mime-sniffer.h"
 33 #include "hotplug-mimetypes.h"
 34 
 35 #include <glib/gi18n.h>
 36 
 37 #include <gdk-pixbuf/gdk-pixbuf.h>
 38 
 39 #define LOADER_ATTRS                          \
 40   G_FILE_ATTRIBUTE_STANDARD_TYPE ","          \
 41   G_FILE_ATTRIBUTE_STANDARD_NAME ","          \
 42   G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE ","
 43 
 44 #define WATCHDOG_TIMEOUT 1500
 45 #define DIRECTORY_LOAD_ITEMS_PER_CALLBACK 100
 46 #define HIGH_SCORE_RATIO 0.10
 47 
 48 G_DEFINE_TYPE (ShellMimeSniffer, shell_mime_sniffer, G_TYPE_OBJECT);
 49 
 50 enum {
 51   PROP_FILE = 1,
 52   NUM_PROPERTIES
 53 };
 54 
 55 static GHashTable *image_type_table = NULL;
 56 static GHashTable *audio_type_table = NULL;
 57 static GHashTable *video_type_table = NULL;
 58 static GHashTable *docs_type_table = NULL;
 59 
 60 static GParamSpec *properties[NUM_PROPERTIES] = { NULL, };
 61 
 62 typedef struct {
 63   ShellMimeSniffer *self;
 64 
 65   GFile *file;
 66   GFileEnumerator *enumerator;
 67   GList *deep_count_subdirectories;
 68 
 69   gint audio_count;
 70   gint image_count;
 71   gint document_count;
 72   gint video_count;
 73 
 74   gint total_items;
 75 } DeepCountState;
 76 
 77 struct _ShellMimeSnifferPrivate {
 78   GFile *file;
 79 
 80   GCancellable *cancellable;
 81   guint watchdog_id;
 82 
 83   GSimpleAsyncResult *async_result;
 84   gchar **sniffed_mime;
 85 };
 86 
 87 static void deep_count_load (DeepCountState *state,
 88                              GFile *file);
 89 
 90 static void
 91 init_mimetypes (void)
 92 {
 93   static gsize once_init = 0;
 94 
 95   if (g_once_init_enter (&once_init))
 96     {
 97       GSList *formats, *l;
 98       GdkPixbufFormat *format;
 99       gchar **types;
100       gint idx;
101 
102       image_type_table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
103       video_type_table = g_hash_table_new (g_str_hash, g_str_equal);
104       audio_type_table = g_hash_table_new (g_str_hash, g_str_equal);
105       docs_type_table = g_hash_table_new (g_str_hash, g_str_equal);
106 
107       formats = gdk_pixbuf_get_formats ();
108 
109       for (l = formats; l != NULL; l = l->next)
110         {
111           format = l->data;
112           types = gdk_pixbuf_format_get_mime_types (format);
113 
114           for (idx = 0; types[idx] != NULL; idx++)
115             g_hash_table_insert (image_type_table, g_strdup (types[idx]), GINT_TO_POINTER (1));
116 
117           g_strfreev (types);
118         }
119 
120       g_slist_free (formats);
121 
122       for (idx = 0; audio_mimetypes[idx] != NULL; idx++)
123         g_hash_table_insert (audio_type_table, (gpointer) audio_mimetypes[idx], GINT_TO_POINTER (1));
124 
125       for (idx = 0; video_mimetypes[idx] != NULL; idx++)
126         g_hash_table_insert (video_type_table, (gpointer) video_mimetypes[idx], GINT_TO_POINTER (1));
127 
128       for (idx = 0; docs_mimetypes[idx] != NULL; idx++)
129         g_hash_table_insert (docs_type_table, (gpointer) docs_mimetypes[idx], GINT_TO_POINTER (1));
130 
131       g_once_init_leave (&once_init, 1);
132     }
133 }
134 
135 static void
136 add_content_type_to_cache (DeepCountState *state,
137                            const gchar *content_type)
138 {
139   gboolean matched = TRUE;
140 
141   if (g_hash_table_lookup (image_type_table, content_type))
142     state->image_count++;
143   else if (g_hash_table_lookup (video_type_table, content_type))
144     state->video_count++;
145   else if (g_hash_table_lookup (docs_type_table, content_type))
146     state->document_count++;
147   else if (g_hash_table_lookup (audio_type_table, content_type))
148     state->audio_count++;
149   else
150     matched = FALSE;
151 
152   if (matched)
153     state->total_items++;
154 }
155 
156 typedef struct {
157   const gchar *type;
158   gdouble ratio;
159 } SniffedResult;
160 
161 static gint
162 results_cmp_func (gconstpointer a,
163                   gconstpointer b)
164 {
165   const SniffedResult *sniffed_a = a;
166   const SniffedResult *sniffed_b = b;
167 
168   if (sniffed_a->ratio < sniffed_b->ratio)
169     return 1;
170 
171   if (sniffed_a->ratio > sniffed_b->ratio)
172     return -1;
173 
174   return 0;
175 }
176 
177 static void
178 prepare_async_result (DeepCountState *state)
179 {
180   ShellMimeSniffer *self = state->self;
181   GArray *results;
182   GPtrArray *sniffed_mime;
183   SniffedResult result;
184 
185   sniffed_mime = g_ptr_array_new ();
186   results = g_array_new (TRUE, TRUE, sizeof (SniffedResult));
187 
188   if (state->total_items == 0)
189     goto out;
190 
191   result.type = "x-content/video";
192   result.ratio = (gdouble) state->video_count / (gdouble) state->total_items;
193   g_array_append_val (results, result);
194 
195   result.type = "x-content/audio";
196   result.ratio = (gdouble) state->audio_count / (gdouble) state->total_items;
197   g_array_append_val (results, result);
198 
199   result.type = "x-content/pictures";
200   result.ratio = (gdouble) state->image_count / (gdouble) state->total_items;
201   g_array_append_val (results, result);
202 
203   result.type = "x-content/documents";
204   result.ratio = (gdouble) state->document_count / (gdouble) state->total_items;
205   g_array_append_val (results, result);
206 
207   g_array_sort (results, results_cmp_func);
208 
209   result = g_array_index (results, SniffedResult, 0);
210   g_ptr_array_add (sniffed_mime, g_strdup (result.type));
211 
212   /* if other types score high in ratio, add them, up to three */
213   result = g_array_index (results, SniffedResult, 1);
214   if (result.ratio < HIGH_SCORE_RATIO)
215     goto out;
216   g_ptr_array_add (sniffed_mime, g_strdup (result.type));
217 
218   result = g_array_index (results, SniffedResult, 2);
219   if (result.ratio < HIGH_SCORE_RATIO)
220     goto out;
221   g_ptr_array_add (sniffed_mime, g_strdup (result.type));
222 
223  out:
224   g_ptr_array_add (sniffed_mime, NULL);
225   self->priv->sniffed_mime = (gchar **) g_ptr_array_free (sniffed_mime, FALSE);
226 
227   g_array_free (results, TRUE);
228   g_simple_async_result_complete_in_idle (self->priv->async_result);
229 }
230 
231 /* adapted from nautilus/libnautilus-private/nautilus-directory-async.c */
232 static void
233 deep_count_one (DeepCountState *state,
234 		GFileInfo *info)
235 {
236   GFile *subdir;
237   const char *content_type;
238 
239   if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY)
240     {
241       /* record the fact that we have to descend into this directory */
242       subdir = g_file_get_child (state->file, g_file_info_get_name (info));
243       state->deep_count_subdirectories =
244         g_list_append (state->deep_count_subdirectories, subdir);
245     } 
246   else
247     {
248       content_type = g_file_info_get_content_type (info);
249       add_content_type_to_cache (state, content_type);
250     }
251 }
252 
253 static void
254 deep_count_finish (DeepCountState *state)
255 {
256   prepare_async_result (state);
257 
258   if (state->enumerator)
259     {
260       if (!g_file_enumerator_is_closed (state->enumerator))
261         g_file_enumerator_close_async (state->enumerator,
262                                        0, NULL, NULL, NULL);
263 
264       g_object_unref (state->enumerator);
265     }
266 
267   g_cancellable_reset (state->self->priv->cancellable);
268   g_clear_object (&state->file);
269 
270   g_list_free_full (state->deep_count_subdirectories, g_object_unref);
271 
272   g_free (state);
273 }
274 
275 static void
276 deep_count_next_dir (DeepCountState *state)
277 {
278   GFile *new_file;
279 
280   g_clear_object (&state->file);
281 
282   if (state->deep_count_subdirectories != NULL)
283     {
284       /* Work on a new directory. */
285       new_file = state->deep_count_subdirectories->data;
286       state->deep_count_subdirectories =
287         g_list_remove (state->deep_count_subdirectories, new_file);
288 
289       deep_count_load (state, new_file);
290       g_object_unref (new_file);
291     }
292   else
293     {
294       deep_count_finish (state);
295     }
296 }
297 
298 static void
299 deep_count_more_files_callback (GObject *source_object,
300 				GAsyncResult *res,
301 				gpointer user_data)
302 {
303   DeepCountState *state;
304   GList *files, *l;
305   GFileInfo *info;
306 
307   state = user_data;
308 
309   if (g_cancellable_is_cancelled (state->self->priv->cancellable))
310     {
311       deep_count_finish (state);
312       return;
313     }
314 	
315   files = g_file_enumerator_next_files_finish (state->enumerator,
316                                                res, NULL);
317   
318   for (l = files; l != NULL; l = l->next)
319     {
320       info = l->data;
321       deep_count_one (state, info);
322       g_object_unref (info);
323     }
324 
325   if (files == NULL)
326     {
327       g_file_enumerator_close_async (state->enumerator, 0, NULL, NULL, NULL);
328       g_object_unref (state->enumerator);
329       state->enumerator = NULL;
330 
331       deep_count_next_dir (state);
332     }
333   else
334     {
335       g_file_enumerator_next_files_async (state->enumerator,
336                                           DIRECTORY_LOAD_ITEMS_PER_CALLBACK,
337                                           G_PRIORITY_LOW,
338                                           state->self->priv->cancellable,
339                                           deep_count_more_files_callback,
340                                           state);
341     }
342 
343   g_list_free (files);
344 }
345 
346 static void
347 deep_count_callback (GObject *source_object,
348 		     GAsyncResult *res,
349 		     gpointer user_data)
350 {
351   DeepCountState *state;
352   GFileEnumerator *enumerator;
353 
354   state = user_data;
355 
356   if (g_cancellable_is_cancelled (state->self->priv->cancellable))
357     {
358       deep_count_finish (state);
359       return;
360     }
361 
362   enumerator = g_file_enumerate_children_finish (G_FILE (source_object),
363                                                  res, NULL);
364 	
365   if (enumerator == NULL)
366     {
367       deep_count_next_dir (state);
368     }
369   else
370     {
371       state->enumerator = enumerator;
372       g_file_enumerator_next_files_async (state->enumerator,
373                                           DIRECTORY_LOAD_ITEMS_PER_CALLBACK,
374                                           G_PRIORITY_LOW,
375                                           state->self->priv->cancellable,
376                                           deep_count_more_files_callback,
377                                           state);
378     }
379 }
380 
381 static void
382 deep_count_load (DeepCountState *state,
383                  GFile *file)
384 {
385   state->file = g_object_ref (file);
386 
387   g_file_enumerate_children_async (state->file,
388                                    LOADER_ATTRS,
389                                    G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, /* flags */
390                                    G_PRIORITY_LOW, /* prio */
391                                    state->self->priv->cancellable,
392                                    deep_count_callback,
393                                    state);
394 }
395 
396 static void
397 deep_count_start (ShellMimeSniffer *self)
398 {
399   DeepCountState *state;
400 
401   state = g_new0 (DeepCountState, 1);
402   state->self = self;
403 
404   deep_count_load (state, self->priv->file);
405 }
406 
407 static void
408 query_info_async_ready_cb (GObject *source,
409                            GAsyncResult *res,
410                            gpointer user_data)
411 {
412   GFileInfo *info;
413   GError *error = NULL;
414   ShellMimeSniffer *self = user_data;
415 
416   info = g_file_query_info_finish (G_FILE (source),
417                                    res, &error);
418 
419   if (error != NULL)
420     {
421       g_simple_async_result_take_error (self->priv->async_result,
422                                         error);
423       g_simple_async_result_complete_in_idle (self->priv->async_result);
424 
425       return;
426     }
427 
428   if (g_file_info_get_file_type (info) != G_FILE_TYPE_DIRECTORY)
429     {
430       g_simple_async_result_set_error (self->priv->async_result,
431                                        G_IO_ERROR,
432                                        G_IO_ERROR_NOT_DIRECTORY,
433                                        "Not a directory");
434       g_simple_async_result_complete_in_idle (self->priv->async_result);
435 
436       return;
437     }
438 
439   deep_count_start (self);
440 }
441 
442 static gboolean
443 watchdog_timeout_reached_cb (gpointer user_data)
444 {
445   ShellMimeSniffer *self = user_data;
446 
447   self->priv->watchdog_id = 0;
448   g_cancellable_cancel (self->priv->cancellable);
449 
450   return FALSE;
451 }
452 
453 static void
454 start_loading_file (ShellMimeSniffer *self)
455 {
456   g_file_query_info_async (self->priv->file,
457                            LOADER_ATTRS,
458                            G_FILE_QUERY_INFO_NONE,
459                            G_PRIORITY_DEFAULT,
460                            self->priv->cancellable,
461                            query_info_async_ready_cb,
462                            self);
463 }
464 
465 static void
466 shell_mime_sniffer_set_file (ShellMimeSniffer *self,
467                             GFile *file)
468 {
469   g_clear_object (&self->priv->file);
470   self->priv->file = g_object_ref (file);
471 }
472 
473 static void
474 shell_mime_sniffer_dispose (GObject *object)
475 {
476   ShellMimeSniffer *self = SHELL_MIME_SNIFFER (object);
477 
478   g_clear_object (&self->priv->file);
479   g_clear_object (&self->priv->cancellable);
480   g_clear_object (&self->priv->async_result);
481 
482   if (self->priv->watchdog_id != 0)
483     {
484       g_source_remove (self->priv->watchdog_id);
485       self->priv->watchdog_id = 0;
486     }
487 
488   G_OBJECT_CLASS (shell_mime_sniffer_parent_class)->dispose (object);
489 }
490 
491 static void
492 shell_mime_sniffer_finalize (GObject *object)
493 {
494   ShellMimeSniffer *self = SHELL_MIME_SNIFFER (object);
495 
496   g_strfreev (self->priv->sniffed_mime);
497 
498   G_OBJECT_CLASS (shell_mime_sniffer_parent_class)->finalize (object);
499 }
500 
501 static void
502 shell_mime_sniffer_get_property (GObject *object,
503                                 guint       prop_id,
504                                 GValue     *value,
505                                 GParamSpec *pspec)
506 {
507   ShellMimeSniffer *self = SHELL_MIME_SNIFFER (object);
508 
509   switch (prop_id) {
510   case PROP_FILE:
511     g_value_set_object (value, self->priv->file);
512     break;
513   default:
514     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
515     break;
516   }
517 }
518 
519 static void
520 shell_mime_sniffer_set_property (GObject *object,
521                                 guint       prop_id,
522                                 const GValue *value,
523                                 GParamSpec *pspec)
524 {
525   ShellMimeSniffer *self = SHELL_MIME_SNIFFER (object);
526 
527   switch (prop_id) {
528   case PROP_FILE:
529     shell_mime_sniffer_set_file (self, g_value_get_object (value));
530     break;
531   default:
532     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
533     break;
534   }
535 }
536 
537 static void
538 shell_mime_sniffer_class_init (ShellMimeSnifferClass *klass)
539 {
540   GObjectClass *oclass;
541 
542   oclass = G_OBJECT_CLASS (klass);
543   oclass->dispose = shell_mime_sniffer_dispose;
544   oclass->finalize = shell_mime_sniffer_finalize;
545   oclass->get_property = shell_mime_sniffer_get_property;
546   oclass->set_property = shell_mime_sniffer_set_property;
547 
548   properties[PROP_FILE] =
549     g_param_spec_object ("file",
550                          "File",
551                          "The loaded file",
552                          G_TYPE_FILE,
553                          G_PARAM_READWRITE);
554 
555   g_type_class_add_private (klass, sizeof (ShellMimeSnifferPrivate));
556   g_object_class_install_properties (oclass, NUM_PROPERTIES, properties);
557 }
558 
559 static void
560 shell_mime_sniffer_init (ShellMimeSniffer *self)
561 {
562   self->priv =
563     G_TYPE_INSTANCE_GET_PRIVATE (self,
564                                  SHELL_TYPE_MIME_SNIFFER,
565                                  ShellMimeSnifferPrivate);
566   init_mimetypes ();
567 }
568 
569 ShellMimeSniffer *
570 shell_mime_sniffer_new (GFile *file)
571 {
572   return g_object_new (SHELL_TYPE_MIME_SNIFFER,
573                        "file", file,
574                        NULL);
575 }
576 
577 void
578 shell_mime_sniffer_sniff_async (ShellMimeSniffer *self,
579                                 GAsyncReadyCallback callback,
580                                 gpointer user_data)
581 {
582   g_assert (self->priv->watchdog_id == 0);
583   g_assert (self->priv->async_result == NULL);
584 
585   self->priv->async_result = 
586     g_simple_async_result_new (G_OBJECT (self),
587                                callback, user_data,
588                                shell_mime_sniffer_sniff_finish);
589   
590   self->priv->cancellable = g_cancellable_new ();
591 
592   self->priv->watchdog_id =
593     g_timeout_add (WATCHDOG_TIMEOUT,
594                    watchdog_timeout_reached_cb, self);
595 
596   start_loading_file (self);
597 }
598 
599 gchar **
600 shell_mime_sniffer_sniff_finish (ShellMimeSniffer *self,
601                                  GAsyncResult *res,
602                                  GError **error)
603 {
604   if (g_simple_async_result_propagate_error (self->priv->async_result, error))
605     return NULL;
606 
607   return g_strdupv (self->priv->sniffed_mime);
608 }