#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <glib.h>

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

#include "cfg.h"
#include "edvtypes.h"
#include "edvdate.h"
#include "edvarchobj.h"
#include "endeavour.h"
#include "edvarchfio.h"
#include "edvutils.h"
#include "edvcfglist.h"
#include "config.h"


static gboolean EDVArchFIOCheckFilterPath(
	const gchar **list, gint total,		/* Can be empty */
	const gchar *path
);

/* Line Parsing */
static void EDVArchFIOParseLineARJ(
	edv_archive_object_struct *obj, const gchar *buf
);
static void EDVArchFIOParseLineLHA(
	edv_archive_object_struct *obj, const gchar *buf,
	const struct tm *cur_mtime_buf
);
static void EDVArchFIOParseLineRAR(
	edv_archive_object_struct *obj, const gchar *buf
);
static void EDVArchFIOParseLineRPM(
	edv_archive_object_struct *obj, const gchar *buf
);
static void EDVArchFIOParseLineTar(
	edv_archive_object_struct *obj, const gchar *buf
);
static void EDVArchFIOParseLineZip(
	edv_archive_object_struct *obj, const gchar *buf
);

/* Get Stats For A Object In Archive */
static edv_archive_object_struct *EDVArchFIOStatARJ(
	edv_core_struct *core_ptr,
	const gchar *arch_obj, const gchar *path
);
static edv_archive_object_struct *EDVArchFIOStatLHA(
	edv_core_struct *core_ptr,
	const gchar *arch_obj, const gchar *path
);
static edv_archive_object_struct *EDVArchFIOStatRAR(
	edv_core_struct *core_ptr,
	const gchar *arch_obj, const gchar *path
);
static edv_archive_object_struct *EDVArchFIOStatRPM(
	edv_core_struct *core_ptr,
	const gchar *arch_obj, const gchar *path
);
static edv_archive_object_struct *EDVArchFIOStatTar(
	edv_core_struct *core_ptr,
	const gchar *arch_obj, const gchar *path,
	gboolean is_compress_compressed,
	gboolean is_gzip_compressed,
	gboolean is_bzip2_compressed
);
static edv_archive_object_struct *EDVArchFIOStatZip(
	edv_core_struct *core_ptr,
	const gchar *arch_obj, const gchar *path
);
edv_archive_object_struct *EDVArchFIOStat(
	edv_core_struct *core_ptr,
	const gchar *arch_obj, const gchar *path,
	const gchar *password
);

/* Get Stats For All Objects In Archive */
static edv_archive_object_struct **EDVArchFIOGetListingARJ(
	edv_core_struct *core_ptr,
	const gchar *arch_obj, gint *total,
	const gchar **path, gint npaths
);
static edv_archive_object_struct **EDVArchFIOGetListingLHA(
	edv_core_struct *core_ptr,
	const gchar *arch_obj, gint *total,
	const gchar **path, gint npaths
);
static edv_archive_object_struct **EDVArchFIOGetListingRAR(
	edv_core_struct *core_ptr,
	const gchar *arch_obj, gint *total,
	const gchar **path, gint npaths
);
static edv_archive_object_struct **EDVArchFIOGetListingRPM(
	edv_core_struct *core_ptr,
	const gchar *arch_obj, gint *total,
	const gchar **path, gint npaths
);
static edv_archive_object_struct **EDVArchFIOGetListingTar(
	edv_core_struct *core_ptr,
	const gchar *arch_obj, gint *total,
	const gchar **path, gint npaths,
	gboolean is_compress_compressed,
	gboolean is_gzip_compressed,
	gboolean is_bzip2_compressed
);
static edv_archive_object_struct **EDVArchFIOGetListingZip(
	edv_core_struct *core_ptr,
	const gchar *arch_obj, gint *total,
	const gchar **path, gint npaths
);
edv_archive_object_struct **EDVArchFIOGetListing(
	edv_core_struct *core_ptr,
	const gchar *arch_obj, gint *total,
	const gchar **path, gint npaths,
	const gchar *password
);

/* Get Archive Comment */
static gchar **EDVArchFIOGetCommentRAR(
	edv_core_struct *core_ptr, const gchar *arch_obj,
	gint *total
);
static gchar **EDVArchFIOGetCommentZip(
	edv_core_struct *core_ptr, const gchar *arch_obj,
	gint *total
);
gchar **EDVArchFIOGetComment(
	edv_core_struct *core_ptr, const gchar *arch_obj,
	gint *total
);

/* Set Archive Comment */
static void EDVArchFIOSetCommentWriteCommentSpool(
	const gchar *path,
	const gchar *prefix, const gchar *postfix,
	const gchar *comment
);
static gint EDVArchFIOSetCommentARJ(
	edv_core_struct *core_ptr, const gchar *arch_obj,
	const gchar *comment
);
static gint EDVArchFIOSetCommentRAR(
	edv_core_struct *core_ptr, const gchar *arch_obj,
	const gchar *comment
);
static gint EDVArchFIOSetCommentZip(
	edv_core_struct *core_ptr, const gchar *arch_obj,
	const gchar *comment
);
gint EDVArchFIOSetComment(
	edv_core_struct *core_ptr, const gchar *arch_obj,
	gchar **strv, gint strc
);


#define ISCR(c)		(((c) == '\n') || ((c) == '\r'))

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

#define UNLINK(p)	(((p) != NULL) ? (gint)unlink((const char *)(p)) : -1)


static gchar *G_STRCAT(gchar *s, const gchar *s2)
{
        if(s != NULL) {
            if(s2 != NULL) {
                gchar *sr = g_strconcat(s, s2, NULL);
                g_free(s);
                s = sr;
            }
        } else {
            if(s2 != NULL)
                s = STRDUP(s2);
            else
                s = STRDUP("");
        }
        return(s);
}


/*
 *	Checks if the given path matches one of the paths in the list
 *	and returns TRUE if a match is made.
 *
 *	The given path will be coppied and have any post ending
 *	characters removed while the filter check is being done.
 *
 *	If the list of path is empty then TRUE is always returned.
 */
static gboolean EDVArchFIOCheckFilterPath(
	const gchar **list, gint total,		/* Can be empty */
	const gchar *path
)
{
	/* If no path is given then no match can ever be made so always
	 * return FALSE
	 */
	if(path == NULL)
	    return(FALSE);

	/* List of filter paths given? */
	if((list != NULL) && (total > 0))
	{
	    /* Copy the given path and remove any characters beyond the
	     * first space
	     */
	    gint i;
	    const gchar *fpath;
	    gchar *s, *dpath = STRDUP(path);

	    /* Remove first blank character */
	    for(s = dpath; *s != '\0'; s++)
	    {
		if(ISSPACE(*s))
		{
		    *s = '\0';
		    break;
		}
	    }

	    /* Check if the path is in the list of filter paths */
	    for(i = 0; i < total; i++)
	    {
		fpath = list[i];
		if(STRISEMPTY(fpath))
		    continue;

		if(!strcmp((const char *)fpath, (const char *)dpath))
		    break;
	    }

	    g_free(dpath);		/* Delete coppied path */

	    /* Matched path in list of filter paths? */
	    return((i < total) ? TRUE : FALSE);
	}
	else
	{
	    /* No list of filter paths given so we should always treat
	     * this as a match
	     */
	    return(TRUE);
	}
}

/*
 *	Parses the given null terminated string buf which is from a
 *	ARJ Packager list output.
 *
 *	The given string contain four lines per object description
 *	per ARJ Packager list output format.
 */
static void EDVArchFIOParseLineARJ(
	edv_archive_object_struct *obj, const gchar *buf
)
{
	gint c;
	const gchar *buf_ptr = buf;
	struct tm mtime_buf;


	if((obj == NULL) || (buf_ptr == NULL))
	    return;

	memset(&mtime_buf, 0x00, sizeof(struct tm));


	/* Sample format line:

002) dir/file.ext
 11 UNIX           1293        638 0.493 03-08-21 00:16:06 -rw-r--r-- ---  +1
				   DTA   03-08-25 20:00:36
				   DTC   03-08-21 00:16:06

	*/

	/* Index (ignored) */
	while(!ISSPACE(*buf_ptr) && (*buf_ptr != '\0'))
	    buf_ptr++;
	while(ISSPACE(*buf_ptr))
	    buf_ptr++;

	/* Full Path & Name */
	if(*buf_ptr != '\0')
	{
	    gint len;
	    const gchar *s = buf_ptr;

	    while(!ISSPACE(*buf_ptr) && (*buf_ptr != '\0'))
		buf_ptr++;

	    len = (gint)(buf_ptr - s);

	    /* Get full path */
	    g_free(obj->full_path);
	    obj->full_path = (gchar *)g_malloc(
		(len + 1) * sizeof(gchar)
	    );
	    if(len > 0)
		strncpy((char *)obj->full_path, (const char *)s, len);
	    obj->full_path[len] = '\0';

	    /* Strip tailing deliminators */
	    StripPath((char *)obj->full_path);

	    /* Get name from full path */
	    g_free(obj->name);
	    obj->name = STRDUP(EDVGetPathName(obj->full_path));
	}
	while(ISSPACE(*buf_ptr))
	    buf_ptr++;

	/* Rev (ignored) */
	while(!ISSPACE(*buf_ptr) && (*buf_ptr != '\0'))
	    buf_ptr++;
	while(ISSPACE(*buf_ptr))
	    buf_ptr++;

	/* Platform (ignored) */
	while(!ISSPACE(*buf_ptr) && (*buf_ptr != '\0'))
	    buf_ptr++;
	while(ISSPACE(*buf_ptr))
	    buf_ptr++;

	/* Size */
	obj->size = (gulong)ATOL(buf_ptr);
	while(!ISSPACE(*buf_ptr) && (*buf_ptr != '\0'))
	    buf_ptr++;
	while(ISSPACE(*buf_ptr))
	    buf_ptr++;

	/* Compressed Size */
	obj->compressed_size = (gulong)ATOL(buf_ptr);
	while(!ISSPACE(*buf_ptr) && (*buf_ptr != '\0'))
	    buf_ptr++;
	while(ISSPACE(*buf_ptr))
	    buf_ptr++;

	/* Compression Ratio */
	obj->compression_ratio = ATOF(buf_ptr);
	while(!ISSPACE(*buf_ptr) && (*buf_ptr != '\0'))
	    buf_ptr++;
	while(ISSPACE(*buf_ptr))
	    buf_ptr++;

	/* Year-month-date */
	if(*buf_ptr != '\0')
	{
	    gint year_2digit;

	    /* Get 2-digit year (use "imperfect" Y2K fix) */
	    if(*buf_ptr == '0')
		buf_ptr++;
	    year_2digit = ATOI(buf_ptr);
	    mtime_buf.tm_year = (int)((year_2digit < 60) ?
		(year_2digit + 100) : year_2digit
	    );
	    while((*buf_ptr != '-') && (*buf_ptr != '\0'))
		buf_ptr++;
	    if(*buf_ptr == '-')
		buf_ptr++;

	    /* Month */
	    if(*buf_ptr == '0')
		buf_ptr++;
	    mtime_buf.tm_mon = (int)ATOI(buf_ptr) - 1;
	    while((*buf_ptr != '-') && (*buf_ptr != '\0'))
		buf_ptr++;
	    if(*buf_ptr == '-')
		buf_ptr++;

	    /* Day */
	    if(*buf_ptr == '0')
		buf_ptr++;
	    mtime_buf.tm_mday = (int)ATOI(buf_ptr);
	    while(!ISSPACE(*buf_ptr) && (*buf_ptr != '\0'))
		buf_ptr++;
	}
	while(ISSPACE(*buf_ptr))
	    buf_ptr++;

	/* Hour:minutes:seconds */
	if(*buf_ptr != '\0')
	{
	    /* Hour */
	    if(*buf_ptr == '0')
		buf_ptr++;
	    mtime_buf.tm_hour = (int)ATOI(buf_ptr) - 1;
	    while((*buf_ptr != ':') && (*buf_ptr != '\0'))
		buf_ptr++;
	    if(*buf_ptr == ':')
		buf_ptr++;

	    /* Minutes */
	    if(*buf_ptr == '0')
		buf_ptr++;
	    mtime_buf.tm_min = (int)ATOI(buf_ptr);
	    while((*buf_ptr != ':') && (*buf_ptr != '\0'))
		buf_ptr++;
	    if(*buf_ptr == ':')
		buf_ptr++;

	    /* Seconds */
	    if(*buf_ptr == '0')
		buf_ptr++;
	    mtime_buf.tm_sec = (int)ATOI(buf_ptr);
	    while(!ISSPACE(*buf_ptr) && (*buf_ptr != '\0'))
		buf_ptr++;
	}

	/* Parse time compoents and generate system time since we now
	 * have enough information to do that
	 */
	obj->access_time = (gulong)mktime(&mtime_buf);
	obj->modify_time = obj->access_time;
	obj->change_time = obj->access_time;

	while(ISSPACE(*buf_ptr))
	    buf_ptr++;


	/* Type */
	c = *buf_ptr;
	if(c != '\0')
	{
	    if(c == 'd')
		obj->type = EDV_OBJECT_TYPE_DIRECTORY;
	    else if(c == 'l')
		obj->type = EDV_OBJECT_TYPE_LINK;
	    else if(c == 'p')
		obj->type = EDV_OBJECT_TYPE_FIFO;
	    else if(c == 'b')
		obj->type = EDV_OBJECT_TYPE_DEVICE_BLOCK;
	    else if(c == 'c')
		obj->type = EDV_OBJECT_TYPE_DEVICE_CHARACTER;
	    else if(c == 's')
		obj->type = EDV_OBJECT_TYPE_SOCKET;
	    else
		obj->type = EDV_OBJECT_TYPE_FILE;

	    buf_ptr++;
	}

	/* Permissions */
	obj->permissions = 0x00000000;

	/* Owner read/write/execute */
	c = *buf_ptr;
	if(c != '\0')
	{
	    obj->permissions |= (c == 'r') ? EDV_PERMISSION_UREAD : 0;
	    buf_ptr++;
	}
	c = *buf_ptr;
	if(c != '\0')
	{
	    obj->permissions |= (c == 'w') ? EDV_PERMISSION_UWRITE : 0;
	    buf_ptr++;
	}
	c = *buf_ptr;
	if(c != '\0')
	{
	    obj->permissions |= (c == 'x') ? EDV_PERMISSION_UEXECUTE : 0;
	    buf_ptr++;
	}

	/* Group read/write/execute */
	c = *buf_ptr;
	if(c != '\0')
	{
	    obj->permissions |= (c == 'r') ? EDV_PERMISSION_GREAD : 0;
	    buf_ptr++;
	}
	c = *buf_ptr;
	if(c != '\0')
	{
	    obj->permissions |= (c == 'w') ? EDV_PERMISSION_GWRITE : 0;
	    buf_ptr++;
	}
	c = *buf_ptr;
	if(c != '\0')
	{
	    obj->permissions |= (c == 'x') ? EDV_PERMISSION_GEXECUTE : 0;
	    buf_ptr++;
	}

	/* Anonymous read/write/execute */
	c = *buf_ptr;
	if(c != '\0')
	{
	    obj->permissions |= (c == 'r') ? EDV_PERMISSION_AREAD : 0;
	    buf_ptr++;
	}
	c = *buf_ptr;
	if(c != '\0')
	{
	    obj->permissions |= (c == 'w') ? EDV_PERMISSION_AWRITE : 0;
	    buf_ptr++;
	}
	c = *buf_ptr;
	if(c != '\0')
	{
	    obj->permissions |= (c == 'x') ? EDV_PERMISSION_AEXECUTE : 0;
	    buf_ptr++;
	}

	while(ISSPACE(*buf_ptr))
	    buf_ptr++;

	/* GUI (ignored) */
	while(!ISSPACE(*buf_ptr) && (*buf_ptr != '\0'))
	    buf_ptr++;
	while(ISSPACE(*buf_ptr))
	    buf_ptr++;

	/* BPMGS (use as method) */
	if(*buf_ptr != '\0')
	{
	    gint len;
	    const gchar *s = buf_ptr;

	    while(!ISSPACE(*buf_ptr) && (*buf_ptr != '\0'))
		buf_ptr++;

	    len = (gint)(buf_ptr - s);

	    g_free(obj->method);
	    obj->method = (gchar *)g_malloc((len + 1) * sizeof(gchar));
	    if(len > 0)
		strncpy((char *)obj->method, (const char *)s, len);
	    obj->method[len] = '\0';
	}
	while(ISSPACE(*buf_ptr))
	    buf_ptr++;

	/* Ignore other dates and a whole bunch of other stuff */
}

