#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
#include <errno.h>
#include <unistd.h>
#include <gtk/gtk.h>

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

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

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

#include "images/icon_trash_32x32.xpm"


static gchar *last_error;

/*
 *	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
 */

static void EDVRecBinSyncIndexFile(
	edv_core_struct *core_ptr,
	const gchar *recbin_idx_file,
	const gchar *recbin_dir,
	GtkWidget *toplevel,
	gboolean show_progress, gboolean interactive,
	gboolean *yes_to_all, gint *status,
	void (*status_message_cb)(gpointer, const gchar *, gpointer),
	void (*status_progress_cb)(gpointer, gfloat, gpointer),
	gpointer client_data
);
static void EDVRecBinSyncObjects(
	edv_core_struct *core_ptr,
	const gchar *recbin_idx_file,
	const gchar *recbin_dir,
	GtkWidget *toplevel,
	gboolean show_progress, gboolean interactive,
	gboolean *yes_to_all, gint *status,
	void (*status_message_cb)(gpointer, const gchar *, gpointer),
	void (*status_progress_cb)(gpointer, gfloat, gpointer),
	gpointer client_data
);

gint EDVRecBinSync(
	edv_core_struct *core_ptr,
	GtkWidget *toplevel,
	gboolean show_progress, gboolean interactive,
	gboolean *yes_to_all,
	void (*status_message_cb)(gpointer, const gchar *, gpointer),
	void (*status_progress_cb)(gpointer, gfloat, gpointer),
	gpointer client_data
);


#define STATUS_MESSAGE(s)	{		\
 if(status_message_cb != NULL)			\
  status_message_cb(NULL, (s), client_data);	\
}
#define STATUS_PROGRESS(f)	{		\
 if(status_progress_cb != NULL)			\
  status_progress_cb(NULL, (f), client_data);	\
}

#define MESSAGE_ERROR(t,s)	{	\
 if(interactive) {			\
  EDVPlaySoundError(core_ptr);		\
  EDVMessageError(			\
   (t), (s), NULL, toplevel		\
  );					\
} }

#define MESSAGE_WARNING(t,s)	{	\
 if(interactive) {			\
  EDVPlaySoundWarning(core_ptr);	\
  EDVMessageWarning(			\
   (t), (s), NULL, toplevel		\
  );					\
} }

