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

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

#include "guiutils.h"
#include "cdialog.h"
#include "progressdialog.h"

#include "edvtypes.h"
#include "cfg.h"
#include "edvrecbin.h"
#include "edvrecbinfio.h"
#include "edvrecbinfop.h"
#include "endeavour.h"
#include "edvutils.h"
#include "edvutilsgtk.h"
#include "edvcfglist.h"
#include "config.h"

#include "images/pdi_file01_20x20.xpm"
#include "images/pdi_file02_20x20.xpm"
#include "images/pdi_file03_20x20.xpm"
#include "images/pdi_file04_20x20.xpm"
#include "images/pdi_file05_20x20.xpm"
#include "images/pdi_file06_20x20.xpm"
#include "images/pdi_folder_32x32.xpm"
#include "images/pdi_folderfile_32x32.xpm"
#include "images/pdi_trash01_20x20.xpm"
#include "images/pdi_trash02_20x20.xpm"
#include "images/pdi_trash03_20x20.xpm"
#include "images/pdi_trash04_20x20.xpm"
#include "images/pdi_trash05_20x20.xpm"
#include "images/pdi_trash06_20x20.xpm"

#include "images/icon_trash_32x32.xpm"
#include "images/icon_trash_empty_32x32.xpm"


/*
 *	Return values have the following error meanings:
 *
 *	0	Success (no error)
 *	-1	General error
 *	-2	Ambiguous, not found, other error
 *	-3	Systems error (out of memory/out of disk space)
 *	-4	User responded with "Cancel"
 *	-5	User responded with "No" or response is not available
 *	-6	Call would cause reentry
 */

const gchar *EDVRecBinFOPGetError();

static gboolean EDVRecBinNoDeleteCheck(
	edv_core_struct *core_ptr, GtkWidget *toplevel,
	gboolean interactive, gboolean *yes_to_all,
	const gchar *path, struct stat *stat_buf
);

static void EDVRecBinFOPMapProgressDialogRecover(
	const gchar *label, gfloat progress_value, GtkWidget *toplevel,
	gboolean force_remap
);
static void EDVRecBinFOPMapProgressDialogDelete(
	const gchar *label, gfloat progress_value, GtkWidget *toplevel,
	gboolean force_remap,
	edv_delete_method delete_method
);
static void EDVRecBinFOPMapProgressDialogPurge(
	const gchar *label, gfloat progress_value, GtkWidget *toplevel,
	gboolean force_remap
);
static gint EDVRecBinFOPRecycleProgressPercentCB(
	gpointer client_data, gfloat percent
);
static gint EDVRecBinFOPRecycleProgressCB(
	gpointer client_data, gulong pos, gulong total
);

gint EDVRecBinFOPRecover(
	edv_core_struct *core_ptr,
	guint index,			/* Recycled object to recover */
	const gchar *tar_obj,		/* Can be NULL */
	gchar **new_obj_rtn,		/* Dynamically allocated */
	GtkWidget *toplevel,		/* Can be NULL */
	gboolean show_progress, gboolean interactive,
	gboolean *yes_to_all
);

static guint EDVRecBinFOPDeleteIterate(
	edv_core_struct *core_ptr,
	const gchar *recycled_index_file,
	const gchar *path,
	guint **index_rtn, gint *total_index_rtns,
	GtkWidget *toplevel,		/* Can be NULL */
	gboolean show_progress, gboolean interactive,
	gboolean *yes_to_all, gint *status,
	edv_delete_method delete_method
);
gint EDVRecBinFOPDelete(
	edv_core_struct *core_ptr,
	const gchar *path,		/* Disk object to delete */
	guint **index_rtn, gint *total_index_rtns,
	GtkWidget *toplevel,		/* Can be NULL */
	gboolean show_progress, gboolean interactive,
	gboolean *yes_to_all
);
gint EDVRecBinFOPPurge(
	edv_core_struct *core_ptr,
	guint index,
	GtkWidget *toplevel,		/* Can be NULL */
	gfloat progress_value,		/* Can be -1.0 */
	gboolean show_progress, gboolean interactive,
	gboolean *yes_to_all
);
gint EDVRecBinFOPPurgeAll(
	edv_core_struct *core_ptr,
	GtkWidget *toplevel,		/* Can be NULL */
	gboolean show_progress, gboolean interactive,
	gboolean *yes_to_all
);


static const gchar *last_error = NULL;


#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 statically allocated string describing the last
 *	error message (or NULL if there was no error) since the last
 *	call to a EDVRecBinFOP*() function.
 */
const gchar *EDVRecBinFOPGetError()
{
	return(last_error);
}


/*
 *	Checks if the object specified by path and stat_buf may not be
 *	deleted for some reason.
 *
 *	Returns TRUE if the object may not be deleted.
 */
