
/*
 * inode.c:
 *
 * Copyright (c) 2000 James McKenzie <james@fishsoup.dhs.org>,
 * All rights reserved.
 *
 */

static char rcsid[] =
    "$Id: inode.c,v 1.16 2002/06/23 17:16:39 root Exp root $";

/*
 * $Log: inode.c,v $
 * Revision 1.16  2002/06/23 17:16:39  root
 * #
 *
 * Revision 1.15  2001/10/12 13:51:11  root
 * #
 *
 * Revision 1.14  2001/10/04 18:21:16  root
 * #
 *
 * Revision 1.13  2001/01/30 06:16:18  root
 * #
 *
 * Revision 1.12  2000/12/01 04:42:10  root
 * #
 *
 * Revision 1.11  2000/11/23 23:45:19  root
 * #
 *
 * Revision 1.10  2000/10/07 18:30:03  root
 * #
 *
 * Revision 1.9  2000/09/24 01:58:21  root
 * #
 *
 * Revision 1.8  2000/09/24 01:48:12  root
 * #
 *
 * Revision 1.7  2000/09/24 01:44:47  root
 * #
 *
 * Revision 1.6  2000/09/24 01:12:43  root
 * #
 *
 * Revision 1.5  2000/09/19 05:06:22  root
 * #
 *
 * Revision 1.4  2000/09/19 03:53:27  root
 * #
 *
 */

#include "project.h"
#include "signal.h"

static char collid[] = "collection.id";

static DB *inodedb = NULL;

BEGIN_OBJECT(Devtlm)
Eweid e;
dev_t d;
char *p;
END_OBJECT(Devtlm)

static void devtlm_destructor(Devtlm d)
{
  if (!d)
    return;

  if (d->p)
    free(d->p);
  if (d->e)
    Obj_Delete(d->e);
  free(d);

}

CREATOR(Devtlm) = (Creator(Devtlm)) Obj_DefaultCreator;
DESTRUCTOR(Devtlm) = devtlm_destructor;


static Devtlm_list devlist = NULL;

/*Initialize the inode -> name database*/
static void inode_init(void)
{
  int ret;

  if (inodedb)
    return;

  ret = db_create(&inodedb, NULL, 0);
  if (ret) {
    fprintf(stderr, "db_create: %s\n", db_strerror(ret));
    inodedb = NULL;
    return;
  }

  ret = inodedb->open(inodedb, NULL, NULL, DB_BTREE, DB_CREATE, 0x600);

  if (ret) {
    inodedb->err(inodedb, ret, "inodedb");
    inodedb->close(inodedb, 0);
    inodedb = NULL;
  }

}

/* ensure inodedb is initialized */
#define DOINIT  { if (!inodedb) inode_init(); };
#define LISTINIT { if (!devlist) devlist=CREATE_LIST(Devtlm); };

static Eweid get_devid_worker(const char *dir, dev_t dev, char **path)
{
  char *buf;
  int fd;
  struct stat s;

  buf = malloc(strlen(dir) + sizeof(collid));

  strcpy(buf, dir);
  strcat(buf, collid);

  fd = open(buf, O_RDONLY);
  free(buf);

  *path = strdup(dir);

  if (fd >= 0) {
    Eweid ret = CREATE_OBJ(Eweid);

    if (read(fd, &ret->e, sizeof(ret->e)) != sizeof(ret->e)) {
      Obj_Delete(ret);
      close(fd);
      return NULL;
    }
    close(fd);

    return ret;
  }

  if (stat(dir, &s) < 0) {
    return NULL;
  }

  if (s.st_dev != dev) {
    return NULL;
  }

  if (strlen(dir) == 1) {
    return NULL;
  }
  {
    char *next = hack_off(dir);
    return get_devid_worker(next, dev, path);
    free(next);
  }
}


/* Fill in the first part of the eweid, dev is from struct stat   */
/* it needs a path also if it hasn't seend dev before it needs to */
/* drop up paths to find the collection id file.                  */

