/*
 *
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You under the Apache License, Version 2.0
 *  (the "License"); you may not use this file except in compliance with
 *  the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */

package flex.tools.debugger.cli;

import flash.localization.LocalizationManager;
import flash.tools.debugger.*;
import flash.tools.debugger.concrete.DProtocol;
import flash.tools.debugger.concrete.DSwfInfo;
import flash.tools.debugger.events.*;
import flash.tools.debugger.expression.ECMA;
import flash.tools.debugger.expression.NoSuchVariableException;
import flash.tools.debugger.expression.PlayerFaultException;
import flash.tools.debugger.expression.ValueExp;
import flash.util.FieldFormat;
import flash.util.Trace;
import flex.tools.debugger.cli.ExpressionCache.EvaluationResult;
import flex.tools.debugger.cli.FaultActions.FaultActionsBuilder;
import org.apache.commons.lang.LocaleUtils;

import java.io.*;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.*;

/**
 * This is a front end command line interface to the Flash Debugger
 * Player.
 * <p/>
 * This tool utilizes the Debugger Java API (DJAPI) for Flash
 * Player that exists in flash.tools.debuggger.
 * <p/>
 * This tool is not completely compliant with the API, since
 * some commands expose implementation specific information for
 * debugging purposes.  Instances where this occurs are kept to a
 * minimum and are isolated in a special class called Extensions.
 * If you wish to build a version that is completely API
 * compatible.  Replace Extensions with ExtensionsDisabled in
 * the static method calls at the end of this file.
 */
public class DebugCLI implements Runnable, SourceLocator {
	public static final String VERSION = "82"; //$NON-NLS-1$

	public static final int CMD_UNKNOWN = 0;
	public static final int CMD_QUIT = 1;
	public static final int CMD_CONTINUE = 2;
	public static final int CMD_STEP = 3;
	public static final int CMD_NEXT = 4;
	public static final int CMD_FINISH = 5;
	public static final int CMD_BREAK = 6;
	public static final int CMD_SET = 7;
	public static final int CMD_LIST = 8;
	public static final int CMD_PRINT = 9;
	public static final int CMD_TUTORIAL = 10;
	public static final int CMD_INFO = 11;
	public static final int CMD_HOME = 12;
	public static final int CMD_RUN = 13;
	public static final int CMD_FILE = 14;
	public static final int CMD_DELETE = 15;
	public static final int CMD_SOURCE = 16;
	public static final int CMD_COMMENT = 17;
	public static final int CMD_CLEAR = 18;
	public static final int CMD_HELP = 19;
	public static final int CMD_SHOW = 20;
	public static final int CMD_KILL = 21;
	public static final int CMD_HANDLE = 22;
	public static final int CMD_ENABLE = 23;
	public static final int CMD_DISABLE = 24;
	public static final int CMD_DISPLAY = 25;
	public static final int CMD_UNDISPLAY = 26;
	public static final int CMD_COMMANDS = 27;
	public static final int CMD_PWD = 28;
	public static final int CMD_CF = 29;
	public static final int CMD_CONDITION = 30;
	public static final int CMD_AWATCH = 31;
	public static final int CMD_WATCH = 32;
	public static final int CMD_RWATCH = 33;
	public static final int CMD_WHAT = 34;
	public static final int CMD_DISASSEMBLE = 35;
	public static final int CMD_HALT = 36;
	public static final int CMD_MCTREE = 37;
	public static final int CMD_VIEW_SWF = 38;
	public static final int CMD_DOWN = 39;
	public static final int CMD_UP = 40;
	public static final int CMD_FRAME = 41;
	public static final int CMD_DIRECTORY = 42;
	public static final int CMD_CATCH = 43;
	public static final int CMD_CONNECT = 44;
	public static final int CMD_WORKER = 45;

	/* info sub commands */
	public static final int INFO_UNKNOWN_CMD = 100;
	public static final int INFO_ARGS_CMD = 101;
	public static final int INFO_BREAK_CMD = 102;
	public static final int INFO_FILES_CMD = 103;
	public static final int INFO_HANDLE_CMD = 104;
	public static final int INFO_FUNCTIONS_CMD = 105;
	public static final int INFO_LOCALS_CMD = 106;
	public static final int INFO_SCOPECHAIN_CMD = 107;
	public static final int INFO_SOURCES_CMD = 108;
	public static final int INFO_STACK_CMD = 109;
	public static final int INFO_VARIABLES_CMD = 110;
	public static final int INFO_DISPLAY_CMD = 111;
	public static final int INFO_TARGETS_CMD = 112;
	public static final int INFO_SWFS_CMD = 113;
	public static final int INFO_WORKERS_CMD = 114;

	/* show subcommands */
	public static final int SHOW_UNKNOWN_CMD = 200;
	public static final int SHOW_NET_CMD = 201;
	public static final int SHOW_FUNC_CMD = 202;
	public static final int SHOW_URI_CMD = 203;
	public static final int SHOW_PROPERTIES_CMD = 204;
	public static final int SHOW_FILES_CMD = 205;
	public static final int SHOW_BREAK_CMD = 206;
	public static final int SHOW_VAR_CMD = 207;
	public static final int SHOW_MEM_CMD = 208;
	public static final int SHOW_LOC_CMD = 209;
	public static final int SHOW_DIRS_CMD = 210;

	/* misc subcommands */
	public static final int ENABLE_ONCE_CMD = 301;

	// default metadata retry count 8 attempts per waitForMetadata() call * 5 calls
	public static final int METADATA_RETRIES = 8 * 5;

	/**
	 * Whether the break command will show it changed workers.
	 * true at the moment because the break function itself does not display in
	 * which worker the breakpoint has been set.
	 */
	public static final boolean WORKER_DISPLAY_INTERNAL_SWAP_INFO = true;

	/* Enum for the state of the initial prompt shown when a swf is loaded */
	public static enum InitialPromptState {
		NEVER_SHOWN, SHOWN_ONCE, DONE
	}

	Stack<LineNumberReader> m_readerStack = new Stack<LineNumberReader>();
	public PrintStream m_err;
	public PrintStream m_out;
	Session m_session;
	String m_launchURI;
	boolean m_fullnameOption; // emacs mode
	String m_cdPath;
	String m_mruURI;
	String m_connectPort;
	private boolean m_quietMode; // Don't dump out the state of the execution when WORKER_DISPLAY_INTERNAL_SWAP_INFO is set to false.
	public final static String m_newline = System.getProperty("line.separator"); //$NON-NLS-1$

	private final static LocalizationManager m_localizationManager = new LocalizationManager();
	private final static FaultActionsBuilder faultActionsBuilder = new FaultActionsBuilder(m_localizationManager);

	List<String> m_sourceDirectories; // List of String
	int m_sourceDirectoriesChangeCount;
	private File m_flexHomeDirectory; // <application.home>/frameworks/projects/*/src always goes in m_sourceDirectories
	private boolean m_initializedFlexHomeDirectory;

	// context information for our current session
	FileInfoCache m_fileInfo;
	FaultActions m_faultTable;
	Vector<Integer> m_breakIsolates;
	ExpressionCache m_exprCache;
	Vector<BreakAction> m_breakpoints;
	Vector<WatchAction> m_watchpoints;
	Vector<CatchAction> m_catchpoints;
	ArrayList<DisplayAction> m_displays;
	//	boolean			m_requestResume;
//	boolean			m_requestHalt;
//	boolean			m_stepResume;
	int m_activeIsolate;
	DebugCLIIsolateState m_mainState;

	/* This indicates the isolate for which we have been showing prompts for setting
     * breakpoints( so that we don't switch worker while the user is setting breakpoints) */
	int m_lastPromptIsolate;

	private HashMap<Integer, DebugCLIIsolateState> m_isolateState;

	class DebugCLIIsolateState {
		//		public FileInfoCache	m_fileInfo;
//		public ExpressionCache m_exprCache;
		public boolean m_requestResume;
		public boolean m_requestHalt;
		public boolean m_stepResume;
		/* Indicates whether the prompt for setting initial breakpoints has been displayed for this isolate */
		public InitialPromptState m_promptState;
//		public Vector<BreakAction>	        m_breakpoints;
//		public Vector<WatchAction>			m_watchpoints;
//		public Vector<CatchAction>			m_catchpoints;
//		public ArrayList<DisplayAction>	m_displays;


		public DebugCLIIsolateState(DebugCLI debugcli) {
//			m_exprCache = new ExpressionCache(debugcli);
			m_faultTable = faultActionsBuilder.build();//new FaultActions();
//			m_breakpoints = new Vector<BreakAction>();
//			m_watchpoints = new Vector<WatchAction>();
//			m_catchpoints = new Vector<CatchAction>();
//			m_displays = new ArrayList<DisplayAction>();
		}
	}

	private DebugCLIIsolateState getIsolateState(int isolateId) {
		if (isolateId == Isolate.DEFAULT_ID)
			return m_mainState;
		DebugCLIIsolateState isolateState = null;
		if (!m_isolateState.containsKey(isolateId)) {
			isolateState = new DebugCLIIsolateState(this);
			m_isolateState.put(isolateId, isolateState);
		} else
			isolateState = m_isolateState.get(isolateId);
		return isolateState;
	}

	public int getActiveIsolateId() {
		return m_activeIsolate;
	}

	private boolean getRequestResume(int isolateId) {
		return getIsolateState(isolateId).m_requestResume;
	}

	private void setRequestResume(boolean value, int isolateId) {
		getIsolateState(isolateId).m_requestResume = value;
	}

	private boolean getStepResume(int isolateId) {
		return getIsolateState(isolateId).m_stepResume;
	}

	private void setStepResume(boolean value, int isolateId) {
		getIsolateState(isolateId).m_stepResume = value;
	}

	private boolean getRequestHalt(int isolateId) {
		return getIsolateState(isolateId).m_requestHalt;
	}

	private void setRequestHalt(boolean value, int isolateId) {
		getIsolateState(isolateId).m_requestHalt = value;
	}

	private InitialPromptState getPromptState(int isolateId) {
		return getIsolateState(isolateId).m_promptState;
	}

	private void setPromptState(InitialPromptState value, int isolateId) {
		getIsolateState(isolateId).m_promptState = value;
	}

	/* our current input processing context */
	LineNumberReader m_in;
	public LineNumberReader m_keyboardStream;
	Vector<String> m_keyboardInput;
	boolean m_keyboardReadRequest;
	StringTokenizer m_currentTokenizer;
	String m_currentToken;
	String m_currentLine;
	public String m_repeatLine;
	private boolean m_isIde;

	/**
	 * The module that the next "list" command should display if no
	 * module is explicitly specified.
	 */
	public static final String LIST_MODULE = "$listmodule"; //$NON-NLS-1$

	/**
	 * The line number at which the next "list" command should begin if no
	 * line number is explicitly specified.
	 */
	public static final String LIST_LINE = "$listline"; //$NON-NLS-1$

	public static final String LIST_WORKER = "$listworker"; //$NON-NLS-1$

	/**
	 * The number of lines displayed by the "list" command.
	 */
	private static final String LIST_SIZE = "$listsize"; //$NON-NLS-1$

	private static final String COLUMN_WIDTH = "$columnwidth"; //$NON-NLS-1$

	private static final String UPDATE_DELAY = "$updatedelay"; //$NON-NLS-1$

	private static final String HALT_TIMEOUT = "$halttimeout"; //$NON-NLS-1$

	/**
	 * Current breakpoint number.
	 */
	private static final String BPNUM = "$bpnum"; //$NON-NLS-1$

	/**
	 * Used to determine how much context information should be displayed.
	 */
	private static final String LAST_FRAME_DEPTH = "$lastframedepth"; //$NON-NLS-1$

	/**
	 * Used to determine how much context information should be displayed.
	 */
	private static final String CURRENT_FRAME_DEPTH = "$currentframedepth"; //$NON-NLS-1$

	/**
	 * The current frame we are viewing -- controlled by the "up", "down", and "frame" commands.
	 */
	public static final String DISPLAY_FRAME_NUMBER = "$displayframenumber"; //$NON-NLS-1$

	private static final String FILE_LIST_WRAP = "$filelistwrap"; //$NON-NLS-1$

	private static final String NO_WAITING = "$nowaiting"; //$NON-NLS-1$

	/**
	 * Show this pointer for info stack.
	 */
	private static final String INFO_STACK_SHOW_THIS = "$infostackshowthis"; //$NON-NLS-1$

	private static final String PLAYER_FULL_SUPPORT = "$playerfullsupport"; //$NON-NLS-1$

	/**
	 * Whether the "print" command will display attributes of members.
	 */
	public static final String DISPLAY_ATTRIBUTES = "$displayattributes"; //$NON-NLS-1$

	/* class's static init */
	static {
		// set up for localizing messages
		m_localizationManager.addLocalizer(new DebuggerLocalizer("flex.tools.debugger.cli.fdb.")); //$NON-NLS-1$
	}

	public static void main(String[] args) {
		DebugCLI cli = new DebugCLI();

		/* attach our 'main' input method and out/err*/
		cli.m_err = System.err;
		cli.m_out = System.out;

		// get the default <application.home>/projects/frameworks/*/src entries into the source path
		cli.initSourceDirectoriesList();

		// a big of wrangling for our keyboard input stream since its special
		cli.m_keyboardStream = new LineNumberReader(new InputStreamReader(System.in));
		cli.pushStream(cli.m_keyboardStream);

		/* iterate through the args list */
		cli.processArgs(args);

		/* figure out $HOME and the current directory */
		String userHome = System.getProperty("user.home"); //$NON-NLS-1$
		String userDir = System.getProperty("user.dir"); //$NON-NLS-1$

		/*
		 * If the current directory is not $HOME, and a .fdbinit file exists in the current directory,
		 * then push it onto the stack of files to read.
		 *
		 * Note, we want ./.fdbinit to be read AFTER $HOME/.fdbinit, but we push them in reverse
		 * order, because they're going onto a stack.  If we push them in reverse order, then they
		 * will be read in the correct order (last one pushed is the first one read).
		 */
		if (userDir != null && !userDir.equals(userHome)) {
			try {
				FileReader sr = new FileReader(new File(userDir, ".fdbinit")); //$NON-NLS-1$
				cli.pushStream(new LineNumberReader(sr));
			}
			catch(FileNotFoundException fnf) {}
		}

		/*
		 * If a .fdbinit file exists in the $HOME directory, then push it onto the stack of files
		 * to read.
		 *
		 * Note, we want ./.fdbinit to be read AFTER $HOME/.fdbinit, but we push them in reverse
		 * order, because they're going onto a stack.  If we push them in reverse order, then they
		 * will be read in the correct order (last one pushed is the first one read).
		 */
		if (userHome != null) {
			try {
				FileReader sr = new FileReader(new File(userHome, ".fdbinit")); //$NON-NLS-1$
				cli.pushStream(new LineNumberReader(sr));
			}
			catch(FileNotFoundException fnf) {}
		}

		cli.execute();
	}

	public DebugCLI() {
		m_fullnameOption = false;
		m_faultTable = faultActionsBuilder.build();
		m_exprCache = new ExpressionCache(this);
		m_breakpoints = new Vector<BreakAction>();
		m_watchpoints = new Vector<WatchAction>();
		m_catchpoints = new Vector<CatchAction>();
		m_displays = new ArrayList<DisplayAction>();
		m_keyboardInput = new Vector<String>();
		m_mruURI = null;
		m_sourceDirectories = new LinkedList<String>();

		initProperties();
		m_mainState = new DebugCLIIsolateState(this);
		m_lastPromptIsolate = -1;
		initIsolateState();
	}

	public static LocalizationManager getLocalizationManager() {
		return m_localizationManager;
	}

	public Session getSession() {
		return m_session;
	}

	public FileInfoCache getFileCache() {
		return m_fileInfo;
	}

	public boolean isIde() {
		return m_isIde;
	}

	/**
	 * Convert a module to class name.  This is used
	 * by the ExpressionCache to find variables
	 * that live at royale package scope.   That
	 * is variables such as mx.core.Component.
	 */
	public String module2ClassName(int moduleId) {
		String pkg = null;
		try {
			SourceFile file = m_fileInfo.getFile(moduleId);
			pkg = file.getPackageName();
		} catch (Exception npe) {
			// didn't work ignore it.
		}
		return pkg;
	}

	LineNumberReader	popStream()						{ return m_readerStack.pop(); }
	public void				pushStream(LineNumberReader r)  { m_readerStack.push(r); }
	boolean				haveStreams()					{ return !m_readerStack.empty(); }

	public void processArgs(String[] args) {
		for (int i = 0; i < args.length; i++) {
			String arg = args[i];
//			System.out.println("arg["+i+"]= '"+arg+"'");
			if (arg.charAt(0) == '-') {
				// its an option
				if (arg.equals("-unit")) // unit-testing mode //$NON-NLS-1$
				{
					System.setProperty("fdbunit", ""); //$NON-NLS-1$ //$NON-NLS-2$
				} else if (arg.equals("-fullname") || arg.equals("-f")) //$NON-NLS-1$ //$NON-NLS-2$
				{
					m_fullnameOption = true; // emacs mode
				} else if (arg.equals("-cd")) //$NON-NLS-1$
				{
					// consume the path
					if (i + 1 < args.length)
						m_cdPath = args[i++];
				} else if (arg.equals("-p")) //$NON-NLS-1$
				{
					// consume the port
					if (i + 1 < args.length)
						m_connectPort = args[++i];
				} else if (arg.equals("-ide")) //$NON-NLS-1$
				{
					m_isIde = true;
				} else if (arg.equals("-lang")) //$NON-NLS-1$
				{
					if (i + 1 < args.length)
						getLocalizationManager().setLocale(LocaleUtils.toLocale(args[++i]));

				} else {
					err("Unknown command-line argument: " + arg); //$NON-NLS-1$
				}
			} else {
				// its a URI to run
				StringReader sr = new StringReader("run " + arg + m_newline); //$NON-NLS-1$
				pushStream(new LineNumberReader(sr));
			}
		}
	}

	/**
	 * Dispose of the current line and read the next from the current stream, if its an empty
	 * line and we are console then repeat last line.
	 */
	protected String readLine() throws IOException {
		String line = null;
		if (haveStreams())
			line = m_in.readLine();
		else
			line = keyboardReadLine();

		setCurrentLine(line);
		return line;
	}

	/**
	 * The reader portion of our keyboard input routine
	 * Block until input arrives.
	 */
	synchronized String keyboardReadLine() {
		// enable a request then block on the queue
		m_keyboardReadRequest = true;
		try {
			wait();
		} catch (InterruptedException ie) {
		}

		// pull from the front of the queue
		return m_keyboardInput.remove(0);
	}

	/**
	 * A seperate thread collects our input so that we can
	 * block in the doContinue on the main thread and then
	 * allow the user to interrupt us via keyboard input
	 * on this thread.
	 *
	 * We built the stupid thing in this manner, since readLine()
	 * will block no matter what and if we 'quit' we can't
	 * seem to kill this thread.  .close() doesn't work
	 * and Thread.stop(), etc. all fail to do the job.
	 *
	 * Thus we needed to take a request response approach
	 * so that we only block when requested to do so.
	 */
	public void run() {
		// while we have this stream
		while (m_keyboardStream != null) {
			try {
				// only if someone is requesting us to read do we do so...
				if (m_keyboardReadRequest) {
					// block on keyboard input and put it onto the end of the queue
					String s = m_keyboardStream.readLine();
					m_keyboardInput.add(s);

					// fullfilled request, now notify blocking thread.
					m_keyboardReadRequest = false;
					synchronized(this) { notifyAll(); }
					}
				else
					try { Thread.sleep(50); } catch(InterruptedException ie) {}
					}
			catch(IOException io)
			{
//				io.printStackTrace();
			}
		}
	}

	public void setCurrentLine(String s) {
		m_currentLine = s;
		if (m_currentLine == null)
			m_currentTokenizer = null;   /* eof */
		else {
			m_currentLine = m_currentLine.trim();

			/* if nothing provided on this command then pull our 'repeat' command  */
			if (m_repeatLine != null && !haveStreams() && m_currentLine.length() == 0)
				m_currentLine = m_repeatLine;

			m_currentTokenizer = new StringTokenizer(m_currentLine, " \n\r\t"); //$NON-NLS-1$
		}
	}

	/* Helpers for extracting tokens from the current line */
	public boolean		hasMoreTokens()									{ return m_currentTokenizer.hasMoreTokens(); }
	public String		nextToken()										{ m_currentToken = m_currentTokenizer.nextToken(); return m_currentToken; }
	public int			nextIntToken() throws NumberFormatException		{ nextToken(); return Integer.parseInt(m_currentToken);	}
	public long			nextLongToken() throws NumberFormatException	{ nextToken(); return Long.parseLong(m_currentToken);	}
	public String		restOfLine()									{ return m_currentTokenizer.nextToken("").trim(); } //$NON-NLS-1$

	public void execute() {
		/* dump console message */
		displayStartMessage();

		/* now fire our keyboard input thread */
		Thread t = new Thread(this, "Keyboard input"); //$NON-NLS-1$
		t.start();

		/* keep processing streams until we have no more to do */
		while (haveStreams()) {
			try {
				m_in = popStream();
				process();
			} catch (EOFException eof) {
				; /* quite allright */
			} catch (IOException io) {
				Map<String, Object> args = new HashMap<String, Object>();
				args.put("exceptionMessage", io); //$NON-NLS-1$
				err(getLocalizationManager().getLocalizedTextString("errorWhileProcessingFile", args)); //$NON-NLS-1$
			}
		}

		/* we done kill everything */
		exitSession();

		// clear this thing, which also halts our other thread.
		m_keyboardStream = null;
	}

	public PrintStream getOut() {
		return m_out;
	}

	private void displayStartMessage() {
		String build = getLocalizationManager().getLocalizedTextString("defaultBuildName"); //$NON-NLS-1$

		try {
			Properties p = new Properties();
			p.load(this.getClass().getResourceAsStream("version.properties")); //$NON-NLS-1$
			String buildString = p.getProperty("build"); //$NON-NLS-1$
			if ((buildString != null) && (!buildString.equals(""))) //$NON-NLS-1$
			{
				build = buildString;
			}
		} catch (Exception t) {
			// ignore
		}

		Map<String, Object> aboutMap = new HashMap<String, Object>();
		aboutMap.put("build", build); //$NON-NLS-1$
		out(getLocalizationManager().getLocalizedTextString("about", aboutMap)); //$NON-NLS-1$
		out(getLocalizationManager().getLocalizedTextString("copyright")); //$NON-NLS-1$
	}

	void displayPrompt() {
		m_out.print("(fdb) "); //$NON-NLS-1$
	}

	void displayCommandPrompt() {
		m_out.print(">"); //$NON-NLS-1$
	}

	// add the given character n times to sb
	void repeat(StringBuilder sb, char c, int n) {
		while (n-- > 0)
			sb.append(c);
	}

	// Prompt the user to respond to a yes or no type question
	boolean yesNoQuery(String prompt) throws IOException {
		boolean result = false;
		m_out.print(prompt);
		m_out.print(getLocalizationManager().getLocalizedTextString("yesOrNoAppendedToAllQuestions")); //$NON-NLS-1$

		String in = readLine();
		if (in != null && in.equals(getLocalizationManager().getLocalizedTextString("singleCharacterUserTypesForYes"))) //$NON-NLS-1$
			result = true;
		else if (in != null && in.equals("escape")) //$NON-NLS-1$
			throw new IllegalArgumentException("escape"); //$NON-NLS-1$
		else
			out(getLocalizationManager().getLocalizedTextString("yesNoQueryNotConfirmed")); //$NON-NLS-1$
		return result;
	}

	public void err(String s) {
		// Doesn't make sense to send messages to stderr, because this is
		// an interactive application; and besides that, sending a combination
		// of interwoven but related messages to both stdout and stderr causes
		// the output to be in the wrong order sometimes.
		out(s);
	}

	public void out(String s) {
		if (s.length() > 0 && (s.charAt(s.length() - 1) == '\n'))
			m_out.print(s);
		else
			m_out.println(s);
	}

	static String uft() {
		Runtime rt = Runtime.getRuntime();
		long free = rt.freeMemory(), total = rt.totalMemory(), used = total - free;
//		long max = rt.maxMemory();
		java.text.NumberFormat nf = java.text.NumberFormat.getInstance();
//        System.out.println("used: "+nf.format(used)+" free: "+nf.format(free)+" total: "+nf.format(total)+" max: "+nf.format(max));
		return "Used " + nf.format(used) + " - free " + nf.format(free) + " - total " + nf.format(total); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
	}

	/**
	 * Add all properties that we know about
	 */
	void initProperties() {
		propertyPut(LIST_SIZE, 10);
		propertyPut(LIST_LINE, 1);
		propertyPut(LIST_MODULE, 1);  // default to module #1
		propertyPut(LIST_WORKER, Isolate.DEFAULT_ID);
		propertyPut(COLUMN_WIDTH, 70);
		propertyPut(UPDATE_DELAY, 25);
		propertyPut(HALT_TIMEOUT, 7000);
		propertyPut(BPNUM, 0);                // set current breakpoint number as something bad
		propertyPut(LAST_FRAME_DEPTH, 0);        // used to determine how much context information should be displayed
		propertyPut(CURRENT_FRAME_DEPTH, 0);   // used to determine how much context information should be displayed
		propertyPut(DISPLAY_FRAME_NUMBER, 0);  // houses the current frame we are viewing
		propertyPut(FILE_LIST_WRAP, 999999);   // default 1 file name per line
		propertyPut(NO_WAITING, 0);
		propertyPut(INFO_STACK_SHOW_THIS, 1); // show this pointer for info stack
	}

	// getter/setter for properties; in the expression cache, so that they can be used in expressions!
	public void propertyPut(String k, int v) {
		m_exprCache.put(k, v);
	}

	public int propertyGet(String k) {
		return ((Integer) m_exprCache.get(k)).intValue();
	}

	public Set<String> propertyKeys() {
		return m_exprCache.keySet();
	}

	/**
	 * Process this reader until its done
	 */
	void process() throws IOException {
		boolean done = false;
		while (!done) {
			try {
				/**
				 * Now if we are in a session and that session is suspended then we go
				 * into a state where we wait for some user interaction to get us out
				 */
				runningLoop();

				/* if we are in the stdin then put out a prompt */
				if (!haveStreams())
					displayPrompt();

				/* now read in the next line */
				readLine();
				if (m_currentLine == null)
					break;

				done = processLine();
			} catch (NoResponseException nre) {
				err(getLocalizationManager().getLocalizedTextString("noResponseException")); //$NON-NLS-1$
			} catch (NotSuspendedException nse) {
				err(getLocalizationManager().getLocalizedTextString("notSuspendedException")); //$NON-NLS-1$
			} catch (AmbiguousException ae) {
				// we already put up a warning for the user
			} catch (IllegalStateException ise) {
				err(getLocalizationManager().getLocalizedTextString("illegalStateException")); //$NON-NLS-1$
			} catch (IllegalMonitorStateException ime) {
				err(getLocalizationManager().getLocalizedTextString("illegalMonitorStateException")); //$NON-NLS-1$
			} catch (NoSuchElementException nse) {
				err(getLocalizationManager().getLocalizedTextString("noSuchElementException")); //$NON-NLS-1$
			} catch (NumberFormatException nfe) {
				err(getLocalizationManager().getLocalizedTextString("numberFormatException")); //$NON-NLS-1$
			} catch (SocketException se) {
				Map<String, Object> socketArgs = new HashMap<String, Object>();
				socketArgs.put("message", se.getMessage()); //$NON-NLS-1$
				err(getLocalizationManager().getLocalizedTextString("socketException", socketArgs)); //$NON-NLS-1$
			} catch (VersionException ve) {
				err(getLocalizationManager().getLocalizedTextString("versionException")); //$NON-NLS-1$
			} catch (NotConnectedException nce) {
				// handled by isConnectionLost()
			} catch (Exception e) {
				err(getLocalizationManager().getLocalizedTextString("unexpectedError")); //$NON-NLS-1$
				err(getLocalizationManager().getLocalizedTextString("stackTraceFollows")); //$NON-NLS-1$
				e.printStackTrace();
			}

			// check for a lost connection and if it is clean-up!
			if (isConnectionLost()) {
				try {
					dumpHaltState(false);
				} catch (PlayerDebugException pde) {
					err(getLocalizationManager().getLocalizedTextString("sessionEndedAbruptly")); //$NON-NLS-1$
				}
			}
		}
	}

	// check if we have lost the connect without our help...
	boolean isConnectionLost() {
		boolean lost = false;

		if (m_session != null && !m_session.isConnected())
			lost = true;

		return lost;
	}

	boolean haveConnection() {
		boolean have = false;

		if (m_session != null && m_session.isConnected())
			have = true;

		return have;
	}

	void doShow() throws AmbiguousException, PlayerDebugException {
		/* show without any args brings up help */
		if (!hasMoreTokens())
			out(getHelpTopic("show")); //$NON-NLS-1$
		else {
			/* otherwise we have a boatload of options */
			String subCmdString = nextToken();
			int subCmd = showCommandFor(subCmdString);
			switch (subCmd) {
				case SHOW_NET_CMD:
					doShowStats();
					break;

				case SHOW_FUNC_CMD:
					doShowFuncs();
					break;

				case SHOW_URI_CMD:
					doShowUri();
					break;

				case SHOW_PROPERTIES_CMD:
					doShowProperties();
					break;

				case SHOW_FILES_CMD:
					doShowFiles();
					break;

				case SHOW_BREAK_CMD:
					doShowBreak();
					break;

				case SHOW_VAR_CMD:
					doShowVariable();
					break;

				case SHOW_MEM_CMD:
					doShowMemory();
					break;

				case SHOW_LOC_CMD:
					doShowLocations();
					break;

				case SHOW_DIRS_CMD:
					doShowDirectories();
					break;

				default:
					doUnknown("show", subCmdString); //$NON-NLS-1$
					break;
			}
		}
	}

