歷史沿襲至今,在大多數UNIX系統中,控制終端的名字是/dev/tty. POSIX.1提供了一個運行時函數,可被用來確定控制終端的名字。
#include <stdio.h>
char *ctermid(char *ptr);
返回值:若成功則返回指向控制終端名的指針,若出錯則返回指向空字符串的指針
如果ptr非空,則它被認為是一個指針,指向長度至少為L_ctermid字節的數組,進程的控制終端名存放在該數組中。常量L_ctermid定義在<stdio.h>中。若ptr是一個空指針,則該函數為數組(通常作為靜態變量)分配空間。同樣,進程的控制終端名存放在該數組中。
在這兩種情況中,該數組的起始地址被作為函數值返回。因為大多數UNIX系統都使用/dev/tty作為控制終端名,所以此函數的主要作用是幫助提高向其他操作系統的可移植性。
?
實例:ctermid函數
程序清單18-3 POSIX.1 ctermid函數的實現
#include <stdio.h>
#include <string.h>
?
static char ctermid_name[L_ctermid];
?
char *
ctermid(char *str)
{
????if(str == NULL)
????????str = ctermid_name;
????return(strcpy(str, "/dev/tty"));????/* strcpy() returns str */
}
?
注意,因為我們無法確定調用者緩沖區的大小,所以也就不能防止過度使用該緩沖區。
?
另外兩個與終端標識有關的函數是isatty和ttyname。前者在文件描述符引用一個終端設備時返回真,而后者則返回在該文件描述符上打開的終端設備的路徑名。
#include <unisd.h>
int isatty(int filedes);
返回值:若為終端設備則返回1(真),反則返回0(假)
?
char *ttyname(int filedes);
返回值:指向終端路徑名的指針,若出錯則返回NULL
?
實例:isatty函數
程序清單18-4 POSIX.1 isatty函數的實現
#include <termios.h>
?
int
isatty(int fd)
{
????struct termios????ts;
????
????return(tcgetattr(fd, &ts) != -1);????/* true if no error (is a tty) */
}
?
程序清單18-5測試isatty函數
#include "apue.h"
?
int
main(void)
{
????PRintf("fd 0: %s/n", isatty(0) ? "tty" : "not a tty");
????printf("fd 1: %s/n", isatty(1) ? "tty" : "not a tty");
????printf("fd 2: %s/n", isatty(2) ? "tty" : "not a tty");
????exit(0);
}
?
運行程序清單18-5中的程序時,我們可以得到下面的結果:
?
實例:ttyname函數
程序清單18-6 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節點編號的表項。每個文件系統有一個唯一的設備號(stat結構中的st_dev字段http://www.CUOXin.com/nufangrensheng/p/3501385.html),文件系統中的每個目錄項有一個唯一的i節點號(stat結構中的st_ino字段)。在此函數中假定當找到一個匹配的設備號和匹配的i節點號時,就找到了所希望的目錄項。
我們的終端名可能在/dev的子目錄中。于是,需要搜索在/dev之下的整個文件系統子樹。我們跳過了很多產生不正確或奇怪結果的目錄,它們是/dev/.,/dev/..和/dev/fd。我么也跳過了一些別名,即/dev/stdin、/dev/stdout以及/dev/stderr,它們是對在/dev/fd目錄中文件的符號鏈接。
?
程序清單18-7 測試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);
}
?
運行該程序得到:
文件描述符0、1和2都指向了同一終端/dev/tty1.
新聞熱點
疑難解答