static gboolean EDVRecBinNoDeleteCheck(
	edv_core_struct *core_ptr, GtkWidget *toplevel,
	gboolean interactive, gboolean *yes_to_all,
	const gchar *path, struct stat *stat_buf
)
{
typedef struct {
	gchar		*path;
	gchar		*message;
	gint		error_level;
} no_delete_struct;
	gint i;
	gboolean no_delete;
	const gchar *s;
	gchar *parent_path;
	const cfg_item_struct *cfg_list;
	no_delete_struct *nd_ptr = NULL;
	no_delete_struct no_delete_list[] = EDV_NO_DELETE_LIST;


	/* Check for valid inputs, return TRUE if they are invalid to
	 * prevent calling operation from proceeding
	 */
	if((core_ptr == NULL) || STRISEMPTY(path))
	    return(TRUE);

	cfg_list = core_ptr->cfg_list;

	/* Iterate through no delete list, checking if an entry has a
	 * path that matches the given path
	 */
	i = 0;
	no_delete = FALSE;
	while(no_delete_list[i].path != NULL)
	{
	    nd_ptr = &no_delete_list[i];

	    /* Given object path matches one in the no delete list? */
	    if(!strcmp((const char *)nd_ptr->path, (const char *)path))
	    {
		/* Handle by error level of this no delete entry */
		switch(nd_ptr->error_level)
		{
		  case 0:	/* Only warn user */
		    if(interactive)
		    {


		    }
		    break;

		  case 1:	/* Query user for no delete */
		    if(interactive && !(*yes_to_all))
		    {
			gint status;
			gchar *buf = g_strdup_printf(
#if defined(PROG_LANGUAGE_SPANISH)
"El \"%s\" objetivo no se debe borrar, la razón:\n\
\n\
%s.\n\
\n\
¿Usted está seguro que usted quiere borrar este objeto?"
#elif defined(PROG_LANGUAGE_FRENCH)
"Le \"%s\" d'objet ne devrait pas être effacé, la raison:\n\
\n\
%s.\n\
\n\
Etes-vous sûr que vous voulez effacer cet objet? "
#elif defined(PROG_LANGUAGE_GERMAN)
"Das Objekt \"%s\" sollte, Grund nicht gelöscht werden:\n\
\n\
%s.\n\
\n\
Sind Sie sicher Sie dieses Objekt wollen löschen?"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Lo \"%s\" di oggetto non dovrebbe essere cancellato, la ragione:\n\
\n\
%s.\n\
\n\
Lei sono sicuro che lei vuole cancellare quest'oggetto?"
#elif defined(PROG_LANGUAGE_DUTCH)
"Het voorwerp \"%s\" zou, reden niet moeten geschrappet worden:\n\
\n\
%s.\n\
\n\
Bent u zeker u deze voorwerp wil schrappen?"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"O \"%s\" de objeto nao deve ser anulado, razão:\n\
\n\
%s.\n\
\n\
Estão seguro quer anular este objeto?"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Objektet \"%s\" stryker ikke, grunn:\n\
\n\
%s.\n\
\n\
Er De sikker De stryker dette objektet?"
#else
"The object \"%s\" should not be deleted, reason:\n\
\n\
%s.\n\
\n\
Are you sure you want to delete this object?"
#endif
			    , path,
			    (nd_ptr->message != NULL) ?
				nd_ptr->message :
				"Object is marked \"no_delete\" by this process"
			);
			EDVPlaySoundQuestion(core_ptr);
			CDialogSetTransientFor(toplevel);
			status = CDialogGetResponse(
#if defined(PROG_LANGUAGE_SPANISH)
"Borre Advertir"
#elif defined(PROG_LANGUAGE_FRENCH)
"Effacer L'Avertissement"
#elif defined(PROG_LANGUAGE_GERMAN)
"Löschen Sie Warnen"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Cancellare L'Avvertimento"
#elif defined(PROG_LANGUAGE_DUTCH)
"Schrap Waarschuwen"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Anule Aviso"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Stryk Warning"
#else
"Delete Warning"
#endif
			    , buf, NULL,
			    CDIALOG_ICON_WARNING,
			    CDIALOG_BTNFLAG_YES | CDIALOG_BTNFLAG_NO,
			    CDIALOG_BTNFLAG_NO
			);
			CDialogSetTransientFor(NULL);
			g_free(buf);

			switch(status)
			{
			  case CDIALOG_RESPONSE_YES_TO_ALL:
			    *yes_to_all = TRUE;
			  case CDIALOG_RESPONSE_YES:
			    break;

			  default:
			    no_delete = TRUE;
			    break;
			}
		    }
		    else
		    {
			no_delete = TRUE;
		    }
		    break;

		  default:	/* No delete under any case */
		    no_delete = TRUE;
		    if(interactive)
		    {
			gchar *buf = g_strdup_printf(
"Unable to delete object \"%s\".\n\
\n\
%s.\n",
			    path,
			    (nd_ptr->message != NULL) ?
				nd_ptr->message :
				"Object is marked \"no_delete\" by this process"
			);
			EDVPlaySoundError(core_ptr);
			EDVMessageError(
#if defined(PROG_LANGUAGE_SPANISH)
"Borre Fallado"
#elif defined(PROG_LANGUAGE_FRENCH)
"Effacer Echoué"
#elif defined(PROG_LANGUAGE_GERMAN)
"Löschen Sie Versagt"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Cancellare Fallito"
#elif defined(PROG_LANGUAGE_DUTCH)
"Schrap Geverzuimenene"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Anule Fracassado"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Stryk Failed"
#else
"Delete Failed"
#endif
			    , buf, NULL,
			    toplevel
			);
			g_free(buf);
		    }
		    break;
		}
		/* Stop iterating through no delete list and return
		 * response
		 */
		return(no_delete);
	    }

	    i++;
	}


	/* Check for other paths that are defined at run time and may
	 * not be deleted
	 */

	/* Special notations? */
	s = EDVGetPathName(path);
	if(!STRISEMPTY(s))
	{
	    /* Current or parent directory? */
	    if(!strcmp((const char *)s, ".") ||
	       !strcmp((const char *)s, "..")
	    )
	    {
		if(interactive)
		{
		    gchar *buf = g_strdup_printf(
#if defined(PROG_LANGUAGE_SPANISH)
"Las anotaciones objetivas especiales tal como \"%s\"\n\
no se están acostumbrados a reffer a un objeto que\n\
deberá ser borrado."
#elif defined(PROG_LANGUAGE_FRENCH)
"Les notations spéciales d'objet telles que \"%s\"\n\
ne peut pas être utilisé à reffer à un objet qui\n\
va être effacé."
#elif defined(PROG_LANGUAGE_GERMAN)
"Besondere Objekt Aufzeichnungen kann wie zum\n\
Beispiel \"%s\" zu reffer zu einem Objekt nicht benutzt\n\
werden, das gelöscht werden soll."
#elif defined(PROG_LANGUAGE_ITALIAN)
"Le notazioni di oggetto speciali come \"%s\" non\n\
possono essere usate al reffer a un oggetto che è\n\
essere cancellato."
#elif defined(PROG_LANGUAGE_DUTCH)
"Speciale voorwerp aantekeningen zal zoals \"%s\"\n\
te reffer aan een voorwerp niet misschien gebruikt\n\
worden dat geschrappet worden zal."
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Anotações especiais de objeto tal como \"%s\"\n\
nao pode ser usado a reffer a um objeto que é ser\n\
anulado."
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Spesiell objektnotasjoner slik som \"%s\" ikke\n\
brukt til reffer til et objekt stryker som."
#else
"Special object notations such as \"%s\" may not be\n\
used to reffer to an object that is to be deleted."
#endif
			, s
		    );
		    EDVPlaySoundError(core_ptr);
		    EDVMessageError(
#if defined(PROG_LANGUAGE_SPANISH)
"Borre Fallado"
#elif defined(PROG_LANGUAGE_FRENCH)
"Effacer Echoué"
#elif defined(PROG_LANGUAGE_GERMAN)
"Löschen Sie Versagt"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Cancellare Fallito"
#elif defined(PROG_LANGUAGE_DUTCH)
"Schrap Geverzuimenene"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Anule Fracassado"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Stryk Failed"
#else
"Delete Failed"
#endif
			, buf, NULL,
			toplevel
		    );
		    g_free(buf);
		}
		return(TRUE);
	    }
	}

	/* Home directory? */
	s = core_ptr->home_dir;
	if(!STRISEMPTY(s) ?
	    !strcmp((const char *)s, (const char *)path) : FALSE
	)
	{
	    if(interactive)
	    {
		gchar *buf = g_strdup_printf(
#if defined(PROG_LANGUAGE_SPANISH)
"El \"%s\" objetivo no se puede borrar, la razón:\n\
\n\
La guía buscadora del usuario."
#elif defined(PROG_LANGUAGE_FRENCH)
"Le \"%s\" d'objet ne peut pas être effacé, la raison:\n\
\n\
L'annuaire de maison de l'utilisateur."
#elif defined(PROG_LANGUAGE_GERMAN)
"Das Objekt \"%s\" kann, Grund nicht gelöscht werden:\n\
\n\
Das Heim von Verbraucher Verzeichnis."
#elif defined(PROG_LANGUAGE_ITALIAN)
"Lo \"%s\" di oggetto non può essere cancellato, la ragione:\n\
\n\
L'elenco di casa dell'operatore."
#elif defined(PROG_LANGUAGE_DUTCH)
"Het voorwerp \"%s\" kan niet, reden is geschrappet:\n\
\n\
Het huis van gebruiker gids."
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"O \"%s\" de objeto nao pode ser anulado, razão:\n\
\n\
O guia de lar do operador."
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Objektet \"%s\" strøket ikke, grunn:\n\
\n\
Brukers hjemkatalog."
#else
"Unable to delete object \"%s\".\n\
\n\
Object is the user's home directory.\n"
#endif
		    , path
		);
		EDVPlaySoundError(core_ptr);
		EDVMessageError(
#if defined(PROG_LANGUAGE_SPANISH)
"Borre Fallado"
#elif defined(PROG_LANGUAGE_FRENCH)
"Effacer Echoué"
#elif defined(PROG_LANGUAGE_GERMAN)
"Löschen Sie Versagt"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Cancellare Fallito"
#elif defined(PROG_LANGUAGE_DUTCH)
"Schrap Geverzuimenene"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Anule Fracassado"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Stryk Failed"
#else
"Delete Failed"
#endif
		    , buf, NULL,
		    toplevel
		);
		g_free(buf);
	    }
	    return(TRUE);
	}

	/* Is the object part of the recycled objects system?
	 *
	 * Check this by getting the parent of the recycled objects
	 * index file, this parent directory will be checked to see if
	 * it is the parent of the specified object
	 *
	 * If it is then we know that the object is in within the
	 * recycled objects directory and therefore is a part of the
	 * recycled objects system (which may not be deleted)
	 */
	s = EDV_GET_S(EDV_CFG_PARM_FILE_RECYCLED_INDEX);
	parent_path = !STRISEMPTY(s) ? g_dirname(s) : NULL;
	if(!STRISEMPTY(parent_path) ? EDVIsParentPath(parent_path, path) : FALSE)
	{
	    if(interactive)
	    {
		gchar *buf = g_strdup_printf(
#if defined(PROG_LANGUAGE_SPANISH)
"El \"%s\" objetivo no se puede borrar, la razón:\n\
\n\
El objeto forma parte del sistema de objetos de\n\
recycled (purga de uso en lugar)."
#elif defined(PROG_LANGUAGE_FRENCH)
"Le \"%s\" d'objet ne peut pas être effacé, la raison:\n\
\n\
L'objet fait partie du système d'objets recyclé\n\
(la purge d'usage plutôt)."
#elif defined(PROG_LANGUAGE_GERMAN)
"Das Objekt \"%s\" kann, Grund nicht gelöscht werden:\n\
\n\
Objekt ist teil des wiederverwerteten objekte systems\b\
(gebrauch reinigt anstatt)."
#elif defined(PROG_LANGUAGE_ITALIAN)
"Lo \"%s\" di oggetto non può essere cancellato, la ragione:\n\
\n\
L'oggetto è la parte del sistema di oggetti riciclato\n\
(la purga di uso invece)."
#elif defined(PROG_LANGUAGE_DUTCH)
"Het voorwerp \"%s\" kan niet, reden is geschrappet:\n\
\n\
Voorwerp is deel van de gerecyclde voorwerpen systeem\n\
(gebruik zuivering in plaats van)."
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"O \"%s\" de objeto nao pode ser anulado, razão:\n\
\n\
O objeto é parte do sistema de objetos de recycled\n\
(purge de uso contráriamente)."
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Objektet \"%s\" strøket ikke, grunn:\n\
\n\
Objekt er del av det resirkuleredde objektsystem renser\n\
(bruk istedenfor)."
#else
"Unable to delete object \"%s\".\n\
\n\
Object is part of the recycled objects system.\n"
#endif
		    , path
		);
		EDVPlaySoundError(core_ptr);
		EDVMessageError(
#if defined(PROG_LANGUAGE_SPANISH)
"Borre Fallado"
#elif defined(PROG_LANGUAGE_FRENCH)
"Effacer Echoué"
#elif defined(PROG_LANGUAGE_GERMAN)
"Löschen Sie Versagt"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Cancellare Fallito"
#elif defined(PROG_LANGUAGE_DUTCH)
"Schrap Geverzuimenene"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Anule Fracassado"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Stryk Failed"
#else
"Delete Failed"
#endif
		    , buf, NULL,
		    toplevel
		);
		g_free(buf);
	    }

	    g_free(parent_path);

	    return(TRUE);
	}

	g_free(parent_path);

	return(FALSE);
}


