package net.momodalo.app.vimtouch;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.UnsupportedEncodingException;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import jackpal.androidterm.emulatorview.ColorScheme;
import jackpal.androidterm.emulatorview.TermSession;
import jackpal.androidterm.emulatorview.UpdateCallback;
/**
* A terminal session, consisting of a TerminalEmulator, a TranscriptScreen,
* the PID of the process attached to the session, and the I/O streams used to
* talk to the process.
*/
public class VimTermSession extends TermSession {
private VimSettings mSettings;
private FileDescriptor mTermFd;
private Thread mWatcherThread;
// A cookie which uniquely identifies this session.
private String mHandle;
private String mInitialCommand;
private String mApp;
private String mNat;
private String mFilePath;
private String mUrl;
public static final int PROCESS_EXIT_FINISHES_SESSION = 0;
public static final int PROCESS_EXIT_DISPLAYS_MESSAGE = 1;
private int mProcessExitBehavior = PROCESS_EXIT_FINISHES_SESSION;
private String mProcessExitMessage;
private static final int PROCESS_EXITED = 1;
static class ProcessHandler extends Handler {
private final WeakReference<VimTermSession> mSession;
ProcessHandler(VimTermSession session) {
mSession = new WeakReference<VimTermSession>(session);
}
@Override
public void handleMessage(Message msg) {
VimTermSession session = mSession.get();
if (session == null)
return;
if (!session.isRunning())
return;
if (msg.what == PROCESS_EXITED)
session.onProcessExit((Integer) msg.obj);
}
}
private ProcessHandler mMsgHandler = new ProcessHandler(this);
private UpdateCallback mUTF8ModeNotify = new UpdateCallback() {
public void onUpdate() {
Exec.setPtyUTF8Mode(mTermFd, getUTF8Mode());
}
};
public VimTermSession(String app, String nat, String filepath, String url, VimSettings settings,
String initialCommand) {
super();
mApp = app;
mNat = nat;
mFilePath = filepath;
mUrl = url;
updatePrefs(settings);
initializeSession();
mInitialCommand = initialCommand;
mWatcherThread = new Thread() {
@Override
public void run() {
Log.i(VimTouch.LOG_TAG, "waiting for exec vim" );
int result = Exec.nativeWait();
Log.i(VimTouch.LOG_TAG, "Subprocess exited: " + result);
mMsgHandler.sendMessage(mMsgHandler.obtainMessage(PROCESS_EXITED, result));
}
};
mWatcherThread.setName("Process watcher");
}
public void updatePrefs(VimSettings settings) {
mSettings = settings;
setColorScheme(new ColorScheme(settings.getColorScheme()));
setDefaultUTF8Mode(settings.defaultToUTF8Mode());
}
private boolean mSuRoot = false;
public boolean getSuRoot() {
return mSuRoot;
}
private void initializeSession() {
VimSettings settings = mSettings;
String path = System.getenv("PATH");
if (settings.doPathExtensions()) {
String appendPath = settings.getAppendPath();
if (appendPath != null && appendPath.length() > 0) {
path = path + ":" + appendPath;
}
if (settings.allowPathPrepend()) {
String prependPath = settings.getPrependPath();
if (prependPath != null && prependPath.length() > 0) {
path = prependPath + ":" + path;
}
}
}
if (settings.verifyPath()) {
path = checkPath(path);
}
String[] env = new String[3];
env[0] = "TERM=" + settings.getTermType();
env[1] = "PATH=" + path;
env[2] = "HOME="+mApp;
mSuRoot = settings.getSuRoot();
if(mSuRoot){
try {
Process p = Runtime.getRuntime().exec("su -c ls");
p.waitFor();
} catch (Exception e) {
mSuRoot = false;
}
}
createSubprocess(settings.getShell(), env);
setTermOut(new FileOutputStream(mTermFd));
setTermIn(new FileInputStream(mTermFd));
}
private String checkPath(String path) {
String[] dirs = path.split(":");
StringBuilder checkedPath = new StringBuilder(path.length());
for (String dirname : dirs) {
File dir = new File(dirname);
if (dir.isDirectory()) {
checkedPath.append(dirname);
checkedPath.append(":");
}
}
return checkedPath.substring(0, checkedPath.length()-1);
}
@Override
public void initializeEmulator(int columns, int rows) {
super.initializeEmulator(columns, rows);
Exec.setPtyWindowSize(mTermFd, rows, columns, 0, 0);
Exec.setPtyUTF8Mode(mTermFd, getUTF8Mode());
setUTF8ModeUpdateCallback(mUTF8ModeNotify);
mWatcherThread.start();
Exec.startVim();
//sendInitialCommand(mInitialCommand);
}
private void sendInitialCommand(String initialCommand) {
if (initialCommand.length() > 0) {
write(initialCommand + '\r');
}
}
private void createSubprocess(String shell, String[] env) {
mTermFd = Exec.createSubprocess(mNat, mFilePath, mUrl, getSuRoot()?"/system/xbin/su":null, env);
Log.i("TermSession", "FD: "+mTermFd);
}
private ArrayList<String> parse(String cmd) {
final int PLAIN = 0;
final int WHITESPACE = 1;
final int INQUOTE = 2;
int state = WHITESPACE;
ArrayList<String> result = new ArrayList<String>();
int cmdLen = cmd.length();
StringBuilder builder = new StringBuilder();
for (int i = 0; i < cmdLen; i++) {
char c = cmd.charAt(i);
if (state == PLAIN) {
if (Character.isWhitespace(c)) {
result.add(builder.toString());
builder.delete(0,builder.length());
state = WHITESPACE;
} else if (c == '"') {
state = INQUOTE;
} else {
builder.append(c);
}
} else if (state == WHITESPACE) {
if (Character.isWhitespace(c)) {
// do nothing
} else if (c == '"') {
state = INQUOTE;
} else {
state = PLAIN;
builder.append(c);
}
} else if (state == INQUOTE) {
if (c == '\\') {
if (i + 1 < cmdLen) {
i += 1;
builder.append(cmd.charAt(i));
}
} else if (c == '"') {
state = PLAIN;
} else {
builder.append(c);
}
}
}
if (builder.length() > 0) {
result.add(builder.toString());
}
return result;
}
@Override
public void updateSize(int columns, int rows) {
// Inform the attached pty of our new size:
Exec.setPtyWindowSize(mTermFd, rows, columns, 0, 0);
super.updateSize(columns, rows);
}
/* XXX We should really get this ourselves from the resource bundle, but
we cannot hold a context */
public void setProcessExitMessage(String message) {
mProcessExitMessage = message;
}
private void onProcessExit(int result) {
if (mSettings.closeWindowOnProcessExit()) {
finish();
} else if (mProcessExitMessage != null) {
try {
byte[] msg = ("\r\n[" + mProcessExitMessage + "]").getBytes("UTF-8");
appendToEmulator(msg, 0, msg.length);
notifyUpdate();
} catch (UnsupportedEncodingException e) {
// Never happens
}
}
}
@Override
public void finish() {
Exec.close(mTermFd);
super.finish();
}
public void setHandle(String handle) {
if (mHandle != null) {
throw new IllegalStateException("Cannot change handle once set");
}
mHandle = handle;
}
public String getHandle() {
return mHandle;
}
}