#define MESSAGE(t,s)	{		\
 if(interactive) {			\
 EDVPlaySoundInfo(core_ptr);		\
 CDialogSetTransientFor(toplevel);	\
 CDialogGetResponseIconData(		\
  (t), (s), NULL,			\
  (guint8 **)icon_trash_32x32_xpm,	\
  CDIALOG_BTNFLAG_OK,			\
  CDIALOG_BTNFLAG_OK			\
 );					\
 CDialogSetTransientFor(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)


/*
 *	Scans the index file, fixes it as needed, and removes refering
 *	indices to non-existant recycled objects.
 *
 *	Inputs assumed valid.
 */
static void EDVRecBinSyncIndexFile(
	edv_core_struct *core_ptr,
	const gchar *recbin_idx_file,
	const gchar *recbin_dir,
	GtkWidget *toplevel,
	gboolean show_progress, gboolean interactive,
	gboolean *yes_to_all, gint *status,
	void (*status_message_cb)(gpointer, const gchar *, gpointer),
	void (*status_progress_cb)(gpointer, gfloat, gpointer),
	gpointer client_data
)
{
	guint index;
	gchar *recycled_obj_path;
	GList *missing_idx = NULL, *missing_name = NULL;
	edv_recbin_index_struct *rbi_ptr = EDVRecBinIndexOpen(
	    recbin_idx_file
	);
	const edv_recbin_object_struct *obj;
	struct stat lstat_buf;


	/* Unable to open recycle bin index file? */
	if(rbi_ptr == NULL)
	{
	    gchar *buf = g_strdup_printf(
#if defined(PROG_LANGUAGE_SPANISH)
"Incapaz de abrir el archivo del índice de cajón de recirculación:\n\
\n\
    %s\n"
#elif defined(PROG_LANGUAGE_FRENCH)
"Incapable d'ouvrir recycler le fichier d'index d'huche:\n\
\n\
    %s\n"
#elif defined(PROG_LANGUAGE_GERMAN)
"Unfähig offen, behälter index akte wiederzuverwerten:\n\
\n\
    %s\n"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Incapace per aprire il file di indice di contenitore per la raccolta differenziata:\n\
\n\
    %s\n"
#elif defined(PROG_LANGUAGE_DUTCH)
"Onbekwaam open bak index dossier te recyclen:\n\
\n\
    %s\n"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Incapaz de abrir recicla arquivo de índice de caixa:\n\
\n\
    %s\n"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Maktesløs åpne resirkulasjons beholder indeksarkiv:\n\
\n\
    %s\n"
#else
"Unable to open recycle bin index file:\n\
\n\
    %s\n"
#endif
		, recbin_idx_file
	    );
	    MESSAGE_ERROR(
"Recycle Bin Error",
buf
	    );
	    g_free(buf);
	    if(!(*status)) *status = -1;
	    return;
	}

	STATUS_MESSAGE(
"Reading recycle bin index file..."
	);
	STATUS_PROGRESS(-1.0f);

	/* Iterate through all recycled objects referenced in the
	 * recycle bin index file
	 */
	while(!EDVRecBinIndexNext(rbi_ptr))
	{
	    STATUS_PROGRESS(-1.0f);

	    index = rbi_ptr->index;
	    obj = rbi_ptr->obj;

	    /* Generate path to recycled object */
	    recycled_obj_path = g_strdup_printf(
		"%s%c%i",
		recbin_dir, G_DIR_SEPARATOR, index
	    );
	    /* Does not exist? */
	    if(lstat(recycled_obj_path, &lstat_buf))
	    {
		missing_idx = g_list_append(
		    missing_idx, (gpointer)index
		);
		missing_name = g_list_append(
		    missing_name, STRDUP(obj->name)
		);
	    }
	    /* Is not a regular file? */
	    else if(!S_ISREG(lstat_buf.st_mode))
	    {
		missing_idx = g_list_append(
		    missing_idx, (gpointer)index
		);
		missing_name = g_list_append(
		    missing_name, STRDUP(obj->name)
		);
	    }

	    g_free(recycled_obj_path);
	}

	/* Close recycle bin index file */
	EDVRecBinIndexClose(rbi_ptr);

 
	STATUS_MESSAGE(
"Handling missing objects..."
	);
	STATUS_PROGRESS(0.0f);

	/* At this point we now have a list of missing recycled objects
	 * reffered to by their index numbers, begin querying user for
	 * their removal
	 */
	if(missing_idx != NULL)
	{
	    gboolean got_cancel = FALSE;
	    gint response;
	    guint index;
	    const gchar *name;
	    GList	*glist_idx = missing_idx,
			*glist_name = missing_name;
	    gint i = 0, m = g_list_length(glist_idx);


	    /* Iterate through missing index list */
	    while(glist_idx != NULL)
	    {
		STATUS_PROGRESS((m > 0) ? (gfloat)i / (gfloat)m : 0.0f);

		index = (guint)glist_idx->data;
		name = (const gchar *)glist_name->data;

		/* Query user for removal of this index */
		if(interactive && !(*yes_to_all))
		{
		    gchar *buf = g_strdup_printf(
#if defined(PROG_LANGUAGE_SPANISH)
"Reffered objetivo reciclado de \"%s\" a por el índice:\n\
\n\
    #%i\n\
\n\
¿No exista, usted quiere quitar su referencia del archivo del índice?\n"
#elif defined(PROG_LANGUAGE_FRENCH)
"L'objet recyclé \"%s\" reffered à par l'index:\n\
\n\
    #%i\n\
\n\
Ne pas exister, voulez-vous enlever sa référence du fichier d'index?\n"
#elif defined(PROG_LANGUAGE_GERMAN)
"Wiederverwertet Objekt \"%s\" reffered zu durch Index:\n\
\n\
    #%i\n\
\n\
Existieren sie, sie wollen herausnehmen seine verweisung von der Index akte nicht?\n"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Il reffered di \"%s\" di oggetto riciclato a dall'indice:\n\
\n\
    #%i\n\
\n\
Non esiste, lei vuole togliere il suo riferimento dal file di indice?\n"
#elif defined(PROG_LANGUAGE_DUTCH)
"Gerecyclde voorwerp \"%s\" reffered te door index:\n\
\n\
    #%i\n\
\n\
Besta niet, u wil verwijderen zijne referentie van het index dossier?\n"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Reffered reciclado de \"%s\" de objeto a por índice:\n\
\n\
    #%i\n\
\n\
Não existe, quer retirar seua referência do arquivo de índice?\n"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Resirkuleredd objekt \"%s\" reffered til ved indeks:\n\
\n\
    #%i\n\
\n\
Finnes ikke, gjør De fjerner dets referanse fra indeksarkivet?\n"
#else
"Recycled object \"%s\" reffered to by index:\n\
\n\
    #%i\n\
\n\
Does not exist, do you want to remove its reference from the index file?\n"
#endif
			, name, index
		    );
		    EDVPlaySoundQuestion(core_ptr);
		    CDialogSetTransientFor(toplevel);
		    response = CDialogGetResponse(
#if defined(PROG_LANGUAGE_SPANISH)
"Perder Recicló se Opone",
buf,
"¿Mencionó un objeto en los objetos reciclados indexa el\n\
archivo no existe, usted quiere quitar la referencia al no\n\
objeto de existant de los objetos reciclados indexa el\n\
archivo?\n",
#elif defined(PROG_LANGUAGE_FRENCH)
"Manquer A Recyclé L'Objet",
buf,
"Un referenced d'objet dans le fichier d'index d'objets\n\
recyclé n'existe pas, voulez-vous enlever la référence à\n\
l'objet de non-existant du fichier d'index d'objets recyclé?\n",
#elif defined(PROG_LANGUAGE_GERMAN)
"Verpassen Hat Objekt Wiederverwertet",
buf,
"Ablegt ein objekt auf in den wiederverwerteten objekten akte,\n\
sie die verweisung zum nicht-existant objekt vom wiederverwerteten\n\
objekten index nicht hat verwiesen indiziert existiert wollen\n\
herausnehmen?\n",
#elif defined(PROG_LANGUAGE_ITALIAN)
"Mancare Ha Riciclato L'Oggetto",
buf,
"Un referenced di oggetto nel file di indice di oggetti riciclato\n\
non esiste, lei vuole togliere il riferimento all'oggetto di\n\
non-existant dal file di indice di oggetti riciclato?\n",
#elif defined(PROG_LANGUAGE_DUTCH)
"Missen Recyclde Voorwerp",
buf,
"In niet de gerecyclde voorwerpen indexeer dossier bestaat, een\n\
voorwerp verwees naar u wil verwijderen de referentie aan het\n\
niete-existant voorwerp van het gerecycld voorwerpen index\n\
dossier?\n",
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Ausente Que Recicla Objeto",
buf,
"Um referenced de objeto no arquivo reciclado de índice de objetos\n\
não existe, quer retirar a referência ao objeto de não-existant do\n\
arquivo reciclado de índice de objetos?\n",
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Mangling Resirkuleredd Objekt",
buf,
"En objektreferanse i de resirkuleredde objektene registrerer\n\
arkiv ikke finnes, gjør De fjerner referanseen til\n\
ikke--existantobjektet fra det resirkuleredde\n\
objektindeksarkiv?\n",
#else
"Missing Recycled Object",
buf,
"An object referenced in the recycled objects index file\n\
does not exist, do you want to remove the reference to\n\
the non-existant object from the recycled objects index\n\
file?\n",
#endif
			CDIALOG_ICON_WARNING,
			CDIALOG_BTNFLAG_YES |
			CDIALOG_BTNFLAG_YES_TO_ALL |
			CDIALOG_BTNFLAG_NO |
			CDIALOG_BTNFLAG_CANCEL |
			CDIALOG_BTNFLAG_HELP,
			CDIALOG_BTNFLAG_YES
		    );
		    CDialogSetTransientFor(NULL);
		    g_free(buf);
		}
		else
		{
		    response = CDIALOG_RESPONSE_YES_TO_ALL;
		}
		/* Handle response */
		switch(response)
		{
		  case CDIALOG_RESPONSE_YES_TO_ALL:
		    *yes_to_all = TRUE;
		  case CDIALOG_RESPONSE_YES:
		    /* Yes, remove the index from the index file */
		    EDVRecBinIndexRemove(recbin_idx_file, index);
		    break;
		  case CDIALOG_RESPONSE_NO:
		    /* No, do not remove the index from the index file */
		    break;
		  default:
		    /* Cancel */
		    if(!(*status)) *status = -4;
		    got_cancel = TRUE;
		    break;
		}
		if(got_cancel)
		    break;

		/* Go to next missing recycled object index */
		glist_idx = g_list_next(glist_idx);
		glist_name = g_list_next(glist_name);

		i++;
	    }

	    if(!(*status)) *status = -1;
	}

	STATUS_MESSAGE(NULL);
	STATUS_PROGRESS(0.0f);

	/* Delete list of index of missing recycled objects */
	g_list_free(missing_idx);
	g_list_foreach(missing_name, (GFunc)g_free, NULL);
	g_list_free(missing_name);
}


/*
 *	Scans the recycle bin directory and checks if any recycled
 *	objects are not listed in the index file, adds non-referenced
 *	recycled objects to the index file.
 *
 *      Inputs assumed valid.
 */
static void EDVRecBinSyncObjects(
	edv_core_struct *core_ptr,
	const gchar *recbin_idx_file,
	const gchar *recbin_dir,
	GtkWidget *toplevel,
	gboolean show_progress, gboolean interactive,
	gboolean *yes_to_all, gint *status,
	void (*status_message_cb)(gpointer, const gchar *, gpointer),
	void (*status_progress_cb)(gpointer, gfloat, gpointer),
	gpointer client_data
)
{
	gint i, strc;
	gchar **strv;
	const gchar	*obj_name,
			*idx_file_name = EDVGetPathName(recbin_idx_file);
	GList *idx_list = NULL;
	edv_recbin_index_struct *rbi_ptr = EDVRecBinIndexOpen(
	    recbin_idx_file
	);


	if(STRISEMPTY(idx_file_name))
	    idx_file_name = "";

	/* Able to open  recycle bin index file? */
	if(rbi_ptr != NULL)
	{
	    STATUS_MESSAGE(
"Reading recycle bin index file..."
	    );
	    STATUS_PROGRESS(-1.0f);

	    /* Get list of indices */
	    while(!EDVRecBinIndexNext(rbi_ptr))
	    {
		STATUS_PROGRESS(-1.0f);
		idx_list = g_list_append(
		    idx_list, (gpointer)rbi_ptr->index
		);
	    }

	    /* Close recycle bin index file */
	    EDVRecBinIndexClose(rbi_ptr);
	}

	STATUS_MESSAGE(
"Handling inert objects..."
	);
	STATUS_PROGRESS(0.0f);

	/* Get listing of every single objects in the recycled
	 * objects directory
	 */
	strv = GetDirEntNames2(recbin_dir, &strc);
	for(i = 0; i < strc; i++)
	{
	    STATUS_PROGRESS((strc > 0) ? (gfloat)i / (gfloat)strc : 0.0f);

	    obj_name = strv[i];
	    if(STRISEMPTY(obj_name))
		continue;

	    /* Skip certain names */
	    if(!strcmp(obj_name, ".") ||
	       !strcmp(obj_name, "..") ||
	       !strcmp(obj_name, idx_file_name)
	    )
		continue;

	    /* Object name does not start with a number? */
	    if(!isdigit(*obj_name))
	    {
		/* The object probably does not belong in this directory
		 * since it starts with a number, prompt to move the
		 * object to the user's home directory
		 */
		gint response;
		gboolean got_cancel = FALSE;

		if(!(*status)) *status = -1;

		if(interactive && !(*yes_to_all))
		{
		    gchar *buf = g_strdup_printf(
#if defined(PROG_LANGUAGE_SPANISH)
"Un objeto denominó \"%s\" no aparece pertenecer\n\
en la guía reciclada de objetos.\n\
\n\
¿Mueve este objeto a la guía del hogar?\n"
#elif defined(PROG_LANGUAGE_FRENCH)
"Un objet a nommé \"%s\" n'apparaît pas d'appartenir\n\
dans l'annuaire d'objets recyclé.\n\
\n\
Transfère cet objet à l'annuaire de maison?\n"
#elif defined(PROG_LANGUAGE_GERMAN)
"Ein objekt erscheint \"%s\" nicht hat genannt,\n\
im wiederverwerteten objekten verzeichnis zu gehören.\n\
\n\
Bewegen sie dieses objekt zum heim verzeichnis?\n"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Un oggetto ha dato un nome a \"%s\" non appare\n\
appartenere nell'elenco di oggetti riciclato.\n\
\n\
Muove quest'oggetto all'elenco di casa?\n"
#elif defined(PROG_LANGUAGE_DUTCH)
"Een voorwerp verschijnt \"%s\" niet noemde\n\
om in de gerecyclde voorwerpen gids te horen.\n\
\n\
Beweeg deze voorwerp aan de huis gids?\n"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Um objeto nomeou \"%s\" não aparece pertencer\n\
no guia reciclado de objetos.\n\
\n\
Mova este objeto ao guia de lar?\n"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Et objekt kalt \"%s\" kommer ikke fram høre\n\
til i den resirkuleredde objektkatalog.\n\
\n\
Flytt dette objektet til hjemkatalogen?\n"
#else
"An object named \"%s\" does not appear to belong\n\
in the recycled objects directory.\n\
\n\
Move this object to the home directory?\n"
#endif
			, obj_name
		    );
		    EDVPlaySoundQuestion(core_ptr);
		    CDialogSetTransientFor(toplevel);
		    response = CDialogGetResponse(
#if defined(PROG_LANGUAGE_SPANISH)
"El Objeto Inerte Encontró",
buf,
"Un objeto se ha encontrado en la guía reciclada de\n\
objetos que no aparece para pertenecer allí. Para ayudar\n\
a mantener la guía reciclada de objetos lo limpia es\n\
recomienda que el objeto se sea mudado de la guía\n\
reciclada de objetos a la guía del hogar.\n",
#elif defined(PROG_LANGUAGE_FRENCH)
"L'Objet Inerte A Trouvé",
buf,
"Un objet a été trouvé dans l'annuaire d'objets recyclé\n\
qui n'apparaît pas d'appartenir là-bas. Pour aider garder\n\
les objets recyclé que l'annuaire nettoie c'est recommandent\n\
que l'objet ait sorti de l'annuaire d'objets recyclé à\n\
l'annuaire de maison.\n",
#elif defined(PROG_LANGUAGE_GERMAN)
"Träges Objekt Hat Gefunden",
buf,
"Ein objekt ist im wiederverwerteten objekten\n\
verzeichnis gefunden worden, das nicht erscheint, dort zu\n\
gehören. Zu helfen, um das wiederverwertete objekte\n\
verzeichnis zu behalten, um es zu reinigen, empfiehlt ist,\n\
daß das Objekt aus dem wiederverwerteten objekten verzeichnis\n\
zum heim verzeichnis bewegt ist.\n",
#elif defined(PROG_LANGUAGE_ITALIAN)
"L'Oggetto Inerte Ha Trovato",
buf,
"Un oggetto è stato trovato nell'elenco di oggetti\n\
riciclato che non appare appartenere lí. Per aiutare tenere\n\
l'elenco di oggetti riciclato pulisce è raccomanda che\n\
l'oggetto è fuori mosso dell'elenco di oggetti riciclato\n\
all'elenco di casa.\n",
#elif defined(PROG_LANGUAGE_DUTCH)
"Inert Voorwerp Vond",
buf,
"Een voorwerp is in de gerecyclde voorwerpen gids gevonden\n\
dat niet verschijnt om daar te horen. Om kost de gerecyclde\n\
voorwerpen gids volkomen het is, aanraadt te helpen dat het\n\
voorwerp uit de gerecyclde voorwerpen gids aan de huis gids\n\
bewogen is.\n",
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Objeto Inerte Achou",
buf,
"Um objeto foi achado no guia reciclado de objetos que não\n\
aparece pertencer aí. Ajudar mantem o guia reciclado de\n\
objetos limpar é recomenda que o objeto é movido para fora do\n\
guia reciclado de objetos ao guia de lar.\n",
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Nøytralt Objekt Funnet",
buf,
"Et objekt finner i den resirkuleredde objektkatalog som\n\
ikke kommer fram høre til der. Hjelpe beholder den\n\
resirkuleredde objektkatalog rengjør det er anbefaler at\n\
objektet flyttet ut av den resirkuleredde objektkatalog til\n\
hjemkatalogen.\n",
#else
"Inert Object Found",
buf,
"A object has been found in the recycled objects directory\n\
that does not appear to belong there. To help keep the\n\
recycled objects directory clean it is recommend that the\n\
object be moved out of the recycled objects directory to\n\
the home directory.\n",
#endif
			CDIALOG_ICON_WARNING,
			CDIALOG_BTNFLAG_YES |
			CDIALOG_BTNFLAG_YES_TO_ALL |
			CDIALOG_BTNFLAG_NO |
			CDIALOG_BTNFLAG_CANCEL |
			CDIALOG_BTNFLAG_HELP,
			CDIALOG_BTNFLAG_YES
		    );
		    CDialogSetTransientFor(NULL);
		    g_free(buf);
		}
		else
		{
		    response = CDIALOG_RESPONSE_YES_TO_ALL;
		}
		/* Handle response */
		switch(response)
		{
		  case CDIALOG_RESPONSE_YES_TO_ALL:
		    *yes_to_all = TRUE;
		  case CDIALOG_RESPONSE_YES:
		    /* Yes, move the object */
		    if(TRUE)
		    {
			gchar	*tar = g_strdup_printf(
			    "%s%c%s",
			    core_ptr->home_dir, G_DIR_SEPARATOR, obj_name
			),
				*src = g_strdup_printf(
			    "%s%c%s",
			    recbin_dir, G_DIR_SEPARATOR, obj_name
			);
			if(rename(src, tar))
			{
			    gchar *buf = g_strdup_printf(
#if defined(PROG_LANGUAGE_SPANISH)
"Incapaz de mover objeto:\n\
\n\
    %s\n\
\n\
A:\n\
\n\
    %s\n"
#elif defined(PROG_LANGUAGE_FRENCH)
"Incapable de déplacer l'objet:\n\
\n\
    %s\n\
\n\
A:\n\
\n\
    %s\n"
#elif defined(PROG_LANGUAGE_GERMAN)
"Unfähig, objekt zu bewegen:\n\
\n\
    %s\n\
\n\
Zu:\n\
\n\
    %s\n"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Incapace per muovere l'oggetto:\n\
\n\
    %s\n\
\n\
A:\n\
\n\
    %s\n"
#elif defined(PROG_LANGUAGE_DUTCH)
"Onbekwaam voorwerp te bewegen:\n\
\n\
    %s\n\
\n\
Te:\n\
\n\
    %s\n"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Incapaz de mover objeto:\n\
\n\
    %s\n\
\n\
A:\n\
\n\
    %s\n"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Maktesløs flytte objekt:\n\
\n\
    %s\n\
\n\
Til:\n\
\n\
    %s\n"
#else
"Unable to move object:\n\
\n\
    %s\n\
\n\
To:\n\
\n\
    %s\n"
#endif
				, obj_name, core_ptr->home_dir
			    );
			    MESSAGE_ERROR(
"Move Error",
buf
			    );
			    g_free(buf);
			    if(!(*status)) *status = -1;
			}
			g_free(tar);
			g_free(src);
		    }
		    break;
		  case CDIALOG_RESPONSE_NO:
		    /* No, do not move the object */
		    break;
		  default:
		    /* Cancel */
		    if(!(*status)) *status = -4;
		    got_cancel = TRUE;
		    break;
		}
		if(got_cancel)
		    break;

		continue;
	    }

	    /* Each recycled object's name is a number, so get the
	     * numeric value of the object's name as the recycled
	     * object's index
	     */
	    if(TRUE)
	    {
		gboolean in_list = FALSE;
		guint index = (guint)ATOI(obj_name);
		GList *glist = idx_list;
		while(glist != NULL)
		{
		    if((guint)glist->data == index)
		    {
			in_list = TRUE;
			break;
		    }
		    glist = g_list_next(glist);
		}

		/* Recycled object in the index file? */
		if(in_list)
		{
		    /* Object belongs in the recycled objects directory */
		    gchar *path = g_strdup_printf(
			"%s%c%s",
			recbin_dir, G_DIR_SEPARATOR, obj_name
		    );

		    /* Set permissions */
		    if(chmod(path, S_IRUSR | S_IWUSR))
		    {
			gchar *buf = g_strdup_printf(
#if defined(PROG_LANGUAGE_SPANISH)
"Incapaz de poner los permisos en reciclado se opone:\n\
\n\
    #%s\n\
\n\
Esto puede colocar un concearn de la seguridad desde\n\
que otros usuarios pueden ser capaces de conseguir\n\
acceso a los objetos reciclados.\n"
#elif defined(PROG_LANGUAGE_FRENCH)
"Incapable de régler des permissions sur l'objet recyclé:\n\
\n\
    #%s\n\
\n\
Ceci peut poser un concearn de sécurité puisque les\n\
autres utilisateurs peuvent pouvoir accéder aux objets\n\
recyclés.\n"
#elif defined(PROG_LANGUAGE_GERMAN)
"Unfähig, erlaubnis auf wiederverwerteten objekt zu setzen:\n\
\n\
    #%s\n\
\n\
Dies kann einen sicherheitsconcearn aufstellen, da\n\
andere verbraucher auf die wiederverwerteten objekte\n\
dürfen zugreifen können.\n"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Incapace per regolare i permessi sull'oggetto riciclato:\n\
\n\
    #%s\n\
\n\
Questo può posare un concearn di sicurezza poiché gli\n\
altri operatori possono essere in grado di accedere agli\n\
oggetti riciclati.\n"
#elif defined(PROG_LANGUAGE_DUTCH)
"Onbekwaam toestemmingen op gerecycld voorwerp te zetten:\n\
\n\
    #%s\n\
\n\
Dit zou een veiligheidsconcearn kunnen poseren\n\
aangezien anderze gebruikers toegang tot de gerecyclde\n\
voorwerpen misschien hebben kan.\n"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Incapaz de por permissões em objeto reciclado:\n\
\n\
    #%s\n\
\n\
Isto pode posar um concearn de segurança desde que\n\
outros operadores podem ser capazes de aceder os objetos\n\
reciclado.\n"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Maktesløs sette tillatelse på resirkuleredd objekt:\n\
\n\
    #%s\n\
\n\
Dette oppstiller en sikkerhet concearn da andre\n\
brukere er kyndige komme til de resirkuleredde\n\
objektene.\n"
#else
"Unable to set permissions on recycled object:\n\
\n\
    #%s\n\
\n\
This may pose a security concearn since other users\n\
may be able to access the recycled objects.\n"
#endif
			    , obj_name
			);
			MESSAGE_ERROR(
"Change Mode Error",
buf
			);
			g_free(buf);

			if(!(*status)) *status = -1;
		    }

		    g_free(path);
		}
		else
		{
		    /* The object probably does not belong in this
		     * directory since it starts with a number, prompt
		     * to move the object to the user's home directory
		     */
		    gint response;
		    gboolean got_cancel = FALSE;

		    if(!(*status)) *status = -1;

		    if(interactive && !(*yes_to_all))
		    {
			gchar *buf = g_strdup_printf(
#if defined(PROG_LANGUAGE_SPANISH)
"Un objeto denominó \"%s\" no aparece pertenecer\n\
en la guía reciclada de objetos.\n\
\n\
¿Mueve este objeto a la guía del hogar?\n"
#elif defined(PROG_LANGUAGE_FRENCH)
"Un objet a nommé \"%s\" n'apparaît pas d'appartenir\n\
dans l'annuaire d'objets recyclé.\n\
\n\
Transfère cet objet à l'annuaire de maison?\n"
#elif defined(PROG_LANGUAGE_GERMAN)
"Ein objekt erscheint \"%s\" nicht hat genannt,\n\
im wiederverwerteten objekten verzeichnis zu gehören.\n\
\n\
Bewegen sie dieses objekt zum heim verzeichnis?\n"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Un oggetto ha dato un nome a \"%s\" non appare\n\
appartenere nell'elenco di oggetti riciclato.\n\
\n\
Muove quest'oggetto all'elenco di casa?\n"
#elif defined(PROG_LANGUAGE_DUTCH)
"Een voorwerp verschijnt \"%s\" niet noemde\n\
om in de gerecyclde voorwerpen gids te horen.\n\
\n\
Beweeg deze voorwerp aan de huis gids?\n"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Um objeto nomeou \"%s\" não aparece pertencer\n\
no guia reciclado de objetos.\n\
\n\
Mova este objeto ao guia de lar?\n"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Et objekt kalt \"%s\" kommer ikke fram høre\n\
til i den resirkuleredde objektkatalog.\n\
\n\
Flytt dette objektet til hjemkatalogen?\n"
#else
"An object named \"%s\" does not appear to belong\n\
in the recycled objects directory.\n\
\n\
Move this object to the home directory?\n"
#endif
			    , obj_name
			);
			EDVPlaySoundQuestion(core_ptr);
			CDialogSetTransientFor(toplevel);
			response = CDialogGetResponse(
#if defined(PROG_LANGUAGE_SPANISH)
"El Objeto Inerte Encontró",
buf,
"Un objeto se ha encontrado en la guía reciclada de\n\
objetos que no aparece para pertenecer allí. Para ayudar\n\
a mantener la guía reciclada de objetos lo limpia es\n\
recomienda que el objeto se sea mudado de la guía\n\
reciclada de objetos a la guía del hogar.\n",
#elif defined(PROG_LANGUAGE_FRENCH)
"L'Objet Inerte A Trouvé",
buf,
"Un objet a été trouvé dans l'annuaire d'objets recyclé\n\
qui n'apparaît pas d'appartenir là-bas. Pour aider garder\n\
les objets recyclé que l'annuaire nettoie c'est recommandent\n\
que l'objet ait sorti de l'annuaire d'objets recyclé à\n\
l'annuaire de maison.\n",
#elif defined(PROG_LANGUAGE_GERMAN)
"Träges Objekt Hat Gefunden",
buf,
"Ein objekt ist im wiederverwerteten objekten\n\
verzeichnis gefunden worden, das nicht erscheint, dort zu\n\
gehören. Zu helfen, um das wiederverwertete objekte\n\
verzeichnis zu behalten, um es zu reinigen, empfiehlt ist,\n\
daß das Objekt aus dem wiederverwerteten objekten verzeichnis\n\
zum heim verzeichnis bewegt ist.\n",
#elif defined(PROG_LANGUAGE_ITALIAN)
"L'Oggetto Inerte Ha Trovato",
buf,
"Un oggetto è stato trovato nell'elenco di oggetti\n\
riciclato che non appare appartenere lí. Per aiutare tenere\n\
l'elenco di oggetti riciclato pulisce è raccomanda che\n\
l'oggetto è fuori mosso dell'elenco di oggetti riciclato\n\
all'elenco di casa.\n",
#elif defined(PROG_LANGUAGE_DUTCH)
"Inert Voorwerp Vond",
buf,
"Een voorwerp is in de gerecyclde voorwerpen gids gevonden\n\
dat niet verschijnt om daar te horen. Om kost de gerecyclde\n\
voorwerpen gids volkomen het is, aanraadt te helpen dat het\n\
voorwerp uit de gerecyclde voorwerpen gids aan de huis gids\n\
bewogen is.\n",
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Objeto Inerte Achou",
buf,
"Um objeto foi achado no guia reciclado de objetos que não\n\
aparece pertencer aí. Ajudar mantem o guia reciclado de\n\
objetos limpar é recomenda que o objeto é movido para fora do\n\
guia reciclado de objetos ao guia de lar.\n",
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Nøytralt Objekt Funnet",
buf,
"Et objekt finner i den resirkuleredde objektkatalog som\n\
ikke kommer fram høre til der. Hjelpe beholder den\n\
resirkuleredde objektkatalog rengjør det er anbefaler at\n\
objektet flyttet ut av den resirkuleredde objektkatalog til\n\
hjemkatalogen.\n",
#else
"Inert Object Found",
buf,
"A object has been found in the recycled objects directory\n\
that does not appear to belong there. To help keep the\n\
recycled objects directory clean it is recommend that the\n\
object be moved out of the recycled objects directory to\n\
the home directory.\n",
#endif
			    CDIALOG_ICON_WARNING,
			    CDIALOG_BTNFLAG_YES |
			    CDIALOG_BTNFLAG_YES_TO_ALL |
			    CDIALOG_BTNFLAG_NO |
			    CDIALOG_BTNFLAG_CANCEL |
			    CDIALOG_BTNFLAG_HELP,
			    CDIALOG_BTNFLAG_YES
			);
			CDialogSetTransientFor(NULL);
			g_free(buf);
		    }
		    else
		    {
			response = CDIALOG_RESPONSE_YES_TO_ALL;
		    }
		    /* Handle response */
		    switch(response)
		    {
		      case CDIALOG_RESPONSE_YES_TO_ALL:
			*yes_to_all = TRUE;
		      case CDIALOG_RESPONSE_YES:
			/* Yes, move the object */
			if(TRUE)
			{
			    gchar   *tar = g_strdup_printf(
				"%s%c%s",
				core_ptr->home_dir, G_DIR_SEPARATOR, obj_name
			    ),
				    *src = g_strdup_printf(
				"%s%c%s",
				recbin_dir, G_DIR_SEPARATOR, obj_name
			    );
			    if(rename(src, tar))
			    {
				gchar *buf = g_strdup_printf(
#if defined(PROG_LANGUAGE_SPANISH)
"Incapaz de mover objeto:\n\
\n\
    %s\n\
\n\
A:\n\
\n\
    %s\n"
#elif defined(PROG_LANGUAGE_FRENCH)
"Incapable de déplacer l'objet:\n\
\n\
    %s\n\
\n\
A:\n\
\n\
    %s\n"
#elif defined(PROG_LANGUAGE_GERMAN)
"Unfähig, objekt zu bewegen:\n\
\n\
    %s\n\
\n\
Zu:\n\
\n\
    %s\n"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Incapace per muovere l'oggetto:\n\
\n\
    %s\n\
\n\
A:\n\
\n\
    %s\n"
#elif defined(PROG_LANGUAGE_DUTCH)
"Onbekwaam voorwerp te bewegen:\n\
\n\
    %s\n\
\n\
Te:\n\
\n\
    %s\n"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Incapaz de mover objeto:\n\
\n\
    %s\n\
\n\
A:\n\
\n\
    %s\n"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Maktesløs flytte objekt:\n\
\n\
    %s\n\
\n\
Til:\n\
\n\
    %s\n"
#else
"Unable to move object:\n\
\n\
    %s\n\
\n\
To:\n\
\n\
    %s\n"
#endif
				    , obj_name, core_ptr->home_dir
				);
				MESSAGE_ERROR(
"Move Error",
buf
				);
				g_free(buf);
				if(!(*status)) *status = -1;
			    }
			    g_free(tar);
			    g_free(src);
			}
			break;
		      case CDIALOG_RESPONSE_NO:
			/* No, do not move the object */
			break;
		      default:
			/* Cancel */
			if(!(*status)) *status = -4;
			got_cancel = TRUE;
			break;
		    }
		    if(got_cancel)
			break;

		    continue;
		}
	    }
	}


	STATUS_MESSAGE(NULL);
	STATUS_PROGRESS(0.0f);

	/* Delete indices list */
	g_list_free(idx_list);

	/* Delete list of recycled object names */
	for(i = 0; i < strc; i++)
	    g_free(strv[i]);
	g_free(strv);
}



/*
 *	Compacts objects and fixes any errors in the recycle bin.
 */
gint EDVRecBinSync(
	edv_core_struct *core_ptr,
	GtkWidget *toplevel,
	gboolean show_progress, gboolean interactive,
	gboolean *yes_to_all,
	void (*status_message_cb)(gpointer, const gchar *, gpointer),
	void (*status_progress_cb)(gpointer, gfloat, gpointer),
	gpointer client_data
)
{
	static gboolean reenterent = FALSE;
	static gint status;
	static gboolean lyes_to_all;
	const gchar *recbin_idx_file;
	gchar *recbin_dir = NULL;
	gulong time_start = (gulong)time(NULL);
	struct stat stat_buf;


	if(reenterent)
	{
	    status = -6;
	    return(status);
	}
	else
	{
	    reenterent = TRUE;
	}

#define POSTOP_AND_RETURN(rtn_val)	{	\
 /* Report final progress */			\
 if(status_progress_cb != NULL)			\
  status_progress_cb(				\
   NULL, 1.0f, client_data			\
  );						\
						\
 g_free(recbin_dir);				\
 recbin_dir = NULL;				\
						\
 reenterent = FALSE;				\
 return(rtn_val);				\
}

	/* Reset last error */
	last_error = NULL;

	/* Reset status */
	status = 0;

	/* Use local yes_to_all if none is given */
	if(yes_to_all == NULL)
	{
	    lyes_to_all = FALSE;	/* Reset when using local */
	    yes_to_all = &lyes_to_all;
	}

	/* Report initial progress */
	if(status_progress_cb != NULL)
	    status_progress_cb(NULL, 0.0f, client_data);

	/* Check for invalid input values */
	if(core_ptr == NULL)
	{
	    MESSAGE_ERROR(
"Input Error",
"Bad input value"
	    );
	    if(!status) status = -1;
	    POSTOP_AND_RETURN(status);
	}

	/* Get path to the recycle bin index file */
	recbin_idx_file = CFGItemListGetValueS(
	    core_ptr->cfg_list, EDV_CFG_PARM_FILE_RECYCLED_INDEX
	);
	if(STRISEMPTY(recbin_idx_file))
	{
	    MESSAGE_ERROR(
"Recycle Bin Error",
"Recycled objects index file is not defined"
	    );
	    if(!status) status = -1;
	    POSTOP_AND_RETURN(status);
	}

	/* Get recycle bin directory */
	recbin_dir = EDVRecBinGetDirectoryFromIndexPath(recbin_idx_file);
	if(STRISEMPTY(recbin_dir))
	{
	    MESSAGE_ERROR(
"Recycle Bin Error",
"Unable to determine recycle bin directory"
	    );
	    if(!status) status = -1;
	    POSTOP_AND_RETURN(status);
	}


	/* Begin checking for errors */

	/* Create recycled bin directory as needed */
	if(stat(recbin_dir, &stat_buf))
	{
	    if(rmkdir(recbin_dir, S_IRUSR | S_IWUSR | S_IXUSR))
	    {
		gchar *buf = g_strdup_printf(
"Unable to create recycle bin directory:\n\
\n\
    %s\n\
\n\
Unable to continue with operation since the recycle\n\
bin directory does not exist and cannot be created.\n\
\n\
Check if you have write permission to the above\n\
location.\n",
		    recbin_dir
		);
		MESSAGE_ERROR(
"Create Directory Error",
buf
		);
		g_free(buf);
		if(!status) status = -1;
		POSTOP_AND_RETURN(status);
	    }
	}
	else if(!S_ISDIR(stat_buf.st_mode))
	{
	    gchar *buf = g_strdup_printf(
"Recycle bin directory path:\n\
\n\
    %s\n\
\n\
Is a non-directory object, the recycle bin directory\n\
must be a directory object.\n\
\n\
Try moving the object found at the above location to\n\
a different location or modify the recycle bin\n\
directory's location.\n",
		recbin_dir
	    );
	    MESSAGE_ERROR(
"Recycle Bin Error",
buf
	    );
	    g_free(buf);
	    if(!status) status = -1;
	    POSTOP_AND_RETURN(status);
	}

	/* Update permissions on recycle bin directory */
	if(chmod(recbin_dir, S_IRUSR | S_IWUSR | S_IXUSR))
	{
	    gchar *buf = g_strdup_printf(
"Unable to set permissions on recycle bin directory:\n\
\n\
    %s\n\
\n\
This may pose a security concearn since other users\n\
may be able to access the recycled objects.\n",
		recbin_dir
	    );
	    MESSAGE_ERROR(
"Change Mode Error",
buf
	    );
	    g_free(buf);

	    if(!status) status = -1;

	    /* Check for specific error */
	    switch(errno)
	    {
	      case EPERM:
		buf = g_strdup_printf(
"You do not own the recycle bin directory:\n\
\n\
    %s\n\
\n\
You must be the owner of the recycle bin directory\n\
in order to fix any errors in it.\n",
		    recbin_dir
		);
		break;
	      case EROFS:
		buf = g_strdup_printf(
"The recycle bin directory:\n\
\n\
    %s\n\
\n\
Is located on a read-only file system, any errors\n\
found on the recycle bin cannot be fixed.\n",
		    recbin_dir
		);
		break;
	      case EACCES:
		buf = g_strdup_printf(
"One or more compoent(s) of the recycle bin path:\n\
\n\
    %s\n\
\n\
Is not set searchable, therefore any errors found\n\
on the recycle bin cannot be fixed.\n",
		    recbin_dir
		);
		break;
	      default:
		buf = NULL;
		break;
	    }
	    if(buf != NULL)
	    {
		MESSAGE_ERROR(
"Recycle Bin Error",
buf
		);
		g_free(buf);
	    }

	    POSTOP_AND_RETURN(status);
	}


	/* Recycled objects index file exists? */
	if(!access(recbin_idx_file, F_OK) && (status != -4))
	{
	    /* Update permissions on the recycled objects index file */
	    if(chmod(recbin_idx_file, S_IRUSR | S_IWUSR))
	    {
		gchar *buf = g_strdup_printf(
"Unable to set permissions on the index file:\n\
\n\
    %s\n\
\n\
This may pose a security concearn since other users\n\
may be able to access the recycled objects.\n",
		    recbin_idx_file
		);
		MESSAGE_ERROR(
"Change Mode Error",
buf
		);
		g_free(buf);

		if(!status) status = -1;

		/* Check for specific error */
		switch(errno)
		{
		  case EPERM:
		    buf = g_strdup_printf(
"You do not own the index file:\n\
\n\
    %s\n\
\n\
You must be the owner of the index file in order\n\
to fix any errors in it.\n",
			recbin_idx_file
		    );
		    break;
		  case EROFS:
		    buf = g_strdup_printf(
"The index file:\n\
\n\
    %s\n\
\n\
Is located on a read-only file system, any errors\n\
found on the recycle bin cannot be fixed.\n",
			recbin_idx_file
		    );
		    break;
		  case EACCES:
		    buf = g_strdup_printf(
"One or more compoent(s) of the index file path:\n\
\n\
    %s\n\
\n\
Is not set searchable, therefore any errors found\n\
on the recycle bin cannot be fixed.\n",
			recbin_idx_file
		    );
		    break;
		  default:
		    buf = NULL;
		    break;
		}
		if(buf != NULL)
		{
		    MESSAGE_ERROR(
"Recycle Bin Error",
buf
		    );
		    g_free(buf);
		}

		POSTOP_AND_RETURN(status);
	    }

	    /* Check for indices that reffer to non-existant recycled
	     * objects
	     */
	    EDVRecBinSyncIndexFile(
		core_ptr,
		recbin_idx_file, recbin_dir,
		toplevel,
		show_progress, interactive,
		yes_to_all, &status,
		status_message_cb,
		status_progress_cb,
		client_data
	    );
	}



	/* Recycled objects index file exists? */
	if(status != -4)
	{
	    /* Check for recycled objects that are not listed in the
	     * index file
	     */
	    EDVRecBinSyncObjects(
		core_ptr,
		recbin_idx_file, recbin_dir,
		toplevel,
		show_progress, interactive,
		yes_to_all, &status,
		status_message_cb,
		status_progress_cb,
		client_data
	    );
	}



	/* Errors found? */
	if(status && (status != -4))
	{
	    MESSAGE(
"Recycle Bin Sync Results",
"Errors in the recycle bin were encountered and fixed.\n\
\n\
Recycle bin has been compacted.\n"
	    );
	}


	/* Record history */
	EDVAppendHistory(
	    core_ptr,
	    EDV_HISTORY_SYNC_RECBIN,
	    time_start, (gulong)time(NULL),
	    status,
	    recbin_idx_file,	/* Source */
	    NULL,		/* Target */
	    last_error		/* Comment */
	);

	POSTOP_AND_RETURN(status);
#undef POSTOP_AND_RETURN
}
