Name: mc57594 Date: 03/14/97
There are two related problems here. The first is that pipes to
and from child processes don't work. The second is ( I believe)
related. The first is described in comments in the code.
CommandStreamEditor.doPipe is the method to look at.
The second problem is that when a command shell is launched as a
child, and used to launch another child
( something like cmd /c tar --help ), the command processor opens
a fullscreen window and sends stdout to the window. The window closes
and nothing comes back to the parent of the command shell. After
doing some MSVC ( ewww ) research, I thought
this may be because the command shell is launched with options
indicating that handles should be non-inheritable.
You can see this by doing something like
dir | java CommandStreamEditor
or
java CommandStreamEditor someparam
then type in the name of an executable on the path that writes to stdout.
I've set it up so that if there's nothing in the lower window,
nothing will be sent to the child process. That seems to be
part of the problem - see the comments.
Anyways, here's the source:
=======================================
import java.awt.*;
import java.io.*;
/**
This class reads from stdin, displays what's there in
an editable window. When the window is closed, the contents
are sent to stdout.
*/
class BasicStreamEditor extends Frame
{
private TextArea editWindow;
protected String args[];
public BasicStreamEditor ( String someArgs[] )
{
super();
args = someArgs;
setTitle ( getClass().getName() );
createLayout();
}
protected void createLayout()
{
// make the area where input is displayed
// and which can be edited for the output
editWindow = new TextArea();
editWindow.setForeground ( SystemColor.textText );
editWindow.setBackground ( SystemColor.text );
editWindow.setFont ( new Font ( "Dauphin", Font.PLAIN, 20 ) );
add ( "Center", editWindow );
}
public boolean handleEvent(Event event)
{
if (event.id == Event.WINDOW_DESTROY)
{
// send the contents of the edit area to stdout
System.out.print ( getContents() );
System.exit(0);
}
return super.handleEvent(event);
}
public void setContents ( Reader anInput )
throws IOException
{
BufferedReader input = new BufferedReader ( anInput );
StringBuffer buffer = new StringBuffer ( "" );
String next;
// can't use input.ready() or input.available here
while ( ( next = input.readLine() ) != null )
{
buffer.append ( input.readLine() );
buffer.append ( "\n" );
}
editWindow.setText ( buffer.toString() );
}
// convenience method for InputStream, rather than Reader.
// Silly dichotomy that... ;-)
public void setContents ( InputStream anInput )
{
try
{
setContents ( new BufferedReader ( new InputStreamReader ( anInput ) ) );
}
catch ( IOException exception )
{
editWindow.setText ( "Error\n" );
}
}
public String getContents()
{
return editWindow.getText();
}
public void startEditor()
{
if ( args.length == 0 )
{
try
{
setContents ( System.in );
}
catch ( Exception exception )
{
System.err.println ( exception.getMessage() );
exception.printStackTrace();
System.exit(0);
}
}
pack();
show();
// put the cursor in the edit area
editWindow.requestFocus();
}
// if there are no arguments then stdin is read. Otherwise
// not. This is mainly to allow for debugging.
public static void main ( String args[] )
throws IOException
{
BasicStreamEditor instance = new BasicStreamEditor( args );
instance.startEditor();
}
}
=======================================
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.util.*;
class CommandStreamEditor
extends BasicStreamEditor
implements ActionListener
{
private TextField commandLine;
private Process child;
public CommandStreamEditor ( String someArgs[] )
{
super ( someArgs );
}
protected void createLayout()
{
super.createLayout();
// make the text field into which commands can be typed
commandLine = new TextField();
commandLine.addActionListener ( this );
commandLine.setForeground ( SystemColor.textText );
commandLine.setBackground ( SystemColor.text );
commandLine.setFont ( new Font ( "Lucida Console", Font.PLAIN, 10 ) );
add ( "North", commandLine );
}
public void actionPerformed ( ActionEvent event)
{
// catch the enter pressed
doPipe();
}
// make a child process. Send the contents of the edit window
// to the stdin of the process, and retrieve the stdout of the
// process. Replace the contents of the edit window with the
// contents of stdout from the process.
protected void doPipe()
{
try
{
// get the string that allows child processes and
// shell commands to be executed
/*
Properties localProperties = new Properties();
localProperties.load ( new FileInputStream ( "properties" ) );
// run the specified child command
child = Runtime.getRuntime().exec (
localProperties.getProperty( "shellExecChild" ) + " " + commandLine.getText()
);
*/
// Does this exec allow children of child to inherit
// child's file handles ( stdin stdout and stderr) ?
// It seems not because a command shell ( cmd.exe or 4nt.exe ) // trying to execute its own child
// process seems to always open its own window. The std out from that child
// process always appears ( briefly ) in the window,
// but never gets back to here.
// This bug also affects the CgiServlet in Java Server.
child = Runtime.getRuntime().exec ( commandLine.getText() );
if ( getContents().length() != 0 )
{
try
{
// send the contents of the TextArea to the child command
System.err.println ( "make toChild" );
OutputStreamWriter toChild = new OutputStreamWriter ( child.getOutputStream() );
System.err.println ( "send contents" );
toChild.write ( getContents() );
// make sure the child knows where the pipe ends
System.err.println ( "Flushing..." );
// it stops and waits forever here if the child process
// is ignoring stdin
// But if the child process is killed from somewhere else
// this thread keeps going...
toChild.flush();
System.err.println ( "closing..." );
toChild.close();
System.err.println ( "closed" );
}
catch ( IOException e )
{
System.err.println ( "Error sending contents" );
}
}
// get the output from the child command
System.err.println ( "getting input..." );
// if a process does accept input from stdin,
// then we wait here forever.
// But: if no attempt is made to send input to the
// child, this line works OK.
// If the child process is killed from somewhere else
// most of its ouput is picked up.
setContents ( child.getInputStream() );
System.err.println ( "got input" );
// make sure gc happens
child = null;
}
catch ( IOException exception )
{
exception.printStackTrace();
}
}
// override this to get focus to the right component
public void startEditor()
{
super.startEditor();
commandLine.requestFocus();
}
static public void main ( String args[] )
{
CommandStreamEditor instance = new CommandStreamEditor ( args );
instance.startEditor();
}
}
company - Semiosix , email - ###@###.###
======================================================================