/* mboxfs - Creates a filesystem based on the contents of a mailbox.
   Copyright (C) 2002, Ludovic Courtès <ludo@chbouib.org>
 
   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License as
   published by the Free Software Foundation; either version 2 of the
   License, or * (at your option) any later version.
 
   This program is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   General Public License for more details.
 
   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA */

#include <hurd.h>
#include <hurd/netfs.h>
#include <error.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <dirent.h>
#include <fcntl.h>
#include <limits.h>
#include "backend.h"
#include "fs.h"

/* Data used by netfs_get_dirents() */
static struct node *curr_dir;
static struct node *curr_node;
static int    curr_entry = 0;

error_t
sfs_init (struct node **root)
{
  error_t err;
  struct node *mynode;

  /* General stuff. */
  err = fs_init ();
  assert (err == 0);

  /* Create root node */
  err = fs_make_node (&netfs_root_node, NULL, NULL, S_IFDIR | 0555);
  if (err)
    return err;

  /* Create a few nodes. */
#if 0
  err = fs_make_node (NULL, netfs_root_node, "slash/node", S_IFREG | 0444);
  if (err)
    return err;
#endif
  err = fs_make_node (NULL, netfs_root_node, "firstNode", S_IFREG | 0444);
  if (err)
    return err;
  err = fs_make_node (NULL, netfs_root_node, "secondNode", S_IFREG | 0444);
  if (err)
    return err;
  err = fs_make_node (&mynode, netfs_root_node, "dir", S_IFDIR | 0555);
  if (err)
    return err;
  err = fs_make_node (NULL, mynode, "oneMoreEntry", S_IFREG | 0444);
  if (err)
    return err;
  err = fs_make_node (NULL, mynode, "yetAnotherEntry", S_IFREG | 0444);
  if (err)
    return err;
  err = fs_make_node (&mynode, mynode, "depth", S_IFDIR | 0555);
  if (err)
    return err;
  err = fs_make_node (NULL, mynode, "paf", S_IFREG | 0444);
  if (err)
    return err;
  err = fs_make_node (NULL, mynode, "shbweeb", S_IFREG | 0444);
  if (err)
    return err;

  return 0;
}

void
sfs_get_argp (struct argp *a)
{
  return;	/* FIXME! */
}

int
sfs_set_cd (struct node *dir)
{
  curr_dir = dir;
  curr_node = dir->nn->entries;
  curr_entry = 0;
  return 0;
}

int
sfs_skip_entries (int n)
{
  assert (n >= 0);

  /* Skip N first DIR entries. */
  curr_node = curr_dir->nn->entries;

  if (n > 2)
  {
    /* Skip more than `,' and `,,' */
    curr_entry = 2;
    while ((curr_entry < n) && (curr_node))
    {
      curr_node = curr_node->next;
      curr_entry++;
    }
  }
  else
    curr_entry = n;
  
  /* Returns non-null if could not skip N entries. */
  return (curr_entry<=n)?0:1;
}

int
sfs_new_dirent (struct dirent** e, const struct node *n, const char* nodename)
{
  size_t namelen;
  char*  name;
  kern_return_t err;

  assert (nodename != NULL);

  /* N==NULL means that we are considering the node on which the
   * translator is set. */
  namelen = (n) ? strlen (nodename) : 2;

  /* Allocate it. */
  err = vm_allocate (mach_task_self (),
      (vm_address_t*)e, sizeof(struct dirent)+namelen, 1);
  assert (err == KERN_SUCCESS);
  
  /* Copy node name */
  name = &(*e)->d_name[0];

  if (n == NULL)
  {
    /* `..' */
    memcpy(name, nodename, 3);
    namelen = 2;
    (*e)->d_type = DT_DIR;
  }
  else
  {
    memcpy (name, nodename, namelen+1);
    /* Set the type corresponding to n->nn_stat.st_mode */
    if (n->nn_stat.st_mode & S_IFREG)
      (*e)->d_type = DT_REG;
    else if (n->nn_stat.st_mode & S_IFDIR)
      (*e)->d_type = DT_DIR;
    else if (n->nn_stat.st_mode & S_IFLNK)
      (*e)->d_type = DT_LNK;
    else
      (*e)->d_type = DT_UNKNOWN;
  }

  assert (namelen != 0);

  /* if FILENO==0 then the node won't appear. */
  (*e)->d_fileno = 2;
  (*e)->d_namlen = namelen;
  (*e)->d_reclen = sizeof(struct dirent) + namelen;

  return 0;
}

int
sfs_get_next_entry (struct dirent **entry)
{
  switch (curr_entry++)
  {
    case 0:
      sfs_new_dirent (entry, curr_dir, ".");
      break;
    case 1:
      sfs_new_dirent (entry, curr_dir->nn->dir, "..");
      break;
    default:
      if (!curr_node)
	return 1;	/* no more entries */
      else
      {
	sfs_new_dirent (entry, curr_node, curr_node->nn->name);
	curr_node = curr_node->next;
      }
      break;
  }

  return 0;
}

error_t
sfs_lookup_node (struct node** node, struct node* dir, const char* name)
{
  struct node *n;
 
  /* Look for NAME in DIR entries. */
  for (n = dir->nn->entries;
       n && strcmp(name, n->nn->name);
       n = n->next);

  if (n)
    *node = n;
  else
    return ENOENT;

  return 0;
}

error_t
sfs_read_node (struct node *np, off_t offset, size_t *len, void* data)
{
  size_t l;
  char strnod[] = "This is a node.\n";
  char strdir[] = "This is a directory.\n";
  char* str;

  str = (np->nn_stat.st_mode & S_IFDIR) ? strdir : strnod;
  l = strlen(str);
  if (offset < l)
  {
    *len = l - offset;
    memcpy (data, &str[offset], l - offset + 1);
  }
  else
  {
    *len = 0;
    return 0;	/* XXX */
  }

  return 0;
}

/* Defines the debug backend. */
struct fs_backend sfs_backend =
{
  sfs_init,
  sfs_get_argp,
  NULL,
  NULL,
  sfs_set_cd,
  sfs_skip_entries,
  sfs_get_next_entry,
  sfs_lookup_node,
  sfs_read_node,
};
