#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <gtk/gtk.h>

#include "edvtypes.h"
#include "edvmimetypes.h"
#include "endeavour.h"
#include "edvopen.h"
#include "edvop.h"
#include "edvutils.h"
#include "edvutilsgtk.h"
#include "edvcfglist.h"
#include "config.h"


static gchar *EDVOpenFormatCommandPath(
	const gchar *cmd, const gchar *path
);

gint EDVOpenObjectPath(
	edv_core_struct *core_ptr, const gchar *path,
	const gchar *command_name,
	GtkWidget *toplevel, gboolean verbose,
	gchar **stdout_path_rtn, gchar **stderr_path_rtn
);

gint EDVOpenWithObjectPath(
	edv_core_struct *core_ptr, const gchar *path,
	const gchar *command_name,
	GtkWidget *toplevel, gboolean verbose,
	gchar **stdout_path_rtn, gchar **stderr_path_rtn
);


#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)


/*
 *	Substitutes the "%s" in the specified command with the specified
 *	path. Quotes '"' will be added as needed and '&' will be
 *	appended.
 *
 *	Returns a dynamically allocated string describing the formatted
 *	command, the calling function must delete ther returned string.
 */
static gchar *EDVOpenFormatCommandPath(
	const gchar *cmd, const gchar *path
)
{
	gchar *rs, *st;
	const gchar *ss, *ss2;


	if(cmd == NULL)
	    return(NULL);

	if(path == NULL)
	    path = "";

	/* Allocate return string */
	rs = st = (gchar *)g_malloc(
	    (STRLEN(cmd) + STRLEN(path) + 10) * sizeof(gchar)
	);
	if(rs == NULL)
	    return(NULL);

	/* Look for token in in the specified command string (if any) */
	ss = cmd;
	ss2 = (const gchar *)strstr(
	    (const char *)ss, "%s"
	);
	if(ss2 != NULL)
	{
	    gboolean token_has_quotes = (strstr(ss, "\"%s\"") != NULL) ?
		TRUE : FALSE;

	    /* Copy command to return string */
	    gint len = (gint)(ss2 - ss);
	    if(len > 0)
		memcpy(st, ss, len);
	    ss += len + STRLEN("%s");
	    st += len;

	    /* Copy the path to the return string */
	    if(!token_has_quotes)
		*st++ = '"';
	    len = STRLEN(path);
	    strcpy(st, path);
	    st += len;
	    if(!token_has_quotes)
		*st++ = '"';    

	    /* Copy the rest of the command to the return string */
	    len = STRLEN(ss);
	    strcpy(st, ss);
	    st += len;

	    len = STRLEN(" &");
	    strcpy(st, " &");
	    st += len;
	}
	else
	{
	    /* Copy command to return string */
	    gint len = STRLEN(ss);
	    if(len > 0)
		memcpy(st, ss, len);
	    st += len;

	    /* Append the path to the return string with quotes */
	    len = STRLEN(" \"");
	    strcpy(st, " \"");
	    st += len;

	    len = STRLEN(path);
	    strcpy(st, path);
	    st += len;

	    len = STRLEN("\" &");
	    strcpy(st, "\" &");
	    st += len;
	}

	return(rs);
}


/*
 *	Opens the object specified by path.
 *
 *	The MIME Type appropriate for the object will be looked up and
 *	if it is found then the program or method specified by the MIME
 *	Type to open the object will be used.
 *
 *	The command_name specifies which MIME Type command must be used
 *	to open the object.
 *
 *	If stdout_path_rtn or stderr_path_rtn are not NULL, then paths
 *	to the stdout and stderr files will be returned. The calling
 *	function must delete the returned strings.
 *
 *	Returns:
 *
 *	0	Success
 *	-1	General error
 *	-2	No MIME Type, no application, or ambiguous
 *	-3	System error
 *	-6	Call is a re-enterent to an existing call to this function
 */
