/**
   * Move the cursor <i>where</i> characters, withough checking the current buffer.
   *
   * @see #where
   * @param where the number of characters to move to the right or left.
   */
  private final void moveInternal(final int where) throws IOException {
    // debug ("move cursor " + where + " ("
    // + buf.cursor + " => " + (buf.cursor + where) + ")");
    buf.cursor += where;

    char c;

    if (where < 0) {
      int len = 0;
      for (int i = buf.cursor; i < buf.cursor - where; i++) {
        if (buf.getBuffer().charAt(i) == '\t') len += TAB_WIDTH;
        else len++;
      }

      char cbuf[] = new char[len];
      Arrays.fill(cbuf, BACKSPACE);
      out.write(cbuf);

      return;
    } else if (buf.cursor == 0) {
      return;
    } else if (mask != null) {
      c = mask.charValue();
    } else {
      printCharacters(buf.buffer.substring(buf.cursor - where, buf.cursor).toCharArray());
      return;
    }

    // null character mask: don't output anything
    if (NULL_MASK.equals(mask)) {
      return;
    }

    printCharacters(c, Math.abs(where));
  }
 private final void printCharacters(final char c, final int num) throws IOException {
   if (num == 1) {
     printCharacter(c);
   } else {
     char[] chars = new char[num];
     Arrays.fill(chars, c);
     printCharacters(chars);
   }
 }
  /**
   * Output the specified character to the output stream without manipulating the current buffer.
   */
  private final void printCharacter(final int c) throws IOException {
    if (c == '\t') {
      char cbuf[] = new char[TAB_WIDTH];
      Arrays.fill(cbuf, ' ');
      out.write(cbuf);
      return;
    }

    out.write(c);
  }
  /**
   * Redraw the rest of the buffer from the cursor onwards. This is necessary for inserting text
   * into the buffer.
   *
   * @param clear the number of characters to clear after the end of the buffer
   */
  private final void drawBuffer(final int clear) throws IOException {
    // debug ("drawBuffer: " + clear);
    char[] chars = buf.buffer.substring(buf.cursor).toCharArray();
    if (mask != null) Arrays.fill(chars, mask.charValue());

    printCharacters(chars);

    clearAhead(clear);
    back(chars.length);
    flushConsole();
  }
  /**
   * Output the specified characters to the output stream without manipulating the current buffer.
   */
  private final void printCharacters(final char[] c) throws IOException {
    int len = 0;
    for (int i = 0; i < c.length; i++)
      if (c[i] == '\t') len += TAB_WIDTH;
      else len++;

    char cbuf[];
    if (len == c.length) cbuf = c;
    else {
      cbuf = new char[len];
      int pos = 0;
      for (int i = 0; i < c.length; i++) {
        if (c[i] == '\t') {
          Arrays.fill(cbuf, pos, pos + TAB_WIDTH, ' ');
          pos += TAB_WIDTH;
        } else {
          cbuf[pos] = c[i];
          pos++;
        }
      }
    }

    out.write(cbuf);
  }
  /**
   * Create a new reader.
   *
   * @param in the input
   * @param out the output
   * @param bindings the key bindings to use
   * @param term the terminal to use
   */
  public ConsoleReader(InputStream in, Writer out, InputStream bindings, Terminal term)
      throws IOException {
    this.terminal = term;
    setInput(in);
    this.out = out;

    if (bindings == null) {
      try {
        String bindingFile =
            System.getProperty(
                "jline.keybindings",
                new File(System.getProperty("user.home", ".jlinebindings.properties"))
                    .getAbsolutePath());

        if (new File(bindingFile).isFile()) {
          bindings = new FileInputStream(new File(bindingFile));
        }
      } catch (Exception e) {
        // swallow exceptions with option debugging
        if (debugger != null) {
          e.printStackTrace(debugger);
        }
      }
    }

    if (bindings == null) {
      bindings = terminal.getDefaultBindings();
    }

    this.keybindings = new short[Character.MAX_VALUE * 2];

    Arrays.fill(this.keybindings, UNKNOWN);

    /**
     * Loads the key bindings. Bindings file is in the format:
     *
     * <p>keycode: operation name
     */
    if (bindings != null) {
      Properties p = new Properties();
      p.load(bindings);
      bindings.close();

      for (Iterator i = p.keySet().iterator(); i.hasNext(); ) {
        String val = (String) i.next();

        try {
          Short code = new Short(val);
          String op = (String) p.getProperty(val);

          Short opval = (Short) KEYMAP_NAMES.get(op);

          if (opval != null) {
            keybindings[code.shortValue()] = opval.shortValue();
          }
        } catch (NumberFormatException nfe) {
          consumeException(nfe);
        }
      }

      // hardwired arrow key bindings
      // keybindings[VK_UP] = PREV_HISTORY;
      // keybindings[VK_DOWN] = NEXT_HISTORY;
      // keybindings[VK_LEFT] = PREV_CHAR;
      // keybindings[VK_RIGHT] = NEXT_CHAR;
    }
  }