#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <gtk/gtk.h>

#include "../include/string.h"
#include "../include/fio.h"
#include "../include/disk.h"

#include "edvtypes.h"
#include "edvmimetypes.h"
#include "edvmimetypesfio.h"
#include "config.h"


void EDVDMimeTypesListLoadFromSystem(
	edv_mimetype_struct ***list, gint *total,
	gint insert_index,
	gint (*progress_cb)(gpointer, gulong, gulong),
	gpointer progress_data,
	void (*added_cb)(gpointer, gint, edv_mimetype_struct *),
	gpointer added_data,
	gboolean mark_all_loaded_read_only
);

void EDVMimeTypeListLoadFromFile(
	const gchar *filename,
	edv_mimetype_struct ***list, gint *total,
	gint insert_index,
	gboolean update, gboolean only_newer,
	gint (*progress_cb)(gpointer, gulong, gulong),
	gpointer progress_data,
	void (*added_cb)(gpointer, gint, edv_mimetype_struct *),
	gpointer added_data,
	gboolean mark_all_loaded_read_only
);
void EDVMimeTypeListSaveToFile(
	const gchar *filename,
	edv_mimetype_struct **list, gint total,
	gboolean include_read_only,
	gint (*progress_cb)(gpointer, gulong, gulong),
	gpointer progress_data
);


#define ATOI(s)         (((s) != NULL) ? atoi(s) : 0)
#define ATOL(s)         (((s) != NULL) ? atol(s) : 0)
#define ATOF(s)         (((s) != NULL) ? atof(s) : 0.0f)
#define STRDUP(s)       (((s) != NULL) ? g_strdup(s) : NULL)

#define MAX(a,b)        (((a) > (b)) ? (a) : (b))
#define MIN(a,b)        (((a) < (b)) ? (a) : (b))
#define CLIP(a,l,h)     (MIN(MAX((a),(l)),(h)))
#define STRLEN(s)       (((s) != NULL) ? strlen(s) : 0)
#define STRISEMPTY(s)   (((s) != NULL) ? (*(s) == '\0') : TRUE)

#define ISCR(c)		(((c) == '\n') || ((c) == '\r'))
#define BLANK(c)	(((c) == ' ') || ((c) == '\t'))


/*
 *	Opens the MIME Types specified by the system.
 *
 *	The insert_index specifies the position at which to insert the
 *	MIME Types read from file to the list, or it can be -1 to
 *	append to the list.
 *
 *	If mark_all_loaded_read_only is TRUE then all MIME Types read
 *	from this file will be marked as read only.
 */
