Linux终端的Stdout

时间:2014-03-09 21:37:20

标签: c linux terminal

  • 平台:Linux 3.2.0(Debian Wheezy)
  • 编译:GCC 4.7.2(Debian 4.7.2-5)

我正在编写一个使用ANSI转义序列的函数来获取打开终端中的当前光标位置。最初该函数被硬编码为stdin和stdout。但是我想通过将文件描述符作为参数来使函数在其他终端上工作。我只是不知道为什么或如何将某个人拥有另一个终端的文件描述符。我试图使用谷歌,但我真的不知道如何提出我的问题。无论如何,在我目前的测试用例中,我打开了两个gnome-terminal并运行tty来找到终端的stdin之一。然后我在另一个终端中运行的应用程序中使用open()打开该文件。然后,应用程序请求当前光标位置,并将响应发送到其他终端stdin。我知道他们是一个响应,因为我可以看到部分响应回应到另一个终端,这很奇怪,因为应该禁用回声。所以我的问题是,为什么我只收到部分回复?如何使我的功能按预期工作(在另一个终端中找到当前光标位置的值)。

#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <stdio.h>

int btccgetxy(int term, int *x, int *y)
{
    struct termios attr = {0};
    tcflag_t c_lflag = 0;
    cc_t vtime = 0;
    cc_t vmin = 0;
    int tmpy = 0;
    int tmpx = 0;
    int ret = 0;
    char c = 0;

    //NOTE: This function is written a bit awkwardly
    //because the curent terminal settings at the call of the
    //function must be restored weither an error has occured
    //or not. So in order to avoid rewriting the cleanup code
    //I wrote the function on tracks. If at any point an error
    //should occur the function's flow will move to the end of
    //the function and restore the orginial settings.

    if(tcgetattr(term, &attr) == 0)
    {
        c_lflag = attr.c_lflag;
        vtime = attr.c_cc[VTIME];
        vmin = attr.c_cc[VMIN];

        if(attr.c_lflag & ECHO) attr.c_lflag ^= ECHO;
        if(attr.c_lflag & ICANON) attr.c_lflag ^= ICANON;
        attr.c_cc[VMIN] = 0;
        attr.c_cc[VTIME] = 1;

        if(tcsetattr(term, TCSAFLUSH, &attr) == 0)
        {
            if(write(term, "\033[6n", 4) == 4)
            {
                if(read(term, &c, 1) != 1 || c != '\033'
                || read(term, &c, 1) != 1 || c != '['
                || read(term, &c, 1) != 1 || c == ';')
                    ret = BTCC_ERESPONSE;

                while(c != ';')
                {   
                    if(c < '0' || c > '9')
                    {
                        ret = BTCC_ERESPONSE;
                        break;
                    }

                    tmpy = tmpy * 10 + (c - '0');

                    if(read(term, &c, 1) != 1)
                    {
                        ret = BTCC_ERESPONSE;
                        break;
                    }
                }

                if(ret == 0)
                {
                    if(read(term, &c, 1) == 1 && c != 'R')
                    {
                        while(c != 'R')
                        {
                            if(c < '0' || c > '9')
                            {
                                ret = BTCC_ERESPONSE;
                                break;
                            }

                            tmpx = tmpx * 10 + (c - '0');

                            if(read(term, &c, 1) != 1)
                            {
                                ret = BTCC_ERESPONSE;
                                break;
                            }
                        }

                        if(ret == 0)
                        {
                            *x = tmpx - 1;
                            *y = tmpy - 1;
                        }
                    }
                    else ret = BTCC_ERESPONSE;
                }
            }
            else ret = BTCC_EREQUEST;
        }
        else ret = BTCC_ESET;
    }
    else ret = BTCC_ESAVE;

    attr.c_lflag = c_lflag;
    attr.c_cc[VTIME] = vtime;
    attr.c_cc[VMIN] = vmin;

    if(tcsetattr(term, TCSANOW, &attr) == -1)
        for(int count = 1; count <= BTCC_UNSETATTEMPTS; count++)
            if(tcsetattr(term, TCSANOW, &attr) == -1 && count == BTCC_UNSETATTEMPTS)
                return BTCC_EUNSET;

    return ret;
}

int main()
{
    int x = 0;
    int y = 0;
    int ret = 0;
    int term = 0;
    char c = 0;

    errno = 0;

    //You will need to run tty to find a valid path name
    if((term = open(<valid path name>, O_RDWR)) == -1)
    {
        perror("ERROR(open)");
        return -1;
    }

    ret = btccgetxy(term, &x, &y);

    printf("ret = %i | (%i, %i)\n", ret, x, y);

    return 0;
}

1 个答案:

答案 0 :(得分:2)

在Linux上,使用适当的权限,您可以通过访问文件/proc/1234/fd/1来访问进程1234的文件描述符N(0,1,...)(当然,N = 1)。如果这恰好是一个终端,那么它就像一个终端。无论如何,这是获取另一个进程的文件描述符的一种方法。

我认为你的其余部分几乎不可解决。其他过程使终端开放阅读;尝试从终端读取的进程中的哪一个获得终端发送的响应。到目前为止,你已经失败了;这部分是运气不好,但主要是预期的。

我认为没有一种简单的方法可以阻止另一个进程读取您想要阅读的响应,而不是设置挂起(SIGSTOP)其他终端打开以供阅读的其他进程,以便他们在您的流程之前无法读取数据。但是,这确实不是一种解决方案。