gint EDVOpenObjectPath(
	edv_core_struct *core_ptr, const gchar *path,
	const gchar *command_name,
	GtkWidget *toplevel, gboolean verbose,
	gchar **stdout_path_rtn, gchar **stderr_path_rtn
)
{
	gboolean got_match;
	gint i, status;
	const gchar *name, *command_base;
	const cfg_item_struct *cfg_list;
	edv_mimetype_struct *mt_ptr;

	/* Reset returns */
	if(stdout_path_rtn != NULL)
	    *stdout_path_rtn = NULL;
	if(stderr_path_rtn != NULL)
	    *stderr_path_rtn = NULL;

	if((core_ptr == NULL) || STRISEMPTY(path))
	    return(-1);

	cfg_list = core_ptr->cfg_list;

	/* Get the object's name */
	name = EDVGetPathName(path);

	/* Iterate through MIME Types list and look for a MIME Type
	 * appropriate for the specified object
	 */
	mt_ptr = NULL;
	got_match = FALSE;
	for(i = 0; i < core_ptr->total_mimetypes; i++)
	{
	    mt_ptr = core_ptr->mimetype[i];
	    if(mt_ptr == NULL)
		continue;

	    /* Handle by the MIME Type's class */
	    got_match = FALSE;
	    switch(mt_ptr->mt_class)
	    {
	      case EDV_MIMETYPE_CLASS_SYSTEM:
		break;
	      case EDV_MIMETYPE_CLASS_FORMAT:
		if(EDVIsExtension(name, mt_ptr->value))
		{
		    got_match = TRUE;
		}
		break;
	      case EDV_MIMETYPE_CLASS_PROGRAM:
		break;
	      case EDV_MIMETYPE_CLASS_UNIQUE:
		if(mt_ptr->value != NULL)
		{
		    if(!strcmp(path, mt_ptr->value))
			got_match = TRUE;
		}
		break;
	    }
	    /* Stop iterating for other MIME Types if a match was made */
	    if(got_match)
		break;
	}
	/* Unable to match MIME Type? */
	if(!got_match || (mt_ptr == NULL))
	{
	    const char *def_viewer = EDV_GET_S(EDV_CFG_PARM_PROG_DEF_VIEWER);

	    /* Since this object's name does not match any MIME Type,
	     * check if the object itself is executable and if it is
	     * then execute it
	     */
	    if(!access(path, X_OK))
	    {
		gint pid;
		gchar *parent, *pwd, *cmd;

		/* Record previous working dir */
		pwd = EDVGetCWD();

		/* Set new working dir as the parent of the specified
		 * path
		 */
		parent = g_dirname(path);
		EDVCHDir(parent);
		g_free(parent);

		/* Format command to execute the object */
		cmd = g_strdup_printf(
		    "%s &",
		    path
		);

		/* Execute command */
		pid = EDVSystem(cmd);
		if(pid <= 0)
		    status = -1;
		else
		    status = 0;

		g_free(cmd);

		/* Change back to the previous working dir */
		EDVCHDir(pwd);
		g_free(pwd);

		/* Failed to execute the given object? */
		if(status && verbose)
		{
		    gchar *buf = g_strdup_printf(
#if defined(PROG_LANGUAGE_SPANISH)
"Incapaz de ejecutar objeto:\n\
\n\
    %s\n"
#elif defined(PROG_LANGUAGE_FRENCH)
"Incapable pour exécuter l'objet:\n\
\n\
    %s\n"
#elif defined(PROG_LANGUAGE_GERMAN)
"Unfähig, objekt durchzuführen:\n\
\n\
    %s\n"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Incapace per eseguire l'oggetto:\n\
\n\
    %s\n"
#elif defined(PROG_LANGUAGE_DUTCH)
"Onbekwaam voorwerp uit te voeren:\n\
\n\
    %s\n"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Incapaz de executar objeto:\n\
\n\
    %s\n"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Maktesløs utføre objekt:\n\
\n\
    %s\n"
#else
"Unable to execute:\n\
\n\
    %s\n"
#endif
			, path
		    );
		    EDVPlaySoundError(core_ptr);
		    EDVMessageError(
#if defined(PROG_LANGUAGE_SPANISH)
"Abra Fallado",
buf,
"Si usted desea abrir este objeto ejecutable\n\
con un específico de tipo MIME, el Archivo de\n\
La Prueba->Abre Con.",
#elif defined(PROG_LANGUAGE_FRENCH)
"Ouvrir A Echoué",
buf,
"Si vous souhaitez ouvrir cet objet réalisable\n\
avec un Type de MIME spécifique, essayez Le\n\
Fichier->Ouvre Avec.",
#elif defined(PROG_LANGUAGE_GERMAN)
"Offen Versagt",
buf,
"Wenn sie wünschen, dieses ausführbare objekt\n\
mit einem spezifischen MIME Typ zu offnen,\n\
Legt Versuch->Offen Mit Ab.",
#elif defined(PROG_LANGUAGE_ITALIAN)
"Aprire Fallito",
buf,
"Se lei desidera aprire quest'oggetto eseguibile\n\
con un Tipo di MIME specifico, tenta\n\
Il File->Apre Con.",
#elif defined(PROG_LANGUAGE_DUTCH)
"Open Verzuimde",
buf,
"Indien u wenst deze uitvoerbaar voorwerp met een\n\
specifiek MIME Type, poging Dossier->Open Met te\n\
openen.",
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Abra Fracassado",
buf,
"Se deseja abrir este objeto de executable com um\n\
tipo específico de MIMO, tenta Arquivo->Abre Com.",
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Åpen Failed",
buf,
"Om De ønsker åpne dette gjennomførbare objektet med\n\
en spesifikk MIME Type, forsøk File->Open With.",
#else
"Open Failed",
buf,
"If you wish to open this executable object with\n\
a specific MIME Type, try File->Open With.",
#endif
			toplevel
		    );
		    g_free(buf);
		}
		return(status);
	    }
	    /* Use the default viewer to view this object */
	    else if(!STRISEMPTY(def_viewer))
	    {
		gint pid;
		gchar *parent, *pwd, *cmd;

		/* Record previous working dir */
		pwd = EDVGetCWD();

		/* Set new working dir as the parent of the specified
		 * path
		 */
		parent = g_dirname(path);
		EDVCHDir(parent);
		g_free(parent);

		/* Format command to view the object */
		cmd = EDVOpenFormatCommandPath(def_viewer, path);

		/* Execute command */
		pid = EDVSystem(cmd);
		if(pid <= 0)
		    status = -1;
		else
		    status = 0;

		g_free(cmd);

		/* Change back to the previous working dir */
		EDVCHDir(pwd);
		g_free(pwd);

		return(status);
	    }
	    /* All else print warning */
	    else if(verbose)
	    {
		gchar *buf = g_strdup_printf(
#if defined(PROG_LANGUAGE_SPANISH)
"Incapaz de encontrar de tipo MIME asociado con objeto:\n\
\n\
    %s\n"
#elif defined(PROG_LANGUAGE_FRENCH)
"Incapable pour trouver le Type de MIME associé avec l'objet:\n\
\n\
    %s\n"
#elif defined(PROG_LANGUAGE_GERMAN)
"Unfähig, MIME Typ zu finden, mit objekt hat verbunden:\n\
\n\
    %s\n"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Incapace per trovare il Tipo di MIME è frequentato l'oggetto:\n\
\n\
    %s\n"
#elif defined(PROG_LANGUAGE_DUTCH)
"Onbekwaam MIME Type te vinden met voorwerp associeerde:\n\
\n\
    %s\n"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Incapaz de achar Tipo de MIMO associado com objeto:\n\
\n\
    %s\n"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Maktesløs finne MIME Type vÆrer som tilknyttet med objekt:\n\
\n\
    %s\n"
#else
"Unable to find a MIME Type associated with object:\n\
\n\
    %s\n"
#endif
		    , path
		);
		EDVPlaySoundWarning(core_ptr);
		EDVMessageWarning(
#if defined(PROG_LANGUAGE_SPANISH)
"Abra Fallado",
buf,
"Para definir un de tipo MIME para este objeto,\n\
va a Ver->De Tipo MIME...\n\
\n\
Para la ayuda adicional a establecer de tipo MIME,\n\
vea Ayuda->De Tipo MIME...",
#elif defined(PROG_LANGUAGE_FRENCH)
"Ouvrir A Echoué",
buf,
"Pour définir un Type de MIME pour cet objet,\n\
Aller Regarder->Les Types De MIME...\n\
\n\
Pour l'aide supplémentaire sur monte les Types de MIME,\n\
L'Aide D'évêché -> Les Types De MIME...",
#elif defined(PROG_LANGUAGE_GERMAN)
"Offen Versagt",
buf,
"Um einen MIME Typ für dieses objekt zu definieren,\n\
gehen sie, Um->MIME Typen Anzusehen...\n\
\n\
Für zusätzliche Hilfe stellend auf MIME Typen auf,\n\
sieht Hilfe->MIME Typen...",
#elif defined(PROG_LANGUAGE_ITALIAN)
"Aprire Fallito",
buf,
"Per definire un Tipo di MIME per quest'oggetto,\n\
andare Osservare->I Tipi Di MIME...\n\
\n\
Per l'aiuto ulteriore su installa i Tipi di MIME,\n\
vedere L'Aiuto->I Tipi Di MIME...",
#elif defined(PROG_LANGUAGE_DUTCH)
"Open Verzuimde",
buf,
"Om een MIME Type voor deze voorwerp te definiëren,\n\
Ga Om->MIME Typen Te Bekijken...\n\
\n\
Voor bijkomende hulp op opstellen van MIME Typen,\n\
ziet Hulp->MIME Typen...",
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Abra Fracassado",
buf,
"Definir um Tipo de MIMO para este objeto,\n\
vai Ver->Tipos de MIMO...\n\
\n\
Para ajuda adicional em armar Tipos de MIMO,\n\
vêem Ajuda->Tipos De MIMO...",
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Åpen Failed",
buf,
"Definere en MIME Type for dette objektet,\n\
dra til View->MIME Types...\n\
\n\
For ytterligere hjelp på innstilling ser opp MIME Types,\n\
Help->MIME Types...",
#else
"Open Failed",
buf,
"To define a MIME Type for this object, go to\n\
View->MIME Types...\n\
\n\
For additional help on setting up MIME Types, see\n\
Help->MIME Types...",
#endif
		    toplevel
		);
		g_free(buf);
	    }
	    return(-2);
	}

	/* At this point a MIME Type has been matched, find out how we
	 * should open it by checking its handler code
	 *
	 * Any handler code specifying an internal Endeavour resource
	 * will return within this switch() statement
	 */
	switch(mt_ptr->handler)
	{
	  case EDV_MIMETYPE_HANDLER_COMMAND:
	    /* Open this object with a command, so go on with the
	     * rest of this function which deals with opening by the
	     * MIME Type's command
	     */
	    break;

	  case EDV_MIMETYPE_HANDLER_EDV_ARCHIVER:
	    EDVNewArchiverOpen(core_ptr, path);
	    return(0);
	    break;

	  case EDV_MIMETYPE_HANDLER_EDV_IMAGE_BROWSER:
/* TODO */
	    return(0);
	    break;

	  case EDV_MIMETYPE_HANDLER_EDV_RECYCLE_BIN:
/* TODO */
	    return(0);
	    break;

	  default:
	    EDVPlaySoundWarning(core_ptr);
	    EDVMessageWarning(
#if defined(PROG_LANGUAGE_SPANISH)
"El Tratante No Sostuvo",
"El tratante definido para el de tipo MIME asociado\n\
con este objeto no es sostenido.\n",
#elif defined(PROG_LANGUAGE_FRENCH)
"L'Agent N'A Pas Soutenu",
"L'agent défini pour le Type de MIME associé avec cet\n\
objet n'est pas soutenu.\n",
#elif defined(PROG_LANGUAGE_GERMAN)
"Behandler Hat Nicht Unterstützt",
"Der Behandler ist für den MIME Typ, der mit diesem\n\
objekt verbunden worden ist, nicht unterstützt hat\n\
definiert.\n",
#elif defined(PROG_LANGUAGE_ITALIAN)
"L'Addestratore Non Ha Sostenuto",
"L'Addestratore definito per il Tipo di MIME è frequentato\n\
quest'oggetto non è sostenuto.\n",
#elif defined(PROG_LANGUAGE_DUTCH)
"Handler Steunde Niet",
"De handler is voor het MIME Type, die met deze voorwerp\n\
geassocieerd is niet gesteund definieerde.\n",
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"O Manipulador Nao Apoiou",
"O Manipulador definido para o Tipo de MIMO associado\n\
com este objeto nao é apoiado.\n",
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Behandler Not Supported",
"Det behandler-definerte for den MIME Type som vÆrer\n\
tilknyttet med dette objektet ikke støtter.\n",
#else
"Handler Not Supported",
"The handler defined for the MIME Type associated\n\
with this object is not supported.\n",
#endif
		NULL,
		toplevel
	    );
	    return(-2);
	    break;
	}

	/* If this point has been reached, it means that an external
	 * command should be executed to open the object
	 */

	/* Get the base command command_base that matches the given
	 * command name or the first defined command on the MIME Type
	 * if no command name is given
	 */
	command_base = NULL;
	if(!STRISEMPTY(command_name))
	{
	    const gchar *s;
	    /* Command name is given, iterate through commands and get
	     * the command who's name matches the given command name
	     * as the base command
	     */
	    for(i = 0; i < mt_ptr->total_commands; i++)
	    {
		s = mt_ptr->command_name[i];
		if(!STRISEMPTY(s) ? !g_strcasecmp(s, command_name) : FALSE)
		{
		    command_base = mt_ptr->command[i];
		    break;
		}
	    }
	}
	else
	{
	    /* No specified command name given, so use the default
	     * (the first) command defined on the MIME Type (if any)
	     */
	    if(mt_ptr->total_commands > 0)
		command_base = mt_ptr->command[0];
	}

	/* Unable to find base command from the matched MIME Type? */
	if(STRISEMPTY(command_base))
	{
	    /* MIME Type has no such command */
	    if(verbose)
	    {
		gchar *buf;
		if(!STRISEMPTY(command_name))
		    buf = g_strdup_printf(
#if defined(PROG_LANGUAGE_SPANISH)
"MIME Type \"%s\" no tiene una orden de \"%s\".\n"
#elif defined(PROG_LANGUAGE_FRENCH)
"MIME Type \"%s\" n'a pas un ordre de \"%s\".\n"
#elif defined(PROG_LANGUAGE_GERMAN)
"MIME Type \"%s\" hat keinen \"%s\" Befehl.\n"
#elif defined(PROG_LANGUAGE_ITALIAN)
"MIME Type \"%s\" non ha un comando di \"%s\".\n"
#elif defined(PROG_LANGUAGE_DUTCH)
"MIME Type \"%s\" heeft geen \"%s\" bevel.\n"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"MIME Type \"%s\" não tem um comando de \"%s\".\n"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"MIME Type \"%s\" har ikke en \"%s\" kommando.\n"
#else
"MIME Type \"%s\" does not have a \"%s\" command.\n"
#endif
			, mt_ptr->type, command_name
		    );
		else
		    buf = g_strdup_printf(
#if defined(PROG_LANGUAGE_SPANISH)
"MIME Type \"%s\" no tiene ninguna orden definida.\n"
#elif defined(PROG_LANGUAGE_FRENCH)
"MIME Type \"%s\" n'a pas d'ordres définis.\n"
#elif defined(PROG_LANGUAGE_GERMAN)
"MIME Type \"%s\" hat irgendeine definierten Befehle nicht.\n"
#elif defined(PROG_LANGUAGE_ITALIAN)
"MIME Type \"%s\" non ha qualunque comandi definiti.\n"
#elif defined(PROG_LANGUAGE_DUTCH)
"MIME Type \"%s\" heeft geen gedefinieerde bevelen.\n"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"MIME Type \"%s\" não tem qualquer comandos definidos.\n"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"MIME Type \"%s\" har ikke noen definerte kommandoer.\n"
#else
"MIME Type \"%s\" does not have any defined commands.\n"
#endif
			, mt_ptr->type
		    );
		EDVPlaySoundWarning(core_ptr);
		EDVMessageWarning(
#if defined(PROG_LANGUAGE_SPANISH)
"Abra Fallado",
buf,
"Para redactar el de tipo MIME para este objeto,\n\
va a Ver->De Tipo MIME...\n\
\n\
Para la ayuda adicional a establecer de tipo MIME,\n\
Vea Ayuda->De Tipo MIME...",
#elif defined(PROG_LANGUAGE_FRENCH)
"Ouvrir A Echoué",
buf,
"Pour éditer le Type de MIME pour cet objet,\n\
aller Regarder->Les Types De MIME...\n\
\n\
Pour l'aide supplémentaire sur monte les Types de MIME,\n\
L'Aide D'évêché->Les Types De MIME...",
#elif defined(PROG_LANGUAGE_GERMAN)
"Offen Versagt",
buf,
"Um den MIME Typ für dieses Objekt zu redigieren,\n\
gehen Sie, Um->MIME Typen Anzusehen...\n\
\n\
Für zusätzliche Hilfe stellend auf MIME Typen auf,\n\
sieht Hilfe->MIME Typen...",
#elif defined(PROG_LANGUAGE_ITALIAN)
"Aprire Fallito",
buf,
"Per redigere il Tipo di MIME per quest'oggetto,\n\
andare Osservare->I Tipi Di MIME...\n\
\n\
Per l'aiuto ulteriore su installa i Tipi di MIME,\n\
vedere L'Aiuto->I Tipi Di MIME...",
#elif defined(PROG_LANGUAGE_DUTCH)
"Open Verzuimde",
buf,
"Om het MIME Type voor deze voorwerp te redigeren,\n\
ga Om->MIME Typen Te Bekijken...\n\
\n\
Voor bijkomende hulp op opstellen van MIME Typen,\n\
ziet Hulp->MIME Typen...",
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Abra Fracassado",
buf,
"Editar o Tipo de MIMO para este objeto,\n\
vai Ver->Tipos De MIMO...\n\
\n\
Para ajuda adicional em armar Tipos de MIMO,\n\
vêem Ajuda->Tipos De MIMO...",
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Åpen Failed",
buf,
"Redigere MIME Type for dette objektet,\n\
dra til View->MIME Types...\n\
\n\
For ytterligere hjelp på innstilling ser opp MIME Types,\n\
Help->MIME Types...",
#else
"Open Failed",
buf,
"To edit the MIME Type for this object, go to\n\
View->MIME Types...\n\
\n\
For additional help on setting up MIME Types, see\n\
Help->MIME Types...",
#endif
		    toplevel
		);
		g_free(buf);
	    }
	    return(-2);
	}


	/* If the matched base command is not an absolute path then it
	 * implies that it reffers to another MIME Type, so try to find
	 * another MIME Type who's class is EDV_MIMETYPE_CLASS_PROGRAM and
	 * type matches the base command
	 */
	if(*command_base != G_DIR_SEPARATOR)
	{
	    /* Find another MIME Type who's type matches the matched
	     * base command
	     */
	    mt_ptr = NULL;
	    got_match = FALSE;
	    for(i = 0; i < core_ptr->total_mimetypes; i++)
	    {
		mt_ptr = core_ptr->mimetype[i];
		if(mt_ptr == NULL)
		    continue;

		/* Check only MIME Types who's class is
		 * EDV_MIMETYPE_CLASS_PROGRAM
		 */
		if(mt_ptr->mt_class == EDV_MIMETYPE_CLASS_PROGRAM)
		{
		    if((mt_ptr->type != NULL) ?
			!strcmp(mt_ptr->type, command_base) : FALSE
		    )
		    {
			/* Found a referenced MIME Type, now get its
			 * first command and update command_base to
			 * point to this MIME Type's command base
			 *
			 * Note that only the first command will be
			 * obtained from a MIME Type of class
			 * EDV_MIMETYPE_CLASS_PROGRAM
			 */
			if(mt_ptr->total_commands > 0)
			    command_base = mt_ptr->command[0];
			else
			    command_base = NULL;
			got_match = TRUE;
		    }
		}
		if(got_match)
		    break;
	    }
	    /* Unable to match MIME Type? */
	    if(!got_match || (mt_ptr == NULL))
	    {
		/* No MIME Type associated for the given path */
		if(verbose)
		{
		    gchar *buf = g_strdup_printf(
#if defined(PROG_LANGUAGE_SPANISH)
"Incapaz de encontrar la clase de la Aplicación \"%s\" de tipo MIME."
#elif defined(PROG_LANGUAGE_FRENCH)
"Incapable pour trouver \"%s\" de Type de MIME de classe d'Application."
#elif defined(PROG_LANGUAGE_GERMAN)
"Unfähig, Anwendung Klasse MIME Typ \"%s\" zu finden."
#elif defined(PROG_LANGUAGE_ITALIAN)
"Incapace per trovare \"%s\" di Tipo di MIME di classe di Domanda."
#elif defined(PROG_LANGUAGE_DUTCH)
"Onbekwaam Toepassing klas MIME Type \"%s\" te vinden."
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Incapaz de achar \"%s\" de Tipo de MIMO de classe de Aplicação."
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Maktesløs finne Application klasse MIME Type \"%s\"."
#else
"Unable to find Application class MIME Type \"%s\"."
#endif
			, command_base
		    );
		    EDVPlaySoundWarning(core_ptr);
		    EDVMessageWarning(
#if defined(PROG_LANGUAGE_SPANISH)
"Abra Fallado",
buf,
"Para redactar el de tipo MIME para este objeto,\n\
va a Ver->De Tipo MIME...\n\
\n\
Para la ayuda adicional a establecer de tipo MIME,\n\
Vea Ayuda->De Tipo MIME...",
#elif defined(PROG_LANGUAGE_FRENCH)
"Ouvrir A Echoué",
buf,
"Pour éditer le Type de MIME pour cet objet,\n\
aller Regarder->Les Types De MIME...\n\
\n\
Pour l'aide supplémentaire sur monte les Types de MIME,\n\
L'Aide D'évêché->Les Types De MIME...",
#elif defined(PROG_LANGUAGE_GERMAN)
"Offen Versagt",
buf,
"Um den MIME Typ für dieses Objekt zu redigieren,\n\
gehen Sie, Um->MIME Typen Anzusehen...\n\
\n\
Für zusätzliche Hilfe stellend auf MIME Typen auf,\n\
sieht Hilfe->MIME Typen...",
#elif defined(PROG_LANGUAGE_ITALIAN)
"Aprire Fallito",
buf,
"Per redigere il Tipo di MIME per quest'oggetto,\n\
andare Osservare->I Tipi Di MIME...\n\
\n\
Per l'aiuto ulteriore su installa i Tipi di MIME,\n\
vedere L'Aiuto->I Tipi Di MIME...",
#elif defined(PROG_LANGUAGE_DUTCH)
"Open Verzuimde",
buf,
"Om het MIME Type voor deze voorwerp te redigeren,\n\
ga Om->MIME Typen Te Bekijken...\n\
\n\
Voor bijkomende hulp op opstellen van MIME Typen,\n\
ziet Hulp->MIME Typen...",
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Abra Fracassado",
buf,
"Editar o Tipo de MIMO para este objeto,\n\
vai Ver->Tipos De MIMO...\n\
\n\
Para ajuda adicional em armar Tipos de MIMO,\n\
vêem Ajuda->Tipos De MIMO...",
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Åpen Failed",
buf,
"Redigere MIME Type for dette objektet,\n\
dra til View->MIME Types...\n\
\n\
For ytterligere hjelp på innstilling ser opp MIME Types,\n\
Help->MIME Types...",
#else
"Open Failed",
buf,
"To edit the MIME Type for this object, go to\n\
View->MIME Types...\n\
\n\
For additional help on setting up MIME Types, see\n\
Help->MIME Types...",
#endif
			toplevel
		    );
		    g_free(buf);
		}
		return(-2);
	    }
	}


	/* mt_ptr at this point should be considered invalid */

	/* The command_base may be modified at this point, check if
	 * the MIME Type has no defined command
	 */
	if(STRISEMPTY(command_base))
	{
	    if(verbose)
	    {
		gchar *buf;
		if(mt_ptr != NULL)
		    buf = g_strdup_printf(
#if defined(PROG_LANGUAGE_SPANISH)
"\"%s\" de tipo MIME no tiene una orden definida."
#elif defined(PROG_LANGUAGE_FRENCH)
"MIME \"%s\" de Type n'a pas un ordre défini."
#elif defined(PROG_LANGUAGE_GERMAN)
"MIME Typ \"%s\" hat keinen definierten Befehl."
#elif defined(PROG_LANGUAGE_ITALIAN)
"\"%s\" di Tipo di MIME non ha un comando definito."
#elif defined(PROG_LANGUAGE_DUTCH)
"MIME Type \"%s\" heeft geen gedefinieerd bevel."
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"\"%s\" de Tipo de MIMO nao tem um comando definido."
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"MIME Type \"%s\" har ikke en definert kommando."
#else
"MIME Type \"%s\" does not have a defined command."
#endif
			, mt_ptr->type
		    );
		else
		    buf = STRDUP(
"MIME Type does not have a defined command."
		    );
		EDVPlaySoundWarning(core_ptr);
		EDVMessageWarning(
#if defined(PROG_LANGUAGE_SPANISH)
"Abra Fallado",
buf,
"Para redactar el de tipo MIME para este objeto,\n\
va a Ver->De Tipo MIME...\n\
\n\
Para la ayuda adicional a establecer de tipo MIME,\n\
Vea Ayuda->De Tipo MIME...",
#elif defined(PROG_LANGUAGE_FRENCH)
"Ouvrir A Echoué",
buf,
"Pour éditer le Type de MIME pour cet objet,\n\
aller Regarder->Les Types De MIME...\n\
\n\
Pour l'aide supplémentaire sur monte les Types de MIME,\n\
L'Aide D'évêché->Les Types De MIME...",
#elif defined(PROG_LANGUAGE_GERMAN)
"Offen Versagt",
buf,
"Um den MIME Typ für dieses Objekt zu redigieren,\n\
gehen Sie, Um->MIME Typen Anzusehen...\n\
\n\
Für zusätzliche Hilfe stellend auf MIME Typen auf,\n\
sieht Hilfe->MIME Typen...",
#elif defined(PROG_LANGUAGE_ITALIAN)
"Aprire Fallito",
buf,
"Per redigere il Tipo di MIME per quest'oggetto,\n\
andare Osservare->I Tipi Di MIME...\n\
\n\
Per l'aiuto ulteriore su installa i Tipi di MIME,\n\
vedere L'Aiuto->I Tipi Di MIME...",
#elif defined(PROG_LANGUAGE_DUTCH)
"Open Verzuimde",
buf,
"Om het MIME Type voor deze voorwerp te redigeren,\n\
ga Om->MIME Typen Te Bekijken...\n\
\n\
Voor bijkomende hulp op opstellen van MIME Typen,\n\
ziet Hulp->MIME Typen...",
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Abra Fracassado",
buf,
"Editar o Tipo de MIMO para este objeto,\n\
vai Ver->Tipos De MIMO...\n\
\n\
Para ajuda adicional em armar Tipos de MIMO,\n\
vêem Ajuda->Tipos De MIMO...",
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Åpen Failed",
buf,
"Redigere MIME Type for dette objektet,\n\
dra til View->MIME Types...\n\
\n\
For ytterligere hjelp på innstilling ser opp MIME Types,\n\
Help->MIME Types...",
#else
"Open Failed",
buf,
"To edit the MIME Type for this object, go to\n\
View->MIME Types...\n\
\n\
For additional help on setting up MIME Types, see\n\
Help->MIME Types...",
#endif
		    toplevel
		);
		g_free(buf);
	    }
	    return(-2);
	}


	/* We now have sufficient information to run the program */
	if(TRUE)
	{
	    gint pid;
	    gchar *parent, *pwd, *cmd;

	    /* Format command */
	    cmd = EDVOpenFormatCommandPath(command_base, path);

	    /* Record previous working dir */  
	    pwd = EDVGetCWD();

	    /* Set new working dir as the parent of the specified
	     * path
	     */
	    parent = g_dirname(path);
	    EDVCHDir(parent);
	    g_free(parent);

	    /* Execute command */
	    pid = EDVSystem(cmd);
	    if(pid <= 0)
		status = -1;
	    else
		status = 0;

	    g_free(cmd);

	    /* Change back to the previous working dir */
	    EDVCHDir(pwd);
	    g_free(pwd);
	}

	return(status);
}

