diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 12:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 12:47:55 +0000 |
commit | 2aadc03ef15cb5ca5cc2af8a7c08e070742f0ac4 (patch) | |
tree | 033cc839730fda84ff08db877037977be94e5e3a /vendor/vte | |
parent | Initial commit. (diff) | |
download | cargo-2aadc03ef15cb5ca5cc2af8a7c08e070742f0ac4.tar.xz cargo-2aadc03ef15cb5ca5cc2af8a7c08e070742f0ac4.zip |
Adding upstream version 0.70.1+ds1.upstream/0.70.1+ds1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/vte')
-rw-r--r-- | vendor/vte/.cargo-checksum.json | 1 | ||||
-rw-r--r-- | vendor/vte/CHANGELOG.md | 57 | ||||
-rw-r--r-- | vendor/vte/Cargo.lock | 58 | ||||
-rw-r--r-- | vendor/vte/Cargo.toml | 39 | ||||
-rw-r--r-- | vendor/vte/LICENSE-APACHE | 176 | ||||
-rw-r--r-- | vendor/vte/LICENSE-MIT | 25 | ||||
-rw-r--r-- | vendor/vte/README.md | 19 | ||||
-rw-r--r-- | vendor/vte/debian/patches/00-remove-nightly-feature | 9 | ||||
-rw-r--r-- | vendor/vte/debian/patches/series | 1 | ||||
-rw-r--r-- | vendor/vte/examples/parselog.rs | 75 | ||||
-rw-r--r-- | vendor/vte/rustfmt.toml | 13 | ||||
-rw-r--r-- | vendor/vte/src/definitions.rs | 114 | ||||
-rw-r--r-- | vendor/vte/src/lib.rs | 973 | ||||
-rw-r--r-- | vendor/vte/src/params.rs | 142 | ||||
-rw-r--r-- | vendor/vte/src/table.rs | 171 | ||||
-rw-r--r-- | vendor/vte/tests/demo.vte | 2052 |
16 files changed, 3925 insertions, 0 deletions
diff --git a/vendor/vte/.cargo-checksum.json b/vendor/vte/.cargo-checksum.json new file mode 100644 index 0000000..e55c6bd --- /dev/null +++ b/vendor/vte/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{},"package":"6cbce692ab4ca2f1f3047fcf732430249c0e971bfdd2b234cf2c47ad93af5983"}
\ No newline at end of file diff --git a/vendor/vte/CHANGELOG.md b/vendor/vte/CHANGELOG.md new file mode 100644 index 0000000..f094a89 --- /dev/null +++ b/vendor/vte/CHANGELOG.md @@ -0,0 +1,57 @@ +CHANGELOG +========= + +## 0.10.1 + +- Fixed invalid intermediates when transitioning from DCS to ESC + +## 0.10.0 + +- Changed the type of CSI parameters from i64 to u16 +- All methods of the `Perform` trait are now optional + +## 0.9.0 + +- Added CSI subparameter support; required changes can be seen in Alacritty: + https://github.com/alacritty/alacritty/commit/576252294d09c1f52ec73bde03652349bdf5a529#diff-49ac9e6f6e6a855312bfcd393201f18ca53e6148c4a22a3a4949f1f9d1d137a8 + +## 0.8.0 + +- Remove C1 ST support in OSCs, fixing OSCs with ST in the payload + +## 0.7.1 + +- Out of bounds when parsing a DCS with more than 16 parameters + +## 0.7.0 + +- Fix params reset between escapes +- Removed unused parameter from `esc_dispatch` + +## 0.6.0 + +- Fix build failure on Rust 1.36.0 +- Add `bool_terminated` parameter to osc dispatch + +## 0.5.0 + +- Support for dynamically sized escape buffers without feature `no_std` +- Improved UTF8 parser performance +- Migrate to Rust 2018 + +## 0.4.0 + +- Fix handling of DCS escapes + +## 0.3.3 + +- Fix off-by-one error in CSI parsing when params list was at max length + (previously caused a panic). +- Support no_std + +## 0.2.0 + +- Removes `osc_start`, `osc_put`, and `osc_end` +- Adds `osc_dispatch` which simply receives a list of parameters +- Removes `byte: u8` parameter from `hook` and `unhook` because it's always + zero. diff --git a/vendor/vte/Cargo.lock b/vendor/vte/Cargo.lock new file mode 100644 index 0000000..ff4c42b --- /dev/null +++ b/vendor/vte/Cargo.lock @@ -0,0 +1,58 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "arrayvec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" + +[[package]] +name = "proc-macro2" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "unicode-xid" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" + +[[package]] +name = "utf8parse" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "936e4b492acfd135421d8dca4b1aa80a7bfc26e702ef3af710e0752684df5372" + +[[package]] +name = "vte" +version = "0.10.1" +dependencies = [ + "arrayvec", + "utf8parse", + "vte_generate_state_changes", +] + +[[package]] +name = "vte_generate_state_changes" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d257817081c7dffcdbab24b9e62d2def62e2ff7d00b1c20062551e6cccc145ff" +dependencies = [ + "proc-macro2", + "quote", +] diff --git a/vendor/vte/Cargo.toml b/vendor/vte/Cargo.toml new file mode 100644 index 0000000..42b45f3 --- /dev/null +++ b/vendor/vte/Cargo.toml @@ -0,0 +1,39 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies +# +# If you believe there's an error in this file please file an +# issue against the rust-lang/cargo repository. If you're +# editing this file be aware that the upstream Cargo.toml +# will likely look very different (and much more reasonable) + +[package] +edition = "2018" +name = "vte" +version = "0.10.1" +authors = ["Joe Wilm <joe@jwilm.com>", "Christian Duerr <contact@christianduerr.com>"] +exclude = ["/.travis.yml"] +description = "Parser for implementing terminal emulators" +documentation = "https://docs.rs/vte/" +readme = "README.md" +keywords = ["ansi", "vte", "parser", "terminal"] +categories = ["parsing", "no-std"] +license = "Apache-2.0 OR MIT" +repository = "https://github.com/alacritty/vte" +[dependencies.arrayvec] +version = "0.5.1" +optional = true +default-features = false + +[dependencies.utf8parse] +version = "0.2.0" + +[dependencies.vte_generate_state_changes] +version = "0.1.0" + +[features] +default = ["no_std"] +no_std = ["arrayvec"] diff --git a/vendor/vte/LICENSE-APACHE b/vendor/vte/LICENSE-APACHE new file mode 100644 index 0000000..1b5ec8b --- /dev/null +++ b/vendor/vte/LICENSE-APACHE @@ -0,0 +1,176 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS diff --git a/vendor/vte/LICENSE-MIT b/vendor/vte/LICENSE-MIT new file mode 100644 index 0000000..bb419c2 --- /dev/null +++ b/vendor/vte/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2016 Joe Wilm + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/vendor/vte/README.md b/vendor/vte/README.md new file mode 100644 index 0000000..a9c016a --- /dev/null +++ b/vendor/vte/README.md @@ -0,0 +1,19 @@ +vte +=== + +[![Build Status](https://travis-ci.org/alacritty/vte.svg?branch=master)](https://travis-ci.org/alacritty/vte) +[![Crates.io Version](https://img.shields.io/crates/v/vte.svg)](https://crates.io/crates/vte/) + +Parser for implementing virtual terminal emulators in Rust. + +The parser is implemented according to [Paul Williams' ANSI parser state +machine]. The state machine doesn't assign meaning to the parsed data and is +thus not itself sufficient for writing a terminal emulator. Instead, it is +expected that an implementation of the `Perform` trait which does something +useful with the parsed data. The `Parser` handles the book keeping, and the +`Perform` gets to simply handle actions. + +See the [docs] for more info. + +[Paul Williams' ANSI parser state machine]: https://vt100.net/emu/dec_ansi_parser +[docs]: https://docs.rs/crate/vte/ diff --git a/vendor/vte/debian/patches/00-remove-nightly-feature b/vendor/vte/debian/patches/00-remove-nightly-feature new file mode 100644 index 0000000..0ebb7ce --- /dev/null +++ b/vendor/vte/debian/patches/00-remove-nightly-feature @@ -0,0 +1,9 @@ +--- a/Cargo.toml ++++ b/Cargo.toml +@@ -72,6 +72,5 @@ + "bitflags", + ] + default = ["no_std"] +-nightly = ["utf8parse/nightly"] + no_std = ["arrayvec"] + serde = ["dep:serde"] diff --git a/vendor/vte/debian/patches/series b/vendor/vte/debian/patches/series new file mode 100644 index 0000000..203bdd7 --- /dev/null +++ b/vendor/vte/debian/patches/series @@ -0,0 +1 @@ +00-remove-nightly-feature diff --git a/vendor/vte/examples/parselog.rs b/vendor/vte/examples/parselog.rs new file mode 100644 index 0000000..dfd0aee --- /dev/null +++ b/vendor/vte/examples/parselog.rs @@ -0,0 +1,75 @@ +//! Parse input from stdin and log actions on stdout +use std::io::{self, Read}; + +use vte::{Params, Parser, Perform}; + +/// A type implementing Perform that just logs actions +struct Log; + +impl Perform for Log { + fn print(&mut self, c: char) { + println!("[print] {:?}", c); + } + + fn execute(&mut self, byte: u8) { + println!("[execute] {:02x}", byte); + } + + fn hook(&mut self, params: &Params, intermediates: &[u8], ignore: bool, c: char) { + println!( + "[hook] params={:?}, intermediates={:?}, ignore={:?}, char={:?}", + params, intermediates, ignore, c + ); + } + + fn put(&mut self, byte: u8) { + println!("[put] {:02x}", byte); + } + + fn unhook(&mut self) { + println!("[unhook]"); + } + + fn osc_dispatch(&mut self, params: &[&[u8]], bell_terminated: bool) { + println!("[osc_dispatch] params={:?} bell_terminated={}", params, bell_terminated); + } + + fn csi_dispatch(&mut self, params: &Params, intermediates: &[u8], ignore: bool, c: char) { + println!( + "[csi_dispatch] params={:#?}, intermediates={:?}, ignore={:?}, char={:?}", + params, intermediates, ignore, c + ); + } + + fn esc_dispatch(&mut self, intermediates: &[u8], ignore: bool, byte: u8) { + println!( + "[esc_dispatch] intermediates={:?}, ignore={:?}, byte={:02x}", + intermediates, ignore, byte + ); + } +} + +fn main() { + let input = io::stdin(); + let mut handle = input.lock(); + + let mut statemachine = Parser::new(); + let mut performer = Log; + + let mut buf = [0; 2048]; + + loop { + match handle.read(&mut buf) { + Ok(0) => break, + Ok(n) => { + for byte in &buf[..n] { + statemachine.advance(&mut performer, *byte); + } + }, + Err(err) => { + println!("err: {}", err); + break; + }, + } + } +} diff --git a/vendor/vte/rustfmt.toml b/vendor/vte/rustfmt.toml new file mode 100644 index 0000000..9308ba9 --- /dev/null +++ b/vendor/vte/rustfmt.toml @@ -0,0 +1,13 @@ +format_code_in_doc_comments = true +match_block_trailing_comma = true +condense_wildcard_suffixes = true +use_field_init_shorthand = true +overflow_delimited_expr = true +use_small_heuristics = "Max" +normalize_comments = true +reorder_impl_items = true +use_try_shorthand = true +newline_style = "Unix" +format_strings = true +wrap_comments = true +comment_width = 100 diff --git a/vendor/vte/src/definitions.rs b/vendor/vte/src/definitions.rs new file mode 100644 index 0000000..fe7952d --- /dev/null +++ b/vendor/vte/src/definitions.rs @@ -0,0 +1,114 @@ +use core::mem; + +#[allow(dead_code)] +#[derive(Debug, Copy, Clone)] +pub enum State { + Anywhere = 0, + CsiEntry = 1, + CsiIgnore = 2, + CsiIntermediate = 3, + CsiParam = 4, + DcsEntry = 5, + DcsIgnore = 6, + DcsIntermediate = 7, + DcsParam = 8, + DcsPassthrough = 9, + Escape = 10, + EscapeIntermediate = 11, + Ground = 12, + OscString = 13, + SosPmApcString = 14, + Utf8 = 15, +} + +impl Default for State { + fn default() -> State { + State::Ground + } +} + +#[allow(dead_code)] +#[derive(Debug, Clone, Copy)] +pub enum Action { + None = 0, + Clear = 1, + Collect = 2, + CsiDispatch = 3, + EscDispatch = 4, + Execute = 5, + Hook = 6, + Ignore = 7, + OscEnd = 8, + OscPut = 9, + OscStart = 10, + Param = 11, + Print = 12, + Put = 13, + Unhook = 14, + BeginUtf8 = 15, +} + +/// Unpack a u8 into a State and Action +/// +/// The implementation of this assumes that there are *precisely* 16 variants for both Action and +/// State. Furthermore, it assumes that the enums are tag-only; that is, there is no data in any +/// variant. +/// +/// Bad things will happen if those invariants are violated. +#[inline(always)] +pub fn unpack(delta: u8) -> (State, Action) { + unsafe { + ( + // State is stored in bottom 4 bits + mem::transmute(delta & 0x0f), + // Action is stored in top 4 bits + mem::transmute(delta >> 4), + ) + } +} + +#[inline(always)] +pub const fn pack(state: State, action: Action) -> u8 { + (action as u8) << 4 | state as u8 +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn unpack_state_action() { + match unpack(0xee) { + (State::SosPmApcString, Action::Unhook) => (), + _ => panic!("unpack failed"), + } + + match unpack(0x0f) { + (State::Utf8, Action::None) => (), + _ => panic!("unpack failed"), + } + + match unpack(0xff) { + (State::Utf8, Action::BeginUtf8) => (), + _ => panic!("unpack failed"), + } + } + + #[test] + fn pack_state_action() { + match unpack(0xee) { + (State::SosPmApcString, Action::Unhook) => (), + _ => panic!("unpack failed"), + } + + match unpack(0x0f) { + (State::Utf8, Action::None) => (), + _ => panic!("unpack failed"), + } + + match unpack(0xff) { + (State::Utf8, Action::BeginUtf8) => (), + _ => panic!("unpack failed"), + } + } +} diff --git a/vendor/vte/src/lib.rs b/vendor/vte/src/lib.rs new file mode 100644 index 0000000..157392a --- /dev/null +++ b/vendor/vte/src/lib.rs @@ -0,0 +1,973 @@ +//! Parser for implementing virtual terminal emulators +//! +//! [`Parser`] is implemented according to [Paul Williams' ANSI parser +//! state machine]. The state machine doesn't assign meaning to the parsed data +//! and is thus not itself sufficient for writing a terminal emulator. Instead, +//! it is expected that an implementation of [`Perform`] is provided which does +//! something useful with the parsed data. The [`Parser`] handles the book +//! keeping, and the [`Perform`] gets to simply handle actions. +//! +//! # Examples +//! +//! For an example of using the [`Parser`] please see the examples folder. The example included +//! there simply logs all the actions [`Perform`] does. One quick thing to see it in action is to +//! pipe `vim` into it +//! +//! ```sh +//! cargo build --release --example parselog +//! vim | target/release/examples/parselog +//! ``` +//! +//! Just type `:q` to exit. +//! +//! # Differences from original state machine description +//! +//! * UTF-8 Support for Input +//! * OSC Strings can be terminated by 0x07 +//! * Only supports 7-bit codes. Some 8-bit codes are still supported, but they no longer work in +//! all states. +//! +//! [`Parser`]: struct.Parser.html +//! [`Perform`]: trait.Perform.html +//! [Paul Williams' ANSI parser state machine]: https://vt100.net/emu/dec_ansi_parser +#![deny(clippy::all, clippy::if_not_else, clippy::enum_glob_use, clippy::wrong_pub_self_convention)] +#![cfg_attr(all(feature = "nightly", test), feature(test))] +#![cfg_attr(feature = "no_std", no_std)] + +use core::mem::MaybeUninit; + +#[cfg(feature = "no_std")] +use arrayvec::ArrayVec; +use utf8parse as utf8; + +mod definitions; +mod params; +mod table; + +pub use params::{Params, ParamsIter}; + +use definitions::{unpack, Action, State}; + +const MAX_INTERMEDIATES: usize = 2; +const MAX_OSC_PARAMS: usize = 16; +#[cfg(any(feature = "no_std", test))] +const MAX_OSC_RAW: usize = 1024; + +struct VtUtf8Receiver<'a, P: Perform>(&'a mut P, &'a mut State); + +impl<'a, P: Perform> utf8::Receiver for VtUtf8Receiver<'a, P> { + fn codepoint(&mut self, c: char) { + self.0.print(c); + *self.1 = State::Ground; + } + + fn invalid_sequence(&mut self) { + self.0.print('�'); + *self.1 = State::Ground; + } +} + +/// Parser for raw _VTE_ protocol which delegates actions to a [`Perform`] +/// +/// [`Perform`]: trait.Perform.html +#[derive(Default)] +pub struct Parser { + state: State, + intermediates: [u8; MAX_INTERMEDIATES], + intermediate_idx: usize, + params: Params, + param: u16, + #[cfg(feature = "no_std")] + osc_raw: ArrayVec<[u8; MAX_OSC_RAW]>, + #[cfg(not(feature = "no_std"))] + osc_raw: Vec<u8>, + osc_params: [(usize, usize); MAX_OSC_PARAMS], + osc_num_params: usize, + ignoring: bool, + utf8_parser: utf8::Parser, +} + +impl Parser { + /// Create a new Parser + pub fn new() -> Parser { + Parser::default() + } + + #[inline] + fn params(&self) -> &Params { + &self.params + } + + #[inline] + fn intermediates(&self) -> &[u8] { + &self.intermediates[..self.intermediate_idx] + } + + /// Advance the parser state + /// + /// Requires a [`Perform`] in case `byte` triggers an action + /// + /// [`Perform`]: trait.Perform.html + #[inline] + pub fn advance<P: Perform>(&mut self, performer: &mut P, byte: u8) { + // Utf8 characters are handled out-of-band. + if let State::Utf8 = self.state { + self.process_utf8(performer, byte); + return; + } + + // Handle state changes in the anywhere state before evaluating changes + // for current state. + let mut change = table::STATE_CHANGES[State::Anywhere as usize][byte as usize]; + + if change == 0 { + change = table::STATE_CHANGES[self.state as usize][byte as usize]; + } + + // Unpack into a state and action + let (state, action) = unpack(change); + + self.perform_state_change(performer, state, action, byte); + } + + #[inline] + fn process_utf8<P>(&mut self, performer: &mut P, byte: u8) + where + P: Perform, + { + let mut receiver = VtUtf8Receiver(performer, &mut self.state); + let utf8_parser = &mut self.utf8_parser; + utf8_parser.advance(&mut receiver, byte); + } + + #[inline] + fn perform_state_change<P>(&mut self, performer: &mut P, state: State, action: Action, byte: u8) + where + P: Perform, + { + macro_rules! maybe_action { + ($action:expr, $arg:expr) => { + match $action { + Action::None => (), + action => { + self.perform_action(performer, action, $arg); + }, + } + }; + } + + match state { + State::Anywhere => { + // Just run the action + self.perform_action(performer, action, byte); + }, + state => { + match self.state { + State::DcsPassthrough => { + self.perform_action(performer, Action::Unhook, byte); + }, + State::OscString => { + self.perform_action(performer, Action::OscEnd, byte); + }, + _ => (), + } + + maybe_action!(action, byte); + + match state { + State::CsiEntry | State::DcsEntry | State::Escape => { + self.perform_action(performer, Action::Clear, byte); + }, + State::DcsPassthrough => { + self.perform_action(performer, Action::Hook, byte); + }, + State::OscString => { + self.perform_action(performer, Action::OscStart, byte); + }, + _ => (), + } + + // Assume the new state + self.state = state; + }, + } + } + + /// Separate method for osc_dispatch that borrows self as read-only + /// + /// The aliasing is needed here for multiple slices into self.osc_raw + #[inline] + fn osc_dispatch<P: Perform>(&self, performer: &mut P, byte: u8) { + let mut slices: [MaybeUninit<&[u8]>; MAX_OSC_PARAMS] = + unsafe { MaybeUninit::uninit().assume_init() }; + + for (i, slice) in slices.iter_mut().enumerate().take(self.osc_num_params) { + let indices = self.osc_params[i]; + *slice = MaybeUninit::new(&self.osc_raw[indices.0..indices.1]); + } + + unsafe { + let num_params = self.osc_num_params; + let params = &slices[..num_params] as *const [MaybeUninit<&[u8]>] as *const [&[u8]]; + performer.osc_dispatch(&*params, byte == 0x07); + } + } + + #[inline] + fn perform_action<P: Perform>(&mut self, performer: &mut P, action: Action, byte: u8) { + match action { + Action::Print => performer.print(byte as char), + Action::Execute => performer.execute(byte), + Action::Hook => { + if self.params.is_full() { + self.ignoring = true; + } else { + self.params.push(self.param); + } + + performer.hook(self.params(), self.intermediates(), self.ignoring, byte as char); + }, + Action::Put => performer.put(byte), + Action::OscStart => { + self.osc_raw.clear(); + self.osc_num_params = 0; + }, + Action::OscPut => { + #[cfg(feature = "no_std")] + { + if self.osc_raw.is_full() { + return; + } + } + + let idx = self.osc_raw.len(); + + // Param separator + if byte == b';' { + let param_idx = self.osc_num_params; + match param_idx { + // Only process up to MAX_OSC_PARAMS + MAX_OSC_PARAMS => return, + + // First param is special - 0 to current byte index + 0 => { + self.osc_params[param_idx] = (0, idx); + }, + + // All other params depend on previous indexing + _ => { + let prev = self.osc_params[param_idx - 1]; + let begin = prev.1; + self.osc_params[param_idx] = (begin, idx); + }, + } + + self.osc_num_params += 1; + } else { + self.osc_raw.push(byte); + } + }, + Action::OscEnd => { + let param_idx = self.osc_num_params; + let idx = self.osc_raw.len(); + + match param_idx { + // Finish last parameter if not already maxed + MAX_OSC_PARAMS => (), + + // First param is special - 0 to current byte index + 0 => { + self.osc_params[param_idx] = (0, idx); + self.osc_num_params += 1; + }, + + // All other params depend on previous indexing + _ => { + let prev = self.osc_params[param_idx - 1]; + let begin = prev.1; + self.osc_params[param_idx] = (begin, idx); + self.osc_num_params += 1; + }, + } + self.osc_dispatch(performer, byte); + }, + Action::Unhook => performer.unhook(), + Action::CsiDispatch => { + if self.params.is_full() { + self.ignoring = true; + } else { + self.params.push(self.param); + } + + performer.csi_dispatch( + self.params(), + self.intermediates(), + self.ignoring, + byte as char, + ); + }, + Action::EscDispatch => { + performer.esc_dispatch(self.intermediates(), self.ignoring, byte) + }, + Action::Collect => { + if self.intermediate_idx == MAX_INTERMEDIATES { + self.ignoring = true; + } else { + self.intermediates[self.intermediate_idx] = byte; + self.intermediate_idx += 1; + } + }, + Action::Param => { + if self.params.is_full() { + self.ignoring = true; + return; + } + + if byte == b';' { + self.params.push(self.param); + self.param = 0; + } else if byte == b':' { + self.params.extend(self.param); + self.param = 0; + } else { + // Continue collecting bytes into param + self.param = self.param.saturating_mul(10); + self.param = self.param.saturating_add((byte - b'0') as u16); + } + }, + Action::Clear => { + // Reset everything on ESC/CSI/DCS entry + self.intermediate_idx = 0; + self.ignoring = false; + self.param = 0; + + self.params.clear(); + }, + Action::BeginUtf8 => self.process_utf8(performer, byte), + Action::Ignore => (), + Action::None => (), + } + } +} + +/// Performs actions requested by the Parser +/// +/// Actions in this case mean, for example, handling a CSI escape sequence describing cursor +/// movement, or simply printing characters to the screen. +/// +/// The methods on this type correspond to actions described in +/// http://vt100.net/emu/dec_ansi_parser. I've done my best to describe them in +/// a useful way in my own words for completeness, but the site should be +/// referenced if something isn't clear. If the site disappears at some point in +/// the future, consider checking archive.org. +pub trait Perform { + /// Draw a character to the screen and update states. + fn print(&mut self, _c: char) {} + + /// Execute a C0 or C1 control function. + fn execute(&mut self, _byte: u8) {} + + /// Invoked when a final character arrives in first part of device control string. + /// + /// The control function should be determined from the private marker, final character, and + /// execute with a parameter list. A handler should be selected for remaining characters in the + /// string; the handler function should subsequently be called by `put` for every character in + /// the control string. + /// + /// The `ignore` flag indicates that more than two intermediates arrived and + /// subsequent characters were ignored. + fn hook(&mut self, _params: &Params, _intermediates: &[u8], _ignore: bool, _action: char) {} + + /// Pass bytes as part of a device control string to the handle chosen in `hook`. C0 controls + /// will also be passed to the handler. + fn put(&mut self, _byte: u8) {} + + /// Called when a device control string is terminated. + /// + /// The previously selected handler should be notified that the DCS has + /// terminated. + fn unhook(&mut self) {} + + /// Dispatch an operating system command. + fn osc_dispatch(&mut self, _params: &[&[u8]], _bell_terminated: bool) {} + + /// A final character has arrived for a CSI sequence + /// + /// The `ignore` flag indicates that either more than two intermediates arrived + /// or the number of parameters exceeded the maximum supported length, + /// and subsequent characters were ignored. + fn csi_dispatch( + &mut self, + _params: &Params, + _intermediates: &[u8], + _ignore: bool, + _action: char, + ) { + } + + /// The final character of an escape sequence has arrived. + /// + /// The `ignore` flag indicates that more than two intermediates arrived and + /// subsequent characters were ignored. + fn esc_dispatch(&mut self, _intermediates: &[u8], _ignore: bool, _byte: u8) {} +} + +#[cfg(all(test, feature = "no_std"))] +#[macro_use] +extern crate std; + +#[cfg(test)] +mod tests { + use super::*; + + use std::string::String; + use std::vec::Vec; + + static OSC_BYTES: &[u8] = &[ + 0x1b, 0x5d, // Begin OSC + b'2', b';', b'j', b'w', b'i', b'l', b'm', b'@', b'j', b'w', b'i', b'l', b'm', b'-', b'd', + b'e', b's', b'k', b':', b' ', b'~', b'/', b'c', b'o', b'd', b'e', b'/', b'a', b'l', b'a', + b'c', b'r', b'i', b't', b't', b'y', 0x07, // End OSC + ]; + + #[derive(Default)] + struct Dispatcher { + dispatched: Vec<Sequence>, + } + + #[derive(Debug, PartialEq, Eq)] + enum Sequence { + Osc(Vec<Vec<u8>>, bool), + Csi(Vec<Vec<u16>>, Vec<u8>, bool, char), + Esc(Vec<u8>, bool, u8), + DcsHook(Vec<Vec<u16>>, Vec<u8>, bool, char), + DcsPut(u8), + DcsUnhook, + } + + impl Perform for Dispatcher { + fn osc_dispatch(&mut self, params: &[&[u8]], bell_terminated: bool) { + let params = params.iter().map(|p| p.to_vec()).collect(); + self.dispatched.push(Sequence::Osc(params, bell_terminated)); + } + + fn csi_dispatch(&mut self, params: &Params, intermediates: &[u8], ignore: bool, c: char) { + let params = params.iter().map(|subparam| subparam.to_vec()).collect(); + let intermediates = intermediates.to_vec(); + self.dispatched.push(Sequence::Csi(params, intermediates, ignore, c)); + } + + fn esc_dispatch(&mut self, intermediates: &[u8], ignore: bool, byte: u8) { + let intermediates = intermediates.to_vec(); + self.dispatched.push(Sequence::Esc(intermediates, ignore, byte)); + } + + fn hook(&mut self, params: &Params, intermediates: &[u8], ignore: bool, c: char) { + let params = params.iter().map(|subparam| subparam.to_vec()).collect(); + let intermediates = intermediates.to_vec(); + self.dispatched.push(Sequence::DcsHook(params, intermediates, ignore, c)); + } + + fn put(&mut self, byte: u8) { + self.dispatched.push(Sequence::DcsPut(byte)); + } + + fn unhook(&mut self) { + self.dispatched.push(Sequence::DcsUnhook); + } + } + + #[test] + fn parse_osc() { + let mut dispatcher = Dispatcher::default(); + let mut parser = Parser::new(); + + for byte in OSC_BYTES { + parser.advance(&mut dispatcher, *byte); + } + + assert_eq!(dispatcher.dispatched.len(), 1); + match &dispatcher.dispatched[0] { + Sequence::Osc(params, _) => { + assert_eq!(params.len(), 2); + assert_eq!(params[0], &OSC_BYTES[2..3]); + assert_eq!(params[1], &OSC_BYTES[4..(OSC_BYTES.len() - 1)]); + }, + _ => panic!("expected osc sequence"), + } + } + + #[test] + fn parse_empty_osc() { + let mut dispatcher = Dispatcher::default(); + let mut parser = Parser::new(); + + for byte in &[0x1b, 0x5d, 0x07] { + parser.advance(&mut dispatcher, *byte); + } + + assert_eq!(dispatcher.dispatched.len(), 1); + match &dispatcher.dispatched[0] { + Sequence::Osc(..) => (), + _ => panic!("expected osc sequence"), + } + } + + #[test] + fn parse_osc_max_params() { + let params = std::iter::repeat(";").take(params::MAX_PARAMS + 1).collect::<String>(); + let input = format!("\x1b]{}\x1b", ¶ms[..]).into_bytes(); + let mut dispatcher = Dispatcher::default(); + let mut parser = Parser::new(); + + for byte in input { + parser.advance(&mut dispatcher, byte); + } + + assert_eq!(dispatcher.dispatched.len(), 1); + match &dispatcher.dispatched[0] { + Sequence::Osc(params, _) => { + assert_eq!(params.len(), MAX_OSC_PARAMS); + assert!(params.iter().all(Vec::is_empty)); + }, + _ => panic!("expected osc sequence"), + } + } + + #[test] + fn osc_bell_terminated() { + static INPUT: &[u8] = b"\x1b]11;ff/00/ff\x07"; + let mut dispatcher = Dispatcher::default(); + let mut parser = Parser::new(); + + for byte in INPUT { + parser.advance(&mut dispatcher, *byte); + } + + assert_eq!(dispatcher.dispatched.len(), 1); + match &dispatcher.dispatched[0] { + Sequence::Osc(_, true) => (), + _ => panic!("expected osc with bell terminator"), + } + } + + #[test] + fn osc_c0_st_terminated() { + static INPUT: &[u8] = b"\x1b]11;ff/00/ff\x1b\\"; + let mut dispatcher = Dispatcher::default(); + let mut parser = Parser::new(); + + for byte in INPUT { + parser.advance(&mut dispatcher, *byte); + } + + assert_eq!(dispatcher.dispatched.len(), 2); + match &dispatcher.dispatched[0] { + Sequence::Osc(_, false) => (), + _ => panic!("expected osc with ST terminator"), + } + } + + #[test] + fn parse_osc_with_utf8_arguments() { + static INPUT: &[u8] = &[ + 0x0d, 0x1b, 0x5d, 0x32, 0x3b, 0x65, 0x63, 0x68, 0x6f, 0x20, 0x27, 0xc2, 0xaf, 0x5c, + 0x5f, 0x28, 0xe3, 0x83, 0x84, 0x29, 0x5f, 0x2f, 0xc2, 0xaf, 0x27, 0x20, 0x26, 0x26, + 0x20, 0x73, 0x6c, 0x65, 0x65, 0x70, 0x20, 0x31, 0x07, + ]; + let mut dispatcher = Dispatcher::default(); + let mut parser = Parser::new(); + + for byte in INPUT { + parser.advance(&mut dispatcher, *byte); + } + + assert_eq!(dispatcher.dispatched.len(), 1); + match &dispatcher.dispatched[0] { + Sequence::Osc(params, _) => { + assert_eq!(params[0], &[b'2']); + assert_eq!(params[1], &INPUT[5..(INPUT.len() - 1)]); + }, + _ => panic!("expected osc sequence"), + } + } + + #[test] + fn osc_containing_string_terminator() { + static INPUT: &[u8] = b"\x1b]2;\xe6\x9c\xab\x1b\\"; + let mut dispatcher = Dispatcher::default(); + let mut parser = Parser::new(); + + for byte in INPUT { + parser.advance(&mut dispatcher, *byte); + } + + assert_eq!(dispatcher.dispatched.len(), 2); + match &dispatcher.dispatched[0] { + Sequence::Osc(params, _) => { + assert_eq!(params[1], &INPUT[4..(INPUT.len() - 2)]); + }, + _ => panic!("expected osc sequence"), + } + } + + #[test] + fn exceed_max_buffer_size() { + static NUM_BYTES: usize = MAX_OSC_RAW + 100; + static INPUT_START: &[u8] = &[0x1b, b']', b'5', b'2', b';', b's']; + static INPUT_END: &[u8] = &[b'\x07']; + + let mut dispatcher = Dispatcher::default(); + let mut parser = Parser::new(); + + // Create valid OSC escape + for byte in INPUT_START { + parser.advance(&mut dispatcher, *byte); + } + + // Exceed max buffer size + for _ in 0..NUM_BYTES { + parser.advance(&mut dispatcher, b'a'); + } + + // Terminate escape for dispatch + for byte in INPUT_END { + parser.advance(&mut dispatcher, *byte); + } + + assert_eq!(dispatcher.dispatched.len(), 1); + match &dispatcher.dispatched[0] { + Sequence::Osc(params, _) => { + assert_eq!(params.len(), 2); + assert_eq!(params[0], b"52"); + + #[cfg(not(feature = "no_std"))] + assert_eq!(params[1].len(), NUM_BYTES + INPUT_END.len()); + + #[cfg(feature = "no_std")] + assert_eq!(params[1].len(), MAX_OSC_RAW - params[0].len()); + }, + _ => panic!("expected osc sequence"), + } + } + + #[test] + fn parse_csi_max_params() { + // This will build a list of repeating '1;'s + // The length is MAX_PARAMS - 1 because the last semicolon is interpreted + // as an implicit zero, making the total number of parameters MAX_PARAMS + let params = std::iter::repeat("1;").take(params::MAX_PARAMS - 1).collect::<String>(); + let input = format!("\x1b[{}p", ¶ms[..]).into_bytes(); + + let mut dispatcher = Dispatcher::default(); + let mut parser = Parser::new(); + + for byte in input { + parser.advance(&mut dispatcher, byte); + } + + assert_eq!(dispatcher.dispatched.len(), 1); + match &dispatcher.dispatched[0] { + Sequence::Csi(params, _, ignore, _) => { + assert_eq!(params.len(), params::MAX_PARAMS); + assert!(!ignore); + }, + _ => panic!("expected csi sequence"), + } + } + + #[test] + fn parse_csi_params_ignore_long_params() { + // This will build a list of repeating '1;'s + // The length is MAX_PARAMS because the last semicolon is interpreted + // as an implicit zero, making the total number of parameters MAX_PARAMS + 1 + let params = std::iter::repeat("1;").take(params::MAX_PARAMS).collect::<String>(); + let input = format!("\x1b[{}p", ¶ms[..]).into_bytes(); + + let mut dispatcher = Dispatcher::default(); + let mut parser = Parser::new(); + + for byte in input { + parser.advance(&mut dispatcher, byte); + } + + assert_eq!(dispatcher.dispatched.len(), 1); + match &dispatcher.dispatched[0] { + Sequence::Csi(params, _, ignore, _) => { + assert_eq!(params.len(), params::MAX_PARAMS); + assert!(ignore); + }, + _ => panic!("expected csi sequence"), + } + } + + #[test] + fn parse_csi_params_trailing_semicolon() { + let mut dispatcher = Dispatcher::default(); + let mut parser = Parser::new(); + + for byte in b"\x1b[4;m" { + parser.advance(&mut dispatcher, *byte); + } + + assert_eq!(dispatcher.dispatched.len(), 1); + match &dispatcher.dispatched[0] { + Sequence::Csi(params, ..) => assert_eq!(params, &[[4], [0]]), + _ => panic!("expected csi sequence"), + } + } + + #[test] + fn parse_csi_params_leading_semicolon() { + // Create dispatcher and check state + let mut dispatcher = Dispatcher::default(); + let mut parser = Parser::new(); + + for byte in b"\x1b[;4m" { + parser.advance(&mut dispatcher, *byte); + } + + assert_eq!(dispatcher.dispatched.len(), 1); + match &dispatcher.dispatched[0] { + Sequence::Csi(params, ..) => assert_eq!(params, &[[0], [4]]), + _ => panic!("expected csi sequence"), + } + } + + #[test] + fn parse_long_csi_param() { + // The important part is the parameter, which is (i64::MAX + 1) + static INPUT: &[u8] = b"\x1b[9223372036854775808m"; + let mut dispatcher = Dispatcher::default(); + let mut parser = Parser::new(); + + for byte in INPUT { + parser.advance(&mut dispatcher, *byte); + } + + assert_eq!(dispatcher.dispatched.len(), 1); + match &dispatcher.dispatched[0] { + Sequence::Csi(params, ..) => assert_eq!(params, &[[std::u16::MAX as u16]]), + _ => panic!("expected csi sequence"), + } + } + + #[test] + fn csi_reset() { + static INPUT: &[u8] = b"\x1b[3;1\x1b[?1049h"; + let mut dispatcher = Dispatcher::default(); + let mut parser = Parser::new(); + + for byte in INPUT { + parser.advance(&mut dispatcher, *byte); + } + + assert_eq!(dispatcher.dispatched.len(), 1); + match &dispatcher.dispatched[0] { + Sequence::Csi(params, intermediates, ignore, _) => { + assert_eq!(intermediates, &[b'?']); + assert_eq!(params, &[[1049]]); + assert!(!ignore); + }, + _ => panic!("expected csi sequence"), + } + } + + #[test] + fn csi_subparameters() { + static INPUT: &[u8] = b"\x1b[38:2:255:0:255;1m"; + let mut dispatcher = Dispatcher::default(); + let mut parser = Parser::new(); + + for byte in INPUT { + parser.advance(&mut dispatcher, *byte); + } + + assert_eq!(dispatcher.dispatched.len(), 1); + match &dispatcher.dispatched[0] { + Sequence::Csi(params, intermediates, ignore, _) => { + assert_eq!(params, &[vec![38, 2, 255, 0, 255], vec![1]]); + assert_eq!(intermediates, &[]); + assert!(!ignore); + }, + _ => panic!("expected csi sequence"), + } + } + + #[test] + fn parse_dcs_max_params() { + let params = std::iter::repeat("1;").take(params::MAX_PARAMS + 1).collect::<String>(); + let input = format!("\x1bP{}p", ¶ms[..]).into_bytes(); + let mut dispatcher = Dispatcher::default(); + let mut parser = Parser::new(); + + for byte in input { + parser.advance(&mut dispatcher, byte); + } + + assert_eq!(dispatcher.dispatched.len(), 1); + match &dispatcher.dispatched[0] { + Sequence::DcsHook(params, _, ignore, _) => { + assert_eq!(params.len(), params::MAX_PARAMS); + assert!(params.iter().all(|param| param == &[1])); + assert!(ignore); + }, + _ => panic!("expected dcs sequence"), + } + } + + #[test] + fn dcs_reset() { + static INPUT: &[u8] = b"\x1b[3;1\x1bP1$tx\x9c"; + let mut dispatcher = Dispatcher::default(); + let mut parser = Parser::new(); + + for byte in INPUT { + parser.advance(&mut dispatcher, *byte); + } + + assert_eq!(dispatcher.dispatched.len(), 3); + match &dispatcher.dispatched[0] { + Sequence::DcsHook(params, intermediates, ignore, _) => { + assert_eq!(intermediates, &[b'$']); + assert_eq!(params, &[[1]]); + assert!(!ignore); + }, + _ => panic!("expected dcs sequence"), + } + assert_eq!(dispatcher.dispatched[1], Sequence::DcsPut(b'x')); + assert_eq!(dispatcher.dispatched[2], Sequence::DcsUnhook); + } + + #[test] + fn parse_dcs() { + static INPUT: &[u8] = b"\x1bP0;1|17/ab\x9c"; + let mut dispatcher = Dispatcher::default(); + let mut parser = Parser::new(); + + for byte in INPUT { + parser.advance(&mut dispatcher, *byte); + } + + assert_eq!(dispatcher.dispatched.len(), 7); + match &dispatcher.dispatched[0] { + Sequence::DcsHook(params, _, _, c) => { + assert_eq!(params, &[[0], [1]]); + assert_eq!(c, &'|'); + }, + _ => panic!("expected dcs sequence"), + } + for (i, byte) in b"17/ab".iter().enumerate() { + assert_eq!(dispatcher.dispatched[1 + i], Sequence::DcsPut(*byte)); + } + assert_eq!(dispatcher.dispatched[6], Sequence::DcsUnhook); + } + + #[test] + fn intermediate_reset_on_dcs_exit() { + static INPUT: &[u8] = b"\x1bP=1sZZZ\x1b+\x5c"; + let mut dispatcher = Dispatcher::default(); + let mut parser = Parser::new(); + + for byte in INPUT { + parser.advance(&mut dispatcher, *byte); + } + + assert_eq!(dispatcher.dispatched.len(), 6); + match &dispatcher.dispatched[5] { + Sequence::Esc(intermediates, ..) => assert_eq!(intermediates, &[b'+']), + _ => panic!("expected esc sequence"), + } + } + + #[test] + fn esc_reset() { + static INPUT: &[u8] = b"\x1b[3;1\x1b(A"; + let mut dispatcher = Dispatcher::default(); + let mut parser = Parser::new(); + + for byte in INPUT { + parser.advance(&mut dispatcher, *byte); + } + + assert_eq!(dispatcher.dispatched.len(), 1); + match &dispatcher.dispatched[0] { + Sequence::Esc(intermediates, ignore, byte) => { + assert_eq!(intermediates, &[b'(']); + assert_eq!(*byte, b'A'); + assert!(!ignore); + }, + _ => panic!("expected esc sequence"), + } + } +} + +#[cfg(all(feature = "nightly", test))] +mod bench { + extern crate std; + extern crate test; + + use super::*; + + use test::{black_box, Bencher}; + + static VTE_DEMO: &[u8] = include_bytes!("../tests/demo.vte"); + + struct BenchDispatcher; + impl Perform for BenchDispatcher { + fn print(&mut self, c: char) { + black_box(c); + } + + fn execute(&mut self, byte: u8) { + black_box(byte); + } + + fn hook(&mut self, params: &Params, intermediates: &[u8], ignore: bool, c: char) { + black_box((params, intermediates, ignore, c)); + } + + fn put(&mut self, byte: u8) { + black_box(byte); + } + + fn osc_dispatch(&mut self, params: &[&[u8]], bell_terminated: bool) { + black_box((params, bell_terminated)); + } + + fn csi_dispatch(&mut self, params: &Params, intermediates: &[u8], ignore: bool, c: char) { + black_box((params, intermediates, ignore, c)); + } + + fn esc_dispatch(&mut self, intermediates: &[u8], ignore: bool, byte: u8) { + black_box((intermediates, ignore, byte)); + } + } + + #[bench] + fn testfile(b: &mut Bencher) { + b.iter(|| { + let mut dispatcher = BenchDispatcher; + let mut parser = Parser::new(); + + for byte in VTE_DEMO { + parser.advance(&mut dispatcher, *byte); + } + }); + } + + #[bench] + fn state_changes(b: &mut Bencher) { + let input = b"\x1b]2;X\x1b\\ \x1b[0m \x1bP0@\x1b\\"; + b.iter(|| { + let mut dispatcher = BenchDispatcher; + let mut parser = Parser::new(); + + for _ in 0..1_000 { + for byte in input { + parser.advance(&mut dispatcher, *byte); + } + } + }); + } +} diff --git a/vendor/vte/src/params.rs b/vendor/vte/src/params.rs new file mode 100644 index 0000000..ca6ba48 --- /dev/null +++ b/vendor/vte/src/params.rs @@ -0,0 +1,142 @@ +//! Fixed size parameters list with optional subparameters. + +use core::fmt::{self, Debug, Formatter}; + +pub(crate) const MAX_PARAMS: usize = 32; + +#[derive(Default)] +pub struct Params { + /// Number of subparameters for each parameter. + /// + /// For each entry in the `params` slice, this stores the length of the param as number of + /// subparams at the same index as the param in the `params` slice. + /// + /// At the subparam positions the length will always be `0`. + subparams: [u8; MAX_PARAMS], + + /// All parameters and subparameters. + params: [u16; MAX_PARAMS], + + /// Number of suparameters in the current parameter. + current_subparams: u8, + + /// Total number of parameters and subparameters. + len: usize, +} + +impl Params { + /// Returns the number of parameters. + #[inline] + pub fn len(&self) -> usize { + self.len + } + + /// Returns `true` if there are no parameters present. + #[inline] + pub fn is_empty(&self) -> bool { + self.len == 0 + } + + /// Returns an iterator over all parameters and subparameters. + #[inline] + pub fn iter(&self) -> ParamsIter<'_> { + ParamsIter::new(self) + } + + /// Returns `true` if there is no more space for additional parameters. + #[inline] + pub(crate) fn is_full(&self) -> bool { + self.len == MAX_PARAMS + } + + /// Clear all parameters. + #[inline] + pub(crate) fn clear(&mut self) { + self.current_subparams = 0; + self.len = 0; + } + + /// Add an additional parameter. + #[inline] + pub(crate) fn push(&mut self, item: u16) { + self.subparams[self.len - self.current_subparams as usize] = self.current_subparams + 1; + self.params[self.len] = item; + self.current_subparams = 0; + self.len += 1; + } + + /// Add an additional subparameter to the current parameter. + #[inline] + pub(crate) fn extend(&mut self, item: u16) { + self.params[self.len] = item; + self.current_subparams += 1; + self.len += 1; + } +} + +impl<'a> IntoIterator for &'a Params { + type IntoIter = ParamsIter<'a>; + type Item = &'a [u16]; + + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + +/// Immutable subparameter iterator. +pub struct ParamsIter<'a> { + params: &'a Params, + index: usize, +} + +impl<'a> ParamsIter<'a> { + fn new(params: &'a Params) -> Self { + Self { params, index: 0 } + } +} + +impl<'a> Iterator for ParamsIter<'a> { + type Item = &'a [u16]; + + fn next(&mut self) -> Option<Self::Item> { + if self.index >= self.params.len() { + return None; + } + + // Get all subparameters for the current parameter. + let num_subparams = self.params.subparams[self.index]; + let param = &self.params.params[self.index..self.index + num_subparams as usize]; + + // Jump to the next parameter. + self.index += num_subparams as usize; + + Some(param) + } + + fn size_hint(&self) -> (usize, Option<usize>) { + let remaining = self.params.len() - self.index; + (remaining, Some(remaining)) + } +} + +impl Debug for Params { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "[")?; + + for (i, param) in self.iter().enumerate() { + if i != 0 { + write!(f, ";")?; + } + + for (i, subparam) in param.iter().enumerate() { + if i != 0 { + write!(f, ":")?; + } + + subparam.fmt(f)?; + } + } + + write!(f, "]") + } +} diff --git a/vendor/vte/src/table.rs b/vendor/vte/src/table.rs new file mode 100644 index 0000000..f2c0105 --- /dev/null +++ b/vendor/vte/src/table.rs @@ -0,0 +1,171 @@ +/// This is the state change table. It's indexed first by current state and then by the next +/// character in the pty stream. +use crate::definitions::{pack, Action, State}; + +use vte_generate_state_changes::generate_state_changes; + +// Generate state changes at compile-time +pub static STATE_CHANGES: [[u8; 256]; 16] = state_changes(); +generate_state_changes!(state_changes, { + Anywhere { + 0x18 => (Ground, Execute), + 0x1a => (Ground, Execute), + 0x1b => (Escape, None), + }, + + Ground { + 0x00..=0x17 => (Anywhere, Execute), + 0x19 => (Anywhere, Execute), + 0x1c..=0x1f => (Anywhere, Execute), + 0x20..=0x7f => (Anywhere, Print), + 0x80..=0x8f => (Anywhere, Execute), + 0x91..=0x9a => (Anywhere, Execute), + 0x9c => (Anywhere, Execute), + // Beginning of UTF-8 2 byte sequence + 0xc2..=0xdf => (Utf8, BeginUtf8), + // Beginning of UTF-8 3 byte sequence + 0xe0..=0xef => (Utf8, BeginUtf8), + // Beginning of UTF-8 4 byte sequence + 0xf0..=0xf4 => (Utf8, BeginUtf8), + }, + + Escape { + 0x00..=0x17 => (Anywhere, Execute), + 0x19 => (Anywhere, Execute), + 0x1c..=0x1f => (Anywhere, Execute), + 0x7f => (Anywhere, Ignore), + 0x20..=0x2f => (EscapeIntermediate, Collect), + 0x30..=0x4f => (Ground, EscDispatch), + 0x51..=0x57 => (Ground, EscDispatch), + 0x59 => (Ground, EscDispatch), + 0x5a => (Ground, EscDispatch), + 0x5c => (Ground, EscDispatch), + 0x60..=0x7e => (Ground, EscDispatch), + 0x5b => (CsiEntry, None), + 0x5d => (OscString, None), + 0x50 => (DcsEntry, None), + 0x58 => (SosPmApcString, None), + 0x5e => (SosPmApcString, None), + 0x5f => (SosPmApcString, None), + }, + + EscapeIntermediate { + 0x00..=0x17 => (Anywhere, Execute), + 0x19 => (Anywhere, Execute), + 0x1c..=0x1f => (Anywhere, Execute), + 0x20..=0x2f => (Anywhere, Collect), + 0x7f => (Anywhere, Ignore), + 0x30..=0x7e => (Ground, EscDispatch), + }, + + CsiEntry { + 0x00..=0x17 => (Anywhere, Execute), + 0x19 => (Anywhere, Execute), + 0x1c..=0x1f => (Anywhere, Execute), + 0x7f => (Anywhere, Ignore), + 0x20..=0x2f => (CsiIntermediate, Collect), + 0x30..=0x39 => (CsiParam, Param), + 0x3a..=0x3b => (CsiParam, Param), + 0x3c..=0x3f => (CsiParam, Collect), + 0x40..=0x7e => (Ground, CsiDispatch), + }, + + CsiIgnore { + 0x00..=0x17 => (Anywhere, Execute), + 0x19 => (Anywhere, Execute), + 0x1c..=0x1f => (Anywhere, Execute), + 0x20..=0x3f => (Anywhere, Ignore), + 0x7f => (Anywhere, Ignore), + 0x40..=0x7e => (Ground, None), + }, + + CsiParam { + 0x00..=0x17 => (Anywhere, Execute), + 0x19 => (Anywhere, Execute), + 0x1c..=0x1f => (Anywhere, Execute), + 0x30..=0x39 => (Anywhere, Param), + 0x3a..=0x3b => (Anywhere, Param), + 0x7f => (Anywhere, Ignore), + 0x3c..=0x3f => (CsiIgnore, None), + 0x20..=0x2f => (CsiIntermediate, Collect), + 0x40..=0x7e => (Ground, CsiDispatch), + }, + + CsiIntermediate { + 0x00..=0x17 => (Anywhere, Execute), + 0x19 => (Anywhere, Execute), + 0x1c..=0x1f => (Anywhere, Execute), + 0x20..=0x2f => (Anywhere, Collect), + 0x7f => (Anywhere, Ignore), + 0x30..=0x3f => (CsiIgnore, None), + 0x40..=0x7e => (Ground, CsiDispatch), + }, + + DcsEntry { + 0x00..=0x17 => (Anywhere, Ignore), + 0x19 => (Anywhere, Ignore), + 0x1c..=0x1f => (Anywhere, Ignore), + 0x7f => (Anywhere, Ignore), + 0x20..=0x2f => (DcsIntermediate, Collect), + 0x30..=0x39 => (DcsParam, Param), + 0x3a..=0x3b => (DcsParam, Param), + 0x3c..=0x3f => (DcsParam, Collect), + 0x40..=0x7e => (DcsPassthrough, None), + }, + + DcsIntermediate { + 0x00..=0x17 => (Anywhere, Ignore), + 0x19 => (Anywhere, Ignore), + 0x1c..=0x1f => (Anywhere, Ignore), + 0x20..=0x2f => (Anywhere, Collect), + 0x7f => (Anywhere, Ignore), + 0x30..=0x3f => (DcsIgnore, None), + 0x40..=0x7e => (DcsPassthrough, None), + }, + + DcsIgnore { + 0x00..=0x17 => (Anywhere, Ignore), + 0x19 => (Anywhere, Ignore), + 0x1c..=0x1f => (Anywhere, Ignore), + 0x20..=0x7f => (Anywhere, Ignore), + 0x9c => (Ground, None), + }, + + DcsParam { + 0x00..=0x17 => (Anywhere, Ignore), + 0x19 => (Anywhere, Ignore), + 0x1c..=0x1f => (Anywhere, Ignore), + 0x30..=0x39 => (Anywhere, Param), + 0x3a..=0x3b => (Anywhere, Param), + 0x7f => (Anywhere, Ignore), + 0x3c..=0x3f => (DcsIgnore, None), + 0x20..=0x2f => (DcsIntermediate, Collect), + 0x40..=0x7e => (DcsPassthrough, None), + }, + + DcsPassthrough { + 0x00..=0x17 => (Anywhere, Put), + 0x19 => (Anywhere, Put), + 0x1c..=0x1f => (Anywhere, Put), + 0x20..=0x7e => (Anywhere, Put), + 0x7f => (Anywhere, Ignore), + 0x9c => (Ground, None), + }, + + SosPmApcString { + 0x00..=0x17 => (Anywhere, Ignore), + 0x19 => (Anywhere, Ignore), + 0x1c..=0x1f => (Anywhere, Ignore), + 0x20..=0x7f => (Anywhere, Ignore), + 0x9c => (Ground, None), + }, + + OscString { + 0x00..=0x06 => (Anywhere, Ignore), + 0x07 => (Ground, None), + 0x08..=0x17 => (Anywhere, Ignore), + 0x19 => (Anywhere, Ignore), + 0x1c..=0x1f => (Anywhere, Ignore), + 0x20..=0xff => (Anywhere, OscPut), + } +}); diff --git a/vendor/vte/tests/demo.vte b/vendor/vte/tests/demo.vte new file mode 100644 index 0000000..ed62b35 --- /dev/null +++ b/vendor/vte/tests/demo.vte @@ -0,0 +1,2052 @@ +Test +[31;42mRED ON GREEN +]52;c;Y2xpcGJvYXJkIHRlc3Q=];;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;some random text; what +[1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;p[4;m +[;4m +[123123123123123123123123123123123123mP0;1|17/ab +
]2;echo '¯\_(ツ)_/¯' && sleep 1 +[31mREEEEEED +Test +[31;42mRED ON GREEN +]52;c;Y2xpcGJvYXJkIHRlc3Q=];;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;some random text; what +[1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;p[4;m +[;4m +[123123123123123123123123123123123123mP0;1|17/ab +
]2;echo '¯\_(ツ)_/¯' && sleep 1 +[31mREEEEEEEEEEEEED +[6 q +[0 q +[0m +[0H[2J + +0 +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 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +326 +327 +328 +329 +330 +331 +332 +333 +334 +335 +336 +337 +338 +339 +340 +341 +342 +343 +344 +345 +346 +347 +348 +349 +350 +351 +352 +353 +354 +355 +356 +357 +358 +359 +360 +361 +362 +363 +364 +365 +366 +367 +368 +369 +370 +371 +372 +373 +374 +375 +376 +377 +378 +379 +380 +381 +382 +383 +384 +385 +386 +387 +388 +389 +390 +391 +392 +393 +394 +395 +396 +397 +398 +399 +400 +401 +402 +403 +404 +405 +406 +407 +408 +409 +410 +411 +412 +413 +414 +415 +416 +417 +418 +419 +420 +421 +422 +423 +424 +425 +426 +427 +428 +429 +430 +431 +432 +433 +434 +435 +436 +437 +438 +439 +440 +441 +442 +443 +444 +445 +446 +447 +448 +449 +450 +451 +452 +453 +454 +455 +456 +457 +458 +459 +460 +461 +462 +463 +464 +465 +466 +467 +468 +469 +470 +471 +472 +473 +474 +475 +476 +477 +478 +479 +480 +481 +482 +483 +484 +485 +486 +487 +488 +489 +490 +491 +492 +493 +494 +495 +496 +497 +498 +499 +500 +501 +502 +503 +504 +505 +506 +507 +508 +509 +510 +511 +512 +513 +514 +515 +516 +517 +518 +519 +520 +521 +522 +523 +524 +525 +526 +527 +528 +529 +530 +531 +532 +533 +534 +535 +536 +537 +538 +539 +540 +541 +542 +543 +544 +545 +546 +547 +548 +549 +550 +551 +552 +553 +554 +555 +556 +557 +558 +559 +560 +561 +562 +563 +564 +565 +566 +567 +568 +569 +570 +571 +572 +573 +574 +575 +576 +577 +578 +579 +580 +581 +582 +583 +584 +585 +586 +587 +588 +589 +590 +591 +592 +593 +594 +595 +596 +597 +598 +599 +600 +601 +602 +603 +604 +605 +606 +607 +608 +609 +610 +611 +612 +613 +614 +615 +616 +617 +618 +619 +620 +621 +622 +623 +624 +625 +626 +627 +628 +629 +630 +631 +632 +633 +634 +635 +636 +637 +638 +639 +640 +641 +642 +643 +644 +645 +646 +647 +648 +649 +650 +651 +652 +653 +654 +655 +656 +657 +658 +659 +660 +661 +662 +663 +664 +665 +666 +667 +668 +669 +670 +671 +672 +673 +674 +675 +676 +677 +678 +679 +680 +681 +682 +683 +684 +685 +686 +687 +688 +689 +690 +691 +692 +693 +694 +695 +696 +697 +698 +699 +700 +701 +702 +703 +704 +705 +706 +707 +708 +709 +710 +711 +712 +713 +714 +715 +716 +717 +718 +719 +720 +721 +722 +723 +724 +725 +726 +727 +728 +729 +730 +731 +732 +733 +734 +735 +736 +737 +738 +739 +740 +741 +742 +743 +744 +745 +746 +747 +748 +749 +750 +751 +752 +753 +754 +755 +756 +757 +758 +759 +760 +761 +762 +763 +764 +765 +766 +767 +768 +769 +770 +771 +772 +773 +774 +775 +776 +777 +778 +779 +780 +781 +782 +783 +784 +785 +786 +787 +788 +789 +790 +791 +792 +793 +794 +795 +796 +797 +798 +799 +800 +801 +802 +803 +804 +805 +806 +807 +808 +809 +810 +811 +812 +813 +814 +815 +816 +817 +818 +819 +820 +821 +822 +823 +824 +825 +826 +827 +828 +829 +830 +831 +832 +833 +834 +835 +836 +837 +838 +839 +840 +841 +842 +843 +844 +845 +846 +847 +848 +849 +850 +851 +852 +853 +854 +855 +856 +857 +858 +859 +860 +861 +862 +863 +864 +865 +866 +867 +868 +869 +870 +871 +872 +873 +874 +875 +876 +877 +878 +879 +880 +881 +882 +883 +884 +885 +886 +887 +888 +889 +890 +891 +892 +893 +894 +895 +896 +897 +898 +899 +900 +901 +902 +903 +904 +905 +906 +907 +908 +909 +910 +911 +912 +913 +914 +915 +916 +917 +918 +919 +920 +921 +922 +923 +924 +925 +926 +927 +928 +929 +930 +931 +932 +933 +934 +935 +936 +937 +938 +939 +940 +941 +942 +943 +944 +945 +946 +947 +948 +949 +950 +951 +952 +953 +954 +955 +956 +957 +958 +959 +960 +961 +962 +963 +964 +965 +966 +967 +968 +969 +970 +971 +972 +973 +974 +975 +976 +977 +978 +979 +980 +981 +982 +983 +984 +985 +986 +987 +988 +989 +990 +991 +992 +993 +994 +995 +996 +997 +998 +999 +1000 + +[0 +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 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +326 +327 +328 +329 +330 +331 +332 +333 +334 +335 +336 +337 +338 +339 +340 +341 +342 +343 +344 +345 +346 +347 +348 +349 +350 +351 +352 +353 +354 +355 +356 +357 +358 +359 +360 +361 +362 +363 +364 +365 +366 +367 +368 +369 +370 +371 +372 +373 +374 +375 +376 +377 +378 +379 +380 +381 +382 +383 +384 +385 +386 +387 +388 +389 +390 +391 +392 +393 +394 +395 +396 +397 +398 +399 +400 +401 +402 +403 +404 +405 +406 +407 +408 +409 +410 +411 +412 +413 +414 +415 +416 +417 +418 +419 +420 +421 +422 +423 +424 +425 +426 +427 +428 +429 +430 +431 +432 +433 +434 +435 +436 +437 +438 +439 +440 +441 +442 +443 +444 +445 +446 +447 +448 +449 +450 +451 +452 +453 +454 +455 +456 +457 +458 +459 +460 +461 +462 +463 +464 +465 +466 +467 +468 +469 +470 +471 +472 +473 +474 +475 +476 +477 +478 +479 +480 +481 +482 +483 +484 +485 +486 +487 +488 +489 +490 +491 +492 +493 +494 +495 +496 +497 +498 +499 +500 +501 +502 +503 +504 +505 +506 +507 +508 +509 +510 +511 +512 +513 +514 +515 +516 +517 +518 +519 +520 +521 +522 +523 +524 +525 +526 +527 +528 +529 +530 +531 +532 +533 +534 +535 +536 +537 +538 +539 +540 +541 +542 +543 +544 +545 +546 +547 +548 +549 +550 +551 +552 +553 +554 +555 +556 +557 +558 +559 +560 +561 +562 +563 +564 +565 +566 +567 +568 +569 +570 +571 +572 +573 +574 +575 +576 +577 +578 +579 +580 +581 +582 +583 +584 +585 +586 +587 +588 +589 +590 +591 +592 +593 +594 +595 +596 +597 +598 +599 +600 +601 +602 +603 +604 +605 +606 +607 +608 +609 +610 +611 +612 +613 +614 +615 +616 +617 +618 +619 +620 +621 +622 +623 +624 +625 +626 +627 +628 +629 +630 +631 +632 +633 +634 +635 +636 +637 +638 +639 +640 +641 +642 +643 +644 +645 +646 +647 +648 +649 +650 +651 +652 +653 +654 +655 +656 +657 +658 +659 +660 +661 +662 +663 +664 +665 +666 +667 +668 +669 +670 +671 +672 +673 +674 +675 +676 +677 +678 +679 +680 +681 +682 +683 +684 +685 +686 +687 +688 +689 +690 +691 +692 +693 +694 +695 +696 +697 +698 +699 +700 +701 +702 +703 +704 +705 +706 +707 +708 +709 +710 +711 +712 +713 +714 +715 +716 +717 +718 +719 +720 +721 +722 +723 +724 +725 +726 +727 +728 +729 +730 +731 +732 +733 +734 +735 +736 +737 +738 +739 +740 +741 +742 +743 +744 +745 +746 +747 +748 +749 +750 +751 +752 +753 +754 +755 +756 +757 +758 +759 +760 +761 +762 +763 +764 +765 +766 +767 +768 +769 +770 +771 +772 +773 +774 +775 +776 +777 +778 +779 +780 +781 +782 +783 +784 +785 +786 +787 +788 +789 +790 +791 +792 +793 +794 +795 +796 +797 +798 +799 +800 +801 +802 +803 +804 +805 +806 +807 +808 +809 +810 +811 +812 +813 +814 +815 +816 +817 +818 +819 +820 +821 +822 +823 +824 +825 +826 +827 +828 +829 +830 +831 +832 +833 +834 +835 +836 +837 +838 +839 +840 +841 +842 +843 +844 +845 +846 +847 +848 +849 +850 +851 +852 +853 +854 +855 +856 +857 +858 +859 +860 +861 +862 +863 +864 +865 +866 +867 +868 +869 +870 +871 +872 +873 +874 +875 +876 +877 +878 +879 +880 +881 +882 +883 +884 +885 +886 +887 +888 +889 +890 +891 +892 +893 +894 +895 +896 +897 +898 +899 +900 +901 +902 +903 +904 +905 +906 +907 +908 +909 +910 +911 +912 +913 +914 +915 +916 +917 +918 +919 +920 +921 +922 +923 +924 +925 +926 +927 +928 +929 +930 +931 +932 +933 +934 +935 +936 +937 +938 +939 +940 +941 +942 +943 +944 +945 +946 +947 +948 +949 +950 +951 +952 +953 +954 +955 +956 +957 +958 +959 +960 +961 +962 +963 +964 +965 +966 +967 +968 +969 +970 +971 +972 +973 +974 +975 +976 +977 +978 +979 +980 +981 +982 +983 +984 +985 +986 +987 +988 +989 +990 +991 +992 +993 +994 +995 +996 +997 +998 +999 +1000 + +Hello, World +Oops +Z +汉字漢字汉字漢字汉字漢字汉字漢字汉字漢字汉字漢字汉字漢字汉字漢字 + + + +-[ \eAAAAAAABBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDDDDDEEEEEEEEEFFFFFFFFFGGGGGGG +<N7p0CR'yOIUQ0K<QrG~R&X +WX&5B6Ar"JlTd&.JC)`??ݸƬg鸛r͘[9qȵpc5LrH~4mls-'ҩ}ӝO
m(?0<%{{_ +ZeAN.Q^~V.kJK=sz0=i +vʈF X\ \:ڸч2b"}o:EiW@r*WsKŕ!Sg5Yg+Y2R6`"Jʈ0)@G/ (Ck:N +Ut;MQiba.|(2&lڃx5۸ȁ8ZaX )QRklthR[)73m(8D'R(7.ٮ12jo^/P$H +HD ?&^Ci- ؆?vcF|O%hn9&BV3;Sv䪝쓻Q.Ia` Uv"{K(d)JHy[rש4jU +<=L"8,Sf`Dvj='z]0^ʼHIjoəIC?$SG9ߛ|앥eu +䓅wniE2R}s@uO.6_Q1,OՖ.!n{C +`TuhM +%=D̹]iTYn`w{b{ד`^
WhCT
ŀ6|>4|huﴯU-:aу_'rUu8p35
sjm79GCG8
ܘޮM!8]w!tTrRh ks>XجIy'\\Z0:lwkI4Jg`Mb$p;gj +g
!EI#߉kۘ7"BV|ؽ=]K试7)H{Qa2B7YI|"w]mwNJb ],p gԍ@rB$3zSDKqZEr"BgM+9EQ +r^ƮxJEM>vF?D +O}wEqoYtAZ
X{JN JcZ!=ߎb8 +D/!IK`
>B(HL FEO: +LA/u:| +]oC#Mux*ۘ8_)p6B3L
mìO1 +Iw@qR)Ue@V2VzDzӃ>~r$ȸπW7דJ;jd"~J;^G91(<]ayh-PSg(6BD3O7laU%&:>+ )L؞x +}~KBot:Zuag("H?z72f;DHBfш%%=ΫօyT-㼡Nj +Xk,@G;T|]ґlحRȭP.A\V|~NPQ%UA銰YRre T1:=K"a|&UY8?-J4]濋G?!pGy(Vvc
i
*omzw%25@ܒZBxQy ++$o~2N70!=Q"̢C/\?&xj_n1 Psu9rȬ^?3R5U>c8_SW5LBϚ1Zo?| ИDW9_lx_/Hyb0;ϫO,eͦ8gG]8;j拱g,uH'rU[YPͣEp.9WMs)řmfΧ͍Va>D"h<H<*tc%]S
\ No newline at end of file |