问题描述
我正在尝试测试一个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;
}