[395465] [dstore][shells] customer hit an NPE on shell cleanup
diff --git a/rse/plugins/org.eclipse.rse.services.dstore/miners/org/eclipse/rse/internal/dstore/universal/miners/command/CommandMinerThread.java b/rse/plugins/org.eclipse.rse.services.dstore/miners/org/eclipse/rse/internal/dstore/universal/miners/command/CommandMinerThread.java index eb721e1..bfa5e37 100644 --- a/rse/plugins/org.eclipse.rse.services.dstore/miners/org/eclipse/rse/internal/dstore/universal/miners/command/CommandMinerThread.java +++ b/rse/plugins/org.eclipse.rse.services.dstore/miners/org/eclipse/rse/internal/dstore/universal/miners/command/CommandMinerThread.java
@@ -1,1503 +1,1508 @@ -/******************************************************************************* - * Copyright (c) 2003, 2010 IBM Corporation and others. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Initial Contributors: - * The following IBM employees contributed to the Remote System Explorer - * component that contains this file: David McKnight, Kushal Munir, - * Michael Berger, David Dykstal, Phil Coulthard, Don Yantzi, Eric Simpson, - * Emily Bruner, Mazen Faraj, Adrian Storisteanu, Li Ding, and Kent Hawley. - * - * Contributors: - * {Name} (company) - description of contribution. - * David McKnight (IBM) - [202822] updating cleanup - * David McKnight (IBM) - [196624] dstore miner IDs should be String constants rather than dynamic lookup - * Noriaki Takatsu (IBM) - [220126] [dstore][api][breaking] Single process server for multiple clients - * David McKnight (IBM) [224906] [dstore] changes for getting properties and doing exit due to single-process capability - * David McKnight (IBM) [250203] [dstore][shells]%var% is substituted to null in Unix shell - * David McKnight (IBM) [249715] [dstore][shells] Unix shell does not echo command - * David McKnight (IBM) [153275] [dstore-shells] Ctrl+C does not break remote program - * David McKnight (IBM) [284179] [dstore] commands have a hard coded line length limit of 100 characters - * David McKnight (IBM) - [286671] Dstore shell service interprets < and > sequences - * David McKnight (IBM) [290743] [dstore][shells] allow bash shells and custom shell invocation - * David McKnight (IBM) [287305] [dstore] Need to set proper uid for commands when using SecuredThread and single server for multiple clients[ - * Peter Wang (IBM) [299422] [dstore] OutputHandler.readLines() not compatible with servers that return max 1024bytes available to be read - * David McKnight (IBM) [302174] [dstore] shell init command can potentially get called too late - * David McKnight (IBM) [302724] problems with environment variable substitution - * David McKnight (IBM) [302996] [dstore] null checks and performance issue with shell output - * David McKnight (IBM) [308246] [dstore] fix for Bug 287305 breaks on z/OS due to "su" usage - * David McKnight (IBM) [323262] [dstore] zos shell does not display [ ] brackets properly - *******************************************************************************/ - -package org.eclipse.rse.internal.dstore.universal.miners.command; - - - -import java.io.BufferedWriter; -import java.io.DataInputStream; -import java.io.File; -import java.io.IOException; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.io.UnsupportedEncodingException; -import java.util.ArrayList; -import java.util.Enumeration; -import java.util.Hashtable; -import java.util.List; - -import org.eclipse.dstore.core.miners.MinerThread; -import org.eclipse.dstore.core.model.Client; -import org.eclipse.dstore.core.model.DE; -import org.eclipse.dstore.core.model.DataElement; -import org.eclipse.dstore.core.model.DataStoreAttributes; -import org.eclipse.rse.dstore.universal.miners.CommandMiner; -import org.eclipse.rse.dstore.universal.miners.IUniversalDataStoreConstants; -import org.eclipse.rse.internal.dstore.universal.miners.command.patterns.ParsedOutput; -import org.eclipse.rse.internal.dstore.universal.miners.command.patterns.Patterns; - - - -/** - * CommandMinerThread is used for running and handling io for shell commands - * in a thread. - */ -public class CommandMinerThread extends MinerThread -{ - class InitRunnable implements Runnable - { - private boolean _done = false; - private String _initCmd; - - public InitRunnable(String command){ - _initCmd = command; - } - - public boolean isDone(){ - return _done; - } - - public void run() - { - // wait a second so the profile can complete startup - try { - sleep(1000); - } - catch (Exception e) - { - } - - _done = true; // setting before the call so that sendInput doesn't wait on this - sendInput(_initCmd); - } - } - - private DataElement _status; - private String _invocation; - - private DataInputStream _stdInput; - private DataInputStream _stdError; - - - private BufferedWriter _stdOutput; - - private Patterns _patterns; - - private Process _theProcess; - - private DataElement _subject; - private String _cwdStr; - private OutputHandler _stdOutputHandler; - private OutputHandler _stdErrorHandler; - private boolean _isShell; - private boolean _isDone; - private boolean _isWindows; - private boolean _isTTY; - private boolean _didInitialCWDQuery = false; - - private int _maxLineLength = 4096; - - - private CommandMiner.CommandMinerDescriptors _descriptors; - - // default - private String PSEUDO_TERMINAL; - - private DataElement _lastPrompt; - private InitRunnable _initRunnable; - private Thread _cdThread; - - public CommandMinerThread(DataElement theElement, String invocation, DataElement status, Patterns thePatterns, CommandMiner.CommandMinerDescriptors descriptors) - { - super(theElement.getDataStore()); - _isShell = false; - _isDone = false; - _status = status; - _descriptors = descriptors; - boolean isBash = false; - - _subject = theElement; - - String maxLineLengthStr = System.getProperty("DSTORE_SHELL_MAX_LINE"); //$NON-NLS-1$ - if (maxLineLengthStr != null) - { - try { - _maxLineLength = Integer.parseInt(maxLineLengthStr); - } - catch (NumberFormatException e) - {} - } - - String theOS = System.getProperty("os.name"); //$NON-NLS-1$ - - _invocation = invocation.trim(); - _patterns = thePatterns; - _patterns.refresh(_invocation); - - boolean isZ = theOS.toLowerCase().startsWith("z");//$NON-NLS-1$ - - if (isZ) - { - String inCoding = System.getProperty("dstore.stdin.encoding"); //$NON-NLS-1$ - if (inCoding == null || inCoding.length() == 0){ - // IBM-1047 works better on z/OS than cp037 - System.setProperty("dstore.stdin.encoding","IBM-1047"); //$NON-NLS-1$ //$NON-NLS-2$ - } - } - - _isWindows = theOS.toLowerCase().startsWith("win"); //$NON-NLS-1$ - if (!_isWindows) - { - PSEUDO_TERMINAL = _dataStore.getAttribute(DataStoreAttributes.A_PLUGIN_PATH) + File.separatorChar + "rseterm" + File.separatorChar + "rseterm"; //$NON-NLS-1$ //$NON-NLS-2$ - } - - try - { - String suCommand = null; - String userHome = null; - Client client = _dataStore.getClient(); - - if (client != null && !theOS.equals("z/OS")){ //$NON-NLS-1$ - String clientActualUserId = client.getProperty("user.name");//$NON-NLS-1$ - String clientUserId = client.getUserid(); - - userHome = client.getProperty("user.home");//$NON-NLS-1$ - if (clientUserId != null && !clientActualUserId.equals(clientUserId)){ - suCommand = "su " + clientUserId + " -c "; //$NON-NLS-1$ //$NON-NLS-2$ - } - } - else { - userHome = System.getProperty("user.home");//$NON-NLS-1$ - } - - _cwdStr = theElement.getSource(); - if (_cwdStr == null || _cwdStr.length() == 0) - { - _cwdStr = userHome; - } - - File theDirectory = new File(_cwdStr); - if (!theDirectory.isDirectory()) - theDirectory = theDirectory.getParentFile(); - try - { - _cwdStr = theDirectory.getAbsolutePath(); - } - catch (Exception e) - { - _cwdStr = userHome; - } - _status.setAttribute(DE.A_SOURCE, _cwdStr); - - - boolean didLogin = false; - - String theShell = null; - if (!_isWindows) - { - File psuedoTerminal = new File(PSEUDO_TERMINAL); - if (psuedoTerminal.exists()) - { - _isTTY = true; - PSEUDO_TERMINAL = psuedoTerminal.getAbsolutePath(); - } - else - { - _isTTY = false; - } - - - - - - _patterns.setIsTerminal(_isTTY); - - String property = "SHELL="; //$NON-NLS-1$ - - String[] env = getEnvironment(_subject); - boolean isBashonZ = false; - boolean isSHonZ = false; - - for (int i = 0; i < env.length; i++) - { - String var = env[i]; - if (var.startsWith(property)) - { - theShell = var.substring(property.length(), var.length()); - if (theShell.endsWith("bash"))//$NON-NLS-1$ - { - if (isZ) - { - isBashonZ = true; - } - else - { - isBash = true; - } - } - else if (theShell.endsWith("sh") && isZ)//$NON-NLS-1$ - { - isSHonZ = true; - } - } - // In a single-process server, both user.home and HOME don't represent - // each client home directory. - if (_dataStore.getClient() != null) - { - if (var.startsWith("HOME")) //$NON-NLS-1$ - { - env[i] = "HOME=" + _dataStore.getClient().getProperty("user.home"); //$NON-NLS-1$ //$NON-NLS-2$ - } - } - } - - // check for custom shell invocation - String customShellInvocation = System.getProperty("DSTORE_SHELL_INVOCATION"); //$NON-NLS-1$ - if (customShellInvocation != null && customShellInvocation.length() > 0){ - theShell = customShellInvocation; - } - - if (theShell == null) - { - if (_invocation.equals(">")) //$NON-NLS-1$ - { - _invocation = "sh"; //$NON-NLS-1$ - - _isShell = true; - if (isZ) - isSHonZ = true; - } - - - if (_isTTY) - { - if (isSHonZ) - { - String args[] = new String[3]; - args[0] = PSEUDO_TERMINAL; - args[1] = "sh"; //$NON-NLS-1$ - args[2] = "-L"; //$NON-NLS-1$ - - try { - _theProcess = Runtime.getRuntime().exec(args, env, theDirectory); - } - catch (Exception e) { - _theProcess = Runtime.getRuntime().exec(_invocation, env, theDirectory); - } - didLogin = true; - } - else - { - List argsList = new ArrayList(); - - if (suCommand != null){ - String[] suSplit = suCommand.split(" "); //$NON-NLS-1$ - for (int i = 0; i < suSplit.length; i++){ // su before terminal - argsList.add(suSplit[i]); - } - } - argsList.add(PSEUDO_TERMINAL); - argsList.add(invocation); - - - String args[] = (String[])argsList.toArray(new String[argsList.size()]); - _theProcess = Runtime.getRuntime().exec(args, env, theDirectory); - } - } - else - { - //VRB: for Linux on System z we end up here - if (suCommand!=null) - _invocation = suCommand + _invocation; - _theProcess = Runtime.getRuntime().exec(_invocation, env, theDirectory); - } - } - else - { - if (_invocation.equals(">")) //$NON-NLS-1$ - { - _invocation = theShell; - - - _isShell = true; - - if (_isTTY) - { - List argsList = new ArrayList(); - - if (!isBashonZ && !isSHonZ && suCommand != null){ - // su before starting rseterm - String[] suArgs = suCommand.split(" "); //$NON-NLS-1$ - for (int i = 0; i < suArgs.length; i++){ - argsList.add(suArgs[i]); - } - } - argsList.add(PSEUDO_TERMINAL); - - if (!isBashonZ && !isSHonZ && suCommand != null){ - // need sh -c before invocation - argsList.add("sh"); //$NON-NLS-1$ - argsList.add("-c"); //$NON-NLS-1$ - } - else { - - argsList.add("-w"); //$NON-NLS-1$ - argsList.add(""+_maxLineLength); //$NON-NLS-1$ - } - - argsList.add(_invocation); - - if (isBashonZ){ - argsList.add("--login"); //$NON-NLS-1$ - didLogin = true; - } - else if (isBash){ - argsList.add("-l"); //$NON-NLS-1$ - didLogin = true; - } - else if (isSHonZ){ - argsList.add("-L"); //$NON-NLS-1$ - didLogin = true; - } - - String args[] = (String[])argsList.toArray(new String[argsList.size()]); - - try { - _theProcess = Runtime.getRuntime().exec(args, env, theDirectory); - } - catch (Exception e) { - _theProcess = Runtime.getRuntime().exec(_invocation, env, theDirectory); - } - - } - else - { - if (!isBashonZ && !isSHonZ && suCommand != null){ - _invocation = suCommand + _invocation; - } - - if (customShellInvocation != null && customShellInvocation.length() > 0){ - // all handled in the custom shell invocation - _theProcess = Runtime.getRuntime().exec(_invocation, env, theDirectory); - } - else { - if (isBashonZ) - { - _theProcess = Runtime.getRuntime().exec(_invocation + " --login", env, theDirectory); //$NON-NLS-1$ - didLogin = true; - } - else if (isBash) - { - _theProcess = Runtime.getRuntime().exec(_invocation + " -l", env, theDirectory); //$NON-NLS-1$ - didLogin = true; - } - else if (isSHonZ) - { - _theProcess = Runtime.getRuntime().exec(_invocation + " -L", env, theDirectory); //$NON-NLS-1$ - didLogin = true; - } - else - { - _theProcess = Runtime.getRuntime().exec(_invocation, env, theDirectory); - } - } - } - } - else - { - if (suCommand != null){ - theShell = suCommand + theShell; - } - - List argsList = new ArrayList(); - - String[] shellArray = theShell.split(" "); //$NON-NLS-1$ - for (int i = 0; i < shellArray.length; i++){ - argsList.add(shellArray[i]); - } - argsList.add("-c"); //$NON-NLS-1$ - argsList.add(_invocation); - - String args[] = (String[])argsList.toArray(new String[argsList.size()]); - _theProcess = Runtime.getRuntime().exec(args, env, theDirectory); - } - } - } - else // windows - { - if ((theOS.indexOf("95") >= 0) || (theOS.indexOf("98") >= 0) || (theOS.indexOf("ME") >= 0)) //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - { - theShell = "start"; //$NON-NLS-1$ - } - else - { - theShell = "cmd"; //$NON-NLS-1$ - } - if (_invocation.equals(">")) //$NON-NLS-1$ - { - _invocation = theShell; - _isShell = true; - } - - - String args[] = new String[3]; - args[0]= theShell; - if (theShell.equals("start")) //$NON-NLS-1$ - { - args[1] = "/B "; //$NON-NLS-1$ - } - else - { - args[1] = "/C "; //$NON-NLS-1$ - } - args[2] = _invocation; - - String[] env = getEnvironment(_subject); - - if (_invocation.equals(theShell)) - { - - _theProcess = Runtime.getRuntime().exec(_invocation, env, theDirectory); - } - else - { - _theProcess = Runtime.getRuntime().exec(args, env, theDirectory); - } - - } - - - - OutputStream output = _theProcess.getOutputStream(); - _stdInput = new DataInputStream(_theProcess.getInputStream()); - _stdError = new DataInputStream(_theProcess.getErrorStream()); - - - String specialEncoding = System.getProperty("dstore.stdin.encoding"); //$NON-NLS-1$ - - if (specialEncoding != null) - { - /* - _stdInput = new BufferedReader(new InputStreamReader(_theProcess.getInputStream(), specialEncoding)); - _stdError = new BufferedReader(new InputStreamReader(_theProcess.getErrorStream(), specialEncoding)); - */ - try - { - _stdOutput = new BufferedWriter(new OutputStreamWriter(output, specialEncoding)); - } - catch (UnsupportedEncodingException e) - { - _stdOutput = new BufferedWriter(new OutputStreamWriter(output)); - } - } - else - { - //_stdInput = new BufferedReader(new InputStreamReader(_theProcess.getInputStream())); - //_stdError = new BufferedReader(new InputStreamReader(_theProcess.getErrorStream())); - _stdOutput = new BufferedWriter(new OutputStreamWriter(output)); - } - - createObject("command", _invocation); //$NON-NLS-1$ - createObject("stdout", ""); //$NON-NLS-1$ //$NON-NLS-2$ - - status.setAttribute(DE.A_NAME, "progress"); //$NON-NLS-1$ - _dataStore.update(status); - _dataStore.disconnectObjects(status); - - _stdOutputHandler = new OutputHandler(_stdInput, null, _isWindows || _isTTY, false, _isShell, this); - _stdOutputHandler.setWaitTime(100); - _stdOutputHandler.setDataStore(_dataStore); - _stdOutputHandler.start(); - - _stdErrorHandler = new OutputHandler(_stdError, null, _isWindows || _isTTY, true, _isShell, this); - _stdErrorHandler.setWaitTime(100); - _stdOutputHandler.setDataStore(_dataStore); - _stdErrorHandler.start(); - - // initialization - if (didLogin || _isTTY) - { - String initCmd = ""; //$NON-NLS-1$ - if (_isTTY){ - initCmd = "export PS1='$PWD>';" ; //$NON-NLS-1$ - } - if (didLogin && !userHome.equals(_cwdStr)){ - initCmd += "cd " + _cwdStr; //$NON-NLS-1$ - } - - // need to CD to the correct directory - _initRunnable = new InitRunnable(initCmd); - _cdThread = new Thread(_initRunnable); - _cdThread.start(); - } - else if (_isShell && !_isWindows && !_isTTY) - { - createPrompt(_cwdStr +">", _cwdStr); //$NON-NLS-1$ - refreshStatus(); - } - - } - catch (IOException e) - { - _theProcess = null; - _dataStore.trace(e); - createObject("command", e.getMessage()); //$NON-NLS-1$ - status.setAttribute(DE.A_NAME, "done"); //$NON-NLS-1$ - return; - } - - } - - -// private String[] parseArgs(String full) -// { -// StringBuffer result = new StringBuffer(); -// char[] chars = full.toCharArray(); -// boolean inQuotes = false; -// boolean escaping = false; -// for (int i = 0; i < chars.length; i++) -// { -// char c = chars[i]; -// if (c == '\"') -// { -// inQuotes = !inQuotes; -// } -// else -// { -// if (c == '\\') -// { -// escaping = true; -// } -// else -// { -// if (c == ' ') -// { -// if (!inQuotes && !escaping) -// { -// c = ','; -// } -// escaping = false; -// } -// } -// result.append(c); -// } -// -// } -// return result.toString().split(","); -// } - - public Process getProcess() - { - return _theProcess; - } - - public String getCWD() - { - return _cwdStr; - } - - public void queryCWD() - { - BufferedWriter writer = _stdOutput; - try - { - // hidden command - writer.write("echo '<'PWD=$PWD"); //$NON-NLS-1$ - writer.newLine(); - writer.flush(); - } - catch (Exception e) - { - } - _didInitialCWDQuery = true; - - } - - - public void sendInput(String input) - { - if (!_isDone) - { - if (_initRunnable != null && !_initRunnable.isDone()){ - try { - _cdThread.join(); - } - catch (InterruptedException e){} - } - - String origInput = input; - input.getBytes(); - - try - { - BufferedWriter writer = _stdOutput; - // pty executable handles the break now - if (input.equals("#break") && !_isTTY) //$NON-NLS-1$ - { - // if no pty, then do it explicitly - _theProcess.destroy(); - - return; - } - else if (input.equals("#enter")) //$NON-NLS-1$ - { - writer.newLine(); - writer.flush(); - return; - } - - if (_isShell) - { - if (_lastPrompt != null) - { - if (!_isTTY) - { - String promptText = _lastPrompt.getName(); - if (promptText.endsWith(">")) //$NON-NLS-1$ - { - _lastPrompt.setAttribute(DE.A_NAME, promptText + input); - _dataStore.refresh(_lastPrompt); - } - - else - { -// String cwd = getCWD(); -// String line = cwd + ">" + input; - //createObject("prompt", line); - //createPrompt(line, cwd); - } - } - } - - _patterns.update(input); - } - - if (!_isWindows && !_isTTY) - { - createObject("input", origInput); //$NON-NLS-1$ - } - - writer.write(input); - writer.newLine(); - try{ - writer.flush(); - } - catch (Exception e){ - //TODO find actual cause of problem. This only fails on certain machines. - } - - if (!_isWindows && (input.startsWith("cd ") || input.equals("cd"))) //$NON-NLS-1$ //$NON-NLS-2$ - { - if (!_isTTY) - queryCWD(); - } - else if (!_didInitialCWDQuery) - { - if (!_isTTY) - queryCWD(); - } - if (!_isWindows && !_isTTY) - { - // always prompt after the command - writer.write("echo $PWD'>'"); //$NON-NLS-1$ - writer.newLine(); - writer.flush(); - } - } - catch (IOException e) - { - cleanupThread(); - } - } - } - private String[] getEnvironment(DataElement theSubject) - { - //Grab the system environment: - DataElement envMiner = _dataStore.findMinerInformation(IUniversalDataStoreConstants.UNIVERSAL_ENVIRONMENT_MINER_ID); - DataElement systemEnv = _dataStore.find(envMiner, DE.A_NAME, "System Environment", 1); //$NON-NLS-1$ - //Walk up until we find an element with an inhabits relationship. - DataElement theProject = theSubject; - List projectEnvReference = null; - while (theProject != null && !theProject.getValue().equals("Data")) //$NON-NLS-1$ - { - projectEnvReference = theProject.getAssociated("inhabits"); //$NON-NLS-1$ - if (projectEnvReference.size() > 0) - break; - theProject = theProject.getParent(); - } - DataElement projectEnv = null; - if (projectEnvReference != null && (projectEnvReference.size() > 0)) - projectEnv = (DataElement) projectEnvReference.get(0); - - String[] theEnv = mergeEnvironments(systemEnv, projectEnv); - - return theEnv; - } - - private String[] mergeEnvironments(DataElement systemEnv, DataElement projectEnv) - { - - List prjVars = null; - List sysVars = null; - //Fill the ArrayLists with the environment variables - if (systemEnv != null) - sysVars = systemEnv.getNestedData(); - if (projectEnv != null) - prjVars = projectEnv.getNestedData(); - //If one or both of the ArrayLists are null, exit early: - if ((sysVars == null) || (sysVars.size() == 0)) - return listToArray(prjVars); - if ((prjVars == null) || (prjVars.size() == 0)) - return listToArray(sysVars); - //If we get here, then we have both system and project variables...to make merging the 2 lists easier, we'll - //use a Hashtable (Variable Names are the keys, Variables Values are the values): - Hashtable varTable = new Hashtable(); - - //First fill the varTable with the sysVars - varTable.putAll(mapVars(sysVars)); - - //Now for every project variable, check to see if it already exists, and if the value contains other variables: - for (int i = 0; i < prjVars.size(); i++) - { - DataElement envElement = (DataElement) prjVars.get(i); - if (!envElement.getType().equals("Environment Variable")) //$NON-NLS-1$ - continue; - String theVariable = envElement.getValue(); - String theKey = getKey(theVariable); - String theValue = getValue(theVariable); - theValue = calculateValue(theValue, varTable); - - varTable.put(theKey, theValue); - } - - - if (_isTTY) - { - varTable.put("PS1","'$PWD/>'"); //$NON-NLS-1$ //$NON-NLS-2$ - - //if (_maxLineLength ) - - varTable.put("COLUMNS","" + _maxLineLength); //$NON-NLS-1$ //$NON-NLS-2$ - } - - - /* DKM: for some reason this isn't getting applied properly here - * but it works via export - * */ - String theOS = System.getProperty("os.name"); //$NON-NLS-1$ - if (theOS.toLowerCase().startsWith("os")) //$NON-NLS-1$ - { - varTable.put("QIBM_JAVA_STDIO_CONVERT","Y"); //$NON-NLS-1$ //$NON-NLS-2$ - varTable.put("QIBM_USE_DESCRIPTOR_STDIO","I"); //$NON-NLS-1$ //$NON-NLS-2$ - varTable.put("PASE_STDIO_ISATTY","N"); //$NON-NLS-1$ //$NON-NLS-2$ - varTable.put("TERMINAL_TYPE","REMOTE"); //$NON-NLS-1$ //$NON-NLS-2$ - varTable.put("STDIO_ISATTY","Y"); //$NON-NLS-1$ //$NON-NLS-2$ - } - - - - return tableToArray(varTable); - } //This method is responsible for replacing variable references with their values. - //We support 3 methods of referencing a variable (assume we are referencing a variable called FOO): - // 1. $FOO - common to most shells (must be followed by a non-alphanumeric or nothing...in other words, we - // always construct the longest name after the $) - // 2. ${FOO} - used when you want do something like ${FOO}bar, since $FOObar means a variable named FOObar not - // the value of FOO followed by "bar". - // 3. %FOO% - Windows command interpreter - private String calculateValue(String value, Hashtable theTable) - { - value = value.replaceAll("}","\n}"); //$NON-NLS-1$ //$NON-NLS-2$ - StringBuffer theValue = new StringBuffer(value); - try - { - int index = 0; - char c; - while (index < theValue.length()) - { - c = theValue.charAt(index); - if (c == '{') - { - index++; - c = theValue.charAt(index); - // skip everything til end quote - while (index < theValue.length() && c != '}') - { - index++; - c = theValue.charAt(index); - } - } - //If the current char is a $, then look for a { or just match alphanumerics - else if (c == '$' && !_isWindows) - { - int nextIndex = index + 1; - if (nextIndex < theValue.length()) - { - c = theValue.charAt(nextIndex); - //If there is a { then we just look for the closing }, and replace the span with the variable value - if (c == '{') - { - int next = theValue.toString().indexOf("}", nextIndex); //$NON-NLS-1$ - if (next > 0) - { - String replacementValue = findValue(theValue.substring(nextIndex + 1, next), theTable, true); - theValue.replace(index, next + 1, replacementValue); - index += replacementValue.length() - 1; - } - } //If there is no { then we just keep matching alphanumerics to construct the longest possible variable name - else - { - if (Character.isJavaIdentifierStart(c)) - { - - while (nextIndex + 1 < theValue.length() && (Character.isJavaIdentifierPart(c))) - { - nextIndex++; - c = theValue.charAt(nextIndex); - - if (nextIndex + 1 == theValue.length()){ // last character? - if (Character.isJavaIdentifierPart(c)){ - nextIndex++; - } - } - } - - String v = theValue.substring(index + 1, nextIndex); - String replacementValue = findValue(v, theTable, true); - theValue.replace(index, nextIndex, replacementValue); - index += replacementValue.length() - 1; - } - } - } - - } //If the current char is a %, then simply look for a matching % - else if (c == '%' && _isWindows) - { - int next = theValue.toString().indexOf("%", index + 1); //$NON-NLS-1$ - if (next > 0) - { - String replacementValue = findValue(theValue.substring(index + 1, next), theTable, false); - theValue.replace(index, next + 1, replacementValue); - index += replacementValue.length() - 1; - } - } - else if (c == '"') - { - index++; - c = theValue.charAt(index); - // skip everything til end quote - while (index < theValue.length() && c != '"') - { - index++; - c = theValue.charAt(index); - } - - } - - index++; - } - } - catch (Throwable e) - { - _dataStore.trace(e); - } - return theValue.toString(); - } - private String findValue(String key, Hashtable theTable, boolean caseSensitive) - { - Object theValue = null; - if (caseSensitive) - theValue = theTable.get(key); - else - { - String matchString = key.toUpperCase(); - for (Enumeration e = theTable.keys(); e.hasMoreElements();) - { - String theKey = (String) e.nextElement(); - if (matchString.equals(theKey.toUpperCase())) - theValue = theTable.get(theKey); - } - } - if (theValue == null) - return ""; //$NON-NLS-1$ - return (String) theValue; - } - private String getKey(String var) - { - int index = var.indexOf("="); //$NON-NLS-1$ - if (index < 0) - return var; - return var.substring(0, index); - } - private String getValue(String var) - { - var = var.replaceAll("}","\n}"); //$NON-NLS-1$ //$NON-NLS-2$ - int index = var.indexOf("=") + 1; //$NON-NLS-1$ - int varLength = var.length(); - if ((index < 1) || (index == var.length())) - return ""; //$NON-NLS-1$ - return var.substring(index, varLength); - } - private Hashtable mapVars(List theVars) - { - Hashtable theTable = new Hashtable(); - int theSize = theVars.size(); - for (int i = 0; i < theSize; i++) - { - String theVar = ((DataElement) theVars.get(i)).getValue(); - theTable.put(getKey(theVar), getValue(theVar)); - } - return theTable; - } - private String[] listToArray(List theList) - { - if (theList == null) - theList = new ArrayList(); - int theSize = theList.size(); - String theArray[] = new String[theSize]; - for (int i = 0; i < theSize; i++) - theArray[i] = ((DataElement) theList.get(i)).getValue(); - return theArray; - } - private String[] tableToArray(Hashtable theTable) - { - if (theTable == null) - theTable = new Hashtable(); - int theSize = theTable.size(); - String theArray[] = new String[theSize]; - int i = 0; - for (Enumeration e = theTable.keys(); e.hasMoreElements();) - { - String theKey = (String) e.nextElement(); - String theValue = (String) theTable.get(theKey); - theArray[i++] = theKey + "=" + theValue; //$NON-NLS-1$ - } - return theArray; - } - public boolean doThreadedWork() - { - - if (((_stdOutputHandler == null) || _stdOutputHandler.isFinished()) && ((_stdErrorHandler == null) || _stdErrorHandler.isFinished())) - { - return false; - } - else - { - return true; - } - } - public void initializeThread() - { - } - - public void sendExit() - { - if (_isShell) - { - sendInput("exit"); //$NON-NLS-1$ - - // in case exit doesn't end it - try - { - Thread.sleep(1000); - } - catch (Exception e) - { - - } - if (_stdOutputHandler.isAlive() && _theProcess != null) - { - _theProcess.destroy(); - } - } - } - - - - public void cleanupThread() - { - - _isDone = true; - try - { - - - - /* - if (_isShell) - { - sendInput("#exit"); - }*/ - - if (_theProcess != null) - { - int exitcode; - try - { - if (_isCancelled) - { - _theProcess.destroy(); - } - else - { - exitcode = _theProcess.exitValue(); - createObject("prompt", "> Shell Completed (exit code = " + exitcode + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - } - } - catch (IllegalThreadStateException e) - { - exitcode = -1; - _theProcess.destroy(); - } - _theProcess = null; - } - - - _stdOutputHandler.finish(); - _stdErrorHandler.finish(); - _stdInput.close(); - _stdError.close(); - - _status.setAttribute(DE.A_NAME, "done"); //$NON-NLS-1$ - _dataStore.refresh(_status); - - // disconnecting all - - _dataStore.disconnectObjects(_status); - - - // clean up the associated environment - List projectEnvReference = _subject.getAssociated("inhabits"); //$NON-NLS-1$ - - if (projectEnvReference != null && projectEnvReference.size() > 0) - { - DataElement env = (DataElement)projectEnvReference.get(0); - DataElement envParent = env.getParent(); - _dataStore.deleteObject(envParent, env); - _dataStore.refresh(envParent); - } - _dataStore.disconnectObject(_subject); //bug 70420 - } - catch (IOException e) - { - _dataStore.trace(e); - } - } - - - public void interpretLine(String line, boolean stdError) - { - // Line wrapping here is due to the fix for an internal IBM bug: - // https://cs.opensource.ibm.com/tracker/index.php?func=detail&aid=65874&group_id=1196&atid=1622 - // - // Here is the description written by Song Wu: - // - // In the command shell, the message displayed might be too long to be displayed on one line. It's truncated currently. - // Hover over doesn't help. The message needs to be wrapped. - // -------------------------------------------------------- - // - // The problem was resolved by forcing lines to be wrapped (in this case using 100 as the max line length): - // int maxLine = 100; - // - // I think this was really just a workaround for the real problem - where the Windows table column imposes a - // limit on the number of chars displayed. - // - // The problem with the forced line wrapping fix is that it introduces bug 284179. I think bug 284179 is a - // worse problem and therefore I'm in favour of increasing the max line to 4096 as suggested by Chris Recoskie. - // - // A new property, DSTORE_SHELL_MAX_LINE allows for the customization of this value now. The default - // is 4096. - // - int num = line.length(); - String[] lines = new String[num/_maxLineLength+1]; - if(lines.length>1) - { - int beg=0; - int end=_maxLineLength; - for(int i=0;i<lines.length;i++) - { - //try/catch put in for testing purposes - //try - //{ - if(end>line.length()) - { - lines[i]=line.substring(beg); - } - else - { - lines[i]=line.substring(beg,end); - } - beg=end; - end=end+_maxLineLength; - //} - //catch(Exception e) - //{ - // createObject(_descriptors._stdout, "<<EXCEPTION>> line:= " + num + " beg : " + beg + " end = " + end); - // return; - //} - } - } - else - { - lines[0]=line; - } - - for(int i=0;i<lines.length;i++) - { - line=lines[i]; - - // for prompting - if (line.startsWith("<PWD")) //$NON-NLS-1$ - { - // special processing - String statement = line.substring(1); - String pair[] = statement.split("="); //$NON-NLS-1$ - String value = pair[1]; - _status.setAttribute(DE.A_SOURCE, value); - - return; - } - if (line.indexOf("echo '<'PWD=$PWD") > 0) //$NON-NLS-1$ - { - // ignore this line - } - else if (line.indexOf("PS1='$PWD>';") > 0){ //$NON-NLS-1$ - // ignore this line too - } - else - { - ParsedOutput parsedMsg = null; - - try - { - parsedMsg = _patterns.matchLine(removeWhitespace(line)); - } - catch (Throwable e) - { - _dataStore.trace(e); - } - if (parsedMsg == null) - { - if (stdError) - { - createObject(_descriptors._stderr, line); - } - else - { - createObject(_descriptors._stdout, line); - } - } - else - { - try - { - String fileName = parsedMsg.file; - if (parsedMsg.type.equals("prompt")) //$NON-NLS-1$ - { - int tildaIndex = fileName.indexOf("~"); //$NON-NLS-1$ - if (tildaIndex == 0) - { - String userHome = null; - - if (_dataStore.getClient() != null){ - userHome = _dataStore.getClient().getProperty("user.home"); //$NON-NLS-1$ - } - else { - userHome = System.getProperty("user.home"); //$NON-NLS-1$ - } - - fileName = userHome + fileName.substring(1); - } - - - File promptFile = new File(fileName); - if (promptFile.exists()) - { - createPrompt(line, fileName); - } - else - { - createObject(_descriptors._stdout, line); - } - } - else if (parsedMsg.type.equals("file")) //$NON-NLS-1$ - { - createObject(parsedMsg.type, line, fileName, null); - } - else - { - createObject(parsedMsg.type, line, fileName, new Integer(parsedMsg.line)); - } - } - catch (NumberFormatException e) - { - _dataStore.trace(e); - } - } - } - } - - // moving this to do refresh after serious of lines interpretted - //refreshStatus(); - } - - public void refreshStatus() - { - _dataStore.refresh(_status); - } - - public void createPrompt(String line, String fileName) - { - // prevent duplicate prompts - DataElement object = null; - int size = _status.getNestedSize(); - if (size > 0) - { - DataElement lastObject = _status.get(size - 1); - if (!lastObject.getType().equals("prompt")) //$NON-NLS-1$ - { - line = line.replaceAll("//", "/"); //$NON-NLS-1$ //$NON-NLS-2$ - fileName = fileName.replaceAll("//", "/"); //$NON-NLS-1$ //$NON-NLS-2$ - object = createObject("prompt", line, fileName, null); //$NON-NLS-1$ - - _lastPrompt = object; - _cwdStr = object.getSource(); - _status.setAttribute(DE.A_SOURCE, fileName); - } - } - } - - public String removeWhitespace(String theLine) - { - StringBuffer strippedLine = new StringBuffer(); - boolean inWhitespace = true; - char curChar; - for (int i = 0; i < theLine.length(); i++) - { - curChar = theLine.charAt(i); - if (curChar == '\t') - { - if (!inWhitespace) - { - strippedLine.append(' '); - inWhitespace = true; - } - } - else if (curChar == ' ') - { - if (!inWhitespace) - { - strippedLine.append(' '); - inWhitespace = true; - } - } - else - { - strippedLine.append(curChar); - inWhitespace = false; - } - } - return strippedLine.toString(); - } - - /************************************************************************************************ - private void createObject (String,String) - Create a simple object with no source information - *************************************************************************************************/ - public DataElement createObject(String type, String text) - { - DataElement newObj = null; - DataElement descriptorType = _descriptors.getDescriptorFor(type); - if (descriptorType != null) - { - newObj = _dataStore.createObject(_status, descriptorType, text, ""); //$NON-NLS-1$ - } - else - { - newObj = _dataStore.createObject(_status, type, text, ""); //$NON-NLS-1$ - } - return newObj; - } - - public DataElement createObject(DataElement type, String text) - { - return _dataStore.createObject(_status, type, text, ""); //$NON-NLS-1$ - } - - /************************************************************************************************ - private void createObject (String,String,String,Integer,Integer) - - Create an object that can contain file information as well as line an column. - Note: currently our editors do not support jumping to a column, so neither - do we here. - *************************************************************************************************/ - private DataElement createObject(String type, String text, String file, Integer line) - { - DataElement descriptorType = null; - if (file != null && file.length() > 0) - { - boolean foundFile = false; - String expectedPath = null; - File aFile = new File(file); - if (type.equals("prompt")) //$NON-NLS-1$ - { - descriptorType = _descriptors._prompt; - expectedPath = file; - _cwdStr = file.replaceAll("//", "/"); //$NON-NLS-1$ //$NON-NLS-2$ - } - else if (aFile.exists()) - { - expectedPath = aFile.getAbsolutePath(); - file = expectedPath; - if (aFile.isDirectory() && type.equals("file")) //$NON-NLS-1$ - { - type = "directory"; //$NON-NLS-1$ - } - foundFile = true; - } - else if (_cwdStr.endsWith("/")) //$NON-NLS-1$ - { - if (file.equals("/")) //$NON-NLS-1$ - { - expectedPath = _cwdStr; - } - else - { - expectedPath = _cwdStr + file; - } - } - else - { - expectedPath = _cwdStr + "/" + file; //$NON-NLS-1$ - } - - if (!foundFile) - { - - File qfile = new File(expectedPath); - if (!qfile.exists()) - { - expectedPath = file; - qfile = new File(expectedPath); - if (qfile.exists()) - { - if (qfile.isDirectory() && type.equals("file")) //$NON-NLS-1$ - { - type = "directory"; //$NON-NLS-1$ - } - } - else - { - File cwdFile = new File(_cwdStr); - String cwdParent = cwdFile.getAbsolutePath(); - if (cwdFile.getParent() != null) - { - cwdParent = cwdFile.getParentFile().getAbsolutePath(); - } - - if (cwdParent.endsWith("/")) //$NON-NLS-1$ - { - expectedPath = cwdParent + file; - } - else - { - expectedPath = cwdParent + "/" + file; //$NON-NLS-1$ - } - - qfile = new File(expectedPath); - if (qfile.exists()) - { - if (qfile.isDirectory() && type.equals("file")) //$NON-NLS-1$ - { - type = "directory"; //$NON-NLS-1$ - } - file = expectedPath; - } - else - { - // no match, so can't be a file - if (type.equals("file")) //$NON-NLS-1$ - { - type = "stdout"; //$NON-NLS-1$ - descriptorType = _descriptors._stdout; - } - else if (type.equals("error")) //$NON-NLS-1$ - { - type = "stderr"; //$NON-NLS-1$ - descriptorType = _descriptors._stderr; - } - else - { - type = "stdout"; //$NON-NLS-1$ - descriptorType = _descriptors._stdout; - } - } - } - } - else - { - if (qfile.isDirectory() && type.equals("file")) //$NON-NLS-1$ - { - type = "directory"; //$NON-NLS-1$ - expectedPath = expectedPath.replaceAll("//", "/"); //$NON-NLS-1$ //$NON-NLS-2$ - } - file = expectedPath; - } - } - - - DataElement obj = null; - if (line == null || (line.intValue() == 1)) - { - if (descriptorType != null) - { - obj = _dataStore.createObject(_status, descriptorType, text, file); - } - else - { - obj = _dataStore.createObject(_status, type, text, file); - } - } - else - { - if (descriptorType != null) - { - obj = _dataStore.createObject(_status, descriptorType, text, file); - } - else - { - obj = _dataStore.createObject(_status, type, text, file); - } - obj.setAttribute(DE.A_SOURCE, obj.getSource() + ':' + line.toString()); - } - - return obj; - } - else - { - - return createObject(type, text); - } - } -} +/******************************************************************************* + * Copyright (c) 2003, 2013 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Initial Contributors: + * The following IBM employees contributed to the Remote System Explorer + * component that contains this file: David McKnight, Kushal Munir, + * Michael Berger, David Dykstal, Phil Coulthard, Don Yantzi, Eric Simpson, + * Emily Bruner, Mazen Faraj, Adrian Storisteanu, Li Ding, and Kent Hawley. + * + * Contributors: + * {Name} (company) - description of contribution. + * David McKnight (IBM) - [202822] updating cleanup + * David McKnight (IBM) - [196624] dstore miner IDs should be String constants rather than dynamic lookup + * Noriaki Takatsu (IBM) - [220126] [dstore][api][breaking] Single process server for multiple clients + * David McKnight (IBM) [224906] [dstore] changes for getting properties and doing exit due to single-process capability + * David McKnight (IBM) [250203] [dstore][shells]%var% is substituted to null in Unix shell + * David McKnight (IBM) [249715] [dstore][shells] Unix shell does not echo command + * David McKnight (IBM) [153275] [dstore-shells] Ctrl+C does not break remote program + * David McKnight (IBM) [284179] [dstore] commands have a hard coded line length limit of 100 characters + * David McKnight (IBM) - [286671] Dstore shell service interprets < and > sequences + * David McKnight (IBM) [290743] [dstore][shells] allow bash shells and custom shell invocation + * David McKnight (IBM) [287305] [dstore] Need to set proper uid for commands when using SecuredThread and single server for multiple clients[ + * Peter Wang (IBM) [299422] [dstore] OutputHandler.readLines() not compatible with servers that return max 1024bytes available to be read + * David McKnight (IBM) [302174] [dstore] shell init command can potentially get called too late + * David McKnight (IBM) [302724] problems with environment variable substitution + * David McKnight (IBM) [302996] [dstore] null checks and performance issue with shell output + * David McKnight (IBM) [308246] [dstore] fix for Bug 287305 breaks on z/OS due to "su" usage + * David McKnight (IBM) [323262] [dstore] zos shell does not display [ ] brackets properly + * David McKnight (IBM) [395465] [dstore][shells] customer hit an NPE on shell cleanup + *******************************************************************************/ + +package org.eclipse.rse.internal.dstore.universal.miners.command; + + + +import java.io.BufferedWriter; +import java.io.DataInputStream; +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.UnsupportedEncodingException; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.List; + +import org.eclipse.dstore.core.miners.MinerThread; +import org.eclipse.dstore.core.model.Client; +import org.eclipse.dstore.core.model.DE; +import org.eclipse.dstore.core.model.DataElement; +import org.eclipse.dstore.core.model.DataStoreAttributes; +import org.eclipse.rse.dstore.universal.miners.CommandMiner; +import org.eclipse.rse.dstore.universal.miners.IUniversalDataStoreConstants; +import org.eclipse.rse.internal.dstore.universal.miners.command.patterns.ParsedOutput; +import org.eclipse.rse.internal.dstore.universal.miners.command.patterns.Patterns; + + + +/** + * CommandMinerThread is used for running and handling io for shell commands + * in a thread. + */ +public class CommandMinerThread extends MinerThread +{ + class InitRunnable implements Runnable + { + private boolean _done = false; + private String _initCmd; + + public InitRunnable(String command){ + _initCmd = command; + } + + public boolean isDone(){ + return _done; + } + + public void run() + { + // wait a second so the profile can complete startup + try { + sleep(1000); + } + catch (Exception e) + { + } + + _done = true; // setting before the call so that sendInput doesn't wait on this + sendInput(_initCmd); + } + } + + private DataElement _status; + private String _invocation; + + private DataInputStream _stdInput; + private DataInputStream _stdError; + + + private BufferedWriter _stdOutput; + + private Patterns _patterns; + + private Process _theProcess; + + private DataElement _subject; + private String _cwdStr; + private OutputHandler _stdOutputHandler; + private OutputHandler _stdErrorHandler; + private boolean _isShell; + private boolean _isDone; + private boolean _isWindows; + private boolean _isTTY; + private boolean _didInitialCWDQuery = false; + + private int _maxLineLength = 4096; + + + private CommandMiner.CommandMinerDescriptors _descriptors; + + // default + private String PSEUDO_TERMINAL; + + private DataElement _lastPrompt; + private InitRunnable _initRunnable; + private Thread _cdThread; + + public CommandMinerThread(DataElement theElement, String invocation, DataElement status, Patterns thePatterns, CommandMiner.CommandMinerDescriptors descriptors) + { + super(theElement.getDataStore()); + _isShell = false; + _isDone = false; + _status = status; + _descriptors = descriptors; + boolean isBash = false; + + _subject = theElement; + + String maxLineLengthStr = System.getProperty("DSTORE_SHELL_MAX_LINE"); //$NON-NLS-1$ + if (maxLineLengthStr != null) + { + try { + _maxLineLength = Integer.parseInt(maxLineLengthStr); + } + catch (NumberFormatException e) + {} + } + + String theOS = System.getProperty("os.name"); //$NON-NLS-1$ + + _invocation = invocation.trim(); + _patterns = thePatterns; + _patterns.refresh(_invocation); + + boolean isZ = theOS.toLowerCase().startsWith("z");//$NON-NLS-1$ + + if (isZ) + { + String inCoding = System.getProperty("dstore.stdin.encoding"); //$NON-NLS-1$ + if (inCoding == null || inCoding.length() == 0){ + // IBM-1047 works better on z/OS than cp037 + System.setProperty("dstore.stdin.encoding","IBM-1047"); //$NON-NLS-1$ //$NON-NLS-2$ + } + } + + _isWindows = theOS.toLowerCase().startsWith("win"); //$NON-NLS-1$ + if (!_isWindows) + { + PSEUDO_TERMINAL = _dataStore.getAttribute(DataStoreAttributes.A_PLUGIN_PATH) + File.separatorChar + "rseterm" + File.separatorChar + "rseterm"; //$NON-NLS-1$ //$NON-NLS-2$ + } + + try + { + String suCommand = null; + String userHome = null; + Client client = _dataStore.getClient(); + + if (client != null && !theOS.equals("z/OS")){ //$NON-NLS-1$ + String clientActualUserId = client.getProperty("user.name");//$NON-NLS-1$ + String clientUserId = client.getUserid(); + + userHome = client.getProperty("user.home");//$NON-NLS-1$ + if (clientUserId != null && !clientActualUserId.equals(clientUserId)){ + suCommand = "su " + clientUserId + " -c "; //$NON-NLS-1$ //$NON-NLS-2$ + } + } + else { + userHome = System.getProperty("user.home");//$NON-NLS-1$ + } + + _cwdStr = theElement.getSource(); + if (_cwdStr == null || _cwdStr.length() == 0) + { + _cwdStr = userHome; + } + + File theDirectory = new File(_cwdStr); + if (!theDirectory.isDirectory()) + theDirectory = theDirectory.getParentFile(); + try + { + _cwdStr = theDirectory.getAbsolutePath(); + } + catch (Exception e) + { + _cwdStr = userHome; + } + _status.setAttribute(DE.A_SOURCE, _cwdStr); + + + boolean didLogin = false; + + String theShell = null; + if (!_isWindows) + { + File psuedoTerminal = new File(PSEUDO_TERMINAL); + if (psuedoTerminal.exists()) + { + _isTTY = true; + PSEUDO_TERMINAL = psuedoTerminal.getAbsolutePath(); + } + else + { + _isTTY = false; + } + + + + + + _patterns.setIsTerminal(_isTTY); + + String property = "SHELL="; //$NON-NLS-1$ + + String[] env = getEnvironment(_subject); + boolean isBashonZ = false; + boolean isSHonZ = false; + + for (int i = 0; i < env.length; i++) + { + String var = env[i]; + if (var.startsWith(property)) + { + theShell = var.substring(property.length(), var.length()); + if (theShell.endsWith("bash"))//$NON-NLS-1$ + { + if (isZ) + { + isBashonZ = true; + } + else + { + isBash = true; + } + } + else if (theShell.endsWith("sh") && isZ)//$NON-NLS-1$ + { + isSHonZ = true; + } + } + // In a single-process server, both user.home and HOME don't represent + // each client home directory. + if (_dataStore.getClient() != null) + { + if (var.startsWith("HOME")) //$NON-NLS-1$ + { + env[i] = "HOME=" + _dataStore.getClient().getProperty("user.home"); //$NON-NLS-1$ //$NON-NLS-2$ + } + } + } + + // check for custom shell invocation + String customShellInvocation = System.getProperty("DSTORE_SHELL_INVOCATION"); //$NON-NLS-1$ + if (customShellInvocation != null && customShellInvocation.length() > 0){ + theShell = customShellInvocation; + } + + if (theShell == null) + { + if (_invocation.equals(">")) //$NON-NLS-1$ + { + _invocation = "sh"; //$NON-NLS-1$ + + _isShell = true; + if (isZ) + isSHonZ = true; + } + + + if (_isTTY) + { + if (isSHonZ) + { + String args[] = new String[3]; + args[0] = PSEUDO_TERMINAL; + args[1] = "sh"; //$NON-NLS-1$ + args[2] = "-L"; //$NON-NLS-1$ + + try { + _theProcess = Runtime.getRuntime().exec(args, env, theDirectory); + } + catch (Exception e) { + _theProcess = Runtime.getRuntime().exec(_invocation, env, theDirectory); + } + didLogin = true; + } + else + { + List argsList = new ArrayList(); + + if (suCommand != null){ + String[] suSplit = suCommand.split(" "); //$NON-NLS-1$ + for (int i = 0; i < suSplit.length; i++){ // su before terminal + argsList.add(suSplit[i]); + } + } + argsList.add(PSEUDO_TERMINAL); + argsList.add(invocation); + + + String args[] = (String[])argsList.toArray(new String[argsList.size()]); + _theProcess = Runtime.getRuntime().exec(args, env, theDirectory); + } + } + else + { + //VRB: for Linux on System z we end up here + if (suCommand!=null) + _invocation = suCommand + _invocation; + _theProcess = Runtime.getRuntime().exec(_invocation, env, theDirectory); + } + } + else + { + if (_invocation.equals(">")) //$NON-NLS-1$ + { + _invocation = theShell; + + + _isShell = true; + + if (_isTTY) + { + List argsList = new ArrayList(); + + if (!isBashonZ && !isSHonZ && suCommand != null){ + // su before starting rseterm + String[] suArgs = suCommand.split(" "); //$NON-NLS-1$ + for (int i = 0; i < suArgs.length; i++){ + argsList.add(suArgs[i]); + } + } + argsList.add(PSEUDO_TERMINAL); + + if (!isBashonZ && !isSHonZ && suCommand != null){ + // need sh -c before invocation + argsList.add("sh"); //$NON-NLS-1$ + argsList.add("-c"); //$NON-NLS-1$ + } + else { + + argsList.add("-w"); //$NON-NLS-1$ + argsList.add(""+_maxLineLength); //$NON-NLS-1$ + } + + argsList.add(_invocation); + + if (isBashonZ){ + argsList.add("--login"); //$NON-NLS-1$ + didLogin = true; + } + else if (isBash){ + argsList.add("-l"); //$NON-NLS-1$ + didLogin = true; + } + else if (isSHonZ){ + argsList.add("-L"); //$NON-NLS-1$ + didLogin = true; + } + + String args[] = (String[])argsList.toArray(new String[argsList.size()]); + + try { + _theProcess = Runtime.getRuntime().exec(args, env, theDirectory); + } + catch (Exception e) { + _theProcess = Runtime.getRuntime().exec(_invocation, env, theDirectory); + } + + } + else + { + if (!isBashonZ && !isSHonZ && suCommand != null){ + _invocation = suCommand + _invocation; + } + + if (customShellInvocation != null && customShellInvocation.length() > 0){ + // all handled in the custom shell invocation + _theProcess = Runtime.getRuntime().exec(_invocation, env, theDirectory); + } + else { + if (isBashonZ) + { + _theProcess = Runtime.getRuntime().exec(_invocation + " --login", env, theDirectory); //$NON-NLS-1$ + didLogin = true; + } + else if (isBash) + { + _theProcess = Runtime.getRuntime().exec(_invocation + " -l", env, theDirectory); //$NON-NLS-1$ + didLogin = true; + } + else if (isSHonZ) + { + _theProcess = Runtime.getRuntime().exec(_invocation + " -L", env, theDirectory); //$NON-NLS-1$ + didLogin = true; + } + else + { + _theProcess = Runtime.getRuntime().exec(_invocation, env, theDirectory); + } + } + } + } + else + { + if (suCommand != null){ + theShell = suCommand + theShell; + } + + List argsList = new ArrayList(); + + String[] shellArray = theShell.split(" "); //$NON-NLS-1$ + for (int i = 0; i < shellArray.length; i++){ + argsList.add(shellArray[i]); + } + argsList.add("-c"); //$NON-NLS-1$ + argsList.add(_invocation); + + String args[] = (String[])argsList.toArray(new String[argsList.size()]); + _theProcess = Runtime.getRuntime().exec(args, env, theDirectory); + } + } + } + else // windows + { + if ((theOS.indexOf("95") >= 0) || (theOS.indexOf("98") >= 0) || (theOS.indexOf("ME") >= 0)) //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + { + theShell = "start"; //$NON-NLS-1$ + } + else + { + theShell = "cmd"; //$NON-NLS-1$ + } + if (_invocation.equals(">")) //$NON-NLS-1$ + { + _invocation = theShell; + _isShell = true; + } + + + String args[] = new String[3]; + args[0]= theShell; + if (theShell.equals("start")) //$NON-NLS-1$ + { + args[1] = "/B "; //$NON-NLS-1$ + } + else + { + args[1] = "/C "; //$NON-NLS-1$ + } + args[2] = _invocation; + + String[] env = getEnvironment(_subject); + + if (_invocation.equals(theShell)) + { + + _theProcess = Runtime.getRuntime().exec(_invocation, env, theDirectory); + } + else + { + _theProcess = Runtime.getRuntime().exec(args, env, theDirectory); + } + + } + + + + OutputStream output = _theProcess.getOutputStream(); + _stdInput = new DataInputStream(_theProcess.getInputStream()); + _stdError = new DataInputStream(_theProcess.getErrorStream()); + + + String specialEncoding = System.getProperty("dstore.stdin.encoding"); //$NON-NLS-1$ + + if (specialEncoding != null) + { + /* + _stdInput = new BufferedReader(new InputStreamReader(_theProcess.getInputStream(), specialEncoding)); + _stdError = new BufferedReader(new InputStreamReader(_theProcess.getErrorStream(), specialEncoding)); + */ + try + { + _stdOutput = new BufferedWriter(new OutputStreamWriter(output, specialEncoding)); + } + catch (UnsupportedEncodingException e) + { + _stdOutput = new BufferedWriter(new OutputStreamWriter(output)); + } + } + else + { + //_stdInput = new BufferedReader(new InputStreamReader(_theProcess.getInputStream())); + //_stdError = new BufferedReader(new InputStreamReader(_theProcess.getErrorStream())); + _stdOutput = new BufferedWriter(new OutputStreamWriter(output)); + } + + createObject("command", _invocation); //$NON-NLS-1$ + createObject("stdout", ""); //$NON-NLS-1$ //$NON-NLS-2$ + + status.setAttribute(DE.A_NAME, "progress"); //$NON-NLS-1$ + _dataStore.update(status); + _dataStore.disconnectObjects(status); + + _stdOutputHandler = new OutputHandler(_stdInput, null, _isWindows || _isTTY, false, _isShell, this); + _stdOutputHandler.setWaitTime(100); + _stdOutputHandler.setDataStore(_dataStore); + _stdOutputHandler.start(); + + _stdErrorHandler = new OutputHandler(_stdError, null, _isWindows || _isTTY, true, _isShell, this); + _stdErrorHandler.setWaitTime(100); + _stdOutputHandler.setDataStore(_dataStore); + _stdErrorHandler.start(); + + // initialization + if (didLogin || _isTTY) + { + String initCmd = ""; //$NON-NLS-1$ + if (_isTTY){ + initCmd = "export PS1='$PWD>';" ; //$NON-NLS-1$ + } + if (didLogin && !userHome.equals(_cwdStr)){ + initCmd += "cd " + _cwdStr; //$NON-NLS-1$ + } + + // need to CD to the correct directory + _initRunnable = new InitRunnable(initCmd); + _cdThread = new Thread(_initRunnable); + _cdThread.start(); + } + else if (_isShell && !_isWindows && !_isTTY) + { + createPrompt(_cwdStr +">", _cwdStr); //$NON-NLS-1$ + refreshStatus(); + } + + } + catch (IOException e) + { + _theProcess = null; + _dataStore.trace(e); + createObject("command", e.getMessage()); //$NON-NLS-1$ + status.setAttribute(DE.A_NAME, "done"); //$NON-NLS-1$ + return; + } + + } + + +// private String[] parseArgs(String full) +// { +// StringBuffer result = new StringBuffer(); +// char[] chars = full.toCharArray(); +// boolean inQuotes = false; +// boolean escaping = false; +// for (int i = 0; i < chars.length; i++) +// { +// char c = chars[i]; +// if (c == '\"') +// { +// inQuotes = !inQuotes; +// } +// else +// { +// if (c == '\\') +// { +// escaping = true; +// } +// else +// { +// if (c == ' ') +// { +// if (!inQuotes && !escaping) +// { +// c = ','; +// } +// escaping = false; +// } +// } +// result.append(c); +// } +// +// } +// return result.toString().split(","); +// } + + public Process getProcess() + { + return _theProcess; + } + + public String getCWD() + { + return _cwdStr; + } + + public void queryCWD() + { + BufferedWriter writer = _stdOutput; + try + { + // hidden command + writer.write("echo '<'PWD=$PWD"); //$NON-NLS-1$ + writer.newLine(); + writer.flush(); + } + catch (Exception e) + { + } + _didInitialCWDQuery = true; + + } + + + public void sendInput(String input) + { + if (!_isDone) + { + if (_initRunnable != null && !_initRunnable.isDone()){ + try { + _cdThread.join(); + } + catch (InterruptedException e){} + } + + String origInput = input; + input.getBytes(); + + try + { + BufferedWriter writer = _stdOutput; + // pty executable handles the break now + if (input.equals("#break") && !_isTTY) //$NON-NLS-1$ + { + // if no pty, then do it explicitly + _theProcess.destroy(); + + return; + } + else if (input.equals("#enter")) //$NON-NLS-1$ + { + writer.newLine(); + writer.flush(); + return; + } + + if (_isShell) + { + if (_lastPrompt != null) + { + if (!_isTTY) + { + String promptText = _lastPrompt.getName(); + if (promptText.endsWith(">")) //$NON-NLS-1$ + { + _lastPrompt.setAttribute(DE.A_NAME, promptText + input); + _dataStore.refresh(_lastPrompt); + } + + else + { +// String cwd = getCWD(); +// String line = cwd + ">" + input; + //createObject("prompt", line); + //createPrompt(line, cwd); + } + } + } + + _patterns.update(input); + } + + if (!_isWindows && !_isTTY) + { + createObject("input", origInput); //$NON-NLS-1$ + } + + writer.write(input); + writer.newLine(); + try{ + writer.flush(); + } + catch (Exception e){ + //TODO find actual cause of problem. This only fails on certain machines. + } + + if (!_isWindows && (input.startsWith("cd ") || input.equals("cd"))) //$NON-NLS-1$ //$NON-NLS-2$ + { + if (!_isTTY) + queryCWD(); + } + else if (!_didInitialCWDQuery) + { + if (!_isTTY) + queryCWD(); + } + if (!_isWindows && !_isTTY) + { + // always prompt after the command + writer.write("echo $PWD'>'"); //$NON-NLS-1$ + writer.newLine(); + writer.flush(); + } + } + catch (IOException e) + { + cleanupThread(); + } + } + } + private String[] getEnvironment(DataElement theSubject) + { + //Grab the system environment: + DataElement envMiner = _dataStore.findMinerInformation(IUniversalDataStoreConstants.UNIVERSAL_ENVIRONMENT_MINER_ID); + DataElement systemEnv = _dataStore.find(envMiner, DE.A_NAME, "System Environment", 1); //$NON-NLS-1$ + //Walk up until we find an element with an inhabits relationship. + DataElement theProject = theSubject; + List projectEnvReference = null; + while (theProject != null && !theProject.getValue().equals("Data")) //$NON-NLS-1$ + { + projectEnvReference = theProject.getAssociated("inhabits"); //$NON-NLS-1$ + if (projectEnvReference.size() > 0) + break; + theProject = theProject.getParent(); + } + DataElement projectEnv = null; + if (projectEnvReference != null && (projectEnvReference.size() > 0)) + projectEnv = (DataElement) projectEnvReference.get(0); + + String[] theEnv = mergeEnvironments(systemEnv, projectEnv); + + return theEnv; + } + + private String[] mergeEnvironments(DataElement systemEnv, DataElement projectEnv) + { + + List prjVars = null; + List sysVars = null; + //Fill the ArrayLists with the environment variables + if (systemEnv != null) + sysVars = systemEnv.getNestedData(); + if (projectEnv != null) + prjVars = projectEnv.getNestedData(); + //If one or both of the ArrayLists are null, exit early: + if ((sysVars == null) || (sysVars.size() == 0)) + return listToArray(prjVars); + if ((prjVars == null) || (prjVars.size() == 0)) + return listToArray(sysVars); + //If we get here, then we have both system and project variables...to make merging the 2 lists easier, we'll + //use a Hashtable (Variable Names are the keys, Variables Values are the values): + Hashtable varTable = new Hashtable(); + + //First fill the varTable with the sysVars + varTable.putAll(mapVars(sysVars)); + + //Now for every project variable, check to see if it already exists, and if the value contains other variables: + for (int i = 0; i < prjVars.size(); i++) + { + DataElement envElement = (DataElement) prjVars.get(i); + if (!envElement.getType().equals("Environment Variable")) //$NON-NLS-1$ + continue; + String theVariable = envElement.getValue(); + String theKey = getKey(theVariable); + String theValue = getValue(theVariable); + theValue = calculateValue(theValue, varTable); + + varTable.put(theKey, theValue); + } + + + if (_isTTY) + { + varTable.put("PS1","'$PWD/>'"); //$NON-NLS-1$ //$NON-NLS-2$ + + //if (_maxLineLength ) + + varTable.put("COLUMNS","" + _maxLineLength); //$NON-NLS-1$ //$NON-NLS-2$ + } + + + /* DKM: for some reason this isn't getting applied properly here + * but it works via export + * */ + String theOS = System.getProperty("os.name"); //$NON-NLS-1$ + if (theOS.toLowerCase().startsWith("os")) //$NON-NLS-1$ + { + varTable.put("QIBM_JAVA_STDIO_CONVERT","Y"); //$NON-NLS-1$ //$NON-NLS-2$ + varTable.put("QIBM_USE_DESCRIPTOR_STDIO","I"); //$NON-NLS-1$ //$NON-NLS-2$ + varTable.put("PASE_STDIO_ISATTY","N"); //$NON-NLS-1$ //$NON-NLS-2$ + varTable.put("TERMINAL_TYPE","REMOTE"); //$NON-NLS-1$ //$NON-NLS-2$ + varTable.put("STDIO_ISATTY","Y"); //$NON-NLS-1$ //$NON-NLS-2$ + } + + + + return tableToArray(varTable); + } //This method is responsible for replacing variable references with their values. + //We support 3 methods of referencing a variable (assume we are referencing a variable called FOO): + // 1. $FOO - common to most shells (must be followed by a non-alphanumeric or nothing...in other words, we + // always construct the longest name after the $) + // 2. ${FOO} - used when you want do something like ${FOO}bar, since $FOObar means a variable named FOObar not + // the value of FOO followed by "bar". + // 3. %FOO% - Windows command interpreter + private String calculateValue(String value, Hashtable theTable) + { + value = value.replaceAll("}","\n}"); //$NON-NLS-1$ //$NON-NLS-2$ + StringBuffer theValue = new StringBuffer(value); + try + { + int index = 0; + char c; + while (index < theValue.length()) + { + c = theValue.charAt(index); + if (c == '{') + { + index++; + c = theValue.charAt(index); + // skip everything til end quote + while (index < theValue.length() && c != '}') + { + index++; + c = theValue.charAt(index); + } + } + //If the current char is a $, then look for a { or just match alphanumerics + else if (c == '$' && !_isWindows) + { + int nextIndex = index + 1; + if (nextIndex < theValue.length()) + { + c = theValue.charAt(nextIndex); + //If there is a { then we just look for the closing }, and replace the span with the variable value + if (c == '{') + { + int next = theValue.toString().indexOf("}", nextIndex); //$NON-NLS-1$ + if (next > 0) + { + String replacementValue = findValue(theValue.substring(nextIndex + 1, next), theTable, true); + theValue.replace(index, next + 1, replacementValue); + index += replacementValue.length() - 1; + } + } //If there is no { then we just keep matching alphanumerics to construct the longest possible variable name + else + { + if (Character.isJavaIdentifierStart(c)) + { + + while (nextIndex + 1 < theValue.length() && (Character.isJavaIdentifierPart(c))) + { + nextIndex++; + c = theValue.charAt(nextIndex); + + if (nextIndex + 1 == theValue.length()){ // last character? + if (Character.isJavaIdentifierPart(c)){ + nextIndex++; + } + } + } + + String v = theValue.substring(index + 1, nextIndex); + String replacementValue = findValue(v, theTable, true); + theValue.replace(index, nextIndex, replacementValue); + index += replacementValue.length() - 1; + } + } + } + + } //If the current char is a %, then simply look for a matching % + else if (c == '%' && _isWindows) + { + int next = theValue.toString().indexOf("%", index + 1); //$NON-NLS-1$ + if (next > 0) + { + String replacementValue = findValue(theValue.substring(index + 1, next), theTable, false); + theValue.replace(index, next + 1, replacementValue); + index += replacementValue.length() - 1; + } + } + else if (c == '"') + { + index++; + c = theValue.charAt(index); + // skip everything til end quote + while (index < theValue.length() && c != '"') + { + index++; + c = theValue.charAt(index); + } + + } + + index++; + } + } + catch (Throwable e) + { + _dataStore.trace(e); + } + return theValue.toString(); + } + private String findValue(String key, Hashtable theTable, boolean caseSensitive) + { + Object theValue = null; + if (caseSensitive) + theValue = theTable.get(key); + else + { + String matchString = key.toUpperCase(); + for (Enumeration e = theTable.keys(); e.hasMoreElements();) + { + String theKey = (String) e.nextElement(); + if (matchString.equals(theKey.toUpperCase())) + theValue = theTable.get(theKey); + } + } + if (theValue == null) + return ""; //$NON-NLS-1$ + return (String) theValue; + } + private String getKey(String var) + { + int index = var.indexOf("="); //$NON-NLS-1$ + if (index < 0) + return var; + return var.substring(0, index); + } + private String getValue(String var) + { + var = var.replaceAll("}","\n}"); //$NON-NLS-1$ //$NON-NLS-2$ + int index = var.indexOf("=") + 1; //$NON-NLS-1$ + int varLength = var.length(); + if ((index < 1) || (index == var.length())) + return ""; //$NON-NLS-1$ + return var.substring(index, varLength); + } + private Hashtable mapVars(List theVars) + { + Hashtable theTable = new Hashtable(); + int theSize = theVars.size(); + for (int i = 0; i < theSize; i++) + { + String theVar = ((DataElement) theVars.get(i)).getValue(); + theTable.put(getKey(theVar), getValue(theVar)); + } + return theTable; + } + private String[] listToArray(List theList) + { + if (theList == null) + theList = new ArrayList(); + int theSize = theList.size(); + String theArray[] = new String[theSize]; + for (int i = 0; i < theSize; i++) + theArray[i] = ((DataElement) theList.get(i)).getValue(); + return theArray; + } + private String[] tableToArray(Hashtable theTable) + { + if (theTable == null) + theTable = new Hashtable(); + int theSize = theTable.size(); + String theArray[] = new String[theSize]; + int i = 0; + for (Enumeration e = theTable.keys(); e.hasMoreElements();) + { + String theKey = (String) e.nextElement(); + String theValue = (String) theTable.get(theKey); + theArray[i++] = theKey + "=" + theValue; //$NON-NLS-1$ + } + return theArray; + } + public boolean doThreadedWork() + { + + if (((_stdOutputHandler == null) || _stdOutputHandler.isFinished()) && ((_stdErrorHandler == null) || _stdErrorHandler.isFinished())) + { + return false; + } + else + { + return true; + } + } + public void initializeThread() + { + } + + public void sendExit() + { + if (_isShell) + { + sendInput("exit"); //$NON-NLS-1$ + + // in case exit doesn't end it + try + { + Thread.sleep(1000); + } + catch (Exception e) + { + + } + if (_stdOutputHandler != null && _stdOutputHandler.isAlive() && _theProcess != null) + { + _theProcess.destroy(); + } + } + } + + + + public void cleanupThread() + { + + _isDone = true; + try + { + + + + /* + if (_isShell) + { + sendInput("#exit"); + }*/ + + if (_theProcess != null) + { + int exitcode; + try + { + if (_isCancelled) + { + _theProcess.destroy(); + } + else + { + exitcode = _theProcess.exitValue(); + createObject("prompt", "> Shell Completed (exit code = " + exitcode + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + } + catch (IllegalThreadStateException e) + { + exitcode = -1; + _theProcess.destroy(); + } + _theProcess = null; + } + + if (_stdOutputHandler != null) + _stdOutputHandler.finish(); + if (_stdErrorHandler != null) + _stdErrorHandler.finish(); + + if (_stdInput != null) + _stdInput.close(); + if (_stdError != null) + _stdError.close(); + + _status.setAttribute(DE.A_NAME, "done"); //$NON-NLS-1$ + _dataStore.refresh(_status); + + // disconnecting all + + _dataStore.disconnectObjects(_status); + + + // clean up the associated environment + List projectEnvReference = _subject.getAssociated("inhabits"); //$NON-NLS-1$ + + if (projectEnvReference != null && projectEnvReference.size() > 0) + { + DataElement env = (DataElement)projectEnvReference.get(0); + DataElement envParent = env.getParent(); + _dataStore.deleteObject(envParent, env); + _dataStore.refresh(envParent); + } + _dataStore.disconnectObject(_subject); //bug 70420 + } + catch (IOException e) + { + _dataStore.trace(e); + } + } + + + public void interpretLine(String line, boolean stdError) + { + // Line wrapping here is due to the fix for an internal IBM bug: + // https://cs.opensource.ibm.com/tracker/index.php?func=detail&aid=65874&group_id=1196&atid=1622 + // + // Here is the description written by Song Wu: + // + // In the command shell, the message displayed might be too long to be displayed on one line. It's truncated currently. + // Hover over doesn't help. The message needs to be wrapped. + // -------------------------------------------------------- + // + // The problem was resolved by forcing lines to be wrapped (in this case using 100 as the max line length): + // int maxLine = 100; + // + // I think this was really just a workaround for the real problem - where the Windows table column imposes a + // limit on the number of chars displayed. + // + // The problem with the forced line wrapping fix is that it introduces bug 284179. I think bug 284179 is a + // worse problem and therefore I'm in favour of increasing the max line to 4096 as suggested by Chris Recoskie. + // + // A new property, DSTORE_SHELL_MAX_LINE allows for the customization of this value now. The default + // is 4096. + // + int num = line.length(); + String[] lines = new String[num/_maxLineLength+1]; + if(lines.length>1) + { + int beg=0; + int end=_maxLineLength; + for(int i=0;i<lines.length;i++) + { + //try/catch put in for testing purposes + //try + //{ + if(end>line.length()) + { + lines[i]=line.substring(beg); + } + else + { + lines[i]=line.substring(beg,end); + } + beg=end; + end=end+_maxLineLength; + //} + //catch(Exception e) + //{ + // createObject(_descriptors._stdout, "<<EXCEPTION>> line:= " + num + " beg : " + beg + " end = " + end); + // return; + //} + } + } + else + { + lines[0]=line; + } + + for(int i=0;i<lines.length;i++) + { + line=lines[i]; + + // for prompting + if (line.startsWith("<PWD")) //$NON-NLS-1$ + { + // special processing + String statement = line.substring(1); + String pair[] = statement.split("="); //$NON-NLS-1$ + String value = pair[1]; + _status.setAttribute(DE.A_SOURCE, value); + + return; + } + if (line.indexOf("echo '<'PWD=$PWD") > 0) //$NON-NLS-1$ + { + // ignore this line + } + else if (line.indexOf("PS1='$PWD>';") > 0){ //$NON-NLS-1$ + // ignore this line too + } + else + { + ParsedOutput parsedMsg = null; + + try + { + parsedMsg = _patterns.matchLine(removeWhitespace(line)); + } + catch (Throwable e) + { + _dataStore.trace(e); + } + if (parsedMsg == null) + { + if (stdError) + { + createObject(_descriptors._stderr, line); + } + else + { + createObject(_descriptors._stdout, line); + } + } + else + { + try + { + String fileName = parsedMsg.file; + if (parsedMsg.type.equals("prompt")) //$NON-NLS-1$ + { + int tildaIndex = fileName.indexOf("~"); //$NON-NLS-1$ + if (tildaIndex == 0) + { + String userHome = null; + + if (_dataStore.getClient() != null){ + userHome = _dataStore.getClient().getProperty("user.home"); //$NON-NLS-1$ + } + else { + userHome = System.getProperty("user.home"); //$NON-NLS-1$ + } + + fileName = userHome + fileName.substring(1); + } + + + File promptFile = new File(fileName); + if (promptFile.exists()) + { + createPrompt(line, fileName); + } + else + { + createObject(_descriptors._stdout, line); + } + } + else if (parsedMsg.type.equals("file")) //$NON-NLS-1$ + { + createObject(parsedMsg.type, line, fileName, null); + } + else + { + createObject(parsedMsg.type, line, fileName, new Integer(parsedMsg.line)); + } + } + catch (NumberFormatException e) + { + _dataStore.trace(e); + } + } + } + } + + // moving this to do refresh after serious of lines interpretted + //refreshStatus(); + } + + public void refreshStatus() + { + _dataStore.refresh(_status); + } + + public void createPrompt(String line, String fileName) + { + // prevent duplicate prompts + DataElement object = null; + int size = _status.getNestedSize(); + if (size > 0) + { + DataElement lastObject = _status.get(size - 1); + if (!lastObject.getType().equals("prompt")) //$NON-NLS-1$ + { + line = line.replaceAll("//", "/"); //$NON-NLS-1$ //$NON-NLS-2$ + fileName = fileName.replaceAll("//", "/"); //$NON-NLS-1$ //$NON-NLS-2$ + object = createObject("prompt", line, fileName, null); //$NON-NLS-1$ + + _lastPrompt = object; + _cwdStr = object.getSource(); + _status.setAttribute(DE.A_SOURCE, fileName); + } + } + } + + public String removeWhitespace(String theLine) + { + StringBuffer strippedLine = new StringBuffer(); + boolean inWhitespace = true; + char curChar; + for (int i = 0; i < theLine.length(); i++) + { + curChar = theLine.charAt(i); + if (curChar == '\t') + { + if (!inWhitespace) + { + strippedLine.append(' '); + inWhitespace = true; + } + } + else if (curChar == ' ') + { + if (!inWhitespace) + { + strippedLine.append(' '); + inWhitespace = true; + } + } + else + { + strippedLine.append(curChar); + inWhitespace = false; + } + } + return strippedLine.toString(); + } + + /************************************************************************************************ + private void createObject (String,String) + Create a simple object with no source information + *************************************************************************************************/ + public DataElement createObject(String type, String text) + { + DataElement newObj = null; + DataElement descriptorType = _descriptors.getDescriptorFor(type); + if (descriptorType != null) + { + newObj = _dataStore.createObject(_status, descriptorType, text, ""); //$NON-NLS-1$ + } + else + { + newObj = _dataStore.createObject(_status, type, text, ""); //$NON-NLS-1$ + } + return newObj; + } + + public DataElement createObject(DataElement type, String text) + { + return _dataStore.createObject(_status, type, text, ""); //$NON-NLS-1$ + } + + /************************************************************************************************ + private void createObject (String,String,String,Integer,Integer) + + Create an object that can contain file information as well as line an column. + Note: currently our editors do not support jumping to a column, so neither + do we here. + *************************************************************************************************/ + private DataElement createObject(String type, String text, String file, Integer line) + { + DataElement descriptorType = null; + if (file != null && file.length() > 0) + { + boolean foundFile = false; + String expectedPath = null; + File aFile = new File(file); + if (type.equals("prompt")) //$NON-NLS-1$ + { + descriptorType = _descriptors._prompt; + expectedPath = file; + _cwdStr = file.replaceAll("//", "/"); //$NON-NLS-1$ //$NON-NLS-2$ + } + else if (aFile.exists()) + { + expectedPath = aFile.getAbsolutePath(); + file = expectedPath; + if (aFile.isDirectory() && type.equals("file")) //$NON-NLS-1$ + { + type = "directory"; //$NON-NLS-1$ + } + foundFile = true; + } + else if (_cwdStr.endsWith("/")) //$NON-NLS-1$ + { + if (file.equals("/")) //$NON-NLS-1$ + { + expectedPath = _cwdStr; + } + else + { + expectedPath = _cwdStr + file; + } + } + else + { + expectedPath = _cwdStr + "/" + file; //$NON-NLS-1$ + } + + if (!foundFile) + { + + File qfile = new File(expectedPath); + if (!qfile.exists()) + { + expectedPath = file; + qfile = new File(expectedPath); + if (qfile.exists()) + { + if (qfile.isDirectory() && type.equals("file")) //$NON-NLS-1$ + { + type = "directory"; //$NON-NLS-1$ + } + } + else + { + File cwdFile = new File(_cwdStr); + String cwdParent = cwdFile.getAbsolutePath(); + if (cwdFile.getParent() != null) + { + cwdParent = cwdFile.getParentFile().getAbsolutePath(); + } + + if (cwdParent.endsWith("/")) //$NON-NLS-1$ + { + expectedPath = cwdParent + file; + } + else + { + expectedPath = cwdParent + "/" + file; //$NON-NLS-1$ + } + + qfile = new File(expectedPath); + if (qfile.exists()) + { + if (qfile.isDirectory() && type.equals("file")) //$NON-NLS-1$ + { + type = "directory"; //$NON-NLS-1$ + } + file = expectedPath; + } + else + { + // no match, so can't be a file + if (type.equals("file")) //$NON-NLS-1$ + { + type = "stdout"; //$NON-NLS-1$ + descriptorType = _descriptors._stdout; + } + else if (type.equals("error")) //$NON-NLS-1$ + { + type = "stderr"; //$NON-NLS-1$ + descriptorType = _descriptors._stderr; + } + else + { + type = "stdout"; //$NON-NLS-1$ + descriptorType = _descriptors._stdout; + } + } + } + } + else + { + if (qfile.isDirectory() && type.equals("file")) //$NON-NLS-1$ + { + type = "directory"; //$NON-NLS-1$ + expectedPath = expectedPath.replaceAll("//", "/"); //$NON-NLS-1$ //$NON-NLS-2$ + } + file = expectedPath; + } + } + + + DataElement obj = null; + if (line == null || (line.intValue() == 1)) + { + if (descriptorType != null) + { + obj = _dataStore.createObject(_status, descriptorType, text, file); + } + else + { + obj = _dataStore.createObject(_status, type, text, file); + } + } + else + { + if (descriptorType != null) + { + obj = _dataStore.createObject(_status, descriptorType, text, file); + } + else + { + obj = _dataStore.createObject(_status, type, text, file); + } + obj.setAttribute(DE.A_SOURCE, obj.getSource() + ':' + line.toString()); + } + + return obj; + } + else + { + + return createObject(type, text); + } + } +}