summaryrefslogtreecommitdiffstats
path: root/otherlibs/labltk/browser/shell.ml
diff options
context:
space:
mode:
Diffstat (limited to 'otherlibs/labltk/browser/shell.ml')
-rw-r--r--otherlibs/labltk/browser/shell.ml116
1 files changed, 94 insertions, 22 deletions
diff --git a/otherlibs/labltk/browser/shell.ml b/otherlibs/labltk/browser/shell.ml
index 7b27b89a7..f2fbd3a7e 100644
--- a/otherlibs/labltk/browser/shell.ml
+++ b/otherlibs/labltk/browser/shell.ml
@@ -15,6 +15,7 @@
open Tk
open Jg_tk
+open Dummy
(* Here again, memoize regexps *)
@@ -37,6 +38,19 @@ class ['a] history () = object
List.nth history pos:((l + count - 1) mod l)
end
+let dump_mem ?(:pos = 0) ?:len obj =
+ if not (Obj.is_block obj) then invalid_arg "Shell.dump_mem";
+ let len =
+ match len with
+ | None -> Obj.size obj * Sys.word_size / 8 - pos
+ | Some x -> x in
+ let buf = Buffer.create 256 in
+ for i = pos to len - 1 do
+ let c = String.unsafe_get (Obj.obj obj) i in
+ Buffer.add_string buf (Printf.sprintf "%02x" (Char.code c))
+ done;
+ Buffer.contents buf
+
(* The shell class. Now encapsulated *)
let protect f x = try f x with _ -> ()
@@ -44,32 +58,52 @@ let protect f x = try f x with _ -> ()
class shell :textw :prog :args :env =
let (in2,out1) = Unix.pipe ()
and (in1,out2) = Unix.pipe ()
- and (err1,err2) = Unix.pipe () in
+ and (err1,err2) = Unix.pipe ()
+ and (sig2,sig1) = Unix.pipe () in
object (self)
- val pid = Unix.create_process_env name:prog :args :env
+ val pid =
+ let env =
+ if Sys.os_type = "Win32" then
+ let sigdef = "CAMLSIGPIPE=" ^ dump_mem (Obj.repr sig2) in
+ Array.append env [|sigdef|]
+ else env
+ in
+ Unix.create_process_env name:prog :args :env
stdin:in2 stdout:out2 stderr:err2
val out = Unix.out_channel_of_descr out1
val h = new history ()
val mutable alive = true
val mutable reading = false
+ val ibuffer = Buffer.create 1024
+ val imutex = Mutex.create ()
+ val mutable ithreads = []
method alive = alive
method kill =
if Winfo.exists textw then Text.configure textw state:`Disabled;
if alive then begin
alive <- false;
protect close_out out;
- List.iter fun:(protect Unix.close) [in1; err1; in2; out2; err2];
try
- Fileevent.remove_fileinput fd:in1;
- Fileevent.remove_fileinput fd:err1;
- Unix.kill :pid signal:Sys.sigkill;
- Unix.waitpid mode:[] pid; ()
+ if Sys.os_type = "Win32" then begin
+ ignore (Unix.write sig1 buf:"T" pos:0 len:1);
+ List.iter fun:(protect Unix.close) [sig1; sig2]
+ end else begin
+ List.iter fun:(protect Unix.close) [in1; err1; sig1; sig2];
+ Fileevent.remove_fileinput fd:in1;
+ Fileevent.remove_fileinput fd:err1;
+ Unix.kill :pid signal:Sys.sigkill;
+ Unix.waitpid mode:[] pid; ()
+ end
with _ -> ()
end
method interrupt =
if alive then try
reading <- false;
- Unix.kill :pid signal:Sys.sigint
+ if Sys.os_type = "Win32" then begin
+ ignore (Unix.write sig1 buf:"C" pos:0 len:1);
+ self#send " "
+ end else
+ Unix.kill :pid signal:Sys.sigint
with Unix.Unix_error _ -> ()
method send s =
if alive then try
@@ -77,12 +111,16 @@ object (self)
flush out
with Sys_error _ -> ()
method private read :fd :len =
- try
+ begin try
let buf = String.create :len in
let len = Unix.read fd :buf pos:0 :len in
- self#insert (String.sub buf pos:0 :len);
- Text.mark_set textw mark:"input" index:(`Mark"insert",[`Char(-1)])
- with Unix.Unix_error _ -> ()
+ if len > 0 then begin
+ self#insert (String.sub buf pos:0 :len);
+ Text.mark_set textw mark:"input" index:(`Mark"insert",[`Char(-1)])
+ end;
+ len
+ with Unix.Unix_error _ -> 0
+ end;
method history (dir : [`next|`previous]) =
if not h#empty then begin
if reading then begin
@@ -151,13 +189,38 @@ object (self)
end;
bind textw events:[`KeyPressDetail"Return"] breakable:true
action:(fun _ -> self#return; break());
- begin try
- List.iter [in1;err1] fun:
- begin fun fd ->
- Fileevent.add_fileinput :fd
- callback:(fun () -> self#read :fd len:1024)
- end
- with _ -> ()
+ List.iter fun:Unix.close [in2;out2;err2];
+ if Sys.os_type = "Win32" then begin
+ let fileinput_thread fd =
+ let buf = String.create len:1024 in
+ let len = ref 0 in
+ try while len := ThreadUnix.read fd :buf pos:0 len:1024; !len > 0 do
+ Mutex.lock imutex;
+ Buffer.add_substring ibuffer buf pos:0 len:!len;
+ Mutex.unlock imutex
+ done with Unix.Unix_error _ -> ()
+ in
+ ithreads <- List.map [in1; err1] fun:(Thread.create fileinput_thread);
+ let rec read_buffer () =
+ Mutex.lock imutex;
+ if Buffer.length ibuffer > 0 then begin
+ self#insert (Str.global_replace pat:~"\r\n" with:"\n"
+ (Buffer.contents ibuffer));
+ Buffer.reset ibuffer;
+ Text.mark_set textw mark:"input" index:(`Mark"insert",[`Char(-1)])
+ end;
+ Mutex.unlock imutex;
+ ignore (Timer.add ms:100 callback:read_buffer)
+ in
+ read_buffer ()
+ end else begin
+ try
+ List.iter [in1;err1] fun:
+ begin fun fd ->
+ Fileevent.add_fileinput :fd
+ callback:(fun () -> ignore (self#read :fd len:1024))
+ end
+ with _ -> ()
end
end
@@ -175,17 +238,26 @@ let get_all () =
shells := all;
all
-let may_exec prog =
+let may_exec_unix prog =
try Unix.access name:prog perm:[Unix.X_OK]; true
with Unix.Unix_error _ -> false
+let may_exec_win prog =
+ List.exists pred:may_exec_unix [prog; prog^".exe"; prog^".cmo"; prog^".bat"]
+
+let may_exec =
+ if Sys.os_type = "Win32" then may_exec_win else may_exec_unix
+
+let path_sep = if Sys.os_type = "Win32" then ";" else ":"
+
let f :prog :title =
let progargs =
List.filter pred:((<>) "") (Str.split sep:~" " prog) in
if progargs = [] then () else
let prog = List.hd progargs in
- let path = try Sys.getenv "PATH" with Not_found -> "/bin:/usr/bin" in
- let exec_path = Str.split sep:~":" path in
+ let path =
+ try Sys.getenv "PATH" with Not_found -> "/bin" ^ path_sep ^ "/usr/bin" in
+ let exec_path = Str.split sep:~path_sep path in
let exists =
if not (Filename.is_implicit prog) then may_exec prog else
List.exists exec_path