/*
 *	Lists the contents of the recycle bin, works similarly to ls.
 *
 *	Use "recover" to recover a recycled object or "purge" to
 *	permanently purge a recycled object.
 */

#include <stdlib.h>
#include <time.h>
#include <fnmatch.h>
#include <pwd.h>
#include <grp.h>
#include <sys/stat.h>
#include <glib.h>
#include <endeavour2.h>
#include "../config.h"


#define RLS_ENV_NAME_COLUMNS		"COLUMNS"
#define RLS_ENV_NAME_RLS_COLORS		"RLS_COLORS"
#define RLS_ENV_NAME_LS_COLORS		"LS_COLORS"


typedef enum {
	RLS_COLOR_CODE_UNKNOWN,
	RLS_COLOR_CODE_REGULAR,
	RLS_COLOR_CODE_DIRECTORY,
	RLS_COLOR_CODE_LINK,
	RLS_COLOR_CODE_FIFO,
	RLS_COLOR_CODE_DEVICE_BLOCK,
	RLS_COLOR_CODE_DEVICE_CHARACTER,
	RLS_COLOR_CODE_SOCKET,
	RLS_COLOR_CODE_EXECUTABLE
} rls_color_code;

typedef enum {
	RLS_SORT_NONE,				/* Order of deletion (same
						 * as unsorted) */
	RLS_SORT_NAME,
	RLS_SORT_INDEX,
	RLS_SORT_SIZE,
	RLS_SORT_LAST_ACCESSED_TIME,
	RLS_SORT_LAST_MODIFIED_TIME,
	RLS_SORT_LAST_CHANGED_TIME
} rls_sort;

typedef enum {
	RLS_LIST_FORMAT_TABLE_COLUMN,		/* Table ordered by column */
	RLS_LIST_FORMAT_TABLE_ROW,		/* Table ordered by row */
	RLS_LIST_FORMAT_SINGLE_COLUMN,		/* One object per line */
	RLS_LIST_FORMAT_SINGLE_LINE_COMMA,			/* Single continuous line with
						 * ',' separated object names */
	RLS_LIST_FORMAT_LONG			/* ls's long list format */
} rls_list_format;

typedef enum {
	RLS_PRINT_COLOR			= (1 << 0),
	RLS_PRINT_NAME_WITH_INDICIES	= (1 << 1),
	RLS_PRINT_ORIGINAL_LOCATION	= (1 << 2),
	RLS_PRINT_FULL_TIME		= (1 << 3),
	RLS_PRINT_UID_GID_NAMES		= (1 << 4),
	RLS_PRINT_FRIENDLY_SIZES	= (1 << 5),
	RLS_PRINT_FRIENDLY_SIZES_1000	= (1 << 6),	/* Use 1000 sized
							 * blocks instead of
							 * 1024 */
	RLS_PRINT_GROUP			= (1 << 7)
} rls_print_options;


typedef struct {
	gint		code,
			attributes;
} rls_color_code_struct;
#define RLS_COLOR_CODE(p)	((rls_color_code_struct *)(p))

typedef struct {
	gchar		*name,
			*name_index;	/* "name (#12345)" */
	guint		index;
	struct stat	stat;
	gchar		*original_path;	/* Original location without the
					 * object's name */
} rls_obj_struct;
#define RLS_OBJ(p)		((rls_obj_struct *)(p))

typedef struct {
	gchar		*name;
	gint		id;
} rls_uid_struct;
#define RLS_UID(p)		((rls_uid_struct *)(p))


/* Print Help */
static void print_usage(const gchar *prog_name);

/* Sort Callbacks */
static int rls_obj_sort_name_cb(const void *a, const void *b);
static int rls_obj_sort_name_rev_cb(const void *a, const void *b);
static int rls_obj_sort_index_cb(const void *a, const void *b);
static int rls_obj_sort_index_rev_cb(const void *a, const void *b);
static int rls_obj_sort_size_cb(const void *a, const void *b);
static int rls_obj_sort_size_rev_cb(const void *a, const void *b);
static int rls_obj_sort_accessed_time_cb(const void *a, const void *b);
static int rls_obj_sort_accessed_time_rev_cb(const void *a, const void *b);
static int rls_obj_sort_modified_time_cb(const void *a, const void *b);
static int rls_obj_sort_modified_time_rev_cb(const void *a, const void *b);
static int rls_obj_sort_changed_time_cb(const void *a, const void *b);
static int rls_obj_sort_changed_time_rev_cb(const void *a, const void *b);

/* RLS Color Codes */
static rls_color_code_struct *rls_color_code_new(void);
static void rls_color_code_delete(rls_color_code_struct *c);

/* RLS Object */
static rls_obj_struct *rls_obj_new(void);
static void rls_obj_delete(rls_obj_struct *obj);
static GList *rls_get_listing(
	edv_context_struct *ctx, gint *nobjs_rtn,
	gulong *total_size,
	const rls_sort sort,
	const gboolean sort_reversed
);

/* RLS UID */
static rls_uid_struct *rls_uid_new(void);
static void rls_uid_delete(rls_uid_struct *uid);

/* Printing */
static gint rls_print_obj_name(
	rls_obj_struct *obj,
	GList *color_codes_list,
	const rls_print_options print_options
);
static void rls_print_obj_size(
	const gulong x, const rls_print_options print_options
);
static void rls_print_obj_list_table(
	GList *rec_obj_list, const gint nobjs,
	GList *color_codes_list,
	const gint column_width,
	const gboolean order_by_row,
	const rls_print_options print_options
);
static void rls_print_obj_list_single_column(
	GList *rec_obj_list, const gint nobjs,
	GList *color_codes_list,
	const rls_print_options print_options
);
static void rls_print_obj_list_comma(
	GList *rec_obj_list, const gint nobjs,
	GList *color_codes_list,
	const rls_print_options print_options
);
static void rls_print_obj_list_long(
	GList *rec_obj_list, const gint nobjs, const gulong total_size,
	GList *color_codes_list,
	const gint column_width,
	const rls_sort sort,
	const rls_print_options print_options
);
static void rls_print_obj_list(
	GList *rec_obj_list, const gint nobjs, const gulong total_size,
	GList *color_codes_list,
	const gint column_width,
	const rls_sort sort,
	const rls_list_format list_format,
	const rls_print_options print_options
);


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


/*
 *	Prints the help message.
 */
