Commit d0e90e40 authored by Matthias Clasen's avatar Matthias Clasen Committed by Matthias Clasen
Browse files

Sync from upstream

2004-11-08  Matthias Clasen  <mclasen@redhat.com>

	* Sync from upstream
parent 9b86c72c
2004-11-08 Matthias Clasen <mclasen@redhat.com>
* Sync from upstream
2004-10-27 Matthias Clasen <mclasen@redhat.com>
* === Released 2.5.4 ===
......
......@@ -5,12 +5,16 @@ noinst_LTLIBRARIES = libxdgmime.la
libxdgmime_la_SOURCES = \
xdgmime.c \
xdgmime.h \
xdgmimealias.c \
xdgmimealias.h \
xdgmimeglob.c \
xdgmimeglob.h \
xdgmimeint.c \
xdgmimeint.h \
xdgmimemagic.c \
xdgmimemagic.h
xdgmimemagic.h \
xdgmimeparent.c \
xdgmimeparent.h
noinst_PROGRAMS = test-mime
......
/* -*- mode: C; c-file-style: "gnu" -*- */
/* test-mime.c: Test program for mime type identification
*
* More info can be found at http://www.freedesktop.org/standards/
*
* Copyright (C) 2003 Red Hat, Inc.
* Copyright (C) 2003 Jonathan Blandford <jrb@alum.mit.edu>
*
* Licensed under the Academic Free License version 2.0
* Or under the following terms:
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include <config.h>
#include "xdgmime.h"
#include "xdgmimeglob.h"
#include <string.h>
......@@ -61,6 +33,67 @@ test_glob_type (void)
test_individual_glob ("tree.[ch]", XDG_GLOB_FULL);
}
static void
test_alias (const char *mime_a,
const char *mime_b,
int expected)
{
int actual;
actual = xdg_mime_mime_type_equal (mime_a, mime_b);
if (actual != expected)
{
printf ("Test Failed: %s is %s to %s\n",
mime_a, actual ? "equal" : "not equal", mime_b);
}
}
static void
test_aliasing (void)
{
test_alias ("application/wordperfect", "application/vnd.wordperfect", 1);
test_alias ("application/x-gnome-app-info", "application/x-desktop", 1);
test_alias ("application/x-wordperfect", "application/vnd.wordperfect", 1);
test_alias ("application/x-wordperfect", "audio/x-midi", 0);
test_alias ("/", "vnd/vnd", 0);
test_alias ("application/octet-stream", "text/plain", 0);
test_alias ("text/plain", "text/*", 0);
}
static void
test_subclass (const char *mime_a,
const char *mime_b,
int expected)
{
int actual;
actual = xdg_mime_mime_type_subclass (mime_a, mime_b);
if (actual != expected)
{
printf ("Test Failed: %s is %s of %s\n",
mime_a, actual ? "subclass" : "not subclass", mime_b);
}
}
static void
test_subclassing (void)
{
test_subclass ("application/rtf", "text/plain", 1);
test_subclass ("message/news", "text/plain", 1);
test_subclass ("message/news", "message/*", 1);
test_subclass ("message/news", "text/*", 1);
test_subclass ("message/news", "application/octet-stream", 1);
test_subclass ("application/rtf", "application/octet-stream", 1);
test_subclass ("application/x-gnome-app-info", "text/plain", 1);
test_subclass ("image/x-djvu", "image/vnd.djvu", 1);
test_subclass ("image/vnd.djvu", "image/x-djvu", 1);
test_subclass ("image/vnd.djvu", "text/plain", 0);
test_subclass ("image/vnd.djvu", "text/*", 0);
test_subclass ("text/*", "text/plain", 0);
}
int
main (int argc, char *argv[])
{
......@@ -69,6 +102,9 @@ main (int argc, char *argv[])
int i;
test_glob_type ();
test_aliasing ();
test_subclassing ();
for (i = 1; i < argc; i++)
{
file_name = argv[i];
......@@ -76,6 +112,9 @@ main (int argc, char *argv[])
printf ("File \"%s\" has a mime-type of %s\n", file_name, result);
}
#if 0
xdg_mime_dump ();
#endif
return 0;
}
......@@ -3,8 +3,8 @@
*
* More info can be found at http://www.freedesktop.org/standards/
*
* Copyright (C) 2003 Red Hat, Inc.
* Copyright (C) 2003 Jonathan Blandford <jrb@alum.mit.edu>
* Copyright (C) 2003,2004 Red Hat, Inc.
* Copyright (C) 2003,2004 Jonathan Blandford <jrb@alum.mit.edu>
*
* Licensed under the Academic Free License version 2.0
* Or under the following terms:
......@@ -30,107 +30,357 @@
#include "xdgmimeint.h"
#include "xdgmimeglob.h"
#include "xdgmimemagic.h"
#include "xdgmimealias.h"
#include "xdgmimeparent.h"
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/time.h>
#include <unistd.h>
#include <assert.h>
typedef struct XdgDirTimeList XdgDirTimeList;
typedef struct XdgCallbackList XdgCallbackList;
static int need_reread = TRUE;
static time_t last_stat_time = 0;
static int initted = 0;
static XdgGlobHash *global_hash = NULL;
static XdgMimeMagic *global_magic = NULL;
static XdgAliasList *alias_list = NULL;
static XdgParentList *parent_list = NULL;
static XdgDirTimeList *dir_time_list = NULL;
static XdgCallbackList *callback_list = NULL;
const char *xdg_mime_type_unknown = "application/octet-stream";
enum
{
XDG_CHECKED_UNCHECKED,
XDG_CHECKED_VALID,
XDG_CHECKED_INVALID
};
struct XdgDirTimeList
{
time_t mtime;
char *directory_name;
int checked;
XdgDirTimeList *next;
};
struct XdgCallbackList
{
XdgCallbackList *next;
XdgCallbackList *prev;
int callback_id;
XdgMimeCallback callback;
void *data;
XdgMimeDestroy destroy;
};
/* Function called by xdg_run_command_on_dirs. If it returns TRUE, further
* directories aren't looked at */
typedef int (*XdgDirectoryFunc) (const char *directory,
void *user_data);
static XdgDirTimeList *
xdg_dir_time_list_new (void)
{
XdgDirTimeList *retval;
retval = calloc (1, sizeof (XdgDirTimeList));
retval->checked = XDG_CHECKED_UNCHECKED;
return retval;
}
static void
_xdg_mime_init_from_directory (const char *directory)
xdg_dir_time_list_free (XdgDirTimeList *list)
{
XdgDirTimeList *next;
while (list)
{
next = list->next;
free (list->directory_name);
free (list);
list = next;
}
}
static int
xdg_mime_init_from_directory (const char *directory)
{
char *file_name;
struct stat st;
XdgDirTimeList *list;
assert (directory != NULL);
file_name = malloc (strlen (directory) + strlen ("/mime/globs") + 1);
strcpy (file_name, directory);
strcat (file_name, "/mime/globs");
_xdg_mime_glob_read_from_file (global_hash, file_name);
free (file_name);
strcpy (file_name, directory); strcat (file_name, "/mime/globs");
if (stat (file_name, &st) == 0)
{
_xdg_mime_glob_read_from_file (global_hash, file_name);
list = xdg_dir_time_list_new ();
list->directory_name = file_name;
list->mtime = st.st_mtime;
list->next = dir_time_list;
dir_time_list = list;
}
else
{
free (file_name);
}
file_name = malloc (strlen (directory) + strlen ("/mime/magic") + 1);
strcpy (file_name, directory);
strcat (file_name, "/mime/magic");
_xdg_mime_magic_read_from_file (global_magic, file_name);
strcpy (file_name, directory); strcat (file_name, "/mime/magic");
if (stat (file_name, &st) == 0)
{
_xdg_mime_magic_read_from_file (global_magic, file_name);
list = xdg_dir_time_list_new ();
list->directory_name = file_name;
list->mtime = st.st_mtime;
list->next = dir_time_list;
dir_time_list = list;
}
else
{
free (file_name);
}
file_name = malloc (strlen (directory) + strlen ("/mime/aliases") + 1);
strcpy (file_name, directory); strcat (file_name, "/mime/aliases");
_xdg_mime_alias_read_from_file (alias_list, file_name);
free (file_name);
file_name = malloc (strlen (directory) + strlen ("/mime/subclasses") + 1);
strcpy (file_name, directory); strcat (file_name, "/mime/subclasses");
_xdg_mime_parent_read_from_file (parent_list, file_name);
free (file_name);
return FALSE; /* Keep processing */
}
/* Runs a command on all the directories in the search path */
static void
xdg_mime_init (void)
xdg_run_command_on_dirs (XdgDirectoryFunc func,
void *user_data)
{
if (initted == 0)
const char *xdg_data_home;
const char *xdg_data_dirs;
const char *ptr;
xdg_data_home = getenv ("XDG_DATA_HOME");
if (xdg_data_home)
{
if ((func) (xdg_data_home, user_data))
return;
}
else
{
const char *xdg_data_home;
const char *xdg_data_dirs;
const char *ptr;
const char *home;
global_hash = _xdg_glob_hash_new ();
global_magic = _xdg_mime_magic_new ();
home = getenv ("HOME");
if (home != NULL)
{
char *guessed_xdg_home;
int stop_processing;
guessed_xdg_home = malloc (strlen (home) + strlen ("/.local/share/") + 1);
strcpy (guessed_xdg_home, home);
strcat (guessed_xdg_home, "/.local/share/");
stop_processing = (func) (guessed_xdg_home, user_data);
free (guessed_xdg_home);
if (stop_processing)
return;
}
}
xdg_data_dirs = getenv ("XDG_DATA_DIRS");
if (xdg_data_dirs == NULL)
xdg_data_dirs = "/usr/local/share/:/usr/share/";
ptr = xdg_data_dirs;
while (*ptr != '\000')
{
const char *end_ptr;
char *dir;
int len;
int stop_processing;
end_ptr = ptr;
while (*end_ptr != ':' && *end_ptr != '\000')
end_ptr ++;
/* We look for globs and magic files based upon the XDG Base Directory
* Specification
*/
xdg_data_home = getenv ("XDG_DATA_HOME");
if (xdg_data_home)
if (end_ptr == ptr)
{
_xdg_mime_init_from_directory (xdg_data_home);
ptr++;
continue;
}
if (*end_ptr == ':')
len = end_ptr - ptr;
else
{
const char *home;
len = end_ptr - ptr + 1;
dir = malloc (len + 1);
strncpy (dir, ptr, len);
dir[len] = '\0';
stop_processing = (func) (dir, user_data);
free (dir);
if (stop_processing)
return;
ptr = end_ptr;
}
}
home = getenv ("HOME");
if (home != NULL)
/* Checks file_path to make sure it has the same mtime as last time it was
* checked. If it has a different mtime, or if the file doesn't exist, it
* returns FALSE.
*
* FIXME: This doesn't protect against permission changes.
*/
static int
xdg_check_file (const char *file_path)
{
struct stat st;
/* If the file exists */
if (stat (file_path, &st) == 0)
{
XdgDirTimeList *list;
for (list = dir_time_list; list; list = list->next)
{
if (! strcmp (list->directory_name, file_path) &&
st.st_mtime == list->mtime)
{
char *guessed_xdg_home;
if (list->checked == XDG_CHECKED_UNCHECKED)
list->checked = XDG_CHECKED_VALID;
else if (list->checked == XDG_CHECKED_VALID)
list->checked = XDG_CHECKED_INVALID;
guessed_xdg_home = malloc (strlen (home) + strlen ("/.local/share/") + 1);
strcpy (guessed_xdg_home, home);
strcat (guessed_xdg_home, "/.local/share/");
_xdg_mime_init_from_directory (guessed_xdg_home);
free (guessed_xdg_home);
return (list->checked != XDG_CHECKED_VALID);
}
}
return TRUE;
}
xdg_data_dirs = getenv ("XDG_DATA_DIRS");
if (xdg_data_dirs == NULL)
xdg_data_dirs = "/usr/local/share/:/usr/share/";
return FALSE;
}
ptr = xdg_data_dirs;
static int
xdg_check_dir (const char *directory,
int *invalid_dir_list)
{
int invalid;
char *file_name;
while (*ptr != '\000')
{
const char *end_ptr;
char *dir;
int len;
assert (directory != NULL);
end_ptr = ptr;
while (*end_ptr != ':' && *end_ptr != '\000')
end_ptr ++;
/* Check the globs file */
file_name = malloc (strlen (directory) + strlen ("/mime/globs") + 1);
strcpy (file_name, directory); strcat (file_name, "/mime/globs");
invalid = xdg_check_file (file_name);
free (file_name);
if (invalid)
{
*invalid_dir_list = TRUE;
return TRUE;
}
if (end_ptr == ptr)
{
ptr++;
continue;
}
/* Check the magic file */
file_name = malloc (strlen (directory) + strlen ("/mime/magic") + 1);
strcpy (file_name, directory); strcat (file_name, "/mime/magic");
invalid = xdg_check_file (file_name);
free (file_name);
if (invalid)
{
*invalid_dir_list = TRUE;
return TRUE;
}
if (*end_ptr == ':')
len = end_ptr - ptr;
else
len = end_ptr - ptr + 1;
dir = malloc (len + 1);
strncpy (dir, ptr, len);
dir[len] = '\0';
_xdg_mime_init_from_directory (dir);
free (dir);
ptr = end_ptr;
}
initted = 1;
return FALSE; /* Keep processing */
}
/* Walks through all the mime files stat()ing them to see if they've changed.
* Returns TRUE if they have. */
static int
xdg_check_dirs (void)
{
XdgDirTimeList *list;
int invalid_dir_list = FALSE;
for (list = dir_time_list; list; list = list->next)
list->checked = XDG_CHECKED_UNCHECKED;
xdg_run_command_on_dirs ((XdgDirectoryFunc) xdg_check_dir,
&invalid_dir_list);
if (invalid_dir_list)
return TRUE;
for (list = dir_time_list; list; list = list->next)
{
if (list->checked != XDG_CHECKED_VALID)
return TRUE;
}
return FALSE;
}
/* We want to avoid stat()ing on every single mime call, so we only look for
* newer files every 5 seconds. This will return TRUE if we need to reread the
* mime data from disk.
*/
static int
xdg_check_time_and_dirs (void)
{
struct timeval tv;
time_t current_time;
int retval = FALSE;
gettimeofday (&tv, NULL);
current_time = tv.tv_sec;
if (current_time >= last_stat_time + 5)
{
retval = xdg_check_dirs ();
last_stat_time = current_time;
}
return retval;
}
/* Called in every public function. It reloads the hash function if need be.
*/
static void
xdg_mime_init (void)
{
if (xdg_check_time_and_dirs ())
{
xdg_mime_shutdown ();
}
if (need_reread)
{
global_hash = _xdg_glob_hash_new ();
global_magic = _xdg_mime_magic_new ();
alias_list = _xdg_mime_alias_list_new ();
parent_list = _xdg_mime_parent_list_new ();
xdg_run_command_on_dirs ((XdgDirectoryFunc) xdg_mime_init_from_directory,
NULL);
need_reread = FALSE;
}
}
......@@ -239,17 +489,36 @@ xdg_mime_is_valid_mime_type (const char *mime_type)
void
xdg_mime_shutdown (void)
{
XdgCallbackList *list;
/* FIXME: Need to make this (and the whole library) thread safe */
if (initted)
if (dir_time_list)
{
xdg_dir_time_list_free (dir_time_list);
dir_time_list = NULL;
}
if (global_hash)
{
_xdg_glob_hash_free (global_hash);
global_hash = NULL;
}
if (global_magic)
{
_xdg_mime_magic_free (global_magic);
global_magic = NULL;
}
initted = 0;
if (alias_list)
{
_xdg_mime_alias_list_free (alias_list);
alias_list = NULL;
}
for (list = callback_list; list; list = list->next)
(list->callback) (list->data);
need_reread = TRUE;
}
int
......@@ -259,3 +528,165 @@ xdg_mime_get_max_buffer_extents (void)
return _xdg_mime_magic_get_buffer_extents (global_magic);
}
static const char *
unalias_mime_type (const char *mime_type)
{
const char *lookup;
if ((lookup = _xdg_mime_alias_list_lookup (alias_list, mime_type)) != NULL)
return lookup;
return mime_type;
}
int
xdg_mime_mime_type_equal (const char *mime_a,
const char *mime_b)