/*
 *      Parses the given null terminated string buf which is from a
 *	LHA Archive list output.
 */
static void EDVArchFIOParseLineLHA(
	edv_archive_object_struct *obj, const gchar *buf,
	const struct tm *cur_mtime_buf
)
{
	gint c;
	const gchar *buf_ptr = buf;
	struct tm mtime_buf;


	if((obj == NULL) || (buf_ptr == NULL))
	    return;

	if(cur_mtime_buf != NULL)
	    memcpy(&mtime_buf, cur_mtime_buf, sizeof(struct tm));
	else
	    memset(&mtime_buf, 0x00, sizeof(struct tm));


	/* Sample format line:

-rw-r--r-- 500/500 1058 2643 40.0% -lh5- bf09 Aug 26 18:32 dir/file.ext

	 */

	/* Type */
	c = *buf_ptr;
	if(c != '\0')
	{
	    if(c == 'd')
		obj->type = EDV_OBJECT_TYPE_DIRECTORY;
	    else if(c == 'l')
		obj->type = EDV_OBJECT_TYPE_LINK;
	    else if(c == 'p')
		obj->type = EDV_OBJECT_TYPE_FIFO;
	    else if(c == 'b')
		obj->type = EDV_OBJECT_TYPE_DEVICE_BLOCK;
	    else if(c == 'c')
		obj->type = EDV_OBJECT_TYPE_DEVICE_CHARACTER;
	    else if(c == 's')
		obj->type = EDV_OBJECT_TYPE_SOCKET;
	    else
		obj->type = EDV_OBJECT_TYPE_FILE;

	    buf_ptr++;
	}

	/* Permissions */
	obj->permissions = 0x00000000;

	/* Owner read/write/execute */
	c = *buf_ptr;
	if(c != '\0')
	{
	    obj->permissions |= (c == 'r') ? EDV_PERMISSION_UREAD : 0;
	    buf_ptr++;
	}
	c = *buf_ptr;
	if(c != '\0')
	{
	    obj->permissions |= (c == 'w') ? EDV_PERMISSION_UWRITE : 0;
	    buf_ptr++;
	}
	c = *buf_ptr;
	if(c != '\0')
	{
	    obj->permissions |= (c == 'x') ? EDV_PERMISSION_UEXECUTE : 0;
	    buf_ptr++;
	}

	/* Group read/write/execute */
	c = *buf_ptr;
	if(c != '\0')
	{
	    obj->permissions |= (c == 'r') ? EDV_PERMISSION_GREAD : 0;
	    buf_ptr++;
	}
	c = *buf_ptr;
	if(c != '\0')
	{
	    obj->permissions |= (c == 'w') ? EDV_PERMISSION_GWRITE : 0;
	    buf_ptr++;
	}
	c = *buf_ptr;
	if(c != '\0')
	{
	    obj->permissions |= (c == 'x') ? EDV_PERMISSION_GEXECUTE : 0;
	    buf_ptr++;
	}

	/* Anonymous read/write/execute */
	c = *buf_ptr;
	if(c != '\0')
	{
	    obj->permissions |= (c == 'r') ? EDV_PERMISSION_AREAD : 0;
	    buf_ptr++;
	}
	c = *buf_ptr;
	if(c != '\0')
	{
	    obj->permissions |= (c == 'w') ? EDV_PERMISSION_AWRITE : 0;
	    buf_ptr++;
	}
	c = *buf_ptr;
	if(c != '\0')
	{
	    obj->permissions |= (c == 'x') ? EDV_PERMISSION_AEXECUTE : 0;
	    buf_ptr++;
	}
	while(ISBLANK(*buf_ptr))
	    buf_ptr++;

	/* Owner/group */
	if(*buf_ptr != '\0')
	{
	    const gchar delim = '/';
	    gchar *s, *s2;

	    /* Get owner */
	    g_free(obj->owner_name);
	    obj->owner_name = s = STRDUP(buf_ptr);
	    s2 = strchr(s, delim);
	    if(s2 != NULL)
		*s2 = '\0';

	    /* Get group */
	    while((*buf_ptr != delim) && (*buf_ptr != '\0'))
		buf_ptr++;
	    if(*buf_ptr == delim)
		buf_ptr++;
	    g_free(obj->group_name);
	    obj->group_name = s = STRDUP(buf_ptr);
	    while(*s != '\0')
	    {
		if(ISBLANK(*s))
		{
		    *s = '\0';
		    break;
		}
		s++;
	    }

	    while(!ISBLANK(*buf_ptr) && (*buf_ptr != '\0'))
		buf_ptr++;
	}
	while(ISBLANK(*buf_ptr))
	    buf_ptr++;

	/* Compressed Size */
	obj->compressed_size = (gulong)ATOL(buf_ptr);
	while(!ISBLANK(*buf_ptr) && (*buf_ptr != '\0'))
	    buf_ptr++;
	while(ISBLANK(*buf_ptr))
	    buf_ptr++;

	/* Size */
	obj->size = (gulong)ATOL(buf_ptr);
	while(!ISBLANK(*buf_ptr) && (*buf_ptr != '\0'))
	    buf_ptr++;
	while(ISBLANK(*buf_ptr))
	    buf_ptr++;

	/* Compression Ratio */
	obj->compression_ratio = CLIP(ATOF(buf_ptr) / 100.0f, 0.0f, 1.0f);
	while(!ISBLANK(*buf_ptr) && (*buf_ptr != '\0'))
	    buf_ptr++;
	while(ISBLANK(*buf_ptr))
	    buf_ptr++;

	/* Method */
	if(*buf_ptr != '\0')
	{
	    gint len;
	    const gchar *s = buf_ptr;

	    while(!ISBLANK(*buf_ptr) && (*buf_ptr != '\0'))
		buf_ptr++;

	    len = (gint)(buf_ptr - s);

	    g_free(obj->method);
	    obj->method = (gchar *)g_malloc((len + 1) * sizeof(gchar));
	    if(len > 0)
		strncpy((char *)obj->method, (const char *)s, len);
	    obj->method[len] = '\0';
	}
	while(ISBLANK(*buf_ptr))
	    buf_ptr++;

	/* CRC */
	if(*buf_ptr != '\0')
	{
	    gint len;
	    const gchar *s = buf_ptr;

	    while(!ISBLANK(*buf_ptr) && (*buf_ptr != '\0'))
		buf_ptr++;

	    len = (gint)(buf_ptr - s);

	    g_free(obj->crc);
	    obj->crc = (gchar *)g_malloc((len + 1) * sizeof(gchar));
	    if(len > 0)
		strncpy((char *)obj->crc, (const char *)s, len);
	    obj->crc[len] = '\0';
	}
	while(ISBLANK(*buf_ptr))
	    buf_ptr++;

	/* Month name */
	if(strcasepfx((const char *)buf_ptr, "jan"))
	    mtime_buf.tm_mon = 0;
	else if(strcasepfx((const char *)buf_ptr, "feb"))
	    mtime_buf.tm_mon = 1;
	else if(strcasepfx((const char *)buf_ptr, "mar"))
	    mtime_buf.tm_mon = 2;
	else if(strcasepfx((const char *)buf_ptr, "apr"))
	    mtime_buf.tm_mon = 3;
	else if(strcasepfx((const char *)buf_ptr, "may"))
	    mtime_buf.tm_mon = 4;
	else if(strcasepfx((const char *)buf_ptr, "jun"))
	    mtime_buf.tm_mon = 5;
	else if(strcasepfx((const char *)buf_ptr, "jul"))
	    mtime_buf.tm_mon = 6;
	else if(strcasepfx((const char *)buf_ptr, "aug"))
	    mtime_buf.tm_mon = 7;
	else if(strcasepfx((const char *)buf_ptr, "sep"))
	    mtime_buf.tm_mon = 8;
	else if(strcasepfx((const char *)buf_ptr, "oct"))
	    mtime_buf.tm_mon = 9;
	else if(strcasepfx((const char *)buf_ptr, "nov"))
	    mtime_buf.tm_mon = 10;
	else if(strcasepfx((const char *)buf_ptr, "dec"))
	    mtime_buf.tm_mon = 11;
	while(!ISBLANK(*buf_ptr) && (*buf_ptr != '\0'))
	    buf_ptr++;
	while(ISBLANK(*buf_ptr))
	    buf_ptr++;

	/* Day */
	mtime_buf.tm_mday = (int)ATOI(buf_ptr);
	while(!ISBLANK(*buf_ptr) && (*buf_ptr != '\0'))
	    buf_ptr++;
	while(ISBLANK(*buf_ptr))
	    buf_ptr++;

	/* Year or time */
	if(*buf_ptr != '\0')
	{
	    gchar *s = STRDUP(buf_ptr), *s2;
	    for(s2 = s; *s2 != '\0'; s2++)
	    {
		if(ISBLANK(*s2))
		{
		    *s2 = '\0';
		    break;
		}
	    }

	    s2 = s;

	    /* Time? */
	    if(strchr((const char *)s2, ':'))
	    {
		/* Hour */
		if(*s2 == '0')
		    s2++;
		mtime_buf.tm_hour = (int)ATOI(s2);
		while((*s2 != ':') && (*s2 != '\0'))
		    s2++;
		if(*s2 == ':')
		    s2++;

		/* Minutes */
		if(*s2 == '0')
		    s2++;
		mtime_buf.tm_min = (int)ATOI(s2);
	    }
	    else
	    {
		/* Year */
		mtime_buf.tm_year = (int)ATOI(s2) - 1900;
	    }
	    g_free(s);

	    while(!ISBLANK(*buf_ptr) && (*buf_ptr != '\0'))
		buf_ptr++;
	}
	while(ISBLANK(*buf_ptr))
	    buf_ptr++;

	/* Parse time compoents and generate system time since we now
	 * have enough information to do that
	 */
	obj->access_time = (gulong)mktime(&mtime_buf);
	obj->modify_time = obj->access_time;
	obj->change_time = obj->access_time;

	/* Full Path & Name */
	if(*buf_ptr != '\0')
	{
	    gint len;
	    const gchar *s = buf_ptr;

	    while(!ISSPACE(*buf_ptr) && (*buf_ptr != '\0'))
		buf_ptr++;

	    len = (gint)(buf_ptr - s);

	    /* Get full path */
	    g_free(obj->full_path);
	    obj->full_path = (gchar *)g_malloc(
		(len + 1) * sizeof(gchar)
	    );
	    if(len > 0)
		strncpy((char *)obj->full_path, (const char *)s, len);
	    obj->full_path[len] = '\0';

	    /* Strip tailing deliminators */
	    StripPath((char *)obj->full_path);

	    /* Get name from full path */
	    g_free(obj->name);
	    obj->name = STRDUP(EDVGetPathName(obj->full_path));
	}
	while(ISSPACE(*buf_ptr))
	    buf_ptr++;
}

/*
 *	Parses the given null terminated string buf which is from a
 *	RAR Archive list output.
 *
 *	The given string must contain two lines per object description
 *	per RAR Archive list output format.
 */