static void print_usage(const gchar *prog_name)
{
	g_print(   
"Usage: %s [object(s)...] [options]\n",
	    prog_name
	);

	g_print("\n\
    The [object(s)...] specifies one or more recycled objects to\n\
    list.\n\
\n\
    Note that if you specify wildcards (* or ?) then you need to put\n\
    them between double quotes (\") such as \"*\" or else the shell\n\
    will expand them as regular object names instead of recycled\n\
    object names.\n\
\n\
    The [options] can be any of the following:\n\
\n\
	-1                      Print single column list format.\n\
	-C                      Print table format ordered by column\n\
				(default).\n\
	-x                      Print table format ordered by row.\n\
	-m                      Print a single line comma-separated\n\
				object names.\n\
	-l                      Print long list format (similar to\n\
				ls's long list format).\n\
\n\
	-U                      Sort by order of deletion (same as\n\
				unsorted).\n\
	-i                      Sort by index.\n\
	-S                      Sort by size.\n\
	-u                      Sort by last accessed time.\n\
	-t                      Sort by last modified time.\n\
	-c                      Sort by last changed time.\n\
	--reverse               Sort reversed.\n\
	-r                      Same as --reverse.\n\
\n\
	--color <when>          Use color to distinguish object types,\n\
				valid values are; never, always, auto.\n\
				Color codes are defined in the\n\
				environment variables RLS_COLORS\n\
				(checked first) or LS_COLORS.\n\
	--clean-names           Print names without indices.\n\
	-N                      Same as --clean-names.\n\
	--original-location     Print the full path to the original\n\
				object.\n\
	-O                      Same as --original-location.\n\
	--full-time             Print full date and time when using\n\
				the -l long list format.\n\
	--numeric-uid-gid       Print numeric UID and GID values\n\
				instead of names.\n\
	-n                      Same as --numeric-uid-gid.\n\
	--human-readable        Print sizes in human readable format\n\
				using 1024 size blocks (i.e. 1K 234M\n\
				2G).\n\
	-h                      Same as --human-readable.\n\
	--si                    Print sizes in human readable format\n\
				using 1000 size blocks (i.e. 1K 234M\n\
				2G).\n\
	-H                      Same as --si.\n\
\n\
	--no-group              Do not print group information.\n\
	-G                      Same as --no-group.\n\
\n\
	--width <i>             Specifies the width of the terminal.\n\
	-w                      Same as --width.\n\
\n\
	--help                  Prints (this) help screen and exits.\n\
	--version               Prints version information and exits.\n\
\n\
    Return values:\n\
\n\
	0       Success.\n\
	1       General error.\n\
	2       Invalid value.\n\
	3       Systems error or memory allocation error.\n\
	4       User aborted.\n\
\n\
    To purge recycled objects, use \"purge\".\n\
    To recover recycled objects, use \"recover\".\n\
    To recycle objects, use \"recycle\".\n\
\n"
	);
}


/*
 *	Sort by name callback.
 */
static int rls_obj_sort_name_cb(const void *a, const void *b)
{
	const rls_obj_struct	*oa = *((const rls_obj_struct **)a),
				*ob = *((const rls_obj_struct **)b);
	return(strcmp(oa->name, ob->name));
} 
static int rls_obj_sort_name_rev_cb(const void *a, const void *b)
{
	const rls_obj_struct	*oa = *((const rls_obj_struct **)a),
				*ob = *((const rls_obj_struct **)b);
	return(strcmp(ob->name, oa->name));
} 

/*
 *	Sort by index callback.
 */
static int rls_obj_sort_index_cb(const void *a, const void *b)
{
	const rls_obj_struct	*oa = *((const rls_obj_struct **)a),
				*ob = *((const rls_obj_struct **)b);
	return(oa->index >= ob->index);
}
static int rls_obj_sort_index_rev_cb(const void *a, const void *b)
{
	const rls_obj_struct	*oa = *((const rls_obj_struct **)a),
				*ob = *((const rls_obj_struct **)b);
	return(ob->index >= oa->index);
}

/*
 *	Sort by size callback.
 */
static int rls_obj_sort_size_cb(const void *a, const void *b)
{
	const rls_obj_struct	*oa = *((const rls_obj_struct **)a),
				*ob = *((const rls_obj_struct **)b);
	return(oa->stat.st_size <= ob->stat.st_size);
}
static int rls_obj_sort_size_rev_cb(const void *a, const void *b)
{
	const rls_obj_struct	*oa = *((const rls_obj_struct **)a),
				*ob = *((const rls_obj_struct **)b);
	return(ob->stat.st_size <= oa->stat.st_size);
}

/*
 *	Sort by last accessed time callback.
 */
static int rls_obj_sort_accessed_time_cb(const void *a, const void *b)
{
	const rls_obj_struct	*oa = *((const rls_obj_struct **)a),
				*ob = *((const rls_obj_struct **)b);
	return(oa->stat.st_atime <= ob->stat.st_atime);
}
static int rls_obj_sort_accessed_time_rev_cb(const void *a, const void *b)
{
	const rls_obj_struct	*oa = *((const rls_obj_struct **)a),
				*ob = *((const rls_obj_struct **)b);
	return(ob->stat.st_atime <= oa->stat.st_atime);
}

/*
 *	Sort by last modified time callback.
 */
static int rls_obj_sort_modified_time_cb(const void *a, const void *b)
{
	const rls_obj_struct	*oa = *((const rls_obj_struct **)a),
				*ob = *((const rls_obj_struct **)b);
	return(oa->stat.st_mtime <= ob->stat.st_mtime);
}
static int rls_obj_sort_modified_time_rev_cb(const void *a, const void *b)
{
	const rls_obj_struct	*oa = *((const rls_obj_struct **)a),
				*ob = *((const rls_obj_struct **)b);
	return(ob->stat.st_mtime <= oa->stat.st_mtime);
}

/*
 *	Sort by last changed time callback.
 */
static int rls_obj_sort_changed_time_cb(const void *a, const void *b)
{
	const rls_obj_struct	*oa = *((const rls_obj_struct **)a),
				*ob = *((const rls_obj_struct **)b);
	return(oa->stat.st_ctime <= ob->stat.st_ctime);
}
static int rls_obj_sort_changed_time_rev_cb(const void *a, const void *b)
{
	const rls_obj_struct	*oa = *((const rls_obj_struct **)a),
				*ob = *((const rls_obj_struct **)b);
	return(ob->stat.st_ctime <= oa->stat.st_ctime);
}


/*
 *	Creates a new color code.
 */
static rls_color_code_struct *rls_color_code_new(void)
{
	return(RLS_COLOR_CODE(g_malloc0(sizeof(rls_color_code_struct))));
}

/*
 *	Deletes the color code.
 */
static void rls_color_code_delete(rls_color_code_struct *c)
{
	if(c == NULL)
	    return;

	g_free(c);
}


/*
 *	Creates a new object.
 */
static rls_obj_struct *rls_obj_new(void)
{
	return(RLS_OBJ(g_malloc0(sizeof(rls_obj_struct))));
}

/*
 *	Deletes the object.
 */
static void rls_obj_delete(rls_obj_struct *obj)
{
	if(obj == NULL)
	    return;

	g_free(obj->name);
	g_free(obj->name_index);
	g_free(obj->original_path);
	g_free(obj);
}


/*
 *	Gets a listing of all recycled objects.
 *
 *	The sort specifies the sort order of the returned list.
 */