/*
 *      Maps the progress dialog as needed in animation mode for recover.
 */
static void EDVRecBinFOPMapProgressDialogRecover(
	const gchar *label, gfloat progress_value, GtkWidget *toplevel,
	gboolean force_remap
)
{
	guint8	**start_icon_data[3],
		**icon_data[6],
		**end_icon_data[3];

	/* Already mapped? */
	if(ProgressDialogIsQuery())
	{
	    /* Check if the progress dialog needs to be unmapped and
	     * remapped again
	     */
	    if(force_remap)
	    {
		ProgressDialogBreakQuery(FALSE);
	    }
	    else
	    {
		/* Already mapped and does not need unmapping, so just
		 * update the progress message
		 */
		ProgressDialogUpdate(
		    NULL, label, NULL, NULL,
		    progress_value, EDV_DEF_PROGRESS_BAR_TICKS, TRUE
		);
		return;
	    }
	}

	ProgressDialogSetTransientFor(toplevel);

	start_icon_data[0] = (guint8 **)icon_trash_32x32_xpm;
	start_icon_data[1] = (guint8 **)icon_trash_32x32_xpm;
	start_icon_data[2] = (guint8 **)icon_trash_32x32_xpm;
	icon_data[0] = (guint8 **)pdi_file01_20x20_xpm;
	icon_data[1] = (guint8 **)pdi_file02_20x20_xpm;
	icon_data[2] = (guint8 **)pdi_file03_20x20_xpm;
	icon_data[3] = (guint8 **)pdi_file04_20x20_xpm;
	icon_data[4] = (guint8 **)pdi_file05_20x20_xpm;
	icon_data[5] = (guint8 **)pdi_file06_20x20_xpm;
	end_icon_data[0] = (guint8 **)pdi_folder_32x32_xpm;
	end_icon_data[1] = (guint8 **)pdi_folder_32x32_xpm;
	end_icon_data[2] = (guint8 **)pdi_folderfile_32x32_xpm;

	ProgressDialogMapAnimation(
#if defined(PROG_LANGUAGE_SPANISH)
	    "Convaleciente",
	    label,
	    "Parada",
#elif defined(PROG_LANGUAGE_FRENCH)
	    "Retrouver",
	    label,
	    "Arrêt",
#elif defined(PROG_LANGUAGE_GERMAN)
	    "Wiedererlangen",
	    label,
	    "Halt",
#elif defined(PROG_LANGUAGE_ITALIAN)
	    "Ricuperare",
	    label,
	    "Fermata",
#elif defined(PROG_LANGUAGE_DUTCH)
	    "Terugkrijgen",
	    label,
	    "Einde",
#elif defined(PROG_LANGUAGE_PORTUGUESE)
	    "Recuperar",
	    label,
	    "Parada",
#elif defined(PROG_LANGUAGE_NORWEGIAN)
	    "Utvinning",
	    label,
	    "Stans",
#else
	    "Recovering",
	    label,
	    "Stop",
#endif
	    start_icon_data, 3,
	    icon_data, 6,
	    end_icon_data, 3,
	    500l,
	    10000
	);
	ProgressDialogUpdate(
	    NULL, NULL, NULL, NULL,
	    progress_value, EDV_DEF_PROGRESS_BAR_TICKS, TRUE
	);

	/* Flush output so dialog gets mapped and we catch the beginning
	 * of the operation (some WM need this)
	 */
	gdk_flush();
}

/*
 *      Maps the progress dialog as needed in animation mode for deleting.
 */