static void EDVArchFIOParseLineRAR(
	edv_archive_object_struct *obj, const gchar *buf
)
{
	gint c;
	const gchar *buf_ptr = buf;
	struct tm mtime_buf;


	if((obj == NULL) || (buf_ptr == NULL))
	    return;

	memset(&mtime_buf, 0x00, sizeof(struct tm));

	/* Sample format line:

 dir/file.ext \n 1604      780  48% 12-03-03 14:50 -rw-r--r-- 5005A169 m3b 2.9

	 */

	/* Full Path & Name */
	if(*buf_ptr != '\0')
	{
	    gint len;
	    const gchar *s = buf_ptr;

	    while(!ISSPACE(*buf_ptr) && (*buf_ptr != '\0'))
		buf_ptr++;

	    len = (gint)(buf_ptr - s);

	    /* Get full path */
	    g_free(obj->full_path);
	    obj->full_path = (gchar *)g_malloc(
		(len + 1) * sizeof(gchar)
	    );
	    if(len > 0)
		strncpy((char *)obj->full_path, (const char *)s, len);
	    obj->full_path[len] = '\0';

	    /* Strip tailing deliminators */
	    StripPath((char *)obj->full_path);

	    /* Get name from full path */
	    g_free(obj->name);
	    obj->name = STRDUP(EDVGetPathName(obj->full_path));
	}
	while(ISSPACE(*buf_ptr))
	    buf_ptr++;

	/* Size */
	obj->size = (gulong)ATOL(buf_ptr);
	while(!ISSPACE(*buf_ptr) && (*buf_ptr != '\0'))
	    buf_ptr++;
	while(ISSPACE(*buf_ptr))
	    buf_ptr++;

	/* Compressed Size */
	obj->compressed_size = (gulong)ATOL(buf_ptr);
	while(!ISSPACE(*buf_ptr) && (*buf_ptr != '\0'))
	    buf_ptr++;
	while(ISSPACE(*buf_ptr))
	    buf_ptr++;

	/* Compression Ratio */
	obj->compression_ratio = (gfloat)ATOI(buf_ptr) / 100.0f;
	while(!ISSPACE(*buf_ptr) && (*buf_ptr != '\0'))
	    buf_ptr++;
	while(ISSPACE(*buf_ptr))
	    buf_ptr++;

	/* Day-month-year */
	if(*buf_ptr != '\0')
	{
	    gint year_2digit;

	    /* Day */
	    if(*buf_ptr == '0')
		buf_ptr++;
	    mtime_buf.tm_mday = (int)ATOI(buf_ptr);
	    while((*buf_ptr != '-') && (*buf_ptr != '\0'))
		buf_ptr++;
	    if(*buf_ptr == '-')
		buf_ptr++;

	    /* Month */
	    if(*buf_ptr == '0')
		buf_ptr++;
	    mtime_buf.tm_mon = (int)ATOI(buf_ptr) - 1;
	    while((*buf_ptr != '-') && (*buf_ptr != '\0'))
		buf_ptr++;
	    if(*buf_ptr == '-')
		buf_ptr++;

	    /* Get 2-digit year (use "imperfect" Y2K fix) */
	    if(*buf_ptr == '0')
		buf_ptr++;
	    year_2digit = ATOI(buf_ptr);
	    mtime_buf.tm_year = (int)((year_2digit < 60) ?
		(year_2digit + 100) : year_2digit
	    );
	    while(!ISSPACE(*buf_ptr) && (*buf_ptr != '\0'))
		buf_ptr++;
	}
	while(ISSPACE(*buf_ptr))
	    buf_ptr++;

	/* Hour:minutes */
	if(*buf_ptr != '\0')
	{
	    /* Hour */
	    if(*buf_ptr == '0')
		buf_ptr++;
	    mtime_buf.tm_hour = (int)(ATOI(buf_ptr) - 1);
	    while((*buf_ptr != ':') && (*buf_ptr != '\0'))
		buf_ptr++;
	    if(*buf_ptr == ':')
		buf_ptr++;

	    /* Minutes */
	    if(*buf_ptr == '0')
		buf_ptr++;
	    mtime_buf.tm_min = (int)ATOI(buf_ptr);
	    while(!ISSPACE(*buf_ptr) && (*buf_ptr != '\0'))
		buf_ptr++;
	}

	/* Parse time compoents and generate system time since we now
	 * have enough information to do that
	 */
	obj->access_time = (gulong)mktime(&mtime_buf);
	obj->modify_time = obj->access_time;
	obj->change_time = obj->access_time;

	while(ISSPACE(*buf_ptr))
	    buf_ptr++;


	/* Type */
	c = *buf_ptr;
	if(c != '\0')
	{
	    if(c == 'd')
		obj->type = EDV_OBJECT_TYPE_DIRECTORY;
	    else if(c == 'l')
		obj->type = EDV_OBJECT_TYPE_LINK;
	    else if(c == 'p')
		obj->type = EDV_OBJECT_TYPE_FIFO;
	    else if(c == 'b')
		obj->type = EDV_OBJECT_TYPE_DEVICE_BLOCK;
	    else if(c == 'c')
		obj->type = EDV_OBJECT_TYPE_DEVICE_CHARACTER;
	    else if(c == 's')
		obj->type = EDV_OBJECT_TYPE_SOCKET;
	    else
		obj->type = EDV_OBJECT_TYPE_FILE;

	    buf_ptr++;
	}

	/* Permissions */
	obj->permissions = 0x00000000;

	/* Owner read/write/execute */
	c = *buf_ptr;
	if(c != '\0')
	{
	    obj->permissions |= (c == 'r') ? EDV_PERMISSION_UREAD : 0;
	    buf_ptr++;
	}
	c = *buf_ptr;
	if(c != '\0')
	{
	    obj->permissions |= (c == 'w') ? EDV_PERMISSION_UWRITE : 0;
	    buf_ptr++;
	}
	c = *buf_ptr;
	if(c != '\0')
	{
	    obj->permissions |= (c == 'x') ? EDV_PERMISSION_UEXECUTE : 0;
	    buf_ptr++;
	}

	/* Group read/write/execute */
	c = *buf_ptr;
	if(c != '\0')
	{
	    obj->permissions |= (c == 'r') ? EDV_PERMISSION_GREAD : 0;
	    buf_ptr++;
	}
	c = *buf_ptr;
	if(c != '\0')
	{
	    obj->permissions |= (c == 'w') ? EDV_PERMISSION_GWRITE : 0;
	    buf_ptr++;
	}
	c = *buf_ptr;
	if(c != '\0')
	{
	    obj->permissions |= (c == 'x') ? EDV_PERMISSION_GEXECUTE : 0;
	    buf_ptr++;
	}

	/* Anonymous read/write/execute */
	c = *buf_ptr;
	if(c != '\0')
	{
	    obj->permissions |= (c == 'r') ? EDV_PERMISSION_AREAD : 0;
	    buf_ptr++;
	}
	c = *buf_ptr;
	if(c != '\0')
	{
	    obj->permissions |= (c == 'w') ? EDV_PERMISSION_AWRITE : 0;
	    buf_ptr++;
	}
	c = *buf_ptr;
	if(c != '\0')
	{
	    obj->permissions |= (c == 'x') ? EDV_PERMISSION_AEXECUTE : 0;
	    buf_ptr++;
	}
	while(!ISBLANK(*buf_ptr) && (*buf_ptr != '\0'))
	    buf_ptr++;
	while(ISSPACE(*buf_ptr))
	    buf_ptr++;

	/* CRC */
	if(*buf_ptr != '\0')
	{
	    gint len;
	    const gchar *s = buf_ptr;

	    while(!ISSPACE(*buf_ptr) && (*buf_ptr != '\0'))
		buf_ptr++;

	    len = (gint)(buf_ptr - s);

	    g_free(obj->crc);
	    obj->crc = (gchar *)g_malloc((len + 1) * sizeof(gchar));
	    if(len > 0)
		strncpy((char *)obj->crc, (const char *)s, len);
	    obj->crc[len] = '\0';
	}
	while(ISSPACE(*buf_ptr))
	    buf_ptr++;

	/* Method & Version */
	if(*buf_ptr != '\0')
	{
	    gint len;
	    const gchar *s = buf_ptr;

	    while(!ISSPACE(*buf_ptr) && (*buf_ptr != '\0'))
		buf_ptr++;
	    while(ISSPACE(*buf_ptr))
		buf_ptr++;
	    while(!ISSPACE(*buf_ptr) && (*buf_ptr != '\0'))
		buf_ptr++;

	    len = (gint)(buf_ptr - s);

	    g_free(obj->method);
	    obj->method = (gchar *)g_malloc((len + 1) * sizeof(gchar));
	    if(len > 0)
		strncpy((char *)obj->method, (const char *)s, len);
	    obj->method[len] = '\0';
	}
	while(ISSPACE(*buf_ptr))
	    buf_ptr++;
}

/*
 *	Parses the given null terminated string buf which is from a
 *	RedHat Package Manager list output.
 */
static void EDVArchFIOParseLineRPM(
	edv_archive_object_struct *obj, const gchar *buf
)
{
	gint c;
	const gchar *buf_ptr = buf;
	struct tm mtime_buf;


	if((obj == NULL) || (buf_ptr == NULL))
	    return;

	memset(&mtime_buf, 0x00, sizeof(struct tm));

	/* Sample format line:

-rw-r--r--    1 root    root            27666 Jan  4  2000 /dir/file.ext

	 */

	/* Type and permissions string (this is one string with no
	 * deliminators
	 */

	/* Type */
	c = *buf_ptr;
	if(c != '\0')
	{
	    if(c == 'd')
		obj->type = EDV_OBJECT_TYPE_DIRECTORY;
	    else if(c == 'l')
		obj->type = EDV_OBJECT_TYPE_LINK;
	    else if(c == 'p')
		obj->type = EDV_OBJECT_TYPE_FIFO;
	    else if(c == 'b')
		obj->type = EDV_OBJECT_TYPE_DEVICE_BLOCK;
	    else if(c == 'c')
		obj->type = EDV_OBJECT_TYPE_DEVICE_CHARACTER;
	    else if(c == 's')
		obj->type = EDV_OBJECT_TYPE_SOCKET;
	    else
		obj->type = EDV_OBJECT_TYPE_FILE;

	    buf_ptr++;
	}

	/* Permissions */
	obj->permissions = 0x00000000;

	/* Owner read/write/execute */
	c = *buf_ptr;
	if(c != '\0')
	{
	    obj->permissions |= (c == 'r') ? EDV_PERMISSION_UREAD : 0;
	    buf_ptr++;
	}
	c = *buf_ptr;
	if(c != '\0')
	{
	    obj->permissions |= (c == 'w') ? EDV_PERMISSION_UWRITE : 0;
	    buf_ptr++;
	}
	c = *buf_ptr;
	if(c != '\0')
	{
	    obj->permissions |= (c == 'x') ? EDV_PERMISSION_UEXECUTE : 0;
	    buf_ptr++;
	}

	/* Group read/write/execute */
	c = *buf_ptr;
	if(c != '\0')
	{
	    obj->permissions |= (c == 'r') ? EDV_PERMISSION_GREAD : 0;
	    buf_ptr++;
	}
	c = *buf_ptr;
	if(c != '\0')
	{
	    obj->permissions |= (c == 'w') ? EDV_PERMISSION_GWRITE : 0;
	    buf_ptr++;
	}
	c = *buf_ptr;
	if(c != '\0')
	{
	    obj->permissions |= (c == 'x') ? EDV_PERMISSION_GEXECUTE : 0;
	    buf_ptr++;
	}

	/* Anonymous read/write/execute */
	c = *buf_ptr;
	if(c != '\0')
	{
	    obj->permissions |= (c == 'r') ? EDV_PERMISSION_AREAD : 0;
	    buf_ptr++;
	}
	c = *buf_ptr;
	if(c != '\0')
	{
	    obj->permissions |= (c == 'w') ? EDV_PERMISSION_AWRITE : 0;
	    buf_ptr++;
	}
	c = *buf_ptr;
	if(c != '\0')
	{
	    obj->permissions |= (c == 'x') ? EDV_PERMISSION_AEXECUTE : 0;
	    buf_ptr++;
	}

	while(ISBLANK(*buf_ptr))
	    buf_ptr++;


	/* This is number, not sure what it is but just seek past it */
	while(!ISBLANK(*buf_ptr) && (*buf_ptr != '\0'))
	    buf_ptr++;
	while(ISBLANK(*buf_ptr))
	    buf_ptr++;


	/* Owner and group strings, deliminated by a space in between */
	if(*buf_ptr != '\0')
	{
	    gchar *s, *s2;

	    /* Get owner */
	    g_free(obj->owner_name);
	    obj->owner_name = s = STRDUP(buf_ptr);
	    s2 = strchr(s, ' ');
	    if(s2 != NULL)
		*s2 = '\0';
	    s2 = strchr(s, '\t');
	    if(s2 != NULL)
		*s2 = '\0';

	    /* Seek to group */
	    while(!ISBLANK(*buf_ptr) && (*buf_ptr != '\0'))
		buf_ptr++;
	    while(ISBLANK(*buf_ptr))
		buf_ptr++;

	    /* Get group */
	    g_free(obj->group_name);
	    obj->group_name = s = STRDUP(buf_ptr);
	    s2 = strchr(s, ' ');
	    if(s2 != NULL)
		*s2 = '\0';
	    s2 = strchr(s, '\t');
	    if(s2 != NULL)
		*s2 = '\0';

	    /* Seek past group to first blank character */
	    while(!ISBLANK(*buf_ptr) && (*buf_ptr != '\0'))
		buf_ptr++;
	}


	while(ISBLANK(*buf_ptr))
	    buf_ptr++;

	/* Size */
	obj->size = (gulong)ATOL(buf_ptr);

	while(!ISBLANK(*buf_ptr) && (*buf_ptr != '\0'))
	    buf_ptr++;
	while(ISBLANK(*buf_ptr))
	    buf_ptr++;


	/* These next three strings are month, day, and year
	 * separated by blank characters but the names are verbose and
	 * thus too difficult to parse
	 */
	/* Skip month */
	while(!ISBLANK(*buf_ptr) && (*buf_ptr != '\0'))
	    buf_ptr++;
	while(ISBLANK(*buf_ptr))
	    buf_ptr++;

	/* Skip day */
	while(!ISBLANK(*buf_ptr) && (*buf_ptr != '\0'))
	    buf_ptr++;
	while(ISBLANK(*buf_ptr))
	    buf_ptr++;

	/* Skip year */
	while(!ISBLANK(*buf_ptr) && (*buf_ptr != '\0'))
	    buf_ptr++;
	while(ISBLANK(*buf_ptr))
	    buf_ptr++;

	/* Full Path & Name */
	if(*buf_ptr != '\0')
	{
	    gint len;
	    const gchar *s = buf_ptr;

	    while(!ISSPACE(*buf_ptr) && (*buf_ptr != '\0'))
		buf_ptr++;

	    len = (gint)(buf_ptr - s);

	    /* Get full path */
	    g_free(obj->full_path);
	    obj->full_path = (gchar *)g_malloc(
		(len + 1) * sizeof(gchar)
	    );
	    if(len > 0)
		strncpy((char *)obj->full_path, (const char *)s, len);
	    obj->full_path[len] = '\0';

	    /* Strip tailing deliminators */
	    StripPath((char *)obj->full_path);

	    /* Get name from full path */
	    g_free(obj->name);
	    obj->name = STRDUP(EDVGetPathName(obj->full_path));
	}
	while(ISSPACE(*buf_ptr))
	    buf_ptr++;

	/* If this object is a link then we need to parse the link
	 * destination
	 */
	if(obj->type == EDV_OBJECT_TYPE_LINK)
	{
	    gint len;
	    const gchar *s;

	    /* Seek past "->" deliminator */
	    while(!ISSPACE(*buf_ptr) && (*buf_ptr != '\0'))
		buf_ptr++;
	    while(ISSPACE(*buf_ptr))
		buf_ptr++;

	    /* Now at link destination value */
	    s = buf_ptr;
	    while(!ISSPACE(*buf_ptr) && (*buf_ptr != '\0'))
		buf_ptr++;

	    len = (gint)(buf_ptr - s);

	    g_free(obj->linked_to);
	    obj->linked_to = (gchar *)g_malloc(
		(len + 1) * sizeof(gchar)
	    );
	    if(len > 0)
		strncpy((char *)obj->linked_to, (const char *)s, len);
	    obj->linked_to[len] = '\0';

	    while(ISSPACE(*buf_ptr))
		buf_ptr++;
	}
}


/*
 *	Parses the given null terminated string buf which is from a
 *	Tape Archiver list output.
 */
