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

#include "edv_types.h"
#include "edv_mime_type.h"
#include "edv_mime_types_list.h"
#include "endeavour2.h"
#include "edv_open.h"
#include "edv_op.h"
#include "edv_utils.h"
#include "edv_utils_gtk.h"

#include "edv_cfg_list.h"
#include "config.h"


static gchar *EDVOpenStripExt(const gchar *s);
static gboolean EDVOpenIsFileExecutable(const gchar *path);
static gchar *EDVOpenFormatCommand(
	edv_core_struct *core, const gchar *cmd, GList *paths_list
);

/* Open */
static gint EDVOpenIteration(
	edv_core_struct *core,
	const gchar *path,
	const gchar *command_name,
	GtkWidget *toplevel,
	const gboolean verbose,
	gchar **stdout_path_rtn, gchar **stderr_path_rtn
);
gint EDVOpen(
	edv_core_struct *core,
	GList *paths_list,
	const gchar *command_name,
	GtkWidget *toplevel,
	const gboolean verbose,
	gchar **stdout_path_rtn, gchar **stderr_path_rtn
);

/* Open With */
gint EDVOpenWith(
	edv_core_struct *core,
	GList *paths_list,
	const gchar *command_name,
	GtkWidget *toplevel,
	const 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)


/*
 *	Returns a copy of the path or name without the extension.
 */
static gchar *EDVOpenStripExt(const gchar *s)
{
	gint len;
	const gchar *name, *ext;
	gchar *path_wo_ext;

	if(s == NULL)
	    return(NULL);

	/* Get the name */
	name = strrchr(s, G_DIR_SEPARATOR);
	if(name != NULL)
	    name++;
	else
	    name = s;

	/* Get the extension */
	if(*name == '.') 
	    ext = strchr(name + 1, '.');
	else
	    ext = strchr(name, '.');

	/* No extension? */
	if(ext == NULL)
	    return(STRDUP(s));

	/* Get the path without the extension */
	len = (gint)(ext - s);
	path_wo_ext = (gchar *)g_malloc((len + 1) * sizeof(gchar));
	if(len > 0)
	    memcpy(path_wo_ext, s, len * sizeof(gchar));
	path_wo_ext[len] = '\0';

	/* Need to tack on the ending double quote character? */
	if(*path_wo_ext == '"')
	{
	    gchar *s2 = g_strconcat(path_wo_ext, "\"", NULL);
	    g_free(path_wo_ext);
	    path_wo_ext = s2;
	}

	return(path_wo_ext);

}

/*
 *	Checks if the object specified by path exists, is a file, and
 *	is executable.
 */
static gboolean EDVOpenIsFileExecutable(const gchar *path)
{
	struct stat stat_buf;

	if(STRISEMPTY(path))
	    return(FALSE);

	/* Get destination stats */
	if(stat(path, &stat_buf))
	    return(FALSE);

#ifdef S_ISREG
	/* Does the path lead to a file? */
	if(!S_ISREG(stat_buf.st_mode))
	    return(FALSE);
#endif

	/* Is this file executable? */
	return(!access(path, X_OK) ? TRUE : FALSE);
}

/*
 *	Performs substitutions to the command string specified by cmd 
 *	with the values specified in the core and the paths list.
 *
 *	Returns a dynamically allocated string describing the command
 *	with all the substitutions made.
 *
 *	Reminder: There is a sister function in lib/edv_open.c which
 *	needs to perform equvilently to this function.
 */
