summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libc/termios/ttyname.c184
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;
+}