static void EDVArchFIOParseLineTar(
	edv_archive_object_struct *obj, const gchar *buf
)
{
	gint c;
	const gchar *buf_ptr = buf;
	struct tm mtime_buf;


	if((obj == NULL) || (buf_ptr == NULL))
	    return;

	memset(&mtime_buf, 0x00, sizeof(struct tm));

	/* Sample format line:

lrwxrwxrwx root/root   0 2001-11-19 02:57 file.ext -> link.dest

	 */

	/* Type and permissions string (this is one string with no
	 * deliminators
	 */

	/* Type */
	c = *buf_ptr;
	if(c != '\0')
	{
	    if(c == 'd')
		obj->type = EDV_OBJECT_TYPE_DIRECTORY;
	    else if(c == 'l')
		obj->type = EDV_OBJECT_TYPE_LINK;
	    else if(c == 'p')
		obj->type = EDV_OBJECT_TYPE_FIFO;
	    else if(c == 'b')
		obj->type = EDV_OBJECT_TYPE_DEVICE_BLOCK;
	    else if(c == 'c')
		obj->type = EDV_OBJECT_TYPE_DEVICE_CHARACTER;
	    else if(c == 's')
		obj->type = EDV_OBJECT_TYPE_SOCKET;
	    else
		obj->type = EDV_OBJECT_TYPE_FILE;

	    buf_ptr++;
	}

	/* Permissions */
	obj->permissions = 0x00000000;

	/* Owner read/write/execute */
	c = *buf_ptr;
	if(c != '\0')
	{
	    obj->permissions |= (c == 'r') ? EDV_PERMISSION_UREAD : 0;
	    buf_ptr++;
	}
	c = *buf_ptr;
	if(c != '\0')
	{
	    obj->permissions |= (c == 'w') ? EDV_PERMISSION_UWRITE : 0;
	    buf_ptr++;
	}
	c = *buf_ptr;
	if(c != '\0')
	{
	    obj->permissions |= (c == 'x') ? EDV_PERMISSION_UEXECUTE : 0;
	    buf_ptr++;
	}

	/* Group read/write/execute */
	c = *buf_ptr;
	if(c != '\0')
	{
	    obj->permissions |= (c == 'r') ? EDV_PERMISSION_GREAD : 0;
	    buf_ptr++;
	}
	c = *buf_ptr;
	if(c != '\0')
	{
	    obj->permissions |= (c == 'w') ? EDV_PERMISSION_GWRITE : 0;
	    buf_ptr++;
	}
	c = *buf_ptr;
	if(c != '\0')
	{
	    obj->permissions |= (c == 'x') ? EDV_PERMISSION_GEXECUTE : 0;
	    buf_ptr++;
	}

	/* Anonymous read/write/execute */
	c = *buf_ptr;
	if(c != '\0')
	{
	    obj->permissions |= (c == 'r') ? EDV_PERMISSION_AREAD : 0;
	    buf_ptr++;
	}
	c = *buf_ptr;
	if(c != '\0')
	{
	    obj->permissions |= (c == 'w') ? EDV_PERMISSION_AWRITE : 0;
	    buf_ptr++;
	}
	c = *buf_ptr;
	if(c != '\0')
	{
	    obj->permissions |= (c == 'x') ? EDV_PERMISSION_AEXECUTE : 0;
	    buf_ptr++;
	}



	while(ISBLANK(*buf_ptr))
	    buf_ptr++;


	/* Owner and group string, separated by a '/' in between */
	if(*buf_ptr != '\0')
	{
	    gchar *s, tmp_buf[1024];

	    /* Copy to tempory buffer for parsing */
	    strncpy(tmp_buf, buf_ptr, 1024);
	    tmp_buf[1024 - 1] = '\0';

	    /* Cap tempory buffer at start of next argument */
	    s = strchr((char *)tmp_buf, ' ');
	    if(s != NULL)
		*s = '\0';
	    s = strchr((char *)tmp_buf, '\t');
	    if(s != NULL)
		*s = '\0';

	    /* Get position of owner and group name deliminator */
	    s = strchr((char *)tmp_buf, '/');
	    if(s != NULL)
	    {
		*s = '\0';	/* Put null char at end of owner name */

		/* Get owner name */
		g_free(obj->owner_name);
		obj->owner_name = STRDUP(tmp_buf);

		/* Get group name (it is valid after the null char) */
		g_free(obj->group_name);
		obj->group_name = STRDUP(s + 1);
	    }

	    while(!ISBLANK(*buf_ptr) && (*buf_ptr != '\0'))
		buf_ptr++;
	}


	while(ISBLANK(*buf_ptr))
	    buf_ptr++;


	/* Size */
	obj->size = (gulong)ATOL(buf_ptr);


	while(!ISBLANK(*buf_ptr) && (*buf_ptr != '\0'))
	    buf_ptr++;
	while(ISBLANK(*buf_ptr))
	    buf_ptr++;


	/* Year-month-day */
	if(*buf_ptr != '\0')
	{
	    /* Get year (input value starts from 1900) */
	    if(*buf_ptr == '0')
		buf_ptr++;
	    mtime_buf.tm_year = (int)(ATOI(buf_ptr) - 1900);
	    while((*buf_ptr != '-') && (*buf_ptr != '\0'))
		buf_ptr++;
	    if(*buf_ptr == '-')
		buf_ptr++;

	    /* Get month */
	    if(*buf_ptr == '0')
		buf_ptr++;
	    mtime_buf.tm_mon = (int)(ATOI(buf_ptr) - 1);
	    while((*buf_ptr != '-') && (*buf_ptr != '\0'))
		buf_ptr++;
	    if(*buf_ptr == '-')
		buf_ptr++;

	    /* Get day */
	    if(*buf_ptr == '0')
		buf_ptr++;
	    mtime_buf.tm_mday = (int)ATOI(buf_ptr);
	    while(!ISBLANK(*buf_ptr) && (*buf_ptr != '\0'))
		buf_ptr++;
	}

	while(ISBLANK(*buf_ptr))
	    buf_ptr++;


	/* Hour:minutes */
	if(*buf_ptr != '\0')
	{
	    /* Get hour */
	    if(*buf_ptr == '0')
		buf_ptr++;
	    mtime_buf.tm_hour = (int)(ATOI(buf_ptr) - 1);
	    while((*buf_ptr != ':') && (*buf_ptr != '\0'))
		buf_ptr++;
	    if(*buf_ptr == ':')
		buf_ptr++;

	    /* Get minutes */
	    if(*buf_ptr == '0')
		buf_ptr++;
	    mtime_buf.tm_min = (int)ATOI(buf_ptr);
	    while(!ISBLANK(*buf_ptr) && (*buf_ptr != '\0'))
		buf_ptr++;
	}

	/* Parse time compoents and generate system time since we now
	 * have enough information to do that
	 */
	obj->access_time = (gulong)mktime(&mtime_buf);
	obj->modify_time = obj->access_time;
	obj->change_time = obj->access_time;

	while(ISBLANK(*buf_ptr))
	    buf_ptr++;

	/* Full Path & Name */
	if(*buf_ptr != '\0')
	{
	    gint len;
	    const gchar *s = buf_ptr;

	    while(!ISSPACE(*buf_ptr) && (*buf_ptr != '\0'))
		buf_ptr++;

	    len = (gint)(buf_ptr - s);

	    /* Get full path */
	    g_free(obj->full_path);
	    obj->full_path = (gchar *)g_malloc(
		(len + 1) * sizeof(gchar)
	    );
	    if(len > 0)
		strncpy((char *)obj->full_path, (const char *)s, len);
	    obj->full_path[len] = '\0';

	    /* Strip tailing deliminators */
	    StripPath((char *)obj->full_path);

	    /* Get name from full path */
	    g_free(obj->name);
	    obj->name = STRDUP(EDVGetPathName(obj->full_path));
	}
	while(ISSPACE(*buf_ptr))
	    buf_ptr++;

	/* If this object is a link then we need to parse the link 
	 * destination
	 */
	if(obj->type == EDV_OBJECT_TYPE_LINK)
	{
	    gint len;
	    const gchar *s;

	    /* Seek past "->" deliminator */
	    while(!ISSPACE(*buf_ptr) && (*buf_ptr != '\0'))
		buf_ptr++;
	    while(ISSPACE(*buf_ptr))
		buf_ptr++;

	    /* Now at link destination value */
	    s = buf_ptr;
	    while(!ISSPACE(*buf_ptr) && (*buf_ptr != '\0'))
		buf_ptr++;

	    len = (gint)(buf_ptr - s);

	    g_free(obj->linked_to);
	    obj->linked_to = (gchar *)g_malloc(
		(len + 1) * sizeof(gchar)
	    );
	    if(len > 0)
		strncpy((char *)obj->linked_to, (const char *)s, len);
	    obj->linked_to[len] = '\0';

	    while(ISSPACE(*buf_ptr))
		buf_ptr++;
	}
}

/*
 *	Parses the given null terminated string buf which is from a
 *	PKZip list output
 */
static void EDVArchFIOParseLineZip(
	edv_archive_object_struct *obj, const gchar *buf
)
{
	const gchar *buf_ptr = buf;
	struct tm mtime_buf;

	if((obj == NULL) || (buf_ptr == NULL))
	    return;

	memset(&mtime_buf, 0x00, sizeof(struct tm));

	/* Sample format line:

    2371  Defl:X      688  71%  08-24-03 20:33  9c2d86e6  file.ext

	 */


	/* Size */
	obj->size = (gulong)ATOL(buf_ptr);
	while(!ISBLANK(*buf_ptr) && (*buf_ptr != '\0'))
	    buf_ptr++;
	while(ISBLANK(*buf_ptr))
	    buf_ptr++;

	/* Compression Method */
	if(*buf_ptr != '\0')
	{
	    gint len;
	    const gchar *s = buf_ptr;

	    while(!ISBLANK(*buf_ptr) && (*buf_ptr != '\0'))
		buf_ptr++;

	    len = (gint)(buf_ptr - s);

	    g_free(obj->method);
	    obj->method = (gchar *)g_malloc((len + 1) * sizeof(gchar));
	    if(len > 0)
		strncpy((char *)obj->method, (const char *)s, len);
	    obj->method[len] = '\0';
	}
	while(ISBLANK(*buf_ptr))
	    buf_ptr++;

	/* Compressed Size */
	obj->compressed_size = (gulong)ATOL(buf_ptr);
	while(!ISBLANK(*buf_ptr) && (*buf_ptr != '\0'))
	    buf_ptr++;
	while(ISBLANK(*buf_ptr))
	    buf_ptr++;

	/* Compression Ratio */
	obj->compression_ratio = (gfloat)ATOI(buf_ptr) / 100.0f;
	while(!ISBLANK(*buf_ptr) && (*buf_ptr != '\0'))
	    buf_ptr++;
	while(ISBLANK(*buf_ptr))
	    buf_ptr++;

	/* Month-day-year */
	if(*buf_ptr != '\0')
	{
	    gint year_2digit;

	    /* Get month */
	    if(*buf_ptr == '0')
		buf_ptr++;
	    mtime_buf.tm_mon = (int)(ATOI(buf_ptr) - 1);
	    while((*buf_ptr != '-') && (*buf_ptr != '\0'))
		buf_ptr++;
	    if(*buf_ptr == '-')
		buf_ptr++;

	    /* Get day */
	    if(*buf_ptr == '0')
		buf_ptr++;
	    mtime_buf.tm_mday = (int)ATOI(buf_ptr);
	    while((*buf_ptr != '-') && (*buf_ptr != '\0'))
		buf_ptr++;
	    if(*buf_ptr == '-')
		buf_ptr++;

	    /* The year is relative to 1900, note that there is a Y2K
	     * problem here but we are compensating for it
	     */
	    if(*buf_ptr == '0')
		buf_ptr++;
	    year_2digit = (int)ATOI(buf_ptr);
	    mtime_buf.tm_year = (int)((year_2digit < 60) ?
		(year_2digit + 100) : year_2digit
	    );
	    while(!ISBLANK(*buf_ptr) && (*buf_ptr != '\0'))
		buf_ptr++;
	}
	while(ISBLANK(*buf_ptr))
	    buf_ptr++;

	/* Hour:minutes */
	if(*buf_ptr != '\0')
	{
	    /* Get hour */
	    if(*buf_ptr == '0')
		buf_ptr++;
	    mtime_buf.tm_hour = (int)(ATOI(buf_ptr) - 1);
	    while((*buf_ptr != ':') && (*buf_ptr != '\0'))
		buf_ptr++;
	    if(*buf_ptr == ':')
		buf_ptr++;

	    /* Get minutes */
	    if(*buf_ptr == '0')
		buf_ptr++;
	    mtime_buf.tm_min = (int)ATOI(buf_ptr);
	    while(!ISBLANK(*buf_ptr) && (*buf_ptr != '\0'))
		buf_ptr++;
	}

	/* Parse time compoents and generate system time since we now
	 * have enough information to do that
	 */
	obj->access_time = (gulong)mktime(&mtime_buf);
	obj->modify_time = obj->access_time;
	obj->change_time = obj->access_time;

	while(ISBLANK(*buf_ptr))
	    buf_ptr++;

	/* CRC */
	if(*buf_ptr != '\0')
	{
	    gint len;
	    const gchar *s = buf_ptr;

	    while(!ISBLANK(*buf_ptr) && (*buf_ptr != '\0'))
		buf_ptr++;

	    len = (gint)(buf_ptr - s);

	    g_free(obj->crc);
	    obj->crc = (gchar *)g_malloc((len + 1) * sizeof(gchar));
	    if(len > 0)
		strncpy((char *)obj->crc, (const char *)s, len);
	    obj->crc[len] = '\0';
	}
	while(ISBLANK(*buf_ptr))
	    buf_ptr++;

	/* Full Path & Name */
	if(*buf_ptr != '\0')
	{
	    gint len;
	    const gchar *s = buf_ptr;

	    while(!ISSPACE(*buf_ptr) && (*buf_ptr != '\0'))
		buf_ptr++;

	    len = (gint)(buf_ptr - s);

	    /* Get full path */
	    g_free(obj->full_path);
	    obj->full_path = (gchar *)g_malloc(
		(len + 1) * sizeof(gchar)
	    );
	    if(len > 0)
		strncpy((char *)obj->full_path, (const char *)s, len);
	    obj->full_path[len] = '\0';

	    /* Determine object type from the full path, if it has a
	     * tailing deliminator then it is a directory, otherwise it
	     * is a file
	     */
	    obj->type = EDV_OBJECT_TYPE_FILE;
	    if(len > 0)
	    {
		if(obj->full_path[len - 1] == G_DIR_SEPARATOR)
		    obj->type = EDV_OBJECT_TYPE_DIRECTORY;
	    }

	    /* Strip tailing deliminators */
	    StripPath((char *)obj->full_path);

	    /* Get name from full path */
	    g_free(obj->name);
	    obj->name = STRDUP(EDVGetPathName(obj->full_path));
	}
	while(ISSPACE(*buf_ptr))
	    buf_ptr++;

	/* Because the PKZip format does not specify permissions, we
	 * need to implicitly set the assumed permissions
	 */
	if(obj->type == EDV_OBJECT_TYPE_DIRECTORY)
	    obj->permissions = (
		EDV_PERMISSION_UREAD | EDV_PERMISSION_UWRITE |
		    EDV_PERMISSION_UEXECUTE |
		EDV_PERMISSION_GREAD | EDV_PERMISSION_GEXECUTE |
		EDV_PERMISSION_AREAD | EDV_PERMISSION_AEXECUTE
	    );
	else
	    obj->permissions = (
		EDV_PERMISSION_UREAD | EDV_PERMISSION_UWRITE |
		EDV_PERMISSION_GREAD |
		EDV_PERMISSION_AREAD
	    );

}


/*
 *	Get stats for the object in the ARJ archive.
 *
 *	Inputs assumed valid.
 */
static edv_archive_object_struct *EDVArchFIOStatARJ(
	edv_core_struct *core_ptr,
	const gchar *arch_obj, const gchar *path
)
{
	const gchar *prog_arj = CFGItemListGetValueS(
	    core_ptr->cfg_list, EDV_CFG_PARM_PROG_ARJ
	);
	FILE *fp;
	gint p;
	gchar *cmd, *stdout_path, *stderr_path;
	edv_archive_object_struct *obj = NULL;


	/* Format archive list output command */
	cmd = g_strdup_printf(
	    "%s v -i -y \"%s\" \"%s\"",
	    prog_arj, arch_obj, path
	);
	if(cmd == NULL)
	    return(obj);

	/* Generate output file paths */
	stdout_path = EDVTmpName(NULL);
	stderr_path = EDVTmpName(NULL);

	/* Execute archive list output command */
	p = (gint)ExecBOE((const char *)cmd, (const char *)stdout_path, (const char *)stderr_path);
	if(p <= 0)
	{
	    g_free(cmd);
	    g_free(stdout_path);
	    g_free(stderr_path);
	    return(obj);
	}

	/* Open output file for reading */
	fp = FOpen((const char *)stdout_path, "rb");
	if(fp != NULL)
	{
	    gboolean	got_header = FALSE;
	    gint i;
	    const gchar *delim_header = "------------";
	    gchar *s, buf[10000];

	    /* Read past header */
	    for(i = 0; TRUE; i++)
	    {
		if(fgets(buf, 10000, fp) != NULL)
		    buf[sizeof(buf) - 1] = '\0';
		else
		    break;

		if(strpfx((const char *)buf, (const char *)delim_header))
		{
		    got_header = TRUE;
		    break;
		}
	    }

	    /* Able to read past header? */
	    if(got_header ? (fgets(buf, 10000, fp) != NULL) : FALSE)
	    {
		gchar *complete_line;

		buf[sizeof(buf) - 1] = '\0';

		s = buf;
		while(ISBLANK(*s))
		    s++;

		/* Read a total of 4 lines (3 more) and concat them
		 * togeather
		 */
		complete_line = STRDUP(s);
		for(i = 0; i < 3; i++)
		{
		    if(fgets(buf, 10000, fp) != NULL)
		    {
			buf[sizeof(buf) - 1] = '\0';
			complete_line = G_STRCAT(complete_line, buf);
		    }
		    else
		    {
			break;
		    }
		}

		/* Create a new archive object structure and parse the
		 * loaded line to it
		 */
		obj = EDVArchObjectNew();
		EDVArchFIOParseLineARJ(obj, complete_line);

		g_free(complete_line);
	    }

	    FClose(fp);
	}

	/* Remove output files */
	UNLINK(stdout_path);
	UNLINK(stderr_path);

	g_free(cmd);
	g_free(stdout_path);
	g_free(stderr_path);

	return(obj);
}