static gchar *EDVOpenFormatCommand(
	edv_core_struct *core, const gchar *cmd, GList *paths_list
)
{
	const gchar *path, *first_path, *last_path;
	gchar	*s, *cmd_rtn,
		*cwd_quoted, *display_quoted, *home_quoted,
		*names_quoted, *paths_quoted,
		*first_name_quoted, *first_name_wo_ext_quoted,
		*last_name_quoted, *last_name_wo_ext_quoted,
		*first_path_quoted, *first_path_wo_ext_quoted,
		*last_path_quoted, *last_path_wo_ext_quoted,
		*pid_quoted;
	GList *glist;

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

	/* Begin creating substitution values */

	/* Current working directory */
	s = EDVGetCWD();
	cwd_quoted = g_strdup_printf("\"%s\"", s);
	g_free(s);

	/* Display address */
	display_quoted = g_strdup_printf(
	    "\"%s\"",
	    g_getenv(ENV_VAR_NAME_DISPLAY)
	);

	/* Home directory */
	home_quoted = g_strdup_printf("\"%s\"", core->home_dir);

	/* All selected object names */
	names_quoted = STRDUP("\"");
	for(glist = paths_list; glist != NULL; glist = g_list_next(glist))
	{
	    path = (gchar *)glist->data;
	    if(path == NULL)
		continue;

	    s = g_strconcat(
		names_quoted,
		g_basename(path),
		(glist->next != NULL) ? " " : "\"",
		NULL
	    );
	    g_free(names_quoted);
	    names_quoted = s;
	}

	/* All selected object paths */
	paths_quoted = STRDUP("\"");
	for(glist = paths_list; glist != NULL; glist = g_list_next(glist))
	{
	    path = (gchar *)glist->data;
	    if(path == NULL)
		continue;

	    s = g_strconcat(
		paths_quoted,
		path,
		(glist->next != NULL) ? " " : "\"",
		NULL
	    );
	    g_free(paths_quoted);
	    paths_quoted = s;
	}

	/* First selected object name and path */
	glist = g_list_first(paths_list);
	first_path = (glist != NULL) ? (gchar *)glist->data : NULL;   
	if(first_path == NULL)                                        
	    first_path = "";
	first_name_quoted = g_strdup_printf("\"%s\"", g_basename(first_path));
	first_name_wo_ext_quoted = EDVOpenStripExt(first_name_quoted);
	first_path_quoted = g_strdup_printf("\"%s\"", first_path);
	first_path_wo_ext_quoted = EDVOpenStripExt(first_path_quoted);

	/* Last selected object name and path */
	glist = g_list_last(paths_list);
	last_path = (glist != NULL) ? (gchar *)glist->data : NULL;
	if(last_path == NULL)
	    last_path = "";
	last_name_quoted = g_strdup_printf("\"%s\"", g_basename(last_path));
	last_name_wo_ext_quoted = EDVOpenStripExt(last_name_quoted);
	last_path_quoted = g_strdup_printf("\"%s\"", last_path);
	last_path_wo_ext_quoted = EDVOpenStripExt(last_path_quoted);
	    


	/* Process ID */
	pid_quoted = g_strdup_printf("\"%i\"", core->pid);


	/* Begin formatting the command, always use the quoted string
	 * as the value and substitute for the quoted token first then
	 * the unquoted token
	 */
	cmd_rtn = STRDUP(cmd);

#define CMDSUB(_token_,_val_)	{			\
 gchar *s = EDVStrSub(cmd_rtn, (_token_), (_val_));	\
 g_free(cmd_rtn);					\
 cmd_rtn = s;						\
}

	/* "%cwd" - Current working directory */
	CMDSUB("\"%cwd\"", cwd_quoted);
	CMDSUB("%cwd", cwd_quoted);

	/* "%display" - Display address */
	CMDSUB("\"%display\"", display_quoted);
	CMDSUB("%display", display_quoted);    

	/* "%home" - Home directory */
	CMDSUB("\"%home\"", home_quoted);
	CMDSUB("%home", home_quoted);


	/* "%names" - Names of all the selected objects (without
	 * paths)
	 */
	CMDSUB("\"%names\"", names_quoted);
	CMDSUB("%names", names_quoted);

	/* "%paths" - Full paths of all the selected objects */
	CMDSUB("\"%paths\"", paths_quoted);
	CMDSUB("%paths", paths_quoted);


	/* "%first_name_wo_ext" - Name of the first selected object
	 * (without path or extension)
	 */
	CMDSUB("\"%first_name_wo_ext\"", first_name_wo_ext_quoted);
	CMDSUB("%first_name_wo_ext", first_name_wo_ext_quoted);

	/* "%first_name" - Name of the first selected object (without
	 * path)
	 */
	CMDSUB("\"%first_name\"", first_name_quoted);
	CMDSUB("%first_name", first_name_quoted);

	/* "%first_path_wo_ext" - Full path to the last selected
	 * object without the extension
	 */
	CMDSUB("\"%first_path_wo_ext\"", first_path_wo_ext_quoted);
	CMDSUB("%first_path_wo_ext", first_path_wo_ext_quoted);

	/* "%first_path" - Full path to the last selected object */
	CMDSUB("\"%first_path\"", first_path_quoted);
	CMDSUB("%first_path", first_path_quoted);


	/* "%name_wo_ext" - Name of the last selected object (without
	 * path or ext)
	 */
	CMDSUB("\"%name_wo_ext\"", last_name_wo_ext_quoted);
	CMDSUB("%name_wo_ext", last_name_wo_ext_quoted);

	/* "%name" - Name of last selected object (without path) */
	CMDSUB("\"%name\"", last_name_quoted);
	CMDSUB("%name", last_name_quoted);

	/* "%path_wo_ext" - Full path to the last selected object
	 * without the extension
	 */
	CMDSUB("\"%path_wo_ext\"", last_path_wo_ext_quoted);
	CMDSUB("%path_wo_ext", last_path_wo_ext_quoted);

	/* "%path" - Full path to the last selected object */
	CMDSUB("\"%path\"", last_path_quoted);
	CMDSUB("%path", last_path_quoted);


	/* "%pid" - This program's process id */
	CMDSUB("\"%pid\"", pid_quoted);
	CMDSUB("%pid", pid_quoted);


	/* "%pe" - Full path to the last selected object without
	 * the extension
	 */
	CMDSUB("\"%pe\"", last_path_wo_ext_quoted);
	CMDSUB("%pe", last_path_wo_ext_quoted);

	/* "%p" - Full path to the last selected object */
	CMDSUB("\"%p\"", last_path_quoted);
	CMDSUB("%p", last_path_quoted);

	/* "%s" - Full path to the last selected object */
	CMDSUB("\"%s\"", last_path_quoted);
	CMDSUB("%s", last_path_quoted);

