应该将哪些字节写入pty以生成KEY_HOME?

问题描述

我正在尝试测试一个ncurses应用程序。应该将哪些字节发送到pty主站,以触发KEY_RESIZE,KEY_HOME,KEY_END等的密钥代码

下面有一些代码,我真正希望没有人会浪费时间,但是会包括完整性。此代码的行为如下:

$ perl -e "print pack('c',8)" | ./wrappty ./show-key
KEY_BACKSPACE

也就是说,如果我将0x08写入pty,则ncurses应用程序会将其视为退格键。应该写入什么字节序列以触发其他键代码?我可能会猜测一个特定的字节序列取决于终端,所以我想知道是否有ptys的标准,或者是否有一种确定正确字节序列的合理方法

wrappty.c:

#include <assert.h>
#include <ctype.h>
#include <err.h>
#include <fcntl.h>
#include <limits.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <sys/ioctl.h>
#include <unistd.h>
#ifdef __linux__
# include <pty.h>
# include <utmp.h>
#else
# include <util.h>
#endif

static void
show_data(const char *t,ssize_t s)
{
        for( ; s > 0; t++,s-- ) {
                const char *fmt = isprint(*t) ? "%c" : "0x%02x";
                fprintf(stderr,fmt,*(unsigned char *)t);
        }
        fflush(stderr);
}

static ssize_t
send_msg(int fd,int b)
{
        char buf[1024];
        if( b != EOF ) {
                write(fd,&b,1);
        }
        ssize_t s;
        while( (s = read(fd,buf,sizeof buf)) == sizeof buf ) {
                show_data(buf,s);
        }
        show_data(buf,s);
        return s;
}

static void
wait_for(int fd,const char *expect,size_t siz)
{
        int rc = 0;
        char buf[1024];
        char *a = buf;
        assert(siz < sizeof buf);
        char *end = buf + siz;

        while( a < end ) {
                ssize_t s = read(fd,a,end - a);
                char *e = a + s;
                rc += 1;
                while( a < e && *a++ == *expect++ ) {
                        ;
                }
                if( s < 1 || a < e ) {
                        fprintf(stderr,"Ivalid data\nReceived: ");
                        show_data(buf,e - buf);
                        fprintf(stderr,"\nExpected: ");
                        show_data(expect,siz);
                        fputc('\n',stderr);
                        exit(1);
                }
        }
}

void noop(int sig,siginfo_t *i,void *v) { (void)sig; (void)i; (void)v; }

int
main(int argc,char **argv)
{
        int primary,secondary,c;

        struct winsize ws = { .ws_row = 24,.ws_col = 80 };

        (void) argc;
        if( openpty(&primary,&secondary,NULL,&ws) ) {
                err(EXIT_FAILURE,"openpty");
        }
        switch( fork() ) {
        case -1:
                err(1,"fork");
                break;
        case 0:
                if( close(primary) ) {
                        err(EXIT_FAILURE,"close");
                }
                if( login_tty(secondary) ) {
                        err(EXIT_FAILURE,"login_tty");
                }
                execvp(argv[1],argv + 1);
                err(EXIT_FAILURE,"execvp %s",argv[1]);
        }
        /* Parent */
        if( close(secondary) ) {
                err(EXIT_FAILURE,"close");
        }

        /* Initialization sequence from ncurses on macos */
        char *expected = "\x1b(B\x1b)0\x1b[?1049h\x1b[1;24r\x1b[m\x0f\x1b[4l"
                "\x1b[?1h\x1b=\x1b[H\x1b[J";
        struct sigaction act;
        memset(&act,sizeof act);
        act.sa_sigaction = noop;
        if( sigaction( SIgalRM,&act,NULL ) ) {
                perror("sigaction");
                return EXIT_FAILURE;
        }
        struct timeval tp = {.tv_sec = 0,.tv_usec = 500000 };
        struct itimerval t = { .it_interval = tp,.it_value = tp };
        setitimer(ITIMER_REAL,&t,NULL);
        wait_for(primary,expected,strlen(expected));
        while( (c = getchar()) != EOF ) {
                send_msg(primary,c);
        }

        send_msg(primary,EOF);
        fputc('\n',stderr);
        return 0;
}

show-key.c:

#define _POSIX_C_SOURCE 200809L                                                    
#define _XOPEN_SOURCE 600                                                          
#define _XOPEN_SOURCE_EXTENDED                                                     
#define _DARWIN_C_SOURCE                                                           
                                                                                   
