diff options
Diffstat (limited to 'ext/pty')
| -rw-r--r-- | ext/pty/.cvsignore | 3 | ||||
| -rw-r--r-- | ext/pty/README | 65 | ||||
| -rw-r--r-- | ext/pty/README.expect | 22 | ||||
| -rw-r--r-- | ext/pty/README.expect.ja | 21 | ||||
| -rw-r--r-- | ext/pty/README.ja | 89 | ||||
| -rw-r--r-- | ext/pty/depend | 20 | ||||
| -rw-r--r-- | ext/pty/expect_sample.rb | 55 | ||||
| -rw-r--r-- | ext/pty/extconf.rb | 10 | ||||
| -rw-r--r-- | ext/pty/lib/expect.rb | 45 | ||||
| -rw-r--r-- | ext/pty/pty.c | 859 | ||||
| -rw-r--r-- | ext/pty/script.rb | 37 | ||||
| -rw-r--r-- | ext/pty/shl.rb | 92 |
12 files changed, 625 insertions, 693 deletions
diff --git a/ext/pty/.cvsignore b/ext/pty/.cvsignore deleted file mode 100644 index 4088712231..0000000000 --- a/ext/pty/.cvsignore +++ /dev/null @@ -1,3 +0,0 @@ -Makefile -mkmf.log -*.def diff --git a/ext/pty/README b/ext/pty/README deleted file mode 100644 index 42c7d4f891..0000000000 --- a/ext/pty/README +++ /dev/null @@ -1,65 +0,0 @@ -pty extension version 0.3 by A.ito - -1. Introduction - -This extension module adds ruby a functionality to execute an -arbitrary command through pseudo tty (pty). - -2. Install - -Follow the instruction below. - -(1) Execute - - ruby extconf.rb - - then Makefile is generated. - -(3) Do make; make install. - -3. What you can do - -This extension module defines a module named PTY, which contains -following module fungtions: - - getpty(command) - spawn(command) - - This function reserves a pty, executes command over the pty - and returns an array. The return value is an array with three - elements. The first element in the array is for reading and the - second for writing. The third element is the process ID of the - child process. If this function is called with an iterator block, - the array is passed to the block as block parameters, and the - function itself returns nil. - - When the child process is suspended or finished, an exception is - raised. If this function is called with an iterator block, - exception is raised only within the block. Child process - monitor is terminated on block exit. - - protect_signal - reset_signal - - These functions are obsolete in this version of pty. - -4. License - -(C) Copyright 1998 by Akinori Ito. - -This software may be redistributed freely for this purpose, in full -or in part, provided that this entire copyright notice is included -on any copies of this software and applications and derivations thereof. - -This software is provided on an "as is" basis, without warranty of any -kind, either expressed or implied, as to any matter including, but not -limited to warranty of fitness of purpose, or merchantability, or -results obtained from use of this software. - -5. Bug report - -Please feel free to send E-mail to - - aito@ei5sun.yz.yamagata-u.ac.jp - -for any bug report, opinion, contribution, etc. diff --git a/ext/pty/README.expect b/ext/pty/README.expect deleted file mode 100644 index fddbb6fdad..0000000000 --- a/ext/pty/README.expect +++ /dev/null @@ -1,22 +0,0 @@ - README for expect - by A. Ito, 28 October, 1998 - - Expect library adds IO class a method called expect(), which -does similar act to tcl's expect extension. - -The usage of the method is: - - IO#expect(pattern,timeout=9999999) - -where `pattern' is an instance of String or Regexp and `timeout' -is Fixnum, which can be omitted. - When the method is called without block, it waits until the -input which matches the pattern is obtained from the IO or the time -specified as the timeout passes. When the pattern is obtained from the -IO, the method returns an array. The first element of the array is the -entire string obtained from the IO until the pattern matches. The -following elements indicates the specific pattern which matched to the -anchor in the regular expression. If the method ends because of -timeout, it returns nil. - When the method is called with block, the array is passed as -the block parameter. diff --git a/ext/pty/README.expect.ja b/ext/pty/README.expect.ja deleted file mode 100644 index db84695ee5..0000000000 --- a/ext/pty/README.expect.ja +++ /dev/null @@ -1,21 +0,0 @@ - README for expect - by A. Ito, 28 October, 1998 - - Expectライブラリは,tcl の expect パッケージと似たような機能を -IOクラスに追加します. - - 追加されるメソッドの使い方は次の通りです. - - IO#expect(pattern,timeout=9999999) - -pattern は String か Regexp のインスタンス,timeout は Fixnum -のインスタンスです.timeout は省略できます. - このメソッドがブロックなしで呼ばれた場合,まずレシーバである -IOオブジェクトから pattern にマッチするパターンが読みこまれる -まで待ちます.パターンが得られたら,そのパターンに関する配列を -返します.配列の最初の要素は,pattern にマッチするまでに読みこ -まれた内容の文字列です.2番目以降の要素は,pattern の正規表現 -の中にアンカーがあった場合に,そのアンカーにマッチする部分です. -もしタイムアウトが起きた場合は,このメソッドはnilを返します. - このメソッドがブロック付きで呼ばれた場合には,マッチした要素の -配列がブロック引数として渡され,ブロックが評価されます. diff --git a/ext/pty/README.ja b/ext/pty/README.ja deleted file mode 100644 index 5ae4fb06a0..0000000000 --- a/ext/pty/README.ja +++ /dev/null @@ -1,89 +0,0 @@ -pty 拡張モジュール version 0.3 by A.ito - -1. はじめに - -この拡張モジュールは,仮想tty (pty) を通して適当なコマンドを -実行する機能を ruby に提供します. - -2. インストール - -次のようにしてインストールしてください. - -(1) ruby extconf.rb - - を実行すると Makefile が生成されます. - -(2) make; make install を実行してください. - -3. 何ができるか - -この拡張モジュールは,PTY というモジュールを定義します.その中 -には,次のようなモジュール関数が含まれています. - - getpty(command) - spawn(command) - - この関数は,仮想ttyを確保し,指定されたコマンドをその仮想tty - の向こうで実行し,配列を返します.戻り値は3つの要素からなる - 配列です.最初の要素は仮想ttyから読み出すためのIOオブジェクト, - 2番目は書きこむためのIOオブジェクト,3番目は子プロセスのプロ - セスIDです.この関数がイテレータとして呼ばれた場合,これらの - 要素はブロックパラメータとして渡され,関数自体はnilを返します. - - この関数によって作られたサブプロセスが動いている間,子プロセス - の状態を監視するために SIGCHLD シグナルを捕捉します.子プロセス - が終了したり停止した場合には,例外が発生します.この間,すべての - SIGCHLD が PTY モジュールのシグナルハンドラに捕捉されるので, - サブプロセスを生成する他の関数(system() とか IO.popen()など)を - 使うと,予期しない例外が発生することがあります.これを防ぐため - には,下記のprotect_signal()を参照してください. - - この関数がブロックパラメータ付きで呼ばれた場合には,そのブロック - の中でのみ SIGCHLD が捕捉されます.したがって,ブロックパラメータ - として渡されたIOオブジェクトを,ブロックの外に持ち出して使うの - は勧められません. - - - protect_signal - - この関数はイテレータです.ここで指定されたブロックの中では, - 子プロセスが終了しても例外を発生しません.この関数を使うことで, - PTYの子プロセスが動いている間でも,system()や IO.popen()などの - 関数を安全に使うことができます.例えば, - - PTY.spawn("command_foo") do |r,w| - ... - ... - PTY.protect_signal do - system "some other commands" - end - ... - end - - このような記述により,"some other commands" が終了したときに - 例外が発生するのを防げます. - - reset_signal - - PTY の子プロセスが動いていても,そのプロセスの終了時に例外が発生 - しないようにします. - -4. 利用について - -伊藤彰則が著作権を保有します. - -ソースプログラムまたはドキュメントに元の著作権表示が改変されずに -表示されている場合に限り,誰でも,このソフトウェアを無償かつ著作 -権者に無断で利用・配布・改変できます.利用目的は限定されていませ -ん. - -このプログラムの利用・配布その他このプログラムに関係する行為によ -って生じたいかなる損害に対しても,作者は一切責任を負いません. - -5. バグ報告等 - -バグレポートは歓迎します. - - aito@ei5sun.yz.yamagata-u.ac.jp - -まで電子メールでバグレポートをお送りください. diff --git a/ext/pty/depend b/ext/pty/depend index 888be6c301..4f0595c99d 100644 --- a/ext/pty/depend +++ b/ext/pty/depend @@ -1 +1,19 @@ -pty.o: pty.c $(hdrdir)/ruby.h $(topdir)/config.h $(hdrdir)/defines.h +# AUTOGENERATED DEPENDENCIES START +pty.o: $(RUBY_EXTCONF_H) +pty.o: $(arch_hdrdir)/ruby/config.h +pty.o: $(hdrdir)/ruby/backward.h +pty.o: $(hdrdir)/ruby/defines.h +pty.o: $(hdrdir)/ruby/encoding.h +pty.o: $(hdrdir)/ruby/intern.h +pty.o: $(hdrdir)/ruby/io.h +pty.o: $(hdrdir)/ruby/missing.h +pty.o: $(hdrdir)/ruby/onigmo.h +pty.o: $(hdrdir)/ruby/oniguruma.h +pty.o: $(hdrdir)/ruby/ruby.h +pty.o: $(hdrdir)/ruby/st.h +pty.o: $(hdrdir)/ruby/subst.h +pty.o: $(hdrdir)/ruby/util.h +pty.o: $(top_srcdir)/include/ruby.h +pty.o: $(top_srcdir)/internal.h +pty.o: pty.c +# AUTOGENERATED DEPENDENCIES END diff --git a/ext/pty/expect_sample.rb b/ext/pty/expect_sample.rb deleted file mode 100644 index bf8a2352fe..0000000000 --- a/ext/pty/expect_sample.rb +++ /dev/null @@ -1,55 +0,0 @@ -# -# sample program of expect.rb -# -# by A. Ito -# -# This program reports the latest version of ruby interpreter -# by connecting to ftp server at ruby-lang.org. -# -require 'pty' -require 'expect' - -fnames = [] -PTY.spawn("ftp ftp.ruby-lang.org") do |r_f,w_f,pid| - w_f.sync = true - - $expect_verbose = false - - r_f.expect(/^Name.*: /) do - w_f.print "ftp\n" - end - - if !ENV['USER'].nil? - username = ENV['USER'] - elsif !ENV['LOGNAME'].nil? - username = ENV['LOGNAME'] - else - username = 'guest' - end - - r_f.expect('word:') do - w_f.print username+"@\n" - end - r_f.expect("> ") do - w_f.print "cd pub/ruby\n" - end - r_f.expect("> ") do - w_f.print "dir\n" - end - - r_f.expect("> ") do |output| - for x in output[0].split("\n") - if x =~ /(ruby.*\.tar\.gz)/ then - fnames.push $1 - end - end - end - begin - w_f.print "quit\n" - rescue - end -end - -print "The latest ruby interpreter is " -print fnames.sort.pop -print "\n" diff --git a/ext/pty/extconf.rb b/ext/pty/extconf.rb index 5e126fe0cf..4379177755 100644 --- a/ext/pty/extconf.rb +++ b/ext/pty/extconf.rb @@ -1,12 +1,18 @@ +# frozen_string_literal: true require 'mkmf' -if /mswin32|mingw|bccwin32/ !~ RUBY_PLATFORM +$INCFLAGS << " -I$(topdir) -I$(top_srcdir)" + +if /mswin|mingw|bccwin|nacl/ !~ RUBY_PLATFORM have_header("sys/stropts.h") have_func("setresuid") have_header("libutil.h") + have_header("util.h") # OpenBSD openpty have_header("pty.h") + have_header("pwd.h") have_library("util", "openpty") - if have_func("openpty") or + if have_func("posix_openpt") or + have_func("openpty") or have_func("_getpty") or have_func("ptsname") or have_func("ioctl") diff --git a/ext/pty/lib/expect.rb b/ext/pty/lib/expect.rb index 08191b05b9..5dbfa09ae9 100644 --- a/ext/pty/lib/expect.rb +++ b/ext/pty/lib/expect.rb @@ -1,27 +1,63 @@ +# frozen_string_literal: true $expect_verbose = false +# Expect library adds the IO instance method #expect, which does similar act to +# tcl's expect extension. +# +# In order to use this method, you must require expect: +# +# require 'expect' +# +# Please see #expect for usage. class IO + # call-seq: + # IO#expect(pattern,timeout=9999999) -> Array + # IO#expect(pattern,timeout=9999999) { |result| ... } -> nil + # + # Reads from the IO until the given +pattern+ matches or the +timeout+ is over. + # + # It returns an array with the read buffer, followed by the matches. + # If a block is given, the result is yielded to the block and returns nil. + # + # When called without a block, it waits until the input that matches the + # given +pattern+ is obtained from the IO or the time specified as the + # timeout passes. An array is returned when the pattern is obtained from the + # IO. The first element of the array is the entire string obtained from the + # IO until the pattern matches, followed by elements indicating which the + # pattern which matched to the anchor in the regular expression. + # + # The optional timeout parameter defines, in seconds, the total time to wait + # for the pattern. If the timeout expires or eof is found, nil is returned + # or yielded. However, the buffer in a timeout session is kept for the next + # expect call. The default timeout is 9999999 seconds. def expect(pat,timeout=9999999) - buf = '' + buf = ''.dup case pat when String e_pat = Regexp.new(Regexp.quote(pat)) when Regexp e_pat = pat + else + raise TypeError, "unsupported pattern class: #{pat.class}" end + @unusedBuf ||= '' while true - if !IO.select([self],nil,nil,timeout) or eof? then + if not @unusedBuf.empty? + c = @unusedBuf.slice!(0) + elsif !IO.select([self],nil,nil,timeout) or eof? then result = nil + @unusedBuf = buf break + else + c = getc end - c = getc.chr buf << c if $expect_verbose STDOUT.print c STDOUT.flush end if mat=e_pat.match(buf) then - result = [buf,*mat.to_a[1..-1]] + result = [buf,*mat.captures] break end end @@ -33,4 +69,3 @@ class IO nil end end - diff --git a/ext/pty/pty.c b/ext/pty/pty.c index 93fb5c0118..341dbbf88b 100644 --- a/ext/pty/pty.c +++ b/ext/pty/pty.c @@ -1,20 +1,33 @@ -#include "config.h" +#include "ruby/config.h" +#ifdef RUBY_EXTCONF_H +#include RUBY_EXTCONF_H +#endif +#include <stdlib.h> #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/file.h> #include <fcntl.h> #include <errno.h> +#ifdef HAVE_PWD_H #include <pwd.h> +#endif #ifdef HAVE_SYS_IOCTL_H #include <sys/ioctl.h> #endif #ifdef HAVE_LIBUTIL_H #include <libutil.h> #endif +#ifdef HAVE_UTIL_H +#include <util.h> +#endif #ifdef HAVE_PTY_H #include <pty.h> #endif +#if defined(HAVE_SYS_PARAM_H) + /* for __FreeBSD_version */ +# include <sys/param.h> +#endif #ifdef HAVE_SYS_WAIT_H #include <sys/wait.h> #else @@ -22,9 +35,9 @@ #endif #include <ctype.h> -#include "ruby.h" -#include "rubyio.h" -#include "util.h" +#include "internal.h" +#include "ruby/io.h" +#include "ruby/util.h" #include <signal.h> #ifdef HAVE_SYS_STROPTS_H @@ -37,72 +50,6 @@ #define DEVICELEN 16 -#if !defined(HAVE_OPENPTY) -#if defined(__hpux) -static -char *MasterDevice = "/dev/ptym/pty%s", - *SlaveDevice = "/dev/pty/tty%s", - *deviceNo[] = { - "p0","p1","p2","p3","p4","p5","p6","p7", - "p8","p9","pa","pb","pc","pd","pe","pf", - "q0","q1","q2","q3","q4","q5","q6","q7", - "q8","q9","qa","qb","qc","qd","qe","qf", - "r0","r1","r2","r3","r4","r5","r6","r7", - "r8","r9","ra","rb","rc","rd","re","rf", - "s0","s1","s2","s3","s4","s5","s6","s7", - "s8","s9","sa","sb","sc","sd","se","sf", - "t0","t1","t2","t3","t4","t5","t6","t7", - "t8","t9","ta","tb","tc","td","te","tf", - "u0","u1","u2","u3","u4","u5","u6","u7", - "u8","u9","ua","ub","uc","ud","ue","uf", - "v0","v1","v2","v3","v4","v5","v6","v7", - "v8","v9","va","vb","vc","vd","ve","vf", - "w0","w1","w2","w3","w4","w5","w6","w7", - "w8","w9","wa","wb","wc","wd","we","wf", - 0, - }; -#elif defined(_IBMESA) /* AIX/ESA */ -static -char *MasterDevice = "/dev/ptyp%s", - *SlaveDevice = "/dev/ttyp%s", - *deviceNo[] = { -"00","01","02","03","04","05","06","07","08","09","0a","0b","0c","0d","0e","0f", -"10","11","12","13","14","15","16","17","18","19","1a","1b","1c","1d","1e","1f", -"20","21","22","23","24","25","26","27","28","29","2a","2b","2c","2d","2e","2f", -"30","31","32","33","34","35","36","37","38","39","3a","3b","3c","3d","3e","3f", -"40","41","42","43","44","45","46","47","48","49","4a","4b","4c","4d","4e","4f", -"50","51","52","53","54","55","56","57","58","59","5a","5b","5c","5d","5e","5f", -"60","61","62","63","64","65","66","67","68","69","6a","6b","6c","6d","6e","6f", -"70","71","72","73","74","75","76","77","78","79","7a","7b","7c","7d","7e","7f", -"80","81","82","83","84","85","86","87","88","89","8a","8b","8c","8d","8e","8f", -"90","91","92","93","94","95","96","97","98","99","9a","9b","9c","9d","9e","9f", -"a0","a1","a2","a3","a4","a5","a6","a7","a8","a9","aa","ab","ac","ad","ae","af", -"b0","b1","b2","b3","b4","b5","b6","b7","b8","b9","ba","bb","bc","bd","be","bf", -"c0","c1","c2","c3","c4","c5","c6","c7","c8","c9","ca","cb","cc","cd","ce","cf", -"d0","d1","d2","d3","d4","d5","d6","d7","d8","d9","da","db","dc","dd","de","df", -"e0","e1","e2","e3","e4","e5","e6","e7","e8","e9","ea","eb","ec","ed","ee","ef", -"f0","f1","f2","f3","f4","f5","f6","f7","f8","f9","fa","fb","fc","fd","fe","ff", - }; -#elif !defined(HAVE_PTSNAME) -static -char *MasterDevice = "/dev/pty%s", - *SlaveDevice = "/dev/tty%s", - *deviceNo[] = { - "p0","p1","p2","p3","p4","p5","p6","p7", - "p8","p9","pa","pb","pc","pd","pe","pf", - "q0","q1","q2","q3","q4","q5","q6","q7", - "q8","q9","qa","qb","qc","qd","qe","qf", - "r0","r1","r2","r3","r4","r5","r6","r7", - "r8","r9","ra","rb","rc","rd","re","rf", - "s0","s1","s2","s3","s4","s5","s6","s7", - "s8","s9","sa","sb","sc","sd","se","sf", - 0, - }; -#endif -#endif /* !defined(HAVE_OPENPTY) */ - -static char SlaveName[DEVICELEN]; - #ifndef HAVE_SETEUID # ifdef HAVE_SETREUID # define seteuid(e) setreuid(-1, (e)) @@ -117,9 +64,11 @@ static char SlaveName[DEVICELEN]; static VALUE eChildExited; +/* Returns the exit status of the child for which PTY#check + * raised this exception + */ static VALUE -echild_status(self) - VALUE self; +echild_status(VALUE self) { return rb_ivar_get(self, rb_intern("status")); } @@ -127,190 +76,218 @@ echild_status(self) struct pty_info { int fd; rb_pid_t child_pid; - VALUE thread; }; -static void -raise_from_wait(state, info) - struct pty_info *info; - char *state; -{ - extern VALUE rb_last_status; - char buf[1024]; - VALUE exc; +static void getDevice(int*, int*, char [DEVICELEN], int); - snprintf(buf, sizeof(buf), "pty - %s: %d", state, info->child_pid); - exc = rb_exc_new2(eChildExited, buf); - rb_iv_set(exc, "status", rb_last_status); - rb_funcall(info->thread, rb_intern("raise"), 1, exc); -} +struct child_info { + int master, slave; + char *slavename; + VALUE execarg_obj; + struct rb_execarg *eargp; +}; -static VALUE -pty_syswait(info) - struct pty_info *info; +static int +chfunc(void *data, char *errbuf, size_t errbuf_len) { - int cpid, status; - - for (;;) { - cpid = rb_waitpid(info->child_pid, &status, WUNTRACED); - if (cpid == -1) return Qnil; + struct child_info *carg = data; + int master = carg->master; + int slave = carg->slave; + +#define ERROR_EXIT(str) do { \ + strlcpy(errbuf, (str), errbuf_len); \ + return -1; \ + } while (0) + + /* + * Set free from process group and controlling terminal + */ +#ifdef HAVE_SETSID + (void) setsid(); +#else /* HAS_SETSID */ +# ifdef HAVE_SETPGRP +# ifdef SETGRP_VOID + if (setpgrp() == -1) + ERROR_EXIT("setpgrp()"); +# else /* SETGRP_VOID */ + if (setpgrp(0, getpid()) == -1) + ERROR_EXIT("setpgrp()"); + { + int i = rb_cloexec_open("/dev/tty", O_RDONLY, 0); + if (i < 0) ERROR_EXIT("/dev/tty"); + rb_update_max_fd(i); + if (ioctl(i, TIOCNOTTY, (char *)0)) + ERROR_EXIT("ioctl(TIOCNOTTY)"); + close(i); + } +# endif /* SETGRP_VOID */ +# endif /* HAVE_SETPGRP */ +#endif /* HAS_SETSID */ -#if defined(IF_STOPPED) - if (IF_STOPPED(status)) { /* suspend */ - raise_from_wait("stopped", info); - } -#elif defined(WIFSTOPPED) - if (WIFSTOPPED(status)) { /* suspend */ - raise_from_wait("stopped", info); - } + /* + * obtain new controlling terminal + */ +#if defined(TIOCSCTTY) + close(master); + (void) ioctl(slave, TIOCSCTTY, (char *)0); + /* errors ignored for sun */ #else ----->> Either IF_STOPPED or WIFSTOPPED is needed <<---- -#endif /* WIFSTOPPED | IF_STOPPED */ - else if (kill(info->child_pid, 0) == 0) { - raise_from_wait("changed", info); - } - else { - raise_from_wait("exited", info); - return Qnil; - } + close(slave); + slave = rb_cloexec_open(carg->slavename, O_RDWR, 0); + if (slave < 0) { + ERROR_EXIT("open: pty slave"); } -} - -static void getDevice _((int*, int*)); - -struct exec_info { - int argc; - VALUE *argv; -}; + rb_update_max_fd(slave); + close(master); +#endif + dup2(slave,0); + dup2(slave,1); + dup2(slave,2); + close(slave); +#if defined(HAVE_SETEUID) || defined(HAVE_SETREUID) || defined(HAVE_SETRESUID) + if (seteuid(getuid())) ERROR_EXIT("seteuid()"); +#endif -static VALUE -pty_exec(arg) - struct exec_info *arg; -{ - return rb_f_exec(arg->argc, arg->argv); + return rb_exec_async_signal_safe(carg->eargp, errbuf, sizeof(errbuf_len)); +#undef ERROR_EXIT } static void -establishShell(argc, argv, info) - int argc; - VALUE *argv; - struct pty_info *info; -{ - static int i,master,slave,currentPid; - char *p, tmp, *getenv(); - struct passwd *pwent; +establishShell(int argc, VALUE *argv, struct pty_info *info, + char SlaveName[DEVICELEN]) +{ + int master, slave, status = 0; + rb_pid_t pid; + char *p, *getenv(); VALUE v; - struct exec_info arg; - int status; + struct child_info carg; + char errbuf[32]; if (argc == 0) { - char *shellname; + const char *shellname = "/bin/sh"; if ((p = getenv("SHELL")) != NULL) { shellname = p; } else { - pwent = getpwuid(getuid()); +#if defined HAVE_PWD_H + const char *username = getenv("USER"); + struct passwd *pwent = getpwnam(username ? username : getlogin()); if (pwent && pwent->pw_shell) shellname = pwent->pw_shell; - else - shellname = "/bin/sh"; +#endif } v = rb_str_new2(shellname); argc = 1; argv = &v; } - getDevice(&master,&slave); - info->thread = rb_thread_current(); - currentPid = getpid(); - if((i = fork()) < 0) { - close(master); - close(slave); - rb_sys_fail("fork failed"); - } + carg.execarg_obj = rb_execarg_new(argc, argv, 1); + carg.eargp = rb_execarg_get(carg.execarg_obj); + rb_execarg_parent_start(carg.execarg_obj); - if(i == 0) { /* child */ - currentPid = getpid(); + getDevice(&master, &slave, SlaveName, 0); - /* - * Set free from process group and controlling terminal - */ -#ifdef HAVE_SETSID - (void) setsid(); -#else /* HAS_SETSID */ -# ifdef HAVE_SETPGRP -# ifdef SETGRP_VOID - if (setpgrp() == -1) - perror("setpgrp()"); -# else /* SETGRP_VOID */ - if (setpgrp(0, currentPid) == -1) - rb_sys_fail("setpgrp()"); - if ((i = open("/dev/tty", O_RDONLY)) < 0) - rb_sys_fail("/dev/tty"); - else { - if (ioctl(i, TIOCNOTTY, (char *)0)) - perror("ioctl(TIOCNOTTY)"); - close(i); - } -# endif /* SETGRP_VOID */ -# endif /* HAVE_SETPGRP */ -#endif /* HAS_SETSID */ + carg.master = master; + carg.slave = slave; + carg.slavename = SlaveName; + errbuf[0] = '\0'; + pid = rb_fork_async_signal_safe(&status, chfunc, &carg, Qnil, errbuf, sizeof(errbuf)); - /* - * obtain new controlling terminal - */ -#if defined(TIOCSCTTY) - close(master); - (void) ioctl(slave, TIOCSCTTY, (char *)0); - /* errors ignored for sun */ -#else - close(slave); - slave = open(SlaveName, O_RDWR); - if (slave < 0) { - perror("open: pty slave"); - _exit(1); - } + if (pid < 0) { + int e = errno; close(master); -#endif - write(slave, "", 1); - dup2(slave,0); - dup2(slave,1); - dup2(slave,2); close(slave); -#if defined(HAVE_SETEUID) || defined(HAVE_SETREUID) || defined(HAVE_SETRESUID) - seteuid(getuid()); -#endif - - arg.argc = argc; - arg.argv = argv; - rb_protect(pty_exec, (VALUE)&arg, &status); - sleep(1); - _exit(1); + rb_execarg_parent_end(carg.execarg_obj); + errno = e; + if (status) rb_jump_tag(status); + rb_sys_fail(errbuf[0] ? errbuf : "fork failed"); } - read(master, &tmp, 1); close(slave); + rb_execarg_parent_end(carg.execarg_obj); - info->child_pid = i; + info->child_pid = pid; info->fd = master; + + RB_GC_GUARD(carg.execarg_obj); } -static VALUE -pty_finalize_syswait(info) - struct pty_info *info; +#if defined(HAVE_POSIX_OPENPT) || defined(HAVE_OPENPTY) || defined(HAVE_PTSNAME) +static int +no_mesg(char *slavedevice, int nomesg) { - rb_thread_kill(info->thread); - rb_funcall(info->thread, rb_intern("value"), 0); - rb_detach_process(info->child_pid); - return Qnil; + if (nomesg) + return chmod(slavedevice, 0600); + else + return 0; } +#endif static int -get_device_once(master, slave, fail) - int *master, *slave, fail; +get_device_once(int *master, int *slave, char SlaveName[DEVICELEN], int nomesg, int fail) { -#if defined HAVE_OPENPTY +#if defined(HAVE_POSIX_OPENPT) + /* Unix98 PTY */ + int masterfd = -1, slavefd = -1; + char *slavedevice; + struct sigaction dfl, old; + + dfl.sa_handler = SIG_DFL; + dfl.sa_flags = 0; + sigemptyset(&dfl.sa_mask); + +#if defined(__sun) || defined(__OpenBSD__) || (defined(__FreeBSD__) && __FreeBSD_version < 902000) + /* workaround for Solaris 10: grantpt() doesn't work if FD_CLOEXEC is set. [ruby-dev:44688] */ + /* FreeBSD 9.2 or later supports O_CLOEXEC + * http://www.freebsd.org/cgi/query-pr.cgi?pr=162374 */ + if ((masterfd = posix_openpt(O_RDWR|O_NOCTTY)) == -1) goto error; + if (sigaction(SIGCHLD, &dfl, &old) == -1) goto error; + if (grantpt(masterfd) == -1) goto grantpt_error; + rb_fd_fix_cloexec(masterfd); +#else + { + int flags = O_RDWR|O_NOCTTY; +# if defined(O_CLOEXEC) + /* glibc posix_openpt() in GNU/Linux calls open("/dev/ptmx", flags) internally. + * So version dependency on GNU/Linux is same as O_CLOEXEC with open(). + * O_CLOEXEC is available since Linux 2.6.23. Linux 2.6.18 silently ignore it. */ + flags |= O_CLOEXEC; +# endif + if ((masterfd = posix_openpt(flags)) == -1) goto error; + } + rb_fd_fix_cloexec(masterfd); + if (sigaction(SIGCHLD, &dfl, &old) == -1) goto error; + if (grantpt(masterfd) == -1) goto grantpt_error; +#endif + if (sigaction(SIGCHLD, &old, NULL) == -1) goto error; + if (unlockpt(masterfd) == -1) goto error; + if ((slavedevice = ptsname(masterfd)) == NULL) goto error; + if (no_mesg(slavedevice, nomesg) == -1) goto error; + if ((slavefd = rb_cloexec_open(slavedevice, O_RDWR|O_NOCTTY, 0)) == -1) goto error; + rb_update_max_fd(slavefd); + +#if defined(I_PUSH) && !defined(__linux__) && !defined(_AIX) + if (ioctl(slavefd, I_PUSH, "ptem") == -1) goto error; + if (ioctl(slavefd, I_PUSH, "ldterm") == -1) goto error; + if (ioctl(slavefd, I_PUSH, "ttcompat") == -1) goto error; +#endif + + *master = masterfd; + *slave = slavefd; + strlcpy(SlaveName, slavedevice, DEVICELEN); + return 0; + + grantpt_error: + sigaction(SIGCHLD, &old, NULL); + error: + if (slavefd != -1) close(slavefd); + if (masterfd != -1) close(masterfd); + if (fail) { + rb_raise(rb_eRuntimeError, "can't get Master/Slave device"); + } + return -1; +#elif defined HAVE_OPENPTY /* * Use openpty(3) of 4.3BSD Reno and later, * or the same interface function. @@ -320,173 +297,453 @@ get_device_once(master, slave, fail) if (!fail) return -1; rb_raise(rb_eRuntimeError, "openpty() failed"); } + rb_fd_fix_cloexec(*master); + rb_fd_fix_cloexec(*slave); + if (no_mesg(SlaveName, nomesg) == -1) { + if (!fail) return -1; + rb_raise(rb_eRuntimeError, "can't chmod slave pty"); + } return 0; + #elif defined HAVE__GETPTY + /* SGI IRIX */ char *name; + mode_t mode = nomesg ? 0600 : 0622; - if (!(name = _getpty(master, O_RDWR, 0622, 0))) { + if (!(name = _getpty(master, O_RDWR, mode, 0))) { if (!fail) return -1; rb_raise(rb_eRuntimeError, "_getpty() failed"); } + rb_fd_fix_cloexec(*master); - *slave = open(name, O_RDWR); - strcpy(SlaveName, name); + *slave = rb_cloexec_open(name, O_RDWR, 0); + /* error check? */ + rb_update_max_fd(*slave); + strlcpy(SlaveName, name, DEVICELEN); return 0; -} -#else /* HAVE__GETPTY */ - int i,j; - -#ifdef HAVE_PTSNAME - char *pn; +#elif defined(HAVE_PTSNAME) + /* System V */ + int masterfd = -1, slavefd = -1; + char *slavedevice; void (*s)(); extern char *ptsname(int); extern int unlockpt(int); extern int grantpt(int); - if((i = open("/dev/ptmx", O_RDWR, 0)) != -1) { - s = signal(SIGCHLD, SIG_DFL); - if(grantpt(i) != -1) { - signal(SIGCHLD, s); - if(unlockpt(i) != -1) { - if((pn = ptsname(i)) != NULL) { - if((j = open(pn, O_RDWR, 0)) != -1) { -#if defined I_PUSH && !defined linux - if(ioctl(j, I_PUSH, "ptem") != -1) { - if(ioctl(j, I_PUSH, "ldterm") != -1) { - ioctl(j, I_PUSH, "ttcompat"); +#if defined(__sun) + /* workaround for Solaris 10: grantpt() doesn't work if FD_CLOEXEC is set. [ruby-dev:44688] */ + if((masterfd = open("/dev/ptmx", O_RDWR, 0)) == -1) goto error; + s = signal(SIGCHLD, SIG_DFL); + if(grantpt(masterfd) == -1) goto error; + rb_fd_fix_cloexec(masterfd); +#else + if((masterfd = rb_cloexec_open("/dev/ptmx", O_RDWR, 0)) == -1) goto error; + rb_update_max_fd(masterfd); + s = signal(SIGCHLD, SIG_DFL); + if(grantpt(masterfd) == -1) goto error; #endif - *master = i; - *slave = j; - strcpy(SlaveName, pn); - return 0; -#if defined I_PUSH && !defined linux - } - } + signal(SIGCHLD, s); + if(unlockpt(masterfd) == -1) goto error; + if((slavedevice = ptsname(masterfd)) == NULL) goto error; + if (no_mesg(slavedevice, nomesg) == -1) goto error; + if((slavefd = rb_cloexec_open(slavedevice, O_RDWR, 0)) == -1) goto error; + rb_update_max_fd(slavefd); +#if defined(I_PUSH) && !defined(__linux__) && !defined(_AIX) + if(ioctl(slavefd, I_PUSH, "ptem") == -1) goto error; + if(ioctl(slavefd, I_PUSH, "ldterm") == -1) goto error; + ioctl(slavefd, I_PUSH, "ttcompat"); #endif - } - } - } - } - close(i); - } - if (!fail) rb_raise(rb_eRuntimeError, "can't get Master/Slave device"); + *master = masterfd; + *slave = slavefd; + strlcpy(SlaveName, slavedevice, DEVICELEN); + return 0; + + error: + if (slavefd != -1) close(slavefd); + if (masterfd != -1) close(masterfd); + if (fail) rb_raise(rb_eRuntimeError, "can't get Master/Slave device"); return -1; #else - char **p; + /* BSD */ + int masterfd = -1, slavefd = -1; + int i; char MasterName[DEVICELEN]; - for (p = deviceNo; *p != NULL; p++) { - sprintf(MasterName,MasterDevice,*p); - if ((i = open(MasterName,O_RDWR,0)) >= 0) { - *master = i; - sprintf(SlaveName,SlaveDevice,*p); - if ((j = open(SlaveName,O_RDWR,0)) >= 0) { - *slave = j; - chown(SlaveName, getuid(), getgid()); - chmod(SlaveName, 0622); +#define HEX1(c) \ + c"0",c"1",c"2",c"3",c"4",c"5",c"6",c"7", \ + c"8",c"9",c"a",c"b",c"c",c"d",c"e",c"f" + +#if defined(__hpux) + static const char MasterDevice[] = "/dev/ptym/pty%s"; + static const char SlaveDevice[] = "/dev/pty/tty%s"; + static const char deviceNo[][3] = { + HEX1("p"), HEX1("q"), HEX1("r"), HEX1("s"), + HEX1("t"), HEX1("u"), HEX1("v"), HEX1("w"), + }; +#elif defined(_IBMESA) /* AIX/ESA */ + static const char MasterDevice[] = "/dev/ptyp%s"; + static const char SlaveDevice[] = "/dev/ttyp%s"; + static const char deviceNo[][3] = { + HEX1("0"), HEX1("1"), HEX1("2"), HEX1("3"), + HEX1("4"), HEX1("5"), HEX1("6"), HEX1("7"), + HEX1("8"), HEX1("9"), HEX1("a"), HEX1("b"), + HEX1("c"), HEX1("d"), HEX1("e"), HEX1("f"), + }; +#else /* 4.2BSD */ + static const char MasterDevice[] = "/dev/pty%s"; + static const char SlaveDevice[] = "/dev/tty%s"; + static const char deviceNo[][3] = { + HEX1("p"), HEX1("q"), HEX1("r"), HEX1("s"), + }; +#endif +#undef HEX1 + for (i = 0; i < numberof(deviceNo); i++) { + const char *const devno = deviceNo[i]; + snprintf(MasterName, sizeof MasterName, MasterDevice, devno); + if ((masterfd = rb_cloexec_open(MasterName,O_RDWR,0)) >= 0) { + rb_update_max_fd(masterfd); + *master = masterfd; + snprintf(SlaveName, DEVICELEN, SlaveDevice, devno); + if ((slavefd = rb_cloexec_open(SlaveName,O_RDWR,0)) >= 0) { + rb_update_max_fd(slavefd); + *slave = slavefd; + if (chown(SlaveName, getuid(), getgid()) != 0) goto error; + if (chmod(SlaveName, nomesg ? 0600 : 0622) != 0) goto error; return 0; } - close(i); + close(masterfd); } } + error: + if (slavefd != -1) close(slavefd); + if (masterfd != -1) close(masterfd); if (fail) rb_raise(rb_eRuntimeError, "can't get %s", SlaveName); return -1; #endif -#endif } static void -getDevice(master, slave) - int *master, *slave; +getDevice(int *master, int *slave, char SlaveName[DEVICELEN], int nomesg) { - if (get_device_once(master, slave, 0)) { + if (get_device_once(master, slave, SlaveName, nomesg, 0)) { rb_gc(); - get_device_once(master, slave, 1); + get_device_once(master, slave, SlaveName, nomesg, 1); } } -static void -freeDevice() +static VALUE +pty_close_pty(VALUE assoc) +{ + VALUE io; + int i; + + for (i = 0; i < 2; i++) { + io = rb_ary_entry(assoc, i); + if (RB_TYPE_P(io, T_FILE) && 0 <= RFILE(io)->fptr->fd) + rb_io_close(io); + } + return Qnil; +} + +/* + * call-seq: + * PTY.open => [master_io, slave_file] + * PTY.open {|master_io, slave_file| ... } => block value + * + * Allocates a pty (pseudo-terminal). + * + * In the block form, yields two arguments <tt>master_io, slave_file</tt> + * and the value of the block is returned from +open+. + * + * The IO and File are both closed after the block completes if they haven't + * been already closed. + * + * PTY.open {|master, slave| + * p master #=> #<IO:masterpty:/dev/pts/1> + * p slave #=> #<File:/dev/pts/1> + * p slave.path #=> "/dev/pts/1" + * } + * + * In the non-block form, returns a two element array, <tt>[master_io, + * slave_file]</tt>. + * + * master, slave = PTY.open + * # do something with master for IO, or the slave file + * + * The arguments in both forms are: + * + * +master_io+:: the master of the pty, as an IO. + * +slave_file+:: the slave of the pty, as a File. The path to the + * terminal device is available via +slave_file.path+ + * + * IO#raw! is usable to disable newline conversions: + * + * require 'io/console' + * PTY.open {|m, s| + * s.raw! + * ... + * } + * + */ +static VALUE +pty_open(VALUE klass) { - chmod(SlaveName, 0666); - chown(SlaveName, 0, 0); + int master_fd, slave_fd; + char slavename[DEVICELEN]; + VALUE master_io, slave_file; + rb_io_t *master_fptr, *slave_fptr; + VALUE assoc; + + getDevice(&master_fd, &slave_fd, slavename, 1); + + master_io = rb_obj_alloc(rb_cIO); + MakeOpenFile(master_io, master_fptr); + master_fptr->mode = FMODE_READWRITE | FMODE_SYNC | FMODE_DUPLEX; + master_fptr->fd = master_fd; + master_fptr->pathv = rb_obj_freeze(rb_sprintf("masterpty:%s", slavename)); + + slave_file = rb_obj_alloc(rb_cFile); + MakeOpenFile(slave_file, slave_fptr); + slave_fptr->mode = FMODE_READWRITE | FMODE_SYNC | FMODE_DUPLEX | FMODE_TTY; + slave_fptr->fd = slave_fd; + slave_fptr->pathv = rb_obj_freeze(rb_str_new_cstr(slavename)); + + assoc = rb_assoc_new(master_io, slave_file); + if (rb_block_given_p()) { + return rb_ensure(rb_yield, assoc, pty_close_pty, assoc); + } + return assoc; } -/* ruby function: getpty */ static VALUE -pty_getpty(argc, argv, self) - int argc; - VALUE *argv; - VALUE self; +pty_detach_process(struct pty_info *info) +{ +#ifdef WNOHANG + int st; + if (rb_waitpid(info->child_pid, &st, WNOHANG) <= 0) + return Qnil; +#endif + rb_detach_process(info->child_pid); + return Qnil; +} + +/* + * call-seq: + * PTY.spawn(command_line) { |r, w, pid| ... } + * PTY.spawn(command_line) => [r, w, pid] + * PTY.spawn(command, arguments, ...) { |r, w, pid| ... } + * PTY.spawn(command, arguments, ...) => [r, w, pid] + * + * Spawns the specified command on a newly allocated pty. You can also use the + * alias ::getpty. + * + * The command's controlling tty is set to the slave device of the pty + * and its standard input/output/error is redirected to the slave device. + * + * +command+ and +command_line+ are the full commands to run, given a String. + * Any additional +arguments+ will be passed to the command. + * + * === Return values + * + * In the non-block form this returns an array of size three, + * <tt>[r, w, pid]</tt>. + * + * In the block form these same values will be yielded to the block: + * + * +r+:: A readable IO that contains the command's + * standard output and standard error + * +w+:: A writable IO that is the command's standard input + * +pid+:: The process identifier for the command. + */ +static VALUE +pty_getpty(int argc, VALUE *argv, VALUE self) { VALUE res; struct pty_info info; - struct pty_info thinfo; - OpenFile *wfptr,*rfptr; + rb_io_t *wfptr,*rfptr; VALUE rport = rb_obj_alloc(rb_cFile); VALUE wport = rb_obj_alloc(rb_cFile); - + char SlaveName[DEVICELEN]; + MakeOpenFile(rport, rfptr); MakeOpenFile(wport, wfptr); - establishShell(argc, argv, &info); + establishShell(argc, argv, &info, SlaveName); - rfptr->mode = rb_io_mode_flags("r"); - rfptr->f = fdopen(info.fd, "r"); - rfptr->path = strdup(SlaveName); + rfptr->mode = rb_io_modestr_fmode("r"); + rfptr->fd = info.fd; + rfptr->pathv = rb_obj_freeze(rb_str_new_cstr(SlaveName)); - wfptr->mode = rb_io_mode_flags("w") | FMODE_SYNC; - wfptr->f = fdopen(dup(info.fd), "w"); - wfptr->path = strdup(SlaveName); + wfptr->mode = rb_io_modestr_fmode("w") | FMODE_SYNC; + wfptr->fd = rb_cloexec_dup(info.fd); + if (wfptr->fd == -1) + rb_sys_fail("dup()"); + rb_update_max_fd(wfptr->fd); + wfptr->pathv = rfptr->pathv; res = rb_ary_new2(3); rb_ary_store(res,0,(VALUE)rport); rb_ary_store(res,1,(VALUE)wport); - rb_ary_store(res,2,INT2FIX(info.child_pid)); - - thinfo.thread = rb_thread_create(pty_syswait, (void*)&info); - thinfo.child_pid = info.child_pid; - rb_thread_schedule(); + rb_ary_store(res,2,PIDT2NUM(info.child_pid)); if (rb_block_given_p()) { - rb_ensure(rb_yield, res, pty_finalize_syswait, (VALUE)&thinfo); + rb_ensure(rb_yield, res, pty_detach_process, (VALUE)&info); return Qnil; } return res; } -/* ruby function: protect_signal - obsolete */ -static VALUE -pty_protect(self) - VALUE self; +NORETURN(static void raise_from_check(rb_pid_t pid, int status)); +static void +raise_from_check(rb_pid_t pid, int status) { - rb_warn("PTY::protect_signal is no longer needed"); - rb_yield(Qnil); - return self; + const char *state; + VALUE msg; + VALUE exc; + +#if defined(WIFSTOPPED) +#elif defined(IF_STOPPED) +#define WIFSTOPPED(status) IF_STOPPED(status) +#else +---->> Either IF_STOPPED or WIFSTOPPED is needed <<---- +#endif /* WIFSTOPPED | IF_STOPPED */ + if (WIFSTOPPED(status)) { /* suspend */ + state = "stopped"; + } + else if (kill(pid, 0) == 0) { + state = "changed"; + } + else { + state = "exited"; + } + msg = rb_sprintf("pty - %s: %ld", state, (long)pid); + exc = rb_exc_new_str(eChildExited, msg); + rb_iv_set(exc, "status", rb_last_status_get()); + rb_exc_raise(exc); } -/* ruby function: reset_signal - obsolete */ +/* + * call-seq: + * PTY.check(pid, raise = false) => Process::Status or nil + * PTY.check(pid, true) => nil or raises PTY::ChildExited + * + * Checks the status of the child process specified by +pid+. + * Returns +nil+ if the process is still alive. + * + * If the process is not alive, and +raise+ was true, a PTY::ChildExited + * exception will be raised. Otherwise it will return a Process::Status + * instance. + * + * +pid+:: The process id of the process to check + * +raise+:: If +true+ and the process identified by +pid+ is no longer + * alive a PTY::ChildExited is raised. + * + */ static VALUE -pty_reset_signal(self) - VALUE self; +pty_check(int argc, VALUE *argv, VALUE self) { - rb_warn("PTY::reset_signal is no longer needed"); - return self; + VALUE pid, exc; + rb_pid_t cpid; + int status; + const int flag = +#ifdef WNOHANG + WNOHANG| +#endif +#ifdef WUNTRACED + WUNTRACED| +#endif + 0; + + rb_scan_args(argc, argv, "11", &pid, &exc); + cpid = rb_waitpid(NUM2PIDT(pid), &status, flag); + if (cpid == -1 || cpid == 0) return Qnil; + + if (!RTEST(exc)) return rb_last_status_get(); + raise_from_check(cpid, status); + + UNREACHABLE; } static VALUE cPTY; +/* + * Document-class: PTY::ChildExited + * + * Thrown when PTY::check is called for a pid that represents a process that + * has exited. + */ + +/* + * Document-class: PTY + * + * Creates and managed pseudo terminals (PTYs). See also + * http://en.wikipedia.org/wiki/Pseudo_terminal + * + * PTY allows you to allocate new terminals using ::open or ::spawn a new + * terminal with a specific command. + * + * == Example + * + * In this example we will change the buffering type in the +factor+ command, + * assuming that factor uses stdio for stdout buffering. + * + * If IO.pipe is used instead of PTY.open, this code deadlocks because factor's + * stdout is fully buffered. + * + * # start by requiring the standard library PTY + * require 'pty' + * + * master, slave = PTY.open + * read, write = IO.pipe + * pid = spawn("factor", :in=>read, :out=>slave) + * read.close # we dont need the read + * slave.close # or the slave + * + * # pipe "42" to the factor command + * write.puts "42" + * # output the response from factor + * p master.gets #=> "42: 2 3 7\n" + * + * # pipe "144" to factor and print out the response + * write.puts "144" + * p master.gets #=> "144: 2 2 2 2 3 3\n" + * write.close # close the pipe + * + * # The result of read operation when pty slave is closed is platform + * # dependent. + * ret = begin + * master.gets # FreeBSD returns nil. + * rescue Errno::EIO # GNU/Linux raises EIO. + * nil + * end + * p ret #=> nil + * + * == License + * + * C) Copyright 1998 by Akinori Ito. + * + * This software may be redistributed freely for this purpose, in full + * or in part, provided that this entire copyright notice is included + * on any copies of this software and applications and derivations thereof. + * + * This software is provided on an "as is" basis, without warranty of any + * kind, either expressed or implied, as to any matter including, but not + * limited to warranty of fitness of purpose, or merchantability, or + * results obtained from use of this software. + */ + void -Init_pty() +Init_pty(void) { cPTY = rb_define_module("PTY"); + /* :nodoc: */ rb_define_module_function(cPTY,"getpty",pty_getpty,-1); rb_define_module_function(cPTY,"spawn",pty_getpty,-1); - rb_define_module_function(cPTY,"protect_signal",pty_protect,0); - rb_define_module_function(cPTY,"reset_signal",pty_reset_signal,0); + rb_define_singleton_method(cPTY,"check",pty_check,-1); + rb_define_singleton_method(cPTY,"open",pty_open,0); eChildExited = rb_define_class_under(cPTY,"ChildExited",rb_eRuntimeError); rb_define_method(eChildExited,"status",echild_status,0); diff --git a/ext/pty/script.rb b/ext/pty/script.rb deleted file mode 100644 index dbb933171f..0000000000 --- a/ext/pty/script.rb +++ /dev/null @@ -1,37 +0,0 @@ -require 'pty' - -if ARGV.size == 0 then - ofile = "typescript" -else - ofile = ARGV[0] -end - -logfile = File.open(ofile,"a") - -system "stty -echo raw lnext ^_" - -PTY.spawn("/bin/csh") do |r_pty,w_pty,pid| - - Thread.new do - while true - w_pty.print STDIN.getc.chr - w_pty.flush - end - end - - begin - while true - c = r_pty.sysread(512) - break if c.nil? - print c - STDOUT.flush - logfile.print c - end - rescue - # print $@,':',$!,"\n" - logfile.close - end -end - -system "stty echo -raw lnext ^v" - diff --git a/ext/pty/shl.rb b/ext/pty/shl.rb deleted file mode 100644 index cdaf8d7398..0000000000 --- a/ext/pty/shl.rb +++ /dev/null @@ -1,92 +0,0 @@ -# -# old-fashioned 'shl' like program -# by A. Ito -# -# commands: -# c creates new shell -# C-z suspends shell -# p lists all shell -# 0,1,... choose shell -# q quit - -require 'pty' - -$shells = [] -$n_shells = 0 - -$r_pty = nil -$w_pty = nil - -def writer - system "stty -echo raw" - begin - while true - c = STDIN.getc - if c == 26 then # C-z - $reader.raise(nil) - return 'Suspend' - end - $w_pty.print c.chr - $w_pty.flush - end - rescue - $reader.raise(nil) - return 'Exit' - ensure - system "stty echo -raw" - end -end - -$reader = Thread.new { - while true - begin - next if $r_pty.nil? - c = $r_pty.getc - if c.nil? then - Thread.stop - end - print c.chr - STDOUT.flush - rescue - Thread.stop - end - end -} - -# $reader.raise(nil) - - -while true - print ">> " - STDOUT.flush - case gets - when /^c/i - $shells[$n_shells] = PTY.spawn("/bin/csh") - $r_pty,$w_pty = $shells[$n_shells] - $n_shells += 1 - $reader.run - if writer == 'Exit' - $n_shells -= 1 - $shells[$n_shells] = nil - end - when /^p/i - for i in 0..$n_shells - unless $shells[i].nil? - print i,"\n" - end - end - when /^([0-9]+)/ - n = $1.to_i - if $shells[n].nil? - print "\##{i} doesn't exist\n" - else - $r_pty,$w_pty = $shells[n] - $reader.run - if writer == 'Exit' then - $shells[n] = nil - end - end - when /^q/i - exit - end -end |