/*
 *	Get stats for the object in the LHA archive.
 *
 *	Inputs assumed valid.
 */
static edv_archive_object_struct *EDVArchFIOStatLHA(
	edv_core_struct *core_ptr,
	const gchar *arch_obj, const gchar *path
)
{
	const gchar *prog_lha = CFGItemListGetValueS(
	    core_ptr->cfg_list, EDV_CFG_PARM_PROG_LHA
	);
	FILE *fp;
	gint p;
	gchar *cmd, *stdout_path, *stderr_path;
	edv_archive_object_struct *obj = NULL;


	/* Format archive list output command */
	cmd = g_strdup_printf(
	    "%s v \"%s\"",
	    prog_lha, arch_obj
	);
	if(cmd == NULL)
	    return(obj);

	/* Generate output file paths */
	stdout_path = EDVTmpName(NULL);
	stderr_path = EDVTmpName(NULL);

	/* Execute archive list output command */
	p = (gint)ExecBOE((const char *)cmd, (const char *)stdout_path, (const char *)stderr_path);
	if(p <= 0)
	{
	    g_free(cmd);
	    g_free(stdout_path);
	    g_free(stderr_path);
	    return(obj);
	}

	/* Open output file for reading */
	fp = FOpen((const char *)stdout_path, "rb");
	if(fp != NULL)
	{
	    gboolean got_header = FALSE;
	    gint i;
	    time_t cur_time = time(NULL);
	    gchar *s;
	    const gchar *delim_header = "----------";
	    gchar buf[10000];
	    const struct tm *tm_ptr;
	    struct tm cur_mtime_buf;


	    /* Get current time */
	    tm_ptr = localtime(&cur_time);
	    if(tm_ptr != NULL)
		memcpy(&cur_mtime_buf, tm_ptr, sizeof(struct tm));
	    else
		memset(&cur_mtime_buf, 0x00, sizeof(struct tm));


	    /* Read past header */
	    for(i = 0; TRUE; i++)
	    {
		if(fgets(buf, 10000, fp) != NULL)
		    buf[sizeof(buf) - 1] = '\0';
		else
		    break;

		if(strpfx((const char *)buf, (const char *)delim_header))
		{
		    got_header = TRUE;
		    break;
		}
	    }

	    /* Able to read past header? */
	    if(got_header ? (fgets(buf, 10000, fp) != NULL) : FALSE)
	    {
		buf[sizeof(buf) - 1] = '\0';

		s = buf;

		/* Seek paste spaces */
		while(ISBLANK(*s))
		    s++;

		/* Create a new archive object structure and parse the
		 * loaded line to it
		 */
		obj = EDVArchObjectNew();
		EDVArchFIOParseLineLHA(obj, s, &cur_mtime_buf);
	    }

	    FClose(fp);
	}

	/* Remove output files */
	UNLINK(stdout_path);
	UNLINK(stderr_path);

	g_free(cmd);
	g_free(stdout_path);
	g_free(stderr_path);

	return(obj);
}

/*
 *	Get stats for the object in the RAR archive.
 *
 *	Inputs assumed valid.
 */
static edv_archive_object_struct *EDVArchFIOStatRAR(
	edv_core_struct *core_ptr,
	const gchar *arch_obj, const gchar *path
)
{
	const gchar *prog_rar = CFGItemListGetValueS(
	    core_ptr->cfg_list, EDV_CFG_PARM_PROG_RAR
	);
	FILE *fp;
	gint p;
	gchar *cmd, *stdout_path, *stderr_path;
	edv_archive_object_struct *obj = NULL;


	/* Format archive list output command */
	cmd = g_strdup_printf(
	    "%s v -y \"%s\" \"%s\"",
	    prog_rar, arch_obj, path
	);
	if(cmd == NULL)
	    return(obj);

	/* Generate output file paths */
	stdout_path = EDVTmpName(NULL);
	stderr_path = EDVTmpName(NULL);

	/* Execute archive list output command */
	p = (gint)ExecBOE((const char *)cmd, (const char *)stdout_path, (const char *)stderr_path);
	if(p <= 0)
	{
	    g_free(cmd);
	    g_free(stdout_path);
	    g_free(stderr_path);
	    return(obj);
	}

	/* Open output file for reading */
	fp = FOpen((const char *)stdout_path, "rb");
	if(fp != NULL)
	{
	    gboolean got_header = FALSE;
	    gint i;
	    const gchar *delim_header = "------------";
	    gchar *s, buf[10000];

	    /* Read past header */
	    for(i = 0; TRUE; i++)
	    {
		if(fgets(buf, 10000, fp) != NULL)
		    buf[sizeof(buf) - 1] = '\0';
		else
		    break;

		if(strpfx((const char *)buf, (const char *)delim_header))
		{
		    got_header = TRUE;
		    break;
		}
	    }

	    /* Able to read past header? */
	    if(got_header ? (fgets(buf, 10000, fp) != NULL) : FALSE)
	    {
		gchar *complete_line;

		buf[sizeof(buf) - 1] = '\0';

		s = buf;
		while(ISBLANK(*s))
		    s++;

		/* Read a total of 2 lines (1 more) and concat them
		 * togeather
		 */
		complete_line = STRDUP(s);
		for(i = 0; i < 1; i++)
		{
		    if(fgets(buf, 10000, fp) != NULL)
		    {
			buf[sizeof(buf) - 1] = '\0';
			complete_line = G_STRCAT(complete_line, buf);
		    }
		    else
		    {
			break;
		    }
		}

		/* Create a new archive object structure and parse the
		 * loaded line to it
		 */
		obj = EDVArchObjectNew();
		EDVArchFIOParseLineRAR(obj, complete_line);

		g_free(complete_line);
	    }

	    FClose(fp);
	}

	/* Remove output files */
	UNLINK(stdout_path);
	UNLINK(stderr_path);

	g_free(cmd);
	g_free(stdout_path);
	g_free(stderr_path);

	return(obj);
}

/*
 *	Get stats for the object in the RedHat Package Manager
 *	package.
 *
 *	Inputs assumed valid.
 */
static edv_archive_object_struct *EDVArchFIOStatRPM(
	edv_core_struct *core_ptr,
	const gchar *arch_obj, const gchar *path
)
{
	const gchar *prog_rpm = CFGItemListGetValueS(
	    core_ptr->cfg_list, EDV_CFG_PARM_PROG_RPM
	);
	FILE *fp;
	gint p;
	gchar *cmd, *stdout_path, *stderr_path;
	edv_archive_object_struct *obj = NULL;


	/* Format archive list output command */
	cmd = g_strdup_printf(
	    "%s -q -l -p -v \"%s\"",
	    prog_rpm, arch_obj
	);
	if(cmd == NULL)
	    return(obj);

	/* Generate output file paths */
	stdout_path = EDVTmpName(NULL);
	stderr_path = EDVTmpName(NULL);

	/* Execute archive list output command */
	p = (gint)ExecBOE((const char *)cmd, (const char *)stdout_path, (const char *)stderr_path);
	if(p <= 0)
	{
	    g_free(cmd);
	    g_free(stdout_path);
	    g_free(stderr_path);
	    return(obj);
	}

	/* Open output file for reading */
	fp = FOpen((const char *)stdout_path, "rb");
	if(fp != NULL)
	{
	    gint i;
	    gchar *s, *s2;
	    gchar buf[10000];

	    /* Begin reading each line and get stats of the line that
	     * contains the given path
	     */
	    while(fgets(buf, 10000, fp) != NULL)
	    {
		buf[sizeof(buf) - 1] = '\0';

		s = buf;

		/* Skip initial spaces */
		while(ISBLANK(*s))
		    s++;

		/* Skip no more than 8 fields to position s2 at the
		 * file name argument
		 */
		s2 = s;
		for(i = 0; i < 8; i++)
		{
		    while(!ISBLANK(*s2) && (*s2 != '\0'))
			s2++;
		    while(ISBLANK(*s2))
			s2++;
		}

		/* Is this the object we want to obtain the stats for? */
		if(strpfx(s2, path))
		{
		    /* Create a new archive object structure and parse
		     * the loaded line to it
		     */
		    obj = EDVArchObjectNew();
		    EDVArchFIOParseLineRPM(obj, s);
		    break;
		}
	    }

	    FClose(fp);
	}

	/* Remove output files */
	UNLINK(stdout_path);
	UNLINK(stderr_path);

	g_free(cmd);
	g_free(stdout_path);
	g_free(stderr_path);

	return(obj);
}

/*
 *	Get stats for the object in the Tape Archive.
 *
 *	Inputs assumed valid.
 */
static edv_archive_object_struct *EDVArchFIOStatTar(
	edv_core_struct *core_ptr,
	const gchar *arch_obj, const gchar *path,
	gboolean is_compress_compressed,
	gboolean is_gzip_compressed,
	gboolean is_bzip2_compressed
)
{
	const gchar *prog_tar = CFGItemListGetValueS(
	    core_ptr->cfg_list, EDV_CFG_PARM_PROG_TAR
	);
	const gchar *prog_bunzip2 = CFGItemListGetValueS(
	    core_ptr->cfg_list, EDV_CFG_PARM_PROG_BUNZIP2
	);
	FILE *fp;
	gint p;
	gchar *cmd, *stdout_path, *stderr_path;
	edv_archive_object_struct *obj = NULL;


	/* Format archive list output command */
	if(is_compress_compressed)
	    cmd = g_strdup_printf(
		"%s -Z -t -v -f \"%s\" \"%s\"",
		prog_tar, arch_obj, path
	    );
	else if(is_gzip_compressed)
	    cmd = g_strdup_printf(
		"%s -z -t -v -f \"%s\" \"%s\"",
		prog_tar, arch_obj, path
	    );
	else if(is_bzip2_compressed)
	    cmd = g_strdup_printf(
		"%s \"--use-compress-program=%s\" -t -v -f \"%s\" \"%s\"",
		prog_tar, prog_bunzip2, arch_obj, path
	    );
	else
	    cmd = g_strdup_printf(
		"%s -t -v -f \"%s\" \"%s\"",
		prog_tar, arch_obj, path
	    );
	if(cmd == NULL)
	    return(obj);

	/* Generate output file paths */
	stdout_path = EDVTmpName(NULL);
	stderr_path = EDVTmpName(NULL);

	/* Execute archive list output command */
	p = (gint)ExecBOE((const char *)cmd, (const char *)stdout_path, (const char *)stderr_path);
	if(p <= 0)
	{
	    g_free(cmd);
	    g_free(stdout_path);
	    g_free(stderr_path);
	    return(obj);
	}

	/* Open output file for reading */
	fp = FOpen((const char *)stdout_path, "rb");
	if(fp != NULL)
	{
	    gchar *s, buf[10000];

	    if(fgets(buf, 10000, fp) != NULL)
	    {
		buf[sizeof(buf) - 1] = '\0';

		s = buf;

		/* Skip initial spaces */
		while(ISBLANK(*s))
		    s++;

		/* Create a new archive object structure and parse the
		 * loaded line to it
		 */
		obj = EDVArchObjectNew();
		EDVArchFIOParseLineTar(obj, buf);
	    }

	    FClose(fp);
	}

	/* Remove output files */
	UNLINK(stdout_path);
	UNLINK(stderr_path);

	g_free(cmd);
	g_free(stdout_path);
	g_free(stderr_path);

	return(obj);
}

/*
 *	Get stats for the object in the PKZip archive.
 *
 *	Inputs assumed valid.
 */
static edv_archive_object_struct *EDVArchFIOStatZip(
	edv_core_struct *core_ptr,
	const gchar *arch_obj, const gchar *path
)
{
	const gchar *prog_unzip = CFGItemListGetValueS(
	    core_ptr->cfg_list, EDV_CFG_PARM_PROG_UNZIP
	);
	FILE *fp;
	gint p;
	gchar *cmd, *stdout_path, *stderr_path;
	edv_archive_object_struct *obj = NULL;


	/* Format archive list output command */
	cmd = g_strdup_printf(
	    "%s -l -v \"%s\" \"%s\"",
	    prog_unzip, arch_obj, path
	);
	if(cmd == NULL)
	    return(obj);

	/* Generate output file paths */
	stdout_path = EDVTmpName(NULL);
	stderr_path = EDVTmpName(NULL);

	/* Execute archive list output command */
	p = (gint)ExecBOE((const char *)cmd, (const char *)stdout_path, (const char *)stderr_path);
	if(p <= 0)
	{
	    g_free(cmd);
	    g_free(stdout_path);
	    g_free(stderr_path);
	    return(obj);
	}

	/* Open output file for reading */
	fp = FOpen((const char *)stdout_path, "rb");
	if(fp != NULL)
	{
	    gboolean got_header = FALSE;
	    gint i;
	    const gchar *delim_header = "--------";
	    gchar *s, buf[10000];

	    /* Read past header */
	    for(i = 0; 1; i++)
	    {
		if(fgets(buf, 10000, fp) != NULL)
		    buf[sizeof(buf) - 1] = '\0';
		else
		    break;

		if(strpfx((const char *)buf, (const char *)delim_header))
		{
		    got_header = TRUE;
		    break;
		}
	    }

	    /* Able to read past header? */
	    if(got_header ? (fgets(buf, 10000, fp) != NULL) : FALSE)
	    {
		buf[sizeof(buf) - 1] = '\0';

		s = buf;

		/* Seek past spaces */
		while(ISBLANK(*s))
		    s++;

		/* Create a new archive object structure and parse the
		 * loaded line to it
		 */
		obj = EDVArchObjectNew();
		EDVArchFIOParseLineZip(obj, s);
	    }

	    FClose(fp);
	}

	/* Remove output files */
	UNLINK(stdout_path);
	UNLINK(stderr_path);

	g_free(cmd);
	g_free(stdout_path);
	g_free(stderr_path);

	return(obj);
}

/*
 *      Fetches statistics from the archive specified by arch_obj for
 *	the object in the archive specified by path.
 *
 *	The returned stats must be deallocated by the calling function.
 *
 *	Can return NULL on error.
 */