static void EDVRecBinFOPMapProgressDialogDelete(
	const gchar *label, gfloat progress_value, GtkWidget *toplevel,
	gboolean force_remap,
	edv_delete_method delete_method
)
{
	guint8	**start_icon_data[3],
		**icon_data[6],
		**end_icon_data[3];

	/* Already mapped? */
	if(ProgressDialogIsQuery())
	{
	    /* Check if the progress dialog needs to be unmapped and
	     * remapped again.
	     */
	    if(force_remap)
	    {
		ProgressDialogBreakQuery(FALSE);
	    }
	    else
	    {
		/* Already mapped and does not need unmapping, so just
		 * update the progress message.
		 */
		ProgressDialogUpdate(
		    NULL, label, NULL, NULL,
		    progress_value, EDV_DEF_PROGRESS_BAR_TICKS, TRUE
		);
		return;
	    }
	}

	ProgressDialogSetTransientFor(toplevel);

	if(delete_method == EDV_DELETE_METHOD_PURGE)
	{
	    start_icon_data[0] = (guint8 **)pdi_folderfile_32x32_xpm;
	    start_icon_data[1] = (guint8 **)pdi_folder_32x32_xpm;
	    start_icon_data[2] = (guint8 **)pdi_folder_32x32_xpm;
	    icon_data[0] = (guint8 **)pdi_trash01_20x20_xpm;
	    icon_data[1] = (guint8 **)pdi_trash02_20x20_xpm;
	    icon_data[2] = (guint8 **)pdi_trash03_20x20_xpm;
	    icon_data[3] = (guint8 **)pdi_trash04_20x20_xpm;
	    icon_data[4] = (guint8 **)pdi_trash05_20x20_xpm;
	    icon_data[5] = (guint8 **)pdi_trash06_20x20_xpm;
	    end_icon_data[0] = (guint8 **)NULL;
	    end_icon_data[1] = (guint8 **)NULL;
	    end_icon_data[2] = (guint8 **)NULL;
	}
	else
	{
	    start_icon_data[0] = (guint8 **)pdi_folderfile_32x32_xpm;
	    start_icon_data[1] = (guint8 **)pdi_folder_32x32_xpm;
	    start_icon_data[2] = (guint8 **)pdi_folder_32x32_xpm;
	    icon_data[0] = (guint8 **)pdi_file01_20x20_xpm;
	    icon_data[1] = (guint8 **)pdi_file02_20x20_xpm;
	    icon_data[2] = (guint8 **)pdi_file03_20x20_xpm;
	    icon_data[3] = (guint8 **)pdi_file04_20x20_xpm;
	    icon_data[4] = (guint8 **)pdi_file05_20x20_xpm;
	    icon_data[5] = (guint8 **)pdi_file06_20x20_xpm;
	    end_icon_data[0] = (guint8 **)icon_trash_32x32_xpm;
	    end_icon_data[1] = (guint8 **)icon_trash_32x32_xpm;
	    end_icon_data[2] = (guint8 **)icon_trash_32x32_xpm;
	}

	ProgressDialogMapAnimation(
#if defined(PROG_LANGUAGE_SPANISH)
	    "Borrar",
	    label,
	    "Parada",
#elif defined(PROG_LANGUAGE_FRENCH)
	    "Effacer",
	    label,
	    "Arrêt",
#elif defined(PROG_LANGUAGE_GERMAN)
	    "Löschen",
	    label,
	    "Halt",
#elif defined(PROG_LANGUAGE_ITALIAN)
	    "Cancellare",
	    label,
	    "Fermata",
#elif defined(PROG_LANGUAGE_DUTCH)
	    "Schrappen",
	    label,
	    "Einde",
#elif defined(PROG_LANGUAGE_PORTUGUESE)
	    "Anular",
	    label,
	    "Parada",
#elif defined(PROG_LANGUAGE_NORWEGIAN)
	    "Stryking",
	    label,
	    "Stans",
#else
	    "Deleting",
	    label,
	    "Stop",
#endif
	    start_icon_data, 3,
	    icon_data, 6,
	    end_icon_data, 3,
	    500l,
	    10000
	);
	ProgressDialogUpdate(
	    NULL, NULL, NULL, NULL,
	    progress_value, EDV_DEF_PROGRESS_BAR_TICKS, TRUE
	);

	/* Flush output so dialog gets mapped and we catch the beginning
	 * of the operation (some WM need this)
	 */
	gdk_flush();
}

/*
 *      Maps the progress dialog as needed in animation mode for purging.
 */
static void EDVRecBinFOPMapProgressDialogPurge(
	const gchar *label, gfloat progress_value, GtkWidget *toplevel,
	gboolean force_remap
)
{
	guint8	**start_icon_data[3],
		**icon_data[6],
		**end_icon_data[3];

	/* Already mapped? */
	if(ProgressDialogIsQuery())
	{
	    /* Check if the progress dialog needs to be unmapped and
	     * remapped again
	     */
	    if(force_remap)
	    {
		ProgressDialogBreakQuery(FALSE);
	    }
	    else
	    {
		/* Already mapped and does not need unmapping, so just
		 * update the progress message
		 */
		ProgressDialogUpdate(
		    NULL, label, NULL, NULL,
		    progress_value, EDV_DEF_PROGRESS_BAR_TICKS, TRUE
		);
		return;
	    }
	}

	ProgressDialogSetTransientFor(toplevel);

	start_icon_data[0] = (guint8 **)icon_trash_32x32_xpm;
	start_icon_data[1] = (guint8 **)icon_trash_empty_32x32_xpm;
	start_icon_data[2] = (guint8 **)icon_trash_empty_32x32_xpm;
	icon_data[0] = (guint8 **)pdi_trash01_20x20_xpm;
	icon_data[1] = (guint8 **)pdi_trash02_20x20_xpm;
	icon_data[2] = (guint8 **)pdi_trash03_20x20_xpm;
	icon_data[3] = (guint8 **)pdi_trash04_20x20_xpm;
	icon_data[4] = (guint8 **)pdi_trash05_20x20_xpm;
	icon_data[5] = (guint8 **)pdi_trash06_20x20_xpm;
	end_icon_data[0] = (guint8 **)NULL;
	end_icon_data[1] = (guint8 **)NULL;
	end_icon_data[2] = (guint8 **)NULL;

	ProgressDialogMapAnimation(
#if defined(PROG_LANGUAGE_SPANISH)
	    "Purgando",
	    label,
	    "Parada",
#elif defined(PROG_LANGUAGE_FRENCH)
	    "Purger",
	    label,
	    "Arrêt",
#elif defined(PROG_LANGUAGE_GERMAN)
	    "Reinigung",
	    label,
	    "Halt",
#elif defined(PROG_LANGUAGE_ITALIAN)
	    "Prosciogliere",
	    label,
	    "Fermata",
#elif defined(PROG_LANGUAGE_DUTCH)
	    "Reinigen",
	    label,
	    "Einde",
#elif defined(PROG_LANGUAGE_PORTUGUESE)
	    "Purging",
	    label,
	    "Parada",
#elif defined(PROG_LANGUAGE_NORWEGIAN)
	    "Rensing",
	    label,
	    "Stans",
#else
	    "Purging",
	    label,
	    "Stop",
#endif
	    start_icon_data, 3,
	    icon_data, 6,
	    end_icon_data, 3,
	    500l,
	    10000
	);
	ProgressDialogUpdate(
	    NULL, NULL, NULL, NULL,
	    progress_value, EDV_DEF_PROGRESS_BAR_TICKS, TRUE
	);

	/* Flush output so dialog gets mapped and we catch the beginning
	 * of the operation (some WM need this)
	 */
	gdk_flush();
}


/*
 *	Recycle recover or delete progress callback.
 */
