diff --git a/ChangeLog b/ChangeLog index 9b317405c..61d35e45c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +2007-11-12 Mike Kestner + + * glib/IOChannel.cs: IOChannel wrapper implementation. + * glib/Makefile.am: build new files. + * glib/Marshaller.cs: new string array marshaling methods. + * glib/Spawn.cs: g_spawn* wrapper implementation. + * sample/SpawnTests.cs: tests for the new GLib.Process class + and a cursory exercise of IOChannel for SpawnAsyncWithPipes. + 2007-11-09 Mike Kestner * generator/Parameters.cs: support for null_term_array attribute. diff --git a/doc/en/GLib/IOChannel.xml b/doc/en/GLib/IOChannel.xml new file mode 100644 index 000000000..04d1a5adb --- /dev/null +++ b/doc/en/GLib/IOChannel.xml @@ -0,0 +1,472 @@ + + + + glib-sharp + 2.10.0.0 + + + System.Object + + + + GLib.IWrapper + + + System.IDisposable + + + + + + Constructor + + + + + A UNIX file descriptor. + Public constructor. + Constructs a channel for a UNIX file descriptor or pipe. + + + + + Constructor + + + + + + Path to a file. + One of "r", "w", "a", "r+", "w+", "a+", with the same meaning as fopen. + Public constructor. + Constructs a channel for a file with a given mode. + + + + + Method + + System.UInt32 + + + + + + + + Priority level. Default priority is 0. System defined values range from -100 (High priority) to 300 (Low priority). + Conditions to notify. + Notification callback delegate. + Adds a notification watch to the mainloop at a given priority. + A source ID which can be removed with . + + The following example spawns a process to run the pwd command and writes the output to the console using an IOChannel watch: + + + +using GLib; +using System; + +public class SpawnTest { + + public static void Main (string[] args) + { + new SpawnTest (); + } + + MainLoop main_loop; + IOChannel channel; + + public SpawnTest () + { + main_loop = new MainLoop (); + + try { + Process proc; + int stdin = Process.IgnorePipe; + int stdout = Process.RequestPipe; + int stderr = Process.IgnorePipe; + GLib.Process.SpawnAsyncWithPipes (null, new string[] {"pwd"}, null, SpawnFlags.SearchPath, null, out proc, ref stdin, ref stdout, ref stderr); + channel = new IOChannel (stdout); + channel.AddWatch (0, IOCondition.In | IOCondition.Hup, new IOFunc (ReadStdout)); + } catch (Exception e) { + Console.WriteLine ("Exception in Spawn: " + e); + } + + main_loop.Run (); + } + + bool ReadStdout (IOChannel source, IOCondition condition) + { + if ((condition & IOCondition.In) == IOCondition.In) { + string txt; + if (source.ReadToEnd (out txt) == IOStatus.Normal) + Console.WriteLine ("[SpawnTest output] " + txt); + } + if ((condition & IOCondition.Hup) == IOCondition.Hup) { + source.Dispose (); + main_loop.Quit (); + return true; + } + return true; + } +} + + + + + + + + + Property + + GLib.IOCondition + + + BufferCondition property. + Indicates if there is data to read or room to output to the channel. + + + + + + Property + + System.Boolean + + + Buffered property. + A boolean value indicating if buffering is active. + Buffering can only be altered when the is . All other encodings must be buffered. It can only be cleared after the buffer has been flushed. + + + + + Property + + System.UInt64 + + + BufferSize property. + The buffer size, or 0 to pick a reasonable size. + + + + + + Property + + System.Boolean + + + CloseOnUnref property. + A boolean indicating if the channel should be closed on disposal. + + + + + + Method + + System.Void + + + + Disposes the channel. + + + + + + Property + + System.String + + + Encoding property. + Specifies the native encoding of the channel. Use for binary channels. The default encoding is UTF8. + + + + + + Method + + GLib.IOChannelError + + + + + + Error number. + Converts an error number to an Error value. + The error. + + + + + + Property + + GLib.IOFlags + + + Flags property. + The IO Flags for the channel. + + + + + + Method + + GLib.IOStatus + + + + Flushes the write buffer for the channel. + Status of the operation. + + + + + + Method + + GLib.IOChannel + + + + + + Native channel pointer. + Wraps a native channel. + A managed channel instance. + Provided for language binding use. + + + + + Property + + System.IntPtr + + + Handle property. + A point to the native channel. + Provided for language binding use. + + + + + Method + + System.Void + + + + Init method. + Provided for subclassers to initialize derived channels. + + + + + Property + + System.Char[] + + + LineTerminator property. + The chars representing line termination in the channel. + This property is represented as an array of chars to allow for null char terminators. + + + + + Method + + GLib.IOStatus + + + + + + + Buffer to store the read data. + Length of data read. + Reads data from the channel. + An operation status value. + Method fills the buffer with as many complete utf8 characters as possible. Provided primarily for binary data reading in null encodings. Use for text stream reading to strings. + + + + + Method + + GLib.IOStatus + + + + + + Returns the next line in the channel. + Reads a line from the channel. + An operation status value. + + + + + + Method + + GLib.IOStatus + + + + + + + Returns the text read. + Location of line terminator. + Reads the next line in the channel. + An operation status value. + + + + + + Method + + GLib.IOStatus + + + + + + Returns the text read. + Reads to the end of the channel. + An operation status value. + + + + + + Method + + GLib.IOStatus + + + + + + Returns the UCS4 character. + Reads a UCS4 character from the channel. + An operation status value. + + + + + + Method + + GLib.IOStatus + + + + + + + Byte offset. + Base position. + Seeks to a position in the channel. + An operation status value. + + + + + + Method + + GLib.IOStatus + + + + + + A boolean indicating if buffer should be flushed. + Shuts down a channel. + An operation status value. + + + + + + Property + + System.Int32 + + + UnixFd property. + An integer representing the descriptor value. + + + + + + Method + + GLib.IOStatus + + + + + + + Data buffer to write. + Returns the number of bytes written. + Writes binary data to a channel. + An operation status value. + + + + + + Method + + GLib.IOStatus + + + + + + + Text to write. + Returns unwritten text. + Writes text to the channel. + An operation status value. + + + + + + Method + + GLib.IOStatus + + + + + + A UCS4 character. + Writes a UCS4 character to the channel. + An operation status value. + + + + + + IO Channel class. + Provides methods to read and write data to an IO channel. + + diff --git a/doc/en/GLib/IOChannelError.xml b/doc/en/GLib/IOChannelError.xml new file mode 100644 index 000000000..4b3578caf --- /dev/null +++ b/doc/en/GLib/IOChannelError.xml @@ -0,0 +1,106 @@ + + + + glib-sharp + 2.10.0.0 + + + System.Enum + + + + + Field + + GLib.IOChannelError + + + Operation failed. + + + + + Field + + GLib.IOChannelError + + + File too large. + + + + + Field + + GLib.IOChannelError + + + Invalid argument. + + + + + Field + + GLib.IOChannelError + + + IO error. + + + + + Field + + GLib.IOChannelError + + + File is a directory. + + + + + Field + + GLib.IOChannelError + + + No space left on device. + + + + + Field + + GLib.IOChannelError + + + No such device or address. + + + + + Field + + GLib.IOChannelError + + + Value too large for defined data table. + + + + + Field + + GLib.IOChannelError + + + Broken pipe. + + + + + IOChannelError enumeration. + Error results for IOChannel operations. + + diff --git a/doc/en/GLib/IOCondition.xml b/doc/en/GLib/IOCondition.xml new file mode 100644 index 000000000..5d3b9f7d8 --- /dev/null +++ b/doc/en/GLib/IOCondition.xml @@ -0,0 +1,81 @@ + + + + glib-sharp + 2.10.0.0 + + + System.Enum + + + + System.Flags + + + + + + Field + + GLib.IOCondition + + + Error condition. + + + + + Field + + GLib.IOCondition + + + Hung up. The connection has been broken. + + + + + Field + + GLib.IOCondition + + + There is data to read. + + + + + Field + + GLib.IOCondition + + + Invalid request. + + + + + Field + + GLib.IOCondition + + + Data can be written without blocking. + + + + + Field + + GLib.IOCondition + + + There is urgent data to be read. + + + + + IOCondition enumeration. + Flags to configure watches on an event source. + + diff --git a/doc/en/GLib/IOFlags.xml b/doc/en/GLib/IOFlags.xml new file mode 100644 index 000000000..da12219ad --- /dev/null +++ b/doc/en/GLib/IOFlags.xml @@ -0,0 +1,101 @@ + + + + glib-sharp + 2.10.0.0 + + + System.Enum + + + + System.Flags + + + + + + Field + + GLib.IOFlags + + + Enables Append mode. + + + + + Field + + GLib.IOFlags + + + Mask for all flags. + + + + + Field + + GLib.IOFlags + + + Enables read support. + + + + + Field + + GLib.IOFlags + + + Enables seek support. + + + + + Field + + GLib.IOFlags + + + Enables write support. + + + + + Field + + GLib.IOFlags + + + Mask for all flags. + + + + + Field + + GLib.IOFlags + + + Enables non-blocking mode. + + + + + Field + + GLib.IOFlags + + + Enables non-blocking and append mode. + + + + + IOFlags enumeration. + Flags for configuring IO channel usage. + + diff --git a/doc/en/GLib/IOFunc.xml b/doc/en/GLib/IOFunc.xml new file mode 100644 index 000000000..529ebea25 --- /dev/null +++ b/doc/en/GLib/IOFunc.xml @@ -0,0 +1,24 @@ + + + + glib-sharp + 2.10.0.0 + + + System.Delegate + + + + + + + System.Boolean + + + The raising the notification. + The condition prompting the notification. + Callback delegate for IO channel notifications. + If , the delegate is removed from the main loop. + Use an instance of this delegate to add watch notifications for IO channels. + + diff --git a/doc/en/GLib/IOStatus.xml b/doc/en/GLib/IOStatus.xml new file mode 100644 index 000000000..d5f733133 --- /dev/null +++ b/doc/en/GLib/IOStatus.xml @@ -0,0 +1,56 @@ + + + + glib-sharp + 2.10.0.0 + + + System.Enum + + + + + Field + + GLib.IOStatus + + + Resource was temporarily unavailable. + + + + + Field + + GLib.IOStatus + + + End of file reached. + + + + + Field + + GLib.IOStatus + + + An error occurred. + + + + + Field + + GLib.IOStatus + + + Operation completed normally. + + + + + IOStatus enumeration. + Status results for IO channel operations. + + diff --git a/doc/en/GLib/Process.xml b/doc/en/GLib/Process.xml new file mode 100644 index 000000000..b52f2581b --- /dev/null +++ b/doc/en/GLib/Process.xml @@ -0,0 +1,302 @@ + + + + glib-sharp + 2.10.0.0 + + + System.Object + + + + + + Field + + System.Int32 + + 2147483647 + + IgnorePipe field. + Pass this value to to ignore a pipe parameter. + + + + + Field + + System.Int32 + + 0 + + RequestPipe field. + Pass this value to to request a request a pipe descriptor be returned in the parameter. + + + + + Method + + System.Boolean + + + + + + The command line with arguments. + Spawns a child process asynchronously. + A boolean value indicating spawning success. + + + This method does not wait for the child process to complete before returning. It is a convenience method for the more detailed method. + + + +... +try { + GLib.Process.SpawnCommandLineAsync ("echo \"[Async echo process running]\""); +} catch (GException e) { + Console.WriteLine ("Exception in Spawn operation: " + e); +} +... + + + + + + + + + Method + + System.Boolean + + + + + + + + + The command line with arguments. + Returns the stdout output of the child process as a string. + Returns the stderr output of the child process as a string. + Returns the exit code returned by the process. + Spawns a child process synchronously. + A boolean value indicating spawning success. + + The method waits for the child process to complete prior to returning. This is a convenience method for the more detailed method. + + + +... +try { + string stdout, stderr; + int exit_status; + GLib.Process.SpawnCommandLineSync ("pwd", out stdout, out stderr, out exit_status); + Console.Write ("pwd exit_status=" + exit_status + " output: " + stdout); +} catch (GException e) { + Console.WriteLine ("Exception in Spawn operation: " + e); +} +... + + + + + + + + + Method + + System.Boolean + + + + + + + + + + + + + The directory in which to execute the process, or to use the current directory of the parent process. + A string array containing the program name in the first element. + An array of environment values in the form 'var=value', or . + Process spawning configuration flags. + A child process setup callback, or . + Returns the stdout output of the child process as a string. + Returns the stderr output of the child process as a string. + Returns the exit code of the child process. + Spawns a child process synchronously. + A boolean value indicating spawning success. + + The method waits for the child process to complete prior to returning. + + + +... +try { + string stdout, stderr; + int exit_status; + GLib.Process.SpawnSync ("/usr", new string[] {"pwd"}, null, GLib.SpawnFlags.SearchPath, null, out stdout, out stderr, out exit_status); + Console.Write ("pwd exit_status=" + exit_status + " output: " + stdout); +} catch (GException e) { + Console.WriteLine ("Exception in Spawn operation: " + e); +} +... + + + + + + + + + Method + + System.Void + + + + Closes a process. + Processes spawned with must use this method to ensure zombie processes are not stranded. + + + + + Method + + System.Boolean + + + + + + + + + + + The directory in which to execute the process, or to use the current directory of the parent process. + A string array containing the program name in the first element. + An array of environment values in the form 'var=value', or . + Process spawning configuration flags. + A child process setup callback, or . + Returns the child process. + Spawns a child process asynchronously. + A boolean value indicating spawning success. + + The method does not wait for the child process to complete before returning. This is a convenience method for the more detailed method. This method throws if an error occurs creating the process. + + + +... +try { + GLib.Process proc; + GLib.Process.SpawnAsync (null, new string[] {"echo", "[Async echo is running]"}, null, GLib.SpawnFlags.SearchPath, null, out proc); +} catch (GException e) { + Console.WriteLine ("Exception in Spawn operation: " + e); +} +... + + + + + + + + + Method + + System.Boolean + + + + + + + + + + + + + + The directory in which to execute the process, or to use the current directory of the parent process. + A string array containing the program name in the first element. + An array of environment values in the form 'var=value', or . + Process spawning configuration flags. + A child process setup callback, or . + Returns the spawned process. + Returns the stdin pipe descriptor if caller passes anything but . + Returns the stdout pipe descriptor if caller passes anything but . + Returns the stderr pipe descriptor if caller passes anything but . + Spawns a child process and returns requested pipe descriptors. + A boolean value indicating operation success. + + This method throws if an error occurs creating the process. + + + +using GLib; +using System; + +public class SpawnTest { + + public static void Main (string[] args) + { + new SpawnTest (); + } + + MainLoop main_loop; + IOChannel channel; + + public SpawnTest () + { + main_loop = new MainLoop (); + + try { + Process proc; + int stdin = Process.IgnorePipe; + int stdout = Process.RequestPipe; + int stderr = Process.IgnorePipe; + GLib.Process.SpawnAsyncWithPipes (null, new string[] {"pwd"}, null, SpawnFlags.SearchPath, null, out proc, ref stdin, ref stdout, ref stderr); + channel = new IOChannel (stdout); + channel.AddWatch (0, IOCondition.In | IOCondition.Hup, new IOFunc (ReadStdout)); + } catch (Exception e) { + Console.WriteLine ("Exception in Spawn: " + e); + } + + main_loop.Run (); + } + + bool ReadStdout (IOChannel source, IOCondition condition) + { + if ((condition & IOCondition.In) == IOCondition.In) { + string txt; + if (source.ReadToEnd (out txt) == IOStatus.Normal) + Console.WriteLine ("[SpawnTest output] " + txt); + } + if ((condition & IOCondition.Hup) == IOCondition.Hup) { + source.Dispose (); + main_loop.Quit (); + return true; + } + return true; + } +} + + + + + + + + + Process class. + Methods for spawning child processes. + + diff --git a/doc/en/GLib/SeekType.xml b/doc/en/GLib/SeekType.xml new file mode 100644 index 000000000..3cbef9aed --- /dev/null +++ b/doc/en/GLib/SeekType.xml @@ -0,0 +1,46 @@ + + + + glib-sharp + 2.10.0.0 + + + System.Enum + + + + + Field + + GLib.SeekType + + + Current position in the file. + + + + + Field + + GLib.SeekType + + + End of the file. + + + + + Field + + GLib.SeekType + + + Start of the file. + + + + + SeekType enumeration. + Base positions for seek operations. + + diff --git a/doc/en/GLib/SpawnChildSetupFunc.xml b/doc/en/GLib/SpawnChildSetupFunc.xml new file mode 100644 index 000000000..00ee71db7 --- /dev/null +++ b/doc/en/GLib/SpawnChildSetupFunc.xml @@ -0,0 +1,18 @@ + + + + glib-sharp + 2.10.0.0 + + + System.Delegate + + + + System.Void + + + Child process setup callback delegate. + Pass this delegate one of the Spawn methods on to perform process setup. Note that using this capability can cause portability issues if you are targetting a win32 environment. In POSIX environments, the function is run in the child's environment, but on win32 this is not possible, so it runs in the parent environment. + + diff --git a/doc/en/GLib/SpawnError.xml b/doc/en/GLib/SpawnError.xml new file mode 100644 index 000000000..fe4b41a9d --- /dev/null +++ b/doc/en/GLib/SpawnError.xml @@ -0,0 +1,216 @@ + + + + glib-sharp + 2.10.0.0 + + + System.Enum + + + + + Field + + GLib.SpawnError + + + execv() returned EACCESS. + + + + + Field + + GLib.SpawnError + + + Changing to working directory failed. + + + + + Field + + GLib.SpawnError + + + Some other failure. Error message output will have additional info. + + + + + Field + + GLib.SpawnError + + + Fork failed due to lack of memory. + + + + + Field + + GLib.SpawnError + + + execv() returned EINVAL. + + + + + Field + + GLib.SpawnError + + + execv() returned EIO. + + + + + Field + + GLib.SpawnError + + + execv() returned EISDIR. + + + + + Field + + GLib.SpawnError + + + execv() returned ELIBBAD. + + + + + Field + + GLib.SpawnError + + + execv() returned ELOOP. + + + + + Field + + GLib.SpawnError + + + execv() returned EMFILE. + + + + + Field + + GLib.SpawnError + + + execv() returned ENAMETOOLONG. + + + + + Field + + GLib.SpawnError + + + execv() returned ENFILE. + + + + + Field + + GLib.SpawnError + + + execv() returned ENOENT. + + + + + Field + + GLib.SpawnError + + + execv() returned ENOEXEC. + + + + + Field + + GLib.SpawnError + + + execv() returned ENOMEM. + + + + + Field + + GLib.SpawnError + + + execv() returned ENOTDIR. + + + + + Field + + GLib.SpawnError + + + execv() returned EPERM. + + + + + Field + + GLib.SpawnError + + + Read or select on pipe failed. + + + + + Field + + GLib.SpawnError + + + execv() returned ETOOBIG. + + + + + Field + + GLib.SpawnError + + + execv() returned ETXTBUSY. + + + + + SpawnError enumeration. + Error codes returned by process spawing methods. + + diff --git a/doc/en/GLib/SpawnFlags.xml b/doc/en/GLib/SpawnFlags.xml new file mode 100644 index 000000000..dacfef455 --- /dev/null +++ b/doc/en/GLib/SpawnFlags.xml @@ -0,0 +1,91 @@ + + + + glib-sharp + 2.10.0.0 + + + System.Enum + + + + System.Flags + + + + + + Field + + GLib.SpawnFlags + + + Indicates if the child process should inherit the parent's stdin. + + + + + Field + + GLib.SpawnFlags + + + Indicates if child should not be reaped automatically. Caller must call on the returned process to avoid zombies. + + + + + Field + + GLib.SpawnFlags + + + Indicates if the first element should be dropped from the argv that is passed to the process. Normally the first element of the argv is the program name to be invoked, and the entire argv is passed to the process, including the program name. Using this flag causes the program name to be dropped from the vector. + + + + + Field + + GLib.SpawnFlags + + + Indicates if parent file descriptors should remain open for the child. Without this flag, all descriptors except for stdin, stdout, and stderr are closed. + + + + + Field + + GLib.SpawnFlags + + + Indicates if the user's PATH variable should be used to locate the executable. Without this flag, a fully-qualified path to the executable must be provided in the command or argument vector. Setting this flag can create security issues, so it should be used with caution. + + + + + Field + + GLib.SpawnFlags + + + Indicates if stderr should be redirected to /dev/null, ignoring the output. If this flag is set, users of must pass for the stderr parameter. + + + + + Field + + GLib.SpawnFlags + + + Indicates if stdout should be redirected to /dev/null, ignoring the output. If this flag is set, users of must pass for the stdout parameter. + + + + + SpawnFlags enumeration. + Provides process spawning configuration information. + + diff --git a/glib/IOChannel.cs b/glib/IOChannel.cs new file mode 100644 index 000000000..d41ce36bc --- /dev/null +++ b/glib/IOChannel.cs @@ -0,0 +1,469 @@ +// glib/IOChannel.cs : IOChannel API wrapper +// +// Author: Mike Kestner +// +// Copyright (c) 2007 Novell, Inc. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of version 2 of the Lesser GNU General +// Public License as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this program; if not, write to the +// Free Software Foundation, Inc., 59 Temple Place - Suite 330, +// Boston, MA 02111-1307, USA. + + +namespace GLibSharp { + + using System; + using System.Runtime.InteropServices; + using GLib; + + [CDeclCallback] + internal delegate bool IOFuncNative(IntPtr source, int condition, IntPtr data); + + internal class IOFuncWrapper { + + IOFunc managed; + + public IOFuncNative NativeDelegate; + + public IOFuncWrapper (IOFunc managed) + { + this.managed = managed; + NativeDelegate = new IOFuncNative (NativeCallback); + } + bool NativeCallback (IntPtr source, int condition, IntPtr data) + { + try { + return managed (IOChannel.FromHandle (source), (IOCondition) condition); + } catch (Exception e) { + ExceptionManager.RaiseUnhandledException (e, false); + return false; + } + } + } +} + +namespace GLib { + + using System; + using System.Runtime.InteropServices; + using GLibSharp; + + public class IOChannel : IDisposable, IWrapper { + + IntPtr handle; + + private IOChannel(IntPtr handle) + { + this.handle = handle; + } + + public IOChannel (int fd) : this (g_io_channel_unix_new (fd)) {} + + public IOChannel (string filename, string mode) + { + IntPtr native_filename = Marshaller.StringToPtrGStrdup (filename); + IntPtr native_mode = Marshaller.StringToPtrGStrdup (mode); + IntPtr error; + handle = g_io_channel_new_file(native_filename, native_mode, out error); + Marshaller.Free (native_filename); + Marshaller.Free (native_mode); + if (error != IntPtr.Zero) throw new GException (error); + } + + public IOCondition BufferCondition + { + get { + return (IOCondition) g_io_channel_get_buffer_condition (Handle); + } + } + + public bool Buffered + { + get { + return g_io_channel_get_buffered (Handle); + } + set { + g_io_channel_set_buffered (Handle, value); + } + } + + public ulong BufferSize + { + get { + return (ulong) g_io_channel_get_buffer_size (Handle); + } + set { + g_io_channel_set_buffer_size (Handle, new UIntPtr (value)); + } + } + + public bool CloseOnUnref + { + get { + return g_io_channel_get_close_on_unref (Handle); + } + set { + g_io_channel_set_close_on_unref (Handle, value); + } + } + + public string Encoding + { + get { + return Marshaller.Utf8PtrToString (g_io_channel_get_encoding (Handle)); + } + set { + IntPtr native_encoding = Marshaller.StringToPtrGStrdup (value); + IntPtr error; + g_io_channel_set_encoding (Handle, native_encoding, out error); + Marshaller.Free (native_encoding); + if (error != IntPtr.Zero) throw new GException (error); + } + } + + public IOFlags Flags + { + get { + return (IOFlags) g_io_channel_get_flags(Handle); + } + set { + IntPtr error; + g_io_channel_set_flags(Handle, (int) value, out error); + if (error != IntPtr.Zero) throw new GException (error); + } + } + + public char[] LineTerminator { + get { + int length; + IntPtr raw = g_io_channel_get_line_term (Handle, out length); + if (length == -1) + return Marshaller.Utf8PtrToString (raw).ToCharArray (); + byte[] buffer = new byte [length]; + return System.Text.Encoding.UTF8.GetChars (buffer); + } + set { + byte[] buffer = System.Text.Encoding.UTF8.GetBytes (value); + g_io_channel_set_line_term (Handle, buffer, buffer.Length); + } + } + + public IntPtr Handle { + get { + return handle; + } + } + + public int UnixFd { + get { + return g_io_channel_unix_get_fd (Handle); + } + } + + protected void Init () + { + g_io_channel_init (Handle); + } + + public void Dispose () + { + g_io_channel_unref (Handle); + } + + public uint AddWatch (int priority, IOCondition condition, IOFunc func) + { + IOFuncWrapper func_wrapper = null; + IntPtr user_data = IntPtr.Zero; + DestroyNotify notify = null; + if (func != null) { + func_wrapper = new IOFuncWrapper (func); + user_data = (IntPtr) GCHandle.Alloc (func_wrapper); + notify = DestroyHelper.NotifyHandler; + } + return g_io_add_watch_full (Handle, priority, (int) condition, func_wrapper.NativeDelegate, user_data, notify); + } + + public IOStatus Flush () + { + IntPtr error; + IOStatus ret = (IOStatus) g_io_channel_flush (Handle, out error); + if (error != IntPtr.Zero) throw new GException (error); + return ret; + } + + public IOStatus ReadChars (byte[] buf, out ulong bytes_read) + { + UIntPtr native_bytes_read; + IntPtr error; + IOStatus ret = (IOStatus) g_io_channel_read_chars (Handle, buf, new UIntPtr ((ulong) buf.Length), out native_bytes_read, out error); + bytes_read = (ulong) native_bytes_read; + if (error != IntPtr.Zero) throw new GException (error); + return ret; + } + + public IOStatus ReadLine (out string str_return) + { + ulong dump; + return ReadLine (out str_return, out dump); + } + + public IOStatus ReadLine (out string str_return, out ulong terminator_pos) + { + IntPtr native_string; + UIntPtr native_terminator_pos; + IntPtr error; + IOStatus ret = (IOStatus) g_io_channel_read_line (Handle, out native_string, IntPtr.Zero, out native_terminator_pos, out error); + terminator_pos = (ulong) native_terminator_pos; + str_return = null; + if (ret == IOStatus.Normal) + str_return = Marshaller.PtrToStringGFree (native_string); + if (error != IntPtr.Zero) throw new GException (error); + return ret; + } + + public IOStatus ReadToEnd (out string str_return) + { + IntPtr native_str; + UIntPtr native_length; + IntPtr error; + IOStatus ret = (IOStatus) g_io_channel_read_to_end (Handle, out native_str, out native_length, out error); + str_return = null; + if (ret == IOStatus.Normal) { + byte[] buffer = new byte [(ulong) native_length]; + Marshal.Copy (native_str, buffer, 0, (int)(ulong) native_length); + str_return = System.Text.Encoding.UTF8.GetString (buffer); + } + Marshaller.Free (native_str); + if (error != IntPtr.Zero) throw new GException (error); + return ret; + } + + public IOStatus ReadUnichar (out uint thechar) + { + IntPtr error; + IOStatus ret = (IOStatus) g_io_channel_read_unichar (Handle, out thechar, out error); + if (error != IntPtr.Zero) throw new GException (error); + return ret; + } + + public IOStatus SeekPosition (long offset, SeekType type) + { + IntPtr error; + IOStatus ret = (IOStatus) g_io_channel_seek_position (Handle, offset, (int) type, out error); + if (error != IntPtr.Zero) throw new GException (error); + return ret; + } + + public IOStatus Shutdown (bool flush) + { + IntPtr error; + IOStatus ret = (IOStatus) g_io_channel_shutdown (Handle, flush, out error); + if (error != IntPtr.Zero) throw new GException (error); + return ret; + } + + public IOStatus WriteChars (string str, out string remainder) + { + ulong written; + System.Text.Encoding enc = System.Text.Encoding.UTF8; + byte[] buffer = enc.GetBytes (str); + IOStatus ret = WriteChars (buffer, out written); + remainder = null; + if ((int) written == buffer.Length) + return ret; + int count = buffer.Length - (int) written; + byte[] rem = new byte [count]; + Array.Copy (buffer, (int) written, rem, 0, count); + remainder = enc.GetString (rem); + return ret; + } + + public IOStatus WriteChars (byte[] buf, out ulong bytes_written) + { + UIntPtr native_bytes_written; + IntPtr error; + IOStatus ret = (IOStatus) g_io_channel_write_chars (Handle, buf, new IntPtr (buf.Length), out native_bytes_written, out error); + bytes_written = (ulong) native_bytes_written; + if (error != IntPtr.Zero) throw new GException (error); + return ret; + } + + public IOStatus WriteUnichar (uint thechar) + { + IntPtr error; + IOStatus ret = (IOStatus) g_io_channel_write_unichar (Handle, thechar, out error); + if (error != IntPtr.Zero) throw new GException (error); + return ret; + } + + public static IOChannel FromHandle (IntPtr handle) + { + if (handle == IntPtr.Zero) + return null; + + g_io_channel_ref (handle); + return new IOChannel (handle); + } + + public static IOChannelError ErrorFromErrno (int en) + { + return (IOChannelError) g_io_channel_error_from_errno (en); + } + + const string libname = "libglib-2.0-0.dll"; + + [DllImport (libname)] + static extern IntPtr g_io_channel_unix_new (int fd); + + [DllImport (libname)] + static extern IntPtr g_io_channel_new_file (IntPtr filename, IntPtr mode, out IntPtr error); + + [DllImport (libname)] + static extern int g_io_channel_error_quark (); + + [DllImport(libname)] + static extern int g_io_channel_error_from_errno (int en); + + [DllImport (libname)] + static extern int g_io_channel_flush (IntPtr raw, out IntPtr error); + + [DllImport (libname)] + static extern void g_io_channel_init (IntPtr raw); + + [DllImport (libname)] + static extern int g_io_channel_read_chars (IntPtr raw, byte[] buf, UIntPtr count, out UIntPtr bytes_read, out IntPtr error); + + [DllImport (libname)] + static extern int g_io_channel_read_line (IntPtr raw, out IntPtr str_return, IntPtr length, out UIntPtr terminator_pos, out IntPtr error); + + [DllImport (libname)] + static extern int g_io_channel_read_to_end (IntPtr raw, out IntPtr str_return, out UIntPtr length, out IntPtr error); + + [DllImport (libname)] + static extern int g_io_channel_read_unichar (IntPtr raw, out uint thechar, out IntPtr error); + + [DllImport (libname)] + static extern int g_io_channel_seek_position (IntPtr raw, long offset, int type, out IntPtr error); + + [DllImport (libname)] + static extern int g_io_channel_shutdown (IntPtr raw, bool flush, out IntPtr err); + + [DllImport (libname)] + static extern int g_io_channel_write_chars (IntPtr raw, byte[] buf, IntPtr count, out UIntPtr bytes_written, out IntPtr error); + + [DllImport (libname)] + static extern int g_io_channel_write_unichar (IntPtr raw, uint thechar, out IntPtr error); + + [DllImport (libname)] + static extern int g_io_channel_get_buffer_condition (IntPtr raw); + + [DllImport (libname)] + static extern bool g_io_channel_get_buffered (IntPtr raw); + + [DllImport (libname)] + static extern void g_io_channel_set_buffered (IntPtr raw, bool buffered); + + [DllImport (libname)] + static extern UIntPtr g_io_channel_get_buffer_size (IntPtr raw); + + [DllImport (libname)] + static extern void g_io_channel_set_buffer_size (IntPtr raw, UIntPtr size); + + [DllImport (libname)] + static extern bool g_io_channel_get_close_on_unref (IntPtr raw); + + [DllImport (libname)] + static extern void g_io_channel_set_close_on_unref (IntPtr raw, bool do_close); + + [DllImport (libname)] + static extern IntPtr g_io_channel_get_encoding (IntPtr raw); + + [DllImport (libname)] + static extern int g_io_channel_set_encoding (IntPtr raw, IntPtr encoding, out IntPtr error); + + [DllImport (libname)] + static extern int g_io_channel_get_flags (IntPtr raw); + + [DllImport (libname)] + static extern int g_io_channel_set_flags (IntPtr raw, int flags, out IntPtr error); + + [DllImport (libname)] + static extern IntPtr g_io_channel_get_line_term (IntPtr raw, out int length); + + [DllImport (libname)] + static extern void g_io_channel_set_line_term (IntPtr raw, byte[] term, int length); + + [DllImport (libname)] + static extern int g_io_channel_unix_get_fd (IntPtr raw); + + [DllImport (libname)] + static extern IntPtr g_io_channel_ref (IntPtr raw); + + [DllImport (libname)] + static extern void g_io_channel_unref (IntPtr raw); + + [DllImport (libname)] + static extern uint g_io_add_watch_full (IntPtr raw, int priority, int condition, IOFuncNative func, IntPtr user_data, DestroyNotify notify); + + [DllImport (libname)] + static extern IntPtr g_io_create_watch (IntPtr raw, int condition); + } + + public delegate bool IOFunc (IOChannel source, IOCondition condition); + + public enum IOChannelError { + FileTooBig, + Inval, + IO, + IsDir, + NoSpace, + Nxio, + Overflow, + Pipe, + Failed, + } + + [Flags] + public enum IOCondition { + In = 1 << 0, + Out = 1 << 2, + Pri = 1 << 1, + Err = 1 << 3, + Hup = 1 << 4, + Nval = 1 << 5, + } + + [Flags] + public enum IOFlags { + Append = 1 << 0, + Nonblock = 1 << 1, + IsReadable = 1 << 2, + IsWriteable = 1 << 3, + IsSeekable = 1 << 4, + Mask = 1 << 5- 1, + GetMask = Mask, + SetMask = Append | Nonblock, + } + + public enum IOStatus { + Error, + Normal, + Eof, + Again, + } + + public enum SeekType { + Cur, + Set, + End, + } +} diff --git a/glib/Makefile.am b/glib/Makefile.am index 3d7434aa6..fba29e1c6 100644 --- a/glib/Makefile.am +++ b/glib/Makefile.am @@ -37,6 +37,7 @@ sources = \ Idle.cs \ IgnoreClassInitializersAttribute.cs \ InitiallyUnowned.cs \ + IOChannel.cs \ IWrapper.cs \ ListBase.cs \ List.cs \ @@ -58,6 +59,7 @@ sources = \ SignalCallback.cs \ SList.cs \ Source.cs \ + Spawn.cs \ Thread.cs \ Timeout.cs \ ToggleRef.cs \ diff --git a/glib/Marshaller.cs b/glib/Marshaller.cs index 32fdd974f..c99157249 100644 --- a/glib/Marshaller.cs +++ b/glib/Marshaller.cs @@ -37,6 +37,12 @@ namespace GLib { g_free (ptr); } + public static void Free (IntPtr[] ptrs) + { + for (int i = 0; i < ptrs.Length; i++) + g_free (ptrs [i]); + } + [DllImport("libglib-2.0-0.dll")] static extern IntPtr g_filename_to_utf8 (IntPtr mem, int len, IntPtr read, out IntPtr written, out IntPtr error); @@ -133,6 +139,17 @@ namespace GLib { return ret.Replace ("%", "%%"); } + public static IntPtr[] StringArrayToNullTermPointer (string[] strs) + { + if (strs == null) + return new IntPtr [0]; + IntPtr[] result = new IntPtr [strs.Length + 1]; + for (int i = 0; i < strs.Length; i++) + result [i] = StringToPtrGStrdup (strs [i]); + result [strs.Length] = IntPtr.Zero; + return result; + } + public static string[] PtrToStringArrayGFree (IntPtr string_array) { if (string_array == IntPtr.Zero) diff --git a/glib/Spawn.cs b/glib/Spawn.cs new file mode 100644 index 000000000..9c1166ef8 --- /dev/null +++ b/glib/Spawn.cs @@ -0,0 +1,216 @@ +// glib/Spawn.cs : Spawn g_spawn API wrapper +// +// Author: Mike Kestner +// +// Copyright (c) 2007 Novell, Inc. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of version 2 of the Lesser GNU General +// Public License as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this program; if not, write to the +// Free Software Foundation, Inc., 59 Temple Place - Suite 330, +// Boston, MA 02111-1307, USA. + + +namespace GLib { + + using System; + using System.Runtime.InteropServices; + + public enum SpawnError { + Fork, + Read, + Chdir, + Acces, + Perm, + TooBig, + NoExec, + NameTooLong, + NoEnt, + NoMem, + NotDir, + Loop, + TxtBusy, + IO, + NFile, + MFile, + Inval, + IsDir, + LibBad, + Failed, + } + + [Flags] + public enum SpawnFlags { + LeaveDescriptorsOpen = 1 << 0, + DoNotReapChild = 1 << 1, + SearchPath = 1 << 2, + StdoutToDevNull = 1 << 3, + StderrToDevNull = 1 << 4, + ChildInheritsStdin = 1 << 5, + FileAndArgvZero = 1 << 6, + } + + public delegate void SpawnChildSetupFunc (); + + [CDeclCallback] + internal delegate void SpawnChildSetupFuncNative (IntPtr gch); + + internal class SpawnChildSetupWrapper { + + SpawnChildSetupFunc handler; + + public SpawnChildSetupWrapper (SpawnChildSetupFunc handler) + { + if (handler == null) + return; + + this.handler = handler; + Data = (IntPtr) GCHandle.Alloc (this); + NativeCallback = new SpawnChildSetupFuncNative (InvokeHandler); + } + + public IntPtr Data; + public SpawnChildSetupFuncNative NativeCallback; + + static void InvokeHandler (IntPtr data) + { + if (data == IntPtr.Zero) + return; + GCHandle gch = (GCHandle) data; + (gch.Target as SpawnChildSetupWrapper).handler (); + gch.Free (); + } + } + + public class Process { + + public const int IgnorePipe = Int32.MaxValue; + public const int RequestPipe = 0; + + long pid; + + private Process (int pid) + { + this.pid = pid; + } + + [DllImport ("libglib-2.0-0.dll")] + static extern void g_spawn_close_pid (int pid); + + public void Close () + { + g_spawn_close_pid ((int) pid); + } + + [DllImport ("libglib-2.0-0.dll")] + static extern bool g_spawn_async (IntPtr dir, IntPtr[] argv, IntPtr[] envp, int flags, SpawnChildSetupFuncNative func, IntPtr data, out int pid, out IntPtr error); + + public static bool SpawnAsync (string working_directory, string[] argv, string[] envp, SpawnFlags flags, SpawnChildSetupFunc child_setup, out Process child_process) + { + int pid; + IntPtr error; + IntPtr native_dir = Marshaller.StringToPtrGStrdup (working_directory); + IntPtr[] native_argv = Marshaller.StringArrayToNullTermPointer (argv); + IntPtr[] native_envp = Marshaller.StringArrayToNullTermPointer (envp); + SpawnChildSetupWrapper wrapper = new SpawnChildSetupWrapper (child_setup); + bool result = g_spawn_async (native_dir, native_argv, native_envp, (int) flags, wrapper.NativeCallback, wrapper.Data, out pid, out error); + child_process = new Process (pid); + Marshaller.Free (native_dir); + Marshaller.Free (native_argv); + Marshaller.Free (native_envp); + if (error != IntPtr.Zero) throw new GLib.GException (error); + return result; + } + + [DllImport ("libglib-2.0-0.dll")] + static extern bool g_spawn_async_with_pipes (IntPtr dir, IntPtr[] argv, IntPtr[] envp, int flags, SpawnChildSetupFuncNative func, IntPtr data, out int pid, IntPtr stdin, IntPtr stdout, IntPtr stderr, out IntPtr error); + + public static bool SpawnAsyncWithPipes (string working_directory, string[] argv, string[] envp, SpawnFlags flags, SpawnChildSetupFunc child_setup, out Process child_process, ref int stdin, ref int stdout, ref int stderr) + { + int pid; + IntPtr error; + IntPtr native_dir = Marshaller.StringToPtrGStrdup (working_directory); + IntPtr[] native_argv = Marshaller.StringArrayToNullTermPointer (argv); + IntPtr[] native_envp = Marshaller.StringArrayToNullTermPointer (envp); + SpawnChildSetupWrapper wrapper = new SpawnChildSetupWrapper (child_setup); + IntPtr in_ptr = stdin == IgnorePipe ? IntPtr.Zero : Marshal.AllocHGlobal (4); + IntPtr out_ptr = stdout == IgnorePipe ? IntPtr.Zero : Marshal.AllocHGlobal (4); + IntPtr err_ptr = stderr == IgnorePipe ? IntPtr.Zero : Marshal.AllocHGlobal (4); + bool result = g_spawn_async_with_pipes (native_dir, native_argv, native_envp, (int) flags, wrapper.NativeCallback, wrapper.Data, out pid, in_ptr, out_ptr, err_ptr, out error); + child_process = new Process (pid); + if (in_ptr != IntPtr.Zero) { + stdin = Marshal.ReadInt32 (in_ptr); + Marshal.FreeHGlobal (in_ptr); + } + if (out_ptr != IntPtr.Zero) { + stdout = Marshal.ReadInt32 (out_ptr); + Marshal.FreeHGlobal (out_ptr); + } + if (err_ptr != IntPtr.Zero) { + stderr = Marshal.ReadInt32 (err_ptr); + Marshal.FreeHGlobal (err_ptr); + } + Marshaller.Free (native_dir); + Marshaller.Free (native_argv); + Marshaller.Free (native_envp); + if (error != IntPtr.Zero) throw new GLib.GException (error); + return result; + } + + [DllImport ("libglib-2.0-0.dll")] + static extern bool g_spawn_sync (IntPtr dir, IntPtr[] argv, IntPtr[] envp, int flags, SpawnChildSetupFuncNative func, IntPtr data, out IntPtr stdout, out IntPtr stderr, out int exit_status, out IntPtr error); + + public static bool SpawnSync (string working_directory, string[] argv, string[] envp, SpawnFlags flags, SpawnChildSetupFunc child_setup, out string stdout, out string stderr, out int exit_status) + { + IntPtr native_stdout, native_stderr, error; + IntPtr native_dir = Marshaller.StringToPtrGStrdup (working_directory); + IntPtr[] native_argv = Marshaller.StringArrayToNullTermPointer (argv); + IntPtr[] native_envp = Marshaller.StringArrayToNullTermPointer (envp); + SpawnChildSetupWrapper wrapper = new SpawnChildSetupWrapper (child_setup); + bool result = g_spawn_sync (native_dir, native_argv, native_envp, (int) flags, wrapper.NativeCallback, wrapper.Data, out native_stdout, out native_stderr, out exit_status, out error); + Marshaller.Free (native_dir); + Marshaller.Free (native_argv); + Marshaller.Free (native_envp); + stdout = Marshaller.PtrToStringGFree (native_stdout); + stderr = Marshaller.PtrToStringGFree (native_stderr); + if (error != IntPtr.Zero) throw new GLib.GException (error); + return result; + } + + [DllImport ("libglib-2.0-0.dll")] + static extern bool g_spawn_command_line_async (IntPtr cmdline, out IntPtr error); + + public static bool SpawnCommandLineAsync (string command_line) + { + IntPtr error; + IntPtr native_cmd = Marshaller.StringToPtrGStrdup (command_line); + bool result = g_spawn_command_line_async (native_cmd, out error); + Marshaller.Free (native_cmd); + if (error != IntPtr.Zero) throw new GLib.GException (error); + return result; + } + + [DllImport ("libglib-2.0-0.dll")] + static extern bool g_spawn_command_line_sync (IntPtr cmdline, out IntPtr stdout, out IntPtr stderr, out int exit_status, out IntPtr error); + + public static bool SpawnCommandLineSync (string command_line, out string stdout, out string stderr, out int exit_status) + { + IntPtr error, native_stdout, native_stderr; + IntPtr native_cmd = Marshaller.StringToPtrGStrdup (command_line); + bool result = g_spawn_command_line_sync (native_cmd, out native_stdout, out native_stderr, out exit_status, out error); + Marshaller.Free (native_cmd); + stdout = Marshaller.PtrToStringGFree (native_stdout); + stderr = Marshaller.PtrToStringGFree (native_stderr); + if (error != IntPtr.Zero) throw new GLib.GException (error); + return result; + } + } +} diff --git a/sample/Makefile.am b/sample/Makefile.am index 03302677b..2c07b94c3 100755 --- a/sample/Makefile.am +++ b/sample/Makefile.am @@ -16,7 +16,7 @@ DOTNET_TARGETS= DOTNET_ASSEMBLY= endif -TARGETS = polarfixed.exe custom-widget.exe custom-cellrenderer.exe gtk-hello-world.exe button.exe calendar.exe subclass.exe menu.exe size.exe scribble.exe scribble-xinput.exe treeviewdemo.exe managedtreeviewdemo.exe nodeviewdemo.exe treemodeldemo.exe testdnd.exe actions.exe $(GLADE_TARGETS) $(DOTNET_TARGETS) +TARGETS = polarfixed.exe custom-widget.exe custom-cellrenderer.exe gtk-hello-world.exe button.exe calendar.exe subclass.exe menu.exe size.exe scribble.exe scribble-xinput.exe treeviewdemo.exe managedtreeviewdemo.exe nodeviewdemo.exe treemodeldemo.exe testdnd.exe actions.exe spawn.exe $(GLADE_TARGETS) $(DOTNET_TARGETS) DEBUGS = $(addsuffix .mdb, $(TARGETS)) @@ -91,6 +91,9 @@ actions.exe: $(srcdir)/Actions.cs polarfixed.exe: $(srcdir)/PolarFixed.cs $(assemblies) $(CSC) /debug /out:polarfixed.exe $(references) $(srcdir)/PolarFixed.cs +spawn.exe: $(srcdir)/SpawnTests.cs $(assemblies) + $(CSC) /out:spawn.exe $(references) $(srcdir)/SpawnTests.cs + EXTRA_DIST = \ HelloWorld.cs \ ButtonApp.cs \ diff --git a/sample/SpawnTests.cs b/sample/SpawnTests.cs new file mode 100644 index 000000000..942d0d767 --- /dev/null +++ b/sample/SpawnTests.cs @@ -0,0 +1,114 @@ +// SpawnTests.cs - Tests for GLib.Process.Spawn* +// +// Author: Mike Kestner +// +// Copyright (c) 2007 Novell, Inc. + +namespace GtkSamples { + + using Gtk; + using Gdk; + using GLib; + using System; + + public class SpawnTests { + + static MainLoop ml; + + public static void Main (string[] args) + { + CommandLineSyncTest (); + CommandLineAsyncTest (); + SyncTest (); + AsyncTest (); + AsyncWithPipesTest (); + ml = new MainLoop (); + ml.Run (); + } + + static void CommandLineAsyncTest () + { + Console.WriteLine ("CommandLineAsyncTest:"); + try { + GLib.Process.SpawnCommandLineAsync ("echo \"[CommandLineAsync running: `pwd`]\""); + } catch (Exception e) { + Console.WriteLine ("Exception in SpawnCommandLineAsync: " + e); + } + Console.WriteLine ("returning"); + } + + static void CommandLineSyncTest () + { + Console.WriteLine ("CommandLineSyncTest:"); + try { + string stdout, stderr; + int exit_status; + GLib.Process.SpawnCommandLineSync ("pwd", out stdout, out stderr, out exit_status); + Console.Write ("pwd exit_status=" + exit_status + " output: " + stdout); + } catch (Exception e) { + Console.WriteLine ("Exception in SpawnCommandLineSync: " + e); + } + Console.WriteLine ("returning"); + } + + static void SyncTest () + { + Console.WriteLine ("SyncTest:"); + try { + string stdout, stderr; + int exit_status; + GLib.Process.SpawnSync ("/usr", new string[] {"pwd"}, null, SpawnFlags.SearchPath, null, out stdout, out stderr, out exit_status); + Console.Write ("pwd exit_status=" + exit_status + " output: " + stdout); + } catch (Exception e) { + Console.WriteLine ("Exception in SpawnSync: " + e); + } + Console.WriteLine ("returning"); + } + + static void AsyncTest () + { + Console.WriteLine ("AsyncTest:"); + try { + Process proc; + GLib.Process.SpawnAsync (null, new string[] {"echo", "[AsyncTest running]"}, null, SpawnFlags.SearchPath, null, out proc); + } catch (Exception e) { + Console.WriteLine ("Exception in SpawnSync: " + e); + } + Console.WriteLine ("returning"); + } + + static IOChannel channel; + + static void AsyncWithPipesTest () + { + Console.WriteLine ("AsyncWithPipesTest:"); + try { + Process proc; + int stdin = Process.IgnorePipe; + int stdout = Process.RequestPipe; + int stderr = Process.IgnorePipe; + GLib.Process.SpawnAsyncWithPipes (null, new string[] {"pwd"}, null, SpawnFlags.SearchPath, null, out proc, ref stdin, ref stdout, ref stderr); + channel = new IOChannel (stdout); + channel.AddWatch (0, IOCondition.In | IOCondition.Hup, new IOFunc (ReadStdout)); + } catch (Exception e) { + Console.WriteLine ("Exception in SpawnSync: " + e); + } + Console.WriteLine ("returning"); + } + + static bool ReadStdout (IOChannel source, IOCondition condition) + { + if ((condition & IOCondition.In) == IOCondition.In) { + string txt; + if (source.ReadToEnd (out txt) == IOStatus.Normal) + Console.WriteLine ("[AsyncWithPipesTest output] " + txt); + } + if ((condition & IOCondition.Hup) == IOCondition.Hup) { + source.Dispose (); + ml.Quit (); + return true; + } + return true; + } + } +}