static GList *rls_get_listing(
	edv_context_struct *ctx, gint *nobjs_rtn,
	gulong *total_size,
	const rls_sort sort,
	const gboolean sort_reversed
)
{
	guint *index;
	gchar **path;
	struct stat **stat_list;
	gint i, nobjs;
	GList *rec_obj_list;
	rls_obj_struct **list, *obj;
	int (*sort_func)(const void *, const void *) = NULL;

	if(nobjs_rtn != NULL)
	    *nobjs_rtn = 0;
	if(total_size != NULL)
	    *total_size = 0l;

	/* Get the stats of all the recycled objects */
	EDVRecycledObjectStatAll(
	    ctx, &path, &index, &stat_list, &nobjs
	);
	if(nobjs <= 0)
	    return(NULL);

	if(nobjs_rtn != NULL)
	    *nobjs_rtn = nobjs;

	/* Create the list of recycled objects using our object
	 * structure in a pointer array
	 */
	list = (rls_obj_struct **)g_malloc(nobjs * sizeof(rls_obj_struct *));
	for(i = 0; i < nobjs; i++)
	{
	    list[i] = obj = rls_obj_new();
	    obj->name = STRDUP(g_basename(path[i]));
	    obj->index = index[i];
	    obj->name_index = g_strdup_printf("%s (#%u)", obj->name, obj->index);
	    memcpy(&obj->stat, stat_list[i], sizeof(struct stat));
	    obj->original_path = g_dirname(path[i]);

	    if(total_size != NULL)
		*total_size = *total_size + (gulong)obj->stat.st_size;

	    g_free(path[i]);
	    g_free(stat_list[i]);
	}

	g_free(path);
	g_free(index);
	g_free(stat_list);

	/* Sort the pointer array list */
	switch(sort)
	{
	  case RLS_SORT_NONE:
	    sort_func = NULL;
	    break;
	  case RLS_SORT_NAME:
	    sort_func = sort_reversed ?
		rls_obj_sort_name_rev_cb : rls_obj_sort_name_cb;
	    break;
	  case RLS_SORT_INDEX:
	    sort_func = sort_reversed ?
		rls_obj_sort_index_rev_cb : rls_obj_sort_index_cb;
	    break;
	  case RLS_SORT_SIZE:
	    sort_func = sort_reversed ?
		rls_obj_sort_size_rev_cb : rls_obj_sort_size_cb;
	    break;
	  case RLS_SORT_LAST_ACCESSED_TIME:
	    sort_func = sort_reversed ?
		rls_obj_sort_accessed_time_rev_cb : rls_obj_sort_accessed_time_cb;
	    break;
	  case RLS_SORT_LAST_MODIFIED_TIME:
	    sort_func = sort_reversed ?
		rls_obj_sort_modified_time_rev_cb : rls_obj_sort_modified_time_cb;
	    break;
	  case RLS_SORT_LAST_CHANGED_TIME:
	    sort_func = sort_reversed ?
		rls_obj_sort_changed_time_rev_cb : rls_obj_sort_changed_time_cb;
	    break;
	}
	if(sort_func != NULL)
	    qsort(
		list, nobjs, sizeof(rls_obj_struct *),
		sort_func
	    );

	/* Add the objects to the return GList */
	rec_obj_list = NULL;
	for(i = 0; i < nobjs; i++)
	    rec_obj_list = g_list_append(
		rec_obj_list, list[i]
	    );

	/* Delete the pointer array */
	g_free(list);

	return(rec_obj_list);
}


/*
 *	Creates a new UID.
 */
static rls_uid_struct *rls_uid_new(void)
{
	return(RLS_UID(g_malloc0(sizeof(rls_uid_struct))));
}

/*
 *	Deletes the UID.
 */
static void rls_uid_delete(rls_uid_struct *uid)
{
	if(uid == NULL)
	    return;

	g_free(uid->name);
	g_free(uid);
}


/*
 *	Returns a string describing the object's name.
 *
 *	If print_options specifies the RLS_PRINT_COLOR option then
 *	the object's name will be printed in the appropriate
 *	color based on the object's type.
 *
 *	If print_options specifies the RLS_PRINT_ORIGINAL_LOCATION
 *	option then the object's original location will be printed
 *	in front with a directory deliminator added.
 *
 *	If print_options specifies the RLS_PRINT_NAME_WITH_INDICIES
 *	option then the object's index will be printed after the
 *	name in the format "name (#12345)"
 *
 *	Returns the number of characters printed.
 */
static gint rls_print_obj_name(
	rls_obj_struct *obj,
	GList *color_codes_list,
	const rls_print_options print_options
)
{
	const mode_t m = obj->stat.st_mode;
	gchar	*color_on_str = STRDUP(""),
		*color_off_str = STRDUP("");

	if(print_options & RLS_PRINT_COLOR)
	{
#define MK_COLOR_STR(_ci_)	{	\
 rls_color_code_struct *c = RLS_COLOR_CODE(g_list_nth_data( \
  color_codes_list, (_ci_)		\
 ));					\
 if(c != NULL) {			\
  g_free(color_on_str);			\
  color_on_str = g_strdup_printf(	\
   "\033[%i;%im",			\
   c->attributes, c->code		\
  );					\
 }					\
}

	    if(FALSE)
	    {

	    }
#ifdef S_ISDIR
	    else if(S_ISDIR(m))
	    {
		MK_COLOR_STR(RLS_COLOR_CODE_DIRECTORY);
	    }
#endif
#ifdef S_ISLNK
	    else if(S_ISLNK(m))
	    {
		MK_COLOR_STR(RLS_COLOR_CODE_LINK);
	    }
#endif
#ifdef S_ISFIFO
	    else if(S_ISFIFO(m))
	    {
		MK_COLOR_STR(RLS_COLOR_CODE_FIFO);
	    }
#endif
#ifdef S_ISBLK
	    else if(S_ISBLK(m))
	    {
		MK_COLOR_STR(RLS_COLOR_CODE_DEVICE_BLOCK);
	    }
#endif
#ifdef S_ISCHR
	    else if(S_ISCHR(m))
	    {
		MK_COLOR_STR(RLS_COLOR_CODE_DEVICE_CHARACTER);
	    }
#endif
#ifdef S_ISSOCK
	    else if(S_ISSOCK(m))
	    {
		MK_COLOR_STR(RLS_COLOR_CODE_SOCKET);
	    }
#endif
	    else if((m & S_IXUSR) || (m & S_IXGRP) || (m & S_IXOTH))
	    {
		MK_COLOR_STR(RLS_COLOR_CODE_EXECUTABLE);
	    }

	    g_free(color_off_str);
	    color_off_str = STRDUP("\033[0;0m");

#undef MK_COLOR_STR
	}

	if(print_options & RLS_PRINT_ORIGINAL_LOCATION)
	{
	    if(print_options & RLS_PRINT_NAME_WITH_INDICIES)
	    {
		g_print(
"%s%s%s%s%s (#%u)",
		    color_on_str,
		    obj->original_path,
		    G_DIR_SEPARATOR_S,
		    obj->name,
		    color_off_str,
		    obj->index
		);
		g_free(color_on_str);
		g_free(color_off_str);
		return(
		    STRLEN(obj->original_path) +
		    STRLEN(G_DIR_SEPARATOR_S) +
		    STRLEN(obj->name_index)
		);
	    }
	    else
	    {
		g_print(
"%s%s%s%s%s",
		    color_on_str,
		    obj->original_path,
		    G_DIR_SEPARATOR_S,
		    obj->name,
		    color_off_str
		);
		g_free(color_on_str);
		g_free(color_off_str);
		return(
		    STRLEN(obj->original_path) +
		    STRLEN(G_DIR_SEPARATOR_S) +
		    STRLEN(obj->name)
		);
	    }
	}
	else
	{
	    if(print_options & RLS_PRINT_NAME_WITH_INDICIES)
	    {
		g_print(
"%s%s%s (#%u)",
		    color_on_str,
		    obj->name,
		    color_off_str,
		    obj->index
		);
		g_free(color_on_str);
		g_free(color_off_str);
		return(
		    STRLEN(obj->name_index)
		);
	    }
	    else
	    {
		g_print(
"%s%s%s",
		    color_on_str,
		    obj->name,
		    color_off_str
		);
		g_free(color_on_str);
		g_free(color_off_str);
		return(
		    STRLEN(obj->name)
		);
	    }
	}

}