static Eweid devt_to_eweid(dev_t dev, const char *path)
{
  Devtlm l;
  char *p = NULL;

  LISTINIT;

  l = devlist->head;

  while (l) {
    if (l->d == dev) {
      if (l != devlist->head) {
        Obj_Remove(devlist, l);
        Obj_Insert(devlist, l, ObjInsertHead);
      }
      return l->e;
    }

    l = l->next;
  }

  l = CREATE_OBJ(Devtlm);
  l->d = dev;
  l->e = get_devid_worker(path, dev, &p);
  l->p = p;


  if (!l->e) {
    Obj_Delete(l);
    return NULL;
  }

  Obj_Insert(devlist, l, ObjInsertHead);


  return l->e;
}

#if 0
static char *eweid_to_base(Eweid ie)
{
  Eweid e = eweid_dev(ie);

  Devtlm l;

  LISTINIT;

  l = devlist->head;

  while (l) {
    if (!EWEID_CMP(l->e, e)) {
      if (l != devlist->head) {
        Obj_Remove(devlist, l);
        Obj_Insert(devlist, l, ObjInsertHead);
      }
      Obj_Delete(e);
      return l->p;
    }
    l = l->next;
  }

  Obj_Delete(e);
  return NULL;

}
#endif

/* Add an inode to the db*/

INTERNAL void inode_add(Eweid e, const char *s)
{
  int ret;
  DBT key = { 0 };
  DBT data = { 0 };

  key.data = e->e;
  key.size = sizeof(e->e);
  data.data = (char *) s;
  data.size = strlen(s);

  DOINIT;

#if 0
  {
    char *a = eweidtoa(e);
    printf("PUT: %s %s \n", s, a);
    free(a);

    if (s[0] != '/')
      kill(getpid(), SIGSEGV);
  }
#endif


  ret = inodedb->put(inodedb, NULL, &key, &data, 0);

  if (ret) {
    inodedb->err(inodedb, ret, "DB->put");
  }
}

#if 0
void inode_add_derel(Eweid e, const char *s, char *base)
{
  char *f;

  DOINIT;

  f = relativise(s, base);

  inode_add(e, f);

  free(f);
}

#endif

static int ftw_worker_1(const char *file, const struct stat *s, int flag)
{
  Eweid e;
  const char *p;

  if (flag == FTW_F) {
    e = devt_to_eweid(s->st_dev, file);

    if (!e)
      return 1;

    e = eweid_combine(e, s->st_ino);

    inode_add(e, file);
    work_add(e);

    Obj_Delete(e);
  }

  return 0;
}

static int ftw_worker_2(const char *file, const struct stat *s, int flag)
{
  const char *p;
  Eweid e;

  if (flag == FTW_F) {

    if (strlen(file) >= 4) {
      p = file + strlen(file) - 4;
      if (!strcmp(p, ".mtd")) {
        e = filename_to_eweid(file);
        text_add(e, " ");
        work_remove(e);
        Obj_Delete(e);

        if (strlen(file) >= 14) {
          p = file + strlen(file) - 14;
          if (strcmp(p, "collection.mtd"))
            parse_mtd(file);
        } else {
          parse_mtd(file);
        }
      }
    }

    if (strlen(file) >= 5) {
      p = file + strlen(file) - 5;
      if (!strcmp(p, ".html")) {
        e = filename_to_eweid(file);
        text_remove(e);
        work_remove(e);
        Obj_Delete(e);
      }
    }


    if (strlen(file) >= 4) {
      p = file + strlen(file) - 4;
      if ((!strcmp(p, ".wav")) || (!strcmp(p, ".jpg"))) {
        e = filename_to_eweid(file);
        text_remove(e);
        work_remove(e);
        Obj_Delete(e);
      }
    }


  }

  return 0;
}





