php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login

Patch 0001-Start-adding-VT100-support-for-Windows-v3 for Output Control Bug #72768

Patch version 2016-08-26 16:36 UTC

Return to Bug #72768 | Download this patch
This patch renders other patches obsolete

Obsolete patches:

Patch Revisions:

Developer: mlocati@gmail.com

From a062d7a4a0d1734b3e5ad3cf53842ebcef4a073c Mon Sep 17 00:00:00 2001
From: Michele Locati <mlocati@gmail.com>
Date: Fri, 26 Aug 2016 14:10:16 +0200
Subject: [PATCH] Start adding VT100 support for Windows

The first parameter should be a stream (php://stdout or php://stderr).

If no second parameter is specified: the function checks if the stream
supports VT100 control codes:
- Windows: returns TRUE if Windows is 10.0.10586 or greater, the
  console has the ENABLE_VIRTUAL_TERMINAL_PROCESSING flag and the
  stream is not redirected.
- POSIX: returns TRUE if the stream is not redirected.
- Others: returns FALSE

If the second parameter is specified: the function tries to enable or
disable the VT100 control codes.
- Windows: returns TRUE if Windows is 10.0.10586 or greater, the stream
  is not redirected and the ENABLE_VIRTUAL_TERMINAL_PROCESSING flag has
  been set/unset
- Others: returns FALSE
---
 ext/standard/basic_functions.c |   6 ++
 ext/standard/streamsfuncs.c    |  93 +++++++++++++++++++++++++++
 ext/standard/streamsfuncs.h    |   1 +
 win32/build/config.w32         |   2 +-
 win32/win_streams.c            | 138 +++++++++++++++++++++++++++++++++++++++++
 win32/win_streams.h            |  44 +++++++++++++
 6 files changed, 283 insertions(+), 1 deletion(-)
 create mode 100644 win32/win_streams.c
 create mode 100644 win32/win_streams.h

diff --git a/ext/standard/basic_functions.c b/ext/standard/basic_functions.c
index 58f9b49..0564301 100644
--- a/ext/standard/basic_functions.c
+++ b/ext/standard/basic_functions.c
@@ -2011,6 +2011,11 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_stream_supports_lock, 0, 0, 1)
     ZEND_ARG_INFO(0, stream)
 ZEND_END_ARG_INFO()
 
+ZEND_BEGIN_ARG_INFO_EX(arginfo_stream_vt100_support, 0, 0, 1)
+	ZEND_ARG_INFO(0, stream)
+	ZEND_ARG_INFO(0, enable)
+ZEND_END_ARG_INFO()
+
 ZEND_BEGIN_ARG_INFO_EX(arginfo_stream_select, 0, 0, 4)
 	ZEND_ARG_INFO(1, read_streams) /* ARRAY_INFO(1, read_streams, 1) */
 	ZEND_ARG_INFO(1, write_streams) /* ARRAY_INFO(1, write_streams, 1) */
@@ -3134,6 +3139,7 @@ const zend_function_entry basic_functions[] = { /* {{{ */
 	PHP_FE(stream_copy_to_stream,											arginfo_stream_copy_to_stream)
 	PHP_FE(stream_get_contents,												arginfo_stream_get_contents)
 	PHP_FE(stream_supports_lock,											arginfo_stream_supports_lock)
+	PHP_FE(stream_vt100_support,											arginfo_stream_vt100_support)
 	PHP_FE(fgetcsv,															arginfo_fgetcsv)
 	PHP_FE(fputcsv,															arginfo_fputcsv)
 	PHP_FE(flock,															arginfo_flock)
diff --git a/ext/standard/streamsfuncs.c b/ext/standard/streamsfuncs.c
index 3ab362f..a8a7cf8 100644
--- a/ext/standard/streamsfuncs.c
+++ b/ext/standard/streamsfuncs.c
@@ -37,6 +37,7 @@ typedef unsigned long long php_timeout_ull;
 #else
 #include "win32/select.h"
 #include "win32/sockets.h"
+#include "win32/win_streams.h"
 typedef unsigned __int64 php_timeout_ull;
 #endif
 
@@ -1635,6 +1636,98 @@ PHP_FUNCTION(stream_supports_lock)
 	RETURN_TRUE;
 }
 
