美文网首页LinuxLinux学习之路
APUE读书笔记-18终端输入输出(10)

APUE读书笔记-18终端输入输出(10)

作者: QuietHeart | 来源:发表于2020-09-03 17:35 被阅读0次

    函数ttyname的例子

    如下面代码所示的,其实ttyname函数很长,因为我们需要搜索所有的设备节点,来查找一个匹配的。

    POSIX.1的ttyname函数的实现

    #include    <sys/stat.h>
    #include    <dirent.h>
    #include    <limits.h>
    #include    <string.h>
    #include    <termios.h>
    #include    <unistd.h>
    #include    <stdlib.h>
    
    struct devdir {
        struct devdir    *d_next;
        char             *d_name;
    };
    
    static struct devdir    *head;
    static struct devdir    *tail;
    static char             pathname[_POSIX_PATH_MAX + 1];
    
    static void add(char *dirname)
    {
        struct devdir    *ddp;
        int              len;
    
        len = strlen(dirname);
    
        /*
        * Skip ., .., and /dev/fd.
        */
        if ((dirname[len-1] == '.') && (dirname[len-2] == '/' ||
          (dirname[len-2] == '.' && dirname[len-3] == '/')))
            return;
        if (strcmp(dirname, "/dev/fd") == 0)
            return;
        ddp = malloc(sizeof(struct devdir));
        if (ddp == NULL)
            return;
    
        ddp->d_name = strdup(dirname);
        if (ddp->d_name == NULL) {
            free(ddp);
            return;
        }
        ddp->d_next = NULL;
        if (tail == NULL) {
            head = ddp;
            tail = ddp;
        } else {
            tail->d_next = ddp;
            tail = ddp;
        }
    }
    
    static void cleanup(void)
    {
        struct devdir *ddp, *nddp;
    
        ddp = head;
        while (ddp != NULL) {
            nddp = ddp->d_next;
            free(ddp->d_name);
            free(ddp);
            ddp = nddp;
        }
        head = NULL;
        tail = NULL;
    }
    
    static char * searchdir(char *dirname, struct stat *fdstatp)
    {
        struct stat     devstat;
        DIR             *dp;
        int             devlen;
        struct dirent   *dirp;
    
        strcpy(pathname, dirname);
        if ((dp = opendir(dirname)) == NULL)
            return(NULL);
        strcat(pathname, "/");
        devlen = strlen(pathname);
        while ((dirp = readdir(dp)) != NULL) {
            strncpy(pathname + devlen, dirp->d_name,
              _POSIX_PATH_MAX - devlen);
            /*
             * Skip aliases.
             */
            if (strcmp(pathname, "/dev/stdin") == 0 ||
              strcmp(pathname, "/dev/stdout") == 0 ||
              strcmp(pathname, "/dev/stderr") == 0)
                continue;
            if (stat(pathname, &devstat) < 0)
                continue;
            if (S_ISDIR(devstat.st_mode)) {
                add(pathname);
                continue;
            }
            if (devstat.st_ino == fdstatp->st_ino &&
              devstat.st_dev == fdstatp->st_dev) { /* found a match */
                closedir(dp);
                return(pathname);
            }
        }
        closedir(dp);
        return(NULL);
    }
    
    char * ttyname(int fd)
    {
        struct stat     fdstat;
        struct devdir   *ddp;
        char            *rval;
    
        if (isatty(fd) == 0)
            return(NULL);
                if (fstat(fd, &fdstat) < 0)
                    return(NULL);
                if (S_ISCHR(fdstat.st_mode) == 0)
                    return(NULL);
    
        rval = searchdir("/dev", &fdstat);
        if (rval == NULL) {
            for (ddp = head; ddp != NULL; ddp = ddp->d_next)
                if ((rval = searchdir(ddp->d_name, &fdstat)) != NULL)
                    break;
        }
    
        cleanup();
        return(rval);
    }
    

    主要的技术是读取/dev目录,查找一个具有同样设备号码以及i-node号码的文件。前面说过,每个文件系统都有一个唯一的设备号码(stat结构的st_dev成员),每个文件系统的目录项都有一个唯一的i-node号(stat结构的st_ino成员)。我们假定在这个函数中,我们遇到一个匹配的设备号码以及i-node号码的时候,我们定位到了想要的目录项。我们也会检查两者的st_rdev成员是否匹配(也就是终端设备的主、次设备号码)以及目录项是不是一个字符设备文件。但是,因为我们已经确定了文件描述符号参数是一个终端设备和字符设备文件,并且设备号码和i-node号码在UNIX系统中是唯一的,所以,没有必要进行额外的检查。

    我们的终端可能在子目录/dev下面,所以我们搜索其中的所有文件。我们会跳过一些目录,因为它门会产生不正确地输出,这些目录是:/dev/.,/dev/..,/dev/fd。我们也会跳过/dev/stdin,/dev/stdout,和/dev/stderr,因为它们是指向/dev/fd中的一个目录项的链接。

    测试ttyname函数的代码

    我们可以通过如下的代码来检查上面实现的函数:

    #include "apue.h"
    int main(void)
    {
        char *name;
    
        if (isatty(0)) {
            name = ttyname(0);
            if (name == NULL)
                name = "undefined";
        } else {
            name = "not a tty";
        }
        printf("fd 0: %s\n", name);
        if (isatty(1)) {
            name = ttyname(1);
            if (name == NULL)
                name = "undefined";
        } else {
            name = "not a tty";
        }
        printf("fd 1: %s\n", name);
        if (isatty(2)) {
            name = ttyname(2);
            if (name == NULL)
                name = "undefined";
        } else {
            name = "not a tty";
        }
        printf("fd 2: %s\n", name);
        exit(0);
    }
    

    运行上面的程序,我们得到如下的输出:

    $ ./a.out < /dev/console 2> /dev/null
    fd 0: /dev/console
    fd 1: /dev/ttyp3
    fd 2: not a tty
    

    译者注

    原文参考

    参考: APUE2/ch18lev1sec9.html

    相关文章

      网友评论

        本文标题:APUE读书笔记-18终端输入输出(10)

        本文链接:https://www.haomeiwen.com/subject/ikmnjktx.html