package org.gscript.terminal; import java.nio.ByteBuffer; import java.util.ArrayList; import android.util.Log; /** * VT100 escape sequence parser * a list of valid VT100 escape sequences can be found at: * {@link http://ascii-table.com/ansi-escape-sequences-vt-100.php} */ public class EscapeSequence { private static final String LOG_TAG = "EscapeSequence"; private static final int MAX_SEQUENCE_LENGTH = 32; /* EscapeSequence initiator types ( ESC and intermediate characters ) */ private static final int ESC = 1; private static final int POUND = 2; private static final int LPARENTHESIS = 3; private static final int RPARENTHESIS = 4; private static final int LBRACKET = 5; private static final int QUESTIONMARK = 6; /* Escape + intermediate characters */ private static final int ESC_POUND = ESC + (10 * POUND); private static final int ESC_LPARENTHESIS = ESC + (10 * LPARENTHESIS); private static final int ESC_RPARENTHESIS = ESC + (10 * RPARENTHESIS); private static final int ESC_LBRACKET = ESC + (10 * LBRACKET); private static final int ESC_LBRACKET_QUESTIONMARK = ESC + (10 * LBRACKET) + (100 * QUESTIONMARK); public static final int SEQUENCE_TYPE_UNKNOWN = -1; public static final int SEQUENCE_TYPE_UNHANDLED = 0; /* VT100 */ public static final int SEQUENCE_TYPE_LMN = 1; public static final int SEQUENCE_TYPE_DECCKM = 2; public static final int SEQUENCE_TYPE_DECANM = 3; public static final int SEQUENCE_TYPE_DECCOLM = 4; public static final int SEQUENCE_TYPE_DECSCLM = 5; public static final int SEQUENCE_TYPE_DECSCNM = 6; public static final int SEQUENCE_TYPE_DECOM = 7; public static final int SEQUENCE_TYPE_DECAWM = 8; public static final int SEQUENCE_TYPE_DECARM = 9; public static final int SEQUENCE_TYPE_DECINLM = 10; public static final int SEQUENCE_TYPE_DECKPAM = 11; public static final int SEQUENCE_TYPE_DECKPNM = 12; public static final int SEQUENCE_TYPE_SETUKG0 = 13; public static final int SEQUENCE_TYPE_SETUKG1 = 14; public static final int SEQUENCE_TYPE_SETUSG0 = 15; public static final int SEQUENCE_TYPE_SETUSG1 = 16; public static final int SEQUENCE_TYPE_SETSPECG0 = 17; public static final int SEQUENCE_TYPE_SETSPECG1 = 18; public static final int SEQUENCE_TYPE_SETALTG0 = 19; public static final int SEQUENCE_TYPE_SETALTG1 = 20; public static final int SEQUENCE_TYPE_SETALTSPECG0 = 21; public static final int SEQUENCE_TYPE_SETALTSPECG1 = 22; public static final int SEQUENCE_TYPE_SS2 = 23; public static final int SEQUENCE_TYPE_SS3 = 24; public static final int SEQUENCE_TYPE_SGR = 25; public static final int SEQUENCE_TYPE_DECSTBM = 26; public static final int SEQUENCE_TYPE_CUU = 27; public static final int SEQUENCE_TYPE_CUD = 28; public static final int SEQUENCE_TYPE_CUF = 29; public static final int SEQUENCE_TYPE_CUB = 30; public static final int SEQUENCE_TYPE_CNL = 31; public static final int SEQUENCE_TYPE_CPL = 32; public static final int SEQUENCE_TYPE_CHA = 33; public static final int SEQUENCE_TYPE_CUP = 34; public static final int SEQUENCE_TYPE_IND = 35; public static final int SEQUENCE_TYPE_RI = 36; public static final int SEQUENCE_TYPE_NEL = 37; public static final int SEQUENCE_TYPE_DECSC = 38; public static final int SEQUENCE_TYPE_DECRC = 39; public static final int SEQUENCE_TYPE_HTS = 40; public static final int SEQUENCE_TYPE_TBC = 41; public static final int SEQUENCE_TYPE_DECDHL = 42; public static final int SEQUENCE_TYPE_DECSWL = 43; public static final int SEQUENCE_TYPE_DECDWL = 44; public static final int SEQUENCE_TYPE_EL0 = 45; public static final int SEQUENCE_TYPE_EL1 = 46; public static final int SEQUENCE_TYPE_EL2 = 47; public static final int SEQUENCE_TYPE_ED0 = 48; public static final int SEQUENCE_TYPE_ED1 = 49; public static final int SEQUENCE_TYPE_ED2 = 50; public static final int SEQUENCE_TYPE_DSR = 51; public static final int SEQUENCE_TYPE_CPR = 52; public static final int SEQUENCE_TYPE_DA = 53; public static final int SEQUENCE_TYPE_RIS = 54; public static final int SEQUENCE_TYPE_DECALN = 55; public static final int SEQUENCE_TYPE_DECTST = 56; public static final int SEQUENCE_TYPE_DECLL0 = 57; public static final int SEQUENCE_TYPE_DECLL1 = 58; public static final int SEQUENCE_TYPE_DECLL2 = 59; public static final int SEQUENCE_TYPE_DECLL3 = 60; public static final int SEQUENCE_TYPE_DECLL4 = 61; public static final int ARGUMENT_DEFAULT = -1; private int mInitiatorIndex = 1; private int mInitiator = 0; private int mType = 0; private boolean mFinished; private int mSequenceLength; private ByteBuffer mSequenceBuffer; private int mArgumentIndex = 0; private ArrayList<Integer> mArguments = new ArrayList<Integer>(); public EscapeSequence(byte b) { mSequenceBuffer = ByteBuffer.allocate(MAX_SEQUENCE_LENGTH); append(b); } public boolean append(byte b) { switch(b) { /* append reserved characters to the initiator sequence */ case ControlCharacter.ESC: appendInitiator(ESC); break; case ControlCharacter.CSI: /* ESC + [ */ appendInitiator(ESC); appendInitiator(LBRACKET); break; /* intermediate characters */ case '#': appendInitiator(POUND); break; case '(': appendInitiator(LPARENTHESIS); break; case ')': appendInitiator(RPARENTHESIS); break; case '[': appendInitiator(LBRACKET); break; case '?': appendInitiator(QUESTIONMARK); break; default: switch (mInitiator) { case ESC: /* ESC */ switch(b) { case ';': nextArgument(); break; case '=': setFinished(SEQUENCE_TYPE_DECKPAM); break; case '>': setFinished(SEQUENCE_TYPE_DECKPNM); break; case 'N': setFinished(SEQUENCE_TYPE_SS2); break; case 'O': setFinished(SEQUENCE_TYPE_SS3); break; case 'D': setFinished(SEQUENCE_TYPE_IND); break; case 'M': setFinished(SEQUENCE_TYPE_RI); break; case 'E': setFinished(SEQUENCE_TYPE_NEL); break; case '7': setFinished(SEQUENCE_TYPE_DECSC); break; case '8': setFinished(SEQUENCE_TYPE_DECRC); break; case 'H': setFinished(SEQUENCE_TYPE_HTS); break; case 'c': setFinished(SEQUENCE_TYPE_RIS); break; /* VT-52 compatibility only */ case '<': case 'F': case 'G': case 'A': case 'B': case 'C': case 'I': case 'K': case 'J': case 'Z': setFinished(SEQUENCE_TYPE_UNHANDLED); break; default: if(!updateArgument(b)) { Log.e(LOG_TAG, "Unknown byte in sequence: "+ b); setFinished(SEQUENCE_TYPE_UNKNOWN); } break; } break; case ESC_POUND: /* ESC # */ switch(b) { case ';': nextArgument(); break; case '3': setFinished(SEQUENCE_TYPE_DECDHL); break; case '4': setFinished(SEQUENCE_TYPE_DECDHL); break; case '5': setFinished(SEQUENCE_TYPE_DECSWL); break; case '6': setFinished(SEQUENCE_TYPE_DECDWL); break; case '8': setFinished(SEQUENCE_TYPE_DECALN); break; default: if(!updateArgument(b)) { Log.e(LOG_TAG, "Unknown byte in sequence: "+ b); setFinished(SEQUENCE_TYPE_UNKNOWN); } break; } break; case ESC_LPARENTHESIS: /* ESC ( */ switch(b) { case ';': nextArgument(); break; case 'A': setFinished(SEQUENCE_TYPE_SETUKG0); break; case 'B': setFinished(SEQUENCE_TYPE_SETUSG0); break; case '0': setFinished(SEQUENCE_TYPE_SETSPECG0); break; case '1': setFinished(SEQUENCE_TYPE_SETALTG0); break; case '2': setFinished(SEQUENCE_TYPE_SETALTSPECG0); break; default: if(!updateArgument(b)) { Log.e(LOG_TAG, "Unknown byte in sequence: "+ b); setFinished(SEQUENCE_TYPE_UNKNOWN); } break; } break; case ESC_RPARENTHESIS: /* ESC ) */ switch(b) { case ';': nextArgument(); break; case 'A': setFinished(SEQUENCE_TYPE_SETUKG1); break; case 'B': setFinished(SEQUENCE_TYPE_SETUSG1); break; case '0': setFinished(SEQUENCE_TYPE_SETSPECG1); break; case '1': setFinished(SEQUENCE_TYPE_SETALTG1); break; case '2': setFinished(SEQUENCE_TYPE_SETALTSPECG1); break; default: if(!updateArgument(b)) { Log.e(LOG_TAG, "Unknown byte in sequence: "+ b); setFinished(SEQUENCE_TYPE_UNKNOWN); } break; } break; case ESC_LBRACKET: /* ESC [ */ switch(b) { case ';': nextArgument(); break; case 'h': setFinished(SEQUENCE_TYPE_LMN); break; case 'l': setFinished(SEQUENCE_TYPE_LMN); break; case 'm': setFinished(SEQUENCE_TYPE_SGR); break; case 'r': setFinished(SEQUENCE_TYPE_DECSTBM); break; case 'A': setFinished(SEQUENCE_TYPE_CUU); break; case 'B': setFinished(SEQUENCE_TYPE_CUD); break; case 'C': setFinished(SEQUENCE_TYPE_CUF); break; case 'D': setFinished(SEQUENCE_TYPE_CUB); break; case 'E': setFinished(SEQUENCE_TYPE_CNL); break; case 'F': setFinished(SEQUENCE_TYPE_CPL); break; case 'G': setFinished(SEQUENCE_TYPE_CHA); break; case 'H': setFinished(SEQUENCE_TYPE_CUP); break; case 'f': /* HVP == CUP */ setFinished(SEQUENCE_TYPE_CUP); break; case 'g': setFinished(SEQUENCE_TYPE_TBC); case 'K': switch(getArgumentOrDefault(0)) { case 0: setFinished(SEQUENCE_TYPE_EL0); break; case 1: setFinished(SEQUENCE_TYPE_EL1); break; case 2: setFinished(SEQUENCE_TYPE_EL2); break; default: setFinished(SEQUENCE_TYPE_UNKNOWN); break; } break; case 'J': switch(getArgumentOrDefault(0)) { case 0: setFinished(SEQUENCE_TYPE_ED0); break; case 1: setFinished(SEQUENCE_TYPE_ED1); break; case 2: setFinished(SEQUENCE_TYPE_ED2); break; default: setFinished(SEQUENCE_TYPE_UNKNOWN); break; } break; case 'n': setFinished(SEQUENCE_TYPE_DSR); break; case 'R': setFinished(SEQUENCE_TYPE_CPR); break; case 'c': setFinished(SEQUENCE_TYPE_DA); break; case 'y': setFinished(SEQUENCE_TYPE_DECTST); break; case 'q': switch(getArgumentOrDefault(0)) { case 0: setFinished(SEQUENCE_TYPE_DECLL0); break; case 1: setFinished(SEQUENCE_TYPE_DECLL1); break; case 2: setFinished(SEQUENCE_TYPE_DECLL2); break; case 3: setFinished(SEQUENCE_TYPE_DECLL3); break; case 4: setFinished(SEQUENCE_TYPE_DECLL4); break; default: setFinished(SEQUENCE_TYPE_UNKNOWN); break; } break; default: if(!updateArgument(b)) { Log.e(LOG_TAG, "Unknown byte in sequence: "+ b); setFinished(SEQUENCE_TYPE_UNKNOWN); } break; } break; case ESC_LBRACKET_QUESTIONMARK: /* ESC[? */ switch(b) { case ';': nextArgument(); break; case 'h': switch(getArgumentOrDefault(0)) { case 1: setFinished(SEQUENCE_TYPE_DECCKM); break; case 2: setFinished(SEQUENCE_TYPE_DECANM); break; case 3: setFinished(SEQUENCE_TYPE_DECCOLM); break; case 4: setFinished(SEQUENCE_TYPE_DECSCLM); break; case 5: setFinished(SEQUENCE_TYPE_DECSCNM); break; case 6: setFinished(SEQUENCE_TYPE_DECOM); break; case 7: setFinished(SEQUENCE_TYPE_DECAWM); break; case 8: setFinished(SEQUENCE_TYPE_DECARM); break; case 9: setFinished(SEQUENCE_TYPE_DECINLM); break; default: setFinished(SEQUENCE_TYPE_UNKNOWN); break; } break; case 'l': switch(getArgumentOrDefault(0)) { case 1: setFinished(SEQUENCE_TYPE_DECCKM); break; case 2: setFinished(SEQUENCE_TYPE_DECANM); break; case 3: setFinished(SEQUENCE_TYPE_DECCOLM); break; case 4: setFinished(SEQUENCE_TYPE_DECSCLM); break; case 5: setFinished(SEQUENCE_TYPE_DECSCNM); break; case 6: setFinished(SEQUENCE_TYPE_DECOM); break; case 7: setFinished(SEQUENCE_TYPE_DECAWM); break; case 8: setFinished(SEQUENCE_TYPE_DECARM); break; case 9: setFinished(SEQUENCE_TYPE_DECINLM); break; default: setFinished(SEQUENCE_TYPE_UNKNOWN); break; } break; case 'c': setFinished(SEQUENCE_TYPE_DA); break; default: if(!updateArgument(b)) { Log.e(LOG_TAG, "Unknown byte in sequence: "+ b); setFinished(SEQUENCE_TYPE_UNKNOWN); } break; } break; default: /* should never happen unless we are receiving * reserved escape characters in a wrong order */ Log.e(LOG_TAG, "Unknown sequence initiator!"); setFinished(SEQUENCE_TYPE_UNKNOWN); break; } } if(mSequenceLength < MAX_SEQUENCE_LENGTH) { /* store the entire escape sequence in a ByteBuffer * so that we can output it for debugging purposes */ mSequenceBuffer.put(b); mSequenceLength++; } else { /* should never happen unless a closing character * of an escape sequence is missing */ Log.e(LOG_TAG, "Sequence longer then max length."); setFinished(SEQUENCE_TYPE_UNKNOWN); } return mFinished; } private boolean updateArgument(byte b) { /* check if the value is in range (numeric 0..9) */ int val = (b - 48); if (val >= 0 && val <= 9) { /* check if the argument has been created if not do so now and set its value to 0 */ if (mArgumentIndex == mArguments.size()) mArguments.add(0); int arg = mArguments.get(mArgumentIndex); // /* set arguments with ARGUMENT_DEFAULT value to 0 */ if(arg == ARGUMENT_DEFAULT) arg = 0; /* argument values come in as a character byte stream, * every time the argument is updated multiply the existing * arg value with 10 and then add the new value. */ arg *= 10; arg += val; /* update argument */ mArguments.set(mArgumentIndex, arg); return true; } return false; } private void nextArgument() { /* arguments are added with ARGUMENT_DEFAULT (-1) as soon as we are actually updating its * value we will first set it to 0. The final EscapeSequence type will decide what value * an argument with ARGUMENT_DEFAULT will become */ if(mArguments.isEmpty()) { /* Happens when we are immediatly moving to the next argument without any previous arg value set * Eg: Esc[;H * Add an argument to compensate so that we will end up with a normalized statement: * Esc[{ARGUMENT_DEFAULT};{ARGUMENT_DEFAULT}H * * */ mArguments.add(ARGUMENT_DEFAULT); } /* raise index and add a new arg value */ mArgumentIndex++; mArguments.add(ARGUMENT_DEFAULT); } private void appendInitiator(int type) { /* multiple initiator type (esc, pound, bracket etc) * with the index value so that we know the initiator order */ mInitiator += (mInitiatorIndex * type); mInitiatorIndex*=10; } public byte[] getSequence() { byte[] sequence = new byte[mSequenceLength]; int pos = mSequenceBuffer.position(); mSequenceBuffer.position(0); mSequenceBuffer.get(sequence, 0, mSequenceLength); mSequenceBuffer.position(pos); return sequence; } public byte getLastSequenceByte() { return (mSequenceLength > 0) ? mSequenceBuffer.get(mSequenceLength-1) : 0; } private void setFinished(int type) { mType = type; mFinished = true; } public boolean isFinished() { return mFinished; } public int getType() { return mType; } public ArrayList<Integer> getArguments() { return mArguments; } public Integer getArgumentOrDefault(int index, int defVal) { if(mArguments.size() < (index + 1)) return defVal; int argVal = mArguments.get(index); return (argVal == ARGUMENT_DEFAULT) ? defVal : argVal; } public Integer getArgumentOrDefault(int index) { return getArgumentOrDefault(index, 0); } public Integer getArgument(int index) { return mArguments.get(index); } public int getArgumentCount() { return mArguments.size(); } @Override public String toString() { String strSequence = ""; byte[] sequence = getSequence(); for(int i=0; i < sequence.length; ++i) { switch(sequence[i]) { case ControlCharacter.ESC: strSequence+="ESC"; break; case ControlCharacter.CSI: strSequence+="CSI"; break; default: strSequence+=(char)sequence[i]; } } Log.d(LOG_TAG, "arguments: "); for(int i=0; i < mArguments.size(); ++i) Log.d(LOG_TAG, "arg: "+ mArguments.get(i)); return String.format( "EscapeSequence ( type:%d, arguments:%d, sequence:%s )", mType, mArguments.size(), strSequence); } }