APUE中ttyname的递归实现

ttyname给定指定文件描述符上打开的终端设备的路经名,在APUE(中文第三版P564-565)中有一个非递归的实现,花了两个多小时的时间搞懂,现在把关键的代码写上注释。在最后会有一个本人改写的递归实现。

源代码:

#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);
    /*
    * 跳过 ., .., 和 /dev/fd 这些目录
    */
    if ((dirname[len-1] == ’.’) && (dirname[len-2] == ’/’|| 
       (dirname[len-2] == ’.’ &&   dirname[len-3] == ’/’)))
        return;
    if (strcmp(dirname, "/dev/fd") == 0)
        return;
    if ((ddp = malloc(sizeof(struct devdir))) == NULL)
        return;
    if ((ddp->d_name = strdup(dirname)) == 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);
}

add这个函数的作用是建立单向链表,链表中有两个指针,一个指向下一个节点,一个指向目录名所在的内存区域,这块内存是由strdup调用malloc建立的,每当遇到一个目录,searchdir就会调用add将这个目录名保存在节点中d_name指针所指的内存中。

ttyname这一部分本人感到比较难以理解,开始不清楚怎样做到遍历/dev的。其实第一次调用searchdir找出了/dev下的所有目录,然后进行while循环,就是在这个循环中,链表长度会发生变化,而不是第一次searchdir后的长度,感觉这里正是整个程序最关键的技巧。比如,第一次的链表为:/dev/dir1–/dev/dir2–/dev/dir3–NULL,然后从dir1开始查找,如果遇到目录,就会变成/dev/dir1–/dev/dir/2–/dev/dir3–/dev/dir1/dir11/–/dev/dir1/dir12–/dev/dir1/dir13–NULL,依次类推,就是逐个节点查找,遇到目录就在在尾部插入节点,所以调用searchdir的次数就不是确定的,与整个/dev下的目录数有关,开始的时候我就以为调用的次数就是第一次调用searchdir建立链表的节点树,可是这样做不到遍历整个文件树啊,就卡住了。

下面是自己写的递归查找的版本:

#include <unistd.h>
#include <stdio.h>
#include <sys/stat.h>
#include <limits.h>
#include <dirent.h>
#include <string.h>
#include <stdlib.h>


static char pathname[_POSIX_PATH_MAX + 1];

char *
searchdir( char *dirname, struct stat *fdstatp)
{
    int         len;    
    DIR     *dp;
    struct dirent   *dirp;
    struct stat statbuf;

    strcpy(pathname,dirname);
    strcat(pathname,"/");
    len = strlen(pathname);

    if((dp = opendir(dirname)) == NULL)
        return NULL;
    while((dirp = readdir(dp)) != NULL){
        strncpy(pathname +len,dirp->d_name,_POSIX_PATH_MAX - len);
        if ( strcmp(pathname + len,".") == 0 || strcmp(pathname+len+1,".") == 0) 
            continue;
        if (lstat(pathname,&statbuf) < 0)
            return NULL;
        if (S_ISDIR(statbuf.st_mode)){
            if (searchdir(pathname,fdstatp) != NULL)
                return pathname;
        } else if ( S_ISCHR(statbuf.st_mode)){
               if (statbuf.st_dev == fdstatp->st_dev && 
                    statbuf.st_ino == fdstatp->st_ino ){

                closedir(dp);
                return pathname;
               }
        }
    }
    closedir(dp);
    return NULL;
}
char *
my_ttyname( int fd)
{
    struct stat     statbuf;
    char        *termpathname;

    if (isatty(fd) == 0)
        return NULL;

    if (fstat(fd,&statbuf) < 0)
        return NULL;
    termpathname = searchdir("/dev",&statbuf);

    return termpathname;
}

int
main(void)
{
    char *name;

    if (isatty(0)){
        name = my_ttyname(0);
        if (name == NULL)
            name = "undefined";
    }else
        name = "not a tty";
    printf("fd=0: %s\n",name);

    if (isatty(1)){
        name = my_ttyname(1);
        if (name == NULL)
            name = "undefined";
    }else
        name = "not a tty";
    printf("fd=1: %s\n",name);

    if (isatty(2)){
        name = my_ttyname(2);
        if (name == NULL)
            name = "undefined";
    }else
        name = "not a tty";
    printf("fd=2: %s\n",name);

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值