libbb: add forgotten file from previous commit :(
This commit is contained in:
		
							
								
								
									
										146
									
								
								libbb/read_key.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										146
									
								
								libbb/read_key.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,146 @@
 | 
			
		||||
/* vi: set sw=4 ts=4: */
 | 
			
		||||
/*
 | 
			
		||||
 * Utility routines.
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2008 Denys Vlasenko
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under GPL version 2, see file LICENSE in this tarball for details.
 | 
			
		||||
 */
 | 
			
		||||
#include "libbb.h"
 | 
			
		||||
 | 
			
		||||
int FAST_FUNC read_key(int fd, smalluint *nbuffered, char *buffer)
 | 
			
		||||
{
 | 
			
		||||
	struct pollfd pfd;
 | 
			
		||||
	const char *seq;
 | 
			
		||||
	int n;
 | 
			
		||||
	int c;
 | 
			
		||||
 | 
			
		||||
	/* Known escape sequences for cursor and function keys */
 | 
			
		||||
	static const char esccmds[] ALIGN1 = {
 | 
			
		||||
		'O','A'        |0x80,KEYCODE_UP      ,
 | 
			
		||||
		'O','B'        |0x80,KEYCODE_DOWN    ,
 | 
			
		||||
		'O','C'        |0x80,KEYCODE_RIGHT   ,
 | 
			
		||||
		'O','D'        |0x80,KEYCODE_LEFT    ,
 | 
			
		||||
		'O','H'        |0x80,KEYCODE_HOME    ,
 | 
			
		||||
		'O','F'        |0x80,KEYCODE_END     ,
 | 
			
		||||
#if 0
 | 
			
		||||
		'O','P'        |0x80,KEYCODE_FUN1    ,
 | 
			
		||||
		'O','Q'        |0x80,KEYCODE_FUN2    ,
 | 
			
		||||
		'O','R'        |0x80,KEYCODE_FUN3    ,
 | 
			
		||||
		'O','S'        |0x80,KEYCODE_FUN4    ,
 | 
			
		||||
#endif
 | 
			
		||||
		'[','A'        |0x80,KEYCODE_UP      ,
 | 
			
		||||
		'[','B'        |0x80,KEYCODE_DOWN    ,
 | 
			
		||||
		'[','C'        |0x80,KEYCODE_RIGHT   ,
 | 
			
		||||
		'[','D'        |0x80,KEYCODE_LEFT    ,
 | 
			
		||||
		'[','H'        |0x80,KEYCODE_HOME    ,
 | 
			
		||||
		'[','F'        |0x80,KEYCODE_END     ,
 | 
			
		||||
		'[','1','~'    |0x80,KEYCODE_HOME    ,
 | 
			
		||||
		'[','2','~'    |0x80,KEYCODE_INSERT  ,
 | 
			
		||||
		'[','3','~'    |0x80,KEYCODE_DELETE  ,
 | 
			
		||||
		'[','4','~'    |0x80,KEYCODE_END     ,
 | 
			
		||||
		'[','5','~'    |0x80,KEYCODE_PAGEUP  ,
 | 
			
		||||
		'[','6','~'    |0x80,KEYCODE_PAGEDOWN,
 | 
			
		||||
#if 0
 | 
			
		||||
		'[','1','1','~'|0x80,KEYCODE_FUN1    ,
 | 
			
		||||
		'[','1','2','~'|0x80,KEYCODE_FUN2    ,
 | 
			
		||||
		'[','1','3','~'|0x80,KEYCODE_FUN3    ,
 | 
			
		||||
		'[','1','4','~'|0x80,KEYCODE_FUN4    ,
 | 
			
		||||
		'[','1','5','~'|0x80,KEYCODE_FUN5    ,
 | 
			
		||||
		'[','1','7','~'|0x80,KEYCODE_FUN6    ,
 | 
			
		||||
		'[','1','8','~'|0x80,KEYCODE_FUN7    ,
 | 
			
		||||
		'[','1','9','~'|0x80,KEYCODE_FUN8    ,
 | 
			
		||||
		'[','2','0','~'|0x80,KEYCODE_FUN9    ,
 | 
			
		||||
		'[','2','1','~'|0x80,KEYCODE_FUN10   ,
 | 
			
		||||
		'[','2','3','~'|0x80,KEYCODE_FUN11   ,
 | 
			
		||||
		'[','2','4','~'|0x80,KEYCODE_FUN12   ,
 | 
			
		||||
#endif
 | 
			
		||||
		0
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	n = *nbuffered;
 | 
			
		||||
	if (n == 0) {
 | 
			
		||||
		/* If no data, block waiting for input. If we read more
 | 
			
		||||
		 * than the minimal ESC sequence size, the "n=0" below
 | 
			
		||||
		 * would instead have to figure out how much to keep,
 | 
			
		||||
		 * resulting in larger code. */
 | 
			
		||||
		n = safe_read(fd, buffer, 3);
 | 
			
		||||
		if (n <= 0)
 | 
			
		||||
			return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Grab character to return from buffer */
 | 
			
		||||
	c = (unsigned char)buffer[0];
 | 
			
		||||
	n--;
 | 
			
		||||
	if (n)
 | 
			
		||||
		memmove(buffer, buffer + 1, n);
 | 
			
		||||
 | 
			
		||||
	/* Only ESC starts ESC sequences */
 | 
			
		||||
	if (c != 27)
 | 
			
		||||
		goto ret;
 | 
			
		||||
 | 
			
		||||
	/* Loop through known ESC sequences */
 | 
			
		||||
	pfd.fd = fd;
 | 
			
		||||
	pfd.events = POLLIN;
 | 
			
		||||
	seq = esccmds;
 | 
			
		||||
	while (*seq != '\0') {
 | 
			
		||||
		/* n - position in sequence we did not read yet */
 | 
			
		||||
		int i = 0; /* position in sequence to compare */
 | 
			
		||||
 | 
			
		||||
		/* Loop through chars in this sequence */
 | 
			
		||||
		while (1) {
 | 
			
		||||
			/* So far escape sequence matched up to [i-1] */
 | 
			
		||||
			if (n <= i) {
 | 
			
		||||
				/* Need more chars, read another one if it wouldn't block.
 | 
			
		||||
				 * Note that escape sequences come in as a unit,
 | 
			
		||||
				 * so if we block for long it's not really an escape sequence.
 | 
			
		||||
				 * Timeout is needed to reconnect escape sequences
 | 
			
		||||
				 * split up by transmission over a serial console. */
 | 
			
		||||
				if (safe_poll(&pfd, 1, 50) == 0) {
 | 
			
		||||
					/* No more data!
 | 
			
		||||
					 * Array is sorted from shortest to longest,
 | 
			
		||||
					 * we can't match anything later in array,
 | 
			
		||||
					 * break out of both loops. */
 | 
			
		||||
					goto ret;
 | 
			
		||||
				}
 | 
			
		||||
				errno = 0;
 | 
			
		||||
				if (safe_read(fd, buffer + n, 1) <= 0) {
 | 
			
		||||
					/* If EAGAIN, then fd is O_NONBLOCK and poll lied:
 | 
			
		||||
					 * in fact, there is no data. */
 | 
			
		||||
					if (errno != EAGAIN)
 | 
			
		||||
						c = -1; /* otherwise it's EOF/error */
 | 
			
		||||
					goto ret;
 | 
			
		||||
				}
 | 
			
		||||
				n++;
 | 
			
		||||
			}
 | 
			
		||||
			if (buffer[i] != (seq[i] & 0x7f)) {
 | 
			
		||||
				/* This seq doesn't match, go to next */
 | 
			
		||||
				seq += i;
 | 
			
		||||
				/* Forward to last char */
 | 
			
		||||
				while (!(*seq & 0x80))
 | 
			
		||||
					seq++;
 | 
			
		||||
				/* Skip it and the keycode which follows */
 | 
			
		||||
				seq += 2;
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
			if (seq[i] & 0x80) {
 | 
			
		||||
				/* Entire seq matched */
 | 
			
		||||
				c = (signed char)seq[i+1];
 | 
			
		||||
				n = 0;
 | 
			
		||||
				/* n -= i; memmove(...);
 | 
			
		||||
				 * would be more correct,
 | 
			
		||||
				 * but we never read ahead that much,
 | 
			
		||||
				 * and n == i here. */
 | 
			
		||||
				goto ret;
 | 
			
		||||
			}
 | 
			
		||||
			i++;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	/* We did not find matching sequence, it was a bare ESC.
 | 
			
		||||
	 * We possibly read and stored more input in buffer[]
 | 
			
		||||
	 * by now. */
 | 
			
		||||
 | 
			
		||||
 ret:
 | 
			
		||||
	*nbuffered = n;
 | 
			
		||||
	return c;
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user