#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if defined(__FreeBSD__)
# include <fstab.h>
# include <sys/param.h>
# include <sys/ucred.h>
# include <sys/mount.h>
#elif defined(__SOLARIS__)
# include <sys/mnttab.h>
# include <sys/vfstab.h>
#else
# include <mntent.h>
# include <sys/vfs.h>
#endif
#include <sys/stat.h>
#include <gtk/gtk.h>

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

#include "edv_types.h"
#include "edv_device.h"
#include "edv_devices_list.h"
#include "config.h"


/* Devices List Matching */
edv_device_struct *EDVDevicesListMatchMountPath(
	edv_device_struct **list, const gint total,
	gint *n,
	const gchar *mount_path
);
edv_device_struct *EDVDevicesListMatchDevicePath(
	edv_device_struct **list, const gint total,
	gint *n,
	const gchar *device_path
);
edv_device_struct *EDVDevicesListMatchObject(
	edv_device_struct **list, const gint total,
	gint *n,
	const gchar *path
);

/* Get Devices List From System */
void EDVDevicesListLoadFromSystem(
	edv_device_struct ***list, gint *total,
	gint (*progress_cb)(gpointer, const gulong, const gulong),
	gpointer client_data
);

/* Mount States */
void EDVDevicesListUpdateMountStatesFromSystem(
	edv_device_struct **list, const gint total
);
void EDVDevicesListUpdateMountStates(
	edv_device_struct **list, const gint total
);

/* Stats */
void EDVDevicesListUpdateStats(
	edv_device_struct **list, const gint total
);

/* Devices List Open & Save */
void EDVDevicesListFileOpen(
	const gchar *filename,
	edv_device_struct ***list, gint *total,
	gint (*progress_cb)(gpointer, const gulong, const gulong),
	gpointer client_data
);
void EDVDeviceListFileSave(
	const gchar *filename,
	edv_device_struct **list, const gint total,
	gint (*progress_cb)(gpointer, const gulong, const gulong),
	gpointer client_data
);


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


/*
 *	Matches the Device from the Devices List who's mount_path 
 *	matches the specified mount_path. 
 */
edv_device_struct *EDVDevicesListMatchMountPath(
	edv_device_struct **list, const gint total,
	gint *n,
	const gchar *mount_path
)
{
	gint i;
	edv_device_struct *d;

	if(n != NULL)
	    *n = -1;

	if(STRISEMPTY(mount_path))
	    return(NULL);

	/* Iterate through the devices list */
	for(i = 0; i < total; i++)
	{
	    d = list[i];
	    if((d != NULL) ? STRISEMPTY(d->mount_path) : TRUE)
		continue;

	    if(!strcmp(d->mount_path, mount_path))
	    {
		if(n != NULL)
		    *n = i;
		return(d);
	    }
	}

	return(NULL);
}

/*
 *	Matches the Device in the Devices List who's device_path
 *	matches the specified device_path.
 */
edv_device_struct *EDVDevicesListMatchDevicePath(
	edv_device_struct **list, const gint total,
	gint *n,
	const gchar *device_path
)
{
	gint i;
	edv_device_struct *d;

	if(n != NULL)
	    *n = -1;

	if(STRISEMPTY(device_path))
	    return(NULL);

	/* Iterate through the devices list */
	for(i = 0; i < total; i++)
	{
	    d = list[i];
	    if((d != NULL) ? STRISEMPTY(d->device_path) : TRUE)
		continue;

	    if(!strcmp(d->device_path, device_path))
	    {
		if(n != NULL)
		    *n = i;
		return(d);
	    }
	}

	return(NULL);
}

/*
 *	Matches the Device in the Devices List that references the 
 *	object specified by path.
 */
edv_device_struct *EDVDevicesListMatchObject(
	edv_device_struct **list, const gint total,
	gint *n,
	const gchar *path
)
{
	gint i;
	edv_device_struct *d;

	if(n != NULL)
	    *n = -1;

	if(STRISEMPTY(path))
	    return(NULL);

	/* Iterate through the devices list */
	for(i = 0; i < total; i++)
	{
	    d = list[i];
	    if((d != NULL) ? STRISEMPTY(d->mount_path) : TRUE)
		continue;

	    if(strpfx(path, d->mount_path))
	    {
		if(n != NULL)
		    *n = i;
		return(d);
	    }
	}    
	 
	return(NULL);
}




