*         stream has been reached
     */
    public Object readBinding(KeyMap keys) throws IOException {
        Object o;
        opBuffer.setLength(0);
        do {
            int c = pushBackChar.isEmpty() ? readCharacter() : pushBackChar.pop();
            if (c == -1) {
                return null;
            }
            opBuffer.appendCodePoint(c);

            if (recording) {
                macro += new String(Character.toChars(c));
            }

            if (quotedInsert) {
                o = Operation.SELF_INSERT;
                quotedInsert = false;
            } else {
                o = keys.getBound(opBuffer);
            }

            /*
             * The kill ring keeps record of whether or not the
             * previous command was a yank or a kill. We reset
             * that state here if needed.
             */
            if (!recording && !(o instanceof KeyMap)) {
                if (o != Operation.YANK_POP && o != Operation.YANK) {
                    killRing.resetLastYank();
                }
                if (o != Operation.KILL_LINE && o != Operation.KILL_WHOLE_LINE
                        && o != Operation.BACKWARD_KILL_WORD && o != Operation.KILL_WORD
                        && o != Operation.UNIX_LINE_DISCARD && o != Operation.UNIX_WORD_RUBOUT) {
                    killRing.resetLastKill();
                }
            }

            if (o == Operation.DO_LOWERCASE_VERSION) {
                opBuffer.setLength(opBuffer.length() - 1);
                opBuffer.append(Character.toLowerCase((char) c));
                o = keys.getBound(opBuffer);
            }

            /*
             * A KeyMap indicates that the key that was struck has a
             * number of keys that can follow it as indicated in the
             * map. This is used primarily for Emacs style ESC-META-x
             * lookups. Since more keys must follow, go back to waiting
             * for the next key.
             */
            if (o instanceof KeyMap) {
                /*
                 * The ESC key (#27) is special in that it is ambiguous until
                 * you know what is coming next.  The ESC could be a literal
                 * escape, like the user entering vi-move mode, or it could
                 * be part of a terminal control sequence.  The following
                 * logic attempts to disambiguate things in the same
                 * fashion as regular vi or readline.
                 *
                 * When ESC is encountered and there is no other pending
                 * character in the pushback queue, then attempt to peek
                 * into the input stream (if the feature is enabled) for
                 * 150ms. If nothing else is coming, then assume it is
                 * not a terminal control sequence, but a raw escape.
                 */
                if (c == ESCAPE
                        && pushBackChar.isEmpty()
                        && in.isNonBlockingEnabled()
                        && in.peek(escapeTimeout) == READ_EXPIRED) {
                    o = ((KeyMap) o).getAnotherKey();
                    if (o == null || o instanceof KeyMap) {
                        continue;
                    }
                    opBuffer.setLength(0);
                } else {
                    continue;
                }
            }

            /*
             * If we didn't find a binding for the key and there is
             * more than one character accumulated then start checking
             * the largest span of characters from the beginning to
             * see if there is a binding for them.
             *
             * For example if our buffer has ESC,CTRL-M,C the getBound()
             * called previously indicated that there is no binding for
             * this sequence, so this then checks ESC,CTRL-M, and failing
             * that, just ESC. Each keystroke that is pealed off the end
             * during these tests is stuffed onto the pushback buffer so
             * they won't be lost.
             *
             * If there is no binding found, then we go back to waiting for
             * input.
             */
            while (o == null && opBuffer.length() > 0) {
                c = opBuffer.charAt(opBuffer.length() - 1);
                opBuffer.setLength(opBuffer.length() - 1);
                Object o2 = keys.getBound(opBuffer);
                if (o2 instanceof KeyMap) {
                    o = ((KeyMap) o2).getAnotherKey();
                    if (o == null) {
                        continue;
                    } else {
                        pushBackChar.push((char) c);
                    }
                }
            }

        } while (o == null || o instanceof KeyMap);

        return o;
    }