edv_archive_object_struct *EDVArchFIOStat(
	edv_core_struct *core_ptr,
	const gchar *arch_obj, const gchar *path,
	const gchar *password
)
{
	const gchar *arch_name;
	edv_archive_object_struct *obj = NULL;
	struct stat stat_buf;


	if((core_ptr == NULL) || STRISEMPTY(arch_obj) || STRISEMPTY(path))
	    return(obj);

	arch_name = EDVGetPathName(arch_obj);

	/* Get archive stats and making sure it is a file */
	if(stat((const char *)arch_obj, &stat_buf))
	    return(obj);
	if(!S_ISREG(stat_buf.st_mode))
	    return(obj);

	/* Get stats by the archive's extension */

	/* ARJ Archive */
	if(EDVIsExtension(arch_name, ".arj"))
	{
	    obj = EDVArchFIOStatARJ(
		core_ptr, arch_obj, path
	    );
	}
	/* LHA Archive */
	else if(EDVIsExtension(arch_name, ".lha"))
	{
	    obj = EDVArchFIOStatLHA(
		core_ptr, arch_obj, path
	    );
	}
	/* RAR Archive */
	else if(EDVIsExtension(arch_name, ".rar"))
	{
	    obj = EDVArchFIOStatRAR(
		core_ptr, arch_obj, path
	    );
	}
	/* RedHat Package Manager Package */
	else if(EDVIsExtension(arch_name, ".rpm"))
	{
	    obj = EDVArchFIOStatRPM(
		core_ptr, arch_obj, path
	    );
	}
	/* Tape Archive (Compressed) */
	else if(EDVIsExtension(arch_name, ".tar.Z"))
	{
	    obj = EDVArchFIOStatTar(
		core_ptr, arch_obj, path,
		TRUE,		/* Is compress compressed */
		FALSE,		/* Not gzip compressed */
		FALSE		/* Not bzip2 compressed */
	    );
	}
	/* Tape Archive (GZip) */
	else if(EDVIsExtension(arch_name, ".tgz .tar.gz"))
	{
	    obj = EDVArchFIOStatTar(
		core_ptr, arch_obj, path,
		FALSE,		/* Not compress compressed */
		TRUE,		/* Is gzip compressed */
		FALSE		/* Not bzip2 compressed */
	    );
	}
	/* Tape Archive (BZip2) */
	else if(EDVIsExtension(arch_name, ".tar.bz2"))
	{
	    obj = EDVArchFIOStatTar(
		core_ptr, arch_obj, path,
		FALSE,		/* Not compress compressed */
		FALSE,		/* Not gzip compressed */
		TRUE		/* Is bzip2 compressed */
	    );
	}
	/* Tape Archive */
	else if(EDVIsExtension(arch_name, ".tar"))
	{
	    obj = EDVArchFIOStatTar(
		core_ptr, arch_obj, path,
		FALSE,		/* Not compress compressed */
		FALSE,		/* Not gzip compressed */
		FALSE		/* Not bzip2 compressed */
	    );
	}
	/* PKZip Archive */
	else if(EDVIsExtension(arch_name, ".xpi .zip"))
	{
	    obj = EDVArchFIOStatZip(
		core_ptr, arch_obj, path
	    );
	}

	return(obj);
}


/*
 *      Returns a listing of all objects in the Arj archive.
 */
static edv_archive_object_struct **EDVArchFIOGetListingARJ(
	edv_core_struct *core_ptr,
	const gchar *arch_obj, gint *total,
	const gchar **path, gint npaths
)
{
	const gchar *prog_arj = CFGItemListGetValueS(
	    core_ptr->cfg_list, EDV_CFG_PARM_PROG_ARJ
	);
	FILE *fp;
	gint p;
	gchar *cmd, *stdout_path, *stderr_path;
	edv_archive_object_struct **list = NULL, *obj;


	/* Format archive list output command */
	cmd = g_strdup_printf(
	    "%s v -i -y \"%s\"",
	    prog_arj, arch_obj
	);
	if(cmd == NULL)
	    return(list);

	/* Generate output file paths */
	stdout_path = EDVTmpName(NULL);
	stderr_path = EDVTmpName(NULL);

	/* Execute archive list output command */
	p = (gint)ExecBOE((const char *)cmd, (const char *)stdout_path, (const char *)stderr_path);
	if(p <= 0)
	{
	    g_free(cmd);
	    g_free(stdout_path);
	    g_free(stderr_path);
	    return(list);
	}

	/* Open output file for reading */
	fp = FOpen((const char *)stdout_path, "rb");
	if(fp != NULL)
	{
	    gint i;
	    gchar *s, *vpath;
	    const gchar *delim_header = "------------";
	    gchar buf[10000];


	    /* Read past header */
	    for(i = 0; TRUE; i++)
	    {
		if(fgets(buf, 10000, fp) != NULL)
		    buf[sizeof(buf) - 1] = '\0';
		else
		    break;

		if(strpfx((const char *)buf, (const char *)delim_header))
		    break;
	    }

	    /* Begin reading each set of four lines describing each
	     * object in the ARJ archive
	     */
	    while(fgets(buf, 10000, fp) != NULL)
	    {
		buf[sizeof(buf) - 1] = '\0';

		s = buf;

		/* Skip initial spaces */
		while(ISBLANK(*s))
		    s++;

		/* Ending deliminator reached? If so do not parse this
		 * line or any subsequent lines
		 */
		if(*s == '-')
		    break;

		/* Seek vpath to the full path value segment in s,
		 * skipping 1 value in s
		 */
		vpath = s;
		for(i = 0; i < 1; i++)
		{
		    while(!ISBLANK(*vpath) && (*vpath != '\0'))
			vpath++;
		    while(ISBLANK(*vpath))
			vpath++;
		}

		/* Check if the path matches any of the paths in the
		 * list of filter paths, if no filter paths are given
		 * then match is always made
		 *
		 * The path may contain characters beyond the path
		 * portion of the string (they are ignored by the
		 * filter check)
		 */
		if(EDVArchFIOCheckFilterPath(
		    path, npaths, vpath
		))
		{
		    /* Read a total of 4 lines (3 more) and concat
		     * them togeather
		     */
		    gchar *complete_line = STRDUP(s);
		    for(i = 0; i < 3; i++)
		    {
			if(fgets(buf, 10000, fp) != NULL)
			{
			    buf[sizeof(buf) - 1] = '\0';
			    complete_line = G_STRCAT(complete_line, buf);
			}
			else
			{
			    break;
			}
		    }

		    /* Create a new archive object structure and parse
		     * the loaded line to it
		     */
		    obj = EDVArchObjectNew();
		    EDVArchFIOParseLineARJ(obj, complete_line);

		    g_free(complete_line);

		    /* Append this object to the list */
		    i = MAX(*total, 0);
		    *total = i + 1;
		    list = (edv_archive_object_struct **)g_realloc(
			list,
			(*total) * sizeof(edv_archive_object_struct *)
		    );
		    if(list == NULL)
		    {
			*total = 0;
			EDVArchObjectDelete(obj);
			break;
		    }
		    else
		    {
			list[i] = obj;
		    }
		}
	    }

	    FClose(fp);
	}

	/* Remove output files */
	UNLINK(stdout_path);
	UNLINK(stderr_path);

	g_free(cmd);
	g_free(stdout_path);
	g_free(stderr_path);

	return(list);
}

/*
 *      Returns a listing of all objects in the LHA Archive.
 */
static edv_archive_object_struct **EDVArchFIOGetListingLHA(
	edv_core_struct *core_ptr,
	const gchar *arch_obj, gint *total,
	const gchar **path, gint npaths
)
{
	const gchar *prog_lha = CFGItemListGetValueS(
	    core_ptr->cfg_list, EDV_CFG_PARM_PROG_LHA
	);
	FILE *fp;
	gint p;
	gchar *cmd, *stdout_path, *stderr_path;
	edv_archive_object_struct **list = NULL, *obj;


	/* Format archive list output command */
	cmd = g_strdup_printf(
	    "%s v \"%s\"",
	    prog_lha, arch_obj
	);
	if(cmd == NULL)
	    return(list);

	/* Generate output file paths */
	stdout_path = EDVTmpName(NULL);
	stderr_path = EDVTmpName(NULL);

	/* Execute archive list output command */
	p = (gint)ExecBOE((const char *)cmd, (const char *)stdout_path, (const char *)stderr_path);
	if(p <= 0)
	{
	    g_free(cmd);
	    g_free(stdout_path);
	    g_free(stderr_path);
	    return(list);
	}

	/* Open output file for reading */
	fp = FOpen((const char *)stdout_path, "rb");
	if(fp != NULL)
	{
	    gint i;
	    time_t cur_time = time(NULL);
	    gchar *s, *vpath;
	    const gchar *delim_header = "----------",
			*delim_footer = "----------";
	    gchar buf[10000];
	    const struct tm *tm_ptr;
	    struct tm cur_mtime_buf;


	    /* Get current time */
	    tm_ptr = localtime(&cur_time);
	    if(tm_ptr != NULL)
		memcpy(&cur_mtime_buf, tm_ptr, sizeof(struct tm));
	    else
		memset(&cur_mtime_buf, 0x00, sizeof(struct tm));


	    /* Read past header */
	    for(i = 0; TRUE; i++)
	    {
		if(fgets(buf, 10000, fp) != NULL)
		    buf[sizeof(buf) - 1] = '\0';
		else
		    break;

		if(strpfx((const char *)buf, (const char *)delim_header))
		    break;
	    }


	    /* Begin reading each line describing each object in the
	     * LHA archive
	     */
	    while(fgets(buf, 10000, fp) != NULL)
	    {
		buf[sizeof(buf) - 1] = '\0';

		/* Footer deliminator reached? */
		if(strpfx((const char *)buf, (const char *)delim_footer))
		    break;

		s = buf;

		/* Skip initial spaces */
		while(ISBLANK(*s))
		    s++;

		/* Seek vpath to the full path value segment in s,
		 * skipping 10 values in s
		 */
		vpath = s;
		for(i = 0; i < 10; i++)
		{
		    while(!ISBLANK(*vpath) && (*vpath != '\0'))
			vpath++;
		    while(ISBLANK(*vpath))
			vpath++;
		}

		/* Check if the path matches any of the paths in the
		 * list of filter paths, if no filter paths are given
		 * then match is always made
		 *
		 * The path may contain characters beyond the path
		 * portion of the string (they are ignored by the
		 * filter check)
		 */
		if(EDVArchFIOCheckFilterPath(
		    path, npaths, vpath
		))
		{
		    /* Create a new archive object structure and parse
		     * the loaded line to it
		     */
		    obj = EDVArchObjectNew();
		    EDVArchFIOParseLineLHA(obj, s, &cur_mtime_buf);

		    /* Append this object to the list */
		    i = MAX(*total, 0);
		    *total = i + 1;
		    list = (edv_archive_object_struct **)g_realloc(
			list,
			(*total) * sizeof(edv_archive_object_struct *)
		    );
		    if(list == NULL)
		    {
			*total = 0;
			EDVArchObjectDelete(obj);
			break;
		    }
		    else
		    {
			list[i] = obj;
		    }
		}
	    }

	    FClose(fp);
	}

	/* Remove output files */
	UNLINK(stdout_path);
	UNLINK(stderr_path);

	g_free(cmd);
	g_free(stdout_path);
	g_free(stderr_path);

	return(list);
}

/*
 *      Returns a listing of all objects in the RAR Archive.
 */
static edv_archive_object_struct **EDVArchFIOGetListingRAR(
	edv_core_struct *core_ptr,
	const gchar *arch_obj, gint *total,
	const gchar **path, gint npaths
)
{
	const gchar *prog_rar = CFGItemListGetValueS(
	    core_ptr->cfg_list, EDV_CFG_PARM_PROG_RAR
	);
	FILE *fp;
	gint p;
	gchar *cmd, *stdout_path, *stderr_path;
	edv_archive_object_struct **list = NULL, *obj;


	/* Format archive list output command */
	cmd = g_strdup_printf(
	    "%s v -y \"%s\"",
	    prog_rar, arch_obj
	);
	if(cmd == NULL)
	    return(list);

	/* Generate output file paths */
	stdout_path = EDVTmpName(NULL);
	stderr_path = EDVTmpName(NULL);

	/* Execute archive list output command */
	p = (gint)ExecBOE((const char *)cmd, (const char *)stdout_path, (const char *)stderr_path);
	if(p <= 0)
	{
	    g_free(cmd);
	    g_free(stdout_path);
	    g_free(stderr_path);
	    return(list);
	}

	/* Open output file for reading */
	fp = FOpen((const char *)stdout_path, "rb");
	if(fp != NULL)
	{
	    gint i;
	    gchar *s, *vpath;
	    const gchar	*delim_header = "------------",
			*delim_footer = "------------";
	    gchar buf[10000];


	    /* Read past header */
	    for(i = 0; TRUE; i++)
	    {
		if(fgets(buf, 10000, fp) != NULL)
		    buf[sizeof(buf) - 1] = '\0';
		else
		    break;

		if(strpfx((const char *)buf, (const char *)delim_header))
		    break;
	    }


	    /* Begin reading each set of two lines describing each
	     * object in the RAR archive
	     */
	    while(fgets(buf, 10000, fp) != NULL)
	    {
		buf[sizeof(buf) - 1] = '\0';

		/* Footer deliminator reached? */
		if(strpfx((const char *)buf, (const char *)delim_footer))
		    break;

		s = buf;

		/* Skip initial spaces */
		while(ISBLANK(*s))
		    s++;

		/* Seek vpath to the full path value segment in s,
		 * skipping 0 values in s
		 */
		vpath = s;
		for(i = 0; i < 0; i++)
		{
		    while(!ISBLANK(*vpath) && (*vpath != '\0'))
			vpath++;
		    while(ISBLANK(*vpath))
			vpath++;
		}

		/* Check if the path matches any of the paths in the
		 * list of filter paths, if no filter paths are given
		 * then match is always made
		 *
		 * The path may contain characters beyond the path
		 * portion of the string (they are ignored by the
		 * filter check)
		 */
		if(EDVArchFIOCheckFilterPath(
		    path, npaths, vpath
		))
		{
		    /* Read a total of 2 lines (1 more) and concat
		     * them togeather
		     */
		    gchar *complete_line = STRDUP(s);
		    for(i = 0; i < 1; i++)
		    {
			if(fgets(buf, 10000, fp) != NULL)
			{
			    buf[sizeof(buf) - 1] = '\0';
			    complete_line = G_STRCAT(complete_line, buf);
			}
			else
			{
			    break;
			}
		    }

		    /* Create a new archive object structure and parse
		     * the loaded line to it
		     */
		    obj = EDVArchObjectNew();
		    EDVArchFIOParseLineRAR(obj, complete_line);

		    g_free(complete_line);

		    /* Append this object to the list */
		    i = MAX(*total, 0);
		    *total = i + 1;
		    list = (edv_archive_object_struct **)g_realloc(
			list,
			(*total) * sizeof(edv_archive_object_struct *)
		    );
		    if(list == NULL)
		    {
			*total = 0;
			EDVArchObjectDelete(obj);
			break;
		    }
		    else
		    {
			list[i] = obj;
		    }
		}
	    }

	    FClose(fp);
	}

	/* Remove output files */
	UNLINK(stdout_path);
	UNLINK(stderr_path);

	g_free(cmd);
	g_free(stdout_path);
	g_free(stderr_path);

	return(list);
}

/*
 *      Returns a listing of all objects in the RPM archive.
 */
static edv_archive_object_struct **EDVArchFIOGetListingRPM(
	edv_core_struct *core_ptr,
	const gchar *arch_obj, gint *total,
	const gchar **path, gint npaths
)
{
	const gchar *prog_rpm = CFGItemListGetValueS(
	    core_ptr->cfg_list, EDV_CFG_PARM_PROG_RPM
	);
	FILE *fp;
	gint p;
	gchar *cmd, *stdout_path, *stderr_path;
	edv_archive_object_struct **list = NULL, *obj;


	/* Format archive list output command */
	cmd = g_strdup_printf(
	    "%s -q -l -p -v \"%s\"",
	    prog_rpm, arch_obj
	);
	if(cmd == NULL)
	    return(list);

	/* Generate output file paths */
	stdout_path = EDVTmpName(NULL);
	stderr_path = EDVTmpName(NULL);

	/* Execute archive list output command */
	p = (gint)ExecBOE((const char *)cmd, (const char *)stdout_path, (const char *)stderr_path);
	if(p <= 0)
	{
	    g_free(cmd);
	    g_free(stdout_path);
	    g_free(stderr_path);
	    return(list);
	}

	/* Read each line from the output file */
	fp = FOpen((const char *)stdout_path, "rb");
	if(fp != NULL)
	{
	    gint i;
	    gchar *s, *vpath;
	    gchar buf[10000];

	    /* Begin reading each line describing each object in the
	     * RedHat Package Manager package
	     */
	    while(fgets(buf, 10000, fp) != NULL)
	    {
		buf[sizeof(buf) - 1] = '\0';

		s = buf;

		/* Skip initial spaces */
		while(ISBLANK(*s))
		    s++;

		/* Seek vpath to the full path value segment in s,
		 * skipping 8 values in s
		 */
		vpath = s;
		for(i = 0; i < 8; i++)
		{
		    while(!ISBLANK(*vpath) && (*vpath != '\0'))
			vpath++;
		    while(ISBLANK(*vpath))
			vpath++;
		}

		/* Check if the path matches any of the paths in the
		 * list of filter paths, if no filter paths are given
		 * then match is always made
		 *
		 * The path may contain characters beyond the path
		 * portion of the string (they are ignored by the
		 * filter check)
		 */
		if(EDVArchFIOCheckFilterPath(
		    path, npaths, vpath
		))
		{
		    /* Create a new archive object structure and parse
		     * the loaded line to it
		     */
		    obj = EDVArchObjectNew();
		    EDVArchFIOParseLineRPM(obj, s);

		    /* Append this object to the list */
		    i = MAX(*total, 0);
		    *total = i + 1;
		    list = (edv_archive_object_struct **)g_realloc(
			list,
			(*total) * sizeof(edv_archive_object_struct *)
		    );
		    if(list == NULL)
		    {
			*total = 0;
			EDVArchObjectDelete(obj);
			break;
		    }
		    else
		    {
			list[i] = obj;
		    }
		}
	    }

	    FClose(fp);
	}

	/* Remove output files */
	UNLINK(stdout_path);
	UNLINK(stderr_path);

	g_free(cmd);
	g_free(stdout_path);
	g_free(stderr_path);

	return(list);
}