/*
 *      Procedure to execute an application selected on the `open with'
 *	popup list widget mapped at the current pointer position.
 *
 *      If and only if stdout_path_rtn or stderr_path_rtn are not NULL,
 *      then they will be set with a dynamically allocated string
 *      containing the stdout and stderr output file paths. Which the
 *      calling function must deallocate.
 *
 *      Returns:
 *
 *      0       Success
 *      -1      General error
 *      -2      No MIME Type, no application, or ambiguous
 *      -3      System error
 *	-4	User responded with `Cancel'
 *      -6      Call is a renterent to an existing call to this function
 */
gint EDVOpenWithObjectPath(
	edv_core_struct *core_ptr, const gchar *path,
	const gchar *command_name,
	GtkWidget *toplevel, gboolean verbose,
	gchar **stdout_path_rtn, gchar **stderr_path_rtn
)
{
	const gchar *value, *command_base;
	gint mt_num, status;
	pulist_struct *pulist;
	edv_mimetype_struct *mt_ptr;
	
	/* Reset returns */
	if(stdout_path_rtn != NULL)
	    *stdout_path_rtn = NULL;
	if(stderr_path_rtn != NULL)
	    *stderr_path_rtn = NULL;

	if((core_ptr == NULL) || STRISEMPTY(path))
	    return(-1);

	/* Get pointer to `open with' popup list */
	pulist = core_ptr->openwith_pulist;
	if(pulist == NULL)
	    return(-1);
	if(PUListIsQuery(pulist))
	    return(-1);

	/* Map the Open With Popup List */
	value = PUListMapQuery(
	    pulist,
	    NULL,		/* No initial value, use last value */
	    -1,			/* Default number of visible lines */
	    PULIST_RELATIVE_DOWN,
	    NULL,		/* No relative widget, meaning use pointer coordinates */
	    NULL		/* No map widget */
	);
	/* User canceled? */
	if(value == NULL)
	    return(-4);

	/* Get MIME Type index number from the matched value */
	mt_num = (gint)PUListGetDataFromValue(pulist, value);
	if((mt_num >= 0) && (mt_num < core_ptr->total_mimetypes))
	    mt_ptr = core_ptr->mimetype[mt_num];
	else
	    mt_ptr = NULL;

	/* No such MIME Type? */
	if(mt_ptr == NULL)
	    return(-2);

	/* MIME Type must be of class EDV_MIMETYPE_CLASS_PROGRAM */
	if(mt_ptr->mt_class != EDV_MIMETYPE_CLASS_PROGRAM)
	    return(-2);

	/* Get first command on the MIME Type as the base command */
	if(mt_ptr->total_commands > 0)
	    command_base = mt_ptr->command[0];
	else
	    command_base = NULL;

	/* MIME Type has no defined command? */
	if(STRISEMPTY(command_base))
	{
	    if(verbose)
	    {
		gchar *buf;
		if(mt_ptr != NULL)
		    buf = g_strdup_printf(
#if defined(PROG_LANGUAGE_SPANISH)
"\"%s\" de tipo MIME no tiene una orden definida."
#elif defined(PROG_LANGUAGE_FRENCH)
"MIME \"%s\" de Type n'a pas un ordre défini."
#elif defined(PROG_LANGUAGE_GERMAN)
"MIME Typ \"%s\" hat keinen definierten Befehl."
#elif defined(PROG_LANGUAGE_ITALIAN)
"\"%s\" di Tipo di MIME non ha un comando definito."
#elif defined(PROG_LANGUAGE_DUTCH)
"MIME Type \"%s\" heeft geen gedefinieerd bevel."
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"\"%s\" de Tipo de MIMO nao tem um comando definido."
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"MIME Type \"%s\" har ikke en definert kommando."
#else
"MIME Type \"%s\" does not have a defined command."
#endif
			, mt_ptr->type
		    );
		else
		    buf = STRDUP(
"MIME Type does not have a defined command."
		    );
		EDVPlaySoundWarning(core_ptr);
		EDVMessageWarning(
#if defined(PROG_LANGUAGE_SPANISH)
"Abra Fallado",
buf,
"Para redactar el de tipo MIME para este objeto,\n\
va a Ver->De Tipo MIME...\n\
\n\
Para la ayuda adicional a establecer de tipo MIME,\n\
Vea Ayuda->De Tipo MIME...",
#elif defined(PROG_LANGUAGE_FRENCH)
"Ouvrir A Echoué",
buf,
"Pour éditer le Type de MIME pour cet objet,\n\
aller Regarder->Les Types De MIME...\n\
\n\
Pour l'aide supplémentaire sur monte les Types de MIME,\n\
L'Aide D'évêché->Les Types De MIME...",
#elif defined(PROG_LANGUAGE_GERMAN)
"Offen Versagt",
buf,
"Um den MIME Typ für dieses Objekt zu redigieren,\n\
gehen Sie, Um->MIME Typen Anzusehen...\n\
\n\
Für zusätzliche Hilfe stellend auf MIME Typen auf,\n\
sieht Hilfe->MIME Typen...",
#elif defined(PROG_LANGUAGE_ITALIAN)
"Aprire Fallito",
buf,
"Per redigere il Tipo di MIME per quest'oggetto,\n\
andare Osservare->I Tipi Di MIME...\n\
\n\
Per l'aiuto ulteriore su installa i Tipi di MIME,\n\
vedere L'Aiuto->I Tipi Di MIME...",
#elif defined(PROG_LANGUAGE_DUTCH)
"Open Verzuimde",
buf,
"Om het MIME Type voor deze voorwerp te redigeren,\n\
ga Om->MIME Typen Te Bekijken...\n\
\n\
Voor bijkomende hulp op opstellen van MIME Typen,\n\
ziet Hulp->MIME Typen...",
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Abra Fracassado",
buf,
"Editar o Tipo de MIMO para este objeto,\n\
vai Ver->Tipos De MIMO...\n\
\n\
Para ajuda adicional em armar Tipos de MIMO,\n\
vêem Ajuda->Tipos De MIMO...",
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Åpen Failed",
buf,
"Redigere MIME Type for dette objektet,\n\
dra til View->MIME Types...\n\
\n\
For ytterligere hjelp på innstilling ser opp MIME Types,\n\
Help->MIME Types...",
#else
"Open Failed",
buf,
"To edit the MIME Type for this object, go to\n\
View->MIME Types...\n\
\n\
For additional help on setting up MIME Types, see\n\
Help->MIME Types...",
#endif
		    toplevel
		);
		g_free(buf);
	    }
	    return(-2);
	}


	/* We now have sufficient information to run the program */
	if(TRUE)
	{
	    gint pid;
	    gchar *parent, *pwd, *cmd;

	    /* Format command */
	    cmd = EDVOpenFormatCommandPath(command_base, path);

	    /* Record previous working dir */
	    pwd = EDVGetCWD();

	    /* Set new working dir as the parent of the specified
	     * path
	     */
	    parent = g_dirname(path);
	    EDVCHDir(parent);
	    g_free(parent);

	    /* Execute command */
	    pid = EDVSystem(cmd);
	    if(pid <= 0)
		status = -1;
	    else
		status = 0;

	    g_free(cmd);

	    /* Change back to the previous working dir */
	    EDVCHDir(pwd);
	    g_free(pwd);
	}

	return(status);
}