/*
 *	Prints the size.
 */
static void rls_print_obj_size(
	const gulong x, const rls_print_options print_options
)
{
	gchar *s;

	if(print_options & RLS_PRINT_FRIENDLY_SIZES)
	{
	    if(x < 1000l)
		s = g_strdup_printf(
		    "          %ld",
		    x
		);
	    else if(x < 10000l)
		s = g_strdup_printf(
		    "          %.1fk",
		    (gfloat)x / 1000.0f
		); 
	    else if(x < 1000000l)
		s = g_strdup_printf(
		    "          %ldk",
		    x / 1000l
		);
	    else if(x < 10000000l)
		s = g_strdup_printf(
		    "          %.1fM",
		    (gfloat)x / 1000.0f / 1000.0f
		);
	    else if(x < 1000000000l)
		s = g_strdup_printf(
		    "          %ldM",
		    x / 1000l / 1000l
		);
	    else if(x < 10000000000l)
		s = g_strdup_printf(
		    "          %.1fG",
		    (gfloat)x / 1000.0f / 1000.0f / 1000.0f
		);
	    else
		s = g_strdup_printf(
		    "          %ldG",
		    x / 1000l / 1000l / 1000l
		);
	    g_print("%s", s + (STRLEN(s) - 11));
	    g_free(s);
	}
	else if(print_options & RLS_PRINT_FRIENDLY_SIZES_1000)
	{
	    if(x < 1000l)
		s = g_strdup_printf(
		    "          %ld",
		    x
		);
	    else if(x < 10000l)
		s = g_strdup_printf(
		    "          %.1fk",
		    (gfloat)x / 1024.0f
		); 
	    else if(x < 1000000l)
		s = g_strdup_printf(
		    "          %ldk",
		    x / 1024l
		);
	    else if(x < 10000000l)
		s = g_strdup_printf(
		    "          %.1fM",
		    (gfloat)x / 1024.0f / 1024.0f
		);
	    else if(x < 1000000000l)
		s = g_strdup_printf(
		    "          %ldM",
		    x / 1024l / 1024l
		);
	    else if(x < 10000000000l)
		s = g_strdup_printf(
		    "          %.1fG",
		    (gfloat)x / 1024.0f / 1024.0f / 1024.0f
		);
	    else
		s = g_strdup_printf(
		    "          %ldG",
		    x / 1024l / 1024l / 1024l
		);
	    g_print("%s", s + (STRLEN(s) - 11));
	    g_free(s);
	}
	else
	{
	    s = g_strdup_printf(
		"          %ld",
		x
	    );
	    g_print("%s", s + (STRLEN(s) - 11));
	    g_free(s);
	}
}

/*
 *	Prints the list of objects using the short list format.
 *
 *	If order_by_row is TRUE then the objects will be printed in
 *	the order of each row. Otherwise the objects will be printed
 *	in the order of each column.
 */
static void rls_print_obj_list_table(
	GList *rec_obj_list, const gint nobjs,
	GList *color_codes_list,
	const gint column_width,
	const gboolean order_by_row,
	const rls_print_options print_options
)
{
	gint	i, j, k, len, longest_name_len, per_column_width,
		ncolumns, nobjs_per_column;
	GList *glist;
	rls_obj_struct *obj;

	/* Find the object with the longest name */
	longest_name_len = 0;
	for(glist = rec_obj_list; glist != NULL; glist = g_list_next(glist))
	{
	    obj = RLS_OBJ(glist->data);
	    if(obj == NULL)
		continue;

	    len = ((print_options & RLS_PRINT_ORIGINAL_LOCATION) ?
		(STRLEN(obj->original_path) + STRLEN(G_DIR_SEPARATOR_S)) : 0) +
		((print_options & RLS_PRINT_NAME_WITH_INDICIES) ?
		STRLEN(obj->name_index) : STRLEN(obj->name)) +
		1;
	    if(len > longest_name_len)
		longest_name_len = len;
	}

	/* Calculate the number of columns that we can fit based on
	 * the longest name length and the column width
	 */
	ncolumns = MAX((column_width / longest_name_len), 1);

	/* Calculate the number of objects to be displayed per
	 * column
	 */
	nobjs_per_column = (nobjs / ncolumns) +
	    (((nobjs % ncolumns) != 0) ? 1 : 0);
	if(nobjs_per_column <= 1)
	    nobjs_per_column = 1;

	/* Calculate the width of each column */
	per_column_width = MAX((column_width / ncolumns), 1) +
	    (((column_width % ncolumns) != 0) ? 1 : 0);

	/* Print the list */
	if(order_by_row)
	{
	    /* Print ordered by row */
	    for(i = 0; i < nobjs; i += ncolumns)
	    {
		for(j = 0; j < ncolumns; j++)
		{
		    k = i + j;
		    if(k >= nobjs)
			break;

		    obj = RLS_OBJ(g_list_nth_data(rec_obj_list, k));
		    if(obj == NULL)
			continue;

		    len = rls_print_obj_name(
			obj,
			color_codes_list,
			print_options
		    );
		    if((j + 1) < ncolumns)
		    {
			gint n;
			for(n = len; n < per_column_width; n++)
			    g_print(" ");
		    }
		}

		g_print("\n");
	    }
	}
	else
	{
	    /* Print ordered by column */
	    gint column_num;

	    for(i = 0; i < nobjs_per_column; i++)
	    {
		for(j = 0, column_num = 0;
		    j < nobjs;
		    j += nobjs_per_column, column_num++
		)
		{
		    k = i + j;
		    if(k >= nobjs)
			break;

		    obj = RLS_OBJ(g_list_nth_data(rec_obj_list, k));
		    if(obj == NULL)
			continue;

		    len = rls_print_obj_name(
			obj,
			color_codes_list,
			print_options
		    );
		    if((column_num + 1) < ncolumns)
		    {
			gint n;
			for(n = len; n < per_column_width; n++)
			    g_print(" ");
		    }
		}

		g_print("\n");
	    }
	}
}

/*
 *	Prints the list of objects using the single list format.
 */
static void rls_print_obj_list_single_column(
	GList *rec_obj_list, const gint nobjs,
	GList *color_codes_list,
	const rls_print_options print_options
)
{
	GList *glist;
	rls_obj_struct *obj;

	/* Print each object */
	for(glist = rec_obj_list; glist != NULL; glist = g_list_next(glist))
	{
	    obj = RLS_OBJ(glist->data);
	    if(obj == NULL)
		continue;

	    rls_print_obj_name(
		obj,
		color_codes_list,
		print_options
	    );
	    g_print("\n");
	}
}

/*
 *	Prints the recycled object on a single line separated by commas.
 */
static void rls_print_obj_list_comma(
	GList *rec_obj_list, const gint nobjs,
	GList *color_codes_list,
	const rls_print_options print_options
)
{
	gint nobjs_printed = 0;
	GList *glist;
	rls_obj_struct *obj;

	/* Print each object */
	for(glist = rec_obj_list; glist != NULL; glist = g_list_next(glist))
	{
	    obj = RLS_OBJ(glist->data);
	    if(obj == NULL)
		continue;

	    if(nobjs_printed > 0)
		g_print(",");

	    rls_print_obj_name(
		obj,
		color_codes_list,
		print_options
	    );

	    nobjs_printed++;
	}

	g_print("\n");
}