/*
 *	Returns a listing of all objects in the Tar archive.
 */
static edv_archive_object_struct **EDVArchFIOGetListingTar(
	edv_core_struct *core_ptr,
	const gchar *arch_obj, gint *total,
	const gchar **path, gint npaths,
	gboolean is_compress_compressed,
	gboolean is_gzip_compressed,
	gboolean is_bzip2_compressed
)
{
	const gchar *prog_tar = CFGItemListGetValueS(
	    core_ptr->cfg_list, EDV_CFG_PARM_PROG_TAR
	);
	const gchar *prog_bunzip2 = CFGItemListGetValueS(
	    core_ptr->cfg_list, EDV_CFG_PARM_PROG_BUNZIP2
	);
	FILE *fp;
	gint p;
	gchar *cmd, *stdout_path, *stderr_path;
	edv_archive_object_struct **list = NULL, *obj;


	/* Format archive list output command */
	if(is_compress_compressed)
	    cmd = g_strdup_printf(
		"%s -Z -t -v -f \"%s\"",
		prog_tar, arch_obj
	    );
	else if(is_gzip_compressed)
	    cmd = g_strdup_printf(
		"%s -z -t -v -f \"%s\"",
		prog_tar, arch_obj
	    );
	else if(is_bzip2_compressed)
	    cmd = g_strdup_printf(
		"%s \"--use-compress-program=%s\" -t -v -f \"%s\"",
		prog_tar, prog_bunzip2, arch_obj
	    );
	else
	    cmd = g_strdup_printf(
		"%s -t -v -f \"%s\"",
		prog_tar, arch_obj
	    );
	if(cmd == NULL)
	    return(list);

	/* Generate output file paths */
	stdout_path = EDVTmpName(NULL);
	stderr_path = EDVTmpName(NULL);

	/* Execute archive list output command */
	p = (gint)ExecBOE((const char *)cmd, (const char *)stdout_path, (const char *)stderr_path);
	if(p <= 0)
	{
	    g_free(cmd);
	    g_free(stdout_path);
	    g_free(stderr_path);
	    return(list);
	}

	/* Read each line from the output file */
	fp = FOpen((const char *)stdout_path, "rb");
	if(fp != NULL)
	{
	    gint i;
	    gchar *s, *vpath;
	    gchar buf[10000];

	    /* Begin reading each line describing each object in the
	     * Tape Archive
	     */
	    while(fgets(buf, 10000, fp) != NULL)
	    {
		buf[sizeof(buf) - 1] = '\0';

		s = buf;

		/* Skip initial spaces */
		while(ISBLANK(*s))
		    s++;

		/* Seek vpath to the full path value segment in s,
		 * skipping 5 value in s
		 */
		vpath = s;
		for(i = 0; i < 5; i++)
		{
		    while(!ISBLANK(*vpath) && (*vpath != '\0'))
			vpath++;
		    while(ISBLANK(*vpath))
			vpath++;
		}

		/* Check if the path matches any of the paths in the
		 * list of filter paths, if no filter paths are given
		 * then match is always made
		 *
		 * The path may contain characters beyond the path
		 * portion of the string (they are ignored by the
		 * filter check)
		 */
		if(EDVArchFIOCheckFilterPath(
		    path, npaths, vpath
		))
		{
		    /* Create a new archive object structure and parse
		     * the loaded line to it
		     */
		    obj = EDVArchObjectNew();
		    EDVArchFIOParseLineTar(obj, s);

		    /* Append this object to the list */
		    i = MAX(*total, 0);
		    *total = i + 1;
		    list = (edv_archive_object_struct **)g_realloc(
			list,
			(*total) * sizeof(edv_archive_object_struct *)
		    );
		    if(list == NULL)
		    {
			*total = 0;
			EDVArchObjectDelete(obj);
			break;
		    }
		    else
		    {
			list[i] = obj;
		    }
		}
	    }

	    FClose(fp);
	}

	/* Remove output files */
	UNLINK(stdout_path);
	UNLINK(stderr_path);

	g_free(cmd);
	g_free(stdout_path);
	g_free(stderr_path);

	return(list);
}

/*
 *	Returns a listing of all objects in the PKZip archive.
 */
static edv_archive_object_struct **EDVArchFIOGetListingZip(
	edv_core_struct *core_ptr,
	const gchar *arch_obj, gint *total,
	const gchar **path, gint npaths
)
{
	const gchar *prog_unzip = CFGItemListGetValueS(
	    core_ptr->cfg_list, EDV_CFG_PARM_PROG_UNZIP
	);
	FILE *fp;
	gint p;
	gchar *cmd, *stdout_path, *stderr_path;
	edv_archive_object_struct **list = NULL, *obj;


	/* Format archive list output command */
	cmd = g_strdup_printf(
	    "%s -l -v \"%s\"",
	    prog_unzip, arch_obj
	);
	if(cmd == NULL)
	    return(list);

	/* Generate output file paths */
	stdout_path = EDVTmpName(NULL);
	stderr_path = EDVTmpName(NULL);

	/* Execute archive list output command */
	p = (gint)ExecBOE((const char *)cmd, (const char *)stdout_path, (const char *)stderr_path);
	if(p <= 0)
	{
	    g_free(cmd);
	    g_free(stdout_path);
	    g_free(stderr_path);
	    return(list);
	}

	/* Open output file for reading */
	fp = FOpen((const char *)stdout_path, "rb");
	if(fp != NULL)
	{
	    gint i;
	    gchar *s, *vpath;
	    const gchar *delim_header = "--------";
	    gchar buf[10000];


	    /* Read past header */
	    for(i = 0; TRUE; i++)
	    {
		if(fgets(buf, 10000, fp) != NULL)
		    buf[sizeof(buf) - 1] = '\0';
		else
		    break;

		if(strpfx((const char *)buf, (const char *)delim_header))
		    break;
	    }

	    /* Begin reading each line describing each object in the
	     * PKZip archive
	     */
	    while(fgets(buf, 10000, fp) != NULL)
	    {
		buf[sizeof(buf) - 1] = '\0';

		s = buf;

		/* Skip initial spaces */
		while(ISBLANK(*s))
		    s++;

		/* Ending deliminator reached? If so do not parse this
		 * line or any subsequent lines
		 */
		if(*s == '-')
		    break;

		/* Seek vpath to the full path value segment in s,
		 * skipping 7 value in s
		 */
		vpath = s;
		for(i = 0; i < 7; i++)
		{
		    while(!ISBLANK(*vpath) && (*vpath != '\0'))
			vpath++;
		    while(ISBLANK(*vpath))
			vpath++;
		}

		/* Check if the path matches any of the paths in the
		 * list of filter paths, if no filter paths are given
		 * then match is always made
		 *
		 * The path may contain characters beyond the path
		 * portion of the string (they are ignored by the
		 * filter check)
		 */
		if(EDVArchFIOCheckFilterPath(
		    path, npaths, vpath
		))
		{
		    /* Create a new archive object structure and parse
		     * the loaded line to it
		     */
		    obj = EDVArchObjectNew();
		    EDVArchFIOParseLineZip(obj, s);

		    /* Append this object to the list */
		    i = MAX(*total, 0);
		    *total = i + 1;
		    list = (edv_archive_object_struct **)g_realloc(
			list,
			(*total) * sizeof(edv_archive_object_struct *)
		    );
		    if(list == NULL)
		    {
			*total = 0;
			EDVArchObjectDelete(obj);
			break;
		    }
		    else
		    {
			list[i] = obj;
		    }
		}
	    }

	    FClose(fp);
	}

	/* Remove output files */
	UNLINK(stdout_path);
	UNLINK(stderr_path);

	g_free(cmd);
	g_free(stdout_path);
	g_free(stderr_path);

	return(list);
}

/*
 *	Returns a list of archive object stats for objects found in
 *	the archive arch_obj.
 *
 *	If the given filter list path and npaths is NULL and 0 then
 *	stats for all objectz in the archive will be returned.
 *
 *	If a filter list path and npaths is given then only stats for
 *	the objects found in the archive that match the given filter
 *	will be returned.
 *
 *	The returned list of stats must be deallocated by the calling
 *	function.
 *
 *	Can return NULL if the archive is not supported, error, or no
 *	objects were found in the archive.
 */
edv_archive_object_struct **EDVArchFIOGetListing(
	edv_core_struct *core_ptr,
	const gchar *arch_obj, gint *total,
	const gchar **path, gint npaths,
	const gchar *password
)
{
	const gchar *arch_name;
	struct stat stat_buf;
	edv_archive_object_struct **list = NULL;


	if(total != NULL)
	    *total = 0;

	if((core_ptr == NULL) || STRISEMPTY(arch_obj) || (total == NULL))
	    return(list);

        arch_name = EDVGetPathName(arch_obj);

	/* Get archive stats and make sure it is a file */
	if(stat((const char *)arch_obj, &stat_buf))
	    return(list);
	if(!S_ISREG(stat_buf.st_mode))
	    return(list);

	/* Get listing by the archive's extension */

	/* ARJ Archive */
	if(EDVIsExtension(arch_name, ".arj"))
	{
	    list = EDVArchFIOGetListingARJ(
		core_ptr, arch_obj, total,
		path, npaths
	    );
	}
	/* LHA Archive */
	else if(EDVIsExtension(arch_name, ".lha"))
	{
	    list = EDVArchFIOGetListingLHA(
		core_ptr, arch_obj, total,
		path, npaths
	    );
	}
	/* RAR Archive */
	else if(EDVIsExtension(arch_name, ".rar"))
	{
	    list = EDVArchFIOGetListingRAR(
		core_ptr, arch_obj, total,
		path, npaths
	    );
	}
	/* RedHat Package Manager Package */
	else if(EDVIsExtension(arch_name, ".rpm"))
	{
	    list = EDVArchFIOGetListingRPM(
		core_ptr, arch_obj, total,
		path, npaths
	    );
	}
	/* Tape Archive (Compressed) */
	else if(EDVIsExtension(arch_name, ".tar.Z"))
	{
	    list = EDVArchFIOGetListingTar(
		core_ptr, arch_obj, total,
		path, npaths,
		TRUE,		/* Not compress compressed */
		FALSE,		/* Not gzip compressed */
		FALSE		/* Not bzip2 compressed */
	    );
	}
	/* Tape Archive (GZip) */
	else if(EDVIsExtension(arch_name, ".tgz .tar.gz"))
	{
	    list = EDVArchFIOGetListingTar(
		core_ptr, arch_obj, total,
		path, npaths,
		FALSE,		/* Not compress compressed */
		TRUE,           /* Is gzip compressed */
		FALSE           /* Not bzip2 compressed */
	    );
	}
	/* Tape Archive (BZip2) */
	else if(EDVIsExtension(arch_name, ".tar.bz2"))
	{
	    list = EDVArchFIOGetListingTar(
		core_ptr, arch_obj, total,
		path, npaths,
		FALSE,          /* Not compress compressed */
		FALSE,          /* Not gzip compressed */
		TRUE            /* Is bzip2 compressed */
	    );
	}
	/* Tape Archive */
	else if(EDVIsExtension(arch_name, ".tar"))
	{
	    list = EDVArchFIOGetListingTar(
		core_ptr, arch_obj, total,
		path, npaths,
		FALSE,          /* Not compress compressed */
		FALSE,          /* Not gzip compressed */
		FALSE           /* Not bzip2 compressed */
	    );
	}
	/* PKZip Archive */
	else if(EDVIsExtension(arch_name, ".xpi .zip"))
	{
	    list = EDVArchFIOGetListingZip(
		core_ptr, arch_obj, total,
		path, npaths
	    );
	}

	return(list);
}


/*
 *      Fetches the comments from the ARJ Archive specified by
 *      arch_obj.
 *
 *      Inputs assumed valid.
 */
static gchar **EDVArchFIOGetCommentARJ(
	edv_core_struct *core_ptr, const gchar *arch_obj,
	gint *total
)
{
	const gchar *prog_arj = CFGItemListGetValueS(
	    core_ptr->cfg_list, EDV_CFG_PARM_PROG_ARJ
	);
	FILE *fp;
	gint p;
	gchar *cmd, *stdout_path, *stderr_path;
	gchar **strv = NULL;
	gint strc = 0;

	/* Reset total */
	*total = strc;

	/* Format archive comment output command */
	cmd = g_strdup_printf(
	    "%s l -y -i \"%s\" nosuchfile",
	    prog_arj, arch_obj
	);
	if(cmd == NULL)
	    return(strv);

	/* Generate output file paths */
	stdout_path = EDVTmpName(NULL);
	stderr_path = EDVTmpName(NULL);

	/* Execute archive comment output command */
	p = (gint)ExecBOE((const char *)cmd, (const char *)stdout_path, (const char *)stderr_path);
	if(p <= 0)
	{
	    g_free(cmd);
	    g_free(stdout_path);
	    g_free(stderr_path);
	    return(strv);
	}

	/* Open output file for reading */
	fp = FOpen((const char *)stdout_path, "rb");
	if(fp != NULL)
	{
	    gboolean got_header = FALSE;
	    gint i;
	    const gchar	*delim_header = "Archive created:",
			*delim_footer = "     0 file(s)";
	    gchar *s, *s2, buf[10000];

	    /* Read past header */
	    for(i = 0; TRUE; i++)
	    {
		if(fgets(buf, 10000, fp) == NULL)
		    break;

		if(strpfx((const char *)buf, (const char *)delim_header))
		{
		    got_header = TRUE;
		    break;
		}
	    }

	    /* Read past header? */
	    if(got_header)
	    {
		/* Begin reading subsequent lines and save each line as
		 * comment lines
		 */
		while(fgets(buf, 10000, fp) != NULL)
		{
		    buf[sizeof(buf) - 1] = '\0';

		    /* Footer deliminator encountered? */
		    if(strpfx((const char *)buf, (const char *)delim_footer))
			break;

		    /* Save this line as a comment line */
		    i = strc;
		    strc = i + 1;
		    strv = (gchar **)g_realloc(
			strv, strc * sizeof(gchar *)
		    );
		    if(strv == NULL)
		    {
			strc = 0;
			break;
		    }
		    strv[i] = s = STRDUP(buf);

		    /* Remove the newline and other junk characters that
		     * we do not want from this comment line
		     */
		    for(s2 = s; *s2 != '\0'; s2++)
		    {
			if(ISCR(*s2))
			{
			    *s2 = '\0';
			    break;
			}
			else if(!isprint(*s2))
			{
			    *s2 = ' ';
			}
		    }
		}
	    }

	    FClose(fp);
	}

	/* Remove output files */
	UNLINK(stdout_path);
	UNLINK(stderr_path);

	g_free(cmd);
	g_free(stdout_path);
	g_free(stderr_path);

	/* Update total */
	*total = strc;

	return(strv);
}

