#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>
#include <dirent.h>

static int __check_dir_for_tty_match(char * dirname, struct stat *st, char *buf, size_t buflen)
{
    DIR *fp;
    int len;
    struct stat dst;
    struct dirent *d;

    fp = opendir(dirname);
    if (fp == NULL)
	return errno;
    strncpy(buf, dirname, buflen);
    strncat(buf, "/", buflen);
    len = strlen(dirname) + 1;

    while ((d = readdir(fp)) != 0) {
	strncpy(buf+len, d->d_name, buflen-len);
	buf[buflen-1]='\0';
#if 0
	/* Stupid filesystems like cramfs fail to guarantee that
	 * st_ino and st_dev uniquely identify a file, contrary to
	 * SuSv3, so we cannot be quite so precise as to require an
	 * exact match.  Settle for something less...  Grumble... */
	if (lstat(buf, &dst) == 0 &&
		st->st_dev == dst.st_dev && st->st_ino == dst.st_ino)
#else
	if (lstat(buf, &dst) == 0 &&
		S_ISCHR(dst.st_mode) && st->st_rdev == dst.st_rdev)
#endif
	{
	    closedir(fp);
	    return 0;
	}
    }
    closedir(fp);
    return ENOTTY;
}

/* This is a fairly slow approach.  We do a linear search through some
 * directories looking for a match.  Yes this is lame.  But it should
 * work, should be small, and will return names that match what is on
 * disk.  Another approach we could use would be to use the info in
 * /proc/self/fd, but that is even more lame since it requires /proc */

char *ttyname(int fd)
{
    static char name[NAME_MAX];
    ttyname_r(fd, name, NAME_MAX);
    return(name);
}

int ttyname_r(int fd, char *buf, size_t buflen)
{
    int noerr;
    struct stat st;

    noerr = errno;
    if (buf==NULL) {
	noerr = EINVAL;
	goto cool_found_it;
    }
    /* Make sure we have enough space to return "/dev/pts/0" */
    if (buflen < 10) {
	noerr = ERANGE;
	goto cool_found_it;
    }
    if (!isatty (fd)) {
	noerr = ENOTTY;
	goto cool_found_it;
    }
    if (fstat(fd, &st) < 0)
	return errno;
    if (!isatty(fd)) {
	noerr = ENOTTY;
	goto cool_found_it;
    }

    /* Lets try /dev/vc first (be devfs compatible) */
    if ( (noerr=__check_dir_for_tty_match("/dev/vc", &st, buf, buflen)) == 0) 
	goto cool_found_it;

    /* Lets try /dev/tts next (be devfs compatible) */
    if ( (noerr=__check_dir_for_tty_match("/dev/tts", &st, buf, buflen)) == 0) 
	goto cool_found_it;

    /* Lets try /dev/pts next */
    if ( (noerr=__check_dir_for_tty_match("/dev/pts", &st, buf, buflen)) == 0) 
	goto cool_found_it;

    /* Lets try walking through /dev last */
    if ( (noerr=__check_dir_for_tty_match("/dev", &st, buf, buflen)) == 0) 
	goto cool_found_it;

cool_found_it:
    __set_errno(noerr);
    return noerr;
}