/*
 *	Prints the recycled object using the long list format.
 */
static void rls_print_obj_list_long(
	GList *rec_obj_list, const gint nobjs, const gulong total_size,
	GList *color_codes_list,
	const gint column_width,
	const rls_sort sort,
	const rls_print_options print_options
)
{
	time_t t, ct = time(NULL);
	mode_t m;
	struct tm *tm_ptr;
	struct stat *stat_ptr;
	gint len, longest_name_len;
	gchar *s;
	GList *glist, *uid_list = NULL, *gid_list = NULL;
	rls_obj_struct *obj;

	if(print_options & RLS_PRINT_UID_GID_NAMES)
	{
	    struct passwd *pwent;
	    struct group *gent;
	    rls_uid_struct *uid;

	    /* Get the user IDs list */
	    pwent = getpwent();
	    while(pwent != NULL)            
	    {
		uid = rls_uid_new();
		uid->name = STRDUP(pwent->pw_name);
		uid->id = pwent->pw_uid;
		uid_list = g_list_append(uid_list, uid);
		pwent = getpwent();
	    }
	    endpwent();

	    /* Get the group IDs list */
	    gent = getgrent();
	    while(gent != NULL)
	    {
		uid = rls_uid_new();
		uid->name = STRDUP(gent->gr_name);
		uid->id = gent->gr_gid;
		gid_list = g_list_append(gid_list, uid);
		gent = getgrent();
	    }
	    endgrent();
	}

	/* Find the object with the longest name */
	longest_name_len = 0;
	for(glist = rec_obj_list; glist != NULL; glist = g_list_next(glist))
	{
	    obj = RLS_OBJ(glist->data);
	    if(obj == NULL)
		continue;

	    len = STRLEN(obj->name);
	    if(len > longest_name_len)
		longest_name_len = len;
	}

	/* Print heading only if the total_size is positive, which
	 * suggests that all the objects were requested to be printed
	 */
	if(total_size > 0l)
	{
	    g_print(
		"Total %i  Size ",
		nobjs
	    );
	    rls_print_obj_size(total_size, print_options);
	    g_print("\n");
	}

	/* Print each object */
	for(glist = rec_obj_list; glist != NULL; glist = g_list_next(glist))
	{
	    obj = RLS_OBJ(glist->data);
	    if(obj == NULL)
		continue;

	    stat_ptr = &obj->stat;
	    m = stat_ptr->st_mode;

	    /* Type */
#ifdef S_ISREG
	    if(S_ISREG(m))
#else
	    if(FALSE)
#endif
		g_print("-");
#ifdef S_ISDIR
	    else if(S_ISDIR(m))
		g_print("d");
#endif
#ifdef S_ISLNK
	    else if(S_ISLNK(m))
		g_print("l");
#endif
#ifdef S_ISFIFO
	    else if(S_ISFIFO(m))
		g_print("p");
#endif
#ifdef S_ISBLK
	    else if(S_ISBLK(m))
		g_print("b");
#endif
#ifdef S_ISCHR
	    else if(S_ISCHR(m))
		g_print("c");
#endif
#ifdef S_ISSOCK
	    else if(S_ISSOCK(m))
		g_print("s");
#endif
	    else
		g_print("-");

	    /* Permissions */
	    g_print(
"%c%c%c%c%c%c%c%c%c   ",
		(m & S_IRUSR) ? 'r' : '-',
		(m & S_IWUSR) ? 'w' : '-',
		(m & S_IXUSR) ? 'x' : ((m & S_ISUID) ? 'S' : '-'),
		(m & S_IRGRP) ? 'r' : '-',
		(m & S_IWGRP) ? 'w' : '-',
		(m & S_IXGRP) ? 'x' : ((m & S_ISGID) ? 'S' : '-'),
		(m & S_IROTH) ? 'r' : '-',
		(m & S_IWOTH) ? 'w' : '-',
		(m & S_IXOTH) ? 'x' : ((m & S_ISVTX) ? 'S' : '-')
	    );

	    /* User */
	    if(TRUE)
	    {
		GList *glist2;
		rls_uid_struct *uid;

		for(glist2 = uid_list;
		    glist2 != NULL;
		    glist2 = g_list_next(glist2)
		)
		{
		    uid = RLS_UID(glist2->data);
		    if(uid == NULL)
			continue;

		    if(uid->id == stat_ptr->st_uid)
		    {
			s = g_strdup_printf(
			    "%s      ",
			    (const gchar *)uid->name
			);
			s[8] = '\0';
			g_print("%s ", s);
			g_free(s);
			break;
		    }
		}
		if(glist2 == NULL)
		{
		    s = g_strdup_printf(
			"%i      ",
			(gint)stat_ptr->st_uid
		    );
		    s[8] = '\0';
		    g_print("%s ", s);
		    g_free(s);
		}
	    }

	    /* Group */
	    if(print_options & RLS_PRINT_GROUP)
	    {
		GList *glist2;
		rls_uid_struct *uid;

		for(glist2 = gid_list;
		    glist2 != NULL;
		    glist2 = g_list_next(glist2)
		)
		{
		    uid = RLS_UID(glist2->data);
		    if(uid == NULL)
			continue;

		    if(uid->id == stat_ptr->st_gid)
		    {
			s = g_strdup_printf(
			    "%s      ",
			    (const gchar *)uid->name
			);
			s[8] = '\0';
			g_print("%s ", s);
			g_free(s);
			break;
		    }
		}
		if(glist2 == NULL)
		{
		    s = g_strdup_printf(
			"%i      ",
			(gint)stat_ptr->st_gid
		    );
		    s[8] = '\0';
		    g_print("%s ", s);
		    g_free(s);
		}
	    }

	    /* Size */
	    rls_print_obj_size(
		(gulong)stat_ptr->st_size,
		print_options
	    );
	    g_print(" ");

	    /* Time */
	    switch(sort)
	    {
	      case RLS_SORT_LAST_ACCESSED_TIME:
		t = (time_t)stat_ptr->st_atime;
		break;
	      case RLS_SORT_LAST_CHANGED_TIME:
		t = (time_t)stat_ptr->st_ctime;
		break;
	      default:
		t = (time_t)stat_ptr->st_mtime;
		break;
	    }
	    tm_ptr = localtime(&t);
	    if(print_options & RLS_PRINT_FULL_TIME)
	    {
		s = (gchar *)g_malloc(25 * sizeof(gchar));
		strftime(
		    (char *)s, 25,
		    "%a %b %e %H:%M:%S %Y",
		    tm_ptr
		);
		s[24] = '\0';
	    }
	    else
	    {
		s = (gchar *)g_malloc(13 * sizeof(gchar));
		/* Time is in the future? */
		if(t > ct)
		    strftime(
			(char *)s, 13, "%b %e %H:%M",
			tm_ptr
		    );
		/* Less than a year old? */
		else if((gulong)(ct - t) < (365l * 24l * 60l * 60l))
		    strftime(
			(char *)s, 13, "%b %e %H:%M",
			tm_ptr
		    );
		else
		    strftime(
			(char *)s, 13, "%b %e  %Y",
			tm_ptr
		    );
		s[12] = '\0';
	    }
	    g_print("%s ", s);
	    g_free(s);

	    /* Name */
	    rls_print_obj_name(
		obj,
		color_codes_list,
		print_options
	    );
	    g_print("\n");
	}

	g_list_foreach(uid_list, (GFunc)rls_uid_delete, NULL);
	g_list_free(uid_list);
	g_list_foreach(gid_list, (GFunc)rls_uid_delete, NULL);
	g_list_free(gid_list);
}

