libbb: add forgotten file from previous commit :(
authorDenis Vlasenko <vda.linux@googlemail.com>
Sat, 25 Oct 2008 23:23:32 +0000 (23:23 -0000)
committerDenis Vlasenko <vda.linux@googlemail.com>
Sat, 25 Oct 2008 23:23:32 +0000 (23:23 -0000)
libbb/read_key.c [new file with mode: 0644]

diff --git a/libbb/read_key.c b/libbb/read_key.c
new file mode 100644 (file)
index 0000000..598bd94
--- /dev/null
@@ -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;
+}