终端标识
程序员文章站
2022-07-01 09:14:43
...
尽管控制终端的名字在多数 UNIX 系统上都是 /dev/tty,不过为了可移植性,POSIX.1 还是提供了一个可用来确定控制终端名字的运行时函数 ctermid。
如果参数 ptr 非空,则被认为是一个指向长度至少为 L_ctermid 字节(定义在 <stdio.h> 中)的数组的指针,终端名也会被存储在该数组中。若 ptr 是一个空指针,则由函数为数组(通常作为静态变量)分配空间。
ctermid 函数的实现大致如下。
isatty 和 ttyname 也是常用的终端函数。如果文件描述符引用一个终端设备,则 isatty 返回真。ttyname 返回的则是该文件描述符打开的终端设备的路径名。
下面代码是 isatty 的实现。我们只使用了一个终端专用函数,并查看其返回值来确认。
测试结果如下:
ttyname 函数的实现就比较长,因为它要搜索所有设备表项来寻找匹配项。
这里通过读取 /dev 目录来寻找具有相同设备号和 i 节点编号的表项。另外,终端名可能在 /dev 的子目录中,所以也需要搜索。这里还跳过了少数几个可能会产生不正确结果或奇怪结果的目录:/dev/.、/dev/.. 和 /dev/fd。同时也跳过了一些别名:/dev/stdin、/dev/stdout 和 /dev/stderr,因为它们是 /dev/fd 目录中文件的符号链接。
运行结果如下:
#include <stdio.h> char *ctermid(char *ptr); /* 返回值:若成功,返回指向终端名的指针;否则,返回指向空字符串的指针 */
如果参数 ptr 非空,则被认为是一个指向长度至少为 L_ctermid 字节(定义在 <stdio.h> 中)的数组的指针,终端名也会被存储在该数组中。若 ptr 是一个空指针,则由函数为数组(通常作为静态变量)分配空间。
ctermid 函数的实现大致如下。
#include <stdio.h> #include <string.h> static char ctermName[L_ctermid]; char *myCtermid(char *ptr){ if(ptr == NULL) ptr = ctermName; return strcpy(ptr, "/dev/tty"); }
isatty 和 ttyname 也是常用的终端函数。如果文件描述符引用一个终端设备,则 isatty 返回真。ttyname 返回的则是该文件描述符打开的终端设备的路径名。
#include <unistd.h> int isatty(int fd); /* 返回值:若为终端设备,返回 1;否则,返回 0 */ char *ttyname(int fd); /* 返回值:指向终端路径名的指针;若出错,返回 NULL */
下面代码是 isatty 的实现。我们只使用了一个终端专用函数,并查看其返回值来确认。
#include <stdio.h> #include <termios.h> int myIsatty(int fd){ struct termios ts; return (tcgetattr(fd, &ts) != -1); // true if no error (is a tty) } int main(void){ printf("fd 0: %s\n", myIsatty(0)? "tty": "not a tty"); printf("fd 1: %s\n", myIsatty(1)? "tty": "not a tty"); printf("fd 2: %s\n", myIsatty(2)? "tty": "not a tty"); return 0; }
测试结果如下:
$ ./myIsatty.out fd 0: tty fd 1: tty fd 2: tty $ ./myIsatty.out </etc/passwd 2>/dev/null fd 0: not a tty fd 1: tty fd 2: not a tty
ttyname 函数的实现就比较长,因为它要搜索所有设备表项来寻找匹配项。
#include <stdio.h> #include <stdlib.h> #include <limits.h> #include <string.h> #include <dirent.h> #include <sys/stat.h> #include <unistd.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){ int 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; struct devdir *ddp = malloc(sizeof(struct devdir)); if(ddp == NULL) return; if((ddp->d_name=strdup(dirname)) == NULL){ free(ddp); return; } ddp->d_next = NULL; if(head == NULL){ head = ddp; tail = ddp; }else{ tail->d_next = ddp; tail = ddp; } } static void cleanup(void){ struct devdir *nddp; while(head != NULL){ nddp = head->d_next; free(head->d_name); free(head); head = nddp; } tail = NULL; } static char *searchdir(char *dirname, struct stat *fdstat){ strcpy(pathname, dirname); strcat(pathname, "/"); int len = strlen(pathname); DIR *dirp = opendir(dirname); struct dirent *itemp; while((itemp=readdir(dirp)) != NULL){ strncpy(pathname+len, itemp->d_name, _POSIX_PATH_MAX-len); /* skip aliases */ if(strcmp(pathname, "/dev/stdin")==0 || strcmp(pathname, "/dev/stdout")==0 || strcmp(pathname, "/dev/stderr")==0) continue; struct stat itemstat; if(stat(pathname, &itemstat) < 0) continue; if(S_ISDIR(itemstat.st_mode)){ add(pathname); continue; } if((itemstat.st_ino==fdstat->st_ino) && (itemstat.st_dev==fdstat->st_dev)){ closedir(dirp); return pathname; } } closedir(dirp); return NULL; } char *myTtyname(int fd){ if(isatty(fd) == 0) return NULL; struct stat fdstat; if(fstat(fd, &fdstat)<0 || S_ISCHR(fdstat.st_mode)==0) return NULL; char *name = searchdir("/dev", &fdstat); if(name == NULL){ struct devdir *ddp; for(ddp=head; ddp!=NULL; ddp=ddp->d_next) if((name=searchdir(ddp->d_name, &fdstat)) != NULL) break; } cleanup(); return name; } void test(int fd){ char *name = NULL; if(isatty(fd)){ if((name=myTtyname(fd)) == NULL) name = "undefined"; }else{ name = "not a tty"; } printf("fd %d: %s\n", fd, name); } int main(void){ test(STDIN_FILENO); test(1); test(2); exit(0); }
这里通过读取 /dev 目录来寻找具有相同设备号和 i 节点编号的表项。另外,终端名可能在 /dev 的子目录中,所以也需要搜索。这里还跳过了少数几个可能会产生不正确结果或奇怪结果的目录:/dev/.、/dev/.. 和 /dev/fd。同时也跳过了一些别名:/dev/stdin、/dev/stdout 和 /dev/stderr,因为它们是 /dev/fd 目录中文件的符号链接。
运行结果如下:
# ./myTtyname.out </dev/console 2>/dev/null fd 0: /dev/console fd 1: /dev/pts/1 fd 2: not a tty
上一篇: struts框架访问数据库
下一篇: 波特率和行控制函数