/*
 *	Prints the list of objects.
 */
static void rls_print_obj_list(
	GList *rec_obj_list, const gint nobjs, const gulong total_size,
	GList *color_codes_list,
	const gint column_width,
	const rls_sort sort,
	const rls_list_format list_format,
	const rls_print_options print_options
)
{
	if((rec_obj_list == NULL) || (nobjs <= 0) || (column_width <= 0))
	    return;

	switch(list_format)
	{
	  case RLS_LIST_FORMAT_TABLE_COLUMN:
	    rls_print_obj_list_table(
		rec_obj_list, nobjs,
		color_codes_list,
		column_width,
		FALSE,			/* Order by column */
		print_options
	    );
	    break;
	  case RLS_LIST_FORMAT_TABLE_ROW:
	    rls_print_obj_list_table(
		rec_obj_list, nobjs,
		color_codes_list,
		column_width,
		TRUE,			/* Order by row */
		print_options
	    );
	    break;
	  case RLS_LIST_FORMAT_SINGLE_COLUMN:
	    rls_print_obj_list_single_column(
		rec_obj_list, nobjs,
		color_codes_list,
		print_options
	    );
	    break;
	  case RLS_LIST_FORMAT_SINGLE_LINE_COMMA:
	    rls_print_obj_list_comma(
		rec_obj_list, nobjs,
		color_codes_list,
		print_options
	    );
	    break;
	  case RLS_LIST_FORMAT_LONG:
	    rls_print_obj_list_long(
		rec_obj_list, nobjs, total_size,
		color_codes_list,
		column_width,
		sort,
		print_options
	    );
	    break;
	}
}


