[前][次][番号順一覧][スレッド一覧]

ruby-changes:23856

From: nobu <ko1@a...>
Date: Mon, 4 Jun 2012 15:39:50 +0900 (JST)
Subject: [ruby-changes:23856] nobu:r35907 (trunk): win32: VT100 escape

nobu	2012-06-04 15:39:40 +0900 (Mon, 04 Jun 2012)

  New Revision: 35907

  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=35907

  Log:
    win32: VT100 escape
    
    * win32/win32.c (constat_apply): apply VT100 functions.
      [ruby-core:44958] [Feature #6418]
    * win32/win32.c (constat_parse): parse some VT100 escape sequence.

  Modified files:
    trunk/ChangeLog
    trunk/bootstraptest/runner.rb
    trunk/lib/test/unit.rb
    trunk/sample/test.rb
    trunk/win32/win32.c

Index: ChangeLog
===================================================================
--- ChangeLog	(revision 35906)
+++ ChangeLog	(revision 35907)
@@ -1,3 +1,10 @@
+Mon Jun  4 15:39:33 2012  Nobuyoshi Nakada  <nobu@r...>
+
+	* win32/win32.c (constat_apply): apply VT100 functions.
+	  [ruby-core:44958] [Feature #6418]
+
+	* win32/win32.c (constat_parse): parse some VT100 escape sequence.
+
 Mon Jun  4 14:06:12 2012  NAKAMURA Usaku  <usa@r...>
 
 	* process.c (rb_exec_err): should preserve errno.
Index: bootstraptest/runner.rb
===================================================================
--- bootstraptest/runner.rb	(revision 35906)
+++ bootstraptest/runner.rb	(revision 35907)
@@ -120,7 +120,6 @@
   case @color
   when nil
     @color = @tty && /dumb/ !~ ENV["TERM"]
-    @color &= /mswin|mingw/ !~ RUBY_PLATFORM
   when true
     @tty = true
   end
Index: sample/test.rb
===================================================================
--- sample/test.rb	(revision 35906)
+++ sample/test.rb	(revision 35907)
@@ -15,7 +15,6 @@
   @rotator = %w[- \\ | /]
   @bs = "\b" * @rotator[0].size
   @tty = STDERR.tty? && /dumb/ !~ ENV["TERM"]
-  @tty &&= /mswin|mingw/ !~ RUBY_PLATFORM
   case @color
   when nil
     @color = @tty
Index: lib/test/unit.rb
===================================================================
--- lib/test/unit.rb	(revision 35906)
+++ lib/test/unit.rb	(revision 35907)
@@ -672,7 +672,6 @@
           color = true
         when :auto, nil
           color = @options[:job_status] == :replace && /dumb/ !~ ENV["TERM"]
-          color &&= /mswin|mingw/ !~ RUBY_PLATFORM
         else
           color = false
         end
Index: win32/win32.c
===================================================================
--- win32/win32.c	(revision 35906)
+++ win32/win32.c	(revision 35907)
@@ -629,11 +629,41 @@
 static CRITICAL_SECTION select_mutex;
 static int NtSocketsInitialized = 0;
 static st_table *socklist = NULL;
+static st_table *conlist = NULL;
 static char *envarea;
 static char *uenvarea;
 
 /* License: Ruby's */
+struct constat {
+    struct {
+	int state, seq[16];
+	WORD attr;
+	COORD saved;
+    } vt100;
+};
+enum {constat_init = -2, constat_esc = -1, constat_seq = 0};
+
+/* License: Ruby's */
+static int
+free_conlist(st_data_t key, st_data_t val, st_data_t arg)
+{
+    xfree((struct constat *)val);
+    return ST_DELETE;
+}
+
+/* License: Ruby's */
 static void
+constat_delete(HANDLE h)
+{
+    if (conlist) {
+	st_data_t key = (st_data_t)h, val;
+	st_delete(conlist, &key, &val);
+	xfree((struct constat *)val);
+    }
+}
+
+/* License: Ruby's */
+static void
 exit_handler(void)
 {
     if (NtSocketsInitialized) {
@@ -645,6 +675,11 @@
 	DeleteCriticalSection(&select_mutex);
 	NtSocketsInitialized = 0;
     }
+    if (conlist) {
+	st_foreach(conlist, free_conlist, 0);
+	st_free_table(conlist);
+	conlist = NULL;
+    }
     if (envarea) {
 	FreeEnvironmentStrings(envarea);
 	envarea = NULL;
@@ -5541,6 +5576,322 @@
 }
 
 /* License: Ruby's */
+static struct constat *
+constat_handle(HANDLE h)
+{
+    st_data_t data;
+    struct constat *p;
+    if (!conlist) {
+	conlist = st_init_numtable();
+    }
+    if (st_lookup(conlist, (st_data_t)h, &data)) {
+	p = (struct constat *)data;
+    }
+    else {
+	CONSOLE_SCREEN_BUFFER_INFO csbi;
+	p = ALLOC(struct constat);
+	p->vt100.state = constat_init;
+	p->vt100.attr = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED;
+	p->vt100.saved.X = p->vt100.saved.Y = 0;
+	if (GetConsoleScreenBufferInfo(h, &csbi)) {
+	    p->vt100.attr = csbi.wAttributes;
+	}
+	st_insert(conlist, (st_data_t)h, (st_data_t)p);
+    }
+    return p;
+}
+
+/* License: Ruby's */
+static void
+constat_reset(HANDLE h)
+{
+    st_data_t data;
+    struct constat *p;
+    if (!conlist) return;
+    if (!st_lookup(conlist, (st_data_t)h, &data)) return;
+    p = (struct constat *)data;
+    p->vt100.state = constat_init;
+}
+
+/* License: Ruby's */
+static DWORD
+constat_attr(int count, const int *seq, DWORD attr, DWORD default_attr)
+{
+#define FOREGROUND_MASK (FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED)
+#define BACKGROUND_MASK (BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED)
+    DWORD bold = attr & (FOREGROUND_INTENSITY | BACKGROUND_INTENSITY);
+    int rev = 0;
+
+    if (!count) return attr;
+    while (count-- > 0) {
+	switch (*seq++) {
+	  case 0:
+	    attr = default_attr;
+	    rev = 0;
+	    bold = 0;
+	    break;
+	  case 1:
+	    bold |= rev ? BACKGROUND_INTENSITY : FOREGROUND_INTENSITY;
+	    break;
+	  case 4:
+#ifndef COMMON_LVB_UNDERSCORE
+#define COMMON_LVB_UNDERSCORE 0x8000
+#endif
+	    attr |= COMMON_LVB_UNDERSCORE;
+	    break;
+	  case 7:
+	    rev = 1;
+	    break;
+
+	  case 30:
+	    attr &= ~(FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED);
+	    break;
+	  case 17:
+	  case 31:
+	    attr = attr & ~(FOREGROUND_BLUE | FOREGROUND_GREEN) | FOREGROUND_RED;
+	    break;
+	  case 18:
+	  case 32:
+	    attr = attr & ~(FOREGROUND_BLUE | FOREGROUND_RED) | FOREGROUND_GREEN;
+	    break;
+	  case 19:
+	  case 33:
+	    attr = attr & ~FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED;
+	    break;
+	  case 20:
+	  case 34:
+	    attr = attr & ~(FOREGROUND_GREEN | FOREGROUND_RED) | FOREGROUND_BLUE;
+	    break;
+	  case 21:
+	  case 35:
+	    attr = attr & ~FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_RED;
+	    break;
+	  case 22:
+	  case 36:
+	    attr = attr & ~FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN;
+	    break;
+	  case 23:
+	  case 37:
+	    attr |= FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED;
+	    break;
+
+	  case 40:
+	    attr &= ~(BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED);
+	  case 41:
+	    attr = attr & ~(BACKGROUND_BLUE | BACKGROUND_GREEN) | BACKGROUND_RED;
+	    break;
+	  case 42:
+	    attr = attr & ~(BACKGROUND_BLUE | BACKGROUND_RED) | BACKGROUND_GREEN;
+	    break;
+	  case 43:
+	    attr = attr & ~BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED;
+	    break;
+	  case 44:
+	    attr = attr & ~(BACKGROUND_GREEN | BACKGROUND_RED) | BACKGROUND_BLUE;
+	    break;
+	  case 45:
+	    attr = attr & ~BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_RED;
+	    break;
+	  case 46:
+	    attr = attr & ~BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_GREEN;
+	    break;
+	  case 47:
+	    attr |= BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED;
+	    break;
+	}
+    }
+    if (rev) {
+	attr = attr & ~(FOREGROUND_MASK | BACKGROUND_MASK) |
+	    ((attr & FOREGROUND_MASK) << 4) |
+	    ((attr & BACKGROUND_MASK) >> 4);
+    }
+    return attr | bold;
+}
+
+/* License: Ruby's */
+static void
+constat_apply(HANDLE handle, struct constat *s, WCHAR w)
+{
+    CONSOLE_SCREEN_BUFFER_INFO csbi;
+    const int *seq = s->vt100.seq;
+    int count = s->vt100.state;
+    int arg1 = 1;
+    COORD pos;
+    DWORD written;
+
+    if (!GetConsoleScreenBufferInfo(handle, &csbi)) return;
+    if (count > 0 && seq[0] > 0) arg1 = seq[0];
+    switch (w) {
+      case L'm':
+	SetConsoleTextAttribute(handle, constat_attr(count, seq, csbi.wAttributes, s->vt100.attr));
+	break;
+      case L'F':
+	csbi.dwCursorPosition.X = 0;
+      case L'A':
+	csbi.dwCursorPosition.Y -= arg1;
+	if (csbi.dwCursorPosition.Y < 0)
+	    csbi.dwCursorPosition.Y = 0;
+	SetConsoleCursorPosition(handle, csbi.dwCursorPosition);
+	break;
+      case L'E':
+	csbi.dwCursorPosition.X = 0;
+      case L'B':
+      case L'e':
+	csbi.dwCursorPosition.Y += arg1;
+	if (csbi.dwCursorPosition.Y >= csbi.dwSize.Y)
+	    csbi.dwCursorPosition.Y = csbi.dwSize.Y;
+	SetConsoleCursorPosition(handle, csbi.dwCursorPosition);
+	break;
+      case L'C':
+	csbi.dwCursorPosition.X += arg1;
+	if (csbi.dwCursorPosition.X >= csbi.dwSize.X)
+	    csbi.dwCursorPosition.X = csbi.dwSize.X;
+	SetConsoleCursorPosition(handle, csbi.dwCursorPosition);
+	break;
+      case L'D':
+	csbi.dwCursorPosition.X -= arg1;
+	if (csbi.dwCursorPosition.X < 0)
+	    csbi.dwCursorPosition.X = 0;
+	SetConsoleCursorPosition(handle, csbi.dwCursorPosition);
+	break;
+      case L'G':
+      case L'`':
+	csbi.dwCursorPosition.X = (arg1 > csbi.dwSize.X ? csbi.dwSize.X : arg1) - 1;
+	SetConsoleCursorPosition(handle, csbi.dwCursorPosition);
+	break;
+      case L'd':
+	csbi.dwCursorPosition.Y = (arg1 > csbi.dwSize.Y ? csbi.dwSize.Y : arg1) - 1;
+	SetConsoleCursorPosition(handle, csbi.dwCursorPosition);
+	break;
+      case L'H':
+      case L'f':
+	pos.Y = (arg1 > csbi.dwSize.Y ? csbi.dwSize.Y : arg1) - 1;
+	if (count < 2 || (arg1 = seq[1]) <= 0) arg1 = 1;
+	pos.X = (arg1 > csbi.dwSize.X ? csbi.dwSize.X : arg1) - 1;
+	SetConsoleCursorPosition(handle, pos);
+	break;
+      case L'J':
+	switch (arg1) {
+	  case 0:	/* erase after cursor */
+	    FillConsoleOutputCharacterW(handle, L' ',
+					csbi.dwSize.X * (csbi.dwSize.Y - csbi.dwCursorPosition.Y) - csbi.dwCursorPosition.X,
+					csbi.dwCursorPosition, &written);
+	    break;
+	  case 1:	/* erase before cursor */
+	    pos.X = 0;
+	    pos.Y = csbi.dwCursorPosition.Y;
+	    FillConsoleOutputCharacterW(handle, L' ',
+					csbi.dwSize.X * csbi.dwCursorPosition.Y + csbi.dwCursorPosition.X,
+					pos, &written);
+	    break;
+	  case 2:	/* erase entire line */
+	    pos.X = 0;
+	    pos.Y = 0;
+	    FillConsoleOutputCharacterW(handle, L' ', csbi.dwSize.X * csbi.dwSize.Y, pos, &written);
+	    break;
+	}
+	break;
+      case L'K':
+	switch (arg1) {
+	  case 0:	/* erase after cursor */
+	    FillConsoleOutputCharacterW(handle, L' ', csbi.dwSize.X - csbi.dwCursorPosition.X, csbi.dwCursorPosition, &written);
+	    break;
+	  case 1:	/* erase before cursor */
+	    pos.X = 0;
+	    pos.Y = csbi.dwCursorPosition.Y;
+	    FillConsoleOutputCharacterW(handle, L' ', csbi.dwCursorPosition.X, pos, &written);
+	    break;
+	  case 2:	/* erase entire line */
+	    pos.X = 0;
+	    pos.Y = csbi.dwCursorPosition.Y;
+	    FillConsoleOutputCharacterW(handle, L' ', csbi.dwSize.X, pos, &written);
+	    break;
+	}
+	break;
+      case L's':
+	s->vt100.saved = csbi.dwCursorPosition;
+	break;
+      case L'u':
+	SetConsoleCursorPosition(handle, s->vt100.saved);
+	break;
+      case L'h':
+	if (count >= 2 && seq[0] == -1 && seq[1] == 25) {
+	    CONSOLE_CURSOR_INFO cci;
+	    GetConsoleCursorInfo(handle, &cci);
+	    cci.bVisible = TRUE;
+	    SetConsoleCursorInfo(handle, &cci);
+	}
+	break;
+      case L'l':
+	if (count >= 2 && seq[0] == -1 && seq[1] == 25) {
+	    CONSOLE_CURSOR_INFO cci;
+	    GetConsoleCursorInfo(handle, &cci);
+	    cci.bVisible = FALSE;
+	    SetConsoleCursorInfo(handle, &cci);
+	}
+	break;
+    }
+}
+
+/* License: Ruby's */
+static long
+constat_parse(HANDLE h, struct constat *s, const WCHAR **ptrp, long *lenp)
+{
+    const WCHAR *ptr = *ptrp;
+    long rest, len = *lenp;
+    while (len-- > 0) {
+	WCHAR wc = *ptr++;
+	if (wc == 0x1b) {
+	    rest = *lenp - len - 1;
+	    s->vt100.state = constat_esc;
+	}
+	else if (s->vt100.state == constat_esc && wc == L'[') {
+	    rest = *lenp - len - 1;
+	    if (rest > 0) --rest;
+	    s->vt100.state = constat_seq;
+	    s->vt100.seq[0] = 0;
+	}
+	else if (s->vt100.state >= constat_seq) {
+	    if (wc >= L'0' && wc <= L'9') {
+		if (s->vt100.state < (int)numberof(s->vt100.seq)) {
+		    int *seq = &s->vt100.seq[s->vt100.state];
+		    *seq = (*seq * 10) + (wc - L'0');
+		}
+	    }
+	    else if (s->vt100.state == constat_seq && s->vt100.seq[0] == 0 && wc == L'?') {
+		s->vt100.seq[s->vt100.state++] = -1;
+	    }
+	    else {
+		do {
+		    if (++s->vt100.state < (int)numberof(s->vt100.seq)) {
+			s->vt100.seq[s->vt100.state] = 0;
+		    }
+		    else {
+			s->vt100.state = (int)numberof(s->vt100.seq);
+		    }
+		} while (0);
+		if (wc != L';') {
+		    constat_apply(h, s, wc);
+		    s->vt100.state = constat_init;
+		}
+	    }
+	    rest = 0;
+	}
+	else {
+	    continue;
+	}
+	*ptrp = ptr;
+	*lenp = len;
+	return rest;
+    }
+    len = *lenp;
+    *ptrp = ptr;
+    *lenp = 0;
+    return len;
+}
+
+
+/* License: Ruby's */
 int
 rb_w32_close(int fd)
 {
@@ -5553,6 +5904,7 @@
     }
     _set_osfhnd(fd, (SOCKET)INVALID_HANDLE_VALUE);
     socklist_delete(&sock, NULL);
+    constat_delete((HANDLE)sock);
     _close(fd);
     errno = save_errno;
     if (closesocket(sock) == SOCKET_ERROR) {
@@ -5608,6 +5960,7 @@
   retry:
     /* get rid of console reading bug */
     if (isconsole) {
+	constat_reset((HANDLE)_osfhnd(fd));
 	if (start)
 	    len = 1;
 	else {
@@ -5860,6 +6213,10 @@
     HANDLE handle;
     DWORD dwMode, reslen;
     VALUE str = strarg;
+    rb_encoding *utf16 = rb_enc_find("UTF-16LE");
+    const WCHAR *ptr, *next;
+    struct constat *s;
+    long len;
 
     if (disable) return -1L;
     handle = (HANDLE)_osfhnd(fd);
@@ -5867,14 +6224,23 @@
 	!rb_econv_has_convpath_p(rb_enc_name(rb_enc_get(str)), "UTF-16LE"))
 	return -1L;
 
-    str = rb_str_encode(str, rb_enc_from_encoding(rb_enc_find("UTF-16LE")),
+    str = rb_str_encode(str, rb_enc_from_encoding(utf16),
 			ECONV_INVALID_REPLACE|ECONV_UNDEF_REPLACE, Qnil);
-    if (!WriteConsoleW(handle, (LPWSTR)RSTRING_PTR(str), RSTRING_LEN(str)/2,
-		       &reslen, NULL)) {
-	if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
-	    disable = TRUE;
-	return -1L;
+    ptr = (const WCHAR *)RSTRING_PTR(str);
+    len = RSTRING_LEN(str) / sizeof(WCHAR);
+    s = constat_handle(handle);
+    while (len > 0) {
+	long curlen = constat_parse(handle, s, (next = ptr, &next), &len);
+	if (curlen > 0) {
+	    if (!WriteConsoleW(handle, ptr, curlen, &reslen, NULL)) {
+		if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
+		    disable = TRUE;
+		return -1L;
+	    }
+	}
+	ptr = next;
     }
+    RB_GC_GUARD(str);
     return (long)reslen;
 }
 

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]