static gint EDVRecBinFOPRecycleProgressPercentCB(
	gpointer client_data, gfloat percent
)
{
	gint status = 0;


	if(ProgressDialogIsQuery())
	{
	    ProgressDialogUpdate(
		NULL, NULL, NULL, NULL,
		percent,
		EDV_DEF_PROGRESS_BAR_TICKS, TRUE
	    );
	    if(ProgressDialogStopCount() > 0)
		status = -1;
	}

	return(status);
}

static gint EDVRecBinFOPRecycleProgressCB(
	gpointer client_data, gulong pos, gulong total
)
{
	return(EDVRecBinFOPRecycleProgressPercentCB(
	    client_data,
	    ((total > 0) && (pos >= 0)) ?
		((gfloat)pos / (gfloat)total) : -1.0
	));
}


/*
 *	Recovers the recycled object specified by the given index and
 *	recreates it as a disk object. On success *new_obj_rtn will be
 *	a dynamically allocated string containing the newly created
 *	disk object.
 *
 *	The calling function must delete the returned string specified
 *	by *new_obj_rtn.
 */
gint EDVRecBinFOPRecover(
	edv_core_struct *core_ptr,
	guint index,                    /* Recycled object to recover */
	const gchar *tar_obj,           /* Can be NULL */
	gchar **new_obj_rtn,            /* Dynamically allocated */
	GtkWidget *toplevel,            /* Can be NULL */
	gboolean show_progress, gboolean interactive,
	gboolean *yes_to_all
)
{
	static gint status;
	const gchar *recycled_index_file;
	gchar *ltar_obj = NULL;
	gulong time_start = (gulong)time(NULL);
	const cfg_item_struct *cfg_list;
	edv_recbin_object_struct *obj = NULL;
	struct stat lstat_buf;


	/* Reset last error message */
	last_error = NULL;


	if((core_ptr == NULL) || (index == 0))
	{
	    last_error = "Bad input value";
	    return(-1);
	}

#define DO_FREE_LOCALS	{	\
 g_free(ltar_obj);		\
 ltar_obj = NULL;		\
				\
 EDVRecBinObjectDelete(obj);	\
 obj = NULL;			\
}

	cfg_list = core_ptr->cfg_list;

	/* Get path to recycled objects index file */
	recycled_index_file = EDV_GET_S(
	    EDV_CFG_PARM_FILE_RECYCLED_INDEX
	);
	if(recycled_index_file == NULL)
	{
	    DO_FREE_LOCALS
	    last_error = "Unable to get recycled objects index file";
	    return(-1);
	}

	/* Get statistics of recycled object */
	obj = EDVRecBinObjectStat(recycled_index_file, index);
	if(obj == NULL)
	{
	    DO_FREE_LOCALS
	    last_error = "Unable to get statistics of recycled object";
	    return(-1);
	}


	/* Formulate full target path */
	if(tar_obj == NULL)
	{
	    /* Recover to target path not given so generate one from
	     * the original path
	     */
	    if((obj->original_path == NULL) || (obj->name == NULL))
	    {
		DO_FREE_LOCALS
		last_error =
"Unable to get original path of recycled object";
		return(-1);
	    }

	    ltar_obj = STRDUP(PrefixPaths(
		(const char *)obj->original_path, (const char *)obj->name
	    ));
	}
	else
	{
	    /* Given recovery path is not NULL, it is assumed to be a
	     * directory so postfix the recycled object's name to it
	     */
	    ltar_obj = STRDUP(PrefixPaths(
		(const char *)tar_obj, (const char *)obj->name
	    ));
	}
	if(ltar_obj == NULL)
	{
	    DO_FREE_LOCALS
	    last_error =
"Unable to generate recovery path of recycled object";
	    return(-1);
	}


	/* Target object must not exist */
	if(!lstat((const char *)ltar_obj, &lstat_buf))
	{
	    DO_FREE_LOCALS
	    last_error =
"An object with the same name already exists at the specified recovery location";
	    return(-1);
	}


	if(show_progress)
	{
	    gchar *p1 = EDVCopyShortenPath(
		obj->name, EDV_DEF_PROGRESS_BAR_PATH_DISPLAY_MAX
	    );
	    gchar *p2 = EDVCopyShortenPath(
		ltar_obj, EDV_DEF_PROGRESS_BAR_PATH_DISPLAY_MAX
	    );
	    gchar *buf = g_strdup_printf(
#if defined(PROG_LANGUAGE_SPANISH)
"Convaleciente:\n\
\n\
    %s\n\
\n\
A:\n\
\n\
    %s\n"
#elif defined(PROG_LANGUAGE_FRENCH)
"Retrouver:\n\
\n\
    %s\n\
\n\
A:\n\
\n\
    %s\n"
#elif defined(PROG_LANGUAGE_GERMAN)
"Wiedererlangen:\n\
\n\
    %s\n\
\n\
Zu:\n\
\n\
    %s\n"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Ricuperare:\n\
\n\
    %s\n\
\n\
A:\n\
\n\
    %s\n"
#elif defined(PROG_LANGUAGE_DUTCH)
"Terugkrijgen:\n\
\n\
    %s\n\
\n\
Te:\n\
\n\
    %s\n"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Recuperar:\n\
\n\
    %s\n\
\n\
A:\n\
\n\
    %s\n"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Utvinning:\n\
\n\
    %s\n\
\n\
Til:\n\
\n\
    %s\n"
#else
"Recovering:\n\
\n\
    %s\n\
\n\
To:\n\
\n\
    %s\n"
#endif
		, p1, p2
	    );

	    EDVRecBinFOPMapProgressDialogRecover(
		buf, 0.0f, toplevel, FALSE
	    );

	    g_free(buf);
	    g_free(p1);
	    g_free(p2);
	}


	/* Do recovery */
	status = EDVRecBinDiskObjectRecover(
	    recycled_index_file,
	    index,
	    ltar_obj,
	    show_progress ? EDVRecBinFOPRecycleProgressCB : NULL,
	    core_ptr
	);
	if(status == -4)
	{
	    /* User aborted recovery */
	}
	else if(status)
	{
	    /* Recovery failed */
	    last_error = EDVRecBinFIOGetError();
	}
	else
	{
	    /* Remove recycled object entry from the recycled objects
	     * index file.
	     */
	    EDVRecBinIndexRemove(
		recycled_index_file, index
	    );

	    /* Update newly recovered object path */
	    if(new_obj_rtn != NULL)
	    {
		g_free(*new_obj_rtn);
		*new_obj_rtn = ltar_obj;
		ltar_obj = NULL;
	    }
	}

	/* Record history */
	EDVAppendHistory(
	    core_ptr,
	    EDV_HISTORY_RECYCLED_OBJECT_RECOVER,
	    time_start, (gulong)time(NULL),
	    status,
	    obj->name,		/* Source name */
	    (new_obj_rtn != NULL) ? *new_obj_rtn : ltar_obj,
	    last_error          /* Use last_error as comment if not NULL */
	);

	DO_FREE_LOCALS
	return(status);
#undef DO_FREE_LOCALS
}


/*
 *	Called by EDVRecBinFOPDelete() to delete the object specified by
 *	path to the recycled objects directory.
 *
 *	Returns the index of the recycled object or 0 if no recycled
 *	object index is available, this can occure if the delete
 *	method is set to purge.
 */
