Welcome to mirror list, hosted at ThFree Co, Russian Federation.

WindowsAnsicon.php « Adapter « src « zend-console « zendframework « vendor - github.com/bareos/bareos-webui.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 1ffc728edd8e0847c36f98cb12793614ac210ca0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
<?php
/**
 * Zend Framework (http://framework.zend.com/)
 *
 * @link      http://github.com/zendframework/zf2 for the canonical source repository
 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
 * @license   http://framework.zend.com/license/new-bsd New BSD License
 */

namespace Zend\Console\Adapter;

use Zend\Console\Charset;
use Zend\Console\Charset\CharsetInterface;
use Zend\Console\Exception;

/**
 * MS Windows with ANSICON console adapter
 *
 * This adapter requires ANSICON extension to be installed. It can be obtained from:
 *      https://github.com/adoxa/ansicon
 *
 * ANSICON has to be loaded and enabled before using this adapter. It's best to install
 * it using following command:
 *      ansicon -I
 *
 * Console should not run in UTF8 code page (65001), because ANSICON does not behave well with it.
 * It's best to use non-unicode code page 437, 850, 851, 852 or similar. Run "help mode" for more
 * information on how to change Windows console code page.
 */
class WindowsAnsicon extends Posix
{
    /**
     * Whether or not mbstring is enabled
     *
     * @var null|bool
     */
    protected static $hasMBString;

    /**
     * Results of mode command
     *
     * @var mixed
     */
    protected $modeResult;

    /**
     * Determine and return current console width.
     *
     * @return int
     */
    public function getWidth()
    {
        static $width;
        if ($width > 0) {
            return $width;
        }

        // Try to read console size from ANSICON env var
        if (preg_match('/\((\d+)x/', getenv('ANSICON'), $matches)) {
            $width = $matches[1];
        } else {
            $width = AbstractAdapter::getWidth();
        }

        return $width;
    }

    /**
     * Determine and return current console height.
     *
     * @return false|int
     */
    public function getHeight()
    {
        static $height;
        if ($height > 0) {
            return $height;
        }

        // Try to read console size from ANSICON env var
        if (preg_match('/\(\d+x(\d+)/', getenv('ANSICON'), $matches)) {
            $height = $matches[1];
        } else {
            $height = AbstractAdapter::getHeight();
        }
        return $height;
    }

    /**
     * Run and cache results of mode command
     *
     * @return void
     */
    protected function runModeCommand()
    {
        exec('mode', $output, $return);
        if ($return || !count($output)) {
            $this->modeResult = '';
        } else {
            $this->modeResult = trim(implode('', $output));
        }
    }

    /**
     * Check if console is UTF-8 compatible
     *
     * @return bool
     */
    public function isUtf8()
    {
        // Try to read code page info from "mode" command
        if ($this->modeResult === null) {
            $this->runModeCommand();
        }

        if (preg_match('/Code page\:\s+(\d+)/', $this->modeResult, $matches)) {
            return (int) $matches[1] == 65001;
        }

        return false;
    }

    /**
     * Return current console window title.
     *
     * @return string
     */
    public function getTitle()
    {
        // Try to use powershell to retrieve console window title
        exec('powershell -command "write $Host.UI.RawUI.WindowTitle"', $output, $result);
        if ($result || !$output) {
            return '';
        }

        return trim($output, "\r\n");
    }

    /**
     * Clear console screen
     */
    public function clear()
    {
        echo chr(27) . '[1J' . chr(27) . '[u';
    }

    /**
     * Clear line at cursor position
     */
    public function clearLine()
    {
        echo chr(27) . '[1K';
    }

    /**
     * Set Console charset to use.
     *
     * @param CharsetInterface $charset
     */
    public function setCharset(CharsetInterface $charset)
    {
        $this->charset = $charset;
    }

    /**
     * Get charset currently in use by this adapter.
     *
     * @return CharsetInterface $charset
     */
    public function getCharset()
    {
        if ($this->charset === null) {
            $this->charset = $this->getDefaultCharset();
        }

        return $this->charset;
    }