#undef CMDSUB

	g_free(cwd_quoted);
	g_free(display_quoted);
	g_free(home_quoted);
	g_free(names_quoted);
	g_free(paths_quoted);
	g_free(first_name_quoted);
	g_free(first_name_wo_ext_quoted);
	g_free(last_name_quoted);
	g_free(last_name_wo_ext_quoted);
	g_free(first_path_quoted);
	g_free(first_path_wo_ext_quoted);
	g_free(last_path_quoted);
	g_free(last_path_wo_ext_quoted);
	g_free(pid_quoted);

	return(cmd_rtn);
}


/*
 *	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	Bad value or no MIME Type found.
 *	-3	System error.
 */
static gint EDVOpenIteration(
	edv_core_struct *core, const gchar *path,
	const gchar *command_name,
	GtkWidget *toplevel,
	const gboolean verbose,
	gchar **stdout_path_rtn, gchar **stderr_path_rtn
)
{
	gint i, status;
	const gchar *command_base;
	cfg_item_struct *cfg_list = core->cfg_list;
	edv_mime_type_struct *mt;

	if(STRISEMPTY(path))
	    return(-2);

	/* Match a MIME Type of class EDV_MIME_TYPE_CLASS_FORMAT or
	 * EDV_MIME_TYPE_CLASS_UNIQUE with the specified path
	 */
	mt = EDVMimeTypesListMatchPath(
	    core->mimetype, core->total_mimetypes,
	    path
	);

	/* Unable to find a MIME Type appropriate for this object? */
	if(mt == NULL)
	{
	    const gchar *def_viewer_cmd = EDV_GET_S(EDV_CFG_PARM_PROG_DEF_VIEWER);

	    /* If this object is a file and is set executable, then
	     * attempt to run it
	     */
	    if(EDVOpenIsFileExecutable(path))
	    {
		gint pid;
		gchar *parent, *cmd;

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

		/* Set new working dir as the parent of the specified
		 * path
		 */
		parent = g_dirname(path);
		EDVSetCWD(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 */
		EDVSetCWD(pwd);
		g_free(pwd);

		/* Failed to execute the object? */
		if((status != 0) && 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);
		    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);
	    }
	    /* Object is not executable, use the default viewer to view
	     * this object
	     */
	    else if(!STRISEMPTY(def_viewer_cmd))
	    {
		gint pid;
		gchar *parent, *cmd;
		GList *paths_list = NULL;

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

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

		/* Format command */
		paths_list = g_list_append(
		    paths_list,
		    STRDUP(path)
		);
		cmd = EDVOpenFormatCommand(core, def_viewer_cmd, paths_list);
		g_list_foreach(paths_list, (GFunc)g_free, NULL);
		g_list_free(paths_list);

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

		g_free(cmd);

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

		return(status);
	    }
	    /* All else print warning about not finding a MIME Type,
	     * not being able to execute, and not being able to use a
	     * default viewer for this object
	     */
	    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);
		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 the MIME Type's handler code
	 *
	 * Any handler code specifying an internal Endeavour method
	 * will return() within this switch() statement
	 */
	switch(mt->handler)
	{
	  case EDV_MIME_TYPE_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_MIME_TYPE_HANDLER_EDV_ARCHIVER:
	    EDVNewArchiver(core, path, NULL);
	    return(0);
	    break;

	  case EDV_MIME_TYPE_HANDLER_EDV_IMAGE_BROWSER:
	    EDVNewImbr(core, path);
	    return(0);
	    break;

	  case EDV_MIME_TYPE_HANDLER_EDV_RECYCLE_BIN:
	    EDVMapRecBin(core);
	    return(0);
	    break;

	  default:
	    EDVPlaySoundWarning(core);
	    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 specified 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, then it means that an
	 * external command should be executed to open the object
	 */

	/* Get the command_base that matches the specified command name,
	 * or the first defined command on the MIME Type if no command
	 * name is given
	 */
	command_base = NULL;
	if(!STRISEMPTY(command_name))
	{
	    /* The command name is specified, find a command on this
	     * MIME Type who's command name matches the specified
	     * command name
	     */
	    edv_mime_type_command_struct *cmd = EDVMimeTypeGetCommandByName(
		mt, command_name
	    );
	    if(cmd != NULL)
		command_base = cmd->command;
	}
	else
	{
	    /* No command name specified, so use the default (the
	     * first) command defined on the MIME Type (if any)
	     */
	    GList *glist = mt->commands_list;
	    if(glist != NULL)
	    {
		edv_mime_type_command_struct *cmd = EDV_MIME_TYPE_COMMAND(
		    glist->data
		);
		if(cmd != NULL)
		    command_base = cmd->command;
	    }
	}

	/* Matched MIME Type has no command_base? */
	if(STRISEMPTY(command_base))
	{
	    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->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->type
		    );
		EDVPlaySoundWarning(core);
		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 command_base is not an absolute path then it
	 * implies that it refers to another MIME Type
	 */
	if(*command_base != G_DIR_SEPARATOR)
	{
	    /* Try to find that other MIME Type who's class is
	     * EDV_MIME_TYPE_CLASS_PROGRAM and its type matches
	     * command_base
	     */
	    gboolean got_match = FALSE;
	    mt = NULL;
	    for(i = 0; i < core->total_mimetypes; i++)
	    {
		mt = core->mimetype[i];
		if(mt == NULL)
		    continue;

		/* Check only MIME Types who's class is
		 * EDV_MIME_TYPE_CLASS_PROGRAM
		 */
		if(mt->mt_class == EDV_MIME_TYPE_CLASS_PROGRAM)
		{
		    if((mt->type != NULL) ?
			!strcmp(mt->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_MIME_TYPE_CLASS_PROGRAM
			 */
			GList *glist = mt->commands_list;
			edv_mime_type_command_struct *cmd = (glist != NULL) ?
			    EDV_MIME_TYPE_COMMAND(glist->data) : NULL;
			if(cmd != NULL)
			    command_base = cmd->command;
			got_match = TRUE;
			break;
		    }
		}
	    }
	    /* Unable to match MIME Type? */
	    if(!got_match || (mt == 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);
		    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);
	    }
	}


	/* MIME Type has no defined command? */
	if(STRISEMPTY(command_base))
	{
	    if(verbose)
	    {
		gchar *buf;
		if(mt != 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->type
		    );
		else
		    buf = STRDUP(
"MIME Type does not have a defined command."
		    );
		EDVPlaySoundWarning(core);
		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, *cmd;
	    GList *paths_list = NULL;

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

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

	    /* Format command */
	    paths_list = g_list_append(
		paths_list,
		STRDUP(path)
	    );
	    cmd = EDVOpenFormatCommand(core, command_base, paths_list);
	    g_list_foreach(paths_list, (GFunc)g_free, NULL);
	    g_list_free(paths_list);

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

	    g_free(cmd);

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

	return(status);
}

/*
 *	Opens the object(s) specified in the paths_list.
 *
 *	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	Bad value, no MIME Type, or no application.
 *	-3	System error.
 *	-6	Call is a re-enterent to an existing call to this function.
 */
gint EDVOpen(
	edv_core_struct *core,
	GList *paths_list,
	const gchar *command_name,
	GtkWidget *toplevel,
	const gboolean verbose,
	gchar **stdout_path_rtn, gchar **stderr_path_rtn
)
{
	gint status;
	GList *glist;

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

	if((core == NULL) || (paths_list == NULL))
	    return(-1);

	for(glist = paths_list; glist != NULL; glist = g_list_next(glist))
	{
	    status = EDVOpenIteration(
		core, (gchar *)glist->data,
		command_name,
		toplevel, verbose,
		stdout_path_rtn, stderr_path_rtn
	    );
	    if(status < 0)
		return(status);
	}

	return(0);
}


/*
 *	Opens the object(s) specified in the paths_list by prompting
 *	the user to select a method from the Open With Popup List.
 *
 *	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	Bad value
 *	-3	System error
 *	-4	User responded with `Cancel'
 *	-6	Call is a renterent to an existing call to this function
 */
gint EDVOpenWith(
	edv_core_struct *core,
	GList *paths_list,
	const gchar *command_name,
	GtkWidget *toplevel,
	const gboolean verbose,
	gchar **stdout_path_rtn, gchar **stderr_path_rtn
)
{
	const gchar *value, *command_base;
	gint mt_num, status;
	GList *glist;
	pulist_struct *pulist;
	edv_mime_type_struct *mt;
	edv_mime_type_command_struct *cmd;
	
	/* Reset returns */
	if(stdout_path_rtn != NULL)
	    *stdout_path_rtn = NULL;
	if(stderr_path_rtn != NULL)
	    *stderr_path_rtn = NULL;

	if((core == NULL) || (paths_list == NULL))
	    return(-1);

	/* Get the Open With Popup List */
	pulist = core->open_with_pulist;
	if(pulist == NULL)
	    return(-1);
	if(PUListIsQuery(pulist))
	    return(-1);

	/* Map the Open With Popup List */
	value = PUListMapQuery(
	    pulist,
	    NULL,		/* Use the last value */
	    -1,			/* Default number of items visible */
	    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 from the matched value */
	mt_num = (gint)PUListGetDataFromValue(pulist, value);
	mt = ((mt_num >= 0) && (mt_num < core->total_mimetypes)) ?
	    core->mimetype[mt_num] : NULL;
	if(mt == NULL)
	    return(-2);

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

	/* Get first command on the MIME Type as the base command */
	glist = mt->commands_list;
	cmd = (glist != NULL) ?
	    EDV_MIME_TYPE_COMMAND(glist->data) : NULL;
	command_base = (cmd != NULL) ? cmd->command : NULL;

	/* MIME Type has no defined command? */
	if(STRISEMPTY(command_base))
	{
	    if(verbose)
	    {
		gchar *buf;
		if(mt != 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->type
		    );
		else
		    buf = STRDUP(
"MIME Type does not have a defined command."
		    );
		EDVPlaySoundWarning(core);
		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, *cmd;

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

	    /* Set new working dir as the parent of the specified
	     * path
	     */
	    parent = g_dirname(
		(paths_list != NULL) ? (gchar *)paths_list->data : NULL
	    );
	    EDVSetCWD(parent);
	    g_free(parent);

	    /* Format command */
	    cmd = EDVOpenFormatCommand(core, command_base, paths_list);

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

	    g_free(cmd);

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

	return(status);
}
