123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118 |
- #include <string.h>
- #include <errno.h>
- #include <assert.h>
- #include <unistd.h>
- #include <dirent.h>
- #include <sys/stat.h>
- /* 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
- * 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 */
- /* 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.)
- *
- * If you change this, also change _SC_TTY_NAME_MAX in libc/unistd/sysconf.c
- */
- #define TTYNAME_BUFLEN 32
- char *ttyname(int fd)
- {
- static char name[TTYNAME_BUFLEN];
- return ttyname_r(fd, name, TTYNAME_BUFLEN) ? NULL : name;
- }
- 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/pty/\0" /* and /dev/pty 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)
- {
- 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;
- }
|