static guint EDVRecBinFOPDeleteIterate(
	edv_core_struct *core_ptr,
	const gchar *recycled_index_file,
	const gchar *path,
	guint **index_rtn, gint *total_index_rtns,
	GtkWidget *toplevel,            /* Can be NULL */
	gboolean show_progress, gboolean interactive,
	gboolean *yes_to_all, gint *status,
	edv_delete_method delete_method
)
{
	gboolean no_delete;
	guint index;
	gint strc;
	gchar **strv;
	struct stat lstat_buf, stat_buf;


	/* Error or user aborted from prior call? */
	if(*status)
	    return(0);

	/* Get local stats of the given disk object to be deleted */
	if(lstat((const char *)path, &lstat_buf))
	{
	    *status = -2;
	    last_error =
"Unable to get statistics of the object to be deleted";
	    return(0);
	}

#if 0
/* Let system determine this */
	/* If effective user ID of this process does not own object
	 * then the object unable to be deleted
	 */
	if(!EDVCheckIsOwner(core_ptr, lstat_buf.st_uid))
	{
	    *status = -2;
	    last_error =
"Effective user id of this process does not own the object";
	    return(0);
	}
#endif

	/* Get destination statistics of this object and check if it
	 * may not be deleted
	 */
	if(stat((const char *)path, &stat_buf))
	    no_delete = EDVRecBinNoDeleteCheck(
		core_ptr, toplevel, interactive, yes_to_all, path, NULL
	    );
	else
	    no_delete = EDVRecBinNoDeleteCheck(
		core_ptr, toplevel, interactive, yes_to_all, path, &stat_buf
	    );
	if(no_delete)
	{
	    *status = -2;
	    /* Set last error only if not interactive since calling
	     * EDVRecBinNoDeleteCheck() would have already displayed 
	     * appropriate error message
	     */
	    if(!interactive)
		last_error =
"Object is part of the function of this process or system and may not be deleted";
	    return(0);
	}


	/* If the given object to be deleted is locally a directory,
	 * then get contents of the directory and delete recursivly
	 */
	if(S_ISDIR(lstat_buf.st_mode))
	    strv = GetDirEntNames2(path, &strc);
	else
	    strv = NULL;
	if(strv != NULL)
	{
	    gint i;
	    gchar *child_path, *name;

	    /* Sort strings */
	    strv = StringQSort(strv, strc);
	    if(strv == NULL)
	    {
		*status = -2;
		last_error = "Unable to sort directory entries";
		return(0);
	    }

	    /* Iterate through directory entry */
	    child_path = NULL;
	    for(i = 0; i < strc; i++)
	    {
#define FREE_AND_CONTINUE	{	\
 g_free(child_path);			\
 child_path = NULL;			\
 g_free(strv[i]);			\
 strv[i] = NULL;			\
 continue;				\
}
		name = strv[i];
		if(name == NULL)
		    FREE_AND_CONTINUE

		/* Error encountered or user abort? */
		if(*status)
		    FREE_AND_CONTINUE

		/* Skip special dir notations */
		if(!strcmp((const char *)name, "..") ||
		   !strcmp((const char *)name, ".")
		)
		    FREE_AND_CONTINUE

		/* Formulate full path to child */
		child_path = STRDUP(PrefixPaths(
		    (const char *)path, (const char *)name
		));
		if(child_path == NULL)
		    FREE_AND_CONTINUE


		/* Delete this object and all its children */
		index = EDVRecBinFOPDeleteIterate(
		    core_ptr, recycled_index_file, child_path,
		    index_rtn, total_index_rtns,
		    toplevel, show_progress, interactive,
		    yes_to_all, status, delete_method
		);
		/* Above return index is ignored */

		FREE_AND_CONTINUE
#undef FREE_AND_CONTINUE
	    }

	    /* Delete pointer array */
	    g_free(strv);
	}

	/* Error or user aborted while deleting any child objects? */
	if(*status)
	    return(0);

	/* Update progress to display the path of this object that is
	 * about to be deleted?
	 */
	if(show_progress)
	{
	    gchar *p1 = EDVCopyShortenPath(
		path, EDV_DEF_PROGRESS_BAR_PATH_DISPLAY_MAX
	    );
	    gchar *buf = g_strdup_printf(
#if defined(PROG_LANGUAGE_SPANISH)
"Borrar:\n\
\n\
    %s\n"
#elif defined(PROG_LANGUAGE_FRENCH)
"Effacer:\n\
\n\
    %s\n"
#elif defined(PROG_LANGUAGE_GERMAN)
"Löschen:\n\
\n\
    %s\n"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Cancellare:\n\
\n\
    %s\n"
#elif defined(PROG_LANGUAGE_DUTCH)
"Schrappen:\n\
\n\
    %s\n"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Anular:\n\
\n\
    %s\n"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Stryking:\n\
\n\
    %s\n"
#else
"Deleting:\n\
\n\
    %s\n"
#endif
		, p1
	    );

	    EDVRecBinFOPMapProgressDialogDelete(
		buf, 0.0f, toplevel, FALSE, delete_method
	    );

	    g_free(buf);
	    g_free(p1);

	    if(ProgressDialogStopCount() > 0)
	    {
		*status = -4;
		return(0);
	    }
	}

	/* At this point, check if we need to delete the object by
	 * moving it to the recycled objects directory or permanently
	 * purging the object
	 */
	if(delete_method == EDV_DELETE_METHOD_PURGE)
	{
	    /* Permanently purge this object */

	    /* Get system permissions of object */
	    mode_t m = lstat_buf.st_mode;

	    /* Delete by object type */
	    /* Directory? */
	    if(S_ISDIR(m))
	    {
		if(rmdir(path))
		{
		    switch(errno)
		    {
		      case ENOTEMPTY:
			last_error = "Directory is not empty";
			break;
		      default:
			last_error = "Unable to remove directory";
			break;
		    }
		    *status = -1;
		}
	    }
	    /* All else, assume it can be unlink'ed */
	    else
	    {
		if(unlink(path))
		{
		    switch(errno)
		    {
		      case EFAULT:
			last_error = "Segmentation fault";
			break;
		      case EACCES:
			last_error =
 "Object's permissions or location does not permit it to be deleted";
			break;
		      case EPERM:
			last_error =
 "You do not own that object in the tempory files location";
			break;
		      case ENAMETOOLONG:
			last_error = "Object's path name is too long";
			break;
		      case ENOENT:
			last_error =
 "A compoent of the object's path does not exist";
			break;
		      case ENOTDIR:
			last_error =
 "A compoent of the object's path is not a directory";
			break;
		      case EISDIR:
			last_error =
 "Object's path reffers to a directory object";
			break;
		      case ENOMEM:
			last_error = "System is out of memory";
			break;
		      case EROFS:
			last_error =
 "The object exists on a read-only filesystem";
			break;
		      case ELOOP:
			last_error =
 "Too many symbolic links encountered in the object's path";
			break;
		      case EIO:
			last_error = "An input/output error occured";
			break;
		      default:
			last_error = "Unable to delete file";
			break;
		    }
		    *status = -1;
	        }
	    }

	    index = 0;
	    if(*status == 0)
	    {
		    /* Append this index to the index array return */
		    if((index_rtn != NULL) && (total_index_rtns != NULL))
		    {
			gint i = MAX(*total_index_rtns, 0);
			*total_index_rtns = i + 1;

			*index_rtn = (guint *)g_realloc(
			    *index_rtn,
			    *total_index_rtns * sizeof(guint)
			);
			if(*index_rtn == NULL)
			{
			    *total_index_rtns = 0;
			}
			else
			{
			    (*index_rtn)[i] = index;
			}
		    }
	    }
	}
	else
	{
	    /* All else, transfer this object to the recycled objects
	     * directory
	     */
	    gchar *parent_path;
	    gulong cur_time = time(NULL);
	    mode_t m = lstat_buf.st_mode;

	    /* Create a new tempory recycled object structure */
	    edv_recbin_object_struct *obj = EDVRecBinObjectNew();
	    if(obj == NULL)
		return(0);

	    /* Set values from object to new recycled object structure */
	    g_free(obj->name);
	    obj->name = STRDUP(EDVGetPathName(path));

	    parent_path = g_dirname(path);
	    if(parent_path == NULL)
		parent_path = STRDUP("/");
	    g_free(obj->original_path);
	    obj->original_path = STRDUP(parent_path);

	    obj->date_deleted = cur_time;

	    obj->type = EDVObjectGetTypeFromStatMode(m);

	    if(S_ISLNK(m))
	    {
		gchar linked_to[PATH_MAX + NAME_MAX + 1];
		gint bytes_read = readlink(
		    path,
		    linked_to,
		    PATH_MAX + NAME_MAX
		);
		if(bytes_read >= 0)
		{
		    if(bytes_read <= (PATH_MAX + NAME_MAX))
			linked_to[bytes_read] = '\0';
		    else
			linked_to[PATH_MAX + NAME_MAX] = '\0';

		    g_free(obj->linked_to);
		    obj->linked_to = STRDUP(linked_to);
		}
	    }

	    obj->permissions = EDVObjectGetPermissionsFromStatMode(m);

	    obj->access_time = lstat_buf.st_atime;
	    obj->modify_time = lstat_buf.st_mtime;
	    obj->change_time = lstat_buf.st_ctime;

	    obj->owner_id = lstat_buf.st_uid;
	    obj->group_id = lstat_buf.st_gid;

	    obj->size = lstat_buf.st_size;

	    /* Add object to recycled index file */
	    index = EDVRecBinIndexAdd(recycled_index_file, obj);
	    if(index != 0)
	    {
		/* Remove the actual disk object and place it into the
		 * recycled objects directory
		 */
		*status = EDVRecBinDiskObjectDelete(
		    recycled_index_file,
		    index,
		    path,
		    show_progress ? EDVRecBinFOPRecycleProgressCB : NULL,
		    core_ptr
		);
		/* User aborted operation? */
		if(*status == -4)
		{
		    EDVRecBinIndexRemove(recycled_index_file, index);
		    index = 0;
		}
		/* Other error occured? */
		else if(*status)
		{
		    EDVRecBinIndexRemove(recycled_index_file, index);
		    index = 0;
		    last_error = EDVRecBinFIOGetError();
		}
		/* Successfully deleted object */
		else
		{
		    /* Append this index to the index array return */
		    if((index_rtn != NULL) && (total_index_rtns != NULL))
		    {
			gint i = MAX(*total_index_rtns, 0);
			*total_index_rtns = i + 1;

			*index_rtn = (guint *)g_realloc(
			    *index_rtn,
			    *total_index_rtns * sizeof(guint)
			);
			if(*index_rtn == NULL)
			{
			    *total_index_rtns = 0;
			}
			else
			{
			    (*index_rtn)[i] = index;
			}
		    }
		}
	    }
	    else
	    {
		last_error = "Unable to generate new recycled object index";
		*status = -1;
	    }

	    /* Delete tempory recycled object structure, it is no
	     * longer needed
	     */
	    EDVRecBinObjectDelete(obj);

	    g_free(parent_path);
	}

	return(index);
}