#include <ctype.h>                                                                 
#include <curses.h>                                                                
#include <err.h>                                                                   
#include <fcntl.h>                                                                 
#include <limits.h>                                                                
#include <stdlib.h>                                                                
#include <string.h>                                                                
#include <signal.h>                                                                
#include <unistd.h>                                                                
                                                                                   
int xopen(const char *path,int flags);                                            
void                                                                               
handle(int sig,void *v)                                             
{                                                                                  
        (void)i;                                                                   
        (void)v;                                                                   
        char *n = NULL;                                                            
        switch(sig) {                                                              
        case SIGHUP:  n =  "SIGHUP\n"; break;                                      
        case SIGTERM:  n = "SIGTERM\n"; break;                                     
        case SIGINT:  n =  "SIGINT\n"; break;                                      
        }                                                                          
        if(n)                                                                      
                write(2,n,strlen(n));                                            
        return;                                                                    
}                                                                                  

int                                                                                
main(int argc,char **argv)                                                        
{                                                                                  
        int r;                                                                     
        wint_t w = 0;                                                              
        if( argc > 1 ) {                                                           
                int fd = xopen(argv[1],O_WRONLY);                                 
                dup2(fd,STDERR_FILENO);                                           
        }                                                                          
        unsetenv("COLUMNS");                                                       
        unsetenv("LInes");                                                         
        struct sigaction act;                                                      
        memset(&act,sizeof act);                                               
        act.sa_sigaction = handle;                                                 
        if( sigaction( SIGTERM,NULL ) ) { perror("sigaction"); exit(1); }   
        if( sigaction( SIGINT,NULL ) ) { perror("sigaction"); exit(1); }    
        if( sigaction( SIGHUP,NULL ) ) { perror("sigaction"); exit(1); }    
        if( initscr() == NULL ) {                                                  
                err(1,"initscr");                                                 
        }                                                                          
        noecho();                                                                  
        keypad(stdscr,true);                                                      
        while( (r = get_wch(&w)) != ERR ) {                                        
                char *d = NULL;                                                    
                if( r == KEY_CODE_YES ) switch(w) {                                
                case KEY_RESIZE: d = "KEY_RESIZE"; break;                          
                case KEY_HOME: d = "KEY_HOME"; break;                              
                case KEY_END: d = "KEY_END"; break;                                
                case KEY_PPAGE: d = "KEY_PPAGE"; break;                            
                case KEY_NPAGE: d = "KEY_NPAGE"; break;                            
                case KEY_BACKSPACE: d = "KEY_BACKSPACE"; break;                    
                case KEY_DC: d = "KEY_DC"; break;                                  
                case KEY_IC: d = "KEY_IC"; break;                                  
                case KEY_BTAB: d = "KEY_BTAB"; break;                              
                case KEY_ENTER: d = "KEY_ENTER"; break;                            
                case KEY_UP: d = "KEY_UP"; break;                                  
                case KEY_DOWN: d = "KEY_DOWN"; break;                              
                case KEY_RIGHT: d = "KEY_RIGHT"; break;                            
                case KEY_LEFT: d = "KEY_LEFT"; break;                              
                }                                                                  
                if( d != NULL ) {                                                  
                        /*printw("(%s)",d);*/                                     
                        fprintf(stderr,"%s",d);                                  
                } else {                                                           
                        /*printw("%lc",w);*/                                      
                        fprintf(stderr,"%lc",w);                                 
                }                                                                  
                /*doupdate();*/                                                    
                fflush(stderr);                                                    
        } 
        endwin();                                                                  
        fprintf(stderr,"ERR\n");                                                  
        return 0;                                                                  
}                                                                                  
                                                                                   
int                                                                                
xopen(const char *path,int flags)                                                 
{                                                                                  
        int f = open(path,flags);                                                 
        if( f == -1 ) {                                                            
                perror(path);                                                      
                exit(EXIT_FAILURE);                                                
        }                                                                          
        return f;                                                                  
} 

解决方法

使用tigetstr来获取与键相对应的字符串。大多数与诅咒名称很容易相关(在getch联机帮助页中列出):

foo = tigetstr("khome"); // KEY_HOME

拥有一张桌子会很好,但是由于命名约定是直观,因此似乎没有人感到需要...