EXTERNAL void files_ftw(char *path)
{

  path = directory_of(path);

/* Remove trailing / */
  path[strlen(path) - 1] = 0;

  ftw(path, ftw_worker_1, 32);
  ftw(path, ftw_worker_2, 32);

  free(path);

}


/*Get the filename from a eweid, return addr is malloc'd so needs to be*/
/*freed*/

INTERNAL char *eweid_to_filename(Eweid e)
{
  DBT key = { 0 };
  DBT data = { 0 };
  int ret;
  char *r;


  key.data = e->e;
  key.size = sizeof(e->e);
  DOINIT;


  ret = inodedb->get(inodedb, NULL, &key, &data, 0);

  if (ret == DB_NOTFOUND) {
    return NULL;
  }

  if (ret) {
    inodedb->err(inodedb, ret, "DB->get");
    return NULL;
  }

  r = (char *) malloc(data.size + 1);

  bcopy(data.data, r, data.size);
  r[data.size] = 0;

#if 0
  {
    char *a = eweidtoa(e);
    printf("GET: %s %s \n", a, r);
    free(a);
  }
#endif

  return r;
}

/*Get the eweid for a filename, also add the eweid to the reverse db*/
EXTERNAL Eweid filename_to_eweid(const char *file)
{
  int ret;
  Eweid e;
  struct stat s;

  if (stat(file, &s))
    return NULL;

  e = devt_to_eweid(s.st_dev, file);

  if (!e)
    return e;

  e = eweid_combine(e, s.st_ino);

  inode_add(e, file);

  return e;
}

EXTERNAL int files_num(void)
{
  DBC *c;
  int ret;
  int n=0;
  DBT data, key;

  DOINIT;

  bzero(&data, sizeof(data));
  bzero(&key, sizeof(key));

  ret = inodedb->cursor(inodedb, NULL, &c, 0);
  if (ret) {
    inodedb->err(inodedb, ret, "cursor");
    return 0;
  }

  while (1) {
    ret = c->c_get(c, &key, &data, DB_NEXT);

    if (ret == DB_NOTFOUND)
      break;

    if (ret) {
      inodedb->err(inodedb, ret, "soupr");
      c->c_close(c);
      return 0;
    }

    n++;
  }

  c->c_close(c);

  return n;
}

EXTERNAL void files_dumpdb(FILE * out, char *wrt)
{
  DBC *c;
  int ret;
  DBT data, key;
  Eweid e;
  char *f, *g, *t, *u, *w, *v;
  char *path = directory_of(wrt);
  int iw;

  DOINIT;

  bzero(&data, sizeof(data));
  bzero(&key, sizeof(key));

  ret = inodedb->cursor(inodedb, NULL, &c, 0);
  if (ret) {
    inodedb->err(inodedb, ret, "cursor");
    return;
  }

  e = CREATE_OBJ(Eweid);

  while (1) {
    ret = c->c_get(c, &key, &data, DB_NEXT);

    if (ret == DB_NOTFOUND)
      break;

    if (ret) {
      inodedb->err(inodedb, ret, "soupr");
      c->c_close(c);
      Obj_Delete(e);
      return;
    }

    bcopy(key.data, &e->e, sizeof(e->e));

    f = malloc(data.size + 1);
    bcopy(data.data, f, data.size);
    f[data.size] = 0;

    t = eweid_to_text_nocheat(e);
    if (!t)
      t = strdup("");
    u = eweid_to_uri(e);

    if (u) {
      w = relativise(u, path);
    } else {
      u = strdup("");
      w = strdup("");
    }

    iw = files_is_work(e);

    g = relativise(f, path);
    v = eweidtoa(e);
    fprintf(out, "EWEID %s \"%s\" \"%s\" \"%s\" \"%d\";\n", v, g, t, w,
            iw);

    free(v);
    free(g);
    free(w);
    free(u);
    free(t);
    free(f);



  }

  c->c_close(c);
  Obj_Delete(e);

  free(path);
}