/*
 *	Loads devices found in the system (/etc/fstab) list of devices
 *	and appends them to the Devices list.
 */
void EDVDevicesListLoadFromSystem(
	edv_device_struct ***list, gint *total,
	gint (*progress_cb)(gpointer, const gulong, const gulong),
	gpointer client_data
)
{
	FILE *fp;
	gulong file_size;
	gint dev_num;
	edv_device_struct *dev;
#if defined(__FreeBSD__)
	struct fstab *fs_ptr;
#elif defined(__SOLARIS__)
	struct vfstab *vfs_ptr;
	int mtback;
#else
	struct mntent *mt_ptr;
#endif
	struct stat stat_buf;

	if((list == NULL) || (total == NULL))
	    return;

	/* Open the system's devices list file */
#if defined(__FreeBSD__)
	setfsent();
	fp = FOpen("/etc/fstab", "rb");
#elif defined(__SOLARIS__)
	fp = FOpen("/etc/vfstab", "rb");
#else
	fp = setmntent("/etc/fstab", "rb");
#endif
	if(fp == NULL)
	    return;

	/* Get file statistics */
	if(fstat(fileno(fp), &stat_buf))
	    file_size = 0l;
	else
	    file_size = (gulong)stat_buf.st_size;

	/* Report initial progress */
	if(progress_cb != NULL)
	    progress_cb(client_data, 0l, file_size);

	/* Begin reading the system's devices list file */
#if defined(__FreeBSD__)
	fs_ptr = getfsent();
	while(fs_ptr != NULL)
#elif defined(__SOLARIS__)
	vfs_ptr = (struct vfstab *)g_malloc(sizeof(struct vfstab));
	mtback = getvfsent(fp, vfs_ptr);
	while(mtback != 0)
#else
	mt_ptr = getmntent(fp);
	while(mt_ptr != NULL)
#endif
	{
	    /* Report progress */
	    if(progress_cb != NULL)
	    {
		if(progress_cb(
		    client_data, (gulong)ftell(fp), file_size
		))
		    break;
	    }

	    /* Check if an existing device already exists in
	     * the list that matches the device_path
	     */
	    dev = EDVDevicesListMatchDevicePath(
		*list, *total,
		&dev_num,
#if defined(__FreeBSD__)
		fs_ptr->fs_spec
#elif defined(__SOLARIS__)
		vfs_ptr->vfs_special
#else
		mt_ptr->mnt_fsname
#endif
	    );
	    if(dev == NULL)
	    {
		/* No existing device structure exists that matches the
		 * device path, so create a new one
		 */
		dev_num = *total;
		*total = dev_num + 1;
		*list = (edv_device_struct **)g_realloc(
		    *list, (*total) * sizeof(edv_device_struct *)
		);
		if(*list == NULL)
		{
		    *total = 0;
		    dev_num = -1;
		    dev = NULL;
		    break;
		}
		else
		{
		    (*list)[dev_num] = dev = EDVDeviceNew(
#if defined(__FreeBSD__)
			EDVDeviceGetFSTypeFromName(fs_ptr->fs_vfstype),
			NULL,
			fs_ptr->fs_spec,
			fs_ptr->fs_file
#elif defined(__SOLARIS__)
			EDVDeviceGetFSTypeFromName(vfs_ptr->vfs_fstype),
			NULL,
			vfs_ptr->vfs_special,
			vfs_ptr->vfs_mountp
#else
			EDVDeviceGetFSTypeFromName(mt_ptr->mnt_type),
			NULL,
			mt_ptr->mnt_fsname,
			mt_ptr->mnt_dir
#endif
		    );
		    if(dev == NULL)
		    {
			dev_num = -1;
			break;
		    }

		    /* Set other default values for this new device */

		    /* Default icons */
		    if(EDV_DEVICE_TOTAL_ICON_STATES >= 1)
		    {
			/* Small icon? */
			if(dev->small_icon_file[0] == NULL)
			{
			    dev->small_icon_file[0] = STRDUP(
				EDV_DEF_DEVICE_ICON_SMALL_FILE
			    );
			}
			/* Medium icon? */
			if(dev->medium_icon_file[0] == NULL)
			{
			    dev->medium_icon_file[0] = STRDUP(
				EDV_DEF_DEVICE_ICON_MEDIUM_FILE
			    );
			}
			/* Large icon? */
			if(dev->large_icon_file[0] == NULL)
			{
			    dev->large_icon_file[0] = STRDUP(
				EDV_DEF_DEVICE_ICON_LARGE_FILE
			    );
			}
		    }

		    /* Default fsck program */
		    g_free(dev->command_check);
		    dev->command_check = g_strdup_printf(
			"%s %s",
			EDV_DEF_FSCK_FRONT_PROG,
#if defined(__FreeBSD__)
			fs_ptr->fs_spec
#elif defined(__SOLARIS__)
			vfs_ptr->vfs_special
#else
			mt_ptr->mnt_fsname
#endif
		    );

		    /* Default format program */
		    g_free(dev->command_format);
		    dev->command_format = g_strdup_printf(
			"%s %s",
			EDV_DEF_FORMAT_FRONT_PROG,
#if defined(__FreeBSD__)
			fs_ptr->fs_spec
#elif defined(__SOLARIS__)
			vfs_ptr->vfs_special
#else
			mt_ptr->mnt_fsname
#endif
		    );

		}
	    }
	    else
	    {
		/* Existing device structure exists that matches the 
		 * device path, so update its values
		 */

		/* File system type */
		dev->fs_type = EDVDeviceGetFSTypeFromName(
#if defined(__FreeBSD__)
		    fs_ptr->fs_vfstype
#elif defined(__SOLARIS__)
		    vfs_ptr->vfs_fstype
#else
		    mt_ptr->mnt_type
#endif
		);

		/* Mount path */
#if defined(__FreeBSD__)
		if(fs_ptr->fs_file != NULL)
#elif defined(__SOLARIS__)
		if(vfs_ptr->vfs_mountp != NULL)
#else
		if(mt_ptr->mnt_dir != NULL)
#endif
		{
		    g_free(dev->mount_path);
#if defined(__FreeBSD__)
		    dev->mount_path = STRDUP(fs_ptr->fs_file);
#elif defined(__SOLARIS__)
		    dev->mount_path = STRDUP(vfs_ptr->vfs_mountp);
#else
		    dev->mount_path = STRDUP(mt_ptr->mnt_dir);
#endif
		}

	    }

	    /* Get the next mount entry */
#if defined(__FreeBSD__)
	    fs_ptr = getfsent();
#elif defined(__SOLARIS__)
	    mtback = getmntent(fp, vfs_ptr);
#else
	    mt_ptr = getmntent(fp);
#endif
	}


	/* Close the system's devices list file */
#if defined(__FreeBSD__)
	endfsent();
	FClose(fp);
#elif defined(__SOLARIS__)
	FClose(fp);
	g_free(vfs_ptr);
#else
	endmntent(fp);
#endif

	/* Report final progress */
	if(progress_cb != NULL)
	    progress_cb(
		client_data, file_size, file_size
	    );
}


