gnome-control-center/capplets/mime-type/mime-info.c
Jody Goldberg 71b67b6586 ui-review
2002-07-10 17:02:59 +00:00

492 lines
11 KiB
C

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
/* Copyright (C) 1998 Redhat Software Inc.
* Authors: Jonathan Blandford <jrb@redhat.com>
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "capplet-widget.h"
#include "gnome.h"
#include <string.h>
#include <sys/types.h>
#include <dirent.h>
#include <regex.h>
#include <ctype.h>
#include "mime-info.h"
#include "mime-data.h"
#include <errno.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#if !defined getc_unlocked && !defined HAVE_GETC_UNLOCKED
# define getc_unlocked(fp) getc (fp)
#endif
typedef struct {
char *mime_type;
GHashTable *keys;
} GnomeMimeContext;
typedef enum {
STATE_NONE,
STATE_LANG,
STATE_LOOKING_FOR_KEY,
STATE_ON_MIME_TYPE,
STATE_ON_KEY,
STATE_ON_VALUE
} ParserState;
static char *current_lang = NULL;
/*
* A hash table containing all of the Mime records for specific
* mime types (full description, like image/png)
*/
static GHashTable *specific_types;
static GHashTable *initial_specific_types;
/*
* A hash table containing all of the Mime records for non-specific
* mime types (like image/\*)
*/
static GHashTable *generic_types;
static GHashTable *initial_generic_types;
#define SWITCH_TO_MIME_TYPE() {
static GnomeMimeContext *
context_new (GString *str, gboolean is_default_context)
{
GnomeMimeContext *context;
GHashTable *table;
char *mime_type, *p;
mime_type = g_strdup (str->str);
if (is_default_context) {
if ((p = strstr (mime_type, "/*")) == NULL){
table = initial_specific_types;
} else {
*(p+1) = 0;
table = initial_generic_types;
}
} else {
if ((p = strstr (mime_type, "/*")) == NULL){
table = specific_types;
} else {
*(p+1) = 0;
table = generic_types;
}
}
context = g_hash_table_lookup (table, mime_type);
if (context)
return context;
context = g_new (GnomeMimeContext, 1);
context->mime_type = mime_type;
context->keys = g_hash_table_new (g_str_hash, g_str_equal);
g_hash_table_insert (table, context->mime_type, context);
return context;
}
static gboolean
release_key_and_value (gpointer key, gpointer value, gpointer user_data)
{
g_free (key);
g_free (value);
return TRUE;
}
static gboolean
remove_this_key (gpointer key, gpointer value, gpointer user_data)
{
if (strcmp ((gchar *)key, (gchar *)user_data) == 0){
g_free (key);
g_free (value);
return TRUE;
}
return FALSE;
}
static void
context_add_key (GnomeMimeContext *context, char *key, char *value)
{
char *v;
v = g_hash_table_lookup (context->keys, key);
if (v)
g_hash_table_foreach_remove (context->keys, remove_this_key, key);
g_hash_table_insert (context->keys, g_strdup (key), g_strdup (value));
}
static void
context_destroy (GnomeMimeContext *context)
{
/*
* Remove the context from our hash tables, we dont know
* where it is: so just remove it from both (it can
* only be in one).
*/
if (context->mime_type) {
g_hash_table_remove (specific_types, context->mime_type);
g_hash_table_remove (generic_types, context->mime_type);
}
/*
* Destroy it
*/
if (context->keys) {
g_hash_table_foreach_remove (context->keys, release_key_and_value, NULL);
g_hash_table_destroy (context->keys);
}
g_free (context->mime_type);
g_free (context);
}
static void
load_mime_type_info_from (char *filename)
{
FILE *mime_file;
gboolean in_comment, context_used;
GString *line;
int column, c;
ParserState state;
GnomeMimeContext *context, *default_context;
char *key;
mime_file = fopen (filename, "r");
if (mime_file == NULL)
return;
in_comment = FALSE;
context_used = FALSE;
column = 0;
context = NULL;
default_context = NULL;
key = NULL;
line = g_string_sized_new (120);
state = STATE_NONE;
while ((c = getc_unlocked (mime_file)) != EOF){
column++;
if (c == '\r')
continue;
if (c == '#' && column == 0){
in_comment = TRUE;
continue;
}
if (c == '\n'){
in_comment = FALSE;
column = 0;
if (state == STATE_ON_MIME_TYPE){
context = context_new (line, FALSE);
default_context = context_new (line, TRUE);
context_used = FALSE;
g_string_assign (line, "");
state = STATE_LOOKING_FOR_KEY;
continue;
}
if (state == STATE_ON_VALUE){
context_used = TRUE;
context_add_key (context, key, line->str);
context_add_key (default_context, key, line->str);
g_string_assign (line, "");
g_free (key);
key = NULL;
state = STATE_LOOKING_FOR_KEY;
continue;
}
continue;
}
if (in_comment)
continue;
switch (state){
case STATE_NONE:
if (c != ' ' && c != '\t')
state = STATE_ON_MIME_TYPE;
else
break;
/* fall down */
case STATE_ON_MIME_TYPE:
if (c == ':'){
in_comment = TRUE;
break;
}
g_string_append_c (line, c);
break;
case STATE_LOOKING_FOR_KEY:
if (c == '\t' || c == ' ')
break;
if (c == '['){
state = STATE_LANG;
break;
}
if (column == 1){
state = STATE_ON_MIME_TYPE;
g_string_append_c (line, c);
break;
}
state = STATE_ON_KEY;
/* falldown */
case STATE_ON_KEY:
if (c == '\\'){
c = getc (mime_file);
if (c == EOF)
break;
}
if (c == '='){
key = g_strdup (line->str);
g_string_assign (line, "");
state = STATE_ON_VALUE;
break;
}
g_string_append_c (line, c);
break;
case STATE_ON_VALUE:
g_string_append_c (line, c);
break;
case STATE_LANG:
if (c == ']'){
state = STATE_ON_KEY;
if (current_lang && line->str [0]){
if (strcmp (current_lang, line->str) != 0){
in_comment = TRUE;
state = STATE_LOOKING_FOR_KEY;
}
} else {
in_comment = TRUE;
state = STATE_LOOKING_FOR_KEY;
}
g_string_assign (line, "");
break;
}
g_string_append_c (line, c);
break;
}
}
if (context){
if (key && line->str [0]) {
context_add_key (context, key, line->str);
context_add_key (default_context, key, line->str);
} else
if (!context_used) {
context_destroy (context);
context_destroy (default_context);
}
}
g_string_free (line, TRUE);
if (key)
g_free (key);
fclose (mime_file);
}
void
set_mime_key_value (gchar *mime_type, gchar *key, gchar *value)
{
GnomeMimeContext *context;
/* Assume no generic context's for now. */
context = g_hash_table_lookup (specific_types, mime_type);
if (context == NULL) {
GString *str = g_string_new (mime_type);
context = context_new (str, FALSE);
g_string_free (str, TRUE);
}
context_add_key (context, key, value);
}
void
init_mime_info (void)
{
gchar *filename;
current_lang = getenv ("LANG");
specific_types = g_hash_table_new (g_str_hash, g_str_equal);
generic_types = g_hash_table_new (g_str_hash, g_str_equal);
initial_specific_types = g_hash_table_new (g_str_hash, g_str_equal);
initial_generic_types = g_hash_table_new (g_str_hash, g_str_equal);
filename = g_build_filename (gnome_util_user_home (), "/.gnome/mime-info/user.keys", NULL);
load_mime_type_info_from (filename);
g_free (filename);
}
const char *
local_mime_get_value (const char *mime_type, char *key)
{
char *value, *generic_type, *p;
GnomeMimeContext *context;
g_return_val_if_fail (mime_type != NULL, NULL);
g_return_val_if_fail (key != NULL, NULL);
context = g_hash_table_lookup (specific_types, mime_type);
if (context){
value = g_hash_table_lookup (context->keys, key);
if (value)
return value;
}
generic_type = g_strdup (mime_type);
p = strchr (generic_type, '/');
if (p)
*(p+1) = 0;
context = g_hash_table_lookup (generic_types, generic_type);
g_free (generic_type);
if (context){
value = g_hash_table_lookup (context->keys, key);
if (value)
return value;
}
return NULL;
}
static void
clean_mime_foreach (gpointer mime_type, gpointer gmc, gpointer data)
{
context_destroy ((GnomeMimeContext *) gmc);
}
static void
write_mime_keys_foreach (gpointer key_name, gpointer value, gpointer data)
{
gchar *buf;
if (current_lang && strcmp (current_lang, "C"))
buf = g_strconcat ("\t[",
current_lang,
"]",
(gchar *) key_name,
"=",
(gchar *) value,
"\n", NULL);
else
buf = g_strconcat ("\t",
(gchar *) key_name,
"=",
(gchar *) value,
"\n", NULL);
fwrite (buf, 1, strlen (buf), (FILE *) data);
g_free (buf);
}
static void
write_mime_foreach (gpointer mime_type, gpointer gmc, gpointer data)
{
gchar *buf;
GnomeMimeContext *context = (GnomeMimeContext *) gmc;
buf = g_strconcat ((gchar *) mime_type, ":\n", NULL);
fwrite (buf, 1, strlen (buf), (FILE *) data);
g_free (buf);
g_hash_table_foreach (context->keys, write_mime_keys_foreach, data);
fwrite ("\n", 1, strlen ("\n"), (FILE *) data);
}
static void
run_error (gchar *message)
{
GtkWidget *error_box;
error_box = gnome_message_box_new (
message,
GNOME_MESSAGE_BOX_ERROR,
GNOME_STOCK_BUTTON_OK,
NULL);
gnome_dialog_run_and_close (GNOME_DIALOG (error_box));
}
static void
write_keys (GHashTable *spec_hash, GHashTable *generic_hash)
{
struct stat s;
gchar *dirname, *filename;
FILE *file;
GtkWidget *error_box;
dirname = g_build_filename (gnome_util_user_home (), ".gnome/mime-info", NULL);
if ((stat (dirname, &s) < 0) || !(S_ISDIR (s.st_mode))){
if (errno == ENOENT) {
if (mkdir (dirname, S_IRWXU) < 0) {
run_error (_("We are unable to create the directory\n"
"~/.gnome/mime-info\n\n"
"We will not be able to save the state."));
return;
}
} else {
run_error (_("We are unable to access the directory\n"
"~/.gnome/mime-info\n\n"
"We will not be able to save the state."));
return;
}
}
filename = g_build_filename (dirname, "user.keys", NULL);
remove (filename);
file = fopen (filename, "w");
if (file == NULL) {
run_error (_("Cannot create the file\n~/.gnome/mime-info/user.keys.\n\n"
"We will not be able to save the state"));
return;
}
g_hash_table_foreach (spec_hash, write_mime_foreach, file);
g_hash_table_foreach (generic_hash, write_mime_foreach, file);
fclose (file);
}
void
write_initial_keys (void)
{
write_keys (initial_generic_types, initial_specific_types);
}
void
write_user_keys (void)
{
write_keys (generic_types, specific_types);
}
static void
print_mime_foreach (gpointer mime_info, gpointer mi, gpointer data)
{
g_print ("mime_info:%s:\n", (char *)mime_info);
g_print ("\t:%s:\n", ((MimeInfo *)mi)->mime_type);
}
void
discard_key_info (void)
{
gchar *filename;
current_lang = getenv ("LANG");
g_hash_table_foreach (generic_types, clean_mime_foreach, NULL);
/* g_hash_table_foreach (specific_types, print_mime_foreach, NULL); */
g_hash_table_foreach (specific_types, clean_mime_foreach, NULL);
g_hash_table_destroy (generic_types);
g_hash_table_destroy (specific_types);
specific_types = g_hash_table_new (g_str_hash, g_str_equal);
generic_types = g_hash_table_new (g_str_hash, g_str_equal);
filename = g_build_filename (gnome_util_user_home (), "/.gnome/mime-info/user.keys", NULL);
load_mime_type_info_from (filename);
reread_list ();
g_free (filename);
}
void
remove_mime_info (gchar *mime_type)
{
g_hash_table_remove (generic_types, mime_type);
g_hash_table_remove (specific_types, mime_type);
}