void EDVDMimeTypesListLoadFromSystem(
	edv_mimetype_struct ***list, gint *total,
	gint insert_index,
	gint (*progress_cb)(gpointer, gulong, gulong),
	gpointer progress_data,
	void (*added_cb)(gpointer, gint, edv_mimetype_struct *),
	gpointer added_data,
	gboolean mark_all_loaded_read_only
)
{
	const gchar     *icon_file_s[EDV_MIMETYPE_TOTAL_ICON_STATES],
			*icon_file_m[EDV_MIMETYPE_TOTAL_ICON_STATES],
			*icon_file_l[EDV_MIMETYPE_TOTAL_ICON_STATES];
	edv_mimetype_struct *m;

	memset(icon_file_s, 0x00, EDV_MIMETYPE_TOTAL_ICON_STATES * sizeof(const gchar *));
	memset(icon_file_m, 0x00, EDV_MIMETYPE_TOTAL_ICON_STATES * sizeof(const gchar *));
	memset(icon_file_l, 0x00, EDV_MIMETYPE_TOTAL_ICON_STATES * sizeof(const gchar *));

#define EDV_ICONS_DIR   PREFIX "/share/endeavour2/icons"

#define APPEND_MIMETYPE(			\
 _val_,_type_,_desc_,				\
 _icon_file_s_,					\
 _icon_file_m_,					\
 _icon_file_l_					\
) {						\
 gint mt_num;					\
						\
 /* Insert? */					\
 if((insert_index >= 0) &&			\
    (insert_index < *total)			\
 ) {						\
  gint i = MAX(*total, 0);			\
  *total = i + 1;				\
  *list = (edv_mimetype_struct **)g_realloc(	\
   *list,					\
   (*total) * sizeof(edv_mimetype_struct *)	\
  );						\
  if(*list == NULL) {				\
   *total = 0;					\
   return;					\
  }						\
  mt_num = insert_index;			\
  for(i = (*total) - 1; i > mt_num; i--)	\
   (*list)[i] = (*list)[i - 1];			\
						\
  (*list)[mt_num] = m = EDVMimeTypeNew(		\
   EDV_MIMETYPE_CLASS_SYSTEM, (_val_),		\
   (_type_), (_desc_)				\
  );						\
  insert_index++;				\
 } else {					\
  mt_num = MAX(*total, 0);			\
  *total = mt_num + 1;				\
  *list = (edv_mimetype_struct **)g_realloc(	\
   *list,					\
   (*total) * sizeof(edv_mimetype_struct *)	\
  );						\
  if(*list == NULL) {				\
   *total = 0;					\
   return;					\
  }						\
  (*list)[mt_num] = m = EDVMimeTypeNew(		\
   EDV_MIMETYPE_CLASS_SYSTEM, (_val_),		\
   (_type_), (_desc_)				\
  );						\
  insert_index = -1;				\
 }						\
						\
 if(m != NULL) {				\
  gint n;					\
						\
  m->read_only = mark_all_loaded_read_only;	\
  m->realized = FALSE;				\
						\
  for(n = 0; n < EDV_MIMETYPE_TOTAL_ICON_STATES; n++) { \
   m->small_icon_file[n] = STRDUP(PrefixPaths(	\
    EDV_ICONS_DIR, (_icon_file_s_)[n]		\
   ));						\
   m->medium_icon_file[n] = STRDUP(PrefixPaths(	\
    EDV_ICONS_DIR, (_icon_file_m_)[n]		\
   ));						\
   m->large_icon_file[n] = STRDUP(PrefixPaths(	\
    EDV_ICONS_DIR, (_icon_file_l_)[n]		\
   ));						\
						\
   if(added_cb != NULL)				\
    added_cb(added_data, mt_num, m);		\
  }						\
 }						\
}

	icon_file_s[EDV_MIMETYPE_ICON_STATE_STANDARD] =
	    "icon_file_20x20.xpm";
	icon_file_s[EDV_MIMETYPE_ICON_STATE_SELECTED] = NULL;
	icon_file_s[EDV_MIMETYPE_ICON_STATE_EXTENDED] = NULL;
	icon_file_s[EDV_MIMETYPE_ICON_STATE_HIDDEN] =
	    "icon_file_hidden_20x20.xpm";
	icon_file_m[EDV_MIMETYPE_ICON_STATE_STANDARD] =      
	    "icon_file_32x32.xpm";
	icon_file_m[EDV_MIMETYPE_ICON_STATE_SELECTED] = NULL;
	icon_file_m[EDV_MIMETYPE_ICON_STATE_EXTENDED] = NULL;
	icon_file_m[EDV_MIMETYPE_ICON_STATE_HIDDEN] =
	    "icon_file_hidden_32x32.xpm";
	icon_file_l[EDV_MIMETYPE_ICON_STATE_STANDARD] =      
	    "icon_file_48x48.xpm";
	icon_file_l[EDV_MIMETYPE_ICON_STATE_SELECTED] = NULL;
	icon_file_l[EDV_MIMETYPE_ICON_STATE_EXTENDED] = NULL;
	icon_file_l[EDV_MIMETYPE_ICON_STATE_HIDDEN] =
	    "icon_file_hidden_48x48.xpm";
	APPEND_MIMETYPE(
	    NULL, EDV_MIMETYPE_STR_FILE, NULL,   
	    icon_file_s, icon_file_m, icon_file_l
	);

	icon_file_s[EDV_MIMETYPE_ICON_STATE_STANDARD] =
	    "icon_executable_20x20.xpm";
	icon_file_s[EDV_MIMETYPE_ICON_STATE_SELECTED] = NULL;
	icon_file_s[EDV_MIMETYPE_ICON_STATE_EXTENDED] = NULL;
	icon_file_s[EDV_MIMETYPE_ICON_STATE_HIDDEN] = NULL;
	icon_file_m[EDV_MIMETYPE_ICON_STATE_STANDARD] =
	    "icon_executable_32x32.xpm";
	icon_file_m[EDV_MIMETYPE_ICON_STATE_SELECTED] = NULL;
	icon_file_m[EDV_MIMETYPE_ICON_STATE_EXTENDED] = NULL;
	icon_file_m[EDV_MIMETYPE_ICON_STATE_HIDDEN] = NULL;
	icon_file_l[EDV_MIMETYPE_ICON_STATE_STANDARD] =
	    "icon_executable_48x48.xpm";
	icon_file_l[EDV_MIMETYPE_ICON_STATE_SELECTED] = NULL;
	icon_file_l[EDV_MIMETYPE_ICON_STATE_EXTENDED] = NULL;
	icon_file_l[EDV_MIMETYPE_ICON_STATE_HIDDEN] = NULL;
	APPEND_MIMETYPE(
	    NULL, EDV_MIMETYPE_STR_FILE_EXECUTABLE, NULL,
	    icon_file_s, icon_file_m, icon_file_l
	);

	icon_file_s[EDV_MIMETYPE_ICON_STATE_STANDARD] =
	    "icon_folder_closed_20x20.xpm";
	icon_file_s[EDV_MIMETYPE_ICON_STATE_SELECTED] =
	    "icon_folder_opened_20x20.xpm";
	icon_file_s[EDV_MIMETYPE_ICON_STATE_EXTENDED] = NULL;
	icon_file_s[EDV_MIMETYPE_ICON_STATE_HIDDEN] =
	    "icon_folder_hidden_20x20.xpm";
	icon_file_m[EDV_MIMETYPE_ICON_STATE_STANDARD] =
	    "icon_folder_closed_32x32.xpm";
	icon_file_m[EDV_MIMETYPE_ICON_STATE_SELECTED] =      
	    "icon_folder_opened_32x32.xpm";
	icon_file_m[EDV_MIMETYPE_ICON_STATE_EXTENDED] = NULL;
	icon_file_m[EDV_MIMETYPE_ICON_STATE_HIDDEN] =
	    "icon_folder_hidden_32x32.xpm";
	icon_file_l[EDV_MIMETYPE_ICON_STATE_STANDARD] =
	    "icon_folder_closed_48x48.xpm";
	icon_file_l[EDV_MIMETYPE_ICON_STATE_SELECTED] =
	    "icon_folder_opened_48x48.xpm";
	icon_file_l[EDV_MIMETYPE_ICON_STATE_EXTENDED] = NULL;
	icon_file_l[EDV_MIMETYPE_ICON_STATE_HIDDEN] =
	    "icon_folder_hidden_48x48.xpm";
	APPEND_MIMETYPE(
	    NULL, EDV_MIMETYPE_STR_DIRECTORY, NULL,   
	    icon_file_s, icon_file_m, icon_file_l
	);

	icon_file_s[EDV_MIMETYPE_ICON_STATE_STANDARD] =
	    "icon_link2_20x20.xpm";
	icon_file_s[EDV_MIMETYPE_ICON_STATE_SELECTED] = NULL;
	icon_file_s[EDV_MIMETYPE_ICON_STATE_EXTENDED] = 
	    "icon_link_broken_20x20.xpm";
	icon_file_s[EDV_MIMETYPE_ICON_STATE_HIDDEN] = NULL;
	icon_file_m[EDV_MIMETYPE_ICON_STATE_STANDARD] =
	    "icon_link2_32x32.xpm";
	icon_file_m[EDV_MIMETYPE_ICON_STATE_SELECTED] = NULL;
	icon_file_m[EDV_MIMETYPE_ICON_STATE_EXTENDED] =
	    "icon_link_broken_32x32.xpm";
	icon_file_m[EDV_MIMETYPE_ICON_STATE_HIDDEN] = NULL;
	icon_file_l[EDV_MIMETYPE_ICON_STATE_STANDARD] =
	    "icon_link2_48x48.xpm";
	icon_file_l[EDV_MIMETYPE_ICON_STATE_SELECTED] = NULL;
	icon_file_l[EDV_MIMETYPE_ICON_STATE_EXTENDED] =  
	    "icon_link_broken_48x48.xpm";
	icon_file_l[EDV_MIMETYPE_ICON_STATE_HIDDEN] = NULL;
	APPEND_MIMETYPE(
	    NULL, EDV_MIMETYPE_STR_LINK, NULL,
	    icon_file_s, icon_file_m, icon_file_l
	);

	icon_file_s[EDV_MIMETYPE_ICON_STATE_STANDARD] =
	    "icon_device_block_20x20.xpm";
	icon_file_s[EDV_MIMETYPE_ICON_STATE_SELECTED] = NULL;
	icon_file_s[EDV_MIMETYPE_ICON_STATE_EXTENDED] = NULL;
	icon_file_s[EDV_MIMETYPE_ICON_STATE_HIDDEN] = NULL;
	icon_file_m[EDV_MIMETYPE_ICON_STATE_STANDARD] =      
	    "icon_device_block_32x32.xpm";
	icon_file_m[EDV_MIMETYPE_ICON_STATE_SELECTED] = NULL;
	icon_file_m[EDV_MIMETYPE_ICON_STATE_EXTENDED] = NULL;
	icon_file_m[EDV_MIMETYPE_ICON_STATE_HIDDEN] = NULL;
	icon_file_l[EDV_MIMETYPE_ICON_STATE_STANDARD] =
	    "icon_device_block_48x48.xpm";
	icon_file_l[EDV_MIMETYPE_ICON_STATE_SELECTED] = NULL;
	icon_file_l[EDV_MIMETYPE_ICON_STATE_EXTENDED] = NULL;
	icon_file_l[EDV_MIMETYPE_ICON_STATE_HIDDEN] = NULL;
	APPEND_MIMETYPE(
	    NULL, EDV_MIMETYPE_STR_DEVICE_BLOCK, NULL,   
	    icon_file_s, icon_file_m, icon_file_l
	);

	icon_file_s[EDV_MIMETYPE_ICON_STATE_STANDARD] =
	    "icon_device_character_20x20.xpm";
	icon_file_s[EDV_MIMETYPE_ICON_STATE_SELECTED] = NULL;
	icon_file_s[EDV_MIMETYPE_ICON_STATE_EXTENDED] = NULL;
	icon_file_s[EDV_MIMETYPE_ICON_STATE_HIDDEN] = NULL;
	icon_file_m[EDV_MIMETYPE_ICON_STATE_STANDARD] =
	    "icon_device_character_32x32.xpm";
	icon_file_m[EDV_MIMETYPE_ICON_STATE_SELECTED] = NULL;
	icon_file_m[EDV_MIMETYPE_ICON_STATE_EXTENDED] = NULL;
	icon_file_m[EDV_MIMETYPE_ICON_STATE_HIDDEN] = NULL;
	icon_file_l[EDV_MIMETYPE_ICON_STATE_STANDARD] =
	    "icon_device_character_48x48.xpm";
	icon_file_l[EDV_MIMETYPE_ICON_STATE_SELECTED] = NULL;
	icon_file_l[EDV_MIMETYPE_ICON_STATE_EXTENDED] = NULL;
	icon_file_l[EDV_MIMETYPE_ICON_STATE_HIDDEN] = NULL;
	APPEND_MIMETYPE(
	    NULL, EDV_MIMETYPE_STR_DEVICE_CHARACTER, NULL,
	    icon_file_s, icon_file_m, icon_file_l
	);

	icon_file_s[EDV_MIMETYPE_ICON_STATE_STANDARD] =
	    "icon_pipe_20x20.xpm";
	icon_file_s[EDV_MIMETYPE_ICON_STATE_SELECTED] = NULL;
	icon_file_s[EDV_MIMETYPE_ICON_STATE_EXTENDED] = NULL;
	icon_file_s[EDV_MIMETYPE_ICON_STATE_HIDDEN] = NULL;
	icon_file_m[EDV_MIMETYPE_ICON_STATE_STANDARD] =
	    "icon_pipe_32x32.xpm";
	icon_file_m[EDV_MIMETYPE_ICON_STATE_SELECTED] = NULL;
	icon_file_m[EDV_MIMETYPE_ICON_STATE_EXTENDED] = NULL;
	icon_file_m[EDV_MIMETYPE_ICON_STATE_HIDDEN] = NULL;
	icon_file_l[EDV_MIMETYPE_ICON_STATE_STANDARD] =
	    "icon_pipe_48x48.xpm";
	icon_file_l[EDV_MIMETYPE_ICON_STATE_SELECTED] = NULL;
	icon_file_l[EDV_MIMETYPE_ICON_STATE_EXTENDED] = NULL;
	icon_file_l[EDV_MIMETYPE_ICON_STATE_HIDDEN] = NULL;
	APPEND_MIMETYPE(
	    NULL, EDV_MIMETYPE_STR_FIFO, NULL,
	    icon_file_s, icon_file_m, icon_file_l
	);

	icon_file_s[EDV_MIMETYPE_ICON_STATE_STANDARD] =
	    "icon_socket_20x20.xpm";
	icon_file_s[EDV_MIMETYPE_ICON_STATE_SELECTED] = NULL;
	icon_file_s[EDV_MIMETYPE_ICON_STATE_EXTENDED] = NULL;
	icon_file_s[EDV_MIMETYPE_ICON_STATE_HIDDEN] = NULL;
	icon_file_m[EDV_MIMETYPE_ICON_STATE_STANDARD] =
	    "icon_socket_32x32.xpm";
	icon_file_m[EDV_MIMETYPE_ICON_STATE_SELECTED] = NULL;
	icon_file_m[EDV_MIMETYPE_ICON_STATE_EXTENDED] = NULL;
	icon_file_m[EDV_MIMETYPE_ICON_STATE_HIDDEN] = NULL;
	icon_file_l[EDV_MIMETYPE_ICON_STATE_STANDARD] =
	    "icon_socket_48x48.xpm";
	icon_file_l[EDV_MIMETYPE_ICON_STATE_SELECTED] = NULL;
	icon_file_l[EDV_MIMETYPE_ICON_STATE_EXTENDED] = NULL;
	icon_file_l[EDV_MIMETYPE_ICON_STATE_HIDDEN] = NULL;
	APPEND_MIMETYPE(
	    NULL, EDV_MIMETYPE_STR_SOCKET, NULL,
	    icon_file_s, icon_file_m, icon_file_l
	);

