#define _GNU_SOURCE
#include <errno.h>
#include <string.h>
#include <dlfcn.h>

#include <hesiod.h>
#include <stdlib.h>
#include <sys/types.h>
#include <pwd.h>

static char *hes_gethomedir(const char *);

struct passwd *
getpwnam(const char *name)
{
	static struct passwd *(*real_getpwnam)(const char *) = NULL;
	static struct passwd pwbuf = {
		.pw_name = NULL,
		.pw_passwd = "x",
		.pw_uid = (uid_t)~0UL,
		.pw_gid = (gid_t)~0UL,
		.pw_gecos = "Hesiod filsys pointer,,,",
		.pw_dir = NULL,
		.pw_shell = "/bin/false"
	};
	struct passwd *result = NULL;
	char *filsys;

	//printf("called getpwnam(%s)\n", name);

	if (!real_getpwnam) {
		real_getpwnam = dlsym(RTLD_NEXT, "getpwnam");
	}
	if (!real_getpwnam) {
		errno = ELIBACC;
		return NULL;
	}

	if (name[0] != '~') {
		result = real_getpwnam(name);
	} else {
		name++;
	}
	if (!result) {
		filsys = hes_gethomedir(name);
		if (!filsys) {
			errno = ENOENT;
			return NULL;
		}
		free(pwbuf.pw_name);
		pwbuf.pw_name = strdup(name);
		free(pwbuf.pw_dir);
		pwbuf.pw_dir = filsys;
		result = &pwbuf;
	}
	return result;
}

static char *
hes_gethomedir (us)
     const char *us;
{
  char **res, **res1, *cp, *rp;
  int which;

  res = hes_resolve(us, "filsys");
  rp = 0;
  if (res != 0) {
    //extern char *strtok();
    if ((*res) != 0) {
      int i, lowest, new;

      /* Use the first filesys if there's an ordered list */
      lowest = -1;
      which = 0;
      if (res[1]) {
        for (i = 0; res[i]; i++) {
          cp = strrchr(res[i], ' ');
          if (!cp)
            return NULL;
          new = atoi(cp + 1);
          if (lowest == -1 || new < lowest) {
            lowest = new;
            which = i;
          }
        }
      }
      /*
       * Look at the first token to determine how to interpret
       * the rest of it.
       * Yes, strtok is evil (it's not thread-safe), but it's also
       * easy to use.
       */
      cp = strtok(res[which], " ");
      if (strcmp(cp, "AFS") == 0) {
        /* next token is AFS pathname.. */
        cp = strtok(NULL, " ");
        if (cp != NULL)
          rp = strdup(cp);
      } else if (strcmp(cp, "NFS") == 0) {
        cp = NULL;
        if ((strtok(NULL, " ")) && /* skip remote pathname */
            (strtok(NULL, " ")) && /* skip host */
            (strtok(NULL, " ")) && /* skip mode */
            (cp = strtok(NULL, " "))) {
          rp = strdup(cp);
        }
      }
    }
    for (res1 = res; *res1; res1++)
      free(*res1);
    return rp;
  }
  return NULL;
}
