blob: b07ceacd59ed3b5d01536fb922a8bacd60d4c45b [file] [log] [blame]
/*
* Copyright (c) 2002-2018, the original author or authors.
*
* This software is distributable under the BSD license. See the terms of the
* BSD license in the documentation provided with this software.
*
* https://opensource.org/licenses/BSD-3-Clause
*/
package jdk.internal.org.jline.utils;
import java.io.Flushable;
import java.io.IOError;
import java.io.IOException;
import java.io.StringWriter;
import java.util.Stack;
/**
* Curses helper methods.
*
* @author <a href="mailto:gnodet@gmail.com">Guillaume Nodet</a>
*/
public final class Curses {
private static Object[] sv = new Object[26];
private static Object[] dv = new Object[26];
private static final int IFTE_NONE = 0;
private static final int IFTE_IF = 1;
private static final int IFTE_THEN = 2;
private static final int IFTE_ELSE = 3;
private Curses() {
}
/**
* Print the given terminal capabilities
*
* @param cap the capability to output
* @param params optional parameters
* @return the result string
*/
public static String tputs(String cap, Object... params) {
if (cap != null) {
StringWriter sw = new StringWriter();
tputs(sw, cap, params);
return sw.toString();
}
return null;
}
/**
* Print the given terminal capabilities
*
* @param out the output stream
* @param str the capability to output
* @param params optional parameters
*/
public static void tputs(Appendable out, String str, Object... params) {
try {
doTputs(out, str, params);
} catch (Exception e) {
throw new IOError(e);
}
}
private static void doTputs(Appendable out, String str, Object... params) throws IOException {
int index = 0;
int length = str.length();
int ifte = IFTE_NONE;
boolean exec = true;
Stack<Object> stack = new Stack<>();
while (index < length) {
char ch = str.charAt(index++);
switch (ch) {
case '\\':
ch = str.charAt(index++);
if (ch >= '0' && ch <= '7') {
int val = ch - '0';
for (int i = 0; i < 2; i++) {
ch = str.charAt(index++);
if (ch < '0' || ch > '7') {
throw new IllegalStateException();
}
val = val * 8 + (ch - '0');
}
out.append((char) val);
} else {
switch (ch) {
case 'e':
case 'E':
if (exec) {
out.append((char) 27); // escape
}
break;
case 'n':
out.append('\n');
break;
// case 'l':
// rawPrint('\l');
// break;
case 'r':
if (exec) {
out.append('\r');
}
break;
case 't':
if (exec) {
out.append('\t');
}
break;
case 'b':
if (exec) {
out.append('\b');
}
break;
case 'f':
if (exec) {
out.append('\f');
}
break;
case 's':
if (exec) {
out.append(' ');
}
break;
case ':':
case '^':
case '\\':
if (exec) {
out.append(ch);
}
break;
default:
throw new IllegalArgumentException();
}
}
break;
case '^':
ch = str.charAt(index++);
if (exec) {
out.append((char)(ch - '@'));
}
break;
case '%':
ch = str.charAt(index++);
switch (ch) {
case '%':
if (exec) {
out.append('%');
}
break;
case 'p':
ch = str.charAt(index++);
if (exec) {
stack.push(params[ch - '1']);
}
break;
case 'P':
ch = str.charAt(index++);
if (ch >= 'a' && ch <= 'z') {
if (exec) {
dv[ch - 'a'] = stack.pop();
}
} else if (ch >= 'A' && ch <= 'Z') {
if (exec) {
sv[ch - 'A'] = stack.pop();
}
} else {
throw new IllegalArgumentException();
}
break;
case 'g':
ch = str.charAt(index++);
if (ch >= 'a' && ch <= 'z') {
if (exec) {
stack.push(dv[ch - 'a']);
}
} else if (ch >= 'A' && ch <= 'Z') {
if (exec) {
stack.push(sv[ch - 'A']);
}
} else {
throw new IllegalArgumentException();
}
break;
case '\'':
ch = str.charAt(index++);
if (exec) {
stack.push((int) ch);
}
ch = str.charAt(index++);
if (ch != '\'') {
throw new IllegalArgumentException();
}
break;
case '{':
int start = index;
while (str.charAt(index++) != '}') ;
if (exec) {
int v = Integer.valueOf(str.substring(start, index - 1));
stack.push(v);
}
break;
case 'l':
if (exec) {
stack.push(stack.pop().toString().length());
}
break;
case '+':
if (exec) {
int v2 = toInteger(stack.pop());
int v1 = toInteger(stack.pop());
stack.push(v1 + v2);
}
break;
case '-':
if (exec) {
int v2 = toInteger(stack.pop());
int v1 = toInteger(stack.pop());
stack.push(v1 - v2);
}
break;
case '*':
if (exec) {
int v2 = toInteger(stack.pop());
int v1 = toInteger(stack.pop());
stack.push(v1 * v2);
}
break;
case '/':
if (exec) {
int v2 = toInteger(stack.pop());
int v1 = toInteger(stack.pop());
stack.push(v1 / v2);
}
break;
case 'm':
if (exec) {
int v2 = toInteger(stack.pop());
int v1 = toInteger(stack.pop());
stack.push(v1 % v2);
}
break;
case '&':
if (exec) {
int v2 = toInteger(stack.pop());
int v1 = toInteger(stack.pop());
stack.push(v1 & v2);
}
break;
case '|':
if (exec) {
int v2 = toInteger(stack.pop());
int v1 = toInteger(stack.pop());
stack.push(v1 | v2);
}
break;
case '^':
if (exec) {
int v2 = toInteger(stack.pop());
int v1 = toInteger(stack.pop());
stack.push(v1 ^ v2);
}
break;
case '=':
if (exec) {
int v2 = toInteger(stack.pop());
int v1 = toInteger(stack.pop());
stack.push(v1 == v2);
}
break;
case '>':
if (exec) {
int v2 = toInteger(stack.pop());
int v1 = toInteger(stack.pop());
stack.push(v1 > v2);
}
break;
case '<':
if (exec) {
int v2 = toInteger(stack.pop());
int v1 = toInteger(stack.pop());
stack.push(v1 < v2);
}
break;
case 'A':
if (exec) {
int v2 = toInteger(stack.pop());
int v1 = toInteger(stack.pop());
stack.push(v1 != 0 && v2 != 0);
}
break;
case '!':
if (exec) {
int v1 = toInteger(stack.pop());
stack.push(v1 == 0);
}
break;
case '~':
if (exec) {
int v1 = toInteger(stack.pop());
stack.push(~v1);
}
break;
case 'O':
if (exec) {
int v2 = toInteger(stack.pop());
int v1 = toInteger(stack.pop());
stack.push(v1 != 0 || v2 != 0);
}
break;
case '?':
if (ifte != IFTE_NONE) {
throw new IllegalArgumentException();
} else {
ifte = IFTE_IF;
}
break;
case 't':
if (ifte != IFTE_IF && ifte != IFTE_ELSE) {
throw new IllegalArgumentException();
} else {
ifte = IFTE_THEN;
}
exec = toInteger(stack.pop()) != 0;
break;
case 'e':
if (ifte != IFTE_THEN) {
throw new IllegalArgumentException();
} else {
ifte = IFTE_ELSE;
}
exec = !exec;
break;
case ';':
if (ifte == IFTE_NONE || ifte == IFTE_IF) {
throw new IllegalArgumentException();
} else {
ifte = IFTE_NONE;
}
exec = true;
break;
case 'i':
if (params.length >= 1) {
params[0] = toInteger(params[0]) + 1;
}
if (params.length >= 2) {
params[1] = toInteger(params[1]) + 1;
}
break;
case 'd':
out.append(Integer.toString(toInteger(stack.pop())));
break;
default:
throw new UnsupportedOperationException();
}
break;
case '$':
if (str.charAt(index) == '<') {
// We don't honour delays, just skip
int nb = 0;
while ((ch = str.charAt(++index)) != '>') {
if (ch >= '0' && ch <= '9') {
nb = nb * 10 + (ch - '0');
} else if (ch == '*') {
// ignore
} else if (ch == '/') {
// ignore
} else {
// illegal, but ...
}
}
index++;
try {
if (out instanceof Flushable) {
((Flushable) out).flush();
}
Thread.sleep(nb);
} catch (InterruptedException e) {
}
} else {
if (exec) {
out.append(ch);
}
}
break;
default:
if (exec) {
out.append(ch);
}
break;
}
}
}
private static int toInteger(Object pop) {
if (pop instanceof Number) {
return ((Number) pop).intValue();
} else if (pop instanceof Boolean) {
return (Boolean) pop ? 1 : 0;
} else {
return Integer.valueOf(pop.toString());
}
}
}