[securemail] Update Composer dependencies
[friendica-addons.git/.git] / securemail / vendor / phpseclib / phpseclib / phpseclib / File / ANSI.php
1 <?php
2
3 /**
4  * Pure-PHP ANSI Decoder
5  *
6  * PHP version 5
7  *
8  * If you call read() in \phpseclib\Net\SSH2 you may get {@link http://en.wikipedia.org/wiki/ANSI_escape_code ANSI escape codes} back.
9  * They'd look like chr(0x1B) . '[00m' or whatever (0x1B = ESC).  They tell a
10  * {@link http://en.wikipedia.org/wiki/Terminal_emulator terminal emulator} how to format the characters, what
11  * color to display them in, etc. \phpseclib\File\ANSI is a {@link http://en.wikipedia.org/wiki/VT100 VT100} terminal emulator.
12  *
13  * @category  File
14  * @package   ANSI
15  * @author    Jim Wigginton <terrafrost@php.net>
16  * @copyright 2012 Jim Wigginton
17  * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
18  * @link      http://phpseclib.sourceforge.net
19  */
20
21 namespace phpseclib\File;
22
23 /**
24  * Pure-PHP ANSI Decoder
25  *
26  * @package ANSI
27  * @author  Jim Wigginton <terrafrost@php.net>
28  * @access  public
29  */
30 class ANSI
31 {
32     /**
33      * Max Width
34      *
35      * @var int
36      * @access private
37      */
38     var $max_x;
39
40     /**
41      * Max Height
42      *
43      * @var int
44      * @access private
45      */
46     var $max_y;
47
48     /**
49      * Max History
50      *
51      * @var int
52      * @access private
53      */
54     var $max_history;
55
56     /**
57      * History
58      *
59      * @var array
60      * @access private
61      */
62     var $history;
63
64     /**
65      * History Attributes
66      *
67      * @var array
68      * @access private
69      */
70     var $history_attrs;
71
72     /**
73      * Current Column
74      *
75      * @var int
76      * @access private
77      */
78     var $x;
79
80     /**
81      * Current Row
82      *
83      * @var int
84      * @access private
85      */
86     var $y;
87
88     /**
89      * Old Column
90      *
91      * @var int
92      * @access private
93      */
94     var $old_x;
95
96     /**
97      * Old Row
98      *
99      * @var int
100      * @access private
101      */
102     var $old_y;
103
104     /**
105      * An empty attribute cell
106      *
107      * @var object
108      * @access private
109      */
110     var $base_attr_cell;
111
112     /**
113      * The current attribute cell
114      *
115      * @var object
116      * @access private
117      */
118     var $attr_cell;
119
120     /**
121      * An empty attribute row
122      *
123      * @var array
124      * @access private
125      */
126     var $attr_row;
127
128     /**
129      * The current screen text
130      *
131      * @var array
132      * @access private
133      */
134     var $screen;
135
136     /**
137      * The current screen attributes
138      *
139      * @var array
140      * @access private
141      */
142     var $attrs;
143
144     /**
145      * Current ANSI code
146      *
147      * @var string
148      * @access private
149      */
150     var $ansi;
151
152     /**
153      * Tokenization
154      *
155      * @var array
156      * @access private
157      */
158     var $tokenization;
159
160     /**
161      * Default Constructor.
162      *
163      * @return \phpseclib\File\ANSI
164      * @access public
165      */
166     function __construct()
167     {
168         $attr_cell = new \stdClass();
169         $attr_cell->bold = false;
170         $attr_cell->underline = false;
171         $attr_cell->blink = false;
172         $attr_cell->background = 'black';
173         $attr_cell->foreground = 'white';
174         $attr_cell->reverse = false;
175         $this->base_attr_cell = clone $attr_cell;
176         $this->attr_cell = clone $attr_cell;
177
178         $this->setHistory(200);
179         $this->setDimensions(80, 24);
180     }
181
182     /**
183      * Set terminal width and height
184      *
185      * Resets the screen as well
186      *
187      * @param int $x
188      * @param int $y
189      * @access public
190      */
191     function setDimensions($x, $y)
192     {
193         $this->max_x = $x - 1;
194         $this->max_y = $y - 1;
195         $this->x = $this->y = 0;
196         $this->history = $this->history_attrs = array();
197         $this->attr_row = array_fill(0, $this->max_x + 2, $this->base_attr_cell);
198         $this->screen = array_fill(0, $this->max_y + 1, '');
199         $this->attrs = array_fill(0, $this->max_y + 1, $this->attr_row);
200         $this->ansi = '';
201     }
202
203     /**
204      * Set the number of lines that should be logged past the terminal height
205      *
206      * @param int $x
207      * @param int $y
208      * @access public
209      */
210     function setHistory($history)
211     {
212         $this->max_history = $history;
213     }
214
215     /**
216      * Load a string
217      *
218      * @param string $source
219      * @access public
220      */
221     function loadString($source)
222     {
223         $this->setDimensions($this->max_x + 1, $this->max_y + 1);
224         $this->appendString($source);
225     }
226
227     /**
228      * Appdend a string
229      *
230      * @param string $source
231      * @access public
232      */
233     function appendString($source)
234     {
235         $this->tokenization = array('');
236         for ($i = 0; $i < strlen($source); $i++) {
237             if (strlen($this->ansi)) {
238                 $this->ansi.= $source[$i];
239                 $chr = ord($source[$i]);
240                 // http://en.wikipedia.org/wiki/ANSI_escape_code#Sequence_elements
241                 // single character CSI's not currently supported
242                 switch (true) {
243                     case $this->ansi == "\x1B=":
244                         $this->ansi = '';
245                         continue 2;
246                     case strlen($this->ansi) == 2 && $chr >= 64 && $chr <= 95 && $chr != ord('['):
247                     case strlen($this->ansi) > 2 && $chr >= 64 && $chr <= 126:
248                         break;
249                     default:
250                         continue 2;
251                 }
252                 $this->tokenization[] = $this->ansi;
253                 $this->tokenization[] = '';
254                 // http://ascii-table.com/ansi-escape-sequences-vt-100.php
255                 switch ($this->ansi) {
256                     case "\x1B[H": // Move cursor to upper left corner
257                         $this->old_x = $this->x;
258                         $this->old_y = $this->y;
259                         $this->x = $this->y = 0;
260                         break;
261                     case "\x1B[J": // Clear screen from cursor down
262                         $this->history = array_merge($this->history, array_slice(array_splice($this->screen, $this->y + 1), 0, $this->old_y));
263                         $this->screen = array_merge($this->screen, array_fill($this->y, $this->max_y, ''));
264
265                         $this->history_attrs = array_merge($this->history_attrs, array_slice(array_splice($this->attrs, $this->y + 1), 0, $this->old_y));
266                         $this->attrs = array_merge($this->attrs, array_fill($this->y, $this->max_y, $this->attr_row));
267
268                         if (count($this->history) == $this->max_history) {
269                             array_shift($this->history);
270                             array_shift($this->history_attrs);
271                         }
272                     case "\x1B[K": // Clear screen from cursor right
273                         $this->screen[$this->y] = substr($this->screen[$this->y], 0, $this->x);
274
275                         array_splice($this->attrs[$this->y], $this->x + 1, $this->max_x - $this->x, array_fill($this->x, $this->max_x - $this->x - 1, $this->base_attr_cell));
276                         break;
277                     case "\x1B[2K": // Clear entire line
278                         $this->screen[$this->y] = str_repeat(' ', $this->x);
279                         $this->attrs[$this->y] = $this->attr_row;
280                         break;
281                     case "\x1B[?1h": // set cursor key to application
282                     case "\x1B[?25h": // show the cursor
283                     case "\x1B(B": // set united states g0 character set
284                         break;
285                     case "\x1BE": // Move to next line
286                         $this->_newLine();
287                         $this->x = 0;
288                         break;
289                     default:
290                         switch (true) {
291                             case preg_match('#\x1B\[(\d+)B#', $this->ansi, $match): // Move cursor down n lines
292                                 $this->old_y = $this->y;
293                                 $this->y+= $match[1];
294                                 break;
295                             case preg_match('#\x1B\[(\d+);(\d+)H#', $this->ansi, $match): // Move cursor to screen location v,h
296                                 $this->old_x = $this->x;
297                                 $this->old_y = $this->y;
298                                 $this->x = $match[2] - 1;
299                                 $this->y = $match[1] - 1;
300                                 break;
301                             case preg_match('#\x1B\[(\d+)C#', $this->ansi, $match): // Move cursor right n lines
302                                 $this->old_x = $this->x;
303                                 $this->x+= $match[1];
304                                 break;
305                             case preg_match('#\x1B\[(\d+)D#', $this->ansi, $match): // Move cursor left n lines
306                                 $this->old_x = $this->x;
307                                 $this->x-= $match[1];
308                                 break;
309                             case preg_match('#\x1B\[(\d+);(\d+)r#', $this->ansi, $match): // Set top and bottom lines of a window
310                                 break;
311                             case preg_match('#\x1B\[(\d*(?:;\d*)*)m#', $this->ansi, $match): // character attributes
312                                 $attr_cell = &$this->attr_cell;
313                                 $mods = explode(';', $match[1]);
314                                 foreach ($mods as $mod) {
315                                     switch ($mod) {
316                                         case 0: // Turn off character attributes
317                                             $attr_cell = clone $this->base_attr_cell;
318                                             break;
319                                         case 1: // Turn bold mode on
320                                             $attr_cell->bold = true;
321                                             break;
322                                         case 4: // Turn underline mode on
323                                             $attr_cell->underline = true;
324                                             break;
325                                         case 5: // Turn blinking mode on
326                                             $attr_cell->blink = true;
327                                             break;
328                                         case 7: // Turn reverse video on
329                                             $attr_cell->reverse = !$attr_cell->reverse;
330                                             $temp = $attr_cell->background;
331                                             $attr_cell->background = $attr_cell->foreground;
332                                             $attr_cell->foreground = $temp;
333                                             break;
334                                         default: // set colors
335                                             //$front = $attr_cell->reverse ? &$attr_cell->background : &$attr_cell->foreground;
336                                             $front = &$attr_cell->{ $attr_cell->reverse ? 'background' : 'foreground' };
337                                             //$back = $attr_cell->reverse ? &$attr_cell->foreground : &$attr_cell->background;
338                                             $back = &$attr_cell->{ $attr_cell->reverse ? 'foreground' : 'background' };
339                                             switch ($mod) {
340                                                 // @codingStandardsIgnoreStart
341                                                 case 30: $front = 'black'; break;
342                                                 case 31: $front = 'red'; break;
343                                                 case 32: $front = 'green'; break;
344                                                 case 33: $front = 'yellow'; break;
345                                                 case 34: $front = 'blue'; break;
346                                                 case 35: $front = 'magenta'; break;
347                                                 case 36: $front = 'cyan'; break;
348                                                 case 37: $front = 'white'; break;
349
350                                                 case 40: $back = 'black'; break;
351                                                 case 41: $back = 'red'; break;
352                                                 case 42: $back = 'green'; break;
353                                                 case 43: $back = 'yellow'; break;
354                                                 case 44: $back = 'blue'; break;
355                                                 case 45: $back = 'magenta'; break;
356                                                 case 46: $back = 'cyan'; break;
357                                                 case 47: $back = 'white'; break;
358                                                 // @codingStandardsIgnoreEnd
359
360                                                 default:
361                                                     //user_error('Unsupported attribute: ' . $mod);
362                                                     $this->ansi = '';
363                                                     break 2;
364                                             }
365                                     }
366                                 }
367                                 break;
368                             default:
369                                 //user_error("{$this->ansi} is unsupported\r\n");
370                         }
371                 }
372                 $this->ansi = '';
373                 continue;
374             }
375
376             $this->tokenization[count($this->tokenization) - 1].= $source[$i];
377             switch ($source[$i]) {
378                 case "\r":
379                     $this->x = 0;
380                     break;
381                 case "\n":
382                     $this->_newLine();
383                     break;
384                 case "\x08": // backspace
385                     if ($this->x) {
386                         $this->x--;
387                         $this->attrs[$this->y][$this->x] = clone $this->base_attr_cell;
388                         $this->screen[$this->y] = substr_replace(
389                             $this->screen[$this->y],
390                             $source[$i],
391                             $this->x,
392                             1
393                         );
394                     }
395                     break;
396                 case "\x0F": // shift
397                     break;
398                 case "\x1B": // start ANSI escape code
399                     $this->tokenization[count($this->tokenization) - 1] = substr($this->tokenization[count($this->tokenization) - 1], 0, -1);
400                     //if (!strlen($this->tokenization[count($this->tokenization) - 1])) {
401                     //    array_pop($this->tokenization);
402                     //}
403                     $this->ansi.= "\x1B";
404                     break;
405                 default:
406                     $this->attrs[$this->y][$this->x] = clone $this->attr_cell;
407                     if ($this->x > strlen($this->screen[$this->y])) {
408                         $this->screen[$this->y] = str_repeat(' ', $this->x);
409                     }
410                     $this->screen[$this->y] = substr_replace(
411                         $this->screen[$this->y],
412                         $source[$i],
413                         $this->x,
414                         1
415                     );
416
417                     if ($this->x > $this->max_x) {
418                         $this->x = 0;
419                         $this->y++;
420                     } else {
421                         $this->x++;
422                     }
423             }
424         }
425     }
426
427     /**
428      * Add a new line
429      *
430      * Also update the $this->screen and $this->history buffers
431      *
432      * @access private
433      */
434     function _newLine()
435     {
436         //if ($this->y < $this->max_y) {
437         //    $this->y++;
438         //}
439
440         while ($this->y >= $this->max_y) {
441             $this->history = array_merge($this->history, array(array_shift($this->screen)));
442             $this->screen[] = '';
443
444             $this->history_attrs = array_merge($this->history_attrs, array(array_shift($this->attrs)));
445             $this->attrs[] = $this->attr_row;
446
447             if (count($this->history) >= $this->max_history) {
448                 array_shift($this->history);
449                 array_shift($this->history_attrs);
450             }
451
452             $this->y--;
453         }
454         $this->y++;
455     }
456
457     /**
458      * Returns the current coordinate without preformating
459      *
460      * @access private
461      * @return string
462      */
463     function _processCoordinate($last_attr, $cur_attr, $char)
464     {
465         $output = '';
466
467         if ($last_attr != $cur_attr) {
468             $close = $open = '';
469             if ($last_attr->foreground != $cur_attr->foreground) {
470                 if ($cur_attr->foreground != 'white') {
471                     $open.= '<span style="color: ' . $cur_attr->foreground . '">';
472                 }
473                 if ($last_attr->foreground != 'white') {
474                     $close = '</span>' . $close;
475                 }
476             }
477             if ($last_attr->background != $cur_attr->background) {
478                 if ($cur_attr->background != 'black') {
479                     $open.= '<span style="background: ' . $cur_attr->background . '">';
480                 }
481                 if ($last_attr->background != 'black') {
482                     $close = '</span>' . $close;
483                 }
484             }
485             if ($last_attr->bold != $cur_attr->bold) {
486                 if ($cur_attr->bold) {
487                     $open.= '<b>';
488                 } else {
489                     $close = '</b>' . $close;
490                 }
491             }
492             if ($last_attr->underline != $cur_attr->underline) {
493                 if ($cur_attr->underline) {
494                     $open.= '<u>';
495                 } else {
496                     $close = '</u>' . $close;
497                 }
498             }
499             if ($last_attr->blink != $cur_attr->blink) {
500                 if ($cur_attr->blink) {
501                     $open.= '<blink>';
502                 } else {
503                     $close = '</blink>' . $close;
504                 }
505             }
506             $output.= $close . $open;
507         }
508
509         $output.= htmlspecialchars($char);
510
511         return $output;
512     }
513
514     /**
515      * Returns the current screen without preformating
516      *
517      * @access private
518      * @return string
519      */
520     function _getScreen()
521     {
522         $output = '';
523         $last_attr = $this->base_attr_cell;
524         for ($i = 0; $i <= $this->max_y; $i++) {
525             for ($j = 0; $j <= $this->max_x; $j++) {
526                 $cur_attr = $this->attrs[$i][$j];
527                 $output.= $this->_processCoordinate($last_attr, $cur_attr, isset($this->screen[$i][$j]) ? $this->screen[$i][$j] : '');
528                 $last_attr = $this->attrs[$i][$j];
529             }
530             $output.= "\r\n";
531         }
532         $output = substr($output, 0, -2);
533         // close any remaining open tags
534         $output.= $this->_processCoordinate($last_attr, $this->base_attr_cell, '');
535         return rtrim($output);
536     }
537
538     /**
539      * Returns the current screen
540      *
541      * @access public
542      * @return string
543      */
544     function getScreen()
545     {
546         return '<pre width="' . ($this->max_x + 1) . '" style="color: white; background: black">' . $this->_getScreen() . '</pre>';
547     }
548
549     /**
550      * Returns the current screen and the x previous lines
551      *
552      * @access public
553      * @return string
554      */
555     function getHistory()
556     {
557         $scrollback = '';
558         $last_attr = $this->base_attr_cell;
559         for ($i = 0; $i < count($this->history); $i++) {
560             for ($j = 0; $j <= $this->max_x + 1; $j++) {
561                 $cur_attr = $this->history_attrs[$i][$j];
562                 $scrollback.= $this->_processCoordinate($last_attr, $cur_attr, isset($this->history[$i][$j]) ? $this->history[$i][$j] : '');
563                 $last_attr = $this->history_attrs[$i][$j];
564             }
565             $scrollback.= "\r\n";
566         }
567         $base_attr_cell = $this->base_attr_cell;
568         $this->base_attr_cell = $last_attr;
569         $scrollback.= $this->_getScreen();
570         $this->base_attr_cell = $base_attr_cell;
571
572         return '<pre width="' . ($this->max_x + 1) . '" style="color: white; background: black">' . $scrollback . '</span></pre>';
573     }
574 }