348 lines
7.8 KiB
PHP
348 lines
7.8 KiB
PHP
<?php
|
|
|
|
/**
|
|
* Hoa
|
|
*
|
|
*
|
|
* @license
|
|
*
|
|
* New BSD License
|
|
*
|
|
* Copyright © 2007-2017, Hoa community. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
* * Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* * Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* * Neither the name of the Hoa nor the names of its contributors may be
|
|
* used to endorse or promote products derived from this software without
|
|
* specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
namespace Psy\Readline\Hoa;
|
|
|
|
/**
|
|
* Class \Hoa\Console.
|
|
*
|
|
* A set of utils and helpers about the console.
|
|
*/
|
|
class Console
|
|
{
|
|
/**
|
|
* Pipe mode: FIFO.
|
|
*/
|
|
const IS_FIFO = 0;
|
|
|
|
/**
|
|
* Pipe mode: character.
|
|
*/
|
|
const IS_CHARACTER = 1;
|
|
|
|
/**
|
|
* Pipe mode: directory.
|
|
*/
|
|
const IS_DIRECTORY = 2;
|
|
|
|
/**
|
|
* Pipe mode: block.
|
|
*/
|
|
const IS_BLOCK = 3;
|
|
|
|
/**
|
|
* Pipe mode: regular.
|
|
*/
|
|
const IS_REGULAR = 4;
|
|
|
|
/**
|
|
* Pipe mode: link.
|
|
*/
|
|
const IS_LINK = 5;
|
|
|
|
/**
|
|
* Pipe mode: socket.
|
|
*/
|
|
const IS_SOCKET = 6;
|
|
|
|
/**
|
|
* Pipe mode: whiteout.
|
|
*/
|
|
const IS_WHITEOUT = 7;
|
|
|
|
/**
|
|
* Advanced interaction is on.
|
|
*/
|
|
private static $_advanced = null;
|
|
|
|
/**
|
|
* Previous STTY configuration.
|
|
*/
|
|
private static $_old = null;
|
|
|
|
/**
|
|
* Mode.
|
|
*/
|
|
protected static $_mode = [];
|
|
|
|
/**
|
|
* Input.
|
|
*/
|
|
protected static $_input = null;
|
|
|
|
/**
|
|
* Output.
|
|
*/
|
|
protected static $_output = null;
|
|
|
|
/**
|
|
* Tput.
|
|
*/
|
|
protected static $_tput = null;
|
|
|
|
/**
|
|
* Prepare the environment for advanced interactions.
|
|
*/
|
|
public static function advancedInteraction(bool $force = false): bool
|
|
{
|
|
if (null !== self::$_advanced) {
|
|
return self::$_advanced;
|
|
}
|
|
|
|
if (\defined('PHP_WINDOWS_VERSION_PLATFORM')) {
|
|
return self::$_advanced = false;
|
|
}
|
|
|
|
if (false === $force &&
|
|
true === \defined('STDIN') &&
|
|
false === self::isDirect(\STDIN)) {
|
|
return self::$_advanced = false;
|
|
}
|
|
|
|
self::$_old = ConsoleProcessus::execute('stty -g < /dev/tty', false);
|
|
ConsoleProcessus::execute('stty -echo -icanon min 1 time 0 < /dev/tty', false);
|
|
|
|
return self::$_advanced = true;
|
|
}
|
|
|
|
/**
|
|
* Restore previous interaction options.
|
|
*/
|
|
public static function restoreInteraction()
|
|
{
|
|
if (null === self::$_old) {
|
|
return;
|
|
}
|
|
|
|
ConsoleProcessus::execute('stty '.self::$_old.' < /dev/tty', false);
|
|
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* Get mode of a certain pipe.
|
|
* Inspired by sys/stat.h.
|
|
*/
|
|
public static function getMode($pipe = \STDIN): int
|
|
{
|
|
$_pipe = (int) $pipe;
|
|
|
|
if (isset(self::$_mode[$_pipe])) {
|
|
return self::$_mode[$_pipe];
|
|
}
|
|
|
|
$stat = \fstat($pipe);
|
|
|
|
switch ($stat['mode'] & 0170000) {
|
|
// named pipe (fifo).
|
|
case 0010000:
|
|
$mode = self::IS_FIFO;
|
|
|
|
break;
|
|
|
|
// character special.
|
|
case 0020000:
|
|
$mode = self::IS_CHARACTER;
|
|
|
|
break;
|
|
|
|
// directory.
|
|
case 0040000:
|
|
$mode = self::IS_DIRECTORY;
|
|
|
|
break;
|
|
|
|
// block special.
|
|
case 0060000:
|
|
$mode = self::IS_BLOCK;
|
|
|
|
break;
|
|
|
|
// regular.
|
|
case 0100000:
|
|
$mode = self::IS_REGULAR;
|
|
|
|
break;
|
|
|
|
// symbolic link.
|
|
case 0120000:
|
|
$mode = self::IS_LINK;
|
|
|
|
break;
|
|
|
|
// socket.
|
|
case 0140000:
|
|
$mode = self::IS_SOCKET;
|
|
|
|
break;
|
|
|
|
// whiteout.
|
|
case 0160000:
|
|
$mode = self::IS_WHITEOUT;
|
|
|
|
break;
|
|
|
|
default:
|
|
$mode = -1;
|
|
}
|
|
|
|
return self::$_mode[$_pipe] = $mode;
|
|
}
|
|
|
|
/**
|
|
* Check whether a certain pipe is a character device (keyboard, screen
|
|
* etc.).
|
|
* For example:
|
|
* $ php Mode.php
|
|
* In this case, self::isDirect(STDOUT) will return true.
|
|
*/
|
|
public static function isDirect($pipe): bool
|
|
{
|
|
return self::IS_CHARACTER === self::getMode($pipe);
|
|
}
|
|
|
|
/**
|
|
* Check whether a certain pipe is a pipe.
|
|
* For example:
|
|
* $ php Mode.php | foobar
|
|
* In this case, self::isPipe(STDOUT) will return true.
|
|
*/
|
|
public static function isPipe($pipe): bool
|
|
{
|
|
return self::IS_FIFO === self::getMode($pipe);
|
|
}
|
|
|
|
/**
|
|
* Check whether a certain pipe is a redirection.
|
|
* For example:
|
|
* $ php Mode.php < foobar
|
|
* In this case, self::isRedirection(STDIN) will return true.
|
|
*/
|
|
public static function isRedirection($pipe): bool
|
|
{
|
|
$mode = self::getMode($pipe);
|
|
|
|
return
|
|
self::IS_REGULAR === $mode ||
|
|
self::IS_DIRECTORY === $mode ||
|
|
self::IS_LINK === $mode ||
|
|
self::IS_SOCKET === $mode ||
|
|
self::IS_BLOCK === $mode;
|
|
}
|
|
|
|
/**
|
|
* Set input layer.
|
|
*/
|
|
public static function setInput(ConsoleInput $input)
|
|
{
|
|
$old = static::$_input;
|
|
static::$_input = $input;
|
|
|
|
return $old;
|
|
}
|
|
|
|
/**
|
|
* Get input layer.
|
|
*/
|
|
public static function getInput(): ConsoleInput
|
|
{
|
|
if (null === static::$_input) {
|
|
static::$_input = new ConsoleInput();
|
|
}
|
|
|
|
return static::$_input;
|
|
}
|
|
|
|
/**
|
|
* Set output layer.
|
|
*/
|
|
public static function setOutput(ConsoleOutput $output)
|
|
{
|
|
$old = static::$_output;
|
|
static::$_output = $output;
|
|
|
|
return $old;
|
|
}
|
|
|
|
/**
|
|
* Get output layer.
|
|
*/
|
|
public static function getOutput(): ConsoleOutput
|
|
{
|
|
if (null === static::$_output) {
|
|
static::$_output = new ConsoleOutput();
|
|
}
|
|
|
|
return static::$_output;
|
|
}
|
|
|
|
/**
|
|
* Set tput.
|
|
*/
|
|
public static function setTput(ConsoleTput $tput)
|
|
{
|
|
$old = static::$_tput;
|
|
static::$_tput = $tput;
|
|
|
|
return $old;
|
|
}
|
|
|
|
/**
|
|
* Get the current tput instance of the current process.
|
|
*/
|
|
public static function getTput(): ConsoleTput
|
|
{
|
|
if (null === static::$_tput) {
|
|
static::$_tput = new ConsoleTput();
|
|
}
|
|
|
|
return static::$_tput;
|
|
}
|
|
|
|
/**
|
|
* Check whether we are running behind TMUX(1).
|
|
*/
|
|
public static function isTmuxRunning(): bool
|
|
{
|
|
return isset($_SERVER['TMUX']);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Restore interaction.
|
|
*/
|
|
\register_shutdown_function([Console::class, 'restoreInteraction']);
|