/*
 *	Updates the mount states of all the Devices in the list with
 *	the system device's mount states.
 */  
void EDVDevicesListUpdateMountStatesFromSystem(
	edv_device_struct **list, const gint total 
)
{
	gint i;
	for(i = 0; i < total; i++)
	    EDVDeviceUpdateMountStateFromSystem(list[i]);
}

/*
 *	Updates the mount states on the Devices List.
 */
void EDVDevicesListUpdateMountStates(
	edv_device_struct **list, const gint total
)
{
	EDVDevicesListUpdateMountStatesFromSystem(list, total);
}


/*
 *	Updates the statistics of each device in Devices List.
 *
 *	If a Device is not marked as mounted then it will be skipped and
 *	its stats will not be 0's.
 *
 *	EDVDevicesListUpdateMountStates() should be called prior to
 *	calling this function.
 */
void EDVDevicesListUpdateStats(
	edv_device_struct **list, const gint total
)
{
	gint i;
	edv_device_struct *dev;
#if defined(__SOLARIS__)
	struct statvfs buf;
#else
	struct statfs buf;
#endif

	/* Iterate through devices */
	for(i = 0; i < total; i++)
	{
	    dev = list[i];
	    if(dev == NULL)
		continue;

	    /* Reset stats */
	    dev->blocks_total = 0l;
	    dev->blocks_available = 0l;
	    dev->blocks_free = 0l;

	    /* Get stats only if mounted */
	    if(EDV_DEVICE_IS_MOUNTED(dev))
	    {
		const gchar *mount_path = dev->mount_path;
#if defined(__SOLARIS__)
		if(!STRISEMPTY(mount_path) ?
		    !statvfs(mount_path, &buf) : FALSE
		)
#else
		if(!STRISEMPTY(mount_path) ?
		    !statfs(mount_path, &buf) : FALSE
		)
#endif
		{
		    /* Check if the block size transfer rate (which is
		     * really just the block size), is larger than or
		     * equal to the base of 1024 bytes per block
		     */
		    const gulong block_size = buf.f_bsize;
		    if(block_size >= 1024l)
		    {
			const gulong block_div = block_size / 1024l;

			dev->blocks_total = buf.f_blocks * block_div;
			dev->blocks_available = buf.f_bavail * block_div;
			dev->blocks_free = buf.f_bfree * block_div;
		    }
		    else if(block_size > 0l)
		    {
			/* Block size is less than 1024 bytes but positive */
			const gulong block_div = 1024l / block_size;

			/* Note, block_div is in range of 1 to 1024 */
			dev->blocks_total = buf.f_blocks / block_div;
			dev->blocks_available = buf.f_bavail / block_div;
			dev->blocks_free = buf.f_bfree / block_div;
		    }
		}
	    }
	}
}