/*
 *	Deletes the disk object specified by the full path and places it
 *	into the recycle bin. On success *index_rtn will be set to a
 *	dynamically allocated string containing an array of recycled
 *	object indexes.
 *
 *	The calling function must delete the returned array in
 *	index_rtn specified by length total_index_rtns.
 */
gint EDVRecBinFOPDelete(
	edv_core_struct *core_ptr,
	const gchar *path,              /* Disk object to delete */
	guint **index_rtn, gint *total_index_rtns,
	GtkWidget *toplevel,            /* Can be NULL */
	gboolean show_progress, gboolean interactive,
	gboolean *yes_to_all
)
{
	static gint status;
	guint index;
	const gchar *recycled_index_file;
	edv_delete_method delete_method;
	gulong time_start = (gulong)time(NULL);
	gchar *lsrc_obj = NULL;
	gchar num_str[80];
	const cfg_item_struct *cfg_list;


	/* Reset last error message */
	last_error = NULL;


	/* Reset returns */
	if(index_rtn != NULL)
	    *index_rtn = NULL;
	if(total_index_rtns != NULL)
	    *total_index_rtns = 0;

	if((core_ptr == NULL) || STRISEMPTY(path))
	{
	    last_error = "Bad input value";
	    return(-1);
	}

#define DO_FREE_LOCALS	{	\
 g_free(lsrc_obj);		\
 lsrc_obj = NULL;		\
}

	cfg_list = core_ptr->cfg_list;

	/* Get path to recycled objects index file */
	recycled_index_file = EDV_GET_S(
	    EDV_CFG_PARM_FILE_RECYCLED_INDEX
	);
	if(recycled_index_file == NULL)
	{
	    DO_FREE_LOCALS
	    last_error = "Unable to get recycled objects index file";
	    return(-1);
	}

	/* Get delete method, so we know if we should just move the
	 * object to the recycled objects directory or permanently
	 * purge it
	 */
	delete_method = (edv_delete_method)EDV_GET_I(
	    EDV_CFG_PARM_DELETE_METHOD
	);


	/* Get copy of path of object to be deleted */
	lsrc_obj = STRDUP(path);
	if(lsrc_obj == NULL)
	{
	    DO_FREE_LOCALS
	    last_error = "Memory allocation error";
	    return(-3);
	}


	status = 0;

	/* Do delete iteration(s) */
	index = EDVRecBinFOPDeleteIterate(
	    core_ptr, recycled_index_file, lsrc_obj,
	    index_rtn, total_index_rtns,
	    toplevel, show_progress, interactive,
	    yes_to_all, &status, delete_method
	);

	/* Record history */
	g_snprintf(num_str, sizeof(num_str), "%i", index);
	EDVAppendHistory(
	    core_ptr,
	    EDV_HISTORY_DISK_OBJECT_DELETE,
	    time_start, (gulong)time(NULL),
	    status,
	    lsrc_obj,	/* Source full path */
	    num_str,	/* Target recycled object as index string */
	    last_error	/* Use last_error as comment if not NULL */
	);

	DO_FREE_LOCALS
	return(status);
#undef DO_FREE_LOCALS
}


/*
 *	Purges the given recycled object specified by index from the
 *	recycled objects directory.
 *
 *	If and only if the progress_value is non negative, then the 
 *	progress dialog will be updated with an internally calculated
 *	value, otherwise the given progress_value is passed to the
 *	progress dialog.
 */
