public String readLine() {
		synchronized (inputLock) {
			base = getLength();
			if (inputScript != null) {
				String line = null;
				try {
					line = inputScript.readLine();
				} catch (IOException ex) {
					throw new ErrorException(ex);
				}
				if (line != null) {
					insert(line, base, INPUT_STYLE);
					insert("\n", base + line.length(), OUTPUT_STYLE);
					base += line.length() + 1;
					return line;
				}
				try {
					inputScript.close();
				} catch (IOException ex) {
					/* Empty */
				}
				inputScript = null;
			}
			int dot;
			char ch;
			setCaretPosition(base);
			while ((ch = buffer.dequeue()) != '\n' && ch != '\r') {
				if (getCaretPosition() < base) {
					setCaretPosition(getLength());
				}
				dot = getSelectionStart();
				switch (ch) {
				  case '\b': case '\177':
					if (dot == getSelectionEnd()) {
						if (dot > base) {
							delete(dot - 1, dot);
							dot--;
						}
					} else {
						dot = deleteSelection();
					}
					break;
				  case 'A'-'@':
					selectAll();
					dot = -1;
					break;
				  case 'B'-'@':
					dot = Math.max(getSelectionStart() - 1, base);
					break;
				  case 'C'-'@':
					copy();
					dot = -1;
					break;
				  case 'F'-'@':
					dot = Math.min(getSelectionEnd() + 1, getLength());
					break;
				  case 'P'-'@':
					console.printConsole();
					dot = -1;
					break;
				  case 'S'-'@':
					console.save();
					dot = -1;
					break;
				  case 'V'-'@':
					paste();
					dot = -1;
					break;
				  case 'X'-'@':
					cut();
					dot = -1;
					break;
				  default:
					if (dot != getSelectionEnd()) {
						dot = deleteSelection();
					}
					insert("" + ch, dot, INPUT_STYLE);
					dot++;
				}
				if (dot != -1) {
					select(dot, dot);
					setCaretPosition(dot);
				}
			}
			int len = getLength() - base;
			String line = getText(base, base + len);
			insert("\n", base + len, OUTPUT_STYLE);
			base += len + 1;
			return line;
		}
	}