/*
 *	Opens and appends the devices from file to the devices list.
 *
 *	Any existing devices in the list that match (if their
 *	device_path matches) devices from the file will be updated.
 *
 *	Reminder: There is a sister function in lib/edv_devices_list.c
 *	which should perform equvilently to this function.
 */
void EDVDevicesListFileOpen(
	const gchar *filename,
	edv_device_struct ***list, gint *total,
	gint (*progress_cb)(gpointer, const gulong, const gulong),
	gpointer client_data
)
{
	gchar *parm;
	FILE *fp;
	gulong file_size;
	gint dev_num = -1;
	edv_device_struct *dev = NULL;
	struct stat stat_buf;

	if((list == NULL) || (total == NULL) || STRISEMPTY(filename))
	    return;

	/* Open devices file */
	fp = FOpen(filename, "rb");
	if(fp == NULL)
	    return;

	/* Get file statistics */
	if(fstat(fileno(fp), &stat_buf))
	    file_size = 0l;
	else
	    file_size = (gulong)stat_buf.st_size;

	/* Call progress callback */
	if(progress_cb != NULL)
	    progress_cb(
		client_data, 0l, file_size
	    );

	/* Begin file */
	parm = NULL;
	while(TRUE)
	{
	    /* Call progress callback */
	    if(progress_cb != NULL)
	    {
		if(progress_cb(
		    client_data, (gulong)ftell(fp), file_size
		))
		    break;
	    }

	    /* Read next parameter */
	    parm = FSeekNextParm(
		fp, parm,
		EDV_CFG_COMMENT_CHAR,
		EDV_CFG_DELIMINATOR_CHAR
	    );
	    if(parm == NULL)
		break;

	    /* Begin handling by parameter */

	    /* BeginDevice */
	    if(!g_strcasecmp(parm, "BeginDevice"))
	    {
		gchar *device_path = FGetString(fp);

		/* Check if an existing device already exists in
		 * the list that matches the device_path
		 */
		dev = EDVDevicesListMatchDevicePath(
		    *list, *total,
		    &dev_num, device_path
		);
		if(dev == NULL)
		{
		    /* No existing device structure exists that matches
		     * the given device_path, so create a new one
		     */
		    dev_num = *total;
		    *total = dev_num + 1;
		    *list = (edv_device_struct **)g_realloc(
			*list, (*total) * sizeof(edv_device_struct *)
		    );
		    if(*list == NULL)
		    {
			*total = 0;
			dev_num = -1;
			dev = NULL;
		    }
		    else
		    {
			(*list)[dev_num] = dev = EDVDeviceNew(
			    EDV_FS_TYPE_EMPTY, NULL, device_path, NULL
			);
			if(dev == NULL)
			{
			    dev_num = -1;
			}
		    }
		}
	    }

	    /* FSType */
	    else if(!g_strcasecmp(parm, "FSType") ||
		    !g_strcasecmp(parm, "FileSystemType")
	    )
	    {
		gint v[1];
		FGetValuesI(fp, v, 1);
		if(dev != NULL)
		    dev->fs_type = v[0];
	    }
	    /* NoUnmount */
	    else if(!g_strcasecmp(parm, "NoUnmount"))
	    {
		gint v[1];
		FGetValuesI(fp, v, 1);
		if(dev != NULL)
		{
		    if(v[0])
			dev->flags |= EDV_DEVICE_NO_UNMOUNT;
		    else
			dev->flags &= ~EDV_DEVICE_NO_UNMOUNT;
		}
	    }
	    /* ReadOnly */
	    else if(!g_strcasecmp(parm, "ReadOnly"))
	    {
		gint v[1];
		FGetValuesI(fp, v, 1);
		if(dev != NULL)
		{
		    if(v[0])
			dev->flags |= EDV_DEVICE_READ_ONLY;
		    else
			dev->flags &= ~EDV_DEVICE_READ_ONLY;
		}
	    }
	    /* Unlisted */
	    else if(!g_strcasecmp(parm, "Unlisted"))
	    {
		gint v[1];
		FGetValuesI(fp, v, 1);
		if(dev != NULL)
		{
		    if(v[0])
			dev->flags |= EDV_DEVICE_UNLISTED;
		    else
			dev->flags &= ~EDV_DEVICE_UNLISTED;
		}
	    }
	    /* Name */
	    else if(!g_strcasecmp(parm, "Name"))
	    {
		gchar *v = FGetString(fp);
		if(dev != NULL)
		{
		    g_free(dev->name);
		    dev->name = STRDUP(v);
		}
		g_free(v);
	    }
	    /* MountPath */
	    else if(!g_strcasecmp(parm, "MountPath"))
	    {
		gchar *v = FGetString(fp);
		if(dev != NULL)
		{
		    g_free(dev->mount_path);
		    dev->mount_path = STRDUP(v);
		}
		g_free(v);
	    }
	    /* CommandMount */
	    else if(!g_strcasecmp(parm, "CommandMount"))
	    {
		gchar *v = FGetString(fp);
		if(dev != NULL)
		{
		    g_free(dev->command_mount);
		    dev->command_mount = STRDUP(v);
		}
		g_free(v);
	    }
	    /* CommandUnmount */
	    else if(!g_strcasecmp(parm, "CommandUnmount"))
	    {
		gchar *v = FGetString(fp);
		if(dev != NULL)
		{
		    g_free(dev->command_unmount);
		    dev->command_unmount = STRDUP(v);
		}
		g_free(v);
	    }
	    /* CommandEject */
	    else if(!g_strcasecmp(parm, "CommandEject"))
	    {
		gchar *v = FGetString(fp);
		if(dev != NULL)
		{
		    g_free(dev->command_eject);
		    dev->command_eject = STRDUP(v);
		}
		g_free(v);
	    }
	    /* CommandCheck */
	    else if(!g_strcasecmp(parm, "CommandCheck"))
	    {
		gchar *v = FGetString(fp);
		if(dev != NULL)
		{
		    g_free(dev->command_check);
		    dev->command_check = STRDUP(v);
		}
		g_free(v);
	    }
	    /* CommandTools */
	    else if(!g_strcasecmp(parm, "CommandTools"))
	    {
		gchar *v = FGetString(fp);
		if(dev != NULL)
		{
		    g_free(dev->command_tools);
		    dev->command_tools = STRDUP(v);
		}
		g_free(v);
	    }
	    /* CommandFormat */
	    else if(!g_strcasecmp(parm, "CommandFormat"))
	    {
		gchar *v = FGetString(fp);
		if(dev != NULL)
		{
		    g_free(dev->command_format);
		    dev->command_format = STRDUP(v);
		}
		g_free(v);
	    }

	    /* IconSmallStandard */
	    else if(!g_strcasecmp(parm, "IconSmallStandard"))
	    {
		gchar *v = FGetString(fp);
		if(dev != NULL)
		{
		    gchar **t = &dev->small_icon_file[
			EDV_DEVICE_ICON_STATE_STANDARD
		    ];
		    g_free(*t);
		    *t = STRDUP(v);
		}
		g_free(v);
	    }
	    /* IconSmallSelected */
	    else if(!g_strcasecmp(parm, "IconSmallSelected"))
	    {
		gchar *v = FGetString(fp);
		if(dev != NULL)
		{
		    gchar **t = &dev->small_icon_file[
			EDV_DEVICE_ICON_STATE_SELECTED
		    ];
		    g_free(*t);
		    *t = STRDUP(v);
		}
		g_free(v);
	    }
	    /* IconSmallUnmounted */
	    else if(!g_strcasecmp(parm, "IconSmallUnmounted"))
	    {
		gchar *v = FGetString(fp);
		if(dev != NULL)
		{
		    gchar **t = &dev->small_icon_file[
			EDV_DEVICE_ICON_STATE_UNMOUNTED
		    ];
		    g_free(*t);
		    *t = STRDUP(v);
		}
		g_free(v);
	    }

	    /* IconMediumStandard */
	    else if(!g_strcasecmp(parm, "IconMediumStandard"))
	    {
		gchar *v = FGetString(fp);
		if(dev != NULL)
		{
		    gchar **t = &dev->medium_icon_file[
			EDV_DEVICE_ICON_STATE_STANDARD
		    ];
		    g_free(*t);
		    *t = STRDUP(v);
		}
		g_free(v);
	    }
	    /* IconMediumSelected */
	    else if(!g_strcasecmp(parm, "IconMediumSelected"))
	    {
		gchar *v = FGetString(fp);
		if(dev != NULL)
		{
		    gchar **t = &dev->medium_icon_file[
		        EDV_DEVICE_ICON_STATE_SELECTED
		    ];
		    g_free(*t);
		    *t = STRDUP(v);
		}
		g_free(v);
	    }
	    /* IconMediumUnmounted */
	    else if(!g_strcasecmp(parm, "IconMediumUnmounted"))
	    {
		gchar *v = FGetString(fp);
		if(dev != NULL)
		{
		    gchar **t = &dev->medium_icon_file[
			EDV_DEVICE_ICON_STATE_UNMOUNTED
		    ];
		    g_free(*t);
		    *t = STRDUP(v);
		}
		g_free(v);
	    }

	    /* IconLargeStandard */
	    else if(!g_strcasecmp(parm, "IconLargeStandard"))
	    {
		gchar *v = FGetString(fp);
		if(dev != NULL)
		{
		    gchar **t = &dev->large_icon_file[
		        EDV_DEVICE_ICON_STATE_STANDARD
		    ];
		    g_free(*t);
		    *t = STRDUP(v);
		}
		g_free(v);
	    }
	    /* IconLargeSelected */
	    else if(!g_strcasecmp(parm, "IconLargeSelected"))
	    {
		gchar *v = FGetString(fp);
		if(dev != NULL)
		{
		    gchar **t = &dev->large_icon_file[
			EDV_DEVICE_ICON_STATE_SELECTED
		    ];
		    g_free(*t);
		    *t = STRDUP(v);
		}
		g_free(v);
	    }
	    /* IconLargeUnmounted */
	    else if(!g_strcasecmp(parm, "IconLargeUnmounted"))
	    {
		gchar *v = FGetString(fp);
		if(dev != NULL)
		{
		    gchar **t = &dev->large_icon_file[
			EDV_DEVICE_ICON_STATE_UNMOUNTED
		    ];
		    g_free(*t);
		    *t = STRDUP(v);
		}
		g_free(v);
	    }

	    /* LastMountTime */
	    else if(!g_strcasecmp(parm, "LastMountTime"))
	    {
		glong v[1];
		FGetValuesL(fp, v, 1);
		if(dev != NULL)
		    dev->last_mount_time = (gulong)v[0];
	    }
	    /* LastCheckTime */
	    else if(!g_strcasecmp(parm, "LastCheckTime"))
	    {
		glong v[1];
		FGetValuesL(fp, v, 1);
		if(dev != NULL)
		    dev->last_check_time = (gulong)v[0];
	    }

	    /* End of a device block? */
	    else if(!g_strcasecmp(parm, "EndDevice"))
	    {
		FSeekNextLine(fp);

		/* Reset contexts */
		dev_num = -1;
		dev = NULL;
	    }
	    /* All else unsupported parameter or wrong context */
	    else
	    {
		FSeekNextLine(fp);
	    }
	}

	/* Delete parameter */
	g_free(parm);

	/* Close devices file */
	FClose(fp);
}