#undef APPEND_MIMETYPE
#undef EDV_ICONS_DIR
}


/*
 *	Opens the MIME Types file and reads all MIME Types from file to
 *	the specified MIME Types list.
 *
 *      The insert_index specifies the position at which to insert the
 *	MIME Types read from file to the list, or it can be -1 to
 *	append to the list.
 *
 *	If mark_all_loaded_read_only is TRUE then all MIME Types read
 *	from this file will be marked as read only.
 */
void EDVMimeTypeListLoadFromFile(
	const gchar *filename,
	edv_mimetype_struct ***list, gint *total,
	gint insert_index,
	gboolean update, gboolean only_newer,
	gint (*progress_cb)(gpointer, gulong, gulong),
	gpointer progress_data,
	void (*added_cb)(gpointer, gint, edv_mimetype_struct *),
	gpointer added_data,
	gboolean mark_all_loaded_read_only
)
{
	gboolean aborted = FALSE;
	gchar *parm;
	FILE *fp;
	gulong	file_size = 0l,
		last_modified = 0l;
	gint mt_num = -1;
	edv_mimetype_struct *mt_ptr = NULL;
	struct stat stat_buf;

	if((list == NULL) || (total == NULL) || STRISEMPTY(filename))
	    return;

	/* Open the MIME Types file for reading */
	fp = FOpen((const char *)filename, "rb");
	if(fp == NULL)
	    return;

	/* Get file stats */
	if(!fstat(fileno(fp), &stat_buf))
	{
	    file_size = (gulong)stat_buf.st_size;
	    last_modified = (gulong)stat_buf.st_mtime;
	}

	/* Report initial progress */
	if(progress_cb != NULL)
	{
	    if(progress_cb(
		progress_data, 0l, file_size
	    ))
		aborted = TRUE;
	}

	/* Begin reading the MIME Types file */
	parm = NULL;
	while(TRUE)
	{
	    if(aborted)
		break;

	    /* Report progress */
	    if(progress_cb != NULL)
	    {
		if(progress_cb(
		    progress_data, (gulong)ftell(fp), file_size
		))
		{
		    aborted = TRUE;
		    break;
		}
	    }

	    /* Read next parameter */
	    parm = (gchar *)FSeekNextParm(
		fp, (char *)parm,
		EDV_CFG_COMMENT_CHAR,
		EDV_CFG_DELIMINATOR_CHAR
	    );
	    if(parm == NULL)
		break;

	    /* Handle by parameter */

	    /* BeginMIMEType */
	    if(!g_strcasecmp(parm, "BeginMIMEType"))
	    {
		gint value[1];
		edv_mimetype_class mt_class;

		/* Get class */
		FGetValuesI(fp, (int *)value, 1);
		mt_class = (edv_mimetype_class)value[0];

		/* Insert or append? */
		if((insert_index >= 0) && (insert_index < *total))
		{
		    /* Insert MIME Type */

		    /* Allocate more pointers */
		    gint i = MAX(*total, 0);
		    *total = i + 1;
		    *list = (edv_mimetype_struct **)g_realloc(
			*list,
			(*total) * sizeof(edv_mimetype_struct *)
		    );
		    if(*list == NULL)
		    {
			g_printerr(
			    "%s: Memory allocation error.\n",
			    filename
			);
			*total = 0;
			break;
		    }

		    /* Get insert index */
		    mt_num = insert_index;

		    /* Shift pointers */
		    for(i = (*total) - 1; i > mt_num; i--)
			(*list)[i] = (*list)[i - 1];

		    /* Create new MIME Type */
		    (*list)[mt_num] = mt_ptr = EDVMimeTypeNew(
			mt_class, NULL, NULL, NULL
		    );

		    insert_index++;
		}
		else
		{
		    /* Append MIME Type */

		    /* Allocate more pointers */
		    mt_num = MAX(*total, 0);
		    *total = mt_num + 1;
		    *list = (edv_mimetype_struct **)g_realloc(
			*list, (*total) * sizeof(edv_mimetype_struct *)
		    );
		    if(*list == NULL)
		    {
			g_printerr(
			    "%s: Memory allocation error.\n",
			    filename
			);
			*total = 0;
			break;
		    }

		    /* Create new MIME Type */
		    (*list)[mt_num] = mt_ptr = EDVMimeTypeNew(
			mt_class, NULL, NULL, NULL
		    );

		    insert_index = -1;
		}

		/* MIME Type created succesfully? */
		if(mt_ptr != NULL)
		{
		    /* Mark as read only? */
		    if(mark_all_loaded_read_only)
			mt_ptr->read_only = TRUE;
		}

	    }

	    /* Type */
	    else if(!g_strcasecmp(parm, "Type"))
	    {
		gchar *type = (gchar *)FGetString(fp);
		if(mt_ptr != NULL)
		{
		    g_free(mt_ptr->type);
		    mt_ptr->type = STRDUP(type);
		}
		g_free(type);
	    }
	    /* Value */
	    else if(!g_strcasecmp(parm, "Value"))
	    {
		gchar *value = (gchar *)FGetString(fp);
		if(mt_ptr != NULL)
		{
		    g_free(mt_ptr->value);
		    mt_ptr->value = STRDUP(value);
		}
		g_free(value);
	    }
	    /* Description */
	    else if(!g_strcasecmp(parm, "Description"))
	    {
		gchar *description = (gchar *)FGetString(fp);
		if(mt_ptr != NULL)
		{
		    g_free(mt_ptr->description);
		    mt_ptr->description = STRDUP(description);
		}
		g_free(description);
	    }

	    /* IconSmallStandard */
	    else if(!g_strcasecmp(parm, "IconSmallStandard"))
	    {
		gchar *path = (gchar *)FGetString(fp);
		if(mt_ptr != NULL)
		{
		    gchar **p = &mt_ptr->small_icon_file[
			EDV_MIMETYPE_ICON_STATE_STANDARD
		    ];
		    g_free(*p);
		    *p = STRDUP(path);
		}
		g_free(path);
	    }
	    /* IconSmallSelected */
	    else if(!g_strcasecmp(parm, "IconSmallSelected"))
	    {
		gchar *path = (gchar *)FGetString(fp);
		if(mt_ptr != NULL)
		{
		    gchar **p = &mt_ptr->small_icon_file[
			EDV_MIMETYPE_ICON_STATE_SELECTED
		    ];
		    g_free(*p);
		    *p = STRDUP(path);
		}
		g_free(path);
	    }
	    /* IconSmallExtended */
	    else if(!g_strcasecmp(parm, "IconSmallExtended"))
	    {
		gchar *path = (gchar *)FGetString(fp);
		if(mt_ptr != NULL)
		{
		    gchar **p = &mt_ptr->small_icon_file[
			EDV_MIMETYPE_ICON_STATE_EXTENDED
		    ];
		    g_free(*p);
		    *p = STRDUP(path);
		}
		g_free(path);
	    }
	    /* IconSmallHidden */
	    else if(!g_strcasecmp(parm, "IconSmallHidden"))
	    {
		gchar *path = (gchar *)FGetString(fp);
		if(mt_ptr != NULL)
		{
		    gchar **p = &mt_ptr->small_icon_file[
			EDV_MIMETYPE_ICON_STATE_HIDDEN
		    ];
		    g_free(*p);
		    *p = STRDUP(path);
		}
		g_free(path);
	    }

	    /* IconMediumStandard */
	    else if(!g_strcasecmp(parm, "IconMediumStandard"))
	    {
		gchar *path = (gchar *)FGetString(fp);
		if(mt_ptr != NULL)
		{
		    gchar **p = &mt_ptr->medium_icon_file[
			EDV_MIMETYPE_ICON_STATE_STANDARD
		    ];
		    g_free(*p);
		    *p = STRDUP(path);
		}
		g_free(path);
	    }
	    /* IconMediumSelected */
	    else if(!g_strcasecmp(parm, "IconMediumSelected"))
	    {
		gchar *path = (gchar *)FGetString(fp);
		if(mt_ptr != NULL)
		{
		    gchar **p = &mt_ptr->medium_icon_file[
			EDV_MIMETYPE_ICON_STATE_SELECTED
		    ];
		    g_free(*p);
		    *p = STRDUP(path);
		}
		g_free(path);
	    }
	    /* IconMediumExtended */
	    else if(!g_strcasecmp(parm, "IconMediumExtended"))
	    {
		gchar *path = (gchar *)FGetString(fp);
		if(mt_ptr != NULL)
		{
		    gchar **p = &mt_ptr->medium_icon_file[
			EDV_MIMETYPE_ICON_STATE_EXTENDED
		    ];
		    g_free(*p);
		    *p = STRDUP(path);
		}
		g_free(path);
	    }
	    /* IconMediumHidden */
	    else if(!g_strcasecmp(parm, "IconMediumHidden"))
	    {
		gchar *path = (gchar *)FGetString(fp);
		if(mt_ptr != NULL)
		{
		    gchar **p = &mt_ptr->medium_icon_file[
			EDV_MIMETYPE_ICON_STATE_HIDDEN
		    ];
		    g_free(*p);
		    *p = STRDUP(path);
		}
		g_free(path);
	    }

	    /* IconLargeStandard */
	    else if(!g_strcasecmp(parm, "IconLargeStandard"))
	    {
		gchar *path = (gchar *)FGetString(fp);
		if(mt_ptr != NULL)
		{
		    gchar **p = &mt_ptr->large_icon_file[
			EDV_MIMETYPE_ICON_STATE_STANDARD
		    ];
		    g_free(*p);
		    *p = STRDUP(path);
		}
		g_free(path);
	    }
	    /* IconLargeSelected */
	    else if(!g_strcasecmp(parm, "IconLargeSelected"))
	    {
		gchar *path = (gchar *)FGetString(fp);
		if(mt_ptr != NULL)
		{
		    gchar **p = &mt_ptr->large_icon_file[
			EDV_MIMETYPE_ICON_STATE_SELECTED
		    ];
		    g_free(*p);
		    *p = STRDUP(path);
		}
		g_free(path);
	    }
	    /* IconLargeExtended */
	    else if(!g_strcasecmp(parm, "IconLargeExtended"))
	    {
		gchar *path = (gchar *)FGetString(fp);
		if(mt_ptr != NULL)
		{
		    gchar **p = &mt_ptr->large_icon_file[
			EDV_MIMETYPE_ICON_STATE_EXTENDED
		    ];
		    g_free(*p);
		    *p = STRDUP(path);
		}
		g_free(path);
	    }
	    /* IconLargeHidden */
	    else if(!g_strcasecmp(parm, "IconLargeHidden"))
	    {
		gchar *path = (gchar *)FGetString(fp);
		if(mt_ptr != NULL)
		{
		    gchar **p = &mt_ptr->large_icon_file[
			EDV_MIMETYPE_ICON_STATE_HIDDEN
		    ];
		    g_free(*p);
		    *p = STRDUP(path);
		}
		g_free(path);
	    }

	    /* Handler */
	    else if(!g_strcasecmp(parm, "Handler"))
	    {
		gchar *s = (gchar *)FGetString(fp);
		if(mt_ptr != NULL)
		{
		    if(!g_strcasecmp(s, "Archiver"))
			mt_ptr->handler = EDV_MIMETYPE_HANDLER_EDV_ARCHIVER;
		    else if(!g_strcasecmp(s, "ImageBrowser"))
			mt_ptr->handler = EDV_MIMETYPE_HANDLER_EDV_IMAGE_BROWSER;
		    else if(!g_strcasecmp(s, "RecycleBin"))
			mt_ptr->handler = EDV_MIMETYPE_HANDLER_EDV_RECYCLE_BIN;
		    else
			mt_ptr->handler = EDV_MIMETYPE_HANDLER_COMMAND;
		}
		g_free(s);
	    }

	    /* Command */
	    else if(!g_strcasecmp(parm, "Command"))
	    {
		gint n;
		gchar *command = (gchar *)FGetString(fp);
		if((mt_ptr != NULL) && !STRISEMPTY(command))
		{
		    /* Allocate more pointers for this command */
		    n = MAX(mt_ptr->total_commands, 0);
		    mt_ptr->total_commands = n + 1;

		    mt_ptr->command = (gchar **)g_realloc(
		        mt_ptr->command,
		        mt_ptr->total_commands * sizeof(gchar *)
		    );
		    mt_ptr->command_name = (gchar **)g_realloc(
		        mt_ptr->command_name,
			mt_ptr->total_commands * sizeof(gchar *)
		    );
		    if((mt_ptr->command == NULL) ||
		       (mt_ptr->command_name == NULL)
		    )
		    {
		        g_free(mt_ptr->command);
		        mt_ptr->command = NULL;
		        g_free(mt_ptr->command_name);
		        mt_ptr->command_name = NULL;
		        mt_ptr->total_commands = 0;
		    }
		    else
		    {
			/* Parse command value */
			gchar *s = (gchar *)strchr(
			    (char *)command,
			    EDV_CFG_DELIMINATOR_CHAR
			);
			if(s != NULL)
			{
			    s++;
			    while(ISBLANK(*s))
				s++;
			}
			else
			{
			    s = command;
			}
			mt_ptr->command[n] = STRDUP(s);
			strstrip((char *)mt_ptr->command[n]);

			/* Parse command name */
			s = (gchar *)strchr(
			    (char *)command,
			    EDV_CFG_DELIMINATOR_CHAR
			);     
			if(s != NULL)
			    *s = '\0';
			mt_ptr->command_name[n] = STRDUP(command);
			strstrip((char *)mt_ptr->command_name[n]);
		    }
		}
		g_free(command);
	    }

	    /* AccessTime */
	    else if(!g_strcasecmp(parm, "AccessTime"))
	    {
		glong value[1];
		FGetValuesL(fp, (long *)value, 1);
		if(mt_ptr != NULL)
		    mt_ptr->access_time = (gulong)value[0];
	    }
	    /* ModifyTime */
	    else if(!g_strcasecmp(parm, "ModifyTime"))
	    {
		glong value[1];
		FGetValuesL(fp, (long *)value, 1);
		if(mt_ptr != NULL)
		    mt_ptr->modify_time = (gulong)value[0];
	    }
	    /* ChangeTime */
	    else if(!g_strcasecmp(parm, "ChangeTime"))
	    {
		glong value[1];
		FGetValuesL(fp, (long *)value, 1);
		if(mt_ptr != NULL)
		    mt_ptr->change_time = (gulong)value[0];
	    }

	    /* EndMIMEType */
	    else if(!g_strcasecmp(parm, "EndMIMEType"))
	    {
		FSeekNextLine(fp);

		/* Report MIME Type added */
		if((added_cb != NULL) && (mt_ptr != NULL))
		    added_cb(added_data, mt_num, mt_ptr);

		/* Reset contexts */
		mt_num = -1;
		mt_ptr = NULL;
	    }
	    /* Unsupported parameter */
	    else
	    {
		/* Ignore unsupported parameter */
		FSeekNextLine(fp);
	    }
	}

	/* Delete parameter */
	g_free(parm);

	/* Close the MIME Types file */
	FClose(fp);

	/* Report final progress */
	if((progress_cb != NULL) && !aborted)
	    progress_cb(
		progress_data, file_size, file_size
	    );
}