/*
 *	Fetches the comments from the RAR Archive specified by
 *	arch_obj.
 *
 *	Inputs assumed valid.
 */
static gchar **EDVArchFIOGetCommentRAR(
	edv_core_struct *core_ptr, const gchar *arch_obj,
	gint *total
)
{
	const gchar *prog_rar = CFGItemListGetValueS(
	    core_ptr->cfg_list, EDV_CFG_PARM_PROG_RAR
	);
	FILE *fp;
	gint p;
	gchar *cmd, *stdout_path, *stderr_path;
	gchar **strv = NULL;
	gint strc = 0;

	/* Reset total */
	*total = strc;

	/* Format archive comment output command */
	cmd = g_strdup_printf(
	    "%s l -y \"%s\"",
	    prog_rar, arch_obj
	);
	if(cmd == NULL)
	    return(strv);

	/* Generate output file paths */
	stdout_path = EDVTmpName(NULL);
	stderr_path = EDVTmpName(NULL);

	/* Execute archive comment output command */
	p = (gint)ExecBOE((const char *)cmd, (const char *)stdout_path, (const char *)stderr_path);
	if(p <= 0)
	{
	    g_free(cmd);
	    g_free(stdout_path);
	    g_free(stderr_path);
	    return(strv);
	}

	/* Open output file for reading */
	fp = FOpen((const char *)stdout_path, "rb");
	if(fp != NULL)
	{
	    gint i;
	    const gchar	*delim_footer = "Archive ";
	    gchar *s, *s2, buf[10000];

	    /* Read past header, skip 4 lines */
	    for(i = 0; i < 4; i++)
	    {
		if(fgets(buf, 10000, fp) == NULL)
		    break;
	    }

	    /* Able to read past header? */
	    if(i >= 1)
	    {
		/* Begin reading subsequent lines and save each line as
		 * comment lines
		 */
		while(fgets(buf, 10000, fp) != NULL)
		{
		    buf[sizeof(buf) - 1] = '\0';

		    /* Footer deliminator encountered? */
		    if(strpfx((const char *)buf, (const char *)delim_footer))
		    {
			/* Since the deliminator may be ambiguous with
			 * other text from the comments, we need to
			 * confirm if since the footer deliminator also
			 * has the archive object's full path that we
			 * can verify
			 */
			s = buf + STRLEN(delim_footer);
			if(strpfx(s, arch_obj))
			    break;
		    }

		    /* Save this line as a comment line */
		    i = strc;
		    strc = i + 1;
		    strv = (gchar **)g_realloc(
			strv, strc * sizeof(gchar *)
		    );
		    if(strv == NULL)
		    {
			strc = 0;
			break;
		    }
		    strv[i] = s = STRDUP(buf);

		    /* Remove the newline and other junk characters that
		     * we do not want from this comment line
		     */
		    for(s2 = s; *s2 != '\0'; s2++)
		    {
			if(ISCR(*s2))
			{
			    *s2 = '\0';
			    break;
			}
			else if(!isprint(*s2))
			{
			    *s2 = ' ';
			}
		    }
		}
	    }

	    FClose(fp);
	}

	/* Need to remove the last line (if any) of the comment since
	 * since there is always an extra line appended to the comment
	 * used to separate/distingush between the comment from the
	 * footer deliminator
	 */
	if(strc >= 2)
	{
	    gint i;

	    for(i = strc - 1; i < strc; i++)
		g_free(strv[i]);

	    strc -= 1;
	    strv = (gchar **)g_realloc(
		strv, strc * sizeof(gchar *)
	    );
	}

	/* Remove output files */
	UNLINK(stdout_path);
	UNLINK(stderr_path);

	g_free(cmd);
	g_free(stdout_path);
	g_free(stderr_path);

	/* Update total */
	*total = strc;

	return(strv);
}

/*
 *	Fetches the comments from the PKZip Package archive specified
 *	by arch_obj.
 *
 *	Inputs assumed valid.
 */
static gchar **EDVArchFIOGetCommentZip(
	edv_core_struct *core_ptr, const gchar *arch_obj,
	gint *total
)
{
	const gchar *prog_unzip = CFGItemListGetValueS(
	    core_ptr->cfg_list, EDV_CFG_PARM_PROG_UNZIP
	);
	FILE *fp;
	gint p;
	gchar *cmd, *stdout_path, *stderr_path;
	gchar **strv = NULL;
	gint strc = 0;

	/* Reset total */
	*total = strc;

	/* Format archive comment output command */
	cmd = g_strdup_printf(
	    "%s -z \"%s\"",
	    prog_unzip, arch_obj
	);
	if(cmd == NULL)
	    return(strv);

	/* Generate output file paths */
	stdout_path = EDVTmpName(NULL);
	stderr_path = EDVTmpName(NULL);

	/* Execute archive comment output command */
	p = (gint)ExecBOE((const char *)cmd, (const char *)stdout_path, (const char *)stderr_path);
	if(p <= 0)
	{
	    g_free(cmd);
	    g_free(stdout_path);
	    g_free(stderr_path);
	    return(strv);
	}

	/* Open output file for reading */
	fp = FOpen((const char *)stdout_path, "rb");
	if(fp != NULL)
	{
	    gint i;
	    gchar *s, *s2, buf[10000];

	    /* Read past header, skip 1 line */
	    for(i = 0; i < 1; i++)
	    {
		if(fgets(buf, 10000, fp) == NULL)
		    break;
	    }

	    /* Able to read past header? */
	    if(i >= 1)
	    {
		/* Begin reading subsequent lines and save each line as
		 * comment lines
		 */
		while(fgets(buf, 10000, fp) != NULL)
		{
		    buf[sizeof(buf) - 1] = '\0';

		    /* Save this line as a comment line */
		    i = strc;
		    strc = i + 1;
		    strv = (gchar **)g_realloc(
			strv, strc * sizeof(gchar *)
		    );
		    if(strv == NULL)
		    {
			strc = 0;
			break;
		    }
		    strv[i] = s = STRDUP(buf);

		    /* Remove the newline and other junk characters that
		     * we do not want from this comment line
		     */
		    for(s2 = s; *s2 != '\0'; s2++)
		    {
			if(ISCR(*s2))
			{
			    *s2 = '\0';
			    break;
			}
			else if(!isprint(*s2))
			{
			    *s2 = ' ';
			}
		    }
		}
	    }

	    FClose(fp);
	}

	/* Remove output files */
	UNLINK(stdout_path);
	UNLINK(stderr_path);

	g_free(cmd);
	g_free(stdout_path);
	g_free(stderr_path);

	/* Update total */
	*total = strc;

	return(strv);
}

/*
 *	Returns the comment (if any) from the archive specified by
 *	arch_obj as a list of strings, each specifying one line of the
 *	comment.
 *
 *	If the archive object does not have a comment or does not
 *	support comments then NULL is returned.
 *
 *	The calling function must delete the returned strings and the
 *	pointer array.
 */
gchar **EDVArchFIOGetComment(
	edv_core_struct *core_ptr, const gchar *arch_obj,
	gint *total
)
{
	const gchar *arch_name;
	gchar **strv = NULL;
	gint strc = 0;
	struct stat stat_buf;

	if(total != NULL)
	    *total = 0;

	if((core_ptr == NULL) || STRISEMPTY(arch_obj) || (total == NULL))
	    return(strv);

        arch_name = EDVGetPathName(arch_obj);

	/* Get archive stats and make sure it is a file */
	if(stat(arch_obj, &stat_buf))
	    return(strv);
	if(!S_ISREG(stat_buf.st_mode))
	    return(strv);

	/* Get comment by the archive's extension */

	/* ARJ Archive */
	if(EDVIsExtension(arch_name, ".arj"))
	{
	    strv = EDVArchFIOGetCommentARJ(
		core_ptr, arch_obj, &strc
	    );
	}
	/* RAR Archive */
	else if(EDVIsExtension(arch_name, ".rar"))
	{
	    strv = EDVArchFIOGetCommentRAR(
		core_ptr, arch_obj, &strc
	    );
	}
	/* PKZip Archive */
	else if(EDVIsExtension(arch_name, ".xpi .zip"))
	{
	    strv = EDVArchFIOGetCommentZip(
		core_ptr, arch_obj, &strc
	    );
	}

	/* Update total */
	*total = strc;

	return(strv);
}


/*
 *	Used by EDVArchFIOSetComment*() functions to write the comments
 *	to file and adding a prefix and postfix (if not NULL) to it.
 *
 *	This file is then used be sent to the archive program for
 *	adding of comment.
 *
 *	Inputs assumed valid.
 */
static void EDVArchFIOSetCommentWriteCommentSpool(
	const gchar *path,
	const gchar *prefix, const gchar *postfix,
	const gchar *comment
)
{
	FILE *fp = FOpen(path, "wb");
	if(fp == NULL)
	    return;

	if(!STRISEMPTY(prefix))
	    fwrite(prefix, sizeof(gchar), STRLEN(prefix), fp);

	if(!STRISEMPTY(comment))
	    fwrite(comment, sizeof(gchar), STRLEN(comment), fp);

	if(!STRISEMPTY(postfix))
	    fwrite(postfix, sizeof(gchar), STRLEN(postfix), fp);

	FClose(fp);
}

/*
 *	Set comment for the ARJ Archive.
 */
static gint EDVArchFIOSetCommentARJ(
	edv_core_struct *core_ptr, const gchar *arch_obj,
	const gchar *comment
)
{
	const gchar *prog_arj = CFGItemListGetValueS(
	    core_ptr->cfg_list, EDV_CFG_PARM_PROG_ARJ
	);
	gint p;

	/* Get comment spool file path */
	gchar *spool_file = EDVTmpName(NULL);

	/* Format set archive comment command */
	gchar *cmd = g_strdup_printf(
	    "%s c -y -i \"%s\" \"-z%s\"",
	    prog_arj,
	    arch_obj,
	    spool_file
	);

	/* Generate output file paths */
	gchar   *stdout_path = EDVTmpName(NULL),
		*stderr_path = EDVTmpName(NULL);

	/* Write comment to spool file for archive program to use */
	EDVArchFIOSetCommentWriteCommentSpool(
	    spool_file,
	    NULL,               /* No prefix */
	    NULL,               /* No postfix */
	    comment             /* Comment */
	);

	/* Execute set archive comment command */
	p = (gint)ExecBOE((const char *)cmd, (const char *)stdout_path, (const char *)stderr_path);

	/* Delete command */
	g_free(cmd);

	/* Remove output files */
	UNLINK(stdout_path);
	g_free(stdout_path);
	UNLINK(stderr_path);
	g_free(stderr_path);

	/* Remove comment spool file */
	UNLINK(spool_file);
	g_free(spool_file);

	return((p == 0) ? -1 : 0);
}

/*
 *      Set comment for the RAR Archive.
 */
static gint EDVArchFIOSetCommentRAR(
	edv_core_struct *core_ptr, const gchar *arch_obj,
	const gchar *comment
)
{
	const gchar *prog_rar = CFGItemListGetValueS(
	    core_ptr->cfg_list, EDV_CFG_PARM_PROG_RAR
	);
	gint p;

	/* Get comment spool file path */
	gchar *spool_file = EDVTmpName(NULL);

	/* Format set archive comment command */
	gchar *cmd = g_strdup_printf(
	    "%s c -y \"-z%s\" \"%s\"",
	    prog_rar,
	    spool_file,
	    arch_obj
	);

	/* Generate output file paths */
	gchar	*stdout_path = EDVTmpName(NULL),
		*stderr_path = EDVTmpName(NULL);

	/* Write comment to spool file for archive program to use */
	EDVArchFIOSetCommentWriteCommentSpool(
	    spool_file,
	    NULL,		/* No prefix */
	    NULL,		/* No postfix */
	    comment		/* Comment */
	);

	/* Execute set archive comment command */
	p = (gint)ExecBOE((const char *)cmd, (const char *)stdout_path, (const char *)stderr_path);

	/* Delete command */
	g_free(cmd);

	/* Remove output files */
	UNLINK(stdout_path);
	g_free(stdout_path);
	UNLINK(stderr_path);
	g_free(stderr_path);

	/* Remove comment spool file */
	UNLINK(spool_file);
	g_free(spool_file);

	return((p == 0) ? -1 : 0);
}

/*
 *	Set comment for the PKZip archive.
 */
static gint EDVArchFIOSetCommentZip(
	edv_core_struct *core_ptr, const gchar *arch_obj,
	const gchar *comment
)
{
	const gchar *prog_zip = CFGItemListGetValueS(
	    core_ptr->cfg_list, EDV_CFG_PARM_PROG_ZIP
	);
	gint pid, status;

	/* Get comment spool file path */
	gchar *spool_file = EDVTmpName(NULL);

	/* Format set archive comment command */
	gchar *cmd = g_strdup_printf(
	    "%s %s | %s -z %s > /dev/null 2> /dev/null",
	    "cat",
	    spool_file,
	    prog_zip,
	    arch_obj
	);

	/* Write comment to spool file for archive program to use */
	EDVArchFIOSetCommentWriteCommentSpool(
	    spool_file,
	    NULL,		/* No prefix */
	    "\n.\n",		/* Postfix */
	    comment		/* Comment */
	);

	/* Execute set archive comment command */
	pid = EDVSystemBlock(cmd, &status);

	/* Delete command */
	g_free(cmd);

	/* Remove comment spool file */
	UNLINK(spool_file);
	g_free(spool_file);

	return((pid > 0) ? 0 : -1);
}

/*
 *	Sets comment for the archive specified by arch_obj.
 *
 *	Returns values:
 *
 *	0	Success
 *	-1	Error setting comment
 *	-2	Comments are not supported by this archive format
 *	-3	Systems error
 */
gint EDVArchFIOSetComment(
	edv_core_struct *core_ptr, const gchar *arch_obj,
	gchar **strv, gint strc
)
{
	gint i, status;
	const gchar *arch_name;
	gchar *comment;
	struct stat stat_buf;

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

        arch_name = EDVGetPathName(arch_obj);

	/* Get archive stats and make sure it is a file */
	if(stat(arch_obj, &stat_buf))
	    return(-1);
	if(!S_ISREG(stat_buf.st_mode))
	    return(-1);

	/* Generate comment line */
	comment = NULL;
	for(i = 0; i < strc; i++)
	{
	    if(strv[i] == NULL)
		continue;

	    comment = G_STRCAT(comment, strv[i]);
	    if(i < (strc - 1))
		comment = G_STRCAT(comment, "\n");
	}

	/* Set comment by the archive's extension */
	status = -2;

	/* ARJ Archive */
	if(EDVIsExtension(arch_name, ".arj"))
	{
	    status = EDVArchFIOSetCommentARJ(
		core_ptr, arch_obj, comment
	    );
	}
	/* RAR Archive */
	else if(EDVIsExtension(arch_name, ".rar"))
	{
	    status = EDVArchFIOSetCommentRAR(
		core_ptr, arch_obj, comment
	    );
	}
	/* PKZip Archive */
	else if(EDVIsExtension(arch_name, ".xpi .zip"))
	{
	    status = EDVArchFIOSetCommentZip(
		core_ptr, arch_obj, comment
	    );
	}

	g_free(comment);

	return(status);
}