/*
 *	Saves the devices list to file.
 *
 *	Reminder: There is a sister function in lib/edv_devices_list.c
 *	which should perform equvilently to this function.
 */
void EDVDeviceListFileSave(
	const gchar *filename,
	edv_device_struct **list, const gint total,
	gint (*progress_cb)(gpointer, const gulong, const gulong),
	gpointer client_data
)
{
	gint dev_num;
	const edv_device_struct *dev;
	gchar *parent_path;
	FILE *fp;
	const gchar *path;


	if((list == NULL) || STRISEMPTY(filename))
	    return;

	/* Get parent directory and create it as needed */
	parent_path = g_dirname(filename);
	if(parent_path != NULL)
	{
	    rmkdir(parent_path, S_IRUSR | S_IWUSR | S_IXUSR);
	    g_free(parent_path);
	    parent_path = NULL;
	}

	/* Open devices file for writing */
	fp = FOpen(filename, "wb");
	if(fp == NULL)
	    return;

	/* Call progress callback */
	if(progress_cb != NULL)
	    progress_cb(
		client_data, 0l, (gulong)total
	    );

	/* Iterate through all device structures */
	for(dev_num = 0; dev_num < total; dev_num++)
	{
	    dev = list[dev_num];
	    if(dev == NULL)
		continue;

	    /* Skip devices that are marked internal, meaning they
	     * should not be saved to file
	     */
	    if(EDV_DEVICE_IS_INTERNAL(dev))
		continue;

	    /* Call progress callback */
	    if(progress_cb != NULL)
	    {
		if(progress_cb(
		    client_data, (gulong)(dev_num + 1), (gulong)total
		))
		    break;
	    }


	    /* BeginDevice */
	    fprintf(
		fp,
		"BeginDevice = %s\n",
		dev->device_path
	    );

	    /* FSType */
	    fprintf(
		fp,
		"\tFSType = %i\n",
		dev->fs_type
	    );
	    /* NoUnmount */
	    fprintf(
		fp,
		"\tNoUnmount = %i\n",
		EDV_DEVICE_IS_NO_UNMOUNT(dev)
	    );
	    /* ReadOnly */
	    fprintf(
		fp,
		"\tReadOnly = %i\n",
		EDV_DEVICE_IS_READ_ONLY(dev)
	    );
	    /* Unlisted */
	    fprintf(
		fp,
		"\tUnlisted = %i\n",
		EDV_DEVICE_IS_UNLISTED(dev)
	    );
	    /* Name */
	    if(!STRISEMPTY(dev->name))
		fprintf(
		    fp,
		    "\tName = %s\n",
		    dev->name
		);
	    /* MountPath */
	    if(!STRISEMPTY(dev->mount_path))
		fprintf(
		    fp,
		    "\tMountPath = %s\n",
		    dev->mount_path
		);

	    /* CommandMount */
	    if(!STRISEMPTY(dev->command_mount))
		fprintf(
		    fp,
		    "\tCommandMount = %s\n",
		    dev->command_mount
		);
	    /* CommandUnmount */
	    if(!STRISEMPTY(dev->command_unmount))
		fprintf(
		    fp,
		    "\tCommandUnmount = %s\n",
		    dev->command_unmount
		);
	    /* CommandEject */
	    if(!STRISEMPTY(dev->command_eject))
		fprintf(
		    fp,
		    "\tCommandEject = %s\n",
		    dev->command_eject
		);
	    /* CommandCheck */
	    if(!STRISEMPTY(dev->command_check))
		fprintf(
		    fp,
		    "\tCommandCheck = %s\n",
		    dev->command_check
		);
	    /* CommandTools */
	    if(!STRISEMPTY(dev->command_tools))
		fprintf(
		    fp,
		    "\tCommandTools = %s\n",
		    dev->command_tools
		);
	    /* CommandFormat */
	    if(!STRISEMPTY(dev->command_format))
		fprintf(
		    fp,
		    "\tCommandFormat = %s\n",
		    dev->command_format
		);

	    /* IconSmallStandard */
	    path = dev->small_icon_file[EDV_DEVICE_ICON_STATE_STANDARD];
	    if(!STRISEMPTY(path))
		fprintf(
		    fp,
		    "\tIconSmallStandard = %s\n",
		    path
		);
	    /* IconSmallSelected */
	    path = dev->small_icon_file[EDV_DEVICE_ICON_STATE_SELECTED];
	    if(!STRISEMPTY(path))
		fprintf(
		    fp,
		    "\tIconSmallSelected = %s\n",
		    path
		);
	    /* IconSmallUnmounted */
	    path = dev->small_icon_file[EDV_DEVICE_ICON_STATE_UNMOUNTED];
	    if(!STRISEMPTY(path))
		fprintf(
		    fp,
		    "\tIconSmallUnmounted = %s\n",
		    path
		);

	    /* IconMediumStandard */
	    path = dev->medium_icon_file[EDV_DEVICE_ICON_STATE_STANDARD];
	    if(!STRISEMPTY(path))
		fprintf(
		    fp,
		    "\tIconMediumStandard = %s\n",
		    path
		);
	    /* IconMediumSelected */
	    path = dev->medium_icon_file[EDV_DEVICE_ICON_STATE_SELECTED];
	    if(!STRISEMPTY(path))
		fprintf(
		    fp,
		    "\tIconMediumSelected = %s\n",
		    path
		);
	    /* IconMediumUnmounted */
	    path = dev->medium_icon_file[EDV_DEVICE_ICON_STATE_UNMOUNTED];
	    if(!STRISEMPTY(path))
		fprintf(
		    fp,
		    "\tIconMediumUnmounted = %s\n",
		    path
		);

	    /* IconLargeStandard */
	    path = dev->large_icon_file[EDV_DEVICE_ICON_STATE_STANDARD];
	    if(!STRISEMPTY(path))
		fprintf(
		    fp,
		    "\tIconLargeStandard = %s\n",
		    path
		);
	    /* IconLargeSelected */
	    path = dev->large_icon_file[EDV_DEVICE_ICON_STATE_SELECTED];
	    if(!STRISEMPTY(path))
		fprintf(
		    fp,
		    "\tIconLargeSelected = %s\n",
		    path
		);
	    /* IconLargeUnmounted */
	    path = dev->large_icon_file[EDV_DEVICE_ICON_STATE_UNMOUNTED];
	    if(!STRISEMPTY(path))
		fprintf(
		    fp,
		    "\tIconLargeUnmounted = %s\n",
		    path
		);

	    /* LastMountTime */
	    if(dev->last_mount_time > 0l)
		fprintf(
		    fp,
		    "\tLastMountTime = %ld\n",
		    dev->last_mount_time
		);
	    /* LastCheckTime */
	    if(dev->last_check_time > 0l)
		fprintf(
		    fp,
		    "\tLastCheckTime = %ld\n",
		    dev->last_check_time
		);

	    /* EndDevice */
	    fprintf(
		fp,
		"EndDevice\n"
	    );
	}

	/* Close devices file */
	FClose(fp);
}