    /**
     * @return Charset\AsciiExtended
     */
    public function getDefaultCharset()
    {
        return new Charset\AsciiExtended();
    }

    /**
     * Read a single character from the console input
     *
     * @param  string|null $mask A list of allowed chars
     * @return string
     * @throws Exception\RuntimeException
     */
    public function readChar($mask = null)
    {
        // Decide if we can use `choice` tool
        $useChoice = $mask !== null && preg_match('/^[a-zA-Z0-9]+$/D', $mask);

        if ($useChoice) {
            // Use Windows 98+ "choice" command, which allows for reading a
            // single character matching a mask, but is limited to lower ASCII
            // range.
            do {
                exec('choice /n /cs /c:' . $mask, $output, $return);
                if ($return == 255 || $return < 1 || $return > strlen($mask)) {
                    throw new Exception\RuntimeException('"choice" command failed to run. Are you using Windows XP or newer?');
                }

                // Fetch the char from mask
                $char = substr($mask, $return - 1, 1);
            } while ("" === $char || ($mask !== null && false === strstr($mask, $char)));

            return $char;
        }

        // Try to use PowerShell, giving it console access. Because PowersShell
        // interpreter can take a short while to load, we are emptying the
        // whole keyboard buffer and picking the last key that has been pressed
        // before or after PowerShell command has started. The ASCII code for
        // that key is then converted to a character.
        if ($mask === null) {
            exec(
                'powershell -NonInteractive -NoProfile -NoLogo -OutputFormat Text -Command "'
                . 'while ($Host.UI.RawUI.KeyAvailable) {$key = $Host.UI.RawUI.ReadKey(\'NoEcho,IncludeKeyDown\');}'
                . 'write $key.VirtualKeyCode;'
                . '"',
                $result,
                $return
            );

            // Retrieve char from the result.
            $char = !empty($result) ? implode('', $result) : null;

            if (!empty($char) && !$return) {
                // We have obtained an ASCII code, convert back to a char ...
                $char = chr($char);

                // ... and return it...
                return $char;
            }
        } else {
            // Windows and DOS will return carriage-return char (ASCII 13) when
            // the user presses [ENTER] key, but Console Adapter user might
            // have provided a \n Newline (ASCII 10) in the mask, to allow
            // [ENTER].  We are going to replace all CR with NL to conform.
            $mask = strtr($mask, "\n", "\r");

            // Prepare a list of ASCII codes from mask chars
            $asciiMask = array_map(function ($char) {
                return ord($char);
            }, str_split($mask));
            $asciiMask = array_unique($asciiMask);

            // Char mask filtering is now handled by the PowerShell itself,
            // because it's a much faster method than invoking PS interpreter
            // after each mismatch. The command should return ASCII code of a
            // matching key.
            $result = $return = null;
            exec(
                'powershell -NonInteractive -NoProfile -NoLogo -OutputFormat Text -Command "'
                . '[int[]] $mask = '.implode(',', $asciiMask).';'
                . 'do {'
                . '$key = $Host.UI.RawUI.ReadKey(\'NoEcho,IncludeKeyDown\').VirtualKeyCode;'
                . '} while ( !($mask -contains $key) );'
                . 'write $key;'
                . '"',
                $result,
                $return
            );

            $char = !empty($result) ? trim(implode('', $result)) : null;

            if (!$return && $char && ($mask === null || in_array($char, $asciiMask))) {
                // We have obtained an ASCII code, check if it is a carriage
                // return and normalize it as needed
                if ($char == 13) {
                    $char = 10;
                }

                // Convert to a character
                $char = chr($char);

                // ... and return it...
                return $char;
            }
        }

        // Fall back to standard input, which on Windows does not allow reading
        // a single character. This is a limitation of Windows streams
        // implementation (not PHP) and this behavior cannot be changed with a
        // command like "stty", known to POSIX systems.
        $stream = fopen('php://stdin', 'rb');
        do {
            $char = fgetc($stream);
            $char = substr(trim($char), 0, 1);
        } while (!$char || ($mask !== null && !stristr($mask, $char)));
        fclose($stream);

        return $char;
    }
}