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

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

#include "edv_types.h"
#include "edv_mime_type.h"
#include "edv_mime_types_list.h"
#include "edv_utils.h"
#include "config.h"


void EDVMimeTypesListMailcapImport(
	const gchar *path,
	edv_mime_type_struct ***list, gint *total,
	const gint insert_index,
	const gboolean update, const gboolean only_newer,
	gint (*progress_cb)(gpointer, const gulong, const gulong),
	gpointer progress_data,
	void (*added_cb)(const gint, edv_mime_type_struct *, gpointer),
	gpointer added_data,
	void (*modified_cb)(const gint, edv_mime_type_struct *, gpointer),
	gpointer modified_data
);
void EDVMimeTypesListMailcapExport(
	const gchar *path,
	edv_mime_type_struct **list, const gint total,
	const gboolean include_read_only,
	gint (*progress_cb)(gpointer, const gulong, const 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 ISEOF(c)	((int)(c) == EOF)


static gchar *G_STRCAT(gchar *s, const gchar *s2)
{
	if(s != NULL) {
	    if(s2 != NULL) {
		gchar *sr = g_strconcat(s, s2, NULL);
		g_free(s);
		s = sr;
	    }
	} else {
	    if(s2 != NULL)
		s = STRDUP(s2);
	    else
		s = STRDUP("");
	}
	return(s);
}

static gchar *GET_CMD_NAME(const gchar *s)
{
	gchar *s2 = STRDUP(s), *s_end;
	if(s2 == NULL)
	    return(NULL);

	for(s_end = s2; *s_end != '\0'; s_end++)
	{
	    if(ISBLANK(*s_end))
	    {
		*s_end = '\0';
		break;
	    }
	}

	return(s2);
}

static gchar *GET_CMD_ARGS(const gchar *s)
{
	const gchar *s2 = (s != NULL) ?
	    strpbrk((const char *)s, " \t") : NULL;
	if(s2 == NULL)
	    return(NULL);

	while(ISBLANK(*s2))
	    s2++;

	return(STRDUP(s2));
}


/*
 *	Imports the MIME Types from the MailCap file.
 *
 *	The path specifies the MailCap file.
 *
 *	The list and total specifies the pointers to the 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 update is TRUE then if a MIME Type from the file already
 *	exists in the MIME Types list then it will be updated instead
 *	of a new MIME Type being added.
 *
 *	If only_newer is TRUE and update is TRUE then if a MIME Type
 *	from the file already exists in the MIME Types list then it
 *	will only be updated if the one from the file has a newer
 *	modified time.
 *
 *	If progress_cb is not NULL then it will be called during the
 *	operation to report the progress. If it returns non-zero then
 *	the operation will be aborted.               
 *
 *	The progress_data specifies the user data which will be passed
 *	to the progress_cb function.
 *
 *	If added_cb is not NULL then it will be called after each new
 *	MIME Type is added to the list.
 *
 *	The added_data specifies the user data which will be passed to
 *	the added_cb function.
 *
 *	If modified_cb is not NULL then it will be called whenever an
 *	existing MIME Type in the MIME Types list is modified.
 *
 *	The modified_data specifies the user data which will be passed
 *	to the modified_cb function.
 */
void EDVMimeTypesListMailcapImport(
	const gchar *path,
	edv_mime_type_struct ***list, gint *total,
	const gint insert_index,
	const gboolean update, const gboolean only_newer,
	gint (*progress_cb)(gpointer, const gulong, const gulong),
	gpointer progress_data,
	void (*added_cb)(const gint, edv_mime_type_struct *, gpointer),
	gpointer added_data,
	void (*modified_cb)(const gint, edv_mime_type_struct *, gpointer),
	gpointer modified_data
)
{
	gboolean aborted = FALSE, in_quotes, mt_exists;
	gint	c,
		cur_insert_index = insert_index,
		mt_num = -1;
	gulong	file_size = 0l,
		file_last_modified = 0l;
	gchar *s, *s_end, buf[10000];
	FILE *fp;
	GList *val_list, *mt_imported_list = NULL;
	edv_mime_type_struct *mt = NULL;
	struct stat stat_buf;

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

	/* Open the Mailcap file for reading */
	fp = fopen((const char *)path, "rb");
	if(fp == NULL)
	    return;

	/* Get the file's statistics */
	if(!fstat(fileno(fp), &stat_buf))
	{
	    file_size = (gulong)stat_buf.st_size;
	    file_last_modified = (gulong)stat_buf.st_mtime;
	}

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

	/* Begin parsing the Mailcap file
	 *
	 * Each line specifies a MIME Type with a set of ';' separated
	 * values, example:
	 *
	 *	image/png; xzgv '%s'; test=test -n "$DISPLAY"
	 *
	 * The first value is always the MIME Type and the second value
	 * is always the command, subsequent values follow the format:
	 *
	 *	<parm>=<value>
	 */
	while(!aborted)
	{
	    /* Get the first non-blank character of the current line */
	    c = (gint)fgetc(fp);
	    while(!ISEOF(c) && ISBLANK(c))
		c = (gint)fgetc(fp);

	    /* End of file? */
	    if(ISEOF(c))
	    {
		break;
	    }
	    /* Empty line? */
	    else if(ISCR(c))
	    {
		continue;
	    }
	    /* Comment? */
	    else if(c == '#')
	    {
		/* Seek to start of next line */
		while(!ISEOF(c) && !ISCR(c))
		    c = (gint)fgetc(fp);
		if(ISEOF(c))
		    break;
		else
		    continue;
	    }

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

	    /* Get the values list for this line */
	    val_list = NULL;
	    in_quotes = FALSE;
	    while(TRUE)
	    {
		/* Get the next value
		 *
		 * Iterate through the buffer until the end of the
		 * buffer or the end of the line is reached
		 */
		for(s = buf, s_end = s + sizeof(buf);
		    s < s_end;
		    s++
		)
		{
		    /* End of file? */
		    if(ISEOF(c))
		    {
			*s = '\0';
			break;
		    }
		    /* Escape? */
		    else if(c == '\\')
		    {
			/* Get the next character that is to be escaped */
			c = (gint)fgetc(fp);

			/* Do not store these escaped characters
			 *
			 * Newlines?
			 */
			if(ISCR(c))
			{
			    s--;
			}
			/* End of file? */
			else if(ISEOF(c))
			{
			    *s = '\0';
			    break;
			}
			/* All else store the escaped character with
			 * the escape character
			 */
			else
			{
			    *s = '\\';

			    s++;
			    if(s < s_end)
				*s = (gchar)c;
			}
		    }
		    /* Quote? */
		    else if(c == '"')
		    {
			in_quotes = !in_quotes;
			*s = (gchar)c;
		    }
		    /* Value deliminator? */
		    else if(c == ';')
		    {
			if(in_quotes)
			{
			    *s = (gchar)c;
			}
			else
			{
			    *s = '\0';
			    break;
			}
		    }
		    /* End of line? */
		    else if(ISCR(c))
		    {
			if(in_quotes)
			{
			    *s = ' ';	/* Store this character as a space */
			}
			else
			{
			    *s = '\0';
			    break;
			}
		    }
		    /* All else store this character */
		    else
		    {
			*s = (gchar)c;
		    }

		    c = (gint)fgetc(fp);
		}

		/* Null terminate the value and strip spaces */
		buf[sizeof(buf) - 1] = '\0';
		strstrip(buf);

		/* Append this value to the values list */
		val_list = g_list_append(
		    val_list, STRDUP(buf)
		);

		/* End of line reached? */
		if(ISCR(c))
		    break;
		else
		    c = (gint)fgetc(fp);
	    }


	    /* Begin creating this new imported MIME Type */

	    /* Reset the contexts */
	    mt_num = -1;
	    mt = NULL;
	    mt_exists = FALSE;

	    /* Check if the MIME Type already exists? */
	    if(update)
	    {
		GList *glist = g_list_nth(val_list, 0);
		const gchar *type = (glist != NULL) ? 
		    (gchar *)glist->data : NULL;
		if(!STRISEMPTY(type))
		{
		    /* Check if this MIME Type is already in the list
		     * so that no duplicate MIME Type will be created
		     */
		    mt = EDVMimeTypesListMatchType(
			*list, *total, &mt_num, type, FALSE
		    );
		    if(mt != NULL)
		    {
			/* MIME Type already exists so just update it */
			mt_exists = TRUE;

			/* Check if the MIME Type was already defined
			 * in this file
			 */
			for(glist = mt_imported_list;
			    glist != NULL;
			    glist = g_list_next(glist)
			)
			{
			    if(EDV_MIME_TYPE(glist->data) == mt)
				break;
			}
			if(glist != NULL)
			{
			    /* Already defined in this file, so set
			     * it NULL so that it is not defined again
			     */
#if 0
			    g_printerr(
 "%s: Warning: Redefination of \"%s\" ignored.\n",
				path, type
			    );
#endif
			    mt = NULL;
			    mt_num = -1;
			}

			/* Check if this MIME Type is newer than the
			 * one from the file and we are only want to
			 * import MIME Types that are newer on file
			 */
			if(only_newer && (mt != NULL))
			{
			    if(mt->modify_time >= file_last_modified)
			    {
				mt = NULL;
				mt_num = -1;
			    }
			}
		    }
		}
	    }

	    /* Insert or append? */
	    if((mt == NULL) && !mt_exists)
	    {
		/* Insert */
		if((cur_insert_index >= 0) && (cur_insert_index < *total))
		{
		    /* Allocate more pointers */
		    gint i = MAX(*total, 0);
		    *total = i + 1;
		    *list = (edv_mime_type_struct **)g_realloc(
		        *list,
		        (*total) * sizeof(edv_mime_type_struct *)
		    );
		    if(*list == NULL)
		    {
		        *total = 0;
		        break;
		    }

		    mt_num = cur_insert_index;

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

		    /* Create the new MIME Type */
		    (*list)[mt_num] = mt = EDVMimeTypeNew(
		        EDV_MIME_TYPE_CLASS_FORMAT,
		        NULL,		/* Value */
		        NULL,		/* Type */
		        NULL		/* Description */
		    );

		    cur_insert_index++;	/* Account for the shift in position */
	        }
		else
		{
		    /* Append */

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

		    /* Create the new MIME Type */
		    (*list)[mt_num] = mt = EDVMimeTypeNew(
		        EDV_MIME_TYPE_CLASS_FORMAT,
		        NULL,		/* Value */
		        NULL,		/* Type */
		        NULL		/* Description */
		    );

		    cur_insert_index = -1;
	        }
	    }

	    /* MIME Type valid for value setting? */
	    if((mt != NULL) && (mt_num >= 0) && (mt_num < *total))
	    {
		gint i;
		GList *glist;
		edv_mime_type_struct *m = mt;

/* Seeks s past the '=' and any blank characters */
#define SEEK_PAST_VALUE_ARG_DELIM	{	\
 for(; *s != '\0'; s++) {			\
  if(*s == '=') {				\
   s++;						\
   while(ISBLANK(*s))				\
    s++;					\
   break;					\
  }						\
 }						\
}
#define ADD_COMMAND(_m_,_n_,_c_)	{	\
 /* Look for a command with an existing name */	\
 edv_mime_type_command_struct *cmd =		\
  EDVMimeTypeGetCommandByName((_m_), (_n_));	\
 if(cmd != NULL) {				\
  g_free(cmd->command);				\
  cmd->command = STRDUP(_c_);			\
 } else {					\
  cmd = EDVMimeTypeCommandNew();		\
  if(cmd != NULL) {				\
   cmd->name = STRDUP(_n_);			\
   cmd->command = STRDUP(_c_);			\
   (_m_)->commands_list = g_list_append(	\
    (_m_)->commands_list, cmd			\
   );						\
  }						\
 }						\
}

		/* Record this MIME Type as being imported from this
		 * file
		 */
		mt_imported_list = g_list_append(
		    mt_imported_list, m 
		);

		/* Iterate through the values list and apply values
		 * to the new MIME Type
		 */
		for(glist = val_list, i = 0;
		    glist != NULL;
		    glist = g_list_next(glist), i++
		)
		{
		    s = (gchar *)glist->data;
		    if(STRISEMPTY(s))
			continue;

		    /* First value is always the MIME Type */
		    if(i == 0)
		    {
			g_free(m->type);
			m->type = STRDUP(s);
		    }
		    /* Second value is always the command */
		    else if(i == 1)
		    {
			gchar *prog, *args;

			substr((char *)s, "'%s'", "\"%s\"");
			prog = GET_CMD_NAME(s);
			args = GET_CMD_ARGS(s);

			if(prog != NULL)
			{
			    gchar *command;
			    if(*prog != '/')
			    {
				gchar *s = EDVWhich(prog);
				if(s != NULL)
				{
				    g_free(prog);
				    prog = s;
				}
			    }
			    if(args != NULL)
				command = g_strdup_printf("%s %s", prog, args);
			    else
				command = STRDUP(prog);
			    ADD_COMMAND(m, "default", command);
			    g_free(command);
			}

			g_free(prog);
			g_free(args);
		    }

		    /* Name Template? */
		    else if(strcasepfx((const char *)s, "nametemplate"))
		    {
			SEEK_PAST_VALUE_ARG_DELIM

#if 0
/* Ignore, this does not provide information about extensions, but
 * rather information on generating tempory files
 *
 * Mailcap files do not provide any information on extensions
 */
			substr((char *)s, "%s", "");

			g_free(m->value);
			m->value = STRDUP(s);
#endif
		    }
		    /* Description? */
		    else if(strcasepfx((const char *)s, "description"))
		    {
			SEEK_PAST_VALUE_ARG_DELIM

			/* Remove first and last quotes if any */
			substr(s, "\"", "");

			g_free(m->description);
			m->description = STRDUP(s);
		    }
		    /* Installer? */
		    else if(strcasepfx((const char *)s, "notes"))
		    {
			SEEK_PAST_VALUE_ARG_DELIM

			/* Ignore */
		    }
		    /* Test? */
		    else if(strcasepfx((const char *)s, "test"))
		    {
			gchar *prog, *args;

			SEEK_PAST_VALUE_ARG_DELIM

			substr((char *)s, "'%s'", "\"%s\"");
			prog = GET_CMD_NAME(s);
			args = GET_CMD_ARGS(s);

			if(prog != NULL)
			{
			    gchar *command;
			    if(*prog != '/')
			    {
				gchar *s = EDVWhich(prog);
				if(s != NULL)
				{
				    g_free(prog);
				    prog = s;
				}
			    }
			    if(args != NULL)
				command = g_strdup_printf("%s %s", prog, args);
			    else
				command = STRDUP(prog);
			    ADD_COMMAND(m, "test", command);
			    g_free(command);
			}

			g_free(prog);
			g_free(args);
		    }
		    /* View? */
		    else if(strcasepfx((const char *)s, "view"))
		    {
			gchar *prog, *args;

			SEEK_PAST_VALUE_ARG_DELIM

			substr((char *)s, "'%s'", "\"%s\"");
			prog = GET_CMD_NAME(s);
			args = GET_CMD_ARGS(s);

			if(prog != NULL)
			{
			    gchar *command;
			    if(*prog != '/')
			    {
				gchar *s = EDVWhich(prog);
				if(s != NULL)
				{
				    g_free(prog);
				    prog = s;
				}
			    }
			    if(args != NULL)
				command = g_strdup_printf("%s %s", prog, args);
			    else
				command = STRDUP(prog);
			    ADD_COMMAND(m, "view", command);
			    g_free(command);
			}

			g_free(prog);
			g_free(args);
		    }
		    /* Edit? */
		    else if(strcasepfx((const char *)s, "edit"))
		    {
			gchar *prog, *args;

			SEEK_PAST_VALUE_ARG_DELIM

			substr((char *)s, "'%s'", "\"%s\"");
			prog = GET_CMD_NAME(s);
			args = GET_CMD_ARGS(s);

			if(prog != NULL)
			{
			    gchar *command;
			    if(*prog != '/')
			    {
				gchar *s = EDVWhich(prog);
				if(s != NULL)
				{
				    g_free(prog);
				    prog = s;
				}
			    }
			    if(args != NULL)
				command = g_strdup_printf("%s %s", prog, args);
			    else
				command = STRDUP(prog);
			    ADD_COMMAND(m, "edit", command);
			    g_free(command);
			}

			g_free(prog);
			g_free(args);
		    }
		    /* Print? */
		    else if(strcasepfx((const char *)s, "print"))
		    {
			gchar *prog, *args;

			SEEK_PAST_VALUE_ARG_DELIM

			substr((char *)s, "'%s'", "\"%s\"");
			prog = GET_CMD_NAME(s);
			args = GET_CMD_ARGS(s);

			if(prog != NULL)
			{
			    gchar *command;
			    if(*prog != '/')
			    {
				gchar *s = EDVWhich(prog);
				if(s != NULL)
				{
				    g_free(prog);
				    prog = s;
				}
			    }
			    if(args != NULL)
				command = g_strdup_printf("%s %s", prog, args);
			    else
				command = STRDUP(prog);
			    ADD_COMMAND(m, "print", command);
			    g_free(command);
			}

			g_free(prog);
			g_free(args);
		    }
		    /* Compose? */
		    else if(strcasepfx((const char *)s, "compose"))
		    {
			gchar *prog, *args;

			SEEK_PAST_VALUE_ARG_DELIM

			substr((char *)s, "'%s'", "\"%s\"");
			prog = GET_CMD_NAME(s);
			args = GET_CMD_ARGS(s);

			if(prog != NULL)
			{                            
			    gchar *command;
			    if(*prog != '/')
			    {
				gchar *s = EDVWhich(prog);
				if(s != NULL)
				{
				    g_free(prog);
				    prog = s;
				}
			    }    
			    if(args != NULL)
				command = g_strdup_printf("%s %s", prog, args);
			    else
				command = STRDUP(prog);
			    ADD_COMMAND(m, "compose", command);
			    g_free(command);
			}

			g_free(prog);
			g_free(args);
		    }
		    /* Compose Typed? */
		    else if(strcasepfx((const char *)s, "composetyped"))
		    {
			gchar *prog, *args;

			SEEK_PAST_VALUE_ARG_DELIM

			substr((char *)s, "'%s'", "\"%s\"");
			prog = GET_CMD_NAME(s);
			args = GET_CMD_ARGS(s);

			if(prog != NULL)
			{
			    gchar *command;
			    if(*prog != '/')
			    {
				gchar *s = EDVWhich(prog);
				if(s != NULL)
				{
				    g_free(prog);
				    prog = s;
				}
			    }
			    if(args != NULL)
				command = g_strdup_printf("%s %s", prog, args);
			    else
				command = STRDUP(prog);
			    ADD_COMMAND(m, "composetyped", command);
			    g_free(command);
			}

			g_free(prog);
			g_free(args);
		    }

		    /* Needs Terminal? */
		    else if(strcasepfx((const char *)s, "needsterminal"))
		    {
/* TODO perhaps append the terminal run command to all commands */
		    }
		    /* Copious Output? */
		    else if(strcasepfx((const char *)s, "copiousoutput"))
		    {
			/* Ignore */
		    }
		    /* Textual Newlines? */
		    else if(strcasepfx((const char *)s, "textualnewlines"))
		    {
			/* Ignore */
		    }
		    else
		    {
			g_printerr(
"%s: MIME Type %s: Unsupported value \"%s\".\n",
			    path, m->type,
			    s
			);
		    }


		}

		/* Update the time stamps */
		if(file_last_modified > m->modify_time)
		    m->modify_time = file_last_modified;
		if(file_last_modified > m->change_time)
		    m->change_time = file_last_modified;

		/* Report this MIME Type as added or modified */
		if(mt_exists)
		{
		    if(modified_cb != NULL)
			modified_cb(mt_num, m, modified_data);
		}
		else
		{
		    if(added_cb != NULL)
			added_cb(mt_num, m, added_data);
		}

#undef ADD_COMMAND
#undef SEEK_PAST_VALUE_ARG_DELIM
	    }

	    /* Delete values list */
	    g_list_foreach(val_list, (GFunc)g_free, NULL);
	    g_list_free(val_list);

	    /* End of file? */
	    if(ISEOF(c))
		break;
	}

	/* Close the MailCap file */
	fclose(fp);

	/* Delete imported MIME Types list */
	g_list_free(mt_imported_list);

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

/*
 *	Exports the MIME Types to the MailCap file.
 *
 *	The path specifies the MailCap file.
 *
 *	The list and total specifies the MIME Types list.
 *
 *	If include_read_only is TRUE then MIME Types that are marked
 *	as read only will also be exported.
 *
 *	If progress_cb is not NULL then it will be called during the
 *	operation to report the progress. If it returns non-zero then
 *	the operation will be aborted.               
 *
 *	The progress_data specifies the user data which will be passed
 *	to the progress_cb function.
 */
void EDVMimeTypesListMailcapExport(
	const gchar *path,
	edv_mime_type_struct **list, const gint total,
	const gboolean include_read_only,
	gint (*progress_cb)(gpointer, const gulong, const gulong),
	gpointer progress_data
)
{
	gboolean aborted = FALSE;
	gint mt_num;
	edv_mime_type_struct *mt;
	gchar *line, *parent;
	FILE *fp;

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

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

	/* Open the Mailcap file for writing */
	fp = fopen((const char *)path, "wb");
	if(fp == NULL)
	    return;

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


	/* Write header */
	fprintf(
	    fp,
"# Mailcap\n\
#\n\
# Generated by %s Version %s\n\
#\n\
\n",
	    PROG_NAME_FULL, PROG_VERSION
	);


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

	    mt = list[mt_num];
	    if(mt == 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->read_only)
		continue;

	    /* MIME Type string must be defined */
	    if(STRISEMPTY(mt->type))
		continue;

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


	    /* Begin formatting this line */
	    line = NULL;

	    /* First value is the type */
	    line = G_STRCAT(line, mt->type);

	    if(mt->commands_list != NULL)
	    {
		gchar *s, *s2;
		GList *glist = mt->commands_list;
		edv_mime_type_command_struct *cmd;

		/* Set the default command */
		cmd = EDVMimeTypeGetCommandByName(mt, "Default");
		if(cmd == NULL)
		    cmd = EDV_MIME_TYPE_COMMAND(glist->data);
		if((cmd != NULL) ? (cmd->command != NULL) : FALSE)
		{
		    s = STRDUP(cmd->command);
		    substr(s, "\"%s\"", "'%s'");
		    line = G_STRCAT(line, ";");
		    line = G_STRCAT(line, s);
		    g_free(s);
		}

		/* Write the subsequent commands */
		for(glist = mt->commands_list;
		    glist != NULL;
		    glist = g_list_next(glist)
		)
		{
		    cmd = EDV_MIME_TYPE_COMMAND(glist->data);
		    if(cmd == NULL)
			continue;

		    if(STRISEMPTY(cmd->name) || STRISEMPTY(cmd->command))
			continue;

		    if(!g_strcasecmp(cmd->name, "Default"))
			continue;

		    s2 = STRDUP(cmd->command);
		    substr(s2, "\"%s\"", "'%s'");
		    s = g_strdup_printf("%s=%s", cmd->name, s2);
		    line = G_STRCAT(line, ";");
		    line = G_STRCAT(line, s);
		    g_free(s);
		    g_free(s2);
		}

		/* Extensions */
		if(!STRISEMPTY(mt->value))
		{
		    s = g_strdup_printf("nametemplate=%%s%s", mt->value);
		    for(s2 = s; *s2 != '\0'; s2++)
		    {
			if(ISBLANK(*s2))
			{
			    *s2 = '\0';
			    break;
			}
		    }
		    line = G_STRCAT(line, ";");
		    line = G_STRCAT(line, s);
		    g_free(s);
		}

		/* Description */
		if(!STRISEMPTY(mt->description))
		{
		    s = g_strdup_printf("description=\"%s\"", mt->description);
		    line = G_STRCAT(line, ";");
		    line = G_STRCAT(line, s);
		    g_free(s);
		}
	    }

	    /* Write this line */
	    fprintf(fp, "%s\n", (const char *)line);

	    g_free(line);
	}

	/* Close the MailCap file */
	fclose(fp);

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