diff options
-rw-r--r-- | libc/termios/ttyname.c | 184 |
1 files changed, 98 insertions, 86 deletions
diff --git a/libc/termios/ttyname.c b/libc/termios/ttyname.c index 569cfd94e..fcd10adf0 100644 --- a/libc/termios/ttyname.c +++ b/libc/termios/ttyname.c @@ -1,45 +1,15 @@ +#include <string.h> #include <errno.h> +#include <assert.h> #include <unistd.h> -#include <string.h> -#include <sys/stat.h> #include <dirent.h> +#include <sys/stat.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; -} +/* Jan 1, 2004 Manuel Novoa III + * + * Kept the same approach, but rewrote the code for the most part. + * Fixed some minor issues plus (as I recall) one SUSv3 errno case. + */ /* 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 @@ -47,57 +17,99 @@ static int __check_dir_for_tty_match(char * dirname, struct stat *st, char *buf, * 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 */ +/* SUSv3 mandates TTY_NAME_MAX as 9. This is obviously insufficient. + * However, there is no need to waste space and support non-standard + * tty names either. So we compromise and use the following buffer + * length. (Erik and Manuel agreed that 32 was more than reasonable.) + */ +#define TTYNAME_BUFLEN 32 + char *ttyname(int fd) { - static char name[NAME_MAX]; - ttyname_r(fd, name, NAME_MAX); - return(name); + static char name[TTYNAME_BUFLEN]; + + return ttyname_r(fd, name, TTYNAME_BUFLEN) ? NULL : name; } -int ttyname_r(int fd, char *buf, size_t buflen) +static const char dirlist[] = +/* 12345670123 */ +"\010/dev/vc/\0" /* Try /dev/vc first (be devfs compatible) */ +"\011/dev/tts/\0" /* and /dev/tts next (be devfs compatible) */ +"\011/dev/pts/\0" /* and try /dev/pts next */ +"\005/dev/\0"; /* and try walking through /dev last */ + +int ttyname_r(int fd, char *ubuf, size_t ubuflen) { - 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; -} + struct dirent *d; + struct stat st; + struct stat dst; + const char *p; + char *s; + DIR *fp; + int rv; + int len; + char buf[TTYNAME_BUFLEN]; + + if (fstat(fd, &st) < 0) { + return errno; + } + + rv = ENOTTY; /* Set up the default return value. */ + if (!isatty(fd)) { + goto DONE; + } + + for (p = dirlist ; *p ; p += 1 + p[-1]) { + len = *p++; + + assert(len + 2 <= TTYNAME_BUFLEN); /* dirname + 1 char + nul */ + + strcpy(buf, p); + s = buf + len; + len = (TTYNAME_BUFLEN-2) - len; /* Available non-nul space. */ + + if (!(fp = opendir(p))) { + continue; + } + + while ((d = readdir(fp)) != NULL) { + /* This should never trigger for standard names, but we + * check it to be safe. */ + if (strlen(d->d_name) > len) { /* Too big? */ + continue; + } + + strcpy(s, d->d_name); + + if ((lstat(buf, &dst) == 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... */ + && (st.st_dev == dst.st_dev) && (st.st_ino == dst.st_ino) +#else + && S_ISCHR(dst.st_mode) && (st.st_rdev == dst.st_rdev) +#endif + ) { /* Found it! */ + closedir(fp); + + /* We treat NULL buf as ERANGE rather than EINVAL. */ + rv = ERANGE; + if (ubuf && (strlen(buf) <= ubuflen)) { + strcpy(ubuf, buf); + rv = 0; + } + goto DONE; + } + } + + closedir(fp); + } + + DONE: + __set_errno(rv); + + return rv; +} |