#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>

#include <unixwalkdir.h>

void *uade_walk_directories(const char *dirname,
			    void * (*fn)(const char *file,
					 enum uade_wtype wtype, void *opaque),
			    void *opaque)
{
  char *dename;
  DIR *dir;
  size_t namelen;
  struct dirent *de;
  void *ret = NULL;
  struct stat st;
  enum uade_wtype wtype;

  namelen = strlen(dirname) + 256 + 2;
  if ((dename = malloc(namelen)) == NULL)
    return NULL;

  if ((dir = opendir(dirname)) == NULL)
    return NULL;

  while ((de = readdir(dir)) != NULL) {

    if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)
      continue;

    if (snprintf(dename, namelen, "%s/%s", dirname, de->d_name) >= namelen) {
      fprintf(stderr, "interesting: too long a filename\n");
      continue;
    }

    if (lstat(dename, &st))
      continue;

    if (S_ISREG(st.st_mode))
      wtype = UADE_WALK_REGULAR_FILE;
    else if (S_ISDIR(st.st_mode))
      wtype = UADE_WALK_DIRECTORY;
    else if (S_ISLNK(st.st_mode))
      wtype = UADE_WALK_SYMLINK;
    else
      wtype = UADE_WALK_SPECIAL;

    if ((ret = fn(dename, wtype, opaque)) != NULL)
      break;

    if (wtype == UADE_WALK_DIRECTORY) {
      if ((ret = uade_walk_directories(dename, fn, opaque)) != NULL)
	break;
    }
  }

  closedir(dir);
  free(dename);

  return ret;
}