gint EDVRecBinFOPPurge(
	edv_core_struct *core_ptr,
	guint index,
	GtkWidget *toplevel,		/* Can be NULL */
	gfloat progress_value,		/* Can be -1.0 */
	gboolean show_progress, gboolean interactive,
	gboolean *yes_to_all
)
{
	static gint status;
	const gchar *recycled_index_file;
	gulong time_start = (gulong)time(NULL);
	edv_recbin_object_struct *obj = NULL;
	gchar num_str[80];
	const cfg_item_struct *cfg_list;

	/* Reset last error message */
	last_error = NULL;

	if((core_ptr == NULL) || (index == 0))
	{
	    last_error = "Bad input value";
	    return(-1);
	}

#define DO_FREE_LOCALS	{	\
 EDVRecBinObjectDelete(obj);	\
 obj = NULL;			\
}

	cfg_list = core_ptr->cfg_list;

	/* Get path to recycled objects index file */
	recycled_index_file = EDV_GET_S(
	    EDV_CFG_PARM_FILE_RECYCLED_INDEX
	);
	if(recycled_index_file == NULL)
	{
	    DO_FREE_LOCALS
	    last_error = "Unable to get recycled objects index file";
	    return(-1);
	}

	/* Get statistics of recycled object */
	obj = EDVRecBinObjectStat(recycled_index_file, index);


	if(show_progress)
	{
	    gchar *buf;

	    if((obj != NULL) ? (obj->name != NULL) : FALSE)
	    {
		gchar *p1 = EDVCopyShortenPath(
		    obj->name, EDV_DEF_PROGRESS_BAR_PATH_DISPLAY_MAX
		);
		buf = g_strdup_printf(
#if defined(PROG_LANGUAGE_SPANISH)
"Purgando:\n\
\n\
    %s\n"
#elif defined(PROG_LANGUAGE_FRENCH)
"Purger:\n\
\n\
    %s\n"
#elif defined(PROG_LANGUAGE_GERMAN)
"Reinigung:\n\
\n\
    %s\n"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Prosciogliere:\n\
\n\
    %s\n"
#elif defined(PROG_LANGUAGE_DUTCH)
"Reinigen:\n\
\n\
    %s\n"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Descartar:\n\
\n\
    %s\n"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Rensing:\n\
\n\
    %s\n"
#else
"Purging:\n\
\n\
    %s\n"
#endif
		    , p1
		);
		g_free(p1);
	    }
	    else
	    {
		buf = g_strdup_printf(
"Purging:\n\
\n\
    #%i\n",
		    index
		);
	    }

	    EDVRecBinFOPMapProgressDialogPurge(
		buf, MAX(progress_value, 0.0f), toplevel, FALSE
	    );

	    g_free(buf);

	    /* If the given progress value is not negative then explicitly
	     * call the progress callback with values simulating the given
	     * progress value.
	     */
	    if(progress_value >= 0.0)
	    {
		if(EDVRecBinFOPRecycleProgressPercentCB(
		    core_ptr, progress_value
		))
		{
		    DO_FREE_LOCALS
		    status = -4;
		    return(status);
		}
	    }
	}


	/* Remove recycled object from the recycled objects directory */
	status = EDVRecBinDiskObjectPurge(
	    recycled_index_file,
	    index,
	    ((progress_value < 0.0f) && show_progress) ?
		EDVRecBinFOPRecycleProgressCB : NULL,
 	    core_ptr
	);
	if(status)
	{
	    last_error = EDVRecBinFIOGetError();
	}
	else
	{
	    /* Remove recycled object entry from the recycled objects
	     * index file
	     */
	    EDVRecBinIndexRemove(
		recycled_index_file, index
	    );
	}


	/* Record history */
	g_snprintf(num_str, sizeof(num_str), "%i", index);
	EDVAppendHistory(
	    core_ptr,
	    EDV_HISTORY_RECYCLED_OBJECT_PURGE,
	    time_start, (gulong)time(NULL),
	    status,
	    num_str,	/* Source index string */
	    NULL,	/* No target since purging into nothingness */
	    last_error	/* Use last_error as comment if not NULL */
	);


	DO_FREE_LOCALS
	return(status);
#undef DO_FREE_LOCALS
}


/*
 *      Purges all recycled objects from the recycled objects directory
 *	including the recycled objects index file.
 *
 *      If and only if the progress_value is non negative, then the
 *      progress dialog will be updated with an internally calculated
 *      value, otherwise the given progress_value is passed to the
 *      progress dialog.
 */
gint EDVRecBinFOPPurgeAll(
	edv_core_struct *core_ptr,
	GtkWidget *toplevel,            /* Can be NULL */
	gboolean show_progress, gboolean interactive,
	gboolean *yes_to_all
)
{
	static gint status;
	const gchar *recycled_index_file;
	gulong time_start = (gulong)time(NULL);
	const cfg_item_struct *cfg_list;


	/* Reset last error message */
	last_error = NULL;

	if(core_ptr == NULL)
	{
	    last_error = "Bad input value";
	    return(-1);
	}

#define DO_FREE_LOCALS  \
{ \
}

	cfg_list = core_ptr->cfg_list;

	/* Get path to recycled objects index file */
	recycled_index_file = EDV_GET_S(
	    EDV_CFG_PARM_FILE_RECYCLED_INDEX
	);
	if(recycled_index_file == NULL)
	{
	    DO_FREE_LOCALS
	    last_error = "Unable to get recycled objects index file";
	    return(-1);
	}


	if(show_progress)
	{
#if defined(PROG_LANGUAGE_SPANISH)
	    gchar *buf = STRDUP(
"Purgando todo contenido del Cajón de la Recirculación...\n"
	    );
#elif defined(PROG_LANGUAGE_FRENCH)
	    gchar *buf = STRDUP(
"Purger tous contenus du Recycle l'Huche...\n"
	    );
#elif defined(PROG_LANGUAGE_GERMAN)
	    gchar *buf = STRDUP(
"Reinigung alles Inhalts vom Verwertet Behälter wieder...\n"
	    );
#elif defined(PROG_LANGUAGE_ITALIAN)
	    gchar *buf = STRDUP(
"Prosciogliere tutto il contenuto dal Contenitore per\n\
la raccolta differenziata...\n"
	    );
#elif defined(PROG_LANGUAGE_DUTCH)
	    gchar *buf = STRDUP(
"Reinigen van alle inhoud van het Recyclt Bak...\n"
	    );
#elif defined(PROG_LANGUAGE_PORTUGUESE)
	    gchar *buf = STRDUP(
"O Purging todo conteúdo da Caixa de Recycle...\n"
	    );
#elif defined(PROG_LANGUAGE_NORWEGIAN)
	    gchar *buf = STRDUP(
"Rensing av all innhold fra Recycle Bin...\n"
	    );
#else
	    gchar *buf = STRDUP(
"Purging all contents from the Recycle Bin...\n"
	    );
#endif
	    EDVRecBinFOPMapProgressDialogPurge(
		buf, 0.0f, toplevel, FALSE
	    );

	    g_free(buf);
	}


	/* Purge all objects found in the recycled objects directory,
	 * including the recycled objects index file.
	 */
	status = EDVRecBinDiskObjectPurgeAll(
	    recycled_index_file, 
	    show_progress ? EDVRecBinFOPRecycleProgressCB : NULL,
	    core_ptr
	);
	if(status)
	    last_error = EDVRecBinFIOGetError();


	/* Record history */
	EDVAppendHistory(
	    core_ptr,
	    EDV_HISTORY_RECYCLED_OBJECT_PURGE_ALL,
	    time_start, (gulong)time(NULL),
	    status,
	    NULL,	/* No source index string to imply all */
	    NULL,	/* No target since purging into nothingness */
	    last_error	/* Use last_error as comment if not NULL */
	);

	DO_FREE_LOCALS
	return(status);
#undef DO_FREE_LOCALS
}