+/* {{{ proto proto stream_vt100_support(string stream[, bool enable])
+Get or set VT100 support for the specified stream. */
+PHP_FUNCTION(stream_vt100_support)
+{
+	zend_string *stream;
+	zend_bool enable;
+	#ifdef PHP_WIN32
+	DWORD stdHandle;
+	#else
+	int fd;
+	#endif
+	int argc = ZEND_NUM_ARGS();
+
+	if (zend_parse_parameters(argc, "S|b", &stream, &enable) == FAILURE) {
+		return;
+	}
+	if (!stream || !ZSTR_VAL(stream)) {
+		RETURN_FALSE;
+	}
+	#ifdef PHP_WIN32
+		if (!SystemHasVT100Support()) {
+			RETURN_FALSE;
+		}
+		stdHandle = StreamNameToStdHandle(ZSTR_VAL(stream));
+		if (!stdHandle) {
+			RETURN_FALSE;
+		}
+		if (StdHandleIsRedirectedToFile(stdHandle)) {
+			RETURN_FALSE;
+		}
+	#else
+		if (strcmp(ZSTR_VAL(stream), "php://stdin") == 0) {
+			fd = STDIN_FILENO;
+		}
+		else if (strcmp(ZSTR_VAL(stream), "php://stdout") == 0) {
+			fd = STDOUT_FILENO;
+		}
+		else if (strcmp(ZSTR_VAL(stream), "php://stderr") == 0) {
+			fd = STDERR_FILENO;
+		} else {
+			RETURN_FALSE;
+		}
+	#endif
+
+	if (argc == 1) {
+		#ifdef PHP_WIN32
+			if (StdHandleHasColorSupport(stdHandle)) {
+				RETURN_TRUE;
+			}
+			else {
+				RETURN_FALSE;
+			}
+		#elif HAVE_POSIX
+			if (isatty(fd)) {
+				RETURN_TRUE;
+			}
+			else {
+				RETURN_FALSE;
+			}
+		#else
+			RETURN_FALSE;
+		#endif
+	}
+	else {
+		#ifdef PHP_WIN32
+			if (EnableStdHandleColorSupport(stdHandle, enable ? TRUE : FALSE)) {
+				RETURN_TRUE;
+			}
+			else {
+				RETURN_FALSE;
+			}
+		#else
+			if (isatty(fd)) {
+				if (enable) {
+					RETURN_TRUE;
+				}
+				else {
+					RETURN_FALSE;
+				}
+			}
+			else {
+				if (enable) {
+					RETURN_FALSE;
+				}
+				else {
+					RETURN_TRUE;
+				}
+			}				
+		#endif
+	}
+}
+
 #ifdef HAVE_SHUTDOWN
 /* {{{ proto int stream_socket_shutdown(resource stream, int how)
 	causes all or part of a full-duplex connection on the socket associated
diff --git a/ext/standard/streamsfuncs.h b/ext/standard/streamsfuncs.h
index 45f51c2..ab169a3 100644
--- a/ext/standard/streamsfuncs.h
+++ b/ext/standard/streamsfuncs.h
@@ -61,6 +61,7 @@ PHP_FUNCTION(stream_socket_shutdown);
 PHP_FUNCTION(stream_resolve_include_path);
 PHP_FUNCTION(stream_is_local);
 PHP_FUNCTION(stream_supports_lock);
+PHP_FUNCTION(stream_vt100_support);
 
 #if HAVE_SOCKETPAIR
 PHP_FUNCTION(stream_socket_pair);
diff --git a/win32/build/config.w32 b/win32/build/config.w32
index fa9ed62..88d597f 100644
--- a/win32/build/config.w32
+++ b/win32/build/config.w32
@@ -165,7 +165,7 @@ ADD_FLAG("CFLAGS_BD_MAIN_STREAMS", "/D ZEND_ENABLE_STATIC_TSRMLS_CACHE=1");
 
 ADD_SOURCES("win32", "dllmain.c glob.c readdir.c \
 	registry.c select.c sendmail.c time.c winutil.c wsyslog.c globals.c \
-	getrusage.c ftok.c ioutil.c codepage.c");
+	getrusage.c ftok.c ioutil.c codepage.c win_streams.c");
 
 ADD_FLAG("CFLAGS_BD_WIN32", "/D ZEND_ENABLE_STATIC_TSRMLS_CACHE=1");
 
diff --git a/win32/win_streams.c b/win32/win_streams.c
new file mode 100644
index 0000000..56040b3
--- /dev/null
+++ b/win32/win_streams.c
@@ -0,0 +1,138 @@
+/*
+   +----------------------------------------------------------------------+
+   | PHP Version 7                                                        |
+   +----------------------------------------------------------------------+
+   | Copyright (c) 1997-2016 The PHP Group                                |
+   +----------------------------------------------------------------------+
+   | This source file is subject to version 3.01 of the PHP license,      |
+   | that is bundled with this package in the file LICENSE, and is        |
+   | available through the world-wide-web at the following url:           |
+   | http://www.php.net/license/3_01.txt                                  |
+   | If you did not receive a copy of the PHP license and are unable to   |
+   | obtain it through the world-wide-web, please send a note to          |
+   | license@php.net so we can mail you a copy immediately.               |
+   +----------------------------------------------------------------------+
+   | Author: Michele Locati <mlocati@gmail.com>                           |
+   +----------------------------------------------------------------------+
+ */
+
+/* $Id$ */
+
+#include "win_streams.h"
+
+BOOL SystemHasVT100Support()
+{
+	static BOOL result = 2;
+	if (result == 2) {
+		const DWORD MINV_MAJOR = 10, MINV_MINOR = 0, MINV_BUILD = 10586;
+		result = FALSE;
+		HMODULE hMod = GetModuleHandle(TEXT("ntdll.dll"));
+		if (hMod) {
+			RtlGetVersionPtr fn = (RtlGetVersionPtr)GetProcAddress(hMod, "RtlGetVersion");
+			if (fn != NULL) {
+				RTL_OSVERSIONINFOW rovi = { 0 };
+				rovi.dwOSVersionInfoSize = sizeof(rovi);
+				if (fn(&rovi) == 0) {
+					if (
+						rovi.dwMajorVersion > MINV_MAJOR
+						||
+						(
+							rovi.dwMajorVersion == MINV_MAJOR
+							&&
+							(
+								rovi.dwMinorVersion > MINV_MINOR
+								||
+								(
+									rovi.dwMinorVersion == MINV_MINOR
+									&& rovi.dwBuildNumber >= MINV_BUILD
+									)
+								)
+							)
+						) {
+						result = TRUE;
+					}
+				}
+			}
+		}
+	}
+	return result;
+}
+
+DWORD StreamNameToStdHandle(char *stream)
+{
+	DWORD result = 0;
+	if (stream != NULL && stream[0]) {
+		if (strcmp(stream, "php://stdin") == 0) {
+			result = STD_INPUT_HANDLE;
+		}
+		else if (strcmp(stream, "php://stdout") == 0) {
+			result = STD_OUTPUT_HANDLE;
+		}
+		else if (strcmp(stream, "php://stderr") == 0) {
+			result = STD_ERROR_HANDLE;
+		}
+	}
+	return result;
+}
+
+BOOL StdHandleIsRedirectedToFile(DWORD stdHandle)
+{
+	BOOL result = FALSE;
+
+	HANDLE hStd = stdHandle ? GetStdHandle(stdHandle) : INVALID_HANDLE_VALUE;
+	if (hStd != INVALID_HANDLE_VALUE) {
+		if (GetFinalPathNameByHandle(hStd, NULL, 0, 0) != 0 || GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
+			result = TRUE;
+		}
+	}
+
+	return result;
+}
+
+BOOL StdHandleHasColorSupport(DWORD stdHandle)
+{
+	BOOL result = FALSE;
+	if (SystemHasVT100Support()) {
+		HANDLE hStdOut = stdHandle ? GetStdHandle(stdHandle) : INVALID_HANDLE_VALUE;
+		if (hStdOut != INVALID_HANDLE_VALUE) {
+			DWORD mode;
+			if (GetConsoleMode(hStdOut, &mode)) {
+				if (mode & ENABLE_VIRTUAL_TERMINAL_PROCESSING) {
+					result = TRUE;
+				}
+			}
+		}
+	}
+
+	return result;
+}
+
+BOOL EnableStdHandleColorSupport(DWORD stdHandle, BOOL enable)
+{
+	BOOL result = FALSE;
+	if (SystemHasVT100Support()) {
+		HANDLE hStdOut;
+		hStdOut = stdHandle ? GetStdHandle(STD_OUTPUT_HANDLE) : INVALID_HANDLE_VALUE;
+		if (hStdOut != INVALID_HANDLE_VALUE) {
+			DWORD mode;
+			if (GetConsoleMode(hStdOut, &mode)) {
+				if (((mode & ENABLE_VIRTUAL_TERMINAL_PROCESSING) ? 1 : 0) == (enable ? 1 : 0)) {
+					result = TRUE;
+				}
+				else {
+					if (enable) {
+						mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
+					}
+					else {
+						mode &= ~ENABLE_VIRTUAL_TERMINAL_PROCESSING;
+					}
+					if (SetConsoleMode(hStdOut, mode)) {
+						result = TRUE;
+					}
+				}
+			}
+		}
+	}
+
+	return result;
+}
diff --git a/win32/win_streams.h b/win32/win_streams.h
new file mode 100644
index 0000000..a4d3ac9
--- /dev/null
+++ b/win32/win_streams.h
@@ -0,0 +1,44 @@
+/*
+  +----------------------------------------------------------------------+
+  | PHP Version 7                                                        |
+  +----------------------------------------------------------------------+
+  | Copyright (c) 1997-2016 The PHP Group                                |
+  +----------------------------------------------------------------------+
+  | This source file is subject to version 3.01 of the PHP license,      |
+  | that is bundled with this package in the file LICENSE, and is        |
+  | available through the world-wide-web at the following url:           |
+  | http://www.php.net/license/3_01.txt                                  |
+  | If you did not receive a copy of the PHP license and are unable to   |
+  | obtain it through the world-wide-web, please send a note to          |
+  | license@php.net so we can mail you a copy immediately.               |
+  +----------------------------------------------------------------------+
+  | Author: Michele Locati <mlocati@gmail.com>                           |
+  +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#ifndef PHP_WIN32_WIN_STREAMS_H
+#define PHP_WIN32_WIN_STREAMS_H
+
+#include "php.h"
+#include "php_streams.h"
+#include <windows.h>
+
+#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
+#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
+#endif
+
+typedef LONG (WINAPI * RtlGetVersionPtr) (PRTL_OSVERSIONINFOW);
+
+BOOL SystemHasVT100Support();
+
+DWORD StreamNameToStdHandle(char *stream);
+
+BOOL StdHandleIsRedirectedToFile(DWORD stdHandle);
+
+BOOL StdHandleHasColorSupport(DWORD stdHandle);
+
+BOOL EnableStdHandleColorSupport(DWORD stdHandle, BOOL enable);
+
+#endif
-- 
2.9.3.windows.1

 
PHP Copyright © 2001-2020 The PHP Group
All rights reserved.
Last updated: Mon Nov 30 01:01:22 2020 UTC