	void doShowUri() {
		// dump the URI that the player has sent us
		try {
			StringBuilder sb = new StringBuilder();
			sb.append("URI = "); //$NON-NLS-1$
			sb.append(m_session.getURI());
			out(sb.toString());
		} catch (Exception e) {
			err(getLocalizationManager().getLocalizedTextString("noUriReceived")); //$NON-NLS-1$
		}
	}

	/**
	 * Dump the content of files in a raw format
	 */
	void doShowFiles() {
		try {
			StringBuilder sb = new StringBuilder();
			for (Isolate isolate : m_session.getWorkers()) {

				Iterator itr = m_fileInfo.getAllFiles(isolate.getId());

				while (itr.hasNext()) {
					SourceFile m = (SourceFile) ((Map.Entry) itr.next()).getValue();

					String name = m.getName();
					int id = m.getId();
					String path = m.getFullPath();

					sb.append(id);
					sb.append(' ');
					sb.append(path);
					sb.append(", "); //$NON-NLS-1$
					sb.append(name);
					sb.append(" ("); //$NON-NLS-1$
					if (isolate.getId() == Isolate.DEFAULT_ID) {
						sb.append(getLocalizationManager().getLocalizedTextString("mainThread")); //$NON-NLS-1$
					} else {
						HashMap<String, Object> wArgs = new HashMap<String, Object>();
						wArgs.put("worker", isolate.getId() - 1); //$NON-NLS-1$
						sb.append(getLocalizationManager().getLocalizedTextString("inWorker", wArgs)); //$NON-NLS-1$
					}
					sb.append(")"); //$NON-NLS-1$
					sb.append(m_newline);
				}
			}
			out(sb.toString());
		} catch (NullPointerException npe) {
			err(getLocalizationManager().getLocalizedTextString("noSourceFilesFound")); //$NON-NLS-1$
		}
	}

	void doShowMemory() {
		out(uft());
	}

	void doShowLocations() {
		StringBuilder sb = new StringBuilder();
		sb.append("Num Type           Disp Enb Address    What" + m_newline); //$NON-NLS-1$

		// our list of breakpoints
		int count = breakpointCount();
		for (int i = 0; i < count; i++) {
			BreakAction b = breakpointAt(i);
			int num = b.getId();

			FieldFormat.formatLong(sb, num, 3);
			sb.append(" breakpoint     "); //$NON-NLS-1$

			if (b.isAutoDisable())
				sb.append("dis  "); //$NON-NLS-1$
			else if (b.isAutoDelete())
				sb.append("del  "); //$NON-NLS-1$
			else
				sb.append("keep "); //$NON-NLS-1$

			if (b.isEnabled())
				sb.append("y   "); //$NON-NLS-1$
			else
				sb.append("n   "); //$NON-NLS-1$

			Iterator<Location> itr = b.getLocations().iterator();
			while (itr.hasNext()) {
				Location l = itr.next();
				SourceFile file = l.getFile();
				String funcName = (file == null)
						? getLocalizationManager().getLocalizedTextString("unknownBreakpointLocation") //$NON-NLS-1$
						: file.getFunctionNameForLine(m_session, l.getLine());
				int offset = adjustOffsetForUnitTests((file == null) ? 0 : file.getOffsetForLine(l.getLine()));

				sb.append("0x"); //$NON-NLS-1$
				FieldFormat.formatLongToHex(sb, offset, 8);
				sb.append(' ');

				if (funcName != null) {
					Map<String, Object> funcArgs = new HashMap<String, Object>();
					funcArgs.put("functionName", funcName); //$NON-NLS-1$
					sb.append(getLocalizationManager().getLocalizedTextString("inFunctionAt", funcArgs)); //$NON-NLS-1$
				}

				sb.append(file.getName());
				sb.append("#"); //$NON-NLS-1$
				sb.append(file.getId());
				sb.append(':');
				sb.append(l.getLine());

				try {
					SwfInfo info = m_fileInfo.swfForFile(file, l.getIsolateId());
					Map<String, Object> swfArgs = new HashMap<String, Object>();
					swfArgs.put("swf", FileInfoCache.shortNameOfSwf(info)); //$NON-NLS-1$
					sb.append(getLocalizationManager().getLocalizedTextString("inSwf", swfArgs)); //$NON-NLS-1$
					if (l.getIsolateId() == Isolate.DEFAULT_ID) {
						sb.append(" ("); //$NON-NLS-1$
						sb.append(getLocalizationManager().getLocalizedTextString("mainThread")); //$NON-NLS-1$
						sb.append(")"); //$NON-NLS-1$
					} else {
						swfArgs = new HashMap<String, Object>();
						swfArgs.put("worker", l.getIsolateId() - 1); //$NON-NLS-1$
						sb.append(" ("); //$NON-NLS-1$
						sb.append(getLocalizationManager().getLocalizedTextString("inWorker", swfArgs)); //$NON-NLS-1$
						sb.append(")"); //$NON-NLS-1$
					}
				} catch (NullPointerException npe) {
					// can't find the swf
					sb.append(getLocalizationManager().getLocalizedTextString("nonRestorable")); //$NON-NLS-1$
				}
				sb.append(m_newline);
				if (itr.hasNext())
					sb.append("                            "); //$NON-NLS-1$
			}
		}
		out(sb.toString());
	}

	/**
	 * When running unit tests, we want byte offsets into the file to
	 * always be displayed as zero, so that the unit test expected
	 * results will match up with the actual results.  This is just a
	 * simple helper function that deals with that.
	 */
	private int adjustOffsetForUnitTests(int offset) {
		if (System.getProperty("fdbunit") == null) //$NON-NLS-1$
			return offset;
		else
			return 0;
	}

	void doShowDirectories() {
		out(getLocalizationManager().getLocalizedTextString("sourceDirectoriesSearched")); //$NON-NLS-1$
		Iterator<String> iter = m_sourceDirectories.iterator();
		while (iter.hasNext()) {
			String dir = iter.next();
			out("  " + dir); //$NON-NLS-1$
		}
	}

	void doHalt() throws SuspendedException, NotConnectedException, NoResponseException {
		out(getLocalizationManager().getLocalizedTextString("attemptingToSuspend")); //$NON-NLS-1$
		IsolateSession session = m_session.getWorkerSession(getActiveIsolateId());
		if (!session.isSuspended())
			session.suspend();
		if (session.isSuspended())
			out(getLocalizationManager().getLocalizedTextString("playerStopped")); //$NON-NLS-1$
		else
			out(getLocalizationManager().getLocalizedTextString("playerRunning")); //$NON-NLS-1$
	}

	public void appendReason(StringBuilder sb, int reason) {
		switch (reason) {
			case SuspendReason.Unknown:
				sb.append(getLocalizationManager().getLocalizedTextString("suspendReason_Unknown")); //$NON-NLS-1$
				break;

			case SuspendReason.Breakpoint:
				sb.append(getLocalizationManager().getLocalizedTextString("suspendReason_HitBreakpoint")); //$NON-NLS-1$
				break;

			case SuspendReason.Watch:
				sb.append(getLocalizationManager().getLocalizedTextString("suspendReason_HitWatchpoint")); //$NON-NLS-1$
				break;

			case SuspendReason.Fault:
				sb.append(getLocalizationManager().getLocalizedTextString("suspendReason_ProgramThrewException")); //$NON-NLS-1$
				break;

			case SuspendReason.StopRequest:
				sb.append(getLocalizationManager().getLocalizedTextString("suspendReason_StopRequest")); //$NON-NLS-1$
				break;

			case SuspendReason.Step:
				sb.append(getLocalizationManager().getLocalizedTextString("suspendReason_ProgramFinishedStepping")); //$NON-NLS-1$
				break;

			case SuspendReason.HaltOpcode:
				sb.append(getLocalizationManager().getLocalizedTextString("suspendReason_HaltOpcode")); //$NON-NLS-1$
				break;

			case SuspendReason.ScriptLoaded:
				sb.append(getLocalizationManager().getLocalizedTextString("suspendReason_ScriptHasLoadedIntoFlashPlayer")); //$NON-NLS-1$
				break;
		}
	}

	/**
	 * The big ticket item, where all your questions are answered.
	 *
	 */
	void doInfo() throws AmbiguousException, PlayerDebugException {
		/* info without any args brings up help */
		if (!hasMoreTokens())
			out(getHelpTopic("info")); //$NON-NLS-1$
		else {
			/* otherwise we have a boatload of options */
			String subCmdString = nextToken();
			int subCmd = infoCommandFor(subCmdString);
			switch (subCmd) {
				case INFO_ARGS_CMD:
					doInfoArgs();
					break;

				case INFO_BREAK_CMD:
					doInfoBreak();
					break;

				case INFO_FILES_CMD:
					doInfoFiles();
					break;

				case INFO_FUNCTIONS_CMD:
					doInfoFuncs();
					break;

				case INFO_HANDLE_CMD:
					doInfoHandle();
					break;

				case INFO_LOCALS_CMD:
					doInfoLocals();
					break;

				case INFO_SCOPECHAIN_CMD:
					doInfoScopeChain();
					break;

				case INFO_SOURCES_CMD:
					doInfoSources();
					break;

				case INFO_STACK_CMD:
					doInfoStack();
					break;

				case INFO_VARIABLES_CMD:
					doInfoVariables();
					break;

				case INFO_DISPLAY_CMD:
					doInfoDisplay();
					break;

				case INFO_TARGETS_CMD:
					doInfoTargets();
					break;

				case INFO_SWFS_CMD:
					doInfoSwfs();
					break;

				case INFO_WORKERS_CMD:
					doInfoWorkers();
					break;

				default:
					doUnknown("info", subCmdString); //$NON-NLS-1$
					break;
			}
		}
	}

	void doInfoWorkers() throws NotConnectedException, NotSupportedException, NotSuspendedException, NoResponseException {
//		waitTilHalted();
		Isolate[] isolates = m_session.getWorkers();
		if (isolates == null || isolates.length == 0) {
			out(getLocalizationManager().getLocalizedTextString("noWorkersRunning")); //$NON-NLS-1$
			return;
		}
		StringBuilder sb = new StringBuilder();
		for (Isolate t : isolates) {
			String status = getLocalizationManager().getLocalizedTextString("workerRunning"); //$NON-NLS-1$
			if (m_session.getWorkerSession(t.getId()).isSuspended()) {
				status = getLocalizationManager().getLocalizedTextString("workerSuspended"); //$NON-NLS-1$
			}
			if (m_activeIsolate == t.getId()) {
				status += " " + getLocalizationManager().getLocalizedTextString("workerSelected"); //$NON-NLS-1$ //$NON-NLS-2$
			}
			if (t.getId() == Isolate.DEFAULT_ID) {
				sb.append(getLocalizationManager().getLocalizedTextString("mainThread")); //$NON-NLS-1$
				sb.append(" "); //$NON-NLS-1$
				sb.append(Isolate.DEFAULT_ID - 1);
			} else {
				HashMap<String, Object> workArgs = new HashMap<String, Object>();
				workArgs.put("worker", (t.getId() - 1)); //$NON-NLS-1$
				sb.append(getLocalizationManager().getLocalizedTextString("inWorker", workArgs)); //$NON-NLS-1$
			}
			sb.append(" - " + status + m_newline); //$NON-NLS-1$
		}
		out(sb.toString());
	}


	void doInfoStack() throws PlayerDebugException {
		waitTilHalted(m_activeIsolate);

		StringBuilder sb = new StringBuilder();
		Frame[] stack = m_session.getWorkerSession(m_activeIsolate).getFrames();
		if (stack == null || stack.length == 0)
			sb.append(getLocalizationManager().getLocalizedTextString("noStackAvailable")); //$NON-NLS-1$
		else {
			boolean showThis = propertyGet(INFO_STACK_SHOW_THIS) == 1;
			for (int i = 0; i < stack.length; i++) {
				// keep spitting out frames until we can't
				Frame frame = stack[i];
				boolean valid = appendFrameInfo(sb, frame, i, showThis, true);
				sb.append(m_newline);
				if (!valid)
					break;
			}
		}

		/* dump it out */
		out(sb.toString());
	}

	/**
	 * Spit out frame information for a given frame number
	 */
	boolean appendFrameInfo(StringBuilder sb, Frame ctx, int frameNumber, boolean showThis, boolean showFileId) throws PlayerDebugException {
		boolean validFrame = true;

		// some formatting properties
		int i = frameNumber;

		Location loc = ctx.getLocation();
		SourceFile file = loc.getFile();
		int line = loc.getLine();
		String name = (file == null) ? "<null>" : file.getName(); //$NON-NLS-1$
		String sig = ctx.getCallSignature();
		String func = extractFunctionName(sig);

		// file == null or line < 0 appears to be a terminator for stack info
		if (file == null && line < 0) {
			validFrame = false;
		} else {
			Variable[] var = ctx.getArguments(m_session);
			Variable dis = ctx.getThis(m_session);
			boolean displayArgs = (func != null) || (var != null);

			sb.append('#');
			FieldFormat.formatLong(sb, i, 3);
			sb.append(' ');

			if (showThis && dis != null) {
				m_exprCache.appendVariable(sb, dis, ctx.getIsolateId());
				sb.append("."); //$NON-NLS-1$
			}

			if (func != null)
				sb.append(func);

			if (displayArgs) {
				sb.append('(');
				for (int j = 0; j < var.length; j++) {
					Variable v = var[j];
					sb.append(v.getName());
					sb.append('=');
					m_exprCache.appendVariableValue(sb, v.getValue(), ctx.getIsolateId());
					if ((j + 1) < var.length)
						sb.append(", "); //$NON-NLS-1$
				}
				sb.append(")"); //$NON-NLS-1$
				sb.append(getLocalizationManager().getLocalizedTextString("atFilename")); //$NON-NLS-1$
			}

			sb.append(name);

			// if this file is currently being filtered put the source file id after it
			if (file != null && (showFileId || !m_fileInfo.inFileList(file))) {
				sb.append('#');
				sb.append(file.getId());

			}
			sb.append(':');
			sb.append(line);
		}
		return validFrame;
	}

	/** extract the function name from a signature */
	public static String extractFunctionName(String sig) {
		// strip everything after the leading (
		int at = sig.indexOf('(');
		if (at > -1)
			sig = sig.substring(0, at);

		// trim the leading [object_name::] since it doesn't seem to add much
		if (sig != null && (at = sig.indexOf("::")) > -1) //$NON-NLS-1$
			sig = sig.substring(at + 2);

		return sig;
	}

	void doInfoVariables() throws PlayerDebugException {
		waitTilHalted(m_activeIsolate);

		// dump a set of locals
		StringBuilder sb = new StringBuilder();

		// use our expression cache formatting routine
		try {
			Variable[] vars = m_session.getWorkerSession(m_activeIsolate).getVariableList();
			for (int i = 0; i < vars.length; i++) {
				Variable v = vars[i];

				// all non-local and non-arg variables
				if (!v.isAttributeSet(VariableAttribute.IS_LOCAL) &&
						!v.isAttributeSet(VariableAttribute.IS_ARGUMENT)) {
					m_exprCache.appendVariable(sb, vars[i], m_activeIsolate);
					sb.append(m_newline);
				}
			}
		} catch (NullPointerException npe) {
			sb.append(getLocalizationManager().getLocalizedTextString("noVariables")); //$NON-NLS-1$
		}

		out(sb.toString());
	}

	void doInfoDisplay() {
		StringBuilder sb = new StringBuilder();
		sb.append("Num Enb Expression" + m_newline); //$NON-NLS-1$

		// our list of displays
		int count = displayCount();
		for (int i = 0; i < count; i++) {
			DisplayAction b = displayAt(i);
			int num = b.getId();
			String exp = b.getContent();

			sb.append(':');
			FieldFormat.formatLong(sb, num, 3);

			if (b.isEnabled())
				sb.append(" y  "); //$NON-NLS-1$
			else
				sb.append(" n  "); //$NON-NLS-1$

			sb.append(exp);
			sb.append(m_newline);
		}

		out(sb.toString());
	}

	void doInfoArgs() throws PlayerDebugException {
		waitTilHalted(m_activeIsolate);

		// dump a set of locals
		StringBuilder sb = new StringBuilder();

		// use our expression cache formatting routine
		try {
			int num = propertyGet(DISPLAY_FRAME_NUMBER);
			Frame[] frames = m_session.getWorkerSession(m_activeIsolate).getFrames();
			Variable[] vars = frames[num].getArguments(m_session);
			for (int i = 0; i < vars.length; i++) {
				m_exprCache.appendVariable(sb, vars[i], m_activeIsolate);
				sb.append(m_newline);
			}
		} catch (NullPointerException npe) {
			sb.append(getLocalizationManager().getLocalizedTextString("noArguments")); //$NON-NLS-1$
		} catch (ArrayIndexOutOfBoundsException aix) {
			sb.append(getLocalizationManager().getLocalizedTextString("notInValidFrame")); //$NON-NLS-1$
		}

		out(sb.toString());
	}

	void doInfoLocals() throws PlayerDebugException {
		waitTilHalted(m_activeIsolate);

		// dump a set of locals
		StringBuilder sb = new StringBuilder();

		// use our expression cache formatting routine
		try {
			// get the variables from the requested frame
			int num = propertyGet(DISPLAY_FRAME_NUMBER);
			Frame[] ar = m_session.getWorkerSession(m_activeIsolate).getFrames();
			Frame ctx = ar[num];
			Variable[] vars = ctx.getLocals(m_session);

			for (int i = 0; i < vars.length; i++) {
				Variable v = vars[i];

				// see if variable is local
				if (v.isAttributeSet(VariableAttribute.IS_LOCAL)) {
					m_exprCache.appendVariable(sb, v, m_activeIsolate);
					sb.append(m_newline);
				}
			}
		} catch (NullPointerException npe) {
			sb.append(getLocalizationManager().getLocalizedTextString("noLocals")); //$NON-NLS-1$
		} catch (ArrayIndexOutOfBoundsException aix) {
			sb.append(getLocalizationManager().getLocalizedTextString("notInValidFrame")); //$NON-NLS-1$
		}

		out(sb.toString());
	}

	void doInfoScopeChain() throws PlayerDebugException {
		waitTilHalted(m_activeIsolate);

		// dump the scope chain
		StringBuilder sb = new StringBuilder();

		// use our expression cache formatting routine
		try {
			// get the scope chainfrom the requested frame
			int num = propertyGet(DISPLAY_FRAME_NUMBER);
			Frame[] ar = m_session.getWorkerSession(m_activeIsolate).getFrames();
			Frame ctx = ar[num];
			Variable[] scopes = ctx.getScopeChain(m_session);

			for (int i = 0; i < scopes.length; i++) {
				Variable scope = scopes[i];
				m_exprCache.appendVariable(sb, scope, m_activeIsolate);
				sb.append(m_newline);
			}
		} catch (NullPointerException npe) {
			sb.append(getLocalizationManager().getLocalizedTextString("noScopeChain")); //$NON-NLS-1$
		} catch (ArrayIndexOutOfBoundsException aix) {
			sb.append(getLocalizationManager().getLocalizedTextString("notInValidFrame")); //$NON-NLS-1$
		}

		out(sb.toString());
	}

	void doInfoTargets() {
		if (!haveConnection()) {
			out(getLocalizationManager().getLocalizedTextString("noActiveSession")); //$NON-NLS-1$
			if (m_launchURI != null) {
				Map<String, Object> args = new HashMap<String, Object>();
				args.put("uri", m_launchURI); //$NON-NLS-1$
				out(getLocalizationManager().getLocalizedTextString("runWillLaunchUri", args)); //$NON-NLS-1$
			}
		} else {
			String uri = m_session.getURI();
			if (uri == null || uri.length() < 1)
				err(getLocalizationManager().getLocalizedTextString("targetUnknown")); //$NON-NLS-1$
			else
				out(uri);
		}
	}

