/*
 * Copyright (c) 2004 Chris Pressey <cpressey@catseye.mine.nu>
 *
 * Copyright (c) 2004 The DragonFly Project.
 * All rights reserved.
 *
 * This code is derived from software contributed to The DragonFly Project
 * by Chris Pressey.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. Neither the name of The DragonFly Project nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific, prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
 * COPYRIGHT HOLDERS, CONTRIBUTORS OR VOICES IN THE AUTHOR'S HEAD
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY
 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

/* 
 * fs.c - filesystem-related bindings for dfuibe_lua
 * $Id: fs.c,v 1.13 2005/01/18 22:13:59 cpressey Exp $
 */

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <sys/ucred.h>
#include <sys/mount.h>

#include <dirent.h>
#include <err.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

#include <aura/fspred.h>
#include <aura/mem.h>
#include <installer/functions.h>

#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"

#include "dfuibe_lua.h"

/*** "STATIC" METHODS ***/

int
lua_fs_dir(lua_State *L)
{
	DIR *d;
	const char *dir_name;
	struct dirent *de;
	int idx, n = 1;

	dir_name = luaL_checkstring(L, 1);

	if ((d = opendir(dir_name)) == NULL) {
		lua_pushnil(L);
		return(1);
	}

        lua_newtable(L);
        idx = lua_gettop(L);

	while ((de = readdir(d)) != NULL) {
		if (strcmp(de->d_name, ".") == 0 ||
		    strcmp(de->d_name, "..") == 0)
			continue;
		/* Insert de->d_name into the list. */
		lua_pushlstring(L, de->d_name, strlen(de->d_name));
		lua_rawseti(L, idx, n++);
	}
	closedir(d);

	return(1);
}

int
lua_fs_is_dir(lua_State *L)
{
	char *filename;

	filename = (char *)luaL_checkstring(L, 1);
	lua_pushboolean(L, is_dir(filename));

	return(1);
}

int
lua_fs_is_file(lua_State *L)
{
	char *filename;

	filename = (char *)luaL_checkstring(L, 1);
	lua_pushboolean(L, is_file(filename));

	return(1);
}

int
lua_fs_is_program(lua_State *L)
{
	char *filename;

	filename = (char *)luaL_checkstring(L, 1);
	lua_pushboolean(L, is_program(filename));

	return(1);
}

int
lua_fs_is_device(lua_State *L)
{
	char *filename;

	filename = (char *)luaL_checkstring(L, 1);
	lua_pushboolean(L, is_device(filename));

	return(1);
}

int
lua_fs_is_named_pipe(lua_State *L)
{
	char *filename;

	filename = (char *)luaL_checkstring(L, 1);
	lua_pushboolean(L, is_named_pipe(filename));

	return(1);
}

int
lua_fs_size_of(lua_State *L)
{
	const char *filename;
	struct stat sb;

	filename = luaL_checkstring(L, 1);

	if (stat(filename, &sb) == 0)
		lua_pushnumber(L, sb.st_size);
	else
		lua_pushnil(L);

	return(1);
}

int
lua_fs_basename(lua_State *L)
{
	char *path, *bname;

	path = (char *)luaL_checkstring(L, 1);
	bname = basename(path);
	lua_pushlstring(L, bname, strlen(bname));

	return(1);
}

int
lua_fs_dirname(lua_State *L)
{
	char *path, *dname;

	path = (char *)luaL_checkstring(L, 1);
	dname = dirname(path);
	lua_pushlstring(L, dname, strlen(dname));

	return(1);
}

/*
 * This returns the file object first, then the filename
 * (the filled-out template.)
 */
int
lua_fs_mkstemp(lua_State *L)
{
	char *tpl;
	int fd;
	FILE *f;

	tpl = aura_strdup((char *)luaL_checkstring(L, 1));
	fd = mkstemp(tpl);
	f = fdopen(fd, "w+");
	if (f == NULL) {
		lua_pushnil(L);
		AURA_FREE(tpl, "temp filename template");
		return(1);
	}

	lua_pushfileptr(L, f);

	lua_pushstring(L, tpl);
	AURA_FREE(tpl, "temp filename template");

	return(2);
}

/*
 * Return an array of filesystem descriptors.  Each contains:
 *   mountpoint - name of the directory on which it is mounted
 *   type       - type of filesystem (ufs, procfs, etc)
 * and possibly more in the future.
 */
int
lua_fs_enumerate(lua_State *L)
{
	struct statfs *fs_array;
	int i, count, local_idx, master_idx;
	long bufsize;

	count = getfsstat(NULL, 0, MNT_WAIT);
	bufsize = count * sizeof(struct statfs) + 4;
	if ((fs_array = malloc(bufsize)) == NULL) {
		lua_pushnil(L);
		return(1);
	}

	lua_newtable(L);
	master_idx = lua_gettop(L);

	count = getfsstat(fs_array, bufsize, MNT_WAIT);
	for (i = 0; i < count; i++) {
		lua_newtable(L);
		local_idx = lua_gettop(L);

		lua_pushstring(L, "mountpoint");
		lua_pushstring(L, fs_array[i].f_mntonname);
		lua_settable(L, local_idx);

		lua_pushstring(L, "device");
		lua_pushstring(L, fs_array[i].f_mntfromname);
		lua_settable(L, local_idx);

		lua_pushstring(L, "type");
		lua_pushstring(L, fs_array[i].f_fstypename);
		lua_settable(L, local_idx);

		lua_pushstring(L, "block_size");
		lua_pushnumber(L, fs_array[i].f_bsize);
		lua_settable(L, local_idx);

		lua_pushstring(L, "blocks_total");
		lua_pushnumber(L, fs_array[i].f_blocks);
		lua_settable(L, local_idx);

		lua_pushstring(L, "blocks_free");
		lua_pushnumber(L, fs_array[i].f_bfree);
		lua_settable(L, local_idx);

		lua_pushstring(L, "blocks_avail");
		lua_pushnumber(L, fs_array[i].f_bavail);
		lua_settable(L, local_idx);

		lua_rawseti(L, master_idx, i + 1);
	}

	free(fs_array);

	return(1);
}

/**** Binding Tables ****/

const luaL_reg fs_methods[] = {
	{"dir",			lua_fs_dir },

	{"is_dir",		lua_fs_is_dir },
	{"is_file",		lua_fs_is_file },
	{"is_program",		lua_fs_is_program },
	{"is_device",		lua_fs_is_device },
	{"is_named_pipe",	lua_fs_is_named_pipe },

	{"size_of",		lua_fs_size_of },

	{"basename",		lua_fs_basename },
	{"dirname",		lua_fs_dirname },

	{"mkstemp",		lua_fs_mkstemp },

	{"enumerate",		lua_fs_enumerate },

	{0, 0}
};

/*** REGISTER ***/

LUA_API int
lua_fs_register(lua_State *L)
{
	luaL_openlib(L, "FileSystem", fs_methods, 0);
	return(1);
}