/*
 *	Saves the MIME Types list to the specified MIME Types file.
 *
 *	If include_read_only is TRUE then MIME Types that are marked
 *	as read only will also be saved.
 */
void EDVMimeTypeListSaveToFile(
	const gchar *filename,   
	edv_mimetype_struct **list, gint total,
	gboolean include_read_only,
	gint (*progress_cb)(gpointer, gulong, gulong),
	gpointer progress_data
)
{
	gboolean aborted = FALSE;
	const gchar *s, *path;
	gchar *parent;
	FILE *fp;
	gint mt_num;
	edv_mimetype_struct *mt_ptr;

	if((list == NULL) || STRISEMPTY(filename))
	    return;

	/* Get parent directory and create it as needed */
	parent = g_dirname(filename);
	if(parent != NULL)
	{
	    rmkdir((char *)parent, S_IRUSR | S_IWUSR | S_IXUSR);
	    g_free(parent);
	}

	/* Open the MIME Types file for writing */
	fp = FOpen((const char *)filename, "wb");
	if(fp == NULL)
	    return;

	/* Report initial progress */
	if(progress_cb != NULL)
	{
	    if(progress_cb(progress_data, 0l, (gulong)total))
		aborted = TRUE;
	}

	/* Iterate through the MIME Types list */
	for(mt_num = 0; mt_num < total; mt_num++)
	{
	    if(aborted)
		break;

	    mt_ptr = list[mt_num];
	    if(mt_ptr == NULL)
		continue;

	    /* Skip MIME Types that are marked read only, meaning they
	     * should not be saved to file since they are created
	     * internally or are loaded from a global configuration
	     */
	    if(!include_read_only && mt_ptr->read_only)
		continue;

	    /* Report progress */
	    if(progress_cb != NULL)
	    {
		if(progress_cb(
		    progress_data,
		    (gulong)(mt_num + 1), (gulong)total
		))
		{
		    aborted = TRUE;
		    break;
		}
	    }


	    /* Begin saving this MIME Type */

	    /* BeginMIMEType */
	    fprintf(
		fp,
		"BeginMIMEType = %i\n",
		(int)mt_ptr->mt_class
	    );

	    /* Type */
	    if(!STRISEMPTY(mt_ptr->type))
		fprintf(
		    fp,
		    "\tType = %s\n",
		    (const char *)mt_ptr->type
		);

	    /* Value */
	    if(!STRISEMPTY(mt_ptr->value))
		fprintf(
		    fp,
		    "\tValue = %s\n",
		    (const char *)mt_ptr->value
		);

	    /* Description */
	    if(!STRISEMPTY(mt_ptr->description))
		fprintf(
		    fp,
		    "\tDescription = %s\n",
		    (const char *)mt_ptr->description
		);

	    /* IconSmallStandard */
	    path = mt_ptr->small_icon_file[EDV_MIMETYPE_ICON_STATE_STANDARD];
	    if(!STRISEMPTY(path))
		fprintf(
		    fp,
		    "\tIconSmallStandard = %s\n",
		    (const char *)path
		);
	    /* IconSmallSelected */
	    path = mt_ptr->small_icon_file[EDV_MIMETYPE_ICON_STATE_SELECTED];
	    if(!STRISEMPTY(path))             
		fprintf(
		    fp,
		    "\tIconSmallSelected = %s\n",
		    (const char *)path
		);
	    /* IconSmallExtended */
	    path = mt_ptr->small_icon_file[EDV_MIMETYPE_ICON_STATE_EXTENDED];
	    if(!STRISEMPTY(path))
		fprintf(
		    fp,
		    "\tIconSmallExtended = %s\n",
		    (const char *)path
		);
	    /* IconSmallHidden */
	    path = mt_ptr->small_icon_file[EDV_MIMETYPE_ICON_STATE_HIDDEN];
	    if(!STRISEMPTY(path))
		fprintf(
		    fp,
		    "\tIconSmallHidden = %s\n",
		    (const char *)path
		);

	    /* IconMediumStandard */
	    path = mt_ptr->medium_icon_file[EDV_MIMETYPE_ICON_STATE_STANDARD];
	    if(!STRISEMPTY(path))
		fprintf(
		    fp,
		    "\tIconMediumStandard = %s\n",
		    (const char *)path
		);
	    /* IconMediumSelected */
	    path = mt_ptr->medium_icon_file[EDV_MIMETYPE_ICON_STATE_SELECTED];
	    if(!STRISEMPTY(path))
		fprintf(
		    fp,
		    "\tIconMediumSelected = %s\n",
		    (const char *)path
		);
	    /* IconMediumExtended */
	    path = mt_ptr->medium_icon_file[EDV_MIMETYPE_ICON_STATE_EXTENDED];
	    if(!STRISEMPTY(path))
		fprintf(
		    fp,
		    "\tIconMediumExtended = %s\n",
		    (const char *)path
		);
	    /* IconMediumHidden */
	    path = mt_ptr->medium_icon_file[EDV_MIMETYPE_ICON_STATE_HIDDEN];
	    if(!STRISEMPTY(path))
		fprintf(
		    fp,
		    "\tIconMediumHidden = %s\n",
		    (const char *)path
		);

	    /* IconLargeStandard */
	    path = mt_ptr->large_icon_file[EDV_MIMETYPE_ICON_STATE_STANDARD];
	    if(!STRISEMPTY(path))
		fprintf(
		    fp,
		    "\tIconLargeStandard = %s\n",
		    (const char *)path
		);
	    /* IconLargeSelected */
	    path = mt_ptr->large_icon_file[EDV_MIMETYPE_ICON_STATE_SELECTED];
	    if(!STRISEMPTY(path))
		fprintf(
		    fp,
		    "\tIconLargeSelected = %s\n",
		    (const char *)path
		);
	    /* IconLargeExtended */
	    path = mt_ptr->large_icon_file[EDV_MIMETYPE_ICON_STATE_EXTENDED];
	    if(!STRISEMPTY(path))
		fprintf(
		    fp,
		    "\tIconLargeExtended = %s\n",
		    (const char *)path
		);
	    /* IconLargeHidden */
	    path = mt_ptr->large_icon_file[EDV_MIMETYPE_ICON_STATE_HIDDEN];
	    if(!STRISEMPTY(path))
		fprintf(
		    fp,
		    "\tIconLargeHidden = %s\n",
		    (const char *)path
		);

	    /* Handler */
	    switch(mt_ptr->handler)
	    {
	      case EDV_MIMETYPE_HANDLER_EDV_ARCHIVER:
		s = "Archiver";
		break;
	      case EDV_MIMETYPE_HANDLER_EDV_IMAGE_BROWSER:
		s = "ImageBrowser";
		break;
	      case EDV_MIMETYPE_HANDLER_EDV_RECYCLE_BIN:
		s = "RecycleBin";
		break;
	      default:	/* EDV_MIMETYPE_HANDLER_COMMAND */
		s = "Command";
		break;
	    }
	    fprintf(
		fp,
		"\tHandler = %s\n",
		(const char *)s
	    );

	    /* Commands */
	    if(mt_ptr->total_commands > 0)
	    {
		gint n;

		for(n = 0; n < mt_ptr->total_commands; n++)
		{
		    fprintf(
			fp,
			"\tCommand = %s=%s\n",
			(const char *)mt_ptr->command_name[n],
			(const char *)mt_ptr->command[n]
		    );
		}
	    }

	    /* AccessTime */
	    if(mt_ptr->access_time > 0l)
		fprintf(
		    fp,
		    "\tAccessTime = %ld\n",
		    (unsigned long)mt_ptr->access_time
	        );
	    /* ModifyTime */
	    if(mt_ptr->modify_time > 0l)
		fprintf(
		    fp,
		    "\tModifyTime = %ld\n",
		    (unsigned long)mt_ptr->modify_time
		);
	    /* ChangeTime */
	    if(mt_ptr->change_time > 0l)
		fprintf(
		    fp,
		    "\tChangeTime = %ld\n",
		    (unsigned long)mt_ptr->change_time
		);

	    /* EndMIMEType */
	    fprintf(
		fp,
		"EndMIMEType\n"
	    );
	}

	/* Close the MIME Types file */
	FClose(fp);

	/* Report final progress */
	if((progress_cb != NULL) && !aborted)
	    progress_cb(progress_data, (gulong)total, (gulong)total);
}