	/**
	 * Dump some stats about our currently loaded swfs.
	 */
	void doInfoSwfs() {
		try {
			StringBuilder sb = new StringBuilder();
			SwfInfo[] swfs = m_fileInfo.getSwfs(m_activeIsolate);
			for (int i = 0; i < swfs.length; i++) {
				SwfInfo e = swfs[i];
				if (e == null || e.isUnloaded())
					continue;

				Map<String, Object> args = new HashMap<String, Object>();
				args.put("swfName", FileInfoCache.nameOfSwf(e)); //$NON-NLS-1$
				args.put("size", NumberFormat.getInstance().format(e.getSwfSize())); //$NON-NLS-1$

				try {
					int size = e.getSwdSize(m_session);

					// our swd is loaded so let's comb through our
					// list of scripts and locate the range of ids.
					SourceFile[] files = e.getSourceList(m_session);
					int max = Integer.MIN_VALUE;
					int min = Integer.MAX_VALUE;
					for (int j = 0; j < files.length; j++) {
						SourceFile f = files[j];
						int id = f.getId();
						max = (id > max) ? id : max;
						min = (id < min) ? id : min;
					}

					args.put("scriptCount", Integer.toString(e.getSourceCount(m_session))); //$NON-NLS-1$
					args.put("min", Integer.toString(min)); //$NON-NLS-1$
					args.put("max", Integer.toString(max)); //$NON-NLS-1$
					args.put("plus", (e.isProcessingComplete()) ? "+" : ""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
					args.put("moreInfo", (size == 0) ? getLocalizationManager().getLocalizedTextString("remainingSourceBeingLoaded") : ""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
				} catch (InProgressException ipe) {
					sb.append(getLocalizationManager().getLocalizedTextString("debugInfoBeingLoaded")); //$NON-NLS-1$
				}
				args.put("url", e.getUrl()); //$NON-NLS-1$
				sb.append(getLocalizationManager().getLocalizedTextString("swfInfo", args)); //$NON-NLS-1$
				sb.append(m_newline);
			}
			out(sb.toString());
		} catch (NullPointerException npe) {
			err(getLocalizationManager().getLocalizedTextString("noSWFs")); //$NON-NLS-1$
		}
	}

	private static final int AUTHORED_FILE = 1;        // a file that was created by the end user, e.g. MyApp.mxml
	private static final int FRAMEWORK_FILE = 2;    // a file from the Flex framework, e.g. mx.controls.Button.as, see FRAMEWORK_FILE_PACKAGES
	private static final int SYNTHETIC_FILE = 3;    // e.g. "<set up XML utilities.1>"
	private static final int ACTIONS_FILE = 4;        // e.g. "Actions for UIComponent: Frame 1 of Layer Name Layer 1"

	private static final String[] FRAMEWORK_FILE_PACKAGES // package prefixes that we consider FRAMEWORK_FILEs
			= new String[]{"mx", "flex", "text"}; // 'text' is Vellum (temporary) //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$

	/**
	 * Given a file, guesses what type it is -- e.g. a file created by the end user,
	 * or a file from the Flex framework, etc.
	 */
	private int getFileType(SourceFile sourceFile) {
		String name = sourceFile.getName();
		String pkg = sourceFile.getPackageName();

		if (name.startsWith("<") && name.endsWith(">") || name.equals("GeneratedLocale")) //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
			return SYNTHETIC_FILE;

		for (final String frameworkPkg : FRAMEWORK_FILE_PACKAGES) {
			// look for packages starting with pkgName
			if (pkg.startsWith(frameworkPkg + '\\') ||
					pkg.startsWith(frameworkPkg + '/') ||
					pkg.equals(frameworkPkg)) {
				return FRAMEWORK_FILE;
			}
		}

		if (name.startsWith("Actions for")) //$NON-NLS-1$
			return ACTIONS_FILE;

		return AUTHORED_FILE;
	}

	void buildFileList(StringBuilder sb, boolean authoredFilesOnly) {
		SourceFile[] ar = m_fileInfo.getFileList(m_activeIsolate);
		if (ar == null) {
			err(getLocalizationManager().getLocalizedTextString("noSourceFilesFound")); //$NON-NLS-1$
			return;
		}

		Vector<String> authoredFiles = new Vector<String>();
		Vector<String> frameworkFiles = new Vector<String>();
		Vector<String> syntheticFiles = new Vector<String>();
		Vector<String> actionsFiles = new Vector<String>();

		for (int i = 0; i < ar.length; i++) {
			SourceFile m = ar[i];
			int fileType = getFileType(m);
			int id = m.getId();
//			int fakeId = m_fileInfo.getFakeId(m);
			String entry = m.getName() + "#" + id; //$NON-NLS-1$

			switch (fileType) {
				case SYNTHETIC_FILE:
					syntheticFiles.add(entry);
					break;
				case FRAMEWORK_FILE:
					frameworkFiles.add(entry);
					break;
				case ACTIONS_FILE:
					actionsFiles.add(entry);
					break;
				case AUTHORED_FILE:
					authoredFiles.add(entry);
					break;
			}
		}

		int wrapAt = propertyGet(FILE_LIST_WRAP);

		if (!authoredFilesOnly) {
			if (actionsFiles.size() > 0) {
				appendStrings(sb, actionsFiles, (actionsFiles.size() > wrapAt));
			}

			if (frameworkFiles.size() > 0) {
				sb.append("---" + m_newline); //$NON-NLS-1$
				appendStrings(sb, frameworkFiles, (frameworkFiles.size() > wrapAt));
			}

			if (syntheticFiles.size() > 0) {
				sb.append("---" + m_newline); //$NON-NLS-1$
				appendStrings(sb, syntheticFiles, (syntheticFiles.size() > wrapAt));
			}

			sb.append("---" + m_newline); //$NON-NLS-1$
		}

		appendStrings(sb, authoredFiles, (authoredFiles.size() > wrapAt));
	}

	/**
	 * Dump a list of strings contained a vector
	 * If flow is set then the strings are placed
	 * on a single line and wrapped at $columnwidth
	 */
	void appendStrings(StringBuilder sb, Vector<String> v, boolean flow) {
		int count = v.size();
		int width = 0;
		int maxCol = propertyGet(COLUMN_WIDTH);

		for (int i = 0; i < count; i++) {
			String s = v.get(i);
			sb.append(s);

			// too many of them, then wrap according to columnwidth
			if (flow) {
				width += (s.length() + 2);
				if (width >= maxCol) {
					sb.append(m_newline);
					width = 0;
				} else
					sb.append(", "); //$NON-NLS-1$
			} else
				sb.append(m_newline);
		}

		// add a line feed for flow based
		if (flow && width > 0)
			sb.append(m_newline);
	}

	void doInfoFiles() {
		try {
			StringBuilder sb = new StringBuilder();
			if (hasMoreTokens()) {
				String arg = nextToken();
				listFilesMatching(sb, arg);
			} else {
				buildFileList(sb, false);
			}
			out(sb.toString());
		} catch (NullPointerException npe) {
			throw new IllegalStateException();
		}
	}

	void doInfoHandle() {
		if (hasMoreTokens()) {
			// user specified a fault
			String faultName = nextToken();

			// make sure we know about this one
			if (!m_faultTable.exists(faultName))
				err(getLocalizationManager().getLocalizedTextString("unrecognizedFault")); //$NON-NLS-1$
			else
				listFault(faultName);
		} else {
			// dump them all
			StringBuilder sb = new StringBuilder();

			appendFaultTitles(sb);

			Object names[] = m_faultTable.names();
			Arrays.sort(names);

			for (int i = 0; i < names.length; i++)
				appendFault(sb, (String) names[i]);

			out(sb.toString());
		}
	}

	void doInfoFuncs() {
		StringBuilder sb = new StringBuilder();

		String arg = null;

		// we take an optional single arg which specifies a module
		try {
			if (hasMoreTokens()) {
				arg = nextToken();
				int id = arg.equals(".") ? propertyGet(LIST_MODULE) : parseFileArg(m_activeIsolate, -1, arg); //$NON-NLS-1$

				SourceFile m = m_fileInfo.getFile(id, m_activeIsolate);
				listFunctionsFor(sb, m);
			} else {
				SourceFile[] ar = m_fileInfo.getFileList(m_activeIsolate);
				if (ar == null)
					err(getLocalizationManager().getLocalizedTextString("noSourceFilesFound")); //$NON-NLS-1$
				else {
					for (int i = 0; i < ar.length; i++) {
						SourceFile m = ar[i];
						listFunctionsFor(sb, m);
					}
				}
			}

			out(sb.toString());
		} catch (NullPointerException npe) {
			err(getLocalizationManager().getLocalizedTextString("noFunctionsFound")); //$NON-NLS-1$
		} catch (ParseException pe) {
			err(pe.getMessage());
		} catch (NoMatchException nme) {
			err(nme.getMessage());
		} catch (AmbiguousException ae) {
			err(ae.getMessage());
		}
	}

	void listFunctionsFor(StringBuilder sb, SourceFile m) {
		String[] names = m.getFunctionNames(m_session);
		if (names == null)
			return;

		Arrays.sort(names);

		Map<String, Object> args = new HashMap<String, Object>();
		args.put("sourceFile", m.getName() + "#" + m.getId()); //$NON-NLS-1$ //$NON-NLS-2$
		sb.append(getLocalizationManager().getLocalizedTextString("functionsInSourceFile", args)); //$NON-NLS-1$
		sb.append(m_newline);

		for (int j = 0; j < names.length; j++) {
			String fname = names[j];
			sb.append(' ');
			sb.append(fname);
			sb.append(' ');
			sb.append(m.getLineForFunctionName(m_session, fname));
			sb.append(m_newline);
		}
	}

	void listFilesMatching(StringBuilder sb, String match) {
		SourceFile[] sourceFiles = m_fileInfo.getFiles(match);

		for (int j = 0; j < sourceFiles.length; j++) {
			SourceFile sourceFile = sourceFiles[j];
			sb.append(sourceFile.getName());
			sb.append('#');
			sb.append(sourceFile.getId());
			sb.append(m_newline);
		}
	}

	void doInfoSources() {
		try {
			StringBuilder sb = new StringBuilder();
			buildFileList(sb, true);
			out(sb.toString());
		} catch (NullPointerException npe) {
			throw new IllegalStateException();
		}
	}

	void doInfoBreak() throws NotConnectedException {
//		waitTilHalted();

		StringBuilder sb = new StringBuilder();
		sb.append("Num Type           Disp Enb Address    What" + m_newline); //$NON-NLS-1$

		// our list of breakpoints
		int count = breakpointCount();
		for (int i = 0; i < count; i++) {
			BreakAction b = breakpointAt(i);
			int status = b.getStatus();
			boolean isResolved = (status == BreakAction.RESOLVED);
			Location l = b.getLocation();
			final LocationCollection locations = b.getLocations();
			SourceFile file = (l != null) ? l.getFile() : null;
			String funcName = (file == null) ? null : file.getFunctionNameForLine(m_session, l.getLine());
			boolean singleSwf = b.isSingleSwf();
			int cmdCount = b.getCommandCount();
			int hits = b.getHits();
			String cond = b.getConditionString();
			boolean silent = b.isSilent();
			int offset = adjustOffsetForUnitTests((file == null) ? 0 : file.getOffsetForLine(l.getLine()));

			int num = b.getId();
			FieldFormat.formatLong(sb, num, 3);
			sb.append(" breakpoint     "); //$NON-NLS-1$

			if (b.isAutoDisable())
				sb.append("dis  "); //$NON-NLS-1$
			else if (b.isAutoDelete())
				sb.append("del  "); //$NON-NLS-1$
			else
				sb.append("keep "); //$NON-NLS-1$

			if (b.isEnabled())
				sb.append("y   "); //$NON-NLS-1$
			else
				sb.append("n   "); //$NON-NLS-1$

			sb.append("0x"); //$NON-NLS-1$
			FieldFormat.formatLongToHex(sb, offset, 8);
			sb.append(' ');

			if (funcName != null) {
				Map<String, Object> args = new HashMap<String, Object>();
				args.put("functionName", funcName); //$NON-NLS-1$
				sb.append(getLocalizationManager().getLocalizedTextString("inFunctionAt", args)); //$NON-NLS-1$
			}

			if (file != null) {
				sb.append(file.getName());
				if (isResolved && singleSwf) {
					sb.append("#"); //$NON-NLS-1$
					sb.append(file.getId());
				}
				sb.append(':');
				sb.append(l.getLine());
			} else {
				String expr = b.getBreakpointExpression();
				if (expr != null)
					sb.append(expr);
			}

			final StringBuilder workerList = new StringBuilder();
			if (locations != null) {
				for (Iterator<Location> iterator = locations.iterator(); iterator.hasNext(); ) {
					Location location = iterator.next();
					workerList.append(location.getIsolateId() - 1);
					if (iterator.hasNext())
						workerList.append(" / ");
				}
			}


			if (l != null) {
				Map<String, Object> workerArgs = new HashMap<String, Object>();
				workerArgs.put("worker", workerList.toString()); //$NON-NLS-1$
				sb.append(" (");
				sb.append(getLocalizationManager().getLocalizedTextString("inWorker", workerArgs)); //$NON-NLS-1$
				sb.append(") ");
			}

			switch (status) {
				case BreakAction.UNRESOLVED:
					sb.append(getLocalizationManager().getLocalizedTextString("breakpointNotYetResolved")); //$NON-NLS-1$
					break;
				case BreakAction.AMBIGUOUS:
					sb.append(getLocalizationManager().getLocalizedTextString("breakpointAmbiguous")); //$NON-NLS-1$
					break;
				case BreakAction.NOCODE:
					sb.append(getLocalizationManager().getLocalizedTextString("breakpointNoCode")); //$NON-NLS-1$
					break;
			}

			// if a single swf break action then append more info
			if (singleSwf && isResolved) {
				try {
					SwfInfo info = m_fileInfo.swfForFile(file, l.getIsolateId());
					Map<String, Object> swfArgs = new HashMap<String, Object>();
					swfArgs.put("swf", FileInfoCache.nameOfSwf(info)); //$NON-NLS-1$
					sb.append(getLocalizationManager().getLocalizedTextString("inSwf", swfArgs)); //$NON-NLS-1$
				} catch (NullPointerException npe) {
					// can't find the swf
					sb.append(getLocalizationManager().getLocalizedTextString("nonRestorable")); //$NON-NLS-1$
				}
			}
			sb.append(m_newline);

			final String INDENT = "        "; //$NON-NLS-1$

			// state our condition if we have one
			if (cond != null && cond.length() > 0) {
				sb.append(INDENT);
				Map<String, Object> args = new HashMap<String, Object>();
				args.put("breakpointCondition", cond); //$NON-NLS-1$
				sb.append(getLocalizationManager().getLocalizedTextString(getLocalizationManager().getLocalizedTextString("stopOnlyIfConditionMet", args))); //$NON-NLS-1$
				sb.append(m_newline);
			}

			// now if its been hit, lets state the fact
			if (hits > 0) {
				sb.append(INDENT);
				Map<String, Object> args = new HashMap<String, Object>();
				args.put("count", Integer.toString(hits)); //$NON-NLS-1$
				sb.append(getLocalizationManager().getLocalizedTextString("breakpointAlreadyHit", args)); //$NON-NLS-1$
				sb.append(m_newline);
			}

			// silent?
			if (silent) {
				sb.append(INDENT);
				sb.append(getLocalizationManager().getLocalizedTextString("silentBreakpoint") + m_newline); //$NON-NLS-1$
			}

			// now if any commands are trailing then we pump them out
			for (int j = 0; j < cmdCount; j++) {
				sb.append(INDENT);
				sb.append(b.commandAt(j));
				sb.append(m_newline);
			}
		}

		int wcount = watchpointCount();
		for (int k = 0; k < wcount; k++) {
			WatchAction b = watchpointAt(k);
			int id = b.getId();
			FieldFormat.formatLong(sb, id, 4);

			int flags = b.getKind();
			switch (flags) {
				case WatchKind.READ:
					sb.append("rd watchpoint  "); //$NON-NLS-1$
					break;
				case WatchKind.WRITE:
					sb.append("wr watchpoint  "); //$NON-NLS-1$
					break;
				case WatchKind.READWRITE:
				default:
					sb.append("watchpoint     "); //$NON-NLS-1$
					break;
			}

			sb.append("keep "); //$NON-NLS-1$
			sb.append("y   "); //$NON-NLS-1$
			sb.append("           "); //$NON-NLS-1$
			sb.append(b.getExpr());
			sb.append(m_newline);
		}

		int ccount = catchpointCount();
		for (int k = 0; k < ccount; k++) {
			CatchAction c = catchpointAt(k);
			int id = c.getId();
			FieldFormat.formatLong(sb, id, 4);

			String typeToCatch = c.getTypeToCatch();
			if (typeToCatch == null)
				typeToCatch = "*"; //$NON-NLS-1$

			sb.append("catch          "); //$NON-NLS-1$
			sb.append("keep "); //$NON-NLS-1$
			sb.append("y   "); //$NON-NLS-1$
			sb.append("           "); //$NON-NLS-1$
			sb.append(typeToCatch);
			sb.append(m_newline);
		}

		out(sb.toString());
	}

	/**
	 * Dump out the state of the execution, either the fact we are running
	 * or the breakpoint we hit.
	 */
	void dumpHaltState(boolean postStep) throws NotConnectedException, SuspendedException, NoResponseException, NotSupportedException, NotSuspendedException, IOException {
		// spit out any event output, if we are to resume after a fault and we're not stepping then we're done.
		processEvents();
//		System.out.println("processEvents = "+m_requestResume);

		//if (m_requestResume && !postStep)
		if (hasAnyPendingResumes() != -1 && !postStep)
			return;

		if (!m_session.isConnected()) {
			// session is kaput
			out(getLocalizationManager().getLocalizedTextString("sessionTerminated")); //$NON-NLS-1$
			exitSession();
		} else {
			if (!m_quietMode && hasAnythingSuspended()) {
				// capture our break location / information
				StringBuilder sbLine = new StringBuilder();
				dumpBreakLine(postStep, sbLine);

				// Process our breakpoints.
				// Since we can have conditional breakpoints, which the
				// player always breaks for, but we may not want to, the variable
				// m_requestResume may be set after this call.  Additionally,
				// silent may be set for one of two reasons; 1) m_requestResume
				// was set to true in the call or one or more breakpoints that
				// hit contained the keyword silent in their command list.
				//
				StringBuilder sbBreak = new StringBuilder();
				boolean silent = processBreak(postStep, sbBreak, m_activeIsolate);

				StringBuilder sb = new StringBuilder();
				if (silent) {
					// silent means we only spit out our current location
					dumpBreakLine(postStep, sb);
				} else {
					// not silent means we append things like normal
					sb.append(sbLine);
					if (sbLine.length() > 0 && sbLine.charAt(sbLine.length() - 1) != '\n')
						sb.append(m_newline);
					sb.append(sbBreak);
				}

				// output whatever was generated
				if (sb.length() > 0)
					out(sb.toString());

//				System.out.println("processbreak = "+m_requestResume+",silent="+silent+",reason="+m_session.suspendReason());
			} else if (!m_quietMode) {
				// very bad, set stepping so that we don't trigger a continue on a breakpoint or fault
				out(getLocalizationManager().getLocalizedTextString("playerDidNotStop")); //$NON-NLS-1$
			}
			m_quietMode = false;
		}
	}

	Location getCurrentLocation() {
		return getCurrentLocationIsolate(Isolate.DEFAULT_ID);
	}

	Location getCurrentLocationIsolate(int isolateId) {
		Location where = null;
		try {
			Frame[] ar = m_session.getWorkerSession(isolateId).getFrames();
			propertyPut(CURRENT_FRAME_DEPTH, (ar.length > 0) ? ar.length : 0);
			where = ((ar.length > 0) ? ar[0].getLocation() : null);
		} catch (PlayerDebugException pde) {
			// where == null
		}
		return where;
	}

	void dumpBreakLine(boolean postStep, StringBuilder sb) throws NotConnectedException {
		int bp = -1;
		String name = getLocalizationManager().getLocalizedTextString("unknownFilename"); //$NON-NLS-1$
		int line = -1;

		// clear our current frame display
		propertyPut(DISPLAY_FRAME_NUMBER, 0);

		int targetIsolate = getLastStoppedIsolate();
		boolean activeIsolateChanged = (m_activeIsolate != targetIsolate);
		m_activeIsolate = targetIsolate;
		propertyPut(LIST_WORKER, targetIsolate);

		/* dump a context line to the console */
		Location l = getCurrentLocationIsolate(targetIsolate);

		// figure out why we stopped
		int reason = SuspendReason.Unknown;
		try {
			reason = m_session.getWorkerSession(targetIsolate).suspendReason();
		} catch (PlayerDebugException pde) {
		}

		// then see if it because of a swfloaded event
		if (reason == SuspendReason.ScriptLoaded) {
			m_fileInfo.setDirty();
			m_fileInfo.getSwfsIsolate(targetIsolate);
			//propertyPut(LIST_MODULE, m_fileInfo.getFakeId(m_fileInfo.getFile(1, targetIsolate)));
			processEvents();
			propagateBreakpoints(targetIsolate);
			propertyPut(LIST_LINE, 1);
			propertyPut(LIST_WORKER, targetIsolate);
			propertyPut(LIST_MODULE, 1);
			sb.append(getLocalizationManager().getLocalizedTextString("additionalCodeLoaded")); //$NON-NLS-1$
			if (activeIsolateChanged) {
				sb.append(m_newline + getLocalizationManager().getLocalizedTextString("workerChanged") + " " + (targetIsolate - 1) + m_newline); //$NON-NLS-1$ //$NON-NLS-2$
			}
			sb.append(m_newline);

			if (resolveBreakpoints(sb))
				sb.append(getLocalizationManager().getLocalizedTextString("setAdditionalBreakpoints") + m_newline); //$NON-NLS-1$
			else
				sb.append(getLocalizationManager().getLocalizedTextString("fixBreakpoints") + m_newline); //$NON-NLS-1$

			setPromptState(InitialPromptState.SHOWN_ONCE, targetIsolate);
		} else if (l == null || l.getFile() == null) {

			if (activeIsolateChanged) {
				sb.append(m_newline + getLocalizationManager().getLocalizedTextString("workerChanged") + " " + (targetIsolate - 1) + m_newline);
			}

			// no idea where we are ?!?
			propertyPut(LAST_FRAME_DEPTH, 0);
			sb.append(getLocalizationManager().getLocalizedTextString("executionHalted")); //$NON-NLS-1$
			sb.append(' ');

			/** disable this line (and enable the one after) if implementation Extensions are not provided */
			appendBreakInfo(sb, m_activeIsolate);
			//sb.append("unknown location");
		}/* else if (reason == SuspendReason.Breakpoint && enabledBreakpointIndexOf(l) == -1) {
            // Dont output anything

            // That's a edge case when a function breaks often, let's say a timer,
            // if the break action is disabled, the break event are still dispatched,
            // sent to the debugger and outputed, the debugger can't then halt the player anymore,
            // even removing the output, that's the same, I will try to fix this better later,
            // in between, I comment my changes.
        }*/ else {
			if (activeIsolateChanged) {
				sb.append(m_newline + getLocalizationManager().getLocalizedTextString("workerChanged") + " " + (targetIsolate - 1) + m_newline);
			}

			SourceFile file = l.getFile();
			name = file.getName();
			line = l.getLine();
			String funcName = file.getFunctionNameForLine(m_session, line);

			// where were we last time
			int lastModule = propertyGet(LIST_MODULE);
			int lastDepth = propertyGet(LAST_FRAME_DEPTH);

			int thisModule = file.getId();
			int thisDepth = propertyGet(CURRENT_FRAME_DEPTH);  // triggered via getCurrentLocation()

			// mark where we stopped
			propertyPut(LAST_FRAME_DEPTH, thisDepth);

			// if we have changed our context or we are not spitting out source then dump our location
			if (!postStep || lastModule != thisModule || lastDepth != thisDepth) {
				// is it a fault?
				String reasonForHalting;
				if (reason == SuspendReason.Fault || reason == SuspendReason.StopRequest) {
					StringBuilder s = new StringBuilder();
					appendReason(s, reason);
					reasonForHalting = s.toString();
				}
				// if its a breakpoint add that information
				else if ((bp = enabledBreakpointIndexOf(l)) > -1) {
					Map<String, Object> args = new HashMap<String, Object>();
					args.put("breakpointNumber", Integer.toString(breakpointAt(bp).getId())); //$NON-NLS-1$
					reasonForHalting = getLocalizationManager().getLocalizedTextString("hitBreakpoint", args); //$NON-NLS-1$
				} else {
					reasonForHalting = getLocalizationManager().getLocalizedTextString("executionHalted"); //$NON-NLS-1$
				}

				Map<String, Object> args = new HashMap<String, Object>();
				args.put("reasonForHalting", reasonForHalting); //$NON-NLS-1$
				args.put("fileAndLine", name + ':' + line); //$NON-NLS-1$
				String formatString;
				if (funcName != null) {
					args.put("functionName", funcName); //$NON-NLS-1$
					formatString = "haltedInFunction"; //$NON-NLS-1$
				} else {
					formatString = "haltedInFile"; //$NON-NLS-1$
				}
				sb.append(getLocalizationManager().getLocalizedTextString(formatString, args));

				if (!m_fullnameOption)
					sb.append(m_newline);
			}

			// set current listing poistion and emit emacs trigger
			setListingPosition(thisModule, line, targetIsolate);

			// dump our source line if not in emacs mode
			if (!m_fullnameOption)
				appendSource(sb, file.getId(), line, file.getLine(line), false);
		}
	}

	private int getLastStoppedIsolate() {
		int targetIsolate = Isolate.DEFAULT_ID;

		if (m_breakIsolates.size() > 0) {
			targetIsolate = m_breakIsolates.get(m_breakIsolates.size() - 1);
		}
		return targetIsolate;
	}

	void appendFullnamePosition(StringBuilder sb, SourceFile file, int lineNbr) {
		// fullname option means we dump 'path:line:col?:offset', which is used for emacs !
		String name = file.getFullPath();
		if (name.startsWith("file:/")) //$NON-NLS-1$
			name = name.substring(6);

		// Ctrl-Z Ctrl-Z
		sb.append('\u001a');
		sb.append('\u001a');

		sb.append(name);
		sb.append(':');
		sb.append(lineNbr);
		sb.append(':');
		sb.append('0');
		sb.append(':');
		sb.append("beg"); //$NON-NLS-1$
		sb.append(':');
		sb.append('0');
	}

	// pretty print a trace statement to the console
	void dumpTraceLine(String s) {
		StringBuilder sb = new StringBuilder();
		sb.append("[trace] "); //$NON-NLS-1$
		sb.append(s);
		out(sb.toString());
	}

	// pretty print a fault statement to the console
	void dumpFaultLine(FaultEvent e) {
		StringBuilder sb = new StringBuilder();

		// use a slightly different format for ConsoleErrorFaults
		if (e instanceof ConsoleErrorFault) {
			sb.append(getLocalizationManager().getLocalizedTextString("linePrefixWhenDisplayingConsoleError")); //$NON-NLS-1$
			sb.append(' ');
			sb.append(e.information);

			final String stackTrace = e.stackTrace();
			if (stackTrace != null && stackTrace.length() > 0) {
				sb.append("\n").append(stackTrace);
			}
		} else {
			String name = e.name();
			sb.append(getLocalizationManager().getLocalizedTextString("linePrefixWhenDisplayingFault")); //$NON-NLS-1$
			sb.append(' ');
			sb.append(name);
			if (e.information != null && e.information.length() > 0) {
				sb.append(getLocalizationManager().getLocalizedTextString("informationAboutFault")); //$NON-NLS-1$
				sb.append(e.information);
			}

			final String stackTrace = e.stackTrace();
			if (stackTrace != null && stackTrace.length() > 0) {
				sb.append("\n").append(stackTrace);
			}
		}
		out(sb.toString());
	}

	/**
	 * Called when a swf has been loaded by the player
	 * @param e event documenting the load
	 */
	void handleSwfLoadedEvent(SwfLoadedEvent e) {
		// first we dump out a message that displays we have loaded a swf
		dumpSwfLoadedLine(e);
	}

	// pretty print a SwfLoaded statement to the console
	void dumpSwfLoadedLine(SwfLoadedEvent e) {
		// now rip off any trailing ? options
		int at = e.path.lastIndexOf('?');
		String name = (at > -1) ? e.path.substring(0, at) : e.path;

		StringBuilder sb = new StringBuilder();
		sb.append(getLocalizationManager().getLocalizedTextString("linePrefixWhenSwfLoaded")); //$NON-NLS-1$
		sb.append(' ');
		sb.append(name);
		sb.append(" - "); //$NON-NLS-1$

		Map<String, Object> args = new HashMap<String, Object>();
		args.put("size", NumberFormat.getInstance().format(e.swfSize)); //$NON-NLS-1$
		sb.append(getLocalizationManager().getLocalizedTextString("sizeAfterDecompression", args)); //$NON-NLS-1$
		out(sb.toString());
	}

	/**
	 * Propagate current breakpoints to the newly loaded swf.
	 */
	void propagateBreakpoints(int isolateId) throws NotConnectedException {
		// get the newly added swf, which lands at the end list
		SwfInfo[] swfs = m_fileInfo.getSwfsIsolate(isolateId);
		SwfInfo swf = (swfs.length > 0) ? swfs[swfs.length - 1] : null;

		// now walk through all breakpoints propagating the
		// the break for each source and line number we
		// find in the new swf
		int size = m_breakpoints.size();
		for (int i = 0; (swf != null) && i < size; i++) {
			// dont do this for single swf breakpoints
			BreakAction bp = breakpointAt(i);
			if (bp.isSingleSwf())
				continue;
			if (bp.getStatus() != BreakAction.RESOLVED)
				continue;
			if (!bp.isPropagable())
				continue;

			try {
				Location l = bp.getLocation();
				int line = l.getLine();
				SourceFile f = l.getFile();
				Location newLoc = findAndEnableBreak(swf, f, line);
				if (newLoc != null)
					bp.addLocation(newLoc);
				else throw new RuntimeException("newLock == null");

				dumpAddedBreakpoint(bp);

			} catch (InProgressException ipe) {
				if (breakpointCount() > 0) {
					Map<String, Object> args = new HashMap<String, Object>();
					args.put("breakpointNumber", Integer.toString(bp.getId())); //$NON-NLS-1$
					out(getLocalizationManager().getLocalizedTextString("breakpointNotPropagated", args)); //$NON-NLS-1$
				}
			}
		}
	}

	private void dumpAddedBreakpoint(BreakAction bp) {
		Location l = bp.getLocations().last();
		int which = bp.getId();
		String name = l.getFile().getName();
		int offset = adjustOffsetForUnitTests(l.getFile().getOffsetForLine(l.getLine()));

		Map<String, Object> args = new HashMap<String, Object>();
		args.put("breakpointNumber", Integer.toString(which)); //$NON-NLS-1$
		args.put("file", name); //$NON-NLS-1$
		args.put("line", Integer.toString(l.getLine())); //$NON-NLS-1$
		String formatString;
		if (offset != 0) {
			args.put("offset", "0x" + Integer.toHexString(offset)); //$NON-NLS-1$ //$NON-NLS-2$
			formatString = "createdBreakpointWithOffset"; //$NON-NLS-1$
		} else {
			formatString = "createdBreakpoint"; //$NON-NLS-1$
		}
		out(getLocalizationManager().getLocalizedTextString(formatString, args));
	}

	/**
	 * Perform the tasks need for when a swf is unloaded
	 * the player
	 */
	void handleSwfUnloadedEvent(SwfUnloadedEvent e) {
		// print out the notification
		dumpSwfUnloadedLine(e);
	}

	// pretty print a SwfUnloaded statement to the console
	void dumpSwfUnloadedLine(SwfUnloadedEvent e) {
		// now rip off any trailing ? options
		int at = e.path.lastIndexOf('?');
		String name = (at > -1) ? e.path.substring(0, at) : e.path;

		StringBuilder sb = new StringBuilder();
		sb.append(getLocalizationManager().getLocalizedTextString("linePrefixWhenSwfUnloaded")); //$NON-NLS-1$
		sb.append(' ');
		sb.append(name);
		out(sb.toString());
	}

	void doContinue() throws NotConnectedException {
		//int stoppedIsolate = getLastStoppedIsolate();
		int stoppedIsolate = m_activeIsolate;
		waitTilHalted(stoppedIsolate);

		// this will trigger a resume when we get back to the main loop
		//m_requestResume = true;
		setRequestResume(true, stoppedIsolate);
		m_repeatLine = m_currentLine;
	}

	boolean hasAnythingSuspended() throws NotConnectedException {
		boolean hasAnythingSuspended = false;
		for (Integer id : m_breakIsolates) {
			if (m_session.getWorkerSession(id).isSuspended()) {
				hasAnythingSuspended = true;
				break;
			}
		}
		return hasAnythingSuspended;
	}

	int hasAnyPendingResumes() throws NotConnectedException {
		int rid = -1;
		if (m_mainState.m_requestResume)
			return Isolate.DEFAULT_ID;
		for (Integer id : m_breakIsolates) {
			if (getIsolateState(id).m_requestResume) {
				rid = id;
				break;
			}
		}
		return rid;
	}

	/**
	 * Returns the first isolate's id for which we need to keep showing prompts.
	 *
	 * @return The isolate id
	 * @throws NotConnectedException
	 */
	int hasPendingInitialPrompts() throws NotConnectedException {
		int rid = -1;
		for (Integer id : m_breakIsolates) {
			if (getPromptState(id) != InitialPromptState.DONE) {
				rid = id;
				break;
			}
		}
		return rid;
	}

	/**
	 * Our main loop when the player is off running
	 */
	public void runningLoop() throws NoResponseException, NotConnectedException, SuspendedException, NotSupportedException, NotSuspendedException, IOException {
		int update = propertyGet(UPDATE_DELAY);
		boolean nowait = (propertyGet(NO_WAITING) == 1) ? true : false;  // DEBUG ONLY; do not document
		boolean stop = false;
		boolean noConnection = !haveConnection();
		boolean hasAnythingSuspended = false;
		int targetIsolate = Isolate.DEFAULT_ID;
		if (!noConnection) {
			hasAnythingSuspended = hasAnythingSuspended();
		}

		if (hasAnythingSuspended) {
			if (m_breakIsolates.size() > 0) {
				targetIsolate = m_breakIsolates.get(m_breakIsolates.size() - 1);
			}
		}
		// not there, not connected or already halted and no pending resume requests => we are done
		//if (noConnection || (hasAnythingSuspended && !m_requestResume) )
		if (noConnection || (hasAnythingSuspended && hasAnyPendingResumes() == -1)) {
			processEvents();
			stop = true;

			if (!noConnection) {
				/** At this point, some isolate is in suspended state and will be until a resume
				 *  is requested via the prompt. We thus check for any pending prompts to be displayed
				 *  for freshly loaded swfs. If any, we switch to that worker and prompt the user to set
				 *  any break points.  */
				int pendingPromptIsolate = -1;
				if (m_lastPromptIsolate != -1 && (getPromptState(m_lastPromptIsolate) != InitialPromptState.DONE)) {
					pendingPromptIsolate = m_lastPromptIsolate;
				} else {
					pendingPromptIsolate = hasPendingInitialPrompts();
				}
				if (pendingPromptIsolate != -1 && pendingPromptIsolate == m_activeIsolate) {
					dumpInitialPrompt(pendingPromptIsolate);
				}
			}
		}

		while (!stop) {
			// allow keyboard input
			if (!nowait)
				m_keyboardReadRequest = true;
			int pendingResumeId = hasAnyPendingResumes();
			if (pendingResumeId != -1) {
				// resume execution (request fulfilled) and look for keyboard input
				try {
					IsolateSession workerSession = m_session.getWorkerSession(pendingResumeId);
					//if (m_stepResume)
					if (getStepResume(pendingResumeId))
						workerSession.stepContinue();
					else {
						workerSession.resume();
					}
					/** The user is done setting initial breakpoints for this isolate,
					 *  clear any pending initial prompts */
					setPromptState(InitialPromptState.DONE, pendingResumeId);
					removeBreakIsolate(pendingResumeId);
				} catch (NotSuspendedException nse) {
					err(getLocalizationManager().getLocalizedTextString("playerAlreadyRunning")); //$NON-NLS-1$
				}

				setRequestResume(false, pendingResumeId);
				setRequestHalt(false, pendingResumeId);
				setStepResume(false, pendingResumeId);
//				m_requestResume = false;
//				m_requestHalt = false;
//				m_stepResume = false;
			}

			// sleep for a bit, then process our events.
			try {
				Thread.sleep(update);
			} catch (InterruptedException ie) {
			}
			processEvents();

			// lost connection?
			if (!haveConnection()) {
				stop = true;
				dumpHaltState(false);
			} else if (hasAnythingSuspended()) {
				/**
				 * We have stopped for some reason.  Now for all cases, but conditional
				 * breakpoints, we should be done.  For conditional breakpoints it
				 * may be that the condition has turned out to be false and thus
				 * we need to continue
				 */

				/**
				 * Now before we do this see, if we have a valid break reason, since
				 * we could be still receiving incoming messages, even though we have halted.
				 * This is definately the case with loading of multiple SWFs.  After the load
				 * we get info on the swf.
				 */
				if (m_breakIsolates.size() > 0) {
					targetIsolate = m_breakIsolates.get(m_breakIsolates.size() - 1);
				} else {
					targetIsolate = Isolate.DEFAULT_ID;
				}
				int tries = 3;
				IsolateSession workerSession = m_session.getWorkerSession(targetIsolate);
				while (tries-- > 0 && workerSession.suspendReason() == SuspendReason.Unknown)
					try {
						Thread.sleep(100);
						processEvents();
					} catch (InterruptedException ie) {
					}

				dumpHaltState(false);
				//if (!m_requestResume)
				if (!getRequestResume(targetIsolate))
					stop = true;
			} else if (nowait) {
				stop = true;  // for DEBUG only
			} else {
				/**
				 * We are still running which is fine.  But let's see if the user has
				 * tried to enter something on the keyboard.  If so, then we need to
				 * stop
				 */
				if (!m_keyboardInput.isEmpty() && System.getProperty("fdbunit") == null) //$NON-NLS-1$
				{
					// flush the queue and prompt the user if they want us to halt
					m_keyboardInput.clear();
					try {
						if (yesNoQuery(getLocalizationManager().getLocalizedTextString("doYouWantToHalt"))) //$NON-NLS-1$
						{
							out(getLocalizationManager().getLocalizedTextString("attemptingToHalt")); //$NON-NLS-1$
							IsolateSession workerSession = m_session.getWorkerSession(m_activeIsolate);
							workerSession.suspend();
//							m_session.suspend();
							getIsolateState(m_activeIsolate).m_requestHalt = true;

							// no connection => dump state and end
							if (!haveConnection()) {
								dumpHaltState(false);
								stop = true;
							} else if (!workerSession.isSuspended())
								err(getLocalizationManager().getLocalizedTextString("couldNotHalt")); //$NON-NLS-1$
						}
					} catch (IllegalArgumentException iae) {
						out(getLocalizationManager().getLocalizedTextString("escapingFromDebuggerPendingLoop")); //$NON-NLS-1$
						propertyPut(NO_WAITING, 1);
						stop = true;
					} catch (IOException io) {
						Map<String, Object> args = new HashMap<String, Object>();
						args.put("error", io.getMessage()); //$NON-NLS-1$
						err(getLocalizationManager().getLocalizedTextString("continuingDueToError", args)); //$NON-NLS-1$
					} catch (SuspendedException se) {
						// lucky us, already stopped
					}
				}
			}
//		System.out.println("doContinue resume="+m_requestResume+",isSuspended="+m_session.isSuspended());
		}

		// DEBUG ONLY: if we are not waiting then process some events
		if (nowait)
			processEvents();
	}

	/**
	 * Does a few of things -
	 * 1) Sets the target worker as 'active'.
	 * 2) Propagates any breakpoints already set which are relevant to the target worker.
	 * 3) Outputs messages indicating additional code load and worker switch.
	 * 4) Sets {@link DebugCLIIsolateState#m_promptState} as {@link InitialPromptState#SHOWN_ONCE} if
	 * the prompt hasn't been shown for this worker before.
	 */
	private void dumpInitialPrompt(int targetIsolate) throws NotConnectedException {
		boolean activeIsolateChanged = (m_activeIsolate != targetIsolate);
		m_activeIsolate = targetIsolate;

		if (activeIsolateChanged) {
			propertyPut(LIST_WORKER, targetIsolate);
			propertyPut(LIST_LINE, 1);
			propertyPut(LIST_MODULE, 1);
			propagateBreakpoints(targetIsolate);
		}

		StringBuilder sb = new StringBuilder();
		if (getPromptState(targetIsolate) == InitialPromptState.NEVER_SHOWN) {
			sb.append(getLocalizationManager().getLocalizedTextString("additionalCodeLoaded")); //$NON-NLS-1$
			sb.append(m_newline + getLocalizationManager().getLocalizedTextString("workerChanged") + " " + (targetIsolate - 1) + m_newline); //$NON-NLS-1$ //$NON-NLS-2$
			sb.append(m_newline);
			setPromptState(InitialPromptState.SHOWN_ONCE, targetIsolate);
		}

		if (resolveBreakpoints(sb))
			sb.append(getLocalizationManager().getLocalizedTextString("setAdditionalBreakpoints") + m_newline); //$NON-NLS-1$
		else
			sb.append(getLocalizationManager().getLocalizedTextString("fixBreakpoints") + m_newline); //$NON-NLS-1$

		// output whatever has to be
		if (sb.length() > 0)
			out(sb.toString());

	}

	private void removeBreakIsolate(int targetIsolate) {
		for (int i = 0; i < m_breakIsolates.size(); i++) {
			int id = m_breakIsolates.get(i);
			if (id == targetIsolate) {
				m_breakIsolates.remove(i);
				break;
			}
		}
	}

	/**
	 * Bring the listing location back to the current frame
	 */
	void doHome() {
		try {
			Location l = getCurrentLocationIsolate(m_activeIsolate);
			SourceFile file = l.getFile();
			int module = file.getId();
			int line = l.getLine();
			int worker = l.getIsolateId();

			// now set it
			setListingPosition(module, line, worker);
		} catch (NullPointerException npe) {
			err(getLocalizationManager().getLocalizedTextString("currentLocationUnknown")); //$NON-NLS-1$
		}
	}

	// Dump a source line of text to the display
	void dumpStep() throws NotConnectedException, SuspendedException, IOException, NotSupportedException, NotSuspendedException, NoResponseException {
		dumpHaltState(true);
	}

	/**
	 * Simple interface used with stepWithTimeout().  Implementors of this interface
	 * are expected to call one of these function: Session.stepInto(), Session.stepOver(),
	 * Session.stepOut(), or Session.stepContinue().
	 */
	private interface AnyKindOfStep {
		public void step() throws PlayerDebugException;
	}

	/**
	 * Helper function to do a stepInto, stepOver, stepOut, or stepContinue,
	 * and then to block (processing events) until either the step has completed
	 * or it has timed out.
	 */
	private void stepWithTimeout(AnyKindOfStep step, int isolateId) throws PlayerDebugException {
		int timeout = m_session.getPreference(SessionManager.PREF_RESPONSE_TIMEOUT);
		long timeoutTime = System.currentTimeMillis() + timeout;

		step.step();
		IsolateSession workerSession = m_session.getWorkerSession(isolateId);
		while (System.currentTimeMillis() < timeoutTime && !workerSession.isSuspended()) {
			processEvents();
			if (!m_session.isSuspended()) {
				try {
					Thread.sleep(1);
				} catch (InterruptedException e) {
				}
			}
		}
		if (System.currentTimeMillis() >= timeoutTime && !workerSession.isSuspended())
			throw new NoResponseException(timeout);
	}

	private boolean allowedToStep(int isolateId) throws NotConnectedException {
		int suspendReason = m_session.getWorkerSession(isolateId).suspendReason();
		if (suspendReason == SuspendReason.ScriptLoaded) {
			err(getLocalizationManager().getLocalizedTextString("cannotStep")); //$NON-NLS-1$
			return false;
		}

		return true;
	}

	/**
	 * Perform step into, optional COUNT parameter
	 */
	void doStep() throws PlayerDebugException, IOException {
		waitTilHalted(m_activeIsolate);

		if (!allowedToStep(m_activeIsolate))
			return;

		int count = 1;
		if (hasMoreTokens())
			count = nextIntToken();
		DebugCLIIsolateState state = getIsolateState(m_activeIsolate);
		while (count-- > 0) {
			stepWithTimeout(new AnyKindOfStep() {
				public void step() throws PlayerDebugException {
					m_session.getWorkerSession(m_activeIsolate).stepInto();
				}
			}, m_activeIsolate);

			for (; ; ) {
				dumpStep();

				if (state.m_requestResume) // perhaps we hit a conditional breakpoint
				{
					state.m_requestResume = false;
					stepWithTimeout(new AnyKindOfStep() {
						public void step() throws PlayerDebugException {
							m_session.getWorkerSession(m_activeIsolate).stepContinue();
						}
					}, m_activeIsolate);
				} else {
					break;
				}
			}
		}

		m_repeatLine = m_currentLine;
	}

	/**
	 * Perform step over, optional COUNT parameter
	 */
	void doNext() throws PlayerDebugException, IOException {
		waitTilHalted(m_activeIsolate);

		if (!allowedToStep(m_activeIsolate))
			return;

		int count = 1;
		if (hasMoreTokens())
			count = nextIntToken();
		DebugCLIIsolateState state = getIsolateState(m_activeIsolate);
		try {
			while (count-- > 0) {
				stepWithTimeout(new AnyKindOfStep() {
					public void step() throws PlayerDebugException {
						m_session.getWorkerSession(m_activeIsolate).stepOver();
					}
				}, m_activeIsolate);

				for (; ; ) {
					dumpStep();

					if (state.m_requestResume) // perhaps we hit a conditional breakpoint
					{
						state.m_requestResume = false;
						stepWithTimeout(new AnyKindOfStep() {
							public void step() throws PlayerDebugException {
								m_session.getWorkerSession(m_activeIsolate).stepContinue();
							}
						}, m_activeIsolate);
					} else {
						break;
					}
				}
			}
		} catch (NoResponseException nre) {
			if (count > 0) {
				Map<String, Object> args = new HashMap<String, Object>();
				args.put("count", Integer.toString(count)); //$NON-NLS-1$
				err(getLocalizationManager().getLocalizedTextString("abortingStep", args)); //$NON-NLS-1$
			}
		}

		m_repeatLine = m_currentLine;
	}

	/**
	 * Perform step out
	 */
	void doFinish() throws PlayerDebugException, IOException {
		waitTilHalted(m_activeIsolate);

		if (!allowedToStep(m_activeIsolate))
			return;
		DebugCLIIsolateState state = getIsolateState(m_activeIsolate);
		try {
			// make sure we have another frame?
			int depth = propertyGet(CURRENT_FRAME_DEPTH);
			if (depth < 2)
				err(getLocalizationManager().getLocalizedTextString("finishCommandNotMeaningfulOnOutermostFrame")); //$NON-NLS-1$
			else {
				stepWithTimeout(new AnyKindOfStep() {
					public void step() throws PlayerDebugException {
						m_session.getWorkerSession(m_activeIsolate).stepOut();
					}
				}, m_activeIsolate);

				for (; ; ) {
					dumpStep();

					if (state.m_requestResume) // perhaps we hit a conditional breakpoint
					{
						state.m_requestResume = false;
						stepWithTimeout(new AnyKindOfStep() {
							public void step() throws PlayerDebugException {
								m_session.getWorkerSession(m_activeIsolate).stepContinue();
							}
						}, m_activeIsolate);
					} else {
						break;
					}
				}

				m_repeatLine = m_currentLine;
			}
		} catch (NullPointerException npe) {
			err(getLocalizationManager().getLocalizedTextString("finishCommandNotMeaningfulWithoutStack")); //$NON-NLS-1$
		}
	}

	/**
	 * Delete a breakpoint, very similar logic to disable.
	 */
	void doDelete() throws IOException, AmbiguousException, NotConnectedException, SuspendedException, NotSupportedException, NotSuspendedException, NoResponseException {
		waitTilHalted(m_activeIsolate);

		try {
			if (!hasMoreTokens()) {
				// no args means delete all breakpoints, last chance...
				if (yesNoQuery(getLocalizationManager().getLocalizedTextString("askDeleteAllBreakpoints"))) //$NON-NLS-1$
				{
					int count = breakpointCount();
					for (int i = count - 1; i > -1; i--)
						removeBreakpointAt(i);

					removeAllWatchpoints();
					removeAllCatchpoints();
				}
			} else {
				// optionally specify  'display' or 'breakpoint'
				String arg = nextToken();
				int cmd = disableCommandFor(arg);
				int id = -1;
				if (cmd == CMD_DISPLAY)
					doUnDisplay();
				else {
					if (cmd == CMD_BREAK)
						id = nextIntToken();  // ignore and get next number token
					else
						id = Integer.parseInt(arg);

					do {
						try {
							int at = breakpointIndexOf(id);
							if (at > -1) {
								removeBreakpointAt(at);
							} else {
								at = watchpointIndexOf(id);
								if (at > -1) {
									removeWatchpointAt(at);
								} else {
									at = catchpointIndexOf(id);
									removeCatchpointAt(at);
								}
							}
						} catch (IndexOutOfBoundsException iob) {
							Map<String, Object> args = new HashMap<String, Object>();
							args.put("breakpointNumber", m_currentToken); //$NON-NLS-1$
							err(getLocalizationManager().getLocalizedTextString("noBreakpointNumber", args)); //$NON-NLS-1$
						}

						if (hasMoreTokens())
							id = nextIntToken();
						else
							id = -1;

						// keep going till we're blue in the face; also note that we cache'd a copy of locations
						// so that breakpoint numbers are consistent.
					}
					while (id > -1);
				}
			}
		} catch (NumberFormatException nfe) {
			Map<String, Object> args = new HashMap<String, Object>();
			args.put("token", m_currentToken); //$NON-NLS-1$
			err(getLocalizationManager().getLocalizedTextString("badBreakpointNumber", args)); //$NON-NLS-1$
		} catch (NullPointerException npe) {
			err(getLocalizationManager().getLocalizedTextString("commandFailed")); //$NON-NLS-1$
		}
	}

	/**
	 * Set a breakpoint
	 */
	void doBreak() throws NotConnectedException, NoResponseException, NotSupportedException, NotSuspendedException, IOException, SuspendedException {
		int isolateId = propertyGet(LIST_WORKER);
		int savedIsolateId = isolateId;

		/* wait a bit if we are not halted */
		waitTilHalted(isolateId);

		int module = propertyGet(LIST_MODULE);
		int line = propertyGet(LIST_LINE);

		FileLocation[] fileLocations = new FileLocation[0];
		boolean wasAlreadySuspended = false;

		String arg = null;
		boolean propagate = true;

		/* currentXXX may NOT be invalid! */
		try {
			if (hasMoreTokens()) {
				arg = nextToken();
				propagate = !arg.contains("@");
				fileLocations = parseLocationArg(module, line, arg);
			} else {
				// no parameter mean use current location;  null pointer if nothing works
				Location l = getCurrentLocationIsolate(isolateId);
				SourceFile file = l.getFile();

				final FileLocation fileLocation = new FileLocation(isolateId, file.getId(), l.getLine());
				fileLocations[0] = fileLocation;
			}

			boolean switching = false;
			for (FileLocation fileLocation : fileLocations) {
				isolateId = fileLocation.getIsolateId();
				module = fileLocation.getModule();
				line = fileLocation.getLine();

				if (isolateId != savedIsolateId) {
					switching = true;
					wasAlreadySuspended = swapActiveWorkerAndStop(isolateId);
				}

				// go off; create it and set it
				BreakAction b = addBreakpoint(module, line, isolateId, propagate);  // throws npe if not able to set
				dumpAddedBreakpoint(b);

				// worked so add it to our tracking state
				propertyPut(BPNUM, b.getId());

				if (switching) {
					continueAndSwapActiveWorkerBack(savedIsolateId, wasAlreadySuspended);
					switching = false;
				}
			}
		} catch (ParseException pe) {
			err(pe.getMessage());
		} catch (AmbiguousException ae) {
			err(ae.getMessage());
		} catch (NoMatchException nme) {
			// We couldn't find a function name or filename which matched what
			// the user entered.  Do *not* fail; instead, just save this breakpoint
			// away, and later, as more ABCs get loaded from the SWF, we may be
			// able to resolve this breakpoint.
			BreakAction b = addUnresolvedBreakpoint(arg, m_activeIsolate);

			Map<String, Object> args = new HashMap<String, Object>();
			args.put("breakpointNumber", Integer.toString(b.getId())); //$NON-NLS-1$
			out(getLocalizationManager().getLocalizedTextString("breakpointCreatedButNotYetResolved", args)); //$NON-NLS-1$

			// add it to our tracking state
			propertyPut(BPNUM, b.getId());
		} catch (NullPointerException npe) {
			String filename;
			try {
				filename = m_fileInfo.getFile(module, isolateId).getName() + "#" + module; //$NON-NLS-1$
			} catch (Exception e) {
				Map<String, Object> args = new HashMap<String, Object>();
				args.put("fileNumber", Integer.toString(module)); //$NON-NLS-1$
				filename = getLocalizationManager().getLocalizedTextString("fileNumber", args); //$NON-NLS-1$
			}

			Map<String, Object> args = new HashMap<String, Object>();
			args.put("filename", filename); //$NON-NLS-1$
			args.put("line", Integer.toString(line)); //$NON-NLS-1$
			err(getLocalizationManager().getLocalizedTextString("breakpointNotSetNoCode", args)); //$NON-NLS-1$

			continueAndSwapActiveWorkerBack(savedIsolateId, wasAlreadySuspended);
		} catch (InProgressException e) {
			e.printStackTrace();
		}
	}

	private boolean swapActiveWorkerAndStop(int isolateId) throws IOException, NotSupportedException, NotSuspendedException, NoResponseException, NotConnectedException, SuspendedException {
		return swapActiveWorkerAndStop(isolateId, WORKER_DISPLAY_INTERNAL_SWAP_INFO);
	}

	private boolean swapActiveWorkerAndStop(int isolateId, boolean displayInfo) throws IOException, NotSupportedException, NotSuspendedException, NoResponseException, NotConnectedException, SuspendedException {
		doWorker(isolateId, displayInfo);

		IsolateSession session = m_session.getWorkerSession(m_activeIsolate);
		final boolean suspended = session.isSuspended();

		if (!suspended) {
			session.suspend();
			waitTilHalted(m_activeIsolate);
		}

		return suspended;
	}

	private void continueAndSwapActiveWorkerBack(int savedIsolateId, boolean wasAlreadySuspended) throws NotConnectedException, IOException, NotSupportedException, NotSuspendedException, NoResponseException {
		continueAndSwapActiveWorkerBack(savedIsolateId, wasAlreadySuspended, WORKER_DISPLAY_INTERNAL_SWAP_INFO);
	}

	private void continueAndSwapActiveWorkerBack(int savedIsolateId, boolean wasAlreadySuspended, boolean displayInfo) throws NotConnectedException, IOException, NotSupportedException, NotSuspendedException, NoResponseException {
		if (!wasAlreadySuspended) {
			m_quietMode = !displayInfo;
			doContinue();
		}

		if (m_activeIsolate != savedIsolateId)
			doWorker(savedIsolateId, displayInfo);
	}

	/**
	 * Clear a breakpoint
	 */
	void doClear() throws NotConnectedException, SuspendedException, NoResponseException, NotSupportedException, NotSuspendedException, IOException {
		int module = propertyGet(LIST_MODULE);
		int line = propertyGet(LIST_LINE);
		int isolateId = propertyGet(LIST_WORKER);
		String arg = null;

		/* wait a bit if we are not halted */
		waitTilHalted(m_activeIsolate);

		/* currentXXX may NOT be invalid! */
		try {
			if (hasMoreTokens()) {
				arg = nextToken();
				FileLocation[] fileLocations = parseLocationArg(module, line, arg);

				for (FileLocation fileLocation : fileLocations) {
					isolateId = fileLocation.getIsolateId();
					module = fileLocation.getModule();
					line = fileLocation.getLine();

					try {
						removeBreakpoint(module, line, isolateId);
					} catch (ArrayIndexOutOfBoundsException aio) {
						// means no breakpoint at this location
						err(getLocalizationManager().getLocalizedTextString("breakpointLocationUnknown")); //$NON-NLS-1$
					} catch (NullPointerException npe) {
						err(getLocalizationManager().getLocalizedTextString("breakpointNotCleared")); //$NON-NLS-1$
					}
				}
			}

		} catch (ParseException pe) {
			err(pe.getMessage());
		} catch (AmbiguousException ae) {
			err(ae.getMessage());
		} catch (NoMatchException e) {
			if (removeUnresolvedBreakpoint(arg) == null)
				err(e.getMessage());
		}
	}

	BreakAction removeBreakpoint(int fileId, int line, int isolateId) throws ArrayIndexOutOfBoundsException, NotConnectedException, SuspendedException, IOException, NotSupportedException, NotSuspendedException, NoResponseException {
		int at = breakpointIndexOf(fileId, line, isolateId);
		return removeBreakpointAt(at, isolateId);
	}

	BreakAction removeUnresolvedBreakpoint(String unresolvedLocation) throws NotConnectedException, SuspendedException, IOException, NotSupportedException, NotSuspendedException, NoResponseException {
		int size = breakpointCount();
		for (int i = 0; i < size; i++) {
			BreakAction b = breakpointAt(i);
			String s = b.getBreakpointExpression();
			if (s != null && s.equals(unresolvedLocation)) {
				m_breakpoints.removeElementAt(i);
				return b;
			}
		}
		return null;
	}

	private BreakAction removeBreakpointAt(int at, int isolateId) throws ArrayIndexOutOfBoundsException, NotConnectedException, NoResponseException, NotSupportedException, SuspendedException, NotSuspendedException, IOException {

		BreakAction a = breakpointAt(at);

		if (a.getStatus() == BreakAction.RESOLVED) {
			int savedIsolateId = m_activeIsolate;
			boolean wasAlreadySuspended = false;

			Location location = null;
			for (Iterator<Location> iterator = a.getLocations().iterator(); iterator.hasNext(); ) {
				location = iterator.next();
				if (location.getIsolateId() == isolateId) {

					boolean switching = false;
					if (location.getIsolateId() != savedIsolateId) {
						switching = true;
						wasAlreadySuspended = swapActiveWorkerAndStop(location.getIsolateId());
					}

					final LocationCollection locationCollection = new LocationCollection();
					locationCollection.add(location);

					breakDisableRequest(locationCollection);
					a.getLocations().remove(location);
					if (a.getLocations().isEmpty())
						m_breakpoints.removeElementAt(at);

					if (switching)
						continueAndSwapActiveWorkerBack(savedIsolateId, wasAlreadySuspended);

				}
			}
		} else if (a.getStatus() == BreakAction.UNRESOLVED) {
			m_breakpoints.removeElementAt(at);
		}

		return a;
	}

	BreakAction removeBreakpointAt(int at) throws ArrayIndexOutOfBoundsException, NotConnectedException, NoResponseException, NotSupportedException, SuspendedException, NotSuspendedException, IOException {
		BreakAction a = breakpointAt(at);

		if (a.getStatus() == BreakAction.RESOLVED) {
			int savedIsolateId = m_activeIsolate;
			boolean wasAlreadySuspended;

			for (Iterator<Location> iterator = a.getLocations().iterator(); iterator.hasNext(); ) {
				Location location = iterator.next();

				if (location.getIsolateId() != savedIsolateId) {
					wasAlreadySuspended = swapActiveWorkerAndStop(a.getLocation().getIsolateId());

					final LocationCollection locationCollection = new LocationCollection();
					locationCollection.add(location);
					breakDisableRequest(locationCollection);

					continueAndSwapActiveWorkerBack(savedIsolateId, wasAlreadySuspended);
				} else
					breakDisableRequest(a.getLocations());
			}

			m_breakpoints.removeElementAt(at);


		} else if (a.getStatus() == BreakAction.UNRESOLVED) {
			m_breakpoints.removeElementAt(at);
		}

		return a;
	}

	/**
	 * Attempt to create new breakpoint at the given file and line. It will be set
	 * @param fileId source file identifier
	 * @param line   line number
	 * @param propagate
	 * @return object associated with breakpoint
	 */
	BreakAction addBreakpoint(int fileId, int line, int isolateId, boolean propagate) throws NotConnectedException, NullPointerException, InProgressException {
		// use fileId SourceFile to denote the name of file in which we wish to set a breakpoint
		final SourceFile f = m_fileInfo.getFile(fileId, isolateId);
		int bp = breakpointIndexOf(fileId, line, 0, true, -1);
		int at = breakpointIndexOf(fileId, line, 0, true, isolateId);

		final boolean bpDoesNotExistAtAll = at == -1 && bp == -1;
		final boolean bpExistsButNotForThisIsolate = at == -1 && bp > -1;

		BreakAction b;

		if (bpDoesNotExistAtAll) {
			LocationCollection col = enableBreak(f, line, isolateId);

			b = new BreakAction(col);  //  throws NullPointerException if collection is null
			b.setEnabled(true);
			b.setSingleSwf(m_fileInfo.isSwfFilterOn());
			b.setPropagable(propagate);
			breakpointAdd(b);
		} else {
			if (bpExistsButNotForThisIsolate){
				b = breakpointAt(bp);
				if (b != null) {
					SwfInfo[] swfs = m_fileInfo.getSwfsIsolate(isolateId);
					SwfInfo swf = (swfs.length > 0) ? swfs[swfs.length - 1] : null;
					Location newLoc = findAndEnableBreak(swf, f, line);
					if (newLoc != null)
						b.addLocation(newLoc);
					else throw new RuntimeException("newLock == null");
				}
			} else return breakpointAt(at); // already set before
		}

		return b;
	}

	/**
	 * Create a new, *unresolved* breakpoint.  Unresolved means we weren't able to
	 * parse the location string, presumably because the filename to which it refers
	 * has not yet been loaded.
	 * @param unresolvedLocation the breakpoint location, exactly as typed by the user
	 * @return object associated with breakpoint
	 */
	private BreakAction addUnresolvedBreakpoint(String unresolvedLocation, int isolateId) {
		BreakAction b = new BreakAction(unresolvedLocation);
		b.setEnabled(true);
		b.setSingleSwf(m_fileInfo.isSwfFilterOn());
		breakpointAdd(b);
		return b;
	}

	/**
	 * Try to resolve any breakpoints which have not yet been resolved.  We
	 * do this every time a new ABC or SWF is loaded.  NOTE: The return
	 * value does NOT indicate whether any breakpoints were resolved!  Rather,
	 * it indicates whether the operation was considered "successful."
	 * If a previously-unresolved breakpoint is now ambiguous, then that is
	 * an error, and the return value is 'false' (indicating that the
	 * debugger should halt).
	 *
	 * Resolve as well resolved BP set in other workers.
	 */
	private boolean resolveBreakpoints(StringBuilder sb) {
		int count = breakpointCount();
		boolean success = true;
		for (int i = 0; i < count; ++i) {
			BreakAction b = breakpointAt(i);
			try {
				tryResolveBreakpoint(b, sb);
			} catch (Exception e) // AmbiguousException or NullPointerException
			{
				Map<String, Object> args = new HashMap<String, Object>();
				args.put("breakpointNumber", Integer.toString(b.getId())); //$NON-NLS-1$
				args.put("expression", b.getBreakpointExpression()); //$NON-NLS-1$
				sb.append(getLocalizationManager().getLocalizedTextString("attemptingToResolve", args)); //$NON-NLS-1$
				sb.append(m_newline);
				sb.append(e.getMessage());
				sb.append(m_newline);
				success = false;
			}
		}
		return success;
	}

	/**
	 * Try to resolve one breakpoint.  We do this every time a new ABC or
	 * SWF is loaded.
	 * @param b  the breakpoint to resolve (it's okay if it's already resolved)
	 * @param sb a StringBuilder to which any messages for are appended;
	 *           to the user.
	 * @return true if the breakpoint is resolved
	 * @throws AmbiguousException
	 * @throws NullPointerException
	 */
	private boolean tryResolveBreakpoint(BreakAction b, StringBuilder sb) throws AmbiguousException {
		int status = b.getStatus();
		boolean resolved = (status == BreakAction.RESOLVED);
		if (status == BreakAction.UNRESOLVED || resolved) // we don't do anything for AMBIGUOUS
		{
			/* wait a bit if we are not halted */
			try {
				waitTilHalted(m_activeIsolate);
				int module = propertyGet(LIST_MODULE);
				int line = propertyGet(LIST_LINE);
				int isolateId;

				String arg = b.getBreakpointExpression();

				if (arg != null) {
					FileLocation[] fileLocations = parseLocationArg(module, line, arg, false);

					if (fileLocations.length == 1) {
						// whoo-hoo, it resolved!
						isolateId = fileLocations[0].getIsolateId();
						module = fileLocations[0].getModule();
						line = fileLocations[0].getLine();

						// use module SourceFile to denote the name of file in which we wish to set a breakpoint
						SourceFile f = m_fileInfo.getFile(module, isolateId);
//		                SourceFile f = m_fileInfo.getFakeFile(module);
						LocationCollection col = enableBreak(f, line, isolateId);
						if (col.isEmpty())
							throw new NullPointerException(getLocalizationManager().getLocalizedTextString("noExecutableCode")); //$NON-NLS-1$
						b.setLocations(col);

						Location l = col.first();
						SourceFile file = (l != null) ? l.getFile() : null;
						String funcName = (file == null) ? null : file.getFunctionNameForLine(m_session, l.getLine());

						Map<String, Object> args = new HashMap<String, Object>();
						String formatString;
						args.put("breakpointNumber", Integer.toString(b.getId())); //$NON-NLS-1$
						if (b.isSingleSwf() && file != null) {
							String filename = file.getName();
							filename = filename + "#" + file.getId(); //$NON-NLS-1$
							args.put("file", filename); //$NON-NLS-1$
						}
						args.put("line", l.getLine()); //$NON-NLS-1$

						if (funcName != null) {
							args.put("functionName", funcName); //$NON-NLS-1$
							formatString = "resolvedBreakpointToFunction"; //$NON-NLS-1$
						} else {
							formatString = "resolvedBreakpointToFile"; //$NON-NLS-1$
						}

						sb.append(getLocalizationManager().getLocalizedTextString(formatString, args));
						sb.append(m_newline);
						sb.append(m_newline);

						resolved |= true;
					}
				}
			} catch (NotConnectedException e) {
				// Ignore
			} catch (NoMatchException e) {
				// Okay, it's still not resolved; do nothing
			} catch (ParseException e) {
				// this shouldn't happen
				if (Trace.error)
					Trace.trace(e.toString());
			} catch (AmbiguousException e) {
				b.setStatus(BreakAction.AMBIGUOUS);
				throw e; // rethrow
			} catch (NullPointerException e) {
				b.setStatus(BreakAction.NOCODE);
				throw e; // rethrow
			}
		}
		return resolved;
	}

	/**
	 * Enable a breakpoint using the SourceFile as a template
	 * for the source file in which the breakpoint should be
	 * set.
	 */
	LocationCollection enableBreak(SourceFile f, int line, int isolateId) throws NotConnectedException {
		LocationCollection col = new LocationCollection();
		boolean singleSwfBreakpoint = m_fileInfo.isSwfFilterOn();
		SwfInfo swf = m_fileInfo.getSwfFilter();

		// If we have a swf filter enabled then we only want to
		// set a breakpoint in a specific swf not all of them
		if (singleSwfBreakpoint) {
			Location l = null;
			try {
				l = findAndEnableBreak(swf, f, line);
			} catch (InProgressException e) {

				if (Trace.error)
					Trace.trace(((swf == null) ? "SWF " : swf.getUrl()) + " still loading, breakpoint at " + f.getName() + ":" + line + " not set"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
			}
			col.add(l);
		} else {
			// walk all swfs looking to add this breakpoint
			SwfInfo[] swfs = m_fileInfo.getSwfs(isolateId);
			for (SwfInfo swf1 : swfs) {
				swf = swf1;
				if (swf != null) {
					Location l = null;
					try {
						l = findAndEnableBreak(swf, f, line);
					} catch (InProgressException e) {
						if (Trace.error)
							Trace.trace((swf.getUrl()) + " still loading, breakpoint at " + f.getName() + ":" + line + " not set"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
					}
					if (l != null)
						col.add(l);
				}
			}
		}
		return col;
	}

	/**
	 * Enable a breakpoint for a particular swf if the sourceFile
	 * is available in that swf.
	 * @param swf if null, then set the breakpoint in the given source file
	 *            otherwise try to locate a matching source file in given swf.
	 * @return null if the swf does not contain this source file
	 */
	Location findAndEnableBreak(final SwfInfo swf, final SourceFile file, final int line) throws NotConnectedException, InProgressException {
		if (swf == null) {
			return breakEnableRequest(file.getId(), line, swf.getIsolateId());
		}

		for (final SourceFile similarFile : getSimilarSourceFilesInSwf(swf, file)) {
			final Location location = breakEnableRequest(similarFile.getId(), line, swf.getIsolateId());
			if (location != null) {
				return location;
			}
		}

		return null;
	}

	private List<SourceFile> getSimilarSourceFilesInSwf(final SwfInfo info, final SourceFile file) throws InProgressException {
		if (!info.isProcessingComplete()) {
			// IDEA-94128. For unknown reason m_fileInfo.getSwfs() may contain "unknown" swf which is marked as not fully loaded,
			// but in fact containing full list of correct sources. At the same time correct swf doesn't contain sources.
			if (!(info instanceof DSwfInfo) || !((DSwfInfo) info).hasAllSource()) {
				throw new InProgressException();
			}
		}

		final List<SourceFile> result = new LinkedList<SourceFile>();

		for (final SourceFile each : info.getSourceList(m_session)) {
			if (m_fileInfo.filesMatch(file, each)) {
				result.add(each);
			}
		}

		return result;
	}

	/**
	 * Received when a breakpoint has been removed (or disabled)
	 */
	Location breakEnableRequest(int fileId, int line, int isolateId) throws NotConnectedException {
		Location l = null;
		try {
			l = m_session.getWorkerSession(isolateId).setBreakpoint(fileId, line);
		} catch (NoResponseException nre) {
			/**
			 * This could be that we have an old player which does not
			 * respond to this request, or that we have a new player and
			 * the location was not set.
			 */
		}
		return l;
	}

	/**
	 * Notification that a breakpoint has been removed (or disabled)
	 * at the CLI level and we may need to remove it at the session level
	 */
	void breakDisableRequest(LocationCollection col) throws NotConnectedException {
		// now let's comb the table looking to see if this breakpoint should
		// be removed at the session level.  Use the first entry as a template
		// for which location we are talking about.
		int at = 0;
		boolean hit = false;
		Location l = col.first();
		do {
			at = breakpointIndexOf(l, at);
			if (at > -1) {
				if (breakpointAt(at).isEnabled()) {
					hit = true;
					try {
						m_session.clearBreakpoint(l);
					} catch (NoResponseException nre) {
					}
				}
				else
					at++; // our location match is not enabled but let's continue after the hit
			}
		}
		while (at > -1 && !hit);
	}

	BreakAction breakpointAt(int at) {
		return m_breakpoints.elementAt(at);
	}

	boolean breakpointAdd(BreakAction a) {
		return m_breakpoints.add(a);
	}

	int breakpointCount() {
		return m_breakpoints.size();
	}

	/**
	 * Probe the table looking for the first breakpoint that
	 * matches our criteria.  Various permutations of the call are supported.
	 */
	int breakpointIndexOf(int fileId, int line) {
		return breakpointIndexOf(fileId, line, 0, true);
	}

	int breakpointIndexOf(int fileId, int line, int isolateId) {
		return breakpointIndexOf(fileId, line, 0, true, isolateId);
	}

	int breakpointIndexOf(Location l, int start) {
		return breakpointIndexOf(l.getFile().getId(), l.getLine(), start, true);
	}

	int enabledBreakpointIndexOf(Location l) {
		return breakpointIndexOf(l.getFile().getId(), l.getLine(), 0, false);
	}

	int breakpointIndexOf(int fileId, int line, int start, boolean includeDisabled) {
		return breakpointIndexOf(fileId, line, start, includeDisabled, m_activeIsolate);
	}

	int breakpointIndexOf(int fileId, int line, int start, boolean includeDisabled, int isolateId) {
		int size = breakpointCount();
		int hit = -1;
		for (int i = start; (hit < 0) && (i < size); i++) {
			BreakAction b = breakpointAt(i);
			if (b.locationMatches(fileId, line, isolateId) && (includeDisabled || b.isEnabled()))
				hit = i;
		}
		return hit;
	}

	// probe by identifier
	int breakpointIndexOf(int id) {
		int size = breakpointCount();
		int hit = -1;

		for (int i = 0; (hit < 0) && (i < size); i++) {
			BreakAction b = breakpointAt(i);
			if (b.getId() == id)
				hit = i;
		}
		return hit;
	}

	// access to display
	DisplayAction displayAt(int at) {
		return m_displays.get(at);
	}

	boolean displayAdd(DisplayAction a) {
		return m_displays.add(a);
	}

	void displayRemoveAt(int at) {
		m_displays.remove(at);
	}

	int displayCount() {
		return m_displays.size();
	}

	// probe by id
	int displayIndexOf(int id) {
		int size = displayCount();
		int hit = -1;

		for (int i = 0; (hit < 0) && (i < size); i++) {
			DisplayAction b = displayAt(i);
			if (b.getId() == id)
				hit = i;
		}
		return hit;
	}

	void doSet() throws NotConnectedException {
		/* wait a bit if we are not halted */
//		waitTilHalted();
		try {
			ValueExp exp = null;

			if (!hasMoreTokens())
				err(getLocalizationManager().getLocalizedTextString("setCommand")); //$NON-NLS-1$
			else {
				// pull the expression
				String s = restOfLine();

				// parse and eval which causes the assignment to occur...
				if ((exp = parseExpression(s)) == null)
					;  // failed parse

					// make sure contains assignment

				else if (!exp.containsAssignment())
					throw new IllegalAccessException("="); //$NON-NLS-1$

				else
					evalExpression(exp, m_activeIsolate);
			}
		} catch (IllegalAccessException iae) {
			Map<String, Object> args = new HashMap<String, Object>();
			args.put("operator", iae.getMessage()); //$NON-NLS-1$
			err(getLocalizationManager().getLocalizedTextString("missingOperator", args)); //$NON-NLS-1$
		} catch (NullPointerException npe) {
			err(getLocalizationManager().getLocalizedTextString("couldNotEvaluate")); //$NON-NLS-1$
		}
	}

	void doPrint() throws NotConnectedException {
		/* wait a bit if we are not halted */
//		waitTilHalted();
		try {
			Object result = null;
			boolean isLookupMembers = false;

			if (!hasMoreTokens()) {
				try {
					// attempt to get the last result
					result = m_exprCache.get("$"); //$NON-NLS-1$
				} catch (ArrayIndexOutOfBoundsException aib) {
					err(getLocalizationManager().getLocalizedTextString("commandHistoryIsEmpty")); //$NON-NLS-1$
					throw new NullPointerException();
				}
			} else {
				// pull the rest of the line
				String s = restOfLine();

				// first parse it, then attempt to evaluate the expression
				ValueExp expr = parseExpression(s);

				// make sure no assignment
				if (expr.containsAssignment())
					throw new IllegalAccessException();

				result = evalExpression(expr, m_activeIsolate).value;
				isLookupMembers = expr.isLookupMembers();
			}

			/* it worked, add it to the list */
			int which = m_exprCache.add(result);

			/* dump the output */
			StringBuilder sb = new StringBuilder();
			sb.append('$');
			sb.append(which);
			sb.append(" = "); //$NON-NLS-1$

			if (result instanceof Variable)
				result = ((Variable) result).getValue();

			if (result instanceof InternalProperty)
				sb.append(((InternalProperty) result).valueOf());
			else if (isLookupMembers)
				sb.append(result);
			else
				m_exprCache.appendVariableValue(sb, result, m_activeIsolate);

			out(sb.toString());

			m_repeatLine = m_currentLine;
		} catch (ArrayIndexOutOfBoundsException aio) {
			// $n not in range 0..size
			Map<String, Object> args = new HashMap<String, Object>();
			args.put("number", aio.getMessage()); //$NON-NLS-1$
			err(getLocalizationManager().getLocalizedTextString("historyHasNotReached", args)); //$NON-NLS-1$
		} catch (IllegalAccessException iae) {
			err(getLocalizationManager().getLocalizedTextString("noSideEffectsAllowed")); //$NON-NLS-1$
		} catch (NoSuchVariableException nsv) {
			Map<String, Object> args = new HashMap<String, Object>();
			args.put("variable", nsv.getMessage()); //$NON-NLS-1$
			err(getLocalizationManager().getLocalizedTextString("variableUnknown", args)); //$NON-NLS-1$
		} catch (NullPointerException npe) {
			err(getLocalizationManager().getLocalizedTextString("couldNotEvaluate")); //$NON-NLS-1$
		}
	}

	/* parse the given string and produce an error message as appropriate */
	ValueExp parseExpression(String s) {
		ValueExp expr = null;
		try {
			expr = m_exprCache.parse(s);
		} catch (ParseException pe) {
			// bad operation code
			err(getLocalizationManager().getLocalizedTextString("expressionCouldNotBeParsed") + " " + pe.getMessage()); //$NON-NLS-1$ //$NON-NLS-2$
		} catch (IOException io) {
			// thrown from parser
			err(getLocalizationManager().getLocalizedTextString("expressionCouldNotBeParsed") + " " + s); //$NON-NLS-1$ //$NON-NLS-2$
		}
		return expr;
	}

	/*
	 * Evaluate the given expression
	 */
	EvaluationResult evalExpression(ValueExp expr, int isolateId) {
		return evalExpression(expr, true, isolateId);
	}

	EvaluationResult evalExpression(ValueExp expr, boolean displayExceptions, int isolateId) {
		/* now we go off and evaluate the expression */
		EvaluationResult result = null;
		try {
			result = m_exprCache.evaluate(expr, isolateId);
		} catch (NoSuchVariableException nsv) {
			if (displayExceptions) {
				Map<String, Object> args = new HashMap<String, Object>();
				args.put("variable", nsv.getMessage()); //$NON-NLS-1$
				err(getLocalizationManager().getLocalizedTextString("variableUnknown", args)); //$NON-NLS-1$
			}
		} catch (NumberFormatException nfe) {
			if (displayExceptions) {
				Map<String, Object> args = new HashMap<String, Object>();
				args.put("value", nfe.getMessage()); //$NON-NLS-1$
				err(getLocalizationManager().getLocalizedTextString("couldNotConvertToNumber", args)); //$NON-NLS-1$
			}
		} catch (PlayerFaultException pfe) {
			if (displayExceptions)
				err(pfe.getMessage());
		} catch (PlayerDebugException e) {
			if (displayExceptions)
				err(e.getMessage());
		}

		return result;
	}

	/**
	 * Specialized dump of the contents of a movie clip tree, dumping
	 * all the _target properties of all MCs
	 * @throws NoResponseException
	 * @throws NotSuspendedException
	 */
	void doMcTree() throws NotConnectedException, NotSuspendedException, NoResponseException {
		/* wait a bit if we are not halted */
		waitTilHalted(m_activeIsolate);
		try {
			String var = nextToken();  // our variable reference
			String member = "_target"; //$NON-NLS-1$
			boolean printPath = false;
			Object result = null;
			String name = null;

			// did the user specify a member name
			if (hasMoreTokens()) {
				member = nextToken();

				// did they specify some other options
				while (hasMoreTokens()) {
					String option = nextToken();
					if (option.equalsIgnoreCase("fullpath")) //$NON-NLS-1$
						printPath = true;
				}
			}

			// first parse it, then attempt to evaluate the expression
			ValueExp expr = parseExpression(var);
			result = evalExpression(expr, m_activeIsolate).value;

			StringBuilder sb = new StringBuilder();

			if (result instanceof Variable) {
				name = ((Variable) result).getName();
				result = ((Variable) result).getValue();
			}

			// It worked an should now be a value that we can traverse looking for member properties

			if (result instanceof Value) {
				ArrayList<Object> e = new ArrayList<Object>();
				dumpTree(new HashMap<Object, String>(), e, name, (Value) result, member);

				// now sort according to our criteria
				treeResults(sb, e, member, printPath);
			} else
				throw new NoSuchVariableException(result);

			out(sb.toString());
		} catch (NoSuchVariableException nsv) {
			Map<String, Object> args = new HashMap<String, Object>();
			args.put("variable", nsv.getMessage()); //$NON-NLS-1$
			err(getLocalizationManager().getLocalizedTextString("variableUnknown", args)); //$NON-NLS-1$
		} catch (NullPointerException npe) {
			err(getLocalizationManager().getLocalizedTextString("couldNotEvaluate")); //$NON-NLS-1$
		}
	}

	/**
	 * Set the context from which info files
	 * and all other file releated commands
	 * will operate from.
	 *
	 * It no swf is given then we use the
	 * default mode which is to display all
	 * files from all swfs.  Files with identical
	 * names are only displayed once.
	 */
	void doViewSwf() {
		try {
			if (hasMoreTokens()) {
				String swfName = nextToken();
				Map<String, Object> args = new HashMap<String, Object>();
				args.put("swf", swfName); //$NON-NLS-1$
				if (m_fileInfo.setSwfFilter(swfName)) {
					out(getLocalizationManager().getLocalizedTextString("commandsLimitedToSpecifiedSwf", args)); //$NON-NLS-1$
				} else {
					err(getLocalizationManager().getLocalizedTextString("notValidSwf", args)); //$NON-NLS-1$
				}
			} else {
				m_fileInfo.setSwfFilter(null);
				out(getLocalizationManager().getLocalizedTextString("commandsApplyToAllSwfs")); //$NON-NLS-1$
			}
		} catch (NullPointerException npe) {
			err(getLocalizationManager().getLocalizedTextString("noActiveSession")); //$NON-NLS-1$
		}
	}

	/**
	 * Increment the frame context by 1 and display the new current frame.
	 */
	void doUp() throws PlayerDebugException {
		int num = propertyGet(DISPLAY_FRAME_NUMBER) + 1;
		try {
			propertyPut(DISPLAY_FRAME_NUMBER, num);

			dumpFrame(num);
			setListingToFrame(num);
		} catch (NullPointerException npe) {
			err(getLocalizationManager().getLocalizedTextString("noActiveSession")); //$NON-NLS-1$
		} catch (ArrayIndexOutOfBoundsException aie) {
			Map<String, Object> args = new HashMap<String, Object>();
			args.put("frameNumber", Integer.toString(num)); //$NON-NLS-1$
			err(getLocalizationManager().getLocalizedTextString("frameDoesNotExist", args)); //$NON-NLS-1$
		}
		m_repeatLine = m_currentLine;
	}

	/**
	 * Decrement the frame context by 1 and display the new current frame.
	 */
	void doDown() throws PlayerDebugException {
		int num = propertyGet(DISPLAY_FRAME_NUMBER) - 1;
		try {
			propertyPut(DISPLAY_FRAME_NUMBER, num);

			dumpFrame(num);
			setListingToFrame(num);
		} catch (NullPointerException npe) {
			err(getLocalizationManager().getLocalizedTextString("noActiveSession")); //$NON-NLS-1$
		} catch (ArrayIndexOutOfBoundsException aie) {
			Map<String, Object> args = new HashMap<String, Object>();
			args.put("frameNumber", Integer.toString(num)); //$NON-NLS-1$
			err(getLocalizationManager().getLocalizedTextString("frameDoesNotExist", args)); //$NON-NLS-1$
		}
		m_repeatLine = m_currentLine;
	}

	/**
	 * Set the frame context to the given number and display the new current frame.
	 */
	void doFrame() throws PlayerDebugException {
		int num = 0;  // frame 0 by default
		try {
			if (hasMoreTokens())
				num = nextIntToken();

			propertyPut(DISPLAY_FRAME_NUMBER, num);

			dumpFrame(num);
			setListingToFrame(num);
		} catch (NumberFormatException nfe) {
			Map<String, Object> args = new HashMap<String, Object>();
			args.put("token", m_currentToken); //$NON-NLS-1$
			err(getLocalizationManager().getLocalizedTextString("notANumber", args)); //$NON-NLS-1$
		} catch (ArrayIndexOutOfBoundsException aie) {
			Map<String, Object> args = new HashMap<String, Object>();
			args.put("frameNumber", Integer.toString(num)); //$NON-NLS-1$
			err(getLocalizationManager().getLocalizedTextString("frameDoesNotExist", args)); //$NON-NLS-1$
		} catch (NullPointerException npe) {
			err(getLocalizationManager().getLocalizedTextString("noActiveSession")); //$NON-NLS-1$
		}
	}

	// Displays information on the current frame
	// @throws ArrayIndexOutOfBoundsException if frame 'frm' doesn't exist
	void dumpFrame(int frm) throws PlayerDebugException, ArrayIndexOutOfBoundsException {
		StringBuilder sb = new StringBuilder();
		Frame[] ar = m_session.getWorkerSession(m_activeIsolate).getFrames();
		appendFrameInfo(sb, ar[frm], frm, false, true);

		sb.append(m_newline);
		out(sb.toString());
	}

	// set the listing command to point to the file/line of the given frame
	void setListingToFrame(int frameNum) throws PlayerDebugException {
		// set the module and line
		Frame[] frames = m_session.getWorkerSession(m_activeIsolate).getFrames();
		Frame ctx = frames[frameNum];

		Location l = ctx.getLocation();
		SourceFile f = l.getFile();
		int id = f.getId();
		int line = l.getLine();

		setListingPosition(id, line, ctx.getIsolateId());
	}

	// Set teh listing position to change to the given module and line number
	// also triggers emacs to move to this position if enabled
	void setListingPosition(int module, int line, int workerid) {
		propertyPut(LIST_MODULE, module);
		propertyPut(LIST_LINE, line);
		propertyPut(LIST_WORKER, workerid);

		// if we are running under emacs then dump out our new location
		if (m_fullnameOption) {
			SourceFile f = m_fileInfo.getFile(module);
			if (f != null) {
				StringBuilder sb = new StringBuilder();
				appendFullnamePosition(sb, f, line);
				sb.append('\n'); // not sure why this is needed but it seems to address some emacs bugs
				out(sb.toString());
			}
		}
	}

	/**
	 * Traverse the given variables dumping any Movieclips we find that
	 * contain a member called 'member'
	 * @throws NotConnectedException
	 * @throws NoResponseException
	 * @throws NotSuspendedException
	 */
	void dumpTree(Map<Object, String> tree, List<Object> e, String name, Value result, String member) throws NotSuspendedException, NoResponseException, NotConnectedException {
		// name for this variable
		if (name == null)
			name = ""; //$NON-NLS-1$

		// have we seen it already
		if (tree.containsKey(result))
			return;

		tree.put(result, name);  // place it

		// first iterate over our members looking for 'member'
		Value proto = result;
		boolean done = false;
		while (!done && proto != null) {
			Variable[] members = proto.getMembers(m_session);
			proto = null;

			// see if we find one called 'member'
			for (int i = 0; i < members.length; i++) {
				Variable m = members[i];
				String memName = m.getName();
				if (memName.equals(member) && !tree.containsKey(m)) {
					e.add(name);
					e.add(result);
					e.add(m);
					tree.put(m, name + "." + memName); //$NON-NLS-1$
					done = true;
				} else if (memName.equals("__proto__")) //$NON-NLS-1$
					proto = members[i].getValue();
			}
		}

		// now traverse other mcs recursively
		done = false;
		proto = result;
		while (!done && proto != null) {
			Variable[] members = proto.getMembers(m_session);
			proto = null;

			// see if we find an mc
			for (int i = 0; i < members.length; i++) {
				Variable m = members[i];
				String memName = m.getName();

				// if our type is NOT object or movieclip then we are done
				if (m.getValue().getType() != VariableType.OBJECT && m.getValue().getType() != VariableType.MOVIECLIP)
					;
				else if (m.getValue().getId() != Value.UNKNOWN_ID)
					dumpTree(tree, e, name, m.getValue(), member);
				else if (memName.equals("__proto__")) //$NON-NLS-1$
				{
					proto = m.getValue();
//					name = name + ".__proto__";
				}
			}
		}
	}

	StringBuilder treeResults(StringBuilder sb, List<Object> e, String memName, boolean fullName) {
		// walk the list
		Iterator<Object> i = e.iterator();
		while (i.hasNext()) {
			String name = (String) i.next();
			Variable key = (Variable) i.next();
			Variable val = (Variable) i.next();

//			sb.append(key);
//			sb.append(".");
//			sb.append(val.getName());
			if (fullName)
				sb.append(name);
			m_exprCache.appendVariableValue(sb, key.getValue(), key.getName(), key.getIsolateId());
			sb.append("."); //$NON-NLS-1$
			sb.append(memName);
			sb.append(" = "); //$NON-NLS-1$
			m_exprCache.appendVariableValue(sb, val.getValue(), val.getName(), val.getIsolateId());
			sb.append(m_newline);
		}
		return sb;
	}

	/**
	 * Output a source line of code to the output channel formatting nicely
	 */
	public void outputSource(int module, int line, String s) {
		StringBuilder sb = new StringBuilder();
		appendSource(sb, module, line, s, true);
		out(sb.toString());
	}

	void appendSource(StringBuilder sb, int module, int line, String s, boolean markCurrent) {
		String lineS = String.valueOf(line);
		int padding = 6 - lineS.length();

		// if we are the current location then mark it
		if (markCurrent && isCurrentLocation(module, line))
			sb.append('=');
		else
			sb.append(' ');
		sb.append(lineS);
		repeat(sb, ' ', padding);
		sb.append(s);
	}

	// see if this module, line combo is the current location
	boolean isCurrentLocation(int module, int line) {
		boolean yes = false;
		Location l = getCurrentLocation();
		if (l != null) {
			SourceFile file = l.getFile();
			if (file != null && file.getId() == module && l.getLine() == line)
				yes = true;
		}
		return yes;
	}

	private int parseLineNumber(String lineNumber) throws ParseException {
		try {
			return Integer.parseInt(lineNumber);
		} catch (NumberFormatException nfe) {
			Map<String, Object> args = new HashMap<String, Object>();
			args.put("token", lineNumber); //$NON-NLS-1$
			throw new ParseException(getLocalizationManager().getLocalizedTextString("expectedLineNumber", args), 0); //$NON-NLS-1$
		}
	}

	private int parseFileNumber(String fileNumber) throws ParseException {
		try {
			return Integer.parseInt(fileNumber);
		} catch (NumberFormatException nfe) {
			Map<String, Object> args = new HashMap<String, Object>();
			args.put("token", fileNumber); //$NON-NLS-1$
			throw new ParseException(getLocalizationManager().getLocalizedTextString("expectedFileNumber", args), 0); //$NON-NLS-1$
		}
	}

	private int parseWorkerId(String workerId) throws ParseException {
		try {
			return Integer.parseInt(workerId);
		} catch (NumberFormatException nfe) {
			Map<String, Object> args = new HashMap<String, Object>();
			args.put("token", workerId); //$NON-NLS-1$
			throw new ParseException(getLocalizationManager().getLocalizedTextString("expectedIsolateNumber", args), 0); //$NON-NLS-1$
		}
	}

	private int parseFileName(int isolateId, String partialFileName) throws NoMatchException, AmbiguousException {
		SourceFile[] sourceFiles = m_fileInfo.getFiles(partialFileName, isolateId);
		int nSourceFiles = sourceFiles.length;

		if (nSourceFiles == 0) {
			Map<String, Object> args = new HashMap<String, Object>();
			args.put("name", partialFileName); //$NON-NLS-1$
			throw new NoMatchException(getLocalizationManager().getLocalizedTextString("noSourceFileWithSpecifiedName", args)); //$NON-NLS-1$
		} else if (nSourceFiles > 1) {
			String s = getLocalizationManager().getLocalizedTextString("ambiguousMatchingFilenames") + m_newline; //$NON-NLS-1$
			for (int i = 0; i < nSourceFiles; i++) {
				SourceFile sourceFile = sourceFiles[i];
				s += " " + sourceFile.getName() + "#" + sourceFile.getId(); //$NON-NLS-1$ //$NON-NLS-2$
				if (i < nSourceFiles - 1)
					s += m_newline;
			}
			throw new AmbiguousException(s);
		}
		return sourceFiles[0].getId();
	}

	/* used by parseFunctionName */
	private static class ModuleFunctionPair implements Comparable<ModuleFunctionPair> {
		public ModuleFunctionPair(int moduleId, String functionName) {
			this.moduleId = moduleId;
			this.functionName = functionName;
		}

		public int moduleId;
		public String functionName;

		public int compareTo(ModuleFunctionPair other) {
			return functionName.compareTo(other.functionName);
		}
	}

	/**
	 * Parse a partial function name
	 * @param module the FIRST module to search; but we also search all the others if 'onlyThisModule' is false
	 * @return two ints: first is the module, and second is the line
	 */
	private int[] parseFunctionName(int module, String partialFunctionName, boolean onlyThisModule) throws NoMatchException, AmbiguousException {
		SourceFile m = m_fileInfo.getFile(module, m_activeIsolate);
		ArrayList<ModuleFunctionPair> functionNames = new ArrayList<ModuleFunctionPair>(); // each member is a ModuleFunctionPair

		appendFunctionNamesMatching(functionNames, m, partialFunctionName);

		if (functionNames.size() == 0) {
			if (!onlyThisModule) {
				// not found in the specified module; search all the other modules
				for (Isolate isolate : m_session.getWorkers()) {
					Iterator fileIter = m_fileInfo.getAllFiles(isolate.getId());
					while (fileIter.hasNext()) {
						SourceFile nextFile = (SourceFile) ((Map.Entry) fileIter.next()).getValue();
						if (nextFile != m) // skip the one file we searched at the beginning
						{
							appendFunctionNamesMatching(functionNames, nextFile, partialFunctionName);
						}
					}
				}
			}

			if (functionNames.size() == 0) {
				Map<String, Object> args = new HashMap<String, Object>();
				args.put("name", partialFunctionName); //$NON-NLS-1$
				throw new NoMatchException(getLocalizationManager().getLocalizedTextString("noFunctionWithSpecifiedName", args)); //$NON-NLS-1$
			}
		}

		if (functionNames.size() > 1) {
			ModuleFunctionPair[] functionNameArray = functionNames.toArray(new ModuleFunctionPair[functionNames.size()]);
			Arrays.sort(functionNameArray);

			String s = getLocalizationManager().getLocalizedTextString("ambiguousMatchingFunctionNames") + m_newline; //$NON-NLS-1$
			Map<String, Object> args = new HashMap<String, Object>();
			for (int i = 0; i < functionNameArray.length; i++) {
				//String moduleName = m_fileInfo.getFile(functionNameArray[i].moduleId).getName();
				String moduleName = m_fileInfo.getFile(functionNameArray[i].moduleId, m_activeIsolate).getName();
				String functionName = functionNameArray[i].functionName;
				args.put("functionName", functionName); //$NON-NLS-1$
				args.put("filename", moduleName + "#" + functionNameArray[i].moduleId); //$NON-NLS-1$ //$NON-NLS-2$
				s += " " + getLocalizationManager().getLocalizedTextString("functionInFile", args); //$NON-NLS-1$ //$NON-NLS-2$
				if (i < functionNameArray.length - 1)
					s += m_newline;
			}
			throw new AmbiguousException(s);
		}

		ModuleFunctionPair pair = functionNames.get(0);
		module = pair.moduleId;
		m = m_fileInfo.getFile(module, m_activeIsolate);
		int line = m.getLineForFunctionName(m_session, pair.functionName);
		return new int[]{module, line};
	}

	/**
	 * Find function names in this module that start with
	 * the specified string, and append them to the specified List.
	 *
	 * If partialName contains parenthesis then we look for an exact match
	 */
	private void appendFunctionNamesMatching(List<ModuleFunctionPair> functionNameList, SourceFile m, String partialName) {
		int exactHitAt = -1;

		// trim off the trailing parenthesis, if any
		int parenAt = partialName.lastIndexOf('(');
		if (parenAt > -1)
			partialName = partialName.substring(0, parenAt);

		String[] names = m.getFunctionNames(m_session);
		for (int i = 0; i < names.length; i++) {
			String functionName = names[i];
			if (functionName.equals(partialName)) {
				exactHitAt = i;
				break;
			} else if (functionName.startsWith(partialName))
				functionNameList.add(new ModuleFunctionPair(m.getId(), functionName));
		}

		// exact match?
		if (exactHitAt > -1) {
			functionNameList.clear();
			functionNameList.add(new ModuleFunctionPair(m.getId(), names[exactHitAt]));
		}
	}


	/**
	 * Parse arg to determine which file it specifies.
	 * Allowed formats: #29, MyApp.mxml, MyA
	 * A variety of exceptions are thrown for other formats.
	 */
	public int parseFileArg(int isolateId, int module, String arg) throws ParseException, AmbiguousException, NoMatchException {
        /* Special case: a location arg like :15 produces a file arg
           which is an empty string. */
		if (arg.length() == 0)
			return module;

		char firstChar = arg.charAt(0);

        /* The first character can't be 0-9 or '-'. */
		if (Character.isDigit(firstChar) || firstChar == '-') {
			Map<String, Object> args = new HashMap<String, Object>();
			args.put("token", arg); //$NON-NLS-1$
			throw new ParseException(getLocalizationManager().getLocalizedTextString("expectedFile", args), 0); //$NON-NLS-1$
		}
        /* If the first character is '#', the rest must be a file number. */
		else if (firstChar == '#') {
			if (isolateId == m_activeIsolate)
				return parseFileNumber(arg.substring(1));
			else {
				Map<String, Object> args = new HashMap<String, Object>();
				args.put("token", arg); //$NON-NLS-1$
				throw new NoMatchException(getLocalizationManager().getLocalizedTextString("noSuchFileOrFunction", args)); //$NON-NLS-1$
			}
		}
        /* Otherwise, assume beforeColon is a full or partial file name. */
		else {
			return parseFileName(isolateId, arg);
		}
	}

	/**
	 * Parse arg to determine which line it specifies.
	 * Allowed formats: 17, MyFunction, MyF
	 * A variety of exceptions are thrown for other formats.
	 */
	public int parseLineArg(int module, String arg) throws ParseException, AmbiguousException, NoMatchException {
        /* Special case: a location arg like #29: produces a line arg
           which is an empty string. */
		if (arg.length() == 0)
			return 1;

		char firstChar = arg.charAt(0);

        /* If the first character is 0-9 or '-', arg is assumed to be a line number. */
		if (Character.isDigit(firstChar) || firstChar == '-') {
			return parseLineNumber(arg);
		}
        /* The first character can't be '#'. */
		else if (firstChar == '#') {
			Map<String, Object> args = new HashMap<String, Object>();
			args.put("token", arg); //$NON-NLS-1$
			throw new ParseException(getLocalizationManager().getLocalizedTextString("expectedLineNumber", args), 0); //$NON-NLS-1$
		}
        /* Otherwise, assume arg is a full or partial function name. */
		else {
			int[] moduleAndLine = parseFunctionName(module, arg, true);
			return moduleAndLine[1];
		}
	}

	public FileLocation[] parseLocationArg(int module, int line, String arg) throws ParseException, AmbiguousException, NoMatchException {
		return parseLocationArg(module, line, arg, true);
	}

	/**
	 * Parse arg to figure out what module and line it specifies.
     *
	 * Allowed formats (assuming Button.as is file #29
	 * and the first line of MyFunction is line 17):
     *
	 * arg                     module      line
	 * 17                      no change   17
	 * MyFunction              no change   17
	 * MyF                     no change   17
	 * #29                     29          1
	 * Button.as               29          1
	 * Bu                      29          1
	 * #29:17                  29          17
	 * #29:MyFunction          29          17
	 * #29:MyF                 29          17
	 * Button.as:17            29          17
	 * Button.as:MyFunction    29          17
	 * Button.as:MyF           29          17
	 * Bu:17                   29          17
	 * Bu:MyFunction           29          17
	 * Bu:MyF                  29          17
     *
	 * A variety of exceptions are thrown for other formats.
	 * Added the possibility to indicate the worker id with this format:
	 *
	 * @2:#29:17
	 * @2:Button.as:17
	 * @2:Bu:17
	 * ...
	 * and all other previous combinations.
	 */
	public FileLocation[] parseLocationArg(int module, int line, String arg, boolean all) throws ParseException, AmbiguousException, NoMatchException {
		int workerId = -1;
		int colonAt = arg.indexOf(':');
		int wasFunc = 0;  // set to 1 if a function was named
		char firstChar = arg.charAt(0);
		final ArrayList<FileLocation> fileLocations = new ArrayList<FileLocation>();
		int currentIsolate;

        /* First deal with the case where arg doesn't contain a ':'
           and therefore might be specifying either a file or a line. */
		final Iterator<Integer> iterator = m_isolateState.keySet().iterator();
		if (colonAt < 0) {
            /* If the first character is 0-9 or '-', arg is assumed to be a line number. */
			if (Character.isDigit(firstChar) || firstChar == '-') {
				line = parseLineNumber(arg);
			}
            /* If the first character is a '#', what follows
               is assumed to be a file number. */
			else if (firstChar == '#') {
				workerId = m_activeIsolate;
				module = parseFileNumber(arg.substring(1));
				line = 1;

				final FileLocation fileLocation = new FileLocation(workerId + 1, module, line, wasFunc);
				fileLocations.add(fileLocation);
			}
            /* Otherwise, assume arg is a full or partial function name or file name. */
			else {
                /* Assume arg is a full or partial function in the specified module. */
				try {
					int[] moduleAndLine = parseFunctionName(module, arg, false);
					module = moduleAndLine[0];
					line = moduleAndLine[1];
					wasFunc = 1;

					final FileLocation fileLocation = new FileLocation(workerId + 1, module, line, wasFunc);
					fileLocations.add(fileLocation);
				} catch (NoMatchException pe) {
                /* If it isn't, assume arg is a full or partial file name. */
					if (all) {
						while (iterator.hasNext()) {
							currentIsolate = iterator.next();
							try {
								module = parseFileName(currentIsolate, arg);
								line = 1;

								final FileLocation fileLocation = new FileLocation(currentIsolate, module, line, wasFunc);
								fileLocations.add(fileLocation);
							} catch (NoMatchException ignored) {
							}
						}
					} else {
						module = parseFileName(m_activeIsolate, arg);
						line = 1;

						final FileLocation fileLocation = new FileLocation(m_activeIsolate, module, line, wasFunc);
						fileLocations.add(fileLocation);
					}
				}
			}
		}

        /* Now deal with the case where arg contains a '@' and / or ':',
           and is therefore specifying both a file and a line plus eventually the workerId. */
		else {
			workerId = m_activeIsolate;
			if (firstChar == '@') {
				workerId = parseWorkerId(arg.substring(1, colonAt));
				arg = arg.substring(colonAt + 1);
				colonAt = arg.indexOf(':');
				if (colonAt < 0) {
					// catch the 'file name' string
					Map<String, Object> args = new HashMap<String, Object>();
					args.put("token", arg); //$NON-NLS-1$
					throw new NoMatchException(getLocalizationManager().getLocalizedTextString("noSuchFileOrFunction", args)); //$NON-NLS-1$
				}

				module = parseFileArg(workerId + 1, module, arg.substring(0, colonAt));
				line = parseLineArg(module, arg.substring(colonAt + 1));
				wasFunc = (arg.substring(colonAt + 1).length() > 1 && Character.isDigit(arg.substring(colonAt + 1).charAt(0))) ? 0 : 1;


				final FileLocation fileLocation = new FileLocation(workerId + 1, module, line, wasFunc);
				fileLocations.add(fileLocation);
			} else if (all) {
				while (iterator.hasNext()) {
					currentIsolate = iterator.next();
					try {
						module = parseFileArg(currentIsolate, module, arg.substring(0, colonAt));
						line = parseLineArg(module, arg.substring(colonAt + 1));
						wasFunc = (arg.substring(colonAt + 1).length() > 1 && Character.isDigit(arg.substring(colonAt + 1).charAt(0))) ? 0 : 1;

						final FileLocation fileLocation = new FileLocation(currentIsolate, module, line, wasFunc);
						fileLocations.add(fileLocation);
					} catch (NoMatchException ignored) {
					}
				}
			} else {
				module = parseFileName(m_activeIsolate, arg.substring(0, colonAt));
				line = parseLineArg(module, arg.substring(colonAt + 1));
				wasFunc = (arg.substring(colonAt + 1).length() > 1 && Character.isDigit(arg.substring(colonAt + 1).charAt(0))) ? 0 : 1;

				final FileLocation fileLocation = new FileLocation(m_activeIsolate, module, line, wasFunc);
				fileLocations.add(fileLocation);
			}
		}

		if (fileLocations.size() == 0) {
			Map<String, Object> args = new HashMap<String, Object>();
			args.put("token", arg); //$NON-NLS-1$
			throw new NoMatchException(getLocalizationManager().getLocalizedTextString("noSuchFileOrFunction", args)); //$NON-NLS-1$
		}

		final Object[] original = fileLocations.toArray();
		return Arrays.copyOf(original, original.length, FileLocation[].class);
	}

	/**
	 * Print the context of a Variable
	 */
	void doWhat() throws NotConnectedException {
		/* wait a bit if we are not halted */
		waitTilHalted(m_activeIsolate);
		try {
			Object result = null;

			/* pull the rest of the line */
			String s = restOfLine();

			// first parse it, then attempt to evaluate the expression
			ValueExp expr = parseExpression(s);

			// make sure no assignment
			if (expr.containsAssignment())
				throw new IllegalAccessException();

			result = evalExpression(expr, m_activeIsolate).value;

			/* dump the output */
			StringBuilder sb = new StringBuilder();

			if (result instanceof Variable) {
				Variable v = (Variable) result;

				// if it has a path then display it!
				if (v.isAttributeSet(VariableAttribute.IS_LOCAL))
					s = getLocalizationManager().getLocalizedTextString("localVariable"); //$NON-NLS-1$
				else if (v.isAttributeSet(VariableAttribute.IS_ARGUMENT))
					s = getLocalizationManager().getLocalizedTextString("functionArgumentVariable"); //$NON-NLS-1$
				else if ((v instanceof VariableFacade) && (s = ((VariableFacade) v).getPath()) != null && s.length() > 0)
					;
				else
					s = "_global"; //$NON-NLS-1$

				sb.append(s);
			} else
				sb.append(getLocalizationManager().getLocalizedTextString("mustBeOnlyOneVariable")); //$NON-NLS-1$

			out(sb.toString());
		} catch (IllegalAccessException iae) {
			err(getLocalizationManager().getLocalizedTextString("noSideEffectsAllowed")); //$NON-NLS-1$
		} catch (NullPointerException npe) {
			err(getLocalizationManager().getLocalizedTextString("couldNotEvaluate")); //$NON-NLS-1$
		}
	}

	/*
     * We accept zero, one or two args for this command. Zero args
     * means list the next 10 line around the previous listing.  One argument
     * specifies a line and 10 lines are listed around that line.  Two arguments
     * with a command between specifies starting and ending lines.
     */
	void doList() {
        /* currentXXX may NOT be invalid! */

		int currentModule = propertyGet(LIST_MODULE);
		int currentLine = propertyGet(LIST_LINE);
		int listsize = propertyGet(LIST_SIZE);
		int workerid = propertyGet(LIST_WORKER);

		String arg1 = null;
		int module1 = currentModule;
		int line1 = currentLine;

		String arg2 = null;
		int line2 = currentLine;

		int numLines = 0;

		try {
			if (hasMoreTokens()) {
				arg1 = nextToken();

				if (arg1.equals("-")) //$NON-NLS-1$
				{
					// move back two times the listing size and if listsize is odd then move forward one
					line1 = line2 = line1 - (2 * listsize);
				} else {
					FileLocation[] fileLocations = parseLocationArg(currentModule, currentLine, arg1, false);

					if (fileLocations.length == 1) {
						module1 = fileLocations[0].getModule();
						line2 = line1 = fileLocations[0].getLine();
					}

					if (hasMoreTokens()) {
						arg2 = nextToken();
						line2 = parseLineArg(module1, arg2);
					}
				}
			}

//			System.out.println("1="+module1+":"+line1+",2=:"+line2);

			/**
			 * Check for a few error conditions, otherwise we'll write a listing!
			 */
			if (hasMoreTokens()) {
				err(getLocalizationManager().getLocalizedTextString("lineJunk")); //$NON-NLS-1$
			} else {
				int half = listsize / 2;
				SourceFile file = m_fileInfo.getFile(module1, workerid);
//				SourceFile file = m_fileInfo.getFakeFile(module1);
				numLines = file.getLineCount();

				int newLine;
				if (numLines == 1 && file.getLine(1).equals("")) //$NON-NLS-1$
				{
					// there's no source in the file at all!
					// this presumably means that the source file isn't in the current directory
					err(getLocalizationManager().getLocalizedTextString("sourceFileNotFound")); //$NON-NLS-1$
					newLine = currentLine;
				} else {
					// pressing return is ok, otherwise throw the exception
					if (line1 > numLines && arg1 != null)
						throw new IndexOutOfBoundsException();

					/* if no arg2 then user requested the next N lines around something */
					if (arg2 == null) {
						line2 = line1 + (half) - 1;
						line1 = line1 - (listsize - half);
					}

					/* adjust our range of lines to ensure we conform */
					if (line1 < 1) {
						/* shrink line 1, grow line2 */
						line2 += -(line1 - 1);
						line1 = 1;
					}

					if (line2 > numLines)
						line2 = numLines;

//				    System.out.println("1="+module1+":"+line1+",2="+module2+":"+line2+",num="+numLines+",half="+half);

					/* nothing to display */
					if (line1 > line2)
						throw new IndexOutOfBoundsException();

					/* now do it! */
					SourceFile source = m_fileInfo.getFile(module1, workerid);
					for (int i = line1; i <= line2; i++)
						outputSource(module1, i, source.getLine(i));

					newLine = line2 + half + (((listsize % 2) == 0) ? 1 : 2);  // add one if even, 2 for odd;
				}

				/* save away valid context */
				propertyPut(LIST_MODULE, module1);
				propertyPut(LIST_LINE, newLine);
				propertyPut(LIST_WORKER, workerid);
				m_repeatLine = "list";   /* allow repeated listing by typing CR */ //$NON-NLS-1$
			}
		} catch (IndexOutOfBoundsException iob) {
			String name = "#" + module1; //$NON-NLS-1$
			Map<String, Object> args = new HashMap<String, Object>();
			args.put("line", Integer.toString(line1)); //$NON-NLS-1$
			args.put("filename", name); //$NON-NLS-1$
			args.put("total", Integer.toString(numLines)); //$NON-NLS-1$
			err(getLocalizationManager().getLocalizedTextString("lineNumberOutOfRange", args)); //$NON-NLS-1$
		} catch (AmbiguousException ae) {
			err(ae.getMessage());
		} catch (NoMatchException nme) {
			// TODO [mmorearty]: try to find a matching source file
			err(nme.getMessage());
		} catch (NullPointerException npe) {
			err(getLocalizationManager().getLocalizedTextString("noFilesFound")); //$NON-NLS-1$
		} catch (ParseException pe) {
			err(pe.getMessage());
		}
	}

	/**
	 * Fire up a session or await a connection from the socket if no
	 * URI was specified.
	 */
	void doRun() throws IOException {
		if (m_session != null) {
			err(getLocalizationManager().getLocalizedTextString("sessionInProgress")); //$NON-NLS-1$
			return;
		}

		SessionManager mgr = Bootstrap.sessionManager();

		if (hasMoreTokens()) {
			if (!setLaunchURI(restOfLine()))
				return;
		}

		if (m_connectPort == null)
			mgr.startListening();

		try {
			if (m_connectPort != null) {
				out(getLocalizationManager().getLocalizedTextString("waitingToConnectToPlayer")); //$NON-NLS-1$
				m_session = mgr.connect(Integer.valueOf(m_connectPort), null);
			} else if (m_launchURI == null) {
				out(getLocalizationManager().getLocalizedTextString("waitingForPlayerToConnect")); //$NON-NLS-1$
				m_session = mgr.accept(null);
			} else {
				out(getLocalizationManager().getLocalizedTextString("launchingWithUrl") + m_newline + m_launchURI); //$NON-NLS-1$
				m_session = mgr.launch(m_launchURI, null, true, null, null);
			}

			// now see what happened
			if (m_session == null) {
				// shouldn't have gotten here
				throw new SocketTimeoutException();
			} else {
				out(getLocalizationManager().getLocalizedTextString("playerConnectedSessionStarting")); //$NON-NLS-1$
				initSession(m_session);

				// pause for a while during startup, don't let exceptions ripple outwards
				try {
					waitTilHalted(Isolate.DEFAULT_ID);
				} catch (Exception e) {
				}

				setInitialSourceFile();

				out(getLocalizationManager().getLocalizedTextString("setBreakpointsThenResume")); //$NON-NLS-1$
				setPromptState(InitialPromptState.DONE, Isolate.DEFAULT_ID);

				// now poke to see if the player is good enough
				try {
					if (m_session.getPreference(SessionManager.PLAYER_SUPPORTS_GET) == 0)
						err(m_newline + getLocalizationManager().getLocalizedTextString("warningNotAllCommandsSupported")); //$NON-NLS-1$
				}
				catch(Exception npe) {}
			}
		} catch (FileNotFoundException fnf) {
			Map<String, Object> args = new HashMap<String, Object>();
			args.put("uri", fnf.getLocalizedMessage()); //$NON-NLS-1$
			err(getLocalizationManager().getLocalizedTextString("fileDoesNotExist", args)); //$NON-NLS-1$
		} catch (SocketTimeoutException ste) {
			err(getLocalizationManager().getLocalizedTextString("failedToConnect")); //$NON-NLS-1$
		} catch (IOException io) {
			err(io.getLocalizedMessage());
		} finally {
			// turn off listening, to allow other session to connect
			if (m_connectPort == null)
				mgr.stopListening();
		}
	}

	/**
	 * Fire up a session or await a connection from the socket if no
	 * URI was specified.
	 */
	void doConnect() throws IOException {
		int port = 0;
		if (hasMoreTokens()) {
			try {
				port = nextIntToken();
			} catch (NumberFormatException ex) {
				err(ex.getLocalizedMessage());
			}
		} else {
			port = DProtocol.DEBUG_CONNECT_PORT;
		}

		if (port > 0) {
			m_connectPort = String.valueOf(port);
			doRun();
		}
	}

	/**
	 * When we begin a debugging session, it would be nice if the default file
	 * for the "list" command etc. was the user's main MXML application file.
	 * There is no good way to really figure out what that file is, but we can
	 * certainly take a guess.
	 */
	private void setInitialSourceFile() {
		int largestAuthoredId = -1;
		SourceFile[] files = m_fileInfo.getFileList(m_activeIsolate);
		for (int i = 0; i < files.length; ++i) {
			SourceFile sf = files[i];
			if (sf.getId() > largestAuthoredId && getFileType(sf) == AUTHORED_FILE)
				largestAuthoredId = sf.getId();
		}
		if (largestAuthoredId != -1)
			setListingPosition(largestAuthoredId, 1, m_activeIsolate);
	}

	private boolean setLaunchURI(String launchURI) {
		if (launchURI != null) {
			SessionManager mgr = Bootstrap.sessionManager();

			// If doing fdbunit, we always try to do launch(), even on platforms
			// that say they don't support it
			if (!mgr.supportsLaunch() && System.getProperty("fdbunit") == null) //$NON-NLS-1$
			{
				err(getLocalizationManager().getLocalizedTextString("manuallyLaunchPlayer")); //$NON-NLS-1$
				return false;
			}

			// check for special form of URI when in fullname mode, since we can't pass http: in this mode?!?
			if (m_fullnameOption) {
				if (launchURI.startsWith("//")) //$NON-NLS-1$
					launchURI = "http:" + launchURI; //$NON-NLS-1$
			}
		}

		m_launchURI = launchURI;
		return true;
	}

	// set the URI
	void doFile() {
		if (!hasMoreTokens())
			setLaunchURI(null);
		else
			setLaunchURI(restOfLine());
	}

	void doSource() {
		String name = ""; //$NON-NLS-1$
		try {
			name = nextToken();
			FileReader f = new FileReader(name);

			// push our current source onto the stack and open the new one
			pushStream(m_in);
			m_in = new LineNumberReader(f);
		} catch (NullPointerException npe) {
			err(getLocalizationManager().getLocalizedTextString("sourceCommandRequiresPath")); //$NON-NLS-1$
		} catch (NoSuchElementException nse) {
			err(getLocalizationManager().getLocalizedTextString("sourceCommandRequiresPath")); //$NON-NLS-1$
		} catch (FileNotFoundException fnf) {
			Map<String, Object> args = new HashMap<String, Object>();
			args.put("filename", name); //$NON-NLS-1$
			err(getLocalizationManager().getLocalizedTextString("fileNotFound", args)); //$NON-NLS-1$
		}
	}

	void listFault(String f) {
		StringBuilder sb = new StringBuilder();
		appendFaultTitles(sb);
		appendFault(sb, f);

		out(sb.toString());
	}

	void appendFault(StringBuilder sb, String f) {
		sb.append(f);

		int space = 30 - f.length();
		repeat(sb, ' ', space);

		boolean stop = m_faultTable.is(f, "stop"); //$NON-NLS-1$
		boolean print = m_faultTable.is(f, "print"); //$NON-NLS-1$

		sb.append(stop ? "Yes" : "No"); //$NON-NLS-1$ //$NON-NLS-2$
		repeat(sb, ' ', stop ? 0 : 1);

		repeat(sb, ' ', 5);

		sb.append(print ? "Yes" : "No"); //$NON-NLS-1$ //$NON-NLS-2$
		repeat(sb, ' ', print ? 0 : 1);

		// description
		repeat(sb, ' ', 7);

		String desc = m_faultTable.getDescription(f);
		sb.append(desc);
		sb.append(m_newline);
	}

	void appendFaultTitles(StringBuilder sb) {
		sb.append("Fault                         Stop    Print     Description" + m_newline); //$NON-NLS-1$
		sb.append("-----                         ----    -----     -----------" + m_newline); //$NON-NLS-1$
	}

	/**
	 * Controls the configuration of what occurs when a
	 * fault is encountered
	 */
	void doHandle() {
		// should be at least on arg
		if (!hasMoreTokens())
			err(getLocalizationManager().getLocalizedTextString("argumentRequired")); //$NON-NLS-1$
		else {
			// poor man's fix for supporting 'all' option
			String faultName = nextToken();
			Object[] names = new Object[]{faultName};

			// replace the single name with all of them
			if (faultName.equalsIgnoreCase("all")) //$NON-NLS-1$
				names = m_faultTable.names();

			// make sure we know about at least one
			if (!m_faultTable.exists((String) names[0]))
				err(getLocalizationManager().getLocalizedTextString("unrecognizedFault")); //$NON-NLS-1$
			else {
				if (!hasMoreTokens())
					listFault((String) names[0]);
				else {
					String action = null;
					try {
						while (hasMoreTokens()) {
							action = nextToken();
							for (int i = 0; i < names.length; i++)
								m_faultTable.action((String) names[i], action);
						}
					} catch (IllegalArgumentException iae) {
						Map<String, Object> args = new HashMap<String, Object>();
						args.put("action", action); //$NON-NLS-1$
						err(getLocalizationManager().getLocalizedTextString("unrecognizedAction", args)); //$NON-NLS-1$
					}
				}
			}
		}
	}

	/**
	 * Do the commands command!  This attaches a series of lines of text input by the user
	 * to a particular breakpoint, with the intention of exeuting these lines when the
	 * breakpoint is hit.
	 */
	void doCommands() throws IOException {
		try {
			int id = -1;
			if (hasMoreTokens())
				id = nextIntToken();
			else
				id = propertyGet(BPNUM);

			// get the breakpoint
			int at = breakpointIndexOf(id);
			BreakAction a = breakpointAt(at);

			// ready it
			a.clearCommands();
			a.setSilent(false);

			// now just pull the commands as they come while not end
			String line = null;
			boolean first = true;
			boolean isEnd = false;

			Map<String, Object> args = new HashMap<String, Object>();
			args.put("breakpointNumber", Integer.toString(id)); //$NON-NLS-1$
			out(getLocalizationManager().getLocalizedTextString("typeCommandsForBreakpoint", args)); //$NON-NLS-1$

			do {
				displayCommandPrompt();
				line = readLine().trim();
				isEnd = line.equalsIgnoreCase("end"); //$NON-NLS-1$

				if (!isEnd) {
					if (first && line.equalsIgnoreCase("silent")) //$NON-NLS-1$
						a.setSilent(true);
					else
						a.addCommand(line);
				}
				first = false;
			}
			while (!isEnd);
		} catch (IndexOutOfBoundsException iob) {
			Map<String, Object> args = new HashMap<String, Object>();
			args.put("breakpointNumber", m_currentToken); //$NON-NLS-1$
			err(getLocalizationManager().getLocalizedTextString("noBreakpointNumber", args)); //$NON-NLS-1$
		} catch (NumberFormatException nfe) {
			Map<String, Object> args = new HashMap<String, Object>();
			args.put("token", m_currentToken); //$NON-NLS-1$
			err(getLocalizationManager().getLocalizedTextString("badBreakpointNumber", args)); //$NON-NLS-1$
		} catch (NullPointerException npe) {
			err(getLocalizationManager().getLocalizedTextString("commandFailed")); //$NON-NLS-1$
		}
	}

	/**
	 * Apply or remove conditions to a breakpoint.
	 */
	void doCondition() throws IOException {
		try {
			// must have a breakpoint number
			int id = nextIntToken();

			// get the breakpoint
			int at = breakpointIndexOf(id);
			BreakAction a = breakpointAt(at);

			// no more parms means to clear it
			if (hasMoreTokens()) {
				// now just pull the commands as they come while not end
				String line = restOfLine();

				// build an expression and attach it to the breakpoint
				ValueExp exp = parseExpression(line);

				// warn about the assignment!
				if (exp.containsAssignment() && !yesNoQuery(getLocalizationManager().getLocalizedTextString("askExpressionContainsAssignment"))) //$NON-NLS-1$
					throw new IllegalAccessException("="); //$NON-NLS-1$

				a.setCondition(exp, line);
			} else {
				a.clearCondition();   // clear it
				Map<String, Object> args = new HashMap<String, Object>();
				args.put("breakpointNumber", Integer.toString(id)); //$NON-NLS-1$
				out(getLocalizationManager().getLocalizedTextString("breakpointNowUnconditional", args)); //$NON-NLS-1$
			}
		} catch (IllegalAccessException iae) {
			err(getLocalizationManager().getLocalizedTextString("breakpointNotChanged")); //$NON-NLS-1$
		} catch (IndexOutOfBoundsException iob) {
			Map<String, Object> args = new HashMap<String, Object>();
			args.put("breakpointNumber", m_currentToken); //$NON-NLS-1$
			err(getLocalizationManager().getLocalizedTextString("noBreakpointNumber", args)); //$NON-NLS-1$
		} catch (NumberFormatException nfe) {
			Map<String, Object> args = new HashMap<String, Object>();
			args.put("token", m_currentToken); //$NON-NLS-1$
			err(getLocalizationManager().getLocalizedTextString("badBreakpointNumber", args)); //$NON-NLS-1$
		} catch (NullPointerException npe) {
			err(getLocalizationManager().getLocalizedTextString("commandFailed")); //$NON-NLS-1$
		}
	}

	/**
	 * Request to add a new watchpoint
	 * This may result in one of two things happening
	 * (1) a new watchpoint could be added or
	 * (2) an existing watchpoint may be modified.
	 *
	 * The watch, awatch, and rwatch commands will set a watchpoint on the
	 * given expression. The different commands control the read/write aspect
	 * of the watchpoint.
	 *
	 * awatch will trigger a break if the expression is read or written.
	 * rwatch will trigger a break if the expression is read.
	 * watch will trigger a break if the expression is written.
	 */
	void doWatch(boolean read, boolean write) throws PlayerDebugException {
		try {
			if (read) {
				err("Only break-on-write watchpoints are supported."); //$NON-NLS-1$
				return;
			}

			StringBuilder sb = new StringBuilder();

			/* pull the rest of the line */
			String s = restOfLine();

			int flags = 3;
			if (read && write) flags = WatchKind.READWRITE;
			else if (read) flags = WatchKind.READ;
			else if (write) flags = WatchKind.WRITE;

			IsolateSession workerSession = m_session.getWorkerSession(m_activeIsolate);
			// snapshot of our existing list
			Watch[] list = workerSession.getWatchList();

			// We need to separate the front part the 'a.b' in 'a.b.c'
			// of the expression to resolve it into a variable
			// We usually get back a VariableFacade which contains
			// the context id (i.e the variable id) and the member name.
			ValueExp expr = parseExpression(s);
			VariableFacade result = (VariableFacade) (evalExpression(expr, m_activeIsolate).value);

			// extract the 2 pieces and get the raw variable.
			long varId = result.getContext(); // TODO fix this???  -mike
			String memberName = result.getName();
			Value v = workerSession.getValue(varId);

			// attempt to set.
			Watch w = m_session.setWatch(v, memberName, flags);
			if (w == null) {
				// failed
				Map<String, Object> args = new HashMap<String, Object>();
				args.put("expression", s); //$NON-NLS-1$
				err(getLocalizationManager().getLocalizedTextString("watchpointCouldNotBeSet", args)); //$NON-NLS-1$
			} else {
				// if modified then lists are same length
				// otherwise 1 will be added
				Watch[] newList = workerSession.getWatchList();
				if (newList.length == list.length) {
					// modified, lets locate the one that changed
					// and reset it
					int at = missingWatchpointIndexOf(newList);
					WatchAction a = null;
					try {
						a = watchpointAt(at);
					} catch (ArrayIndexOutOfBoundsException aio) {
						// this is pretty bad it means the player thinks we have a watchpoint
						// but we don't have a record of it.  So let's create a new one
						// and hope that we are now in sync with the player.
						a = new WatchAction(w);
					}

					// modify our view of the watchpoint
					int id = a.getId();
					a.resetWatch(w);

					Map<String, Object> args = new HashMap<String, Object>();
					args.put("watchpointNumber", Integer.toString(id)); //$NON-NLS-1$
					args.put("expression", s); //$NON-NLS-1$
					args.put("watchpointMode", getWatchpointModeString(a.getKind())); //$NON-NLS-1$
					sb.append(getLocalizationManager().getLocalizedTextString("changedWatchpointMode", args)); //$NON-NLS-1$
				} else {
					// newly added
					WatchAction a = new WatchAction(w);
					watchpointAdd(a);

					int which = a.getId();
					Map<String, Object> args = new HashMap<String, Object>();
					args.put("watchpointNumber", Integer.toString(which)); //$NON-NLS-1$
					args.put("expression", s); //$NON-NLS-1$
					sb.append(getLocalizationManager().getLocalizedTextString("createdWatchpoint", args)); //$NON-NLS-1$
				}
				out(sb.toString());
			}
		} catch (ArrayIndexOutOfBoundsException aio) {
			// We should really do some cleanup after this exception
			// since it most likely means we can't find the watchpoint
			// that was just modified, therefore our watchlists are
			// out of sync with those of the API.
			err(getLocalizationManager().getLocalizedTextString("badWatchpointNumber")); //$NON-NLS-1$
		} catch (NullPointerException npe) {
			err(getLocalizationManager().getLocalizedTextString("couldNotEvaluate")); //$NON-NLS-1$
		} catch (ClassCastException cce) {
			err(getLocalizationManager().getLocalizedTextString("couldNotResolveExpression")); //$NON-NLS-1$
		}
	}

	WatchAction watchpointAt(int at)			{ return m_watchpoints.get(at); }
	boolean		watchpointAdd(WatchAction a)	{ return m_watchpoints.add(a); 	}
	int			watchpointCount()				{ return m_watchpoints.size(); 	}

	int watchpointIndexOf(int id) {
		int size = watchpointCount();
		for (int i = 0; i < size; i++) {
			WatchAction b = watchpointAt(i);
			if (b.getId() == id)
				return i;
		}

		return -1;
	}

	void removeAllWatchpoints() throws NotConnectedException {
		while (watchpointCount() > 0)
			removeWatchpointAt(0);
	}

	void removeWatchpointAt(int at) throws NotConnectedException {
		WatchAction b = watchpointAt(at);
		boolean worked = false;

		try {
			worked = (m_session.clearWatch(b.getWatch()) == null) ? false : true;
		} catch (NoResponseException nre) {
		}

		if (!worked) {
			Map<String, Object> args = new HashMap<String, Object>();
			args.put("variable", b.getExpr()); //$NON-NLS-1$
			err(getLocalizationManager().getLocalizedTextString("couldNotFindWatchpoint", args)); //$NON-NLS-1$
		}

		// remove in any event
		m_watchpoints.remove(at);
	}

	String getWatchpointModeString(int flags) {
		switch (flags) {
			case 1:
				return getLocalizationManager().getLocalizedTextString("watchpointMode_read"); //$NON-NLS-1$
			case 2:
				return getLocalizationManager().getLocalizedTextString("watchpointMode_write"); //$NON-NLS-1$
			case 3:
				return getLocalizationManager().getLocalizedTextString("watchpointMode_readWrite"); //$NON-NLS-1$
		}
		return ""; //$NON-NLS-1$
	}

	/**
	 * Locate the index of a WatchAction that does not
	 * have a corresponding Watch in the given list.
	 *
	 * WARNING: this call can be very expensive but
	 * it is assumed that a.list and watchpointCount()
	 * are both small.
	 */
	int missingWatchpointIndexOf(Watch[] a) {
		int size = watchpointCount();
		int at = -1;
		for (int i = 0; i < size && at < 0; i++) {
			WatchAction action = watchpointAt(i);
			Watch w = action.getWatch();

			// now scan the list of watches looking for a hit
			int hit = -1;
			for (int j = 0; j < a.length && hit < 0; j++) {
				if (w == a[j])
					hit = j;
			}

			// can't find the watch object corresponding to our
			// watchpoint in list of session watches.
			if (hit < 0)
				at = i;
		}

		return at;
	}

	/**
	 * Display command
	 */
	void doDisplay() {
		try {
			if (!hasMoreTokens())
				doInfoDisplay();
			else {
				// followed by an expression (i.e. a line we just pull in)
				String s = restOfLine();

				// first parse it, then attempt to evaluate the expression
				ValueExp expr = parseExpression(s);

				// make sure no assignment
				if (expr.containsAssignment())
					throw new IllegalAccessException();

				// it worked so create a new DisplayAction and then add it in

				DisplayAction b = new DisplayAction(expr, s, m_activeIsolate);
				b.setEnabled(true);
				displayAdd(b);
			}
		} catch (IllegalAccessException iae) {
			err(getLocalizationManager().getLocalizedTextString("noSideEffectsAllowed")); //$NON-NLS-1$
		} catch (NullPointerException npe) {
			// already handled by parseExpression
		}
	}

	/**
	 * Remove auto-display expressions
	 */
	void doUnDisplay() throws IOException {
		try {
			if (!hasMoreTokens()) {
				// no args means delete all displays, last chance...
				if (yesNoQuery(getLocalizationManager().getLocalizedTextString("askDeleteAllAutoDisplay"))) //$NON-NLS-1$
				{
					int count = displayCount();
					for (int i = count - 1; i > -1; i--) {
						displayRemoveAt(i);
					}
				}
			} else {
				while (hasMoreTokens()) {
					int id = nextIntToken();
					int at = displayIndexOf(id);
					displayRemoveAt(at);
				}
			}
		} catch (IndexOutOfBoundsException iob) {
			Map<String, Object> args = new HashMap<String, Object>();
			args.put("displayNumber", m_currentToken); //$NON-NLS-1$
			err(getLocalizationManager().getLocalizedTextString("noDisplayNumber", args)); //$NON-NLS-1$
		} catch (NumberFormatException nfe) {
			Map<String, Object> args = new HashMap<String, Object>();
			args.put("token", m_currentToken); //$NON-NLS-1$
			err(getLocalizationManager().getLocalizedTextString("badDisplayNumber", args)); //$NON-NLS-1$
		} catch (NullPointerException npe) {
			err(getLocalizationManager().getLocalizedTextString("commandFailed")); //$NON-NLS-1$
		}
	}

	/**
	 * Enabled breakpoints and disaplays
	 */
	void doDisable() throws AmbiguousException, NotConnectedException {
		waitTilHalted(m_activeIsolate);

		try {
			if (!hasMoreTokens()) {
				disableAllBreakpoints();
			} else {
				// optionally specify  'display' or 'breakpoint'
				String arg = nextToken();
				int cmd = disableCommandFor(arg);
				int id = -1;
				if (cmd == CMD_DISPLAY) {
					doDisableDisplay();
				} else if (cmd == CMD_BREAK && !hasMoreTokens()) {
					disableAllBreakpoints();
				} else {
					if (cmd == CMD_BREAK)
						id = nextIntToken();  // ignore and get next number token
					else
						id = Integer.parseInt(arg);

					do {
						int at = breakpointIndexOf(id);
						disableBreakpointAt(at);

						if (hasMoreTokens())
							id = nextIntToken();
						else
							id = -1;

						// keep going till we're blue in the face; also note that we cache'd a copy of locations
						// so that breakpoint numbers are consistent.
					}
					while (id > -1);
				}
			}
		} catch (IndexOutOfBoundsException iob) {
			Map<String, Object> args = new HashMap<String, Object>();
			args.put("breakpointNumber", m_currentToken); //$NON-NLS-1$
			err(getLocalizationManager().getLocalizedTextString("noBreakpointNumber", args)); //$NON-NLS-1$
		} catch (NumberFormatException nfe) {
			Map<String, Object> args = new HashMap<String, Object>();
			args.put("token", m_currentToken); //$NON-NLS-1$
			err(getLocalizationManager().getLocalizedTextString("badBreakpointNumber", args)); //$NON-NLS-1$
		} catch (NullPointerException npe) {
			err(getLocalizationManager().getLocalizedTextString("commandFailed")); //$NON-NLS-1$
		}
	}

	void disableAllBreakpoints() throws NotConnectedException {
		// disables all breakpoints,
		int count = breakpointCount();
		for (int i = 0; i < count; i++)
			disableBreakpointAt(i);
	}

	// disable a breakpoint
	void disableBreakpointAt(int at) throws NotConnectedException {
		BreakAction a = breakpointAt(at);
		a.setEnabled(false);
		breakDisableRequest(a.getLocations());
	}

	void doDisableDisplay() {
		doEnableDisableDisplay(false);
	}

	void doEnableDisableDisplay(boolean enable) {
		try {
			if (!hasMoreTokens()) {
				// means do all!
				int size = displayCount();
				for (int i = 0; i < size; i++)
					displayAt(i).setEnabled(enable);
			} else {
				// read ids until no more
				while ((hasMoreTokens())) {
					int id = nextIntToken();
					int at = displayIndexOf(id);
					DisplayAction a = displayAt(at);
					a.setEnabled(enable);
				}
			}
		} catch (IndexOutOfBoundsException iob) {
			Map<String, Object> args = new HashMap<String, Object>();
			args.put("displayNumber", m_currentToken); //$NON-NLS-1$
			err(getLocalizationManager().getLocalizedTextString("noDisplayNumber", args)); //$NON-NLS-1$
		} catch (NumberFormatException nfe) {
			Map<String, Object> args = new HashMap<String, Object>();
			args.put("token", m_currentToken); //$NON-NLS-1$
			err(getLocalizationManager().getLocalizedTextString("badDisplayNumber", args)); //$NON-NLS-1$
		}
	}

	/**
	 * Enables breakpoints (forever, one-shot hit or auto delete) and displays
	 */
	void doEnable() throws AmbiguousException, NotConnectedException {
		waitTilHalted(m_activeIsolate);

		try {
			if (!hasMoreTokens()) {
				enableAllBreakpoints();
			} else {
				// optionally specify  'display' or 'breakpoint'
				String arg = nextToken();
				int cmd = enableCommandFor(arg);
				int id = -1;
				boolean autoDelete = false;
				boolean autoDisable = false;
				if (cmd == CMD_DISPLAY) {
					doEnableDisplay();
				} else if ((cmd == CMD_BREAK) && !hasMoreTokens()) {
					enableAllBreakpoints();
				} else {
					if (cmd == CMD_BREAK)
						id = nextIntToken();  // ignore and get next number token
					else if (cmd == CMD_DELETE) {
						autoDelete = true;
						id = nextIntToken();  // set and get next number token
					} else if (cmd == ENABLE_ONCE_CMD) {
						autoDisable = true;
						id = nextIntToken();  // set and get next number token
					} else
						id = Integer.parseInt(arg);

					boolean worked = true;
					do {
						int at = breakpointIndexOf(id);
						worked = enableBreakpointAt(at, autoDisable, autoDelete);

						if (hasMoreTokens())
							id = nextIntToken();
						else
							id = -1;

						// keep going till we're blue in the face; also note that we cache'd a copy of locations
						// so that breakpoint numbers are consistent.
					}
					while (worked && id > -1);

					if (!worked) {
						Map<String, Object> args = new HashMap<String, Object>();
						args.put("breakpointNumber", Integer.toString(id)); //$NON-NLS-1$
						err(getLocalizationManager().getLocalizedTextString("breakpointLocationNoLongerExists", args)); //$NON-NLS-1$
					}
				}
			}
		} catch (IndexOutOfBoundsException iob) {
			Map<String, Object> args = new HashMap<String, Object>();
			args.put("breakpointNumber", m_currentToken); //$NON-NLS-1$
			err(getLocalizationManager().getLocalizedTextString("noBreakpointNumber", args)); //$NON-NLS-1$
		} catch (NumberFormatException nfe) {
			Map<String, Object> args = new HashMap<String, Object>();
			args.put("token", m_currentToken); //$NON-NLS-1$
			err(getLocalizationManager().getLocalizedTextString("badBreakpointNumber", args)); //$NON-NLS-1$
		} catch (NullPointerException npe) {
			err(getLocalizationManager().getLocalizedTextString("commandFailed")); //$NON-NLS-1$
		}
	}

	void enableAllBreakpoints() throws NotConnectedException {
		// enables all breakpoints
		int count = breakpointCount();
		int tally = 0;
		for (int i = 0; i < count; i++)
			tally += enableBreakpointAt(i) ? 1 : 0;

		// mention that not all was good
		if (tally != count)
			err(getLocalizationManager().getLocalizedTextString("notAllBreakpointsEnabled")); //$NON-NLS-1$
	}

	// request to enable a breakpoint
	// @return false if we couldn't enable it.
	boolean enableBreakpointAt(int at) throws NotConnectedException {
		return enableBreakpointAt(at, false, false);
	}

	boolean enableBreakpointAt(int at, boolean autoDisable, boolean autoDelete) throws NotConnectedException {
		return enableBreakpoint(breakpointAt(at), autoDisable, autoDelete);
	}

	boolean enableBreakpoint(BreakAction a, boolean autoDisable, boolean autoDelete) throws NotConnectedException {
		boolean retval = false;
		final LocationCollection locations = a.getLocations();

		for (Iterator<Location> iterator = locations.iterator(); iterator.hasNext(); ) {
			Location l = iterator.next();

			if (l != null) {
				LocationCollection col = enableBreak(l.getFile(), l.getLine(), l.getIsolateId());
				if (!col.isEmpty()) {
					a.setEnabled(true);
					a.setAutoDisable(autoDisable);
					a.setAutoDelete(autoDelete);
					a.setSingleSwf(false);
					a.setStatus(BreakAction.RESOLVED);
					retval |= true;
				}
			}
		}
		return retval;
	}

	void doEnableDisplay() {
		doEnableDisableDisplay(true);
	}

	/* Print working directory */
	void doPWD() {
		out(System.getProperty("user.dir")); //$NON-NLS-1$
	}

	/* Display or change current file */
	void doCF() {
		try {
			int module = propertyGet(LIST_MODULE);
			int currentLine = propertyGet(LIST_LINE);
			int worker = propertyGet(LIST_WORKER);

			if (hasMoreTokens()) {
				String arg = nextToken();
				module = parseFileArg(worker, module, arg);
				currentLine = 1;
				setListingPosition(module, currentLine, worker);
			}

			SourceFile sourceFile = m_fileInfo.getFile(module, worker);
			StringBuilder sb = new StringBuilder();
			sb.append(sourceFile.getName());
			sb.append('#');
			sb.append(sourceFile.getId());
			sb.append(':');
			sb.append(currentLine);
			out(sb.toString());
		} catch (NullPointerException npe) {
			err(getLocalizationManager().getLocalizedTextString("noFilesFound")); //$NON-NLS-1$
		} catch (ParseException pe) {
			err(pe.getMessage());
		} catch (AmbiguousException ae) {
			err(ae.getMessage());
		} catch (NoMatchException nme) {
			err(nme.getMessage());
		}
	}

	/* Terminates current debugging sesssion */
	void doKill() throws IOException {
		if (m_session == null)
			err(getLocalizationManager().getLocalizedTextString("programNotBeingRun")); //$NON-NLS-1$
		else {
			if (yesNoQuery(getLocalizationManager().getLocalizedTextString("askKillProgram"))) //$NON-NLS-1$
				exitSession();
		}
	}

	/* Terminates fdb */
	boolean doQuit() throws IOException {
		boolean quit = false;

		// no session, no questions
		if (m_session == null)
			quit = true;
		else {
			quit = yesNoQuery(getLocalizationManager().getLocalizedTextString("askProgramIsRunningExitAnyway")); //$NON-NLS-1$
			if (quit)
				exitSession();
		}
		return quit;
	}

	/* (non-Javadoc)
	 * @see flash.tools.debugger.SourceLocator#locateSource(java.lang.String, java.lang.String, java.lang.String)
	 */
	public InputStream locateSource(String path, String pkg, String name) {
		File f = null;
		boolean exists = false;
		String pkgPlusName;

		if ((pkg != null && pkg.length() > 0))
			pkgPlusName = pkg + File.separator + name;
		else
			pkgPlusName = null;

		Iterator<String> iter = m_sourceDirectories.iterator();
		while (iter.hasNext()) {
			String dir = iter.next();

			// new File("", filename) searches the root dir -- that's not what we want!
			if (dir.equals("")) //$NON-NLS-1$
				dir = "."; //$NON-NLS-1$

			// look for sourcedir\package\filename
			if (pkgPlusName != null) {
				f = new File(dir, pkgPlusName);
				exists = f.exists();
			}

			// look for sourcedir\filename
			if (!exists) {
				f = new File(dir, name);
				exists = f.exists();
			}

			if (exists) {
				try {
					return new FileInputStream(f);
				} catch (FileNotFoundException e) {
					e.printStackTrace(); // shouldn't happen
				}
			}
		}

		return null;
	}

	/* (non-Javadoc)
	 * @see flash.tools.debugger.SourceLocator#getChangeCount()
	 */
	public int getChangeCount() {
		return m_sourceDirectoriesChangeCount;
	}

	private void doDirectory() throws IOException {
		if (hasMoreTokens()) {
			// File.separator is ";" on Windows or ":" on Mac
			StringTokenizer dirs = new StringTokenizer(restOfLine(), File.pathSeparator);
			int insertPos = 0;

			while (dirs.hasMoreTokens()) {
				String dir = dirs.nextToken();
				if (dir.length() > 2 && dir.charAt(0) == '"' && dir.charAt(dir.length() - 1) == '"')
					dir = dir.substring(1, dir.length() - 1);
				dir = dir.trim();
				if (dir.length() > 0) {
					// For Unix and Mac, we want to escape "~" and "$HOME"
					if (!System.getProperty("os.name").toLowerCase().startsWith("windows")) //$NON-NLS-1$ //$NON-NLS-2$
					{
						// If the string starts with "~", or contains any environment variables
						// such as "$HOME", we need to escape those
						if (dir.matches("^.*[~$].*$")) //$NON-NLS-1$
						{
							try {
								Process p = Runtime.getRuntime().exec(
										new String[]{"/bin/sh", "-c", "echo " + dir}); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
								BufferedReader r = new BufferedReader(new InputStreamReader(p.getInputStream()));
								String line = r.readLine();
								if (line != null) {
									line = line.trim();
									if (line.length() > 0)
										dir = line;
								}
							} catch (IOException e) {
								// ignore
							}
						}
					}

					try {
						dir = new File(dir).getCanonicalPath();
						m_sourceDirectories.add(insertPos++, dir);
					} catch (IOException e) {
						err(e.getMessage());
					}
				}
			}
			++m_sourceDirectoriesChangeCount;
		} else {
			if (yesNoQuery(getLocalizationManager().getLocalizedTextString("askReinitSourcePath"))) //$NON-NLS-1$
			{
				initSourceDirectoriesList();
			}
		}

		doShowDirectories();
	}

	public void initSourceDirectoriesList() {
		m_sourceDirectories.clear();
		File flexHome = getFlexHomeDirectory();
		if (flexHome != null) {
			try {
				File projectsDir = new File(flexHome, "frameworks/projects"); //$NON-NLS-1$
				File[] files = projectsDir.listFiles();
				if (files != null) {
					for (int i = 0; i < files.length; ++i) {
						if (files[i].isDirectory()) {
							File srcDir = new File(files[i], "src"); //$NON-NLS-1$
							if (srcDir.isDirectory()) {
								m_sourceDirectories.add(srcDir.getCanonicalPath());
							}
						}
					}
				}
			} catch (IOException e) {
				// ignore
			}
		}
		++m_sourceDirectoriesChangeCount;
	}

	/**
	 * Returns the Flex home directory.  This is based on the <code>application.home</code>
	 * Java system property if present, or the current directory otherwise.
	 * This directory is one up from the "bin" and "lib" directories.  For example,
	 * <code>&lt;flexhome&gt;/lib/fdb.jar</code> can be used to refer to the fdb jar.
	 */
	protected File getFlexHomeDirectory() {
		if (!m_initializedFlexHomeDirectory) {
			m_initializedFlexHomeDirectory = true;
			m_flexHomeDirectory = new File("."); // default in case the following logic fails //$NON-NLS-1$
			String flexHome = System.getProperty("application.home"); //$NON-NLS-1$
			if (flexHome != null && flexHome.length() > 0) {
				try {
					m_flexHomeDirectory = new File(flexHome).getCanonicalFile();
				} catch (IOException e) {
					// ignore
				}
			}
		}

		return m_flexHomeDirectory;
	}

	protected String getenv(String var) {
		String[] cmd;
		if (System.getProperty("os.name").toLowerCase().startsWith("windows")) //$NON-NLS-1$ //$NON-NLS-2$
		{
			cmd = new String[]{"cmd.exe", "/c", "echo", "%" + var + "%"}; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
		} else {
			cmd = new String[]{"/bin/sh", "-c", "echo $" + var}; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
		}

		try {
			Process p = Runtime.getRuntime().exec(cmd);
			BufferedReader r = new BufferedReader(new InputStreamReader(p.getInputStream()));
			String line = r.readLine();
			if (line != null && line.length() > 0) {
				return line;
			}
		} catch (IOException e) {
			// ignore
		}

		return null;
	}

	private void doCatch() throws NotConnectedException, NotSuspendedException, NoResponseException {
		/* wait a bit if we are not halted */
		waitTilHalted(m_activeIsolate);

		String typeToCatch = null;

		/* currentXXX may NOT be invalid! */
		if (!hasMoreTokens()) {
			err("Catch requires an exception name."); //$NON-NLS-1$
			return;
		}

		typeToCatch = nextToken();
		if (typeToCatch == null || typeToCatch.length() == 0) {
			err("Illegal argument"); //$NON-NLS-1$
			return;
		}

		Value type = null;
		if (typeToCatch.equals("*")) //$NON-NLS-1$
		{
			typeToCatch = null;
		} else {
			type = getSession().getWorkerSession(m_activeIsolate).getGlobal(typeToCatch);
			if (type == null) {
				err("Type not found."); //$NON-NLS-1$
				return;
			}

			String typeName = type.getTypeName();
			int at = typeName.indexOf('@');
			if (at != -1)
				typeName = typeName.substring(0, at);
			if (!typeName.endsWith("$")) //$NON-NLS-1$
			{
				err("Not a type: " + type); //$NON-NLS-1$
				return;
			}
		}

		CatchAction c;
		try {
			c = addCatch(typeToCatch);
		} catch (NotSupportedException e) {
			e.printStackTrace();
			return;
		}

		Map<String, Object> args = new HashMap<String, Object>();
		args.put("id", c.getId()); //$NON-NLS-1$
		c.getId();
	}

	private CatchAction addCatch(String typeToCatch) throws NotSupportedException, NoResponseException {
		CatchAction c = new CatchAction(typeToCatch);
		catchpointAdd(c);
		return c;
	}

	CatchAction catchpointAt(int at)			{ return m_catchpoints.get(at); }
	int			catchpointCount()				{ return m_catchpoints.size(); 	}

	boolean catchpointAdd(CatchAction a) throws NotSupportedException, NoResponseException {
		if (catchpointCount() == 0)
			getSession().getWorkerSession(m_activeIsolate).breakOnCaughtExceptions(true);

		return m_catchpoints.add(a);
	}

	int catchpointIndexOf(int id) {
		int size = catchpointCount();
		for (int i = 0; i < size; i++) {
			CatchAction c = catchpointAt(i);
			if (c.getId() == id)
				return i;
		}

		return -1;
	}

	void removeAllCatchpoints() throws NotConnectedException {
		while (catchpointCount() > 0)
			removeCatchpointAt(0);
	}

	void removeCatchpointAt(int at) throws NotConnectedException {
		// remove in any event
		m_catchpoints.remove(at);

		if (catchpointCount() == 0) {
			try {
				getSession().getWorkerSession(m_activeIsolate).breakOnCaughtExceptions(false);
			} catch (NotSupportedException e) {
			} catch (NoResponseException e) {
			}
		}
	}

	void doUnknown(String s) {
		doUnknown("", s);
	} //$NON-NLS-1$

	void doUnknown(String what, String s) {
		Map<String, Object> args = new HashMap<String, Object>();
		String formatString;
		args.put("command", s); //$NON-NLS-1$
		if (what == null || what.equals("")) //$NON-NLS-1$
		{
			formatString = "unknownCommand"; //$NON-NLS-1$
			args.put("commandCategory", what); //$NON-NLS-1$
		} else {
			formatString = "unknownSubcommand"; //$NON-NLS-1$
		}
		err(getLocalizationManager().getLocalizedTextString(formatString, args));
	}

	/**
	 * Process the incoming debug event queue
	 */
	void processEvents() throws NotConnectedException {
		boolean requestResume = false;
		int breakIsolate = Isolate.DEFAULT_ID;
		boolean requestHalt = getIsolateState(breakIsolate).m_requestHalt;

		while (m_session != null && m_session.getEventCount() > 0) {
			DebugEvent e = m_session.nextEvent();

			if (e instanceof TraceEvent) {
				dumpTraceLine(e.information);
			} else if (e instanceof SwfLoadedEvent) {
				handleSwfLoadedEvent((SwfLoadedEvent) e);
			} else if (e instanceof SwfUnloadedEvent) {
				handleSwfUnloadedEvent((SwfUnloadedEvent) e);
			} else if (e instanceof IsolateCreateEvent) {
				handleIsolateCreateEvent((IsolateCreateEvent) e);
			} else if (e instanceof IsolateExitEvent) {
				handleIsolateExitEvent((IsolateExitEvent) e);
			} else if (e instanceof BreakEvent) {
				// store away isolate information
				breakIsolate = ((BreakEvent) e).isolateId;
				m_breakIsolates.add(breakIsolate);

				/** If this break event is due to a freshly loaded swf,
				 * we need to mark it for showing prompt. */
				int reason = SuspendReason.Unknown;
				try {
					reason = m_session.getWorkerSession(breakIsolate).suspendReason();
				} catch (PlayerDebugException pde) {
				}
				if (reason == SuspendReason.ScriptLoaded) {
					setPromptState(InitialPromptState.NEVER_SHOWN, breakIsolate);
				}

				break;
			} else if (e instanceof FileListModifiedEvent) {
				// we ignore this
			} else if (e instanceof FunctionMetaDataAvailableEvent) {
				// we ignore this
			} else if (e instanceof FaultEvent) {
				breakIsolate = ((FaultEvent) e).isolateId;
				m_breakIsolates.add(breakIsolate);
				if (handleFault((FaultEvent) e))
					requestResume = true;
				else
					requestHalt = true;
				break;
			} else {
				Map<String, Object> args = new HashMap<String, Object>();
				args.put("type", e); //$NON-NLS-1$
				args.put("info", e.information); //$NON-NLS-1$
				err(getLocalizationManager().getLocalizedTextString("unknownEvent", args)); //$NON-NLS-1$
			}
		}

		// only if we have processed a fault which requested a resume and no other fault asked for a break
		// and we are suspended and it was due to us that the stop occurred!
		IsolateSession workerSession = null;
		if (m_session != null)
			workerSession = m_session.getWorkerSession(breakIsolate);
		if (requestResume && !requestHalt && workerSession != null &&
				workerSession.isSuspended() && workerSession.suspendReason() == SuspendReason.Fault)
			getIsolateState(breakIsolate).m_requestResume = true;
	}

	private void handleIsolateExitEvent(IsolateExitEvent e) {
		dumpIsolateExitLine(e);
	}

	private void handleIsolateCreateEvent(IsolateCreateEvent e) {
		dumpIsolateCreatedLine(e);
	}

	void dumpIsolateCreatedLine(IsolateCreateEvent e) {
		StringBuilder sb = new StringBuilder();
		sb.append(getLocalizationManager().getLocalizedTextString("linePrefixWhenWorkerCreated")); //$NON-NLS-1$
		sb.append(' ');
		sb.append(e.isolate.getId() - 1);
		out(sb.toString());
	}

	void dumpIsolateExitLine(IsolateExitEvent e) {
		StringBuilder sb = new StringBuilder();
		sb.append(getLocalizationManager().getLocalizedTextString("linePrefixWhenWorkerExit")); //$NON-NLS-1$
		sb.append(' ');
		sb.append(e.isolate.getId() - 1);
		out(sb.toString());
	}


	/**
	 * Our logic for handling a break condition.
	 *
	 * @return some hit breakpoint requested silence, shhhh!
	 */
	boolean processBreak(boolean postStep, StringBuilder sb, int isolateId) throws NotConnectedException, SuspendedException, IOException, NotSupportedException, NotSuspendedException, NoResponseException {
		Location l = getCurrentLocationIsolate(isolateId);
		if (l == null || l.getFile() == null)
			return false;

		int fileId = l.getFile().getId();
		int line = l.getLine();
		boolean isSilent = false;
		boolean bpHit = false;
		boolean stoppedDueToBp = false;

		int count = breakpointCount();
		boolean[] markedForRemoval = new boolean[count];
		DebugCLIIsolateState state = getIsolateState(isolateId);
		boolean previousResume = state.m_requestResume;
		IsolateSession workerSession = m_session.getWorkerSession(isolateId);
		for (int i = 0; i < count; i++) {
			BreakAction a = breakpointAt(i);
			if (a.locationMatches(fileId, line, isolateId)) {
				/**
				 * Note that it appears that we stopped due to hitting a hard breakpoint
				 * Now if the breakpoint is conditional it may eval to false, meaning we
				 * won't stop here, otherwise we will process the breakpoint.
				 */
				stoppedDueToBp = (workerSession.suspendReason() == SuspendReason.Breakpoint);
				if (shouldBreak(a, fileId, line, isolateId)) {
					// its a hit
					bpHit = true;
					a.hit();
					isSilent = (isSilent) ? true : a.isSilent();

					// autodelete, autodisable
					if (a.isAutoDisable())
						disableBreakpointAt(i);

					if (a.isAutoDelete())
						markedForRemoval[i] = true;

					// now issue any commands that are attached to the breakpoint
					int n = a.getCommandCount();
					for (int j = 0; j < n; j++)
						issueCommand(a.commandAt(j), sb);
				}
			}
		}

		// kill them backwards so our i is acurate
		for (int i = markedForRemoval.length - 1; i > -1; i--)
			if (markedForRemoval[i])
				removeBreakpointAt(i);

		/**
		 * Now we should request to resume only if it was due to
		 * breakpoints that were hit.
		 *
		 * For the first case, we hit a conditional breakpoint that
		 * eval'd to false, resulting in bpHit == false.  Thus we
		 * want to resume and additionally if we were stepping, we'd
		 * like to do so 'softly' that is without loosing the stepping
		 * information on the Player.
		 *
		 * For the 2nd case, we hit a breakpoint and we executed
		 * commands that resulted in a m_requestResume.
		 */
		if (stoppedDueToBp && !bpHit) {
			state.m_requestResume = true;
			state.m_stepResume = postStep;    // resume without losing our stepping
			isSilent = true;            // do so quietly
		} else if (stoppedDueToBp && bpHit && state.m_requestResume && !previousResume) {
			state.m_requestResume = true;
			state.m_stepResume = postStep;    // resume as we would
			processDisplay(sb);
		}

		// If we aren't continuing, then show display variables
		if (!state.m_requestResume)
			processDisplay(sb);

//		System.out.println("processBreak stopDueToBp="+stoppedDueToBp+",bpHit="+bpHit+",postStep="+postStep+",reason="+suspendReason());

		return isSilent;
	}

	// iterate through our display list entries
	void processDisplay(StringBuilder sb) {
		int count = displayCount();
		for (int i = 0; i < count; i++) {
			DisplayAction a = displayAt(i);
			if (a.isEnabled()) {
				try {
					sb.append(a.getId());
					sb.append(": "); //$NON-NLS-1$
					sb.append(a.getContent());
					sb.append(" = "); //$NON-NLS-1$

					// command[0] contains our expression, so first we parse it, evalulate it then print it
					Object result = m_exprCache.evaluate(a.getExpression(), a.getIsolateId()).value;

					if (result instanceof Variable)
						m_exprCache.appendVariableValue(sb, ((Variable) result).getValue(), a.getIsolateId());

					else if (result instanceof Value)
						m_exprCache.appendVariableValue(sb, (Value) result, a.getIsolateId());

					else if (result instanceof InternalProperty)
						sb.append(((InternalProperty) result).valueOf());

					else
						sb.append(result);

					sb.append(m_newline);
				} catch (NoSuchVariableException nsv) {
					Map<String, Object> args = new HashMap<String, Object>();
					args.put("variable", nsv.getMessage()); //$NON-NLS-1$
					sb.append(getLocalizationManager().getLocalizedTextString("variableUnknown", args)); //$NON-NLS-1$
					sb.append(m_newline);
				} catch (NumberFormatException nfe) {
					Map<String, Object> args = new HashMap<String, Object>();
					args.put("value", nfe.getMessage()); //$NON-NLS-1$
					sb.append(getLocalizationManager().getLocalizedTextString("couldNotConvertToNumber", args)); //$NON-NLS-1$
					sb.append(m_newline);
				} catch (PlayerFaultException pfe) {
					sb.append(pfe.getMessage() + m_newline);
				} catch (PlayerDebugException e) {
					sb.append(e.getMessage() + m_newline);
				} catch (NullPointerException npe) {
					sb.append(getLocalizationManager().getLocalizedTextString("couldNotEvaluate")); //$NON-NLS-1$
				}
			}
		}
	}

	/**
	 * Determines if the given BreakAction requests a halt given the file
	 * line and optionally a conditional to evaluate.'
	 */
	boolean shouldBreak(BreakAction a, int fileId, int line, int isolateId) {
		boolean should = a.isEnabled();
		ValueExp exp = a.getCondition();
		if (should && exp != null && !getRequestHalt(isolateId))  // halt request fires true
		{
			// evaluate it then update our boolean
			try {
				EvaluationResult result = evalExpression(exp, false, isolateId);
				if (result != null)
					should = ECMA.toBoolean(result.context.toValue(result.value));
			}
			catch(NullPointerException npe) {}
			catch(NumberFormatException nfe) {}
		}
		return should;
	}

	/**
	 * Sets the command interpreter up to execute the
	 * given string a  command
	 *
	 * This io redirection crap is really UGLY!!!
	 */
	void issueCommand(String cmd, StringBuilder output) {
		ByteArrayOutputStream ba = new ByteArrayOutputStream();
		PrintStream ps = new PrintStream(ba);

		// temporarily re-wire i/o to catch all output
		PrintStream oldOut = m_out;
		PrintStream oldErr = m_err;

		m_out = ps;
		m_err = ps;
		try {
			setCurrentLine(cmd);
			processLine();
		} catch (AmbiguousException ae) {
			// we already put up a warning for the user
		} catch (IllegalStateException ise) {
			err(getLocalizationManager().getLocalizedTextString("illegalStateException")); //$NON-NLS-1$
		} catch (IllegalMonitorStateException ime) {
			err(getLocalizationManager().getLocalizedTextString("commandNotValidUntilPlayerSuspended")); //$NON-NLS-1$
		} catch (NoSuchElementException nse) {
			err(getLocalizationManager().getLocalizedTextString("noSuchElementException")); //$NON-NLS-1$
		} catch (SocketException se) {
			Map<String, Object> args = new HashMap<String, Object>();
			args.put("socketErrorMessage", se.getMessage()); //$NON-NLS-1$
			err(getLocalizationManager().getLocalizedTextString("problemWithConnection", args)); //$NON-NLS-1$
		} catch (Exception e) {
			err(getLocalizationManager().getLocalizedTextString("unexpectedErrorWithStackTrace")); //$NON-NLS-1$
			if (Trace.error)
				e.printStackTrace();
		}

		// flush the stream and then send its contents to our string buffer
		ps.flush();
		output.append(ba.toString());

		m_err = oldErr;
		m_out = oldOut;
	}

	/**
	 * We have received a fault and are possibly suspended at this point.
	 * We need to look at our fault table and determine what do.
	 * @return true if we resumed execution
	 */
	boolean handleFault(FaultEvent e) {
		// lookup what we need to do
		boolean requestResume = false;
		String name = e.name();
		boolean stop = true;
		boolean print = true;
		try {
			print = m_faultTable.is(name, "print"); //$NON-NLS-1$
			stop = m_faultTable.is(name, "stop"); //$NON-NLS-1$
		} catch (NullPointerException npe) {
			if (Trace.error) {
				Map<String, Object> args = new HashMap<String, Object>();
				args.put("faultName", name); //$NON-NLS-1$
				Trace.trace(getLocalizationManager().getLocalizedTextString("faultHasNoTableEntry", args)); //$NON-NLS-1$
				npe.printStackTrace();
			}
		}

		if (e instanceof ExceptionFault) {
			ExceptionFault ef = (ExceptionFault) e;
			Value thrownValue = ef.getThrownValue();
			if (thrownValue != null) {
				if (!ef.willExceptionBeCaught()) {
					stop = true;
				} else {
					stop = false;

					String typeName = thrownValue.getTypeName();
					int at = typeName.indexOf('@');
					if (at != -1)
						typeName = typeName.substring(0, at);

					for (int i = 0; i < catchpointCount(); ++i) {
						CatchAction c = catchpointAt(i);
						String typeToCatch = c.getTypeToCatch();
						try {
							if (typeToCatch == null || getSession().getWorkerSession(e.isolateId).evalIs(thrownValue, typeToCatch)) {
								stop = true;
								break;
							}
						} catch (PlayerDebugException e1) {
							// TODO Auto-generated catch block
							e1.printStackTrace();
							stop = true;
						} catch (PlayerFaultException e1) {
							// TODO Auto-generated catch block
							e1.printStackTrace();
							stop = true;
						}
					}

					if (!stop)
						print = false;
				}
			}
		}

		// should we stop?
		if (!stop)
			requestResume = true;

		if (print)
			dumpFaultLine(e);

		return requestResume;
	}

	// wait a little bit of time until the player halts, if not throw an exception!
	void waitTilHalted(int isolateId) throws NotConnectedException {
		if (!haveConnection())
			throw new IllegalStateException();

		int timeout = propertyGet(HALT_TIMEOUT);
		int update = propertyGet(UPDATE_DELAY);
		boolean wait = (propertyGet(NO_WAITING) == 1) ? false : true;

		if (wait) {
			// spin for a while waiting for a halt; updating trace messages as we get them
			waitForSuspend(timeout, update, isolateId);

			if (!m_session.getWorkerSession(isolateId).isSuspended())
				throw new IllegalMonitorStateException();
		}
	}

	/**
	 * We spin in this spot until the player reaches the
	 * requested suspend state, either true or false.
	 *
	 * During this time we wake up every period milliseconds
	 * and update the display and our state with information
	 * received from the debug event queue.
	 */
	void waitForSuspend(int timeout, int period) throws NotConnectedException {
		waitForSuspend(timeout, period, Isolate.DEFAULT_ID);
	}

	/**
	 * We spin in this spot until the player reaches the
	 * requested suspend state, either true or false.
	 *
	 * During this time we wake up every period milliseconds
	 * and update the display and our state with information
	 * received from the debug event queue.
	 */
	void waitForSuspend(int timeout, int period, int isolateId) throws NotConnectedException {
		IsolateSession workerSession = m_session.getWorkerSession(isolateId);
		while (timeout > 0) {
			// dump our events to the console while we are waiting.
			processEvents();
			if (workerSession.isSuspended())
				break;

			try {
				Thread.sleep(period);
			} catch (InterruptedException ie) {
			}
			timeout -= period;
		}
	}

	/**
	 * If we still have a socket try to send an exit message
	 * Doesn't seem to work ?!?
	 */
	void exitSession() {
		// clear out our watchpoint list and displays
		// keep breakpoints around so that we can try to reapply them if we reconnect
		m_displays.clear();
		m_watchpoints.clear();

		if (m_fileInfo != null)
			m_fileInfo.unbind();

		if (m_session != null)
			m_session.terminate();

		m_session = null;
		m_fileInfo = null;
	}

	void initSession(Session s) {
		s.setSourceLocator(this);

		m_fileInfo = new FileInfoCache();
		m_exprCache.clear();

		m_fileInfo.bind(s);
		m_exprCache.bind(s);

		// bind catching a version problem
		boolean correctVersion = true;
		try {
			s.bind();
		} catch (VersionException ve) {
			correctVersion = false;
		}

		// reset session properties
		propertyPut(LIST_LINE, 1);
		propertyPut(LIST_MODULE, 1);  // default to module #1
		propertyPut(BPNUM, 0); // set current breakpoint number as something bad
		propertyPut(LAST_FRAME_DEPTH, 0);
		propertyPut(CURRENT_FRAME_DEPTH, 0);
		propertyPut(DISPLAY_FRAME_NUMBER, 0);
		propertyPut(PLAYER_FULL_SUPPORT, correctVersion ? 1 : 0);

		String previousURI = m_mruURI;
		m_mruURI = m_session.getURI();

		// try to reapply the breakpoint's
		if (previousURI != null && m_mruURI != null && previousURI.equalsIgnoreCase(m_mruURI))
			reapplyBreakpoints();
		else {
			while (m_breakpoints.size() > 0)
				m_breakpoints.removeElementAt(0);
		}

		m_mainState.m_requestResume = false;
		m_mainState.m_stepResume = false;
		initIsolateState();
	}

	private void initIsolateState() {
		m_activeIsolate = Isolate.DEFAULT_ID;
		m_breakIsolates = new Vector<Integer>();
		m_isolateState = new HashMap<Integer, DebugCLIIsolateState>();
		m_isolateState.put(Isolate.DEFAULT_ID, m_mainState);
	}

	/**
	 * Walk through the list of breakpoints and try to apply them to our session
	 * We aren't that smart in that we ignore the singleSwf property of the breakpoint
	 * meaning that if you have a breakpoint set on a single swf, it will be restored
	 * across all swfs.
	 */
	void reapplyBreakpoints() {
		int count = breakpointCount();
		for (int i = 0; i < count; i++) {
			BreakAction a = breakpointAt(i);
			a.clearHits();
			a.setStatus(BreakAction.UNRESOLVED);
		}

		StringBuilder sb = new StringBuilder();
		resolveBreakpoints(sb);
		out(sb.toString());
	}

	/**
	 * Process a single line of input and return true if the quit command was encountered
	 */
	public boolean processLine() throws IOException, AmbiguousException, PlayerDebugException {
		if (!hasMoreTokens())
			return false;

		String command = nextToken();
		boolean quit = false;
		int cmdID = commandFor(command);

		/* assume line will not be repeated. (i.e. user hits CR nothing happens) */
		m_repeatLine = null;

		switch (cmdID) {
			case CMD_QUIT:
				quit = doQuit();
				break;

			case CMD_CONTINUE:
				doContinue();
				break;

			case CMD_HOME:
				doHome();
				break;

			case CMD_HELP:
				doHelp();
				break;

			case CMD_SHOW:
				doShow();
				break;

			case CMD_STEP:
				doStep();
				break;

			case CMD_NEXT:
				doNext();
				break;

			case CMD_FINISH:
				doFinish();
				break;

			case CMD_BREAK:
				doBreak();
				break;

			case CMD_CLEAR:
				doClear();
				break;

			case CMD_SET:
				doSet();
				break;

			case CMD_LIST:
				doList();
				break;

			case CMD_PRINT:
				doPrint();
				break;

			case CMD_TUTORIAL:
				doTutorial();
				break;

			case CMD_INFO:
				doInfo();
				break;

			case CMD_FILE:
				doFile();
				break;

			case CMD_DELETE:
				doDelete();
				break;

			case CMD_RUN:
				doRun();
				break;

			case CMD_SOURCE:
				doSource();
				break;

			case CMD_KILL:
				doKill();
				break;

			case CMD_HANDLE:
				doHandle();
				break;

			case CMD_ENABLE:
				doEnable();
				break;

			case CMD_DISABLE:
				doDisable();
				break;

			case CMD_DISPLAY:
				doDisplay();
				break;

			case CMD_UNDISPLAY:
				doUnDisplay();
				break;

			case CMD_COMMANDS:
				doCommands();
				break;

			case CMD_PWD:
				doPWD();
				break;

			case CMD_CF:
				doCF();
				break;

//			case CMD_AWATCH:
//				doWatch(true, true);
//				break;

			case CMD_WATCH:
				doWatch(false, true);
				break;

//			case CMD_RWATCH:
//				doWatch(true, false);
//				break;

			case CMD_CONDITION:
				doCondition();
				break;

			case CMD_WHAT:
				doWhat();
				break;

			case CMD_DISASSEMBLE:
				doDisassemble();
				break;

			case CMD_HALT:
				doHalt();
				break;

			case CMD_MCTREE:
				doMcTree();
				break;

			case CMD_VIEW_SWF:
				doViewSwf();
				break;

			case CMD_DOWN:
				doDown();
				break;

			case CMD_UP:
				doUp();
				break;

			case CMD_FRAME:
				doFrame();
				break;

			case CMD_COMMENT:
				; // nop
				break;

			case INFO_STACK_CMD:
				; // from bt
				doInfoStack();
				break;

			case CMD_DIRECTORY:
				doDirectory();
				break;

			case CMD_CATCH:
				doCatch();
				break;

			case CMD_CONNECT:
				doConnect();
				break;

			case CMD_WORKER:
				doWorker();
				break;

			default:
				doUnknown(command);
				break;
		}
		return quit;
	}

	void doWorker() throws IOException, NotSupportedException, NotSuspendedException, NoResponseException, NotConnectedException {
		if (hasMoreTokens()) {
			String workerId = nextToken();
			if (workerId != null && workerId.length() > 0) {
				int id = Integer.parseInt(workerId);
				doWorker(++id, true);
			}
		}
	}

	void doWorker(int id, boolean doOutput) throws IOException, NotSupportedException, NotSuspendedException, NoResponseException, NotConnectedException {
		try {
			boolean found = false;
			for (Isolate isolate : m_session.getWorkers()) {
				if (isolate.getId() == id) {
					found = true;
					break;
				}
			}
			StringBuilder sb = new StringBuilder();
			if (found) {
				m_activeIsolate = id;
				propertyPut(LIST_WORKER, id);
				propertyPut(LIST_LINE, 1);
				propertyPut(LIST_MODULE, 1);  // default to module #1
				propertyPut(BPNUM, 0); // set current breakpoint number as something bad
				propertyPut(LAST_FRAME_DEPTH, 0);
				propertyPut(CURRENT_FRAME_DEPTH, 0);
				propertyPut(DISPLAY_FRAME_NUMBER, 0);

				if (doOutput) {
					sb.append(getLocalizationManager().getLocalizedTextString("workerChanged") + " "); //$NON-NLS-1$ //$NON-NLS-2$
					if (id == Isolate.DEFAULT_ID) {
						sb.append(getLocalizationManager().getLocalizedTextString("mainThread")); //$NON-NLS-1$
					} else
						sb.append((id - 1));
					sb.append(m_newline);
				}
			} else {
				if (doOutput) {
					sb.append(getLocalizationManager().getLocalizedTextString("workerNotFound") + " " + (id - 1)); //$NON-NLS-1$ //$NON-NLS-2$
					sb.append(m_newline);
				}
			}
			if (doOutput) {
				out(sb.toString());
			}
		} catch (NumberFormatException e) {
			err(e.getLocalizedMessage());
		}
	}


	/**
	 * Read help text from fdbhelp*.txt.
	 */
	String getHelpTopic(String topic) {
		// Open the file fdbhelp*.txt that is a sibling of this class file.
		// (Note: build.xml copies it into the classes directory.)
		InputStream helpStream = Help.getResourceAsStream();
		if (helpStream == null)
			return getLocalizationManager().getLocalizedTextString("noHelpFileFound"); //$NON-NLS-1$

		// Read the help file line-by-line, looking for topic lines like [Break].
		// Build an array of the lines within the section for the specified topic.
		topic = "[" + topic + "]"; //$NON-NLS-1$ //$NON-NLS-2$
		Vector<String> lines = new Vector<String>();
		BufferedReader r = null;
		try {
			r = new BufferedReader(new InputStreamReader(helpStream, "UTF-8")); //$NON-NLS-1$
			String line;
			// Read lines until we find the specified topic line.
			while ((line = r.readLine()) != null) {
				if (line.startsWith(topic))
					break;
			}
			// Read lines until we find the next topic line.
			while ((line = r.readLine()) != null) {
				if (line.startsWith("[")) //$NON-NLS-1$
					break;
				lines.add(line);
			}
		} catch (FileNotFoundException fnf) {
			err(fnf.getLocalizedMessage());
		} catch (IOException e) {
			err(e.getLocalizedMessage());
		} finally {
			if (r != null)
				try {
					r.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
		}

		// Concatenate the lines, leaving out the first and last ones
		// which are supposed to be blank. They're only there to make
		// fdbhelp*.txt more readable.
		StringBuilder helpText = new StringBuilder();
		int n = lines.size();
		for (int i = 1; i < n - 1; i++) {
			String line = lines.get(i);
			helpText.append(line);
			if (i != n - 2)
				helpText.append(m_newline);
		}

		return helpText.toString();
	}

	/**
	 * Provide contenxt sensistive help
	 */
	void doHelp() throws AmbiguousException {
		// someone entered a help command so let's help them!
		String topic = "help"; //$NON-NLS-1$

		int cmd;
		String commandName;

		// they might have entered something like "help br"
		if (hasMoreTokens()) {
			// map "br" to CMD_BREAK
			cmd = commandFor(nextToken());
			// and then back to "break"
			commandName = commandNumberToCommandName(g_commandArray, cmd);
			// so we'll look up the topic named "break" in fdbhelp*.txt
			topic = commandName;

			// they might have entered something like "help inf fil"
			if (cmd == CMD_INFO && hasMoreTokens()) {
				// map "fil" to CMD_INFO_FILE
				cmd = infoCommandFor(nextToken());
				// and then back to "file"
				commandName = commandNumberToCommandName(g_infoCommandArray, cmd);
				// so we'll look up the topic named "info file" in fdbhelp*.txt
				topic += " " + commandName; //$NON-NLS-1$
			}

			// or like "help sho n"
			else if (cmd == CMD_SHOW && hasMoreTokens()) {
				// map "n" to CMD_SHOW_NET
				cmd = showCommandFor(nextToken());
				// and then back to "net"
				commandName = commandNumberToCommandName(g_showCommandArray, cmd);
				// so we'll look up the topic named "show net" in fdbhelp*.txt
				topic += " " + commandName; //$NON-NLS-1$
			}
		}

		out(getHelpTopic(topic));
	}

	void doTutorial() {
		out(getHelpTopic("Tutorial")); //$NON-NLS-1$
	}

	// process strings to command ids
	int commandFor(String s) throws AmbiguousException {
		return determineCommand(g_commandArray, s, CMD_UNKNOWN);
	}

	int showCommandFor(String s) throws AmbiguousException {
		return determineCommand(g_showCommandArray, s, SHOW_UNKNOWN_CMD);
	}

	int infoCommandFor(String s) throws AmbiguousException {
		return determineCommand(g_infoCommandArray, s, INFO_UNKNOWN_CMD);
	}

	int enableCommandFor(String s) throws AmbiguousException {
		return determineCommand(g_enableCommandArray, s, CMD_UNKNOWN);
	}

	int disableCommandFor(String s) throws AmbiguousException {
		return determineCommand(g_disableCommandArray, s, CMD_UNKNOWN);
	}

	/**
	 * Attempt to match given the given string against our set of commands
	 * @return the command code that was hit.
	 */
	int determineCommand(StringIntArray cmdList, String input, int defCmd) throws AmbiguousException {
		int cmd = defCmd;

		// first check for a comment
		if (input.charAt(0) == '#')
			cmd = CMD_COMMENT;
		else {
//			long start = System.currentTimeMillis();
			ArrayList ar = cmdList.elementsStartingWith(input);
//			long end = System.currentTimeMillis();

			int size = ar.size();

			/**
			 * 3 cases:
			 *  - No hits, return unknown and let our caller
			 *    dump the error.
			 *  - We match unambiguously or we have 1 or more matches
			 *    and the input is a single character. We then take the
			 *    first hit as our command.
			 *  - If we have multiple hits then we dump a 'ambiguous' message
			 *    and puke quietly.
			 */
			if (size == 0)
				; // no command match return unknown

				// only 1 match or our input is 1 character or first match is exact
			else if (size == 1 ||
					input.length() == 1 ||
					cmdList.getString(((Integer) ar.get(0)).intValue()).compareTo(input) == 0) {
				cmd = (cmdList.getInteger(((Integer) ar.get(0)).intValue())).intValue();
			} else {
				// matches more than one command dump message and go
				StringBuilder sb = new StringBuilder();
				Map<String, Object> args = new HashMap<String, Object>();
				args.put("input", input); //$NON-NLS-1$
				sb.append(getLocalizationManager().getLocalizedTextString("ambiguousCommand", args)); //$NON-NLS-1$
				sb.append(' ');
				sb.append(input);
				for (int i = 0; i < size; i++) {
					String s = cmdList.getString(((Integer) ar.get(i)).intValue());
					sb.append(s);
					if (i + 1 < size)
						sb.append(", "); //$NON-NLS-1$
				}
				sb.append('.');
				err(sb.toString());
				throw new AmbiguousException();
			}
		}
		return cmd;
	}

	String commandNumberToCommandName(StringIntArray cmdList, int cmdNumber) {
		for (int i = 0; i < cmdList.size(); i++) {
			if (cmdList.getInt(i) == cmdNumber)
				return cmdList.getString(i);
		}

		return "?"; //$NON-NLS-1$
	}

	/**
	 * The array of top level commands that we support.
	 * They are placed into a Nx2 array, whereby the first component
	 * is a String which is the command and the 2nd component is the
	 * integer identifier for the command.
	 *
	 * The StringIntArray object provides a convenient wrapper class
	 * that implements the List interface.
	 *
	 * NOTE: order matters!  For the case of a single character
	 * match, we let the first hit act like an unambiguous match.
	 */
	static StringIntArray g_commandArray = new StringIntArray(new Object[][]
			{
					{"awatch", new Integer(CMD_AWATCH)}, //$NON-NLS-1$
					{"break", new Integer(CMD_BREAK)}, //$NON-NLS-1$
					{"bt", new Integer(INFO_STACK_CMD)}, //$NON-NLS-1$
					{"continue", new Integer(CMD_CONTINUE)}, //$NON-NLS-1$
					{"catch", new Integer(CMD_CATCH)}, //$NON-NLS-1$
					{"cf", new Integer(CMD_CF)}, //$NON-NLS-1$
					{"clear", new Integer(CMD_CLEAR)}, //$NON-NLS-1$
					{"commands", new Integer(CMD_COMMANDS)}, //$NON-NLS-1$
					{"condition", new Integer(CMD_CONDITION)}, //$NON-NLS-1$
					{"connect", new Integer(CMD_CONNECT)}, //$NON-NLS-1$
					{"delete", new Integer(CMD_DELETE)}, //$NON-NLS-1$
					{"disable", new Integer(CMD_DISABLE)}, //$NON-NLS-1$
					{"disassemble", new Integer(CMD_DISASSEMBLE)}, //$NON-NLS-1$
					{"display", new Integer(CMD_DISPLAY)}, //$NON-NLS-1$
					{"directory", new Integer(CMD_DIRECTORY)}, //$NON-NLS-1$
					{"down", new Integer(CMD_DOWN)}, //$NON-NLS-1$
					{"enable", new Integer(CMD_ENABLE)}, //$NON-NLS-1$
					{"finish", new Integer(CMD_FINISH)}, //$NON-NLS-1$
					{"file", new Integer(CMD_FILE)}, //$NON-NLS-1$
					{"frame", new Integer(CMD_FRAME)}, //$NON-NLS-1$
					{"help", new Integer(CMD_HELP)}, //$NON-NLS-1$
					{"halt", new Integer(CMD_HALT)}, //$NON-NLS-1$
					{"handle", new Integer(CMD_HANDLE)}, //$NON-NLS-1$
					{"home", new Integer(CMD_HOME)}, //$NON-NLS-1$
					{"info", new Integer(CMD_INFO)}, //$NON-NLS-1$
					{"kill", new Integer(CMD_KILL)}, //$NON-NLS-1$
					{"list", new Integer(CMD_LIST)}, //$NON-NLS-1$
					{"next", new Integer(CMD_NEXT)}, //$NON-NLS-1$
					{"nexti", new Integer(CMD_NEXT)}, //$NON-NLS-1$
					{"mctree", new Integer(CMD_MCTREE)}, //$NON-NLS-1$
					{"print", new Integer(CMD_PRINT)}, //$NON-NLS-1$
					{"pwd", new Integer(CMD_PWD)}, //$NON-NLS-1$
					{"quit", new Integer(CMD_QUIT)}, //$NON-NLS-1$
					{"run", new Integer(CMD_RUN)}, //$NON-NLS-1$
					{"rwatch", new Integer(CMD_RWATCH)}, //$NON-NLS-1$
					{"step", new Integer(CMD_STEP)}, //$NON-NLS-1$
					{"stepi", new Integer(CMD_STEP)}, //$NON-NLS-1$
					{"set", new Integer(CMD_SET)}, //$NON-NLS-1$
					{"show", new Integer(CMD_SHOW)}, //$NON-NLS-1$
					{"source", new Integer(CMD_SOURCE)}, //$NON-NLS-1$
					{"tutorial", new Integer(CMD_TUTORIAL)}, //$NON-NLS-1$
					{"undisplay", new Integer(CMD_UNDISPLAY)}, //$NON-NLS-1$
					{"up", new Integer(CMD_UP)}, //$NON-NLS-1$
					{"where", new Integer(INFO_STACK_CMD)}, //$NON-NLS-1$
					{"watch", new Integer(CMD_WATCH)}, //$NON-NLS-1$
					{"what", new Integer(CMD_WHAT)}, //$NON-NLS-1$
					{"viewswf", new Integer(CMD_VIEW_SWF)}, //$NON-NLS-1$
					{"worker", new Integer(CMD_WORKER)}, //$NON-NLS-1$

			});

	/**
	 * Info sub-commands
	 */
	static StringIntArray g_infoCommandArray = new StringIntArray(new Object[][]
			{
					{"arguments", new Integer(INFO_ARGS_CMD)}, //$NON-NLS-1$
					{"breakpoints", new Integer(INFO_BREAK_CMD)}, //$NON-NLS-1$
					{"display", new Integer(INFO_DISPLAY_CMD)}, //$NON-NLS-1$
					{"files", new Integer(INFO_FILES_CMD)}, //$NON-NLS-1$
					{"functions", new Integer(INFO_FUNCTIONS_CMD)}, //$NON-NLS-1$
					{"handle", new Integer(INFO_HANDLE_CMD)}, //$NON-NLS-1$
					{"locals", new Integer(INFO_LOCALS_CMD)}, //$NON-NLS-1$
					{"stack", new Integer(INFO_STACK_CMD)}, //$NON-NLS-1$
					{"scopechain", new Integer(INFO_SCOPECHAIN_CMD)}, //$NON-NLS-1$
					{"sources", new Integer(INFO_SOURCES_CMD)}, //$NON-NLS-1$
					{"swfs", new Integer(INFO_SWFS_CMD)}, //$NON-NLS-1$
					{"targets", new Integer(INFO_TARGETS_CMD)}, //$NON-NLS-1$
					{"variables", new Integer(INFO_VARIABLES_CMD)}, //$NON-NLS-1$
					{"workers", new Integer(INFO_WORKERS_CMD)}, //$NON-NLS-1$
			});

	/**
	 * Show sub-commands
	 */
	static StringIntArray g_showCommandArray = new StringIntArray(new Object[][]
			{
					{"break", new Integer(SHOW_BREAK_CMD)}, //$NON-NLS-1$
					{"directories", new Integer(SHOW_DIRS_CMD)}, //$NON-NLS-1$
					{"files", new Integer(SHOW_FILES_CMD)}, //$NON-NLS-1$
					{"functions", new Integer(SHOW_FUNC_CMD)}, //$NON-NLS-1$
					{"locations", new Integer(SHOW_LOC_CMD)}, //$NON-NLS-1$
					{"memory", new Integer(SHOW_MEM_CMD)}, //$NON-NLS-1$
					{"net", new Integer(SHOW_NET_CMD)}, //$NON-NLS-1$
					{"properties", new Integer(SHOW_PROPERTIES_CMD)}, //$NON-NLS-1$
					{"uri", new Integer(SHOW_URI_CMD)}, //$NON-NLS-1$
					{"variable", new Integer(SHOW_VAR_CMD)}, //$NON-NLS-1$
			});

	/**
	 * enable sub-commands
	 */
	static StringIntArray g_enableCommandArray = new StringIntArray(new Object[][]
			{
					{"breakpoints", new Integer(CMD_BREAK)}, //$NON-NLS-1$
					{"display", new Integer(CMD_DISPLAY)}, //$NON-NLS-1$
					{"delete", new Integer(CMD_DELETE)}, //$NON-NLS-1$
					{"once", new Integer(ENABLE_ONCE_CMD)}, //$NON-NLS-1$
			});

	/**
	 * disable sub-commands
	 */
	static StringIntArray g_disableCommandArray = new StringIntArray(new Object[][]
			{
					{"display", new Integer(CMD_DISPLAY)}, //$NON-NLS-1$
					{"breakpoints", new Integer(CMD_BREAK)}, //$NON-NLS-1$
			});


	/**
	 * -------------------------------------------------------------------------
	 * Any code that accesses the implementation of the API is wrapped
	 * in Extensions.  This way one can easily factor this stuff out
	 * and build an fdb that is completely compliant to the API.
	 *
	 * I'm pretty sure there's a better way of doing this like
	 * making Extensions a final static variable and then
	 * toggling it between two classes Extensions and something
	 * like ExtensionsDisabled (methods with only out("not supported")
	 * in them).
	 * -------------------------------------------------------------------------
	 */
	void appendBreakInfo(StringBuilder sb, int isolateId) throws NotConnectedException {
		Extensions.appendBreakInfo(this, sb, false, isolateId);
	}

	void doShowStats() {
		Extensions.doShowStats(this);
	}

	void doShowFuncs() {
		Extensions.doShowFuncs(this);
	}

	void doShowProperties() {
		Extensions.doShowProperties(this);
	}

	void doShowVariable() throws PlayerDebugException {
		Extensions.doShowVariable(this);
	}

	void doShowBreak() throws NotConnectedException {
		Extensions.doShowBreak(this);
	}

	void doDisassemble() throws PlayerDebugException {
		Extensions.doDisassemble(this);
	}
}

class FileLocation {

	public FileLocation () {};

	public FileLocation(int isolateId, int module, int line, int wasFunc) {
		setIsolateId(isolateId);
		setModule(module);
		setLine(line);
		setWasFunc(wasFunc);
	};

	public FileLocation(int isolateId, int module, int line) {
		setIsolateId(isolateId);
		setModule(module);
		setLine(line);
		setWasFunc(0);
	};


	public int getIsolateId() {
		return isolateId;
	}

	public void setIsolateId(int isolateId) {
		this.isolateId = isolateId;
	}

	public int getModule() {
		return module;
	}

	public void setModule(int module) {
		this.module = module;
	}

	public int getLine() {
		return line;
	}

	public void setLine(int line) {
		this.line = line;
	}

	int isolateId;
	int module;
	int line;

	public int getWasFunc() {
		return wasFunc;
	}

	public void setWasFunc(int wasFunc) {
		this.wasFunc = wasFunc;
	}

	int wasFunc;
}