int main(int argc, char *argv[])
{
	gint column_width = ATOI(g_getenv(RLS_ENV_NAME_COLUMNS));
	rls_sort sort = RLS_SORT_NAME;
	gboolean sort_reversed = FALSE;
	rls_list_format list_format = RLS_LIST_FORMAT_TABLE_COLUMN;
	rls_print_options print_options = RLS_PRINT_COLOR |
				RLS_PRINT_NAME_WITH_INDICIES |
				RLS_PRINT_UID_GID_NAMES |
				RLS_PRINT_GROUP;
	GList *names_list = NULL, *color_codes_list = NULL;
	edv_context_struct *ctx = EDVContextNew();
	gint i;
	const gchar *arg;

	EDVContextLoadConfigurationFile(ctx, NULL);

#define DO_FREE_LOCALS	{		\
 g_list_foreach(			\
  names_list, (GFunc)g_free, NULL		\
 );					\
 g_list_free(names_list);			\
 names_list = NULL;			\
					\
 g_list_foreach(			\
  color_codes_list, (GFunc)rls_color_code_delete, NULL \
 );					\
 g_list_free(color_codes_list);		\
 color_codes_list = NULL;		\
					\
 EDVContextDelete(ctx);			\
 ctx = NULL;				\
}

	if(column_width <= 0)
	    column_width = 80;

	/* Handle command line arguments for help and version only */
	if(argc >= 2)
	{
	    /* Parse arguments */
	    for(i = 1; i < argc; i++)
	    {
		arg = argv[i];
		if(arg == NULL)
		    continue;

		/* Help */
		if(!g_strcasecmp(arg, "--help") ||
		   !g_strcasecmp(arg, "-help") ||
		   (!strcmp(arg, "--h") && (argc == 2)) ||
		   (!strcmp(arg, "-h") && (argc == 2))
		)
		{
		    print_usage(argv[0]);
		    DO_FREE_LOCALS
		    return(0);
		}
		/* Version */
		else if(!g_strcasecmp(arg, "--version") ||
			!g_strcasecmp(arg, "-version")
		)
		{
		    g_print(
"Endeavour Mark II Recycle Bin List Version " PROG_VERSION "\n"
PROG_COPYRIGHT
		    );
		    DO_FREE_LOCALS
		    return(0);
		}
		/* Single Column List Format */
		else if(!strcmp(arg, "--1") ||
			!strcmp(arg, "-1")
		)
		{
		    list_format = RLS_LIST_FORMAT_SINGLE_COLUMN;
		}
		/* Table Format Ordered By Column */
		else if(!strcmp(arg, "--C") ||
			!strcmp(arg, "-C")
		)
		{
		    list_format = RLS_LIST_FORMAT_TABLE_COLUMN;
		}
		/* Table Format Ordered By Row */
		else if(!strcmp(arg, "--x") ||
			!strcmp(arg, "-x")
		)
		{
		    list_format = RLS_LIST_FORMAT_TABLE_ROW;
		}
		/* Single Line Comma-Separated */
		else if(!strcmp(arg, "--m") ||
			!strcmp(arg, "-m")
		)
		{
		    list_format = RLS_LIST_FORMAT_SINGLE_LINE_COMMA;
		}
		/* Long List Format */
		else if(!strcmp(arg, "--l") ||
			!strcmp(arg, "-l")
		)
		{
		    list_format = RLS_LIST_FORMAT_LONG;
		}
		/* Sort By Delete order */
		else if(!strcmp(arg, "--U") ||
			!strcmp(arg, "-U")
		)
		{
		    sort = RLS_SORT_NONE;
		}
		/* Sort By Index */
		else if(!strcmp(arg, "--i") ||
			!strcmp(arg, "-i")
		)
		{
		    sort = RLS_SORT_INDEX;
		}
		/* Sort By Size */
		else if(!strcmp(arg, "--S") ||
			!strcmp(arg, "-S")
		)
		{
		    sort = RLS_SORT_SIZE;
		}
		/* Sort By Last Accessed Time */
		else if(!strcmp(arg, "--u") ||
			!strcmp(arg, "-u")
		)
		{
		    sort = RLS_SORT_LAST_ACCESSED_TIME;
		}
		/* Sort By Last Modified Time */
		else if(!strcmp(arg, "--t") ||
			!strcmp(arg, "-t")
		)
		{
		    sort = RLS_SORT_LAST_MODIFIED_TIME;
		}
		/* Sort By Last Changed Time */
		else if(!strcmp(arg, "--c") ||
			!strcmp(arg, "-c")
		)
		{
		    sort = RLS_SORT_LAST_CHANGED_TIME;
		}
		/* Sort Reversed */
		else if(!g_strcasecmp(arg, "--reverse") ||
			!g_strcasecmp(arg, "-reverse") ||
			!strcmp(arg, "--r") ||
			!strcmp(arg, "-r")
		)
		{
		    sort_reversed = TRUE;
		}
		/* Color */
		else if(!g_strcasecmp(arg, "--color") ||
			!g_strcasecmp(arg, "-color")
		)
		{
		    i++;
		    arg = (i < argc) ? argv[i] : NULL;
		    if(arg != NULL)
		    {
			if(!g_strcasecmp(arg, "never"))
			    print_options &= ~RLS_PRINT_COLOR;
			else if(!g_strcasecmp(arg, "always"))
			    print_options |= RLS_PRINT_COLOR;
			else if(!g_strcasecmp(arg, "auto"))
			    print_options |= RLS_PRINT_COLOR;
			else
			{
			    g_printerr(
"%s: Invalid value, valid values are; never always auto\n",
				argv[i - 1]
			    );
			    DO_FREE_LOCALS
			    return(2);
			}
		    }
		    else
		    {
		        g_printerr(
"%s: Requires argument.\n",
			    argv[i - 1]
			);
			DO_FREE_LOCALS
			return(2);
		    }
		}
		/* Print Names Without Indicies */
		else if(!g_strcasecmp(arg, "--clean-names") ||
			!g_strcasecmp(arg, "-clean-names") ||
			!strcmp(arg, "--N") ||
			!strcmp(arg, "-N")
		)
		{
		    print_options &= ~RLS_PRINT_NAME_WITH_INDICIES;
		}
		/* Print Original Location */
		else if(!g_strcasecmp(arg, "--original-location") ||
			!g_strcasecmp(arg, "-original-location") ||
			!strcmp(arg, "--O") ||
			!strcmp(arg, "-O")
		)
		{
		    print_options |= RLS_PRINT_ORIGINAL_LOCATION;
		}
		/* Full Time */
		else if(!g_strcasecmp(arg, "--full-time") ||
			!g_strcasecmp(arg, "-full-time")
		)
		{
		    print_options |= RLS_PRINT_FULL_TIME;
		}
		/* Print Numeric UID & GID Values */
		else if(!g_strcasecmp(arg, "--numeric-uid-gid") ||
			!g_strcasecmp(arg, "-numeric-uid-gid") ||
			!strcmp(arg, "--n") ||
			!strcmp(arg, "-n")
		)
		{
		    print_options &= ~RLS_PRINT_UID_GID_NAMES;
		}
		/* Print Friendly Sizes */
		else if(!g_strcasecmp(arg, "--human-readable") ||
			!g_strcasecmp(arg, "-human-readable") ||
			!strcmp(arg, "--h") ||
			!strcmp(arg, "-h")
		)
		{
		    print_options |= RLS_PRINT_FRIENDLY_SIZES;
		}
		/* Print Friendly Sizes in 1000 blocks*/
		else if(!g_strcasecmp(arg, "--si") ||
			!g_strcasecmp(arg, "-si") ||
			!strcmp(arg, "--H") ||
			!strcmp(arg, "-H")
		)
		{
		    print_options |= RLS_PRINT_FRIENDLY_SIZES_1000;
		}
		/* Do Not Print Group */
		else if(!g_strcasecmp(arg, "--no-group") ||
			!g_strcasecmp(arg, "-no-group") ||
			!strcmp(arg, "--G") ||
			!strcmp(arg, "-G")
		)
		{
		    print_options &= ~RLS_PRINT_GROUP;
		}
		/* Width */
		else if(!g_strcasecmp(arg, "--width") ||
			!g_strcasecmp(arg, "-width") ||
			!strcmp(arg, "--w") ||
			!strcmp(arg, "-w")
		)
		{
		    i++;
		    arg = (i < argc) ? argv[i] : NULL;
		    if(arg != NULL)
		    {
			column_width = MAX(ATOI(arg), 1);
		    }
		    else
		    {
		        g_printerr(
"%s: Requires argument.\n",
			    argv[i - 1]
			);
			DO_FREE_LOCALS
			return(2);
		    }
		}
		/* Single character argument? */
		else if((*arg == '-') ? (arg[1] != '-') : FALSE)
		{
		    const gchar *v = arg + 1;
		    gchar c;

		    while(*v != '\0')
		    {
			c = *v;
			if(c == '1')
		        {
			    list_format = RLS_LIST_FORMAT_SINGLE_COLUMN;
			}
			else if(c == 'C')
		        {
			    list_format = RLS_LIST_FORMAT_TABLE_COLUMN;
		        }
		        else if(c == 'x')
		        {
			    list_format = RLS_LIST_FORMAT_TABLE_ROW;
		        }
		        else if(c == 'm')
		        {
			    list_format = RLS_LIST_FORMAT_SINGLE_LINE_COMMA;
		        }
		        else if(c == 'l')
		        {
			    list_format = RLS_LIST_FORMAT_LONG;
		        }
		        else if(c == 'U')
		        {
			    sort = RLS_SORT_NONE;
		        }
		        else if(c == 'i')
		        {
			    sort = RLS_SORT_INDEX;
		        }
		        else if(c == 'S')
		        {
			    sort = RLS_SORT_SIZE;
		        }
		        else if(c == 'u')
		        {
			    sort = RLS_SORT_LAST_ACCESSED_TIME;
		        }
		        else if(c == 't')
		        {
			    sort = RLS_SORT_LAST_MODIFIED_TIME;
		        }
		        else if(c == 'c')
		        {
			    sort = RLS_SORT_LAST_CHANGED_TIME;
		        }
		        else if(c == 'r')
		        {
			    sort_reversed = TRUE; 
		        }
		        else if(c == 'N')
		        {
			    print_options &= ~RLS_PRINT_NAME_WITH_INDICIES;
		        }
		        else if(c == 'O')
		        {
			    print_options |= RLS_PRINT_FULL_TIME;
		        }
		        else if(c == 'n')
		        {
			    print_options &= ~RLS_PRINT_UID_GID_NAMES;
		        }
		        else if(c == 'h')
		        {
			    print_options |= RLS_PRINT_FRIENDLY_SIZES;
		        }
		        else if(c == 'H')
		        {
			    print_options |= RLS_PRINT_FRIENDLY_SIZES_1000;
		        }
		        else if(c == 'G')
		        {
			    print_options &= ~RLS_PRINT_GROUP;
		        }
		        else
		        {
		            g_printerr(
"-%c: Unsupported argument.\n",
			        c
			    );
			    DO_FREE_LOCALS
			    return(2);
		        }
			v++;
		    }
		}
		/* Object? */
		else if((*arg != '-') && (*arg != '+'))
		{
		    names_list = g_list_append(
			names_list, STRDUP(arg)
		    );
		}
		else
		{
		    g_printerr(
"%s: Unsupported argument.\n",
			arg
		    );
		    DO_FREE_LOCALS
		    return(2);
		}
	    }
	}

	/* Build the colors list */
	if(print_options & RLS_PRINT_COLOR)
	{
	    rls_color_code_struct *c;

	    /* Create the colors list based on the order of the
	     * RLS_COLOR_CODE_* enumerations
	     */

	    /* RLS_COLOR_CODE_UNKNOWN */
	    c = rls_color_code_new();
	    c->code = 0;
	    c->attributes = 0;
	    color_codes_list = g_list_append(color_codes_list, c);

	    /* RLS_COLOR_CODE_REGULAR */
	    c = rls_color_code_new();
	    c->code = 0;
	    c->attributes = 0;
	    color_codes_list = g_list_append(color_codes_list, c);

	    /* RLS_COLOR_CODE_DIRECTORY */
	    c = rls_color_code_new();
	    c->code = 34;
	    c->attributes = 1;
	    color_codes_list = g_list_append(color_codes_list, c);

	    /* RLS_COLOR_CODE_LINK */
	    c = rls_color_code_new();
	    c->code = 36;
	    c->attributes = 1;
	    color_codes_list = g_list_append(color_codes_list, c);

	    /* RLS_COLOR_CODE_FIFO */
	    c = rls_color_code_new();
	    c->code = 31;
	    c->attributes = 1;
	    color_codes_list = g_list_append(color_codes_list, c);

	    /* RLS_COLOR_CODE_DEVICE_BLOCK */
	    c = rls_color_code_new();
	    c->code = 33;
	    c->attributes = 1;
	    color_codes_list = g_list_append(color_codes_list, c);

	    /* RLS_COLOR_CODE_DEVICE_CHARACTER */
	    c = rls_color_code_new();
	    c->code = 33;
	    c->attributes = 1;
	    color_codes_list = g_list_append(color_codes_list, c);

	    /* RLS_COLOR_CODE_SOCKET */
	    c = rls_color_code_new();
	    c->code = 35;
	    c->attributes = 1;
	    color_codes_list = g_list_append(color_codes_list, c);

	    /* RLS_COLOR_CODE_EXECUTABLE */
	    c = rls_color_code_new();
	    c->code = 32;
	    c->attributes = 1;
	    color_codes_list = g_list_append(color_codes_list, c);

	    /* Check if there are user defined colors set in the
	     * environment
	     */
	    arg = g_getenv(RLS_ENV_NAME_RLS_COLORS);
	    if(arg == NULL)
		arg = g_getenv(RLS_ENV_NAME_LS_COLORS);
	    if(arg != NULL)
	    {
		/* Get each color entry */
		gchar **strv = g_strsplit(arg, ":", -1);
		if(strv != NULL)
		{
		    const gchar *type_str, *val_str;
		    gchar **entry_strv;

		    /* Iterate through each color entry */
		    for(i = 0; strv[i] != NULL; i++)
		    {
			/* Parse this color entry */
			entry_strv = g_strsplit(strv[i], "=", -1);
			if(entry_strv == NULL)
			    continue;

			/* Type not available? */
			if(entry_strv[0] == NULL)
			{
			    g_strfreev(entry_strv);
			    continue;
			}
			/* Value not available? */
			if(entry_strv[1] == NULL)
			{
			    g_strfreev(entry_strv);
			    continue;
			}

			/* Get the type and value */
			type_str = entry_strv[0];
			val_str = entry_strv[1];

#define SET_COLOR(_c_)	{				\
 if(c != NULL) {					\
  gchar **val_strv = g_strsplit(val_str, ";", -1);	\
  if(val_strv != NULL) {				\
   /* Code */						\
   if(val_strv[0] != NULL) {				\
    c->code = ATOI(val_strv[0]);			\
    /* Attributes */					\
    if(val_strv[1] != NULL)				\
     c->attributes = ATOI(val_strv[1]);			\
   }							\
   g_strfreev(val_strv);				\
  }							\
 }							\
}

			/* Directory */
			if(!g_strcasecmp(type_str, "di"))
			{
			    c = RLS_COLOR_CODE(g_list_nth_data(
				color_codes_list,
				RLS_COLOR_CODE_DIRECTORY
			    ));
			    SET_COLOR(c);
			}
			/* Link */
			else if(!g_strcasecmp(type_str, "ln"))
			{
			    c = RLS_COLOR_CODE(g_list_nth_data(
				color_codes_list,
				RLS_COLOR_CODE_LINK
			    ));
			    SET_COLOR(c);
			}
			/* FIFO */
			else if(!g_strcasecmp(type_str, "pi"))
			{
			    c = RLS_COLOR_CODE(g_list_nth_data(
				color_codes_list,
				RLS_COLOR_CODE_FIFO
			    ));
			    SET_COLOR(c);
			}
			/* Device Block */
			else if(!g_strcasecmp(type_str, "bd"))
			{
			    c = RLS_COLOR_CODE(g_list_nth_data(
				color_codes_list,
				RLS_COLOR_CODE_DEVICE_BLOCK
			    ));
			    SET_COLOR(c);
			}
			/* Device Character */
			else if(!g_strcasecmp(type_str, "cd"))
			{
			    c = RLS_COLOR_CODE(g_list_nth_data(
				color_codes_list,
				RLS_COLOR_CODE_DEVICE_CHARACTER
			    ));
			    SET_COLOR(c);
			}
			/* Socket */
			else if(!g_strcasecmp(type_str, "sk"))
			{
			    c = RLS_COLOR_CODE(g_list_nth_data(
				color_codes_list,
				RLS_COLOR_CODE_SOCKET
			    ));
			    SET_COLOR(c);
			}
			/* Executable */
			else if(!g_strcasecmp(type_str, "ex"))
			{
			    c = RLS_COLOR_CODE(g_list_nth_data(
				color_codes_list,
				RLS_COLOR_CODE_EXECUTABLE
			    ));
			    SET_COLOR(c);
			}

#undef SET_COLOR

			g_strfreev(entry_strv);
		    }

		    g_strfreev(strv);
		}
	    }
	}

	/* Any particular files specified? */
	if(names_list != NULL)
	{
	    const gchar *arg, *name;
	    gint nobjs, nmatches;
	    GList	*glist,
			*glist2,
			*rec_obj_list = rls_get_listing(
			    ctx, &nobjs, NULL,
			    sort, sort_reversed
			),
			*rec_obj_print_list = NULL;
	    rls_obj_struct *obj;

	    /* Iterate through each file specified and create a list
	     * of objects to print
	     */
	    for(glist = names_list; glist != NULL; glist = g_list_next(glist))
	    {
		arg = (const gchar *)glist->data;
		if(arg == NULL)
		    continue;

		/* Check if this argument matches one of the names of
		 * the recycled objects
		 */
		nmatches = 0;
		for(glist2 = rec_obj_list; glist2 != NULL; glist2 = g_list_next(glist2))
		{
		    obj = RLS_OBJ(glist2->data);
		    if(obj == NULL)
			continue;

		    name = obj->name;
		    if(name == NULL)
			continue;

		    if(fnmatch(arg, name, 0) == 0)
		    {
			/* Add this object to the list of objects to
			 * print
			 */
			rec_obj_print_list = g_list_append(
			    rec_obj_print_list, obj
			);
			nmatches++;
		    }
		}
		if(nmatches == 0)
		    g_printerr("%s: No such object.\n", arg);
	    }

	    /* Print the list of objects to print */
	    rls_print_obj_list(
		rec_obj_print_list, g_list_length(rec_obj_print_list), 0l,
		color_codes_list,
		column_width,
		sort,
		list_format,
		print_options
	    );

	    /* Delete the object lists */
	    g_list_free(rec_obj_print_list);

	    g_list_foreach(rec_obj_list, (GFunc)rls_obj_delete, NULL);
	    g_list_free(rec_obj_list);
	}
	else
	{
	    /* Print the list of all recycled objects */
	    gint nobjs;
	    gulong total_size;
	    GList *rec_obj_list = rls_get_listing(
		ctx, &nobjs, &total_size,
		sort, sort_reversed
	    );
	    rls_print_obj_list(
		rec_obj_list, nobjs, total_size,
		color_codes_list,
		column_width,
		sort,
		list_format,
		print_options
	    );

	    /* Delete the object list */
	    g_list_foreach(rec_obj_list, (GFunc)rls_obj_delete, NULL);
	    g_list_free(rec_obj_list);
	}

	EDVContextSync(ctx);

	DO_FREE_LOCALS

	return(0);
#undef DO_FREE_LOCALS
}
