From 5068d34c08f951a7ea6257d305a1627b09a95817 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 4 May 2024 19:44:55 +0200 Subject: Adding upstream version 0.11.1. Signed-off-by: Daniel Baumann --- .cirrus.yml | 5 + .clang-format | 178 + .clang-tidy | 162 + .codespellrc | 6 + .coveralls.yml | 2 + .github/ISSUE_TEMPLATE/bug_report.md | 17 + .github/ISSUE_TEMPLATE/feature_request.md | 20 + .github/mlc_config.json | 10 + .github/workflows/c-cpp.yml | 191 + .github/workflows/check-md-links.yml | 12 + .github/workflows/coverity.yml | 48 + .github/workflows/tailer-ape.yml | 33 + .gitignore | 104 + .gitmodules | 3 + .readthedocs.yaml | 20 + ARCHITECTURE.md | 121 + AUTHORS | 46 + CMakeLists.txt | 59 + CMakePresets.json | 142 + CMakeUserPresets.json.example | 62 + FUNDING.yml | 2 + INSTALL | 229 + LICENSE | 24 + Makefile.am | 13 + NEWS.md | 1000 + README | 80 + README.md | 157 + TESTS_ENVIRONMENT.in | 275 + appveyor.yml | 19 + autogen.sh | 26 + cleanup_expected.sh | 15 + cmake/CodeCoverage.cmake | 438 + cmake/Hunter/config.cmake | 14 + cmake/HunterGate.cmake | 528 + cmake/coverage.cmake | 33 + cmake/dev-mode.cmake | 32 + cmake/docs-ci.cmake | 112 + cmake/docs.cmake | 46 + cmake/folders.cmake | 21 + cmake/install-rules.cmake | 44 + cmake/install-script.cmake | 18 + cmake/lint-targets.cmake | 32 + cmake/lint.cmake | 50 + cmake/open-cpp-coverage.cmake.example | 31 + cmake/prelude.cmake | 10 + cmake/project-is-top-level.cmake | 6 + cmake/spell-targets.cmake | 22 + cmake/spell.cmake | 29 + cmake/variables.cmake | 28 + conanfile.py | 58 + configure.ac | 331 + demo/Dockerfile | 44 + demo/fly.toml | 35 + demo/loggen.py | 54 + docs/.gitignore | 5 + docs/02_downloads.md | 58 + docs/03_features.md | 140 + docs/04_tutorials.md | 30 + docs/05_docs.md | 6 + docs/06_changeblog.md | 6 + docs/404.html | 25 + docs/CNAME | 1 + docs/Doxyfile.in | 32 + docs/Gemfile | 31 + docs/Gemfile.lock | 270 + docs/Makefile | 153 + docs/README.md | 4 + docs/_config.yml | 62 + docs/_includes/social.html | 121 + docs/_layouts/top.html | 78 + docs/_posts/2013-09-10-json-encoded-logs.md | 56 + docs/_posts/2013-09-13-four-years-on-github.md | 12 + .../2013-10-05-mini-review-in-linux-magazine.md | 8 + docs/_posts/2013-10-06-competing-with-tail.md | 23 + .../2013-11-01-mini-review-linux-user-magazine.md | 9 + docs/_posts/2014-02-22-changes-to-the-scrollbar.md | 21 + docs/_posts/2014-11-11-lofi-mode.md | 23 + docs/_posts/2015-04-11-pretty-print-view.md | 39 + docs/_posts/2016-03-20-lnav-in-print.md | 10 + docs/_posts/2018-03-27-reveal-file-paths.md | 16 + docs/_posts/2018-04-05-linux-magazine-tutorial.md | 9 + docs/_posts/2018-05-17-tags-and-comments.md | 22 + docs/_posts/2018-11-9-visual-filter-editor.md | 35 + docs/_posts/2019-05-08-themes.md | 28 + docs/_posts/2020-12-23-xpath-sql-function.md | 39 + docs/_posts/2021-05-03-tailing-remote-files.md | 32 + docs/_posts/2022-05-01-regex101-integration.md | 73 + docs/_posts/2022-08-04-pretty-errors.md | 46 + docs/_posts/2022-08-06-markdown-support.md | 33 + docs/_posts/2022-09-01-playground.md | 27 + docs/_posts/2022-09-24-vscode-extension.md | 21 + docs/assets/images/favicon.png | Bin 0 -> 1340 bytes docs/assets/images/favicon.svg | 3 + docs/assets/images/linux-user-and-dev-mag.jpeg | Bin 0 -> 381894 bytes docs/assets/images/lnav-after-pretty.png | Bin 0 -> 161296 bytes docs/assets/images/lnav-before-pretty.png | Bin 0 -> 186997 bytes docs/assets/images/lnav-front-page.png | Bin 0 -> 1140435 bytes docs/assets/images/lnav-hist.png | Bin 0 -> 79899 bytes docs/assets/images/lnav-invalid-regex-error.png | Bin 0 -> 79410 bytes docs/assets/images/lnav-multi-file2.png | Bin 0 -> 313849 bytes docs/assets/images/lnav-query.png | Bin 0 -> 86674 bytes docs/assets/images/lnav-sql-error-msg.png | Bin 0 -> 36847 bytes docs/assets/images/lnav-syntax-highlight.gif | Bin 0 -> 26534 bytes docs/assets/images/lnav-syslog-thumb.png | Bin 0 -> 82417 bytes docs/assets/images/lnav-syslog.png | Bin 0 -> 334859 bytes docs/assets/images/lnav-tab-complete.gif | Bin 0 -> 411818 bytes docs/assets/images/lnav-theme-cycle.gif | Bin 0 -> 600599 bytes docs/assets/images/lnav-vscode-extension.png | Bin 0 -> 678830 bytes docs/assets/images/scrollbar-change-2.png | Bin 0 -> 146624 bytes docs/assets/main.scss | 31 + docs/conf.py.in | 6 + docs/index.markdown | 58 + docs/lnav-architecture.png | Bin 0 -> 368248 bytes docs/lnav-tui.png | Bin 0 -> 156641 bytes docs/pages/about.dox | 7 + docs/requirements.txt | 5 + docs/schemas/config-v1.schema.json | 778 + .../event-file-format-detected-v1.schema.json | 26 + docs/schemas/event-file-open-v1.schema.json | 21 + docs/schemas/event-log-msg-detected-v1.schema.json | 57 + docs/schemas/event-session-loaded-v1.schema.json | 16 + docs/schemas/format-v1.schema.json | 623 + docs/source/_ext/__init__.py | 0 docs/source/_ext/lnavlexer.py | 26 + docs/source/_static/theme_overrides.css | 56 + docs/source/cli.rst | 168 + docs/source/commands.rst | 132 + docs/source/conf.py | 460 + docs/source/config.rst | 270 + docs/source/cookbook.rst | 104 + docs/source/data.rst | 193 + docs/source/docutils.conf | 2 + docs/source/events.rst | 56 + docs/source/faq.rst | 57 + docs/source/filter-out-preview.png | Bin 0 -> 690117 bytes docs/source/formats.rst | 527 + docs/source/group_concat-help.png | Bin 0 -> 60071 bytes docs/source/hotkey-tips.png | Bin 0 -> 62825 bytes docs/source/hotkeys.rst | 289 + docs/source/howitworks.rst | 13 + docs/source/index.rst | 35 + docs/source/intro.rst | 131 + docs/source/key-encoding-prompt.png | Bin 0 -> 88912 bytes docs/source/lnav-breadcrumbs-help.png | Bin 0 -> 45782 bytes docs/source/lnav-config-header.png | Bin 0 -> 35192 bytes docs/source/lnav-files-panel.png | Bin 0 -> 70248 bytes docs/source/lnav-filters-panel.png | Bin 0 -> 48867 bytes docs/source/lnav-spectro-cpu-pct.png | Bin 0 -> 558558 bytes docs/source/lnav-ui.png | Bin 0 -> 923959 bytes docs/source/open-error.png | Bin 0 -> 21053 bytes docs/source/open-help.png | Bin 0 -> 98785 bytes docs/source/open-preview.png | Bin 0 -> 53409 bytes docs/source/query-results.png | Bin 0 -> 35595 bytes docs/source/sessions.rst | 23 + docs/source/sql-help.png | Bin 0 -> 29721 bytes docs/source/sqlext.rst | 184 + docs/source/sqltab.rst | 217 + docs/source/ui.rst | 280 + docs/source/usage.rst | 291 + docs/tutorials/playground/index.md | 9 + docs/tutorials/playground/logs/access_log.gz | Bin 0 -> 36529 bytes docs/tutorials/playground/logs/messages.gz | Bin 0 -> 14034 bytes docs/tutorials/playground/run.sh | 16 + docs/tutorials/playground/text/markdown-sample.md | 157 + .../tutorial-lib/configs/tutorial1/config.json | 18 + .../tutorial-lib/lnav-tutorial-key-handler.lnav | 29 + .../tutorial-lib/formats/tutorial-lib/tutorial.sql | 154 + docs/tutorials/tutorial1/index.md | 145 + docs/tutorials/tutorial1/run.sh | 16 + docs/tutorials/tutorial1/tutorial1.glog | 100 + example-scripts/clipboard.sh | 69 + example-scripts/log_to_csv.sh | 66 + example-scripts/report-demo.lnav | 83 + example-scripts/tag-ssh-msgs.lnav | 10 + lnav.1 | 126 + lnav.cfg | 543 + m4/ax_ac_append_to_file.m4 | 32 + m4/ax_ac_print_to_file.m4 | 32 + m4/ax_add_am_macro_static.m4 | 28 + m4/ax_am_macros_static.m4 | 38 + m4/ax_check_gnu_make.m4 | 95 + m4/ax_check_link_flag.m4 | 74 + m4/ax_check_pcre2.m4 | 163 + m4/ax_code_coverage.m4 | 272 + m4/ax_cxx_compile_stdcxx.m4 | 962 + m4/ax_cxx_compile_stdcxx_11.m4 | 39 + m4/ax_cxx_compile_stdcxx_14.m4 | 34 + m4/ax_cxx_compile_stdcxx_17.m4 | 35 + m4/ax_file_escapes.m4 | 30 + m4/ax_prog_cc_for_build.m4 | 141 + m4/ax_pthread.m4 | 507 + m4/ax_with_curses.m4 | 517 + m4/libcurl.m4 | 301 + m4/lnav_common.m4 | 22 + m4/lnav_with_jemalloc.m4 | 81 + m4/lnav_with_libarchive.m4 | 77 + m4/lnav_with_readline.m4 | 95 + m4/lnav_with_sqlite3.m4 | 127 + m4/lnav_with_yajl.m4 | 87 + release/Makefile | 102 + release/README.md | 4 + release/lnav-screenshot.terminal | 453 + release/loggen.py | 225 + release/spectrolog.py | 114 + release/tail-demo.sh | 19 + release/vagrant-static/Vagrantfile | 110 + release/vagrant-static/build-pkg.sh | 30 + release/vagrant-static/build.sh | 84 + release/vagrant-static/musl-pkg.sh | 24 + release/vagrant-static/pkg.sh | 22 + release/vagrant-static/provision-pkg.sh | 7 + release/vagrant-static/provision.sh | 152 + snapcraft.yaml | 84 + src/CMakeLists.txt | 632 + src/Makefile.am | 548 + src/all_logs_vtab.cc | 99 + src/all_logs_vtab.hh | 60 + src/alpha-release.sh | 13 + src/animals.json | 1 + src/ansi-palette.json | 122 + src/archive_manager.cc | 406 + src/archive_manager.cfg.hh | 46 + src/archive_manager.hh | 83 + src/base/CMakeLists.txt | 83 + src/base/Makefile.am | 110 + src/base/README.md | 1 + src/base/ansi_scrubber.cc | 388 + src/base/ansi_scrubber.hh | 75 + src/base/attr_line.builder.cc | 30 + src/base/attr_line.builder.hh | 119 + src/base/attr_line.cc | 537 + src/base/attr_line.hh | 758 + src/base/attr_line.tests.cc | 91 + src/base/auto_fd.hh | 318 + src/base/auto_mem.hh | 402 + src/base/auto_pid.cc | 63 + src/base/auto_pid.hh | 175 + src/base/bus.hh | 68 + src/base/date_time_scanner.cc | 308 + src/base/date_time_scanner.hh | 137 + src/base/enum_util.hh | 48 + src/base/file_range.hh | 78 + src/base/fs_util.cc | 183 + src/base/fs_util.hh | 122 + src/base/fs_util.tests.cc | 56 + src/base/func_util.hh | 159 + src/base/future_util.hh | 111 + src/base/humanize.cc | 110 + src/base/humanize.file_size.tests.cc | 51 + src/base/humanize.hh | 58 + src/base/humanize.network.cc | 76 + src/base/humanize.network.hh | 109 + src/base/humanize.network.tests.cc | 118 + src/base/humanize.time.cc | 205 + src/base/humanize.time.hh | 88 + src/base/humanize.time.tests.cc | 122 + src/base/injector.bind.hh | 173 + src/base/injector.hh | 230 + src/base/intern_string.cc | 303 + src/base/intern_string.hh | 857 + src/base/intern_string.tests.cc | 144 + src/base/is_utf8.cc | 304 + src/base/is_utf8.hh | 48 + src/base/isc.cc | 147 + src/base/isc.hh | 225 + src/base/itertools.hh | 785 + src/base/lnav.console.cc | 494 + src/base/lnav.console.hh | 176 + src/base/lnav.console.into.hh | 51 + src/base/lnav.gzip.cc | 135 + src/base/lnav.gzip.hh | 54 + src/base/lnav.gzip.tests.cc | 67 + src/base/lnav_log.cc | 671 + src/base/lnav_log.hh | 147 + src/base/log_level_enum.hh | 65 + src/base/lrucache.hpp | 83 + src/base/math_util.hh | 73 + src/base/network.tcp.cc | 75 + src/base/network.tcp.hh | 80 + src/base/network.tcp.tests.cc | 50 + src/base/opt_util.hh | 105 + src/base/paths.cc | 130 + src/base/paths.hh | 58 + src/base/result.h | 983 + src/base/snippet_highlighters.cc | 344 + src/base/snippet_highlighters.hh | 43 + src/base/string_attr_type.cc | 51 + src/base/string_attr_type.hh | 670 + src/base/string_util.cc | 307 + src/base/string_util.hh | 232 + src/base/string_util.tests.cc | 90 + src/base/strnatcmp.c | 302 + src/base/strnatcmp.h | 41 + src/base/test_base.cc | 33 + src/base/time_util.cc | 239 + src/base/time_util.hh | 206 + src/big_array.hh | 136 + src/bin2c.hh | 74 + src/bookmarks.cc | 89 + src/bookmarks.hh | 209 + src/bottom_status_source.cc | 250 + src/bottom_status_source.hh | 94 + src/bound_tags.hh | 54 + src/breadcrumb.hh | 134 + src/breadcrumb_curses.cc | 458 + src/breadcrumb_curses.hh | 107 + src/byte_array.hh | 148 + src/collation-functions.cc | 155 + src/column_namer.cc | 114 + src/column_namer.hh | 70 + src/command_executor.cc | 1135 + src/command_executor.hh | 262 + src/config.cmake.h.in | 31 + src/curl_looper.cc | 341 + src/curl_looper.hh | 226 + src/data_parser.cc | 1071 + src/data_parser.hh | 431 + src/data_scanner.cc | 265 + src/data_scanner.hh | 211 + src/data_scanner_re.cc | 36096 +++++++++++++++++++ src/data_scanner_re.re | 277 + src/db_sub_source.cc | 457 + src/db_sub_source.hh | 144 + src/diseases.json | 549 + src/doc_status_source.hh | 81 + src/document.sections.cc | 540 + src/document.sections.hh | 116 + src/dump_internals.cc | 93 + src/dump_internals.hh | 41 + src/elem_to_json.cc | 222 + src/elem_to_json.hh | 41 + src/emojis.json | 4036 +++ src/environ_vtab.cc | 338 + src/environ_vtab.hh | 39 + src/extension-functions.cc | 2828 ++ src/field_overlay_source.cc | 574 + src/field_overlay_source.hh | 86 + src/file_collection.cc | 649 + src/file_collection.hh | 180 + src/file_format.cc | 119 + src/file_format.hh | 76 + src/file_vtab.cc | 345 + src/file_vtab.cfg.hh | 43 + src/files_sub_source.cc | 437 + src/files_sub_source.hh | 117 + src/filter_observer.cc | 111 + src/filter_observer.hh | 78 + src/filter_status_source.cc | 332 + src/filter_status_source.hh | 82 + src/filter_sub_source.cc | 670 + src/filter_sub_source.hh | 92 + src/fmtlib/Makefile.am | 22 + src/fmtlib/fmt/args.h | 234 + src/fmtlib/fmt/chrono.h | 2080 ++ src/fmtlib/fmt/color.h | 651 + src/fmtlib/fmt/compile.h | 603 + src/fmtlib/fmt/core.h | 3280 ++ src/fmtlib/fmt/format-inl.h | 1733 + src/fmtlib/fmt/format.h | 4192 +++ src/fmtlib/fmt/locale.h | 2 + src/fmtlib/fmt/os.h | 478 + src/fmtlib/fmt/ostream.h | 213 + src/fmtlib/fmt/printf.h | 640 + src/fmtlib/fmt/ranges.h | 631 + src/fmtlib/fmt/std.h | 176 + src/fmtlib/fmt/xchar.h | 232 + src/fmtlib/format.cc | 47 + src/fmtlib/os.cc | 361 + src/format2csv.py | 23 + src/formats/README.md | 5 + src/formats/access_log.json | 117 + src/formats/alb_log.json | 133 + src/formats/block_log.json | 23 + src/formats/candlepin_log.json | 49 + src/formats/choose_repo_log.json | 24 + src/formats/cups_log.json | 43 + src/formats/dpkg_log.json | 43 + src/formats/elb_log.json | 109 + src/formats/engine_log.json | 34 + src/formats/error_log.json | 67 + src/formats/esx_syslog_log.json | 66 + src/formats/formats.am | 43 + src/formats/fsck_hfs_log.json | 23 + src/formats/glog_log.json | 52 + src/formats/haproxy_log.json | 173 + src/formats/java_log.json | 147 + src/formats/journald_json_log.json | 84 + src/formats/katello_log.json | 48 + src/formats/logfmt/CMakeLists.txt | 40 + src/formats/logfmt/Makefile.am | 41 + src/formats/logfmt/logfmt.parser.cc | 266 + src/formats/logfmt/logfmt.parser.hh | 91 + src/formats/logfmt/logfmt.parser.test.cc | 221 + src/formats/openam_log.json | 73 + src/formats/openamdb_log.json | 21 + src/formats/openstack_log.json | 65 + src/formats/page_log.json | 67 + src/formats/papertrail_log.json | 52 + src/formats/pcap_log.json | 82 + src/formats/procstate_log.json | 22 + src/formats/s3_log.json | 158 + src/formats/snaplogic_log.json | 55 + src/formats/sssd_log.json | 38 + src/formats/strace_log.json | 44 + src/formats/sudo_log.json | 48 + src/formats/syslog_log.json | 99 + src/formats/tcf_log.json | 51 + src/formats/tcsh_history.json | 18 + src/formats/unifi_iptables_log.json | 154 + src/formats/unifi_log.json | 204 + src/formats/uwsgi_log.json | 108 + src/formats/vdsm_log.json | 67 + src/formats/vmk_log.json | 51 + src/formats/vmw_log.json | 241 + src/formats/vmw_py_log.json | 42 + src/formats/vmw_vc_svc_log.json | 48 + src/formats/xmlrpc_log.json | 43 + src/fs-extension-functions.cc | 260 + src/fstat_vtab.cc | 368 + src/fstat_vtab.hh | 37 + src/fts_fuzzy_match.cc | 227 + src/fts_fuzzy_match.hh | 50 + src/ghc/filesystem.hpp | 6049 ++++ src/ghc/fs_fwd.hpp | 38 + src/ghc/fs_impl.hpp | 35 + src/ghc/fs_std.hpp | 60 + src/ghc/fs_std_fwd.hpp | 63 + src/ghc/fs_std_impl.hpp | 46 + src/grep_highlighter.hh | 39 + src/grep_proc.cc | 426 + src/grep_proc.hh | 307 + src/help.md | 533 + src/help.txt | 1001 + src/help_text.cc | 105 + src/help_text.hh | 225 + src/help_text_formatter.cc | 691 + src/help_text_formatter.hh | 61 + src/highlighter.cc | 146 + src/highlighter.hh | 123 + src/hist_source.cc | 188 + src/hist_source.hh | 408 + src/hotkeys.cc | 965 + src/hotkeys.hh | 36 + src/init.sql | 138 + src/input_dispatcher.cc | 178 + src/input_dispatcher.hh | 79 + src/internals/README.md | 4 + src/internals/cmd-ref.rst | 1628 + src/internals/sql-ref.rst | 3850 ++ src/itertools.similar.hh | 133 + src/json-extension-functions.cc | 915 + src/k_merge_tree.h | 471 + src/keymaps/README.md | 5 + src/keymaps/de-keymap.json | 51 + src/keymaps/default-keymap.json | 163 + src/keymaps/fr-keymap.json | 51 + src/keymaps/keymaps.am | 9 + src/keymaps/sv-keymap.json | 27 + src/keymaps/uk-keymap.json | 15 + src/keymaps/us-keymap.json | 51 + src/line_buffer.cc | 1441 + src/line_buffer.hh | 370 + src/listview_curses.cc | 631 + src/listview_curses.hh | 566 + src/lnav.cc | 3325 ++ src/lnav.events.cc | 177 + src/lnav.events.hh | 128 + src/lnav.hh | 286 + src/lnav.indexing.cc | 441 + src/lnav.indexing.hh | 45 + src/lnav.management_cli.cc | 910 + src/lnav.management_cli.hh | 53 + src/lnav_commands.cc | 5738 +++ src/lnav_commands.hh | 42 + src/lnav_config.cc | 1629 + src/lnav_config.hh | 135 + src/lnav_config_fwd.hh | 70 + src/lnav_util.cc | 334 + src/lnav_util.hh | 241 + src/log.watch.cc | 388 + src/log.watch.hh | 45 + src/log_accel.cc | 106 + src/log_accel.hh | 101 + src/log_actions.cc | 237 + src/log_actions.hh | 67 + src/log_data_helper.cc | 214 + src/log_data_helper.hh | 92 + src/log_data_table.cc | 196 + src/log_data_table.hh | 79 + src/log_format.cc | 3076 ++ src/log_format.hh | 561 + src/log_format_ext.hh | 427 + src/log_format_fwd.hh | 330 + src/log_format_impls.cc | 1775 + src/log_format_loader.cc | 1473 + src/log_format_loader.hh | 81 + src/log_gutter_source.hh | 77 + src/log_level.cc | 111 + src/log_level.hh | 49 + src/log_level_re.cc | 718 + src/log_level_re.re | 103 + src/log_search_table.cc | 270 + src/log_search_table.hh | 83 + src/log_search_table_fwd.hh | 40 + src/log_vtab_impl.cc | 2174 ++ src/log_vtab_impl.hh | 344 + src/logfile.cc | 1116 + src/logfile.cfg.hh | 45 + src/logfile.hh | 462 + src/logfile_fwd.hh | 160 + src/logfile_stats.hh | 40 + src/logfile_sub_source.cc | 2451 ++ src/logfile_sub_source.cfg.hh | 49 + src/logfile_sub_source.hh | 1021 + src/mapbox/optional.hpp | 74 + src/mapbox/recursive_wrapper.hpp | 122 + src/mapbox/variant.hpp | 1053 + src/mapbox/variant_cast.hpp | 85 + src/mapbox/variant_io.hpp | 45 + src/mapbox/variant_visitor.hpp | 43 + src/md2attr_line.cc | 635 + src/md2attr_line.hh | 91 + src/md4cpp.cc | 303 + src/md4cpp.hh | 144 + src/network-extension-functions.cc | 169 + src/optional.hpp | 1808 + src/pcap_manager.cc | 139 + src/pcap_manager.hh | 54 + src/pcrepp/CMakeLists.txt | 16 + src/pcrepp/Makefile.am | 33 + src/pcrepp/pcre2pp.cc | 473 + src/pcrepp/pcre2pp.hh | 367 + src/pcrepp/test_pcre2pp.cc | 246 + src/piper_proc.cc | 237 + src/piper_proc.hh | 86 + src/plain_text_source.cc | 373 + src/plain_text_source.hh | 137 + src/plugins.hh | 48 + src/pollable.cc | 125 + src/pollable.hh | 86 + src/pretty_printer.cc | 378 + src/pretty_printer.hh | 137 + src/preview_status_source.hh | 84 + src/ptimec.c | 157 + src/ptimec.hh | 1119 + src/ptimec_rt.cc | 186 + src/pugixml/Makefile.am | 9 + src/pugixml/pugiconfig.hpp | 77 + src/pugixml/pugixml.cpp | 13029 +++++++ src/pugixml/pugixml.hpp | 1501 + src/readline_callbacks.cc | 901 + src/readline_callbacks.hh | 50 + src/readline_context.hh | 183 + src/readline_curses.cc | 1508 + src/readline_curses.hh | 371 + src/readline_highlighters.cc | 480 + src/readline_highlighters.hh | 48 + src/readline_possibilities.cc | 496 + src/readline_possibilities.hh | 85 + src/regex101.client.cc | 331 + src/regex101.client.hh | 94 + src/regex101.import.cc | 395 + src/regex101.import.hh | 61 + src/regexp_vtab.cc | 643 + src/regexp_vtab.hh | 37 + src/relative_time.cc | 1134 + src/relative_time.hh | 263 + src/remote/CMakeLists.txt | 5 + src/remote/Makefile.am | 20 + src/remote/remote.ssh.cc | 38 + src/remote/remote.ssh.hh | 41 + src/ring_span.hh | 946 + src/root-config.json | 76 + src/safe/accessmode.h | 49 + src/safe/defaulttypes.h | 23 + src/safe/mutableref.h | 40 + src/safe/safe.h | 359 + src/scripts/README.md | 5 + src/scripts/dhclient-summary.lnav | 23 + src/scripts/dump-pid.sh | 13 + src/scripts/lnav-pop-view.lnav | 21 + src/scripts/partition-by-boot.lnav | 12 + src/scripts/rename-stdin.lnav | 12 + src/scripts/scripts.am | 12 + src/scripts/search-for.lnav | 7 + src/sequence_matcher.cc | 79 + src/sequence_matcher.hh | 102 + src/sequence_sink.hh | 89 + src/service_tags.hh | 48 + src/session.export.cc | 484 + src/session.export.hh | 43 + src/session_data.cc | 1824 + src/session_data.hh | 99 + src/shared_buffer.cc | 198 + src/shared_buffer.hh | 210 + src/shlex.cc | 218 + src/shlex.hh | 222 + src/shlex.resolver.hh | 94 + src/simdutf8check.h | 237 + src/spectro_impls.cc | 518 + src/spectro_impls.hh | 87 + src/spectro_source.cc | 615 + src/spectro_source.hh | 208 + src/spookyhash/SpookyV2.cpp | 350 + src/spookyhash/SpookyV2.h | 339 + src/sql_commands.cc | 273 + src/sql_help.hh | 59 + src/sql_util.cc | 1220 + src/sql_util.hh | 136 + src/sqlite-extension-func.cc | 1167 + src/sqlite-extension-func.hh | 109 + src/sqlitepp.cc | 36 + src/sqlitepp.client.hh | 209 + src/sqlitepp.hh | 110 + src/state-extension-functions.cc | 177 + src/static_file_vtab.cc | 333 + src/static_file_vtab.hh | 39 + src/statusview_curses.cc | 240 + src/statusview_curses.hh | 189 + src/string-extension-functions.cc | 1251 + src/strong_int.hh | 116 + src/styling.cc | 246 + src/styling.hh | 229 + src/sysclip.cc | 163 + src/sysclip.cfg.hh | 84 + src/sysclip.hh | 57 + src/tailer/CMakeLists.txt | 24 + src/tailer/Makefile.am | 111 + src/tailer/README.md | 35 + src/tailer/drive_tailer.cc | 288 + src/tailer/sha-256.c | 161 + src/tailer/sha-256.h | 44 + src/tailer/tailer.ape | Bin 0 -> 147456 bytes src/tailer/tailer.c | 100 + src/tailer/tailer.h | 77 + src/tailer/tailer.looper.cc | 1192 + src/tailer/tailer.looper.cfg.hh | 53 + src/tailer/tailer.looper.hh | 158 + src/tailer/tailer.main.c | 1072 + src/tailer/tailerpp.cc | 146 + src/tailer/tailerpp.hh | 315 + src/tailer/test_tailer.sh | 50 + src/term_extra.hh | 119 + src/termios_guard.hh | 79 + src/test_override.c | 56 + src/text_anonymizer.cc | 519 + src/text_anonymizer.hh | 75 + src/text_format.cc | 152 + src/text_format.hh | 125 + src/textfile_highlighters.cc | 464 + src/textfile_highlighters.hh | 37 + src/textfile_sub_source.cc | 864 + src/textfile_sub_source.hh | 173 + src/textview_curses.cc | 1132 + src/textview_curses.hh | 763 + src/textview_curses_fwd.hh | 49 + src/themes/README.md | 5 + src/themes/default-theme.json | 235 + src/themes/eldar.json | 190 + src/themes/grayscale.json | 166 + src/themes/monocai.json | 266 + src/themes/night-owl.json | 213 + src/themes/solarized-dark.json | 204 + src/themes/solarized-light.json | 204 + src/themes/themes.am | 10 + src/third-party/ArenaAlloc/arenaalloc.h | 186 + src/third-party/ArenaAlloc/arenaallocimpl.h | 286 + src/third-party/ArenaAlloc/recyclealloc.h | 184 + src/third-party/CLI/App.hpp | 3246 ++ src/third-party/CLI/CLI.hpp | 36 + src/third-party/CLI/Config.hpp | 396 + src/third-party/CLI/ConfigFwd.hpp | 185 + src/third-party/CLI/Error.hpp | 355 + src/third-party/CLI/Formatter.hpp | 292 + src/third-party/CLI/FormatterFwd.hpp | 184 + src/third-party/CLI/Macros.hpp | 60 + src/third-party/CLI/Option.hpp | 1362 + src/third-party/CLI/Split.hpp | 143 + src/third-party/CLI/StringTools.hpp | 430 + src/third-party/CLI/Timer.hpp | 134 + src/third-party/CLI/TypeTools.hpp | 1558 + src/third-party/CLI/Validators.hpp | 1175 + src/third-party/CLI/Version.hpp | 16 + src/third-party/backward-cpp/backward.hpp | 4460 +++ src/third-party/base64/LICENSE | 28 + src/third-party/base64/include/libbase64.h | 133 + src/third-party/base64/lib/Makefile.am | 23 + src/third-party/base64/lib/arch/avx/codec.c | 42 + src/third-party/base64/lib/arch/avx2/codec.c | 42 + src/third-party/base64/lib/arch/avx2/dec_loop.c | 110 + .../base64/lib/arch/avx2/dec_reshuffle.c | 34 + src/third-party/base64/lib/arch/avx2/enc_loop.c | 89 + .../base64/lib/arch/avx2/enc_reshuffle.c | 83 + .../base64/lib/arch/avx2/enc_translate.c | 30 + .../base64/lib/arch/generic/32/dec_loop.c | 86 + .../base64/lib/arch/generic/32/enc_loop.c | 73 + .../base64/lib/arch/generic/64/enc_loop.c | 77 + src/third-party/base64/lib/arch/generic/codec.c | 39 + src/third-party/base64/lib/arch/generic/dec_head.c | 37 + src/third-party/base64/lib/arch/generic/dec_tail.c | 91 + src/third-party/base64/lib/arch/generic/enc_head.c | 24 + src/third-party/base64/lib/arch/generic/enc_tail.c | 34 + src/third-party/base64/lib/arch/neon32/codec.c | 77 + src/third-party/base64/lib/arch/neon32/dec_loop.c | 106 + src/third-party/base64/lib/arch/neon32/enc_loop.c | 169 + .../base64/lib/arch/neon32/enc_reshuffle.c | 31 + .../base64/lib/arch/neon32/enc_translate.c | 57 + src/third-party/base64/lib/arch/neon64/codec.c | 92 + src/third-party/base64/lib/arch/neon64/dec_loop.c | 129 + src/third-party/base64/lib/arch/neon64/enc_loop.c | 133 + .../base64/lib/arch/neon64/enc_reshuffle.c | 31 + src/third-party/base64/lib/arch/sse41/codec.c | 42 + src/third-party/base64/lib/arch/sse42/codec.c | 42 + src/third-party/base64/lib/arch/ssse3/codec.c | 42 + src/third-party/base64/lib/arch/ssse3/dec_loop.c | 173 + .../base64/lib/arch/ssse3/dec_reshuffle.c | 33 + src/third-party/base64/lib/arch/ssse3/enc_loop.c | 67 + .../base64/lib/arch/ssse3/enc_reshuffle.c | 48 + .../base64/lib/arch/ssse3/enc_translate.c | 33 + src/third-party/base64/lib/codec_choose.c | 281 + src/third-party/base64/lib/codecs.h | 65 + src/third-party/base64/lib/config.h | 7 + src/third-party/base64/lib/env.h | 74 + src/third-party/base64/lib/lib.c | 175 + src/third-party/base64/lib/lib_openmp.c | 149 + .../base64/lib/tables/table_dec_32bit.h | 393 + .../base64/lib/tables/table_enc_12bit.h | 1031 + src/third-party/base64/lib/tables/tables.c | 40 + src/third-party/base64/lib/tables/tables.h | 23 + src/third-party/doctest-root/doctest/doctest.h | 7019 ++++ src/third-party/intervaltree/IntervalTree.h | 346 + src/third-party/md4c/md4c.c | 6410 ++++ src/third-party/md4c/md4c.h | 405 + src/third-party/rapidyaml/ryml_all.hpp | 30945 ++++++++++++++++ src/third-party/robin_hood/robin_hood.h | 2544 ++ src/third-party/scnlib/include/scn/all.h | 26 + src/third-party/scnlib/include/scn/detail/args.h | 619 + src/third-party/scnlib/include/scn/detail/config.h | 466 + .../scnlib/include/scn/detail/context.h | 126 + src/third-party/scnlib/include/scn/detail/error.h | 136 + src/third-party/scnlib/include/scn/detail/file.h | 568 + src/third-party/scnlib/include/scn/detail/fwd.h | 204 + src/third-party/scnlib/include/scn/detail/locale.h | 595 + .../scnlib/include/scn/detail/parse_context.h | 581 + src/third-party/scnlib/include/scn/detail/range.h | 598 + src/third-party/scnlib/include/scn/detail/result.h | 595 + .../scnlib/include/scn/detail/vectored.h | 166 + .../scnlib/include/scn/detail/visitor.h | 248 + src/third-party/scnlib/include/scn/fwd.h | 23 + src/third-party/scnlib/include/scn/istream.h | 23 + .../scnlib/include/scn/ranges/custom_impl.h | 1632 + src/third-party/scnlib/include/scn/ranges/ranges.h | 49 + .../scnlib/include/scn/ranges/std_impl.h | 67 + src/third-party/scnlib/include/scn/ranges/util.h | 419 + src/third-party/scnlib/include/scn/reader/common.h | 1663 + src/third-party/scnlib/include/scn/reader/float.h | 246 + src/third-party/scnlib/include/scn/reader/int.h | 537 + src/third-party/scnlib/include/scn/reader/reader.h | 111 + src/third-party/scnlib/include/scn/reader/string.h | 1336 + src/third-party/scnlib/include/scn/reader/types.h | 220 + src/third-party/scnlib/include/scn/scan/common.h | 131 + src/third-party/scnlib/include/scn/scan/getline.h | 186 + src/third-party/scnlib/include/scn/scan/ignore.h | 189 + src/third-party/scnlib/include/scn/scan/istream.h | 147 + src/third-party/scnlib/include/scn/scan/list.h | 450 + src/third-party/scnlib/include/scn/scan/scan.h | 444 + src/third-party/scnlib/include/scn/scan/vscan.h | 208 + src/third-party/scnlib/include/scn/scn.h | 26 + src/third-party/scnlib/include/scn/tuple_return.h | 23 + .../scnlib/include/scn/tuple_return/tuple_return.h | 123 + .../scnlib/include/scn/tuple_return/util.h | 176 + .../scnlib/include/scn/unicode/common.h | 139 + .../scnlib/include/scn/unicode/unicode.h | 243 + src/third-party/scnlib/include/scn/unicode/utf16.h | 139 + src/third-party/scnlib/include/scn/unicode/utf8.h | 297 + .../scnlib/include/scn/util/algorithm.h | 80 + src/third-party/scnlib/include/scn/util/array.h | 105 + src/third-party/scnlib/include/scn/util/expected.h | 158 + src/third-party/scnlib/include/scn/util/math.h | 121 + src/third-party/scnlib/include/scn/util/memory.h | 404 + src/third-party/scnlib/include/scn/util/meta.h | 77 + src/third-party/scnlib/include/scn/util/optional.h | 105 + .../scnlib/include/scn/util/small_vector.h | 788 + src/third-party/scnlib/include/scn/util/span.h | 240 + .../scnlib/include/scn/util/string_view.h | 270 + .../scnlib/include/scn/util/unique_ptr.h | 118 + src/third-party/scnlib/licenses/README.md | 25 + .../scnlib/licenses/fast_float-apache.txt | 190 + src/third-party/scnlib/licenses/fast_float-mit.txt | 27 + src/third-party/scnlib/licenses/fmt.rst | 27 + src/third-party/scnlib/licenses/nanorange.txt | 23 + src/third-party/scnlib/licenses/utfcpp.txt | 23 + src/third-party/scnlib/src/Makefile.am | 65 + .../single_include/fast_float/fast_float.h | 2981 ++ src/third-party/scnlib/src/file.cpp | 311 + src/third-party/scnlib/src/locale.cpp | 668 + src/third-party/scnlib/src/reader_float.cpp | 433 + src/third-party/scnlib/src/reader_int.cpp | 372 + src/third-party/scnlib/src/vscan.cpp | 80 + src/third-party/sqlite/ext/dbdump.c | 730 + src/third-party/sqlite/ext/series.c | 448 + src/third-party/xxHash/xxh_x86dispatch.c | 770 + src/third-party/xxHash/xxh_x86dispatch.h | 85 + src/third-party/xxHash/xxhash.c | 43 + src/third-party/xxHash/xxhash.h | 6075 ++++ src/time-extension-functions.cc | 266 + src/time_T.hh | 50 + src/time_formats.am | 62 + src/timer.cc | 121 + src/timer.hh | 68 + src/top_status_source.cc | 122 + src/top_status_source.cfg.hh | 37 + src/top_status_source.hh | 79 + src/top_sys_status_source.hh | 80 + src/unique_path.cc | 129 + src/unique_path.hh | 88 + src/url_loader.hh | 150 + src/view_curses.cc | 1233 + src/view_curses.hh | 482 + src/view_helpers.cc | 1233 + src/view_helpers.crumbs.hh | 37 + src/view_helpers.examples.hh | 41 + src/view_helpers.hh | 102 + src/view_helpers.hist.hh | 53 + src/views_vtab.cc | 1074 + src/views_vtab.hh | 39 + src/vis_line.hh | 43 + src/vt52_curses.cc | 313 + src/vt52_curses.hh | 154 + src/vtab_module.cc | 109 + src/vtab_module.hh | 929 + src/vtab_module_json.hh | 58 + src/words.json | 1 + src/ww898/cp_utf8.hpp | 171 + src/xml-entities.json | 2233 ++ src/xml_util.cc | 81 + src/xml_util.hh | 45 + src/xpath_vtab.cc | 393 + src/xpath_vtab.hh | 37 + src/xterm-palette.json | 3842 ++ src/xterm_mouse.cc | 98 + src/xterm_mouse.hh | 131 + src/yajl/CMakeLists.txt | 27 + src/yajl/Makefile.am | 37 + src/yajl/api/yajl_common.h | 75 + src/yajl/api/yajl_gen.h | 165 + src/yajl/api/yajl_parse.h | 228 + src/yajl/api/yajl_tree.h | 186 + src/yajl/yajl.c | 189 + src/yajl/yajl_alloc.c | 52 + src/yajl/yajl_alloc.h | 34 + src/yajl/yajl_buf.c | 103 + src/yajl/yajl_buf.h | 57 + src/yajl/yajl_bytestack.h | 69 + src/yajl/yajl_common.h | 75 + src/yajl/yajl_encode.c | 220 + src/yajl/yajl_encode.h | 34 + src/yajl/yajl_gen.c | 362 + src/yajl/yajl_lex.c | 763 + src/yajl/yajl_lex.h | 117 + src/yajl/yajl_parser.c | 498 + src/yajl/yajl_parser.h | 78 + src/yajl/yajl_tree.c | 503 + src/yajl/yajl_version.c | 7 + src/yajl/yajl_version.h | 23 + src/yajlpp/CMakeLists.txt | 27 + src/yajlpp/Makefile.am | 81 + src/yajlpp/README.md | 5 + src/yajlpp/drive_json_op.cc | 210 + src/yajlpp/drive_json_ptr_walk.cc | 103 + src/yajlpp/json_op.cc | 316 + src/yajlpp/json_op.hh | 82 + src/yajlpp/json_ptr.cc | 521 + src/yajlpp/json_ptr.hh | 136 + src/yajlpp/test_json_op.sh | 114 + src/yajlpp/test_json_ptr.cc | 73 + src/yajlpp/test_json_ptr_walk.sh | 77 + src/yajlpp/test_yajlpp.cc | 166 + src/yajlpp/yajlpp.cc | 1565 + src/yajlpp/yajlpp.hh | 682 + src/yajlpp/yajlpp_def.hh | 1388 + src/yaml-extension-functions.cc | 103 + test/CMakeLists.txt | 88 + test/Makefile.am | 513 + test/UTF-8-test.txt | Bin 0 -> 22781 bytes test/aftest.cc | 63 + test/ansi-colors.0.in | 22 + .../formats/invalid-json/format.json | 5 + .../formats/invalid-key/format.json | 27 + .../formats/invalid-json-format/format.json | 17 + .../formats/invalid-properties/format.json | 45 + test/bad-config/formats/invalid-regex/format.json | 29 + test/bad-config/formats/invalid-sample/format.json | 45 + test/bad-config/formats/invalid-schema/format.json | 3 + test/bad-config/formats/invalid-sql/init.sql | 5 + test/bad-config/formats/invalid-sql/init2.sql | 2 + test/bad-config/formats/no-regexes/format.json | 7 + test/bad-config/formats/no-samples/format.json | 17 + .../formats/invalid-config/config.bad-schema.json | 3 + .../bad-config2/formats/invalid-config/config.json | 5 + .../formats/invalid-config/config.malformed.json | 5 + .../formats/invalid-config/config.truncated.json | 2 + test/books.xml | 120 + test/datafile_ipaddr.0 | 13 + test/datafile_simple.0 | 25 + test/datafile_simple.1 | 13 + test/datafile_simple.10 | 9 + test/datafile_simple.11 | 13 + test/datafile_simple.12 | 10 + test/datafile_simple.13 | 10 + test/datafile_simple.14 | 57 + test/datafile_simple.15 | 75 + test/datafile_simple.16 | 9 + test/datafile_simple.17 | 22 + test/datafile_simple.18 | 23 + test/datafile_simple.19 | 53 + test/datafile_simple.2 | 40 + test/datafile_simple.20 | 17 + test/datafile_simple.21 | 26 + test/datafile_simple.22 | 9 + test/datafile_simple.23 | 88 + test/datafile_simple.3 | 25 + test/datafile_simple.4 | 10 + test/datafile_simple.5 | 10 + test/datafile_simple.6 | 25 + test/datafile_simple.7 | 18 + test/datafile_simple.8 | 44 + test/datafile_simple.9 | 17 + test/datafile_syslog.0 | 23 + test/datafile_syslog.1 | 21 + test/datafile_xml.0 | 26 + test/dhcp-trunc.pcapng | Bin 0 -> 253 bytes test/dhcp.pcapng | Bin 0 -> 1508 bytes test/document.sections.tests.cc | 170 + test/drive_data_scanner.cc | 310 + test/drive_grep_proc.cc | 156 + test/drive_line_buffer.cc | 245 + test/drive_listview.cc | 139 + test/drive_logfile.cc | 194 + test/drive_mvwattrline.cc | 119 + test/drive_readline_curses.cc | 153 + test/drive_sequencer.cc | 150 + test/drive_shlexer.cc | 96 + test/drive_sql.cc | 78 + test/drive_sql_anno.cc | 89 + test/drive_view_colors.cc | 106 + test/drive_vt52_curses.cc | 129 + test/expected/expected.am | 1005 + ...sh_17a68b798354f9a6cdfab372006caeb74038d15c.err | 0 ...sh_17a68b798354f9a6cdfab372006caeb74038d15c.out | 1 + ...sh_5524542b1a6954ff9741155101497270a2f0c557.err | 0 ...sh_5524542b1a6954ff9741155101497270a2f0c557.out | 1 + ...sh_97e19b9ff3775d84074455a2e8993a0611b1c269.err | 8 + ...sh_97e19b9ff3775d84074455a2e8993a0611b1c269.out | 0 ...sh_a1a09f890f4604309d0a81bbbec8e50fb7d5e887.err | 0 ...sh_a1a09f890f4604309d0a81bbbec8e50fb7d5e887.out | 3 + ...sh_f2e41555f1a5f40f54ce241207af602ed1503a2b.err | 0 ...sh_f2e41555f1a5f40f54ce241207af602ed1503a2b.out | 2 + ...sh_017b495b95218b7c083951e2dba331cfec6e90be.err | 6 + ...sh_017b495b95218b7c083951e2dba331cfec6e90be.out | 0 ...sh_0b1e4b1523dfca71927b1fe721c74490c51361d1.err | 0 ...sh_0b1e4b1523dfca71927b1fe721c74490c51361d1.out | 3 + ...sh_0b41fe57743ba0be088037d9ba29bc465e7c9bf9.err | 0 ...sh_0b41fe57743ba0be088037d9ba29bc465e7c9bf9.out | 3 + ...sh_0f0ab532d8d845f8201af65bf5f6fc994e21a8aa.err | 0 ...sh_0f0ab532d8d845f8201af65bf5f6fc994e21a8aa.out | 3 + ...sh_109a44ac6a8f1be2736c8e9c47aeed187e0581ee.err | 0 ...sh_109a44ac6a8f1be2736c8e9c47aeed187e0581ee.out | 2 + ...sh_12856706bfb4a8e2686098dd2644a7989d370b02.err | 0 ...sh_12856706bfb4a8e2686098dd2644a7989d370b02.out | 1 + ...sh_12b4cb9bd6586f9694100db76734b19a75158eab.err | 7 + ...sh_12b4cb9bd6586f9694100db76734b19a75158eab.out | 0 ...sh_145126309709179759926289caf729703ef6e1c6.err | 0 ...sh_145126309709179759926289caf729703ef6e1c6.out | 2 + ...sh_148007d2626b3c92d00ac31639b6918b1fc4aa60.err | 0 ...sh_148007d2626b3c92d00ac31639b6918b1fc4aa60.out | 2 + ...sh_1cab7d240cf85ff2c3538f5a06af141b01bc83ad.err | 0 ...sh_1cab7d240cf85ff2c3538f5a06af141b01bc83ad.out | 3 + ...sh_1d92c5bc12f5e7aaa6d84c5ed47f0b9f96e36c6a.err | 0 ...sh_1d92c5bc12f5e7aaa6d84c5ed47f0b9f96e36c6a.out | 68 + ...sh_1e1c8492b295913ce5afcd104cde0ec4ca1dcdac.err | 6 + ...sh_1e1c8492b295913ce5afcd104cde0ec4ca1dcdac.out | 0 ...sh_1f53f5b16c7c5aa695ed2e6427d822a1b940fcf4.err | 0 ...sh_1f53f5b16c7c5aa695ed2e6427d822a1b940fcf4.out | 3 + ...sh_22577861cb0921a7e7f3d1af6485938f4930ba7b.err | 0 ...sh_22577861cb0921a7e7f3d1af6485938f4930ba7b.out | 2 + ...sh_2339d09953b6937981d8a448000c3fdc2837f8c4.err | 0 ...sh_2339d09953b6937981d8a448000c3fdc2837f8c4.out | 12 + ...sh_2539ff9c4dbed93df3f0408ccc5fd81df34d1193.err | 0 ...sh_2539ff9c4dbed93df3f0408ccc5fd81df34d1193.out | 0 ...sh_29f0c808f4e93c6ef3890e6b793bee274a5b36ca.err | 0 ...sh_29f0c808f4e93c6ef3890e6b793bee274a5b36ca.out | 1 + ...sh_2a449c0a43e895e85c8b1c9547f32d7b5b4f84f6.err | 0 ...sh_2a449c0a43e895e85c8b1c9547f32d7b5b4f84f6.out | 1 + ...sh_2a535de164de4c060d2bff34aa7cc75ac7cac2c2.err | 0 ...sh_2a535de164de4c060d2bff34aa7cc75ac7cac2c2.out | 2 + ...sh_2cd167954a3be3e130e5f9601b72794a856cef92.err | 6 + ...sh_2cd167954a3be3e130e5f9601b72794a856cef92.out | 0 ...sh_2de9ec294e2f533d13e04c70d9525f8b58d47bb2.err | 0 ...sh_2de9ec294e2f533d13e04c70d9525f8b58d47bb2.out | 2 + ...sh_2e123104cdd2087ac40731a0aa533ba6a87ea744.err | 0 ...sh_2e123104cdd2087ac40731a0aa533ba6a87ea744.out | 1 + ...sh_2e67bdbbc9a14aa772b2a9f755ed8f8124708558.err | 0 ...sh_2e67bdbbc9a14aa772b2a9f755ed8f8124708558.out | 23 + ...sh_2ff0fe712c9b0012e42282c5f77b0b83cad37ddf.err | 0 ...sh_2ff0fe712c9b0012e42282c5f77b0b83cad37ddf.out | 1 + ...sh_305b1dfdfe785b945df4220aad6671ae1d364f55.err | 0 ...sh_305b1dfdfe785b945df4220aad6671ae1d364f55.out | 1 + ...sh_3429080ed14d01c6a887900186f37750df0d5ff0.err | 0 ...sh_3429080ed14d01c6a887900186f37750df0d5ff0.out | 2 + ...sh_34a6bcaa2877471b8ea718374101fa9ce3b78235.err | 0 ...sh_34a6bcaa2877471b8ea718374101fa9ce3b78235.out | 1 + ...sh_35b0dd8a030396742bc5acfde7715fb19f312f29.err | 0 ...sh_35b0dd8a030396742bc5acfde7715fb19f312f29.out | 3 + ...sh_36800217930a6a30e68c4efb20f6959c4f71aeb0.err | 7 + ...sh_36800217930a6a30e68c4efb20f6959c4f71aeb0.out | 0 ...sh_38fa2a95b703d4ce12e82882eca1938264822690.err | 0 ...sh_38fa2a95b703d4ce12e82882eca1938264822690.out | 3 + ...sh_3b20a298e2c059d7f6045cbc0c07ca3db3917695.err | 0 ...sh_3b20a298e2c059d7f6045cbc0c07ca3db3917695.out | 2 + ...sh_453054e29aaca4c2662c45c2a1f2f63f3510d8dd.err | 0 ...sh_453054e29aaca4c2662c45c2a1f2f63f3510d8dd.out | 2 + ...sh_4b2d91b19008d5b775090e3ef87c111f9e603b15.err | 0 ...sh_4b2d91b19008d5b775090e3ef87c111f9e603b15.out | 2 + ...sh_4dbe20c11056a07d2c7efb5ed15903050d628216.err | 0 ...sh_4dbe20c11056a07d2c7efb5ed15903050d628216.out | 3 + ...sh_4f06183ed231669965965f5042fbbb507fa7deab.err | 0 ...sh_4f06183ed231669965965f5042fbbb507fa7deab.out | 3 + ...sh_512872aebaae73ca4f33fa93acb2f4e3b018f8b4.err | 5 + ...sh_512872aebaae73ca4f33fa93acb2f4e3b018f8b4.out | 0 ...sh_53a9686102f69b07b034df291f554a00b265ed20.err | 0 ...sh_53a9686102f69b07b034df291f554a00b265ed20.out | 2 + ...sh_55c2fd15ec2c7d96dbef7b36a42a1b7b42f90dbc.err | 4 + ...sh_55c2fd15ec2c7d96dbef7b36a42a1b7b42f90dbc.out | 0 ...sh_5bfd08c1639701476d7b9348c36afd46fdbe6f2a.err | 0 ...sh_5bfd08c1639701476d7b9348c36afd46fdbe6f2a.out | 2 + ...sh_624a41e152675575f4b07c19b2cf0e3a028429a2.err | 0 ...sh_624a41e152675575f4b07c19b2cf0e3a028429a2.out | 2 + ...sh_62d68c0a11757c996f24c8f003e6b4059c3e30b2.err | 0 ...sh_62d68c0a11757c996f24c8f003e6b4059c3e30b2.out | 1 + ...sh_661ec61acdd8f6fa6ec1e3c2cf5f896eef431351.err | 0 ...sh_661ec61acdd8f6fa6ec1e3c2cf5f896eef431351.out | 14 + ...sh_6a6031113aca32fabc5a3da64b7be46f5ce5a312.err | 8 + ...sh_6a6031113aca32fabc5a3da64b7be46f5ce5a312.out | 0 ...sh_6e016c0ed61fc652be1a79b864875ffede64f281.err | 4 + ...sh_6e016c0ed61fc652be1a79b864875ffede64f281.out | 0 ...sh_7270e37dab4549cfa7c5232451c031e1e04b4aef.err | 0 ...sh_7270e37dab4549cfa7c5232451c031e1e04b4aef.out | 1 + ...sh_73ea99c84fb1d4570e8bcd45c423b4a28fe41e81.err | 0 ...sh_73ea99c84fb1d4570e8bcd45c423b4a28fe41e81.out | 3 + ...sh_7cb644890c4b945ff3f1e15c86a58c85cb5425c0.err | 0 ...sh_7cb644890c4b945ff3f1e15c86a58c85cb5425c0.out | 5 + ...sh_7e14e7f18219719453838835fa96c3451f78996d.err | 6 + ...sh_7e14e7f18219719453838835fa96c3451f78996d.out | 0 ...sh_819b3dd21348f7242f3914ad0a8c5b1cdb3f91af.err | 0 ...sh_819b3dd21348f7242f3914ad0a8c5b1cdb3f91af.out | 1 + ...sh_8298805f897346b4bb0f14e53c06b4fa28e309e3.err | 0 ...sh_8298805f897346b4bb0f14e53c06b4fa28e309e3.out | 3 + ...sh_83654557317602d2e00adde1e5cba190d9db0dff.err | 0 ...sh_83654557317602d2e00adde1e5cba190d9db0dff.out | 3 + ...sh_85ae6ac1eb9a8378f7a6c39659f52671218ce64b.err | 0 ...sh_85ae6ac1eb9a8378f7a6c39659f52671218ce64b.out | 1 + ...sh_85ed177028f226e86b1d164eb1a4e18eaf036c9d.err | 0 ...sh_85ed177028f226e86b1d164eb1a4e18eaf036c9d.out | 1 + ...sh_8758082427d6232a15053433942a4b5ad9f2e3ce.err | 0 ...sh_8758082427d6232a15053433942a4b5ad9f2e3ce.out | 1 + ...sh_876116da8ab46c0c8a212ce230d1b8a13970f78f.err | 6 + ...sh_876116da8ab46c0c8a212ce230d1b8a13970f78f.out | 0 ...sh_8765cbf326648e9014f8cf5f761895010fff443a.err | 0 ...sh_8765cbf326648e9014f8cf5f761895010fff443a.out | 37 + ...sh_89afa826d1b33be6926df48443faa1d1c5f285a7.err | 6 + ...sh_89afa826d1b33be6926df48443faa1d1c5f285a7.out | 0 ...sh_8d5b43c693e78804a8fb06989392fa8cccb46b7b.err | 0 ...sh_8d5b43c693e78804a8fb06989392fa8cccb46b7b.out | 2 + ...sh_9445861db011dfa2d21a44788047de345ee291e8.err | 0 ...sh_9445861db011dfa2d21a44788047de345ee291e8.out | 3 + ...sh_95beaabe41d72cf4c6810e79c623da759ac1c71b.err | 0 ...sh_95beaabe41d72cf4c6810e79c623da759ac1c71b.out | 2 + ...sh_968dac54dc80d91a5da2322890c6c26dfa0d8462.err | 0 ...sh_968dac54dc80d91a5da2322890c6c26dfa0d8462.out | 1 + ...sh_a00943ef715598c7554b85de8502454e41bb9e28.err | 0 ...sh_a00943ef715598c7554b85de8502454e41bb9e28.out | 4 + ...sh_a0e6214b2a85c90d31aee12efde850441cca7eb3.err | 0 ...sh_a0e6214b2a85c90d31aee12efde850441cca7eb3.out | 2 + ...sh_a1123427c31c022433d66d05ee5d5e1c8ab415e4.err | 0 ...sh_a1123427c31c022433d66d05ee5d5e1c8ab415e4.out | 2 + ...sh_a190bfc279fa046a823864f1484f899d27d22953.err | 3 + ...sh_a190bfc279fa046a823864f1484f899d27d22953.out | 0 ...sh_a5742238bad948b1372d32f7a491f03fa4e8b711.err | 6 + ...sh_a5742238bad948b1372d32f7a491f03fa4e8b711.out | 0 ...sh_a6c431f2871ea96cfdf4e11465b3bca543c7b678.err | 0 ...sh_a6c431f2871ea96cfdf4e11465b3bca543c7b678.out | 10 + ...sh_a8006c4169d76baecd99a0699c2fc66a583ad676.err | 7 + ...sh_a8006c4169d76baecd99a0699c2fc66a583ad676.out | 0 ...sh_ac45fb0f8f9578c3ded0855f694698ec38ce31ad.err | 0 ...sh_ac45fb0f8f9578c3ded0855f694698ec38ce31ad.out | 12 + ...sh_af0fcbd30b3fd0d13477aa3325ef0302052a4d9f.err | 0 ...sh_af0fcbd30b3fd0d13477aa3325ef0302052a4d9f.out | 1 + ...sh_b5a530d16c982cf769151291f0bfd612ea71183f.err | 0 ...sh_b5a530d16c982cf769151291f0bfd612ea71183f.out | 1 + ...sh_b6a3bb78e9d60e5e1f5ce5b18e40d2f1662707ab.err | 0 ...sh_b6a3bb78e9d60e5e1f5ce5b18e40d2f1662707ab.out | 4417 +++ ...sh_b755a8b48c0f602f0270500b0117b76e11db546e.err | 0 ...sh_b755a8b48c0f602f0270500b0117b76e11db546e.out | 37 + ...sh_b7fcd26c45c850c3d43ce25b1f610a311eb898c5.err | 0 ...sh_b7fcd26c45c850c3d43ce25b1f610a311eb898c5.out | 1 + ...sh_b9f8bf53ec2736432eb048d94a391175eb4dc5bf.err | 0 ...sh_b9f8bf53ec2736432eb048d94a391175eb4dc5bf.out | 2 + ...sh_bc60341827636715c14c562863da9733cbde7e68.err | 0 ...sh_bc60341827636715c14c562863da9733cbde7e68.out | 1 + ...sh_be1d9628fc447b6f17121d9457ea1602afe8f3f3.err | 0 ...sh_be1d9628fc447b6f17121d9457ea1602afe8f3f3.out | 1 + ...sh_be3b7c5874b5f4d86cc230bd2f9802c98909e148.err | 0 ...sh_be3b7c5874b5f4d86cc230bd2f9802c98909e148.out | 1 + ...sh_bf4e7fad67e281beaa11b6e2b03a00b419c7c9b0.err | 7 + ...sh_bf4e7fad67e281beaa11b6e2b03a00b419c7c9b0.out | 0 ...sh_c01e10f7cae8d36fa79ae03be887cb5477025f6d.err | 0 ...sh_c01e10f7cae8d36fa79ae03be887cb5477025f6d.out | 5 + ...sh_c2b4431dd0cc36c6201d263b727b3305e8cda6b1.err | 7 + ...sh_c2b4431dd0cc36c6201d263b727b3305e8cda6b1.out | 0 ...sh_c4777849c39a6c34dea5b0279cd7400692f1ab5f.err | 0 ...sh_c4777849c39a6c34dea5b0279cd7400692f1ab5f.out | 3 + ...sh_c4a15771f7e1487bf73b2e9d1564ad8ecfd76c7e.err | 0 ...sh_c4a15771f7e1487bf73b2e9d1564ad8ecfd76c7e.out | 1 + ...sh_c72aed622c19d493968e33f20d5dde3838a4258f.err | 6 + ...sh_c72aed622c19d493968e33f20d5dde3838a4258f.out | 0 ...sh_c7fabc25374ff47c47931f63b1d697061b816a28.err | 0 ...sh_c7fabc25374ff47c47931f63b1d697061b816a28.out | 2 + ...sh_ca66660c973f76a3c2a147c7f5035bcb4e8a8bbc.err | 0 ...sh_ca66660c973f76a3c2a147c7f5035bcb4e8a8bbc.out | 2 + ...sh_ccd326da92d1cacda63501cd1a3077381a18e8f2.err | 0 ...sh_ccd326da92d1cacda63501cd1a3077381a18e8f2.out | 1 + ...sh_d3b69abdfb39e4bfa5828c2f9593e2b2b7ed4d5d.err | 0 ...sh_d3b69abdfb39e4bfa5828c2f9593e2b2b7ed4d5d.out | 3 + ...sh_d76d77ad95b9f120825417a6a8220c13df9541fc.err | 0 ...sh_d76d77ad95b9f120825417a6a8220c13df9541fc.out | 3 + ...sh_d7eebacdcf2cb194f25fa4ef97b7b5376b442467.err | 7 + ...sh_d7eebacdcf2cb194f25fa4ef97b7b5376b442467.out | 0 ...sh_d836c84398c831c976df46f46fe3bf5983c44c37.err | 0 ...sh_d836c84398c831c976df46f46fe3bf5983c44c37.out | 2 + ...sh_d8eeef53a58bdeddbc1028d7c525413e3ca1c8df.err | 0 ...sh_d8eeef53a58bdeddbc1028d7c525413e3ca1c8df.out | 1 + ...sh_dbdd62995fdefc8318053af05a32416eccfa79fc.err | 0 ...sh_dbdd62995fdefc8318053af05a32416eccfa79fc.out | 1 + ...sh_dd41fbbcd71699314af232156d4155fbdf849131.err | 0 ...sh_dd41fbbcd71699314af232156d4155fbdf849131.out | 3 + ...sh_df6f4cea16bb8f20e6408fe4b40335e6de8a7f18.err | 0 ...sh_df6f4cea16bb8f20e6408fe4b40335e6de8a7f18.out | 3 + ...sh_e495cf059477e3f80c3241c6f8d5808b6f1d19c7.err | 0 ...sh_e495cf059477e3f80c3241c6f8d5808b6f1d19c7.out | 2 + ...sh_e7e8244fac65bc51dbd5af31be476fe3b8776bfc.err | 0 ...sh_e7e8244fac65bc51dbd5af31be476fe3b8776bfc.out | 12 + ...sh_e911aebcb2defb7471aa620c45a86cad449ad505.err | 0 ...sh_e911aebcb2defb7471aa620c45a86cad449ad505.out | 2 + ...sh_eb22c3e94c536a1bfaeae0c40d271b5b4b08f4fc.err | 0 ...sh_eb22c3e94c536a1bfaeae0c40d271b5b4b08f4fc.out | 3 + ...sh_ec2b28c6ea328e3ea56b13ab8ca3d9ee856a9dda.err | 6 + ...sh_ec2b28c6ea328e3ea56b13ab8ca3d9ee856a9dda.out | 0 ...sh_ed5b73be0b991e0e8d6735e31df5b37c4286321b.err | 7 + ...sh_ed5b73be0b991e0e8d6735e31df5b37c4286321b.out | 0 ...sh_f788d5f5932905d09ecbd581040ec5ce76459da5.err | 0 ...sh_f788d5f5932905d09ecbd581040ec5ce76459da5.out | 3 + ...sh_ff6faebbde8586e04bfadba14a3d2bb4451784ad.err | 0 ...sh_ff6faebbde8586e04bfadba14a3d2bb4451784ad.out | 2 + ...sh_2765ea0d4c037b8c935840604edb0ae796c97a04.err | 6 + ...sh_2765ea0d4c037b8c935840604edb0ae796c97a04.out | 0 ...sh_5105c29004e297521310ca0bd0fd560b01c2c549.err | 20 + ...sh_5105c29004e297521310ca0bd0fd560b01c2c549.out | 0 ...sh_5fd9fbccc35e9b06abdd913da0c16bdb306b926e.err | 6 + ...sh_5fd9fbccc35e9b06abdd913da0c16bdb306b926e.out | 0 ...sh_a0907769aba112d628e7ebe39c4ec252e5e0bc69.err | 38 + ...sh_a0907769aba112d628e7ebe39c4ec252e5e0bc69.out | 0 ...sh_b08f7523659d1c12f0e59920cd40d17d4a83b72f.err | 0 ...sh_b08f7523659d1c12f0e59920cd40d17d4a83b72f.out | 0 ...sh_d622658dc98327b1b2fd346802d24bc633e34ac7.err | 12 + ...sh_d622658dc98327b1b2fd346802d24bc633e34ac7.out | 0 ...sh_d708b6fd32d83ce0ee00ca5383388308ba5a06e1.err | 8 + ...sh_d708b6fd32d83ce0ee00ca5383388308ba5a06e1.out | 0 ...sh_eec3768ebc201ca63bca1411270965f78db1abfc.err | 0 ...sh_eec3768ebc201ca63bca1411270965f78db1abfc.out | 1 + ...sh_09ba47d70bfca88e89faf29598c1095292cad435.err | 0 ...sh_09ba47d70bfca88e89faf29598c1095292cad435.out | 0 ...sh_153e221f3cb50f4d3e4581be0bf311e62489c42d.err | 0 ...sh_153e221f3cb50f4d3e4581be0bf311e62489c42d.out | 6 + ...sh_3dae146ef3bf201c43656344803694a34a3dbfec.err | 0 ...sh_3dae146ef3bf201c43656344803694a34a3dbfec.out | 2 + ...sh_6f9523d43f174397829b6a7fe6ee0090d97df5f9.err | 0 ...sh_6f9523d43f174397829b6a7fe6ee0090d97df5f9.out | 0 ...sh_729f77b8e7136d64d22a6610a80ba6b584a2d896.err | 0 ...sh_729f77b8e7136d64d22a6610a80ba6b584a2d896.out | 3 + ...sh_d9c7907f907b2335e1328b23fdc46d0968a608d9.err | 10 + ...sh_d9c7907f907b2335e1328b23fdc46d0968a608d9.out | 0 ...sh_ed8dc44add223341c03ccb7b3e18371bdb42b710.err | 0 ...sh_ed8dc44add223341c03ccb7b3e18371bdb42b710.out | 3 + ...sh_15e861d2327512a721fd42ae51dc5427689e0bb6.err | 0 ...sh_15e861d2327512a721fd42ae51dc5427689e0bb6.out | 9 + ...sh_3f1d6f35e8a9ae4fd3e91ffaa82a037b5a847ab7.err | 171 + ...sh_3f1d6f35e8a9ae4fd3e91ffaa82a037b5a847ab7.out | 0 ...sh_5992e2695b7e6cf1f3520dbb87af8fc2b8f27088.err | 190 + ...sh_5992e2695b7e6cf1f3520dbb87af8fc2b8f27088.out | 0 ...sh_a47f2b090a5d8a226783835c7ff7d1c8821f11ed.err | 61 + ...sh_a47f2b090a5d8a226783835c7ff7d1c8821f11ed.out | 0 ...sh_fca6c1fb9f3aaa69b3ffb2d1a8a86434b2f4a247.err | 66 + ...sh_fca6c1fb9f3aaa69b3ffb2d1a8a86434b2f4a247.out | 0 ...sh_168cac40c27f547044c89d39eb0ff2ef81da4b21.err | 0 ...sh_168cac40c27f547044c89d39eb0ff2ef81da4b21.out | 13 + ...sh_1bb0fd243e916546aea22029245ac590dae17a86.err | 0 ...sh_1bb0fd243e916546aea22029245ac590dae17a86.out | 14 + ...sh_40223ac4742883f883ccc61044bfffd6e102cca6.err | 0 ...sh_40223ac4742883f883ccc61044bfffd6e102cca6.out | 192 + ...sh_4315a3d6124c14cbe3c474b6dbf4cc8720a9859f.err | 0 ...sh_4315a3d6124c14cbe3c474b6dbf4cc8720a9859f.out | 3 + ...sh_469f005b0708d629bc95f0c48a5e390f440c1fef.err | 0 ...sh_469f005b0708d629bc95f0c48a5e390f440c1fef.out | 29 + ...sh_6fbe20faa161ab9fa77df7568fff84bf3e47e920.err | 0 ...sh_6fbe20faa161ab9fa77df7568fff84bf3e47e920.out | 4 + ...sh_7724d1a96d74d4418dd44d7416270f9bb64b2564.err | 0 ...sh_7724d1a96d74d4418dd44d7416270f9bb64b2564.out | 29 + ...sh_7aade92cff911c5b3cfc733685809f949ae35778.err | 0 ...sh_7aade92cff911c5b3cfc733685809f949ae35778.out | 1 + ...sh_7c6529f6bf4a0cb565f5665fdcba032f0ae1ebbe.err | 0 ...sh_7c6529f6bf4a0cb565f5665fdcba032f0ae1ebbe.out | 12 + ...sh_80959e2bb6a7fdf938c2e4dbd7d7c81eb84fa072.err | 0 ...sh_80959e2bb6a7fdf938c2e4dbd7d7c81eb84fa072.out | 8 + ...sh_84a71e94dc34661a70bb9015b67ba00e93e9cfb5.err | 0 ...sh_84a71e94dc34661a70bb9015b67ba00e93e9cfb5.out | 2 + ...sh_85d03b1b41a7f819af135d2521a8f2c59418e907.err | 0 ...sh_85d03b1b41a7f819af135d2521a8f2c59418e907.out | 14 + ...sh_8f2ebcd319afc7966ef11e31f9dd646bf6f001dd.err | 0 ...sh_8f2ebcd319afc7966ef11e31f9dd646bf6f001dd.out | 21 + ...sh_90a037c7d9d70ac4ca97158271ea242787313377.err | 0 ...sh_90a037c7d9d70ac4ca97158271ea242787313377.out | 3 + ...sh_952297a90e312d2184fe3e4df795ddc731b096c9.err | 0 ...sh_952297a90e312d2184fe3e4df795ddc731b096c9.out | 4 + ...sh_989e52d167582648b73c5d025cc0e814c642b3c8.err | 0 ...sh_989e52d167582648b73c5d025cc0e814c642b3c8.out | 4 + ...sh_a06b3cdd46b387e72d6faa4cce648b8b11ae870b.err | 0 ...sh_a06b3cdd46b387e72d6faa4cce648b8b11ae870b.out | 25 + ...sh_a6be47f1311ed92feaf303142fcb103deb80f456.err | 0 ...sh_a6be47f1311ed92feaf303142fcb103deb80f456.out | 4 + ...sh_c1a23804c39b0f74642286d69865ee9d0961a58a.err | 0 ...sh_c1a23804c39b0f74642286d69865ee9d0961a58a.out | 2 + ...sh_c60050b3469f37c5b0864e1dc7eb354e91d6ec81.err | 0 ...sh_c60050b3469f37c5b0864e1dc7eb354e91d6ec81.out | 50 + ...sh_d0ec34389274affb70a5a76ba4789d51fd60f602.err | 0 ...sh_d0ec34389274affb70a5a76ba4789d51fd60f602.out | 4 + ...sh_d7362cffc8335c2fe6b6527315de59bd6f5dcc7f.err | 0 ...sh_d7362cffc8335c2fe6b6527315de59bd6f5dcc7f.out | 3 + ...sh_dfff27a651650a04d93de9a06ab5480e94ce3a79.err | 0 ...sh_dfff27a651650a04d93de9a06ab5480e94ce3a79.out | 4 + ...sh_fe19b7ebd349cd689b3f5c22618eab5ce995e68e.err | 0 ...sh_fe19b7ebd349cd689b3f5c22618eab5ce995e68e.out | 4 + ...sh_08d731a04c877a34819b35de185e30a74c9fd497.err | 0 ...sh_08d731a04c877a34819b35de185e30a74c9fd497.out | 3 + ...sh_09bd16e044302f6b121092534708594bdad11b5a.err | 0 ...sh_09bd16e044302f6b121092534708594bdad11b5a.out | 1 + ...sh_1c6eee38f66356fcd9a9f0faedaea6dbcc901060.err | 0 ...sh_1c6eee38f66356fcd9a9f0faedaea6dbcc901060.out | 2 + ...sh_218ecb88b4753010c4264b3ac351260b4811612f.err | 0 ...sh_218ecb88b4753010c4264b3ac351260b4811612f.out | 2 + ...sh_290a3c49e53c2229a7400c107338fa0bb38375e2.err | 0 ...sh_290a3c49e53c2229a7400c107338fa0bb38375e2.out | 2 + ...sh_3fc6bfd8a6160817211f3e14fde957af75b9dbe7.err | 0 ...sh_3fc6bfd8a6160817211f3e14fde957af75b9dbe7.out | 2 + ...sh_4a2a907fcb069b8d6e65961a7b2e796d6c3a87b1.err | 0 ...sh_4a2a907fcb069b8d6e65961a7b2e796d6c3a87b1.out | 4 + ...sh_6602faf7817c494c33e32da7ee95f13aa9210d01.err | 0 ...sh_6602faf7817c494c33e32da7ee95f13aa9210d01.out | 10 + ...sh_7c2e11488bccc59458b5775db4b90de964858259.err | 0 ...sh_7c2e11488bccc59458b5775db4b90de964858259.out | 6 + ...sh_a7037efd0c4bbf51940137a44e57d94e9307e83e.err | 0 ...sh_a7037efd0c4bbf51940137a44e57d94e9307e83e.out | 1 + ...sh_c18e14a26d8261c9f72747118a469266121d5459.err | 0 ...sh_c18e14a26d8261c9f72747118a469266121d5459.out | 3 + ...sh_e840b674cd65936a72bd64b1dac1524d16fe44c3.err | 0 ...sh_e840b674cd65936a72bd64b1dac1524d16fe44c3.out | 11 + ...sh_154047fb52e4831aabf7d36512247bad6a6a2cf7.err | 7 + ...sh_154047fb52e4831aabf7d36512247bad6a6a2cf7.out | 0 ...sh_3c9b5940f7533c5fc3d4956a6efce50a9e7132d4.err | 11 + ...sh_3c9b5940f7533c5fc3d4956a6efce50a9e7132d4.out | 0 ...sh_41f643bb4f720130625b042563e9591bee4ae588.err | 0 ...sh_41f643bb4f720130625b042563e9591bee4ae588.out | 2 + ...sh_45ff39a3d0ac0ca0c95aaca14d043450cec1cedd.err | 0 ...sh_45ff39a3d0ac0ca0c95aaca14d043450cec1cedd.out | 5 + ...sh_48e85ba0c0945a5085fb4ee255771406061a9c17.err | 0 ...sh_48e85ba0c0945a5085fb4ee255771406061a9c17.out | 6 + ...sh_4c39b356748c67ccf8a6027a1af88da532f8252a.err | 0 ...sh_4c39b356748c67ccf8a6027a1af88da532f8252a.out | 3 + ...sh_7b75763926d832bf9784ca234a060859770aabe7.err | 0 ...sh_7b75763926d832bf9784ca234a060859770aabe7.out | 2 + ...sh_811b1a8a176b25001a89e35b295a1117ab76969b.err | 0 ...sh_811b1a8a176b25001a89e35b295a1117ab76969b.out | 3 + ...sh_83ac877aa9d38b25945cf96d6326a2468187c40f.err | 0 ...sh_83ac877aa9d38b25945cf96d6326a2468187c40f.out | 37 + ...sh_a7489c1f0e001adc732b7e2ab31bb30960fda078.err | 0 ...sh_a7489c1f0e001adc732b7e2ab31bb30960fda078.out | 4 + ...sh_c063f96398650f130941bbbf4cf63c1244fdbee5.err | 0 ...sh_c063f96398650f130941bbbf4cf63c1244fdbee5.out | 3 + ...sh_c75128169049bd88d5eaf8b84a7f617e5ae5d936.err | 0 ...sh_c75128169049bd88d5eaf8b84a7f617e5ae5d936.out | 4 + ...sh_c8fb22932af2467a2651797a8a8d8cddcd09431d.err | 0 ...sh_c8fb22932af2467a2651797a8a8d8cddcd09431d.out | 4 + ...sh_d6af0b41066ca3be0bbce89c83c011f4ecfa516e.err | 0 ...sh_d6af0b41066ca3be0bbce89c83c011f4ecfa516e.out | 5 + ...sh_fd09cb565f44a114d8c9a519e571918e30262eaf.err | 0 ...sh_fd09cb565f44a114d8c9a519e571918e30262eaf.out | 4 + ...sh_fdf4a91aa55262255816dff7d605f1f0a5d6fe92.err | 0 ...sh_fdf4a91aa55262255816dff7d605f1f0a5d6fe92.out | 4 + ...sh_3c255c3c8b28df9d694b329a265e8b8140dae4a2.err | 0 ...sh_3c255c3c8b28df9d694b329a265e8b8140dae4a2.out | 8 + ...sh_4111e649fb49c0a377e552fa0b56c60c370633da.err | 0 ...sh_4111e649fb49c0a377e552fa0b56c60c370633da.out | 4 + ...sh_675a2ff6306df7c54127e39319cf06a2dd353145.err | 0 ...sh_675a2ff6306df7c54127e39319cf06a2dd353145.out | 5 + ...sh_7192f8f68adb14705c8a60e73ff8248c61c7fd03.err | 0 ...sh_7192f8f68adb14705c8a60e73ff8248c61c7fd03.out | 5 + ...sh_a5bee322ea3374690e44a88a16cb6b84feaa11d3.err | 0 ...sh_a5bee322ea3374690e44a88a16cb6b84feaa11d3.out | 3 + ...sh_a6d9042e5e95f2a49194bd80c1eed154813ddf41.err | 0 ...sh_a6d9042e5e95f2a49194bd80c1eed154813ddf41.out | 19 + ...sh_cd361eeca7e91bfab942b75d6c3422c7a456a111.err | 0 ...sh_cd361eeca7e91bfab942b75d6c3422c7a456a111.out | 3 + ...sh_f8feb52a321026d9562b271eb37a2c56dfaed329.err | 0 ...sh_f8feb52a321026d9562b271eb37a2c56dfaed329.out | 1 + ...sh_0fa3663a45aca6a328cb728872af7ed7ee896f1c.err | 2 + ...sh_0fa3663a45aca6a328cb728872af7ed7ee896f1c.out | 0 ...sh_182ae9244db314a953af2bee969726e381bc5a32.err | 3 + ...sh_182ae9244db314a953af2bee969726e381bc5a32.out | 0 ...sh_2158f1f011ba8e1b152396c072790c076fdb8ce8.err | 3 + ...sh_2158f1f011ba8e1b152396c072790c076fdb8ce8.out | 1 + ...sh_281af24141680330791db7f7c5fa70833ce08a6b.err | 1 + ...sh_281af24141680330791db7f7c5fa70833ce08a6b.out | 0 ...sh_35703b13990785632cca82123fb3883797959c0b.err | 0 ...sh_35703b13990785632cca82123fb3883797959c0b.out | 4 + ...sh_366730cac50b4a09b7de4b84641791470b1cb9a3.err | 0 ...sh_366730cac50b4a09b7de4b84641791470b1cb9a3.out | 10 + ...sh_3d18474a3e472fff6e23e0c41337ec9188fee591.err | 34 + ...sh_3d18474a3e472fff6e23e0c41337ec9188fee591.out | 3 + ...sh_442cc58676590a3604d5c2183f5fe0a75c98351a.err | 0 ...sh_442cc58676590a3604d5c2183f5fe0a75c98351a.out | 2 + ...sh_566fd88d216a44bc1c6e23f2d6f2d0caf99d42f9.err | 0 ...sh_566fd88d216a44bc1c6e23f2d6f2d0caf99d42f9.out | 1 + ...sh_5f2f7ecb6ab9cbec4b41385b91bd038906b8a7b2.err | 3 + ...sh_5f2f7ecb6ab9cbec4b41385b91bd038906b8a7b2.out | 0 ...sh_629bde30483e0a6461076e9058f3a5eb81ae0425.err | 3 + ...sh_629bde30483e0a6461076e9058f3a5eb81ae0425.out | 0 ...sh_630db454054cf92ec9bd0f4e3e83300047f583ff.err | 0 ...sh_630db454054cf92ec9bd0f4e3e83300047f583ff.out | 4 + ...sh_771af6f3d29b8350542d5c6e98bdbf4c223cd531.err | 1 + ...sh_771af6f3d29b8350542d5c6e98bdbf4c223cd531.out | 0 ...sh_7991a5b617867cf37c9f7baa85ffa425f7d455a2.err | 5 + ...sh_7991a5b617867cf37c9f7baa85ffa425f7d455a2.out | 0 ...sh_79ee3f5fe71ccec97b2619d8c1f74ca97ffd2243.err | 0 ...sh_79ee3f5fe71ccec97b2619d8c1f74ca97ffd2243.out | 2 + ...sh_7de76c174c58d67bf93e8f01d6d55ebb6a023f10.err | 3 + ...sh_7de76c174c58d67bf93e8f01d6d55ebb6a023f10.out | 0 ...sh_8a43e6657d4f60e68d31eb8302542ca28e80d077.err | 0 ...sh_8a43e6657d4f60e68d31eb8302542ca28e80d077.out | 3 + ...sh_8e93a3b6b941847c71409a297779fbb0a6666a51.err | 3 + ...sh_8e93a3b6b941847c71409a297779fbb0a6666a51.out | 0 ...sh_95c56a9d146ec9a7c2196559d316f928b2ae6ae9.err | 4 + ...sh_95c56a9d146ec9a7c2196559d316f928b2ae6ae9.out | 0 ...sh_9d101ee29c45cdb8c0f117ad736c9a5dd5da5839.err | 1 + ...sh_9d101ee29c45cdb8c0f117ad736c9a5dd5da5839.out | 0 ...sh_c43e07df9b3068696fdc8759c7561135db981b38.err | 2 + ...sh_c43e07df9b3068696fdc8759c7561135db981b38.out | 0 ...sh_cbd859487e4ea011cd6e0f0f114d70158bfd8b43.err | 0 ...sh_cbd859487e4ea011cd6e0f0f114d70158bfd8b43.out | 34 + ...sh_cf6c0a9f0f04e24ce1fae7a0a434830b14447f83.err | 1 + ...sh_cf6c0a9f0f04e24ce1fae7a0a434830b14447f83.out | 0 ...sh_d84597760285c3964b258726341e018f6cd49954.err | 7 + ...sh_d84597760285c3964b258726341e018f6cd49954.out | 0 ...sh_f23e393dbf23d0d8e276e9b7610c7b74d79980f8.err | 0 ...sh_f23e393dbf23d0d8e276e9b7610c7b74d79980f8.out | 15 + ...sh_fc41b6ee90cbf038620151f16d164b361acf82dd.err | 1 + ...sh_fc41b6ee90cbf038620151f16d164b361acf82dd.out | 0 ...sh_0300a1391c33b1c45ddfa90198a6bd0a5404a77f.err | 0 ...sh_0300a1391c33b1c45ddfa90198a6bd0a5404a77f.out | 1 + ...sh_17b85654b929b2a8fc1705a170ced544783292fa.err | 0 ...sh_17b85654b929b2a8fc1705a170ced544783292fa.out | 3 + ...sh_345b0e66dab7b881397c4b38380da81092ab70dd.err | 0 ...sh_345b0e66dab7b881397c4b38380da81092ab70dd.out | 0 ...sh_430b9522ba1a37983138f3c4935cba91b781e415.err | 0 ...sh_430b9522ba1a37983138f3c4935cba91b781e415.out | 0 ...sh_4f13dd3858546b6e04a27e244159d355e368f2ae.err | 0 ...sh_4f13dd3858546b6e04a27e244159d355e368f2ae.out | 0 ...sh_68a89b56c5e7f7db620084cca1eb547cbb19a2c9.err | 0 ...sh_68a89b56c5e7f7db620084cca1eb547cbb19a2c9.out | 4 + ...sh_6d87ff483d5785c58fb271a405ff1c35e4f83cd9.err | 0 ...sh_6d87ff483d5785c58fb271a405ff1c35e4f83cd9.out | 36 + ...sh_858fd0081ed9c46dd81e2f81f1090756f2463558.err | 0 ...sh_858fd0081ed9c46dd81e2f81f1090756f2463558.out | 3 + ...sh_8732dad5481be991ca7f291d9c5451c7b016cea7.err | 0 ...sh_8732dad5481be991ca7f291d9c5451c7b016cea7.out | 33 + ...sh_903b41c950f5f90d7786d7a09bb6e2f217654b15.err | 0 ...sh_903b41c950f5f90d7786d7a09bb6e2f217654b15.out | 0 ...sh_92a98a3e4e3a10bf1f2371d21a8282c5d3d4baa5.err | 0 ...sh_92a98a3e4e3a10bf1f2371d21a8282c5d3d4baa5.out | 2 + ...sh_9978aaa475513f9981840e612f853a7707ffcf90.err | 0 ...sh_9978aaa475513f9981840e612f853a7707ffcf90.out | 11 + ...sh_a92822d121a836140a401fd71535dc4a7a8d5b48.err | 0 ...sh_a92822d121a836140a401fd71535dc4a7a8d5b48.out | 0 ...sh_b3d71a87fcb4e3487f71ccad8c6ce681db220572.err | 0 ...sh_b3d71a87fcb4e3487f71ccad8c6ce681db220572.out | 0 ...sh_b932b33dd087b94d4306dd179c5d4f9ddd394960.err | 0 ...sh_b932b33dd087b94d4306dd179c5d4f9ddd394960.out | 1 + ...sh_ddf45811e9906de9f3930fe802ac7b2cc6e48106.err | 0 ...sh_ddf45811e9906de9f3930fe802ac7b2cc6e48106.out | 1 + ...sh_e39648f425c3f291c9d1c0d14595a019abd0cb48.err | 0 ...sh_e39648f425c3f291c9d1c0d14595a019abd0cb48.out | 33 + ...sh_14dd967cb2af90899c9e5e45d00b676b5a3163aa.err | 0 ...sh_14dd967cb2af90899c9e5e45d00b676b5a3163aa.out | 7 + ...sh_2781f5dd570580cbe746ad91b58a28b8371283b3.err | 0 ...sh_2781f5dd570580cbe746ad91b58a28b8371283b3.out | 7 + ...sh_2af44d06fc137a77bc230be86376ccad23a2806b.err | 0 ...sh_2af44d06fc137a77bc230be86376ccad23a2806b.out | 2 + ...sh_6858e530a8ecb77cbaec1a7507768dd5a1942ac9.err | 0 ...sh_6858e530a8ecb77cbaec1a7507768dd5a1942ac9.out | 5 + ...sh_7f31e16ea2469da7a4328c93c7bcc8e109f84d2f.err | 0 ...sh_7f31e16ea2469da7a4328c93c7bcc8e109f84d2f.out | 7 + ...sh_8aeebcdef56edd783579eaaddaff7c5cc127bb86.err | 0 ...sh_8aeebcdef56edd783579eaaddaff7c5cc127bb86.out | 6 + ...sh_8e9addb0e5b6f4254d81dd89ecf12783109644bb.err | 0 ...sh_8e9addb0e5b6f4254d81dd89ecf12783109644bb.out | 7 + ...sh_90961e6728e96d0a44535a6c9907cc990c10316c.err | 0 ...sh_90961e6728e96d0a44535a6c9907cc990c10316c.out | 6 + ...sh_95c4e861804a5434900fdb4d67b149d1baa2edf4.err | 0 ...sh_95c4e861804a5434900fdb4d67b149d1baa2edf4.out | 5 + ...sh_d7fe5f6b8fc9ba00539fad0fa0bfb08319d8b04b.err | 0 ...sh_d7fe5f6b8fc9ba00539fad0fa0bfb08319d8b04b.out | 6 + ...sh_d9d46422a913e3a06ddbd262933ef5352c30e68f.err | 0 ...sh_d9d46422a913e3a06ddbd262933ef5352c30e68f.out | 9 + ...sh_e0599f0b53d1bd27af767113853f8e84291f137d.err | 0 ...sh_e0599f0b53d1bd27af767113853f8e84291f137d.out | 6 + ...sh_e8fa2239ab17e7563d0c524f5400a79d6ff8bfda.err | 0 ...sh_e8fa2239ab17e7563d0c524f5400a79d6ff8bfda.out | 6 + ...sh_02def66745b063518473df862987747909f56ccc.err | 4 + ...sh_02def66745b063518473df862987747909f56ccc.out | 0 ...sh_0a5d13b62da4cb66a59a51b0240b5fe0b6036b7e.err | 0 ...sh_0a5d13b62da4cb66a59a51b0240b5fe0b6036b7e.out | 2 + ...sh_0d46ee142f80f262c8c14a22751571cc567df525.err | 4 + ...sh_0d46ee142f80f262c8c14a22751571cc567df525.out | 0 ...sh_13429aed81d7edfd47b57e9cdb8a25c43aff35c4.err | 0 ...sh_13429aed81d7edfd47b57e9cdb8a25c43aff35c4.out | 2 + ...sh_1cbb81cfe40ee16332c5c775a74d06b945aa65c2.err | 0 ...sh_1cbb81cfe40ee16332c5c775a74d06b945aa65c2.out | 3 + ...sh_2532083f215ed44630621f18df3dd7b77c06ae10.err | 10 + ...sh_2532083f215ed44630621f18df3dd7b77c06ae10.out | 0 ...sh_26c0d94d7837792144f2d0f866fb3c12a0bd410d.err | 6 + ...sh_26c0d94d7837792144f2d0f866fb3c12a0bd410d.out | 0 ...sh_2959f0c70fca61a07c6c772f193e73022f7794f1.err | 4 + ...sh_2959f0c70fca61a07c6c772f193e73022f7794f1.out | 0 ...sh_2a16a6fd0ff235a7877e1ea93b22d873a3609402.err | 4 + ...sh_2a16a6fd0ff235a7877e1ea93b22d873a3609402.out | 0 ...sh_2cc8a92c6eb73741080b187a2670d309b8171c90.err | 4 + ...sh_2cc8a92c6eb73741080b187a2670d309b8171c90.out | 0 ...sh_2f15b8a38673ac4db45dc6ed2eafe609c332575b.err | 0 ...sh_2f15b8a38673ac4db45dc6ed2eafe609c332575b.out | 3 + ...sh_31df37f254255115611fc321b63374a2fa4a1cd5.err | 0 ...sh_31df37f254255115611fc321b63374a2fa4a1cd5.out | 2 + ...sh_3d77a2092192caf98e141a6039e886ede836f044.err | 4 + ...sh_3d77a2092192caf98e141a6039e886ede836f044.out | 0 ...sh_4090f96ea11a344c1e2939211da778992dab47d8.err | 5 + ...sh_4090f96ea11a344c1e2939211da778992dab47d8.out | 0 ...sh_4629b626c65a85d7a5595571e195b67afca272ba.err | 4 + ...sh_4629b626c65a85d7a5595571e195b67afca272ba.out | 0 ...sh_50c0b2c93b646b848a017764bde8a4282c556e2d.err | 4 + ...sh_50c0b2c93b646b848a017764bde8a4282c556e2d.out | 0 ...sh_528e48a03cdfa7cfbe263a6e22a65606247a8a95.err | 4 + ...sh_528e48a03cdfa7cfbe263a6e22a65606247a8a95.out | 0 ...sh_5532c7a21e3f6b7df3aad10d7bdfbb7a812ae6c7.err | 0 ...sh_5532c7a21e3f6b7df3aad10d7bdfbb7a812ae6c7.out | 2 + ...sh_56047c9470e515bc3e3709354c01e5d50462cde7.err | 0 ...sh_56047c9470e515bc3e3709354c01e5d50462cde7.out | 2 + ...sh_57427f3c4b4ec785ffff7c5802c10db0d3e547cf.err | 5 + ...sh_57427f3c4b4ec785ffff7c5802c10db0d3e547cf.out | 0 ...sh_57edc93426e6767aa44ab2356c55327553dcdc8d.err | 7 + ...sh_57edc93426e6767aa44ab2356c55327553dcdc8d.out | 0 ...sh_5801770f3e0ecc1d62c7a97116d6da1981bbc7bd.err | 4 + ...sh_5801770f3e0ecc1d62c7a97116d6da1981bbc7bd.out | 0 ...sh_5fe26fe4fc22f23f8dbe3a6aab394602886f2971.err | 5 + ...sh_5fe26fe4fc22f23f8dbe3a6aab394602886f2971.out | 0 ...sh_62eb85c9569e71a630d72065238559528a16114c.err | 0 ...sh_62eb85c9569e71a630d72065238559528a16114c.out | 2 + ...sh_6ad9d0adf85c36363f6b24f49950dcdc13dd34ab.err | 6 + ...sh_6ad9d0adf85c36363f6b24f49950dcdc13dd34ab.out | 0 ...sh_6edb0c8d5323d1b962d90dd6ecdd7eee9008d7b5.err | 6 + ...sh_6edb0c8d5323d1b962d90dd6ecdd7eee9008d7b5.out | 0 ...sh_753c343a256d1286750314957d1b4e155464e03e.err | 0 ...sh_753c343a256d1286750314957d1b4e155464e03e.out | 2 + ...sh_764306f0e5f610ba71f521ba3d19fe158ece0ba5.err | 0 ...sh_764306f0e5f610ba71f521ba3d19fe158ece0ba5.out | 2 + ...sh_7f664c9cda0ae1c48333e21051b5e0eeafd5b4bc.err | 6 + ...sh_7f664c9cda0ae1c48333e21051b5e0eeafd5b4bc.out | 0 ...sh_85fe3b9803254ea54b864d4865d7bd4d7a7f86c6.err | 0 ...sh_85fe3b9803254ea54b864d4865d7bd4d7a7f86c6.out | 4 + ...sh_8ee288f1508eaab0367e465e9f382e848f3282aa.err | 0 ...sh_8ee288f1508eaab0367e465e9f382e848f3282aa.out | 4 + ...sh_9a209f3ee1b1f543ca2587b695d2eb0e63e74c51.err | 6 + ...sh_9a209f3ee1b1f543ca2587b695d2eb0e63e74c51.out | 0 ...sh_9b03e9f7a1bc35e408b3a17ee90cfdadea164df6.err | 0 ...sh_9b03e9f7a1bc35e408b3a17ee90cfdadea164df6.out | 4 + ...sh_9ceccab07fbf7130bffe3c201c710719e4a3e9af.err | 6 + ...sh_9ceccab07fbf7130bffe3c201c710719e4a3e9af.out | 0 ...sh_9e1d05b821822ee40e13fadb24ec558f4bfcff10.err | 4 + ...sh_9e1d05b821822ee40e13fadb24ec558f4bfcff10.out | 0 ...sh_a6b68b9f0044d18e7fa8f9287ddc9110701edc33.err | 4 + ...sh_a6b68b9f0044d18e7fa8f9287ddc9110701edc33.out | 0 ...sh_ae7b1f1684e14bf9c16e0d789257b6ef57cfb2b1.err | 0 ...sh_ae7b1f1684e14bf9c16e0d789257b6ef57cfb2b1.out | 2 + ...sh_afe9cdc4898df5c4e112c13dfe3db6dc089c0d7c.err | 0 ...sh_afe9cdc4898df5c4e112c13dfe3db6dc089c0d7c.out | 4 + ...sh_b085d26043f9661d70f82cb90ecb3c5245d25eac.err | 0 ...sh_b085d26043f9661d70f82cb90ecb3c5245d25eac.out | 4 + ...sh_b2694e4fbecdd128798af25ee0d069e7e35fb499.err | 4 + ...sh_b2694e4fbecdd128798af25ee0d069e7e35fb499.out | 0 ...sh_b5aa0561a65de7e8e22085db184c72a94b1a89a9.err | 0 ...sh_b5aa0561a65de7e8e22085db184c72a94b1a89a9.out | 2 + ...sh_bad03a996c0750733ab99c592b9011851f521a69.err | 0 ...sh_bad03a996c0750733ab99c592b9011851f521a69.out | 5 + ...sh_bd46ca4560f8be6307a914e39539bbac0368080a.err | 0 ...sh_bd46ca4560f8be6307a914e39539bbac0368080a.out | 2 + ...sh_c20b0320096342c180146a5d18a6de82319d70b2.err | 4 + ...sh_c20b0320096342c180146a5d18a6de82319d70b2.out | 0 ...sh_c353ef036c505b75996252138fbd4c8d22e8149c.err | 0 ...sh_c353ef036c505b75996252138fbd4c8d22e8149c.out | 4 + ...sh_c5b8da04734fadf3b9eea80e0af997e38e0fb811.err | 0 ...sh_c5b8da04734fadf3b9eea80e0af997e38e0fb811.out | 3 + ...sh_c73dec2706fc0b9a124f5da3a83f40d8d3255beb.err | 4 + ...sh_c73dec2706fc0b9a124f5da3a83f40d8d3255beb.out | 0 ...sh_c7e1dbf4605914720b55787785abfafdf2c4178a.err | 0 ...sh_c7e1dbf4605914720b55787785abfafdf2c4178a.out | 2 + ...sh_cc77a633a66d1778705a34e3657737547b3fb08d.err | 0 ...sh_cc77a633a66d1778705a34e3657737547b3fb08d.out | 2 + ...sh_dd540973a0dc86320d84706845a15608196ae5be.err | 10 + ...sh_dd540973a0dc86320d84706845a15608196ae5be.out | 0 ...sh_e70dc7d2b686c7f91c2b41b10f3920c50f3ea405.err | 0 ...sh_e70dc7d2b686c7f91c2b41b10f3920c50f3ea405.out | 1 + ...sh_ff8a978fc0de0fed675a3cd1454cf435a6856fd5.err | 0 ...sh_ff8a978fc0de0fed675a3cd1454cf435a6856fd5.out | 3 + ...sh_028d5d5af2f3519b59d349d41cb7ecf385253b51.err | 0 ...sh_028d5d5af2f3519b59d349d41cb7ecf385253b51.out | 5 + ...sh_0a37c43350ddd7a2d0d75695be32fac083ad04a4.err | 0 ...sh_0a37c43350ddd7a2d0d75695be32fac083ad04a4.out | 4 + ...sh_1151e5b727f6b57070bf2c8f047f1d7e02b803a6.err | 0 ...sh_1151e5b727f6b57070bf2c8f047f1d7e02b803a6.out | 6 + ...sh_1b29488b949c294479aa6054f80a35bc106b454b.err | 0 ...sh_1b29488b949c294479aa6054f80a35bc106b454b.out | 2 + ...sh_331a152080d2e278b7cc0a37728eca1ded36ed72.err | 0 ...sh_331a152080d2e278b7cc0a37728eca1ded36ed72.out | 5 + ...sh_4ca92f0da538c2f9d524211a021b306af0d2740d.err | 0 ...sh_4ca92f0da538c2f9d524211a021b306af0d2740d.out | 7 + ...sh_73814eca259e469b57bf7469787b91e8e8569b17.err | 0 ...sh_73814eca259e469b57bf7469787b91e8e8569b17.out | 7 + ...sh_74bc5fb90a0c94a1a37d30a8e9254ea02c192a75.err | 0 ...sh_74bc5fb90a0c94a1a37d30a8e9254ea02c192a75.out | 6 + ...sh_7b183037479528581e1eacace7b9acae41c5aa8e.err | 0 ...sh_7b183037479528581e1eacace7b9acae41c5aa8e.out | 12 + ...sh_96ebdc277ae760e1b6efae3195ff678654b04e52.err | 0 ...sh_96ebdc277ae760e1b6efae3195ff678654b04e52.out | 7 + ...sh_99da5994c8c90536dbdb1b8ad7dbfb41698a5e8c.err | 0 ...sh_99da5994c8c90536dbdb1b8ad7dbfb41698a5e8c.out | 10 + ...sh_b1a2ddce48beb3e4b1e3ca4b4229a7c21b83b7c4.err | 0 ...sh_b1a2ddce48beb3e4b1e3ca4b4229a7c21b83b7c4.out | 6 + ...sh_be6839712d088fc7b31618ed90f8ce706c35a9c0.err | 0 ...sh_be6839712d088fc7b31618ed90f8ce706c35a9c0.out | 7 + ...sh_c879ba94fdc1a099cf56bd33e5b3e9be65310036.err | 0 ...sh_c879ba94fdc1a099cf56bd33e5b3e9be65310036.out | 10 + ...sh_c909647ed0e585002074f55c946f3033df1815b2.err | 0 ...sh_c909647ed0e585002074f55c946f3033df1815b2.out | 6 + ...sh_ce0506ee7a12eb0f7b970522cc6a79180ecb20cc.err | 0 ...sh_ce0506ee7a12eb0f7b970522cc6a79180ecb20cc.out | 11 + ...sh_f3c64191d6016767a5857fbb1bad26548586bb96.err | 0 ...sh_f3c64191d6016767a5857fbb1bad26548586bb96.out | 10 + ...sh_077cab6e271c914daf5b221cc512853077891f35.err | 0 ...sh_077cab6e271c914daf5b221cc512853077891f35.out | 2 + ...sh_0ce56741d3c34af274c8ddb4b90c4e5749d05971.err | 0 ...sh_0ce56741d3c34af274c8ddb4b90c4e5749d05971.out | 2 + ...sh_180ad44fe073cc9642da642af1f442adfd98ec62.err | 0 ...sh_180ad44fe073cc9642da642af1f442adfd98ec62.out | 2 + ...sh_2230714a0b2ab6aca9ddfe686734f313cef5a96b.err | 0 ...sh_2230714a0b2ab6aca9ddfe686734f313cef5a96b.out | 2 + ...sh_68515cfd0a50880f6dfc8f9810c9e761493ebb12.err | 0 ...sh_68515cfd0a50880f6dfc8f9810c9e761493ebb12.out | 2 + ...sh_6de2a86c53883ec4430b98edd06b0c0cdf23e741.err | 0 ...sh_6de2a86c53883ec4430b98edd06b0c0cdf23e741.out | 2 + ...sh_918178c6dd9d70d0432ededfde5af5e53c094385.err | 0 ...sh_918178c6dd9d70d0432ededfde5af5e53c094385.out | 2 + ...sh_c76a24a209987e4c668c87588c12b8f34294b144.err | 0 ...sh_c76a24a209987e4c668c87588c12b8f34294b144.out | 2 + ...sh_cacb045d2bce6dc298c4da3d96bdc34dab2404df.err | 0 ...sh_cacb045d2bce6dc298c4da3d96bdc34dab2404df.out | 2 + ...sh_cae4bc239c924bbc05a0b099b63f0e3af7560976.err | 0 ...sh_cae4bc239c924bbc05a0b099b63f0e3af7560976.out | 2 + ...sh_d4e3c9f7a38458726900731d2b71c104d591ef14.err | 0 ...sh_d4e3c9f7a38458726900731d2b71c104d591ef14.out | 2 + ...sh_d5c8f7ab91c3dbe46add7e08f532b17797d9975c.err | 0 ...sh_d5c8f7ab91c3dbe46add7e08f532b17797d9975c.out | 2 + ...sh_eb2c424733ce978d1b6d1dcb93d6e45af7c8fa96.err | 0 ...sh_eb2c424733ce978d1b6d1dcb93d6e45af7c8fa96.out | 2 + ...sh_f045e94d921bfcfbded83ee681bf11445a99ff6d.err | 0 ...sh_f045e94d921bfcfbded83ee681bf11445a99ff6d.out | 2 + ...sh_109ff42de817b56a9082f605f63af71c0db8c9d7.err | 0 ...sh_109ff42de817b56a9082f605f63af71c0db8c9d7.out | 2 + ...sh_17b09f79bfcac1762153ec9650fb1e545a24d8a3.err | 0 ...sh_17b09f79bfcac1762153ec9650fb1e545a24d8a3.out | 2 + ...sh_18ddc138b263dd06f3fe81fec05bc4330caffef7.err | 0 ...sh_18ddc138b263dd06f3fe81fec05bc4330caffef7.out | 2 + ...sh_20a76db446a0a558dcbdf41033f97d4a22ca1bfa.err | 0 ...sh_20a76db446a0a558dcbdf41033f97d4a22ca1bfa.out | 2 + ...sh_2c3f66e78deb8721b1d1fe5a787e9958895401d7.err | 0 ...sh_2c3f66e78deb8721b1d1fe5a787e9958895401d7.out | 2 + ...sh_3ed11101a413e47c3dfe219557b7a6df04a64253.err | 0 ...sh_3ed11101a413e47c3dfe219557b7a6df04a64253.out | 2 + ...sh_469380561dccd79c7249562067107c330838eaad.err | 0 ...sh_469380561dccd79c7249562067107c330838eaad.out | 2 + ...sh_54b004f301907860d360434b37fd6c81fcc12f99.err | 0 ...sh_54b004f301907860d360434b37fd6c81fcc12f99.out | 2 + ...sh_73df81c6889d1f06fb3f3b6bf30c6046b3f52c8b.err | 0 ...sh_73df81c6889d1f06fb3f3b6bf30c6046b3f52c8b.out | 2 + ...sh_74ca242a126316bcb82ccefd9369f9e43b7fd2e1.err | 0 ...sh_74ca242a126316bcb82ccefd9369f9e43b7fd2e1.out | 2 + ...sh_7b116cb0ab7a28b866e0d2b80fe8ef0cd25f2aa3.err | 0 ...sh_7b116cb0ab7a28b866e0d2b80fe8ef0cd25f2aa3.out | 2 + ...sh_7b5d7dd8d0003ab83e3e5cb0a5ce802fe9a0e3b3.err | 0 ...sh_7b5d7dd8d0003ab83e3e5cb0a5ce802fe9a0e3b3.out | 2 + ...sh_917ffde411c1425e8a6addae0170900dcd553986.err | 0 ...sh_917ffde411c1425e8a6addae0170900dcd553986.out | 2 + ...sh_9e2c0a90ce333365ff7354375f2c609bc27135c8.err | 1 + ...sh_9e2c0a90ce333365ff7354375f2c609bc27135c8.out | 0 ...sh_a247b137e71124e496f1beab56c7fe85717c4199.err | 0 ...sh_a247b137e71124e496f1beab56c7fe85717c4199.out | 2 + ...sh_b66242975fd6ecb7260cd96ac29accaf4f4af6ae.err | 0 ...sh_b66242975fd6ecb7260cd96ac29accaf4f4af6ae.out | 2 + ...sh_c5d78cfbf5594cc27590277353c08a92e2497622.err | 0 ...sh_c5d78cfbf5594cc27590277353c08a92e2497622.out | 2 + ...sh_cc402803bf14ee3673089c575f1af87220cb6a72.err | 0 ...sh_cc402803bf14ee3673089c575f1af87220cb6a72.out | 2 + ...sh_cf307d87104e99a1858bb7c4f28ea3068340f188.err | 0 ...sh_cf307d87104e99a1858bb7c4f28ea3068340f188.out | 2 + ...sh_cf670dfa1ae7ac5a074baa642068c6d26ac8e096.err | 1 + ...sh_cf670dfa1ae7ac5a074baa642068c6d26ac8e096.out | 0 ...sh_d51ad77cd67a2a691838c9d95142638df1c07360.err | 0 ...sh_d51ad77cd67a2a691838c9d95142638df1c07360.out | 2 + ...sh_e24cf3f35643f945392e7d7a4ca82fea98b4519e.err | 0 ...sh_e24cf3f35643f945392e7d7a4ca82fea98b4519e.out | 2 + ...sh_f31f240313ddec806aa6f353ceed707dfd9aaf16.err | 0 ...sh_f31f240313ddec806aa6f353ceed707dfd9aaf16.out | 2 + ...sh_026dd9752b6101e0791689d3a2026f7e517e36f5.err | 0 ...sh_026dd9752b6101e0791689d3a2026f7e517e36f5.out | 0 ...sh_1614ebb5e2e83bab11023354dea8a0885ddf64b4.err | 0 ...sh_1614ebb5e2e83bab11023354dea8a0885ddf64b4.out | 3 + ...sh_541a8e35f34a206e340a3880128b6ce137847872.err | 0 ...sh_541a8e35f34a206e340a3880128b6ce137847872.out | 5 + ...sh_59a1497c13a5e09bc8f95ef02552b2835ebea6e5.err | 0 ...sh_59a1497c13a5e09bc8f95ef02552b2835ebea6e5.out | 2 + ...sh_69fd19d56a8cd1fc9c7eb9351270eabb491f8233.err | 0 ...sh_69fd19d56a8cd1fc9c7eb9351270eabb491f8233.out | 5 + ...sh_6f707b6e856dbaab6f95e7e89b98dc3652021f85.err | 0 ...sh_6f707b6e856dbaab6f95e7e89b98dc3652021f85.out | 3 + ...sh_b615b6737b1e0d383c8ce4a1db56332f11dbc158.err | 0 ...sh_b615b6737b1e0d383c8ce4a1db56332f11dbc158.out | 2 + ...sh_dab07d8de7728752ae938a174468d75e85f3ae7e.err | 0 ...sh_dab07d8de7728752ae938a174468d75e85f3ae7e.out | 2 + ...sh_f7681c234d4f60df16c997a05163aeb058c52870.err | 0 ...sh_f7681c234d4f60df16c997a05163aeb058c52870.out | 5 + ...sh_017d24148f3e28f719429b709f4aa5478f458443.err | 0 ...sh_017d24148f3e28f719429b709f4aa5478f458443.out | 2 + ...sh_026077f4d573ee034467065b7e4f1878bdd4e2f2.err | 4 + ...sh_026077f4d573ee034467065b7e4f1878bdd4e2f2.out | 0 ...sh_191436b38db80b1dd9e7e0814c31c5fa7239dc51.err | 0 ...sh_191436b38db80b1dd9e7e0814c31c5fa7239dc51.out | 3 + ...sh_1a74914cbf12fcd5c06935b992f6355acdbcf2d8.err | 0 ...sh_1a74914cbf12fcd5c06935b992f6355acdbcf2d8.out | 2 + ...sh_1c1a2d438d2bde95abd9a859d113c3661e650a36.err | 0 ...sh_1c1a2d438d2bde95abd9a859d113c3661e650a36.out | 2 + ...sh_238417283b8e5db23c992f966e3f106bd178f7d0.err | 0 ...sh_238417283b8e5db23c992f966e3f106bd178f7d0.out | 2 + ...sh_32459ba8e8bb9a1d9e63b6c67059d7f065cf4301.err | 0 ...sh_32459ba8e8bb9a1d9e63b6c67059d7f065cf4301.out | 2 + ...sh_39c13797278d765c027d3581a0b6e0574f5c56eb.err | 0 ...sh_39c13797278d765c027d3581a0b6e0574f5c56eb.out | 2 + ...sh_3cf4b66d40c4b1979ff14a9eccad8bd5ac48151c.err | 0 ...sh_3cf4b66d40c4b1979ff14a9eccad8bd5ac48151c.out | 2 + ...sh_4192f378e320cb3f2c3c228b63ec65de92044704.err | 0 ...sh_4192f378e320cb3f2c3c228b63ec65de92044704.out | 2 + ...sh_57c3aecdced547b837177ab02d3776361363e48d.err | 1 + ...sh_57c3aecdced547b837177ab02d3776361363e48d.out | 0 ...sh_5b4a95677a1fc7d11f4b87d92165f56a60a65828.err | 0 ...sh_5b4a95677a1fc7d11f4b87d92165f56a60a65828.out | 2 + ...sh_5f2feef079a51410e1f8661bfe92da1c3277f665.err | 1 + ...sh_5f2feef079a51410e1f8661bfe92da1c3277f665.out | 0 ...sh_61417198a652aab93e9495b6e8cf3a634af175c6.err | 0 ...sh_61417198a652aab93e9495b6e8cf3a634af175c6.out | 2 + ...sh_79ab816ac01c9902ddbb0f6f20392ab2f2cd6172.err | 0 ...sh_79ab816ac01c9902ddbb0f6f20392ab2f2cd6172.out | 3 + ...sh_7c01aaf09078aaa3f23d127f9e03a317dca066de.err | 0 ...sh_7c01aaf09078aaa3f23d127f9e03a317dca066de.out | 2 + ...sh_80c97b22084a06fd765ad22c935616c578968d07.err | 0 ...sh_80c97b22084a06fd765ad22c935616c578968d07.out | 2 + ...sh_83d8615c9ce5dfab5e4373570c1b68b8608155f5.err | 0 ...sh_83d8615c9ce5dfab5e4373570c1b68b8608155f5.out | 2 + ...sh_8cae9740ddfd6ba4c865fca0117b7bea3bb556e5.err | 0 ...sh_8cae9740ddfd6ba4c865fca0117b7bea3bb556e5.out | 2 + ...sh_8e229f1b5fa3d3803e9db2f295a8d1a490e1b3db.err | 0 ...sh_8e229f1b5fa3d3803e9db2f295a8d1a490e1b3db.out | 2 + ...sh_8e3724c90bf96dff5d8ba3cfaf4b7e2eaa9e5f66.err | 1 + ...sh_8e3724c90bf96dff5d8ba3cfaf4b7e2eaa9e5f66.out | 0 ...sh_93ba3ba52b0dd2d5a3ba43bcb7c3638c05ecfe75.err | 0 ...sh_93ba3ba52b0dd2d5a3ba43bcb7c3638c05ecfe75.out | 2 + ...sh_97aa53b581838f5875fe2beda8d1cb245a24f3d6.err | 0 ...sh_97aa53b581838f5875fe2beda8d1cb245a24f3d6.out | 2 + ...sh_98a83bc899a78c04d1fdb390b2c1e403c35428c7.err | 0 ...sh_98a83bc899a78c04d1fdb390b2c1e403c35428c7.out | 2 + ...sh_98ce02dff32d955466524bb167fa45fdf8591788.err | 0 ...sh_98ce02dff32d955466524bb167fa45fdf8591788.out | 2 + ...sh_9ab4f51486d7cc99c584721bf0e50c223dac4f18.err | 0 ...sh_9ab4f51486d7cc99c584721bf0e50c223dac4f18.out | 2 + ...sh_9d260ed24b28579ef1dbed25b10c42741e52b023.err | 0 ...sh_9d260ed24b28579ef1dbed25b10c42741e52b023.out | 2 + ...sh_9fbfe3c93467666c45b643f3b8ba990a294c17ff.err | 0 ...sh_9fbfe3c93467666c45b643f3b8ba990a294c17ff.out | 2 + ...sh_a4ffc64f89cf9917fbc918227fd3c05e54d9e8b5.err | 0 ...sh_a4ffc64f89cf9917fbc918227fd3c05e54d9e8b5.out | 2 + ...sh_a5e179607645aefce14b9fd12ddef34107afe337.err | 0 ...sh_a5e179607645aefce14b9fd12ddef34107afe337.out | 2 + ...sh_b2fc37822e29f7f59497a02a8968c680b545ee1d.err | 0 ...sh_b2fc37822e29f7f59497a02a8968c680b545ee1d.out | 2 + ...sh_bbd979ed74b46ae1696ed7312a48a436bcf99ec0.err | 0 ...sh_bbd979ed74b46ae1696ed7312a48a436bcf99ec0.out | 2 + ...sh_c1ae603d969a5b106328287523c0ddfed07146ad.err | 0 ...sh_c1ae603d969a5b106328287523c0ddfed07146ad.out | 2 + ...sh_e0ab80f50fb008700ab6cfb90694ed014d40e44b.err | 1 + ...sh_e0ab80f50fb008700ab6cfb90694ed014d40e44b.out | 0 ...sh_ebafb98307f307ae8d8ab6921c32929aab3a1a16.err | 0 ...sh_ebafb98307f307ae8d8ab6921c32929aab3a1a16.out | 2 + ...sh_ee36fbea10a33ca106a211feb05d61ecf8e74634.err | 0 ...sh_ee36fbea10a33ca106a211feb05d61ecf8e74634.out | 2 + ...sh_f1cbc70771cc75520f807261eac3a88dc2d8fe6b.err | 0 ...sh_f1cbc70771cc75520f807261eac3a88dc2d8fe6b.out | 2 + ...sh_f34205b59e04f261897ad89f659595c743a18ca9.err | 0 ...sh_f34205b59e04f261897ad89f659595c743a18ca9.out | 3 + ...sh_f34f5dfa938a1ac7721f924beb16bbceec127a1b.err | 4 + ...sh_f34f5dfa938a1ac7721f924beb16bbceec127a1b.out | 0 ...sh_03257c56e85558aa0cc925b68d3af962afc25125.err | 0 ...sh_03257c56e85558aa0cc925b68d3af962afc25125.out | 4 + ...sh_51293df041b6969ccecc60204dce3676d0fb006d.err | 0 ...sh_51293df041b6969ccecc60204dce3676d0fb006d.out | 2 + ...sh_b841a0c09601e2419eeb99e85f7e286c889e4801.err | 0 ...sh_b841a0c09601e2419eeb99e85f7e286c889e4801.out | 27 + ...sh_bbd1128cf61a9af8f9dc937b46217443f42e1a7a.err | 0 ...sh_bbd1128cf61a9af8f9dc937b46217443f42e1a7a.out | 2 + ...sh_d42e1fcfe6d42394f79da84be2d37e62c4c0ea63.err | 9 + ...sh_d42e1fcfe6d42394f79da84be2d37e62c4c0ea63.out | 0 ...sh_d61af17ff19d640ddfc879460910991825eedd05.err | 0 ...sh_d61af17ff19d640ddfc879460910991825eedd05.out | 2 + ...sh_ed6e9f13f178def009ee58c2aeea8c3c70fdb580.err | 0 ...sh_ed6e9f13f178def009ee58c2aeea8c3c70fdb580.out | 2 + ...sh_1a0d872ebc492fcecb2e79a0993170d5fc771a5b.err | 0 ...sh_1a0d872ebc492fcecb2e79a0993170d5fc771a5b.out | 2 + ...sh_3f5f74863d065418bca5a000e6ad3d9344635164.err | 0 ...sh_3f5f74863d065418bca5a000e6ad3d9344635164.out | 12 + ...sh_5aaae556ecb1661602f176215e28f661d3404032.err | 0 ...sh_5aaae556ecb1661602f176215e28f661d3404032.out | 4 + ...sh_df0fd242f57a96d40f466493938cda0789a094fa.err | 0 ...sh_df0fd242f57a96d40f466493938cda0789a094fa.out | 24 + ...sh_ef9373a76853f345d06234f6e0fe11b5d40da27b.err | 0 ...sh_ef9373a76853f345d06234f6e0fe11b5d40da27b.out | 6 + ...sh_005b9365ac99596e539f47c9fe432668c209b21f.err | 0 ...sh_005b9365ac99596e539f47c9fe432668c209b21f.out | 2 + ...sh_04712488fe50554eb36d3ced80f9a033602f3daa.err | 0 ...sh_04712488fe50554eb36d3ced80f9a033602f3daa.out | 2 + ...sh_0947bfe7ec626eaa0409a45b10fcbb634fb12eb7.err | 0 ...sh_0947bfe7ec626eaa0409a45b10fcbb634fb12eb7.out | 2 + ...sh_11bcc5d32eabbedb6974f160dace9ef1ef0009e9.err | 0 ...sh_11bcc5d32eabbedb6974f160dace9ef1ef0009e9.out | 64 + ...sh_11d458fdadd00df1239a0eeaac049abb49ed212d.err | 0 ...sh_11d458fdadd00df1239a0eeaac049abb49ed212d.out | 198 + ...sh_129e58679e72f3cc5864812026e49a7917baf3d0.err | 0 ...sh_129e58679e72f3cc5864812026e49a7917baf3d0.out | 2 + ...sh_151a0fd71ef6837c8cbd8a67e315019b5812b079.err | 0 ...sh_151a0fd71ef6837c8cbd8a67e315019b5812b079.out | 8 + ...sh_1e7362ac3d9690b1b2cfbd320b6129c46ecfbb8a.err | 1 + ...sh_1e7362ac3d9690b1b2cfbd320b6129c46ecfbb8a.out | 0 ...sh_211c5428db0590795072c31cb116ef35281e02b5.err | 0 ...sh_211c5428db0590795072c31cb116ef35281e02b5.out | 2 + ...sh_2f189f0785bb81a1298db35e9e166983b633c73f.err | 0 ...sh_2f189f0785bb81a1298db35e9e166983b633c73f.out | 8 + ...sh_30f65162174b886130b94a5dd1f094e7f09debed.err | 0 ...sh_30f65162174b886130b94a5dd1f094e7f09debed.out | 2 + ...sh_352434d199f7b493668c9f2774472eb69ef0d9f0.err | 0 ...sh_352434d199f7b493668c9f2774472eb69ef0d9f0.out | 2 + ...sh_36fc9005464f1106f969559e640d9fa36d5fadad.err | 0 ...sh_36fc9005464f1106f969559e640d9fa36d5fadad.out | 2 + ...sh_3855d2cc0ab29171cae8e722f130adec25eae36e.err | 1 + ...sh_3855d2cc0ab29171cae8e722f130adec25eae36e.out | 0 ...sh_3de72fe5c1751dd212a1cd45cf2caa7f3b52bced.err | 0 ...sh_3de72fe5c1751dd212a1cd45cf2caa7f3b52bced.out | 2 + ...sh_4b402274da152135c6c99456b693e1ecabca0256.err | 0 ...sh_4b402274da152135c6c99456b693e1ecabca0256.out | 2 + ...sh_51055e40d709332ee772ba5719039314bbf5e411.err | 0 ...sh_51055e40d709332ee772ba5719039314bbf5e411.out | 2 + ...sh_51766b600fd158a9e0677f6b0fa31b83537b2e5b.err | 0 ...sh_51766b600fd158a9e0677f6b0fa31b83537b2e5b.out | 2 + ...sh_5203db1a4a81e43a693f339fd26e1ed635da9d5a.err | 0 ...sh_5203db1a4a81e43a693f339fd26e1ed635da9d5a.out | 2 + ...sh_5abe3717393fba14ec510a37b4b94fedc67aae8e.err | 0 ...sh_5abe3717393fba14ec510a37b4b94fedc67aae8e.out | 2 + ...sh_5e436fbd4efb140600999c5208886a5a57b8a30e.err | 0 ...sh_5e436fbd4efb140600999c5208886a5a57b8a30e.out | 24 + ...sh_5f9979fa5ce7b76efe714bb27ffbe9f5927ae941.err | 0 ...sh_5f9979fa5ce7b76efe714bb27ffbe9f5927ae941.out | 2 + ...sh_60a005a9f0d44ad022b5554415319933d5743c51.err | 0 ...sh_60a005a9f0d44ad022b5554415319933d5743c51.out | 3 + ...sh_660288b48d9b30244621d873944938f7ef043976.err | 0 ...sh_660288b48d9b30244621d873944938f7ef043976.out | 2 + ...sh_6607c0dd8baff16930eb3e0daf6354af5b50052b.err | 0 ...sh_6607c0dd8baff16930eb3e0daf6354af5b50052b.out | 2 + ...sh_69f5d49e62da48e188bd9d6af4bd3adeb21eb7d1.err | 0 ...sh_69f5d49e62da48e188bd9d6af4bd3adeb21eb7d1.out | 6 + ...sh_6ff984d8ed3e5099376d19f0dd20d5fd1ed42494.err | 0 ...sh_6ff984d8ed3e5099376d19f0dd20d5fd1ed42494.out | 2 + ...sh_71f37db33504b2c08a7a3323c482556f53d88100.err | 0 ...sh_71f37db33504b2c08a7a3323c482556f53d88100.out | 16 + ...sh_77fc174faeec1eda687a9373dbdbdd1aaef56e20.err | 0 ...sh_77fc174faeec1eda687a9373dbdbdd1aaef56e20.out | 3 + ...sh_790da4aab5af901feeff5426790876eb91b967cb.err | 1 + ...sh_790da4aab5af901feeff5426790876eb91b967cb.out | 0 ...sh_7a544cd702579c1fab35870428788ad763cf1143.err | 0 ...sh_7a544cd702579c1fab35870428788ad763cf1143.out | 2 + ...sh_7b6e7c26e8a80459fef55d56156d6ff93c00bd49.err | 0 ...sh_7b6e7c26e8a80459fef55d56156d6ff93c00bd49.out | 2 + ...sh_7c1e7604ac050e7047201638dca0a6b0fcfd8bdf.err | 0 ...sh_7c1e7604ac050e7047201638dca0a6b0fcfd8bdf.out | 2 + ...sh_7f751009d0db15fc97f9113c5c84db05ff1de9c3.err | 0 ...sh_7f751009d0db15fc97f9113c5c84db05ff1de9c3.out | 2 + ...sh_805ca5e97fbf1ed56f2e920befd963255ba190b6.err | 0 ...sh_805ca5e97fbf1ed56f2e920befd963255ba190b6.out | 2 + ...sh_80c1fb9affbfac609ebf1cc5556aafb1ecd223c1.err | 1 + ...sh_80c1fb9affbfac609ebf1cc5556aafb1ecd223c1.out | 0 ...sh_836e3f721a0f945ad27e7aa241121ba739aab618.err | 0 ...sh_836e3f721a0f945ad27e7aa241121ba739aab618.out | 2 + ...sh_838e9bc7873b2b238157ba0358e0dfd6a01d837d.err | 0 ...sh_838e9bc7873b2b238157ba0358e0dfd6a01d837d.out | 2 + ...sh_84e77dedec887c5e2433dbc5b130000cd88963bd.err | 0 ...sh_84e77dedec887c5e2433dbc5b130000cd88963bd.out | 2 + ...sh_887afe94962d958aca2e03f7873d58ca93e190b5.err | 1 + ...sh_887afe94962d958aca2e03f7873d58ca93e190b5.out | 0 ...sh_8c9ef83431ea75050fd16824075bf72056cf5f53.err | 0 ...sh_8c9ef83431ea75050fd16824075bf72056cf5f53.out | 2 + ...sh_8cef54f0617960320b5d3615068eb27333dcf6a3.err | 0 ...sh_8cef54f0617960320b5d3615068eb27333dcf6a3.out | 2 + ...sh_8f4f0ed74c4dc6b821e02a44552b694614cd9353.err | 0 ...sh_8f4f0ed74c4dc6b821e02a44552b694614cd9353.out | 2 + ...sh_949ffd5b2ef9fbcbe17f2e61ef7750f7038f6fd6.err | 0 ...sh_949ffd5b2ef9fbcbe17f2e61ef7750f7038f6fd6.out | 2 + ...sh_a4d84a0082a7df34c95c2e6e070bbf6effaa5594.err | 0 ...sh_a4d84a0082a7df34c95c2e6e070bbf6effaa5594.out | 2 + ...sh_a515ba81cc3655c602da28cd0fa1a186d5e9a6e1.err | 1 + ...sh_a515ba81cc3655c602da28cd0fa1a186d5e9a6e1.out | 0 ...sh_a65d2fb2f841578619528ca10168ca4d650218e9.err | 0 ...sh_a65d2fb2f841578619528ca10168ca4d650218e9.out | 3 + ...sh_ac7ecdda0fcc4279a4694291edaa2f1411f5262e.err | 0 ...sh_ac7ecdda0fcc4279a4694291edaa2f1411f5262e.out | 2 + ...sh_b088735cf46f23ca3d5fb3da41f07a6a3b1cba35.err | 0 ...sh_b088735cf46f23ca3d5fb3da41f07a6a3b1cba35.out | 2 + ...sh_b0e5bf23bbbc0defa8bb26817782c9d46a778ad8.err | 0 ...sh_b0e5bf23bbbc0defa8bb26817782c9d46a778ad8.out | 16 + ...sh_b2aafbcaa7befe426d3f9df71c24f16fdc9d2856.err | 0 ...sh_b2aafbcaa7befe426d3f9df71c24f16fdc9d2856.out | 3 + ...sh_b81b27abfafbd357d41c407428d41ae0f4bb75e2.err | 0 ...sh_b81b27abfafbd357d41c407428d41ae0f4bb75e2.out | 2 + ...sh_bac7f6531a2adf70cd1871fb13eab26dff133b7c.err | 0 ...sh_bac7f6531a2adf70cd1871fb13eab26dff133b7c.out | 2 + ...sh_bfb7088916412360f77683009058b0747784630a.err | 0 ...sh_bfb7088916412360f77683009058b0747784630a.out | 2 + ...sh_bfe8b09e23389af0ef14359b66d68228d0285185.err | 1 + ...sh_bfe8b09e23389af0ef14359b66d68228d0285185.out | 0 ...sh_c26269b10b9b9e8485aa97c2be2afb2cc3ee910d.err | 0 ...sh_c26269b10b9b9e8485aa97c2be2afb2cc3ee910d.out | 2 + ...sh_c9e2f41431bef879364dc37a472ab01f64d89f89.err | 0 ...sh_c9e2f41431bef879364dc37a472ab01f64d89f89.out | 2 + ...sh_cc53348c585ee71a7456157ad6b125689813bafe.err | 0 ...sh_cc53348c585ee71a7456157ad6b125689813bafe.out | 2 + ...sh_ce9db1dbc2e5fee87247135d17787ff3af014d77.err | 0 ...sh_ce9db1dbc2e5fee87247135d17787ff3af014d77.out | 2 + ...sh_d3367527118052081a541a660b091f6f495b1c0d.err | 0 ...sh_d3367527118052081a541a660b091f6f495b1c0d.out | 0 ...sh_d4bc869850f5b7e53353fc2506fea0c8e96f29c5.err | 1 + ...sh_d4bc869850f5b7e53353fc2506fea0c8e96f29c5.out | 0 ...sh_d4e805ff08d4ccf62865dbf8db8d526f7ce02f37.err | 0 ...sh_d4e805ff08d4ccf62865dbf8db8d526f7ce02f37.out | 2 + ...sh_d54a759f5683a22ad289129b2096b80652b1cc0c.err | 0 ...sh_d54a759f5683a22ad289129b2096b80652b1cc0c.out | 47 + ...sh_d8d4cde8bbc98175069be579ff5634de43880b8c.err | 0 ...sh_d8d4cde8bbc98175069be579ff5634de43880b8c.out | 2 + ...sh_e68167bf5edc7a7b1defd06bdfb694ffa8b00df2.err | 0 ...sh_e68167bf5edc7a7b1defd06bdfb694ffa8b00df2.out | 0 ...sh_ec939e82da809965c61f1c00f68d7afaa4a88382.err | 0 ...sh_ec939e82da809965c61f1c00f68d7afaa4a88382.out | 2 + ...sh_028e99419eb1ac80b03b36148ef1d4ae1c38c44c.err | 0 ...sh_028e99419eb1ac80b03b36148ef1d4ae1c38c44c.out | 2 + ...sh_123c85ff1178743f5cb78efeaf98b637bcbe55ff.err | 0 ...sh_123c85ff1178743f5cb78efeaf98b637bcbe55ff.out | 2 + ...sh_14737ee9597b7d22519d23fbe34c0eb7d6c09ff2.err | 0 ...sh_14737ee9597b7d22519d23fbe34c0eb7d6c09ff2.out | 2 + ...sh_1fbeb1ba69a95284eb1d4d052f5068ede7968704.err | 0 ...sh_1fbeb1ba69a95284eb1d4d052f5068ede7968704.out | 2 + ...sh_20477acc218c96f1385dc97e4d28c80a05c93709.err | 0 ...sh_20477acc218c96f1385dc97e4d28c80a05c93709.out | 2 + ...sh_243454526f6b5e19485db771b4932ddffd6f83a4.err | 0 ...sh_243454526f6b5e19485db771b4932ddffd6f83a4.out | 2 + ...sh_28638a132caae65fd89a68459d1b4af0000b8aef.err | 0 ...sh_28638a132caae65fd89a68459d1b4af0000b8aef.out | 2 + ...sh_3b551281347a8144c84f00ade2664db9ac4aacab.err | 0 ...sh_3b551281347a8144c84f00ade2664db9ac4aacab.out | 2 + ...sh_4035ee76938269e9247f9a696927a9ac18cce80a.err | 0 ...sh_4035ee76938269e9247f9a696927a9ac18cce80a.out | 2 + ...sh_42f0fc1a154b0d79b4f6e846f283426be498040f.err | 1 + ...sh_42f0fc1a154b0d79b4f6e846f283426be498040f.out | 0 ...sh_4b96fe71bc2d18955e3625b765a6095ab1f7a75d.err | 0 ...sh_4b96fe71bc2d18955e3625b765a6095ab1f7a75d.out | 2 + ...sh_53b76b094e47691b5bca106142ee470e82e8e420.err | 0 ...sh_53b76b094e47691b5bca106142ee470e82e8e420.out | 2 + ...sh_6288a9e690d381602b2be5665cc1cd3552733bc2.err | 0 ...sh_6288a9e690d381602b2be5665cc1cd3552733bc2.out | 2 + ...sh_652bbd00b5159e22d94970ab1e882997d14b5777.err | 0 ...sh_652bbd00b5159e22d94970ab1e882997d14b5777.out | 2 + ...sh_6832a58259168622af8b3370b0c89534f98f3f9f.err | 1 + ...sh_6832a58259168622af8b3370b0c89534f98f3f9f.out | 0 ...sh_72862ec9c8f261a8507d237eb673c7ddfaafd898.err | 1 + ...sh_72862ec9c8f261a8507d237eb673c7ddfaafd898.out | 0 ...sh_7797302b63d73234c9ec9f0405c7c0a748daf8e9.err | 0 ...sh_7797302b63d73234c9ec9f0405c7c0a748daf8e9.out | 2 + ...sh_9569ab40cb2e51c60f818a6c2729c60d86565e7e.err | 0 ...sh_9569ab40cb2e51c60f818a6c2729c60d86565e7e.out | 2 + ...sh_9e649c4bc10f4d178519983358f7092e9c5dfe71.err | 0 ...sh_9e649c4bc10f4d178519983358f7092e9c5dfe71.out | 2 + ...sh_b0257ced663fc444801a5e6cba89c3053acca11e.err | 0 ...sh_b0257ced663fc444801a5e6cba89c3053acca11e.out | 2 + ...sh_b5f9ec3ea8b4551fd40017398d74c524fb54ebc9.err | 0 ...sh_b5f9ec3ea8b4551fd40017398d74c524fb54ebc9.out | 2 + ...sh_dbe786c096d5a7a5e1d05311b929f1427d8bac79.err | 0 ...sh_dbe786c096d5a7a5e1d05311b929f1427d8bac79.out | 2 + ...sh_f3b1ea49779117bf45f85ad5615fdc5e89193db6.err | 0 ...sh_f3b1ea49779117bf45f85ad5615fdc5e89193db6.out | 2 + ...sh_28e23f4e98b1acd6478e39844fd9306b444550c3.err | 4 + ...sh_28e23f4e98b1acd6478e39844fd9306b444550c3.out | 0 ...sh_32acc1a8bb5028636fdbf08f077f9a835ab51bec.err | 0 ...sh_32acc1a8bb5028636fdbf08f077f9a835ab51bec.out | 19 + ...sh_485a6ac7c69bd4b5d34d3399a9c17f6a2dc89ad3.err | 0 ...sh_485a6ac7c69bd4b5d34d3399a9c17f6a2dc89ad3.out | 1 + ...sh_62d15cb9d5a9259f198aa01ca8ed200d6da38d68.err | 4 + ...sh_62d15cb9d5a9259f198aa01ca8ed200d6da38d68.out | 3 + ...sh_662b5f9b17aa69a8e3aa9a18acb30d9acf6e2837.err | 0 ...sh_662b5f9b17aa69a8e3aa9a18acb30d9acf6e2837.out | 1 + ...sh_6ffd89498b9a7758ded6717148fc2ce77a12621b.err | 0 ...sh_6ffd89498b9a7758ded6717148fc2ce77a12621b.out | 2 + ...sh_764ea85863d4f0ea3b7cb40850ac7c8fde682d57.err | 4 + ...sh_764ea85863d4f0ea3b7cb40850ac7c8fde682d57.out | 0 ...sh_81dc3eb51ec4dc3066a2365524001242c423a9cf.err | 4 + ...sh_81dc3eb51ec4dc3066a2365524001242c423a9cf.out | 2 + ...sh_81ffd4ed3f62228494a966512791202cea7e3b57.err | 0 ...sh_81ffd4ed3f62228494a966512791202cea7e3b57.out | 2 + ...sh_87f53d441e22c1d27c27eaa6003c83da1207c063.err | 4 + ...sh_87f53d441e22c1d27c27eaa6003c83da1207c063.out | 0 ...sh_977cdf5d396522194d6b9e945169ff8073b4296b.err | 0 ...sh_977cdf5d396522194d6b9e945169ff8073b4296b.out | 2 + ...sh_9a5be90921256e90428c77753eca5ea0d31bd910.err | 0 ...sh_9a5be90921256e90428c77753eca5ea0d31bd910.out | 2 + ...sh_a2c0f0e51b3f85ea2a05ecdcacaad962b4fe5d4f.err | 4 + ...sh_a2c0f0e51b3f85ea2a05ecdcacaad962b4fe5d4f.out | 2 + ...sh_ac1f6e9a88608ef8939f9c2f7061a25a86742d46.err | 4 + ...sh_ac1f6e9a88608ef8939f9c2f7061a25a86742d46.out | 0 ...sh_ade121f29bedea0d1a54452cc994b2302ad9dabb.err | 4 + ...sh_ade121f29bedea0d1a54452cc994b2302ad9dabb.out | 0 ...sh_c851bdf3ba2f56fac5a216457b2d11a109e77f03.err | 4 + ...sh_c851bdf3ba2f56fac5a216457b2d11a109e77f03.out | 0 ...sh_d99d884ba6668b66e3ca9ea4ed2d0e236497c35d.err | 0 ...sh_d99d884ba6668b66e3ca9ea4ed2d0e236497c35d.out | 1 + ...sh_e036fabdc6c15f65a374b95c9922212670d494ee.err | 0 ...sh_e036fabdc6c15f65a374b95c9922212670d494ee.out | 1 + ...sh_ec4623bd63ff353f50db44da1231e46a1a4f1824.err | 0 ...sh_ec4623bd63ff353f50db44da1231e46a1a4f1824.out | 3 + ...sh_f7476c76ea51cf479a6a79b037e0cb59871b629c.err | 4 + ...sh_f7476c76ea51cf479a6a79b037e0cb59871b629c.out | 0 ...sh_f8340cb4c62aabd839ea09235b6ebe41b2bb48f4.err | 4 + ...sh_f8340cb4c62aabd839ea09235b6ebe41b2bb48f4.out | 0 ...sh_46dfa23e2effabf3fa150c4b871fd8d22b1c834d.err | 0 ...sh_46dfa23e2effabf3fa150c4b871fd8d22b1c834d.out | 0 ...sh_4effabf11b59580e5f0727199eb74fba049c0cda.err | 0 ...sh_4effabf11b59580e5f0727199eb74fba049c0cda.out | 6 + ...sh_8912b59d5b515ab1373a3d9bc635ebabacd01dfd.err | 0 ...sh_8912b59d5b515ab1373a3d9bc635ebabacd01dfd.out | 6 + ...sh_b036c73528a446cba46625767517cdac868aba72.err | 1 + ...sh_b036c73528a446cba46625767517cdac868aba72.out | 0 ...sh_fefeb387ae14d4171225ea06cbbff3ec43990cf0.err | 1 + ...sh_fefeb387ae14d4171225ea06cbbff3ec43990cf0.out | 0 ...sh_41c6abde708a69e74f5b7fde865d88fa75f91e0a.err | 4 + ...sh_41c6abde708a69e74f5b7fde865d88fa75f91e0a.out | 0 ...sh_dc189d02e8979b7ed245d5d750f68b9965984699.err | 4 + ...sh_dc189d02e8979b7ed245d5d750f68b9965984699.out | 0 ...sh_12f539e535df04364316699f9edeac461aa9f9de.err | 3 + ...sh_12f539e535df04364316699f9edeac461aa9f9de.out | 8 + ...sh_2e69c22dcfa37b5c3e8490a6026eacb7ca953998.err | 2 + ...sh_2e69c22dcfa37b5c3e8490a6026eacb7ca953998.out | 0 ...sh_5b51b55dff7332c5bee2c9b797c401c5614d574a.err | 0 ...sh_5b51b55dff7332c5bee2c9b797c401c5614d574a.out | 178 + ...sh_6a24078983cf1b7a80b6fb65d5186cd125498136.err | 0 ...sh_6a24078983cf1b7a80b6fb65d5186cd125498136.out | 149 + ...sh_801414c6bb6d3f9225973eafa3c6dfa49cd2081d.err | 0 ...sh_801414c6bb6d3f9225973eafa3c6dfa49cd2081d.out | 111 + ...sh_87943c6be50d701a03e901f16493314c839af1ab.err | 0 ...sh_87943c6be50d701a03e901f16493314c839af1ab.out | 111 + ...sh_8b2cd055e6a1db2ed9b2af2a917f8556395fa653.err | 0 ...sh_8b2cd055e6a1db2ed9b2af2a917f8556395fa653.out | 2 + ...sh_ac486314c4e02e480d829ea2f077b86c49fedcec.err | 0 ...sh_ac486314c4e02e480d829ea2f077b86c49fedcec.out | 4 + ...sh_ac872aadda29b9a824361a2c711d62ec1c75d40f.err | 0 ...sh_ac872aadda29b9a824361a2c711d62ec1c75d40f.out | 74 + ...sh_c21295f131c221861568bda5014b76ef99bdd11f.err | 0 ...sh_c21295f131c221861568bda5014b76ef99bdd11f.out | 159 + ...sh_c2a346ca1da2da4346f1d310212e166767993ce9.err | 0 ...sh_c2a346ca1da2da4346f1d310212e166767993ce9.out | 58 + ...sh_e088ea61a5382458cc48a2607e2639e52b0be1da.err | 0 ...sh_e088ea61a5382458cc48a2607e2639e52b0be1da.out | 149 + test/expected_help.txt | 4013 +++ test/file_for_dot_read.sql | 4 + test/formats/collision/format.json | 48 + test/formats/customlevel/format.json | 25 + test/formats/jsontest-subsec/format.json | 27 + test/formats/jsontest/format.json | 36 + test/formats/jsontest/lnav-logstash.json | 47 + test/formats/jsontest/rewrite-user.lnav | 2 + test/formats/jsontest2/format.json | 49 + test/formats/jsontest3/format.json | 75 + test/formats/nestedjson/format.json | 32 + test/formats/scripts/multiline-echo.lnav | 3 + test/formats/scripts/nested-redirecting.lnav | 5 + test/formats/scripts/redirecting.lnav | 6 + test/formats/sqldir/init.sql | 5 + test/formats/timestamp/format.json | 26 + test/formats/xmlmsg/format.json | 73 + test/gp_test.cc | 103 + test/lb_test.cc | 65 + test/listview_output.0 | 70 + test/listview_output.1 | 70 + test/listview_output.2 | 70 + test/listview_output.3 | 70 + test/listview_output.4 | 68 + test/listview_output.5 | 59 + test/listview_output.6 | 59 + test/lnav_doctests.cc | 231 + ...le-057d6c669632ef9d07b6adec605f6bdeae19af27.txt | 13 + ...le-06aaa6f48a801f592558575d886864d6c3ab9ed4.txt | 40 + ...le-1aeb47c0a97d19bb7418f0172480e05e49c6e53e.txt | 17 + ...le-27353a72ba4025448f261dcfa6ea16e474187795.txt | 4 + ...le-3856ad0f551a04fde41a020158d6b33ef97c870a.txt | 17 + .../sample-45364b3fd51af92a4ad8a309b5f4fd88.txt | 40 + ...le-500c9e492e04f5f58862c8086ca301de0dd976ce.txt | 13 + .../sample-55ac97afae4b0650ccb62e2dbc8d89bb.txt | 15 + ...le-6049d4309f26eefb1a3406d937a9ba8a0df592a7.txt | 13 + ...le-62315d884afdc4155b35f905415c74bfcfd39fc2.txt | 17 + ...le-70c906b3c1a1cf03f15bde92ee78edfa6f9b7960.txt | 4 + ...le-9cf7fbb3546c676c686fac0ed096d026f46c875f.txt | 13 + ...le-a74570613c082c7fe283672031e18e54e8887ffb.txt | 13 + ...le-aca2878a2e50779c6697c0747ab1f60e4b368dcb.txt | 15 + ...le-ad31f12d2adabd07e3ddda3ad5b0dbf6b49c4c99.txt | 21 + ...le-bc6f6cf689fa5455616b4d9fbe121a48d3c9de59.txt | 25 + ...le-c15acd32844669d23d0cbc88ec548129ed2c592e.txt | 87 + ...le-c23f22c1b932b904203e018f78dead95fb89b15d.txt | 37 + ...le-d0d6b3fc6766caac5ac3fac4a3754ceaab785eb8.txt | 33 + ...le-d4a0aedc8350f64b22403eeef4eca71fbf749d2b.txt | 9 + ...le-d714b5e8cd354321f376ed1c0a70ec9a2f58076d.txt | 9 + ...le-dd7d406352ec6a11d966b6f015a9482b060f2b29.txt | 23 + ...le-e779d1771e34f5203ae73e85802e78002be63db6.txt | 25 + ...le-f5afbee90a8c054061c4e9ffe673293cce7761de.txt | 13 + ...le-fc8923633e57bacd641d80dde3ff878212230552.txt | 13 + test/log.clog | 2 + test/logfile_access_log.0 | 3 + test/logfile_access_log.1 | 1 + test/logfile_ansi.0 | 1 + test/logfile_ansi.1 | 10 + test/logfile_bad_access_log.0 | 3 + test/logfile_bad_syslog.0 | 4 + test/logfile_block.1 | 4 + test/logfile_block.2 | 2 + test/logfile_blued.0 | 6 + test/logfile_bro_conn.log.0 | 101 + test/logfile_bro_http.log.0 | 206 + test/logfile_crlf.0 | 2 + test/logfile_cxx.0 | 1 + test/logfile_empty.0 | 0 test/logfile_epoch.0 | 2 + test/logfile_epoch.1 | 2 + test/logfile_filter.0 | 13 + test/logfile_for_join.0 | 10 + test/logfile_generic.0 | 2 + test/logfile_generic.1 | 2 + test/logfile_generic.2 | 2 + test/logfile_generic.3 | 2 + test/logfile_generic_with_header.0 | 4 + test/logfile_glog.0 | 7 + test/logfile_haproxy.0 | 17 + test/logfile_invalid_json.json | 5 + test/logfile_invalid_json2.json | 3 + test/logfile_journald.json | 2 + test/logfile_json.json | 13 + test/logfile_json2.json | 3 + test/logfile_json3.json | 3 + test/logfile_json_subsec.json | 2 + test/logfile_leveltest.0 | 8 + test/logfile_logfmt.0 | 5 + test/logfile_mixed_json2.json | 18 + test/logfile_multiline.0 | 3 + test/logfile_nested_json.json | 13 + test/logfile_openam.0 | 2 + test/logfile_plain.0 | 3 + test/logfile_pretty.0 | 23 + test/logfile_procstate.0 | 43 + test/logfile_rollover.0 | 6 + test/logfile_rollover.1 | 5 + test/logfile_strace_log.0 | 9 + test/logfile_syslog.0 | 4 + test/logfile_syslog.1 | 4 + test/logfile_syslog.2 | 3 + test/logfile_syslog.3 | 17 + test/logfile_syslog_fr.0 | 1 + test/logfile_syslog_with_access_log.0 | 5 + test/logfile_syslog_with_header.0 | 6 + test/logfile_syslog_with_mixed_times.0 | 13 + test/logfile_tai64n.0 | 10 + test/logfile_tcf.0 | 30 + test/logfile_tcf.1 | 3 + test/logfile_tcsh_history.0 | 4 + test/logfile_uwsgi.0 | 19 + test/logfile_vami.0 | 4 + test/logfile_vdsm.0 | 16 + test/logfile_vmw_log.0 | 4 + test/logfile_vpxd.0 | 12 + test/logfile_w3c.0 | 5 + test/logfile_w3c.1 | 7 + test/logfile_w3c.2 | 22 + test/logfile_w3c.3 | 10 + test/logfile_w3c.4 | 6 + test/logfile_w3c.5 | 2 + test/logfile_w3c.6 | 5 + test/logfile_w3c_big.0 | 254 + ...eally_long_name_to_test_a_bug_with_long_names.0 | 1 + test/logfile_xml_msg.0 | 36 + test/multiline.lnav | 14 + test/mvwattrline_output.0 | 49 + test/nested.lnav | 2 + test/parser_debugger.py | 249 + test/remote-log-dir/logfile_access_log.0 | 3 + test/remote-log-dir/logfile_access_log.1 | 1 + test/rltest.cc | 216 + test/scripty.cc | 1153 + test/si_test.cc | 49 + test/slicer.cc | 99 + test/sql.0.in | 6 + test/sql.0.out | 7 + test/test_abbrev.cc | 60 + test/test_ansi_scrubber.cc | 98 + test/test_auto_fd.cc | 82 + test/test_auto_mem.cc | 115 + test/test_bookmarks.cc | 146 + test/test_cli.sh | 31 + test/test_cmds.sh | 515 + test/test_column_namer.cc | 68 + test/test_config.sh | 39 + test/test_curl.sh | 48 + test/test_data_parser.sh | 16 + test/test_date_time_scanner.cc | 192 + test/test_events.sh | 31 + test/test_format_installer.sh | 34 + test/test_format_loader.sh | 17 + test/test_grep_proc.sh | 57 + test/test_grep_proc2.cc | 150 + test/test_json_format.sh | 141 + test/test_line_buffer.sh | 78 + test/test_line_buffer2.cc | 157 + test/test_listview.sh | 36 + test/test_log_accel.cc | 70 + test/test_logfile.sh | 701 + test/test_md2attr_line.cc | 34 + test/test_meta.sh | 110 + test/test_mvwattrline.sh | 6 + test/test_ncurses_unicode.cc | 40 + test/test_pretty_print.sh | 45 + test/test_regex101.sh | 77 + test/test_reltime.cc | 427 + test/test_remote.sh | 119 + test/test_scripts.sh | 51 + test/test_sessions.sh | 119 + test/test_shlexer.sh | 32 + test/test_sql.sh | 1097 + test/test_sql_anno.sh | 50 + test/test_sql_coll_func.sh | 29 + test/test_sql_fs_func.sh | 53 + test/test_sql_indexes.sh | 45 + test/test_sql_json_func.sh | 132 + test/test_sql_regexp.sh | 30 + test/test_sql_search_table.sh | 26 + test/test_sql_str_func.sh | 170 + test/test_sql_time_func.sh | 71 + test/test_sql_views_vtab.sh | 173 + test/test_sql_xml_func.sh | 11 + test/test_sql_yaml_func.sh | 5 + test/test_stubs.cc | 93 + test/test_text_anonymizer.cc | 131 + test/test_text_file.sh | 33 + test/test_top_status.cc | 90 + test/test_tui.sh | 28 + test/test_view_colors.sh | 6 + test/test_vt52_curses.sh | 6 + test/textfile_0.md | 9 + test/textfile_ansi.0 | 1 + test/textfile_json_indented.0 | 12 + test/textfile_json_one_line.0 | 1 + test/textfile_quoted_json.0 | 1 + test/toplevel.lnav | 4 + test/tui-captures/tui_echo.0 | 229 + test/tui-captures/tui_help.0 | 179 + test/update_parser_output.sh | 13 + test/view_colors_output.0 | 54 + test/vt52_curses_input.0 | 49 + test/vt52_curses_input.1 | 38 + test/vt52_curses_output.0 | 39 + test/vt52_curses_output.1 | 38 + test/xpath_tui.0 | 458 + tools/Makefile.am | 14 + tools/bin2c.c | 252 + update_expected_output.sh | 99 + 2234 files changed, 371788 insertions(+) create mode 100644 .cirrus.yml create mode 100644 .clang-format create mode 100644 .clang-tidy create mode 100644 .codespellrc create mode 100644 .coveralls.yml create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md create mode 100644 .github/mlc_config.json create mode 100644 .github/workflows/c-cpp.yml create mode 100644 .github/workflows/check-md-links.yml create mode 100644 .github/workflows/coverity.yml create mode 100644 .github/workflows/tailer-ape.yml create mode 100644 .gitignore create mode 100644 .gitmodules create mode 100644 .readthedocs.yaml create mode 100644 ARCHITECTURE.md create mode 100644 AUTHORS create mode 100644 CMakeLists.txt create mode 100644 CMakePresets.json create mode 100644 CMakeUserPresets.json.example create mode 100644 FUNDING.yml create mode 100644 INSTALL create mode 100644 LICENSE create mode 100644 Makefile.am create mode 100644 NEWS.md create mode 100644 README create mode 100644 README.md create mode 100644 TESTS_ENVIRONMENT.in create mode 100644 appveyor.yml create mode 100755 autogen.sh create mode 100755 cleanup_expected.sh create mode 100644 cmake/CodeCoverage.cmake create mode 100644 cmake/Hunter/config.cmake create mode 100644 cmake/HunterGate.cmake create mode 100644 cmake/coverage.cmake create mode 100644 cmake/dev-mode.cmake create mode 100644 cmake/docs-ci.cmake create mode 100644 cmake/docs.cmake create mode 100644 cmake/folders.cmake create mode 100644 cmake/install-rules.cmake create mode 100644 cmake/install-script.cmake create mode 100644 cmake/lint-targets.cmake create mode 100644 cmake/lint.cmake create mode 100644 cmake/open-cpp-coverage.cmake.example create mode 100644 cmake/prelude.cmake create mode 100644 cmake/project-is-top-level.cmake create mode 100644 cmake/spell-targets.cmake create mode 100644 cmake/spell.cmake create mode 100644 cmake/variables.cmake create mode 100644 conanfile.py create mode 100644 configure.ac create mode 100644 demo/Dockerfile create mode 100644 demo/fly.toml create mode 100755 demo/loggen.py create mode 100644 docs/.gitignore create mode 100644 docs/02_downloads.md create mode 100644 docs/03_features.md create mode 100644 docs/04_tutorials.md create mode 100644 docs/05_docs.md create mode 100644 docs/06_changeblog.md create mode 100644 docs/404.html create mode 100644 docs/CNAME create mode 100644 docs/Doxyfile.in create mode 100644 docs/Gemfile create mode 100644 docs/Gemfile.lock create mode 100644 docs/Makefile create mode 100644 docs/README.md create mode 100644 docs/_config.yml create mode 100644 docs/_includes/social.html create mode 100644 docs/_layouts/top.html create mode 100644 docs/_posts/2013-09-10-json-encoded-logs.md create mode 100644 docs/_posts/2013-09-13-four-years-on-github.md create mode 100644 docs/_posts/2013-10-05-mini-review-in-linux-magazine.md create mode 100644 docs/_posts/2013-10-06-competing-with-tail.md create mode 100644 docs/_posts/2013-11-01-mini-review-linux-user-magazine.md create mode 100644 docs/_posts/2014-02-22-changes-to-the-scrollbar.md create mode 100644 docs/_posts/2014-11-11-lofi-mode.md create mode 100644 docs/_posts/2015-04-11-pretty-print-view.md create mode 100644 docs/_posts/2016-03-20-lnav-in-print.md create mode 100644 docs/_posts/2018-03-27-reveal-file-paths.md create mode 100644 docs/_posts/2018-04-05-linux-magazine-tutorial.md create mode 100644 docs/_posts/2018-05-17-tags-and-comments.md create mode 100644 docs/_posts/2018-11-9-visual-filter-editor.md create mode 100644 docs/_posts/2019-05-08-themes.md create mode 100644 docs/_posts/2020-12-23-xpath-sql-function.md create mode 100644 docs/_posts/2021-05-03-tailing-remote-files.md create mode 100644 docs/_posts/2022-05-01-regex101-integration.md create mode 100644 docs/_posts/2022-08-04-pretty-errors.md create mode 100644 docs/_posts/2022-08-06-markdown-support.md create mode 100644 docs/_posts/2022-09-01-playground.md create mode 100644 docs/_posts/2022-09-24-vscode-extension.md create mode 100644 docs/assets/images/favicon.png create mode 100644 docs/assets/images/favicon.svg create mode 100644 docs/assets/images/linux-user-and-dev-mag.jpeg create mode 100644 docs/assets/images/lnav-after-pretty.png create mode 100644 docs/assets/images/lnav-before-pretty.png create mode 100644 docs/assets/images/lnav-front-page.png create mode 100644 docs/assets/images/lnav-hist.png create mode 100644 docs/assets/images/lnav-invalid-regex-error.png create mode 100644 docs/assets/images/lnav-multi-file2.png create mode 100644 docs/assets/images/lnav-query.png create mode 100644 docs/assets/images/lnav-sql-error-msg.png create mode 100644 docs/assets/images/lnav-syntax-highlight.gif create mode 100644 docs/assets/images/lnav-syslog-thumb.png create mode 100644 docs/assets/images/lnav-syslog.png create mode 100644 docs/assets/images/lnav-tab-complete.gif create mode 100644 docs/assets/images/lnav-theme-cycle.gif create mode 100644 docs/assets/images/lnav-vscode-extension.png create mode 100644 docs/assets/images/scrollbar-change-2.png create mode 100644 docs/assets/main.scss create mode 100644 docs/conf.py.in create mode 100644 docs/index.markdown create mode 100644 docs/lnav-architecture.png create mode 100644 docs/lnav-tui.png create mode 100644 docs/pages/about.dox create mode 100644 docs/requirements.txt create mode 100644 docs/schemas/config-v1.schema.json create mode 100644 docs/schemas/event-file-format-detected-v1.schema.json create mode 100644 docs/schemas/event-file-open-v1.schema.json create mode 100644 docs/schemas/event-log-msg-detected-v1.schema.json create mode 100644 docs/schemas/event-session-loaded-v1.schema.json create mode 100644 docs/schemas/format-v1.schema.json create mode 100644 docs/source/_ext/__init__.py create mode 100644 docs/source/_ext/lnavlexer.py create mode 100644 docs/source/_static/theme_overrides.css create mode 100644 docs/source/cli.rst create mode 100644 docs/source/commands.rst create mode 100644 docs/source/conf.py create mode 100644 docs/source/config.rst create mode 100644 docs/source/cookbook.rst create mode 100644 docs/source/data.rst create mode 100644 docs/source/docutils.conf create mode 100644 docs/source/events.rst create mode 100644 docs/source/faq.rst create mode 100644 docs/source/filter-out-preview.png create mode 100644 docs/source/formats.rst create mode 100644 docs/source/group_concat-help.png create mode 100644 docs/source/hotkey-tips.png create mode 100644 docs/source/hotkeys.rst create mode 100644 docs/source/howitworks.rst create mode 100644 docs/source/index.rst create mode 100644 docs/source/intro.rst create mode 100644 docs/source/key-encoding-prompt.png create mode 100644 docs/source/lnav-breadcrumbs-help.png create mode 100644 docs/source/lnav-config-header.png create mode 100644 docs/source/lnav-files-panel.png create mode 100644 docs/source/lnav-filters-panel.png create mode 100644 docs/source/lnav-spectro-cpu-pct.png create mode 100644 docs/source/lnav-ui.png create mode 100644 docs/source/open-error.png create mode 100644 docs/source/open-help.png create mode 100644 docs/source/open-preview.png create mode 100644 docs/source/query-results.png create mode 100644 docs/source/sessions.rst create mode 100644 docs/source/sql-help.png create mode 100644 docs/source/sqlext.rst create mode 100644 docs/source/sqltab.rst create mode 100644 docs/source/ui.rst create mode 100644 docs/source/usage.rst create mode 100644 docs/tutorials/playground/index.md create mode 100644 docs/tutorials/playground/logs/access_log.gz create mode 100644 docs/tutorials/playground/logs/messages.gz create mode 100755 docs/tutorials/playground/run.sh create mode 100644 docs/tutorials/playground/text/markdown-sample.md create mode 100644 docs/tutorials/tutorial-lib/configs/tutorial1/config.json create mode 100644 docs/tutorials/tutorial-lib/formats/tutorial-lib/lnav-tutorial-key-handler.lnav create mode 100644 docs/tutorials/tutorial-lib/formats/tutorial-lib/tutorial.sql create mode 100644 docs/tutorials/tutorial1/index.md create mode 100755 docs/tutorials/tutorial1/run.sh create mode 100644 docs/tutorials/tutorial1/tutorial1.glog create mode 100755 example-scripts/clipboard.sh create mode 100755 example-scripts/log_to_csv.sh create mode 100644 example-scripts/report-demo.lnav create mode 100644 example-scripts/tag-ssh-msgs.lnav create mode 100644 lnav.1 create mode 100644 lnav.cfg create mode 100644 m4/ax_ac_append_to_file.m4 create mode 100644 m4/ax_ac_print_to_file.m4 create mode 100644 m4/ax_add_am_macro_static.m4 create mode 100644 m4/ax_am_macros_static.m4 create mode 100644 m4/ax_check_gnu_make.m4 create mode 100644 m4/ax_check_link_flag.m4 create mode 100644 m4/ax_check_pcre2.m4 create mode 100644 m4/ax_code_coverage.m4 create mode 100644 m4/ax_cxx_compile_stdcxx.m4 create mode 100644 m4/ax_cxx_compile_stdcxx_11.m4 create mode 100644 m4/ax_cxx_compile_stdcxx_14.m4 create mode 100644 m4/ax_cxx_compile_stdcxx_17.m4 create mode 100644 m4/ax_file_escapes.m4 create mode 100644 m4/ax_prog_cc_for_build.m4 create mode 100644 m4/ax_pthread.m4 create mode 100644 m4/ax_with_curses.m4 create mode 100644 m4/libcurl.m4 create mode 100644 m4/lnav_common.m4 create mode 100644 m4/lnav_with_jemalloc.m4 create mode 100644 m4/lnav_with_libarchive.m4 create mode 100644 m4/lnav_with_readline.m4 create mode 100644 m4/lnav_with_sqlite3.m4 create mode 100644 m4/lnav_with_yajl.m4 create mode 100644 release/Makefile create mode 100644 release/README.md create mode 100644 release/lnav-screenshot.terminal create mode 100755 release/loggen.py create mode 100755 release/spectrolog.py create mode 100755 release/tail-demo.sh create mode 100644 release/vagrant-static/Vagrantfile create mode 100755 release/vagrant-static/build-pkg.sh create mode 100755 release/vagrant-static/build.sh create mode 100644 release/vagrant-static/musl-pkg.sh create mode 100755 release/vagrant-static/pkg.sh create mode 100644 release/vagrant-static/provision-pkg.sh create mode 100755 release/vagrant-static/provision.sh create mode 100644 snapcraft.yaml create mode 100644 src/CMakeLists.txt create mode 100644 src/Makefile.am create mode 100644 src/all_logs_vtab.cc create mode 100644 src/all_logs_vtab.hh create mode 100755 src/alpha-release.sh create mode 100644 src/animals.json create mode 100644 src/ansi-palette.json create mode 100644 src/archive_manager.cc create mode 100644 src/archive_manager.cfg.hh create mode 100644 src/archive_manager.hh create mode 100644 src/base/CMakeLists.txt create mode 100644 src/base/Makefile.am create mode 100644 src/base/README.md create mode 100644 src/base/ansi_scrubber.cc create mode 100644 src/base/ansi_scrubber.hh create mode 100644 src/base/attr_line.builder.cc create mode 100644 src/base/attr_line.builder.hh create mode 100644 src/base/attr_line.cc create mode 100644 src/base/attr_line.hh create mode 100644 src/base/attr_line.tests.cc create mode 100644 src/base/auto_fd.hh create mode 100644 src/base/auto_mem.hh create mode 100644 src/base/auto_pid.cc create mode 100644 src/base/auto_pid.hh create mode 100644 src/base/bus.hh create mode 100644 src/base/date_time_scanner.cc create mode 100644 src/base/date_time_scanner.hh create mode 100644 src/base/enum_util.hh create mode 100644 src/base/file_range.hh create mode 100644 src/base/fs_util.cc create mode 100644 src/base/fs_util.hh create mode 100644 src/base/fs_util.tests.cc create mode 100644 src/base/func_util.hh create mode 100644 src/base/future_util.hh create mode 100644 src/base/humanize.cc create mode 100644 src/base/humanize.file_size.tests.cc create mode 100644 src/base/humanize.hh create mode 100644 src/base/humanize.network.cc create mode 100644 src/base/humanize.network.hh create mode 100644 src/base/humanize.network.tests.cc create mode 100644 src/base/humanize.time.cc create mode 100644 src/base/humanize.time.hh create mode 100644 src/base/humanize.time.tests.cc create mode 100644 src/base/injector.bind.hh create mode 100644 src/base/injector.hh create mode 100644 src/base/intern_string.cc create mode 100644 src/base/intern_string.hh create mode 100644 src/base/intern_string.tests.cc create mode 100644 src/base/is_utf8.cc create mode 100644 src/base/is_utf8.hh create mode 100644 src/base/isc.cc create mode 100644 src/base/isc.hh create mode 100644 src/base/itertools.hh create mode 100644 src/base/lnav.console.cc create mode 100644 src/base/lnav.console.hh create mode 100644 src/base/lnav.console.into.hh create mode 100644 src/base/lnav.gzip.cc create mode 100644 src/base/lnav.gzip.hh create mode 100644 src/base/lnav.gzip.tests.cc create mode 100644 src/base/lnav_log.cc create mode 100644 src/base/lnav_log.hh create mode 100644 src/base/log_level_enum.hh create mode 100644 src/base/lrucache.hpp create mode 100644 src/base/math_util.hh create mode 100644 src/base/network.tcp.cc create mode 100644 src/base/network.tcp.hh create mode 100644 src/base/network.tcp.tests.cc create mode 100644 src/base/opt_util.hh create mode 100644 src/base/paths.cc create mode 100644 src/base/paths.hh create mode 100644 src/base/result.h create mode 100644 src/base/snippet_highlighters.cc create mode 100644 src/base/snippet_highlighters.hh create mode 100644 src/base/string_attr_type.cc create mode 100644 src/base/string_attr_type.hh create mode 100644 src/base/string_util.cc create mode 100644 src/base/string_util.hh create mode 100644 src/base/string_util.tests.cc create mode 100644 src/base/strnatcmp.c create mode 100644 src/base/strnatcmp.h create mode 100644 src/base/test_base.cc create mode 100644 src/base/time_util.cc create mode 100644 src/base/time_util.hh create mode 100644 src/big_array.hh create mode 100644 src/bin2c.hh create mode 100644 src/bookmarks.cc create mode 100644 src/bookmarks.hh create mode 100644 src/bottom_status_source.cc create mode 100644 src/bottom_status_source.hh create mode 100644 src/bound_tags.hh create mode 100644 src/breadcrumb.hh create mode 100644 src/breadcrumb_curses.cc create mode 100644 src/breadcrumb_curses.hh create mode 100644 src/byte_array.hh create mode 100644 src/collation-functions.cc create mode 100644 src/column_namer.cc create mode 100644 src/column_namer.hh create mode 100644 src/command_executor.cc create mode 100644 src/command_executor.hh create mode 100644 src/config.cmake.h.in create mode 100644 src/curl_looper.cc create mode 100644 src/curl_looper.hh create mode 100644 src/data_parser.cc create mode 100644 src/data_parser.hh create mode 100644 src/data_scanner.cc create mode 100644 src/data_scanner.hh create mode 100644 src/data_scanner_re.cc create mode 100644 src/data_scanner_re.re create mode 100644 src/db_sub_source.cc create mode 100644 src/db_sub_source.hh create mode 100644 src/diseases.json create mode 100644 src/doc_status_source.hh create mode 100644 src/document.sections.cc create mode 100644 src/document.sections.hh create mode 100644 src/dump_internals.cc create mode 100644 src/dump_internals.hh create mode 100644 src/elem_to_json.cc create mode 100644 src/elem_to_json.hh create mode 100644 src/emojis.json create mode 100644 src/environ_vtab.cc create mode 100644 src/environ_vtab.hh create mode 100644 src/extension-functions.cc create mode 100644 src/field_overlay_source.cc create mode 100644 src/field_overlay_source.hh create mode 100644 src/file_collection.cc create mode 100644 src/file_collection.hh create mode 100644 src/file_format.cc create mode 100644 src/file_format.hh create mode 100644 src/file_vtab.cc create mode 100644 src/file_vtab.cfg.hh create mode 100644 src/files_sub_source.cc create mode 100644 src/files_sub_source.hh create mode 100644 src/filter_observer.cc create mode 100644 src/filter_observer.hh create mode 100644 src/filter_status_source.cc create mode 100644 src/filter_status_source.hh create mode 100644 src/filter_sub_source.cc create mode 100644 src/filter_sub_source.hh create mode 100644 src/fmtlib/Makefile.am create mode 100644 src/fmtlib/fmt/args.h create mode 100644 src/fmtlib/fmt/chrono.h create mode 100644 src/fmtlib/fmt/color.h create mode 100644 src/fmtlib/fmt/compile.h create mode 100644 src/fmtlib/fmt/core.h create mode 100644 src/fmtlib/fmt/format-inl.h create mode 100644 src/fmtlib/fmt/format.h create mode 100644 src/fmtlib/fmt/locale.h create mode 100644 src/fmtlib/fmt/os.h create mode 100644 src/fmtlib/fmt/ostream.h create mode 100644 src/fmtlib/fmt/printf.h create mode 100644 src/fmtlib/fmt/ranges.h create mode 100644 src/fmtlib/fmt/std.h create mode 100644 src/fmtlib/fmt/xchar.h create mode 100644 src/fmtlib/format.cc create mode 100644 src/fmtlib/os.cc create mode 100644 src/format2csv.py create mode 100644 src/formats/README.md create mode 100644 src/formats/access_log.json create mode 100644 src/formats/alb_log.json create mode 100644 src/formats/block_log.json create mode 100644 src/formats/candlepin_log.json create mode 100644 src/formats/choose_repo_log.json create mode 100644 src/formats/cups_log.json create mode 100644 src/formats/dpkg_log.json create mode 100644 src/formats/elb_log.json create mode 100644 src/formats/engine_log.json create mode 100644 src/formats/error_log.json create mode 100644 src/formats/esx_syslog_log.json create mode 100644 src/formats/formats.am create mode 100644 src/formats/fsck_hfs_log.json create mode 100644 src/formats/glog_log.json create mode 100644 src/formats/haproxy_log.json create mode 100644 src/formats/java_log.json create mode 100644 src/formats/journald_json_log.json create mode 100644 src/formats/katello_log.json create mode 100644 src/formats/logfmt/CMakeLists.txt create mode 100644 src/formats/logfmt/Makefile.am create mode 100644 src/formats/logfmt/logfmt.parser.cc create mode 100644 src/formats/logfmt/logfmt.parser.hh create mode 100644 src/formats/logfmt/logfmt.parser.test.cc create mode 100644 src/formats/openam_log.json create mode 100644 src/formats/openamdb_log.json create mode 100644 src/formats/openstack_log.json create mode 100644 src/formats/page_log.json create mode 100644 src/formats/papertrail_log.json create mode 100644 src/formats/pcap_log.json create mode 100644 src/formats/procstate_log.json create mode 100644 src/formats/s3_log.json create mode 100644 src/formats/snaplogic_log.json create mode 100644 src/formats/sssd_log.json create mode 100644 src/formats/strace_log.json create mode 100644 src/formats/sudo_log.json create mode 100644 src/formats/syslog_log.json create mode 100644 src/formats/tcf_log.json create mode 100644 src/formats/tcsh_history.json create mode 100644 src/formats/unifi_iptables_log.json create mode 100644 src/formats/unifi_log.json create mode 100644 src/formats/uwsgi_log.json create mode 100644 src/formats/vdsm_log.json create mode 100644 src/formats/vmk_log.json create mode 100644 src/formats/vmw_log.json create mode 100644 src/formats/vmw_py_log.json create mode 100644 src/formats/vmw_vc_svc_log.json create mode 100644 src/formats/xmlrpc_log.json create mode 100644 src/fs-extension-functions.cc create mode 100644 src/fstat_vtab.cc create mode 100644 src/fstat_vtab.hh create mode 100644 src/fts_fuzzy_match.cc create mode 100644 src/fts_fuzzy_match.hh create mode 100644 src/ghc/filesystem.hpp create mode 100644 src/ghc/fs_fwd.hpp create mode 100644 src/ghc/fs_impl.hpp create mode 100644 src/ghc/fs_std.hpp create mode 100644 src/ghc/fs_std_fwd.hpp create mode 100644 src/ghc/fs_std_impl.hpp create mode 100644 src/grep_highlighter.hh create mode 100644 src/grep_proc.cc create mode 100644 src/grep_proc.hh create mode 100644 src/help.md create mode 100644 src/help.txt create mode 100644 src/help_text.cc create mode 100644 src/help_text.hh create mode 100644 src/help_text_formatter.cc create mode 100644 src/help_text_formatter.hh create mode 100644 src/highlighter.cc create mode 100644 src/highlighter.hh create mode 100644 src/hist_source.cc create mode 100644 src/hist_source.hh create mode 100644 src/hotkeys.cc create mode 100644 src/hotkeys.hh create mode 100644 src/init.sql create mode 100644 src/input_dispatcher.cc create mode 100644 src/input_dispatcher.hh create mode 100644 src/internals/README.md create mode 100644 src/internals/cmd-ref.rst create mode 100644 src/internals/sql-ref.rst create mode 100644 src/itertools.similar.hh create mode 100644 src/json-extension-functions.cc create mode 100644 src/k_merge_tree.h create mode 100644 src/keymaps/README.md create mode 100644 src/keymaps/de-keymap.json create mode 100644 src/keymaps/default-keymap.json create mode 100644 src/keymaps/fr-keymap.json create mode 100644 src/keymaps/keymaps.am create mode 100644 src/keymaps/sv-keymap.json create mode 100644 src/keymaps/uk-keymap.json create mode 100644 src/keymaps/us-keymap.json create mode 100644 src/line_buffer.cc create mode 100644 src/line_buffer.hh create mode 100644 src/listview_curses.cc create mode 100644 src/listview_curses.hh create mode 100644 src/lnav.cc create mode 100644 src/lnav.events.cc create mode 100644 src/lnav.events.hh create mode 100644 src/lnav.hh create mode 100644 src/lnav.indexing.cc create mode 100644 src/lnav.indexing.hh create mode 100644 src/lnav.management_cli.cc create mode 100644 src/lnav.management_cli.hh create mode 100644 src/lnav_commands.cc create mode 100644 src/lnav_commands.hh create mode 100644 src/lnav_config.cc create mode 100644 src/lnav_config.hh create mode 100644 src/lnav_config_fwd.hh create mode 100644 src/lnav_util.cc create mode 100644 src/lnav_util.hh create mode 100644 src/log.watch.cc create mode 100644 src/log.watch.hh create mode 100644 src/log_accel.cc create mode 100644 src/log_accel.hh create mode 100644 src/log_actions.cc create mode 100644 src/log_actions.hh create mode 100644 src/log_data_helper.cc create mode 100644 src/log_data_helper.hh create mode 100644 src/log_data_table.cc create mode 100644 src/log_data_table.hh create mode 100644 src/log_format.cc create mode 100644 src/log_format.hh create mode 100644 src/log_format_ext.hh create mode 100644 src/log_format_fwd.hh create mode 100644 src/log_format_impls.cc create mode 100644 src/log_format_loader.cc create mode 100644 src/log_format_loader.hh create mode 100644 src/log_gutter_source.hh create mode 100644 src/log_level.cc create mode 100644 src/log_level.hh create mode 100644 src/log_level_re.cc create mode 100644 src/log_level_re.re create mode 100644 src/log_search_table.cc create mode 100644 src/log_search_table.hh create mode 100644 src/log_search_table_fwd.hh create mode 100644 src/log_vtab_impl.cc create mode 100644 src/log_vtab_impl.hh create mode 100644 src/logfile.cc create mode 100644 src/logfile.cfg.hh create mode 100644 src/logfile.hh create mode 100644 src/logfile_fwd.hh create mode 100644 src/logfile_stats.hh create mode 100644 src/logfile_sub_source.cc create mode 100644 src/logfile_sub_source.cfg.hh create mode 100644 src/logfile_sub_source.hh create mode 100644 src/mapbox/optional.hpp create mode 100644 src/mapbox/recursive_wrapper.hpp create mode 100644 src/mapbox/variant.hpp create mode 100644 src/mapbox/variant_cast.hpp create mode 100644 src/mapbox/variant_io.hpp create mode 100644 src/mapbox/variant_visitor.hpp create mode 100644 src/md2attr_line.cc create mode 100644 src/md2attr_line.hh create mode 100644 src/md4cpp.cc create mode 100644 src/md4cpp.hh create mode 100644 src/network-extension-functions.cc create mode 100644 src/optional.hpp create mode 100644 src/pcap_manager.cc create mode 100644 src/pcap_manager.hh create mode 100644 src/pcrepp/CMakeLists.txt create mode 100644 src/pcrepp/Makefile.am create mode 100644 src/pcrepp/pcre2pp.cc create mode 100644 src/pcrepp/pcre2pp.hh create mode 100644 src/pcrepp/test_pcre2pp.cc create mode 100644 src/piper_proc.cc create mode 100644 src/piper_proc.hh create mode 100644 src/plain_text_source.cc create mode 100644 src/plain_text_source.hh create mode 100644 src/plugins.hh create mode 100644 src/pollable.cc create mode 100644 src/pollable.hh create mode 100644 src/pretty_printer.cc create mode 100644 src/pretty_printer.hh create mode 100644 src/preview_status_source.hh create mode 100644 src/ptimec.c create mode 100644 src/ptimec.hh create mode 100644 src/ptimec_rt.cc create mode 100644 src/pugixml/Makefile.am create mode 100644 src/pugixml/pugiconfig.hpp create mode 100644 src/pugixml/pugixml.cpp create mode 100644 src/pugixml/pugixml.hpp create mode 100644 src/readline_callbacks.cc create mode 100644 src/readline_callbacks.hh create mode 100644 src/readline_context.hh create mode 100644 src/readline_curses.cc create mode 100644 src/readline_curses.hh create mode 100644 src/readline_highlighters.cc create mode 100644 src/readline_highlighters.hh create mode 100644 src/readline_possibilities.cc create mode 100644 src/readline_possibilities.hh create mode 100644 src/regex101.client.cc create mode 100644 src/regex101.client.hh create mode 100644 src/regex101.import.cc create mode 100644 src/regex101.import.hh create mode 100644 src/regexp_vtab.cc create mode 100644 src/regexp_vtab.hh create mode 100644 src/relative_time.cc create mode 100644 src/relative_time.hh create mode 100644 src/remote/CMakeLists.txt create mode 100644 src/remote/Makefile.am create mode 100644 src/remote/remote.ssh.cc create mode 100644 src/remote/remote.ssh.hh create mode 100644 src/ring_span.hh create mode 100644 src/root-config.json create mode 100644 src/safe/accessmode.h create mode 100644 src/safe/defaulttypes.h create mode 100644 src/safe/mutableref.h create mode 100644 src/safe/safe.h create mode 100644 src/scripts/README.md create mode 100644 src/scripts/dhclient-summary.lnav create mode 100755 src/scripts/dump-pid.sh create mode 100644 src/scripts/lnav-pop-view.lnav create mode 100644 src/scripts/partition-by-boot.lnav create mode 100644 src/scripts/rename-stdin.lnav create mode 100644 src/scripts/scripts.am create mode 100644 src/scripts/search-for.lnav create mode 100644 src/sequence_matcher.cc create mode 100644 src/sequence_matcher.hh create mode 100644 src/sequence_sink.hh create mode 100644 src/service_tags.hh create mode 100644 src/session.export.cc create mode 100644 src/session.export.hh create mode 100644 src/session_data.cc create mode 100644 src/session_data.hh create mode 100644 src/shared_buffer.cc create mode 100644 src/shared_buffer.hh create mode 100644 src/shlex.cc create mode 100644 src/shlex.hh create mode 100644 src/shlex.resolver.hh create mode 100644 src/simdutf8check.h create mode 100644 src/spectro_impls.cc create mode 100644 src/spectro_impls.hh create mode 100644 src/spectro_source.cc create mode 100644 src/spectro_source.hh create mode 100644 src/spookyhash/SpookyV2.cpp create mode 100644 src/spookyhash/SpookyV2.h create mode 100644 src/sql_commands.cc create mode 100644 src/sql_help.hh create mode 100644 src/sql_util.cc create mode 100644 src/sql_util.hh create mode 100644 src/sqlite-extension-func.cc create mode 100644 src/sqlite-extension-func.hh create mode 100644 src/sqlitepp.cc create mode 100644 src/sqlitepp.client.hh create mode 100644 src/sqlitepp.hh create mode 100644 src/state-extension-functions.cc create mode 100644 src/static_file_vtab.cc create mode 100644 src/static_file_vtab.hh create mode 100644 src/statusview_curses.cc create mode 100644 src/statusview_curses.hh create mode 100644 src/string-extension-functions.cc create mode 100644 src/strong_int.hh create mode 100644 src/styling.cc create mode 100644 src/styling.hh create mode 100644 src/sysclip.cc create mode 100644 src/sysclip.cfg.hh create mode 100644 src/sysclip.hh create mode 100644 src/tailer/CMakeLists.txt create mode 100644 src/tailer/Makefile.am create mode 100644 src/tailer/README.md create mode 100644 src/tailer/drive_tailer.cc create mode 100644 src/tailer/sha-256.c create mode 100644 src/tailer/sha-256.h create mode 100755 src/tailer/tailer.ape create mode 100644 src/tailer/tailer.c create mode 100644 src/tailer/tailer.h create mode 100644 src/tailer/tailer.looper.cc create mode 100644 src/tailer/tailer.looper.cfg.hh create mode 100644 src/tailer/tailer.looper.hh create mode 100644 src/tailer/tailer.main.c create mode 100644 src/tailer/tailerpp.cc create mode 100644 src/tailer/tailerpp.hh create mode 100755 src/tailer/test_tailer.sh create mode 100644 src/term_extra.hh create mode 100644 src/termios_guard.hh create mode 100644 src/test_override.c create mode 100644 src/text_anonymizer.cc create mode 100644 src/text_anonymizer.hh create mode 100644 src/text_format.cc create mode 100644 src/text_format.hh create mode 100644 src/textfile_highlighters.cc create mode 100644 src/textfile_highlighters.hh create mode 100644 src/textfile_sub_source.cc create mode 100644 src/textfile_sub_source.hh create mode 100644 src/textview_curses.cc create mode 100644 src/textview_curses.hh create mode 100644 src/textview_curses_fwd.hh create mode 100644 src/themes/README.md create mode 100644 src/themes/default-theme.json create mode 100644 src/themes/eldar.json create mode 100644 src/themes/grayscale.json create mode 100644 src/themes/monocai.json create mode 100644 src/themes/night-owl.json create mode 100644 src/themes/solarized-dark.json create mode 100644 src/themes/solarized-light.json create mode 100644 src/themes/themes.am create mode 100644 src/third-party/ArenaAlloc/arenaalloc.h create mode 100644 src/third-party/ArenaAlloc/arenaallocimpl.h create mode 100644 src/third-party/ArenaAlloc/recyclealloc.h create mode 100644 src/third-party/CLI/App.hpp create mode 100644 src/third-party/CLI/CLI.hpp create mode 100644 src/third-party/CLI/Config.hpp create mode 100644 src/third-party/CLI/ConfigFwd.hpp create mode 100644 src/third-party/CLI/Error.hpp create mode 100644 src/third-party/CLI/Formatter.hpp create mode 100644 src/third-party/CLI/FormatterFwd.hpp create mode 100644 src/third-party/CLI/Macros.hpp create mode 100644 src/third-party/CLI/Option.hpp create mode 100644 src/third-party/CLI/Split.hpp create mode 100644 src/third-party/CLI/StringTools.hpp create mode 100644 src/third-party/CLI/Timer.hpp create mode 100644 src/third-party/CLI/TypeTools.hpp create mode 100644 src/third-party/CLI/Validators.hpp create mode 100644 src/third-party/CLI/Version.hpp create mode 100644 src/third-party/backward-cpp/backward.hpp create mode 100644 src/third-party/base64/LICENSE create mode 100644 src/third-party/base64/include/libbase64.h create mode 100644 src/third-party/base64/lib/Makefile.am create mode 100644 src/third-party/base64/lib/arch/avx/codec.c create mode 100644 src/third-party/base64/lib/arch/avx2/codec.c create mode 100644 src/third-party/base64/lib/arch/avx2/dec_loop.c create mode 100644 src/third-party/base64/lib/arch/avx2/dec_reshuffle.c create mode 100644 src/third-party/base64/lib/arch/avx2/enc_loop.c create mode 100644 src/third-party/base64/lib/arch/avx2/enc_reshuffle.c create mode 100644 src/third-party/base64/lib/arch/avx2/enc_translate.c create mode 100644 src/third-party/base64/lib/arch/generic/32/dec_loop.c create mode 100644 src/third-party/base64/lib/arch/generic/32/enc_loop.c create mode 100644 src/third-party/base64/lib/arch/generic/64/enc_loop.c create mode 100644 src/third-party/base64/lib/arch/generic/codec.c create mode 100644 src/third-party/base64/lib/arch/generic/dec_head.c create mode 100644 src/third-party/base64/lib/arch/generic/dec_tail.c create mode 100644 src/third-party/base64/lib/arch/generic/enc_head.c create mode 100644 src/third-party/base64/lib/arch/generic/enc_tail.c create mode 100644 src/third-party/base64/lib/arch/neon32/codec.c create mode 100644 src/third-party/base64/lib/arch/neon32/dec_loop.c create mode 100644 src/third-party/base64/lib/arch/neon32/enc_loop.c create mode 100644 src/third-party/base64/lib/arch/neon32/enc_reshuffle.c create mode 100644 src/third-party/base64/lib/arch/neon32/enc_translate.c create mode 100644 src/third-party/base64/lib/arch/neon64/codec.c create mode 100644 src/third-party/base64/lib/arch/neon64/dec_loop.c create mode 100644 src/third-party/base64/lib/arch/neon64/enc_loop.c create mode 100644 src/third-party/base64/lib/arch/neon64/enc_reshuffle.c create mode 100644 src/third-party/base64/lib/arch/sse41/codec.c create mode 100644 src/third-party/base64/lib/arch/sse42/codec.c create mode 100644 src/third-party/base64/lib/arch/ssse3/codec.c create mode 100644 src/third-party/base64/lib/arch/ssse3/dec_loop.c create mode 100644 src/third-party/base64/lib/arch/ssse3/dec_reshuffle.c create mode 100644 src/third-party/base64/lib/arch/ssse3/enc_loop.c create mode 100644 src/third-party/base64/lib/arch/ssse3/enc_reshuffle.c create mode 100644 src/third-party/base64/lib/arch/ssse3/enc_translate.c create mode 100644 src/third-party/base64/lib/codec_choose.c create mode 100644 src/third-party/base64/lib/codecs.h create mode 100644 src/third-party/base64/lib/config.h create mode 100644 src/third-party/base64/lib/env.h create mode 100644 src/third-party/base64/lib/lib.c create mode 100644 src/third-party/base64/lib/lib_openmp.c create mode 100644 src/third-party/base64/lib/tables/table_dec_32bit.h create mode 100644 src/third-party/base64/lib/tables/table_enc_12bit.h create mode 100644 src/third-party/base64/lib/tables/tables.c create mode 100644 src/third-party/base64/lib/tables/tables.h create mode 100644 src/third-party/doctest-root/doctest/doctest.h create mode 100644 src/third-party/intervaltree/IntervalTree.h create mode 100644 src/third-party/md4c/md4c.c create mode 100644 src/third-party/md4c/md4c.h create mode 100644 src/third-party/rapidyaml/ryml_all.hpp create mode 100644 src/third-party/robin_hood/robin_hood.h create mode 100644 src/third-party/scnlib/include/scn/all.h create mode 100644 src/third-party/scnlib/include/scn/detail/args.h create mode 100644 src/third-party/scnlib/include/scn/detail/config.h create mode 100644 src/third-party/scnlib/include/scn/detail/context.h create mode 100644 src/third-party/scnlib/include/scn/detail/error.h create mode 100644 src/third-party/scnlib/include/scn/detail/file.h create mode 100644 src/third-party/scnlib/include/scn/detail/fwd.h create mode 100644 src/third-party/scnlib/include/scn/detail/locale.h create mode 100644 src/third-party/scnlib/include/scn/detail/parse_context.h create mode 100644 src/third-party/scnlib/include/scn/detail/range.h create mode 100644 src/third-party/scnlib/include/scn/detail/result.h create mode 100644 src/third-party/scnlib/include/scn/detail/vectored.h create mode 100644 src/third-party/scnlib/include/scn/detail/visitor.h create mode 100644 src/third-party/scnlib/include/scn/fwd.h create mode 100644 src/third-party/scnlib/include/scn/istream.h create mode 100644 src/third-party/scnlib/include/scn/ranges/custom_impl.h create mode 100644 src/third-party/scnlib/include/scn/ranges/ranges.h create mode 100644 src/third-party/scnlib/include/scn/ranges/std_impl.h create mode 100644 src/third-party/scnlib/include/scn/ranges/util.h create mode 100644 src/third-party/scnlib/include/scn/reader/common.h create mode 100644 src/third-party/scnlib/include/scn/reader/float.h create mode 100644 src/third-party/scnlib/include/scn/reader/int.h create mode 100644 src/third-party/scnlib/include/scn/reader/reader.h create mode 100644 src/third-party/scnlib/include/scn/reader/string.h create mode 100644 src/third-party/scnlib/include/scn/reader/types.h create mode 100644 src/third-party/scnlib/include/scn/scan/common.h create mode 100644 src/third-party/scnlib/include/scn/scan/getline.h create mode 100644 src/third-party/scnlib/include/scn/scan/ignore.h create mode 100644 src/third-party/scnlib/include/scn/scan/istream.h create mode 100644 src/third-party/scnlib/include/scn/scan/list.h create mode 100644 src/third-party/scnlib/include/scn/scan/scan.h create mode 100644 src/third-party/scnlib/include/scn/scan/vscan.h create mode 100644 src/third-party/scnlib/include/scn/scn.h create mode 100644 src/third-party/scnlib/include/scn/tuple_return.h create mode 100644 src/third-party/scnlib/include/scn/tuple_return/tuple_return.h create mode 100644 src/third-party/scnlib/include/scn/tuple_return/util.h create mode 100644 src/third-party/scnlib/include/scn/unicode/common.h create mode 100644 src/third-party/scnlib/include/scn/unicode/unicode.h create mode 100644 src/third-party/scnlib/include/scn/unicode/utf16.h create mode 100644 src/third-party/scnlib/include/scn/unicode/utf8.h create mode 100644 src/third-party/scnlib/include/scn/util/algorithm.h create mode 100644 src/third-party/scnlib/include/scn/util/array.h create mode 100644 src/third-party/scnlib/include/scn/util/expected.h create mode 100644 src/third-party/scnlib/include/scn/util/math.h create mode 100644 src/third-party/scnlib/include/scn/util/memory.h create mode 100644 src/third-party/scnlib/include/scn/util/meta.h create mode 100644 src/third-party/scnlib/include/scn/util/optional.h create mode 100644 src/third-party/scnlib/include/scn/util/small_vector.h create mode 100644 src/third-party/scnlib/include/scn/util/span.h create mode 100644 src/third-party/scnlib/include/scn/util/string_view.h create mode 100644 src/third-party/scnlib/include/scn/util/unique_ptr.h create mode 100644 src/third-party/scnlib/licenses/README.md create mode 100644 src/third-party/scnlib/licenses/fast_float-apache.txt create mode 100644 src/third-party/scnlib/licenses/fast_float-mit.txt create mode 100644 src/third-party/scnlib/licenses/fmt.rst create mode 100644 src/third-party/scnlib/licenses/nanorange.txt create mode 100644 src/third-party/scnlib/licenses/utfcpp.txt create mode 100644 src/third-party/scnlib/src/Makefile.am create mode 100644 src/third-party/scnlib/src/deps/fast_float/single_include/fast_float/fast_float.h create mode 100644 src/third-party/scnlib/src/file.cpp create mode 100644 src/third-party/scnlib/src/locale.cpp create mode 100644 src/third-party/scnlib/src/reader_float.cpp create mode 100644 src/third-party/scnlib/src/reader_int.cpp create mode 100644 src/third-party/scnlib/src/vscan.cpp create mode 100644 src/third-party/sqlite/ext/dbdump.c create mode 100644 src/third-party/sqlite/ext/series.c create mode 100644 src/third-party/xxHash/xxh_x86dispatch.c create mode 100644 src/third-party/xxHash/xxh_x86dispatch.h create mode 100644 src/third-party/xxHash/xxhash.c create mode 100644 src/third-party/xxHash/xxhash.h create mode 100644 src/time-extension-functions.cc create mode 100644 src/time_T.hh create mode 100644 src/time_formats.am create mode 100644 src/timer.cc create mode 100644 src/timer.hh create mode 100644 src/top_status_source.cc create mode 100644 src/top_status_source.cfg.hh create mode 100644 src/top_status_source.hh create mode 100644 src/top_sys_status_source.hh create mode 100644 src/unique_path.cc create mode 100644 src/unique_path.hh create mode 100644 src/url_loader.hh create mode 100644 src/view_curses.cc create mode 100644 src/view_curses.hh create mode 100644 src/view_helpers.cc create mode 100644 src/view_helpers.crumbs.hh create mode 100644 src/view_helpers.examples.hh create mode 100644 src/view_helpers.hh create mode 100644 src/view_helpers.hist.hh create mode 100644 src/views_vtab.cc create mode 100644 src/views_vtab.hh create mode 100644 src/vis_line.hh create mode 100644 src/vt52_curses.cc create mode 100644 src/vt52_curses.hh create mode 100644 src/vtab_module.cc create mode 100644 src/vtab_module.hh create mode 100644 src/vtab_module_json.hh create mode 100644 src/words.json create mode 100644 src/ww898/cp_utf8.hpp create mode 100644 src/xml-entities.json create mode 100644 src/xml_util.cc create mode 100644 src/xml_util.hh create mode 100644 src/xpath_vtab.cc create mode 100644 src/xpath_vtab.hh create mode 100644 src/xterm-palette.json create mode 100644 src/xterm_mouse.cc create mode 100644 src/xterm_mouse.hh create mode 100644 src/yajl/CMakeLists.txt create mode 100644 src/yajl/Makefile.am create mode 100644 src/yajl/api/yajl_common.h create mode 100644 src/yajl/api/yajl_gen.h create mode 100644 src/yajl/api/yajl_parse.h create mode 100644 src/yajl/api/yajl_tree.h create mode 100644 src/yajl/yajl.c create mode 100644 src/yajl/yajl_alloc.c create mode 100644 src/yajl/yajl_alloc.h create mode 100644 src/yajl/yajl_buf.c create mode 100644 src/yajl/yajl_buf.h create mode 100644 src/yajl/yajl_bytestack.h create mode 100644 src/yajl/yajl_common.h create mode 100644 src/yajl/yajl_encode.c create mode 100644 src/yajl/yajl_encode.h create mode 100644 src/yajl/yajl_gen.c create mode 100644 src/yajl/yajl_lex.c create mode 100644 src/yajl/yajl_lex.h create mode 100644 src/yajl/yajl_parser.c create mode 100644 src/yajl/yajl_parser.h create mode 100644 src/yajl/yajl_tree.c create mode 100644 src/yajl/yajl_version.c create mode 100644 src/yajl/yajl_version.h create mode 100644 src/yajlpp/CMakeLists.txt create mode 100644 src/yajlpp/Makefile.am create mode 100644 src/yajlpp/README.md create mode 100644 src/yajlpp/drive_json_op.cc create mode 100644 src/yajlpp/drive_json_ptr_walk.cc create mode 100644 src/yajlpp/json_op.cc create mode 100644 src/yajlpp/json_op.hh create mode 100644 src/yajlpp/json_ptr.cc create mode 100644 src/yajlpp/json_ptr.hh create mode 100755 src/yajlpp/test_json_op.sh create mode 100644 src/yajlpp/test_json_ptr.cc create mode 100644 src/yajlpp/test_json_ptr_walk.sh create mode 100644 src/yajlpp/test_yajlpp.cc create mode 100644 src/yajlpp/yajlpp.cc create mode 100644 src/yajlpp/yajlpp.hh create mode 100644 src/yajlpp/yajlpp_def.hh create mode 100644 src/yaml-extension-functions.cc create mode 100644 test/CMakeLists.txt create mode 100644 test/Makefile.am create mode 100644 test/UTF-8-test.txt create mode 100644 test/aftest.cc create mode 100644 test/ansi-colors.0.in create mode 100644 test/bad-config-json/formats/invalid-json/format.json create mode 100644 test/bad-config-json/formats/invalid-key/format.json create mode 100644 test/bad-config/formats/invalid-json-format/format.json create mode 100644 test/bad-config/formats/invalid-properties/format.json create mode 100644 test/bad-config/formats/invalid-regex/format.json create mode 100644 test/bad-config/formats/invalid-sample/format.json create mode 100644 test/bad-config/formats/invalid-schema/format.json create mode 100644 test/bad-config/formats/invalid-sql/init.sql create mode 100644 test/bad-config/formats/invalid-sql/init2.sql create mode 100644 test/bad-config/formats/no-regexes/format.json create mode 100644 test/bad-config/formats/no-samples/format.json create mode 100644 test/bad-config2/formats/invalid-config/config.bad-schema.json create mode 100644 test/bad-config2/formats/invalid-config/config.json create mode 100644 test/bad-config2/formats/invalid-config/config.malformed.json create mode 100644 test/bad-config2/formats/invalid-config/config.truncated.json create mode 100644 test/books.xml create mode 100644 test/datafile_ipaddr.0 create mode 100644 test/datafile_simple.0 create mode 100644 test/datafile_simple.1 create mode 100644 test/datafile_simple.10 create mode 100644 test/datafile_simple.11 create mode 100644 test/datafile_simple.12 create mode 100644 test/datafile_simple.13 create mode 100644 test/datafile_simple.14 create mode 100644 test/datafile_simple.15 create mode 100644 test/datafile_simple.16 create mode 100644 test/datafile_simple.17 create mode 100644 test/datafile_simple.18 create mode 100644 test/datafile_simple.19 create mode 100644 test/datafile_simple.2 create mode 100644 test/datafile_simple.20 create mode 100644 test/datafile_simple.21 create mode 100644 test/datafile_simple.22 create mode 100644 test/datafile_simple.23 create mode 100644 test/datafile_simple.3 create mode 100644 test/datafile_simple.4 create mode 100644 test/datafile_simple.5 create mode 100644 test/datafile_simple.6 create mode 100644 test/datafile_simple.7 create mode 100644 test/datafile_simple.8 create mode 100644 test/datafile_simple.9 create mode 100644 test/datafile_syslog.0 create mode 100644 test/datafile_syslog.1 create mode 100644 test/datafile_xml.0 create mode 100644 test/dhcp-trunc.pcapng create mode 100644 test/dhcp.pcapng create mode 100644 test/document.sections.tests.cc create mode 100644 test/drive_data_scanner.cc create mode 100644 test/drive_grep_proc.cc create mode 100644 test/drive_line_buffer.cc create mode 100644 test/drive_listview.cc create mode 100644 test/drive_logfile.cc create mode 100644 test/drive_mvwattrline.cc create mode 100644 test/drive_readline_curses.cc create mode 100644 test/drive_sequencer.cc create mode 100644 test/drive_shlexer.cc create mode 100644 test/drive_sql.cc create mode 100644 test/drive_sql_anno.cc create mode 100644 test/drive_view_colors.cc create mode 100644 test/drive_vt52_curses.cc create mode 100644 test/expected/expected.am create mode 100644 test/expected/test_cli.sh_17a68b798354f9a6cdfab372006caeb74038d15c.err create mode 100644 test/expected/test_cli.sh_17a68b798354f9a6cdfab372006caeb74038d15c.out create mode 100644 test/expected/test_cli.sh_5524542b1a6954ff9741155101497270a2f0c557.err create mode 100644 test/expected/test_cli.sh_5524542b1a6954ff9741155101497270a2f0c557.out create mode 100644 test/expected/test_cli.sh_97e19b9ff3775d84074455a2e8993a0611b1c269.err create mode 100644 test/expected/test_cli.sh_97e19b9ff3775d84074455a2e8993a0611b1c269.out create mode 100644 test/expected/test_cli.sh_a1a09f890f4604309d0a81bbbec8e50fb7d5e887.err create mode 100644 test/expected/test_cli.sh_a1a09f890f4604309d0a81bbbec8e50fb7d5e887.out create mode 100644 test/expected/test_cli.sh_f2e41555f1a5f40f54ce241207af602ed1503a2b.err create mode 100644 test/expected/test_cli.sh_f2e41555f1a5f40f54ce241207af602ed1503a2b.out create mode 100644 test/expected/test_cmds.sh_017b495b95218b7c083951e2dba331cfec6e90be.err create mode 100644 test/expected/test_cmds.sh_017b495b95218b7c083951e2dba331cfec6e90be.out create mode 100644 test/expected/test_cmds.sh_0b1e4b1523dfca71927b1fe721c74490c51361d1.err create mode 100644 test/expected/test_cmds.sh_0b1e4b1523dfca71927b1fe721c74490c51361d1.out create mode 100644 test/expected/test_cmds.sh_0b41fe57743ba0be088037d9ba29bc465e7c9bf9.err create mode 100644 test/expected/test_cmds.sh_0b41fe57743ba0be088037d9ba29bc465e7c9bf9.out create mode 100644 test/expected/test_cmds.sh_0f0ab532d8d845f8201af65bf5f6fc994e21a8aa.err create mode 100644 test/expected/test_cmds.sh_0f0ab532d8d845f8201af65bf5f6fc994e21a8aa.out create mode 100644 test/expected/test_cmds.sh_109a44ac6a8f1be2736c8e9c47aeed187e0581ee.err create mode 100644 test/expected/test_cmds.sh_109a44ac6a8f1be2736c8e9c47aeed187e0581ee.out create mode 100644 test/expected/test_cmds.sh_12856706bfb4a8e2686098dd2644a7989d370b02.err create mode 100644 test/expected/test_cmds.sh_12856706bfb4a8e2686098dd2644a7989d370b02.out create mode 100644 test/expected/test_cmds.sh_12b4cb9bd6586f9694100db76734b19a75158eab.err create mode 100644 test/expected/test_cmds.sh_12b4cb9bd6586f9694100db76734b19a75158eab.out create mode 100644 test/expected/test_cmds.sh_145126309709179759926289caf729703ef6e1c6.err create mode 100644 test/expected/test_cmds.sh_145126309709179759926289caf729703ef6e1c6.out create mode 100644 test/expected/test_cmds.sh_148007d2626b3c92d00ac31639b6918b1fc4aa60.err create mode 100644 test/expected/test_cmds.sh_148007d2626b3c92d00ac31639b6918b1fc4aa60.out create mode 100644 test/expected/test_cmds.sh_1cab7d240cf85ff2c3538f5a06af141b01bc83ad.err create mode 100644 test/expected/test_cmds.sh_1cab7d240cf85ff2c3538f5a06af141b01bc83ad.out create mode 100644 test/expected/test_cmds.sh_1d92c5bc12f5e7aaa6d84c5ed47f0b9f96e36c6a.err create mode 100644 test/expected/test_cmds.sh_1d92c5bc12f5e7aaa6d84c5ed47f0b9f96e36c6a.out create mode 100644 test/expected/test_cmds.sh_1e1c8492b295913ce5afcd104cde0ec4ca1dcdac.err create mode 100644 test/expected/test_cmds.sh_1e1c8492b295913ce5afcd104cde0ec4ca1dcdac.out create mode 100644 test/expected/test_cmds.sh_1f53f5b16c7c5aa695ed2e6427d822a1b940fcf4.err create mode 100644 test/expected/test_cmds.sh_1f53f5b16c7c5aa695ed2e6427d822a1b940fcf4.out create mode 100644 test/expected/test_cmds.sh_22577861cb0921a7e7f3d1af6485938f4930ba7b.err create mode 100644 test/expected/test_cmds.sh_22577861cb0921a7e7f3d1af6485938f4930ba7b.out create mode 100644 test/expected/test_cmds.sh_2339d09953b6937981d8a448000c3fdc2837f8c4.err create mode 100644 test/expected/test_cmds.sh_2339d09953b6937981d8a448000c3fdc2837f8c4.out create mode 100644 test/expected/test_cmds.sh_2539ff9c4dbed93df3f0408ccc5fd81df34d1193.err create mode 100644 test/expected/test_cmds.sh_2539ff9c4dbed93df3f0408ccc5fd81df34d1193.out create mode 100644 test/expected/test_cmds.sh_29f0c808f4e93c6ef3890e6b793bee274a5b36ca.err create mode 100644 test/expected/test_cmds.sh_29f0c808f4e93c6ef3890e6b793bee274a5b36ca.out create mode 100644 test/expected/test_cmds.sh_2a449c0a43e895e85c8b1c9547f32d7b5b4f84f6.err create mode 100644 test/expected/test_cmds.sh_2a449c0a43e895e85c8b1c9547f32d7b5b4f84f6.out create mode 100644 test/expected/test_cmds.sh_2a535de164de4c060d2bff34aa7cc75ac7cac2c2.err create mode 100644 test/expected/test_cmds.sh_2a535de164de4c060d2bff34aa7cc75ac7cac2c2.out create mode 100644 test/expected/test_cmds.sh_2cd167954a3be3e130e5f9601b72794a856cef92.err create mode 100644 test/expected/test_cmds.sh_2cd167954a3be3e130e5f9601b72794a856cef92.out create mode 100644 test/expected/test_cmds.sh_2de9ec294e2f533d13e04c70d9525f8b58d47bb2.err create mode 100644 test/expected/test_cmds.sh_2de9ec294e2f533d13e04c70d9525f8b58d47bb2.out create mode 100644 test/expected/test_cmds.sh_2e123104cdd2087ac40731a0aa533ba6a87ea744.err create mode 100644 test/expected/test_cmds.sh_2e123104cdd2087ac40731a0aa533ba6a87ea744.out create mode 100644 test/expected/test_cmds.sh_2e67bdbbc9a14aa772b2a9f755ed8f8124708558.err create mode 100644 test/expected/test_cmds.sh_2e67bdbbc9a14aa772b2a9f755ed8f8124708558.out create mode 100644 test/expected/test_cmds.sh_2ff0fe712c9b0012e42282c5f77b0b83cad37ddf.err create mode 100644 test/expected/test_cmds.sh_2ff0fe712c9b0012e42282c5f77b0b83cad37ddf.out create mode 100644 test/expected/test_cmds.sh_305b1dfdfe785b945df4220aad6671ae1d364f55.err create mode 100644 test/expected/test_cmds.sh_305b1dfdfe785b945df4220aad6671ae1d364f55.out create mode 100644 test/expected/test_cmds.sh_3429080ed14d01c6a887900186f37750df0d5ff0.err create mode 100644 test/expected/test_cmds.sh_3429080ed14d01c6a887900186f37750df0d5ff0.out create mode 100644 test/expected/test_cmds.sh_34a6bcaa2877471b8ea718374101fa9ce3b78235.err create mode 100644 test/expected/test_cmds.sh_34a6bcaa2877471b8ea718374101fa9ce3b78235.out create mode 100644 test/expected/test_cmds.sh_35b0dd8a030396742bc5acfde7715fb19f312f29.err create mode 100644 test/expected/test_cmds.sh_35b0dd8a030396742bc5acfde7715fb19f312f29.out create mode 100644 test/expected/test_cmds.sh_36800217930a6a30e68c4efb20f6959c4f71aeb0.err create mode 100644 test/expected/test_cmds.sh_36800217930a6a30e68c4efb20f6959c4f71aeb0.out create mode 100644 test/expected/test_cmds.sh_38fa2a95b703d4ce12e82882eca1938264822690.err create mode 100644 test/expected/test_cmds.sh_38fa2a95b703d4ce12e82882eca1938264822690.out create mode 100644 test/expected/test_cmds.sh_3b20a298e2c059d7f6045cbc0c07ca3db3917695.err create mode 100644 test/expected/test_cmds.sh_3b20a298e2c059d7f6045cbc0c07ca3db3917695.out create mode 100644 test/expected/test_cmds.sh_453054e29aaca4c2662c45c2a1f2f63f3510d8dd.err create mode 100644 test/expected/test_cmds.sh_453054e29aaca4c2662c45c2a1f2f63f3510d8dd.out create mode 100644 test/expected/test_cmds.sh_4b2d91b19008d5b775090e3ef87c111f9e603b15.err create mode 100644 test/expected/test_cmds.sh_4b2d91b19008d5b775090e3ef87c111f9e603b15.out create mode 100644 test/expected/test_cmds.sh_4dbe20c11056a07d2c7efb5ed15903050d628216.err create mode 100644 test/expected/test_cmds.sh_4dbe20c11056a07d2c7efb5ed15903050d628216.out create mode 100644 test/expected/test_cmds.sh_4f06183ed231669965965f5042fbbb507fa7deab.err create mode 100644 test/expected/test_cmds.sh_4f06183ed231669965965f5042fbbb507fa7deab.out create mode 100644 test/expected/test_cmds.sh_512872aebaae73ca4f33fa93acb2f4e3b018f8b4.err create mode 100644 test/expected/test_cmds.sh_512872aebaae73ca4f33fa93acb2f4e3b018f8b4.out create mode 100644 test/expected/test_cmds.sh_53a9686102f69b07b034df291f554a00b265ed20.err create mode 100644 test/expected/test_cmds.sh_53a9686102f69b07b034df291f554a00b265ed20.out create mode 100644 test/expected/test_cmds.sh_55c2fd15ec2c7d96dbef7b36a42a1b7b42f90dbc.err create mode 100644 test/expected/test_cmds.sh_55c2fd15ec2c7d96dbef7b36a42a1b7b42f90dbc.out create mode 100644 test/expected/test_cmds.sh_5bfd08c1639701476d7b9348c36afd46fdbe6f2a.err create mode 100644 test/expected/test_cmds.sh_5bfd08c1639701476d7b9348c36afd46fdbe6f2a.out create mode 100644 test/expected/test_cmds.sh_624a41e152675575f4b07c19b2cf0e3a028429a2.err create mode 100644 test/expected/test_cmds.sh_624a41e152675575f4b07c19b2cf0e3a028429a2.out create mode 100644 test/expected/test_cmds.sh_62d68c0a11757c996f24c8f003e6b4059c3e30b2.err create mode 100644 test/expected/test_cmds.sh_62d68c0a11757c996f24c8f003e6b4059c3e30b2.out create mode 100644 test/expected/test_cmds.sh_661ec61acdd8f6fa6ec1e3c2cf5f896eef431351.err create mode 100644 test/expected/test_cmds.sh_661ec61acdd8f6fa6ec1e3c2cf5f896eef431351.out create mode 100644 test/expected/test_cmds.sh_6a6031113aca32fabc5a3da64b7be46f5ce5a312.err create mode 100644 test/expected/test_cmds.sh_6a6031113aca32fabc5a3da64b7be46f5ce5a312.out create mode 100644 test/expected/test_cmds.sh_6e016c0ed61fc652be1a79b864875ffede64f281.err create mode 100644 test/expected/test_cmds.sh_6e016c0ed61fc652be1a79b864875ffede64f281.out create mode 100644 test/expected/test_cmds.sh_7270e37dab4549cfa7c5232451c031e1e04b4aef.err create mode 100644 test/expected/test_cmds.sh_7270e37dab4549cfa7c5232451c031e1e04b4aef.out create mode 100644 test/expected/test_cmds.sh_73ea99c84fb1d4570e8bcd45c423b4a28fe41e81.err create mode 100644 test/expected/test_cmds.sh_73ea99c84fb1d4570e8bcd45c423b4a28fe41e81.out create mode 100644 test/expected/test_cmds.sh_7cb644890c4b945ff3f1e15c86a58c85cb5425c0.err create mode 100644 test/expected/test_cmds.sh_7cb644890c4b945ff3f1e15c86a58c85cb5425c0.out create mode 100644 test/expected/test_cmds.sh_7e14e7f18219719453838835fa96c3451f78996d.err create mode 100644 test/expected/test_cmds.sh_7e14e7f18219719453838835fa96c3451f78996d.out create mode 100644 test/expected/test_cmds.sh_819b3dd21348f7242f3914ad0a8c5b1cdb3f91af.err create mode 100644 test/expected/test_cmds.sh_819b3dd21348f7242f3914ad0a8c5b1cdb3f91af.out create mode 100644 test/expected/test_cmds.sh_8298805f897346b4bb0f14e53c06b4fa28e309e3.err create mode 100644 test/expected/test_cmds.sh_8298805f897346b4bb0f14e53c06b4fa28e309e3.out create mode 100644 test/expected/test_cmds.sh_83654557317602d2e00adde1e5cba190d9db0dff.err create mode 100644 test/expected/test_cmds.sh_83654557317602d2e00adde1e5cba190d9db0dff.out create mode 100644 test/expected/test_cmds.sh_85ae6ac1eb9a8378f7a6c39659f52671218ce64b.err create mode 100644 test/expected/test_cmds.sh_85ae6ac1eb9a8378f7a6c39659f52671218ce64b.out create mode 100644 test/expected/test_cmds.sh_85ed177028f226e86b1d164eb1a4e18eaf036c9d.err create mode 100644 test/expected/test_cmds.sh_85ed177028f226e86b1d164eb1a4e18eaf036c9d.out create mode 100644 test/expected/test_cmds.sh_8758082427d6232a15053433942a4b5ad9f2e3ce.err create mode 100644 test/expected/test_cmds.sh_8758082427d6232a15053433942a4b5ad9f2e3ce.out create mode 100644 test/expected/test_cmds.sh_876116da8ab46c0c8a212ce230d1b8a13970f78f.err create mode 100644 test/expected/test_cmds.sh_876116da8ab46c0c8a212ce230d1b8a13970f78f.out create mode 100644 test/expected/test_cmds.sh_8765cbf326648e9014f8cf5f761895010fff443a.err create mode 100644 test/expected/test_cmds.sh_8765cbf326648e9014f8cf5f761895010fff443a.out create mode 100644 test/expected/test_cmds.sh_89afa826d1b33be6926df48443faa1d1c5f285a7.err create mode 100644 test/expected/test_cmds.sh_89afa826d1b33be6926df48443faa1d1c5f285a7.out create mode 100644 test/expected/test_cmds.sh_8d5b43c693e78804a8fb06989392fa8cccb46b7b.err create mode 100644 test/expected/test_cmds.sh_8d5b43c693e78804a8fb06989392fa8cccb46b7b.out create mode 100644 test/expected/test_cmds.sh_9445861db011dfa2d21a44788047de345ee291e8.err create mode 100644 test/expected/test_cmds.sh_9445861db011dfa2d21a44788047de345ee291e8.out create mode 100644 test/expected/test_cmds.sh_95beaabe41d72cf4c6810e79c623da759ac1c71b.err create mode 100644 test/expected/test_cmds.sh_95beaabe41d72cf4c6810e79c623da759ac1c71b.out create mode 100644 test/expected/test_cmds.sh_968dac54dc80d91a5da2322890c6c26dfa0d8462.err create mode 100644 test/expected/test_cmds.sh_968dac54dc80d91a5da2322890c6c26dfa0d8462.out create mode 100644 test/expected/test_cmds.sh_a00943ef715598c7554b85de8502454e41bb9e28.err create mode 100644 test/expected/test_cmds.sh_a00943ef715598c7554b85de8502454e41bb9e28.out create mode 100644 test/expected/test_cmds.sh_a0e6214b2a85c90d31aee12efde850441cca7eb3.err create mode 100644 test/expected/test_cmds.sh_a0e6214b2a85c90d31aee12efde850441cca7eb3.out create mode 100644 test/expected/test_cmds.sh_a1123427c31c022433d66d05ee5d5e1c8ab415e4.err create mode 100644 test/expected/test_cmds.sh_a1123427c31c022433d66d05ee5d5e1c8ab415e4.out create mode 100644 test/expected/test_cmds.sh_a190bfc279fa046a823864f1484f899d27d22953.err create mode 100644 test/expected/test_cmds.sh_a190bfc279fa046a823864f1484f899d27d22953.out create mode 100644 test/expected/test_cmds.sh_a5742238bad948b1372d32f7a491f03fa4e8b711.err create mode 100644 test/expected/test_cmds.sh_a5742238bad948b1372d32f7a491f03fa4e8b711.out create mode 100644 test/expected/test_cmds.sh_a6c431f2871ea96cfdf4e11465b3bca543c7b678.err create mode 100644 test/expected/test_cmds.sh_a6c431f2871ea96cfdf4e11465b3bca543c7b678.out create mode 100644 test/expected/test_cmds.sh_a8006c4169d76baecd99a0699c2fc66a583ad676.err create mode 100644 test/expected/test_cmds.sh_a8006c4169d76baecd99a0699c2fc66a583ad676.out create mode 100644 test/expected/test_cmds.sh_ac45fb0f8f9578c3ded0855f694698ec38ce31ad.err create mode 100644 test/expected/test_cmds.sh_ac45fb0f8f9578c3ded0855f694698ec38ce31ad.out create mode 100644 test/expected/test_cmds.sh_af0fcbd30b3fd0d13477aa3325ef0302052a4d9f.err create mode 100644 test/expected/test_cmds.sh_af0fcbd30b3fd0d13477aa3325ef0302052a4d9f.out create mode 100644 test/expected/test_cmds.sh_b5a530d16c982cf769151291f0bfd612ea71183f.err create mode 100644 test/expected/test_cmds.sh_b5a530d16c982cf769151291f0bfd612ea71183f.out create mode 100644 test/expected/test_cmds.sh_b6a3bb78e9d60e5e1f5ce5b18e40d2f1662707ab.err create mode 100644 test/expected/test_cmds.sh_b6a3bb78e9d60e5e1f5ce5b18e40d2f1662707ab.out create mode 100644 test/expected/test_cmds.sh_b755a8b48c0f602f0270500b0117b76e11db546e.err create mode 100644 test/expected/test_cmds.sh_b755a8b48c0f602f0270500b0117b76e11db546e.out create mode 100644 test/expected/test_cmds.sh_b7fcd26c45c850c3d43ce25b1f610a311eb898c5.err create mode 100644 test/expected/test_cmds.sh_b7fcd26c45c850c3d43ce25b1f610a311eb898c5.out create mode 100644 test/expected/test_cmds.sh_b9f8bf53ec2736432eb048d94a391175eb4dc5bf.err create mode 100644 test/expected/test_cmds.sh_b9f8bf53ec2736432eb048d94a391175eb4dc5bf.out create mode 100644 test/expected/test_cmds.sh_bc60341827636715c14c562863da9733cbde7e68.err create mode 100644 test/expected/test_cmds.sh_bc60341827636715c14c562863da9733cbde7e68.out create mode 100644 test/expected/test_cmds.sh_be1d9628fc447b6f17121d9457ea1602afe8f3f3.err create mode 100644 test/expected/test_cmds.sh_be1d9628fc447b6f17121d9457ea1602afe8f3f3.out create mode 100644 test/expected/test_cmds.sh_be3b7c5874b5f4d86cc230bd2f9802c98909e148.err create mode 100644 test/expected/test_cmds.sh_be3b7c5874b5f4d86cc230bd2f9802c98909e148.out create mode 100644 test/expected/test_cmds.sh_bf4e7fad67e281beaa11b6e2b03a00b419c7c9b0.err create mode 100644 test/expected/test_cmds.sh_bf4e7fad67e281beaa11b6e2b03a00b419c7c9b0.out create mode 100644 test/expected/test_cmds.sh_c01e10f7cae8d36fa79ae03be887cb5477025f6d.err create mode 100644 test/expected/test_cmds.sh_c01e10f7cae8d36fa79ae03be887cb5477025f6d.out create mode 100644 test/expected/test_cmds.sh_c2b4431dd0cc36c6201d263b727b3305e8cda6b1.err create mode 100644 test/expected/test_cmds.sh_c2b4431dd0cc36c6201d263b727b3305e8cda6b1.out create mode 100644 test/expected/test_cmds.sh_c4777849c39a6c34dea5b0279cd7400692f1ab5f.err create mode 100644 test/expected/test_cmds.sh_c4777849c39a6c34dea5b0279cd7400692f1ab5f.out create mode 100644 test/expected/test_cmds.sh_c4a15771f7e1487bf73b2e9d1564ad8ecfd76c7e.err create mode 100644 test/expected/test_cmds.sh_c4a15771f7e1487bf73b2e9d1564ad8ecfd76c7e.out create mode 100644 test/expected/test_cmds.sh_c72aed622c19d493968e33f20d5dde3838a4258f.err create mode 100644 test/expected/test_cmds.sh_c72aed622c19d493968e33f20d5dde3838a4258f.out create mode 100644 test/expected/test_cmds.sh_c7fabc25374ff47c47931f63b1d697061b816a28.err create mode 100644 test/expected/test_cmds.sh_c7fabc25374ff47c47931f63b1d697061b816a28.out create mode 100644 test/expected/test_cmds.sh_ca66660c973f76a3c2a147c7f5035bcb4e8a8bbc.err create mode 100644 test/expected/test_cmds.sh_ca66660c973f76a3c2a147c7f5035bcb4e8a8bbc.out create mode 100644 test/expected/test_cmds.sh_ccd326da92d1cacda63501cd1a3077381a18e8f2.err create mode 100644 test/expected/test_cmds.sh_ccd326da92d1cacda63501cd1a3077381a18e8f2.out create mode 100644 test/expected/test_cmds.sh_d3b69abdfb39e4bfa5828c2f9593e2b2b7ed4d5d.err create mode 100644 test/expected/test_cmds.sh_d3b69abdfb39e4bfa5828c2f9593e2b2b7ed4d5d.out create mode 100644 test/expected/test_cmds.sh_d76d77ad95b9f120825417a6a8220c13df9541fc.err create mode 100644 test/expected/test_cmds.sh_d76d77ad95b9f120825417a6a8220c13df9541fc.out create mode 100644 test/expected/test_cmds.sh_d7eebacdcf2cb194f25fa4ef97b7b5376b442467.err create mode 100644 test/expected/test_cmds.sh_d7eebacdcf2cb194f25fa4ef97b7b5376b442467.out create mode 100644 test/expected/test_cmds.sh_d836c84398c831c976df46f46fe3bf5983c44c37.err create mode 100644 test/expected/test_cmds.sh_d836c84398c831c976df46f46fe3bf5983c44c37.out create mode 100644 test/expected/test_cmds.sh_d8eeef53a58bdeddbc1028d7c525413e3ca1c8df.err create mode 100644 test/expected/test_cmds.sh_d8eeef53a58bdeddbc1028d7c525413e3ca1c8df.out create mode 100644 test/expected/test_cmds.sh_dbdd62995fdefc8318053af05a32416eccfa79fc.err create mode 100644 test/expected/test_cmds.sh_dbdd62995fdefc8318053af05a32416eccfa79fc.out create mode 100644 test/expected/test_cmds.sh_dd41fbbcd71699314af232156d4155fbdf849131.err create mode 100644 test/expected/test_cmds.sh_dd41fbbcd71699314af232156d4155fbdf849131.out create mode 100644 test/expected/test_cmds.sh_df6f4cea16bb8f20e6408fe4b40335e6de8a7f18.err create mode 100644 test/expected/test_cmds.sh_df6f4cea16bb8f20e6408fe4b40335e6de8a7f18.out create mode 100644 test/expected/test_cmds.sh_e495cf059477e3f80c3241c6f8d5808b6f1d19c7.err create mode 100644 test/expected/test_cmds.sh_e495cf059477e3f80c3241c6f8d5808b6f1d19c7.out create mode 100644 test/expected/test_cmds.sh_e7e8244fac65bc51dbd5af31be476fe3b8776bfc.err create mode 100644 test/expected/test_cmds.sh_e7e8244fac65bc51dbd5af31be476fe3b8776bfc.out create mode 100644 test/expected/test_cmds.sh_e911aebcb2defb7471aa620c45a86cad449ad505.err create mode 100644 test/expected/test_cmds.sh_e911aebcb2defb7471aa620c45a86cad449ad505.out create mode 100644 test/expected/test_cmds.sh_eb22c3e94c536a1bfaeae0c40d271b5b4b08f4fc.err create mode 100644 test/expected/test_cmds.sh_eb22c3e94c536a1bfaeae0c40d271b5b4b08f4fc.out create mode 100644 test/expected/test_cmds.sh_ec2b28c6ea328e3ea56b13ab8ca3d9ee856a9dda.err create mode 100644 test/expected/test_cmds.sh_ec2b28c6ea328e3ea56b13ab8ca3d9ee856a9dda.out create mode 100644 test/expected/test_cmds.sh_ed5b73be0b991e0e8d6735e31df5b37c4286321b.err create mode 100644 test/expected/test_cmds.sh_ed5b73be0b991e0e8d6735e31df5b37c4286321b.out create mode 100644 test/expected/test_cmds.sh_f788d5f5932905d09ecbd581040ec5ce76459da5.err create mode 100644 test/expected/test_cmds.sh_f788d5f5932905d09ecbd581040ec5ce76459da5.out create mode 100644 test/expected/test_cmds.sh_ff6faebbde8586e04bfadba14a3d2bb4451784ad.err create mode 100644 test/expected/test_cmds.sh_ff6faebbde8586e04bfadba14a3d2bb4451784ad.out create mode 100644 test/expected/test_config.sh_2765ea0d4c037b8c935840604edb0ae796c97a04.err create mode 100644 test/expected/test_config.sh_2765ea0d4c037b8c935840604edb0ae796c97a04.out create mode 100644 test/expected/test_config.sh_5105c29004e297521310ca0bd0fd560b01c2c549.err create mode 100644 test/expected/test_config.sh_5105c29004e297521310ca0bd0fd560b01c2c549.out create mode 100644 test/expected/test_config.sh_5fd9fbccc35e9b06abdd913da0c16bdb306b926e.err create mode 100644 test/expected/test_config.sh_5fd9fbccc35e9b06abdd913da0c16bdb306b926e.out create mode 100644 test/expected/test_config.sh_a0907769aba112d628e7ebe39c4ec252e5e0bc69.err create mode 100644 test/expected/test_config.sh_a0907769aba112d628e7ebe39c4ec252e5e0bc69.out create mode 100644 test/expected/test_config.sh_b08f7523659d1c12f0e59920cd40d17d4a83b72f.err create mode 100644 test/expected/test_config.sh_b08f7523659d1c12f0e59920cd40d17d4a83b72f.out create mode 100644 test/expected/test_config.sh_d622658dc98327b1b2fd346802d24bc633e34ac7.err create mode 100644 test/expected/test_config.sh_d622658dc98327b1b2fd346802d24bc633e34ac7.out create mode 100644 test/expected/test_config.sh_d708b6fd32d83ce0ee00ca5383388308ba5a06e1.err create mode 100644 test/expected/test_config.sh_d708b6fd32d83ce0ee00ca5383388308ba5a06e1.out create mode 100644 test/expected/test_config.sh_eec3768ebc201ca63bca1411270965f78db1abfc.err create mode 100644 test/expected/test_config.sh_eec3768ebc201ca63bca1411270965f78db1abfc.out create mode 100644 test/expected/test_events.sh_09ba47d70bfca88e89faf29598c1095292cad435.err create mode 100644 test/expected/test_events.sh_09ba47d70bfca88e89faf29598c1095292cad435.out create mode 100644 test/expected/test_events.sh_153e221f3cb50f4d3e4581be0bf311e62489c42d.err create mode 100644 test/expected/test_events.sh_153e221f3cb50f4d3e4581be0bf311e62489c42d.out create mode 100644 test/expected/test_events.sh_3dae146ef3bf201c43656344803694a34a3dbfec.err create mode 100644 test/expected/test_events.sh_3dae146ef3bf201c43656344803694a34a3dbfec.out create mode 100644 test/expected/test_events.sh_6f9523d43f174397829b6a7fe6ee0090d97df5f9.err create mode 100644 test/expected/test_events.sh_6f9523d43f174397829b6a7fe6ee0090d97df5f9.out create mode 100644 test/expected/test_events.sh_729f77b8e7136d64d22a6610a80ba6b584a2d896.err create mode 100644 test/expected/test_events.sh_729f77b8e7136d64d22a6610a80ba6b584a2d896.out create mode 100644 test/expected/test_events.sh_d9c7907f907b2335e1328b23fdc46d0968a608d9.err create mode 100644 test/expected/test_events.sh_d9c7907f907b2335e1328b23fdc46d0968a608d9.out create mode 100644 test/expected/test_events.sh_ed8dc44add223341c03ccb7b3e18371bdb42b710.err create mode 100644 test/expected/test_events.sh_ed8dc44add223341c03ccb7b3e18371bdb42b710.out create mode 100644 test/expected/test_format_loader.sh_15e861d2327512a721fd42ae51dc5427689e0bb6.err create mode 100644 test/expected/test_format_loader.sh_15e861d2327512a721fd42ae51dc5427689e0bb6.out create mode 100644 test/expected/test_format_loader.sh_3f1d6f35e8a9ae4fd3e91ffaa82a037b5a847ab7.err create mode 100644 test/expected/test_format_loader.sh_3f1d6f35e8a9ae4fd3e91ffaa82a037b5a847ab7.out create mode 100644 test/expected/test_format_loader.sh_5992e2695b7e6cf1f3520dbb87af8fc2b8f27088.err create mode 100644 test/expected/test_format_loader.sh_5992e2695b7e6cf1f3520dbb87af8fc2b8f27088.out create mode 100644 test/expected/test_format_loader.sh_a47f2b090a5d8a226783835c7ff7d1c8821f11ed.err create mode 100644 test/expected/test_format_loader.sh_a47f2b090a5d8a226783835c7ff7d1c8821f11ed.out create mode 100644 test/expected/test_format_loader.sh_fca6c1fb9f3aaa69b3ffb2d1a8a86434b2f4a247.err create mode 100644 test/expected/test_format_loader.sh_fca6c1fb9f3aaa69b3ffb2d1a8a86434b2f4a247.out create mode 100644 test/expected/test_json_format.sh_168cac40c27f547044c89d39eb0ff2ef81da4b21.err create mode 100644 test/expected/test_json_format.sh_168cac40c27f547044c89d39eb0ff2ef81da4b21.out create mode 100644 test/expected/test_json_format.sh_1bb0fd243e916546aea22029245ac590dae17a86.err create mode 100644 test/expected/test_json_format.sh_1bb0fd243e916546aea22029245ac590dae17a86.out create mode 100644 test/expected/test_json_format.sh_40223ac4742883f883ccc61044bfffd6e102cca6.err create mode 100644 test/expected/test_json_format.sh_40223ac4742883f883ccc61044bfffd6e102cca6.out create mode 100644 test/expected/test_json_format.sh_4315a3d6124c14cbe3c474b6dbf4cc8720a9859f.err create mode 100644 test/expected/test_json_format.sh_4315a3d6124c14cbe3c474b6dbf4cc8720a9859f.out create mode 100644 test/expected/test_json_format.sh_469f005b0708d629bc95f0c48a5e390f440c1fef.err create mode 100644 test/expected/test_json_format.sh_469f005b0708d629bc95f0c48a5e390f440c1fef.out create mode 100644 test/expected/test_json_format.sh_6fbe20faa161ab9fa77df7568fff84bf3e47e920.err create mode 100644 test/expected/test_json_format.sh_6fbe20faa161ab9fa77df7568fff84bf3e47e920.out create mode 100644 test/expected/test_json_format.sh_7724d1a96d74d4418dd44d7416270f9bb64b2564.err create mode 100644 test/expected/test_json_format.sh_7724d1a96d74d4418dd44d7416270f9bb64b2564.out create mode 100644 test/expected/test_json_format.sh_7aade92cff911c5b3cfc733685809f949ae35778.err create mode 100644 test/expected/test_json_format.sh_7aade92cff911c5b3cfc733685809f949ae35778.out create mode 100644 test/expected/test_json_format.sh_7c6529f6bf4a0cb565f5665fdcba032f0ae1ebbe.err create mode 100644 test/expected/test_json_format.sh_7c6529f6bf4a0cb565f5665fdcba032f0ae1ebbe.out create mode 100644 test/expected/test_json_format.sh_80959e2bb6a7fdf938c2e4dbd7d7c81eb84fa072.err create mode 100644 test/expected/test_json_format.sh_80959e2bb6a7fdf938c2e4dbd7d7c81eb84fa072.out create mode 100644 test/expected/test_json_format.sh_84a71e94dc34661a70bb9015b67ba00e93e9cfb5.err create mode 100644 test/expected/test_json_format.sh_84a71e94dc34661a70bb9015b67ba00e93e9cfb5.out create mode 100644 test/expected/test_json_format.sh_85d03b1b41a7f819af135d2521a8f2c59418e907.err create mode 100644 test/expected/test_json_format.sh_85d03b1b41a7f819af135d2521a8f2c59418e907.out create mode 100644 test/expected/test_json_format.sh_8f2ebcd319afc7966ef11e31f9dd646bf6f001dd.err create mode 100644 test/expected/test_json_format.sh_8f2ebcd319afc7966ef11e31f9dd646bf6f001dd.out create mode 100644 test/expected/test_json_format.sh_90a037c7d9d70ac4ca97158271ea242787313377.err create mode 100644 test/expected/test_json_format.sh_90a037c7d9d70ac4ca97158271ea242787313377.out create mode 100644 test/expected/test_json_format.sh_952297a90e312d2184fe3e4df795ddc731b096c9.err create mode 100644 test/expected/test_json_format.sh_952297a90e312d2184fe3e4df795ddc731b096c9.out create mode 100644 test/expected/test_json_format.sh_989e52d167582648b73c5d025cc0e814c642b3c8.err create mode 100644 test/expected/test_json_format.sh_989e52d167582648b73c5d025cc0e814c642b3c8.out create mode 100644 test/expected/test_json_format.sh_a06b3cdd46b387e72d6faa4cce648b8b11ae870b.err create mode 100644 test/expected/test_json_format.sh_a06b3cdd46b387e72d6faa4cce648b8b11ae870b.out create mode 100644 test/expected/test_json_format.sh_a6be47f1311ed92feaf303142fcb103deb80f456.err create mode 100644 test/expected/test_json_format.sh_a6be47f1311ed92feaf303142fcb103deb80f456.out create mode 100644 test/expected/test_json_format.sh_c1a23804c39b0f74642286d69865ee9d0961a58a.err create mode 100644 test/expected/test_json_format.sh_c1a23804c39b0f74642286d69865ee9d0961a58a.out create mode 100644 test/expected/test_json_format.sh_c60050b3469f37c5b0864e1dc7eb354e91d6ec81.err create mode 100644 test/expected/test_json_format.sh_c60050b3469f37c5b0864e1dc7eb354e91d6ec81.out create mode 100644 test/expected/test_json_format.sh_d0ec34389274affb70a5a76ba4789d51fd60f602.err create mode 100644 test/expected/test_json_format.sh_d0ec34389274affb70a5a76ba4789d51fd60f602.out create mode 100644 test/expected/test_json_format.sh_d7362cffc8335c2fe6b6527315de59bd6f5dcc7f.err create mode 100644 test/expected/test_json_format.sh_d7362cffc8335c2fe6b6527315de59bd6f5dcc7f.out create mode 100644 test/expected/test_json_format.sh_dfff27a651650a04d93de9a06ab5480e94ce3a79.err create mode 100644 test/expected/test_json_format.sh_dfff27a651650a04d93de9a06ab5480e94ce3a79.out create mode 100644 test/expected/test_json_format.sh_fe19b7ebd349cd689b3f5c22618eab5ce995e68e.err create mode 100644 test/expected/test_json_format.sh_fe19b7ebd349cd689b3f5c22618eab5ce995e68e.out create mode 100644 test/expected/test_logfile.sh_08d731a04c877a34819b35de185e30a74c9fd497.err create mode 100644 test/expected/test_logfile.sh_08d731a04c877a34819b35de185e30a74c9fd497.out create mode 100644 test/expected/test_logfile.sh_09bd16e044302f6b121092534708594bdad11b5a.err create mode 100644 test/expected/test_logfile.sh_09bd16e044302f6b121092534708594bdad11b5a.out create mode 100644 test/expected/test_logfile.sh_1c6eee38f66356fcd9a9f0faedaea6dbcc901060.err create mode 100644 test/expected/test_logfile.sh_1c6eee38f66356fcd9a9f0faedaea6dbcc901060.out create mode 100644 test/expected/test_logfile.sh_218ecb88b4753010c4264b3ac351260b4811612f.err create mode 100644 test/expected/test_logfile.sh_218ecb88b4753010c4264b3ac351260b4811612f.out create mode 100644 test/expected/test_logfile.sh_290a3c49e53c2229a7400c107338fa0bb38375e2.err create mode 100644 test/expected/test_logfile.sh_290a3c49e53c2229a7400c107338fa0bb38375e2.out create mode 100644 test/expected/test_logfile.sh_3fc6bfd8a6160817211f3e14fde957af75b9dbe7.err create mode 100644 test/expected/test_logfile.sh_3fc6bfd8a6160817211f3e14fde957af75b9dbe7.out create mode 100644 test/expected/test_logfile.sh_4a2a907fcb069b8d6e65961a7b2e796d6c3a87b1.err create mode 100644 test/expected/test_logfile.sh_4a2a907fcb069b8d6e65961a7b2e796d6c3a87b1.out create mode 100644 test/expected/test_logfile.sh_6602faf7817c494c33e32da7ee95f13aa9210d01.err create mode 100644 test/expected/test_logfile.sh_6602faf7817c494c33e32da7ee95f13aa9210d01.out create mode 100644 test/expected/test_logfile.sh_7c2e11488bccc59458b5775db4b90de964858259.err create mode 100644 test/expected/test_logfile.sh_7c2e11488bccc59458b5775db4b90de964858259.out create mode 100644 test/expected/test_logfile.sh_a7037efd0c4bbf51940137a44e57d94e9307e83e.err create mode 100644 test/expected/test_logfile.sh_a7037efd0c4bbf51940137a44e57d94e9307e83e.out create mode 100644 test/expected/test_logfile.sh_c18e14a26d8261c9f72747118a469266121d5459.err create mode 100644 test/expected/test_logfile.sh_c18e14a26d8261c9f72747118a469266121d5459.out create mode 100644 test/expected/test_logfile.sh_e840b674cd65936a72bd64b1dac1524d16fe44c3.err create mode 100644 test/expected/test_logfile.sh_e840b674cd65936a72bd64b1dac1524d16fe44c3.out create mode 100644 test/expected/test_meta.sh_154047fb52e4831aabf7d36512247bad6a6a2cf7.err create mode 100644 test/expected/test_meta.sh_154047fb52e4831aabf7d36512247bad6a6a2cf7.out create mode 100644 test/expected/test_meta.sh_3c9b5940f7533c5fc3d4956a6efce50a9e7132d4.err create mode 100644 test/expected/test_meta.sh_3c9b5940f7533c5fc3d4956a6efce50a9e7132d4.out create mode 100644 test/expected/test_meta.sh_41f643bb4f720130625b042563e9591bee4ae588.err create mode 100644 test/expected/test_meta.sh_41f643bb4f720130625b042563e9591bee4ae588.out create mode 100644 test/expected/test_meta.sh_45ff39a3d0ac0ca0c95aaca14d043450cec1cedd.err create mode 100644 test/expected/test_meta.sh_45ff39a3d0ac0ca0c95aaca14d043450cec1cedd.out create mode 100644 test/expected/test_meta.sh_48e85ba0c0945a5085fb4ee255771406061a9c17.err create mode 100644 test/expected/test_meta.sh_48e85ba0c0945a5085fb4ee255771406061a9c17.out create mode 100644 test/expected/test_meta.sh_4c39b356748c67ccf8a6027a1af88da532f8252a.err create mode 100644 test/expected/test_meta.sh_4c39b356748c67ccf8a6027a1af88da532f8252a.out create mode 100644 test/expected/test_meta.sh_7b75763926d832bf9784ca234a060859770aabe7.err create mode 100644 test/expected/test_meta.sh_7b75763926d832bf9784ca234a060859770aabe7.out create mode 100644 test/expected/test_meta.sh_811b1a8a176b25001a89e35b295a1117ab76969b.err create mode 100644 test/expected/test_meta.sh_811b1a8a176b25001a89e35b295a1117ab76969b.out create mode 100644 test/expected/test_meta.sh_83ac877aa9d38b25945cf96d6326a2468187c40f.err create mode 100644 test/expected/test_meta.sh_83ac877aa9d38b25945cf96d6326a2468187c40f.out create mode 100644 test/expected/test_meta.sh_a7489c1f0e001adc732b7e2ab31bb30960fda078.err create mode 100644 test/expected/test_meta.sh_a7489c1f0e001adc732b7e2ab31bb30960fda078.out create mode 100644 test/expected/test_meta.sh_c063f96398650f130941bbbf4cf63c1244fdbee5.err create mode 100644 test/expected/test_meta.sh_c063f96398650f130941bbbf4cf63c1244fdbee5.out create mode 100644 test/expected/test_meta.sh_c75128169049bd88d5eaf8b84a7f617e5ae5d936.err create mode 100644 test/expected/test_meta.sh_c75128169049bd88d5eaf8b84a7f617e5ae5d936.out create mode 100644 test/expected/test_meta.sh_c8fb22932af2467a2651797a8a8d8cddcd09431d.err create mode 100644 test/expected/test_meta.sh_c8fb22932af2467a2651797a8a8d8cddcd09431d.out create mode 100644 test/expected/test_meta.sh_d6af0b41066ca3be0bbce89c83c011f4ecfa516e.err create mode 100644 test/expected/test_meta.sh_d6af0b41066ca3be0bbce89c83c011f4ecfa516e.out create mode 100644 test/expected/test_meta.sh_fd09cb565f44a114d8c9a519e571918e30262eaf.err create mode 100644 test/expected/test_meta.sh_fd09cb565f44a114d8c9a519e571918e30262eaf.out create mode 100644 test/expected/test_meta.sh_fdf4a91aa55262255816dff7d605f1f0a5d6fe92.err create mode 100644 test/expected/test_meta.sh_fdf4a91aa55262255816dff7d605f1f0a5d6fe92.out create mode 100644 test/expected/test_pretty_print.sh_3c255c3c8b28df9d694b329a265e8b8140dae4a2.err create mode 100644 test/expected/test_pretty_print.sh_3c255c3c8b28df9d694b329a265e8b8140dae4a2.out create mode 100644 test/expected/test_pretty_print.sh_4111e649fb49c0a377e552fa0b56c60c370633da.err create mode 100644 test/expected/test_pretty_print.sh_4111e649fb49c0a377e552fa0b56c60c370633da.out create mode 100644 test/expected/test_pretty_print.sh_675a2ff6306df7c54127e39319cf06a2dd353145.err create mode 100644 test/expected/test_pretty_print.sh_675a2ff6306df7c54127e39319cf06a2dd353145.out create mode 100644 test/expected/test_pretty_print.sh_7192f8f68adb14705c8a60e73ff8248c61c7fd03.err create mode 100644 test/expected/test_pretty_print.sh_7192f8f68adb14705c8a60e73ff8248c61c7fd03.out create mode 100644 test/expected/test_pretty_print.sh_a5bee322ea3374690e44a88a16cb6b84feaa11d3.err create mode 100644 test/expected/test_pretty_print.sh_a5bee322ea3374690e44a88a16cb6b84feaa11d3.out create mode 100644 test/expected/test_pretty_print.sh_a6d9042e5e95f2a49194bd80c1eed154813ddf41.err create mode 100644 test/expected/test_pretty_print.sh_a6d9042e5e95f2a49194bd80c1eed154813ddf41.out create mode 100644 test/expected/test_pretty_print.sh_cd361eeca7e91bfab942b75d6c3422c7a456a111.err create mode 100644 test/expected/test_pretty_print.sh_cd361eeca7e91bfab942b75d6c3422c7a456a111.out create mode 100644 test/expected/test_pretty_print.sh_f8feb52a321026d9562b271eb37a2c56dfaed329.err create mode 100644 test/expected/test_pretty_print.sh_f8feb52a321026d9562b271eb37a2c56dfaed329.out create mode 100644 test/expected/test_regex101.sh_0fa3663a45aca6a328cb728872af7ed7ee896f1c.err create mode 100644 test/expected/test_regex101.sh_0fa3663a45aca6a328cb728872af7ed7ee896f1c.out create mode 100644 test/expected/test_regex101.sh_182ae9244db314a953af2bee969726e381bc5a32.err create mode 100644 test/expected/test_regex101.sh_182ae9244db314a953af2bee969726e381bc5a32.out create mode 100644 test/expected/test_regex101.sh_2158f1f011ba8e1b152396c072790c076fdb8ce8.err create mode 100644 test/expected/test_regex101.sh_2158f1f011ba8e1b152396c072790c076fdb8ce8.out create mode 100644 test/expected/test_regex101.sh_281af24141680330791db7f7c5fa70833ce08a6b.err create mode 100644 test/expected/test_regex101.sh_281af24141680330791db7f7c5fa70833ce08a6b.out create mode 100644 test/expected/test_regex101.sh_35703b13990785632cca82123fb3883797959c0b.err create mode 100644 test/expected/test_regex101.sh_35703b13990785632cca82123fb3883797959c0b.out create mode 100644 test/expected/test_regex101.sh_366730cac50b4a09b7de4b84641791470b1cb9a3.err create mode 100644 test/expected/test_regex101.sh_366730cac50b4a09b7de4b84641791470b1cb9a3.out create mode 100644 test/expected/test_regex101.sh_3d18474a3e472fff6e23e0c41337ec9188fee591.err create mode 100644 test/expected/test_regex101.sh_3d18474a3e472fff6e23e0c41337ec9188fee591.out create mode 100644 test/expected/test_regex101.sh_442cc58676590a3604d5c2183f5fe0a75c98351a.err create mode 100644 test/expected/test_regex101.sh_442cc58676590a3604d5c2183f5fe0a75c98351a.out create mode 100644 test/expected/test_regex101.sh_566fd88d216a44bc1c6e23f2d6f2d0caf99d42f9.err create mode 100644 test/expected/test_regex101.sh_566fd88d216a44bc1c6e23f2d6f2d0caf99d42f9.out create mode 100644 test/expected/test_regex101.sh_5f2f7ecb6ab9cbec4b41385b91bd038906b8a7b2.err create mode 100644 test/expected/test_regex101.sh_5f2f7ecb6ab9cbec4b41385b91bd038906b8a7b2.out create mode 100644 test/expected/test_regex101.sh_629bde30483e0a6461076e9058f3a5eb81ae0425.err create mode 100644 test/expected/test_regex101.sh_629bde30483e0a6461076e9058f3a5eb81ae0425.out create mode 100644 test/expected/test_regex101.sh_630db454054cf92ec9bd0f4e3e83300047f583ff.err create mode 100644 test/expected/test_regex101.sh_630db454054cf92ec9bd0f4e3e83300047f583ff.out create mode 100644 test/expected/test_regex101.sh_771af6f3d29b8350542d5c6e98bdbf4c223cd531.err create mode 100644 test/expected/test_regex101.sh_771af6f3d29b8350542d5c6e98bdbf4c223cd531.out create mode 100644 test/expected/test_regex101.sh_7991a5b617867cf37c9f7baa85ffa425f7d455a2.err create mode 100644 test/expected/test_regex101.sh_7991a5b617867cf37c9f7baa85ffa425f7d455a2.out create mode 100644 test/expected/test_regex101.sh_79ee3f5fe71ccec97b2619d8c1f74ca97ffd2243.err create mode 100644 test/expected/test_regex101.sh_79ee3f5fe71ccec97b2619d8c1f74ca97ffd2243.out create mode 100644 test/expected/test_regex101.sh_7de76c174c58d67bf93e8f01d6d55ebb6a023f10.err create mode 100644 test/expected/test_regex101.sh_7de76c174c58d67bf93e8f01d6d55ebb6a023f10.out create mode 100644 test/expected/test_regex101.sh_8a43e6657d4f60e68d31eb8302542ca28e80d077.err create mode 100644 test/expected/test_regex101.sh_8a43e6657d4f60e68d31eb8302542ca28e80d077.out create mode 100644 test/expected/test_regex101.sh_8e93a3b6b941847c71409a297779fbb0a6666a51.err create mode 100644 test/expected/test_regex101.sh_8e93a3b6b941847c71409a297779fbb0a6666a51.out create mode 100644 test/expected/test_regex101.sh_95c56a9d146ec9a7c2196559d316f928b2ae6ae9.err create mode 100644 test/expected/test_regex101.sh_95c56a9d146ec9a7c2196559d316f928b2ae6ae9.out create mode 100644 test/expected/test_regex101.sh_9d101ee29c45cdb8c0f117ad736c9a5dd5da5839.err create mode 100644 test/expected/test_regex101.sh_9d101ee29c45cdb8c0f117ad736c9a5dd5da5839.out create mode 100644 test/expected/test_regex101.sh_c43e07df9b3068696fdc8759c7561135db981b38.err create mode 100644 test/expected/test_regex101.sh_c43e07df9b3068696fdc8759c7561135db981b38.out create mode 100644 test/expected/test_regex101.sh_cbd859487e4ea011cd6e0f0f114d70158bfd8b43.err create mode 100644 test/expected/test_regex101.sh_cbd859487e4ea011cd6e0f0f114d70158bfd8b43.out create mode 100644 test/expected/test_regex101.sh_cf6c0a9f0f04e24ce1fae7a0a434830b14447f83.err create mode 100644 test/expected/test_regex101.sh_cf6c0a9f0f04e24ce1fae7a0a434830b14447f83.out create mode 100644 test/expected/test_regex101.sh_d84597760285c3964b258726341e018f6cd49954.err create mode 100644 test/expected/test_regex101.sh_d84597760285c3964b258726341e018f6cd49954.out create mode 100644 test/expected/test_regex101.sh_f23e393dbf23d0d8e276e9b7610c7b74d79980f8.err create mode 100644 test/expected/test_regex101.sh_f23e393dbf23d0d8e276e9b7610c7b74d79980f8.out create mode 100644 test/expected/test_regex101.sh_fc41b6ee90cbf038620151f16d164b361acf82dd.err create mode 100644 test/expected/test_regex101.sh_fc41b6ee90cbf038620151f16d164b361acf82dd.out create mode 100644 test/expected/test_sessions.sh_0300a1391c33b1c45ddfa90198a6bd0a5404a77f.err create mode 100644 test/expected/test_sessions.sh_0300a1391c33b1c45ddfa90198a6bd0a5404a77f.out create mode 100644 test/expected/test_sessions.sh_17b85654b929b2a8fc1705a170ced544783292fa.err create mode 100644 test/expected/test_sessions.sh_17b85654b929b2a8fc1705a170ced544783292fa.out create mode 100644 test/expected/test_sessions.sh_345b0e66dab7b881397c4b38380da81092ab70dd.err create mode 100644 test/expected/test_sessions.sh_345b0e66dab7b881397c4b38380da81092ab70dd.out create mode 100644 test/expected/test_sessions.sh_430b9522ba1a37983138f3c4935cba91b781e415.err create mode 100644 test/expected/test_sessions.sh_430b9522ba1a37983138f3c4935cba91b781e415.out create mode 100644 test/expected/test_sessions.sh_4f13dd3858546b6e04a27e244159d355e368f2ae.err create mode 100644 test/expected/test_sessions.sh_4f13dd3858546b6e04a27e244159d355e368f2ae.out create mode 100644 test/expected/test_sessions.sh_68a89b56c5e7f7db620084cca1eb547cbb19a2c9.err create mode 100644 test/expected/test_sessions.sh_68a89b56c5e7f7db620084cca1eb547cbb19a2c9.out create mode 100644 test/expected/test_sessions.sh_6d87ff483d5785c58fb271a405ff1c35e4f83cd9.err create mode 100644 test/expected/test_sessions.sh_6d87ff483d5785c58fb271a405ff1c35e4f83cd9.out create mode 100644 test/expected/test_sessions.sh_858fd0081ed9c46dd81e2f81f1090756f2463558.err create mode 100644 test/expected/test_sessions.sh_858fd0081ed9c46dd81e2f81f1090756f2463558.out create mode 100644 test/expected/test_sessions.sh_8732dad5481be991ca7f291d9c5451c7b016cea7.err create mode 100644 test/expected/test_sessions.sh_8732dad5481be991ca7f291d9c5451c7b016cea7.out create mode 100644 test/expected/test_sessions.sh_903b41c950f5f90d7786d7a09bb6e2f217654b15.err create mode 100644 test/expected/test_sessions.sh_903b41c950f5f90d7786d7a09bb6e2f217654b15.out create mode 100644 test/expected/test_sessions.sh_92a98a3e4e3a10bf1f2371d21a8282c5d3d4baa5.err create mode 100644 test/expected/test_sessions.sh_92a98a3e4e3a10bf1f2371d21a8282c5d3d4baa5.out create mode 100644 test/expected/test_sessions.sh_9978aaa475513f9981840e612f853a7707ffcf90.err create mode 100644 test/expected/test_sessions.sh_9978aaa475513f9981840e612f853a7707ffcf90.out create mode 100644 test/expected/test_sessions.sh_a92822d121a836140a401fd71535dc4a7a8d5b48.err create mode 100644 test/expected/test_sessions.sh_a92822d121a836140a401fd71535dc4a7a8d5b48.out create mode 100644 test/expected/test_sessions.sh_b3d71a87fcb4e3487f71ccad8c6ce681db220572.err create mode 100644 test/expected/test_sessions.sh_b3d71a87fcb4e3487f71ccad8c6ce681db220572.out create mode 100644 test/expected/test_sessions.sh_b932b33dd087b94d4306dd179c5d4f9ddd394960.err create mode 100644 test/expected/test_sessions.sh_b932b33dd087b94d4306dd179c5d4f9ddd394960.out create mode 100644 test/expected/test_sessions.sh_ddf45811e9906de9f3930fe802ac7b2cc6e48106.err create mode 100644 test/expected/test_sessions.sh_ddf45811e9906de9f3930fe802ac7b2cc6e48106.out create mode 100644 test/expected/test_sessions.sh_e39648f425c3f291c9d1c0d14595a019abd0cb48.err create mode 100644 test/expected/test_sessions.sh_e39648f425c3f291c9d1c0d14595a019abd0cb48.out create mode 100644 test/expected/test_shlexer.sh_14dd967cb2af90899c9e5e45d00b676b5a3163aa.err create mode 100644 test/expected/test_shlexer.sh_14dd967cb2af90899c9e5e45d00b676b5a3163aa.out create mode 100644 test/expected/test_shlexer.sh_2781f5dd570580cbe746ad91b58a28b8371283b3.err create mode 100644 test/expected/test_shlexer.sh_2781f5dd570580cbe746ad91b58a28b8371283b3.out create mode 100644 test/expected/test_shlexer.sh_2af44d06fc137a77bc230be86376ccad23a2806b.err create mode 100644 test/expected/test_shlexer.sh_2af44d06fc137a77bc230be86376ccad23a2806b.out create mode 100644 test/expected/test_shlexer.sh_6858e530a8ecb77cbaec1a7507768dd5a1942ac9.err create mode 100644 test/expected/test_shlexer.sh_6858e530a8ecb77cbaec1a7507768dd5a1942ac9.out create mode 100644 test/expected/test_shlexer.sh_7f31e16ea2469da7a4328c93c7bcc8e109f84d2f.err create mode 100644 test/expected/test_shlexer.sh_7f31e16ea2469da7a4328c93c7bcc8e109f84d2f.out create mode 100644 test/expected/test_shlexer.sh_8aeebcdef56edd783579eaaddaff7c5cc127bb86.err create mode 100644 test/expected/test_shlexer.sh_8aeebcdef56edd783579eaaddaff7c5cc127bb86.out create mode 100644 test/expected/test_shlexer.sh_8e9addb0e5b6f4254d81dd89ecf12783109644bb.err create mode 100644 test/expected/test_shlexer.sh_8e9addb0e5b6f4254d81dd89ecf12783109644bb.out create mode 100644 test/expected/test_shlexer.sh_90961e6728e96d0a44535a6c9907cc990c10316c.err create mode 100644 test/expected/test_shlexer.sh_90961e6728e96d0a44535a6c9907cc990c10316c.out create mode 100644 test/expected/test_shlexer.sh_95c4e861804a5434900fdb4d67b149d1baa2edf4.err create mode 100644 test/expected/test_shlexer.sh_95c4e861804a5434900fdb4d67b149d1baa2edf4.out create mode 100644 test/expected/test_shlexer.sh_d7fe5f6b8fc9ba00539fad0fa0bfb08319d8b04b.err create mode 100644 test/expected/test_shlexer.sh_d7fe5f6b8fc9ba00539fad0fa0bfb08319d8b04b.out create mode 100644 test/expected/test_shlexer.sh_d9d46422a913e3a06ddbd262933ef5352c30e68f.err create mode 100644 test/expected/test_shlexer.sh_d9d46422a913e3a06ddbd262933ef5352c30e68f.out create mode 100644 test/expected/test_shlexer.sh_e0599f0b53d1bd27af767113853f8e84291f137d.err create mode 100644 test/expected/test_shlexer.sh_e0599f0b53d1bd27af767113853f8e84291f137d.out create mode 100644 test/expected/test_shlexer.sh_e8fa2239ab17e7563d0c524f5400a79d6ff8bfda.err create mode 100644 test/expected/test_shlexer.sh_e8fa2239ab17e7563d0c524f5400a79d6ff8bfda.out create mode 100644 test/expected/test_sql.sh_02def66745b063518473df862987747909f56ccc.err create mode 100644 test/expected/test_sql.sh_02def66745b063518473df862987747909f56ccc.out create mode 100644 test/expected/test_sql.sh_0a5d13b62da4cb66a59a51b0240b5fe0b6036b7e.err create mode 100644 test/expected/test_sql.sh_0a5d13b62da4cb66a59a51b0240b5fe0b6036b7e.out create mode 100644 test/expected/test_sql.sh_0d46ee142f80f262c8c14a22751571cc567df525.err create mode 100644 test/expected/test_sql.sh_0d46ee142f80f262c8c14a22751571cc567df525.out create mode 100644 test/expected/test_sql.sh_13429aed81d7edfd47b57e9cdb8a25c43aff35c4.err create mode 100644 test/expected/test_sql.sh_13429aed81d7edfd47b57e9cdb8a25c43aff35c4.out create mode 100644 test/expected/test_sql.sh_1cbb81cfe40ee16332c5c775a74d06b945aa65c2.err create mode 100644 test/expected/test_sql.sh_1cbb81cfe40ee16332c5c775a74d06b945aa65c2.out create mode 100644 test/expected/test_sql.sh_2532083f215ed44630621f18df3dd7b77c06ae10.err create mode 100644 test/expected/test_sql.sh_2532083f215ed44630621f18df3dd7b77c06ae10.out create mode 100644 test/expected/test_sql.sh_26c0d94d7837792144f2d0f866fb3c12a0bd410d.err create mode 100644 test/expected/test_sql.sh_26c0d94d7837792144f2d0f866fb3c12a0bd410d.out create mode 100644 test/expected/test_sql.sh_2959f0c70fca61a07c6c772f193e73022f7794f1.err create mode 100644 test/expected/test_sql.sh_2959f0c70fca61a07c6c772f193e73022f7794f1.out create mode 100644 test/expected/test_sql.sh_2a16a6fd0ff235a7877e1ea93b22d873a3609402.err create mode 100644 test/expected/test_sql.sh_2a16a6fd0ff235a7877e1ea93b22d873a3609402.out create mode 100644 test/expected/test_sql.sh_2cc8a92c6eb73741080b187a2670d309b8171c90.err create mode 100644 test/expected/test_sql.sh_2cc8a92c6eb73741080b187a2670d309b8171c90.out create mode 100644 test/expected/test_sql.sh_2f15b8a38673ac4db45dc6ed2eafe609c332575b.err create mode 100644 test/expected/test_sql.sh_2f15b8a38673ac4db45dc6ed2eafe609c332575b.out create mode 100644 test/expected/test_sql.sh_31df37f254255115611fc321b63374a2fa4a1cd5.err create mode 100644 test/expected/test_sql.sh_31df37f254255115611fc321b63374a2fa4a1cd5.out create mode 100644 test/expected/test_sql.sh_3d77a2092192caf98e141a6039e886ede836f044.err create mode 100644 test/expected/test_sql.sh_3d77a2092192caf98e141a6039e886ede836f044.out create mode 100644 test/expected/test_sql.sh_4090f96ea11a344c1e2939211da778992dab47d8.err create mode 100644 test/expected/test_sql.sh_4090f96ea11a344c1e2939211da778992dab47d8.out create mode 100644 test/expected/test_sql.sh_4629b626c65a85d7a5595571e195b67afca272ba.err create mode 100644 test/expected/test_sql.sh_4629b626c65a85d7a5595571e195b67afca272ba.out create mode 100644 test/expected/test_sql.sh_50c0b2c93b646b848a017764bde8a4282c556e2d.err create mode 100644 test/expected/test_sql.sh_50c0b2c93b646b848a017764bde8a4282c556e2d.out create mode 100644 test/expected/test_sql.sh_528e48a03cdfa7cfbe263a6e22a65606247a8a95.err create mode 100644 test/expected/test_sql.sh_528e48a03cdfa7cfbe263a6e22a65606247a8a95.out create mode 100644 test/expected/test_sql.sh_5532c7a21e3f6b7df3aad10d7bdfbb7a812ae6c7.err create mode 100644 test/expected/test_sql.sh_5532c7a21e3f6b7df3aad10d7bdfbb7a812ae6c7.out create mode 100644 test/expected/test_sql.sh_56047c9470e515bc3e3709354c01e5d50462cde7.err create mode 100644 test/expected/test_sql.sh_56047c9470e515bc3e3709354c01e5d50462cde7.out create mode 100644 test/expected/test_sql.sh_57427f3c4b4ec785ffff7c5802c10db0d3e547cf.err create mode 100644 test/expected/test_sql.sh_57427f3c4b4ec785ffff7c5802c10db0d3e547cf.out create mode 100644 test/expected/test_sql.sh_57edc93426e6767aa44ab2356c55327553dcdc8d.err create mode 100644 test/expected/test_sql.sh_57edc93426e6767aa44ab2356c55327553dcdc8d.out create mode 100644 test/expected/test_sql.sh_5801770f3e0ecc1d62c7a97116d6da1981bbc7bd.err create mode 100644 test/expected/test_sql.sh_5801770f3e0ecc1d62c7a97116d6da1981bbc7bd.out create mode 100644 test/expected/test_sql.sh_5fe26fe4fc22f23f8dbe3a6aab394602886f2971.err create mode 100644 test/expected/test_sql.sh_5fe26fe4fc22f23f8dbe3a6aab394602886f2971.out create mode 100644 test/expected/test_sql.sh_62eb85c9569e71a630d72065238559528a16114c.err create mode 100644 test/expected/test_sql.sh_62eb85c9569e71a630d72065238559528a16114c.out create mode 100644 test/expected/test_sql.sh_6ad9d0adf85c36363f6b24f49950dcdc13dd34ab.err create mode 100644 test/expected/test_sql.sh_6ad9d0adf85c36363f6b24f49950dcdc13dd34ab.out create mode 100644 test/expected/test_sql.sh_6edb0c8d5323d1b962d90dd6ecdd7eee9008d7b5.err create mode 100644 test/expected/test_sql.sh_6edb0c8d5323d1b962d90dd6ecdd7eee9008d7b5.out create mode 100644 test/expected/test_sql.sh_753c343a256d1286750314957d1b4e155464e03e.err create mode 100644 test/expected/test_sql.sh_753c343a256d1286750314957d1b4e155464e03e.out create mode 100644 test/expected/test_sql.sh_764306f0e5f610ba71f521ba3d19fe158ece0ba5.err create mode 100644 test/expected/test_sql.sh_764306f0e5f610ba71f521ba3d19fe158ece0ba5.out create mode 100644 test/expected/test_sql.sh_7f664c9cda0ae1c48333e21051b5e0eeafd5b4bc.err create mode 100644 test/expected/test_sql.sh_7f664c9cda0ae1c48333e21051b5e0eeafd5b4bc.out create mode 100644 test/expected/test_sql.sh_85fe3b9803254ea54b864d4865d7bd4d7a7f86c6.err create mode 100644 test/expected/test_sql.sh_85fe3b9803254ea54b864d4865d7bd4d7a7f86c6.out create mode 100644 test/expected/test_sql.sh_8ee288f1508eaab0367e465e9f382e848f3282aa.err create mode 100644 test/expected/test_sql.sh_8ee288f1508eaab0367e465e9f382e848f3282aa.out create mode 100644 test/expected/test_sql.sh_9a209f3ee1b1f543ca2587b695d2eb0e63e74c51.err create mode 100644 test/expected/test_sql.sh_9a209f3ee1b1f543ca2587b695d2eb0e63e74c51.out create mode 100644 test/expected/test_sql.sh_9b03e9f7a1bc35e408b3a17ee90cfdadea164df6.err create mode 100644 test/expected/test_sql.sh_9b03e9f7a1bc35e408b3a17ee90cfdadea164df6.out create mode 100644 test/expected/test_sql.sh_9ceccab07fbf7130bffe3c201c710719e4a3e9af.err create mode 100644 test/expected/test_sql.sh_9ceccab07fbf7130bffe3c201c710719e4a3e9af.out create mode 100644 test/expected/test_sql.sh_9e1d05b821822ee40e13fadb24ec558f4bfcff10.err create mode 100644 test/expected/test_sql.sh_9e1d05b821822ee40e13fadb24ec558f4bfcff10.out create mode 100644 test/expected/test_sql.sh_a6b68b9f0044d18e7fa8f9287ddc9110701edc33.err create mode 100644 test/expected/test_sql.sh_a6b68b9f0044d18e7fa8f9287ddc9110701edc33.out create mode 100644 test/expected/test_sql.sh_ae7b1f1684e14bf9c16e0d789257b6ef57cfb2b1.err create mode 100644 test/expected/test_sql.sh_ae7b1f1684e14bf9c16e0d789257b6ef57cfb2b1.out create mode 100644 test/expected/test_sql.sh_afe9cdc4898df5c4e112c13dfe3db6dc089c0d7c.err create mode 100644 test/expected/test_sql.sh_afe9cdc4898df5c4e112c13dfe3db6dc089c0d7c.out create mode 100644 test/expected/test_sql.sh_b085d26043f9661d70f82cb90ecb3c5245d25eac.err create mode 100644 test/expected/test_sql.sh_b085d26043f9661d70f82cb90ecb3c5245d25eac.out create mode 100644 test/expected/test_sql.sh_b2694e4fbecdd128798af25ee0d069e7e35fb499.err create mode 100644 test/expected/test_sql.sh_b2694e4fbecdd128798af25ee0d069e7e35fb499.out create mode 100644 test/expected/test_sql.sh_b5aa0561a65de7e8e22085db184c72a94b1a89a9.err create mode 100644 test/expected/test_sql.sh_b5aa0561a65de7e8e22085db184c72a94b1a89a9.out create mode 100644 test/expected/test_sql.sh_bad03a996c0750733ab99c592b9011851f521a69.err create mode 100644 test/expected/test_sql.sh_bad03a996c0750733ab99c592b9011851f521a69.out create mode 100644 test/expected/test_sql.sh_bd46ca4560f8be6307a914e39539bbac0368080a.err create mode 100644 test/expected/test_sql.sh_bd46ca4560f8be6307a914e39539bbac0368080a.out create mode 100644 test/expected/test_sql.sh_c20b0320096342c180146a5d18a6de82319d70b2.err create mode 100644 test/expected/test_sql.sh_c20b0320096342c180146a5d18a6de82319d70b2.out create mode 100644 test/expected/test_sql.sh_c353ef036c505b75996252138fbd4c8d22e8149c.err create mode 100644 test/expected/test_sql.sh_c353ef036c505b75996252138fbd4c8d22e8149c.out create mode 100644 test/expected/test_sql.sh_c5b8da04734fadf3b9eea80e0af997e38e0fb811.err create mode 100644 test/expected/test_sql.sh_c5b8da04734fadf3b9eea80e0af997e38e0fb811.out create mode 100644 test/expected/test_sql.sh_c73dec2706fc0b9a124f5da3a83f40d8d3255beb.err create mode 100644 test/expected/test_sql.sh_c73dec2706fc0b9a124f5da3a83f40d8d3255beb.out create mode 100644 test/expected/test_sql.sh_c7e1dbf4605914720b55787785abfafdf2c4178a.err create mode 100644 test/expected/test_sql.sh_c7e1dbf4605914720b55787785abfafdf2c4178a.out create mode 100644 test/expected/test_sql.sh_cc77a633a66d1778705a34e3657737547b3fb08d.err create mode 100644 test/expected/test_sql.sh_cc77a633a66d1778705a34e3657737547b3fb08d.out create mode 100644 test/expected/test_sql.sh_dd540973a0dc86320d84706845a15608196ae5be.err create mode 100644 test/expected/test_sql.sh_dd540973a0dc86320d84706845a15608196ae5be.out create mode 100644 test/expected/test_sql.sh_e70dc7d2b686c7f91c2b41b10f3920c50f3ea405.err create mode 100644 test/expected/test_sql.sh_e70dc7d2b686c7f91c2b41b10f3920c50f3ea405.out create mode 100644 test/expected/test_sql.sh_ff8a978fc0de0fed675a3cd1454cf435a6856fd5.err create mode 100644 test/expected/test_sql.sh_ff8a978fc0de0fed675a3cd1454cf435a6856fd5.out create mode 100644 test/expected/test_sql_anno.sh_028d5d5af2f3519b59d349d41cb7ecf385253b51.err create mode 100644 test/expected/test_sql_anno.sh_028d5d5af2f3519b59d349d41cb7ecf385253b51.out create mode 100644 test/expected/test_sql_anno.sh_0a37c43350ddd7a2d0d75695be32fac083ad04a4.err create mode 100644 test/expected/test_sql_anno.sh_0a37c43350ddd7a2d0d75695be32fac083ad04a4.out create mode 100644 test/expected/test_sql_anno.sh_1151e5b727f6b57070bf2c8f047f1d7e02b803a6.err create mode 100644 test/expected/test_sql_anno.sh_1151e5b727f6b57070bf2c8f047f1d7e02b803a6.out create mode 100644 test/expected/test_sql_anno.sh_1b29488b949c294479aa6054f80a35bc106b454b.err create mode 100644 test/expected/test_sql_anno.sh_1b29488b949c294479aa6054f80a35bc106b454b.out create mode 100644 test/expected/test_sql_anno.sh_331a152080d2e278b7cc0a37728eca1ded36ed72.err create mode 100644 test/expected/test_sql_anno.sh_331a152080d2e278b7cc0a37728eca1ded36ed72.out create mode 100644 test/expected/test_sql_anno.sh_4ca92f0da538c2f9d524211a021b306af0d2740d.err create mode 100644 test/expected/test_sql_anno.sh_4ca92f0da538c2f9d524211a021b306af0d2740d.out create mode 100644 test/expected/test_sql_anno.sh_73814eca259e469b57bf7469787b91e8e8569b17.err create mode 100644 test/expected/test_sql_anno.sh_73814eca259e469b57bf7469787b91e8e8569b17.out create mode 100644 test/expected/test_sql_anno.sh_74bc5fb90a0c94a1a37d30a8e9254ea02c192a75.err create mode 100644 test/expected/test_sql_anno.sh_74bc5fb90a0c94a1a37d30a8e9254ea02c192a75.out create mode 100644 test/expected/test_sql_anno.sh_7b183037479528581e1eacace7b9acae41c5aa8e.err create mode 100644 test/expected/test_sql_anno.sh_7b183037479528581e1eacace7b9acae41c5aa8e.out create mode 100644 test/expected/test_sql_anno.sh_96ebdc277ae760e1b6efae3195ff678654b04e52.err create mode 100644 test/expected/test_sql_anno.sh_96ebdc277ae760e1b6efae3195ff678654b04e52.out create mode 100644 test/expected/test_sql_anno.sh_99da5994c8c90536dbdb1b8ad7dbfb41698a5e8c.err create mode 100644 test/expected/test_sql_anno.sh_99da5994c8c90536dbdb1b8ad7dbfb41698a5e8c.out create mode 100644 test/expected/test_sql_anno.sh_b1a2ddce48beb3e4b1e3ca4b4229a7c21b83b7c4.err create mode 100644 test/expected/test_sql_anno.sh_b1a2ddce48beb3e4b1e3ca4b4229a7c21b83b7c4.out create mode 100644 test/expected/test_sql_anno.sh_be6839712d088fc7b31618ed90f8ce706c35a9c0.err create mode 100644 test/expected/test_sql_anno.sh_be6839712d088fc7b31618ed90f8ce706c35a9c0.out create mode 100644 test/expected/test_sql_anno.sh_c879ba94fdc1a099cf56bd33e5b3e9be65310036.err create mode 100644 test/expected/test_sql_anno.sh_c879ba94fdc1a099cf56bd33e5b3e9be65310036.out create mode 100644 test/expected/test_sql_anno.sh_c909647ed0e585002074f55c946f3033df1815b2.err create mode 100644 test/expected/test_sql_anno.sh_c909647ed0e585002074f55c946f3033df1815b2.out create mode 100644 test/expected/test_sql_anno.sh_ce0506ee7a12eb0f7b970522cc6a79180ecb20cc.err create mode 100644 test/expected/test_sql_anno.sh_ce0506ee7a12eb0f7b970522cc6a79180ecb20cc.out create mode 100644 test/expected/test_sql_anno.sh_f3c64191d6016767a5857fbb1bad26548586bb96.err create mode 100644 test/expected/test_sql_anno.sh_f3c64191d6016767a5857fbb1bad26548586bb96.out create mode 100644 test/expected/test_sql_coll_func.sh_077cab6e271c914daf5b221cc512853077891f35.err create mode 100644 test/expected/test_sql_coll_func.sh_077cab6e271c914daf5b221cc512853077891f35.out create mode 100644 test/expected/test_sql_coll_func.sh_0ce56741d3c34af274c8ddb4b90c4e5749d05971.err create mode 100644 test/expected/test_sql_coll_func.sh_0ce56741d3c34af274c8ddb4b90c4e5749d05971.out create mode 100644 test/expected/test_sql_coll_func.sh_180ad44fe073cc9642da642af1f442adfd98ec62.err create mode 100644 test/expected/test_sql_coll_func.sh_180ad44fe073cc9642da642af1f442adfd98ec62.out create mode 100644 test/expected/test_sql_coll_func.sh_2230714a0b2ab6aca9ddfe686734f313cef5a96b.err create mode 100644 test/expected/test_sql_coll_func.sh_2230714a0b2ab6aca9ddfe686734f313cef5a96b.out create mode 100644 test/expected/test_sql_coll_func.sh_68515cfd0a50880f6dfc8f9810c9e761493ebb12.err create mode 100644 test/expected/test_sql_coll_func.sh_68515cfd0a50880f6dfc8f9810c9e761493ebb12.out create mode 100644 test/expected/test_sql_coll_func.sh_6de2a86c53883ec4430b98edd06b0c0cdf23e741.err create mode 100644 test/expected/test_sql_coll_func.sh_6de2a86c53883ec4430b98edd06b0c0cdf23e741.out create mode 100644 test/expected/test_sql_coll_func.sh_918178c6dd9d70d0432ededfde5af5e53c094385.err create mode 100644 test/expected/test_sql_coll_func.sh_918178c6dd9d70d0432ededfde5af5e53c094385.out create mode 100644 test/expected/test_sql_coll_func.sh_c76a24a209987e4c668c87588c12b8f34294b144.err create mode 100644 test/expected/test_sql_coll_func.sh_c76a24a209987e4c668c87588c12b8f34294b144.out create mode 100644 test/expected/test_sql_coll_func.sh_cacb045d2bce6dc298c4da3d96bdc34dab2404df.err create mode 100644 test/expected/test_sql_coll_func.sh_cacb045d2bce6dc298c4da3d96bdc34dab2404df.out create mode 100644 test/expected/test_sql_coll_func.sh_cae4bc239c924bbc05a0b099b63f0e3af7560976.err create mode 100644 test/expected/test_sql_coll_func.sh_cae4bc239c924bbc05a0b099b63f0e3af7560976.out create mode 100644 test/expected/test_sql_coll_func.sh_d4e3c9f7a38458726900731d2b71c104d591ef14.err create mode 100644 test/expected/test_sql_coll_func.sh_d4e3c9f7a38458726900731d2b71c104d591ef14.out create mode 100644 test/expected/test_sql_coll_func.sh_d5c8f7ab91c3dbe46add7e08f532b17797d9975c.err create mode 100644 test/expected/test_sql_coll_func.sh_d5c8f7ab91c3dbe46add7e08f532b17797d9975c.out create mode 100644 test/expected/test_sql_coll_func.sh_eb2c424733ce978d1b6d1dcb93d6e45af7c8fa96.err create mode 100644 test/expected/test_sql_coll_func.sh_eb2c424733ce978d1b6d1dcb93d6e45af7c8fa96.out create mode 100644 test/expected/test_sql_coll_func.sh_f045e94d921bfcfbded83ee681bf11445a99ff6d.err create mode 100644 test/expected/test_sql_coll_func.sh_f045e94d921bfcfbded83ee681bf11445a99ff6d.out create mode 100644 test/expected/test_sql_fs_func.sh_109ff42de817b56a9082f605f63af71c0db8c9d7.err create mode 100644 test/expected/test_sql_fs_func.sh_109ff42de817b56a9082f605f63af71c0db8c9d7.out create mode 100644 test/expected/test_sql_fs_func.sh_17b09f79bfcac1762153ec9650fb1e545a24d8a3.err create mode 100644 test/expected/test_sql_fs_func.sh_17b09f79bfcac1762153ec9650fb1e545a24d8a3.out create mode 100644 test/expected/test_sql_fs_func.sh_18ddc138b263dd06f3fe81fec05bc4330caffef7.err create mode 100644 test/expected/test_sql_fs_func.sh_18ddc138b263dd06f3fe81fec05bc4330caffef7.out create mode 100644 test/expected/test_sql_fs_func.sh_20a76db446a0a558dcbdf41033f97d4a22ca1bfa.err create mode 100644 test/expected/test_sql_fs_func.sh_20a76db446a0a558dcbdf41033f97d4a22ca1bfa.out create mode 100644 test/expected/test_sql_fs_func.sh_2c3f66e78deb8721b1d1fe5a787e9958895401d7.err create mode 100644 test/expected/test_sql_fs_func.sh_2c3f66e78deb8721b1d1fe5a787e9958895401d7.out create mode 100644 test/expected/test_sql_fs_func.sh_3ed11101a413e47c3dfe219557b7a6df04a64253.err create mode 100644 test/expected/test_sql_fs_func.sh_3ed11101a413e47c3dfe219557b7a6df04a64253.out create mode 100644 test/expected/test_sql_fs_func.sh_469380561dccd79c7249562067107c330838eaad.err create mode 100644 test/expected/test_sql_fs_func.sh_469380561dccd79c7249562067107c330838eaad.out create mode 100644 test/expected/test_sql_fs_func.sh_54b004f301907860d360434b37fd6c81fcc12f99.err create mode 100644 test/expected/test_sql_fs_func.sh_54b004f301907860d360434b37fd6c81fcc12f99.out create mode 100644 test/expected/test_sql_fs_func.sh_73df81c6889d1f06fb3f3b6bf30c6046b3f52c8b.err create mode 100644 test/expected/test_sql_fs_func.sh_73df81c6889d1f06fb3f3b6bf30c6046b3f52c8b.out create mode 100644 test/expected/test_sql_fs_func.sh_74ca242a126316bcb82ccefd9369f9e43b7fd2e1.err create mode 100644 test/expected/test_sql_fs_func.sh_74ca242a126316bcb82ccefd9369f9e43b7fd2e1.out create mode 100644 test/expected/test_sql_fs_func.sh_7b116cb0ab7a28b866e0d2b80fe8ef0cd25f2aa3.err create mode 100644 test/expected/test_sql_fs_func.sh_7b116cb0ab7a28b866e0d2b80fe8ef0cd25f2aa3.out create mode 100644 test/expected/test_sql_fs_func.sh_7b5d7dd8d0003ab83e3e5cb0a5ce802fe9a0e3b3.err create mode 100644 test/expected/test_sql_fs_func.sh_7b5d7dd8d0003ab83e3e5cb0a5ce802fe9a0e3b3.out create mode 100644 test/expected/test_sql_fs_func.sh_917ffde411c1425e8a6addae0170900dcd553986.err create mode 100644 test/expected/test_sql_fs_func.sh_917ffde411c1425e8a6addae0170900dcd553986.out create mode 100644 test/expected/test_sql_fs_func.sh_9e2c0a90ce333365ff7354375f2c609bc27135c8.err create mode 100644 test/expected/test_sql_fs_func.sh_9e2c0a90ce333365ff7354375f2c609bc27135c8.out create mode 100644 test/expected/test_sql_fs_func.sh_a247b137e71124e496f1beab56c7fe85717c4199.err create mode 100644 test/expected/test_sql_fs_func.sh_a247b137e71124e496f1beab56c7fe85717c4199.out create mode 100644 test/expected/test_sql_fs_func.sh_b66242975fd6ecb7260cd96ac29accaf4f4af6ae.err create mode 100644 test/expected/test_sql_fs_func.sh_b66242975fd6ecb7260cd96ac29accaf4f4af6ae.out create mode 100644 test/expected/test_sql_fs_func.sh_c5d78cfbf5594cc27590277353c08a92e2497622.err create mode 100644 test/expected/test_sql_fs_func.sh_c5d78cfbf5594cc27590277353c08a92e2497622.out create mode 100644 test/expected/test_sql_fs_func.sh_cc402803bf14ee3673089c575f1af87220cb6a72.err create mode 100644 test/expected/test_sql_fs_func.sh_cc402803bf14ee3673089c575f1af87220cb6a72.out create mode 100644 test/expected/test_sql_fs_func.sh_cf307d87104e99a1858bb7c4f28ea3068340f188.err create mode 100644 test/expected/test_sql_fs_func.sh_cf307d87104e99a1858bb7c4f28ea3068340f188.out create mode 100644 test/expected/test_sql_fs_func.sh_cf670dfa1ae7ac5a074baa642068c6d26ac8e096.err create mode 100644 test/expected/test_sql_fs_func.sh_cf670dfa1ae7ac5a074baa642068c6d26ac8e096.out create mode 100644 test/expected/test_sql_fs_func.sh_d51ad77cd67a2a691838c9d95142638df1c07360.err create mode 100644 test/expected/test_sql_fs_func.sh_d51ad77cd67a2a691838c9d95142638df1c07360.out create mode 100644 test/expected/test_sql_fs_func.sh_e24cf3f35643f945392e7d7a4ca82fea98b4519e.err create mode 100644 test/expected/test_sql_fs_func.sh_e24cf3f35643f945392e7d7a4ca82fea98b4519e.out create mode 100644 test/expected/test_sql_fs_func.sh_f31f240313ddec806aa6f353ceed707dfd9aaf16.err create mode 100644 test/expected/test_sql_fs_func.sh_f31f240313ddec806aa6f353ceed707dfd9aaf16.out create mode 100644 test/expected/test_sql_indexes.sh_026dd9752b6101e0791689d3a2026f7e517e36f5.err create mode 100644 test/expected/test_sql_indexes.sh_026dd9752b6101e0791689d3a2026f7e517e36f5.out create mode 100644 test/expected/test_sql_indexes.sh_1614ebb5e2e83bab11023354dea8a0885ddf64b4.err create mode 100644 test/expected/test_sql_indexes.sh_1614ebb5e2e83bab11023354dea8a0885ddf64b4.out create mode 100644 test/expected/test_sql_indexes.sh_541a8e35f34a206e340a3880128b6ce137847872.err create mode 100644 test/expected/test_sql_indexes.sh_541a8e35f34a206e340a3880128b6ce137847872.out create mode 100644 test/expected/test_sql_indexes.sh_59a1497c13a5e09bc8f95ef02552b2835ebea6e5.err create mode 100644 test/expected/test_sql_indexes.sh_59a1497c13a5e09bc8f95ef02552b2835ebea6e5.out create mode 100644 test/expected/test_sql_indexes.sh_69fd19d56a8cd1fc9c7eb9351270eabb491f8233.err create mode 100644 test/expected/test_sql_indexes.sh_69fd19d56a8cd1fc9c7eb9351270eabb491f8233.out create mode 100644 test/expected/test_sql_indexes.sh_6f707b6e856dbaab6f95e7e89b98dc3652021f85.err create mode 100644 test/expected/test_sql_indexes.sh_6f707b6e856dbaab6f95e7e89b98dc3652021f85.out create mode 100644 test/expected/test_sql_indexes.sh_b615b6737b1e0d383c8ce4a1db56332f11dbc158.err create mode 100644 test/expected/test_sql_indexes.sh_b615b6737b1e0d383c8ce4a1db56332f11dbc158.out create mode 100644 test/expected/test_sql_indexes.sh_dab07d8de7728752ae938a174468d75e85f3ae7e.err create mode 100644 test/expected/test_sql_indexes.sh_dab07d8de7728752ae938a174468d75e85f3ae7e.out create mode 100644 test/expected/test_sql_indexes.sh_f7681c234d4f60df16c997a05163aeb058c52870.err create mode 100644 test/expected/test_sql_indexes.sh_f7681c234d4f60df16c997a05163aeb058c52870.out create mode 100644 test/expected/test_sql_json_func.sh_017d24148f3e28f719429b709f4aa5478f458443.err create mode 100644 test/expected/test_sql_json_func.sh_017d24148f3e28f719429b709f4aa5478f458443.out create mode 100644 test/expected/test_sql_json_func.sh_026077f4d573ee034467065b7e4f1878bdd4e2f2.err create mode 100644 test/expected/test_sql_json_func.sh_026077f4d573ee034467065b7e4f1878bdd4e2f2.out create mode 100644 test/expected/test_sql_json_func.sh_191436b38db80b1dd9e7e0814c31c5fa7239dc51.err create mode 100644 test/expected/test_sql_json_func.sh_191436b38db80b1dd9e7e0814c31c5fa7239dc51.out create mode 100644 test/expected/test_sql_json_func.sh_1a74914cbf12fcd5c06935b992f6355acdbcf2d8.err create mode 100644 test/expected/test_sql_json_func.sh_1a74914cbf12fcd5c06935b992f6355acdbcf2d8.out create mode 100644 test/expected/test_sql_json_func.sh_1c1a2d438d2bde95abd9a859d113c3661e650a36.err create mode 100644 test/expected/test_sql_json_func.sh_1c1a2d438d2bde95abd9a859d113c3661e650a36.out create mode 100644 test/expected/test_sql_json_func.sh_238417283b8e5db23c992f966e3f106bd178f7d0.err create mode 100644 test/expected/test_sql_json_func.sh_238417283b8e5db23c992f966e3f106bd178f7d0.out create mode 100644 test/expected/test_sql_json_func.sh_32459ba8e8bb9a1d9e63b6c67059d7f065cf4301.err create mode 100644 test/expected/test_sql_json_func.sh_32459ba8e8bb9a1d9e63b6c67059d7f065cf4301.out create mode 100644 test/expected/test_sql_json_func.sh_39c13797278d765c027d3581a0b6e0574f5c56eb.err create mode 100644 test/expected/test_sql_json_func.sh_39c13797278d765c027d3581a0b6e0574f5c56eb.out create mode 100644 test/expected/test_sql_json_func.sh_3cf4b66d40c4b1979ff14a9eccad8bd5ac48151c.err create mode 100644 test/expected/test_sql_json_func.sh_3cf4b66d40c4b1979ff14a9eccad8bd5ac48151c.out create mode 100644 test/expected/test_sql_json_func.sh_4192f378e320cb3f2c3c228b63ec65de92044704.err create mode 100644 test/expected/test_sql_json_func.sh_4192f378e320cb3f2c3c228b63ec65de92044704.out create mode 100644 test/expected/test_sql_json_func.sh_57c3aecdced547b837177ab02d3776361363e48d.err create mode 100644 test/expected/test_sql_json_func.sh_57c3aecdced547b837177ab02d3776361363e48d.out create mode 100644 test/expected/test_sql_json_func.sh_5b4a95677a1fc7d11f4b87d92165f56a60a65828.err create mode 100644 test/expected/test_sql_json_func.sh_5b4a95677a1fc7d11f4b87d92165f56a60a65828.out create mode 100644 test/expected/test_sql_json_func.sh_5f2feef079a51410e1f8661bfe92da1c3277f665.err create mode 100644 test/expected/test_sql_json_func.sh_5f2feef079a51410e1f8661bfe92da1c3277f665.out create mode 100644 test/expected/test_sql_json_func.sh_61417198a652aab93e9495b6e8cf3a634af175c6.err create mode 100644 test/expected/test_sql_json_func.sh_61417198a652aab93e9495b6e8cf3a634af175c6.out create mode 100644 test/expected/test_sql_json_func.sh_79ab816ac01c9902ddbb0f6f20392ab2f2cd6172.err create mode 100644 test/expected/test_sql_json_func.sh_79ab816ac01c9902ddbb0f6f20392ab2f2cd6172.out create mode 100644 test/expected/test_sql_json_func.sh_7c01aaf09078aaa3f23d127f9e03a317dca066de.err create mode 100644 test/expected/test_sql_json_func.sh_7c01aaf09078aaa3f23d127f9e03a317dca066de.out create mode 100644 test/expected/test_sql_json_func.sh_80c97b22084a06fd765ad22c935616c578968d07.err create mode 100644 test/expected/test_sql_json_func.sh_80c97b22084a06fd765ad22c935616c578968d07.out create mode 100644 test/expected/test_sql_json_func.sh_83d8615c9ce5dfab5e4373570c1b68b8608155f5.err create mode 100644 test/expected/test_sql_json_func.sh_83d8615c9ce5dfab5e4373570c1b68b8608155f5.out create mode 100644 test/expected/test_sql_json_func.sh_8cae9740ddfd6ba4c865fca0117b7bea3bb556e5.err create mode 100644 test/expected/test_sql_json_func.sh_8cae9740ddfd6ba4c865fca0117b7bea3bb556e5.out create mode 100644 test/expected/test_sql_json_func.sh_8e229f1b5fa3d3803e9db2f295a8d1a490e1b3db.err create mode 100644 test/expected/test_sql_json_func.sh_8e229f1b5fa3d3803e9db2f295a8d1a490e1b3db.out create mode 100644 test/expected/test_sql_json_func.sh_8e3724c90bf96dff5d8ba3cfaf4b7e2eaa9e5f66.err create mode 100644 test/expected/test_sql_json_func.sh_8e3724c90bf96dff5d8ba3cfaf4b7e2eaa9e5f66.out create mode 100644 test/expected/test_sql_json_func.sh_93ba3ba52b0dd2d5a3ba43bcb7c3638c05ecfe75.err create mode 100644 test/expected/test_sql_json_func.sh_93ba3ba52b0dd2d5a3ba43bcb7c3638c05ecfe75.out create mode 100644 test/expected/test_sql_json_func.sh_97aa53b581838f5875fe2beda8d1cb245a24f3d6.err create mode 100644 test/expected/test_sql_json_func.sh_97aa53b581838f5875fe2beda8d1cb245a24f3d6.out create mode 100644 test/expected/test_sql_json_func.sh_98a83bc899a78c04d1fdb390b2c1e403c35428c7.err create mode 100644 test/expected/test_sql_json_func.sh_98a83bc899a78c04d1fdb390b2c1e403c35428c7.out create mode 100644 test/expected/test_sql_json_func.sh_98ce02dff32d955466524bb167fa45fdf8591788.err create mode 100644 test/expected/test_sql_json_func.sh_98ce02dff32d955466524bb167fa45fdf8591788.out create mode 100644 test/expected/test_sql_json_func.sh_9ab4f51486d7cc99c584721bf0e50c223dac4f18.err create mode 100644 test/expected/test_sql_json_func.sh_9ab4f51486d7cc99c584721bf0e50c223dac4f18.out create mode 100644 test/expected/test_sql_json_func.sh_9d260ed24b28579ef1dbed25b10c42741e52b023.err create mode 100644 test/expected/test_sql_json_func.sh_9d260ed24b28579ef1dbed25b10c42741e52b023.out create mode 100644 test/expected/test_sql_json_func.sh_9fbfe3c93467666c45b643f3b8ba990a294c17ff.err create mode 100644 test/expected/test_sql_json_func.sh_9fbfe3c93467666c45b643f3b8ba990a294c17ff.out create mode 100644 test/expected/test_sql_json_func.sh_a4ffc64f89cf9917fbc918227fd3c05e54d9e8b5.err create mode 100644 test/expected/test_sql_json_func.sh_a4ffc64f89cf9917fbc918227fd3c05e54d9e8b5.out create mode 100644 test/expected/test_sql_json_func.sh_a5e179607645aefce14b9fd12ddef34107afe337.err create mode 100644 test/expected/test_sql_json_func.sh_a5e179607645aefce14b9fd12ddef34107afe337.out create mode 100644 test/expected/test_sql_json_func.sh_b2fc37822e29f7f59497a02a8968c680b545ee1d.err create mode 100644 test/expected/test_sql_json_func.sh_b2fc37822e29f7f59497a02a8968c680b545ee1d.out create mode 100644 test/expected/test_sql_json_func.sh_bbd979ed74b46ae1696ed7312a48a436bcf99ec0.err create mode 100644 test/expected/test_sql_json_func.sh_bbd979ed74b46ae1696ed7312a48a436bcf99ec0.out create mode 100644 test/expected/test_sql_json_func.sh_c1ae603d969a5b106328287523c0ddfed07146ad.err create mode 100644 test/expected/test_sql_json_func.sh_c1ae603d969a5b106328287523c0ddfed07146ad.out create mode 100644 test/expected/test_sql_json_func.sh_e0ab80f50fb008700ab6cfb90694ed014d40e44b.err create mode 100644 test/expected/test_sql_json_func.sh_e0ab80f50fb008700ab6cfb90694ed014d40e44b.out create mode 100644 test/expected/test_sql_json_func.sh_ebafb98307f307ae8d8ab6921c32929aab3a1a16.err create mode 100644 test/expected/test_sql_json_func.sh_ebafb98307f307ae8d8ab6921c32929aab3a1a16.out create mode 100644 test/expected/test_sql_json_func.sh_ee36fbea10a33ca106a211feb05d61ecf8e74634.err create mode 100644 test/expected/test_sql_json_func.sh_ee36fbea10a33ca106a211feb05d61ecf8e74634.out create mode 100644 test/expected/test_sql_json_func.sh_f1cbc70771cc75520f807261eac3a88dc2d8fe6b.err create mode 100644 test/expected/test_sql_json_func.sh_f1cbc70771cc75520f807261eac3a88dc2d8fe6b.out create mode 100644 test/expected/test_sql_json_func.sh_f34205b59e04f261897ad89f659595c743a18ca9.err create mode 100644 test/expected/test_sql_json_func.sh_f34205b59e04f261897ad89f659595c743a18ca9.out create mode 100644 test/expected/test_sql_json_func.sh_f34f5dfa938a1ac7721f924beb16bbceec127a1b.err create mode 100644 test/expected/test_sql_json_func.sh_f34f5dfa938a1ac7721f924beb16bbceec127a1b.out create mode 100644 test/expected/test_sql_regexp.sh_03257c56e85558aa0cc925b68d3af962afc25125.err create mode 100644 test/expected/test_sql_regexp.sh_03257c56e85558aa0cc925b68d3af962afc25125.out create mode 100644 test/expected/test_sql_regexp.sh_51293df041b6969ccecc60204dce3676d0fb006d.err create mode 100644 test/expected/test_sql_regexp.sh_51293df041b6969ccecc60204dce3676d0fb006d.out create mode 100644 test/expected/test_sql_regexp.sh_b841a0c09601e2419eeb99e85f7e286c889e4801.err create mode 100644 test/expected/test_sql_regexp.sh_b841a0c09601e2419eeb99e85f7e286c889e4801.out create mode 100644 test/expected/test_sql_regexp.sh_bbd1128cf61a9af8f9dc937b46217443f42e1a7a.err create mode 100644 test/expected/test_sql_regexp.sh_bbd1128cf61a9af8f9dc937b46217443f42e1a7a.out create mode 100644 test/expected/test_sql_regexp.sh_d42e1fcfe6d42394f79da84be2d37e62c4c0ea63.err create mode 100644 test/expected/test_sql_regexp.sh_d42e1fcfe6d42394f79da84be2d37e62c4c0ea63.out create mode 100644 test/expected/test_sql_regexp.sh_d61af17ff19d640ddfc879460910991825eedd05.err create mode 100644 test/expected/test_sql_regexp.sh_d61af17ff19d640ddfc879460910991825eedd05.out create mode 100644 test/expected/test_sql_regexp.sh_ed6e9f13f178def009ee58c2aeea8c3c70fdb580.err create mode 100644 test/expected/test_sql_regexp.sh_ed6e9f13f178def009ee58c2aeea8c3c70fdb580.out create mode 100644 test/expected/test_sql_search_table.sh_1a0d872ebc492fcecb2e79a0993170d5fc771a5b.err create mode 100644 test/expected/test_sql_search_table.sh_1a0d872ebc492fcecb2e79a0993170d5fc771a5b.out create mode 100644 test/expected/test_sql_search_table.sh_3f5f74863d065418bca5a000e6ad3d9344635164.err create mode 100644 test/expected/test_sql_search_table.sh_3f5f74863d065418bca5a000e6ad3d9344635164.out create mode 100644 test/expected/test_sql_search_table.sh_5aaae556ecb1661602f176215e28f661d3404032.err create mode 100644 test/expected/test_sql_search_table.sh_5aaae556ecb1661602f176215e28f661d3404032.out create mode 100644 test/expected/test_sql_search_table.sh_df0fd242f57a96d40f466493938cda0789a094fa.err create mode 100644 test/expected/test_sql_search_table.sh_df0fd242f57a96d40f466493938cda0789a094fa.out create mode 100644 test/expected/test_sql_search_table.sh_ef9373a76853f345d06234f6e0fe11b5d40da27b.err create mode 100644 test/expected/test_sql_search_table.sh_ef9373a76853f345d06234f6e0fe11b5d40da27b.out create mode 100644 test/expected/test_sql_str_func.sh_005b9365ac99596e539f47c9fe432668c209b21f.err create mode 100644 test/expected/test_sql_str_func.sh_005b9365ac99596e539f47c9fe432668c209b21f.out create mode 100644 test/expected/test_sql_str_func.sh_04712488fe50554eb36d3ced80f9a033602f3daa.err create mode 100644 test/expected/test_sql_str_func.sh_04712488fe50554eb36d3ced80f9a033602f3daa.out create mode 100644 test/expected/test_sql_str_func.sh_0947bfe7ec626eaa0409a45b10fcbb634fb12eb7.err create mode 100644 test/expected/test_sql_str_func.sh_0947bfe7ec626eaa0409a45b10fcbb634fb12eb7.out create mode 100644 test/expected/test_sql_str_func.sh_11bcc5d32eabbedb6974f160dace9ef1ef0009e9.err create mode 100644 test/expected/test_sql_str_func.sh_11bcc5d32eabbedb6974f160dace9ef1ef0009e9.out create mode 100644 test/expected/test_sql_str_func.sh_11d458fdadd00df1239a0eeaac049abb49ed212d.err create mode 100644 test/expected/test_sql_str_func.sh_11d458fdadd00df1239a0eeaac049abb49ed212d.out create mode 100644 test/expected/test_sql_str_func.sh_129e58679e72f3cc5864812026e49a7917baf3d0.err create mode 100644 test/expected/test_sql_str_func.sh_129e58679e72f3cc5864812026e49a7917baf3d0.out create mode 100644 test/expected/test_sql_str_func.sh_151a0fd71ef6837c8cbd8a67e315019b5812b079.err create mode 100644 test/expected/test_sql_str_func.sh_151a0fd71ef6837c8cbd8a67e315019b5812b079.out create mode 100644 test/expected/test_sql_str_func.sh_1e7362ac3d9690b1b2cfbd320b6129c46ecfbb8a.err create mode 100644 test/expected/test_sql_str_func.sh_1e7362ac3d9690b1b2cfbd320b6129c46ecfbb8a.out create mode 100644 test/expected/test_sql_str_func.sh_211c5428db0590795072c31cb116ef35281e02b5.err create mode 100644 test/expected/test_sql_str_func.sh_211c5428db0590795072c31cb116ef35281e02b5.out create mode 100644 test/expected/test_sql_str_func.sh_2f189f0785bb81a1298db35e9e166983b633c73f.err create mode 100644 test/expected/test_sql_str_func.sh_2f189f0785bb81a1298db35e9e166983b633c73f.out create mode 100644 test/expected/test_sql_str_func.sh_30f65162174b886130b94a5dd1f094e7f09debed.err create mode 100644 test/expected/test_sql_str_func.sh_30f65162174b886130b94a5dd1f094e7f09debed.out create mode 100644 test/expected/test_sql_str_func.sh_352434d199f7b493668c9f2774472eb69ef0d9f0.err create mode 100644 test/expected/test_sql_str_func.sh_352434d199f7b493668c9f2774472eb69ef0d9f0.out create mode 100644 test/expected/test_sql_str_func.sh_36fc9005464f1106f969559e640d9fa36d5fadad.err create mode 100644 test/expected/test_sql_str_func.sh_36fc9005464f1106f969559e640d9fa36d5fadad.out create mode 100644 test/expected/test_sql_str_func.sh_3855d2cc0ab29171cae8e722f130adec25eae36e.err create mode 100644 test/expected/test_sql_str_func.sh_3855d2cc0ab29171cae8e722f130adec25eae36e.out create mode 100644 test/expected/test_sql_str_func.sh_3de72fe5c1751dd212a1cd45cf2caa7f3b52bced.err create mode 100644 test/expected/test_sql_str_func.sh_3de72fe5c1751dd212a1cd45cf2caa7f3b52bced.out create mode 100644 test/expected/test_sql_str_func.sh_4b402274da152135c6c99456b693e1ecabca0256.err create mode 100644 test/expected/test_sql_str_func.sh_4b402274da152135c6c99456b693e1ecabca0256.out create mode 100644 test/expected/test_sql_str_func.sh_51055e40d709332ee772ba5719039314bbf5e411.err create mode 100644 test/expected/test_sql_str_func.sh_51055e40d709332ee772ba5719039314bbf5e411.out create mode 100644 test/expected/test_sql_str_func.sh_51766b600fd158a9e0677f6b0fa31b83537b2e5b.err create mode 100644 test/expected/test_sql_str_func.sh_51766b600fd158a9e0677f6b0fa31b83537b2e5b.out create mode 100644 test/expected/test_sql_str_func.sh_5203db1a4a81e43a693f339fd26e1ed635da9d5a.err create mode 100644 test/expected/test_sql_str_func.sh_5203db1a4a81e43a693f339fd26e1ed635da9d5a.out create mode 100644 test/expected/test_sql_str_func.sh_5abe3717393fba14ec510a37b4b94fedc67aae8e.err create mode 100644 test/expected/test_sql_str_func.sh_5abe3717393fba14ec510a37b4b94fedc67aae8e.out create mode 100644 test/expected/test_sql_str_func.sh_5e436fbd4efb140600999c5208886a5a57b8a30e.err create mode 100644 test/expected/test_sql_str_func.sh_5e436fbd4efb140600999c5208886a5a57b8a30e.out create mode 100644 test/expected/test_sql_str_func.sh_5f9979fa5ce7b76efe714bb27ffbe9f5927ae941.err create mode 100644 test/expected/test_sql_str_func.sh_5f9979fa5ce7b76efe714bb27ffbe9f5927ae941.out create mode 100644 test/expected/test_sql_str_func.sh_60a005a9f0d44ad022b5554415319933d5743c51.err create mode 100644 test/expected/test_sql_str_func.sh_60a005a9f0d44ad022b5554415319933d5743c51.out create mode 100644 test/expected/test_sql_str_func.sh_660288b48d9b30244621d873944938f7ef043976.err create mode 100644 test/expected/test_sql_str_func.sh_660288b48d9b30244621d873944938f7ef043976.out create mode 100644 test/expected/test_sql_str_func.sh_6607c0dd8baff16930eb3e0daf6354af5b50052b.err create mode 100644 test/expected/test_sql_str_func.sh_6607c0dd8baff16930eb3e0daf6354af5b50052b.out create mode 100644 test/expected/test_sql_str_func.sh_69f5d49e62da48e188bd9d6af4bd3adeb21eb7d1.err create mode 100644 test/expected/test_sql_str_func.sh_69f5d49e62da48e188bd9d6af4bd3adeb21eb7d1.out create mode 100644 test/expected/test_sql_str_func.sh_6ff984d8ed3e5099376d19f0dd20d5fd1ed42494.err create mode 100644 test/expected/test_sql_str_func.sh_6ff984d8ed3e5099376d19f0dd20d5fd1ed42494.out create mode 100644 test/expected/test_sql_str_func.sh_71f37db33504b2c08a7a3323c482556f53d88100.err create mode 100644 test/expected/test_sql_str_func.sh_71f37db33504b2c08a7a3323c482556f53d88100.out create mode 100644 test/expected/test_sql_str_func.sh_77fc174faeec1eda687a9373dbdbdd1aaef56e20.err create mode 100644 test/expected/test_sql_str_func.sh_77fc174faeec1eda687a9373dbdbdd1aaef56e20.out create mode 100644 test/expected/test_sql_str_func.sh_790da4aab5af901feeff5426790876eb91b967cb.err create mode 100644 test/expected/test_sql_str_func.sh_790da4aab5af901feeff5426790876eb91b967cb.out create mode 100644 test/expected/test_sql_str_func.sh_7a544cd702579c1fab35870428788ad763cf1143.err create mode 100644 test/expected/test_sql_str_func.sh_7a544cd702579c1fab35870428788ad763cf1143.out create mode 100644 test/expected/test_sql_str_func.sh_7b6e7c26e8a80459fef55d56156d6ff93c00bd49.err create mode 100644 test/expected/test_sql_str_func.sh_7b6e7c26e8a80459fef55d56156d6ff93c00bd49.out create mode 100644 test/expected/test_sql_str_func.sh_7c1e7604ac050e7047201638dca0a6b0fcfd8bdf.err create mode 100644 test/expected/test_sql_str_func.sh_7c1e7604ac050e7047201638dca0a6b0fcfd8bdf.out create mode 100644 test/expected/test_sql_str_func.sh_7f751009d0db15fc97f9113c5c84db05ff1de9c3.err create mode 100644 test/expected/test_sql_str_func.sh_7f751009d0db15fc97f9113c5c84db05ff1de9c3.out create mode 100644 test/expected/test_sql_str_func.sh_805ca5e97fbf1ed56f2e920befd963255ba190b6.err create mode 100644 test/expected/test_sql_str_func.sh_805ca5e97fbf1ed56f2e920befd963255ba190b6.out create mode 100644 test/expected/test_sql_str_func.sh_80c1fb9affbfac609ebf1cc5556aafb1ecd223c1.err create mode 100644 test/expected/test_sql_str_func.sh_80c1fb9affbfac609ebf1cc5556aafb1ecd223c1.out create mode 100644 test/expected/test_sql_str_func.sh_836e3f721a0f945ad27e7aa241121ba739aab618.err create mode 100644 test/expected/test_sql_str_func.sh_836e3f721a0f945ad27e7aa241121ba739aab618.out create mode 100644 test/expected/test_sql_str_func.sh_838e9bc7873b2b238157ba0358e0dfd6a01d837d.err create mode 100644 test/expected/test_sql_str_func.sh_838e9bc7873b2b238157ba0358e0dfd6a01d837d.out create mode 100644 test/expected/test_sql_str_func.sh_84e77dedec887c5e2433dbc5b130000cd88963bd.err create mode 100644 test/expected/test_sql_str_func.sh_84e77dedec887c5e2433dbc5b130000cd88963bd.out create mode 100644 test/expected/test_sql_str_func.sh_887afe94962d958aca2e03f7873d58ca93e190b5.err create mode 100644 test/expected/test_sql_str_func.sh_887afe94962d958aca2e03f7873d58ca93e190b5.out create mode 100644 test/expected/test_sql_str_func.sh_8c9ef83431ea75050fd16824075bf72056cf5f53.err create mode 100644 test/expected/test_sql_str_func.sh_8c9ef83431ea75050fd16824075bf72056cf5f53.out create mode 100644 test/expected/test_sql_str_func.sh_8cef54f0617960320b5d3615068eb27333dcf6a3.err create mode 100644 test/expected/test_sql_str_func.sh_8cef54f0617960320b5d3615068eb27333dcf6a3.out create mode 100644 test/expected/test_sql_str_func.sh_8f4f0ed74c4dc6b821e02a44552b694614cd9353.err create mode 100644 test/expected/test_sql_str_func.sh_8f4f0ed74c4dc6b821e02a44552b694614cd9353.out create mode 100644 test/expected/test_sql_str_func.sh_949ffd5b2ef9fbcbe17f2e61ef7750f7038f6fd6.err create mode 100644 test/expected/test_sql_str_func.sh_949ffd5b2ef9fbcbe17f2e61ef7750f7038f6fd6.out create mode 100644 test/expected/test_sql_str_func.sh_a4d84a0082a7df34c95c2e6e070bbf6effaa5594.err create mode 100644 test/expected/test_sql_str_func.sh_a4d84a0082a7df34c95c2e6e070bbf6effaa5594.out create mode 100644 test/expected/test_sql_str_func.sh_a515ba81cc3655c602da28cd0fa1a186d5e9a6e1.err create mode 100644 test/expected/test_sql_str_func.sh_a515ba81cc3655c602da28cd0fa1a186d5e9a6e1.out create mode 100644 test/expected/test_sql_str_func.sh_a65d2fb2f841578619528ca10168ca4d650218e9.err create mode 100644 test/expected/test_sql_str_func.sh_a65d2fb2f841578619528ca10168ca4d650218e9.out create mode 100644 test/expected/test_sql_str_func.sh_ac7ecdda0fcc4279a4694291edaa2f1411f5262e.err create mode 100644 test/expected/test_sql_str_func.sh_ac7ecdda0fcc4279a4694291edaa2f1411f5262e.out create mode 100644 test/expected/test_sql_str_func.sh_b088735cf46f23ca3d5fb3da41f07a6a3b1cba35.err create mode 100644 test/expected/test_sql_str_func.sh_b088735cf46f23ca3d5fb3da41f07a6a3b1cba35.out create mode 100644 test/expected/test_sql_str_func.sh_b0e5bf23bbbc0defa8bb26817782c9d46a778ad8.err create mode 100644 test/expected/test_sql_str_func.sh_b0e5bf23bbbc0defa8bb26817782c9d46a778ad8.out create mode 100644 test/expected/test_sql_str_func.sh_b2aafbcaa7befe426d3f9df71c24f16fdc9d2856.err create mode 100644 test/expected/test_sql_str_func.sh_b2aafbcaa7befe426d3f9df71c24f16fdc9d2856.out create mode 100644 test/expected/test_sql_str_func.sh_b81b27abfafbd357d41c407428d41ae0f4bb75e2.err create mode 100644 test/expected/test_sql_str_func.sh_b81b27abfafbd357d41c407428d41ae0f4bb75e2.out create mode 100644 test/expected/test_sql_str_func.sh_bac7f6531a2adf70cd1871fb13eab26dff133b7c.err create mode 100644 test/expected/test_sql_str_func.sh_bac7f6531a2adf70cd1871fb13eab26dff133b7c.out create mode 100644 test/expected/test_sql_str_func.sh_bfb7088916412360f77683009058b0747784630a.err create mode 100644 test/expected/test_sql_str_func.sh_bfb7088916412360f77683009058b0747784630a.out create mode 100644 test/expected/test_sql_str_func.sh_bfe8b09e23389af0ef14359b66d68228d0285185.err create mode 100644 test/expected/test_sql_str_func.sh_bfe8b09e23389af0ef14359b66d68228d0285185.out create mode 100644 test/expected/test_sql_str_func.sh_c26269b10b9b9e8485aa97c2be2afb2cc3ee910d.err create mode 100644 test/expected/test_sql_str_func.sh_c26269b10b9b9e8485aa97c2be2afb2cc3ee910d.out create mode 100644 test/expected/test_sql_str_func.sh_c9e2f41431bef879364dc37a472ab01f64d89f89.err create mode 100644 test/expected/test_sql_str_func.sh_c9e2f41431bef879364dc37a472ab01f64d89f89.out create mode 100644 test/expected/test_sql_str_func.sh_cc53348c585ee71a7456157ad6b125689813bafe.err create mode 100644 test/expected/test_sql_str_func.sh_cc53348c585ee71a7456157ad6b125689813bafe.out create mode 100644 test/expected/test_sql_str_func.sh_ce9db1dbc2e5fee87247135d17787ff3af014d77.err create mode 100644 test/expected/test_sql_str_func.sh_ce9db1dbc2e5fee87247135d17787ff3af014d77.out create mode 100644 test/expected/test_sql_str_func.sh_d3367527118052081a541a660b091f6f495b1c0d.err create mode 100644 test/expected/test_sql_str_func.sh_d3367527118052081a541a660b091f6f495b1c0d.out create mode 100644 test/expected/test_sql_str_func.sh_d4bc869850f5b7e53353fc2506fea0c8e96f29c5.err create mode 100644 test/expected/test_sql_str_func.sh_d4bc869850f5b7e53353fc2506fea0c8e96f29c5.out create mode 100644 test/expected/test_sql_str_func.sh_d4e805ff08d4ccf62865dbf8db8d526f7ce02f37.err create mode 100644 test/expected/test_sql_str_func.sh_d4e805ff08d4ccf62865dbf8db8d526f7ce02f37.out create mode 100644 test/expected/test_sql_str_func.sh_d54a759f5683a22ad289129b2096b80652b1cc0c.err create mode 100644 test/expected/test_sql_str_func.sh_d54a759f5683a22ad289129b2096b80652b1cc0c.out create mode 100644 test/expected/test_sql_str_func.sh_d8d4cde8bbc98175069be579ff5634de43880b8c.err create mode 100644 test/expected/test_sql_str_func.sh_d8d4cde8bbc98175069be579ff5634de43880b8c.out create mode 100644 test/expected/test_sql_str_func.sh_e68167bf5edc7a7b1defd06bdfb694ffa8b00df2.err create mode 100644 test/expected/test_sql_str_func.sh_e68167bf5edc7a7b1defd06bdfb694ffa8b00df2.out create mode 100644 test/expected/test_sql_str_func.sh_ec939e82da809965c61f1c00f68d7afaa4a88382.err create mode 100644 test/expected/test_sql_str_func.sh_ec939e82da809965c61f1c00f68d7afaa4a88382.out create mode 100644 test/expected/test_sql_time_func.sh_028e99419eb1ac80b03b36148ef1d4ae1c38c44c.err create mode 100644 test/expected/test_sql_time_func.sh_028e99419eb1ac80b03b36148ef1d4ae1c38c44c.out create mode 100644 test/expected/test_sql_time_func.sh_123c85ff1178743f5cb78efeaf98b637bcbe55ff.err create mode 100644 test/expected/test_sql_time_func.sh_123c85ff1178743f5cb78efeaf98b637bcbe55ff.out create mode 100644 test/expected/test_sql_time_func.sh_14737ee9597b7d22519d23fbe34c0eb7d6c09ff2.err create mode 100644 test/expected/test_sql_time_func.sh_14737ee9597b7d22519d23fbe34c0eb7d6c09ff2.out create mode 100644 test/expected/test_sql_time_func.sh_1fbeb1ba69a95284eb1d4d052f5068ede7968704.err create mode 100644 test/expected/test_sql_time_func.sh_1fbeb1ba69a95284eb1d4d052f5068ede7968704.out create mode 100644 test/expected/test_sql_time_func.sh_20477acc218c96f1385dc97e4d28c80a05c93709.err create mode 100644 test/expected/test_sql_time_func.sh_20477acc218c96f1385dc97e4d28c80a05c93709.out create mode 100644 test/expected/test_sql_time_func.sh_243454526f6b5e19485db771b4932ddffd6f83a4.err create mode 100644 test/expected/test_sql_time_func.sh_243454526f6b5e19485db771b4932ddffd6f83a4.out create mode 100644 test/expected/test_sql_time_func.sh_28638a132caae65fd89a68459d1b4af0000b8aef.err create mode 100644 test/expected/test_sql_time_func.sh_28638a132caae65fd89a68459d1b4af0000b8aef.out create mode 100644 test/expected/test_sql_time_func.sh_3b551281347a8144c84f00ade2664db9ac4aacab.err create mode 100644 test/expected/test_sql_time_func.sh_3b551281347a8144c84f00ade2664db9ac4aacab.out create mode 100644 test/expected/test_sql_time_func.sh_4035ee76938269e9247f9a696927a9ac18cce80a.err create mode 100644 test/expected/test_sql_time_func.sh_4035ee76938269e9247f9a696927a9ac18cce80a.out create mode 100644 test/expected/test_sql_time_func.sh_42f0fc1a154b0d79b4f6e846f283426be498040f.err create mode 100644 test/expected/test_sql_time_func.sh_42f0fc1a154b0d79b4f6e846f283426be498040f.out create mode 100644 test/expected/test_sql_time_func.sh_4b96fe71bc2d18955e3625b765a6095ab1f7a75d.err create mode 100644 test/expected/test_sql_time_func.sh_4b96fe71bc2d18955e3625b765a6095ab1f7a75d.out create mode 100644 test/expected/test_sql_time_func.sh_53b76b094e47691b5bca106142ee470e82e8e420.err create mode 100644 test/expected/test_sql_time_func.sh_53b76b094e47691b5bca106142ee470e82e8e420.out create mode 100644 test/expected/test_sql_time_func.sh_6288a9e690d381602b2be5665cc1cd3552733bc2.err create mode 100644 test/expected/test_sql_time_func.sh_6288a9e690d381602b2be5665cc1cd3552733bc2.out create mode 100644 test/expected/test_sql_time_func.sh_652bbd00b5159e22d94970ab1e882997d14b5777.err create mode 100644 test/expected/test_sql_time_func.sh_652bbd00b5159e22d94970ab1e882997d14b5777.out create mode 100644 test/expected/test_sql_time_func.sh_6832a58259168622af8b3370b0c89534f98f3f9f.err create mode 100644 test/expected/test_sql_time_func.sh_6832a58259168622af8b3370b0c89534f98f3f9f.out create mode 100644 test/expected/test_sql_time_func.sh_72862ec9c8f261a8507d237eb673c7ddfaafd898.err create mode 100644 test/expected/test_sql_time_func.sh_72862ec9c8f261a8507d237eb673c7ddfaafd898.out create mode 100644 test/expected/test_sql_time_func.sh_7797302b63d73234c9ec9f0405c7c0a748daf8e9.err create mode 100644 test/expected/test_sql_time_func.sh_7797302b63d73234c9ec9f0405c7c0a748daf8e9.out create mode 100644 test/expected/test_sql_time_func.sh_9569ab40cb2e51c60f818a6c2729c60d86565e7e.err create mode 100644 test/expected/test_sql_time_func.sh_9569ab40cb2e51c60f818a6c2729c60d86565e7e.out create mode 100644 test/expected/test_sql_time_func.sh_9e649c4bc10f4d178519983358f7092e9c5dfe71.err create mode 100644 test/expected/test_sql_time_func.sh_9e649c4bc10f4d178519983358f7092e9c5dfe71.out create mode 100644 test/expected/test_sql_time_func.sh_b0257ced663fc444801a5e6cba89c3053acca11e.err create mode 100644 test/expected/test_sql_time_func.sh_b0257ced663fc444801a5e6cba89c3053acca11e.out create mode 100644 test/expected/test_sql_time_func.sh_b5f9ec3ea8b4551fd40017398d74c524fb54ebc9.err create mode 100644 test/expected/test_sql_time_func.sh_b5f9ec3ea8b4551fd40017398d74c524fb54ebc9.out create mode 100644 test/expected/test_sql_time_func.sh_dbe786c096d5a7a5e1d05311b929f1427d8bac79.err create mode 100644 test/expected/test_sql_time_func.sh_dbe786c096d5a7a5e1d05311b929f1427d8bac79.out create mode 100644 test/expected/test_sql_time_func.sh_f3b1ea49779117bf45f85ad5615fdc5e89193db6.err create mode 100644 test/expected/test_sql_time_func.sh_f3b1ea49779117bf45f85ad5615fdc5e89193db6.out create mode 100644 test/expected/test_sql_views_vtab.sh_28e23f4e98b1acd6478e39844fd9306b444550c3.err create mode 100644 test/expected/test_sql_views_vtab.sh_28e23f4e98b1acd6478e39844fd9306b444550c3.out create mode 100644 test/expected/test_sql_views_vtab.sh_32acc1a8bb5028636fdbf08f077f9a835ab51bec.err create mode 100644 test/expected/test_sql_views_vtab.sh_32acc1a8bb5028636fdbf08f077f9a835ab51bec.out create mode 100644 test/expected/test_sql_views_vtab.sh_485a6ac7c69bd4b5d34d3399a9c17f6a2dc89ad3.err create mode 100644 test/expected/test_sql_views_vtab.sh_485a6ac7c69bd4b5d34d3399a9c17f6a2dc89ad3.out create mode 100644 test/expected/test_sql_views_vtab.sh_62d15cb9d5a9259f198aa01ca8ed200d6da38d68.err create mode 100644 test/expected/test_sql_views_vtab.sh_62d15cb9d5a9259f198aa01ca8ed200d6da38d68.out create mode 100644 test/expected/test_sql_views_vtab.sh_662b5f9b17aa69a8e3aa9a18acb30d9acf6e2837.err create mode 100644 test/expected/test_sql_views_vtab.sh_662b5f9b17aa69a8e3aa9a18acb30d9acf6e2837.out create mode 100644 test/expected/test_sql_views_vtab.sh_6ffd89498b9a7758ded6717148fc2ce77a12621b.err create mode 100644 test/expected/test_sql_views_vtab.sh_6ffd89498b9a7758ded6717148fc2ce77a12621b.out create mode 100644 test/expected/test_sql_views_vtab.sh_764ea85863d4f0ea3b7cb40850ac7c8fde682d57.err create mode 100644 test/expected/test_sql_views_vtab.sh_764ea85863d4f0ea3b7cb40850ac7c8fde682d57.out create mode 100644 test/expected/test_sql_views_vtab.sh_81dc3eb51ec4dc3066a2365524001242c423a9cf.err create mode 100644 test/expected/test_sql_views_vtab.sh_81dc3eb51ec4dc3066a2365524001242c423a9cf.out create mode 100644 test/expected/test_sql_views_vtab.sh_81ffd4ed3f62228494a966512791202cea7e3b57.err create mode 100644 test/expected/test_sql_views_vtab.sh_81ffd4ed3f62228494a966512791202cea7e3b57.out create mode 100644 test/expected/test_sql_views_vtab.sh_87f53d441e22c1d27c27eaa6003c83da1207c063.err create mode 100644 test/expected/test_sql_views_vtab.sh_87f53d441e22c1d27c27eaa6003c83da1207c063.out create mode 100644 test/expected/test_sql_views_vtab.sh_977cdf5d396522194d6b9e945169ff8073b4296b.err create mode 100644 test/expected/test_sql_views_vtab.sh_977cdf5d396522194d6b9e945169ff8073b4296b.out create mode 100644 test/expected/test_sql_views_vtab.sh_9a5be90921256e90428c77753eca5ea0d31bd910.err create mode 100644 test/expected/test_sql_views_vtab.sh_9a5be90921256e90428c77753eca5ea0d31bd910.out create mode 100644 test/expected/test_sql_views_vtab.sh_a2c0f0e51b3f85ea2a05ecdcacaad962b4fe5d4f.err create mode 100644 test/expected/test_sql_views_vtab.sh_a2c0f0e51b3f85ea2a05ecdcacaad962b4fe5d4f.out create mode 100644 test/expected/test_sql_views_vtab.sh_ac1f6e9a88608ef8939f9c2f7061a25a86742d46.err create mode 100644 test/expected/test_sql_views_vtab.sh_ac1f6e9a88608ef8939f9c2f7061a25a86742d46.out create mode 100644 test/expected/test_sql_views_vtab.sh_ade121f29bedea0d1a54452cc994b2302ad9dabb.err create mode 100644 test/expected/test_sql_views_vtab.sh_ade121f29bedea0d1a54452cc994b2302ad9dabb.out create mode 100644 test/expected/test_sql_views_vtab.sh_c851bdf3ba2f56fac5a216457b2d11a109e77f03.err create mode 100644 test/expected/test_sql_views_vtab.sh_c851bdf3ba2f56fac5a216457b2d11a109e77f03.out create mode 100644 test/expected/test_sql_views_vtab.sh_d99d884ba6668b66e3ca9ea4ed2d0e236497c35d.err create mode 100644 test/expected/test_sql_views_vtab.sh_d99d884ba6668b66e3ca9ea4ed2d0e236497c35d.out create mode 100644 test/expected/test_sql_views_vtab.sh_e036fabdc6c15f65a374b95c9922212670d494ee.err create mode 100644 test/expected/test_sql_views_vtab.sh_e036fabdc6c15f65a374b95c9922212670d494ee.out create mode 100644 test/expected/test_sql_views_vtab.sh_ec4623bd63ff353f50db44da1231e46a1a4f1824.err create mode 100644 test/expected/test_sql_views_vtab.sh_ec4623bd63ff353f50db44da1231e46a1a4f1824.out create mode 100644 test/expected/test_sql_views_vtab.sh_f7476c76ea51cf479a6a79b037e0cb59871b629c.err create mode 100644 test/expected/test_sql_views_vtab.sh_f7476c76ea51cf479a6a79b037e0cb59871b629c.out create mode 100644 test/expected/test_sql_views_vtab.sh_f8340cb4c62aabd839ea09235b6ebe41b2bb48f4.err create mode 100644 test/expected/test_sql_views_vtab.sh_f8340cb4c62aabd839ea09235b6ebe41b2bb48f4.out create mode 100644 test/expected/test_sql_xml_func.sh_46dfa23e2effabf3fa150c4b871fd8d22b1c834d.err create mode 100644 test/expected/test_sql_xml_func.sh_46dfa23e2effabf3fa150c4b871fd8d22b1c834d.out create mode 100644 test/expected/test_sql_xml_func.sh_4effabf11b59580e5f0727199eb74fba049c0cda.err create mode 100644 test/expected/test_sql_xml_func.sh_4effabf11b59580e5f0727199eb74fba049c0cda.out create mode 100644 test/expected/test_sql_xml_func.sh_8912b59d5b515ab1373a3d9bc635ebabacd01dfd.err create mode 100644 test/expected/test_sql_xml_func.sh_8912b59d5b515ab1373a3d9bc635ebabacd01dfd.out create mode 100644 test/expected/test_sql_xml_func.sh_b036c73528a446cba46625767517cdac868aba72.err create mode 100644 test/expected/test_sql_xml_func.sh_b036c73528a446cba46625767517cdac868aba72.out create mode 100644 test/expected/test_sql_xml_func.sh_fefeb387ae14d4171225ea06cbbff3ec43990cf0.err create mode 100644 test/expected/test_sql_xml_func.sh_fefeb387ae14d4171225ea06cbbff3ec43990cf0.out create mode 100644 test/expected/test_sql_yaml_func.sh_41c6abde708a69e74f5b7fde865d88fa75f91e0a.err create mode 100644 test/expected/test_sql_yaml_func.sh_41c6abde708a69e74f5b7fde865d88fa75f91e0a.out create mode 100644 test/expected/test_sql_yaml_func.sh_dc189d02e8979b7ed245d5d750f68b9965984699.err create mode 100644 test/expected/test_sql_yaml_func.sh_dc189d02e8979b7ed245d5d750f68b9965984699.out create mode 100644 test/expected/test_tailer.sh_12f539e535df04364316699f9edeac461aa9f9de.err create mode 100644 test/expected/test_tailer.sh_12f539e535df04364316699f9edeac461aa9f9de.out create mode 100644 test/expected/test_text_file.sh_2e69c22dcfa37b5c3e8490a6026eacb7ca953998.err create mode 100644 test/expected/test_text_file.sh_2e69c22dcfa37b5c3e8490a6026eacb7ca953998.out create mode 100644 test/expected/test_text_file.sh_5b51b55dff7332c5bee2c9b797c401c5614d574a.err create mode 100644 test/expected/test_text_file.sh_5b51b55dff7332c5bee2c9b797c401c5614d574a.out create mode 100644 test/expected/test_text_file.sh_6a24078983cf1b7a80b6fb65d5186cd125498136.err create mode 100644 test/expected/test_text_file.sh_6a24078983cf1b7a80b6fb65d5186cd125498136.out create mode 100644 test/expected/test_text_file.sh_801414c6bb6d3f9225973eafa3c6dfa49cd2081d.err create mode 100644 test/expected/test_text_file.sh_801414c6bb6d3f9225973eafa3c6dfa49cd2081d.out create mode 100644 test/expected/test_text_file.sh_87943c6be50d701a03e901f16493314c839af1ab.err create mode 100644 test/expected/test_text_file.sh_87943c6be50d701a03e901f16493314c839af1ab.out create mode 100644 test/expected/test_text_file.sh_8b2cd055e6a1db2ed9b2af2a917f8556395fa653.err create mode 100644 test/expected/test_text_file.sh_8b2cd055e6a1db2ed9b2af2a917f8556395fa653.out create mode 100644 test/expected/test_text_file.sh_ac486314c4e02e480d829ea2f077b86c49fedcec.err create mode 100644 test/expected/test_text_file.sh_ac486314c4e02e480d829ea2f077b86c49fedcec.out create mode 100644 test/expected/test_text_file.sh_ac872aadda29b9a824361a2c711d62ec1c75d40f.err create mode 100644 test/expected/test_text_file.sh_ac872aadda29b9a824361a2c711d62ec1c75d40f.out create mode 100644 test/expected/test_text_file.sh_c21295f131c221861568bda5014b76ef99bdd11f.err create mode 100644 test/expected/test_text_file.sh_c21295f131c221861568bda5014b76ef99bdd11f.out create mode 100644 test/expected/test_text_file.sh_c2a346ca1da2da4346f1d310212e166767993ce9.err create mode 100644 test/expected/test_text_file.sh_c2a346ca1da2da4346f1d310212e166767993ce9.out create mode 100644 test/expected/test_text_file.sh_e088ea61a5382458cc48a2607e2639e52b0be1da.err create mode 100644 test/expected/test_text_file.sh_e088ea61a5382458cc48a2607e2639e52b0be1da.out create mode 100644 test/expected_help.txt create mode 100644 test/file_for_dot_read.sql create mode 100644 test/formats/collision/format.json create mode 100644 test/formats/customlevel/format.json create mode 100644 test/formats/jsontest-subsec/format.json create mode 100644 test/formats/jsontest/format.json create mode 100644 test/formats/jsontest/lnav-logstash.json create mode 100644 test/formats/jsontest/rewrite-user.lnav create mode 100644 test/formats/jsontest2/format.json create mode 100644 test/formats/jsontest3/format.json create mode 100644 test/formats/nestedjson/format.json create mode 100644 test/formats/scripts/multiline-echo.lnav create mode 100644 test/formats/scripts/nested-redirecting.lnav create mode 100644 test/formats/scripts/redirecting.lnav create mode 100644 test/formats/sqldir/init.sql create mode 100644 test/formats/timestamp/format.json create mode 100644 test/formats/xmlmsg/format.json create mode 100644 test/gp_test.cc create mode 100644 test/lb_test.cc create mode 100644 test/listview_output.0 create mode 100644 test/listview_output.1 create mode 100644 test/listview_output.2 create mode 100644 test/listview_output.3 create mode 100644 test/listview_output.4 create mode 100644 test/listview_output.5 create mode 100644 test/listview_output.6 create mode 100644 test/lnav_doctests.cc create mode 100644 test/log-samples/sample-057d6c669632ef9d07b6adec605f6bdeae19af27.txt create mode 100644 test/log-samples/sample-06aaa6f48a801f592558575d886864d6c3ab9ed4.txt create mode 100644 test/log-samples/sample-1aeb47c0a97d19bb7418f0172480e05e49c6e53e.txt create mode 100644 test/log-samples/sample-27353a72ba4025448f261dcfa6ea16e474187795.txt create mode 100644 test/log-samples/sample-3856ad0f551a04fde41a020158d6b33ef97c870a.txt create mode 100644 test/log-samples/sample-45364b3fd51af92a4ad8a309b5f4fd88.txt create mode 100644 test/log-samples/sample-500c9e492e04f5f58862c8086ca301de0dd976ce.txt create mode 100644 test/log-samples/sample-55ac97afae4b0650ccb62e2dbc8d89bb.txt create mode 100644 test/log-samples/sample-6049d4309f26eefb1a3406d937a9ba8a0df592a7.txt create mode 100644 test/log-samples/sample-62315d884afdc4155b35f905415c74bfcfd39fc2.txt create mode 100644 test/log-samples/sample-70c906b3c1a1cf03f15bde92ee78edfa6f9b7960.txt create mode 100644 test/log-samples/sample-9cf7fbb3546c676c686fac0ed096d026f46c875f.txt create mode 100644 test/log-samples/sample-a74570613c082c7fe283672031e18e54e8887ffb.txt create mode 100644 test/log-samples/sample-aca2878a2e50779c6697c0747ab1f60e4b368dcb.txt create mode 100644 test/log-samples/sample-ad31f12d2adabd07e3ddda3ad5b0dbf6b49c4c99.txt create mode 100644 test/log-samples/sample-bc6f6cf689fa5455616b4d9fbe121a48d3c9de59.txt create mode 100644 test/log-samples/sample-c15acd32844669d23d0cbc88ec548129ed2c592e.txt create mode 100644 test/log-samples/sample-c23f22c1b932b904203e018f78dead95fb89b15d.txt create mode 100644 test/log-samples/sample-d0d6b3fc6766caac5ac3fac4a3754ceaab785eb8.txt create mode 100644 test/log-samples/sample-d4a0aedc8350f64b22403eeef4eca71fbf749d2b.txt create mode 100644 test/log-samples/sample-d714b5e8cd354321f376ed1c0a70ec9a2f58076d.txt create mode 100644 test/log-samples/sample-dd7d406352ec6a11d966b6f015a9482b060f2b29.txt create mode 100644 test/log-samples/sample-e779d1771e34f5203ae73e85802e78002be63db6.txt create mode 100644 test/log-samples/sample-f5afbee90a8c054061c4e9ffe673293cce7761de.txt create mode 100644 test/log-samples/sample-fc8923633e57bacd641d80dde3ff878212230552.txt create mode 100644 test/log.clog create mode 100644 test/logfile_access_log.0 create mode 100644 test/logfile_access_log.1 create mode 100644 test/logfile_ansi.0 create mode 100644 test/logfile_ansi.1 create mode 100644 test/logfile_bad_access_log.0 create mode 100644 test/logfile_bad_syslog.0 create mode 100644 test/logfile_block.1 create mode 100644 test/logfile_block.2 create mode 100644 test/logfile_blued.0 create mode 100644 test/logfile_bro_conn.log.0 create mode 100644 test/logfile_bro_http.log.0 create mode 100644 test/logfile_crlf.0 create mode 100644 test/logfile_cxx.0 create mode 100644 test/logfile_empty.0 create mode 100644 test/logfile_epoch.0 create mode 100644 test/logfile_epoch.1 create mode 100644 test/logfile_filter.0 create mode 100644 test/logfile_for_join.0 create mode 100644 test/logfile_generic.0 create mode 100644 test/logfile_generic.1 create mode 100644 test/logfile_generic.2 create mode 100644 test/logfile_generic.3 create mode 100644 test/logfile_generic_with_header.0 create mode 100644 test/logfile_glog.0 create mode 100644 test/logfile_haproxy.0 create mode 100644 test/logfile_invalid_json.json create mode 100644 test/logfile_invalid_json2.json create mode 100644 test/logfile_journald.json create mode 100644 test/logfile_json.json create mode 100644 test/logfile_json2.json create mode 100644 test/logfile_json3.json create mode 100644 test/logfile_json_subsec.json create mode 100644 test/logfile_leveltest.0 create mode 100644 test/logfile_logfmt.0 create mode 100644 test/logfile_mixed_json2.json create mode 100644 test/logfile_multiline.0 create mode 100644 test/logfile_nested_json.json create mode 100644 test/logfile_openam.0 create mode 100644 test/logfile_plain.0 create mode 100644 test/logfile_pretty.0 create mode 100644 test/logfile_procstate.0 create mode 100644 test/logfile_rollover.0 create mode 100644 test/logfile_rollover.1 create mode 100644 test/logfile_strace_log.0 create mode 100644 test/logfile_syslog.0 create mode 100644 test/logfile_syslog.1 create mode 100644 test/logfile_syslog.2 create mode 100644 test/logfile_syslog.3 create mode 100644 test/logfile_syslog_fr.0 create mode 100644 test/logfile_syslog_with_access_log.0 create mode 100644 test/logfile_syslog_with_header.0 create mode 100644 test/logfile_syslog_with_mixed_times.0 create mode 100644 test/logfile_tai64n.0 create mode 100644 test/logfile_tcf.0 create mode 100644 test/logfile_tcf.1 create mode 100644 test/logfile_tcsh_history.0 create mode 100644 test/logfile_uwsgi.0 create mode 100644 test/logfile_vami.0 create mode 100644 test/logfile_vdsm.0 create mode 100644 test/logfile_vmw_log.0 create mode 100644 test/logfile_vpxd.0 create mode 100644 test/logfile_w3c.0 create mode 100644 test/logfile_w3c.1 create mode 100644 test/logfile_w3c.2 create mode 100644 test/logfile_w3c.3 create mode 100644 test/logfile_w3c.4 create mode 100644 test/logfile_w3c.5 create mode 100644 test/logfile_w3c.6 create mode 100644 test/logfile_w3c_big.0 create mode 100644 test/logfile_with_a_really_long_name_to_test_a_bug_with_long_names.0 create mode 100644 test/logfile_xml_msg.0 create mode 100644 test/multiline.lnav create mode 100644 test/mvwattrline_output.0 create mode 100644 test/nested.lnav create mode 100755 test/parser_debugger.py create mode 100644 test/remote-log-dir/logfile_access_log.0 create mode 100644 test/remote-log-dir/logfile_access_log.1 create mode 100644 test/rltest.cc create mode 100644 test/scripty.cc create mode 100644 test/si_test.cc create mode 100644 test/slicer.cc create mode 100644 test/sql.0.in create mode 100644 test/sql.0.out create mode 100644 test/test_abbrev.cc create mode 100644 test/test_ansi_scrubber.cc create mode 100644 test/test_auto_fd.cc create mode 100644 test/test_auto_mem.cc create mode 100644 test/test_bookmarks.cc create mode 100644 test/test_cli.sh create mode 100644 test/test_cmds.sh create mode 100644 test/test_column_namer.cc create mode 100755 test/test_config.sh create mode 100644 test/test_curl.sh create mode 100644 test/test_data_parser.sh create mode 100644 test/test_date_time_scanner.cc create mode 100644 test/test_events.sh create mode 100644 test/test_format_installer.sh create mode 100644 test/test_format_loader.sh create mode 100644 test/test_grep_proc.sh create mode 100644 test/test_grep_proc2.cc create mode 100644 test/test_json_format.sh create mode 100644 test/test_line_buffer.sh create mode 100644 test/test_line_buffer2.cc create mode 100644 test/test_listview.sh create mode 100644 test/test_log_accel.cc create mode 100644 test/test_logfile.sh create mode 100644 test/test_md2attr_line.cc create mode 100644 test/test_meta.sh create mode 100644 test/test_mvwattrline.sh create mode 100644 test/test_ncurses_unicode.cc create mode 100644 test/test_pretty_print.sh create mode 100644 test/test_regex101.sh create mode 100644 test/test_reltime.cc create mode 100644 test/test_remote.sh create mode 100644 test/test_scripts.sh create mode 100644 test/test_sessions.sh create mode 100644 test/test_shlexer.sh create mode 100644 test/test_sql.sh create mode 100644 test/test_sql_anno.sh create mode 100644 test/test_sql_coll_func.sh create mode 100644 test/test_sql_fs_func.sh create mode 100644 test/test_sql_indexes.sh create mode 100644 test/test_sql_json_func.sh create mode 100644 test/test_sql_regexp.sh create mode 100644 test/test_sql_search_table.sh create mode 100644 test/test_sql_str_func.sh create mode 100644 test/test_sql_time_func.sh create mode 100644 test/test_sql_views_vtab.sh create mode 100644 test/test_sql_xml_func.sh create mode 100644 test/test_sql_yaml_func.sh create mode 100644 test/test_stubs.cc create mode 100644 test/test_text_anonymizer.cc create mode 100644 test/test_text_file.sh create mode 100644 test/test_top_status.cc create mode 100644 test/test_tui.sh create mode 100644 test/test_view_colors.sh create mode 100644 test/test_vt52_curses.sh create mode 100644 test/textfile_0.md create mode 100644 test/textfile_ansi.0 create mode 100644 test/textfile_json_indented.0 create mode 100644 test/textfile_json_one_line.0 create mode 100644 test/textfile_quoted_json.0 create mode 100644 test/toplevel.lnav create mode 100644 test/tui-captures/tui_echo.0 create mode 100644 test/tui-captures/tui_help.0 create mode 100755 test/update_parser_output.sh create mode 100644 test/view_colors_output.0 create mode 100644 test/vt52_curses_input.0 create mode 100644 test/vt52_curses_input.1 create mode 100644 test/vt52_curses_output.0 create mode 100644 test/vt52_curses_output.1 create mode 100644 test/xpath_tui.0 create mode 100644 tools/Makefile.am create mode 100644 tools/bin2c.c create mode 100755 update_expected_output.sh diff --git a/.cirrus.yml b/.cirrus.yml new file mode 100644 index 0000000..3a77b7f --- /dev/null +++ b/.cirrus.yml @@ -0,0 +1,5 @@ +freebsd_instance: + image: freebsd-12-1-release-amd64 +task: + install_script: pkg install -y wget git m4 bash autoconf automake sqlite3 gmake + build_script: ./autogen.sh && ./configure && gmake -j3 diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..5eeefee --- /dev/null +++ b/.clang-format @@ -0,0 +1,178 @@ +--- +Language: Cpp +# BasedOnStyle: Chromium +AccessModifierOffset: -4 +AlignAfterOpenBracket: Align +AlignConsecutiveMacros: Consecutive +AlignConsecutiveAssignments: None +AlignConsecutiveBitFields: None +AlignConsecutiveDeclarations: None +AlignEscapedNewlines: DontAlign +AlignOperands: DontAlign +AlignTrailingComments: false +AllowAllArgumentsOnNextLine: true +AllowAllConstructorInitializersOnNextLine: false +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortEnumsOnASingleLine: false +AllowShortBlocksOnASingleLine: Empty +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: Inline +AllowShortLambdasOnASingleLine: All +AllowShortIfStatementsOnASingleLine: Never +AllowShortLoopsOnASingleLine: false +# AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: TopLevelDefinitions +AlwaysBreakBeforeMultilineStrings: true +AlwaysBreakTemplateDeclarations: Yes +BinPackArguments: false +BinPackParameters: false +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: MultiLine + AfterEnum: false + AfterFunction: true + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: true + BeforeCatch: false + BeforeElse: false + BeforeLambdaBody: false + BeforeWhile: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakBeforeBinaryOperators: All +BreakBeforeBraces: Custom +# BreakBeforeInheritanceComma: true +BreakInheritanceList: BeforeComma +BreakBeforeTernaryOperators: true +# BreakConstructorInitializersBeforeComma: true +BreakConstructorInitializers: BeforeColon +BreakAfterJavaFieldAnnotations: true +BreakStringLiterals: true +ColumnLimit: 80 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DeriveLineEnding: false +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IncludeBlocks: Regroup +IncludeCategories: + # Standard library headers come before anything else + - Regex: '^<[a-z_]+>' + Priority: -1 + - Regex: '^<.+\.h(pp)?>' + Priority: 1 + - Regex: '^<.*' + Priority: 2 + - Regex: '.*' + Priority: 3 +IncludeIsMainRegex: '' +IncludeIsMainSourceRegex: '' +IndentCaseLabels: true +IndentCaseBlocks: false +IndentGotoLabels: true +IndentPPDirectives: AfterHash +IndentExternBlock: NoIndent +IndentWidth: 4 +IndentWrappedFunctionNames: false +InsertTrailingCommas: Wrapped +JavaScriptQuotes: Double +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: false +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBinPackProtocolList: Never +ObjCBlockIndentWidth: 4 +ObjCBreakBeforeNestedBlockParam: true +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PenaltyBreakAssignment: 4 +PenaltyBreakBeforeFirstCallParameter: 1 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 200 +PointerAlignment: Left +RawStringFormats: + - Language: Cpp + Delimiters: + - cc + - CC + - cpp + - Cpp + - CPP + - 'c++' + - 'C++' + CanonicalDelimiter: '' + BasedOnStyle: google + - Language: TextProto + Delimiters: + - pb + - PB + - proto + - PROTO + EnclosingFunctions: + - EqualsProto + - EquivToProto + - PARSE_PARTIAL_TEXT_PROTO + - PARSE_TEST_PROTO + - PARSE_TEXT_PROTO + - ParseTextOrDie + - ParseTextProtoOrDie + - ParseTestProto + - ParsePartialTestProto + CanonicalDelimiter: '' + BasedOnStyle: google +ReflowComments: true +SortIncludes: CaseInsensitive +SortUsingDeclarations: true +SpaceAfterCStyleCast: true +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: false +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatementsExceptForEachMacros +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyBlock: false +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 2 +SpacesInAngles: Never +SpacesInConditionalStatement: false +SpacesInContainerLiterals: false +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +SpaceBeforeSquareBrackets: false +Standard: Auto +StatementMacros: + - Q_UNUSED + - QT_REQUIRE_VERSION +TabWidth: 8 +UseCRLF: false +UseTab: Never +WhitespaceSensitiveMacros: + - STRINGIZE + - PP_STRINGIZE + - BOOST_PP_STRINGIZE +... diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 0000000..138dd8a --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,162 @@ +--- +# Enable ALL the things! Except not really +# misc-non-private-member-variables-in-classes: the options don't do anything +Checks: > + *, + -google-readability-todo, + -altera-unroll-loops, + -altera-id-dependent-backward-branch, + -altera-struct-pack-align, + -fuchsia-*, + fuchsia-multiple-inheritance, + -llvm-header-guard, + -llvm-include-order, + -llvmlibc-*, + -modernize-use-trailing-return-type, + -misc-non-private-member-variables-in-classes, + -cppcoreguidelines-pro-type-vararg, + -hicpp-vararg, + -cppcoreguidelines-avoid-c-arrays, + -hicpp-avoid-c-arrays, + -modernize-avoid-c-arrays +WarningsAsErrors: '' +CheckOptions: + - key: 'bugprone-argument-comment.StrictMode' + value: 'true' +# Prefer using enum classes with 2 values for parameters instead of bools + - key: 'bugprone-argument-comment.CommentBoolLiterals' + value: 'true' + - key: 'bugprone-misplaced-widening-cast.CheckImplicitCasts' + value: 'true' + - key: 'bugprone-sizeof-expression.WarnOnSizeOfIntegerExpression' + value: 'true' + - key: 'bugprone-suspicious-string-compare.WarnOnLogicalNotComparison' + value: 'true' + - key: 'readability-simplify-boolean-expr.ChainedConditionalReturn' + value: 'true' + - key: 'readability-simplify-boolean-expr.ChainedConditionalAssignment' + value: 'true' + - key: 'readability-uniqueptr-delete-release.PreferResetCall' + value: 'true' + - key: 'cppcoreguidelines-init-variables.MathHeader' + value: '' + - key: 'cppcoreguidelines-narrowing-conversions.PedanticMode' + value: 'true' + - key: 'readability-else-after-return.WarnOnUnfixable' + value: 'true' + - key: 'readability-else-after-return.WarnOnConditionVariables' + value: 'true' + - key: 'readability-inconsistent-declaration-parameter-name.Strict' + value: 'true' + - key: 'readability-qualified-auto.AddConstToQualified' + value: 'true' + - key: 'readability-redundant-access-specifiers.CheckFirstDeclaration' + value: 'true' +# These seem to be the most common identifier styles + - key: 'readability-identifier-naming.AbstractClassCase' + value: 'lower_case' + - key: 'readability-identifier-naming.ClassCase' + value: 'lower_case' + - key: 'readability-identifier-naming.ClassConstantCase' + value: 'UPPER_CASE' + - key: 'readability-identifier-naming.ClassMemberCase' + value: 'lower_case' + - key: 'readability-identifier-naming.ClassMethodCase' + value: 'lower_case' + - key: 'readability-identifier-naming.ConstantCase' + value: 'UPPER_CASE' + - key: 'readability-identifier-naming.ConstantMemberCase' + value: 'UPPER_CASE' + - key: 'readability-identifier-naming.ConstantParameterCase' + value: 'lower_case' + - key: 'readability-identifier-naming.ConstantPointerParameterCase' + value: 'lower_case' + - key: 'readability-identifier-naming.ConstexprFunctionCase' + value: 'lower_case' + - key: 'readability-identifier-naming.ConstexprMethodCase' + value: 'lower_case' + - key: 'readability-identifier-naming.ConstexprVariableCase' + value: 'lower_case' + - key: 'readability-identifier-naming.EnumCase' + value: 'lower_case' + - key: 'readability-identifier-naming.EnumConstantCase' + value: 'lower_case' + - key: 'readability-identifier-naming.FunctionCase' + value: 'lower_case' + - key: 'readability-identifier-naming.GlobalConstantCase' + value: 'UPPER_CASE' + - key: 'readability-identifier-naming.GlobalConstantPointerCase' + value: 'lower_case' + - key: 'readability-identifier-naming.GlobalFunctionCase' + value: 'lower_case' + - key: 'readability-identifier-naming.GlobalPointerCase' + value: 'lower_case' + - key: 'readability-identifier-naming.GlobalVariableCase' + value: 'lower_case' + - key: 'readability-identifier-naming.InlineNamespaceCase' + value: 'lower_case' + - key: 'readability-identifier-naming.LocalConstantCase' + value: 'lower_case' + - key: 'readability-identifier-naming.LocalConstantPointerCase' + value: 'lower_case' + - key: 'readability-identifier-naming.LocalPointerCase' + value: 'lower_case' + - key: 'readability-identifier-naming.LocalVariableCase' + value: 'lower_case' + - key: 'readability-identifier-naming.MacroDefinitionCase' + value: 'UPPER_CASE' + - key: 'readability-identifier-naming.MemberCase' + value: 'lower_case' + - key: 'readability-identifier-naming.MethodCase' + value: 'lower_case' + - key: 'readability-identifier-naming.NamespaceCase' + value: 'lower_case' + - key: 'readability-identifier-naming.ParameterCase' + value: 'lower_case' + - key: 'readability-identifier-naming.ParameterPackCase' + value: 'lower_case' + - key: 'readability-identifier-naming.PointerParameterCase' + value: 'lower_case' + - key: 'readability-identifier-naming.PrivateMemberCase' + value: 'lower_case' + - key: 'readability-identifier-naming.PrivateMethodCase' + value: 'lower_case' + - key: 'readability-identifier-naming.ProtectedMemberCase' + value: 'lower_case' + - key: 'readability-identifier-naming.ProtectedMethodCase' + value: 'lower_case' + - key: 'readability-identifier-naming.PublicMemberCase' + value: 'lower_case' + - key: 'readability-identifier-naming.PublicMethodCase' + value: 'lower_case' + - key: 'readability-identifier-naming.ScopedEnumConstantCase' + value: 'lower_case' + - key: 'readability-identifier-naming.StaticConstantCase' + value: 'UPPER_CASE' + - key: 'readability-identifier-naming.StaticVariableCase' + value: 'lower_case' + - key: 'readability-identifier-naming.StructCase' + value: 'lower_case' + - key: 'readability-identifier-naming.TemplateParameterCase' + value: 'CamelCase' + - key: 'readability-identifier-naming.TemplateTemplateParameterCase' + value: 'CamelCase' + - key: 'readability-identifier-naming.TypeAliasCase' + value: 'lower_case' + - key: 'readability-identifier-naming.TypedefCase' + value: 'lower_case' + - key: 'readability-identifier-naming.TypeTemplateParameterCase' + value: 'CamelCase' + - key: 'readability-identifier-naming.UnionCase' + value: 'lower_case' + - key: 'readability-identifier-naming.ValueTemplateParameterCase' + value: 'CamelCase' + - key: 'readability-identifier-naming.VariableCase' + value: 'lower_case' + - key: 'readability-identifier-naming.VirtualMethodCase' + value: 'lower_case' + - key: 'readability-identifier-length.MinimumVariableNameLength' + value: '2' + - key: 'readability-identifier-length.MinimumParameterNameLength' + value: '2' +... diff --git a/.codespellrc b/.codespellrc new file mode 100644 index 0000000..d3634a3 --- /dev/null +++ b/.codespellrc @@ -0,0 +1,6 @@ +[codespell] +builtin = clear,rare,en-GB_to_en-US,names,informal,code +check-filenames = +check-hidden = +skip = */.git,*/build,*/prefix,*/conan +quiet-level = 2 diff --git a/.coveralls.yml b/.coveralls.yml new file mode 100644 index 0000000..38d8835 --- /dev/null +++ b/.coveralls.yml @@ -0,0 +1,2 @@ +service_name: github-actions + diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..8c8cc0a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,17 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: bug +assignees: '' + +--- + +**lnav version** +v0.11.0 is the latest + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..11fc491 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: enhancement +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/mlc_config.json b/.github/mlc_config.json new file mode 100644 index 0000000..be01fdb --- /dev/null +++ b/.github/mlc_config.json @@ -0,0 +1,10 @@ +{ + "ignorePatterns": [ + { + "pattern": "^/assets" + }, + { + "pattern": "^https://archive.org" + } + ] +} \ No newline at end of file diff --git a/.github/workflows/c-cpp.yml b/.github/workflows/c-cpp.yml new file mode 100644 index 0000000..af9bdbf --- /dev/null +++ b/.github/workflows/c-cpp.yml @@ -0,0 +1,191 @@ +name: ci-build + +on: + push: + branches: [ master ] + tags: ['*'] + pull_request: + branches: [ master ] + +jobs: + coverage: + runs-on: self-hosted + steps: + - uses: actions/checkout@v2 +# - name: Update apt +# run: sudo apt-get update +# - name: Install packages +# run: sudo apt-get install libncursesw5-dev libpcre++-dev libsqlite3-dev libbz2-dev libcurl4-openssl-dev libreadline-dev zlib1g-dev lcov +# - name: install cpp-coveralls +# run: pip install --user cpp-coveralls + - name: autogen + run: ./autogen.sh + - name: configure + run: ./configure --disable-static --enable-code-coverage --enable-debug CFLAGS=-g3 CXXFLAGS=-g3 + - name: make + run: make -j3 + - name: make check + run: make check + - name: upload cover + env: + COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }} + TRAVIS_JOB_ID: ${{ github.run_id }}-${{ github.run_number }} + run: >- + coveralls + --exclude src/doctest.hh + --exclude src/fmtlib + --exclude src/ghc + --exclude src/k_merge_tree.h + --exclude src/mapbox + --exclude src/pugixml + --exclude src/base/result.h + --exclude src/safe + --exclude src/spookyhash + --exclude src/third-party + --exclude src/ww898 + --exclude src/yajl + --exclude test + --exclude src/data_scanner_re.cc + --gcov-options '\-lp' + + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Update apt + run: sudo apt-get update + - name: Install packages + run: >- + sudo apt-get install -y + make + automake + autoconf + g++ + libpcre2-dev + libpcre3-dev + libncurses-dev + libsqlite3-dev + libbz2-dev + libcurl4-openssl-dev + libreadline-dev + tshark + zlib1g-dev + - name: autogen + run: ./autogen.sh + - name: configure + run: ./configure --disable-static + - name: make + run: make -j4 + - name: make distcheck + run: make distcheck -j4 || (test -e lnav-*/_build/sub/src/tailer/test-suite.log && cat lnav-*/_build/sub/src/tailer/test-suite.log && false) || (test -e lnav-*/_build/sub/test/test-suite.log && cat lnav-*/_build/sub/test/test-suite.log && false) + - name: Upload a Build Artifact + uses: actions/upload-artifact@v2 + with: + # Artifact name + name: lnav-tot-linux-64bit.zip + # A file, directory or wildcard pattern that describes what to upload + path: src/lnav + + build-windows: + runs-on: windows-latest + strategy: + fail-fast: false + matrix: + include: + - { icon: '🟦', sys: MSYS } + name: ${{ matrix.icon }} ${{ matrix.sys }} + defaults: + run: + shell: msys2 {0} + steps: + - name: '🧰 Checkout' + uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: '${{ matrix.icon }} Setup MSYS2' + uses: msys2/setup-msys2@v2 + with: + msystem: ${{matrix.sys}} + update: true + install: >- + autoconf + automake + gcc + git + make + zip + msys/libarchive-devel + msys/libbz2-devel + msys/libcurl-devel + msys/libidn2-devel + msys/liblzma-devel + msys/libreadline-devel + msys/libsqlite-devel + msys/libunistring-devel + msys/ncurses-devel + msys/pcre2-devel + msys/zlib-devel + - name: '🔧 Generate and configure' + run: | + set -x + ./autogen.sh + mkdir -p ../lnav-build + cd ../lnav-build + export PREFIX=$PWD/lnav + ../lnav/configure \ + --enable-static \ + LDFLAGS="-static" \ + CPPFLAGS="-I../src -I../../lnav/src -I../../lnav/src/fmtlib -O2 -DNCURSES_STATIC" \ + CXXFLAGS="-fPIC" \ + CFLAGS="-fPIC" \ + LIBS="-larchive -lssh2 -llzma -llz4 -lz -lzstd -lssl -lcrypto -liconv -lunistring -lbrotlicommon" \ + --sysconfdir=/etc \ + --prefix=$PREFIX || cat config.log + - name: '🚧 Make (do not use -j)' + run: | + set -x + cd ../lnav-build + make CFLAGS="-c" + strip -s src/lnav.exe + - name: '📦 Package for distribution' + run: | + set -x + cd ../lnav-build + export PREFIX=$PWD/lnav + make install + ldd $PREFIX/bin/lnav.exe | grep /usr | cut -d' ' -f3 | xargs -I {} cp {} $PREFIX/bin/ + mkdir -p lib/terminfo/78 + cp -r /usr/lib/terminfo/78/xterm-256color lib/terminfo/78/ + zip -r ../lnav/lnav-${{ github.ref_name }}-windows-amd64.zip lnav lib + - name: '💉 Basic test' + run: | + set -x + cd ../lnav-build + export PREFIX=$PWD/lnav + $PREFIX/bin/lnav.exe -n ../lnav/test/logfile_multiline.0 + - name: '⬆️ Upload a Build Artifact' + uses: actions/upload-artifact@v2 + with: + name: lnav-${{ github.ref_name }}-windows-amd64.zip + path: lnav-${{ github.ref_name }}-windows-amd64.zip + if-no-files-found: error +# - name: '🎁 Create Release' +# id: create_release +# uses: actions/create-release@v1 +# env: +# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} +# with: +# tag_name: ${{ github.ref_name }} +# release_name: Release ${{ github.ref_name }} +# draft: false +# prerelease: false +# - name: '⬆️ Upload Release Asset' +# id: upload-release-asset +# uses: actions/upload-release-asset@v1 +# env: +# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} +# with: +# upload_url: ${{ steps.create_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps +# asset_path: ./lnav-${{ github.ref_name }}-windows-amd64.zip +# asset_name: lnav-${{ github.ref_name }}-windows-amd64.zip +# asset_content_type: application/zip diff --git a/.github/workflows/check-md-links.yml b/.github/workflows/check-md-links.yml new file mode 100644 index 0000000..f55b468 --- /dev/null +++ b/.github/workflows/check-md-links.yml @@ -0,0 +1,12 @@ +name: Check Markdown links + +on: push + +jobs: + markdown-link-check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@master + - uses: gaurav-nelson/github-action-markdown-link-check@v1 + with: + config-file: '.github/mlc_config.json' diff --git a/.github/workflows/coverity.yml b/.github/workflows/coverity.yml new file mode 100644 index 0000000..9f4ef4d --- /dev/null +++ b/.github/workflows/coverity.yml @@ -0,0 +1,48 @@ +# GitHub actions workflow. +# https://docs.github.com/en/actions/learn-github-actions/workflow-syntax-for-github-actions + +name: Coverity Scan + +on: + push: + branches: [ main ] + + schedule: + # The GH mirroring from Google GoB does not trigger push actions. + # Fire it once a week to provide some coverage. + - cron: '39 2 * * WED' + + # Allow for manual triggers from the web. + workflow_dispatch: + +jobs: + coverity: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Update apt + run: sudo apt-get update + - name: Install packages + run: >- + sudo apt-get install -y + make + automake + autoconf + g++ + libpcre3-dev + libncurses-dev + libsqlite3-dev + libbz2-dev + libcurl4-openssl-dev + libreadline-dev + tshark + zlib1g-dev + - name: autogen + run: ./autogen.sh + - name: configure + run: ./configure --disable-static + - uses: vapier/coverity-scan-action@v1 + with: + command: make -j$(getconf _NPROCESSORS_CONF) + email: ${{ secrets.COVERITY_SCAN_EMAIL }} + token: ${{ secrets.COVERITY_SCAN_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/tailer-ape.yml b/.github/workflows/tailer-ape.yml new file mode 100644 index 0000000..52c65e4 --- /dev/null +++ b/.github/workflows/tailer-ape.yml @@ -0,0 +1,33 @@ +name: tailer-ape + +on: + push: + branches: [ master ] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: update apt + run: sudo apt-get update + - name: Install packages + run: >- + wget https://github.com/tstack/cosmopolitan/releases/download/v1.0stack/cosmopolitan-amalgamation-1.0-stack.zip && + unzip cosmopolitan-amalgamation-1.0-stack.zip + - name: Build + run: >- + gcc -g -Os -static -nostdlib -nostdinc -fno-pie -no-pie -mno-red-zone + -fno-omit-frame-pointer -pg -mnop-mcount + -o tailer.dbg -I src/tailer + src/tailer/tailer.main.c src/tailer/tailer.c src/tailer/sha-256.c + -fuse-ld=bfd -Wl,-T,ape.lds + -include cosmopolitan.h crt.o ape.o cosmopolitan.a + - name: Objcopy + run: objcopy -S -O binary tailer.dbg src/tailer/tailer.ape + - uses: stefanzweifel/git-auto-commit-action@v4 + with: + # Optional, but recommended + # Defaults to "Apply automatic changes" + commit_message: Update tailer + file_pattern: src/tailer/tailer.ape diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..100682c --- /dev/null +++ b/.gitignore @@ -0,0 +1,104 @@ +.deps +.lnav +*.dat +*.diff +test/*.err +*.index +*.log +*.o +*.obj +*.pyc +*.tmp +*.trs +*~ +.idea/ +Makefile +Makefile.in +TESTS_ENVIRONMENT +aclocal.m4 +aminclude_static.am +ar-lib +autom4te.cache +compile +config.guess +config.log +config.status +config.sub +configure +depcomp +install-sh +missing +mkinstalldirs +test-driver +docs/build +release/release-NEWS.md +release/linux-pkg/ +release/osx-build-dir/ +release/osx-pkg/ +release/outbox/ +release/vagrant-static-linux/.vagrant +release/vagrant-static-linux/lnav +cmake-build-debug/ +src/bin2c +src/config.h +src/config.h.in +src/default-config-json.c +src/default-log-formats-json.c +src/dump-pid-sh.c +src/help.c +src/init-sql.c +src/libdiag.a +src/lnav +src/lnav-test +src/ptimec +src/spookyhash/.dirstamp +src/stamp-h1 +src/static-libs/ +src/time_fmts.cc +src/yajl/.dirstamp +test/drive_data_scanner +test/drive_grep_proc +test/drive_json_op +test/drive_json_ptr_walk +test/drive_line_buffer +test/drive_listview +test/drive_logfile +test/drive_mvwattrline +test/drive_readline_curses +test/drive_sequencer +test/drive_sql +test/drive_view_colors +test/drive_vt52_curses +test/logfile_append.0 +test/logfile_syslog.1.bz2 +test/logfile_syslog.1.gz +test/scanned.dpt +test/scripty +test/simple-db.db +test/slicer +test/test_ansi_scrubber +test/test_auto_fd +test/test_auto_mem +test/test_bookmarks +test/test_chunky_index +test/test_concise +test/test_date_time_scanner +test/test_grep_proc2 +test/test_hist_source +test/test_json_ptr +test/test_line_buffer2 +test/test_log_accel +test/test_pcrepp +test/test_top_status +test/test_yajlpp +test/truncfile.0 +cmake-build/ +.vs/ +.vscode/ +build/ +cmake/open-cpp-coverage.cmake +cmake-build-*/ +conan/ +prefix/ +CMakeLists.txt.user +CMakeUserPresets.json diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..8bff71c --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "ext/pcapplusplus"] + path = ext/pcapplusplus + url = https://github.com/seladb/PcapPlusPlus.git diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 0000000..fd30369 --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,20 @@ +# .readthedocs.yaml +# Read the Docs configuration file +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +# Required +version: 2 + +# Build documentation in the docs/ directory with Sphinx +sphinx: + configuration: docs/source/conf.py + +# Optionally build your docs in additional formats such as PDF +formats: + - pdf + +# Optionally set the version of Python and requirements required to build your docs +python: + version: 3.7 + install: + - requirements: docs/requirements.txt diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md new file mode 100644 index 0000000..3ef11cb --- /dev/null +++ b/ARCHITECTURE.md @@ -0,0 +1,121 @@ +# Architecture + +This document covers the internal architecture of the Logfile Navigator (lnav), +a terminal-based tool for viewing and analyzing log files. + +## Goals + +The following goals drive the design and implementation of lnav: + +- Don't make the user do something that can be done automatically. + + Example: Automatically detect log formats for files instead of making them + specify the format for each file. + +- Be performant on low-spec hardware. + + Example: Prefer single-threaded optimizations over trying to parallelize + +- Operations should be "live" and not block the user from continuing to work. + + Example: Searches are run in the background. + +- Provide context-sensitive help. + + Example: When the cursor is over a SQL keyword/function, the help text for + that is shown above. + +- Show a preview of operations so the user knows what is going to happen. + + Example: When entering a `:filter-out` command, the matched parts of the + lines are highlighted in red. + +## Overview + +The whole of lnav consists of a +[log file parser](https://docs.lnav.org/en/latest/formats.html), +[text UI](https://docs.lnav.org/en/latest/ui.html), +[integrations with SQLite](https://docs.lnav.org/en/latest/sqlext.html), +[command-line interface](https://docs.lnav.org/en/latest/cli.html), and +[commands for operating on logs](https://docs.lnav.org/en/latest/commands.html). +Since the majority of lnav's operations center around logs, the core +data-structure is the combined log message index. The message index is populated +when new messages are read from log files. The text UI displays a subset of +messages from the index. The SQLite virtual-tables allow for programmatic access +to the messages and lnav's internal state. + +[![lnav architecture](docs/lnav-architecture.png)](https://whimsical.com/lnav-architecture-UM594Qo4G3nt2XWaSZA1mh) + +## File Monitoring + +Each file being monitored by lnav has an associated [`logfile`](src/logfile.hh) +object, be they plaintext files or files with a recognized format. These +objects are periodically polled by the main event loop to check if the file +was deleted, truncated, or new lines added. While reading new lines, if no +log format has matched yet, each line will be passed through the log format +regular expressions to try and find a match. Each line that is read is added +to an index + +#### Why is `mmap()` not used? + +Note that file contents are consumed using `pread(2)`/`read(2)` and not +`mmap(2)` since `mmap(2)` does not react well to files changing out from +underneath it. For example, a truncated file would likely result in a +`SIGBUS`. + +## Log Messages + +As files are being indexed, if a matching format is found, the file is +"promoted" from a plaintext file to a log file. When the file is promoted, +it is added to the [logfile_sub_source](src/logfile_sub_source.hh), which +collates all log messages together into a single index. + +### Timestamp Parsing + +Since all log messages need to have a timestamp, timestamp parsing needs to be +very efficient. The standard `strptime()` function is quite expensive, so lnav +includes an optimized custom parser and code-generator in the +[ptimec](src/ptimec.hh) component. The code-generator is used at compile-time +to generate parsers for several [common formats](src/time_formats.am). + +## Log Formats + +[log_format](src/log_format.hh) instances are used to parse lines from files +into `logline` objects. The majority of log formats are +[external_log_format](src/log_format_ext.hh) objects that are create from +[JSON format definitions](https://docs.lnav.org/en/latest/formats.html). The +built-in definitions are located in the [formats](src/formats) directory. Log +formats that cannot be handled through a simple regular expression are +implemented in the [log_format_impls.cc](src/log_format_impls.cc) file. + +## User Interface + +The lnav text-user-interface is built on top of +[ncurses](https://invisible-island.net/ncurses/announce.html). +However, the higher-level functionality of panels, widgets, and such is not +used. Instead, the following custom components are built on top of the ncurses +primitives: + +- [view_curses](src/view_curses.hh) - Provides the basics for text roles, which + allows for themes to color and style text. The `mvwattrline()` function does + all the heavy lifting of drawing ["attributed" lines](src/base/attr_line.hh), + which are strings that have attributes associated with a given range of + characters. +- [listview_curses](src/listview_curses.hh) - Displays a list of items that are + provided by a source. +- [textview_curses](src/textview_curses.hh) - Builds on the list view by adding + support for searching, filtering, bookmarks, etc... The main panel that + displays the logs/plaintext/help is a textview. +- [statusview_curses](src/state-extension-functions.cc) - Draws the status bars + at the top and bottom of the TUI. +- [vt52_curses](src/vt52_curses.hh) - Adapts vt52 escape codes to the ncurses + API. +- [readline_curses](src/readline_curses.hh) - Provides access to the readline + library. The readline code is executed in a child process since readline + does not get along with ncurses. The child process and readline is set to + use a vt52 terminal and the vt52_curses view is uses to translate those + escape codes to ncurses. + +The following diagram shows the underlying components that make up the TUI: + +[![lnav TUI](docs/lnav-tui.png)](https://whimsical.com/lnav-tui-MQjXc7Vx23BxQTHrnuNp5F) diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..47c4075 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,46 @@ + +Primary Author +-------------- + +Timothy Stack (timothyshanestack@gmail.com) + +Contributors +------------ + +Suresh Sundriyal (sureshsundriyal@gmail.com) +Matt Dordal (matt@dordal.org) +Christopher Meng +Salvatore Bonaccorso +Henrietta Stack +Pablo Iranzo Gómez +Brian Cain +Paul Wayper +Adam Spiers +Kevin Pham +Eli Young +Victor Hooi +Michael Bouvy +Santiago Agüero +Benny Zlotnik +Thomas Hurst +Justin Berger +Jan Chren +Geoff Crompton +Medina Maza +Phil Hord +Tristan Ramseyer +Aurélien Rouëné +Emiliano Bonassi +Darragh O'Reilly +Stéphane Blondon +Miguel Terron +Enguerrand de Rochefort +Nicolas Werner +Matt Hayden +Simos Xenitellis +Finnegan Stack +Amos Bird +Cristian Chiru +Peter Schiffer +Pedro Pombeiro +Fredrik Forséll diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..328f077 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,59 @@ +cmake_minimum_required(VERSION 3.14) + +include(cmake/prelude.cmake) + +set(CMAKE_CXX_STANDARD 14) +project( + lnav + VERSION 0.11.1 + DESCRIPTION "An advanced log file viewer for the small-scale." + HOMEPAGE_URL "https://lnav.org/" + LANGUAGES CXX C +) + +include(cmake/project-is-top-level.cmake) +include(cmake/variables.cmake) + +find_package(SQLite3 REQUIRED) +find_package(BZip2 REQUIRED) +find_package(LibArchive REQUIRED) +find_package(ZLIB REQUIRED) +find_package(pcre REQUIRED) +find_package(pcre2 REQUIRED) +find_package(readline REQUIRED) +find_package(ncurses REQUIRED) +find_package(CURL REQUIRED) + +set(lnav_LIBS + CURL::libcurl + SQLite::SQLite3 + BZip2::BZip2 + ncurses::libcurses + pcre::libpcre + pcre2::pcre2 + readline::readline + LibArchive::LibArchive + ZLIB::ZLIB + ) + +add_subdirectory(src) +# add_subdirectory(test) + +# ---- Install rules ---- + +if (NOT CMAKE_SKIP_INSTALL_RULES) + include(cmake/install-rules.cmake) +endif () + +# ---- Developer mode ---- + +if (NOT lnav_DEVELOPER_MODE) + return() +elseif (NOT PROJECT_IS_TOP_LEVEL) + message( + AUTHOR_WARNING + "Developer mode is intended for developers of lnav" + ) +endif () + +include(cmake/dev-mode.cmake) diff --git a/CMakePresets.json b/CMakePresets.json new file mode 100644 index 0000000..d1adc00 --- /dev/null +++ b/CMakePresets.json @@ -0,0 +1,142 @@ +{ + "version": 2, + "cmakeMinimumRequired": { + "major": 3, + "minor": 14, + "patch": 0 + }, + "configurePresets": [ + { + "name": "cmake-pedantic", + "hidden": true, + "warnings": { + "dev": true, + "deprecated": true, + "uninitialized": true, + "unusedCli": true, + "systemVars": false + }, + "errors": { + "dev": true, + "deprecated": true + } + }, + { + "name": "dev-mode", + "hidden": true, + "inherits": "cmake-pedantic", + "cacheVariables": { + "lnav_DEVELOPER_MODE": "ON" + } + }, + { + "name": "conan", + "hidden": true, + "cacheVariables": { + "CMAKE_TOOLCHAIN_FILE": "${sourceDir}/conan/conan_toolchain.cmake" + } + }, + { + "name": "cppcheck", + "hidden": true, + "cacheVariables": { + "CMAKE_CXX_CPPCHECK": "cppcheck;--inline-suppr" + } + }, + { + "name": "clang-tidy", + "hidden": true, + "cacheVariables": { + "CMAKE_CXX_CLANG_TIDY": "clang-tidy;--header-filter=${sourceDir}/*" + } + }, + { + "name": "ci-std", + "description": "This preset makes sure the project actually builds with at least the specified standard", + "hidden": true, + "cacheVariables": { + "CMAKE_CXX_EXTENSIONS": "OFF", + "CMAKE_CXX_STANDARD": "14", + "CMAKE_CXX_STANDARD_REQUIRED": "ON" + } + }, + { + "name": "flags-unix", + "hidden": true, + "cacheVariables": { + "CMAKE_CXX_FLAGS": "" + } + }, + { + "name": "flags-windows", + "hidden": true, + "cacheVariables": { + "CMAKE_CXX_FLAGS": "/W4 /permissive- /utf-8 /volatile:iso /EHsc /Zc:__cplusplus /Zc:throwingNew" + } + }, + { + "name": "ci-unix", + "generator": "Unix Makefiles", + "hidden": true, + "inherits": ["flags-unix", "ci-std"], + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release" + } + }, + { + "name": "ci-win64", + "inherits": ["flags-windows", "ci-std"], + "generator": "Visual Studio 17 2022", + "architecture": "x64", + "hidden": true + }, + { + "name": "coverage-unix", + "binaryDir": "${sourceDir}/build/coverage", + "inherits": "ci-unix", + "hidden": true, + "cacheVariables": { + "ENABLE_COVERAGE": "ON", + "CMAKE_BUILD_TYPE": "Coverage", + "CMAKE_CXX_FLAGS_COVERAGE": "-Og -g --coverage -fkeep-inline-functions -fkeep-static-functions", + "CMAKE_EXE_LINKER_FLAGS_COVERAGE": "--coverage", + "CMAKE_SHARED_LINKER_FLAGS_COVERAGE": "--coverage", + "CMAKE_MAP_IMPORTED_CONFIG_SANITIZE": "Coverage;RelWithDebInfo;Release;Debug;" + } + }, + { + "name": "ci-coverage", + "inherits": ["coverage-unix", "dev-mode", "conan"], + "cacheVariables": { + "COVERAGE_HTML_COMMAND": "" + } + }, + { + "name": "ci-sanitize", + "binaryDir": "${sourceDir}/build/sanitize", + "inherits": ["ci-unix", "dev-mode", "conan"], + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Sanitize", + "CMAKE_CXX_FLAGS_SANITIZE": "-O2 -g -fsanitize=address,undefined -fno-omit-frame-pointer -fno-common", + "CMAKE_MAP_IMPORTED_CONFIG_SANITIZE": "Sanitize;RelWithDebInfo;Release;Debug;" + } + }, + { + "name": "ci-build", + "binaryDir": "${sourceDir}/build", + "hidden": true + }, + { + "name": "ci-macos", + "inherits": ["ci-build", "ci-unix", "dev-mode", "conan"] + }, + { + "name": "ci-ubuntu", + "inherits": ["ci-build", "ci-unix", "clang-tidy", "conan", "dev-mode"] + }, + { + "name": "ci-windows", + "inherits": ["ci-build", "ci-win64", "dev-mode", "conan"] + } + ] +} diff --git a/CMakeUserPresets.json.example b/CMakeUserPresets.json.example new file mode 100644 index 0000000..64a7918 --- /dev/null +++ b/CMakeUserPresets.json.example @@ -0,0 +1,62 @@ +{ + "version": 2, + "cmakeMinimumRequired": { + "major": 3, + "minor": 14, + "patch": 0 + }, + "configurePresets": [ + { + "name": "dev-common", + "hidden": true, + "inherits": ["conan"], + "cacheVariables": { + "BUILD_MCSS_DOCS": "ON" + } + }, + { + "name": "dev-unix", + "binaryDir": "${sourceDir}/build/dev-unix", + "inherits": ["dev-common", "ci-unix", "dev-mode"], + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug" + } + }, + { + "name": "dev-win64", + "binaryDir": "${sourceDir}/build/dev-win64", + "inherits": ["dev-common", "ci-win64"] + }, + { + "name": "dev", + "binaryDir": "${sourceDir}/build/dev", + "inherits": "dev-unix" + }, + { + "name": "dev-coverage", + "binaryDir": "${sourceDir}/build/coverage", + "inherits": ["dev-mode", "coverage-unix", "conan"] + } + ], + "buildPresets": [ + { + "name": "dev", + "configurePreset": "dev", + "configuration": "Debug", + "jobs": 4 + } + ], + "testPresets": [ + { + "name": "dev", + "configurePreset": "dev", + "configuration": "Debug", + "output": { + "outputOnFailure": true + }, + "execution": { + "jobs": 4 + } + } + ] +} diff --git a/FUNDING.yml b/FUNDING.yml new file mode 100644 index 0000000..0f832f7 --- /dev/null +++ b/FUNDING.yml @@ -0,0 +1,2 @@ +# These are supported funding model platforms +github: [tstack] diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..a4b3414 --- /dev/null +++ b/INSTALL @@ -0,0 +1,229 @@ +Copyright 1994, 1995, 1996, 1999, 2000, 2001, 2002 Free Software +Foundation, Inc. + + This file is free documentation; the Free Software Foundation gives +unlimited permission to copy, distribute and modify it. + +Basic Installation +================== + + These are generic installation instructions. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, and a +file `config.log' containing compiler output (useful mainly for +debugging `configure'). + + It can also use an optional file (typically called `config.cache' +and enabled with `--cache-file=config.cache' or simply `-C') that saves +the results of its tests to speed up reconfiguring. (Caching is +disabled by default to prevent problems with accidental use of stale +cache files.) + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If you are using the cache, and at +some point `config.cache' contains results you don't want to keep, you +may remove or edit it. + + The file `configure.ac' (or `configure.in') is used to create +`configure' by a program called `autoconf'. You only need +`configure.ac' if you want to change it or regenerate `configure' using +a newer version of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. If you're + using `csh' on an old version of System V, you might need to type + `sh ./configure' instead to prevent `csh' from trying to execute + `configure' itself. + + Running `configure' takes awhile. While running, it prints some + messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package. + + 4. Type `make install' to install the programs and any data files and + documentation. + + 5. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. Run `./configure --help' +for details on some of the pertinent environment variables. + + You can give `configure' initial values for configuration parameters +by setting variables in the command line or in the environment. Here +is an example: + + ./configure CC=c89 CFLAGS=-O2 LIBS=-lposix + + *Note Defining Variables::, for more details. + +Compiling For Multiple Architectures +==================================== + + You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you must use a version of `make' that +supports the `VPATH' variable, such as GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + If you have to use a `make' that does not support the `VPATH' +variable, you have to compile the package for one architecture at a +time in the source code directory. After you have installed the +package for one architecture, use `make distclean' before reconfiguring +for another architecture. + +Installation Names +================== + + By default, `make install' will install the package's files in +`/usr/local/bin', `/usr/local/man', etc. You can specify an +installation prefix other than `/usr/local' by giving `configure' the +option `--prefix=PATH'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +give `configure' the option `--exec-prefix=PATH', the package will use +PATH as the prefix for installing programs and libraries. +Documentation and other data files will still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=PATH' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + + Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Specifying the System Type +========================== + + There may be some features `configure' cannot figure out +automatically, but needs to determine by the type of machine the package +will run on. Usually, assuming the package is built to be run on the +_same_ architectures, `configure' can figure that out, but if it prints +a message saying it cannot guess the machine type, give it the +`--build=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name which has the form: + + CPU-COMPANY-SYSTEM + +where SYSTEM can have one of these forms: + + OS KERNEL-OS + + See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the machine type. + + If you are _building_ compiler tools for cross-compiling, you should +use the `--target=TYPE' option to select the type of system they will +produce code for. + + If you want to _use_ a cross compiler, that generates code for a +platform different from the build platform, you should specify the +"host" platform (i.e., that on which the generated programs will +eventually be run) with `--host=TYPE'. + +Sharing Defaults +================ + + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Defining Variables +================== + + Variables not defined in a site shell script can be set in the +environment passed to `configure'. However, some packages may run +configure again during the build, and the customized values of these +variables may be lost. In order to avoid this problem, you should set +them in the `configure' command line, using `VAR=value'. For example: + + ./configure CC=/usr/local2/bin/gcc + +will cause the specified gcc to be used as the C compiler (unless it is +overridden in the site shell script). + +`configure' Invocation +====================== + + `configure' recognizes the following options to control how it +operates. + +`--help' +`-h' + Print a summary of the options to `configure', and exit. + +`--version' +`-V' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`--cache-file=FILE' + Enable the cache: use and save the results of the tests in FILE, + traditionally `config.cache'. FILE defaults to `/dev/null' to + disable caching. + +`--config-cache' +`-C' + Alias for `--cache-file=config.cache'. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`configure' also accepts some other, not widely useful, options. Run +`configure --help' for more details. + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..4ddb9f3 --- /dev/null +++ b/LICENSE @@ -0,0 +1,24 @@ +Copyright (c) 2010, Timothy Stack +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..d3f34d5 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,13 @@ + +ACLOCAL_AMFLAGS = -I . + +SUBDIRS = tools src test + +noinst_SCRIPTS = TESTS_ENVIRONMENT + +dist_man_MANS = lnav.1 + +EXTRA_DIST = \ + AUTHORS \ + LICENSE \ + README.md diff --git a/NEWS.md b/NEWS.md new file mode 100644 index 0000000..89aefc2 --- /dev/null +++ b/NEWS.md @@ -0,0 +1,1000 @@ +## lnav v0.11.1 + +Features: +* Additional validation checks for log formats have been + added and will result in warnings. Pass `-W` on the + command-line to view the warnings. The following new + check have been added: + - Each regex must have a corresponding sample log message + that it matches. + - Each sample must be matched by only one regex. +* Added built-in support for anonymizing content. The + `:write-*` commands now accept an `--anonymize` option + and there is an `anonymize()` SQL function. The + anonymization process will try to replace identifying + information with random data. For example, IPv4 addresses + are replaced with addresses in the 10.0.0.0/8 range. + (This feature is mainly intended to help with providing + information to lnav support that does not have sensitive + values.) +* Added `parse_url()` and `unparse_url()` SQL functions for + parsing URLs into a JSON object and then back again. Note + that the implementation relies on libcurl which has some + limitations, like not supporting all types of schemes + (e.g. `mailto:`). +* Added the `subsecond-field` and `subsecond-units` log + format properties to allow for specifying a separate + field for the sub-second portion of a timestamp. +* Added a keymap for Swedish keyboards. + +Breaking changes: +* The `regexp_capture()` table-valued-function now returns NULL + instead of an empty string for the `capture_name` column if + the capture is not named. + +Fixes: +* Reduce the "no patterns have a capture" error to a warning + so that it doesn't block lnav from starting up. +* Some ANSI escape sequences will now be removed before testing + regexes against a log message. +* If a line in a JSON-lines log file does not start with a + `{`, it will now be shown as-is and will not have the JSON + parse error. + +Cost of Doing Business: +* Migrated from pcre to pcre2. + +## lnav v0.11.0 + +Features: +* Redesigned the top status area to allow for user-specified + messages and added a second line that displays an interactive + breadcrumb bar. The top status line now shows the clock and + the remaining area displays whatever messages are inserted + into the lnav_user_notifications table. The information that + was originally on top is now in a second line and organized + as breadcrumbs. Pressing `ENTER` will activate the breadcrumb bar + and the left/right cursor keys can be used to select a particular + crumb while the up/down keys can select a value to switch to. + While a crumb is selected, you can also type in some text to do + a fuzzy search on the possibilities or, if the crumb represents + an array of values, enter the index to jump to. +* The pretty-print view will now show breadcrumbs that indicate the + location of the top line in the view with the prettified structure. +* Markdown files (those with a .md extension) are now rendered in the + TEXT view. The breadcrumb bar at the top will also be updated + depending on the section of the document that you are in and you + can use it to jump to different parts of the doc. +* The `:goto` command will now accept anchor links (i.e. `#section-id`) + as an argument when the text file being viewed has sections. You + can also specify an anchor when opening a file by appending + `#`. For example, `README.md#screenshot`. +* Log message comments are now treated as markdown and rendered + accordingly in the overlay. Multi-line comments are now supported + as well. +* Metadata embedded in files can now be accessed by the + `lnav_file_metadata` table. Currently, only the front-matter in + Markdown files is supported. +* Added an integration with regex101.com to make it easier to edit + log message regular expressions. Using the new "management CLI" + (activated by the `-m` option), a log format can be created from + a regular expression entry on regex101.com and existing patterns + can be edited. +* In the spectrogram view, the selected value range is now shown by + an overlay that includes a summary of the range and the number of + values that fall in that range. There is also a detail panel at + the bottom that shows the log-messages/DB-rows whose values are in + that range. You can then press TAB to focus on the detail view + and scroll around. +* Add initial support for pcap(3) files using tshark(1). +* SQL statement execution can now be canceled by pressing `CTRL+]` + (same as canceling out of a prompt). +* To make it possible to automate some operations, there is now an + `lnav_events` table that is updated when internal events occur + within lnav (e.g. opening a file, format is detected). You + can then add SQLite `TRIGGER`s to this table that can perform a + task by updating other tables. +* Tags can automatically be added to messages by defining a pattern + in a log format. Under a format definition, add the tag name + into the "tags" object in a format definition. The "pattern" + property specifies the regular expression to match against a line + in a file that matches the format. If a match is found, the tag + will be applied to the log message. To restrict matches to + certain files, you can add a "paths" array whose object elements + contain a "glob" property that will be matched against file names. +* Log messages can now be detected automatically via "watch + expressions". These are SQL expressions that are executed for + each log message. If the expressions evaluates to true, an + event is published to the `lnav_events` table that includes the + message contents. +* Added the `regexp_capture_into_json()` table-valued-function that + is similar to `regexp_capture()`, but returns a single row with a + JSON value for each match instead of a row for each capture. +* Added a `top_meta` column to the lnav_views table that contains + metadata related to the top line in the view. +* Added a `log_opid` hidden column to all log tables that contains + the "operation ID" as specified in the log format. +* Moved the `log_format` column from the all_logs table to a hidden + column on all tables. +* Add format for UniFi gateway. +* Added a `glob` property to search tables defined in log formats + to constrain searches to log messages from files that have a + matching log_path value. +* Initial indexing of large files should be faster. Decompression + and searching for line-endings are now pipelined, so they happen + in a thread that is separate from the regular expression matcher. +* Writing to the clipboard now falls back to OSC 52 escape sequence + if none of the clipboard commands could be detected. Your + terminal software will need to support the sequence and you may + need to explicitly enable it in the terminal. +* Added the `:export-session-to ` command that writes the + current session state to a file as a list of commands/SQL + statements. This script file can be executed to restore the + majority of the current state. +* Added the `echoln()` SQL function that behaves similarly to the + `:echo` command, writing its first argument to the current + output. +* Added `encode()` and `decode()` SQL functions for transcoding + blobs or text values using one of the following algorithms: + base64, hex, or uri. +* In regular expressions, capture group names are now semantically + highlighted (e.g. in the capture, `(?\w+)`, "name" would + have a unique color). Also, operations or previews that use + that regular expression will highlight the matched data with + the same color. +* Added an lnav_views_echo table that is a real SQLite table that + you can create TRIGGERs on in order to perform actions when + scrolling in a view. +* Added a `yaml_to_json()` SQL function that converts a YAML + document to the equivalent JSON. + +Breaking Changes: +* Formats definitions are now checked to ensure that values have a + corresponding capture in at least one pattern. +* Added a 'language' column to the lnav_view_filters table that + specifies the language of the 'pattern' column, either 'regex' + or 'sql'. +* Timestamps that do not have a day or month are rewritten to a + full timestamp like YYYY-MM-DD HH:MM:SS. +* Removed the summary overlay at the bottom of the log view that + displayed things like "Error rate" and the time span. It doesn't + seem like anyone used it. +* Removed the `log_msg_instance` column from the logline and search + tables since it causes problems with performance. +* Search tables now search for multiple matches within a message + instead of stopping at the first hit. Each additional match is + returned as a separate row. A `match_index` column has been + added to capture the index of the match within the message. + The table regex is also compiled with the "multiline" flag enabled + so the meaning of the `^` and `$` metacharacters are changed + to match the start/end of a line instead of the start/end of + the entire message string. +* Search tables defined in formats are now constrained to only + match log messages that are in that log format instead of all + log messages. As a benefit, the search table now includes + the columns that are defined as part of the format. +* The lnav_view_filters table will treats the tuple of + (view_name, type, language, pattern) as a `UNIQUE` index and + will raise a conflict error on an `INSERT`. Use `REPLACE INTO` + instead of `INSERT INTO` to ignore conflict error. +* The types of SQL values stored as local variables in scripts + is now preserved when used as bound variables at a later point + in the script. + +Fixes: +* Toggling enabled/disabled filters when there is a SQL expression + no longer causes a crash. +* Fix a crash related to long lines that are word wrapped. +* Multiple SQL statements in a SQL block of a script are now + executed instead of just the first one. +* In cases where there were many colors on screen, some text would + be colored incorrectly. +* The pretty-print view now handles ANSI escape sequences. +* The "overstrike" convention for doing bold and underline is now + supported. (Overstrike is a character followed by a backspace + and then the same character for bold or an underscore for + underline.) +* The `:eval` command now works with searching (using the '/' + prefix). + +## lnav v0.10.1 + +Features: +* Added `:show-only-this-file` command that hides all files except the + one for the top line in the view. +* The `:write-raw-to` command now accepts a `--view` flag that specifies + the source view for the data to write. For example, to write the + results of a SQL query, you would pass `--view=db` to the command. +* The commands used to access the clipboard are now configured through + the "tuning" section of the configuration. +* Added an `lnav_version()` SQL function that returns the current + version string. +* Added basic support for the logfmt file format. Currently, only files + whose lines are entirely logfmt-encoded are supported. The lines + must also contain either a field named `time` or `ts` that contains + the timestamp. +* Added the `logfmt2json()` SQL function to convert a string containing + a logfmt-encoded message into a JSON object that can be operated on + more easily. +* Added the `gzip()` and `gunzip()` SQL functions to compress values + into a blob and decompress a blob into a string. +Interface changes: +* The xclip implementation for accessing the system clipboard now writes + to the "clipboard" selection instead of the "primary" selection. +* The 'query' bookmark type and `y`/`Y` hotkeys have been removed due to + performance issues and the functionality is probably rarely used. +Bug Fixes: +* The text "send-input" would show up on some terminals instead of + ignoring the escape sequence. This control sequence was only + intended to be used in the test suite. +* Remote file synchronization has been optimized a bit. +* Configuration values loaded from the `~/.lnav/configs` directory + are now included in the default configuration, so they won't be + saved into the `~/.lnav/config.json` user configuration file. +* Key handling in the visual filter editor will no longer swallow + certain key-presses when editing a filter. +* Scrolling performance restored in the SQL view. +* The `:redirect-to` command now works with `/dev/clipboard` +* The field overlay (opened by pressing 'p') now shows `log_time` + for the timestamp field instead of the name defined in the format. +* The search term in the bottom status bar will now update properly + when switching views. +* The "Out-Of-Time-Order Message" overlay will be shown again. +* The tab for the "Files" panel will be highlighted in red if there + is an issue opening a file. +* Overwritten files should be reloaded again. +* The `jget()` SQL function now returns numbers with the correct type. +* The `json_contains()` SQL function now returns false if the first + argument is NULL instead of NULL. +* The local copies of remote files are now cleaned up after a couple + days of the host not being accessed. +* The initial loading and indexing phase has been optimized. + +## lnav v0.10.0 + +Features: +* Files on remote machines can be viewed/tailed if they are accessible + via SSH. The syntax for specifying the host and path is similar to + scp. For example, to view the files in the /var/log directory on the + machine `host1.example.org`: + ```console + $ lnav user@host1.example.org:/var/log + ``` + Note that you must be able to log into the machine without any + interaction. +* Added the `:filter-expr` command to filter log messages based on an SQL + expression. This command allows much greater control over filtering. +* Added the `:mark-expr` command to mark log messages based on an SQL + expression. This command makes it easier to programmatically mark + log messages compared to using SQL. +* Added support for archive files, like zip, and other compression formats, + like xz, when compiled with libarchive. When one of these types of + files is detected, they are unpacked into a temporary directory and + all the files are loaded into lnav. +* Added an `xpath()` table-valued function for extracting values from + strings containing XML snippets. +* Added the `:prompt` command to allow for more customization of prompts. + Combined with a custom keymapping, you can now open a prompt and prefill + it with a given value. For example, a key could be bound to the + following command to open the command prompt with `:filter-in ` + already filled in: + ```lnav + :prompt command : 'filter-in ' + ``` +* Added support for the W3C Extended Log File Format with the name + `w3c_log`. Similarly to the bro log format, the header is used to + determine the columns in a particular file. However, since the columns + can be different between files, the SQL table only has a well-known set + of columns and the remainder are accessible through JSON-objects stored + in columns like `cs_headers` and `sc_headers`. +* Added support for the S3 Access File Format. +* To jump to the first search hit above the top line in a view, you can + press `CTRL+J` instead of `ENTER` in the search prompt. Pressing `ENTER` + will jump to the first hit below the current window. +* Filtering, as a whole, can be now disabled/enabled without affecting + the state of individual filters. This includes text and time-filters + (i.e. `:hide-lines-before`). You can enable/disable filtering by: + pressing `f` in the filter editor UI; executing the `:toggle-filtering` + command; or by doing an `UPDATE` on the "filtering" column of the + `lnav_views` SQLite table. +* Themes can now include definitions for text highlights under: + `/ui/theme-defs//highlights` +* Added a "grayscale" theme that isn't so colorful. +* Added the `humanize_file_size()` SQL function that converts a numeric size + to a human-friendly string. +* Added the `sparkline()` SQL function that returns a "sparkline" bar made + out of unicode characters. It can be used with a single value or as + an aggregator. +* Added a `log_time_msecs` hidden column to the log tables that returns + the timestamp as the number of milliseconds from the epoch. +* Added an `lnav_top_file()` SQL function that can be used to get the + name of the top line in the top view or NULL if the line did not come + from a file. +* Added a `mimetype` column to the lnav_file table that returns a guess as + to the MIME type of the file contents. +* Added a `content` hidden column to the lnav_file table that can be used + to read the contents of the file. The contents can then be passed to + functions that operate on XML/JSON data, like `xpath()` or `json_tree()`. +* Added an `lnav_top_view` SQL VIEW that returns the row for the top view + in the lnav_views table. +* The `generate_series()` SQLite extension is now included by default. + One change from the standard implementation is that both the start and + stop are required parameters. +* Added the `;.read` SQL command for executing a plain SQL file. +* Added the `-N` flag so that lnav will run without opening the default + syslog file. + +Interface Changes: +* When copying log lines, the file name and time offset will be included + in the copy if they are enabled. +* Log messages that cannot be parsed properly will be given an "invalid" + log level and the invalid portions colored yellow. +* The range_start and range_stop values of the `regexp_capture()` results + now start at 1 instead of zero to match with what the other SQL string + functions expect. +* The `:write-cols-to` command has been renamed to `:write-table-to`. +* The DB view will limit the maximum column width to 120 characters. +* The `:echo` command now evaluates its message to do variable + substitution. +* The `:write-raw-to` command has been changed to write the original + log file content of marked lines. For example, when viewing a JSON + log, the JSON-Line values from the log file will be written to the + output file. The `:write-view-to` command has been added to perform + the previous work of `:write-raw-to` where the raw content of the view + is written to the file. + +Fixes: +* Unicode text can now be entered in prompts. +* The `replicate()` SQL function would cause a crash if the number of + replications was zero. +* Many internal improvements. + +## lnav v0.9.0 + +Features: +* Added support for themes and included a few as well: default, eldar, + monocai, night-owl, solarized-light, and solarized-dark. The theme + can be changed using the `:config` command, like so: + ```lnav + :config /ui/theme night-owl + ``` + Consult the online documentation for defining a new theme at: + https://lnav.readthedocs.io/en/latest/config.html#theme-definitions +* Added support for custom keymaps and included the following: de, fr, + uk, us. The keymap can be changed using the `:config` command, like so: + ```lnav + :config /ui/keymap uk + ``` + Consult the online documentation for defining a new keymap at: + https://lnav.readthedocs.io/en/latest/config.html#keymap-definitions +* The following JSON-Schemas have been published for the log format and + configuration JSON files: + - https://lnav.org/schemas/format-v1.schema.json + - https://lnav.org/schemas/config-v1.schema.json + + Formats should be updated to reference the schema using the `$schema` + property. +* Indexing of new data in log files can now be paused by pressing `=` + and unpaused by pressing it again. The bottom status bar will display + 'Paused' in the right corner while paused. +* CMake is now a supported way to build. +* When viewing data from the standard-input, a symbolic name can be used + to preserve session state. The name can be changed using the + `|rename-stdin` lnav script or by doing an `UPDATE` to the filepath + column of the lnav_file table. For example, to assign the name + "journald", the following SQL statement can be executed in lnav: + ```lnav + ;UPDATE lnav_file SET filepath='journald' WHERE filepath='stdin' + ``` +* The size of the terminal can be accessed in SQL using the `$LINES` and + `$COLS` variables. +* The `raise_error(msg)` SQL function has been added to make it easier to + raise an error in an lnav script to stop execution and notify the user. +* Added the `json_concat()` function to make it easier to append/concatenate + values onto arrays. +* Added the `:write-jsonlines-to` command that writes the result of a SQL + query to a file in the JSON Lines format. + +Interface Changes: +* Data piped into lnav is no longer dumped to the console after exit. + Instead, a file containing the data is left in `.lnav/stdin-captures/` + and a message is printed to the console indicating the file name. +* In time-offset mode, the deltas for messages before the first mark + are now negative instead of relative to the start of the log. +* The $XDG_CONFIG_HOME environment variable (or `~/.config` directory) are + now respected for storing lnav's configuration. If you have an existing + `~/.lnav` directory, that will continue to be used until you move it to + `$XDG_CONFIG_HOME/lnav` or `~/.config/lnav`. +* Removed the `:save-config` command. Changes to the configuration are now + immediately saved. + +Fixes: +* Added 'notice' log level. +* If a `timestamp-format` is used in an element of a `line-format`, the + field name is ignored and a formatted timestamp is always used. +* Ignore stdin when it is connected to `/dev/null`. + +## lnav v0.8.5 + +Features: +* Added a visual filter editor to make it easier to update existing + filters. The editor can be opened by pressing `TAB`. Once the editor + is opened, you can create/delete, enable/disable, and edit the patterns + with hotkeys. +* Added an `lnav_view_filters` SQL table that can be used to + programmatically manipulate filters. +* Added an `lnav_view_filter_stats` SQL table that contains the number of + times a given filter matched a line in the view. +* Added a `log_filters` column to log tables that can be used to see what + filters matched the log message. +* A history of locations in a view is now kept so that you can jump back + to where you were previously using the `{` and `}` keys. The location + history can also be accessed through the `:prev-location` and + `:next-location` commands. +* The `:write-*` commands will now accept `/dev/clipboard` as a file name + that writes to the system clipboard. +* The `:write-to` and `:write-raw-to` commands will now print out comments + and tags attached to the lines. +* Added a `:redirect-to ` command to redirect command output to the + given file. This command is mostly useful in scripts where one might + want to redirect all output from commands like `:echo` and `:write-to -` + to a single file. +* If a log file format has multiple patterns for matching log messages, + each pattern is now tried to match a message in a file. Previously, + only one pattern was ever used for an entire file. +* Added haproxy log format from Peter Hoffmann. +* Added `spooky_hash()` and `group_spooky_hash()` SQL functions to + generate a hash of their parameters. +* Added `time_offset` to the `lnav_file` table so that the timestamps in + a file can be adjusted programmatically. + +Interface Changes: +* The auto-complete behavior in the prompt has been modified to fall back + to a fuzzy search if the prefix search finds no matches. For example, + typing in `:fin` and pressing TAB would previously not do anything. + Now, the `:fin` will be completed to `:filter-in ` since that is a + strong fuzzy match. If there are multiple matches, as would happen + with `:dfil`, readline's menu-complete behavior will be engaged and + you can press `TAB` cycle through the options. +* Added `CTRL+F` to toggle the enabled/disabled state of all filters for the + current view. +* The `-r` flag is now for recursively loading files. The functionality + for loading rotated files is now under the `-R` flag. +* The current search term is now shown in the bottom status bar. +* Some initial help text is now shown for the search and SQL prompts to + refresh the memory. +* When entering the `:comment` command for a line with a comment, the + command prompt will be filled in with the existing comment to make + editing easier. +* Hidden fields now show up as a unicode vertical ellipsis (⋮) instead of + three-dot ellipsis to save space. +* Pressing 7/8 will now move to the previous/next minute. +* The `:write-raw-to` command has been changed to write the entire + contents of the current view and a `:write-screen-to` command has been + added to write only the current screen contents. +* Disabled filters are now saved in sessions. +* The `:adjust-log-time` command now accepts relative times as input. + +Fixes: +* The `:write-json-to` command will now pass through JSON cells as their + JSON values instead of a JSON-encoded string. + +## lnav v0.8.4 + +Features: +* Added the `:comment` command that can be used to attach a comment to a + log line. The comment will be displayed below the line, like so: + ``` + 2017-01-01T15:30:00 error: computer is on fire + + This is where it all went wrong + ``` + The `:clear-comment` command will remove the attached comment. Comments + are searchable with the standard search mechanism and they are available + in SQL through the `log_comment` column. +* Added the `:tag`, `:untag`, and `:delete-tags` commands that can be used + to attach/detach tags on the top log line and delete all instances of + a tag. Tags are also searchable and are available in SQL as a JSON + array in the `log_tags` column. +* Pressing left-arrow while viewing log messages will reveal the source + file name for each line and the unique parts of the source path. + Pressing again will reveal the full path. +* The file name section of the top status line will show only the unique + parts of the log file path if there is not enough room to show the full + path. +* Added the `:hide-unmarked-lines` and `:show-unmarked-lines` commands + that hide/show lines based on whether they are bookmarked. +* Added the `json_contains()` SQL function to check if a JSON value + contains a number of a string. +* The relative time parser recognizes "next" at the beginning of the + input, for example, "next hour" or "next day". Handy for use in the + `:goto` command. +* Added a "text-transform" option for formatting JSON log messages. The + supported options are: none, uppercase, lowercase, and capitalize. +* Added a special `__level__` field name for formatting JSON messages so + that the lnav level name can be used instead of the internal value in + the JSON object. +* Added a log format for journald JSON logs. + +Interface Changes: +* When typing in a search, instead of moving the view to the first match + that was found, the first ten matches will be displayed in the preview + window. +* The pretty-print view maintains highlighting from the log view. +* The pretty-print view no longer tries to reverse lookup IP addresses. +* The online help for commands and SQL functions now includes a 'See Also' + section that lists related commands/functions. + +Fixes: +* The HOME key should now work in the command-prompt and move the cursor + to the beginning of the line. +* The `:delete-filter` command should now tab-complete existing filters. +* Milliseconds can now be used in relative times (e.g. 10:00:00.123) +* The `J`/`K` hotkeys were not marking lines correctly when the bottom of + the view was reached. +* The level field in JSON logs should now be recognized by the level + patterns in the format. + +## lnav v0.8.3 + +Features: +* Support for the Bro Network Security Monitor (https://www.bro.org) log + file format. +* Added an `fstat()` table-valued function for querying the local + filesystem. +* Added `readlink()` and `realpath()` SQL functions. +* Highlights specified in log formats can now specify the colors to use + for the highlighted parts of the log message. +* Added a `:quit` command. +* Added a `/ui/default-colors` configuration option to specify that the + terminal's default background and foreground colors should be used + instead of black and white. + +Interface Changes: +* Pressing delete at a command-prompt will exit the prompt if there is no + other input. + +Fixes: +* The help view now includes all the command-help that would pop up as + you entered commands and SQL queries. +* Hidden fields and lines hidden before/after times are now saved in the + current session and restored. +* Unicode characters should now be displayed correctly (make sure you + have LANG set to a UTF-8 locale). + +## lnav v0.8.2 + +Features: +* The timestamp format for JSON log files can be specified with the + `timestamp-format` option in the `line-format` array. +* Added "min-width", "max-width", "align", and "overflow" options to the + "line-format" in format definitions for JSON log files. These options + give you more control over how the displayed line looks. +* Added a "hidden" option to log format values so that you can hide JSON + log fields from being displayed if they are not in the line format. +* Added a `rewriter` field to log format value definitions that is a + command used to rewrite the field in the pretty-printed version of a + log message. For example, the HTTP access log format will rewrite the + status code field to include the textual version (e.g. 200 (OK)). +* Log message fields can now be hidden using the `:hide-fields` command or + by setting the 'hidden' property in the log format. When hidden, the + fields will be replaced with a yellow ellipsis when displayed. Hiding + large fields that contain extra details can make the log easier to read. + The `x` hotkey can be used to quickly toggle whether these fields are + displayed or not. +* Added a `:mark` command to bookmark the top line in the current view. +* Added an `:alt-msg` command that can be used to set the text to be + displayed in the bottom right of the command line. This command is + mostly intended for use by hotkey maps to set the help text. +* In lnav scripts, the first row of a SQL query result will now be turned + into local variables that can be referenced in other commands or + queries. For example, the following script will print the number one: + ```lnav + ;SELECT 1 as foobar + :eval :echo ${foobar} + ``` +* Added an `lnav_view_stack` SQL table that gives access to the view + stack. +* Added a `top_time` column to the lnav_views table so that you can get + the timestamp for the top line in views that are time-based as well as + allowing you to move the view to a given time with an UPDATE statement. +* Added a 'search' column to the lnav_views table so that you can perform + a text search programmatically. +* Added a `regexp_capture(, )` table-valued function for + getting detailed results from matching a regular expression against a + string. +* Added a `timediff(, )` SQL function for computing the + difference between two relative or absolute timestamps. +* Log formats can now define a default set of highlights with the + "highlights" property. +* Added a `|search-for ` built-in script that can be used to + start a search from the command-line. +* Log format definitions can now specify the expected log level for a + sample line. This check should make it easier to validate the + definition. + +Interface Changes: +* Command and SQL documentation is now displayed in a section at the + bottom of the screen when a command or query is being entered. Some + commands will also display a preview of the command results. For + example, the `:open` command will display the first ten lines of the + file to be opened and the `:filter-out` command will highlight text + that matches in the current view. The preview pane can be shown/hidden + by pressing `CTRL-P`. +* The color used for text colored via `:highlight` is now based on the + the regex instead of randomly picked so that colors are consistent + across invocations. +* The "graph" view has been removed since it's functionality has been + obsoleted by other features, like `:create-search-table`. +* When doing a search, if a hit is found within a second after hitting + ``, the view will move to the matched line. The previous behavior + was to stay on the current line, which tended to be a surprise to new + users. +* Pressing `n`/`N` to move through the next/previous search hit will now + skip adjacent lines, up to the vertical size of the view. This should + make scanning through clusters of hits much faster. Repeatedly + pressing these keys within a short time will also accelerate scanning + by moving the view at least a full page at a time. + +Breaking Changes: +* The captured timestamp text in log files must fully match a known format + or an error will be reported. The previous behavior was to ignore any + text at the end of the line. + +Fixes: +* You can now execute commands from the standard input by using a dash (`-`) + with the `-f` command-line argument. Reading commands from a file + descriptor should also work, for example, with the following bash + syntax: + ```console + $ lnav -f <(echo :open the-file-to-open) + ``` +* Programming language syntax highlighting should now only be applied to + source code files instead of everywhere. + +## lnav v0.8.1 + +Features: +* Added a spectrogram command and view that displays the values of a + numeric field over time. The view works for log message fields or + for database result columns. +* Log formats can now create SQL views and execute other statements + by adding `.sql` files to their format directories. The SQL scripts + will be executed on startup. +* Added `json_group_object` and `json_group_array` aggregate SQL + functions that collects values from a GROUP BY query into a JSON + object or array, respectively. +* The SQL view will now graph values found in JSON objects/arrays in + addition to the regular columns in the result. +* Added an `regexp_match(, )` SQL function that can be used to + extract values from a string using a regular expression. +* Added an `extract()` SQL function that extracts values using the + same data discover/extraction parser used in the `logline` table. +* Added a "summary" overlay line to the bottom of the log view that + displays how long ago the last message was received, along with the + total number of files and the error rate over the past five minutes. +* Pressing `V` in the DB view will now check for a column with a + timestamp and move to the corresponding time in the log view. +* Added `a`/`A` hotkeys to restore a view previously popped with `q`/`Q`. +* Added `:hide-lines-before`, `:hide-lines-after`, and + `:show-lines-before-and-after` commands so that you can filter out + log lines based on time. +* Scripts containing lnav commands/queries can now be executed using + the pipe (`|`) hotkey. See the documentation for more information. +* Added an `:eval` command that can be used to execute a command or + query after performing environment variable substitution. +* Added an `:echo` command that can be useful for scripts to message + the user. +* The `log_part` column can now be set with an SQL `UPDATE` statement. +* Added a `log_body` hidden column that returns the body of the log + message. +* Added `:config`, `:reset-config`, and `:save-config` commands to change + configuration options, reset to default, and save them for future + executions. +* Added a `/ui/clock-format` configuration option that controls the time + format in the top-left corner. +* Added a `/ui/dim-text` configuration option that controls the brightness + of text in the UI. +* Added support for TAI64 timestamps (http://cr.yp.to/libtai/tai64.html). +* Added a safe execution mode. If the `LNAVSECURE` environment variable is + set before executing lnav, the following commands are disabled: + - `:open` + - `:pipe-to` + - `:pipe-line-to` + - `:write-*-to` + + This makes it easier to run lnav with escalated privileges in restricted + environments, without the risk of users being able to use the above + mentioned commands to gain privileged access. + +Interface Changes: +* The `o`/`O` hotkeys have been reassigned to navigate through log + messages that have a matching "opid" field. The old action of + moving forward and backward by 60 minutes can be simulated by + using the `:goto` command with a relative time and the `r`/`R` + hotkeys. +* Log messages with timestamps that pre-date previous log messages will + have the timestamp highlighted in yellow and underlined. These out- + of-time-order messages will be assigned the time of the previous + message for sorting purposes. You can press the 'p' hotkey to examine + the 'Received Time' of the message as well as the time parsed from the + original message. A `log_actual_time` hidden field has also been + added to the SQLite virtual table so you can operate on the original + message time from the file. +* The `A`/`B` hotkeys for moving forward/backward by 10% line increments + have been reassigned to `[` and `]`. The `a` and `A` hotkeys are now + used to return to the previously popped view while trying to preserve + the time range. For example, after leaving the spectrogram view with + 'q', you can press 'A' return to the view with the top time in the + spectrogram matching the top time in the log view. +* The 'Q' hotkey now pops the current view off of the stack while + maintaining the top time between views. + +Fixes: +* Issues with tailing JSON logs have been fixed. +* The `jget()` SQL function should now work for objects nested in arrays. + +## lnav v0.8.0 + +Features: +* Integration with "papertrailapp.com" for querying and tailing + server log and syslog messages. See the Papertrail section in + the online help for more details. +* Remote files can be opened when lnav is built with libcurl v7.23.0+ +* SQL queries can now be done on lines that match a regular expression + using the `log_search` table or by creating custom tables with the + `:create-search-table` command. +* Log formats that are "containers" for other log formats, like + syslog, are now supported. See the online help for more + information. +* Formats can be installed from git repositories using the `-i` option. + A standard set of extra formats can be installed by doing + `lnav -i extra`. (You must have git installed for this to work.) +* Added support for 'VMware vSphere Auto Deploy' log format. +* Added a 'sudo' log format. +* Added hotkeys to move left/right by a smaller increment (H/L or + Shift+Left/Shift+Right). +* A color-coded bar has been added to the left side to show where + messages from one file stop and messages from another file start. +* The `-C` option will now try to check any specified log files to + make sure the format(s) match all of the lines. +* Added an `all_logs` SQLite table that contains the message format + extracted from each log line. Also added a `;.msgformat` SQL command + that executes a query that returns the counts for each format and the + first line where the format was seen. +* Added an `lnav_views` SQLite table that can be used to query and + change the lnav view state. +* When typing in a command, the status bar will display a short + summary of the currently entered command. +* Added a `:delete-filter` command. +* Added a `log_msg_instance` column to the logline and log_search + tables to make it easier to join tables that are matching log + messages that are ordered. +* Added a `timeslice()` function to SQLite so that it is easier to + group log messages by time buckets. +* The `:goto` command now supports relative time values like + `a minute ago`, `an hour later`, and many more. + +Interface Changes: +* The `r`/`R` hotkeys have been reassigned to navigate through the log + messages by the relative time value that was last used with the + `:goto` command. + +Fixes: +* The pretty-print view should now work for text files. +* Nested fields in JSON logs are now supported for levels, bodies, etc... +* Tab-completion should work for quoted SQL identifiers. +* 'lo-fi' mode key shortcut changed to `CTRL+L`. +* 'redraw' shortcut removed. Relegated to just a command. +* Fixed lnav hang in pretty-print mode while doing a dns lookup. +* The generic log message parser used to extract data has been + optimized and should be a bit faster. + +## lnav v0.7.3 + +Features: +* Add `:pipe-to` and `:pipe-line-to` commands that pipe the currently + marked lines or the current log message to a shell command, + respectively. +* Added a "pretty-print" view (P hotkey) that tries to reformat log + messages so that they are easier to read. +* Added a `:redraw` command (CTRL+L hotkey) to redraw the window in + case it has been corrupted. +* Added a `:relative-goto` command to move the current view relative + to its current position. +* Experimental support for linking with jemalloc. +* The plain text view now supports filtering. +* Added `:next-mark` and `:prev-mark` commands to jump to the next or + previous bookmarked line (e.g. error, warning, ...) +* Added a `:zoom-to` command to change the zoom level of the histogram + view. +* Log formats can now define their own timestamp formats with the + `timestamp-format` field. + +Fixes: +* Autotools scripts overhaul. +* Added a configure option to disable linking with libtinfo. The newer + versions of ncurses don't require it, however the build silently pulls + it in as a dependency, if it is available on the system. This can be + explicitly disabled using the `--disable-tinfo` option during configure. +* Fixed the configure script behavior to ignore the values specified using + the CFLAGS and LDFLAGS environment variables while searching for sqlite3 + when `--with-sqlite3` switch was specified without the prefix. +* The configure script now recognizes libeditline symlink'ed to masquerade + as libreadline. This previously used to cause problems at compile time, + specially on OS X. If you come across this error, use the + `--with-readline=prefix` switch to specify the path to the correct + location of libreadline. +* The order that log formats are tried against a log file is now + automatically determined so that more specific formats are tested + before more general ones. The order is determined on startup based on + how each format matches each other formats sample lines. +* Command files (i.e. those executed via the `-f` flag) now support + commands/queries that span more than one line. +* Added more log levels: stats, debug2 - debug5. + +## lnav v0.7.2 + +* Added log formats for vdsm, openstack, and the vmkernel. +* Added a "lo-fi" mode (L hotkey) that dumps the displayed log lines + to the terminal without any decorations. The `:write-to`, `:write-json-to`, + and `:write-csv-to` commands will also write their output to the terminal + when passed `-` as the file name. This mode can be useful for copying + plain text lines to the clipboard. +* (OS X) Text search strings are copied to the system's "find" clipboard. + Also, when starting a new search, the current value in the "find" + clipboard can be tab-completed. + +## lnav v0.7.1 + +Features: +* Added an `environ` SQL table that reflects lnav's environment + variables. The table can be read and written to using SQL + `SELECT`, `INSERT`, `UPDATE`, and `DELETE` statements. Setting + variables can be a way to use SQL query results in lnav commands. +* Added a `jget()` SQLite function that can extract fields from a JSON- + encoded value. +* Added log formats for the OpenAM identity provider. +* Added a `:clear-highlight` command to clear previous calls to the + `:highlight` command. +* Fixed some performance bugs in indexing JSON log formats. Loading + times should be at least five times faster. +* Filtering performance should be improved so that enabling/disabling + filters should be almost instantaneous. +* The `:filter-in`, `:filter-out`, and `:highlight` commands now support + tab-completion of text in the current view. +* Add a `-i` flag that installs format files in: `~/.lnav/formats/installed` + +## lnav v0.7.0 + +Features: +* Add the '.schema' SQL command to open a view that displays the schema + for the internal tables and any attached databases. If lnav was only + executed with a SQLite database and no text files, this view will open + by default. +* The scroll bar now indicates the location of errors/warnings, search + hits, and bookmarks. +* The xterm title is update to reflect the file name for the top line + in the view. +* Added a "headless" mode so that you can execute commands and run SQL + queries from the command-line without having to do it from the curses + UI. +* When doing a search or SQL query, any text that is currently being + displayed can be tab-completed. +* The `-H` option was added so you can view the internal help text. +* Added the 'g/G' hotkeys to move to the top/bottom of the file. +* Added a `log_mark` column to the log tables that indicates whether or + not a log message is bookmarked. The field is writable, so you can + bookmark lines using an SQL UPDATE query. +* Added syntax-highlighting when editing SQL queries or search regexes. +* Added a `:write-json-to` command that writes the result of a SQL query + to a JSON-formatted file. +* The "elapsed time" column now uses red/green coloring to indicate + sharp changes in the message rate. +* Added a `:set-min-log-level` command to filter out log messages that + are below a given level. + +Fixes: +* Performance improvements. +* Multi-line filtering has been fixed. +* A collator has been added to the log_level column in the log tables + so that you can write expressions like `log_level > 'warning'`. +* The log_time datetime format now matches what is returned by + `datetime('now')` so that collating works correctly. +* If a search string is not valid PCRE syntax, a search is done for + the exact string instead of just returning an error. +* Static-linking has been cleaned up. +* OpenSSL is no longer a requirement. +* Alpha support for Windows/cygwin. +* Environment variables can now be accessed in SQL queries using + the syntax: `$VAR_NAME` +* An internal log is kept and written out on a crash. +* Partition bookmarks are now tracked separately from regular user + bookmarks. You can start a partition with the 'partition-name' + command and remove it with the 'clear-partition' command. +* Improved display of possible matches during tab-completion in the + command-prompt. The matches are now shown in a separate view and + pressing tab repeatedly will scroll through the view. +* The "open" command now does shell word expansion for file names. +* More config directory paths have been added: `/etc/lnav`, + `$prefix/etc/lnav`, and directories passed on the command-line + with `-I`. + +## lnav v0.6.2 + +Features: +* Word-wrap support. + +Fixes: +* Fix some OS X Mavericks build/runtime issues. + +## lnav v0.6.1 +Features: +* Support for JSON-encoded log files. + +Fixes: +* Some minor fixes and performance improvements. + +## lnav v0.6.0 + +Features: +* Custom log formats and more builtin formats +* Automatic extraction of data from logs +* UI improvements, support for 256 color terminals + +## lnav v0.5.1 + +Features: +* Added the `-t` and `-w` options which can be used to prepend a + timestamp to any data piped in on stdin and to specify a file to + write the contents of stdin to. + +Fixes: +* Cleanup for packaging. + +## lnav v0.5.0 + +Features: +* Files can be specified on the command-line using wildcards so that + new files are automatically loaded. Directories can also be passed + as command-line arguments to read all of the files in the directory. +* Builds on cygwin again. +* Added the `C` hotkey to clear any existing user bookmarks. +* Added experimental support for accepting input from mice. + +Fixes: +* Internal cleanup. +* Copying to the clipboard on OS X is now supported. +* Many bug fixes. + +## lnav v0.4.0 + +Features: +* Files that are not recognized as containing log messages have been + broken out to a separate text files view. You can flip between the + log view and the text file view with the `t` hotkey. When viewing + text files, the `f` hotkey will switch between files. +* Files compressed with bzip2 are recognized and decompressed on the + fly. +* Added a "session" file and command for storing commands that should + be executed on startup. For example, if you always want some + highlighting to be done, you can add that command to the session + file. + +Fixes: +* Add some more log file formats for generic log files. +* Performance improvements for compressed files. +* Works on OS X now. + +## lnav v0.3.0 + +Changes: +* The hotkey for the SQL view was changed to `v` and `V` from '.'. + +Features: +* You can now switch between the SQL result view and the log view while + keeping the top of the views in sync with the `log_line` column. + +Fixes: +* The `log_line` column is no longer included in the SQL result view's + stacked bar graph. +* Added a "warnings" count to the histogram view. diff --git a/README b/README new file mode 100644 index 0000000..904ce9a --- /dev/null +++ b/README @@ -0,0 +1,80 @@ + +LNAV +---- + +The log file navigator, lnav, is an enhanced log file viewer that +takes advantage of any semantic information that can be gleaned from +the files being viewed, such as timestamps and log levels. Using this +extra semantic information, lnav can do things like interleaving +messages from different files, generate histograms of messages over +time, and providing hotkeys for navigating through the file. It is +hoped that these features will allow the user to quickly and +efficiently zero in on problems. + + +PREREQUISITES +------------- + +The following software packages are required to build/run lnav: + + gcc/clang - A C++14-compatible compiler. + libpcre2 - The Perl Compatible Regular Expression v2 (PCRE2) library. + sqlite - The SQLite database engine. Version 3.9.0 or higher is required. + ncurses - The ncurses text UI library. + readline - The readline line editing library. + zlib - The zlib compression library. + bz2 - The bzip2 compression library. + re2c - The re2c scanner generator. + libcurl - The cURL library for downloading files from URLs. Version + 7.23.0 or higher is required. + libarchive - The libarchive library for opening archive files, like zip/tgz. + wireshark - The 'tshark' program is used to interpret pcap files. + + +INSTALLATION +------------ + +Lnav follows the usual GNU style for configuring and installing software: + +Run "./autogen.sh" if compiling from a cloned repository. + + $ ./configure + $ make + $ sudo make install + + +USING +----- + +The only file installed is the executable, "lnav". You can execute it +with no arguments to view the default set of files: + + $ lnav + +You can view all the syslog messages by running: + + $ lnav /var/log/messages* + + +SUPPORT +------- + +The lnav mailing list can be reached at: + + lnav@googlegroups.com + + +ACKNOWLEDGEMENTS +---------------- + +The xterm color database was copied from: + + https://jonasjacek.github.io/colors/ + + +SEE ALSO +-------- + +The lnav website: + + https://lnav.org diff --git a/README.md b/README.md new file mode 100644 index 0000000..5f1185a --- /dev/null +++ b/README.md @@ -0,0 +1,157 @@ + + +[![Build](https://github.com/tstack/lnav/workflows/ci-build/badge.svg)](https://github.com/tstack/lnav/actions?query=workflow%3Aci-build) +[![Docs](https://readthedocs.org/projects/lnav/badge/?version=latest&style=plastic)](https://docs.lnav.org) +[![Coverage Status](https://coveralls.io/repos/github/tstack/lnav/badge.svg?branch=master)](https://coveralls.io/github/tstack/lnav?branch=master) +[![lnav](https://snapcraft.io/lnav/badge.svg)](https://snapcraft.io/lnav) + +[](https://discord.gg/erBPnKwz7R) + +_This is the source repository for **lnav**, visit [https://lnav.org](https://lnav.org) for a high level overview._ + +# LNAV -- The Logfile Navigator + +The Log File Navigator, **lnav** for short, is an advanced log file viewer +for the small-scale. It is a terminal application that can understand +your log files and make it easy for you to find problems with little to +no setup. + +## Screenshot + +The following screenshot shows a syslog file. Log lines are displayed with +highlights. Errors are red and warnings are yellow. + +[![Screenshot](docs/assets/images/lnav-syslog-thumb.png)](docs/assets/images/lnav-syslog.png) + +## Features + +- Log messages from different files are collated together into a single view +- Automatic detection of log format +- Automatic decompression of GZip and BZip2 files +- Filter log messages based on regular expressions +- Use SQL to analyze your logs +- And more... + +## Installation + +[Download a statically-linked binary for Linux/MacOS from the release page](https://github.com/tstack/lnav/releases/latest#release-artifacts) + +## Usage + +The only file installed is the executable, `lnav`. You can execute it +with no arguments to view the default set of files: + +``` +$ lnav +``` + +You can view all the syslog messages by running: + +``` +$ lnav /var/log/messages* +``` + +### Usage with `systemd-journald` + +On systems running `systemd-journald`, you can use `lnav` as the pager: + +``` +$ journalctl | lnav +``` + +or in follow mode: + +``` +$ journalctl -f | lnav +``` + +Since `journalctl`'s default output format omits the year, if you are +viewing logs which span multiple years you will need to change the +output format to include the year, otherwise `lnav` gets confused: + +``` +$ journalctl -o short-iso | lnav +``` + +It is also possible to use `journalctl`'s json output format and `lnav` +will make use of additional fields such as PRIORITY and \_SYSTEMD_UNIT: + +``` +$ journalctl -o json | lnav +``` + +In case some MESSAGE fields contain special characters such as +ANSI color codes which are considered as unprintable by journalctl, +specifying `journalctl`'s `-a` option might be preferable in order +to output those messages still in a non-binary representation: + +``` +$ journalctl -a -o json | lnav +``` + +If using systemd v236 or newer, the output fields can be limited to +the ones actually recognized by `lnav` for increased efficiency: + +``` +$ journalctl -o json --output-fields=MESSAGE,PRIORITY,_PID,SYSLOG_IDENTIFIER,_SYSTEMD_UNIT | lnav +``` + +If your system has been running for a long time, for increased +efficiency you may want to limit the number of log lines fed into +`lnav`, e.g. via `journalctl`'s `-n` or `--since=...` options. + +In case of a persistent journal, you may want to limit the number +of log lines fed into `lnav` via `journalctl`'s `-b` option. + +## Support + +Please file issues on this repository or use the discussions section. +The following alternatives are also available: + +- [support@lnav.org](mailto:support@lnav.org) +- [Discord](https://discord.gg/erBPnKwz7R) +- [Google Groups](https://groups.google.com/g/lnav) + +## Links + +- [Main Site](https://lnav.org) +- [**Documentation**](https://docs.lnav.org) on Read the Docs +- [Internal Architecture](ARCHITECTURE.md) + +## Contributing + +- [Become a Sponsor on GitHub](https://github.com/sponsors/tstack) + +### Building From Source + +#### Prerequisites + +The following software packages are required to build lnav: + +- gcc/clang - A C++14-compatible compiler. +- libpcre2 - The Perl Compatible Regular Expression v2 (PCRE2) library. +- sqlite - The SQLite database engine. Version 3.9.0 or higher is required. +- ncurses - The ncurses text UI library. +- readline - The readline line editing library. +- zlib - The zlib compression library. +- bz2 - The bzip2 compression library. +- libcurl - The cURL library for downloading files from URLs. Version 7.23.0 or higher is required. +- libarchive - The libarchive library for opening archive files, like zip/tgz. +- wireshark - The 'tshark' program is used to interpret pcap files. + +#### Build + +Lnav follows the usual GNU style for configuring and installing software: + +Run `./autogen.sh` if compiling from a cloned repository. + +```console +$ ./configure +$ make +$ sudo make install +``` + +## See Also + +[Angle-grinder](https://github.com/rcoh/angle-grinder) is a tool to slice and dice log files on the command-line. +If you're familiar with the SumoLogic query language, you might find this tool more comfortable to work with. diff --git a/TESTS_ENVIRONMENT.in b/TESTS_ENVIRONMENT.in new file mode 100644 index 0000000..775c59a --- /dev/null +++ b/TESTS_ENVIRONMENT.in @@ -0,0 +1,275 @@ +#! /bin/bash + +top_srcdir="@abssrcdir@" +export top_srcdir + +top_srcdir_parent=`dirname ${top_srcdir}` +export top_srcdir_parent + +srcdir="@abssrcdir@/test" +export srcdir + +# The top build directory, derived from the path to this script. +top_builddir=`dirname $0` +export top_builddir + +builddir=`pwd -P` +export builddir + +test_dir="@abssrcdir@/test" +export test_dir + +# Let the tests know whether bzip is supported or not. +BZIP2_SUPPORT="@BZIP2_SUPPORT@" +export BZIP2_SUPPORT + +BZIP2_CMD="@BZIP2_CMD@" +export BZIP2_CMD + +XZ_CMD="@XZ_CMD@" +export XZ_CMD + +TSHARK_CMD="@TSHARK_CMD@" +export TSHARK_CMD + +LIBARCHIVE_LIBS="@LIBARCHIVE_LIBS@" +export LIBARCHIVE_LIBS + +HOME="${top_builddir}/test" +export HOME + +# The full path of the test case +test_file=$1 +# The base name of the test case +test_file_base=`basename $1` +# The current test number for shell based tests. +test_num=0 + +test_hash="" + +lnav_test="${top_builddir}/src/lnav-test" +export lnav_test + +lnav="${top_builddir}/src/lnav" +export lnav + +LNAV_LOG_PATH="${top_builddir}/test/test.log" +export LNAV_LOG_PATH + +SFTP_TEST_URL="@SFTP_TEST_URL@" +export SFTP_TEST_URL + +HAVE_SQLITE3_VALUE_SUBTYPE="@HAVE_SQLITE3_VALUE_SUBTYPE@" +export HAVE_SQLITE3_VALUE_SUBTYPE + +HAVE_SQLITE3_ERROR_OFFSET="@HAVE_SQLITE3_ERROR_OFFSET@" +export HAVE_SQLITE3_ERROR_OFFSET + +## BEGIN Functions + +LAST_TEST="" + +LAST_CAP_TEST=() + +has_errors="" + +# +# Run a test case and capture its standard out and standard err. +# +# Usage: run_test [ ...] +# +# Example: +# +# To run rktimes and capture all of its stdio output: +# +# run_test rktimes -V +# +run_test() { + printf "%s \033[0;35m=============================================================\033[0m\n" $(date -Iseconds) + LAST_TEST=("test: $@") + echo "${LAST_TEST[@]}" + export test_num=`expr ${test_num} \+ 1` + "$@" > ${test_file_base}_${test_num}.tmp 2> ${test_file_base}_${test_num}.err +} + +run_cap_test() { + LAST_CAP_TEST=("test: $@") + local full_cmd=$(echo "${LAST_CAP_TEST[@]}" | sed -e "s;${test_dir};{test_dir};g" -e "s;${top_srcdir};{top_srcdir};g") + export test_hash=$(echo "${full_cmd}" | shasum | cut -f 1 -d ' ') + echo "${full_cmd}" > ${test_file_base}_${test_hash}.cmd + "$@" > ${test_file_base}_${test_hash}.out 2> ${test_file_base}_${test_hash}.err + + sed -ibak \ + -e "s;${test_dir};{test_dir};g" \ + -e "s;${builddir};{test_dir};g" \ + -e "s;${top_srcdir};{top_srcdir};g" \ + -e "s;${top_srcdir_parent};{top_srcdir_parent};g" \ + ${test_file_base}_${test_hash}.out + echo + printf "%s \033[0;35m=============================================================\033[0m\n" $(date -Iseconds) + printf '\033[0;35mCommand\033[0m: %s\n' "${full_cmd}" + printf '\033[0;32mBEGIN\033[0m %s\n' "${test_file_base}_${test_hash}.out" + cat "${test_file_base}_${test_hash}.out" + printf '\033[0;32mEND\033[0m %s\n' "${test_file_base}_${test_hash}.out" + if test -f ${srcdir}/expected/${test_file_base}_${test_hash}.out; then + diff -w -u \ + ${srcdir}/expected/${test_file_base}_${test_hash}.out \ + ${test_file_base}_${test_hash}.out \ + > ${test_file_base}_${test_hash}.diff + if test $? -ne 0; then + echo OUT: "${full_cmd}" + cat ${test_file_base}_${test_hash}.diff + echo "FAIL! EXPECTED OUT DIFF" + export has_errors="yes" + fi + else + echo "FAIL! EXPECTED OUT MISSING -- ${srcdir}/expected/${test_file_base}_${test_hash}.out" + export has_errors="yes" + fi + + sed -ibak -E \ + -e "s;${test_dir};{test_dir};g" \ + -e "s;${builddir};{builddir};g" \ + -e "s;${top_srcdir};{top_srcdir};g" \ + -e 's;"errorId":".+";;g' \ + ${test_file_base}_${test_hash}.err + printf '\033[0;31mBEGIN\033[0m %s\n' "${test_file_base}_${test_hash}.err" + cat "${test_file_base}_${test_hash}.err" + printf '\033[0;31mEND\033[0m %s\n' "${test_file_base}_${test_hash}.err" + if test -f ${srcdir}/expected/${test_file_base}_${test_hash}.err; then + diff -w -u ${srcdir}/expected/${test_file_base}_${test_hash}.err \ + ${test_file_base}_${test_hash}.err \ + > ${test_file_base}_${test_hash}.err.diff + if test $? -ne 0; then + echo ERR: "${full_cmd}" + cat ${test_file_base}_${test_hash}.err.diff + echo "FAIL! EXPECTED ERR DIFF" + export has_errors="yes" + fi + else + echo "FAIL! EXPECTED ERR MISSING" + export has_errors="yes" + fi +} + +# +# Check the output generated by a run_test() call. +# +# Usage: check_output {Expected output on stdin} +# +# Example: +# +# To check the output of 'rktimes -V' and print out 'Unable to get version?' +# if the output doesn't match: +# +# run_test rktimes -V +# check_output "Unable to get version?" < ${test_file_base}_${test_num}.diff + if test $? -ne 0; then + echo "${LAST_TEST[@]}" + echo $1 + cat ${test_file_base}_${test_num}.diff + exit 1 + fi +} + +check_output_ws() { + diff -u - ${test_file_base}_${test_num}.tmp > ${test_file_base}_${test_num}.diff + if test $? -ne 0; then + echo "${LAST_TEST[@]}" + echo $1 + cat ${test_file_base}_${test_num}.diff + exit 1 + fi +} + +test_filename() { + echo ${test_file_base}_${test_num}.tmp +} + +test_err_filename() { + echo ${test_file_base}_${test_num}.err +} + +check_error_output() { + sed -ibak \ + -e "s;${test_dir};{test_dir};g" \ + -e "s;${top_srcdir};{top_srcdir};g" \ + ${test_file_base}_${test_num}.err + diff -w -u - ${test_file_base}_${test_num}.err \ + > ${test_file_base}_${test_num}.err.diff + if test $? -ne 0; then + echo "${LAST_TEST[@]}" + echo $1 + cat ${test_file_base}_${test_num}.err.diff + exit 1 + fi +} + +# +# Grep for a string in the output generated by a run_test() call. +# +# Usage: grep_output_for +# +# Example: +# +# To check the output of 'cbhey -l' for 'IDL:Foobar:1.0' and print out +# 'Unable to list supported interfaces?' if it is not found: +# +# run_test cbhey -l +# grep_output_for "IDL:Foobar:1.0" "Unable to list supported interface?" +# +grep_output_for() { + if grep -q $1 ${test_file_base}_${test_num}.tmp > /dev/null 2>&1; then + : + else + echo "${test_file_base}_${test_num}.tmp: $2" + exit 1 + fi +} + +on_error_log() { + if test $? -ne 0; then + echo $1 > /dev/stderr + cat ${test_file_base}_${test_num}.tmp + cat ${test_file_base}_${test_num}.err + fi +} + +on_error_fail_with() { + if test $? -ne 0; then + echo $1 > /dev/stderr + cat ${test_file_base}_${test_num}.tmp + cat ${test_file_base}_${test_num}.err + exit 1 + fi +} + +## END Functions + + +# Finally, run the test... + +if test -x $1 && test `basename $1 .sh` == `basename $1`; then + exec $* +else + # Shell script + shift + . ${test_file} +fi + +cleanup() { + if test "${has_errors}"x = "yes"x; then + exit 1 + fi +} + +trap "cleanup" EXIT diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 0000000..4b22031 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,19 @@ +version: "{build}" +image: Visual Studio 2022 + +environment: + matrix: + - cygwin: cygwin + cygsetup: setup-x86.exe + - cygwin: cygwin64 + cygsetup: setup-x86_64.exe + +install: + - C:\%cygwin%\%cygsetup% -qnNdOX -R C:/%cygwin% -l C:/%cygwin%/var/cache/setup -P libpcre2-devel -P libncurses-devel -P libreadline-devel -P zlib-devel -P libbz2-devel -P libsqlite3-devel -P libcurl-devel -P libarchive-devel + +build_script: + - C:\%cygwin%\bin\sh -lc "uname -a && gcc --version && cd /cygdrive/c/projects/lnav && ./autogen.sh && ./configure && make && strip src/lnav.exe && ldd src/lnav.exe" + +artifacts: + - path: src\lnav.exe + name: lnav.exe diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..c235230 --- /dev/null +++ b/autogen.sh @@ -0,0 +1,26 @@ +#! /bin/sh + + +if test x"${AUTORECONF}" = x""; then + autoreconf -V 1>/dev/null 2>/dev/null + if test $? -eq 0; then + AUTORECONF=autoreconf + fi +fi + +if test x"${AUTORECONF}" != x""; then + ${AUTORECONF} -vfi -I m4 +else + AUTOCONF=${AUTOCONF:-autoconf} + AUTOMAKE=${AUTOMAKE:-automake} + AUTOHEADER=${AUTOHEADER:-autoheader} + ACLOCAL=${ACLOCAL:-aclocal} + + ${AUTOCONF} --version + ${AUTOMAKE} --version + + ${ACLOCAL} -I m4 -I . + ${AUTOHEADER} -I . + ${AUTOMAKE} --add-missing --copy --force-missing --foreign + ${AUTOCONF} +fi diff --git a/cleanup_expected.sh b/cleanup_expected.sh new file mode 100755 index 0000000..5be90be --- /dev/null +++ b/cleanup_expected.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +srcdir="$1" +builddir="$2" + +for fname in "${srcdir}"/expected/*.out; do + stem=$(basename "$fname" | sed -e 's/.out$//') + + if ! test -f "${builddir}/$stem.cmd"; then + echo "removing $fname" + guilt rm "$fname" + echo "removing ${srcdir}/expected/${stem}.err" + guilt rm "${srcdir}/expected/${stem}.err" + fi +done diff --git a/cmake/CodeCoverage.cmake b/cmake/CodeCoverage.cmake new file mode 100644 index 0000000..2f54ee0 --- /dev/null +++ b/cmake/CodeCoverage.cmake @@ -0,0 +1,438 @@ +# Copyright (c) 2012 - 2017, Lars Bilke +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors +# may be used to endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# CHANGES: +# +# 2012-01-31, Lars Bilke +# - Enable Code Coverage +# +# 2013-09-17, Joakim Söderberg +# - Added support for Clang. +# - Some additional usage instructions. +# +# 2016-02-03, Lars Bilke +# - Refactored functions to use named parameters +# +# 2017-06-02, Lars Bilke +# - Merged with modified version from github.com/ufz/ogs +# +# 2019-05-06, Anatolii Kurotych +# - Remove unnecessary --coverage flag +# +# 2019-12-13, FeRD (Frank Dana) +# - Deprecate COVERAGE_LCOVR_EXCLUDES and COVERAGE_GCOVR_EXCLUDES lists in favor +# of tool-agnostic COVERAGE_EXCLUDES variable, or EXCLUDE setup arguments. +# - CMake 3.4+: All excludes can be specified relative to BASE_DIRECTORY +# - All setup functions: accept BASE_DIRECTORY, EXCLUDE list +# - Set lcov basedir with -b argument +# - Add automatic --demangle-cpp in lcovr, if 'c++filt' is available (can be +# overridden with NO_DEMANGLE option in setup_target_for_coverage_lcovr().) +# - Delete output dir, .info file on 'make clean' +# - Remove Python detection, since version mismatches will break gcovr +# - Minor cleanup (lowercase function names, update examples...) +# +# 2019-12-19, FeRD (Frank Dana) +# - Rename Lcov outputs, make filtered file canonical, fix cleanup for targets +# +# 2020-01-19, Bob Apthorpe +# - Added gfortran support +# +# 2020-02-17, FeRD (Frank Dana) +# - Make all add_custom_target()s VERBATIM to auto-escape wildcard characters +# in EXCLUDEs, and remove manual escaping from gcovr targets +# +# USAGE: +# +# 1. Copy this file into your cmake modules path. +# +# 2. Add the following line to your CMakeLists.txt (best inside an if-condition +# using a CMake option() to enable it just optionally): +# include(CodeCoverage) +# +# 3. Append necessary compiler flags: +# append_coverage_compiler_flags() +# +# 3.a (OPTIONAL) Set appropriate optimization flags, e.g. -O0, -O1 or -Og +# +# 4. If you need to exclude additional directories from the report, specify them +# using full paths in the COVERAGE_EXCLUDES variable before calling +# setup_target_for_coverage_*(). +# Example: +# set(COVERAGE_EXCLUDES +# '${PROJECT_SOURCE_DIR}/src/dir1/*' +# '/path/to/my/src/dir2/*') +# Or, use the EXCLUDE argument to setup_target_for_coverage_*(). +# Example: +# setup_target_for_coverage_lcov( +# NAME coverage +# EXECUTABLE testrunner +# EXCLUDE "${PROJECT_SOURCE_DIR}/src/dir1/*" "/path/to/my/src/dir2/*") +# +# 4.a NOTE: With CMake 3.4+, COVERAGE_EXCLUDES or EXCLUDE can also be set +# relative to the BASE_DIRECTORY (default: PROJECT_SOURCE_DIR) +# Example: +# set(COVERAGE_EXCLUDES "dir1/*") +# setup_target_for_coverage_gcovr_html( +# NAME coverage +# EXECUTABLE testrunner +# BASE_DIRECTORY "${PROJECT_SOURCE_DIR}/src" +# EXCLUDE "dir2/*") +# +# 5. Use the functions described below to create a custom make target which +# runs your test executable and produces a code coverage report. +# +# 6. Build a Debug build: +# cmake -DCMAKE_BUILD_TYPE=Debug .. +# make +# make my_coverage_target +# + +include(CMakeParseArguments) + +# Check prereqs +find_program( GCOV_PATH gcov ) +find_program( LCOV_PATH NAMES lcov lcov.bat lcov.exe lcov.perl) +find_program( GENHTML_PATH NAMES genhtml genhtml.perl genhtml.bat ) +find_program( GCOVR_PATH gcovr PATHS ${CMAKE_SOURCE_DIR}/scripts/test) +find_program( CPPFILT_PATH NAMES c++filt ) + +if(NOT GCOV_PATH) + message(FATAL_ERROR "gcov not found! Aborting...") +endif() # NOT GCOV_PATH + +message("foo ${CMAKE_CXX_COMPILER_ID}") + +if("${CMAKE_CXX_COMPILER_ID}" MATCHES "(Apple)?[Cc]lang") + if("${CMAKE_CXX_COMPILER_VERSION}" VERSION_LESS 3) + message(FATAL_ERROR "Clang version must be 3.0.0 or greater! Aborting...") + endif() +elseif(NOT CMAKE_COMPILER_IS_GNUCXX) + if("${CMAKE_Fortran_COMPILER_ID}" MATCHES "[Ff]lang") + # Do nothing; exit conditional without error if true + elseif("${CMAKE_Fortran_COMPILER_ID}" MATCHES "GNU") + # Do nothing; exit conditional without error if true + else() + message(FATAL_ERROR "Compiler is not GNU gcc! Aborting...") + endif() +endif() + +set(COVERAGE_COMPILER_FLAGS "-g -fprofile-arcs -ftest-coverage" + CACHE INTERNAL "") + +set(CMAKE_Fortran_FLAGS_COVERAGE + ${COVERAGE_COMPILER_FLAGS} + CACHE STRING "Flags used by the Fortran compiler during coverage builds." + FORCE ) +set(CMAKE_CXX_FLAGS_COVERAGE + ${COVERAGE_COMPILER_FLAGS} + CACHE STRING "Flags used by the C++ compiler during coverage builds." + FORCE ) +set(CMAKE_C_FLAGS_COVERAGE + ${COVERAGE_COMPILER_FLAGS} + CACHE STRING "Flags used by the C compiler during coverage builds." + FORCE ) +set(CMAKE_EXE_LINKER_FLAGS_COVERAGE + "" + CACHE STRING "Flags used for linking binaries during coverage builds." + FORCE ) +set(CMAKE_SHARED_LINKER_FLAGS_COVERAGE + "" + CACHE STRING "Flags used by the shared libraries linker during coverage builds." + FORCE ) +mark_as_advanced( + CMAKE_Fortran_FLAGS_COVERAGE + CMAKE_CXX_FLAGS_COVERAGE + CMAKE_C_FLAGS_COVERAGE + CMAKE_EXE_LINKER_FLAGS_COVERAGE + CMAKE_SHARED_LINKER_FLAGS_COVERAGE ) + +if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug") + message(WARNING "Code coverage results with an optimised (non-Debug) build may be misleading") +endif() # NOT CMAKE_BUILD_TYPE STREQUAL "Debug" + +if(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_Fortran_COMPILER_ID STREQUAL "GNU") + link_libraries(gcov) +endif() + +# Defines a target for running and collection code coverage information +# Builds dependencies, runs the given executable and outputs reports. +# NOTE! The executable should always have a ZERO as exit code otherwise +# the coverage generation will not complete. +# +# setup_target_for_coverage_lcov( +# NAME testrunner_coverage # New target name +# EXECUTABLE testrunner -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR +# DEPENDENCIES testrunner # Dependencies to build first +# BASE_DIRECTORY "../" # Base directory for report +# # (defaults to PROJECT_SOURCE_DIR) +# EXCLUDE "src/dir1/*" "src/dir2/*" # Patterns to exclude (can be relative +# # to BASE_DIRECTORY, with CMake 3.4+) +# NO_DEMANGLE # Don't demangle C++ symbols +# # even if c++filt is found +# ) +function(setup_target_for_coverage_lcov) + + set(options NO_DEMANGLE) + set(oneValueArgs BASE_DIRECTORY NAME) + set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES LCOV_ARGS GENHTML_ARGS) + cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if(NOT LCOV_PATH) + message(FATAL_ERROR "lcov not found! Aborting...") + endif() # NOT LCOV_PATH + + if(NOT GENHTML_PATH) + message(FATAL_ERROR "genhtml not found! Aborting...") + endif() # NOT GENHTML_PATH + + # Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR + if(${Coverage_BASE_DIRECTORY}) + get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE) + else() + set(BASEDIR ${PROJECT_SOURCE_DIR}) + endif() + + # Collect excludes (CMake 3.4+: Also compute absolute paths) + set(LCOV_EXCLUDES "") + foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_LCOV_EXCLUDES}) + if(CMAKE_VERSION VERSION_GREATER 3.4) + get_filename_component(EXCLUDE ${EXCLUDE} ABSOLUTE BASE_DIR ${BASEDIR}) + endif() + list(APPEND LCOV_EXCLUDES "${EXCLUDE}") + endforeach() + list(REMOVE_DUPLICATES LCOV_EXCLUDES) + + # Conditional arguments + if(CPPFILT_PATH AND NOT ${Coverage_NO_DEMANGLE}) + set(GENHTML_EXTRA_ARGS "--demangle-cpp") + endif() + + # Setup target + add_custom_target(${Coverage_NAME} + + # Cleanup lcov + COMMAND ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} -directory . -b ${BASEDIR} --zerocounters + # Create baseline to make sure untouched files show up in the report + COMMAND ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} -c -i -d . -b ${BASEDIR} -o ${Coverage_NAME}.base + + # Run tests + COMMAND ${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS} + + # Capturing lcov counters and generating report + COMMAND ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} --directory . -b ${BASEDIR} --capture --output-file ${Coverage_NAME}.capture + # add baseline counters + COMMAND ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} -a ${Coverage_NAME}.base -a ${Coverage_NAME}.capture --output-file ${Coverage_NAME}.total + # filter collected data to final coverage report + COMMAND ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} --remove ${Coverage_NAME}.total ${LCOV_EXCLUDES} --output-file ${Coverage_NAME}.info + + # Generate HTML output + COMMAND ${GENHTML_PATH} ${GENHTML_EXTRA_ARGS} ${Coverage_GENHTML_ARGS} -o ${Coverage_NAME} ${Coverage_NAME}.info + + # Set output files as GENERATED (will be removed on 'make clean') + BYPRODUCTS + ${Coverage_NAME}.base + ${Coverage_NAME}.capture + ${Coverage_NAME}.total + ${Coverage_NAME}.info + ${Coverage_NAME} # report directory + + WORKING_DIRECTORY ${PROJECT_BINARY_DIR} + DEPENDS ${Coverage_DEPENDENCIES} + VERBATIM # Protect arguments to commands + COMMENT "Resetting code coverage counters to zero.\nProcessing code coverage counters and generating report." + ) + + # Show where to find the lcov info report + add_custom_command(TARGET ${Coverage_NAME} POST_BUILD + COMMAND ; + COMMENT "Lcov code coverage info report saved in ${Coverage_NAME}.info." + ) + + # Show info where to find the report + add_custom_command(TARGET ${Coverage_NAME} POST_BUILD + COMMAND ; + COMMENT "Open ./${Coverage_NAME}/index.html in your browser to view the coverage report." + ) + +endfunction() # setup_target_for_coverage_lcov + +# Defines a target for running and collection code coverage information +# Builds dependencies, runs the given executable and outputs reports. +# NOTE! The executable should always have a ZERO as exit code otherwise +# the coverage generation will not complete. +# +# setup_target_for_coverage_gcovr_xml( +# NAME ctest_coverage # New target name +# EXECUTABLE ctest -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR +# DEPENDENCIES executable_target # Dependencies to build first +# BASE_DIRECTORY "../" # Base directory for report +# # (defaults to PROJECT_SOURCE_DIR) +# EXCLUDE "src/dir1/*" "src/dir2/*" # Patterns to exclude (can be relative +# # to BASE_DIRECTORY, with CMake 3.4+) +# ) +function(setup_target_for_coverage_gcovr_xml) + + set(options NONE) + set(oneValueArgs BASE_DIRECTORY NAME) + set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES) + cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if(NOT GCOVR_PATH) + message(FATAL_ERROR "gcovr not found! Aborting...") + endif() # NOT GCOVR_PATH + + # Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR + if(${Coverage_BASE_DIRECTORY}) + get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE) + else() + set(BASEDIR ${PROJECT_SOURCE_DIR}) + endif() + + # Collect excludes (CMake 3.4+: Also compute absolute paths) + set(GCOVR_EXCLUDES "") + foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_GCOVR_EXCLUDES}) + if(CMAKE_VERSION VERSION_GREATER 3.4) + get_filename_component(EXCLUDE ${EXCLUDE} ABSOLUTE BASE_DIR ${BASEDIR}) + endif() + list(APPEND GCOVR_EXCLUDES "${EXCLUDE}") + endforeach() + list(REMOVE_DUPLICATES GCOVR_EXCLUDES) + + # Combine excludes to several -e arguments + set(GCOVR_EXCLUDE_ARGS "") + foreach(EXCLUDE ${GCOVR_EXCLUDES}) + list(APPEND GCOVR_EXCLUDE_ARGS "-e") + list(APPEND GCOVR_EXCLUDE_ARGS "${EXCLUDE}") + endforeach() + + add_custom_target(${Coverage_NAME} + # Run tests + ${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS} + + # Running gcovr + COMMAND ${GCOVR_PATH} --xml + -r ${BASEDIR} ${GCOVR_EXCLUDE_ARGS} + --object-directory=${PROJECT_BINARY_DIR} + -o ${Coverage_NAME}.xml + BYPRODUCTS ${Coverage_NAME}.xml + WORKING_DIRECTORY ${PROJECT_BINARY_DIR} + DEPENDS ${Coverage_DEPENDENCIES} + VERBATIM # Protect arguments to commands + COMMENT "Running gcovr to produce Cobertura code coverage report." + ) + + # Show info where to find the report + add_custom_command(TARGET ${Coverage_NAME} POST_BUILD + COMMAND ; + COMMENT "Cobertura code coverage report saved in ${Coverage_NAME}.xml." + ) +endfunction() # setup_target_for_coverage_gcovr_xml + +# Defines a target for running and collection code coverage information +# Builds dependencies, runs the given executable and outputs reports. +# NOTE! The executable should always have a ZERO as exit code otherwise +# the coverage generation will not complete. +# +# setup_target_for_coverage_gcovr_html( +# NAME ctest_coverage # New target name +# EXECUTABLE ctest -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR +# DEPENDENCIES executable_target # Dependencies to build first +# BASE_DIRECTORY "../" # Base directory for report +# # (defaults to PROJECT_SOURCE_DIR) +# EXCLUDE "src/dir1/*" "src/dir2/*" # Patterns to exclude (can be relative +# # to BASE_DIRECTORY, with CMake 3.4+) +# ) +function(setup_target_for_coverage_gcovr_html) + + set(options NONE) + set(oneValueArgs BASE_DIRECTORY NAME) + set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES) + cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if(NOT GCOVR_PATH) + message(FATAL_ERROR "gcovr not found! Aborting...") + endif() # NOT GCOVR_PATH + + # Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR + if(${Coverage_BASE_DIRECTORY}) + get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE) + else() + set(BASEDIR ${PROJECT_SOURCE_DIR}) + endif() + + # Collect excludes (CMake 3.4+: Also compute absolute paths) + set(GCOVR_EXCLUDES "") + foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_GCOVR_EXCLUDES}) + if(CMAKE_VERSION VERSION_GREATER 3.4) + get_filename_component(EXCLUDE ${EXCLUDE} ABSOLUTE BASE_DIR ${BASEDIR}) + endif() + list(APPEND GCOVR_EXCLUDES "${EXCLUDE}") + endforeach() + list(REMOVE_DUPLICATES GCOVR_EXCLUDES) + + # Combine excludes to several -e arguments + set(GCOVR_EXCLUDE_ARGS "") + foreach(EXCLUDE ${GCOVR_EXCLUDES}) + list(APPEND GCOVR_EXCLUDE_ARGS "-e") + list(APPEND GCOVR_EXCLUDE_ARGS "${EXCLUDE}") + endforeach() + + add_custom_target(${Coverage_NAME} + # Run tests + ${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS} + + # Create folder + COMMAND ${CMAKE_COMMAND} -E make_directory ${PROJECT_BINARY_DIR}/${Coverage_NAME} + + # Running gcovr + COMMAND ${GCOVR_PATH} --html --html-details + -r ${BASEDIR} ${GCOVR_EXCLUDE_ARGS} + --object-directory=${PROJECT_BINARY_DIR} + -o ${Coverage_NAME}/index.html + + BYPRODUCTS ${PROJECT_BINARY_DIR}/${Coverage_NAME} # report directory + WORKING_DIRECTORY ${PROJECT_BINARY_DIR} + DEPENDS ${Coverage_DEPENDENCIES} + VERBATIM # Protect arguments to commands + COMMENT "Running gcovr to produce HTML code coverage report." + ) + + # Show info where to find the report + add_custom_command(TARGET ${Coverage_NAME} POST_BUILD + COMMAND ; + COMMENT "Open ./${Coverage_NAME}/index.html in your browser to view the coverage report." + ) + +endfunction() # setup_target_for_coverage_gcovr_html + +function(append_coverage_compiler_flags) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE) + set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE) + message(STATUS "Appending code coverage compiler flags: ${COVERAGE_COMPILER_FLAGS}") +endfunction() # append_coverage_compiler_flags diff --git a/cmake/Hunter/config.cmake b/cmake/Hunter/config.cmake new file mode 100644 index 0000000..f731b2d --- /dev/null +++ b/cmake/Hunter/config.cmake @@ -0,0 +1,14 @@ + +hunter_config( + libpcre + VERSION 8.41 + CMAKE_ARGS + EXTRA_FLAGS=--enable-unicode-properties --enable-jit --enable-utf +) + +hunter_config( + readline + VERSION 6.3 + CMAKE_ARGS + EXTRA_FLAGS=CFLAGS=-Wno-implicit-function-declaration +) diff --git a/cmake/HunterGate.cmake b/cmake/HunterGate.cmake new file mode 100644 index 0000000..e78d3e8 --- /dev/null +++ b/cmake/HunterGate.cmake @@ -0,0 +1,528 @@ +# Copyright (c) 2013-2019, Ruslan Baratov +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +# This is a gate file to Hunter package manager. +# Include this file using `include` command and add package you need, example: +# +# cmake_minimum_required(VERSION 3.2) +# +# include("cmake/HunterGate.cmake") +# HunterGate( +# URL "https://github.com/path/to/hunter/archive.tar.gz" +# SHA1 "798501e983f14b28b10cda16afa4de69eee1da1d" +# ) +# +# project(MyProject) +# +# hunter_add_package(Foo) +# hunter_add_package(Boo COMPONENTS Bar Baz) +# +# Projects: +# * https://github.com/hunter-packages/gate/ +# * https://github.com/ruslo/hunter + +option(HUNTER_ENABLED "Enable Hunter package manager support" ON) + +if(HUNTER_ENABLED) + if(CMAKE_VERSION VERSION_LESS "3.2") + message( + FATAL_ERROR + "At least CMake version 3.2 required for Hunter dependency management." + " Update CMake or set HUNTER_ENABLED to OFF." + ) + endif() +endif() + +include(CMakeParseArguments) # cmake_parse_arguments + +option(HUNTER_STATUS_PRINT "Print working status" ON) +option(HUNTER_STATUS_DEBUG "Print a lot info" OFF) +option(HUNTER_TLS_VERIFY "Enable/disable TLS certificate checking on downloads" ON) + +set(HUNTER_ERROR_PAGE "https://docs.hunter.sh/en/latest/reference/errors") + +function(hunter_gate_status_print) + if(HUNTER_STATUS_PRINT OR HUNTER_STATUS_DEBUG) + foreach(print_message ${ARGV}) + message(STATUS "[hunter] ${print_message}") + endforeach() + endif() +endfunction() + +function(hunter_gate_status_debug) + if(HUNTER_STATUS_DEBUG) + foreach(print_message ${ARGV}) + string(TIMESTAMP timestamp) + message(STATUS "[hunter *** DEBUG *** ${timestamp}] ${print_message}") + endforeach() + endif() +endfunction() + +function(hunter_gate_error_page error_page) + message("------------------------------ ERROR ------------------------------") + message(" ${HUNTER_ERROR_PAGE}/${error_page}.html") + message("-------------------------------------------------------------------") + message("") + message(FATAL_ERROR "") +endfunction() + +function(hunter_gate_internal_error) + message("") + foreach(print_message ${ARGV}) + message("[hunter ** INTERNAL **] ${print_message}") + endforeach() + message("[hunter ** INTERNAL **] [Directory:${CMAKE_CURRENT_LIST_DIR}]") + message("") + hunter_gate_error_page("error.internal") +endfunction() + +function(hunter_gate_fatal_error) + cmake_parse_arguments(hunter "" "ERROR_PAGE" "" "${ARGV}") + if("${hunter_ERROR_PAGE}" STREQUAL "") + hunter_gate_internal_error("Expected ERROR_PAGE") + endif() + message("") + foreach(x ${hunter_UNPARSED_ARGUMENTS}) + message("[hunter ** FATAL ERROR **] ${x}") + endforeach() + message("[hunter ** FATAL ERROR **] [Directory:${CMAKE_CURRENT_LIST_DIR}]") + message("") + hunter_gate_error_page("${hunter_ERROR_PAGE}") +endfunction() + +function(hunter_gate_user_error) + hunter_gate_fatal_error(${ARGV} ERROR_PAGE "error.incorrect.input.data") +endfunction() + +function(hunter_gate_self root version sha1 result) + string(COMPARE EQUAL "${root}" "" is_bad) + if(is_bad) + hunter_gate_internal_error("root is empty") + endif() + + string(COMPARE EQUAL "${version}" "" is_bad) + if(is_bad) + hunter_gate_internal_error("version is empty") + endif() + + string(COMPARE EQUAL "${sha1}" "" is_bad) + if(is_bad) + hunter_gate_internal_error("sha1 is empty") + endif() + + string(SUBSTRING "${sha1}" 0 7 archive_id) + + set( + hunter_self + "${root}/_Base/Download/Hunter/${version}/${archive_id}/Unpacked" + ) + + set("${result}" "${hunter_self}" PARENT_SCOPE) +endfunction() + +# Set HUNTER_GATE_ROOT cmake variable to suitable value. +function(hunter_gate_detect_root) + # Check CMake variable + string(COMPARE NOTEQUAL "${HUNTER_ROOT}" "" not_empty) + if(not_empty) + set(HUNTER_GATE_ROOT "${HUNTER_ROOT}" PARENT_SCOPE) + hunter_gate_status_debug("HUNTER_ROOT detected by cmake variable") + return() + endif() + + # Check environment variable + string(COMPARE NOTEQUAL "$ENV{HUNTER_ROOT}" "" not_empty) + if(not_empty) + set(HUNTER_GATE_ROOT "$ENV{HUNTER_ROOT}" PARENT_SCOPE) + hunter_gate_status_debug("HUNTER_ROOT detected by environment variable") + return() + endif() + + # Check HOME environment variable + string(COMPARE NOTEQUAL "$ENV{HOME}" "" result) + if(result) + set(HUNTER_GATE_ROOT "$ENV{HOME}/.hunter" PARENT_SCOPE) + hunter_gate_status_debug("HUNTER_ROOT set using HOME environment variable") + return() + endif() + + # Check SYSTEMDRIVE and USERPROFILE environment variable (windows only) + if(WIN32) + string(COMPARE NOTEQUAL "$ENV{SYSTEMDRIVE}" "" result) + if(result) + set(HUNTER_GATE_ROOT "$ENV{SYSTEMDRIVE}/.hunter" PARENT_SCOPE) + hunter_gate_status_debug( + "HUNTER_ROOT set using SYSTEMDRIVE environment variable" + ) + return() + endif() + + string(COMPARE NOTEQUAL "$ENV{USERPROFILE}" "" result) + if(result) + set(HUNTER_GATE_ROOT "$ENV{USERPROFILE}/.hunter" PARENT_SCOPE) + hunter_gate_status_debug( + "HUNTER_ROOT set using USERPROFILE environment variable" + ) + return() + endif() + endif() + + hunter_gate_fatal_error( + "Can't detect HUNTER_ROOT" + ERROR_PAGE "error.detect.hunter.root" + ) +endfunction() + +function(hunter_gate_download dir) + string( + COMPARE + NOTEQUAL + "$ENV{HUNTER_DISABLE_AUTOINSTALL}" + "" + disable_autoinstall + ) + if(disable_autoinstall AND NOT HUNTER_RUN_INSTALL) + hunter_gate_fatal_error( + "Hunter not found in '${dir}'" + "Set HUNTER_RUN_INSTALL=ON to auto-install it from '${HUNTER_GATE_URL}'" + "Settings:" + " HUNTER_ROOT: ${HUNTER_GATE_ROOT}" + " HUNTER_SHA1: ${HUNTER_GATE_SHA1}" + ERROR_PAGE "error.run.install" + ) + endif() + string(COMPARE EQUAL "${dir}" "" is_bad) + if(is_bad) + hunter_gate_internal_error("Empty 'dir' argument") + endif() + + string(COMPARE EQUAL "${HUNTER_GATE_SHA1}" "" is_bad) + if(is_bad) + hunter_gate_internal_error("HUNTER_GATE_SHA1 empty") + endif() + + string(COMPARE EQUAL "${HUNTER_GATE_URL}" "" is_bad) + if(is_bad) + hunter_gate_internal_error("HUNTER_GATE_URL empty") + endif() + + set(done_location "${dir}/DONE") + set(sha1_location "${dir}/SHA1") + + set(build_dir "${dir}/Build") + set(cmakelists "${dir}/CMakeLists.txt") + + hunter_gate_status_debug("Locking directory: ${dir}") + file(LOCK "${dir}" DIRECTORY GUARD FUNCTION) + hunter_gate_status_debug("Lock done") + + if(EXISTS "${done_location}") + # while waiting for lock other instance can do all the job + hunter_gate_status_debug("File '${done_location}' found, skip install") + return() + endif() + + file(REMOVE_RECURSE "${build_dir}") + file(REMOVE_RECURSE "${cmakelists}") + + file(MAKE_DIRECTORY "${build_dir}") # check directory permissions + + # Disabling languages speeds up a little bit, reduces noise in the output + # and avoids path too long windows error + file( + WRITE + "${cmakelists}" + "cmake_minimum_required(VERSION 3.2)\n" + "project(HunterDownload LANGUAGES NONE)\n" + "include(ExternalProject)\n" + "ExternalProject_Add(\n" + " Hunter\n" + " URL\n" + " \"${HUNTER_GATE_URL}\"\n" + " URL_HASH\n" + " SHA1=${HUNTER_GATE_SHA1}\n" + " DOWNLOAD_DIR\n" + " \"${dir}\"\n" + " TLS_VERIFY\n" + " ${HUNTER_TLS_VERIFY}\n" + " SOURCE_DIR\n" + " \"${dir}/Unpacked\"\n" + " CONFIGURE_COMMAND\n" + " \"\"\n" + " BUILD_COMMAND\n" + " \"\"\n" + " INSTALL_COMMAND\n" + " \"\"\n" + ")\n" + ) + + if(HUNTER_STATUS_DEBUG) + set(logging_params "") + else() + set(logging_params OUTPUT_QUIET) + endif() + + hunter_gate_status_debug("Run generate") + + # Need to add toolchain file too. + # Otherwise on Visual Studio + MDD this will fail with error: + # "Could not find an appropriate version of the Windows 10 SDK installed on this machine" + if(EXISTS "${CMAKE_TOOLCHAIN_FILE}") + get_filename_component(absolute_CMAKE_TOOLCHAIN_FILE "${CMAKE_TOOLCHAIN_FILE}" ABSOLUTE) + set(toolchain_arg "-DCMAKE_TOOLCHAIN_FILE=${absolute_CMAKE_TOOLCHAIN_FILE}") + else() + # 'toolchain_arg' can't be empty + set(toolchain_arg "-DCMAKE_TOOLCHAIN_FILE=") + endif() + + string(COMPARE EQUAL "${CMAKE_MAKE_PROGRAM}" "" no_make) + if(no_make) + set(make_arg "") + else() + # Test case: remove Ninja from PATH but set it via CMAKE_MAKE_PROGRAM + set(make_arg "-DCMAKE_MAKE_PROGRAM=${CMAKE_MAKE_PROGRAM}") + endif() + + execute_process( + COMMAND + "${CMAKE_COMMAND}" + "-H${dir}" + "-B${build_dir}" + "-G${CMAKE_GENERATOR}" + "${toolchain_arg}" + ${make_arg} + WORKING_DIRECTORY "${dir}" + RESULT_VARIABLE download_result + ${logging_params} + ) + + if(NOT download_result EQUAL 0) + hunter_gate_internal_error( + "Configure project failed." + "To reproduce the error run: ${CMAKE_COMMAND} -H${dir} -B${build_dir} -G${CMAKE_GENERATOR} ${toolchain_arg} ${make_arg}" + "In directory ${dir}" + ) + endif() + + hunter_gate_status_print( + "Initializing Hunter workspace (${HUNTER_GATE_SHA1})" + " ${HUNTER_GATE_URL}" + " -> ${dir}" + ) + execute_process( + COMMAND "${CMAKE_COMMAND}" --build "${build_dir}" + WORKING_DIRECTORY "${dir}" + RESULT_VARIABLE download_result + ${logging_params} + ) + + if(NOT download_result EQUAL 0) + hunter_gate_internal_error("Build project failed") + endif() + + file(REMOVE_RECURSE "${build_dir}") + file(REMOVE_RECURSE "${cmakelists}") + + file(WRITE "${sha1_location}" "${HUNTER_GATE_SHA1}") + file(WRITE "${done_location}" "DONE") + + hunter_gate_status_debug("Finished") +endfunction() + +# Must be a macro so master file 'cmake/Hunter' can +# apply all variables easily just by 'include' command +# (otherwise PARENT_SCOPE magic needed) +macro(HunterGate) + if(HUNTER_GATE_DONE) + # variable HUNTER_GATE_DONE set explicitly for external project + # (see `hunter_download`) + set_property(GLOBAL PROPERTY HUNTER_GATE_DONE YES) + endif() + + # First HunterGate command will init Hunter, others will be ignored + get_property(_hunter_gate_done GLOBAL PROPERTY HUNTER_GATE_DONE SET) + + if(NOT HUNTER_ENABLED) + # Empty function to avoid error "unknown function" + function(hunter_add_package) + endfunction() + + set( + _hunter_gate_disabled_mode_dir + "${CMAKE_CURRENT_LIST_DIR}/cmake/Hunter/disabled-mode" + ) + if(EXISTS "${_hunter_gate_disabled_mode_dir}") + hunter_gate_status_debug( + "Adding \"disabled-mode\" modules: ${_hunter_gate_disabled_mode_dir}" + ) + list(APPEND CMAKE_PREFIX_PATH "${_hunter_gate_disabled_mode_dir}") + endif() + elseif(_hunter_gate_done) + hunter_gate_status_debug("Secondary HunterGate (use old settings)") + hunter_gate_self( + "${HUNTER_CACHED_ROOT}" + "${HUNTER_VERSION}" + "${HUNTER_SHA1}" + _hunter_self + ) + include("${_hunter_self}/cmake/Hunter") + else() + set(HUNTER_GATE_LOCATION "${CMAKE_CURRENT_SOURCE_DIR}") + + string(COMPARE NOTEQUAL "${PROJECT_NAME}" "" _have_project_name) + if(_have_project_name) + hunter_gate_fatal_error( + "Please set HunterGate *before* 'project' command. " + "Detected project: ${PROJECT_NAME}" + ERROR_PAGE "error.huntergate.before.project" + ) + endif() + + cmake_parse_arguments( + HUNTER_GATE "LOCAL" "URL;SHA1;GLOBAL;FILEPATH" "" ${ARGV} + ) + + string(COMPARE EQUAL "${HUNTER_GATE_SHA1}" "" _empty_sha1) + string(COMPARE EQUAL "${HUNTER_GATE_URL}" "" _empty_url) + string( + COMPARE + NOTEQUAL + "${HUNTER_GATE_UNPARSED_ARGUMENTS}" + "" + _have_unparsed + ) + string(COMPARE NOTEQUAL "${HUNTER_GATE_GLOBAL}" "" _have_global) + string(COMPARE NOTEQUAL "${HUNTER_GATE_FILEPATH}" "" _have_filepath) + + if(_have_unparsed) + hunter_gate_user_error( + "HunterGate unparsed arguments: ${HUNTER_GATE_UNPARSED_ARGUMENTS}" + ) + endif() + if(_empty_sha1) + hunter_gate_user_error("SHA1 suboption of HunterGate is mandatory") + endif() + if(_empty_url) + hunter_gate_user_error("URL suboption of HunterGate is mandatory") + endif() + if(_have_global) + if(HUNTER_GATE_LOCAL) + hunter_gate_user_error("Unexpected LOCAL (already has GLOBAL)") + endif() + if(_have_filepath) + hunter_gate_user_error("Unexpected FILEPATH (already has GLOBAL)") + endif() + endif() + if(HUNTER_GATE_LOCAL) + if(_have_global) + hunter_gate_user_error("Unexpected GLOBAL (already has LOCAL)") + endif() + if(_have_filepath) + hunter_gate_user_error("Unexpected FILEPATH (already has LOCAL)") + endif() + endif() + if(_have_filepath) + if(_have_global) + hunter_gate_user_error("Unexpected GLOBAL (already has FILEPATH)") + endif() + if(HUNTER_GATE_LOCAL) + hunter_gate_user_error("Unexpected LOCAL (already has FILEPATH)") + endif() + endif() + + hunter_gate_detect_root() # set HUNTER_GATE_ROOT + + # Beautify path, fix probable problems with windows path slashes + get_filename_component( + HUNTER_GATE_ROOT "${HUNTER_GATE_ROOT}" ABSOLUTE + ) + hunter_gate_status_debug("HUNTER_ROOT: ${HUNTER_GATE_ROOT}") + if(NOT HUNTER_ALLOW_SPACES_IN_PATH) + string(FIND "${HUNTER_GATE_ROOT}" " " _contain_spaces) + if(NOT _contain_spaces EQUAL -1) + hunter_gate_fatal_error( + "HUNTER_ROOT (${HUNTER_GATE_ROOT}) contains spaces." + "Set HUNTER_ALLOW_SPACES_IN_PATH=ON to skip this error" + "(Use at your own risk!)" + ERROR_PAGE "error.spaces.in.hunter.root" + ) + endif() + endif() + + string( + REGEX + MATCH + "[0-9]+\\.[0-9]+\\.[0-9]+[-_a-z0-9]*" + HUNTER_GATE_VERSION + "${HUNTER_GATE_URL}" + ) + string(COMPARE EQUAL "${HUNTER_GATE_VERSION}" "" _is_empty) + if(_is_empty) + set(HUNTER_GATE_VERSION "unknown") + endif() + + hunter_gate_self( + "${HUNTER_GATE_ROOT}" + "${HUNTER_GATE_VERSION}" + "${HUNTER_GATE_SHA1}" + _hunter_self + ) + + set(_master_location "${_hunter_self}/cmake/Hunter") + get_filename_component(_archive_id_location "${_hunter_self}/.." ABSOLUTE) + set(_done_location "${_archive_id_location}/DONE") + set(_sha1_location "${_archive_id_location}/SHA1") + + # Check Hunter already downloaded by HunterGate + if(NOT EXISTS "${_done_location}") + hunter_gate_download("${_archive_id_location}") + endif() + + if(NOT EXISTS "${_done_location}") + hunter_gate_internal_error("hunter_gate_download failed") + endif() + + if(NOT EXISTS "${_sha1_location}") + hunter_gate_internal_error("${_sha1_location} not found") + endif() + file(READ "${_sha1_location}" _sha1_value) + string(COMPARE EQUAL "${_sha1_value}" "${HUNTER_GATE_SHA1}" _is_equal) + if(NOT _is_equal) + hunter_gate_internal_error( + "Short SHA1 collision:" + " ${_sha1_value} (from ${_sha1_location})" + " ${HUNTER_GATE_SHA1} (HunterGate)" + ) + endif() + if(NOT EXISTS "${_master_location}") + hunter_gate_user_error( + "Master file not found:" + " ${_master_location}" + "try to update Hunter/HunterGate" + ) + endif() + include("${_master_location}") + set_property(GLOBAL PROPERTY HUNTER_GATE_DONE YES) + endif() +endmacro() diff --git a/cmake/coverage.cmake b/cmake/coverage.cmake new file mode 100644 index 0000000..c89cc16 --- /dev/null +++ b/cmake/coverage.cmake @@ -0,0 +1,33 @@ +# ---- Variables ---- + +# We use variables separate from what CTest uses, because those have +# customization issues +set( + COVERAGE_TRACE_COMMAND + lcov -c -q + -o "${PROJECT_BINARY_DIR}/coverage.info" + -d "${PROJECT_BINARY_DIR}" + --include "${PROJECT_SOURCE_DIR}/*" + CACHE STRING + "; separated command to generate a trace for the 'coverage' target" +) + +set( + COVERAGE_HTML_COMMAND + genhtml --legend -f -q + "${PROJECT_BINARY_DIR}/coverage.info" + -p "${PROJECT_SOURCE_DIR}" + -o "${PROJECT_BINARY_DIR}/coverage_html" + CACHE STRING + "; separated command to generate an HTML report for the 'coverage' target" +) + +# ---- Coverage target ---- + +add_custom_target( + coverage + COMMAND ${COVERAGE_TRACE_COMMAND} + COMMAND ${COVERAGE_HTML_COMMAND} + COMMENT "Generating coverage report" + VERBATIM +) diff --git a/cmake/dev-mode.cmake b/cmake/dev-mode.cmake new file mode 100644 index 0000000..73a31fb --- /dev/null +++ b/cmake/dev-mode.cmake @@ -0,0 +1,32 @@ +include(cmake/folders.cmake) + +include(CTest) +if(BUILD_TESTING) + add_subdirectory(test) +endif() + +add_custom_target( + run-exe + COMMAND lnav + VERBATIM +) +add_dependencies(run-exe lnav) + +option(BUILD_MCSS_DOCS "Build documentation using Doxygen and m.css" OFF) +if(BUILD_MCSS_DOCS) + include(cmake/docs.cmake) +endif() + +option(ENABLE_COVERAGE "Enable coverage support separate from CTest's" OFF) +if(ENABLE_COVERAGE) + include(cmake/coverage.cmake) +endif() + +if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows") + include(cmake/open-cpp-coverage.cmake OPTIONAL) +endif() + +include(cmake/lint-targets.cmake) +include(cmake/spell-targets.cmake) + +add_folders(Project) diff --git a/cmake/docs-ci.cmake b/cmake/docs-ci.cmake new file mode 100644 index 0000000..ae7f0c7 --- /dev/null +++ b/cmake/docs-ci.cmake @@ -0,0 +1,112 @@ +cmake_minimum_required(VERSION 3.14) + +foreach(var IN ITEMS PROJECT_BINARY_DIR PROJECT_SOURCE_DIR) + if(NOT DEFINED "${var}") + message(FATAL_ERROR "${var} must be defined") + endif() +endforeach() +set(bin "${PROJECT_BINARY_DIR}") +set(src "${PROJECT_SOURCE_DIR}") + +# ---- Dependencies ---- + +set(mcss_SOURCE_DIR "${bin}/docs/.ci") +if(NOT IS_DIRECTORY "${mcss_SOURCE_DIR}") + file(MAKE_DIRECTORY "${mcss_SOURCE_DIR}") + file( + DOWNLOAD + https://github.com/friendlyanon/m.css/releases/download/release-1/mcss.zip + "${mcss_SOURCE_DIR}/mcss.zip" + STATUS status + EXPECTED_MD5 00cd2757ebafb9bcba7f5d399b3bec7f + ) + if(NOT status MATCHES "^0;") + message(FATAL_ERROR "Download failed with ${status}") + endif() + execute_process( + COMMAND "${CMAKE_COMMAND}" -E tar xf mcss.zip + WORKING_DIRECTORY "${mcss_SOURCE_DIR}" + RESULT_VARIABLE result + ) + if(NOT result EQUAL "0") + message(FATAL_ERROR "Extraction failed with ${result}") + endif() + file(REMOVE "${mcss_SOURCE_DIR}/mcss.zip") +endif() + +find_program(Python3_EXECUTABLE NAMES python3 python) +if(NOT Python3_EXECUTABLE) + message(FATAL_ERROR "Python executable was not found") +endif() + +# ---- Process project() call in CMakeLists.txt ---- + +file(READ "${src}/CMakeLists.txt" content) + +string(FIND "${content}" "project(" index) +if(index EQUAL "-1") + message(FATAL_ERROR "Could not find \"project(\"") +endif() +string(SUBSTRING "${content}" "${index}" -1 content) + +string(FIND "${content}" "\n)\n" index) +if(index EQUAL "-1") + message(FATAL_ERROR "Could not find \"\\n)\\n\"") +endif() +string(SUBSTRING "${content}" 0 "${index}" content) + +file(WRITE "${bin}/docs-ci.project.cmake" "docs_${content}\n)\n") + +macro(list_pop_front list out) + list(GET "${list}" 0 "${out}") + list(REMOVE_AT "${list}" 0) +endmacro() + +function(docs_project name) + cmake_parse_arguments(PARSE_ARGV 1 "" "" "VERSION;DESCRIPTION;HOMEPAGE_URL" LANGUAGES) + set(PROJECT_NAME "${name}" PARENT_SCOPE) + if(DEFINED _VERSION) + set(PROJECT_VERSION "${_VERSION}" PARENT_SCOPE) + string(REGEX MATCH "^[0-9]+(\\.[0-9]+)*" versions "${_VERSION}") + string(REPLACE . ";" versions "${versions}") + set(suffixes MAJOR MINOR PATCH TWEAK) + while(NOT versions STREQUAL "" AND NOT suffixes STREQUAL "") + list_pop_front(versions version) + list_pop_front(suffixes suffix) + set("PROJECT_VERSION_${suffix}" "${version}" PARENT_SCOPE) + endwhile() + endif() + if(DEFINED _DESCRIPTION) + set(PROJECT_DESCRIPTION "${_DESCRIPTION}" PARENT_SCOPE) + endif() + if(DEFINED _HOMEPAGE_URL) + set(PROJECT_HOMEPAGE_URL "${_HOMEPAGE_URL}" PARENT_SCOPE) + endif() +endfunction() + +include("${bin}/docs-ci.project.cmake") + +# ---- Generate docs ---- + +if(NOT DEFINED DOXYGEN_OUTPUT_DIRECTORY) + set(DOXYGEN_OUTPUT_DIRECTORY "${bin}/docs") +endif() +set(out "${DOXYGEN_OUTPUT_DIRECTORY}") + +foreach(file IN ITEMS Doxyfile conf.py) + configure_file("${src}/docs/${file}.in" "${bin}/docs/${file}" @ONLY) +endforeach() + +set(mcss_script "${mcss_SOURCE_DIR}/documentation/doxygen.py") +set(config "${bin}/docs/conf.py") + +file(REMOVE_RECURSE "${out}/html" "${out}/xml") + +execute_process( + COMMAND "${Python3_EXECUTABLE}" "${mcss_script}" "${config}" + WORKING_DIRECTORY "${bin}/docs" + RESULT_VARIABLE result +) +if(NOT result EQUAL "0") + message(FATAL_ERROR "m.css returned with ${result}") +endif() diff --git a/cmake/docs.cmake b/cmake/docs.cmake new file mode 100644 index 0000000..286d027 --- /dev/null +++ b/cmake/docs.cmake @@ -0,0 +1,46 @@ +# ---- Dependencies ---- + +set(extract_timestamps "") +if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.24") + set(extract_timestamps DOWNLOAD_EXTRACT_TIMESTAMP YES) +endif () + +include(FetchContent) +FetchContent_Declare( + mcss URL + https://github.com/friendlyanon/m.css/releases/download/release-1/mcss.zip + URL_MD5 00cd2757ebafb9bcba7f5d399b3bec7f + SOURCE_DIR "${PROJECT_BINARY_DIR}/mcss" + UPDATE_DISCONNECTED YES + ${extract_timestamps} +) +FetchContent_MakeAvailable(mcss) + +find_package(Python3 3.6 REQUIRED) + +# ---- Declare documentation target ---- + +set( + DOXYGEN_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/docs" + CACHE PATH "Path for the generated Doxygen documentation" +) + +set(working_dir "${PROJECT_BINARY_DIR}/docs") + +foreach (file IN ITEMS Doxyfile conf.py) + configure_file("docs/${file}.in" "${working_dir}/${file}" @ONLY) +endforeach () + +set(mcss_script "${mcss_SOURCE_DIR}/documentation/doxygen.py") +set(config "${working_dir}/conf.py") + +add_custom_target( + docs + COMMAND "${CMAKE_COMMAND}" -E remove_directory + "${DOXYGEN_OUTPUT_DIRECTORY}/html" + "${DOXYGEN_OUTPUT_DIRECTORY}/xml" + COMMAND "${Python3_EXECUTABLE}" "${mcss_script}" "${config}" + COMMENT "Building documentation using Doxygen and m.css" + WORKING_DIRECTORY "${working_dir}" + VERBATIM +) diff --git a/cmake/folders.cmake b/cmake/folders.cmake new file mode 100644 index 0000000..da7bd33 --- /dev/null +++ b/cmake/folders.cmake @@ -0,0 +1,21 @@ +set_property(GLOBAL PROPERTY USE_FOLDERS YES) + +# Call this function at the end of a directory scope to assign a folder to +# targets created in that directory. Utility targets will be assigned to the +# UtilityTargets folder, otherwise to the ${name}Targets folder. If a target +# already has a folder assigned, then that target will be skipped. +function(add_folders name) + get_property(targets DIRECTORY PROPERTY BUILDSYSTEM_TARGETS) + foreach(target IN LISTS targets) + get_property(folder TARGET "${target}" PROPERTY FOLDER) + if(DEFINED folder) + continue() + endif() + set(folder Utility) + get_property(type TARGET "${target}" PROPERTY TYPE) + if(NOT type STREQUAL "UTILITY") + set(folder "${name}") + endif() + set_property(TARGET "${target}" PROPERTY FOLDER "${folder}Targets") + endforeach() +endfunction() diff --git a/cmake/install-rules.cmake b/cmake/install-rules.cmake new file mode 100644 index 0000000..ed43e76 --- /dev/null +++ b/cmake/install-rules.cmake @@ -0,0 +1,44 @@ +include(CMakePackageConfigHelpers) +include(GNUInstallDirs) + +# find_package() call for consumers to find this project +set(package lnav) + +install( + TARGETS lnav + RUNTIME COMPONENT lnav_Runtime +) + +write_basic_package_version_file( + "${package}ConfigVersion.cmake" + COMPATIBILITY SameMajorVersion +) + +# Allow package maintainers to freely override the path for the configs +set( + lnav_INSTALL_CMAKEDIR "${CMAKE_INSTALL_DATADIR}/${package}" + CACHE PATH "CMake package config location relative to the install prefix" +) +mark_as_advanced(lnav_INSTALL_CMAKEDIR) + +install( + FILES "${PROJECT_BINARY_DIR}/${package}ConfigVersion.cmake" + DESTINATION "${lnav_INSTALL_CMAKEDIR}" + COMPONENT lnav_Development +) + +# Export variables for the install script to use +install(CODE " +set(lnav_NAME [[$]]) +set(lnav_INSTALL_CMAKEDIR [[${lnav_INSTALL_CMAKEDIR}]]) +set(CMAKE_INSTALL_BINDIR [[${CMAKE_INSTALL_BINDIR}]]) +" COMPONENT lnav_Development) + +install( + SCRIPT cmake/install-script.cmake + COMPONENT lnav_Development +) + +if(PROJECT_IS_TOP_LEVEL) + include(CPack) +endif() diff --git a/cmake/install-script.cmake b/cmake/install-script.cmake new file mode 100644 index 0000000..6bd0c6e --- /dev/null +++ b/cmake/install-script.cmake @@ -0,0 +1,18 @@ +file( + RELATIVE_PATH relative_path + "/${lnav_INSTALL_CMAKEDIR}" + "/${CMAKE_INSTALL_BINDIR}/${lnav_NAME}" +) + +get_filename_component(prefix "${CMAKE_INSTALL_PREFIX}" ABSOLUTE) +set(config_dir "${prefix}/${lnav_INSTALL_CMAKEDIR}") +set(config_file "${config_dir}/lnavConfig.cmake") + +message(STATUS "Installing: ${config_file}") +file(WRITE "${config_file}" "\ +set( + LNAV_EXECUTABLE + \"\${CMAKE_CURRENT_LIST_DIR}/${relative_path}\" + CACHE FILEPATH \"Path to the lnav executable\" +) +") diff --git a/cmake/lint-targets.cmake b/cmake/lint-targets.cmake new file mode 100644 index 0000000..1ffd8c0 --- /dev/null +++ b/cmake/lint-targets.cmake @@ -0,0 +1,32 @@ +set( + FORMAT_PATTERNS + src/*.cc src/*.hh + test/*.cc test/*.hh + CACHE STRING + "; separated patterns relative to the project source dir to format" +) + +set(FORMAT_COMMAND clang-format CACHE STRING "Formatter to use") + +add_custom_target( + format-check + COMMAND "${CMAKE_COMMAND}" + -D "FORMAT_COMMAND=${FORMAT_COMMAND}" + -D "PATTERNS=${FORMAT_PATTERNS}" + -P "${PROJECT_SOURCE_DIR}/cmake/lint.cmake" + WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}" + COMMENT "Linting the code" + VERBATIM +) + +add_custom_target( + format-fix + COMMAND "${CMAKE_COMMAND}" + -D "FORMAT_COMMAND=${FORMAT_COMMAND}" + -D "PATTERNS=${FORMAT_PATTERNS}" + -D FIX=YES + -P "${PROJECT_SOURCE_DIR}/cmake/lint.cmake" + WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}" + COMMENT "Fixing the code" + VERBATIM +) diff --git a/cmake/lint.cmake b/cmake/lint.cmake new file mode 100644 index 0000000..32bfdc4 --- /dev/null +++ b/cmake/lint.cmake @@ -0,0 +1,50 @@ +cmake_minimum_required(VERSION 3.14) + +macro(default name) + if(NOT DEFINED "${name}") + set("${name}" "${ARGN}") + endif() +endmacro() + +default(FORMAT_COMMAND clang-format) +default( + PATTERNS + src/*.cc src/*.hh + test/*.cc test/*.hh +) +default(FIX NO) + +set(flag --output-replacements-xml) +set(args OUTPUT_VARIABLE output) +if(FIX) + set(flag -i) + set(args "") +endif() + +file(GLOB_RECURSE files ${PATTERNS}) +set(badly_formatted "") +set(output "") +string(LENGTH "${CMAKE_SOURCE_DIR}/" path_prefix_length) + +foreach(file IN LISTS files) + execute_process( + COMMAND "${FORMAT_COMMAND}" --style=file "${flag}" "${file}" + WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" + RESULT_VARIABLE result + ${args} + ) + if(NOT result EQUAL "0") + message(FATAL_ERROR "'${file}': formatter returned with ${result}") + endif() + if(NOT FIX AND output MATCHES "\n/dev/null) : 'v\([[0-9]]*\.[[0-9]]*\.[[0-9]]*.*\)' 2>/dev/null) +cd $saved_location +AS_IF([test $? -eq 0], + [version=$(echo $version | tr -d '\n') + version=${version:-${PACKAGE_VERSION}} + AC_DEFINE_UNQUOTED([VCS_PACKAGE_STRING], ["$PACKAGE_NAME $version"], + [VCS package string])], + AC_DEFINE_UNQUOTED([VCS_PACKAGE_STRING], ["$PACKAGE_STRING"], [VCS package string])) + +AM_CONDITIONAL(USE_INCLUDED_YAJL, test $HAVE_LOCAL_YAJL -eq 0) +AM_CONDITIONAL(HAVE_LIBCURL, test x"$LIBCURL" != x"") +AM_CONDITIONAL([DISABLE_DOCUMENTATION], [ test x"$cross_compiling" != x"no" ]) + +USER_CXXFLAGS="${CXXFLAGS}" +AC_SUBST(USER_CXXFLAGS) + +AC_CONFIG_HEADERS([src/config.h]) +AC_CONFIG_FILES([Makefile]) +AC_CONFIG_FILES([TESTS_ENVIRONMENT]) +AC_CONFIG_FILES([tools/Makefile]) +AC_CONFIG_FILES([src/Makefile]) +AC_CONFIG_FILES([src/base/Makefile]) +AC_CONFIG_FILES([src/formats/logfmt/Makefile]) +AC_CONFIG_FILES([src/fmtlib/Makefile]) +AC_CONFIG_FILES([src/pcrepp/Makefile]) +AC_CONFIG_FILES([src/pugixml/Makefile]) +AC_CONFIG_FILES([src/tailer/Makefile]) +AC_CONFIG_FILES([src/yajl/Makefile]) +AC_CONFIG_FILES([src/yajlpp/Makefile]) +AC_CONFIG_FILES([src/third-party/base64/lib/Makefile]) +AC_CONFIG_FILES([src/third-party/scnlib/src/Makefile]) +AC_CONFIG_FILES([test/Makefile]) + +AC_OUTPUT diff --git a/demo/Dockerfile b/demo/Dockerfile new file mode 100644 index 0000000..25ded84 --- /dev/null +++ b/demo/Dockerfile @@ -0,0 +1,44 @@ + +FROM debian:11.3-slim + +RUN set -eux; \ + export DEBIAN_FRONTEND=noninteractive; \ + apt update; \ + apt install --yes --no-install-recommends bind9-dnsutils iputils-ping iproute2 curl ca-certificates htop wget unzip openssh-server; \ + apt clean autoclean; \ + apt autoremove --yes; \ + rm -rf /var/lib/{apt,dpkg,cache,log}/; \ + echo "Installed base utils!" + +ADD https://github.com/tstack/lnav/releases/download/v0.11.0/lnav-0.11.0-musl-64bit.zip / +RUN unzip lnav-0.11.0-musl-64bit.zip + +COPY docs/tutorials tutorials +RUN gunzip /tutorials/playground/logs/*.gz + +RUN useradd -rm -d /home/playground -s /bin/bash playground +RUN echo 'playground:playground' | chpasswd +RUN passwd -d playground + +RUN useradd -rm -d /home/tutorial1 -s /bin/bash tutorial1 +RUN echo 'tutorial1:tutorial1' | chpasswd +RUN passwd -d tutorial1 + +USER playground +RUN /lnav-0.11.0/lnav -nN -c ":config /ui/theme monocai" + +USER tutorial1 +RUN /lnav-0.11.0/lnav -nN -c ":config /ui/theme monocai" + +USER root + +RUN echo 'Match User playground' >> /etc/ssh/sshd_config +RUN echo 'ForceCommand env PATH=/lnav-0.11.0:$PATH /tutorials/playground/run.sh' >> /etc/ssh/sshd_config +RUN echo 'PermitEmptyPasswords yes' >> /etc/ssh/sshd_config +RUN echo 'Match User tutorial1' >> /etc/ssh/sshd_config +RUN echo 'ForceCommand env PATH=/lnav-0.11.0:$PATH /tutorials/tutorial1/run.sh' >> /etc/ssh/sshd_config +RUN echo 'PermitEmptyPasswords yes' >> /etc/ssh/sshd_config +RUN service ssh start +EXPOSE 22 + +CMD ["/usr/sbin/sshd", "-D"] diff --git a/demo/fly.toml b/demo/fly.toml new file mode 100644 index 0000000..75affba --- /dev/null +++ b/demo/fly.toml @@ -0,0 +1,35 @@ +# fly.toml file generated for lnav-demo on 2022-08-22T10:26:28-07:00 + +app = "lnav-demo" +kill_signal = "SIGINT" +kill_timeout = 5 +processes = [] + +[env] + +[experimental] + allowed_public_ports = [] + auto_rollback = true + +[[services]] + http_checks = [] + internal_port = 22 + processes = ["app"] + protocol = "tcp" + script_checks = [] + [services.concurrency] + hard_limit = 25 + soft_limit = 20 + type = "connections" + + [[services.ports]] + port = 22 + + [[services.tcp_checks]] + grace_period = "1s" + interval = "15s" + restart_limit = 0 + timeout = "2s" + +[build] +image = "lnav-demo" diff --git a/demo/loggen.py b/demo/loggen.py new file mode 100755 index 0000000..6790a09 --- /dev/null +++ b/demo/loggen.py @@ -0,0 +1,54 @@ +import datetime +import os +import random +import shutil +import sys + +MSGS = [ + "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", + "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.", + "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.", + "Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", +] + +GLOG_DATE_FMT = "%Y%m%d %H:%M:%S" + +START_TIME = datetime.datetime.fromtimestamp(1490191111) + +try: + shutil.rmtree("/tmp/demo") + os.makedirs("/tmp/demo") +except OSError: + pass + +PIDS = [ + "123", + "123", + "123", + "121", + "124", + "123", + "61456", + "61456", + "61457", +] + +LOG_LOCS = [ + "demo.cc:123", + "demo.cc:352", + "loader.cc:13", + "loader.cc:552", + "blaster.cc:352", + "blaster.cc:112", + "blaster.cc:6782", +] + +CURR_TIME = START_TIME +for _index in range(0, int(sys.argv[1])): + CURR_TIME += datetime.timedelta(seconds=random.randrange(1, 22)) + print("I%s.%06d %s %s] %s" % ( + CURR_TIME.strftime(GLOG_DATE_FMT), + random.randrange(0, 100000), + random.choice(PIDS), + random.choice(LOG_LOCS), + random.choice(MSGS))) diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 0000000..f40fbd8 --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,5 @@ +_site +.sass-cache +.jekyll-cache +.jekyll-metadata +vendor diff --git a/docs/02_downloads.md b/docs/02_downloads.md new file mode 100644 index 0000000..9cc360e --- /dev/null +++ b/docs/02_downloads.md @@ -0,0 +1,58 @@ +--- +layout: page +title: Downloads +permalink: /downloads +--- + +The latest **stable** release is [**v{{ site.version }}**](https://github.com/tstack/lnav/releases/latest). + +The following options are available for installing **lnav**: + +## Linux + + +Download a [statically linked 64-bit binary](https://github.com/tstack/lnav/releases/download/v{{site.version}}/lnav-{{site.version}}-musl-64bit.zip). + +Install from the [Snap Store](https://snapcraft.io/lnav): + +```console +$ sudo snap install lnav +``` + +## MacOS + + +Download a [statically linked 64-bit binary](https://github.com/tstack/lnav/releases/download/v{{site.version}}/lnav-{{site.version}}-os-x.zip) + +Install using [Homebrew](https://formulae.brew.sh/formula/lnav): + +```console +$ brew install lnav +``` + +## Source + + +Download the [source](https://github.com/tstack/lnav/releases/download/v{{site.version}}/lnav-{{site.version}}.tar.gz) +and install any dependencies. The following commands will unpack the source +tar ball, configure the build for your system, build, and then install: + +```console +$ tar xvfz lnav-{{site.version}}.tar.gz +$ cd lnav-{{site.version}} +$ ./configure +$ make +$ make install +``` + +### GitHub + +If you would like to contribute to the development of lnav, visit our page on +[GitHub](https://github.com/tstack/lnav). + +# VSCode Extension + +The [lnav VSCode Extension](https://marketplace.visualstudio.com/items?itemName=lnav.lnav) +can be used to add syntax highlighting to lnav scripts. + +![Screenshot of an lnav script](/assets/images/lnav-vscode-extension.png) diff --git a/docs/03_features.md b/docs/03_features.md new file mode 100644 index 0000000..6ef52af --- /dev/null +++ b/docs/03_features.md @@ -0,0 +1,140 @@ +--- +layout: page +title: Features +permalink: /features +--- + +* TOC +{:toc} + +## Single Log View + +All log file contents are merged into a single view based on message timestamps. +You no longer need to manually correlate timestamps across multiple windows or +figure out the order in which to view rotated log files. The color bars on the +left-hand side help to show which file a message belongs to. + +![Screenshot of lnav showing messages from multiple files](/assets/images/lnav-multi-file2.png) + +## Automatic Log Format Detection + +The log message format is automatically determined by lnav while scanning your +files. The following formats are built in by default: + +* Common Web Access Log format +* CUPS page_log +* Syslog +* Glog +* VMware ESXi/vCenter Logs +* dpkg.log +* uwsgi +* "Generic" - Any message that starts with a timestamp +* Strace +* sudo + +GZIP'ed and BZIP2'ed files are also detected automatically and decompressed on-the-fly. + +## Filters + +Display only lines that match or do not match a set of regular expressions. +Useful for removing extraneous log lines that you are not interested in. + +## Timeline View + +The timeline view shows a histogram of messages over time. The number of +warnings and errors are highlighted in the display so that you can easily see +where problems have occurred. Once you have found a period of time that is of +interest, a key-press will take you back to the log message view at the +corresponding time. + +![Screenshot of timeline view](/assets/images/lnav-hist.png) + +## Pretty-Print View + +The pretty-print view will reformat structured data, like XML or JSON, so that +it is easier to read. Simply press SHIFT+P in the log view to have all the +currently displayed lines pretty-printed. + +The following screenshot shows an XML blob with no indentation: + +![A flat blob of XML](/assets/images/lnav-before-pretty.png) + +After pressing SHIFT+P, the XML is pretty-printed for easier viewing: + +![A pretty-printed blob of XML](/assets/images/lnav-after-pretty.png) + +## Query Logs Using SQL + +Log files are directly used as the backing for SQLite virtual tables. This +means you can perform queries on messages without having to load the data into +an SQL database. For example, the screenshot below shows the result of +running the following query against an Apache access_log file: + +```sql +SELECT c_ip, count(*), sum(sc_bytes) AS total FROM access_log + GROUP BY c_ip ORDER BY total DESC; +``` + +![The results of a SQL query](/assets/images/lnav-query.png) + +## "Live" Operation + +Searches are done as you type; new log lines are automatically loaded and +searched as they are added; filters apply to lines as they are loaded; and, SQL +queries are checked for correctness as you type. + +## Themes + +The UI can be [customized through themes](https://lnav.readthedocs.io/en/latest/config.html#theme-definitions). + +![Animation of the UI cycling through themes](/assets/images/lnav-theme-cycle.gif) + +## Syntax Highlighting + +Errors and warnings are colored in red and yellow, respectively. Highlights are +also applied to: SQL keywords, XML tags, file and line numbers in Java +backtraces, and quoted strings. The search and SQL query prompt are also +highlighted as you type, making it easier to see errors and matching brackets. + +![Animation of syntax highlighting](/assets/images/lnav-syntax-highlight.gif) + +## Tab-completion + +The command prompt supports tab-completion for almost all operations. For +example, when doing a search, you can tab-complete words that are displayed on +screen rather than having to do a copy & paste. + +![Animation of TAB-completion](/assets/images/lnav-tab-complete.gif) + +## Custom Keymaps + +[Hotkeys can be customized](https://lnav.readthedocs.io/en/latest/config.html#keymap-definitions) +to run lnav commands or scripts. + +## Sessions + +Session information is saved automatically and restored when you are viewing the +same set of files. The current location in files, bookmarks, and applied filters +are all saved as part of the session. + +## Headless Mode + +The log processing features of lnav can be used in scripts if you have a canned +set of operations or queries that you want to perform regularly. You can enable +headless mode with the '-n' switch on the command-line and then use the '-c' +flag to specify the commands or queries you want to execute. For example, to get +the top 10 client IP addresses from an apache access log file and write the +results to standard out in CSV format: + +```console +$ lnav -n \ + -c ';SELECT c_ip, count(*) AS total FROM access_log GROUP BY c_ip ORDER BY total DESC LIMIT 10' \ + -c ':write-csv-to -' \ + access.log + +c_ip,total +10.208.110.176,2989570 +10.178.4.102,11183 +10.32.110.197,2020 +10.29.165.250,443 +``` diff --git a/docs/04_tutorials.md b/docs/04_tutorials.md new file mode 100644 index 0000000..14b89f2 --- /dev/null +++ b/docs/04_tutorials.md @@ -0,0 +1,30 @@ +--- +layout: page +title: Tutorials +permalink: /tutorials +--- + +These tutorials are provided to help you learn how **lnav** works +without having to install anything. They are running on a shared +[fly.io](https://fly.io) instance, so please be kind. + +The tutorials are implemented using features in **lnav** and not +built in to the code itself. The tutorial text is +[markdown](https://docs.lnav.org/en/latest/ui.html#markdown), +the logic is written [SQL](https://docs.lnav.org/en/latest/sqlext.html), +and the reactions are triggered through +[events](https://docs.lnav.org/en/latest/events.html). + +The source for the tutorials can be found [here](https://github.com/tstack/lnav/tree/master/docs/tutorials). + +# Tutorial 1 + +
+

Learn how to navigate an example log file using lnav:

+ + +$ +ssh tutorial1@demo.lnav.org + +
+ diff --git a/docs/05_docs.md b/docs/05_docs.md new file mode 100644 index 0000000..3c19354 --- /dev/null +++ b/docs/05_docs.md @@ -0,0 +1,6 @@ +--- +title: Docs +permalink: /docs +redirect_to: + - https://docs.lnav.org +--- diff --git a/docs/06_changeblog.md b/docs/06_changeblog.md new file mode 100644 index 0000000..1f2bdc9 --- /dev/null +++ b/docs/06_changeblog.md @@ -0,0 +1,6 @@ +--- +title: ChangeBlog +layout: home +permalink: /blog/ +--- + diff --git a/docs/404.html b/docs/404.html new file mode 100644 index 0000000..086a5c9 --- /dev/null +++ b/docs/404.html @@ -0,0 +1,25 @@ +--- +permalink: /404.html +layout: default +--- + + + +
+

404

+ +

Page not found :(

+

The requested page could not be found.

+
diff --git a/docs/CNAME b/docs/CNAME new file mode 100644 index 0000000..330a24a --- /dev/null +++ b/docs/CNAME @@ -0,0 +1 @@ +lnav.org \ No newline at end of file diff --git a/docs/Doxyfile.in b/docs/Doxyfile.in new file mode 100644 index 0000000..a6da763 --- /dev/null +++ b/docs/Doxyfile.in @@ -0,0 +1,32 @@ +# Configuration for Doxygen for use with CMake +# Only options that deviate from the default are included +# To create a new Doxyfile containing all available options, call `doxygen -g` + +# Get Project name and version from CMake +PROJECT_NAME = "@PROJECT_NAME@" +PROJECT_NUMBER = "@PROJECT_VERSION@" + +# Add sources +INPUT = "@PROJECT_SOURCE_DIR@/README.md" "@PROJECT_SOURCE_DIR@/include" "@PROJECT_SOURCE_DIR@/source" "@PROJECT_SOURCE_DIR@/docs/pages" +EXTRACT_ALL = YES +RECURSIVE = YES +OUTPUT_DIRECTORY = "@DOXYGEN_OUTPUT_DIRECTORY@" + +# Use the README as a main page +USE_MDFILE_AS_MAINPAGE = "@PROJECT_SOURCE_DIR@/README.md" + +# set relative include paths +FULL_PATH_NAMES = YES +STRIP_FROM_PATH = "@PROJECT_SOURCE_DIR@/include" "@PROJECT_SOURCE_DIR@" +STRIP_FROM_INC_PATH = + +# We use m.css to generate the html documentation, so we only need XML output +GENERATE_XML = YES +GENERATE_HTML = NO +GENERATE_LATEX = NO +XML_PROGRAMLISTING = NO +CREATE_SUBDIRS = NO + +# Include all directories, files and namespaces in the documentation +# Disable to include only explicitly documented objects +M_SHOW_UNDOCUMENTED = YES diff --git a/docs/Gemfile b/docs/Gemfile new file mode 100644 index 0000000..10560d9 --- /dev/null +++ b/docs/Gemfile @@ -0,0 +1,31 @@ +source "https://rubygems.org" +# Hello! This is where you manage which Jekyll version is used to run. +# When you want to use a different version, change it below, save the +# file and run `bundle install`. Run Jekyll with `bundle exec`, like so: +# +# bundle exec jekyll serve +# +# This will help ensure the proper Jekyll version is running. +# Happy Jekylling! +# gem "jekyll", "~> 4.2.0" +# This is the default theme for new Jekyll sites. You may change this to anything you like. +gem "minima", "~> 2.5" +# If you want to use GitHub Pages, remove the "gem "jekyll"" above and +# uncomment the line below. To upgrade, run `bundle update github-pages`. +gem "github-pages", group: :jekyll_plugins +# If you have any plugins, put them here! +group :jekyll_plugins do +end + +# Windows and JRuby does not include zoneinfo files, so bundle the tzinfo-data gem +# and associated library. +platforms :mingw, :x64_mingw, :mswin, :jruby do + gem "tzinfo", "~> 1.2" + gem "tzinfo-data" +end + +# Performance-booster for watching directories on Windows +gem "wdm", "~> 0.1.1", :platforms => [:mingw, :x64_mingw, :mswin] + + +gem "webrick", "~> 1.7" diff --git a/docs/Gemfile.lock b/docs/Gemfile.lock new file mode 100644 index 0000000..f13c8d6 --- /dev/null +++ b/docs/Gemfile.lock @@ -0,0 +1,270 @@ +GEM + remote: https://rubygems.org/ + specs: + activesupport (6.0.5.1) + concurrent-ruby (~> 1.0, >= 1.0.2) + i18n (>= 0.7, < 2) + minitest (~> 5.1) + tzinfo (~> 1.1) + zeitwerk (~> 2.2, >= 2.2.2) + addressable (2.8.1) + public_suffix (>= 2.0.2, < 6.0) + coffee-script (2.4.1) + coffee-script-source + execjs + coffee-script-source (1.11.1) + colorator (1.1.0) + commonmarker (0.23.6) + concurrent-ruby (1.1.10) + dnsruby (1.61.9) + simpleidn (~> 0.1) + em-websocket (0.5.3) + eventmachine (>= 0.12.9) + http_parser.rb (~> 0) + ethon (0.15.0) + ffi (>= 1.15.0) + eventmachine (1.2.7) + execjs (2.8.1) + faraday (2.5.2) + faraday-net_http (>= 2.0, < 3.1) + ruby2_keywords (>= 0.0.4) + faraday-net_http (3.0.0) + ffi (1.15.5) + forwardable-extended (2.6.0) + gemoji (3.0.1) + github-pages (227) + github-pages-health-check (= 1.17.9) + jekyll (= 3.9.2) + jekyll-avatar (= 0.7.0) + jekyll-coffeescript (= 1.1.1) + jekyll-commonmark-ghpages (= 0.2.0) + jekyll-default-layout (= 0.1.4) + jekyll-feed (= 0.15.1) + jekyll-gist (= 1.5.0) + jekyll-github-metadata (= 2.13.0) + jekyll-include-cache (= 0.2.1) + jekyll-mentions (= 1.6.0) + jekyll-optional-front-matter (= 0.3.2) + jekyll-paginate (= 1.1.0) + jekyll-readme-index (= 0.3.0) + jekyll-redirect-from (= 0.16.0) + jekyll-relative-links (= 0.6.1) + jekyll-remote-theme (= 0.4.3) + jekyll-sass-converter (= 1.5.2) + jekyll-seo-tag (= 2.8.0) + jekyll-sitemap (= 1.4.0) + jekyll-swiss (= 1.0.0) + jekyll-theme-architect (= 0.2.0) + jekyll-theme-cayman (= 0.2.0) + jekyll-theme-dinky (= 0.2.0) + jekyll-theme-hacker (= 0.2.0) + jekyll-theme-leap-day (= 0.2.0) + jekyll-theme-merlot (= 0.2.0) + jekyll-theme-midnight (= 0.2.0) + jekyll-theme-minimal (= 0.2.0) + jekyll-theme-modernist (= 0.2.0) + jekyll-theme-primer (= 0.6.0) + jekyll-theme-slate (= 0.2.0) + jekyll-theme-tactile (= 0.2.0) + jekyll-theme-time-machine (= 0.2.0) + jekyll-titles-from-headings (= 0.5.3) + jemoji (= 0.12.0) + kramdown (= 2.3.2) + kramdown-parser-gfm (= 1.1.0) + liquid (= 4.0.3) + mercenary (~> 0.3) + minima (= 2.5.1) + nokogiri (>= 1.13.6, < 2.0) + rouge (= 3.26.0) + terminal-table (~> 1.4) + github-pages-health-check (1.17.9) + addressable (~> 2.3) + dnsruby (~> 1.60) + octokit (~> 4.0) + public_suffix (>= 3.0, < 5.0) + typhoeus (~> 1.3) + html-pipeline (2.14.2) + activesupport (>= 2) + nokogiri (>= 1.4) + http_parser.rb (0.8.0) + i18n (0.9.5) + concurrent-ruby (~> 1.0) + jekyll (3.9.2) + addressable (~> 2.4) + colorator (~> 1.0) + em-websocket (~> 0.5) + i18n (~> 0.7) + jekyll-sass-converter (~> 1.0) + jekyll-watch (~> 2.0) + kramdown (>= 1.17, < 3) + liquid (~> 4.0) + mercenary (~> 0.3.3) + pathutil (~> 0.9) + rouge (>= 1.7, < 4) + safe_yaml (~> 1.0) + jekyll-avatar (0.7.0) + jekyll (>= 3.0, < 5.0) + jekyll-coffeescript (1.1.1) + coffee-script (~> 2.2) + coffee-script-source (~> 1.11.1) + jekyll-commonmark (1.4.0) + commonmarker (~> 0.22) + jekyll-commonmark-ghpages (0.2.0) + commonmarker (~> 0.23.4) + jekyll (~> 3.9.0) + jekyll-commonmark (~> 1.4.0) + rouge (>= 2.0, < 4.0) + jekyll-default-layout (0.1.4) + jekyll (~> 3.0) + jekyll-feed (0.15.1) + jekyll (>= 3.7, < 5.0) + jekyll-gist (1.5.0) + octokit (~> 4.2) + jekyll-github-metadata (2.13.0) + jekyll (>= 3.4, < 5.0) + octokit (~> 4.0, != 4.4.0) + jekyll-include-cache (0.2.1) + jekyll (>= 3.7, < 5.0) + jekyll-mentions (1.6.0) + html-pipeline (~> 2.3) + jekyll (>= 3.7, < 5.0) + jekyll-optional-front-matter (0.3.2) + jekyll (>= 3.0, < 5.0) + jekyll-paginate (1.1.0) + jekyll-readme-index (0.3.0) + jekyll (>= 3.0, < 5.0) + jekyll-redirect-from (0.16.0) + jekyll (>= 3.3, < 5.0) + jekyll-relative-links (0.6.1) + jekyll (>= 3.3, < 5.0) + jekyll-remote-theme (0.4.3) + addressable (~> 2.0) + jekyll (>= 3.5, < 5.0) + jekyll-sass-converter (>= 1.0, <= 3.0.0, != 2.0.0) + rubyzip (>= 1.3.0, < 3.0) + jekyll-sass-converter (1.5.2) + sass (~> 3.4) + jekyll-seo-tag (2.8.0) + jekyll (>= 3.8, < 5.0) + jekyll-sitemap (1.4.0) + jekyll (>= 3.7, < 5.0) + jekyll-swiss (1.0.0) + jekyll-theme-architect (0.2.0) + jekyll (> 3.5, < 5.0) + jekyll-seo-tag (~> 2.0) + jekyll-theme-cayman (0.2.0) + jekyll (> 3.5, < 5.0) + jekyll-seo-tag (~> 2.0) + jekyll-theme-dinky (0.2.0) + jekyll (> 3.5, < 5.0) + jekyll-seo-tag (~> 2.0) + jekyll-theme-hacker (0.2.0) + jekyll (> 3.5, < 5.0) + jekyll-seo-tag (~> 2.0) + jekyll-theme-leap-day (0.2.0) + jekyll (> 3.5, < 5.0) + jekyll-seo-tag (~> 2.0) + jekyll-theme-merlot (0.2.0) + jekyll (> 3.5, < 5.0) + jekyll-seo-tag (~> 2.0) + jekyll-theme-midnight (0.2.0) + jekyll (> 3.5, < 5.0) + jekyll-seo-tag (~> 2.0) + jekyll-theme-minimal (0.2.0) + jekyll (> 3.5, < 5.0) + jekyll-seo-tag (~> 2.0) + jekyll-theme-modernist (0.2.0) + jekyll (> 3.5, < 5.0) + jekyll-seo-tag (~> 2.0) + jekyll-theme-primer (0.6.0) + jekyll (> 3.5, < 5.0) + jekyll-github-metadata (~> 2.9) + jekyll-seo-tag (~> 2.0) + jekyll-theme-slate (0.2.0) + jekyll (> 3.5, < 5.0) + jekyll-seo-tag (~> 2.0) + jekyll-theme-tactile (0.2.0) + jekyll (> 3.5, < 5.0) + jekyll-seo-tag (~> 2.0) + jekyll-theme-time-machine (0.2.0) + jekyll (> 3.5, < 5.0) + jekyll-seo-tag (~> 2.0) + jekyll-titles-from-headings (0.5.3) + jekyll (>= 3.3, < 5.0) + jekyll-watch (2.2.1) + listen (~> 3.0) + jemoji (0.12.0) + gemoji (~> 3.0) + html-pipeline (~> 2.2) + jekyll (>= 3.0, < 5.0) + kramdown (2.3.2) + rexml + kramdown-parser-gfm (1.1.0) + kramdown (~> 2.0) + liquid (4.0.3) + listen (3.7.1) + rb-fsevent (~> 0.10, >= 0.10.3) + rb-inotify (~> 0.9, >= 0.9.10) + mercenary (0.3.6) + minima (2.5.1) + jekyll (>= 3.5, < 5.0) + jekyll-feed (~> 0.9) + jekyll-seo-tag (~> 2.1) + minitest (5.16.3) + nokogiri (1.13.8-arm64-darwin) + racc (~> 1.4) + nokogiri (1.13.8-x86_64-linux) + racc (~> 1.4) + octokit (4.25.1) + faraday (>= 1, < 3) + sawyer (~> 0.9) + pathutil (0.16.2) + forwardable-extended (~> 2.6) + public_suffix (4.0.7) + racc (1.6.0) + rb-fsevent (0.11.2) + rb-inotify (0.10.1) + ffi (~> 1.0) + rexml (3.2.5) + rouge (3.26.0) + ruby2_keywords (0.0.5) + rubyzip (2.3.2) + safe_yaml (1.0.5) + sass (3.7.4) + sass-listen (~> 4.0.0) + sass-listen (4.0.0) + rb-fsevent (~> 0.9, >= 0.9.4) + rb-inotify (~> 0.9, >= 0.9.7) + sawyer (0.9.2) + addressable (>= 2.3.5) + faraday (>= 0.17.3, < 3) + simpleidn (0.2.1) + unf (~> 0.1.4) + terminal-table (1.8.0) + unicode-display_width (~> 1.1, >= 1.1.1) + thread_safe (0.3.6) + typhoeus (1.4.0) + ethon (>= 0.9.0) + tzinfo (1.2.10) + thread_safe (~> 0.1) + unf (0.1.4) + unf_ext + unf_ext (0.0.8.2) + unicode-display_width (1.8.0) + webrick (1.7.0) + zeitwerk (2.6.0) + +PLATFORMS + arm64-darwin-21 + x86_64-linux + +DEPENDENCIES + github-pages + minima (~> 2.5) + tzinfo (~> 1.2) + tzinfo-data + wdm (~> 0.1.1) + webrick (~> 1.7) + +BUNDLED WITH + 2.3.20 diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..b16b62c --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,153 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = build + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source +# the i18n builder cannot share the environment and doctrees with the others +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source + +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext + +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " texinfo to make Texinfo files" + @echo " info to make Texinfo files and run them through makeinfo" + @echo " gettext to make PO message catalogs" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + +clean: + -rm -rf $(BUILDDIR)/* + +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/lnav.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/lnav.qhc" + +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/lnav" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/lnav" + @echo "# devhelp" + +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +texinfo: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo + @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." + @echo "Run \`make' in that directory to run these through makeinfo" \ + "(use \`make info' here to do that automatically)." + +info: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo "Running Texinfo files through makeinfo..." + make -C $(BUILDDIR)/texinfo info + @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." + +gettext: + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale + @echo + @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..c93d111 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,4 @@ +# Docs + +This directory contains the ReST documentation that is published to +[docs.lnav.org](https://docs.lnav.org) diff --git a/docs/_config.yml b/docs/_config.yml new file mode 100644 index 0000000..75cf684 --- /dev/null +++ b/docs/_config.yml @@ -0,0 +1,62 @@ +# Welcome to Jekyll! +# +# This config file is meant for settings that affect your whole blog, values +# which you are expected to set up once and rarely edit after that. If you find +# yourself editing this file very often, consider using Jekyll's data files +# feature for the data you need to update frequently. +# +# For technical reasons, this file is *NOT* reloaded automatically when you use +# 'bundle exec jekyll serve'. If you change this file, please restart the server process. +# +# If you need help with YAML syntax, here are some quick references for you: +# https://learn-the-web.algonquindesign.ca/topics/markdown-yaml-cheat-sheet/#yaml +# https://learnxinyminutes.com/docs/yaml/ +# +# Site settings +# These are used to personalize your new site. If you look in the HTML files, +# you will see them accessed via {{ site.title }}, {{ site.email }}, and so on. +# You can create any custom variable you would like, and they will be accessible +# in the templates via {{ site.myvariable }}. + +title: The Logfile Navigator +version: 0.11.0 +email: support@lnav.org +description: >- # this means to ignore newlines until "baseurl:" + The Logfile Navigator, lnav for short, is an advanced log file viewer + for the small-scale. +# baseurl: /lnav +# url: "http://lnav.org" # the base hostname & protocol for your site, e.g. http://example.com +twitter_username: lnavapp +github: tstack/lnav + +# Build settings +theme: minima +plugins: + - jekyll-feed + - jekyll-redirect-from + +show_excerpts: true + +exclude: + - source + - tutorials + +# Exclude from processing. +# The following items will not be processed, by default. +# Any item listed under the `exclude:` key here will be automatically added to +# the internal "default list". +# +# Excluded items can be processed by explicitly listing the directories or +# their entries' file path in the `include:` list. +# +# exclude: +# - .sass-cache/ +# - .jekyll-cache/ +# - gemfiles/ +# - Gemfile +# - Gemfile.lock +# - node_modules/ +# - vendor/bundle/ +# - vendor/cache/ +# - vendor/gems/ +# - vendor/ruby/ diff --git a/docs/_includes/social.html b/docs/_includes/social.html new file mode 100644 index 0000000..15672c0 --- /dev/null +++ b/docs/_includes/social.html @@ -0,0 +1,121 @@ + + + diff --git a/docs/_layouts/top.html b/docs/_layouts/top.html new file mode 100644 index 0000000..75be485 --- /dev/null +++ b/docs/_layouts/top.html @@ -0,0 +1,78 @@ + + + +{%- include head.html -%} + + + + + + + + + + Fork me on GitHub + + +{%- include header.html -%} + +
+ {{ content }} +
+ +{%- include footer.html -%} + + + + diff --git a/docs/_posts/2013-09-10-json-encoded-logs.md b/docs/_posts/2013-09-10-json-encoded-logs.md new file mode 100644 index 0000000..1fa36a4 --- /dev/null +++ b/docs/_posts/2013-09-10-json-encoded-logs.md @@ -0,0 +1,56 @@ +--- +layout: post +title: "Support for JSON-encoded logs in v0.6.1" +date: 2013-09-10 00:00:00 +excerpt: Turning JSON barf into something readable. +--- + +Making logs easily digestible by machines is becoming a concern as tools like +elasticsearch become more popular. One of the popular strategies is to encode +the whole log message in JSON and then write that as a single line to a file. +For example: + +```json +{"time": "2013-09-04T23:55:09.274041Z", "level" : "INFO", "body" : "Hello, World!" } +{"time": "2013-09-04T23:56:00.285224Z", "level" : "ERROR", "body" : "Something terrible has happened!", "tb": " foo.c:12\n bar.y:33" } +``` + +Unfortunately, what is good for a machine is not so great for a human. To try to +improve the situation, the latest release of lnav includes support for parsing +JSON log messages and transforming them on-the-fly. The display format is +specified in a log format configuration and can specify which fields should be +displayed on the main message line. Any unused fields that are found in the +message will be displayed below the main field so you don't miss anything. + +The above log lines can be transformed using the following format configuration: + +```json +{ + "json_ex_log": { + "title": "Example JSON Log", + "description": "An example log format configuration for JSON logs", + "json": true, + "file-pattern": "test-log\\.json.*", + "level-field": "level", + "line-format": [ + { + "field": "time" + }, + " ", + { + "field": "body" + } + ] + } +} +``` + +After copying this config to `~/.lnav/formats/test/format.json`, the log messages +will look like this when viewed in lnav: + +``` +2013-09-04T23:55:09.274041Z Hello, World! +2013-09-04T23:56:00.285224Z Something terrible has happened! + tb: foo.c:12 + tb: bar.y:33 +``` diff --git a/docs/_posts/2013-09-13-four-years-on-github.md b/docs/_posts/2013-09-13-four-years-on-github.md new file mode 100644 index 0000000..875dc53 --- /dev/null +++ b/docs/_posts/2013-09-13-four-years-on-github.md @@ -0,0 +1,12 @@ +--- +layout: post +title: "Four years on GitHub" +date: 2013-09-13 00:00:00 +excerpt: Still going strong! +--- + +The [first commit](https://github.com/tstack/lnav/commit/b4ec432515e95e86ec9d711833b8cb34d0912546) +to the [lnav repository](https://github.com/tstack/lnav) was four years ago +today! I started working on lnav a couple of years before that, but this was +its first public appearance. I still use it every day and plan to keep things +going for years to come. diff --git a/docs/_posts/2013-10-05-mini-review-in-linux-magazine.md b/docs/_posts/2013-10-05-mini-review-in-linux-magazine.md new file mode 100644 index 0000000..19f9c73 --- /dev/null +++ b/docs/_posts/2013-10-05-mini-review-in-linux-magazine.md @@ -0,0 +1,8 @@ +--- +layout: post +title: "Mini review of lnav in Linux Magazine" +date: 2013-10-05 00:00:00 +--- + +The [October issue of Linux Magazine](http://www.linux-magazine.com/Issues/2013/155/Tool-Tips) +has a nice review of lnav v0.5.0, the author gave it four out of five stars! diff --git a/docs/_posts/2013-10-06-competing-with-tail.md b/docs/_posts/2013-10-06-competing-with-tail.md new file mode 100644 index 0000000..b93e7bd --- /dev/null +++ b/docs/_posts/2013-10-06-competing-with-tail.md @@ -0,0 +1,23 @@ +--- +layout: post +title: "Competing with 'tail -f'" +date: 2013-09-10 00:00:00 +excerpt: The standard utilities are tough competition. +--- + +Probably the toughest competition for lnav is the standard Unix utilities like +tail, grep, less, and emacs/vim. It can be hard trying to convince people that +these built-in commands that they've used for forever can be improved upon. The +advanced features of lnav might even work against it since folks are expecting +to have to learn a bunch of stuff to see any benefits. + +The reality is that there are quite a few "passive" features in lnav that can +provide value with no effort required by the user. For example, lnav can easily +replace 'tail -f', it's even shorter to type! Beyond the basic task of +displaying new lines appended to a log file, you also get to see log messages +from multiple files interleaved, the ability to scroll backwards, syntax +highlighting, live searching, and so on. These basic features do not have the +same "wow" factor as executing a SQL query over data automatically extracted +from a log file, but they're the features that get used 90% of the time. + +Anyways, I think I'm gaining a new appreciation for marketing/sales... diff --git a/docs/_posts/2013-11-01-mini-review-linux-user-magazine.md b/docs/_posts/2013-11-01-mini-review-linux-user-magazine.md new file mode 100644 index 0000000..02d67f6 --- /dev/null +++ b/docs/_posts/2013-11-01-mini-review-linux-user-magazine.md @@ -0,0 +1,9 @@ +--- +layout: post +title: "Mini-review of lnav in Linux User Magazine" +date: 2013-09-10 00:00:00 +--- + +The german magazine, [Linux User](http://www.linux-user.de/), has a +mini-review of lnav v0.6.0 in their +[November issue](http://www.linux-user.de/Downloads/LUCE/2013/lu-ce_2013-11.pdf)! diff --git a/docs/_posts/2014-02-22-changes-to-the-scrollbar.md b/docs/_posts/2014-02-22-changes-to-the-scrollbar.md new file mode 100644 index 0000000..2c9a21f --- /dev/null +++ b/docs/_posts/2014-02-22-changes-to-the-scrollbar.md @@ -0,0 +1,21 @@ +--- +layout: post +title: "Changes To The Scrollbar" +date: 2014-02-22 00:00:00 +excerpt: Packing more information into the right scrollbar. +--- + +I've made some changes to the scrollbar that is shown on the right side of the +display based on some feedback from users. The scroll area now has a single +vertical line extending from the top to the bottom. Previously, this area would +show log message characters and it wasn't very clear that the scroll bar +existed. The line is colored based whether there are errors or warnings in that +part of the log. The coloring should make it easier to see the distribution of +errors across the whole log. Similarly, there are notches added to the left and +right side of the line to show search hits and bookmarks, respectively. See the +following screenshot to get an idea of what it looks like: + +![Screenshot of the redesigned scrollbar](/assets/images/scrollbar-change-2.png) + +These changes are currently only in the latest code from git. I'll be playing +with things a bit more before making a release. diff --git a/docs/_posts/2014-11-11-lofi-mode.md b/docs/_posts/2014-11-11-lofi-mode.md new file mode 100644 index 0000000..b6ca628 --- /dev/null +++ b/docs/_posts/2014-11-11-lofi-mode.md @@ -0,0 +1,23 @@ +--- +layout: post +title: Added "lo-fi" mode +excerpt: An alternative way to copy displayed text. +--- + +*(This change is in v0.7.2+)* + +Copying text to the clipboard can be done by marking lines with the +[bookmark hotkeys](https://docs.lnav.org/en/latest/hotkeys.html#bookmarks), +like `m`, and then pressing `c`. Commands that write to a file, +like [`:write-csv-to`](https://docs.lnav.org/en/latest/commands.html#write-csv-to-path) +accept `/dev/clipboard` as a way to write to the clipboard. However, if the +native clipboard isn't supported, or you're on an SSH connection, you can +now switch to "lo-fi" mode. In "lo-fi" mode, lnav drops out of the curses +display and prints the raw text to the terminal. You can switch to "lo-fi" +mode in a view by pressing `CTRL-L`. For commands, you can use a dash `-` +to switch to "lo-fi" and print to standard out. + + diff --git a/docs/_posts/2015-04-11-pretty-print-view.md b/docs/_posts/2015-04-11-pretty-print-view.md new file mode 100644 index 0000000..6ea31b1 --- /dev/null +++ b/docs/_posts/2015-04-11-pretty-print-view.md @@ -0,0 +1,39 @@ +--- +layout: post +title: "Pretty-print view in v0.7.3" +date: 2015-04-11 00:00:00 +excerpt: Automatically reformat structured data with "SHIFT+P". +--- + +I wanted to call out the pretty-print feature in the latest release of lnav. +This idea came from a coworker of Suresh who was having a hard time trying to +read some unformatted XML in a log. They wanted the XML pretty-printed and were +hoping that could be done by just piping the message to xmlpp or the like. So, +first we implemented the 'pipe-to' and 'pipe-line-to' commands that will let you +pipe log messages to a command and then display the result inside of lnav. That +worked well enough, but pretty-printing is such a frequent operation that having +to execute a command was kind of a pain. It would also be nice if it worked for +a variety of text, like JSON or Python data. The solution we came up with was to +leverage the existing code for parsing log messages to create a simple +pretty-printer that should work for most data formats. Another benefit is that +the log message does not have to be well-formed for the printer to work, any +leading or trailing garbage shouldn't confuse things. + +As an example, here is a screenshot of the log message with the unformatted XML +text with word-wrapping turned on: + +![Screenshot of raw XML](/assets/images/lnav-before-pretty.png) + +That's not very easy to read and it's hard to figure out the structure of the +message. Now, here is that same message after pressing SHIFT+P to switch to the +pretty-print view of lnav: + +![Screenshot of pretty-printed XML](/assets/images/lnav-after-pretty.png) + +The XML text is indented nicely and the usual syntax highlighting is applied. +Also notice that lnav will automatically try to lookup the DNS name for IP +addresses. Overall, I think it's a major improvement over the raw view. + +This is a pretty simple feature but I have found it quite useful in the couple +weeks that it has been implemented. It's so useful that I'm kicking myself for +not having thought of it before. diff --git a/docs/_posts/2016-03-20-lnav-in-print.md b/docs/_posts/2016-03-20-lnav-in-print.md new file mode 100644 index 0000000..73e7722 --- /dev/null +++ b/docs/_posts/2016-03-20-lnav-in-print.md @@ -0,0 +1,10 @@ +--- +layout: post +title: "lnav in print" +date: 2016-03-20 00:00:00 +--- + +A [review of lnav](https://archive.org/details/Linux_User_Developer_162_2016_UK/page/n87/mode/2up) +is in Linux User & Developer magazine issue 162: + +![Picture of lnav story in the magazine](/assets/images/linux-user-and-dev-mag.jpeg) diff --git a/docs/_posts/2018-03-27-reveal-file-paths.md b/docs/_posts/2018-03-27-reveal-file-paths.md new file mode 100644 index 0000000..01b31d8 --- /dev/null +++ b/docs/_posts/2018-03-27-reveal-file-paths.md @@ -0,0 +1,16 @@ +--- +layout: post +title: "Reveal log message source file paths by pressing left-arrow" +excerpt: Find out where log messages are coming from. +--- + +*(This change is in v0.8.4+)* + +If you want to know which file log messages are coming from, you can press the +left-arrow to reveal the unique name and then press it again to reveal the +full path. + + diff --git a/docs/_posts/2018-04-05-linux-magazine-tutorial.md b/docs/_posts/2018-04-05-linux-magazine-tutorial.md new file mode 100644 index 0000000..9d868b5 --- /dev/null +++ b/docs/_posts/2018-04-05-linux-magazine-tutorial.md @@ -0,0 +1,9 @@ +--- +layout: post +title: "Tutorial for lnav in Linux Magazine" +date: 2018-04-05 00:00:00 +--- + +Looks like there was an in-depth +[tutorial on lnav in Linux Magazine](http://www.linux-magazine.com/Issues/2017/196/Tutorials-lnav). +Unfortunately, I didn't notice until now and missed out on a hardcopy. diff --git a/docs/_posts/2018-05-17-tags-and-comments.md b/docs/_posts/2018-05-17-tags-and-comments.md new file mode 100644 index 0000000..c6f2fef --- /dev/null +++ b/docs/_posts/2018-05-17-tags-and-comments.md @@ -0,0 +1,22 @@ +--- +layout: post +title: "Support for tagging and commenting on log messages" +excerpt: Annotate log messages with your thoughts. +--- + +*(This change is in v0.8.4+)* + +If you have been wanting to add notes to log messages you might be interested +in, the new [`:comment`](https://docs.lnav.org/en/latest/commands.html#comment-text) +and [`:tag`](https://docs.lnav.org/en/latest/commands.html#tag-tag) commands. +These commands add a comment or tag(s) to the top message in the log view. +The comments and tags are saved in the session, so they will be restored +automatically when the file is reopened. These annotations can be searched +for using the regular search prompt and can be accessed in the +[log tables](https://docs.lnav.org/en/latest/sqlext.html#log-tables) using the +`log_tags` and `log_comment` columns. + + diff --git a/docs/_posts/2018-11-9-visual-filter-editor.md b/docs/_posts/2018-11-9-visual-filter-editor.md new file mode 100644 index 0000000..8fb59a4 --- /dev/null +++ b/docs/_posts/2018-11-9-visual-filter-editor.md @@ -0,0 +1,35 @@ +--- +layout: post +title: "Visual filter editor" +excerpt: A friendlier way to interact with filters. +--- + +*(This change is in v0.8.5+)* + +A visual filter editor has been added to make it easier to create, edit, +enable, disable, and delete filters. In the log or text views, pressing `TAB` +will open the filter editor panel. While the panel is in focus, the following +hotkeys can be used: + +- `i` - Create an IN filter that will only show lines that match the given + regular expression. +- `o` - Create an OUT filter that will hide lines that match the given regular + expression. +- `Space` - Toggle the filter between being enabled and disabled. +- `Enter` - Edit the selected filter. +- `Shift+D` - Delete the filter. +- `t` - Switch a filter from an IN to an OUT or vice-versa. +- `f` - Globally enable or disable filtering. + +When editing a filter, the main view will highlight lines that portion of the +lines that match the given regular expression: + +- Lines that match an OUT filter are highlighted with red; +- Lines that match an IN filter are highlighted with green. + +You can also press `TAB` to complete words that are visible in the main view. + + diff --git a/docs/_posts/2019-05-08-themes.md b/docs/_posts/2019-05-08-themes.md new file mode 100644 index 0000000..a145ca6 --- /dev/null +++ b/docs/_posts/2019-05-08-themes.md @@ -0,0 +1,28 @@ +--- +layout: post +title: Support for Themes +excerpt: Change the user-interface to your liking +--- + +*(This change is in v0.9.0+)* + +The lnav user-interface can now be customized by selecting one of the +builtin themes, like +[monocai](https://github.com/tstack/lnav/blob/master/src/themes/monocai.json) +and +[night-owl](https://github.com/tstack/lnav/blob/master/src/themes/night-owl.json), +or +[by defining your own theme](https://lnav.readthedocs.io/en/latest/config.html#theme-definitions). + +Selecting a theme can be done through the +[`:config`](https://docs.lnav.org/en/latest/commands.html#config-option-value) +command, like so: + +``` +:config /ui/theme monocai +``` + +Pressing `TAB` after the `/ui/theme ` will cycle through the available themes, +like so: + +![Animation of lnav cycling through themes](/assets/images/lnav-theme-cycle.gif) diff --git a/docs/_posts/2020-12-23-xpath-sql-function.md b/docs/_posts/2020-12-23-xpath-sql-function.md new file mode 100644 index 0000000..b0a7657 --- /dev/null +++ b/docs/_posts/2020-12-23-xpath-sql-function.md @@ -0,0 +1,39 @@ +--- +layout: post +title: Drilling down into XML snippets +excerpt: The new "xpath()" table-valued SQL function. +--- + +*(This change is in [**v0.10.0+**](https://github.com/tstack/lnav/releases/tag/v0.10.0))* + +XML snippets in log messages can now be queried using the +[`xpath()`](https://docs.lnav.org/en/latest/sqlext.html#xpath-xpath-xmldoc) +table-valued SQL function. The function takes an +[XPath](https://developer.mozilla.org/en-US/docs/Web/XPath), the XML snippet +to be queried, and returns a table with the results of the XPath query. +For example, given following XML document: + +```xml +Hello, World! +``` + +Extracting the text value from the `msg` node can be done using the following +query: + +```sql +SELECT result FROM xpath('/msg/text()', 'Hello, World!') +``` + +Of course, you won't typically be passing XML values as string literals, you +will be extracting them from log messages. Assuming your log format already +extracts the XML data, you can do a `SELECT` on the log format table and join +that with the `xpath()` call. Since it can be challenging to construct a +correct `xpath()` call, lnav will suggest calls for the nodes it finds in any +XML log message fields. The following asciicast demonstrates this flow: + + + +The implementation uses the [pugixml](https://pugixml.org) library. diff --git a/docs/_posts/2021-05-03-tailing-remote-files.md b/docs/_posts/2021-05-03-tailing-remote-files.md new file mode 100644 index 0000000..9d1d15c --- /dev/null +++ b/docs/_posts/2021-05-03-tailing-remote-files.md @@ -0,0 +1,32 @@ +--- +layout: post +title: Tailing files on remote hosts +excerpt: Native support for tailing logs on machines accessible via SSH +--- + +*(This change is in [**v0.10.0+**](https://github.com/tstack/lnav/releases/tag/v0.10.0))* + +One of the new features in the upcoming v0.10.0 release of lnav is support +for tailing log files on remote hosts via SSH. This feature allows you to +view local files and files from multiple remote hosts alongside each other +in the log view. The only setup required is to ensure the machines can be +accessed via SSH without any interaction, meaning the host key must have +been previously accepted and public key authentication configured. Opening +a remote file is then simply a matter of specifying the location using the +common scp syntax (i.e. `user@host:/path/to/file`). + +When lnav accesses a remote host, it transfers an agent (called the +"tailer") to the host to handle file system requests from lnav. The agent +is an [αcτµαlly pδrταblε εxεcµταblε](https://justine.lol/ape.html) that +should run on most X86 Operating Systems. The agent will monitor the +files of interest and synchronize their contents back to the host machine. +In addition, the agent can be used to satisfy interactive requests for +TAB-completion of remote file paths and previewing directory and file +contents. + +The following asciicast shows lnav opening log files on MacOS and FreeBSD: + + diff --git a/docs/_posts/2022-05-01-regex101-integration.md b/docs/_posts/2022-05-01-regex101-integration.md new file mode 100644 index 0000000..02011ce --- /dev/null +++ b/docs/_posts/2022-05-01-regex101-integration.md @@ -0,0 +1,73 @@ +--- +layout: post +title: Integration with regex101.com +excerpt: Create/edit format files using regex101.com +--- + +*(This change will be in the upcoming v0.11.0 release)* + +Creating and updating format files for **lnav** can be a bit tedious and +error-prone. To help streamline the process, an integration with regex101.com +has been added. Now, you can create regular expressions for plaintext log +files on https://regex101.com and then create a skeleton format file with a +simple command. If you already have a format file that needs to be updated, +you can push the regexes up to regex101, edit them with their interface, and +then pull the changes back down as a format patch file. + +To further improve the experience of developing with format files, there is +also work underway to improve error messages. Many messages should be clearer, +more context is provided, and they should look nicer as well. For example, the +following error is displayed when a format regex is not valid: + +![Screenshot of an error message](/assets/images/lnav-invalid-regex-error.png) + +## Management CLI + +The regex101 integration can be accessed through the new "management-mode CLI". +This mode can be accessed by passing `-m` as the first option to **lnav**. The +management CLI is organized as a series of nested commands. If you're not sure +what to do at a given level, run the command as-is and the CLI should print out +help text to guide you through the hierarchy of commands and required +parameters. + +### Create a format from a regular expression + +The `regex101 import` command can be used to import a regular expression from +regex101.com and create or patch a format file. The command takes the URL of +the regex, the format name, and the name of the regex in the log format ( +defaults to "std" if not given). For example, the following command can be used +to import the regex at "https://regex101.com/r/zpEnjV/2" into the format named " +re101_example_log": + +```console +$ lnav -m regex101 import https://regex101.com/r/zpEnjV/2 re101_example_log +``` + +If the import was successful, the path to the skeleton format file will be +printed. You will most likely need to edit the file to fill in more details +about your log format. + +### Editing an existing regular expression + +If you have a log format with a regex that needs to be updated, you can push +the regex to regex101.com for editing with a command like (replace +"myformat_log"/"std" with the name of your format and regex): + +```console +$ lnav -m format myformat_log regex std regex101 push +``` + +Along with the regex, the format's samples will be added to the entry to ensure +changes won't break existing matches. If the push was successful, the URL for +the new regex101.com entry will be printed out. You can use that URL to edit the +regex to your needs. Once you're done editing the regex, you can pull the +changes down to a "patch" file using the following command: + +```console +$ lnav -m format myformat_log regex std regex101 pull +``` + +The patch file will be evaluated after the original format file and override +the values from the original. Once you are satisfied with the changes, you +can move the contents of the patch file to the original file and delete the +patch. diff --git a/docs/_posts/2022-08-04-pretty-errors.md b/docs/_posts/2022-08-04-pretty-errors.md new file mode 100644 index 0000000..b04c9e8 --- /dev/null +++ b/docs/_posts/2022-08-04-pretty-errors.md @@ -0,0 +1,46 @@ +--- +layout: post +title: Pretty error messages +excerpt: Error message improvements +--- + +*(This change will be in the upcoming v0.11.0 release)* + +Taking a page from compilers like rustc, I've spent some time +improving error messages to make them look nicer and be more +helpful. Fortunately, SQLite has improved their error reporting +as well by adding +[sqlite3_error_offset()](http://sqlite.com/c3ref/errcode.html). +This function can point to the part of the SQL statement that +was in error. As an example of the improvement, a SQL file +that contained the following content: + +```sql + +-- This is a test +SELECT abc), +rtrim(def) +FROM mytable; +``` + +Would report an error like the following on startup in v0.10.1: + +```text +error:/Users/tstack/.config/lnav/formats/installed/test.sql:2:near ")": syntax error +``` + +Now, you will get a clearer error message with a syntax-highlighted +code snippet and a pointer to the part of the code that has the +problem: + +![Screenshot of a SQL error](/assets/images/lnav-sql-error-msg.png) + +Inside the TUI, a panel has been added at the bottom to display these +long-form error messages. The panel will disappear after a short +time or when input is received. Here is an example showing an error +for an invalid regular expression: + + diff --git a/docs/_posts/2022-08-06-markdown-support.md b/docs/_posts/2022-08-06-markdown-support.md new file mode 100644 index 0000000..4504bbd --- /dev/null +++ b/docs/_posts/2022-08-06-markdown-support.md @@ -0,0 +1,33 @@ +--- +layout: post +title: Markdown Support +excerpt: A side effect of fancier help text +--- + +*(This change will be in the upcoming v0.11.0 release)* + +As part of the effort to polish the lnav TUI, I wanted to make the builtin +help text look a bit nicer. The current help text is a plain text file with +some ANSI escape sequences for colors. It's not easy to write or read. Since +Markdown has become a dominant way to write this type of document, I figured +I could use that and have the side benefit of allowing lnav to read Markdown +docs. Fortunately, the [MD4C](https://github.com/mity/md4c) library exists. +This library provides a nice event-driven parser for documents instead of +just converting directly to HTML. In addition, document structure is now +shown/navigable through the new breadcrumb bar at the top. I think the +result is pretty nice: + + + +## Viewing Markdown Files + +Files with an `.md` suffix will be considered as Markdown and will be +parsed as such. As an example, here is lnav displaying its README.md file: + + diff --git a/docs/_posts/2022-09-01-playground.md b/docs/_posts/2022-09-01-playground.md new file mode 100644 index 0000000..3cbaa11 --- /dev/null +++ b/docs/_posts/2022-09-01-playground.md @@ -0,0 +1,27 @@ +--- +layout: post +title: Playground and Tutorial +excerpt: Try lnav without having to install anything +--- + +To make it easier to try out **lnav**, I've deployed an ssh-based playground +and tutorial. You can SSH as follows to try them out: + +```console +$ ssh playground@demo.lnav.org +$ ssh tutorial1@demo.lnav.org +``` + + + +The playground has a couple of example logs to play with. The tutorial +tries to guide you through the basics of navigating log files with lnav. +The server is running on the free-tier of [fly.io](https://fly.io), so +please be kind. + +This effort was inspired by the `git.charm.sh` SSH server and by the +[fasterthanli.me](https://fasterthanli.me/articles/remote-development-with-rust-on-fly-io) +post on doing remote development on fly.io. diff --git a/docs/_posts/2022-09-24-vscode-extension.md b/docs/_posts/2022-09-24-vscode-extension.md new file mode 100644 index 0000000..6c7b78f --- /dev/null +++ b/docs/_posts/2022-09-24-vscode-extension.md @@ -0,0 +1,21 @@ +--- +layout: post +title: VSCode Extension for lnav +excerpt: Syntax highlighting for lnav scripts +--- + +I've published a simple [Visual Studio Code extension for lnav]( +https://marketplace.visualstudio.com/items?itemName=lnav.lnav) +that adds syntax highlighting for scripts. The following is a +screenshot showing the `dhclient-summary.lnav` script that is +builtin: + +![Screenshot of an lnav script](/assets/images/lnav-vscode-extension.png) + +The lnav commands, those prefixed with colons, are marked as +keywords and the SQL blocks are treated as an embedded language +and highlighted accordingly. + +If people find this useful, we can take it further and add +support for running the current script/snippet in a new lnav +process or even talking to an existing one. diff --git a/docs/assets/images/favicon.png b/docs/assets/images/favicon.png new file mode 100644 index 0000000..e126db3 Binary files /dev/null and b/docs/assets/images/favicon.png differ diff --git a/docs/assets/images/favicon.svg b/docs/assets/images/favicon.svg new file mode 100644 index 0000000..6c62941 --- /dev/null +++ b/docs/assets/images/favicon.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/docs/assets/images/linux-user-and-dev-mag.jpeg b/docs/assets/images/linux-user-and-dev-mag.jpeg new file mode 100644 index 0000000..c61e8e0 Binary files /dev/null and b/docs/assets/images/linux-user-and-dev-mag.jpeg differ diff --git a/docs/assets/images/lnav-after-pretty.png b/docs/assets/images/lnav-after-pretty.png new file mode 100644 index 0000000..3983713 Binary files /dev/null and b/docs/assets/images/lnav-after-pretty.png differ diff --git a/docs/assets/images/lnav-before-pretty.png b/docs/assets/images/lnav-before-pretty.png new file mode 100644 index 0000000..3100091 Binary files /dev/null and b/docs/assets/images/lnav-before-pretty.png differ diff --git a/docs/assets/images/lnav-front-page.png b/docs/assets/images/lnav-front-page.png new file mode 100644 index 0000000..20483dd Binary files /dev/null and b/docs/assets/images/lnav-front-page.png differ diff --git a/docs/assets/images/lnav-hist.png b/docs/assets/images/lnav-hist.png new file mode 100644 index 0000000..f3e92eb Binary files /dev/null and b/docs/assets/images/lnav-hist.png differ diff --git a/docs/assets/images/lnav-invalid-regex-error.png b/docs/assets/images/lnav-invalid-regex-error.png new file mode 100644 index 0000000..d67d53c Binary files /dev/null and b/docs/assets/images/lnav-invalid-regex-error.png differ diff --git a/docs/assets/images/lnav-multi-file2.png b/docs/assets/images/lnav-multi-file2.png new file mode 100644 index 0000000..3498fbc Binary files /dev/null and b/docs/assets/images/lnav-multi-file2.png differ diff --git a/docs/assets/images/lnav-query.png b/docs/assets/images/lnav-query.png new file mode 100644 index 0000000..470fdc6 Binary files /dev/null and b/docs/assets/images/lnav-query.png differ diff --git a/docs/assets/images/lnav-sql-error-msg.png b/docs/assets/images/lnav-sql-error-msg.png new file mode 100644 index 0000000..526f6c3 Binary files /dev/null and b/docs/assets/images/lnav-sql-error-msg.png differ diff --git a/docs/assets/images/lnav-syntax-highlight.gif b/docs/assets/images/lnav-syntax-highlight.gif new file mode 100644 index 0000000..6f0ce13 Binary files /dev/null and b/docs/assets/images/lnav-syntax-highlight.gif differ diff --git a/docs/assets/images/lnav-syslog-thumb.png b/docs/assets/images/lnav-syslog-thumb.png new file mode 100644 index 0000000..e1f0533 Binary files /dev/null and b/docs/assets/images/lnav-syslog-thumb.png differ diff --git a/docs/assets/images/lnav-syslog.png b/docs/assets/images/lnav-syslog.png new file mode 100644 index 0000000..b676931 Binary files /dev/null and b/docs/assets/images/lnav-syslog.png differ diff --git a/docs/assets/images/lnav-tab-complete.gif b/docs/assets/images/lnav-tab-complete.gif new file mode 100644 index 0000000..de49539 Binary files /dev/null and b/docs/assets/images/lnav-tab-complete.gif differ diff --git a/docs/assets/images/lnav-theme-cycle.gif b/docs/assets/images/lnav-theme-cycle.gif new file mode 100644 index 0000000..f21281f Binary files /dev/null and b/docs/assets/images/lnav-theme-cycle.gif differ diff --git a/docs/assets/images/lnav-vscode-extension.png b/docs/assets/images/lnav-vscode-extension.png new file mode 100644 index 0000000..f41760f Binary files /dev/null and b/docs/assets/images/lnav-vscode-extension.png differ diff --git a/docs/assets/images/scrollbar-change-2.png b/docs/assets/images/scrollbar-change-2.png new file mode 100644 index 0000000..c7671c1 Binary files /dev/null and b/docs/assets/images/scrollbar-change-2.png differ diff --git a/docs/assets/main.scss b/docs/assets/main.scss new file mode 100644 index 0000000..bda771d --- /dev/null +++ b/docs/assets/main.scss @@ -0,0 +1,31 @@ +--- +--- + +@import "{{ site.theme }}"; + +#playground-box { + font-size: x-large; + border-radius: 25px; + background: #8d8; + padding: 20px; + display: inline-block; +} + +#playground-box h4 { + margin-bottom: 0; +} + +#playground-box code { + padding-left: 20px; + background: #444; + border-color: #444; +} + +#playground-box code a { + padding-right: 20px; + color: white; +} + +#playground-box .prompt { + color: #4f4; +} diff --git a/docs/conf.py.in b/docs/conf.py.in new file mode 100644 index 0000000..b81e3d9 --- /dev/null +++ b/docs/conf.py.in @@ -0,0 +1,6 @@ +DOXYFILE = 'Doxyfile' + +LINKS_NAVBAR1 = [ + (None, 'pages', [(None, 'about')]), + (None, 'namespaces', []), +] diff --git a/docs/index.markdown b/docs/index.markdown new file mode 100644 index 0000000..eadb501 --- /dev/null +++ b/docs/index.markdown @@ -0,0 +1,58 @@ +--- + +# Feel free to add content and custom Front Matter to this file. + +# To modify the layout, see https://jekyllrb.com/docs/themes/#overriding-theme-defaults + +layout: top +--- + +![Screenshot of lnav](/assets/images/lnav-front-page.png){: +style="float: right; max-width: 50%"} + +
+An advanced log file viewer for the small-scale +
+ +
+

Watch and analyze your log files from a terminal.

+ +

No server. No setup. Still featureful.

+ +
+

Try it out:

+ + +$ +ssh playground@demo.lnav.org + +
+
+ +
+
+
In Your Terminal
+
+Many logging tools, like Splunk, provide great features but are optimized for +large-scale deployments. They require installing and configuring servers +before they can be effectively used. There is still a need for a robust log +file analyzer for the terminal. +
+
+ +
+
Easy to Use
+
+Just point lnav to a directory and it will take care of the rest. File formats +are automatically detected and compressed files are unpacked on the fly. +
+
+ +
+
Improved Presentation
+
+Log files are a wealth of information, lnav can help highlight the parts that +are important and filter out the noise. +
+
+
diff --git a/docs/lnav-architecture.png b/docs/lnav-architecture.png new file mode 100644 index 0000000..dc9fae9 Binary files /dev/null and b/docs/lnav-architecture.png differ diff --git a/docs/lnav-tui.png b/docs/lnav-tui.png new file mode 100644 index 0000000..bcb87ca Binary files /dev/null and b/docs/lnav-tui.png differ diff --git a/docs/pages/about.dox b/docs/pages/about.dox new file mode 100644 index 0000000..2efbda9 --- /dev/null +++ b/docs/pages/about.dox @@ -0,0 +1,7 @@ +/** + * @page about About + * @section about-doxygen Doxygen documentation + * This page is auto generated using + * Doxygen, making use of some useful + * special commands. + */ diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 0000000..6683740 --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1,5 @@ +sphinx>=4.3.0 +sphinx-jsonschema +sphinx-prompt +pathlib +sphinx-rtd-theme>=0.5.1 diff --git a/docs/schemas/config-v1.schema.json b/docs/schemas/config-v1.schema.json new file mode 100644 index 0000000..c2b0fb4 --- /dev/null +++ b/docs/schemas/config-v1.schema.json @@ -0,0 +1,778 @@ +{ + "$id": "https://lnav.org/schemas/config-v1.schema.json", + "title": "https://lnav.org/schemas/config-v1.schema.json", + "$schema": "http://json-schema.org/draft-07/schema#", + "properties": { + "$schema": { + "title": "/$schema", + "description": "The URI that specifies the schema that describes this type of file", + "type": "string", + "examples": [ + "https://lnav.org/schemas/config-v1.schema.json" + ] + }, + "tuning": { + "description": "Internal settings", + "title": "/tuning", + "type": "object", + "properties": { + "archive-manager": { + "description": "Settings related to opening archive files", + "title": "/tuning/archive-manager", + "type": "object", + "properties": { + "min-free-space": { + "title": "/tuning/archive-manager/min-free-space", + "description": "The minimum free space, in bytes, to maintain when unpacking archives", + "type": "integer", + "minimum": 0 + }, + "cache-ttl": { + "title": "/tuning/archive-manager/cache-ttl", + "description": "The time-to-live for unpacked archives, expressed as a duration (e.g. '3d' for three days)", + "type": "string", + "examples": [ + "3d", + "12h" + ] + } + }, + "additionalProperties": false + }, + "file-vtab": { + "description": "Settings related to the lnav_file virtual-table", + "title": "/tuning/file-vtab", + "type": "object", + "properties": { + "max-content-size": { + "title": "/tuning/file-vtab/max-content-size", + "description": "The maximum allowed file size for the content column", + "type": "integer", + "minimum": 0 + } + }, + "additionalProperties": false + }, + "logfile": { + "description": "Settings related to log files", + "title": "/tuning/logfile", + "type": "object", + "properties": { + "max-unrecognized-lines": { + "title": "/tuning/logfile/max-unrecognized-lines", + "description": "The maximum number of lines in a file to use when detecting the format", + "type": "integer", + "minimum": 1 + } + }, + "additionalProperties": false + }, + "remote": { + "description": "Settings related to remote file support", + "title": "/tuning/remote", + "type": "object", + "properties": { + "cache-ttl": { + "title": "/tuning/remote/cache-ttl", + "description": "The time-to-live for files copied from remote hosts, expressed as a duration (e.g. '3d' for three days)", + "type": "string", + "examples": [ + "3d", + "12h" + ] + }, + "ssh": { + "description": "Settings related to the ssh command used to contact remote machines", + "title": "/tuning/remote/ssh", + "type": "object", + "properties": { + "command": { + "title": "/tuning/remote/ssh/command", + "description": "The SSH command to execute", + "type": "string" + }, + "transfer-command": { + "title": "/tuning/remote/ssh/transfer-command", + "description": "Command executed on the remote host when transferring the file", + "type": "string" + }, + "start-command": { + "title": "/tuning/remote/ssh/start-command", + "description": "Command executed on the remote host to start the tailer", + "type": "string" + }, + "flags": { + "title": "/tuning/remote/ssh/flags", + "description": "The flags to pass to the SSH command", + "type": "string" + }, + "options": { + "description": "The options to pass to the SSH command", + "title": "/tuning/remote/ssh/options", + "type": "object", + "patternProperties": { + "(\\w+)": { + "title": "/tuning/remote/ssh/options/", + "description": "Set an option to be passed to the SSH command", + "type": "string" + } + }, + "additionalProperties": false + }, + "config": { + "description": "The ssh_config options to pass to SSH with the -o option", + "title": "/tuning/remote/ssh/config", + "type": "object", + "patternProperties": { + "(\\w+)": { + "title": "/tuning/remote/ssh/config/", + "description": "Set an SSH configuration value", + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + "clipboard": { + "description": "Settings related to the clipboard", + "title": "/tuning/clipboard", + "type": "object", + "properties": { + "impls": { + "description": "Clipboard implementations", + "title": "/tuning/clipboard/impls", + "type": "object", + "patternProperties": { + "([\\w\\-]+)": { + "description": "Clipboard implementation", + "title": "/tuning/clipboard/impls/", + "type": "object", + "properties": { + "test": { + "title": "/tuning/clipboard/impls//test", + "description": "The command that checks", + "type": "string", + "examples": [ + "command -v pbcopy" + ] + }, + "general": { + "description": "Commands to work with the general clipboard", + "title": "/tuning/clipboard/impls//general", + "$ref": "#/definitions/clip-commands" + }, + "find": { + "description": "Commands to work with the find clipboard", + "title": "/tuning/clipboard/impls//find", + "$ref": "#/definitions/clip-commands" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + "ui": { + "description": "User-interface settings", + "title": "/ui", + "type": "object", + "properties": { + "clock-format": { + "title": "/ui/clock-format", + "description": "The format for the clock displayed in the top-left corner using strftime(3) conversions", + "type": "string", + "examples": [ + "%a %b %d %H:%M:%S %Z" + ] + }, + "dim-text": { + "title": "/ui/dim-text", + "description": "Reduce the brightness of text (useful for xterms). This setting can be useful when running in an xterm where the white color is very bright.", + "type": "boolean" + }, + "default-colors": { + "title": "/ui/default-colors", + "description": "Use default terminal background and foreground colors instead of black and white for all text coloring. This setting can be useful when transparent background or alternate color theme terminal is used.", + "type": "boolean" + }, + "keymap": { + "title": "/ui/keymap", + "description": "The name of the keymap to use.", + "type": "string" + }, + "theme": { + "title": "/ui/theme", + "description": "The name of the theme to use.", + "type": "string" + }, + "theme-defs": { + "description": "Theme definitions.", + "title": "/ui/theme-defs", + "type": "object", + "patternProperties": { + "([\\w\\-]+)": { + "description": "Theme definitions", + "title": "/ui/theme-defs/", + "type": "object", + "properties": { + "vars": { + "description": "Variables definitions that are used in this theme.", + "title": "/ui/theme-defs//vars", + "type": "object", + "patternProperties": { + "(\\w+)": { + "title": "/ui/theme-defs//vars/", + "description": "A theme variable definition", + "type": "string" + } + }, + "additionalProperties": false + }, + "styles": { + "description": "Styles for log messages.", + "title": "/ui/theme-defs//styles", + "type": "object", + "properties": { + "identifier": { + "description": "Styling for identifiers in logs", + "title": "/ui/theme-defs//styles/identifier", + "$ref": "#/definitions/style" + }, + "text": { + "description": "Styling for plain text", + "title": "/ui/theme-defs//styles/text", + "$ref": "#/definitions/style" + }, + "alt-text": { + "description": "Styling for plain text when alternating", + "title": "/ui/theme-defs//styles/alt-text", + "$ref": "#/definitions/style" + }, + "error": { + "description": "Styling for error messages", + "title": "/ui/theme-defs//styles/error", + "$ref": "#/definitions/style" + }, + "ok": { + "description": "Styling for success messages", + "title": "/ui/theme-defs//styles/ok", + "$ref": "#/definitions/style" + }, + "info": { + "description": "Styling for informational messages", + "title": "/ui/theme-defs//styles/info", + "$ref": "#/definitions/style" + }, + "warning": { + "description": "Styling for warning messages", + "title": "/ui/theme-defs//styles/warning", + "$ref": "#/definitions/style" + }, + "hidden": { + "description": "Styling for hidden fields in logs", + "title": "/ui/theme-defs//styles/hidden", + "$ref": "#/definitions/style" + }, + "adjusted-time": { + "description": "Styling for timestamps that have been adjusted", + "title": "/ui/theme-defs//styles/adjusted-time", + "$ref": "#/definitions/style" + }, + "skewed-time": { + "description": "Styling for timestamps that are different from the received time", + "title": "/ui/theme-defs//styles/skewed-time", + "$ref": "#/definitions/style" + }, + "offset-time": { + "description": "Styling for hidden fields", + "title": "/ui/theme-defs//styles/offset-time", + "$ref": "#/definitions/style" + }, + "invalid-msg": { + "description": "Styling for invalid log messages", + "title": "/ui/theme-defs//styles/invalid-msg", + "$ref": "#/definitions/style" + }, + "popup": { + "description": "Styling for popup windows", + "title": "/ui/theme-defs//styles/popup", + "$ref": "#/definitions/style" + }, + "focused": { + "description": "Styling for a focused row in a list view", + "title": "/ui/theme-defs//styles/focused", + "$ref": "#/definitions/style" + }, + "disabled-focused": { + "description": "Styling for a disabled focused row in a list view", + "title": "/ui/theme-defs//styles/disabled-focused", + "$ref": "#/definitions/style" + }, + "scrollbar": { + "description": "Styling for scrollbars", + "title": "/ui/theme-defs//styles/scrollbar", + "$ref": "#/definitions/style" + }, + "h1": { + "description": "Styling for top-level headers", + "title": "/ui/theme-defs//styles/h1", + "$ref": "#/definitions/style" + }, + "h2": { + "description": "Styling for 2nd-level headers", + "title": "/ui/theme-defs//styles/h2", + "$ref": "#/definitions/style" + }, + "h3": { + "description": "Styling for 3rd-level headers", + "title": "/ui/theme-defs//styles/h3", + "$ref": "#/definitions/style" + }, + "h4": { + "description": "Styling for 4th-level headers", + "title": "/ui/theme-defs//styles/h4", + "$ref": "#/definitions/style" + }, + "h5": { + "description": "Styling for 5th-level headers", + "title": "/ui/theme-defs//styles/h5", + "$ref": "#/definitions/style" + }, + "h6": { + "description": "Styling for 6th-level headers", + "title": "/ui/theme-defs//styles/h6", + "$ref": "#/definitions/style" + }, + "hr": { + "description": "Styling for horizontal rules", + "title": "/ui/theme-defs//styles/hr", + "$ref": "#/definitions/style" + }, + "hyperlink": { + "description": "Styling for hyperlinks", + "title": "/ui/theme-defs//styles/hyperlink", + "$ref": "#/definitions/style" + }, + "list-glyph": { + "description": "Styling for glyphs that prefix a list item", + "title": "/ui/theme-defs//styles/list-glyph", + "$ref": "#/definitions/style" + }, + "breadcrumb": { + "description": "Styling for the separator between breadcrumbs", + "title": "/ui/theme-defs//styles/breadcrumb", + "$ref": "#/definitions/style" + }, + "table-border": { + "description": "Styling for table borders", + "title": "/ui/theme-defs//styles/table-border", + "$ref": "#/definitions/style" + }, + "table-header": { + "description": "Styling for table headers", + "title": "/ui/theme-defs//styles/table-header", + "$ref": "#/definitions/style" + }, + "quote-border": { + "description": "Styling for quoted-block borders", + "title": "/ui/theme-defs//styles/quote-border", + "$ref": "#/definitions/style" + }, + "quoted-text": { + "description": "Styling for quoted text blocks", + "title": "/ui/theme-defs//styles/quoted-text", + "$ref": "#/definitions/style" + }, + "footnote-border": { + "description": "Styling for footnote borders", + "title": "/ui/theme-defs//styles/footnote-border", + "$ref": "#/definitions/style" + }, + "footnote-text": { + "description": "Styling for footnote text", + "title": "/ui/theme-defs//styles/footnote-text", + "$ref": "#/definitions/style" + }, + "snippet-border": { + "description": "Styling for snippet borders", + "title": "/ui/theme-defs//styles/snippet-border", + "$ref": "#/definitions/style" + } + }, + "additionalProperties": false + }, + "syntax-styles": { + "description": "Styles for syntax highlighting in text files.", + "title": "/ui/theme-defs//syntax-styles", + "type": "object", + "properties": { + "quoted-code": { + "description": "Styling for quoted code blocks", + "title": "/ui/theme-defs//syntax-styles/quoted-code", + "$ref": "#/definitions/style" + }, + "code-border": { + "description": "Styling for quoted-code borders", + "title": "/ui/theme-defs//syntax-styles/code-border", + "$ref": "#/definitions/style" + }, + "keyword": { + "description": "Styling for keywords in source files", + "title": "/ui/theme-defs//syntax-styles/keyword", + "$ref": "#/definitions/style" + }, + "string": { + "description": "Styling for single/double-quoted strings in text", + "title": "/ui/theme-defs//syntax-styles/string", + "$ref": "#/definitions/style" + }, + "comment": { + "description": "Styling for comments in source files", + "title": "/ui/theme-defs//syntax-styles/comment", + "$ref": "#/definitions/style" + }, + "doc-directive": { + "description": "Styling for documentation directives in source files", + "title": "/ui/theme-defs//syntax-styles/doc-directive", + "$ref": "#/definitions/style" + }, + "variable": { + "description": "Styling for variables in text", + "title": "/ui/theme-defs//syntax-styles/variable", + "$ref": "#/definitions/style" + }, + "symbol": { + "description": "Styling for symbols in source files", + "title": "/ui/theme-defs//syntax-styles/symbol", + "$ref": "#/definitions/style" + }, + "number": { + "description": "Styling for numbers in source files", + "title": "/ui/theme-defs//syntax-styles/number", + "$ref": "#/definitions/style" + }, + "re-special": { + "description": "Styling for special characters in regular expressions", + "title": "/ui/theme-defs//syntax-styles/re-special", + "$ref": "#/definitions/style" + }, + "re-repeat": { + "description": "Styling for repeats in regular expressions", + "title": "/ui/theme-defs//syntax-styles/re-repeat", + "$ref": "#/definitions/style" + }, + "diff-delete": { + "description": "Styling for deleted lines in diffs", + "title": "/ui/theme-defs//syntax-styles/diff-delete", + "$ref": "#/definitions/style" + }, + "diff-add": { + "description": "Styling for added lines in diffs", + "title": "/ui/theme-defs//syntax-styles/diff-add", + "$ref": "#/definitions/style" + }, + "diff-section": { + "description": "Styling for diffs", + "title": "/ui/theme-defs//syntax-styles/diff-section", + "$ref": "#/definitions/style" + }, + "spectrogram-low": { + "description": "Styling for the lower threshold values in the spectrogram view", + "title": "/ui/theme-defs//syntax-styles/spectrogram-low", + "$ref": "#/definitions/style" + }, + "spectrogram-medium": { + "description": "Styling for the medium threshold values in the spectrogram view", + "title": "/ui/theme-defs//syntax-styles/spectrogram-medium", + "$ref": "#/definitions/style" + }, + "spectrogram-high": { + "description": "Styling for the high threshold values in the spectrogram view", + "title": "/ui/theme-defs//syntax-styles/spectrogram-high", + "$ref": "#/definitions/style" + }, + "file": { + "description": "Styling for file names in source files", + "title": "/ui/theme-defs//syntax-styles/file", + "$ref": "#/definitions/style" + } + }, + "additionalProperties": false + }, + "status-styles": { + "description": "Styles for the user-interface components.", + "title": "/ui/theme-defs//status-styles", + "type": "object", + "properties": { + "text": { + "description": "Styling for status bars", + "title": "/ui/theme-defs//status-styles/text", + "$ref": "#/definitions/style" + }, + "warn": { + "description": "Styling for warnings in status bars", + "title": "/ui/theme-defs//status-styles/warn", + "$ref": "#/definitions/style" + }, + "alert": { + "description": "Styling for alerts in status bars", + "title": "/ui/theme-defs//status-styles/alert", + "$ref": "#/definitions/style" + }, + "active": { + "description": "Styling for activity in status bars", + "title": "/ui/theme-defs//status-styles/active", + "$ref": "#/definitions/style" + }, + "inactive-alert": { + "description": "Styling for inactive alert status bars", + "title": "/ui/theme-defs//status-styles/inactive-alert", + "$ref": "#/definitions/style" + }, + "inactive": { + "description": "Styling for inactive status bars", + "title": "/ui/theme-defs//status-styles/inactive", + "$ref": "#/definitions/style" + }, + "title-hotkey": { + "description": "Styling for hotkey highlights in titles", + "title": "/ui/theme-defs//status-styles/title-hotkey", + "$ref": "#/definitions/style" + }, + "title": { + "description": "Styling for title sections of status bars", + "title": "/ui/theme-defs//status-styles/title", + "$ref": "#/definitions/style" + }, + "disabled-title": { + "description": "Styling for title sections of status bars", + "title": "/ui/theme-defs//status-styles/disabled-title", + "$ref": "#/definitions/style" + }, + "subtitle": { + "description": "Styling for subtitle sections of status bars", + "title": "/ui/theme-defs//status-styles/subtitle", + "$ref": "#/definitions/style" + }, + "info": { + "description": "Styling for informational messages in status bars", + "title": "/ui/theme-defs//status-styles/info", + "$ref": "#/definitions/style" + }, + "hotkey": { + "description": "Styling for hotkey highlights of status bars", + "title": "/ui/theme-defs//status-styles/hotkey", + "$ref": "#/definitions/style" + } + }, + "additionalProperties": false + }, + "log-level-styles": { + "description": "Styles for each log message level.", + "title": "/ui/theme-defs//log-level-styles", + "type": "object", + "patternProperties": { + "(trace|debug5|debug4|debug3|debug2|debug|info|stats|notice|warning|error|critical|fatal|invalid)": { + "title": "/ui/theme-defs//log-level-styles/", + "$ref": "#/definitions/style" + } + }, + "additionalProperties": false + }, + "highlights": { + "description": "Styles for text highlights.", + "title": "/ui/theme-defs//highlights", + "type": "object", + "patternProperties": { + "([\\w\\-]+)": { + "title": "/ui/theme-defs//highlights/", + "type": "object", + "properties": { + "pattern": { + "title": "/ui/theme-defs//highlights//pattern", + "description": "The regular expression to highlight", + "type": "string" + }, + "style": { + "description": "The styling for the text that matches the associated pattern", + "title": "/ui/theme-defs//highlights//style", + "$ref": "#/definitions/style" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + "keymap-defs": { + "description": "Keymap definitions.", + "title": "/ui/keymap-defs", + "type": "object", + "patternProperties": { + "([\\w\\-]+)": { + "description": "The keymap definitions", + "title": "/ui/keymap-defs/", + "type": "object", + "patternProperties": { + "((?:x[0-9a-f]{2})+)": { + "description": "Map of key codes to commands to execute. The field names are the keys to be mapped using as a hexadecimal representation of the UTF-8 encoding. Each byte of the UTF-8 should start with an 'x' followed by the hexadecimal representation of the byte.", + "title": "/ui/keymap-defs//", + "type": "object", + "properties": { + "command": { + "title": "/ui/keymap-defs///command", + "description": "The command to execute for the given key sequence. Use a script to execute more complicated operations.", + "type": "string", + "pattern": "^[:|;].*", + "examples": [ + ":goto next hour" + ] + }, + "alt-msg": { + "title": "/ui/keymap-defs///alt-msg", + "description": "The help message to display after the key is pressed.", + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + "log": { + "description": "Log message settings", + "title": "/log", + "type": "object", + "properties": { + "watch-expressions": { + "description": "Log message watch expressions", + "title": "/log/watch-expressions", + "type": "object", + "patternProperties": { + "([\\w\\-]+)": { + "description": "A log message watch expression", + "title": "/log/watch-expressions/", + "type": "object", + "properties": { + "expr": { + "title": "/log/watch-expressions//expr", + "description": "The SQL expression to execute for each input line. If expression evaluates to true, a 'log message detected' event will be published.", + "type": "string" + }, + "enabled": { + "title": "/log/watch-expressions//enabled", + "description": "Indicates whether or not this expression should be evaluated during log processing.", + "type": "boolean" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + "global": { + "description": "Global variable definitions", + "title": "/global", + "type": "object", + "patternProperties": { + "(\\w+)": { + "title": "/global/", + "description": "A global variable definition. Global variables can be referenced in scripts, SQL statements, or commands.", + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false, + "definitions": { + "clip-commands": { + "title": "clip-commands", + "description": "Container for the commands used to read from and write to the system clipboard", + "type": "object", + "$$target": "#/definitions/clip-commands", + "properties": { + "write": { + "title": "/write", + "description": "The command used to write to the clipboard", + "type": "string", + "examples": [ + "pbcopy" + ] + }, + "read": { + "title": "/read", + "description": "The command used to read from the clipboard", + "type": "string", + "examples": [ + "pbpaste" + ] + } + }, + "additionalProperties": false + }, + "style": { + "title": "style", + "type": "object", + "$$target": "#/definitions/style", + "properties": { + "color": { + "title": "/color", + "description": "The foreground color value for this style. The value can be the name of an xterm color, the hexadecimal value, or a theme variable reference.", + "type": "string", + "examples": [ + "#fff", + "Green", + "$black" + ] + }, + "background-color": { + "title": "/background-color", + "description": "The background color value for this style. The value can be the name of an xterm color, the hexadecimal value, or a theme variable reference.", + "type": "string", + "examples": [ + "#2d2a2e", + "Green" + ] + }, + "underline": { + "title": "/underline", + "description": "Indicates that the text should be underlined.", + "type": "boolean" + }, + "bold": { + "title": "/bold", + "description": "Indicates that the text should be bolded.", + "type": "boolean" + } + }, + "additionalProperties": false + } + } +} diff --git a/docs/schemas/event-file-format-detected-v1.schema.json b/docs/schemas/event-file-format-detected-v1.schema.json new file mode 100644 index 0000000..edb8531 --- /dev/null +++ b/docs/schemas/event-file-format-detected-v1.schema.json @@ -0,0 +1,26 @@ +{ + "$id": "https://lnav.org/event-file-format-detected-v1.schema.json", + "title": "https://lnav.org/event-file-format-detected-v1.schema.json", + "$schema": "http://json-schema.org/draft-07/schema#", + "description": "Event fired when a log format is detected for a file.", + "properties": { + "$schema": { + "title": "/$schema", + "type": "string", + "examples": [ + "https://lnav.org/event-file-format-detected-v1.schema.json" + ] + }, + "filename": { + "title": "/filename", + "description": "The path of the file for which a matching format was found", + "type": "string" + }, + "format": { + "title": "/format", + "description": "The name of the format", + "type": "string" + } + }, + "additionalProperties": false +} diff --git a/docs/schemas/event-file-open-v1.schema.json b/docs/schemas/event-file-open-v1.schema.json new file mode 100644 index 0000000..76a6bc1 --- /dev/null +++ b/docs/schemas/event-file-open-v1.schema.json @@ -0,0 +1,21 @@ +{ + "$id": "https://lnav.org/event-file-open-v1.schema.json", + "title": "https://lnav.org/event-file-open-v1.schema.json", + "$schema": "http://json-schema.org/draft-07/schema#", + "description": "Event fired when a file is opened.", + "properties": { + "$schema": { + "title": "/$schema", + "type": "string", + "examples": [ + "https://lnav.org/event-file-open-v1.schema.json" + ] + }, + "filename": { + "title": "/filename", + "description": "The path of the file that was opened", + "type": "string" + } + }, + "additionalProperties": false +} diff --git a/docs/schemas/event-log-msg-detected-v1.schema.json b/docs/schemas/event-log-msg-detected-v1.schema.json new file mode 100644 index 0000000..30ec3a2 --- /dev/null +++ b/docs/schemas/event-log-msg-detected-v1.schema.json @@ -0,0 +1,57 @@ +{ + "$id": "https://lnav.org/event-log-msg-detected-v1.schema.json", + "title": "https://lnav.org/event-log-msg-detected-v1.schema.json", + "$schema": "http://json-schema.org/draft-07/schema#", + "description": "Event fired when a log message is detected by a watch expression.", + "properties": { + "$schema": { + "title": "/$schema", + "type": "string", + "examples": [ + "https://lnav.org/event-log-msg-detected-v1.schema.json" + ] + }, + "watch-name": { + "title": "/watch-name", + "description": "The name of the watch expression that matched this log message", + "type": "string" + }, + "filename": { + "title": "/filename", + "description": "The path of the file containing the log message", + "type": "string" + }, + "line-number": { + "title": "/line-number", + "description": "The line number in the file, starting from zero", + "type": "integer" + }, + "format": { + "title": "/format", + "description": "The name of the log format that matched this log message", + "type": "string" + }, + "timestamp": { + "title": "/timestamp", + "description": "The timestamp of the log message", + "type": "string" + }, + "values": { + "description": "The log message values captured by the log format", + "title": "/values", + "type": "object", + "patternProperties": { + "([\\w\\-]+)": { + "title": "/values/", + "type": [ + "boolean", + "integer", + "string" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false +} diff --git a/docs/schemas/event-session-loaded-v1.schema.json b/docs/schemas/event-session-loaded-v1.schema.json new file mode 100644 index 0000000..f64f051 --- /dev/null +++ b/docs/schemas/event-session-loaded-v1.schema.json @@ -0,0 +1,16 @@ +{ + "$id": "https://lnav.org/event-session-loaded-v1.schema.json", + "title": "https://lnav.org/event-session-loaded-v1.schema.json", + "$schema": "http://json-schema.org/draft-07/schema#", + "description": "Event fired when a session is loaded.", + "properties": { + "$schema": { + "title": "/$schema", + "type": "string", + "examples": [ + "https://lnav.org/event-session-loaded-v1.schema.json" + ] + } + }, + "additionalProperties": false +} diff --git a/docs/schemas/format-v1.schema.json b/docs/schemas/format-v1.schema.json new file mode 100644 index 0000000..7403379 --- /dev/null +++ b/docs/schemas/format-v1.schema.json @@ -0,0 +1,623 @@ +{ + "$id": "https://lnav.org/schemas/format-v1.schema.json", + "title": "https://lnav.org/schemas/format-v1.schema.json", + "$schema": "http://json-schema.org/draft-07/schema#", + "properties": { + "$schema": { + "title": "/$schema", + "description": "Specifies the type of this file", + "type": "string" + } + }, + "patternProperties": { + "(\\w+)": { + "description": "The definition of a log file format.", + "title": "/", + "type": "object", + "properties": { + "regex": { + "description": "The set of regular expressions used to match log messages", + "title": "//regex", + "type": "object", + "patternProperties": { + "([^/]+)": { + "description": "The set of patterns used to match log messages", + "title": "//regex/", + "type": "object", + "properties": { + "pattern": { + "title": "//regex//pattern", + "description": "The regular expression to match a log message and capture fields.", + "type": "string", + "minLength": 1 + }, + "module-format": { + "title": "//regex//module-format", + "description": "If true, this pattern will only be used to parse message bodies of container formats, like syslog", + "type": "boolean" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + "json": { + "title": "//json", + "description": "Indicates that log files are JSON-encoded (deprecated, use \"file-type\": \"json\")", + "type": "boolean" + }, + "convert-to-local-time": { + "title": "//convert-to-local-time", + "description": "Indicates that displayed timestamps should automatically be converted to local time", + "type": "boolean" + }, + "hide-extra": { + "title": "//hide-extra", + "description": "Specifies whether extra values in JSON logs should be displayed", + "type": "boolean" + }, + "multiline": { + "title": "//multiline", + "description": "Indicates that log messages can span multiple lines", + "type": "boolean" + }, + "timestamp-divisor": { + "title": "//timestamp-divisor", + "description": "The value to divide a numeric timestamp by in a JSON log.", + "type": [ + "integer", + "number" + ] + }, + "file-pattern": { + "title": "//file-pattern", + "description": "A regular expression that restricts this format to log files with a matching name", + "type": "string" + }, + "mime-types": { + "title": "//mime-types", + "description": "A list of mime-types this format should be used for", + "type": "array", + "items": { + "type": "string", + "enum": [ + "application/vnd.tcpdump.pcap" + ] + } + }, + "level-field": { + "title": "//level-field", + "description": "The name of the level field in the log message pattern", + "type": "string" + }, + "level-pointer": { + "title": "//level-pointer", + "description": "A regular-expression that matches the JSON-pointer of the level property", + "type": "string" + }, + "timestamp-field": { + "title": "//timestamp-field", + "description": "The name of the timestamp field in the log message pattern", + "type": "string" + }, + "subsecond-field": { + "title": "//subsecond-field", + "description": "The path to the property in a JSON-lines log message that contains the sub-second time value", + "type": "string" + }, + "subsecond-units": { + "title": "//subsecond-units", + "description": "The units of the subsecond-field property value", + "type": "string", + "enum": [ + "milli", + "micro", + "nano" + ] + }, + "time-field": { + "title": "//time-field", + "description": "The name of the time field in the log message pattern. This field should only be specified if the timestamp field only contains a date.", + "type": "string" + }, + "body-field": { + "title": "//body-field", + "description": "The name of the body field in the log message pattern", + "type": "string" + }, + "url": { + "title": "//url", + "description": "A URL with more information about this log format", + "type": [ + "array", + "string" + ], + "items": { + "type": "string" + } + }, + "title": { + "title": "//title", + "description": "The human-readable name for this log format", + "type": "string" + }, + "description": { + "title": "//description", + "description": "A longer description of this log format", + "type": "string" + }, + "timestamp-format": { + "title": "//timestamp-format", + "description": "An array of strptime(3)-like timestamp formats", + "type": "array", + "items": { + "type": "string" + } + }, + "module-field": { + "title": "//module-field", + "description": "The name of the module field in the log message pattern", + "type": "string" + }, + "opid-field": { + "title": "//opid-field", + "description": "The name of the operation-id field in the log message pattern", + "type": "string" + }, + "ordered-by-time": { + "title": "//ordered-by-time", + "description": "Indicates that the order of messages in the file is time-based.", + "type": "boolean" + }, + "level": { + "description": "The map of level names to patterns or integer values", + "title": "//level", + "type": "object", + "patternProperties": { + "(trace|debug[2345]?|info|stats|notice|warning|error|critical|fatal)": { + "title": "//level/", + "description": "The regular expression used to match the log text for this level. For JSON logs with numeric levels, this should be the number for the corresponding level.", + "type": [ + "integer", + "string" + ] + } + }, + "additionalProperties": false + }, + "value": { + "description": "The set of value definitions", + "title": "//value", + "type": "object", + "patternProperties": { + "([^/]+)": { + "description": "The set of values captured by the log message patterns", + "title": "//value/", + "type": "object", + "properties": { + "kind": { + "title": "//value//kind", + "description": "The type of data in the field", + "type": "string", + "enum": [ + "string", + "integer", + "float", + "boolean", + "json", + "struct", + "quoted", + "xml" + ] + }, + "collate": { + "title": "//value//collate", + "description": "The collating function to use for this column", + "type": "string" + }, + "unit": { + "description": "Unit definitions for this field", + "title": "//value//unit", + "type": "object", + "properties": { + "field": { + "title": "//value//unit/field", + "description": "The name of the field that contains the units for this field", + "type": "string" + }, + "scaling-factor": { + "description": "Transforms the numeric value by the given factor", + "title": "//value//unit/scaling-factor", + "type": "object", + "patternProperties": { + "([^/]+)": { + "title": "//value//unit/scaling-factor/", + "type": "object", + "properties": { + "op": { + "title": "//value//unit/scaling-factor//op", + "type": "string", + "enum": [ + "identity", + "multiply", + "divide" + ] + }, + "value": { + "title": "//value//unit/scaling-factor//value", + "type": "number" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + "identifier": { + "title": "//value//identifier", + "description": "Indicates whether or not this field contains an identifier that should be highlighted", + "type": "boolean" + }, + "foreign-key": { + "title": "//value//foreign-key", + "description": "Indicates whether or not this field should be treated as a foreign key for row in another table", + "type": "boolean" + }, + "hidden": { + "title": "//value//hidden", + "description": "Indicates whether or not this field should be hidden", + "type": "boolean" + }, + "action-list": { + "title": "//value//action-list", + "description": "Actions to execute when this field is clicked on", + "type": "array", + "items": { + "type": "string" + } + }, + "rewriter": { + "title": "//value//rewriter", + "description": "A command that will rewrite this field when pretty-printing", + "type": "string", + "examples": [ + ";SELECT :sc_status || ' (' || (SELECT message FROM http_status_codes WHERE status = :sc_status) || ') '" + ] + }, + "description": { + "title": "//value//description", + "description": "A description of the field", + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + "tags": { + "description": "The tags to automatically apply to log messages", + "title": "//tags", + "type": "object", + "patternProperties": { + "([\\w:;\\._\\-]+)": { + "description": "The name of the tag to apply", + "title": "//tags/", + "type": "object", + "properties": { + "paths": { + "description": "Restrict tagging to the given paths", + "title": "//tags//paths", + "type": "array", + "items": { + "type": "object", + "properties": { + "glob": { + "title": "//tags//paths/glob", + "description": "The glob to match against file paths", + "type": "string", + "examples": [ + "*/system.log*" + ] + } + }, + "additionalProperties": false + } + }, + "pattern": { + "title": "//tags//pattern", + "description": "The regular expression to match against the body of the log message", + "type": "string", + "examples": [ + "\\w+ is down" + ] + }, + "description": { + "title": "//tags//description", + "description": "A description of this tag", + "type": "string" + }, + "level": { + "title": "//tags//level", + "description": "Constrain hits to log messages with this level", + "type": "string", + "enum": [ + "trace", + "debug5", + "debug4", + "debug3", + "debug2", + "debug", + "info", + "stats", + "notice", + "warning", + "error", + "critical", + "fatal" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + "action": { + "title": "//action", + "type": "object", + "patternProperties": { + "(\\w+)": { + "title": "//action/", + "type": [ + "string", + "object" + ], + "properties": { + "label": { + "title": "//action//label", + "type": "string" + }, + "capture-output": { + "title": "//action//capture-output", + "type": "boolean" + }, + "cmd": { + "title": "//action//cmd", + "type": "array", + "items": { + "type": "string" + } + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + "sample": { + "description": "An array of sample log messages to be tested against the log message patterns", + "title": "//sample", + "type": "array", + "items": { + "type": "object", + "properties": { + "description": { + "title": "//sample/description", + "description": "A description of this sample.", + "type": "string" + }, + "line": { + "title": "//sample/line", + "description": "A sample log line that should match a pattern in this format.", + "type": "string" + }, + "level": { + "title": "//sample/level", + "description": "The expected level for this sample log line.", + "type": "string", + "enum": [ + "trace", + "debug5", + "debug4", + "debug3", + "debug2", + "debug", + "info", + "stats", + "notice", + "warning", + "error", + "critical", + "fatal" + ] + } + }, + "additionalProperties": false + } + }, + "line-format": { + "description": "The display format for JSON-encoded log messages", + "title": "//line-format", + "type": "array", + "items": { + "type": [ + "string", + "object" + ], + "properties": { + "field": { + "title": "//line-format/field", + "description": "The name of the field to substitute at this position", + "type": "string" + }, + "default-value": { + "title": "//line-format/default-value", + "description": "The default value for this position if the field is null", + "type": "string" + }, + "timestamp-format": { + "title": "//line-format/timestamp-format", + "description": "The strftime(3) format for this field", + "type": "string", + "minLength": 1 + }, + "min-width": { + "title": "//line-format/min-width", + "description": "The minimum width of the field", + "type": "integer", + "minimum": 0 + }, + "max-width": { + "title": "//line-format/max-width", + "description": "The maximum width of the field", + "type": "integer", + "minimum": 0 + }, + "align": { + "title": "//line-format/align", + "description": "Align the text in the column to the left or right side", + "type": "string", + "enum": [ + "left", + "right" + ] + }, + "overflow": { + "title": "//line-format/overflow", + "description": "Overflow style", + "type": "string", + "enum": [ + "abbrev", + "truncate", + "dot-dot" + ] + }, + "text-transform": { + "title": "//line-format/text-transform", + "description": "Text transformation", + "type": "string", + "enum": [ + "none", + "uppercase", + "lowercase", + "capitalize" + ] + } + }, + "additionalProperties": false + } + }, + "search-table": { + "description": "Search tables to automatically define for this log format", + "title": "//search-table", + "type": "object", + "patternProperties": { + "(\\w+)": { + "description": "The set of search tables to be automatically defined", + "title": "//search-table/", + "type": "object", + "properties": { + "pattern": { + "title": "//search-table//pattern", + "description": "The regular expression for this search table.", + "type": "string" + }, + "glob": { + "title": "//search-table//glob", + "description": "Glob pattern used to constrain hits to messages that match the given pattern.", + "type": "string" + }, + "level": { + "title": "//search-table//level", + "description": "Constrain hits to log messages with this level", + "type": "string", + "enum": [ + "trace", + "debug5", + "debug4", + "debug3", + "debug2", + "debug", + "info", + "stats", + "notice", + "warning", + "error", + "critical", + "fatal" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + "highlights": { + "description": "The set of highlight definitions", + "title": "//highlights", + "type": "object", + "patternProperties": { + "([^/]+)": { + "description": "The definition of a highlight", + "title": "//highlights/", + "type": "object", + "properties": { + "pattern": { + "title": "//highlights//pattern", + "description": "A regular expression to highlight in logs of this format.", + "type": "string" + }, + "color": { + "title": "//highlights//color", + "description": "The color to use when highlighting this pattern.", + "type": "string" + }, + "background-color": { + "title": "//highlights//background-color", + "description": "The background color to use when highlighting this pattern.", + "type": "string" + }, + "underline": { + "title": "//highlights//underline", + "description": "Highlight this pattern with an underline.", + "type": "boolean" + }, + "blink": { + "title": "//highlights//blink", + "description": "Highlight this pattern by blinking.", + "type": "boolean" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + "file-type": { + "title": "//file-type", + "description": "The type of file that contains the log messages", + "type": "string", + "enum": [ + "text", + "json", + "csv" + ] + }, + "max-unrecognized-lines": { + "title": "//max-unrecognized-lines", + "description": "The maximum number of lines in a file to use when detecting the format", + "type": "integer", + "minimum": 1 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false +} diff --git a/docs/source/_ext/__init__.py b/docs/source/_ext/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/docs/source/_ext/lnavlexer.py b/docs/source/_ext/lnavlexer.py new file mode 100644 index 0000000..2509a5f --- /dev/null +++ b/docs/source/_ext/lnavlexer.py @@ -0,0 +1,26 @@ + +__all__ = ['LnavCommandLexer'] + +import re + +from pygments.token import Whitespace, Text, Keyword, Literal +from pygments.lexers._mapping import LEXERS +from pygments.lexers.python import RegexLexer + +class LnavCommandLexer(RegexLexer): + name = 'lnav' + + flags = re.IGNORECASE + tokens = { + 'root': [ + (r'\s+', Whitespace), + (r':[\w\-]+', Keyword), + (r'\<[\w\-]+\>', Literal.String.Doc), + (r'.', Text), + ] + } + +def setup(app): + LEXERS['LnavCommandLexer'] = ( + '_ext.lnavlexer', 'lnav', ('lnav',), ('*.lnav',), ('text/lnav',)) + app.add_lexer('lnav', LnavCommandLexer) diff --git a/docs/source/_static/theme_overrides.css b/docs/source/_static/theme_overrides.css new file mode 100644 index 0000000..4fc1c9f --- /dev/null +++ b/docs/source/_static/theme_overrides.css @@ -0,0 +1,56 @@ +/* override table width restrictions */ +@media screen and (min-width: 767px) { + + .wy-table-responsive table td { + /* !important prevents the common CSS stylesheets from overriding + this as on RTD they are loaded after this stylesheet */ + white-space: normal !important; + } + +} + +th p { + margin-bottom: 0px; +} + +.wy-nav-content { + max-width: 900px; +} + +table.query-results p { + font-size: 0.9em !important; +} + +DL DT:target, :target > H2, :target > H3, span:target + H2, span:target + H3 { + border: 0 !important; + border-bottom: 1px solid #d3d381 !important; + background: #ffc !important; + /* padding: 1em !important;*/ +} + +DL DT { + border: 0 !important; + background: inherit !important; + border-bottom: 1px solid #c3c0ee !important; + display: block !important; +} + +kbd { + padding: 0.1em 0.6em; + border: 1px solid #ccc; + font-size: 11px; + font-family: Arial, Helvetica, sans-serif; + background-color: #f7f7f7; + color: #333; + -moz-box-shadow: 0 1px 0px rgba(0, 0, 0, 0.2), 0 0 0 2px #ffffff inset; + -webkit-box-shadow: 0 1px 0px rgba(0, 0, 0, 0.2), 0 0 0 2px #ffffff inset; + box-shadow: 0 1px 0px rgba(0, 0, 0, 0.2), 0 0 0 2px #ffffff inset; + -moz-border-radius: 3px; + -webkit-border-radius: 3px; + border-radius: 3px; + display: inline-block; + margin: 0 0.1em 0.1em 0.1em; + text-shadow: 0 1px 0 #fff; + line-height: 1.4; + white-space: nowrap; +} diff --git a/docs/source/cli.rst b/docs/source/cli.rst new file mode 100644 index 0000000..565e54f --- /dev/null +++ b/docs/source/cli.rst @@ -0,0 +1,168 @@ +.. _cli: + +Command Line Interface +====================== + +There are two command-line interfaces provided by **lnav**, one for viewing +files and one for managing **lnav**'s configuration. The file viewing mode is +the default and is all that most people will need. The management mode can +be useful for those that are developing log file formats and is activated by +passing the :option:`-m` option as the first argument. + +File Viewing Mode +----------------- + +The following options can be used when starting **lnav**. There are not +many flags because the majority of the functionality is accessed using +the :option:`-c` option to execute :ref:`commands` or +:ref:`SQL queries`. + +Options +^^^^^^^ + +.. option:: -h + + Print these command-line options and exit. + +.. option:: -H + + Start lnav and switch to the help view. + +.. option:: -C + + Check the given files against the configuration, report any errors, and + exit. This option can be helpful for validating that a log format is + well-formed. + +.. option:: -c + + Execute the given lnav command, SQL query, or lnav script. The + argument must be prefixed with the character used to enter the prompt + to distinguish between the different types (i.e. ':', ';', '|'). + This option can be given multiple times. + +.. option:: -f + + Execute the given command file. This option can be given multiple times. + +.. option:: -I + + Add a configuration directory. + +.. option:: -i + + Install the format files in the :file:`.lnav/formats/` directory. + Individual files will be installed in the :file:`installed` + directory and git repositories will be cloned with a directory + name based on their repository URI. + +.. option:: -u + + Update formats installed from git repositories. + +.. option:: -d + + Write debug messages to the given file. + +.. option:: -n + + Run without the curses UI (headless mode). + +.. option:: -N + + Do not open the default syslog file if no files are given. + +.. option:: -r + + Recursively load files from the given base directories. + +.. option:: -t + + Prepend timestamps to the lines of data being read in on the standard input. + +.. option:: -w + + Write the contents of the standard input to this file. + +.. option:: -V + + Print the version of lnav. + +.. option:: -q + + Do not print the log messages after executing all of the commands. + + +Management Mode (v0.11.0+) +-------------------------- + +The management CLI mode provides functionality for query **lnav**'s log +format definitions. + +Options +^^^^^^^ + +.. option:: -m + + Switch to management mode. This must be the first option passed on the + command-line. + +Subcommands +^^^^^^^^^^^ + +.. option:: regex101 import [] + + Convert a regex101.com entry into a skeleton log format file. + +.. option:: format regex push + + Push a log format regular expression to regex101.com . + +.. option:: format regex pull + + Pull changes to a regex that was previously pushed to regex101.com . + +Environment Variables +--------------------- + +.. envvar:: XDG_CONFIG_HOME + + If this variable is set, lnav will use this directory to store its + configuration in a sub-directory named :file:`lnav`. + +.. envvar:: HOME + + If :envvar:`XDG_CONFIG_HOME` is not set, lnav will use this directory + to store its configuration in a sub-directory named :file:`.lnav`. + +.. envvar:: APPDATA + + On Windows, lnav will use this directory instead of HOME + to store its configuration in a sub-directory named :file:`.lnav`. + +.. envvar:: TZ + + The timezone setting is used in some log formats to convert UTC timestamps + to the local timezone. + + +Examples +-------- + + To load and follow the system syslog file: + + .. prompt:: bash + + lnav + + To load all of the files in :file:`/var/log`: + + .. prompt:: bash + + lnav /var/log + + To watch the output of make with timestamps prepended: + + .. prompt:: bash + + make 2>&1 | lnav -t diff --git a/docs/source/commands.rst b/docs/source/commands.rst new file mode 100644 index 0000000..d83a07e --- /dev/null +++ b/docs/source/commands.rst @@ -0,0 +1,132 @@ +.. role:: lnavcmd(code) + :language: lnav + :class: highlight + +.. _commands: + +Commands +======== + +Commands provide access to some of the more advanced features in **lnav**, like +:ref:`filtering` and +:ref:`"search tables"`. You can activate the command +prompt by pressing the :kbd:`:` key. At the prompt, you can start typing +in the desired command and/or double-tap :kbd:`TAB` to activate +auto-completion and show the available commands. To guide you in the usage of +the commands, a help window will appear above the command prompt with an +explanation of the command and its parameters (if it has any). For example, +the screenshot below shows the help for the :code:`:open` command: + +.. figure:: open-help.png + :align: center + :figwidth: 50% + + Screenshot of the online help for the :code:`:open` command. + +In addition to online help, many commands provide a preview of the effects that +the command will have. This preview will activate shortly after you have +finished typing, but before you have pressed :kbd:`Enter` to execute the +command. For example, the :code:`:open` command will show a preview of the +first few lines of the file given as its argument: + +.. figure:: open-preview.png + :align: center + + Screenshot of the preview shown for the :code:`:open` command. + +The :lnavcmd:`:filter-out pattern` command is another instance where the preview behavior +can help you craft the correct command-line. This command takes a PCRE2 regular +expression that specifies the log messages that should be filtered out of the +view. The preview for this command will highlight the portion of the log +messages that match the expression in red. Thus, you can be certain that the +regular expression is matching the log messages you are interested in before +committing the filter. The following screenshot shows an example of this +preview behavior for the string "launchd": + +.. figure:: filter-out-preview.png + :align: center + + Screenshot showing the preview for the :code:`:filter-out` command. + +Any errors detected during preview will be shown in the status bar right above +the command prompt. For example, an attempt to open an unknown file will show +an error message in the status bar, like so: + +.. figure:: open-error.png + :align: center + + Screenshot of the error shown when trying to open a non-existent file. + +.. tip:: + + Note that almost all commands support TAB-completion for their arguments. + So, if you are in doubt as to what to type for an argument, you can double- + tap the :kbd:`TAB` key to get suggestions. For example, the + TAB-completion for the :code:`filter-in` command will suggest words that are + currently displayed in the view. + +.. note:: The following commands can be disabled by setting the ``LNAVSECURE`` + environment variable before executing the **lnav** binary: + + - :code:`:open` + - :code:`:pipe-to` + - :code:`:pipe-line-to` + - :code:`:write-*-to` + + This makes it easier to run **lnav** in restricted environments without the + risk of privilege escalation. + +I/O Commands +------------ + +Anonymization +^^^^^^^^^^^^^ + +Anonymization is the process of removing identifying information from content +to make it safer for sharing with others. For example, an IP address can +often be used to uniquely identify an entity. Substituting all instances of +a particular IP with the same dummy value would remove the identifying data +without losing statistical accuracy. **lnav** has built-in support for +anonymization through the :code:`--anonymize` flag on the :code:`:write-*` +collection of commands. While the anonymization process should catch most + + :IPv4 Addresses: Are replaced with addresses in the :code:`10.0.0.0/8` range. + + :IPv6 Addresses: Are replaced with addresses in the :code:`2001:db8::/32` range. + + :URL User Names: Are replaced with a random animal name. + + :URL Passwords: Are replaced with a hash of the input password. + + :URL Hosts: Are replaced with a random name under the example.com domain. + + :URL Paths: Are recursively examined for substitution. + + :URL Query Strings: Are recursively examined for substitution. + + :URL Fragments: Are recursively examined for substitution. + + :Paths: Are recursively examined for substitution. + + :Credit Card Numbers: Are replaced with a 16 digit hash of the input number. + + :MAC Addresses: Are replaced with addresses in the :code:`00:00:5E:00:53:00` range. + + :Hex Dumps: Are replaced with a hash of the input replicated to the size of input. + + :Email User Names: Are replaced with a random animal name. + + :Email Host Names: Are replaced with a random name under the example.com domain. + + :Words: Are replaced with a random word with a matching case style. + + :Quoted Strings: Are recursively examined for substitution. + + :UUID: Are replaced with a hash of the input. + + :XML Attribute Values: Are recursively examined for substitution. + +Reference +--------- + +.. include:: ../../src/internals/cmd-ref.rst diff --git a/docs/source/conf.py b/docs/source/conf.py new file mode 100644 index 0000000..5f74b8c --- /dev/null +++ b/docs/source/conf.py @@ -0,0 +1,460 @@ +# -*- coding: utf-8 -*- +# +# lnav documentation build configuration file, created by +# sphinx-quickstart on Fri Jul 12 21:09:39 2013. +# +# This file is execfile()d with the current directory set to its containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys, os + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +this_dir = os.path.abspath('.') +src_dir = os.path.join(this_dir, "..", "..", "src") +sys.path.insert(0, this_dir) +sys.path.insert(0, src_dir) + +import format2csv + +format2csv.main(["", + os.path.join(this_dir, "format-table.csv"), + os.path.join(src_dir, "formats")]) + +import re +from pygments.lexer import RegexLexer, words +from pygments.token import Punctuation, Whitespace, Text, Comment, Operator, \ + Keyword, Name, String, Literal, Number, Generic +from sphinx.highlighting import lexers + + +class CustSqliteLexer(RegexLexer): + name = 'custsqlite' + + flags = re.IGNORECASE + tokens = { + 'root': [ + (r'\s+', Text), + (r'--.*\n?', Comment.Single), + (r'#.*\n?', Comment.Single), + (r'/\*', Comment.Multiline, 'multiline-comments'), + (words(( + 'ABORT', + 'ACTION', + 'ADD', + 'AFTER', + 'ALL', + 'ALTER', + 'ALWAYS', + 'ANALYZE', + 'AND', + 'AS', + 'ASC', + 'ATTACH', + 'AUTOINCREMENT', + 'BEFORE', + 'BEGIN', + 'BETWEEN', + 'BY', + 'CASCADE', + 'CASE', + 'CAST', + 'CHECK', + 'COLLATE', + 'COLUMN', + 'COMMIT', + 'CONFLICT', + 'CONSTRAINT', + 'CREATE', + 'CROSS', + 'CURRENT', + 'CURRENT_DATE', + 'CURRENT_TIME', + 'CURRENT_TIMESTAMP', + 'DATABASE', + 'DEFAULT', + 'DEFERRABLE', + 'DEFERRED', + 'DELETE', + 'DESC', + 'DETACH', + 'DISTINCT', + 'DO', + 'DROP', + 'EACH', + 'ELSE', + 'END', + 'ESCAPE', + 'EXCEPT', + 'EXCLUDE', + 'EXCLUSIVE', + 'EXISTS', + 'EXPLAIN', + 'FAIL', + 'FILTER', + 'FIRST', + 'FOLLOWING', + 'FOR', + 'FOREIGN', + 'FROM', + 'FULL', + 'GENERATED', + 'GLOB', + 'GROUP', + 'GROUPS', + 'HAVING', + 'IF', + 'IGNORE', + 'IMMEDIATE', + 'IN', + 'INDEX', + 'INDEXED', + 'INITIALLY', + 'INNER', + 'INSERT', + 'INSTEAD', + 'INTERSECT', + 'INTO', + 'IS', + 'ISNULL', + 'JOIN', + 'KEY', + 'LAST', + 'LEFT', + 'LIKE', + 'LIMIT', + 'MATCH', + 'NATURAL', + 'NO', + 'NOT', + 'NOTHING', + 'NOTNULL', + 'NULL', + 'NULLS', + 'OF', + 'OFFSET', + 'ON', + 'OR', + 'ORDER', + 'OTHERS', + 'OUTER', + 'OVER', + 'PARTITION', + 'PLAN', + 'PRAGMA', + 'PRECEDING', + 'PRIMARY', + 'QUERY', + 'RAISE', + 'RANGE', + 'RECURSIVE', + 'REFERENCES', + 'REGEXP', + 'REINDEX', + 'RELEASE', + 'RENAME', + 'REPLACE', + 'RESTRICT', + 'RIGHT', + 'ROLLBACK', + 'ROW', + 'ROWS', + 'SAVEPOINT', + 'SELECT', + 'SET', + 'TABLE', + 'TEMP', + 'TEMPORARY', + 'THEN', + 'TIES', + 'TO', + 'TRANSACTION', + 'TRIGGER', + 'UNBOUNDED', + 'UNION', + 'UNIQUE', + 'UPDATE', + 'USING', + 'VACUUM', + 'VALUES', + 'VIEW', + 'VIRTUAL', + 'WHEN', + 'WHERE', + 'WINDOW', + 'WITH', + 'WITHOUT'), suffix=r'\b'), + Keyword), + (words(( + 'ARRAY', 'BIGINT', 'BINARY', 'BIT', 'BLOB', 'BOOLEAN', 'CHAR', + 'CHARACTER', 'DATE', 'DEC', 'DECIMAL', 'FLOAT', 'INT', 'INTEGER', + 'INTERVAL', 'NUMBER', 'NUMERIC', 'REAL', 'SERIAL', 'SMALLINT', + 'VARCHAR', 'VARYING', 'INT8', 'SERIAL8', 'TEXT'), suffix=r'\b'), + Name.Builtin), + (r'[+*/<>=~!@#%^&|`?-]', Operator), + (r'[0-9]+', Number.Integer), + # TODO: Backslash escapes? + (r"'(''|[^'])*'", String.Single), + (r'"(""|[^"])*"', String.Symbol), # not a real string literal in ANSI SQL + (r'[a-z_][\w$]*', Name), # allow $s in strings for Oracle + (r'\$[a-z_]+', Name), + (r'[;:()\[\],.]', Punctuation) + ], + 'multiline-comments': [ + (r'/\*', Comment.Multiline, 'multiline-comments'), + (r'\*/', Comment.Multiline, '#pop'), + (r'[^/*]+', Comment.Multiline), + (r'[/*]', Comment.Multiline) + ] + } + + def analyse_text(text): + return 0.01 + + +lexers['custsqlite'] = CustSqliteLexer(startinline=True) + +# -- General configuration ----------------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. +# needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be extensions +# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +extensions = [ + 'sphinx.ext.coverage', + "sphinx_rtd_theme", + 'sphinx-jsonschema', + 'sphinx-prompt', + '_ext.lnavlexer', +] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +# source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'lnav' +copyright = u'2022, Tim Stack' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = '0.11' +# The full version, including alpha/beta/rc tags. +release = '0.11.1' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +# today = '' +# Else, today_fmt is used as the format for a strftime call. +# today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = [] + +# The reST default role (used for this markup: `text`) to use for all documents. +# default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +# add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +# add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +# show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +# modindex_common_prefix = [] + +# -- Options for HTML output --------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = 'sphinx_rtd_theme' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +html_theme_options = { +} + +# Add any paths that contain custom themes here, relative to this directory. +# html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +# html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +# html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +# html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +# html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + + +def setup(app): + app.add_css_file('theme_overrides.css') + + +# html_context = { +# 'css_files': [ +# '_static/theme_overrides.css', # override wide tables in RTD theme +# ], +# } + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +# html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +# html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +# html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +# html_additional_pages = {} + +# If false, no module index is generated. +# html_domain_indices = True + +# If false, no index is generated. +# html_use_index = True + +# If true, the index is split into individual pages for each letter. +# html_split_index = False + +# If true, links to the reST sources are added to the pages. +# html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +# html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +# html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +# html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +# html_file_suffix = None + +# Output file base name for HTML help builder. +htmlhelp_basename = 'lnavdoc' + +# -- Options for LaTeX output -------------------------------------------------- + +latex_elements = { + # The paper size ('letterpaper' or 'a4paper'). + # 'papersize': 'letterpaper', + + # The font size ('10pt', '11pt' or '12pt'). + # 'pointsize': '10pt', + + # Additional stuff for the LaTeX preamble. + # 'preamble': '', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass [howto/manual]). +latex_documents = [ + ('index', 'lnav.tex', u'lnav Documentation', + u'Tim Stack', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +# latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +# latex_use_parts = False + +# If true, show page references after internal links. +# latex_show_pagerefs = False + +# If true, show URL addresses after external links. +# latex_show_urls = False + +# Documents to append as an appendix to all manuals. +# latex_appendices = [] + +# If false, no module index is generated. +# latex_domain_indices = True + + +# -- Options for manual page output -------------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ('index', 'lnav', u'lnav Documentation', + [u'Tim Stack'], 1) +] + +# If true, show URL addresses after external links. +# man_show_urls = False + + +# -- Options for Texinfo output ------------------------------------------------ + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + ('index', 'lnav', u'lnav Documentation', + u'Tim Stack', 'lnav', 'One line description of project.', + 'Miscellaneous'), +] + +# Documents to append as an appendix to all manuals. +# texinfo_appendices = [] + +# If false, no module index is generated. +# texinfo_domain_indices = True + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +# texinfo_show_urls = 'footnote' diff --git a/docs/source/config.rst b/docs/source/config.rst new file mode 100644 index 0000000..5294e52 --- /dev/null +++ b/docs/source/config.rst @@ -0,0 +1,270 @@ + +.. _Configuration: + +Configuration +============= + +The configuration for **lnav** is stored in the following JSON files in +:file:`~/.lnav`: + +* :file:`config.json` -- Contains local customizations that were done using the + :code:`:config` command. +* :file:`configs/default/*.json` -- The default configuration files that are + built into lnav are written to this directory with :file:`.sample` appended. + Removing the :file:`.sample` extension and editing the file will allow you to + do basic customizations. +* :file:`configs/installed/*.json` -- Contains configuration files installed + using the :option:`-i` flag (e.g. :code:`$ lnav -i /path/to/config.json`). +* :file:`configs/*/*.json` -- Other directories that contain :file:`*.json` + files will be loaded on startup. This structure is convenient for installing + **lnav** configurations, like from a git repository. + +A valid **lnav** configuration file must contain an object with the +:code:`$schema` property, like so: + +.. code-block:: json + + { + "$schema": "https://lnav.org/schemas/config-v1.schema.json" + } + +.. note:: + + Log format definitions are stored separately in the :file:`~/.lnav/formats` + directly. See the :ref:`Log Formats` chapter for more + information. + + +Options +------- + +The following configuration options can be used to customize the **lnav** UI to +your liking. The options can be changed using the :code:`:config` command. + +.. jsonschema:: ../schemas/config-v1.schema.json#/properties/ui/properties/keymap + +.. jsonschema:: ../schemas/config-v1.schema.json#/properties/ui/properties/theme + +.. jsonschema:: ../schemas/config-v1.schema.json#/properties/ui/properties/clock-format + +.. jsonschema:: ../schemas/config-v1.schema.json#/properties/ui/properties/dim-text + +.. jsonschema:: ../schemas/config-v1.schema.json#/properties/ui/properties/default-colors + + +.. _themes: + +Theme Definitions +----------------- + +User Interface themes are defined in a JSON configuration file. A theme is +made up of the style definitions for different types of text in the UI. A +:ref:`definition` can include the foreground/background colors +and the bold/underline attributes. The style definitions are broken up into +multiple categories for the sake of organization. To make it easier to write +a definition, a theme can define variables that can be referenced as color +values. + +Variables +^^^^^^^^^ + +The :code:`vars` object in a theme definition contains the mapping of variable +names to color values. These variables can be referenced in style definitions +by prefixing them with a dollar-sign (e.g. :code:`$black`). The following +variables can also be defined to control the values of the ANSI colors that +are log messages or plain text: + +.. csv-table:: ANSI colors + :header: "Variable Name", "ANSI Escape" + + "black", "ESC[30m" + "red", "ESC[31m" + "green", "ESC[32m" + "yellow", "ESC[33m" + "blue", "ESC[34m" + "magenta", "ESC[35m" + "cyan", "ESC[36m" + "white", "ESC[37m" + +Specifying Colors +^^^^^^^^^^^^^^^^^ + +Colors can be specified using hexadecimal notation by starting with a hash +(e.g. :code:`#aabbcc`) or using a color name as found at +http://jonasjacek.github.io/colors/. If colors are not specified for a style, +the values from the :code:`styles/text` definition. + +.. note:: + + When specifying colors in hexadecimal notation, you do not need to have an + exact match in the XTerm 256 color palette. A best approximation will be + picked based on the `CIEDE2000 `_ + color difference algorithm. + + + +Example +^^^^^^^ + +The following example sets the black/background color for text to a dark grey +using a variable and sets the foreground to an off-white. This theme is +incomplete, but it works enough to give you an idea of how a theme is defined. +You can copy the code block, save it to a file in +:file:`~/.lnav/configs/installed/` and then activate it by executing +:code:`:config /ui/theme example` in lnav. For a more complete theme +definition, see one of the definitions built into **lnav**, like +`monocai `_. + + .. code-block:: json + + { + "$schema": "https://lnav.org/schemas/config-v1.schema.json", + "ui": { + "theme-defs": { + "example1": { + "vars": { + "black": "#2d2a2e" + }, + "styles": { + "text": { + "color": "#f6f6f6", + "background-color": "$black" + } + } + } + } + } + } + +Reference +^^^^^^^^^ + +.. jsonschema:: ../schemas/config-v1.schema.json#/properties/ui/properties/theme-defs/patternProperties/([\w\-]+)/properties/vars + +.. jsonschema:: ../schemas/config-v1.schema.json#/properties/ui/properties/theme-defs/patternProperties/([\w\-]+)/properties/styles + +.. jsonschema:: ../schemas/config-v1.schema.json#/properties/ui/properties/theme-defs/patternProperties/([\w\-]+)/properties/syntax-styles + +.. jsonschema:: ../schemas/config-v1.schema.json#/properties/ui/properties/theme-defs/patternProperties/([\w\-]+)/properties/status-styles + +.. jsonschema:: ../schemas/config-v1.schema.json#/properties/ui/properties/theme-defs/patternProperties/([\w\-]+)/properties/log-level-styles + +.. _theme_style: + +.. jsonschema:: ../schemas/config-v1.schema.json#/definitions/style + + +.. _keymaps: + +Keymap Definitions +------------------ + +Keymaps in **lnav** map a key sequence to a command to execute. When a key is +pressed, it is converted into a hex-encoded string that is looked up in the +keymap. The :code:`command` value associated with the entry in the keymap is +then executed. Note that the "command" can be an **lnav** +:ref:`command`, a :ref:`SQL statement/query`, or an +**lnav** script. If an :code:`alt-msg` value is included in the entry, the +bottom-right section of the UI will be updated with the help text. + +.. note:: + + Not all functionality is available via commands or SQL at the moment. Also, + some hotkeys are not implemented via keymaps. + +Key Sequence Encoding +^^^^^^^^^^^^^^^^^^^^^ + +Key presses are converted into a hex-encoded string that is used to lookup an +entry in the keymap. Each byte of the keypress value is formatted as an +:code:`x` followed by the hex-encoding in lowercase. For example, the encoding +for the £ key would be :code:`xc2xa3`. To make it easier to discover the +encoding for unassigned keys, **lnav** will print in the command prompt the +:code:`:config` command and +`JSON-Pointer `_ for assigning a command +to the key. + +.. figure:: key-encoding-prompt.png + :align: center + + Screenshot of the command prompt when an unassigned key is pressed. + +.. note:: + + Since **lnav** is a terminal application, it can only receive keypresses that + can be represented as characters or escape sequences. For example, it cannot + handle the press of a modifier key. + +Reference +^^^^^^^^^ + +.. jsonschema:: ../schemas/config-v1.schema.json#/properties/ui/properties/keymap-defs/patternProperties/([\w\-]+) + + +Log Handling +------------ + +The handling of logs is largely determined by the +:ref:`log file formats`, this section covers options that are not +specific to a particular format. + +Watch Expressions (v0.11.0+) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Watch expressions can be used to fire an event when a log message matches a +condition. You can then install a listener for these events and trigger an +action to be performed. For example, to automate filtering based on +identifiers, a watch expression can match messages that mention the ID and then +a trigger can install a filter for that ID. Creating a watch expression is +done by adding an entry into the :code:`/log/watch-expressions` configuration +tree. For example, to create a watch named "dhcpdiscover" that matches +DHCPDISCOVER messages from the :code:`dhclient` daemon, you would run the +following: + +.. code-block:: lnav + + :config /log/watch-expressions/dhcpdiscover/expr :log_procname = 'dhclient' AND startswith(:log_body, 'DHCPDISCOVER') + +The watch expression can refer to column names in the log message by prefixing +them with a colon. The expression is evaluated by passing the log message +fields as bound parameters and not against a table. The easiest way to test +out an expression is with the :ref:`mark_expr` command, since it will behave +similarly. After changing the configuration, you'll need to restart lnav +for the effect to take place. You can then query the :code:`lnav_events` +table to see any generated +:code:`https://lnav.org/event-log-msg-detected-v1.schema.json` events from the +logs that were loaded: + +.. code-block:: custsqlite + + ;SELECT * FROM lnav_events + +From there, you can create a SQLite trigger on the :code:`lnav_events` table +that will examine the event contents and perform an action. See the +:ref:`Events` section for more information on handling events. + +Reference +^^^^^^^^^ + +.. jsonschema:: ../schemas/config-v1.schema.json#/properties/log/properties/watch-expressions/patternProperties/([\w\-]+) + +.. _tuning: + +Tuning +------ + +The following configuration options can be used to tune the internals of +**lnav** to your liking. The options can be changed using the :code:`:config` +command. + +.. jsonschema:: ../schemas/config-v1.schema.json#/properties/tuning/properties/archive-manager + +.. jsonschema:: ../schemas/config-v1.schema.json#/properties/tuning/properties/clipboard + +.. jsonschema:: ../schemas/config-v1.schema.json#/definitions/clip-commands + +.. jsonschema:: ../schemas/config-v1.schema.json#/properties/tuning/properties/file-vtab + +.. jsonschema:: ../schemas/config-v1.schema.json#/properties/tuning/properties/logfile + +.. jsonschema:: ../schemas/config-v1.schema.json#/properties/tuning/properties/remote/properties/ssh diff --git a/docs/source/cookbook.rst b/docs/source/cookbook.rst new file mode 100644 index 0000000..3eb0ffd --- /dev/null +++ b/docs/source/cookbook.rst @@ -0,0 +1,104 @@ + +.. _Cookbook: + +Cookbook +======== + +This chapter contains recipes for common tasks that can be done in **lnav**. +These recipes can be used as a starting point for your own needs after some +adaptation. + + +Log Formats +----------- + +TBD + +Defining a New Format +^^^^^^^^^^^^^^^^^^^^^ + +TBD + + +Annotating Logs +--------------- + +Log messages can be annotated in a couple of different ways in **lnav** to help +you get organized. + +Create partitions for Linux boots +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +When digging through logs that can be broken up into multiple sections, +**lnav**'s :ref:`partitioning feature` can be used to keep track +of which section you are in. For example, if a collection of Linux logs +covered multiple boots, the following script could be used to create partitions +for each boot. After the partition name is set for the log messages, the +current name will show up in the top status bar next to the current time. + +.. literalinclude:: ../../src/scripts/partition-by-boot.lnav + :language: custsqlite + :caption: partition-by-boot.lnav + :linenos: + +Tagging SSH log messages +^^^^^^^^^^^^^^^^^^^^^^^^ + +Log messages can be tagged interactively with the :ref:`:tag` command or +programmatically using the :ref:`sql-ext`. This example uses a script to +search for interesting SSH messages and automatically adds an appropriate tag. + +.. literalinclude:: ../../example-scripts/tag-ssh-msgs.lnav + :language: custsqlite + :caption: tag-ssh-msgs.lnav + :linenos: + +Log Analysis +------------ + +Most log analysis within **lnav** is done through the :ref:`sql-ext`. The +following examples should give you some ideas to start leveraging this +functionality. One thing to keep in mind is that if a query gets to be too +large or multiple statements need to be executed, you can create a +:code:`.lnav` script that contains the statements and execute it using the +:kbd:`\|` command prompt. + +Count client IPs in web access logs +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +To count the occurrences of an IP in web access logs and order the results +from highest to lowest: + + .. code-block:: custsqlite + + ;SELECT c_ip, count(*) as hits FROM access_log GROUP BY c_ip ORDER BY hits DESC + + +Show only lines where a numeric field is in a range +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The :ref:`:filter-expr` command can be used to filter web access +logs to only show lines where the number of bytes transferred to the client is +between 10,000 and 40,000 bytes like so: + + .. code-block:: custsqlite + + :filter-expr :sc_bytes BETWEEN 10000 AND 40000 + + +Generating a Report +^^^^^^^^^^^^^^^^^^^ + +Reports can be generated by writing an **lnav** :ref:`script` that +uses SQL queries and commands to format a document. A basic script can simply +execute a SQL query that is shown in the DB view. More sophisticated scripts +can use the following commands to generate customized output for a report: + +* The :ref:`:echo` command to write plain text +* :ref:`SQL queries` followed by a "write" command, like + :ref:`:write-table-to`. + +.. literalinclude:: ../../example-scripts/report-demo.lnav + :language: custsqlite + :caption: report-demo.lnav + :linenos: diff --git a/docs/source/data.rst b/docs/source/data.rst new file mode 100644 index 0000000..a7352e1 --- /dev/null +++ b/docs/source/data.rst @@ -0,0 +1,193 @@ + +.. _data-ext: + +Extracting Data +=============== + +**Note**: This feature is still in **BETA**, you should expect bugs and +incompatible changes in the future. + +Log messages contain a good deal of useful data, but it's not always easy to get +at. The log parser built into **lnav** is able to extract data as described by +:ref:`log_formats` as well as discovering data in plain text messages. This data +can then be queried and processed using the SQLite front-end that is also +incorporated into **lnav**. As an example, the following Syslog message from +:code:`sudo` can be processed to extract several key/value pairs:: + + Jul 31 11:42:26 Example-MacBook-Pro.local sudo[87024]: testuser : TTY=ttys004 ; PWD=/Users/testuser/github/lbuild ; USER=root ; COMMAND=/usr/bin/make install + +The data that can be extracted by the parser is viewable directly in **lnav** +by pressing the 'p' key. The results will be shown in an overlay like the +following:: + + Current Time: 2013-07-31T11:42:26.000 Original Time: 2013-07-31T11:42:26.000 Offset: +0.000 + Known message fields: + ├ log_hostname = Example-MacBook-Pro.local + ├ log_procname = sudo + ├ log_pid = 87024 + Discovered message fields: + ├ col_0 = testuser + ├ TTY = ttys004 + ├ PWD = /Users/testuser/github/lbuild + ├ USER = root + └ COMMAND = /usr/bin/make install + +Notice that the parser has detected pairs of the form '='. The data +parser will also look for pairs separated by a colon. If there are no clearly +demarcated pairs, then the parser will extract anything that looks like data +values and assign them keys of the form 'col_N'. For example, two data values, +an IPv4 address and a symbol, will be extracted from the following log +message:: + + Apr 29 08:13:43 sample-centos5 avahi-daemon[2467]: Registering new address record for 10.1.10.62 on eth0. + +Since there are no keys for the values in the message, the parser will assign +'col_0' for the IP address and 'col_1' for the symbol, as seen here:: + + Current Time: 2013-04-29T08:13:43.000 Original Time: 2013-04-29T08:13:43.000 Offset: +0.000 + Known message fields: + ├ log_hostname = sample-centos5 + ├ log_procname = avahi-daemon + ├ log_pid = 2467 + Discovered message fields: + ├ col_0 = 10.1.10.62 + └ col_1 = eth0 + +Now that you have an idea of how the parser works, you can begin to perform +queries on the data that is being extracted. The SQLite database engine is +embedded into **lnav** and its `Virtual Table +`_ mechanism is used to provide a means to +process this log data. Each log format has its own table that can be used to +access all of the loaded messages that are in that format. For accessing log +message content that is more free-form, like the examples given here, the +**logline** table can be used. The **logline** table is recreated for each +query and is based on the format and pairs discovered in the log message at +the top of the display. + +Queries can be performed by pressing the semi-colon (;) key in **lnav**. After +pressing the key, the overlay showing any known or discovered fields will be +displayed to give you an idea of what data is available. The query can be any +`SQL query `_ supported by SQLite. To make +analysis easier, **lnav** includes many extra functions for processing strings, +paths, and IP addresses. See :ref:`sql-ext` for more information. + +As an example, the simplest query to perform initially would be a "select all", +like so: + +.. code-block:: sql + + SELECT * FROM logline + +When this query is run against the second example log message given above, the +following results are received:: + + log_line log_part log_time log_idle_msecs log_level log_hostname log_procname log_pid col_0 col_1 + + 292 p.0 2013-04-11T16:42:51.000 0 info localhost avahi-daemon 2480 fe80::a00:27ff:fe98:7f6e eth0 + 293 p.0 2013-04-11T16:42:51.000 0 info localhost avahi-daemon 2480 10.0.2.15 eth0 + 330 p.0 2013-04-11T16:47:02.000 0 info localhost avahi-daemon 2480 fe80::a00:27ff:fe98:7f6e eth0 + 336 p.0 2013-04-11T16:47:02.000 0 info localhost avahi-daemon 2480 10.1.10.75 eth0 + 343 p.0 2013-04-11T16:47:02.000 0 info localhost avahi-daemon 2480 10.1.10.75 eth0 + 370 p.0 2013-04-11T16:59:39.000 0 info localhost avahi-daemon 2480 10.1.10.75 eth0 + 377 p.0 2013-04-11T16:59:39.000 0 info localhost avahi-daemon 2480 10.1.10.75 eth0 + 382 p.0 2013-04-11T16:59:41.000 0 info localhost avahi-daemon 2480 fe80::a00:27ff:fe98:7f6e eth0 + 401 p.0 2013-04-11T17:20:45.000 0 info localhost avahi-daemon 4247 fe80::a00:27ff:fe98:7f6e eth0 + 402 p.0 2013-04-11T17:20:45.000 0 info localhost avahi-daemon 4247 10.1.10.75 eth0 + + 735 p.0 2013-04-11T17:41:46.000 0 info sample-centos5 avahi-daemon 2465 fe80::a00:27ff:fe98:7f6e eth0 + 736 p.0 2013-04-11T17:41:46.000 0 info sample-centos5 avahi-daemon 2465 10.1.10.75 eth0 + 781 p.0 2013-04-12T03:32:30.000 0 info sample-centos5 avahi-daemon 2465 10.1.10.64 eth0 + 788 p.0 2013-04-12T03:32:30.000 0 info sample-centos5 avahi-daemon 2465 10.1.10.64 eth0 + 1166 p.0 2013-04-25T10:56:00.000 0 info sample-centos5 avahi-daemon 2467 fe80::a00:27ff:fe98:7f6e eth0 + 1167 p.0 2013-04-25T10:56:00.000 0 info sample-centos5 avahi-daemon 2467 10.1.10.111 eth0 + 1246 p.0 2013-04-26T06:06:25.000 0 info sample-centos5 avahi-daemon 2467 10.1.10.49 eth0 + 1253 p.0 2013-04-26T06:06:25.000 0 info sample-centos5 avahi-daemon 2467 10.1.10.49 eth0 + 1454 p.0 2013-04-28T06:53:55.000 0 info sample-centos5 avahi-daemon 2467 10.1.10.103 eth0 + 1461 p.0 2013-04-28T06:53:55.000 0 info sample-centos5 avahi-daemon 2467 10.1.10.103 eth0 + + 1497 p.0 2013-04-29T08:13:43.000 0 info sample-centos5 avahi-daemon 2467 10.1.10.62 eth0 + 1504 p.0 2013-04-29T08:13:43.000 0 info sample-centos5 avahi-daemon 2467 10.1.10.62 eth0 + +Note that **lnav** is not returning results for all messages that are in this +syslog file. Rather, it searches for messages that match the format for the +given line and returns only those messages in results. In this case, that +format is "Registering new address record for on ", which +corresponds to the parts of the message that were not recognized as data. + +More sophisticated queries can be done, of course. For example, to find out the +frequency of IP addresses mentioned in these messages, you can run: + +.. code-block:: sql + + SELECT col_0,count(*) FROM logline GROUP BY col_0 + +The results for this query are:: + + col_0 count(*) + + 10.0.2.15 1 + 10.1.10.49 2 + 10.1.10.62 2 + 10.1.10.64 2 + 10.1.10.75 6 + 10.1.10.103 2 + 10.1.10.111 1 + fe80::a00:27ff:fe98:7f6e 6 + +Since this type of query is fairly common, **lnav** includes a "summarize" +command that will compute the frequencies of identifiers as well as min, max, +average, median, and standard deviation for number columns. In this case, you +can run the following to compute the frequencies and return an ordered set of +results:: + + :summarize col_0 + + +Recognized Data Types +--------------------- + +When searching for data to extract from log messages, **lnav** looks for the +following set of patterns: + + +Strings + Single and double-quoted strings. Example: "The quick brown fox." + +URLs + URLs that contain the '://' separator. Example: http://example.com + +Paths + File system paths. Examples: /path/to/file, ./relative/path + +MAC Address + Ethernet MAC addresses. Example: c4:2c:03:0e:e4:4a + +Hex Dumps + A colon-separated string of hex numbers. Example: e8:06:88:ff + +Date/Time + Date and time stamps of the form "YYYY-mm-DD" and "HH:MM:SS". + +IP Addresses + IPv4 and IPv6 addresses. Examples: 127.0.0.1, fe80::c62c:3ff:fe0e:e44a%en0 + +UUID + The common formatting for 128-bit UUIDs. Example: + 0E305E39-F1E9-4DE4-B10B-5829E5DF54D0 + +Version Numbers + Dot-separated version numbers. Example: 3.7.17 + +Numbers + Numbers in base ten, hex, and octal formats. Examples: 1234, 0xbeef, 0777 + +E-Mail Address + Strings that look close to an e-mail address. Example: gary@example.com + +Constants + Common constants in languages, like: true, false, null, None. + +Symbols + Words that follow the common conventions for symbols in programming + languages. For example, containing all capital letters, or separated + by colons. Example: SOME_CONSTANT_VALUE, namespace::value diff --git a/docs/source/docutils.conf b/docs/source/docutils.conf new file mode 100644 index 0000000..1bf4d83 --- /dev/null +++ b/docs/source/docutils.conf @@ -0,0 +1,2 @@ +[restructuredtext parser] +syntax_highlight = short diff --git a/docs/source/events.rst b/docs/source/events.rst new file mode 100644 index 0000000..8318abf --- /dev/null +++ b/docs/source/events.rst @@ -0,0 +1,56 @@ +.. _Events: + +Events (v0.11.0+) +================= + +The events mechanism allows **lnav** to be automated based on events that +occur during processing. For example, filters could be added only when a +particular log file format is detected instead of always installing them. +Events are published through the :ref:`lnav_events` SQLite +table. Reacting to events can be done by creating a SQLite trigger on the +table and inspecting the content of the event. + +Trigger Example +--------------- + +The following is an example of a trigger that adds an out filter when a +syslog file is loaded. You can copy the code into an :file:`.sql` file and +install it by running :code:`lnav -i my_trigger.sql`. + +.. code-block:: sql + :caption: my_trigger.sql + :linenos: + + CREATE TRIGGER IF NOT EXISTS add_format_specific_filters + AFTER INSERT ON lnav_events WHEN + -- Check the event type + jget(NEW.content, '/$schema') = + 'https://lnav.org/event-file-format-detected-v1.schema.json' AND + -- Only create the filter when a given format is seen + jget(NEW.content, '/format') = 'syslog_log' AND + -- Don't create the filter if it's already there + NOT EXISTS ( + SELECT 1 FROM lnav_view_filters WHERE pattern = 'noisy message') + BEGIN + INSERT INTO lnav_view_filters (view_name, enabled, type, pattern) VALUES + ('log', 1, 'OUT', 'noisy message'); + END; + +.. _event_reference: + +Reference +--------- + +The following tables describe the schema of the event JSON objects. + +.. jsonschema:: ../schemas/event-file-open-v1.schema.json# + :lift_description: + +.. jsonschema:: ../schemas/event-file-format-detected-v1.schema.json# + :lift_description: + +.. jsonschema:: ../schemas/event-log-msg-detected-v1.schema.json# + :lift_description: + +.. jsonschema:: ../schemas/event-session-loaded-v1.schema.json# + :lift_description: diff --git a/docs/source/faq.rst b/docs/source/faq.rst new file mode 100644 index 0000000..6ca1d92 --- /dev/null +++ b/docs/source/faq.rst @@ -0,0 +1,57 @@ + +.. _faq: + +Frequently Asked Questions +========================== + +Q: How can I copy & paste without decorations? +---------------------------------------------- + +:Answer: There are a few ways to do this: + + * Use the :ref:`bookmark` hotkeys to mark lines and then + press :kbd:`c` to copy to the local system keyboard. + + * Press :kbd:`CTRL` + :kbd:`l` to temporarily switch to "lo-fi" + mode where the contents of the current view are printed to the terminal. + This option is useful when you are logged into a remote host. + + +Q: How can I force a format for a file? +--------------------------------------- + +:Answer: The log format for a file is automatically detected and cannot be + forced. + +:Solution: Add some of the log file lines to the :ref:`sample` + array and then startup lnav to get a detailed explanation of where the format + patterns are not matching the sample lines. + +:Details: The first lines of the file are matched against the + :ref:`regular expressions defined in the format definitions`. + The order of the formats is automatically determined so that more specific + formats are tried before more generic ones. Therefore, if the expected + format is not being chosen for a file, then it means the regular expressions + defined by that format are not matching the first few lines of the file. + + See :ref:`format_order` for more information. + +Q: Why isn't my log file highlighted correctly? +----------------------------------------------- + +TBD + +Q: Why isn't a file being displayed? +------------------------------------ + +:Answer: Plaintext files are displayed separately from log files in the TEXT + view. + +:Solution: Press the :kbd:`t` key to switch to the text view. Or, open the + files configuration panel by pressing :kbd:`TAB` to cycle through the + panels, and then press :kbd:`/` to search for the file you're interested in. + If the file is a log, a new :ref:`log format` will need to be + created or an existing one modified. + +:Details: If a file being monitored by lnav does not match a known log file + format, it is treated as plaintext and will be displayed in the TEXT view. diff --git a/docs/source/filter-out-preview.png b/docs/source/filter-out-preview.png new file mode 100644 index 0000000..8f9816f Binary files /dev/null and b/docs/source/filter-out-preview.png differ diff --git a/docs/source/formats.rst b/docs/source/formats.rst new file mode 100644 index 0000000..8960761 --- /dev/null +++ b/docs/source/formats.rst @@ -0,0 +1,527 @@ +.. _log_formats: + +Log Formats +=========== + +Log files loaded into **lnav** are parsed based on formats defined in +configuration files. Many +formats are already built in to the **lnav** binary and you can define your own +using a JSON file. When loading files, each format is checked to see if it can +parse the first few lines in the file. Once a match is found, that format will +be considered that files format and used to parse the remaining lines in the +file. If no match is found, the file is considered to be plain text and can +be viewed in the "text" view that is accessed with the **t** key. + +The following log formats are built into **lnav**: + +.. csv-table:: + :header: "Name", "Table Name", "Description" + :widths: 8 5 20 + :file: format-table.csv + +In addition to the above formats, the following self-describing formats are +supported: + +* The + `Bro Network Security Monitor `_ + TSV log format is supported in lnav versions v0.8.3+. The Bro log format is + self-describing, so **lnav** will read the header to determine the shape of + the file. +* The + `W3C Extend Log File Format `_ + is supported in lnav versions v0.10.0+. The W3C log format is + self-describing, so **lnav** will read the header to determine the shape of + the file. + +There is also basic support for the `logfmt `_ +convention for formatting log messages. Files that use this format must +have the entire line be key/value pairs and the timestamp contained in a +field named :code:`time` or :code:`ts`. If the file you're using does not +quite follow this formatting, but wraps logfmt data with another recognized +format, you can use the :ref:`logfmt2json` SQL function to convert the data +into JSON for further analysis. + + +Defining a New Format +--------------------- + +New log formats can be defined by placing JSON configuration files in +subdirectories of the :file:`~/.lnav/formats/` directory. The directories and +files can be named anything you like, but the files must have the '.json' suffix. A +sample file containing the builtin configuration will be written to this +directory when **lnav** starts up. You can consult that file when writing your +own formats or if you need to modify existing ones. Format directories can +also contain '.sql' and '.lnav' script files that can be used automate log file +analysis. + +Creating a Format Using Regex101.com (v0.11.0+) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +For plain-text log files, the easiest way to create a log format definition is +to create the regular expression that recognizes log messages using +https://regex101.com . Simply copy a log line into the test string input box +on the site and then start editing the regular expression. When building the +regular expression, you'll want to use named captures for the structured parts +of the log message. Any raw message text should be matched by a captured named +"body". Once you have a regex that matches the whole log message, you can use +**lnav**'s "management CLI" to create a skeleton format file. The skeleton +will be populated with the regular expression from the site and the test +string, along with any unit tests, will be added to the "samples" list. The +"regex101 import" management command is used to create the skeleton and has +the following form: + +.. prompt:: bash + + lnav -m regex101 import [] + +If the import was successful, the path to the new format file should be +printed out. The skeleton will most likely need some changes to make it +fully functional. For example, the :code:`kind` properties for captured values +default to :code:`string`, but you'll want to change them to the appropriate +type. + +Format File Reference +^^^^^^^^^^^^^^^^^^^^^ + +An **lnav** format file must contain a single JSON object, preferably with a +:code:`$schema` property that refers to the +`format-v1.schema `_, +like so: + +.. code-block:: json + + { + "$schema": "https://lnav.org/schemas/format-v1.schema.json" + } + +Each format to be defined in the file should be a separate field in the top-level +object. The field name should be the symbolic name of the format. This value +will also be used as the SQL table name for the log. The value for each field +should be another object with the following fields: + + :title: The short and human-readable name for the format. + :description: A longer description of the format. + :url: A URL to the definition of the format. + + :file-pattern: A regular expression used to match log file paths. Typically, + every file format will be tried during the detection process. This field + can be used to limit which files a format is applied to in case there is + a potential for conflicts. + + .. _format_regex: + + :regex: This object contains sub-objects that describe the message formats + to match in a plain-text log file. Each :code:`regex` MUST only match one + type of log message. It must not match log messages that are matched by + other regexes in this format. This uniqueness requirement is necessary + because **lnav** will "lock-on" to a regex and use it to match against + the next line in a file. So, if the regexes do not uniquely match each + type of log message, messages can be matched by the wrong regex. The + "lock-on" behavior is needed to avoid the performance hit of having to + try too many different regexes. + + .. note:: Log files that contain JSON messages should not specify this field. + + :pattern: The regular expression that should be used to match log messages. + The `PCRE2 `_ library is used by **lnav** to do all + regular expression matching. + + :module-format: If true, this regex will only be used to parse message + bodies for formats that can act as containers, such as syslog. Default: + false. + + :json: True if each log line is JSON-encoded. + + :line-format: An array that specifies the text format for JSON-encoded + log messages. Log files that are JSON-encoded will have each message + converted from the raw JSON encoding into this format. Each element + is either an object that defines which fields should be inserted into + the final message string and or a string constant that should be + inserted. For example, the following configuration will tranform each + log message object into a string that contains the timestamp, followed + by a space, and then the message body: + + .. code-block:: json + + [ { "field": "ts" }, " ", { "field": "msg" } ] + + :field: The name or `JSON-Pointer `_ + of the message field that should be inserted at this point in the + message. The special :code:`__timestamp__` field name can be used to + insert a human-readable timestamp. The :code:`__level__` field can be + used to insert the level name as defined by lnav. + + .. tip:: + + Use a JSON-Pointer to reference nested fields. For example, to include + a "procname" property that is nested in a "details" object, you would + write the field reference as :code:`/details/procname`. + + :min-width: The minimum width for the field. If the value for the field + in a given log message is shorter, padding will be added as needed to + meet the minimum-width requirement. (v0.8.2+) + :max-width: The maximum width for the field. If the value for the field + in a given log message is longer, the overflow algorithm will be applied + to try and shorten the field. (v0.8.2+) + :align: Specifies the alignment for the field, either "left" or "right". + If "left", padding to meet the minimum-width will be added on the right. + If "right", padding will be added on the left. (v0.8.2+) + :overflow: The algorithm used to shorten a field that is longer than + "max-width". The following algorithms are supported: + + :abbrev: Removes all but the first letter in dotted text. For example, + "com.example.foo" would be shortened to "c.e.foo". + :truncate: Truncates any text past the maximum width. + :dot-dot: Cuts out the middle of the text and replaces it with two + dots (i.e. '..'). + + (v0.8.2+) + :timestamp-format: The timestamp format to use when displaying the time + for this log message. (v0.8.2+) + :default-value: The default value to use if the field could not be found + in the current log message. The built-in default is "-". + :text-transform: Transform the text in the field. Supported options are: + none, uppercase, lowercase, capitalize + + :timestamp-field: The name of the field that contains the log message + timestamp. Defaults to "timestamp". + + :timestamp-format: An array of timestamp formats using a subset of the + strftime conversion specification. The following conversions are + supported: %a, %b, %L, %M, %H, %I, %d, %e, %k, %l, %m, %p, %y, %Y, %S, %s, + %Z, %z. In addition, you can also use the following: + + :%L: Milliseconds as a decimal number (range 000 to 999). + :%f: Microseconds as a decimal number (range 000000 to 999999). + :%N: Nanoseconds as a decimal number (range 000000000 to 999999999). + :%q: Seconds from the epoch as a hexidecimal number. + :%i: Milliseconds from the epoch. + :%6: Microseconds from the epoch. + + :timestamp-divisor: For JSON logs with numeric timestamps, this value is used + to divide the timestamp by to get the number of seconds and fractional + seconds. + + :subsecond-field: (v0.11.1+) The path to the property in a JSON-lines log + message that contains the sub-second time value + + :subsecond-units: (v0.11.1+) The units of the subsecond-field property value. + The following values are supported: + + :milli: for milliseconds + :micro: for microseconds + :nano: for nanoseconds + + :ordered-by-time: (v0.8.3+) Indicates that the order of messages in the file + is time-based. Files that are not naturally ordered by time will be sorted + in order to display them in the correct order. Note that this sorting can + incur a performance penalty when tailing logs. + + :level-field: The name of the regex capture group that contains the log + message level. Defaults to "level". + + :body-field: The name of the field that contains the main body of the + message. Defaults to "body". + + :opid-field: The name of the field that contains the "operation ID" of the + message. An "operation ID" establishes a thread of messages that might + correspond to a particular operation/request/transaction. The user can + press the 'o' or 'Shift+O' hotkeys to move forward/backward through the + list of messages that have the same operation ID. Note: For JSON-encoded + logs, the opid field can be a path (e.g. "foo/bar/opid") if the field is + nested in an object and it MUST be included in the "line-format" for the + 'o' hotkeys to work. + + :module-field: The name of the field that contains the module identifier + that distinguishes messages from one log source from another. This field + should be used if this message format can act as a container for other + types of log messages. For example, an Apache access log can be sent to + syslog instead of written to a file. In this case, **lnav** will parse + the syslog message and then separately parse the body of the message to + determine the "sub" format. This module identifier is used to help + **lnav** quickly identify the format to use when parsing message bodies. + + :hide-extra: A boolean for JSON logs that indicates whether fields not + present in the line-format should be displayed on their own lines. + + :level: A mapping of error levels to regular expressions. During scanning + the contents of the capture group specified by *level-field* will be + checked against each of these regexes. Once a match is found, the log + message level will set to the corresponding level. The available levels, + in order of severity, are: **fatal**, **critical**, **error**, + **warning**, **stats**, **info**, **debug**, **debug2-5**, **trace**. + For JSON logs with exact numeric levels, the number for the corresponding + level can be supplied. If the JSON log format uses numeric ranges instead + of exact numbers, you can supply a pattern and the number found in the log + will be converted to a string for pattern-matching. + + :multiline: If false, **lnav** will consider any log lines that do not + match one of the message patterns to be in error when checking files with + the '-C' option. This flag will not affect normal viewing operation. + Default: true. + + :value: This object contains the definitions for the values captured by the + regexes. + + :kind: The type of data that was captured **string**, **integer**, + **float**, **json**, **quoted**. + :collate: The name of the SQLite collation function for this value. + The standard SQLite collation functions can be used as well as the + ones defined by lnav, as described in :ref:`collators`. + :identifier: A boolean that indicates whether or not this field represents + an identifier and should be syntax colored. + :foreign-key: A boolean that indicates that this field is a key and should + not be graphed. This should only need to be set for integer fields. + :hidden: A boolean for log fields that indicates whether they should + be displayed. The behavior is slightly different for JSON logs and text + logs. For a JSON log, this property determines whether an extra line + will be added with the key/value pair. For text logs, this property + controls whether the value should be displayed by default or replaced + with an ellipsis. + :rewriter: A command to rewrite this field when pretty-printing log + messages containing this value. The command must start with ':', ';', + or '|' to signify whether it is a regular command, SQL query, or a script + to be executed. The other fields in the line are accessible in SQL by + using the ':' prefix. The text value of this field will then be replaced + with the result of the command when pretty-printing. For example, the + HTTP access log format will rewrite the status code field to include the + textual version (e.g. 200 (OK)) using the following SQL query: + + .. code-block:: sql + + ;SELECT :sc_status || ' (' || ( + SELECT message FROM http_status_codes + WHERE status = :sc_status) || ') ' + + :tags: This object contains the tags that should automatically be added to + log messages. + + :pattern: The regular expression evaluated over a line in the log file as + it is read in. If there is a match, the log message the line is a part + of will have this tag added to it. + :paths: This array contains objects that define restrictions on the file + paths that the tags will be applied to. The objects in this array can + contain: + + :glob: A glob pattern to check against the log files read by lnav. + + .. _format_sample: + + :sample: A list of objects that contain sample log messages. All formats + must include at least one sample and it must be matched by one of the + included regexes. Each object must contain the following field: + + :line: The sample message. + :level: The expected error level. An error will be raised if this level + does not match the level parsed by lnav for this sample message. + + :highlights: This object contains the definitions for patterns to be + highlighted in a log message. Each entry should have a name and a + definition with the following fields: + + :pattern: The regular expression to match in the log message body. + :color: The foreground color to use when highlighting the part of the + message that matched the pattern. If no color is specified, one will be + picked automatically. Colors can be specified using hexadecimal notation + by starting with a hash (e.g. #aabbcc) or using a color name as found + at http://jonasjacek.github.io/colors/. + :background-color: The background color to use when highlighting the part + of the message that matched the pattern. If no background color is + specified, black will be used. The background color is only considered + if a foreground color is specified. + :underline: If true, underline the part of the message that matched the + pattern. + :blink: If true, blink the part of the message that matched the pattern. + +Example format: + +.. code-block:: json + + { + "$schema": "https://lnav.org/schemas/format-v1.schema.json", + "example_log" : { + "title" : "Example Log Format", + "description" : "Log format used in the documentation example.", + "url" : "http://example.com/log-format.html", + "regex" : { + "basic" : { + "pattern" : "^(?\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{3}Z)>>(?\\w+)>>(?\\w+)>>(?.*)$" + } + }, + "level-field" : "level", + "level" : { + "error" : "ERROR", + "warning" : "WARNING" + }, + "value" : { + "component" : { + "kind" : "string", + "identifier" : true + } + }, + "sample" : [ + { + "line" : "2011-04-01T15:14:34.203Z>>ERROR>>core>>Shit's on fire yo!" + } + ] + } + } + +Patching an Existing Format +--------------------------- + +When loading log formats from files, **lnav** will overlay any new data over +previously loaded data. This feature allows you to override existing value or +append new ones to the format configurations. For example, you can separately +add a new regex to the example log format given above by creating another file +with the following contents: + +.. code-block:: json + + { + "$schema": "https://lnav.org/schemas/format-v1.schema.json", + "example_log" : { + "regex" : { + "custom1" : { + "pattern" : "^(?\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{3}Z)<<(?\\w+)--(?\\w+)>>(?.*)$" + } + }, + "sample" : [ + { + "line" : "2011-04-01T15:14:34.203Z<>Shit's on fire yo!" + } + ] + } + } + +.. _scripts: + +Scripts +------- + +Format directories may also contain :file:`.sql` and :file:`.lnav` files to help automate +log file analysis. The SQL files are executed on startup to create any helper +tables or views and the '.lnav' script files can be executed using the pipe +hotkey :kbd:`|`. For example, **lnav** includes a "partition-by-boot" script that +partitions the log view based on boot messages from the Linux kernel. A script +can have a mix of SQL and **lnav** commands, as well as include other scripts. +The type of statement to execute is determined by the leading character on a +line: a semi-colon begins a SQL statement; a colon starts an **lnav** command; +and a pipe :code:`|` denotes another script to be executed. Lines beginning with a +hash are treated as comments. The following variables are defined in a script: + +.. envvar:: # + + The number of arguments passed to the script. + +.. envvar:: __all__ + + A string containing all the arguments joined by a single space. + +.. envvar:: 0 + + The path to the script being executed. + +.. envvar:: 1-N + + The arguments passed to the script. + +Remember that you need to use the :ref:`:eval` command when referencing +variables in most **lnav** commands. Scripts can provide help text to be +displayed during interactive usage by adding the following tags in a comment +header: + + :@synopsis: The synopsis should contain the name of the script and any + parameters to be passed. For example:: + + # @synopsis: hello-world [ ... ] + + :@description: A one-line description of what the script does. For example:: + + # @description: Say hello to the given names. + + + +.. tip:: + + The :ref:`:eval` command can be used to do variable substitution for + commands that do not natively support it. For example, to substitute the + variable, :code:`pattern`, in a :ref:`:filter-out` command: + + .. code-block:: lnav + + :eval :filter-out ${pattern} + +VSCode Extension +^^^^^^^^^^^^^^^^ + +The `lnav VSCode Extension `_ +can be installed to add syntax highlighting to lnav scripts. + +Installing Formats +------------------ + +File formats are loaded from subdirectories in :file:`/etc/lnav/formats` and +:file:`~/.lnav/formats/`. You can manually create these subdirectories and +copy the format files into there. Or, you can pass the '-i' option to **lnav** +to automatically install formats from the command-line. For example: + +.. code-block:: bash + + $ lnav -i myformat.json + info: installed: /home/example/.lnav/formats/installed/myformat_log.json + +Format files installed using this method will be placed in the :file:`installed` +subdirectory and named based on the first format name found in the file. + +You can also install formats from git repositories by passing the repository's +clone URL. A standard set of repositories is maintained at +(https://github.com/tstack/lnav-config) and can be installed by passing 'extra' +on the command line, like so: + +.. prompt:: bash + + lnav -i extra + +These repositories can be updated by running **lnav** with the '-u' flag. + +Format files can also be made executable by adding a shebang (#!) line to the +top of the file, like so:: + + #! /usr/bin/env lnav -i + { + "myformat_log" : ... + } + +Executing the format file should then install it automatically: + +.. code-block:: bash + + $ chmod ugo+rx myformat.json + $ ./myformat.json + info: installed: /home/example/.lnav/formats/installed/myformat_log.json + +.. _format_order: + +Format Order When Scanning a File +--------------------------------- + +When **lnav** loads a file, it tries each log format against the first 15,000 +lines [#]_ of the file trying to find a match. When a match is found, that log +format will be locked in and used for the rest of the lines in that file. +Since there may be overlap between formats, **lnav** performs a test on +startup to determine which formats match each others sample lines. Using +this information it will create an ordering of the formats so that the more +specific formats are tried before the more generic ones. For example, a +format that matches certain syslog messages will match its own sample lines, +but not the ones in the syslog samples. On the other hand, the syslog format +will match its own samples and those in the more specific format. You can +see the order of the format by enabling debugging and checking the **lnav** +log file for the "Format order" message: + +.. prompt:: bash + + lnav -d /tmp/lnav.log + +.. [#] The maximum number of lines to check can be configured. See the + :ref:`tuning` section for more details. diff --git a/docs/source/group_concat-help.png b/docs/source/group_concat-help.png new file mode 100644 index 0000000..f7b6f08 Binary files /dev/null and b/docs/source/group_concat-help.png differ diff --git a/docs/source/hotkey-tips.png b/docs/source/hotkey-tips.png new file mode 100644 index 0000000..5b78686 Binary files /dev/null and b/docs/source/hotkey-tips.png differ diff --git a/docs/source/hotkeys.rst b/docs/source/hotkeys.rst new file mode 100644 index 0000000..f91c1e8 --- /dev/null +++ b/docs/source/hotkeys.rst @@ -0,0 +1,289 @@ +.. _hotkeys: + +Hotkey Reference +================ + +This reference covers the keys used to control **lnav**. Consult the `built-in +help `_ in **lnav** for +a more detailed explanation of each key. + +Spatial Navigation +------------------ + +The majority of these hotkeys should be available in all views. + +.. list-table:: + :header-rows: 1 + :widths: 6 6 6 20 + + * - Keypress + - + - + - Command + * - :kbd:`Space` + - :kbd:`PgDn` + - + - Down a page + * - :kbd:`b` + - :kbd:`Backspace` + - :kbd:`PgUp` + - Up a page + * - :kbd:`j` + - :kbd:`↓` + - + - Down a line + * - :kbd:`k` + - :kbd:`↑` + - + - Up a line + * - :kbd:`h` + - :kbd:`←` + - + - Left half a page. In the log view, pressing left while at the start of + the message text will reveal the source file name for each line. + Pressing again will reveal the full path. + * - :kbd:`Shift` + :kbd:`h` + - :kbd:`Shift` + :kbd:`←` + - + - Left ten columns + * - :kbd:`l` + - :kbd:`→` + - + - Right half a page + * - :kbd:`Shift` + :kbd:`l` + - :kbd:`Shift` + :kbd:`→` + - + - Right ten columns + * - :kbd:`Home` + - :kbd:`g` + - + - Top of the view + * - :kbd:`End` + - :kbd:`G` + - + - Bottom of the view + * - :kbd:`e` + - :kbd:`Shift` + :kbd:`e` + - + - Next/previous error + * - :kbd:`w` + - :kbd:`Shift` + :kbd:`w` + - + - Next/previous warning + * - :kbd:`n` + - :kbd:`Shift` + :kbd:`n` + - + - Next/previous search hit + * - :kbd:`>` + - :kbd:`<` + - + - Next/previous search hit (horizontal) + * - :kbd:`f` + - :kbd:`Shift` + :kbd:`f` + - + - Next/previous file + * - :kbd:`u` + - :kbd:`Shift` + :kbd:`u` + - + - Next/previous bookmark + * - :kbd:`o` + - :kbd:`Shift` + :kbd:`o` + - + - Forward/backward through log messages with a matching "opid" field + * - :kbd:`s` + - :kbd:`Shift` + :kbd:`s` + - + - Next/previous slow down in the log message rate + * - :kbd:`{` + - :kbd:`}` + - + - Previous/next location in history + +Chronological Navigation +------------------------ + +These hotkeys are only functional on views that are time-based, like the log +view or the histogram view. + +.. list-table:: + :header-rows: 1 + :widths: 5 5 20 + + * - Keypress + - + - Command + * - :kbd:`d` + - :kbd:`Shift` + :kbd:`d` + - Forward/backward 24 hours + * - :kbd:`1` - :kbd:`6` + - :kbd:`Shift` + :kbd:`1` - :kbd:`6` + - Next/previous n'th ten minute of the hour + * - :kbd:`7` + - :kbd:`8` + - Previous/next minute + * - :kbd:`0` + - :kbd:`Shift` + :kbd:`0` + - Next/previous day + * - :kbd:`r` + - :kbd:`Shift` + :kbd:`r` + - Forward/backward by the relative time that was last used with the goto command. + +Breadcrumb Navigation +--------------------- + +The following hotkeys are related to the breadcrumb bar that is below the top +status bar. + +.. list-table:: + :header-rows: 1 + :widths: 5 20 + + * - Keypress + - Description + * - :kbd:`ENTER` + - Focus on the breadcrumb bar. Or, if the bar is currently focused, + accept the selected value and drop focus. + * - :kbd:`Escape` + - Drop focus on the breadcrumb bar. + * - :kbd:`←` + - Select the crumb to the left. If the first crumb is selected, the + selection will wrap around to the last crumb. + * - :kbd:`→` + - Accept the current value, which might mean navigating to the value in + the view, then selecting the crumb to the right. + * - :kbd:`Ctrl` + :kbd:`a` + - Select the first crumb. + * - :kbd:`Ctrl` + :kbd:`e` + - Select the last crumb. + * - :kbd:`↓` + - Select the next value in the crumb dropdown. + * - :kbd:`↑` + - Select the previous value in the crumb dropdown. + * - :kbd:`Home` + - Select the first value in the crumb dropdown. + * - :kbd:`End` + - Select the last value in the crumb dropdown. + +While a crumb is selected, you can perform a fuzzy search on the possible +values by typing in the value you are interested in. + +.. _hotkeys_bookmarks: + +Bookmarks +--------- + +.. list-table:: + :header-rows: 1 + :widths: 5 20 + + * - Keypress + - Command + * - :kbd:`m` + - Mark/unmark the top line + * - :kbd:`Shift` + :kbd:`m` + - Mark/unmark the range of lines from the last marked to the top + * - :kbd:`Shift` + :kbd:`j` + - Mark/unmark the next line after the previously marked + * - :kbd:`Shift` + :kbd:`k` + - Mark/unmark the previous line + * - :kbd:`c` + - Copy marked lines to the clipboard + * - :kbd:`Shift` + :kbd:`c` + - Clear marked lines + +.. _hotkeys_display: + +Display +------- + +.. list-table:: + :header-rows: 1 + :widths: 5 20 + + * - Keypress + - Command + * - :kbd:`?` + - View/leave builtin help + * - :kbd:`q` + - Return to the previous view/quit + * - :kbd:`Shift` + :kbd:`q` + - Return to the previous view/quit while matching the top times of the two views + * - :kbd:`a` + - Restore the view that was previously popped with 'q/Q' + * - :kbd:`Shift` + :kbd:`a` + - Restore the view that was previously popped with 'q/Q' and match the top times of the views + * - :kbd:`Shift` + :kbd:`p` + - Switch to/from the pretty-printed view of the displayed log or text files + * - :kbd:`Shift` + :kbd:`t` + - Display elapsed time between lines + * - :kbd:`t` + - Switch to/from the text file view + * - :kbd:`i` + - Switch to/from the histogram view + * - :kbd:`Shift` + :kbd:`i` + - Switch to/from the histogram view + * - :kbd:`v` + - Switch to/from the SQL result view + * - :kbd:`Shift` + :kbd:`v` + - Switch to/from the SQL result view and move to the corresponding in the + log_line column + * - :kbd:`p` + - Toggle the display of the log parser results + * - :kbd:`Tab` + - In the log/text views, focus on the configuration panel for editing + filters and examining the list of loaded files. In the SQL result view, + cycle through columns to display as bar graphs + * - :kbd:`Ctrl` + :kbd:`l` + - Switch to lo-fi mode. The displayed log lines will be dumped to the + terminal without any decorations so they can be copied easily. + * - :kbd:`Ctrl` + :kbd:`w` + - Toggle word-wrap. + * - :kbd:`Ctrl` + :kbd:`p` + - Show/hide the data preview panel that may be opened when entering + commands or SQL queries. + * - :kbd:`Ctrl` + :kbd:`f` + - Toggle the enabled/disabled state of all filters in the current view. + * - :kbd:`x` + - Toggle the hiding of log message fields. The hidden fields will be + replaced with three bullets and highlighted in yellow. + * - :kbd:`=` + - Pause/unpause loading of new file data. + +Session +------- + +.. list-table:: + :header-rows: 1 + :widths: 5 20 + + * - Keypress + - Command + * - :kbd:`Ctrl` + :kbd:`R` + - Reset the current :ref:`session` state. The session state + includes things like filters, bookmarks, and hidden fields. + +Query Prompts +------------- + +.. list-table:: + :header-rows: 1 + :widths: 5 20 + + * - Keypress + - Command + * - :kbd:`/` + - Search for lines matching a regular expression + * - :kbd:`;` + - Open the :ref:`sql-ext` to execute SQL statements/queries + * - :kbd:`:` + - Execute an internal command, see :ref:`commands` for more information + * - :kbd:`\|` + - Execute an lnav script located in a format directory + * - :kbd:`Ctrl` + :kbd:`]` + - Abort the prompt + +Customizing +----------- + +You can customize the behavior of hotkeys by defining your own keymaps. +Consult the :ref:`Keymaps` configuration section for more information. diff --git a/docs/source/howitworks.rst b/docs/source/howitworks.rst new file mode 100644 index 0000000..d111fd0 --- /dev/null +++ b/docs/source/howitworks.rst @@ -0,0 +1,13 @@ + +.. _howitworks: + +How It Works +============ + +"Magic" + +Internal Architecture +--------------------- + +The `ARCHITECTURE.md `_ +file in the source tree contains some information about lnav's internals. diff --git a/docs/source/index.rst b/docs/source/index.rst new file mode 100644 index 0000000..d94c0eb --- /dev/null +++ b/docs/source/index.rst @@ -0,0 +1,35 @@ +Welcome to lnav's documentation! +================================ + +The `Log File Navigator `_ (**lnav**) is an advanced log file +viewer for the console. + +Contents: + +.. toctree:: + :maxdepth: 2 + + intro + ui + hotkeys + cli + usage + cookbook + config + formats + sessions + commands + sqlext + sqltab + events + data + howitworks + faq + + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/docs/source/intro.rst b/docs/source/intro.rst new file mode 100644 index 0000000..ae3164c --- /dev/null +++ b/docs/source/intro.rst @@ -0,0 +1,131 @@ +Introduction +============ + +The Log File Navigator, **lnav**, is an advanced log file viewer for the +terminal. It provides an :ref:`easy-to-use interface` for monitoring and +analyzing your log files with little to no setup. Simply point **lnav** at +your log files and it will automatically detect the :ref:`log_formats`, index +their contents, and display a combined view of all log messages. You can +navigate through your logs using a variety of :ref:`hotkeys`. +:ref:`Commands` give you additional control over **lnav**'s behavior +for doing things like applying filters, tagging messages, and more. You can +then analyze your log messages using the :ref:`sql-ext`. + +Dependencies +------------ + +When compiling from source, the following dependencies are required: + +* `NCurses `_ +* `PCRE2 `_ +* `SQLite `_ +* `ZLib `_ +* `Bzip2 `_ +* `Readline `_ +* `libcurl `_ +* `libarchive `_ + +Installation +------------ + +Check the `downloads page `_ to see if there are +packages for your operating system. To compile from source, use the following +commands: + +.. prompt:: bash + + ./configure + make + sudo make install + + +Viewing Logs +------------ + +The arguments to **lnav** are the log files, directories, or URLs to be viewed. +For example, to view all of the CUPS logs on your system: + +.. prompt:: bash + + lnav /var/log/cups + +The formats of the logs are determined automatically and indexed on-the-fly. +See :ref:`log_formats` for a listing of the predefined formats and how to +define your own. + +If no arguments are given, **lnav** will try to open the syslog file on your +system: + +.. prompt:: bash + + lnav + + +Setup +----- + +After starting **lnav**, you might want to set the +:ref:`configuration options` mentioned below. Configuration in +**lnav** is done using the :code:`:config` command. To change a configuration +option, start by pressing :kbd:`:` to enter the command prompt. Then, +type "config" followed by the option name and value. + +.. note:: + + Tab-completion is available for these configuration options and, in some + cases, their values as well. + + +Keymap +^^^^^^ + +The keymap defines the mapping from :ref:`hotkeys` to commands to +execute. The default mapping is for "U.S." keyboards. The following command +can be used to change the keymap: + +.. code-block:: lnav + + :config /ui/keymap + +The builtin keymaps are: + + :de: `German `_ + :fr: `French `_ + :sv: `Swedish `_ + :uk: `United Kingdom `_ + :us: `United States `_ + +To create or customize a keymap, consult the :ref:`keymaps` section. + + +Theme +^^^^^ + +The visual styling of **lnav** can be customized using a theme. The following +command can be used to the change the theme: + +.. code-block:: lnav + + :config /ui/theme + +The builtin themes are: +`default `_, +`eldar `_, +`grayscale `_, +`monocai `_, +`night-owl `_, +`solarized-dark `_, +and +`solarized-light `_. + +To create or customize a theme, consult the :ref:`themes` section. + + +Log Formats +^^^^^^^^^^^ + +In order for **lnav** to understand your log files, it needs to told how to +parse the log messages using a log format definition. There are many log +formats builtin and **lnav** will automatically determine the best format to +use. In case your log file is not recognized, consult the :ref:`log_formats` +section for information on how to create a format. diff --git a/docs/source/key-encoding-prompt.png b/docs/source/key-encoding-prompt.png new file mode 100644 index 0000000..fe63412 Binary files /dev/null and b/docs/source/key-encoding-prompt.png differ diff --git a/docs/source/lnav-breadcrumbs-help.png b/docs/source/lnav-breadcrumbs-help.png new file mode 100644 index 0000000..b3b80d0 Binary files /dev/null and b/docs/source/lnav-breadcrumbs-help.png differ diff --git a/docs/source/lnav-config-header.png b/docs/source/lnav-config-header.png new file mode 100644 index 0000000..079b9c2 Binary files /dev/null and b/docs/source/lnav-config-header.png differ diff --git a/docs/source/lnav-files-panel.png b/docs/source/lnav-files-panel.png new file mode 100644 index 0000000..7aed793 Binary files /dev/null and b/docs/source/lnav-files-panel.png differ diff --git a/docs/source/lnav-filters-panel.png b/docs/source/lnav-filters-panel.png new file mode 100644 index 0000000..788e63f Binary files /dev/null and b/docs/source/lnav-filters-panel.png differ diff --git a/docs/source/lnav-spectro-cpu-pct.png b/docs/source/lnav-spectro-cpu-pct.png new file mode 100644 index 0000000..a9df2b6 Binary files /dev/null and b/docs/source/lnav-spectro-cpu-pct.png differ diff --git a/docs/source/lnav-ui.png b/docs/source/lnav-ui.png new file mode 100644 index 0000000..2dcad66 Binary files /dev/null and b/docs/source/lnav-ui.png differ diff --git a/docs/source/open-error.png b/docs/source/open-error.png new file mode 100644 index 0000000..6454b2d Binary files /dev/null and b/docs/source/open-error.png differ diff --git a/docs/source/open-help.png b/docs/source/open-help.png new file mode 100644 index 0000000..a11656a Binary files /dev/null and b/docs/source/open-help.png differ diff --git a/docs/source/open-preview.png b/docs/source/open-preview.png new file mode 100644 index 0000000..b888376 Binary files /dev/null and b/docs/source/open-preview.png differ diff --git a/docs/source/query-results.png b/docs/source/query-results.png new file mode 100644 index 0000000..14ae5b8 Binary files /dev/null and b/docs/source/query-results.png differ diff --git a/docs/source/sessions.rst b/docs/source/sessions.rst new file mode 100644 index 0000000..e97e598 --- /dev/null +++ b/docs/source/sessions.rst @@ -0,0 +1,23 @@ + +.. _sessions: + +Sessions +======== + +Session information is stored automatically for the set of files that were +passed in on the command-line and reloaded the next time **lnav** is executed. +The information currently stored is: + +* Position within the files being viewed. +* Active searches for each view. +* :ref:`Log filters`. +* :ref:`Highlights`. +* :ref:`Hidden files`. +* :ref:`Hidden fields`. + +Bookmarks and log-time adjustments are stored separately on a per-file basis. +Note that the bookmarks are associated with files based on the content of the +first line of the file so that they are preserved even if the file has been +moved from its current location. + +Session data is stored in the :file:`~/.lnav` directory. diff --git a/docs/source/sql-help.png b/docs/source/sql-help.png new file mode 100644 index 0000000..44901a4 Binary files /dev/null and b/docs/source/sql-help.png differ diff --git a/docs/source/sqlext.rst b/docs/source/sqlext.rst new file mode 100644 index 0000000..e0d7543 --- /dev/null +++ b/docs/source/sqlext.rst @@ -0,0 +1,184 @@ + +.. _sql-ext: + +SQLite Interface +================ + +Log analysis in **lnav** can be done using the SQLite interface. Log messages +can be accessed via `virtual tables `_ that +are created for each file format. The tables have the same name as the log +format and each message is its own row in the table. For example, given the +following log message from an Apache access log:: + + 127.0.0.1 - frank [10/Oct/2000:13:55:36 -0700] "GET /apache_pb.gif HTTP/1.0" 200 2326 + +These columns would be available for its row in the :code:`access_log` table: + +.. csv-table:: + :class: query-results + :header-rows: 1 + + log_line,log_part,log_time,log_idle_msecs,log_level,log_mark,log_comment,log_tags,log_filters,c_ip,cs_method,cs_referer,cs_uri_query,cs_uri_stem,cs_user_agent,cs_username,cs_version,sc_bytes,sc_status + 0,,2000-10-10 13:55:36.000,0,info,1,,,,127.0.0.1,GET,,,/apache_pb.gif,,frank,HTTP/1.0,2326,200 + +.. note:: Some columns are hidden by default to reduce the amount of noise in + results, but they can still be accessed when explicitly used. The hidden + columns are: :code:`log_path`, :code:`log_text`, :code:`log_body`, and + :code:`log_raw_text`. + +You can activate the SQL prompt by pressing the :kbd:`;` key. At the +prompt, you can start typing in the desired SQL statement and/or double-tap +:kbd:`TAB` to activate auto-completion. A help window will appear above +the prompt to guide you in the usage of SQL keywords and functions. + +.. figure:: sql-help.png + :align: center + + Screenshot of the online help for the SQL prompt. + +.. figure:: group_concat-help.png + :align: center + + Screenshot of the online help for the :code:`group_concat()` function. + +A simple query to perform on an Apache access log might be to get the average +and maximum number of bytes returned by the server, grouped by IP address: + +.. code-block:: custsqlite + + ;SELECT c_ip, avg(sc_bytes), max(sc_bytes) FROM access_log GROUP BY c_ip + +After pressing :kbd:`Enter`, SQLite will execute the query using **lnav**'s +virtual table implementation to extract the data directly from the log files. +Once the query has finished, the main window will switch to the DB view to +show the results. Press :kbd:`q` to return to the log view and press :kbd:`v` +to return to the log view. If the SQL results contain a +:code:`log_line` column, you can press to :kbd:`Shift` + :kbd:`V` to +switch between the DB view and the log + +.. figure:: query-results.png + :align: center + + Screenshot of the SQL results view. + +The DB view has the following display features: + +* Column headers stick to the top of the view when scrolling. +* A stacked bar chart of the numeric column values is displayed underneath the + rows. Pressing :kbd:`TAB` will cycle through displaying no columns, each + individual column, or all columns. +* JSON columns in the top row can be pretty-printed by pressing :kbd:`p`. + The display will show the value and JSON-Pointer path that can be passed to + the `jget`_ function. + + +Log Tables +---------- + +Each log format has its own database table that can be used to access log +messages that match that format. The table name is the same as the format +name, for example, the :code:`syslog_log` format will have a table that is +also named :code:`syslog_log`. There is also an :code:`all_logs` table +that provides access to all messages from all formats. + +.. note:: Only the displayed log messages are reflected in the SQLite + interface. Any log messages that have been filtered out are not + accessible. + +The columns in the log tables are made up of several builtins along with +the values captured by the log format specification. Use the :code:`.schema` +command in the SQL prompt to examine a dump of the current database schema. + +The following columns are builtin and included in a :code:`SELECT *`: + + :log_line: The line number for the message in the log view. + :log_part: The partition the message is in. This column can be changed by + an :code:`UPDATE` or the :ref:`:parition-name` command. + :log_time: The adjusted timestamp for the log message. This time can differ + from the log message's time stamp if it arrived out-of-order and the log + format expects log files to be time-ordered. + :log_actual_time: The log messages original timestamp in the file. + :log_idle_msecs: The difference in time between this messages and the + previous. The unit of time is milliseconds. + :log_level: The log message level. + :log_mark: True if the log message was marked by the user. + :log_comment: The comment for the message. This column can be changed by + an :code:`UPDATE` or the :ref:`:comment` command. + :log_tags: A JSON list of tags for the message. This column can be changed + by an :code:`UPDATE` or the :ref:`:tag` command. + :log_filters: A JSON list of filter IDs that matched this message + +The following columns are builtin and are hidden, so they will *not* be +included in a :code:`SELECT *`: + + :log_time_msecs: The adjusted timestamp for the log message as the number of + milliseconds from the epoch. This column can be more efficient to use for + time-related operations, like :ref:`timeslice()`. + :log_path: The path to the log file this message is from. + :log_text: The full text of the log message. + :log_body: The body of the log message. + :log_raw_text: The raw text of this message from the log file. In this case + of JSON and CSV logs, this will be the exact line of JSON-Line and CSV + text from the file. + +Extensions +---------- + +To make it easier to analyze log data from within **lnav**, there are several +built-in extensions that provide extra functions and collators beyond those +`provided by SQLite `_. The majority +of the functions are from the +`extensions-functions.c `_ file available from +the `sqlite.org `_ web site. + +.. tip:: You can include a SQLite database file on the command-line and use + **lnav**'s interface to perform queries. The database will be attached with + a name based on the database file name. + +Commands +-------- + +A SQL command is an internal macro implemented by lnav. + +* .schema - Open the schema view. This view contains a dump of the schema + for the internal tables and any tables in attached databases. +* .msgformats - Executes a canned query that groups and counts log messages by + the format of their message bodies. This command can be useful for quickly + finding out the types of messages that are most common in a log file. + +Variables +--------- + +The following variables are available in SQL statements: + +* $LINES - The number of lines in the terminal window. +* $COLS - The number of columns in the terminal window. + +Environment +----------- + +Environment variables can be accessed in queries using the usual syntax of +:code:`$VAR_NAME`. For example, to read the value of the "USER" variable, you +can write: + +.. code-block:: custsqlite + + ;SELECT $USER + +.. _collators: + +Collators +--------- + +* **naturalcase** - Compare strings "naturally" so that number values in the + string are compared based on their numeric value and not their character + values. For example, "foo10" would be considered greater than "foo2". +* **naturalnocase** - The same as naturalcase, but case-insensitive. +* **ipaddress** - Compare IPv4/IPv6 addresses. + +Reference +--------- + +The following is a reference of the SQL syntax and functions that are available: + +.. include:: ../../src/internals/sql-ref.rst diff --git a/docs/source/sqltab.rst b/docs/source/sqltab.rst new file mode 100644 index 0000000..ca2fefb --- /dev/null +++ b/docs/source/sqltab.rst @@ -0,0 +1,217 @@ +.. _sql-tab: + +SQLite Tables Reference +======================= + +In addition to the tables generated for each log format, **lnav** includes +the following tables/views: + +* `environ`_ +* `lnav_events`_ +* `lnav_file`_ +* `lnav_user_notifications`_ +* `lnav_views`_ +* `lnav_views_echo`_ +* `lnav_view_stack`_ +* `lnav_view_filters`_ +* `lnav_view_filter_stats`_ +* `lnav_view_filters_and_stats`_ +* `all_logs`_ +* `http_status_codes`_ +* `regexp_capture(, )`_ + +These extra tables provide useful information and can let you manipulate +**lnav**'s internal state. You can get a dump of the entire database schema +by executing the '.schema' SQL command, like so:: + + ;.schema + +environ +------- + +The **environ** table gives you access to the **lnav** process' environment +variables. You can SELECT, INSERT, and UPDATE environment variables, like +so: + +.. code-block:: custsqlite + + ;SELECT * FROM environ WHERE name = 'SHELL' + name value + SHELL /bin/tcsh + + ;UPDATE environ SET value = '/bin/sh' WHERE name = 'SHELL' + +Environment variables can be used to store simple values or pass values +from **lnav**'s SQL environment to **lnav**'s commands. For example, the +"open" command will do variable substitution, so you can insert a variable +named "FILENAME" and then open it in **lnav** by referencing it with +"$FILENAME": + +.. code-block:: custsqlite + + ;INSERT INTO environ VALUES ('FILENAME', '/path/to/file') + :open $FILENAME + + +.. _table_lnav_events: + +lnav_events +----------- + +The **lnav_events** table allows you to react to events that occur while +**lnav** is running using SQLite triggers. For example, when a file is +opened, a row is inserted into the :code:`lnav_events` table that contains +a timestamp and a JSON object with the event ID and the path of the file. +The following columns are available in this table: + + :ts: The timestamp of the event. + :content: A JSON object that contains the event information. See the + :ref:`event_reference` for more information about the types + of events that are available. + +lnav_file +--------- + +The **lnav_file** table allows you to examine and perform limited updates to +the metadata for the files that are currently loaded into **lnav**. The +following columns are available in this table: + + :device: The device the file is stored on. + :inode: The inode for the file on the device. + :filepath: If this is a real file, it will be the absolute path. Otherwise, + it is a symbolic name. If it is a symbolic name, it can be UPDATEd so that + this file will be considered when saving and loading session information. + :format: The log file format for the file. + :lines: The number of lines in the file. + :time_offset: The millisecond offset for timestamps. This column can be + UPDATEd to change the offset of timestamps in the file. + +.. _table_lnav_user_notifications: + +lnav_user_notifications +----------------------- + +The :code:`lnav_user_notifications` table allows you to display a custom message +in the top-right corner of the UI. For example, to display "Hello, World!", +you can enter: + +.. code-block:: custsqlite + + ;REPLACE INTO lnav_user_notifications (message) VALUES ('Hello, World!') + +There are additional columns to have finer control of what is displayed and +when: + + :id: The unique ID for the message, defaults to "org.lnav.user". This is + the primary key for the table, so more than one type of message is not + allowed. + :priority: The priority of the message. Higher priority messages will be + displayed until they are cleared or are expired. + :created: The time the message was created. + :expiration: The time when the message should expire or NULL if it should + not automatically expire. + :views: A JSON array of view names where the message is applicable or NULL + if the message should be shown in all views. + :message: The message itself. + +This table will most likely be used in combination with :ref:`Events` and the +`lnav_views_echo`_ table. + +lnav_views +---------- + +The **lnav_views** table allows you to SELECT and UPDATE information related +to **lnav**'s "views" (e.g. log, text, ...). The following columns are +available in this table: + + :name: The name of the view. + :top: The line number at the top of the view. This value can be UPDATEd to + move the view to the given line. + :left: The left-most column number to display. This value can be UPDATEd to + move the view left or right. + :height: The number of lines that are displayed on the screen. + :inner_height: The number of lines of content being displayed. + :top_time: The timestamp of the top line in the view or NULL if the view is + not time-based. This value can be UPDATEd to move the view to the given + time. + :paused: Indicates if the view is paused and will not load new data. + :search: The search string for this view. This value can be UPDATEd to + initiate a text search in this view. + +lnav_views_echo +--------------- + +The :code:`lnav_views_echo` table is a real SQLite table that you can create +TRIGGERs on in order to react to users moving around in a view. + +.. note:: + + The table is periodically updated to reflect the current state of the views. + The changes are *not* performed immediately after the user action. + +lnav_view_stack +--------------- + +The **lnav_view_stack** table allows you to SELECT and DELETE from the stack of +**lnav** "views" (e.g. log, text, ...). The following columns are available in +this table: + + :name: The name of the view. + +.. _table_lnav_view_filters: + +lnav_view_filters +----------------- + +The **lnav_view_filters** table allows you to manipulate the filters in the +**lnav** views. The following columns are available in this table: + + :view_name: The name of the view the filter is applied to. + :filter_id: The filter identifier. This will be assigned on insertion. + :enabled: Indicates whether this filter is enabled or disabled. + :type: The type of filter, either 'in' or 'out'. + :pattern: The regular expression to filter on. + +This table supports SELECT, INSERT, UPDATE, and DELETE on the table rows to +read, create, update, and delete filters for the views. + +lnav_view_filter_stats +---------------------- + +The **lnav_view_filter_stats** table allows you to get information about how +many lines matched a given filter. The following columns are available in +this table: + + :view_name: The name of the view. + :filter_id: The filter identifier. + :hits: The number of lines that matched this filter. + +This table is read-only. + +lnav_view_filters_and_stats +--------------------------- + +The **lnav_view_filters_and_stats** view joins the **lnav_view_filters** table +with the **lnav_view_filter_stats** table into a single view for ease of use. + +all_logs +-------- + +.. f0:sql.tables.all_logs + +The **all_logs** table lets you query the format derived from the **lnav** +log message parser that is used to automatically extract data, see +:ref:`data-ext` for more details. + +http_status_codes +----------------- + +The **http_status_codes** table is a handy reference that can be used to turn +HTTP status codes into human-readable messages. + +regexp_capture(, ) +--------------------------------- + +The **regexp_capture()** table-valued function applies the regular expression +to the given string and returns detailed results for the captured portions of +the string. diff --git a/docs/source/ui.rst b/docs/source/ui.rst new file mode 100644 index 0000000..c7d7ffe --- /dev/null +++ b/docs/source/ui.rst @@ -0,0 +1,280 @@ +.. _ui: + +User Interface +============== + +The **lnav** TUI displays the content of the current "view" in the middle, +with status bars above and below, and the interactive prompt as the last line. + +.. figure:: lnav-ui.png + :align: center + :alt: Screenshot of lnav showing a mix of syslog and web access_log messages. + + Screenshot of **lnav** viewing syslog and web access_log messages. + +The default view shows the log messages from the log files that have been +loaded. There are other views for displaying content like plaintext files +and SQL results. The :ref:`ui_views` section describes the characteristics of +each view in more detail. You can switch to the different views using the +hotkeys described in the :ref:`hotkeys_display` section or by pressing +:kbd:`ENTER` to activate the breadcrumb bar, moving to the first crumb, and +then selecting the desired view. You can switch back to the previous view by +pressing :kbd:`q`. You can switch forward to the new view by pressing +:kbd:`a`. If the views are time-based (e.g. log and histogram), pressing +:kbd:`Shift` + :kbd:`q` and :kbd:`Shift` + :kbd:`a` will synchronize the top +times in the views. + +The right side of the display has a proportionally sized 'scrollbar' that +shows: + +* the current position in the file; +* the locations of errors/warnings in the log files by using red or yellow + coloring; +* the locations of search hits by using a tick-mark pointing to the left; +* the locations of bookmarks by using a tick-mark pointing to the right. + +Top Status Bar +-------------- + +The top status bar shows the current time and messages stored in the +:ref:`table_lnav_user_notifications` table. + +Below the top status bar is the breadcrumb bar that displays the semantic +location of the top line in the main view. For example, within a +pretty-printed JSON document, it will show the path to property at the top +of the view. The actual content of the bar depends on the current view and +will be updated as you navigate around the main view. The bar can also be +used to navigate around the document by focusing on it. + +Breadcrumb Bar +-------------- + +.. figure:: lnav-breadcrumbs-help.png + :align: center + :figwidth: 90% + + Screenshot of the breadcrumb bar focused and navigating the help text + +To focus on the breadcrumb bar, press :kbd:`ENTER`. The :kbd:`←`/:kbd:`→` +cursor keys can be used to select a crumb and the :kbd:`↑`/:kbd:`↓` keys can +be used select a value of that crumb. To accept a value and drop focus on the +bar, press :kbd:`ENTER`. To accept a value and move to the next crumb, press +:kbd:`→`. Using :kbd:`→` makes it quicker to drill down into a document +without having to constantly switch focus. To drop focus on the bar without +accepting anything, press :kbd:`Escape`. + +There are three types of crumbs: + +* a dropdown where one of a limited set of values can be selected; +* a combobox where a value can be entered directly or selected; +* a numeric input for entering array indexes. + +When a dropdown or combobox is selected, you can type part of the desired value +to filter the list of values. For example, the first crumb is always the +current view, typing in "hi" will filter the list down to the "HIST" value. + +Configuration Panels +-------------------- + +.. figure:: lnav-config-header.png + :align: center + :figwidth: 90% + + Screenshot of the header for the configuration panels when they are hidden. + +After the main view content, there is a header bar for two configuration +panels: Files and Filters. These panels provide visual access to parts of +lnav's configuration. To access the panels, press the :kbd:`TAB` key. +To hide the panels again, press :kbd:`q`. + +.. figure:: lnav-files-panel.png + :align: center + :figwidth: 90% + + Screenshot of the files panel showing the loaded files. + +The Files panel is open initially to display progress in loading files. +The following information can be displayed for each file: + +* the "unique" portion of the path relative to the other files; +* the amount of data that has been indexed; +* the date range of log messages contained in the file; +* the errors that were encountered while trying to index the file; +* the notes recorded for files where some automatic action was taken, + like hiding the file if it was seen as a duplicate of another file. + +.. figure:: lnav-filters-panel.png + :align: center + :figwidth: 90% + + Screenshot of the filters panel showing an OUT and a disabled IN filter. + +If the view supports filtering, there will be a status line showing the +following: + +* the number of enabled filters and the total number of filters; +* the number of lines that are **not** displayed because of filtering. + +To edit the filters, you can press TAB to change the focus from the main +view to the filter editor. The editor allows you to create, enable/disable, +and delete filters easily. + +Bottom Status Bar +----------------- + +The second to last line is the bottom status bar, which shows the following: + +* the line number of the top line, starting from zero; +* the location within the view, as a percentage; +* the current search hit, the total number of hits, and the search term; +* the loading indicator. + +When the interactive prompt is active, this bar can show the prompt +description, help text, or error message. + +Prompt +------ + +Finally, the last line on the display is where you can enter search +patterns and execute internal commands, such as converting a +unix-timestamp into a human-readable date. The following key-presses +will activate a corresponding prompt: + +* :kbd:`/` - The search prompt. You can enter a PCRE2-flavored regular + expression to search for in the current view. +* :kbd:`:` - The command prompt. Commands are used to perform common + operations. +* :kbd:`;` - The SQL prompt. SQL queries can be used for log analysis + and manipulating **lnav**'s state. +* :kbd:`|` - The script prompt. Enter a path to the lnav script to + execute, along with the arguments to pass in. + +The command-line is by the readline library, so the usual set of keyboard +shortcuts can be used for editing and moving within the command-line. + +.. _ui_views: + +Views +----- + +The accessible content within lnav is separated into the following views. + +LOG +^^^ + +The log view displays the log messages from any loaded log files in time +order. This view will be shown by default if any log messages are available. + +On color displays, the log messages will be highlighted as follows: + +* Errors will be colored in red; +* warnings will be yellow; +* search hits are reverse video; +* various color highlights will be applied to: IP addresses, SQL keywords, + XML tags, file and line numbers in Java backtraces, and quoted strings; +* "identifiers" in the messages will be randomly assigned colors based on their + content (works best on "xterm-256color" terminals). + +.. note:: + + If the coloring is too much for your tastes, you can change to the + "grayscale" theme by entering the following command: + + .. code-block:: lnav + + :config /ui/theme grayscale + +.. note:: + + If a log message has a timestamp that is out-of-order with its neighboring + messages, the timestamp will be highlighted in yellow. When one of these + messages is at the top of the log view, an overlay will display the + difference between the "actual time" and the "received time". The "actual + time" is the original textual timestamp. The "received time" is the time + of an earlier message that is larger than this log message's time. + +The breadcrumb bar will show the following crumbs: + +* the timestamp for the top line; +* the log format for the top line; +* the name of the file the top line was pulled from; +* the "operation ID" of the top log message, if it is supported by the log + format. + +These crumbs are interactive and can be used to navigate to different parts +of the log view. For example, selecting a different value in the log format +crumb will jump to the first message with that format. + +TEXT +^^^^ + +The text view displays files for which lnav could not detect any log messages. + +Markdown +"""""""" + +Files with an :code:`.md` (or :code:`.markdown`) extension will be treated as +Markdown files and rendered separately. + +DB +^^ + +The DB view shows the results of queries done through the SQLite interface. +You can execute a query by pressing :kbd:`;` and then entering a SQL statement. +You can switch to the SQL view by pressing :kbd:`v`. + +HELP +^^^^ + +The help view displays the builtin help text. Press :kbd:`?` to switch to the +help view at any time. While in the help view, the breadcrumb bar can be used +to navigate to different sections of the document. + +HIST +^^^^ + +The histogram view displays a stacked bar chart of messages over time +classified by their log level and whether they've been bookmarked. Press +:kbd:`i` to switch back and forth to the histogram view. You can also press +:kbd:`Shift`+:kbd:`i` to toggle the histogram view while synchronizing the top +time. While in the histogram view, pressing :kbd:`z`/:kbd:`Shift`+:kbd:`z` +will zoom in/out. + +PRETTY +^^^^^^ + +The pretty-print view takes the text displayed in the current view and shows +the result of a pretty-printer run on that text. For example, if a log +message contained an XML message on a single line, the pretty-printer would +break the XML across multiple lines with appropriate indentation. + +SCHEMA +^^^^^^ + +The schema view displays the current schema of the builtin SQLite database. + +SPECTRO +^^^^^^^ + +The spectrogram view is a "three"-dimensional display of data points of a log +field or a SQL query column. The dimensions are time on the Y axis, the range +of data point values on the X axis, and the number of data points as a color. +For example, if you were to visualize process CPU usage over time, the range +of values on the X axis would be CPU percentages and there would be colored +blocks at each point on the line where a process had that CPU percentage, like +so + +.. figure:: lnav-spectro-cpu-pct.png + :align: center + + Screenshot of the **lnav** spectrogram view showing CPU usage of processes. + +The colors correspond to the relative number of data points in a bucket. +The legend overlaid at the top line in the view shows the counts of data +points that are in a particular color, with green having the fewest number of +data points, yellow the middle, and red the most. You can select a particular +bucket using the cursor keys to see the exact number of data points and the +range of values. The panel at the bottom of the view shows the data points +themselves from the original source, the log file or the SQL query results. +You can press :kbd:`TAB` to focus on the details panel so you can scroll +around and get a closer look at the values. diff --git a/docs/source/usage.rst b/docs/source/usage.rst new file mode 100644 index 0000000..d43ed52 --- /dev/null +++ b/docs/source/usage.rst @@ -0,0 +1,291 @@ +.. _usage: + +Usage +===== + +This chapter contains an overview of how to use **lnav**. + + +Basic Controls +-------------- + +Like most file viewers, scrolling through files can be done with the usual +:ref:`hotkeys`. For non-trivial operations, you can enter the +:ref:`command` prompt by pressing :kbd:`:`. To analyze data in a +log file, you can enter the :ref:`SQL prompt` by pressing :kbd:`;`. + +.. tip:: + + Check the bottom right corner of the screen for tips on hotkeys that might + be useful in the current context. + + .. figure:: hotkey-tips.png + :align: center + + When **lnav** is first open, it suggests using :kbd:`e` and + :kbd:`Shift` + :kbd:`e` to jump to error messages. + + +Viewing Files +------------- + +The files to view in **lnav** can be given on the command-line or passed to the +:ref:`:open` command. A +`glob pattern `_ can be given +to watch for files with a common name. If the path is a directory, all of the +files in the directory will be opened and the directory will be monitored for +files to be added or removed from the view. If the path is an archive or +compressed file (and lnav was built with libarchive), the archive will be +extracted to a temporary location and the files within will be loaded. The +files that are found will be scanned to identify their file format. Files +that match a log format will be collated by time and displayed in the LOG +view. Plain text files can be viewed in the TEXT view, which can be accessed +by pressing :kbd:`t`. + + +Archive Support +^^^^^^^^^^^^^^^ + +.. f0:archive + +If **lnav** is compiled with `libarchive `_, +any files to be opened will be examined to see if they are a supported archive +type. If so, the contents of the archive will be extracted to the +:code:`$TMPDIR/lnav-user-${UID}-work/archives/` directory. Once extracted, the +files within will be loaded into lnav. To speed up opening large amounts of +files, any file that meets the following conditions will be automatically +hidden and not indexed: + +* Binary files +* Plain text files that are larger than 128KB +* Duplicate log files + +The unpacked files will be left in the temporary directory after exiting +**lnav** so that opening the same archive again will be faster. Unpacked +archives that have not been accessed in the past two days will be automatically +deleted the next time **lnav** is started. + + +.. _remote: + +Remote Files +^^^^^^^^^^^^ + +Files on remote machines can be viewed and tailed if you have access to the +machines via SSH. First, make sure you can SSH into the remote machine without +any interaction by: 1) accepting the host key as known and 2) copying your +identity's public key to the :file:`.ssh/authorized_keys` file on the remote +machine. Once the setup is complete, you can open a file on a remote host +using the same syntax as :command:`scp(1)` where the username and host are +given, followed by a colon, and then the path to the files, like so:: + + [user@]host:/path/to/logs + +For example, to open :file:`/var/log/syslog.log` on "host1.example.com" as the +user "dean", you would write: + +.. prompt:: bash + + lnav dean@host1.example.com:/var/log/syslog.log + +Remote files can also be opened using the :ref:`:open` command. Opening +a remote file in the TUI has the advantage that the file path can be +:kbd:`TAB`-completed and a preview is shown of the first few lines of the +file. + +.. note:: + + If lnav is installed from the `snap `_, you will + need to connect it to the + `ssh-keys plug `_ using the + following command: + + .. prompt:: bash + + sudo snap connect lnav:ssh-keys + +.. note:: + + Remote file access is implemented by transferring an + `αcτµαlly pδrταblε εxεcµταblε `_ to the + destination and invoking it. An APE binary can run on most any x86_64 + machine and OS (i.e. MacOS, Linux, FreeBSD, Windows). The binary is + baked into the lnav executable itself, so there is no extra setup that + needs to be done on the remote machine. + + The binary file is named ``tailer.bin.XXXXXX`` where *XXXXXX* is 6 random digits. + The file is, under normal circumstancies, deleted immediately. + +Searching +--------- + +Any log messages that are loaded into **lnav** are indexed by time and log +level (e.g. error, warning) to make searching quick and easy with +:ref:`hotkeys`. For example, pressing :kbd:`e` will jump to the +next error in the file and pressing :kbd:`Shift` + :kbd:`e` will jump to +the previous error. Plain text searches can be done by pressing :kbd:`/` +to enter the search prompt. A regular expression can be entered into the +prompt to start a search through the current view. + + +.. _filtering: + +Filtering +--------- + +To reduce the amount of noise in a log file, **lnav** can hide log messages +that match certain criteria. The following sub-sections explain ways to go +about that. + + +Regular Expression Match +^^^^^^^^^^^^^^^^^^^^^^^^ + +If there are log messages that you are not interested in, you can do a +"filter out" to hide messages that match a pattern. A filter can be created +using the interactive editor, the :ref:`:filter-out` command, or +by doing an :code:`INSERT` into the +:ref:`lnav_view_filters` table. + +If there are log messages that you are only interested in, you can do a +"filter in" to only show messages that match a pattern. The filter can be +created using the interactive editor, the :ref:`:filter-in` command, +or by doing an :code:`INSERT` into the +:ref:`lnav_view_filters` table. + + +SQLite Expression +^^^^^^^^^^^^^^^^^ + +Complex filtering can be done by passing a SQLite expression to the +:ref:`:filter-expr` command. The expression will be executed for +every log message and if it returns true, the line will be shown in the log +view. + + +Time +^^^^ + +To limit log messages to a given time frame, the +:ref:`:hide-lines-before` and +:ref:`:hide-lines-after` commands can be used to specify +the beginning and end of the time frame. + + +Log level +^^^^^^^^^ + +To hide messages below a certain log level, you can use the +:ref:`:set-min-log-level` command. + +.. _search_tables: + +Search Tables +------------- + +Search tables allow you to access arbitrary data in log messages through +SQLite virtual tables. If there is some data in a log message that you can +match with a regular expression, you can create a search-table that matches +that data and any capture groups will be plumbed through as columns in the +search table. + +Creating a search table can be done interactively using the +:ref:`:create-search-table` command or by adding it to +a :ref:`log format definition`. The main difference between +the two is that tables defined as part of a format will only search messages +from log files with that format and the tables will include log message +columns defined in that format. Whereas a table created with the command +will search messages from all different formats and no format-specific +columns will be included in the table. + +.. _taking_notes: + +Taking Notes +------------ + +A few of the columns in the log tables can be updated on a row-by-row basis to +allow you to take notes. The majority of the columns in a log table are +read-only since they are backed by the log files themselves. However, the +following columns can be changed by an :code:`UPDATE` statement: + +* **log_part** - The "partition" the log message belongs to. This column can + also be changed by the :ref:`:partition-name` command. +* **log_mark** - Indicates whether the line has been bookmarked. +* **log_comment** - A free-form text field for storing commentary. This + column can also be changed by the :ref:`:comment` command. +* **log_tags** - A JSON list of tags associated with the log message. This + column can also be changed by the :ref:`:tag` command. + +While these columns can be updated by through other means, using the SQL +interface allows you to make changes automatically and en masse. For example, +to bookmark all lines that have the text "something interesting" in the log +message body, you can execute: + +.. code-block:: custsqlite + + ;UPDATE all_logs SET log_mark = 1 WHERE log_body LIKE '%something interesting%' + +As a more advanced example of the power afforded by SQL and **lnav**'s virtual +tables, we will tag log messages where the IP address bound by dhclient has +changed. For example, if dhclient reports "bound to 10.0.0.1" initially and +then reports "bound to 10.0.0.2", we want to tag only the messages where the +IP address was different from the previous message. While this can be done +with a single SQL statement [#]_, we will break things down into a few steps for +this example. First, we will use the :ref:`:create-search-table` +command to match the dhclient message and extract the IP address: + +.. code-block:: lnav + + :create-search-table dhclient_ip bound to (?[^ ]+) + +The above command will create a new table named :code:`dhclient_ip` with the +standard log columns and an :code:`ip` column that contains the IP address. +Next, we will create a view over the :code:`dhclient_ip` table that returns +the log message line number, the IP address from the current row and the IP +address from the previous row: + +.. code-block:: custsqlite + + ;CREATE VIEW IF NOT EXISTS dhclient_ip_changes AS SELECT log_line, ip, lag(ip) OVER (ORDER BY log_line) AS prev_ip FROM dhclient_ip + +Finally, the following :code:`UPDATE` statement will concatenate the tag +"#ipchanged" onto the :code:`log_tags` column for any rows in the view where +the current IP is different from the previous IP: + +.. code-block:: custsqlite + + ;UPDATE syslog_log SET log_tags = json_concat(log_tags, '#ipchanged') WHERE log_line IN (SELECT log_line FROM dhclient_ip_changes WHERE ip != prev_ip) + +Since the above can be a lot to type out interactively, you can put these +commands into a :ref:`script` and execute that script with the +:kbd:`\|` hotkey. + +.. [#] The expression :code:`regexp_match('bound to ([^ ]+)', log_body) as ip` + can be used to extract the IP address from the log message body. + +Sharing Sessions With Others +---------------------------- + +After setting up filters, bookmarks, and making notes, you might want to share +your work with others. If they have access to the same log files, you can +use the :ref:`:export-session-to` command to write an +executable **lnav** script that will recreate the current session state. The +script contains various SQL statements and **lnav** commands that capture the +current state. So, you should feel free to modify the script or use it as a +reference to learn about more advanced uses of lnav. + +The script will capture the file paths that were explicitly specified and +not the files that were actually opened. For example, if you specified +"/var/log" on the command line, the script will include +:code:`:open /var/log/*` and not an individual open for each file in that +directory. + +Also, in order to support archives of log files, lnav will try to find the +directory where the archive was unpacked and use that as the base for the +:code:`:open` command. Currently, this is done by searching for the top +"README" file in the directory hierarchy containing the files [1]_. The +consumer of the session script can then set the :code:`LOG_DIR_0` (or 1, 2, +...) environment variable to change where the log files will be loaded from. + +.. [1] It is assumed a log archive would have a descriptive README file. + Other heuristics may be added in the future. diff --git a/docs/tutorials/playground/index.md b/docs/tutorials/playground/index.md new file mode 100644 index 0000000..e1516b1 --- /dev/null +++ b/docs/tutorials/playground/index.md @@ -0,0 +1,9 @@ + +# Playground + +Welcome to the **lnav** playground! + +There are some sample files loaded into the log and text views. +Press `q` to switch back to the log view and start exploring. +You can also press `f` in this view to switch to the other +text files that are loaded, like a markdown sample. diff --git a/docs/tutorials/playground/logs/access_log.gz b/docs/tutorials/playground/logs/access_log.gz new file mode 100644 index 0000000..485cf4e Binary files /dev/null and b/docs/tutorials/playground/logs/access_log.gz differ diff --git a/docs/tutorials/playground/logs/messages.gz b/docs/tutorials/playground/logs/messages.gz new file mode 100644 index 0000000..e6ad012 Binary files /dev/null and b/docs/tutorials/playground/logs/messages.gz differ diff --git a/docs/tutorials/playground/run.sh b/docs/tutorials/playground/run.sh new file mode 100755 index 0000000..4a6723d --- /dev/null +++ b/docs/tutorials/playground/run.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +export LNAVSECURE=1 +export TERM=xterm-256color + +timeout --foreground --kill-after=30s 10m lnav \ + -d "/tmp/$(echo "playground."$(date "+%Y-%m-%dT%H-%M-%S")".$$.log")" \ + /tutorials/playground/logs \ + /tutorials/playground/text \ + /tutorials/playground/index.md#playground + +if [ $? = 124 ]; then + echo "error: reached connection time limit, reconnect if you're not a bot." +else + echo "Thanks for trying out lnav! Have a nice day!" +fi diff --git a/docs/tutorials/playground/text/markdown-sample.md b/docs/tutorials/playground/text/markdown-sample.md new file mode 100644 index 0000000..4b56cae --- /dev/null +++ b/docs/tutorials/playground/text/markdown-sample.md @@ -0,0 +1,157 @@ +An h1 header +============ + +Paragraphs are separated by a blank line. + +2nd paragraph. *Italic*, **bold**, and `monospace`. Itemized lists +look like: + + * this one + * that one + * the other one + +Note that --- not considering the asterisk --- the actual text +content starts at 4-columns in. + +> Block quotes are +> written like so. +> +> They can span multiple paragraphs, +> if you like. + +Use 3 dashes for an em-dash. Use 2 dashes for ranges (ex., "it's all +in chapters 12--14"). Three dots ... will be converted to an ellipsis. +Unicode is supported. ☺ + + + +An h2 header +------------ + +Here's a numbered list: + + 1. first item + 2. second item + 3. third item + +Note again how the actual text starts at 4 columns in (4 characters +from the left side). Here's a code sample: + + # Let me re-iterate ... + for i in 1 .. 10 { do-something(i) } + +As you probably guessed, indented 4 spaces. By the way, instead of +indenting the block, you can use delimited blocks, if you like: + +~~~ +define foobar() { + print "Welcome to flavor country!"; +} +~~~ + +(which makes copying & pasting easier). You can optionally mark the +delimited block for Pandoc to syntax highlight it: + +~~~python +import time +# Quick, count to ten! +for i in range(10): + # (but not *too* quick) + time.sleep(0.5) + print i +~~~ + + + +### An h3 header ### + +Now a nested list: + + 1. First, get these ingredients: + + * carrots + * celery + * lentils + + 2. Boil some water. + + 3. Dump everything in the pot and follow + this algorithm: + + find wooden spoon + uncover pot + stir + cover pot + balance wooden spoon precariously on pot handle + wait 10 minutes + goto first step (or shut off burner when done) + + Do not bump wooden spoon or it will fall. + +Notice again how text always lines up on 4-space indents (including +that last line which continues item 3 above). + +Here's a link to [a website](https://lnav.org), to a [local +doc](../index.md), and to a [section heading in the current +doc](#an-h2-header). Here's a footnote [^1]. + +[^1]: Footnote text goes here. + +Tables can look like this: + +size material color +---- ------------ ------------ +9 leather brown +10 hemp canvas natural +11 glass transparent + +Table: Shoes, their sizes, and what they're made of + +(The above is the caption for the table.) Pandoc also supports +multi-line tables: + +-------- ----------------------- +keyword text +-------- ----------------------- +red Sunsets, apples, and + other red or reddish + things. + +green Leaves, grass, frogs + and other things it's + not easy being. +-------- ----------------------- + +A horizontal rule follows. + +*** + +Here's a definition list: + +apples + : Good for making applesauce. +oranges + : Citrus! +tomatoes + : There's no "e" in tomatoe. + +Again, text is indented 4 spaces. (Put a blank line between each +term/definition pair to spread things out more.) + +Here's a "line block": + +| Line one +| Line too +| Line tree + +and images can be specified like so: + +![example image](../../../assets/images/lnav-front-page.png "An exemplary image") + +Inline math equations go in like so: $\omega = d\phi / dt$. Display +math should get its own line and be put in in double-dollarsigns: + +$$I = \int \rho R^{2} dV$$ + +And note that you can backslash-escape any punctuation characters +which you wish to be displayed literally, ex.: \`foo\`, \*bar\*, etc. \ No newline at end of file diff --git a/docs/tutorials/tutorial-lib/configs/tutorial1/config.json b/docs/tutorials/tutorial-lib/configs/tutorial1/config.json new file mode 100644 index 0000000..4407368 --- /dev/null +++ b/docs/tutorials/tutorial-lib/configs/tutorial1/config.json @@ -0,0 +1,18 @@ +{ + "$schema": "https://lnav.org/schemas/config-v1.schema.json", + "global": { + "lnav_tutorial_name": "tutorial1" + }, + "ui": { + "keymap-defs": { + "default": { + "x79": { + "command": "|lnav-tutorial-key-handler next" + }, + "x59": { + "command": "|lnav-tutorial-key-handler prev" + } + } + } + } +} \ No newline at end of file diff --git a/docs/tutorials/tutorial-lib/formats/tutorial-lib/lnav-tutorial-key-handler.lnav b/docs/tutorials/tutorial-lib/formats/tutorial-lib/lnav-tutorial-key-handler.lnav new file mode 100644 index 0000000..becadec --- /dev/null +++ b/docs/tutorials/tutorial-lib/formats/tutorial-lib/lnav-tutorial-key-handler.lnav @@ -0,0 +1,29 @@ + +;SELECT filepath AS tutorial_path FROM lnav_file + WHERE filepath GLOB '*/tutorial1/index.md' LIMIT 1 +;SELECT CASE + WHEN $1 = 'next' AND + step < (SELECT max(step) FROM lnav_tutorial_steps WHERE name = $lnav_tutorial_name) + THEN step + 1 + WHEN $1 = 'prev' AND step > 1 THEN step - 1 + ELSE step + END AS new_step + FROM lnav_tutorial_step WHERE name = $lnav_tutorial_name +;SELECT CASE + WHEN $1 = 'next' AND + step = (SELECT max(step) FROM lnav_tutorial_steps WHERE name = $lnav_tutorial_name) + THEN '#conclusion' + ELSE '#step-' || $new_step + END AS new_anchor + FROM lnav_tutorial_step WHERE name = $lnav_tutorial_name +;UPDATE lnav_tutorial_step SET step = $new_step WHERE name = $lnav_tutorial_name +;UPDATE lnav_views SET top_meta = json_object( + 'file', $tutorial_path, + 'anchor', $new_anchor +) + WHERE name = 'text' +:switch-to-view text +;UPDATE lnav_views SET top = 0, left = 0 + WHERE name = 'log' +;REPLACE INTO lnav_user_notifications (id, views, message) + SELECT * FROM lnav_tutorial_log_notification; \ No newline at end of file diff --git a/docs/tutorials/tutorial-lib/formats/tutorial-lib/tutorial.sql b/docs/tutorials/tutorial-lib/formats/tutorial-lib/tutorial.sql new file mode 100644 index 0000000..2813357 --- /dev/null +++ b/docs/tutorials/tutorial-lib/formats/tutorial-lib/tutorial.sql @@ -0,0 +1,154 @@ + +-- Tracks the current step in the tutorial +CREATE TABLE lnav_tutorial_step +( + name TEXT NOT NULL PRIMARY KEY, + step INTEGER NOT NULL +); + +INSERT INTO lnav_tutorial_step + VALUES ('tutorial1', 1); + +-- A description of each step in the tutorial with its achievements +CREATE TABLE lnav_tutorial_steps +( + name TEXT NOT NULL, + step INTEGER NOT NULL, + achievements TEXT NOT NULL, + PRIMARY KEY (name, step) +); + +-- Tracks the progress through the achievements in a step of the tutorial +CREATE TABLE IF NOT EXISTS lnav_tutorial_progress +( + name TEXT NOT NULL, + step INTEGER NOT NULL, + achieved TEXT NOT NULL, + + PRIMARY KEY (name, step, achieved) +); + +CREATE TABLE IF NOT EXISTS lnav_tutorial_lines +( + name TEXT NOT NULL, + step INTEGER NOT NULL, + view_ptr TEXT NOT NULL, + view_value TEXT NOT NULL, + achievement TEXT NOT NULL, + log_comment TEXT +); + +-- Copy the tutorial data from the markdown frontmatter to +-- the appropriate tables. +CREATE TRIGGER IF NOT EXISTS add_tutorial_data + AFTER INSERT + ON lnav_events + WHEN jget(new.content, '/$schema') = 'https://lnav.org/event-file-format-detected-v1.schema.json' AND + jget(new.content, '/format') = 'text/markdown' +BEGIN + INSERT INTO lnav_tutorial_steps + SELECT jget(tutorial_meta, '/name'), + key + 1, + value + FROM (SELECT yaml_to_json(lnav_file_metadata.content) AS tutorial_meta + FROM lnav_file_metadata + WHERE filepath = jget(new.content, '/filename')) AS meta_content, + json_each(jget(meta_content.tutorial_meta, '/steps')); + + REPLACE INTO lnav_tutorial_lines + SELECT name, + step, + jget(value, '/view_ptr'), + jget(value, '/view_value'), + key, + jget(value, '/comment') + FROM lnav_tutorial_steps, + json_each(achievements) + WHERE jget(value, '/view_ptr') IS NOT NULL; + + REPLACE INTO lnav_user_notifications (id, views, message) + SELECT * + FROM lnav_tutorial_log_notification; +END; + +CREATE TRIGGER IF NOT EXISTS tutorial_move_log_after_load + AFTER INSERT + ON lnav_events + WHEN jget(new.content, '/$schema') = 'https://lnav.org/event-session-loaded-v1.schema.json' +BEGIN + UPDATE lnav_views SET top = 0 WHERE name = 'log'; +END; + +CREATE TRIGGER IF NOT EXISTS lnav_tutorial_view_listener UPDATE OF top + ON lnav_views_echo + WHEN new.name = 'log' +BEGIN + INSERT OR IGNORE INTO lnav_tutorial_progress + SELECT lnav_tutorial_lines.name, + lnav_tutorial_lines.step, + achievement + FROM lnav_tutorial_step, + lnav_tutorial_lines + WHERE lnav_tutorial_step.step = lnav_tutorial_lines.step + AND jget(json_object('top', new.top, + 'left', new.left, + 'search', new.search), + view_ptr) = view_value; + UPDATE all_logs + SET log_comment = (SELECT log_comment + FROM lnav_tutorial_step, + lnav_tutorial_lines + WHERE lnav_tutorial_step.step = lnav_tutorial_lines.step + AND lnav_tutorial_lines.log_comment IS NOT NULL + AND jget(json_object('top', new.top, + 'left', new.left, + 'search', new.search), view_ptr) = view_value) + WHERE log_line = new.top + AND log_comment IS NULL; +END; + +CREATE TABLE lnav_tutorial_message +( + msgid INTEGER PRIMARY KEY, + msg TEXT +); + +CREATE VIEW lnav_tutorial_current_achievements AS +SELECT key AS achievement, value + FROM lnav_tutorial_step, + lnav_tutorial_steps, json_each(lnav_tutorial_steps.achievements) + WHERE lnav_tutorial_step.step = lnav_tutorial_steps.step; + +CREATE VIEW lnav_tutorial_current_progress AS +SELECT achieved + FROM lnav_tutorial_step, + lnav_tutorial_progress + WHERE lnav_tutorial_step.step = lnav_tutorial_progress.step; + +CREATE VIEW lnav_tutorial_remaining_achievements AS +SELECT * + FROM lnav_tutorial_current_achievements + WHERE achievement NOT IN (SELECT * FROM lnav_tutorial_current_progress); + +CREATE VIEW lnav_tutorial_log_notification AS +SELECT * + FROM (SELECT 'org.lnav.tutorial.log' AS id, '["log"]' AS views, jget(value, '/notification') AS message + FROM lnav_tutorial_remaining_achievements + UNION ALL + SELECT 'org.lnav.tutorial.log' AS id, + '["log"]' AS views, + 'Press `y` to go to the next step in the tutorial' AS message) + LIMIT 1; + +CREATE TRIGGER IF NOT EXISTS lnav_tutorial_progress_listener + AFTER INSERT + ON lnav_tutorial_progress +BEGIN + DELETE FROM lnav_user_notifications WHERE id = 'org.lnav.tutorial.log'; + REPLACE INTO lnav_user_notifications (id, views, message) + SELECT * + FROM lnav_tutorial_log_notification; +END; + +REPLACE INTO lnav_user_notifications (id, views, message) + VALUES ('org.lnav.tutorial.text', '["text"]', 'Press `q` to go to the log view') diff --git a/docs/tutorials/tutorial1/index.md b/docs/tutorials/tutorial1/index.md new file mode 100644 index 0000000..a608ba1 --- /dev/null +++ b/docs/tutorials/tutorial1/index.md @@ -0,0 +1,145 @@ +--- +name: tutorial1 +steps: + - move-to-error: + description: "Move to an error" + view_ptr: /top + view_value: 6 + notification: | + Press `e`/`Shift+E` to move through the + errors + comment: | + You found the error! + [Log formats](https://docs.lnav.org/en/latest/formats.html#format-file-reference) + can define the log levels for a given message. + The [theme](https://docs.lnav.org/en/latest/config.html#theme-definitions) defines + how the levels are displayed. + move-to-warning: + description: "Move to a warning" + notification: | + Press `w`/`Shift+W` to move through the + warnings + view_ptr: /top + view_value: 3 + comment: | + You found the warning! The scrollbar on the right is highlighted + to show the position of + warnings and + errors in this + view. + - search-for-term: + description: "Search for something" + notification: "Press `/` to search for '1AF9...'" + view_ptr: /search + view_value: 1AF9293A-F42D-4318-BCDF-60234B240955 + move-to-next-hit: + description: "Move to the next hit" + notification: "Press `n`/`Shift+N` to move through the search hits" + view_ptr: /top + view_value: 53 + comment: | + The matching text in a search is highlighted in + reverse-video. + However, the text is not always on-screen, so the bar on the + left will also be highlighted. You can then press `>` to + move right to the next (horizontal) search hit. Pressing + `<` will move left to the previous (horizontal) hit or all + the way back to the start of the line. + move-right: + description: "Move to the right" + notification: "Press `>` to move horizontally to view the search hit" + view_ptr: /left + view_value: 582 + - move-to-half-hour: + description: "Move to the next half-hour" + notification: "Press `3`/`Shift+3` to move through the half-hour marks" + view_ptr: /top + view_value: 34 + comment: | + This file is in the _glog_ format and timestamps consist of the + year, month, and day squished together. This log message's + timestamp is March 22nd, 2017. You can see the timestamp for + the top line in the view in the breadcrumb bar. Next, go to the + log messages for the following day using `:goto March 23` or the + breadcrumb bar above. + move-to-timestamp: + description: "Move to a given timestamp" + notification: "Move to '**March 23**' using `:goto` or the breadcrumb bar" + view_ptr: /top + view_value: 79 + comment: | + Many different timestamp formats are recognized as well as + relative times, like `+1h` or `-2h`. +--- +# Tutorial 1 + +Welcome to the first _interactive_ **lnav** tutorial! + +This tutorial will guide you through the basics of navigating log files. +Pressing `q` will display an example log file to try out commands on. +Pressing `y` will return you to the next step in the tutorial. + +## Step 1 + +Finding errors quickly is one of the main use-cases for **lnav**. To +make that quick and easy, **lnav** parses the log messages in log files +as they are loaded and builds indexes of the errors and warnings. You +can then use the following hotkeys to jump to them in the log view: + +| Key | Action | +|-----------|----------------------------------------------------------------------------------| +| `e` | Move to the next error | +| `Shift+E` | Move to the previous error | +| `w` | Move to the next warning | +| `Shift+W` | Move to the previous warning | + +To complete this step in the tutorial, you'll need to navigate to the +errors and warnings in the sample log file. You can check the upper-right +↗↗↗ status bar for tips on what you need to do next. Now, press `q` to +switch to the log view and begin navigating the sample log file. + +## Step 2 + +To search for text in files, you can press `/` to enter the search +prompt. To make it easier to search for text that is on-screen, you +can press `TAB` to complete values that are shown on screen. For +example, to search for the UUID "1AF9293A-F42D-4318-BCDF-60234B240955" +that is in one of the error messages, you can enter "1AF9" and then +press `TAB` to complete the rest of the UUID. + +Press `q` to switch to the log view and try searching for the UUID. + +## Step 3 + +To move to a particular time in the logs, you have a few options: + +* The number keys can be used to move to messages at the ten-minute + marks within an hour. For example, pressing `2` will move to the + first message after the next twenty-minute mark, pressing `3` + will move to the next half-hour mark, and so on. +* Pressing `ENTER` to focus on the breadcrumb bar, then you + can press `TAB` (or right-arrow) to move to the time crumb. + With the time crumb selected, you can then type in an absolute + or relative time. Or, you can use the up and down arrow keys + to select a preset relative time. +* Pressing `:` will activate the command prompt, then you can use + the `:goto` command to move to a given timestamp (or line number). + +Press `q` to switch to the log view and try moving to different +times. + +## Conclusion + +That's all for now, thanks for your time! Visit the +[downloads](https://lnav.org/downloads) page to find out how to +download or install **lnav** for your system. The full +documentation is available at https://docs.lnav.org + +Press `q` to switch to the log view and then press `q` again to +exit **lnav**. + +## Colophon + +The source for this tutorial is available here: + +https://github.com/tstack/lnav/tree/master/docs/tutorials/ diff --git a/docs/tutorials/tutorial1/run.sh b/docs/tutorials/tutorial1/run.sh new file mode 100755 index 0000000..6785ac4 --- /dev/null +++ b/docs/tutorials/tutorial1/run.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +export LNAVSECURE=1 +export TERM=xterm-256color + +timeout --foreground --kill-after=30s 5m lnav \ + -d "/tmp/$(echo "tutorial1."$(date "+%Y-%m-%dT%H-%M-%S")".$$.log")" \ + -I /tutorials/tutorial-lib \ + /tutorials/tutorial1/tutorial1.glog \ + /tutorials/tutorial1/index.md#tutorial-1 + +if [ $? = 124 ]; then + echo "error: reached connection time limit, reconnect if you're not a bot." +else + echo "Thanks for trying out lnav! Have a nice day!" +fi diff --git a/docs/tutorials/tutorial1/tutorial1.glog b/docs/tutorials/tutorial1/tutorial1.glog new file mode 100644 index 0000000..b996d68 --- /dev/null +++ b/docs/tutorials/tutorial1/tutorial1.glog @@ -0,0 +1,100 @@ +I20170322 06:58:47.082758 61456 blaster.cc:112] Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. +I20170322 06:58:58.019562 121 demo.cc:123] Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +I20170322 06:58:59.059175 123 blaster.cc:6782] Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. +W20170322 06:59:16.062826 61456 demo.cc:352] Ut enim ad minim veniam, quis nostrud exercitation 1AF9293A-F42D-4318-BCDF-60234B240955 ullamco laboris nisi ut aliquip ex ea commodo consequat. +I20170322 06:59:28.084062 124 blaster.cc:112] Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. +I20170322 06:59:32.053551 123 loader.cc:13] Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +E20170322 06:59:53.084969 123 loader.cc:552] Excepteur sint occaecat cupidatat non proident 1AF9293A-F42D-4318-BCDF-60234B240955, sunt in culpa qui officia deserunt mollit anim id est laborum. +I20170322 07:00:00.096693 123 blaster.cc:112] Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. +I20170322 07:00:03.049849 123 demo.cc:352] Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. +I20170322 07:00:08.070575 123 blaster.cc:6782] Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +I20170322 07:00:23.019849 123 blaster.cc:352] Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. +I20170322 07:00:28.022692 61457 loader.cc:552] Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +I20170322 07:00:29.058438 61456 blaster.cc:352] Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +I20170322 07:00:30.028483 123 loader.cc:13] Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +I20170322 07:00:49.070676 123 demo.cc:352] Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +I20170322 07:00:56.095214 123 loader.cc:552] Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +W20170322 07:01:14.042785 123 blaster.cc:112] Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. +I20170322 07:01:31.083704 123 blaster.cc:112] Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +I20170322 07:01:44.013733 121 blaster.cc:112] Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. +I20170322 07:01:55.024085 121 blaster.cc:112] Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. +I20170322 07:02:02.027811 121 blaster.cc:6782] Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. +I20170322 07:02:14.022939 61456 blaster.cc:112] Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +I20170322 07:02:30.035925 123 loader.cc:13] Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. +I20170322 07:02:49.024985 123 loader.cc:13] Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +I20170322 07:03:09.056478 121 blaster.cc:6782] Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. +I20170322 07:03:15.023777 123 demo.cc:352] Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +I20170322 07:03:32.066107 123 blaster.cc:352] Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +I20170322 07:03:48.028662 124 blaster.cc:6782] Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. +I20170322 07:03:54.027078 123 demo.cc:123] Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +I20170322 07:04:09.041478 123 demo.cc:123] Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. +I20170322 07:04:14.068162 121 blaster.cc:6782] Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. +I20170322 07:04:28.099513 124 blaster.cc:112] Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +I20170322 07:04:40.063473 124 loader.cc:552] Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. +I20170322 07:04:50.024030 123 loader.cc:552] Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +I20170322 07:34:56.081415 121 blaster.cc:352] Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +I20170322 07:35:14.096304 123 blaster.cc:352] Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. +I20170322 08:05:21.086331 123 demo.cc:352] Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. +I20170322 08:05:33.039503 123 loader.cc:13] Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. +I20170322 08:05:43.092657 124 blaster.cc:6782] Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +I20170322 08:05:59.002644 123 demo.cc:123] Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +I20170322 08:06:01.022102 123 demo.cc:352] Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +I20170322 08:06:22.005675 123 blaster.cc:6782] Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. +I20170322 08:06:37.088974 123 blaster.cc:112] Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +I20170322 08:06:44.043938 61457 demo.cc:123] Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +I20170322 08:06:47.060703 123 loader.cc:13] Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +I20170322 08:06:49.052185 61456 demo.cc:123] Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. +I20170322 08:06:52.074424 61457 demo.cc:352] Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +I20170322 08:07:02.063191 123 demo.cc:123] Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +I20170322 08:07:10.030327 61457 blaster.cc:112] Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +I20170322 08:07:11.011338 123 loader.cc:13] Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +I20170322 08:07:27.078391 123 blaster.cc:352] Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. +I20170322 08:07:41.061684 123 blaster.cc:112] Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. +I20170322 08:07:53.076558 121 blaster.cc:112] Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +I20170322 08:38:04.055174 121 blaster.cc:112] Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur: 1AF9293A-F42D-4318-BCDF-60234B240955 +I20170322 08:38:18.046756 123 blaster.cc:112] Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. +I20170322 08:48:28.004198 123 loader.cc:552] Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +I20170322 08:48:36.032193 61457 blaster.cc:352] Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. +I20170322 09:08:50.028964 61456 loader.cc:13] Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. +I20170322 09:08:56.074576 124 blaster.cc:112] Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +I20170322 09:08:57.090258 123 loader.cc:13] Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. +I20170322 09:09:00.067690 121 blaster.cc:352] Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. +I20170322 09:09:19.036483 61457 blaster.cc:112] Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +I20170322 09:09:40.048046 123 blaster.cc:352] Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +I20170322 09:09:52.051526 123 loader.cc:13] Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. +I20170322 09:10:11.003845 61456 loader.cc:552] Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. +I20170322 09:10:27.094133 123 blaster.cc:6782] Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +I20170322 09:10:43.027892 121 blaster.cc:352] Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. +I20170322 09:10:57.078489 124 demo.cc:352] Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. +I20170322 09:11:09.014685 123 demo.cc:123] Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. +I20170322 09:11:18.029203 61456 blaster.cc:352] Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. +I20170322 09:11:24.067068 121 demo.cc:123] Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +I20170322 09:11:38.053891 61456 loader.cc:552] Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. +I20170322 09:11:59.027292 61457 blaster.cc:112] Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +I20170322 09:12:10.069054 61457 loader.cc:13] Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. +I20170322 09:12:22.018053 123 loader.cc:552] Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +I20170322 09:12:39.000436 123 demo.cc:352] Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +I20170322 09:12:53.009916 123 loader.cc:13] Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. +I20170322 09:13:13.051890 121 demo.cc:123] Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. +I20170322 09:13:24.076724 123 demo.cc:123] Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +I20170323 06:13:34.075980 123 blaster.cc:6782] Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. +I20170323 06:13:35.096130 61456 blaster.cc:6782] Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +I20170323 06:13:49.087790 121 demo.cc:123] Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +I20170323 06:14:08.033671 61457 blaster.cc:112] Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. +I20170323 06:14:23.091358 61456 blaster.cc:112] Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +I20170323 06:14:35.088133 61456 demo.cc:123] Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +I20170323 06:14:55.005577 123 blaster.cc:352] Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. +I20170323 06:14:58.008392 61457 demo.cc:123] Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. +I20170323 06:15:05.004789 123 loader.cc:552] Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. +I20170323 07:28:07.070013 123 blaster.cc:6782] Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +I20170323 07:32:08.012805 123 blaster.cc:112] Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. +I20170323 07:33:25.042509 61456 loader.cc:552] Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +E20170323 08:15:32.027688 123 blaster.cc:6782] Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +I20170323 08:15:41.020299 61456 blaster.cc:6782] Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. +I20170323 08:15:42.021039 124 loader.cc:552] Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +I20170323 08:15:59.063918 123 blaster.cc:112] Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. +I20170323 08:16:19.082250 123 loader.cc:552] Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +I20170323 08:16:20.026445 61457 loader.cc:13] Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +I20170323 08:16:41.048447 123 blaster.cc:6782] Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +I20170323 08:16:52.097215 61456 demo.cc:123] Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. +I20170323 09:17:01.020663 61456 blaster.cc:112] Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. diff --git a/example-scripts/clipboard.sh b/example-scripts/clipboard.sh new file mode 100755 index 0000000..2cdd016 --- /dev/null +++ b/example-scripts/clipboard.sh @@ -0,0 +1,69 @@ +#!/bin/sh +# Wrapper for various clipboard I/O on Linux desktop environments and Windows emulations thereof + +if [ -z "$STDIN_COPY_COMMAND" ] || [ -z "$STDOUT_PASTE_COMMAND" ] +then + if [ -n "$WAYLAND_DISPLAY" ] + then + STDIN_COPY_COMMAND="wl-copy --foreground --type text/plain" + STDOUT_PASTE_COMMAND="wl-paste --no-newline" + elif [ -n "$DISPLAY" ] + then + if command -v xclip + then + STDIN_COPY_COMMAND="xclip -quiet -i -selection clipboard" + STDOUT_PASTE_COMMAND="xclip -o -selection clipboard" + elif command -v xsel + then + STDIN_COPY_COMMAND="xsel --nodetach -i --clipboard" + STDOUT_PASTE_COMMAND="xsel -o --clipboard" + fi + elif command -v lemonade + then + STDIN_COPY_COMMAND="lemonade copy" + STDOUT_PASTE_COMMAND="lemonade paste" + elif command -v doitclient + then + STDIN_COPY_COMMAND="doitclient wclip" + STDOUT_PASTE_COMMAND="doitclient wclip -r" + elif command -v win32yank.exe + then + STDIN_COPY_COMMAND="win32yank.exe -i --crlf" + STDOUT_PASTE_COMMAND="win32yank.exe -o --lf" + elif command -v clip.exe + then + STDIN_COPY_COMMAND="clip.exe" + STDOUT_PASTE_COMMAND=":" + elif [ -n "$TMUX" ] + then + STDIN_COPY_COMMAND="tmux load-buffer -" + STDOUT_PASTE_COMMAND="tmux save-buffer -" + else + echo 'No clipboard command' >&2 + exit 10 + fi > /dev/null +fi + +case $1 in + copy) exec $STDIN_COPY_COMMAND > /dev/null 2>/dev/null ;; + paste) exec $STDOUT_PASTE_COMMAND < /dev/null 2>/dev/null ;; + "") # Try to guess intention + if ! [ -t 0 ] # stdin is piped + then + exec $STDIN_COPY_COMMAND > /dev/null 2>/dev/null + elif ! [ -t 1 ] # stdout is piped + then + exec $STDOUT_PASTE_COMMAND < /dev/null 2>/dev/null + else + export STDIN_COPY_COMMAND STDOUT_PASTE_COMMAND + fi + ;; + *) cat << EOF +Usage: + clipboard copy + clipboard paste + . clipboard +EOF + exit 10 + ;; +esac diff --git a/example-scripts/log_to_csv.sh b/example-scripts/log_to_csv.sh new file mode 100755 index 0000000..70117dd --- /dev/null +++ b/example-scripts/log_to_csv.sh @@ -0,0 +1,66 @@ +#!/usr/bin/env bash + +# +# An example script that converts messages in a syslog file into a +# CSV-formatted file. The CSV file is written to the current directory +# with the same base name as the source file. If the script is run on +# the same file multiple times, it will only convert newly added lines. +# +# NOTE: lnav is going to save some state in $HOME; you might want to change +# $HOME to something else... +# +# NOTE 2: Unfortunately, this is pretty inefficient right now since lnav +# is going to store the entire log file in memory when processing the +# result of the SQL SELECT. +# + +if test $# -lt 1; then + echo "usage: $0 " + echo "Convert a syslog file into CSV format." + exit 1 +fi + +if test ! -f "$1"; then + echo "error: expecting a log file as the first argument" + exit 1 +fi + +# Figure out a unique file name. +out_file_base=$(basename "$1") +counter=0 +while test -e "${out_file_base}.${counter}.csv"; do + counter=$((counter + 1)) +done +export OUT_FILE="${out_file_base}.${counter}.csv" + +# Here's a quick summary of what this is doing: +# +# 1. ':load-session' will load the session data which stores which lines +# are bookmarked in a file. We're using bookmarks to keep track of the +# last line that we converted in a previous run of this script. +# 2. ';CREATE TABLE helper' creates a temporary table that we use to store +# the range of messages that we'll be converting. +# 3. ';INSERT INTO helper' will figure out the range of lines in syslog file +# to convert. +# 4. ';UPDATE syslog_log' will set a bookmark on the last line of the range +# we computed in the previous step. +# 5. ';SELECT *' will pull all of the log messages in the computed range. +# 6. ':write-csv-to' will write out the log messages SELECTed in step #5. +# 7. ':save-session' will save the bookmark we set so it can be loaded on +# future runs of this script. + +lnav -nq -d /tmp/lnav.err \ + -c ":load-session" \ + -c ";CREATE TABLE helper ( start_line int, max_line int )" \ + -c ";INSERT INTO helper ( start_line, max_line ) VALUES (\ + (SELECT coalesce(\ + (SELECT max(log_line) FROM syslog_log where log_mark = 1) + 1,\ + 0)),\ + (SELECT max(log_line) FROM syslog_log ))" \ + -c ";UPDATE syslog_log SET log_mark = 1 where log_line = (\ + SELECT max_line FROM helper)" \ + -c ";SELECT *,log_text FROM syslog_log where log_line between (\ + SELECT start_line FROM helper) and (SELECT max_line FROM helper)" \ + -c ':write-csv-to $OUT_FILE' \ + -c ":save-session" \ + "$1" diff --git a/example-scripts/report-demo.lnav b/example-scripts/report-demo.lnav new file mode 100644 index 0000000..aeb0040 --- /dev/null +++ b/example-scripts/report-demo.lnav @@ -0,0 +1,83 @@ +# +# @synopsis: report-demo [] +# @description: Generate a report for requests in access_log files +# + +# Figure out the file path where the report should be written to, default is +# stdout +;SELECT CASE + WHEN $1 IS NULL THEN '-' + ELSE $1 + END AS out_path + +# Redirect output from commands to $out_path +:redirect-to $out_path + +# Print an introductory message +;SELECT printf('\n%d total requests', count(1)) AS msg FROM access_log +:echo $msg + +;WITH top_paths AS ( + SELECT + cs_uri_stem, + count(1) AS total_hits, + sum(sc_bytes) as bytes, + count(distinct c_ip) as visitors + FROM access_log + WHERE sc_status BETWEEN 200 AND 300 + GROUP BY cs_uri_stem + ORDER BY total_hits DESC + LIMIT 50), + weekly_hits_with_gaps AS ( + SELECT timeslice(log_time_msecs, '1w') AS week, + cs_uri_stem, + count(1) AS weekly_hits + FROM access_log + WHERE cs_uri_stem IN (SELECT cs_uri_stem FROM top_paths) AND + sc_status BETWEEN 200 AND 300 + GROUP BY week, cs_uri_stem), + all_weeks AS ( + SELECT week + FROM weekly_hits_with_gaps + GROUP BY week + ORDER BY week ASC), + weekly_hits AS ( + SELECT all_weeks.week, + top_paths.cs_uri_stem, + ifnull(weekly_hits, 0) AS hits + FROM all_weeks + CROSS JOIN top_paths + LEFT JOIN weekly_hits_with_gaps + ON all_weeks.week = weekly_hits_with_gaps.week AND + top_paths.cs_uri_stem = weekly_hits_with_gaps.cs_uri_stem) + SELECT weekly_hits.cs_uri_stem AS Path, + printf('%,9d', total_hits) AS Hits, + printf('%,9d', visitors) AS Visitors, + printf('%9s', humanize_file_size(bytes)) as Amount, + sparkline(hits) AS Weeks + FROM weekly_hits + LEFT JOIN top_paths ON top_paths.cs_uri_stem = weekly_hits.cs_uri_stem + GROUP BY weekly_hits.cs_uri_stem + ORDER BY Hits DESC + LIMIT 10 + +:write-table-to - + +:echo +:echo Failed Requests +:echo + +;SELECT printf('%,9d', count(1)) AS Hits, + printf('%,9d', count(distinct c_ip)) AS Visitors, + sc_status AS Status, + cs_method AS Method, + group_concat(distinct cs_version) AS Versions, + cs_uri_stem AS Path, + replicate('|', (cast(count(1) AS REAL) / $total_requests) * 100.0) AS "% of Requests" + FROM access_log + WHERE sc_status >= 400 + GROUP BY cs_method, cs_uri_stem + ORDER BY Hits DESC + LIMIT 10 + +:write-table-to - diff --git a/example-scripts/tag-ssh-msgs.lnav b/example-scripts/tag-ssh-msgs.lnav new file mode 100644 index 0000000..9ffad6a --- /dev/null +++ b/example-scripts/tag-ssh-msgs.lnav @@ -0,0 +1,10 @@ +# +# @synopsis: tag-ssh-msgs +# @description: Tag interesting SSH log messages +# + +;UPDATE all_logs + SET log_tags = json_concat(log_tags, '#ssh.invalid-user') + WHERE log_text LIKE '%Invalid user from%' + +;SELECT 'Tagged ' || changes() || ' messages'; diff --git a/lnav.1 b/lnav.1 new file mode 100644 index 0000000..4a1366a --- /dev/null +++ b/lnav.1 @@ -0,0 +1,126 @@ +.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.43.3. +.\" Some roff macros, for reference: +.\" .nh disable hyphenation +.\" .hy enable hyphenation +.\" .ad l left justify +.\" .ad b justify to both left and right margins +.\" .nf disable filling +.\" .fi enable filling +.\" .br insert line break +.\" .sp insert n+1 empty lines +.\" for manpage-specific macros, see man(7) +.\" Define macros +.de Vb \" Begin verbatim text +.ft CW +.nf +.ne \\$1 +.. +.de Ve \" End verbatim text +.ft R +.fi +.. +.TH LNAV "1" "August 2022" +.SH NAME +lnav \- ncurses-based log file viewer +.SH SYNOPSIS +.B lnav +[\-hVsar] [logfile1 logfile2 ...] +.SH DESCRIPTION +The log file navigator, lnav, is an enhanced log file viewer that +takes advantage of any semantic information that can be gleaned from +the files being viewed, such as timestamps and log levels. Using this +extra semantic information, lnav can do things like interleaving +messages from different files, generate histograms of messages over +time, and providing hotkeys for navigating through the file. It is +hoped that these features will allow the user to quickly and +efficiently zero in on problems. +.SH KEY BINDINGS +.TP +? +View/leave the online help text. +.TP +q +Quit the program. +.SH OPTIONS +.TP +\fB\-h\fR +Print help and exit +.TP +\fB\-H\fR +Display the internal help text. +.TP +\fB\-n\fR +Run without the curses UI. (headless mode) +.TP +\fB\-c\fR cmd +Execute a command after the files have been loaded. +.TP +\fB\-f\fR path +Execute the commands in the given file. +.TP +\fB\-I\fR path +Add the given configuration directory to the search path. +.TP +\fB\-n\fR +Do not open the default syslog file if no files are given. +.TP +\fB\-q\fR +Quiet mode. Do not print the log messages after executing all of the commands. +.TP +\fB\-i\fR +Install the given format files in the $HOME/.lnav/formats/installed directory +and exit. +.TP +\fB\-u\fR +Update formats installed from git repositories. +.TP +\fB\-C\fR +Check the configuration and exit. The log format files will be loaded and +checked. Any files given on the command-line will be loaded checked to make +sure they match a log format. +.TP +\fB\-d\fR file +Write debug messages to the given file. +.TP +\fB\-V\fR +Print version information. +.TP +\fB\-r\fR +Recursively load files from the given directories. +.TP +\fB\-R\fR +Load older rotated log files as well. +.TP +\fB\-t\fR +Prepend timestamps to the lines of data being read in +on the standard input. +.TP +\fB\-w\fR file +Write the contents of the standard input to this file. +.SS "Optional arguments:" +.TP +logfile1 +The log files or directories to view. If a +directory is given, all of the files in the +directory will be loaded. +.SH EXAMPLES +To load and follow the syslog file: +.PP +.Vb 1 +\& lnav +.Ve +.PP +To load all of the files in /var/log: +.PP +.Vb 1 +\& lnav /var/log +.Ve +.PP +To watch the output of make with timestamps prepended: +.PP +.Vb 1 +\& make 2>&1 | lnav \-t +.Ve +.SH AUTHOR +This manual page was written by Salvatore Bonaccorso +for the Debian system (but may be used by others). diff --git a/lnav.cfg b/lnav.cfg new file mode 100644 index 0000000..216b409 --- /dev/null +++ b/lnav.cfg @@ -0,0 +1,543 @@ + +# +# General options +# + +# The type of line endings +newlines = lf # auto/lf/crlf/cr + +# The original size of tabs in the input +input_tab_size = 8 # number + +# The size of tabs in the output (only used if align_with_tabs=true) +output_tab_size = 8 # number + +# The ascii value of the string escape char, usually 92 (\). (Pawn) +string_escape_char = 92 # number + +# +# Indenting +# + +# The number of columns to indent per level (usually 2, 3, 4, or 8) +indent_columns = 4 # number + +# How to use tabs when indenting code +# 0=spaces only +# 1=indent with tabs, align with spaces +# 2=indent and align with tabs +indent_with_tabs = 0 # number + +# Whether to indent strings broken by '\' so that they line up +indent_align_string = true # false/true + +# Spaces to indent '{' from level +indent_brace = 0 # number + +# Whether braces are indented to the body level +indent_braces = false # false/true + +# Disabled indenting function braces if indent_braces is true +indent_braces_no_func = false # false/true + +# Indent based on the size of the brace parent, ie 'if' => 3 spaces, 'for' => 4 spaces, etc. +indent_brace_parent = false # false/true + +# Whether the 'namespace' body is indented +indent_namespace = false # false/true + +# Whether the 'class' body is indented +indent_class = true # false/true + +# Whether to indent the stuff after a leading class colon +indent_class_colon = true # false/true + +# Whether to indent continued function call parameters one indent level (true) or aligns instead of indent (false) +indent_func_call_param = false # false/true + +# The number of spaces to indent a continued '->' or '.' +# Usually set to indent_columns. +indent_member = 0 # number + +# Spaces to indent single line ('//') comments on lines before code +indent_sing_line_comments = 0 # number + +# Spaces to indent 'case' from 'switch' +indent_switch_case = 0 # number + +# Spaces to indent 'case' body from 'case' +indent_case_body = 4 # number + +# Spaces to indent '{' from 'case' +indent_case_brace = 0 # number + +# Whether to indent comments found in first column +indent_col1_comment = false # false/true + +# How to indent goto labels (>0=absolute column where 1 is the leftmost column, <=0=subtract from brace indent) +indent_label = 1 # number + +# If an open paren is followed by a newline, indent the next line so that it lines up after the open paren (not recommended) +indent_paren_nl = false # false/true + +# If an open square is followed by a newline, indent the next line so that it lines up after the open square (not recommended) +indent_square_nl = false # false/true + +# +# Spacing options +# + +# Add or remove space around arithmetic operator '+', '-', '/', '*', etc +sp_arith = force # ignore/add/remove/force + +# Add or remove space around assignment operator '=', '+=', etc +sp_assign = force # ignore/add/remove/force + +# Add or remove space around boolean operators '&&' and '||' +sp_bool = force # ignore/add/remove/force + +# Add or remove space around compare operator '<', '>', '==', etc +sp_compare = force # ignore/add/remove/force + +# Add or remove space inside '(' and ')' +sp_inside_paren = remove # ignore/add/remove/force + +# Add or remove space between nested parens +sp_paren_paren = remove # ignore/add/remove/force + +# Add or remove space between ')' and '{' +sp_paren_brace = ignore # ignore/add/remove/force + +# Add or remove space before pointer star '*' +sp_before_ptr_star = force # ignore/add/remove/force + +# Add or remove space between pointer stars '*' +sp_between_ptr_star = remove # ignore/add/remove/force + +# Add or remove space after pointer star '*' +sp_after_ptr_star = remove # ignore/add/remove/force + +# Add or remove space before reference sign '&' +sp_before_byref = force # ignore/add/remove/force + +# Add or remove space after reference sign '&' +sp_after_byref = ignore # ignore/add/remove/force + +# Add or remove space before '<>' +sp_before_angle = remove # ignore/add/remove/force + +# Add or remove space after '<>' +sp_after_angle = force # ignore/add/remove/force + +# Add or remove space before '(' of 'if', 'for', 'switch', and 'while' +sp_before_sparen = force # ignore/add/remove/force + +# Add or remove space inside if-condition '(' and ')' +sp_inside_sparen = remove # ignore/add/remove/force + +# Add or remove space after ')' of 'if', 'for', 'switch', and 'while' +sp_after_sparen = force # ignore/add/remove/force + +# Add or remove space between ')' and '{' of 'if', 'for', 'switch', and 'while' +sp_sparen_brace = add # ignore/add/remove/force + +# Add or remove space before empty statement ';' on 'if', 'for' and 'while' +sp_special_semi = ignore # ignore/add/remove/force + +# Add or remove space before ';' +sp_before_semi = remove # ignore/add/remove/force + +# Add or remove space before '[' (except '[]') +sp_before_square = ignore # ignore/add/remove/force + +# Add or remove space before '[]' +sp_before_squares = ignore # ignore/add/remove/force + +# Add or remove space inside '[' and ']' +sp_inside_square = remove # ignore/add/remove/force + +# Add or remove space after ',' +sp_after_comma = force # ignore/add/remove/force + +# Add or remove space between 'operator' and operator sign +sp_after_operator = ignore # ignore/add/remove/force + +# Add or remove space after cast +sp_after_cast = remove # ignore/add/remove/force + +# Add or remove space between 'sizeof' and '(' +sp_sizeof_paren = remove # ignore/add/remove/force + +# Add or remove space after the tag keyword (Pawn) +sp_after_tag = ignore # ignore/add/remove/force + +# Add or remove space inside enum '{' and '}' +sp_inside_braces_enum = force # ignore/add/remove/force + +# Add or remove space inside struct/union '{' and '}' +sp_inside_braces_struct = force # ignore/add/remove/force + +# Add or remove space inside '{' and '}' +sp_inside_braces = force # ignore/add/remove/force + +# Add or remove space inside '<' and '>' +sp_inside_angle = remove # ignore/add/remove/force + +# Add or remove space between return type and function name (a minimum of 1 is forced except for pointer return types) +sp_type_func = ignore # ignore/add/remove/force + +# Add or remove space between function name and '(' on function declaration +sp_func_proto_paren = remove # ignore/add/remove/force + +# Add or remove space between function name and '(' on function definition +sp_func_def_paren = remove # ignore/add/remove/force + +# Add or remove space inside empty function '()' +sp_inside_fparens = ignore # ignore/add/remove/force + +# Add or remove space inside function '(' and ')' +sp_inside_fparen = remove # ignore/add/remove/force + +# Add or remove space between ']' and '(' when part of a function call. +sp_square_fparen = ignore # ignore/add/remove/force + +# Add or remove space between ')' and '{' of function +sp_fparen_brace = add # ignore/add/remove/force + +# Add or remove space between function name and '(' on function calls +sp_func_call_paren = remove # ignore/add/remove/force + +# Add or remove space between a constructor/destructor and the open paren +sp_func_class_paren = remove # ignore/add/remove/force + +# Add or remove space between 'return' and '(' +sp_return_paren = add # ignore/add/remove/force + +# Add or remove space between macro and value +sp_macro = ignore # ignore/add/remove/force + +# Add or remove space between macro function ')' and value +sp_macro_func = ignore # ignore/add/remove/force + +# Add or remove space between 'else' and '{' if on the same line +sp_else_brace = ignore # ignore/add/remove/force + +# Add or remove space between '}' and 'else' if on the same line +sp_brace_else = ignore # ignore/add/remove/force + +# +# Code alignment (not left column spaces/tabs) +# + +# Whether to keep non-indenting tabs +align_keep_tabs = false # false/true + +# Whether to use tabs for alinging +align_with_tabs = false # false/true + +# Whether to bump out to the next tab when aligning +align_on_tabstop = false # false/true + +# Whether to left-align numbers +align_number_left = true # false/true + +# The span for aligning variable definitions (0=don't align) +align_var_def_span = 1 # number + +# Whether the pointer star is part of the variable name or not +align_var_def_star = true # false/true + +# The threshold for aligning variable definitions (0=no limit) +align_var_def_thresh = 12 # number + +# Whether to align the colon in struct bit fields +align_var_def_colon = true # false/true + +# Whether to align inline struct/enum/union variable definitions +align_var_def_inline = true # false/true + +# The span for aligning on '=' in assignments (0=don't align) +align_assign_span = 1 # number + +# The threshold for aligning on '=' in assignments (0=no limit) +align_assign_thresh = 12 # number + +# The span for aligning on '=' in enums (0=don't align) +align_enum_equ_span = 4 # number + +# The threshold for aligning on '=' in enums (0=no limit) +align_enum_equ_thresh = 0 # number + +# The span for aligning struct/union (0=don't align) +align_var_struct_span = 99 # number + +# The span for aligning struct initializer values (0=don't align) +align_struct_init_span = 3 # number + +# The minimum space between the type and the synonym of a typedef +align_typedef_gap = 1 # number + +# The span for aligning single-line typedefs (0=don't align) +align_typedef_span = 5 # number + +# Controls the positioning of the '*' in typedefs. Just try it. +# 0: Align on typdef type, ignore '*' +# 1: The '*' is part of type name: typedef int *pint; +# 2: The '*' is part of the type: typedef int * pint; +align_typedef_star_style = 1 # number + +# The span for aligning comments that end lines (0=don't align) +align_right_cmt_span = 3 # number + +# The span for aligning function prototypes (0=don't align) +align_func_proto_span = 0 # number + +# Whether to align macros wrapped with a backslash and a newline +align_nl_cont = true # false/true + +# The minimum space between label and value of a preprocessor define +align_pp_define_gap = 4 # number + +# The span for aligning on '#define' bodies (0=don't align) +align_pp_define_span = 3 # number + +# +# Newline adding and removing options +# + +# Try to limit code width to N number of columns +code_width = 79 # number + +# Whether to collapse empty blocks between '{' and '}' +nl_collapse_empty_body = true # false/true + +# Don't touch one-line function bodies inside a class xx { } body +nl_class_leave_one_liners = true # false/true + +# Add or remove newlines at the start of the file +nl_start_of_file = remove # ignore/add/remove/force + +# The number of newlines at the start of the file (only used if nl_start_of_file is 'add' or 'force' +nl_start_of_file_min = 0 # number + +# Add or remove newline at the end of the file +nl_end_of_file = force # ignore/add/remove/force + +# The number of newlines at the end of the file (only used if nl_end_of_file is 'add' or 'force') +nl_end_of_file_min = 1 # number + +# Add or remove newline between '=' and '{' +nl_assign_brace = remove # ignore/add/remove/force + +# The number of newlines after a block of variable definitions +nl_func_var_def_blk = 1 # number + +# Add or remove newline between function call and '(' +nl_fcall_brace = add # ignore/add/remove/force + +# Add or remove newline between 'enum' and '{' +nl_enum_brace = remove # ignore/add/remove/force + +# Add or remove newline between 'struct and '{' +nl_struct_brace = remove # ignore/add/remove/force + +# Add or remove newline between 'union' and '{' +nl_union_brace = remove # ignore/add/remove/force + +# Add or remove newline between 'if' and '{' +nl_if_brace = remove # ignore/add/remove/force + +# Add or remove newline between '}' and 'else' +nl_brace_else = add # ignore/add/remove/force + +# Add or remove newline between 'else if' and '{' +# If set to ignore, nl_if_brace is used instead +nl_elseif_brace = remove # ignore/add/remove/force + +# Add or remove newline between 'else' and '{' +nl_else_brace = remove # ignore/add/remove/force + +# Add or remove newline between 'for' and '{' +nl_for_brace = remove # ignore/add/remove/force + +# Add or remove newline between 'while' and '{' +nl_while_brace = remove # ignore/add/remove/force + +# Add or remove newline between 'do' and '{' +nl_do_brace = remove # ignore/add/remove/force + +# Add or remove newline between '}' and 'while' of 'do' statement +nl_brace_while = remove # ignore/add/remove/force + +# Add or remove newline between 'switch' and '{' +nl_switch_brace = remove # ignore/add/remove/force + +# Whether to put a newline before 'case' statement +nl_before_case = true # false/true + +# Whether to put a newline after 'case' statement +nl_after_case = false # false/true + +# Newline between namespace and { +nl_namespace_brace = ignore # ignore/add/remove/force + +# Add or remove newline between 'template<>' and 'class' +nl_template_class = add # ignore/add/remove/force + +# Add or remove newline between 'class' and '{' +nl_class_brace = remove # ignore/add/remove/force + +# Add or remove newline after each ',' in the constructor member initialization +nl_class_init_args = add # ignore/add/remove/force + +# Add or remove newline between return type and function name in definition +nl_func_type_name = ignore # ignore/add/remove/force + +# Add or remove newline after '(' in a function declaration +nl_func_decl_start = ignore # ignore/add/remove/force + +# Add or remove newline after each ',' in a function declaration +nl_func_decl_args = ignore # ignore/add/remove/force + +# Add or remove newline before the ')' in a function declaration +nl_func_decl_end = ignore # ignore/add/remove/force + +# Add or remove newline between function signature and '{' +nl_fdef_brace = add # ignore/add/remove/force + +# Whether to put a newline after 'return' statement +nl_after_return = true # false/true + +# Whether to put a newline after semicolons, except in 'for' statements +nl_after_semicolon = false # false/true + +# Whether to put a newline after brace open +nl_after_brace_open = false # false/true + +# Whether to alter newlines in '#define' macros +nl_define_macro = false # false/true + +# Whether to not put blanks after '#ifxx', '#elxx', or before '#endif' +nl_squeeze_ifdef = true # false/true + +# Add or remove newline before 'if' +nl_before_if = ignore # ignore/add/remove/force + +# Add or remove newline after 'if' +nl_after_if = ignore # ignore/add/remove/force + +# Add or remove newline before 'for' +nl_before_for = ignore # ignore/add/remove/force + +# Add or remove newline after 'for' +nl_after_for = ignore # ignore/add/remove/force + +# Add or remove newline before 'while' +nl_before_while = ignore # ignore/add/remove/force + +# Add or remove newline after 'while' +nl_after_while = ignore # ignore/add/remove/force + +# Add or remove newline before 'switch' +nl_before_switch = ignore # ignore/add/remove/force + +# Add or remove newline after 'switch' +nl_after_switch = ignore # ignore/add/remove/force + +# Add or remove newline before 'do' +nl_before_do = ignore # ignore/add/remove/force + +# Add or remove newline after 'do' +nl_after_do = ignore # ignore/add/remove/force + +# +# Positioning options +# + +# The position of boolean operators in wrapped expressions +pos_bool = trail # ignore/lead/trail + +# The position of colons between constructor and member initialization +pos_class_colon = lead # ignore/lead/trail + +# +# Blank line options +# + +# The maximum consecutive newlines +nl_max = 4 # number + +# The number of newlines after a function prototype, if followed by another function prototype +nl_after_func_proto = 0 # number + +# The number of newlines after a function prototype, if not followed by another function prototype +nl_after_func_proto_group = 2 # number + +# The number of newlines after '}' of the function body +nl_after_func_body = 2 # number + +# The minimum number of newlines before a multi-line comment (doesn't apply if after a brace open) +nl_before_block_comment = 2 # number + +# Whether to remove blank lines after '{' +eat_blanks_after_open_brace = true # false/true + +# Whether to remove blank lines before '}' +eat_blanks_before_close_brace = true # false/true + +# +# Code modifying options (non-whitespace) +# + +# Add or remove braces on single-line 'do' statement +mod_full_brace_do = add # ignore/add/remove/force + +# Add or remove braces on single-line 'for' statement +mod_full_brace_for = add # ignore/add/remove/force + +# Add or remove braces on single-line function defintions. (Pawn) +mod_full_brace_function = ignore # ignore/add/remove/force + +# Add or remove braces on single-line 'if' statement +mod_full_brace_if = add # ignore/add/remove/force + +# Don't remove braces around statements that span N newlines +mod_full_brace_nl = 0 # number + +# Add or remove braces on single-line 'while' statement +mod_full_brace_while = add # ignore/add/remove/force + +# Add or remove unnecessary paren on 'return' statement +mod_paren_on_return = remove # ignore/add/remove/force + +# Whether to change optional semicolons to real semicolons +mod_pawn_semicolon = false # false/true + +# +# Comment modifications +# + +# Whether to group cpp-comments that look like they are in a block +cmt_cpp_group = false # false/true + +# Whether to put an empty '/*' on the first line of the combined cpp-comment +cmt_cpp_nl_start = false # false/true + +# Whether to put a newline before the closing '*/' of the combined cpp-comment +cmt_cpp_nl_end = false # false/true + +# Whether to change cpp-comments into c-comments +cmt_cpp_to_c = true # false/true + +# Whether to put a star on subsequent comment lines +cmt_star_cont = true # false/true + +# +# Preprocessor options +# + +# Add or remove indent of preprocessor directives +pp_indent = ignore # ignore/add/remove/force + +# Add or remove space between # and, say, define +pp_space = ignore # ignore/add/remove/force diff --git a/m4/ax_ac_append_to_file.m4 b/m4/ax_ac_append_to_file.m4 new file mode 100644 index 0000000..242b3d5 --- /dev/null +++ b/m4/ax_ac_append_to_file.m4 @@ -0,0 +1,32 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_ac_append_to_file.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_AC_APPEND_TO_FILE([FILE],[DATA]) +# +# DESCRIPTION +# +# Appends the specified data to the specified Autoconf is run. If you want +# to append to a file when configure is run use AX_APPEND_TO_FILE instead. +# +# LICENSE +# +# Copyright (c) 2009 Allan Caffee +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 10 + +AC_DEFUN([AX_AC_APPEND_TO_FILE],[ +AC_REQUIRE([AX_FILE_ESCAPES]) +m4_esyscmd( +AX_FILE_ESCAPES +[ +printf "%s" "$2" >> "$1" +]) +]) diff --git a/m4/ax_ac_print_to_file.m4 b/m4/ax_ac_print_to_file.m4 new file mode 100644 index 0000000..642dfc1 --- /dev/null +++ b/m4/ax_ac_print_to_file.m4 @@ -0,0 +1,32 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_ac_print_to_file.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_AC_PRINT_TO_FILE([FILE],[DATA]) +# +# DESCRIPTION +# +# Writes the specified data to the specified file when Autoconf is run. If +# you want to print to a file when configure is run use AX_PRINT_TO_FILE +# instead. +# +# LICENSE +# +# Copyright (c) 2009 Allan Caffee +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 10 + +AC_DEFUN([AX_AC_PRINT_TO_FILE],[ +m4_esyscmd( +AC_REQUIRE([AX_FILE_ESCAPES]) +[ +printf "%s" "$2" > "$1" +]) +]) diff --git a/m4/ax_add_am_macro_static.m4 b/m4/ax_add_am_macro_static.m4 new file mode 100644 index 0000000..6442d24 --- /dev/null +++ b/m4/ax_add_am_macro_static.m4 @@ -0,0 +1,28 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_add_am_macro_static.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_ADD_AM_MACRO_STATIC([RULE]) +# +# DESCRIPTION +# +# Adds the specified rule to $AMINCLUDE. +# +# LICENSE +# +# Copyright (c) 2009 Tom Howard +# Copyright (c) 2009 Allan Caffee +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 8 + +AC_DEFUN([AX_ADD_AM_MACRO_STATIC],[ + AC_REQUIRE([AX_AM_MACROS_STATIC]) + AX_AC_APPEND_TO_FILE(AMINCLUDE_STATIC,[$1]) +]) diff --git a/m4/ax_am_macros_static.m4 b/m4/ax_am_macros_static.m4 new file mode 100644 index 0000000..f4cee8c --- /dev/null +++ b/m4/ax_am_macros_static.m4 @@ -0,0 +1,38 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_am_macros_static.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_AM_MACROS_STATIC +# +# DESCRIPTION +# +# Adds support for macros that create Automake rules. You must manually +# add the following line +# +# include $(top_srcdir)/aminclude_static.am +# +# to your Makefile.am files. +# +# LICENSE +# +# Copyright (c) 2009 Tom Howard +# Copyright (c) 2009 Allan Caffee +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 11 + +AC_DEFUN([AMINCLUDE_STATIC],[aminclude_static.am]) + +AC_DEFUN([AX_AM_MACROS_STATIC], +[ +AX_AC_PRINT_TO_FILE(AMINCLUDE_STATIC,[ +# ]AMINCLUDE_STATIC[ generated automatically by Autoconf +# from AX_AM_MACROS_STATIC on ]m4_esyscmd([LC_ALL=C date])[ +]) +]) diff --git a/m4/ax_check_gnu_make.m4 b/m4/ax_check_gnu_make.m4 new file mode 100644 index 0000000..785dc96 --- /dev/null +++ b/m4/ax_check_gnu_make.m4 @@ -0,0 +1,95 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_check_gnu_make.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_CHECK_GNU_MAKE([run-if-true],[run-if-false]) +# +# DESCRIPTION +# +# This macro searches for a GNU version of make. If a match is found: +# +# * The makefile variable `ifGNUmake' is set to the empty string, otherwise +# it is set to "#". This is useful for including a special features in a +# Makefile, which cannot be handled by other versions of make. +# * The makefile variable `ifnGNUmake' is set to #, otherwise +# it is set to the empty string. This is useful for including a special +# features in a Makefile, which can be handled +# by other versions of make or to specify else like clause. +# * The variable `_cv_gnu_make_command` is set to the command to invoke +# GNU make if it exists, the empty string otherwise. +# * The variable `ax_cv_gnu_make_command` is set to the command to invoke +# GNU make by copying `_cv_gnu_make_command`, otherwise it is unset. +# * If GNU Make is found, its version is extracted from the output of +# `make --version` as the last field of a record of space-separated +# columns and saved into the variable `ax_check_gnu_make_version`. +# * Additionally if GNU Make is found, run shell code run-if-true +# else run shell code run-if-false. +# +# Here is an example of its use: +# +# Makefile.in might contain: +# +# # A failsafe way of putting a dependency rule into a makefile +# $(DEPEND): +# $(CC) -MM $(srcdir)/*.c > $(DEPEND) +# +# @ifGNUmake@ ifeq ($(DEPEND),$(wildcard $(DEPEND))) +# @ifGNUmake@ include $(DEPEND) +# @ifGNUmake@ else +# fallback code +# @ifGNUmake@ endif +# +# Then configure.in would normally contain: +# +# AX_CHECK_GNU_MAKE() +# AC_OUTPUT(Makefile) +# +# Then perhaps to cause gnu make to override any other make, we could do +# something like this (note that GNU make always looks for GNUmakefile +# first): +# +# if ! test x$_cv_gnu_make_command = x ; then +# mv Makefile GNUmakefile +# echo .DEFAULT: > Makefile ; +# echo \ $_cv_gnu_make_command \$@ >> Makefile; +# fi +# +# Then, if any (well almost any) other make is called, and GNU make also +# exists, then the other make wraps the GNU make. +# +# LICENSE +# +# Copyright (c) 2008 John Darrington +# Copyright (c) 2015 Enrico M. Crisostomo +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 12 + +AC_DEFUN([AX_CHECK_GNU_MAKE],dnl + [AC_PROG_AWK + AC_CACHE_CHECK([for GNU make],[_cv_gnu_make_command],[dnl + _cv_gnu_make_command="" ; +dnl Search all the common names for GNU make + for a in "$MAKE" make gmake gnumake ; do + if test -z "$a" ; then continue ; fi ; + if "$a" --version 2> /dev/null | grep GNU 2>&1 > /dev/null ; then + _cv_gnu_make_command=$a ; + AX_CHECK_GNU_MAKE_HEADLINE=$("$a" --version 2> /dev/null | grep "GNU Make") + ax_check_gnu_make_version=$(echo ${AX_CHECK_GNU_MAKE_HEADLINE} | ${AWK} -F " " '{ print $(NF); }') + break ; + fi + done ;]) +dnl If there was a GNU version, then set @ifGNUmake@ to the empty string, '#' otherwise + AS_VAR_IF([_cv_gnu_make_command], [""], [AS_VAR_SET([ifGNUmake], ["#"])], [AS_VAR_SET([ifGNUmake], [""])]) + AS_VAR_IF([_cv_gnu_make_command], [""], [AS_VAR_SET([ifnGNUmake], [""])], [AS_VAR_SET([ifnGNUmake], ["#"])]) + AS_VAR_IF([_cv_gnu_make_command], [""], [AS_UNSET(ax_cv_gnu_make_command)], [AS_VAR_SET([ax_cv_gnu_make_command], [${_cv_gnu_make_command}])]) + AS_VAR_IF([_cv_gnu_make_command], [""],[$2],[$1]) + AC_SUBST([ifGNUmake]) + AC_SUBST([ifnGNUmake]) +]) diff --git a/m4/ax_check_link_flag.m4 b/m4/ax_check_link_flag.m4 new file mode 100644 index 0000000..819409a --- /dev/null +++ b/m4/ax_check_link_flag.m4 @@ -0,0 +1,74 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_check_link_flag.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_CHECK_LINK_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT]) +# +# DESCRIPTION +# +# Check whether the given FLAG works with the linker or gives an error. +# (Warnings, however, are ignored) +# +# ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on +# success/failure. +# +# If EXTRA-FLAGS is defined, it is added to the linker's default flags +# when the check is done. The check is thus made with the flags: "LDFLAGS +# EXTRA-FLAGS FLAG". This can for example be used to force the linker to +# issue an error when a bad flag is given. +# +# INPUT gives an alternative input source to AC_LINK_IFELSE. +# +# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this +# macro in sync with AX_CHECK_{PREPROC,COMPILE}_FLAG. +# +# LICENSE +# +# Copyright (c) 2008 Guido U. Draheim +# Copyright (c) 2011 Maarten Bosmans +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 5 + +AC_DEFUN([AX_CHECK_LINK_FLAG], +[AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF +AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_ldflags_$4_$1])dnl +AC_CACHE_CHECK([whether the linker accepts $1], CACHEVAR, [ + ax_check_save_flags=$LDFLAGS + LDFLAGS="$LDFLAGS $4 $1" + AC_LINK_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])], + [AS_VAR_SET(CACHEVAR,[yes])], + [AS_VAR_SET(CACHEVAR,[no])]) + LDFLAGS=$ax_check_save_flags]) +AS_VAR_IF(CACHEVAR,yes, + [m4_default([$2], :)], + [m4_default([$3], :)]) +AS_VAR_POPDEF([CACHEVAR])dnl +])dnl AX_CHECK_LINK_FLAGS diff --git a/m4/ax_check_pcre2.m4 b/m4/ax_check_pcre2.m4 new file mode 100644 index 0000000..9ae01ad --- /dev/null +++ b/m4/ax_check_pcre2.m4 @@ -0,0 +1,163 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_check_pcre2.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_CHECK_PCRE2([bits], [action-if-found], [action-if-not-found]) +# +# DESCRIPTION +# +# Search for an installed libpcre2-8 library. If nothing was specified +# when calling configure, it searches first in /usr/local and then in +# /usr, /opt/local and /sw. If the --with-pcre2=DIR is specified, it will +# try to find it in DIR/include/pcre2.h and DIR/lib/libpcre2-8. If +# --without-pcre2 is specified, the library is not searched at all. +# +# If 'bits' is empty or '8', PCRE2 8-bit character support is checked +# only. If 'bits' contains '16', PCRE2 8-bit and 16-bit character support +# are checked. If 'bits' contains '32', PCRE2 8-bit and 32-bit character +# support are checked. When 'bits' contains both '16' and '32', PCRE2 +# 8-bit, 16-bit, and 32-bit character support is checked. +# +# If either the header file (pcre2.h), or the library (libpcre2-8) is not +# found, or the specified PCRE2 character bit width is not supported, +# shell commands 'action-if-not-found' is run. If 'action-if-not-found' is +# not specified, the configuration exits on error, asking for a valid +# PCRE2 installation directory or --without-pcre2. +# +# If both header file and library are found, and the specified PCRE2 bit +# widths are supported, shell commands 'action-if-found' is run. If +# 'action-if-found' is not specified, the default action appends +# '-I${PCRE2_HOME}/include' to CPFLAGS, appends '-L$PCRE2_HOME}/lib' to +# LDFLAGS, prepends '-lpcre2-8' to LIBS, and calls AC_DEFINE(HAVE_PCRE2). +# You should use autoheader to include a definition for this symbol in a +# config.h file. Sample usage in a C/C++ source is as follows: +# +# #ifdef HAVE_PCRE2 +# #define PCRE2_CODE_UNIT_WIDTH 8 +# #include +# #endif /* HAVE_PCRE2 */ +# +# LICENSE +# +# Copyright (c) 2020 Robert van Engelen +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 2 + +AC_DEFUN([AX_CHECK_PCRE2], +# +# Handle user hints +# +[AC_MSG_CHECKING(if PCRE2 is wanted) +pcre2_places="/usr/local /usr /opt/local /sw" +AC_ARG_WITH([pcre2], +[ --with-pcre2=DIR root directory path of PCRE2 installation @<:@defaults to + /usr/local or /usr if not found in /usr/local@:>@ + --without-pcre2 to disable PCRE2 usage completely], +[if test "$withval" != "no" ; then + AC_MSG_RESULT(yes) + if test -d "$withval" + then + pcre2_places="$withval $pcre2_places" + else + AC_MSG_WARN([Sorry, $withval does not exist, checking usual places]) + fi +else + pcre2_places="" + AC_MSG_RESULT(no) +fi], +[AC_MSG_RESULT(yes)]) +# +# Locate PCRE2, if wanted +# +if test -n "${pcre2_places}" +then + # check the user supplied or any other more or less 'standard' place: + # Most UNIX systems : /usr/local and /usr + # MacPorts / Fink on OSX : /opt/local respectively /sw + for PCRE2_HOME in ${pcre2_places} ; do + if test -f "${PCRE2_HOME}/include/pcre2.h"; then break; fi + PCRE2_HOME="" + done + + PCRE2_OLD_LDFLAGS=$LDFLAGS + PCRE2_OLD_CPPFLAGS=$CPPFLAGS + if test -n "${PCRE2_HOME}"; then + LDFLAGS="$LDFLAGS -L${PCRE2_HOME}/lib" + CPPFLAGS="$CPPFLAGS -I${PCRE2_HOME}/include" + fi + AC_LANG_PUSH([C]) + AC_CHECK_LIB([pcre2-8], [pcre2_compile_8], [pcre2_cv_libpcre2=yes], [pcre2_cv_libpcre2=no]) + AC_CHECK_HEADER([pcre2.h], [pcre2_cv_pcre2_h=yes], [pcre2_cv_pcre2_h=no], [#define PCRE2_CODE_UNIT_WIDTH 8]) + case "$1" in + *16*) + AC_CHECK_LIB([pcre2-16], [pcre2_compile_16], [pcre2_cv_libpcre2_16=yes], [pcre2_cv_libpcre2_16=no]) + AC_CHECK_HEADER([pcre2.h], [pcre2_cv_pcre2_16_h=yes], [pcre2_cv_pcre2_16_h=no], [#define PCRE2_CODE_UNIT_WIDTH 16]) + if test "$pcre2_cv_libpcre2_16" = "no" || test "$pcre2_cv_pcre2_16_h" = "no"; then + pcre2_cv_libpcre2=no + fi + ;; + esac + case "$1" in + *32*) + AC_CHECK_LIB([pcre2-32], [pcre2_compile_32], [pcre2_cv_libpcre2_32=yes], [pcre2_cv_libpcre2_32=no]) + AC_CHECK_HEADER([pcre2.h], [pcre2_cv_pcre2_32_h=yes], [pcre2_cv_pcre2_32_h=no], [#define PCRE2_CODE_UNIT_WIDTH 32]) + if test "$pcre2_cv_libpcre2_32" = "no" || test "$pcre2_cv_pcre2_32_h" = "no"; then + pcre2_cv_libpcre2=no + fi + esac + AC_LANG_POP([C]) + if test "$pcre2_cv_libpcre2" = "yes" && test "$pcre2_cv_pcre2_h" = "yes" + then + # + # If both library and header were found, action-if-found + # + m4_ifblank([$2],[ + CPPFLAGS="$CPPFLAGS -I${PCRE2_HOME}/include" + LDFLAGS="$LDFLAGS -L${PCRE2_HOME}/lib" + LIBS="-lpcre2-8 $LIBS" + AC_DEFINE([HAVE_PCRE2], [1], + [Define to 1 if you have `PCRE2' library (-lpcre2-$1)]) + ],[ + # Restore variables + LDFLAGS="$PCRE2_OLD_LDFLAGS" + CPPFLAGS="$PCRE2_OLD_CPPFLAGS" + $2 + ]) + else + # + # If either header or library was not found, action-if-not-found + # + m4_default([$3],[ + AC_MSG_ERROR([either specify a valid PCRE2 installation with --with-pcre2=DIR or disable PCRE2 usage with --without-pcre2]) + ]) + fi +fi +]) diff --git a/m4/ax_code_coverage.m4 b/m4/ax_code_coverage.m4 new file mode 100644 index 0000000..53dcf7b --- /dev/null +++ b/m4/ax_code_coverage.m4 @@ -0,0 +1,272 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_code_coverage.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_CODE_COVERAGE() +# +# DESCRIPTION +# +# Defines CODE_COVERAGE_CPPFLAGS, CODE_COVERAGE_CFLAGS, +# CODE_COVERAGE_CXXFLAGS and CODE_COVERAGE_LIBS which should be included +# in the CPPFLAGS, CFLAGS CXXFLAGS and LIBS/LIBADD variables of every +# build target (program or library) which should be built with code +# coverage support. Also add rules using AX_ADD_AM_MACRO_STATIC; and +# $enable_code_coverage which can be used in subsequent configure output. +# CODE_COVERAGE_ENABLED is defined and substituted, and corresponds to the +# value of the --enable-code-coverage option, which defaults to being +# disabled. +# +# Test also for gcov program and create GCOV variable that could be +# substituted. +# +# Note that all optimization flags in CFLAGS must be disabled when code +# coverage is enabled. +# +# Usage example: +# +# configure.ac: +# +# AX_CODE_COVERAGE +# +# Makefile.am: +# +# include $(top_srcdir)/aminclude_static.am +# +# my_program_LIBS = ... $(CODE_COVERAGE_LIBS) ... +# my_program_CPPFLAGS = ... $(CODE_COVERAGE_CPPFLAGS) ... +# my_program_CFLAGS = ... $(CODE_COVERAGE_CFLAGS) ... +# my_program_CXXFLAGS = ... $(CODE_COVERAGE_CXXFLAGS) ... +# +# clean-local: code-coverage-clean +# distclean-local: code-coverage-dist-clean +# +# This results in a "check-code-coverage" rule being added to any +# Makefile.am which do "include $(top_srcdir)/aminclude_static.am" +# (assuming the module has been configured with --enable-code-coverage). +# Running `make check-code-coverage` in that directory will run the +# module's test suite (`make check`) and build a code coverage report +# detailing the code which was touched, then print the URI for the report. +# +# This code was derived from Makefile.decl in GLib, originally licensed +# under LGPLv2.1+. +# +# LICENSE +# +# Copyright (c) 2012, 2016 Philip Withnall +# Copyright (c) 2012 Xan Lopez +# Copyright (c) 2012 Christian Persch +# Copyright (c) 2012 Paolo Borelli +# Copyright (c) 2012 Dan Winship +# Copyright (c) 2015,2018 Bastien ROUCARIES +# +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or (at +# your option) any later version. +# +# This library is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser +# General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + +#serial 34 + +m4_define(_AX_CODE_COVERAGE_RULES,[ +AX_ADD_AM_MACRO_STATIC([ +# Code coverage +# +# Optional: +# - CODE_COVERAGE_DIRECTORY: Top-level directory for code coverage reporting. +# Multiple directories may be specified, separated by whitespace. +# (Default: \$(top_builddir)) +# - CODE_COVERAGE_OUTPUT_FILE: Filename and path for the .info file generated +# by lcov for code coverage. (Default: +# \$(PACKAGE_NAME)-\$(PACKAGE_VERSION)-coverage.info) +# - CODE_COVERAGE_OUTPUT_DIRECTORY: Directory for generated code coverage +# reports to be created. (Default: +# \$(PACKAGE_NAME)-\$(PACKAGE_VERSION)-coverage) +# - CODE_COVERAGE_BRANCH_COVERAGE: Set to 1 to enforce branch coverage, +# set to 0 to disable it and leave empty to stay with the default. +# (Default: empty) +# - CODE_COVERAGE_LCOV_SHOPTS_DEFAULT: Extra options shared between both lcov +# instances. (Default: based on $CODE_COVERAGE_BRANCH_COVERAGE) +# - CODE_COVERAGE_LCOV_SHOPTS: Extra options to shared between both lcov +# instances. (Default: $CODE_COVERAGE_LCOV_SHOPTS_DEFAULT) +# - CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH: --gcov-tool pathtogcov +# - CODE_COVERAGE_LCOV_OPTIONS_DEFAULT: Extra options to pass to the +# collecting lcov instance. (Default: $CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH) +# - CODE_COVERAGE_LCOV_OPTIONS: Extra options to pass to the collecting lcov +# instance. (Default: $CODE_COVERAGE_LCOV_OPTIONS_DEFAULT) +# - CODE_COVERAGE_LCOV_RMOPTS_DEFAULT: Extra options to pass to the filtering +# lcov instance. (Default: empty) +# - CODE_COVERAGE_LCOV_RMOPTS: Extra options to pass to the filtering lcov +# instance. (Default: $CODE_COVERAGE_LCOV_RMOPTS_DEFAULT) +# - CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT: Extra options to pass to the +# genhtml instance. (Default: based on $CODE_COVERAGE_BRANCH_COVERAGE) +# - CODE_COVERAGE_GENHTML_OPTIONS: Extra options to pass to the genhtml +# instance. (Default: $CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT) +# - CODE_COVERAGE_IGNORE_PATTERN: Extra glob pattern of files to ignore +# +# The generated report will be titled using the \$(PACKAGE_NAME) and +# \$(PACKAGE_VERSION). In order to add the current git hash to the title, +# use the git-version-gen script, available online. +# Optional variables +# run only on top dir +if CODE_COVERAGE_ENABLED + ifeq (\$(abs_builddir), \$(abs_top_builddir)) +CODE_COVERAGE_DIRECTORY ?= \$(top_builddir) +CODE_COVERAGE_OUTPUT_FILE ?= \$(PACKAGE_NAME)-\$(PACKAGE_VERSION)-coverage.info +CODE_COVERAGE_OUTPUT_DIRECTORY ?= \$(PACKAGE_NAME)-\$(PACKAGE_VERSION)-coverage + +CODE_COVERAGE_BRANCH_COVERAGE ?= +CODE_COVERAGE_LCOV_SHOPTS_DEFAULT ?= \$(if \$(CODE_COVERAGE_BRANCH_COVERAGE),\ +--rc lcov_branch_coverage=\$(CODE_COVERAGE_BRANCH_COVERAGE)) +CODE_COVERAGE_LCOV_SHOPTS ?= \$(CODE_COVERAGE_LCOV_SHOPTS_DEFAULT) +CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH ?= --gcov-tool \"\$(GCOV)\" +CODE_COVERAGE_LCOV_OPTIONS_DEFAULT ?= \$(CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH) +CODE_COVERAGE_LCOV_OPTIONS ?= \$(CODE_COVERAGE_LCOV_OPTIONS_DEFAULT) +CODE_COVERAGE_LCOV_RMOPTS_DEFAULT ?= +CODE_COVERAGE_LCOV_RMOPTS ?= \$(CODE_COVERAGE_LCOV_RMOPTS_DEFAULT) +CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT ?=\ +\$(if \$(CODE_COVERAGE_BRANCH_COVERAGE),\ +--rc genhtml_branch_coverage=\$(CODE_COVERAGE_BRANCH_COVERAGE)) +CODE_COVERAGE_GENHTML_OPTIONS ?= \$(CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT) +CODE_COVERAGE_IGNORE_PATTERN ?= + +GITIGNOREFILES := \$(GITIGNOREFILES) \$(CODE_COVERAGE_OUTPUT_FILE) \$(CODE_COVERAGE_OUTPUT_DIRECTORY) +code_coverage_v_lcov_cap = \$(code_coverage_v_lcov_cap_\$(V)) +code_coverage_v_lcov_cap_ = \$(code_coverage_v_lcov_cap_\$(AM_DEFAULT_VERBOSITY)) +code_coverage_v_lcov_cap_0 = @echo \" LCOV --capture\" \$(CODE_COVERAGE_OUTPUT_FILE); +code_coverage_v_lcov_ign = \$(code_coverage_v_lcov_ign_\$(V)) +code_coverage_v_lcov_ign_ = \$(code_coverage_v_lcov_ign_\$(AM_DEFAULT_VERBOSITY)) +code_coverage_v_lcov_ign_0 = @echo \" LCOV --remove /tmp/*\" \$(CODE_COVERAGE_IGNORE_PATTERN); +code_coverage_v_genhtml = \$(code_coverage_v_genhtml_\$(V)) +code_coverage_v_genhtml_ = \$(code_coverage_v_genhtml_\$(AM_DEFAULT_VERBOSITY)) +code_coverage_v_genhtml_0 = @echo \" GEN \" \"\$(CODE_COVERAGE_OUTPUT_DIRECTORY)\"; +code_coverage_quiet = \$(code_coverage_quiet_\$(V)) +code_coverage_quiet_ = \$(code_coverage_quiet_\$(AM_DEFAULT_VERBOSITY)) +code_coverage_quiet_0 = --quiet + +# sanitizes the test-name: replaces with underscores: dashes and dots +code_coverage_sanitize = \$(subst -,_,\$(subst .,_,\$(1))) + +# Use recursive makes in order to ignore errors during check +check-code-coverage: + -\$(AM_V_at)\$(MAKE) \$(AM_MAKEFLAGS) -k check + \$(AM_V_at)\$(MAKE) \$(AM_MAKEFLAGS) code-coverage-capture + +# Capture code coverage data +code-coverage-capture: code-coverage-capture-hook + \$(code_coverage_v_lcov_cap)\$(LCOV) \$(code_coverage_quiet) \$(addprefix --directory ,\$(CODE_COVERAGE_DIRECTORY)) --capture --output-file \"\$(CODE_COVERAGE_OUTPUT_FILE).tmp\" --test-name \"\$(call code_coverage_sanitize,\$(PACKAGE_NAME)-\$(PACKAGE_VERSION))\" --no-checksum --compat-libtool \$(CODE_COVERAGE_LCOV_SHOPTS) \$(CODE_COVERAGE_LCOV_OPTIONS) + \$(code_coverage_v_lcov_ign)\$(LCOV) \$(code_coverage_quiet) \$(addprefix --directory ,\$(CODE_COVERAGE_DIRECTORY)) --remove \"\$(CODE_COVERAGE_OUTPUT_FILE).tmp\" \"/tmp/*\" \$(CODE_COVERAGE_IGNORE_PATTERN) --output-file \"\$(CODE_COVERAGE_OUTPUT_FILE)\" \$(CODE_COVERAGE_LCOV_SHOPTS) \$(CODE_COVERAGE_LCOV_RMOPTS) + -@rm -f \"\$(CODE_COVERAGE_OUTPUT_FILE).tmp\" + \$(code_coverage_v_genhtml)LANG=C \$(GENHTML) \$(code_coverage_quiet) \$(addprefix --prefix ,\$(CODE_COVERAGE_DIRECTORY)) --output-directory \"\$(CODE_COVERAGE_OUTPUT_DIRECTORY)\" --title \"\$(PACKAGE_NAME)-\$(PACKAGE_VERSION) Code Coverage\" --legend --show-details \"\$(CODE_COVERAGE_OUTPUT_FILE)\" \$(CODE_COVERAGE_GENHTML_OPTIONS) + @echo \"file://\$(abs_builddir)/\$(CODE_COVERAGE_OUTPUT_DIRECTORY)/index.html\" + +code-coverage-clean: + -\$(LCOV) --directory \$(top_builddir) -z + -rm -rf \"\$(CODE_COVERAGE_OUTPUT_FILE)\" \"\$(CODE_COVERAGE_OUTPUT_FILE).tmp\" \"\$(CODE_COVERAGE_OUTPUT_DIRECTORY)\" + -find . \\( -name \"*.gcda\" -o -name \"*.gcno\" -o -name \"*.gcov\" \\) -delete + +code-coverage-dist-clean: + +A][M_DISTCHECK_CONFIGURE_FLAGS := \$(A][M_DISTCHECK_CONFIGURE_FLAGS) --disable-code-coverage + else # ifneq (\$(abs_builddir), \$(abs_top_builddir)) +check-code-coverage: + +code-coverage-capture: code-coverage-capture-hook + +code-coverage-clean: + +code-coverage-dist-clean: + endif # ifeq (\$(abs_builddir), \$(abs_top_builddir)) +else #! CODE_COVERAGE_ENABLED +# Use recursive makes in order to ignore errors during check +check-code-coverage: + @echo \"Need to reconfigure with --enable-code-coverage\" +# Capture code coverage data +code-coverage-capture: code-coverage-capture-hook + @echo \"Need to reconfigure with --enable-code-coverage\" + +code-coverage-clean: + +code-coverage-dist-clean: + +endif #CODE_COVERAGE_ENABLED +# Hook rule executed before code-coverage-capture, overridable by the user +code-coverage-capture-hook: + +.PHONY: check-code-coverage code-coverage-capture code-coverage-dist-clean code-coverage-clean code-coverage-capture-hook +]) +]) + +AC_DEFUN([_AX_CODE_COVERAGE_ENABLED],[ + AX_CHECK_GNU_MAKE([],[AC_MSG_ERROR([not using GNU make that is needed for coverage])]) + AC_REQUIRE([AX_ADD_AM_MACRO_STATIC]) + # check for gcov + AC_CHECK_TOOL([GCOV], + [$_AX_CODE_COVERAGE_GCOV_PROG_WITH], + [:]) + AS_IF([test "X$GCOV" = "X:"], + [AC_MSG_ERROR([gcov is needed to do coverage])]) + AC_SUBST([GCOV]) + + dnl Check if gcc is being used + AS_IF([ test "$GCC" = "no" ], [ + AC_MSG_ERROR([not compiling with gcc, which is required for gcov code coverage]) + ]) + + AC_CHECK_PROG([LCOV], [lcov], [lcov]) + AC_CHECK_PROG([GENHTML], [genhtml], [genhtml]) + + AS_IF([ test x"$LCOV" = x ], [ + AC_MSG_ERROR([To enable code coverage reporting you must have lcov installed]) + ]) + + AS_IF([ test x"$GENHTML" = x ], [ + AC_MSG_ERROR([Could not find genhtml from the lcov package]) + ]) + + dnl Build the code coverage flags + dnl Define CODE_COVERAGE_LDFLAGS for backwards compatibility + CODE_COVERAGE_CPPFLAGS="-DNDEBUG" + CODE_COVERAGE_CFLAGS="-O0 -g -fprofile-arcs -ftest-coverage -fprofile-abs-path" + CODE_COVERAGE_CXXFLAGS="-O0 -g -fprofile-arcs -ftest-coverage -fprofile-abs-path" + CODE_COVERAGE_LIBS="-lgcov" + + AC_SUBST([CODE_COVERAGE_CPPFLAGS]) + AC_SUBST([CODE_COVERAGE_CFLAGS]) + AC_SUBST([CODE_COVERAGE_CXXFLAGS]) + AC_SUBST([CODE_COVERAGE_LIBS]) +]) + +AC_DEFUN([AX_CODE_COVERAGE],[ + dnl Check for --enable-code-coverage + + # allow to override gcov location + AC_ARG_WITH([gcov], + [AS_HELP_STRING([--with-gcov[=GCOV]], [use given GCOV for coverage (GCOV=gcov).])], + [_AX_CODE_COVERAGE_GCOV_PROG_WITH=$with_gcov], + [_AX_CODE_COVERAGE_GCOV_PROG_WITH=gcov]) + + AC_MSG_CHECKING([whether to build with code coverage support]) + AC_ARG_ENABLE([code-coverage], + AS_HELP_STRING([--enable-code-coverage], + [Whether to enable code coverage support]),, + enable_code_coverage=no) + + AM_CONDITIONAL([CODE_COVERAGE_ENABLED], [test "x$enable_code_coverage" = xyes]) + AC_SUBST([CODE_COVERAGE_ENABLED], [$enable_code_coverage]) + AC_MSG_RESULT($enable_code_coverage) + + AS_IF([ test "x$enable_code_coverage" = xyes ], [ + _AX_CODE_COVERAGE_ENABLED + ]) + + _AX_CODE_COVERAGE_RULES +]) diff --git a/m4/ax_cxx_compile_stdcxx.m4 b/m4/ax_cxx_compile_stdcxx.m4 new file mode 100644 index 0000000..9413da6 --- /dev/null +++ b/m4/ax_cxx_compile_stdcxx.m4 @@ -0,0 +1,962 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_CXX_COMPILE_STDCXX(VERSION, [ext|noext], [mandatory|optional]) +# +# DESCRIPTION +# +# Check for baseline language coverage in the compiler for the specified +# version of the C++ standard. If necessary, add switches to CXX and +# CXXCPP to enable support. VERSION may be '11' (for the C++11 standard) +# or '14' (for the C++14 standard). +# +# The second argument, if specified, indicates whether you insist on an +# extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g. +# -std=c++11). If neither is specified, you get whatever works, with +# preference for no added switch, and then for an extended mode. +# +# The third argument, if specified 'mandatory' or if left unspecified, +# indicates that baseline support for the specified C++ standard is +# required and that the macro should error out if no mode with that +# support is found. If specified 'optional', then configuration proceeds +# regardless, after defining HAVE_CXX${VERSION} if and only if a +# supporting mode is found. +# +# LICENSE +# +# Copyright (c) 2008 Benjamin Kosnik +# Copyright (c) 2012 Zack Weinberg +# Copyright (c) 2013 Roy Stogner +# Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov +# Copyright (c) 2015 Paul Norman +# Copyright (c) 2015 Moritz Klammler +# Copyright (c) 2016, 2018 Krzesimir Nowak +# Copyright (c) 2019 Enji Cooper +# Copyright (c) 2020 Jason Merrill +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 12 + +dnl This macro is based on the code from the AX_CXX_COMPILE_STDCXX_11 macro +dnl (serial version number 13). + +AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl + m4_if([$1], [11], [ax_cxx_compile_alternatives="11 0x"], + [$1], [14], [ax_cxx_compile_alternatives="14 1y"], + [$1], [17], [ax_cxx_compile_alternatives="17 1z"], + [m4_fatal([invalid first argument `$1' to AX_CXX_COMPILE_STDCXX])])dnl + m4_if([$2], [], [], + [$2], [ext], [], + [$2], [noext], [], + [m4_fatal([invalid second argument `$2' to AX_CXX_COMPILE_STDCXX])])dnl + m4_if([$3], [], [ax_cxx_compile_cxx$1_required=true], + [$3], [mandatory], [ax_cxx_compile_cxx$1_required=true], + [$3], [optional], [ax_cxx_compile_cxx$1_required=false], + [m4_fatal([invalid third argument `$3' to AX_CXX_COMPILE_STDCXX])]) + AC_LANG_PUSH([C++])dnl + ac_success=no + + m4_if([$2], [], [dnl + AC_CACHE_CHECK(whether $CXX supports C++$1 features by default, + ax_cv_cxx_compile_cxx$1, + [AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], + [ax_cv_cxx_compile_cxx$1=yes], + [ax_cv_cxx_compile_cxx$1=no])]) + if test x$ax_cv_cxx_compile_cxx$1 = xyes; then + ac_success=yes + fi]) + + m4_if([$2], [noext], [], [dnl + if test x$ac_success = xno; then + for alternative in ${ax_cxx_compile_alternatives}; do + switch="-std=gnu++${alternative}" + cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch]) + AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch, + $cachevar, + [ac_save_CXX="$CXX" + CXX="$CXX $switch" + AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], + [eval $cachevar=yes], + [eval $cachevar=no]) + CXX="$ac_save_CXX"]) + if eval test x\$$cachevar = xyes; then + CXX="$CXX $switch" + if test -n "$CXXCPP" ; then + CXXCPP="$CXXCPP $switch" + fi + ac_success=yes + break + fi + done + fi]) + + m4_if([$2], [ext], [], [dnl + if test x$ac_success = xno; then + dnl HP's aCC needs +std=c++11 according to: + dnl http://h21007.www2.hp.com/portal/download/files/unprot/aCxx/PDF_Release_Notes/769149-001.pdf + dnl Cray's crayCC needs "-h std=c++11" + for alternative in ${ax_cxx_compile_alternatives}; do + for switch in -std=c++${alternative} +std=c++${alternative} "-h std=c++${alternative}"; do + cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch]) + AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch, + $cachevar, + [ac_save_CXX="$CXX" + CXX="$CXX $switch" + AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], + [eval $cachevar=yes], + [eval $cachevar=no]) + CXX="$ac_save_CXX"]) + if eval test x\$$cachevar = xyes; then + CXX="$CXX $switch" + if test -n "$CXXCPP" ; then + CXXCPP="$CXXCPP $switch" + fi + ac_success=yes + break + fi + done + if test x$ac_success = xyes; then + break + fi + done + fi]) + AC_LANG_POP([C++]) + if test x$ax_cxx_compile_cxx$1_required = xtrue; then + if test x$ac_success = xno; then + AC_MSG_ERROR([*** A compiler with support for C++$1 language features is required.]) + fi + fi + if test x$ac_success = xno; then + HAVE_CXX$1=0 + AC_MSG_NOTICE([No compiler with C++$1 support was found]) + else + HAVE_CXX$1=1 + AC_DEFINE(HAVE_CXX$1,1, + [define if the compiler supports basic C++$1 syntax]) + fi + AC_SUBST(HAVE_CXX$1) +]) + + +dnl Test body for checking C++11 support + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_11], + _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 +) + + +dnl Test body for checking C++14 support + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_14], + _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 + _AX_CXX_COMPILE_STDCXX_testbody_new_in_14 +) + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_17], + _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 + _AX_CXX_COMPILE_STDCXX_testbody_new_in_14 + _AX_CXX_COMPILE_STDCXX_testbody_new_in_17 +) + +dnl Tests for new features in C++11 + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_11], [[ + +// If the compiler admits that it is not ready for C++11, why torture it? +// Hopefully, this will speed up the test. + +#ifndef __cplusplus + +#error "This is not a C++ compiler" + +#elif __cplusplus < 201103L + +#error "This is not a C++11 compiler" + +#else + +namespace cxx11 +{ + + namespace test_static_assert + { + + template + struct check + { + static_assert(sizeof(int) <= sizeof(T), "not big enough"); + }; + + } + + namespace test_final_override + { + + struct Base + { + virtual ~Base() {} + virtual void f() {} + }; + + struct Derived : public Base + { + virtual ~Derived() override {} + virtual void f() override {} + }; + + } + + namespace test_double_right_angle_brackets + { + + template < typename T > + struct check {}; + + typedef check single_type; + typedef check> double_type; + typedef check>> triple_type; + typedef check>>> quadruple_type; + + } + + namespace test_decltype + { + + int + f() + { + int a = 1; + decltype(a) b = 2; + return a + b; + } + + } + + namespace test_type_deduction + { + + template < typename T1, typename T2 > + struct is_same + { + static const bool value = false; + }; + + template < typename T > + struct is_same + { + static const bool value = true; + }; + + template < typename T1, typename T2 > + auto + add(T1 a1, T2 a2) -> decltype(a1 + a2) + { + return a1 + a2; + } + + int + test(const int c, volatile int v) + { + static_assert(is_same::value == true, ""); + static_assert(is_same::value == false, ""); + static_assert(is_same::value == false, ""); + auto ac = c; + auto av = v; + auto sumi = ac + av + 'x'; + auto sumf = ac + av + 1.0; + static_assert(is_same::value == true, ""); + static_assert(is_same::value == true, ""); + static_assert(is_same::value == true, ""); + static_assert(is_same::value == false, ""); + static_assert(is_same::value == true, ""); + return (sumf > 0.0) ? sumi : add(c, v); + } + + } + + namespace test_noexcept + { + + int f() { return 0; } + int g() noexcept { return 0; } + + static_assert(noexcept(f()) == false, ""); + static_assert(noexcept(g()) == true, ""); + + } + + namespace test_constexpr + { + + template < typename CharT > + unsigned long constexpr + strlen_c_r(const CharT *const s, const unsigned long acc) noexcept + { + return *s ? strlen_c_r(s + 1, acc + 1) : acc; + } + + template < typename CharT > + unsigned long constexpr + strlen_c(const CharT *const s) noexcept + { + return strlen_c_r(s, 0UL); + } + + static_assert(strlen_c("") == 0UL, ""); + static_assert(strlen_c("1") == 1UL, ""); + static_assert(strlen_c("example") == 7UL, ""); + static_assert(strlen_c("another\0example") == 7UL, ""); + + } + + namespace test_rvalue_references + { + + template < int N > + struct answer + { + static constexpr int value = N; + }; + + answer<1> f(int&) { return answer<1>(); } + answer<2> f(const int&) { return answer<2>(); } + answer<3> f(int&&) { return answer<3>(); } + + void + test() + { + int i = 0; + const int c = 0; + static_assert(decltype(f(i))::value == 1, ""); + static_assert(decltype(f(c))::value == 2, ""); + static_assert(decltype(f(0))::value == 3, ""); + } + + } + + namespace test_uniform_initialization + { + + struct test + { + static const int zero {}; + static const int one {1}; + }; + + static_assert(test::zero == 0, ""); + static_assert(test::one == 1, ""); + + } + + namespace test_lambdas + { + + void + test1() + { + auto lambda1 = [](){}; + auto lambda2 = lambda1; + lambda1(); + lambda2(); + } + + int + test2() + { + auto a = [](int i, int j){ return i + j; }(1, 2); + auto b = []() -> int { return '0'; }(); + auto c = [=](){ return a + b; }(); + auto d = [&](){ return c; }(); + auto e = [a, &b](int x) mutable { + const auto identity = [](int y){ return y; }; + for (auto i = 0; i < a; ++i) + a += b--; + return x + identity(a + b); + }(0); + return a + b + c + d + e; + } + + int + test3() + { + const auto nullary = [](){ return 0; }; + const auto unary = [](int x){ return x; }; + using nullary_t = decltype(nullary); + using unary_t = decltype(unary); + const auto higher1st = [](nullary_t f){ return f(); }; + const auto higher2nd = [unary](nullary_t f1){ + return [unary, f1](unary_t f2){ return f2(unary(f1())); }; + }; + return higher1st(nullary) + higher2nd(nullary)(unary); + } + + } + + namespace test_variadic_templates + { + + template + struct sum; + + template + struct sum + { + static constexpr auto value = N0 + sum::value; + }; + + template <> + struct sum<> + { + static constexpr auto value = 0; + }; + + static_assert(sum<>::value == 0, ""); + static_assert(sum<1>::value == 1, ""); + static_assert(sum<23>::value == 23, ""); + static_assert(sum<1, 2>::value == 3, ""); + static_assert(sum<5, 5, 11>::value == 21, ""); + static_assert(sum<2, 3, 5, 7, 11, 13>::value == 41, ""); + + } + + // http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae + // Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function + // because of this. + namespace test_template_alias_sfinae + { + + struct foo {}; + + template + using member = typename T::member_type; + + template + void func(...) {} + + template + void func(member*) {} + + void test(); + + void test() { func(0); } + + } + +} // namespace cxx11 + +#endif // __cplusplus >= 201103L + +]]) + + +dnl Tests for new features in C++14 + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_14], [[ + +// If the compiler admits that it is not ready for C++14, why torture it? +// Hopefully, this will speed up the test. + +#ifndef __cplusplus + +#error "This is not a C++ compiler" + +#elif __cplusplus < 201402L + +#error "This is not a C++14 compiler" + +#else + +namespace cxx14 +{ + + namespace test_polymorphic_lambdas + { + + int + test() + { + const auto lambda = [](auto&&... args){ + const auto istiny = [](auto x){ + return (sizeof(x) == 1UL) ? 1 : 0; + }; + const int aretiny[] = { istiny(args)... }; + return aretiny[0]; + }; + return lambda(1, 1L, 1.0f, '1'); + } + + } + + namespace test_binary_literals + { + + constexpr auto ivii = 0b0000000000101010; + static_assert(ivii == 42, "wrong value"); + + } + + namespace test_generalized_constexpr + { + + template < typename CharT > + constexpr unsigned long + strlen_c(const CharT *const s) noexcept + { + auto length = 0UL; + for (auto p = s; *p; ++p) + ++length; + return length; + } + + static_assert(strlen_c("") == 0UL, ""); + static_assert(strlen_c("x") == 1UL, ""); + static_assert(strlen_c("test") == 4UL, ""); + static_assert(strlen_c("another\0test") == 7UL, ""); + + } + + namespace test_lambda_init_capture + { + + int + test() + { + auto x = 0; + const auto lambda1 = [a = x](int b){ return a + b; }; + const auto lambda2 = [a = lambda1(x)](){ return a; }; + return lambda2(); + } + + } + + namespace test_digit_separators + { + + constexpr auto ten_million = 100'000'000; + static_assert(ten_million == 100000000, ""); + + } + + namespace test_return_type_deduction + { + + auto f(int& x) { return x; } + decltype(auto) g(int& x) { return x; } + + template < typename T1, typename T2 > + struct is_same + { + static constexpr auto value = false; + }; + + template < typename T > + struct is_same + { + static constexpr auto value = true; + }; + + int + test() + { + auto x = 0; + static_assert(is_same::value, ""); + static_assert(is_same::value, ""); + return x; + } + + } + +} // namespace cxx14 + +#endif // __cplusplus >= 201402L + +]]) + + +dnl Tests for new features in C++17 + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_17], [[ + +// If the compiler admits that it is not ready for C++17, why torture it? +// Hopefully, this will speed up the test. + +#ifndef __cplusplus + +#error "This is not a C++ compiler" + +#elif __cplusplus < 201703L + +#error "This is not a C++17 compiler" + +#else + +#include +#include +#include + +namespace cxx17 +{ + + namespace test_constexpr_lambdas + { + + constexpr int foo = [](){return 42;}(); + + } + + namespace test::nested_namespace::definitions + { + + } + + namespace test_fold_expression + { + + template + int multiply(Args... args) + { + return (args * ... * 1); + } + + template + bool all(Args... args) + { + return (args && ...); + } + + } + + namespace test_extended_static_assert + { + + static_assert (true); + + } + + namespace test_auto_brace_init_list + { + + auto foo = {5}; + auto bar {5}; + + static_assert(std::is_same, decltype(foo)>::value); + static_assert(std::is_same::value); + } + + namespace test_typename_in_template_template_parameter + { + + template typename X> struct D; + + } + + namespace test_fallthrough_nodiscard_maybe_unused_attributes + { + + int f1() + { + return 42; + } + + [[nodiscard]] int f2() + { + [[maybe_unused]] auto unused = f1(); + + switch (f1()) + { + case 17: + f1(); + [[fallthrough]]; + case 42: + f1(); + } + return f1(); + } + + } + + namespace test_extended_aggregate_initialization + { + + struct base1 + { + int b1, b2 = 42; + }; + + struct base2 + { + base2() { + b3 = 42; + } + int b3; + }; + + struct derived : base1, base2 + { + int d; + }; + + derived d1 {{1, 2}, {}, 4}; // full initialization + derived d2 {{}, {}, 4}; // value-initialized bases + + } + + namespace test_general_range_based_for_loop + { + + struct iter + { + int i; + + int& operator* () + { + return i; + } + + const int& operator* () const + { + return i; + } + + iter& operator++() + { + ++i; + return *this; + } + }; + + struct sentinel + { + int i; + }; + + bool operator== (const iter& i, const sentinel& s) + { + return i.i == s.i; + } + + bool operator!= (const iter& i, const sentinel& s) + { + return !(i == s); + } + + struct range + { + iter begin() const + { + return {0}; + } + + sentinel end() const + { + return {5}; + } + }; + + void f() + { + range r {}; + + for (auto i : r) + { + [[maybe_unused]] auto v = i; + } + } + + } + + namespace test_lambda_capture_asterisk_this_by_value + { + + struct t + { + int i; + int foo() + { + return [*this]() + { + return i; + }(); + } + }; + + } + + namespace test_enum_class_construction + { + + enum class byte : unsigned char + {}; + + byte foo {42}; + + } + + namespace test_constexpr_if + { + + template + int f () + { + if constexpr(cond) + { + return 13; + } + else + { + return 42; + } + } + + } + + namespace test_selection_statement_with_initializer + { + + int f() + { + return 13; + } + + int f2() + { + if (auto i = f(); i > 0) + { + return 3; + } + + switch (auto i = f(); i + 4) + { + case 17: + return 2; + + default: + return 1; + } + } + + } + + namespace test_template_argument_deduction_for_class_templates + { + + template + struct pair + { + pair (T1 p1, T2 p2) + : m1 {p1}, + m2 {p2} + {} + + T1 m1; + T2 m2; + }; + + void f() + { + [[maybe_unused]] auto p = pair{13, 42u}; + } + + } + + namespace test_non_type_auto_template_parameters + { + + template + struct B + {}; + + B<5> b1; + B<'a'> b2; + + } + + namespace test_structured_bindings + { + + int arr[2] = { 1, 2 }; + std::pair pr = { 1, 2 }; + + auto f1() -> int(&)[2] + { + return arr; + } + + auto f2() -> std::pair& + { + return pr; + } + + struct S + { + int x1 : 2; + volatile double y1; + }; + + S f3() + { + return {}; + } + + auto [ x1, y1 ] = f1(); + auto& [ xr1, yr1 ] = f1(); + auto [ x2, y2 ] = f2(); + auto& [ xr2, yr2 ] = f2(); + const auto [ x3, y3 ] = f3(); + + } + + namespace test_exception_spec_type_system + { + + struct Good {}; + struct Bad {}; + + void g1() noexcept; + void g2(); + + template + Bad + f(T*, T*); + + template + Good + f(T1*, T2*); + + static_assert (std::is_same_v); + + } + + namespace test_inline_variables + { + + template void f(T) + {} + + template inline T g(T) + { + return T{}; + } + + template<> inline void f<>(int) + {} + + template<> int g<>(int) + { + return 5; + } + + } + +} // namespace cxx17 + +#endif // __cplusplus < 201703L + +]]) diff --git a/m4/ax_cxx_compile_stdcxx_11.m4 b/m4/ax_cxx_compile_stdcxx_11.m4 new file mode 100644 index 0000000..0aadeaf --- /dev/null +++ b/m4/ax_cxx_compile_stdcxx_11.m4 @@ -0,0 +1,39 @@ +# ============================================================================ +# http://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx_11.html +# ============================================================================ +# +# SYNOPSIS +# +# AX_CXX_COMPILE_STDCXX_11([ext|noext], [mandatory|optional]) +# +# DESCRIPTION +# +# Check for baseline language coverage in the compiler for the C++11 +# standard; if necessary, add switches to CXX and CXXCPP to enable +# support. +# +# This macro is a convenience alias for calling the AX_CXX_COMPILE_STDCXX +# macro with the version set to C++11. The two optional arguments are +# forwarded literally as the second and third argument respectively. +# Please see the documentation for the AX_CXX_COMPILE_STDCXX macro for +# more information. If you want to use this macro, you also need to +# download the ax_cxx_compile_stdcxx.m4 file. +# +# LICENSE +# +# Copyright (c) 2008 Benjamin Kosnik +# Copyright (c) 2012 Zack Weinberg +# Copyright (c) 2013 Roy Stogner +# Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov +# Copyright (c) 2015 Paul Norman +# Copyright (c) 2015 Moritz Klammler +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 17 + +AX_REQUIRE_DEFINED([AX_CXX_COMPILE_STDCXX]) +AC_DEFUN([AX_CXX_COMPILE_STDCXX_11], [AX_CXX_COMPILE_STDCXX([11], [$1], [$2])]) diff --git a/m4/ax_cxx_compile_stdcxx_14.m4 b/m4/ax_cxx_compile_stdcxx_14.m4 new file mode 100644 index 0000000..094db0d --- /dev/null +++ b/m4/ax_cxx_compile_stdcxx_14.m4 @@ -0,0 +1,34 @@ +# ============================================================================= +# https://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx_14.html +# ============================================================================= +# +# SYNOPSIS +# +# AX_CXX_COMPILE_STDCXX_14([ext|noext], [mandatory|optional]) +# +# DESCRIPTION +# +# Check for baseline language coverage in the compiler for the C++14 +# standard; if necessary, add switches to CXX and CXXCPP to enable +# support. +# +# This macro is a convenience alias for calling the AX_CXX_COMPILE_STDCXX +# macro with the version set to C++14. The two optional arguments are +# forwarded literally as the second and third argument respectively. +# Please see the documentation for the AX_CXX_COMPILE_STDCXX macro for +# more information. If you want to use this macro, you also need to +# download the ax_cxx_compile_stdcxx.m4 file. +# +# LICENSE +# +# Copyright (c) 2015 Moritz Klammler +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 5 + +AX_REQUIRE_DEFINED([AX_CXX_COMPILE_STDCXX]) +AC_DEFUN([AX_CXX_COMPILE_STDCXX_14], [AX_CXX_COMPILE_STDCXX([14], [$1], [$2])]) diff --git a/m4/ax_cxx_compile_stdcxx_17.m4 b/m4/ax_cxx_compile_stdcxx_17.m4 new file mode 100644 index 0000000..a683417 --- /dev/null +++ b/m4/ax_cxx_compile_stdcxx_17.m4 @@ -0,0 +1,35 @@ +# ============================================================================= +# https://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx_17.html +# ============================================================================= +# +# SYNOPSIS +# +# AX_CXX_COMPILE_STDCXX_17([ext|noext], [mandatory|optional]) +# +# DESCRIPTION +# +# Check for baseline language coverage in the compiler for the C++17 +# standard; if necessary, add switches to CXX and CXXCPP to enable +# support. +# +# This macro is a convenience alias for calling the AX_CXX_COMPILE_STDCXX +# macro with the version set to C++17. The two optional arguments are +# forwarded literally as the second and third argument respectively. +# Please see the documentation for the AX_CXX_COMPILE_STDCXX macro for +# more information. If you want to use this macro, you also need to +# download the ax_cxx_compile_stdcxx.m4 file. +# +# LICENSE +# +# Copyright (c) 2015 Moritz Klammler +# Copyright (c) 2016 Krzesimir Nowak +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 2 + +AX_REQUIRE_DEFINED([AX_CXX_COMPILE_STDCXX]) +AC_DEFUN([AX_CXX_COMPILE_STDCXX_17], [AX_CXX_COMPILE_STDCXX([17], [$1], [$2])]) diff --git a/m4/ax_file_escapes.m4 b/m4/ax_file_escapes.m4 new file mode 100644 index 0000000..a86fdc3 --- /dev/null +++ b/m4/ax_file_escapes.m4 @@ -0,0 +1,30 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_file_escapes.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_FILE_ESCAPES +# +# DESCRIPTION +# +# Writes the specified data to the specified file. +# +# LICENSE +# +# Copyright (c) 2008 Tom Howard +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 8 + +AC_DEFUN([AX_FILE_ESCAPES],[ +AX_DOLLAR="\$" +AX_SRB="\\135" +AX_SLB="\\133" +AX_BS="\\\\" +AX_DQ="\"" +]) diff --git a/m4/ax_prog_cc_for_build.m4 b/m4/ax_prog_cc_for_build.m4 new file mode 100644 index 0000000..18a4287 --- /dev/null +++ b/m4/ax_prog_cc_for_build.m4 @@ -0,0 +1,141 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_prog_cc_for_build.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_PROG_CC_FOR_BUILD +# +# DESCRIPTION +# +# This macro searches for a C compiler that generates native executables, +# that is a C compiler that surely is not a cross-compiler. This can be +# useful if you have to generate source code at compile-time like for +# example GCC does. +# +# The macro sets the CC_FOR_BUILD and CPP_FOR_BUILD macros to anything +# needed to compile or link (CC_FOR_BUILD) and preprocess (CPP_FOR_BUILD). +# The value of these variables can be overridden by the user by specifying +# a compiler with an environment variable (like you do for standard CC). +# +# It also sets BUILD_EXEEXT and BUILD_OBJEXT to the executable and object +# file extensions for the build platform, and GCC_FOR_BUILD to `yes' if +# the compiler we found is GCC. All these variables but GCC_FOR_BUILD are +# substituted in the Makefile. +# +# LICENSE +# +# Copyright (c) 2008 Paolo Bonzini +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 18 + +AU_ALIAS([AC_PROG_CC_FOR_BUILD], [AX_PROG_CC_FOR_BUILD]) +AC_DEFUN([AX_PROG_CC_FOR_BUILD], [dnl +AC_REQUIRE([AC_PROG_CC])dnl +AC_REQUIRE([AC_PROG_CPP])dnl +AC_REQUIRE([AC_CANONICAL_BUILD])dnl + +dnl Use the standard macros, but make them use other variable names +dnl +pushdef([ac_cv_prog_CPP], ac_cv_build_prog_CPP)dnl +pushdef([ac_cv_prog_cc_c89], ac_cv_build_prog_cc_c89)dnl +pushdef([ac_cv_prog_gcc], ac_cv_build_prog_gcc)dnl +pushdef([ac_cv_prog_cc_works], ac_cv_build_prog_cc_works)dnl +pushdef([ac_cv_prog_cc_cross], ac_cv_build_prog_cc_cross)dnl +pushdef([ac_cv_prog_cc_g], ac_cv_build_prog_cc_g)dnl +dnl pushdef([ac_cv_c_compiler_gnu], ac_cv_build_c_compiler_gnu)dnl +pushdef([ac_cv_exeext], ac_cv_build_exeext)dnl +pushdef([ac_cv_objext], ac_cv_build_objext)dnl +pushdef([ac_exeext], ac_build_exeext)dnl +pushdef([ac_objext], ac_build_objext)dnl +pushdef([CC], CC_FOR_BUILD)dnl +pushdef([CPP], CPP_FOR_BUILD)dnl +pushdef([GCC], GCC_FOR_BUILD)dnl +pushdef([CFLAGS], CFLAGS_FOR_BUILD)dnl +pushdef([CPPFLAGS], CPPFLAGS_FOR_BUILD)dnl +pushdef([EXEEXT], BUILD_EXEEXT)dnl +pushdef([LDFLAGS], LDFLAGS_FOR_BUILD)dnl +pushdef([OBJEXT], BUILD_OBJEXT)dnl +pushdef([host], build)dnl +pushdef([host_alias], build_alias)dnl +pushdef([host_cpu], build_cpu)dnl +pushdef([host_vendor], build_vendor)dnl +pushdef([host_os], build_os)dnl +pushdef([ac_cv_host], ac_cv_build)dnl +pushdef([ac_cv_host_alias], ac_cv_build_alias)dnl +pushdef([ac_cv_host_cpu], ac_cv_build_cpu)dnl +pushdef([ac_cv_host_vendor], ac_cv_build_vendor)dnl +pushdef([ac_cv_host_os], ac_cv_build_os)dnl +pushdef([ac_tool_prefix], ac_build_tool_prefix)dnl +pushdef([am_cv_CC_dependencies_compiler_type], am_cv_build_CC_dependencies_compiler_type)dnl +pushdef([am_cv_prog_cc_c_o], am_cv_build_prog_cc_c_o)dnl +pushdef([cross_compiling], cross_compiling_build)dnl + +_save_ax_prog_cc_for_build__ac_ext="$ac_cv_c_compiler_gnu" +cross_compiling_build=no + +ac_build_tool_prefix= +AS_IF([test -n "$build"], [ac_build_tool_prefix="$build-"], + [test -n "$build_alias"],[ac_build_tool_prefix="$build_alias-"]) + +AC_LANG_PUSH([C]) +AC_PROG_CC +_AC_COMPILER_EXEEXT +_AC_COMPILER_OBJEXT +AC_PROG_CPP + +dnl Restore the old definitions +dnl +ac_cv_c_compiler_gnu="$_save_ax_prog_cc_for_build__ac_ext" +popdef([cross_compiling])dnl +popdef([am_cv_prog_cc_c_o])dnl +popdef([am_cv_CC_dependencies_compiler_type])dnl +popdef([ac_tool_prefix])dnl +popdef([ac_cv_host_os])dnl +popdef([ac_cv_host_vendor])dnl +popdef([ac_cv_host_cpu])dnl +popdef([ac_cv_host_alias])dnl +popdef([ac_cv_host])dnl +popdef([host_os])dnl +popdef([host_vendor])dnl +popdef([host_cpu])dnl +popdef([host_alias])dnl +popdef([host])dnl +popdef([OBJEXT])dnl +popdef([LDFLAGS])dnl +popdef([EXEEXT])dnl +popdef([CPPFLAGS])dnl +popdef([CFLAGS])dnl +popdef([GCC])dnl +popdef([CPP])dnl +popdef([CC])dnl +popdef([ac_objext])dnl +popdef([ac_exeext])dnl +popdef([ac_cv_objext])dnl +popdef([ac_cv_exeext])dnl +dnl popdef([ac_cv_c_compiler_gnu])dnl +popdef([ac_cv_prog_cc_g])dnl +popdef([ac_cv_prog_cc_cross])dnl +popdef([ac_cv_prog_cc_works])dnl +popdef([ac_cv_prog_cc_c89])dnl +popdef([ac_cv_prog_gcc])dnl +popdef([ac_cv_prog_CPP])dnl + +dnl restore global variables ac_ext, ac_cpp, ac_compile, +dnl ac_link, ac_compiler_gnu (dependant on the current +dnl language after popping): +AC_LANG_POP([C]) + +dnl Finally, set Makefile variables +dnl +AC_SUBST(BUILD_EXEEXT)dnl +AC_SUBST(BUILD_OBJEXT)dnl +AC_SUBST([CFLAGS_FOR_BUILD])dnl +AC_SUBST([CPPFLAGS_FOR_BUILD])dnl +AC_SUBST([LDFLAGS_FOR_BUILD])dnl +]) diff --git a/m4/ax_pthread.m4 b/m4/ax_pthread.m4 new file mode 100644 index 0000000..1598d07 --- /dev/null +++ b/m4/ax_pthread.m4 @@ -0,0 +1,507 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_pthread.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) +# +# DESCRIPTION +# +# This macro figures out how to build C programs using POSIX threads. It +# sets the PTHREAD_LIBS output variable to the threads library and linker +# flags, and the PTHREAD_CFLAGS output variable to any special C compiler +# flags that are needed. (The user can also force certain compiler +# flags/libs to be tested by setting these environment variables.) +# +# Also sets PTHREAD_CC to any special C compiler that is needed for +# multi-threaded programs (defaults to the value of CC otherwise). (This +# is necessary on AIX to use the special cc_r compiler alias.) +# +# NOTE: You are assumed to not only compile your program with these flags, +# but also to link with them as well. For example, you might link with +# $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS +# +# If you are only building threaded programs, you may wish to use these +# variables in your default LIBS, CFLAGS, and CC: +# +# LIBS="$PTHREAD_LIBS $LIBS" +# CFLAGS="$CFLAGS $PTHREAD_CFLAGS" +# CC="$PTHREAD_CC" +# +# In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant +# has a nonstandard name, this macro defines PTHREAD_CREATE_JOINABLE to +# that name (e.g. PTHREAD_CREATE_UNDETACHED on AIX). +# +# Also HAVE_PTHREAD_PRIO_INHERIT is defined if pthread is found and the +# PTHREAD_PRIO_INHERIT symbol is defined when compiling with +# PTHREAD_CFLAGS. +# +# ACTION-IF-FOUND is a list of shell commands to run if a threads library +# is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it +# is not found. If ACTION-IF-FOUND is not specified, the default action +# will define HAVE_PTHREAD. +# +# Please let the authors know if this macro fails on any platform, or if +# you have any other suggestions or comments. This macro was based on work +# by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help +# from M. Frigo), as well as ac_pthread and hb_pthread macros posted by +# Alejandro Forero Cuervo to the autoconf macro repository. We are also +# grateful for the helpful feedback of numerous users. +# +# Updated for Autoconf 2.68 by Daniel Richard G. +# +# LICENSE +# +# Copyright (c) 2008 Steven G. Johnson +# Copyright (c) 2011 Daniel Richard G. +# Copyright (c) 2019 Marc Stevens +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 27 + +AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD]) +AC_DEFUN([AX_PTHREAD], [ +AC_REQUIRE([AC_CANONICAL_HOST]) +AC_REQUIRE([AC_PROG_CC]) +AC_REQUIRE([AC_PROG_SED]) +AC_LANG_PUSH([C]) +ax_pthread_ok=no + +# We used to check for pthread.h first, but this fails if pthread.h +# requires special compiler flags (e.g. on Tru64 or Sequent). +# It gets checked for in the link test anyway. + +# First of all, check if the user has set any of the PTHREAD_LIBS, +# etcetera environment variables, and if threads linking works using +# them: +if test "x$PTHREAD_CFLAGS$PTHREAD_LIBS" != "x"; then + ax_pthread_save_CC="$CC" + ax_pthread_save_CFLAGS="$CFLAGS" + ax_pthread_save_LIBS="$LIBS" + AS_IF([test "x$PTHREAD_CC" != "x"], [CC="$PTHREAD_CC"]) + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + LIBS="$PTHREAD_LIBS $LIBS" + AC_MSG_CHECKING([for pthread_join using $CC $PTHREAD_CFLAGS $PTHREAD_LIBS]) + AC_LINK_IFELSE([AC_LANG_CALL([], [pthread_join])], [ax_pthread_ok=yes]) + AC_MSG_RESULT([$ax_pthread_ok]) + if test "x$ax_pthread_ok" = "xno"; then + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" + fi + CC="$ax_pthread_save_CC" + CFLAGS="$ax_pthread_save_CFLAGS" + LIBS="$ax_pthread_save_LIBS" +fi + +# We must check for the threads library under a number of different +# names; the ordering is very important because some systems +# (e.g. DEC) have both -lpthread and -lpthreads, where one of the +# libraries is broken (non-POSIX). + +# Create a list of thread flags to try. Items with a "," contain both +# C compiler flags (before ",") and linker flags (after ","). Other items +# starting with a "-" are C compiler flags, and remaining items are +# library names, except for "none" which indicates that we try without +# any flags at all, and "pthread-config" which is a program returning +# the flags for the Pth emulation library. + +ax_pthread_flags="pthreads none -Kthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" + +# The ordering *is* (sometimes) important. Some notes on the +# individual items follow: + +# pthreads: AIX (must check this before -lpthread) +# none: in case threads are in libc; should be tried before -Kthread and +# other compiler flags to prevent continual compiler warnings +# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) +# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads), Tru64 +# (Note: HP C rejects this with "bad form for `-t' option") +# -pthreads: Solaris/gcc (Note: HP C also rejects) +# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it +# doesn't hurt to check since this sometimes defines pthreads and +# -D_REENTRANT too), HP C (must be checked before -lpthread, which +# is present but should not be used directly; and before -mthreads, +# because the compiler interprets this as "-mt" + "-hreads") +# -mthreads: Mingw32/gcc, Lynx/gcc +# pthread: Linux, etcetera +# --thread-safe: KAI C++ +# pthread-config: use pthread-config program (for GNU Pth library) + +case $host_os in + + freebsd*) + + # -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) + # lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) + + ax_pthread_flags="-kthread lthread $ax_pthread_flags" + ;; + + hpux*) + + # From the cc(1) man page: "[-mt] Sets various -D flags to enable + # multi-threading and also sets -lpthread." + + ax_pthread_flags="-mt -pthread pthread $ax_pthread_flags" + ;; + + openedition*) + + # IBM z/OS requires a feature-test macro to be defined in order to + # enable POSIX threads at all, so give the user a hint if this is + # not set. (We don't define these ourselves, as they can affect + # other portions of the system API in unpredictable ways.) + + AC_EGREP_CPP([AX_PTHREAD_ZOS_MISSING], + [ +# if !defined(_OPEN_THREADS) && !defined(_UNIX03_THREADS) + AX_PTHREAD_ZOS_MISSING +# endif + ], + [AC_MSG_WARN([IBM z/OS requires -D_OPEN_THREADS or -D_UNIX03_THREADS to enable pthreads support.])]) + ;; + + solaris*) + + # On Solaris (at least, for some versions), libc contains stubbed + # (non-functional) versions of the pthreads routines, so link-based + # tests will erroneously succeed. (N.B.: The stubs are missing + # pthread_cleanup_push, or rather a function called by this macro, + # so we could check for that, but who knows whether they'll stub + # that too in a future libc.) So we'll check first for the + # standard Solaris way of linking pthreads (-mt -lpthread). + + ax_pthread_flags="-mt,-lpthread pthread $ax_pthread_flags" + ;; +esac + +# Are we compiling with Clang? + +AC_CACHE_CHECK([whether $CC is Clang], + [ax_cv_PTHREAD_CLANG], + [ax_cv_PTHREAD_CLANG=no + # Note that Autoconf sets GCC=yes for Clang as well as GCC + if test "x$GCC" = "xyes"; then + AC_EGREP_CPP([AX_PTHREAD_CC_IS_CLANG], + [/* Note: Clang 2.7 lacks __clang_[a-z]+__ */ +# if defined(__clang__) && defined(__llvm__) + AX_PTHREAD_CC_IS_CLANG +# endif + ], + [ax_cv_PTHREAD_CLANG=yes]) + fi + ]) +ax_pthread_clang="$ax_cv_PTHREAD_CLANG" + + +# GCC generally uses -pthread, or -pthreads on some platforms (e.g. SPARC) + +# Note that for GCC and Clang -pthread generally implies -lpthread, +# except when -nostdlib is passed. +# This is problematic using libtool to build C++ shared libraries with pthread: +# [1] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=25460 +# [2] https://bugzilla.redhat.com/show_bug.cgi?id=661333 +# [3] https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=468555 +# To solve this, first try -pthread together with -lpthread for GCC + +AS_IF([test "x$GCC" = "xyes"], + [ax_pthread_flags="-pthread,-lpthread -pthread -pthreads $ax_pthread_flags"]) + +# Clang takes -pthread (never supported any other flag), but we'll try with -lpthread first + +AS_IF([test "x$ax_pthread_clang" = "xyes"], + [ax_pthread_flags="-pthread,-lpthread -pthread"]) + + +# The presence of a feature test macro requesting re-entrant function +# definitions is, on some systems, a strong hint that pthreads support is +# correctly enabled + +case $host_os in + darwin* | hpux* | linux* | osf* | solaris*) + ax_pthread_check_macro="_REENTRANT" + ;; + + aix*) + ax_pthread_check_macro="_THREAD_SAFE" + ;; + + *) + ax_pthread_check_macro="--" + ;; +esac +AS_IF([test "x$ax_pthread_check_macro" = "x--"], + [ax_pthread_check_cond=0], + [ax_pthread_check_cond="!defined($ax_pthread_check_macro)"]) + + +if test "x$ax_pthread_ok" = "xno"; then +for ax_pthread_try_flag in $ax_pthread_flags; do + + case $ax_pthread_try_flag in + none) + AC_MSG_CHECKING([whether pthreads work without any flags]) + ;; + + *,*) + PTHREAD_CFLAGS=`echo $ax_pthread_try_flag | sed "s/^\(.*\),\(.*\)$/\1/"` + PTHREAD_LIBS=`echo $ax_pthread_try_flag | sed "s/^\(.*\),\(.*\)$/\2/"` + AC_MSG_CHECKING([whether pthreads work with "$PTHREAD_CFLAGS" and "$PTHREAD_LIBS"]) + ;; + + -*) + AC_MSG_CHECKING([whether pthreads work with $ax_pthread_try_flag]) + PTHREAD_CFLAGS="$ax_pthread_try_flag" + ;; + + pthread-config) + AC_CHECK_PROG([ax_pthread_config], [pthread-config], [yes], [no]) + AS_IF([test "x$ax_pthread_config" = "xno"], [continue]) + PTHREAD_CFLAGS="`pthread-config --cflags`" + PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" + ;; + + *) + AC_MSG_CHECKING([for the pthreads library -l$ax_pthread_try_flag]) + PTHREAD_LIBS="-l$ax_pthread_try_flag" + ;; + esac + + ax_pthread_save_CFLAGS="$CFLAGS" + ax_pthread_save_LIBS="$LIBS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + LIBS="$PTHREAD_LIBS $LIBS" + + # Check for various functions. We must include pthread.h, + # since some functions may be macros. (On the Sequent, we + # need a special flag -Kthread to make this header compile.) + # We check for pthread_join because it is in -lpthread on IRIX + # while pthread_create is in libc. We check for pthread_attr_init + # due to DEC craziness with -lpthreads. We check for + # pthread_cleanup_push because it is one of the few pthread + # functions on Solaris that doesn't have a non-functional libc stub. + # We try pthread_create on general principles. + + AC_LINK_IFELSE([AC_LANG_PROGRAM([#include +# if $ax_pthread_check_cond +# error "$ax_pthread_check_macro must be defined" +# endif + static void *some_global = NULL; + static void routine(void *a) + { + /* To avoid any unused-parameter or + unused-but-set-parameter warning. */ + some_global = a; + } + static void *start_routine(void *a) { return a; }], + [pthread_t th; pthread_attr_t attr; + pthread_create(&th, 0, start_routine, 0); + pthread_join(th, 0); + pthread_attr_init(&attr); + pthread_cleanup_push(routine, 0); + pthread_cleanup_pop(0) /* ; */])], + [ax_pthread_ok=yes], + []) + + CFLAGS="$ax_pthread_save_CFLAGS" + LIBS="$ax_pthread_save_LIBS" + + AC_MSG_RESULT([$ax_pthread_ok]) + AS_IF([test "x$ax_pthread_ok" = "xyes"], [break]) + + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" +done +fi + + +# Clang needs special handling, because older versions handle the -pthread +# option in a rather... idiosyncratic way + +if test "x$ax_pthread_clang" = "xyes"; then + + # Clang takes -pthread; it has never supported any other flag + + # (Note 1: This will need to be revisited if a system that Clang + # supports has POSIX threads in a separate library. This tends not + # to be the way of modern systems, but it's conceivable.) + + # (Note 2: On some systems, notably Darwin, -pthread is not needed + # to get POSIX threads support; the API is always present and + # active. We could reasonably leave PTHREAD_CFLAGS empty. But + # -pthread does define _REENTRANT, and while the Darwin headers + # ignore this macro, third-party headers might not.) + + # However, older versions of Clang make a point of warning the user + # that, in an invocation where only linking and no compilation is + # taking place, the -pthread option has no effect ("argument unused + # during compilation"). They expect -pthread to be passed in only + # when source code is being compiled. + # + # Problem is, this is at odds with the way Automake and most other + # C build frameworks function, which is that the same flags used in + # compilation (CFLAGS) are also used in linking. Many systems + # supported by AX_PTHREAD require exactly this for POSIX threads + # support, and in fact it is often not straightforward to specify a + # flag that is used only in the compilation phase and not in + # linking. Such a scenario is extremely rare in practice. + # + # Even though use of the -pthread flag in linking would only print + # a warning, this can be a nuisance for well-run software projects + # that build with -Werror. So if the active version of Clang has + # this misfeature, we search for an option to squash it. + + AC_CACHE_CHECK([whether Clang needs flag to prevent "argument unused" warning when linking with -pthread], + [ax_cv_PTHREAD_CLANG_NO_WARN_FLAG], + [ax_cv_PTHREAD_CLANG_NO_WARN_FLAG=unknown + # Create an alternate version of $ac_link that compiles and + # links in two steps (.c -> .o, .o -> exe) instead of one + # (.c -> exe), because the warning occurs only in the second + # step + ax_pthread_save_ac_link="$ac_link" + ax_pthread_sed='s/conftest\.\$ac_ext/conftest.$ac_objext/g' + ax_pthread_link_step=`$as_echo "$ac_link" | sed "$ax_pthread_sed"` + ax_pthread_2step_ac_link="($ac_compile) && (echo ==== >&5) && ($ax_pthread_link_step)" + ax_pthread_save_CFLAGS="$CFLAGS" + for ax_pthread_try in '' -Qunused-arguments -Wno-unused-command-line-argument unknown; do + AS_IF([test "x$ax_pthread_try" = "xunknown"], [break]) + CFLAGS="-Werror -Wunknown-warning-option $ax_pthread_try -pthread $ax_pthread_save_CFLAGS" + ac_link="$ax_pthread_save_ac_link" + AC_LINK_IFELSE([AC_LANG_SOURCE([[int main(void){return 0;}]])], + [ac_link="$ax_pthread_2step_ac_link" + AC_LINK_IFELSE([AC_LANG_SOURCE([[int main(void){return 0;}]])], + [break]) + ]) + done + ac_link="$ax_pthread_save_ac_link" + CFLAGS="$ax_pthread_save_CFLAGS" + AS_IF([test "x$ax_pthread_try" = "x"], [ax_pthread_try=no]) + ax_cv_PTHREAD_CLANG_NO_WARN_FLAG="$ax_pthread_try" + ]) + + case "$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG" in + no | unknown) ;; + *) PTHREAD_CFLAGS="$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG $PTHREAD_CFLAGS" ;; + esac + +fi # $ax_pthread_clang = yes + + + +# Various other checks: +if test "x$ax_pthread_ok" = "xyes"; then + ax_pthread_save_CFLAGS="$CFLAGS" + ax_pthread_save_LIBS="$LIBS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + LIBS="$PTHREAD_LIBS $LIBS" + + # Detect AIX lossage: JOINABLE attribute is called UNDETACHED. + AC_CACHE_CHECK([for joinable pthread attribute], + [ax_cv_PTHREAD_JOINABLE_ATTR], + [ax_cv_PTHREAD_JOINABLE_ATTR=unknown + for ax_pthread_attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do + AC_LINK_IFELSE([AC_LANG_PROGRAM([#include ], + [int attr = $ax_pthread_attr; return attr /* ; */])], + [ax_cv_PTHREAD_JOINABLE_ATTR=$ax_pthread_attr; break], + []) + done + ]) + AS_IF([test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xunknown" && \ + test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xPTHREAD_CREATE_JOINABLE" && \ + test "x$ax_pthread_joinable_attr_defined" != "xyes"], + [AC_DEFINE_UNQUOTED([PTHREAD_CREATE_JOINABLE], + [$ax_cv_PTHREAD_JOINABLE_ATTR], + [Define to necessary symbol if this constant + uses a non-standard name on your system.]) + ax_pthread_joinable_attr_defined=yes + ]) + + AC_CACHE_CHECK([whether more special flags are required for pthreads], + [ax_cv_PTHREAD_SPECIAL_FLAGS], + [ax_cv_PTHREAD_SPECIAL_FLAGS=no + case $host_os in + solaris*) + ax_cv_PTHREAD_SPECIAL_FLAGS="-D_POSIX_PTHREAD_SEMANTICS" + ;; + esac + ]) + AS_IF([test "x$ax_cv_PTHREAD_SPECIAL_FLAGS" != "xno" && \ + test "x$ax_pthread_special_flags_added" != "xyes"], + [PTHREAD_CFLAGS="$ax_cv_PTHREAD_SPECIAL_FLAGS $PTHREAD_CFLAGS" + ax_pthread_special_flags_added=yes]) + + AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT], + [ax_cv_PTHREAD_PRIO_INHERIT], + [AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include ]], + [[int i = PTHREAD_PRIO_INHERIT; + return i;]])], + [ax_cv_PTHREAD_PRIO_INHERIT=yes], + [ax_cv_PTHREAD_PRIO_INHERIT=no]) + ]) + AS_IF([test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes" && \ + test "x$ax_pthread_prio_inherit_defined" != "xyes"], + [AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], [1], [Have PTHREAD_PRIO_INHERIT.]) + ax_pthread_prio_inherit_defined=yes + ]) + + CFLAGS="$ax_pthread_save_CFLAGS" + LIBS="$ax_pthread_save_LIBS" + + # More AIX lossage: compile with *_r variant + if test "x$GCC" != "xyes"; then + case $host_os in + aix*) + AS_CASE(["x/$CC"], + [x*/c89|x*/c89_128|x*/c99|x*/c99_128|x*/cc|x*/cc128|x*/xlc|x*/xlc_v6|x*/xlc128|x*/xlc128_v6], + [#handle absolute path differently from PATH based program lookup + AS_CASE(["x$CC"], + [x/*], + [AS_IF([AS_EXECUTABLE_P([${CC}_r])],[PTHREAD_CC="${CC}_r"])], + [AC_CHECK_PROGS([PTHREAD_CC],[${CC}_r],[$CC])])]) + ;; + esac + fi +fi + +test -n "$PTHREAD_CC" || PTHREAD_CC="$CC" + +AC_SUBST([PTHREAD_LIBS]) +AC_SUBST([PTHREAD_CFLAGS]) +AC_SUBST([PTHREAD_CC]) + +# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: +if test "x$ax_pthread_ok" = "xyes"; then + ifelse([$1],,[AC_DEFINE([HAVE_PTHREAD],[1],[Define if you have POSIX threads libraries and header files.])],[$1]) + : +else + ax_pthread_ok=no + $2 +fi +AC_LANG_POP +])dnl AX_PTHREAD diff --git a/m4/ax_with_curses.m4 b/m4/ax_with_curses.m4 new file mode 100644 index 0000000..cf1ee01 --- /dev/null +++ b/m4/ax_with_curses.m4 @@ -0,0 +1,517 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_with_curses.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_WITH_CURSES +# +# DESCRIPTION +# +# This macro checks whether a SysV or X/Open-compatible Curses library is +# present, along with the associated header file. The NcursesW +# (wide-character) library is searched for first, followed by Ncurses, +# then the system-default plain Curses. The first library found is the +# one returned. +# +# The following options are understood: --with-ncursesw, --with-ncurses, +# --without-ncursesw, --without-ncurses. The "--with" options force the +# macro to use that particular library, terminating with an error if not +# found. The "--without" options simply skip the check for that library. +# The effect on the search pattern is: +# +# (no options) - NcursesW, Ncurses, Curses +# --with-ncurses --with-ncursesw - NcursesW only [*] +# --without-ncurses --with-ncursesw - NcursesW only [*] +# --with-ncursesw - NcursesW only [*] +# --with-ncurses --without-ncursesw - Ncurses only [*] +# --with-ncurses - NcursesW, Ncurses [**] +# --without-ncurses --without-ncursesw - Curses only +# --without-ncursesw - Ncurses, Curses +# --without-ncurses - NcursesW, Curses +# +# [*] If the library is not found, abort the configure script. +# +# [**] If the second library (Ncurses) is not found, abort configure. +# +# The following preprocessor symbols may be defined by this macro if the +# appropriate conditions are met: +# +# HAVE_CURSES - if any SysV or X/Open Curses library found +# HAVE_CURSES_ENHANCED - if library supports X/Open Enhanced functions +# HAVE_CURSES_COLOR - if library supports color (enhanced functions) +# HAVE_CURSES_OBSOLETE - if library supports certain obsolete features +# HAVE_NCURSESW - if NcursesW (wide char) library is to be used +# HAVE_NCURSES - if the Ncurses library is to be used +# +# HAVE_CURSES_H - if is present and should be used +# HAVE_NCURSESW_H - if should be used +# HAVE_NCURSES_H - if should be used +# HAVE_NCURSESW_CURSES_H - if should be used +# HAVE_NCURSES_CURSES_H - if should be used +# +# (These preprocessor symbols are discussed later in this document.) +# +# The following output variable is defined by this macro; it is precious +# and may be overridden on the ./configure command line: +# +# CURSES_LIB - library to add to xxx_LDADD +# +# The library listed in CURSES_LIB is NOT added to LIBS by default. You +# need to add CURSES_LIB to the appropriate xxx_LDADD line in your +# Makefile.am. For example: +# +# prog_LDADD = @CURSES_LIB@ +# +# If CURSES_LIB is set on the configure command line (such as by running +# "./configure CURSES_LIB=-lmycurses"), then the only header searched for +# is . The user may use the CPPFLAGS precious variable to +# override the standard #include search path. If the user needs to +# specify an alternative path for a library (such as for a non-standard +# NcurseW), the user should use the LDFLAGS variable. +# +# The following shell variables may be defined by this macro: +# +# ax_cv_curses - set to "yes" if any Curses library found +# ax_cv_curses_enhanced - set to "yes" if Enhanced functions present +# ax_cv_curses_color - set to "yes" if color functions present +# ax_cv_curses_obsolete - set to "yes" if obsolete features present +# +# ax_cv_ncursesw - set to "yes" if NcursesW library found +# ax_cv_ncurses - set to "yes" if Ncurses library found +# ax_cv_plaincurses - set to "yes" if plain Curses library found +# ax_cv_curses_which - set to "ncursesw", "ncurses", "plaincurses" or "no" +# +# These variables can be used in your configure.ac to determine the level +# of support you need from the Curses library. For example, if you must +# have either Ncurses or NcursesW, you could include: +# +# AX_WITH_CURSES +# if test "x$ax_cv_ncursesw" != xyes && test "x$ax_cv_ncurses" != xyes; then +# AX_MSG_ERROR([requires either NcursesW or Ncurses library]) +# fi +# +# If any Curses library will do (but one must be present and must support +# color), you could use: +# +# AX_WITH_CURSES +# if test "x$ax_cv_curses" != xyes || test "x$ax_cv_curses_color" != xyes; then +# AC_MSG_ERROR([requires an X/Open-compatible Curses library with color]) +# fi +# +# Certain preprocessor symbols and shell variables defined by this macro +# can be used to determine various features of the Curses library. In +# particular, HAVE_CURSES and ax_cv_curses are defined if the Curses +# library found conforms to the traditional SysV and/or X/Open Base Curses +# definition. Any working Curses library conforms to this level. +# +# HAVE_CURSES_ENHANCED and ax_cv_curses_enhanced are defined if the +# library supports the X/Open Enhanced Curses definition. In particular, +# the wide-character types attr_t, cchar_t and wint_t, the functions +# wattr_set() and wget_wch() and the macros WA_NORMAL and _XOPEN_CURSES +# are checked. The Ncurses library does NOT conform to this definition, +# although NcursesW does. +# +# HAVE_CURSES_COLOR and ax_cv_curses_color are defined if the library +# supports color functions and macros such as COLOR_PAIR, A_COLOR, +# COLOR_WHITE, COLOR_RED and init_pair(). These are NOT part of the +# X/Open Base Curses definition, but are part of the Enhanced set of +# functions. The Ncurses library DOES support these functions, as does +# NcursesW. +# +# HAVE_CURSES_OBSOLETE and ax_cv_curses_obsolete are defined if the +# library supports certain features present in SysV and BSD Curses but not +# defined in the X/Open definition. In particular, the functions +# getattrs(), getcurx() and getmaxx() are checked. +# +# To use the HAVE_xxx_H preprocessor symbols, insert the following into +# your system.h (or equivalent) header file: +# +# #if defined HAVE_NCURSESW_CURSES_H +# # include +# #elif defined HAVE_NCURSESW_H +# # include +# #elif defined HAVE_NCURSES_CURSES_H +# # include +# #elif defined HAVE_NCURSES_H +# # include +# #elif defined HAVE_CURSES_H +# # include +# #else +# # error "SysV or X/Open-compatible Curses header file required" +# #endif +# +# For previous users of this macro: you should not need to change anything +# in your configure.ac or Makefile.am, as the previous (serial 10) +# semantics are still valid. However, you should update your system.h (or +# equivalent) header file to the fragment shown above. You are encouraged +# also to make use of the extended functionality provided by this version +# of AX_WITH_CURSES, as well as in the additional macros +# AX_WITH_CURSES_PANEL, AX_WITH_CURSES_MENU and AX_WITH_CURSES_FORM. +# +# LICENSE +# +# Copyright (c) 2009 Mark Pulford +# Copyright (c) 2009 Damian Pietras +# Copyright (c) 2012 Reuben Thomas +# Copyright (c) 2011 John Zaitseff +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 13 + +AC_DEFUN([AX_WITH_CURSES], [ + AC_ARG_VAR([CURSES_LIB], [linker library for Curses, e.g. -lcurses]) + AC_ARG_WITH([ncurses], [AS_HELP_STRING([--with-ncurses], + [force the use of Ncurses or NcursesW])], + [], [with_ncurses=check]) + AC_ARG_WITH([ncursesw], [AS_HELP_STRING([--without-ncursesw], + [do not use NcursesW (wide character support)])], + [], [with_ncursesw=check]) + + ax_saved_LIBS=$LIBS + AS_IF([test "x$with_ncurses" = xyes || test "x$with_ncursesw" = xyes], + [ax_with_plaincurses=no], [ax_with_plaincurses=check]) + + ax_cv_curses_which=no + + # Test for NcursesW + + AS_IF([test "x$CURSES_LIB" = x && test "x$with_ncursesw" != xno], [ + LIBS="$ax_saved_LIBS -lncursesw" + + AC_CACHE_CHECK([for NcursesW wide-character library], [ax_cv_ncursesw], [ + AC_LINK_IFELSE([AC_LANG_CALL([], [initscr])], + [ax_cv_ncursesw=yes], [ax_cv_ncursesw=no]) + ]) + AS_IF([test "x$ax_cv_ncursesw" = xno && test "x$with_ncursesw" = xyes], [ + AC_MSG_ERROR([--with-ncursesw specified but could not find NcursesW library]) + ]) + + AS_IF([test "x$ax_cv_ncursesw" = xyes], [ + ax_cv_curses=yes + ax_cv_curses_which=ncursesw + CURSES_LIB="-lncursesw" + AC_DEFINE([HAVE_NCURSESW], [1], [Define to 1 if the NcursesW library is present]) + AC_DEFINE([HAVE_CURSES], [1], [Define to 1 if a SysV or X/Open compatible Curses library is present]) + + AC_CACHE_CHECK([for working ncursesw/curses.h], [ax_cv_header_ncursesw_curses_h], [ + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ + @%:@define _XOPEN_SOURCE_EXTENDED 1 + @%:@include + ]], [[ + chtype a = A_BOLD; + int b = KEY_LEFT; + chtype c = COLOR_PAIR(1) & A_COLOR; + attr_t d = WA_NORMAL; + cchar_t e; + wint_t f; + int g = getattrs(stdscr); + int h = getcurx(stdscr) + getmaxx(stdscr); + initscr(); + init_pair(1, COLOR_WHITE, COLOR_RED); + wattr_set(stdscr, d, 0, NULL); + wget_wch(stdscr, &f); + ]])], + [ax_cv_header_ncursesw_curses_h=yes], + [ax_cv_header_ncursesw_curses_h=no]) + ]) + AS_IF([test "x$ax_cv_header_ncursesw_curses_h" = xyes], [ + ax_cv_curses_enhanced=yes + ax_cv_curses_color=yes + ax_cv_curses_obsolete=yes + AC_DEFINE([HAVE_CURSES_ENHANCED], [1], [Define to 1 if library supports X/Open Enhanced functions]) + AC_DEFINE([HAVE_CURSES_COLOR], [1], [Define to 1 if library supports color (enhanced functions)]) + AC_DEFINE([HAVE_CURSES_OBSOLETE], [1], [Define to 1 if library supports certain obsolete features]) + AC_DEFINE([HAVE_NCURSESW_CURSES_H], [1], [Define to 1 if is present]) + ]) + + AC_CACHE_CHECK([for working ncursesw.h], [ax_cv_header_ncursesw_h], [ + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ + @%:@define _XOPEN_SOURCE_EXTENDED 1 + @%:@include + ]], [[ + chtype a = A_BOLD; + int b = KEY_LEFT; + chtype c = COLOR_PAIR(1) & A_COLOR; + attr_t d = WA_NORMAL; + cchar_t e; + wint_t f; + int g = getattrs(stdscr); + int h = getcurx(stdscr) + getmaxx(stdscr); + initscr(); + init_pair(1, COLOR_WHITE, COLOR_RED); + wattr_set(stdscr, d, 0, NULL); + wget_wch(stdscr, &f); + ]])], + [ax_cv_header_ncursesw_h=yes], + [ax_cv_header_ncursesw_h=no]) + ]) + AS_IF([test "x$ax_cv_header_ncursesw_h" = xyes], [ + ax_cv_curses_enhanced=yes + ax_cv_curses_color=yes + ax_cv_curses_obsolete=yes + AC_DEFINE([HAVE_CURSES_ENHANCED], [1], [Define to 1 if library supports X/Open Enhanced functions]) + AC_DEFINE([HAVE_CURSES_COLOR], [1], [Define to 1 if library supports color (enhanced functions)]) + AC_DEFINE([HAVE_CURSES_OBSOLETE], [1], [Define to 1 if library supports certain obsolete features]) + AC_DEFINE([HAVE_NCURSESW_H], [1], [Define to 1 if is present]) + ]) + + AC_CACHE_CHECK([for working ncurses.h], [ax_cv_header_ncurses_h_with_ncursesw], [ + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ + @%:@define _XOPEN_SOURCE_EXTENDED 1 + @%:@include + ]], [[ + chtype a = A_BOLD; + int b = KEY_LEFT; + chtype c = COLOR_PAIR(1) & A_COLOR; + attr_t d = WA_NORMAL; + cchar_t e; + wint_t f; + int g = getattrs(stdscr); + int h = getcurx(stdscr) + getmaxx(stdscr); + initscr(); + init_pair(1, COLOR_WHITE, COLOR_RED); + wattr_set(stdscr, d, 0, NULL); + wget_wch(stdscr, &f); + ]])], + [ax_cv_header_ncurses_h_with_ncursesw=yes], + [ax_cv_header_ncurses_h_with_ncursesw=no]) + ]) + AS_IF([test "x$ax_cv_header_ncurses_h_with_ncursesw" = xyes], [ + ax_cv_curses_enhanced=yes + ax_cv_curses_color=yes + ax_cv_curses_obsolete=yes + AC_DEFINE([HAVE_CURSES_ENHANCED], [1], [Define to 1 if library supports X/Open Enhanced functions]) + AC_DEFINE([HAVE_CURSES_COLOR], [1], [Define to 1 if library supports color (enhanced functions)]) + AC_DEFINE([HAVE_CURSES_OBSOLETE], [1], [Define to 1 if library supports certain obsolete features]) + AC_DEFINE([HAVE_NCURSES_H], [1], [Define to 1 if is present]) + ]) + + AS_IF([test "x$ax_cv_header_ncursesw_curses_h" = xno && test "x$ax_cv_header_ncursesw_h" = xno && test "x$ax_cv_header_ncurses_h_with_ncursesw" = xno], [ + AC_MSG_WARN([could not find a working ncursesw/curses.h, ncursesw.h or ncurses.h]) + ]) + ]) + ]) + + # Test for Ncurses + + AS_IF([test "x$CURSES_LIB" = x && test "x$with_ncurses" != xno && test "x$ax_cv_curses_which" = xno], [ + LIBS="$ax_saved_LIBS -lncurses" + + AC_CACHE_CHECK([for Ncurses library], [ax_cv_ncurses], [ + AC_LINK_IFELSE([AC_LANG_CALL([], [initscr])], + [ax_cv_ncurses=yes], [ax_cv_ncurses=no]) + ]) + AS_IF([test "x$ax_cv_ncurses" = xno && test "x$with_ncurses" = xyes], [ + AC_MSG_ERROR([--with-ncurses specified but could not find Ncurses library]) + ]) + + AS_IF([test "x$ax_cv_ncurses" = xyes], [ + ax_cv_curses=yes + ax_cv_curses_which=ncurses + CURSES_LIB="-lncurses" + AC_DEFINE([HAVE_NCURSES], [1], [Define to 1 if the Ncurses library is present]) + AC_DEFINE([HAVE_CURSES], [1], [Define to 1 if a SysV or X/Open compatible Curses library is present]) + + AC_CACHE_CHECK([for working ncurses/curses.h], [ax_cv_header_ncurses_curses_h], [ + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ + @%:@include + ]], [[ + chtype a = A_BOLD; + int b = KEY_LEFT; + chtype c = COLOR_PAIR(1) & A_COLOR; + int g = getattrs(stdscr); + int h = getcurx(stdscr) + getmaxx(stdscr); + initscr(); + init_pair(1, COLOR_WHITE, COLOR_RED); + ]])], + [ax_cv_header_ncurses_curses_h=yes], + [ax_cv_header_ncurses_curses_h=no]) + ]) + AS_IF([test "x$ax_cv_header_ncurses_curses_h" = xyes], [ + ax_cv_curses_color=yes + ax_cv_curses_obsolete=yes + AC_DEFINE([HAVE_CURSES_COLOR], [1], [Define to 1 if library supports color (enhanced functions)]) + AC_DEFINE([HAVE_CURSES_OBSOLETE], [1], [Define to 1 if library supports certain obsolete features]) + AC_DEFINE([HAVE_NCURSES_CURSES_H], [1], [Define to 1 if is present]) + ]) + + AC_CACHE_CHECK([for working ncurses.h], [ax_cv_header_ncurses_h], [ + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ + @%:@include + ]], [[ + chtype a = A_BOLD; + int b = KEY_LEFT; + chtype c = COLOR_PAIR(1) & A_COLOR; + int g = getattrs(stdscr); + int h = getcurx(stdscr) + getmaxx(stdscr); + initscr(); + init_pair(1, COLOR_WHITE, COLOR_RED); + ]])], + [ax_cv_header_ncurses_h=yes], + [ax_cv_header_ncurses_h=no]) + ]) + AS_IF([test "x$ax_cv_header_ncurses_h" = xyes], [ + ax_cv_curses_color=yes + ax_cv_curses_obsolete=yes + AC_DEFINE([HAVE_CURSES_COLOR], [1], [Define to 1 if library supports color (enhanced functions)]) + AC_DEFINE([HAVE_CURSES_OBSOLETE], [1], [Define to 1 if library supports certain obsolete features]) + AC_DEFINE([HAVE_NCURSES_H], [1], [Define to 1 if is present]) + ]) + + AS_IF([test "x$ax_cv_header_ncurses_curses_h" = xno && test "x$ax_cv_header_ncurses_h" = xno], [ + AC_MSG_WARN([could not find a working ncurses/curses.h or ncurses.h]) + ]) + ]) + ]) + + # Test for plain Curses (or if CURSES_LIB was set by user) + + AS_IF([test "x$with_plaincurses" != xno && test "x$ax_cv_curses_which" = xno], [ + AS_IF([test "x$CURSES_LIB" != x], [ + LIBS="$ax_saved_LIBS $CURSES_LIB" + ], [ + LIBS="$ax_saved_LIBS -lcurses" + ]) + + AC_CACHE_CHECK([for Curses library], [ax_cv_plaincurses], [ + AC_LINK_IFELSE([AC_LANG_CALL([], [initscr])], + [ax_cv_plaincurses=yes], [ax_cv_plaincurses=no]) + ]) + + AS_IF([test "x$ax_cv_plaincurses" = xyes], [ + ax_cv_curses=yes + ax_cv_curses_which=plaincurses + AS_IF([test "x$CURSES_LIB" = x], [ + CURSES_LIB="-lcurses" + ]) + AC_DEFINE([HAVE_CURSES], [1], [Define to 1 if a SysV or X/Open compatible Curses library is present]) + + # Check for base conformance (and header file) + + AC_CACHE_CHECK([for working curses.h], [ax_cv_header_curses_h], [ + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ + @%:@include + ]], [[ + chtype a = A_BOLD; + int b = KEY_LEFT; + initscr(); + ]])], + [ax_cv_header_curses_h=yes], + [ax_cv_header_curses_h=no]) + ]) + AS_IF([test "x$ax_cv_header_curses_h" = xyes], [ + AC_DEFINE([HAVE_CURSES_H], [1], [Define to 1 if is present]) + + # Check for X/Open Enhanced conformance + + AC_CACHE_CHECK([for X/Open Enhanced Curses conformance], [ax_cv_plaincurses_enhanced], [ + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ + @%:@define _XOPEN_SOURCE_EXTENDED 1 + @%:@include + @%:@ifndef _XOPEN_CURSES + @%:@error "this Curses library is not enhanced" + "this Curses library is not enhanced" + @%:@endif + ]], [[ + chtype a = A_BOLD; + int b = KEY_LEFT; + chtype c = COLOR_PAIR(1) & A_COLOR; + attr_t d = WA_NORMAL; + cchar_t e; + wint_t f; + initscr(); + init_pair(1, COLOR_WHITE, COLOR_RED); + wattr_set(stdscr, d, 0, NULL); + wget_wch(stdscr, &f); + ]])], + [ax_cv_plaincurses_enhanced=yes], + [ax_cv_plaincurses_enhanced=no]) + ]) + AS_IF([test "x$ax_cv_plaincurses_enhanced" = xyes], [ + ax_cv_curses_enhanced=yes + ax_cv_curses_color=yes + AC_DEFINE([HAVE_CURSES_ENHANCED], [1], [Define to 1 if library supports X/Open Enhanced functions]) + AC_DEFINE([HAVE_CURSES_COLOR], [1], [Define to 1 if library supports color (enhanced functions)]) + ]) + + # Check for color functions + + AC_CACHE_CHECK([for Curses color functions], [ax_cv_plaincurses_color], [ + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ + @%:@define _XOPEN_SOURCE_EXTENDED 1 + @%:@include + ]], [[ + chtype a = A_BOLD; + int b = KEY_LEFT; + chtype c = COLOR_PAIR(1) & A_COLOR; + initscr(); + init_pair(1, COLOR_WHITE, COLOR_RED); + ]])], + [ax_cv_plaincurses_color=yes], + [ax_cv_plaincurses_color=no]) + ]) + AS_IF([test "x$ax_cv_plaincurses_color" = xyes], [ + ax_cv_curses_color=yes + AC_DEFINE([HAVE_CURSES_COLOR], [1], [Define to 1 if library supports color (enhanced functions)]) + ]) + + # Check for obsolete functions + + AC_CACHE_CHECK([for obsolete Curses functions], [ax_cv_plaincurses_obsolete], [ + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ + @%:@include + ]], [[ + chtype a = A_BOLD; + int b = KEY_LEFT; + int g = getattrs(stdscr); + int h = getcurx(stdscr) + getmaxx(stdscr); + initscr(); + ]])], + [ax_cv_plaincurses_obsolete=yes], + [ax_cv_plaincurses_obsolete=no]) + ]) + AS_IF([test "x$ax_cv_plaincurses_obsolete" = xyes], [ + ax_cv_curses_obsolete=yes + AC_DEFINE([HAVE_CURSES_OBSOLETE], [1], [Define to 1 if library supports certain obsolete features]) + ]) + ]) + + AS_IF([test "x$ax_cv_header_curses_h" = xno], [ + AC_MSG_WARN([could not find a working curses.h]) + ]) + ]) + ]) + + AS_IF([test "x$ax_cv_curses" != xyes], [ax_cv_curses=no]) + AS_IF([test "x$ax_cv_curses_enhanced" != xyes], [ax_cv_curses_enhanced=no]) + AS_IF([test "x$ax_cv_curses_color" != xyes], [ax_cv_curses_color=no]) + AS_IF([test "x$ax_cv_curses_obsolete" != xyes], [ax_cv_curses_obsolete=no]) + + LIBS=$ax_saved_LIBS +])dnl diff --git a/m4/libcurl.m4 b/m4/libcurl.m4 new file mode 100644 index 0000000..02d00a7 --- /dev/null +++ b/m4/libcurl.m4 @@ -0,0 +1,301 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) 2006 - 2020, David Shaw +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +########################################################################### +# LIBCURL_CHECK_CONFIG ([DEFAULT-ACTION], [MINIMUM-VERSION], +# [ACTION-IF-YES], [ACTION-IF-NO]) +# ---------------------------------------------------------- +# David Shaw May-09-2006 +# +# Checks for libcurl. DEFAULT-ACTION is the string yes or no to +# specify whether to default to --with-libcurl or --without-libcurl. +# If not supplied, DEFAULT-ACTION is yes. MINIMUM-VERSION is the +# minimum version of libcurl to accept. Pass the version as a regular +# version number like 7.10.1. If not supplied, any version is +# accepted. ACTION-IF-YES is a list of shell commands to run if +# libcurl was successfully found and passed the various tests. +# ACTION-IF-NO is a list of shell commands that are run otherwise. +# Note that using --without-libcurl does run ACTION-IF-NO. +# +# This macro #defines HAVE_LIBCURL if a working libcurl setup is +# found, and sets @LIBCURL@ and @LIBCURL_CPPFLAGS@ to the necessary +# values. Other useful defines are LIBCURL_FEATURE_xxx where xxx are +# the various features supported by libcurl, and LIBCURL_PROTOCOL_yyy +# where yyy are the various protocols supported by libcurl. Both xxx +# and yyy are capitalized. See the list of AH_TEMPLATEs at the top of +# the macro for the complete list of possible defines. Shell +# variables $libcurl_feature_xxx and $libcurl_protocol_yyy are also +# defined to 'yes' for those features and protocols that were found. +# Note that xxx and yyy keep the same capitalization as in the +# curl-config list (e.g. it's "HTTP" and not "http"). +# +# Users may override the detected values by doing something like: +# LIBCURL="-lcurl" LIBCURL_CPPFLAGS="-I/usr/myinclude" ./configure +# +# For the sake of sanity, this macro assumes that any libcurl that is +# found is after version 7.7.2, the first version that included the +# curl-config script. Note that it is very important for people +# packaging binary versions of libcurl to include this script! +# Without curl-config, we can only guess what protocols are available, +# or use curl_version_info to figure it out at runtime. + +AC_DEFUN([LIBCURL_CHECK_CONFIG], +[ + AH_TEMPLATE([LIBCURL_FEATURE_SSL],[Defined if libcurl supports SSL]) + AH_TEMPLATE([LIBCURL_FEATURE_KRB4],[Defined if libcurl supports KRB4]) + AH_TEMPLATE([LIBCURL_FEATURE_IPV6],[Defined if libcurl supports IPv6]) + AH_TEMPLATE([LIBCURL_FEATURE_LIBZ],[Defined if libcurl supports libz]) + AH_TEMPLATE([LIBCURL_FEATURE_ASYNCHDNS],[Defined if libcurl supports AsynchDNS]) + AH_TEMPLATE([LIBCURL_FEATURE_IDN],[Defined if libcurl supports IDN]) + AH_TEMPLATE([LIBCURL_FEATURE_SSPI],[Defined if libcurl supports SSPI]) + AH_TEMPLATE([LIBCURL_FEATURE_NTLM],[Defined if libcurl supports NTLM]) + + AH_TEMPLATE([LIBCURL_PROTOCOL_HTTP],[Defined if libcurl supports HTTP]) + AH_TEMPLATE([LIBCURL_PROTOCOL_HTTPS],[Defined if libcurl supports HTTPS]) + AH_TEMPLATE([LIBCURL_PROTOCOL_FTP],[Defined if libcurl supports FTP]) + AH_TEMPLATE([LIBCURL_PROTOCOL_FTPS],[Defined if libcurl supports FTPS]) + AH_TEMPLATE([LIBCURL_PROTOCOL_FILE],[Defined if libcurl supports FILE]) + AH_TEMPLATE([LIBCURL_PROTOCOL_TELNET],[Defined if libcurl supports TELNET]) + AH_TEMPLATE([LIBCURL_PROTOCOL_LDAP],[Defined if libcurl supports LDAP]) + AH_TEMPLATE([LIBCURL_PROTOCOL_DICT],[Defined if libcurl supports DICT]) + AH_TEMPLATE([LIBCURL_PROTOCOL_TFTP],[Defined if libcurl supports TFTP]) + AH_TEMPLATE([LIBCURL_PROTOCOL_RTSP],[Defined if libcurl supports RTSP]) + AH_TEMPLATE([LIBCURL_PROTOCOL_POP3],[Defined if libcurl supports POP3]) + AH_TEMPLATE([LIBCURL_PROTOCOL_IMAP],[Defined if libcurl supports IMAP]) + AH_TEMPLATE([LIBCURL_PROTOCOL_SMTP],[Defined if libcurl supports SMTP]) + + AC_ARG_WITH(libcurl, + AS_HELP_STRING([--with-libcurl=PREFIX],[look for the curl library in PREFIX/lib and headers in PREFIX/include]), + [_libcurl_with=$withval],[_libcurl_with=ifelse([$1],,[yes],[$1])]) + + if test "$_libcurl_with" != "no" ; then + + AC_PROG_AWK + + _libcurl_version_parse="eval $AWK '{split(\$NF,A,\".\"); X=256*256*A[[1]]+256*A[[2]]+A[[3]]; print X;}'" + + _libcurl_try_link=yes + + if test -d "$_libcurl_with" ; then + LIBCURL_CPPFLAGS="-I$withval/include" + _libcurl_ldflags="-L$withval/lib" + AC_PATH_PROG([_libcurl_config],[curl-config],[], + ["$withval/bin"]) + else + AC_PATH_PROG([_libcurl_config],[curl-config],[],[$PATH]) + fi + + if test x$_libcurl_config != "x" ; then + AC_CACHE_CHECK([for the version of libcurl], + [libcurl_cv_lib_curl_version], + [libcurl_cv_lib_curl_version=`$_libcurl_config --version | $AWK '{print $[]2}'`]) + + _libcurl_version=`echo $libcurl_cv_lib_curl_version | $_libcurl_version_parse` + _libcurl_wanted=`echo ifelse([$2],,[0],[$2]) | $_libcurl_version_parse` + + if test $_libcurl_wanted -gt 0 ; then + AC_CACHE_CHECK([for libcurl >= version $2], + [libcurl_cv_lib_version_ok], + [ + if test $_libcurl_version -ge $_libcurl_wanted ; then + libcurl_cv_lib_version_ok=yes + else + libcurl_cv_lib_version_ok=no + fi + ]) + fi + + if test $_libcurl_wanted -eq 0 || test x$libcurl_cv_lib_version_ok = xyes ; then + if test x"$LIBCURL_CPPFLAGS" = "x" ; then + LIBCURL_CPPFLAGS=`$_libcurl_config --cflags` + fi + if test x"$LIBCURL" = "x" ; then + if $5; then + case "$host_os" in + darwin*) + LIBCURL=`$_libcurl_config --libs` + ;; + *) + LIBCURL=`$_libcurl_config --static-libs` + ;; + esac + else + LIBCURL=`$_libcurl_config --libs` + fi + + # This is so silly, but Apple actually has a bug in their + # curl-config script. Fixed in Tiger, but there are still + # lots of Panther installs around. + case "${host}" in + powerpc-apple-darwin7*) + LIBCURL=`echo $LIBCURL | sed -e 's|-arch i386||g'` + ;; + esac + fi + + dnl If we are on OS X and we haven't picked up libcurl static or + dnl otherwise, then let's just go ahead and use the one present on + dnl the system. Since this compiled binary will only run on OS X + dnl which almost always has cURL installed, it's OK to add the + dnl static dependency. + AS_IF([test "x${LIBCURL}" = "x"], + [AS_CASE(["$host_os"], + [darwin*], + [AS_IF([test "x$_libcurl_config" != "x"], + AS_VAR_SET(LIBCURL, $($_libcurl_config --libs)), + [] + )], + [] + )], + [] + ) + + # All curl-config scripts support --feature + _libcurl_features=`$_libcurl_config --feature` + + # Is it modern enough to have --protocols? (7.12.4) + if test $_libcurl_version -ge 461828 ; then + _libcurl_protocols=`$_libcurl_config --protocols` + fi + else + _libcurl_try_link=no + fi + + unset _libcurl_wanted + fi + + if test $_libcurl_try_link = yes ; then + + # we didn't find curl-config, so let's see if the user-supplied + # link line (or failing that, "-lcurl") is enough. + # LIBCURL=${LIBCURL-"$_libcurl_ldflags -lcurl"} + + AC_CACHE_CHECK([whether libcurl is usable], + [libcurl_cv_lib_curl_usable], + [ + _libcurl_save_cppflags=$CPPFLAGS + CPPFLAGS="$LIBCURL_CPPFLAGS $CPPFLAGS" + _libcurl_save_libs=$LIBS + LIBS="$_libcurl_ldflags $LIBCURL $LIBS" + + AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include ]],[[ +/* Try and use a few common options to force a failure if we are + missing symbols or can't link. */ +int x; +curl_easy_setopt(NULL,CURLOPT_URL,NULL); +x=CURL_ERROR_SIZE; +x=CURLOPT_WRITEFUNCTION; +x=CURLOPT_WRITEDATA; +x=CURLOPT_ERRORBUFFER; +x=CURLOPT_STDERR; +x=CURLOPT_VERBOSE; +if (x) {;} +]])],libcurl_cv_lib_curl_usable=yes,libcurl_cv_lib_curl_usable=no) + + CPPFLAGS=$_libcurl_save_cppflags + LIBS=$_libcurl_save_libs + unset _libcurl_save_cppflags + unset _libcurl_save_libs + ]) + + if test $libcurl_cv_lib_curl_usable = yes ; then + + # Does curl_free() exist in this version of libcurl? + # If not, fake it with free() + + _libcurl_save_cppflags=$CPPFLAGS + CPPFLAGS="$CPPFLAGS $LIBCURL_CPPFLAGS" + _libcurl_save_libs=$LIBS + LIBS="$LIBS $LIBCURL" + LDFLAGS="$_libcurl_ldflags $LDFLAGS" + + AC_CHECK_FUNC(curl_free,, + AC_DEFINE(curl_free,free, + [Define curl_free() as free() if our version of curl lacks curl_free.])) + + CPPFLAGS=$_libcurl_save_cppflags + LIBS=$_libcurl_save_libs + unset _libcurl_save_cppflags + unset _libcurl_save_libs + + AC_DEFINE(HAVE_LIBCURL,1, + [Define to 1 if you have a functional curl library.]) + AC_SUBST(LIBCURL_CPPFLAGS) + AC_SUBST(LIBCURL) + + for _libcurl_feature in $_libcurl_features ; do + AC_DEFINE_UNQUOTED(AS_TR_CPP(libcurl_feature_$_libcurl_feature),[1]) + eval AS_TR_SH(libcurl_feature_$_libcurl_feature)=yes + done + + if test "x$_libcurl_protocols" = "x" ; then + + # We don't have --protocols, so just assume that all + # protocols are available + _libcurl_protocols="HTTP FTP FILE TELNET LDAP DICT TFTP" + + if test x$libcurl_feature_SSL = xyes ; then + _libcurl_protocols="$_libcurl_protocols HTTPS" + + # FTPS wasn't standards-compliant until version + # 7.11.0 (0x070b00 == 461568) + if test $_libcurl_version -ge 461568; then + _libcurl_protocols="$_libcurl_protocols FTPS" + fi + fi + + # RTSP, IMAP, POP3 and SMTP were added in + # 7.20.0 (0x071400 == 463872) + if test $_libcurl_version -ge 463872; then + _libcurl_protocols="$_libcurl_protocols RTSP IMAP POP3 SMTP" + fi + fi + + for _libcurl_protocol in $_libcurl_protocols ; do + AC_DEFINE_UNQUOTED(AS_TR_CPP(libcurl_protocol_$_libcurl_protocol),[1]) + eval AS_TR_SH(libcurl_protocol_$_libcurl_protocol)=yes + done + else + unset LIBCURL + unset LIBCURL_CPPFLAGS + fi + fi + + unset _libcurl_try_link + unset _libcurl_version_parse + unset _libcurl_config + unset _libcurl_feature + unset _libcurl_features + unset _libcurl_protocol + unset _libcurl_protocols + unset _libcurl_version + unset _libcurl_ldflags + fi + + if test x$_libcurl_with = xno || test x$libcurl_cv_lib_curl_usable != xyes ; then + # This is the IF-NO path + ifelse([$4],,:,[$4]) + else + # This is the IF-YES path + ifelse([$3],,:,[$3]) + fi + + unset _libcurl_with +])dnl diff --git a/m4/lnav_common.m4 b/m4/lnav_common.m4 new file mode 100644 index 0000000..1619afe --- /dev/null +++ b/m4/lnav_common.m4 @@ -0,0 +1,22 @@ +AC_DEFUN([LNAV_ADDTO], [ + if test "x$$1" = "x"; then + test "x$verbose" = "xyes" && echo " setting $1 to \"$2\"" + $1="$2" + else + ats_addto_bugger="$2" + for i in $ats_addto_bugger; do + ats_addto_duplicate="0" + for j in $$1; do + if test "x$i" = "x$j"; then + ats_addto_duplicate="1" + break + fi + done + if test $ats_addto_duplicate = "0"; then + test "x$verbose" = "xyes" && echo " adding \"$i\" to $1" + $1="$$1 $i" + fi + done + fi +])dnl + diff --git a/m4/lnav_with_jemalloc.m4 b/m4/lnav_with_jemalloc.m4 new file mode 100644 index 0000000..99103eb --- /dev/null +++ b/m4/lnav_with_jemalloc.m4 @@ -0,0 +1,81 @@ +dnl -------------------------------------------------------- -*- autoconf -*- +dnl Licensed to the Apache Software Foundation (ASF) under one or more +dnl contributor license agreements. See the NOTICE file distributed with +dnl this work for additional information regarding copyright ownership. +dnl The ASF licenses this file to You under the Apache License, Version 2.0 +dnl (the "License"); you may not use this file except in compliance with +dnl the License. You may obtain a copy of the License at +dnl +dnl http://www.apache.org/licenses/LICENSE-2.0 +dnl +dnl Unless required by applicable law or agreed to in writing, software +dnl distributed under the License is distributed on an "AS IS" BASIS, +dnl WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +dnl See the License for the specific language governing permissions and +dnl limitations under the License. + +dnl +dnl lnav_with_jemalloc.m4: lnav's jemalloc autoconf macros +dnl +dnl +AC_DEFUN([LNAV_WITH_JEMALLOC], [ +enable_jemalloc=no +AC_ARG_WITH([jemalloc], + [AS_HELP_STRING([--with-jemalloc=DIR],[use a specific jemalloc library])], +[ + if test "$withval" != "no"; then + enable_jemalloc=yes + modify_env_variables=no + case "$withval" in + yes) + AC_MSG_NOTICE([Checking for jemalloc libs]) + ;; + *) + jemalloc_include="$withval/include" + jemalloc_ldflags="$withval/lib" + modify_env_variables="yes" + AC_MSG_NOTICE([Checking for jemalloc libs in $withval]) + ;; + esac + fi +]) + +jemalloch=0 +if test "$enable_jemalloc" != "no"; then + saved_ldflags=$LDFLAGS + saved_cppflags=$CPPFLAGS + saved_libtool_link_flags=$LIBTOOL_LINK_FLAGS + jemalloc_have_headers=0 + jemalloc_have_libs=0 + + if test "$modify_env_variables" != "no"; then + LNAV_ADDTO(CPPFLAGS, [-I${jemalloc_include}]) + LNAV_ADDTO(LDFLAGS, [-L${jemalloc_ldflags}]) + LNAV_ADDTO(LIBTOOL_LINK_FLAGS, [-R${jemalloc_ldflags}]) + fi + # On Darwin, jemalloc symbols are prefixed with je_. Search for that first, + # then fall back to unadorned symbols. + AC_SEARCH_LIBS([je_malloc_stats_print], [jemalloc], [jemalloc_have_libs=1], + [AC_SEARCH_LIBS([malloc_stats_print], [jemalloc], [jemalloc_have_libs=1])] + ) + if test "$jemalloc_have_libs" != "0"; then + AC_MSG_NOTICE([Checking for jemalloc includes]) + AC_CHECK_HEADERS(jemalloc/jemalloc.h, [jemalloc_have_headers=1]) + if test "$jemalloc_have_headers" != "0"; then + jemalloch=1 + LNAV_ADDTO(LIBS, [-ljemalloc]) + else + AC_MSG_WARN([jemalloc headers not found]) + fi + else + AC_MSG_WARN([jemalloc libs not found]) + fi + if test "$jemalloc_have_libs" = "0"; then + CPPFLAGS=$saved_cppflags + LDFLAGS=$saved_ldflags + LIBTOOL_LINK_FLAGS=$saved_libtool_link_flags + AC_MSG_WARN([jemalloc not found]) + fi +fi +AC_SUBST(jemalloch) +])dnl diff --git a/m4/lnav_with_libarchive.m4 b/m4/lnav_with_libarchive.m4 new file mode 100644 index 0000000..a6fa4fd --- /dev/null +++ b/m4/lnav_with_libarchive.m4 @@ -0,0 +1,77 @@ +dnl +dnl Copyright (c) 2020, Timothy Stack +dnl +dnl All rights reserved. +dnl +dnl Redistribution and use in source and binary forms, with or without +dnl modification, are permitted provided that the following conditions are met: +dnl +dnl dnl Redistributions of source code must retain the above copyright notice, this +dnl list of conditions and the following disclaimer. +dnl dnl Redistributions in binary form must reproduce the above copyright notice, +dnl this list of conditions and the following disclaimer in the documentation +dnl and/or other materials provided with the distribution. +dnl dnl Neither the name of Timothy Stack nor the names of its contributors +dnl may be used to endorse or promote products derived from this software +dnl without specific prior written permission. +dnl +dnl THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY +dnl EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +dnl WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +dnl DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY +dnl DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +dnl (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +dnl LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +dnl ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +dnl (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +dnl SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +dnl +dnl @file lnav_with_libarchive.m4 +dnl +AC_DEFUN([AX_PATH_LIB_ARCHIVE],[dnl +AC_MSG_CHECKING([lib archive]) +AC_ARG_WITH(libarchive, +[ --with-libarchive[[=prefix]]],, + with_libarchive="yes") +if test ".$with_libarchive" = ".no" ; then + AC_MSG_RESULT([disabled]) + m4_ifval($2,$2) +else + AC_MSG_RESULT([(testing)]) + AC_CHECK_LIB(archive, archive_read_new) + AC_CHECK_HEADERS(archive.h) + if test "$ac_cv_lib_archive_archive_read_new" = "yes" && \ + test "x$ac_cv_header_archive_h" = xyes; then + LIBARCHIVE_LIBS="-larchive" + AC_MSG_CHECKING([lib archive]) + AC_MSG_RESULT([$LIBARCHIVE_LIBS]) + m4_ifval($1,$1) + else + unset ac_cv_header_archive_h + OLDLDFLAGS="$LDFLAGS" ; LDFLAGS="$LDFLAGS -L$with_libarchive/lib" + OLDCPPFLAGS="$CPPFLAGS" ; CPPFLAGS="$CPPFLAGS -I$with_libarchive/include" + AC_CHECK_LIB(archive, archive_read_new) + AC_CHECK_HEADERS(archive.h) + CPPFLAGS="$OLDCPPFLAGS" + LDFLAGS="$OLDLDFLAGS" + if test "$ac_cv_lib_archive_archive_read_new" = "yes" && \ + test "x$ac_cv_header_archive_h" = xyes; then + AC_MSG_RESULT(.setting LIBARCHIVE_LIBS -L$with_libarchive/lib -larchive) + LIBARCHIVE_LDFLAGS="-L$with_libarchive/lib" + LIBARCHIVE_LIBS="-larchive" + test -d "$with_libarchive/include" && LIBARCHIVE_CFLAGS="-I$with_libarchive/include" + AC_MSG_CHECKING([lib archive]) + AC_MSG_RESULT([$LIBARCHIVE_LIBS]) + m4_ifval($1,$1) + else + AC_MSG_CHECKING([lib archive]) + AC_MSG_RESULT([[no]]) + m4_ifval($2,$2) + fi + fi +fi +AC_SUBST([LIBARCHIVE_LIBS]) +AC_SUBST([LIBARCHIVE_LDFLAGS]) +AC_SUBST([LIBARCHIVE_CFLAGS]) +]) + diff --git a/m4/lnav_with_readline.m4 b/m4/lnav_with_readline.m4 new file mode 100644 index 0000000..217391c --- /dev/null +++ b/m4/lnav_with_readline.m4 @@ -0,0 +1,95 @@ +dnl +dnl Copyright (c) 2007-2015, Timothy Stack +dnl Copyright (c) 2015, Suresh Sundriyal +dnl +dnl All rights reserved. +dnl +dnl Redistribution and use in source and binary forms, with or without +dnl modification, are permitted provided that the following conditions are met: +dnl +dnl dnl Redistributions of source code must retain the above copyright notice, this +dnl list of conditions and the following disclaimer. +dnl dnl Redistributions in binary form must reproduce the above copyright notice, +dnl this list of conditions and the following disclaimer in the documentation +dnl and/or other materials provided with the distribution. +dnl dnl Neither the name of Timothy Stack nor the names of its contributors +dnl may be used to endorse or promote products derived from this software +dnl without specific prior written permission. +dnl +dnl THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY +dnl EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +dnl WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +dnl DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY +dnl DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +dnl (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +dnl LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +dnl ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +dnl (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +dnl SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +dnl +dnl @file lnav_with_readline.m4 +dnl +AC_DEFUN([AX_PATH_LIB_READLINE], + [dnl + AC_REQUIRE([AX_WITH_CURSES]) + AC_MSG_CHECKING([lib readline]) + AC_ARG_WITH([readline], + AS_HELP_STRING([--with-readline@<:@=prefix@:>@],dnl + [compile xmlreadline part (via libreadline check)]), + [], + [with_readline="yes"] + )dnl + + AS_VAR_SET(saved_CFLAGS, $CFLAGS) + AS_VAR_SET(saved_CPPFLAGS, $CPPFLAGS) + AS_VAR_SET(saved_LDFLAGS, $LDFLAGS) + AS_VAR_SET(saved_LIBS, $LIBS) + AS_CASE(["$with_readline"], + [no], + AC_MSG_ERROR([readline required to build]), + [yes], + [], + [dnl + AS_VAR_SET([READLINE_CFLAGS], ["-I$with_readline/include"]) + AS_VAR_SET([READLINE_LDFLAGS], ["-L$with_readline/lib"]) + LNAV_ADDTO(CFLAGS, [-I$with_readline/include]) + LNAV_ADDTO(CPPFLAGS, [-I$with_readline/include]) + dnl We want the provided path to be the first in the search order. + LDFLAGS="-L$with_readline/lib $LDFLAGS" + ]dnl + ) + + AC_SEARCH_LIBS([readline], [readline], + [AS_VAR_SET([READLINE_LIBS], ["-lreadline"])], + [AC_MSG_ERROR([libreadline library not found])], + [$CURSES_LIB]dnl + )dnl + + dnl Ensure that the readline library has the required symbols. + dnl i.e. We haven't picked up editline. + AC_SEARCH_LIBS([history_set_history_state], [readline], + [], + AC_MSG_ERROR([libreadline does not have the required symbols. editline possibly masquerading as readline.]), + [$CURSES_LIB]dnl + ) + + AC_CHECK_HEADERS([readline.h readline/readline.h], + [dnl + AS_VAR_SET([HAVE_READLINE_HEADERS], [1]) + break + ]dnl + ) + + AS_VAR_SET_IF([HAVE_READLINE_HEADERS], [], + [AC_MSG_ERROR([readline headers not found])] + ) + AS_VAR_SET(CFLAGS, $saved_CFLAGS) + AS_VAR_SET(CPPFLAGS, $saved_CPPFLAGS) + AS_VAR_SET(LDFLAGS, $saved_LDFLAGS) + AS_VAR_SET(LIBS, $saved_LIBS) + + AC_SUBST([READLINE_LIBS]) + AC_SUBST([READLINE_CFLAGS]) + AC_SUBST([READLINE_LDFLAGS]) + ]dnl +)dnl diff --git a/m4/lnav_with_sqlite3.m4 b/m4/lnav_with_sqlite3.m4 new file mode 100644 index 0000000..6eaa9fa --- /dev/null +++ b/m4/lnav_with_sqlite3.m4 @@ -0,0 +1,127 @@ +dnl +dnl @Modified from: ax_sqlite3.m4 +dnl @author Mateusz Loskot +dnl @version $Date: 2006/08/30 14:28:55 $ +dnl @license AllPermissive +dnl +dnl Copyright (c) 2015, Suresh Sundriyal +dnl +dnl @file lnav_with_sqlite3.m4 +dnl +AC_DEFUN([LNAV_WITH_SQLITE3], +[dnl + AC_ARG_WITH([sqlite3], + AS_HELP_STRING([--with-sqlite3=@<:@prefix@:>@],[compile with sqlite3]), + [], + [with_sqlite3="yes"] + ) + + AS_VAR_SET(saved_CFLAGS, $CFLAGS) + AS_VAR_SET(saved_CPPFLAGS, $CPPFLAGS) + AS_VAR_SET(saved_LDFLAGS, $LDFLAGS) + AS_VAR_SET(saved_LIBS, $LIBS) + + AS_CASE(["$with_sqlite3"], + [no], + AC_MSG_ERROR([sqlite3 required to build]), + [yes], + [], + [dnl + LNAV_ADDTO(CFLAGS, [-I$with_sqlite3/include]) + LNAV_ADDTO(CPPFLAGS, [-I$with_sqlite3/include]) + AS_VAR_SET([SQLITE3_LDFLAGS], ["-L$with_sqlite3/lib"]) + AS_VAR_SET([SQLITE3_CFLAGS], ["-I$with_sqlite3/include"]) + LDFLAGS="-L$with_sqlite3/lib $LDFLAGS" + ] + ) + + AC_SEARCH_LIBS([sqlite3_open], [sqlite3], + AS_VAR_SET([SQLITE3_LIBS], ["-lsqlite3"]), + AC_MSG_ERROR([sqlite3 library not found]), + [-pthread]dnl + ) + + AC_CHECK_HEADERS([sqlite3.h], [], + AC_MSG_ERROR([sqlite3 headers not found]) + ) + + sqlite3_version_req=ifelse([$1], [], [3.0.0], [$1]) + sqlite3_version_req_shorten=`expr $sqlite3_version_req : '\([[0-9]]*\.[[0-9]]*\)'` + sqlite3_version_req_major=`expr $sqlite3_version_req : '\([[0-9]]*\)'` + sqlite3_version_req_minor=`expr $sqlite3_version_req : '[[0-9]]*\.\([[0-9]]*\)'` + sqlite3_version_req_micro=`expr $sqlite3_version_req : '[[0-9]]*\.[[0-9]]*\.\([[0-9]]*\)'` + if test "x$sqlite3_version_req_micro" = "x" ; then + sqlite3_version_req_micro="0" + fi + + sqlite3_version_req_number=`expr $sqlite3_version_req_major \* 1000000 \ + \+ $sqlite3_version_req_minor \* 1000 \ + \+ $sqlite3_version_req_micro` + + AC_MSG_CHECKING([for SQLite3 library >= $sqlite3_version_req]) + AC_LANG_PUSH(C++) + AC_COMPILE_IFELSE( + [ + AC_LANG_PROGRAM([[@%:@include ]], + [[ + #if (SQLITE_VERSION_NUMBER >= $sqlite3_version_req_number) + // Everything is okay + #else + # error SQLite version is too old + #endif + ]] + ) + ], + [ + AC_MSG_RESULT([yes]) + ], + [ + AC_MSG_RESULT([not found]) + AC_MSG_ERROR([SQLite3 version >= $sqlite3_version_req is required]) + ] + ) + AC_LANG_POP([C++]) + + AC_CHECK_FUNC(sqlite3_stmt_readonly, + AC_DEFINE([HAVE_SQLITE3_STMT_READONLY], [1], + [Have the sqlite3_stmt_readonly function] + ) + ) + + AC_CHECK_FUNC(sqlite3_value_subtype, + HAVE_SQLITE3_VALUE_SUBTYPE=1 + AC_DEFINE([HAVE_SQLITE3_VALUE_SUBTYPE], [1], + [Have the sqlite3_value_subtype function] + ) + ) + + AC_SUBST(HAVE_SQLITE3_VALUE_SUBTYPE) + + AC_CHECK_FUNC(sqlite3_error_offset, + HAVE_SQLITE3_ERROR_OFFSET=1 + AC_DEFINE([HAVE_SQLITE3_ERROR_OFFSET], [1], + [Have the sqlite3_error_offset function] + ) + ) + + AC_SUBST(HAVE_SQLITE3_ERROR_OFFSET) + + AC_CHECK_FUNC(sqlite3_drop_modules, + HAVE_SQLITE3_DROP_MODULES=1 + AC_DEFINE([HAVE_SQLITE3_DROP_MODULES], [1], + [Have the sqlite3_drop_modules function] + ) + ) + + AC_SUBST(HAVE_SQLITE3_DROP_MODULES) + + AS_VAR_SET(CFLAGS, $saved_CFLAGS) + AS_VAR_SET(CPPFLAGS, $saved_CPPFLAGS) + AS_VAR_SET(LDFLAGS, $saved_LDFLAGS) + AS_VAR_SET(LIBS, $saved_LIBS) + + AC_SUBST(SQLITE3_CFLAGS) + AC_SUBST(SQLITE3_LDFLAGS) + AC_SUBST(SQLITE3_LIBS) +] +) diff --git a/m4/lnav_with_yajl.m4 b/m4/lnav_with_yajl.m4 new file mode 100644 index 0000000..0f9d942 --- /dev/null +++ b/m4/lnav_with_yajl.m4 @@ -0,0 +1,87 @@ +dnl +dnl Copyright (c) 2015, Suresh Sundriyal +dnl +dnl All rights reserved. +dnl +dnl Redistribution and use in source and binary forms, with or without +dnl modification, are permitted provided that the following conditions are met: +dnl +dnl dnl Redistributions of source code must retain the above copyright notice, this +dnl list of conditions and the following disclaimer. +dnl dnl Redistributions in binary form must reproduce the above copyright notice, +dnl this list of conditions and the following disclaimer in the documentation +dnl and/or other materials provided with the distribution. +dnl dnl Neither the name of Timothy Stack nor the names of its contributors +dnl may be used to endorse or promote products derived from this software +dnl without specific prior written permission. +dnl +dnl THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY +dnl EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +dnl WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +dnl DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY +dnl DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +dnl (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +dnl LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +dnl ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +dnl (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +dnl SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +dnl +dnl @file lnav_with_yajl.m4 +AC_DEFUN([LNAV_WITH_LOCAL_YAJL], + [ + AC_ARG_WITH([yajl], + AS_HELP_STRING([--with-yajl=DIR],[use a local installed version of yajl]), + [ + AS_IF([test "$withval" != "no"], + [ + AS_CASE(["$withval"], + [yes], + [AC_MSG_NOTICE([Checking for yajl libs])], + [ + AS_VAR_SET([yajl_saved_ldflags], ["$LDFLAGS"]) + AS_VAR_SET([yajl_saved_cppflags], ["$CPPFLAGS"]) + AS_VAR_SET([yajl_saved_libtool_link_flags], + ["$LIBTOOL_LIBK_FLAGS"] + ) + + LNAV_ADDTO(CPPFLAGS, [-I$withval/include]) + LNAV_ADDTO(LDFLAGS, [-I$withval/lib]) + LNAV_ADDTO(LIBTOOL_LINK_FLAGS, [-R$withval/lib]) + + AC_MSG_NOTICE([Checking for yajl libs in $withval]) + ] + ) + + AC_SEARCH_LIBS([yajl_gen_alloc], + [yajl], + [ + AC_MSG_NOTICE([Checking for yajl headers]) + AC_CHECK_HEADERS([yajl/yajl_parse.h], + AS_VAR_SET([HAVE_LOCAL_YAJL], [1]), + AC_MSG_WARN([yajl not found on the local system]) + ) + ] + ) + ] + ) + ] + ) + + AS_VAR_SET_IF([HAVE_LOCAL_YAJL], + [], + [ + AC_MSG_NOTICE([compiling with the included version of yajl]) + AS_VAR_SET([HAVE_LOCAL_YAJL], [0]) + AS_VAR_SET_IF([yajl_saved_ldflags], + [ + AS_VAR_SET([CPPFLAGS], ["$yajl_saved_cppflags"]) + AS_VAR_SET([LDFLAGS], ["$yajl_saved_ldflags"]) + AS_VAR_SET([LIBTOOL_LIBK_FLAGS], ["$yajl_saved_libtool_link_flags"]) + ] + ) + ] + ) + + AC_SUBST(HAVE_LOCAL_YAJL) + ] +)dnl diff --git a/release/Makefile b/release/Makefile new file mode 100644 index 0000000..6f2c561 --- /dev/null +++ b/release/Makefile @@ -0,0 +1,102 @@ + +VERSION=0.11.1 + +VERSION_TAG=v$(VERSION) + +SRC_VERSION=master + +outbox: + mkdir -p $@ + +clean-outbox: outbox + rm -f outbox/* + +PACKAGE_URLS = \ + https://www.libarchive.org/downloads/libarchive-3.6.1.tar.gz \ + https://ftp.gnu.org/gnu/make/make-4.2.1.tar.gz \ + ftp://ftp.gnu.org/gnu/ncurses/ncurses-6.3.tar.gz \ + https://github.com/PCRE2Project/pcre2/releases/download/pcre2-10.40/pcre2-10.40.tar.gz \ + https://ftp.gnu.org/gnu/readline/readline-6.3.tar.gz \ + https://zlib.net/zlib-1.2.12.tar.gz \ + https://sourceware.org/pub/bzip2/bzip2-1.0.8.tar.gz \ + https://www.sqlite.org/2022/sqlite-autoconf-3390400.tar.gz \ + https://www.openssl.org/source/openssl-1.0.2n.tar.gz \ + https://www.libssh2.org/download/libssh2-1.9.0.tar.gz \ + https://curl.se/download/curl-7.85.0.tar.gz \ + https://tukaani.org/xz/xz-5.2.5.tar.gz + +.PHONY: linux freebsd pkger download-pkgs musl + +%-vm: % + cd vagrant-static && vagrant up $< + +download-pkgs: + mkdir -p vagrant-static/pkgs && cd vagrant-static/pkgs && \ + for pkg in $(PACKAGE_URLS); do \ + if ! wget -N $${pkg}; then \ + exit 1; \ + fi \ + done + +%-build: % %-vm + cd vagrant-static && vagrant ssh $< -c "/vagrant/build.sh ${SRC_VERSION}" + +%-package: % clean-outbox %-build + mkdir -p $<-pkg/lnav-${VERSION} + cp ../README ../NEWS.md $<-pkg/lnav-${VERSION} + mv vagrant-static/lnav $<-pkg/lnav-${VERSION} + cd $<-pkg && zip -r ../outbox/lnav-${VERSION}-x86_64-linux-$<.zip lnav-${VERSION} + rm -rf $<-pkg + +linux-ospkg: pkger-vm linux-package + rm -f vagrant-static/lnav\*.deb vagrant-static/lnav\*.rpm + cd vagrant-static && vagrant ssh pkger -c "/vagrant/build-pkg.sh ${VERSION}" + mv vagrant-static/lnav*.deb vagrant-static/lnav*.rpm outbox/ + +osx-build: + rm -rf osx-build-dir + mkdir -p osx-build-dir + cd ../ && ./autogen.sh + cd osx-build-dir && \ + ../../configure --enable-static \ + --with-readline=/usr/local/opt/readline \ + --with-sqlite3=/usr/local/opt/sqlite \ + --with-libarchive=/usr/local/opt/libarchive \ + "CXXFLAGS=-I/usr/local/opt/ncurses/include -g1 -O2" \ + "CFLAGS=-I/usr/local/opt/ncurses/include -g1 -O2" \ + "LDFLAGS=-L/usr/local/opt/ncurses/lib -L/usr/local/opt/xz/lib" \ + "LIBS=-llzma -lzstd -lbrotlidec-static -liconv -llz4" \ + && make -j8 && make dist -j8 && make dist-bzip2 + +osx-package: clean-outbox osx-build + mkdir -p osx-pkg/lnav-${VERSION} + git pull --rebase + cp ../README ../NEWS.md osx-pkg/lnav-${VERSION} + cp osx-build-dir/src/lnav osx-pkg/lnav-${VERSION} + cp osx-build-dir/lnav-${VERSION}.tar.gz outbox/ + cp osx-build-dir/lnav-${VERSION}.tar.bz2 outbox/ + cd osx-pkg && zip -r ../outbox/lnav-${VERSION}-x86_64-macos.zip lnav-${VERSION} + rm -rf osx-pkg + +release-NEWS.md: ../NEWS.md + sed -n "/^## lnav v${VERSION}/,/^## /p" $< | sed '$$d' > $@ + +release-tag: release-NEWS.md + gh release create ${VERSION_TAG} \ + -d \ + -t "lnav v${VERSION}" \ + --notes-file release-NEWS.md + +release: osx-package musl-package release-NEWS.md + gh release edit ${VERSION_TAG} \ + -t "lnav v${VERSION}" \ + --notes-file release-NEWS.md + gh release upload ${VERSION_TAG} outbox/* + +push: + env LANG=UTF-8 package_cloud push tstack/lnav/ubuntu/lucid outbox/lnav*.deb + env LANG=UTF-8 package_cloud push tstack/lnav/el/5 outbox/lnav-0.11.1-1.x86_64.rpm + +clean: + cd vagrant-static && vagrant destroy -f + rm -rf vagrant-static/pkgs diff --git a/release/README.md b/release/README.md new file mode 100644 index 0000000..1832b2a --- /dev/null +++ b/release/README.md @@ -0,0 +1,4 @@ +# Release + +This directory contains the [Makefile](Makefile) and scripts used to build the +binaries for a release. diff --git a/release/lnav-screenshot.terminal b/release/lnav-screenshot.terminal new file mode 100644 index 0000000..c22cbf4 --- /dev/null +++ b/release/lnav-screenshot.terminal @@ -0,0 +1,453 @@ + + + + + WindowList + + + Origin + {2361, 836} + ShowsTabBar + + ShowsTabBarInFullScreen + + Window Settings + + + Tab Column Count + 114 + Tab Row Count + 35 + Tab Scrollback Restorable + + Tab Session Class ID + E5A9F716-7408-4C2C-8F6D-F626F389C346 + Tab Settings + + ANSIBlueColor + + YnBsaXN0MDDUAQIDBAUGFRZYJHZl + cnNpb25YJG9iamVjdHNZJGFyY2hp + dmVyVCR0b3ASAAGGoKMHCA9VJG51 + bGzTCQoLDA0OVU5TUkdCXE5TQ29s + b3JTcGFjZVYkY2xhc3NPECowLjAw + MDUwNTEyMTkyOTggMC40NTMwNDQz + ODc3IDAuODI4MjczNjA3MwAQAYAC + 0hAREhNaJGNsYXNzbmFtZVgkY2xh + c3Nlc1dOU0NvbG9yohIUWE5TT2Jq + ZWN0XxAPTlNLZXllZEFyY2hpdmVy + 0RcYVHJvb3SAAQgRGiMtMjc7QUhO + W2KPkZOYo6y0t8DS1doAAAAAAAAB + AQAAAAAAAAAZAAAAAAAAAAAAAAAA + AAAA3A== + + ANSIBrightBlueColor + + YnBsaXN0MDDUAQIDBAUGFRZYJHZl + cnNpb25YJG9iamVjdHNZJGFyY2hp + dmVyVCR0b3ASAAGGoKMHCA9VJG51 + bGzTCQoLDA0OVU5TUkdCXE5TQ29s + b3JTcGFjZVYkY2xhc3NPEBwwIDAu + NTk4MzQyOTQ5MSAwLjk5ODE4ODk3 + MjUAEAGAAtIQERITWiRjbGFzc25h + bWVYJGNsYXNzZXNXTlNDb2xvcqIS + FFhOU09iamVjdF8QD05TS2V5ZWRB + cmNoaXZlctEXGFRyb290gAEIERoj + LTI3O0FITltigYOFipWepqmyxMfM + AAAAAAAAAQEAAAAAAAAAGQAAAAAA + AAAAAAAAAAAAAM4= + + ANSIRedColor + + YnBsaXN0MDDUAQIDBAUGFRZYJHZl + cnNpb25YJG9iamVjdHNZJGFyY2hp + dmVyVCR0b3ASAAGGoKMHCA9VJG51 + bGzTCQoLDA0OVU5TUkdCXE5TQ29s + b3JTcGFjZVYkY2xhc3NPECkwLjc0 + MzAzNjY4NDggMC4wMzYwNjI5Mjkw + OSAwLjA5ODYwMDQ3MDg0ABABgALS + EBESE1okY2xhc3NuYW1lWCRjbGFz + c2VzV05TQ29sb3KiEhRYTlNPYmpl + Y3RfEA9OU0tleWVkQXJjaGl2ZXLR + FxhUcm9vdIABCBEaIy0yNztBSE5b + Yo6Qkpeiq7O2v9HU2QAAAAAAAAEB + AAAAAAAAABkAAAAAAAAAAAAAAAAA + AADb + + ANSIYellowColor + + YnBsaXN0MDDUAQIDBAUGFRZYJHZl + cnNpb25YJG9iamVjdHNZJGFyY2hp + dmVyVCR0b3ASAAGGoKMHCA9VJG51 + bGzTCQoLDA0OVU5TUkdCXE5TQ29s + b3JTcGFjZVYkY2xhc3NPECcwLjU5 + MzYzNjA0OSAwLjYwMTM3MTQzMzQg + MC4wMjMwMzQ5ODc5OAAQAYAC0hAR + EhNaJGNsYXNzbmFtZVgkY2xhc3Nl + c1dOU0NvbG9yohIUWE5TT2JqZWN0 + XxAPTlNLZXllZEFyY2hpdmVy0RcY + VHJvb3SAAQgRGiMtMjc7QUhOW2KM + jpCVoKmxtL3P0tcAAAAAAAABAQAA + AAAAAAAZAAAAAAAAAAAAAAAAAAAA + 2Q== + + BackgroundBlur + 0.10492684248554913 + BackgroundColor + + YnBsaXN0MDDUAQIDBAUGFRZYJHZl + cnNpb25YJG9iamVjdHNZJGFyY2hp + dmVyVCR0b3ASAAGGoKMHCA9VJG51 + bGzTCQoLDA0OV05TV2hpdGVcTlND + b2xvclNwYWNlViRjbGFzc00wIDAu + ODUwMDAwMDIAEAOAAtIQERITWiRj + bGFzc25hbWVYJGNsYXNzZXNXTlND + b2xvcqISFFhOU09iamVjdF8QD05T + S2V5ZWRBcmNoaXZlctEXGFRyb290 + gAEIERojLTI3O0FIUF1kcnR2e4aP + l5qjtbi9AAAAAAAAAQEAAAAAAAAA + GQAAAAAAAAAAAAAAAAAAAL8= + + Bell + + CursorColor + + YnBsaXN0MDDUAQIDBAUGFRZYJHZl + cnNpb25YJG9iamVjdHNZJGFyY2hp + dmVyVCR0b3ASAAGGoKMHCA9VJG51 + bGzTCQoLDA0OVU5TUkdCXE5TQ29s + b3JTcGFjZVYkY2xhc3NPECcwLjgy + MjYwNTI5ODkgMC4yMTIxNjI2MDU5 + IDAuNTc2NDI4NDQzMgAQAYAC0hAR + EhNaJGNsYXNzbmFtZVgkY2xhc3Nl + c1dOU0NvbG9yohIUWE5TT2JqZWN0 + XxAPTlNLZXllZEFyY2hpdmVy0RcY + VHJvb3SAAQgRGiMtMjc7QUhOW2KM + jpCVoKmxtL3P0tcAAAAAAAABAQAA + AAAAAAAZAAAAAAAAAAAAAAAAAAAA + 2Q== + + DisableANSIColor + + Font + + YnBsaXN0MDDUAQIDBAUGGBlYJHZl + cnNpb25YJG9iamVjdHNZJGFyY2hp + dmVyVCR0b3ASAAGGoKQHCBESVSRu + dWxs1AkKCwwNDg8QVk5TU2l6ZVhO + U2ZGbGFnc1ZOU05hbWVWJGNsYXNz + I0AkAAAAAAAAEBCAAoADVk1vbmFj + b9ITFBUWWiRjbGFzc25hbWVYJGNs + YXNzZXNWTlNGb250ohUXWE5TT2Jq + ZWN0XxAPTlNLZXllZEFyY2hpdmVy + 0RobVHJvb3SAAQgRGiMtMjc8QktS + W2JpcnR2eH+Ej5ifoqu9wMUAAAAA + AAABAQAAAAAAAAAcAAAAAAAAAAAA + AAAAAAAAxw== + + FontAntialias + + FontHeightSpacing + 1 + FontWidthSpacing + 1 + Linewrap + + ProfileCurrentVersion + 2.02 + SelectionColor + + YnBsaXN0MDDUAQIDBAUGFRZYJHZl + cnNpb25YJG9iamVjdHNZJGFyY2hp + dmVyVCR0b3ASAAGGoKMHCA9VJG51 + bGzTCQoLDA0OV05TV2hpdGVcTlND + b2xvclNwYWNlViRjbGFzc0swLjI1 + NDAzMjI1ABADgALSEBESE1okY2xh + c3NuYW1lWCRjbGFzc2VzV05TQ29s + b3KiEhRYTlNPYmplY3RfEA9OU0tl + eWVkQXJjaGl2ZXLRFxhUcm9vdIAB + CBEaIy0yNztBSFBdZHBydHmEjZWY + obO2uwAAAAAAAAEBAAAAAAAAABkA + AAAAAAAAAAAAAAAAAAC9 + + ShowWindowSettingsNameInTitle + + TerminalType + xterm-256color + TextBoldColor + + YnBsaXN0MDDUAQIDBAUGFRZYJHZl + cnNpb25YJG9iamVjdHNZJGFyY2hp + dmVyVCR0b3ASAAGGoKMHCA9VJG51 + bGzTCQoLDA0OV05TV2hpdGVcTlND + b2xvclNwYWNlViRjbGFzc0IxABAD + gALSEBESE1okY2xhc3NuYW1lWCRj + bGFzc2VzV05TQ29sb3KiEhRYTlNP + YmplY3RfEA9OU0tleWVkQXJjaGl2 + ZXLRFxhUcm9vdIABCBEaIy0yNztB + SFBdZGdpa3B7hIyPmKqtsgAAAAAA + AAEBAAAAAAAAABkAAAAAAAAAAAAA + AAAAAAC0 + + TextColor + + YnBsaXN0MDDUAQIDBAUGFRZYJHZl + cnNpb25YJG9iamVjdHNZJGFyY2hp + dmVyVCR0b3ASAAGGoKMHCA9VJG51 + bGzTCQoLDA0OV05TV2hpdGVcTlND + b2xvclNwYWNlViRjbGFzc0swLjk0 + NzU4MDY0ABADgALSEBESE1okY2xh + c3NuYW1lWCRjbGFzc2VzV05TQ29s + b3KiEhRYTlNPYmplY3RfEA9OU0tl + eWVkQXJjaGl2ZXLRFxhUcm9vdIAB + CBEaIy0yNztBSFBdZHBydHmEjZWY + obO2uwAAAAAAAAEBAAAAAAAAABkA + AAAAAAAAAAAAAAAAAAC9 + + UseBoldFonts + + UseBrightBold + + WindowTitle + Terminal + columnCount + 156 + deleteSendsBackspace + + keyMapBoundKeys + + $F700 + Oa + $F701 + Ob + $F708 + [25~ + $F709 + [26~ + $F70A + [28~ + $F70B + [29~ + $F70C + [31~ + $F70D + [22~ + $F70E + [33~ + $F70F + [34~ + $F729 + scrollToBeginningOfDocument: + $F72B +  + $F72C + scrollPageUp: + $F72D + scrollPageDown: + F704 + OP + F705 + OQ + F706 + OR + F707 + OS + F708 + [15~ + F709 + [17~ + F70A + [18~ + F70B + [19~ + F70C + [20~ + F70D + [21~ + F70E + [23~ + F70F + [24~ + F710 + [25~ + F711 + [26~ + F712 + [28~ + F713 + [29~ + F714 + [31~ + F715 + [32~ + F716 + [33~ + F717 + [34~ + F728 + [3~ + F729 + OH + F72B + OF + F72C + [5~ + F72D + [6~ + ^F702 + Od + ^F703 + Oc + ~F702 + b + ~F703 + f + ~F704 + [17~ + ~F705 + [18~ + ~F706 + [19~ + ~F707 + [20~ + ~F708 + [21~ + ~F709 + [23~ + ~F70A + [24~ + ~F70B + [25~ + ~F70C + [26~ + ~F70D + [28~ + ~F70E + [29~ + ~F70F + [31~ + ~F710 + [32~ + ~F711 + [33~ + ~F712 + [34~ + + mouseLeftClick + + BAtzdHJlYW10eXBlZIHoA4QBQISE + hAhOU051bWJlcgCEhAdOU1ZhbHVl + AISECE5TT2JqZWN0AIWEASqEhAFj + lwGG + + mouseMiddleClick + + BAtzdHJlYW10eXBlZIHoA4QBQISE + hAhOU051bWJlcgCEhAdOU1ZhbHVl + AISECE5TT2JqZWN0AIWEASqEhAFj + lwGG + + mouseRightClick + + BAtzdHJlYW10eXBlZIHoA4QBQISE + hAhOU051bWJlcgCEhAdOU1ZhbHVl + AISECE5TT2JqZWN0AIWEASqEhAFj + lwGG + + mouseWheel + + BAtzdHJlYW10eXBlZIHoA4QBQISE + hAhOU051bWJlcgCEhAdOU1ZhbHVl + AISECE5TT2JqZWN0AIWEASqEhAFj + lwGG + + mouseWheelEmulation + + BAtzdHJlYW10eXBlZIHoA4QBQISE + hAhOU051bWJlcgCEhAdOU1ZhbHVl + AISECE5TT2JqZWN0AIWEASqEhAFj + lwGG + + name + ProTerminalLatest + rowCount + 49 + shellExitAction + 0 + type + Window Settings + useOptionAsMetaKey + + + Tab Settings Name + ProTerminalLatest + TabSelected + + + + WindowNumber + 5 + + + Origin + {2106, 1046} + ShowsTabBar + + ShowsTabBarInFullScreen + + Window Settings + + + Tab Column Count + 171 + Tab Row Count + 63 + Tab Scrollback Restorable + + Tab Session Class ID + 6DC31F6C-EFF7-4364-93B8-EBD258566CB7 + Tab Settings + + Font + + YnBsaXN0MDDUAQIDBAUGGBlYJHZl + cnNpb25YJG9iamVjdHNZJGFyY2hp + dmVyVCR0b3ASAAGGoKQHCBESVSRu + dWxs1AkKCwwNDg8QVk5TU2l6ZVhO + U2ZGbGFnc1ZOU05hbWVWJGNsYXNz + I0AmAAAAAAAAEBCAAoADXU1lbmxv + LVJlZ3VsYXLSExQVFlokY2xhc3Nu + YW1lWCRjbGFzc2VzVk5TRm9udKIV + F1hOU09iamVjdF8QD05TS2V5ZWRB + cmNoaXZlctEaG1Ryb290gAEIERoj + LTI3PEJLUltiaXJ0dniGi5afpqmy + xMfMAAAAAAAAAQEAAAAAAAAAHAAA + AAAAAAAAAAAAAAAAAM4= + + FontAntialias + + FontWidthSpacing + 1.004032258064516 + ProfileCurrentVersion + 2.02 + name + Basic + type + Window Settings + + Tab Settings Name + Basic + TabSelected + + + + WindowNumber + 1 + + + name + lnav-screenshot + type + Window Group + + diff --git a/release/loggen.py b/release/loggen.py new file mode 100755 index 0000000..5f95c57 --- /dev/null +++ b/release/loggen.py @@ -0,0 +1,225 @@ +#! /usr/bin/env python3 + +import os +import sys +import time +import uuid +import shutil +import random +import datetime + +SYSLOG_DATE_FMT = "%b %d %H:%M:%S" +ACCESS_LOG_DATE_FMT = "%d/%b/%Y:%H:%M:%S" +GENERIC_DATE_FMT = "%Y-%m-%dT%H:%M:%S.%%s" + +TEST_ADDRESSES = ( + ["192.0.2.55"] * 20 + + ["192.0.2.44"] * 20 + + ["192.0.2.3"] * 40 + + ["192.0.2.100"] * 10 + + ["192.0.2.122"] * 30 + + ["192.0.2.42"] * 100 +) + +TEST_USERNAMES = [ + "bob@example.com", + "bob@example.com", + "bob@example.com", + "combatcarl@example.com", + "combatcarl@example.com", + "combatcarl@example.com", + "combatcarl@example.com", + "combatcarl@example.com", + "combatcarl@example.com", + "-", + "-", + "-", + "-", + "-", + "-", +] + +TEST_METHODS = [ + "GET", + "GET", + "GET", + "GET", + "GET", + "GET", + "PUT", +] + +PATH_COMPS = "Lorem ipsum dolor sit amet consectetur adipiscing elit sed do eiusmod tempor incididunt ut labore et dolore magna aliqua".split() + +TEST_URLS = [ + "/index.html", + "/index.html", + "/index.html", + "/features.html", + "/images/compass.jpg", + "/obj/1234", + "/obj/1235?foo=bar", + "/obj/1236?search=demo&start=1", +] + +for index in range(0, 50): + path_list = [] + for count in range(random.randint(2, 8)): + path_list.append(random.choice(PATH_COMPS)) + TEST_URLS.append("/".join(path_list)) + +TEST_VERSIONS = [ + "HTTP/1.0", + "HTTP/1.0", + "HTTP/1.1", +] + +TEST_STATUS = [ + 200, + 200, + 200, + 200, + 200, + 200, + 200, + 200, + 200, + 200, + 404, + 404, + 404, + 404, + 403, + 403, + 403, + 500 +] + +TEST_REFERRERS = [ + "-", + "-", + "-", + "-", + "-", + "-", + "http://lnav.org/download.html", +] + +TEST_AGENTS = [ + "-", + "-", + "-", + "-", + "Apache-HttpClient/4.2.3 (java 1.5)", + "Apache-HttpClient/4.2.3 (java 1.5)", + "Apache-HttpClient/4.2.3 (java 1.5)", + "Apache-HttpClient/4.2.3 (java 1.5)", + "Apache-HttpClient/4.2.3 (java 1.5)", + "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36", + "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36", + "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36", + "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36", + "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36", + "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36", + "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36", + "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36", + "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36", + "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36", + "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36", + "Roku4640X/DVP-7.70 (297.70E04154A)", + "Roku4640X/DVP-7.70 (297.70E04154A)", + "Roku4640X/DVP-7.70 (297.70E04154A)", +] + +START_TIME = datetime.datetime.fromtimestamp(1641898727) +ACCESS_LOG_CURR_TIME = START_TIME +SYSLOG_LOG_CURR_TIME = START_TIME + + +def access_log_msgs(): + global ACCESS_LOG_CURR_TIME + while True: + ACCESS_LOG_CURR_TIME += datetime.timedelta(seconds=random.randrange(1, 3)) + yield '%s - %s [%s +0000] "%s %s %s" %s %s "%s" "%s"\n' % ( + random.choice(TEST_ADDRESSES), + random.choice(TEST_USERNAMES), + ACCESS_LOG_CURR_TIME.strftime(ACCESS_LOG_DATE_FMT), + random.choice(TEST_METHODS), + random.choice(TEST_URLS), + random.choice(TEST_VERSIONS), + random.choice(TEST_STATUS), + int(random.lognormvariate(1, 1) * 1000), + random.choice(TEST_REFERRERS), + random.choice(TEST_AGENTS) + ) + + +TEST_PROCS = [ + "server[123]", + "server[123]", + "server[123]", + "server[121]", + "server[124]", + "server[123]", + "worker[61456]", + "worker[61456]", + "worker[61457]", +] + +TEST_MSGS = [ + "Handling request %s" % uuid.uuid4(), + "Handling request %s" % uuid.uuid4(), + "Handling request %s" % uuid.uuid4(), + "Successfully started helper", + "Reading from device: /dev/hda", + "Received packet from %s" % random.choice(TEST_ADDRESSES), + "Received packet from %s" % random.choice(TEST_ADDRESSES), + "Received packet from %s" % random.choice(TEST_ADDRESSES), +] + + +def syslog_msgs(): + global SYSLOG_LOG_CURR_TIME + while True: + SYSLOG_LOG_CURR_TIME += datetime.timedelta(seconds=random.randrange(1, 3)) + yield '%s frontend3 %s: %s\n' % ( + SYSLOG_LOG_CURR_TIME.strftime(SYSLOG_DATE_FMT), + random.choice(TEST_PROCS), + random.choice(TEST_MSGS), + ) + + +try: + shutil.rmtree("/tmp/demo") + os.makedirs("/tmp/demo") +except OSError: + pass + +FILES = [ + ("/tmp/demo/access_log", access_log_msgs()), + ("/tmp/demo/messages", syslog_msgs()), +] + +COUNTER = 0 +while COUNTER < 5000: + loop_inc = datetime.timedelta(seconds=random.weibullvariate(1, 1.5) * 500) + ACCESS_LOG_CURR_TIME += loop_inc + SYSLOG_LOG_CURR_TIME += loop_inc + for fname, gen in FILES: + for i in range(random.randrange(4, 8)): + COUNTER += 1 + with open(fname, "a+") as fp: + if random.uniform(0.0, 1.0) < 0.01: + line = next(gen) + prefix = line[:50] + suffix = line[50:] + fp.write(prefix) + # time.sleep(random.uniform(0.5, 0.6)) + fp.write(suffix) + else: + fp.write(next(gen)) + # if random.uniform(0.0, 1.0) < 0.010: + # fp.truncate(0) + # time.sleep(random.uniform(0.01, 0.02)) + # if random.uniform(0.0, 1.0) < 0.001: + # os.remove(fname) diff --git a/release/spectrolog.py b/release/spectrolog.py new file mode 100755 index 0000000..df08a12 --- /dev/null +++ b/release/spectrolog.py @@ -0,0 +1,114 @@ +#! /usr/bin/env python + +import sys +import time +import datetime +import random + +DATE_FMT = "%a %b %d %H:%M:%S %Y" + +duration = [] + [80] * 10 + [100] * 10 + [40] * 10 + +diter = iter(duration) + +DURATIONS = ( + 40, + 40, + 40, + 40, + 40, + 40, + 40, + 40, + 40, + 40, + 40, + 40, + 40, + 50, + 50, + 50, + 50, + 75, + 75, + 100, + 100, + 100, + 100, + 100, + 100, + 100, + 100, + 100, + 100, + 100, + 100, + 100, + 100, + 100, + 100, + 100, + 100, + 100, + 100, + 100, + 100, + 100, + 100, + 100, + 100, + 100, + 100, + 100, + 100, + 100, + 100, + 100, + 100, + 100, + 100, + 100, + 100, + 100, + 100, +) + +DURATION_FUZZ = ( + 0, + 0, + 0, + 0, + 0, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -2, + -2, + -2 +) + +while True: + print ("[pid: 88186|app: 0|req: 5/19] 127.0.0.1 () {38 vars in 696 bytes} " + "[%s] POST /update_metrics => generated 47 bytes " + "in %s msecs (HTTP/1.1 200) 9 headers in 378 bytes (1 switches on core 60)" % + (datetime.datetime.utcnow().strftime(DATE_FMT), + random.choice(DURATIONS) + random.choice(DURATION_FUZZ))) + # diter.next())) + sys.stdout.flush() + + time.sleep(0.01) diff --git a/release/tail-demo.sh b/release/tail-demo.sh new file mode 100755 index 0000000..a9a5998 --- /dev/null +++ b/release/tail-demo.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash + +PAUSE_TIME=0.5 + +while true; do + logger "lnav is always looking for new log messages" + sleep ${PAUSE_TIME} + logger " and files in directories" + sleep ${PAUSE_TIME} + logger "Filters are always applied as new data is loaded in" + logger " which is indicated by the 'Not Shown' count" + logger "bad-message-to-filter" + sleep ${PAUSE_TIME} + logger "" + sleep ${PAUSE_TIME} + logger "Scroll up to lock the view in place" + sleep 3 + logger "Data is still being read in the meantime" +done diff --git a/release/vagrant-static/Vagrantfile b/release/vagrant-static/Vagrantfile new file mode 100644 index 0000000..98df560 --- /dev/null +++ b/release/vagrant-static/Vagrantfile @@ -0,0 +1,110 @@ +# -*- mode: ruby -*- +# vi: set ft=ruby : + +# All Vagrant configuration is done below. The "2" in Vagrant.configure +# configures the configuration version (we support older styles for +# backwards compatibility). Please don't change it unless you know what +# you're doing. +Vagrant.configure(2) do |config| + # The most common configuration options are documented and commented below. + # For a complete reference, please see the online documentation at + # https://docs.vagrantup.com. + + # Every Vagrant development environment requires a box. You can search for + # boxes at https://atlas.hashicorp.com/search. + config.vm.provider "virtualbox" do |v| + v.memory = 2048 + v.cpus = 4 + end + + config.vm.synced_folder ".", "/vagrant", type: "nfs" + config.vm.define :freebsd do |freebsd| + freebsd.vm.network "private_network", :type => 'dhcp' + freebsd.vm.provision "shell", path:"pkg.sh" + freebsd.vm.provision "shell", path:"provision.sh" + freebsd.vm.box = "freebsd/FreeBSD-12.1-RELEASE" + freebsd.vm.box_version = "2019.11.01" + freebsd.vm.synced_folder ".", "/vagrant", type: "nfs" + freebsd.ssh.shell = "sh" + freebsd.vm.base_mac = "080027D14C66" + freebsd.vm.boot_timeout = 1200 + end + + config.vm.define :musl do |musl| + musl.vbguest.auto_update = false + musl.vm.synced_folder ".", "/vagrant", type: "virtualbox" + musl.vm.network "private_network", ip: "192.168.56.16" + musl.vm.provision "shell", path:"musl-pkg.sh" + musl.vm.provision "shell", path:"provision.sh" + musl.vm.box = "generic/alpine315" + end + + config.vm.define :linux do |linux| + linux.vm.network :private_network, ip: "192.168.56.14" + linux.vm.provision "shell", path:"pkg.sh" + linux.vm.provision "shell", path:"provision.sh" + linux.vm.box = "centos/7" + end + + config.vm.define :pkger do |pkger| + # config.vm.synced_folder ".", "/vagrant", type: "nfs" + pkger.vm.network :private_network, ip: "192.168.56.15" + pkger.vm.provision "shell", path:"provision-pkg.sh" + pkger.vm.box = "ubuntu/xenial64" + end + + # Disable automatic box update checking. If you disable this, then + # boxes will only be checked for updates when the user runs + # `vagrant box outdated`. This is not recommended. + # config.vm.box_check_update = false + + # Create a forwarded port mapping which allows access to a specific port + # within the machine from a port on the host machine. In the example below, + # accessing "localhost:8080" will access port 80 on the guest machine. + # config.vm.network "forwarded_port", guest: 80, host: 8080 + + # Create a private network, which allows host-only access to the machine + # using a specific IP. + # config.vm.network "private_network", ip: "192.168.33.10" + + # Create a public network, which generally matched to bridged network. + # Bridged networks make the machine appear as another physical device on + # your network. + # config.vm.network "public_network" + + # Share an additional folder to the guest VM. The first argument is + # the path on the host to the actual folder. The second argument is + # the path on the guest to mount the folder. And the optional third + # argument is a set of non-required options. + # config.vm.synced_folder "../data", "/vagrant_data" + + # Provider-specific configuration so you can fine-tune various + # backing providers for Vagrant. These expose provider-specific options. + # Example for VirtualBox: + # + # config.vm.provider "virtualbox" do |vb| + # # Display the VirtualBox GUI when booting the machine + # vb.gui = true + # + # # Customize the amount of memory on the VM: + # vb.memory = "1024" + # end + # + # View the documentation for the provider you are using for more + # information on available options. + + # Define a Vagrant Push strategy for pushing to Atlas. Other push strategies + # such as FTP and Heroku are also available. See the documentation at + # https://docs.vagrantup.com/v2/push/atlas.html for more information. + # config.push.define "atlas" do |push| + # push.app = "YOUR_ATLAS_USERNAME/YOUR_APPLICATION_NAME" + # end + + # Enable provisioning with a shell script. Additional provisioners such as + # Puppet, Chef, Ansible, Salt, and Docker are also available. Please see the + # documentation for more information about their specific syntax and use. + # config.vm.provision "shell", inline: <<-SHELL + # sudo apt-get update + # sudo apt-get install -y apache2 + # SHELL +end diff --git a/release/vagrant-static/build-pkg.sh b/release/vagrant-static/build-pkg.sh new file mode 100755 index 0000000..232abd5 --- /dev/null +++ b/release/vagrant-static/build-pkg.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash + +VERSION=$1 + +fpm --verbose \ + -s zip \ + -t rpm \ + -n "lnav" \ + -v "$VERSION" \ + -p /vagrant/ \ + -a native \ + --url https://lnav.org \ + -m 'timothyshanestack@gmail.com' \ + --description 'A log file viewer and analyzer for the terminal' \ + --license BSD-2-Clause \ + --category 'System/Monitoring' \ + /vagrant/lnav-linux.zip + +fpm --verbose \ + -s zip \ + -t deb \ + -n "lnav" \ + -v "$VERSION" \ + -p /vagrant/ \ + -a native \ + --url https://lnav.org \ + -m 'timothyshanestack@gmail.com' \ + --description 'A log file viewer and analyzer for the terminal' \ + --license BSD-2-Clause \ + /vagrant/lnav-linux.zip diff --git a/release/vagrant-static/build.sh b/release/vagrant-static/build.sh new file mode 100755 index 0000000..aa0491b --- /dev/null +++ b/release/vagrant-static/build.sh @@ -0,0 +1,84 @@ +#!/usr/bin/env bash + +OS=$(uname -s) +if test x"${OS}" != x"FreeBSD"; then + source scl_source enable devtoolset-9 +fi + +if test x"${OS}" != x"FreeBSD"; then + MAKE=make +else + MAKE=gmake +fi + +FAKE_ROOT=/home/vagrant/fake.root + +SRC_VERSION=$1 + +mkdir -p ~/github + +cd ~/github +if ! test -d lnav; then + git clone https://github.com/tstack/lnav.git +fi + +cd ~/github/lnav +git restore . +git pull --rebase + +if test -n "$SRC_VERSION"; then + git checkout "$SRC_VERSION" +fi + +saved_PATH=${PATH} +export PATH=${FAKE_ROOT}/bin:${PATH} +saved_LD_LIBRARY_PATH=${LD_LIBRARY_PATH} +export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:${FAKE_ROOT}/lib +if test ! -f "configure"; then + ./autogen.sh + rm -rf ~/github/lbuild + mkdir -p ~/github/lbuild +fi +cd ~/github/lbuild + +TARGET_FILE='/vagrant/lnav-linux.zip' +if test x"${OS}" != x"FreeBSD"; then + if test x"$(lsb_release | awk '{print $3}')" == x"Alpine"; then + TARGET_FILE='/vagrant/lnav-musl.zip' + ../lnav/configure \ + --with-libarchive=${FAKE_ROOT} \ + CFLAGS='-static -g1 -gz=zlib -no-pie -O2' \ + CXXFLAGS='-static -g1 -gz=zlib -U__unused -no-pie -O2' \ + LDFLAGS="-L${FAKE_ROOT}/lib" \ + CPPFLAGS="-I${FAKE_ROOT}/include" \ + LIBS="-L${FAKE_ROOT}/lib -lexecinfo -lssh2 -llzma -lssl -lcrypto -lz" \ + --enable-static + PATH="${FAKE_ROOT}/bin:${PATH}" + else + ../lnav/configure \ + --enable-static \ + --with-libarchive=${FAKE_ROOT} \ + LDFLAGS="-L${FAKE_ROOT}/lib" \ + CPPFLAGS="-I${FAKE_ROOT}/include -O2" \ + LIBS="-L${FAKE_ROOT}/lib -lssh2 -llzma -lssl -lcrypto -lz" \ + PATH="${FAKE_ROOT}/bin:${PATH}" + fi +else + ../lnav/configure \ + --enable-static \ + LDFLAGS="-L${FAKE_ROOT}/lib -static" \ + LIBS="-lm -lelf" \ + CPPFLAGS="-I${FAKE_ROOT}/include -O2" \ + PATH="${FAKE_ROOT}/bin:${PATH}" +fi + +${MAKE} -j2 && cp src/lnav /vagrant/lnav + +if test x"${OS}" != x"FreeBSD"; then + mkdir instdir + make install DESTDIR=$PWD/instdir + (cd instdir/ && zip -r "${TARGET_FILE}" .) +fi + +export PATH=${saved_PATH} +export LD_LIBRARY_PATH=${saved_LD_LIBRARY_PATH} diff --git a/release/vagrant-static/musl-pkg.sh b/release/vagrant-static/musl-pkg.sh new file mode 100644 index 0000000..fea8308 --- /dev/null +++ b/release/vagrant-static/musl-pkg.sh @@ -0,0 +1,24 @@ +#!/bin/sh + +sudo apk update && sudo apk upgrade +sudo apk add \ + build-base \ + binutils \ + m4 \ + git \ + zip \ + perl \ + ncurses \ + ncurses-dev \ + autoconf \ + automake \ + elfutils \ + elfutils-dev \ + libelf-static \ + libexecinfo-dev \ + libexecinfo-static \ + libtool \ + libunwind \ + libunwind-dev \ + libunwind-static \ + linux-headers diff --git a/release/vagrant-static/pkg.sh b/release/vagrant-static/pkg.sh new file mode 100755 index 0000000..931dc33 --- /dev/null +++ b/release/vagrant-static/pkg.sh @@ -0,0 +1,22 @@ +#!/bin/sh + +OS=$(uname -s) +if test x"${OS}" != x"FreeBSD"; then + sudo yum install -y \ + zip \ + unzip \ + m4 \ + autoconf \ + automake \ + ncurses \ + ncurses-devel \ + ncurses-static \ + git \ + centos-release-scl \ + perl-Data-Dumper \ + patch \ + wget + sudo yum install -y "devtoolset-9-gcc*" +else + pkg install -y wget git m4 bash autoconf automake sqlite3 gmake +fi diff --git a/release/vagrant-static/provision-pkg.sh b/release/vagrant-static/provision-pkg.sh new file mode 100644 index 0000000..bb1991d --- /dev/null +++ b/release/vagrant-static/provision-pkg.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +# Installs dependencies for running fpm + +sudo apt-get update +sudo apt-get install -y ruby2.3 ruby-dev gcc make zip unzip rpm +sudo gem install fpm diff --git a/release/vagrant-static/provision.sh b/release/vagrant-static/provision.sh new file mode 100755 index 0000000..3b98103 --- /dev/null +++ b/release/vagrant-static/provision.sh @@ -0,0 +1,152 @@ +#!/usr/bin/env bash + +OS=$(uname -s) +if test x"${OS}" != x"FreeBSD"; then + source scl_source enable devtoolset-9 +fi + +FAKE_ROOT=/home/vagrant/fake.root + +rm -rf ~/extract +mkdir -p ${FAKE_ROOT} ~/packages ~/extract ~/github + +export PATH=${FAKE_ROOT}/bin:${PATH} + +cd ~/github + +SQLITE_CFLAGS="\ + -DSQLITE_ENABLE_COLUMN_METADATA \ + -DSQLITE_SOUNDEX \ + -DSQLITE_ENABLE_DBSTAT_VTAB \ + -DSQLITE_ENABLE_API_ARMOR \ + -DSQLITE_ENABLE_JSON1 \ + -DSQLITE_ENABLE_UPDATE_DELETE_LIMIT \ + " + +NCURSES_FALLBACKS="\ +ansi,\ +cygwin,\ +Eterm,\ +Eterm-256color,\ +gnome,\ +gnome-256color,\ +konsole,\ +konsole-256color,\ +linux,\ +putty,\ +rxvt,\ +rxvt-256color,\ +screen,\ +screen-16color,\ +screen-256color,\ +tmux,\ +tmux-256color,\ +vt100,\ +vt220,\ +xterm,\ +xterm-256color\ +" + +cd ~/extract + +for pkg in /vagrant/pkgs/*.tar.gz; do + tar xfz "$pkg" +done + +(cd make-4.2.1 && ./configure --prefix=${FAKE_ROOT} && make && make install) + +OS=$(uname -s) + + +(cd readline-6.3 && ./configure --prefix=${FAKE_ROOT} && make && make install) + +(cd bzip2-1.0.8 && make install PREFIX=${FAKE_ROOT}) + +(cd sqlite-* && + ./configure --disable-editline --prefix=${FAKE_ROOT} \ + CFLAGS="${SQLITE_CFLAGS}" \ + && \ + make && make install) + +(cd openssl-* && + ./config --prefix=${FAKE_ROOT} -fPIC && + make && + make install) + +(cd ncurses-6.3 && \ + ./configure --prefix=${FAKE_ROOT} \ + --enable-ext-mouse \ + --enable-sigwinch \ + --with-default-terminfo-dir=/usr/share/terminfo \ + --enable-ext-colors \ + --enable-widec \ + --enable-termcap \ + --with-fallbacks=$NCURSES_FALLBACKS \ + && \ + make && make install) + +(cd pcre2-* && \ + ./configure --prefix=${FAKE_ROOT} \ + --enable-jit \ + && \ + make && make install) + +if test x"${OS}" != x"FreeBSD"; then + (cd zlib-1.2.12 && ./configure --prefix=${FAKE_ROOT} && make && make install) + + (cd libssh2-* && + ./configure --prefix=${FAKE_ROOT} \ + --with-libssl-prefix=${FAKE_ROOT} \ + --with-libz-prefix=${FAKE_ROOT} \ + "CPPFLAGS=-I${FAKE_ROOT}/include" \ + "LDFLAGS=-ldl -L${FAKE_ROOT}/lib" && + make && + make install) + + (cd curl-* && + ./configure --prefix=${FAKE_ROOT} \ + --with-libssh2=${FAKE_ROOT} \ + --with-ssl=${FAKE_ROOT} \ + --with-zlib=${FAKE_ROOT} \ + "LDFLAGS=-ldl" && + make && + make install) +else + (cd zlib-1.2.12 && ./configure --prefix=${FAKE_ROOT} "CFLAGS=-fPIC" \ + && make && make install) + + (cd libssh2-* && + ./configure --prefix=${FAKE_ROOT} \ + --with-libssl-prefix=${FAKE_ROOT} \ + --with-libz-prefix=${FAKE_ROOT} \ + && + make && + make install) + + (cd curl-* && + ./configure --prefix=${FAKE_ROOT} \ + --with-libssh2=${FAKE_ROOT} \ + --with-ssl=${FAKE_ROOT} \ + --with-zlib=${FAKE_ROOT} \ + "CFLAGS=-fPIC" && + make && + make install) +fi + +(cd xz-* && + ./configure --prefix=${FAKE_ROOT} \ + --disable-shared \ + "LDFLAGS=-L${FAKE_ROOT}/lib" \ + "CPPFLAGS=-I${FAKE_ROOT}/include" \ + && + make && + make install) + +(cd libarchive-* && + ./configure --prefix=${FAKE_ROOT} \ + --disable-shared \ + "LDFLAGS=-L${FAKE_ROOT}/lib" \ + "CPPFLAGS=-I${FAKE_ROOT}/include" \ + && + make && + make install) diff --git a/snapcraft.yaml b/snapcraft.yaml new file mode 100644 index 0000000..55239fd --- /dev/null +++ b/snapcraft.yaml @@ -0,0 +1,84 @@ +name: lnav +adopt-info: lnav +summary: Log File Navigator +description: | + The Log File Navigator, **lnav** for short, is an advanced log file viewer + for the small-scale. +icon: docs/assets/images/favicon.png + +base: core20 +grade: stable +confinement: strict + +environment: + LOCPATH: $SNAP/usr/lib/locale + GIT_TEMPLATE_DIR: $SNAP/usr/share/git-core/templates + GIT_EXEC_PATH: $SNAP/usr/lib/git-core + +apps: + lnav: + command: usr/bin/lnav + plugs: + - home # optional, allows to read log files from home directory + - log-observe # required, provide access to system logs in /var/log + - network # required, lnav uses sendto() with UNIX domain socket + - removable-media + - ssh-keys + - x11 + +parts: + selective-checkout: + source: https://github.com/Lin-Buo-Ren/selective-checkout.git + source-tag: v2.0.2 + plugin: dump + build-packages: + # Uncomment the VCS your main part is using + - git + - curl + - jq + - sed + + #- mercurial + #- subversion + stage: + - scriptlets/selective-checkout + prime: + - -* + lnav: + after: + - selective-checkout + plugin: autotools + autotools-configure-parameters: + - CFLAGS="-O2" + - CXXFLAGS="-O2" + source: https://github.com/tstack/lnav.git + source-depth: 500 + override-pull: | + snapcraftctl pull + + "$SNAPCRAFT_STAGE"/scriptlets/selective-checkout --debug --force-snapshot + build-packages: + - build-essential + - libarchive-dev + - libcurl4-gnutls-dev + - libpcre2-dev + - libsqlite3-dev + - libncursesw6 + - libreadline-dev + - zlib1g-dev + - libbz2-dev + - libgpm-dev + stage-packages: + - zlib1g + - git-core + - libcurl4 + - libncursesw6 + - libpcre2-8-0 + - libgpm2 + - libarchive13 + - libicu66 + - libxml2 + - locales-all + - ssh + - tshark + - xclip diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..8d91d23 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,632 @@ +include(CheckTypeSize) +include(CheckIncludeFile) +include(CheckLibraryExists) +include(CheckFunctionExists) + +check_type_size(off_t SIZEOF_OFF_T) + +check_include_file("pty.h" HAVE_PTY_H) +check_include_file("util.h" HAVE_UTIL_H) +check_include_file("execinfo.h" HAVE_EXECINFO_H) + +set(VCS_PACKAGE_STRING "lnav ${CMAKE_PROJECT_VERSION}") +set(PACKAGE_VERSION "${CMAKE_PROJECT_VERSION}") + +configure_file(config.cmake.h.in config.h) + +add_subdirectory(base) +add_subdirectory(pcrepp) +add_subdirectory(remote) +add_subdirectory(tailer) +add_subdirectory(formats/logfmt) +add_subdirectory(yajl) +add_subdirectory(yajlpp) + +add_executable(bin2c bin2c.hh ../tools/bin2c.c) +target_link_libraries(bin2c ZLIB::ZLIB) + +add_executable(ptimec ptimec.hh ptimec.c) + +set(TIME_FORMATS + "@%@" + "%Y-%m-%d %H:%M:%S" + "%Y-%m-%d %H:%M:%S%z" + "%Y-%m-%d %H:%M:%S %z" + "%Y-%m-%d %H:%M" + "%Y-%m-%dT%H:%M:%S.%f%z" + "%y-%m-%dT%H:%M:%S.%f%z" + "%Y-%m-%dT%H:%M:%SZ" + "%Y-%m-%dT%H:%M:%S%z" + "%Y-%m-%dT%H:%M:%S" + "%Y-%m-%dT%H:%M:%S%z" + "%Y/%m/%d %H:%M:%S" + "%Y/%m/%d %H:%M:%S %z" + "%Y/%m/%d %H:%M:%S%z" + "%Y/%m/%d %H:%M" + "%Y %b %d %a %H:%M:%S.%L" + "%Y %b %d %H:%M:%S.%L" + "%Y %b %d %H:%M:%S" + "%a %b %d %H:%M:%S %Y" + "%a %b %d %H:%M:%S.%f %Y" + "%a %b %d %H:%M:%S %Z %Y" + "%a %b %d %H:%M:%S " + "%a %b %d %H:%M:%S.%L " + "%a %b %d %H:%M " + "%a %b %e %H:%M:%S %Z %Y" + "%d/%b/%Y:%H:%M:%S +0000" + "%d/%b/%Y:%H:%M:%S %z" + "%d-%b-%Y %H:%M:%S %z" + "%d-%b-%Y %H:%M:%S %Z" + "%d %b %Y %H:%M:%S" + "%d %b %Y %H:%M:%S.%L" + "%d %b %Y %H:%M:%S,%L" + "%b %d %H:%M:%S" + "%b %d %k:%M:%S" + "%b %d %l:%M:%S" + "%b %e, %Y %l:%M:%S %p" + "%m/%d/%y %H:%M:%S" + "%m/%d/%Y %I:%M:%S:%L %p %Z" + "%m/%d/%Y %I:%M:%S %p %Z" + "%m/%d/%Y %l:%M:%S %p %Z" + "%m/%e/%Y %I:%M:%S %p" + "%m/%e/%Y %l:%M:%S %p" + "%m/%d/%Y %H:%M:%S" + "%d/%b/%y %H:%M:%S" + "%m%d %H:%M:%S" + "%Y%m%d %H:%M:%S" + "%Y%m%d.%H%M%S" + "%H:%M:%S" + "%M:%S" + "%m/%d %H:%M:%S" + "%Y-%m-%d" + "%Y-%m" + "%Y/%m/%d" + "%Y/%m" + "%s.%f") + +set(GEN_SRCS "") + +add_custom_command(OUTPUT time_fmts.cc COMMAND ptimec ${TIME_FORMATS} > + time_fmts.cc) + +add_library(lnavdt STATIC config.h.in ptimec.hh ptimec_rt.cc time_fmts.cc) +target_include_directories(lnavdt PUBLIC . ${CMAKE_CURRENT_BINARY_DIR}) + +function(bin2c) + cmake_parse_arguments(BIN2C_ "" "VARNAME" "" ${ARGN}) + + list(TRANSFORM BIN2C_UNPARSED_ARGUMENTS "\\." "-") + add_custom_command( + OUTPUT "${DST_FILE}.h" "${DST_FILE}.cc" + COMMAND bin2c "${DST_FILE}" "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_TO_LINK}" + DEPENDS bin2c "${FILE_TO_LINK}") +endfunction(bin2c) + +foreach (FILE_TO_LINK animals.json ansi-palette.json diseases.json emojis.json xml-entities.json xterm-palette.json help.txt help.md init.sql words.json) + string(REPLACE "." "-" DST_FILE "${FILE_TO_LINK}") + add_custom_command( + OUTPUT "${DST_FILE}.h" "${DST_FILE}.cc" + COMMAND bin2c "${DST_FILE}" "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_TO_LINK}" + DEPENDS bin2c "${FILE_TO_LINK}") + list(APPEND GEN_SRCS "${CMAKE_CURRENT_BINARY_DIR}/${DST_FILE}.h" + "${CMAKE_CURRENT_BINARY_DIR}/${DST_FILE}.cc") +endforeach (FILE_TO_LINK) + +set(FORMAT_FILES + formats/access_log.json + formats/alb_log.json + formats/block_log.json + formats/candlepin_log.json + formats/choose_repo_log.json + formats/cups_log.json + formats/dpkg_log.json + formats/elb_log.json + formats/engine_log.json + formats/error_log.json + formats/esx_syslog_log.json + formats/fsck_hfs_log.json + formats/glog_log.json + formats/haproxy_log.json + formats/java_log.json + formats/journald_json_log.json + formats/katello_log.json + formats/openam_log.json + formats/openamdb_log.json + formats/openstack_log.json + formats/page_log.json + formats/papertrail_log.json + formats/pcap_log.json + formats/procstate_log.json + formats/snaplogic_log.json + formats/sssd_log.json + formats/strace_log.json + formats/sudo_log.json + formats/syslog_log.json + formats/s3_log.json + formats/tcf_log.json + formats/tcsh_history.json + formats/uwsgi_log.json + formats/vdsm_log.json + formats/vmk_log.json + formats/vmw_log.json + formats/vmw_vc_svc_log.json + formats/vmw_py_log.json + formats/xmlrpc_log.json) + +set(FORMAT_FILE_PATHS ${FORMAT_FILES}) + +list(TRANSFORM FORMAT_FILE_PATHS PREPEND "${CMAKE_CURRENT_SOURCE_DIR}/") + +add_custom_command( + OUTPUT default-formats.h default-formats.cc + COMMAND bin2c -n lnav_format_json default-formats ${FORMAT_FILE_PATHS} + DEPENDS bin2c ${FORMAT_FILES}) +list(APPEND GEN_SRCS default-formats.h default-formats.cc) + +set(CONFIG_FILES + root-config.json + keymaps/de-keymap.json + keymaps/default-keymap.json + keymaps/fr-keymap.json + keymaps/uk-keymap.json + keymaps/us-keymap.json + themes/default-theme.json + themes/grayscale.json + themes/eldar.json + themes/monocai.json + themes/night-owl.json + themes/solarized-dark.json + themes/solarized-light.json) + +set(CONFIG_FILE_PATHS ${CONFIG_FILES}) + +list(TRANSFORM CONFIG_FILE_PATHS PREPEND "${CMAKE_CURRENT_SOURCE_DIR}/") + +add_custom_command( + OUTPUT default-config.h default-config.cc + COMMAND bin2c -n lnav_config_json default-config ${CONFIG_FILE_PATHS} + DEPENDS bin2c ${CONFIG_FILES}) +list(APPEND GEN_SRCS default-config.h default-config.cc) + +set(BUILTIN_LNAV_SCRIPTS + scripts/dhclient-summary.lnav scripts/lnav-pop-view.lnav + scripts/partition-by-boot.lnav scripts/rename-stdin.lnav + scripts/search-for.lnav) + +set(BUILTIN_LNAV_SCRIPT_PATHS ${BUILTIN_LNAV_SCRIPTS}) + +list(TRANSFORM BUILTIN_LNAV_SCRIPT_PATHS PREPEND "${CMAKE_CURRENT_SOURCE_DIR}/") + +add_custom_command( + OUTPUT builtin-scripts.h builtin-scripts.cc + COMMAND bin2c -n lnav_scripts builtin-scripts ${BUILTIN_LNAV_SCRIPT_PATHS} + DEPENDS bin2c ${BUILTIN_LNAV_SCRIPTS}) +list(APPEND GEN_SRCS builtin-scripts.h builtin-scripts.cc) + +set(BUILTIN_SH_SCRIPTS scripts/dhclient-summary.lnav scripts/lnav-pop-view.lnav + scripts/partition-by-boot.lnav scripts/search-for.lnav) + +set(BUILTIN_SH_SCRIPT_PATHS ${BUILTIN_SH_SCRIPTS}) + +list(TRANSFORM BUILTIN_SH_SCRIPT_PATHS PREPEND "${CMAKE_CURRENT_SOURCE_DIR}/") + +add_custom_command( + OUTPUT builtin-sh-scripts.h builtin-sh-scripts.cc + COMMAND bin2c -n lnav_sh_scripts builtin-sh-scripts ${BUILTIN_SH_SCRIPT_PATHS} + DEPENDS bin2c ${BUILTIN_SH_SCRIPTS}) +list(APPEND GEN_SRCS builtin-sh-scripts.h builtin-sh-scripts.cc) + +add_library( + cppfmt STATIC + fmtlib/format.cc + fmtlib/os.cc + fmtlib/fmt/args.h + fmtlib/fmt/chrono.h + fmtlib/fmt/color.h + fmtlib/fmt/compile.h + fmtlib/fmt/core.h + fmtlib/fmt/format-inl.h + fmtlib/fmt/format.h + fmtlib/fmt/locale.h + fmtlib/fmt/os.h + fmtlib/fmt/ostream.h + fmtlib/fmt/printf.h + fmtlib/fmt/ranges.h + fmtlib/fmt/std.h + fmtlib/fmt/xchar.h +) +target_include_directories(cppfmt PUBLIC fmtlib) + +add_library( + cppscnlib STATIC + third-party/scnlib/src/reader_float.cpp + third-party/scnlib/src/reader_int.cpp + third-party/scnlib/src/locale.cpp + third-party/scnlib/src/file.cpp + third-party/scnlib/src/vscan.cpp + + third-party/scnlib/include/scn/reader/reader.h + third-party/scnlib/include/scn/reader/float.h + third-party/scnlib/include/scn/reader/types.h + third-party/scnlib/include/scn/reader/int.h + third-party/scnlib/include/scn/reader/common.h + third-party/scnlib/include/scn/reader/string.h + third-party/scnlib/include/scn/ranges/custom_impl.h + third-party/scnlib/include/scn/ranges/std_impl.h + third-party/scnlib/include/scn/ranges/ranges.h + third-party/scnlib/include/scn/ranges/util.h + third-party/scnlib/include/scn/fwd.h + third-party/scnlib/include/scn/util/algorithm.h + third-party/scnlib/include/scn/util/small_vector.h + third-party/scnlib/include/scn/util/optional.h + third-party/scnlib/include/scn/util/expected.h + third-party/scnlib/include/scn/util/array.h + third-party/scnlib/include/scn/util/unique_ptr.h + third-party/scnlib/include/scn/util/math.h + third-party/scnlib/include/scn/util/memory.h + third-party/scnlib/include/scn/util/span.h + third-party/scnlib/include/scn/util/meta.h + third-party/scnlib/include/scn/util/string_view.h + third-party/scnlib/include/scn/unicode/unicode.h + third-party/scnlib/include/scn/unicode/common.h + third-party/scnlib/include/scn/unicode/utf16.h + third-party/scnlib/include/scn/unicode/utf8.h + third-party/scnlib/include/scn/all.h + third-party/scnlib/include/scn/tuple_return/tuple_return.h + third-party/scnlib/include/scn/tuple_return/util.h + third-party/scnlib/include/scn/scan/ignore.h + third-party/scnlib/include/scn/scan/getline.h + third-party/scnlib/include/scn/scan/list.h + third-party/scnlib/include/scn/scan/common.h + third-party/scnlib/include/scn/scan/istream.h + third-party/scnlib/include/scn/scan/vscan.h + third-party/scnlib/include/scn/scan/scan.h + third-party/scnlib/include/scn/tuple_return.h + third-party/scnlib/include/scn/detail/error.h + third-party/scnlib/include/scn/detail/fwd.h + third-party/scnlib/include/scn/detail/range.h + third-party/scnlib/include/scn/detail/locale.h + third-party/scnlib/include/scn/detail/config.h + third-party/scnlib/include/scn/detail/file.h + third-party/scnlib/include/scn/detail/context.h + third-party/scnlib/include/scn/detail/result.h + third-party/scnlib/include/scn/detail/visitor.h + third-party/scnlib/include/scn/detail/args.h + third-party/scnlib/include/scn/detail/parse_context.h + third-party/scnlib/include/scn/detail/vectored.h + third-party/scnlib/include/scn/scn.h + third-party/scnlib/include/scn/istream.h +) +target_include_directories(cppscnlib PRIVATE third-party/scnlib/src/deps/fast_float/single_include) +target_include_directories(cppscnlib PUBLIC third-party/scnlib/include) + +add_library( + base64 STATIC + third-party/base64/lib/lib.c + third-party/base64/lib/arch/generic/codec.c + third-party/base64/lib/tables/tables.c +) +target_include_directories(base64 PRIVATE third-party/base64/lib) +target_include_directories(base64 PUBLIC third-party/base64/include) + +add_library( + spookyhash STATIC + spookyhash/SpookyV2.cpp +) + +add_library(lnavfileio STATIC + grep_proc.hh + line_buffer.hh + pollable.hh + shared_buffer.hh + + grep_proc.cc + line_buffer.cc + pollable.cc + shared_buffer.cc + ) +target_include_directories(lnavfileio PRIVATE . ${CMAKE_CURRENT_BINARY_DIR}) +target_link_libraries(lnavfileio cppfmt spookyhash pcrepp base BZip2::BZip2 ZLIB::ZLIB) + +add_library( + diag STATIC + ${GEN_SRCS} + config.h.in + all_logs_vtab.cc + archive_manager.cc + document.sections.cc + bin2c.hh + bookmarks.cc + bottom_status_source.cc + breadcrumb_curses.cc + collation-functions.cc + column_namer.cc + command_executor.cc + curl_looper.cc + db_sub_source.cc + dump_internals.cc + elem_to_json.cc + environ_vtab.cc + extension-functions.cc + field_overlay_source.cc + file_collection.cc + file_format.cc + file_vtab.cc + files_sub_source.cc + filter_observer.cc + filter_status_source.cc + filter_sub_source.cc + fs-extension-functions.cc + fstat_vtab.cc + fts_fuzzy_match.cc + help_text.cc + help_text_formatter.cc + highlighter.cc + hist_source.cc + hotkeys.cc + input_dispatcher.cc + json-extension-functions.cc + listview_curses.cc + lnav.events.cc + lnav.indexing.cc + lnav.management_cli.cc + lnav_commands.cc + lnav_config.cc + lnav_util.cc + log.watch.cc + log_accel.cc + log_actions.cc + log_data_helper.cc + log_data_table.cc + log_format.cc + log_format_loader.cc + log_level.cc + log_search_table.cc + logfile.cc + logfile_sub_source.cc + md2attr_line.cc + md4cpp.cc + network-extension-functions.cc + data_scanner.cc + data_scanner_re.cc + data_parser.cc + pcap_manager.cc + plain_text_source.cc + pretty_printer.cc + pugixml/pugixml.cpp + readline_callbacks.cc + readline_curses.cc + readline_highlighters.cc + readline_possibilities.cc + regexp_vtab.cc + regex101.client.cc + regex101.import.cc + relative_time.cc + session.export.cc + session_data.cc + sequence_matcher.cc + shlex.cc + sqlite-extension-func.cc + static_file_vtab.cc + statusview_curses.cc + string-extension-functions.cc + sysclip.cc + piper_proc.cc + spectro_impls.cc + spectro_source.cc + sql_commands.cc + sql_util.cc + sqlitepp.cc + state-extension-functions.cc + styling.cc + text_anonymizer.cc + text_format.cc + textfile_highlighters.cc + textfile_sub_source.cc + textview_curses.cc + top_status_source.cc + time-extension-functions.cc + timer.cc + unique_path.cc + unique_path.hh + view_curses.cc + view_helpers.cc + views_vtab.cc + vt52_curses.cc + vtab_module.cc + log_vtab_impl.cc + xml_util.cc + xpath_vtab.cc + xterm_mouse.cc + yaml-extension-functions.cc + third-party/md4c/md4c.c + third-party/sqlite/ext/series.c + third-party/sqlite/ext/dbdump.c + + all_logs_vtab.hh + archive_manager.hh + archive_manager.cfg.hh + document.sections.hh + big_array.hh + bottom_status_source.hh + bound_tags.hh + breadcrumb.hh + breadcrumb_curses.hh + byte_array.hh + command_executor.hh + column_namer.hh + curl_looper.hh + doc_status_source.hh + dump_internals.hh + elem_to_json.hh + field_overlay_source.hh + file_collection.hh + file_format.hh + files_sub_source.hh + filter_observer.hh + filter_status_source.hh + filter_sub_source.hh + fstat_vtab.hh + fts_fuzzy_match.hh + grep_highlighter.hh + help_text.hh + help_text_formatter.hh + highlighter.hh + hotkeys.hh + input_dispatcher.hh + itertools.similar.hh + k_merge_tree.h + lnav.events.hh + lnav.indexing.hh + lnav.management_cli.hh + lnav_config.hh + lnav_config_fwd.hh + lnav_util.hh + log.watch.hh + log_actions.hh + log_data_helper.hh + log_data_table.hh + log_format.hh + log_format_ext.hh + log_format_fwd.hh + log_format_impls.cc + log_gutter_source.hh + log_level.hh + log_search_table.hh + log_search_table_fwd.hh + logfile_sub_source.cfg.hh + logfile.hh + logfile_fwd.hh + logfile_stats.hh + md2attr_line.hh + md4cpp.hh + optional.hpp + pcap_manager.hh + plain_text_source.hh + pretty_printer.hh + preview_status_source.hh + pugixml/pugiconfig.hpp + pugixml/pugixml.hpp + readline_callbacks.hh + readline_context.hh + readline_possibilities.hh + regex101.client.hh + regex101.import.hh + regexp_vtab.hh + relative_time.hh + styling.hh + ring_span.hh + safe/accessmode.h + safe/defaulttypes.h + safe/mutableref.h + safe/safe.h + session.export.hh + sequence_sink.hh + shlex.hh + shlex.resolver.hh + simdutf8check.h + spectro_impls.hh + spectro_source.hh + sqlitepp.hh + sql_help.hh + sql_util.hh + static_file_vtab.hh + strong_int.hh + sysclip.hh + sysclip.cfg.hh + term_extra.hh + termios_guard.hh + text_anonymizer.hh + text_format.hh + textfile_highlighters.hh + textfile_sub_source.hh + textview_curses.hh + textview_curses_fwd.hh + time_T.hh + timer.hh + top_status_source.hh + url_loader.hh + view_helpers.hh + view_helpers.crumbs.hh + view_helpers.examples.hh + view_helpers.hist.hh + views_vtab.hh + vis_line.hh + vtab_module.hh + vtab_module_json.hh + xml_util.hh + xpath_vtab.hh + mapbox/recursive_wrapper.hpp + mapbox/variant.hpp + mapbox/variant_io.hpp + mapbox/variant_visitor.hpp + ghc/filesystem.hpp + ghc/fs_fwd.hpp + ghc/fs_impl.hpp + ghc/fs_std.hpp + ghc/fs_std_fwd.hpp + ghc/fs_std_impl.hpp + ww898/cp_utf8.hpp + log_level_re.cc + + third-party/ArenaAlloc/arenaalloc.h + third-party/ArenaAlloc/arenaallocimpl.h + + third-party/CLI/StringTools.hpp + third-party/CLI/App.hpp + third-party/CLI/Macros.hpp + third-party/CLI/Option.hpp + third-party/CLI/Config.hpp + third-party/CLI/CLI.hpp + third-party/CLI/Formatter.hpp + third-party/CLI/Error.hpp + third-party/CLI/Version.hpp + third-party/CLI/Timer.hpp + third-party/CLI/FormatterFwd.hpp + third-party/CLI/Validators.hpp + third-party/CLI/Split.hpp + third-party/CLI/TypeTools.hpp + third-party/CLI/ConfigFwd.hpp + + third-party/intervaltree/IntervalTree.h + + third-party/md4c/md4c.h + + third-party/robin_hood/robin_hood.h +) + +set(lnav_SRCS lnav.cc) + +target_include_directories(diag PUBLIC . fmtlib ${CMAKE_CURRENT_BINARY_DIR} + third-party + third-party/base64/include + third-party/rapidyaml + ) + +target_link_libraries( + diag + base + lnavdt + lnavfileio + pcrepp + tailerservice + tailerpp + tailercommon + logfmt + yajlpp + cppfmt + base64 + spookyhash + ${lnav_LIBS}) +target_compile_definitions(diag PRIVATE SQLITE_OMIT_LOAD_EXTENSION) + +check_library_exists(util openpty "" HAVE_LIBUTIL) + +if (HAVE_LIBUTIL) + target_link_libraries(diag util) +endif () + +add_executable(lnav ${lnav_SRCS}) +target_link_libraries(lnav diag) + +install(TARGETS lnav DESTINATION bin) diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..24eaaf1 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,548 @@ + +include $(top_srcdir)/aminclude_static.am + +CXXFLAGS = + +SUBDIRS = \ + fmtlib \ + third-party/base64/lib \ + third-party/scnlib/src \ + pcrepp \ + base \ + tailer \ + pugixml \ + yajl \ + yajlpp \ + formats/logfmt \ + . + +bin_PROGRAMS = lnav + +noinst_PROGRAMS = lnav-test + +noinst_LIBRARIES = libdiag.a libdatascanner.a + +PTIME_V = $(PTIME_V_@AM_V@) +PTIME_V_ = $(PTIME_V_@AM_DEFAULT_V@) +PTIME_V_0 = @echo " TIMEFMT " $@; + +BIN2C_V = $(BIN2C_V_@AM_V@) +BIN2C_V_ = $(BIN2C_V_@AM_DEFAULT_V@) +BIN2C_V_0 = @echo " BIN2C " $@; + +RE2C_V = $(RE2C_V_@AM_V@) +RE2C_V_ = $(RE2C_V_@AM_DEFAULT_V@) +RE2C_V_0 = @echo " RE2C " $@; + +BIN2C_PATH = ../tools/bin2c$(BUILD_EXEEXT) + +include formats/formats.am + +default-formats.cc: $(BIN2C_PATH) $(FORMAT_FILES) + $(BIN2C_V)$(BIN2C_PATH) -n lnav_format_json default-formats $(FORMAT_FILES) + +include keymaps/keymaps.am +include themes/themes.am + +CONFIG_FILES = \ + $(srcdir)/root-config.json \ + $(KEYMAP_FILES) \ + $(THEME_FILES) \ + $() + +default-config.cc: $(BIN2C_PATH) $(CONFIG_FILES) + $(BIN2C_V)$(BIN2C_PATH) -n lnav_config_json default-config $(CONFIG_FILES) + +include scripts/scripts.am + +builtin-scripts.cc: $(BIN2C_PATH) $(BUILTIN_LNAVSCRIPTS) + $(BIN2C_V)$(BIN2C_PATH) -n lnav_scripts builtin-scripts $(BUILTIN_LNAVSCRIPTS) + +builtin-sh-scripts.cc: $(BIN2C_PATH) $(BUILTIN_SHSCRIPTS) + $(BIN2C_V)$(BIN2C_PATH) -n lnav_sh_scripts builtin-sh-scripts $(BUILTIN_SHSCRIPTS) + +%-sh.cc: $(srcdir)/%.sh $(BIN2C_PATH) + $(BIN2C_V)$(BIN2C_PATH) $(*)-sh $< + +%-txt.cc: $(srcdir)/%.txt $(BIN2C_PATH) + $(BIN2C_V)$(BIN2C_PATH) $(*)-txt $< + +%-md.cc: $(srcdir)/%.md $(BIN2C_PATH) + $(BIN2C_V)$(BIN2C_PATH) $(*)-md $< + +%-sql.cc: $(srcdir)/%.sql $(BIN2C_PATH) + $(BIN2C_V)$(BIN2C_PATH) $(*)-sql $< + +%-lnav.cc: $(srcdir)/%.lnav $(BIN2C_PATH) + $(BIN2C_V)$(BIN2C_PATH) $(*)-lnav $< + +%-json.cc: $(srcdir)/%.json $(BIN2C_PATH) + $(BIN2C_V)$(BIN2C_PATH) $(*)-json $< + +include time_formats.am + +time_fmts.cc: ptimec$(BUILD_EXEEXT) + $(PTIME_V)./ptimec$(BUILD_EXEEXT) $(TIME_FORMATS) > $@ + +if HAVE_RE2C +%.cc: %.re + $(RE2C_V)$(RE2C_CMD) --bit-vectors -W --tags -8 -o $@ $< + $(REC2_V)test $@ -ef $(srcdir)/$*.cc || cp $@ $(srcdir)/$*.cc +endif + +LNAV_BUILT_FILES = \ + animals-json.cc \ + ansi-palette-json.cc \ + builtin-scripts.cc \ + builtin-sh-scripts.cc \ + default-config.cc \ + default-formats.cc \ + diseases-json.cc \ + emojis-json.cc \ + words-json.cc \ + help-md.cc \ + init-sql.cc \ + time_fmts.cc \ + xml-entities-json.cc \ + xterm-palette-json.cc + +BUILT_SOURCES = $(LNAV_BUILT_FILES) + +AM_LIBS = $(CODE_COVERAGE_LIBS) +AM_CFLAGS = $(CODE_COVERAGE_CFLAGS) +AM_CXXFLAGS = $(CODE_COVERAGE_CXXFLAGS) $(USER_CXXFLAGS) + +AM_LDFLAGS = \ + $(STATIC_LDFLAGS) \ + $(LIBARCHIVE_LDFLAGS) \ + $(READLINE_LDFLAGS) \ + $(SQLITE3_LDFLAGS) \ + $(PCRE_LDFLAGS) + +AM_CPPFLAGS = \ + -DSYSCONFDIR='"$(sysconfdir)"' \ + -DSQLITE_OMIT_LOAD_EXTENSION \ + -I$(srcdir)/fmtlib \ + -I$(srcdir)/third-party \ + -I$(srcdir)/third-party/base64/include \ + -I$(srcdir)/third-party/rapidyaml \ + -I$(top_srcdir)/src/third-party/scnlib/include \ + -Wall \ + $(CODE_COVERAGE_CPPFLAGS) \ + $(LIBARCHIVE_CFLAGS) \ + $(READLINE_CFLAGS) \ + $(SQLITE3_CFLAGS) \ + $(PCRE_CFLAGS) \ + $(LIBCURL_CPPFLAGS) + +LDADD = \ + libdiag.a \ + libdatascanner.a \ + base/libbase.a \ + formats/logfmt/liblogfmt.a \ + fmtlib/libcppfmt.a \ + third-party/scnlib/src/libscnlib.a \ + pcrepp/libpcrepp.a \ + pugixml/libpugixml.a \ + tailer/libtailerservice.a \ + tailer/libtailercommon.a \ + tailer/libtailerpp.a \ + yajl/libyajl.a \ + yajlpp/libyajlpp.a \ + third-party/base64/lib/libbase64.a \ + $(READLINE_LIBS) \ + $(CURSES_LIB) \ + $(SQLITE3_LIBS) \ + $(LIBARCHIVE_LIBS) \ + $(LIBCURL) + +# emojis.json is from https://gist.github.com/oliveratgithub/0bf11a9aff0d6da7b46f1490f86a71eb/ +# xml-entities.json is from https://html.spec.whatwg.org/entities.json + +dist_noinst_DATA = \ + alpha-release.sh \ + animals.json \ + ansi-palette.json \ + diseases.json \ + emojis.json \ + $(BUILTIN_LNAVSCRIPTS) \ + $(BUILTIN_SHSCRIPTS) \ + $(CONFIG_FILES) \ + $(FORMAT_FILES) \ + words.json \ + xml-entities.json \ + xterm-palette.json + +noinst_HEADERS = \ + third-party/md4c/md4c.h \ + third-party/rapidyaml/ryml_all.hpp \ + all_logs_vtab.hh \ + archive_manager.hh \ + archive_manager.cfg.hh \ + big_array.hh \ + bin2c.hh \ + bookmarks.hh \ + bottom_status_source.hh \ + bound_tags.hh \ + breadcrumb.hh \ + breadcrumb_curses.hh \ + byte_array.hh \ + column_namer.hh \ + command_executor.hh \ + curl_looper.hh \ + data_scanner.hh \ + data_scanner_re.re \ + data_parser.hh \ + db_sub_source.hh \ + doc_status_source.hh \ + document.sections.hh \ + dump_internals.hh \ + elem_to_json.hh \ + environ_vtab.hh \ + field_overlay_source.hh \ + file_collection.hh \ + file_format.hh \ + file_vtab.cfg.hh \ + files_sub_source.hh \ + filter_observer.hh \ + filter_status_source.hh \ + filter_sub_source.hh \ + fstat_vtab.hh \ + fts_fuzzy_match.hh \ + grep_highlighter.hh \ + grep_proc.hh \ + help.md \ + help.txt \ + help_text.hh \ + help_text_formatter.hh \ + highlighter.hh \ + hist_source.hh \ + hotkeys.hh \ + init.sql \ + input_dispatcher.hh \ + itertools.similar.hh \ + k_merge_tree.h \ + line_buffer.hh \ + listview_curses.hh \ + lnav.hh \ + lnav.events.hh \ + lnav.indexing.hh \ + lnav.management_cli.hh \ + lnav_commands.hh \ + lnav_config.hh \ + lnav_config_fwd.hh \ + lnav_util.hh \ + log.watch.hh \ + log_accel.hh \ + log_actions.hh \ + log_data_helper.hh \ + log_data_table.hh \ + log_format.hh \ + log_format_ext.hh \ + log_format_fwd.hh \ + log_format_loader.hh \ + log_gutter_source.hh \ + log_level.hh \ + log_level_re.re \ + log_search_table.hh \ + log_search_table_fwd.hh \ + logfile.hh \ + logfile.cfg.hh \ + logfile_fwd.hh \ + logfile_sub_source.hh \ + logfile_sub_source.cfg.hh \ + mapbox/recursive_wrapper.hpp \ + mapbox/variant.hpp \ + mapbox/variant_io.hpp \ + mapbox/variant_visitor.hpp \ + md2attr_line.hh \ + md4cpp.hh \ + optional.hpp \ + pcap_manager.hh \ + piper_proc.hh \ + plain_text_source.hh \ + pollable.hh \ + pretty_printer.hh \ + preview_status_source.hh \ + ptimec.hh \ + readline_callbacks.hh \ + readline_context.hh \ + readline_curses.hh \ + readline_highlighters.hh \ + readline_possibilities.hh \ + regex101.client.hh \ + regex101.import.hh \ + regexp_vtab.hh \ + relative_time.hh \ + ring_span.hh \ + safe/accessmode.h \ + safe/defaulttypes.h \ + safe/mutableref.h \ + safe/safe.h \ + service_tags.hh \ + session.export.hh \ + session_data.hh \ + shared_buffer.hh \ + shlex.hh \ + shlex.resolver.hh \ + simdutf8check.h \ + spectro_impls.hh \ + spectro_source.hh \ + sqlitepp.hh \ + sqlitepp.client.hh \ + sql_help.hh \ + sql_util.hh \ + sqlite-extension-func.hh \ + static_file_vtab.hh \ + styling.hh \ + statusview_curses.hh \ + strong_int.hh \ + sysclip.hh \ + sysclip.cfg.hh \ + termios_guard.hh \ + term_extra.hh \ + text_anonymizer.hh \ + text_format.hh \ + textfile_highlighters.hh \ + textfile_sub_source.hh \ + textview_curses.hh \ + textview_curses_fwd.hh \ + time_T.hh \ + timer.hh \ + top_status_source.hh \ + top_status_source.cfg.hh \ + unique_path.hh \ + url_loader.hh \ + view_curses.hh \ + view_helpers.hh \ + view_helpers.crumbs.hh \ + view_helpers.examples.hh \ + view_helpers.hist.hh \ + views_vtab.hh \ + vis_line.hh \ + vt52_curses.hh \ + vtab_module.hh \ + vtab_module_json.hh \ + log_vtab_impl.hh \ + log_format_impls.cc \ + xml_util.hh \ + xpath_vtab.hh \ + xterm_mouse.hh \ + spookyhash/SpookyV2.h \ + ghc/filesystem.hpp \ + ghc/fs_fwd.hpp \ + ghc/fs_impl.hpp \ + ghc/fs_std.hpp \ + ghc/fs_std_fwd.hpp \ + ghc/fs_std_impl.hpp \ + ww898/cp_utf8.hpp + +nodist_libdiag_a_SOURCES = \ + $(LNAV_BUILT_FILES) + +THIRD_PARTY_SRCS = \ + third-party/ArenaAlloc/arenaalloc.h \ + third-party/ArenaAlloc/arenaallocimpl.h \ + third-party/ArenaAlloc/recyclealloc.h \ + third-party/backward-cpp/backward.hpp \ + third-party/CLI/StringTools.hpp \ + third-party/CLI/App.hpp \ + third-party/CLI/Macros.hpp \ + third-party/CLI/Option.hpp \ + third-party/CLI/Config.hpp \ + third-party/CLI/CLI.hpp \ + third-party/CLI/Formatter.hpp \ + third-party/CLI/Error.hpp \ + third-party/CLI/Version.hpp \ + third-party/CLI/Timer.hpp \ + third-party/CLI/FormatterFwd.hpp \ + third-party/CLI/Validators.hpp \ + third-party/CLI/Split.hpp \ + third-party/CLI/TypeTools.hpp \ + third-party/CLI/ConfigFwd.hpp \ + third-party/doctest-root/doctest/doctest.h \ + third-party/intervaltree/IntervalTree.h \ + third-party/md4c/md4c.c \ + third-party/robin_hood/robin_hood.h \ + third-party/sqlite/ext/dbdump.c \ + third-party/sqlite/ext/series.c + +libdatascanner_a_SOURCES = \ + data_scanner.cc \ + data_scanner_re.cc + +libdiag_a_SOURCES = \ + $(THIRD_PARTY_SRCS) \ + all_logs_vtab.cc \ + archive_manager.cc \ + bookmarks.cc \ + bottom_status_source.cc \ + breadcrumb_curses.cc \ + collation-functions.cc \ + column_namer.cc \ + command_executor.cc \ + curl_looper.cc \ + db_sub_source.cc \ + document.sections.cc \ + dump_internals.cc \ + elem_to_json.cc \ + environ_vtab.cc \ + extension-functions.cc \ + field_overlay_source.cc \ + file_collection.cc \ + file_format.cc \ + files_sub_source.cc \ + filter_observer.cc \ + filter_status_source.cc \ + filter_sub_source.cc \ + fstat_vtab.cc \ + fs-extension-functions.cc \ + fts_fuzzy_match.cc \ + grep_proc.cc \ + help_text.cc \ + help_text_formatter.cc \ + highlighter.cc \ + hist_source.cc \ + hotkeys.cc \ + input_dispatcher.cc \ + json-extension-functions.cc \ + line_buffer.cc \ + listview_curses.cc \ + lnav_commands.cc \ + lnav_config.cc \ + lnav_util.cc \ + log.watch.cc \ + log_accel.cc \ + log_actions.cc \ + log_data_helper.cc \ + log_data_table.cc \ + log_format.cc \ + log_format_loader.cc \ + log_level.cc \ + log_level_re.cc \ + log_search_table.cc \ + logfile.cc \ + logfile_sub_source.cc \ + md2attr_line.cc \ + md4cpp.cc \ + network-extension-functions.cc \ + data_parser.cc \ + pcap_manager.cc \ + plain_text_source.cc \ + pollable.cc \ + pretty_printer.cc \ + ptimec_rt.cc \ + readline_callbacks.cc \ + readline_curses.cc \ + readline_highlighters.cc \ + readline_possibilities.cc \ + regex101.client.cc \ + regex101.import.cc \ + regexp_vtab.cc \ + relative_time.cc \ + session.export.cc \ + session_data.cc \ + shared_buffer.cc \ + shlex.cc \ + spectro_impls.cc \ + spectro_source.cc \ + sqlitepp.cc \ + sqlite-extension-func.cc \ + static_file_vtab.cc \ + statusview_curses.cc \ + string-extension-functions.cc \ + styling.cc \ + text_anonymizer.cc \ + text_format.cc \ + textfile_sub_source.cc \ + timer.cc \ + piper_proc.cc \ + sql_commands.cc \ + sql_util.cc \ + state-extension-functions.cc \ + sysclip.cc \ + textfile_highlighters.cc \ + textview_curses.cc \ + time-extension-functions.cc \ + top_status_source.cc \ + unique_path.cc \ + view_curses.cc \ + view_helpers.cc \ + views_vtab.cc \ + vt52_curses.cc \ + vtab_module.cc \ + log_vtab_impl.cc \ + xml_util.cc \ + xpath_vtab.cc \ + xterm_mouse.cc \ + yaml-extension-functions.cc \ + spookyhash/SpookyV2.cpp + +PLUGIN_SRCS = \ + file_vtab.cc + +lnav_SOURCES = \ + lnav.cc \ + lnav.events.cc \ + lnav.indexing.cc \ + lnav.management_cli.cc \ + $(PLUGIN_SRCS) + +lnav_test_SOURCES = \ + lnav.cc \ + lnav.events.cc \ + lnav.indexing.cc \ + lnav.management_cli.cc \ + test_override.c \ + $(PLUGIN_SRCS) + +ptimec$(BUILD_EXEEXT): ptimec.c + $(AM_V_CC) $(CC_FOR_BUILD) $(CPPFLAGS_FOR_BUILD) $(LDFLAGS_FOR_BUILD) -g3 -o $@ $? + +if HAVE_RE2C +RE2C_FILES = data_scanner_re.cc log_level_re.cc +endif + +EXTRA_DIST = \ + ptimec.c + +CLEANFILES = \ + ptimec$(BUILD_EXEEXT) + +DISTCLEANFILES = \ + $(LNAV_BUILT_FILES) \ + animals-json.h \ + ansi-palette-json.h \ + builtin-scripts.h \ + builtin-sh-scripts.h \ + default-config.h \ + default-formats.h \ + diseases-json.h \ + emojis-json.h \ + words-json.h \ + help-md.h \ + init-sql.h \ + time_fmts.h \ + xml-entities-json.h \ + xterm-palette-json.h \ + $(RE2C_FILES) + +distclean-local: + $(RM_V)rm -rf *.dSYM + +uncrusty: + (cd $(srcdir) && uncrustify -c ../lnav.cfg --replace $(SOURCES) \ + $(HEADERS)) + +if !DISABLE_DOCUMENTATION +all-local: $(LNAV_BUILT_FILES) lnav + if test -w $(srcdir)/internals; then \ + env DUMP_INTERNALS_DIR=$(srcdir)/internals DUMP_CRASH=1 ./lnav Makefile; \ + mv $(srcdir)/internals/*.schema.json $(top_srcdir)/docs/schemas; \ + fi +else +all-local: $(LNAV_BUILT_FILES) +endif + +install-exec-hook: + bash $(srcdir)/alpha-release.sh diff --git a/src/all_logs_vtab.cc b/src/all_logs_vtab.cc new file mode 100644 index 0000000..f4468a6 --- /dev/null +++ b/src/all_logs_vtab.cc @@ -0,0 +1,99 @@ +/** + * Copyright (c) 2020, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "all_logs_vtab.hh" + +#include "base/attr_line.hh" +#include "config.h" + +static auto intern_lifetime = intern_string::get_table_lifetime(); + +all_logs_vtab::all_logs_vtab() + : log_vtab_impl(intern_string::lookup("all_logs")), + alv_msg_meta( + intern_string::lookup("log_msg_format"), value_kind_t::VALUE_TEXT, 0), + alv_schema_meta( + intern_string::lookup("log_msg_schema"), value_kind_t::VALUE_TEXT, 1) +{ + this->alv_msg_meta.lvm_identifier = true; + this->alv_schema_meta.lvm_identifier = true; +} + +void +all_logs_vtab::get_columns(std::vector& cols) const +{ + cols.emplace_back( + vtab_column(this->alv_msg_meta.lvm_name.get()) + .with_comment( + "The message format with variables replaced by hash marks")); + cols.emplace_back(this->alv_schema_meta.lvm_name.get(), + SQLITE3_TEXT, + "", + true, + "The ID for the message schema"); +} + +void +all_logs_vtab::extract(logfile* lf, + uint64_t line_number, + logline_value_vector& values) +{ + auto& line = values.lvv_sbr; + auto* format = lf->get_format_ptr(); + + logline_value_vector sub_values; + + this->vi_attrs.clear(); + sub_values.lvv_sbr = line; + format->annotate(line_number, this->vi_attrs, sub_values, false); + + auto body = find_string_attr_range(this->vi_attrs, &SA_BODY); + if (body.lr_start == -1) { + body.lr_start = 0; + body.lr_end = line.length(); + } + + data_scanner ds( + line.to_string_fragment().sub_range(body.lr_start, body.lr_end)); + data_parser dp(&ds); + std::string str; + + dp.dp_msg_format = &str; + dp.parse(); + + values.lvv_values.emplace_back(this->alv_msg_meta, std::move(str)); + values.lvv_values.emplace_back(this->alv_schema_meta, + dp.dp_schema_id.to_string()); +} + +bool +all_logs_vtab::next(log_cursor& lc, logfile_sub_source& lss) +{ + return true; +} diff --git a/src/all_logs_vtab.hh b/src/all_logs_vtab.hh new file mode 100644 index 0000000..cab0a4d --- /dev/null +++ b/src/all_logs_vtab.hh @@ -0,0 +1,60 @@ +/** + * Copyright (c) 2015, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef lnav_all_logs_vtab_hh +#define lnav_all_logs_vtab_hh + +#include + +#include "data_parser.hh" +#include "log_vtab_impl.hh" + +/** + * A virtual table that provides access to all log messages from all formats. + * + * @feature f0:sql.tables.all_logs + */ +class all_logs_vtab : public log_vtab_impl { +public: + all_logs_vtab(); + + void get_columns(std::vector& cols) const override; + + void extract(logfile* lf, + uint64_t line_number, + logline_value_vector& values) override; + + bool next(log_cursor& lc, logfile_sub_source& lss) override; + +private: + logline_value_meta alv_msg_meta; + logline_value_meta alv_schema_meta; +}; + +#endif // LNAV_ALL_LOGS_VTAB_HH diff --git a/src/alpha-release.sh b/src/alpha-release.sh new file mode 100755 index 0000000..d0104ab --- /dev/null +++ b/src/alpha-release.sh @@ -0,0 +1,13 @@ +#!/bin/bash + + +if test x"${TRAVIS_BUILD_DIR}" == x""; then + exit 0 +fi + +cp lnav ${TRAVIS_BUILD_DIR}/ +cd ${TRAVIS_BUILD_DIR} + +ldd lnav +VERSION=`git describe --tags` +zip lnav-${VERSION}-linux-64bit.zip lnav diff --git a/src/animals.json b/src/animals.json new file mode 100644 index 0000000..46d2e3d --- /dev/null +++ b/src/animals.json @@ -0,0 +1 @@ +{"data":["meerkat","aardvark","addax","alligator","alpaca","anteater","antelope","aoudad","ape","argali","armadillo","baboon","badger","basilisk","bat","bear","beaver","bighorn","bison","boar","budgerigar","buffalo","bull","bunny","burro","camel","canary","capybara","cat","chameleon","chamois","cheetah","chimpanzee","chinchilla","chipmunk","civet","coati","colt","cougar","cow","coyote","crocodile","crow","deer","dingo","doe","dung-beetle","dog","donkey","dormouse","dromedary","duckbill-platypus","dugong","eland","elephant","elk","ermine","ewe","fawn","ferret","finch","fish","fox","frog","gazelle","gemsbok","gila-monster","giraffe","gnu","goat","gopher","gorilla","grizzly-bear","ground-hog","guanaco","guinea-pig","hamster","hare","hartebeest","hedgehog","highland-cow","hippopotamus","hog","horse","hyena","ibex","iguana","impala","jackal","jaguar","jerboa","kangaroo","kitten","koala","lamb","lemur","leopard","lion","lizard","llama","lovebird","lynx","mandrill","mare","marmoset","marten","mink","mole","mongoose","monkey","moose","mountain-goat","mouse","mule","musk-deer","musk-ox","muskrat","mustang","mynah-bird","newt","ocelot","okapi","opossum","orangutan","oryx","otter","ox","panda","panther","parakeet","parrot","peccary","pig","octopus","thorny-devil","starfish","blue-crab","snowy-owl","chicken","rooster","bumble-bee","eagle-owl","polar-bear","pony","porcupine","porpoise","prairie-dog","pronghorn","puma","puppy","quagga","rabbit","raccoon","ram","rat","reindeer","rhinoceros","salamander","seal","sheep","shrew","silver-fox","skunk","sloth","snake","springbok","squirrel","stallion","steer","tapir","tiger","toad","turtle","vicuna","walrus","warthog","waterbuck","weasel","whale","wildcat","bald-eagle","wolf","wolverine","wombat","woodchuck","yak","zebra","zebu"]} \ No newline at end of file diff --git a/src/ansi-palette.json b/src/ansi-palette.json new file mode 100644 index 0000000..f419ba6 --- /dev/null +++ b/src/ansi-palette.json @@ -0,0 +1,122 @@ +[ + { + "colorId": 0, + "hexString": "#000000", + "rgb": { + "r": 0, + "g": 0, + "b": 0 + }, + "hsl": { + "h": 0, + "s": 0, + "l": 0 + }, + "name": "Black" + }, + { + "colorId": 1, + "hexString": "#800000", + "rgb": { + "r": 128, + "g": 0, + "b": 0 + }, + "hsl": { + "h": 0, + "s": 100, + "l": 25 + }, + "name": "Maroon" + }, + { + "colorId": 2, + "hexString": "#008000", + "rgb": { + "r": 0, + "g": 128, + "b": 0 + }, + "hsl": { + "h": 120, + "s": 100, + "l": 25 + }, + "name": "Green" + }, + { + "colorId": 3, + "hexString": "#808000", + "rgb": { + "r": 128, + "g": 128, + "b": 0 + }, + "hsl": { + "h": 60, + "s": 100, + "l": 25 + }, + "name": "Olive" + }, + { + "colorId": 4, + "hexString": "#000080", + "rgb": { + "r": 0, + "g": 0, + "b": 128 + }, + "hsl": { + "h": 240, + "s": 100, + "l": 25 + }, + "name": "Navy" + }, + { + "colorId": 5, + "hexString": "#800080", + "rgb": { + "r": 128, + "g": 0, + "b": 128 + }, + "hsl": { + "h": 300, + "s": 100, + "l": 25 + }, + "name": "Purple" + }, + { + "colorId": 6, + "hexString": "#008080", + "rgb": { + "r": 0, + "g": 128, + "b": 128 + }, + "hsl": { + "h": 180, + "s": 100, + "l": 25 + }, + "name": "Teal" + }, + { + "colorId": 7, + "hexString": "#c0c0c0", + "rgb": { + "r": 192, + "g": 192, + "b": 192 + }, + "hsl": { + "h": 0, + "s": 0, + "l": 75 + }, + "name": "Silver" + } +] \ No newline at end of file diff --git a/src/archive_manager.cc b/src/archive_manager.cc new file mode 100644 index 0000000..705842e --- /dev/null +++ b/src/archive_manager.cc @@ -0,0 +1,406 @@ +/** + * Copyright (c) 2020, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @file archive_manager.cc + */ + +#include + +#include "config.h" + +#if HAVE_ARCHIVE_H +# include "archive.h" +# include "archive_entry.h" +#endif + +#include "archive_manager.cfg.hh" +#include "archive_manager.hh" +#include "base/auto_fd.hh" +#include "base/auto_mem.hh" +#include "base/fs_util.hh" +#include "base/humanize.hh" +#include "base/injector.hh" +#include "base/lnav_log.hh" +#include "base/paths.hh" +#include "fmt/format.h" +#include "lnav_util.hh" + +namespace fs = ghc::filesystem; + +namespace archive_manager { + +#if HAVE_ARCHIVE_H +/** + * Enables a subset of the supported archive formats to speed up detection, + * since some formats, like xar are unlikely to be used. + */ +static void +enable_desired_archive_formats(archive* arc) +{ + /** @feature f0:archive.formats */ + archive_read_support_format_7zip(arc); + archive_read_support_format_cpio(arc); + archive_read_support_format_lha(arc); + archive_read_support_format_rar(arc); + archive_read_support_format_tar(arc); + archive_read_support_format_zip(arc); +} +#endif + +bool +is_archive(const fs::path& filename) +{ +#if HAVE_ARCHIVE_H + auto_mem arc(archive_read_free); + + arc = archive_read_new(); + + archive_read_support_filter_all(arc); + enable_desired_archive_formats(arc); + archive_read_support_format_raw(arc); + log_debug("read open %s", filename.c_str()); + auto r = archive_read_open_filename(arc, filename.c_str(), 128 * 1024); + if (r == ARCHIVE_OK) { + struct archive_entry* entry = nullptr; + + const auto* format_name = archive_format_name(arc); + + log_debug("read next header %s %s", format_name, filename.c_str()); + if (archive_read_next_header(arc, &entry) == ARCHIVE_OK) { + log_debug("read next done %s", filename.c_str()); + + static const auto RAW_FORMAT_NAME = string_fragment("raw"); + static const auto GZ_FILTER_NAME = string_fragment("gzip"); + + format_name = archive_format_name(arc); + + if (RAW_FORMAT_NAME == format_name) { + auto filter_count = archive_filter_count(arc); + + if (filter_count == 1) { + return false; + } + + const auto* first_filter_name = archive_filter_name(arc, 0); + if (filter_count == 2 && GZ_FILTER_NAME == first_filter_name) { + return false; + } + } + log_info( + "detected archive: %s -- %s", filename.c_str(), format_name); + return true; + } + + log_info("archive read header failed: %s -- %s", + filename.c_str(), + archive_error_string(arc)); + } else { + log_info("archive open failed: %s -- %s", + filename.c_str(), + archive_error_string(arc)); + } +#endif + + return false; +} + +static fs::path +archive_cache_path() +{ + return lnav::paths::workdir() / "archives"; +} + +fs::path +filename_to_tmp_path(const std::string& filename) +{ + auto fn_path = fs::path(filename); + auto basename = fn_path.filename().string(); + hasher h; + + h.update(basename); + auto fd = auto_fd(lnav::filesystem::openp(filename, O_RDONLY)); + if (fd != -1) { + char buffer[1024]; + int rc; + + rc = read(fd, buffer, sizeof(buffer)); + if (rc >= 0) { + h.update(buffer, rc); + } + } + basename = fmt::format(FMT_STRING("arc-{}-{}"), h.to_string(), basename); + + return archive_cache_path() / basename; +} + +#if HAVE_ARCHIVE_H +static walk_result_t +copy_data(const std::string& filename, + struct archive* ar, + struct archive_entry* entry, + struct archive* aw, + const fs::path& entry_path, + struct extract_progress* ep) +{ + int r; + const void* buff; + size_t size, total = 0, next_space_check = 0; + la_int64_t offset; + + for (;;) { + if (total >= next_space_check) { + const auto& cfg = injector::get(); + auto tmp_space = fs::space(entry_path); + + if (tmp_space.available < cfg.amc_min_free_space) { + return Err(fmt::format( + FMT_STRING("available space on disk ({}) is below the " + "minimum-free threshold ({}). Unable to unpack " + "'{}' to '{}'"), + humanize::file_size(tmp_space.available, + humanize::alignment::none), + humanize::file_size(cfg.amc_min_free_space, + humanize::alignment::none), + entry_path.filename().string(), + entry_path.parent_path().string())); + } + next_space_check += 1024 * 1024; + } + + r = archive_read_data_block(ar, &buff, &size, &offset); + if (r == ARCHIVE_EOF) { + return Ok(); + } + if (r != ARCHIVE_OK) { + return Err(fmt::format( + FMT_STRING("failed to extract '{}' from archive '{}' -- {}"), + archive_entry_pathname_utf8(entry), + filename, + archive_error_string(ar))); + } + r = archive_write_data_block(aw, buff, size, offset); + if (r != ARCHIVE_OK) { + return Err(fmt::format(FMT_STRING("failed to write file: {} -- {}"), + entry_path.string(), + archive_error_string(aw))); + } + + total += size; + ep->ep_out_size.fetch_add(size); + } +} + +static walk_result_t +extract(const std::string& filename, const extract_cb& cb) +{ + static const int FLAGS = ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_PERM + | ARCHIVE_EXTRACT_ACL | ARCHIVE_EXTRACT_FFLAGS; + + std::error_code ec; + auto tmp_path = filename_to_tmp_path(filename); + + fs::create_directories(tmp_path.parent_path(), ec); + if (ec) { + return Err(fmt::format("unable to create directory: {} -- {}", + tmp_path.parent_path().string(), + ec.message())); + } + + auto arc_lock = lnav::filesystem::file_lock(tmp_path); + auto lock_guard = lnav::filesystem::file_lock::guard(&arc_lock); + auto done_path = tmp_path; + + done_path += ".done"; + + if (fs::exists(done_path)) { + size_t file_count = 0; + if (fs::is_directory(tmp_path)) { + for (const auto& entry : fs::directory_iterator(tmp_path)) { + (void) entry; + file_count += 1; + } + } + if (file_count > 0) { + fs::last_write_time(done_path, std::chrono::system_clock::now()); + log_info("%s: archive has already been extracted!", + done_path.c_str()); + return Ok(); + } + log_warning("%s: archive cache has been damaged, re-extracting", + done_path.c_str()); + + fs::remove(done_path); + } + + auto_mem arc(archive_free); + auto_mem ext(archive_free); + + arc = archive_read_new(); + enable_desired_archive_formats(arc); + archive_read_support_format_raw(arc); + archive_read_support_filter_all(arc); + ext = archive_write_disk_new(); + archive_write_disk_set_options(ext, FLAGS); + archive_write_disk_set_standard_lookup(ext); + if (archive_read_open_filename(arc, filename.c_str(), 10240) != ARCHIVE_OK) + { + return Err(fmt::format(FMT_STRING("unable to open archive: {} -- {}"), + filename, + archive_error_string(arc))); + } + + log_info("extracting %s to %s", filename.c_str(), tmp_path.c_str()); + while (true) { + struct archive_entry* entry = nullptr; + auto r = archive_read_next_header(arc, &entry); + if (r == ARCHIVE_EOF) { + log_info("all done"); + break; + } + if (r != ARCHIVE_OK) { + return Err( + fmt::format(FMT_STRING("unable to read entry header: {} -- {}"), + filename, + archive_error_string(arc))); + } + + const auto* format_name = archive_format_name(arc); + auto filter_count = archive_filter_count(arc); + + auto_mem wentry(archive_entry_free); + wentry = archive_entry_clone(entry); + auto desired_pathname = fs::path(archive_entry_pathname(entry)); + if (strcmp(format_name, "raw") == 0 && filter_count >= 2) { + desired_pathname = fs::path(filename).filename(); + } + auto entry_path = tmp_path / desired_pathname; + auto* prog = cb( + entry_path, + archive_entry_size_is_set(entry) ? archive_entry_size(entry) : -1); + archive_entry_copy_pathname(wentry, entry_path.c_str()); + auto entry_mode = archive_entry_mode(wentry); + + archive_entry_set_perm( + wentry, S_IRUSR | (S_ISDIR(entry_mode) ? S_IXUSR | S_IWUSR : 0)); + r = archive_write_header(ext, wentry); + if (r < ARCHIVE_OK) { + return Err( + fmt::format(FMT_STRING("unable to write entry: {} -- {}"), + entry_path.string(), + archive_error_string(ext))); + } + + if (!archive_entry_size_is_set(entry) || archive_entry_size(entry) > 0) + { + TRY(copy_data(filename, arc, entry, ext, entry_path, prog)); + } + r = archive_write_finish_entry(ext); + if (r != ARCHIVE_OK) { + return Err( + fmt::format(FMT_STRING("unable to finish entry: {} -- {}"), + entry_path.string(), + archive_error_string(ext))); + } + } + archive_read_close(arc); + archive_write_close(ext); + + lnav::filesystem::create_file(done_path, O_WRONLY, 0600); + + return Ok(); +} +#endif + +walk_result_t +walk_archive_files( + const std::string& filename, + const extract_cb& cb, + const std::function& + callback) +{ +#if HAVE_ARCHIVE_H + auto tmp_path = filename_to_tmp_path(filename); + + auto result = extract(filename, cb); + if (result.isErr()) { + fs::remove_all(tmp_path); + return result; + } + + for (const auto& entry : fs::recursive_directory_iterator(tmp_path)) { + if (!entry.is_regular_file()) { + continue; + } + + callback(tmp_path, entry); + } + + return Ok(); +#else + return Err(std::string("not compiled with libarchive")); +#endif +} + +void +cleanup_cache() +{ + (void) std::async(std::launch::async, []() { + auto now = std::chrono::system_clock::now(); + auto cache_path = archive_cache_path(); + const auto& cfg = injector::get(); + std::vector to_remove; + + log_debug("cache-ttl %d", cfg.amc_cache_ttl.count()); + for (const auto& entry : fs::directory_iterator(cache_path)) { + if (entry.path().extension() != ".done") { + continue; + } + + auto mtime = fs::last_write_time(entry.path()); + auto exp_time = mtime + cfg.amc_cache_ttl; + if (now < exp_time) { + continue; + } + + to_remove.emplace_back(entry.path()); + } + + for (auto& entry : to_remove) { + log_debug("removing cached archive: %s", entry.c_str()); + fs::remove(entry); + + entry.replace_extension(".lck"); + fs::remove(entry); + + entry.replace_extension(); + fs::remove_all(entry); + } + }); +} + +} // namespace archive_manager diff --git a/src/archive_manager.cfg.hh b/src/archive_manager.cfg.hh new file mode 100644 index 0000000..1c0b821 --- /dev/null +++ b/src/archive_manager.cfg.hh @@ -0,0 +1,46 @@ +/** + * Copyright (c) 2021, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @file archive_manager.cfg.hh + */ + +#ifndef lnav_archive_manager_cfg_hh +#define lnav_archive_manager_cfg_hh + +#include + +namespace archive_manager { + +struct config { + uint64_t amc_min_free_space{32 * 1024 * 1024}; + std::chrono::seconds amc_cache_ttl{std::chrono::hours(48)}; +}; + +} // namespace archive_manager + +#endif diff --git a/src/archive_manager.hh b/src/archive_manager.hh new file mode 100644 index 0000000..777ae87 --- /dev/null +++ b/src/archive_manager.hh @@ -0,0 +1,83 @@ +/** + * Copyright (c) 2020, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @file archive_manager.hh + */ + +#ifndef lnav_archive_manager_hh +#define lnav_archive_manager_hh + +#include +#include +#include +#include + +#include "base/result.h" +#include "ghc/filesystem.hpp" + +namespace archive_manager { + +struct extract_progress { + extract_progress(ghc::filesystem::path path, ssize_t total) + : ep_path(std::move(path)), ep_total_size(total) + { + } + + const ghc::filesystem::path ep_path; + const ssize_t ep_total_size; + std::atomic ep_out_size{0}; +}; + +using extract_cb + = std::function; + +bool is_archive(const ghc::filesystem::path& filename); + +ghc::filesystem::path filename_to_tmp_path(const std::string& filename); + +using walk_result_t = Result; + +/** + * + * @feature f0:archive + * + * @param filename + * @param cb + * @return + */ +walk_result_t walk_archive_files( + const std::string& filename, + const extract_cb& cb, + const std::function&); + +void cleanup_cache(); + +} // namespace archive_manager + +#endif diff --git a/src/base/CMakeLists.txt b/src/base/CMakeLists.txt new file mode 100644 index 0000000..aa4143f --- /dev/null +++ b/src/base/CMakeLists.txt @@ -0,0 +1,83 @@ +add_library( + base STATIC + ../config.h.in + ansi_scrubber.cc + attr_line.cc + attr_line.builder.cc + auto_pid.cc + date_time_scanner.cc + fs_util.cc + humanize.cc + humanize.network.cc + humanize.time.cc + intern_string.cc + is_utf8.cc + isc.cc + lnav.console.cc + lnav.gzip.cc + lnav_log.cc + network.tcp.cc + paths.cc + snippet_highlighters.cc + string_attr_type.cc + string_util.cc + strnatcmp.c + time_util.cc + + ansi_scrubber.hh + attr_line.hh + attr_line.builder.hh + auto_fd.hh + auto_mem.hh + auto_pid.hh + bus.hh + date_time_scanner.hh + enum_util.hh + fs_util.hh + func_util.hh + future_util.hh + humanize.hh + humanize.network.hh + humanize.time.hh + injector.hh + injector.bind.hh + intern_string.hh + is_utf8.hh + isc.hh + itertools.hh + lnav.console.hh + lnav.console.into.hh + log_level_enum.hh + lrucache.hpp + math_util.hh + network.tcp.hh + paths.hh + result.h + snippet_highlighters.hh + string_attr_type.hh + strnatcmp.h + time_util.hh + + ../third-party/xxHash/xxhash.h + ../third-party/xxHash/xxhash.c +) + +target_include_directories(base PUBLIC . .. ../third-party + ${CMAKE_CURRENT_BINARY_DIR}/..) +target_link_libraries(base cppfmt cppscnlib pcrepp ncurses::libcurses pthread) + +add_executable( + test_base + attr_line.tests.cc + fs_util.tests.cc + humanize.file_size.tests.cc + humanize.network.tests.cc + humanize.time.tests.cc + intern_string.tests.cc + lnav.gzip.tests.cc + string_util.tests.cc + network.tcp.tests.cc + test_base.cc) +target_include_directories(test_base PUBLIC ../third-party/doctest-root) +target_link_libraries(test_base base pcrepp ZLIB::ZLIB) +add_test(NAME test_base COMMAND test_base) diff --git a/src/base/Makefile.am b/src/base/Makefile.am new file mode 100644 index 0000000..4a459a6 --- /dev/null +++ b/src/base/Makefile.am @@ -0,0 +1,110 @@ + +include $(top_srcdir)/aminclude_static.am + +AM_CPPFLAGS = \ + $(CODE_COVERAGE_CPPFLAGS) \ + -Wall \ + -I$(top_srcdir)/src/ \ + -I$(top_srcdir)/src/third-party \ + -I$(top_srcdir)/src/fmtlib \ + -I$(top_srcdir)/src/third-party/scnlib/include \ + $(LIBARCHIVE_CFLAGS) \ + $(READLINE_CFLAGS) \ + $(SQLITE3_CFLAGS) \ + $(PCRE_CFLAGS) \ + $(LIBCURL_CPPFLAGS) + +AM_LIBS = $(CODE_COVERAGE_LIBS) +AM_CFLAGS = $(CODE_COVERAGE_CFLAGS) +AM_CXXFLAGS = $(CODE_COVERAGE_CXXFLAGS) + +noinst_LIBRARIES = libbase.a + +noinst_HEADERS = \ + ansi_scrubber.hh \ + attr_line.hh \ + attr_line.builder.hh \ + auto_fd.hh \ + auto_mem.hh \ + auto_pid.hh \ + bus.hh \ + date_time_scanner.hh \ + enum_util.hh \ + file_range.hh \ + fs_util.hh \ + func_util.hh \ + future_util.hh \ + humanize.hh \ + humanize.network.hh \ + humanize.time.hh \ + injector.hh \ + injector.bind.hh \ + intern_string.hh \ + is_utf8.hh \ + isc.hh \ + itertools.hh \ + lnav_log.hh \ + lnav.console.hh \ + lnav.console.into.hh \ + lnav.gzip.hh \ + log_level_enum.hh \ + lrucache.hpp \ + math_util.hh \ + network.tcp.hh \ + opt_util.hh \ + paths.hh \ + result.h \ + snippet_highlighters.hh \ + string_attr_type.hh \ + string_util.hh \ + strnatcmp.h \ + time_util.hh + +libbase_a_SOURCES = \ + ansi_scrubber.cc \ + attr_line.cc \ + attr_line.builder.cc \ + auto_pid.cc \ + date_time_scanner.cc \ + fs_util.cc \ + humanize.cc \ + humanize.network.cc \ + humanize.time.cc \ + intern_string.cc \ + is_utf8.cc \ + isc.cc \ + lnav.console.cc \ + lnav.gzip.cc \ + lnav_log.cc \ + network.tcp.cc \ + paths.cc \ + snippet_highlighters.cc \ + string_attr_type.cc \ + string_util.cc \ + strnatcmp.c \ + time_util.cc \ + ../third-party/xxHash/xxhash.h \ + ../third-party/xxHash/xxhash.c + +check_PROGRAMS = \ + test_base + +test_base_SOURCES = \ + attr_line.tests.cc \ + fs_util.tests.cc \ + humanize.file_size.tests.cc \ + humanize.network.tests.cc \ + humanize.time.tests.cc \ + intern_string.tests.cc \ + lnav.gzip.tests.cc \ + string_util.tests.cc \ + test_base.cc + +test_base_LDADD = \ + libbase.a \ + ../fmtlib/libcppfmt.a \ + ../third-party/scnlib/src/libscnlib.a \ + ../pcrepp/libpcrepp.a + +TESTS = \ + test_base diff --git a/src/base/README.md b/src/base/README.md new file mode 100644 index 0000000..944dde8 --- /dev/null +++ b/src/base/README.md @@ -0,0 +1 @@ +# libbase -- Library of utility functions diff --git a/src/base/ansi_scrubber.cc b/src/base/ansi_scrubber.cc new file mode 100644 index 0000000..26ae070 --- /dev/null +++ b/src/base/ansi_scrubber.cc @@ -0,0 +1,388 @@ +/** + * Copyright (c) 2013, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @file ansi_scrubber.cc + */ + +#include + +#include "ansi_scrubber.hh" + +#include "base/opt_util.hh" +#include "config.h" +#include "pcrepp/pcre2pp.hh" +#include "scn/scn.h" +#include "view_curses.hh" + +static const lnav::pcre2pp::code& +ansi_regex() +{ + static const auto retval = lnav::pcre2pp::code::from_const( + "\x1b\\[([\\d=;\\?]*)([a-zA-Z])|(?:\\X\x08\\X)+"); + + return retval; +} + +size_t +erase_ansi_escapes(string_fragment input) +{ + static thread_local auto md = lnav::pcre2pp::match_data::unitialized(); + + const auto& regex = ansi_regex(); + nonstd::optional move_start; + size_t fill_index = 0; + + auto matcher = regex.capture_from(input).into(md); + while (true) { + auto match_res = matcher.matches(PCRE2_NO_UTF_CHECK); + + if (match_res.is()) { + break; + } + if (match_res.is()) { + log_error("ansi scrub regex failure"); + break; + } + + auto sf = md[0].value(); + auto bs_index_res = sf.codepoint_to_byte_index(1); + + if (move_start) { + auto move_len = sf.sf_begin - move_start.value(); + memmove(input.writable_data(fill_index), + input.data() + move_start.value(), + move_len); + fill_index += move_len; + } else { + fill_index = sf.sf_begin; + } + + if (sf.length() >= 3 && bs_index_res.isOk() + && sf[bs_index_res.unwrap()] == '\b') + { + static const auto OVERSTRIKE_RE + = lnav::pcre2pp::code::from_const(R"((\X)\x08(\X))"); + + auto loop_res = OVERSTRIKE_RE.capture_from(sf).for_each( + [&fill_index, &input](lnav::pcre2pp::match_data& over_md) { + auto lhs = over_md[1].value(); + if (lhs == "_") { + auto rhs = over_md[2].value(); + memmove(input.writable_data(fill_index), + rhs.data(), + rhs.length()); + fill_index += rhs.length(); + } else { + memmove(input.writable_data(fill_index), + lhs.data(), + lhs.length()); + fill_index += lhs.length(); + } + }); + } + move_start = md.remaining().sf_begin; + } + + memmove(input.writable_data(fill_index), + md.remaining().data(), + md.remaining().length()); + fill_index += md.remaining().length(); + + return fill_index; +} + +void +scrub_ansi_string(std::string& str, string_attrs_t* sa) +{ + static thread_local auto md = lnav::pcre2pp::match_data::unitialized(); + const auto& regex = ansi_regex(); + int64_t origin_offset = 0; + int last_origin_offset_end = 0; + + replace(str.begin(), str.end(), '\0', ' '); + auto matcher = regex.capture_from(str).into(md); + while (true) { + auto match_res = matcher.matches(PCRE2_NO_UTF_CHECK); + + if (match_res.is()) { + break; + } + if (match_res.is()) { + log_error("ansi scrub regex failure"); + break; + } + + const auto sf = md[0].value(); + auto bs_index_res = sf.codepoint_to_byte_index(1); + + if (sf.length() >= 3 && bs_index_res.isOk() + && sf[bs_index_res.unwrap()] == '\b') + { + ssize_t fill_index = sf.sf_begin; + line_range bold_range; + line_range ul_range; + auto sub_sf = sf; + + while (!sub_sf.empty()) { + auto lhs_opt = sub_sf.consume_codepoint(); + if (!lhs_opt) { + return; + } + auto lhs_pair = lhs_opt.value(); + auto mid_opt = lhs_pair.second.consume_codepoint(); + if (!mid_opt) { + return; + } + auto mid_pair = mid_opt.value(); + auto rhs_opt = mid_pair.second.consume_codepoint(); + if (!rhs_opt) { + return; + } + auto rhs_pair = rhs_opt.value(); + sub_sf = rhs_pair.second; + + if (lhs_pair.first == '_' || rhs_pair.first == '_') { + if (sa != nullptr && bold_range.is_valid()) { + sa->emplace_back(bold_range, + VC_STYLE.value(text_attrs{A_BOLD})); + bold_range.clear(); + } + if (ul_range.is_valid()) { + ul_range.lr_end += 1; + } else { + ul_range.lr_start = fill_index; + ul_range.lr_end = fill_index + 1; + } + auto cp = lhs_pair.first == '_' ? rhs_pair.first + : lhs_pair.first; + ww898::utf::utf8::write(cp, [&str, &fill_index](auto ch) { + str[fill_index++] = ch; + }); + } else { + if (sa != nullptr && ul_range.is_valid()) { + sa->emplace_back( + ul_range, VC_STYLE.value(text_attrs{A_UNDERLINE})); + ul_range.clear(); + } + if (bold_range.is_valid()) { + bold_range.lr_end += 1; + } else { + bold_range.lr_start = fill_index; + bold_range.lr_end = fill_index + 1; + } + try { + ww898::utf::utf8::write(lhs_pair.first, + [&str, &fill_index](auto ch) { + str[fill_index++] = ch; + }); + } catch (const std::runtime_error& e) { + log_error("invalid UTF-8 at %d", sf.sf_begin); + return; + } + } + } + + auto output_size = fill_index - sf.sf_begin; + auto erased_size = sf.length() - output_size; + + if (sa != nullptr) { +#if 0 + shift_string_attrs( + *sa, caps->c_begin + sf.length() / 3, -erased_size); +#endif + sa->emplace_back(line_range{last_origin_offset_end, + sf.sf_begin + (int) output_size}, + SA_ORIGIN_OFFSET.value(origin_offset)); + } + + if (sa != nullptr && ul_range.is_valid()) { + sa->emplace_back(ul_range, + VC_STYLE.value(text_attrs{A_UNDERLINE})); + ul_range.clear(); + } + if (sa != nullptr && bold_range.is_valid()) { + sa->emplace_back(bold_range, + VC_STYLE.value(text_attrs{A_BOLD})); + bold_range.clear(); + } + + str.erase(str.begin() + fill_index, str.begin() + sf.sf_end); + last_origin_offset_end = sf.sf_begin + output_size; + origin_offset += erased_size; + matcher.reload_input(str, last_origin_offset_end); + continue; + } + + auto seq = md[1].value(); + auto terminator = md[2].value(); + struct line_range lr; + bool has_attrs = false; + text_attrs attrs; + auto role = nonstd::optional(); + size_t lpc; + + switch (terminator[0]) { + case 'm': + for (lpc = seq.sf_begin; + lpc != std::string::npos && lpc < (size_t) seq.sf_end;) + { + auto ansi_code_res = scn::scan_value( + scn::string_view{&str[lpc], &str[seq.sf_end]}); + + if (ansi_code_res) { + auto ansi_code = ansi_code_res.value(); + if (90 <= ansi_code && ansi_code <= 97) { + ansi_code -= 60; + attrs.ta_attrs |= A_STANDOUT; + } + if (30 <= ansi_code && ansi_code <= 37) { + attrs.ta_fg_color = ansi_code - 30; + } + if (40 <= ansi_code && ansi_code <= 47) { + attrs.ta_bg_color = ansi_code - 40; + } + switch (ansi_code) { + case 1: + attrs.ta_attrs |= A_BOLD; + break; + + case 2: + attrs.ta_attrs |= A_DIM; + break; + + case 4: + attrs.ta_attrs |= A_UNDERLINE; + break; + + case 7: + attrs.ta_attrs |= A_REVERSE; + break; + } + } + lpc = str.find(';', lpc); + if (lpc != std::string::npos) { + lpc += 1; + } + } + has_attrs = true; + break; + + case 'C': { + auto spaces_res + = scn::scan_value(seq.to_string_view()); + + if (spaces_res && spaces_res.value() > 0) { + str.insert((std::string::size_type) sf.sf_end, + spaces_res.value(), + ' '); + } + break; + } + + case 'H': { + unsigned int row = 0, spaces = 0; + + if (scn::scan(seq.to_string_view(), "{};{}", row, spaces) + && spaces > 1) + { + int ispaces = spaces - 1; + if (ispaces > sf.sf_begin) { + str.insert((unsigned long) sf.sf_end, + ispaces - sf.sf_begin, + ' '); + } + } + break; + } + + case 'O': { + auto role_res = scn::scan_value(seq.to_string_view()); + + if (role_res) { + role_t role_tmp = (role_t) role_res.value(); + if (role_tmp > role_t::VCR_NONE + && role_tmp < role_t::VCR__MAX) + { + role = role_tmp; + has_attrs = true; + } + } + break; + } + } + str.erase(str.begin() + sf.sf_begin, str.begin() + sf.sf_end); + if (sa != nullptr) { + shift_string_attrs(*sa, sf.sf_begin, -sf.length()); + + if (has_attrs) { + for (auto rit = sa->rbegin(); rit != sa->rend(); rit++) { + if (rit->sa_range.lr_end != -1) { + continue; + } + rit->sa_range.lr_end = sf.sf_begin; + } + lr.lr_start = sf.sf_begin; + lr.lr_end = -1; + if (attrs.ta_attrs || attrs.ta_fg_color || attrs.ta_bg_color) { + sa->emplace_back(lr, VC_STYLE.value(attrs)); + } + role | [&lr, &sa](role_t r) { + sa->emplace_back(lr, VC_ROLE.value(r)); + }; + } + sa->emplace_back(line_range{last_origin_offset_end, sf.sf_begin}, + SA_ORIGIN_OFFSET.value(origin_offset)); + last_origin_offset_end = sf.sf_begin; + origin_offset += sf.length(); + } + + matcher.reload_input(str, sf.sf_begin); + } + + if (sa != nullptr && last_origin_offset_end > 0) { + sa->emplace_back(line_range{last_origin_offset_end, (int) str.size()}, + SA_ORIGIN_OFFSET.value(origin_offset)); + } +} + +void +add_ansi_vars(std::map& vars) +{ + vars["ansi_csi"] = ANSI_CSI; + vars["ansi_norm"] = ANSI_NORM; + vars["ansi_bold"] = ANSI_BOLD_START; + vars["ansi_underline"] = ANSI_UNDERLINE_START; + vars["ansi_black"] = ANSI_COLOR(COLOR_BLACK); + vars["ansi_red"] = ANSI_COLOR(COLOR_RED); + vars["ansi_green"] = ANSI_COLOR(COLOR_GREEN); + vars["ansi_yellow"] = ANSI_COLOR(COLOR_YELLOW); + vars["ansi_blue"] = ANSI_COLOR(COLOR_BLUE); + vars["ansi_magenta"] = ANSI_COLOR(COLOR_MAGENTA); + vars["ansi_cyan"] = ANSI_COLOR(COLOR_CYAN); + vars["ansi_white"] = ANSI_COLOR(COLOR_WHITE); +} diff --git a/src/base/ansi_scrubber.hh b/src/base/ansi_scrubber.hh new file mode 100644 index 0000000..b832e17 --- /dev/null +++ b/src/base/ansi_scrubber.hh @@ -0,0 +1,75 @@ +/** + * Copyright (c) 2013, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @file ansi_scrubber.hh + */ + +#ifndef lnav_ansi_scrubber_hh +#define lnav_ansi_scrubber_hh + +#include +#include + +#include "attr_line.hh" +#include "shlex.resolver.hh" + +#define ANSI_CSI "\x1b[" +#define ANSI_CHAR_ATTR "m" +#define ANSI_BOLD_PARAM "1" +#define ANSI_BOLD_START ANSI_CSI ANSI_BOLD_PARAM ANSI_CHAR_ATTR +#define ANSI_UNDERLINE_START ANSI_CSI "4m" +#define ANSI_NORM ANSI_CSI "0m" +#define ANSI_STRIKE_PARAM "9" +#define ANSI_STRIKE_START ANSI_CSI ANSI_STRIKE_PARAM ANSI_CHAR_ATTR + +#define ANSI_BOLD(msg) ANSI_BOLD_START msg ANSI_NORM +#define ANSI_UNDERLINE(msg) ANSI_UNDERLINE_START msg ANSI_NORM + +#define ANSI_ROLE(msg) ANSI_CSI "%dO" msg ANSI_NORM +#define XANSI_COLOR(col) "3" #col +#define ANSI_COLOR_PARAM(col) XANSI_COLOR(col) +#define ANSI_COLOR(col) ANSI_CSI XANSI_COLOR(col) "m" + +/** + * Check a string for ANSI escape sequences, process them, remove them, and add + * any style attributes to the given attribute container. + * + * @param str The string to check for ANSI escape sequences. + * @param sa The container for any style attributes. + */ +void scrub_ansi_string(std::string& str, string_attrs_t* sa); + +size_t erase_ansi_escapes(string_fragment input); + +/** + * Populate a variable map with strings that contain escape sequences that + * might be useful to script writers. + */ +void add_ansi_vars(std::map& vars); + +#endif diff --git a/src/base/attr_line.builder.cc b/src/base/attr_line.builder.cc new file mode 100644 index 0000000..95416dc --- /dev/null +++ b/src/base/attr_line.builder.cc @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2022, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "attr_line.builder.hh" diff --git a/src/base/attr_line.builder.hh b/src/base/attr_line.builder.hh new file mode 100644 index 0000000..1e62532 --- /dev/null +++ b/src/base/attr_line.builder.hh @@ -0,0 +1,119 @@ +/** + * Copyright (c) 2022, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef lnav_attr_line_builder_hh +#define lnav_attr_line_builder_hh + +#include + +#include "attr_line.hh" + +class attr_line_builder { +public: + explicit attr_line_builder(attr_line_t& al) : alb_line(al) {} + + class attr_guard { + public: + attr_guard(attr_line_t& al, string_attr_pair sap) + : ag_line(al), ag_start(al.get_string().length()), + ag_attr(std::move(sap)) + { + } + + attr_guard(const attr_guard&) = delete; + + attr_guard& operator=(const attr_guard&) = delete; + + attr_guard(attr_guard&& other) noexcept + : ag_line(other.ag_line), ag_start(other.ag_start), + ag_attr(std::move(other.ag_attr)) + { + other.ag_start = nonstd::nullopt; + } + + ~attr_guard() + { + if (this->ag_start) { + this->ag_line.al_attrs.emplace_back( + line_range{ + this->ag_start.value(), + (int) this->ag_line.get_string().length(), + }, + this->ag_attr); + } + } + + private: + attr_line_t& ag_line; + nonstd::optional ag_start; + string_attr_pair ag_attr; + }; + + attr_guard with_attr(string_attr_pair sap) + { + return {this->alb_line, std::move(sap)}; + } + + template + attr_line_builder& overlay_attr(Args... args) + { + this->alb_line.al_attrs.template emplace_back(args...); + return *this; + } + + template + attr_line_builder& overlay_attr_for_char(int index, Args... args) + { + this->alb_line.al_attrs.template emplace_back( + line_range{index, index + 1}, args...); + return *this; + } + + template + attr_line_builder& append(Args... args) + { + this->alb_line.append(args...); + + return *this; + } + + attr_line_builder& indent(size_t amount) + { + auto pre = this->with_attr(SA_PREFORMATTED.value()); + + this->append(amount, ' '); + + return *this; + } + +private: + attr_line_t& alb_line; +}; + +#endif diff --git a/src/base/attr_line.cc b/src/base/attr_line.cc new file mode 100644 index 0000000..db9c0f1 --- /dev/null +++ b/src/base/attr_line.cc @@ -0,0 +1,537 @@ +/** + * Copyright (c) 2020, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include "attr_line.hh" + +#include + +#include "ansi_scrubber.hh" +#include "auto_mem.hh" +#include "config.h" +#include "lnav_log.hh" +#include "pcrepp/pcre2pp.hh" + +attr_line_t& +attr_line_t::with_ansi_string(const char* str, ...) +{ + auto_mem formatted_str; + va_list args; + + va_start(args, str); + auto ret = vasprintf(formatted_str.out(), str, args); + va_end(args); + + if (ret >= 0 && formatted_str != nullptr) { + this->al_string = formatted_str; + scrub_ansi_string(this->al_string, &this->al_attrs); + } + + return *this; +} + +attr_line_t& +attr_line_t::with_ansi_string(const std::string& str) +{ + this->al_string = str; + scrub_ansi_string(this->al_string, &this->al_attrs); + + return *this; +} + +namespace text_stream { +struct word { + string_fragment w_word; + string_fragment w_remaining; +}; + +struct space { + string_fragment s_value; + string_fragment s_remaining; +}; + +struct corrupt { + string_fragment c_value; + string_fragment c_remaining; +}; + +struct eof { + string_fragment e_remaining; +}; + +using chunk = mapbox::util::variant; + +chunk +consume(const string_fragment text) +{ + static const auto WORD_RE + = lnav::pcre2pp::code::from_const(R"((*UTF)^[^\p{Z}\p{So}\p{C}]+)"); + static const auto SPACE_RE + = lnav::pcre2pp::code::from_const(R"((*UTF)^\s)"); + + if (text.empty()) { + return eof{text}; + } + + auto word_find_res + = WORD_RE.find_in(text, PCRE2_NO_UTF_CHECK).ignore_error(); + if (word_find_res) { + auto split_res = text.split_n(word_find_res->f_all.length()).value(); + + return word{split_res.first, split_res.second}; + } + + if (isspace(text.front())) { + auto split_res = text.split_n(1).value(); + + return space{split_res.first, split_res.second}; + } + + auto space_find_res + = SPACE_RE.find_in(text, PCRE2_NO_UTF_CHECK).ignore_error(); + if (space_find_res) { + auto split_res = text.split_n(space_find_res->f_all.length()).value(); + + return space{split_res.first, split_res.second}; + } + + auto csize_res = ww898::utf::utf8::char_size( + [&text]() { return std::make_pair(text.front(), text.length()); }); + + if (csize_res.isErr()) { + auto split_res = text.split_n(1); + + return corrupt{split_res->first, split_res->second}; + } + + auto split_res = text.split_n(csize_res.unwrap()); + + return word{split_res->first, split_res->second}; +} + +} // namespace text_stream + +static void +split_attrs(attr_line_t& al, const line_range& lr) +{ + if (lr.empty()) { + return; + } + + string_attrs_t new_attrs; + + for (auto& attr : al.al_attrs) { + if (!lr.intersects(attr.sa_range)) { + continue; + } + + new_attrs.emplace_back(line_range{lr.lr_end, attr.sa_range.lr_end}, + std::make_pair(attr.sa_type, attr.sa_value)); + attr.sa_range.lr_end = lr.lr_start; + } + for (auto& new_attr : new_attrs) { + al.al_attrs.emplace_back(std::move(new_attr)); + } +} + +attr_line_t& +attr_line_t::insert(size_t index, + const attr_line_t& al, + text_wrap_settings* tws) +{ + if (index < this->al_string.length()) { + shift_string_attrs(this->al_attrs, index, al.al_string.length()); + } + + this->al_string.insert(index, al.al_string); + + for (const auto& sa : al.al_attrs) { + this->al_attrs.emplace_back(sa); + + line_range& lr = this->al_attrs.back().sa_range; + + lr.shift(0, index); + if (lr.lr_end == -1) { + lr.lr_end = index + al.al_string.length(); + } + } + + if (tws == nullptr) { + return *this; + } + + auto starting_line_index = this->al_string.rfind('\n', index); + if (starting_line_index == std::string::npos) { + starting_line_index = 0; + } else { + starting_line_index += 1; + } + + const ssize_t usable_width = tws->tws_width - tws->tws_indent; + + auto text_to_wrap = string_fragment::from_str_range( + this->al_string, starting_line_index, this->al_string.length()); + string_fragment last_word; + ssize_t line_ch_count = 0; + auto needs_indent = false; + + while (!text_to_wrap.empty()) { + if (needs_indent) { + this->insert(text_to_wrap.sf_begin, + tws->tws_indent + tws->tws_padding_indent, + ' '); + auto indent_lr = line_range{ + text_to_wrap.sf_begin, + text_to_wrap.sf_begin + tws->tws_indent, + }; + split_attrs(*this, indent_lr); + indent_lr.lr_end += tws->tws_padding_indent; + line_ch_count += tws->tws_padding_indent; + if (!indent_lr.empty()) { + this->al_attrs.emplace_back(indent_lr, SA_PREFORMATTED.value()); + } + text_to_wrap = text_to_wrap.prepend( + this->al_string.data(), + tws->tws_indent + tws->tws_padding_indent); + needs_indent = false; + } + auto chunk = text_stream::consume(text_to_wrap); + + text_to_wrap = chunk.match( + [&](text_stream::word word) { + auto ch_count + = word.w_word.utf8_length().unwrapOr(word.w_word.length()); + + if ((line_ch_count + ch_count) > usable_width + && find_string_attr_containing(this->al_attrs, + &SA_PREFORMATTED, + text_to_wrap.sf_begin) + == this->al_attrs.end()) + { + this->insert(word.w_word.sf_begin, 1, '\n'); + this->insert(word.w_word.sf_begin + 1, + tws->tws_indent + tws->tws_padding_indent, + ' '); + auto indent_lr = line_range{ + word.w_word.sf_begin + 1, + word.w_word.sf_begin + 1 + tws->tws_indent, + }; + split_attrs(*this, indent_lr); + indent_lr.lr_end += tws->tws_padding_indent; + if (!indent_lr.empty()) { + this->al_attrs.emplace_back(indent_lr, + SA_PREFORMATTED.value()); + } + line_ch_count = tws->tws_padding_indent + ch_count; + auto trailing_space_count = 0; + if (!last_word.empty()) { + trailing_space_count + = word.w_word.sf_begin - last_word.sf_begin; + this->erase(last_word.sf_begin, trailing_space_count); + } + return word.w_remaining + .erase_before(this->al_string.data(), + trailing_space_count) + .prepend(this->al_string.data(), + 1 + tws->tws_indent + tws->tws_padding_indent); + } + line_ch_count += ch_count; + + return word.w_remaining; + }, + [&](text_stream::space space) { + if (space.s_value == "\n") { + line_ch_count = 0; + needs_indent = true; + return space.s_remaining; + } + + if (line_ch_count > 0) { + auto ch_count = space.s_value.utf8_length().unwrapOr( + space.s_value.length()); + + if ((line_ch_count + ch_count) > usable_width + && find_string_attr_containing(this->al_attrs, + &SA_PREFORMATTED, + text_to_wrap.sf_begin) + == this->al_attrs.end()) + { + this->erase(space.s_value.sf_begin, + space.s_value.length()); + this->insert(space.s_value.sf_begin, "\n"); + line_ch_count = 0; + needs_indent = true; + + auto trailing_space_count = 0; + if (!last_word.empty()) { + trailing_space_count + = space.s_value.sf_begin - last_word.sf_begin; + this->erase(last_word.sf_end, trailing_space_count); + } + + return space.s_remaining + .erase_before( + this->al_string.data(), + space.s_value.length() + trailing_space_count) + .prepend(this->al_string.data(), 1); + } + line_ch_count += ch_count; + } else if (find_string_attr_containing(this->al_attrs, + &SA_PREFORMATTED, + text_to_wrap.sf_begin) + == this->al_attrs.end()) + { + this->erase(space.s_value.sf_begin, space.s_value.length()); + return space.s_remaining.erase_before( + this->al_string.data(), space.s_value.length()); + } + + return space.s_remaining; + }, + [](text_stream::corrupt corrupt) { return corrupt.c_remaining; }, + [](text_stream::eof eof) { return eof.e_remaining; }); + + if (chunk.is()) { + last_word = text_to_wrap; + } + + ensure(this->al_string.data() == text_to_wrap.sf_string); + ensure(text_to_wrap.sf_begin <= text_to_wrap.sf_end); + } + return *this; +} + +attr_line_t +attr_line_t::subline(size_t start, size_t len) const +{ + if (len == std::string::npos) { + len = this->al_string.length() - start; + } + + line_range lr{(int) start, (int) (start + len)}; + attr_line_t retval; + + retval.al_string = this->al_string.substr(start, len); + for (const auto& sa : this->al_attrs) { + if (!lr.intersects(sa.sa_range)) { + continue; + } + + auto ilr = lr.intersection(sa.sa_range).shift(0, -lr.lr_start); + retval.al_attrs.emplace_back(ilr, + std::make_pair(sa.sa_type, sa.sa_value)); + const auto& last_lr = retval.al_attrs.back().sa_range; + + ensure(last_lr.lr_end <= (int) retval.al_string.length()); + } + + return retval; +} + +void +attr_line_t::split_lines(std::vector& lines) const +{ + size_t pos = 0, next_line; + + while ((next_line = this->al_string.find('\n', pos)) != std::string::npos) { + lines.emplace_back(this->subline(pos, next_line - pos)); + pos = next_line + 1; + } + lines.emplace_back(this->subline(pos)); +} + +attr_line_t& +attr_line_t::right_justify(unsigned long width) +{ + long padding = width - this->length(); + if (padding > 0) { + this->al_string.insert(0, padding, ' '); + for (auto& al_attr : this->al_attrs) { + if (al_attr.sa_range.lr_start > 0) { + al_attr.sa_range.lr_start += padding; + } + if (al_attr.sa_range.lr_end != -1) { + al_attr.sa_range.lr_end += padding; + } + } + } + + return *this; +} + +size_t +attr_line_t::nearest_text(size_t x) const +{ + if (x > 0 && x >= (size_t) this->length()) { + if (this->empty()) { + x = 0; + } else { + x = this->length() - 1; + } + } + + while (x > 0 && isspace(this->al_string[x])) { + x -= 1; + } + + return x; +} + +void +attr_line_t::apply_hide() +{ + auto& sa = this->al_attrs; + + for (auto& sattr : sa) { + if (sattr.sa_type == &SA_HIDDEN && sattr.sa_range.length() > 3) { + struct line_range& lr = sattr.sa_range; + + std::for_each(sa.begin(), sa.end(), [&](string_attr& attr) { + if (attr.sa_type == &VC_STYLE && lr.contains(attr.sa_range)) { + attr.sa_type = &SA_REMOVED; + } + }); + + this->al_string.replace(lr.lr_start, lr.length(), "\xE2\x8B\xAE"); + shift_string_attrs(sa, lr.lr_start + 1, -(lr.length() - 3)); + sattr.sa_type = &VC_ROLE; + sattr.sa_value = role_t::VCR_HIDDEN; + lr.lr_end = lr.lr_start + 3; + } + } +} + +attr_line_t& +attr_line_t::rtrim() +{ + auto index = this->al_string.length(); + + for (; index > 0; index--) { + if (find_string_attr_containing( + this->al_attrs, &SA_PREFORMATTED, index - 1) + != this->al_attrs.end()) + { + break; + } + if (!isspace(this->al_string[index - 1])) { + break; + } + } + if (index > 0 && index < this->al_string.length()) { + this->erase(index); + } + return *this; +} + +attr_line_t& +attr_line_t::erase(size_t pos, size_t len) +{ + if (len == std::string::npos) { + len = this->al_string.size() - pos; + } + if (len == 0) { + return *this; + } + + this->al_string.erase(pos, len); + + shift_string_attrs(this->al_attrs, pos, -((int32_t) len)); + auto new_end = std::remove_if( + this->al_attrs.begin(), this->al_attrs.end(), [](const auto& attr) { + return attr.sa_range.empty(); + }); + this->al_attrs.erase(new_end, this->al_attrs.end()); + + return *this; +} + +attr_line_t& +attr_line_t::pad_to(ssize_t size) +{ + const auto curr_len = this->utf8_length_or_length(); + + if (curr_len < size) { + this->append((size - curr_len), ' '); + for (auto& attr : this->al_attrs) { + if (attr.sa_range.lr_start == 0 && attr.sa_range.lr_end == curr_len) + { + attr.sa_range.lr_end = this->al_string.length(); + } + } + } + + return *this; +} + +line_range +line_range::intersection(const line_range& other) const +{ + int actual_end; + + if (this->lr_end == -1) { + actual_end = other.lr_end; + } else if (other.lr_end == -1) { + actual_end = this->lr_end; + } else { + actual_end = std::min(this->lr_end, other.lr_end); + } + return line_range{std::max(this->lr_start, other.lr_start), actual_end}; +} + +line_range& +line_range::shift(int32_t start, int32_t amount) +{ + if (start == this->lr_start) { + if (amount > 0) { + this->lr_start += amount; + } + if (this->lr_end != -1) { + this->lr_end += amount; + if (this->lr_end < this->lr_start) { + this->lr_end = this->lr_start; + } + } + } else if (start < this->lr_start) { + this->lr_start = std::max(0, this->lr_start + amount); + if (this->lr_end != -1) { + this->lr_end = std::max(0, this->lr_end + amount); + } + } else if (this->lr_end != -1) { + if (start < this->lr_end) { + if (amount < 0 && amount < (start - this->lr_end)) { + this->lr_end = start; + } else { + this->lr_end = std::max(this->lr_start, this->lr_end + amount); + } + } + } + + return *this; +} diff --git a/src/base/attr_line.hh b/src/base/attr_line.hh new file mode 100644 index 0000000..c9cb6a8 --- /dev/null +++ b/src/base/attr_line.hh @@ -0,0 +1,758 @@ +/** + * Copyright (c) 2017, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @file attr_line.hh + */ + +#ifndef attr_line_hh +#define attr_line_hh + +#include +#include +#include + +#include + +#include "fmt/format.h" +#include "intern_string.hh" +#include "string_attr_type.hh" +#include "string_util.hh" + +/** + * Encapsulates a range in a string. + */ +struct line_range { + enum class unit { + bytes, + codepoint, + }; + + int lr_start; + int lr_end; + unit lr_unit; + + explicit line_range(int start = -1, int end = -1, unit u = unit::bytes) + : lr_start(start), lr_end(end), lr_unit(u) + { + } + + bool is_valid() const { return this->lr_start != -1; } + + int length() const + { + return this->lr_end == -1 ? INT_MAX : this->lr_end - this->lr_start; + } + + bool empty() const { return this->length() == 0; } + + void clear() + { + this->lr_start = -1; + this->lr_end = -1; + } + + int end_for_string(const std::string& str) const + { + return this->lr_end == -1 ? str.length() : this->lr_end; + } + + bool contains(int pos) const + { + return this->lr_start <= pos + && (this->lr_end == -1 || pos < this->lr_end); + } + + bool contains(const struct line_range& other) const + { + return this->contains(other.lr_start) + && (this->lr_end == -1 || other.lr_end <= this->lr_end); + } + + bool intersects(const struct line_range& other) const + { + if (this->contains(other.lr_start)) { + return true; + } + if (other.lr_end > 0 && this->contains(other.lr_end - 1)) { + return true; + } + if (other.contains(this->lr_start)) { + return true; + } + + return false; + } + + line_range intersection(const struct line_range& other) const; + + line_range& shift(int32_t start, int32_t amount); + + void ltrim(const char* str) + { + while (this->lr_start < this->lr_end && isspace(str[this->lr_start])) { + this->lr_start += 1; + } + } + + bool operator<(const struct line_range& rhs) const + { + if (this->lr_start < rhs.lr_start) { + return true; + } + if (this->lr_start > rhs.lr_start) { + return false; + } + + // this->lr_start == rhs.lr_start + if (this->lr_end == rhs.lr_end) { + return false; + } + + if (this->lr_end < rhs.lr_end) { + return false; + } + return true; + } + + bool operator==(const struct line_range& rhs) const + { + return (this->lr_start == rhs.lr_start && this->lr_end == rhs.lr_end); + } + + const char* substr(const std::string& str) const + { + if (this->lr_start == -1) { + return str.c_str(); + } + return &(str.c_str()[this->lr_start]); + } + + size_t sublen(const std::string& str) const + { + if (this->lr_start == -1) { + return str.length(); + } + if (this->lr_end == -1) { + return str.length() - this->lr_start; + } + return this->length(); + } +}; + +inline line_range +to_line_range(const string_fragment& frag) +{ + return line_range{frag.sf_begin, frag.sf_end}; +} + +struct string_attr { + string_attr(const struct line_range& lr, const string_attr_pair& value) + : sa_range(lr), sa_type(value.first), sa_value(value.second) + { + } + + string_attr() = default; + + bool operator<(const struct string_attr& rhs) const + { + if (this->sa_range < rhs.sa_range) { + return true; + } + if (this->sa_range == rhs.sa_range && this->sa_type == rhs.sa_type + && this->sa_type == &VC_ROLE + && this->sa_value.get() < rhs.sa_value.get()) + { + return true; + } + + return false; + } + + struct line_range sa_range; + const string_attr_type_base* sa_type{nullptr}; + string_attr_value sa_value; +}; + +template +struct string_attr_wrapper { + explicit string_attr_wrapper(const string_attr* sa) : saw_string_attr(sa) {} + + template + std::enable_if_t::value, const U&> get() const + { + return this->saw_string_attr->sa_value.template get(); + } + + const string_attr* saw_string_attr; +}; + +/** A map of line ranges to attributes for that range. */ +using string_attrs_t = std::vector; + +inline string_attrs_t::const_iterator +find_string_attr(const string_attrs_t& sa, + const string_attr_type_base* type, + int start = 0) +{ + string_attrs_t::const_iterator iter; + + for (iter = sa.begin(); iter != sa.end(); ++iter) { + if (iter->sa_type == type && iter->sa_range.lr_start >= start) { + break; + } + } + + return iter; +} + +inline nonstd::optional +get_string_attr(const string_attrs_t& sa, + const string_attr_type_base* type, + int start = 0) +{ + auto iter = find_string_attr(sa, type, start); + + if (iter == sa.end()) { + return nonstd::nullopt; + } + + return nonstd::make_optional(&(*iter)); +} + +template +inline nonstd::optional> +get_string_attr(const string_attrs_t& sa, + const string_attr_type& type, + int start = 0) +{ + auto iter = find_string_attr(sa, &type, start); + + if (iter == sa.end()) { + return nonstd::nullopt; + } + + return nonstd::make_optional(string_attr_wrapper(&(*iter))); +} + +template +inline string_attrs_t::const_iterator +find_string_attr_containing(const string_attrs_t& sa, + const string_attr_type_base* type, + T x) +{ + string_attrs_t::const_iterator iter; + + for (iter = sa.begin(); iter != sa.end(); ++iter) { + if (iter->sa_type == type && iter->sa_range.contains(x)) { + break; + } + } + + return iter; +} + +inline string_attrs_t::iterator +find_string_attr(string_attrs_t& sa, const struct line_range& lr) +{ + string_attrs_t::iterator iter; + + for (iter = sa.begin(); iter != sa.end(); ++iter) { + if (lr.contains(iter->sa_range)) { + break; + } + } + + return iter; +} + +inline string_attrs_t::const_iterator +find_string_attr(const string_attrs_t& sa, size_t near) +{ + auto nearest = sa.end(); + ssize_t last_diff = INT_MAX; + + for (auto iter = sa.begin(); iter != sa.end(); ++iter) { + const auto& lr = iter->sa_range; + + if (!lr.is_valid() || !lr.contains(near)) { + continue; + } + + ssize_t diff = near - lr.lr_start; + if (diff < last_diff) { + last_diff = diff; + nearest = iter; + } + } + + return nearest; +} + +template +inline string_attrs_t::const_iterator +rfind_string_attr_if(const string_attrs_t& sa, ssize_t near, T predicate) +{ + auto nearest = sa.end(); + ssize_t last_diff = INT_MAX; + + for (auto iter = sa.begin(); iter != sa.end(); ++iter) { + const auto& lr = iter->sa_range; + + if (lr.lr_start > near) { + continue; + } + + if (!predicate(*iter)) { + continue; + } + + ssize_t diff = near - lr.lr_start; + if (diff < last_diff) { + last_diff = diff; + nearest = iter; + } + } + + return nearest; +} + +inline struct line_range +find_string_attr_range(const string_attrs_t& sa, string_attr_type_base* type) +{ + auto iter = find_string_attr(sa, type); + + if (iter != sa.end()) { + return iter->sa_range; + } + + return line_range(); +} + +inline void +remove_string_attr(string_attrs_t& sa, const struct line_range& lr) +{ + string_attrs_t::iterator iter; + + while ((iter = find_string_attr(sa, lr)) != sa.end()) { + sa.erase(iter); + } +} + +inline void +remove_string_attr(string_attrs_t& sa, string_attr_type_base* type) +{ + for (auto iter = sa.begin(); iter != sa.end();) { + if (iter->sa_type == type) { + iter = sa.erase(iter); + } else { + ++iter; + } + } +} + +inline void +shift_string_attrs(string_attrs_t& sa, int32_t start, int32_t amount) +{ + for (auto& iter : sa) { + iter.sa_range.shift(start, amount); + } +} + +struct text_wrap_settings { + text_wrap_settings& with_indent(int indent) + { + this->tws_indent = indent; + return *this; + } + + text_wrap_settings& with_padding_indent(int indent) + { + this->tws_padding_indent = indent; + return *this; + } + + text_wrap_settings& with_width(int width) + { + this->tws_width = width; + return *this; + } + + int tws_indent{2}; + int tws_width{80}; + int tws_padding_indent{0}; +}; + +/** + * A line that has attributes. + */ +class attr_line_t { +public: + attr_line_t() = default; + + attr_line_t(std::string str) : al_string(std::move(str)) {} + + attr_line_t(const char* str) : al_string(str) {} + + static inline attr_line_t from_ansi_str(const char* str) + { + attr_line_t retval; + + return retval.with_ansi_string("%s", str); + } + + /** @return The string itself. */ + std::string& get_string() { return this->al_string; } + + const std::string& get_string() const { return this->al_string; } + + /** @return The attributes for the string. */ + string_attrs_t& get_attrs() { return this->al_attrs; } + + const string_attrs_t& get_attrs() const { return this->al_attrs; } + + attr_line_t& with_string(const std::string& str) + { + this->al_string = str; + return *this; + } + + attr_line_t& with_ansi_string(const char* str, ...); + + attr_line_t& with_ansi_string(const std::string& str); + + attr_line_t& with_attr(const string_attr& sa) + { + this->al_attrs.push_back(sa); + return *this; + } + + attr_line_t& ensure_space() + { + if (!this->al_string.empty() && this->al_string.back() != ' ' + && this->al_string.back() != '[') + { + this->append(1, ' '); + } + + return *this; + } + + template + attr_line_t& append(S str, const string_attr_pair& value) + { + size_t start_len = this->al_string.length(); + + this->al_string.append(str); + + line_range lr{(int) start_len, (int) this->al_string.length()}; + + this->al_attrs.emplace_back(lr, value); + + return *this; + } + + template + attr_line_t& append(const std::pair& value) + { + size_t start_len = this->al_string.length(); + + this->al_string.append(std::move(value.first)); + + line_range lr{(int) start_len, (int) this->al_string.length()}; + + this->al_attrs.emplace_back(lr, value.second); + + return *this; + } + + template + attr_line_t& append_quoted(const std::pair& value) + { + this->al_string.append("\u201c"); + + size_t start_len = this->al_string.length(); + + this->al_string.append(std::move(value.first)); + + line_range lr{(int) start_len, (int) this->al_string.length()}; + + this->al_attrs.emplace_back(lr, value.second); + + this->al_string.append("\u201d"); + + return *this; + } + + attr_line_t& append_quoted(const intern_string_t str) + { + this->al_string.append("\u201c"); + this->al_string.append(str.get(), str.size()); + this->al_string.append("\u201d"); + + return *this; + } + + attr_line_t& append_quoted(const attr_line_t& al) + { + this->al_string.append("\u201c"); + this->append(al); + this->al_string.append("\u201d"); + + return *this; + } + + template + attr_line_t& append_quoted(S s) + { + this->al_string.append("\u201c"); + this->append(std::move(s)); + this->al_string.append("\u201d"); + + return *this; + } + + attr_line_t& append(const intern_string_t str) + { + this->al_string.append(str.get(), str.size()); + return *this; + } + + attr_line_t& append(const string_fragment& str) + { + this->al_string.append(str.data(), str.length()); + return *this; + } + + template + attr_line_t& append(S str) + { + this->al_string.append(str); + return *this; + } + + template + attr_line_t& appendf(fmt::format_string fstr, Args&&... args) + { + this->template append( + fmt::vformat(fstr, fmt::make_format_args(args...))); + return *this; + } + + attr_line_t& with_attr_for_all(const string_attr_pair& sap) + { + this->al_attrs.emplace_back(line_range{0, -1}, sap); + return *this; + } + + template + attr_line_t& join(const C& container, + const string_attr_pair& sap, + const F& fill) + { + bool init = true; + for (const auto& elem : container) { + if (!init) { + this->append(fill); + } + this->append(std::make_pair(elem, sap)); + init = false; + } + + return *this; + } + + template + attr_line_t& join(const C& container, const F& fill) + { + bool init = true; + for (const auto& elem : container) { + if (!init) { + this->append(fill); + } + this->append(elem); + init = false; + } + + return *this; + } + + attr_line_t& insert(size_t index, + const attr_line_t& al, + text_wrap_settings* tws = nullptr); + + attr_line_t& append(const attr_line_t& al, + text_wrap_settings* tws = nullptr) + { + return this->insert(this->al_string.length(), al, tws); + } + + attr_line_t& append(size_t len, char c) + { + this->al_string.append(len, c); + return *this; + } + + attr_line_t& insert(size_t index, size_t len, char c) + { + this->al_string.insert(index, len, c); + + shift_string_attrs(this->al_attrs, index, len); + + return *this; + } + + attr_line_t& insert(size_t index, const char* str) + { + this->al_string.insert(index, str); + + shift_string_attrs(this->al_attrs, index, strlen(str)); + + return *this; + } + + template + attr_line_t& add_header(Args... args) + { + if (!this->blank()) { + this->insert(0, args...); + } + return *this; + } + + template + attr_line_t& with_default(Args... args) + { + if (this->blank()) { + this->clear(); + this->append(args...); + } + + return *this; + } + + attr_line_t& erase(size_t pos, size_t len = std::string::npos); + + attr_line_t& rtrim(); + + attr_line_t& erase_utf8_chars(size_t start) + { + auto byte_index = utf8_char_to_byte_index(this->al_string, start); + this->erase(byte_index); + + return *this; + } + + attr_line_t& right_justify(unsigned long width); + + attr_line_t& pad_to(ssize_t size); + + ssize_t length() const + { + size_t retval = this->al_string.length(); + + for (const auto& al_attr : this->al_attrs) { + retval = std::max(retval, (size_t) al_attr.sa_range.lr_start); + if (al_attr.sa_range.lr_end != -1) { + retval = std::max(retval, (size_t) al_attr.sa_range.lr_end); + } + } + + return retval; + } + + Result utf8_length() const + { + return utf8_string_length(this->al_string); + } + + ssize_t utf8_length_or_length() const + { + return utf8_string_length(this->al_string).unwrapOr(this->length()); + } + + std::string get_substring(const line_range& lr) const + { + if (!lr.is_valid()) { + return ""; + } + return this->al_string.substr(lr.lr_start, lr.sublen(this->al_string)); + } + + string_fragment to_string_fragment( + string_attrs_t::const_iterator iter) const + { + return string_fragment(this->al_string.c_str(), + iter->sa_range.lr_start, + iter->sa_range.end_for_string(this->al_string)); + } + + string_attrs_t::const_iterator find_attr(size_t near) const + { + near = std::min(near, this->al_string.length() - 1); + + while (near > 0 && isspace(this->al_string[near])) { + near -= 1; + } + + return find_string_attr(this->al_attrs, near); + } + + bool empty() const { return this->length() == 0; } + + bool blank() const { return is_blank(this->al_string); } + + /** Clear the string and the attributes for the string. */ + attr_line_t& clear() + { + this->al_string.clear(); + this->al_attrs.clear(); + + return *this; + } + + attr_line_t subline(size_t start, size_t len = std::string::npos) const; + + void split_lines(std::vector& lines) const; + + std::vector split_lines() const + { + std::vector retval; + + this->split_lines(retval); + return retval; + } + + size_t nearest_text(size_t x) const; + + void apply_hide(); + + std::string al_string; + string_attrs_t al_attrs; +}; + +#endif diff --git a/src/base/attr_line.tests.cc b/src/base/attr_line.tests.cc new file mode 100644 index 0000000..53b338e --- /dev/null +++ b/src/base/attr_line.tests.cc @@ -0,0 +1,91 @@ +/** + * Copyright (c) 2022, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include "attr_line.hh" + +#include "config.h" +#include "doctest/doctest.h" + +using namespace lnav::roles::literals; + +TEST_CASE("attr_line_t::basic-wrapping") +{ + text_wrap_settings tws = {3, 21}; + attr_line_t to_be_wrapped{"This line, right here, needs to be wrapped."}; + attr_line_t dst; + + to_be_wrapped.al_attrs.emplace_back( + line_range{0, (int) to_be_wrapped.al_string.length()}, + VC_ROLE.value(role_t::VCR_ERROR)); + dst.append(to_be_wrapped, &tws); + + CHECK(dst.get_string() == + "This line, right\n" + " here, needs to be\n" + " wrapped."); + + for (const auto& attr : dst.al_attrs) { + printf("attr %d:%d %s\n", + attr.sa_range.lr_start, + attr.sa_range.lr_end, + attr.sa_type->sat_name); + } +} + +TEST_CASE("attr_line_t::unicode-wrap") +{ + text_wrap_settings tws = {3, 21}; + attr_line_t prefix; + + prefix.append(" ") + .append("\u2022"_list_glyph) + .append(" ") + .with_attr_for_all(SA_PREFORMATTED.value()); + + attr_line_t body; + body.append("This is a long line that needs to be wrapped and indented"); + + attr_line_t li; + + li.append(prefix) + .append(body, &tws) + .with_attr_for_all(SA_PREFORMATTED.value()); + + attr_line_t dst; + + dst.append(li); + + CHECK(dst.get_string() + == " \u2022 This is a long\n" + " line that needs to\n" + " be wrapped and\n" + " indented"); +} diff --git a/src/base/auto_fd.hh b/src/base/auto_fd.hh new file mode 100644 index 0000000..da4a582 --- /dev/null +++ b/src/base/auto_fd.hh @@ -0,0 +1,318 @@ +/** + * Copyright (c) 2007-2012, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @file auto_fd.hh + */ + +#ifndef auto_fd_hh +#define auto_fd_hh + +#include +#include +#include + +#include +#include +#include +#include + +#include "base/lnav_log.hh" +#include "base/result.h" + +/** + * Resource management class for file descriptors. + * + * @see auto_ptr + */ +class auto_fd { +public: + /** + * Wrapper for the posix pipe(2) function that stores the file descriptor + * results in an auto_fd array. + * + * @param af An array of at least two auto_fd elements, where the first + * contains the reader end of the pipe and the second contains the writer. + * @return The result of the pipe(2) function. + */ + static int pipe(auto_fd* af) + { + int retval, fd[2]; + + require(af != nullptr); + + if ((retval = ::pipe(fd)) == 0) { + af[0] = fd[0]; + af[1] = fd[1]; + } + + return retval; + } + + /** + * dup(2) the given file descriptor and wrap it in an auto_fd. + * + * @param fd The file descriptor to duplicate. + * @return A new auto_fd that contains the duplicated file descriptor. + */ + static auto_fd dup_of(int fd) + { + if (fd == -1) { + return auto_fd{}; + } + + auto new_fd = ::dup(fd); + + if (new_fd == -1) { + throw std::bad_alloc(); + } + + return auto_fd(new_fd); + } + + /** + * Construct an auto_fd to manage the given file descriptor. + * + * @param fd The file descriptor to be managed. + */ + explicit auto_fd(int fd = -1) : af_fd(fd) { require(fd >= -1); } + + /** + * Non-const copy constructor. Management of the file descriptor will be + * transferred from the source to this object and the source will be + * cleared. + * + * @param af The source of the file descriptor. + */ + auto_fd(auto_fd&& af) noexcept : af_fd(af.release()) {} + + /** + * Const copy constructor. The file descriptor from the source will be + * dup(2)'d and the new descriptor stored in this object. + * + * @param af The source of the file descriptor. + */ + auto_fd(const auto_fd& af) = delete; + + auto_fd dup() const + { + int new_fd; + + if (this->af_fd == -1 || (new_fd = ::dup(this->af_fd)) == -1) { + throw std::bad_alloc(); + } + + return auto_fd{new_fd}; + } + + /** + * Destructor that will close the file descriptor managed by this object. + */ + ~auto_fd() { this->reset(); } + + /** @return The file descriptor as a plain integer. */ + operator int() const { return this->af_fd; } + + /** + * Replace the current descriptor with the given one. The current + * descriptor will be closed. + * + * @param fd The file descriptor to store in this object. + * @return *this + */ + auto_fd& operator=(int fd) + { + require(fd >= -1); + + this->reset(fd); + return *this; + } + + /** + * Transfer management of the given file descriptor to this object. + * + * @param af The old manager of the file descriptor. + * @return *this + */ + auto_fd& operator=(auto_fd&& af) noexcept + { + this->reset(af.release()); + return *this; + } + + /** + * Return a pointer that can be passed to functions that require an out + * parameter for file descriptors (e.g. openpty). + * + * @return A pointer to the internal integer. + */ + int* out() + { + this->reset(); + return &this->af_fd; + } + + /** + * Stop managing the file descriptor in this object and return its value. + * + * @return The file descriptor. + */ + int release() + { + int retval = this->af_fd; + + this->af_fd = -1; + return retval; + } + + /** + * @return The file descriptor. + */ + int get() const { return this->af_fd; } + + /** + * Closes the current file descriptor and replaces its value with the given + * one. + * + * @param fd The new file descriptor to be managed. + */ + void reset(int fd = -1) + { + require(fd >= -1); + + if (this->af_fd != fd) { + if (this->af_fd != -1) { + switch (this->af_fd) { + case STDIN_FILENO: + case STDOUT_FILENO: + case STDERR_FILENO: + break; + default: + close(this->af_fd); + break; + } + } + this->af_fd = fd; + } + } + + void close_on_exec() const + { + if (this->af_fd == -1) { + return; + } + log_perror(fcntl(this->af_fd, F_SETFD, FD_CLOEXEC)); + } + +private: + int af_fd; /*< The managed file descriptor. */ +}; + +class auto_pipe { +public: + static Result for_child_fd(int child_fd) + { + auto_pipe retval(child_fd); + + if (retval.open() == -1) { + return Err(std::string(strerror(errno))); + } + + return Ok(std::move(retval)); + } + + explicit auto_pipe(int child_fd = -1, int child_flags = O_RDONLY) + : ap_child_flags(child_flags), ap_child_fd(child_fd) + { + switch (child_fd) { + case STDIN_FILENO: + this->ap_child_flags = O_RDONLY; + break; + case STDOUT_FILENO: + case STDERR_FILENO: + this->ap_child_flags = O_WRONLY; + break; + } + } + + int open() { return auto_fd::pipe(this->ap_fd); } + + void close() + { + this->ap_fd[0].reset(); + this->ap_fd[1].reset(); + } + + auto_fd& read_end() { return this->ap_fd[0]; } + + auto_fd& write_end() { return this->ap_fd[1]; } + + void after_fork(pid_t child_pid) + { + int new_fd; + + switch (child_pid) { + case -1: + this->close(); + break; + case 0: + if (this->ap_child_flags == O_RDONLY) { + this->write_end().reset(); + if (this->read_end().get() == -1) { + this->read_end() = ::open("/dev/null", O_RDONLY); + } + new_fd = this->read_end().get(); + } else { + this->read_end().reset(); + if (this->write_end().get() == -1) { + this->write_end() = ::open("/dev/null", O_WRONLY); + } + new_fd = this->write_end().get(); + } + if (this->ap_child_fd != -1) { + if (new_fd != this->ap_child_fd) { + dup2(new_fd, this->ap_child_fd); + this->close(); + } + } + break; + default: + if (this->ap_child_flags == O_RDONLY) { + this->read_end().reset(); + } else { + this->write_end().reset(); + } + break; + } + } + + int ap_child_flags; + int ap_child_fd; + auto_fd ap_fd[2]; +}; + +#endif diff --git a/src/base/auto_mem.hh b/src/base/auto_mem.hh new file mode 100644 index 0000000..e6b456c --- /dev/null +++ b/src/base/auto_mem.hh @@ -0,0 +1,402 @@ +/** + * Copyright (c) 2007-2019, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @file auto_mem.hh + */ + +#ifndef lnav_auto_mem_hh +#define lnav_auto_mem_hh + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "base/result.h" + +using free_func_t = void (*)(void*); + +/** + * Resource management class for memory allocated by a custom allocator. + * + * @param T The object type. + * @param auto_free The function to call to free the managed object. + */ +template +class auto_mem { +public: + static void noop_free(void*) {} + + static auto_mem leak(T* ptr) + { + auto_mem retval(noop_free); + + retval = ptr; + + return retval; + } + + explicit auto_mem(T* ptr = nullptr) + : am_ptr(ptr), am_free_func(default_free) + { + } + + auto_mem(const auto_mem& am) = delete; + + template + explicit auto_mem(F free_func) noexcept + : am_ptr(nullptr), am_free_func((free_func_t) free_func) + { + } + + auto_mem(auto_mem&& other) noexcept + : am_ptr(other.release()), am_free_func(other.am_free_func) + { + } + + ~auto_mem() { this->reset(); } + + bool empty() const { return this->am_ptr == nullptr; } + + operator T*() const { return this->am_ptr; } + + T* operator->() { return this->am_ptr; } + + auto_mem& operator=(T* ptr) + { + this->reset(ptr); + return *this; + } + + auto_mem& operator=(auto_mem&) = delete; + + auto_mem& operator=(auto_mem&& am) noexcept + { + this->reset(am.release()); + this->am_free_func = am.am_free_func; + return *this; + } + + T* release() + { + T* retval = this->am_ptr; + + this->am_ptr = nullptr; + return retval; + } + + T* in() const { return this->am_ptr; } + + T** out() + { + this->reset(); + return &this->am_ptr; + } + + template + F get_free_func() const + { + return (F) this->am_free_func; + } + + void reset(T* ptr = nullptr) + { + if (this->am_ptr != ptr) { + if (this->am_ptr != nullptr) { + this->am_free_func((void*) this->am_ptr); + } + this->am_ptr = ptr; + } + } + +private: + T* am_ptr; + void (*am_free_func)(void*); +}; + +template +class static_root_mem { +public: + static_root_mem() { memset(&this->srm_value, 0, sizeof(T)); } + + ~static_root_mem() { free_func(&this->srm_value); } + + const T* operator->() const { return &this->srm_value; } + + const T& in() const { return this->srm_value; } + + T* inout() + { + free_func(&this->srm_value); + memset(&this->srm_value, 0, sizeof(T)); + return &this->srm_value; + } + +private: + static_root_mem& operator=(T&) { return *this; } + + static_root_mem& operator=(static_root_mem&) { return *this; } + + T srm_value; +}; + +class auto_buffer { +public: + using value_type = char; + + static auto_buffer alloc(size_t capacity) + { + return auto_buffer{capacity == 0 ? nullptr : (char*) malloc(capacity), + capacity}; + } + + static auto_buffer alloc_bitmap(size_t capacity_in_bits) + { + return alloc((capacity_in_bits + 7) / 8); + } + + static auto_buffer from(const char* mem, size_t size) + { + auto retval = alloc(size); + + retval.resize(size); + memcpy(retval.in(), mem, size); + return retval; + } + + auto_buffer(const auto_buffer&) = delete; + + auto_buffer(auto_buffer&& other) noexcept + : ab_buffer(other.ab_buffer), ab_size(other.ab_size), + ab_capacity(other.ab_capacity) + { + other.ab_buffer = nullptr; + other.ab_size = 0; + other.ab_capacity = 0; + } + + ~auto_buffer() + { + free(this->ab_buffer); + this->ab_buffer = nullptr; + this->ab_size = 0; + this->ab_capacity = 0; + } + + auto_buffer& operator=(auto_buffer&) = delete; + + auto_buffer& operator=(auto_buffer&& other) noexcept + { + free(this->ab_buffer); + this->ab_buffer = std::exchange(other.ab_buffer, nullptr); + this->ab_size = std::exchange(other.ab_size, 0); + this->ab_capacity = std::exchange(other.ab_capacity, 0); + return *this; + } + + void swap(auto_buffer& other) + { + std::swap(this->ab_buffer, other.ab_buffer); + std::swap(this->ab_size, other.ab_size); + std::swap(this->ab_capacity, other.ab_capacity); + } + + char* in() { return this->ab_buffer; } + + char* at(size_t offset) { return &this->ab_buffer[offset]; } + + const char* at(size_t offset) const { return &this->ab_buffer[offset]; } + + char* begin() { return this->ab_buffer; } + + const char* begin() const { return this->ab_buffer; } + + auto_buffer& push_back(char ch) + { + if (this->ab_size == this->ab_capacity) { + this->expand_by(256); + } + this->ab_buffer[this->ab_size] = ch; + this->ab_size += 1; + + return *this; + } + + void pop_back() { this->ab_size -= 1; } + + bool is_bit_set(size_t bit_offset) const + { + size_t byte_offset = bit_offset / 8; + auto bitmask = 1UL << (bit_offset % 8); + + return this->ab_buffer[byte_offset] & bitmask; + } + + void set_bit(size_t bit_offset) + { + size_t byte_offset = bit_offset / 8; + auto bitmask = 1UL << (bit_offset % 8); + + this->ab_buffer[byte_offset] |= bitmask; + } + + void clear_bit(size_t bit_offset) + { + size_t byte_offset = bit_offset / 8; + auto bitmask = 1UL << (bit_offset % 8); + + this->ab_buffer[byte_offset] &= ~bitmask; + } + + std::reverse_iterator rbegin() + { + return std::reverse_iterator(this->end()); + } + + std::reverse_iterator rbegin() const + { + return std::reverse_iterator(this->end()); + } + + char* end() { return &this->ab_buffer[this->ab_size]; } + + const char* end() const { return &this->ab_buffer[this->ab_size]; } + + std::reverse_iterator rend() + { + return std::reverse_iterator(this->begin()); + } + + std::reverse_iterator rend() const + { + return std::reverse_iterator(this->begin()); + } + + std::pair release() + { + auto retval = std::make_pair(this->ab_buffer, this->ab_size); + + this->ab_buffer = nullptr; + this->ab_size = 0; + this->ab_capacity = 0; + return retval; + } + + size_t size() const { return this->ab_size; } + + size_t bitmap_size() const { return this->ab_size * 8; } + + bool empty() const { return this->ab_size == 0; } + + bool full() const { return this->ab_size == this->ab_capacity; } + + size_t capacity() const { return this->ab_capacity; } + + size_t available() const { return this->ab_capacity - this->ab_size; } + + void clear() { this->resize(0); } + + auto_buffer& resize(size_t new_size) + { + assert(new_size <= this->ab_capacity); + + this->ab_size = new_size; + return *this; + } + + auto_buffer& resize_bitmap(size_t new_size_in_bits, int fill = 0) + { + auto new_size = (new_size_in_bits + 7) / 8; + assert(new_size <= this->ab_capacity); + + auto old_size = std::exchange(this->ab_size, new_size); + memset(this->at(old_size), 0, this->ab_size - old_size); + return *this; + } + + auto_buffer& resize_by(ssize_t amount) + { + return this->resize(this->ab_size + amount); + } + + void expand_to(size_t new_capacity) + { + if (new_capacity <= this->ab_capacity) { + return; + } + auto* new_buffer = (char*) realloc(this->ab_buffer, new_capacity); + + if (new_buffer == nullptr) { + throw std::bad_alloc(); + } + + this->ab_buffer = new_buffer; + this->ab_capacity = new_capacity; + } + + void expand_bitmap_to(size_t new_capacity_in_bits) + { + this->expand_to((new_capacity_in_bits + 7) / 8); + } + + void expand_by(size_t amount) + { + if (amount == 0) { + return; + } + + this->expand_to(this->ab_capacity + amount); + } + + std::string to_string() const { return {this->ab_buffer, this->ab_size}; } + +private: + auto_buffer(char* buffer, size_t capacity) + : ab_buffer(buffer), ab_capacity(capacity) + { + } + + char* ab_buffer; + size_t ab_size{0}; + size_t ab_capacity; +}; + +struct text_auto_buffer { + auto_buffer inner; +}; + +struct blob_auto_buffer { + auto_buffer inner; +}; + +#endif diff --git a/src/base/auto_pid.cc b/src/base/auto_pid.cc new file mode 100644 index 0000000..a662988 --- /dev/null +++ b/src/base/auto_pid.cc @@ -0,0 +1,63 @@ +/** + * Copyright (c) 2021, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "auto_pid.hh" + +#include + +#include "config.h" +#include "fmt/format.h" +#include "lnav_log.hh" + +namespace lnav { +namespace pid { + +bool in_child = false; + +Result, std::string> +from_fork() +{ + auto pid = ::fork(); + + if (pid == -1) { + return Err( + fmt::format(FMT_STRING("fork() failed: {}"), strerror(errno))); + } + + if (pid != 0) { + log_debug("started child: %d", pid); + } else { + in_child = true; + } + + return Ok(auto_pid(pid)); +} + +} // namespace pid +} // namespace lnav diff --git a/src/base/auto_pid.hh b/src/base/auto_pid.hh new file mode 100644 index 0000000..702af1e --- /dev/null +++ b/src/base/auto_pid.hh @@ -0,0 +1,175 @@ +/** + * Copyright (c) 2013, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @file auto_pid.hh + */ + +#ifndef auto_pid_hh +#define auto_pid_hh + +#include +#include + +#include +#include + +#include "base/lnav_log.hh" +#include "base/result.h" +#include "mapbox/variant.hpp" + +enum class process_state { + running, + finished, +}; + +template +class auto_pid { +public: + explicit auto_pid(pid_t child, int status = 0) + : ap_status(status), ap_child(child) + { + } + + auto_pid(const auto_pid& other) = delete; + + auto_pid(auto_pid&& other) noexcept + : ap_status(other.ap_status), ap_child(std::move(other).release()) + { + } + + ~auto_pid() noexcept + { + this->reset(); + } + + auto_pid& operator=(auto_pid&& other) noexcept + { + auto other_status = other.ap_status; + this->reset(std::move(other).release()); + this->ap_status = other_status; + return *this; + } + + auto_pid& operator=(const auto_pid& other) = delete; + + pid_t in() const + { + return this->ap_child; + } + + bool in_child() const + { + static_assert(ProcState == process_state::running, + "this method is only available in the RUNNING state"); + return this->ap_child == 0; + } + + pid_t release() && + { + return std::exchange(this->ap_child, -1); } + + int status() const + { + static_assert(ProcState == process_state::finished, + "wait_for_child() must be called first"); + return this->ap_status; + } + + bool was_normal_exit() const + { + static_assert(ProcState == process_state::finished, + "wait_for_child() must be called first"); + return WIFEXITED(this->ap_status); + } + + int exit_status() const + { + static_assert(ProcState == process_state::finished, + "wait_for_child() must be called first"); + return WEXITSTATUS(this->ap_status); + } + + using poll_result + = mapbox::util::variant, + auto_pid>; + + poll_result poll() && + { + if (this->ap_child != -1) { + auto rc = waitpid(this->ap_child, &this->ap_status, WNOHANG); + + if (rc <= 0) { + return std::move(*this); + } + } + + return auto_pid( + std::exchange(this->ap_child, -1), this->ap_status); + } + + auto_pid wait_for_child(int options = 0) && + { + if (this->ap_child != -1) { + while ((waitpid(this->ap_child, &this->ap_status, options)) < 0 + && (errno == EINTR)) + { + ; + } + } + + return auto_pid( + std::exchange(this->ap_child, -1), this->ap_status); + } + + void reset(pid_t child = -1) noexcept + { + if (this->ap_child != child) { + this->ap_status = 0; + if (ProcState == process_state::running && this->ap_child != -1) { + log_debug("sending SIGTERM to child: %d", this->ap_child); + kill(this->ap_child, SIGTERM); + } + this->ap_child = child; + } + } + +private: + int ap_status{0}; + pid_t ap_child; +}; + +namespace lnav { +namespace pid { + +extern bool in_child; + +Result, std::string> from_fork(); +} // namespace pid +} // namespace lnav + +#endif diff --git a/src/base/bus.hh b/src/base/bus.hh new file mode 100644 index 0000000..dea23ac --- /dev/null +++ b/src/base/bus.hh @@ -0,0 +1,68 @@ +/** + * Copyright (c) 2022, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef lnav_bus_hh +#define lnav_bus_hh + +#include + +#include "lnav_log.hh" + +template +class bus { +public: + bus() = default; + + virtual ~bus() { require(this->b_components.empty()); } + + bus(const bus&) = delete; + + void attach(T* component) + { + this->b_components.template emplace_back(component); + } + + void detach(T* component) + { + auto iter = this->b_components.begin(); + for (; iter != this->b_components.end(); ++iter) { + if (*iter == component) { + break; + } + } + require(iter != this->b_components.end()); + + this->b_components.erase(iter); + } + +protected: + std::vector b_components; +}; + +#endif diff --git a/src/base/date_time_scanner.cc b/src/base/date_time_scanner.cc new file mode 100644 index 0000000..72b7e5d --- /dev/null +++ b/src/base/date_time_scanner.cc @@ -0,0 +1,308 @@ +/** + * Copyright (c) 2020, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @file date_time_scanner.cc + */ + +#include + +#include "date_time_scanner.hh" + +#include "config.h" +#include "ptimec.hh" +#include "scn/scn.h" + +size_t +date_time_scanner::ftime(char* dst, + size_t len, + const char* const time_fmt[], + const exttm& tm) const +{ + off_t off = 0; + + if (time_fmt == nullptr) { + PTIMEC_FORMATS[this->dts_fmt_lock].pf_ffunc(dst, off, len, tm); + if (tm.et_flags & ETF_MILLIS_SET) { + dst[off++] = '.'; + ftime_L(dst, off, len, tm); + } else if (tm.et_flags & ETF_MICROS_SET) { + dst[off++] = '.'; + ftime_f(dst, off, len, tm); + } else if (tm.et_flags & ETF_NANOS_SET) { + dst[off++] = '.'; + ftime_N(dst, off, len, tm); + } + dst[off] = '\0'; + } else { + off = ftime_fmt(dst, len, time_fmt[this->dts_fmt_lock], tm); + } + + return (size_t) off; +} + +bool +next_format(const char* const fmt[], int& index, int& locked_index) +{ + bool retval = true; + + if (locked_index == -1) { + index += 1; + if (fmt[index] == nullptr) { + retval = false; + } + } else if (index == locked_index) { + retval = false; + } else { + index = locked_index; + } + + return retval; +} + +const char* +date_time_scanner::scan(const char* time_dest, + size_t time_len, + const char* const time_fmt[], + struct exttm* tm_out, + struct timeval& tv_out, + bool convert_local) +{ + int curr_time_fmt = -1; + bool found = false; + const char* retval = nullptr; + + if (!time_fmt) { + time_fmt = PTIMEC_FORMAT_STR; + } + + while (next_format(time_fmt, curr_time_fmt, this->dts_fmt_lock)) { + *tm_out = this->dts_base_tm; + tm_out->et_flags = 0; + if (time_len > 1 && time_dest[0] == '+' && isdigit(time_dest[1])) { + retval = nullptr; + auto epoch_scan_res = scn::scan_value( + scn::string_view{time_dest, time_len}); + if (epoch_scan_res) { + time_t gmt = epoch_scan_res.value(); + + if (convert_local && this->dts_local_time) { + localtime_r(&gmt, &tm_out->et_tm); +#ifdef HAVE_STRUCT_TM_TM_ZONE + tm_out->et_tm.tm_zone = nullptr; +#endif + tm_out->et_tm.tm_isdst = 0; + gmt = tm2sec(&tm_out->et_tm); + } + tv_out.tv_sec = gmt; + tv_out.tv_usec = 0; + tm_out->et_flags = ETF_DAY_SET | ETF_MONTH_SET | ETF_YEAR_SET + | ETF_MACHINE_ORIENTED | ETF_EPOCH_TIME; + + this->dts_fmt_lock = curr_time_fmt; + this->dts_fmt_len = std::distance(epoch_scan_res.begin(), + epoch_scan_res.end()); + retval = time_dest + this->dts_fmt_len; + found = true; + break; + } + } else if (time_fmt == PTIMEC_FORMAT_STR) { + ptime_func func = PTIMEC_FORMATS[curr_time_fmt].pf_func; + off_t off = 0; + +#ifdef HAVE_STRUCT_TM_TM_ZONE + if (!this->dts_keep_base_tz) { + tm_out->et_tm.tm_zone = nullptr; + } +#endif + if (func(tm_out, time_dest, off, time_len)) { + retval = &time_dest[off]; + + if (tm_out->et_tm.tm_year < 70) { + tm_out->et_tm.tm_year = 80; + } + if (convert_local + && (this->dts_local_time + || tm_out->et_flags & ETF_EPOCH_TIME)) + { + time_t gmt = tm2sec(&tm_out->et_tm); + + this->to_localtime(gmt, *tm_out); + } + const auto& last_tm = this->dts_last_tm.et_tm; + if (last_tm.tm_year == tm_out->et_tm.tm_year + && last_tm.tm_mon == tm_out->et_tm.tm_mon + && last_tm.tm_mday == tm_out->et_tm.tm_mday + && last_tm.tm_hour == tm_out->et_tm.tm_hour + && last_tm.tm_min == tm_out->et_tm.tm_min) + { + const auto sec_diff = tm_out->et_tm.tm_sec - last_tm.tm_sec; + + // log_debug("diff %d", sec_diff); + tv_out = this->dts_last_tv; + tv_out.tv_sec += sec_diff; + tm_out->et_tm.tm_wday = last_tm.tm_wday; + } else { + // log_debug("doing tm2sec"); + tv_out.tv_sec = tm2sec(&tm_out->et_tm); + secs2wday(tv_out, &tm_out->et_tm); + } + tv_out.tv_usec = tm_out->et_nsec / 1000; + + this->dts_fmt_lock = curr_time_fmt; + this->dts_fmt_len = retval - time_dest; + + found = true; + break; + } + } else { + off_t off = 0; + +#ifdef HAVE_STRUCT_TM_TM_ZONE + if (!this->dts_keep_base_tz) { + tm_out->et_tm.tm_zone = nullptr; + } +#endif + if (ptime_fmt( + time_fmt[curr_time_fmt], tm_out, time_dest, off, time_len) + && (time_dest[off] == '.' || time_dest[off] == ',' + || off == (off_t) time_len)) + { + retval = &time_dest[off]; + if (tm_out->et_tm.tm_year < 70) { + tm_out->et_tm.tm_year = 80; + } + if (convert_local + && (this->dts_local_time + || tm_out->et_flags & ETF_EPOCH_TIME)) + { + time_t gmt = tm2sec(&tm_out->et_tm); + + this->to_localtime(gmt, *tm_out); +#ifdef HAVE_STRUCT_TM_TM_ZONE + tm_out->et_tm.tm_zone = nullptr; +#endif + tm_out->et_tm.tm_isdst = 0; + } + + tv_out.tv_sec = tm2sec(&tm_out->et_tm); + tv_out.tv_usec = tm_out->et_nsec / 1000; + secs2wday(tv_out, &tm_out->et_tm); + + this->dts_fmt_lock = curr_time_fmt; + this->dts_fmt_len = retval - time_dest; + + found = true; + break; + } + } + } + + if (!found) { + retval = nullptr; + } + + if (retval != nullptr) { + this->dts_last_tm = *tm_out; + this->dts_last_tv = tv_out; + } + + if (retval != nullptr && static_cast(retval - time_dest) < time_len) + { + /* Try to pull out the milli/micro-second value. */ + if (retval[0] == '.' || retval[0] == ',') { + off_t off = (retval - time_dest) + 1; + + if (ptime_N(tm_out, time_dest, off, time_len)) { + tv_out.tv_usec + = std::chrono::duration_cast( + std::chrono::nanoseconds{tm_out->et_nsec}) + .count(); + this->dts_fmt_len += 10; + tm_out->et_flags |= ETF_NANOS_SET; + retval += 10; + } else if (ptime_f(tm_out, time_dest, off, time_len)) { + tv_out.tv_usec + = std::chrono::duration_cast( + std::chrono::nanoseconds{tm_out->et_nsec}) + .count(); + this->dts_fmt_len += 7; + tm_out->et_flags |= ETF_MICROS_SET; + retval += 7; + } else if (ptime_L(tm_out, time_dest, off, time_len)) { + tv_out.tv_usec + = std::chrono::duration_cast( + std::chrono::nanoseconds{tm_out->et_nsec}) + .count(); + this->dts_fmt_len += 4; + tm_out->et_flags |= ETF_MILLIS_SET; + retval += 4; + } + } + } + + return retval; +} + +void +date_time_scanner::set_base_time(time_t base_time, const tm& local_tm) +{ + this->dts_base_time = base_time; + this->dts_base_tm.et_tm = local_tm; + this->dts_last_tm = exttm{}; + this->dts_last_tv = timeval{}; +} + +void +date_time_scanner::to_localtime(time_t t, exttm& tm_out) +{ + if (t < (24 * 60 * 60)) { + // Don't convert and risk going past the epoch. + return; + } + + if (t < this->dts_local_offset_valid || t >= this->dts_local_offset_expiry) + { + time_t new_gmt; + + localtime_r(&t, &tm_out.et_tm); +#ifdef HAVE_STRUCT_TM_TM_ZONE + tm_out.et_tm.tm_zone = nullptr; +#endif + tm_out.et_tm.tm_isdst = 0; + + new_gmt = tm2sec(&tm_out.et_tm); + this->dts_local_offset_cache = t - new_gmt; + this->dts_local_offset_valid = t; + this->dts_local_offset_expiry = t + (EXPIRE_TIME - 1); + this->dts_local_offset_expiry + -= this->dts_local_offset_expiry % EXPIRE_TIME; + } else { + time_t adjust_gmt = t - this->dts_local_offset_cache; + gmtime_r(&adjust_gmt, &tm_out.et_tm); + } +} diff --git a/src/base/date_time_scanner.hh b/src/base/date_time_scanner.hh new file mode 100644 index 0000000..90eaffa --- /dev/null +++ b/src/base/date_time_scanner.hh @@ -0,0 +1,137 @@ +/** + * Copyright (c) 2020, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @file date_time_scanner.hh + */ + +#ifndef lnav_date_time_scanner_hh +#define lnav_date_time_scanner_hh + +#include +#include + +#include + +#include "time_util.hh" + +/** + * Scans a timestamp string to discover the date-time format using the custom + * ptimec parser. Once a format is found, it is locked in so that the next + * time a timestamp needs to be scanned, the format does not have to be + * rediscovered. The discovered date-time format can also be used to convert + * an exttm struct to a string using the ftime() method. + */ +struct date_time_scanner { + date_time_scanner() { this->clear(); } + + void clear() + { + this->dts_base_time = 0; + this->dts_base_tm = exttm{}; + this->dts_fmt_lock = -1; + this->dts_fmt_len = -1; + this->dts_last_tv = timeval{}; + this->dts_last_tm = exttm{}; + } + + /** + * Unlock this scanner so that the format is rediscovered. + */ + void unlock() + { + this->dts_fmt_lock = -1; + this->dts_fmt_len = -1; + } + + void set_base_time(time_t base_time, const tm& local_tm); + + /** + * Convert a timestamp to local time. + * + * Calling localtime_r is slow since it wants to lookup the timezone on + * every call, so we cache the result and only call it again if the + * requested time falls outside of a fifteen minute range. + */ + void to_localtime(time_t t, struct exttm& tm_out); + + bool dts_keep_base_tz{false}; + bool dts_local_time{false}; + time_t dts_base_time{0}; + struct exttm dts_base_tm; + int dts_fmt_lock{-1}; + int dts_fmt_len{-1}; + struct exttm dts_last_tm {}; + struct timeval dts_last_tv {}; + time_t dts_local_offset_cache{0}; + time_t dts_local_offset_valid{0}; + time_t dts_local_offset_expiry{0}; + + static const int EXPIRE_TIME = 15 * 60; + + const char* scan(const char* time_src, + size_t time_len, + const char* const time_fmt[], + struct exttm* tm_out, + struct timeval& tv_out, + bool convert_local = true); + + size_t ftime(char* dst, + size_t len, + const char* const time_fmt[], + const struct exttm& tm) const; + + bool convert_to_timeval(const char* time_src, + ssize_t time_len, + const char* const time_fmt[], + struct timeval& tv_out) + { + struct exttm tm; + + if (time_len == -1) { + time_len = strlen(time_src); + } + if (this->scan(time_src, time_len, time_fmt, &tm, tv_out) != nullptr) { + return true; + } + return false; + } + + bool convert_to_timeval(const std::string& time_src, struct timeval& tv_out) + { + struct exttm tm; + + if (this->scan(time_src.c_str(), time_src.size(), nullptr, &tm, tv_out) + != nullptr) + { + return true; + } + return false; + } +}; + +#endif diff --git a/src/base/enum_util.hh b/src/base/enum_util.hh new file mode 100644 index 0000000..437292d --- /dev/null +++ b/src/base/enum_util.hh @@ -0,0 +1,48 @@ +/** + * Copyright (c) 2019, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef lnav_enum_util_hh +#define lnav_enum_util_hh + +#include + +namespace lnav { +namespace enums { + +template +constexpr auto +to_underlying(E e) noexcept +{ + return static_cast>(e); +} + +} // namespace enums +} // namespace lnav + +#endif diff --git a/src/base/file_range.hh b/src/base/file_range.hh new file mode 100644 index 0000000..d9a3de4 --- /dev/null +++ b/src/base/file_range.hh @@ -0,0 +1,78 @@ +/** + * Copyright (c) 2019, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef lnav_file_range_hh +#define lnav_file_range_hh + +#include + +#include "intern_string.hh" + +using file_off_t = int64_t; +using file_size_t = uint64_t; +using file_ssize_t = int64_t; + +class file_range { +public: + struct metadata { + bool m_valid_utf{true}; + bool m_has_ansi{false}; + }; + + file_off_t fr_offset{0}; + file_ssize_t fr_size{0}; + metadata fr_metadata; + + void clear() + { + this->fr_offset = 0; + this->fr_size = 0; + } + + ssize_t next_offset() const { return this->fr_offset + this->fr_size; } + + bool empty() const { return this->fr_size == 0; } +}; + +struct source_location { + source_location() + : sl_source(intern_string::lookup("unknown")), sl_line_number(0) + { + } + + explicit source_location(intern_string_t source, int32_t line = 0) + : sl_source(source), sl_line_number(line) + { + } + + intern_string_t sl_source; + int32_t sl_line_number; +}; + +#endif diff --git a/src/base/fs_util.cc b/src/base/fs_util.cc new file mode 100644 index 0000000..f72aaa6 --- /dev/null +++ b/src/base/fs_util.cc @@ -0,0 +1,183 @@ +/** + * Copyright (c) 2022, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "fs_util.hh" + +#include "config.h" +#include "fmt/format.h" +#include "itertools.hh" +#include "opt_util.hh" + +namespace lnav { +namespace filesystem { + +Result +create_file(const ghc::filesystem::path& path, int flags, mode_t mode) +{ + auto fd = openp(path, flags | O_CREAT, mode); + + if (fd == -1) { + return Err(fmt::format(FMT_STRING("Failed to open: {} -- {}"), + path.string(), + strerror(errno))); + } + + return Ok(auto_fd(fd)); +} + +Result +open_file(const ghc::filesystem::path& path, int flags) +{ + auto fd = openp(path, flags); + + if (fd == -1) { + return Err(fmt::format(FMT_STRING("Failed to open: {} -- {}"), + path.string(), + strerror(errno))); + } + + return Ok(auto_fd(fd)); +} + +Result, std::string> +open_temp_file(const ghc::filesystem::path& pattern) +{ + auto pattern_str = pattern.string(); + char pattern_copy[pattern_str.size() + 1]; + int fd; + + strcpy(pattern_copy, pattern_str.c_str()); + if ((fd = mkstemp(pattern_copy)) == -1) { + return Err( + fmt::format(FMT_STRING("unable to create temporary file: {} -- {}"), + pattern.string(), + strerror(errno))); + } + + return Ok(std::make_pair(ghc::filesystem::path(pattern_copy), auto_fd(fd))); +} + +Result +read_file(const ghc::filesystem::path& path) +{ + try { + ghc::filesystem::ifstream file_stream(path); + + if (!file_stream) { + return Err(std::string(strerror(errno))); + } + + std::string retval; + retval.assign((std::istreambuf_iterator(file_stream)), + std::istreambuf_iterator()); + return Ok(retval); + } catch (const std::exception& e) { + return Err(std::string(e.what())); + } +} + +Result +write_file(const ghc::filesystem::path& path, const string_fragment& content) +{ + auto tmp_pattern = path; + tmp_pattern += ".XXXXXX"; + + auto tmp_pair = TRY(open_temp_file(tmp_pattern)); + auto bytes_written + = write(tmp_pair.second.get(), content.data(), content.length()); + if (bytes_written < 0) { + return Err( + fmt::format(FMT_STRING("unable to write to temporary file {}: {}"), + tmp_pair.first.string(), + strerror(errno))); + } + if (bytes_written != content.length()) { + return Err(fmt::format(FMT_STRING("short write to file {}: {} < {}"), + tmp_pair.first.string(), + bytes_written, + content.length())); + } + std::error_code ec; + ghc::filesystem::rename(tmp_pair.first, path, ec); + if (ec) { + return Err( + fmt::format(FMT_STRING("unable to move temporary file {}: {}"), + tmp_pair.first.string(), + ec.message())); + } + + return Ok(); +} + +std::string +build_path(const std::vector& paths) +{ + return paths + | lnav::itertools::map([](const auto& path) { return path.string(); }) + | lnav::itertools::append(getenv_opt("PATH").value_or("")) + | lnav::itertools::filter_out(&std::string::empty) + | lnav::itertools::fold( + [](const auto& elem, auto& accum) { + if (!accum.empty()) { + accum.push_back(':'); + } + return accum.append(elem); + }, + std::string()); +} + +Result +stat_file(const ghc::filesystem::path& path) +{ + struct stat retval; + + if (statp(path, &retval) == 0) { + return Ok(retval); + } + + return Err(fmt::format(FMT_STRING("failed to find file: {} -- {}"), + path.string(), + strerror(errno))); +} + +file_lock::file_lock(const ghc::filesystem::path& archive_path) +{ + auto lock_path = archive_path; + + lock_path += ".lck"; + auto open_res + = lnav::filesystem::create_file(lock_path, O_RDWR | O_CLOEXEC, 0600); + if (open_res.isErr()) { + throw std::runtime_error(open_res.unwrapErr()); + } + this->lh_fd = open_res.unwrap(); +} + +} // namespace filesystem +} // namespace lnav diff --git a/src/base/fs_util.hh b/src/base/fs_util.hh new file mode 100644 index 0000000..b9253ff --- /dev/null +++ b/src/base/fs_util.hh @@ -0,0 +1,122 @@ +/** + * Copyright (c) 2022, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef lnav_fs_util_hh +#define lnav_fs_util_hh + +#include +#include + +#include "auto_fd.hh" +#include "ghc/filesystem.hpp" +#include "intern_string.hh" +#include "result.h" + +namespace lnav { +namespace filesystem { + +inline int +statp(const ghc::filesystem::path& path, struct stat* buf) +{ + return stat(path.c_str(), buf); +} + +inline int +openp(const ghc::filesystem::path& path, int flags) +{ + return open(path.c_str(), flags); +} + +inline int +openp(const ghc::filesystem::path& path, int flags, mode_t mode) +{ + return open(path.c_str(), flags, mode); +} + +Result create_file(const ghc::filesystem::path& path, + int flags, + mode_t mode); + +Result open_file(const ghc::filesystem::path& path, + int flags); + +Result stat_file(const ghc::filesystem::path& path); + +Result, std::string> open_temp_file( + const ghc::filesystem::path& pattern); + +Result read_file(const ghc::filesystem::path& path); + +Result write_file(const ghc::filesystem::path& path, + const string_fragment& content); + +std::string build_path(const std::vector& paths); + +class file_lock { +public: + class guard { + public: + explicit guard(file_lock* arc_lock) : g_lock(arc_lock) + { + this->g_lock->lock(); + } + + guard(guard&& other) noexcept + : g_lock(std::exchange(other.g_lock, nullptr)) + { + } + + ~guard() + { + if (this->g_lock != nullptr) { + this->g_lock->unlock(); + } + } + + guard(const guard&) = delete; + guard& operator=(const guard&) = delete; + guard& operator=(guard&&) = delete; + + private: + file_lock* g_lock; + }; + + void lock() const { lockf(this->lh_fd, F_LOCK, 0); } + + void unlock() const { lockf(this->lh_fd, F_ULOCK, 0); } + + explicit file_lock(const ghc::filesystem::path& archive_path); + + auto_fd lh_fd; +}; + +} // namespace filesystem +} // namespace lnav + +#endif diff --git a/src/base/fs_util.tests.cc b/src/base/fs_util.tests.cc new file mode 100644 index 0000000..9ed3377 --- /dev/null +++ b/src/base/fs_util.tests.cc @@ -0,0 +1,56 @@ +/** + * Copyright (c) 2022, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include "base/fs_util.hh" + +#include "config.h" +#include "doctest/doctest.h" + +TEST_CASE("fs_util::build_path") +{ + auto* old_path = getenv("PATH"); + unsetenv("PATH"); + + CHECK("" == lnav::filesystem::build_path({})); + + CHECK("/bin:/usr/bin" + == lnav::filesystem::build_path({"", "/bin", "/usr/bin", ""})); + setenv("PATH", "/usr/local/bin", 1); + CHECK("/bin:/usr/bin:/usr/local/bin" + == lnav::filesystem::build_path({"", "/bin", "/usr/bin", ""})); + setenv("PATH", "/usr/local/bin:/opt/bin", 1); + CHECK("/usr/local/bin:/opt/bin" == lnav::filesystem::build_path({})); + CHECK("/bin:/usr/bin:/usr/local/bin:/opt/bin" + == lnav::filesystem::build_path({"", "/bin", "/usr/bin", ""})); + if (old_path != nullptr) { + setenv("PATH", old_path, 1); + } +} diff --git a/src/base/func_util.hh b/src/base/func_util.hh new file mode 100644 index 0000000..01a2328 --- /dev/null +++ b/src/base/func_util.hh @@ -0,0 +1,159 @@ +/** + * Copyright (c) 2020, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef lnav_func_util_hh +#define lnav_func_util_hh + +#include +#include + +template +decltype(auto) +bind_mem(F&& f, FrontArg&& frontArg) +{ + return [f = std::forward(f), + frontArg = std::forward(frontArg)](auto&&... backArgs) { + return (frontArg->*f)(std::forward(backArgs)...); + }; +} + +struct noop_func { + struct anything { + template + operator T() + { + return {}; + } + // optional reference support. Somewhat evil. + template + operator T&() const + { + static T t{}; + return t; + } + }; + template + anything operator()(Args&&...) const + { + return {}; + } +}; + +namespace lnav { +namespace func { + +class scoped_cb { +public: + class guard { + public: + explicit guard(scoped_cb* owner) : g_owner(owner) {} + + guard(const guard&) = delete; + guard& operator=(const guard&) = delete; + + guard(guard&& gu) noexcept : g_owner(std::exchange(gu.g_owner, nullptr)) + { + } + + guard& operator=(guard&& gu) noexcept + { + this->g_owner = std::exchange(gu.g_owner, nullptr); + return *this; + } + + ~guard() + { + if (this->g_owner != nullptr) { + this->g_owner->s_callback = {}; + } + } + + private: + scoped_cb* g_owner; + }; + + guard install(std::function cb) + { + this->s_callback = std::move(cb); + + return guard{this}; + } + + void operator()() + { + if (s_callback) { + s_callback(); + } + } + +private: + std::function s_callback; +}; + +template>{}, int> = 0> +constexpr decltype(auto) +invoke(Fn&& f, Args&&... args) noexcept( + noexcept(std::mem_fn(f)(std::forward(args)...))) +{ + return std::mem_fn(f)(std::forward(args)...); +} + +template>{}, int> = 0> +constexpr decltype(auto) +invoke(Fn&& f, Args&&... args) noexcept( + noexcept(std::forward(f)(std::forward(args)...))) +{ + return std::forward(f)(std::forward(args)...); +} + +template +struct is_invocable { + template + static auto test(U&& p) + -> decltype((std::declval().*p)(std::declval()...), + void(), + std::true_type()); + template + static auto test(U* p) -> decltype((*p)(std::declval()...), + void(), + std::true_type()); + template + static auto test(...) -> decltype(std::false_type()); + + static constexpr bool value = decltype(test(0))::value; +}; + +} // namespace func +} // namespace lnav + +#endif diff --git a/src/base/future_util.hh b/src/base/future_util.hh new file mode 100644 index 0000000..8797faa --- /dev/null +++ b/src/base/future_util.hh @@ -0,0 +1,111 @@ +/** + * Copyright (c) 2020, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef lnav_future_util_hh +#define lnav_future_util_hh + +#include +#include + +namespace lnav { +namespace futures { + +/** + * Create a future that is ready to immediately return a result. + * + * @tparam T The result type of the future. + * @param t The value the future should return. + * @return The new future. + */ +template +std::future> +make_ready_future(T&& t) +{ + std::promise> pr; + auto r = pr.get_future(); + pr.set_value(std::forward(t)); + return r; +} + +/** + * A queue used to limit the number of futures that are running concurrently. + * + * @tparam T The result of the futures. + * @tparam MAX_QUEUE_SIZE The maximum number of futures that can be in flight. + */ +template +class future_queue { +public: + /** + * @param processor The function to execute with the result of a future. + */ + explicit future_queue(std::function processor) + : fq_processor(processor){}; + + ~future_queue() + { + this->pop_to(); + } + + /** + * Add a future to the queue. If the size of the queue is greater than the + * MAX_QUEUE_SIZE, this call will block waiting for the first queued + * future to return a result. + * + * @param f The future to add to the queue. + */ + void push_back(std::future&& f) + { + this->fq_deque.emplace_back(std::move(f)); + this->pop_to(MAX_QUEUE_SIZE); + } + + /** + * Removes the next future from the queue, waits for the result, and then + * repeats until the queue reaches the given size. + * + * @param size The new desired size of the queue. + */ + void pop_to(size_t size = 0) + { + while (this->fq_deque.size() > size) { + auto v = this->fq_deque.front().get(); + this->fq_processor(v); + this->fq_deque.pop_front(); + } + } + + std::function fq_processor; + std::deque> fq_deque; +}; + +} // namespace futures +} // namespace lnav + +#endif diff --git a/src/base/humanize.cc b/src/base/humanize.cc new file mode 100644 index 0000000..5e96bf3 --- /dev/null +++ b/src/base/humanize.cc @@ -0,0 +1,110 @@ +/** + * Copyright (c) 2019, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include "humanize.hh" + +#include "config.h" +#include "fmt/format.h" + +namespace humanize { + +std::string +file_size(file_ssize_t value, alignment align) +{ + static const double LN1024 = log(1024.0); + static const std::vector UNITS = { + " ", + "K", + "M", + "G", + "T", + "P", + "E", + }; + + if (value < 0) { + return "Unknown"; + } + + if (value == 0) { + switch (align) { + case alignment::none: + return "0B"; + case alignment::columnar: + return "0.0 B"; + } + } + + auto exp + = floor(std::min(log(value) / LN1024, (double) (UNITS.size() - 1))); + auto divisor = pow(1024, exp); + + if (align == alignment::none && divisor <= 1) { + return fmt::format(FMT_STRING("{}B"), value, UNITS[exp]); + } + return fmt::format(FMT_STRING("{:.1f}{}B"), + divisor == 0 ? value : value / divisor, + UNITS[exp]); +} + +const std::string& +sparkline(double value, nonstd::optional upper_opt) +{ + static const std::string ZERO = " "; + static const std::string BARS[] = { + "\u2581", + "\u2582", + "\u2583", + "\u2584", + "\u2585", + "\u2586", + "\u2587", + "\u2588", + }; + static const double BARS_COUNT = std::distance(begin(BARS), end(BARS)); + + if (value <= 0.0) { + return ZERO; + } + + auto upper = upper_opt.value_or(100.0); + + if (value >= upper) { + return BARS[(size_t) BARS_COUNT - 1]; + } + + size_t index = ceil((value / upper) * BARS_COUNT) - 1; + + return BARS[index]; +} + +} // namespace humanize diff --git a/src/base/humanize.file_size.tests.cc b/src/base/humanize.file_size.tests.cc new file mode 100644 index 0000000..ff70c7e --- /dev/null +++ b/src/base/humanize.file_size.tests.cc @@ -0,0 +1,51 @@ +/** + * Copyright (c) 2021, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include "base/humanize.hh" + +#include "config.h" +#include "doctest/doctest.h" + +TEST_CASE("humanize::file_size") +{ + CHECK(humanize::file_size(0, humanize::alignment::columnar) == "0.0 B"); + CHECK(humanize::file_size(1, humanize::alignment::columnar) == "1.0 B"); + CHECK(humanize::file_size(1024, humanize::alignment::columnar) == "1.0KB"); + CHECK(humanize::file_size(1500, humanize::alignment::columnar) == "1.5KB"); + CHECK(humanize::file_size(55LL * 784LL * 1024LL * 1024LL, + humanize::alignment::columnar) + == "42.1GB"); + CHECK(humanize::file_size(-1LL, humanize::alignment::columnar) + == "Unknown"); + CHECK(humanize::file_size(std::numeric_limits::max(), + humanize::alignment::columnar) + == "8.0EB"); +} diff --git a/src/base/humanize.hh b/src/base/humanize.hh new file mode 100644 index 0000000..3f9f66c --- /dev/null +++ b/src/base/humanize.hh @@ -0,0 +1,58 @@ +/** + * Copyright (c) 2020, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef lnav_humanize_hh +#define lnav_humanize_hh + +#include + +#include + +#include "file_range.hh" + +namespace humanize { + +enum class alignment { + none, + columnar, +}; + +/** + * Format the given size as a human-friendly string. + * + * @param value The value to format. + * @return The formatted string. + */ +std::string file_size(file_ssize_t value, alignment align); + +const std::string& sparkline(double value, nonstd::optional upper); + +} // namespace humanize + +#endif diff --git a/src/base/humanize.network.cc b/src/base/humanize.network.cc new file mode 100644 index 0000000..2bf390d --- /dev/null +++ b/src/base/humanize.network.cc @@ -0,0 +1,76 @@ +/** + * Copyright (c) 2021, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "humanize.network.hh" + +#include "config.h" +#include "pcrepp/pcre2pp.hh" + +namespace humanize { +namespace network { +namespace path { + +nonstd::optional<::network::path> +from_str(string_fragment sf) +{ + static const auto REMOTE_PATTERN = lnav::pcre2pp::code::from_const( + "^(?:(?[\\w\\._\\-]+)@)?" + "(?:\\[(?[^\\]]+)\\]|(?[^\\[/:]+)):" + "(?.*)$"); + static thread_local auto REMOTE_MATCH_DATA + = REMOTE_PATTERN.create_match_data(); + + auto match_res = REMOTE_PATTERN.capture_from(sf) + .into(REMOTE_MATCH_DATA) + .matches() + .ignore_error(); + + if (!match_res) { + return nonstd::nullopt; + } + + const auto username = REMOTE_MATCH_DATA["username"].map( + [](auto sf) { return sf.to_string(); }); + const auto ipv6 = REMOTE_MATCH_DATA["ipv6"]; + const auto hostname = REMOTE_MATCH_DATA["hostname"]; + const auto locality_hostname = ipv6 ? ipv6.value() : hostname.value(); + auto path = *REMOTE_MATCH_DATA["path"]; + + if (path.empty()) { + path = string_fragment::from_const("."); + } + return ::network::path{ + {username, locality_hostname.to_string(), nonstd::nullopt}, + path.to_string(), + }; +} + +} // namespace path +} // namespace network +} // namespace humanize diff --git a/src/base/humanize.network.hh b/src/base/humanize.network.hh new file mode 100644 index 0000000..609f57d --- /dev/null +++ b/src/base/humanize.network.hh @@ -0,0 +1,109 @@ +/** + * Copyright (c) 2021, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef lnav_humanize_network_hh +#define lnav_humanize_network_hh + +#include + +#include "fmt/format.h" +#include "intern_string.hh" +#include "network.tcp.hh" +#include "optional.hpp" + +namespace fmt { + +template<> +struct formatter { + constexpr auto parse(format_parse_context& ctx) + { + const auto it = ctx.begin(); + const auto end = ctx.end(); + + // Check if reached the end of the range: + if (it != end && *it != '}') { + throw format_error("invalid format"); + } + + // Return an iterator past the end of the parsed range: + return it; + } + + template + auto format(const network::locality& l, FormatContext& ctx) + { + bool is_ipv6 = l.l_hostname.find(':') != std::string::npos; + + return format_to(ctx.out(), + "{}{}{}{}{}", + l.l_username.value_or(std::string()), + l.l_username ? "@" : "", + is_ipv6 ? "[" : "", + l.l_hostname, + is_ipv6 ? "]" : ""); + } +}; + +template<> +struct formatter { + constexpr auto parse(format_parse_context& ctx) + { + const auto it = ctx.begin(); + const auto end = ctx.end(); + + // Check if reached the end of the range: + if (it != end && *it != '}') { + throw format_error("invalid format"); + } + + // Return an iterator past the end of the parsed range: + return it; + } + + template + auto format(const network::path& p, FormatContext& ctx) + { + return format_to( + ctx.out(), "{}:{}", p.p_locality, p.p_path == "." ? "" : p.p_path); + } +}; + +} // namespace fmt + +namespace humanize { +namespace network { +namespace path { + +nonstd::optional<::network::path> from_str(string_fragment sf); + +} // namespace path +} // namespace network +} // namespace humanize + +#endif diff --git a/src/base/humanize.network.tests.cc b/src/base/humanize.network.tests.cc new file mode 100644 index 0000000..fe2e9d2 --- /dev/null +++ b/src/base/humanize.network.tests.cc @@ -0,0 +1,118 @@ +/** + * Copyright (c) 2021, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include "base/humanize.network.hh" +#include "config.h" +#include "doctest/doctest.h" + +TEST_CASE("humanize::network::path") +{ + { + auto rp_opt = humanize::network::path::from_str( + string_fragment::from_const("foobar")); + CHECK(!rp_opt); + } + { + auto rp_opt = humanize::network::path::from_str( + string_fragment::from_const("dean@foobar/bar")); + CHECK(!rp_opt); + } + + { + auto rp_opt = humanize::network::path::from_str( + string_fragment::from_const("dean@host1.example.com:/var/log")); + CHECK(rp_opt.has_value()); + + auto rp = *rp_opt; + CHECK(rp.p_locality.l_username.has_value()); + CHECK(rp.p_locality.l_username.value() == "dean"); + CHECK(rp.p_locality.l_hostname == "host1.example.com"); + CHECK(!rp.p_locality.l_service.has_value()); + CHECK(rp.p_path == "/var/log"); + } + + { + auto rp_opt + = humanize::network::path::from_str(string_fragment::from_const( + "dean@[fe80::184f:c67:baf1:fe02%en0]:/var/log")); + CHECK(rp_opt.has_value()); + + auto rp = *rp_opt; + CHECK(rp.p_locality.l_username.has_value()); + CHECK(rp.p_locality.l_username.value() == "dean"); + CHECK(rp.p_locality.l_hostname == "fe80::184f:c67:baf1:fe02%en0"); + CHECK(!rp.p_locality.l_service.has_value()); + CHECK(rp.p_path == "/var/log"); + + CHECK(fmt::format("{}", rp.p_locality) + == "dean@[fe80::184f:c67:baf1:fe02%en0]"); + } + + { + auto rp_opt + = humanize::network::path::from_str(string_fragment::from_const( + "[fe80::184f:c67:baf1:fe02%en0]:/var/log")); + CHECK(rp_opt.has_value()); + + auto rp = *rp_opt; + CHECK(!rp.p_locality.l_username.has_value()); + CHECK(rp.p_locality.l_hostname == "fe80::184f:c67:baf1:fe02%en0"); + CHECK(!rp.p_locality.l_service.has_value()); + CHECK(rp.p_path == "/var/log"); + + CHECK(fmt::format("{}", rp.p_locality) + == "[fe80::184f:c67:baf1:fe02%en0]"); + } + + { + auto rp_opt = humanize::network::path::from_str( + string_fragment::from_const("host1.example.com:/var/log")); + CHECK(rp_opt.has_value()); + + auto rp = *rp_opt; + CHECK(!rp.p_locality.l_username.has_value()); + CHECK(rp.p_locality.l_hostname == "host1.example.com"); + CHECK(!rp.p_locality.l_service.has_value()); + CHECK(rp.p_path == "/var/log"); + } + + { + auto rp_opt = humanize::network::path::from_str( + string_fragment::from_const("host1.example.com:")); + CHECK(rp_opt.has_value()); + + auto rp = *rp_opt; + CHECK(!rp.p_locality.l_username.has_value()); + CHECK(rp.p_locality.l_hostname == "host1.example.com"); + CHECK(!rp.p_locality.l_service.has_value()); + CHECK(rp.p_path == "."); + } +} diff --git a/src/base/humanize.time.cc b/src/base/humanize.time.cc new file mode 100644 index 0000000..e9a35de --- /dev/null +++ b/src/base/humanize.time.cc @@ -0,0 +1,205 @@ +/** + * Copyright (c) 2021, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include "humanize.time.hh" + +#include "config.h" +#include "fmt/format.h" +#include "time_util.hh" + +namespace humanize { +namespace time { + +using namespace std::chrono_literals; + +point +point::from_tv(const timeval& tv) +{ + return point(tv); +} + +std::string +point::as_time_ago() const +{ + struct timeval current_time + = this->p_recent_point.value_or(current_timeval()); + + if (this->p_convert_to_local) { + current_time.tv_sec = convert_log_time_to_local(current_time.tv_sec); + } + + auto delta + = std::chrono::seconds(current_time.tv_sec - this->p_past_point.tv_sec); + if (delta < 0s) { + return "in the future"; + } + if (delta < 1min) { + return "just now"; + } + if (delta < 2min) { + return "one minute ago"; + } + if (delta < 1h) { + return fmt::format( + FMT_STRING("{} minutes ago"), + std::chrono::duration_cast(delta).count()); + } + if (delta < 2h) { + return "one hour ago"; + } + if (delta < 24h) { + return fmt::format( + FMT_STRING("{} hours ago"), + std::chrono::duration_cast(delta).count()); + } + if (delta < 48h) { + return "one day ago"; + } + if (delta < 365 * 24h) { + return fmt::format(FMT_STRING("{} days ago"), delta / 24h); + } + if (delta < (2 * 365 * 24h)) { + return "over a year ago"; + } + return fmt::format(FMT_STRING("over {} years ago"), delta / (365 * 24h)); +} + +std::string +point::as_precise_time_ago() const +{ + struct timeval now, diff; + + now = this->p_recent_point.value_or(current_timeval()); + if (this->p_convert_to_local) { + now.tv_sec = convert_log_time_to_local(now.tv_sec); + } + + timersub(&now, &this->p_past_point, &diff); + if (diff.tv_sec < 0) { + return this->as_time_ago(); + } else if (diff.tv_sec <= 1) { + return "a second ago"; + } else if (diff.tv_sec < (10 * 60)) { + if (diff.tv_sec < 60) { + return fmt::format(FMT_STRING("{:2} seconds ago"), diff.tv_sec); + } + + time_t seconds = diff.tv_sec % 60; + time_t minutes = diff.tv_sec / 60; + + return fmt::format(FMT_STRING("{:2} minute{} and {:2} second{} ago"), + minutes, + minutes > 1 ? "s" : "", + seconds, + seconds == 1 ? "" : "s"); + } else { + return this->as_time_ago(); + } +} + +duration +duration::from_tv(const struct timeval& tv) +{ + return duration{tv}; +} + +std::string +duration::to_string() const +{ + /* 24h22m33s111 */ + + static const struct rel_interval { + uint64_t length; + const char* format; + const char* symbol; + } intervals[] = { + {1000, "%03lld%s", ""}, + {60, "%lld%s", "s"}, + {60, "%lld%s", "m"}, + {24, "%lld%s", "h"}, + {0, "%lld%s", "d"}, + }; + + const auto* curr_interval = intervals; + auto usecs = std::chrono::duration_cast( + std::chrono::seconds(this->d_timeval.tv_sec)) + + std::chrono::microseconds(this->d_timeval.tv_usec); + auto millis = std::chrono::duration_cast(usecs); + std::string retval; + bool neg = false; + + if (millis < 0s) { + neg = true; + millis = -millis; + } + + uint64_t remaining; + if (millis >= 10min) { + remaining + = std::chrono::duration_cast(millis).count(); + curr_interval += 1; + } else { + remaining = millis.count(); + } + + for (; curr_interval != std::end(intervals); curr_interval++) { + uint64_t amount; + char segment[32]; + + if (curr_interval->length) { + amount = remaining % curr_interval->length; + remaining = remaining / curr_interval->length; + } else { + amount = remaining; + remaining = 0; + } + + if (!amount && !remaining) { + break; + } + + snprintf(segment, + sizeof(segment), + curr_interval->format, + amount, + curr_interval->symbol); + retval.insert(0, segment); + } + + if (neg) { + retval.insert(0, "-"); + } + + return retval; +} + +} // namespace time +} // namespace humanize diff --git a/src/base/humanize.time.hh b/src/base/humanize.time.hh new file mode 100644 index 0000000..96edebd --- /dev/null +++ b/src/base/humanize.time.hh @@ -0,0 +1,88 @@ +/** + * Copyright (c) 2021, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef lnav_humanize_time_hh +#define lnav_humanize_time_hh + +#include + +#include + +#include "optional.hpp" + +namespace humanize { +namespace time { + +class point { +public: + static point from_tv(const struct timeval& tv); + + point& with_recent_point(const struct timeval& tv) + { + this->p_recent_point = tv; + return *this; + } + + point& with_convert_to_local(bool convert_to_local) + { + this->p_convert_to_local = convert_to_local; + return *this; + } + + std::string as_time_ago() const; + + std::string as_precise_time_ago() const; + +private: + explicit point(const struct timeval& tv) + : p_past_point{tv.tv_sec, tv.tv_usec} + { + } + + struct timeval p_past_point; + nonstd::optional p_recent_point; + bool p_convert_to_local{false}; +}; + +class duration { +public: + static duration from_tv(const struct timeval& tv); + + std::string to_string() const; + +private: + explicit duration(const struct timeval& tv) : d_timeval(tv) {} + + struct timeval d_timeval; +}; + +} // namespace time +} // namespace humanize + +#endif diff --git a/src/base/humanize.time.tests.cc b/src/base/humanize.time.tests.cc new file mode 100644 index 0000000..1b84684 --- /dev/null +++ b/src/base/humanize.time.tests.cc @@ -0,0 +1,122 @@ +/** + * Copyright (c) 2021, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include "config.h" +#include "doctest/doctest.h" +#include "humanize.time.hh" + +TEST_CASE("time ago") +{ + using namespace std::chrono_literals; + + time_t t1 = 1610000000; + auto t1_chrono = std::chrono::seconds(t1); + + auto p1 = humanize::time::point::from_tv({t1, 0}).with_recent_point( + {(time_t) t1 + 5, 0}); + + CHECK(p1.as_time_ago() == "just now"); + CHECK(p1.as_precise_time_ago() == " 5 seconds ago"); + + auto p2 = humanize::time::point::from_tv({t1, 0}).with_recent_point( + {(time_t) t1 + 65, 0}); + + CHECK(p2.as_time_ago() == "one minute ago"); + CHECK(p2.as_precise_time_ago() == " 1 minute and 5 seconds ago"); + + auto p3 = humanize::time::point::from_tv({t1, 0}).with_recent_point( + {(time_t) t1 + (3 * 60 + 5), 0}); + + CHECK(p3.as_time_ago() == "3 minutes ago"); + CHECK(p3.as_precise_time_ago() == " 3 minutes and 5 seconds ago"); + + auto p4 = humanize::time::point::from_tv({t1, 0}).with_recent_point( + {(time_t) (t1_chrono + 65min).count(), 0}); + + CHECK(p4.as_time_ago() == "one hour ago"); + CHECK(p4.as_precise_time_ago() == "one hour ago"); + + auto p5 = humanize::time::point::from_tv({t1, 0}).with_recent_point( + {(time_t) (t1_chrono + 3h).count(), 0}); + + CHECK(p5.as_time_ago() == "3 hours ago"); + CHECK(p5.as_precise_time_ago() == "3 hours ago"); + + auto p6 = humanize::time::point::from_tv({t1, 0}).with_recent_point( + {(time_t) (t1_chrono + 25h).count(), 0}); + + CHECK(p6.as_time_ago() == "one day ago"); + CHECK(p6.as_precise_time_ago() == "one day ago"); + + auto p7 = humanize::time::point::from_tv({t1, 0}).with_recent_point( + {(time_t) (t1_chrono + 50h).count(), 0}); + + CHECK(p7.as_time_ago() == "2 days ago"); + CHECK(p7.as_precise_time_ago() == "2 days ago"); + + auto p8 = humanize::time::point::from_tv({t1, 0}).with_recent_point( + {(time_t) (t1_chrono + 370 * 24h).count(), 0}); + + CHECK(p8.as_time_ago() == "over a year ago"); + CHECK(p8.as_precise_time_ago() == "over a year ago"); + + auto p9 = humanize::time::point::from_tv({t1, 0}).with_recent_point( + {(time_t) (t1_chrono + 800 * 24h).count(), 0}); + + CHECK(p9.as_time_ago() == "over 2 years ago"); + CHECK(p9.as_precise_time_ago() == "over 2 years ago"); + + CHECK(humanize::time::point::from_tv({1610000000, 0}) + .with_recent_point({(time_t) 1612000000, 0}) + .as_time_ago() + == "23 days ago"); +} + +TEST_CASE("duration to_string") +{ + std::string val; + + val = humanize::time::duration::from_tv({25 * 60 * 60, 123000}).to_string(); + CHECK(val == "1d1h0m0s"); + val = humanize::time::duration::from_tv({10, 123000}).to_string(); + CHECK(val == "10s123"); + val = humanize::time::duration::from_tv({10, 0}).to_string(); + CHECK(val == "10s000"); + val = humanize::time::duration::from_tv({0, 100000}).to_string(); + CHECK(val == "100"); + val = humanize::time::duration::from_tv({0, 0}).to_string(); + CHECK(val == ""); + val = humanize::time::duration::from_tv({0, -10000}).to_string(); + CHECK(val == "-010"); + val = humanize::time::duration::from_tv({-10, 0}).to_string(); + CHECK(val == "-10s000"); +} diff --git a/src/base/injector.bind.hh b/src/base/injector.bind.hh new file mode 100644 index 0000000..f240cd1 --- /dev/null +++ b/src/base/injector.bind.hh @@ -0,0 +1,173 @@ +/** + * Copyright (c) 2021, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @file injector.bind.hh + */ + +#ifndef lnav_injector_bind_hh +#define lnav_injector_bind_hh + +#include "injector.hh" + +namespace injector { + +namespace details { + +template +std::function()> +create_factory(R (*)(Args...)) +{ + return []() { return std::make_shared(::injector::get()...); }; +} + +template::value, bool> = true> +std::function()> +create_factory() +{ + typename I::injectable* i = nullptr; + + return create_factory(i); +} + +template::value, bool> = true> +std::function()> +create_factory() noexcept +{ + return []() { return std::make_shared(); }; +} + +} // namespace details + +template +struct bind : singleton_storage { + template::value, bool> = true> + static bool to_singleton() noexcept + { + typename I::injectable* i = nullptr; + singleton_storage::ss_owner + = create_from_injectable(i)(); + singleton_storage::ss_data + = singleton_storage::ss_owner.get(); + singleton_storage::ss_scope = scope::singleton; + + return true; + } + + template::value, bool> = true> + static bool to_singleton() noexcept + { + singleton_storage::ss_owner = std::make_shared(); + singleton_storage::ss_data + = singleton_storage::ss_owner.get(); + singleton_storage::ss_scope = scope::singleton; + return true; + } + + struct lifetime { + ~lifetime() + { + singleton_storage::ss_owner = nullptr; + singleton_storage::ss_data = nullptr; + } + }; + + template::value, bool> = true> + static lifetime to_scoped_singleton() noexcept + { + typename I::injectable* i = nullptr; + singleton_storage::ss_owner + = create_from_injectable(i)(); + singleton_storage::ss_data + = singleton_storage::ss_owner.get(); + singleton_storage::ss_scope = scope::singleton; + + return {}; + } + + template + static bool to_instance(T* (*f)(Args...)) noexcept + { + singleton_storage::ss_data + = f(::injector::get()...); + singleton_storage::ss_scope = scope::singleton; + return true; + } + + static bool to_instance(T* data) noexcept + { + singleton_storage::ss_data = data; + singleton_storage::ss_scope = scope::singleton; + return true; + } + + template + static bool to() noexcept + { + singleton_storage::ss_factory + = details::create_factory(); + singleton_storage::ss_scope = scope::none; + return true; + } +}; + +template +struct bind_multiple : multiple_storage { + bind_multiple() noexcept = default; + + template + bind_multiple& add() noexcept + { + multiple_storage::get_factories()[typeid(I).name()] + = details::create_factory(); + + return *this; + } + + template + bind_multiple& add_singleton() noexcept + { + auto factory = details::create_factory(); + auto single = factory(); + + if (sizeof...(Annotations) > 0) { + bind::to_instance(single.get()); + } + bind::to_instance(single.get()); + multiple_storage::get_factories()[typeid(I).name()] + = [single]() { return single; }; + + return *this; + } +}; + +} // namespace injector + +#endif diff --git a/src/base/injector.hh b/src/base/injector.hh new file mode 100644 index 0000000..c6848a6 --- /dev/null +++ b/src/base/injector.hh @@ -0,0 +1,230 @@ +/** + * Copyright (c) 2021, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @file injector.hh + */ + +#ifndef lnav_injector_hh +#define lnav_injector_hh + +#include +#include +#include +#include + +#include + +#include "base/lnav_log.hh" + +namespace injector { + +enum class scope { + undefined, + none, + singleton, +}; + +template +void force_linking(Annotation anno); + +template +using void_t = void; + +template +struct with_annotations { + T value; +}; + +template +struct has_injectable : std::false_type {}; + +template +struct has_injectable> : std::true_type {}; + +template +struct singleton_storage { + static scope get_scope() { return ss_scope; } + + static T* get() + { + static int _[] = {0, (force_linking(Annotations{}), 0)...}; + (void) _; + return ss_data; + } + + static std::shared_ptr get_owner() + { + static int _[] = {0, (force_linking(Annotations{}), 0)...}; + (void) _; + return ss_owner; + } + + static std::shared_ptr create() + { + static int _[] = {0, (force_linking(Annotations{}), 0)...}; + (void) _; + return ss_factory(); + } + +protected: + static scope ss_scope; + static T* ss_data; + static std::shared_ptr ss_owner; + static std::function()> ss_factory; +}; + +template +T* singleton_storage::ss_data = nullptr; + +template +scope singleton_storage::ss_scope = scope::undefined; + +template +std::shared_ptr singleton_storage::ss_owner; + +template +std::function()> + singleton_storage::ss_factory; + +template +struct Impl { + using type = T; +}; + +template +struct multiple_storage { + static std::vector> create() + { + std::vector> retval; + + for (const auto& pair : get_factories()) { + retval.template emplace_back(pair.second()); + } + return retval; + } + +protected: + using factory_map_t + = std::map()>>; + + static factory_map_t& get_factories() + { + static factory_map_t retval; + + return retval; + } +}; + +template::value, bool> = true> +T +get() +{ + using plain_t = std::remove_const_t>; + + return *singleton_storage::get(); +} + +template::value, bool> = true> +T +get() +{ + using plain_t = std::remove_const_t>; + + return singleton_storage::get(); +} + +template +struct is_shared_ptr : std::false_type {}; + +template +struct is_shared_ptr> : std::true_type {}; + +template +struct is_vector : std::false_type {}; + +template +struct is_vector> : std::true_type {}; + +template +std::function()> create_from_injectable(R (*)(IArgs...), + Args&... args); + +template::value, bool> + = true, + std::enable_if_t::value, bool> = true> +T +get(Args&... args) +{ + typename T::element_type::injectable* i = nullptr; + + if (singleton_storage::get_scope() + == scope::singleton) + { + return singleton_storage::get_owner(); + } + return create_from_injectable(i, args...)(); +} + +template< + typename T, + typename... Annotations, + std::enable_if_t::value, bool> + = true, + std::enable_if_t::value, bool> = true> +T +get() +{ + return singleton_storage::get_owner(); +} + +template::value, bool> = true> +T +get() +{ + return multiple_storage::create(); +} + +template +std::function()> +create_from_injectable(R (*)(IArgs...), Args&... args) +{ + return [&]() { + return std::make_shared(args..., ::injector::get()...); + }; +} + +} // namespace injector + +#endif diff --git a/src/base/intern_string.cc b/src/base/intern_string.cc new file mode 100644 index 0000000..8410720 --- /dev/null +++ b/src/base/intern_string.cc @@ -0,0 +1,303 @@ +/** + * Copyright (c) 2014, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @file intern_string.cc + */ + +#include + +#include "intern_string.hh" + +#include + +#include "config.h" +#include "pcrepp/pcre2pp.hh" +#include "xxHash/xxhash.h" + +const static int TABLE_SIZE = 4095; + +struct intern_string::intern_table { + ~intern_table() + { + for (auto is : this->it_table) { + auto curr = is; + + while (curr != nullptr) { + auto next = curr->is_next; + + delete curr; + curr = next; + } + } + } + + intern_string* it_table[TABLE_SIZE]; +}; + +intern_table_lifetime +intern_string::get_table_lifetime() +{ + static intern_table_lifetime retval = std::make_shared(); + + return retval; +} + +unsigned long +hash_str(const char* str, size_t len) +{ + return XXH3_64bits(str, len); +} + +const intern_string* +intern_string::lookup(const char* str, ssize_t len) noexcept +{ + unsigned long h; + intern_string* curr; + + if (len == -1) { + len = strlen(str); + } + h = hash_str(str, len) % TABLE_SIZE; + + { + static std::mutex table_mutex; + + std::lock_guard lk(table_mutex); + auto tab = get_table_lifetime(); + + curr = tab->it_table[h]; + while (curr != nullptr) { + if (static_cast(curr->is_str.size()) == len + && strncmp(curr->is_str.c_str(), str, len) == 0) + { + return curr; + } + curr = curr->is_next; + } + + curr = new intern_string(str, len); + curr->is_next = tab->it_table[h]; + tab->it_table[h] = curr; + + return curr; + } +} + +const intern_string* +intern_string::lookup(const string_fragment& sf) noexcept +{ + return lookup(sf.data(), sf.length()); +} + +const intern_string* +intern_string::lookup(const std::string& str) noexcept +{ + return lookup(str.c_str(), str.size()); +} + +bool +intern_string::startswith(const char* prefix) const +{ + const char* curr = this->is_str.data(); + + while (*prefix != '\0' && *prefix == *curr) { + prefix += 1; + curr += 1; + } + + return *prefix == '\0'; +} + +string_fragment +string_fragment::trim(const char* tokens) const +{ + string_fragment retval = *this; + + while (retval.sf_begin < retval.sf_end) { + bool found = false; + + for (int lpc = 0; tokens[lpc] != '\0'; lpc++) { + if (retval.sf_string[retval.sf_begin] == tokens[lpc]) { + found = true; + break; + } + } + if (!found) { + break; + } + + retval.sf_begin += 1; + } + while (retval.sf_begin < retval.sf_end) { + bool found = false; + + for (int lpc = 0; tokens[lpc] != '\0'; lpc++) { + if (retval.sf_string[retval.sf_end - 1] == tokens[lpc]) { + found = true; + break; + } + } + if (!found) { + break; + } + + retval.sf_end -= 1; + } + + return retval; +} + +string_fragment +string_fragment::trim() const +{ + return this->trim(" \t\r\n"); +} + +nonstd::optional +string_fragment::consume_n(int amount) const +{ + if (amount > this->length()) { + return nonstd::nullopt; + } + + return string_fragment{ + this->sf_string, + this->sf_begin + amount, + this->sf_end, + }; +} + +string_fragment::split_result +string_fragment::split_n(int amount) const +{ + if (amount > this->length()) { + return nonstd::nullopt; + } + + return std::make_pair( + string_fragment{ + this->sf_string, + this->sf_begin, + this->sf_begin + amount, + }, + string_fragment{ + this->sf_string, + this->sf_begin + amount, + this->sf_end, + }); +} + +std::vector +string_fragment::split_lines() const +{ + std::vector retval; + int start = this->sf_begin; + + for (auto index = start; index < this->sf_end; index++) { + if ((*this)[index] == '\n') { + retval.emplace_back(this->sf_string, start, index + 1); + start = index + 1; + } + } + retval.emplace_back(this->sf_string, start, this->sf_end); + + return retval; +} + +Result +string_fragment::utf8_length() const +{ + ssize_t retval = 0; + + for (ssize_t byte_index = this->sf_begin; byte_index < this->sf_end;) { + auto ch_size = TRY(ww898::utf::utf8::char_size([this, byte_index]() { + return std::make_pair(this->sf_string[byte_index], + this->sf_end - byte_index); + })); + byte_index += ch_size; + retval += 1; + } + + return Ok(retval); +} + +string_fragment::case_style +string_fragment::detect_text_case_style() const +{ + static const auto LOWER_RE + = lnav::pcre2pp::code::from_const(R"(^[^A-Z]+$)"); + static const auto UPPER_RE + = lnav::pcre2pp::code::from_const(R"(^[^a-z]+$)"); + static const auto CAMEL_RE + = lnav::pcre2pp::code::from_const(R"(^(?:[A-Z][a-z0-9]+)+$)"); + + if (LOWER_RE.find_in(*this).ignore_error().has_value()) { + return case_style::lower; + } + if (UPPER_RE.find_in(*this).ignore_error().has_value()) { + return case_style::upper; + } + if (CAMEL_RE.find_in(*this).ignore_error().has_value()) { + return case_style::camel; + } + + return case_style::mixed; +} + +std::string +string_fragment::to_string_with_case_style(case_style style) const +{ + std::string retval; + + switch (style) { + case case_style::lower: { + for (auto ch : *this) { + retval.append(1, std::tolower(ch)); + } + break; + } + case case_style::upper: { + for (auto ch : *this) { + retval.append(1, std::toupper(ch)); + } + break; + } + case case_style::camel: { + retval = this->to_string(); + if (!this->empty()) { + retval[0] = toupper(retval[0]); + } + break; + } + case case_style::mixed: { + return this->to_string(); + } + } + + return retval; +} diff --git a/src/base/intern_string.hh b/src/base/intern_string.hh new file mode 100644 index 0000000..37ccc36 --- /dev/null +++ b/src/base/intern_string.hh @@ -0,0 +1,857 @@ +/** + * Copyright (c) 2014, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @file intern_string.hh + */ + +#ifndef intern_string_hh +#define intern_string_hh + +#include +#include +#include + +#include +#include +#include + +#include "fmt/format.h" +#include "optional.hpp" +#include "scn/util/string_view.h" +#include "strnatcmp.h" +#include "ww898/cp_utf8.hpp" + +struct string_fragment { + using iterator = const char*; + + static string_fragment invalid() + { + string_fragment retval; + + retval.invalidate(); + return retval; + } + + static string_fragment from_c_str(const char* str) + { + return string_fragment{str, 0, str != nullptr ? (int) strlen(str) : 0}; + } + + static string_fragment from_c_str(const unsigned char* str) + { + return string_fragment{ + str, 0, str != nullptr ? (int) strlen((char*) str) : 0}; + } + + template + static string_fragment from_const(const T (&str)[N]) + { + return string_fragment{str, 0, (int) N - 1}; + } + + static string_fragment from_str(const std::string& str) + { + return string_fragment{str.c_str(), 0, (int) str.size()}; + } + + static string_fragment from_substr(const std::string& str, + size_t offset, + size_t length) + { + return string_fragment{ + str.c_str(), (int) offset, (int) (offset + length)}; + } + + static string_fragment from_str_range(const std::string& str, + size_t begin, + size_t end) + { + return string_fragment{str.c_str(), (int) begin, (int) end}; + } + + static string_fragment from_bytes(const char* bytes, size_t len) + { + return string_fragment{bytes, 0, (int) len}; + } + + static string_fragment from_bytes(const unsigned char* bytes, size_t len) + { + return string_fragment{(const char*) bytes, 0, (int) len}; + } + + static string_fragment from_memory_buffer(const fmt::memory_buffer& buf) + { + return string_fragment{buf.data(), 0, (int) buf.size()}; + } + + static string_fragment from_byte_range(const char* bytes, + size_t begin, + size_t end) + { + return string_fragment{bytes, (int) begin, (int) end}; + } + + explicit string_fragment(const char* str = "", int begin = 0, int end = -1) + : sf_string(str), sf_begin(begin), sf_end(end == -1 ? strlen(str) : end) + { + } + + explicit string_fragment(const unsigned char* str, + int begin = 0, + int end = -1) + : sf_string((const char*) str), sf_begin(begin), + sf_end(end == -1 ? strlen((const char*) str) : end) + { + } + + string_fragment(const std::string& str) + : sf_string(str.c_str()), sf_begin(0), sf_end(str.length()) + { + } + + bool is_valid() const + { + return this->sf_begin != -1 && this->sf_begin <= this->sf_end; + } + + int length() const { return this->sf_end - this->sf_begin; } + + Result utf8_length() const; + + const char* data() const { return &this->sf_string[this->sf_begin]; } + + const unsigned char* udata() const + { + return (const unsigned char*) &this->sf_string[this->sf_begin]; + } + + char* writable_data(int offset = 0) + { + return (char*) &this->sf_string[this->sf_begin + offset]; + } + + char front() const { return this->sf_string[this->sf_begin]; } + + uint32_t front_codepoint() const + { + size_t index = 0; + try { + return ww898::utf::utf8::read( + [this, &index]() { return this->data()[index++]; }); + } catch (const std::runtime_error& e) { + return this->data()[0]; + } + } + + char back() const { return this->sf_string[this->sf_end - 1]; } + + iterator begin() const { return &this->sf_string[this->sf_begin]; } + + iterator end() const { return &this->sf_string[this->sf_end]; } + + bool empty() const { return !this->is_valid() || length() == 0; } + + Result codepoint_to_byte_index(ssize_t cp_index) const + { + ssize_t retval = 0; + + while (cp_index > 0) { + if (retval >= this->length()) { + return Err("index is beyond the end of the string"); + } + auto ch_len = TRY(ww898::utf::utf8::char_size([this, retval]() { + return std::make_pair(this->data()[retval], + this->length() - retval - 1); + })); + + retval += ch_len; + cp_index -= 1; + } + + return Ok(retval); + } + + char operator[](int index) const + { + return this->sf_string[sf_begin + index]; + } + + bool operator==(const std::string& str) const + { + if (this->length() != (int) str.length()) { + return false; + } + + return memcmp( + &this->sf_string[this->sf_begin], str.c_str(), str.length()) + == 0; + } + + bool operator==(const string_fragment& sf) const + { + if (this->length() != sf.length()) { + return false; + } + + return memcmp(this->data(), sf.data(), sf.length()) == 0; + } + + bool iequal(const string_fragment& sf) const + { + if (this->length() != sf.length()) { + return false; + } + + return strnatcasecmp( + this->length(), this->data(), sf.length(), sf.data()) + == 0; + } + + bool operator==(const char* str) const + { + size_t len = strlen(str); + + return len == (size_t) this->length() + && strncmp(this->data(), str, this->length()) == 0; + } + + bool operator!=(const char* str) const { return !(*this == str); } + + bool startswith(const char* prefix) const + { + const auto* iter = this->begin(); + + while (*prefix != '\0' && iter < this->end() && *prefix == *iter) { + prefix += 1; + iter += 1; + } + + return *prefix == '\0'; + } + + bool endswith(const char* suffix) const + { + int suffix_len = strlen(suffix); + + if (suffix_len > this->length()) { + return false; + } + + const auto* curr = this->end() - suffix_len; + while (*suffix != '\0' && *curr == *suffix) { + suffix += 1; + curr += 1; + } + + return *suffix == '\0'; + } + + string_fragment substr(int begin) const + { + return string_fragment{ + this->sf_string, this->sf_begin + begin, this->sf_end}; + } + + string_fragment sub_range(int begin, int end) const + { + return string_fragment{ + this->sf_string, this->sf_begin + begin, this->sf_begin + end}; + } + + size_t count(char ch) const { + size_t retval = 0; + + for (int lpc = this->sf_begin; lpc < this->sf_end; lpc++) { + if (this->sf_string[lpc] == ch) { + retval += 1; + } + } + + return retval; + } + + nonstd::optional find(char ch) const + { + for (int lpc = this->sf_begin; lpc < this->sf_end; lpc++) { + if (this->sf_string[lpc] == ch) { + return lpc - this->sf_begin; + } + } + + return nonstd::nullopt; + } + + template + string_fragment find_left_boundary(size_t start, P&& predicate) const + { + assert((int) start <= this->length()); + + if (start > 0 && start == this->length()) { + start -= 1; + } + while (start > 0) { + if (predicate(this->data()[start])) { + start += 1; + break; + } + start -= 1; + } + + return string_fragment{ + this->sf_string, + (int) start, + this->sf_end, + }; + } + + template + string_fragment find_right_boundary(size_t start, P&& predicate) const + { + while ((int) start < this->length()) { + if (predicate(this->data()[start])) { + break; + } + start += 1; + } + + return string_fragment{ + this->sf_string, + this->sf_begin, + this->sf_begin + (int) start, + }; + } + + template + string_fragment find_boundaries_around(size_t start, P&& predicate) const + { + return this->template find_left_boundary(start, predicate) + .find_right_boundary(0, predicate); + } + + nonstd::optional> consume_codepoint() + const + { + auto cp = this->front_codepoint(); + auto index_res = this->codepoint_to_byte_index(1); + + if (index_res.isErr()) { + return nonstd::nullopt; + } + + return std::make_pair(cp, this->substr(index_res.unwrap())); + } + + template + nonstd::optional consume(P predicate) const + { + int consumed = 0; + while (consumed < this->length()) { + if (!predicate(this->data()[consumed])) { + break; + } + + consumed += 1; + } + + if (consumed == 0) { + return nonstd::nullopt; + } + + return string_fragment{ + this->sf_string, + this->sf_begin + consumed, + this->sf_end, + }; + } + + nonstd::optional consume_n(int amount) const; + + template + string_fragment skip(P predicate) const + { + int offset = 0; + while (offset < this->length() && predicate(this->data()[offset])) { + offset += 1; + } + + return string_fragment{ + this->sf_string, + this->sf_begin + offset, + this->sf_end, + }; + } + + using split_result + = nonstd::optional>; + + template + split_result split_while(P&& predicate) const + { + int consumed = 0; + while (consumed < this->length()) { + if (!predicate(this->data()[consumed])) { + break; + } + + consumed += 1; + } + + if (consumed == 0) { + return nonstd::nullopt; + } + + return std::make_pair( + string_fragment{ + this->sf_string, + this->sf_begin, + this->sf_begin + consumed, + }, + string_fragment{ + this->sf_string, + this->sf_begin + consumed, + this->sf_end, + }); + } + + template + split_result split_when(P&& predicate) const + { + int consumed = 0; + while (consumed < this->length()) { + if (predicate(this->data()[consumed])) { + break; + } + + consumed += 1; + } + + if (consumed == 0) { + return nonstd::nullopt; + } + + return std::make_pair( + string_fragment{ + this->sf_string, + this->sf_begin, + this->sf_begin + consumed, + }, + string_fragment{ + this->sf_string, + this->sf_begin + consumed + 1, + this->sf_end, + }); + } + + split_result split_n(int amount) const; + + std::vector split_lines() const; + + struct tag1 { + const char t_value; + + bool operator()(char ch) const { return this->t_value == ch; } + }; + + struct quoted_string_body { + bool qs_in_escape{false}; + + bool operator()(char ch) + { + if (this->qs_in_escape) { + this->qs_in_escape = false; + return true; + } else if (ch == '\\') { + this->qs_in_escape = true; + return true; + } else if (ch == '"') { + return false; + } else { + return true; + } + } + }; + + const char* to_string(char* buf) const + { + memcpy(buf, this->data(), this->length()); + buf[this->length()] = '\0'; + + return buf; + } + + std::string to_string() const + { + return {this->data(), (size_t) this->length()}; + } + + void clear() + { + this->sf_begin = 0; + this->sf_end = 0; + } + + void invalidate() + { + this->sf_begin = -1; + this->sf_end = -1; + } + + string_fragment trim(const char* tokens) const; + string_fragment trim() const; + + string_fragment prepend(const char* str, int amount) const + { + return string_fragment{ + str, + this->sf_begin + amount, + this->sf_end + amount, + }; + } + + string_fragment erase_before(const char* str, int amount) const + { + return string_fragment{ + str, + this->sf_begin - amount, + this->sf_end - amount, + }; + } + + string_fragment erase(const char* str, int amount) const + { + return string_fragment{ + str, + this->sf_begin, + this->sf_end - amount, + }; + } + + template + const char* to_c_str(A allocator) const + { + auto* retval = allocator.allocate(this->length() + 1); + memcpy(retval, this->data(), this->length()); + retval[this->length()] = '\0'; + return retval; + } + + template + string_fragment to_owned(A allocator) const + { + return string_fragment{ + this->template to_c_str(allocator), + 0, + this->length(), + }; + } + + scn::string_view to_string_view() const + { + return scn::string_view{this->begin(), this->end()}; + } + + enum class case_style { + lower, + upper, + camel, + mixed, + }; + + case_style detect_text_case_style() const; + + std::string to_string_with_case_style(case_style style) const; + + const char* sf_string; + int sf_begin; + int sf_end; +}; + +inline bool +operator==(const std::string& left, const string_fragment& right) +{ + return right == left; +} + +inline bool +operator<(const char* left, const string_fragment& right) +{ + int rc = strncmp(left, right.data(), right.length()); + return rc < 0; +} + +inline void +operator+=(std::string& left, const string_fragment& right) +{ + left.append(right.data(), right.length()); +} + +inline bool +operator<(const string_fragment& left, const char* right) +{ + return strncmp(left.data(), right, left.length()) < 0; +} + +inline std::ostream& +operator<<(std::ostream& os, const string_fragment& sf) +{ + os.write(sf.data(), sf.length()); + return os; +} + +class intern_string { +public: + static const intern_string* lookup(const char* str, ssize_t len) noexcept; + + static const intern_string* lookup(const string_fragment& sf) noexcept; + + static const intern_string* lookup(const std::string& str) noexcept; + + const char* get() const { return this->is_str.c_str(); }; + + size_t size() const { return this->is_str.size(); } + + std::string to_string() const { return this->is_str; } + + string_fragment to_string_fragment() const + { + return string_fragment{this->is_str}; + } + + bool startswith(const char* prefix) const; + + struct intern_table; + static std::shared_ptr get_table_lifetime(); + +private: + friend intern_table; + + intern_string(const char* str, ssize_t len) + : is_next(nullptr), is_str(str, (size_t) len) + { + } + + intern_string* is_next; + std::string is_str; +}; + +using intern_table_lifetime = std::shared_ptr; + +class intern_string_t { +public: + using iterator = const char*; + + intern_string_t(const intern_string* is = nullptr) : ist_interned_string(is) + { + } + + const intern_string* unwrap() const { return this->ist_interned_string; } + + void clear() { this->ist_interned_string = nullptr; }; + + bool empty() const { return this->ist_interned_string == nullptr; } + + const char* get() const + { + if (this->empty()) { + return ""; + } + return this->ist_interned_string->get(); + } + + const char* c_str() const { return this->get(); } + + iterator begin() const { return this->get(); } + + iterator end() const { return this->get() + this->size(); } + + size_t size() const + { + if (this->ist_interned_string == nullptr) { + return 0; + } + return this->ist_interned_string->size(); + } + + size_t hash() const + { + auto ptr = (uintptr_t) this->ist_interned_string; + + return ptr; + } + + std::string to_string() const + { + if (this->ist_interned_string == nullptr) { + return ""; + } + return this->ist_interned_string->to_string(); + } + + string_fragment to_string_fragment() const + { + if (this->ist_interned_string == nullptr) { + return string_fragment{"", 0, 0}; + } + return this->ist_interned_string->to_string_fragment(); + } + + bool operator<(const intern_string_t& rhs) const + { + return strcmp(this->get(), rhs.get()) < 0; + } + + bool operator==(const intern_string_t& rhs) const + { + return this->ist_interned_string == rhs.ist_interned_string; + } + + bool operator!=(const intern_string_t& rhs) const + { + return !(*this == rhs); + } + + bool operator==(const char* rhs) const + { + return strcmp(this->get(), rhs) == 0; + } + + bool operator!=(const char* rhs) const + { + return strcmp(this->get(), rhs) != 0; + } + + static bool case_lt(const intern_string_t& lhs, const intern_string_t& rhs) + { + return strnatcasecmp(lhs.size(), lhs.get(), rhs.size(), rhs.get()) < 0; + } + +private: + const intern_string* ist_interned_string; +}; + +unsigned long hash_str(const char* str, size_t len); + +namespace fmt { +template<> +struct formatter : formatter { + template + auto format(const string_fragment& sf, FormatContext& ctx) + { + return formatter::format( + string_view{sf.data(), (size_t) sf.length()}, ctx); + } +}; + +template<> +struct formatter : formatter { + template + auto format(const intern_string_t& is, FormatContext& ctx) + { + return formatter::format( + string_view{is.get(), (size_t) is.size()}, ctx); + } +}; +} // namespace fmt + +namespace std { +template<> +struct hash { + std::size_t operator()(const intern_string_t& ist) const + { + return ist.hash(); + } +}; +} // namespace std + +inline bool +operator<(const char* left, const intern_string_t& right) +{ + int rc = strncmp(left, right.get(), right.size()); + return rc < 0; +} + +inline bool +operator<(const intern_string_t& left, const char* right) +{ + return strncmp(left.get(), right, left.size()) < 0; +} + +inline bool +operator==(const intern_string_t& left, const string_fragment& sf) +{ + return ((int) left.size() == sf.length()) + && (memcmp(left.get(), sf.data(), left.size()) == 0); +} + +inline bool +operator==(const string_fragment& left, const intern_string_t& right) +{ + return (left.length() == (int) right.size()) + && (memcmp(left.data(), right.get(), left.length()) == 0); +} + +namespace std { +inline string +to_string(const string_fragment& s) +{ + return {s.data(), (size_t) s.length()}; +} + +inline string +to_string(const intern_string_t& s) +{ + return s.to_string(); +} +} // namespace std + +inline string_fragment +to_string_fragment(const string_fragment& s) +{ + return s; +} + +inline string_fragment +to_string_fragment(const intern_string_t& s) +{ + return string_fragment(s.get(), 0, s.size()); +} + +inline string_fragment +to_string_fragment(const std::string& s) +{ + return string_fragment(s.c_str(), 0, s.length()); +} + +struct frag_hasher { + size_t operator()(const string_fragment& sf) const + { + return hash_str(sf.data(), sf.length()); + } +}; + +#endif diff --git a/src/base/intern_string.tests.cc b/src/base/intern_string.tests.cc new file mode 100644 index 0000000..47dda14 --- /dev/null +++ b/src/base/intern_string.tests.cc @@ -0,0 +1,144 @@ +/** + * Copyright (c) 2021, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include "intern_string.hh" + +#include "config.h" +#include "doctest/doctest.h" + +TEST_CASE("string_fragment::startswith") +{ + std::string empty; + auto sf = string_fragment{empty}; + + CHECK_FALSE(sf.startswith("abc")); +} + +TEST_CASE("split_lines") +{ + std::string in1 = "Hello, World!"; + std::string in2 = "Hello, World!\nGoodbye, World!"; + + { + auto sf = string_fragment(in1); + auto split = sf.split_lines(); + + CHECK(1 == split.size()); + CHECK(in1 == split[0].to_string()); + } + + { + auto sf = string_fragment(in2); + auto split = sf.split_lines(); + + CHECK(2 == split.size()); + CHECK("Hello, World!\n" == split[0].to_string()); + CHECK("Goodbye, World!" == split[1].to_string()); + } +} + +TEST_CASE("consume") +{ + auto is_eq = string_fragment::tag1{'='}; + auto is_dq = string_fragment::tag1{'"'}; + auto is_colon = string_fragment::tag1{':'}; + + const char* pair = "foo = bar"; + auto sf = string_fragment(pair); + + auto split_sf = sf.split_while(isalnum); + + CHECK(split_sf.has_value()); + CHECK(split_sf->first.to_string() == "foo"); + CHECK(split_sf->second.to_string() == " = bar"); + + auto value_frag = split_sf->second.skip(isspace).consume(is_eq); + + CHECK(value_frag.has_value()); + CHECK(value_frag->to_string() == " bar"); + + auto stripped_value_frag = value_frag->consume(isspace); + + CHECK(stripped_value_frag.has_value()); + CHECK(stripped_value_frag->to_string() == "bar"); + + auto no_value = sf.consume(is_colon); + CHECK(!no_value.has_value()); + + const char* qs = R"("foo \" bar")"; + auto qs_sf = string_fragment{qs}; + + auto qs_body = qs_sf.consume(is_dq); + string_fragment::quoted_string_body qsb; + auto split_body = qs_body->split_while(qsb); + + CHECK(split_body.has_value()); + CHECK(split_body->first.to_string() == "foo \\\" bar"); + CHECK(split_body->second.to_string() == "\""); + + auto empty = split_body->second.consume(is_dq); + + CHECK(empty.has_value()); + CHECK(empty->empty()); +} + +TEST_CASE("find_left_boundary") +{ + std::string in1 = "Hello,\nWorld!\n"; + + { + auto sf = string_fragment{in1}; + + auto world_sf = sf.find_left_boundary( + in1.length() - 3, [](auto ch) { return ch == '\n'; }); + CHECK(world_sf.to_string() == "World!\n"); + auto full_sf + = sf.find_left_boundary(3, [](auto ch) { return ch == '\n'; }); + CHECK(full_sf.to_string() == in1); + } +} + +TEST_CASE("find_right_boundary") +{ + std::string in1 = "Hello,\nWorld!\n"; + + { + auto sf = string_fragment{in1}; + + auto world_sf = sf.find_right_boundary( + in1.length() - 3, [](auto ch) { return ch == '\n'; }); + CHECK(world_sf.to_string() == "Hello,\nWorld!"); + auto hello_sf + = sf.find_right_boundary(3, [](auto ch) { return ch == '\n'; }); + CHECK(hello_sf.to_string() == "Hello,"); + } +} diff --git a/src/base/is_utf8.cc b/src/base/is_utf8.cc new file mode 100644 index 0000000..ca6bca7 --- /dev/null +++ b/src/base/is_utf8.cc @@ -0,0 +1,304 @@ +/* + * is_utf8 is distributed under the following terms: + * + * Copyright (c) 2013 Palard Julien. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "is_utf8.hh" + +#include "config.h" + +/* + Check if the given unsigned char * is a valid utf-8 sequence. + + Return value : + If the string is valid utf-8, 0 is returned. + Else the position, starting from 1, is returned. + + Source: + http://www.unicode.org/versions/Unicode7.0.0/UnicodeStandard-7.0.pdf + page 124, 3.9 "Unicode Encoding Forms", "UTF-8" + + + Table 3-7. Well-Formed UTF-8 Byte Sequences + ----------------------------------------------------------------------------- + | Code Points | First Byte | Second Byte | Third Byte | Fourth Byte | + | U+0000..U+007F | 00..7F | | | | + | U+0080..U+07FF | C2..DF | 80..BF | | | + | U+0800..U+0FFF | E0 | A0..BF | 80..BF | | + | U+1000..U+CFFF | E1..EC | 80..BF | 80..BF | | + | U+D000..U+D7FF | ED | 80..9F | 80..BF | | + | U+E000..U+FFFF | EE..EF | 80..BF | 80..BF | | + | U+10000..U+3FFFF | F0 | 90..BF | 80..BF | 80..BF | + | U+40000..U+FFFFF | F1..F3 | 80..BF | 80..BF | 80..BF | + | U+100000..U+10FFFF | F4 | 80..8F | 80..BF | 80..BF | + ----------------------------------------------------------------------------- + + Returns the first erroneous byte position, and give in + `faulty_bytes` the number of actually existing bytes taking part in this + error. +*/ +utf8_scan_result +is_utf8(const unsigned char* str, + size_t len, + const char** message, + int* faulty_bytes, + nonstd::optional terminator) +{ + bool has_ansi = false; + ssize_t i = 0; + + *message = nullptr; + *faulty_bytes = 0; + while (i < len) { + if (str[i] == '\x1b') { + has_ansi = true; + } + + if (terminator && str[i] == terminator.value()) { + *message = nullptr; + return {i, has_ansi}; + } + + if (str[i] <= 0x7F) /* 00..7F */ { + i += 1; + } else if (str[i] >= 0xC2 && str[i] <= 0xDF) /* C2..DF 80..BF */ { + if (i + 1 < len) /* Expect a 2nd byte */ { + if (str[i + 1] < 0x80 || str[i + 1] > 0xBF) { + *message + = "After a first byte between C2 and DF, expecting a " + "2nd byte between 80 and BF"; + *faulty_bytes = 2; + return {i, has_ansi}; + } + } else { + *message + = "After a first byte between C2 and DF, expecting a 2nd " + "byte."; + *faulty_bytes = 1; + return {i, has_ansi}; + } + i += 2; + } else if (str[i] == 0xE0) /* E0 A0..BF 80..BF */ { + if (i + 2 < len) /* Expect a 2nd and 3rd byte */ { + if (str[i + 1] < 0xA0 || str[i + 1] > 0xBF) { + *message + = "After a first byte of E0, expecting a 2nd byte " + "between A0 and BF."; + *faulty_bytes = 2; + return {i, has_ansi}; + } + if (str[i + 2] < 0x80 || str[i + 2] > 0xBF) { + *message + = "After a first byte of E0, expecting a 3nd byte " + "between 80 and BF."; + *faulty_bytes = 3; + return {i, has_ansi}; + } + } else { + *message + = "After a first byte of E0, expecting two following " + "bytes."; + *faulty_bytes = 1; + return {i, has_ansi}; + } + i += 3; + } else if (str[i] >= 0xE1 && str[i] <= 0xEC) /* E1..EC 80..BF 80..BF */ + { + if (i + 2 < len) /* Expect a 2nd and 3rd byte */ { + if (str[i + 1] < 0x80 || str[i + 1] > 0xBF) { + *message + = "After a first byte between E1 and EC, expecting the " + "2nd byte between 80 and BF."; + *faulty_bytes = 2; + return {i, has_ansi}; + } + if (str[i + 2] < 0x80 || str[i + 2] > 0xBF) { + *message + = "After a first byte between E1 and EC, expecting the " + "3rd byte between 80 and BF."; + *faulty_bytes = 3; + return {i, has_ansi}; + } + } else { + *message + = "After a first byte between E1 and EC, expecting two " + "following bytes."; + *faulty_bytes = 1; + return {i, has_ansi}; + } + i += 3; + } else if (str[i] == 0xED) /* ED 80..9F 80..BF */ { + if (i + 2 < len) /* Expect a 2nd and 3rd byte */ { + if (str[i + 1] < 0x80 || str[i + 1] > 0x9F) { + *message + = "After a first byte of ED, expecting 2nd byte " + "between 80 and 9F."; + *faulty_bytes = 2; + return {i, has_ansi}; + } + if (str[i + 2] < 0x80 || str[i + 2] > 0xBF) { + *message + = "After a first byte of ED, expecting 3rd byte " + "between 80 and BF."; + *faulty_bytes = 3; + return {i, has_ansi}; + } + } else { + *message + = "After a first byte of ED, expecting two following " + "bytes."; + *faulty_bytes = 1; + return {i, has_ansi}; + } + i += 3; + } else if (str[i] >= 0xEE && str[i] <= 0xEF) /* EE..EF 80..BF 80..BF */ + { + if (i + 2 < len) /* Expect a 2nd and 3rd byte */ { + if (str[i + 1] < 0x80 || str[i + 1] > 0xBF) { + *message + = "After a first byte between EE and EF, expecting 2nd " + "byte between 80 and BF."; + *faulty_bytes = 2; + return {i, has_ansi}; + } + if (str[i + 2] < 0x80 || str[i + 2] > 0xBF) { + *message + = "After a first byte between EE and EF, expecting 3rd " + "byte between 80 and BF."; + *faulty_bytes = 3; + return {i, has_ansi}; + } + } else { + *message + = "After a first byte between EE and EF, two following " + "bytes."; + *faulty_bytes = 1; + return {i, has_ansi}; + } + i += 3; + } else if (str[i] == 0xF0) /* F0 90..BF 80..BF 80..BF */ { + if (i + 3 < len) /* Expect a 2nd, 3rd 3th byte */ { + if (str[i + 1] < 0x90 || str[i + 1] > 0xBF) { + *message + = "After a first byte of F0, expecting 2nd byte " + "between 90 and BF."; + *faulty_bytes = 2; + return {i, has_ansi}; + } + if (str[i + 2] < 0x80 || str[i + 2] > 0xBF) { + *message + = "After a first byte of F0, expecting 3rd byte " + "between 80 and BF."; + *faulty_bytes = 3; + return {i, has_ansi}; + } + if (str[i + 3] < 0x80 || str[i + 3] > 0xBF) { + *message + = "After a first byte of F0, expecting 4th byte " + "between 80 and BF."; + *faulty_bytes = 4; + return {i, has_ansi}; + } + } else { + *message + = "After a first byte of F0, expecting three following " + "bytes."; + *faulty_bytes = 1; + return {i, has_ansi}; + } + i += 4; + } else if (str[i] >= 0xF1 + && str[i] <= 0xF3) /* F1..F3 80..BF 80..BF 80..BF */ + { + if (i + 3 < len) /* Expect a 2nd, 3rd 3th byte */ { + if (str[i + 1] < 0x80 || str[i + 1] > 0xBF) { + *message + = "After a first byte of F1, F2, or F3, expecting a " + "2nd byte between 80 and BF."; + *faulty_bytes = 2; + return {i, has_ansi}; + } + if (str[i + 2] < 0x80 || str[i + 2] > 0xBF) { + *message + = "After a first byte of F1, F2, or F3, expecting a " + "3rd byte between 80 and BF."; + *faulty_bytes = 3; + return {i, has_ansi}; + } + if (str[i + 3] < 0x80 || str[i + 3] > 0xBF) { + *message + = "After a first byte of F1, F2, or F3, expecting a " + "4th byte between 80 and BF."; + *faulty_bytes = 4; + return {i, has_ansi}; + } + } else { + *message + = "After a first byte of F1, F2, or F3, expecting three " + "following bytes."; + *faulty_bytes = 1; + return {i, has_ansi}; + } + i += 4; + } else if (str[i] == 0xF4) /* F4 80..8F 80..BF 80..BF */ { + if (i + 3 < len) /* Expect a 2nd, 3rd 3th byte */ { + if (str[i + 1] < 0x80 || str[i + 1] > 0x8F) { + *message + = "After a first byte of F4, expecting 2nd byte " + "between 80 and 8F."; + *faulty_bytes = 2; + return {i, has_ansi}; + } + if (str[i + 2] < 0x80 || str[i + 2] > 0xBF) { + *message + = "After a first byte of F4, expecting 3rd byte " + "between 80 and BF."; + *faulty_bytes = 3; + return {i, has_ansi}; + } + if (str[i + 3] < 0x80 || str[i + 3] > 0xBF) { + *message + = "After a first byte of F4, expecting 4th byte " + "between 80 and BF."; + *faulty_bytes = 4; + return {i, has_ansi}; + } + } else { + *message + = "After a first byte of F4, expecting three following " + "bytes."; + *faulty_bytes = 1; + return {i, has_ansi}; + } + i += 4; + } else { + *message + = "Expecting bytes in the following ranges: 00..7F C2..F4."; + *faulty_bytes = 1; + return {i, has_ansi}; + } + } + return {-1, has_ansi}; +} diff --git a/src/base/is_utf8.hh b/src/base/is_utf8.hh new file mode 100644 index 0000000..ed94521 --- /dev/null +++ b/src/base/is_utf8.hh @@ -0,0 +1,48 @@ +/* + * is_utf8 is distributed under the following terms: + * + * Copyright (c) 2013 Palard Julien. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _IS_UTF8_H +#define _IS_UTF8_H + +#include +#include + +#include "optional.hpp" + +struct utf8_scan_result { + ssize_t usr_end{0}; + bool usr_has_ansi{false}; +}; + +utf8_scan_result is_utf8(const unsigned char* str, + size_t len, + const char** message, + int* faulty_bytes, + nonstd::optional terminator + = nonstd::nullopt); + +#endif /* _IS_UTF8_H */ diff --git a/src/base/isc.cc b/src/base/isc.cc new file mode 100644 index 0000000..7dbce11 --- /dev/null +++ b/src/base/isc.cc @@ -0,0 +1,147 @@ +/** + * Copyright (c) 2021, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @file isc.cc + */ + +#include + +#include "isc.hh" + +#include "config.h" + +namespace isc { + +void +service_base::start() +{ + log_debug("starting service thread for: %s", this->s_name.c_str()); + this->s_thread = std::thread(&service_base::run, this); + this->s_started = true; +} + +void* +service_base::run() +{ + log_info("BEGIN isc thread: %s", this->s_name.c_str()); + while (this->s_looping) { + mstime_t current_time = getmstime(); + auto timeout = this->compute_timeout(current_time); + + this->s_port.process_for(timeout); + this->s_children.cleanup_children(); + + try { + this->loop_body(); + } catch (const std::exception& e) { + log_error("%s: loop_body() failed with -- %s", + this->s_name.c_str(), + e.what()); + this->s_looping = false; + } catch (...) { + log_error("%s: loop_body() failed with non-standard exception", + this->s_name.c_str()); + this->s_looping = false; + } + } + if (!this->s_children.empty()) { + log_debug("stopping children of service: %s", this->s_name.c_str()); + this->s_children.stop_children(); + } + this->stopped(); + log_info("END isc thread: %s", this->s_name.c_str()); + + return nullptr; +} + +void +service_base::stop() +{ + if (this->s_started) { + log_debug("stopping service thread: %s", this->s_name.c_str()); + if (this->s_looping) { + this->s_looping = false; + this->s_port.send(empty_msg()); + } + log_debug("waiting for service thread: %s", this->s_name.c_str()); + this->s_thread.join(); + log_debug("joined service thread: %s", this->s_name.c_str()); + this->s_started = false; + } +} + +supervisor::supervisor(service_list servs, service_base* parent) + : s_service_list(std::move(servs)), s_parent(parent) +{ + for (auto& serv : this->s_service_list) { + serv->start(); + } +} + +supervisor::~supervisor() +{ + this->stop_children(); +} + +void +supervisor::stop_children() +{ + for (auto& serv : this->s_service_list) { + serv->stop(); + } + this->cleanup_children(); +} + +void +supervisor::cleanup_children() +{ + this->s_service_list.erase( + std::remove_if(this->s_service_list.begin(), + this->s_service_list.end(), + [this](auto& child) { + if (child->is_looping()) { + return false; + } + + child->stop(); + if (this->s_parent != nullptr) { + this->s_parent->child_finished(child); + } + return true; + }), + this->s_service_list.end()); +} + +void +supervisor::add_child_service(std::shared_ptr new_service) +{ + this->s_service_list.emplace_back(new_service); + new_service->start(); +} + +} // namespace isc diff --git a/src/base/isc.hh b/src/base/isc.hh new file mode 100644 index 0000000..339dcaf --- /dev/null +++ b/src/base/isc.hh @@ -0,0 +1,225 @@ +/** + * Copyright (c) 2021, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @file isc.hh + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "injector.hh" +#include "safe/safe.h" +#include "time_util.hh" + +#ifndef lnav_isc_hh +# define lnav_isc_hh + +namespace isc { + +struct msg { + std::function m_callback; +}; + +inline msg +empty_msg() +{ + return {[]() {}}; +} + +class msg_port { +public: + msg_port() = default; + + void send(msg&& m) + { + safe::WriteAccess writable_msgs( + this->mp_messages); + + writable_msgs->emplace_back(m); + this->sp_cond.notify_all(); + } + + template + void process_for(const std::chrono::duration& rel_time) + { + std::deque tmp_msgs; + + { + safe::WriteAccess + writable_msgs(this->mp_messages); + + if (writable_msgs->empty() && rel_time.count() > 0) { + this->sp_cond.template wait_for(writable_msgs.lock, rel_time); + } + + tmp_msgs.swap(*writable_msgs); + } + while (!tmp_msgs.empty()) { + auto& m = tmp_msgs.front(); + + m.m_callback(); + tmp_msgs.pop_front(); + } + } + +private: + using message_list = std::deque; + using safe_message_list = safe::Safe; + + std::condition_variable sp_cond; + safe_message_list mp_messages; +}; + +class service_base; +using service_list = std::vector>; + +struct supervisor { + explicit supervisor(service_list servs = {}, + service_base* parent = nullptr); + + ~supervisor(); + + bool empty() const { return this->s_service_list.empty(); } + + void add_child_service(std::shared_ptr new_service); + + void stop_children(); + + void cleanup_children(); + +protected: + service_list s_service_list; + service_base* s_parent; +}; + +class service_base : public std::enable_shared_from_this { +public: + explicit service_base(std::string name) + : s_name(std::move(name)), s_children({}, this) + { + } + + virtual ~service_base() = default; + + bool is_looping() const { return this->s_looping; } + + msg_port& get_port() { return this->s_port; } + + friend supervisor; + +private: + void start(); + + void stop(); + +protected: + virtual void* run(); + virtual void loop_body() {} + virtual void child_finished(std::shared_ptr child) {} + virtual void stopped() {} + virtual std::chrono::milliseconds compute_timeout( + mstime_t current_time) const + { + using namespace std::literals::chrono_literals; + + return 1s; + } + + const std::string s_name; + bool s_started{false}; + std::thread s_thread; + std::atomic s_looping{true}; + msg_port s_port; + supervisor s_children; +}; + +template +class service : public service_base { +public: + explicit service(std::string sub_name = "") + : service_base(std::string(__PRETTY_FUNCTION__) + " " + sub_name) + { + } + + template + void send(F msg) + { + this->s_port.send({[lifetime = this->shared_from_this(), this, msg]() { + msg(*(static_cast(this))); + }}); + } + + template + void send_and_wait(F msg, + const std::chrono::duration& rel_time) + { + msg_port reply_port; + + this->s_port.send( + {[lifetime = this->shared_from_this(), this, &reply_port, msg]() { + msg(*(static_cast(this))); + reply_port.send(empty_msg()); + }}); + reply_port.template process_for(rel_time); + } +}; + +template +struct to { + void send(std::function cb) + { + auto& service = injector::get(); + + service.send(cb); + } + + template + void send_and_wait(std::function cb, + const std::chrono::duration& rel_time) + { + auto& service = injector::get(); + + service.send_and_wait(cb, rel_time); + } + + void send_and_wait(std::function cb) + { + using namespace std::literals::chrono_literals; + + this->send_and_wait(cb, 48h); + } +}; + +} // namespace isc + +#endif diff --git a/src/base/itertools.hh b/src/base/itertools.hh new file mode 100644 index 0000000..058ceb8 --- /dev/null +++ b/src/base/itertools.hh @@ -0,0 +1,785 @@ +/** + * Copyright (c) 2022, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef lnav_itertools_hh +#define lnav_itertools_hh + +#include +#include +#include +#include +#include + +#include "func_util.hh" +#include "optional.hpp" + +namespace lnav { +namespace itertools { + +struct empty {}; + +struct not_empty {}; + +struct full { + size_t f_max_size; +}; + +namespace details { + +template +struct unwrap_or { + T uo_value; +}; + +template +struct find_if { + P fi_predicate; +}; + +template +struct find { + T f_value; +}; + +struct first {}; + +struct second {}; + +template +struct filter_in { + F f_func; +}; + +template +struct filter_out { + F f_func; +}; + +template +struct sort_by { + C sb_cmp; +}; + +struct sorted {}; + +template +struct mapper { + F m_func; +}; + +template +struct flat_mapper { + F fm_func; +}; + +template +struct for_eacher { + F fe_func; +}; + +template +struct folder { + R f_func; + T f_init; +}; + +template +struct prepend { + T p_value; +}; + +template +struct append { + T p_value; +}; + +struct nth { + nonstd::optional a_index; +}; + +struct skip { + size_t a_count; +}; + +struct unique {}; + +struct max_value {}; + +template +struct max_with_init { + T m_init; +}; + +struct sum {}; + +} // namespace details + +template +inline details::unwrap_or +unwrap_or(T value) +{ + return details::unwrap_or{ + value, + }; +} + +template +inline details::find_if

+find_if(P predicate) +{ + return details::find_if

{ + predicate, + }; +} + +template +inline details::find +find(T value) +{ + return details::find{ + value, + }; +} + +inline details::first +first() +{ + return details::first{}; +} + +inline details::second +second() +{ + return details::second{}; +} + +inline details::nth +nth(nonstd::optional index) +{ + return details::nth{ + index, + }; +} + +inline details::skip +skip(size_t count) +{ + return details::skip{ + count, + }; +} + +template +inline details::filter_in +filter_in(F func) +{ + return details::filter_in{ + func, + }; +} + +template +inline details::filter_out +filter_out(F func) +{ + return details::filter_out{ + func, + }; +} + +template +inline details::prepend +prepend(T value) +{ + return details::prepend{ + std::move(value), + }; +} + +template +inline details::append +append(T value) +{ + return details::append{ + std::move(value), + }; +} + +template +inline details::sort_by +sort_with(C cmp) +{ + return details::sort_by{cmp}; +} + +template +inline auto +sort_by(T C::*m) +{ + return sort_with( + [m](const C& lhs, const C& rhs) { return lhs.*m < rhs.*m; }); +} + +template +inline details::mapper +map(F func) +{ + return details::mapper{func}; +} + +template +inline details::flat_mapper +flat_map(F func) +{ + return details::flat_mapper{func}; +} + +template +inline details::for_eacher +for_each(F func) +{ + return details::for_eacher{func}; +} + +inline auto +deref() +{ + return map([](auto iter) { return *iter; }); +} + +template +inline details::folder +fold(R func, T init) +{ + return details::folder{func, init}; +} + +inline details::unique +unique() +{ + return details::unique{}; +} + +inline details::sorted +sorted() +{ + return details::sorted{}; +} + +template +T +chain(const T& value1, const Args&... args) +{ + T retval; + + for (const auto& arg : {value1, args...}) { + for (const auto& elem : arg) { + retval.emplace_back(elem); + } + } + + return retval; +} + +inline details::max_value +max() +{ + return details::max_value{}; +} + +template +inline details::max_with_init +max(T init) +{ + return details::max_with_init{init}; +} + +inline details::sum +sum() +{ + return details::sum{}; +} + +} // namespace itertools +} // namespace lnav + +template +nonstd::optional>::value, + typename std::remove_reference_t::const_iterator, + typename std::remove_reference_t::iterator>> +operator|(C&& in, const lnav::itertools::details::find_if

& finder) +{ + for (auto iter = in.begin(); iter != in.end(); ++iter) { + if (lnav::func::invoke(finder.fi_predicate, *iter)) { + return nonstd::make_optional(iter); + } + } + + return nonstd::nullopt; +} + +template +nonstd::optional +operator|(const C& in, const lnav::itertools::details::find& finder) +{ + size_t retval = 0; + for (const auto& elem : in) { + if (elem == finder.f_value) { + return nonstd::make_optional(retval); + } + retval += 1; + } + + return nonstd::nullopt; +} + +template +nonstd::optional +operator|(const C& in, const lnav::itertools::details::nth indexer) +{ + if (!indexer.a_index.has_value()) { + return nonstd::nullopt; + } + + if (indexer.a_index.value() < in.size()) { + auto iter = in.begin(); + + std::advance(iter, indexer.a_index.value()); + return nonstd::make_optional(iter); + } + + return nonstd::nullopt; +} + +template +std::vector +operator|(const C& in, const lnav::itertools::details::first indexer) +{ + std::vector retval; + + for (const auto& pair : in) { + retval.emplace_back(pair.first); + } + + return retval; +} + +template +nonstd::optional +operator|(const C& in, const lnav::itertools::details::max_value maxer) +{ + nonstd::optional retval; + + for (const auto& elem : in) { + if (!retval) { + retval = elem; + continue; + } + + if (elem > retval.value()) { + retval = elem; + } + } + + return retval; +} + +template +typename C::value_type +operator|(const C& in, const lnav::itertools::details::max_with_init maxer) +{ + typename C::value_type retval = (typename C::value_type) maxer.m_init; + + for (const auto& elem : in) { + if (elem > retval) { + retval = elem; + } + } + + return retval; +} + +template +typename C::value_type +operator|(const C& in, const lnav::itertools::details::sum summer) +{ + typename C::value_type retval{0}; + + for (const auto& elem : in) { + retval += elem; + } + + return retval; +} + +template +C +operator|(const C& in, const lnav::itertools::details::skip& skipper) +{ + C retval; + + if (skipper.a_count < in.size()) { + auto iter = in.begin(); + std::advance(iter, skipper.a_count); + for (; iter != in.end(); ++iter) { + retval.emplace_back(*iter); + } + } + + return retval; +} + +template +std::vector +operator|(const std::vector>& in, + const lnav::itertools::details::filter_in& filterer) +{ + std::vector retval; + + for (const auto& elem : in) { + if (lnav::func::invoke(filterer.f_func, elem)) { + retval.emplace_back(elem.get()); + } + } + + return retval; +} + +template +C +operator|(const C& in, const lnav::itertools::details::filter_in& filterer) +{ + C retval; + + for (const auto& elem : in) { + if (lnav::func::invoke(filterer.f_func, elem)) { + retval.emplace_back(elem); + } + } + + return retval; +} + +template +C +operator|(const C& in, const lnav::itertools::details::filter_out& filterer) +{ + C retval; + + for (const auto& elem : in) { + if (!lnav::func::invoke(filterer.f_func, elem)) { + retval.emplace_back(elem); + } + } + + return retval; +} + +template +C +operator|(C in, const lnav::itertools::details::prepend& prepender) +{ + in.emplace(in.begin(), prepender.p_value); + + return in; +} + +template +C +operator|(C in, const lnav::itertools::details::append& appender) +{ + in.emplace_back(appender.p_value); + + return in; +} + +template +T +operator|(const C& in, const lnav::itertools::details::folder& folder) +{ + auto accum = folder.f_init; + + for (const auto& elem : in) { + accum = folder.f_func(elem, accum); + } + + return accum; +} + +template +std::set +operator|(C&& in, const lnav::itertools::details::unique& sorter) +{ + return {in.begin(), in.end()}; +} + +template +T +operator|(T in, const lnav::itertools::details::sort_by& sorter) +{ + std::sort(in.begin(), in.end(), sorter.sb_cmp); + + return in; +} + +template +T +operator|(T in, const lnav::itertools::details::sorted& sorter) +{ + std::sort(in.begin(), in.end()); + + return in; +} + +template::value, int> = 0> +auto +operator|(nonstd::optional in, + const lnav::itertools::details::flat_mapper& mapper) -> + typename std::remove_const_t> +{ + if (!in) { + return nonstd::nullopt; + } + + return lnav::func::invoke(mapper.fm_func, in.value()); +} + +template::value, int> = 0> +void +operator|(nonstd::optional in, + const lnav::itertools::details::for_eacher& eacher) +{ + if (!in) { + return; + } + + lnav::func::invoke(eacher.fe_func, in.value()); +} + +template::value, int> = 0> +void +operator|(std::vector>& in, + const lnav::itertools::details::for_eacher& eacher) +{ + for (auto& elem : in) { + lnav::func::invoke(eacher.fe_func, *elem); + } +} + +template::value, int> = 0> +auto +operator|(nonstd::optional in, + const lnav::itertools::details::mapper& mapper) + -> nonstd::optional< + typename std::remove_const_t>> +{ + if (!in) { + return nonstd::nullopt; + } + + return nonstd::make_optional(lnav::func::invoke(mapper.m_func, in.value())); +} + +template +auto +operator|(const T& in, const lnav::itertools::details::mapper& mapper) + -> std::vector()))>>> +{ + using return_type = std::vector()))>>>; + return_type retval; + + retval.reserve(in.size()); + std::transform( + in.begin(), in.end(), std::back_inserter(retval), mapper.m_func); + + return retval; +} + +template +auto +operator|(const T& in, const lnav::itertools::details::mapper& mapper) + -> std::vector< + std::remove_const_t> +{ + using return_type = std::vector< + std::remove_const_t>; + return_type retval; + + retval.reserve(in.size()); + for (const auto& elem : in) { + retval.template emplace_back((elem.*mapper.m_func)()); + } + + return retval; +} + +template +auto +operator|(const std::vector& in, + const lnav::itertools::details::mapper& mapper) + -> std::vector()).*mapper.m_func)())>>> +{ + using return_type + = std::vector()).*mapper.m_func)())>>>; + return_type retval; + + retval.reserve(in.size()); + std::transform( + in.begin(), + in.end(), + std::back_inserter(retval), + [&mapper](const auto& elem) { return ((*elem).*mapper.m_func)(); }); + + return retval; +} + +template +auto +operator|(const std::set& in, + const lnav::itertools::details::mapper& mapper) + -> std::vector()).*mapper.m_func)())>>> +{ + using return_type + = std::vector()).*mapper.m_func)())>>>; + return_type retval; + + retval.reserve(in.size()); + std::transform( + in.begin(), + in.end(), + std::back_inserter(retval), + [&mapper](const auto& elem) { return ((*elem).*mapper.m_func)(); }); + + return retval; +} + +template::value, int> = 0> +auto +operator|(const std::vector>& in, + const lnav::itertools::details::mapper& mapper) + -> std::vector>> +{ + using return_type = std::vector< + typename std::remove_const_t>>; + return_type retval; + + retval.reserve(in.size()); + for (const auto& elem : in) { + retval.template emplace_back(((*elem).*mapper.m_func)); + } + + return retval; +} + +template::value, int> = 0> +auto +operator|(const std::vector& in, + const lnav::itertools::details::mapper& mapper) + -> std::vector< + typename std::remove_const_t>> +{ + using return_type = std::vector< + typename std::remove_const_t>>; + return_type retval; + + retval.reserve(in.size()); + for (const auto& elem : in) { + retval.template emplace_back(elem.*mapper.m_func); + } + + return retval; +} + +template::value, int> = 0> +auto +operator|(nonstd::optional in, + const lnav::itertools::details::mapper& mapper) + -> nonstd::optional>> +{ + if (!in) { + return nonstd::nullopt; + } + + return nonstd::make_optional((in.value()).*mapper.m_func); +} + +template::value, int> = 0> +auto +operator|(nonstd::optional in, + const lnav::itertools::details::mapper& mapper) + -> nonstd::optional< + typename std::remove_const_t>> +{ + if (!in) { + return nonstd::nullopt; + } + + return nonstd::make_optional((*in.value()).*mapper.m_func); +} + +template +T +operator|(nonstd::optional in, + const lnav::itertools::details::unwrap_or& unwrapper) +{ + return in.value_or(unwrapper.uo_value); +} + +#endif diff --git a/src/base/lnav.console.cc b/src/base/lnav.console.cc new file mode 100644 index 0000000..a34ebac --- /dev/null +++ b/src/base/lnav.console.cc @@ -0,0 +1,494 @@ +/** + * Copyright (c) 2022, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include "lnav.console.hh" + +#include "config.h" +#include "fmt/color.h" +#include "itertools.hh" +#include "lnav.console.into.hh" +#include "log_level_enum.hh" +#include "pcrepp/pcre2pp.hh" +#include "snippet_highlighters.hh" +#include "view_curses.hh" + +using namespace lnav::roles::literals; + +namespace lnav { +namespace console { + +user_message +user_message::raw(const attr_line_t& al) +{ + user_message retval; + + retval.um_level = level::raw; + retval.um_message.append(al); + return retval; +} + +user_message +user_message::error(const attr_line_t& al) +{ + user_message retval; + + retval.um_level = level::error; + retval.um_message.append(al); + return retval; +} + +user_message +user_message::info(const attr_line_t& al) +{ + user_message retval; + + retval.um_level = level::info; + retval.um_message.append(al); + return retval; +} + +user_message +user_message::ok(const attr_line_t& al) +{ + user_message retval; + + retval.um_level = level::ok; + retval.um_message.append(al); + return retval; +} + +user_message +user_message::warning(const attr_line_t& al) +{ + user_message retval; + + retval.um_level = level::warning; + retval.um_message.append(al); + return retval; +} + +attr_line_t +user_message::to_attr_line(std::set flags) const +{ + auto indent = 1; + attr_line_t retval; + + if (this->um_level == level::warning) { + indent = 3; + } + + if (flags.count(render_flags::prefix)) { + switch (this->um_level) { + case level::raw: + break; + case level::ok: + retval.append(lnav::roles::ok("\u2714 ")); + break; + case level::info: + retval.append("\u24d8 info"_info).append(": "); + break; + case level::warning: + retval.append(lnav::roles::warning("\u26a0 warning")) + .append(": "); + break; + case level::error: + retval.append(lnav::roles::error("\u2718 error")).append(": "); + break; + } + } + + retval.append(this->um_message).append("\n"); + if (!this->um_reason.empty()) { + bool first_line = true; + for (const auto& line : this->um_reason.split_lines()) { + auto role = this->um_level == level::error ? role_t::VCR_ERROR + : role_t::VCR_WARNING; + attr_line_t prefix; + + if (first_line) { + prefix.append(indent, ' ') + .append("reason", VC_ROLE.value(role)) + .append(": "); + first_line = false; + } else { + prefix.append(" | ", VC_ROLE.value(role)) + .append(indent, ' '); + } + retval.append(prefix).append(line).append("\n"); + } + } + if (!this->um_snippets.empty()) { + for (const auto& snip : this->um_snippets) { + attr_line_t header; + + header.append(" --> "_snippet_border) + .append(lnav::roles::file(snip.s_location.sl_source.get())); + if (snip.s_location.sl_line_number > 0) { + header.append(":").appendf(FMT_STRING("{}"), + snip.s_location.sl_line_number); + } + retval.append(header).append("\n"); + if (!snip.s_content.blank()) { + auto snippet_lines = snip.s_content.split_lines(); + auto longest_line_length = snippet_lines + | lnav::itertools::map(&attr_line_t::utf8_length_or_length) + | lnav::itertools::max(40); + + for (auto& line : snippet_lines) { + line.pad_to(longest_line_length); + retval.append(" | "_snippet_border) + .append(line) + .append("\n"); + } + } + } + } + if (!this->um_notes.empty()) { + for (const auto& note : this->um_notes) { + bool first_line = true; + for (const auto& line : note.split_lines()) { + attr_line_t prefix; + + if (first_line) { + prefix.append(" ="_snippet_border) + .append(indent, ' ') + .append("note"_snippet_border) + .append(": "); + first_line = false; + } else { + prefix.append(" ").append(indent, ' '); + } + + retval.append(prefix).append(line).append("\n"); + } + } + } + if (!this->um_help.empty()) { + bool first_line = true; + for (const auto& line : this->um_help.split_lines()) { + attr_line_t prefix; + + if (first_line) { + prefix.append(" ="_snippet_border) + .append(indent, ' ') + .append("help"_snippet_border) + .append(": "); + first_line = false; + } else { + prefix.append(" "); + } + + retval.append(prefix).append(line).append("\n"); + } + } + + return retval; +} + +static nonstd::optional +curses_color_to_terminal_color(int curses_color) +{ + switch (curses_color) { + case COLOR_BLACK: + return fmt::terminal_color::black; + case COLOR_CYAN: + return fmt::terminal_color::cyan; + case COLOR_WHITE: + return fmt::terminal_color::white; + case COLOR_MAGENTA: + return fmt::terminal_color::magenta; + case COLOR_BLUE: + return fmt::terminal_color::blue; + case COLOR_YELLOW: + return fmt::terminal_color::yellow; + case COLOR_GREEN: + return fmt::terminal_color::green; + case COLOR_RED: + return fmt::terminal_color::red; + default: + return nonstd::nullopt; + } +} + +void +println(FILE* file, const attr_line_t& al) +{ + const auto& str = al.get_string(); + + if (getenv("NO_COLOR") != nullptr + || (!isatty(fileno(file)) && getenv("YES_COLOR") == nullptr)) + { + fmt::print(file, "{}\n", str); + return; + } + + std::set points = {0, static_cast(al.length())}; + + for (const auto& attr : al.get_attrs()) { + if (!attr.sa_range.is_valid()) { + continue; + } + points.insert(attr.sa_range.lr_start); + if (attr.sa_range.lr_end > 0) { + points.insert(attr.sa_range.lr_end); + } + } + + nonstd::optional last_point; + for (const auto& point : points) { + if (!last_point) { + last_point = point; + continue; + } + auto default_fg_style = fmt::text_style{}; + auto default_bg_style = fmt::text_style{}; + auto line_style = fmt::text_style{}; + auto fg_style = fmt::text_style{}; + auto start = last_point.value(); + + for (const auto& attr : al.get_attrs()) { + if (!attr.sa_range.contains(start) + && !attr.sa_range.contains(point - 1)) + { + continue; + } + + try { + if (attr.sa_type == &VC_BACKGROUND) { + auto saw = string_attr_wrapper(&attr); + auto color_opt = curses_color_to_terminal_color(saw.get()); + + if (color_opt) { + line_style |= fmt::bg(color_opt.value()); + } + } else if (attr.sa_type == &VC_FOREGROUND) { + auto saw = string_attr_wrapper(&attr); + auto color_opt = curses_color_to_terminal_color(saw.get()); + + if (color_opt) { + fg_style = fmt::fg(color_opt.value()); + } + } else if (attr.sa_type == &VC_STYLE) { + auto saw = string_attr_wrapper(&attr); + auto style = saw.get(); + + if (style.ta_attrs & A_REVERSE) { + line_style |= fmt::emphasis::reverse; + } + if (style.ta_attrs & A_BOLD) { + line_style |= fmt::emphasis::bold; + } + if (style.ta_attrs & A_UNDERLINE) { + line_style |= fmt::emphasis::underline; + } + if (style.ta_fg_color) { + auto color_opt = curses_color_to_terminal_color( + style.ta_fg_color.value()); + + if (color_opt) { + fg_style = fmt::fg(color_opt.value()); + } + } + if (style.ta_bg_color) { + auto color_opt = curses_color_to_terminal_color( + style.ta_bg_color.value()); + + if (color_opt) { + line_style |= fmt::bg(color_opt.value()); + } + } + } else if (attr.sa_type == &SA_LEVEL) { + auto level = static_cast( + attr.sa_value.get()); + + switch (level) { + case LEVEL_FATAL: + case LEVEL_CRITICAL: + case LEVEL_ERROR: + line_style |= fmt::fg(fmt::terminal_color::red); + break; + case LEVEL_WARNING: + line_style |= fmt::fg(fmt::terminal_color::yellow); + break; + default: + break; + } + } else if (attr.sa_type == &VC_ROLE) { + auto saw = string_attr_wrapper(&attr); + auto role = saw.get(); + + switch (role) { + case role_t::VCR_TEXT: + case role_t::VCR_IDENTIFIER: + break; + case role_t::VCR_SEARCH: + line_style |= fmt::emphasis::reverse; + break; + case role_t::VCR_ERROR: + line_style |= fmt::fg(fmt::terminal_color::red) + | fmt::emphasis::bold; + break; + case role_t::VCR_WARNING: + case role_t::VCR_RE_REPEAT: + line_style |= fmt::fg(fmt::terminal_color::yellow); + break; + case role_t::VCR_COMMENT: + line_style |= fmt::fg(fmt::terminal_color::green); + break; + case role_t::VCR_SNIPPET_BORDER: + line_style |= fmt::fg(fmt::terminal_color::cyan); + break; + case role_t::VCR_OK: + line_style |= fmt::emphasis::bold + | fmt::fg(fmt::terminal_color::green); + break; + case role_t::VCR_INFO: + case role_t::VCR_STATUS: + line_style |= fmt::emphasis::bold + | fmt::fg(fmt::terminal_color::magenta); + break; + case role_t::VCR_KEYWORD: + case role_t::VCR_RE_SPECIAL: + line_style |= fmt::emphasis::bold + | fmt::fg(fmt::terminal_color::cyan); + break; + case role_t::VCR_STRING: + line_style |= fmt::fg(fmt::terminal_color::magenta); + break; + case role_t::VCR_VARIABLE: + line_style |= fmt::emphasis::underline; + break; + case role_t::VCR_SYMBOL: + case role_t::VCR_NUMBER: + case role_t::VCR_FILE: + line_style |= fmt::emphasis::bold; + break; + case role_t::VCR_H1: + line_style |= fmt::emphasis::bold + | fmt::fg(fmt::terminal_color::magenta); + break; + case role_t::VCR_H2: + line_style |= fmt::emphasis::bold; + break; + case role_t::VCR_H3: + case role_t::VCR_H4: + case role_t::VCR_H5: + case role_t::VCR_H6: + line_style |= fmt::emphasis::underline; + break; + case role_t::VCR_LIST_GLYPH: + line_style |= fmt::fg(fmt::terminal_color::yellow); + break; + case role_t::VCR_QUOTED_CODE: + default_fg_style + = fmt::fg(fmt::terminal_color::white); + default_bg_style + = fmt::bg(fmt::terminal_color::black); + break; + case role_t::VCR_LOW_THRESHOLD: + line_style |= fmt::bg(fmt::terminal_color::green); + break; + case role_t::VCR_MED_THRESHOLD: + line_style |= fmt::bg(fmt::terminal_color::yellow); + break; + case role_t::VCR_HIGH_THRESHOLD: + line_style |= fmt::bg(fmt::terminal_color::red); + break; + default: + // log_debug("missing role handler %d", (int) role); + break; + } + } + } catch (const fmt::format_error& e) { + log_error("style error: %s", e.what()); + } + } + + if (!line_style.has_foreground() && fg_style.has_foreground()) { + line_style |= fg_style; + } + if (!line_style.has_foreground() && default_fg_style.has_foreground()) { + line_style |= default_fg_style; + } + if (!line_style.has_background() && default_bg_style.has_background()) { + line_style |= default_bg_style; + } + + if (start < str.size()) { + auto actual_end = std::min(str.size(), static_cast(point)); + fmt::print(file, + line_style, + FMT_STRING("{}"), + str.substr(start, actual_end - start)); + } + last_point = point; + } + fmt::print(file, "\n"); +} + +void +print(FILE* file, const user_message& um) +{ + auto al = um.to_attr_line(); + + if (endswith(al.get_string(), "\n")) { + al.erase(al.length() - 1); + } + println(file, al); +} + +user_message +to_user_message(intern_string_t src, const lnav::pcre2pp::compile_error& ce) +{ + attr_line_t pcre_error_content{ce.ce_pattern}; + + lnav::snippets::regex_highlighter(pcre_error_content, + pcre_error_content.length(), + line_range{ + 0, + (int) pcre_error_content.length(), + }); + pcre_error_content.append("\n") + .append(ce.ce_offset, ' ') + .append(lnav::roles::error("^ ")) + .append(lnav::roles::error(ce.get_message())) + .with_attr_for_all(VC_ROLE.value(role_t::VCR_QUOTED_CODE)); + + return user_message::error( + attr_line_t() + .append_quoted(ce.ce_pattern) + .append(" is not a valid regular expression")) + .with_reason(ce.get_message()) + .with_snippet(lnav::console::snippet::from(src, pcre_error_content)); +} + +} // namespace console +} // namespace lnav diff --git a/src/base/lnav.console.hh b/src/base/lnav.console.hh new file mode 100644 index 0000000..ac4c2b0 --- /dev/null +++ b/src/base/lnav.console.hh @@ -0,0 +1,176 @@ +/** + * Copyright (c) 2022, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef lnav_console_hh +#define lnav_console_hh + +#include +#include + +#include "base/attr_line.hh" +#include "base/file_range.hh" + +namespace lnav { +namespace console { + +void println(FILE* file, const attr_line_t& al); + +struct snippet { + static snippet from(intern_string_t src, const attr_line_t& content) + { + snippet retval; + + retval.s_location.sl_source = src; + retval.s_content = content; + return retval; + } + + static snippet from(source_location loc, const attr_line_t& content) + { + snippet retval; + + retval.s_location = loc; + retval.s_content = content; + return retval; + } + + snippet& with_line(int32_t line) + { + this->s_location.sl_line_number = line; + return *this; + } + + source_location s_location; + attr_line_t s_content; +}; + +struct user_message { + enum class level { + raw, + ok, + info, + warning, + error, + }; + + static user_message raw(const attr_line_t& al); + + static user_message error(const attr_line_t& al); + + static user_message warning(const attr_line_t& al); + + static user_message info(const attr_line_t& al); + + static user_message ok(const attr_line_t& al); + + user_message& with_reason(const attr_line_t& al) + { + this->um_reason = al; + this->um_reason.rtrim(); + return *this; + } + + user_message& with_reason(const user_message& um) + { + return this->with_reason(um.to_attr_line({})); + } + + user_message& with_errno_reason() + { + this->um_reason = strerror(errno); + return *this; + } + + user_message& with_snippet(const snippet& sn) + { + this->um_snippets.emplace_back(sn); + return *this; + } + + template + user_message& with_snippets(C snippets) + { + this->um_snippets.insert(this->um_snippets.end(), + std::make_move_iterator(std::begin(snippets)), + std::make_move_iterator(std::end(snippets))); + if (this->um_snippets.size() > 1) { + for (auto iter = this->um_snippets.begin(); + iter != this->um_snippets.end();) { + if (iter->s_content.empty()) { + iter = this->um_snippets.erase(iter); + } else { + ++iter; + } + } + } + return *this; + } + + user_message& with_note(const attr_line_t& al) + { + if (!al.blank()) { + this->um_notes.emplace_back(al); + } + + return *this; + } + + user_message& with_help(const attr_line_t& al) + { + if (al.blank()) { + this->um_help.clear(); + } else { + this->um_help = al; + this->um_help.rtrim(); + } + + return *this; + } + + enum class render_flags { + prefix, + }; + + attr_line_t to_attr_line(std::set flags + = {render_flags::prefix}) const; + + level um_level{level::ok}; + attr_line_t um_message; + std::vector um_snippets; + attr_line_t um_reason; + std::vector um_notes; + attr_line_t um_help; +}; + +void print(FILE* file, const user_message& um); + +} // namespace console +} // namespace lnav + +#endif diff --git a/src/base/lnav.console.into.hh b/src/base/lnav.console.into.hh new file mode 100644 index 0000000..206d563 --- /dev/null +++ b/src/base/lnav.console.into.hh @@ -0,0 +1,51 @@ +/** + * Copyright (c) 2022, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef lnav_console_into_hh +#define lnav_console_into_hh + +#include "intern_string.hh" +#include "lnav.console.hh" + +namespace lnav { +namespace pcre2pp { + +struct compile_error; + +} + +namespace console { + +user_message to_user_message(intern_string_t src, + const pcre2pp::compile_error& ce); + +} +} // namespace lnav + +#endif diff --git a/src/base/lnav.gzip.cc b/src/base/lnav.gzip.cc new file mode 100644 index 0000000..6b31dad --- /dev/null +++ b/src/base/lnav.gzip.cc @@ -0,0 +1,135 @@ +/** + * Copyright (c) 2021, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @file lnav.gzip.cc + */ + +#include "lnav.gzip.hh" + +#include + +#include "config.h" +#include "fmt/format.h" + +namespace lnav { +namespace gzip { + +bool +is_gzipped(const char* buffer, size_t len) +{ + return len > 2 && buffer[0] == '\037' && buffer[1] == '\213'; +} + +Result +compress(const void* input, size_t len) +{ + auto retval = auto_buffer::alloc(len + 4096); + + z_stream zs; + zs.zalloc = Z_NULL; + zs.zfree = Z_NULL; + zs.opaque = Z_NULL; + zs.avail_in = (uInt) len; + zs.next_in = (Bytef*) input; + zs.avail_out = (uInt) retval.capacity(); + zs.next_out = (Bytef*) retval.in(); + zs.total_out = 0; + + auto rc = deflateInit2( + &zs, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15 | 16, 8, Z_DEFAULT_STRATEGY); + if (rc != Z_OK) { + return Err(fmt::format( + FMT_STRING("unable to initialize compressor -- {}"), zError(rc))); + } + rc = deflate(&zs, Z_FINISH); + if (rc != Z_STREAM_END) { + return Err(fmt::format(FMT_STRING("unable to compress data -- {}"), + zError(rc))); + } + rc = deflateEnd(&zs); + if (rc != Z_OK) { + return Err(fmt::format( + FMT_STRING("unable to finalize compression -- {}"), zError(rc))); + } + return Ok(std::move(retval.resize(zs.total_out))); +} + +Result +uncompress(const std::string& src, const void* buffer, size_t size) +{ + auto uncomp = auto_buffer::alloc(size * 2); + z_stream strm; + int err; + + strm.next_in = (Bytef*) buffer; + strm.msg = Z_NULL; + strm.avail_in = size; + strm.total_in = 0; + strm.total_out = 0; + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + + if ((err = inflateInit2(&strm, (16 + MAX_WBITS))) != Z_OK) { + return Err(fmt::format(FMT_STRING("invalid gzip data: {} -- {}"), + src, + strm.msg ? strm.msg : zError(err))); + } + + bool done = false; + + while (!done) { + if (strm.total_out >= uncomp.size()) { + uncomp.expand_by(size / 2); + } + + strm.next_out = (Bytef*) (uncomp.in() + strm.total_out); + strm.avail_out = uncomp.capacity() - strm.total_out; + + // Inflate another chunk. + err = inflate(&strm, Z_SYNC_FLUSH); + if (err == Z_STREAM_END) { + done = true; + } else if (err != Z_OK) { + inflateEnd(&strm); + return Err(fmt::format(FMT_STRING("unable to uncompress: {} -- {}"), + src, + strm.msg ? strm.msg : zError(err))); + } + } + + if (inflateEnd(&strm) != Z_OK) { + return Err(fmt::format(FMT_STRING("unable to uncompress: {} -- {}"), + src, + strm.msg ? strm.msg : zError(err))); + } + + return Ok(std::move(uncomp.resize(strm.total_out))); +} + +} // namespace gzip +} // namespace lnav diff --git a/src/base/lnav.gzip.hh b/src/base/lnav.gzip.hh new file mode 100644 index 0000000..bd73965 --- /dev/null +++ b/src/base/lnav.gzip.hh @@ -0,0 +1,54 @@ +/** + * Copyright (c) 2021, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @file lnav.gzip.hh + */ + +#ifndef lnav_gzip_hh +#define lnav_gzip_hh + +#include + +#include "auto_mem.hh" +#include "result.h" + +namespace lnav { +namespace gzip { + +bool is_gzipped(const char* buffer, size_t len); + +Result compress(const void* input, size_t len); + +Result uncompress(const std::string& src, + const void* buffer, + size_t size); + +} // namespace gzip +} // namespace lnav + +#endif diff --git a/src/base/lnav.gzip.tests.cc b/src/base/lnav.gzip.tests.cc new file mode 100644 index 0000000..2f6939e --- /dev/null +++ b/src/base/lnav.gzip.tests.cc @@ -0,0 +1,67 @@ +/** + * Copyright (c) 2021, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +#include "base/lnav.gzip.hh" +#include "config.h" +#include "doctest/doctest.h" + +TEST_CASE("lnav::gzip::uncompress") +{ + { + auto u_res = lnav::gzip::uncompress("empty", nullptr, 0); + + CHECK(u_res.isErr()); + CHECK(u_res.unwrapErr() + == "unable to uncompress: empty -- stream error"); + } + + { + auto u_res = lnav::gzip::uncompress("garbage", "abc", 3); + + CHECK(u_res.isErr()); + CHECK(u_res.unwrapErr() + == "unable to uncompress: garbage -- incorrect header check"); + } +} + +TEST_CASE("lnav::gzip::roundtrip") +{ + const char msg[] = "Hello, World!"; + + auto c_res = lnav::gzip::compress(msg, sizeof(msg)); + auto buf = c_res.unwrap(); + auto u_res = lnav::gzip::uncompress("test", buf.in(), buf.size()); + auto buf2 = u_res.unwrap(); + + CHECK(std::string(msg) == std::string(buf2.in())); +} diff --git a/src/base/lnav_log.cc b/src/base/lnav_log.cc new file mode 100644 index 0000000..c1b08c4 --- /dev/null +++ b/src/base/lnav_log.cc @@ -0,0 +1,671 @@ +/** + * Copyright (c) 2014, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @file lnav_log.cc + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" + +#ifdef HAVE_EXECINFO_H +# include +#endif +#if BACKWARD_HAS_DW == 1 +# include "backward-cpp/backward.hpp" +#endif + +#include +#include +#include +#include + +#define PCRE2_CODE_UNIT_WIDTH 8 +#include +#include +#include +#include +#include +#include + +#if defined HAVE_NCURSESW_CURSES_H +# include +# include +#elif defined HAVE_NCURSESW_H +# include +# include +#elif defined HAVE_NCURSES_CURSES_H +# include +# include +#elif defined HAVE_NCURSES_H +# include +# include +#elif defined HAVE_CURSES_H +# include +# include +#else +# error "SysV or X/Open-compatible Curses header file required" +#endif + +#include "auto_mem.hh" +#include "enum_util.hh" +#include "lnav_log.hh" +#include "opt_util.hh" + +static const size_t BUFFER_SIZE = 256 * 1024; +static const size_t MAX_LOG_LINE_SIZE = 2 * 1024; + +static const char* CRASH_MSG + = "\n" + "\n" + "==== GURU MEDITATION ====\n" + "Unfortunately, lnav has crashed, sorry for the inconvenience.\n" + "\n" + "You can help improve lnav by sending the following file " + "to " PACKAGE_BUGREPORT + " :\n" + " %s\n" + "=========================\n"; + +nonstd::optional lnav_log_file; +lnav_log_level_t lnav_log_level = lnav_log_level_t::DEBUG; +const char* lnav_log_crash_dir; +nonstd::optional lnav_log_orig_termios; +// NOTE: This mutex is leaked so that it is not destroyed during exit. +// Otherwise, any attempts to log will fail. +static std::mutex* +lnav_log_mutex() +{ + static auto* retval = new std::mutex(); + + return retval; +} + +static std::vector& +DUMPER_LIST() +{ + static auto* retval = new std::vector(); + + return *retval; +} +static std::vector CRASH_LIST; + +struct thid { + static uint32_t COUNTER; + + thid() noexcept : t_id(COUNTER++) {} + + uint32_t t_id; +}; + +uint32_t thid::COUNTER = 0; + +thread_local thid current_thid; +thread_local std::string thread_log_prefix; + +static struct { + size_t lr_length; + off_t lr_frag_start; + off_t lr_frag_end; + char lr_data[BUFFER_SIZE]; +} log_ring = {0, BUFFER_SIZE, 0, {}}; + +static const char* LEVEL_NAMES[] = { + "T", + "D", + "I", + "W", + "E", +}; + +static char* +log_alloc() +{ + off_t data_end = log_ring.lr_length + MAX_LOG_LINE_SIZE; + + if (data_end >= (off_t) BUFFER_SIZE) { + const char* new_start = &log_ring.lr_data[MAX_LOG_LINE_SIZE]; + + new_start = (const char*) memchr( + new_start, '\n', log_ring.lr_length - MAX_LOG_LINE_SIZE); + log_ring.lr_frag_start = new_start - log_ring.lr_data; + log_ring.lr_frag_end = log_ring.lr_length; + log_ring.lr_length = 0; + + assert(log_ring.lr_frag_start >= 0); + assert(log_ring.lr_frag_start <= (off_t) BUFFER_SIZE); + } else if (data_end >= log_ring.lr_frag_start) { + const char* new_start = &log_ring.lr_data[log_ring.lr_frag_start]; + + new_start = (const char*) memchr( + new_start, '\n', log_ring.lr_frag_end - log_ring.lr_frag_start); + assert(new_start != nullptr); + log_ring.lr_frag_start = new_start - log_ring.lr_data; + assert(log_ring.lr_frag_start >= 0); + assert(log_ring.lr_frag_start <= (off_t) BUFFER_SIZE); + } + + return &log_ring.lr_data[log_ring.lr_length]; +} + +void +log_argv(int argc, char* argv[]) +{ + const char* log_path = getenv("LNAV_LOG_PATH"); + + if (log_path != nullptr) { + lnav_log_file = make_optional_from_nullable(fopen(log_path, "a")); + } + + log_info("argv[%d] =", argc); + for (int lpc = 0; lpc < argc; lpc++) { + log_info(" [%d] = %s", lpc, argv[lpc]); + } +} + +void +log_set_thread_prefix(std::string prefix) +{ + // thread_log_prefix = std::move(prefix); +} + +void +log_host_info() +{ + char cwd[MAXPATHLEN]; + char jittarget[128]; + struct utsname un; + struct rusage ru; + uint32_t pcre_jit; + + uname(&un); + pcre2_config(PCRE2_CONFIG_JIT, &pcre_jit); + pcre2_config(PCRE2_CONFIG_JITTARGET, jittarget); + + log_info("uname:"); + log_info(" sysname=%s", un.sysname); + log_info(" nodename=%s", un.nodename); + log_info(" machine=%s", un.machine); + log_info(" release=%s", un.release); + log_info(" version=%s", un.version); + log_info("PCRE:"); + log_info(" jit=%d", pcre_jit); + log_info(" jittarget=%s", jittarget); + log_info("Environment:"); + log_info(" HOME=%s", getenv("HOME")); + log_info(" XDG_CONFIG_HOME=%s", getenv("XDG_CONFIG_HOME")); + log_info(" LANG=%s", getenv("LANG")); + log_info(" PATH=%s", getenv("PATH")); + log_info(" TERM=%s", getenv("TERM")); + log_info(" TZ=%s", getenv("TZ")); + log_info("Process:"); + log_info(" pid=%d", getpid()); + log_info(" ppid=%d", getppid()); + log_info(" pgrp=%d", getpgrp()); + log_info(" uid=%d", getuid()); + log_info(" gid=%d", getgid()); + log_info(" euid=%d", geteuid()); + log_info(" egid=%d", getegid()); + if (getcwd(cwd, sizeof(cwd)) == nullptr) { + log_info(" ERROR: getcwd failed"); + } else { + log_info(" cwd=%s", cwd); + } + log_info("Executable:"); + log_info(" version=%s", VCS_PACKAGE_STRING); + + getrusage(RUSAGE_SELF, &ru); + log_rusage(lnav_log_level_t::INFO, ru); +} + +void +log_rusage_raw(enum lnav_log_level_t level, + const char* src_file, + int line_number, + const struct rusage& ru) +{ + log_msg(level, src_file, line_number, "rusage:"); + log_msg(level, + src_file, + line_number, + " utime=%d.%06d", + ru.ru_utime.tv_sec, + ru.ru_utime.tv_usec); + log_msg(level, + src_file, + line_number, + " stime=%d.%06d", + ru.ru_stime.tv_sec, + ru.ru_stime.tv_usec); + log_msg(level, src_file, line_number, " maxrss=%ld", ru.ru_maxrss); + log_msg(level, src_file, line_number, " ixrss=%ld", ru.ru_ixrss); + log_msg(level, src_file, line_number, " idrss=%ld", ru.ru_idrss); + log_msg(level, src_file, line_number, " isrss=%ld", ru.ru_isrss); + log_msg(level, src_file, line_number, " minflt=%ld", ru.ru_minflt); + log_msg(level, src_file, line_number, " majflt=%ld", ru.ru_majflt); + log_msg(level, src_file, line_number, " nswap=%ld", ru.ru_nswap); + log_msg(level, src_file, line_number, " inblock=%ld", ru.ru_inblock); + log_msg(level, src_file, line_number, " oublock=%ld", ru.ru_oublock); + log_msg(level, src_file, line_number, " msgsnd=%ld", ru.ru_msgsnd); + log_msg(level, src_file, line_number, " msgrcv=%ld", ru.ru_msgrcv); + log_msg(level, src_file, line_number, " nsignals=%ld", ru.ru_nsignals); + log_msg(level, src_file, line_number, " nvcsw=%ld", ru.ru_nvcsw); + log_msg(level, src_file, line_number, " nivcsw=%ld", ru.ru_nivcsw); +} + +void +log_msg(lnav_log_level_t level, + const char* src_file, + int line_number, + const char* fmt, + ...) +{ + struct timeval curr_time; + struct tm localtm; + ssize_t prefix_size; + va_list args; + ssize_t rc; + + if (level < lnav_log_level) { + return; + } + + std::lock_guard log_lock(*lnav_log_mutex()); + + { + // get the base name of the file. NB: can't use basename() since it + // can modify its argument + const char* last_slash = src_file; + + for (int lpc = 0; src_file[lpc]; lpc++) { + if (src_file[lpc] == '/' || src_file[lpc] == '\\') { + last_slash = &src_file[lpc + 1]; + } + } + + src_file = last_slash; + } + + va_start(args, fmt); + gettimeofday(&curr_time, nullptr); + localtime_r(&curr_time.tv_sec, &localtm); + auto line = log_alloc(); + prefix_size = snprintf(line, + MAX_LOG_LINE_SIZE, + "%4d-%02d-%02dT%02d:%02d:%02d.%03d %s t%u %s:%d ", + localtm.tm_year + 1900, + localtm.tm_mon + 1, + localtm.tm_mday, + localtm.tm_hour, + localtm.tm_min, + localtm.tm_sec, + (int) (curr_time.tv_usec / 1000), + LEVEL_NAMES[lnav::enums::to_underlying(level)], + current_thid.t_id, + src_file, + line_number); +#if 0 + if (!thread_log_prefix.empty()) { + prefix_size += snprintf( + &line[prefix_size], MAX_LOG_LINE_SIZE - prefix_size, + "%s ", + thread_log_prefix.c_str()); + } +#endif + rc = vsnprintf( + &line[prefix_size], MAX_LOG_LINE_SIZE - prefix_size, fmt, args); + if (rc >= (ssize_t) (MAX_LOG_LINE_SIZE - prefix_size)) { + rc = MAX_LOG_LINE_SIZE - prefix_size - 1; + } + line[prefix_size + rc] = '\n'; + log_ring.lr_length += prefix_size + rc + 1; + lnav_log_file | [&](auto file) { + fwrite(line, 1, prefix_size + rc + 1, file); + fflush(file); + }; + va_end(args); +} + +void +log_msg_extra(const char* fmt, ...) +{ + std::lock_guard mg(*lnav_log_mutex()); + va_list args; + + va_start(args, fmt); + auto line = log_alloc(); + auto rc = vsnprintf(line, MAX_LOG_LINE_SIZE - 1, fmt, args); + log_ring.lr_length += rc; + lnav_log_file | [&](auto file) { + fwrite(line, 1, rc, file); + fflush(file); + }; + va_end(args); +} + +void +log_msg_extra_complete() +{ + std::lock_guard mg(*lnav_log_mutex()); + auto line = log_alloc(); + line[0] = '\n'; + log_ring.lr_length += 1; + lnav_log_file | [&](auto file) { + fwrite(line, 1, 1, file); + fflush(file); + }; +} + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-result" +static void +sigabrt(int sig, siginfo_t* info, void* ctx) +{ + char crash_path[1024], latest_crash_path[1024]; + int fd; +#ifdef HAVE_EXECINFO_H + int frame_count; + void* frames[128]; +#endif + struct tm localtm; + time_t curr_time; + + if (lnav_log_crash_dir == nullptr) { + printf("%*s", (int) log_ring.lr_length, log_ring.lr_data); + return; + } + + log_error("Received signal: %d", sig); + +#ifdef HAVE_EXECINFO_H + frame_count = backtrace(frames, 128); +#endif + curr_time = time(nullptr); + localtime_r(&curr_time, &localtm); + snprintf(crash_path, + sizeof(crash_path), + "%s/crash-%4d-%02d-%02d-%02d-%02d-%02d.%d.log", + lnav_log_crash_dir, + localtm.tm_year + 1900, + localtm.tm_mon + 1, + localtm.tm_mday, + localtm.tm_hour, + localtm.tm_min, + localtm.tm_sec, + getpid()); + snprintf(latest_crash_path, + sizeof(latest_crash_path), + "%s/latest-crash.log", + lnav_log_crash_dir); + if ((fd = open(crash_path, O_CREAT | O_TRUNC | O_RDWR, 0600)) != -1) { + if (log_ring.lr_frag_start < (off_t) BUFFER_SIZE) { + (void) write(fd, + &log_ring.lr_data[log_ring.lr_frag_start], + log_ring.lr_frag_end - log_ring.lr_frag_start); + } + (void) write(fd, log_ring.lr_data, log_ring.lr_length); +#ifdef HAVE_EXECINFO_H + backtrace_symbols_fd(frames, frame_count, fd); +#endif +#if BACKWARD_HAS_DW == 1 + { + ucontext_t* uctx = static_cast(ctx); + void* error_addr = nullptr; + +# ifdef REG_RIP // x86_64 + error_addr + = reinterpret_cast(uctx->uc_mcontext.gregs[REG_RIP]); +# elif defined(REG_EIP) // x86_32 + error_addr + = reinterpret_cast(uctx->uc_mcontext.gregs[REG_EIP]); +# endif + + backward::StackTrace st; + + if (error_addr) { + st.load_from(error_addr, + 32, + reinterpret_cast(uctx), + info->si_addr); + } else { + st.load_here(32, reinterpret_cast(uctx), info->si_addr); + } + backward::TraceResolver tr; + + tr.load_stacktrace(st); + for (size_t lpc = 0; lpc < st.size(); lpc++) { + auto trace = tr.resolve(st[lpc]); + char buf[1024]; + + snprintf(buf, + sizeof(buf), + "Frame %lu:%s:%s (%s:%d)\n", + lpc, + trace.object_filename.c_str(), + trace.object_function.c_str(), + trace.source.filename.c_str(), + trace.source.line); + write(fd, buf, strlen(buf)); + } + } +#endif + log_ring.lr_length = 0; + log_ring.lr_frag_start = BUFFER_SIZE; + log_ring.lr_frag_end = 0; + + log_host_info(); + + for (auto lsd : DUMPER_LIST()) { + lsd->log_state(); + } + + if (log_ring.lr_frag_start < (off_t) BUFFER_SIZE) { + write(fd, + &log_ring.lr_data[log_ring.lr_frag_start], + log_ring.lr_frag_end - log_ring.lr_frag_start); + } + write(fd, log_ring.lr_data, log_ring.lr_length); + if (getenv("DUMP_CRASH") != nullptr) { + char buffer[1024]; + int rc; + + lseek(fd, 0, SEEK_SET); + while ((rc = read(fd, buffer, sizeof(buffer))) > 0) { + write(STDERR_FILENO, buffer, rc); + } + } + close(fd); + + remove(latest_crash_path); + symlink(crash_path, latest_crash_path); + } + + lnav_log_orig_termios | [](auto termios) { + for (auto lcr : CRASH_LIST) { + lcr->log_crash_recover(); + } + + tcsetattr(STDOUT_FILENO, TCSAFLUSH, termios); + dup2(STDOUT_FILENO, STDERR_FILENO); + }; + fprintf(stderr, CRASH_MSG, crash_path); + +#ifndef ATTACH_ON_SIGNAL + if (isatty(STDIN_FILENO)) { + char response; + + fprintf(stderr, "\nWould you like to attach a debugger? (y/N) "); + fflush(stderr); + + if (scanf("%c", &response) > 0 && tolower(response) == 'y') { + pid_t lnav_pid = getpid(); + pid_t child_pid; + + switch ((child_pid = fork())) { + case 0: { + char pid_str[32]; + + snprintf(pid_str, sizeof(pid_str), "--pid=%d", lnav_pid); + execlp("gdb", "gdb", pid_str, nullptr); + + snprintf(pid_str, sizeof(pid_str), "%d", lnav_pid); + execlp("lldb", "lldb", "--attach-pid", pid_str, nullptr); + + fprintf(stderr, "Could not attach gdb or lldb, exiting.\n"); + _exit(1); + break; + } + + case -1: { + break; + } + + default: { + int status; + + while (wait(&status) < 0) { + } + break; + } + } + } + } +#endif + + _exit(1); +} +#pragma GCC diagnostic pop + +void +log_install_handlers() +{ + const size_t stack_size = 8 * 1024 * 1024; + const int sigs[] = { + SIGABRT, + SIGSEGV, + SIGBUS, + SIGILL, + SIGFPE, + }; + static auto_mem stack_content; + + stack_t ss; + + stack_content = malloc(stack_size); + ss.ss_sp = stack_content; + ss.ss_size = stack_size; + ss.ss_flags = 0; + sigaltstack(&ss, nullptr); + for (const auto sig : sigs) { + struct sigaction sa; + + memset(&sa, 0, sizeof(sa)); + sa.sa_flags = SA_SIGINFO | SA_ONSTACK | SA_NODEFER | SA_RESETHAND; + sigfillset(&sa.sa_mask); + sigdelset(&sa.sa_mask, sig); + sa.sa_sigaction = sigabrt; + + sigaction(sig, &sa, nullptr); + } +} + +void +log_abort() +{ + raise(SIGABRT); + _exit(1); +} + +void +log_pipe_err(int fd) +{ + std::thread reader([fd]() { + char buffer[1024]; + bool done = false; + + while (!done) { + int rc = read(fd, buffer, sizeof(buffer)); + + switch (rc) { + case -1: + case 0: + done = true; + break; + default: + while (buffer[rc - 1] == '\n' || buffer[rc - 1] == '\r') { + rc -= 1; + } + + log_error("%.*s", rc, buffer); + break; + } + } + + close(fd); + }); + + reader.detach(); +} + +log_state_dumper::log_state_dumper() +{ + DUMPER_LIST().push_back(this); +} + +log_state_dumper::~log_state_dumper() +{ + auto iter = std::find(DUMPER_LIST().begin(), DUMPER_LIST().end(), this); + if (iter != DUMPER_LIST().end()) { + DUMPER_LIST().erase(iter); + } +} + +log_crash_recoverer::log_crash_recoverer() +{ + CRASH_LIST.push_back(this); +} + +log_crash_recoverer::~log_crash_recoverer() +{ + auto iter = std::find(CRASH_LIST.begin(), CRASH_LIST.end(), this); + + if (iter != CRASH_LIST.end()) { + CRASH_LIST.erase(iter); + } +} diff --git a/src/base/lnav_log.hh b/src/base/lnav_log.hh new file mode 100644 index 0000000..8fd8e36 --- /dev/null +++ b/src/base/lnav_log.hh @@ -0,0 +1,147 @@ +/** + * Copyright (c) 2014, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @file lnav_log.hh + */ + +#ifndef lnav_log_hh +#define lnav_log_hh + +#include + +#include +#include +#include + +#ifndef lnav_dead2 +# define lnav_dead2 __attribute__((noreturn)) +#endif + +#include "optional.hpp" + +struct termios; + +enum class lnav_log_level_t : uint32_t { + TRACE, + DEBUG, + INFO, + WARNING, + ERROR, +}; + +void log_argv(int argc, char* argv[]); +void log_host_info(); +void log_rusage_raw(enum lnav_log_level_t level, + const char* src_file, + int line_number, + const struct rusage& ru); +void log_msg(enum lnav_log_level_t level, + const char* src_file, + int line_number, + const char* fmt, + ...); +void log_msg_extra(const char* fmt, ...); +void log_msg_extra_complete(); +void log_install_handlers(); +void log_abort() lnav_dead2; +void log_pipe_err(int fd); +void log_set_thread_prefix(std::string prefix); + +struct log_state_dumper { +public: + log_state_dumper(); + + virtual ~log_state_dumper(); + + virtual void log_state(){ + + }; + + log_state_dumper(const log_state_dumper&) = delete; + log_state_dumper& operator=(const log_state_dumper&) = delete; +}; + +struct log_crash_recoverer { +public: + log_crash_recoverer(); + + virtual ~log_crash_recoverer(); + + virtual void log_crash_recover() = 0; +}; + +extern nonstd::optional lnav_log_file; +extern const char* lnav_log_crash_dir; +extern nonstd::optional lnav_log_orig_termios; +extern enum lnav_log_level_t lnav_log_level; + +#define log_msg_wrapper(level, fmt...) \ + do { \ + if (lnav_log_level <= level) { \ + log_msg(level, __FILE__, __LINE__, fmt); \ + } \ + } while (false) + +#define log_rusage(level, ru) log_rusage_raw(level, __FILE__, __LINE__, ru); + +#define log_error(fmt...) log_msg_wrapper(lnav_log_level_t::ERROR, fmt); + +#define log_warning(fmt...) log_msg_wrapper(lnav_log_level_t::WARNING, fmt); + +#define log_info(fmt...) log_msg_wrapper(lnav_log_level_t::INFO, fmt); + +#define log_debug(fmt...) log_msg_wrapper(lnav_log_level_t::DEBUG, fmt); + +#define log_trace(fmt...) log_msg_wrapper(lnav_log_level_t::TRACE, fmt); + +#define require(e) ((void) ((e) ? 0 : lnav_require(#e, __FILE__, __LINE__))) +#define lnav_require(e, file, line) \ + (log_msg( \ + lnav_log_level_t::ERROR, file, line, "failed precondition `%s'", e), \ + log_abort(), \ + 1) + +#define ensure(e) ((void) ((e) ? 0 : lnav_ensure(#e, __FILE__, __LINE__))) +#define lnav_ensure(e, file, line) \ + (log_msg( \ + lnav_log_level_t::ERROR, file, line, "failed postcondition `%s'", e), \ + log_abort(), \ + 1) + +#define log_perror(e) \ + ((void) ((e != -1) ? 0 : lnav_log_perror(#e, __FILE__, __LINE__))) +#define lnav_log_perror(e, file, line) \ + (log_msg(lnav_log_level_t::ERROR, \ + file, \ + line, \ + "syscall failed `%s' -- %s", \ + e, \ + strerror(errno)), \ + 1) + +#endif diff --git a/src/base/log_level_enum.hh b/src/base/log_level_enum.hh new file mode 100644 index 0000000..983fd5c --- /dev/null +++ b/src/base/log_level_enum.hh @@ -0,0 +1,65 @@ +/** + * Copyright (c) 2020, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef lnav_log_level_enum_hh +#define lnav_log_level_enum_hh + +/** + * The logging level identifiers for a line(s). + */ +enum log_level_t : int { + LEVEL_UNKNOWN, + LEVEL_TRACE, + LEVEL_DEBUG5, + LEVEL_DEBUG4, + LEVEL_DEBUG3, + LEVEL_DEBUG2, + LEVEL_DEBUG, + LEVEL_INFO, + LEVEL_STATS, + LEVEL_NOTICE, + LEVEL_WARNING, + LEVEL_ERROR, + LEVEL_CRITICAL, + LEVEL_FATAL, + LEVEL_INVALID, + + LEVEL__MAX, + + LEVEL_IGNORE = 0x10, /*< Ignore */ + LEVEL_TIME_SKEW = 0x20, /*< Received after timestamp. */ + LEVEL_MARK = 0x40, /*< Bookmarked line. */ + LEVEL_CONTINUED = 0x80, /*< Continuation of multiline entry. */ + + /** Mask of flags for the level field. */ + LEVEL__FLAGS + = (LEVEL_IGNORE | LEVEL_TIME_SKEW | LEVEL_MARK | LEVEL_CONTINUED) +}; + +#endif diff --git a/src/base/lrucache.hpp b/src/base/lrucache.hpp new file mode 100644 index 0000000..8bcbad6 --- /dev/null +++ b/src/base/lrucache.hpp @@ -0,0 +1,83 @@ +/* + * File: lrucache.hpp + * Author: Alexander Ponomarev + * + * Created on June 20, 2013, 5:09 PM + */ + +#ifndef _LRUCACHE_HPP_INCLUDED_ +#define _LRUCACHE_HPP_INCLUDED_ + +#include +#include +#include +#include + +#include "optional.hpp" + +namespace cache { + +template +class lru_cache { +public: + typedef typename std::pair key_value_pair_t; + typedef typename std::list::iterator list_iterator_t; + + lru_cache(size_t max_size) : + _max_size(max_size) { + } + + void put(const key_t& key, const value_t& value) { + auto it = _cache_items_map.find(key); + _cache_items_list.push_front(key_value_pair_t(key, value)); + if (it != _cache_items_map.end()) { + _cache_items_list.erase(it->second); + _cache_items_map.erase(it); + } + _cache_items_map[key] = _cache_items_list.begin(); + + if (_cache_items_map.size() > _max_size) { + auto last = _cache_items_list.end(); + last--; + _cache_items_map.erase(last->first); + _cache_items_list.pop_back(); + } + } + + nonstd::optional get(const key_t& key) { + auto it = _cache_items_map.find(key); + if (it == _cache_items_map.end()) { + return nonstd::nullopt; + } + + _cache_items_list.splice(_cache_items_list.begin(), _cache_items_list, it->second); + return it->second->second; + } + + bool exists(const key_t& key) const { + return _cache_items_map.find(key) != _cache_items_map.end(); + } + + size_t size() const { + return _cache_items_map.size(); + } + + void set_max_size(size_t max_size) { + this->_max_size = max_size; + } + + void clear() { + this->_cache_items_map.clear(); + this->_cache_items_list.clear(); + } + +private: + std::list _cache_items_list; + std::map _cache_items_map; + size_t _max_size; +}; + +} // namespace cache + +#endif /* _LRUCACHE_HPP_INCLUDED_ */ + diff --git a/src/base/math_util.hh b/src/base/math_util.hh new file mode 100644 index 0000000..842b319 --- /dev/null +++ b/src/base/math_util.hh @@ -0,0 +1,73 @@ +/** + * Copyright (c) 2020, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef lnav_math_util_hh +#define lnav_math_util_hh + +#include + +#undef rounddown + +/** + * Round down a number based on a given granularity. + * + * @param + * @param step The granularity. + */ +template +inline int +rounddown(Size size, Step step) +{ + return size - (size % step); +} + +inline int +rounddown_offset(size_t size, int step, int offset) +{ + return size - ((size - offset) % step); +} + +inline size_t +roundup_size(size_t size, int step) +{ + size_t retval = size + step; + + retval -= (retval % step); + + return retval; +} + +template +T +abs_diff(T a, T b) +{ + return a > b ? a - b : b - a; +} + +#endif diff --git a/src/base/network.tcp.cc b/src/base/network.tcp.cc new file mode 100644 index 0000000..0194b6f --- /dev/null +++ b/src/base/network.tcp.cc @@ -0,0 +1,75 @@ +/** + * Copyright (c) 2021, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "network.tcp.hh" + +#include +#include +#include + +#include "auto_mem.hh" +#include "config.h" +#include "fmt/format.h" + +namespace network { +namespace tcp { + +Result +connect(const char* hostname, const char* servname) +{ + struct addrinfo hints; + auto_mem ai(freeaddrinfo); + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + auto rc = getaddrinfo(hostname, servname, &hints, ai.out()); + + if (rc != 0) { + return Err(fmt::format(FMT_STRING("unable to resolve {}:{} -- {}"), + hostname, + servname, + gai_strerror(rc))); + } + + auto retval = auto_fd(socket(ai->ai_family, ai->ai_socktype, 0)); + + rc = ::connect(retval, ai->ai_addr, ai->ai_addrlen); + if (rc != 0) { + return Err(fmt::format(FMT_STRING("unable to connect to {}:{} -- {}"), + hostname, + servname, + strerror(rc))); + } + + return Ok(std::move(retval)); +} + +} // namespace tcp +} // namespace network diff --git a/src/base/network.tcp.hh b/src/base/network.tcp.hh new file mode 100644 index 0000000..49fc392 --- /dev/null +++ b/src/base/network.tcp.hh @@ -0,0 +1,80 @@ +/** + * Copyright (c) 2021, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef lnav_network_tcp_hh +#define lnav_network_tcp_hh + +#include + +#include "auto_fd.hh" +#include "result.h" + +namespace network { + +struct locality { + locality(nonstd::optional username, + std::string hostname, + nonstd::optional service) + : l_username(std::move(username)), l_hostname(std::move(hostname)), + l_service(std::move(service)) + { + } + + nonstd::optional l_username; + std::string l_hostname; + nonstd::optional l_service; +}; + +struct path { + locality p_locality; + std::string p_path; + + path(locality loc, std::string path) + : p_locality(std::move(loc)), p_path(std::move(path)) + { + } + + path home() const + { + return { + this->p_locality, + ".", + }; + } +}; + +namespace tcp { + +Result connect(const char* hostname, + const char* servname); + +} +} // namespace network + +#endif diff --git a/src/base/network.tcp.tests.cc b/src/base/network.tcp.tests.cc new file mode 100644 index 0000000..76846ca --- /dev/null +++ b/src/base/network.tcp.tests.cc @@ -0,0 +1,50 @@ +/** + * Copyright (c) 2021, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include "config.h" +#include "doctest/doctest.h" +#include "network.tcp.hh" + +TEST_CASE("bad hostname") +{ + auto connect_res = network::tcp::connect("foobar.bazzer", "http"); + CHECK(connect_res.unwrapErr() == + "unable to resolve foobar.bazzer:http -- nodename nor servname " + "provided, or not known"); +} + +TEST_CASE("bad servname") +{ + auto connect_res = network::tcp::connect("www.cnn.com", "non-existent"); + CHECK(connect_res.unwrapErr() == + "unable to resolve www.cnn.com:non-existent -- nodename nor " + "servname provided, or not known"); +} diff --git a/src/base/opt_util.hh b/src/base/opt_util.hh new file mode 100644 index 0000000..aea038e --- /dev/null +++ b/src/base/opt_util.hh @@ -0,0 +1,105 @@ +/** + * Copyright (c) 2019, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef lnav_opt_util_hh +#define lnav_opt_util_hh + +#include + +#include "optional.hpp" + +namespace detail { + +template +typename std::enable_if::value, T>::type +void_or_nullopt() +{ + return; +} + +template +typename std::enable_if::value, T>::type +void_or_nullopt() +{ + return nonstd::nullopt; +} + +template +struct is_optional : std::false_type { +}; + +template +struct is_optional> : std::true_type { +}; +} // namespace detail + +template>::value, int> = 0> +auto +operator|(T&& t, F f) + -> decltype(detail::void_or_nullopt(t). + operator*()))>()) +{ + using return_type = decltype(f(std::forward(t).operator*())); + if (t) + return f(std::forward(t).operator*()); + else + return detail::void_or_nullopt(); +} + +template +optional_constexpr nonstd::optional::type> +make_optional_from_nullable(T&& v) +{ + if (v != nullptr) { + return nonstd::optional::type>( + std::forward(v)); + } + return nonstd::nullopt; +} + +template class C, typename T> +nonstd::optional +cget(const C& container, size_t index) +{ + if (index < container.size()) { + return container[index]; + } + + return nonstd::nullopt; +} + +inline nonstd::optional +getenv_opt(const char* name) +{ + return make_optional_from_nullable(getenv(name)); +} + +#endif diff --git a/src/base/paths.cc b/src/base/paths.cc new file mode 100644 index 0000000..ca53a07 --- /dev/null +++ b/src/base/paths.cc @@ -0,0 +1,130 @@ +/** + * Copyright (c) 2021, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#ifdef __CYGWIN__ +# include +# include +#endif + +#include "fmt/format.h" +#include "paths.hh" + +namespace lnav { +namespace paths { + +#ifdef __CYGWIN__ +char* +windows_to_unix_file_path(char* input) +{ + if (input == nullptr) { + return nullptr; + } + std::string file_path; + file_path.assign(input); + + // Replace the slashes + std::replace(file_path.begin(), + file_path.end(), + WINDOWS_FILE_PATH_SEPARATOR, + UNIX_FILE_PATH_SEPARATOR); + + // Convert the drive letter to lowercase + std::transform( + file_path.begin(), + file_path.begin() + 1, + file_path.begin(), + [](unsigned char character) { return std::tolower(character); }); + + // Remove the colon + const auto drive_letter = file_path.substr(0, 1); + const auto remaining_path = file_path.substr(2, file_path.size() - 2); + file_path = drive_letter + remaining_path; + + std::stringstream stringstream; + stringstream << "/cygdrive/"; + stringstream << file_path; + + return const_cast(stringstream.str().c_str()); +} +#endif + +ghc::filesystem::path +dotlnav() +{ +#ifdef __CYGWIN__ + auto home_env = windows_to_unix_file_path(getenv("APPDATA")); +#else + auto home_env = getenv("HOME"); +#endif + auto xdg_config_home = getenv("XDG_CONFIG_HOME"); + + if (home_env != nullptr) { + auto home_path = ghc::filesystem::path(home_env); + + if (ghc::filesystem::is_directory(home_path)) { + auto home_lnav = home_path / ".lnav"; + + if (ghc::filesystem::is_directory(home_lnav)) { + return home_lnav; + } + + if (xdg_config_home != nullptr) { + auto xdg_path = ghc::filesystem::path(xdg_config_home); + + if (ghc::filesystem::is_directory(xdg_path)) { + return xdg_path / "lnav"; + } + } + + auto home_config = home_path / ".config"; + + if (ghc::filesystem::is_directory(home_config)) { + return home_config / "lnav"; + } + + return home_lnav; + } + } + + return ghc::filesystem::current_path(); +} + +ghc::filesystem::path +workdir() +{ + auto subdir_name = fmt::format(FMT_STRING("lnav-user-{}-work"), getuid()); + auto tmp_path = ghc::filesystem::temp_directory_path(); + + return tmp_path / ghc::filesystem::path(subdir_name); +} + +} // namespace paths +} // namespace lnav diff --git a/src/base/paths.hh b/src/base/paths.hh new file mode 100644 index 0000000..6c43a2a --- /dev/null +++ b/src/base/paths.hh @@ -0,0 +1,58 @@ +/** + * Copyright (c) 2021, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef lnav_paths_hh +#define lnav_paths_hh + +#include "ghc/filesystem.hpp" + +namespace lnav { +namespace paths { + +#ifdef __CYGWIN__ +static const char WINDOWS_FILE_PATH_SEPARATOR = '\\'; +static const char UNIX_FILE_PATH_SEPARATOR = '/'; + +char* windows_to_unix_file_path(char* input); +#endif + +/** + * Compute the path to a file in the user's '.lnav' directory. + * + * @param sub The path to the file in the '.lnav' directory. + * @return The full path + */ +ghc::filesystem::path dotlnav(); + +ghc::filesystem::path workdir(); + +} // namespace paths +} // namespace lnav + +#endif diff --git a/src/base/result.h b/src/base/result.h new file mode 100644 index 0000000..27500de --- /dev/null +++ b/src/base/result.h @@ -0,0 +1,983 @@ +/* + Mathieu Stefani, 03 mai 2016 + + This header provides a Result type that can be used to replace exceptions in code + that has to handle error. + + Result can be used to return and propagate an error to the caller. Result is an algebraic + data type that can either Ok(T) to represent success or Err(E) to represent an error. +*/ + +#pragma once + +#include +#include +#include +#include + +namespace types { + template + struct Ok { + Ok(const T& val) : val(val) { } + Ok(T&& val) : val(std::move(val)) { } + + T val; + }; + + template<> + struct Ok { }; + + template + struct Err { + Err(const E& val) : val(val) { } + Err(E&& val) : val(std::move(val)) { } + + E val; + }; + + template<> + struct Err { }; +}; + +template::type> +types::Ok Ok(T&& val) { + return types::Ok(std::forward(val)); +} + +inline types::Ok Ok() { + return {}; +} + +template::type> +types::Err Err(E&& val) { + return types::Err(std::forward(val)); +} + +inline types::Err Err() { + return {}; +} + +template struct Result; + +namespace details { + +template struct void_t { typedef void type; }; + +namespace impl { + template struct result_of; + + template + struct result_of : public result_of { }; + + template + struct result_of> { + typedef Ret type; + }; +} + +template +struct result_of : public impl::result_of { }; + +template +struct result_of { + typedef Ret type; +}; + +template +struct result_of { + typedef Ret type; +}; + +template +struct ResultOkType { typedef typename std::decay::type type; }; + +template +struct ResultOkType> { + typedef T type; +}; + +template +struct ResultErrType { typedef R type; }; + +template +struct ResultErrType> { + typedef typename std::remove_reference::type type; +}; + +template struct IsResult : public std::false_type { }; +template +struct IsResult> : public std::true_type { }; + +namespace ok { + +namespace impl { + +template struct Map; + +template +struct Map : public Map { }; + +template +struct Map : public Map { }; + +// General implementation +template +struct Map { + + static_assert(!IsResult::value, + "Can not map a callback returning a Result, use then instead"); + + template + static Result map(const Result& result, Func func) { + + static_assert( + std::is_same::value || + std::is_convertible::value, + "Incompatible types detected"); + + if (result.isOk()) { + auto res = func(result.storage().template get()); + return types::Ok(std::move(res)); + } + + return types::Err(result.storage().template get()); + } +}; + +// Specialization for callback returning void +template +struct Map { + + template + static Result map(const Result& result, Func func) { + + if (result.isOk()) { + func(result.storage().template get()); + return types::Ok(); + } + + return types::Err(result.storage().template get()); + } +}; + +// Specialization for a void Result +template +struct Map { + + template + static Result map(const Result& result, Func func) { + static_assert(std::is_same::value, + "Can not map a void callback on a non-void Result"); + + if (result.isOk()) { + auto ret = func(); + return types::Ok(std::move(ret)); + } + + return types::Err(result.storage().template get()); + } +}; + +// Specialization for callback returning void on a void Result +template<> +struct Map { + + template + static Result map(const Result& result, Func func) { + static_assert(std::is_same::value, + "Can not map a void callback on a non-void Result"); + + if (result.isOk()) { + func(); + return types::Ok(); + } + + return types::Err(result.storage().template get()); + } +}; + +// General specialization for a callback returning a Result +template +struct Map (Arg)> { + + template + static Result map(const Result& result, Func func) { + static_assert( + std::is_same::value || + std::is_convertible::value, + "Incompatible types detected"); + + if (result.isOk()) { + auto res = func(result.storage().template get()); + return res; + } + + return types::Err(result.storage().template get()); + } +}; + +// Specialization for a void callback returning a Result +template +struct Map (void)> { + + template + static Result map(const Result& result, Func func) { + static_assert(std::is_same::value, "Can not call a void-callback on a non-void Result"); + + if (result.isOk()) { + auto res = func(); + return res; + } + + return types::Err(result.storage().template get()); + } + +}; + +} // namespace impl + +template struct Map; + +template +struct Map : public impl::Map { }; + +template +struct Map : public impl::Map { }; + +template +struct Map : public impl::Map { }; + +template +struct Map : public impl::Map { }; + +template +struct Map> : public impl::Map { }; + +} // namespace ok + + +namespace err { + +namespace impl { + +template struct Map; + +template +struct Map { + + static_assert(!IsResult::value, + "Can not map a callback returning a Result, use orElse instead"); + + template + static Result map(const Result& result, Func func) { + if (result.isErr()) { + auto res = func(result.storage().template get()); + return types::Err(res); + } + + return types::Ok(result.storage().template get()); + } + + template + static Result map(const Result& result, Func func) { + if (result.isErr()) { + auto res = func(result.storage().template get()); + return types::Err(res); + } + + return types::Ok(); + } + + +}; + +} // namespace impl + +template struct Map : public impl::Map { }; + +} // namespace err; + +namespace And { + +namespace impl { + + template struct Then; + + template + struct Then : public Then { }; + + template + struct Then : public Then { }; + + template + struct Then : public Then { }; + + template + struct Then { + static_assert(std::is_same::value, + "then() should not return anything, use map() instead"); + + template + static Result then(const Result& result, Func func) { + if (result.isOk()) { + func(result.storage().template get()); + } + return result; + } + }; + + template + struct Then { + static_assert(std::is_same::value, + "then() should not return anything, use map() instead"); + + template + static Result then(const Result& result, Func func) { + static_assert(std::is_same::value, "Can not call a void-callback on a non-void Result"); + + if (result.isOk()) { + func(); + } + + return result; + } + }; + + +} // namespace impl + +template +struct Then : public impl::Then { }; + +template +struct Then : public impl::Then { }; + +template +struct Then : public impl::Then { }; + +template +struct Then : public impl::Then { }; + +template +struct Then : public impl::Then { }; + +template +struct Then> : public impl::Then { }; + +} // namespace And + +namespace Or { + +namespace impl { + + template struct Else; + + template + struct Else : public Else { }; + + template + struct Else : public Else { }; + + template + struct Else : public Else { }; + + template + struct Else (Arg)> { + + template + static Result orElse(const Result& result, Func func) { + static_assert( + std::is_same::value || + std::is_convertible::value, + "Incompatible types detected"); + + if (result.isErr()) { + auto res = func(result.storage().template get()); + return res; + } + + return types::Ok(result.storage().template get()); + } + + template + static Result orElse(const Result& result, Func func) { + if (result.isErr()) { + auto res = func(result.storage().template get()); + return res; + } + + return types::Ok(); + } + + }; + + template + struct Else (void)> { + + template + static Result orElse(const Result& result, Func func) { + static_assert(std::is_same::value, + "Can not call a void-callback on a non-void Result"); + + if (result.isErr()) { + auto res = func(); + return res; + } + + return types::Ok(result.storage().template get()); + } + + template + static Result orElse(const Result& result, Func func) { + if (result.isErr()) { + auto res = func(); + return res; + } + + return types::Ok(); + } + + }; + +} // namespace impl + +template +struct Else : public impl::Else { }; + +template +struct Else : public impl::Else { }; + +template +struct Else : public impl::Else { }; + +template +struct Else : public impl::Else { }; + +} // namespace Or + +namespace Other { + +namespace impl { + + template struct Wise; + + template + struct Wise : public Wise { }; + + template + struct Wise : public Wise { }; + + template + struct Wise : public Wise { }; + + template + struct Wise { + + template + static Result otherwise(const Result& result, Func func) { + static_assert( + std::is_same::value || + std::is_convertible::value, + "Incompatible types detected"); + + static_assert(std::is_same::value, + "callback should not return anything, use mapError() for that"); + + if (result.isErr()) { + func(result.storage().template get()); + } + return result; + } + + }; + +} // namespace impl + +template +struct Wise : public impl::Wise { }; + +template +struct Wise : public impl::Wise { }; + +template +struct Wise : public impl::Wise { }; + +template +struct Wise : public impl::Wise { }; + +} // namespace Other + +template +decltype(auto) map(const Result& result, Func func) { + return ok::Map::map(result, func); +} + +template::type + >::type + > + > +Ret mapError(const Result& result, Func func) { + return err::Map::map(result, func); +} + +template +Result then(const Result& result, Func func) { + return And::Then::then(result, func); +} + +template +Result otherwise(const Result& result, Func func) { + return Other::Wise::otherwise(result, func); +} + +template::type + >::type + > +> +Ret orElse(const Result& result, Func func) { + return Or::Else::orElse(result, func); +} + +struct ok_tag { }; +struct err_tag { }; + +template +struct Storage { + static constexpr size_t Size = sizeof(T) > sizeof(E) ? sizeof(T) : sizeof(E); + static constexpr size_t Align = sizeof(T) > sizeof(E) ? alignof(T) : alignof(E); + + typedef typename std::aligned_storage::type type; + + Storage() + : initialized_(false) + { } + + void construct(types::Ok ok) + { + new (&storage_) T(std::move(ok.val)); + initialized_ = true; + } + void construct(types::Err err) + { + new (&storage_) E(err.val); + initialized_ = true; + } + + template + void rawConstruct(U&& val) { + typedef typename std::decay::type CleanU; + + new (&storage_) CleanU(std::forward(val)); + initialized_ = true; + } + + template + const U& get() const { + return *reinterpret_cast(&storage_); + } + + template + U& get() { + return *reinterpret_cast(&storage_); + } + + void destroy(ok_tag) { + if (initialized_) { + get().~T(); + initialized_ = false; + } + } + + void destroy(err_tag) { + if (initialized_) { + get().~E(); + initialized_ = false; + } + } + + type storage_; + bool initialized_; +}; + +template +struct Storage { + typedef typename std::aligned_storage::type type; + + void construct(types::Ok) + { + initialized_ = true; + } + + void construct(types::Err err) + { + new (&storage_) E(err.val); + initialized_ = true; + } + + template + void rawConstruct(U&& val) { + typedef typename std::decay::type CleanU; + + new (&storage_) CleanU(std::forward(val)); + initialized_ = true; + } + + void destroy(ok_tag) { initialized_ = false; } + void destroy(err_tag) { + if (initialized_) { + get().~E(); initialized_ = false; + } + } + + template::value>> + const U& get() const { + return *reinterpret_cast(&storage_); + } + + template::value>> + typename std::add_lvalue_reference::type get() { + return *reinterpret_cast(&storage_); + } + + template::value>> + void get() {} + + type storage_; + bool initialized_; +}; + +template +struct Constructor { + + static void move(Storage&& src, Storage& dst, ok_tag) { + dst.rawConstruct(std::move(src.template get())); + src.destroy(ok_tag()); + } + + static void copy(const Storage& src, Storage& dst, ok_tag) { + dst.rawConstruct(src.template get()); + } + + static void move(Storage&& src, Storage& dst, err_tag) { + dst.rawConstruct(std::move(src.template get())); + src.destroy(err_tag()); + } + + static void copy(const Storage& src, Storage& dst, err_tag) { + dst.rawConstruct(src.template get()); + } +}; + +template +struct Constructor { + static void move(Storage&& src, Storage& dst, ok_tag) { + } + + static void copy(const Storage& src, Storage& dst, ok_tag) { + } + + static void move(Storage&& src, Storage& dst, err_tag) { + dst.rawConstruct(std::move(src.template get())); + src.destroy(err_tag()); + } + + static void copy(const Storage& src, Storage& dst, err_tag) { + dst.rawConstruct(src.template get()); + } +}; + +} // namespace details + +namespace concept { + +template struct EqualityComparable : std::false_type { }; + +template +struct EqualityComparable() == std::declval())>::type + >::type +> : std::true_type +{ +}; + + +} // namespace concept + +template +struct Result { + + static_assert(!std::is_same::value, "void error type is not allowed"); + + typedef details::Storage storage_type; + + Result(types::Ok ok) + : ok_(true) + { + storage_.construct(std::move(ok)); + } + + Result(types::Err err) + : ok_(false) + { + storage_.construct(std::move(err)); + } + + Result(Result&& other) { + if (other.isOk()) { + details::Constructor::move(std::move(other.storage_), storage_, details::ok_tag()); + ok_ = true; + } else { + details::Constructor::move(std::move(other.storage_), storage_, details::err_tag()); + ok_ = false; + } + } + + Result(const Result& other) { + if (other.isOk()) { + details::Constructor::copy(other.storage_, storage_, details::ok_tag()); + ok_ = true; + } else { + details::Constructor::copy(other.storage_, storage_, details::err_tag()); + ok_ = false; + } + } + + ~Result() { + if (ok_) + storage_.destroy(details::ok_tag()); + else + storage_.destroy(details::err_tag()); + } + + bool isOk() const { + return ok_; + } + + bool isErr() const { + return !ok_; + } + + T expect(const char* str) + { + if (!isOk()) { + ::fprintf(stderr, "%s\n", str); + abort(); + } + return expect_impl(std::is_same()); + } + + template + auto map(Func func) + { + using return_type = decltype(func(T{})); + + if (this->isOk()) { + auto value = std::move(this->storage().template get()); + auto res = func(std::move(value)); + return Result( + types::Ok(std::move(res))); + } + + return Result( + types::Err(this->storage().template get())); + } + + template::type + >::type + > + > + Ret mapError(Func func) const { + return details::mapError(*this, func); + } + + template + Result then(Func func) { + if (this->isOk()) { + func(std::move(this->storage().template get())); + + return Ok(); + } + + return Err(std::move(this->storage().template get())); + } + + template + Result::type, E> then(Func func) { + if (this->isOk()) { + return Ok(func(std::move(this->storage().template get()))); + } + + return Err(std::move(this->storage().template get())); + } + + template + void otherwise(Func func) { + if (this->isOk()) { + return; + } + + func(std::move(this->storage().template get())); + } + + template::type + >::type + > + > + Ret orElse(Func func) const { + return details::orElse(*this, func); + } + + storage_type& storage() { + return storage_; + } + + const storage_type& storage() const { + return storage_; + } + + template + typename std::enable_if< + !std::is_same::value, + T + >::type + unwrapOr(const U& defaultValue) const { + if (isOk()) { + return storage().template get(); + } + return defaultValue; + } + + template + auto unwrapOrElse(Func func) const { + if (isOk()) { + return storage().template get(); + } + return func(this->storage().template get()); + } + + template + typename std::enable_if< + !std::is_same::value, + U + >::type + unwrap() const { + if (isOk()) { + return std::move(storage().template get()); + } + + ::fprintf(stderr, "Attempting to unwrap an error Result\n"); + abort(); + } + + template + typename std::enable_if< + !std::is_same::value, + U + >::type + unwrap() { + if (isOk()) { + return std::move(storage().template get()); + } + + ::fprintf(stderr, "Attempting to unwrap an error Result\n"); + abort(); + } + + template + typename std::enable_if::value, U>::type unwrap() + const + { + if (isOk()) { + return; + } + + ::fprintf(stderr, "Attempting to unwrap an error Result\n"); + abort(); + } + + E unwrapErr() const + { + if (isErr()) { + return storage().template get(); + } + + ::fprintf(stderr, "Attempting to unwrapErr an ok Result\n"); + abort(); + } + +private: + T expect_impl(std::true_type) const {} + T expect_impl(std::false_type) + { + return std::move(storage_.template get()); + } + + bool ok_; + storage_type storage_; +}; + +template +bool operator==(const Result& lhs, const Result& rhs) { + static_assert(concept::EqualityComparable::value, "T must be EqualityComparable for Result to be comparable"); + static_assert(concept::EqualityComparable::value, "E must be EqualityComparable for Result to be comparable"); + + if (lhs.isOk() && rhs.isOk()) { + return lhs.storage().template get() == rhs.storage().template get(); + } + if (lhs.isErr() && rhs.isErr()) { + return lhs.storage().template get() == rhs.storage().template get(); + } +} + +template +bool operator==(const Result& lhs, types::Ok ok) { + static_assert(concept::EqualityComparable::value, "T must be EqualityComparable for Result to be comparable"); + + if (!lhs.isOk()) return false; + + return lhs.storage().template get() == ok.val; +} + +template +bool operator==(const Result& lhs, types::Ok) { + return lhs.isOk(); +} + +template +bool operator==(const Result& lhs, types::Err err) { + static_assert(concept::EqualityComparable::value, "E must be EqualityComparable for Result to be comparable"); + if (!lhs.isErr()) return false; + + return lhs.storage().template get() == err.val; +} + +#define TRY(...) \ + ({ \ + auto res = __VA_ARGS__; \ + if (!res.isOk()) { \ + typedef typename ::details::ResultErrType::type E; \ + return ::types::Err(res.storage().template get()); \ + } \ + res.unwrap(); \ + }) diff --git a/src/base/snippet_highlighters.cc b/src/base/snippet_highlighters.cc new file mode 100644 index 0000000..058fa41 --- /dev/null +++ b/src/base/snippet_highlighters.cc @@ -0,0 +1,344 @@ +/** + * Copyright (c) 2022, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "snippet_highlighters.hh" + +#include "attr_line.builder.hh" +#include "pcrepp/pcre2pp.hh" +#include "view_curses.hh" + +namespace lnav { +namespace snippets { + +static bool +is_bracket(const std::string& str, int index, bool is_lit) +{ + if (index == 0) { + return true; + } + + if (is_lit && str[index - 1] == '\\') { + return true; + } + if (!is_lit && str[index - 1] != '\\') { + return true; + } + return false; +} + +static void +find_matching_bracket( + attr_line_t& al, int x, line_range sub, char left, char right) +{ + bool is_lit = (left == 'Q'); + attr_line_builder alb(al); + const auto& line = al.get_string(); + int depth = 0; + + if (x < sub.lr_start || x > sub.lr_end) { + return; + } + + if (line[x] == right && is_bracket(line, x, is_lit)) { + for (int lpc = x - 1; lpc >= sub.lr_start; lpc--) { + if (line[lpc] == right && is_bracket(line, lpc, is_lit)) { + depth += 1; + } else if (line[lpc] == left && is_bracket(line, lpc, is_lit)) { + if (depth == 0) { + alb.overlay_attr_for_char( + lpc, VC_STYLE.value(text_attrs{A_BOLD | A_REVERSE})); + alb.overlay_attr_for_char(lpc, + VC_ROLE.value(role_t::VCR_OK)); + break; + } + depth -= 1; + } + } + } + + if (line[x] == left && is_bracket(line, x, is_lit)) { + for (int lpc = x + 1; lpc < sub.lr_end; lpc++) { + if (line[lpc] == left && is_bracket(line, lpc, is_lit)) { + depth += 1; + } else if (line[lpc] == right && is_bracket(line, lpc, is_lit)) { + if (depth == 0) { + alb.overlay_attr_for_char( + lpc, VC_STYLE.value(text_attrs{A_BOLD | A_REVERSE})); + alb.overlay_attr_for_char(lpc, + VC_ROLE.value(role_t::VCR_OK)); + break; + } + depth -= 1; + } + } + } + + nonstd::optional first_left; + + depth = 0; + + for (auto lpc = sub.lr_start; lpc < sub.lr_end; lpc++) { + if (line[lpc] == left && is_bracket(line, lpc, is_lit)) { + depth += 1; + if (!first_left) { + first_left = lpc; + } + } else if (line[lpc] == right && is_bracket(line, lpc, is_lit)) { + if (depth > 0) { + depth -= 1; + } else { + auto lr = line_range(is_lit ? lpc - 1 : lpc, lpc + 1); + alb.overlay_attr( + lr, VC_STYLE.value(text_attrs{A_BOLD | A_REVERSE})); + alb.overlay_attr(lr, VC_ROLE.value(role_t::VCR_ERROR)); + } + } + } + + if (depth > 0) { + auto lr + = line_range(is_lit ? first_left.value() - 1 : first_left.value(), + first_left.value() + 1); + alb.overlay_attr(lr, VC_STYLE.value(text_attrs{A_BOLD | A_REVERSE})); + alb.overlay_attr(lr, VC_ROLE.value(role_t::VCR_ERROR)); + } +} + +static bool +check_re_prev(const std::string& line, int x) +{ + bool retval = false; + + if ((x > 0 && line[x - 1] != ')' && line[x - 1] != ']' && line[x - 1] != '*' + && line[x - 1] != '?' && line[x - 1] != '+') + && (x < 2 || line[x - 2] != '\\')) + { + retval = true; + } + + return retval; +} + +static char +safe_read(const std::string& str, std::string::size_type index) +{ + if (index < str.length()) { + return str.at(index); + } + + return 0; +} + +void +regex_highlighter(attr_line_t& al, int x, line_range sub) +{ + static const char* brackets[] = { + "[]", + "{}", + "()", + "QE", + + nullptr, + }; + + const auto& line = al.get_string(); + attr_line_builder alb(al); + bool backslash_is_quoted = false; + + for (auto lpc = sub.lr_start; lpc < sub.lr_end; lpc++) { + if (lpc == 0 || line[lpc - 1] != '\\') { + switch (line[lpc]) { + case '^': + case '$': + case '*': + case '+': + case '|': + case '.': + alb.overlay_attr_for_char( + lpc, VC_ROLE.value(role_t::VCR_RE_SPECIAL)); + + if ((line[lpc] == '*' || line[lpc] == '+') + && check_re_prev(line, lpc)) + { + alb.overlay_attr_for_char( + lpc - 1, VC_ROLE.value(role_t::VCR_RE_REPEAT)); + } + break; + case '?': { + struct line_range lr(lpc, lpc + 1); + + if (lpc == sub.lr_start || (lpc - sub.lr_start) == 0) { + alb.overlay_attr_for_char( + lpc, + VC_STYLE.value(text_attrs{A_BOLD | A_REVERSE})); + alb.overlay_attr_for_char( + lpc, VC_ROLE.value(role_t::VCR_ERROR)); + } else if (line[lpc - 1] == '(') { + switch (line[lpc + 1]) { + case ':': + case '!': + case '#': + lr.lr_end += 1; + break; + } + alb.overlay_attr(lr, VC_ROLE.value(role_t::VCR_OK)); + if (line[lpc + 1] == '<') { + alb.overlay_attr( + line_range(lpc + 1, lpc + 2), + VC_ROLE.value(role_t::VCR_RE_SPECIAL)); + } + } else { + alb.overlay_attr(lr, + VC_ROLE.value(role_t::VCR_RE_SPECIAL)); + + if (check_re_prev(line, lpc)) { + alb.overlay_attr_for_char( + lpc - 1, VC_ROLE.value(role_t::VCR_RE_REPEAT)); + } + } + break; + } + case '>': { + static const auto CAP_RE + = lnav::pcre2pp::code::from_const(R"(\(\?\<\w+$)"); + + auto capture_start + = string_fragment::from_str_range( + line, sub.lr_start, lpc) + .find_left_boundary(lpc - sub.lr_start - 1, + string_fragment::tag1{'('}); + + auto cap_find_res + = CAP_RE.find_in(capture_start).ignore_error(); + + if (cap_find_res) { + alb.overlay_attr( + line_range(capture_start.sf_begin + + cap_find_res->f_all.sf_begin + 3, + capture_start.sf_begin + + cap_find_res->f_all.sf_end), + VC_ROLE.value(role_t::VCR_IDENTIFIER)); + alb.overlay_attr(line_range(lpc, lpc + 1), + VC_ROLE.value(role_t::VCR_RE_SPECIAL)); + } + break; + } + + case '(': + case ')': + case '{': + case '}': + case '[': + case ']': + alb.overlay_attr_for_char(lpc, + VC_ROLE.value(role_t::VCR_OK)); + break; + } + } + if (lpc > 0 && line[lpc - 1] == '\\') { + if (backslash_is_quoted) { + backslash_is_quoted = false; + continue; + } + switch (line[lpc]) { + case '\\': + backslash_is_quoted = true; + alb.overlay_attr(line_range(lpc - 1, lpc + 1), + VC_ROLE.value(role_t::VCR_RE_SPECIAL)); + break; + case 'd': + case 'D': + case 'h': + case 'H': + case 'N': + case 'R': + case 's': + case 'S': + case 'v': + case 'V': + case 'w': + case 'W': + case 'X': + + case 'A': + case 'b': + case 'B': + case 'G': + case 'Z': + case 'z': + alb.overlay_attr(line_range(lpc - 1, lpc + 1), + VC_ROLE.value(role_t::VCR_SYMBOL)); + break; + case ' ': + alb.overlay_attr( + line_range(lpc - 1, lpc + 1), + VC_STYLE.value(text_attrs{A_BOLD | A_REVERSE})); + alb.overlay_attr(line_range(lpc - 1, lpc + 1), + VC_ROLE.value(role_t::VCR_ERROR)); + break; + case '0': + case 'x': + if (safe_read(line, lpc + 1) == '{') { + alb.overlay_attr(line_range(lpc - 1, lpc + 1), + VC_ROLE.value(role_t::VCR_RE_SPECIAL)); + } else if (isdigit(safe_read(line, lpc + 1)) + && isdigit(safe_read(line, lpc + 2))) + { + alb.overlay_attr(line_range(lpc - 1, lpc + 3), + VC_ROLE.value(role_t::VCR_RE_SPECIAL)); + } else { + alb.overlay_attr( + line_range(lpc - 1, lpc + 1), + VC_STYLE.value(text_attrs{A_BOLD | A_REVERSE})); + alb.overlay_attr(line_range(lpc - 1, lpc + 1), + VC_ROLE.value(role_t::VCR_ERROR)); + } + break; + case 'Q': + case 'E': + alb.overlay_attr(line_range(lpc - 1, lpc + 1), + VC_ROLE.value(role_t::VCR_OK)); + break; + default: + if (isdigit(line[lpc])) { + alb.overlay_attr(line_range(lpc - 1, lpc + 1), + VC_ROLE.value(role_t::VCR_RE_SPECIAL)); + } + break; + } + } + } + + for (int lpc = 0; brackets[lpc]; lpc++) { + find_matching_bracket(al, x, sub, brackets[lpc][0], brackets[lpc][1]); + } +} + +} // namespace snippets +} // namespace lnav diff --git a/src/base/snippet_highlighters.hh b/src/base/snippet_highlighters.hh new file mode 100644 index 0000000..0da6291 --- /dev/null +++ b/src/base/snippet_highlighters.hh @@ -0,0 +1,43 @@ +/** + * Copyright (c) 2022, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef lnav_snippet_highlighters_hh +#define lnav_snippet_highlighters_hh + +#include "attr_line.hh" + +namespace lnav { +namespace snippets { + +void regex_highlighter(attr_line_t& al, int x, line_range sub); + +} // namespace snippets +} // namespace lnav + +#endif diff --git a/src/base/string_attr_type.cc b/src/base/string_attr_type.cc new file mode 100644 index 0000000..9a3950b --- /dev/null +++ b/src/base/string_attr_type.cc @@ -0,0 +1,51 @@ +/** + * Copyright (c) 2020, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "string_attr_type.hh" + +#include "config.h" + +string_attr_type SA_ORIGINAL_LINE("original_line"); +string_attr_type SA_BODY("body"); +string_attr_type SA_HIDDEN("hidden"); +string_attr_type SA_FORMAT("format"); +string_attr_type SA_REMOVED("removed"); +string_attr_type SA_PREFORMATTED("preformatted"); +string_attr_type SA_INVALID("invalid"); +string_attr_type SA_ERROR("error"); +string_attr_type SA_LEVEL("level"); +string_attr_type SA_ORIGIN("origin"); +string_attr_type SA_ORIGIN_OFFSET("origin-offset"); + +string_attr_type VC_ROLE("role"); +string_attr_type VC_ROLE_FG("role-fg"); +string_attr_type VC_STYLE("style"); +string_attr_type VC_GRAPHIC("graphic"); +string_attr_type VC_FOREGROUND("foreground"); +string_attr_type VC_BACKGROUND("background"); diff --git a/src/base/string_attr_type.hh b/src/base/string_attr_type.hh new file mode 100644 index 0000000..fe1b987 --- /dev/null +++ b/src/base/string_attr_type.hh @@ -0,0 +1,670 @@ +/** + * Copyright (c) 2020, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef lnav_string_attr_type_hh +#define lnav_string_attr_type_hh + +#include +#include + +#include + +#include "base/intern_string.hh" +#include "mapbox/variant.hpp" + +class logfile; +struct bookmark_metadata; + +/** Roles that can be mapped to curses attributes using attrs_for_role() */ +enum class role_t : int32_t { + VCR_NONE = -1, + + VCR_TEXT, /*< Raw text. */ + VCR_IDENTIFIER, + VCR_SEARCH, /*< A search hit. */ + VCR_OK, + VCR_INFO, + VCR_ERROR, /*< An error message. */ + VCR_WARNING, /*< A warning message. */ + VCR_ALT_ROW, /*< Highlight for alternating rows in a list */ + VCR_HIDDEN, + VCR_ADJUSTED_TIME, + VCR_SKEWED_TIME, + VCR_OFFSET_TIME, + VCR_INVALID_MSG, + VCR_STATUS, /*< Normal status line text. */ + VCR_WARN_STATUS, + VCR_ALERT_STATUS, /*< Alert status line text. */ + VCR_ACTIVE_STATUS, /*< */ + VCR_ACTIVE_STATUS2, /*< */ + VCR_STATUS_TITLE, + VCR_STATUS_SUBTITLE, + VCR_STATUS_INFO, + VCR_STATUS_STITCH_TITLE_TO_SUB, + VCR_STATUS_STITCH_SUB_TO_TITLE, + VCR_STATUS_STITCH_SUB_TO_NORMAL, + VCR_STATUS_STITCH_NORMAL_TO_SUB, + VCR_STATUS_STITCH_TITLE_TO_NORMAL, + VCR_STATUS_STITCH_NORMAL_TO_TITLE, + VCR_STATUS_TITLE_HOTKEY, + VCR_STATUS_DISABLED_TITLE, + VCR_STATUS_HOTKEY, + VCR_INACTIVE_STATUS, + VCR_INACTIVE_ALERT_STATUS, + VCR_SCROLLBAR, + VCR_SCROLLBAR_ERROR, + VCR_SCROLLBAR_WARNING, + VCR_FOCUSED, + VCR_DISABLED_FOCUSED, + VCR_POPUP, + VCR_COLOR_HINT, + + VCR_QUOTED_CODE, + VCR_CODE_BORDER, + VCR_KEYWORD, + VCR_STRING, + VCR_COMMENT, + VCR_DOC_DIRECTIVE, + VCR_VARIABLE, + VCR_SYMBOL, + VCR_NUMBER, + VCR_RE_SPECIAL, + VCR_RE_REPEAT, + VCR_FILE, + + VCR_DIFF_DELETE, /*< Deleted line in a diff. */ + VCR_DIFF_ADD, /*< Added line in a diff. */ + VCR_DIFF_SECTION, /*< Section marker in a diff. */ + + VCR_LOW_THRESHOLD, + VCR_MED_THRESHOLD, + VCR_HIGH_THRESHOLD, + + VCR_H1, + VCR_H2, + VCR_H3, + VCR_H4, + VCR_H5, + VCR_H6, + + VCR_HR, + VCR_HYPERLINK, + VCR_LIST_GLYPH, + VCR_BREADCRUMB, + VCR_TABLE_BORDER, + VCR_TABLE_HEADER, + VCR_QUOTE_BORDER, + VCR_QUOTED_TEXT, + VCR_FOOTNOTE_BORDER, + VCR_FOOTNOTE_TEXT, + VCR_SNIPPET_BORDER, + + VCR__MAX +}; + +struct text_attrs { + bool empty() const + { + return this->ta_attrs == 0 && !this->ta_fg_color && !this->ta_bg_color; + } + + text_attrs operator|(const text_attrs& other) const + { + return text_attrs{ + this->ta_attrs | other.ta_attrs, + this->ta_fg_color ? this->ta_fg_color : other.ta_fg_color, + this->ta_bg_color ? this->ta_bg_color : other.ta_bg_color, + }; + } + + bool operator==(const text_attrs& other) const + { + return this->ta_attrs == other.ta_attrs && + this->ta_fg_color == other.ta_fg_color && + this->ta_bg_color == other.ta_bg_color; + } + + int32_t ta_attrs{0}; + nonstd::optional ta_fg_color; + nonstd::optional ta_bg_color; +}; + +using string_attr_value = mapbox::util::variant, + bookmark_metadata*, + timespec, + string_fragment>; + +class string_attr_type_base { +public: + explicit string_attr_type_base(const char* name) noexcept : sat_name(name) + { + } + + const char* const sat_name; +}; + +using string_attr_pair + = std::pair; + +template +class string_attr_type : public string_attr_type_base { +public: + using value_type = T; + + explicit string_attr_type(const char* name) noexcept + : string_attr_type_base(name) + { + } + + template + std::enable_if_t::value, string_attr_pair> value( + const U& val) const + { + return std::make_pair(this, val); + } + + template + std::enable_if_t::value, string_attr_pair> value() const + { + return std::make_pair(this, string_attr_value{}); + } +}; + +extern string_attr_type SA_ORIGINAL_LINE; +extern string_attr_type SA_BODY; +extern string_attr_type SA_HIDDEN; +extern string_attr_type SA_FORMAT; +extern string_attr_type SA_REMOVED; +extern string_attr_type SA_PREFORMATTED; +extern string_attr_type SA_INVALID; +extern string_attr_type SA_ERROR; +extern string_attr_type SA_LEVEL; +extern string_attr_type SA_ORIGIN; +extern string_attr_type SA_ORIGIN_OFFSET; + +extern string_attr_type VC_ROLE; +extern string_attr_type VC_ROLE_FG; +extern string_attr_type VC_STYLE; +extern string_attr_type VC_GRAPHIC; +extern string_attr_type VC_FOREGROUND; +extern string_attr_type VC_BACKGROUND; + +namespace lnav { + +namespace string { +namespace attrs { + +template +inline std::pair +preformatted(S str) +{ + return std::make_pair(std::move(str), SA_PREFORMATTED.template value()); +} + +} // namespace attrs +} // namespace string + +namespace roles { + +template +inline std::pair +error(S str) +{ + return std::make_pair(std::move(str), + VC_ROLE.template value(role_t::VCR_ERROR)); +} + +template +inline std::pair +warning(S str) +{ + return std::make_pair(std::move(str), + VC_ROLE.template value(role_t::VCR_WARNING)); +} + +template +inline std::pair +status(S str) +{ + return std::make_pair(std::move(str), + VC_ROLE.template value(role_t::VCR_STATUS)); +} + +template +inline std::pair +inactive_status(S str) +{ + return std::make_pair(std::move(str), + VC_ROLE.template value(role_t::VCR_INACTIVE_STATUS)); +} + +template +inline std::pair +status_title(S str) +{ + return std::make_pair(std::move(str), + VC_ROLE.template value(role_t::VCR_STATUS_TITLE)); +} + +template +inline std::pair +ok(S str) +{ + return std::make_pair(std::move(str), + VC_ROLE.template value(role_t::VCR_OK)); +} + +template +inline std::pair +file(S str) +{ + return std::make_pair(std::move(str), + VC_ROLE.template value(role_t::VCR_FILE)); +} + +template +inline std::pair +symbol(S str) +{ + return std::make_pair(std::move(str), + VC_ROLE.template value(role_t::VCR_SYMBOL)); +} + +template +inline std::pair +keyword(S str) +{ + return std::make_pair(std::move(str), + VC_ROLE.template value(role_t::VCR_KEYWORD)); +} + +template +inline std::pair +variable(S str) +{ + return std::make_pair(std::move(str), + VC_ROLE.template value(role_t::VCR_VARIABLE)); +} + +template +inline std::pair +number(S str) +{ + return std::make_pair(std::move(str), + VC_ROLE.template value(role_t::VCR_NUMBER)); +} + +template +inline std::pair +comment(S str) +{ + return std::make_pair(std::move(str), + VC_ROLE.template value(role_t::VCR_COMMENT)); +} + +template +inline std::pair +identifier(S str) +{ + return std::make_pair(std::move(str), + VC_ROLE.template value(role_t::VCR_IDENTIFIER)); +} + +template +inline std::pair +hr(S str) +{ + return std::make_pair(std::move(str), + VC_ROLE.template value(role_t::VCR_HR)); +} + +template +inline std::pair +hyperlink(S str) +{ + return std::make_pair(std::move(str), + VC_ROLE.template value(role_t::VCR_HYPERLINK)); +} + +template +inline std::pair +list_glyph(S str) +{ + return std::make_pair(std::move(str), + VC_ROLE.template value(role_t::VCR_LIST_GLYPH)); +} + +template +inline std::pair +breadcrumb(S str) +{ + return std::make_pair(std::move(str), + VC_ROLE.template value(role_t::VCR_BREADCRUMB)); +} + +template +inline std::pair +quoted_code(S str) +{ + return std::make_pair(std::move(str), + VC_ROLE.template value(role_t::VCR_QUOTED_CODE)); +} + +template +inline std::pair +code_border(S str) +{ + return std::make_pair(std::move(str), + VC_ROLE.template value(role_t::VCR_CODE_BORDER)); +} + +template +inline std::pair +table_border(S str) +{ + return std::make_pair(std::move(str), + VC_ROLE.template value(role_t::VCR_TABLE_BORDER)); +} + +template +inline std::pair +table_header(S str) +{ + return std::make_pair(std::move(str), + VC_ROLE.template value(role_t::VCR_TABLE_HEADER)); +} + +template +inline std::pair +quote_border(S str) +{ + return std::make_pair(std::move(str), + VC_ROLE.template value(role_t::VCR_QUOTE_BORDER)); +} + +template +inline std::pair +quoted_text(S str) +{ + return std::make_pair(std::move(str), + VC_ROLE.template value(role_t::VCR_QUOTED_TEXT)); +} + +template +inline std::pair +footnote_border(S str) +{ + return std::make_pair(std::move(str), + VC_ROLE.template value(role_t::VCR_FOOTNOTE_BORDER)); +} + +template +inline std::pair +footnote_text(S str) +{ + return std::make_pair(std::move(str), + VC_ROLE.template value(role_t::VCR_FOOTNOTE_TEXT)); +} + +template +inline std::pair +h1(S str) +{ + return std::make_pair(std::move(str), + VC_ROLE.template value(role_t::VCR_H1)); +} + +template +inline std::pair +h2(S str) +{ + return std::make_pair(std::move(str), + VC_ROLE.template value(role_t::VCR_H2)); +} + +template +inline std::pair +h3(S str) +{ + return std::make_pair(std::move(str), + VC_ROLE.template value(role_t::VCR_H3)); +} + +template +inline std::pair +h4(S str) +{ + return std::make_pair(std::move(str), + VC_ROLE.template value(role_t::VCR_H4)); +} + +template +inline std::pair +h5(S str) +{ + return std::make_pair(std::move(str), + VC_ROLE.template value(role_t::VCR_H5)); +} + +template +inline std::pair +h6(S str) +{ + return std::make_pair(std::move(str), + VC_ROLE.template value(role_t::VCR_H6)); +} + +namespace literals { + +inline std::pair operator"" _ok(const char* str, + std::size_t len) +{ + return std::make_pair(std::string(str, len), + VC_ROLE.template value(role_t::VCR_OK)); +} + +inline std::pair operator"" _error( + const char* str, std::size_t len) +{ + return std::make_pair(std::string(str, len), + VC_ROLE.template value(role_t::VCR_ERROR)); +} + +inline std::pair operator"" _info( + const char* str, std::size_t len) +{ + return std::make_pair(std::string(str, len), + VC_ROLE.template value(role_t::VCR_INFO)); +} + +inline std::pair operator"" _symbol( + const char* str, std::size_t len) +{ + return std::make_pair(std::string(str, len), + VC_ROLE.template value(role_t::VCR_SYMBOL)); +} + +inline std::pair operator"" _keyword( + const char* str, std::size_t len) +{ + return std::make_pair(std::string(str, len), + VC_ROLE.template value(role_t::VCR_KEYWORD)); +} + +inline std::pair operator"" _variable( + const char* str, std::size_t len) +{ + return std::make_pair(std::string(str, len), + VC_ROLE.template value(role_t::VCR_VARIABLE)); +} + +inline std::pair operator"" _comment( + const char* str, std::size_t len) +{ + return std::make_pair(std::string(str, len), + VC_ROLE.template value(role_t::VCR_COMMENT)); +} + +inline std::pair operator"" _hotkey( + const char* str, std::size_t len) +{ + return std::make_pair(std::string(str, len), + VC_ROLE.template value(role_t::VCR_STATUS_HOTKEY)); +} + +inline std::pair operator"" _h1(const char* str, + std::size_t len) +{ + return std::make_pair(std::string(str, len), + VC_ROLE.template value(role_t::VCR_H1)); +} + +inline std::pair operator"" _h2(const char* str, + std::size_t len) +{ + return std::make_pair(std::string(str, len), + VC_ROLE.template value(role_t::VCR_H2)); +} + +inline std::pair operator"" _h3(const char* str, + std::size_t len) +{ + return std::make_pair(std::string(str, len), + VC_ROLE.template value(role_t::VCR_H3)); +} + +inline std::pair operator"" _h4(const char* str, + std::size_t len) +{ + return std::make_pair(std::string(str, len), + VC_ROLE.template value(role_t::VCR_H4)); +} + +inline std::pair operator"" _h5(const char* str, + std::size_t len) +{ + return std::make_pair(std::string(str, len), + VC_ROLE.template value(role_t::VCR_H5)); +} + +inline std::pair operator"" _hr(const char* str, + std::size_t len) +{ + return std::make_pair(std::string(str, len), + VC_ROLE.template value(role_t::VCR_HR)); +} + +inline std::pair operator"" _hyperlink( + const char* str, std::size_t len) +{ + return std::make_pair(std::string(str, len), + VC_ROLE.template value(role_t::VCR_HYPERLINK)); +} + +inline std::pair operator"" _list_glyph( + const char* str, std::size_t len) +{ + return std::make_pair(std::string(str, len), + VC_ROLE.template value(role_t::VCR_LIST_GLYPH)); +} + +inline std::pair operator"" _breadcrumb( + const char* str, std::size_t len) +{ + return std::make_pair(std::string(str, len), + VC_ROLE.template value(role_t::VCR_BREADCRUMB)); +} + +inline std::pair operator"" _quoted_code( + const char* str, std::size_t len) +{ + return std::make_pair(std::string(str, len), + VC_ROLE.template value(role_t::VCR_QUOTED_CODE)); +} + +inline std::pair operator"" _code_border( + const char* str, std::size_t len) +{ + return std::make_pair(std::string(str, len), + VC_ROLE.template value(role_t::VCR_CODE_BORDER)); +} + +inline std::pair operator"" _table_border( + const char* str, std::size_t len) +{ + return std::make_pair(std::string(str, len), + VC_ROLE.template value(role_t::VCR_TABLE_BORDER)); +} + +inline std::pair operator"" _quote_border( + const char* str, std::size_t len) +{ + return std::make_pair(std::string(str, len), + VC_ROLE.template value(role_t::VCR_QUOTE_BORDER)); +} + +inline std::pair operator"" _quoted_text( + const char* str, std::size_t len) +{ + return std::make_pair(std::string(str, len), + VC_ROLE.template value(role_t::VCR_QUOTED_TEXT)); +} + +inline std::pair operator"" _footnote_border( + const char* str, std::size_t len) +{ + return std::make_pair(std::string(str, len), + VC_ROLE.template value(role_t::VCR_FOOTNOTE_BORDER)); +} + +inline std::pair operator"" _footnote_text( + const char* str, std::size_t len) +{ + return std::make_pair(std::string(str, len), + VC_ROLE.template value(role_t::VCR_FOOTNOTE_BORDER)); +} + +inline std::pair operator"" _snippet_border( + const char* str, std::size_t len) +{ + return std::make_pair(std::string(str, len), + VC_ROLE.template value(role_t::VCR_SNIPPET_BORDER)); +} + +} // namespace literals + +} // namespace roles +} // namespace lnav + +#endif diff --git a/src/base/string_util.cc b/src/base/string_util.cc new file mode 100644 index 0000000..d4e0795 --- /dev/null +++ b/src/base/string_util.cc @@ -0,0 +1,307 @@ +/** + * Copyright (c) 2019, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +#include "string_util.hh" + +#include "config.h" +#include "is_utf8.hh" +#include "lnav_log.hh" + +void +scrub_to_utf8(char* buffer, size_t length) +{ + const char* msg; + int faulty_bytes; + + while (true) { + auto scan_res + = is_utf8((unsigned char*) buffer, length, &msg, &faulty_bytes); + + if (msg == nullptr) { + break; + } + for (int lpc = 0; lpc < faulty_bytes; lpc++) { + buffer[scan_res.usr_end + lpc] = '?'; + } + } +} + +void +quote_content(auto_buffer& buf, const string_fragment& sf, char quote_char) +{ + for (char ch : sf) { + if (ch == quote_char) { + buf.push_back('\\').push_back(ch); + continue; + } + switch (ch) { + case '\\': + buf.push_back('\\').push_back('\\'); + break; + case '\n': + buf.push_back('\\').push_back('n'); + break; + case '\t': + buf.push_back('\\').push_back('t'); + break; + case '\r': + buf.push_back('\\').push_back('r'); + break; + case '\a': + buf.push_back('\\').push_back('a'); + break; + case '\b': + buf.push_back('\\').push_back('b'); + break; + default: + buf.push_back(ch); + break; + } + } +} + +size_t +unquote_content(char* dst, const char* str, size_t len, char quote_char) +{ + size_t index = 0; + + for (size_t lpc = 0; lpc < len; lpc++, index++) { + dst[index] = str[lpc]; + if (str[lpc] == quote_char) { + lpc += 1; + } else if (str[lpc] == '\\' && (lpc + 1) < len) { + switch (str[lpc + 1]) { + case 'n': + dst[index] = '\n'; + break; + case 'r': + dst[index] = '\r'; + break; + case 't': + dst[index] = '\t'; + break; + default: + dst[index] = str[lpc + 1]; + break; + } + lpc += 1; + } + } + dst[index] = '\0'; + + return index; +} + +size_t +unquote(char* dst, const char* str, size_t len) +{ + if (str[0] == 'r' || str[0] == 'u') { + str += 1; + len -= 1; + } + char quote_char = str[0]; + + require(str[0] == '\'' || str[0] == '"'); + + return unquote_content(dst, &str[1], len - 2, quote_char); +} + +size_t +unquote_w3c(char* dst, const char* str, size_t len) +{ + size_t index = 0; + + require(str[0] == '\'' || str[0] == '"'); + + for (size_t lpc = 1; lpc < (len - 1); lpc++, index++) { + dst[index] = str[lpc]; + if (str[lpc] == '"') { + lpc += 1; + } + } + dst[index] = '\0'; + + return index; +} + +void +truncate_to(std::string& str, size_t max_char_len) +{ + static const std::string ELLIPSIS = "\u22ef"; + + if (str.length() < max_char_len) { + return; + } + + auto str_char_len_res = utf8_string_length(str); + + if (str_char_len_res.isErr()) { + // XXX + return; + } + + auto str_char_len = str_char_len_res.unwrap(); + if (str_char_len <= max_char_len) { + return; + } + + if (max_char_len < 3) { + str = ELLIPSIS; + return; + } + + auto chars_to_remove = (str_char_len - max_char_len) + 1; + auto midpoint = str_char_len / 2; + auto chars_to_keep_at_front = midpoint - (chars_to_remove / 2); + auto bytes_to_keep_at_front + = utf8_char_to_byte_index(str, chars_to_keep_at_front); + auto remove_up_to_bytes = utf8_char_to_byte_index( + str, chars_to_keep_at_front + chars_to_remove); + auto bytes_to_remove = remove_up_to_bytes - bytes_to_keep_at_front; + str.erase(bytes_to_keep_at_front, bytes_to_remove); + str.insert(bytes_to_keep_at_front, ELLIPSIS); +} + +bool +is_url(const std::string& fn) +{ + static const auto url_re = std::regex("^(file|https?|ftps?|scp|sftp):.*"); + + return std::regex_match(fn, url_re); +} + +size_t +abbreviate_str(char* str, size_t len, size_t max_len) +{ + size_t last_start = 1; + + if (len < max_len) { + return len; + } + + for (size_t index = 0; index < len; index++) { + switch (str[index]) { + case '.': + case '-': + case '/': + case ':': + memmove(&str[last_start], &str[index], len - index); + len -= (index - last_start); + index = last_start + 1; + last_start = index + 1; + + if (len < max_len) { + return len; + } + break; + } + } + + return len; +} + +void +split_ws(const std::string& str, std::vector& toks_out) +{ + std::stringstream ss(str); + std::string buf; + + while (ss >> buf) { + toks_out.push_back(buf); + } +} + +std::string +repeat(const std::string& input, size_t num) +{ + std::ostringstream os; + std::fill_n(std::ostream_iterator(os), num, input); + return os.str(); +} + +std::string +center_str(const std::string& subject, size_t width) +{ + std::string retval = subject; + + truncate_to(retval, width); + + auto retval_length = utf8_string_length(retval).unwrapOr(retval.length()); + auto total_fill = width - retval_length; + auto before = total_fill / 2; + auto after = total_fill - before; + + retval.insert(0, before, ' '); + retval.append(after, ' '); + + return retval; +} + +bool +is_blank(const std::string& str) +{ + return std::all_of( + str.begin(), str.end(), [](const auto ch) { return isspace(ch); }); +} + +std::string +scrub_ws(const char* in) +{ + static const std::string TAB_SYMBOL = "\u21e5"; + static const std::string LF_SYMBOL = "\u240a"; + static const std::string CR_SYMBOL = "\u240d"; + + std::string retval; + + for (size_t lpc = 0; in[lpc]; lpc++) { + auto ch = in[lpc]; + + switch (ch) { + case '\t': + retval.append(TAB_SYMBOL); + break; + case '\n': + retval.append(LF_SYMBOL); + break; + case '\r': + retval.append(CR_SYMBOL); + break; + default: + retval.append(1, ch); + break; + } + } + + return retval; +} diff --git a/src/base/string_util.hh b/src/base/string_util.hh new file mode 100644 index 0000000..73a8b87 --- /dev/null +++ b/src/base/string_util.hh @@ -0,0 +1,232 @@ +/** + * Copyright (c) 2019, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef lnav_string_util_hh +#define lnav_string_util_hh + +#include +#include + +#include + +#include "auto_mem.hh" +#include "intern_string.hh" +#include "ww898/cp_utf8.hpp" + +void scrub_to_utf8(char* buffer, size_t length); + +inline bool +is_line_ending(char ch) +{ + return ch == '\r' || ch == '\n'; +} + +void quote_content(auto_buffer& buf, + const string_fragment& sf, + char quote_char); + +size_t unquote_content(char* dst, const char* str, size_t len, char quote_char); + +size_t unquote(char* dst, const char* str, size_t len); + +size_t unquote_w3c(char* dst, const char* str, size_t len); + +inline bool +startswith(const char* str, const char* prefix) +{ + return strncmp(str, prefix, strlen(prefix)) == 0; +} + +inline bool +startswith(const std::string& str, const char* prefix) +{ + return startswith(str.c_str(), prefix); +} + +inline bool +startswith(const std::string& str, const std::string& prefix) +{ + return startswith(str.c_str(), prefix.c_str()); +} + +inline bool +endswith(const char* str, const char* suffix) +{ + size_t len = strlen(str), suffix_len = strlen(suffix); + + if (suffix_len > len) { + return false; + } + + return strcmp(&str[len - suffix_len], suffix) == 0; +} + +template +inline bool +endswith(const std::string& str, const char (&suffix)[N]) +{ + if (N - 1 > str.length()) { + return false; + } + + return strcmp(&str[str.size() - (N - 1)], suffix) == 0; +} + +void truncate_to(std::string& str, size_t max_char_len); + +std::string scrub_ws(const char* in); + +inline std::string +trim(const std::string& str) +{ + std::string::size_type start, end; + + for (start = 0; start < str.size() && isspace(str[start]); start++) + ; + for (end = str.size(); end > 0 && isspace(str[end - 1]); end--) + ; + + return str.substr(start, end - start); +} + +inline std::string +rtrim(const std::string& str) +{ + std::string::size_type end; + + for (end = str.size(); end > 0 && isspace(str[end - 1]); end--) + ; + + return str.substr(0, end); +} + +inline std::string +tolower(const char* str) +{ + std::string retval; + + for (int lpc = 0; str[lpc]; lpc++) { + retval.push_back(::tolower(str[lpc])); + } + + return retval; +} + +inline std::string +tolower(const std::string& str) +{ + return tolower(str.c_str()); +} + +inline std::string +toupper(const char* str) +{ + std::string retval; + + for (int lpc = 0; str[lpc]; lpc++) { + retval.push_back(::toupper(str[lpc])); + } + + return retval; +} + +inline std::string +toupper(const std::string& str) +{ + return toupper(str.c_str()); +} + +inline ssize_t +utf8_char_to_byte_index(const std::string& str, ssize_t ch_index) +{ + ssize_t retval = 0; + + while (ch_index > 0) { + auto ch_len + = ww898::utf::utf8::char_size([&str, retval]() { + return std::make_pair(str[retval], str.length() - retval - 1); + }).unwrapOr(1); + + retval += ch_len; + ch_index -= 1; + } + + return retval; +} + +inline Result +utf8_string_length(const char* str, ssize_t len = -1) +{ + size_t retval = 0; + + if (len == -1) { + len = strlen(str); + } + + for (ssize_t byte_index = 0; byte_index < len;) { + auto ch_size + = TRY(ww898::utf::utf8::char_size([str, len, byte_index]() { + return std::make_pair(str[byte_index], len - byte_index); + })); + byte_index += ch_size; + retval += 1; + } + + return Ok(retval); +} + +inline Result +utf8_string_length(const std::string& str) +{ + return utf8_string_length(str.c_str(), str.length()); +} + +bool is_url(const std::string& fn); + +bool is_blank(const std::string& str); + +size_t abbreviate_str(char* str, size_t len, size_t max_len); + +void split_ws(const std::string& str, std::vector& toks_out); + +std::string repeat(const std::string& input, size_t num); + +std::string center_str(const std::string& subject, size_t width); + +inline std::string +on_blank(const std::string& str, const std::string& def) +{ + if (is_blank(str)) { + return def; + } + + return str; +} + +#endif diff --git a/src/base/string_util.tests.cc b/src/base/string_util.tests.cc new file mode 100644 index 0000000..98cf5c8 --- /dev/null +++ b/src/base/string_util.tests.cc @@ -0,0 +1,90 @@ +/** + * Copyright (c) 2021, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include "base/string_util.hh" + +#include "base/strnatcmp.h" +#include "config.h" +#include "doctest/doctest.h" + +TEST_CASE("endswith") +{ + std::string hw("hello"); + + CHECK(endswith(hw, "f") == false); + CHECK(endswith(hw, "lo") == true); +} + +TEST_CASE("truncate_to") +{ + const std::string orig = "0123456789abcdefghijklmnopqrstuvwxyz"; + std::string str; + + truncate_to(str, 10); + CHECK(str == ""); + str = "abc"; + truncate_to(str, 10); + CHECK(str == "abc"); + str = orig; + truncate_to(str, 10); + CHECK(str == "01234\u22efwxyz"); + str = orig; + truncate_to(str, 1); + CHECK(str == "\u22ef"); + str = orig; + truncate_to(str, 2); + CHECK(str == "\u22ef"); + str = orig; + truncate_to(str, 3); + CHECK(str == "0\u22efz"); + str = orig; + truncate_to(str, 4); + CHECK(str == "01\u22efz"); + str = orig; + truncate_to(str, 5); + CHECK(str == "01\u22efyz"); +} + +TEST_CASE("strnatcmp") +{ + { + constexpr const char* n1 = "010"; + constexpr const char* n2 = "020"; + + CHECK(strnatcmp(strlen(n1), n1, strlen(n2), n2) < 0); + } + { + constexpr const char* n1 = "2"; + constexpr const char* n2 = "10"; + + CHECK(strnatcmp(strlen(n1), n1, strlen(n2), n2) < 0); + } +} diff --git a/src/base/strnatcmp.c b/src/base/strnatcmp.c new file mode 100644 index 0000000..2a2d2e9 --- /dev/null +++ b/src/base/strnatcmp.c @@ -0,0 +1,302 @@ +/* -*- mode: c; c-file-style: "k&r" -*- + + strnatcmp.c -- Perform 'natural order' comparisons of strings in C. + Copyright (C) 2000, 2004 by Martin Pool + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + + +/* partial change history: + * + * 2004-10-10 mbp: Lift out character type dependencies into macros. + * + * Eric Sosman pointed out that ctype functions take a parameter whose + * value must be that of an unsigned int, even on platforms that have + * negative chars in their default char type. + */ + +#include +#include + +#include "strnatcmp.h" + + +/* These are defined as macros to make it easier to adapt this code to + * different characters types or comparison functions. */ +static inline int +nat_isdigit(nat_char a) +{ + return isdigit((unsigned char) a); +} + + +static inline int +nat_isspace(nat_char a) +{ + return isspace((unsigned char) a); +} + + +static inline nat_char +nat_toupper(nat_char a) +{ + return toupper((unsigned char) a); +} + + + +static int +compare_right(int a_len, nat_char const *a, int b_len, nat_char const *b, int *len_out) +{ + int bias = 0; + + /* The longest run of digits wins. That aside, the greatest + value wins, but we can't know that it will until we've scanned + both numbers to know that they have the same magnitude, so we + remember it in BIAS. */ + for (;; a++, b++, a_len--, b_len--, (*len_out)++) { + if (a_len == 0 && b_len == 0) + return bias; + if (a_len == 0) + return -1; + if (b_len == 0) + return 1; + if (!nat_isdigit(*a) && !nat_isdigit(*b)) + return bias; + else if (!nat_isdigit(*a)) + return -1; + else if (!nat_isdigit(*b)) + return +1; + else if (*a < *b) { + if (!bias) + bias = -1; + } else if (*a > *b) { + if (!bias) + bias = +1; + } else if (!*a && !*b) + return bias; + } + + return 0; +} + +static int +compare_left(int a_len, nat_char const *a, int b_len, nat_char const *b, int *len_out) +{ + /* Compare two left-aligned numbers: the first to have a + different value wins. */ + for (;; a++, b++, a_len--, b_len--, (*len_out)++) { + if (a_len == 0 && b_len == 0) + return 0; + if (a_len == 0) + return -1; + if (b_len == 0) + return 1; + if (!nat_isdigit(*a) && !nat_isdigit(*b)) + return 0; + else if (!nat_isdigit(*a)) + return -1; + else if (!nat_isdigit(*b)) + return +1; + else if (*a < *b) + return -1; + else if (*a > *b) + return +1; + } + + return 0; +} + +static int strnatcmp0(int a_len, nat_char const *a, + int b_len, nat_char const *b, + int fold_case) +{ + int ai, bi; + nat_char ca, cb; + int fractional, result; + + assert(a && b); + ai = bi = 0; + while (1) { + if (ai >= a_len) + ca = 0; + else + ca = a[ai]; + if (bi >= b_len) + cb = 0; + else + cb = b[bi]; + + /* skip over leading spaces or zeros */ + while (nat_isspace(ca)) { + ai += 1; + if (ai >= a_len) + ca = 0; + else + ca = a[ai]; + } + + while (nat_isspace(cb)) { + bi += 1; + if (bi >= b_len) + cb = 0; + else + cb = b[bi]; + } + + /* process run of digits */ + if (nat_isdigit(ca) && nat_isdigit(cb)) { + int num_len = 0; + + fractional = (ca == '0' || cb == '0'); + + if (fractional) { + if ((result = compare_left(a_len - ai, a + ai, b_len - bi, + b + bi, &num_len)) != 0) { + return result; + } + } else { + if ((result = compare_right(a_len - ai, a + ai, b_len - bi, + b + bi, &num_len)) != 0) { + return result; + } + } + + ai += num_len; + bi += num_len; + continue; + } + + if (!ca && !cb) { + /* The strings compare the same. Perhaps the caller + will want to call strcmp to break the tie. */ + return 0; + } + + if (fold_case) { + ca = nat_toupper(ca); + cb = nat_toupper(cb); + } + + if (ca < cb) + return -1; + else if (ca > cb) + return +1; + + ++ai; + ++bi; + } +} + +int ipv4cmp(int a_len, nat_char const *a, + int b_len, nat_char const *b, + int *res_out) +{ + int ai, bi; + nat_char ca, cb; + int fractional, result = 0; + + assert(a && b); + ai = bi = 0; + while (result == 0) { + if (ai >= a_len) + ca = 0; + else + ca = a[ai]; + if (bi >= b_len) + cb = 0; + else + cb = b[bi]; + + /* skip over leading spaces or zeros */ + while (nat_isspace(ca)) { + ai += 1; + if (ai >= a_len) + ca = 0; + else + ca = a[ai]; + } + + while (nat_isspace(cb)) { + bi += 1; + if (bi >= b_len) + cb = 0; + else + cb = b[bi]; + } + + /* process run of digits */ + if (nat_isdigit(ca) && nat_isdigit(cb)) { + int num_len = 0; + + fractional = (ca == '0' || cb == '0'); + + if (fractional) { + result = compare_left(a_len - ai, a + ai, b_len - bi, + b + bi, &num_len); + } else { + result = compare_right(a_len - ai, a + ai, b_len - bi, + b + bi, &num_len); + } + + ai += num_len; + bi += num_len; + continue; + } + + if (!ca && !cb) { + /* The strings compare the same. Perhaps the caller + will want to call strcmp to break the tie. */ + *res_out = result; + return 1; + } + + if (ca != '.' || cb != '.') { + return 0; + } + + ++ai; + ++bi; + } + + for (; ai < a_len; ai++) { + if (!isdigit(a[ai]) || a[ai] != '.') { + return 0; + } + } + + for (; bi < b_len; bi++) { + if (!isdigit(b[bi]) || b[bi] != '.') { + return 0; + } + } + + *res_out = result; + return 1; +} + +int strnatcmp(int a_len, nat_char const *a, int b_len, nat_char const *b) +{ + return strnatcmp0(a_len, a, b_len, b, 0); +} + +/* Compare, recognizing numeric string and ignoring case. */ +int strnatcasecmp(int a_len, nat_char const *a, int b_len, nat_char const *b) +{ + return strnatcmp0(a_len, a, b_len, b, 1); +} diff --git a/src/base/strnatcmp.h b/src/base/strnatcmp.h new file mode 100644 index 0000000..24b0c53 --- /dev/null +++ b/src/base/strnatcmp.h @@ -0,0 +1,41 @@ +/* -*- mode: c; c-file-style: "k&r" -*- + + strnatcmp.c -- Perform 'natural order' comparisons of strings in C. + Copyright (C) 2000, 2004 by Martin Pool + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/* CUSTOMIZATION SECTION + * + * You can change this typedef, but must then also change the inline + * functions in strnatcmp.c */ +typedef char nat_char; + +int strnatcmp(int a_len, nat_char const *a, int b_len, nat_char const *b); + +int strnatcasecmp(int a_len, nat_char const *a, int b_len, nat_char const *b); + +int ipv4cmp(int a_len, nat_char const *a, int b_len, nat_char const *b, int *res_out); + +#ifdef __cplusplus +} +#endif diff --git a/src/base/test_base.cc b/src/base/test_base.cc new file mode 100644 index 0000000..654cd57 --- /dev/null +++ b/src/base/test_base.cc @@ -0,0 +1,33 @@ +/** + * Copyright (c) 2021, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include "doctest/doctest.h" diff --git a/src/base/time_util.cc b/src/base/time_util.cc new file mode 100644 index 0000000..0d46107 --- /dev/null +++ b/src/base/time_util.cc @@ -0,0 +1,239 @@ +/** + * Copyright (c) 2020, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @file time_util.cc + */ + +#include + +#include "time_util.hh" + +#include "config.h" + +namespace lnav { + +ssize_t +strftime_rfc3339( + char* buffer, size_t buffer_size, lnav::time64_t tim, int millis, char sep) +{ + struct tm gmtm; + int year, month, index = 0; + + secs2tm(tim, &gmtm); + year = gmtm.tm_year + 1900; + month = gmtm.tm_mon + 1; + buffer[index++] = '0' + ((year / 1000) % 10); + buffer[index++] = '0' + ((year / 100) % 10); + buffer[index++] = '0' + ((year / 10) % 10); + buffer[index++] = '0' + ((year / 1) % 10); + buffer[index++] = '-'; + buffer[index++] = '0' + ((month / 10) % 10); + buffer[index++] = '0' + ((month / 1) % 10); + buffer[index++] = '-'; + buffer[index++] = '0' + ((gmtm.tm_mday / 10) % 10); + buffer[index++] = '0' + ((gmtm.tm_mday / 1) % 10); + buffer[index++] = sep; + buffer[index++] = '0' + ((gmtm.tm_hour / 10) % 10); + buffer[index++] = '0' + ((gmtm.tm_hour / 1) % 10); + buffer[index++] = ':'; + buffer[index++] = '0' + ((gmtm.tm_min / 10) % 10); + buffer[index++] = '0' + ((gmtm.tm_min / 1) % 10); + buffer[index++] = ':'; + buffer[index++] = '0' + ((gmtm.tm_sec / 10) % 10); + buffer[index++] = '0' + ((gmtm.tm_sec / 1) % 10); + buffer[index++] = '.'; + buffer[index++] = '0' + ((millis / 100) % 10); + buffer[index++] = '0' + ((millis / 10) % 10); + buffer[index++] = '0' + ((millis / 1) % 10); + buffer[index] = '\0'; + + return index; +} + +} + +static time_t BAD_DATE = -1; + +time_t +tm2sec(const struct tm* t) +{ + int year; + time_t days, secs; + const int dayoffset[12] + = {306, 337, 0, 31, 61, 92, 122, 153, 184, 214, 245, 275}; + + year = t->tm_year; + + if (year < 70) { + return BAD_DATE; + } + if ((sizeof(time_t) <= 4) && (year >= 138)) { + year = 137; + } + + /* shift new year to 1st March in order to make leap year calc easy */ + + if (t->tm_mon < 2) { + year--; + } + + /* Find number of days since 1st March 1900 (in the Gregorian calendar). */ + + days = year * 365 + year / 4 - year / 100 + (year / 100 + 3) / 4; + days += dayoffset[t->tm_mon] + t->tm_mday - 1; + days -= 25508; /* 1 jan 1970 is 25508 days since 1 mar 1900 */ + + secs = ((days * 24 + t->tm_hour) * 60 + t->tm_min) * 60 + t->tm_sec; + + if (secs < 0) { + return BAD_DATE; + } /* must have overflowed */ + else + { +#ifdef HAVE_STRUCT_TM_TM_ZONE + if (t->tm_zone) { + secs -= t->tm_gmtoff; + } +#endif + return secs; + } /* must be a valid time */ +} + +static const int SECSPERMIN = 60; +static const int SECSPERHOUR = 60 * SECSPERMIN; +static const int SECSPERDAY = 24 * SECSPERHOUR; +static const int YEAR_BASE = 1900; +static const int EPOCH_WDAY = 4; +static const int DAYSPERWEEK = 7; +static const int EPOCH_YEAR = 1970; + +#define isleap(y) ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0) + +static const int year_lengths[2] = {365, 366}; + +const unsigned short int mon_yday[2][13] = { + /* Normal years. */ + {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365}, + /* Leap years. */ + {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}}; + +void +secs2wday(const struct timeval& tv, struct tm* res) +{ + long days, rem; + time_t lcltime; + + /* base decision about std/dst time on current time */ + lcltime = tv.tv_sec; + + days = ((long) lcltime) / SECSPERDAY; + rem = ((long) lcltime) % SECSPERDAY; + while (rem < 0) { + rem += SECSPERDAY; + --days; + } + + /* compute day of week */ + if ((res->tm_wday = ((EPOCH_WDAY + days) % DAYSPERWEEK)) < 0) { + res->tm_wday += DAYSPERWEEK; + } +} + +struct tm* +secs2tm(lnav::time64_t tim, struct tm* res) +{ + long days, rem; + lnav::time64_t lcltime; + int y; + int yleap; + const unsigned short int* ip; + + /* base decision about std/dst time on current time */ + lcltime = tim; + + days = ((long) lcltime) / SECSPERDAY; + rem = ((long) lcltime) % SECSPERDAY; + while (rem < 0) { + rem += SECSPERDAY; + --days; + } + + /* compute hour, min, and sec */ + res->tm_hour = (int) (rem / SECSPERHOUR); + rem %= SECSPERHOUR; + res->tm_min = (int) (rem / SECSPERMIN); + res->tm_sec = (int) (rem % SECSPERMIN); + + /* compute day of week */ + if ((res->tm_wday = ((EPOCH_WDAY + days) % DAYSPERWEEK)) < 0) + res->tm_wday += DAYSPERWEEK; + + /* compute year & day of year */ + y = EPOCH_YEAR; + if (days >= 0) { + for (;;) { + yleap = isleap(y); + if (days < year_lengths[yleap]) + break; + y++; + days -= year_lengths[yleap]; + } + } else { + do { + --y; + yleap = isleap(y); + days += year_lengths[yleap]; + } while (days < 0); + } + + res->tm_year = y - YEAR_BASE; + res->tm_yday = days; + ip = mon_yday[isleap(y)]; + for (y = 11; days < (long int) ip[y]; --y) + continue; + days -= ip[y]; + res->tm_mon = y; + res->tm_mday = days + 1; + + res->tm_isdst = 0; + + return (res); +} + +struct timeval +exttm::to_timeval() const +{ + struct timeval retval; + + retval.tv_sec = tm2sec(&this->et_tm); + retval.tv_usec = std::chrono::duration_cast( + std::chrono::nanoseconds(this->et_nsec)) + .count(); + + return retval; +} diff --git a/src/base/time_util.hh b/src/base/time_util.hh new file mode 100644 index 0000000..ef9687f --- /dev/null +++ b/src/base/time_util.hh @@ -0,0 +1,206 @@ +/** + * Copyright (c) 2020, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef lnav_time_util_hh +#define lnav_time_util_hh + +#include +#include +#include +#include +#include + +#include "config.h" + +namespace lnav { + +using time64_t = uint64_t; + +ssize_t strftime_rfc3339(char* buffer, + size_t buffer_size, + lnav::time64_t tim, + int millis, + char sep = ' '); + +} // namespace lnav + +struct tm* secs2tm(lnav::time64_t tim, struct tm* res); +/** + * Convert the time stored in a 'tm' struct into epoch time. + * + * @param t The 'tm' structure to convert to epoch time. + * @return The given time in seconds since the epoch. + */ +time_t tm2sec(const struct tm* t); +void secs2wday(const struct timeval& tv, struct tm* res); + +inline time_t +convert_log_time_to_local(time_t value) +{ + struct tm tm; + + localtime_r(&value, &tm); +#ifdef HAVE_STRUCT_TM_TM_ZONE + tm.tm_zone = NULL; +#endif + tm.tm_isdst = 0; + return tm2sec(&tm); +} + +constexpr lnav::time64_t MAX_TIME_T = 4000000000LL; + +enum exttm_bits_t { + ETB_YEAR_SET, + ETB_MONTH_SET, + ETB_DAY_SET, + ETB_HOUR_SET, + ETB_MINUTE_SET, + ETB_SECOND_SET, + ETB_MACHINE_ORIENTED, + ETB_EPOCH_TIME, + ETB_MILLIS_SET, + ETB_MICROS_SET, + ETB_NANOS_SET, +}; + +enum exttm_flags_t { + ETF_YEAR_SET = (1UL << ETB_YEAR_SET), + ETF_MONTH_SET = (1UL << ETB_MONTH_SET), + ETF_DAY_SET = (1UL << ETB_DAY_SET), + ETF_HOUR_SET = (1UL << ETB_HOUR_SET), + ETF_MINUTE_SET = (1UL << ETB_MINUTE_SET), + ETF_SECOND_SET = (1UL << ETB_SECOND_SET), + ETF_MACHINE_ORIENTED = (1UL << ETB_MACHINE_ORIENTED), + ETF_EPOCH_TIME = (1UL << ETB_EPOCH_TIME), + ETF_MILLIS_SET = (1UL << ETB_MILLIS_SET), + ETF_MICROS_SET = (1UL << ETB_MICROS_SET), + ETF_NANOS_SET = (1UL << ETB_NANOS_SET), +}; + +struct exttm { + struct tm et_tm {}; + int32_t et_nsec{0}; + unsigned int et_flags{0}; + long et_gmtoff{0}; + + exttm() { memset(&this->et_tm, 0, sizeof(this->et_tm)); } + + bool operator==(const exttm& other) const + { + return memcmp(this, &other, sizeof(exttm)) == 0; + } + + struct timeval to_timeval() const; +}; + +inline bool +operator<(const struct timeval& left, time_t right) +{ + return left.tv_sec < right; +} + +inline bool +operator<(time_t left, const struct timeval& right) +{ + return left < right.tv_sec; +} + +inline bool +operator<(const struct timeval& left, const struct timeval& right) +{ + return left.tv_sec < right.tv_sec + || ((left.tv_sec == right.tv_sec) && (left.tv_usec < right.tv_usec)); +} + +inline bool +operator!=(const struct timeval& left, const struct timeval& right) +{ + return left.tv_sec != right.tv_sec || left.tv_usec != right.tv_usec; +} + +inline bool +operator==(const struct timeval& left, const struct timeval& right) +{ + return left.tv_sec == right.tv_sec || left.tv_usec == right.tv_usec; +} + +inline struct timeval +operator-(const struct timeval& lhs, const struct timeval& rhs) +{ + struct timeval diff; + + timersub(&lhs, &rhs, &diff); + return diff; +} + +typedef int64_t mstime_t; + +inline mstime_t +getmstime() +{ + struct timeval tv; + + gettimeofday(&tv, nullptr); + + return tv.tv_sec * 1000ULL + tv.tv_usec / 1000ULL; +} + +inline struct timeval +current_timeval() +{ + struct timeval retval; + + gettimeofday(&retval, nullptr); + + return retval; +} + +inline struct timespec +current_timespec() +{ + struct timespec retval; + + clock_gettime(CLOCK_REALTIME, &retval); + + return retval; +} + +inline time_t +day_num(time_t ti) +{ + return ti / (24 * 60 * 60); +} + +inline time_t +hour_num(time_t ti) +{ + return ti / (60 * 60); +} + +#endif diff --git a/src/big_array.hh b/src/big_array.hh new file mode 100644 index 0000000..8401ead --- /dev/null +++ b/src/big_array.hh @@ -0,0 +1,136 @@ +/** + * Copyright (c) 2017, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @file big_array.hh + */ + +#ifndef lnav_big_array_hh +#define lnav_big_array_hh + +#include +#include + +#include "base/math_util.hh" + +template +struct big_array { + static const size_t DEFAULT_INCREMENT = 100 * 1000; + + big_array() + : ba_ptr(nullptr), ba_size(0), ba_capacity(0){ + + }; + + bool reserve(size_t size) + { + if (size < this->ba_capacity) { + return false; + } + + if (this->ba_ptr) { + munmap(this->ba_ptr, + roundup_size(this->ba_capacity * sizeof(T), getpagesize())); + } + + this->ba_capacity = size + DEFAULT_INCREMENT; + void* result + = mmap(nullptr, + roundup_size(this->ba_capacity * sizeof(T), getpagesize()), + PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_PRIVATE, + -1, + 0); + + ensure(result != MAP_FAILED); + + this->ba_ptr = (T*) result; + + return true; + }; + + void clear() + { + this->ba_size = 0; + }; + + size_t size() const + { + return this->ba_size; + }; + + void shrink_to(size_t new_size) + { + require(new_size <= this->ba_size); + + this->ba_size = new_size; + } + + bool empty() const + { + return this->ba_size == 0; + }; + + void push_back(const T& val) + { + this->ba_ptr[this->ba_size] = val; + this->ba_size += 1; + }; + + T& operator[](size_t index) + { + return this->ba_ptr[index]; + }; + + const T& operator[](size_t index) const + { + return this->ba_ptr[index]; + }; + + T& back() + { + return this->ba_ptr[this->ba_size - 1]; + } + + typedef T* iterator; + + iterator begin() + { + return this->ba_ptr; + }; + + iterator end() + { + return this->ba_ptr + this->ba_size; + }; + + T* ba_ptr; + size_t ba_size; + size_t ba_capacity; +}; + +#endif diff --git a/src/bin2c.hh b/src/bin2c.hh new file mode 100644 index 0000000..fc6c4db --- /dev/null +++ b/src/bin2c.hh @@ -0,0 +1,74 @@ +/** + * Copyright (c) 2020, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @file bin2c.hh + */ + +#ifndef lnav_bin2c_hh +#define lnav_bin2c_hh + +#include + +#include +#include +#include + +#include "base/intern_string.hh" + +struct bin_src_file { + bin_src_file(const char* name, + const unsigned char* data, + size_t compressed_size, + size_t size) + : bsf_name(name), bsf_data(new unsigned char[size + 1]), bsf_size(size) + { + uLongf zsize = size; + auto rc + = uncompress(this->bsf_data.get(), &zsize, data, compressed_size); + assert(rc == Z_OK); + assert(zsize == size); + this->bsf_data[size] = '\0'; + }; + + string_fragment to_string_fragment() const + { + return string_fragment{this->bsf_data.get(), 0, (int) this->bsf_size}; + } + + const char* get_name() const + { + return this->bsf_name; + } + +private: + const char* bsf_name; + std::unique_ptr bsf_data; + ssize_t bsf_size; +}; + +#endif diff --git a/src/bookmarks.cc b/src/bookmarks.cc new file mode 100644 index 0000000..89637ad --- /dev/null +++ b/src/bookmarks.cc @@ -0,0 +1,89 @@ +/** + * Copyright (c) 2007-2012, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @file bookmarks.cc + */ + +#include "bookmarks.hh" + +#include "base/itertools.hh" +#include "config.h" + +std::unordered_set bookmark_metadata::KNOWN_TAGS; + +void +bookmark_metadata::add_tag(const std::string& tag) +{ + if (!(this->bm_tags | lnav::itertools::find(tag))) { + this->bm_tags.emplace_back(tag); + } +} + +bool +bookmark_metadata::remove_tag(const std::string& tag) +{ + auto iter = std::find(this->bm_tags.begin(), this->bm_tags.end(), tag); + bool retval = false; + + if (iter != this->bm_tags.end()) { + this->bm_tags.erase(iter); + retval = true; + } + return retval; +} + +bool +bookmark_metadata::empty() const +{ + return this->bm_name.empty() && this->bm_comment.empty() + && this->bm_tags.empty(); +} + +void +bookmark_metadata::clear() +{ + this->bm_comment.clear(); + this->bm_tags.clear(); +} + +nonstd::optional +bookmark_type_t::find_type(const std::string& name) +{ + return get_all_types() + | lnav::itertools::find_if( + [&name](const auto& elem) { return elem->bt_name == name; }) + | lnav::itertools::deref(); +} + +std::vector& +bookmark_type_t::get_all_types() +{ + static std::vector all_types; + + return all_types; +} diff --git a/src/bookmarks.hh b/src/bookmarks.hh new file mode 100644 index 0000000..189e830 --- /dev/null +++ b/src/bookmarks.hh @@ -0,0 +1,209 @@ +/** + * Copyright (c) 2007-2012, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @file bookmarks.hh + */ + +#ifndef bookmarks_hh +#define bookmarks_hh + +#include +#include +#include +#include +#include + +#include "base/lnav_log.hh" + +struct bookmark_metadata { + static std::unordered_set KNOWN_TAGS; + + std::string bm_name; + std::string bm_comment; + std::vector bm_tags; + + void add_tag(const std::string& tag); + + bool remove_tag(const std::string& tag); + + bool empty() const; + + void clear(); +}; + +/** + * Extension of the STL vector that is used to store bookmarks for + * files being viewed, where a bookmark is just a particular line in + * the file(s). The value-added over the standard vector are some + * methods for doing content-wise iteration. In other words, given a + * value that may or may not be in the vector, find the next or + * previous value that is in the vector. + * + * @param LineType The type used to store line numbers. (e.g. + * vis_line_t or content_line_t) + * + * @note The vector is expected to be sorted. + */ +template +class bookmark_vector : public std::vector { + using base_vector = std::vector; + +public: + using size_type = typename base_vector::size_type; + using iterator = typename base_vector::iterator; + using const_iterator = typename base_vector::const_iterator; + + /** + * Insert a bookmark into this vector, but only if it is not already in the + * vector. + * + * @param vl The line to bookmark. + */ + iterator insert_once(LineType vl) + { + iterator retval; + + require(vl >= 0); + + auto lb = std::lower_bound(this->begin(), this->end(), vl); + if (lb == this->end() || *lb != vl) { + this->insert(lb, vl); + retval = this->end(); + } else { + retval = lb; + } + + return retval; + } + + std::pair equal_range(LineType start, LineType stop) + { + auto lb = std::lower_bound(this->begin(), this->end(), start); + + if (stop == LineType(-1)) { + return std::make_pair(lb, this->end()); + } + + auto up = std::upper_bound(this->begin(), this->end(), stop); + + return std::make_pair(lb, up); + } + + /** + * @param start The value to start the search for the next bookmark. + * @return The next bookmark value in the vector or -1 if there are + * no more remaining bookmarks. If the 'start' value is a bookmark, + * the next bookmark is returned. If the 'start' value is not a + * bookmark, the next highest value in the vector is returned. + */ + nonstd::optional next(LineType start) const; + + /** + * @param start The value to start the search for the previous + * bookmark. + * @return The previous bookmark value in the vector or -1 if there + * are no more prior bookmarks. + * @see next + */ + nonstd::optional prev(LineType start) const; +}; + +/** + * Dummy type whose instances are used to distinguish between + * bookmarks maintained by different source modules. + */ +class bookmark_type_t { +public: + using type_iterator = std::vector::iterator; + + static type_iterator type_begin() { return get_all_types().begin(); } + + static type_iterator type_end() { return get_all_types().end(); } + + static nonstd::optional find_type( + const std::string& name); + + static std::vector& get_all_types(); + + explicit bookmark_type_t(std::string name) : bt_name(std::move(name)) + { + get_all_types().push_back(this); + } + + const std::string& get_name() const { return this->bt_name; } + +private: + const std::string bt_name; +}; + +template +nonstd::optional +bookmark_vector::next(LineType start) const +{ + nonstd::optional retval; + + require(start >= -1); + + auto ub = std::upper_bound(this->cbegin(), this->cend(), start); + if (ub != this->cend()) { + retval = *ub; + } + + ensure(!retval || start < retval.value()); + + return retval; +} + +template +nonstd::optional +bookmark_vector::prev(LineType start) const +{ + nonstd::optional retval; + + require(start >= 0); + + auto lb = std::lower_bound(this->cbegin(), this->cend(), start); + if (lb != this->cbegin()) { + lb -= 1; + retval = *lb; + } + + ensure(!retval || retval.value() < start); + + return retval; +} + +/** + * Map of bookmark types to bookmark vectors. + */ +template +struct bookmarks { + using type = std::map>; +}; + +#endif diff --git a/src/bottom_status_source.cc b/src/bottom_status_source.cc new file mode 100644 index 0000000..05bc88e --- /dev/null +++ b/src/bottom_status_source.cc @@ -0,0 +1,250 @@ +/** + * Copyright (c) 2019, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "bottom_status_source.hh" + +#include "base/snippet_highlighters.hh" +#include "config.h" + +bottom_status_source::bottom_status_source() +{ + this->bss_fields[BSF_LINE_NUMBER].set_min_width(10); + this->bss_fields[BSF_LINE_NUMBER].set_share(1000); + this->bss_fields[BSF_PERCENT].set_width(6); + this->bss_fields[BSF_PERCENT].set_left_pad(1); + this->bss_fields[BSF_HITS].set_min_width(10); + this->bss_fields[BSF_HITS].set_share(5); + this->bss_fields[BSF_SEARCH_TERM].set_min_width(10); + this->bss_fields[BSF_SEARCH_TERM].set_share(1); + this->bss_fields[BSF_LOADING].set_width(13); + this->bss_fields[BSF_LOADING].right_justify(true); + this->bss_fields[BSF_HELP].set_width(14); + this->bss_fields[BSF_HELP].set_value("?:View Help"); + this->bss_fields[BSF_HELP].right_justify(true); + this->bss_prompt.set_left_pad(1); + this->bss_prompt.set_min_width(35); + this->bss_prompt.set_share(1); + this->bss_error.set_left_pad(1); + this->bss_error.set_min_width(35); + this->bss_error.set_share(1); + this->bss_line_error.set_left_pad(1); + this->bss_line_error.set_min_width(35); + this->bss_line_error.set_share(1); +} + +void +bottom_status_source::update_line_number(listview_curses* lc) +{ + status_field& sf = this->bss_fields[BSF_LINE_NUMBER]; + + if (lc->get_inner_height() == 0) { + sf.set_value(" L0"); + } else { + sf.set_value(" L%'d", (int) lc->get_top()); + } + + this->bss_line_error.set_value( + lc->map_top_row([](const attr_line_t& top_row) + -> nonstd::optional { + const auto& sa = top_row.get_attrs(); + auto error_wrapper = get_string_attr(sa, SA_ERROR); + if (error_wrapper) { + return error_wrapper.value().get(); + } + return nonstd::nullopt; + }).value_or("")); +} + +void +bottom_status_source::update_search_term(textview_curses& tc) +{ + auto& sf = this->bss_fields[BSF_SEARCH_TERM]; + auto search_term = tc.get_current_search(); + + sf.clear(); + if (!search_term.empty()) { + auto search_term_al = attr_line_t(search_term); + + lnav::snippets::regex_highlighter( + search_term_al, -1, line_range{0, (int) search_term_al.length()}); + sf.get_value().append_quoted(search_term_al); + } + + this->bss_paused = tc.is_paused(); + this->update_loading(0, 0); +} + +void +bottom_status_source::update_percent(listview_curses* lc) +{ + status_field& sf = this->bss_fields[BSF_PERCENT]; + vis_line_t top = lc->get_top(); + vis_line_t bottom, height; + unsigned long width; + double percent; + + lc->get_dimensions(height, width); + + if (lc->get_inner_height() > 0) { + bottom = std::min(top + height - 1_vl, lc->get_inner_height() - 1_vl); + percent = (double) (bottom + 1); + percent /= (double) lc->get_inner_height(); + percent *= 100.0; + } else { + percent = 0.0; + } + sf.set_value("%3d%% ", (int) percent); +} + +void +bottom_status_source::update_marks(listview_curses* lc) +{ + auto* tc = static_cast(lc); + vis_bookmarks& bm = tc->get_bookmarks(); + status_field& sf = this->bss_fields[BSF_HITS]; + + if (bm.find(&textview_curses::BM_SEARCH) != bm.end()) { + bookmark_vector& bv = bm[&textview_curses::BM_SEARCH]; + + if (!bv.empty() || !tc->get_current_search().empty()) { + bookmark_vector::iterator lb; + + lb = std::lower_bound(bv.begin(), bv.end(), tc->get_top()); + if (lb != bv.end() && *lb == tc->get_top()) { + sf.set_value(" Hit %'d of %'d for ", + std::distance(bv.begin(), lb) + 1, + tc->get_match_count()); + } else { + sf.set_value(" %'d hits for ", tc->get_match_count()); + } + } else { + sf.clear(); + } + } else { + sf.clear(); + } +} + +void +bottom_status_source::update_hits(textview_curses* tc) +{ + status_field& sf = this->bss_fields[BSF_HITS]; + role_t new_role; + + if (tc->is_searching()) { + this->bss_hit_spinner += 1; + if (this->bss_hit_spinner % 2) { + new_role = role_t::VCR_ACTIVE_STATUS; + } else { + new_role = role_t::VCR_ACTIVE_STATUS2; + } + sf.set_cylon(true); + } else { + new_role = role_t::VCR_STATUS; + sf.set_cylon(false); + } + // this->bss_error.clear(); + sf.set_role(new_role); + this->update_marks(tc); +} + +void +bottom_status_source::update_loading(file_off_t off, file_ssize_t total) +{ + auto& sf = this->bss_fields[BSF_LOADING]; + + require(off >= 0); + require(off <= total); + + if (total == 0) { + sf.set_cylon(false); + sf.set_role(role_t::VCR_STATUS); + if (this->bss_paused) { + sf.set_value("\xE2\x80\x96 Paused"); + } else { + sf.clear(); + } + } else if (off == total) { + static const std::vector DOTS = { + " ", + ". ", + ".. ", + "...", + ".. ", + ". ", + }; + + this->bss_load_percent += 1; + sf.set_cylon(true); + sf.set_role(role_t::VCR_ACTIVE_STATUS2); + sf.set_value(" Working%s ", + DOTS[this->bss_load_percent % DOTS.size()].c_str()); + } else { + int pct = (int) (((double) off / (double) total) * 100.0); + + if (this->bss_load_percent != pct) { + this->bss_load_percent = pct; + + sf.set_cylon(true); + sf.set_role(role_t::VCR_ACTIVE_STATUS2); + sf.set_value(" Loading %2d%% ", pct); + } + } +} + +size_t +bottom_status_source::statusview_fields() +{ + size_t retval; + + if (this->bss_prompt.empty() && this->bss_error.empty() + && this->bss_line_error.empty()) + { + retval = BSF__MAX; + } else { + retval = 1; + } + + return retval; +} + +status_field& +bottom_status_source::statusview_value_for_field(int field) +{ + if (!this->bss_error.empty()) { + return this->bss_error; + } + if (!this->bss_prompt.empty()) { + return this->bss_prompt; + } + if (!this->bss_line_error.empty()) { + return this->bss_line_error; + } + return this->get_field((field_t) field); +} diff --git a/src/bottom_status_source.hh b/src/bottom_status_source.hh new file mode 100644 index 0000000..dcf6c35 --- /dev/null +++ b/src/bottom_status_source.hh @@ -0,0 +1,94 @@ +/** + * Copyright (c) 2007-2012, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef lnav_bottom_status_source_hh +#define lnav_bottom_status_source_hh + +#include + +#include "grep_proc.hh" +#include "statusview_curses.hh" +#include "textview_curses.hh" + +class bottom_status_source + : public status_data_source + , public grep_proc_control { +public: + typedef enum { + BSF_LINE_NUMBER, + BSF_PERCENT, + BSF_HITS, + BSF_SEARCH_TERM, + BSF_LOADING, + BSF_HELP, + + BSF__MAX + } field_t; + + bottom_status_source(); + + status_field& get_field(field_t id) { return this->bss_fields[id]; } + + void set_prompt(const std::string& prompt) + { + this->bss_prompt.set_value(prompt); + } + + void grep_error(const std::string& msg) override + { + this->bss_error.set_value(msg); + } + + size_t statusview_fields() override; + + status_field& statusview_value_for_field(int field) override; + + void update_line_number(listview_curses* lc); + + void update_search_term(textview_curses& tc); + + void update_percent(listview_curses* lc); + + void update_marks(listview_curses* lc); + + void update_hits(textview_curses* tc); + + void update_loading(file_off_t off, file_ssize_t total); + +private: + status_field bss_prompt{1024, role_t::VCR_STATUS}; + status_field bss_error{1024, role_t::VCR_ALERT_STATUS}; + status_field bss_line_error{1024, role_t::VCR_ALERT_STATUS}; + status_field bss_fields[BSF__MAX]; + int bss_hit_spinner{0}; + int bss_load_percent{0}; + bool bss_paused{false}; +}; + +#endif diff --git a/src/bound_tags.hh b/src/bound_tags.hh new file mode 100644 index 0000000..a24f38c --- /dev/null +++ b/src/bound_tags.hh @@ -0,0 +1,54 @@ +/** + * Copyright (c) 2021, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @file bound_tags.hh + */ + +#ifndef lnav_bound_tags_hh +#define lnav_bound_tags_hh + +struct last_relative_time_tag {}; + +struct sql_cmd_map_tag {}; + +enum { + LNB_HEADLESS, + LNB_MANAGEMENT, + LNB_SECURE_MODE, +}; + +/** Flags set on the lnav command-line. */ +typedef enum { + LNF_HEADLESS = (1L << LNB_HEADLESS), + LNF_MANAGEMENT = (1L << LNB_MANAGEMENT), + LNF_SECURE_MODE = (1L << LNB_SECURE_MODE), +} lnav_flags_t; + +struct lnav_flags_tag {}; + +#endif diff --git a/src/breadcrumb.hh b/src/breadcrumb.hh new file mode 100644 index 0000000..051ba2b --- /dev/null +++ b/src/breadcrumb.hh @@ -0,0 +1,134 @@ +/** + * Copyright (c) 2022, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef lnav_breadcrumb_hh +#define lnav_breadcrumb_hh + +#include + +#include "base/attr_line.hh" +#include "fmt/format.h" +#include "mapbox/variant.hpp" + +namespace breadcrumb { + +struct possibility { + possibility(std::string key, attr_line_t value) + : p_key(std::move(key)), p_display_value(std::move(value)) + { + } + + explicit possibility(std::string key) : p_key(key), p_display_value(key) {} + + possibility() = default; + + bool operator<(const possibility& rhs) const { return p_key < rhs.p_key; } + bool operator>(const possibility& rhs) const { return rhs < *this; } + bool operator<=(const possibility& rhs) const { return !(rhs < *this); } + bool operator>=(const possibility& rhs) const { return !(*this < rhs); } + + std::string p_key; + attr_line_t p_display_value; +}; + +using crumb_possibilities = std::function()>; + +struct crumb { + using key_t = mapbox::util::variant; + + using perform = std::function; + + crumb(std::string key, + attr_line_t al, + crumb_possibilities cp, + perform performer) + : c_key(std::move(key)), c_display_value(std::move(al)), + c_possibility_provider(std::move(cp)), + c_performer(std::move(performer)) + { + } + + crumb(std::string key, crumb_possibilities cp, perform performer) + : c_key(key), c_display_value(key), + c_possibility_provider(std::move(cp)), + c_performer(std::move(performer)) + { + } + + explicit crumb(size_t index, crumb_possibilities cp, perform performer) + : c_key(index), c_display_value(fmt::format(FMT_STRING("[{}]"), index)), + c_possibility_provider(std::move(cp)), + c_performer(std::move(performer)) + { + } + + explicit crumb(key_t key, crumb_possibilities cp, perform performer) + : c_key(key), c_display_value(key.match( + [](std::string str) { return str; }, + [](size_t index) { + return fmt::format(FMT_STRING("[{}]"), index); + })), + c_possibility_provider(std::move(cp)), + c_performer(std::move(performer)) + { + } + + crumb& with_possible_range(size_t count) + { + this->c_possible_range = count; + if (count == 0) { + this->c_search_placeholder = "(Array is empty)"; + } else if (count == 1) { + this->c_search_placeholder = "(Array contains a single element)"; + } else { + this->c_search_placeholder = fmt::format( + FMT_STRING("(Enter a number from 0 to {})"), count - 1); + } + return *this; + } + + enum class expected_input_t { + exact, + index, + index_or_exact, + anything, + }; + + key_t c_key; + attr_line_t c_display_value; + crumb_possibilities c_possibility_provider; + perform c_performer; + nonstd::optional c_possible_range; + expected_input_t c_expected_input{expected_input_t::exact}; + std::string c_search_placeholder; +}; + +} // namespace breadcrumb + +#endif diff --git a/src/breadcrumb_curses.cc b/src/breadcrumb_curses.cc new file mode 100644 index 0000000..eb05d4d --- /dev/null +++ b/src/breadcrumb_curses.cc @@ -0,0 +1,458 @@ +/** + * Copyright (c) 2022, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "breadcrumb_curses.hh" + +#include "base/itertools.hh" +#include "itertools.similar.hh" +#include "log_format_fwd.hh" +#include "logfile.hh" + +using namespace lnav::roles::literals; + +breadcrumb_curses::breadcrumb_curses() +{ + this->bc_match_search_overlay.sos_parent = this; + this->bc_match_source.set_reverse_selection(true); + this->bc_match_view.set_selectable(true); + this->bc_match_view.set_overlay_source(&this->bc_match_search_overlay); + this->bc_match_view.set_sub_source(&this->bc_match_source); + this->bc_match_view.set_height(0_vl); + this->bc_match_view.set_show_scrollbar(true); + this->bc_match_view.set_default_role(role_t::VCR_POPUP); + this->add_child_view(&this->bc_match_view); +} + +void +breadcrumb_curses::do_update() +{ + if (!this->bc_line_source) { + return; + } + + size_t crumb_index = 0; + size_t sel_crumb_offset = 0; + auto width = static_cast(getmaxx(this->bc_window)); + auto crumbs = this->bc_focused_crumbs.empty() ? this->bc_line_source() + : this->bc_focused_crumbs; + if (this->bc_last_selected_crumb + && this->bc_last_selected_crumb.value() >= crumbs.size()) + { + this->bc_last_selected_crumb = crumbs.size() - 1; + } + attr_line_t crumbs_line; + for (const auto& crumb : crumbs) { + auto accum_width + = utf8_string_length(crumbs_line.get_string()).template unwrap(); + auto elem_width = utf8_string_length(crumb.c_display_value.get_string()) + .template unwrap(); + auto is_selected = this->bc_selected_crumb + && (crumb_index == this->bc_selected_crumb.value()); + + if (is_selected && ((accum_width + elem_width) > width)) { + crumbs_line.clear(); + crumbs_line.append("\u22ef\u276d"_breadcrumb); + accum_width = 2; + } + + crumbs_line.append(crumb.c_display_value); + if (is_selected) { + sel_crumb_offset = accum_width; + crumbs_line.get_attrs().emplace_back( + line_range{ + (int) (crumbs_line.length() + - crumb.c_display_value.length()), + (int) crumbs_line.length(), + }, + VC_STYLE.template value(text_attrs{A_REVERSE})); + } + crumb_index += 1; + crumbs_line.append("\u276d"_breadcrumb); + } + + line_range lr{0, static_cast(width)}; + view_curses::mvwattrline( + this->bc_window, this->bc_y, 0, crumbs_line, lr, role_t::VCR_STATUS); + + if (this->bc_selected_crumb) { + this->bc_match_view.set_x(sel_crumb_offset); + } + view_curses::do_update(); +} + +void +breadcrumb_curses::reload_data() +{ + if (!this->bc_selected_crumb) { + return; + } + + auto& selected_crumb_ref + = this->bc_focused_crumbs[this->bc_selected_crumb.value()]; + this->bc_possible_values = selected_crumb_ref.c_possibility_provider(); + + nonstd::optional selected_value; + this->bc_similar_values = this->bc_possible_values + | lnav::itertools::similar_to( + [](const auto& elem) { return elem.p_key; }, + this->bc_current_search, + 128) + | lnav::itertools::sort_with([](const auto& lhs, const auto& rhs) { + return strnatcasecmp(lhs.p_key.size(), + lhs.p_key.data(), + rhs.p_key.size(), + rhs.p_key.data()) + < 0; + }); + if (selected_crumb_ref.c_key.is() + && selected_crumb_ref.c_expected_input + != breadcrumb::crumb::expected_input_t::anything) + { + auto& selected_crumb_key = selected_crumb_ref.c_key.get(); + auto found_poss_opt = this->bc_similar_values + | lnav::itertools::find_if([&selected_crumb_key](const auto& elem) { + return elem.p_key == selected_crumb_key; + }); + + if (found_poss_opt) { + selected_value = std::distance(this->bc_similar_values.begin(), + found_poss_opt.value()); + } else { + selected_value = 0; + } + } + + auto matches = attr_line_t().join( + this->bc_similar_values + | lnav::itertools::map(&breadcrumb::possibility::p_display_value), + "\n"); + this->bc_match_source.replace_with(matches); + auto width = this->bc_possible_values + | lnav::itertools::fold( + [](const auto& match, auto& accum) { + auto mlen = match.p_display_value.length(); + if (mlen > accum) { + return mlen; + } + return accum; + }, + selected_crumb_ref.c_display_value.length()); + + if (static_cast(selected_crumb_ref.c_search_placeholder.size()) + > width) + { + width = selected_crumb_ref.c_search_placeholder.size(); + } + this->bc_match_view.set_height(vis_line_t( + std::min(this->bc_match_source.get_lines().size() + 1, size_t{4}))); + this->bc_match_view.set_width(width + 3); + this->bc_match_view.set_needs_update(); + this->bc_match_view.set_selection( + vis_line_t(selected_value.value_or(-1_vl))); + this->bc_match_view.scroll_selection_into_view(); + this->bc_match_view.reload_data(); + this->set_needs_update(); +} + +void +breadcrumb_curses::focus() +{ + this->bc_focused_crumbs = this->bc_line_source(); + if (this->bc_focused_crumbs.empty()) { + return; + } + + this->bc_current_search.clear(); + if (this->bc_last_selected_crumb + && this->bc_last_selected_crumb.value() + >= this->bc_focused_crumbs.size()) + { + this->bc_last_selected_crumb = this->bc_focused_crumbs.size() - 1; + } + this->bc_selected_crumb = this->bc_last_selected_crumb.value_or(0); + this->reload_data(); +} + +void +breadcrumb_curses::blur() +{ + this->bc_last_selected_crumb = this->bc_selected_crumb; + this->bc_focused_crumbs.clear(); + this->bc_selected_crumb = nonstd::nullopt; + this->bc_current_search.clear(); + this->bc_match_view.set_height(0_vl); + this->bc_match_view.set_selection(-1_vl); + this->bc_match_source.clear(); + this->set_needs_update(); +} + +bool +breadcrumb_curses::handle_key(int ch) +{ + bool retval = false; + + switch (ch) { + case KEY_CTRL_A: + if (this->bc_selected_crumb) { + this->bc_selected_crumb = 0; + this->bc_current_search.clear(); + this->reload_data(); + } + retval = true; + break; + case KEY_CTRL_E: + if (this->bc_selected_crumb) { + this->bc_selected_crumb = this->bc_focused_crumbs.size() - 1; + this->bc_current_search.clear(); + this->reload_data(); + } + retval = true; + break; + case KEY_BTAB: + case KEY_LEFT: + if (this->bc_selected_crumb) { + if (this->bc_selected_crumb.value() > 0) { + this->bc_selected_crumb + = this->bc_selected_crumb.value() - 1; + } else { + this->bc_selected_crumb + = this->bc_focused_crumbs.size() - 1; + } + this->bc_current_search.clear(); + this->reload_data(); + } + retval = true; + break; + case '\t': + case KEY_RIGHT: + if (this->bc_selected_crumb) { + this->perform_selection(perform_behavior_t::if_different); + this->blur(); + this->focus(); + this->reload_data(); + if (this->bc_selected_crumb.value() + < this->bc_focused_crumbs.size() - 1) { + this->bc_selected_crumb + = this->bc_selected_crumb.value() + 1; + retval = true; + } + this->bc_current_search.clear(); + this->reload_data(); + } else { + retval = true; + } + break; + case KEY_HOME: + this->bc_match_view.set_selection(0_vl); + retval = true; + break; + case KEY_END: + this->bc_match_view.set_selection( + this->bc_match_view.get_inner_height() - 1_vl); + retval = true; + break; + case KEY_NPAGE: + this->bc_match_view.shift_selection(3); + retval = true; + break; + case KEY_PPAGE: + this->bc_match_view.shift_selection(-3); + retval = true; + break; + case KEY_UP: + this->bc_match_view.shift_selection(-1); + retval = true; + break; + case KEY_DOWN: + this->bc_match_view.shift_selection(1); + retval = true; + break; + case 0x7f: + case KEY_BACKSPACE: + if (!this->bc_current_search.empty()) { + this->bc_current_search.pop_back(); + this->reload_data(); + } + retval = true; + break; + case KEY_ENTER: + case '\r': + this->perform_selection(perform_behavior_t::always); + break; + default: + if (isprint(ch)) { + this->bc_current_search.push_back(ch); + this->reload_data(); + retval = true; + } + break; + } + + if (!retval) { + this->blur(); + } + this->set_needs_update(); + return retval; +} + +void +breadcrumb_curses::perform_selection( + breadcrumb_curses::perform_behavior_t behavior) +{ + if (!this->bc_selected_crumb) { + return; + } + + auto& selected_crumb_ref + = this->bc_focused_crumbs[this->bc_selected_crumb.value()]; + auto match_sel = this->bc_match_view.get_selection(); + if (match_sel >= 0 + && match_sel < vis_line_t(this->bc_similar_values.size())) { + const auto& new_value = this->bc_similar_values[match_sel].p_key; + + switch (behavior) { + case perform_behavior_t::if_different: + if (breadcrumb::crumb::key_t{new_value} + == selected_crumb_ref.c_key) { + return; + } + break; + case perform_behavior_t::always: + break; + } + selected_crumb_ref.c_performer(new_value); + } else if (!this->bc_current_search.empty()) { + switch (selected_crumb_ref.c_expected_input) { + case breadcrumb::crumb::expected_input_t::exact: + break; + case breadcrumb::crumb::expected_input_t::index: + case breadcrumb::crumb::expected_input_t::index_or_exact: { + size_t index; + + if (sscanf(this->bc_current_search.c_str(), "%zu", &index) == 1) + { + selected_crumb_ref.c_performer(index); + } + break; + } + case breadcrumb::crumb::expected_input_t::anything: + selected_crumb_ref.c_performer(this->bc_current_search); + break; + } + } +} + +bool +breadcrumb_curses::search_overlay_source::list_value_for_overlay( + const listview_curses& lv, + int y, + int bottom, + vis_line_t line, + attr_line_t& value_out) +{ + if (y == 0) { + auto* parent = this->sos_parent; + auto sel_opt = parent->bc_focused_crumbs + | lnav::itertools::nth(parent->bc_selected_crumb); + auto exp_input = sel_opt + | lnav::itertools::map(&breadcrumb::crumb::c_expected_input) + | lnav::itertools::unwrap_or( + breadcrumb::crumb::expected_input_t::exact); + + value_out.with_attr_for_all(VC_STYLE.value(text_attrs{A_UNDERLINE})); + + if (!parent->bc_current_search.empty()) { + value_out = parent->bc_current_search; + + role_t combobox_role = role_t::VCR_STATUS; + switch (exp_input) { + case breadcrumb::crumb::expected_input_t::exact: + if (parent->bc_similar_values.empty()) { + combobox_role = role_t::VCR_ALERT_STATUS; + } + break; + case breadcrumb::crumb::expected_input_t::index: { + size_t index; + + if (sscanf(parent->bc_current_search.c_str(), "%zu", &index) + != 1 + || index < 0 + || (index + >= (sel_opt + | lnav::itertools::map([](const auto& cr) { + return cr->c_possible_range.value_or(0); + }) + | lnav::itertools::unwrap_or(size_t{0})))) + { + combobox_role = role_t::VCR_ALERT_STATUS; + } + break; + } + case breadcrumb::crumb::expected_input_t::index_or_exact: { + size_t index; + + if (sscanf(parent->bc_current_search.c_str(), "%zu", &index) + == 1) { + if (index < 0 + || (index + >= (sel_opt + | lnav::itertools::map([](const auto& cr) { + return cr->c_possible_range.value_or( + 0); + }) + | lnav::itertools::unwrap_or(size_t{0})))) + { + combobox_role = role_t::VCR_ALERT_STATUS; + } + } else if (parent->bc_similar_values.empty()) { + combobox_role = role_t::VCR_ALERT_STATUS; + } + break; + } + case breadcrumb::crumb::expected_input_t::anything: + break; + } + value_out.with_attr_for_all(VC_ROLE.value(combobox_role)); + return true; + } + if (parent->bc_selected_crumb) { + auto& selected_crumb_ref + = parent->bc_focused_crumbs[parent->bc_selected_crumb.value()]; + + if (!selected_crumb_ref.c_search_placeholder.empty()) { + value_out = selected_crumb_ref.c_search_placeholder; + value_out.with_attr_for_all( + VC_ROLE.value(role_t::VCR_INACTIVE_STATUS)); + return true; + } + } + } + + return false; +} diff --git a/src/breadcrumb_curses.hh b/src/breadcrumb_curses.hh new file mode 100644 index 0000000..06218c3 --- /dev/null +++ b/src/breadcrumb_curses.hh @@ -0,0 +1,107 @@ +/** + * Copyright (c) 2022, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef lnav_breadcrumb_curses_hh +#define lnav_breadcrumb_curses_hh + +#include +#include +#include + +#include "plain_text_source.hh" +#include "textview_curses.hh" +#include "view_curses.hh" + +class breadcrumb_curses : public view_curses { +public: + breadcrumb_curses(); + + void set_y(int y) + { + this->bc_y = y; + this->bc_match_view.set_y(y + 1); + } + + int get_y() const { return this->bc_y; } + + void set_window(WINDOW* win) + { + this->bc_window = win; + this->bc_match_view.set_window(win); + } + + void set_line_source(std::function()> ls) + { + this->bc_line_source = std::move(ls); + } + + void focus(); + void blur(); + + bool handle_key(int ch); + + void do_update() override; + + void reload_data(); + +private: + class search_overlay_source : public list_overlay_source { + public: + bool list_value_for_overlay(const listview_curses& lv, + int y, + int bottom, + vis_line_t line, + attr_line_t& value_out) override; + + breadcrumb_curses* sos_parent{nullptr}; + }; + + enum class perform_behavior_t { + always, + if_different, + }; + + void perform_selection(perform_behavior_t behavior); + + WINDOW* bc_window{nullptr}; + std::function()> bc_line_source; + int bc_y{0}; + std::vector bc_focused_crumbs; + nonstd::optional bc_selected_crumb; + nonstd::optional bc_last_selected_crumb; + std::vector bc_possible_values; + std::vector bc_similar_values; + std::string bc_current_search; + + plain_text_source bc_match_source; + search_overlay_source bc_match_search_overlay; + textview_curses bc_match_view; +}; + +#endif diff --git a/src/byte_array.hh b/src/byte_array.hh new file mode 100644 index 0000000..9057f96 --- /dev/null +++ b/src/byte_array.hh @@ -0,0 +1,148 @@ +/** + * Copyright (c) 2007-2013, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef byte_array_hh +#define byte_array_hh + +#include +#include + +#include +#include +#include + +#include "base/lnav_log.hh" +#include "fmt/format.h" +#include "optional.hpp" + +template +struct byte_array { + static constexpr size_t BYTE_COUNT = COUNT * sizeof(T); + static constexpr size_t STRING_SIZE = BYTE_COUNT * 2 + 1; + + byte_array() = default; + + static byte_array from(std::initializer_list bytes) + { + byte_array retval; + size_t index = 0; + + for (const auto by : bytes) { + retval.ba_data[index++] = by; + } + return retval; + } + + byte_array(const byte_array& other) + { + memcpy(this->ba_data, other.ba_data, BYTE_COUNT); + } + + bool operator<(const byte_array& other) const + { + return memcmp(this->ba_data, other.ba_data, BYTE_COUNT) < 0; + } + + bool operator!=(const byte_array& other) const + { + return memcmp(this->ba_data, other.ba_data, BYTE_COUNT) != 0; + } + + bool operator==(const byte_array& other) const + { + return memcmp(this->ba_data, other.ba_data, BYTE_COUNT) == 0; + } + + void clear() { memset(this->ba_data, 0, BYTE_COUNT); } + + template + void to_string(OutputIt out, + nonstd::optional separator = nonstd::nullopt) const + { + for (size_t lpc = 0; lpc < BYTE_COUNT; lpc++) { + if (lpc > 0 && separator) { + *out = separator.value(); + } + fmt::format_to(out, FMT_STRING("{:02x}"), this->ba_data[lpc]); + } + } + + std::string to_uuid_string() const + { + return fmt::format( + FMT_STRING("{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-" + "{:02x}{:02x}-{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}"), + this->ba_data[0 % BYTE_COUNT], + this->ba_data[1 % BYTE_COUNT], + this->ba_data[2 % BYTE_COUNT], + this->ba_data[3 % BYTE_COUNT], + this->ba_data[4 % BYTE_COUNT], + this->ba_data[5 % BYTE_COUNT], + this->ba_data[6 % BYTE_COUNT], + this->ba_data[7 % BYTE_COUNT], + this->ba_data[8 % BYTE_COUNT], + this->ba_data[9 % BYTE_COUNT], + this->ba_data[10 % BYTE_COUNT], + this->ba_data[11 % BYTE_COUNT], + this->ba_data[12 % BYTE_COUNT], + this->ba_data[13 % BYTE_COUNT], + this->ba_data[14 % BYTE_COUNT], + this->ba_data[15 % BYTE_COUNT]); + } + + std::string to_string(nonstd::optional separator + = nonstd::nullopt) const + { + std::string retval; + + retval.reserve(STRING_SIZE); + this->to_string(std::back_inserter(retval), separator); + return retval; + } + + const unsigned char* in() const { return this->ba_data; } + + T* out(int offset = 0) + { + T* ptr = (T*) this->ba_data; + + return &ptr[offset]; + } + + unsigned char ba_data[BYTE_COUNT]{}; +}; + +template +std::ostream& +operator<<(std::ostream& os, const byte_array& ba) +{ + os << ba.to_string(); + return os; +} + +#endif diff --git a/src/collation-functions.cc b/src/collation-functions.cc new file mode 100644 index 0000000..8ad474b --- /dev/null +++ b/src/collation-functions.cc @@ -0,0 +1,155 @@ +/** + * Copyright (c) 2013, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @file logfile_sub_source.hh + */ + +#include +#include +#include +#include +#include + +#include "base/strnatcmp.h" +#include "config.h" +#include "log_level.hh" + +constexpr int MAX_ADDR_LEN = 128; + +static int +try_inet_pton(int p_len, const char* p, char* n) +{ + static int ADDR_FAMILIES[] = {AF_INET, AF_INET6}; + + char buf[MAX_ADDR_LEN + 1]; + int retval = AF_MAX; + + strncpy(buf, p, p_len); + buf[p_len] = '\0'; + for (int family : ADDR_FAMILIES) { + if (inet_pton(family, buf, n) == 1) { + retval = family; + break; + } + } + + return retval; +} + +static int +convert_v6_to_v4(int family, char* n) +{ + struct in6_addr* ia = (struct in6_addr*) n; + + if (family == AF_INET6 + && (IN6_IS_ADDR_V4COMPAT(ia) || IN6_IS_ADDR_V4MAPPED(ia))) + { + family = AF_INET; + memmove(n, n + 12, sizeof(struct in_addr)); + } + + return family; +} + +static int +ipaddress(void* ptr, int a_len, const void* a_in, int b_len, const void* b_in) +{ + char a_addr[sizeof(struct in6_addr)], b_addr[sizeof(struct in6_addr)]; + const char *a_str = (const char*) a_in, *b_str = (const char*) b_in; + int a_family, b_family, retval; + + if ((a_len > MAX_ADDR_LEN) || (b_len > MAX_ADDR_LEN)) { + return strnatcasecmp(a_len, a_str, b_len, b_str); + } + + int v4res = 0; + if (ipv4cmp(a_len, a_str, b_len, b_str, &v4res)) { + return v4res; + } + + a_family = try_inet_pton(a_len, a_str, a_addr); + b_family = try_inet_pton(b_len, b_str, b_addr); + + if (a_family == AF_MAX && b_family == AF_MAX) { + return strnatcasecmp(a_len, a_str, b_len, b_str); + } else if (a_family == AF_MAX && b_family != AF_MAX) { + retval = -1; + } else if (a_family != AF_MAX && b_family == AF_MAX) { + retval = 1; + } else { + a_family = convert_v6_to_v4(a_family, a_addr); + b_family = convert_v6_to_v4(b_family, b_addr); + if (a_family == b_family) { + retval = memcmp(a_addr, + b_addr, + a_family == AF_INET ? sizeof(struct in_addr) + : sizeof(struct in6_addr)); + } else if (a_family == AF_INET) { + retval = -1; + } else { + retval = 1; + } + } + + return retval; +} + +static int +sql_strnatcmp( + void* ptr, int a_len, const void* a_in, int b_len, const void* b_in) +{ + return strnatcmp(a_len, (char*) a_in, b_len, (char*) b_in); +} + +static int +sql_strnatcasecmp( + void* ptr, int a_len, const void* a_in, int b_len, const void* b_in) +{ + return strnatcasecmp(a_len, (char*) a_in, b_len, (char*) b_in); +} + +static int +sql_loglevelcmp( + void* ptr, int a_len, const void* a_in, int b_len, const void* b_in) +{ + return levelcmp((const char*) a_in, a_len, (const char*) b_in, b_len); +} + +int +register_collation_functions(sqlite3* db) +{ + sqlite3_create_collation(db, "ipaddress", SQLITE_UTF8, nullptr, ipaddress); + sqlite3_create_collation( + db, "naturalcase", SQLITE_UTF8, nullptr, sql_strnatcmp); + sqlite3_create_collation( + db, "naturalnocase", SQLITE_UTF8, nullptr, sql_strnatcasecmp); + sqlite3_create_collation( + db, "loglevel", SQLITE_UTF8, nullptr, sql_loglevelcmp); + + return 0; +} diff --git a/src/column_namer.cc b/src/column_namer.cc new file mode 100644 index 0000000..ba2d872 --- /dev/null +++ b/src/column_namer.cc @@ -0,0 +1,114 @@ +/** + * Copyright (c) 2019, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @file column_namer.cc + */ + +#include + +#include "column_namer.hh" + +#include "base/itertools.hh" +#include "base/lnav_log.hh" +#include "config.h" +#include "sql_util.hh" + +const char column_namer::BUILTIN_COL[] = "col"; + +column_namer::column_namer(language lang) : cn_language(lang) {} + +bool +column_namer::existing_name(const string_fragment& in_name) const +{ + switch (this->cn_language) { + case language::SQL: { + auto upped = toupper(in_name.to_string()); + + if (std::binary_search( + std::begin(sql_keywords), std::end(sql_keywords), upped)) { + return true; + } + break; + } + case language::JSON: + break; + } + + if (this->cn_builtin_names | lnav::itertools::find(in_name)) { + return true; + } + + if (this->cn_names | lnav::itertools::find(in_name)) { + return true; + } + + return false; +} + +string_fragment +column_namer::add_column(const string_fragment& in_name) +{ + string_fragment base_name; + string_fragment retval; + fmt::memory_buffer buf; + int num = 0; + + if (in_name.empty()) { + base_name = string_fragment::from_const(BUILTIN_COL); + } else { + base_name = in_name; + } + + retval = base_name; + auto counter_iter = this->cn_name_counters.find(retval); + if (counter_iter != this->cn_name_counters.end()) { + num = ++counter_iter->second; + fmt::format_to( + std::back_inserter(buf), FMT_STRING("{}_{}"), base_name, num); + retval = string_fragment::from_memory_buffer(buf); + } + + while (this->existing_name(retval)) { + if (num == 0) { + auto counter_name = retval.to_owned(this->cn_alloc); + this->cn_name_counters[counter_name] = num; + } + + log_debug( + "column name already exists: %.*s", retval.length(), retval.data()); + fmt::format_to( + std::back_inserter(buf), FMT_STRING("{}_{}"), base_name, num); + retval = string_fragment::from_memory_buffer(buf); + num += 1; + } + + retval = retval.to_owned(this->cn_alloc); + this->cn_names.emplace_back(retval); + + return retval; +} diff --git a/src/column_namer.hh b/src/column_namer.hh new file mode 100644 index 0000000..a98d9ef --- /dev/null +++ b/src/column_namer.hh @@ -0,0 +1,70 @@ +/** + * Copyright (c) 2013, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @file column_namer.hh + */ + +#ifndef lnav_column_namer_hh +#define lnav_column_namer_hh + +#include +#include +#include + +#include "ArenaAlloc/arenaalloc.h" +#include "base/intern_string.hh" + +class column_namer { +public: + enum class language { + SQL, + JSON, + }; + + column_namer(language lang); + + bool existing_name(const string_fragment& in_name) const; + + string_fragment add_column(const string_fragment& in_name); + + static const char BUILTIN_COL[]; + + ArenaAlloc::Alloc cn_alloc; + language cn_language; + std::vector cn_builtin_names{string_fragment(BUILTIN_COL)}; + std::vector cn_names; + std::unordered_map< + string_fragment, + size_t, + frag_hasher, + std::equal_to, + ArenaAlloc::Alloc>> + cn_name_counters; +}; + +#endif diff --git a/src/command_executor.cc b/src/command_executor.cc new file mode 100644 index 0000000..e9a0296 --- /dev/null +++ b/src/command_executor.cc @@ -0,0 +1,1135 @@ +/** + * Copyright (c) 2015, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include "command_executor.hh" + +#include "base/ansi_scrubber.hh" +#include "base/fs_util.hh" +#include "base/injector.hh" +#include "base/itertools.hh" +#include "base/string_util.hh" +#include "bound_tags.hh" +#include "config.h" +#include "db_sub_source.hh" +#include "help_text_formatter.hh" +#include "lnav.hh" +#include "lnav.indexing.hh" +#include "lnav_config.hh" +#include "lnav_util.hh" +#include "log_format_loader.hh" +#include "readline_highlighters.hh" +#include "service_tags.hh" +#include "shlex.hh" +#include "sql_util.hh" +#include "vtab_module.hh" +#include "yajlpp/json_ptr.hh" + +using namespace lnav::roles::literals; + +exec_context INIT_EXEC_CONTEXT; + +static const std::string MSG_FORMAT_STMT = R"( +SELECT count(*) AS total, min(log_line) AS log_line, log_msg_format + FROM all_logs + GROUP BY log_msg_format + ORDER BY total DESC +)"; + +int +sql_progress(const struct log_cursor& lc) +{ + ssize_t total = lnav_data.ld_log_source.text_line_count(); + off_t off = lc.lc_curr_line; + + if (off < 0 || off >= total) { + return 0; + } + + if (lnav_data.ld_window == nullptr) { + return 0; + } + + if (!lnav_data.ld_looping) { + return 1; + } + + static sig_atomic_t sql_counter = 0; + + if (ui_periodic_timer::singleton().time_to_update(sql_counter)) { + lnav_data.ld_bottom_source.update_loading(off, total); + lnav_data.ld_status_refresher(); + } + + return 0; +} + +void +sql_progress_finished() +{ + if (lnav_data.ld_window == nullptr) { + return; + } + + lnav_data.ld_bottom_source.update_loading(0, 0); + lnav_data.ld_status_refresher(); + lnav_data.ld_views[LNV_DB].redo_search(); +} + +static Result execute_from_file( + exec_context& ec, + const ghc::filesystem::path& path, + int line_number, + const std::string& cmdline); + +Result +execute_command(exec_context& ec, const std::string& cmdline) +{ + std::vector args; + + log_info("Executing: %s", cmdline.c_str()); + + split_ws(cmdline, args); + + if (!args.empty()) { + readline_context::command_map_t::iterator iter; + + if ((iter = lnav_commands.find(args[0])) == lnav_commands.end()) { + return ec.make_error("unknown command - {}", args[0]); + } + + ec.ec_current_help = &iter->second->c_help; + auto retval = iter->second->c_func(ec, cmdline, args); + if (retval.isErr()) { + auto um = retval.unwrapErr(); + + ec.add_error_context(um); + ec.ec_current_help = nullptr; + return Err(um); + } + ec.ec_current_help = nullptr; + + return retval; + } + + return ec.make_error("no command to execute"); +} + +static Result, + lnav::console::user_message> +bind_sql_parameters(exec_context& ec, sqlite3_stmt* stmt) +{ + std::map retval; + auto param_count = sqlite3_bind_parameter_count(stmt); + for (int lpc = 0; lpc < param_count; lpc++) { + std::map::iterator ov_iter; + const auto* name = sqlite3_bind_parameter_name(stmt, lpc + 1); + if (name == nullptr) { + auto um + = lnav::console::user_message::error("invalid SQL statement") + .with_reason( + "using a question-mark (?) for bound variables " + "is not supported, only named bound parameters " + "are supported") + .with_help( + "named parameters start with a dollar-sign " + "($) or colon (:) followed by the variable name"); + ec.add_error_context(um); + + return Err(um); + } + + ov_iter = ec.ec_override.find(name); + if (ov_iter != ec.ec_override.end()) { + sqlite3_bind_text(stmt, + lpc, + ov_iter->second.c_str(), + ov_iter->second.length(), + SQLITE_TRANSIENT); + } else if (name[0] == '$') { + const auto& lvars = ec.ec_local_vars.top(); + const auto& gvars = ec.ec_global_vars; + std::map::const_iterator local_var, + global_var; + const char* env_value; + + if (lnav_data.ld_window) { + char buf[32]; + int lines, cols; + + getmaxyx(lnav_data.ld_window, lines, cols); + if (strcmp(name, "$LINES") == 0) { + snprintf(buf, sizeof(buf), "%d", lines); + sqlite3_bind_text(stmt, lpc + 1, buf, -1, SQLITE_TRANSIENT); + } else if (strcmp(name, "$COLS") == 0) { + snprintf(buf, sizeof(buf), "%d", cols); + sqlite3_bind_text(stmt, lpc + 1, buf, -1, SQLITE_TRANSIENT); + } + } + + if ((local_var = lvars.find(&name[1])) != lvars.end()) { + mapbox::util::apply_visitor( + sqlitepp::bind_visitor(stmt, lpc + 1), local_var->second); + retval[name] = local_var->second; + } else if ((global_var = gvars.find(&name[1])) != gvars.end()) { + mapbox::util::apply_visitor( + sqlitepp::bind_visitor(stmt, lpc + 1), global_var->second); + retval[name] = global_var->second; + } else if ((env_value = getenv(&name[1])) != nullptr) { + sqlite3_bind_text(stmt, lpc + 1, env_value, -1, SQLITE_STATIC); + retval[name] = env_value; + } + } else if (name[0] == ':' && ec.ec_line_values != nullptr) { + for (auto& lv : ec.ec_line_values->lvv_values) { + if (lv.lv_meta.lvm_name != &name[1]) { + continue; + } + switch (lv.lv_meta.lvm_kind) { + case value_kind_t::VALUE_BOOLEAN: + sqlite3_bind_int64(stmt, lpc + 1, lv.lv_value.i); + retval[name] = fmt::to_string(lv.lv_value.i); + break; + case value_kind_t::VALUE_FLOAT: + sqlite3_bind_double(stmt, lpc + 1, lv.lv_value.d); + retval[name] = fmt::to_string(lv.lv_value.d); + break; + case value_kind_t::VALUE_INTEGER: + sqlite3_bind_int64(stmt, lpc + 1, lv.lv_value.i); + retval[name] = fmt::to_string(lv.lv_value.i); + break; + case value_kind_t::VALUE_NULL: + sqlite3_bind_null(stmt, lpc + 1); + retval[name] = db_label_source::NULL_STR; + break; + default: + sqlite3_bind_text(stmt, + lpc + 1, + lv.text_value(), + lv.text_length(), + SQLITE_TRANSIENT); + retval[name] = lv.to_string(); + break; + } + } + } else { + sqlite3_bind_null(stmt, lpc + 1); + log_warning("Could not bind variable: %s", name); + retval[name] = db_label_source::NULL_STR; + } + } + + return Ok(retval); +} + +static void +execute_search(const std::string& search_cmd) +{ + lnav_data.ld_view_stack.top() | [&search_cmd](auto tc) { + auto search_term + = string_fragment(search_cmd) + .find_right_boundary(0, string_fragment::tag1{'\n'}) + .to_string(); + tc->execute_search(search_term); + }; +} + +Result +execute_sql(exec_context& ec, const std::string& sql, std::string& alt_msg) +{ + db_label_source& dls = lnav_data.ld_db_row_source; + auto_mem stmt(sqlite3_finalize); + struct timeval start_tv, end_tv; + std::string stmt_str = trim(sql); + std::string retval; + int retcode = SQLITE_OK; + + log_info("Executing SQL: %s", sql.c_str()); + + auto old_mode = lnav_data.ld_mode; + lnav_data.ld_mode = ln_mode_t::BUSY; + auto mode_fin = finally([old_mode]() { lnav_data.ld_mode = old_mode; }); + lnav_data.ld_bottom_source.grep_error(""); + + if (startswith(stmt_str, ".")) { + std::vector args; + split_ws(stmt_str, args); + + auto* sql_cmd_map = injector::get(); + auto cmd_iter = sql_cmd_map->find(args[0]); + + if (cmd_iter != sql_cmd_map->end()) { + ec.ec_current_help = &cmd_iter->second->c_help; + auto retval = cmd_iter->second->c_func(ec, stmt_str, args); + ec.ec_current_help = nullptr; + + return retval; + } + } + + if (stmt_str == ".msgformats") { + stmt_str = MSG_FORMAT_STMT; + } + + ec.ec_accumulator->clear(); + + const auto& source = ec.ec_source.back(); + sql_progress_guard progress_guard(sql_progress, + sql_progress_finished, + source.s_location, + source.s_content); + gettimeofday(&start_tv, nullptr); + + const auto* curr_stmt = stmt_str.c_str(); + auto last_is_readonly = false; + while (curr_stmt != nullptr) { + const char* tail = nullptr; + while (isspace(*curr_stmt)) { + curr_stmt += 1; + } + retcode = sqlite3_prepare_v2( + lnav_data.ld_db.in(), curr_stmt, -1, stmt.out(), &tail); + if (retcode != SQLITE_OK) { + const char* errmsg = sqlite3_errmsg(lnav_data.ld_db); + + alt_msg = ""; + + auto um = lnav::console::user_message::error( + "failed to compile SQL statement") + .with_reason(errmsg) + .with_snippets(ec.ec_source); + + auto annotated_sql = annotate_sql_with_error( + lnav_data.ld_db.in(), curr_stmt, tail); + auto loc = um.um_snippets.back().s_location; + if (curr_stmt == stmt_str.c_str()) { + um.um_snippets.pop_back(); + } else { + auto tail_iter = stmt_str.begin(); + + std::advance(tail_iter, (curr_stmt - stmt_str.c_str())); + loc.sl_line_number + += std::count(stmt_str.begin(), tail_iter, '\n'); + } + + um.with_snippet(lnav::console::snippet::from(loc, annotated_sql)); + + return Err(um); + } + if (stmt == nullptr) { + retcode = SQLITE_DONE; + break; + } +#ifdef HAVE_SQLITE3_STMT_READONLY + last_is_readonly = sqlite3_stmt_readonly(stmt.in()); + if (ec.is_read_only() && !last_is_readonly) { + return ec.make_error( + "modifying statements are not allowed in this context: {}", + sql); + } +#endif + bool done = false; + + auto bound_values = TRY(bind_sql_parameters(ec, stmt.in())); + if (lnav_data.ld_rl_view != nullptr) { + if (lnav_data.ld_rl_view) { + lnav_data.ld_rl_view->set_attr_value( + lnav::console::user_message::info( + attr_line_t("executing SQL statement, press ") + .append("CTRL+]"_hotkey) + .append(" to cancel")) + .to_attr_line()); + lnav_data.ld_rl_view->do_update(); + } + } + + ec.ec_sql_callback(ec, stmt.in()); + while (!done) { + retcode = sqlite3_step(stmt.in()); + + switch (retcode) { + case SQLITE_OK: + case SQLITE_DONE: + done = true; + break; + + case SQLITE_ROW: + ec.ec_sql_callback(ec, stmt.in()); + break; + + default: { + attr_line_t bound_note; + + if (!bound_values.empty()) { + bound_note.append( + "the bound parameters are set as follows:\n"); + for (const auto& bval : bound_values) { + auto val_as_str = fmt::to_string(bval.second); + auto sql_type = bval.second.match( + [](const std::string&) { return SQLITE_TEXT; }, + [](const string_fragment&) { + return SQLITE_TEXT; + }, + [](int64_t) { return SQLITE_INTEGER; }, + [](null_value_t) { return SQLITE_NULL; }, + [](double) { return SQLITE_FLOAT; }); + auto scrubbed_val = scrub_ws(val_as_str.c_str()); + truncate_to(scrubbed_val, 40); + bound_note.append(" ") + .append(lnav::roles::variable(bval.first)) + .append(":") + .append(sqlite3_type_to_string(sql_type)) + .append(" = ") + .append_quoted(scrubbed_val) + .append("\n"); + } + } + + log_error("sqlite3_step error code: %d", retcode); + auto um = sqlite3_error_to_user_message(lnav_data.ld_db) + .with_snippets(ec.ec_source) + .with_note(bound_note); + + return Err(um); + } + } + } + + curr_stmt = tail; + } + + if (lnav_data.ld_rl_view != nullptr) { + lnav_data.ld_rl_view->clear_value(); + } + + gettimeofday(&end_tv, nullptr); + if (retcode == SQLITE_DONE) { + if (lnav_data.ld_log_source.is_line_meta_changed()) { + lnav_data.ld_log_source.text_filters_changed(); + lnav_data.ld_views[LNV_LOG].reload_data(); + } + lnav_data.ld_filter_view.reload_data(); + lnav_data.ld_files_view.reload_data(); + lnav_data.ld_views[LNV_DB].reload_data(); + lnav_data.ld_views[LNV_DB].set_left(0); + + lnav_data.ld_active_files.fc_files + | lnav::itertools::for_each(&logfile::dump_stats); + if (ec.ec_sql_callback != sql_callback) { + retval = ec.ec_accumulator->get_string(); + } else if (!dls.dls_rows.empty()) { + if (lnav_data.ld_flags & LNF_HEADLESS) { + if (ec.ec_local_vars.size() == 1) { + ensure_view(&lnav_data.ld_views[LNV_DB]); + } + + retval = ""; + alt_msg = ""; + } else if (dls.dls_rows.size() == 1) { + auto& row = dls.dls_rows[0]; + + if (dls.dls_headers.size() == 1) { + retval = row[0]; + } else { + for (unsigned int lpc = 0; lpc < dls.dls_headers.size(); + lpc++) + { + if (lpc > 0) { + retval.append("; "); + } + retval.append(dls.dls_headers[lpc].hm_name); + retval.push_back('='); + retval.append(row[lpc]); + } + } + } else { + int row_count = dls.dls_rows.size(); + char row_count_buf[128]; + struct timeval diff_tv; + + timersub(&end_tv, &start_tv, &diff_tv); + snprintf(row_count_buf, + sizeof(row_count_buf), + ANSI_BOLD("%'d") " row%s matched in " ANSI_BOLD( + "%ld.%03ld") " seconds", + row_count, + row_count == 1 ? "" : "s", + diff_tv.tv_sec, + std::max((long) diff_tv.tv_usec / 1000, 1L)); + retval = row_count_buf; + if (dls.has_log_time_column()) { + alt_msg = HELP_MSG_1(Q, + "to switch back to the previous view " + "at the matching 'log_time' value"); + } else { + alt_msg = ""; + } + } + } +#ifdef HAVE_SQLITE3_STMT_READONLY + else if (last_is_readonly) + { + retval = "info: No rows matched"; + alt_msg = ""; + + if (lnav_data.ld_flags & LNF_HEADLESS) { + if (ec.ec_local_vars.size() == 1) { + ensure_view(&lnav_data.ld_views[LNV_DB]); + } + } + } +#endif + } + + return Ok(retval); +} + +static Result +execute_file_contents(exec_context& ec, + const ghc::filesystem::path& path, + bool multiline) +{ + static ghc::filesystem::path stdin_path("-"); + static ghc::filesystem::path dev_stdin_path("/dev/stdin"); + + std::string retval; + FILE* file; + + if (path == stdin_path || path == dev_stdin_path) { + if (isatty(STDIN_FILENO)) { + return ec.make_error("stdin has already been consumed"); + } + file = stdin; + } else if ((file = fopen(path.c_str(), "r")) == nullptr) { + return ec.make_error("unable to open file"); + } + + int line_number = 0, starting_line_number = 0; + auto_mem line; + size_t line_max_size; + ssize_t line_size; + nonstd::optional cmdline; + + ec.ec_path_stack.emplace_back(path.parent_path()); + exec_context::output_guard og(ec); + while ((line_size = getline(line.out(), &line_max_size, file)) != -1) { + line_number += 1; + + if (trim(line.in()).empty()) { + if (multiline && cmdline) { + cmdline = cmdline.value() + "\n"; + } + continue; + } + if (line[0] == '#') { + continue; + } + + switch (line[0]) { + case ':': + case '/': + case ';': + case '|': + if (cmdline) { + retval = TRY(execute_from_file( + ec, path, starting_line_number, trim(cmdline.value()))); + } + + starting_line_number = line_number; + cmdline = std::string(line); + break; + default: + if (multiline) { + cmdline = fmt::format("{}{}", cmdline.value(), line); + } else { + retval = TRY(execute_from_file( + ec, path, line_number, fmt::format(":{}", line.in()))); + } + break; + } + } + + if (cmdline) { + retval = TRY(execute_from_file( + ec, path, starting_line_number, trim(cmdline.value()))); + } + + if (file == stdin) { + if (isatty(STDOUT_FILENO)) { + log_perror(dup2(STDOUT_FILENO, STDIN_FILENO)); + } + } else { + fclose(file); + } + ec.ec_path_stack.pop_back(); + + return Ok(retval); +} + +Result +execute_file(exec_context& ec, const std::string& path_and_args, bool multiline) +{ + available_scripts scripts; + std::vector split_args; + std::string retval, msg; + shlex lexer(path_and_args); + + log_info("Executing file: %s", path_and_args.c_str()); + + if (!lexer.split(split_args, ec.ec_local_vars.top())) { + return ec.make_error("unable to parse path"); + } + if (split_args.empty()) { + return ec.make_error("no script specified"); + } + + ec.ec_local_vars.push({}); + + auto script_name = split_args[0]; + auto& vars = ec.ec_local_vars.top(); + char env_arg_name[32]; + std::string star, open_error = "file not found"; + + add_ansi_vars(vars); + + snprintf( + env_arg_name, sizeof(env_arg_name), "%d", (int) split_args.size() - 1); + + vars["#"] = env_arg_name; + for (size_t lpc = 0; lpc < split_args.size(); lpc++) { + snprintf(env_arg_name, sizeof(env_arg_name), "%lu", lpc); + vars[env_arg_name] = split_args[lpc]; + } + for (size_t lpc = 1; lpc < split_args.size(); lpc++) { + if (lpc > 1) { + star.append(" "); + } + star.append(split_args[lpc]); + } + vars["__all__"] = star; + + std::vector paths_to_exec; + + find_format_scripts(lnav_data.ld_config_paths, scripts); + auto iter = scripts.as_scripts.find(script_name); + if (iter != scripts.as_scripts.end()) { + paths_to_exec = iter->second; + } + if (script_name == "-" || script_name == "/dev/stdin") { + paths_to_exec.push_back({script_name, "", "", ""}); + } else if (access(script_name.c_str(), R_OK) == 0) { + struct script_metadata meta; + + meta.sm_path = script_name; + extract_metadata_from_file(meta); + paths_to_exec.push_back(meta); + } else if (errno != ENOENT) { + open_error = strerror(errno); + } else { + auto script_path = ghc::filesystem::path(script_name); + + if (!script_path.is_absolute()) { + script_path = ec.ec_path_stack.back() / script_path; + } + + if (ghc::filesystem::is_regular_file(script_path)) { + struct script_metadata meta; + + meta.sm_path = script_path; + extract_metadata_from_file(meta); + paths_to_exec.push_back(meta); + } else if (errno != ENOENT) { + open_error = strerror(errno); + } + } + + if (!paths_to_exec.empty()) { + for (auto& path_iter : paths_to_exec) { + retval + = TRY(execute_file_contents(ec, path_iter.sm_path, multiline)); + } + } + ec.ec_local_vars.pop(); + + if (paths_to_exec.empty()) { + return ec.make_error( + "unknown script -- {} -- {}", script_name, open_error); + } + + return Ok(retval); +} + +Result +execute_from_file(exec_context& ec, + const ghc::filesystem::path& path, + int line_number, + const std::string& cmdline) +{ + std::string retval, alt_msg; + auto _sg = ec.enter_source( + intern_string::lookup(path.string()), line_number, cmdline); + + switch (cmdline[0]) { + case ':': + retval = TRY(execute_command(ec, cmdline.substr(1))); + break; + case '/': + execute_search(cmdline.substr(1)); + break; + case ';': + setup_logline_table(ec); + retval = TRY(execute_sql(ec, cmdline.substr(1), alt_msg)); + break; + case '|': + retval = TRY(execute_file(ec, cmdline.substr(1))); + break; + default: + retval = TRY(execute_command(ec, cmdline)); + break; + } + + log_info("%s:%d:execute result -- %s", + path.c_str(), + line_number, + retval.c_str()); + + return Ok(retval); +} + +Result +execute_any(exec_context& ec, const std::string& cmdline_with_mode) +{ + std::string retval, alt_msg, cmdline = cmdline_with_mode.substr(1); + auto _cleanup = finally([&ec] { + if (ec.is_read_write() && + // only rebuild in a script or non-interactive mode so we don't + // block the UI. + (lnav_data.ld_flags & LNF_HEADLESS || ec.ec_path_stack.size() > 1)) + { + rescan_files(); + rebuild_indexes_repeatedly(); + } + }); + + switch (cmdline_with_mode[0]) { + case ':': + retval = TRY(execute_command(ec, cmdline)); + break; + case '/': + execute_search(cmdline); + break; + case ';': + setup_logline_table(ec); + retval = TRY(execute_sql(ec, cmdline, alt_msg)); + break; + case '|': { + retval = TRY(execute_file(ec, cmdline)); + break; + } + default: + retval = TRY(execute_command(ec, cmdline)); + break; + } + + return Ok(retval); +} + +void +execute_init_commands( + exec_context& ec, + std::vector, + std::string>>& msgs) +{ + if (lnav_data.ld_cmd_init_done) { + return; + } + + nonstd::optional ec_out; + auto_fd fd_copy; + + if (!(lnav_data.ld_flags & LNF_HEADLESS)) { + auto_mem tmpout(fclose); + + tmpout = std::tmpfile(); + if (!tmpout) { + msgs.emplace_back(Err(lnav::console::user_message::error( + "Unable to open temporary output file") + .with_errno_reason()), + ""); + return; + } + fd_copy = auto_fd::dup_of(fileno(tmpout)); + ec_out = std::make_pair(tmpout.release(), fclose); + } + + auto& dls = lnav_data.ld_db_row_source; + int option_index = 1; + + { + log_info("Executing initial commands"); + exec_context::output_guard og(ec, "tmp", ec_out); + + for (auto& cmd : lnav_data.ld_commands) { + static const auto COMMAND_OPTION_SRC + = intern_string::lookup("command-option"); + + std::string alt_msg; + + wait_for_children(); + + log_debug("init cmd: %s", cmd.c_str()); + { + auto _sg + = ec.enter_source(COMMAND_OPTION_SRC, option_index++, cmd); + switch (cmd.at(0)) { + case ':': + msgs.emplace_back(execute_command(ec, cmd.substr(1)), + alt_msg); + break; + case '/': + execute_search(cmd.substr(1)); + break; + case ';': + setup_logline_table(ec); + msgs.emplace_back( + execute_sql(ec, cmd.substr(1), alt_msg), alt_msg); + break; + case '|': + msgs.emplace_back(execute_file(ec, cmd.substr(1)), + alt_msg); + break; + } + + rescan_files(); + rebuild_indexes_repeatedly(); + } + if (dls.dls_rows.size() > 1) { + ensure_view(LNV_DB); + } + } + } + lnav_data.ld_commands.clear(); + + struct stat st; + + if (ec_out && fstat(fd_copy, &st) != -1 && st.st_size > 0) { + static const auto OUTPUT_NAME = std::string("Initial command output"); + + lnav_data.ld_active_files.fc_file_names[OUTPUT_NAME] + .with_fd(std::move(fd_copy)) + .with_include_in_session(false) + .with_detect_format(false); + lnav_data.ld_files_to_front.emplace_back(OUTPUT_NAME, 0_vl); + + if (lnav_data.ld_rl_view != nullptr) { + lnav_data.ld_rl_view->set_alt_value( + HELP_MSG_1(X, "to close the file")); + } + } + + lnav_data.ld_cmd_init_done = true; +} + +int +sql_callback(exec_context& ec, sqlite3_stmt* stmt) +{ + auto& dls = lnav_data.ld_db_row_source; + + if (!sqlite3_stmt_busy(stmt)) { + dls.clear(); + + return 0; + } + + auto& chart = dls.dls_chart; + auto& vc = view_colors::singleton(); + int ncols = sqlite3_column_count(stmt); + int row_number; + int lpc, retval = 0; + auto set_vars = false; + + row_number = dls.dls_rows.size(); + dls.dls_rows.resize(row_number + 1); + if (dls.dls_headers.empty()) { + for (lpc = 0; lpc < ncols; lpc++) { + int type = sqlite3_column_type(stmt, lpc); + std::string colname = sqlite3_column_name(stmt, lpc); + bool graphable; + + graphable = ((type == SQLITE_INTEGER || type == SQLITE_FLOAT) + && !binary_search(lnav_data.ld_db_key_names.begin(), + lnav_data.ld_db_key_names.end(), + colname)); + + dls.push_header(colname, type, graphable); + if (graphable) { + auto name_for_ident_attrs = colname; + auto attrs = vc.attrs_for_ident(name_for_ident_attrs); + for (size_t attempt = 0; + chart.attrs_in_use(attrs) && attempt < 3; + attempt++) + { + name_for_ident_attrs += " "; + attrs = vc.attrs_for_ident(name_for_ident_attrs); + } + chart.with_attrs_for_ident(colname, attrs); + dls.dls_headers.back().hm_title_attrs = attrs; + } + } + set_vars = true; + } + for (lpc = 0; lpc < ncols; lpc++) { + auto* raw_value = sqlite3_column_value(stmt, lpc); + auto value_type = sqlite3_value_type(raw_value); + scoped_value_t value; + auto& hm = dls.dls_headers[lpc]; + + switch (value_type) { + case SQLITE_INTEGER: + value = (int64_t) sqlite3_value_int64(raw_value); + break; + case SQLITE_FLOAT: + value = sqlite3_value_double(raw_value); + break; + case SQLITE_NULL: + value = null_value_t{}; + break; + default: + value = string_fragment::from_bytes( + sqlite3_value_text(raw_value), + sqlite3_value_bytes(raw_value)); + break; + } + dls.push_column(value); + if ((hm.hm_column_type == SQLITE_TEXT + || hm.hm_column_type == SQLITE_NULL) + && hm.hm_sub_type == 0) + { + switch (value_type) { + case SQLITE_TEXT: + hm.hm_column_type = SQLITE_TEXT; + hm.hm_sub_type = sqlite3_value_subtype(raw_value); + break; + } + } + if (set_vars && !ec.ec_local_vars.empty() && !ec.ec_dry_run) { + if (sql_ident_needs_quote(hm.hm_name.c_str())) { + continue; + } + auto& vars = ec.ec_local_vars.top(); + + if (value.is()) { + value = value.get().to_string(); + } + vars[hm.hm_name] = value; + } + } + + return retval; +} + +std::future +pipe_callback(exec_context& ec, const std::string& cmdline, auto_fd& fd) +{ + auto out = ec.get_output(); + + if (out) { + FILE* file = *out; + + return std::async(std::launch::async, [&fd, file]() { + char buffer[1024]; + ssize_t rc; + + if (file == stdout) { + lnav_data.ld_stdout_used = true; + } + + while ((rc = read(fd, buffer, sizeof(buffer))) > 0) { + fwrite(buffer, rc, 1, file); + } + + return std::string(); + }); + } + auto tmp_fd + = lnav::filesystem::open_temp_file( + ghc::filesystem::temp_directory_path() / "lnav.out.XXXXXX") + .map([](auto pair) { + ghc::filesystem::remove(pair.first); + + return std::move(pair.second); + }) + .expect("Cannot create temporary file for callback"); + auto pp + = std::make_shared(std::move(fd), false, std::move(tmp_fd)); + static int exec_count = 0; + + lnav_data.ld_pipers.push_back(pp); + auto desc + = fmt::format(FMT_STRING("[{}] Output of {}"), exec_count++, cmdline); + lnav_data.ld_active_files.fc_file_names[desc] + .with_fd(pp->get_fd()) + .with_include_in_session(false) + .with_detect_format(false); + lnav_data.ld_files_to_front.emplace_back(desc, 0_vl); + if (lnav_data.ld_rl_view != nullptr) { + lnav_data.ld_rl_view->set_alt_value(HELP_MSG_1(X, "to close the file")); + } + + return lnav::futures::make_ready_future(std::string()); +} + +void +add_global_vars(exec_context& ec) +{ + for (const auto& iter : lnav_config.lc_global_vars) { + shlex subber(iter.second); + std::string str; + + if (!subber.eval(str, ec.ec_global_vars)) { + log_error("Unable to evaluate global variable value: %s", + iter.second.c_str()); + continue; + } + + ec.ec_global_vars[iter.first] = str; + } +} + +void +exec_context::set_output(const std::string& name, + FILE* file, + int (*closer)(FILE*)) +{ + log_info("redirecting command output to: %s", name.c_str()); + this->ec_output_stack.back().second | [](auto out) { + if (out.second != nullptr) { + out.second(out.first); + } + }; + this->ec_output_stack.back() + = std::make_pair(name, std::make_pair(file, closer)); +} + +void +exec_context::clear_output() +{ + log_info("redirecting command output to screen"); + this->ec_output_stack.back().second | [](auto out) { + if (out.second != nullptr) { + out.second(out.first); + } + }; + this->ec_output_stack.back() = std::make_pair("default", nonstd::nullopt); +} + +exec_context::exec_context(logline_value_vector* line_values, + sql_callback_t sql_callback, + pipe_callback_t pipe_callback) + : ec_line_values(line_values), + ec_accumulator(std::make_unique()), + ec_sql_callback(sql_callback), ec_pipe_callback(pipe_callback) +{ + static const auto COMMAND_SRC = intern_string::lookup("command"); + + this->ec_local_vars.push(std::map()); + this->ec_path_stack.emplace_back("."); + this->ec_source.emplace_back( + lnav::console::snippet::from(COMMAND_SRC, "").with_line(1)); + this->ec_output_stack.emplace_back("screen", nonstd::nullopt); + this->ec_error_callback_stack.emplace_back( + [](const auto& um) { lnav::console::print(stderr, um); }); +} + +void +exec_context::execute(const std::string& cmdline) +{ + auto exec_res = execute_any(*this, cmdline); + if (exec_res.isErr()) { + this->ec_error_callback_stack.back()(exec_res.unwrapErr()); + } +} + +void +exec_context::add_error_context(lnav::console::user_message& um) +{ + switch (um.um_level) { + case lnav::console::user_message::level::raw: + case lnav::console::user_message::level::info: + case lnav::console::user_message::level::ok: + return; + default: + break; + } + + if (um.um_snippets.empty()) { + um.with_snippets(this->ec_source); + } + + if (this->ec_current_help != nullptr && um.um_help.empty()) { + attr_line_t help; + + format_help_text_for_term(*this->ec_current_help, + 70, + help, + help_text_content::synopsis_and_summary); + um.with_help(help); + } +} + +exec_context::source_guard +exec_context::enter_source(intern_string_t path, + int line_number, + const std::string& content) +{ + attr_line_t content_al{content}; + content_al.with_attr_for_all(VC_ROLE.value(role_t::VCR_QUOTED_CODE)); + readline_lnav_highlighter(content_al, -1); + this->ec_source.emplace_back( + lnav::console::snippet::from(path, content_al).with_line(line_number)); + return {this}; +} + +exec_context::output_guard::output_guard(exec_context& context, + std::string name, + const nonstd::optional& file) + : sg_context(context) +{ + if (file) { + log_info("redirecting command output to: %s", name.c_str()); + } + context.ec_output_stack.emplace_back(std::move(name), file); +} + +exec_context::output_guard::~output_guard() +{ + this->sg_context.clear_output(); + this->sg_context.ec_output_stack.pop_back(); +} diff --git a/src/command_executor.hh b/src/command_executor.hh new file mode 100644 index 0000000..4461d55 --- /dev/null +++ b/src/command_executor.hh @@ -0,0 +1,262 @@ +/** + * Copyright (c) 2015, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LNAV_COMMAND_EXECUTOR_H +#define LNAV_COMMAND_EXECUTOR_H + +#include +#include +#include + +#include + +#include "base/auto_fd.hh" +#include "base/lnav.console.hh" +#include "bookmarks.hh" +#include "fmt/format.h" +#include "ghc/filesystem.hpp" +#include "help_text.hh" +#include "optional.hpp" +#include "shlex.resolver.hh" +#include "vis_line.hh" + +struct exec_context; +class attr_line_t; +class logline_value; +struct logline_value_vector; + +using sql_callback_t = int (*)(exec_context&, sqlite3_stmt*); +int sql_callback(exec_context& ec, sqlite3_stmt* stmt); + +using pipe_callback_t + = std::future (*)(exec_context&, const std::string&, auto_fd&); + +using error_callback_t + = std::function; + +struct exec_context { + enum class perm_t { + READ_WRITE, + READ_ONLY, + }; + + using output_t = std::pair; + + exec_context(logline_value_vector* line_values = nullptr, + sql_callback_t sql_callback = ::sql_callback, + pipe_callback_t pipe_callback = nullptr); + + bool is_read_write() const + { + return this->ec_perms == perm_t::READ_WRITE; + } + + bool is_read_only() const + { + return this->ec_perms == perm_t::READ_ONLY; + } + + exec_context& with_perms(perm_t perms) + { + this->ec_perms = perms; + return *this; + } + + void add_error_context(lnav::console::user_message& um); + + template + Result make_error( + fmt::string_view format_str, const Args&... args) + { + auto retval = lnav::console::user_message::error( + fmt::vformat(format_str, fmt::make_format_args(args...))); + + this->add_error_context(retval); + + return Err(retval); + } + + nonstd::optional get_output() + { + for (auto iter = this->ec_output_stack.rbegin(); + iter != this->ec_output_stack.rend(); + ++iter) + { + if (iter->second && (*iter->second).first) { + return (*iter->second).first; + } + } + + return nonstd::nullopt; + } + + void set_output(const std::string& name, FILE* file, int (*closer)(FILE*)); + + void clear_output(); + + struct source_guard { + source_guard(exec_context* context) : sg_context(context) {} + + source_guard(const source_guard&) = delete; + + source_guard(source_guard&& other) : sg_context(other.sg_context) + { + other.sg_context = nullptr; + } + + ~source_guard() + { + if (this->sg_context != nullptr) { + this->sg_context->ec_source.pop_back(); + } + } + + exec_context* sg_context; + }; + + struct output_guard { + explicit output_guard(exec_context& context, + std::string name = "default", + const nonstd::optional& file + = nonstd::nullopt); + + ~output_guard(); + + exec_context& sg_context; + }; + + source_guard enter_source(intern_string_t path, + int line_number, + const std::string& content); + + struct error_cb_guard { + error_cb_guard(exec_context* context) : sg_context(context) {} + + error_cb_guard(const error_cb_guard&) = delete; + error_cb_guard(error_cb_guard&& other) : sg_context(other.sg_context) + { + other.sg_context = nullptr; + } + + ~error_cb_guard() + { + if (this->sg_context != nullptr) { + this->sg_context->ec_error_callback_stack.pop_back(); + } + } + + exec_context* sg_context; + }; + + error_cb_guard add_error_callback(error_callback_t cb) + { + this->ec_error_callback_stack.emplace_back(std::move(cb)); + return {this}; + } + + scoped_resolver create_resolver() + { + return { + &this->ec_local_vars.top(), + &this->ec_global_vars, + }; + } + + void execute(const std::string& cmdline); + + using kv_pair_t = std::pair; + + void execute_with_int(const std::string& cmdline) + { + this->execute(cmdline); + } + + template + void execute_with_int(const std::string& cmdline, + kv_pair_t pair, + Args... args) + { + this->ec_local_vars.top().template emplace(pair); + this->execute(cmdline, args...); + } + + template + void execute_with(const std::string& cmdline, Args... args) + { + this->ec_local_vars.push({}); + this->execute_with_int(cmdline, args...); + this->ec_local_vars.pop(); + } + + vis_line_t ec_top_line{0_vl}; + bool ec_dry_run{false}; + perm_t ec_perms{perm_t::READ_WRITE}; + + std::map ec_override; + logline_value_vector* ec_line_values; + std::stack> ec_local_vars; + std::map ec_global_vars; + std::vector ec_path_stack; + std::vector ec_source; + help_text* ec_current_help{nullptr}; + + std::vector>> + ec_output_stack; + + std::unique_ptr ec_accumulator; + + sql_callback_t ec_sql_callback; + pipe_callback_t ec_pipe_callback; + std::vector ec_error_callback_stack; +}; + +Result execute_command( + exec_context& ec, const std::string& cmdline); + +Result execute_sql( + exec_context& ec, const std::string& sql, std::string& alt_msg); +Result execute_file( + exec_context& ec, const std::string& path_and_args, bool multiline = true); +Result execute_any( + exec_context& ec, const std::string& cmdline); +void execute_init_commands( + exec_context& ec, + std::vector, + std::string>>& msgs); + +std::future pipe_callback(exec_context& ec, + const std::string& cmdline, + auto_fd& fd); + +int sql_progress(const struct log_cursor& lc); +void sql_progress_finished(); + +void add_global_vars(exec_context& ec); + +#endif // LNAV_COMMAND_EXECUTOR_H diff --git a/src/config.cmake.h.in b/src/config.cmake.h.in new file mode 100644 index 0000000..56803fb --- /dev/null +++ b/src/config.cmake.h.in @@ -0,0 +1,31 @@ + +#define HAVE_PCRE_H +#define HAVE_NCURSESW_CURSES_H +#define HAVE_ARCHIVE_H 1 +#define HAVE_BZLIB_H 1 + +#define HAVE_LIBCURL + +#cmakedefine SIZEOF_OFF_T @SIZEOF_OFF_T@ + +#cmakedefine VCS_PACKAGE_STRING "@VCS_PACKAGE_STRING@" + +#cmakedefine PACKAGE_VERSION "@PACKAGE_VERSION@" + +#cmakedefine HAVE_PTY_H + +#cmakedefine HAVE_UTIL_H + +#cmakedefine HAVE_EXECINFO_H + +#define HAVE_SQLITE3_STMT_READONLY + +#define HAVE_SQLITE3_VALUE_SUBTYPE + +#define HAVE_SQLITE3_ERROR_OFFSET + +#define HAVE_SQLITE3_DROP_MODULES + +#define _XOPEN_SOURCE_EXTENDED 1 + +#define PACKAGE_BUGREPORT "lnav@googlegroups.com" diff --git a/src/curl_looper.cc b/src/curl_looper.cc new file mode 100644 index 0000000..3b7bb4e --- /dev/null +++ b/src/curl_looper.cc @@ -0,0 +1,341 @@ +/** + * Copyright (c) 2015, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @file curl_looper.cc + */ + +#include + +#include "config.h" + +#if defined(HAVE_LIBCURL) +# include + +# include "curl_looper.hh" + +using namespace std::chrono_literals; + +# if !CURL_AT_LEAST_VERSION(7, 80, 0) +extern "C" +{ +const char* +curl_url_strerror(CURLUcode error) +{ + switch (error) { + case CURLUE_OK: + return "No error"; + + case CURLUE_BAD_HANDLE: + return "An invalid CURLU pointer was passed as argument"; + + case CURLUE_BAD_PARTPOINTER: + return "An invalid 'part' argument was passed as argument"; + + case CURLUE_MALFORMED_INPUT: + return "Malformed input to a URL function"; + + case CURLUE_BAD_PORT_NUMBER: + return "Port number was not a decimal number between 0 and 65535"; + + case CURLUE_UNSUPPORTED_SCHEME: + return "Unsupported URL scheme"; + + case CURLUE_URLDECODE: + return "URL decode error, most likely because of rubbish in the " + "input"; + + case CURLUE_OUT_OF_MEMORY: + return "A memory function failed"; + + case CURLUE_USER_NOT_ALLOWED: + return "Credentials was passed in the URL when prohibited"; + + case CURLUE_UNKNOWN_PART: + return "An unknown part ID was passed to a URL API function"; + + case CURLUE_NO_SCHEME: + return "No scheme part in the URL"; + + case CURLUE_NO_USER: + return "No user part in the URL"; + + case CURLUE_NO_PASSWORD: + return "No password part in the URL"; + + case CURLUE_NO_OPTIONS: + return "No options part in the URL"; + + case CURLUE_NO_HOST: + return "No host part in the URL"; + + case CURLUE_NO_PORT: + return "No port part in the URL"; + + case CURLUE_NO_QUERY: + return "No query part in the URL"; + + case CURLUE_NO_FRAGMENT: + return "No fragment part in the URL"; + } + + return "CURLUcode unknown"; +} +} +# endif + +struct curl_request_eq { + explicit curl_request_eq(const std::string& name) : cre_name(name){}; + + bool operator()(const std::shared_ptr& cr) const + { + return this->cre_name == cr->get_name(); + } + + bool operator()( + const std::pair>& pair) const + { + return this->cre_name == pair.second->get_name(); + } + + const std::string& cre_name; +}; + +int +curl_request::debug_cb( + CURL* handle, curl_infotype type, char* data, size_t size, void* userp) +{ + curl_request* cr = (curl_request*) userp; + bool write_to_log; + + switch (type) { + case CURLINFO_TEXT: + write_to_log = true; + break; + case CURLINFO_HEADER_IN: + case CURLINFO_HEADER_OUT: + if (lnav_log_level == lnav_log_level_t::TRACE) { + write_to_log = true; + } else { + write_to_log = false; + } + break; + default: + write_to_log = false; + break; + } + + if (write_to_log) { + while (size > 0 && isspace(data[size - 1])) { + size -= 1; + } + log_debug("%s:%.*s", cr->get_name().c_str(), size, data); + } + + return 0; +} + +size_t +curl_request::string_cb(void* data, size_t size, size_t nmemb, void* userp) +{ + auto realsize = size * nmemb; + auto& vec = *static_cast(userp); + + vec.append((char*) data, ((char*) data) + realsize); + + return realsize; +} + +void +curl_looper::loop_body() +{ + mstime_t current_time = getmstime(); + + this->perform_io(); + + this->check_for_finished_requests(); + + this->check_for_new_requests(); + + this->requeue_requests(current_time + 5); +} + +void +curl_looper::perform_io() +{ + if (this->cl_handle_to_request.empty()) { + return; + } + + mstime_t current_time = getmstime(); + auto timeout = this->compute_timeout(current_time); + int running_handles; + + if (timeout < 1ms) { + timeout = 5ms; + } + curl_multi_wait(this->cl_curl_multi, nullptr, 0, timeout.count(), nullptr); + curl_multi_perform(this->cl_curl_multi, &running_handles); +} + +void +curl_looper::requeue_requests(mstime_t up_to_time) +{ + while (!this->cl_poll_queue.empty() + && this->cl_poll_queue.front().first <= up_to_time) + { + auto cr = this->cl_poll_queue.front().second; + + log_debug("%s:polling request is ready again -- %p", + cr->get_name().c_str(), + cr.get()); + this->cl_handle_to_request[cr->get_handle()] = cr; + curl_multi_add_handle(this->cl_curl_multi, cr->get_handle()); + this->cl_poll_queue.erase(this->cl_poll_queue.begin()); + } +} + +void +curl_looper::check_for_new_requests() +{ + while (!this->cl_new_requests.empty()) { + auto cr = this->cl_new_requests.back(); + + log_info("%s:new curl request %p", cr->get_name().c_str(), cr.get()); + this->cl_handle_to_request[cr->get_handle()] = cr; + curl_multi_add_handle(this->cl_curl_multi, cr->get_handle()); + this->cl_new_requests.pop_back(); + } + while (!this->cl_close_requests.empty()) { + const std::string& name = this->cl_close_requests.back(); + auto all_iter = find_if(this->cl_all_requests.begin(), + this->cl_all_requests.end(), + curl_request_eq(name)); + + log_info("attempting to close request -- %s", name.c_str()); + if (all_iter != this->cl_all_requests.end()) { + auto cr = *all_iter; + + log_info( + "%s:closing request -- %p", cr->get_name().c_str(), cr.get()); + (*all_iter)->close(); + auto act_iter = this->cl_handle_to_request.find(cr->get_handle()); + if (act_iter != this->cl_handle_to_request.end()) { + curl_multi_remove_handle(this->cl_curl_multi, cr->get_handle()); + this->cl_handle_to_request.erase(act_iter); + } + auto poll_iter = find_if(this->cl_poll_queue.begin(), + this->cl_poll_queue.end(), + curl_request_eq(name)); + if (poll_iter != this->cl_poll_queue.end()) { + this->cl_poll_queue.erase(poll_iter); + } + this->cl_all_requests.erase(all_iter); + } else { + log_error("Unable to find request with the name -- %s", + name.c_str()); + } + + this->cl_close_requests.pop_back(); + } +} + +void +curl_looper::check_for_finished_requests() +{ + CURLMsg* msg; + int msgs_left; + + while ((msg = curl_multi_info_read(this->cl_curl_multi, &msgs_left)) + != nullptr) + { + if (msg->msg != CURLMSG_DONE) { + continue; + } + + CURL* easy = msg->easy_handle; + auto iter = this->cl_handle_to_request.find(easy); + + curl_multi_remove_handle(this->cl_curl_multi, easy); + if (iter != this->cl_handle_to_request.end()) { + auto cr = iter->second; + this->cl_handle_to_request.erase(iter); + auto delay_ms = cr->complete(msg->data.result); + if (delay_ms < 0) { + log_info("%s:curl_request %p finished, deleting...", + cr->get_name().c_str(), + cr.get()); + auto all_iter = find(this->cl_all_requests.begin(), + this->cl_all_requests.end(), + cr); + if (all_iter != this->cl_all_requests.end()) { + this->cl_all_requests.erase(all_iter); + } + } else { + log_debug("%s:curl_request %p is polling, requeueing in %d", + cr->get_name().c_str(), + cr.get(), + delay_ms); + this->cl_poll_queue.emplace_back(getmstime() + delay_ms, cr); + sort(this->cl_poll_queue.begin(), this->cl_poll_queue.end()); + } + } + } +} + +std::chrono::milliseconds +curl_looper::compute_timeout(mstime_t current_time) const +{ + std::chrono::milliseconds retval = 1s; + + if (!this->cl_handle_to_request.empty()) { + retval = 0ms; + } else if (!this->cl_poll_queue.empty()) { + retval + = std::max(1ms, + std::chrono::milliseconds( + this->cl_poll_queue.front().first - current_time)); + } + + return retval; +} + +void +curl_looper::process_all() +{ + this->check_for_new_requests(); + + this->requeue_requests(LONG_MAX); + + while (!this->cl_handle_to_request.empty()) { + this->perform_io(); + + this->check_for_finished_requests(); + } +} + +#endif diff --git a/src/curl_looper.hh b/src/curl_looper.hh new file mode 100644 index 0000000..2b350ad --- /dev/null +++ b/src/curl_looper.hh @@ -0,0 +1,226 @@ +/** + * Copyright (c) 2015, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @file curl_looper.hh + */ + +#ifndef curl_looper_hh +#define curl_looper_hh + +#include +#include +#include +#include +#include + +#include "base/isc.hh" +#include "base/lnav.console.hh" +#include "base/result.h" +#include "config.h" + +#if !defined(HAVE_LIBCURL) + +typedef int CURLcode; + +class curl_request { +public: + curl_request(const std::string& name){}; +}; + +class curl_looper : public isc::service { +public: + void start(){}; + void stop(){}; + void add_request(std::shared_ptr cr){}; + void close_request(const std::string& name){}; + void process_all(){}; +}; + +#else +# include +# include +# include + +# include + +# include "base/auto_mem.hh" +# include "base/lnav_log.hh" +# include "base/time_util.hh" + +class curl_request { +public: + curl_request(std::string name) + : cr_name(std::move(name)), cr_handle(curl_easy_cleanup) + { + this->cr_handle.reset(curl_easy_init()); + curl_easy_setopt(this->cr_handle, CURLOPT_NOSIGNAL, 1); + curl_easy_setopt( + this->cr_handle, CURLOPT_ERRORBUFFER, this->cr_error_buffer); + curl_easy_setopt(this->cr_handle, CURLOPT_DEBUGFUNCTION, debug_cb); + curl_easy_setopt(this->cr_handle, CURLOPT_DEBUGDATA, this); + curl_easy_setopt(this->cr_handle, CURLOPT_VERBOSE, 1); + if (getenv("SSH_AUTH_SOCK") != nullptr) { + curl_easy_setopt(this->cr_handle, + CURLOPT_SSH_AUTH_TYPES, +# ifdef CURLSSH_AUTH_AGENT + CURLSSH_AUTH_AGENT | +# endif + CURLSSH_AUTH_PASSWORD); + } + } + + virtual ~curl_request() = default; + + const std::string& get_name() const + { + return this->cr_name; + } + + virtual void close() + { + this->cr_open = false; + } + + bool is_open() const + { + return this->cr_open; + } + + CURL* get_handle() const + { + return this->cr_handle; + } + + operator CURL*() const + { + return this->cr_handle; + } + + int get_completions() const + { + return this->cr_completions; + } + + virtual long complete(CURLcode result) + { + double total_time = 0, download_size = 0, download_speed = 0; + + this->cr_completions += 1; + curl_easy_getinfo(this->cr_handle, CURLINFO_TOTAL_TIME, &total_time); + log_debug("%s: total_time=%f", this->cr_name.c_str(), total_time); + curl_easy_getinfo( + this->cr_handle, CURLINFO_SIZE_DOWNLOAD, &download_size); + log_debug("%s: download_size=%f", this->cr_name.c_str(), download_size); + curl_easy_getinfo( + this->cr_handle, CURLINFO_SPEED_DOWNLOAD, &download_speed); + log_debug( + "%s: download_speed=%f", this->cr_name.c_str(), download_speed); + + return -1; + } + + Result perform() + { + std::string response; + + curl_easy_setopt(this->get_handle(), CURLOPT_WRITEFUNCTION, string_cb); + curl_easy_setopt(this->get_handle(), CURLOPT_WRITEDATA, &response); + + auto rc = curl_easy_perform(this->get_handle()); + if (rc == CURLE_OK) { + return Ok(response); + } + + return Err(rc); + } + + long get_response_code() const + { + long retval; + + curl_easy_getinfo(this->get_handle(), CURLINFO_RESPONSE_CODE, &retval); + return retval; + } + +protected: + static int debug_cb( + CURL* handle, curl_infotype type, char* data, size_t size, void* userp); + + static size_t string_cb(void* data, size_t size, size_t nmemb, void* userp); + + const std::string cr_name; + bool cr_open{true}; + auto_mem cr_handle; + char cr_error_buffer[CURL_ERROR_SIZE]; + int cr_completions{0}; +}; + +class curl_looper : public isc::service { +public: + curl_looper() : cl_curl_multi(curl_multi_cleanup) + { + this->cl_curl_multi.reset(curl_multi_init()); + } + + void process_all(); + + void add_request(const std::shared_ptr& cr) + { + require(cr != nullptr); + + this->cl_all_requests.emplace_back(cr); + this->cl_new_requests.emplace_back(cr); + } + + void close_request(const std::string& name) + { + this->cl_close_requests.emplace_back(name); + } + +protected: + void loop_body() override; + +private: + void perform_io(); + void check_for_new_requests(); + void check_for_finished_requests(); + void requeue_requests(mstime_t up_to_time); + std::chrono::milliseconds compute_timeout( + mstime_t current_time) const override; + + auto_mem cl_curl_multi; + std::vector > cl_all_requests; + std::vector > cl_new_requests; + std::vector cl_close_requests; + std::map > cl_handle_to_request; + std::vector > > + cl_poll_queue; +}; +#endif + +#endif diff --git a/src/data_parser.cc b/src/data_parser.cc new file mode 100644 index 0000000..a751b30 --- /dev/null +++ b/src/data_parser.cc @@ -0,0 +1,1071 @@ +/** + * Copyright (c) 2007-2012, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include "data_parser.hh" + +#include "config.h" +#include "spookyhash/SpookyV2.h" + +data_format data_parser::FORMAT_SEMI("semi", DT_COMMA, DT_SEMI); +data_format data_parser::FORMAT_COMMA("comma", DT_INVALID, DT_COMMA); +data_format data_parser::FORMAT_PLAIN("plain", DT_INVALID, DT_INVALID); + +data_parser::data_parser(data_scanner* ds) + : dp_errors("dp_errors", __FILE__, __LINE__), + dp_pairs("dp_pairs", __FILE__, __LINE__), dp_msg_format(nullptr), + dp_msg_format_begin(ds->get_init_offset()), dp_scanner(ds) +{ + if (TRACE_FILE != nullptr) { + fprintf(TRACE_FILE, "input %s\n", ds->get_input().to_string().c_str()); + } +} + +void +data_parser::pairup(data_parser::schema_id_t* schema, + data_parser::element_list_t& pairs_out, + data_parser::element_list_t& in_list, + int group_depth) +{ + element_list_t ELEMENT_LIST_T(el_stack), ELEMENT_LIST_T(free_row), + ELEMENT_LIST_T(key_comps), ELEMENT_LIST_T(value), + ELEMENT_LIST_T(prefix); + SpookyHash context; + + require(in_list.el_format.df_name != nullptr); + + POINT_TRACE("pairup_start"); + + FORMAT_TRACE(in_list); + + for (auto iter = in_list.begin(); iter != in_list.end(); ++iter) { + if (iter->e_token == DNT_GROUP) { + element_list_t ELEMENT_LIST_T(group_pairs); + + this->pairup( + nullptr, group_pairs, *iter->e_sub_elements, group_depth + 1); + if (!group_pairs.empty()) { + iter->assign_elements(group_pairs); + } + } + + if (in_list.el_format.df_prefix_terminator != DT_INVALID) { + if (iter->e_token == in_list.el_format.df_prefix_terminator) { + in_list.el_format.df_prefix_terminator = DT_INVALID; + } else { + el_stack.PUSH_BACK(*iter); + } + } else if (iter->e_token == in_list.el_format.df_terminator) { + this->end_of_value( + el_stack, key_comps, value, in_list, group_depth); + + key_comps.PUSH_BACK(*iter); + } else if (iter->e_token == in_list.el_format.df_qualifier) { + value.SPLICE( + value.end(), key_comps, key_comps.begin(), key_comps.end()); + strip(value, element_is_space{}); + if (!value.empty()) { + el_stack.PUSH_BACK(element(value, DNT_VALUE)); + } + } else if (iter->e_token == in_list.el_format.df_separator) { + auto key_iter = key_comps.end(); + bool found = false, key_is_values = true; + + if (!key_comps.empty()) { + do { + --key_iter; + if (key_iter->e_token == in_list.el_format.df_appender) { + ++key_iter; + value.SPLICE(value.end(), + key_comps, + key_comps.begin(), + key_iter); + key_comps.POP_FRONT(); + found = true; + } else if (key_iter->e_token + == in_list.el_format.df_terminator) + { + std::vector key_copy; + + value.SPLICE(value.end(), + key_comps, + key_comps.begin(), + key_iter); + key_comps.POP_FRONT(); + strip(key_comps, element_is_space{}); + if (key_comps.empty()) { + key_iter = key_comps.end(); + } else { + key_iter = key_comps.begin(); + } + found = true; + } + if (key_iter != key_comps.end()) { + switch (key_iter->e_token) { + case DT_WORD: + case DT_SYMBOL: + key_is_values = false; + break; + default: + break; + } + } + } while (key_iter != key_comps.begin() && !found); + } + if (!found && !el_stack.empty() && !key_comps.empty()) { + element_list_t::iterator value_iter; + + if (el_stack.size() > 1 + && in_list.el_format.df_appender != DT_INVALID + && in_list.el_format.df_terminator != DT_INVALID) + { + /* If we're expecting a terminator and haven't found it */ + /* then this is part of the value. */ + continue; + } + + value.SPLICE( + value.end(), key_comps, key_comps.begin(), key_comps.end()); + value_iter = value.end(); + std::advance(value_iter, -1); + key_comps.SPLICE( + key_comps.begin(), value, value_iter, value.end()); + key_comps.resize(1); + } + + strip(value, element_is_space{}); + value.remove_if(element_if(DT_COMMA)); + if (!value.empty()) { + el_stack.PUSH_BACK(element(value, DNT_VALUE)); + } + strip(key_comps, element_is_space{}); + if (!key_comps.empty()) { + if (key_is_values) { + el_stack.PUSH_BACK(element(key_comps, DNT_VALUE)); + } else { + el_stack.PUSH_BACK(element(key_comps, DNT_KEY, false)); + } + } + key_comps.CLEAR(); + value.CLEAR(); + } else { + key_comps.PUSH_BACK(*iter); + } + + POINT_TRACE("pairup_loop"); + } + + POINT_TRACE("pairup_eol"); + + CONSUMED_TRACE(in_list); + + // Only perform the free-row logic at the top level, if we're in a group + // assume it is a list. + if (group_depth < 1 && el_stack.empty()) { + free_row.SPLICE( + free_row.begin(), key_comps, key_comps.begin(), key_comps.end()); + } else { + this->end_of_value(el_stack, key_comps, value, in_list, group_depth); + } + + POINT_TRACE("pairup_stack"); + + context.Init(0, 0); + while (!el_stack.empty()) { + auto kv_iter = el_stack.begin(); + if (kv_iter->e_token == DNT_VALUE) { + if (pairs_out.empty()) { + free_row.PUSH_BACK(el_stack.front()); + } else { + element_list_t ELEMENT_LIST_T(free_pair_subs); + struct element blank; + + blank.e_capture.c_begin = blank.e_capture.c_end + = el_stack.front().e_capture.c_begin; + blank.e_token = DNT_KEY; + free_pair_subs.PUSH_BACK(blank); + free_pair_subs.PUSH_BACK(el_stack.front()); + pairs_out.PUSH_BACK(element(free_pair_subs, DNT_PAIR)); + } + } + if (kv_iter->e_token != DNT_KEY) { + el_stack.POP_FRONT(); + continue; + } + + ++kv_iter; + if (kv_iter == el_stack.end()) { + el_stack.POP_FRONT(); + continue; + } + + element_list_t ELEMENT_LIST_T(pair_subs); + + if (schema != nullptr) { + size_t key_len; + const char* key_val + = this->get_element_string(el_stack.front(), key_len); + context.Update(key_val, key_len); + } + + while (!free_row.empty()) { + element_list_t ELEMENT_LIST_T(free_pair_subs); + struct element blank; + + blank.e_capture.c_begin = blank.e_capture.c_end + = free_row.front().e_capture.c_begin; + blank.e_token = DNT_KEY; + free_pair_subs.PUSH_BACK(blank); + free_pair_subs.PUSH_BACK(free_row.front()); + pairs_out.PUSH_BACK(element(free_pair_subs, DNT_PAIR)); + free_row.POP_FRONT(); + } + + bool has_value = false; + + if (kv_iter->e_token == DNT_VALUE) { + ++kv_iter; + has_value = true; + } + + pair_subs.SPLICE( + pair_subs.begin(), el_stack, el_stack.begin(), kv_iter); + + if (!has_value) { + element_list_t ELEMENT_LIST_T(blank_value); + struct element blank; + + blank.e_token = DT_QUOTED_STRING; + blank.e_capture.c_begin = blank.e_capture.c_end + = pair_subs.front().e_capture.c_end; + if (blank.e_capture.c_begin >= 0 + && blank.e_capture.c_begin + < this->dp_scanner->get_input().sf_end) + { + switch (this->dp_scanner->to_string_fragment(blank.e_capture) + .front()) + { + case '=': + case ':': + blank.e_capture.c_begin += 1; + blank.e_capture.c_end += 1; + break; + } + } + blank_value.PUSH_BACK(blank); + pair_subs.PUSH_BACK(element(blank_value, DNT_VALUE)); + } + + pairs_out.PUSH_BACK(element(pair_subs, DNT_PAIR)); + } + + if (pairs_out.size() == 1) { + element& pair = pairs_out.front(); + element& evalue = pair.e_sub_elements->back(); + + if (evalue.e_token == DNT_VALUE && evalue.e_sub_elements != nullptr + && evalue.e_sub_elements->size() > 1) + { + element_list_t::iterator next_sub; + + next_sub = pair.e_sub_elements->begin(); + ++next_sub; + prefix.SPLICE(prefix.begin(), + *pair.e_sub_elements, + pair.e_sub_elements->begin(), + next_sub); + free_row.CLEAR(); + free_row.SPLICE(free_row.begin(), + *evalue.e_sub_elements, + evalue.e_sub_elements->begin(), + evalue.e_sub_elements->end()); + pairs_out.CLEAR(); + context.Init(0, 0); + } + } + + if (group_depth >= 1 && pairs_out.empty() && !free_row.empty()) { + pairs_out.SWAP(free_row); + } + + if (pairs_out.empty() && !free_row.empty()) { + while (!free_row.empty()) { + switch (free_row.front().e_token) { + case DNT_GROUP: + case DNT_VALUE: + case DT_EMAIL: + case DT_CONSTANT: + case DT_NUMBER: + case DT_SYMBOL: + case DT_HEX_NUMBER: + case DT_OCTAL_NUMBER: + case DT_VERSION_NUMBER: + case DT_QUOTED_STRING: + case DT_IPV4_ADDRESS: + case DT_IPV6_ADDRESS: + case DT_MAC_ADDRESS: + case DT_HEX_DUMP: + case DT_XML_DECL_TAG: + case DT_XML_OPEN_TAG: + case DT_XML_CLOSE_TAG: + case DT_XML_EMPTY_TAG: + case DT_UUID: + case DT_URL: + case DT_PATH: + case DT_DATE: + case DT_TIME: + case DT_PERCENTAGE: { + element_list_t ELEMENT_LIST_T(pair_subs); + struct element blank; + + blank.e_capture.c_begin = blank.e_capture.c_end + = free_row.front().e_capture.c_begin; + blank.e_token = DNT_KEY; + pair_subs.PUSH_BACK(blank); + pair_subs.PUSH_BACK(free_row.front()); + pairs_out.PUSH_BACK(element(pair_subs, DNT_PAIR)); + + // Throw something into the hash so that the number of + // columns is significant. I don't think we want to + // use the token ID since some columns values might vary + // between rows. + context.Update(" ", 1); + } break; + + default: { + size_t key_len; + const char* key_val + = this->get_element_string(free_row.front(), key_len); + + context.Update(key_val, key_len); + } break; + } + + free_row.POP_FRONT(); + } + } + + if (!prefix.empty()) { + element_list_t ELEMENT_LIST_T(pair_subs); + struct element blank; + + blank.e_capture.c_begin = blank.e_capture.c_end + = prefix.front().e_capture.c_begin; + blank.e_token = DNT_KEY; + pair_subs.PUSH_BACK(blank); + pair_subs.PUSH_BACK(prefix.front()); + pairs_out.PUSH_FRONT(element(pair_subs, DNT_PAIR)); + } + + if (schema != nullptr) { + context.Final(schema->out(0), schema->out(1)); + } + + if (schema != nullptr && this->dp_msg_format != nullptr) { + for (auto& fiter : pairs_out) { + *(this->dp_msg_format) += this->get_string_up_to_value(fiter); + this->dp_msg_format->append("#"); + } + if ((size_t) this->dp_msg_format_begin + < this->dp_scanner->get_input().length()) + { + auto last = this->dp_scanner->get_input().substr( + this->dp_msg_format_begin); + + switch (last.front()) { + case '\'': + case '"': + last.sf_begin += 1; + break; + } + *(this->dp_msg_format) += last.to_string(); + } + } + + if (pairs_out.size() > 1000) { + pairs_out.resize(1000); + } +} + +void +data_parser::discover_format() +{ + std::stack state_stack; + this->dp_group_token.push_back(DT_INVALID); + this->dp_group_stack.resize(1); + + state_stack.push(discover_format_state()); + while (true) { + auto tok_res = this->dp_scanner->tokenize2(); + if (!tok_res) { + break; + } + + element elem; + elem.e_token = tok_res->tr_token; + elem.e_capture = tok_res->tr_inner_capture; + + require(elem.e_capture.c_begin >= 0); + require(elem.e_capture.c_end >= 0); + + state_stack.top().update_for_element(elem); + switch (elem.e_token) { + case DT_LPAREN: + case DT_LANGLE: + case DT_LCURLY: + case DT_LSQUARE: + this->dp_group_token.push_back(elem.e_token); + this->dp_group_stack.emplace_back("_anon_", __FILE__, __LINE__); + state_stack.push(discover_format_state()); + break; + + case DT_EMPTY_CONTAINER: { + auto& curr_group = this->dp_group_stack.back(); + auto empty_list = element_list_t("_anon_", __FILE__, __LINE__); + discover_format_state dfs; + + dfs.finalize(); + + empty_list.el_format = dfs.dfs_format; + curr_group.PUSH_BACK(element()); + + auto& empty = curr_group.back(); + empty.e_capture.c_begin = elem.e_capture.c_begin + 1; + empty.e_capture.c_end = elem.e_capture.c_begin + 1; + empty.e_token = DNT_GROUP; + empty.assign_elements(empty_list); + break; + } + + case DT_RPAREN: + case DT_RANGLE: + case DT_RCURLY: + case DT_RSQUARE: + if (this->dp_group_token.back() == (elem.e_token - 1)) { + this->dp_group_token.pop_back(); + + auto riter = this->dp_group_stack.rbegin(); + ++riter; + state_stack.top().finalize(); + this->dp_group_stack.back().el_format + = state_stack.top().dfs_format; + state_stack.pop(); + if (!this->dp_group_stack.back().empty()) { + (*riter).PUSH_BACK( + element(this->dp_group_stack.back(), DNT_GROUP)); + } else { + (*riter).PUSH_BACK(element()); + riter->back().e_capture.c_begin + = elem.e_capture.c_begin; + riter->back().e_capture.c_end = elem.e_capture.c_begin; + riter->back().e_token = DNT_GROUP; + riter->back().assign_elements( + this->dp_group_stack.back()); + } + this->dp_group_stack.pop_back(); + } else { + this->dp_group_stack.back().PUSH_BACK(elem); + } + break; + + default: + this->dp_group_stack.back().PUSH_BACK(elem); + break; + } + } + + while (this->dp_group_stack.size() > 1) { + this->dp_group_token.pop_back(); + + auto riter = this->dp_group_stack.rbegin(); + ++riter; + if (!this->dp_group_stack.back().empty()) { + state_stack.top().finalize(); + this->dp_group_stack.back().el_format + = state_stack.top().dfs_format; + state_stack.pop(); + (*riter).PUSH_BACK(element(this->dp_group_stack.back(), DNT_GROUP)); + } + this->dp_group_stack.pop_back(); + } + + state_stack.top().finalize(); + this->dp_group_stack.back().el_format = state_stack.top().dfs_format; +} + +void +data_parser::end_of_value(data_parser::element_list_t& el_stack, + data_parser::element_list_t& key_comps, + data_parser::element_list_t& value, + const data_parser::element_list_t& in_list, + int group_depth) +{ + key_comps.remove_if(element_if(in_list.el_format.df_terminator)); + key_comps.remove_if(element_if(DT_COMMA)); + value.remove_if(element_if(in_list.el_format.df_terminator)); + value.remove_if(element_if(DT_COMMA)); + strip(key_comps, element_is_space{}); + strip(value, element_is_space{}); + if ((el_stack.empty() || el_stack.back().e_token != DNT_KEY) + && value.empty() && key_comps.size() > 1 + && (key_comps.front().e_token == DT_WORD + || key_comps.front().e_token == DT_SYMBOL)) + { + element_list_t::iterator key_iter, key_end; + bool found_value = false; + int word_count = 0; + key_iter = key_comps.begin(); + key_end = key_comps.begin(); + for (; key_iter != key_comps.end(); ++key_iter) { + if (key_iter->e_token == DT_WORD || key_iter->e_token == DT_SYMBOL) + { + word_count += 1; + if (found_value) { + key_end = key_comps.begin(); + } + } else if (key_iter->e_token == DT_WHITE + || key_iter->e_token == DT_CSI) + { + } else { + if (!found_value) { + key_end = key_iter; + } + found_value = true; + } + } + if (word_count != 1) { + key_end = key_comps.begin(); + } + value.SPLICE(value.end(), key_comps, key_end, key_comps.end()); + strip(key_comps, element_is_space{}); + if (!key_comps.empty()) { + el_stack.PUSH_BACK(element(key_comps, DNT_KEY, false)); + } + key_comps.CLEAR(); + } else { + value.SPLICE( + value.end(), key_comps, key_comps.begin(), key_comps.end()); + } + strip(value, element_is_space{}); + strip(value, element_if(DT_COLON)); + strip(value, element_is_space{}); + if (!value.empty()) { + if (value.size() == 2 && value.back().e_token == DNT_GROUP) { + element_list_t ELEMENT_LIST_T(group_pair); + + group_pair.PUSH_BACK(element(value, DNT_PAIR)); + el_stack.PUSH_BACK(element(group_pair, DNT_VALUE)); + } else { + el_stack.PUSH_BACK(element(value, DNT_VALUE)); + } + } + value.CLEAR(); +} + +void +data_parser::parse() +{ + this->discover_format(); + + this->pairup( + &this->dp_schema_id, this->dp_pairs, this->dp_group_stack.front()); +} + +std::string +data_parser::get_element_string(const data_parser::element& elem) const +{ + return this->dp_scanner->to_string_fragment(elem.e_capture).to_string(); +} + +std::string +data_parser::get_string_up_to_value(const data_parser::element& elem) +{ + const element& val_elem + = elem.e_token == DNT_PAIR ? elem.e_sub_elements->back() : elem; + + if (this->dp_msg_format_begin <= val_elem.e_capture.c_begin) { + auto leading_and_key = data_scanner::capture_t( + this->dp_msg_format_begin, val_elem.e_capture.c_begin); + auto str = this->dp_scanner->get_input().data(); + if (leading_and_key.length() >= 2) { + switch (str[leading_and_key.c_end - 1]) { + case '\'': + case '"': + leading_and_key.c_end -= 1; + switch (str[leading_and_key.c_end - 1]) { + case 'r': + case 'u': + leading_and_key.c_end -= 1; + break; + } + break; + } + switch (str[leading_and_key.c_begin]) { + case '\'': + case '"': + leading_and_key.c_begin += 1; + break; + } + } + this->dp_msg_format_begin = val_elem.e_capture.c_end; + return this->dp_scanner->to_string_fragment(leading_and_key) + .to_string(); + } else { + this->dp_msg_format_begin = val_elem.e_capture.c_end; + } + return ""; +} + +const char* +data_parser::get_element_string(const data_parser::element& elem, + size_t& len_out) +{ + len_out = elem.e_capture.length(); + return this->dp_scanner->to_string_fragment(elem.e_capture).data(); +} + +void +data_parser::print(FILE* out, data_parser::element_list_t& el) +{ + fprintf(out, + " %s\n", + this->dp_scanner->get_input().to_string().c_str()); + for (auto& iter : el) { + iter.print(out, *this->dp_scanner); + } +} + +FILE* data_parser::TRACE_FILE; + +data_format_state_t +dfs_prefix_next(data_format_state_t state, data_token_t next_token) +{ + data_format_state_t retval = state; + + switch (state) { + case DFS_INIT: + switch (next_token) { + case DT_PATH: + case DT_COLON: + case DT_EQUALS: + case DT_CONSTANT: + case DT_EMAIL: + case DT_WORD: + case DT_SYMBOL: + case DT_OCTAL_NUMBER: + case DT_HEX_NUMBER: + case DT_NUMBER: + case DT_WHITE: + case DT_CSI: + case DT_LSQUARE: + case DT_RSQUARE: + case DT_LANGLE: + case DT_RANGLE: + case DT_EMPTY_CONTAINER: + break; + + default: + retval = DFS_ERROR; + break; + } + break; + + case DFS_EXPECTING_SEP: + case DFS_ERROR: + retval = DFS_ERROR; + break; + + default: + break; + } + + return retval; +} + +data_format_state_t +dfs_semi_next(data_format_state_t state, data_token_t next_token) +{ + data_format_state_t retval = state; + + switch (state) { + case DFS_INIT: + switch (next_token) { + case DT_COMMA: + case DT_SEMI: + retval = DFS_ERROR; + break; + + default: + retval = DFS_KEY; + break; + } + break; + + case DFS_KEY: + switch (next_token) { + case DT_COLON: + case DT_EQUALS: + retval = DFS_VALUE; + break; + + case DT_SEMI: + retval = DFS_ERROR; + break; + + default: + break; + } + break; + + case DFS_VALUE: + switch (next_token) { + case DT_SEMI: + retval = DFS_INIT; + break; + + default: + break; + } + break; + + case DFS_EXPECTING_SEP: + case DFS_ERROR: + retval = DFS_ERROR; + break; + } + + return retval; +} + +data_format_state_t +dfs_comma_next(data_format_state_t state, data_token_t next_token) +{ + data_format_state_t retval = state; + + switch (state) { + case DFS_INIT: + switch (next_token) { + case DT_COMMA: + break; + + case DT_SEMI: + retval = DFS_ERROR; + break; + + default: + retval = DFS_KEY; + break; + } + break; + + case DFS_KEY: + switch (next_token) { + case DT_COLON: + case DT_EQUALS: + retval = DFS_VALUE; + break; + + case DT_COMMA: + retval = DFS_INIT; + break; + + case DT_WORD: + retval = DFS_EXPECTING_SEP; + break; + + case DT_SEMI: + retval = DFS_ERROR; + break; + + default: + break; + } + break; + + case DFS_EXPECTING_SEP: + switch (next_token) { + case DT_COLON: + case DT_EQUALS: + case DT_LPAREN: + case DT_LCURLY: + case DT_LSQUARE: + case DT_LANGLE: + retval = DFS_VALUE; + break; + + case DT_EMPTY_CONTAINER: + retval = DFS_INIT; + break; + + case DT_COMMA: + case DT_SEMI: + retval = DFS_ERROR; + break; + + default: + break; + } + break; + + case DFS_VALUE: + switch (next_token) { + case DT_COMMA: + retval = DFS_INIT; + break; + + case DT_COLON: + case DT_EQUALS: + retval = DFS_ERROR; + break; + + default: + break; + } + break; + + case DFS_ERROR: + retval = DFS_ERROR; + break; + } + + return retval; +} + +data_parser::element::element() + : e_capture(-1, -1), e_token(DT_INVALID), e_sub_elements(nullptr) +{ +} + +data_parser::element::element(data_parser::element_list_t& subs, + data_token_t token, + bool assign_subs_elements) + : e_capture(subs.front().e_capture.c_begin, subs.back().e_capture.c_end), + e_token(token), e_sub_elements(nullptr) +{ + if (assign_subs_elements) { + this->assign_elements(subs); + } +} + +data_parser::element::element(const data_parser::element& other) +{ + /* require(other.e_sub_elements == nullptr); */ + + this->e_capture = other.e_capture; + this->e_token = other.e_token; + this->e_sub_elements = nullptr; + if (other.e_sub_elements != nullptr) { + this->assign_elements(*other.e_sub_elements); + } +} + +data_parser::element::~element() +{ + delete this->e_sub_elements; + this->e_sub_elements = nullptr; +} + +data_parser::element& +data_parser::element::operator=(const data_parser::element& other) +{ + this->e_capture = other.e_capture; + this->e_token = other.e_token; + this->e_sub_elements = nullptr; + if (other.e_sub_elements != nullptr) { + this->assign_elements(*other.e_sub_elements); + } + return *this; +} + +void +data_parser::element::assign_elements(data_parser::element_list_t& subs) +{ + if (this->e_sub_elements == nullptr) { + this->e_sub_elements = new element_list_t("_sub_", __FILE__, __LINE__); + this->e_sub_elements->el_format = subs.el_format; + } + this->e_sub_elements->SWAP(subs); + this->update_capture(); +} + +void +data_parser::element::update_capture() +{ + if (this->e_sub_elements != nullptr && !this->e_sub_elements->empty()) { + this->e_capture.c_begin + = this->e_sub_elements->front().e_capture.c_begin; + this->e_capture.c_end = this->e_sub_elements->back().e_capture.c_end; + } +} + +const data_parser::element& +data_parser::element::get_pair_value() const +{ + require(this->e_token == DNT_PAIR); + + return this->e_sub_elements->back(); +} + +data_token_t +data_parser::element::value_token() const +{ + data_token_t retval = DT_INVALID; + + if (this->e_token == DNT_VALUE) { + if (this->e_sub_elements != nullptr + && this->e_sub_elements->size() == 1) + { + retval = this->e_sub_elements->front().e_token; + } else { + retval = DT_SYMBOL; + } + } else { + retval = this->e_token; + } + return retval; +} + +const data_parser::element& +data_parser::element::get_value_elem() const +{ + if (this->e_token == DNT_VALUE) { + if (this->e_sub_elements != nullptr + && this->e_sub_elements->size() == 1) + { + return this->e_sub_elements->front(); + } + } + return *this; +} + +const data_parser::element& +data_parser::element::get_pair_elem() const +{ + if (this->e_token == DNT_VALUE) { + return this->e_sub_elements->front(); + } + return *this; +} + +void +data_parser::element::print(FILE* out, data_scanner& ds, int offset) const +{ + int lpc; + + if (this->e_sub_elements != nullptr) { + for (auto& e_sub_element : *this->e_sub_elements) { + e_sub_element.print(out, ds, offset + 1); + } + } + + fprintf(out, + "%4s %3d:%-3d ", + data_scanner::token2name(this->e_token), + this->e_capture.c_begin, + this->e_capture.c_end); + for (lpc = 0; lpc < this->e_capture.c_end; lpc++) { + if (lpc == this->e_capture.c_begin) { + fputc('^', out); + } else if (lpc == (this->e_capture.c_end - 1)) { + fputc('^', out); + } else if (lpc > this->e_capture.c_begin) { + fputc('-', out); + } else { + fputc(' ', out); + } + } + for (; lpc < (int) ds.get_input().length(); lpc++) { + fputc(' ', out); + } + + std::string sub = ds.to_string_fragment(this->e_capture).to_string(); + fprintf(out, " %s\n", sub.c_str()); +} + +data_parser::discover_format_state::discover_format_state() + : dfs_prefix_state(DFS_INIT), dfs_semi_state(DFS_INIT), + dfs_comma_state(DFS_INIT) +{ + memset(this->dfs_hist, 0, sizeof(this->dfs_hist)); +} + +void +data_parser::discover_format_state::update_for_element( + const data_parser::element& elem) +{ + this->dfs_prefix_state + = dfs_prefix_next(this->dfs_prefix_state, elem.e_token); + this->dfs_semi_state = dfs_semi_next(this->dfs_semi_state, elem.e_token); + this->dfs_comma_state = dfs_comma_next(this->dfs_comma_state, elem.e_token); + if (this->dfs_prefix_state != DFS_ERROR) { + if (this->dfs_semi_state == DFS_ERROR) { + this->dfs_semi_state = DFS_INIT; + } + if (this->dfs_comma_state == DFS_ERROR) { + this->dfs_comma_state = DFS_INIT; + } + } + this->dfs_hist[elem.e_token] += 1; +} + +void +data_parser::discover_format_state::finalize() +{ + data_token_t qualifier = this->dfs_format.df_qualifier; + data_token_t separator = this->dfs_format.df_separator; + data_token_t prefix_term = this->dfs_format.df_prefix_terminator; + + this->dfs_format = FORMAT_PLAIN; + if (this->dfs_hist[DT_EQUALS]) { + qualifier = DT_COLON; + separator = DT_EQUALS; + } + + if (this->dfs_semi_state != DFS_ERROR && this->dfs_hist[DT_SEMI]) { + this->dfs_format = FORMAT_SEMI; + } else if (this->dfs_comma_state != DFS_ERROR) { + this->dfs_format = FORMAT_COMMA; + if (separator == DT_COLON && this->dfs_hist[DT_COMMA] > 0) { + if (!((this->dfs_hist[DT_COLON] == this->dfs_hist[DT_COMMA]) + || ((this->dfs_hist[DT_COLON] - 1) + == this->dfs_hist[DT_COMMA]))) + { + separator = DT_INVALID; + if (this->dfs_hist[DT_COLON] == 1) { + prefix_term = DT_COLON; + } + } + } + } + + this->dfs_format.df_qualifier = qualifier; + this->dfs_format.df_separator = separator; + this->dfs_format.df_prefix_terminator = prefix_term; +} diff --git a/src/data_parser.hh b/src/data_parser.hh new file mode 100644 index 0000000..ca54a58 --- /dev/null +++ b/src/data_parser.hh @@ -0,0 +1,431 @@ +/** + * Copyright (c) 2007-2012, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef data_parser_hh +#define data_parser_hh + +#include +#include +#include +#include + +#include + +#include "base/lnav_log.hh" +#include "byte_array.hh" +#include "data_scanner.hh" + +#define ELEMENT_LIST_T(var) var("" #var, __FILE__, __LINE__, group_depth) +#define PUSH_FRONT(elem) push_front(elem, __FILE__, __LINE__) +#define PUSH_BACK(elem) push_back(elem, __FILE__, __LINE__) +#define POP_FRONT(elem) pop_front(__FILE__, __LINE__) +#define POP_BACK(elem) pop_back(__FILE__, __LINE__) +#define CLEAR(elem) clear2(__FILE__, __LINE__) +#define SWAP(other) swap(other, __FILE__, __LINE__) +#define SPLICE(pos, other, first, last) \ + splice(pos, other, first, last, __FILE__, __LINE__) + +template +void +strip(Container& container, UnaryPredicate p) +{ + while (!container.empty() && p(container.front())) { + container.POP_FRONT(); + } + while (!container.empty() && p(container.back())) { + container.POP_BACK(); + } +} + +enum data_format_state_t { + DFS_ERROR = -1, + DFS_INIT, + DFS_KEY, + DFS_EXPECTING_SEP, + DFS_VALUE, +}; + +struct data_format { + data_format(const char* name = nullptr, + data_token_t appender = DT_INVALID, + data_token_t terminator = DT_INVALID) noexcept + : df_name(name), df_appender(appender), df_terminator(terminator), + df_qualifier(DT_INVALID), df_separator(DT_COLON), + df_prefix_terminator(DT_INVALID) + { + } + + const char* df_name; + data_token_t df_appender; + data_token_t df_terminator; + data_token_t df_qualifier; + data_token_t df_separator; + data_token_t df_prefix_terminator; +}; + +data_format_state_t dfs_prefix_next(data_format_state_t state, + data_token_t next_token); +data_format_state_t dfs_semi_next(data_format_state_t state, + data_token_t next_token); +data_format_state_t dfs_comma_next(data_format_state_t state, + data_token_t next_token); + +#define LIST_INIT_TRACE \ + do { \ + if (TRACE_FILE != NULL) { \ + fprintf(TRACE_FILE, \ + "%p %s:%d %s %s %d\n", \ + this, \ + fn, \ + line, \ + __func__, \ + varname, \ + group_depth); \ + } \ + } while (false) + +#define LIST_DEINIT_TRACE \ + do { \ + if (TRACE_FILE != NULL) { \ + fprintf(TRACE_FILE, "%p %s:%d %s\n", this, fn, line, __func__); \ + } \ + } while (false) + +#define ELEMENT_TRACE \ + do { \ + if (TRACE_FILE != NULL) { \ + fprintf(TRACE_FILE, \ + "%p %s:%d %s %s %d:%d\n", \ + this, \ + fn, \ + line, \ + __func__, \ + data_scanner::token2name(elem.e_token), \ + elem.e_capture.c_begin, \ + elem.e_capture.c_end); \ + } \ + } while (false) + +#define LIST_TRACE \ + do { \ + if (TRACE_FILE != NULL) { \ + fprintf(TRACE_FILE, "%p %s:%d %s\n", this, fn, line, __func__); \ + } \ + } while (false) + +#define SPLICE_TRACE \ + do { \ + if (TRACE_FILE != NULL) { \ + fprintf(TRACE_FILE, \ + "%p %s:%d %s %d %p %d:%d\n", \ + this, \ + fn, \ + line, \ + __func__, \ + (int) std::distance(this->begin(), pos), \ + &other, \ + (int) std::distance(other.begin(), first), \ + (int) std::distance(last, other.end())); \ + } \ + } while (false); + +#define SWAP_TRACE(other) \ + do { \ + if (TRACE_FILE != NULL) { \ + fprintf(TRACE_FILE, \ + "%p %s:%d %s %p\n", \ + this, \ + fn, \ + line, \ + __func__, \ + &other); \ + } \ + } while (false); + +#define POINT_TRACE(name) \ + do { \ + if (TRACE_FILE) { \ + fprintf( \ + TRACE_FILE, "0x0 %s:%d point %s\n", __FILE__, __LINE__, name); \ + } \ + } while (false); + +#define FORMAT_TRACE(elist) \ + do { \ + if (TRACE_FILE) { \ + const data_format& df = elist.el_format; \ + fprintf(TRACE_FILE, \ + "%p %s:%d format %d %s %s %s %s %s\n", \ + &elist, \ + __FILE__, \ + __LINE__, \ + group_depth, \ + data_scanner::token2name(df.df_appender), \ + data_scanner::token2name(df.df_terminator), \ + data_scanner::token2name(df.df_qualifier), \ + data_scanner::token2name(df.df_separator), \ + data_scanner::token2name(df.df_prefix_terminator)); \ + } \ + } while (false); + +#define CONSUMED_TRACE(elist) \ + do { \ + if (TRACE_FILE) { \ + fprintf(TRACE_FILE, \ + "%p %s:%d consumed\n", \ + &elist, \ + __FILE__, \ + __LINE__); \ + } \ + } while (false); + +class data_parser { +public: + static data_format FORMAT_SEMI; + static data_format FORMAT_COMMA; + static data_format FORMAT_PLAIN; + + static FILE* TRACE_FILE; + + typedef byte_array<2, uint64_t> schema_id_t; + + struct element; + /* typedef std::list element_list_t; */ + + class element_list_t : public std::list { + public: + element_list_t(const char* varname, + const char* fn, + int line, + int group_depth = -1) + { + LIST_INIT_TRACE; + } + + element_list_t() + { + const char* varname = "_anon2_"; + const char* fn = __FILE__; + int line = __LINE__; + int group_depth = -1; + + LIST_INIT_TRACE; + } + + element_list_t(const element_list_t& other) : std::list(other) + { + this->el_format = other.el_format; + } + + ~element_list_t() + { + const char* fn = __FILE__; + int line = __LINE__; + + LIST_DEINIT_TRACE; + } + + void push_front(const element& elem, const char* fn, int line) + { + ELEMENT_TRACE; + + require(elem.e_capture.c_end >= -1); + this->std::list::push_front(elem); + } + + void push_back(const element& elem, const char* fn, int line) + { + ELEMENT_TRACE; + + require(elem.e_capture.c_end >= -1); + this->std::list::push_back(elem); + } + + void pop_front(const char* fn, int line) + { + LIST_TRACE; + + this->std::list::pop_front(); + } + + void pop_back(const char* fn, int line) + { + LIST_TRACE; + + this->std::list::pop_back(); + } + + void clear2(const char* fn, int line) + { + LIST_TRACE; + + this->std::list::clear(); + } + + void swap(element_list_t& other, const char* fn, int line) + { + SWAP_TRACE(other); + + this->std::list::swap(other); + } + + void splice(iterator pos, + element_list_t& other, + iterator first, + iterator last, + const char* fn, + int line) + { + SPLICE_TRACE; + + this->std::list::splice(pos, other, first, last); + } + + data_format el_format; + }; + + struct element { + element(); + + element(element_list_t& subs, + data_token_t token, + bool assign_subs_elements = true); + + element(const element& other); + + ~element(); + + element& operator=(const element& other); + + void assign_elements(element_list_t& subs); + + void update_capture(); + + const element& get_pair_value() const; + + data_token_t value_token() const; + + const element& get_value_elem() const; + + const element& get_pair_elem() const; + + void print(FILE* out, data_scanner&, int offset = 0) const; + + data_scanner::capture_t e_capture; + data_token_t e_token; + + element_list_t* e_sub_elements; + }; + + struct element_cmp { + bool operator()(data_token_t token, const element& elem) const + { + return token == elem.e_token || token == DT_ANY; + } + + bool operator()(const element& elem, data_token_t token) const + { + return (*this)(token, elem); + } + }; + + struct element_if { + element_if(data_token_t token) : ei_token(token) {} + + bool operator()(const element& a) const + { + return a.e_token == this->ei_token; + } + + private: + data_token_t ei_token; + }; + + struct element_is_space { + bool operator()(const element& el) const + { + return el.e_token == DT_WHITE || el.e_token == DT_CSI; + } + }; + + struct discover_format_state { + discover_format_state(); + + void update_for_element(const element& elem); + + void finalize(); + + data_format_state_t dfs_prefix_state; + data_format_state_t dfs_semi_state; + data_format_state_t dfs_comma_state; + int dfs_hist[DT_TERMINAL_MAX]; + + data_format dfs_format; + }; + + data_parser(data_scanner* ds); + + void pairup(schema_id_t* schema, + element_list_t& pairs_out, + element_list_t& in_list, + int group_depth = 0); + + void discover_format(); + + void end_of_value(element_list_t& el_stack, + element_list_t& key_comps, + element_list_t& value, + const element_list_t& in_list, + int group_depth); + + void parse(); + + std::string get_element_string(const element& elem) const; + + std::string get_string_up_to_value(const element& elem); + + const char* get_element_string(const element& elem, size_t& len_out); + + void print(FILE* out, element_list_t& el); + + std::vector dp_group_token; + std::list dp_group_stack; + + element_list_t dp_errors; + + element_list_t dp_pairs; + schema_id_t dp_schema_id; + std::string* dp_msg_format; + int dp_msg_format_begin; + +private: + data_scanner* dp_scanner; +}; + +#endif diff --git a/src/data_scanner.cc b/src/data_scanner.cc new file mode 100644 index 0000000..f270a13 --- /dev/null +++ b/src/data_scanner.cc @@ -0,0 +1,265 @@ +/** + * Copyright (c) 2007-2012, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include "data_scanner.hh" + +#include "config.h" + +void +data_scanner::capture_t::ltrim(const char* str) +{ + while (this->c_begin < this->c_end && isspace(str[this->c_begin])) { + this->c_begin += 1; + } +} + +static struct { + const char* name; +} MATCHERS[DT_TERMINAL_MAX] = { + { + "quot", + }, + { + "url", + }, + { + "path", + }, + { + "mac", + }, + { + "date", + }, + { + "time", + }, + { + "dt", + }, + /* { "qual", pcrepp("\\A([^\\s:=]+:[^\\s:=,]+(?!,)(?::[^\\s:=,]+)*)"), }, */ + { + "ipv6", + }, + { + "hexd", + }, + + { + "xmld", + }, + { + "xmlt", + }, + { + "xmlo", + }, + + { + "xmlc", + }, + + { + "h1", + }, + { + "h2", + }, + { + "h3", + }, + + { + "coln", + }, + { + "eq", + }, + { + "comm", + }, + { + "semi", + }, + + { + "empt", + }, + + { + "lcur", + }, + { + "rcur", + }, + + { + "lsqu", + }, + { + "rsqu", + }, + + { + "lpar", + }, + { + "rpar", + }, + + { + "lang", + }, + { + "rang", + }, + + { + "ipv4", + }, + + { + "uuid", + }, + + { + "cc", + }, + { + "vers", + }, + { + "oct", + }, + { + "pcnt", + }, + { + "num", + }, + { + "hex", + }, + + { + "mail", + }, + { + "cnst", + }, + { + "word", + }, + { + "sym", + }, + { + "line", + }, + { + "wspc", + }, + { + "dot", + }, + { + "escc", + }, + { + "csi", + }, + + { + "gbg", + }, +}; + +const char* DNT_NAMES[DNT_MAX - DNT_KEY] = { + "key", + "pair", + "val", + "row", + "unit", + "meas", + "var", + "rang", + "grp", +}; + +const char* +data_scanner::token2name(data_token_t token) +{ + if (token < 0) { + return "inv"; + } + if (token < DT_TERMINAL_MAX) { + return MATCHERS[token].name; + } + if (token == DT_ANY) { + return "any"; + } + return DNT_NAMES[token - DNT_KEY]; +} + +bool +data_scanner::is_credit_card(string_fragment cc) const +{ + auto cc_no_spaces = cc.to_string(); + auto new_end = std::remove_if(cc_no_spaces.begin(), + cc_no_spaces.end(), + [](auto ch) { return ch == ' '; }); + cc_no_spaces.erase(new_end, cc_no_spaces.end()); + int len = cc_no_spaces.size(); + int double_even_sum = 0; + + // Step 1: double every second digit, starting from right. + // if results in 2 digit number, add the digits to obtain single digit + // number. sum all answers to obtain 'double_even_sum' + + for (int lpc = len - 2; lpc >= 0; lpc = lpc - 2) { + int dbl = ((cc_no_spaces[lpc] - '0') * 2); + if (dbl > 9) { + dbl = (dbl / 10) + (dbl % 10); + } + double_even_sum += dbl; + } + + // Step 2: add every odd placed digit from right to double_even_sum's value + + for (int lpc = len - 1; lpc >= 0; lpc = lpc - 2) { + double_even_sum += (cc_no_spaces[lpc] - 48); + } + + // Step 3: check if final 'double_even_sum' is multiple of 10 + // if yes, it is valid. + + return double_even_sum % 10 == 0; +} diff --git a/src/data_scanner.hh b/src/data_scanner.hh new file mode 100644 index 0000000..3859ebb --- /dev/null +++ b/src/data_scanner.hh @@ -0,0 +1,211 @@ +/** + * Copyright (c) 2007-2012, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef data_scanner_hh +#define data_scanner_hh + +#include + +#include "pcrepp/pcre2pp.hh" +#include "shared_buffer.hh" + +enum data_token_t { + DT_INVALID = -1, + + DT_QUOTED_STRING = 0, + DT_URL, + DT_PATH, + DT_MAC_ADDRESS, + DT_DATE, + DT_TIME, + DT_DATE_TIME, + DT_IPV6_ADDRESS, + DT_HEX_DUMP, + DT_XML_DECL_TAG, + DT_XML_EMPTY_TAG, + DT_XML_OPEN_TAG, + DT_XML_CLOSE_TAG, + + DT_H1, + DT_H2, + DT_H3, + + /* DT_QUALIFIED_NAME, */ + + DT_COLON, + DT_EQUALS, + DT_COMMA, + DT_SEMI, + + DT_EMPTY_CONTAINER, + + DT_LCURLY, + DT_RCURLY, + + DT_LSQUARE, + DT_RSQUARE, + + DT_LPAREN, + DT_RPAREN, + + DT_LANGLE, + DT_RANGLE, + + DT_IPV4_ADDRESS, + DT_UUID, + + DT_CREDIT_CARD_NUMBER, + DT_VERSION_NUMBER, + DT_OCTAL_NUMBER, + DT_PERCENTAGE, + DT_NUMBER, + DT_HEX_NUMBER, + + DT_EMAIL, + DT_CONSTANT, + DT_WORD, + DT_SYMBOL, + DT_LINE, + DT_WHITE, + DT_DOT, + DT_ESCAPED_CHAR, + DT_CSI, + + DT_GARBAGE, + + DT_TERMINAL_MAX = DT_GARBAGE + 1, + + DNT_KEY = 50, + DNT_PAIR, + DNT_VALUE, + DNT_ROW, + DNT_UNITS, + DNT_MEASUREMENT, + DNT_VARIABLE_KEY, + DNT_ROWRANGE, + DNT_GROUP, + + DNT_MAX, + + DT_ANY = 100, +}; + +class data_scanner { +public: + static const char* token2name(data_token_t token); + + struct capture_t { + capture_t() + { /* We don't initialize anything since it's a perf hit. */ + } + + capture_t(int begin, int end) : c_begin(begin), c_end(end) + { + assert(begin <= end); + } + + int c_begin; + int c_end; + + void ltrim(const char* str); + + bool contains(int pos) const + { + return this->c_begin <= pos && pos < this->c_end; + } + + bool is_valid() const { return this->c_begin != -1; } + + int length() const { return this->c_end - this->c_begin; } + + bool empty() const { return this->c_begin == this->c_end; } + }; + + data_scanner(const std::string& line, size_t off = 0) + : ds_line(line), ds_input(this->ds_line), ds_init_offset(off), + ds_next_offset(off) + { + if (!line.empty() && line.back() == '.') { + this->ds_input.sf_end -= 1; + } + } + + explicit data_scanner(string_fragment sf) : ds_input(sf) + { + if (!sf.empty() && sf.back() == '.') { + this->ds_input.sf_end -= 1; + } + } + + explicit data_scanner(shared_buffer_ref& line, size_t off, size_t end) + : ds_sbr(line), ds_input(line.to_string_fragment().sub_range(0, end)), + ds_init_offset(off), ds_next_offset(off) + { + if (!this->ds_input.empty() && this->ds_input.back() == '.') { + this->ds_input.sf_end -= 1; + } + } + + struct tokenize_result { + data_token_t tr_token{DT_INVALID}; + capture_t tr_capture; + capture_t tr_inner_capture; + const char* tr_data{nullptr}; + + std::string to_string() const + { + return {&this->tr_data[this->tr_capture.c_begin], + (size_t) this->tr_capture.length()}; + } + }; + + nonstd::optional tokenize2(); + + void reset() { this->ds_next_offset = this->ds_init_offset; } + + int get_init_offset() const { return this->ds_init_offset; } + + string_fragment get_input() const { return this->ds_input; } + + string_fragment to_string_fragment(capture_t cap) const + { + return this->ds_input.sub_range(cap.c_begin, cap.c_end); + } + +private: + bool is_credit_card(string_fragment frag) const; + + std::string ds_line; + shared_buffer_ref ds_sbr; + string_fragment ds_input; + int ds_init_offset{0}; + int ds_next_offset{0}; +}; + +#endif diff --git a/src/data_scanner_re.cc b/src/data_scanner_re.cc new file mode 100644 index 0000000..21bb7ce --- /dev/null +++ b/src/data_scanner_re.cc @@ -0,0 +1,36096 @@ +/* Generated by re2c 3.0 on Mon Sep 19 01:36:56 2022 */ +#line 1 "../../lnav/src/data_scanner_re.re" +/** + * Copyright (c) 2015, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include "base/date_time_scanner.hh" +#include "config.h" +#include "data_scanner.hh" + +nonstd::optional data_scanner::tokenize2() +{ + data_token_t token_out = DT_INVALID; + capture_t cap_all; + capture_t cap_inner; +# define YYCTYPE unsigned char +# define CAPTURE(tok) { \ + if (YYCURSOR.val == EMPTY) { \ + this->ds_next_offset = this->ds_input.length(); \ + } else { \ + this->ds_next_offset = YYCURSOR.val - this->ds_input.udata(); \ + } \ + cap_all.c_end = this->ds_next_offset; \ + cap_inner.c_end = this->ds_next_offset; \ + token_out = tok; \ + } + +# define RET(tok) { \ + CAPTURE(tok); \ + return tokenize_result{token_out, cap_all, cap_inner, this->ds_input.data()}; \ + } + static const unsigned char *EMPTY = (const unsigned char *) ""; + + struct _YYCURSOR { + YYCTYPE operator*() const { + if (this->val < this->lim) { + return *val; + } + return '\0'; + } + + operator const YYCTYPE *() const { + if (this->val < this->lim) { + return this->val; + } + return EMPTY; + } + + const YYCTYPE *operator=(const YYCTYPE *rhs) { + this->val = rhs; + return rhs; + } + + const YYCTYPE *operator+(int rhs) { + return this->val + rhs; + } + + const _YYCURSOR *operator-=(int rhs) { + this->val -= rhs; + return this; + } + + _YYCURSOR& operator++() { + this->val += 1; + return *this; + } + + const YYCTYPE *val{nullptr}; + const YYCTYPE *lim{nullptr}; + } YYCURSOR; + YYCURSOR = (const unsigned char *) this->ds_input.udata() + this->ds_next_offset; + _YYCURSOR yyt1; + _YYCURSOR yyt2; + _YYCURSOR yyt3; + _YYCURSOR yyt4; + const YYCTYPE *YYLIMIT = (const unsigned char *) this->ds_input.end(); + const YYCTYPE *YYMARKER = YYCURSOR; + + YYCURSOR.lim = YYLIMIT; + + cap_all.c_begin = this->ds_next_offset; + cap_all.c_end = this->ds_next_offset; + cap_inner.c_begin = this->ds_next_offset; + cap_inner.c_end = this->ds_next_offset; + + +#line 117 "../../lnav/src/data_scanner_re.cc" +{ + YYCTYPE yych; + unsigned int yyaccept = 0; + static const unsigned char yybm[] = { + /* table 1 .. 8: 0 */ + 0, 231, 231, 231, 231, 231, 231, 231, + 231, 239, 231, 231, 231, 239, 231, 231, + 231, 231, 231, 231, 231, 231, 231, 231, + 231, 231, 231, 231, 231, 231, 231, 231, + 239, 231, 34, 231, 231, 231, 231, 36, + 231, 231, 231, 231, 231, 175, 231, 183, + 175, 175, 175, 175, 175, 175, 175, 175, + 175, 175, 175, 231, 231, 231, 128, 183, + 231, 175, 175, 175, 175, 175, 175, 175, + 175, 175, 175, 175, 175, 175, 175, 175, + 175, 175, 175, 175, 175, 175, 175, 175, + 175, 175, 175, 231, 1, 231, 231, 175, + 231, 175, 175, 175, 175, 175, 175, 175, + 175, 175, 175, 175, 175, 175, 175, 175, + 175, 175, 175, 175, 175, 175, 175, 175, + 175, 175, 175, 231, 231, 231, 231, 231, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + /* table 9 .. 16: 256 */ + 0, 249, 249, 249, 249, 249, 249, 249, + 249, 248, 248, 249, 249, 248, 249, 249, + 249, 249, 249, 249, 249, 249, 249, 249, + 249, 249, 249, 248, 249, 249, 249, 249, + 248, 249, 176, 249, 249, 252, 252, 104, + 248, 248, 249, 249, 249, 28, 249, 28, + 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 25, 249, 249, 252, 25, 28, + 249, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 248, 33, 248, 249, 25, + 249, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 248, 249, 248, 249, 249, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + /* table 17 .. 24: 512 */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 32, + 0, 0, 0, 0, 0, 19, 64, 0, + 159, 159, 159, 159, 159, 159, 159, 159, + 155, 155, 16, 0, 0, 0, 0, 0, + 0, 155, 155, 155, 155, 155, 155, 147, + 147, 147, 147, 147, 147, 147, 147, 147, + 147, 147, 147, 147, 147, 147, 147, 147, + 147, 147, 147, 0, 0, 0, 0, 145, + 0, 154, 154, 154, 154, 154, 154, 146, + 146, 146, 146, 146, 146, 146, 146, 146, + 146, 146, 146, 146, 146, 146, 146, 146, + 146, 146, 146, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + /* table 25 .. 32: 768 */ + 0, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 0, 2, 2, 2, 2, + 34, 10, 2, 10, 26, 11, 10, 0, + 10, 10, 10, 3, 2, 27, 27, 26, + 95, 95, 95, 95, 95, 95, 95, 95, + 95, 95, 2, 2, 2, 2, 2, 2, + 26, 91, 91, 91, 91, 91, 91, 91, + 91, 91, 91, 91, 91, 91, 91, 91, + 91, 91, 91, 91, 91, 91, 91, 91, + 91, 91, 91, 2, 8, 2, 10, 27, + 2, 219, 219, 219, 219, 219, 219, 219, + 219, 219, 219, 219, 219, 219, 219, 219, + 219, 219, 219, 219, 219, 219, 219, 219, + 219, 219, 219, 2, 2, 2, 10, 2, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + /* table 33 .. 37: 1024 */ + 0, 136, 136, 136, 136, 136, 136, 136, + 136, 160, 128, 136, 136, 160, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 0, 136, 136, 136, 136, + 160, 128, 0, 128, 136, 128, 128, 128, + 128, 128, 128, 128, 128, 152, 152, 136, + 216, 216, 216, 216, 216, 216, 216, 216, + 216, 216, 128, 192, 128, 192, 128, 192, + 136, 152, 152, 152, 152, 152, 152, 152, + 152, 152, 152, 152, 152, 152, 152, 152, + 152, 152, 152, 152, 152, 152, 152, 152, + 152, 152, 152, 128, 0, 128, 128, 152, + 128, 152, 152, 152, 152, 152, 152, 152, + 152, 152, 152, 152, 152, 152, 152, 152, + 152, 152, 152, 152, 152, 152, 152, 152, + 152, 152, 152, 128, 128, 128, 128, 136, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + }; + yych = *YYCURSOR; + if (yych <= 'E') { + if (yych <= '(') { + if (yych <= 0x1F) { + if (yych <= '\n') { + if (yych <= 0x00) goto yy2; + if (yych <= 0x08) goto yy3; + if (yych <= '\t') goto yy6; + goto yy8; + } else { + if (yych <= '\r') { + if (yych <= '\f') goto yy3; + goto yy10; + } else { + if (yych == 0x1B) goto yy11; + goto yy3; + } + } + } else { + if (yych <= '#') { + if (yych <= ' ') goto yy6; + if (yych == '"') goto yy14; + goto yy13; + } else { + if (yych <= '%') { + if (yych <= '$') goto yy3; + goto yy15; + } else { + if (yych <= '&') goto yy13; + if (yych <= '\'') goto yy16; + goto yy17; + } + } + } + } else { + if (yych <= '1') { + if (yych <= ',') { + if (yych <= ')') goto yy18; + if (yych <= '*') goto yy13; + if (yych <= '+') goto yy15; + goto yy19; + } else { + if (yych <= '.') { + if (yych <= '-') goto yy20; + goto yy21; + } else { + if (yych <= '/') goto yy23; + if (yych <= '0') goto yy25; + goto yy27; + } + } + } else { + if (yych <= '<') { + if (yych <= '9') { + if (yych <= '2') goto yy28; + goto yy29; + } else { + if (yych <= ':') goto yy30; + if (yych <= ';') goto yy32; + goto yy33; + } + } else { + if (yych <= '>') { + if (yych <= '=') goto yy35; + goto yy36; + } else { + if (yych <= '?') goto yy13; + if (yych <= '@') goto yy3; + goto yy37; + } + } + } + } + } else { + if (yych <= 'n') { + if (yych <= 'Z') { + if (yych <= 'Q') { + if (yych <= 'F') goto yy38; + if (yych == 'N') goto yy40; + goto yy39; + } else { + if (yych <= 'S') { + if (yych <= 'R') goto yy41; + goto yy39; + } else { + if (yych <= 'T') goto yy42; + if (yych <= 'U') goto yy41; + goto yy39; + } + } + } else { + if (yych <= '_') { + if (yych <= '\\') { + if (yych <= '[') goto yy43; + goto yy44; + } else { + if (yych <= ']') goto yy45; + if (yych <= '^') goto yy13; + goto yy46; + } + } else { + if (yych <= 'e') { + if (yych <= '`') goto yy13; + goto yy47; + } else { + if (yych <= 'f') goto yy48; + if (yych <= 'm') goto yy49; + goto yy50; + } + } + } + } else { + if (yych <= '}') { + if (yych <= 't') { + if (yych == 'r') goto yy51; + if (yych <= 's') goto yy49; + goto yy52; + } else { + if (yych <= 'z') { + if (yych <= 'u') goto yy53; + goto yy49; + } else { + if (yych <= '{') goto yy54; + if (yych <= '|') goto yy13; + goto yy55; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + if (yych <= '~') goto yy13; + goto yy3; + } else { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) goto yy56; + goto yy57; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) goto yy58; + goto yy59; + } else { + if (yych <= 0xF3) goto yy60; + if (yych <= 0xF4) goto yy61; + } + } + } + } + } +yy1: + YYCURSOR = YYMARKER; + if (yyaccept <= 17) { + if (yyaccept <= 8) { + if (yyaccept <= 4) { + if (yyaccept <= 2) { + if (yyaccept <= 1) { + if (yyaccept == 0) { + goto yy5; + } else { + goto yy7; + } + } else { + goto yy9; + } + } else { + if (yyaccept == 3) { + goto yy12; + } else { + goto yy22; + } + } + } else { + if (yyaccept <= 6) { + if (yyaccept == 5) { + goto yy24; + } else { + goto yy26; + } + } else { + if (yyaccept == 7) { + goto yy31; + } else { + goto yy34; + } + } + } + } else { + if (yyaccept <= 13) { + if (yyaccept <= 11) { + if (yyaccept <= 10) { + if (yyaccept == 9) { + goto yy72; + } else { + goto yy106; + } + } else { + goto yy109; + } + } else { + if (yyaccept == 12) { + goto yy113; + } else { + goto yy125; + } + } + } else { + if (yyaccept <= 15) { + if (yyaccept == 14) { + goto yy161; + } else { + goto yy222; + } + } else { + if (yyaccept == 16) { + yyt3 = yyt4; + goto yy222; + } else { + goto yy199; + } + } + } + } + } else { + if (yyaccept <= 26) { + if (yyaccept <= 22) { + if (yyaccept <= 20) { + if (yyaccept <= 19) { + if (yyaccept == 18) { + yyt2 = yyt1; + goto yy199; + } else { + goto yy338; + } + } else { + goto yy345; + } + } else { + if (yyaccept == 21) { + goto yy354; + } else { + goto yy371; + } + } + } else { + if (yyaccept <= 24) { + if (yyaccept == 23) { + goto yy428; + } else { + goto yy450; + } + } else { + if (yyaccept == 25) { + goto yy435; + } else { + goto yy217; + } + } + } + } else { + if (yyaccept <= 31) { + if (yyaccept <= 29) { + if (yyaccept <= 28) { + if (yyaccept == 27) { + goto yy310; + } else { + goto yy315; + } + } else { + goto yy643; + } + } else { + if (yyaccept == 30) { + goto yy654; + } else { + goto yy674; + } + } + } else { + if (yyaccept <= 33) { + if (yyaccept == 32) { + goto yy943; + } else { + goto yy971; + } + } else { + if (yyaccept == 34) { + goto yy1017; + } else { + goto yy1091; + } + } + } + } + } +yy2: + ++YYCURSOR; +#line 141 "../../lnav/src/data_scanner_re.re" + { return nonstd::nullopt; } +#line 586 "../../lnav/src/data_scanner_re.cc" +yy3: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); +yy4: + if (yybm[1024+yych] & 8) { + goto yy3; + } + if (yych <= 0xE0) { + if (yych <= ':') { + if (yych >= '-') goto yy62; + } else { + if (yych <= 0xC1) goto yy5; + if (yych <= 0xDF) goto yy56; + goto yy57; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) goto yy58; + goto yy59; + } else { + if (yych <= 0xF3) goto yy60; + if (yych <= 0xF4) goto yy61; + } + } +yy5: +#line 266 "../../lnav/src/data_scanner_re.re" + { + RET(DT_SYMBOL); + } +#line 616 "../../lnav/src/data_scanner_re.cc" +yy6: + yyaccept = 1; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '/') goto yy64; + if (yych <= '9') goto yy65; + if (yych <= ':') goto yy66; + goto yy64; +yy7: +#line 271 "../../lnav/src/data_scanner_re.re" + { RET(DT_WHITE); } +#line 627 "../../lnav/src/data_scanner_re.cc" +yy8: + yyaccept = 2; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '@') goto yy9; + if (yych <= 'Z') goto yy67; +yy9: +#line 270 "../../lnav/src/data_scanner_re.re" + { RET(DT_LINE); } +#line 636 "../../lnav/src/data_scanner_re.cc" +yy10: + yyaccept = 1; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '/') { + if (yych == '\n') goto yy68; + goto yy64; + } else { + if (yych <= '9') goto yy65; + if (yych <= ':') goto yy66; + goto yy64; + } +yy11: + yyaccept = 3; + yych = *(YYMARKER = ++YYCURSOR); + if (yych == '[') goto yy69; +yy12: +#line 274 "../../lnav/src/data_scanner_re.re" + { RET(DT_GARBAGE); } +#line 655 "../../lnav/src/data_scanner_re.cc" +yy13: + ++YYCURSOR; + goto yy12; +yy14: + yyaccept = 3; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= 0x1B) { + if (yych <= 0x00) goto yy12; + if (yych <= 0x1A) goto yy71; + goto yy12; + } else { + if (yych <= 0x7F) goto yy71; + if (yych <= 0xC1) goto yy12; + if (yych <= 0xF4) goto yy71; + goto yy12; + } +yy15: + yyaccept = 3; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '/') { + if (yych <= '*') { + if (yych == '%') goto yy81; + goto yy12; + } else { + if (yych == ',') goto yy12; + if (yych <= '.') goto yy81; + goto yy12; + } + } else { + if (yych <= '^') { + if (yych <= '9') goto yy81; + if (yych <= '?') goto yy12; + if (yych <= 'Z') goto yy81; + goto yy12; + } else { + if (yych == '`') goto yy12; + if (yych <= 'z') goto yy81; + goto yy12; + } + } +yy16: + yyaccept = 3; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= 0x1B) { + if (yych <= 0x00) goto yy12; + if (yych <= 0x1A) goto yy84; + goto yy12; + } else { + if (yych <= 0x7F) goto yy84; + if (yych <= 0xC1) goto yy12; + if (yych <= 0xF4) goto yy84; + goto yy12; + } +yy17: + yych = *++YYCURSOR; + if (yych == ')') goto yy93; +#line 225 "../../lnav/src/data_scanner_re.re" + { RET(DT_LPAREN); } +#line 714 "../../lnav/src/data_scanner_re.cc" +yy18: + ++YYCURSOR; +#line 226 "../../lnav/src/data_scanner_re.re" + { RET(DT_RPAREN); } +#line 719 "../../lnav/src/data_scanner_re.cc" +yy19: + ++YYCURSOR; +#line 218 "../../lnav/src/data_scanner_re.re" + { RET(DT_COMMA); } +#line 724 "../../lnav/src/data_scanner_re.cc" +yy20: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '0') { + if (yych <= '+') { + if (yych == '%') goto yy80; + if (yych <= '*') goto yy4; + goto yy80; + } else { + if (yych <= ',') goto yy4; + if (yych <= '.') goto yy46; + if (yych <= '/') goto yy4; + goto yy94; + } + } else { + if (yych <= 'Z') { + if (yych <= '9') goto yy95; + if (yych <= '?') goto yy4; + if (yych <= '@') goto yy96; + goto yy46; + } else { + if (yych <= '_') { + if (yych <= '^') goto yy4; + goto yy46; + } else { + if (yych <= '`') goto yy4; + if (yych <= 'z') goto yy46; + goto yy4; + } + } + } +yy21: + yyaccept = 4; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '/') { + if (yych <= '+') { + if (yych == '%') goto yy81; + if (yych >= '+') goto yy81; + } else { + if (yych <= ',') goto yy22; + if (yych <= '-') goto yy81; + if (yych <= '.') goto yy97; + goto yy98; + } + } else { + if (yych <= '^') { + if (yych <= '9') goto yy81; + if (yych <= '?') goto yy22; + if (yych <= 'Z') goto yy81; + } else { + if (yych == '`') goto yy22; + if (yych <= 'z') goto yy81; + } + } +yy22: +#line 272 "../../lnav/src/data_scanner_re.re" + { RET(DT_DOT); } +#line 782 "../../lnav/src/data_scanner_re.cc" +yy23: + yyaccept = 5; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '9') { + if (yych <= ' ') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy24; + if (yych <= 0x08) goto yy4; + if (yych >= '\v') goto yy4; + } else { + if (yych <= 0x1A) { + if (yych >= 0x0E) goto yy4; + } else { + if (yych <= 0x1B) goto yy24; + if (yych <= 0x1F) goto yy4; + } + } + } else { + if (yych <= '$') { + if (yych == '"') goto yy24; + if (yych <= '#') goto yy99; + goto yy101; + } else { + if (yych <= '\'') { + if (yych <= '&') goto yy99; + } else { + if (yych <= '*') goto yy99; + if (yych >= '-') goto yy101; + } + } + } + } else { + if (yych <= '^') { + if (yych <= 'P') { + if (yych <= ':') goto yy4; + if (yych <= '?') goto yy24; + if (yych <= 'O') goto yy101; + goto yy102; + } else { + if (yych <= '[') { + if (yych <= 'Z') goto yy101; + } else { + if (yych != ']') goto yy99; + } + } + } else { + if (yych <= '}') { + if (yych == '`') goto yy24; + if (yych <= 'z') goto yy101; + } else { + if (yych <= 0x7F) { + if (yych <= '~') goto yy99; + goto yy4; + } else { + if (yych <= 0xC1) goto yy24; + if (yych <= 0xF4) goto yy4; + } + } + } + } +yy24: +#line 171 "../../lnav/src/data_scanner_re.re" + { RET(DT_PATH); } +#line 846 "../../lnav/src/data_scanner_re.cc" +yy25: + yyaccept = 6; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[768+yych] & 32) { + goto yy103; + } + if (yych <= ':') { + if (yych <= '$') { + if (yych <= '\r') { + if (yych <= 0x08) { + if (yych >= 0x01) goto yy4; + } else { + if (yych <= '\n') goto yy26; + if (yych <= '\f') goto yy4; + } + } else { + if (yych <= 0x1B) { + if (yych <= 0x1A) goto yy4; + } else { + if (yych <= 0x1F) goto yy4; + if (yych >= '$') goto yy4; + } + } + } else { + if (yych <= '-') { + if (yych <= '*') { + if (yych <= '%') goto yy105; + } else { + if (yych <= '+') goto yy80; + if (yych >= '-') goto yy46; + } + } else { + if (yych <= '/') { + if (yych <= '.') goto yy107; + goto yy4; + } else { + if (yych <= '7') goto yy108; + if (yych <= '9') goto yy110; + goto yy111; + } + } + } + } else { + if (yych <= 'd') { + if (yych <= 'F') { + if (yych <= '@') { + if (yych >= '@') goto yy96; + } else { + if (yych == 'E') goto yy114; + goto yy112; + } + } else { + if (yych <= '^') { + if (yych <= 'Z') goto yy115; + } else { + if (yych <= '_') goto yy46; + if (yych >= 'a') goto yy112; + } + } + } else { + if (yych <= 'z') { + if (yych <= 'f') { + if (yych <= 'e') goto yy114; + goto yy112; + } else { + if (yych == 'x') goto yy116; + goto yy115; + } + } else { + if (yych <= 0x7F) { + if (yych >= 0x7F) goto yy4; + } else { + if (yych <= 0xC1) goto yy26; + if (yych <= 0xF4) goto yy4; + } + } + } + } +yy26: +#line 257 "../../lnav/src/data_scanner_re.re" + { RET(DT_NUMBER); } +#line 928 "../../lnav/src/data_scanner_re.cc" +yy27: + yyaccept = 6; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[768+yych] & 32) { + goto yy103; + } + if (yych <= '9') { + if (yych <= '#') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy26; + if (yych <= 0x08) goto yy4; + if (yych <= '\n') goto yy26; + goto yy4; + } else { + if (yych <= 0x1A) { + if (yych <= '\r') goto yy26; + goto yy4; + } else { + if (yych <= 0x1B) goto yy26; + if (yych <= 0x1F) goto yy4; + goto yy26; + } + } + } else { + if (yych <= '+') { + if (yych <= '$') goto yy4; + if (yych <= '%') goto yy105; + if (yych <= '*') goto yy26; + goto yy80; + } else { + if (yych <= '-') { + if (yych <= ',') goto yy26; + goto yy46; + } else { + if (yych <= '.') goto yy107; + if (yych <= '/') goto yy4; + goto yy117; + } + } + } + } else { + if (yych <= '_') { + if (yych <= 'D') { + if (yych <= ':') goto yy111; + if (yych <= '?') goto yy26; + if (yych <= '@') goto yy96; + goto yy112; + } else { + if (yych <= 'F') { + if (yych <= 'E') goto yy114; + goto yy112; + } else { + if (yych <= 'Z') goto yy115; + if (yych <= '^') goto yy26; + goto yy46; + } + } + } else { + if (yych <= 'z') { + if (yych <= 'd') { + if (yych <= '`') goto yy26; + goto yy112; + } else { + if (yych <= 'e') goto yy114; + if (yych <= 'f') goto yy112; + goto yy115; + } + } else { + if (yych <= 0x7F) { + if (yych <= '~') goto yy26; + goto yy4; + } else { + if (yych <= 0xC1) goto yy26; + if (yych <= 0xF4) goto yy4; + goto yy26; + } + } + } + } +yy28: + yyaccept = 6; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[768+yych] & 32) { + goto yy103; + } + if (yych <= '5') { + if (yych <= '#') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy26; + if (yych <= 0x08) goto yy4; + if (yych <= '\n') goto yy26; + goto yy4; + } else { + if (yych <= 0x1A) { + if (yych <= '\r') goto yy26; + goto yy4; + } else { + if (yych <= 0x1B) goto yy26; + if (yych <= 0x1F) goto yy4; + goto yy26; + } + } + } else { + if (yych <= ',') { + if (yych <= '%') { + if (yych <= '$') goto yy4; + goto yy105; + } else { + if (yych == '+') goto yy80; + goto yy26; + } + } else { + if (yych <= '.') { + if (yych <= '-') goto yy46; + goto yy107; + } else { + if (yych <= '/') goto yy4; + if (yych <= '4') goto yy117; + goto yy118; + } + } + } + } else { + if (yych <= '_') { + if (yych <= 'D') { + if (yych <= ':') { + if (yych <= '9') goto yy110; + goto yy111; + } else { + if (yych <= '?') goto yy26; + if (yych <= '@') goto yy96; + goto yy112; + } + } else { + if (yych <= 'F') { + if (yych <= 'E') goto yy114; + goto yy112; + } else { + if (yych <= 'Z') goto yy115; + if (yych <= '^') goto yy26; + goto yy46; + } + } + } else { + if (yych <= 'z') { + if (yych <= 'd') { + if (yych <= '`') goto yy26; + goto yy112; + } else { + if (yych <= 'e') goto yy114; + if (yych <= 'f') goto yy112; + goto yy115; + } + } else { + if (yych <= 0x7F) { + if (yych <= '~') goto yy26; + goto yy4; + } else { + if (yych <= 0xC1) goto yy26; + if (yych <= 0xF4) goto yy4; + goto yy26; + } + } + } + } +yy29: + yyaccept = 6; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[768+yych] & 32) { + goto yy103; + } + if (yych <= '9') { + if (yych <= '#') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy26; + if (yych <= 0x08) goto yy4; + if (yych <= '\n') goto yy26; + goto yy4; + } else { + if (yych <= 0x1A) { + if (yych <= '\r') goto yy26; + goto yy4; + } else { + if (yych <= 0x1B) goto yy26; + if (yych <= 0x1F) goto yy4; + goto yy26; + } + } + } else { + if (yych <= '+') { + if (yych <= '$') goto yy4; + if (yych <= '%') goto yy105; + if (yych <= '*') goto yy26; + goto yy80; + } else { + if (yych <= '-') { + if (yych <= ',') goto yy26; + goto yy46; + } else { + if (yych <= '.') goto yy107; + if (yych <= '/') goto yy4; + goto yy110; + } + } + } + } else { + if (yych <= '_') { + if (yych <= 'D') { + if (yych <= ':') goto yy111; + if (yych <= '?') goto yy26; + if (yych <= '@') goto yy96; + goto yy112; + } else { + if (yych <= 'F') { + if (yych <= 'E') goto yy114; + goto yy112; + } else { + if (yych <= 'Z') goto yy115; + if (yych <= '^') goto yy26; + goto yy46; + } + } + } else { + if (yych <= 'z') { + if (yych <= 'd') { + if (yych <= '`') goto yy26; + goto yy112; + } else { + if (yych <= 'e') goto yy114; + if (yych <= 'f') goto yy112; + goto yy115; + } + } else { + if (yych <= 0x7F) { + if (yych <= '~') goto yy26; + goto yy4; + } else { + if (yych <= 0xC1) goto yy26; + if (yych <= 0xF4) goto yy4; + goto yy26; + } + } + } + } +yy30: + yyaccept = 7; + yych = *(YYMARKER = ++YYCURSOR); + if (yych == ':') goto yy119; +yy31: +#line 216 "../../lnav/src/data_scanner_re.re" + { RET(DT_COLON); } +#line 1180 "../../lnav/src/data_scanner_re.cc" +yy32: + ++YYCURSOR; +#line 219 "../../lnav/src/data_scanner_re.re" + { RET(DT_SEMI); } +#line 1185 "../../lnav/src/data_scanner_re.cc" +yy33: + yyaccept = 8; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '>') { + if (yych <= '-') { + if (yych == '!') goto yy120; + if (yych >= '-') goto yy121; + } else { + if (yych <= '.') goto yy34; + if (yych <= '/') goto yy122; + if (yych <= ':') goto yy121; + } + } else { + if (yych <= '^') { + if (yych <= '?') goto yy123; + if (yych <= '@') goto yy34; + if (yych <= 'Z') goto yy121; + } else { + if (yych == '`') goto yy34; + if (yych <= 'z') goto yy121; + } + } +yy34: +#line 227 "../../lnav/src/data_scanner_re.re" + { RET(DT_LANGLE); } +#line 1211 "../../lnav/src/data_scanner_re.cc" +yy35: + ++YYCURSOR; +#line 217 "../../lnav/src/data_scanner_re.re" + { RET(DT_EQUALS); } +#line 1216 "../../lnav/src/data_scanner_re.cc" +yy36: + ++YYCURSOR; +#line 228 "../../lnav/src/data_scanner_re.re" + { RET(DT_RANGLE); } +#line 1221 "../../lnav/src/data_scanner_re.cc" +yy37: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '9') { + if (yych <= '*') { + if (yych <= '%') { + if (yych <= '$') goto yy4; + goto yy80; + } else { + if (yych == '\'') goto yy124; + goto yy4; + } + } else { + if (yych <= ',') { + if (yych <= '+') goto yy80; + goto yy4; + } else { + if (yych <= '.') goto yy46; + if (yych <= '/') goto yy4; + goto yy126; + } + } + } else { + if (yych <= 'Z') { + if (yych <= '?') { + if (yych <= ':') goto yy127; + goto yy4; + } else { + if (yych <= '@') goto yy96; + if (yych <= 'F') goto yy128; + goto yy129; + } + } else { + if (yych <= '`') { + if (yych == '_') goto yy46; + goto yy4; + } else { + if (yych <= 'f') goto yy130; + if (yych <= 'z') goto yy131; + goto yy4; + } + } + } +yy38: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= ':') { + if (yych <= '*') { + if (yych <= '%') { + if (yych <= '$') goto yy4; + goto yy80; + } else { + if (yych == '\'') goto yy124; + goto yy4; + } + } else { + if (yych <= '.') { + if (yych <= '+') goto yy80; + if (yych <= ',') goto yy4; + goto yy46; + } else { + if (yych <= '/') goto yy4; + if (yych <= '9') goto yy126; + goto yy127; + } + } + } else { + if (yych <= '^') { + if (yych <= 'A') { + if (yych <= '?') goto yy4; + if (yych <= '@') goto yy96; + goto yy132; + } else { + if (yych <= 'F') goto yy128; + if (yych <= 'Z') goto yy129; + goto yy4; + } + } else { + if (yych <= 'a') { + if (yych <= '_') goto yy46; + if (yych <= '`') goto yy4; + goto yy133; + } else { + if (yych <= 'f') goto yy130; + if (yych <= 'z') goto yy131; + goto yy4; + } + } + } +yy39: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '/') { + if (yych <= '\'') { + if (yych == '%') goto yy80; + if (yych <= '&') goto yy4; + goto yy124; + } else { + if (yych <= '+') { + if (yych <= '*') goto yy4; + goto yy80; + } else { + if (yych <= ',') goto yy4; + if (yych <= '.') goto yy46; + goto yy4; + } + } + } else { + if (yych <= 'Z') { + if (yych <= ':') { + if (yych <= '9') goto yy115; + goto yy134; + } else { + if (yych <= '?') goto yy4; + if (yych <= '@') goto yy96; + goto yy129; + } + } else { + if (yych <= '_') { + if (yych <= '^') goto yy4; + goto yy46; + } else { + if (yych <= '`') goto yy4; + if (yych <= 'z') goto yy131; + goto yy4; + } + } + } +yy40: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= ':') { + if (yych <= '*') { + if (yych <= '%') { + if (yych <= '$') goto yy4; + goto yy80; + } else { + if (yych == '\'') goto yy124; + goto yy4; + } + } else { + if (yych <= '.') { + if (yych <= '+') goto yy80; + if (yych <= ',') goto yy4; + goto yy46; + } else { + if (yych <= '/') goto yy4; + if (yych <= '9') goto yy115; + goto yy134; + } + } + } else { + if (yych <= '^') { + if (yych <= 'T') { + if (yych <= '?') goto yy4; + if (yych <= '@') goto yy96; + goto yy129; + } else { + if (yych <= 'U') goto yy135; + if (yych <= 'Z') goto yy129; + goto yy4; + } + } else { + if (yych <= 'n') { + if (yych <= '_') goto yy46; + if (yych <= '`') goto yy4; + goto yy131; + } else { + if (yych <= 'o') goto yy136; + if (yych <= 'z') goto yy131; + goto yy4; + } + } + } +yy41: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '/') { + if (yych <= '\'') { + if (yych == '%') goto yy80; + if (yych <= '&') goto yy4; + goto yy137; + } else { + if (yych <= '+') { + if (yych <= '*') goto yy4; + goto yy80; + } else { + if (yych <= ',') goto yy4; + if (yych <= '.') goto yy46; + goto yy4; + } + } + } else { + if (yych <= 'Z') { + if (yych <= ':') { + if (yych <= '9') goto yy115; + goto yy134; + } else { + if (yych <= '?') goto yy4; + if (yych <= '@') goto yy96; + goto yy129; + } + } else { + if (yych <= '_') { + if (yych <= '^') goto yy4; + goto yy46; + } else { + if (yych <= '`') goto yy4; + if (yych <= 'z') goto yy131; + goto yy4; + } + } + } +yy42: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= ':') { + if (yych <= '*') { + if (yych <= '%') { + if (yych <= '$') goto yy4; + goto yy80; + } else { + if (yych == '\'') goto yy124; + goto yy4; + } + } else { + if (yych <= '.') { + if (yych <= '+') goto yy80; + if (yych <= ',') goto yy4; + goto yy46; + } else { + if (yych <= '/') goto yy4; + if (yych <= '9') goto yy115; + goto yy134; + } + } + } else { + if (yych <= '^') { + if (yych <= 'Q') { + if (yych <= '?') goto yy4; + if (yych <= '@') goto yy96; + goto yy129; + } else { + if (yych <= 'R') goto yy138; + if (yych <= 'Z') goto yy129; + goto yy4; + } + } else { + if (yych <= 'q') { + if (yych <= '_') goto yy46; + if (yych <= '`') goto yy4; + goto yy131; + } else { + if (yych <= 'r') goto yy139; + if (yych <= 'z') goto yy131; + goto yy4; + } + } + } +yy43: + yych = *++YYCURSOR; + if (yych == ']') goto yy93; +#line 223 "../../lnav/src/data_scanner_re.re" + { RET(DT_LSQUARE); } +#line 1486 "../../lnav/src/data_scanner_re.cc" +yy44: + yyaccept = 3; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= 0x7F) { + if (yych <= '[') { + if (yych == '\n') goto yy12; + goto yy140; + } else { + if (yych <= '\\') goto yy98; + if (yych == 'n') goto yy68; + goto yy140; + } + } else { + if (yych <= 0xEF) { + if (yych <= 0xC1) goto yy12; + if (yych <= 0xDF) goto yy141; + if (yych <= 0xE0) goto yy142; + goto yy143; + } else { + if (yych <= 0xF0) goto yy144; + if (yych <= 0xF3) goto yy145; + if (yych <= 0xF4) goto yy146; + goto yy12; + } + } +yy45: + ++YYCURSOR; +#line 224 "../../lnav/src/data_scanner_re.re" + { RET(DT_RSQUARE); } +#line 1516 "../../lnav/src/data_scanner_re.cc" +yy46: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[1024+yych] & 16) { + goto yy46; + } + if (yych <= ',') { + if (yych <= 0x1B) { + if (yych <= '\n') { + if (yych <= 0x00) goto yy5; + if (yych <= 0x08) goto yy3; + goto yy5; + } else { + if (yych == '\r') goto yy5; + if (yych <= 0x1A) goto yy3; + goto yy5; + } + } else { + if (yych <= '$') { + if (yych <= 0x1F) goto yy3; + if (yych <= '#') goto yy5; + goto yy3; + } else { + if (yych <= '%') goto yy80; + if (yych == '+') goto yy80; + goto yy5; + } + } + } else { + if (yych <= 0xC1) { + if (yych <= '?') { + if (yych <= '/') goto yy3; + if (yych <= ':') goto yy62; + goto yy5; + } else { + if (yych <= '@') goto yy96; + if (yych == 0x7F) goto yy3; + goto yy5; + } + } else { + if (yych <= 0xEF) { + if (yych <= 0xDF) goto yy56; + if (yych <= 0xE0) goto yy57; + goto yy58; + } else { + if (yych <= 0xF0) goto yy59; + if (yych <= 0xF3) goto yy60; + if (yych <= 0xF4) goto yy61; + goto yy5; + } + } + } +yy47: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '9') { + if (yych <= '*') { + if (yych <= '%') { + if (yych <= '$') goto yy4; + goto yy80; + } else { + if (yych == '\'') goto yy124; + goto yy4; + } + } else { + if (yych <= ',') { + if (yych <= '+') goto yy80; + goto yy4; + } else { + if (yych <= '.') goto yy46; + if (yych <= '/') goto yy4; + goto yy126; + } + } + } else { + if (yych <= 'Z') { + if (yych <= '?') { + if (yych <= ':') goto yy147; + goto yy4; + } else { + if (yych <= '@') goto yy96; + if (yych <= 'F') goto yy128; + goto yy129; + } + } else { + if (yych <= '`') { + if (yych == '_') goto yy46; + goto yy4; + } else { + if (yych <= 'f') goto yy130; + if (yych <= 'z') goto yy131; + goto yy4; + } + } + } +yy48: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= ':') { + if (yych <= '*') { + if (yych <= '%') { + if (yych <= '$') goto yy4; + goto yy80; + } else { + if (yych == '\'') goto yy124; + goto yy4; + } + } else { + if (yych <= '.') { + if (yych <= '+') goto yy80; + if (yych <= ',') goto yy4; + goto yy46; + } else { + if (yych <= '/') goto yy4; + if (yych <= '9') goto yy126; + goto yy147; + } + } + } else { + if (yych <= '^') { + if (yych <= '@') { + if (yych <= '?') goto yy4; + goto yy96; + } else { + if (yych <= 'F') goto yy128; + if (yych <= 'Z') goto yy129; + goto yy4; + } + } else { + if (yych <= 'a') { + if (yych <= '_') goto yy46; + if (yych <= '`') goto yy4; + goto yy133; + } else { + if (yych <= 'f') goto yy130; + if (yych <= 'z') goto yy131; + goto yy4; + } + } + } +yy49: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '/') { + if (yych <= '\'') { + if (yych == '%') goto yy80; + if (yych <= '&') goto yy4; + goto yy124; + } else { + if (yych <= '+') { + if (yych <= '*') goto yy4; + goto yy80; + } else { + if (yych <= ',') goto yy4; + if (yych <= '.') goto yy46; + goto yy4; + } + } + } else { + if (yych <= 'Z') { + if (yych <= ':') { + if (yych <= '9') goto yy115; + goto yy148; + } else { + if (yych <= '?') goto yy4; + if (yych <= '@') goto yy96; + goto yy129; + } + } else { + if (yych <= '_') { + if (yych <= '^') goto yy4; + goto yy46; + } else { + if (yych <= '`') goto yy4; + if (yych <= 'z') goto yy131; + goto yy4; + } + } + } +yy50: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '9') { + if (yych <= '*') { + if (yych <= '%') { + if (yych <= '$') goto yy4; + goto yy80; + } else { + if (yych == '\'') goto yy124; + goto yy4; + } + } else { + if (yych <= ',') { + if (yych <= '+') goto yy80; + goto yy4; + } else { + if (yych <= '.') goto yy46; + if (yych <= '/') goto yy4; + goto yy115; + } + } + } else { + if (yych <= '^') { + if (yych <= '?') { + if (yych <= ':') goto yy148; + goto yy4; + } else { + if (yych <= '@') goto yy96; + if (yych <= 'Z') goto yy129; + goto yy4; + } + } else { + if (yych <= 't') { + if (yych <= '_') goto yy46; + if (yych <= '`') goto yy4; + goto yy131; + } else { + if (yych <= 'u') goto yy149; + if (yych <= 'z') goto yy131; + goto yy4; + } + } + } +yy51: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '/') { + if (yych <= '&') { + if (yych <= '"') { + if (yych <= '!') goto yy4; + goto yy70; + } else { + if (yych == '%') goto yy80; + goto yy4; + } + } else { + if (yych <= '+') { + if (yych <= '\'') goto yy150; + if (yych <= '*') goto yy4; + goto yy80; + } else { + if (yych <= ',') goto yy4; + if (yych <= '.') goto yy46; + goto yy4; + } + } + } else { + if (yych <= '^') { + if (yych <= '?') { + if (yych <= '9') goto yy115; + if (yych <= ':') goto yy148; + goto yy4; + } else { + if (yych <= '@') goto yy96; + if (yych <= 'Z') goto yy129; + goto yy4; + } + } else { + if (yych <= 'd') { + if (yych <= '_') goto yy46; + if (yych <= '`') goto yy4; + goto yy131; + } else { + if (yych <= 'e') goto yy151; + if (yych <= 'z') goto yy131; + goto yy4; + } + } + } +yy52: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '9') { + if (yych <= '*') { + if (yych <= '%') { + if (yych <= '$') goto yy4; + goto yy80; + } else { + if (yych == '\'') goto yy124; + goto yy4; + } + } else { + if (yych <= ',') { + if (yych <= '+') goto yy80; + goto yy4; + } else { + if (yych <= '.') goto yy46; + if (yych <= '/') goto yy4; + goto yy115; + } + } + } else { + if (yych <= '^') { + if (yych <= '?') { + if (yych <= ':') goto yy148; + goto yy4; + } else { + if (yych <= '@') goto yy96; + if (yych <= 'Z') goto yy129; + goto yy4; + } + } else { + if (yych <= 'q') { + if (yych <= '_') goto yy46; + if (yych <= '`') goto yy4; + goto yy131; + } else { + if (yych <= 'r') goto yy139; + if (yych <= 'z') goto yy131; + goto yy4; + } + } + } +yy53: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '.') { + if (yych <= '&') { + if (yych <= '"') { + if (yych <= '!') goto yy4; + goto yy70; + } else { + if (yych == '%') goto yy80; + goto yy4; + } + } else { + if (yych <= '*') { + if (yych <= '\'') goto yy150; + goto yy4; + } else { + if (yych <= '+') goto yy80; + if (yych <= ',') goto yy4; + goto yy46; + } + } + } else { + if (yych <= '@') { + if (yych <= '9') { + if (yych <= '/') goto yy4; + goto yy115; + } else { + if (yych <= ':') goto yy148; + if (yych <= '?') goto yy4; + goto yy96; + } + } else { + if (yych <= '_') { + if (yych <= 'Z') goto yy129; + if (yych <= '^') goto yy4; + goto yy46; + } else { + if (yych <= '`') goto yy4; + if (yych <= 'z') goto yy131; + goto yy4; + } + } + } +yy54: + yych = *++YYCURSOR; + if (yych == '}') goto yy93; +#line 221 "../../lnav/src/data_scanner_re.re" + { RET(DT_LCURLY); } +#line 1879 "../../lnav/src/data_scanner_re.cc" +yy55: + ++YYCURSOR; +#line 222 "../../lnav/src/data_scanner_re.re" + { RET(DT_RCURLY); } +#line 1884 "../../lnav/src/data_scanner_re.cc" +yy56: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy3; + goto yy1; +yy57: + yych = *++YYCURSOR; + if (yych <= 0x9F) goto yy1; + if (yych <= 0xBF) goto yy56; + goto yy1; +yy58: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy56; + goto yy1; +yy59: + yych = *++YYCURSOR; + if (yych <= 0x8F) goto yy1; + if (yych <= 0xBF) goto yy58; + goto yy1; +yy60: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy58; + goto yy1; +yy61: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0x8F) goto yy58; + goto yy1; +yy62: + yych = *++YYCURSOR; + if (yych == ':') goto yy152; + goto yy1; +yy63: + yych = *++YYCURSOR; +yy64: + if (yybm[1024+yych] & 32) { + goto yy63; + } + goto yy7; +yy65: + yych = *++YYCURSOR; + if (yych == ':') goto yy153; + goto yy1; +yy66: + yych = *++YYCURSOR; + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy154; + goto yy1; +yy67: + yych = *++YYCURSOR; + if (yych == '\n') goto yy1; + goto yy156; +yy68: + ++YYCURSOR; + goto yy9; +yy69: + yych = *++YYCURSOR; + if (yybm[1024+yych] & 64) { + goto yy69; + } + if (yych <= '@') goto yy1; + if (yych <= 'Z') goto yy157; + if (yych <= '`') goto yy1; + if (yych <= 'z') goto yy157; + goto yy1; +yy70: + yych = *++YYCURSOR; +yy71: + if (yybm[1024+yych] & 128) { + goto yy70; + } + if (yych <= 0xDF) { + if (yych <= '"') { + if (yych <= 0x1B) goto yy1; + } else { + if (yych <= '\\') goto yy73; + if (yych <= 0xC1) goto yy1; + goto yy74; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xE0) goto yy75; + if (yych <= 0xEF) goto yy76; + goto yy77; + } else { + if (yych <= 0xF3) goto yy78; + if (yych <= 0xF4) goto yy79; + goto yy1; + } + } + yyaccept = 9; + yych = *(YYMARKER = ++YYCURSOR); + if (yych == '"') goto yy70; +yy72: +#line 143 "../../lnav/src/data_scanner_re.re" + { + CAPTURE(DT_QUOTED_STRING); + switch (this->ds_input[cap_inner.c_begin]) { + case 'u': + case 'r': + cap_inner.c_begin += 1; + break; + } + cap_inner.c_begin += 1; + cap_inner.c_end -= 1; + return tokenize_result{token_out, cap_all, cap_inner, this->ds_input.data()}; + } +#line 1994 "../../lnav/src/data_scanner_re.cc" +yy73: + yych = *++YYCURSOR; + if (yych <= 0xDF) { + if (yych <= '\n') { + if (yych <= '\t') goto yy70; + goto yy1; + } else { + if (yych <= 0x7F) goto yy70; + if (yych <= 0xC1) goto yy1; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xE0) goto yy75; + if (yych <= 0xEF) goto yy76; + goto yy77; + } else { + if (yych <= 0xF3) goto yy78; + if (yych <= 0xF4) goto yy79; + goto yy1; + } + } +yy74: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy70; + goto yy1; +yy75: + yych = *++YYCURSOR; + if (yych <= 0x9F) goto yy1; + if (yych <= 0xBF) goto yy74; + goto yy1; +yy76: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy74; + goto yy1; +yy77: + yych = *++YYCURSOR; + if (yych <= 0x8F) goto yy1; + if (yych <= 0xBF) goto yy76; + goto yy1; +yy78: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy76; + goto yy1; +yy79: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0x8F) goto yy76; + goto yy1; +yy80: + yych = *++YYCURSOR; +yy81: + if (yybm[768+yych] & 1) { + goto yy80; + } + if (yych != '@') goto yy1; +yy82: + yych = *++YYCURSOR; + if (yych == '.') goto yy158; + goto yy159; +yy83: + yych = *++YYCURSOR; +yy84: + if (yybm[768+yych] & 2) { + goto yy83; + } + if (yych <= 0xDF) { + if (yych <= '\'') { + if (yych <= 0x1B) goto yy1; + } else { + if (yych <= '\\') goto yy86; + if (yych <= 0xC1) goto yy1; + goto yy87; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xE0) goto yy88; + if (yych <= 0xEF) goto yy89; + goto yy90; + } else { + if (yych <= 0xF3) goto yy91; + if (yych <= 0xF4) goto yy92; + goto yy1; + } + } +yy85: + yych = *++YYCURSOR; + if (yych <= 0x7F) { + if (yych <= 'R') { + if (yych == '\'') { + yyt2 = YYCURSOR; + goto yy162; + } + yyt2 = YYCURSOR; + goto yy160; + } else { + if (yych <= 'S') goto yy1; + if (yych == 's') goto yy1; + yyt2 = YYCURSOR; + goto yy160; + } + } else { + if (yych <= 0xEF) { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy163; + } + if (yych <= 0xE0) { + yyt2 = YYCURSOR; + goto yy164; + } + yyt2 = YYCURSOR; + goto yy165; + } else { + if (yych <= 0xF0) { + yyt2 = YYCURSOR; + goto yy166; + } + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy167; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy168; + } + goto yy1; + } + } +yy86: + yych = *++YYCURSOR; + if (yych <= 0xDF) { + if (yych <= '\n') { + if (yych <= '\t') goto yy83; + goto yy1; + } else { + if (yych <= 0x7F) goto yy83; + if (yych <= 0xC1) goto yy1; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xE0) goto yy88; + if (yych <= 0xEF) goto yy89; + goto yy90; + } else { + if (yych <= 0xF3) goto yy91; + if (yych <= 0xF4) goto yy92; + goto yy1; + } + } +yy87: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy83; + goto yy1; +yy88: + yych = *++YYCURSOR; + if (yych <= 0x9F) goto yy1; + if (yych <= 0xBF) goto yy87; + goto yy1; +yy89: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy87; + goto yy1; +yy90: + yych = *++YYCURSOR; + if (yych <= 0x8F) goto yy1; + if (yych <= 0xBF) goto yy89; + goto yy1; +yy91: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy89; + goto yy1; +yy92: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0x8F) goto yy89; + goto yy1; +yy93: + ++YYCURSOR; +#line 220 "../../lnav/src/data_scanner_re.re" + { RET(DT_EMPTY_CONTAINER); } +#line 2182 "../../lnav/src/data_scanner_re.cc" +yy94: + yyaccept = 6; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[768+yych] & 32) { + goto yy103; + } + if (yych <= ':') { + if (yych <= '$') { + if (yych <= '\r') { + if (yych <= 0x08) { + if (yych <= 0x00) goto yy26; + goto yy4; + } else { + if (yych <= '\n') goto yy26; + if (yych <= '\f') goto yy4; + goto yy26; + } + } else { + if (yych <= 0x1B) { + if (yych <= 0x1A) goto yy4; + goto yy26; + } else { + if (yych <= 0x1F) goto yy4; + if (yych <= '#') goto yy26; + goto yy4; + } + } + } else { + if (yych <= '-') { + if (yych <= '*') { + if (yych <= '%') goto yy105; + goto yy26; + } else { + if (yych <= '+') goto yy80; + if (yych <= ',') goto yy26; + goto yy46; + } + } else { + if (yych <= '/') { + if (yych <= '.') goto yy169; + goto yy4; + } else { + if (yych <= '7') goto yy170; + if (yych >= ':') goto yy4; + } + } + } + } else { + if (yych <= 'd') { + if (yych <= 'F') { + if (yych <= '@') { + if (yych <= '?') goto yy26; + goto yy96; + } else { + if (yych == 'E') goto yy172; + goto yy171; + } + } else { + if (yych <= '^') { + if (yych <= 'Z') goto yy46; + goto yy26; + } else { + if (yych <= '_') goto yy46; + if (yych <= '`') goto yy26; + goto yy171; + } + } + } else { + if (yych <= 'z') { + if (yych <= 'f') { + if (yych <= 'e') goto yy172; + goto yy171; + } else { + if (yych == 'x') goto yy173; + goto yy46; + } + } else { + if (yych <= 0x7F) { + if (yych <= '~') goto yy26; + goto yy4; + } else { + if (yych <= 0xC1) goto yy26; + if (yych <= 0xF4) goto yy4; + goto yy26; + } + } + } + } +yy95: + yyaccept = 6; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[768+yych] & 32) { + goto yy103; + } + if (yych <= '@') { + if (yych <= '$') { + if (yych <= '\r') { + if (yych <= 0x08) { + if (yych <= 0x00) goto yy26; + goto yy3; + } else { + if (yych <= '\n') goto yy26; + if (yych <= '\f') goto yy3; + goto yy26; + } + } else { + if (yych <= 0x1B) { + if (yych <= 0x1A) goto yy3; + goto yy26; + } else { + if (yych <= 0x1F) goto yy3; + if (yych <= '#') goto yy26; + goto yy3; + } + } + } else { + if (yych <= '-') { + if (yych <= '*') { + if (yych <= '%') goto yy105; + goto yy26; + } else { + if (yych <= '+') goto yy80; + if (yych <= ',') goto yy26; + goto yy46; + } + } else { + if (yych <= '9') { + if (yych <= '.') goto yy169; + if (yych <= '/') goto yy3; + goto yy95; + } else { + if (yych <= ':') goto yy62; + if (yych <= '?') goto yy26; + } + } + } + } else { + if (yych <= 'f') { + if (yych <= '^') { + if (yych <= 'E') { + if (yych <= 'D') goto yy171; + goto yy172; + } else { + if (yych <= 'F') goto yy171; + if (yych <= 'Z') goto yy46; + goto yy26; + } + } else { + if (yych <= '`') { + if (yych <= '_') goto yy46; + goto yy26; + } else { + if (yych == 'e') goto yy172; + goto yy171; + } + } + } else { + if (yych <= 0xDF) { + if (yych <= '~') { + if (yych <= 'z') goto yy46; + goto yy26; + } else { + if (yych <= 0x7F) goto yy3; + if (yych <= 0xC1) goto yy26; + goto yy56; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xE0) goto yy57; + if (yych <= 0xEF) goto yy58; + goto yy59; + } else { + if (yych <= 0xF3) goto yy60; + if (yych <= 0xF4) goto yy61; + goto yy26; + } + } + } + } +yy96: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '9') { + if (yych <= ',') goto yy4; + if (yych == '/') goto yy4; + goto yy174; + } else { + if (yych <= 'Z') { + if (yych <= '@') goto yy4; + goto yy174; + } else { + if (yych <= '`') goto yy4; + if (yych <= 'z') goto yy174; + goto yy4; + } + } +yy97: + yych = *++YYCURSOR; + if (yych != '/') goto yy81; +yy98: + yych = *++YYCURSOR; + if (yych == 'P') goto yy175; + goto yy100; +yy99: + yych = *++YYCURSOR; +yy100: + if (yybm[768+yych] & 8) { + goto yy99; + } + goto yy24; +yy101: + yyaccept = 5; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[768+yych] & 16) { + goto yy101; + } + if (yych <= ',') { + if (yych <= 0x1B) { + if (yych <= '\n') { + if (yych <= 0x00) goto yy24; + if (yych <= 0x08) goto yy3; + goto yy24; + } else { + if (yych == '\r') goto yy24; + if (yych <= 0x1A) goto yy3; + goto yy24; + } + } else { + if (yych <= '"') { + if (yych <= 0x1F) goto yy3; + if (yych == '!') goto yy99; + goto yy24; + } else { + if (yych == '\'') goto yy24; + if (yych <= '*') goto yy99; + goto yy24; + } + } + } else { + if (yych <= 0x7F) { + if (yych <= ']') { + if (yych <= ':') goto yy62; + if (yych == '\\') goto yy99; + goto yy24; + } else { + if (yych <= '^') goto yy99; + if (yych <= '}') goto yy24; + if (yych <= '~') goto yy99; + goto yy3; + } + } else { + if (yych <= 0xEF) { + if (yych <= 0xC1) goto yy24; + if (yych <= 0xDF) goto yy56; + if (yych <= 0xE0) goto yy57; + goto yy58; + } else { + if (yych <= 0xF0) goto yy59; + if (yych <= 0xF3) goto yy60; + if (yych <= 0xF4) goto yy61; + goto yy24; + } + } + } +yy102: + yyaccept = 5; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '9') { + if (yych <= ' ') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy24; + if (yych <= 0x08) goto yy4; + if (yych <= '\n') goto yy24; + goto yy4; + } else { + if (yych <= 0x1A) { + if (yych <= '\r') goto yy24; + goto yy4; + } else { + if (yych <= 0x1B) goto yy24; + if (yych <= 0x1F) goto yy4; + goto yy24; + } + } + } else { + if (yych <= '$') { + if (yych == '"') goto yy24; + if (yych <= '#') goto yy99; + goto yy101; + } else { + if (yych <= '\'') { + if (yych <= '&') goto yy99; + goto yy24; + } else { + if (yych <= '*') goto yy99; + if (yych <= ',') goto yy24; + goto yy101; + } + } + } + } else { + if (yych <= '`') { + if (yych <= '[') { + if (yych <= ':') goto yy4; + if (yych <= '?') goto yy24; + if (yych <= 'Z') goto yy101; + goto yy24; + } else { + if (yych <= ']') { + if (yych <= '\\') goto yy99; + goto yy24; + } else { + if (yych <= '^') goto yy99; + if (yych <= '_') goto yy101; + goto yy24; + } + } + } else { + if (yych <= '}') { + if (yych == 'r') goto yy176; + if (yych <= 'z') goto yy101; + goto yy24; + } else { + if (yych <= 0x7F) { + if (yych <= '~') goto yy99; + goto yy4; + } else { + if (yych <= 0xC1) goto yy24; + if (yych <= 0xF4) goto yy4; + goto yy24; + } + } + } + } +yy103: + yych = *++YYCURSOR; +yy104: + if (yybm[768+yych] & 32) { + goto yy103; + } + if (yych == '%') goto yy177; + goto yy1; +yy105: + yyaccept = 10; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '/') { + if (yych <= '*') { + if (yych == '%') goto yy81; + } else { + if (yych == ',') goto yy106; + if (yych <= '.') goto yy81; + } + } else { + if (yych <= '^') { + if (yych <= '9') goto yy81; + if (yych <= '?') goto yy106; + if (yych <= 'Z') goto yy81; + } else { + if (yych == '`') goto yy106; + if (yych <= 'z') goto yy81; + } + } +yy106: +#line 256 "../../lnav/src/data_scanner_re.re" + { RET(DT_PERCENTAGE); } +#line 2548 "../../lnav/src/data_scanner_re.cc" +yy107: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '1') { + if (yych <= '+') { + if (yych == '%') goto yy80; + if (yych <= '*') goto yy4; + goto yy80; + } else { + if (yych <= '.') { + if (yych <= ',') goto yy4; + goto yy46; + } else { + if (yych <= '/') goto yy4; + if (yych <= '0') goto yy178; + goto yy179; + } + } + } else { + if (yych <= 'Z') { + if (yych <= '9') { + if (yych <= '2') goto yy180; + goto yy178; + } else { + if (yych <= '?') goto yy4; + if (yych <= '@') goto yy96; + goto yy46; + } + } else { + if (yych <= '_') { + if (yych <= '^') goto yy4; + goto yy46; + } else { + if (yych <= '`') goto yy4; + if (yych <= 'z') goto yy46; + goto yy4; + } + } + } +yy108: + yyaccept = 11; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[768+yych] & 32) { + goto yy103; + } + if (yych <= '9') { + if (yych <= '#') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy109; + if (yych <= 0x08) goto yy4; + if (yych >= '\v') goto yy4; + } else { + if (yych <= 0x1A) { + if (yych >= 0x0E) goto yy4; + } else { + if (yych <= 0x1B) goto yy109; + if (yych <= 0x1F) goto yy4; + } + } + } else { + if (yych <= ',') { + if (yych <= '%') { + if (yych <= '$') goto yy4; + goto yy105; + } else { + if (yych == '+') goto yy80; + } + } else { + if (yych <= '.') { + if (yych <= '-') goto yy181; + goto yy182; + } else { + if (yych <= '/') goto yy183; + if (yych <= '7') goto yy184; + goto yy185; + } + } + } + } else { + if (yych <= '_') { + if (yych <= 'D') { + if (yych <= ':') goto yy186; + if (yych <= '?') goto yy109; + if (yych <= '@') goto yy96; + goto yy187; + } else { + if (yych <= 'F') { + if (yych <= 'E') goto yy188; + goto yy187; + } else { + if (yych <= 'Z') goto yy115; + if (yych >= '_') goto yy46; + } + } + } else { + if (yych <= 'z') { + if (yych <= 'd') { + if (yych >= 'a') goto yy187; + } else { + if (yych <= 'e') goto yy188; + if (yych <= 'f') goto yy187; + goto yy115; + } + } else { + if (yych <= 0x7F) { + if (yych >= 0x7F) goto yy4; + } else { + if (yych <= 0xC1) goto yy109; + if (yych <= 0xF4) goto yy4; + } + } + } + } +yy109: +#line 255 "../../lnav/src/data_scanner_re.re" + { RET(DT_OCTAL_NUMBER); } +#line 2665 "../../lnav/src/data_scanner_re.cc" +yy110: + yyaccept = 6; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[768+yych] & 32) { + goto yy103; + } + if (yych <= '9') { + if (yych <= '#') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy26; + if (yych <= 0x08) goto yy4; + if (yych <= '\n') goto yy26; + goto yy4; + } else { + if (yych <= 0x1A) { + if (yych <= '\r') goto yy26; + goto yy4; + } else { + if (yych <= 0x1B) goto yy26; + if (yych <= 0x1F) goto yy4; + goto yy26; + } + } + } else { + if (yych <= '+') { + if (yych <= '$') goto yy4; + if (yych <= '%') goto yy105; + if (yych <= '*') goto yy26; + goto yy80; + } else { + if (yych <= '-') { + if (yych <= ',') goto yy26; + goto yy181; + } else { + if (yych <= '.') goto yy182; + if (yych <= '/') goto yy183; + goto yy185; + } + } + } + } else { + if (yych <= '_') { + if (yych <= 'D') { + if (yych <= ':') goto yy186; + if (yych <= '?') goto yy26; + if (yych <= '@') goto yy96; + goto yy187; + } else { + if (yych <= 'F') { + if (yych <= 'E') goto yy188; + goto yy187; + } else { + if (yych <= 'Z') goto yy115; + if (yych <= '^') goto yy26; + goto yy46; + } + } + } else { + if (yych <= 'z') { + if (yych <= 'd') { + if (yych <= '`') goto yy26; + goto yy187; + } else { + if (yych <= 'e') goto yy188; + if (yych <= 'f') goto yy187; + goto yy115; + } + } else { + if (yych <= 0x7F) { + if (yych <= '~') goto yy26; + goto yy4; + } else { + if (yych <= 0xC1) goto yy26; + if (yych <= 0xF4) goto yy4; + goto yy26; + } + } + } + } +yy111: + yych = *++YYCURSOR; + if (yych <= ':') { + if (yych <= '.') goto yy1; + if (yych <= '/') goto yy189; + if (yych <= '9') goto yy190; + goto yy191; + } else { + if (yych <= 'F') { + if (yych <= '@') goto yy1; + goto yy192; + } else { + if (yych <= '`') goto yy1; + if (yych <= 'f') goto yy192; + goto yy1; + } + } +yy112: + yyaccept = 12; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '.') { + if (yych <= 0x1F) { + if (yych <= '\f') { + if (yych <= 0x00) goto yy113; + if (yych <= 0x08) goto yy4; + if (yych >= '\v') goto yy4; + } else { + if (yych <= '\r') goto yy113; + if (yych != 0x1B) goto yy4; + } + } else { + if (yych <= '*') { + if (yych <= '#') goto yy113; + if (yych <= '$') goto yy4; + if (yych <= '%') goto yy80; + } else { + if (yych <= '+') goto yy80; + if (yych <= ',') goto yy113; + if (yych <= '-') goto yy181; + goto yy46; + } + } + } else { + if (yych <= '^') { + if (yych <= '?') { + if (yych <= '/') goto yy4; + if (yych <= '9') goto yy187; + if (yych <= ':') goto yy193; + } else { + if (yych <= '@') goto yy96; + if (yych <= 'F') goto yy187; + if (yych <= 'Z') goto yy115; + } + } else { + if (yych <= 'z') { + if (yych <= '_') goto yy46; + if (yych <= '`') goto yy113; + if (yych <= 'f') goto yy187; + goto yy115; + } else { + if (yych <= 0x7F) { + if (yych >= 0x7F) goto yy4; + } else { + if (yych <= 0xC1) goto yy113; + if (yych <= 0xF4) goto yy4; + } + } + } + } +yy113: +#line 258 "../../lnav/src/data_scanner_re.re" + { RET(DT_HEX_NUMBER); } +#line 2817 "../../lnav/src/data_scanner_re.cc" +yy114: + yyaccept = 12; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '.') { + if (yych <= 0x1F) { + if (yych <= '\f') { + if (yych <= 0x00) goto yy113; + if (yych <= 0x08) goto yy4; + if (yych <= '\n') goto yy113; + goto yy4; + } else { + if (yych <= '\r') goto yy113; + if (yych == 0x1B) goto yy113; + goto yy4; + } + } else { + if (yych <= '*') { + if (yych <= '#') goto yy113; + if (yych <= '$') goto yy4; + if (yych <= '%') goto yy80; + goto yy113; + } else { + if (yych <= '+') goto yy194; + if (yych <= ',') goto yy113; + if (yych <= '-') goto yy195; + goto yy46; + } + } + } else { + if (yych <= '^') { + if (yych <= '?') { + if (yych <= '/') goto yy4; + if (yych <= '9') goto yy187; + if (yych <= ':') goto yy193; + goto yy113; + } else { + if (yych <= '@') goto yy96; + if (yych <= 'F') goto yy187; + if (yych >= '[') goto yy113; + } + } else { + if (yych <= 'z') { + if (yych <= '_') goto yy46; + if (yych <= '`') goto yy113; + if (yych <= 'f') goto yy187; + } else { + if (yych <= 0x7F) { + if (yych <= '~') goto yy113; + goto yy4; + } else { + if (yych <= 0xC1) goto yy113; + if (yych <= 0xF4) goto yy4; + goto yy113; + } + } + } + } +yy115: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[768+yych] & 64) { + goto yy115; + } + if (yych <= '.') { + if (yych <= 0x1B) { + if (yych <= '\n') { + if (yych <= 0x00) goto yy5; + if (yych <= 0x08) goto yy3; + goto yy5; + } else { + if (yych == '\r') goto yy5; + if (yych <= 0x1A) goto yy3; + goto yy5; + } + } else { + if (yych <= '%') { + if (yych <= 0x1F) goto yy3; + if (yych <= '#') goto yy5; + if (yych <= '$') goto yy3; + goto yy80; + } else { + if (yych == '+') goto yy80; + if (yych <= ',') goto yy5; + goto yy46; + } + } + } else { + if (yych <= 0x7F) { + if (yych <= '@') { + if (yych <= '/') goto yy3; + if (yych <= ':') goto yy148; + if (yych <= '?') goto yy5; + goto yy96; + } else { + if (yych == '_') goto yy46; + if (yych <= '~') goto yy5; + goto yy3; + } + } else { + if (yych <= 0xEF) { + if (yych <= 0xC1) goto yy5; + if (yych <= 0xDF) goto yy56; + if (yych <= 0xE0) goto yy57; + goto yy58; + } else { + if (yych <= 0xF0) goto yy59; + if (yych <= 0xF3) goto yy60; + if (yych <= 0xF4) goto yy61; + goto yy5; + } + } + } +yy116: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= ':') { + if (yych <= '+') { + if (yych == '%') goto yy80; + if (yych <= '*') goto yy4; + goto yy80; + } else { + if (yych <= '.') { + if (yych <= ',') goto yy4; + goto yy46; + } else { + if (yych <= '/') goto yy4; + if (yych <= '9') goto yy196; + goto yy148; + } + } + } else { + if (yych <= '^') { + if (yych <= '@') { + if (yych <= '?') goto yy4; + goto yy96; + } else { + if (yych <= 'F') goto yy196; + if (yych <= 'Z') goto yy115; + goto yy4; + } + } else { + if (yych <= '`') { + if (yych <= '_') goto yy46; + goto yy4; + } else { + if (yych <= 'f') goto yy196; + if (yych <= 'z') goto yy115; + goto yy4; + } + } + } +yy117: + yyaccept = 6; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[768+yych] & 32) { + goto yy103; + } + if (yych <= '9') { + if (yych <= '#') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy26; + if (yych <= 0x08) goto yy4; + if (yych <= '\n') goto yy26; + goto yy4; + } else { + if (yych <= 0x1A) { + if (yych <= '\r') goto yy26; + goto yy4; + } else { + if (yych <= 0x1B) goto yy26; + if (yych <= 0x1F) goto yy4; + goto yy26; + } + } + } else { + if (yych <= '+') { + if (yych <= '$') goto yy4; + if (yych <= '%') goto yy105; + if (yych <= '*') goto yy26; + goto yy80; + } else { + if (yych <= '-') { + if (yych <= ',') goto yy26; + goto yy181; + } else { + if (yych <= '.') goto yy182; + if (yych <= '/') goto yy183; + goto yy197; + } + } + } + } else { + if (yych <= '_') { + if (yych <= 'D') { + if (yych <= ':') goto yy186; + if (yych <= '?') goto yy26; + if (yych <= '@') goto yy96; + goto yy187; + } else { + if (yych <= 'F') { + if (yych <= 'E') goto yy188; + goto yy187; + } else { + if (yych <= 'Z') goto yy115; + if (yych <= '^') goto yy26; + goto yy46; + } + } + } else { + if (yych <= 'z') { + if (yych <= 'd') { + if (yych <= '`') goto yy26; + goto yy187; + } else { + if (yych <= 'e') goto yy188; + if (yych <= 'f') goto yy187; + goto yy115; + } + } else { + if (yych <= 0x7F) { + if (yych <= '~') goto yy26; + goto yy4; + } else { + if (yych <= 0xC1) goto yy26; + if (yych <= 0xF4) goto yy4; + goto yy26; + } + } + } + } +yy118: + yyaccept = 6; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[768+yych] & 32) { + goto yy103; + } + if (yych <= '9') { + if (yych <= '#') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy26; + if (yych <= 0x08) goto yy4; + if (yych <= '\n') goto yy26; + goto yy4; + } else { + if (yych <= 0x1A) { + if (yych <= '\r') goto yy26; + goto yy4; + } else { + if (yych <= 0x1B) goto yy26; + if (yych <= 0x1F) goto yy4; + goto yy26; + } + } + } else { + if (yych <= ',') { + if (yych <= '%') { + if (yych <= '$') goto yy4; + goto yy105; + } else { + if (yych == '+') goto yy80; + goto yy26; + } + } else { + if (yych <= '.') { + if (yych <= '-') goto yy181; + goto yy182; + } else { + if (yych <= '/') goto yy183; + if (yych <= '5') goto yy197; + goto yy185; + } + } + } + } else { + if (yych <= '_') { + if (yych <= 'D') { + if (yych <= ':') goto yy186; + if (yych <= '?') goto yy26; + if (yych <= '@') goto yy96; + goto yy187; + } else { + if (yych <= 'F') { + if (yych <= 'E') goto yy188; + goto yy187; + } else { + if (yych <= 'Z') goto yy115; + if (yych <= '^') goto yy26; + goto yy46; + } + } + } else { + if (yych <= 'z') { + if (yych <= 'd') { + if (yych <= '`') goto yy26; + goto yy187; + } else { + if (yych <= 'e') goto yy188; + if (yych <= 'f') goto yy187; + goto yy115; + } + } else { + if (yych <= 0x7F) { + if (yych <= '~') goto yy26; + goto yy4; + } else { + if (yych <= 0xC1) goto yy26; + if (yych <= 0xF4) goto yy4; + goto yy26; + } + } + } + } +yy119: + yych = *++YYCURSOR; + if (yych <= '`') { + if (yych <= '9') { + if (yych <= '0') { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy200; + } else { + if (yych <= '1') goto yy201; + if (yych <= '2') goto yy202; + goto yy200; + } + } else { + if (yych <= 'E') { + if (yych <= ':') goto yy1; + if (yych <= '@') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy203; + } else { + if (yych <= 'F') goto yy204; + if (yych <= 'Z') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } + } + } else { + if (yych <= 0xDF) { + if (yych <= 'z') { + if (yych <= 'e') goto yy203; + if (yych <= 'f') goto yy204; + goto yy1; + } else { + if (yych <= 0x7F) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0xC1) goto yy1; + yyt2 = YYCURSOR; + goto yy205; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xE0) { + yyt2 = YYCURSOR; + goto yy206; + } + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy120: + yych = *++YYCURSOR; + if (yych <= '@') { + if (yych <= '-') { + if (yych <= ',') goto yy1; + goto yy211; + } else { + if (yych <= '/') goto yy1; + if (yych <= ':') goto yy211; + goto yy1; + } + } else { + if (yych <= '_') { + if (yych <= 'Z') goto yy211; + if (yych <= '^') goto yy1; + goto yy211; + } else { + if (yych <= '`') goto yy1; + if (yych <= 'z') goto yy211; + goto yy1; + } + } +yy121: + yych = *++YYCURSOR; + if (yybm[512+yych] & 16) { + goto yy214; + } + goto yy213; +yy122: + yych = *++YYCURSOR; + if (yych <= '\r') { + if (yych == '\t') goto yy1; + if (yych <= '\f') goto yy219; + goto yy1; + } else { + if (yych <= ' ') { + if (yych <= 0x1F) goto yy219; + goto yy1; + } else { + if (yych == '>') goto yy1; + goto yy219; + } + } +yy123: + yych = *++YYCURSOR; + if (yych <= '@') { + if (yych <= '-') { + if (yych <= ',') goto yy1; + goto yy220; + } else { + if (yych <= '/') goto yy1; + if (yych <= ':') goto yy220; + goto yy1; + } + } else { + if (yych <= '_') { + if (yych <= 'Z') goto yy220; + if (yych <= '^') goto yy1; + goto yy220; + } else { + if (yych <= '`') goto yy1; + if (yych <= 'z') goto yy220; + goto yy1; + } + } +yy124: + yyaccept = 13; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[768+yych] & 128) { + goto yy137; + } + if (yych <= '*') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy225; + if (yych <= 0x08) goto yy125; + if (yych <= '\n') goto yy225; + } else { + if (yych <= 0x1F) { + if (yych <= '\r') goto yy225; + } else { + if (yych <= '"') goto yy225; + if (yych >= '\'') goto yy225; + } + } + } else { + if (yych <= '.') { + if (yych == ',') goto yy225; + if (yych >= '.') { + yyt4 = YYCURSOR; + goto yy226; + } + } else { + if (yych <= ';') { + if (yych >= ':') goto yy225; + } else { + if (yych == '?') goto yy225; + } + } + } +yy125: +#line 155 "../../lnav/src/data_scanner_re.re" + { + CAPTURE(DT_WORD); + } +#line 3304 "../../lnav/src/data_scanner_re.cc" +yy126: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= ':') { + if (yych <= ',') { + if (yych <= '%') { + if (yych <= '$') goto yy4; + goto yy80; + } else { + if (yych == '+') goto yy80; + goto yy4; + } + } else { + if (yych <= '.') { + if (yych <= '-') goto yy181; + goto yy46; + } else { + if (yych <= '/') goto yy4; + if (yych <= '9') goto yy227; + goto yy193; + } + } + } else { + if (yych <= '^') { + if (yych <= '@') { + if (yych <= '?') goto yy4; + goto yy96; + } else { + if (yych <= 'F') goto yy227; + if (yych <= 'Z') goto yy115; + goto yy4; + } + } else { + if (yych <= '`') { + if (yych <= '_') goto yy46; + goto yy4; + } else { + if (yych <= 'f') goto yy227; + if (yych <= 'z') goto yy115; + goto yy4; + } + } + } +yy127: + yych = *++YYCURSOR; + if (yych <= '@') { + if (yych <= '/') { + if (yych <= '.') goto yy1; + goto yy189; + } else { + if (yych <= '9') goto yy192; + if (yych <= ':') goto yy191; + goto yy1; + } + } else { + if (yych <= '\\') { + if (yych <= 'F') goto yy192; + if (yych <= '[') goto yy1; + goto yy98; + } else { + if (yych <= '`') goto yy1; + if (yych <= 'f') goto yy192; + goto yy1; + } + } +yy128: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= ':') { + if (yych <= ',') { + if (yych <= '%') { + if (yych <= '$') goto yy4; + goto yy80; + } else { + if (yych == '+') goto yy80; + goto yy4; + } + } else { + if (yych <= '.') { + if (yych <= '-') goto yy181; + goto yy46; + } else { + if (yych <= '/') goto yy4; + if (yych <= '9') goto yy227; + goto yy193; + } + } + } else { + if (yych <= '^') { + if (yych <= '@') { + if (yych <= '?') goto yy4; + goto yy96; + } else { + if (yych <= 'F') goto yy228; + if (yych <= 'Z') goto yy229; + goto yy4; + } + } else { + if (yych <= '`') { + if (yych <= '_') goto yy46; + goto yy4; + } else { + if (yych <= 'f') goto yy228; + if (yych <= 'z') goto yy229; + goto yy4; + } + } + } +yy129: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '9') { + if (yych <= '+') { + if (yych == '%') goto yy80; + if (yych <= '*') goto yy4; + goto yy80; + } else { + if (yych <= ',') goto yy4; + if (yych <= '.') goto yy46; + if (yych <= '/') goto yy4; + goto yy115; + } + } else { + if (yych <= 'Z') { + if (yych <= ':') goto yy148; + if (yych <= '?') goto yy4; + if (yych <= '@') goto yy96; + goto yy229; + } else { + if (yych <= '_') { + if (yych <= '^') goto yy4; + goto yy46; + } else { + if (yych <= '`') goto yy4; + if (yych <= 'z') goto yy229; + goto yy4; + } + } + } +yy130: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= ',') { + if (yych <= '!') { + if (yych <= '\f') { + if (yych <= 0x00) { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych <= 0x08) goto yy4; + if (yych <= '\n') { + yyt4 = YYCURSOR; + goto yy221; + } + goto yy4; + } else { + if (yych <= '\r') { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych <= 0x1F) goto yy4; + if (yych <= ' ') { + yyt4 = YYCURSOR; + goto yy221; + } + yyt4 = YYCURSOR; + goto yy223; + } + } else { + if (yych <= '&') { + if (yych <= '"') { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych == '%') goto yy80; + goto yy4; + } else { + if (yych <= '\'') { + yyt4 = YYCURSOR; + goto yy224; + } + if (yych <= '*') { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych <= '+') goto yy80; + yyt4 = YYCURSOR; + goto yy223; + } + } + } else { + if (yych <= '?') { + if (yych <= '9') { + if (yych <= '-') goto yy181; + if (yych <= '.') { + yyt4 = YYCURSOR; + goto yy230; + } + if (yych <= '/') goto yy4; + goto yy227; + } else { + if (yych <= ':') { + yyt4 = YYCURSOR; + goto yy231; + } + if (yych <= ';') { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych <= '>') goto yy4; + yyt4 = YYCURSOR; + goto yy223; + } + } else { + if (yych <= '^') { + if (yych <= '@') goto yy96; + if (yych <= 'F') goto yy228; + if (yych <= 'Z') goto yy229; + goto yy4; + } else { + if (yych <= '`') { + if (yych <= '_') goto yy46; + goto yy4; + } else { + if (yych <= 'f') goto yy232; + if (yych <= 'z') goto yy233; + goto yy4; + } + } + } + } +yy131: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '+') { + if (yych <= ' ') { + if (yych <= '\n') { + if (yych <= 0x00) { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych <= 0x08) goto yy4; + yyt4 = YYCURSOR; + goto yy221; + } else { + if (yych == '\r') { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych <= 0x1F) goto yy4; + yyt4 = YYCURSOR; + goto yy221; + } + } else { + if (yych <= '%') { + if (yych <= '!') { + yyt4 = YYCURSOR; + goto yy223; + } + if (yych <= '"') { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych <= '$') goto yy4; + goto yy80; + } else { + if (yych <= '&') goto yy4; + if (yych <= '\'') { + yyt4 = YYCURSOR; + goto yy224; + } + if (yych <= '*') { + yyt4 = YYCURSOR; + goto yy221; + } + goto yy80; + } + } + } else { + if (yych <= '>') { + if (yych <= '/') { + if (yych <= ',') { + yyt4 = YYCURSOR; + goto yy223; + } + if (yych <= '-') goto yy46; + if (yych <= '.') { + yyt4 = YYCURSOR; + goto yy230; + } + goto yy4; + } else { + if (yych <= '9') goto yy115; + if (yych <= ':') { + yyt3 = YYCURSOR; + goto yy234; + } + if (yych <= ';') { + yyt4 = YYCURSOR; + goto yy221; + } + goto yy4; + } + } else { + if (yych <= '^') { + if (yych <= '?') { + yyt4 = YYCURSOR; + goto yy223; + } + if (yych <= '@') goto yy96; + if (yych <= 'Z') goto yy229; + goto yy4; + } else { + if (yych <= '_') goto yy46; + if (yych <= '`') goto yy4; + if (yych <= 'z') goto yy233; + goto yy4; + } + } + } +yy132: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '?') { + if (yych <= ',') { + if (yych <= '%') { + if (yych <= '$') goto yy4; + goto yy80; + } else { + if (yych == '+') goto yy80; + goto yy4; + } + } else { + if (yych <= '/') { + if (yych <= '-') goto yy181; + if (yych <= '.') goto yy46; + goto yy4; + } else { + if (yych <= '9') goto yy227; + if (yych <= ':') goto yy193; + goto yy4; + } + } + } else { + if (yych <= 'Z') { + if (yych <= 'F') { + if (yych <= '@') goto yy96; + goto yy228; + } else { + if (yych == 'L') goto yy235; + goto yy229; + } + } else { + if (yych <= '`') { + if (yych == '_') goto yy46; + goto yy4; + } else { + if (yych <= 'f') goto yy228; + if (yych <= 'z') goto yy229; + goto yy4; + } + } + } +yy133: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '-') { + if (yych <= '!') { + if (yych <= '\f') { + if (yych <= 0x00) { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych <= 0x08) goto yy4; + if (yych <= '\n') { + yyt4 = YYCURSOR; + goto yy221; + } + goto yy4; + } else { + if (yych <= '\r') { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych <= 0x1F) goto yy4; + if (yych <= ' ') { + yyt4 = YYCURSOR; + goto yy221; + } + yyt4 = YYCURSOR; + goto yy223; + } + } else { + if (yych <= '&') { + if (yych <= '"') { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych == '%') goto yy80; + goto yy4; + } else { + if (yych <= '*') { + if (yych <= '\'') { + yyt4 = YYCURSOR; + goto yy224; + } + yyt4 = YYCURSOR; + goto yy221; + } else { + if (yych <= '+') goto yy80; + if (yych <= ',') { + yyt4 = YYCURSOR; + goto yy223; + } + goto yy181; + } + } + } + } else { + if (yych <= 'F') { + if (yych <= ':') { + if (yych <= '.') { + yyt4 = YYCURSOR; + goto yy230; + } + if (yych <= '/') goto yy4; + if (yych <= '9') goto yy227; + yyt4 = YYCURSOR; + goto yy231; + } else { + if (yych <= '>') { + if (yych <= ';') { + yyt4 = YYCURSOR; + goto yy221; + } + goto yy4; + } else { + if (yych <= '?') { + yyt4 = YYCURSOR; + goto yy223; + } + if (yych <= '@') goto yy96; + goto yy228; + } + } + } else { + if (yych <= '`') { + if (yych <= 'Z') goto yy229; + if (yych == '_') goto yy46; + goto yy4; + } else { + if (yych <= 'k') { + if (yych <= 'f') goto yy232; + goto yy233; + } else { + if (yych <= 'l') goto yy236; + if (yych <= 'z') goto yy233; + goto yy4; + } + } + } + } +yy134: + yych = *++YYCURSOR; + if (yych <= '9') { + if (yych == '/') goto yy189; + goto yy1; + } else { + if (yych <= ':') goto yy152; + if (yych == '\\') goto yy98; + goto yy1; + } +yy135: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= ':') { + if (yych <= '+') { + if (yych == '%') goto yy80; + if (yych <= '*') goto yy4; + goto yy80; + } else { + if (yych <= '.') { + if (yych <= ',') goto yy4; + goto yy46; + } else { + if (yych <= '/') goto yy4; + if (yych <= '9') goto yy115; + goto yy148; + } + } + } else { + if (yych <= 'Z') { + if (yych <= '@') { + if (yych <= '?') goto yy4; + goto yy96; + } else { + if (yych == 'L') goto yy237; + goto yy229; + } + } else { + if (yych <= '_') { + if (yych <= '^') goto yy4; + goto yy46; + } else { + if (yych <= '`') goto yy4; + if (yych <= 'z') goto yy229; + goto yy4; + } + } + } +yy136: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= ',') { + if (yych <= '!') { + if (yych <= '\f') { + if (yych <= 0x00) { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych <= 0x08) goto yy4; + if (yych <= '\n') { + yyt4 = YYCURSOR; + goto yy221; + } + goto yy4; + } else { + if (yych <= '\r') { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych <= 0x1F) goto yy4; + if (yych <= ' ') { + yyt4 = YYCURSOR; + goto yy221; + } + yyt4 = YYCURSOR; + goto yy223; + } + } else { + if (yych <= '&') { + if (yych <= '"') { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych == '%') goto yy80; + goto yy4; + } else { + if (yych <= '\'') { + yyt4 = YYCURSOR; + goto yy224; + } + if (yych <= '*') { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych <= '+') goto yy80; + yyt4 = YYCURSOR; + goto yy223; + } + } + } else { + if (yych <= '?') { + if (yych <= '9') { + if (yych <= '-') goto yy46; + if (yych <= '.') { + yyt4 = YYCURSOR; + goto yy230; + } + if (yych <= '/') goto yy4; + goto yy115; + } else { + if (yych <= ':') { + yyt3 = YYCURSOR; + goto yy234; + } + if (yych <= ';') { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych <= '>') goto yy4; + yyt4 = YYCURSOR; + goto yy223; + } + } else { + if (yych <= '_') { + if (yych <= '@') goto yy96; + if (yych <= 'Z') goto yy229; + if (yych <= '^') goto yy4; + goto yy46; + } else { + if (yych <= 'm') { + if (yych <= '`') goto yy4; + goto yy233; + } else { + if (yych <= 'n') goto yy238; + if (yych <= 'z') goto yy233; + goto yy4; + } + } + } + } +yy137: + yych = *++YYCURSOR; + if (yybm[768+yych] & 128) { + goto yy137; + } + if (yych <= '&') { + if (yych <= '\r') { + if (yych <= 0x08) { + if (yych <= 0x00) { + yyt4 = YYCURSOR; + goto yy221; + } + goto yy1; + } else { + if (yych <= '\n') { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych <= '\f') goto yy1; + yyt4 = YYCURSOR; + goto yy221; + } + } else { + if (yych <= ' ') { + if (yych <= 0x1F) goto yy1; + yyt4 = YYCURSOR; + goto yy221; + } else { + if (yych <= '!') { + yyt4 = YYCURSOR; + goto yy223; + } + if (yych <= '"') { + yyt4 = YYCURSOR; + goto yy221; + } + goto yy1; + } + } + } else { + if (yych <= '-') { + if (yych <= '*') { + if (yych <= '\'') { + yyt4 = YYCURSOR; + goto yy224; + } + yyt4 = YYCURSOR; + goto yy221; + } else { + if (yych == ',') { + yyt4 = YYCURSOR; + goto yy223; + } + goto yy1; + } + } else { + if (yych <= ';') { + if (yych <= '.') { + yyt4 = YYCURSOR; + goto yy226; + } + if (yych <= '9') goto yy1; + yyt4 = YYCURSOR; + goto yy221; + } else { + if (yych == '?') { + yyt4 = YYCURSOR; + goto yy223; + } + goto yy1; + } + } + } +yy138: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= ':') { + if (yych <= '+') { + if (yych == '%') goto yy80; + if (yych <= '*') goto yy4; + goto yy80; + } else { + if (yych <= '.') { + if (yych <= ',') goto yy4; + goto yy46; + } else { + if (yych <= '/') goto yy4; + if (yych <= '9') goto yy115; + goto yy148; + } + } + } else { + if (yych <= 'Z') { + if (yych <= '@') { + if (yych <= '?') goto yy4; + goto yy96; + } else { + if (yych == 'U') goto yy239; + goto yy229; + } + } else { + if (yych <= '_') { + if (yych <= '^') goto yy4; + goto yy46; + } else { + if (yych <= '`') goto yy4; + if (yych <= 'z') goto yy229; + goto yy4; + } + } + } +yy139: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= ',') { + if (yych <= '!') { + if (yych <= '\f') { + if (yych <= 0x00) { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych <= 0x08) goto yy4; + if (yych <= '\n') { + yyt4 = YYCURSOR; + goto yy221; + } + goto yy4; + } else { + if (yych <= '\r') { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych <= 0x1F) goto yy4; + if (yych <= ' ') { + yyt4 = YYCURSOR; + goto yy221; + } + yyt4 = YYCURSOR; + goto yy223; + } + } else { + if (yych <= '&') { + if (yych <= '"') { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych == '%') goto yy80; + goto yy4; + } else { + if (yych <= '\'') { + yyt4 = YYCURSOR; + goto yy224; + } + if (yych <= '*') { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych <= '+') goto yy80; + yyt4 = YYCURSOR; + goto yy223; + } + } + } else { + if (yych <= '?') { + if (yych <= '9') { + if (yych <= '-') goto yy46; + if (yych <= '.') { + yyt4 = YYCURSOR; + goto yy230; + } + if (yych <= '/') goto yy4; + goto yy115; + } else { + if (yych <= ':') { + yyt3 = YYCURSOR; + goto yy234; + } + if (yych <= ';') { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych <= '>') goto yy4; + yyt4 = YYCURSOR; + goto yy223; + } + } else { + if (yych <= '_') { + if (yych <= '@') goto yy96; + if (yych <= 'Z') goto yy229; + if (yych <= '^') goto yy4; + goto yy46; + } else { + if (yych <= 't') { + if (yych <= '`') goto yy4; + goto yy233; + } else { + if (yych <= 'u') goto yy238; + if (yych <= 'z') goto yy233; + goto yy4; + } + } + } + } +yy140: + ++YYCURSOR; +#line 273 "../../lnav/src/data_scanner_re.re" + { RET(DT_ESCAPED_CHAR); } +#line 4114 "../../lnav/src/data_scanner_re.cc" +yy141: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy140; + goto yy1; +yy142: + yych = *++YYCURSOR; + if (yych <= 0x9F) goto yy1; + if (yych <= 0xBF) goto yy141; + goto yy1; +yy143: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy141; + goto yy1; +yy144: + yych = *++YYCURSOR; + if (yych <= 0x8F) goto yy1; + if (yych <= 0xBF) goto yy143; + goto yy1; +yy145: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy143; + goto yy1; +yy146: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0x8F) goto yy143; + goto yy1; +yy147: + yych = *++YYCURSOR; + if (yych <= ':') { + if (yych <= '.') goto yy1; + if (yych <= '/') goto yy189; + if (yych <= '9') goto yy192; + goto yy191; + } else { + if (yych <= 'F') { + if (yych <= '@') goto yy1; + goto yy192; + } else { + if (yych <= '`') goto yy1; + if (yych <= 'f') goto yy192; + goto yy1; + } + } +yy148: + yych = *++YYCURSOR; + if (yych == '/') goto yy189; + if (yych == ':') goto yy152; + goto yy1; +yy149: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= ',') { + if (yych <= '!') { + if (yych <= '\f') { + if (yych <= 0x00) { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych <= 0x08) goto yy4; + if (yych <= '\n') { + yyt4 = YYCURSOR; + goto yy221; + } + goto yy4; + } else { + if (yych <= '\r') { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych <= 0x1F) goto yy4; + if (yych <= ' ') { + yyt4 = YYCURSOR; + goto yy221; + } + yyt4 = YYCURSOR; + goto yy223; + } + } else { + if (yych <= '&') { + if (yych <= '"') { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych == '%') goto yy80; + goto yy4; + } else { + if (yych <= '\'') { + yyt4 = YYCURSOR; + goto yy224; + } + if (yych <= '*') { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych <= '+') goto yy80; + yyt4 = YYCURSOR; + goto yy223; + } + } + } else { + if (yych <= '?') { + if (yych <= '9') { + if (yych <= '-') goto yy46; + if (yych <= '.') { + yyt4 = YYCURSOR; + goto yy230; + } + if (yych <= '/') goto yy4; + goto yy115; + } else { + if (yych <= ':') { + yyt3 = YYCURSOR; + goto yy234; + } + if (yych <= ';') { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych <= '>') goto yy4; + yyt4 = YYCURSOR; + goto yy223; + } + } else { + if (yych <= '_') { + if (yych <= '@') goto yy96; + if (yych <= 'Z') goto yy229; + if (yych <= '^') goto yy4; + goto yy46; + } else { + if (yych <= 'k') { + if (yych <= '`') goto yy4; + goto yy233; + } else { + if (yych <= 'l') goto yy240; + if (yych <= 'z') goto yy233; + goto yy4; + } + } + } + } +yy150: + yych = *++YYCURSOR; + if (yych <= '-') { + if (yych <= 0x1F) { + if (yych <= '\f') { + if (yych <= 0x00) { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych <= 0x08) goto yy83; + if (yych <= '\n') { + yyt3 = YYCURSOR; + goto yy241; + } + goto yy83; + } else { + if (yych <= '\r') { + yyt3 = YYCURSOR; + goto yy241; + } + if (yych == 0x1B) goto yy1; + goto yy83; + } + } else { + if (yych <= '&') { + if (yych == '!') { + yyt3 = YYCURSOR; + goto yy242; + } + if (yych <= '"') { + yyt3 = YYCURSOR; + goto yy241; + } + goto yy83; + } else { + if (yych <= '*') { + if (yych <= '\'') { + yyt3 = YYCURSOR; + goto yy243; + } + yyt3 = YYCURSOR; + goto yy241; + } else { + if (yych == ',') { + yyt3 = YYCURSOR; + goto yy242; + } + goto yy83; + } + } + } + } else { + if (yych <= 'z') { + if (yych <= '>') { + if (yych <= '.') { + yyt3 = YYCURSOR; + goto yy244; + } + if (yych <= '9') goto yy83; + if (yych <= ';') { + yyt3 = YYCURSOR; + goto yy241; + } + goto yy83; + } else { + if (yych <= '[') { + if (yych <= '?') { + yyt3 = YYCURSOR; + goto yy242; + } + goto yy83; + } else { + if (yych <= '\\') goto yy86; + if (yych <= '`') goto yy83; + goto yy150; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) goto yy83; + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) goto yy87; + goto yy88; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) goto yy89; + goto yy90; + } else { + if (yych <= 0xF3) goto yy91; + if (yych <= 0xF4) goto yy92; + goto yy1; + } + } + } + } +yy151: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '+') { + if (yych <= ' ') { + if (yych <= '\n') { + if (yych <= 0x00) { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych <= 0x08) goto yy4; + yyt4 = YYCURSOR; + goto yy221; + } else { + if (yych == '\r') { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych <= 0x1F) goto yy4; + yyt4 = YYCURSOR; + goto yy221; + } + } else { + if (yych <= '%') { + if (yych <= '!') { + yyt4 = YYCURSOR; + goto yy223; + } + if (yych <= '"') { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych <= '$') goto yy4; + goto yy80; + } else { + if (yych <= '&') goto yy4; + if (yych <= '\'') { + yyt4 = YYCURSOR; + goto yy224; + } + if (yych <= '*') { + yyt4 = YYCURSOR; + goto yy221; + } + goto yy80; + } + } + } else { + if (yych <= '>') { + if (yych <= '/') { + if (yych <= ',') { + yyt4 = YYCURSOR; + goto yy223; + } + if (yych <= '-') goto yy245; + if (yych <= '.') { + yyt4 = YYCURSOR; + goto yy230; + } + goto yy4; + } else { + if (yych <= '9') goto yy115; + if (yych <= ':') { + yyt3 = YYCURSOR; + goto yy234; + } + if (yych <= ';') { + yyt4 = YYCURSOR; + goto yy221; + } + goto yy4; + } + } else { + if (yych <= '^') { + if (yych <= '?') { + yyt4 = YYCURSOR; + goto yy223; + } + if (yych <= '@') goto yy96; + if (yych <= 'Z') goto yy229; + goto yy4; + } else { + if (yych <= '_') goto yy46; + if (yych <= '`') goto yy4; + if (yych <= 'z') goto yy233; + goto yy4; + } + } + } +yy152: + yych = *++YYCURSOR; + if (yybm[1024+yych] & 8) { + goto yy3; + } + if (yych <= 0xEF) { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) goto yy56; + if (yych <= 0xE0) goto yy57; + goto yy58; + } else { + if (yych <= 0xF0) goto yy59; + if (yych <= 0xF3) goto yy60; + if (yych <= 0xF4) goto yy61; + goto yy1; + } +yy153: + yych = *++YYCURSOR; + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy246; + goto yy1; +yy154: + yych = *++YYCURSOR; + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy247; + goto yy1; +yy155: + yych = *++YYCURSOR; +yy156: + if (yybm[512+yych] & 1) { + goto yy155; + } + if (yych == '\n') goto yy248; + goto yy1; +yy157: + ++YYCURSOR; +#line 212 "../../lnav/src/data_scanner_re.re" + { + RET(DT_CSI); + } +#line 4483 "../../lnav/src/data_scanner_re.cc" +yy158: + yych = *++YYCURSOR; +yy159: + if (yybm[512+yych] & 2) { + goto yy158; + } + if (yych <= ',') goto yy1; + if (yych <= '.') goto yy249; + goto yy1; +yy160: + ++YYCURSOR; + yyt1 = yyt2; +yy161: + YYCURSOR = yyt1; +#line 158 "../../lnav/src/data_scanner_re.re" + { + CAPTURE(DT_QUOTED_STRING); + switch (this->ds_input[cap_inner.c_begin]) { + case 'u': + case 'r': + cap_inner.c_begin += 1; + break; + } + cap_inner.c_begin += 1; + cap_inner.c_end -= 1; + return tokenize_result{token_out, cap_all, cap_inner, this->ds_input.data()}; + } +#line 4511 "../../lnav/src/data_scanner_re.cc" +yy162: + yyaccept = 14; + yych = *(YYMARKER = ++YYCURSOR); + yyt1 = yyt2; + if (yybm[768+yych] & 2) { + goto yy83; + } + if (yych <= 0xDF) { + if (yych <= '\'') { + if (yych <= 0x1B) goto yy161; + goto yy85; + } else { + if (yych <= '\\') goto yy86; + if (yych <= 0xC1) goto yy161; + goto yy87; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xE0) goto yy88; + if (yych <= 0xEF) goto yy89; + goto yy90; + } else { + if (yych <= 0xF3) goto yy91; + if (yych <= 0xF4) goto yy92; + goto yy161; + } + } +yy163: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy160; + goto yy1; +yy164: + yych = *++YYCURSOR; + if (yych <= 0x9F) goto yy1; + if (yych <= 0xBF) goto yy163; + goto yy1; +yy165: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy163; + goto yy1; +yy166: + yych = *++YYCURSOR; + if (yych <= 0x8F) goto yy1; + if (yych <= 0xBF) goto yy165; + goto yy1; +yy167: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy165; + goto yy1; +yy168: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0x8F) goto yy165; + goto yy1; +yy169: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '9') { + if (yych <= '+') { + if (yych == '%') goto yy80; + if (yych <= '*') goto yy4; + goto yy80; + } else { + if (yych <= ',') goto yy4; + if (yych <= '.') goto yy46; + if (yych <= '/') goto yy4; + goto yy250; + } + } else { + if (yych <= '^') { + if (yych <= '?') goto yy4; + if (yych <= '@') goto yy96; + if (yych <= 'Z') goto yy46; + goto yy4; + } else { + if (yych == '`') goto yy4; + if (yych <= 'z') goto yy46; + goto yy4; + } + } +yy170: + yyaccept = 11; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[768+yych] & 32) { + goto yy103; + } + if (yych <= '?') { + if (yych <= '$') { + if (yych <= '\r') { + if (yych <= 0x08) { + if (yych <= 0x00) goto yy109; + goto yy3; + } else { + if (yych <= '\n') goto yy109; + if (yych <= '\f') goto yy3; + goto yy109; + } + } else { + if (yych <= 0x1B) { + if (yych <= 0x1A) goto yy3; + goto yy109; + } else { + if (yych <= 0x1F) goto yy3; + if (yych <= '#') goto yy109; + goto yy3; + } + } + } else { + if (yych <= '-') { + if (yych <= '*') { + if (yych <= '%') goto yy105; + goto yy109; + } else { + if (yych <= '+') goto yy80; + if (yych <= ',') goto yy109; + goto yy46; + } + } else { + if (yych <= '7') { + if (yych <= '.') goto yy169; + if (yych <= '/') goto yy3; + goto yy170; + } else { + if (yych <= '9') goto yy95; + if (yych <= ':') goto yy62; + goto yy109; + } + } + } + } else { + if (yych <= 'f') { + if (yych <= 'Z') { + if (yych <= 'D') { + if (yych <= '@') goto yy96; + } else { + if (yych <= 'E') goto yy172; + if (yych >= 'G') goto yy46; + } + } else { + if (yych <= '`') { + if (yych == '_') goto yy46; + goto yy109; + } else { + if (yych == 'e') goto yy172; + } + } + } else { + if (yych <= 0xDF) { + if (yych <= '~') { + if (yych <= 'z') goto yy46; + goto yy109; + } else { + if (yych <= 0x7F) goto yy3; + if (yych <= 0xC1) goto yy109; + goto yy56; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xE0) goto yy57; + if (yych <= 0xEF) goto yy58; + goto yy59; + } else { + if (yych <= 0xF3) goto yy60; + if (yych <= 0xF4) goto yy61; + goto yy109; + } + } + } + } +yy171: + yyaccept = 12; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[512+yych] & 8) { + goto yy171; + } + if (yych <= ':') { + if (yych <= 0x1F) { + if (yych <= '\f') { + if (yych <= 0x00) goto yy113; + if (yych <= 0x08) goto yy3; + if (yych <= '\n') goto yy113; + goto yy3; + } else { + if (yych <= '\r') goto yy113; + if (yych == 0x1B) goto yy113; + goto yy3; + } + } else { + if (yych <= '*') { + if (yych <= '#') goto yy113; + if (yych <= '$') goto yy3; + if (yych <= '%') goto yy80; + goto yy113; + } else { + if (yych <= ',') { + if (yych <= '+') goto yy80; + goto yy113; + } else { + if (yych <= '.') goto yy46; + if (yych <= '/') goto yy3; + goto yy62; + } + } + } + } else { + if (yych <= '~') { + if (yych <= '^') { + if (yych <= '?') goto yy113; + if (yych <= '@') goto yy96; + if (yych <= 'Z') goto yy46; + goto yy113; + } else { + if (yych == '`') goto yy113; + if (yych <= 'z') goto yy46; + goto yy113; + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) goto yy3; + if (yych <= 0xC1) goto yy113; + if (yych <= 0xDF) goto yy56; + goto yy57; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) goto yy58; + goto yy59; + } else { + if (yych <= 0xF3) goto yy60; + if (yych <= 0xF4) goto yy61; + goto yy113; + } + } + } + } +yy172: + yyaccept = 12; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[512+yych] & 8) { + goto yy171; + } + if (yych <= ',') { + if (yych <= 0x1B) { + if (yych <= '\n') { + if (yych <= 0x00) goto yy113; + if (yych <= 0x08) goto yy4; + goto yy113; + } else { + if (yych == '\r') goto yy113; + if (yych <= 0x1A) goto yy4; + goto yy113; + } + } else { + if (yych <= '$') { + if (yych <= 0x1F) goto yy4; + if (yych <= '#') goto yy113; + goto yy4; + } else { + if (yych <= '%') goto yy80; + if (yych == '+') goto yy194; + goto yy113; + } + } + } else { + if (yych <= '^') { + if (yych <= ':') { + if (yych <= '-') goto yy251; + if (yych <= '.') goto yy46; + goto yy4; + } else { + if (yych <= '?') goto yy113; + if (yych <= '@') goto yy96; + if (yych <= 'Z') goto yy46; + goto yy113; + } + } else { + if (yych <= '~') { + if (yych == '`') goto yy113; + if (yych <= 'z') goto yy46; + goto yy113; + } else { + if (yych <= 0x7F) goto yy4; + if (yych <= 0xC1) goto yy113; + if (yych <= 0xF4) goto yy4; + goto yy113; + } + } + } +yy173: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[512+yych] & 8) { + goto yy171; + } + if (yych <= '?') { + if (yych <= '*') { + if (yych == '%') goto yy80; + goto yy4; + } else { + if (yych <= '+') goto yy80; + if (yych <= ',') goto yy4; + if (yych <= '.') goto yy46; + goto yy4; + } + } else { + if (yych <= '^') { + if (yych <= '@') goto yy96; + if (yych <= 'Z') goto yy46; + goto yy4; + } else { + if (yych == '`') goto yy4; + if (yych <= 'z') goto yy46; + goto yy4; + } + } +yy174: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= ':') { + if (yych <= 0x1F) { + if (yych <= '\f') { + if (yych <= 0x00) goto yy5; + if (yych <= 0x08) goto yy3; + if (yych <= '\n') goto yy5; + goto yy3; + } else { + if (yych <= '\r') goto yy5; + if (yych == 0x1B) goto yy5; + goto yy3; + } + } else { + if (yych <= '-') { + if (yych == '$') goto yy3; + if (yych <= ',') goto yy5; + goto yy174; + } else { + if (yych <= '.') goto yy252; + if (yych <= '/') goto yy3; + if (yych <= '9') goto yy174; + goto yy62; + } + } + } else { + if (yych <= '~') { + if (yych <= '^') { + if (yych <= '?') goto yy5; + if (yych <= '@') goto yy3; + if (yych <= 'Z') goto yy174; + goto yy5; + } else { + if (yych <= '_') goto yy3; + if (yych <= '`') goto yy5; + if (yych <= 'z') goto yy174; + goto yy5; + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) goto yy3; + if (yych <= 0xC1) goto yy5; + if (yych <= 0xDF) goto yy56; + goto yy57; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) goto yy58; + goto yy59; + } else { + if (yych <= 0xF3) goto yy60; + if (yych <= 0xF4) goto yy61; + goto yy5; + } + } + } + } +yy175: + yych = *++YYCURSOR; + if (yych == 'r') goto yy253; + goto yy100; +yy176: + yyaccept = 5; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '9') { + if (yych <= ' ') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy24; + if (yych <= 0x08) goto yy4; + if (yych <= '\n') goto yy24; + goto yy4; + } else { + if (yych <= 0x1A) { + if (yych <= '\r') goto yy24; + goto yy4; + } else { + if (yych <= 0x1B) goto yy24; + if (yych <= 0x1F) goto yy4; + goto yy24; + } + } + } else { + if (yych <= '$') { + if (yych == '"') goto yy24; + if (yych <= '#') goto yy99; + goto yy101; + } else { + if (yych <= '\'') { + if (yych <= '&') goto yy99; + goto yy24; + } else { + if (yych <= '*') goto yy99; + if (yych <= ',') goto yy24; + goto yy101; + } + } + } + } else { + if (yych <= '`') { + if (yych <= '[') { + if (yych <= ':') goto yy4; + if (yych <= '?') goto yy24; + if (yych <= 'Z') goto yy101; + goto yy24; + } else { + if (yych <= ']') { + if (yych <= '\\') goto yy99; + goto yy24; + } else { + if (yych <= '^') goto yy99; + if (yych <= '_') goto yy101; + goto yy24; + } + } + } else { + if (yych <= '}') { + if (yych == 'o') goto yy254; + if (yych <= 'z') goto yy101; + goto yy24; + } else { + if (yych <= 0x7F) { + if (yych <= '~') goto yy99; + goto yy4; + } else { + if (yych <= 0xC1) goto yy24; + if (yych <= 0xF4) goto yy4; + goto yy24; + } + } + } + } +yy177: + ++YYCURSOR; + goto yy106; +yy178: + yyaccept = 6; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[768+yych] & 32) { + goto yy103; + } + if (yych <= '/') { + if (yych <= 0x1F) { + if (yych <= '\f') { + if (yych <= 0x00) goto yy26; + if (yych <= 0x08) goto yy4; + if (yych <= '\n') goto yy26; + goto yy4; + } else { + if (yych <= '\r') goto yy26; + if (yych == 0x1B) goto yy26; + goto yy4; + } + } else { + if (yych <= '*') { + if (yych <= '#') goto yy26; + if (yych <= '$') goto yy4; + if (yych <= '%') goto yy105; + goto yy26; + } else { + if (yych <= ',') { + if (yych <= '+') goto yy80; + goto yy26; + } else { + if (yych <= '-') goto yy255; + if (yych <= '.') goto yy256; + goto yy4; + } + } + } + } else { + if (yych <= '_') { + if (yych <= '@') { + if (yych <= '9') goto yy257; + if (yych <= ':') goto yy4; + if (yych <= '?') goto yy26; + goto yy96; + } else { + if (yych <= 'E') { + if (yych <= 'D') goto yy258; + goto yy259; + } else { + if (yych <= 'Z') goto yy258; + if (yych <= '^') goto yy26; + goto yy258; + } + } + } else { + if (yych <= 'z') { + if (yych <= '`') goto yy26; + if (yych == 'e') goto yy259; + goto yy258; + } else { + if (yych <= 0x7F) { + if (yych <= '~') goto yy26; + goto yy4; + } else { + if (yych <= 0xC1) goto yy26; + if (yych <= 0xF4) goto yy4; + goto yy26; + } + } + } + } +yy179: + yyaccept = 6; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[768+yych] & 32) { + goto yy103; + } + if (yych <= '/') { + if (yych <= 0x1F) { + if (yych <= '\f') { + if (yych <= 0x00) goto yy26; + if (yych <= 0x08) goto yy4; + if (yych <= '\n') goto yy26; + goto yy4; + } else { + if (yych <= '\r') goto yy26; + if (yych == 0x1B) goto yy26; + goto yy4; + } + } else { + if (yych <= '*') { + if (yych <= '#') goto yy26; + if (yych <= '$') goto yy4; + if (yych <= '%') goto yy105; + goto yy26; + } else { + if (yych <= ',') { + if (yych <= '+') goto yy80; + goto yy26; + } else { + if (yych <= '-') goto yy255; + if (yych <= '.') goto yy256; + goto yy4; + } + } + } + } else { + if (yych <= '_') { + if (yych <= '@') { + if (yych <= '9') goto yy178; + if (yych <= ':') goto yy4; + if (yych <= '?') goto yy26; + goto yy96; + } else { + if (yych <= 'E') { + if (yych <= 'D') goto yy258; + goto yy259; + } else { + if (yych <= 'Z') goto yy258; + if (yych <= '^') goto yy26; + goto yy258; + } + } + } else { + if (yych <= 'z') { + if (yych <= '`') goto yy26; + if (yych == 'e') goto yy259; + goto yy258; + } else { + if (yych <= 0x7F) { + if (yych <= '~') goto yy26; + goto yy4; + } else { + if (yych <= 0xC1) goto yy26; + if (yych <= 0xF4) goto yy4; + goto yy26; + } + } + } + } +yy180: + yyaccept = 6; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[768+yych] & 32) { + goto yy103; + } + if (yych <= '4') { + if (yych <= '#') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy26; + if (yych <= 0x08) goto yy4; + if (yych <= '\n') goto yy26; + goto yy4; + } else { + if (yych <= 0x1A) { + if (yych <= '\r') goto yy26; + goto yy4; + } else { + if (yych <= 0x1B) goto yy26; + if (yych <= 0x1F) goto yy4; + goto yy26; + } + } + } else { + if (yych <= '+') { + if (yych <= '$') goto yy4; + if (yych <= '%') goto yy105; + if (yych <= '*') goto yy26; + goto yy80; + } else { + if (yych <= '-') { + if (yych <= ',') goto yy26; + goto yy255; + } else { + if (yych <= '.') goto yy256; + if (yych <= '/') goto yy4; + goto yy178; + } + } + } + } else { + if (yych <= '^') { + if (yych <= '?') { + if (yych <= '5') goto yy260; + if (yych <= '9') goto yy257; + if (yych <= ':') goto yy4; + goto yy26; + } else { + if (yych <= 'D') { + if (yych <= '@') goto yy96; + goto yy258; + } else { + if (yych <= 'E') goto yy259; + if (yych <= 'Z') goto yy258; + goto yy26; + } + } + } else { + if (yych <= 'z') { + if (yych <= '`') { + if (yych <= '_') goto yy258; + goto yy26; + } else { + if (yych == 'e') goto yy259; + goto yy258; + } + } else { + if (yych <= 0x7F) { + if (yych <= '~') goto yy26; + goto yy4; + } else { + if (yych <= 0xC1) goto yy26; + if (yych <= 0xF4) goto yy4; + goto yy26; + } + } + } + } +yy181: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= ':') { + if (yych <= '#') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy5; + if (yych <= 0x08) goto yy3; + if (yych <= '\n') goto yy5; + goto yy3; + } else { + if (yych <= 0x1A) { + if (yych <= '\r') goto yy5; + goto yy3; + } else { + if (yych <= 0x1B) goto yy5; + if (yych <= 0x1F) goto yy3; + goto yy5; + } + } + } else { + if (yych <= '+') { + if (yych <= '$') goto yy3; + if (yych <= '%') goto yy80; + if (yych <= '*') goto yy5; + goto yy80; + } else { + if (yych <= '.') { + if (yych <= ',') goto yy5; + goto yy46; + } else { + if (yych <= '/') goto yy3; + if (yych <= '9') goto yy261; + goto yy62; + } + } + } + } else { + if (yych <= 'z') { + if (yych <= 'Z') { + if (yych <= '?') goto yy5; + if (yych <= '@') goto yy96; + if (yych <= 'F') goto yy261; + goto yy46; + } else { + if (yych <= '_') { + if (yych <= '^') goto yy5; + goto yy46; + } else { + if (yych <= '`') goto yy5; + if (yych <= 'f') goto yy261; + goto yy46; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + if (yych <= '~') goto yy5; + goto yy3; + } else { + if (yych <= 0xC1) goto yy5; + if (yych <= 0xDF) goto yy56; + goto yy57; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) goto yy58; + goto yy59; + } else { + if (yych <= 0xF3) goto yy60; + if (yych <= 0xF4) goto yy61; + goto yy5; + } + } + } + } +yy182: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '1') { + if (yych <= '+') { + if (yych == '%') goto yy80; + if (yych <= '*') goto yy4; + goto yy80; + } else { + if (yych <= '.') { + if (yych <= ',') goto yy4; + goto yy46; + } else { + if (yych <= '/') goto yy4; + if (yych <= '0') goto yy262; + goto yy263; + } + } + } else { + if (yych <= 'Z') { + if (yych <= '9') { + if (yych <= '2') goto yy264; + goto yy262; + } else { + if (yych <= '?') goto yy4; + if (yych <= '@') goto yy96; + goto yy46; + } + } else { + if (yych <= '_') { + if (yych <= '^') goto yy4; + goto yy46; + } else { + if (yych <= '`') goto yy4; + if (yych <= 'z') goto yy46; + goto yy4; + } + } + } +yy183: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '@') goto yy4; + if (yych <= 'Z') goto yy265; + if (yych <= '`') goto yy4; + if (yych <= 'z') goto yy265; + goto yy4; +yy184: + yyaccept = 11; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[768+yych] & 32) { + goto yy103; + } + if (yych <= '9') { + if (yych <= '#') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy109; + if (yych <= 0x08) goto yy4; + if (yych <= '\n') goto yy109; + goto yy4; + } else { + if (yych <= 0x1A) { + if (yych <= '\r') goto yy109; + goto yy4; + } else { + if (yych <= 0x1B) goto yy109; + if (yych <= 0x1F) goto yy4; + goto yy109; + } + } + } else { + if (yych <= ',') { + if (yych <= '%') { + if (yych <= '$') goto yy4; + goto yy105; + } else { + if (yych == '+') goto yy80; + goto yy109; + } + } else { + if (yych <= '.') { + if (yych <= '-') goto yy46; + goto yy266; + } else { + if (yych <= '/') goto yy4; + if (yych <= '7') goto yy267; + goto yy268; + } + } + } + } else { + if (yych <= '_') { + if (yych <= 'D') { + if (yych <= ':') goto yy147; + if (yych <= '?') goto yy109; + if (yych <= '@') goto yy96; + goto yy269; + } else { + if (yych <= 'F') { + if (yych <= 'E') goto yy270; + goto yy269; + } else { + if (yych <= 'Z') goto yy115; + if (yych <= '^') goto yy109; + goto yy46; + } + } + } else { + if (yych <= 'z') { + if (yych <= 'd') { + if (yych <= '`') goto yy109; + goto yy269; + } else { + if (yych <= 'e') goto yy270; + if (yych <= 'f') goto yy269; + goto yy115; + } + } else { + if (yych <= 0x7F) { + if (yych <= '~') goto yy109; + goto yy4; + } else { + if (yych <= 0xC1) goto yy109; + if (yych <= 0xF4) goto yy4; + goto yy109; + } + } + } + } +yy185: + yyaccept = 6; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[768+yych] & 32) { + goto yy103; + } + if (yych <= '9') { + if (yych <= '#') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy26; + if (yych <= 0x08) goto yy4; + if (yych <= '\n') goto yy26; + goto yy4; + } else { + if (yych <= 0x1A) { + if (yych <= '\r') goto yy26; + goto yy4; + } else { + if (yych <= 0x1B) goto yy26; + if (yych <= 0x1F) goto yy4; + goto yy26; + } + } + } else { + if (yych <= '+') { + if (yych <= '$') goto yy4; + if (yych <= '%') goto yy105; + if (yych <= '*') goto yy26; + goto yy80; + } else { + if (yych <= '-') { + if (yych <= ',') goto yy26; + goto yy46; + } else { + if (yych <= '.') goto yy266; + if (yych <= '/') goto yy4; + goto yy268; + } + } + } + } else { + if (yych <= '_') { + if (yych <= 'D') { + if (yych <= ':') goto yy147; + if (yych <= '?') goto yy26; + if (yych <= '@') goto yy96; + goto yy269; + } else { + if (yych <= 'F') { + if (yych <= 'E') goto yy270; + goto yy269; + } else { + if (yych <= 'Z') goto yy115; + if (yych <= '^') goto yy26; + goto yy46; + } + } + } else { + if (yych <= 'z') { + if (yych <= 'd') { + if (yych <= '`') goto yy26; + goto yy269; + } else { + if (yych <= 'e') goto yy270; + if (yych <= 'f') goto yy269; + goto yy115; + } + } else { + if (yych <= 0x7F) { + if (yych <= '~') goto yy26; + goto yy4; + } else { + if (yych <= 0xC1) goto yy26; + if (yych <= 0xF4) goto yy4; + goto yy26; + } + } + } + } +yy186: + yych = *++YYCURSOR; + if (yych <= ':') { + if (yych <= '.') goto yy1; + if (yych <= '/') goto yy189; + if (yych <= '9') goto yy271; + goto yy191; + } else { + if (yych <= 'F') { + if (yych <= '@') goto yy1; + goto yy272; + } else { + if (yych <= '`') goto yy1; + if (yych <= 'f') goto yy272; + goto yy1; + } + } +yy187: + yyaccept = 12; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '/') { + if (yych <= 0x1F) { + if (yych <= '\f') { + if (yych <= 0x00) goto yy113; + if (yych <= 0x08) goto yy4; + if (yych <= '\n') goto yy113; + goto yy4; + } else { + if (yych <= '\r') goto yy113; + if (yych == 0x1B) goto yy113; + goto yy4; + } + } else { + if (yych <= '*') { + if (yych <= '#') goto yy113; + if (yych <= '$') goto yy4; + if (yych <= '%') goto yy80; + goto yy113; + } else { + if (yych <= '+') goto yy80; + if (yych <= ',') goto yy113; + if (yych <= '.') goto yy46; + goto yy4; + } + } + } else { + if (yych <= '_') { + if (yych <= '@') { + if (yych <= '9') goto yy269; + if (yych <= ':') goto yy147; + if (yych <= '?') goto yy113; + goto yy96; + } else { + if (yych <= 'F') goto yy269; + if (yych <= 'Z') goto yy115; + if (yych <= '^') goto yy113; + goto yy46; + } + } else { + if (yych <= '~') { + if (yych <= '`') goto yy113; + if (yych <= 'f') goto yy269; + if (yych <= 'z') goto yy115; + goto yy113; + } else { + if (yych <= 0x7F) goto yy4; + if (yych <= 0xC1) goto yy113; + if (yych <= 0xF4) goto yy4; + goto yy113; + } + } + } +yy188: + yyaccept = 12; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '.') { + if (yych <= 0x1F) { + if (yych <= '\f') { + if (yych <= 0x00) goto yy113; + if (yych <= 0x08) goto yy4; + if (yych <= '\n') goto yy113; + goto yy4; + } else { + if (yych <= '\r') goto yy113; + if (yych == 0x1B) goto yy113; + goto yy4; + } + } else { + if (yych <= '*') { + if (yych <= '#') goto yy113; + if (yych <= '$') goto yy4; + if (yych <= '%') goto yy80; + goto yy113; + } else { + if (yych <= '+') goto yy194; + if (yych <= ',') goto yy113; + if (yych <= '-') goto yy251; + goto yy46; + } + } + } else { + if (yych <= '^') { + if (yych <= '?') { + if (yych <= '/') goto yy4; + if (yych <= '9') goto yy269; + if (yych <= ':') goto yy147; + goto yy113; + } else { + if (yych <= '@') goto yy96; + if (yych <= 'F') goto yy269; + if (yych <= 'Z') goto yy115; + goto yy113; + } + } else { + if (yych <= 'z') { + if (yych <= '_') goto yy46; + if (yych <= '`') goto yy113; + if (yych <= 'f') goto yy269; + goto yy115; + } else { + if (yych <= 0x7F) { + if (yych <= '~') goto yy113; + goto yy4; + } else { + if (yych <= 0xC1) goto yy113; + if (yych <= 0xF4) goto yy4; + goto yy113; + } + } + } + } +yy189: + yych = *++YYCURSOR; + if (yych <= '<') { + if (yych <= ',') { + if (yych <= '$') goto yy274; + if (yych <= '&') goto yy273; + goto yy274; + } else { + if (yych == '.') goto yy274; + if (yych <= '9') goto yy273; + goto yy274; + } + } else { + if (yych <= '@') { + if (yych == '>') goto yy274; + if (yych <= '?') goto yy273; + goto yy274; + } else { + if (yych <= 'Z') goto yy273; + if (yych <= '`') goto yy274; + if (yych <= 'z') goto yy273; + goto yy274; + } + } +yy190: + yych = *++YYCURSOR; + if (yych <= '@') { + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy281; + if (yych <= ':') goto yy282; + goto yy1; + } else { + if (yych <= 'F') goto yy283; + if (yych <= '`') goto yy1; + if (yych <= 'f') goto yy283; + goto yy1; + } +yy191: + yych = *++YYCURSOR; + if (yych <= '?') { + if (yych <= '#') { + if (yych <= '\f') { + if (yych <= 0x00) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0x08) { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= '\n') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= 0x1A) { + if (yych <= '\r') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= 0x1B) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0x1F) { + yyt2 = YYCURSOR; + goto yy284; + } + yyt2 = YYCURSOR; + goto yy198; + } + } + } else { + if (yych <= '0') { + if (yych <= '$') { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= ',') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy284; + } + goto yy285; + } else { + if (yych <= '2') { + if (yych <= '1') goto yy286; + goto yy287; + } else { + if (yych <= '9') goto yy285; + if (yych <= ':') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } + } + } + } else { + if (yych <= '~') { + if (yych <= '^') { + if (yych <= '@') { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= 'F') goto yy288; + if (yych <= 'Z') goto yy3; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= '`') { + if (yych <= '_') { + yyt2 = YYCURSOR; + goto yy284; + } + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 'f') goto yy288; + if (yych <= 'z') goto yy3; + yyt2 = YYCURSOR; + goto yy198; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy289; + } + yyt2 = YYCURSOR; + goto yy290; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy291; + } + yyt2 = YYCURSOR; + goto yy292; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy293; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy294; + } + goto yy1; + } + } + } + } +yy192: + yych = *++YYCURSOR; + if (yych <= '@') { + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy283; + if (yych <= ':') goto yy282; + goto yy1; + } else { + if (yych <= 'F') goto yy283; + if (yych <= '`') goto yy1; + if (yych <= 'f') goto yy283; + goto yy1; + } +yy193: + yych = *++YYCURSOR; + if (yych <= ':') { + if (yych <= '.') goto yy1; + if (yych <= '/') goto yy189; + if (yych <= '9') goto yy272; + goto yy191; + } else { + if (yych <= 'F') { + if (yych <= '@') goto yy1; + goto yy272; + } else { + if (yych <= '`') goto yy1; + if (yych <= 'f') goto yy272; + goto yy1; + } + } +yy194: + yych = *++YYCURSOR; + if (yych <= '/') goto yy81; + if (yych <= '9') goto yy295; + goto yy81; +yy195: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '?') { + if (yych <= '+') { + if (yych == '%') goto yy80; + if (yych <= '*') goto yy4; + goto yy80; + } else { + if (yych <= '.') { + if (yych <= ',') goto yy4; + goto yy46; + } else { + if (yych <= '/') goto yy4; + if (yych <= '9') goto yy296; + goto yy4; + } + } + } else { + if (yych <= '^') { + if (yych <= '@') goto yy96; + if (yych <= 'F') goto yy261; + if (yych <= 'Z') goto yy46; + goto yy4; + } else { + if (yych <= '`') { + if (yych <= '_') goto yy46; + goto yy4; + } else { + if (yych <= 'f') goto yy261; + if (yych <= 'z') goto yy46; + goto yy4; + } + } + } +yy196: + yyaccept = 12; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= ':') { + if (yych <= '#') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy113; + if (yych <= 0x08) goto yy3; + if (yych <= '\n') goto yy113; + goto yy3; + } else { + if (yych <= 0x1A) { + if (yych <= '\r') goto yy113; + goto yy3; + } else { + if (yych <= 0x1B) goto yy113; + if (yych <= 0x1F) goto yy3; + goto yy113; + } + } + } else { + if (yych <= '+') { + if (yych <= '$') goto yy3; + if (yych <= '%') goto yy80; + if (yych <= '*') goto yy113; + goto yy80; + } else { + if (yych <= '.') { + if (yych <= ',') goto yy113; + goto yy46; + } else { + if (yych <= '/') goto yy3; + if (yych <= '9') goto yy196; + goto yy148; + } + } + } + } else { + if (yych <= 'z') { + if (yych <= 'Z') { + if (yych <= '?') goto yy113; + if (yych <= '@') goto yy96; + if (yych <= 'F') goto yy196; + goto yy115; + } else { + if (yych <= '_') { + if (yych <= '^') goto yy113; + goto yy46; + } else { + if (yych <= '`') goto yy113; + if (yych <= 'f') goto yy196; + goto yy115; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + if (yych <= '~') goto yy113; + goto yy3; + } else { + if (yych <= 0xC1) goto yy113; + if (yych <= 0xDF) goto yy56; + goto yy57; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) goto yy58; + goto yy59; + } else { + if (yych <= 0xF3) goto yy60; + if (yych <= 0xF4) goto yy61; + goto yy113; + } + } + } + } +yy197: + yyaccept = 6; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[768+yych] & 32) { + goto yy103; + } + if (yych <= '9') { + if (yych <= '#') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy26; + if (yych <= 0x08) goto yy4; + if (yych <= '\n') goto yy26; + goto yy4; + } else { + if (yych <= 0x1A) { + if (yych <= '\r') goto yy26; + goto yy4; + } else { + if (yych <= 0x1B) goto yy26; + if (yych <= 0x1F) goto yy4; + goto yy26; + } + } + } else { + if (yych <= '+') { + if (yych <= '$') goto yy4; + if (yych <= '%') goto yy105; + if (yych <= '*') goto yy26; + goto yy80; + } else { + if (yych <= '-') { + if (yych <= ',') goto yy26; + goto yy46; + } else { + if (yych <= '.') goto yy182; + if (yych <= '/') goto yy4; + goto yy268; + } + } + } + } else { + if (yych <= '_') { + if (yych <= 'D') { + if (yych <= ':') goto yy147; + if (yych <= '?') goto yy26; + if (yych <= '@') goto yy96; + goto yy269; + } else { + if (yych <= 'F') { + if (yych <= 'E') goto yy270; + goto yy269; + } else { + if (yych <= 'Z') goto yy115; + if (yych <= '^') goto yy26; + goto yy46; + } + } + } else { + if (yych <= 'z') { + if (yych <= 'd') { + if (yych <= '`') goto yy26; + goto yy269; + } else { + if (yych <= 'e') goto yy270; + if (yych <= 'f') goto yy269; + goto yy115; + } + } else { + if (yych <= 0x7F) { + if (yych <= '~') goto yy26; + goto yy4; + } else { + if (yych <= 0xC1) goto yy26; + if (yych <= 0xF4) goto yy4; + goto yy26; + } + } + } + } +yy198: + ++YYCURSOR; +yy199: + YYCURSOR = yyt2; +#line 190 "../../lnav/src/data_scanner_re.re" + { RET(DT_IPV6_ADDRESS); } +#line 5991 "../../lnav/src/data_scanner_re.cc" +yy200: + yych = *++YYCURSOR; + if (yych <= 'f') { + if (yych <= ':') { + if (yych <= '.') { + if (yych <= '-') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt1 = YYCURSOR; + goto yy297; + } else { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy298; + goto yy299; + } + } else { + if (yych <= 'F') { + if (yych <= '@') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy300; + } else { + if (yych <= 'Z') goto yy1; + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy300; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + if (yych <= 'z') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + yyt2 = YYCURSOR; + goto yy206; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy201: + yych = *++YYCURSOR; + if (yych <= 'f') { + if (yych <= ':') { + if (yych <= '.') { + if (yych <= '-') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt1 = YYCURSOR; + goto yy297; + } else { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy301; + goto yy299; + } + } else { + if (yych <= 'F') { + if (yych <= '@') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy300; + } else { + if (yych <= 'Z') goto yy1; + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy300; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + if (yych <= 'z') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + yyt2 = YYCURSOR; + goto yy206; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy202: + yych = *++YYCURSOR; + if (yych <= '`') { + if (yych <= '5') { + if (yych <= '.') { + if (yych <= '-') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt1 = YYCURSOR; + goto yy297; + } else { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '4') goto yy301; + goto yy302; + } + } else { + if (yych <= '@') { + if (yych <= '9') goto yy298; + if (yych <= ':') goto yy299; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 'F') goto yy300; + if (yych <= 'Z') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } + } + } else { + if (yych <= 0xDF) { + if (yych <= 'z') { + if (yych <= 'f') goto yy300; + goto yy1; + } else { + if (yych <= 0x7F) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0xC1) goto yy1; + yyt2 = YYCURSOR; + goto yy205; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xE0) { + yyt2 = YYCURSOR; + goto yy206; + } + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy203: + yych = *++YYCURSOR; + if (yych <= 'z') { + if (yych <= '@') { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy300; + if (yych <= ':') goto yy299; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 'Z') { + if (yych <= 'F') goto yy300; + goto yy1; + } else { + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 'f') goto yy300; + goto yy1; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + yyt2 = YYCURSOR; + goto yy206; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy204: + yych = *++YYCURSOR; + if (yych <= 'f') { + if (yych <= 'E') { + if (yych <= '9') { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy300; + } else { + if (yych <= ':') goto yy299; + if (yych <= '@') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy300; + } + } else { + if (yych <= 'Z') { + if (yych <= 'F') goto yy303; + goto yy1; + } else { + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 'e') goto yy300; + goto yy303; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + if (yych <= 'z') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 0xC1) goto yy1; + if (yych >= 0xE0) { + yyt2 = YYCURSOR; + goto yy206; + } + yyt2 = YYCURSOR; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy205: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy198; + goto yy1; +yy206: + yych = *++YYCURSOR; + if (yych <= 0x9F) goto yy1; + if (yych <= 0xBF) goto yy205; + goto yy1; +yy207: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy205; + goto yy1; +yy208: + yych = *++YYCURSOR; + if (yych <= 0x8F) goto yy1; + if (yych <= 0xBF) goto yy207; + goto yy1; +yy209: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy207; + goto yy1; +yy210: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0x8F) goto yy207; + goto yy1; +yy211: + yych = *++YYCURSOR; + if (yych <= '@') { + if (yych <= '-') { + if (yych <= ',') goto yy305; + goto yy308; + } else { + if (yych <= '/') goto yy305; + if (yych <= ':') goto yy308; + goto yy305; + } + } else { + if (yych <= '_') { + if (yych <= 'Z') goto yy308; + if (yych <= '^') goto yy305; + goto yy308; + } else { + if (yych <= '`') goto yy305; + if (yych <= 'z') goto yy308; + goto yy305; + } + } +yy212: + yych = *++YYCURSOR; +yy213: + if (yych <= '/') { + if (yych <= 0x1F) { + if (yych <= '\t') { + if (yych <= 0x08) goto yy1; + goto yy212; + } else { + if (yych == '\r') goto yy212; + goto yy1; + } + } else { + if (yych <= ',') { + if (yych <= ' ') goto yy212; + goto yy1; + } else { + if (yych <= '-') goto yy311; + if (yych <= '.') goto yy1; + goto yy215; + } + } + } else { + if (yych <= '@') { + if (yych <= '=') { + if (yych <= ':') goto yy311; + goto yy1; + } else { + if (yych <= '>') goto yy216; + if (yych <= '?') goto yy215; + goto yy1; + } + } else { + if (yych <= '_') { + if (yych <= 'Z') goto yy311; + if (yych <= '^') goto yy1; + goto yy311; + } else { + if (yych <= '`') goto yy1; + if (yych <= 'z') goto yy311; + goto yy1; + } + } + } +yy214: + yych = *++YYCURSOR; + if (yybm[512+yych] & 16) { + goto yy214; + } + if (yych <= ' ') { + if (yych <= '\f') { + if (yych == '\t') goto yy312; + goto yy1; + } else { + if (yych <= '\r') goto yy312; + if (yych <= 0x1F) goto yy1; + goto yy312; + } + } else { + if (yych <= '<') { + if (yych != '/') goto yy1; + } else { + if (yych <= '=') goto yy313; + if (yych <= '>') goto yy216; + if (yych >= '@') goto yy1; + } + } +yy215: + yych = *++YYCURSOR; + if (yych == '>') goto yy314; + goto yy1; +yy216: + ++YYCURSOR; +yy217: +#line 200 "../../lnav/src/data_scanner_re.re" + { + RET(DT_XML_OPEN_TAG); + } +#line 6460 "../../lnav/src/data_scanner_re.cc" +yy218: + yych = *++YYCURSOR; +yy219: + if (yych <= '/') { + if (yych <= '\r') { + if (yych == '\t') goto yy316; + if (yych <= '\f') goto yy1; + goto yy316; + } else { + if (yych <= ' ') { + if (yych <= 0x1F) goto yy1; + goto yy316; + } else { + if (yych == '-') goto yy218; + goto yy1; + } + } + } else { + if (yych <= 'Z') { + if (yych <= '=') { + if (yych <= ':') goto yy218; + goto yy1; + } else { + if (yych <= '>') goto yy317; + if (yych <= '@') goto yy1; + goto yy218; + } + } else { + if (yych <= '_') { + if (yych <= '^') goto yy1; + goto yy218; + } else { + if (yych <= '`') goto yy1; + if (yych <= 'z') goto yy218; + goto yy1; + } + } + } +yy220: + yych = *++YYCURSOR; + if (yych <= '@') { + if (yych <= '-') { + if (yych <= ',') goto yy319; + goto yy320; + } else { + if (yych <= '/') goto yy319; + if (yych <= ':') goto yy320; + goto yy319; + } + } else { + if (yych <= '_') { + if (yych <= 'Z') goto yy320; + if (yych <= '^') goto yy319; + goto yy320; + } else { + if (yych <= '`') goto yy319; + if (yych <= 'z') goto yy320; + goto yy319; + } + } +yy221: + ++YYCURSOR; + yyt3 = yyt4; +yy222: + YYCURSOR = yyt3; +#line 264 "../../lnav/src/data_scanner_re.re" + { RET(DT_WORD); } +#line 6528 "../../lnav/src/data_scanner_re.cc" +yy223: + yych = *++YYCURSOR; + if (yych <= '\f') { + if (yych == '\t') goto yy221; + yyt3 = yyt4; + goto yy222; + } else { + if (yych <= '\r') goto yy221; + if (yych == ' ') goto yy221; + yyt3 = yyt4; + goto yy222; + } +yy224: + yyaccept = 15; + yych = *(YYMARKER = ++YYCURSOR); +yy225: + if (yych <= '\'') { + if (yych <= '\r') { + if (yych <= 0x08) { + if (yych <= 0x00) { + yyt4 = YYCURSOR; + goto yy221; + } + yyt3 = yyt4; + goto yy222; + } else { + if (yych <= '\n') { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych <= '\f') { + yyt3 = yyt4; + goto yy222; + } + yyt4 = YYCURSOR; + goto yy221; + } + } else { + if (yych <= '!') { + if (yych <= 0x1F) { + yyt3 = yyt4; + goto yy222; + } + if (yych <= ' ') { + yyt4 = YYCURSOR; + goto yy221; + } + yyt4 = YYCURSOR; + goto yy223; + } else { + if (yych <= '"') { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych <= '&') { + yyt3 = yyt4; + goto yy222; + } + yyt4 = YYCURSOR; + goto yy224; + } + } + } else { + if (yych <= '9') { + if (yych <= ',') { + if (yych <= '*') { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych <= '+') { + yyt3 = yyt4; + goto yy222; + } + yyt4 = YYCURSOR; + goto yy223; + } else { + if (yych != '.') { + yyt3 = yyt4; + goto yy222; + } + yyt3 = yyt4; + yyt4 = YYCURSOR; + } + } else { + if (yych <= '?') { + if (yych <= ';') { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych <= '>') { + yyt3 = yyt4; + goto yy222; + } + yyt4 = YYCURSOR; + goto yy223; + } else { + if (yych <= '`') { + yyt3 = yyt4; + goto yy222; + } + if (yych <= 'z') { + yyt3 = yyt4; + goto yy137; + } + yyt3 = yyt4; + goto yy222; + } + } + } +yy226: + yych = *++YYCURSOR; + if (yych <= '\f') { + if (yych == '\t') goto yy221; + goto yy1; + } else { + if (yych <= '\r') goto yy221; + if (yych == ' ') goto yy221; + goto yy1; + } +yy227: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= ':') { + if (yych <= '+') { + if (yych == '%') goto yy80; + if (yych <= '*') goto yy4; + goto yy80; + } else { + if (yych <= '.') { + if (yych <= ',') goto yy4; + goto yy46; + } else { + if (yych <= '/') goto yy4; + if (yych <= '9') goto yy321; + goto yy147; + } + } + } else { + if (yych <= '^') { + if (yych <= '@') { + if (yych <= '?') goto yy4; + goto yy96; + } else { + if (yych <= 'F') goto yy321; + if (yych <= 'Z') goto yy115; + goto yy4; + } + } else { + if (yych <= '`') { + if (yych <= '_') goto yy46; + goto yy4; + } else { + if (yych <= 'f') goto yy321; + if (yych <= 'z') goto yy115; + goto yy4; + } + } + } +yy228: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '9') { + if (yych <= '*') { + if (yych <= ' ') { + if (yych <= 0x1F) goto yy4; + goto yy322; + } else { + if (yych == '%') goto yy80; + goto yy4; + } + } else { + if (yych <= ',') { + if (yych <= '+') goto yy80; + goto yy4; + } else { + if (yych <= '.') goto yy46; + if (yych <= '/') goto yy4; + goto yy321; + } + } + } else { + if (yych <= 'Z') { + if (yych <= '?') { + if (yych <= ':') goto yy147; + goto yy4; + } else { + if (yych <= '@') goto yy96; + if (yych <= 'F') goto yy321; + goto yy115; + } + } else { + if (yych <= '`') { + if (yych == '_') goto yy46; + goto yy4; + } else { + if (yych <= 'f') goto yy321; + if (yych <= 'z') goto yy115; + goto yy4; + } + } + } +yy229: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[768+yych] & 64) { + goto yy115; + } + if (yych <= ',') { + if (yych <= '$') { + if (yych == ' ') goto yy322; + goto yy4; + } else { + if (yych <= '%') goto yy80; + if (yych == '+') goto yy80; + goto yy4; + } + } else { + if (yych <= '?') { + if (yych <= '.') goto yy46; + if (yych <= '/') goto yy4; + if (yych <= ':') goto yy148; + goto yy4; + } else { + if (yych <= '@') goto yy96; + if (yych == '_') goto yy46; + goto yy4; + } + } +yy230: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[1024+yych] & 16) { + goto yy46; + } + if (yych <= ' ') { + if (yych <= '\f') { + if (yych == '\t') goto yy221; + goto yy4; + } else { + if (yych <= '\r') goto yy221; + if (yych <= 0x1F) goto yy4; + goto yy221; + } + } else { + if (yych <= '*') { + if (yych == '%') goto yy80; + goto yy4; + } else { + if (yych <= '+') goto yy80; + if (yych == '@') goto yy96; + goto yy4; + } + } +yy231: + yyaccept = 16; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= ':') { + if (yych <= '.') { + yyt3 = yyt4; + goto yy222; + } + if (yych <= '/') goto yy189; + if (yych <= '9') goto yy272; + goto yy191; + } else { + if (yych <= 'F') { + if (yych <= '@') { + yyt3 = yyt4; + goto yy222; + } + goto yy272; + } else { + if (yych <= '`') { + yyt3 = yyt4; + goto yy222; + } + if (yych <= 'f') goto yy272; + yyt3 = yyt4; + goto yy222; + } + } +yy232: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= ',') { + if (yych <= '!') { + if (yych <= '\f') { + if (yych <= 0x00) { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych <= 0x08) goto yy4; + if (yych <= '\n') { + yyt4 = YYCURSOR; + goto yy221; + } + goto yy4; + } else { + if (yych <= '\r') { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych <= 0x1F) goto yy4; + if (yych <= ' ') { + yyt3 = YYCURSOR; + goto yy323; + } + yyt4 = YYCURSOR; + goto yy223; + } + } else { + if (yych <= '&') { + if (yych <= '"') { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych == '%') goto yy80; + goto yy4; + } else { + if (yych <= '\'') { + yyt4 = YYCURSOR; + goto yy224; + } + if (yych <= '*') { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych <= '+') goto yy80; + yyt4 = YYCURSOR; + goto yy223; + } + } + } else { + if (yych <= '?') { + if (yych <= '9') { + if (yych <= '-') goto yy46; + if (yych <= '.') { + yyt4 = YYCURSOR; + goto yy230; + } + if (yych <= '/') goto yy4; + goto yy321; + } else { + if (yych <= ':') { + yyt3 = YYCURSOR; + goto yy324; + } + if (yych <= ';') { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych <= '>') goto yy4; + yyt4 = YYCURSOR; + goto yy223; + } + } else { + if (yych <= '^') { + if (yych <= '@') goto yy96; + if (yych <= 'F') goto yy321; + if (yych <= 'Z') goto yy115; + goto yy4; + } else { + if (yych <= '`') { + if (yych <= '_') goto yy46; + goto yy4; + } else { + if (yych <= 'f') goto yy325; + if (yych <= 'z') goto yy326; + goto yy4; + } + } + } + } +yy233: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '+') { + if (yych <= ' ') { + if (yych <= '\n') { + if (yych <= 0x00) { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych <= 0x08) goto yy4; + yyt4 = YYCURSOR; + goto yy221; + } else { + if (yych == '\r') { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych <= 0x1F) goto yy4; + yyt3 = YYCURSOR; + goto yy323; + } + } else { + if (yych <= '%') { + if (yych <= '!') { + yyt4 = YYCURSOR; + goto yy223; + } + if (yych <= '"') { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych <= '$') goto yy4; + goto yy80; + } else { + if (yych <= '&') goto yy4; + if (yych <= '\'') { + yyt4 = YYCURSOR; + goto yy224; + } + if (yych <= '*') { + yyt4 = YYCURSOR; + goto yy221; + } + goto yy80; + } + } + } else { + if (yych <= '>') { + if (yych <= '/') { + if (yych <= ',') { + yyt4 = YYCURSOR; + goto yy223; + } + if (yych <= '-') goto yy46; + if (yych <= '.') { + yyt4 = YYCURSOR; + goto yy230; + } + goto yy4; + } else { + if (yych <= '9') goto yy115; + if (yych <= ':') { + yyt3 = YYCURSOR; + goto yy234; + } + if (yych <= ';') { + yyt4 = YYCURSOR; + goto yy221; + } + goto yy4; + } + } else { + if (yych <= '^') { + if (yych <= '?') { + yyt4 = YYCURSOR; + goto yy223; + } + if (yych <= '@') goto yy96; + if (yych <= 'Z') goto yy115; + goto yy4; + } else { + if (yych <= '_') goto yy46; + if (yych <= '`') goto yy4; + if (yych <= 'z') goto yy326; + goto yy4; + } + } + } +yy234: + yyaccept = 15; + yych = *(YYMARKER = ++YYCURSOR); + if (yych == '/') goto yy189; + if (yych == ':') goto yy152; + goto yy222; +yy235: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '9') { + if (yych <= '*') { + if (yych <= ' ') { + if (yych <= 0x1F) goto yy4; + goto yy322; + } else { + if (yych == '%') goto yy80; + goto yy4; + } + } else { + if (yych <= ',') { + if (yych <= '+') goto yy80; + goto yy4; + } else { + if (yych <= '.') goto yy46; + if (yych <= '/') goto yy4; + goto yy115; + } + } + } else { + if (yych <= 'S') { + if (yych <= '?') { + if (yych <= ':') goto yy148; + goto yy4; + } else { + if (yych <= '@') goto yy96; + if (yych <= 'R') goto yy115; + goto yy327; + } + } else { + if (yych <= '_') { + if (yych <= 'Z') goto yy115; + if (yych <= '^') goto yy4; + goto yy46; + } else { + if (yych <= '`') goto yy4; + if (yych <= 'z') goto yy115; + goto yy4; + } + } + } +yy236: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= ',') { + if (yych <= '!') { + if (yych <= '\f') { + if (yych <= 0x00) { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych <= 0x08) goto yy4; + if (yych <= '\n') { + yyt4 = YYCURSOR; + goto yy221; + } + goto yy4; + } else { + if (yych <= '\r') { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych <= 0x1F) goto yy4; + if (yych <= ' ') { + yyt3 = YYCURSOR; + goto yy323; + } + yyt4 = YYCURSOR; + goto yy223; + } + } else { + if (yych <= '&') { + if (yych <= '"') { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych == '%') goto yy80; + goto yy4; + } else { + if (yych <= '\'') { + yyt4 = YYCURSOR; + goto yy224; + } + if (yych <= '*') { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych <= '+') goto yy80; + yyt4 = YYCURSOR; + goto yy223; + } + } + } else { + if (yych <= '?') { + if (yych <= '9') { + if (yych <= '-') goto yy46; + if (yych <= '.') { + yyt4 = YYCURSOR; + goto yy230; + } + if (yych <= '/') goto yy4; + goto yy115; + } else { + if (yych <= ':') { + yyt3 = YYCURSOR; + goto yy234; + } + if (yych <= ';') { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych <= '>') goto yy4; + yyt4 = YYCURSOR; + goto yy223; + } + } else { + if (yych <= '_') { + if (yych <= '@') goto yy96; + if (yych <= 'Z') goto yy115; + if (yych <= '^') goto yy4; + goto yy46; + } else { + if (yych <= 'r') { + if (yych <= '`') goto yy4; + goto yy326; + } else { + if (yych <= 's') goto yy328; + if (yych <= 'z') goto yy326; + goto yy4; + } + } + } + } +yy237: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '9') { + if (yych <= '*') { + if (yych <= ' ') { + if (yych <= 0x1F) goto yy4; + goto yy322; + } else { + if (yych == '%') goto yy80; + goto yy4; + } + } else { + if (yych <= ',') { + if (yych <= '+') goto yy80; + goto yy4; + } else { + if (yych <= '.') goto yy46; + if (yych <= '/') goto yy4; + goto yy115; + } + } + } else { + if (yych <= 'L') { + if (yych <= '?') { + if (yych <= ':') goto yy148; + goto yy4; + } else { + if (yych <= '@') goto yy96; + if (yych <= 'K') goto yy115; + goto yy329; + } + } else { + if (yych <= '_') { + if (yych <= 'Z') goto yy115; + if (yych <= '^') goto yy4; + goto yy46; + } else { + if (yych <= '`') goto yy4; + if (yych <= 'z') goto yy115; + goto yy4; + } + } + } +yy238: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= ',') { + if (yych <= '!') { + if (yych <= '\f') { + if (yych <= 0x00) { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych <= 0x08) goto yy4; + if (yych <= '\n') { + yyt4 = YYCURSOR; + goto yy221; + } + goto yy4; + } else { + if (yych <= '\r') { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych <= 0x1F) goto yy4; + if (yych <= ' ') { + yyt3 = YYCURSOR; + goto yy323; + } + yyt4 = YYCURSOR; + goto yy223; + } + } else { + if (yych <= '&') { + if (yych <= '"') { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych == '%') goto yy80; + goto yy4; + } else { + if (yych <= '\'') { + yyt4 = YYCURSOR; + goto yy224; + } + if (yych <= '*') { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych <= '+') goto yy80; + yyt4 = YYCURSOR; + goto yy223; + } + } + } else { + if (yych <= '?') { + if (yych <= '9') { + if (yych <= '-') goto yy46; + if (yych <= '.') { + yyt4 = YYCURSOR; + goto yy230; + } + if (yych <= '/') goto yy4; + goto yy115; + } else { + if (yych <= ':') { + yyt3 = YYCURSOR; + goto yy234; + } + if (yych <= ';') { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych <= '>') goto yy4; + yyt4 = YYCURSOR; + goto yy223; + } + } else { + if (yych <= '_') { + if (yych <= '@') goto yy96; + if (yych <= 'Z') goto yy115; + if (yych <= '^') goto yy4; + goto yy46; + } else { + if (yych <= 'd') { + if (yych <= '`') goto yy4; + goto yy326; + } else { + if (yych <= 'e') goto yy330; + if (yych <= 'z') goto yy326; + goto yy4; + } + } + } + } +yy239: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '9') { + if (yych <= '*') { + if (yych <= ' ') { + if (yych <= 0x1F) goto yy4; + goto yy322; + } else { + if (yych == '%') goto yy80; + goto yy4; + } + } else { + if (yych <= ',') { + if (yych <= '+') goto yy80; + goto yy4; + } else { + if (yych <= '.') goto yy46; + if (yych <= '/') goto yy4; + goto yy115; + } + } + } else { + if (yych <= 'E') { + if (yych <= '?') { + if (yych <= ':') goto yy148; + goto yy4; + } else { + if (yych <= '@') goto yy96; + if (yych <= 'D') goto yy115; + goto yy329; + } + } else { + if (yych <= '_') { + if (yych <= 'Z') goto yy115; + if (yych <= '^') goto yy4; + goto yy46; + } else { + if (yych <= '`') goto yy4; + if (yych <= 'z') goto yy115; + goto yy4; + } + } + } +yy240: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= ',') { + if (yych <= '!') { + if (yych <= '\f') { + if (yych <= 0x00) { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych <= 0x08) goto yy4; + if (yych <= '\n') { + yyt4 = YYCURSOR; + goto yy221; + } + goto yy4; + } else { + if (yych <= '\r') { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych <= 0x1F) goto yy4; + if (yych <= ' ') { + yyt3 = YYCURSOR; + goto yy323; + } + yyt4 = YYCURSOR; + goto yy223; + } + } else { + if (yych <= '&') { + if (yych <= '"') { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych == '%') goto yy80; + goto yy4; + } else { + if (yych <= '\'') { + yyt4 = YYCURSOR; + goto yy224; + } + if (yych <= '*') { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych <= '+') goto yy80; + yyt4 = YYCURSOR; + goto yy223; + } + } + } else { + if (yych <= '?') { + if (yych <= '9') { + if (yych <= '-') goto yy46; + if (yych <= '.') { + yyt4 = YYCURSOR; + goto yy230; + } + if (yych <= '/') goto yy4; + goto yy115; + } else { + if (yych <= ':') { + yyt3 = YYCURSOR; + goto yy234; + } + if (yych <= ';') { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych <= '>') goto yy4; + yyt4 = YYCURSOR; + goto yy223; + } + } else { + if (yych <= '_') { + if (yych <= '@') goto yy96; + if (yych <= 'Z') goto yy115; + if (yych <= '^') goto yy4; + goto yy46; + } else { + if (yych <= 'k') { + if (yych <= '`') goto yy4; + goto yy326; + } else { + if (yych <= 'l') goto yy330; + if (yych <= 'z') goto yy326; + goto yy4; + } + } + } + } +yy241: + yyaccept = 15; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= 0x1B) { + if (yych <= 0x00) goto yy222; + if (yych <= 0x1A) goto yy84; + goto yy222; + } else { + if (yych <= 0x7F) goto yy84; + if (yych <= 0xC1) goto yy222; + if (yych <= 0xF4) goto yy84; + goto yy222; + } +yy242: + yyaccept = 15; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= 0x1A) { + if (yych <= '\t') { + if (yych <= 0x00) goto yy222; + if (yych <= 0x08) goto yy84; + goto yy241; + } else { + if (yych == '\r') goto yy241; + goto yy84; + } + } else { + if (yych <= ' ') { + if (yych <= 0x1B) goto yy222; + if (yych <= 0x1F) goto yy84; + goto yy241; + } else { + if (yych <= 0x7F) goto yy84; + if (yych <= 0xC1) goto yy222; + if (yych <= 0xF4) goto yy84; + goto yy222; + } + } +yy243: + yyaccept = 15; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= 'S') { + if (yych <= ',') { + if (yych <= '&') { + if (yych == '!') { + yyt1 = yyt4 = YYCURSOR; + goto yy331; + } + yyt2 = YYCURSOR; + goto yy160; + } else { + if (yych <= '\'') { + yyt1 = YYCURSOR; + goto yy332; + } + if (yych <= '+') { + yyt2 = YYCURSOR; + goto yy160; + } + yyt1 = yyt4 = YYCURSOR; + goto yy331; + } + } else { + if (yych <= '>') { + if (yych == '.') { + yyt1 = yyt4 = YYCURSOR; + goto yy331; + } + yyt2 = YYCURSOR; + goto yy160; + } else { + if (yych <= '?') { + yyt1 = yyt4 = YYCURSOR; + goto yy331; + } + if (yych <= 'R') { + yyt2 = YYCURSOR; + goto yy160; + } + goto yy222; + } + } + } else { + if (yych <= 0xC1) { + if (yych <= 's') { + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy160; + } + if (yych <= 'r') { + yyt1 = YYCURSOR; + goto yy333; + } + goto yy137; + } else { + if (yych <= 'z') { + yyt1 = YYCURSOR; + goto yy333; + } + if (yych <= 0x7F) { + yyt2 = YYCURSOR; + goto yy160; + } + goto yy222; + } + } else { + if (yych <= 0xEF) { + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy163; + } + if (yych <= 0xE0) { + yyt2 = YYCURSOR; + goto yy164; + } + yyt2 = YYCURSOR; + goto yy165; + } else { + if (yych <= 0xF0) { + yyt2 = YYCURSOR; + goto yy166; + } + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy167; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy168; + } + goto yy222; + } + } + } +yy244: + yych = *++YYCURSOR; + if (yych <= '\f') { + if (yych == '\t') goto yy241; + goto yy84; + } else { + if (yych <= '\r') goto yy241; + if (yych == ' ') goto yy241; + goto yy84; + } +yy245: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '9') { + if (yych <= '+') { + if (yych == '%') goto yy80; + if (yych <= '*') goto yy4; + goto yy80; + } else { + if (yych <= ',') goto yy4; + if (yych == '/') goto yy4; + goto yy46; + } + } else { + if (yych <= '^') { + if (yych <= '?') goto yy4; + if (yych <= '@') goto yy96; + if (yych <= 'Z') goto yy334; + goto yy4; + } else { + if (yych <= '_') goto yy46; + if (yych <= '`') goto yy4; + if (yych <= 'z') goto yy334; + goto yy4; + } + } +yy246: + yych = *++YYCURSOR; + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy335; + goto yy1; +yy247: + yych = *++YYCURSOR; + if (yych == ':') goto yy336; + goto yy1; +yy248: + ++YYCURSOR; +#line 208 "../../lnav/src/data_scanner_re.re" + { + RET(DT_H1); + } +#line 7589 "../../lnav/src/data_scanner_re.cc" +yy249: + yych = *++YYCURSOR; + if (yybm[512+yych] & 64) { + goto yy249; + } + if (yych <= '9') { + if (yych == '-') goto yy158; + if (yych <= '/') goto yy1; + goto yy158; + } else { + if (yych <= 'Z') { + if (yych <= '@') goto yy1; + goto yy337; + } else { + if (yych <= '`') goto yy1; + if (yych <= 'z') goto yy337; + goto yy1; + } + } +yy250: + yyaccept = 6; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[768+yych] & 32) { + goto yy103; + } + if (yych <= '?') { + if (yych <= '#') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy26; + if (yych <= 0x08) goto yy3; + if (yych <= '\n') goto yy26; + goto yy3; + } else { + if (yych <= 0x1A) { + if (yych <= '\r') goto yy26; + goto yy3; + } else { + if (yych <= 0x1B) goto yy26; + if (yych <= 0x1F) goto yy3; + goto yy26; + } + } + } else { + if (yych <= ',') { + if (yych <= '%') { + if (yych <= '$') goto yy3; + goto yy105; + } else { + if (yych == '+') goto yy80; + goto yy26; + } + } else { + if (yych <= '/') { + if (yych <= '.') goto yy46; + goto yy3; + } else { + if (yych <= '9') goto yy250; + if (yych <= ':') goto yy62; + goto yy26; + } + } + } + } else { + if (yych <= 'z') { + if (yych <= '^') { + if (yych <= 'D') { + if (yych <= '@') goto yy96; + goto yy46; + } else { + if (yych <= 'E') goto yy339; + if (yych <= 'Z') goto yy46; + goto yy26; + } + } else { + if (yych <= '`') { + if (yych <= '_') goto yy46; + goto yy26; + } else { + if (yych == 'e') goto yy339; + goto yy46; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + if (yych <= '~') goto yy26; + goto yy3; + } else { + if (yych <= 0xC1) goto yy26; + if (yych <= 0xDF) goto yy56; + goto yy57; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) goto yy58; + goto yy59; + } else { + if (yych <= 0xF3) goto yy60; + if (yych <= 0xF4) goto yy61; + goto yy26; + } + } + } + } +yy251: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '9') { + if (yych <= '+') { + if (yych == '%') goto yy80; + if (yych <= '*') goto yy4; + goto yy80; + } else { + if (yych <= ',') goto yy4; + if (yych <= '.') goto yy46; + if (yych <= '/') goto yy4; + goto yy340; + } + } else { + if (yych <= '^') { + if (yych <= '?') goto yy4; + if (yych <= '@') goto yy96; + if (yych <= 'Z') goto yy46; + goto yy4; + } else { + if (yych == '`') goto yy4; + if (yych <= 'z') goto yy46; + goto yy4; + } + } +yy252: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= ':') { + if (yych <= 0x1F) { + if (yych <= '\f') { + if (yych <= 0x00) goto yy5; + if (yych <= 0x08) goto yy3; + if (yych <= '\n') goto yy5; + goto yy3; + } else { + if (yych <= '\r') goto yy5; + if (yych == 0x1B) goto yy5; + goto yy3; + } + } else { + if (yych <= '-') { + if (yych == '$') goto yy3; + if (yych <= ',') goto yy5; + goto yy174; + } else { + if (yych <= '.') goto yy252; + if (yych <= '/') goto yy3; + if (yych <= '9') goto yy174; + goto yy62; + } + } + } else { + if (yych <= '~') { + if (yych <= '^') { + if (yych <= '?') goto yy5; + if (yych <= '@') goto yy3; + if (yych <= 'Z') goto yy341; + goto yy5; + } else { + if (yych <= '_') goto yy3; + if (yych <= '`') goto yy5; + if (yych <= 'z') goto yy341; + goto yy5; + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) goto yy3; + if (yych <= 0xC1) goto yy5; + if (yych <= 0xDF) goto yy56; + goto yy57; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) goto yy58; + goto yy59; + } else { + if (yych <= 0xF3) goto yy60; + if (yych <= 0xF4) goto yy61; + goto yy5; + } + } + } + } +yy253: + yych = *++YYCURSOR; + if (yych == 'o') goto yy342; + goto yy100; +yy254: + yyaccept = 5; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '9') { + if (yych <= ' ') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy24; + if (yych <= 0x08) goto yy4; + if (yych <= '\n') goto yy24; + goto yy4; + } else { + if (yych <= 0x1A) { + if (yych <= '\r') goto yy24; + goto yy4; + } else { + if (yych <= 0x1B) goto yy24; + if (yych <= 0x1F) goto yy4; + goto yy24; + } + } + } else { + if (yych <= '$') { + if (yych == '"') goto yy24; + if (yych <= '#') goto yy99; + goto yy101; + } else { + if (yych <= '\'') { + if (yych <= '&') goto yy99; + goto yy24; + } else { + if (yych <= '*') goto yy99; + if (yych <= ',') goto yy24; + goto yy101; + } + } + } + } else { + if (yych <= '`') { + if (yych <= '[') { + if (yych <= ':') goto yy4; + if (yych <= '?') goto yy24; + if (yych <= 'Z') goto yy101; + goto yy24; + } else { + if (yych <= ']') { + if (yych <= '\\') goto yy99; + goto yy24; + } else { + if (yych <= '^') goto yy99; + if (yych <= '_') goto yy101; + goto yy24; + } + } + } else { + if (yych <= '}') { + if (yych == 'g') goto yy343; + if (yych <= 'z') goto yy101; + goto yy24; + } else { + if (yych <= 0x7F) { + if (yych <= '~') goto yy99; + goto yy4; + } else { + if (yych <= 0xC1) goto yy24; + if (yych <= 0xF4) goto yy4; + goto yy24; + } + } + } + } +yy255: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '9') { + if (yych <= '+') { + if (yych == '%') goto yy80; + if (yych <= '*') goto yy4; + goto yy80; + } else { + if (yych <= ',') goto yy4; + if (yych <= '.') goto yy46; + if (yych <= '/') goto yy4; + goto yy344; + } + } else { + if (yych <= '^') { + if (yych <= '?') goto yy4; + if (yych <= '@') goto yy96; + if (yych <= 'Z') goto yy344; + goto yy4; + } else { + if (yych == '`') goto yy4; + if (yych <= 'z') goto yy344; + goto yy4; + } + } +yy256: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '1') { + if (yych <= '+') { + if (yych == '%') goto yy80; + if (yych <= '*') goto yy4; + goto yy80; + } else { + if (yych <= '.') { + if (yych <= ',') goto yy4; + goto yy46; + } else { + if (yych <= '/') goto yy4; + if (yych <= '0') goto yy346; + goto yy347; + } + } + } else { + if (yych <= 'Z') { + if (yych <= '9') { + if (yych <= '2') goto yy348; + goto yy346; + } else { + if (yych <= '?') goto yy4; + if (yych <= '@') goto yy96; + goto yy46; + } + } else { + if (yych <= '_') { + if (yych <= '^') goto yy4; + goto yy46; + } else { + if (yych <= '`') goto yy4; + if (yych <= 'z') goto yy46; + goto yy4; + } + } + } +yy257: + yyaccept = 6; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[768+yych] & 32) { + goto yy103; + } + if (yych <= '/') { + if (yych <= 0x1F) { + if (yych <= '\f') { + if (yych <= 0x00) goto yy26; + if (yych <= 0x08) goto yy4; + if (yych <= '\n') goto yy26; + goto yy4; + } else { + if (yych <= '\r') goto yy26; + if (yych == 0x1B) goto yy26; + goto yy4; + } + } else { + if (yych <= '*') { + if (yych <= '#') goto yy26; + if (yych <= '$') goto yy4; + if (yych <= '%') goto yy105; + goto yy26; + } else { + if (yych <= ',') { + if (yych <= '+') goto yy80; + goto yy26; + } else { + if (yych <= '-') goto yy255; + if (yych <= '.') goto yy256; + goto yy4; + } + } + } + } else { + if (yych <= '_') { + if (yych <= '@') { + if (yych <= '9') goto yy349; + if (yych <= ':') goto yy4; + if (yych <= '?') goto yy26; + goto yy96; + } else { + if (yych <= 'E') { + if (yych >= 'E') goto yy259; + } else { + if (yych <= 'Z') goto yy258; + if (yych <= '^') goto yy26; + } + } + } else { + if (yych <= 'z') { + if (yych <= '`') goto yy26; + if (yych == 'e') goto yy259; + } else { + if (yych <= 0x7F) { + if (yych <= '~') goto yy26; + goto yy4; + } else { + if (yych <= 0xC1) goto yy26; + if (yych <= 0xF4) goto yy4; + goto yy26; + } + } + } + } +yy258: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[512+yych] & 128) { + goto yy258; + } + if (yych <= '-') { + if (yych <= 0x1B) { + if (yych <= '\n') { + if (yych <= 0x00) goto yy5; + if (yych <= 0x08) goto yy3; + goto yy5; + } else { + if (yych == '\r') goto yy5; + if (yych <= 0x1A) goto yy3; + goto yy5; + } + } else { + if (yych <= '%') { + if (yych <= 0x1F) goto yy3; + if (yych <= '#') goto yy5; + if (yych <= '$') goto yy3; + goto yy80; + } else { + if (yych == '+') goto yy80; + if (yych <= ',') goto yy5; + goto yy255; + } + } + } else { + if (yych <= 0x7F) { + if (yych <= ':') { + if (yych <= '.') goto yy350; + if (yych <= '/') goto yy3; + goto yy62; + } else { + if (yych == '@') goto yy96; + if (yych <= '~') goto yy5; + goto yy3; + } + } else { + if (yych <= 0xEF) { + if (yych <= 0xC1) goto yy5; + if (yych <= 0xDF) goto yy56; + if (yych <= 0xE0) goto yy57; + goto yy58; + } else { + if (yych <= 0xF0) goto yy59; + if (yych <= 0xF3) goto yy60; + if (yych <= 0xF4) goto yy61; + goto yy5; + } + } + } +yy259: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[512+yych] & 128) { + goto yy258; + } + if (yych <= ',') { + if (yych <= '%') { + if (yych <= '$') goto yy4; + goto yy80; + } else { + if (yych == '+') goto yy351; + goto yy4; + } + } else { + if (yych <= '.') { + if (yych <= '-') goto yy352; + goto yy350; + } else { + if (yych == '@') goto yy96; + goto yy4; + } + } +yy260: + yyaccept = 6; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[768+yych] & 32) { + goto yy103; + } + if (yych <= '5') { + if (yych <= '#') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy26; + if (yych <= 0x08) goto yy4; + if (yych <= '\n') goto yy26; + goto yy4; + } else { + if (yych <= 0x1A) { + if (yych <= '\r') goto yy26; + goto yy4; + } else { + if (yych <= 0x1B) goto yy26; + if (yych <= 0x1F) goto yy4; + goto yy26; + } + } + } else { + if (yych <= '+') { + if (yych <= '$') goto yy4; + if (yych <= '%') goto yy105; + if (yych <= '*') goto yy26; + goto yy80; + } else { + if (yych <= '-') { + if (yych <= ',') goto yy26; + goto yy255; + } else { + if (yych <= '.') goto yy256; + if (yych <= '/') goto yy4; + goto yy257; + } + } + } + } else { + if (yych <= '_') { + if (yych <= '@') { + if (yych <= '9') goto yy349; + if (yych <= ':') goto yy4; + if (yych <= '?') goto yy26; + goto yy96; + } else { + if (yych <= 'E') { + if (yych <= 'D') goto yy258; + goto yy259; + } else { + if (yych <= 'Z') goto yy258; + if (yych <= '^') goto yy26; + goto yy258; + } + } + } else { + if (yych <= 'z') { + if (yych <= '`') goto yy26; + if (yych == 'e') goto yy259; + goto yy258; + } else { + if (yych <= 0x7F) { + if (yych <= '~') goto yy26; + goto yy4; + } else { + if (yych <= 0xC1) goto yy26; + if (yych <= 0xF4) goto yy4; + goto yy26; + } + } + } + } +yy261: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= ':') { + if (yych <= '#') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy5; + if (yych <= 0x08) goto yy3; + if (yych <= '\n') goto yy5; + goto yy3; + } else { + if (yych <= 0x1A) { + if (yych <= '\r') goto yy5; + goto yy3; + } else { + if (yych <= 0x1B) goto yy5; + if (yych <= 0x1F) goto yy3; + goto yy5; + } + } + } else { + if (yych <= '+') { + if (yych <= '$') goto yy3; + if (yych <= '%') goto yy80; + if (yych <= '*') goto yy5; + goto yy80; + } else { + if (yych <= '.') { + if (yych <= ',') goto yy5; + goto yy46; + } else { + if (yych <= '/') goto yy3; + if (yych <= '9') goto yy353; + goto yy62; + } + } + } + } else { + if (yych <= 'z') { + if (yych <= 'Z') { + if (yych <= '?') goto yy5; + if (yych <= '@') goto yy96; + if (yych <= 'F') goto yy353; + goto yy46; + } else { + if (yych <= '_') { + if (yych <= '^') goto yy5; + goto yy46; + } else { + if (yych <= '`') goto yy5; + if (yych <= 'f') goto yy353; + goto yy46; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + if (yych <= '~') goto yy5; + goto yy3; + } else { + if (yych <= 0xC1) goto yy5; + if (yych <= 0xDF) goto yy56; + goto yy57; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) goto yy58; + goto yy59; + } else { + if (yych <= 0xF3) goto yy60; + if (yych <= 0xF4) goto yy61; + goto yy5; + } + } + } + } +yy262: + yyaccept = 6; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[768+yych] & 32) { + goto yy103; + } + if (yych <= '/') { + if (yych <= 0x1F) { + if (yych <= '\f') { + if (yych <= 0x00) goto yy26; + if (yych <= 0x08) goto yy4; + if (yych <= '\n') goto yy26; + goto yy4; + } else { + if (yych <= '\r') goto yy26; + if (yych == 0x1B) goto yy26; + goto yy4; + } + } else { + if (yych <= '*') { + if (yych <= '#') goto yy26; + if (yych <= '$') goto yy4; + if (yych <= '%') goto yy105; + goto yy26; + } else { + if (yych <= ',') { + if (yych <= '+') goto yy80; + goto yy26; + } else { + if (yych <= '-') goto yy255; + if (yych <= '.') goto yy256; + goto yy4; + } + } + } + } else { + if (yych <= '_') { + if (yych <= '@') { + if (yych <= '9') goto yy355; + if (yych <= ':') goto yy4; + if (yych <= '?') goto yy26; + goto yy96; + } else { + if (yych <= 'E') { + if (yych <= 'D') goto yy258; + goto yy356; + } else { + if (yych <= 'Z') goto yy258; + if (yych <= '^') goto yy26; + goto yy258; + } + } + } else { + if (yych <= 'z') { + if (yych <= '`') goto yy26; + if (yych == 'e') goto yy356; + goto yy258; + } else { + if (yych <= 0x7F) { + if (yych <= '~') goto yy26; + goto yy4; + } else { + if (yych <= 0xC1) goto yy26; + if (yych <= 0xF4) goto yy4; + goto yy26; + } + } + } + } +yy263: + yyaccept = 6; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[768+yych] & 32) { + goto yy103; + } + if (yych <= '/') { + if (yych <= 0x1F) { + if (yych <= '\f') { + if (yych <= 0x00) goto yy26; + if (yych <= 0x08) goto yy4; + if (yych <= '\n') goto yy26; + goto yy4; + } else { + if (yych <= '\r') goto yy26; + if (yych == 0x1B) goto yy26; + goto yy4; + } + } else { + if (yych <= '*') { + if (yych <= '#') goto yy26; + if (yych <= '$') goto yy4; + if (yych <= '%') goto yy105; + goto yy26; + } else { + if (yych <= ',') { + if (yych <= '+') goto yy80; + goto yy26; + } else { + if (yych <= '-') goto yy255; + if (yych <= '.') goto yy256; + goto yy4; + } + } + } + } else { + if (yych <= '_') { + if (yych <= '@') { + if (yych <= '9') goto yy262; + if (yych <= ':') goto yy4; + if (yych <= '?') goto yy26; + goto yy96; + } else { + if (yych <= 'E') { + if (yych <= 'D') goto yy258; + goto yy356; + } else { + if (yych <= 'Z') goto yy258; + if (yych <= '^') goto yy26; + goto yy258; + } + } + } else { + if (yych <= 'z') { + if (yych <= '`') goto yy26; + if (yych == 'e') goto yy356; + goto yy258; + } else { + if (yych <= 0x7F) { + if (yych <= '~') goto yy26; + goto yy4; + } else { + if (yych <= 0xC1) goto yy26; + if (yych <= 0xF4) goto yy4; + goto yy26; + } + } + } + } +yy264: + yyaccept = 6; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[768+yych] & 32) { + goto yy103; + } + if (yych <= '4') { + if (yych <= '#') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy26; + if (yych <= 0x08) goto yy4; + if (yych <= '\n') goto yy26; + goto yy4; + } else { + if (yych <= 0x1A) { + if (yych <= '\r') goto yy26; + goto yy4; + } else { + if (yych <= 0x1B) goto yy26; + if (yych <= 0x1F) goto yy4; + goto yy26; + } + } + } else { + if (yych <= '+') { + if (yych <= '$') goto yy4; + if (yych <= '%') goto yy105; + if (yych <= '*') goto yy26; + goto yy80; + } else { + if (yych <= '-') { + if (yych <= ',') goto yy26; + goto yy255; + } else { + if (yych <= '.') goto yy256; + if (yych <= '/') goto yy4; + goto yy262; + } + } + } + } else { + if (yych <= '^') { + if (yych <= '?') { + if (yych <= '5') goto yy357; + if (yych <= '9') goto yy355; + if (yych <= ':') goto yy4; + goto yy26; + } else { + if (yych <= 'D') { + if (yych <= '@') goto yy96; + goto yy258; + } else { + if (yych <= 'E') goto yy356; + if (yych <= 'Z') goto yy258; + goto yy26; + } + } + } else { + if (yych <= 'z') { + if (yych <= '`') { + if (yych <= '_') goto yy258; + goto yy26; + } else { + if (yych == 'e') goto yy356; + goto yy258; + } + } else { + if (yych <= 0x7F) { + if (yych <= '~') goto yy26; + goto yy4; + } else { + if (yych <= 0xC1) goto yy26; + if (yych <= 0xF4) goto yy4; + goto yy26; + } + } + } + } +yy265: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '@') goto yy4; + if (yych <= 'Z') goto yy358; + if (yych <= '`') goto yy4; + if (yych <= 'z') goto yy358; + goto yy4; +yy266: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '9') { + if (yych <= '+') { + if (yych == '%') goto yy80; + if (yych <= '*') goto yy4; + goto yy80; + } else { + if (yych <= ',') goto yy4; + if (yych <= '.') goto yy46; + if (yych <= '/') goto yy4; + goto yy359; + } + } else { + if (yych <= '^') { + if (yych <= '?') goto yy4; + if (yych <= '@') goto yy96; + if (yych <= 'Z') goto yy46; + goto yy4; + } else { + if (yych == '`') goto yy4; + if (yych <= 'z') goto yy46; + goto yy4; + } + } +yy267: + yyaccept = 11; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '7') { + if (yych <= ' ') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy109; + if (yych <= 0x08) goto yy4; + if (yych <= '\n') goto yy109; + goto yy4; + } else { + if (yych <= 0x1A) { + if (yych <= '\r') goto yy109; + goto yy4; + } else { + if (yych <= 0x1B) goto yy109; + if (yych <= 0x1F) goto yy4; + goto yy360; + } + } + } else { + if (yych <= '+') { + if (yych <= '$') { + if (yych <= '#') goto yy109; + goto yy4; + } else { + if (yych <= '%') goto yy105; + if (yych <= '*') goto yy109; + goto yy80; + } + } else { + if (yych <= '-') { + if (yych <= ',') goto yy109; + goto yy361; + } else { + if (yych <= '.') goto yy266; + if (yych <= '/') goto yy362; + goto yy363; + } + } + } + } else { + if (yych <= '_') { + if (yych <= 'D') { + if (yych <= ':') { + if (yych <= '9') goto yy364; + goto yy365; + } else { + if (yych <= '?') goto yy109; + if (yych <= '@') goto yy96; + goto yy366; + } + } else { + if (yych <= 'F') { + if (yych <= 'E') goto yy367; + goto yy366; + } else { + if (yych <= 'Z') goto yy115; + if (yych <= '^') goto yy109; + goto yy46; + } + } + } else { + if (yych <= 'z') { + if (yych <= 'd') { + if (yych <= '`') goto yy109; + goto yy366; + } else { + if (yych <= 'e') goto yy367; + if (yych <= 'f') goto yy366; + goto yy115; + } + } else { + if (yych <= 0x7F) { + if (yych <= '~') goto yy109; + goto yy4; + } else { + if (yych <= 0xC1) goto yy109; + if (yych <= 0xF4) goto yy4; + goto yy109; + } + } + } + } +yy268: + yyaccept = 6; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '9') { + if (yych <= ' ') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy26; + if (yych <= 0x08) goto yy4; + if (yych <= '\n') goto yy26; + goto yy4; + } else { + if (yych <= 0x1A) { + if (yych <= '\r') goto yy26; + goto yy4; + } else { + if (yych <= 0x1B) goto yy26; + if (yych <= 0x1F) goto yy4; + goto yy360; + } + } + } else { + if (yych <= '+') { + if (yych <= '$') { + if (yych <= '#') goto yy26; + goto yy4; + } else { + if (yych <= '%') goto yy105; + if (yych <= '*') goto yy26; + goto yy80; + } + } else { + if (yych <= '-') { + if (yych <= ',') goto yy26; + goto yy361; + } else { + if (yych <= '.') goto yy266; + if (yych <= '/') goto yy362; + goto yy364; + } + } + } + } else { + if (yych <= '_') { + if (yych <= 'D') { + if (yych <= ':') goto yy365; + if (yych <= '?') goto yy26; + if (yych <= '@') goto yy96; + goto yy366; + } else { + if (yych <= 'F') { + if (yych <= 'E') goto yy367; + goto yy366; + } else { + if (yych <= 'Z') goto yy115; + if (yych <= '^') goto yy26; + goto yy46; + } + } + } else { + if (yych <= 'z') { + if (yych <= 'd') { + if (yych <= '`') goto yy26; + goto yy366; + } else { + if (yych <= 'e') goto yy367; + if (yych <= 'f') goto yy366; + goto yy115; + } + } else { + if (yych <= 0x7F) { + if (yych <= '~') goto yy26; + goto yy4; + } else { + if (yych <= 0xC1) goto yy26; + if (yych <= 0xF4) goto yy4; + goto yy26; + } + } + } + } +yy269: + yyaccept = 12; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '/') { + if (yych <= 0x1F) { + if (yych <= '\f') { + if (yych <= 0x00) goto yy113; + if (yych <= 0x08) goto yy4; + if (yych <= '\n') goto yy113; + goto yy4; + } else { + if (yych <= '\r') goto yy113; + if (yych == 0x1B) goto yy113; + goto yy4; + } + } else { + if (yych <= '*') { + if (yych <= '#') goto yy113; + if (yych <= '$') goto yy4; + if (yych <= '%') goto yy80; + goto yy113; + } else { + if (yych <= '+') goto yy80; + if (yych <= ',') goto yy113; + if (yych <= '.') goto yy46; + goto yy4; + } + } + } else { + if (yych <= '_') { + if (yych <= '@') { + if (yych <= '9') goto yy366; + if (yych <= ':') goto yy365; + if (yych <= '?') goto yy113; + goto yy96; + } else { + if (yych <= 'F') goto yy366; + if (yych <= 'Z') goto yy115; + if (yych <= '^') goto yy113; + goto yy46; + } + } else { + if (yych <= '~') { + if (yych <= '`') goto yy113; + if (yych <= 'f') goto yy366; + if (yych <= 'z') goto yy115; + goto yy113; + } else { + if (yych <= 0x7F) goto yy4; + if (yych <= 0xC1) goto yy113; + if (yych <= 0xF4) goto yy4; + goto yy113; + } + } + } +yy270: + yyaccept = 12; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '.') { + if (yych <= 0x1F) { + if (yych <= '\f') { + if (yych <= 0x00) goto yy113; + if (yych <= 0x08) goto yy4; + if (yych <= '\n') goto yy113; + goto yy4; + } else { + if (yych <= '\r') goto yy113; + if (yych == 0x1B) goto yy113; + goto yy4; + } + } else { + if (yych <= '*') { + if (yych <= '#') goto yy113; + if (yych <= '$') goto yy4; + if (yych <= '%') goto yy80; + goto yy113; + } else { + if (yych <= '+') goto yy194; + if (yych <= ',') goto yy113; + if (yych <= '-') goto yy251; + goto yy46; + } + } + } else { + if (yych <= '^') { + if (yych <= '?') { + if (yych <= '/') goto yy4; + if (yych <= '9') goto yy366; + if (yych <= ':') goto yy365; + goto yy113; + } else { + if (yych <= '@') goto yy96; + if (yych <= 'F') goto yy366; + if (yych <= 'Z') goto yy115; + goto yy113; + } + } else { + if (yych <= 'z') { + if (yych <= '_') goto yy46; + if (yych <= '`') goto yy113; + if (yych <= 'f') goto yy366; + goto yy115; + } else { + if (yych <= 0x7F) { + if (yych <= '~') goto yy113; + goto yy4; + } else { + if (yych <= 0xC1) goto yy113; + if (yych <= 0xF4) goto yy4; + goto yy113; + } + } + } + } +yy271: + yych = *++YYCURSOR; + if (yych <= '@') { + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy368; + if (yych <= ':') goto yy282; + goto yy1; + } else { + if (yych <= 'F') goto yy369; + if (yych <= '`') goto yy1; + if (yych <= 'f') goto yy369; + goto yy1; + } +yy272: + yych = *++YYCURSOR; + if (yych <= '@') { + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy369; + if (yych <= ':') goto yy282; + goto yy1; + } else { + if (yych <= 'F') goto yy369; + if (yych <= '`') goto yy1; + if (yych <= 'f') goto yy369; + goto yy1; + } +yy273: + yych = *++YYCURSOR; +yy274: + if (yybm[256+yych] & 1) { + goto yy273; + } + if (yych <= 0xC1) { + if (yych <= ')') { + if (yych <= '"') goto yy1; + if (yych <= '&') goto yy370; + goto yy1; + } else { + if (yych <= 'Z') goto yy370; + if (yych <= ']') goto yy1; + if (yych <= 'z') goto yy370; + goto yy1; + } + } else { + if (yych <= 0xEF) { + if (yych <= 0xDF) goto yy275; + if (yych <= 0xE0) goto yy276; + goto yy277; + } else { + if (yych <= 0xF0) goto yy278; + if (yych <= 0xF3) goto yy279; + if (yych <= 0xF4) goto yy280; + goto yy1; + } + } +yy275: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy273; + goto yy1; +yy276: + yych = *++YYCURSOR; + if (yych <= 0x9F) goto yy1; + if (yych <= 0xBF) goto yy275; + goto yy1; +yy277: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy275; + goto yy1; +yy278: + yych = *++YYCURSOR; + if (yych <= 0x8F) goto yy1; + if (yych <= 0xBF) goto yy277; + goto yy1; +yy279: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy277; + goto yy1; +yy280: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0x8F) goto yy277; + goto yy1; +yy281: + yych = *++YYCURSOR; + if (yych <= '@') { + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy372; + if (yych <= ':') goto yy373; + goto yy1; + } else { + if (yych <= 'F') goto yy372; + if (yych <= '`') goto yy1; + if (yych <= 'f') goto yy372; + goto yy1; + } +yy282: + yych = *++YYCURSOR; + if (yych <= '@') { + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy374; + if (yych <= ':') goto yy375; + goto yy1; + } else { + if (yych <= 'F') goto yy374; + if (yych <= '`') goto yy1; + if (yych <= 'f') goto yy374; + goto yy1; + } +yy283: + yych = *++YYCURSOR; + if (yych <= '@') { + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy372; + if (yych <= ':') goto yy282; + goto yy1; + } else { + if (yych <= 'F') goto yy372; + if (yych <= '`') goto yy1; + if (yych <= 'f') goto yy372; + goto yy1; + } +yy284: + yyaccept = 17; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= ',') { + if (yych <= '\r') { + if (yych <= 0x08) { + if (yych <= 0x00) goto yy199; + goto yy4; + } else { + if (yych <= '\n') goto yy199; + if (yych <= '\f') goto yy4; + goto yy199; + } + } else { + if (yych <= 0x1F) { + if (yych == 0x1B) goto yy199; + goto yy4; + } else { + if (yych == '$') goto yy4; + goto yy199; + } + } + } else { + if (yych <= '`') { + if (yych <= 'Z') { + if (yych <= ':') goto yy4; + if (yych <= '?') goto yy199; + goto yy4; + } else { + if (yych == '_') goto yy4; + goto yy199; + } + } else { + if (yych <= 0x7F) { + if (yych <= 'z') goto yy4; + if (yych <= '~') goto yy199; + goto yy4; + } else { + if (yych <= 0xC1) goto yy199; + if (yych <= 0xF4) goto yy4; + goto yy199; + } + } + } +yy285: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '?') { + if (yych <= 0x1F) { + if (yych <= '\f') { + if (yych <= 0x00) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0x08) { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= '\n') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= '\r') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych == 0x1B) { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } + } else { + if (yych <= '-') { + if (yych == '$') { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= ',') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= '/') { + if (yych <= '.') { + yyt2 = YYCURSOR; + goto yy376; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= '9') goto yy377; + if (yych <= ':') goto yy378; + yyt2 = YYCURSOR; + goto yy198; + } + } + } + } else { + if (yych <= '~') { + if (yych <= '^') { + if (yych <= '@') { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= 'F') goto yy379; + if (yych <= 'Z') goto yy3; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= '`') { + if (yych <= '_') { + yyt2 = YYCURSOR; + goto yy284; + } + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 'f') goto yy379; + if (yych <= 'z') goto yy3; + yyt2 = YYCURSOR; + goto yy198; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= 0xC1) goto yy5; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy289; + } + yyt2 = YYCURSOR; + goto yy290; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy291; + } + yyt2 = YYCURSOR; + goto yy292; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy293; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy294; + } + goto yy5; + } + } + } + } +yy286: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '?') { + if (yych <= 0x1F) { + if (yych <= '\f') { + if (yych <= 0x00) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0x08) { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= '\n') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= '\r') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych == 0x1B) { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } + } else { + if (yych <= '-') { + if (yych == '$') { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= ',') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= '/') { + if (yych <= '.') { + yyt2 = YYCURSOR; + goto yy376; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= '9') goto yy380; + if (yych <= ':') goto yy378; + yyt2 = YYCURSOR; + goto yy198; + } + } + } + } else { + if (yych <= '~') { + if (yych <= '^') { + if (yych <= '@') { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= 'F') goto yy379; + if (yych <= 'Z') goto yy3; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= '`') { + if (yych <= '_') { + yyt2 = YYCURSOR; + goto yy284; + } + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 'f') goto yy379; + if (yych <= 'z') goto yy3; + yyt2 = YYCURSOR; + goto yy198; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= 0xC1) goto yy5; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy289; + } + yyt2 = YYCURSOR; + goto yy290; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy291; + } + yyt2 = YYCURSOR; + goto yy292; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy293; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy294; + } + goto yy5; + } + } + } + } +yy287: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= ':') { + if (yych <= '#') { + if (yych <= '\f') { + if (yych <= 0x00) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0x08) { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= '\n') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= 0x1A) { + if (yych <= '\r') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= 0x1B) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0x1F) { + yyt2 = YYCURSOR; + goto yy284; + } + yyt2 = YYCURSOR; + goto yy198; + } + } + } else { + if (yych <= '.') { + if (yych <= '$') { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= ',') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '-') { + yyt2 = YYCURSOR; + goto yy284; + } + yyt2 = YYCURSOR; + goto yy376; + } else { + if (yych <= '4') { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy284; + } + goto yy380; + } else { + if (yych <= '5') goto yy381; + if (yych <= '9') goto yy377; + goto yy378; + } + } + } + } else { + if (yych <= 'z') { + if (yych <= 'Z') { + if (yych <= '?') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '@') { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= 'F') goto yy379; + goto yy3; + } else { + if (yych <= '_') { + if (yych <= '^') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 'f') goto yy379; + goto yy3; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + if (yych <= '~') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= 0xC1) goto yy5; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy289; + } + yyt2 = YYCURSOR; + goto yy290; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy291; + } + yyt2 = YYCURSOR; + goto yy292; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy293; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy294; + } + goto yy5; + } + } + } + } +yy288: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '@') { + if (yych <= 0x1F) { + if (yych <= '\f') { + if (yych <= 0x00) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0x08) { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= '\n') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= '\r') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych == 0x1B) { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } + } else { + if (yych <= '/') { + if (yych == '$') { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= ',') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= '9') goto yy379; + if (yych <= ':') goto yy378; + if (yych <= '?') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } + } + } else { + if (yych <= '~') { + if (yych <= '_') { + if (yych <= 'F') goto yy379; + if (yych <= 'Z') goto yy3; + if (yych <= '^') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 'f') goto yy379; + if (yych <= 'z') goto yy3; + yyt2 = YYCURSOR; + goto yy198; + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= 0xC1) goto yy5; + if (yych >= 0xE0) { + yyt2 = YYCURSOR; + goto yy290; + } + yyt2 = YYCURSOR; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy291; + } + yyt2 = YYCURSOR; + goto yy292; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy293; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy294; + } + goto yy5; + } + } + } + } +yy289: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy284; + goto yy1; +yy290: + yych = *++YYCURSOR; + if (yych <= 0x9F) goto yy1; + if (yych <= 0xBF) goto yy289; + goto yy1; +yy291: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy289; + goto yy1; +yy292: + yych = *++YYCURSOR; + if (yych <= 0x8F) goto yy1; + if (yych <= 0xBF) goto yy291; + goto yy1; +yy293: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy291; + goto yy1; +yy294: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0x8F) goto yy291; + goto yy1; +yy295: + yyaccept = 6; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '9') { + if (yych <= '+') { + if (yych == '%') goto yy80; + if (yych <= '*') goto yy26; + goto yy80; + } else { + if (yych <= ',') goto yy26; + if (yych <= '.') goto yy80; + if (yych <= '/') goto yy26; + goto yy295; + } + } else { + if (yych <= '^') { + if (yych <= '?') goto yy26; + if (yych <= '@') goto yy82; + if (yych <= 'Z') goto yy80; + goto yy26; + } else { + if (yych == '`') goto yy26; + if (yych <= 'z') goto yy80; + goto yy26; + } + } +yy296: + yyaccept = 6; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '/') { + if (yych <= 0x1F) { + if (yych <= '\f') { + if (yych <= 0x00) goto yy26; + if (yych <= 0x08) goto yy4; + if (yych <= '\n') goto yy26; + goto yy4; + } else { + if (yych <= '\r') goto yy26; + if (yych == 0x1B) goto yy26; + goto yy4; + } + } else { + if (yych <= '*') { + if (yych <= '#') goto yy26; + if (yych <= '$') goto yy4; + if (yych <= '%') goto yy80; + goto yy26; + } else { + if (yych <= '+') goto yy80; + if (yych <= ',') goto yy26; + if (yych <= '.') goto yy46; + goto yy4; + } + } + } else { + if (yych <= '_') { + if (yych <= '@') { + if (yych <= '9') goto yy382; + if (yych <= ':') goto yy4; + if (yych <= '?') goto yy26; + goto yy96; + } else { + if (yych <= 'F') goto yy353; + if (yych <= 'Z') goto yy46; + if (yych <= '^') goto yy26; + goto yy46; + } + } else { + if (yych <= '~') { + if (yych <= '`') goto yy26; + if (yych <= 'f') goto yy353; + if (yych <= 'z') goto yy46; + goto yy26; + } else { + if (yych <= 0x7F) goto yy4; + if (yych <= 0xC1) goto yy26; + if (yych <= 0xF4) goto yy4; + goto yy26; + } + } + } +yy297: + yyaccept = 18; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '1') { + if (yych <= '/') { + yyt2 = yyt1; + goto yy199; + } + if (yych <= '0') goto yy383; + goto yy384; + } else { + if (yych <= '2') goto yy385; + if (yych <= '9') goto yy383; + yyt2 = yyt1; + goto yy199; + } +yy298: + yych = *++YYCURSOR; + if (yych <= 'f') { + if (yych <= ':') { + if (yych <= '.') { + if (yych <= '-') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt1 = YYCURSOR; + goto yy297; + } else { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy386; + } + } else { + if (yych <= 'F') { + if (yych <= '@') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy386; + } else { + if (yych <= 'Z') goto yy1; + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy386; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + if (yych <= 'z') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + yyt2 = YYCURSOR; + goto yy206; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy299: + yych = *++YYCURSOR; + if (yych <= '@') { + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy387; + goto yy1; + } else { + if (yych <= 'F') goto yy387; + if (yych <= '`') goto yy1; + if (yych <= 'f') goto yy387; + goto yy1; + } +yy300: + yych = *++YYCURSOR; + if (yych <= 'z') { + if (yych <= '@') { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy386; + if (yych <= ':') goto yy299; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 'Z') { + if (yych <= 'F') goto yy386; + goto yy1; + } else { + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 'f') goto yy386; + goto yy1; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + yyt2 = YYCURSOR; + goto yy206; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy301: + yych = *++YYCURSOR; + if (yych <= 'f') { + if (yych <= ':') { + if (yych <= '.') { + if (yych <= '-') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt1 = YYCURSOR; + goto yy297; + } else { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy388; + goto yy299; + } + } else { + if (yych <= 'F') { + if (yych <= '@') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy386; + } else { + if (yych <= 'Z') goto yy1; + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy386; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + if (yych <= 'z') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + yyt2 = YYCURSOR; + goto yy206; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy302: + yych = *++YYCURSOR; + if (yych <= '`') { + if (yych <= '9') { + if (yych <= '.') { + if (yych <= '-') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt1 = YYCURSOR; + goto yy297; + } else { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '5') goto yy388; + goto yy386; + } + } else { + if (yych <= '@') { + if (yych <= ':') goto yy299; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 'F') goto yy386; + if (yych <= 'Z') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } + } + } else { + if (yych <= 0xDF) { + if (yych <= 'z') { + if (yych <= 'f') goto yy386; + goto yy1; + } else { + if (yych <= 0x7F) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0xC1) goto yy1; + yyt2 = YYCURSOR; + goto yy205; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xE0) { + yyt2 = YYCURSOR; + goto yy206; + } + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy303: + yych = *++YYCURSOR; + if (yych <= 'f') { + if (yych <= 'E') { + if (yych <= '9') { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy386; + } else { + if (yych <= ':') goto yy299; + if (yych <= '@') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy386; + } + } else { + if (yych <= 'Z') { + if (yych <= 'F') goto yy389; + goto yy1; + } else { + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 'e') goto yy386; + goto yy389; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + if (yych <= 'z') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + yyt2 = YYCURSOR; + goto yy206; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy304: + yych = *++YYCURSOR; +yy305: + if (yych <= ',') { + if (yych <= 0x1F) { + if (yych <= '\t') { + if (yych <= 0x08) goto yy1; + goto yy304; + } else { + if (yych == '\r') goto yy304; + goto yy1; + } + } else { + if (yych <= '"') { + if (yych <= ' ') goto yy304; + if (yych <= '!') goto yy1; + } else { + if (yych == '\'') goto yy307; + goto yy1; + } + } + } else { + if (yych <= '@') { + if (yych <= ':') { + if (yych <= '-') goto yy390; + if (yych <= '/') goto yy1; + goto yy390; + } else { + if (yych == '>') goto yy309; + goto yy1; + } + } else { + if (yych <= '_') { + if (yych <= 'Z') goto yy390; + if (yych <= '^') goto yy1; + goto yy390; + } else { + if (yych <= '`') goto yy1; + if (yych <= 'z') goto yy390; + goto yy1; + } + } + } +yy306: + yych = *++YYCURSOR; + if (yybm[256+yych] & 8) { + goto yy391; + } + if (yych <= 0xE0) { + if (yych <= '\\') { + if (yych <= '"') goto yy1; + goto yy392; + } else { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) goto yy393; + goto yy394; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) goto yy395; + goto yy396; + } else { + if (yych <= 0xF3) goto yy397; + if (yych <= 0xF4) goto yy398; + goto yy1; + } + } +yy307: + yych = *++YYCURSOR; + if (yybm[256+yych] & 16) { + goto yy399; + } + if (yych <= 0xE0) { + if (yych <= '\\') { + if (yych <= '\'') goto yy1; + goto yy400; + } else { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) goto yy401; + goto yy402; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) goto yy403; + goto yy404; + } else { + if (yych <= 0xF3) goto yy405; + if (yych <= 0xF4) goto yy406; + goto yy1; + } + } +yy308: + yych = *++YYCURSOR; + if (yych <= '-') { + if (yych <= ' ') { + if (yych <= '\f') { + if (yych == '\t') goto yy407; + goto yy1; + } else { + if (yych <= '\r') goto yy407; + if (yych <= 0x1F) goto yy1; + goto yy407; + } + } else { + if (yych <= '&') { + if (yych == '"') goto yy306; + goto yy1; + } else { + if (yych <= '\'') goto yy307; + if (yych <= ',') goto yy1; + goto yy308; + } + } + } else { + if (yych <= '@') { + if (yych <= '<') { + if (yych <= '/') goto yy1; + if (yych <= ':') goto yy308; + goto yy1; + } else { + if (yych <= '=') goto yy408; + if (yych >= '?') goto yy1; + } + } else { + if (yych <= '_') { + if (yych <= 'Z') goto yy308; + if (yych <= '^') goto yy1; + goto yy308; + } else { + if (yych <= '`') goto yy1; + if (yych <= 'z') goto yy308; + goto yy1; + } + } + } +yy309: + ++YYCURSOR; +yy310: +#line 192 "../../lnav/src/data_scanner_re.re" + { + RET(DT_XML_DECL_TAG); + } +#line 10017 "../../lnav/src/data_scanner_re.cc" +yy311: + yych = *++YYCURSOR; + if (yych <= ':') { + if (yych <= 0x1F) { + if (yych <= '\t') { + if (yych <= 0x08) goto yy1; + goto yy409; + } else { + if (yych == '\r') goto yy409; + goto yy1; + } + } else { + if (yych <= '-') { + if (yych <= ' ') goto yy409; + if (yych <= ',') goto yy1; + goto yy311; + } else { + if (yych <= '.') goto yy1; + if (yych <= '/') goto yy215; + goto yy311; + } + } + } else { + if (yych <= '@') { + if (yych <= '=') { + if (yych <= '<') goto yy1; + goto yy313; + } else { + if (yych <= '>') goto yy216; + if (yych <= '?') goto yy215; + goto yy1; + } + } else { + if (yych <= '_') { + if (yych <= 'Z') goto yy311; + if (yych <= '^') goto yy1; + goto yy311; + } else { + if (yych <= '`') goto yy1; + if (yych <= 'z') goto yy311; + goto yy1; + } + } + } +yy312: + yych = *++YYCURSOR; + if (yych <= ':') { + if (yych <= 0x1F) { + if (yych <= '\t') { + if (yych <= 0x08) goto yy1; + goto yy312; + } else { + if (yych == '\r') goto yy312; + goto yy1; + } + } else { + if (yych <= '-') { + if (yych <= ' ') goto yy312; + if (yych <= ',') goto yy1; + goto yy311; + } else { + if (yych <= '.') goto yy1; + if (yych <= '/') goto yy215; + goto yy311; + } + } + } else { + if (yych <= '@') { + if (yych <= '=') { + if (yych <= '<') goto yy1; + } else { + if (yych <= '>') goto yy216; + if (yych <= '?') goto yy215; + goto yy1; + } + } else { + if (yych <= '_') { + if (yych <= 'Z') goto yy311; + if (yych <= '^') goto yy1; + goto yy311; + } else { + if (yych <= '`') goto yy1; + if (yych <= 'z') goto yy311; + goto yy1; + } + } + } +yy313: + yych = *++YYCURSOR; + if (yych <= '\'') { + if (yych <= '\r') { + if (yych <= 0x08) { + if (yych <= 0x00) goto yy1; + goto yy410; + } else { + if (yych <= '\t') goto yy411; + if (yych <= '\f') goto yy410; + goto yy411; + } + } else { + if (yych <= '!') { + if (yych == ' ') goto yy411; + goto yy410; + } else { + if (yych <= '"') goto yy412; + if (yych <= '&') goto yy410; + goto yy413; + } + } + } else { + if (yych <= 0xDF) { + if (yych <= '>') { + if (yych <= '=') goto yy410; + goto yy1; + } else { + if (yych <= 0x7F) goto yy410; + if (yych <= 0xC1) goto yy1; + goto yy414; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xE0) goto yy415; + if (yych <= 0xEF) goto yy416; + goto yy417; + } else { + if (yych <= 0xF3) goto yy418; + if (yych <= 0xF4) goto yy419; + goto yy1; + } + } + } +yy314: + ++YYCURSOR; +yy315: +#line 196 "../../lnav/src/data_scanner_re.re" + { + RET(DT_XML_EMPTY_TAG); + } +#line 10156 "../../lnav/src/data_scanner_re.cc" +yy316: + yych = *++YYCURSOR; + if (yych <= '\r') { + if (yych == '\t') goto yy316; + if (yych <= '\f') goto yy1; + goto yy316; + } else { + if (yych <= ' ') { + if (yych <= 0x1F) goto yy1; + goto yy316; + } else { + if (yych != '>') goto yy1; + } + } +yy317: + ++YYCURSOR; +#line 204 "../../lnav/src/data_scanner_re.re" + { + RET(DT_XML_CLOSE_TAG); + } +#line 10177 "../../lnav/src/data_scanner_re.cc" +yy318: + yych = *++YYCURSOR; +yy319: + if (yych <= '/') { + if (yych <= 0x1F) { + if (yych <= '\t') { + if (yych <= 0x08) goto yy1; + goto yy318; + } else { + if (yych == '\r') goto yy318; + goto yy1; + } + } else { + if (yych <= ',') { + if (yych <= ' ') goto yy318; + goto yy1; + } else { + if (yych <= '-') goto yy420; + if (yych <= '.') goto yy1; + goto yy215; + } + } + } else { + if (yych <= 'Z') { + if (yych <= '>') { + if (yych <= ':') goto yy420; + goto yy1; + } else { + if (yych <= '?') goto yy215; + if (yych <= '@') goto yy1; + goto yy420; + } + } else { + if (yych <= '_') { + if (yych <= '^') goto yy1; + goto yy420; + } else { + if (yych <= '`') goto yy1; + if (yych <= 'z') goto yy420; + goto yy1; + } + } + } +yy320: + yych = *++YYCURSOR; + if (yych <= ':') { + if (yych <= 0x1F) { + if (yych <= '\t') { + if (yych <= 0x08) goto yy1; + goto yy421; + } else { + if (yych == '\r') goto yy421; + goto yy1; + } + } else { + if (yych <= '-') { + if (yych <= ' ') goto yy421; + if (yych <= ',') goto yy1; + goto yy320; + } else { + if (yych <= '.') goto yy1; + if (yych <= '/') goto yy215; + goto yy320; + } + } + } else { + if (yych <= '@') { + if (yych <= '=') { + if (yych <= '<') goto yy1; + goto yy422; + } else { + if (yych == '?') goto yy215; + goto yy1; + } + } else { + if (yych <= '_') { + if (yych <= 'Z') goto yy320; + if (yych <= '^') goto yy1; + goto yy320; + } else { + if (yych <= '`') goto yy1; + if (yych <= 'z') goto yy320; + goto yy1; + } + } + } +yy321: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= ':') { + if (yych <= '+') { + if (yych == '%') goto yy80; + if (yych <= '*') goto yy4; + goto yy80; + } else { + if (yych <= '.') { + if (yych <= ',') goto yy4; + goto yy46; + } else { + if (yych <= '/') goto yy4; + if (yych <= '9') goto yy423; + goto yy365; + } + } + } else { + if (yych <= '^') { + if (yych <= '@') { + if (yych <= '?') goto yy4; + goto yy96; + } else { + if (yych <= 'F') goto yy423; + if (yych <= 'Z') goto yy115; + goto yy4; + } + } else { + if (yych <= '`') { + if (yych <= '_') goto yy46; + goto yy4; + } else { + if (yych <= 'f') goto yy423; + if (yych <= 'z') goto yy115; + goto yy4; + } + } + } +yy322: + yych = *++YYCURSOR; + if (yych == ' ') goto yy424; + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy424; + goto yy1; +yy323: + yyaccept = 15; + yych = *(YYMARKER = ++YYCURSOR); + if (yych == ' ') goto yy424; + if (yych <= '/') goto yy222; + if (yych <= '9') goto yy424; + goto yy222; +yy324: + yyaccept = 15; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= ':') { + if (yych <= '.') goto yy222; + if (yych <= '/') goto yy189; + if (yych <= '9') goto yy192; + goto yy191; + } else { + if (yych <= 'F') { + if (yych <= '@') goto yy222; + goto yy192; + } else { + if (yych <= '`') goto yy222; + if (yych <= 'f') goto yy192; + goto yy222; + } + } +yy325: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= ',') { + if (yych <= '!') { + if (yych <= '\f') { + if (yych <= 0x00) { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych <= 0x08) goto yy4; + if (yych <= '\n') { + yyt4 = YYCURSOR; + goto yy221; + } + goto yy4; + } else { + if (yych <= '\r') { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych <= 0x1F) goto yy4; + if (yych <= ' ') { + yyt4 = YYCURSOR; + goto yy221; + } + yyt4 = YYCURSOR; + goto yy223; + } + } else { + if (yych <= '&') { + if (yych <= '"') { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych == '%') goto yy80; + goto yy4; + } else { + if (yych <= '\'') { + yyt4 = YYCURSOR; + goto yy224; + } + if (yych <= '*') { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych <= '+') goto yy80; + yyt4 = YYCURSOR; + goto yy223; + } + } + } else { + if (yych <= '?') { + if (yych <= '9') { + if (yych <= '-') goto yy46; + if (yych <= '.') { + yyt4 = YYCURSOR; + goto yy230; + } + if (yych <= '/') goto yy4; + goto yy423; + } else { + if (yych <= ':') { + yyt3 = YYCURSOR; + goto yy425; + } + if (yych <= ';') { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych <= '>') goto yy4; + yyt4 = YYCURSOR; + goto yy223; + } + } else { + if (yych <= '^') { + if (yych <= '@') goto yy96; + if (yych <= 'F') goto yy423; + if (yych <= 'Z') goto yy115; + goto yy4; + } else { + if (yych <= '`') { + if (yych <= '_') goto yy46; + goto yy4; + } else { + if (yych <= 'f') goto yy426; + if (yych >= '{') goto yy4; + } + } + } + } +yy326: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '.') { + if (yych <= '!') { + if (yych <= '\r') { + if (yych <= 0x08) { + if (yych <= 0x00) { + yyt4 = YYCURSOR; + goto yy221; + } + goto yy3; + } else { + if (yych <= '\n') { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych <= '\f') goto yy3; + yyt4 = YYCURSOR; + goto yy221; + } + } else { + if (yych <= 0x1B) { + if (yych <= 0x1A) goto yy3; + goto yy5; + } else { + if (yych <= 0x1F) goto yy3; + if (yych <= ' ') { + yyt4 = YYCURSOR; + goto yy221; + } + yyt4 = YYCURSOR; + goto yy223; + } + } + } else { + if (yych <= '&') { + if (yych <= '#') { + if (yych <= '"') { + yyt4 = YYCURSOR; + goto yy221; + } + goto yy5; + } else { + if (yych <= '$') goto yy3; + if (yych <= '%') goto yy80; + goto yy5; + } + } else { + if (yych <= '+') { + if (yych <= '\'') { + yyt4 = YYCURSOR; + goto yy224; + } + if (yych <= '*') { + yyt4 = YYCURSOR; + goto yy221; + } + goto yy80; + } else { + if (yych <= ',') { + yyt4 = YYCURSOR; + goto yy223; + } + if (yych <= '-') goto yy46; + yyt4 = YYCURSOR; + goto yy230; + } + } + } + } else { + if (yych <= '`') { + if (yych <= '>') { + if (yych <= '9') { + if (yych <= '/') goto yy3; + goto yy115; + } else { + if (yych <= ':') { + yyt3 = YYCURSOR; + goto yy234; + } + if (yych <= ';') { + yyt4 = YYCURSOR; + goto yy221; + } + goto yy5; + } + } else { + if (yych <= 'Z') { + if (yych <= '?') { + yyt4 = YYCURSOR; + goto yy223; + } + if (yych <= '@') goto yy96; + goto yy115; + } else { + if (yych == '_') goto yy46; + goto yy5; + } + } + } else { + if (yych <= 0xDF) { + if (yych <= '~') { + if (yych <= 'z') goto yy326; + goto yy5; + } else { + if (yych <= 0x7F) goto yy3; + if (yych <= 0xC1) goto yy5; + goto yy56; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xE0) goto yy57; + if (yych <= 0xEF) goto yy58; + goto yy59; + } else { + if (yych <= 0xF3) goto yy60; + if (yych <= 0xF4) goto yy61; + goto yy5; + } + } + } + } +yy327: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= ':') { + if (yych <= '+') { + if (yych == '%') goto yy80; + if (yych <= '*') goto yy4; + goto yy80; + } else { + if (yych <= '.') { + if (yych <= ',') goto yy4; + goto yy46; + } else { + if (yych <= '/') goto yy4; + if (yych <= '9') goto yy115; + goto yy148; + } + } + } else { + if (yych <= 'Z') { + if (yych <= '@') { + if (yych <= '?') goto yy4; + goto yy96; + } else { + if (yych == 'E') goto yy329; + goto yy115; + } + } else { + if (yych <= '_') { + if (yych <= '^') goto yy4; + goto yy46; + } else { + if (yych <= '`') goto yy4; + if (yych <= 'z') goto yy115; + goto yy4; + } + } + } +yy328: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= ',') { + if (yych <= '!') { + if (yych <= '\f') { + if (yych <= 0x00) { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych <= 0x08) goto yy4; + if (yych <= '\n') { + yyt4 = YYCURSOR; + goto yy221; + } + goto yy4; + } else { + if (yych <= '\r') { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych <= 0x1F) goto yy4; + if (yych <= ' ') { + yyt4 = YYCURSOR; + goto yy221; + } + yyt4 = YYCURSOR; + goto yy223; + } + } else { + if (yych <= '&') { + if (yych <= '"') { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych == '%') goto yy80; + goto yy4; + } else { + if (yych <= '\'') { + yyt4 = YYCURSOR; + goto yy224; + } + if (yych <= '*') { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych <= '+') goto yy80; + yyt4 = YYCURSOR; + goto yy223; + } + } + } else { + if (yych <= '?') { + if (yych <= '9') { + if (yych <= '-') goto yy46; + if (yych <= '.') { + yyt4 = YYCURSOR; + goto yy230; + } + if (yych <= '/') goto yy4; + goto yy115; + } else { + if (yych <= ':') { + yyt3 = YYCURSOR; + goto yy234; + } + if (yych <= ';') { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych <= '>') goto yy4; + yyt4 = YYCURSOR; + goto yy223; + } + } else { + if (yych <= '_') { + if (yych <= '@') goto yy96; + if (yych <= 'Z') goto yy115; + if (yych <= '^') goto yy4; + goto yy46; + } else { + if (yych <= 'd') { + if (yych <= '`') goto yy4; + goto yy326; + } else { + if (yych <= 'e') goto yy330; + if (yych <= 'z') goto yy326; + goto yy4; + } + } + } + } +yy329: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[768+yych] & 64) { + goto yy115; + } + if (yych <= '*') { + if (yych <= 0x1F) { + if (yych <= '\n') { + if (yych <= 0x00) { + yyt1 = YYCURSOR; + goto yy427; + } + if (yych <= 0x08) goto yy4; + yyt1 = YYCURSOR; + goto yy427; + } else { + if (yych == '\r') { + yyt1 = YYCURSOR; + goto yy427; + } + goto yy4; + } + } else { + if (yych <= '"') { + if (yych == '!') { + yyt1 = YYCURSOR; + goto yy429; + } + yyt1 = YYCURSOR; + goto yy427; + } else { + if (yych == '%') goto yy80; + if (yych <= '&') goto yy4; + yyt1 = YYCURSOR; + goto yy427; + } + } + } else { + if (yych <= ':') { + if (yych <= '-') { + if (yych <= '+') goto yy80; + if (yych <= ',') { + yyt1 = YYCURSOR; + goto yy429; + } + goto yy46; + } else { + if (yych <= '.') { + yyt1 = YYCURSOR; + goto yy430; + } + if (yych <= '/') goto yy4; + yyt1 = YYCURSOR; + goto yy431; + } + } else { + if (yych <= '?') { + if (yych <= ';') { + yyt1 = YYCURSOR; + goto yy427; + } + if (yych <= '>') goto yy4; + yyt1 = YYCURSOR; + goto yy429; + } else { + if (yych <= '@') goto yy96; + if (yych == '_') goto yy46; + goto yy4; + } + } + } +yy330: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '+') { + if (yych <= ' ') { + if (yych <= '\n') { + if (yych <= 0x00) { + yyt1 = YYCURSOR; + goto yy427; + } + if (yych <= 0x08) goto yy4; + yyt1 = YYCURSOR; + goto yy427; + } else { + if (yych == '\r') { + yyt1 = YYCURSOR; + goto yy427; + } + if (yych <= 0x1F) goto yy4; + yyt1 = YYCURSOR; + goto yy427; + } + } else { + if (yych <= '%') { + if (yych <= '!') { + yyt1 = YYCURSOR; + goto yy429; + } + if (yych <= '"') { + yyt1 = YYCURSOR; + goto yy427; + } + if (yych <= '$') goto yy4; + goto yy80; + } else { + if (yych <= '&') goto yy4; + if (yych <= '\'') { + yyt1 = YYCURSOR; + goto yy432; + } + if (yych <= '*') { + yyt1 = YYCURSOR; + goto yy427; + } + goto yy80; + } + } + } else { + if (yych <= '>') { + if (yych <= '/') { + if (yych <= ',') { + yyt1 = YYCURSOR; + goto yy429; + } + if (yych <= '-') goto yy46; + if (yych <= '.') { + yyt1 = YYCURSOR; + goto yy430; + } + goto yy4; + } else { + if (yych <= '9') goto yy115; + if (yych <= ':') { + yyt1 = YYCURSOR; + goto yy431; + } + if (yych <= ';') { + yyt1 = YYCURSOR; + goto yy427; + } + goto yy4; + } + } else { + if (yych <= '^') { + if (yych <= '?') { + yyt1 = YYCURSOR; + goto yy429; + } + if (yych <= '@') goto yy96; + if (yych <= 'Z') goto yy115; + goto yy4; + } else { + if (yych <= '_') goto yy46; + if (yych <= '`') goto yy4; + if (yych <= 'z') goto yy326; + goto yy4; + } + } + } +yy331: + yych = *++YYCURSOR; + if (yych <= '\f') { + if (yych == '\t') goto yy221; + goto yy161; + } else { + if (yych <= '\r') goto yy221; + if (yych == ' ') goto yy221; + goto yy161; + } +yy332: + yyaccept = 14; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '-') { + if (yych <= 0x1F) { + if (yych <= '\f') { + if (yych <= 0x00) { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych <= 0x08) goto yy83; + if (yych <= '\n') { + yyt3 = YYCURSOR; + goto yy241; + } + goto yy83; + } else { + if (yych <= '\r') { + yyt3 = YYCURSOR; + goto yy241; + } + if (yych == 0x1B) goto yy161; + goto yy83; + } + } else { + if (yych <= '&') { + if (yych == '!') { + yyt3 = YYCURSOR; + goto yy242; + } + if (yych <= '"') { + yyt3 = YYCURSOR; + goto yy241; + } + goto yy83; + } else { + if (yych <= '*') { + if (yych <= '\'') { + yyt3 = YYCURSOR; + goto yy243; + } + yyt3 = YYCURSOR; + goto yy241; + } else { + if (yych == ',') { + yyt3 = YYCURSOR; + goto yy242; + } + goto yy83; + } + } + } + } else { + if (yych <= 'z') { + if (yych <= '>') { + if (yych <= '.') { + yyt3 = YYCURSOR; + goto yy244; + } + if (yych <= '9') goto yy83; + if (yych <= ';') { + yyt3 = YYCURSOR; + goto yy241; + } + goto yy83; + } else { + if (yych <= '[') { + if (yych <= '?') { + yyt3 = YYCURSOR; + goto yy242; + } + goto yy83; + } else { + if (yych <= '\\') goto yy86; + if (yych <= '`') goto yy83; + goto yy150; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) goto yy83; + if (yych <= 0xC1) goto yy161; + if (yych <= 0xDF) goto yy87; + goto yy88; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) goto yy89; + goto yy90; + } else { + if (yych <= 0xF3) goto yy91; + if (yych <= 0xF4) goto yy92; + goto yy161; + } + } + } + } +yy333: + yyaccept = 14; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[768+yych] & 128) { + goto yy137; + } + if (yych <= '*') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy225; + if (yych <= 0x08) goto yy161; + if (yych <= '\n') goto yy225; + goto yy161; + } else { + if (yych <= 0x1F) { + if (yych <= '\r') goto yy225; + goto yy161; + } else { + if (yych <= '"') goto yy225; + if (yych <= '&') goto yy161; + goto yy225; + } + } + } else { + if (yych <= '.') { + if (yych == ',') goto yy225; + if (yych <= '-') goto yy161; + yyt4 = YYCURSOR; + goto yy226; + } else { + if (yych <= ';') { + if (yych <= '9') goto yy161; + goto yy225; + } else { + if (yych == '?') goto yy225; + goto yy161; + } + } + } +yy334: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '/') { + if (yych <= '\'') { + if (yych == '%') goto yy80; + if (yych <= '&') goto yy4; + goto yy137; + } else { + if (yych <= '+') { + if (yych <= '*') goto yy4; + goto yy80; + } else { + if (yych <= ',') goto yy4; + if (yych <= '.') goto yy46; + goto yy4; + } + } + } else { + if (yych <= 'Z') { + if (yych <= '9') goto yy46; + if (yych <= '?') goto yy4; + if (yych <= '@') goto yy96; + goto yy46; + } else { + if (yych <= '_') { + if (yych <= '^') goto yy4; + goto yy46; + } else { + if (yych <= '`') goto yy4; + if (yych <= 'z') goto yy433; + goto yy4; + } + } + } +yy335: + yych = *++YYCURSOR; + if (yych <= 0xDF) { + if (yych <= ':') { + if (yych <= '9') { + yyt1 = YYCURSOR; + goto yy434; + } + } else { + if (yych <= 0x7F) { + yyt1 = YYCURSOR; + goto yy434; + } + if (yych <= 0xC1) goto yy1; + yyt1 = YYCURSOR; + goto yy436; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xE0) { + yyt1 = YYCURSOR; + goto yy437; + } + if (yych <= 0xEF) { + yyt1 = YYCURSOR; + goto yy438; + } + yyt1 = YYCURSOR; + goto yy439; + } else { + if (yych <= 0xF3) { + yyt1 = YYCURSOR; + goto yy440; + } + if (yych <= 0xF4) { + yyt1 = YYCURSOR; + goto yy441; + } + goto yy1; + } + } +yy336: + yych = *++YYCURSOR; + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy442; + goto yy1; +yy337: + yyaccept = 19; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[512+yych] & 64) { + goto yy249; + } + if (yych <= '9') { + if (yych == '-') goto yy158; + if (yych >= '0') goto yy158; + } else { + if (yych <= 'Z') { + if (yych >= 'A') goto yy337; + } else { + if (yych <= '`') goto yy338; + if (yych <= 'z') goto yy337; + } + } +yy338: +#line 260 "../../lnav/src/data_scanner_re.re" + { RET(DT_EMAIL); } +#line 11084 "../../lnav/src/data_scanner_re.cc" +yy339: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '/') { + if (yych <= '+') { + if (yych == '%') goto yy80; + if (yych <= '*') goto yy4; + goto yy194; + } else { + if (yych <= ',') goto yy4; + if (yych <= '-') goto yy251; + if (yych <= '.') goto yy46; + goto yy4; + } + } else { + if (yych <= 'Z') { + if (yych <= '9') goto yy46; + if (yych <= '?') goto yy4; + if (yych <= '@') goto yy96; + goto yy46; + } else { + if (yych <= '_') { + if (yych <= '^') goto yy4; + goto yy46; + } else { + if (yych <= '`') goto yy4; + if (yych <= 'z') goto yy46; + goto yy4; + } + } + } +yy340: + yyaccept = 6; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '9') { + if (yych <= 0x1F) { + if (yych <= '\f') { + if (yych <= 0x00) goto yy26; + if (yych <= 0x08) goto yy3; + if (yych <= '\n') goto yy26; + goto yy3; + } else { + if (yych <= '\r') goto yy26; + if (yych == 0x1B) goto yy26; + goto yy3; + } + } else { + if (yych <= '*') { + if (yych <= '#') goto yy26; + if (yych <= '$') goto yy3; + if (yych <= '%') goto yy80; + goto yy26; + } else { + if (yych <= ',') { + if (yych <= '+') goto yy80; + goto yy26; + } else { + if (yych <= '.') goto yy46; + if (yych <= '/') goto yy3; + goto yy340; + } + } + } + } else { + if (yych <= '~') { + if (yych <= 'Z') { + if (yych <= ':') goto yy62; + if (yych <= '?') goto yy26; + if (yych <= '@') goto yy96; + goto yy46; + } else { + if (yych <= '_') { + if (yych <= '^') goto yy26; + goto yy46; + } else { + if (yych <= '`') goto yy26; + if (yych <= 'z') goto yy46; + goto yy26; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) goto yy3; + if (yych <= 0xC1) goto yy26; + if (yych <= 0xDF) goto yy56; + goto yy57; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) goto yy58; + goto yy59; + } else { + if (yych <= 0xF3) goto yy60; + if (yych <= 0xF4) goto yy61; + goto yy26; + } + } + } + } +yy341: + yyaccept = 19; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= ':') { + if (yych <= 0x1F) { + if (yych <= '\f') { + if (yych <= 0x00) goto yy338; + if (yych <= 0x08) goto yy3; + if (yych <= '\n') goto yy338; + goto yy3; + } else { + if (yych <= '\r') goto yy338; + if (yych == 0x1B) goto yy338; + goto yy3; + } + } else { + if (yych <= '-') { + if (yych == '$') goto yy3; + if (yych <= ',') goto yy338; + goto yy174; + } else { + if (yych <= '.') goto yy252; + if (yych <= '/') goto yy3; + if (yych <= '9') goto yy174; + goto yy62; + } + } + } else { + if (yych <= '~') { + if (yych <= '^') { + if (yych <= '?') goto yy338; + if (yych <= '@') goto yy3; + if (yych <= 'Z') goto yy341; + goto yy338; + } else { + if (yych <= '_') goto yy3; + if (yych <= '`') goto yy338; + if (yych <= 'z') goto yy341; + goto yy338; + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) goto yy3; + if (yych <= 0xC1) goto yy338; + if (yych <= 0xDF) goto yy56; + goto yy57; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) goto yy58; + goto yy59; + } else { + if (yych <= 0xF3) goto yy60; + if (yych <= 0xF4) goto yy61; + goto yy338; + } + } + } + } +yy342: + yych = *++YYCURSOR; + if (yych == 'g') goto yy443; + goto yy100; +yy343: + yyaccept = 5; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '9') { + if (yych <= ' ') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy24; + if (yych <= 0x08) goto yy4; + if (yych <= '\n') goto yy24; + goto yy4; + } else { + if (yych <= 0x1A) { + if (yych <= '\r') goto yy24; + goto yy4; + } else { + if (yych <= 0x1B) goto yy24; + if (yych <= 0x1F) goto yy4; + goto yy24; + } + } + } else { + if (yych <= '$') { + if (yych == '"') goto yy24; + if (yych <= '#') goto yy99; + goto yy101; + } else { + if (yych <= '\'') { + if (yych <= '&') goto yy99; + goto yy24; + } else { + if (yych <= '*') goto yy99; + if (yych <= ',') goto yy24; + goto yy101; + } + } + } + } else { + if (yych <= '`') { + if (yych <= '[') { + if (yych <= ':') goto yy4; + if (yych <= '?') goto yy24; + if (yych <= 'Z') goto yy101; + goto yy24; + } else { + if (yych <= ']') { + if (yych <= '\\') goto yy99; + goto yy24; + } else { + if (yych <= '^') goto yy99; + if (yych <= '_') goto yy101; + goto yy24; + } + } + } else { + if (yych <= '}') { + if (yych == 'r') goto yy444; + if (yych <= 'z') goto yy101; + goto yy24; + } else { + if (yych <= 0x7F) { + if (yych <= '~') goto yy99; + goto yy4; + } else { + if (yych <= 0xC1) goto yy24; + if (yych <= 0xF4) goto yy4; + goto yy24; + } + } + } + } +yy344: + yyaccept = 20; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '9') { + if (yych <= 0x1F) { + if (yych <= '\f') { + if (yych <= 0x00) goto yy345; + if (yych <= 0x08) goto yy3; + if (yych >= '\v') goto yy3; + } else { + if (yych <= '\r') goto yy345; + if (yych != 0x1B) goto yy3; + } + } else { + if (yych <= '*') { + if (yych <= '#') goto yy345; + if (yych <= '$') goto yy3; + if (yych <= '%') goto yy80; + } else { + if (yych <= ',') { + if (yych <= '+') goto yy80; + } else { + if (yych <= '.') goto yy46; + if (yych <= '/') goto yy3; + goto yy344; + } + } + } + } else { + if (yych <= '~') { + if (yych <= 'Z') { + if (yych <= ':') goto yy62; + if (yych <= '?') goto yy345; + if (yych <= '@') goto yy96; + goto yy344; + } else { + if (yych <= '_') { + if (yych >= '_') goto yy344; + } else { + if (yych <= '`') goto yy345; + if (yych <= 'z') goto yy344; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) goto yy3; + if (yych <= 0xC1) goto yy345; + if (yych <= 0xDF) goto yy56; + goto yy57; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) goto yy58; + goto yy59; + } else { + if (yych <= 0xF3) goto yy60; + if (yych <= 0xF4) goto yy61; + } + } + } + } +yy345: +#line 251 "../../lnav/src/data_scanner_re.re" + { + RET(DT_VERSION_NUMBER); + } +#line 11380 "../../lnav/src/data_scanner_re.cc" +yy346: + yyaccept = 20; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '-') { + if (yych <= 0x1B) { + if (yych <= '\n') { + if (yych <= 0x00) goto yy345; + if (yych <= 0x08) goto yy4; + goto yy345; + } else { + if (yych == '\r') goto yy345; + if (yych <= 0x1A) goto yy4; + goto yy345; + } + } else { + if (yych <= '%') { + if (yych <= 0x1F) goto yy4; + if (yych <= '#') goto yy345; + if (yych <= '$') goto yy4; + goto yy80; + } else { + if (yych == '+') goto yy80; + if (yych <= ',') goto yy345; + goto yy255; + } + } + } else { + if (yych <= '^') { + if (yych <= ':') { + if (yych <= '.') goto yy445; + if (yych <= '/') goto yy4; + if (yych <= '9') goto yy446; + goto yy4; + } else { + if (yych <= '?') goto yy345; + if (yych <= '@') goto yy96; + if (yych <= 'Z') goto yy447; + goto yy345; + } + } else { + if (yych <= '~') { + if (yych == '`') goto yy345; + if (yych <= 'z') goto yy447; + goto yy345; + } else { + if (yych <= 0x7F) goto yy4; + if (yych <= 0xC1) goto yy345; + if (yych <= 0xF4) goto yy4; + goto yy345; + } + } + } +yy347: + yyaccept = 20; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '-') { + if (yych <= 0x1B) { + if (yych <= '\n') { + if (yych <= 0x00) goto yy345; + if (yych <= 0x08) goto yy4; + goto yy345; + } else { + if (yych == '\r') goto yy345; + if (yych <= 0x1A) goto yy4; + goto yy345; + } + } else { + if (yych <= '%') { + if (yych <= 0x1F) goto yy4; + if (yych <= '#') goto yy345; + if (yych <= '$') goto yy4; + goto yy80; + } else { + if (yych == '+') goto yy80; + if (yych <= ',') goto yy345; + goto yy255; + } + } + } else { + if (yych <= '^') { + if (yych <= ':') { + if (yych <= '.') goto yy445; + if (yych <= '/') goto yy4; + if (yych <= '9') goto yy346; + goto yy4; + } else { + if (yych <= '?') goto yy345; + if (yych <= '@') goto yy96; + if (yych <= 'Z') goto yy447; + goto yy345; + } + } else { + if (yych <= '~') { + if (yych == '`') goto yy345; + if (yych <= 'z') goto yy447; + goto yy345; + } else { + if (yych <= 0x7F) goto yy4; + if (yych <= 0xC1) goto yy345; + if (yych <= 0xF4) goto yy4; + goto yy345; + } + } + } +yy348: + yyaccept = 20; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '.') { + if (yych <= 0x1F) { + if (yych <= '\f') { + if (yych <= 0x00) goto yy345; + if (yych <= 0x08) goto yy4; + if (yych <= '\n') goto yy345; + goto yy4; + } else { + if (yych <= '\r') goto yy345; + if (yych == 0x1B) goto yy345; + goto yy4; + } + } else { + if (yych <= '*') { + if (yych <= '#') goto yy345; + if (yych <= '$') goto yy4; + if (yych <= '%') goto yy80; + goto yy345; + } else { + if (yych <= '+') goto yy80; + if (yych <= ',') goto yy345; + if (yych <= '-') goto yy255; + goto yy445; + } + } + } else { + if (yych <= 'Z') { + if (yych <= '9') { + if (yych <= '/') goto yy4; + if (yych <= '4') goto yy346; + if (yych <= '5') goto yy448; + goto yy446; + } else { + if (yych <= ':') goto yy4; + if (yych <= '?') goto yy345; + if (yych <= '@') goto yy96; + goto yy447; + } + } else { + if (yych <= 'z') { + if (yych == '_') goto yy447; + if (yych <= '`') goto yy345; + goto yy447; + } else { + if (yych <= 0x7F) { + if (yych <= '~') goto yy345; + goto yy4; + } else { + if (yych <= 0xC1) goto yy345; + if (yych <= 0xF4) goto yy4; + goto yy345; + } + } + } + } +yy349: + yyaccept = 6; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[768+yych] & 32) { + goto yy103; + } + if (yych <= '?') { + if (yych <= '$') { + if (yych <= '\r') { + if (yych <= 0x08) { + if (yych <= 0x00) goto yy26; + goto yy3; + } else { + if (yych <= '\n') goto yy26; + if (yych <= '\f') goto yy3; + goto yy26; + } + } else { + if (yych <= 0x1B) { + if (yych <= 0x1A) goto yy3; + goto yy26; + } else { + if (yych <= 0x1F) goto yy3; + if (yych <= '#') goto yy26; + goto yy3; + } + } + } else { + if (yych <= '-') { + if (yych <= '*') { + if (yych <= '%') goto yy105; + goto yy26; + } else { + if (yych <= '+') goto yy80; + if (yych <= ',') goto yy26; + goto yy255; + } + } else { + if (yych <= '/') { + if (yych >= '/') goto yy3; + } else { + if (yych <= '9') goto yy349; + if (yych <= ':') goto yy62; + goto yy26; + } + } + } + } else { + if (yych <= 'z') { + if (yych <= '^') { + if (yych <= 'D') { + if (yych <= '@') goto yy96; + goto yy258; + } else { + if (yych <= 'E') goto yy259; + if (yych <= 'Z') goto yy258; + goto yy26; + } + } else { + if (yych <= '`') { + if (yych <= '_') goto yy258; + goto yy26; + } else { + if (yych == 'e') goto yy259; + goto yy258; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + if (yych <= '~') goto yy26; + goto yy3; + } else { + if (yych <= 0xC1) goto yy26; + if (yych <= 0xDF) goto yy56; + goto yy57; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) goto yy58; + goto yy59; + } else { + if (yych <= 0xF3) goto yy60; + if (yych <= 0xF4) goto yy61; + goto yy26; + } + } + } + } +yy350: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '9') { + if (yych <= 0x1F) { + if (yych <= '\f') { + if (yych <= 0x00) goto yy5; + if (yych <= 0x08) goto yy3; + if (yych <= '\n') goto yy5; + goto yy3; + } else { + if (yych <= '\r') goto yy5; + if (yych == 0x1B) goto yy5; + goto yy3; + } + } else { + if (yych <= '*') { + if (yych <= '#') goto yy5; + if (yych <= '$') goto yy3; + if (yych <= '%') goto yy80; + goto yy5; + } else { + if (yych <= ',') { + if (yych <= '+') goto yy80; + goto yy5; + } else { + if (yych <= '.') goto yy46; + if (yych <= '/') goto yy3; + goto yy447; + } + } + } + } else { + if (yych <= '~') { + if (yych <= 'Z') { + if (yych <= ':') goto yy62; + if (yych <= '?') goto yy5; + if (yych <= '@') goto yy96; + goto yy46; + } else { + if (yych <= '_') { + if (yych <= '^') goto yy5; + goto yy46; + } else { + if (yych <= '`') goto yy5; + if (yych <= 'z') goto yy46; + goto yy5; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) goto yy3; + if (yych <= 0xC1) goto yy5; + if (yych <= 0xDF) goto yy56; + goto yy57; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) goto yy58; + goto yy59; + } else { + if (yych <= 0xF3) goto yy60; + if (yych <= 0xF4) goto yy61; + goto yy5; + } + } + } + } +yy351: + yych = *++YYCURSOR; + if (yych <= '/') goto yy81; + if (yych <= '9') goto yy449; + goto yy81; +yy352: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '9') { + if (yych <= '+') { + if (yych == '%') goto yy80; + if (yych <= '*') goto yy4; + goto yy80; + } else { + if (yych <= ',') goto yy4; + if (yych <= '.') goto yy46; + if (yych <= '/') goto yy4; + goto yy451; + } + } else { + if (yych <= '^') { + if (yych <= '?') goto yy4; + if (yych <= '@') goto yy96; + if (yych <= 'Z') goto yy344; + goto yy4; + } else { + if (yych == '`') goto yy4; + if (yych <= 'z') goto yy344; + goto yy4; + } + } +yy353: + yyaccept = 21; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '9') { + if (yych <= '#') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy354; + if (yych <= 0x08) goto yy3; + if (yych >= '\v') goto yy3; + } else { + if (yych <= 0x1A) { + if (yych >= 0x0E) goto yy3; + } else { + if (yych <= 0x1B) goto yy354; + if (yych <= 0x1F) goto yy3; + } + } + } else { + if (yych <= '+') { + if (yych <= '$') goto yy3; + if (yych <= '%') goto yy80; + if (yych >= '+') goto yy80; + } else { + if (yych <= '-') { + if (yych >= '-') goto yy181; + } else { + if (yych == '/') goto yy3; + goto yy46; + } + } + } + } else { + if (yych <= '~') { + if (yych <= 'Z') { + if (yych <= ':') goto yy452; + if (yych <= '?') goto yy354; + if (yych <= '@') goto yy96; + goto yy46; + } else { + if (yych <= '_') { + if (yych >= '_') goto yy46; + } else { + if (yych <= '`') goto yy354; + if (yych <= 'z') goto yy46; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) goto yy3; + if (yych <= 0xC1) goto yy354; + if (yych <= 0xDF) goto yy56; + goto yy57; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) goto yy58; + goto yy59; + } else { + if (yych <= 0xF3) goto yy60; + if (yych <= 0xF4) goto yy61; + } + } + } + } +yy354: +#line 174 "../../lnav/src/data_scanner_re.re" + { + if ((YYCURSOR.val - (this->ds_input.udata() + this->ds_next_offset)) == 17) { + RET(DT_MAC_ADDRESS); + } else { + RET(DT_HEX_DUMP); + } + } +#line 11802 "../../lnav/src/data_scanner_re.cc" +yy355: + yyaccept = 6; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[768+yych] & 32) { + goto yy103; + } + if (yych <= '/') { + if (yych <= 0x1F) { + if (yych <= '\f') { + if (yych <= 0x00) goto yy26; + if (yych <= 0x08) goto yy4; + if (yych <= '\n') goto yy26; + goto yy4; + } else { + if (yych <= '\r') goto yy26; + if (yych == 0x1B) goto yy26; + goto yy4; + } + } else { + if (yych <= '*') { + if (yych <= '#') goto yy26; + if (yych <= '$') goto yy4; + if (yych <= '%') goto yy105; + goto yy26; + } else { + if (yych <= ',') { + if (yych <= '+') goto yy80; + goto yy26; + } else { + if (yych <= '-') goto yy255; + if (yych <= '.') goto yy256; + goto yy4; + } + } + } + } else { + if (yych <= '_') { + if (yych <= '@') { + if (yych <= '9') goto yy359; + if (yych <= ':') goto yy4; + if (yych <= '?') goto yy26; + goto yy96; + } else { + if (yych <= 'E') { + if (yych <= 'D') goto yy258; + } else { + if (yych <= 'Z') goto yy258; + if (yych <= '^') goto yy26; + goto yy258; + } + } + } else { + if (yych <= 'z') { + if (yych <= '`') goto yy26; + if (yych != 'e') goto yy258; + } else { + if (yych <= 0x7F) { + if (yych <= '~') goto yy26; + goto yy4; + } else { + if (yych <= 0xC1) goto yy26; + if (yych <= 0xF4) goto yy4; + goto yy26; + } + } + } + } +yy356: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[512+yych] & 128) { + goto yy258; + } + if (yych <= ',') { + if (yych <= '%') { + if (yych <= '$') goto yy4; + goto yy80; + } else { + if (yych == '+') goto yy194; + goto yy4; + } + } else { + if (yych <= '.') { + if (yych <= '-') goto yy255; + goto yy350; + } else { + if (yych == '@') goto yy96; + goto yy4; + } + } +yy357: + yyaccept = 6; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[768+yych] & 32) { + goto yy103; + } + if (yych <= '5') { + if (yych <= '#') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy26; + if (yych <= 0x08) goto yy4; + if (yych <= '\n') goto yy26; + goto yy4; + } else { + if (yych <= 0x1A) { + if (yych <= '\r') goto yy26; + goto yy4; + } else { + if (yych <= 0x1B) goto yy26; + if (yych <= 0x1F) goto yy4; + goto yy26; + } + } + } else { + if (yych <= '+') { + if (yych <= '$') goto yy4; + if (yych <= '%') goto yy105; + if (yych <= '*') goto yy26; + goto yy80; + } else { + if (yych <= '-') { + if (yych <= ',') goto yy26; + goto yy255; + } else { + if (yych <= '.') goto yy256; + if (yych <= '/') goto yy4; + goto yy355; + } + } + } + } else { + if (yych <= '_') { + if (yych <= '@') { + if (yych <= '9') goto yy359; + if (yych <= ':') goto yy4; + if (yych <= '?') goto yy26; + goto yy96; + } else { + if (yych <= 'E') { + if (yych <= 'D') goto yy258; + goto yy356; + } else { + if (yych <= 'Z') goto yy258; + if (yych <= '^') goto yy26; + goto yy258; + } + } + } else { + if (yych <= 'z') { + if (yych <= '`') goto yy26; + if (yych == 'e') goto yy356; + goto yy258; + } else { + if (yych <= 0x7F) { + if (yych <= '~') goto yy26; + goto yy4; + } else { + if (yych <= 0xC1) goto yy26; + if (yych <= 0xF4) goto yy4; + goto yy26; + } + } + } + } +yy358: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '@') goto yy4; + if (yych <= 'Z') goto yy453; + if (yych <= '`') goto yy4; + if (yych <= 'z') goto yy453; + goto yy4; +yy359: + yyaccept = 6; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[768+yych] & 32) { + goto yy103; + } + if (yych <= '?') { + if (yych <= '$') { + if (yych <= '\r') { + if (yych <= 0x08) { + if (yych <= 0x00) goto yy26; + goto yy3; + } else { + if (yych <= '\n') goto yy26; + if (yych <= '\f') goto yy3; + goto yy26; + } + } else { + if (yych <= 0x1B) { + if (yych <= 0x1A) goto yy3; + goto yy26; + } else { + if (yych <= 0x1F) goto yy3; + if (yych <= '#') goto yy26; + goto yy3; + } + } + } else { + if (yych <= '-') { + if (yych <= '*') { + if (yych <= '%') goto yy105; + goto yy26; + } else { + if (yych <= '+') goto yy80; + if (yych <= ',') goto yy26; + goto yy255; + } + } else { + if (yych <= '/') { + if (yych <= '.') goto yy350; + goto yy3; + } else { + if (yych <= '9') goto yy359; + if (yych <= ':') goto yy62; + goto yy26; + } + } + } + } else { + if (yych <= 'z') { + if (yych <= '^') { + if (yych <= 'D') { + if (yych <= '@') goto yy96; + goto yy258; + } else { + if (yych <= 'E') goto yy356; + if (yych <= 'Z') goto yy258; + goto yy26; + } + } else { + if (yych <= '`') { + if (yych <= '_') goto yy258; + goto yy26; + } else { + if (yych == 'e') goto yy356; + goto yy258; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + if (yych <= '~') goto yy26; + goto yy3; + } else { + if (yych <= 0xC1) goto yy26; + if (yych <= 0xDF) goto yy56; + goto yy57; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) goto yy58; + goto yy59; + } else { + if (yych <= 0xF3) goto yy60; + if (yych <= 0xF4) goto yy61; + goto yy26; + } + } + } + } +yy360: + yych = *++YYCURSOR; + if (yych <= '/') goto yy104; + if (yych <= '9') goto yy454; + goto yy104; +yy361: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '9') { + if (yych <= '+') { + if (yych == '%') goto yy80; + if (yych <= '*') goto yy4; + goto yy80; + } else { + if (yych <= ',') goto yy4; + if (yych <= '.') goto yy46; + if (yych <= '/') goto yy4; + goto yy455; + } + } else { + if (yych <= '^') { + if (yych <= '?') goto yy4; + if (yych <= '@') goto yy96; + if (yych <= 'Z') goto yy46; + goto yy4; + } else { + if (yych == '`') goto yy4; + if (yych <= 'z') goto yy46; + goto yy4; + } + } +yy362: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '/') goto yy4; + if (yych <= '9') goto yy456; + goto yy4; +yy363: + yyaccept = 11; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[768+yych] & 32) { + goto yy103; + } + if (yych <= '9') { + if (yych <= '#') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy109; + if (yych <= 0x08) goto yy4; + if (yych <= '\n') goto yy109; + goto yy4; + } else { + if (yych <= 0x1A) { + if (yych <= '\r') goto yy109; + goto yy4; + } else { + if (yych <= 0x1B) goto yy109; + if (yych <= 0x1F) goto yy4; + goto yy109; + } + } + } else { + if (yych <= ',') { + if (yych <= '%') { + if (yych <= '$') goto yy4; + goto yy105; + } else { + if (yych == '+') goto yy80; + goto yy109; + } + } else { + if (yych <= '.') { + if (yych <= '-') goto yy46; + goto yy266; + } else { + if (yych <= '/') goto yy4; + if (yych <= '7') goto yy457; + goto yy458; + } + } + } + } else { + if (yych <= '_') { + if (yych <= 'D') { + if (yych <= ':') goto yy148; + if (yych <= '?') goto yy109; + if (yych <= '@') goto yy96; + goto yy459; + } else { + if (yych <= 'F') { + if (yych <= 'E') goto yy460; + goto yy459; + } else { + if (yych <= 'Z') goto yy115; + if (yych <= '^') goto yy109; + goto yy46; + } + } + } else { + if (yych <= 'z') { + if (yych <= 'd') { + if (yych <= '`') goto yy109; + goto yy459; + } else { + if (yych <= 'e') goto yy460; + if (yych <= 'f') goto yy459; + goto yy115; + } + } else { + if (yych <= 0x7F) { + if (yych <= '~') goto yy109; + goto yy4; + } else { + if (yych <= 0xC1) goto yy109; + if (yych <= 0xF4) goto yy4; + goto yy109; + } + } + } + } +yy364: + yyaccept = 6; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[768+yych] & 32) { + goto yy103; + } + if (yych <= '9') { + if (yych <= '#') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy26; + if (yych <= 0x08) goto yy4; + if (yych <= '\n') goto yy26; + goto yy4; + } else { + if (yych <= 0x1A) { + if (yych <= '\r') goto yy26; + goto yy4; + } else { + if (yych <= 0x1B) goto yy26; + if (yych <= 0x1F) goto yy4; + goto yy26; + } + } + } else { + if (yych <= '+') { + if (yych <= '$') goto yy4; + if (yych <= '%') goto yy105; + if (yych <= '*') goto yy26; + goto yy80; + } else { + if (yych <= '-') { + if (yych <= ',') goto yy26; + goto yy46; + } else { + if (yych <= '.') goto yy266; + if (yych <= '/') goto yy4; + goto yy458; + } + } + } + } else { + if (yych <= '_') { + if (yych <= 'D') { + if (yych <= ':') goto yy148; + if (yych <= '?') goto yy26; + if (yych <= '@') goto yy96; + goto yy459; + } else { + if (yych <= 'F') { + if (yych <= 'E') goto yy460; + goto yy459; + } else { + if (yych <= 'Z') goto yy115; + if (yych <= '^') goto yy26; + goto yy46; + } + } + } else { + if (yych <= 'z') { + if (yych <= 'd') { + if (yych <= '`') goto yy26; + goto yy459; + } else { + if (yych <= 'e') goto yy460; + if (yych <= 'f') goto yy459; + goto yy115; + } + } else { + if (yych <= 0x7F) { + if (yych <= '~') goto yy26; + goto yy4; + } else { + if (yych <= 0xC1) goto yy26; + if (yych <= 0xF4) goto yy4; + goto yy26; + } + } + } + } +yy365: + yych = *++YYCURSOR; + if (yych <= '9') { + if (yych <= '%') { + if (yych <= '$') goto yy1; + goto yy461; + } else { + if (yych <= '.') goto yy1; + if (yych <= '/') goto yy189; + goto yy192; + } + } else { + if (yych <= 'F') { + if (yych <= ':') goto yy462; + if (yych <= '@') goto yy1; + goto yy192; + } else { + if (yych <= '`') goto yy1; + if (yych <= 'f') goto yy192; + goto yy1; + } + } +yy366: + yyaccept = 12; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '/') { + if (yych <= 0x1F) { + if (yych <= '\f') { + if (yych <= 0x00) goto yy113; + if (yych <= 0x08) goto yy4; + if (yych <= '\n') goto yy113; + goto yy4; + } else { + if (yych <= '\r') goto yy113; + if (yych == 0x1B) goto yy113; + goto yy4; + } + } else { + if (yych <= '*') { + if (yych <= '#') goto yy113; + if (yych <= '$') goto yy4; + if (yych <= '%') goto yy80; + goto yy113; + } else { + if (yych <= '+') goto yy80; + if (yych <= ',') goto yy113; + if (yych <= '.') goto yy46; + goto yy4; + } + } + } else { + if (yych <= '_') { + if (yych <= '@') { + if (yych <= '9') goto yy459; + if (yych <= ':') goto yy148; + if (yych <= '?') goto yy113; + goto yy96; + } else { + if (yych <= 'F') goto yy459; + if (yych <= 'Z') goto yy115; + if (yych <= '^') goto yy113; + goto yy46; + } + } else { + if (yych <= '~') { + if (yych <= '`') goto yy113; + if (yych <= 'f') goto yy459; + if (yych <= 'z') goto yy115; + goto yy113; + } else { + if (yych <= 0x7F) goto yy4; + if (yych <= 0xC1) goto yy113; + if (yych <= 0xF4) goto yy4; + goto yy113; + } + } + } +yy367: + yyaccept = 12; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '.') { + if (yych <= 0x1F) { + if (yych <= '\f') { + if (yych <= 0x00) goto yy113; + if (yych <= 0x08) goto yy4; + if (yych <= '\n') goto yy113; + goto yy4; + } else { + if (yych <= '\r') goto yy113; + if (yych == 0x1B) goto yy113; + goto yy4; + } + } else { + if (yych <= '*') { + if (yych <= '#') goto yy113; + if (yych <= '$') goto yy4; + if (yych <= '%') goto yy80; + goto yy113; + } else { + if (yych <= '+') goto yy194; + if (yych <= ',') goto yy113; + if (yych <= '-') goto yy251; + goto yy46; + } + } + } else { + if (yych <= '^') { + if (yych <= '?') { + if (yych <= '/') goto yy4; + if (yych <= '9') goto yy459; + if (yych <= ':') goto yy148; + goto yy113; + } else { + if (yych <= '@') goto yy96; + if (yych <= 'F') goto yy459; + if (yych <= 'Z') goto yy115; + goto yy113; + } + } else { + if (yych <= 'z') { + if (yych <= '_') goto yy46; + if (yych <= '`') goto yy113; + if (yych <= 'f') goto yy459; + goto yy115; + } else { + if (yych <= 0x7F) { + if (yych <= '~') goto yy113; + goto yy4; + } else { + if (yych <= 0xC1) goto yy113; + if (yych <= 0xF4) goto yy4; + goto yy113; + } + } + } + } +yy368: + yyaccept = 21; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= 'f') { + if (yych <= '9') { + if (yych == '-') { + yyt1 = YYCURSOR; + goto yy463; + } + if (yych <= '/') { + yyt1 = YYCURSOR; + goto yy434; + } + yyt1 = YYCURSOR; + goto yy464; + } else { + if (yych <= '@') { + if (yych <= ':') goto yy465; + yyt1 = YYCURSOR; + goto yy434; + } else { + if (yych <= 'F') { + yyt1 = YYCURSOR; + goto yy464; + } + if (yych <= '`') { + yyt1 = YYCURSOR; + goto yy434; + } + yyt1 = YYCURSOR; + goto yy464; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + yyt1 = YYCURSOR; + goto yy434; + } + if (yych <= 0xC1) goto yy354; + if (yych <= 0xDF) { + yyt1 = YYCURSOR; + goto yy436; + } + yyt1 = YYCURSOR; + goto yy437; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt1 = YYCURSOR; + goto yy438; + } + yyt1 = YYCURSOR; + goto yy439; + } else { + if (yych <= 0xF3) { + yyt1 = YYCURSOR; + goto yy440; + } + if (yych <= 0xF4) { + yyt1 = YYCURSOR; + goto yy441; + } + goto yy354; + } + } + } +yy369: + yyaccept = 21; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= ':') { + if (yych <= '-') { + if (yych <= ',') goto yy354; + goto yy466; + } else { + if (yych <= '/') goto yy354; + if (yych <= '9') goto yy372; + goto yy467; + } + } else { + if (yych <= 'F') { + if (yych <= '@') goto yy354; + goto yy372; + } else { + if (yych <= '`') goto yy354; + if (yych <= 'f') goto yy372; + goto yy354; + } + } +yy370: + yyaccept = 22; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[256+yych] & 1) { + goto yy273; + } + if (yych <= 0xC1) { + if (yych <= ')') { + if (yych <= '"') goto yy371; + if (yych <= '&') goto yy370; + } else { + if (yych <= 'Z') goto yy370; + if (yych <= ']') goto yy371; + if (yych <= 'z') goto yy370; + } + } else { + if (yych <= 0xEF) { + if (yych <= 0xDF) goto yy275; + if (yych <= 0xE0) goto yy276; + goto yy277; + } else { + if (yych <= 0xF0) goto yy278; + if (yych <= 0xF3) goto yy279; + if (yych <= 0xF4) goto yy280; + } + } +yy371: +#line 170 "../../lnav/src/data_scanner_re.re" + { RET(DT_URL); } +#line 12517 "../../lnav/src/data_scanner_re.cc" +yy372: + yych = *++YYCURSOR; + if (yych <= '@') { + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy468; + if (yych <= ':') goto yy282; + goto yy1; + } else { + if (yych <= 'F') goto yy468; + if (yych <= '`') goto yy1; + if (yych <= 'f') goto yy468; + goto yy1; + } +yy373: + yych = *++YYCURSOR; + if (yych <= '@') { + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy469; + if (yych <= ':') goto yy375; + goto yy1; + } else { + if (yych <= 'F') goto yy374; + if (yych <= '`') goto yy1; + if (yych >= 'g') goto yy1; + } +yy374: + yych = *++YYCURSOR; + if (yych <= '@') { + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy470; + if (yych <= ':') goto yy471; + goto yy1; + } else { + if (yych <= 'F') goto yy470; + if (yych <= '`') goto yy1; + if (yych <= 'f') goto yy470; + goto yy1; + } +yy375: + yych = *++YYCURSOR; + if (yych <= '`') { + if (yych <= '9') { + if (yych <= '0') { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy472; + } else { + if (yych <= '1') goto yy473; + if (yych <= '2') goto yy474; + goto yy472; + } + } else { + if (yych <= '@') { + if (yych <= ':') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 'F') goto yy475; + if (yych <= 'Z') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } + } + } else { + if (yych <= 0xDF) { + if (yych <= 'z') { + if (yych <= 'f') goto yy475; + goto yy1; + } else { + if (yych <= 0x7F) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0xC1) goto yy1; + yyt2 = YYCURSOR; + goto yy205; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xE0) { + yyt2 = YYCURSOR; + goto yy206; + } + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy376: + yyaccept = 17; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '1') { + if (yych <= 0x1B) { + if (yych <= '\n') { + if (yych <= 0x00) goto yy199; + if (yych <= 0x08) goto yy4; + goto yy199; + } else { + if (yych == '\r') goto yy199; + if (yych <= 0x1A) goto yy4; + goto yy199; + } + } else { + if (yych <= '$') { + if (yych <= 0x1F) goto yy4; + if (yych <= '#') goto yy199; + goto yy4; + } else { + if (yych <= ',') goto yy199; + if (yych <= '/') goto yy4; + if (yych <= '0') goto yy476; + goto yy477; + } + } + } else { + if (yych <= '_') { + if (yych <= ':') { + if (yych <= '2') goto yy478; + if (yych <= '9') goto yy476; + goto yy4; + } else { + if (yych <= '?') goto yy199; + if (yych <= 'Z') goto yy4; + if (yych <= '^') goto yy199; + goto yy4; + } + } else { + if (yych <= '~') { + if (yych <= '`') goto yy199; + if (yych <= 'z') goto yy4; + goto yy199; + } else { + if (yych <= 0x7F) goto yy4; + if (yych <= 0xC1) goto yy199; + if (yych <= 0xF4) goto yy4; + goto yy199; + } + } + } +yy377: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '?') { + if (yych <= 0x1F) { + if (yych <= '\f') { + if (yych <= 0x00) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0x08) { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= '\n') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= '\r') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych == 0x1B) { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } + } else { + if (yych <= '-') { + if (yych == '$') { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= ',') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= '/') { + if (yych <= '.') { + yyt2 = YYCURSOR; + goto yy376; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= '9') goto yy479; + if (yych >= ';') { + yyt2 = YYCURSOR; + goto yy198; + } + } + } + } + } else { + if (yych <= '~') { + if (yych <= '^') { + if (yych <= '@') { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= 'F') goto yy479; + if (yych <= 'Z') goto yy3; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= '`') { + if (yych <= '_') { + yyt2 = YYCURSOR; + goto yy284; + } + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 'f') goto yy479; + if (yych <= 'z') goto yy3; + yyt2 = YYCURSOR; + goto yy198; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= 0xC1) goto yy5; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy289; + } + yyt2 = YYCURSOR; + goto yy290; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy291; + } + yyt2 = YYCURSOR; + goto yy292; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy293; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy294; + } + goto yy5; + } + } + } + } +yy378: + yych = *++YYCURSOR; + if (yych <= '@') { + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy475; + if (yych <= ':') goto yy152; + goto yy1; + } else { + if (yych <= 'F') goto yy475; + if (yych <= '`') goto yy1; + if (yych <= 'f') goto yy475; + goto yy1; + } +yy379: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '@') { + if (yych <= 0x1F) { + if (yych <= '\f') { + if (yych <= 0x00) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0x08) { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= '\n') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= '\r') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych == 0x1B) { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } + } else { + if (yych <= '/') { + if (yych == '$') { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= ',') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= '9') goto yy479; + if (yych <= ':') goto yy378; + if (yych <= '?') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } + } + } else { + if (yych <= '~') { + if (yych <= '_') { + if (yych <= 'F') goto yy479; + if (yych <= 'Z') goto yy3; + if (yych <= '^') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 'f') goto yy479; + if (yych <= 'z') goto yy3; + yyt2 = YYCURSOR; + goto yy198; + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= 0xC1) goto yy5; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy289; + } + yyt2 = YYCURSOR; + goto yy290; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy291; + } + yyt2 = YYCURSOR; + goto yy292; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy293; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy294; + } + goto yy5; + } + } + } + } +yy380: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '?') { + if (yych <= 0x1F) { + if (yych <= '\f') { + if (yych <= 0x00) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0x08) { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= '\n') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= '\r') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych == 0x1B) { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } + } else { + if (yych <= '-') { + if (yych == '$') { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= ',') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= '/') { + if (yych <= '.') { + yyt2 = YYCURSOR; + goto yy376; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= '9') goto yy480; + if (yych <= ':') goto yy378; + yyt2 = YYCURSOR; + goto yy198; + } + } + } + } else { + if (yych <= '~') { + if (yych <= '^') { + if (yych <= '@') { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= 'F') goto yy479; + if (yych <= 'Z') goto yy3; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= '`') { + if (yych <= '_') { + yyt2 = YYCURSOR; + goto yy284; + } + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 'f') goto yy479; + if (yych <= 'z') goto yy3; + yyt2 = YYCURSOR; + goto yy198; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= 0xC1) goto yy5; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy289; + } + yyt2 = YYCURSOR; + goto yy290; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy291; + } + yyt2 = YYCURSOR; + goto yy292; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy293; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy294; + } + goto yy5; + } + } + } + } +yy381: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '?') { + if (yych <= '#') { + if (yych <= '\f') { + if (yych <= 0x00) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0x08) { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= '\n') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= 0x1A) { + if (yych <= '\r') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= 0x1B) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0x1F) { + yyt2 = YYCURSOR; + goto yy284; + } + yyt2 = YYCURSOR; + goto yy198; + } + } + } else { + if (yych <= '.') { + if (yych <= '$') { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= ',') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '-') { + yyt2 = YYCURSOR; + goto yy284; + } + yyt2 = YYCURSOR; + goto yy376; + } else { + if (yych <= '5') { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy284; + } + goto yy480; + } else { + if (yych <= '9') goto yy479; + if (yych <= ':') goto yy378; + yyt2 = YYCURSOR; + goto yy198; + } + } + } + } else { + if (yych <= '~') { + if (yych <= '^') { + if (yych <= '@') { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= 'F') goto yy479; + if (yych <= 'Z') goto yy3; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= '`') { + if (yych <= '_') { + yyt2 = YYCURSOR; + goto yy284; + } + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 'f') goto yy479; + if (yych <= 'z') goto yy3; + yyt2 = YYCURSOR; + goto yy198; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= 0xC1) goto yy5; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy289; + } + yyt2 = YYCURSOR; + goto yy290; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy291; + } + yyt2 = YYCURSOR; + goto yy292; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy293; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy294; + } + goto yy5; + } + } + } + } +yy382: + yyaccept = 21; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '-') { + if (yych <= 0x1B) { + if (yych <= '\n') { + if (yych <= 0x00) goto yy354; + if (yych <= 0x08) goto yy4; + goto yy354; + } else { + if (yych == '\r') goto yy354; + if (yych <= 0x1A) goto yy4; + goto yy354; + } + } else { + if (yych <= '%') { + if (yych <= 0x1F) goto yy4; + if (yych <= '#') goto yy354; + if (yych <= '$') goto yy4; + goto yy80; + } else { + if (yych == '+') goto yy80; + if (yych <= ',') goto yy354; + goto yy181; + } + } + } else { + if (yych <= '^') { + if (yych <= ':') { + if (yych <= '.') goto yy46; + if (yych <= '/') goto yy4; + if (yych <= '9') goto yy340; + goto yy452; + } else { + if (yych <= '?') goto yy354; + if (yych <= '@') goto yy96; + if (yych <= 'Z') goto yy46; + goto yy354; + } + } else { + if (yych <= '~') { + if (yych == '`') goto yy354; + if (yych <= 'z') goto yy46; + goto yy354; + } else { + if (yych <= 0x7F) goto yy4; + if (yych <= 0xC1) goto yy354; + if (yych <= 0xF4) goto yy4; + goto yy354; + } + } + } +yy383: + yych = *++YYCURSOR; + if (yych == '.') goto yy481; + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy482; + goto yy1; +yy384: + yych = *++YYCURSOR; + if (yych == '.') goto yy481; + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy383; + goto yy1; +yy385: + yych = *++YYCURSOR; + if (yych <= '/') { + if (yych == '.') goto yy481; + goto yy1; + } else { + if (yych <= '4') goto yy383; + if (yych <= '5') goto yy483; + if (yych <= '9') goto yy482; + goto yy1; + } +yy386: + yych = *++YYCURSOR; + if (yych <= 'z') { + if (yych <= '@') { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy484; + if (yych <= ':') goto yy299; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 'Z') { + if (yych <= 'F') goto yy484; + goto yy1; + } else { + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 'f') goto yy484; + goto yy1; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + yyt2 = YYCURSOR; + goto yy206; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy387: + yych = *++YYCURSOR; + if (yych <= 'z') { + if (yych <= '@') { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy485; + if (yych <= ':') goto yy486; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 'Z') { + if (yych <= 'F') goto yy485; + goto yy1; + } else { + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 'f') goto yy485; + goto yy1; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + yyt2 = YYCURSOR; + goto yy206; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy388: + yych = *++YYCURSOR; + if (yych <= 'f') { + if (yych <= ':') { + if (yych <= '.') { + if (yych <= '-') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt1 = YYCURSOR; + goto yy297; + } else { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy484; + goto yy299; + } + } else { + if (yych <= 'F') { + if (yych <= '@') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy484; + } else { + if (yych <= 'Z') goto yy1; + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy484; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + if (yych <= 'z') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + yyt2 = YYCURSOR; + goto yy206; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy389: + yych = *++YYCURSOR; + if (yych <= 'f') { + if (yych <= 'E') { + if (yych <= '9') { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy484; + } else { + if (yych <= ':') goto yy299; + if (yych <= '@') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy484; + } + } else { + if (yych <= 'Z') { + if (yych <= 'F') goto yy487; + goto yy1; + } else { + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 'e') goto yy484; + goto yy487; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + if (yych <= 'z') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + yyt2 = YYCURSOR; + goto yy206; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy390: + yych = *++YYCURSOR; + if (yych <= '-') { + if (yych <= ' ') { + if (yych <= '\f') { + if (yych == '\t') goto yy488; + goto yy1; + } else { + if (yych <= '\r') goto yy488; + if (yych <= 0x1F) goto yy1; + goto yy488; + } + } else { + if (yych <= '&') { + if (yych == '"') goto yy306; + goto yy1; + } else { + if (yych <= '\'') goto yy307; + if (yych <= ',') goto yy1; + goto yy390; + } + } + } else { + if (yych <= '@') { + if (yych <= '<') { + if (yych <= '/') goto yy1; + if (yych <= ':') goto yy390; + goto yy1; + } else { + if (yych <= '=') goto yy408; + if (yych <= '>') goto yy309; + goto yy1; + } + } else { + if (yych <= '_') { + if (yych <= 'Z') goto yy390; + if (yych <= '^') goto yy1; + goto yy390; + } else { + if (yych <= '`') goto yy1; + if (yych <= 'z') goto yy390; + goto yy1; + } + } + } +yy391: + yych = *++YYCURSOR; + if (yybm[256+yych] & 8) { + goto yy391; + } + if (yych <= 0xDF) { + if (yych <= '"') { + if (yych <= 0x00) goto yy1; + goto yy489; + } else { + if (yych <= '\\') goto yy392; + if (yych <= 0xC1) goto yy1; + goto yy393; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xE0) goto yy394; + if (yych <= 0xEF) goto yy395; + goto yy396; + } else { + if (yych <= 0xF3) goto yy397; + if (yych <= 0xF4) goto yy398; + goto yy1; + } + } +yy392: + yych = *++YYCURSOR; + if (yych <= 0xDF) { + if (yych <= '\n') { + if (yych <= '\t') goto yy391; + goto yy1; + } else { + if (yych <= 0x7F) goto yy391; + if (yych <= 0xC1) goto yy1; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xE0) goto yy394; + if (yych <= 0xEF) goto yy395; + goto yy396; + } else { + if (yych <= 0xF3) goto yy397; + if (yych <= 0xF4) goto yy398; + goto yy1; + } + } +yy393: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy391; + goto yy1; +yy394: + yych = *++YYCURSOR; + if (yych <= 0x9F) goto yy1; + if (yych <= 0xBF) goto yy393; + goto yy1; +yy395: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy393; + goto yy1; +yy396: + yych = *++YYCURSOR; + if (yych <= 0x8F) goto yy1; + if (yych <= 0xBF) goto yy395; + goto yy1; +yy397: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy395; + goto yy1; +yy398: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0x8F) goto yy395; + goto yy1; +yy399: + yych = *++YYCURSOR; + if (yybm[256+yych] & 16) { + goto yy399; + } + if (yych <= 0xDF) { + if (yych <= '\'') { + if (yych <= 0x00) goto yy1; + goto yy489; + } else { + if (yych <= '\\') goto yy400; + if (yych <= 0xC1) goto yy1; + goto yy401; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xE0) goto yy402; + if (yych <= 0xEF) goto yy403; + goto yy404; + } else { + if (yych <= 0xF3) goto yy405; + if (yych <= 0xF4) goto yy406; + goto yy1; + } + } +yy400: + yych = *++YYCURSOR; + if (yych <= 0xDF) { + if (yych <= '\n') { + if (yych <= '\t') goto yy399; + goto yy1; + } else { + if (yych <= 0x7F) goto yy399; + if (yych <= 0xC1) goto yy1; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xE0) goto yy402; + if (yych <= 0xEF) goto yy403; + goto yy404; + } else { + if (yych <= 0xF3) goto yy405; + if (yych <= 0xF4) goto yy406; + goto yy1; + } + } +yy401: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy399; + goto yy1; +yy402: + yych = *++YYCURSOR; + if (yych <= 0x9F) goto yy1; + if (yych <= 0xBF) goto yy401; + goto yy1; +yy403: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy401; + goto yy1; +yy404: + yych = *++YYCURSOR; + if (yych <= 0x8F) goto yy1; + if (yych <= 0xBF) goto yy403; + goto yy1; +yy405: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy403; + goto yy1; +yy406: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0x8F) goto yy403; + goto yy1; +yy407: + yych = *++YYCURSOR; + if (yych <= '-') { + if (yych <= ' ') { + if (yych <= '\f') { + if (yych == '\t') goto yy407; + goto yy1; + } else { + if (yych <= '\r') goto yy407; + if (yych <= 0x1F) goto yy1; + goto yy407; + } + } else { + if (yych <= '&') { + if (yych == '"') goto yy306; + goto yy1; + } else { + if (yych <= '\'') goto yy307; + if (yych <= ',') goto yy1; + goto yy390; + } + } + } else { + if (yych <= '@') { + if (yych <= '<') { + if (yych <= '/') goto yy1; + if (yych <= ':') goto yy390; + goto yy1; + } else { + if (yych <= '=') goto yy408; + if (yych <= '>') goto yy309; + goto yy1; + } + } else { + if (yych <= '_') { + if (yych <= 'Z') goto yy390; + if (yych <= '^') goto yy1; + goto yy390; + } else { + if (yych <= '`') goto yy1; + if (yych <= 'z') goto yy390; + goto yy1; + } + } + } +yy408: + yych = *++YYCURSOR; + if (yybm[0+yych] & 1) { + goto yy490; + } + if (yych <= 0xDF) { + if (yych <= '"') { + if (yych <= 0x00) goto yy1; + goto yy491; + } else { + if (yych <= '\'') goto yy492; + if (yych <= 0xC1) goto yy1; + goto yy493; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xE0) goto yy494; + if (yych <= 0xEF) goto yy495; + goto yy496; + } else { + if (yych <= 0xF3) goto yy497; + if (yych <= 0xF4) goto yy498; + goto yy1; + } + } +yy409: + yych = *++YYCURSOR; + if (yych <= ' ') { + if (yych <= '\f') { + if (yych == '\t') goto yy409; + goto yy1; + } else { + if (yych <= '\r') goto yy409; + if (yych <= 0x1F) goto yy1; + goto yy409; + } + } else { + if (yych <= '<') { + if (yych == '/') goto yy215; + goto yy1; + } else { + if (yych <= '=') goto yy313; + if (yych <= '>') goto yy216; + if (yych <= '?') goto yy215; + goto yy1; + } + } +yy410: + yych = *++YYCURSOR; + if (yybm[256+yych] & 32) { + goto yy410; + } + if (yych <= 'z') { + if (yych <= '/') { + if (yych <= 0x00) goto yy1; + if (yych <= '-') goto yy499; + goto yy500; + } else { + if (yych <= ':') goto yy499; + if (yych <= '>') goto yy216; + if (yych <= '?') goto yy500; + goto yy499; + } + } else { + if (yych <= 0xEF) { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) goto yy414; + if (yych <= 0xE0) goto yy415; + goto yy416; + } else { + if (yych <= 0xF0) goto yy417; + if (yych <= 0xF3) goto yy418; + if (yych <= 0xF4) goto yy419; + goto yy1; + } + } +yy411: + yych = *++YYCURSOR; + if (yybm[0+yych] & 16) { + goto yy500; + } + if (yych <= '=') { + if (yych <= '!') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy1; + if (yych == '\t') goto yy411; + goto yy410; + } else { + if (yych <= '\r') goto yy411; + if (yych == ' ') goto yy411; + goto yy410; + } + } else { + if (yych <= ',') { + if (yych <= '"') goto yy412; + if (yych == '\'') goto yy413; + goto yy410; + } else { + if (yych == '.') goto yy410; + if (yych <= ':') goto yy499; + goto yy410; + } + } + } else { + if (yych <= 0x7F) { + if (yych <= '^') { + if (yych <= '>') goto yy216; + if (yych <= '@') goto yy410; + if (yych <= 'Z') goto yy499; + goto yy410; + } else { + if (yych == '`') goto yy410; + if (yych <= 'z') goto yy499; + goto yy410; + } + } else { + if (yych <= 0xEF) { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) goto yy414; + if (yych <= 0xE0) goto yy415; + goto yy416; + } else { + if (yych <= 0xF0) goto yy417; + if (yych <= 0xF3) goto yy418; + if (yych <= 0xF4) goto yy419; + goto yy1; + } + } + } +yy412: + yych = *++YYCURSOR; + if (yybm[256+yych] & 64) { + goto yy412; + } + if (yych <= '\\') { + if (yych <= '/') { + if (yych <= 0x00) goto yy1; + if (yych <= '"') goto yy410; + if (yych <= '-') goto yy501; + goto yy502; + } else { + if (yych <= '>') { + if (yych <= ':') goto yy501; + goto yy503; + } else { + if (yych <= '?') goto yy502; + if (yych <= 'Z') goto yy501; + goto yy504; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 'z') goto yy501; + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) goto yy505; + goto yy506; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) goto yy507; + goto yy508; + } else { + if (yych <= 0xF3) goto yy509; + if (yych <= 0xF4) goto yy510; + goto yy1; + } + } + } +yy413: + yych = *++YYCURSOR; + if (yybm[256+yych] & 128) { + goto yy413; + } + if (yych <= '\\') { + if (yych <= '/') { + if (yych <= 0x00) goto yy1; + if (yych <= '\'') goto yy410; + if (yych <= '-') goto yy511; + goto yy512; + } else { + if (yych <= '>') { + if (yych <= ':') goto yy511; + goto yy513; + } else { + if (yych <= '?') goto yy512; + if (yych <= 'Z') goto yy511; + goto yy514; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 'z') goto yy511; + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) goto yy515; + goto yy516; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) goto yy517; + goto yy518; + } else { + if (yych <= 0xF3) goto yy519; + if (yych <= 0xF4) goto yy520; + goto yy1; + } + } + } +yy414: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy410; + goto yy1; +yy415: + yych = *++YYCURSOR; + if (yych <= 0x9F) goto yy1; + if (yych <= 0xBF) goto yy414; + goto yy1; +yy416: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy414; + goto yy1; +yy417: + yych = *++YYCURSOR; + if (yych <= 0x8F) goto yy1; + if (yych <= 0xBF) goto yy416; + goto yy1; +yy418: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy416; + goto yy1; +yy419: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0x8F) goto yy416; + goto yy1; +yy420: + yych = *++YYCURSOR; + if (yych <= ':') { + if (yych <= 0x1F) { + if (yych <= '\t') { + if (yych <= 0x08) goto yy1; + goto yy521; + } else { + if (yych == '\r') goto yy521; + goto yy1; + } + } else { + if (yych <= '-') { + if (yych <= ' ') goto yy521; + if (yych <= ',') goto yy1; + goto yy420; + } else { + if (yych <= '.') goto yy1; + if (yych <= '/') goto yy215; + goto yy420; + } + } + } else { + if (yych <= '@') { + if (yych <= '=') { + if (yych <= '<') goto yy1; + goto yy422; + } else { + if (yych == '?') goto yy215; + goto yy1; + } + } else { + if (yych <= '_') { + if (yych <= 'Z') goto yy420; + if (yych <= '^') goto yy1; + goto yy420; + } else { + if (yych <= '`') goto yy1; + if (yych <= 'z') goto yy420; + goto yy1; + } + } + } +yy421: + yych = *++YYCURSOR; + if (yych <= ':') { + if (yych <= 0x1F) { + if (yych <= '\t') { + if (yych <= 0x08) goto yy1; + goto yy421; + } else { + if (yych == '\r') goto yy421; + goto yy1; + } + } else { + if (yych <= '-') { + if (yych <= ' ') goto yy421; + if (yych <= ',') goto yy1; + goto yy420; + } else { + if (yych <= '.') goto yy1; + if (yych <= '/') goto yy215; + goto yy420; + } + } + } else { + if (yych <= '@') { + if (yych <= '=') { + if (yych <= '<') goto yy1; + } else { + if (yych == '?') goto yy215; + goto yy1; + } + } else { + if (yych <= '_') { + if (yych <= 'Z') goto yy420; + if (yych <= '^') goto yy1; + goto yy420; + } else { + if (yych <= '`') goto yy1; + if (yych <= 'z') goto yy420; + goto yy1; + } + } + } +yy422: + yych = *++YYCURSOR; + if (yych <= '\'') { + if (yych <= '\r') { + if (yych <= 0x08) { + if (yych <= 0x00) goto yy1; + goto yy522; + } else { + if (yych <= '\t') goto yy523; + if (yych <= '\f') goto yy522; + goto yy523; + } + } else { + if (yych <= '!') { + if (yych == ' ') goto yy523; + goto yy522; + } else { + if (yych <= '"') goto yy524; + if (yych <= '&') goto yy522; + goto yy525; + } + } + } else { + if (yych <= 0xDF) { + if (yych <= '>') { + if (yych <= '=') goto yy522; + goto yy1; + } else { + if (yych <= 0x7F) goto yy522; + if (yych <= 0xC1) goto yy1; + goto yy526; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xE0) goto yy527; + if (yych <= 0xEF) goto yy528; + goto yy529; + } else { + if (yych <= 0xF3) goto yy530; + if (yych <= 0xF4) goto yy531; + goto yy1; + } + } + } +yy423: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= ':') { + if (yych <= '+') { + if (yych == '%') goto yy80; + if (yych <= '*') goto yy4; + goto yy80; + } else { + if (yych <= '.') { + if (yych <= ',') goto yy4; + goto yy46; + } else { + if (yych <= '/') goto yy4; + if (yych <= '9') goto yy532; + goto yy148; + } + } + } else { + if (yych <= '^') { + if (yych <= '@') { + if (yych <= '?') goto yy4; + goto yy96; + } else { + if (yych <= 'F') goto yy532; + if (yych <= 'Z') goto yy115; + goto yy4; + } + } else { + if (yych <= '`') { + if (yych <= '_') goto yy46; + goto yy4; + } else { + if (yych <= 'f') goto yy532; + if (yych <= 'z') goto yy115; + goto yy4; + } + } + } +yy424: + yych = *++YYCURSOR; + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy533; + goto yy1; +yy425: + yyaccept = 15; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '9') { + if (yych <= '%') { + if (yych <= '$') goto yy222; + goto yy461; + } else { + if (yych <= '.') goto yy222; + if (yych <= '/') goto yy189; + goto yy192; + } + } else { + if (yych <= 'F') { + if (yych <= ':') goto yy462; + if (yych <= '@') goto yy222; + goto yy192; + } else { + if (yych <= '`') goto yy222; + if (yych <= 'f') goto yy192; + goto yy222; + } + } +yy426: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= ',') { + if (yych <= '!') { + if (yych <= '\f') { + if (yych <= 0x00) { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych <= 0x08) goto yy4; + if (yych <= '\n') { + yyt4 = YYCURSOR; + goto yy221; + } + goto yy4; + } else { + if (yych <= '\r') { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych <= 0x1F) goto yy4; + if (yych <= ' ') { + yyt4 = YYCURSOR; + goto yy221; + } + yyt4 = YYCURSOR; + goto yy223; + } + } else { + if (yych <= '&') { + if (yych <= '"') { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych == '%') goto yy80; + goto yy4; + } else { + if (yych <= '\'') { + yyt4 = YYCURSOR; + goto yy224; + } + if (yych <= '*') { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych <= '+') goto yy80; + yyt4 = YYCURSOR; + goto yy223; + } + } + } else { + if (yych <= '?') { + if (yych <= '9') { + if (yych <= '-') goto yy46; + if (yych <= '.') { + yyt4 = YYCURSOR; + goto yy230; + } + if (yych <= '/') goto yy4; + goto yy532; + } else { + if (yych <= ':') { + yyt3 = YYCURSOR; + goto yy234; + } + if (yych <= ';') { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych <= '>') goto yy4; + yyt4 = YYCURSOR; + goto yy223; + } + } else { + if (yych <= '^') { + if (yych <= '@') goto yy96; + if (yych <= 'F') goto yy532; + if (yych <= 'Z') goto yy115; + goto yy4; + } else { + if (yych <= '`') { + if (yych <= '_') goto yy46; + goto yy4; + } else { + if (yych <= 'f') goto yy534; + if (yych <= 'z') goto yy326; + goto yy4; + } + } + } + } +yy427: + ++YYCURSOR; +yy428: + YYCURSOR = yyt1; +#line 262 "../../lnav/src/data_scanner_re.re" + { RET(DT_CONSTANT); } +#line 14275 "../../lnav/src/data_scanner_re.cc" +yy429: + yych = *++YYCURSOR; + if (yych <= '\f') { + if (yych == '\t') goto yy427; + goto yy428; + } else { + if (yych <= '\r') goto yy427; + if (yych == ' ') goto yy427; + goto yy428; + } +yy430: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[1024+yych] & 16) { + goto yy46; + } + if (yych <= ' ') { + if (yych <= '\f') { + if (yych == '\t') goto yy427; + goto yy4; + } else { + if (yych <= '\r') goto yy427; + if (yych <= 0x1F) goto yy4; + goto yy427; + } + } else { + if (yych <= '*') { + if (yych == '%') goto yy80; + goto yy4; + } else { + if (yych <= '+') goto yy80; + if (yych == '@') goto yy96; + goto yy4; + } + } +yy431: + yyaccept = 23; + yych = *(YYMARKER = ++YYCURSOR); + if (yych == '/') goto yy189; + if (yych == ':') goto yy152; + goto yy428; +yy432: + yyaccept = 23; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[768+yych] & 128) { + goto yy137; + } + if (yych <= '*') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy225; + if (yych <= 0x08) goto yy428; + if (yych <= '\n') goto yy225; + goto yy428; + } else { + if (yych <= 0x1F) { + if (yych <= '\r') goto yy225; + goto yy428; + } else { + if (yych <= '"') goto yy225; + if (yych <= '&') goto yy428; + goto yy225; + } + } + } else { + if (yych <= '.') { + if (yych == ',') goto yy225; + if (yych <= '-') goto yy428; + yyt4 = YYCURSOR; + goto yy226; + } else { + if (yych <= ';') { + if (yych <= '9') goto yy428; + goto yy225; + } else { + if (yych == '?') goto yy225; + goto yy428; + } + } + } +yy433: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '.') { + if (yych <= '!') { + if (yych <= '\r') { + if (yych <= 0x08) { + if (yych <= 0x00) { + yyt4 = YYCURSOR; + goto yy221; + } + goto yy3; + } else { + if (yych <= '\n') { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych <= '\f') goto yy3; + yyt4 = YYCURSOR; + goto yy221; + } + } else { + if (yych <= 0x1B) { + if (yych <= 0x1A) goto yy3; + goto yy5; + } else { + if (yych <= 0x1F) goto yy3; + if (yych <= ' ') { + yyt4 = YYCURSOR; + goto yy221; + } + yyt4 = YYCURSOR; + goto yy223; + } + } + } else { + if (yych <= '&') { + if (yych <= '#') { + if (yych <= '"') { + yyt4 = YYCURSOR; + goto yy221; + } + goto yy5; + } else { + if (yych <= '$') goto yy3; + if (yych <= '%') goto yy80; + goto yy5; + } + } else { + if (yych <= '+') { + if (yych <= '\'') { + yyt4 = YYCURSOR; + goto yy224; + } + if (yych <= '*') { + yyt4 = YYCURSOR; + goto yy221; + } + goto yy80; + } else { + if (yych <= ',') { + yyt4 = YYCURSOR; + goto yy223; + } + if (yych <= '-') goto yy46; + yyt4 = YYCURSOR; + goto yy230; + } + } + } + } else { + if (yych <= '`') { + if (yych <= '>') { + if (yych <= '9') { + if (yych <= '/') goto yy3; + goto yy46; + } else { + if (yych <= ':') { + yyt3 = YYCURSOR; + goto yy535; + } + if (yych <= ';') { + yyt4 = YYCURSOR; + goto yy221; + } + goto yy5; + } + } else { + if (yych <= 'Z') { + if (yych <= '?') { + yyt4 = YYCURSOR; + goto yy223; + } + if (yych <= '@') goto yy96; + goto yy46; + } else { + if (yych == '_') goto yy46; + goto yy5; + } + } + } else { + if (yych <= 0xDF) { + if (yych <= '~') { + if (yych <= 'z') goto yy433; + goto yy5; + } else { + if (yych <= 0x7F) goto yy3; + if (yych <= 0xC1) goto yy5; + goto yy56; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xE0) goto yy57; + if (yych <= 0xEF) goto yy58; + goto yy59; + } else { + if (yych <= 0xF3) goto yy60; + if (yych <= 0xF4) goto yy61; + goto yy5; + } + } + } + } +yy434: + ++YYCURSOR; +yy435: + YYCURSOR = yyt1; +#line 172 "../../lnav/src/data_scanner_re.re" + { RET(DT_TIME); } +#line 14484 "../../lnav/src/data_scanner_re.cc" +yy436: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy434; + goto yy1; +yy437: + yych = *++YYCURSOR; + if (yych <= 0x9F) goto yy1; + if (yych <= 0xBF) goto yy436; + goto yy1; +yy438: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy436; + goto yy1; +yy439: + yych = *++YYCURSOR; + if (yych <= 0x8F) goto yy1; + if (yych <= 0xBF) goto yy438; + goto yy1; +yy440: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy438; + goto yy1; +yy441: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0x8F) goto yy438; + goto yy1; +yy442: + yych = *++YYCURSOR; + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy536; + goto yy1; +yy443: + yych = *++YYCURSOR; + if (yych == 'r') goto yy537; + goto yy100; +yy444: + yyaccept = 5; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= ',') { + if (yych <= 0x1F) { + if (yych <= '\f') { + if (yych <= 0x00) goto yy24; + if (yych <= 0x08) goto yy4; + if (yych <= '\n') goto yy24; + goto yy4; + } else { + if (yych <= '\r') goto yy24; + if (yych == 0x1B) goto yy24; + goto yy4; + } + } else { + if (yych <= '#') { + if (yych == '!') goto yy99; + if (yych <= '"') goto yy24; + goto yy99; + } else { + if (yych <= '&') { + if (yych <= '$') goto yy101; + goto yy99; + } else { + if (yych <= '\'') goto yy24; + if (yych <= '*') goto yy99; + goto yy24; + } + } + } + } else { + if (yych <= '_') { + if (yych <= 'Z') { + if (yych <= '9') goto yy101; + if (yych <= ':') goto yy4; + if (yych <= '?') goto yy24; + goto yy101; + } else { + if (yych <= '\\') { + if (yych <= '[') goto yy24; + goto yy99; + } else { + if (yych <= ']') goto yy24; + if (yych <= '^') goto yy99; + goto yy101; + } + } + } else { + if (yych <= '}') { + if (yych <= '`') goto yy24; + if (yych <= 'a') goto yy538; + if (yych <= 'z') goto yy101; + goto yy24; + } else { + if (yych <= 0x7F) { + if (yych <= '~') goto yy99; + goto yy4; + } else { + if (yych <= 0xC1) goto yy24; + if (yych <= 0xF4) goto yy4; + goto yy24; + } + } + } + } +yy445: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '1') { + if (yych <= '+') { + if (yych == '%') goto yy80; + if (yych <= '*') goto yy4; + goto yy80; + } else { + if (yych <= '.') { + if (yych <= ',') goto yy4; + goto yy46; + } else { + if (yych <= '/') goto yy4; + if (yych <= '0') goto yy539; + goto yy540; + } + } + } else { + if (yych <= 'Z') { + if (yych <= '9') { + if (yych <= '2') goto yy541; + goto yy539; + } else { + if (yych <= '?') goto yy4; + if (yych <= '@') goto yy96; + goto yy46; + } + } else { + if (yych <= '_') { + if (yych <= '^') goto yy4; + goto yy46; + } else { + if (yych <= '`') goto yy4; + if (yych <= 'z') goto yy46; + goto yy4; + } + } + } +yy446: + yyaccept = 20; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '-') { + if (yych <= 0x1B) { + if (yych <= '\n') { + if (yych <= 0x00) goto yy345; + if (yych <= 0x08) goto yy4; + goto yy345; + } else { + if (yych == '\r') goto yy345; + if (yych <= 0x1A) goto yy4; + goto yy345; + } + } else { + if (yych <= '%') { + if (yych <= 0x1F) goto yy4; + if (yych <= '#') goto yy345; + if (yych <= '$') goto yy4; + goto yy80; + } else { + if (yych == '+') goto yy80; + if (yych <= ',') goto yy345; + goto yy255; + } + } + } else { + if (yych <= '^') { + if (yych <= ':') { + if (yych <= '.') goto yy445; + if (yych <= '/') goto yy4; + if (yych >= ':') goto yy4; + } else { + if (yych <= '?') goto yy345; + if (yych <= '@') goto yy96; + if (yych >= '[') goto yy345; + } + } else { + if (yych <= '~') { + if (yych == '`') goto yy345; + if (yych >= '{') goto yy345; + } else { + if (yych <= 0x7F) goto yy4; + if (yych <= 0xC1) goto yy345; + if (yych <= 0xF4) goto yy4; + goto yy345; + } + } + } +yy447: + yyaccept = 20; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '9') { + if (yych <= '#') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy345; + if (yych <= 0x08) goto yy3; + if (yych <= '\n') goto yy345; + goto yy3; + } else { + if (yych <= 0x1A) { + if (yych <= '\r') goto yy345; + goto yy3; + } else { + if (yych <= 0x1B) goto yy345; + if (yych <= 0x1F) goto yy3; + goto yy345; + } + } + } else { + if (yych <= '+') { + if (yych <= '$') goto yy3; + if (yych <= '%') goto yy80; + if (yych <= '*') goto yy345; + goto yy80; + } else { + if (yych <= '-') { + if (yych <= ',') goto yy345; + goto yy255; + } else { + if (yych <= '.') goto yy350; + if (yych <= '/') goto yy3; + goto yy447; + } + } + } + } else { + if (yych <= '~') { + if (yych <= 'Z') { + if (yych <= ':') goto yy62; + if (yych <= '?') goto yy345; + if (yych <= '@') goto yy96; + goto yy447; + } else { + if (yych <= '_') { + if (yych <= '^') goto yy345; + goto yy447; + } else { + if (yych <= '`') goto yy345; + if (yych <= 'z') goto yy447; + goto yy345; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) goto yy3; + if (yych <= 0xC1) goto yy345; + if (yych <= 0xDF) goto yy56; + goto yy57; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) goto yy58; + goto yy59; + } else { + if (yych <= 0xF3) goto yy60; + if (yych <= 0xF4) goto yy61; + goto yy345; + } + } + } + } +yy448: + yyaccept = 20; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '.') { + if (yych <= 0x1F) { + if (yych <= '\f') { + if (yych <= 0x00) goto yy345; + if (yych <= 0x08) goto yy4; + if (yych <= '\n') goto yy345; + goto yy4; + } else { + if (yych <= '\r') goto yy345; + if (yych == 0x1B) goto yy345; + goto yy4; + } + } else { + if (yych <= '*') { + if (yych <= '#') goto yy345; + if (yych <= '$') goto yy4; + if (yych <= '%') goto yy80; + goto yy345; + } else { + if (yych <= '+') goto yy80; + if (yych <= ',') goto yy345; + if (yych <= '-') goto yy255; + goto yy445; + } + } + } else { + if (yych <= '^') { + if (yych <= ':') { + if (yych <= '/') goto yy4; + if (yych <= '5') goto yy446; + if (yych <= '9') goto yy447; + goto yy4; + } else { + if (yych <= '?') goto yy345; + if (yych <= '@') goto yy96; + if (yych <= 'Z') goto yy447; + goto yy345; + } + } else { + if (yych <= '~') { + if (yych == '`') goto yy345; + if (yych <= 'z') goto yy447; + goto yy345; + } else { + if (yych <= 0x7F) goto yy4; + if (yych <= 0xC1) goto yy345; + if (yych <= 0xF4) goto yy4; + goto yy345; + } + } + } +yy449: + yyaccept = 24; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '9') { + if (yych <= '+') { + if (yych == '%') goto yy80; + if (yych >= '+') goto yy80; + } else { + if (yych <= ',') goto yy450; + if (yych <= '.') goto yy80; + if (yych >= '0') goto yy449; + } + } else { + if (yych <= '^') { + if (yych <= '?') goto yy450; + if (yych <= '@') goto yy82; + if (yych <= 'Z') goto yy80; + } else { + if (yych == '`') goto yy450; + if (yych <= 'z') goto yy80; + } + } +yy450: +#line 249 "../../lnav/src/data_scanner_re.re" + { RET(DT_NUMBER); } +#line 14829 "../../lnav/src/data_scanner_re.cc" +yy451: + yyaccept = 24; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '9') { + if (yych <= 0x1F) { + if (yych <= '\f') { + if (yych <= 0x00) goto yy450; + if (yych <= 0x08) goto yy3; + if (yych <= '\n') goto yy450; + goto yy3; + } else { + if (yych <= '\r') goto yy450; + if (yych == 0x1B) goto yy450; + goto yy3; + } + } else { + if (yych <= '*') { + if (yych <= '#') goto yy450; + if (yych <= '$') goto yy3; + if (yych <= '%') goto yy80; + goto yy450; + } else { + if (yych <= ',') { + if (yych <= '+') goto yy80; + goto yy450; + } else { + if (yych <= '.') goto yy46; + if (yych <= '/') goto yy3; + goto yy451; + } + } + } + } else { + if (yych <= '~') { + if (yych <= 'Z') { + if (yych <= ':') goto yy62; + if (yych <= '?') goto yy450; + if (yych <= '@') goto yy96; + goto yy344; + } else { + if (yych <= '_') { + if (yych <= '^') goto yy450; + goto yy344; + } else { + if (yych <= '`') goto yy450; + if (yych <= 'z') goto yy344; + goto yy450; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) goto yy3; + if (yych <= 0xC1) goto yy450; + if (yych <= 0xDF) goto yy56; + goto yy57; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) goto yy58; + goto yy59; + } else { + if (yych <= 0xF3) goto yy60; + if (yych <= 0xF4) goto yy61; + goto yy450; + } + } + } + } +yy452: + yych = *++YYCURSOR; + if (yych <= '@') { + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy542; + if (yych <= ':') goto yy152; + goto yy1; + } else { + if (yych <= 'F') goto yy542; + if (yych <= '`') goto yy1; + if (yych <= 'f') goto yy542; + goto yy1; + } +yy453: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych == '/') goto yy543; + goto yy4; +yy454: + yych = *++YYCURSOR; + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy544; + goto yy1; +yy455: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '/') { + if (yych <= '+') { + if (yych == '%') goto yy80; + if (yych <= '*') goto yy4; + goto yy80; + } else { + if (yych <= ',') goto yy4; + if (yych <= '-') goto yy545; + if (yych <= '.') goto yy46; + goto yy4; + } + } else { + if (yych <= 'Z') { + if (yych <= '9') goto yy546; + if (yych <= '?') goto yy4; + if (yych <= '@') goto yy96; + goto yy46; + } else { + if (yych <= '_') { + if (yych <= '^') goto yy4; + goto yy46; + } else { + if (yych <= '`') goto yy4; + if (yych <= 'z') goto yy46; + goto yy4; + } + } + } +yy456: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '.') goto yy4; + if (yych <= '/') goto yy547; + if (yych <= '9') goto yy548; + goto yy4; +yy457: + yyaccept = 11; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[768+yych] & 32) { + goto yy103; + } + if (yych <= '9') { + if (yych <= '#') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy109; + if (yych <= 0x08) goto yy4; + if (yych <= '\n') goto yy109; + goto yy4; + } else { + if (yych <= 0x1A) { + if (yych <= '\r') goto yy109; + goto yy4; + } else { + if (yych <= 0x1B) goto yy109; + if (yych <= 0x1F) goto yy4; + goto yy109; + } + } + } else { + if (yych <= ',') { + if (yych <= '%') { + if (yych <= '$') goto yy4; + goto yy105; + } else { + if (yych == '+') goto yy80; + goto yy109; + } + } else { + if (yych <= '.') { + if (yych <= '-') goto yy46; + goto yy266; + } else { + if (yych <= '/') goto yy4; + if (yych <= '7') goto yy549; + goto yy550; + } + } + } + } else { + if (yych <= '_') { + if (yych <= 'D') { + if (yych <= ':') goto yy148; + if (yych <= '?') goto yy109; + if (yych <= '@') goto yy96; + goto yy551; + } else { + if (yych <= 'F') { + if (yych <= 'E') goto yy552; + goto yy551; + } else { + if (yych <= 'Z') goto yy115; + if (yych <= '^') goto yy109; + goto yy46; + } + } + } else { + if (yych <= 'z') { + if (yych <= 'd') { + if (yych <= '`') goto yy109; + goto yy551; + } else { + if (yych <= 'e') goto yy552; + if (yych <= 'f') goto yy551; + goto yy115; + } + } else { + if (yych <= 0x7F) { + if (yych <= '~') goto yy109; + goto yy4; + } else { + if (yych <= 0xC1) goto yy109; + if (yych <= 0xF4) goto yy4; + goto yy109; + } + } + } + } +yy458: + yyaccept = 6; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[768+yych] & 32) { + goto yy103; + } + if (yych <= '9') { + if (yych <= '#') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy26; + if (yych <= 0x08) goto yy4; + if (yych <= '\n') goto yy26; + goto yy4; + } else { + if (yych <= 0x1A) { + if (yych <= '\r') goto yy26; + goto yy4; + } else { + if (yych <= 0x1B) goto yy26; + if (yych <= 0x1F) goto yy4; + goto yy26; + } + } + } else { + if (yych <= '+') { + if (yych <= '$') goto yy4; + if (yych <= '%') goto yy105; + if (yych <= '*') goto yy26; + goto yy80; + } else { + if (yych <= '-') { + if (yych <= ',') goto yy26; + goto yy46; + } else { + if (yych <= '.') goto yy266; + if (yych <= '/') goto yy4; + goto yy550; + } + } + } + } else { + if (yych <= '_') { + if (yych <= 'D') { + if (yych <= ':') goto yy148; + if (yych <= '?') goto yy26; + if (yych <= '@') goto yy96; + goto yy551; + } else { + if (yych <= 'F') { + if (yych <= 'E') goto yy552; + goto yy551; + } else { + if (yych <= 'Z') goto yy115; + if (yych <= '^') goto yy26; + goto yy46; + } + } + } else { + if (yych <= 'z') { + if (yych <= 'd') { + if (yych <= '`') goto yy26; + goto yy551; + } else { + if (yych <= 'e') goto yy552; + if (yych <= 'f') goto yy551; + goto yy115; + } + } else { + if (yych <= 0x7F) { + if (yych <= '~') goto yy26; + goto yy4; + } else { + if (yych <= 0xC1) goto yy26; + if (yych <= 0xF4) goto yy4; + goto yy26; + } + } + } + } +yy459: + yyaccept = 12; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '/') { + if (yych <= 0x1F) { + if (yych <= '\f') { + if (yych <= 0x00) goto yy113; + if (yych <= 0x08) goto yy4; + if (yych <= '\n') goto yy113; + goto yy4; + } else { + if (yych <= '\r') goto yy113; + if (yych == 0x1B) goto yy113; + goto yy4; + } + } else { + if (yych <= '*') { + if (yych <= '#') goto yy113; + if (yych <= '$') goto yy4; + if (yych <= '%') goto yy80; + goto yy113; + } else { + if (yych <= '+') goto yy80; + if (yych <= ',') goto yy113; + if (yych <= '.') goto yy46; + goto yy4; + } + } + } else { + if (yych <= '_') { + if (yych <= '@') { + if (yych <= '9') goto yy551; + if (yych <= ':') goto yy148; + if (yych <= '?') goto yy113; + goto yy96; + } else { + if (yych <= 'F') goto yy551; + if (yych <= 'Z') goto yy115; + if (yych <= '^') goto yy113; + goto yy46; + } + } else { + if (yych <= '~') { + if (yych <= '`') goto yy113; + if (yych <= 'f') goto yy551; + if (yych <= 'z') goto yy115; + goto yy113; + } else { + if (yych <= 0x7F) goto yy4; + if (yych <= 0xC1) goto yy113; + if (yych <= 0xF4) goto yy4; + goto yy113; + } + } + } +yy460: + yyaccept = 12; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '.') { + if (yych <= 0x1F) { + if (yych <= '\f') { + if (yych <= 0x00) goto yy113; + if (yych <= 0x08) goto yy4; + if (yych <= '\n') goto yy113; + goto yy4; + } else { + if (yych <= '\r') goto yy113; + if (yych == 0x1B) goto yy113; + goto yy4; + } + } else { + if (yych <= '*') { + if (yych <= '#') goto yy113; + if (yych <= '$') goto yy4; + if (yych <= '%') goto yy80; + goto yy113; + } else { + if (yych <= '+') goto yy194; + if (yych <= ',') goto yy113; + if (yych <= '-') goto yy251; + goto yy46; + } + } + } else { + if (yych <= '^') { + if (yych <= '?') { + if (yych <= '/') goto yy4; + if (yych <= '9') goto yy551; + if (yych <= ':') goto yy148; + goto yy113; + } else { + if (yych <= '@') goto yy96; + if (yych <= 'F') goto yy551; + if (yych <= 'Z') goto yy115; + goto yy113; + } + } else { + if (yych <= 'z') { + if (yych <= '_') goto yy46; + if (yych <= '`') goto yy113; + if (yych <= 'f') goto yy551; + goto yy115; + } else { + if (yych <= 0x7F) { + if (yych <= '~') goto yy113; + goto yy4; + } else { + if (yych <= 0xC1) goto yy113; + if (yych <= 0xF4) goto yy4; + goto yy113; + } + } + } + } +yy461: + yych = *++YYCURSOR; + if (yych <= '@') { + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy553; + goto yy1; + } else { + if (yych <= 'Z') goto yy553; + if (yych <= '`') goto yy1; + if (yych <= 'z') goto yy553; + goto yy1; + } +yy462: + yych = *++YYCURSOR; + if (yych <= '?') { + if (yych <= '#') { + if (yych <= '\f') { + if (yych <= 0x00) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0x08) { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= '\n') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= 0x1A) { + if (yych <= '\r') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= 0x1B) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0x1F) { + yyt2 = YYCURSOR; + goto yy284; + } + yyt2 = YYCURSOR; + goto yy198; + } + } + } else { + if (yych <= '0') { + if (yych <= '$') { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= ',') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy284; + } + goto yy554; + } else { + if (yych <= '2') { + if (yych <= '1') goto yy555; + goto yy556; + } else { + if (yych <= '9') goto yy554; + if (yych <= ':') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } + } + } + } else { + if (yych <= '~') { + if (yych <= '^') { + if (yych <= '@') { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= 'F') goto yy557; + if (yych <= 'Z') goto yy3; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= '`') { + if (yych <= '_') { + yyt2 = YYCURSOR; + goto yy284; + } + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 'f') goto yy557; + if (yych <= 'z') goto yy3; + yyt2 = YYCURSOR; + goto yy198; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy289; + } + yyt2 = YYCURSOR; + goto yy290; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy291; + } + yyt2 = YYCURSOR; + goto yy292; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy293; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy294; + } + goto yy1; + } + } + } + } +yy463: + yyaccept = 25; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '@') { + if (yych <= '/') goto yy435; + if (yych <= '9') goto yy542; + goto yy435; + } else { + if (yych <= 'F') goto yy542; + if (yych <= '`') goto yy435; + if (yych <= 'f') goto yy542; + goto yy435; + } +yy464: + yyaccept = 25; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '@') { + if (yych <= '/') goto yy435; + if (yych <= '9') goto yy468; + if (yych <= ':') goto yy282; + goto yy435; + } else { + if (yych <= 'F') goto yy468; + if (yych <= '`') goto yy435; + if (yych <= 'f') goto yy468; + goto yy435; + } +yy465: + yych = *++YYCURSOR; + if (yych <= '@') { + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy558; + if (yych <= ':') goto yy375; + goto yy1; + } else { + if (yych <= 'F') goto yy559; + if (yych <= '`') goto yy1; + if (yych <= 'f') goto yy559; + goto yy1; + } +yy466: + yych = *++YYCURSOR; + if (yych <= '@') { + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy542; + goto yy1; + } else { + if (yych <= 'F') goto yy542; + if (yych <= '`') goto yy1; + if (yych <= 'f') goto yy542; + goto yy1; + } +yy467: + yych = *++YYCURSOR; + if (yych <= '@') { + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy559; + if (yych <= ':') goto yy375; + goto yy1; + } else { + if (yych <= 'F') goto yy559; + if (yych <= '`') goto yy1; + if (yych <= 'f') goto yy559; + goto yy1; + } +yy468: + yych = *++YYCURSOR; + if (yych == ':') goto yy282; + goto yy1; +yy469: + yych = *++YYCURSOR; + if (yych <= '@') { + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy560; + if (yych <= ':') goto yy471; + goto yy1; + } else { + if (yych <= 'F') goto yy470; + if (yych <= '`') goto yy1; + if (yych >= 'g') goto yy1; + } +yy470: + yych = *++YYCURSOR; + if (yych <= '@') { + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy561; + if (yych >= ';') goto yy1; + } else { + if (yych <= 'F') goto yy561; + if (yych <= '`') goto yy1; + if (yych <= 'f') goto yy561; + goto yy1; + } +yy471: + yych = *++YYCURSOR; + if (yych <= '@') { + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy562; + if (yych <= ':') goto yy563; + goto yy1; + } else { + if (yych <= 'F') goto yy562; + if (yych <= '`') goto yy1; + if (yych <= 'f') goto yy562; + goto yy1; + } +yy472: + yych = *++YYCURSOR; + if (yych <= 'f') { + if (yych <= ':') { + if (yych <= '.') { + if (yych <= '-') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt1 = YYCURSOR; + goto yy297; + } else { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy564; + goto yy565; + } + } else { + if (yych <= 'F') { + if (yych <= '@') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy566; + } else { + if (yych <= 'Z') goto yy1; + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy566; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + if (yych <= 'z') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + yyt2 = YYCURSOR; + goto yy206; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy473: + yych = *++YYCURSOR; + if (yych <= 'f') { + if (yych <= ':') { + if (yych <= '.') { + if (yych <= '-') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt1 = YYCURSOR; + goto yy297; + } else { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy567; + goto yy565; + } + } else { + if (yych <= 'F') { + if (yych <= '@') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy566; + } else { + if (yych <= 'Z') goto yy1; + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy566; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + if (yych <= 'z') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + yyt2 = YYCURSOR; + goto yy206; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy474: + yych = *++YYCURSOR; + if (yych <= '`') { + if (yych <= '5') { + if (yych <= '.') { + if (yych <= '-') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt1 = YYCURSOR; + goto yy297; + } else { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '4') goto yy567; + goto yy568; + } + } else { + if (yych <= '@') { + if (yych <= '9') goto yy564; + if (yych <= ':') goto yy565; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 'F') goto yy566; + if (yych <= 'Z') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } + } + } else { + if (yych <= 0xDF) { + if (yych <= 'z') { + if (yych <= 'f') goto yy566; + goto yy1; + } else { + if (yych <= 0x7F) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0xC1) goto yy1; + yyt2 = YYCURSOR; + goto yy205; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xE0) { + yyt2 = YYCURSOR; + goto yy206; + } + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy475: + yych = *++YYCURSOR; + if (yych <= 'z') { + if (yych <= '@') { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy566; + if (yych <= ':') goto yy565; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 'Z') { + if (yych <= 'F') goto yy566; + goto yy1; + } else { + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 'f') goto yy566; + goto yy1; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + yyt2 = YYCURSOR; + goto yy206; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy476: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych == '.') goto yy569; + if (yych <= '/') goto yy4; + if (yych <= '9') goto yy570; + goto yy4; +yy477: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych == '.') goto yy569; + if (yych <= '/') goto yy4; + if (yych <= '9') goto yy476; + goto yy4; +yy478: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '/') { + if (yych == '.') goto yy569; + goto yy4; + } else { + if (yych <= '4') goto yy476; + if (yych <= '5') goto yy571; + if (yych <= '9') goto yy570; + goto yy4; + } +yy479: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '@') { + if (yych <= 0x1F) { + if (yych <= '\f') { + if (yych <= 0x00) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0x08) { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= '\n') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= '\r') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych == 0x1B) { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } + } else { + if (yych <= '/') { + if (yych == '$') { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= ',') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= '9') goto yy572; + if (yych <= ':') goto yy378; + if (yych <= '?') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } + } + } else { + if (yych <= '~') { + if (yych <= '_') { + if (yych <= 'F') goto yy572; + if (yych <= 'Z') goto yy3; + if (yych <= '^') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 'f') goto yy572; + if (yych <= 'z') goto yy3; + yyt2 = YYCURSOR; + goto yy198; + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= 0xC1) goto yy5; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy289; + } + yyt2 = YYCURSOR; + goto yy290; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy291; + } + yyt2 = YYCURSOR; + goto yy292; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy293; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy294; + } + goto yy5; + } + } + } + } +yy480: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '?') { + if (yych <= 0x1F) { + if (yych <= '\f') { + if (yych <= 0x00) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0x08) { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= '\n') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= '\r') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych == 0x1B) { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } + } else { + if (yych <= '-') { + if (yych == '$') { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= ',') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= '/') { + if (yych <= '.') { + yyt2 = YYCURSOR; + goto yy376; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= '9') goto yy572; + if (yych <= ':') goto yy378; + yyt2 = YYCURSOR; + goto yy198; + } + } + } + } else { + if (yych <= '~') { + if (yych <= '^') { + if (yych <= '@') { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= 'F') goto yy572; + if (yych <= 'Z') goto yy3; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= '`') { + if (yych <= '_') { + yyt2 = YYCURSOR; + goto yy284; + } + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 'f') goto yy572; + if (yych <= 'z') goto yy3; + yyt2 = YYCURSOR; + goto yy198; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= 0xC1) goto yy5; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy289; + } + yyt2 = YYCURSOR; + goto yy290; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy291; + } + yyt2 = YYCURSOR; + goto yy292; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy293; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy294; + } + goto yy5; + } + } + } + } +yy481: + yych = *++YYCURSOR; + if (yych <= '1') { + if (yych <= '/') goto yy1; + if (yych <= '0') goto yy573; + goto yy574; + } else { + if (yych <= '2') goto yy575; + if (yych <= '9') goto yy573; + goto yy1; + } +yy482: + yych = *++YYCURSOR; + if (yych == '.') goto yy481; + goto yy1; +yy483: + yych = *++YYCURSOR; + if (yych == '.') goto yy481; + if (yych <= '/') goto yy1; + if (yych <= '5') goto yy482; + goto yy1; +yy484: + yych = *++YYCURSOR; + if (yych <= 0x7F) { + if (yych <= '@') { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy1; + if (yych <= ':') goto yy299; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 'Z') goto yy1; + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 'z') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } + } else { + if (yych <= 0xEF) { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + if (yych <= 0xE0) { + yyt2 = YYCURSOR; + goto yy206; + } + yyt2 = YYCURSOR; + goto yy207; + } else { + if (yych <= 0xF0) { + yyt2 = YYCURSOR; + goto yy208; + } + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } +yy485: + yych = *++YYCURSOR; + if (yych <= 'z') { + if (yych <= '@') { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy576; + if (yych >= ';') { + yyt2 = YYCURSOR; + goto yy198; + } + } else { + if (yych <= 'Z') { + if (yych <= 'F') goto yy576; + goto yy1; + } else { + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 'f') goto yy576; + goto yy1; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + yyt2 = YYCURSOR; + goto yy206; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy486: + yych = *++YYCURSOR; + if (yych <= '@') { + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy475; + goto yy1; + } else { + if (yych <= 'F') goto yy475; + if (yych <= '`') goto yy1; + if (yych <= 'f') goto yy475; + goto yy1; + } +yy487: + yych = *++YYCURSOR; + if (yych <= 0x7F) { + if (yych <= '@') { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy1; + if (yych <= ':') goto yy577; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 'Z') goto yy1; + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 'z') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } + } else { + if (yych <= 0xEF) { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + if (yych <= 0xE0) { + yyt2 = YYCURSOR; + goto yy206; + } + yyt2 = YYCURSOR; + goto yy207; + } else { + if (yych <= 0xF0) { + yyt2 = YYCURSOR; + goto yy208; + } + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } +yy488: + yych = *++YYCURSOR; + if (yych <= '!') { + if (yych <= '\f') { + if (yych == '\t') goto yy488; + goto yy1; + } else { + if (yych <= '\r') goto yy488; + if (yych == ' ') goto yy488; + goto yy1; + } + } else { + if (yych <= '\'') { + if (yych <= '"') goto yy306; + if (yych <= '&') goto yy1; + goto yy307; + } else { + if (yych <= '<') goto yy1; + if (yych <= '=') goto yy408; + if (yych <= '>') goto yy309; + goto yy1; + } + } +yy489: + yych = *++YYCURSOR; + if (yych <= ',') { + if (yych <= 0x1F) { + if (yych <= '\t') { + if (yych <= 0x08) goto yy1; + goto yy578; + } else { + if (yych == '\r') goto yy578; + goto yy1; + } + } else { + if (yych <= '"') { + if (yych <= ' ') goto yy578; + if (yych <= '!') goto yy1; + goto yy306; + } else { + if (yych == '\'') goto yy307; + goto yy1; + } + } + } else { + if (yych <= '@') { + if (yych <= ':') { + if (yych <= '-') goto yy390; + if (yych <= '/') goto yy1; + goto yy390; + } else { + if (yych == '>') goto yy309; + goto yy1; + } + } else { + if (yych <= '_') { + if (yych <= 'Z') goto yy390; + if (yych <= '^') goto yy1; + goto yy390; + } else { + if (yych <= '`') goto yy1; + if (yych <= 'z') goto yy390; + goto yy1; + } + } + } +yy490: + yych = *++YYCURSOR; + if (yybm[0+yych] & 1) { + goto yy490; + } + if (yych <= 0xDF) { + if (yych <= '\'') { + if (yych <= 0x00) goto yy1; + if (yych >= '#') goto yy492; + } else { + if (yych <= '>') goto yy309; + if (yych <= 0xC1) goto yy1; + goto yy493; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xE0) goto yy494; + if (yych <= 0xEF) goto yy495; + goto yy496; + } else { + if (yych <= 0xF3) goto yy497; + if (yych <= 0xF4) goto yy498; + goto yy1; + } + } +yy491: + yych = *++YYCURSOR; + if (yybm[0+yych] & 2) { + goto yy491; + } + if (yych <= 0xDF) { + if (yych <= '>') { + if (yych <= 0x00) goto yy1; + if (yych <= '\'') goto yy579; + goto yy580; + } else { + if (yych <= '\\') goto yy581; + if (yych <= 0xC1) goto yy1; + goto yy582; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xE0) goto yy583; + if (yych <= 0xEF) goto yy584; + goto yy585; + } else { + if (yych <= 0xF3) goto yy586; + if (yych <= 0xF4) goto yy587; + goto yy1; + } + } +yy492: + yych = *++YYCURSOR; + if (yybm[0+yych] & 4) { + goto yy492; + } + if (yych <= 0xDF) { + if (yych <= '>') { + if (yych <= 0x00) goto yy1; + if (yych <= '"') goto yy579; + goto yy588; + } else { + if (yych <= '\\') goto yy589; + if (yych <= 0xC1) goto yy1; + goto yy590; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xE0) goto yy591; + if (yych <= 0xEF) goto yy592; + goto yy593; + } else { + if (yych <= 0xF3) goto yy594; + if (yych <= 0xF4) goto yy595; + goto yy1; + } + } +yy493: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy490; + goto yy1; +yy494: + yych = *++YYCURSOR; + if (yych <= 0x9F) goto yy1; + if (yych <= 0xBF) goto yy493; + goto yy1; +yy495: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy493; + goto yy1; +yy496: + yych = *++YYCURSOR; + if (yych <= 0x8F) goto yy1; + if (yych <= 0xBF) goto yy495; + goto yy1; +yy497: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy495; + goto yy1; +yy498: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0x8F) goto yy495; + goto yy1; +yy499: + yych = *++YYCURSOR; + if (yybm[0+yych] & 8) { + goto yy499; + } + if (yych <= 0x7F) { + if (yych <= '<') { + if (yych <= 0x00) goto yy1; + if (yych != '/') goto yy410; + } else { + if (yych <= '=') goto yy411; + if (yych <= '>') goto yy216; + if (yych >= '@') goto yy410; + } + } else { + if (yych <= 0xEF) { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) goto yy414; + if (yych <= 0xE0) goto yy415; + goto yy416; + } else { + if (yych <= 0xF0) goto yy417; + if (yych <= 0xF3) goto yy418; + if (yych <= 0xF4) goto yy419; + goto yy1; + } + } +yy500: + yych = *++YYCURSOR; + if (yybm[256+yych] & 32) { + goto yy410; + } + if (yych <= 'z') { + if (yych <= '/') { + if (yych <= 0x00) goto yy1; + if (yych <= '-') goto yy499; + goto yy500; + } else { + if (yych <= ':') goto yy499; + if (yych <= '>') goto yy314; + if (yych <= '?') goto yy500; + goto yy499; + } + } else { + if (yych <= 0xEF) { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) goto yy414; + if (yych <= 0xE0) goto yy415; + goto yy416; + } else { + if (yych <= 0xF0) goto yy417; + if (yych <= 0xF3) goto yy418; + if (yych <= 0xF4) goto yy419; + goto yy1; + } + } +yy501: + yych = *++YYCURSOR; + if (yych <= '>') { + if (yych <= '!') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy1; + if (yych == '\t') goto yy501; + goto yy412; + } else { + if (yych <= '\r') goto yy501; + if (yych == ' ') goto yy501; + goto yy412; + } + } else { + if (yych <= '.') { + if (yych <= '"') goto yy410; + if (yych == '-') goto yy501; + goto yy412; + } else { + if (yych <= ':') { + if (yych >= '0') goto yy501; + } else { + if (yych <= '<') goto yy412; + if (yych <= '=') goto yy596; + goto yy503; + } + } + } + } else { + if (yych <= 'z') { + if (yych <= '[') { + if (yych <= '?') goto yy502; + if (yych <= '@') goto yy412; + if (yych <= 'Z') goto yy501; + goto yy412; + } else { + if (yych <= '^') { + if (yych <= '\\') goto yy504; + goto yy412; + } else { + if (yych == '`') goto yy412; + goto yy501; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) goto yy412; + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) goto yy505; + goto yy506; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) goto yy507; + goto yy508; + } else { + if (yych <= 0xF3) goto yy509; + if (yych <= 0xF4) goto yy510; + goto yy1; + } + } + } + } +yy502: + yych = *++YYCURSOR; + if (yybm[256+yych] & 64) { + goto yy412; + } + if (yych <= '\\') { + if (yych <= '/') { + if (yych <= 0x00) goto yy1; + if (yych <= '"') goto yy410; + if (yych <= '-') goto yy501; + goto yy502; + } else { + if (yych <= '>') { + if (yych <= ':') goto yy501; + goto yy597; + } else { + if (yych <= '?') goto yy502; + if (yych <= 'Z') goto yy501; + goto yy504; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 'z') goto yy501; + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) goto yy505; + goto yy506; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) goto yy507; + goto yy508; + } else { + if (yych <= 0xF3) goto yy509; + if (yych <= 0xF4) goto yy510; + goto yy1; + } + } + } +yy503: + yyaccept = 26; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= 0xC1) { + if (yych <= '"') { + if (yych <= 0x00) goto yy217; + if (yych <= '!') goto yy598; + goto yy599; + } else { + if (yych == '\\') goto yy600; + if (yych <= 0x7F) goto yy598; + goto yy217; + } + } else { + if (yych <= 0xEF) { + if (yych <= 0xDF) goto yy601; + if (yych <= 0xE0) goto yy602; + goto yy603; + } else { + if (yych <= 0xF0) goto yy604; + if (yych <= 0xF3) goto yy605; + if (yych <= 0xF4) goto yy606; + goto yy217; + } + } +yy504: + yych = *++YYCURSOR; + if (yych <= 'Z') { + if (yych <= '.') { + if (yych <= '\n') { + if (yych <= 0x00) goto yy598; + if (yych <= '\t') goto yy412; + goto yy410; + } else { + if (yych == '-') goto yy501; + goto yy412; + } + } else { + if (yych <= '=') { + if (yych <= '/') goto yy502; + if (yych <= ':') goto yy501; + goto yy412; + } else { + if (yych <= '>') goto yy503; + if (yych <= '?') goto yy502; + if (yych <= '@') goto yy412; + goto yy501; + } + } + } else { + if (yych <= 0xC1) { + if (yych <= '`') { + if (yych == '_') goto yy501; + goto yy412; + } else { + if (yych <= 'z') goto yy501; + if (yych <= 0x7F) goto yy412; + goto yy1; + } + } else { + if (yych <= 0xEF) { + if (yych <= 0xDF) goto yy505; + if (yych <= 0xE0) goto yy506; + goto yy507; + } else { + if (yych <= 0xF0) goto yy508; + if (yych <= 0xF3) goto yy509; + if (yych <= 0xF4) goto yy510; + goto yy1; + } + } + } +yy505: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy412; + goto yy1; +yy506: + yych = *++YYCURSOR; + if (yych <= 0x9F) goto yy1; + if (yych <= 0xBF) goto yy505; + goto yy1; +yy507: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy505; + goto yy1; +yy508: + yych = *++YYCURSOR; + if (yych <= 0x8F) goto yy1; + if (yych <= 0xBF) goto yy507; + goto yy1; +yy509: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy507; + goto yy1; +yy510: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0x8F) goto yy507; + goto yy1; +yy511: + yych = *++YYCURSOR; + if (yych <= '>') { + if (yych <= '&') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy1; + if (yych == '\t') goto yy511; + goto yy413; + } else { + if (yych <= '\r') goto yy511; + if (yych == ' ') goto yy511; + goto yy413; + } + } else { + if (yych <= '.') { + if (yych <= '\'') goto yy410; + if (yych == '-') goto yy511; + goto yy413; + } else { + if (yych <= ':') { + if (yych >= '0') goto yy511; + } else { + if (yych <= '<') goto yy413; + if (yych <= '=') goto yy607; + goto yy513; + } + } + } + } else { + if (yych <= 'z') { + if (yych <= '[') { + if (yych <= '?') goto yy512; + if (yych <= '@') goto yy413; + if (yych <= 'Z') goto yy511; + goto yy413; + } else { + if (yych <= '^') { + if (yych <= '\\') goto yy514; + goto yy413; + } else { + if (yych == '`') goto yy413; + goto yy511; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) goto yy413; + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) goto yy515; + goto yy516; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) goto yy517; + goto yy518; + } else { + if (yych <= 0xF3) goto yy519; + if (yych <= 0xF4) goto yy520; + goto yy1; + } + } + } + } +yy512: + yych = *++YYCURSOR; + if (yybm[256+yych] & 128) { + goto yy413; + } + if (yych <= '\\') { + if (yych <= '/') { + if (yych <= 0x00) goto yy1; + if (yych <= '\'') goto yy410; + if (yych <= '-') goto yy511; + goto yy512; + } else { + if (yych <= '>') { + if (yych <= ':') goto yy511; + goto yy608; + } else { + if (yych <= '?') goto yy512; + if (yych <= 'Z') goto yy511; + goto yy514; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 'z') goto yy511; + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) goto yy515; + goto yy516; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) goto yy517; + goto yy518; + } else { + if (yych <= 0xF3) goto yy519; + if (yych <= 0xF4) goto yy520; + goto yy1; + } + } + } +yy513: + yyaccept = 26; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= 0xC1) { + if (yych <= '\'') { + if (yych <= 0x00) goto yy217; + if (yych <= '&') goto yy609; + goto yy599; + } else { + if (yych == '\\') goto yy610; + if (yych <= 0x7F) goto yy609; + goto yy217; + } + } else { + if (yych <= 0xEF) { + if (yych <= 0xDF) goto yy611; + if (yych <= 0xE0) goto yy612; + goto yy613; + } else { + if (yych <= 0xF0) goto yy614; + if (yych <= 0xF3) goto yy615; + if (yych <= 0xF4) goto yy616; + goto yy217; + } + } +yy514: + yych = *++YYCURSOR; + if (yych <= 'Z') { + if (yych <= '.') { + if (yych <= '\n') { + if (yych <= 0x00) goto yy609; + if (yych <= '\t') goto yy413; + goto yy410; + } else { + if (yych == '-') goto yy511; + goto yy413; + } + } else { + if (yych <= '=') { + if (yych <= '/') goto yy512; + if (yych <= ':') goto yy511; + goto yy413; + } else { + if (yych <= '>') goto yy513; + if (yych <= '?') goto yy512; + if (yych <= '@') goto yy413; + goto yy511; + } + } + } else { + if (yych <= 0xC1) { + if (yych <= '`') { + if (yych == '_') goto yy511; + goto yy413; + } else { + if (yych <= 'z') goto yy511; + if (yych <= 0x7F) goto yy413; + goto yy1; + } + } else { + if (yych <= 0xEF) { + if (yych <= 0xDF) goto yy515; + if (yych <= 0xE0) goto yy516; + goto yy517; + } else { + if (yych <= 0xF0) goto yy518; + if (yych <= 0xF3) goto yy519; + if (yych <= 0xF4) goto yy520; + goto yy1; + } + } + } +yy515: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy413; + goto yy1; +yy516: + yych = *++YYCURSOR; + if (yych <= 0x9F) goto yy1; + if (yych <= 0xBF) goto yy515; + goto yy1; +yy517: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy515; + goto yy1; +yy518: + yych = *++YYCURSOR; + if (yych <= 0x8F) goto yy1; + if (yych <= 0xBF) goto yy517; + goto yy1; +yy519: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy517; + goto yy1; +yy520: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0x8F) goto yy517; + goto yy1; +yy521: + yych = *++YYCURSOR; + if (yych <= ' ') { + if (yych <= '\f') { + if (yych == '\t') goto yy521; + goto yy1; + } else { + if (yych <= '\r') goto yy521; + if (yych <= 0x1F) goto yy1; + goto yy521; + } + } else { + if (yych <= '<') { + if (yych == '/') goto yy215; + goto yy1; + } else { + if (yych <= '=') goto yy422; + if (yych == '?') goto yy215; + goto yy1; + } + } +yy522: + yych = *++YYCURSOR; + if (yych <= '^') { + if (yych <= ':') { + if (yych <= '-') { + if (yych <= 0x00) goto yy1; + if (yych <= ',') goto yy522; + goto yy617; + } else { + if (yych <= '.') goto yy522; + if (yych <= '/') goto yy618; + goto yy617; + } + } else { + if (yych <= '?') { + if (yych <= '=') goto yy522; + if (yych <= '>') goto yy1; + goto yy618; + } else { + if (yych <= '@') goto yy522; + if (yych <= 'Z') goto yy617; + goto yy522; + } + } + } else { + if (yych <= 0xDF) { + if (yych <= 'z') { + if (yych == '`') goto yy522; + goto yy617; + } else { + if (yych <= 0x7F) goto yy522; + if (yych <= 0xC1) goto yy1; + goto yy526; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xE0) goto yy527; + if (yych <= 0xEF) goto yy528; + goto yy529; + } else { + if (yych <= 0xF3) goto yy530; + if (yych <= 0xF4) goto yy531; + goto yy1; + } + } + } +yy523: + yych = *++YYCURSOR; + if (yych <= '=') { + if (yych <= '!') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy1; + if (yych == '\t') goto yy523; + goto yy522; + } else { + if (yych <= '\r') goto yy523; + if (yych == ' ') goto yy523; + goto yy522; + } + } else { + if (yych <= ',') { + if (yych <= '"') goto yy524; + if (yych == '\'') goto yy525; + goto yy522; + } else { + if (yych <= '.') { + if (yych <= '-') goto yy617; + goto yy522; + } else { + if (yych <= '/') goto yy618; + if (yych <= ':') goto yy617; + goto yy522; + } + } + } + } else { + if (yych <= 'z') { + if (yych <= 'Z') { + if (yych <= '>') goto yy1; + if (yych <= '?') goto yy618; + if (yych <= '@') goto yy522; + goto yy617; + } else { + if (yych == '_') goto yy617; + if (yych <= '`') goto yy522; + goto yy617; + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) goto yy522; + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) goto yy526; + goto yy527; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) goto yy528; + goto yy529; + } else { + if (yych <= 0xF3) goto yy530; + if (yych <= 0xF4) goto yy531; + goto yy1; + } + } + } + } +yy524: + yych = *++YYCURSOR; + if (yych <= '[') { + if (yych <= '/') { + if (yych <= '"') { + if (yych <= 0x00) goto yy1; + if (yych <= '!') goto yy524; + goto yy522; + } else { + if (yych == '-') goto yy619; + if (yych <= '.') goto yy524; + goto yy620; + } + } else { + if (yych <= '>') { + if (yych <= ':') goto yy619; + if (yych <= '=') goto yy524; + goto yy621; + } else { + if (yych <= '?') goto yy620; + if (yych <= '@') goto yy524; + if (yych <= 'Z') goto yy619; + goto yy524; + } + } + } else { + if (yych <= 0xC1) { + if (yych <= '_') { + if (yych <= '\\') goto yy622; + if (yych <= '^') goto yy524; + goto yy619; + } else { + if (yych <= '`') goto yy524; + if (yych <= 'z') goto yy619; + if (yych <= 0x7F) goto yy524; + goto yy1; + } + } else { + if (yych <= 0xEF) { + if (yych <= 0xDF) goto yy623; + if (yych <= 0xE0) goto yy624; + goto yy625; + } else { + if (yych <= 0xF0) goto yy626; + if (yych <= 0xF3) goto yy627; + if (yych <= 0xF4) goto yy628; + goto yy1; + } + } + } +yy525: + yych = *++YYCURSOR; + if (yych <= '[') { + if (yych <= '/') { + if (yych <= '\'') { + if (yych <= 0x00) goto yy1; + if (yych <= '&') goto yy525; + goto yy522; + } else { + if (yych == '-') goto yy629; + if (yych <= '.') goto yy525; + goto yy630; + } + } else { + if (yych <= '>') { + if (yych <= ':') goto yy629; + if (yych <= '=') goto yy525; + goto yy631; + } else { + if (yych <= '?') goto yy630; + if (yych <= '@') goto yy525; + if (yych <= 'Z') goto yy629; + goto yy525; + } + } + } else { + if (yych <= 0xC1) { + if (yych <= '_') { + if (yych <= '\\') goto yy632; + if (yych <= '^') goto yy525; + goto yy629; + } else { + if (yych <= '`') goto yy525; + if (yych <= 'z') goto yy629; + if (yych <= 0x7F) goto yy525; + goto yy1; + } + } else { + if (yych <= 0xEF) { + if (yych <= 0xDF) goto yy633; + if (yych <= 0xE0) goto yy634; + goto yy635; + } else { + if (yych <= 0xF0) goto yy636; + if (yych <= 0xF3) goto yy637; + if (yych <= 0xF4) goto yy638; + goto yy1; + } + } + } +yy526: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy522; + goto yy1; +yy527: + yych = *++YYCURSOR; + if (yych <= 0x9F) goto yy1; + if (yych <= 0xBF) goto yy526; + goto yy1; +yy528: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy526; + goto yy1; +yy529: + yych = *++YYCURSOR; + if (yych <= 0x8F) goto yy1; + if (yych <= 0xBF) goto yy528; + goto yy1; +yy530: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy528; + goto yy1; +yy531: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0x8F) goto yy528; + goto yy1; +yy532: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= ':') { + if (yych <= '+') { + if (yych == '%') goto yy80; + if (yych <= '*') goto yy4; + goto yy80; + } else { + if (yych <= '.') { + if (yych <= ',') goto yy4; + goto yy46; + } else { + if (yych <= '/') goto yy4; + if (yych <= '9') goto yy639; + goto yy148; + } + } + } else { + if (yych <= '^') { + if (yych <= '@') { + if (yych <= '?') goto yy4; + goto yy96; + } else { + if (yych <= 'F') goto yy639; + if (yych <= 'Z') goto yy115; + goto yy4; + } + } else { + if (yych <= '`') { + if (yych <= '_') goto yy46; + goto yy4; + } else { + if (yych <= 'f') goto yy639; + if (yych <= 'z') goto yy115; + goto yy4; + } + } + } +yy533: + yych = *++YYCURSOR; + if (yych == ' ') goto yy640; + goto yy1; +yy534: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= ',') { + if (yych <= '!') { + if (yych <= '\f') { + if (yych <= 0x00) { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych <= 0x08) goto yy4; + if (yych <= '\n') { + yyt4 = YYCURSOR; + goto yy221; + } + goto yy4; + } else { + if (yych <= '\r') { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych <= 0x1F) goto yy4; + if (yych <= ' ') { + yyt4 = YYCURSOR; + goto yy221; + } + yyt4 = YYCURSOR; + goto yy223; + } + } else { + if (yych <= '&') { + if (yych <= '"') { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych == '%') goto yy80; + goto yy4; + } else { + if (yych <= '\'') { + yyt4 = YYCURSOR; + goto yy224; + } + if (yych <= '*') { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych <= '+') goto yy80; + yyt4 = YYCURSOR; + goto yy223; + } + } + } else { + if (yych <= '?') { + if (yych <= '9') { + if (yych <= '-') goto yy46; + if (yych <= '.') { + yyt4 = YYCURSOR; + goto yy230; + } + if (yych <= '/') goto yy4; + goto yy639; + } else { + if (yych <= ':') { + yyt3 = YYCURSOR; + goto yy234; + } + if (yych <= ';') { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych <= '>') goto yy4; + yyt4 = YYCURSOR; + goto yy223; + } + } else { + if (yych <= '^') { + if (yych <= '@') goto yy96; + if (yych <= 'F') goto yy639; + if (yych <= 'Z') goto yy115; + goto yy4; + } else { + if (yych <= '`') { + if (yych <= '_') goto yy46; + goto yy4; + } else { + if (yych <= 'f') goto yy641; + if (yych <= 'z') goto yy326; + goto yy4; + } + } + } + } +yy535: + yyaccept = 15; + yych = *(YYMARKER = ++YYCURSOR); + if (yych == ':') goto yy152; + goto yy222; +yy536: + yych = *++YYCURSOR; + if (yych <= 0xC1) { + if (yych <= '9') { + if (yych == '.') { + yyt2 = YYCURSOR; + goto yy644; + } + yyt2 = YYCURSOR; + goto yy642; + } else { + if (yych <= ':') goto yy1; + if (yych <= 0x7F) { + yyt2 = YYCURSOR; + goto yy642; + } + goto yy1; + } + } else { + if (yych <= 0xEF) { + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy645; + } + if (yych <= 0xE0) { + yyt2 = YYCURSOR; + goto yy646; + } + yyt2 = YYCURSOR; + goto yy647; + } else { + if (yych <= 0xF0) { + yyt2 = YYCURSOR; + goto yy648; + } + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy649; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy650; + } + goto yy1; + } + } +yy537: + yych = *++YYCURSOR; + if (yych == 'a') goto yy651; + goto yy100; +yy538: + yyaccept = 5; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '9') { + if (yych <= ' ') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy24; + if (yych <= 0x08) goto yy4; + if (yych <= '\n') goto yy24; + goto yy4; + } else { + if (yych <= 0x1A) { + if (yych <= '\r') goto yy24; + goto yy4; + } else { + if (yych <= 0x1B) goto yy24; + if (yych <= 0x1F) goto yy4; + goto yy24; + } + } + } else { + if (yych <= '$') { + if (yych == '"') goto yy24; + if (yych <= '#') goto yy99; + goto yy101; + } else { + if (yych <= '\'') { + if (yych <= '&') goto yy99; + goto yy24; + } else { + if (yych <= '*') goto yy99; + if (yych <= ',') goto yy24; + goto yy101; + } + } + } + } else { + if (yych <= '`') { + if (yych <= '[') { + if (yych <= ':') goto yy4; + if (yych <= '?') goto yy24; + if (yych <= 'Z') goto yy101; + goto yy24; + } else { + if (yych <= ']') { + if (yych <= '\\') goto yy99; + goto yy24; + } else { + if (yych <= '^') goto yy99; + if (yych <= '_') goto yy101; + goto yy24; + } + } + } else { + if (yych <= '}') { + if (yych == 'm') goto yy652; + if (yych <= 'z') goto yy101; + goto yy24; + } else { + if (yych <= 0x7F) { + if (yych <= '~') goto yy99; + goto yy4; + } else { + if (yych <= 0xC1) goto yy24; + if (yych <= 0xF4) goto yy4; + goto yy24; + } + } + } + } +yy539: + yyaccept = 20; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '9') { + if (yych <= '#') { + if (yych <= '\f') { + if (yych <= 0x00) { + yyt1 = YYCURSOR; + goto yy653; + } + if (yych <= 0x08) { + yyt1 = YYCURSOR; + goto yy655; + } + if (yych <= '\n') { + yyt1 = YYCURSOR; + goto yy653; + } + yyt1 = YYCURSOR; + goto yy655; + } else { + if (yych <= 0x1A) { + if (yych <= '\r') { + yyt1 = YYCURSOR; + goto yy653; + } + yyt1 = YYCURSOR; + goto yy655; + } else { + if (yych <= 0x1B) { + yyt1 = YYCURSOR; + goto yy653; + } + if (yych <= 0x1F) { + yyt1 = YYCURSOR; + goto yy655; + } + yyt1 = YYCURSOR; + goto yy653; + } + } + } else { + if (yych <= '+') { + if (yych <= '$') { + yyt1 = YYCURSOR; + goto yy655; + } + if (yych <= '%') { + yyt1 = YYCURSOR; + goto yy656; + } + if (yych <= '*') { + yyt1 = YYCURSOR; + goto yy653; + } + yyt1 = YYCURSOR; + goto yy656; + } else { + if (yych <= '-') { + if (yych <= ',') { + yyt1 = YYCURSOR; + goto yy653; + } + yyt1 = YYCURSOR; + goto yy657; + } else { + if (yych <= '.') { + yyt1 = YYCURSOR; + goto yy658; + } + if (yych <= '/') { + yyt1 = YYCURSOR; + goto yy655; + } + goto yy659; + } + } + } + } else { + if (yych <= '~') { + if (yych <= 'Z') { + if (yych <= ':') { + yyt1 = YYCURSOR; + goto yy660; + } + if (yych <= '?') { + yyt1 = YYCURSOR; + goto yy653; + } + if (yych <= '@') { + yyt1 = YYCURSOR; + goto yy661; + } + yyt1 = YYCURSOR; + goto yy662; + } else { + if (yych <= '_') { + if (yych <= '^') { + yyt1 = YYCURSOR; + goto yy653; + } + yyt1 = YYCURSOR; + goto yy662; + } else { + if (yych <= '`') { + yyt1 = YYCURSOR; + goto yy653; + } + if (yych <= 'z') { + yyt1 = YYCURSOR; + goto yy662; + } + yyt1 = YYCURSOR; + goto yy653; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + yyt1 = YYCURSOR; + goto yy655; + } + if (yych <= 0xC1) goto yy345; + if (yych <= 0xDF) { + yyt1 = YYCURSOR; + goto yy663; + } + yyt1 = YYCURSOR; + goto yy664; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt1 = YYCURSOR; + goto yy665; + } + yyt1 = YYCURSOR; + goto yy666; + } else { + if (yych <= 0xF3) { + yyt1 = YYCURSOR; + goto yy667; + } + if (yych <= 0xF4) { + yyt1 = YYCURSOR; + goto yy668; + } + goto yy345; + } + } + } + } +yy540: + yyaccept = 20; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '9') { + if (yych <= '#') { + if (yych <= '\f') { + if (yych <= 0x00) { + yyt1 = YYCURSOR; + goto yy653; + } + if (yych <= 0x08) { + yyt1 = YYCURSOR; + goto yy655; + } + if (yych <= '\n') { + yyt1 = YYCURSOR; + goto yy653; + } + yyt1 = YYCURSOR; + goto yy655; + } else { + if (yych <= 0x1A) { + if (yych <= '\r') { + yyt1 = YYCURSOR; + goto yy653; + } + yyt1 = YYCURSOR; + goto yy655; + } else { + if (yych <= 0x1B) { + yyt1 = YYCURSOR; + goto yy653; + } + if (yych <= 0x1F) { + yyt1 = YYCURSOR; + goto yy655; + } + yyt1 = YYCURSOR; + goto yy653; + } + } + } else { + if (yych <= '+') { + if (yych <= '$') { + yyt1 = YYCURSOR; + goto yy655; + } + if (yych <= '%') { + yyt1 = YYCURSOR; + goto yy656; + } + if (yych <= '*') { + yyt1 = YYCURSOR; + goto yy653; + } + yyt1 = YYCURSOR; + goto yy656; + } else { + if (yych <= '-') { + if (yych <= ',') { + yyt1 = YYCURSOR; + goto yy653; + } + yyt1 = YYCURSOR; + goto yy657; + } else { + if (yych <= '.') { + yyt1 = YYCURSOR; + goto yy658; + } + if (yych <= '/') { + yyt1 = YYCURSOR; + goto yy655; + } + goto yy539; + } + } + } + } else { + if (yych <= '~') { + if (yych <= 'Z') { + if (yych <= ':') { + yyt1 = YYCURSOR; + goto yy660; + } + if (yych <= '?') { + yyt1 = YYCURSOR; + goto yy653; + } + if (yych <= '@') { + yyt1 = YYCURSOR; + goto yy661; + } + yyt1 = YYCURSOR; + goto yy662; + } else { + if (yych <= '_') { + if (yych <= '^') { + yyt1 = YYCURSOR; + goto yy653; + } + yyt1 = YYCURSOR; + goto yy662; + } else { + if (yych <= '`') { + yyt1 = YYCURSOR; + goto yy653; + } + if (yych <= 'z') { + yyt1 = YYCURSOR; + goto yy662; + } + yyt1 = YYCURSOR; + goto yy653; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + yyt1 = YYCURSOR; + goto yy655; + } + if (yych <= 0xC1) goto yy345; + if (yych <= 0xDF) { + yyt1 = YYCURSOR; + goto yy663; + } + yyt1 = YYCURSOR; + goto yy664; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt1 = YYCURSOR; + goto yy665; + } + yyt1 = YYCURSOR; + goto yy666; + } else { + if (yych <= 0xF3) { + yyt1 = YYCURSOR; + goto yy667; + } + if (yych <= 0xF4) { + yyt1 = YYCURSOR; + goto yy668; + } + goto yy345; + } + } + } + } +yy541: + yyaccept = 20; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '5') { + if (yych <= '#') { + if (yych <= '\f') { + if (yych <= 0x00) { + yyt1 = YYCURSOR; + goto yy653; + } + if (yych <= 0x08) { + yyt1 = YYCURSOR; + goto yy655; + } + if (yych <= '\n') { + yyt1 = YYCURSOR; + goto yy653; + } + yyt1 = YYCURSOR; + goto yy655; + } else { + if (yych <= 0x1A) { + if (yych <= '\r') { + yyt1 = YYCURSOR; + goto yy653; + } + yyt1 = YYCURSOR; + goto yy655; + } else { + if (yych <= 0x1B) { + yyt1 = YYCURSOR; + goto yy653; + } + if (yych <= 0x1F) { + yyt1 = YYCURSOR; + goto yy655; + } + yyt1 = YYCURSOR; + goto yy653; + } + } + } else { + if (yych <= ',') { + if (yych <= '%') { + if (yych <= '$') { + yyt1 = YYCURSOR; + goto yy655; + } + yyt1 = YYCURSOR; + goto yy656; + } else { + if (yych == '+') { + yyt1 = YYCURSOR; + goto yy656; + } + yyt1 = YYCURSOR; + goto yy653; + } + } else { + if (yych <= '.') { + if (yych <= '-') { + yyt1 = YYCURSOR; + goto yy657; + } + yyt1 = YYCURSOR; + goto yy658; + } else { + if (yych <= '/') { + yyt1 = YYCURSOR; + goto yy655; + } + if (yych <= '4') goto yy539; + goto yy669; + } + } + } + } else { + if (yych <= 'z') { + if (yych <= '@') { + if (yych <= '9') goto yy659; + if (yych <= ':') { + yyt1 = YYCURSOR; + goto yy660; + } + if (yych <= '?') { + yyt1 = YYCURSOR; + goto yy653; + } + yyt1 = YYCURSOR; + goto yy661; + } else { + if (yych <= '^') { + if (yych <= 'Z') { + yyt1 = YYCURSOR; + goto yy662; + } + yyt1 = YYCURSOR; + goto yy653; + } else { + if (yych == '`') { + yyt1 = YYCURSOR; + goto yy653; + } + yyt1 = YYCURSOR; + goto yy662; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + if (yych <= '~') { + yyt1 = YYCURSOR; + goto yy653; + } + yyt1 = YYCURSOR; + goto yy655; + } else { + if (yych <= 0xC1) goto yy345; + if (yych <= 0xDF) { + yyt1 = YYCURSOR; + goto yy663; + } + yyt1 = YYCURSOR; + goto yy664; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt1 = YYCURSOR; + goto yy665; + } + yyt1 = YYCURSOR; + goto yy666; + } else { + if (yych <= 0xF3) { + yyt1 = YYCURSOR; + goto yy667; + } + if (yych <= 0xF4) { + yyt1 = YYCURSOR; + goto yy668; + } + goto yy345; + } + } + } + } +yy542: + yych = *++YYCURSOR; + if (yych <= '@') { + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy670; + goto yy1; + } else { + if (yych <= 'F') goto yy670; + if (yych <= '`') goto yy1; + if (yych <= 'f') goto yy670; + goto yy1; + } +yy543: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '/') goto yy4; + if (yych <= '9') goto yy671; + goto yy4; +yy544: + yych = *++YYCURSOR; + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy672; + goto yy1; +yy545: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '9') { + if (yych <= '+') { + if (yych == '%') goto yy80; + if (yych <= '*') goto yy4; + goto yy80; + } else { + if (yych <= ',') goto yy4; + if (yych <= '.') goto yy46; + if (yych <= '/') goto yy4; + goto yy673; + } + } else { + if (yych <= '^') { + if (yych <= '?') goto yy4; + if (yych <= '@') goto yy96; + if (yych <= 'Z') goto yy46; + goto yy4; + } else { + if (yych == '`') goto yy4; + if (yych <= 'z') goto yy46; + goto yy4; + } + } +yy546: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '/') { + if (yych <= '+') { + if (yych == '%') goto yy80; + if (yych <= '*') goto yy4; + goto yy80; + } else { + if (yych <= ',') goto yy4; + if (yych <= '-') goto yy545; + if (yych <= '.') goto yy46; + goto yy4; + } + } else { + if (yych <= 'Z') { + if (yych <= '9') goto yy46; + if (yych <= '?') goto yy4; + if (yych <= '@') goto yy96; + goto yy46; + } else { + if (yych <= '_') { + if (yych <= '^') goto yy4; + goto yy46; + } else { + if (yych <= '`') goto yy4; + if (yych <= 'z') goto yy46; + goto yy4; + } + } + } +yy547: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '/') goto yy4; + if (yych <= '9') goto yy675; + goto yy4; +yy548: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych == '/') goto yy547; + goto yy4; +yy549: + yyaccept = 11; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[768+yych] & 32) { + goto yy103; + } + if (yych <= '9') { + if (yych <= '#') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy109; + if (yych <= 0x08) goto yy4; + if (yych <= '\n') goto yy109; + goto yy4; + } else { + if (yych <= 0x1A) { + if (yych <= '\r') goto yy109; + goto yy4; + } else { + if (yych <= 0x1B) goto yy109; + if (yych <= 0x1F) goto yy4; + goto yy109; + } + } + } else { + if (yych <= ',') { + if (yych <= '%') { + if (yych <= '$') goto yy4; + goto yy105; + } else { + if (yych == '+') goto yy80; + goto yy109; + } + } else { + if (yych <= '.') { + if (yych <= '-') goto yy46; + goto yy266; + } else { + if (yych <= '/') goto yy4; + if (yych <= '7') goto yy676; + goto yy677; + } + } + } + } else { + if (yych <= '_') { + if (yych <= 'D') { + if (yych <= ':') goto yy148; + if (yych <= '?') goto yy109; + if (yych <= '@') goto yy96; + goto yy678; + } else { + if (yych <= 'F') { + if (yych <= 'E') goto yy679; + goto yy678; + } else { + if (yych <= 'Z') goto yy115; + if (yych <= '^') goto yy109; + goto yy46; + } + } + } else { + if (yych <= 'z') { + if (yych <= 'd') { + if (yych <= '`') goto yy109; + goto yy678; + } else { + if (yych <= 'e') goto yy679; + if (yych <= 'f') goto yy678; + goto yy115; + } + } else { + if (yych <= 0x7F) { + if (yych <= '~') goto yy109; + goto yy4; + } else { + if (yych <= 0xC1) goto yy109; + if (yych <= 0xF4) goto yy4; + goto yy109; + } + } + } + } +yy550: + yyaccept = 6; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[768+yych] & 32) { + goto yy103; + } + if (yych <= '9') { + if (yych <= '#') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy26; + if (yych <= 0x08) goto yy4; + if (yych <= '\n') goto yy26; + goto yy4; + } else { + if (yych <= 0x1A) { + if (yych <= '\r') goto yy26; + goto yy4; + } else { + if (yych <= 0x1B) goto yy26; + if (yych <= 0x1F) goto yy4; + goto yy26; + } + } + } else { + if (yych <= '+') { + if (yych <= '$') goto yy4; + if (yych <= '%') goto yy105; + if (yych <= '*') goto yy26; + goto yy80; + } else { + if (yych <= '-') { + if (yych <= ',') goto yy26; + goto yy46; + } else { + if (yych <= '.') goto yy266; + if (yych <= '/') goto yy4; + goto yy677; + } + } + } + } else { + if (yych <= '_') { + if (yych <= 'D') { + if (yych <= ':') goto yy148; + if (yych <= '?') goto yy26; + if (yych <= '@') goto yy96; + goto yy678; + } else { + if (yych <= 'F') { + if (yych <= 'E') goto yy679; + goto yy678; + } else { + if (yych <= 'Z') goto yy115; + if (yych <= '^') goto yy26; + goto yy46; + } + } + } else { + if (yych <= 'z') { + if (yych <= 'd') { + if (yych <= '`') goto yy26; + goto yy678; + } else { + if (yych <= 'e') goto yy679; + if (yych <= 'f') goto yy678; + goto yy115; + } + } else { + if (yych <= 0x7F) { + if (yych <= '~') goto yy26; + goto yy4; + } else { + if (yych <= 0xC1) goto yy26; + if (yych <= 0xF4) goto yy4; + goto yy26; + } + } + } + } +yy551: + yyaccept = 12; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '/') { + if (yych <= 0x1F) { + if (yych <= '\f') { + if (yych <= 0x00) goto yy113; + if (yych <= 0x08) goto yy4; + if (yych <= '\n') goto yy113; + goto yy4; + } else { + if (yych <= '\r') goto yy113; + if (yych == 0x1B) goto yy113; + goto yy4; + } + } else { + if (yych <= '*') { + if (yych <= '#') goto yy113; + if (yych <= '$') goto yy4; + if (yych <= '%') goto yy80; + goto yy113; + } else { + if (yych <= '+') goto yy80; + if (yych <= ',') goto yy113; + if (yych <= '.') goto yy46; + goto yy4; + } + } + } else { + if (yych <= '_') { + if (yych <= '@') { + if (yych <= '9') goto yy678; + if (yych <= ':') goto yy148; + if (yych <= '?') goto yy113; + goto yy96; + } else { + if (yych <= 'F') goto yy678; + if (yych <= 'Z') goto yy115; + if (yych <= '^') goto yy113; + goto yy46; + } + } else { + if (yych <= '~') { + if (yych <= '`') goto yy113; + if (yych <= 'f') goto yy678; + if (yych <= 'z') goto yy115; + goto yy113; + } else { + if (yych <= 0x7F) goto yy4; + if (yych <= 0xC1) goto yy113; + if (yych <= 0xF4) goto yy4; + goto yy113; + } + } + } +yy552: + yyaccept = 12; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '.') { + if (yych <= 0x1F) { + if (yych <= '\f') { + if (yych <= 0x00) goto yy113; + if (yych <= 0x08) goto yy4; + if (yych <= '\n') goto yy113; + goto yy4; + } else { + if (yych <= '\r') goto yy113; + if (yych == 0x1B) goto yy113; + goto yy4; + } + } else { + if (yych <= '*') { + if (yych <= '#') goto yy113; + if (yych <= '$') goto yy4; + if (yych <= '%') goto yy80; + goto yy113; + } else { + if (yych <= '+') goto yy194; + if (yych <= ',') goto yy113; + if (yych <= '-') goto yy251; + goto yy46; + } + } + } else { + if (yych <= '^') { + if (yych <= '?') { + if (yych <= '/') goto yy4; + if (yych <= '9') goto yy678; + if (yych <= ':') goto yy148; + goto yy113; + } else { + if (yych <= '@') goto yy96; + if (yych <= 'F') goto yy678; + if (yych <= 'Z') goto yy115; + goto yy113; + } + } else { + if (yych <= 'z') { + if (yych <= '_') goto yy46; + if (yych <= '`') goto yy113; + if (yych <= 'f') goto yy678; + goto yy115; + } else { + if (yych <= 0x7F) { + if (yych <= '~') goto yy113; + goto yy4; + } else { + if (yych <= 0xC1) goto yy113; + if (yych <= 0xF4) goto yy4; + goto yy113; + } + } + } + } +yy553: + yych = *++YYCURSOR; + if (yych <= 0x7F) { + if (yych <= '@') { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy553; + if (yych <= ':') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 'Z') goto yy553; + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 'z') goto yy553; + yyt2 = YYCURSOR; + goto yy198; + } + } else { + if (yych <= 0xEF) { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + if (yych <= 0xE0) { + yyt2 = YYCURSOR; + goto yy206; + } + yyt2 = YYCURSOR; + goto yy207; + } else { + if (yych <= 0xF0) { + yyt2 = YYCURSOR; + goto yy208; + } + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } +yy554: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '?') { + if (yych <= '#') { + if (yych <= '\f') { + if (yych <= 0x00) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0x08) { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= '\n') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= 0x1A) { + if (yych <= '\r') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= 0x1B) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0x1F) { + yyt2 = YYCURSOR; + goto yy284; + } + yyt2 = YYCURSOR; + goto yy198; + } + } + } else { + if (yych <= '-') { + if (yych <= '$') { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= '%') { + yyt1 = YYCURSOR; + goto yy680; + } + if (yych <= ',') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= '/') { + if (yych <= '.') { + yyt2 = YYCURSOR; + goto yy376; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= '9') goto yy681; + if (yych <= ':') goto yy682; + yyt2 = YYCURSOR; + goto yy198; + } + } + } + } else { + if (yych <= '~') { + if (yych <= '^') { + if (yych <= '@') { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= 'F') goto yy683; + if (yych <= 'Z') goto yy3; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= '`') { + if (yych <= '_') { + yyt2 = YYCURSOR; + goto yy284; + } + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 'f') goto yy683; + if (yych <= 'z') goto yy3; + yyt2 = YYCURSOR; + goto yy198; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= 0xC1) goto yy5; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy289; + } + yyt2 = YYCURSOR; + goto yy290; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy291; + } + yyt2 = YYCURSOR; + goto yy292; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy293; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy294; + } + goto yy5; + } + } + } + } +yy555: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '?') { + if (yych <= '#') { + if (yych <= '\f') { + if (yych <= 0x00) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0x08) { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= '\n') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= 0x1A) { + if (yych <= '\r') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= 0x1B) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0x1F) { + yyt2 = YYCURSOR; + goto yy284; + } + yyt2 = YYCURSOR; + goto yy198; + } + } + } else { + if (yych <= '-') { + if (yych <= '$') { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= '%') { + yyt1 = YYCURSOR; + goto yy680; + } + if (yych <= ',') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= '/') { + if (yych <= '.') { + yyt2 = YYCURSOR; + goto yy376; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= '9') goto yy684; + if (yych <= ':') goto yy682; + yyt2 = YYCURSOR; + goto yy198; + } + } + } + } else { + if (yych <= '~') { + if (yych <= '^') { + if (yych <= '@') { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= 'F') goto yy683; + if (yych <= 'Z') goto yy3; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= '`') { + if (yych <= '_') { + yyt2 = YYCURSOR; + goto yy284; + } + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 'f') goto yy683; + if (yych <= 'z') goto yy3; + yyt2 = YYCURSOR; + goto yy198; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= 0xC1) goto yy5; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy289; + } + yyt2 = YYCURSOR; + goto yy290; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy291; + } + yyt2 = YYCURSOR; + goto yy292; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy293; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy294; + } + goto yy5; + } + } + } + } +yy556: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= ':') { + if (yych <= '#') { + if (yych <= '\f') { + if (yych <= 0x00) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0x08) { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= '\n') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= 0x1A) { + if (yych <= '\r') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= 0x1B) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0x1F) { + yyt2 = YYCURSOR; + goto yy284; + } + yyt2 = YYCURSOR; + goto yy198; + } + } + } else { + if (yych <= '.') { + if (yych <= '%') { + if (yych <= '$') { + yyt2 = YYCURSOR; + goto yy284; + } + yyt1 = YYCURSOR; + goto yy680; + } else { + if (yych <= ',') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '-') { + yyt2 = YYCURSOR; + goto yy284; + } + yyt2 = YYCURSOR; + goto yy376; + } + } else { + if (yych <= '4') { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy284; + } + goto yy684; + } else { + if (yych <= '5') goto yy685; + if (yych <= '9') goto yy681; + goto yy682; + } + } + } + } else { + if (yych <= 'z') { + if (yych <= 'Z') { + if (yych <= '?') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '@') { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= 'F') goto yy683; + goto yy3; + } else { + if (yych <= '_') { + if (yych <= '^') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 'f') goto yy683; + goto yy3; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + if (yych <= '~') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= 0xC1) goto yy5; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy289; + } + yyt2 = YYCURSOR; + goto yy290; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy291; + } + yyt2 = YYCURSOR; + goto yy292; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy293; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy294; + } + goto yy5; + } + } + } + } +yy557: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '@') { + if (yych <= 0x1F) { + if (yych <= '\f') { + if (yych <= 0x00) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0x08) { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= '\n') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= '\r') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych == 0x1B) { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } + } else { + if (yych <= ',') { + if (yych <= '#') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '$') { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= '%') { + yyt1 = YYCURSOR; + goto yy680; + } + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= '9') { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy284; + } + goto yy683; + } else { + if (yych <= ':') goto yy682; + if (yych <= '?') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } + } + } + } else { + if (yych <= '~') { + if (yych <= '_') { + if (yych <= 'F') goto yy683; + if (yych <= 'Z') goto yy3; + if (yych <= '^') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 'f') goto yy683; + if (yych <= 'z') goto yy3; + yyt2 = YYCURSOR; + goto yy198; + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= 0xC1) goto yy5; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy289; + } + yyt2 = YYCURSOR; + goto yy290; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy291; + } + yyt2 = YYCURSOR; + goto yy292; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy293; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy294; + } + goto yy5; + } + } + } + } +yy558: + yych = *++YYCURSOR; + if (yych <= '@') { + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy686; + if (yych <= ':') goto yy471; + goto yy1; + } else { + if (yych <= 'F') goto yy687; + if (yych <= '`') goto yy1; + if (yych <= 'f') goto yy687; + goto yy1; + } +yy559: + yych = *++YYCURSOR; + if (yych <= '@') { + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy687; + if (yych <= ':') goto yy471; + goto yy1; + } else { + if (yych <= 'F') goto yy687; + if (yych <= '`') goto yy1; + if (yych <= 'f') goto yy687; + goto yy1; + } +yy560: + yych = *++YYCURSOR; + if (yych <= 'f') { + if (yych <= '9') { + if (yych == '.') { + yyt2 = YYCURSOR; + goto yy644; + } + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy642; + } + yyt1 = YYCURSOR; + goto yy688; + } else { + if (yych <= '@') { + if (yych <= ':') goto yy471; + yyt2 = YYCURSOR; + goto yy642; + } else { + if (yych <= 'F') { + yyt1 = YYCURSOR; + goto yy688; + } + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy642; + } + yyt1 = YYCURSOR; + goto yy688; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + yyt2 = YYCURSOR; + goto yy642; + } + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy645; + } + yyt2 = YYCURSOR; + goto yy646; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy647; + } + yyt2 = YYCURSOR; + goto yy648; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy649; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy650; + } + goto yy1; + } + } + } +yy561: + yych = *++YYCURSOR; + if (yych <= '@') { + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy689; + if (yych <= ':') goto yy471; + goto yy1; + } else { + if (yych <= 'F') goto yy689; + if (yych <= '`') goto yy1; + if (yych <= 'f') goto yy689; + goto yy1; + } +yy562: + yych = *++YYCURSOR; + if (yych <= '@') { + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy690; + if (yych <= ':') goto yy691; + goto yy1; + } else { + if (yych <= 'F') goto yy690; + if (yych <= '`') goto yy1; + if (yych <= 'f') goto yy690; + goto yy1; + } +yy563: + yych = *++YYCURSOR; + if (yych <= '`') { + if (yych <= '9') { + if (yych <= '0') { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy692; + } else { + if (yych <= '1') goto yy693; + if (yych <= '2') goto yy694; + goto yy692; + } + } else { + if (yych <= '@') { + if (yych <= ':') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 'F') goto yy695; + if (yych <= 'Z') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } + } + } else { + if (yych <= 0xDF) { + if (yych <= 'z') { + if (yych <= 'f') goto yy695; + goto yy1; + } else { + if (yych <= 0x7F) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0xC1) goto yy1; + yyt2 = YYCURSOR; + goto yy205; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xE0) { + yyt2 = YYCURSOR; + goto yy206; + } + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy564: + yych = *++YYCURSOR; + if (yych <= 'f') { + if (yych <= ':') { + if (yych <= '.') { + if (yych <= '-') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt1 = YYCURSOR; + goto yy297; + } else { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy696; + } + } else { + if (yych <= 'F') { + if (yych <= '@') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy696; + } else { + if (yych <= 'Z') goto yy1; + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy696; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + if (yych <= 'z') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + yyt2 = YYCURSOR; + goto yy206; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy565: + yych = *++YYCURSOR; + if (yych <= '@') { + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy695; + goto yy1; + } else { + if (yych <= 'F') goto yy695; + if (yych <= '`') goto yy1; + if (yych <= 'f') goto yy695; + goto yy1; + } +yy566: + yych = *++YYCURSOR; + if (yych <= 'z') { + if (yych <= '@') { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy696; + if (yych <= ':') goto yy565; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 'Z') { + if (yych <= 'F') goto yy696; + goto yy1; + } else { + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 'f') goto yy696; + goto yy1; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + yyt2 = YYCURSOR; + goto yy206; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy567: + yych = *++YYCURSOR; + if (yych <= 'f') { + if (yych <= ':') { + if (yych <= '.') { + if (yych <= '-') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt1 = YYCURSOR; + goto yy297; + } else { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy697; + goto yy565; + } + } else { + if (yych <= 'F') { + if (yych <= '@') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy696; + } else { + if (yych <= 'Z') goto yy1; + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy696; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + if (yych <= 'z') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + yyt2 = YYCURSOR; + goto yy206; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy568: + yych = *++YYCURSOR; + if (yych <= '`') { + if (yych <= '9') { + if (yych <= '.') { + if (yych <= '-') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt1 = YYCURSOR; + goto yy297; + } else { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '5') goto yy697; + goto yy696; + } + } else { + if (yych <= '@') { + if (yych <= ':') goto yy565; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 'F') goto yy696; + if (yych <= 'Z') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } + } + } else { + if (yych <= 0xDF) { + if (yych <= 'z') { + if (yych <= 'f') goto yy696; + goto yy1; + } else { + if (yych <= 0x7F) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0xC1) goto yy1; + yyt2 = YYCURSOR; + goto yy205; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xE0) { + yyt2 = YYCURSOR; + goto yy206; + } + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy569: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '1') { + if (yych <= '/') goto yy4; + if (yych <= '0') goto yy698; + goto yy699; + } else { + if (yych <= '2') goto yy700; + if (yych <= '9') goto yy698; + goto yy4; + } +yy570: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych == '.') goto yy569; + goto yy4; +yy571: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych == '.') goto yy569; + if (yych <= '/') goto yy4; + if (yych <= '5') goto yy570; + goto yy4; +yy572: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '?') { + if (yych <= 0x1B) { + if (yych <= '\n') { + if (yych <= 0x00) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0x08) { + yyt2 = YYCURSOR; + goto yy284; + } + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych == '\r') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0x1A) { + yyt2 = YYCURSOR; + goto yy284; + } + yyt2 = YYCURSOR; + goto yy198; + } + } else { + if (yych <= ',') { + if (yych <= 0x1F) { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych == '$') { + yyt2 = YYCURSOR; + goto yy284; + } + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= '9') goto yy3; + if (yych <= ':') goto yy378; + yyt2 = YYCURSOR; + goto yy198; + } + } + } else { + if (yych <= 0x7F) { + if (yych <= '_') { + if (yych <= '@') { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= 'Z') goto yy3; + if (yych <= '^') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 'z') goto yy3; + if (yych <= '~') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } + } else { + if (yych <= 0xEF) { + if (yych <= 0xC1) goto yy5; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy289; + } + if (yych <= 0xE0) { + yyt2 = YYCURSOR; + goto yy290; + } + yyt2 = YYCURSOR; + goto yy291; + } else { + if (yych <= 0xF0) { + yyt2 = YYCURSOR; + goto yy292; + } + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy293; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy294; + } + goto yy5; + } + } + } +yy573: + yych = *++YYCURSOR; + if (yych == '.') goto yy701; + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy702; + goto yy1; +yy574: + yych = *++YYCURSOR; + if (yych == '.') goto yy701; + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy573; + goto yy1; +yy575: + yych = *++YYCURSOR; + if (yych <= '/') { + if (yych == '.') goto yy701; + goto yy1; + } else { + if (yych <= '4') goto yy573; + if (yych <= '5') goto yy703; + if (yych <= '9') goto yy702; + goto yy1; + } +yy576: + yych = *++YYCURSOR; + if (yych <= 'z') { + if (yych <= '@') { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy704; + if (yych <= ':') goto yy486; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 'Z') { + if (yych <= 'F') goto yy704; + goto yy1; + } else { + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 'f') goto yy704; + goto yy1; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + yyt2 = YYCURSOR; + goto yy206; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy577: + yych = *++YYCURSOR; + if (yych <= '9') { + if (yych <= '0') { + if (yych <= '/') goto yy1; + goto yy705; + } else { + if (yych <= '1') goto yy706; + if (yych <= '2') goto yy707; + goto yy708; + } + } else { + if (yych <= 'F') { + if (yych <= '@') goto yy1; + goto yy387; + } else { + if (yych <= '`') goto yy1; + if (yych <= 'f') goto yy387; + goto yy1; + } + } +yy578: + yych = *++YYCURSOR; + if (yych <= ' ') { + if (yych <= '\f') { + if (yych == '\t') goto yy578; + goto yy1; + } else { + if (yych <= '\r') goto yy578; + if (yych <= 0x1F) goto yy1; + goto yy578; + } + } else { + if (yych <= '&') { + if (yych == '"') goto yy306; + goto yy1; + } else { + if (yych <= '\'') goto yy307; + if (yych == '>') goto yy309; + goto yy1; + } + } +yy579: + yych = *++YYCURSOR; + if (yybm[0+yych] & 32) { + goto yy579; + } + if (yych <= 0xDF) { + if (yych <= '>') { + if (yych <= 0x00) goto yy1; + goto yy709; + } else { + if (yych <= '\\') goto yy710; + if (yych <= 0xC1) goto yy1; + goto yy711; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xE0) goto yy712; + if (yych <= 0xEF) goto yy713; + goto yy714; + } else { + if (yych <= 0xF3) goto yy715; + if (yych <= 0xF4) goto yy716; + goto yy1; + } + } +yy580: + yyaccept = 27; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[256+yych] & 8) { + goto yy391; + } + if (yych <= 0xDF) { + if (yych <= '"') { + if (yych <= 0x00) goto yy310; + goto yy489; + } else { + if (yych <= '\\') goto yy392; + if (yych <= 0xC1) goto yy310; + goto yy393; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xE0) goto yy394; + if (yych <= 0xEF) goto yy395; + goto yy396; + } else { + if (yych <= 0xF3) goto yy397; + if (yych <= 0xF4) goto yy398; + goto yy310; + } + } +yy581: + yych = *++YYCURSOR; + if (yych <= 0x7F) { + if (yych <= '&') { + if (yych <= 0x00) goto yy391; + if (yych == '\n') goto yy490; + goto yy491; + } else { + if (yych <= '\'') goto yy579; + if (yych == '>') goto yy580; + goto yy491; + } + } else { + if (yych <= 0xEF) { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) goto yy582; + if (yych <= 0xE0) goto yy583; + goto yy584; + } else { + if (yych <= 0xF0) goto yy585; + if (yych <= 0xF3) goto yy586; + if (yych <= 0xF4) goto yy587; + goto yy1; + } + } +yy582: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy491; + goto yy1; +yy583: + yych = *++YYCURSOR; + if (yych <= 0x9F) goto yy1; + if (yych <= 0xBF) goto yy582; + goto yy1; +yy584: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy582; + goto yy1; +yy585: + yych = *++YYCURSOR; + if (yych <= 0x8F) goto yy1; + if (yych <= 0xBF) goto yy584; + goto yy1; +yy586: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy584; + goto yy1; +yy587: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0x8F) goto yy584; + goto yy1; +yy588: + yyaccept = 27; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[256+yych] & 16) { + goto yy399; + } + if (yych <= 0xDF) { + if (yych <= '\'') { + if (yych <= 0x00) goto yy310; + goto yy489; + } else { + if (yych <= '\\') goto yy400; + if (yych <= 0xC1) goto yy310; + goto yy401; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xE0) goto yy402; + if (yych <= 0xEF) goto yy403; + goto yy404; + } else { + if (yych <= 0xF3) goto yy405; + if (yych <= 0xF4) goto yy406; + goto yy310; + } + } +yy589: + yych = *++YYCURSOR; + if (yych <= 0x7F) { + if (yych <= '!') { + if (yych <= 0x00) goto yy399; + if (yych == '\n') goto yy490; + goto yy492; + } else { + if (yych <= '"') goto yy579; + if (yych == '>') goto yy588; + goto yy492; + } + } else { + if (yych <= 0xEF) { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) goto yy590; + if (yych <= 0xE0) goto yy591; + goto yy592; + } else { + if (yych <= 0xF0) goto yy593; + if (yych <= 0xF3) goto yy594; + if (yych <= 0xF4) goto yy595; + goto yy1; + } + } +yy590: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy492; + goto yy1; +yy591: + yych = *++YYCURSOR; + if (yych <= 0x9F) goto yy1; + if (yych <= 0xBF) goto yy590; + goto yy1; +yy592: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy590; + goto yy1; +yy593: + yych = *++YYCURSOR; + if (yych <= 0x8F) goto yy1; + if (yych <= 0xBF) goto yy592; + goto yy1; +yy594: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy592; + goto yy1; +yy595: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0x8F) goto yy592; + goto yy1; +yy596: + yych = *++YYCURSOR; + if (yych <= '?') { + if (yych <= '&') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy1; + if (yych == '\t') goto yy596; + goto yy412; + } else { + if (yych <= '\r') goto yy596; + if (yych == ' ') goto yy596; + goto yy412; + } + } else { + if (yych <= '.') { + if (yych <= '\'') goto yy717; + if (yych == '-') goto yy501; + goto yy412; + } else { + if (yych <= ':') { + if (yych <= '/') goto yy502; + goto yy501; + } else { + if (yych <= '=') goto yy412; + if (yych <= '>') goto yy503; + goto yy502; + } + } + } + } else { + if (yych <= 'z') { + if (yych <= '\\') { + if (yych <= '@') goto yy412; + if (yych <= 'Z') goto yy501; + if (yych <= '[') goto yy412; + goto yy504; + } else { + if (yych == '_') goto yy501; + if (yych <= '`') goto yy412; + goto yy501; + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) goto yy412; + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) goto yy505; + goto yy506; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) goto yy507; + goto yy508; + } else { + if (yych <= 0xF3) goto yy509; + if (yych <= 0xF4) goto yy510; + goto yy1; + } + } + } + } +yy597: + yyaccept = 28; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= 0xC1) { + if (yych <= '"') { + if (yych <= 0x00) goto yy315; + if (yych >= '"') goto yy599; + } else { + if (yych == '\\') goto yy600; + if (yych >= 0x80) goto yy315; + } + } else { + if (yych <= 0xEF) { + if (yych <= 0xDF) goto yy601; + if (yych <= 0xE0) goto yy602; + goto yy603; + } else { + if (yych <= 0xF0) goto yy604; + if (yych <= 0xF3) goto yy605; + if (yych <= 0xF4) goto yy606; + goto yy315; + } + } +yy598: + yych = *++YYCURSOR; + if (yych <= 0xC1) { + if (yych <= '"') { + if (yych <= 0x00) goto yy1; + if (yych <= '!') goto yy598; + } else { + if (yych == '\\') goto yy600; + if (yych <= 0x7F) goto yy598; + goto yy1; + } + } else { + if (yych <= 0xEF) { + if (yych <= 0xDF) goto yy601; + if (yych <= 0xE0) goto yy602; + goto yy603; + } else { + if (yych <= 0xF0) goto yy604; + if (yych <= 0xF3) goto yy605; + if (yych <= 0xF4) goto yy606; + goto yy1; + } + } +yy599: + yych = *++YYCURSOR; + if (yych <= '/') { + if (yych <= 0x1F) { + if (yych <= '\t') { + if (yych <= 0x08) goto yy1; + goto yy718; + } else { + if (yych == '\r') goto yy718; + goto yy1; + } + } else { + if (yych <= ',') { + if (yych <= ' ') goto yy718; + goto yy1; + } else { + if (yych <= '-') goto yy311; + if (yych <= '.') goto yy1; + goto yy215; + } + } + } else { + if (yych <= '@') { + if (yych <= '=') { + if (yych <= ':') goto yy311; + goto yy1; + } else { + if (yych <= '>') goto yy216; + if (yych <= '?') goto yy215; + goto yy1; + } + } else { + if (yych <= '_') { + if (yych <= 'Z') goto yy311; + if (yych <= '^') goto yy1; + goto yy311; + } else { + if (yych <= '`') goto yy1; + if (yych <= 'z') goto yy311; + goto yy1; + } + } + } +yy600: + yych = *++YYCURSOR; + if (yych <= 0xDF) { + if (yych <= '\n') { + if (yych <= '\t') goto yy598; + goto yy1; + } else { + if (yych <= 0x7F) goto yy598; + if (yych <= 0xC1) goto yy1; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xE0) goto yy602; + if (yych <= 0xEF) goto yy603; + goto yy604; + } else { + if (yych <= 0xF3) goto yy605; + if (yych <= 0xF4) goto yy606; + goto yy1; + } + } +yy601: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy598; + goto yy1; +yy602: + yych = *++YYCURSOR; + if (yych <= 0x9F) goto yy1; + if (yych <= 0xBF) goto yy601; + goto yy1; +yy603: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy601; + goto yy1; +yy604: + yych = *++YYCURSOR; + if (yych <= 0x8F) goto yy1; + if (yych <= 0xBF) goto yy603; + goto yy1; +yy605: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy603; + goto yy1; +yy606: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0x8F) goto yy603; + goto yy1; +yy607: + yych = *++YYCURSOR; + if (yych <= '?') { + if (yych <= '!') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy1; + if (yych == '\t') goto yy607; + goto yy413; + } else { + if (yych <= '\r') goto yy607; + if (yych == ' ') goto yy607; + goto yy413; + } + } else { + if (yych <= '.') { + if (yych <= '"') goto yy717; + if (yych == '-') goto yy511; + goto yy413; + } else { + if (yych <= ':') { + if (yych <= '/') goto yy512; + goto yy511; + } else { + if (yych <= '=') goto yy413; + if (yych <= '>') goto yy513; + goto yy512; + } + } + } + } else { + if (yych <= 'z') { + if (yych <= '\\') { + if (yych <= '@') goto yy413; + if (yych <= 'Z') goto yy511; + if (yych <= '[') goto yy413; + goto yy514; + } else { + if (yych == '_') goto yy511; + if (yych <= '`') goto yy413; + goto yy511; + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) goto yy413; + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) goto yy515; + goto yy516; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) goto yy517; + goto yy518; + } else { + if (yych <= 0xF3) goto yy519; + if (yych <= 0xF4) goto yy520; + goto yy1; + } + } + } + } +yy608: + yyaccept = 28; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= 0xC1) { + if (yych <= '\'') { + if (yych <= 0x00) goto yy315; + if (yych >= '\'') goto yy599; + } else { + if (yych == '\\') goto yy610; + if (yych >= 0x80) goto yy315; + } + } else { + if (yych <= 0xEF) { + if (yych <= 0xDF) goto yy611; + if (yych <= 0xE0) goto yy612; + goto yy613; + } else { + if (yych <= 0xF0) goto yy614; + if (yych <= 0xF3) goto yy615; + if (yych <= 0xF4) goto yy616; + goto yy315; + } + } +yy609: + yych = *++YYCURSOR; + if (yych <= 0xC1) { + if (yych <= '\'') { + if (yych <= 0x00) goto yy1; + if (yych <= '&') goto yy609; + goto yy599; + } else { + if (yych == '\\') goto yy610; + if (yych <= 0x7F) goto yy609; + goto yy1; + } + } else { + if (yych <= 0xEF) { + if (yych <= 0xDF) goto yy611; + if (yych <= 0xE0) goto yy612; + goto yy613; + } else { + if (yych <= 0xF0) goto yy614; + if (yych <= 0xF3) goto yy615; + if (yych <= 0xF4) goto yy616; + goto yy1; + } + } +yy610: + yych = *++YYCURSOR; + if (yych <= 0xDF) { + if (yych <= '\n') { + if (yych <= '\t') goto yy609; + goto yy1; + } else { + if (yych <= 0x7F) goto yy609; + if (yych <= 0xC1) goto yy1; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xE0) goto yy612; + if (yych <= 0xEF) goto yy613; + goto yy614; + } else { + if (yych <= 0xF3) goto yy615; + if (yych <= 0xF4) goto yy616; + goto yy1; + } + } +yy611: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy609; + goto yy1; +yy612: + yych = *++YYCURSOR; + if (yych <= 0x9F) goto yy1; + if (yych <= 0xBF) goto yy611; + goto yy1; +yy613: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy611; + goto yy1; +yy614: + yych = *++YYCURSOR; + if (yych <= 0x8F) goto yy1; + if (yych <= 0xBF) goto yy613; + goto yy1; +yy615: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy613; + goto yy1; +yy616: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0x8F) goto yy613; + goto yy1; +yy617: + yych = *++YYCURSOR; + if (yych <= '>') { + if (yych <= ' ') { + if (yych <= '\t') { + if (yych <= 0x00) goto yy1; + if (yych <= 0x08) goto yy522; + goto yy617; + } else { + if (yych == '\r') goto yy617; + if (yych <= 0x1F) goto yy522; + goto yy617; + } + } else { + if (yych <= '/') { + if (yych == '-') goto yy617; + if (yych <= '.') goto yy522; + } else { + if (yych <= ':') goto yy617; + if (yych <= '<') goto yy522; + if (yych <= '=') goto yy523; + goto yy1; + } + } + } else { + if (yych <= 0x7F) { + if (yych <= '^') { + if (yych <= '?') goto yy618; + if (yych <= '@') goto yy522; + if (yych <= 'Z') goto yy617; + goto yy522; + } else { + if (yych == '`') goto yy522; + if (yych <= 'z') goto yy617; + goto yy522; + } + } else { + if (yych <= 0xEF) { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) goto yy526; + if (yych <= 0xE0) goto yy527; + goto yy528; + } else { + if (yych <= 0xF0) goto yy529; + if (yych <= 0xF3) goto yy530; + if (yych <= 0xF4) goto yy531; + goto yy1; + } + } + } +yy618: + yych = *++YYCURSOR; + if (yych <= '^') { + if (yych <= ':') { + if (yych <= '-') { + if (yych <= 0x00) goto yy1; + if (yych <= ',') goto yy522; + goto yy617; + } else { + if (yych <= '.') goto yy522; + if (yych <= '/') goto yy618; + goto yy617; + } + } else { + if (yych <= '?') { + if (yych <= '=') goto yy522; + if (yych <= '>') goto yy314; + goto yy618; + } else { + if (yych <= '@') goto yy522; + if (yych <= 'Z') goto yy617; + goto yy522; + } + } + } else { + if (yych <= 0xDF) { + if (yych <= 'z') { + if (yych == '`') goto yy522; + goto yy617; + } else { + if (yych <= 0x7F) goto yy522; + if (yych <= 0xC1) goto yy1; + goto yy526; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xE0) goto yy527; + if (yych <= 0xEF) goto yy528; + goto yy529; + } else { + if (yych <= 0xF3) goto yy530; + if (yych <= 0xF4) goto yy531; + goto yy1; + } + } + } +yy619: + yych = *++YYCURSOR; + if (yych <= '>') { + if (yych <= '!') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy1; + if (yych == '\t') goto yy619; + goto yy524; + } else { + if (yych <= '\r') goto yy619; + if (yych == ' ') goto yy619; + goto yy524; + } + } else { + if (yych <= '.') { + if (yych <= '"') goto yy522; + if (yych == '-') goto yy619; + goto yy524; + } else { + if (yych <= ':') { + if (yych >= '0') goto yy619; + } else { + if (yych <= '<') goto yy524; + if (yych <= '=') goto yy719; + goto yy621; + } + } + } + } else { + if (yych <= 'z') { + if (yych <= '[') { + if (yych <= '?') goto yy620; + if (yych <= '@') goto yy524; + if (yych <= 'Z') goto yy619; + goto yy524; + } else { + if (yych <= '^') { + if (yych <= '\\') goto yy622; + goto yy524; + } else { + if (yych == '`') goto yy524; + goto yy619; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) goto yy524; + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) goto yy623; + goto yy624; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) goto yy625; + goto yy626; + } else { + if (yych <= 0xF3) goto yy627; + if (yych <= 0xF4) goto yy628; + goto yy1; + } + } + } + } +yy620: + yych = *++YYCURSOR; + if (yych <= '[') { + if (yych <= '/') { + if (yych <= '"') { + if (yych <= 0x00) goto yy1; + if (yych <= '!') goto yy524; + goto yy522; + } else { + if (yych == '-') goto yy619; + if (yych <= '.') goto yy524; + goto yy620; + } + } else { + if (yych <= '>') { + if (yych <= ':') goto yy619; + if (yych <= '=') goto yy524; + goto yy720; + } else { + if (yych <= '?') goto yy620; + if (yych <= '@') goto yy524; + if (yych <= 'Z') goto yy619; + goto yy524; + } + } + } else { + if (yych <= 0xC1) { + if (yych <= '_') { + if (yych <= '\\') goto yy622; + if (yych <= '^') goto yy524; + goto yy619; + } else { + if (yych <= '`') goto yy524; + if (yych <= 'z') goto yy619; + if (yych <= 0x7F) goto yy524; + goto yy1; + } + } else { + if (yych <= 0xEF) { + if (yych <= 0xDF) goto yy623; + if (yych <= 0xE0) goto yy624; + goto yy625; + } else { + if (yych <= 0xF0) goto yy626; + if (yych <= 0xF3) goto yy627; + if (yych <= 0xF4) goto yy628; + goto yy1; + } + } + } +yy621: + yych = *++YYCURSOR; + if (yych <= 0xC1) { + if (yych <= '"') { + if (yych <= 0x00) goto yy1; + if (yych <= '!') goto yy621; + goto yy721; + } else { + if (yych == '\\') goto yy722; + if (yych <= 0x7F) goto yy621; + goto yy1; + } + } else { + if (yych <= 0xEF) { + if (yych <= 0xDF) goto yy723; + if (yych <= 0xE0) goto yy724; + goto yy725; + } else { + if (yych <= 0xF0) goto yy726; + if (yych <= 0xF3) goto yy727; + if (yych <= 0xF4) goto yy728; + goto yy1; + } + } +yy622: + yych = *++YYCURSOR; + if (yych <= 'Z') { + if (yych <= '.') { + if (yych <= '\n') { + if (yych <= 0x00) goto yy621; + if (yych <= '\t') goto yy524; + goto yy522; + } else { + if (yych == '-') goto yy619; + goto yy524; + } + } else { + if (yych <= '=') { + if (yych <= '/') goto yy620; + if (yych <= ':') goto yy619; + goto yy524; + } else { + if (yych <= '>') goto yy621; + if (yych <= '?') goto yy620; + if (yych <= '@') goto yy524; + goto yy619; + } + } + } else { + if (yych <= 0xC1) { + if (yych <= '`') { + if (yych == '_') goto yy619; + goto yy524; + } else { + if (yych <= 'z') goto yy619; + if (yych <= 0x7F) goto yy524; + goto yy1; + } + } else { + if (yych <= 0xEF) { + if (yych <= 0xDF) goto yy623; + if (yych <= 0xE0) goto yy624; + goto yy625; + } else { + if (yych <= 0xF0) goto yy626; + if (yych <= 0xF3) goto yy627; + if (yych <= 0xF4) goto yy628; + goto yy1; + } + } + } +yy623: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy524; + goto yy1; +yy624: + yych = *++YYCURSOR; + if (yych <= 0x9F) goto yy1; + if (yych <= 0xBF) goto yy623; + goto yy1; +yy625: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy623; + goto yy1; +yy626: + yych = *++YYCURSOR; + if (yych <= 0x8F) goto yy1; + if (yych <= 0xBF) goto yy625; + goto yy1; +yy627: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy625; + goto yy1; +yy628: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0x8F) goto yy625; + goto yy1; +yy629: + yych = *++YYCURSOR; + if (yych <= '>') { + if (yych <= '&') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy1; + if (yych == '\t') goto yy629; + goto yy525; + } else { + if (yych <= '\r') goto yy629; + if (yych == ' ') goto yy629; + goto yy525; + } + } else { + if (yych <= '.') { + if (yych <= '\'') goto yy522; + if (yych == '-') goto yy629; + goto yy525; + } else { + if (yych <= ':') { + if (yych >= '0') goto yy629; + } else { + if (yych <= '<') goto yy525; + if (yych <= '=') goto yy729; + goto yy631; + } + } + } + } else { + if (yych <= 'z') { + if (yych <= '[') { + if (yych <= '?') goto yy630; + if (yych <= '@') goto yy525; + if (yych <= 'Z') goto yy629; + goto yy525; + } else { + if (yych <= '^') { + if (yych <= '\\') goto yy632; + goto yy525; + } else { + if (yych == '`') goto yy525; + goto yy629; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) goto yy525; + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) goto yy633; + goto yy634; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) goto yy635; + goto yy636; + } else { + if (yych <= 0xF3) goto yy637; + if (yych <= 0xF4) goto yy638; + goto yy1; + } + } + } + } +yy630: + yych = *++YYCURSOR; + if (yych <= '[') { + if (yych <= '/') { + if (yych <= '\'') { + if (yych <= 0x00) goto yy1; + if (yych <= '&') goto yy525; + goto yy522; + } else { + if (yych == '-') goto yy629; + if (yych <= '.') goto yy525; + goto yy630; + } + } else { + if (yych <= '>') { + if (yych <= ':') goto yy629; + if (yych <= '=') goto yy525; + goto yy730; + } else { + if (yych <= '?') goto yy630; + if (yych <= '@') goto yy525; + if (yych <= 'Z') goto yy629; + goto yy525; + } + } + } else { + if (yych <= 0xC1) { + if (yych <= '_') { + if (yych <= '\\') goto yy632; + if (yych <= '^') goto yy525; + goto yy629; + } else { + if (yych <= '`') goto yy525; + if (yych <= 'z') goto yy629; + if (yych <= 0x7F) goto yy525; + goto yy1; + } + } else { + if (yych <= 0xEF) { + if (yych <= 0xDF) goto yy633; + if (yych <= 0xE0) goto yy634; + goto yy635; + } else { + if (yych <= 0xF0) goto yy636; + if (yych <= 0xF3) goto yy637; + if (yych <= 0xF4) goto yy638; + goto yy1; + } + } + } +yy631: + yych = *++YYCURSOR; + if (yych <= 0xC1) { + if (yych <= '\'') { + if (yych <= 0x00) goto yy1; + if (yych <= '&') goto yy631; + goto yy721; + } else { + if (yych == '\\') goto yy731; + if (yych <= 0x7F) goto yy631; + goto yy1; + } + } else { + if (yych <= 0xEF) { + if (yych <= 0xDF) goto yy732; + if (yych <= 0xE0) goto yy733; + goto yy734; + } else { + if (yych <= 0xF0) goto yy735; + if (yych <= 0xF3) goto yy736; + if (yych <= 0xF4) goto yy737; + goto yy1; + } + } +yy632: + yych = *++YYCURSOR; + if (yych <= 'Z') { + if (yych <= '.') { + if (yych <= '\n') { + if (yych <= 0x00) goto yy631; + if (yych <= '\t') goto yy525; + goto yy522; + } else { + if (yych == '-') goto yy629; + goto yy525; + } + } else { + if (yych <= '=') { + if (yych <= '/') goto yy630; + if (yych <= ':') goto yy629; + goto yy525; + } else { + if (yych <= '>') goto yy631; + if (yych <= '?') goto yy630; + if (yych <= '@') goto yy525; + goto yy629; + } + } + } else { + if (yych <= 0xC1) { + if (yych <= '`') { + if (yych == '_') goto yy629; + goto yy525; + } else { + if (yych <= 'z') goto yy629; + if (yych <= 0x7F) goto yy525; + goto yy1; + } + } else { + if (yych <= 0xEF) { + if (yych <= 0xDF) goto yy633; + if (yych <= 0xE0) goto yy634; + goto yy635; + } else { + if (yych <= 0xF0) goto yy636; + if (yych <= 0xF3) goto yy637; + if (yych <= 0xF4) goto yy638; + goto yy1; + } + } + } +yy633: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy525; + goto yy1; +yy634: + yych = *++YYCURSOR; + if (yych <= 0x9F) goto yy1; + if (yych <= 0xBF) goto yy633; + goto yy1; +yy635: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy633; + goto yy1; +yy636: + yych = *++YYCURSOR; + if (yych <= 0x8F) goto yy1; + if (yych <= 0xBF) goto yy635; + goto yy1; +yy637: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy635; + goto yy1; +yy638: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0x8F) goto yy635; + goto yy1; +yy639: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= ':') { + if (yych <= '+') { + if (yych == '%') goto yy80; + if (yych <= '*') goto yy4; + goto yy80; + } else { + if (yych <= '.') { + if (yych <= ',') goto yy4; + goto yy46; + } else { + if (yych <= '/') goto yy4; + if (yych <= '9') goto yy738; + goto yy148; + } + } + } else { + if (yych <= '^') { + if (yych <= '@') { + if (yych <= '?') goto yy4; + goto yy96; + } else { + if (yych <= 'F') goto yy738; + if (yych <= 'Z') goto yy115; + goto yy4; + } + } else { + if (yych <= '`') { + if (yych <= '_') goto yy46; + goto yy4; + } else { + if (yych <= 'f') goto yy738; + if (yych <= 'z') goto yy115; + goto yy4; + } + } + } +yy640: + yych = *++YYCURSOR; + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy739; + goto yy1; +yy641: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= ',') { + if (yych <= '!') { + if (yych <= '\f') { + if (yych <= 0x00) { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych <= 0x08) goto yy4; + if (yych <= '\n') { + yyt4 = YYCURSOR; + goto yy221; + } + goto yy4; + } else { + if (yych <= '\r') { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych <= 0x1F) goto yy4; + if (yych <= ' ') { + yyt4 = YYCURSOR; + goto yy221; + } + yyt4 = YYCURSOR; + goto yy223; + } + } else { + if (yych <= '&') { + if (yych <= '"') { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych == '%') goto yy80; + goto yy4; + } else { + if (yych <= '\'') { + yyt4 = YYCURSOR; + goto yy224; + } + if (yych <= '*') { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych <= '+') goto yy80; + yyt4 = YYCURSOR; + goto yy223; + } + } + } else { + if (yych <= '?') { + if (yych <= '9') { + if (yych <= '-') goto yy46; + if (yych <= '.') { + yyt4 = YYCURSOR; + goto yy230; + } + if (yych <= '/') goto yy4; + goto yy738; + } else { + if (yych <= ':') { + yyt3 = YYCURSOR; + goto yy234; + } + if (yych <= ';') { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych <= '>') goto yy4; + yyt4 = YYCURSOR; + goto yy223; + } + } else { + if (yych <= '^') { + if (yych <= '@') goto yy96; + if (yych <= 'F') goto yy738; + if (yych <= 'Z') goto yy115; + goto yy4; + } else { + if (yych <= '`') { + if (yych <= '_') goto yy46; + goto yy4; + } else { + if (yych <= 'f') goto yy740; + if (yych <= 'z') goto yy326; + goto yy4; + } + } + } + } +yy642: + ++YYCURSOR; + yyt1 = yyt2; +yy643: + YYCURSOR = yyt1; +#line 173 "../../lnav/src/data_scanner_re.re" + { RET(DT_TIME); } +#line 20685 "../../lnav/src/data_scanner_re.cc" +yy644: + yyaccept = 29; + yych = *(YYMARKER = ++YYCURSOR); + yyt1 = yyt2; + if (yych <= '/') goto yy643; + if (yych <= '9') goto yy741; + goto yy643; +yy645: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy642; + goto yy1; +yy646: + yych = *++YYCURSOR; + if (yych <= 0x9F) goto yy1; + if (yych <= 0xBF) goto yy645; + goto yy1; +yy647: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy645; + goto yy1; +yy648: + yych = *++YYCURSOR; + if (yych <= 0x8F) goto yy1; + if (yych <= 0xBF) goto yy647; + goto yy1; +yy649: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy647; + goto yy1; +yy650: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0x8F) goto yy647; + goto yy1; +yy651: + yych = *++YYCURSOR; + if (yych == 'm') goto yy742; + goto yy100; +yy652: + yyaccept = 5; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[768+yych] & 16) { + goto yy101; + } + if (yych <= '\'') { + if (yych <= 0x1A) { + if (yych <= '\n') { + if (yych <= 0x00) goto yy24; + if (yych <= 0x08) goto yy4; + goto yy24; + } else { + if (yych == '\r') goto yy24; + goto yy4; + } + } else { + if (yych <= ' ') { + if (yych <= 0x1B) goto yy24; + if (yych <= 0x1F) goto yy4; + goto yy743; + } else { + if (yych == '"') goto yy24; + if (yych <= '&') goto yy99; + goto yy24; + } + } + } else { + if (yych <= ']') { + if (yych <= ':') { + if (yych <= '*') goto yy99; + if (yych <= ',') goto yy24; + goto yy4; + } else { + if (yych == '\\') goto yy99; + goto yy24; + } + } else { + if (yych <= '~') { + if (yych <= '^') goto yy99; + if (yych <= '}') goto yy24; + goto yy99; + } else { + if (yych <= 0x7F) goto yy4; + if (yych <= 0xC1) goto yy24; + if (yych <= 0xF4) goto yy4; + goto yy24; + } + } + } +yy653: + ++YYCURSOR; +yy654: + YYCURSOR = yyt1; +#line 230 "../../lnav/src/data_scanner_re.re" + { + RET(DT_IPV4_ADDRESS); + } +#line 20785 "../../lnav/src/data_scanner_re.cc" +yy655: + yyaccept = 30; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= ',') { + if (yych <= '\r') { + if (yych <= 0x08) { + if (yych <= 0x00) goto yy654; + goto yy4; + } else { + if (yych <= '\n') goto yy654; + if (yych <= '\f') goto yy4; + goto yy654; + } + } else { + if (yych <= 0x1F) { + if (yych == 0x1B) goto yy654; + goto yy4; + } else { + if (yych == '$') goto yy4; + goto yy654; + } + } + } else { + if (yych <= '`') { + if (yych <= 'Z') { + if (yych <= ':') goto yy4; + if (yych <= '?') goto yy654; + goto yy4; + } else { + if (yych == '_') goto yy4; + goto yy654; + } + } else { + if (yych <= 0x7F) { + if (yych <= 'z') goto yy4; + if (yych <= '~') goto yy654; + goto yy4; + } else { + if (yych <= 0xC1) goto yy654; + if (yych <= 0xF4) goto yy4; + goto yy654; + } + } + } +yy656: + yyaccept = 30; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '/') { + if (yych <= '*') { + if (yych == '%') goto yy81; + goto yy654; + } else { + if (yych == ',') goto yy654; + if (yych <= '.') goto yy81; + goto yy654; + } + } else { + if (yych <= '^') { + if (yych <= '9') goto yy81; + if (yych <= '?') goto yy654; + if (yych <= 'Z') goto yy81; + goto yy654; + } else { + if (yych == '`') goto yy654; + if (yych <= 'z') goto yy81; + goto yy654; + } + } +yy657: + yyaccept = 30; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '.') { + if (yych <= 0x1B) { + if (yych <= '\n') { + if (yych <= 0x00) goto yy654; + if (yych <= 0x08) goto yy4; + goto yy654; + } else { + if (yych == '\r') goto yy654; + if (yych <= 0x1A) goto yy4; + goto yy654; + } + } else { + if (yych <= '%') { + if (yych <= 0x1F) goto yy4; + if (yych <= '#') goto yy654; + if (yych <= '$') goto yy4; + goto yy80; + } else { + if (yych == '+') goto yy80; + if (yych <= ',') goto yy654; + goto yy46; + } + } + } else { + if (yych <= '^') { + if (yych <= ':') { + if (yych <= '/') goto yy4; + if (yych <= '9') goto yy344; + goto yy4; + } else { + if (yych <= '?') goto yy654; + if (yych <= '@') goto yy96; + if (yych <= 'Z') goto yy344; + goto yy654; + } + } else { + if (yych <= '~') { + if (yych == '`') goto yy654; + if (yych <= 'z') goto yy344; + goto yy654; + } else { + if (yych <= 0x7F) goto yy4; + if (yych <= 0xC1) goto yy654; + if (yych <= 0xF4) goto yy4; + goto yy654; + } + } + } +yy658: + yyaccept = 30; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '.') { + if (yych <= 0x1B) { + if (yych <= '\n') { + if (yych <= 0x00) goto yy654; + if (yych <= 0x08) goto yy4; + goto yy654; + } else { + if (yych == '\r') goto yy654; + if (yych <= 0x1A) goto yy4; + goto yy654; + } + } else { + if (yych <= '%') { + if (yych <= 0x1F) goto yy4; + if (yych <= '#') goto yy654; + if (yych <= '$') goto yy4; + goto yy80; + } else { + if (yych == '+') goto yy80; + if (yych <= ',') goto yy654; + goto yy46; + } + } + } else { + if (yych <= '^') { + if (yych <= ':') { + if (yych <= '/') goto yy4; + if (yych <= '9') goto yy447; + goto yy4; + } else { + if (yych <= '?') goto yy654; + if (yych <= '@') goto yy96; + if (yych <= 'Z') goto yy46; + goto yy654; + } + } else { + if (yych <= '~') { + if (yych == '`') goto yy654; + if (yych <= 'z') goto yy46; + goto yy654; + } else { + if (yych <= 0x7F) goto yy4; + if (yych <= 0xC1) goto yy654; + if (yych <= 0xF4) goto yy4; + goto yy654; + } + } + } +yy659: + yyaccept = 20; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '9') { + if (yych <= '#') { + if (yych <= '\f') { + if (yych <= 0x00) { + yyt1 = YYCURSOR; + goto yy653; + } + if (yych <= 0x08) { + yyt1 = YYCURSOR; + goto yy655; + } + if (yych <= '\n') { + yyt1 = YYCURSOR; + goto yy653; + } + yyt1 = YYCURSOR; + goto yy655; + } else { + if (yych <= 0x1A) { + if (yych <= '\r') { + yyt1 = YYCURSOR; + goto yy653; + } + yyt1 = YYCURSOR; + goto yy655; + } else { + if (yych <= 0x1B) { + yyt1 = YYCURSOR; + goto yy653; + } + if (yych <= 0x1F) { + yyt1 = YYCURSOR; + goto yy655; + } + yyt1 = YYCURSOR; + goto yy653; + } + } + } else { + if (yych <= '+') { + if (yych <= '$') { + yyt1 = YYCURSOR; + goto yy655; + } + if (yych <= '%') { + yyt1 = YYCURSOR; + goto yy656; + } + if (yych <= '*') { + yyt1 = YYCURSOR; + goto yy653; + } + yyt1 = YYCURSOR; + goto yy656; + } else { + if (yych <= '-') { + if (yych <= ',') { + yyt1 = YYCURSOR; + goto yy653; + } + yyt1 = YYCURSOR; + goto yy657; + } else { + if (yych <= '.') { + yyt1 = YYCURSOR; + goto yy658; + } + if (yych <= '/') { + yyt1 = YYCURSOR; + goto yy655; + } + goto yy447; + } + } + } + } else { + if (yych <= '~') { + if (yych <= 'Z') { + if (yych <= ':') { + yyt1 = YYCURSOR; + goto yy660; + } + if (yych <= '?') { + yyt1 = YYCURSOR; + goto yy653; + } + if (yych <= '@') { + yyt1 = YYCURSOR; + goto yy661; + } + yyt1 = YYCURSOR; + goto yy662; + } else { + if (yych <= '_') { + if (yych <= '^') { + yyt1 = YYCURSOR; + goto yy653; + } + yyt1 = YYCURSOR; + goto yy662; + } else { + if (yych <= '`') { + yyt1 = YYCURSOR; + goto yy653; + } + if (yych <= 'z') { + yyt1 = YYCURSOR; + goto yy662; + } + yyt1 = YYCURSOR; + goto yy653; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + yyt1 = YYCURSOR; + goto yy655; + } + if (yych <= 0xC1) goto yy345; + if (yych <= 0xDF) { + yyt1 = YYCURSOR; + goto yy663; + } + yyt1 = YYCURSOR; + goto yy664; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt1 = YYCURSOR; + goto yy665; + } + yyt1 = YYCURSOR; + goto yy666; + } else { + if (yych <= 0xF3) { + yyt1 = YYCURSOR; + goto yy667; + } + if (yych <= 0xF4) { + yyt1 = YYCURSOR; + goto yy668; + } + goto yy345; + } + } + } + } +yy660: + yyaccept = 30; + yych = *(YYMARKER = ++YYCURSOR); + if (yych == ':') goto yy152; + goto yy654; +yy661: + yyaccept = 30; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '/') { + if (yych <= 0x1A) { + if (yych <= '\n') { + if (yych <= 0x00) goto yy654; + if (yych <= 0x08) goto yy4; + goto yy654; + } else { + if (yych == '\r') goto yy654; + goto yy4; + } + } else { + if (yych <= '#') { + if (yych <= 0x1B) goto yy654; + if (yych <= 0x1F) goto yy4; + goto yy654; + } else { + if (yych <= '$') goto yy4; + if (yych <= ',') goto yy654; + if (yych <= '.') goto yy174; + goto yy4; + } + } + } else { + if (yych <= '_') { + if (yych <= '?') { + if (yych <= '9') goto yy174; + if (yych <= ':') goto yy4; + goto yy654; + } else { + if (yych <= '@') goto yy4; + if (yych <= 'Z') goto yy174; + if (yych <= '^') goto yy654; + goto yy4; + } + } else { + if (yych <= '~') { + if (yych <= '`') goto yy654; + if (yych <= 'z') goto yy174; + goto yy654; + } else { + if (yych <= 0x7F) goto yy4; + if (yych <= 0xC1) goto yy654; + if (yych <= 0xF4) goto yy4; + goto yy654; + } + } + } +yy662: + yyaccept = 30; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '-') { + if (yych <= 0x1B) { + if (yych <= '\n') { + if (yych <= 0x00) goto yy654; + if (yych <= 0x08) goto yy4; + goto yy654; + } else { + if (yych == '\r') goto yy654; + if (yych <= 0x1A) goto yy4; + goto yy654; + } + } else { + if (yych <= '%') { + if (yych <= 0x1F) goto yy4; + if (yych <= '#') goto yy654; + if (yych <= '$') goto yy4; + goto yy80; + } else { + if (yych == '+') goto yy80; + if (yych <= ',') goto yy654; + goto yy255; + } + } + } else { + if (yych <= '^') { + if (yych <= ':') { + if (yych <= '.') goto yy350; + if (yych <= '/') goto yy4; + if (yych <= '9') goto yy447; + goto yy4; + } else { + if (yych <= '?') goto yy654; + if (yych <= '@') goto yy96; + if (yych <= 'Z') goto yy447; + goto yy654; + } + } else { + if (yych <= '~') { + if (yych == '`') goto yy654; + if (yych <= 'z') goto yy447; + goto yy654; + } else { + if (yych <= 0x7F) goto yy4; + if (yych <= 0xC1) goto yy654; + if (yych <= 0xF4) goto yy4; + goto yy654; + } + } + } +yy663: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy655; + goto yy1; +yy664: + yych = *++YYCURSOR; + if (yych <= 0x9F) goto yy1; + if (yych <= 0xBF) goto yy663; + goto yy1; +yy665: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy663; + goto yy1; +yy666: + yych = *++YYCURSOR; + if (yych <= 0x8F) goto yy1; + if (yych <= 0xBF) goto yy665; + goto yy1; +yy667: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy665; + goto yy1; +yy668: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0x8F) goto yy665; + goto yy1; +yy669: + yyaccept = 20; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '5') { + if (yych <= '#') { + if (yych <= '\f') { + if (yych <= 0x00) { + yyt1 = YYCURSOR; + goto yy653; + } + if (yych <= 0x08) { + yyt1 = YYCURSOR; + goto yy655; + } + if (yych <= '\n') { + yyt1 = YYCURSOR; + goto yy653; + } + yyt1 = YYCURSOR; + goto yy655; + } else { + if (yych <= 0x1A) { + if (yych <= '\r') { + yyt1 = YYCURSOR; + goto yy653; + } + yyt1 = YYCURSOR; + goto yy655; + } else { + if (yych <= 0x1B) { + yyt1 = YYCURSOR; + goto yy653; + } + if (yych <= 0x1F) { + yyt1 = YYCURSOR; + goto yy655; + } + yyt1 = YYCURSOR; + goto yy653; + } + } + } else { + if (yych <= '+') { + if (yych <= '$') { + yyt1 = YYCURSOR; + goto yy655; + } + if (yych <= '%') { + yyt1 = YYCURSOR; + goto yy656; + } + if (yych <= '*') { + yyt1 = YYCURSOR; + goto yy653; + } + yyt1 = YYCURSOR; + goto yy656; + } else { + if (yych <= '-') { + if (yych <= ',') { + yyt1 = YYCURSOR; + goto yy653; + } + yyt1 = YYCURSOR; + goto yy657; + } else { + if (yych <= '.') { + yyt1 = YYCURSOR; + goto yy658; + } + if (yych <= '/') { + yyt1 = YYCURSOR; + goto yy655; + } + goto yy659; + } + } + } + } else { + if (yych <= 'z') { + if (yych <= '@') { + if (yych <= '9') goto yy447; + if (yych <= ':') { + yyt1 = YYCURSOR; + goto yy660; + } + if (yych <= '?') { + yyt1 = YYCURSOR; + goto yy653; + } + yyt1 = YYCURSOR; + goto yy661; + } else { + if (yych <= '^') { + if (yych <= 'Z') { + yyt1 = YYCURSOR; + goto yy662; + } + yyt1 = YYCURSOR; + goto yy653; + } else { + if (yych == '`') { + yyt1 = YYCURSOR; + goto yy653; + } + yyt1 = YYCURSOR; + goto yy662; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + if (yych <= '~') { + yyt1 = YYCURSOR; + goto yy653; + } + yyt1 = YYCURSOR; + goto yy655; + } else { + if (yych <= 0xC1) goto yy345; + if (yych <= 0xDF) { + yyt1 = YYCURSOR; + goto yy663; + } + yyt1 = YYCURSOR; + goto yy664; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt1 = YYCURSOR; + goto yy665; + } + yyt1 = YYCURSOR; + goto yy666; + } else { + if (yych <= 0xF3) { + yyt1 = YYCURSOR; + goto yy667; + } + if (yych <= 0xF4) { + yyt1 = YYCURSOR; + goto yy668; + } + goto yy345; + } + } + } + } +yy670: + yyaccept = 21; + yych = *(YYMARKER = ++YYCURSOR); + if (yych == '-') goto yy466; + if (yych == ':') goto yy466; + goto yy354; +yy671: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '/') goto yy4; + if (yych <= '9') goto yy744; + goto yy4; +yy672: + yych = *++YYCURSOR; + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy745; + goto yy1; +yy673: + yyaccept = 31; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '.') { + if (yych <= 0x1F) { + if (yych <= '\f') { + if (yych <= 0x00) goto yy674; + if (yych <= 0x08) goto yy4; + if (yych >= '\v') goto yy4; + } else { + if (yych <= '\r') goto yy674; + if (yych != 0x1B) goto yy4; + } + } else { + if (yych <= '%') { + if (yych <= ' ') goto yy746; + if (yych <= '#') goto yy674; + if (yych <= '$') goto yy4; + goto yy80; + } else { + if (yych == '+') goto yy80; + if (yych >= '-') goto yy46; + } + } + } else { + if (yych <= 'Z') { + if (yych <= '?') { + if (yych <= '/') goto yy4; + if (yych <= '9') goto yy747; + if (yych <= ':') goto yy4; + } else { + if (yych <= '@') goto yy96; + if (yych == 'T') goto yy748; + goto yy46; + } + } else { + if (yych <= 'z') { + if (yych == '_') goto yy46; + if (yych >= 'a') goto yy46; + } else { + if (yych <= 0x7F) { + if (yych >= 0x7F) goto yy4; + } else { + if (yych <= 0xC1) goto yy674; + if (yych <= 0xF4) goto yy4; + } + } + } + } +yy674: +#line 187 "../../lnav/src/data_scanner_re.re" + { + RET(DT_DATE); + } +#line 21464 "../../lnav/src/data_scanner_re.cc" +yy675: + yyaccept = 31; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '9') { + if (yych <= 0x1B) { + if (yych <= '\n') { + if (yych <= 0x00) goto yy674; + if (yych <= 0x08) goto yy4; + goto yy674; + } else { + if (yych == '\r') goto yy674; + if (yych <= 0x1A) goto yy4; + goto yy674; + } + } else { + if (yych <= '#') { + if (yych <= 0x1F) goto yy4; + if (yych <= ' ') goto yy746; + goto yy674; + } else { + if (yych <= '$') goto yy4; + if (yych <= ',') goto yy674; + if (yych <= '/') goto yy4; + goto yy749; + } + } + } else { + if (yych <= '_') { + if (yych <= 'S') { + if (yych <= ':') goto yy4; + if (yych <= '?') goto yy674; + goto yy4; + } else { + if (yych <= 'T') goto yy750; + if (yych <= 'Z') goto yy4; + if (yych <= '^') goto yy674; + goto yy4; + } + } else { + if (yych <= '~') { + if (yych <= '`') goto yy674; + if (yych <= 'z') goto yy4; + goto yy674; + } else { + if (yych <= 0x7F) goto yy4; + if (yych <= 0xC1) goto yy674; + if (yych <= 0xF4) goto yy4; + goto yy674; + } + } + } +yy676: + yyaccept = 11; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[768+yych] & 32) { + goto yy103; + } + if (yych <= '9') { + if (yych <= '#') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy109; + if (yych <= 0x08) goto yy4; + if (yych <= '\n') goto yy109; + goto yy4; + } else { + if (yych <= 0x1A) { + if (yych <= '\r') goto yy109; + goto yy4; + } else { + if (yych <= 0x1B) goto yy109; + if (yych <= 0x1F) goto yy4; + goto yy109; + } + } + } else { + if (yych <= ',') { + if (yych <= '%') { + if (yych <= '$') goto yy4; + goto yy105; + } else { + if (yych == '+') goto yy80; + goto yy109; + } + } else { + if (yych <= '.') { + if (yych <= '-') goto yy751; + goto yy266; + } else { + if (yych <= '/') goto yy4; + if (yych <= '7') goto yy752; + goto yy753; + } + } + } + } else { + if (yych <= '_') { + if (yych <= 'D') { + if (yych <= ':') goto yy148; + if (yych <= '?') goto yy109; + if (yych <= '@') goto yy96; + goto yy196; + } else { + if (yych <= 'F') { + if (yych <= 'E') goto yy754; + goto yy196; + } else { + if (yych <= 'Z') goto yy115; + if (yych <= '^') goto yy109; + goto yy46; + } + } + } else { + if (yych <= 'z') { + if (yych <= 'd') { + if (yych <= '`') goto yy109; + goto yy196; + } else { + if (yych <= 'e') goto yy754; + if (yych <= 'f') goto yy196; + goto yy115; + } + } else { + if (yych <= 0x7F) { + if (yych <= '~') goto yy109; + goto yy4; + } else { + if (yych <= 0xC1) goto yy109; + if (yych <= 0xF4) goto yy4; + goto yy109; + } + } + } + } +yy677: + yyaccept = 6; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[768+yych] & 32) { + goto yy103; + } + if (yych <= '9') { + if (yych <= '#') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy26; + if (yych <= 0x08) goto yy4; + if (yych <= '\n') goto yy26; + goto yy4; + } else { + if (yych <= 0x1A) { + if (yych <= '\r') goto yy26; + goto yy4; + } else { + if (yych <= 0x1B) goto yy26; + if (yych <= 0x1F) goto yy4; + goto yy26; + } + } + } else { + if (yych <= '+') { + if (yych <= '$') goto yy4; + if (yych <= '%') goto yy105; + if (yych <= '*') goto yy26; + goto yy80; + } else { + if (yych <= '-') { + if (yych <= ',') goto yy26; + goto yy751; + } else { + if (yych <= '.') goto yy266; + if (yych <= '/') goto yy4; + goto yy753; + } + } + } + } else { + if (yych <= '_') { + if (yych <= 'D') { + if (yych <= ':') goto yy148; + if (yych <= '?') goto yy26; + if (yych <= '@') goto yy96; + goto yy196; + } else { + if (yych <= 'F') { + if (yych <= 'E') goto yy754; + goto yy196; + } else { + if (yych <= 'Z') goto yy115; + if (yych <= '^') goto yy26; + goto yy46; + } + } + } else { + if (yych <= 'z') { + if (yych <= 'd') { + if (yych <= '`') goto yy26; + goto yy196; + } else { + if (yych <= 'e') goto yy754; + if (yych <= 'f') goto yy196; + goto yy115; + } + } else { + if (yych <= 0x7F) { + if (yych <= '~') goto yy26; + goto yy4; + } else { + if (yych <= 0xC1) goto yy26; + if (yych <= 0xF4) goto yy4; + goto yy26; + } + } + } + } +yy678: + yyaccept = 12; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '.') { + if (yych <= 0x1F) { + if (yych <= '\f') { + if (yych <= 0x00) goto yy113; + if (yych <= 0x08) goto yy4; + if (yych <= '\n') goto yy113; + goto yy4; + } else { + if (yych <= '\r') goto yy113; + if (yych == 0x1B) goto yy113; + goto yy4; + } + } else { + if (yych <= '*') { + if (yych <= '#') goto yy113; + if (yych <= '$') goto yy4; + if (yych <= '%') goto yy80; + goto yy113; + } else { + if (yych <= '+') goto yy80; + if (yych <= ',') goto yy113; + if (yych <= '-') goto yy751; + goto yy46; + } + } + } else { + if (yych <= '^') { + if (yych <= '?') { + if (yych <= '/') goto yy4; + if (yych <= '9') goto yy196; + if (yych <= ':') goto yy148; + goto yy113; + } else { + if (yych <= '@') goto yy96; + if (yych <= 'F') goto yy196; + if (yych <= 'Z') goto yy115; + goto yy113; + } + } else { + if (yych <= 'z') { + if (yych <= '_') goto yy46; + if (yych <= '`') goto yy113; + if (yych <= 'f') goto yy196; + goto yy115; + } else { + if (yych <= 0x7F) { + if (yych <= '~') goto yy113; + goto yy4; + } else { + if (yych <= 0xC1) goto yy113; + if (yych <= 0xF4) goto yy4; + goto yy113; + } + } + } + } +yy679: + yyaccept = 12; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '.') { + if (yych <= 0x1F) { + if (yych <= '\f') { + if (yych <= 0x00) goto yy113; + if (yych <= 0x08) goto yy4; + if (yych <= '\n') goto yy113; + goto yy4; + } else { + if (yych <= '\r') goto yy113; + if (yych == 0x1B) goto yy113; + goto yy4; + } + } else { + if (yych <= '*') { + if (yych <= '#') goto yy113; + if (yych <= '$') goto yy4; + if (yych <= '%') goto yy80; + goto yy113; + } else { + if (yych <= '+') goto yy194; + if (yych <= ',') goto yy113; + if (yych <= '-') goto yy755; + goto yy46; + } + } + } else { + if (yych <= '^') { + if (yych <= '?') { + if (yych <= '/') goto yy4; + if (yych <= '9') goto yy196; + if (yych <= ':') goto yy148; + goto yy113; + } else { + if (yych <= '@') goto yy96; + if (yych <= 'F') goto yy196; + if (yych <= 'Z') goto yy115; + goto yy113; + } + } else { + if (yych <= 'z') { + if (yych <= '_') goto yy46; + if (yych <= '`') goto yy113; + if (yych <= 'f') goto yy196; + goto yy115; + } else { + if (yych <= 0x7F) { + if (yych <= '~') goto yy113; + goto yy4; + } else { + if (yych <= 0xC1) goto yy113; + if (yych <= 0xF4) goto yy4; + goto yy113; + } + } + } + } +yy680: + yyaccept = 18; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '@') { + if (yych <= '/') { + yyt2 = yyt1; + goto yy199; + } + if (yych <= '9') goto yy553; + yyt2 = yyt1; + goto yy199; + } else { + if (yych <= 'Z') goto yy553; + if (yych <= '`') { + yyt2 = yyt1; + goto yy199; + } + if (yych <= 'z') goto yy553; + yyt2 = yyt1; + goto yy199; + } +yy681: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '?') { + if (yych <= '#') { + if (yych <= '\f') { + if (yych <= 0x00) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0x08) { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= '\n') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= 0x1A) { + if (yych <= '\r') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= 0x1B) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0x1F) { + yyt2 = YYCURSOR; + goto yy284; + } + yyt2 = YYCURSOR; + goto yy198; + } + } + } else { + if (yych <= '-') { + if (yych <= '$') { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= '%') { + yyt1 = YYCURSOR; + goto yy680; + } + if (yych <= ',') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= '/') { + if (yych <= '.') { + yyt2 = YYCURSOR; + goto yy376; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= '9') goto yy756; + if (yych >= ';') { + yyt2 = YYCURSOR; + goto yy198; + } + } + } + } + } else { + if (yych <= '~') { + if (yych <= '^') { + if (yych <= '@') { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= 'F') goto yy756; + if (yych <= 'Z') goto yy3; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= '`') { + if (yych <= '_') { + yyt2 = YYCURSOR; + goto yy284; + } + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 'f') goto yy756; + if (yych <= 'z') goto yy3; + yyt2 = YYCURSOR; + goto yy198; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= 0xC1) goto yy5; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy289; + } + yyt2 = YYCURSOR; + goto yy290; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy291; + } + yyt2 = YYCURSOR; + goto yy292; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy293; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy294; + } + goto yy5; + } + } + } + } +yy682: + yych = *++YYCURSOR; + if (yych <= '@') { + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy757; + if (yych <= ':') goto yy152; + goto yy1; + } else { + if (yych <= 'F') goto yy757; + if (yych <= '`') goto yy1; + if (yych <= 'f') goto yy757; + goto yy1; + } +yy683: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '@') { + if (yych <= 0x1F) { + if (yych <= '\f') { + if (yych <= 0x00) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0x08) { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= '\n') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= '\r') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych == 0x1B) { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } + } else { + if (yych <= ',') { + if (yych <= '#') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '$') { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= '%') { + yyt1 = YYCURSOR; + goto yy680; + } + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= '9') { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy284; + } + goto yy756; + } else { + if (yych <= ':') goto yy682; + if (yych <= '?') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } + } + } + } else { + if (yych <= '~') { + if (yych <= '_') { + if (yych <= 'F') goto yy756; + if (yych <= 'Z') goto yy3; + if (yych <= '^') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 'f') goto yy756; + if (yych <= 'z') goto yy3; + yyt2 = YYCURSOR; + goto yy198; + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= 0xC1) goto yy5; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy289; + } + yyt2 = YYCURSOR; + goto yy290; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy291; + } + yyt2 = YYCURSOR; + goto yy292; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy293; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy294; + } + goto yy5; + } + } + } + } +yy684: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '?') { + if (yych <= '#') { + if (yych <= '\f') { + if (yych <= 0x00) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0x08) { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= '\n') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= 0x1A) { + if (yych <= '\r') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= 0x1B) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0x1F) { + yyt2 = YYCURSOR; + goto yy284; + } + yyt2 = YYCURSOR; + goto yy198; + } + } + } else { + if (yych <= '-') { + if (yych <= '$') { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= '%') { + yyt1 = YYCURSOR; + goto yy680; + } + if (yych <= ',') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= '/') { + if (yych <= '.') { + yyt2 = YYCURSOR; + goto yy376; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= '9') goto yy758; + if (yych <= ':') goto yy682; + yyt2 = YYCURSOR; + goto yy198; + } + } + } + } else { + if (yych <= '~') { + if (yych <= '^') { + if (yych <= '@') { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= 'F') goto yy756; + if (yych <= 'Z') goto yy3; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= '`') { + if (yych <= '_') { + yyt2 = YYCURSOR; + goto yy284; + } + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 'f') goto yy756; + if (yych <= 'z') goto yy3; + yyt2 = YYCURSOR; + goto yy198; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= 0xC1) goto yy5; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy289; + } + yyt2 = YYCURSOR; + goto yy290; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy291; + } + yyt2 = YYCURSOR; + goto yy292; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy293; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy294; + } + goto yy5; + } + } + } + } +yy685: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= ':') { + if (yych <= '#') { + if (yych <= '\f') { + if (yych <= 0x00) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0x08) { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= '\n') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= 0x1A) { + if (yych <= '\r') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= 0x1B) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0x1F) { + yyt2 = YYCURSOR; + goto yy284; + } + yyt2 = YYCURSOR; + goto yy198; + } + } + } else { + if (yych <= '-') { + if (yych <= '$') { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= '%') { + yyt1 = YYCURSOR; + goto yy680; + } + if (yych <= ',') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= '/') { + if (yych <= '.') { + yyt2 = YYCURSOR; + goto yy376; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= '5') goto yy758; + if (yych <= '9') goto yy756; + goto yy682; + } + } + } + } else { + if (yych <= 'z') { + if (yych <= 'Z') { + if (yych <= '?') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '@') { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= 'F') goto yy756; + goto yy3; + } else { + if (yych <= '_') { + if (yych <= '^') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 'f') goto yy756; + goto yy3; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + if (yych <= '~') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= 0xC1) goto yy5; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy289; + } + yyt2 = YYCURSOR; + goto yy290; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy291; + } + yyt2 = YYCURSOR; + goto yy292; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy293; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy294; + } + goto yy5; + } + } + } + } +yy686: + yyaccept = 21; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '`') { + if (yych <= '/') { + if (yych <= ',') { + yyt2 = YYCURSOR; + goto yy642; + } + if (yych <= '-') { + yyt1 = YYCURSOR; + goto yy759; + } + if (yych <= '.') { + yyt2 = YYCURSOR; + goto yy644; + } + yyt2 = YYCURSOR; + goto yy642; + } else { + if (yych <= ':') { + if (yych <= '9') { + yyt1 = YYCURSOR; + goto yy688; + } + goto yy760; + } else { + if (yych <= '@') { + yyt2 = YYCURSOR; + goto yy642; + } + if (yych <= 'F') { + yyt1 = YYCURSOR; + goto yy688; + } + yyt2 = YYCURSOR; + goto yy642; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + if (yych <= 'f') { + yyt1 = YYCURSOR; + goto yy688; + } + yyt2 = YYCURSOR; + goto yy642; + } else { + if (yych <= 0xC1) goto yy354; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy645; + } + yyt2 = YYCURSOR; + goto yy646; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy647; + } + yyt2 = YYCURSOR; + goto yy648; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy649; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy650; + } + goto yy354; + } + } + } +yy687: + yyaccept = 21; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= ':') { + if (yych <= '-') { + if (yych <= ',') goto yy354; + goto yy466; + } else { + if (yych <= '/') goto yy354; + if (yych <= '9') goto yy561; + goto yy760; + } + } else { + if (yych <= 'F') { + if (yych <= '@') goto yy354; + goto yy561; + } else { + if (yych <= '`') goto yy354; + if (yych <= 'f') goto yy561; + goto yy354; + } + } +yy688: + yyaccept = 29; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '@') { + if (yych <= '/') goto yy643; + if (yych <= '9') goto yy689; + if (yych <= ':') goto yy471; + goto yy643; + } else { + if (yych <= 'F') goto yy689; + if (yych <= '`') goto yy643; + if (yych >= 'g') goto yy643; + } +yy689: + yych = *++YYCURSOR; + if (yych == ':') goto yy471; + goto yy1; +yy690: + yych = *++YYCURSOR; + if (yych <= '@') { + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy761; + if (yych >= ';') goto yy1; + } else { + if (yych <= 'F') goto yy761; + if (yych <= '`') goto yy1; + if (yych <= 'f') goto yy761; + goto yy1; + } +yy691: + yych = *++YYCURSOR; + if (yych <= '@') { + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy762; + if (yych <= ':') goto yy763; + goto yy1; + } else { + if (yych <= 'F') goto yy762; + if (yych <= '`') goto yy1; + if (yych <= 'f') goto yy762; + goto yy1; + } +yy692: + yych = *++YYCURSOR; + if (yych <= 'f') { + if (yych <= ':') { + if (yych <= '.') { + if (yych <= '-') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt1 = YYCURSOR; + goto yy297; + } else { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy764; + goto yy765; + } + } else { + if (yych <= 'F') { + if (yych <= '@') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy766; + } else { + if (yych <= 'Z') goto yy1; + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy766; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + if (yych <= 'z') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + yyt2 = YYCURSOR; + goto yy206; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy693: + yych = *++YYCURSOR; + if (yych <= 'f') { + if (yych <= ':') { + if (yych <= '.') { + if (yych <= '-') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt1 = YYCURSOR; + goto yy297; + } else { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy767; + goto yy765; + } + } else { + if (yych <= 'F') { + if (yych <= '@') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy766; + } else { + if (yych <= 'Z') goto yy1; + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy766; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + if (yych <= 'z') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + yyt2 = YYCURSOR; + goto yy206; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy694: + yych = *++YYCURSOR; + if (yych <= '`') { + if (yych <= '5') { + if (yych <= '.') { + if (yych <= '-') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt1 = YYCURSOR; + goto yy297; + } else { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '4') goto yy767; + goto yy768; + } + } else { + if (yych <= '@') { + if (yych <= '9') goto yy764; + if (yych <= ':') goto yy765; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 'F') goto yy766; + if (yych <= 'Z') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } + } + } else { + if (yych <= 0xDF) { + if (yych <= 'z') { + if (yych <= 'f') goto yy766; + goto yy1; + } else { + if (yych <= 0x7F) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0xC1) goto yy1; + yyt2 = YYCURSOR; + goto yy205; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xE0) { + yyt2 = YYCURSOR; + goto yy206; + } + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy695: + yych = *++YYCURSOR; + if (yych <= 'z') { + if (yych <= '@') { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy766; + if (yych <= ':') goto yy765; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 'Z') { + if (yych <= 'F') goto yy766; + goto yy1; + } else { + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 'f') goto yy766; + goto yy1; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + yyt2 = YYCURSOR; + goto yy206; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy696: + yych = *++YYCURSOR; + if (yych <= 'z') { + if (yych <= '@') { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy769; + if (yych <= ':') goto yy565; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 'Z') { + if (yych <= 'F') goto yy769; + goto yy1; + } else { + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 'f') goto yy769; + goto yy1; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + yyt2 = YYCURSOR; + goto yy206; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy697: + yych = *++YYCURSOR; + if (yych <= 'f') { + if (yych <= ':') { + if (yych <= '.') { + if (yych <= '-') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt1 = YYCURSOR; + goto yy297; + } else { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy769; + goto yy565; + } + } else { + if (yych <= 'F') { + if (yych <= '@') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy769; + } else { + if (yych <= 'Z') goto yy1; + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy769; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + if (yych <= 'z') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + yyt2 = YYCURSOR; + goto yy206; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy698: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych == '.') goto yy770; + if (yych <= '/') goto yy4; + if (yych <= '9') goto yy771; + goto yy4; +yy699: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych == '.') goto yy770; + if (yych <= '/') goto yy4; + if (yych <= '9') goto yy698; + goto yy4; +yy700: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '/') { + if (yych == '.') goto yy770; + goto yy4; + } else { + if (yych <= '4') goto yy698; + if (yych <= '5') goto yy772; + if (yych <= '9') goto yy771; + goto yy4; + } +yy701: + yych = *++YYCURSOR; + if (yych <= '1') { + if (yych <= '/') goto yy1; + if (yych <= '0') goto yy773; + goto yy774; + } else { + if (yych <= '2') goto yy775; + if (yych <= '9') goto yy773; + goto yy1; + } +yy702: + yych = *++YYCURSOR; + if (yych == '.') goto yy701; + goto yy1; +yy703: + yych = *++YYCURSOR; + if (yych == '.') goto yy701; + if (yych <= '/') goto yy1; + if (yych <= '5') goto yy702; + goto yy1; +yy704: + yych = *++YYCURSOR; + if (yych <= 0x7F) { + if (yych <= '@') { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy1; + if (yych <= ':') goto yy486; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 'Z') goto yy1; + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 'z') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } + } else { + if (yych <= 0xEF) { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + if (yych <= 0xE0) { + yyt2 = YYCURSOR; + goto yy206; + } + yyt2 = YYCURSOR; + goto yy207; + } else { + if (yych <= 0xF0) { + yyt2 = YYCURSOR; + goto yy208; + } + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } +yy705: + yych = *++YYCURSOR; + if (yych <= 'f') { + if (yych <= ':') { + if (yych <= '.') { + if (yych <= '-') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt1 = YYCURSOR; + goto yy297; + } else { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy776; + goto yy777; + } + } else { + if (yych <= 'F') { + if (yych <= '@') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy485; + } else { + if (yych <= 'Z') goto yy1; + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy485; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + if (yych <= 'z') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + yyt2 = YYCURSOR; + goto yy206; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy706: + yych = *++YYCURSOR; + if (yych <= 'f') { + if (yych <= ':') { + if (yych <= '.') { + if (yych <= '-') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt1 = YYCURSOR; + goto yy297; + } else { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy778; + goto yy486; + } + } else { + if (yych <= 'F') { + if (yych <= '@') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy485; + } else { + if (yych <= 'Z') goto yy1; + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy485; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + if (yych <= 'z') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + yyt2 = YYCURSOR; + goto yy206; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy707: + yych = *++YYCURSOR; + if (yych <= '`') { + if (yych <= '5') { + if (yych <= '.') { + if (yych <= '-') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt1 = YYCURSOR; + goto yy297; + } else { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '4') goto yy778; + goto yy779; + } + } else { + if (yych <= '@') { + if (yych <= '9') goto yy776; + if (yych <= ':') goto yy486; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 'F') goto yy485; + if (yych <= 'Z') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } + } + } else { + if (yych <= 0xDF) { + if (yych <= 'z') { + if (yych <= 'f') goto yy485; + goto yy1; + } else { + if (yych <= 0x7F) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0xC1) goto yy1; + yyt2 = YYCURSOR; + goto yy205; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xE0) { + yyt2 = YYCURSOR; + goto yy206; + } + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy708: + yych = *++YYCURSOR; + if (yych <= 'f') { + if (yych <= ':') { + if (yych <= '.') { + if (yych <= '-') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt1 = YYCURSOR; + goto yy297; + } else { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy776; + goto yy486; + } + } else { + if (yych <= 'F') { + if (yych <= '@') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy485; + } else { + if (yych <= 'Z') goto yy1; + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy485; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + if (yych <= 'z') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + yyt2 = YYCURSOR; + goto yy206; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy709: + yyaccept = 27; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[0+yych] & 128) { + goto yy780; + } + if (yych <= 0xDF) { + if (yych <= '\'') { + if (yych <= 0x00) goto yy310; + if (yych <= '"') goto yy781; + goto yy782; + } else { + if (yych <= '\\') goto yy783; + if (yych <= 0xC1) goto yy310; + goto yy784; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xE0) goto yy785; + if (yych <= 0xEF) goto yy786; + goto yy787; + } else { + if (yych <= 0xF3) goto yy788; + if (yych <= 0xF4) goto yy789; + goto yy310; + } + } +yy710: + yych = *++YYCURSOR; + if (yych <= 0xC1) { + if (yych <= '\n') { + if (yych <= 0x00) goto yy780; + if (yych <= '\t') goto yy579; + goto yy490; + } else { + if (yych == '>') goto yy709; + if (yych <= 0x7F) goto yy579; + goto yy1; + } + } else { + if (yych <= 0xEF) { + if (yych <= 0xDF) goto yy711; + if (yych <= 0xE0) goto yy712; + goto yy713; + } else { + if (yych <= 0xF0) goto yy714; + if (yych <= 0xF3) goto yy715; + if (yych <= 0xF4) goto yy716; + goto yy1; + } + } +yy711: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy579; + goto yy1; +yy712: + yych = *++YYCURSOR; + if (yych <= 0x9F) goto yy1; + if (yych <= 0xBF) goto yy711; + goto yy1; +yy713: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy711; + goto yy1; +yy714: + yych = *++YYCURSOR; + if (yych <= 0x8F) goto yy1; + if (yych <= 0xBF) goto yy713; + goto yy1; +yy715: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy713; + goto yy1; +yy716: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0x8F) goto yy713; + goto yy1; +yy717: + yych = *++YYCURSOR; + if (yybm[0+yych] & 64) { + goto yy717; + } + if (yych <= 'Z') { + if (yych <= '-') { + if (yych <= 0x00) goto yy1; + if (yych <= '"') goto yy413; + if (yych <= '\'') goto yy412; + goto yy790; + } else { + if (yych <= ':') { + if (yych <= '/') goto yy791; + goto yy790; + } else { + if (yych <= '>') goto yy792; + if (yych <= '?') goto yy791; + goto yy790; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 'z') { + if (yych <= '\\') goto yy793; + goto yy790; + } else { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) goto yy794; + goto yy795; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) goto yy796; + goto yy797; + } else { + if (yych <= 0xF3) goto yy798; + if (yych <= 0xF4) goto yy799; + goto yy1; + } + } + } +yy718: + yych = *++YYCURSOR; + if (yych <= ' ') { + if (yych <= '\f') { + if (yych == '\t') goto yy718; + goto yy1; + } else { + if (yych <= '\r') goto yy718; + if (yych <= 0x1F) goto yy1; + goto yy718; + } + } else { + if (yych <= '=') { + if (yych == '/') goto yy215; + goto yy1; + } else { + if (yych <= '>') goto yy216; + if (yych <= '?') goto yy215; + goto yy1; + } + } +yy719: + yych = *++YYCURSOR; + if (yych <= '?') { + if (yych <= '&') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy1; + if (yych == '\t') goto yy719; + goto yy524; + } else { + if (yych <= '\r') goto yy719; + if (yych == ' ') goto yy719; + goto yy524; + } + } else { + if (yych <= '.') { + if (yych <= '\'') goto yy800; + if (yych == '-') goto yy619; + goto yy524; + } else { + if (yych <= ':') { + if (yych <= '/') goto yy620; + goto yy619; + } else { + if (yych <= '=') goto yy524; + if (yych <= '>') goto yy621; + goto yy620; + } + } + } + } else { + if (yych <= 'z') { + if (yych <= '\\') { + if (yych <= '@') goto yy524; + if (yych <= 'Z') goto yy619; + if (yych <= '[') goto yy524; + goto yy622; + } else { + if (yych == '_') goto yy619; + if (yych <= '`') goto yy524; + goto yy619; + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) goto yy524; + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) goto yy623; + goto yy624; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) goto yy625; + goto yy626; + } else { + if (yych <= 0xF3) goto yy627; + if (yych <= 0xF4) goto yy628; + goto yy1; + } + } + } + } +yy720: + yyaccept = 28; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= 0xC1) { + if (yych <= '"') { + if (yych <= 0x00) goto yy315; + if (yych <= '!') goto yy621; + } else { + if (yych == '\\') goto yy722; + if (yych <= 0x7F) goto yy621; + goto yy315; + } + } else { + if (yych <= 0xEF) { + if (yych <= 0xDF) goto yy723; + if (yych <= 0xE0) goto yy724; + goto yy725; + } else { + if (yych <= 0xF0) goto yy726; + if (yych <= 0xF3) goto yy727; + if (yych <= 0xF4) goto yy728; + goto yy315; + } + } +yy721: + yych = *++YYCURSOR; + if (yych <= '/') { + if (yych <= 0x1F) { + if (yych <= '\t') { + if (yych <= 0x08) goto yy1; + goto yy801; + } else { + if (yych == '\r') goto yy801; + goto yy1; + } + } else { + if (yych <= ',') { + if (yych <= ' ') goto yy801; + goto yy1; + } else { + if (yych <= '-') goto yy420; + if (yych <= '.') goto yy1; + goto yy215; + } + } + } else { + if (yych <= 'Z') { + if (yych <= '>') { + if (yych <= ':') goto yy420; + goto yy1; + } else { + if (yych <= '?') goto yy215; + if (yych <= '@') goto yy1; + goto yy420; + } + } else { + if (yych <= '_') { + if (yych <= '^') goto yy1; + goto yy420; + } else { + if (yych <= '`') goto yy1; + if (yych <= 'z') goto yy420; + goto yy1; + } + } + } +yy722: + yych = *++YYCURSOR; + if (yych <= 0xDF) { + if (yych <= '\n') { + if (yych <= '\t') goto yy621; + goto yy1; + } else { + if (yych <= 0x7F) goto yy621; + if (yych <= 0xC1) goto yy1; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xE0) goto yy724; + if (yych <= 0xEF) goto yy725; + goto yy726; + } else { + if (yych <= 0xF3) goto yy727; + if (yych <= 0xF4) goto yy728; + goto yy1; + } + } +yy723: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy621; + goto yy1; +yy724: + yych = *++YYCURSOR; + if (yych <= 0x9F) goto yy1; + if (yych <= 0xBF) goto yy723; + goto yy1; +yy725: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy723; + goto yy1; +yy726: + yych = *++YYCURSOR; + if (yych <= 0x8F) goto yy1; + if (yych <= 0xBF) goto yy725; + goto yy1; +yy727: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy725; + goto yy1; +yy728: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0x8F) goto yy725; + goto yy1; +yy729: + yych = *++YYCURSOR; + if (yych <= '?') { + if (yych <= '!') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy1; + if (yych == '\t') goto yy729; + goto yy525; + } else { + if (yych <= '\r') goto yy729; + if (yych == ' ') goto yy729; + goto yy525; + } + } else { + if (yych <= '.') { + if (yych <= '"') goto yy800; + if (yych == '-') goto yy629; + goto yy525; + } else { + if (yych <= ':') { + if (yych <= '/') goto yy630; + goto yy629; + } else { + if (yych <= '=') goto yy525; + if (yych <= '>') goto yy631; + goto yy630; + } + } + } + } else { + if (yych <= 'z') { + if (yych <= '\\') { + if (yych <= '@') goto yy525; + if (yych <= 'Z') goto yy629; + if (yych <= '[') goto yy525; + goto yy632; + } else { + if (yych == '_') goto yy629; + if (yych <= '`') goto yy525; + goto yy629; + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) goto yy525; + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) goto yy633; + goto yy634; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) goto yy635; + goto yy636; + } else { + if (yych <= 0xF3) goto yy637; + if (yych <= 0xF4) goto yy638; + goto yy1; + } + } + } + } +yy730: + yyaccept = 28; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= 0xC1) { + if (yych <= '\'') { + if (yych <= 0x00) goto yy315; + if (yych <= '&') goto yy631; + goto yy721; + } else { + if (yych == '\\') goto yy731; + if (yych <= 0x7F) goto yy631; + goto yy315; + } + } else { + if (yych <= 0xEF) { + if (yych <= 0xDF) goto yy732; + if (yych <= 0xE0) goto yy733; + goto yy734; + } else { + if (yych <= 0xF0) goto yy735; + if (yych <= 0xF3) goto yy736; + if (yych <= 0xF4) goto yy737; + goto yy315; + } + } +yy731: + yych = *++YYCURSOR; + if (yych <= 0xDF) { + if (yych <= '\n') { + if (yych <= '\t') goto yy631; + goto yy1; + } else { + if (yych <= 0x7F) goto yy631; + if (yych <= 0xC1) goto yy1; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xE0) goto yy733; + if (yych <= 0xEF) goto yy734; + goto yy735; + } else { + if (yych <= 0xF3) goto yy736; + if (yych <= 0xF4) goto yy737; + goto yy1; + } + } +yy732: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy631; + goto yy1; +yy733: + yych = *++YYCURSOR; + if (yych <= 0x9F) goto yy1; + if (yych <= 0xBF) goto yy732; + goto yy1; +yy734: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy732; + goto yy1; +yy735: + yych = *++YYCURSOR; + if (yych <= 0x8F) goto yy1; + if (yych <= 0xBF) goto yy734; + goto yy1; +yy736: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy734; + goto yy1; +yy737: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0x8F) goto yy734; + goto yy1; +yy738: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[768+yych] & 64) { + goto yy115; + } + if (yych <= '.') { + if (yych <= '*') { + if (yych == '%') goto yy80; + goto yy4; + } else { + if (yych <= '+') goto yy80; + if (yych <= ',') goto yy4; + if (yych <= '-') goto yy751; + goto yy46; + } + } else { + if (yych <= '?') { + if (yych <= '/') goto yy4; + if (yych <= ':') goto yy148; + goto yy4; + } else { + if (yych <= '@') goto yy96; + if (yych == '_') goto yy46; + goto yy4; + } + } +yy739: + yych = *++YYCURSOR; + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy802; + goto yy1; +yy740: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '+') { + if (yych <= ' ') { + if (yych <= '\n') { + if (yych <= 0x00) { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych <= 0x08) goto yy4; + yyt4 = YYCURSOR; + goto yy221; + } else { + if (yych == '\r') { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych <= 0x1F) goto yy4; + yyt4 = YYCURSOR; + goto yy221; + } + } else { + if (yych <= '%') { + if (yych <= '!') { + yyt4 = YYCURSOR; + goto yy223; + } + if (yych <= '"') { + yyt4 = YYCURSOR; + goto yy221; + } + if (yych <= '$') goto yy4; + goto yy80; + } else { + if (yych <= '&') goto yy4; + if (yych <= '\'') { + yyt4 = YYCURSOR; + goto yy224; + } + if (yych <= '*') { + yyt4 = YYCURSOR; + goto yy221; + } + goto yy80; + } + } + } else { + if (yych <= '>') { + if (yych <= '/') { + if (yych <= ',') { + yyt4 = YYCURSOR; + goto yy223; + } + if (yych <= '-') goto yy751; + if (yych <= '.') { + yyt4 = YYCURSOR; + goto yy230; + } + goto yy4; + } else { + if (yych <= '9') goto yy115; + if (yych <= ':') { + yyt3 = YYCURSOR; + goto yy234; + } + if (yych <= ';') { + yyt4 = YYCURSOR; + goto yy221; + } + goto yy4; + } + } else { + if (yych <= '^') { + if (yych <= '?') { + yyt4 = YYCURSOR; + goto yy223; + } + if (yych <= '@') goto yy96; + if (yych <= 'Z') goto yy115; + goto yy4; + } else { + if (yych <= '_') goto yy46; + if (yych <= '`') goto yy4; + if (yych <= 'z') goto yy326; + goto yy4; + } + } + } +yy741: + yych = *++YYCURSOR; + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy803; + goto yy1; +yy742: + yyaccept = 5; + yych = *(YYMARKER = ++YYCURSOR); + if (yych != ' ') goto yy100; +yy743: + yych = *++YYCURSOR; + if (yych == 'F') goto yy804; + goto yy1; +yy744: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '/') goto yy4; + if (yych <= '9') goto yy805; + goto yy4; +yy745: + yych = *++YYCURSOR; + if (yych == ' ') goto yy806; + goto yy1; +yy746: + yych = *++YYCURSOR; + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy807; + goto yy1; +yy747: + yyaccept = 31; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '.') { + if (yych <= 0x1F) { + if (yych <= '\f') { + if (yych <= 0x00) goto yy674; + if (yych <= 0x08) goto yy4; + if (yych <= '\n') goto yy674; + goto yy4; + } else { + if (yych <= '\r') goto yy674; + if (yych == 0x1B) goto yy674; + goto yy4; + } + } else { + if (yych <= '%') { + if (yych <= ' ') goto yy746; + if (yych <= '#') goto yy674; + if (yych <= '$') goto yy4; + goto yy80; + } else { + if (yych == '+') goto yy80; + if (yych <= ',') goto yy674; + goto yy46; + } + } + } else { + if (yych <= 'Z') { + if (yych <= '?') { + if (yych <= '/') goto yy4; + if (yych <= '9') goto yy46; + if (yych <= ':') goto yy4; + goto yy674; + } else { + if (yych <= '@') goto yy96; + if (yych != 'T') goto yy46; + } + } else { + if (yych <= 'z') { + if (yych == '_') goto yy46; + if (yych <= '`') goto yy674; + goto yy46; + } else { + if (yych <= 0x7F) { + if (yych <= '~') goto yy674; + goto yy4; + } else { + if (yych <= 0xC1) goto yy674; + if (yych <= 0xF4) goto yy4; + goto yy674; + } + } + } + } +yy748: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '9') { + if (yych <= '+') { + if (yych == '%') goto yy80; + if (yych <= '*') goto yy4; + goto yy80; + } else { + if (yych <= ',') goto yy4; + if (yych <= '.') goto yy46; + if (yych <= '/') goto yy4; + goto yy808; + } + } else { + if (yych <= '^') { + if (yych <= '?') goto yy4; + if (yych <= '@') goto yy96; + if (yych <= 'Z') goto yy46; + goto yy4; + } else { + if (yych == '`') goto yy4; + if (yych <= 'z') goto yy46; + goto yy4; + } + } +yy749: + yyaccept = 31; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= ':') { + if (yych <= 0x1A) { + if (yych <= '\n') { + if (yych <= 0x00) goto yy674; + if (yych <= 0x08) goto yy4; + goto yy674; + } else { + if (yych == '\r') goto yy674; + goto yy4; + } + } else { + if (yych <= ' ') { + if (yych <= 0x1B) goto yy674; + if (yych <= 0x1F) goto yy4; + goto yy746; + } else { + if (yych == '$') goto yy4; + if (yych <= ',') goto yy674; + goto yy4; + } + } + } else { + if (yych <= '_') { + if (yych <= 'T') { + if (yych <= '?') goto yy674; + if (yych <= 'S') goto yy4; + } else { + if (yych <= 'Z') goto yy4; + if (yych <= '^') goto yy674; + goto yy4; + } + } else { + if (yych <= '~') { + if (yych <= '`') goto yy674; + if (yych <= 'z') goto yy4; + goto yy674; + } else { + if (yych <= 0x7F) goto yy4; + if (yych <= 0xC1) goto yy674; + if (yych <= 0xF4) goto yy4; + goto yy674; + } + } + } +yy750: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '/') goto yy4; + if (yych <= '9') goto yy809; + goto yy4; +yy751: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '?') { + if (yych <= '+') { + if (yych == '%') goto yy80; + if (yych <= '*') goto yy4; + goto yy80; + } else { + if (yych <= '.') { + if (yych <= ',') goto yy4; + goto yy46; + } else { + if (yych <= '/') goto yy4; + if (yych <= '9') goto yy810; + goto yy4; + } + } + } else { + if (yych <= '^') { + if (yych <= '@') goto yy96; + if (yych <= 'F') goto yy810; + if (yych <= 'Z') goto yy46; + goto yy4; + } else { + if (yych <= '`') { + if (yych <= '_') goto yy46; + goto yy4; + } else { + if (yych <= 'f') goto yy810; + if (yych <= 'z') goto yy46; + goto yy4; + } + } + } +yy752: + yyaccept = 11; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[768+yych] & 32) { + goto yy103; + } + if (yych <= '9') { + if (yych <= '#') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy109; + if (yych <= 0x08) goto yy4; + if (yych <= '\n') goto yy109; + goto yy4; + } else { + if (yych <= 0x1A) { + if (yych <= '\r') goto yy109; + goto yy4; + } else { + if (yych <= 0x1B) goto yy109; + if (yych <= 0x1F) goto yy4; + goto yy109; + } + } + } else { + if (yych <= ',') { + if (yych <= '%') { + if (yych <= '$') goto yy4; + goto yy105; + } else { + if (yych == '+') goto yy80; + goto yy109; + } + } else { + if (yych <= '.') { + if (yych <= '-') goto yy46; + goto yy266; + } else { + if (yych <= '/') goto yy4; + if (yych <= '7') goto yy811; + goto yy812; + } + } + } + } else { + if (yych <= '_') { + if (yych <= 'D') { + if (yych <= ':') goto yy148; + if (yych <= '?') goto yy109; + if (yych <= '@') goto yy96; + goto yy196; + } else { + if (yych <= 'F') { + if (yych <= 'E') goto yy754; + goto yy196; + } else { + if (yych <= 'Z') goto yy115; + if (yych <= '^') goto yy109; + goto yy46; + } + } + } else { + if (yych <= 'z') { + if (yych <= 'd') { + if (yych <= '`') goto yy109; + goto yy196; + } else { + if (yych <= 'e') goto yy754; + if (yych <= 'f') goto yy196; + goto yy115; + } + } else { + if (yych <= 0x7F) { + if (yych <= '~') goto yy109; + goto yy4; + } else { + if (yych <= 0xC1) goto yy109; + if (yych <= 0xF4) goto yy4; + goto yy109; + } + } + } + } +yy753: + yyaccept = 6; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[768+yych] & 32) { + goto yy103; + } + if (yych <= '9') { + if (yych <= '#') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy26; + if (yych <= 0x08) goto yy4; + if (yych <= '\n') goto yy26; + goto yy4; + } else { + if (yych <= 0x1A) { + if (yych <= '\r') goto yy26; + goto yy4; + } else { + if (yych <= 0x1B) goto yy26; + if (yych <= 0x1F) goto yy4; + goto yy26; + } + } + } else { + if (yych <= '+') { + if (yych <= '$') goto yy4; + if (yych <= '%') goto yy105; + if (yych <= '*') goto yy26; + goto yy80; + } else { + if (yych <= '-') { + if (yych <= ',') goto yy26; + goto yy46; + } else { + if (yych <= '.') goto yy266; + if (yych <= '/') goto yy4; + goto yy812; + } + } + } + } else { + if (yych <= '_') { + if (yych <= 'D') { + if (yych <= ':') goto yy148; + if (yych <= '?') goto yy26; + if (yych <= '@') goto yy96; + goto yy196; + } else { + if (yych <= 'F') { + if (yych >= 'F') goto yy196; + } else { + if (yych <= 'Z') goto yy115; + if (yych <= '^') goto yy26; + goto yy46; + } + } + } else { + if (yych <= 'z') { + if (yych <= 'd') { + if (yych <= '`') goto yy26; + goto yy196; + } else { + if (yych <= 'e') goto yy754; + if (yych <= 'f') goto yy196; + goto yy115; + } + } else { + if (yych <= 0x7F) { + if (yych <= '~') goto yy26; + goto yy4; + } else { + if (yych <= 0xC1) goto yy26; + if (yych <= 0xF4) goto yy4; + goto yy26; + } + } + } + } +yy754: + yyaccept = 12; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '.') { + if (yych <= 0x1F) { + if (yych <= '\f') { + if (yych <= 0x00) goto yy113; + if (yych <= 0x08) goto yy4; + if (yych <= '\n') goto yy113; + goto yy4; + } else { + if (yych <= '\r') goto yy113; + if (yych == 0x1B) goto yy113; + goto yy4; + } + } else { + if (yych <= '*') { + if (yych <= '#') goto yy113; + if (yych <= '$') goto yy4; + if (yych <= '%') goto yy80; + goto yy113; + } else { + if (yych <= '+') goto yy194; + if (yych <= ',') goto yy113; + if (yych <= '-') goto yy251; + goto yy46; + } + } + } else { + if (yych <= '^') { + if (yych <= '?') { + if (yych <= '/') goto yy4; + if (yych <= '9') goto yy196; + if (yych <= ':') goto yy148; + goto yy113; + } else { + if (yych <= '@') goto yy96; + if (yych <= 'F') goto yy196; + if (yych <= 'Z') goto yy115; + goto yy113; + } + } else { + if (yych <= 'z') { + if (yych <= '_') goto yy46; + if (yych <= '`') goto yy113; + if (yych <= 'f') goto yy196; + goto yy115; + } else { + if (yych <= 0x7F) { + if (yych <= '~') goto yy113; + goto yy4; + } else { + if (yych <= 0xC1) goto yy113; + if (yych <= 0xF4) goto yy4; + goto yy113; + } + } + } + } +yy755: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '?') { + if (yych <= '+') { + if (yych == '%') goto yy80; + if (yych <= '*') goto yy4; + goto yy80; + } else { + if (yych <= '.') { + if (yych <= ',') goto yy4; + goto yy46; + } else { + if (yych <= '/') goto yy4; + if (yych <= '9') goto yy813; + goto yy4; + } + } + } else { + if (yych <= '^') { + if (yych <= '@') goto yy96; + if (yych <= 'F') goto yy810; + if (yych <= 'Z') goto yy46; + goto yy4; + } else { + if (yych <= '`') { + if (yych <= '_') goto yy46; + goto yy4; + } else { + if (yych <= 'f') goto yy810; + if (yych <= 'z') goto yy46; + goto yy4; + } + } + } +yy756: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '@') { + if (yych <= 0x1F) { + if (yych <= '\f') { + if (yych <= 0x00) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0x08) { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= '\n') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= '\r') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych == 0x1B) { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } + } else { + if (yych <= ',') { + if (yych <= '#') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '$') { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= '%') { + yyt1 = YYCURSOR; + goto yy680; + } + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= '9') { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy284; + } + goto yy814; + } else { + if (yych <= ':') goto yy682; + if (yych <= '?') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } + } + } + } else { + if (yych <= '~') { + if (yych <= '_') { + if (yych <= 'F') goto yy814; + if (yych <= 'Z') goto yy3; + if (yych <= '^') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 'f') goto yy814; + if (yych <= 'z') goto yy3; + yyt2 = YYCURSOR; + goto yy198; + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= 0xC1) goto yy5; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy289; + } + yyt2 = YYCURSOR; + goto yy290; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy291; + } + yyt2 = YYCURSOR; + goto yy292; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy293; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy294; + } + goto yy5; + } + } + } + } +yy757: + yych = *++YYCURSOR; + if (yych <= 'f') { + if (yych <= ':') { + if (yych <= '%') { + if (yych <= '$') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt1 = YYCURSOR; + goto yy680; + } else { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy815; + goto yy816; + } + } else { + if (yych <= 'F') { + if (yych <= '@') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy815; + } else { + if (yych <= 'Z') goto yy1; + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy815; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + if (yych <= 'z') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + yyt2 = YYCURSOR; + goto yy206; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy758: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '?') { + if (yych <= '#') { + if (yych <= '\f') { + if (yych <= 0x00) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0x08) { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= '\n') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= 0x1A) { + if (yych <= '\r') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= 0x1B) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0x1F) { + yyt2 = YYCURSOR; + goto yy284; + } + yyt2 = YYCURSOR; + goto yy198; + } + } + } else { + if (yych <= '-') { + if (yych <= '$') { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= '%') { + yyt1 = YYCURSOR; + goto yy680; + } + if (yych <= ',') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= '/') { + if (yych <= '.') { + yyt2 = YYCURSOR; + goto yy376; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= '9') goto yy814; + if (yych <= ':') goto yy682; + yyt2 = YYCURSOR; + goto yy198; + } + } + } + } else { + if (yych <= '~') { + if (yych <= '^') { + if (yych <= '@') { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= 'F') goto yy814; + if (yych <= 'Z') goto yy3; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= '`') { + if (yych <= '_') { + yyt2 = YYCURSOR; + goto yy284; + } + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 'f') goto yy814; + if (yych <= 'z') goto yy3; + yyt2 = YYCURSOR; + goto yy198; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= 0xC1) goto yy5; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy289; + } + yyt2 = YYCURSOR; + goto yy290; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy291; + } + yyt2 = YYCURSOR; + goto yy292; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy293; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy294; + } + goto yy5; + } + } + } + } +yy759: + yyaccept = 29; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '@') { + if (yych <= '/') goto yy643; + if (yych <= '9') goto yy542; + goto yy643; + } else { + if (yych <= 'F') goto yy542; + if (yych <= '`') goto yy643; + if (yych <= 'f') goto yy542; + goto yy643; + } +yy760: + yych = *++YYCURSOR; + if (yych <= '@') { + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy817; + if (yych <= ':') goto yy563; + goto yy1; + } else { + if (yych <= 'F') goto yy817; + if (yych <= '`') goto yy1; + if (yych <= 'f') goto yy817; + goto yy1; + } +yy761: + yych = *++YYCURSOR; + if (yych <= '@') { + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy818; + if (yych <= ':') goto yy691; + goto yy1; + } else { + if (yych <= 'F') goto yy818; + if (yych <= '`') goto yy1; + if (yych <= 'f') goto yy818; + goto yy1; + } +yy762: + yych = *++YYCURSOR; + if (yych <= '@') { + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy819; + if (yych <= ':') goto yy820; + goto yy1; + } else { + if (yych <= 'F') goto yy819; + if (yych <= '`') goto yy1; + if (yych <= 'f') goto yy819; + goto yy1; + } +yy763: + yych = *++YYCURSOR; + if (yych <= '`') { + if (yych <= '9') { + if (yych <= '0') { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy821; + } else { + if (yych <= '1') goto yy822; + if (yych <= '2') goto yy823; + goto yy821; + } + } else { + if (yych <= '@') { + if (yych <= ':') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 'F') goto yy824; + if (yych <= 'Z') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } + } + } else { + if (yych <= 0xDF) { + if (yych <= 'z') { + if (yych <= 'f') goto yy824; + goto yy1; + } else { + if (yych <= 0x7F) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0xC1) goto yy1; + yyt2 = YYCURSOR; + goto yy205; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xE0) { + yyt2 = YYCURSOR; + goto yy206; + } + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy764: + yych = *++YYCURSOR; + if (yych <= 'f') { + if (yych <= ':') { + if (yych <= '.') { + if (yych <= '-') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt1 = YYCURSOR; + goto yy297; + } else { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy825; + } + } else { + if (yych <= 'F') { + if (yych <= '@') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy825; + } else { + if (yych <= 'Z') goto yy1; + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy825; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + if (yych <= 'z') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + yyt2 = YYCURSOR; + goto yy206; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy765: + yych = *++YYCURSOR; + if (yych <= '@') { + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy824; + goto yy1; + } else { + if (yych <= 'F') goto yy824; + if (yych <= '`') goto yy1; + if (yych <= 'f') goto yy824; + goto yy1; + } +yy766: + yych = *++YYCURSOR; + if (yych <= 'z') { + if (yych <= '@') { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy825; + if (yych <= ':') goto yy765; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 'Z') { + if (yych <= 'F') goto yy825; + goto yy1; + } else { + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 'f') goto yy825; + goto yy1; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + yyt2 = YYCURSOR; + goto yy206; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy767: + yych = *++YYCURSOR; + if (yych <= 'f') { + if (yych <= ':') { + if (yych <= '.') { + if (yych <= '-') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt1 = YYCURSOR; + goto yy297; + } else { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy826; + goto yy765; + } + } else { + if (yych <= 'F') { + if (yych <= '@') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy825; + } else { + if (yych <= 'Z') goto yy1; + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy825; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + if (yych <= 'z') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + yyt2 = YYCURSOR; + goto yy206; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy768: + yych = *++YYCURSOR; + if (yych <= '`') { + if (yych <= '9') { + if (yych <= '.') { + if (yych <= '-') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt1 = YYCURSOR; + goto yy297; + } else { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '5') goto yy826; + goto yy825; + } + } else { + if (yych <= '@') { + if (yych <= ':') goto yy765; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 'F') goto yy825; + if (yych <= 'Z') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } + } + } else { + if (yych <= 0xDF) { + if (yych <= 'z') { + if (yych <= 'f') goto yy825; + goto yy1; + } else { + if (yych <= 0x7F) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0xC1) goto yy1; + yyt2 = YYCURSOR; + goto yy205; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xE0) { + yyt2 = YYCURSOR; + goto yy206; + } + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy769: + yych = *++YYCURSOR; + if (yych <= 0x7F) { + if (yych <= '@') { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy1; + if (yych <= ':') goto yy565; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 'Z') goto yy1; + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 'z') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } + } else { + if (yych <= 0xEF) { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + if (yych <= 0xE0) { + yyt2 = YYCURSOR; + goto yy206; + } + yyt2 = YYCURSOR; + goto yy207; + } else { + if (yych <= 0xF0) { + yyt2 = YYCURSOR; + goto yy208; + } + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } +yy770: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '1') { + if (yych <= '/') goto yy4; + if (yych <= '0') goto yy827; + goto yy828; + } else { + if (yych <= '2') goto yy829; + if (yych <= '9') goto yy827; + goto yy4; + } +yy771: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych == '.') goto yy770; + goto yy4; +yy772: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych == '.') goto yy770; + if (yych <= '/') goto yy4; + if (yych <= '5') goto yy771; + goto yy4; +yy773: + yych = *++YYCURSOR; + if (yych <= 0x7F) { + if (yych <= '@') { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy830; + if (yych <= ':') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 'Z') goto yy1; + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 'z') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } + } else { + if (yych <= 0xEF) { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + if (yych <= 0xE0) { + yyt2 = YYCURSOR; + goto yy206; + } + yyt2 = YYCURSOR; + goto yy207; + } else { + if (yych <= 0xF0) { + yyt2 = YYCURSOR; + goto yy208; + } + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } +yy774: + yych = *++YYCURSOR; + if (yych <= 0x7F) { + if (yych <= '@') { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy773; + if (yych <= ':') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 'Z') goto yy1; + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 'z') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } + } else { + if (yych <= 0xEF) { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + if (yych <= 0xE0) { + yyt2 = YYCURSOR; + goto yy206; + } + yyt2 = YYCURSOR; + goto yy207; + } else { + if (yych <= 0xF0) { + yyt2 = YYCURSOR; + goto yy208; + } + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } +yy775: + yych = *++YYCURSOR; + if (yych <= 'z') { + if (yych <= '9') { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '4') goto yy773; + if (yych <= '5') goto yy831; + goto yy830; + } else { + if (yych <= '@') { + if (yych <= ':') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 'Z') goto yy1; + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy1; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + yyt2 = YYCURSOR; + goto yy206; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy776: + yych = *++YYCURSOR; + if (yych <= 'f') { + if (yych <= ':') { + if (yych <= '.') { + if (yych <= '-') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt1 = YYCURSOR; + goto yy297; + } else { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy576; + goto yy486; + } + } else { + if (yych <= 'F') { + if (yych <= '@') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy576; + } else { + if (yych <= 'Z') goto yy1; + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy576; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + if (yych <= 'z') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + yyt2 = YYCURSOR; + goto yy206; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy777: + yych = *++YYCURSOR; + if (yych <= '9') { + if (yych <= '0') { + if (yych <= '/') goto yy1; + goto yy832; + } else { + if (yych <= '1') goto yy473; + if (yych <= '2') goto yy474; + goto yy472; + } + } else { + if (yych <= 'F') { + if (yych <= '@') goto yy1; + goto yy475; + } else { + if (yych <= '`') goto yy1; + if (yych <= 'f') goto yy475; + goto yy1; + } + } +yy778: + yych = *++YYCURSOR; + if (yych <= 'f') { + if (yych <= ':') { + if (yych <= '.') { + if (yych <= '-') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt1 = YYCURSOR; + goto yy297; + } else { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy833; + goto yy486; + } + } else { + if (yych <= 'F') { + if (yych <= '@') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy576; + } else { + if (yych <= 'Z') goto yy1; + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy576; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + if (yych <= 'z') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + yyt2 = YYCURSOR; + goto yy206; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy779: + yych = *++YYCURSOR; + if (yych <= '`') { + if (yych <= '9') { + if (yych <= '.') { + if (yych <= '-') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt1 = YYCURSOR; + goto yy297; + } else { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '5') goto yy833; + goto yy576; + } + } else { + if (yych <= '@') { + if (yych <= ':') goto yy486; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 'F') goto yy576; + if (yych <= 'Z') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } + } + } else { + if (yych <= 0xDF) { + if (yych <= 'z') { + if (yych <= 'f') goto yy576; + goto yy1; + } else { + if (yych <= 0x7F) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0xC1) goto yy1; + yyt2 = YYCURSOR; + goto yy205; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xE0) { + yyt2 = YYCURSOR; + goto yy206; + } + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy780: + yych = *++YYCURSOR; + if (yybm[0+yych] & 128) { + goto yy780; + } + if (yych <= 0xDF) { + if (yych <= '\'') { + if (yych <= 0x00) goto yy1; + if (yych >= '#') goto yy782; + } else { + if (yych <= '\\') goto yy783; + if (yych <= 0xC1) goto yy1; + goto yy784; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xE0) goto yy785; + if (yych <= 0xEF) goto yy786; + goto yy787; + } else { + if (yych <= 0xF3) goto yy788; + if (yych <= 0xF4) goto yy789; + goto yy1; + } + } +yy781: + yych = *++YYCURSOR; + if (yych <= '>') { + if (yych <= '!') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy1; + if (yych == '\t') goto yy834; + goto yy399; + } else { + if (yych <= '\r') goto yy834; + if (yych == ' ') goto yy834; + goto yy399; + } + } else { + if (yych <= ',') { + if (yych <= '"') goto yy835; + if (yych == '\'') goto yy836; + goto yy399; + } else { + if (yych <= '/') { + if (yych <= '-') goto yy837; + goto yy399; + } else { + if (yych <= ':') goto yy837; + if (yych <= '=') goto yy399; + goto yy588; + } + } + } + } else { + if (yych <= 'z') { + if (yych <= '\\') { + if (yych <= '@') goto yy399; + if (yych <= 'Z') goto yy837; + if (yych <= '[') goto yy399; + goto yy400; + } else { + if (yych == '_') goto yy837; + if (yych <= '`') goto yy399; + goto yy837; + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) goto yy399; + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) goto yy401; + goto yy402; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) goto yy403; + goto yy404; + } else { + if (yych <= 0xF3) goto yy405; + if (yych <= 0xF4) goto yy406; + goto yy1; + } + } + } + } +yy782: + yych = *++YYCURSOR; + if (yych <= '>') { + if (yych <= '!') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy1; + if (yych == '\t') goto yy838; + goto yy391; + } else { + if (yych <= '\r') goto yy838; + if (yych == ' ') goto yy838; + goto yy391; + } + } else { + if (yych <= ',') { + if (yych <= '"') goto yy839; + if (yych == '\'') goto yy840; + goto yy391; + } else { + if (yych <= '/') { + if (yych <= '-') goto yy841; + goto yy391; + } else { + if (yych <= ':') goto yy841; + if (yych <= '=') goto yy391; + goto yy580; + } + } + } + } else { + if (yych <= 'z') { + if (yych <= '\\') { + if (yych <= '@') goto yy391; + if (yych <= 'Z') goto yy841; + if (yych <= '[') goto yy391; + goto yy392; + } else { + if (yych == '_') goto yy841; + if (yych <= '`') goto yy391; + goto yy841; + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) goto yy391; + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) goto yy393; + goto yy394; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) goto yy395; + goto yy396; + } else { + if (yych <= 0xF3) goto yy397; + if (yych <= 0xF4) goto yy398; + goto yy1; + } + } + } + } +yy783: + yych = *++YYCURSOR; + if (yych <= 0xDF) { + if (yych <= '\n') { + if (yych <= '\t') goto yy780; + goto yy1; + } else { + if (yych <= 0x7F) goto yy780; + if (yych <= 0xC1) goto yy1; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xE0) goto yy785; + if (yych <= 0xEF) goto yy786; + goto yy787; + } else { + if (yych <= 0xF3) goto yy788; + if (yych <= 0xF4) goto yy789; + goto yy1; + } + } +yy784: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy780; + goto yy1; +yy785: + yych = *++YYCURSOR; + if (yych <= 0x9F) goto yy1; + if (yych <= 0xBF) goto yy784; + goto yy1; +yy786: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy784; + goto yy1; +yy787: + yych = *++YYCURSOR; + if (yych <= 0x8F) goto yy1; + if (yych <= 0xBF) goto yy786; + goto yy1; +yy788: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy786; + goto yy1; +yy789: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0x8F) goto yy786; + goto yy1; +yy790: + yych = *++YYCURSOR; + if (yych <= '=') { + if (yych <= '"') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy1; + if (yych == '\t') goto yy790; + goto yy717; + } else { + if (yych <= 0x1F) { + if (yych <= '\r') goto yy790; + goto yy717; + } else { + if (yych <= ' ') goto yy790; + if (yych <= '!') goto yy717; + goto yy413; + } + } + } else { + if (yych <= '-') { + if (yych == '\'') goto yy412; + if (yych <= ',') goto yy717; + goto yy790; + } else { + if (yych <= '/') { + if (yych <= '.') goto yy717; + } else { + if (yych <= ':') goto yy790; + if (yych <= '<') goto yy717; + goto yy842; + } + } + } + } else { + if (yych <= '`') { + if (yych <= 'Z') { + if (yych <= '>') goto yy792; + if (yych <= '?') goto yy791; + if (yych <= '@') goto yy717; + goto yy790; + } else { + if (yych <= '\\') { + if (yych <= '[') goto yy717; + goto yy793; + } else { + if (yych == '_') goto yy790; + goto yy717; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + if (yych <= 'z') goto yy790; + goto yy717; + } else { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) goto yy794; + goto yy795; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) goto yy796; + goto yy797; + } else { + if (yych <= 0xF3) goto yy798; + if (yych <= 0xF4) goto yy799; + goto yy1; + } + } + } + } +yy791: + yych = *++YYCURSOR; + if (yybm[0+yych] & 64) { + goto yy717; + } + if (yych <= 'Z') { + if (yych <= '-') { + if (yych <= 0x00) goto yy1; + if (yych <= '"') goto yy413; + if (yych <= '\'') goto yy412; + goto yy790; + } else { + if (yych <= ':') { + if (yych <= '/') goto yy791; + goto yy790; + } else { + if (yych <= '>') goto yy843; + if (yych <= '?') goto yy791; + goto yy790; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 'z') { + if (yych <= '\\') goto yy793; + goto yy790; + } else { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) goto yy794; + goto yy795; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) goto yy796; + goto yy797; + } else { + if (yych <= 0xF3) goto yy798; + if (yych <= 0xF4) goto yy799; + goto yy1; + } + } + } +yy792: + yyaccept = 26; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= 0x7F) { + if (yych <= '&') { + if (yych <= 0x00) goto yy217; + if (yych == '"') goto yy845; + goto yy844; + } else { + if (yych <= '\'') goto yy846; + if (yych == '\\') goto yy847; + goto yy844; + } + } else { + if (yych <= 0xEF) { + if (yych <= 0xC1) goto yy217; + if (yych <= 0xDF) goto yy848; + if (yych <= 0xE0) goto yy849; + goto yy850; + } else { + if (yych <= 0xF0) goto yy851; + if (yych <= 0xF3) goto yy852; + if (yych <= 0xF4) goto yy853; + goto yy217; + } + } +yy793: + yych = *++YYCURSOR; + if (yych <= 'Z') { + if (yych <= '.') { + if (yych <= '\n') { + if (yych <= 0x00) goto yy844; + if (yych <= '\t') goto yy717; + goto yy410; + } else { + if (yych == '-') goto yy790; + goto yy717; + } + } else { + if (yych <= '=') { + if (yych <= '/') goto yy791; + if (yych <= ':') goto yy790; + goto yy717; + } else { + if (yych <= '>') goto yy792; + if (yych <= '?') goto yy791; + if (yych <= '@') goto yy717; + goto yy790; + } + } + } else { + if (yych <= 0xC1) { + if (yych <= '`') { + if (yych == '_') goto yy790; + goto yy717; + } else { + if (yych <= 'z') goto yy790; + if (yych <= 0x7F) goto yy717; + goto yy1; + } + } else { + if (yych <= 0xEF) { + if (yych <= 0xDF) goto yy794; + if (yych <= 0xE0) goto yy795; + goto yy796; + } else { + if (yych <= 0xF0) goto yy797; + if (yych <= 0xF3) goto yy798; + if (yych <= 0xF4) goto yy799; + goto yy1; + } + } + } +yy794: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy717; + goto yy1; +yy795: + yych = *++YYCURSOR; + if (yych <= 0x9F) goto yy1; + if (yych <= 0xBF) goto yy794; + goto yy1; +yy796: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy794; + goto yy1; +yy797: + yych = *++YYCURSOR; + if (yych <= 0x8F) goto yy1; + if (yych <= 0xBF) goto yy796; + goto yy1; +yy798: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy796; + goto yy1; +yy799: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0x8F) goto yy796; + goto yy1; +yy800: + yych = *++YYCURSOR; + if (yych <= 'Z') { + if (yych <= '-') { + if (yych <= '"') { + if (yych <= 0x00) goto yy1; + if (yych <= '!') goto yy800; + goto yy525; + } else { + if (yych == '\'') goto yy524; + if (yych <= ',') goto yy800; + goto yy854; + } + } else { + if (yych <= '=') { + if (yych <= '.') goto yy800; + if (yych <= '/') goto yy855; + if (yych <= ':') goto yy854; + goto yy800; + } else { + if (yych <= '>') goto yy856; + if (yych <= '?') goto yy855; + if (yych <= '@') goto yy800; + goto yy854; + } + } + } else { + if (yych <= 0x7F) { + if (yych <= '^') { + if (yych == '\\') goto yy857; + goto yy800; + } else { + if (yych == '`') goto yy800; + if (yych <= 'z') goto yy854; + goto yy800; + } + } else { + if (yych <= 0xEF) { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) goto yy858; + if (yych <= 0xE0) goto yy859; + goto yy860; + } else { + if (yych <= 0xF0) goto yy861; + if (yych <= 0xF3) goto yy862; + if (yych <= 0xF4) goto yy863; + goto yy1; + } + } + } +yy801: + yych = *++YYCURSOR; + if (yych <= 0x1F) { + if (yych <= '\t') { + if (yych <= 0x08) goto yy1; + goto yy801; + } else { + if (yych == '\r') goto yy801; + goto yy1; + } + } else { + if (yych <= '/') { + if (yych <= ' ') goto yy801; + if (yych <= '.') goto yy1; + goto yy215; + } else { + if (yych == '?') goto yy215; + goto yy1; + } + } +yy802: + yych = *++YYCURSOR; + if (yych == ':') goto yy864; + goto yy1; +yy803: + yych = *++YYCURSOR; + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy865; + goto yy1; +yy804: + yych = *++YYCURSOR; + if (yych == 'i') goto yy866; + goto yy1; +yy805: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '/') goto yy4; + if (yych <= '9') goto yy749; + goto yy4; +yy806: + yych = *++YYCURSOR; + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy867; + goto yy1; +yy807: + yych = *++YYCURSOR; + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy868; + goto yy1; +yy808: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '9') { + if (yych <= '+') { + if (yych == '%') goto yy80; + if (yych <= '*') goto yy4; + goto yy80; + } else { + if (yych <= ',') goto yy4; + if (yych <= '.') goto yy46; + if (yych <= '/') goto yy4; + goto yy869; + } + } else { + if (yych <= '^') { + if (yych <= '?') goto yy4; + if (yych <= '@') goto yy96; + if (yych <= 'Z') goto yy46; + goto yy4; + } else { + if (yych == '`') goto yy4; + if (yych <= 'z') goto yy46; + goto yy4; + } + } +yy809: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '/') goto yy4; + if (yych <= '9') goto yy870; + goto yy4; +yy810: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '?') { + if (yych <= '+') { + if (yych == '%') goto yy80; + if (yych <= '*') goto yy4; + goto yy80; + } else { + if (yych <= '.') { + if (yych <= ',') goto yy4; + goto yy46; + } else { + if (yych <= '/') goto yy4; + if (yych <= '9') goto yy871; + goto yy4; + } + } + } else { + if (yych <= '^') { + if (yych <= '@') goto yy96; + if (yych <= 'F') goto yy871; + if (yych <= 'Z') goto yy46; + goto yy4; + } else { + if (yych <= '`') { + if (yych <= '_') goto yy46; + goto yy4; + } else { + if (yych <= 'f') goto yy871; + if (yych <= 'z') goto yy46; + goto yy4; + } + } + } +yy811: + yyaccept = 11; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[768+yych] & 32) { + goto yy103; + } + if (yych <= '9') { + if (yych <= '#') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy109; + if (yych <= 0x08) goto yy4; + if (yych <= '\n') goto yy109; + goto yy4; + } else { + if (yych <= 0x1A) { + if (yych <= '\r') goto yy109; + goto yy4; + } else { + if (yych <= 0x1B) goto yy109; + if (yych <= 0x1F) goto yy4; + goto yy109; + } + } + } else { + if (yych <= ',') { + if (yych <= '%') { + if (yych <= '$') goto yy4; + goto yy105; + } else { + if (yych == '+') goto yy80; + goto yy109; + } + } else { + if (yych <= '.') { + if (yych <= '-') goto yy46; + goto yy266; + } else { + if (yych <= '/') goto yy4; + if (yych <= '7') goto yy872; + goto yy873; + } + } + } + } else { + if (yych <= '_') { + if (yych <= 'D') { + if (yych <= ':') goto yy148; + if (yych <= '?') goto yy109; + if (yych <= '@') goto yy96; + goto yy196; + } else { + if (yych <= 'F') { + if (yych <= 'E') goto yy754; + goto yy196; + } else { + if (yych <= 'Z') goto yy115; + if (yych <= '^') goto yy109; + goto yy46; + } + } + } else { + if (yych <= 'z') { + if (yych <= 'd') { + if (yych <= '`') goto yy109; + goto yy196; + } else { + if (yych <= 'e') goto yy754; + if (yych <= 'f') goto yy196; + goto yy115; + } + } else { + if (yych <= 0x7F) { + if (yych <= '~') goto yy109; + goto yy4; + } else { + if (yych <= 0xC1) goto yy109; + if (yych <= 0xF4) goto yy4; + goto yy109; + } + } + } + } +yy812: + yyaccept = 6; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[768+yych] & 32) { + goto yy103; + } + if (yych <= '9') { + if (yych <= '#') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy26; + if (yych <= 0x08) goto yy4; + if (yych <= '\n') goto yy26; + goto yy4; + } else { + if (yych <= 0x1A) { + if (yych <= '\r') goto yy26; + goto yy4; + } else { + if (yych <= 0x1B) goto yy26; + if (yych <= 0x1F) goto yy4; + goto yy26; + } + } + } else { + if (yych <= '+') { + if (yych <= '$') goto yy4; + if (yych <= '%') goto yy105; + if (yych <= '*') goto yy26; + goto yy80; + } else { + if (yych <= '-') { + if (yych <= ',') goto yy26; + goto yy46; + } else { + if (yych <= '.') goto yy266; + if (yych <= '/') goto yy4; + goto yy873; + } + } + } + } else { + if (yych <= '_') { + if (yych <= 'D') { + if (yych <= ':') goto yy148; + if (yych <= '?') goto yy26; + if (yych <= '@') goto yy96; + goto yy196; + } else { + if (yych <= 'F') { + if (yych <= 'E') goto yy754; + goto yy196; + } else { + if (yych <= 'Z') goto yy115; + if (yych <= '^') goto yy26; + goto yy46; + } + } + } else { + if (yych <= 'z') { + if (yych <= 'd') { + if (yych <= '`') goto yy26; + goto yy196; + } else { + if (yych <= 'e') goto yy754; + if (yych <= 'f') goto yy196; + goto yy115; + } + } else { + if (yych <= 0x7F) { + if (yych <= '~') goto yy26; + goto yy4; + } else { + if (yych <= 0xC1) goto yy26; + if (yych <= 0xF4) goto yy4; + goto yy26; + } + } + } + } +yy813: + yyaccept = 6; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '/') { + if (yych <= 0x1F) { + if (yych <= '\f') { + if (yych <= 0x00) goto yy26; + if (yych <= 0x08) goto yy4; + if (yych <= '\n') goto yy26; + goto yy4; + } else { + if (yych <= '\r') goto yy26; + if (yych == 0x1B) goto yy26; + goto yy4; + } + } else { + if (yych <= '*') { + if (yych <= '#') goto yy26; + if (yych <= '$') goto yy4; + if (yych <= '%') goto yy80; + goto yy26; + } else { + if (yych <= '+') goto yy80; + if (yych <= ',') goto yy26; + if (yych <= '.') goto yy46; + goto yy4; + } + } + } else { + if (yych <= '_') { + if (yych <= '@') { + if (yych <= '9') goto yy874; + if (yych <= ':') goto yy4; + if (yych <= '?') goto yy26; + goto yy96; + } else { + if (yych <= 'F') goto yy871; + if (yych <= 'Z') goto yy46; + if (yych <= '^') goto yy26; + goto yy46; + } + } else { + if (yych <= '~') { + if (yych <= '`') goto yy26; + if (yych <= 'f') goto yy871; + if (yych <= 'z') goto yy46; + goto yy26; + } else { + if (yych <= 0x7F) goto yy4; + if (yych <= 0xC1) goto yy26; + if (yych <= 0xF4) goto yy4; + goto yy26; + } + } + } +yy814: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '?') { + if (yych <= 0x1F) { + if (yych <= '\f') { + if (yych <= 0x00) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0x08) { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= '\n') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= '\r') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych == 0x1B) { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } + } else { + if (yych <= ',') { + if (yych <= '#') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '$') { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= '%') { + yyt1 = YYCURSOR; + goto yy680; + } + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= '9') goto yy3; + if (yych <= ':') goto yy682; + yyt2 = YYCURSOR; + goto yy198; + } + } + } else { + if (yych <= 0x7F) { + if (yych <= '_') { + if (yych <= '@') { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= 'Z') goto yy3; + if (yych <= '^') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 'z') goto yy3; + if (yych <= '~') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } + } else { + if (yych <= 0xEF) { + if (yych <= 0xC1) goto yy5; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy289; + } + if (yych <= 0xE0) { + yyt2 = YYCURSOR; + goto yy290; + } + yyt2 = YYCURSOR; + goto yy291; + } else { + if (yych <= 0xF0) { + yyt2 = YYCURSOR; + goto yy292; + } + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy293; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy294; + } + goto yy5; + } + } + } +yy815: + yych = *++YYCURSOR; + if (yych <= 'f') { + if (yych <= ':') { + if (yych <= '%') { + if (yych <= '$') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt1 = YYCURSOR; + goto yy680; + } else { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy875; + } + } else { + if (yych <= 'F') { + if (yych <= '@') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy875; + } else { + if (yych <= 'Z') goto yy1; + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy875; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + if (yych <= 'z') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + yyt2 = YYCURSOR; + goto yy206; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy816: + yych = *++YYCURSOR; + if (yych <= '@') { + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy876; + goto yy1; + } else { + if (yych <= 'F') goto yy876; + if (yych <= '`') goto yy1; + if (yych <= 'f') goto yy876; + goto yy1; + } +yy817: + yych = *++YYCURSOR; + if (yych <= '@') { + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy877; + if (yych <= ':') goto yy691; + goto yy1; + } else { + if (yych <= 'F') goto yy877; + if (yych <= '`') goto yy1; + if (yych <= 'f') goto yy877; + goto yy1; + } +yy818: + yych = *++YYCURSOR; + if (yych == ':') goto yy691; + goto yy1; +yy819: + yych = *++YYCURSOR; + if (yych <= '@') { + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy878; + if (yych >= ';') goto yy1; + } else { + if (yych <= 'F') goto yy878; + if (yych <= '`') goto yy1; + if (yych <= 'f') goto yy878; + goto yy1; + } +yy820: + yych = *++YYCURSOR; + if (yych <= '@') { + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy879; + if (yych <= ':') goto yy880; + goto yy1; + } else { + if (yych <= 'F') goto yy879; + if (yych <= '`') goto yy1; + if (yych <= 'f') goto yy879; + goto yy1; + } +yy821: + yych = *++YYCURSOR; + if (yych <= 'f') { + if (yych <= ':') { + if (yych <= '.') { + if (yych <= '-') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt1 = YYCURSOR; + goto yy297; + } else { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy881; + goto yy882; + } + } else { + if (yych <= 'F') { + if (yych <= '@') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy883; + } else { + if (yych <= 'Z') goto yy1; + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy883; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + if (yych <= 'z') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + yyt2 = YYCURSOR; + goto yy206; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy822: + yych = *++YYCURSOR; + if (yych <= 'f') { + if (yych <= ':') { + if (yych <= '.') { + if (yych <= '-') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt1 = YYCURSOR; + goto yy297; + } else { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy884; + goto yy882; + } + } else { + if (yych <= 'F') { + if (yych <= '@') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy883; + } else { + if (yych <= 'Z') goto yy1; + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy883; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + if (yych <= 'z') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + yyt2 = YYCURSOR; + goto yy206; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy823: + yych = *++YYCURSOR; + if (yych <= '`') { + if (yych <= '5') { + if (yych <= '.') { + if (yych <= '-') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt1 = YYCURSOR; + goto yy297; + } else { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '4') goto yy884; + goto yy885; + } + } else { + if (yych <= '@') { + if (yych <= '9') goto yy881; + if (yych <= ':') goto yy882; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 'F') goto yy883; + if (yych <= 'Z') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } + } + } else { + if (yych <= 0xDF) { + if (yych <= 'z') { + if (yych <= 'f') goto yy883; + goto yy1; + } else { + if (yych <= 0x7F) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0xC1) goto yy1; + yyt2 = YYCURSOR; + goto yy205; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xE0) { + yyt2 = YYCURSOR; + goto yy206; + } + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy824: + yych = *++YYCURSOR; + if (yych <= 'z') { + if (yych <= '@') { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy883; + if (yych <= ':') goto yy882; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 'Z') { + if (yych <= 'F') goto yy883; + goto yy1; + } else { + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 'f') goto yy883; + goto yy1; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + yyt2 = YYCURSOR; + goto yy206; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy825: + yych = *++YYCURSOR; + if (yych <= 'z') { + if (yych <= '@') { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy886; + if (yych <= ':') goto yy765; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 'Z') { + if (yych <= 'F') goto yy886; + goto yy1; + } else { + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 'f') goto yy886; + goto yy1; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + yyt2 = YYCURSOR; + goto yy206; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy826: + yych = *++YYCURSOR; + if (yych <= 'f') { + if (yych <= ':') { + if (yych <= '.') { + if (yych <= '-') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt1 = YYCURSOR; + goto yy297; + } else { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy886; + goto yy765; + } + } else { + if (yych <= 'F') { + if (yych <= '@') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy886; + } else { + if (yych <= 'Z') goto yy1; + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy886; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + if (yych <= 'z') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + yyt2 = YYCURSOR; + goto yy206; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy827: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '?') { + if (yych <= 0x1B) { + if (yych <= '\n') { + if (yych <= 0x00) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0x08) { + yyt2 = YYCURSOR; + goto yy284; + } + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych == '\r') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0x1A) { + yyt2 = YYCURSOR; + goto yy284; + } + yyt2 = YYCURSOR; + goto yy198; + } + } else { + if (yych <= ',') { + if (yych <= 0x1F) { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych == '$') { + yyt2 = YYCURSOR; + goto yy284; + } + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= '9') goto yy887; + if (yych <= ':') goto yy62; + yyt2 = YYCURSOR; + goto yy198; + } + } + } else { + if (yych <= 0x7F) { + if (yych <= '_') { + if (yych <= '@') { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= 'Z') goto yy3; + if (yych <= '^') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 'z') goto yy3; + if (yych <= '~') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } + } else { + if (yych <= 0xEF) { + if (yych <= 0xC1) goto yy5; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy289; + } + if (yych <= 0xE0) { + yyt2 = YYCURSOR; + goto yy290; + } + yyt2 = YYCURSOR; + goto yy291; + } else { + if (yych <= 0xF0) { + yyt2 = YYCURSOR; + goto yy292; + } + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy293; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy294; + } + goto yy5; + } + } + } +yy828: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '?') { + if (yych <= 0x1B) { + if (yych <= '\n') { + if (yych <= 0x00) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0x08) { + yyt2 = YYCURSOR; + goto yy284; + } + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych == '\r') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0x1A) { + yyt2 = YYCURSOR; + goto yy284; + } + yyt2 = YYCURSOR; + goto yy198; + } + } else { + if (yych <= ',') { + if (yych <= 0x1F) { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych == '$') { + yyt2 = YYCURSOR; + goto yy284; + } + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= '9') goto yy827; + if (yych <= ':') goto yy62; + yyt2 = YYCURSOR; + goto yy198; + } + } + } else { + if (yych <= 0x7F) { + if (yych <= '_') { + if (yych <= '@') { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= 'Z') goto yy3; + if (yych <= '^') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 'z') goto yy3; + if (yych <= '~') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } + } else { + if (yych <= 0xEF) { + if (yych <= 0xC1) goto yy5; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy289; + } + if (yych <= 0xE0) { + yyt2 = YYCURSOR; + goto yy290; + } + yyt2 = YYCURSOR; + goto yy291; + } else { + if (yych <= 0xF0) { + yyt2 = YYCURSOR; + goto yy292; + } + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy293; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy294; + } + goto yy5; + } + } + } +yy829: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= ':') { + if (yych <= 0x1F) { + if (yych <= '\f') { + if (yych <= 0x00) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0x08) { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= '\n') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= '\r') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych == 0x1B) { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } + } else { + if (yych <= '/') { + if (yych == '$') { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= ',') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= '4') goto yy827; + if (yych <= '5') goto yy888; + if (yych <= '9') goto yy887; + goto yy62; + } + } + } else { + if (yych <= '~') { + if (yych <= '^') { + if (yych <= '?') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '@') { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= 'Z') goto yy3; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= '_') { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 'z') goto yy3; + yyt2 = YYCURSOR; + goto yy198; + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= 0xC1) goto yy5; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy289; + } + yyt2 = YYCURSOR; + goto yy290; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy291; + } + yyt2 = YYCURSOR; + goto yy292; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy293; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy294; + } + goto yy5; + } + } + } + } +yy830: + yych = *++YYCURSOR; + if (yych <= 0x7F) { + if (yych <= '@') { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= ':') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 'Z') goto yy1; + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 'z') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } + } else { + if (yych <= 0xEF) { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + if (yych <= 0xE0) { + yyt2 = YYCURSOR; + goto yy206; + } + yyt2 = YYCURSOR; + goto yy207; + } else { + if (yych <= 0xF0) { + yyt2 = YYCURSOR; + goto yy208; + } + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } +yy831: + yych = *++YYCURSOR; + if (yych <= 0x7F) { + if (yych <= '@') { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '5') goto yy830; + if (yych <= ':') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 'Z') goto yy1; + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 'z') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } + } else { + if (yych <= 0xEF) { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + if (yych <= 0xE0) { + yyt2 = YYCURSOR; + goto yy206; + } + yyt2 = YYCURSOR; + goto yy207; + } else { + if (yych <= 0xF0) { + yyt2 = YYCURSOR; + goto yy208; + } + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } +yy832: + yych = *++YYCURSOR; + if (yych <= 'f') { + if (yych <= ':') { + if (yych <= '.') { + if (yych <= '-') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt1 = YYCURSOR; + goto yy297; + } else { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy564; + goto yy889; + } + } else { + if (yych <= 'F') { + if (yych <= '@') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy566; + } else { + if (yych <= 'Z') goto yy1; + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy566; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + if (yych <= 'z') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + yyt2 = YYCURSOR; + goto yy206; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy833: + yych = *++YYCURSOR; + if (yych <= 'f') { + if (yych <= ':') { + if (yych <= '.') { + if (yych <= '-') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt1 = YYCURSOR; + goto yy297; + } else { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy704; + goto yy486; + } + } else { + if (yych <= 'F') { + if (yych <= '@') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy704; + } else { + if (yych <= 'Z') goto yy1; + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy704; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + if (yych <= 'z') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + yyt2 = YYCURSOR; + goto yy206; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy834: + yych = *++YYCURSOR; + if (yych <= '=') { + if (yych <= 0x1F) { + if (yych <= '\t') { + if (yych <= 0x00) goto yy1; + if (yych <= 0x08) goto yy399; + goto yy834; + } else { + if (yych == '\r') goto yy834; + goto yy399; + } + } else { + if (yych <= '"') { + if (yych <= ' ') goto yy834; + if (yych <= '!') goto yy399; + } else { + if (yych == '\'') goto yy836; + goto yy399; + } + } + } else { + if (yych <= 0xDF) { + if (yych <= '\\') { + if (yych <= '>') goto yy588; + if (yych <= '[') goto yy399; + goto yy400; + } else { + if (yych <= 0x7F) goto yy399; + if (yych <= 0xC1) goto yy1; + goto yy401; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xE0) goto yy402; + if (yych <= 0xEF) goto yy403; + goto yy404; + } else { + if (yych <= 0xF3) goto yy405; + if (yych <= 0xF4) goto yy406; + goto yy1; + } + } + } +yy835: + yych = *++YYCURSOR; + if (yybm[0+yych] & 128) { + goto yy780; + } + if (yych <= 0xDF) { + if (yych <= '\'') { + if (yych <= 0x00) goto yy1; + if (yych <= '"') goto yy399; + goto yy782; + } else { + if (yych <= '\\') goto yy783; + if (yych <= 0xC1) goto yy1; + goto yy784; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xE0) goto yy785; + if (yych <= 0xEF) goto yy786; + goto yy787; + } else { + if (yych <= 0xF3) goto yy788; + if (yych <= 0xF4) goto yy789; + goto yy1; + } + } +yy836: + yych = *++YYCURSOR; + if (yych <= '>') { + if (yych <= '!') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy1; + if (yych == '\t') goto yy834; + goto yy399; + } else { + if (yych <= '\r') goto yy834; + if (yych == ' ') goto yy834; + goto yy399; + } + } else { + if (yych <= ',') { + if (yych <= '"') goto yy835; + if (yych == '\'') goto yy307; + goto yy399; + } else { + if (yych <= '/') { + if (yych >= '.') goto yy399; + } else { + if (yych <= ':') goto yy837; + if (yych <= '=') goto yy399; + goto yy588; + } + } + } + } else { + if (yych <= 'z') { + if (yych <= '\\') { + if (yych <= '@') goto yy399; + if (yych <= 'Z') goto yy837; + if (yych <= '[') goto yy399; + goto yy400; + } else { + if (yych == '_') goto yy837; + if (yych <= '`') goto yy399; + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) goto yy399; + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) goto yy401; + goto yy402; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) goto yy403; + goto yy404; + } else { + if (yych <= 0xF3) goto yy405; + if (yych <= 0xF4) goto yy406; + goto yy1; + } + } + } + } +yy837: + yych = *++YYCURSOR; + if (yych <= '=') { + if (yych <= '!') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy1; + if (yych == '\t') goto yy890; + goto yy399; + } else { + if (yych <= '\r') goto yy890; + if (yych == ' ') goto yy890; + goto yy399; + } + } else { + if (yych <= ',') { + if (yych <= '"') goto yy835; + if (yych == '\'') goto yy836; + goto yy399; + } else { + if (yych <= '/') { + if (yych <= '-') goto yy837; + goto yy399; + } else { + if (yych <= ':') goto yy837; + if (yych <= '<') goto yy399; + goto yy891; + } + } + } + } else { + if (yych <= 'z') { + if (yych <= '[') { + if (yych <= '>') goto yy588; + if (yych <= '@') goto yy399; + if (yych <= 'Z') goto yy837; + goto yy399; + } else { + if (yych <= '^') { + if (yych <= '\\') goto yy400; + goto yy399; + } else { + if (yych == '`') goto yy399; + goto yy837; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) goto yy399; + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) goto yy401; + goto yy402; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) goto yy403; + goto yy404; + } else { + if (yych <= 0xF3) goto yy405; + if (yych <= 0xF4) goto yy406; + goto yy1; + } + } + } + } +yy838: + yych = *++YYCURSOR; + if (yych <= '=') { + if (yych <= 0x1F) { + if (yych <= '\t') { + if (yych <= 0x00) goto yy1; + if (yych <= 0x08) goto yy391; + goto yy838; + } else { + if (yych == '\r') goto yy838; + goto yy391; + } + } else { + if (yych <= '"') { + if (yych <= ' ') goto yy838; + if (yych <= '!') goto yy391; + } else { + if (yych == '\'') goto yy840; + goto yy391; + } + } + } else { + if (yych <= 0xDF) { + if (yych <= '\\') { + if (yych <= '>') goto yy580; + if (yych <= '[') goto yy391; + goto yy392; + } else { + if (yych <= 0x7F) goto yy391; + if (yych <= 0xC1) goto yy1; + goto yy393; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xE0) goto yy394; + if (yych <= 0xEF) goto yy395; + goto yy396; + } else { + if (yych <= 0xF3) goto yy397; + if (yych <= 0xF4) goto yy398; + goto yy1; + } + } + } +yy839: + yych = *++YYCURSOR; + if (yych <= '>') { + if (yych <= '!') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy1; + if (yych == '\t') goto yy838; + goto yy391; + } else { + if (yych <= '\r') goto yy838; + if (yych == ' ') goto yy838; + goto yy391; + } + } else { + if (yych <= ',') { + if (yych <= '"') goto yy306; + if (yych != '\'') goto yy391; + } else { + if (yych <= '/') { + if (yych <= '-') goto yy841; + goto yy391; + } else { + if (yych <= ':') goto yy841; + if (yych <= '=') goto yy391; + goto yy580; + } + } + } + } else { + if (yych <= 'z') { + if (yych <= '\\') { + if (yych <= '@') goto yy391; + if (yych <= 'Z') goto yy841; + if (yych <= '[') goto yy391; + goto yy392; + } else { + if (yych == '_') goto yy841; + if (yych <= '`') goto yy391; + goto yy841; + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) goto yy391; + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) goto yy393; + goto yy394; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) goto yy395; + goto yy396; + } else { + if (yych <= 0xF3) goto yy397; + if (yych <= 0xF4) goto yy398; + goto yy1; + } + } + } + } +yy840: + yych = *++YYCURSOR; + if (yybm[0+yych] & 128) { + goto yy780; + } + if (yych <= 0xDF) { + if (yych <= '\'') { + if (yych <= 0x00) goto yy1; + if (yych <= '"') goto yy781; + goto yy391; + } else { + if (yych <= '\\') goto yy783; + if (yych <= 0xC1) goto yy1; + goto yy784; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xE0) goto yy785; + if (yych <= 0xEF) goto yy786; + goto yy787; + } else { + if (yych <= 0xF3) goto yy788; + if (yych <= 0xF4) goto yy789; + goto yy1; + } + } +yy841: + yych = *++YYCURSOR; + if (yych <= '=') { + if (yych <= '!') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy1; + if (yych == '\t') goto yy892; + goto yy391; + } else { + if (yych <= '\r') goto yy892; + if (yych == ' ') goto yy892; + goto yy391; + } + } else { + if (yych <= ',') { + if (yych <= '"') goto yy839; + if (yych == '\'') goto yy840; + goto yy391; + } else { + if (yych <= '/') { + if (yych <= '-') goto yy841; + goto yy391; + } else { + if (yych <= ':') goto yy841; + if (yych <= '<') goto yy391; + goto yy893; + } + } + } + } else { + if (yych <= 'z') { + if (yych <= '[') { + if (yych <= '>') goto yy580; + if (yych <= '@') goto yy391; + if (yych <= 'Z') goto yy841; + goto yy391; + } else { + if (yych <= '^') { + if (yych <= '\\') goto yy392; + goto yy391; + } else { + if (yych == '`') goto yy391; + goto yy841; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) goto yy391; + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) goto yy393; + goto yy394; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) goto yy395; + goto yy396; + } else { + if (yych <= 0xF3) goto yy397; + if (yych <= 0xF4) goto yy398; + goto yy1; + } + } + } + } +yy842: + yych = *++YYCURSOR; + if (yych <= '@') { + if (yych <= ',') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy1; + if (yych == '\t') goto yy842; + goto yy717; + } else { + if (yych <= '\r') goto yy842; + if (yych == ' ') goto yy842; + goto yy717; + } + } else { + if (yych <= ':') { + if (yych <= '-') goto yy790; + if (yych <= '.') goto yy717; + if (yych <= '/') goto yy791; + goto yy790; + } else { + if (yych <= '=') goto yy717; + if (yych <= '>') goto yy792; + if (yych <= '?') goto yy791; + goto yy717; + } + } + } else { + if (yych <= 0x7F) { + if (yych <= '^') { + if (yych <= 'Z') goto yy790; + if (yych == '\\') goto yy793; + goto yy717; + } else { + if (yych == '`') goto yy717; + if (yych <= 'z') goto yy790; + goto yy717; + } + } else { + if (yych <= 0xEF) { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) goto yy794; + if (yych <= 0xE0) goto yy795; + goto yy796; + } else { + if (yych <= 0xF0) goto yy797; + if (yych <= 0xF3) goto yy798; + if (yych <= 0xF4) goto yy799; + goto yy1; + } + } + } +yy843: + yyaccept = 28; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= 0x7F) { + if (yych <= '&') { + if (yych <= 0x00) goto yy315; + if (yych == '"') goto yy845; + } else { + if (yych <= '\'') goto yy846; + if (yych == '\\') goto yy847; + } + } else { + if (yych <= 0xEF) { + if (yych <= 0xC1) goto yy315; + if (yych <= 0xDF) goto yy848; + if (yych <= 0xE0) goto yy849; + goto yy850; + } else { + if (yych <= 0xF0) goto yy851; + if (yych <= 0xF3) goto yy852; + if (yych <= 0xF4) goto yy853; + goto yy315; + } + } +yy844: + yych = *++YYCURSOR; + if (yych <= 0x7F) { + if (yych <= '&') { + if (yych <= 0x00) goto yy1; + if (yych != '"') goto yy844; + } else { + if (yych <= '\'') goto yy846; + if (yych == '\\') goto yy847; + goto yy844; + } + } else { + if (yych <= 0xEF) { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) goto yy848; + if (yych <= 0xE0) goto yy849; + goto yy850; + } else { + if (yych <= 0xF0) goto yy851; + if (yych <= 0xF3) goto yy852; + if (yych <= 0xF4) goto yy853; + goto yy1; + } + } +yy845: + yych = *++YYCURSOR; + if (yych <= '?') { + if (yych <= '&') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy1; + if (yych == '\t') goto yy894; + goto yy609; + } else { + if (yych <= '\r') goto yy894; + if (yych == ' ') goto yy894; + goto yy609; + } + } else { + if (yych <= '.') { + if (yych <= '\'') goto yy599; + if (yych == '-') goto yy895; + goto yy609; + } else { + if (yych <= ':') { + if (yych <= '/') goto yy896; + goto yy895; + } else { + if (yych <= '=') goto yy609; + if (yych <= '>') goto yy513; + goto yy896; + } + } + } + } else { + if (yych <= 'z') { + if (yych <= '\\') { + if (yych <= '@') goto yy609; + if (yych <= 'Z') goto yy895; + if (yych <= '[') goto yy609; + goto yy610; + } else { + if (yych == '_') goto yy895; + if (yych <= '`') goto yy609; + goto yy895; + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) goto yy609; + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) goto yy611; + goto yy612; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) goto yy613; + goto yy614; + } else { + if (yych <= 0xF3) goto yy615; + if (yych <= 0xF4) goto yy616; + goto yy1; + } + } + } + } +yy846: + yych = *++YYCURSOR; + if (yych <= '?') { + if (yych <= '!') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy1; + if (yych == '\t') goto yy897; + goto yy598; + } else { + if (yych <= '\r') goto yy897; + if (yych == ' ') goto yy897; + goto yy598; + } + } else { + if (yych <= '.') { + if (yych <= '"') goto yy599; + if (yych == '-') goto yy898; + goto yy598; + } else { + if (yych <= ':') { + if (yych <= '/') goto yy899; + goto yy898; + } else { + if (yych <= '=') goto yy598; + if (yych <= '>') goto yy503; + goto yy899; + } + } + } + } else { + if (yych <= 'z') { + if (yych <= '\\') { + if (yych <= '@') goto yy598; + if (yych <= 'Z') goto yy898; + if (yych <= '[') goto yy598; + goto yy600; + } else { + if (yych == '_') goto yy898; + if (yych <= '`') goto yy598; + goto yy898; + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) goto yy598; + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) goto yy601; + goto yy602; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) goto yy603; + goto yy604; + } else { + if (yych <= 0xF3) goto yy605; + if (yych <= 0xF4) goto yy606; + goto yy1; + } + } + } + } +yy847: + yych = *++YYCURSOR; + if (yych <= 0xDF) { + if (yych <= '\n') { + if (yych <= '\t') goto yy844; + goto yy1; + } else { + if (yych <= 0x7F) goto yy844; + if (yych <= 0xC1) goto yy1; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xE0) goto yy849; + if (yych <= 0xEF) goto yy850; + goto yy851; + } else { + if (yych <= 0xF3) goto yy852; + if (yych <= 0xF4) goto yy853; + goto yy1; + } + } +yy848: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy844; + goto yy1; +yy849: + yych = *++YYCURSOR; + if (yych <= 0x9F) goto yy1; + if (yych <= 0xBF) goto yy848; + goto yy1; +yy850: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy848; + goto yy1; +yy851: + yych = *++YYCURSOR; + if (yych <= 0x8F) goto yy1; + if (yych <= 0xBF) goto yy850; + goto yy1; +yy852: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy850; + goto yy1; +yy853: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0x8F) goto yy850; + goto yy1; +yy854: + yych = *++YYCURSOR; + if (yych <= '=') { + if (yych <= '"') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy1; + if (yych == '\t') goto yy854; + goto yy800; + } else { + if (yych <= 0x1F) { + if (yych <= '\r') goto yy854; + goto yy800; + } else { + if (yych <= ' ') goto yy854; + if (yych <= '!') goto yy800; + goto yy525; + } + } + } else { + if (yych <= '-') { + if (yych == '\'') goto yy524; + if (yych <= ',') goto yy800; + goto yy854; + } else { + if (yych <= '/') { + if (yych <= '.') goto yy800; + } else { + if (yych <= ':') goto yy854; + if (yych <= '<') goto yy800; + goto yy900; + } + } + } + } else { + if (yych <= '`') { + if (yych <= 'Z') { + if (yych <= '>') goto yy856; + if (yych <= '?') goto yy855; + if (yych <= '@') goto yy800; + goto yy854; + } else { + if (yych <= '\\') { + if (yych <= '[') goto yy800; + goto yy857; + } else { + if (yych == '_') goto yy854; + goto yy800; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + if (yych <= 'z') goto yy854; + goto yy800; + } else { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) goto yy858; + goto yy859; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) goto yy860; + goto yy861; + } else { + if (yych <= 0xF3) goto yy862; + if (yych <= 0xF4) goto yy863; + goto yy1; + } + } + } + } +yy855: + yych = *++YYCURSOR; + if (yych <= 'Z') { + if (yych <= '-') { + if (yych <= '"') { + if (yych <= 0x00) goto yy1; + if (yych <= '!') goto yy800; + goto yy525; + } else { + if (yych == '\'') goto yy524; + if (yych <= ',') goto yy800; + goto yy854; + } + } else { + if (yych <= '=') { + if (yych <= '.') goto yy800; + if (yych <= '/') goto yy855; + if (yych <= ':') goto yy854; + goto yy800; + } else { + if (yych <= '>') goto yy901; + if (yych <= '?') goto yy855; + if (yych <= '@') goto yy800; + goto yy854; + } + } + } else { + if (yych <= 0x7F) { + if (yych <= '^') { + if (yych == '\\') goto yy857; + goto yy800; + } else { + if (yych == '`') goto yy800; + if (yych <= 'z') goto yy854; + goto yy800; + } + } else { + if (yych <= 0xEF) { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) goto yy858; + if (yych <= 0xE0) goto yy859; + goto yy860; + } else { + if (yych <= 0xF0) goto yy861; + if (yych <= 0xF3) goto yy862; + if (yych <= 0xF4) goto yy863; + goto yy1; + } + } + } +yy856: + yych = *++YYCURSOR; + if (yych <= 0x7F) { + if (yych <= '&') { + if (yych <= 0x00) goto yy1; + if (yych == '"') goto yy902; + goto yy856; + } else { + if (yych <= '\'') goto yy903; + if (yych == '\\') goto yy904; + goto yy856; + } + } else { + if (yych <= 0xEF) { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) goto yy905; + if (yych <= 0xE0) goto yy906; + goto yy907; + } else { + if (yych <= 0xF0) goto yy908; + if (yych <= 0xF3) goto yy909; + if (yych <= 0xF4) goto yy910; + goto yy1; + } + } +yy857: + yych = *++YYCURSOR; + if (yych <= 'Z') { + if (yych <= '.') { + if (yych <= '\n') { + if (yych <= 0x00) goto yy856; + if (yych <= '\t') goto yy800; + goto yy522; + } else { + if (yych == '-') goto yy854; + goto yy800; + } + } else { + if (yych <= '=') { + if (yych <= '/') goto yy855; + if (yych <= ':') goto yy854; + goto yy800; + } else { + if (yych <= '>') goto yy856; + if (yych <= '?') goto yy855; + if (yych <= '@') goto yy800; + goto yy854; + } + } + } else { + if (yych <= 0xC1) { + if (yych <= '`') { + if (yych == '_') goto yy854; + goto yy800; + } else { + if (yych <= 'z') goto yy854; + if (yych <= 0x7F) goto yy800; + goto yy1; + } + } else { + if (yych <= 0xEF) { + if (yych <= 0xDF) goto yy858; + if (yych <= 0xE0) goto yy859; + goto yy860; + } else { + if (yych <= 0xF0) goto yy861; + if (yych <= 0xF3) goto yy862; + if (yych <= 0xF4) goto yy863; + goto yy1; + } + } + } +yy858: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy800; + goto yy1; +yy859: + yych = *++YYCURSOR; + if (yych <= 0x9F) goto yy1; + if (yych <= 0xBF) goto yy858; + goto yy1; +yy860: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy858; + goto yy1; +yy861: + yych = *++YYCURSOR; + if (yych <= 0x8F) goto yy1; + if (yych <= 0xBF) goto yy860; + goto yy1; +yy862: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy860; + goto yy1; +yy863: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0x8F) goto yy860; + goto yy1; +yy864: + yych = *++YYCURSOR; + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy911; + goto yy1; +yy865: + yych = *++YYCURSOR; + if (yych <= 0xDF) { + if (yych <= ':') { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy642; + } + if (yych <= '9') { + yyt1 = YYCURSOR; + goto yy912; + } + goto yy1; + } else { + if (yych <= 0x7F) { + yyt2 = YYCURSOR; + goto yy642; + } + if (yych <= 0xC1) goto yy1; + yyt2 = YYCURSOR; + goto yy645; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xE0) { + yyt2 = YYCURSOR; + goto yy646; + } + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy647; + } + yyt2 = YYCURSOR; + goto yy648; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy649; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy650; + } + goto yy1; + } + } +yy866: + yych = *++YYCURSOR; + if (yych == 'l') goto yy913; + goto yy1; +yy867: + yych = *++YYCURSOR; + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy914; + goto yy1; +yy868: + yych = *++YYCURSOR; + if (yych == ':') goto yy915; + goto yy1; +yy869: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[1024+yych] & 16) { + goto yy46; + } + if (yych <= '+') { + if (yych == '%') goto yy80; + if (yych <= '*') goto yy4; + goto yy80; + } else { + if (yych <= ':') { + if (yych <= '/') goto yy4; + goto yy916; + } else { + if (yych == '@') goto yy96; + goto yy4; + } + } +yy870: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych == ':') goto yy916; + goto yy4; +yy871: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '?') { + if (yych <= '+') { + if (yych == '%') goto yy80; + if (yych <= '*') goto yy4; + goto yy80; + } else { + if (yych <= '.') { + if (yych <= ',') goto yy4; + goto yy46; + } else { + if (yych <= '/') goto yy4; + if (yych <= '9') goto yy917; + goto yy4; + } + } + } else { + if (yych <= '^') { + if (yych <= '@') goto yy96; + if (yych <= 'F') goto yy917; + if (yych <= 'Z') goto yy46; + goto yy4; + } else { + if (yych <= '`') { + if (yych <= '_') goto yy46; + goto yy4; + } else { + if (yych <= 'f') goto yy917; + if (yych <= 'z') goto yy46; + goto yy4; + } + } + } +yy872: + yyaccept = 11; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[768+yych] & 32) { + goto yy103; + } + if (yych <= '9') { + if (yych <= '#') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy109; + if (yych <= 0x08) goto yy4; + if (yych <= '\n') goto yy109; + goto yy4; + } else { + if (yych <= 0x1A) { + if (yych <= '\r') goto yy109; + goto yy4; + } else { + if (yych <= 0x1B) goto yy109; + if (yych <= 0x1F) goto yy4; + goto yy109; + } + } + } else { + if (yych <= ',') { + if (yych <= '%') { + if (yych <= '$') goto yy4; + goto yy105; + } else { + if (yych == '+') goto yy80; + goto yy109; + } + } else { + if (yych <= '.') { + if (yych <= '-') goto yy46; + goto yy266; + } else { + if (yych <= '/') goto yy4; + if (yych <= '7') goto yy918; + goto yy919; + } + } + } + } else { + if (yych <= '_') { + if (yych <= 'D') { + if (yych <= ':') goto yy148; + if (yych <= '?') goto yy109; + if (yych <= '@') goto yy96; + goto yy196; + } else { + if (yych <= 'F') { + if (yych <= 'E') goto yy754; + goto yy196; + } else { + if (yych <= 'Z') goto yy115; + if (yych <= '^') goto yy109; + goto yy46; + } + } + } else { + if (yych <= 'z') { + if (yych <= 'd') { + if (yych <= '`') goto yy109; + goto yy196; + } else { + if (yych <= 'e') goto yy754; + if (yych <= 'f') goto yy196; + goto yy115; + } + } else { + if (yych <= 0x7F) { + if (yych <= '~') goto yy109; + goto yy4; + } else { + if (yych <= 0xC1) goto yy109; + if (yych <= 0xF4) goto yy4; + goto yy109; + } + } + } + } +yy873: + yyaccept = 6; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[768+yych] & 32) { + goto yy103; + } + if (yych <= '9') { + if (yych <= '#') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy26; + if (yych <= 0x08) goto yy4; + if (yych <= '\n') goto yy26; + goto yy4; + } else { + if (yych <= 0x1A) { + if (yych <= '\r') goto yy26; + goto yy4; + } else { + if (yych <= 0x1B) goto yy26; + if (yych <= 0x1F) goto yy4; + goto yy26; + } + } + } else { + if (yych <= '+') { + if (yych <= '$') goto yy4; + if (yych <= '%') goto yy105; + if (yych <= '*') goto yy26; + goto yy80; + } else { + if (yych <= '-') { + if (yych <= ',') goto yy26; + goto yy46; + } else { + if (yych <= '.') goto yy266; + if (yych <= '/') goto yy4; + goto yy919; + } + } + } + } else { + if (yych <= '_') { + if (yych <= 'D') { + if (yych <= ':') goto yy148; + if (yych <= '?') goto yy26; + if (yych <= '@') goto yy96; + goto yy196; + } else { + if (yych <= 'F') { + if (yych <= 'E') goto yy754; + goto yy196; + } else { + if (yych <= 'Z') goto yy115; + if (yych <= '^') goto yy26; + goto yy46; + } + } + } else { + if (yych <= 'z') { + if (yych <= 'd') { + if (yych <= '`') goto yy26; + goto yy196; + } else { + if (yych <= 'e') goto yy754; + if (yych <= 'f') goto yy196; + goto yy115; + } + } else { + if (yych <= 0x7F) { + if (yych <= '~') goto yy26; + goto yy4; + } else { + if (yych <= 0xC1) goto yy26; + if (yych <= 0xF4) goto yy4; + goto yy26; + } + } + } + } +yy874: + yyaccept = 6; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '/') { + if (yych <= 0x1F) { + if (yych <= '\f') { + if (yych <= 0x00) goto yy26; + if (yych <= 0x08) goto yy4; + if (yych <= '\n') goto yy26; + goto yy4; + } else { + if (yych <= '\r') goto yy26; + if (yych == 0x1B) goto yy26; + goto yy4; + } + } else { + if (yych <= '*') { + if (yych <= '#') goto yy26; + if (yych <= '$') goto yy4; + if (yych <= '%') goto yy80; + goto yy26; + } else { + if (yych <= '+') goto yy80; + if (yych <= ',') goto yy26; + if (yych <= '.') goto yy46; + goto yy4; + } + } + } else { + if (yych <= '_') { + if (yych <= '@') { + if (yych <= '9') goto yy920; + if (yych <= ':') goto yy4; + if (yych <= '?') goto yy26; + goto yy96; + } else { + if (yych <= 'F') goto yy917; + if (yych <= 'Z') goto yy46; + if (yych <= '^') goto yy26; + goto yy46; + } + } else { + if (yych <= '~') { + if (yych <= '`') goto yy26; + if (yych <= 'f') goto yy917; + if (yych <= 'z') goto yy46; + goto yy26; + } else { + if (yych <= 0x7F) goto yy4; + if (yych <= 0xC1) goto yy26; + if (yych <= 0xF4) goto yy4; + goto yy26; + } + } + } +yy875: + yych = *++YYCURSOR; + if (yych <= 'f') { + if (yych <= ':') { + if (yych <= '%') { + if (yych <= '$') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt1 = YYCURSOR; + goto yy680; + } else { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy921; + goto yy816; + } + } else { + if (yych <= 'F') { + if (yych <= '@') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy921; + } else { + if (yych <= 'Z') goto yy1; + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy921; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + if (yych <= 'z') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + yyt2 = YYCURSOR; + goto yy206; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy876: + yych = *++YYCURSOR; + if (yych <= 'f') { + if (yych <= ':') { + if (yych <= '%') { + if (yych <= '$') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt1 = YYCURSOR; + goto yy680; + } else { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy922; + goto yy923; + } + } else { + if (yych <= 'F') { + if (yych <= '@') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy922; + } else { + if (yych <= 'Z') goto yy1; + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy922; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + if (yych <= 'z') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + yyt2 = YYCURSOR; + goto yy206; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy877: + yyaccept = 21; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= ':') { + if (yych <= '-') { + if (yych <= ',') goto yy354; + goto yy466; + } else { + if (yych <= '/') goto yy354; + if (yych <= '9') goto yy761; + goto yy924; + } + } else { + if (yych <= 'F') { + if (yych <= '@') goto yy354; + goto yy761; + } else { + if (yych <= '`') goto yy354; + if (yych <= 'f') goto yy761; + goto yy354; + } + } +yy878: + yych = *++YYCURSOR; + if (yych <= '@') { + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy925; + if (yych <= ':') goto yy820; + goto yy1; + } else { + if (yych <= 'F') goto yy925; + if (yych <= '`') goto yy1; + if (yych <= 'f') goto yy925; + goto yy1; + } +yy879: + yych = *++YYCURSOR; + if (yych <= '@') { + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy926; + if (yych <= ':') goto yy927; + goto yy1; + } else { + if (yych <= 'F') goto yy926; + if (yych <= '`') goto yy1; + if (yych <= 'f') goto yy926; + goto yy1; + } +yy880: + yych = *++YYCURSOR; + if (yych <= 'z') { + if (yych <= '@') { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy928; + if (yych <= ':') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 'Z') { + if (yych <= 'F') goto yy928; + goto yy1; + } else { + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 'f') goto yy928; + goto yy1; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + yyt2 = YYCURSOR; + goto yy206; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy881: + yych = *++YYCURSOR; + if (yych <= 'f') { + if (yych <= ':') { + if (yych <= '.') { + if (yych <= '-') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt1 = YYCURSOR; + goto yy297; + } else { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy929; + } + } else { + if (yych <= 'F') { + if (yych <= '@') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy929; + } else { + if (yych <= 'Z') goto yy1; + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy929; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + if (yych <= 'z') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + yyt2 = YYCURSOR; + goto yy206; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy882: + yych = *++YYCURSOR; + if (yych <= '@') { + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy928; + goto yy1; + } else { + if (yych <= 'F') goto yy928; + if (yych <= '`') goto yy1; + if (yych <= 'f') goto yy928; + goto yy1; + } +yy883: + yych = *++YYCURSOR; + if (yych <= 'z') { + if (yych <= '@') { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy929; + if (yych <= ':') goto yy882; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 'Z') { + if (yych <= 'F') goto yy929; + goto yy1; + } else { + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 'f') goto yy929; + goto yy1; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + yyt2 = YYCURSOR; + goto yy206; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy884: + yych = *++YYCURSOR; + if (yych <= 'f') { + if (yych <= ':') { + if (yych <= '.') { + if (yych <= '-') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt1 = YYCURSOR; + goto yy297; + } else { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy930; + goto yy882; + } + } else { + if (yych <= 'F') { + if (yych <= '@') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy929; + } else { + if (yych <= 'Z') goto yy1; + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy929; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + if (yych <= 'z') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + yyt2 = YYCURSOR; + goto yy206; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy885: + yych = *++YYCURSOR; + if (yych <= '`') { + if (yych <= '9') { + if (yych <= '.') { + if (yych <= '-') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt1 = YYCURSOR; + goto yy297; + } else { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '5') goto yy930; + goto yy929; + } + } else { + if (yych <= '@') { + if (yych <= ':') goto yy882; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 'F') goto yy929; + if (yych <= 'Z') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } + } + } else { + if (yych <= 0xDF) { + if (yych <= 'z') { + if (yych <= 'f') goto yy929; + goto yy1; + } else { + if (yych <= 0x7F) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0xC1) goto yy1; + yyt2 = YYCURSOR; + goto yy205; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xE0) { + yyt2 = YYCURSOR; + goto yy206; + } + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy886: + yych = *++YYCURSOR; + if (yych <= 0x7F) { + if (yych <= '@') { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy1; + if (yych <= ':') goto yy765; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 'Z') goto yy1; + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 'z') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } + } else { + if (yych <= 0xEF) { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + if (yych <= 0xE0) { + yyt2 = YYCURSOR; + goto yy206; + } + yyt2 = YYCURSOR; + goto yy207; + } else { + if (yych <= 0xF0) { + yyt2 = YYCURSOR; + goto yy208; + } + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } +yy887: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '@') { + if (yych <= 0x1B) { + if (yych <= '\n') { + if (yych <= 0x00) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0x08) { + yyt2 = YYCURSOR; + goto yy284; + } + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych == '\r') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0x1A) { + yyt2 = YYCURSOR; + goto yy284; + } + yyt2 = YYCURSOR; + goto yy198; + } + } else { + if (yych <= ',') { + if (yych <= 0x1F) { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych == '$') { + yyt2 = YYCURSOR; + goto yy284; + } + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= ':') goto yy4; + if (yych <= '?') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } + } + } else { + if (yych <= 0x7F) { + if (yych <= '_') { + if (yych <= 'Z') goto yy4; + if (yych <= '^') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 'z') goto yy4; + if (yych <= '~') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } + } else { + if (yych <= 0xEF) { + if (yych <= 0xC1) goto yy4; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy289; + } + if (yych <= 0xE0) { + yyt2 = YYCURSOR; + goto yy290; + } + yyt2 = YYCURSOR; + goto yy291; + } else { + if (yych <= 0xF0) { + yyt2 = YYCURSOR; + goto yy292; + } + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy293; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy294; + } + goto yy4; + } + } + } +yy888: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '?') { + if (yych <= 0x1B) { + if (yych <= '\n') { + if (yych <= 0x00) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0x08) { + yyt2 = YYCURSOR; + goto yy284; + } + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych == '\r') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0x1A) { + yyt2 = YYCURSOR; + goto yy284; + } + yyt2 = YYCURSOR; + goto yy198; + } + } else { + if (yych <= ',') { + if (yych <= 0x1F) { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych == '$') { + yyt2 = YYCURSOR; + goto yy284; + } + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= '5') goto yy887; + if (yych <= ':') goto yy4; + yyt2 = YYCURSOR; + goto yy198; + } + } + } else { + if (yych <= 0x7F) { + if (yych <= '_') { + if (yych <= '@') { + yyt2 = YYCURSOR; + goto yy284; + } + if (yych <= 'Z') goto yy4; + if (yych <= '^') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } else { + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 'z') goto yy4; + if (yych <= '~') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy284; + } + } else { + if (yych <= 0xEF) { + if (yych <= 0xC1) goto yy4; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy289; + } + if (yych <= 0xE0) { + yyt2 = YYCURSOR; + goto yy290; + } + yyt2 = YYCURSOR; + goto yy291; + } else { + if (yych <= 0xF0) { + yyt2 = YYCURSOR; + goto yy292; + } + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy293; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy294; + } + goto yy4; + } + } + } +yy889: + yych = *++YYCURSOR; + if (yych <= '9') { + if (yych <= '0') { + if (yych <= '/') goto yy1; + goto yy931; + } else { + if (yych <= '1') goto yy693; + if (yych <= '2') goto yy694; + goto yy692; + } + } else { + if (yych <= 'F') { + if (yych <= '@') goto yy1; + goto yy695; + } else { + if (yych <= '`') goto yy1; + if (yych <= 'f') goto yy695; + goto yy1; + } + } +yy890: + yych = *++YYCURSOR; + if (yych <= '<') { + if (yych <= 0x1F) { + if (yych <= '\t') { + if (yych <= 0x00) goto yy1; + if (yych <= 0x08) goto yy399; + goto yy890; + } else { + if (yych == '\r') goto yy890; + goto yy399; + } + } else { + if (yych <= '"') { + if (yych <= ' ') goto yy890; + if (yych <= '!') goto yy399; + goto yy835; + } else { + if (yych == '\'') goto yy836; + goto yy399; + } + } + } else { + if (yych <= 0xC1) { + if (yych <= '[') { + if (yych <= '=') goto yy891; + if (yych <= '>') goto yy588; + goto yy399; + } else { + if (yych <= '\\') goto yy400; + if (yych <= 0x7F) goto yy399; + goto yy1; + } + } else { + if (yych <= 0xEF) { + if (yych <= 0xDF) goto yy401; + if (yych <= 0xE0) goto yy402; + goto yy403; + } else { + if (yych <= 0xF0) goto yy404; + if (yych <= 0xF3) goto yy405; + if (yych <= 0xF4) goto yy406; + goto yy1; + } + } + } +yy891: + yych = *++YYCURSOR; + if (yybm[0+yych] & 4) { + goto yy492; + } + if (yych <= 0xDF) { + if (yych <= '>') { + if (yych <= 0x00) goto yy1; + if (yych <= '"') goto yy579; + goto yy399; + } else { + if (yych <= '\\') goto yy589; + if (yych <= 0xC1) goto yy1; + goto yy590; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xE0) goto yy591; + if (yych <= 0xEF) goto yy592; + goto yy593; + } else { + if (yych <= 0xF3) goto yy594; + if (yych <= 0xF4) goto yy595; + goto yy1; + } + } +yy892: + yych = *++YYCURSOR; + if (yych <= '<') { + if (yych <= 0x1F) { + if (yych <= '\t') { + if (yych <= 0x00) goto yy1; + if (yych <= 0x08) goto yy391; + goto yy892; + } else { + if (yych == '\r') goto yy892; + goto yy391; + } + } else { + if (yych <= '"') { + if (yych <= ' ') goto yy892; + if (yych <= '!') goto yy391; + goto yy839; + } else { + if (yych == '\'') goto yy840; + goto yy391; + } + } + } else { + if (yych <= 0xC1) { + if (yych <= '[') { + if (yych <= '=') goto yy893; + if (yych <= '>') goto yy580; + goto yy391; + } else { + if (yych <= '\\') goto yy392; + if (yych <= 0x7F) goto yy391; + goto yy1; + } + } else { + if (yych <= 0xEF) { + if (yych <= 0xDF) goto yy393; + if (yych <= 0xE0) goto yy394; + goto yy395; + } else { + if (yych <= 0xF0) goto yy396; + if (yych <= 0xF3) goto yy397; + if (yych <= 0xF4) goto yy398; + goto yy1; + } + } + } +yy893: + yych = *++YYCURSOR; + if (yybm[0+yych] & 2) { + goto yy491; + } + if (yych <= 0xDF) { + if (yych <= '>') { + if (yych <= 0x00) goto yy1; + if (yych <= '\'') goto yy579; + goto yy391; + } else { + if (yych <= '\\') goto yy581; + if (yych <= 0xC1) goto yy1; + goto yy582; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xE0) goto yy583; + if (yych <= 0xEF) goto yy584; + goto yy585; + } else { + if (yych <= 0xF3) goto yy586; + if (yych <= 0xF4) goto yy587; + goto yy1; + } + } +yy894: + yych = *++YYCURSOR; + if (yych <= '=') { + if (yych <= 0x1F) { + if (yych <= '\t') { + if (yych <= 0x00) goto yy1; + if (yych <= 0x08) goto yy609; + goto yy894; + } else { + if (yych == '\r') goto yy894; + goto yy609; + } + } else { + if (yych <= '\'') { + if (yych <= ' ') goto yy894; + if (yych <= '&') goto yy609; + goto yy599; + } else { + if (yych == '/') goto yy896; + goto yy609; + } + } + } else { + if (yych <= 0xC1) { + if (yych <= '[') { + if (yych <= '>') goto yy513; + if (yych <= '?') goto yy896; + goto yy609; + } else { + if (yych <= '\\') goto yy610; + if (yych <= 0x7F) goto yy609; + goto yy1; + } + } else { + if (yych <= 0xEF) { + if (yych <= 0xDF) goto yy611; + if (yych <= 0xE0) goto yy612; + goto yy613; + } else { + if (yych <= 0xF0) goto yy614; + if (yych <= 0xF3) goto yy615; + if (yych <= 0xF4) goto yy616; + goto yy1; + } + } + } +yy895: + yych = *++YYCURSOR; + if (yych <= '>') { + if (yych <= '&') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy1; + if (yych == '\t') goto yy932; + goto yy609; + } else { + if (yych <= '\r') goto yy932; + if (yych == ' ') goto yy932; + goto yy609; + } + } else { + if (yych <= '.') { + if (yych <= '\'') goto yy599; + if (yych == '-') goto yy895; + goto yy609; + } else { + if (yych <= ':') { + if (yych >= '0') goto yy895; + } else { + if (yych <= '<') goto yy609; + if (yych <= '=') goto yy933; + goto yy513; + } + } + } + } else { + if (yych <= 'z') { + if (yych <= '[') { + if (yych <= '?') goto yy896; + if (yych <= '@') goto yy609; + if (yych <= 'Z') goto yy895; + goto yy609; + } else { + if (yych <= '^') { + if (yych <= '\\') goto yy610; + goto yy609; + } else { + if (yych == '`') goto yy609; + goto yy895; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) goto yy609; + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) goto yy611; + goto yy612; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) goto yy613; + goto yy614; + } else { + if (yych <= 0xF3) goto yy615; + if (yych <= 0xF4) goto yy616; + goto yy1; + } + } + } + } +yy896: + yych = *++YYCURSOR; + if (yych <= 0x7F) { + if (yych <= '=') { + if (yych <= 0x00) goto yy1; + if (yych == '\'') goto yy599; + goto yy609; + } else { + if (yych <= '>') goto yy608; + if (yych == '\\') goto yy610; + goto yy609; + } + } else { + if (yych <= 0xEF) { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) goto yy611; + if (yych <= 0xE0) goto yy612; + goto yy613; + } else { + if (yych <= 0xF0) goto yy614; + if (yych <= 0xF3) goto yy615; + if (yych <= 0xF4) goto yy616; + goto yy1; + } + } +yy897: + yych = *++YYCURSOR; + if (yych <= '=') { + if (yych <= 0x1F) { + if (yych <= '\t') { + if (yych <= 0x00) goto yy1; + if (yych <= 0x08) goto yy598; + goto yy897; + } else { + if (yych == '\r') goto yy897; + goto yy598; + } + } else { + if (yych <= '"') { + if (yych <= ' ') goto yy897; + if (yych <= '!') goto yy598; + goto yy599; + } else { + if (yych == '/') goto yy899; + goto yy598; + } + } + } else { + if (yych <= 0xC1) { + if (yych <= '[') { + if (yych <= '>') goto yy503; + if (yych <= '?') goto yy899; + goto yy598; + } else { + if (yych <= '\\') goto yy600; + if (yych <= 0x7F) goto yy598; + goto yy1; + } + } else { + if (yych <= 0xEF) { + if (yych <= 0xDF) goto yy601; + if (yych <= 0xE0) goto yy602; + goto yy603; + } else { + if (yych <= 0xF0) goto yy604; + if (yych <= 0xF3) goto yy605; + if (yych <= 0xF4) goto yy606; + goto yy1; + } + } + } +yy898: + yych = *++YYCURSOR; + if (yych <= '>') { + if (yych <= '!') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy1; + if (yych == '\t') goto yy934; + goto yy598; + } else { + if (yych <= '\r') goto yy934; + if (yych == ' ') goto yy934; + goto yy598; + } + } else { + if (yych <= '.') { + if (yych <= '"') goto yy599; + if (yych == '-') goto yy898; + goto yy598; + } else { + if (yych <= ':') { + if (yych >= '0') goto yy898; + } else { + if (yych <= '<') goto yy598; + if (yych <= '=') goto yy935; + goto yy503; + } + } + } + } else { + if (yych <= 'z') { + if (yych <= '[') { + if (yych <= '?') goto yy899; + if (yych <= '@') goto yy598; + if (yych <= 'Z') goto yy898; + goto yy598; + } else { + if (yych <= '^') { + if (yych <= '\\') goto yy600; + goto yy598; + } else { + if (yych == '`') goto yy598; + goto yy898; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) goto yy598; + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) goto yy601; + goto yy602; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) goto yy603; + goto yy604; + } else { + if (yych <= 0xF3) goto yy605; + if (yych <= 0xF4) goto yy606; + goto yy1; + } + } + } + } +yy899: + yych = *++YYCURSOR; + if (yych <= 0x7F) { + if (yych <= '=') { + if (yych <= 0x00) goto yy1; + if (yych == '"') goto yy599; + goto yy598; + } else { + if (yych <= '>') goto yy597; + if (yych == '\\') goto yy600; + goto yy598; + } + } else { + if (yych <= 0xEF) { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) goto yy601; + if (yych <= 0xE0) goto yy602; + goto yy603; + } else { + if (yych <= 0xF0) goto yy604; + if (yych <= 0xF3) goto yy605; + if (yych <= 0xF4) goto yy606; + goto yy1; + } + } +yy900: + yych = *++YYCURSOR; + if (yych <= '@') { + if (yych <= ',') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy1; + if (yych == '\t') goto yy900; + goto yy800; + } else { + if (yych <= '\r') goto yy900; + if (yych == ' ') goto yy900; + goto yy800; + } + } else { + if (yych <= ':') { + if (yych <= '-') goto yy854; + if (yych <= '.') goto yy800; + if (yych <= '/') goto yy855; + goto yy854; + } else { + if (yych <= '=') goto yy800; + if (yych <= '>') goto yy856; + if (yych <= '?') goto yy855; + goto yy800; + } + } + } else { + if (yych <= 0x7F) { + if (yych <= '^') { + if (yych <= 'Z') goto yy854; + if (yych == '\\') goto yy857; + goto yy800; + } else { + if (yych == '`') goto yy800; + if (yych <= 'z') goto yy854; + goto yy800; + } + } else { + if (yych <= 0xEF) { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) goto yy858; + if (yych <= 0xE0) goto yy859; + goto yy860; + } else { + if (yych <= 0xF0) goto yy861; + if (yych <= 0xF3) goto yy862; + if (yych <= 0xF4) goto yy863; + goto yy1; + } + } + } +yy901: + yyaccept = 28; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= 0x7F) { + if (yych <= '&') { + if (yych <= 0x00) goto yy315; + if (yych != '"') goto yy856; + } else { + if (yych <= '\'') goto yy903; + if (yych == '\\') goto yy904; + goto yy856; + } + } else { + if (yych <= 0xEF) { + if (yych <= 0xC1) goto yy315; + if (yych <= 0xDF) goto yy905; + if (yych <= 0xE0) goto yy906; + goto yy907; + } else { + if (yych <= 0xF0) goto yy908; + if (yych <= 0xF3) goto yy909; + if (yych <= 0xF4) goto yy910; + goto yy315; + } + } +yy902: + yych = *++YYCURSOR; + if (yych <= '?') { + if (yych <= '&') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy1; + if (yych == '\t') goto yy936; + goto yy631; + } else { + if (yych <= '\r') goto yy936; + if (yych == ' ') goto yy936; + goto yy631; + } + } else { + if (yych <= '.') { + if (yych <= '\'') goto yy721; + if (yych == '-') goto yy937; + goto yy631; + } else { + if (yych <= '/') goto yy938; + if (yych <= ':') goto yy937; + if (yych <= '>') goto yy631; + goto yy938; + } + } + } else { + if (yych <= 'z') { + if (yych <= '\\') { + if (yych <= '@') goto yy631; + if (yych <= 'Z') goto yy937; + if (yych <= '[') goto yy631; + goto yy731; + } else { + if (yych == '_') goto yy937; + if (yych <= '`') goto yy631; + goto yy937; + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) goto yy631; + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) goto yy732; + goto yy733; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) goto yy734; + goto yy735; + } else { + if (yych <= 0xF3) goto yy736; + if (yych <= 0xF4) goto yy737; + goto yy1; + } + } + } + } +yy903: + yych = *++YYCURSOR; + if (yych <= '?') { + if (yych <= '!') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy1; + if (yych == '\t') goto yy939; + goto yy621; + } else { + if (yych <= '\r') goto yy939; + if (yych == ' ') goto yy939; + goto yy621; + } + } else { + if (yych <= '.') { + if (yych <= '"') goto yy721; + if (yych == '-') goto yy940; + goto yy621; + } else { + if (yych <= '/') goto yy941; + if (yych <= ':') goto yy940; + if (yych <= '>') goto yy621; + goto yy941; + } + } + } else { + if (yych <= 'z') { + if (yych <= '\\') { + if (yych <= '@') goto yy621; + if (yych <= 'Z') goto yy940; + if (yych <= '[') goto yy621; + goto yy722; + } else { + if (yych == '_') goto yy940; + if (yych <= '`') goto yy621; + goto yy940; + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) goto yy621; + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) goto yy723; + goto yy724; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) goto yy725; + goto yy726; + } else { + if (yych <= 0xF3) goto yy727; + if (yych <= 0xF4) goto yy728; + goto yy1; + } + } + } + } +yy904: + yych = *++YYCURSOR; + if (yych <= 0xDF) { + if (yych <= '\n') { + if (yych <= '\t') goto yy856; + goto yy1; + } else { + if (yych <= 0x7F) goto yy856; + if (yych <= 0xC1) goto yy1; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xE0) goto yy906; + if (yych <= 0xEF) goto yy907; + goto yy908; + } else { + if (yych <= 0xF3) goto yy909; + if (yych <= 0xF4) goto yy910; + goto yy1; + } + } +yy905: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy856; + goto yy1; +yy906: + yych = *++YYCURSOR; + if (yych <= 0x9F) goto yy1; + if (yych <= 0xBF) goto yy905; + goto yy1; +yy907: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy905; + goto yy1; +yy908: + yych = *++YYCURSOR; + if (yych <= 0x8F) goto yy1; + if (yych <= 0xBF) goto yy907; + goto yy1; +yy909: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy907; + goto yy1; +yy910: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0x8F) goto yy907; + goto yy1; +yy911: + yych = *++YYCURSOR; + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy942; + goto yy1; +yy912: + yyaccept = 29; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= 0xDF) { + if (yych <= ':') { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy642; + } + if (yych <= '9') { + yyt1 = YYCURSOR; + goto yy944; + } + goto yy643; + } else { + if (yych <= 0x7F) { + yyt2 = YYCURSOR; + goto yy642; + } + if (yych <= 0xC1) goto yy643; + yyt2 = YYCURSOR; + goto yy645; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xE0) { + yyt2 = YYCURSOR; + goto yy646; + } + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy647; + } + yyt2 = YYCURSOR; + goto yy648; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy649; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy650; + } + goto yy643; + } + } +yy913: + yych = *++YYCURSOR; + if (yych == 'e') goto yy945; + goto yy1; +yy914: + yych = *++YYCURSOR; + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy946; + goto yy1; +yy915: + yych = *++YYCURSOR; + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy947; + goto yy1; +yy916: + yych = *++YYCURSOR; + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy947; + if (yych <= ':') goto yy152; + goto yy1; +yy917: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '?') { + if (yych <= '+') { + if (yych == '%') goto yy80; + if (yych <= '*') goto yy4; + goto yy80; + } else { + if (yych <= '.') { + if (yych <= ',') goto yy4; + goto yy46; + } else { + if (yych <= '/') goto yy4; + if (yych <= '9') goto yy948; + goto yy4; + } + } + } else { + if (yych <= '^') { + if (yych <= '@') goto yy96; + if (yych <= 'F') goto yy948; + if (yych <= 'Z') goto yy46; + goto yy4; + } else { + if (yych <= '`') { + if (yych <= '_') goto yy46; + goto yy4; + } else { + if (yych <= 'f') goto yy948; + if (yych <= 'z') goto yy46; + goto yy4; + } + } + } +yy918: + yyaccept = 11; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[768+yych] & 32) { + goto yy103; + } + if (yych <= '9') { + if (yych <= '#') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy109; + if (yych <= 0x08) goto yy4; + if (yych <= '\n') goto yy109; + goto yy4; + } else { + if (yych <= 0x1A) { + if (yych <= '\r') goto yy109; + goto yy4; + } else { + if (yych <= 0x1B) goto yy109; + if (yych <= 0x1F) goto yy4; + goto yy109; + } + } + } else { + if (yych <= ',') { + if (yych <= '%') { + if (yych <= '$') goto yy4; + goto yy105; + } else { + if (yych == '+') goto yy80; + goto yy109; + } + } else { + if (yych <= '.') { + if (yych <= '-') goto yy46; + goto yy266; + } else { + if (yych <= '/') goto yy4; + if (yych <= '7') goto yy949; + goto yy950; + } + } + } + } else { + if (yych <= '_') { + if (yych <= 'D') { + if (yych <= ':') goto yy148; + if (yych <= '?') goto yy109; + if (yych <= '@') goto yy96; + goto yy196; + } else { + if (yych <= 'F') { + if (yych <= 'E') goto yy754; + goto yy196; + } else { + if (yych <= 'Z') goto yy115; + if (yych <= '^') goto yy109; + goto yy46; + } + } + } else { + if (yych <= 'z') { + if (yych <= 'd') { + if (yych <= '`') goto yy109; + goto yy196; + } else { + if (yych <= 'e') goto yy754; + if (yych <= 'f') goto yy196; + goto yy115; + } + } else { + if (yych <= 0x7F) { + if (yych <= '~') goto yy109; + goto yy4; + } else { + if (yych <= 0xC1) goto yy109; + if (yych <= 0xF4) goto yy4; + goto yy109; + } + } + } + } +yy919: + yyaccept = 6; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[768+yych] & 32) { + goto yy103; + } + if (yych <= '9') { + if (yych <= '#') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy26; + if (yych <= 0x08) goto yy4; + if (yych <= '\n') goto yy26; + goto yy4; + } else { + if (yych <= 0x1A) { + if (yych <= '\r') goto yy26; + goto yy4; + } else { + if (yych <= 0x1B) goto yy26; + if (yych <= 0x1F) goto yy4; + goto yy26; + } + } + } else { + if (yych <= '+') { + if (yych <= '$') goto yy4; + if (yych <= '%') goto yy105; + if (yych <= '*') goto yy26; + goto yy80; + } else { + if (yych <= '-') { + if (yych <= ',') goto yy26; + goto yy46; + } else { + if (yych <= '.') goto yy266; + if (yych <= '/') goto yy4; + goto yy950; + } + } + } + } else { + if (yych <= '_') { + if (yych <= 'D') { + if (yych <= ':') goto yy148; + if (yych <= '?') goto yy26; + if (yych <= '@') goto yy96; + goto yy196; + } else { + if (yych <= 'F') { + if (yych <= 'E') goto yy754; + goto yy196; + } else { + if (yych <= 'Z') goto yy115; + if (yych <= '^') goto yy26; + goto yy46; + } + } + } else { + if (yych <= 'z') { + if (yych <= 'd') { + if (yych <= '`') goto yy26; + goto yy196; + } else { + if (yych <= 'e') goto yy754; + if (yych <= 'f') goto yy196; + goto yy115; + } + } else { + if (yych <= 0x7F) { + if (yych <= '~') goto yy26; + goto yy4; + } else { + if (yych <= 0xC1) goto yy26; + if (yych <= 0xF4) goto yy4; + goto yy26; + } + } + } + } +yy920: + yyaccept = 6; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '/') { + if (yych <= 0x1F) { + if (yych <= '\f') { + if (yych <= 0x00) goto yy26; + if (yych <= 0x08) goto yy4; + if (yych <= '\n') goto yy26; + goto yy4; + } else { + if (yych <= '\r') goto yy26; + if (yych == 0x1B) goto yy26; + goto yy4; + } + } else { + if (yych <= '*') { + if (yych <= '#') goto yy26; + if (yych <= '$') goto yy4; + if (yych <= '%') goto yy80; + goto yy26; + } else { + if (yych <= '+') goto yy80; + if (yych <= ',') goto yy26; + if (yych <= '.') goto yy46; + goto yy4; + } + } + } else { + if (yych <= '_') { + if (yych <= '@') { + if (yych <= '9') goto yy951; + if (yych <= ':') goto yy4; + if (yych <= '?') goto yy26; + goto yy96; + } else { + if (yych <= 'F') goto yy948; + if (yych <= 'Z') goto yy46; + if (yych <= '^') goto yy26; + goto yy46; + } + } else { + if (yych <= '~') { + if (yych <= '`') goto yy26; + if (yych <= 'f') goto yy948; + if (yych <= 'z') goto yy46; + goto yy26; + } else { + if (yych <= 0x7F) goto yy4; + if (yych <= 0xC1) goto yy26; + if (yych <= 0xF4) goto yy4; + goto yy26; + } + } + } +yy921: + yych = *++YYCURSOR; + if (yych <= 'z') { + if (yych <= '9') { + if (yych == '%') { + yyt1 = YYCURSOR; + goto yy680; + } + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy1; + } else { + if (yych <= '@') { + if (yych <= ':') goto yy816; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 'Z') goto yy1; + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy1; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + yyt2 = YYCURSOR; + goto yy206; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy922: + yych = *++YYCURSOR; + if (yych <= 'f') { + if (yych <= ':') { + if (yych <= '%') { + if (yych <= '$') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt1 = YYCURSOR; + goto yy680; + } else { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy952; + } + } else { + if (yych <= 'F') { + if (yych <= '@') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy952; + } else { + if (yych <= 'Z') goto yy1; + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy952; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + if (yych <= 'z') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + yyt2 = YYCURSOR; + goto yy206; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy923: + yych = *++YYCURSOR; + if (yych <= '@') { + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy953; + goto yy1; + } else { + if (yych <= 'F') goto yy953; + if (yych <= '`') goto yy1; + if (yych <= 'f') goto yy953; + goto yy1; + } +yy924: + yych = *++YYCURSOR; + if (yych <= '@') { + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy954; + if (yych <= ':') goto yy763; + goto yy1; + } else { + if (yych <= 'F') goto yy954; + if (yych <= '`') goto yy1; + if (yych <= 'f') goto yy954; + goto yy1; + } +yy925: + yych = *++YYCURSOR; + if (yych == ':') goto yy820; + goto yy1; +yy926: + yych = *++YYCURSOR; + if (yych <= '@') { + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy955; + if (yych >= ';') goto yy1; + } else { + if (yych <= 'F') goto yy955; + if (yych <= '`') goto yy1; + if (yych <= 'f') goto yy955; + goto yy1; + } +yy927: + yych = *++YYCURSOR; + if (yych <= '@') { + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy956; + if (yych <= ':') goto yy957; + goto yy1; + } else { + if (yych <= 'F') goto yy956; + if (yych <= '`') goto yy1; + if (yych <= 'f') goto yy956; + goto yy1; + } +yy928: + yych = *++YYCURSOR; + if (yych <= 'z') { + if (yych <= '@') { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy958; + if (yych <= ':') goto yy959; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 'Z') { + if (yych <= 'F') goto yy958; + goto yy1; + } else { + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 'f') goto yy958; + goto yy1; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + yyt2 = YYCURSOR; + goto yy206; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy929: + yych = *++YYCURSOR; + if (yych <= 'z') { + if (yych <= '@') { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy960; + if (yych <= ':') goto yy882; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 'Z') { + if (yych <= 'F') goto yy960; + goto yy1; + } else { + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 'f') goto yy960; + goto yy1; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + yyt2 = YYCURSOR; + goto yy206; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy930: + yych = *++YYCURSOR; + if (yych <= 'f') { + if (yych <= ':') { + if (yych <= '.') { + if (yych <= '-') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt1 = YYCURSOR; + goto yy297; + } else { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy960; + goto yy882; + } + } else { + if (yych <= 'F') { + if (yych <= '@') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy960; + } else { + if (yych <= 'Z') goto yy1; + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy960; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + if (yych <= 'z') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + yyt2 = YYCURSOR; + goto yy206; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy931: + yych = *++YYCURSOR; + if (yych <= 'f') { + if (yych <= ':') { + if (yych <= '.') { + if (yych <= '-') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt1 = YYCURSOR; + goto yy297; + } else { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy764; + goto yy961; + } + } else { + if (yych <= 'F') { + if (yych <= '@') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy766; + } else { + if (yych <= 'Z') goto yy1; + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy766; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + if (yych <= 'z') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + yyt2 = YYCURSOR; + goto yy206; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy932: + yych = *++YYCURSOR; + if (yych <= '=') { + if (yych <= 0x1F) { + if (yych <= '\t') { + if (yych <= 0x00) goto yy1; + if (yych <= 0x08) goto yy609; + goto yy932; + } else { + if (yych == '\r') goto yy932; + goto yy609; + } + } else { + if (yych <= '\'') { + if (yych <= ' ') goto yy932; + if (yych <= '&') goto yy609; + goto yy599; + } else { + if (yych == '/') goto yy896; + if (yych <= '<') goto yy609; + } + } + } else { + if (yych <= 0xC1) { + if (yych <= '[') { + if (yych <= '>') goto yy513; + if (yych <= '?') goto yy896; + goto yy609; + } else { + if (yych <= '\\') goto yy610; + if (yych <= 0x7F) goto yy609; + goto yy1; + } + } else { + if (yych <= 0xEF) { + if (yych <= 0xDF) goto yy611; + if (yych <= 0xE0) goto yy612; + goto yy613; + } else { + if (yych <= 0xF0) goto yy614; + if (yych <= 0xF3) goto yy615; + if (yych <= 0xF4) goto yy616; + goto yy1; + } + } + } +yy933: + yych = *++YYCURSOR; + if (yych <= '>') { + if (yych <= '\r') { + if (yych <= 0x08) { + if (yych <= 0x00) goto yy1; + goto yy413; + } else { + if (yych <= '\t') goto yy607; + if (yych <= '\f') goto yy413; + goto yy607; + } + } else { + if (yych <= '!') { + if (yych == ' ') goto yy607; + goto yy413; + } else { + if (yych <= '"') goto yy717; + if (yych <= '=') goto yy413; + goto yy609; + } + } + } else { + if (yych <= 0xDF) { + if (yych <= '\\') { + if (yych <= '[') goto yy413; + goto yy514; + } else { + if (yych <= 0x7F) goto yy413; + if (yych <= 0xC1) goto yy1; + goto yy515; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xE0) goto yy516; + if (yych <= 0xEF) goto yy517; + goto yy518; + } else { + if (yych <= 0xF3) goto yy519; + if (yych <= 0xF4) goto yy520; + goto yy1; + } + } + } +yy934: + yych = *++YYCURSOR; + if (yych <= '=') { + if (yych <= 0x1F) { + if (yych <= '\t') { + if (yych <= 0x00) goto yy1; + if (yych <= 0x08) goto yy598; + goto yy934; + } else { + if (yych == '\r') goto yy934; + goto yy598; + } + } else { + if (yych <= '"') { + if (yych <= ' ') goto yy934; + if (yych <= '!') goto yy598; + goto yy599; + } else { + if (yych == '/') goto yy899; + if (yych <= '<') goto yy598; + } + } + } else { + if (yych <= 0xC1) { + if (yych <= '[') { + if (yych <= '>') goto yy503; + if (yych <= '?') goto yy899; + goto yy598; + } else { + if (yych <= '\\') goto yy600; + if (yych <= 0x7F) goto yy598; + goto yy1; + } + } else { + if (yych <= 0xEF) { + if (yych <= 0xDF) goto yy601; + if (yych <= 0xE0) goto yy602; + goto yy603; + } else { + if (yych <= 0xF0) goto yy604; + if (yych <= 0xF3) goto yy605; + if (yych <= 0xF4) goto yy606; + goto yy1; + } + } + } +yy935: + yych = *++YYCURSOR; + if (yych <= '>') { + if (yych <= '\r') { + if (yych <= 0x08) { + if (yych <= 0x00) goto yy1; + goto yy412; + } else { + if (yych <= '\t') goto yy596; + if (yych <= '\f') goto yy412; + goto yy596; + } + } else { + if (yych <= '&') { + if (yych == ' ') goto yy596; + goto yy412; + } else { + if (yych <= '\'') goto yy717; + if (yych <= '=') goto yy412; + goto yy598; + } + } + } else { + if (yych <= 0xDF) { + if (yych <= '\\') { + if (yych <= '[') goto yy412; + goto yy504; + } else { + if (yych <= 0x7F) goto yy412; + if (yych <= 0xC1) goto yy1; + goto yy505; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xE0) goto yy506; + if (yych <= 0xEF) goto yy507; + goto yy508; + } else { + if (yych <= 0xF3) goto yy509; + if (yych <= 0xF4) goto yy510; + goto yy1; + } + } + } +yy936: + yych = *++YYCURSOR; + if (yych <= '>') { + if (yych <= 0x1F) { + if (yych <= '\t') { + if (yych <= 0x00) goto yy1; + if (yych <= 0x08) goto yy631; + goto yy936; + } else { + if (yych == '\r') goto yy936; + goto yy631; + } + } else { + if (yych <= '\'') { + if (yych <= ' ') goto yy936; + if (yych <= '&') goto yy631; + goto yy721; + } else { + if (yych == '/') goto yy938; + goto yy631; + } + } + } else { + if (yych <= 0xDF) { + if (yych <= '\\') { + if (yych <= '?') goto yy938; + if (yych <= '[') goto yy631; + goto yy731; + } else { + if (yych <= 0x7F) goto yy631; + if (yych <= 0xC1) goto yy1; + goto yy732; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xE0) goto yy733; + if (yych <= 0xEF) goto yy734; + goto yy735; + } else { + if (yych <= 0xF3) goto yy736; + if (yych <= 0xF4) goto yy737; + goto yy1; + } + } + } +yy937: + yych = *++YYCURSOR; + if (yych <= '>') { + if (yych <= '&') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy1; + if (yych == '\t') goto yy962; + goto yy631; + } else { + if (yych <= '\r') goto yy962; + if (yych == ' ') goto yy962; + goto yy631; + } + } else { + if (yych <= '.') { + if (yych <= '\'') goto yy721; + if (yych == '-') goto yy937; + goto yy631; + } else { + if (yych <= ':') { + if (yych >= '0') goto yy937; + } else { + if (yych == '=') goto yy963; + goto yy631; + } + } + } + } else { + if (yych <= 'z') { + if (yych <= '[') { + if (yych <= '?') goto yy938; + if (yych <= '@') goto yy631; + if (yych <= 'Z') goto yy937; + goto yy631; + } else { + if (yych <= '^') { + if (yych <= '\\') goto yy731; + goto yy631; + } else { + if (yych == '`') goto yy631; + goto yy937; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) goto yy631; + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) goto yy732; + goto yy733; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) goto yy734; + goto yy735; + } else { + if (yych <= 0xF3) goto yy736; + if (yych <= 0xF4) goto yy737; + goto yy1; + } + } + } + } +yy938: + yych = *++YYCURSOR; + if (yych <= 0x7F) { + if (yych <= '=') { + if (yych <= 0x00) goto yy1; + if (yych == '\'') goto yy721; + goto yy631; + } else { + if (yych <= '>') goto yy730; + if (yych == '\\') goto yy731; + goto yy631; + } + } else { + if (yych <= 0xEF) { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) goto yy732; + if (yych <= 0xE0) goto yy733; + goto yy734; + } else { + if (yych <= 0xF0) goto yy735; + if (yych <= 0xF3) goto yy736; + if (yych <= 0xF4) goto yy737; + goto yy1; + } + } +yy939: + yych = *++YYCURSOR; + if (yych <= '>') { + if (yych <= 0x1F) { + if (yych <= '\t') { + if (yych <= 0x00) goto yy1; + if (yych <= 0x08) goto yy621; + goto yy939; + } else { + if (yych == '\r') goto yy939; + goto yy621; + } + } else { + if (yych <= '"') { + if (yych <= ' ') goto yy939; + if (yych <= '!') goto yy621; + goto yy721; + } else { + if (yych == '/') goto yy941; + goto yy621; + } + } + } else { + if (yych <= 0xDF) { + if (yych <= '\\') { + if (yych <= '?') goto yy941; + if (yych <= '[') goto yy621; + goto yy722; + } else { + if (yych <= 0x7F) goto yy621; + if (yych <= 0xC1) goto yy1; + goto yy723; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xE0) goto yy724; + if (yych <= 0xEF) goto yy725; + goto yy726; + } else { + if (yych <= 0xF3) goto yy727; + if (yych <= 0xF4) goto yy728; + goto yy1; + } + } + } +yy940: + yych = *++YYCURSOR; + if (yych <= '>') { + if (yych <= '!') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy1; + if (yych == '\t') goto yy964; + goto yy621; + } else { + if (yych <= '\r') goto yy964; + if (yych == ' ') goto yy964; + goto yy621; + } + } else { + if (yych <= '.') { + if (yych <= '"') goto yy721; + if (yych == '-') goto yy940; + goto yy621; + } else { + if (yych <= ':') { + if (yych >= '0') goto yy940; + } else { + if (yych == '=') goto yy965; + goto yy621; + } + } + } + } else { + if (yych <= 'z') { + if (yych <= '[') { + if (yych <= '?') goto yy941; + if (yych <= '@') goto yy621; + if (yych <= 'Z') goto yy940; + goto yy621; + } else { + if (yych <= '^') { + if (yych <= '\\') goto yy722; + goto yy621; + } else { + if (yych == '`') goto yy621; + goto yy940; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) goto yy621; + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) goto yy723; + goto yy724; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) goto yy725; + goto yy726; + } else { + if (yych <= 0xF3) goto yy727; + if (yych <= 0xF4) goto yy728; + goto yy1; + } + } + } + } +yy941: + yych = *++YYCURSOR; + if (yych <= 0x7F) { + if (yych <= '=') { + if (yych <= 0x00) goto yy1; + if (yych == '"') goto yy721; + goto yy621; + } else { + if (yych <= '>') goto yy720; + if (yych == '\\') goto yy722; + goto yy621; + } + } else { + if (yych <= 0xEF) { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) goto yy723; + if (yych <= 0xE0) goto yy724; + goto yy725; + } else { + if (yych <= 0xF0) goto yy726; + if (yych <= 0xF3) goto yy727; + if (yych <= 0xF4) goto yy728; + goto yy1; + } + } +yy942: + yyaccept = 32; + yych = *(YYMARKER = ++YYCURSOR); + if (yych == ':') goto yy966; +yy943: +#line 184 "../../lnav/src/data_scanner_re.re" + { + RET(DT_DATE_TIME); + } +#line 31412 "../../lnav/src/data_scanner_re.cc" +yy944: + yyaccept = 29; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= 0xDF) { + if (yych <= ':') { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy642; + } + if (yych <= '9') { + yyt1 = YYCURSOR; + goto yy967; + } + goto yy643; + } else { + if (yych <= 0x7F) { + yyt2 = YYCURSOR; + goto yy642; + } + if (yych <= 0xC1) goto yy643; + yyt2 = YYCURSOR; + goto yy645; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xE0) { + yyt2 = YYCURSOR; + goto yy646; + } + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy647; + } + yyt2 = YYCURSOR; + goto yy648; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy649; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy650; + } + goto yy643; + } + } +yy945: + yych = *++YYCURSOR; + if (yych == 's') goto yy968; + goto yy1; +yy946: + yych = *++YYCURSOR; + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy969; + goto yy1; +yy947: + yych = *++YYCURSOR; + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy970; + goto yy1; +yy948: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '/') { + if (yych <= '+') { + if (yych == '%') goto yy80; + if (yych <= '*') goto yy4; + goto yy80; + } else { + if (yych <= ',') goto yy4; + if (yych <= '-') goto yy972; + if (yych <= '.') goto yy46; + goto yy4; + } + } else { + if (yych <= 'Z') { + if (yych <= '9') goto yy46; + if (yych <= '?') goto yy4; + if (yych <= '@') goto yy96; + goto yy46; + } else { + if (yych <= '_') { + if (yych <= '^') goto yy4; + goto yy46; + } else { + if (yych <= '`') goto yy4; + if (yych <= 'z') goto yy46; + goto yy4; + } + } + } +yy949: + yyaccept = 11; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[768+yych] & 32) { + goto yy103; + } + if (yych <= '9') { + if (yych <= '#') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy109; + if (yych <= 0x08) goto yy4; + if (yych <= '\n') goto yy109; + goto yy4; + } else { + if (yych <= 0x1A) { + if (yych <= '\r') goto yy109; + goto yy4; + } else { + if (yych <= 0x1B) goto yy109; + if (yych <= 0x1F) goto yy4; + goto yy109; + } + } + } else { + if (yych <= ',') { + if (yych <= '%') { + if (yych <= '$') goto yy4; + goto yy105; + } else { + if (yych == '+') goto yy80; + goto yy109; + } + } else { + if (yych <= '.') { + if (yych <= '-') goto yy46; + goto yy266; + } else { + if (yych <= '/') goto yy4; + if (yych <= '7') goto yy973; + goto yy974; + } + } + } + } else { + if (yych <= '_') { + if (yych <= 'D') { + if (yych <= ':') goto yy148; + if (yych <= '?') goto yy109; + if (yych <= '@') goto yy96; + goto yy196; + } else { + if (yych <= 'F') { + if (yych <= 'E') goto yy754; + goto yy196; + } else { + if (yych <= 'Z') goto yy115; + if (yych <= '^') goto yy109; + goto yy46; + } + } + } else { + if (yych <= 'z') { + if (yych <= 'd') { + if (yych <= '`') goto yy109; + goto yy196; + } else { + if (yych <= 'e') goto yy754; + if (yych <= 'f') goto yy196; + goto yy115; + } + } else { + if (yych <= 0x7F) { + if (yych <= '~') goto yy109; + goto yy4; + } else { + if (yych <= 0xC1) goto yy109; + if (yych <= 0xF4) goto yy4; + goto yy109; + } + } + } + } +yy950: + yyaccept = 6; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[768+yych] & 32) { + goto yy103; + } + if (yych <= '9') { + if (yych <= '#') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy26; + if (yych <= 0x08) goto yy4; + if (yych <= '\n') goto yy26; + goto yy4; + } else { + if (yych <= 0x1A) { + if (yych <= '\r') goto yy26; + goto yy4; + } else { + if (yych <= 0x1B) goto yy26; + if (yych <= 0x1F) goto yy4; + goto yy26; + } + } + } else { + if (yych <= '+') { + if (yych <= '$') goto yy4; + if (yych <= '%') goto yy105; + if (yych <= '*') goto yy26; + goto yy80; + } else { + if (yych <= '-') { + if (yych <= ',') goto yy26; + goto yy46; + } else { + if (yych <= '.') goto yy266; + if (yych <= '/') goto yy4; + goto yy974; + } + } + } + } else { + if (yych <= '_') { + if (yych <= 'D') { + if (yych <= ':') goto yy148; + if (yych <= '?') goto yy26; + if (yych <= '@') goto yy96; + goto yy196; + } else { + if (yych <= 'F') { + if (yych <= 'E') goto yy754; + goto yy196; + } else { + if (yych <= 'Z') goto yy115; + if (yych <= '^') goto yy26; + goto yy46; + } + } + } else { + if (yych <= 'z') { + if (yych <= 'd') { + if (yych <= '`') goto yy26; + goto yy196; + } else { + if (yych <= 'e') goto yy754; + if (yych <= 'f') goto yy196; + goto yy115; + } + } else { + if (yych <= 0x7F) { + if (yych <= '~') goto yy26; + goto yy4; + } else { + if (yych <= 0xC1) goto yy26; + if (yych <= 0xF4) goto yy4; + goto yy26; + } + } + } + } +yy951: + yyaccept = 6; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '-') { + if (yych <= 0x1B) { + if (yych <= '\n') { + if (yych <= 0x00) goto yy26; + if (yych <= 0x08) goto yy4; + goto yy26; + } else { + if (yych == '\r') goto yy26; + if (yych <= 0x1A) goto yy4; + goto yy26; + } + } else { + if (yych <= '%') { + if (yych <= 0x1F) goto yy4; + if (yych <= '#') goto yy26; + if (yych <= '$') goto yy4; + goto yy80; + } else { + if (yych == '+') goto yy80; + if (yych <= ',') goto yy26; + goto yy972; + } + } + } else { + if (yych <= '^') { + if (yych <= ':') { + if (yych <= '.') goto yy46; + if (yych <= '/') goto yy4; + if (yych <= '9') goto yy340; + goto yy4; + } else { + if (yych <= '?') goto yy26; + if (yych <= '@') goto yy96; + if (yych <= 'Z') goto yy46; + goto yy26; + } + } else { + if (yych <= '~') { + if (yych == '`') goto yy26; + if (yych <= 'z') goto yy46; + goto yy26; + } else { + if (yych <= 0x7F) goto yy4; + if (yych <= 0xC1) goto yy26; + if (yych <= 0xF4) goto yy4; + goto yy26; + } + } + } +yy952: + yych = *++YYCURSOR; + if (yych <= 'f') { + if (yych <= ':') { + if (yych <= '%') { + if (yych <= '$') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt1 = YYCURSOR; + goto yy680; + } else { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy975; + goto yy923; + } + } else { + if (yych <= 'F') { + if (yych <= '@') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy975; + } else { + if (yych <= 'Z') goto yy1; + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy975; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + if (yych <= 'z') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + yyt2 = YYCURSOR; + goto yy206; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy953: + yych = *++YYCURSOR; + if (yych <= 'f') { + if (yych <= ':') { + if (yych <= '%') { + if (yych <= '$') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt1 = YYCURSOR; + goto yy680; + } else { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy976; + goto yy882; + } + } else { + if (yych <= 'F') { + if (yych <= '@') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy976; + } else { + if (yych <= 'Z') goto yy1; + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy976; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + if (yych <= 'z') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + yyt2 = YYCURSOR; + goto yy206; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy954: + yych = *++YYCURSOR; + if (yych <= '@') { + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy977; + if (yych <= ':') goto yy820; + goto yy1; + } else { + if (yych <= 'F') goto yy977; + if (yych <= '`') goto yy1; + if (yych <= 'f') goto yy977; + goto yy1; + } +yy955: + yych = *++YYCURSOR; + if (yych <= '@') { + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy978; + if (yych <= ':') goto yy927; + goto yy1; + } else { + if (yych <= 'F') goto yy978; + if (yych <= '`') goto yy1; + if (yych <= 'f') goto yy978; + goto yy1; + } +yy956: + yych = *++YYCURSOR; + if (yych <= '@') { + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy979; + if (yych <= ':') goto yy980; + goto yy1; + } else { + if (yych <= 'F') goto yy979; + if (yych <= '`') goto yy1; + if (yych <= 'f') goto yy979; + goto yy1; + } +yy957: + yych = *++YYCURSOR; + if (yych <= 'z') { + if (yych <= '@') { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy981; + if (yych <= ':') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 'Z') { + if (yych <= 'F') goto yy981; + goto yy1; + } else { + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 'f') goto yy981; + goto yy1; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + yyt2 = YYCURSOR; + goto yy206; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy958: + yych = *++YYCURSOR; + if (yych <= 'z') { + if (yych <= '@') { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy982; + if (yych >= ';') { + yyt2 = YYCURSOR; + goto yy198; + } + } else { + if (yych <= 'Z') { + if (yych <= 'F') goto yy982; + goto yy1; + } else { + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 'f') goto yy982; + goto yy1; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + yyt2 = YYCURSOR; + goto yy206; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy959: + yych = *++YYCURSOR; + if (yych <= '@') { + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy981; + goto yy1; + } else { + if (yych <= 'F') goto yy981; + if (yych <= '`') goto yy1; + if (yych <= 'f') goto yy981; + goto yy1; + } +yy960: + yych = *++YYCURSOR; + if (yych <= 0x7F) { + if (yych <= '@') { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy1; + if (yych <= ':') goto yy882; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 'Z') goto yy1; + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 'z') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } + } else { + if (yych <= 0xEF) { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + if (yych <= 0xE0) { + yyt2 = YYCURSOR; + goto yy206; + } + yyt2 = YYCURSOR; + goto yy207; + } else { + if (yych <= 0xF0) { + yyt2 = YYCURSOR; + goto yy208; + } + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } +yy961: + yych = *++YYCURSOR; + if (yych <= '9') { + if (yych <= '0') { + if (yych <= '/') goto yy1; + goto yy983; + } else { + if (yych <= '1') goto yy822; + if (yych <= '2') goto yy823; + goto yy821; + } + } else { + if (yych <= 'F') { + if (yych <= '@') goto yy1; + goto yy824; + } else { + if (yych <= '`') goto yy1; + if (yych <= 'f') goto yy824; + goto yy1; + } + } +yy962: + yych = *++YYCURSOR; + if (yych <= '=') { + if (yych <= 0x1F) { + if (yych <= '\t') { + if (yych <= 0x00) goto yy1; + if (yych <= 0x08) goto yy631; + goto yy962; + } else { + if (yych == '\r') goto yy962; + goto yy631; + } + } else { + if (yych <= '\'') { + if (yych <= ' ') goto yy962; + if (yych <= '&') goto yy631; + goto yy721; + } else { + if (yych == '/') goto yy938; + if (yych <= '<') goto yy631; + } + } + } else { + if (yych <= 0xC1) { + if (yych <= '[') { + if (yych == '?') goto yy938; + goto yy631; + } else { + if (yych <= '\\') goto yy731; + if (yych <= 0x7F) goto yy631; + goto yy1; + } + } else { + if (yych <= 0xEF) { + if (yych <= 0xDF) goto yy732; + if (yych <= 0xE0) goto yy733; + goto yy734; + } else { + if (yych <= 0xF0) goto yy735; + if (yych <= 0xF3) goto yy736; + if (yych <= 0xF4) goto yy737; + goto yy1; + } + } + } +yy963: + yych = *++YYCURSOR; + if (yych <= '>') { + if (yych <= '\r') { + if (yych <= 0x08) { + if (yych <= 0x00) goto yy1; + goto yy525; + } else { + if (yych <= '\t') goto yy729; + if (yych <= '\f') goto yy525; + goto yy729; + } + } else { + if (yych <= '!') { + if (yych == ' ') goto yy729; + goto yy525; + } else { + if (yych <= '"') goto yy800; + if (yych <= '=') goto yy525; + goto yy631; + } + } + } else { + if (yych <= 0xDF) { + if (yych <= '\\') { + if (yych <= '[') goto yy525; + goto yy632; + } else { + if (yych <= 0x7F) goto yy525; + if (yych <= 0xC1) goto yy1; + goto yy633; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xE0) goto yy634; + if (yych <= 0xEF) goto yy635; + goto yy636; + } else { + if (yych <= 0xF3) goto yy637; + if (yych <= 0xF4) goto yy638; + goto yy1; + } + } + } +yy964: + yych = *++YYCURSOR; + if (yych <= '=') { + if (yych <= 0x1F) { + if (yych <= '\t') { + if (yych <= 0x00) goto yy1; + if (yych <= 0x08) goto yy621; + goto yy964; + } else { + if (yych == '\r') goto yy964; + goto yy621; + } + } else { + if (yych <= '"') { + if (yych <= ' ') goto yy964; + if (yych <= '!') goto yy621; + goto yy721; + } else { + if (yych == '/') goto yy941; + if (yych <= '<') goto yy621; + } + } + } else { + if (yych <= 0xC1) { + if (yych <= '[') { + if (yych == '?') goto yy941; + goto yy621; + } else { + if (yych <= '\\') goto yy722; + if (yych <= 0x7F) goto yy621; + goto yy1; + } + } else { + if (yych <= 0xEF) { + if (yych <= 0xDF) goto yy723; + if (yych <= 0xE0) goto yy724; + goto yy725; + } else { + if (yych <= 0xF0) goto yy726; + if (yych <= 0xF3) goto yy727; + if (yych <= 0xF4) goto yy728; + goto yy1; + } + } + } +yy965: + yych = *++YYCURSOR; + if (yych <= '>') { + if (yych <= '\r') { + if (yych <= 0x08) { + if (yych <= 0x00) goto yy1; + goto yy524; + } else { + if (yych <= '\t') goto yy719; + if (yych <= '\f') goto yy524; + goto yy719; + } + } else { + if (yych <= '&') { + if (yych == ' ') goto yy719; + goto yy524; + } else { + if (yych <= '\'') goto yy800; + if (yych <= '=') goto yy524; + goto yy621; + } + } + } else { + if (yych <= 0xDF) { + if (yych <= '\\') { + if (yych <= '[') goto yy524; + goto yy622; + } else { + if (yych <= 0x7F) goto yy524; + if (yych <= 0xC1) goto yy1; + goto yy623; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xE0) goto yy624; + if (yych <= 0xEF) goto yy625; + goto yy626; + } else { + if (yych <= 0xF3) goto yy627; + if (yych <= 0xF4) goto yy628; + goto yy1; + } + } + } +yy966: + yych = *++YYCURSOR; + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy984; + goto yy1; +yy967: + yyaccept = 29; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= 0xDF) { + if (yych <= ':') { + if (yych <= '9') { + yyt2 = YYCURSOR; + goto yy642; + } + goto yy643; + } else { + if (yych <= 0x7F) { + yyt2 = YYCURSOR; + goto yy642; + } + if (yych <= 0xC1) goto yy643; + yyt2 = YYCURSOR; + goto yy645; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xE0) { + yyt2 = YYCURSOR; + goto yy646; + } + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy647; + } + yyt2 = YYCURSOR; + goto yy648; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy649; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy650; + } + goto yy643; + } + } +yy968: + yyaccept = 5; + yych = *(YYMARKER = ++YYCURSOR); + if (yych == ' ') goto yy985; + goto yy100; +yy969: + yych = *++YYCURSOR; + if (yych == ' ') goto yy986; + goto yy1; +yy970: + yyaccept = 33; + yych = *(YYMARKER = ++YYCURSOR); + if (yych == ':') goto yy987; +yy971: +#line 181 "../../lnav/src/data_scanner_re.re" + { + RET(DT_DATE_TIME); + } +#line 32346 "../../lnav/src/data_scanner_re.cc" +yy972: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '?') { + if (yych <= '+') { + if (yych == '%') goto yy80; + if (yych <= '*') goto yy4; + goto yy80; + } else { + if (yych <= '.') { + if (yych <= ',') goto yy4; + goto yy46; + } else { + if (yych <= '/') goto yy4; + if (yych <= '9') goto yy988; + goto yy4; + } + } + } else { + if (yych <= '^') { + if (yych <= '@') goto yy96; + if (yych <= 'F') goto yy988; + if (yych <= 'Z') goto yy46; + goto yy4; + } else { + if (yych <= '`') { + if (yych <= '_') goto yy46; + goto yy4; + } else { + if (yych <= 'f') goto yy988; + if (yych <= 'z') goto yy46; + goto yy4; + } + } + } +yy973: + yyaccept = 11; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[768+yych] & 32) { + goto yy103; + } + if (yych <= '9') { + if (yych <= '#') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy109; + if (yych <= 0x08) goto yy4; + if (yych <= '\n') goto yy109; + goto yy4; + } else { + if (yych <= 0x1A) { + if (yych <= '\r') goto yy109; + goto yy4; + } else { + if (yych <= 0x1B) goto yy109; + if (yych <= 0x1F) goto yy4; + goto yy109; + } + } + } else { + if (yych <= ',') { + if (yych <= '%') { + if (yych <= '$') goto yy4; + goto yy105; + } else { + if (yych == '+') goto yy80; + goto yy109; + } + } else { + if (yych <= '.') { + if (yych <= '-') goto yy46; + goto yy266; + } else { + if (yych <= '/') goto yy4; + if (yych <= '7') goto yy989; + goto yy990; + } + } + } + } else { + if (yych <= '_') { + if (yych <= 'D') { + if (yych <= ':') goto yy148; + if (yych <= '?') goto yy109; + if (yych <= '@') goto yy96; + goto yy196; + } else { + if (yych <= 'F') { + if (yych <= 'E') goto yy754; + goto yy196; + } else { + if (yych <= 'Z') goto yy115; + if (yych <= '^') goto yy109; + goto yy46; + } + } + } else { + if (yych <= 'z') { + if (yych <= 'd') { + if (yych <= '`') goto yy109; + goto yy196; + } else { + if (yych <= 'e') goto yy754; + if (yych <= 'f') goto yy196; + goto yy115; + } + } else { + if (yych <= 0x7F) { + if (yych <= '~') goto yy109; + goto yy4; + } else { + if (yych <= 0xC1) goto yy109; + if (yych <= 0xF4) goto yy4; + goto yy109; + } + } + } + } +yy974: + yyaccept = 6; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[768+yych] & 32) { + goto yy103; + } + if (yych <= '9') { + if (yych <= '#') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy26; + if (yych <= 0x08) goto yy4; + if (yych <= '\n') goto yy26; + goto yy4; + } else { + if (yych <= 0x1A) { + if (yych <= '\r') goto yy26; + goto yy4; + } else { + if (yych <= 0x1B) goto yy26; + if (yych <= 0x1F) goto yy4; + goto yy26; + } + } + } else { + if (yych <= '+') { + if (yych <= '$') goto yy4; + if (yych <= '%') goto yy105; + if (yych <= '*') goto yy26; + goto yy80; + } else { + if (yych <= '-') { + if (yych <= ',') goto yy26; + goto yy46; + } else { + if (yych <= '.') goto yy266; + if (yych <= '/') goto yy4; + goto yy990; + } + } + } + } else { + if (yych <= '_') { + if (yych <= 'D') { + if (yych <= ':') goto yy148; + if (yych <= '?') goto yy26; + if (yych <= '@') goto yy96; + goto yy196; + } else { + if (yych <= 'F') { + if (yych <= 'E') goto yy754; + goto yy196; + } else { + if (yych <= 'Z') goto yy115; + if (yych <= '^') goto yy26; + goto yy46; + } + } + } else { + if (yych <= 'z') { + if (yych <= 'd') { + if (yych <= '`') goto yy26; + goto yy196; + } else { + if (yych <= 'e') goto yy754; + if (yych <= 'f') goto yy196; + goto yy115; + } + } else { + if (yych <= 0x7F) { + if (yych <= '~') goto yy26; + goto yy4; + } else { + if (yych <= 0xC1) goto yy26; + if (yych <= 0xF4) goto yy4; + goto yy26; + } + } + } + } +yy975: + yych = *++YYCURSOR; + if (yych <= 'z') { + if (yych <= '9') { + if (yych == '%') { + yyt1 = YYCURSOR; + goto yy680; + } + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy1; + } else { + if (yych <= '@') { + if (yych <= ':') goto yy923; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 'Z') goto yy1; + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy1; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + yyt2 = YYCURSOR; + goto yy206; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy976: + yych = *++YYCURSOR; + if (yych <= 'f') { + if (yych <= ':') { + if (yych <= '%') { + if (yych <= '$') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt1 = YYCURSOR; + goto yy680; + } else { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy991; + goto yy882; + } + } else { + if (yych <= 'F') { + if (yych <= '@') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy991; + } else { + if (yych <= 'Z') goto yy1; + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy991; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + if (yych <= 'z') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + yyt2 = YYCURSOR; + goto yy206; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy977: + yyaccept = 21; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= ':') { + if (yych <= '-') { + if (yych <= ',') goto yy354; + goto yy466; + } else { + if (yych <= '/') goto yy354; + if (yych <= '9') goto yy878; + goto yy992; + } + } else { + if (yych <= 'F') { + if (yych <= '@') goto yy354; + goto yy878; + } else { + if (yych <= '`') goto yy354; + if (yych <= 'f') goto yy878; + goto yy354; + } + } +yy978: + yych = *++YYCURSOR; + if (yych == ':') goto yy927; + goto yy1; +yy979: + yych = *++YYCURSOR; + if (yych <= '@') { + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy993; + if (yych >= ';') goto yy1; + } else { + if (yych <= 'F') goto yy993; + if (yych <= '`') goto yy1; + if (yych <= 'f') goto yy993; + goto yy1; + } +yy980: + yych = *++YYCURSOR; + if (yych <= '@') { + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy981; + if (yych <= ':') goto yy830; + goto yy1; + } else { + if (yych <= 'F') goto yy981; + if (yych <= '`') goto yy1; + if (yych >= 'g') goto yy1; + } +yy981: + yych = *++YYCURSOR; + if (yych <= 'z') { + if (yych <= '@') { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy994; + if (yych <= ':') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 'Z') { + if (yych <= 'F') goto yy994; + goto yy1; + } else { + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 'f') goto yy994; + goto yy1; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + yyt2 = YYCURSOR; + goto yy206; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy982: + yych = *++YYCURSOR; + if (yych <= 'z') { + if (yych <= '@') { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy995; + if (yych <= ':') goto yy959; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 'Z') { + if (yych <= 'F') goto yy995; + goto yy1; + } else { + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 'f') goto yy995; + goto yy1; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + yyt2 = YYCURSOR; + goto yy206; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy983: + yych = *++YYCURSOR; + if (yych <= 'f') { + if (yych <= ':') { + if (yych <= '.') { + if (yych <= '-') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt1 = YYCURSOR; + goto yy297; + } else { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy881; + goto yy996; + } + } else { + if (yych <= 'F') { + if (yych <= '@') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy883; + } else { + if (yych <= 'Z') goto yy1; + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy883; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + if (yych <= 'z') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + yyt2 = YYCURSOR; + goto yy206; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy984: + yych = *++YYCURSOR; + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy997; + goto yy1; +yy985: + yych = *++YYCURSOR; + if (yych == '(') goto yy998; + goto yy1; +yy986: + yych = *++YYCURSOR; + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy999; + goto yy1; +yy987: + yych = *++YYCURSOR; + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy1000; + goto yy1; +yy988: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '?') { + if (yych <= '+') { + if (yych == '%') goto yy80; + if (yych <= '*') goto yy4; + goto yy80; + } else { + if (yych <= '.') { + if (yych <= ',') goto yy4; + goto yy46; + } else { + if (yych <= '/') goto yy4; + if (yych <= '9') goto yy1001; + goto yy4; + } + } + } else { + if (yych <= '^') { + if (yych <= '@') goto yy96; + if (yych <= 'F') goto yy1001; + if (yych <= 'Z') goto yy46; + goto yy4; + } else { + if (yych <= '`') { + if (yych <= '_') goto yy46; + goto yy4; + } else { + if (yych <= 'f') goto yy1001; + if (yych <= 'z') goto yy46; + goto yy4; + } + } + } +yy989: + yyaccept = 11; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[768+yych] & 32) { + goto yy103; + } + if (yych <= '9') { + if (yych <= '#') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy109; + if (yych <= 0x08) goto yy4; + if (yych <= '\n') goto yy109; + goto yy4; + } else { + if (yych <= 0x1A) { + if (yych <= '\r') goto yy109; + goto yy4; + } else { + if (yych <= 0x1B) goto yy109; + if (yych <= 0x1F) goto yy4; + goto yy109; + } + } + } else { + if (yych <= ',') { + if (yych <= '%') { + if (yych <= '$') goto yy4; + goto yy105; + } else { + if (yych == '+') goto yy80; + goto yy109; + } + } else { + if (yych <= '.') { + if (yych <= '-') goto yy46; + goto yy266; + } else { + if (yych <= '/') goto yy4; + if (yych <= '7') goto yy1002; + goto yy1003; + } + } + } + } else { + if (yych <= '_') { + if (yych <= 'D') { + if (yych <= ':') goto yy148; + if (yych <= '?') goto yy109; + if (yych <= '@') goto yy96; + goto yy196; + } else { + if (yych <= 'F') { + if (yych <= 'E') goto yy754; + goto yy196; + } else { + if (yych <= 'Z') goto yy115; + if (yych <= '^') goto yy109; + goto yy46; + } + } + } else { + if (yych <= 'z') { + if (yych <= 'd') { + if (yych <= '`') goto yy109; + goto yy196; + } else { + if (yych <= 'e') goto yy754; + if (yych <= 'f') goto yy196; + goto yy115; + } + } else { + if (yych <= 0x7F) { + if (yych <= '~') goto yy109; + goto yy4; + } else { + if (yych <= 0xC1) goto yy109; + if (yych <= 0xF4) goto yy4; + goto yy109; + } + } + } + } +yy990: + yyaccept = 6; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[768+yych] & 32) { + goto yy103; + } + if (yych <= '9') { + if (yych <= '#') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy26; + if (yych <= 0x08) goto yy4; + if (yych <= '\n') goto yy26; + goto yy4; + } else { + if (yych <= 0x1A) { + if (yych <= '\r') goto yy26; + goto yy4; + } else { + if (yych <= 0x1B) goto yy26; + if (yych <= 0x1F) goto yy4; + goto yy26; + } + } + } else { + if (yych <= '+') { + if (yych <= '$') goto yy4; + if (yych <= '%') goto yy105; + if (yych <= '*') goto yy26; + goto yy80; + } else { + if (yych <= '-') { + if (yych <= ',') goto yy26; + goto yy46; + } else { + if (yych <= '.') goto yy266; + if (yych <= '/') goto yy4; + goto yy1003; + } + } + } + } else { + if (yych <= '_') { + if (yych <= 'D') { + if (yych <= ':') goto yy148; + if (yych <= '?') goto yy26; + if (yych <= '@') goto yy96; + goto yy196; + } else { + if (yych <= 'F') { + if (yych <= 'E') goto yy754; + goto yy196; + } else { + if (yych <= 'Z') goto yy115; + if (yych <= '^') goto yy26; + goto yy46; + } + } + } else { + if (yych <= 'z') { + if (yych <= 'd') { + if (yych <= '`') goto yy26; + goto yy196; + } else { + if (yych <= 'e') goto yy754; + if (yych <= 'f') goto yy196; + goto yy115; + } + } else { + if (yych <= 0x7F) { + if (yych <= '~') goto yy26; + goto yy4; + } else { + if (yych <= 0xC1) goto yy26; + if (yych <= 0xF4) goto yy4; + goto yy26; + } + } + } + } +yy991: + yych = *++YYCURSOR; + if (yych <= 'f') { + if (yych <= ':') { + if (yych <= '%') { + if (yych <= '$') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt1 = YYCURSOR; + goto yy680; + } else { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy1004; + goto yy882; + } + } else { + if (yych <= 'F') { + if (yych <= '@') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy1004; + } else { + if (yych <= 'Z') goto yy1; + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy1004; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + if (yych <= 'z') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + yyt2 = YYCURSOR; + goto yy206; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy992: + yych = *++YYCURSOR; + if (yych <= '@') { + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy1005; + if (yych <= ':') goto yy880; + goto yy1; + } else { + if (yych <= 'F') goto yy1005; + if (yych <= '`') goto yy1; + if (yych <= 'f') goto yy1005; + goto yy1; + } +yy993: + yych = *++YYCURSOR; + if (yych <= '@') { + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy1006; + if (yych <= ':') goto yy980; + goto yy1; + } else { + if (yych <= 'F') goto yy1006; + if (yych <= '`') goto yy1; + if (yych <= 'f') goto yy1006; + goto yy1; + } +yy994: + yych = *++YYCURSOR; + if (yych <= 'z') { + if (yych <= '@') { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy1007; + if (yych <= ':') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 'Z') { + if (yych <= 'F') goto yy1007; + goto yy1; + } else { + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 'f') goto yy1007; + goto yy1; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + yyt2 = YYCURSOR; + goto yy206; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy995: + yych = *++YYCURSOR; + if (yych <= 0x7F) { + if (yych <= '@') { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy1; + if (yych <= ':') goto yy959; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 'Z') goto yy1; + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 'z') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } + } else { + if (yych <= 0xEF) { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + if (yych <= 0xE0) { + yyt2 = YYCURSOR; + goto yy206; + } + yyt2 = YYCURSOR; + goto yy207; + } else { + if (yych <= 0xF0) { + yyt2 = YYCURSOR; + goto yy208; + } + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } +yy996: + yych = *++YYCURSOR; + if (yych <= '9') { + if (yych <= '0') { + if (yych <= '/') goto yy1; + goto yy1008; + } else { + if (yych <= '1') goto yy1009; + if (yych <= '2') goto yy1010; + goto yy1008; + } + } else { + if (yych <= 'F') { + if (yych <= '@') goto yy1; + goto yy928; + } else { + if (yych <= '`') goto yy1; + if (yych <= 'f') goto yy928; + goto yy1; + } + } +yy997: + yyaccept = 32; + yych = *(YYMARKER = ++YYCURSOR); + if (yych == '.') goto yy1011; + goto yy943; +yy998: + yych = *++YYCURSOR; + if (yych == 'x') goto yy1012; + goto yy1; +yy999: + yych = *++YYCURSOR; + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy1013; + goto yy1; +yy1000: + yych = *++YYCURSOR; + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy1014; + goto yy1; +yy1001: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '?') { + if (yych <= '+') { + if (yych == '%') goto yy80; + if (yych <= '*') goto yy4; + goto yy80; + } else { + if (yych <= '.') { + if (yych <= ',') goto yy4; + goto yy46; + } else { + if (yych <= '/') goto yy4; + if (yych <= '9') goto yy1015; + goto yy4; + } + } + } else { + if (yych <= '^') { + if (yych <= '@') goto yy96; + if (yych <= 'F') goto yy1015; + if (yych <= 'Z') goto yy46; + goto yy4; + } else { + if (yych <= '`') { + if (yych <= '_') goto yy46; + goto yy4; + } else { + if (yych <= 'f') goto yy1015; + if (yych <= 'z') goto yy46; + goto yy4; + } + } + } +yy1002: + yyaccept = 11; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '?') { + if (yych <= '#') { + if (yych <= '\r') { + if (yych <= 0x08) { + if (yych <= 0x00) { + yyt1 = YYCURSOR; + goto yy1016; + } + yyt1 = YYCURSOR; + goto yy1018; + } else { + if (yych <= '\n') { + yyt1 = YYCURSOR; + goto yy1016; + } + if (yych <= '\f') { + yyt1 = YYCURSOR; + goto yy1018; + } + yyt1 = YYCURSOR; + goto yy1016; + } + } else { + if (yych <= 0x1B) { + if (yych <= 0x1A) { + yyt1 = YYCURSOR; + goto yy1018; + } + yyt1 = YYCURSOR; + goto yy1016; + } else { + if (yych <= 0x1F) { + yyt1 = YYCURSOR; + goto yy1018; + } + if (yych <= ' ') { + yyt1 = YYCURSOR; + goto yy1019; + } + yyt1 = YYCURSOR; + goto yy1016; + } + } + } else { + if (yych <= ',') { + if (yych <= '%') { + if (yych <= '$') { + yyt1 = YYCURSOR; + goto yy1018; + } + yyt1 = YYCURSOR; + goto yy1020; + } else { + if (yych == '+') { + yyt1 = YYCURSOR; + goto yy1020; + } + yyt1 = YYCURSOR; + goto yy1016; + } + } else { + if (yych <= '/') { + if (yych <= '-') { + yyt1 = YYCURSOR; + goto yy1021; + } + if (yych <= '.') { + yyt1 = YYCURSOR; + goto yy1022; + } + yyt1 = YYCURSOR; + goto yy1018; + } else { + if (yych <= '9') goto yy1024; + if (yych <= ':') { + yyt1 = YYCURSOR; + goto yy1026; + } + yyt1 = YYCURSOR; + goto yy1016; + } + } + } + } else { + if (yych <= 'f') { + if (yych <= 'Z') { + if (yych <= 'D') { + if (yych <= '@') { + yyt1 = YYCURSOR; + goto yy1027; + } + yyt1 = YYCURSOR; + goto yy1028; + } else { + if (yych <= 'E') { + yyt1 = YYCURSOR; + goto yy1029; + } + if (yych <= 'F') { + yyt1 = YYCURSOR; + goto yy1028; + } + yyt1 = YYCURSOR; + goto yy1030; + } + } else { + if (yych <= '`') { + if (yych == '_') { + yyt1 = YYCURSOR; + goto yy1021; + } + yyt1 = YYCURSOR; + goto yy1016; + } else { + if (yych == 'e') { + yyt1 = YYCURSOR; + goto yy1029; + } + yyt1 = YYCURSOR; + goto yy1028; + } + } + } else { + if (yych <= 0xDF) { + if (yych <= '~') { + if (yych <= 'z') { + yyt1 = YYCURSOR; + goto yy1030; + } + yyt1 = YYCURSOR; + goto yy1016; + } else { + if (yych <= 0x7F) { + yyt1 = YYCURSOR; + goto yy1018; + } + if (yych <= 0xC1) goto yy1024; + yyt1 = YYCURSOR; + goto yy1031; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xE0) { + yyt1 = YYCURSOR; + goto yy1032; + } + if (yych <= 0xEF) { + yyt1 = YYCURSOR; + goto yy1033; + } + yyt1 = YYCURSOR; + goto yy1034; + } else { + if (yych <= 0xF3) { + yyt1 = YYCURSOR; + goto yy1035; + } + if (yych <= 0xF4) { + yyt1 = YYCURSOR; + goto yy1036; + } + goto yy1024; + } + } + } + } +yy1003: + yyaccept = 6; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '?') { + if (yych <= '#') { + if (yych <= '\r') { + if (yych <= 0x08) { + if (yych <= 0x00) { + yyt1 = YYCURSOR; + goto yy1016; + } + yyt1 = YYCURSOR; + goto yy1018; + } else { + if (yych <= '\n') { + yyt1 = YYCURSOR; + goto yy1016; + } + if (yych <= '\f') { + yyt1 = YYCURSOR; + goto yy1018; + } + yyt1 = YYCURSOR; + goto yy1016; + } + } else { + if (yych <= 0x1B) { + if (yych <= 0x1A) { + yyt1 = YYCURSOR; + goto yy1018; + } + yyt1 = YYCURSOR; + goto yy1016; + } else { + if (yych <= 0x1F) { + yyt1 = YYCURSOR; + goto yy1018; + } + if (yych <= ' ') { + yyt1 = YYCURSOR; + goto yy1019; + } + yyt1 = YYCURSOR; + goto yy1016; + } + } + } else { + if (yych <= ',') { + if (yych <= '%') { + if (yych <= '$') { + yyt1 = YYCURSOR; + goto yy1018; + } + yyt1 = YYCURSOR; + goto yy1020; + } else { + if (yych == '+') { + yyt1 = YYCURSOR; + goto yy1020; + } + yyt1 = YYCURSOR; + goto yy1016; + } + } else { + if (yych <= '/') { + if (yych <= '-') { + yyt1 = YYCURSOR; + goto yy1021; + } + if (yych <= '.') { + yyt1 = YYCURSOR; + goto yy1022; + } + yyt1 = YYCURSOR; + goto yy1018; + } else { + if (yych <= '9') goto yy1025; + if (yych <= ':') { + yyt1 = YYCURSOR; + goto yy1026; + } + yyt1 = YYCURSOR; + goto yy1016; + } + } + } + } else { + if (yych <= 'f') { + if (yych <= 'Z') { + if (yych <= 'D') { + if (yych <= '@') { + yyt1 = YYCURSOR; + goto yy1027; + } + yyt1 = YYCURSOR; + goto yy1028; + } else { + if (yych <= 'E') { + yyt1 = YYCURSOR; + goto yy1029; + } + if (yych <= 'F') { + yyt1 = YYCURSOR; + goto yy1028; + } + yyt1 = YYCURSOR; + goto yy1030; + } + } else { + if (yych <= '`') { + if (yych == '_') { + yyt1 = YYCURSOR; + goto yy1021; + } + yyt1 = YYCURSOR; + goto yy1016; + } else { + if (yych == 'e') { + yyt1 = YYCURSOR; + goto yy1029; + } + yyt1 = YYCURSOR; + goto yy1028; + } + } + } else { + if (yych <= 0xDF) { + if (yych <= '~') { + if (yych <= 'z') { + yyt1 = YYCURSOR; + goto yy1030; + } + yyt1 = YYCURSOR; + goto yy1016; + } else { + if (yych <= 0x7F) { + yyt1 = YYCURSOR; + goto yy1018; + } + if (yych <= 0xC1) goto yy26; + yyt1 = YYCURSOR; + goto yy1031; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xE0) { + yyt1 = YYCURSOR; + goto yy1032; + } + if (yych <= 0xEF) { + yyt1 = YYCURSOR; + goto yy1033; + } + yyt1 = YYCURSOR; + goto yy1034; + } else { + if (yych <= 0xF3) { + yyt1 = YYCURSOR; + goto yy1035; + } + if (yych <= 0xF4) { + yyt1 = YYCURSOR; + goto yy1036; + } + goto yy26; + } + } + } + } +yy1004: + yych = *++YYCURSOR; + if (yych <= 'z') { + if (yych <= '9') { + if (yych == '%') { + yyt1 = YYCURSOR; + goto yy680; + } + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy1; + } else { + if (yych <= '@') { + if (yych <= ':') goto yy882; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 'Z') goto yy1; + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy1; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + yyt2 = YYCURSOR; + goto yy206; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy1005: + yych = *++YYCURSOR; + if (yych <= '@') { + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy1037; + if (yych <= ':') goto yy927; + goto yy1; + } else { + if (yych <= 'F') goto yy1037; + if (yych <= '`') goto yy1; + if (yych <= 'f') goto yy1037; + goto yy1; + } +yy1006: + yych = *++YYCURSOR; + if (yych == ':') goto yy980; + goto yy1; +yy1007: + yych = *++YYCURSOR; + if (yych <= 'z') { + if (yych <= '@') { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy830; + if (yych <= ':') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 'Z') { + if (yych <= 'F') goto yy830; + goto yy1; + } else { + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 'f') goto yy830; + goto yy1; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + yyt2 = YYCURSOR; + goto yy206; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy1008: + yych = *++YYCURSOR; + if (yych <= 'f') { + if (yych <= ':') { + if (yych <= '.') { + if (yych <= '-') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt1 = YYCURSOR; + goto yy297; + } else { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy1038; + goto yy959; + } + } else { + if (yych <= 'F') { + if (yych <= '@') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy958; + } else { + if (yych <= 'Z') goto yy1; + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy958; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + if (yych <= 'z') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + yyt2 = YYCURSOR; + goto yy206; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy1009: + yych = *++YYCURSOR; + if (yych <= 'f') { + if (yych <= ':') { + if (yych <= '.') { + if (yych <= '-') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt1 = YYCURSOR; + goto yy297; + } else { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy1039; + goto yy959; + } + } else { + if (yych <= 'F') { + if (yych <= '@') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy958; + } else { + if (yych <= 'Z') goto yy1; + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy958; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + if (yych <= 'z') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + yyt2 = YYCURSOR; + goto yy206; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy1010: + yych = *++YYCURSOR; + if (yych <= '`') { + if (yych <= '5') { + if (yych <= '.') { + if (yych <= '-') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt1 = YYCURSOR; + goto yy297; + } else { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '4') goto yy1039; + goto yy1040; + } + } else { + if (yych <= '@') { + if (yych <= '9') goto yy1038; + if (yych <= ':') goto yy959; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 'F') goto yy958; + if (yych <= 'Z') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } + } + } else { + if (yych <= 0xDF) { + if (yych <= 'z') { + if (yych <= 'f') goto yy958; + goto yy1; + } else { + if (yych <= 0x7F) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0xC1) goto yy1; + yyt2 = YYCURSOR; + goto yy205; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xE0) { + yyt2 = YYCURSOR; + goto yy206; + } + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy1011: + yych = *++YYCURSOR; + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy1041; + goto yy1; +yy1012: + yych = *++YYCURSOR; + if (yych == '8') goto yy1042; + goto yy1; +yy1013: + yych = *++YYCURSOR; + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy1043; + goto yy1; +yy1014: + yyaccept = 33; + yych = *(YYMARKER = ++YYCURSOR); + if (yych == '.') goto yy1044; + goto yy971; +yy1015: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '?') { + if (yych <= '+') { + if (yych == '%') goto yy80; + if (yych <= '*') goto yy4; + goto yy80; + } else { + if (yych <= '.') { + if (yych <= ',') goto yy4; + goto yy46; + } else { + if (yych <= '/') goto yy4; + if (yych <= '9') goto yy1045; + goto yy4; + } + } + } else { + if (yych <= '^') { + if (yych <= '@') goto yy96; + if (yych <= 'F') goto yy1045; + if (yych <= 'Z') goto yy46; + goto yy4; + } else { + if (yych <= '`') { + if (yych <= '_') goto yy46; + goto yy4; + } else { + if (yych <= 'f') goto yy1045; + if (yych <= 'z') goto yy46; + goto yy4; + } + } + } +yy1016: + ++YYCURSOR; +yy1017: + YYCURSOR = yyt1; +#line 236 "../../lnav/src/data_scanner_re.re" + { + CAPTURE(DT_CREDIT_CARD_NUMBER); + if (!this->is_credit_card(this->to_string_fragment(cap_all))) { + if (cap_all.length() > 16) { + cap_all.c_end = cap_all.c_begin + 4; + cap_inner.c_end = cap_inner.c_begin + 4; + } + this->ds_next_offset = cap_all.c_end; + token_out = DT_NUMBER; + } + return tokenize_result{token_out, cap_all, cap_inner, this->ds_input.data()}; + } +#line 34170 "../../lnav/src/data_scanner_re.cc" +yy1018: + yyaccept = 34; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= ',') { + if (yych <= '\r') { + if (yych <= 0x08) { + if (yych <= 0x00) goto yy1017; + goto yy4; + } else { + if (yych <= '\n') goto yy1017; + if (yych <= '\f') goto yy4; + goto yy1017; + } + } else { + if (yych <= 0x1F) { + if (yych == 0x1B) goto yy1017; + goto yy4; + } else { + if (yych == '$') goto yy4; + goto yy1017; + } + } + } else { + if (yych <= '`') { + if (yych <= 'Z') { + if (yych <= ':') goto yy4; + if (yych <= '?') goto yy1017; + goto yy4; + } else { + if (yych == '_') goto yy4; + goto yy1017; + } + } else { + if (yych <= 0x7F) { + if (yych <= 'z') goto yy4; + if (yych <= '~') goto yy1017; + goto yy4; + } else { + if (yych <= 0xC1) goto yy1017; + if (yych <= 0xF4) goto yy4; + goto yy1017; + } + } + } +yy1019: + yyaccept = 34; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[768+yych] & 32) { + goto yy103; + } + if (yych == '%') goto yy177; + goto yy1017; +yy1020: + yyaccept = 34; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '/') { + if (yych <= '*') { + if (yych == '%') goto yy81; + goto yy1017; + } else { + if (yych == ',') goto yy1017; + if (yych <= '.') goto yy81; + goto yy1017; + } + } else { + if (yych <= '^') { + if (yych <= '9') goto yy81; + if (yych <= '?') goto yy1017; + if (yych <= 'Z') goto yy81; + goto yy1017; + } else { + if (yych == '`') goto yy1017; + if (yych <= 'z') goto yy81; + goto yy1017; + } + } +yy1021: + yyaccept = 34; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[1024+yych] & 16) { + goto yy46; + } + if (yych <= '%') { + if (yych <= '\r') { + if (yych <= 0x08) { + if (yych <= 0x00) goto yy1017; + goto yy4; + } else { + if (yych <= '\n') goto yy1017; + if (yych <= '\f') goto yy4; + goto yy1017; + } + } else { + if (yych <= 0x1F) { + if (yych == 0x1B) goto yy1017; + goto yy4; + } else { + if (yych <= '#') goto yy1017; + if (yych <= '$') goto yy4; + goto yy80; + } + } + } else { + if (yych <= '?') { + if (yych <= '+') { + if (yych <= '*') goto yy1017; + goto yy80; + } else { + if (yych <= ',') goto yy1017; + if (yych <= ':') goto yy4; + goto yy1017; + } + } else { + if (yych <= 0x7F) { + if (yych <= '@') goto yy96; + if (yych <= '~') goto yy1017; + goto yy4; + } else { + if (yych <= 0xC1) goto yy1017; + if (yych <= 0xF4) goto yy4; + goto yy1017; + } + } + } +yy1022: + yyaccept = 34; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '.') { + if (yych <= 0x1B) { + if (yych <= '\n') { + if (yych <= 0x00) goto yy1017; + if (yych <= 0x08) goto yy4; + goto yy1017; + } else { + if (yych == '\r') goto yy1017; + if (yych <= 0x1A) goto yy4; + goto yy1017; + } + } else { + if (yych <= '%') { + if (yych <= 0x1F) goto yy4; + if (yych <= '#') goto yy1017; + if (yych <= '$') goto yy4; + goto yy80; + } else { + if (yych == '+') goto yy80; + if (yych <= ',') goto yy1017; + goto yy46; + } + } + } else { + if (yych <= '^') { + if (yych <= ':') { + if (yych <= '/') goto yy4; + if (yych <= '9') goto yy359; + goto yy4; + } else { + if (yych <= '?') goto yy1017; + if (yych <= '@') goto yy96; + if (yych <= 'Z') goto yy46; + goto yy1017; + } + } else { + if (yych <= '~') { + if (yych == '`') goto yy1017; + if (yych <= 'z') goto yy46; + goto yy1017; + } else { + if (yych <= 0x7F) goto yy4; + if (yych <= 0xC1) goto yy1017; + if (yych <= 0xF4) goto yy4; + goto yy1017; + } + } + } +yy1023: + yyaccept = 11; + yych = *(YYMARKER = ++YYCURSOR); +yy1024: + if (yybm[768+yych] & 32) { + goto yy103; + } + if (yych <= '?') { + if (yych <= '$') { + if (yych <= '\r') { + if (yych <= 0x08) { + if (yych <= 0x00) goto yy109; + goto yy3; + } else { + if (yych <= '\n') goto yy109; + if (yych <= '\f') goto yy3; + goto yy109; + } + } else { + if (yych <= 0x1B) { + if (yych <= 0x1A) goto yy3; + goto yy109; + } else { + if (yych <= 0x1F) goto yy3; + if (yych <= '#') goto yy109; + goto yy3; + } + } + } else { + if (yych <= '-') { + if (yych <= '*') { + if (yych <= '%') goto yy105; + goto yy109; + } else { + if (yych <= '+') goto yy80; + if (yych <= ',') goto yy109; + goto yy46; + } + } else { + if (yych <= '7') { + if (yych <= '.') goto yy266; + if (yych <= '/') goto yy3; + goto yy1023; + } else { + if (yych <= '9') goto yy1025; + if (yych <= ':') goto yy148; + goto yy109; + } + } + } + } else { + if (yych <= 'f') { + if (yych <= 'Z') { + if (yych <= 'D') { + if (yych <= '@') goto yy96; + goto yy196; + } else { + if (yych <= 'E') goto yy754; + if (yych <= 'F') goto yy196; + goto yy115; + } + } else { + if (yych <= '`') { + if (yych == '_') goto yy46; + goto yy109; + } else { + if (yych == 'e') goto yy754; + goto yy196; + } + } + } else { + if (yych <= 0xDF) { + if (yych <= '~') { + if (yych <= 'z') goto yy115; + goto yy109; + } else { + if (yych <= 0x7F) goto yy3; + if (yych <= 0xC1) goto yy109; + goto yy56; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xE0) goto yy57; + if (yych <= 0xEF) goto yy58; + goto yy59; + } else { + if (yych <= 0xF3) goto yy60; + if (yych <= 0xF4) goto yy61; + goto yy109; + } + } + } + } +yy1025: + yyaccept = 6; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[768+yych] & 32) { + goto yy103; + } + if (yych <= '@') { + if (yych <= '$') { + if (yych <= '\r') { + if (yych <= 0x08) { + if (yych <= 0x00) goto yy26; + goto yy3; + } else { + if (yych <= '\n') goto yy26; + if (yych <= '\f') goto yy3; + goto yy26; + } + } else { + if (yych <= 0x1B) { + if (yych <= 0x1A) goto yy3; + goto yy26; + } else { + if (yych <= 0x1F) goto yy3; + if (yych <= '#') goto yy26; + goto yy3; + } + } + } else { + if (yych <= '-') { + if (yych <= '*') { + if (yych <= '%') goto yy105; + goto yy26; + } else { + if (yych <= '+') goto yy80; + if (yych <= ',') goto yy26; + goto yy46; + } + } else { + if (yych <= '9') { + if (yych <= '.') goto yy266; + if (yych <= '/') goto yy3; + goto yy1025; + } else { + if (yych <= ':') goto yy148; + if (yych <= '?') goto yy26; + goto yy96; + } + } + } + } else { + if (yych <= 'f') { + if (yych <= '^') { + if (yych <= 'E') { + if (yych <= 'D') goto yy196; + goto yy754; + } else { + if (yych <= 'F') goto yy196; + if (yych <= 'Z') goto yy115; + goto yy26; + } + } else { + if (yych <= '`') { + if (yych <= '_') goto yy46; + goto yy26; + } else { + if (yych == 'e') goto yy754; + goto yy196; + } + } + } else { + if (yych <= 0xDF) { + if (yych <= '~') { + if (yych <= 'z') goto yy115; + goto yy26; + } else { + if (yych <= 0x7F) goto yy3; + if (yych <= 0xC1) goto yy26; + goto yy56; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xE0) goto yy57; + if (yych <= 0xEF) goto yy58; + goto yy59; + } else { + if (yych <= 0xF3) goto yy60; + if (yych <= 0xF4) goto yy61; + goto yy26; + } + } + } + } +yy1026: + yyaccept = 34; + yych = *(YYMARKER = ++YYCURSOR); + if (yych == '/') goto yy189; + if (yych == ':') goto yy152; + goto yy1017; +yy1027: + yyaccept = 34; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '/') { + if (yych <= 0x1A) { + if (yych <= '\n') { + if (yych <= 0x00) goto yy1017; + if (yych <= 0x08) goto yy4; + goto yy1017; + } else { + if (yych == '\r') goto yy1017; + goto yy4; + } + } else { + if (yych <= '#') { + if (yych <= 0x1B) goto yy1017; + if (yych <= 0x1F) goto yy4; + goto yy1017; + } else { + if (yych <= '$') goto yy4; + if (yych <= ',') goto yy1017; + if (yych <= '.') goto yy174; + goto yy4; + } + } + } else { + if (yych <= '_') { + if (yych <= '?') { + if (yych <= '9') goto yy174; + if (yych <= ':') goto yy4; + goto yy1017; + } else { + if (yych <= '@') goto yy4; + if (yych <= 'Z') goto yy174; + if (yych <= '^') goto yy1017; + goto yy4; + } + } else { + if (yych <= '~') { + if (yych <= '`') goto yy1017; + if (yych <= 'z') goto yy174; + goto yy1017; + } else { + if (yych <= 0x7F) goto yy4; + if (yych <= 0xC1) goto yy1017; + if (yych <= 0xF4) goto yy4; + goto yy1017; + } + } + } +yy1028: + yyaccept = 34; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '/') { + if (yych <= 0x1F) { + if (yych <= '\f') { + if (yych <= 0x00) goto yy1017; + if (yych <= 0x08) goto yy4; + if (yych <= '\n') goto yy1017; + goto yy4; + } else { + if (yych <= '\r') goto yy1017; + if (yych == 0x1B) goto yy1017; + goto yy4; + } + } else { + if (yych <= '*') { + if (yych <= '#') goto yy1017; + if (yych <= '$') goto yy4; + if (yych <= '%') goto yy80; + goto yy1017; + } else { + if (yych <= '+') goto yy80; + if (yych <= ',') goto yy1017; + if (yych <= '.') goto yy46; + goto yy4; + } + } + } else { + if (yych <= '_') { + if (yych <= '@') { + if (yych <= '9') goto yy196; + if (yych <= ':') goto yy148; + if (yych <= '?') goto yy1017; + goto yy96; + } else { + if (yych <= 'F') goto yy196; + if (yych <= 'Z') goto yy115; + if (yych <= '^') goto yy1017; + goto yy46; + } + } else { + if (yych <= '~') { + if (yych <= '`') goto yy1017; + if (yych <= 'f') goto yy196; + if (yych <= 'z') goto yy115; + goto yy1017; + } else { + if (yych <= 0x7F) goto yy4; + if (yych <= 0xC1) goto yy1017; + if (yych <= 0xF4) goto yy4; + goto yy1017; + } + } + } +yy1029: + yyaccept = 34; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '.') { + if (yych <= 0x1F) { + if (yych <= '\f') { + if (yych <= 0x00) goto yy1017; + if (yych <= 0x08) goto yy4; + if (yych <= '\n') goto yy1017; + goto yy4; + } else { + if (yych <= '\r') goto yy1017; + if (yych == 0x1B) goto yy1017; + goto yy4; + } + } else { + if (yych <= '*') { + if (yych <= '#') goto yy1017; + if (yych <= '$') goto yy4; + if (yych <= '%') goto yy80; + goto yy1017; + } else { + if (yych <= '+') goto yy194; + if (yych <= ',') goto yy1017; + if (yych <= '-') goto yy251; + goto yy46; + } + } + } else { + if (yych <= '^') { + if (yych <= '?') { + if (yych <= '/') goto yy4; + if (yych <= '9') goto yy196; + if (yych <= ':') goto yy148; + goto yy1017; + } else { + if (yych <= '@') goto yy96; + if (yych <= 'F') goto yy196; + if (yych <= 'Z') goto yy115; + goto yy1017; + } + } else { + if (yych <= 'z') { + if (yych <= '_') goto yy46; + if (yych <= '`') goto yy1017; + if (yych <= 'f') goto yy196; + goto yy115; + } else { + if (yych <= 0x7F) { + if (yych <= '~') goto yy1017; + goto yy4; + } else { + if (yych <= 0xC1) goto yy1017; + if (yych <= 0xF4) goto yy4; + goto yy1017; + } + } + } + } +yy1030: + yyaccept = 34; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[768+yych] & 64) { + goto yy115; + } + if (yych <= '+') { + if (yych <= 0x1A) { + if (yych <= '\n') { + if (yych <= 0x00) goto yy1017; + if (yych <= 0x08) goto yy4; + goto yy1017; + } else { + if (yych == '\r') goto yy1017; + goto yy4; + } + } else { + if (yych <= '#') { + if (yych <= 0x1B) goto yy1017; + if (yych <= 0x1F) goto yy4; + goto yy1017; + } else { + if (yych <= '$') goto yy4; + if (yych <= '%') goto yy80; + if (yych <= '*') goto yy1017; + goto yy80; + } + } + } else { + if (yych <= '@') { + if (yych <= '/') { + if (yych <= ',') goto yy1017; + if (yych <= '.') goto yy46; + goto yy4; + } else { + if (yych <= ':') goto yy148; + if (yych <= '?') goto yy1017; + goto yy96; + } + } else { + if (yych <= '~') { + if (yych == '_') goto yy46; + goto yy1017; + } else { + if (yych <= 0x7F) goto yy4; + if (yych <= 0xC1) goto yy1017; + if (yych <= 0xF4) goto yy4; + goto yy1017; + } + } + } +yy1031: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy1018; + goto yy1; +yy1032: + yych = *++YYCURSOR; + if (yych <= 0x9F) goto yy1; + if (yych <= 0xBF) goto yy1031; + goto yy1; +yy1033: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy1031; + goto yy1; +yy1034: + yych = *++YYCURSOR; + if (yych <= 0x8F) goto yy1; + if (yych <= 0xBF) goto yy1033; + goto yy1; +yy1035: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy1033; + goto yy1; +yy1036: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0x8F) goto yy1033; + goto yy1; +yy1037: + yyaccept = 21; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= ':') { + if (yych <= '-') { + if (yych <= ',') goto yy354; + goto yy466; + } else { + if (yych <= '/') goto yy354; + if (yych <= '9') goto yy955; + goto yy1046; + } + } else { + if (yych <= 'F') { + if (yych <= '@') goto yy354; + goto yy955; + } else { + if (yych <= '`') goto yy354; + if (yych <= 'f') goto yy955; + goto yy354; + } + } +yy1038: + yych = *++YYCURSOR; + if (yych <= 'f') { + if (yych <= ':') { + if (yych <= '.') { + if (yych <= '-') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt1 = YYCURSOR; + goto yy297; + } else { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy982; + goto yy959; + } + } else { + if (yych <= 'F') { + if (yych <= '@') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy982; + } else { + if (yych <= 'Z') goto yy1; + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy982; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + if (yych <= 'z') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + yyt2 = YYCURSOR; + goto yy206; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy1039: + yych = *++YYCURSOR; + if (yych <= 'f') { + if (yych <= ':') { + if (yych <= '.') { + if (yych <= '-') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt1 = YYCURSOR; + goto yy297; + } else { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy1047; + goto yy959; + } + } else { + if (yych <= 'F') { + if (yych <= '@') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy982; + } else { + if (yych <= 'Z') goto yy1; + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy982; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + if (yych <= 'z') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + yyt2 = YYCURSOR; + goto yy206; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy1040: + yych = *++YYCURSOR; + if (yych <= '`') { + if (yych <= '9') { + if (yych <= '.') { + if (yych <= '-') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt1 = YYCURSOR; + goto yy297; + } else { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '5') goto yy1047; + goto yy982; + } + } else { + if (yych <= '@') { + if (yych <= ':') goto yy959; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 'F') goto yy982; + if (yych <= 'Z') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } + } + } else { + if (yych <= 0xDF) { + if (yych <= 'z') { + if (yych <= 'f') goto yy982; + goto yy1; + } else { + if (yych <= 0x7F) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0xC1) goto yy1; + yyt2 = YYCURSOR; + goto yy205; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xE0) { + yyt2 = YYCURSOR; + goto yy206; + } + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy1041: + yych = *++YYCURSOR; + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy1048; + goto yy1; +yy1042: + yych = *++YYCURSOR; + if (yych == '6') goto yy1049; + goto yy1; +yy1043: + yych = *++YYCURSOR; + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy1050; + goto yy1; +yy1044: + yych = *++YYCURSOR; + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy1051; + goto yy1; +yy1045: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '/') { + if (yych <= '+') { + if (yych == '%') goto yy80; + if (yych <= '*') goto yy4; + goto yy80; + } else { + if (yych <= ',') goto yy4; + if (yych <= '-') goto yy1052; + if (yych <= '.') goto yy46; + goto yy4; + } + } else { + if (yych <= 'Z') { + if (yych <= '9') goto yy46; + if (yych <= '?') goto yy4; + if (yych <= '@') goto yy96; + goto yy46; + } else { + if (yych <= '_') { + if (yych <= '^') goto yy4; + goto yy46; + } else { + if (yych <= '`') goto yy4; + if (yych <= 'z') goto yy46; + goto yy4; + } + } + } +yy1046: + yych = *++YYCURSOR; + if (yych <= '@') { + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy1053; + if (yych <= ':') goto yy957; + goto yy1; + } else { + if (yych <= 'F') goto yy1053; + if (yych <= '`') goto yy1; + if (yych <= 'f') goto yy1053; + goto yy1; + } +yy1047: + yych = *++YYCURSOR; + if (yych <= 'f') { + if (yych <= ':') { + if (yych <= '.') { + if (yych <= '-') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt1 = YYCURSOR; + goto yy297; + } else { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy995; + goto yy959; + } + } else { + if (yych <= 'F') { + if (yych <= '@') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy995; + } else { + if (yych <= 'Z') goto yy1; + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy995; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + if (yych <= 'z') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + yyt2 = YYCURSOR; + goto yy206; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy1048: + yych = *++YYCURSOR; + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy1054; + goto yy1; +yy1049: + yych = *++YYCURSOR; + if (yych == ')') goto yy99; + goto yy1; +yy1050: + yych = *++YYCURSOR; + if (yych <= 0xDF) { + if (yych <= '9') { + if (yych <= '/') { + yyt1 = YYCURSOR; + goto yy1016; + } + goto yy1; + } else { + if (yych <= 0x7F) { + yyt1 = YYCURSOR; + goto yy1016; + } + if (yych <= 0xC1) goto yy1; + yyt1 = YYCURSOR; + goto yy1055; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xE0) { + yyt1 = YYCURSOR; + goto yy1056; + } + if (yych <= 0xEF) { + yyt1 = YYCURSOR; + goto yy1057; + } + yyt1 = YYCURSOR; + goto yy1058; + } else { + if (yych <= 0xF3) { + yyt1 = YYCURSOR; + goto yy1059; + } + if (yych <= 0xF4) { + yyt1 = YYCURSOR; + goto yy1060; + } + goto yy1; + } + } +yy1051: + yych = *++YYCURSOR; + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy1061; + goto yy1; +yy1052: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '?') { + if (yych <= '+') { + if (yych == '%') goto yy80; + if (yych <= '*') goto yy4; + goto yy80; + } else { + if (yych <= '.') { + if (yych <= ',') goto yy4; + goto yy46; + } else { + if (yych <= '/') goto yy4; + if (yych <= '9') goto yy1062; + goto yy4; + } + } + } else { + if (yych <= '^') { + if (yych <= '@') goto yy96; + if (yych <= 'F') goto yy1062; + if (yych <= 'Z') goto yy46; + goto yy4; + } else { + if (yych <= '`') { + if (yych <= '_') goto yy46; + goto yy4; + } else { + if (yych <= 'f') goto yy1062; + if (yych <= 'z') goto yy46; + goto yy4; + } + } + } +yy1053: + yych = *++YYCURSOR; + if (yych <= '@') { + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy1063; + if (yych <= ':') goto yy980; + goto yy1; + } else { + if (yych <= 'F') goto yy1063; + if (yych <= '`') goto yy1; + if (yych <= 'f') goto yy1063; + goto yy1; + } +yy1054: + yych = *++YYCURSOR; + if (yych <= '/') goto yy943; + if (yych <= '9') goto yy1064; + goto yy943; +yy1055: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy1016; + goto yy1; +yy1056: + yych = *++YYCURSOR; + if (yych <= 0x9F) goto yy1; + if (yych <= 0xBF) goto yy1055; + goto yy1; +yy1057: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy1055; + goto yy1; +yy1058: + yych = *++YYCURSOR; + if (yych <= 0x8F) goto yy1; + if (yych <= 0xBF) goto yy1057; + goto yy1; +yy1059: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0xBF) goto yy1057; + goto yy1; +yy1060: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy1; + if (yych <= 0x8F) goto yy1057; + goto yy1; +yy1061: + yych = *++YYCURSOR; + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy1065; + goto yy1; +yy1062: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '?') { + if (yych <= '+') { + if (yych == '%') goto yy80; + if (yych <= '*') goto yy4; + goto yy80; + } else { + if (yych <= '.') { + if (yych <= ',') goto yy4; + goto yy46; + } else { + if (yych <= '/') goto yy4; + if (yych <= '9') goto yy1066; + goto yy4; + } + } + } else { + if (yych <= '^') { + if (yych <= '@') goto yy96; + if (yych <= 'F') goto yy1066; + if (yych <= 'Z') goto yy46; + goto yy4; + } else { + if (yych <= '`') { + if (yych <= '_') goto yy46; + goto yy4; + } else { + if (yych <= 'f') goto yy1066; + if (yych <= 'z') goto yy46; + goto yy4; + } + } + } +yy1063: + yyaccept = 21; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= ':') { + if (yych <= '-') { + if (yych <= ',') goto yy354; + goto yy466; + } else { + if (yych <= '/') goto yy354; + if (yych <= '9') goto yy993; + goto yy1067; + } + } else { + if (yych <= 'F') { + if (yych <= '@') goto yy354; + goto yy993; + } else { + if (yych <= '`') goto yy354; + if (yych <= 'f') goto yy993; + goto yy354; + } + } +yy1064: + yych = *++YYCURSOR; + if (yych <= '/') goto yy943; + if (yych <= '9') goto yy1068; + goto yy943; +yy1065: + yych = *++YYCURSOR; + if (yych <= '/') goto yy971; + if (yych <= '9') goto yy1069; + goto yy971; +yy1066: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '?') { + if (yych <= '+') { + if (yych == '%') goto yy80; + if (yych <= '*') goto yy4; + goto yy80; + } else { + if (yych <= '.') { + if (yych <= ',') goto yy4; + goto yy46; + } else { + if (yych <= '/') goto yy4; + if (yych <= '9') goto yy1070; + goto yy4; + } + } + } else { + if (yych <= '^') { + if (yych <= '@') goto yy96; + if (yych <= 'F') goto yy1070; + if (yych <= 'Z') goto yy46; + goto yy4; + } else { + if (yych <= '`') { + if (yych <= '_') goto yy46; + goto yy4; + } else { + if (yych <= 'f') goto yy1070; + if (yych <= 'z') goto yy46; + goto yy4; + } + } + } +yy1067: + yych = *++YYCURSOR; + if (yych <= '@') { + if (yych <= '/') goto yy1; + if (yych <= '9') goto yy1071; + if (yych <= ':') goto yy830; + goto yy1; + } else { + if (yych <= 'F') goto yy1071; + if (yych <= '`') goto yy1; + if (yych <= 'f') goto yy1071; + goto yy1; + } +yy1068: + yych = *++YYCURSOR; + if (yych <= '/') goto yy943; + if (yych <= '9') goto yy1072; + goto yy943; +yy1069: + yych = *++YYCURSOR; + if (yych <= '/') goto yy971; + if (yych <= '9') goto yy1073; + goto yy971; +yy1070: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '?') { + if (yych <= '+') { + if (yych == '%') goto yy80; + if (yych <= '*') goto yy4; + goto yy80; + } else { + if (yych <= '.') { + if (yych <= ',') goto yy4; + goto yy46; + } else { + if (yych <= '/') goto yy4; + if (yych <= '9') goto yy1074; + goto yy4; + } + } + } else { + if (yych <= '^') { + if (yych <= '@') goto yy96; + if (yych <= 'F') goto yy1074; + if (yych <= 'Z') goto yy46; + goto yy4; + } else { + if (yych <= '`') { + if (yych <= '_') goto yy46; + goto yy4; + } else { + if (yych <= 'f') goto yy1074; + if (yych <= 'z') goto yy46; + goto yy4; + } + } + } +yy1071: + yych = *++YYCURSOR; + if (yych <= 'z') { + if (yych <= '@') { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy1075; + if (yych <= ':') goto yy1; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 'Z') { + if (yych <= 'F') goto yy1075; + goto yy1; + } else { + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 'f') goto yy1075; + goto yy1; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= 0xC1) goto yy1; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + yyt2 = YYCURSOR; + goto yy206; + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy1; + } + } + } +yy1072: + ++YYCURSOR; + goto yy943; +yy1073: + yych = *++YYCURSOR; + if (yych <= '/') goto yy971; + if (yych <= '9') goto yy1076; + goto yy971; +yy1074: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '/') { + if (yych <= '+') { + if (yych == '%') goto yy80; + if (yych <= '*') goto yy4; + goto yy80; + } else { + if (yych <= ',') goto yy4; + if (yych <= '-') goto yy1077; + if (yych <= '.') goto yy46; + goto yy4; + } + } else { + if (yych <= 'Z') { + if (yych <= '9') goto yy46; + if (yych <= '?') goto yy4; + if (yych <= '@') goto yy96; + goto yy46; + } else { + if (yych <= '_') { + if (yych <= '^') goto yy4; + goto yy46; + } else { + if (yych <= '`') goto yy4; + if (yych <= 'z') goto yy46; + goto yy4; + } + } + } +yy1075: + yyaccept = 21; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= 'f') { + if (yych <= ':') { + if (yych <= '-') { + if (yych <= ',') { + yyt2 = YYCURSOR; + goto yy198; + } + yyt2 = YYCURSOR; + goto yy1078; + } else { + if (yych <= '/') { + yyt2 = YYCURSOR; + goto yy198; + } + if (yych <= '9') goto yy1007; + goto yy466; + } + } else { + if (yych <= 'F') { + if (yych <= '@') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy1007; + } else { + if (yych <= 'Z') goto yy354; + if (yych <= '`') { + yyt2 = YYCURSOR; + goto yy198; + } + goto yy1007; + } + } + } else { + if (yych <= 0xE0) { + if (yych <= 0x7F) { + if (yych <= 'z') goto yy354; + yyt2 = YYCURSOR; + goto yy198; + } else { + if (yych <= 0xC1) goto yy354; + if (yych <= 0xDF) { + yyt2 = YYCURSOR; + goto yy205; + } + yyt2 = YYCURSOR; + goto yy206; + } + } else { + if (yych <= 0xF0) { + if (yych <= 0xEF) { + yyt2 = YYCURSOR; + goto yy207; + } + yyt2 = YYCURSOR; + goto yy208; + } else { + if (yych <= 0xF3) { + yyt2 = YYCURSOR; + goto yy209; + } + if (yych <= 0xF4) { + yyt2 = YYCURSOR; + goto yy210; + } + goto yy354; + } + } + } +yy1076: + ++YYCURSOR; + goto yy971; +yy1077: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '?') { + if (yych <= '+') { + if (yych == '%') goto yy80; + if (yych <= '*') goto yy4; + goto yy80; + } else { + if (yych <= '.') { + if (yych <= ',') goto yy4; + goto yy46; + } else { + if (yych <= '/') goto yy4; + if (yych <= '9') goto yy1079; + goto yy4; + } + } + } else { + if (yych <= '^') { + if (yych <= '@') goto yy96; + if (yych <= 'F') goto yy1079; + if (yych <= 'Z') goto yy46; + goto yy4; + } else { + if (yych <= '`') { + if (yych <= '_') goto yy46; + goto yy4; + } else { + if (yych <= 'f') goto yy1079; + if (yych <= 'z') goto yy46; + goto yy4; + } + } + } +yy1078: + yyaccept = 17; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '@') { + if (yych <= '/') goto yy199; + if (yych <= '9') goto yy542; + goto yy199; + } else { + if (yych <= 'F') goto yy542; + if (yych <= '`') goto yy199; + if (yych <= 'f') goto yy542; + goto yy199; + } +yy1079: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '?') { + if (yych <= '+') { + if (yych == '%') goto yy80; + if (yych <= '*') goto yy4; + goto yy80; + } else { + if (yych <= '.') { + if (yych <= ',') goto yy4; + goto yy46; + } else { + if (yych <= '/') goto yy4; + if (yych >= ':') goto yy4; + } + } + } else { + if (yych <= '^') { + if (yych <= '@') goto yy96; + if (yych <= 'F') goto yy1080; + if (yych <= 'Z') goto yy46; + goto yy4; + } else { + if (yych <= '`') { + if (yych <= '_') goto yy46; + goto yy4; + } else { + if (yych <= 'f') goto yy1080; + if (yych <= 'z') goto yy46; + goto yy4; + } + } + } +yy1080: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '?') { + if (yych <= '+') { + if (yych == '%') goto yy80; + if (yych <= '*') goto yy4; + goto yy80; + } else { + if (yych <= '.') { + if (yych <= ',') goto yy4; + goto yy46; + } else { + if (yych <= '/') goto yy4; + if (yych >= ':') goto yy4; + } + } + } else { + if (yych <= '^') { + if (yych <= '@') goto yy96; + if (yych <= 'F') goto yy1081; + if (yych <= 'Z') goto yy46; + goto yy4; + } else { + if (yych <= '`') { + if (yych <= '_') goto yy46; + goto yy4; + } else { + if (yych <= 'f') goto yy1081; + if (yych <= 'z') goto yy46; + goto yy4; + } + } + } +yy1081: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '?') { + if (yych <= '+') { + if (yych == '%') goto yy80; + if (yych <= '*') goto yy4; + goto yy80; + } else { + if (yych <= '.') { + if (yych <= ',') goto yy4; + goto yy46; + } else { + if (yych <= '/') goto yy4; + if (yych >= ':') goto yy4; + } + } + } else { + if (yych <= '^') { + if (yych <= '@') goto yy96; + if (yych <= 'F') goto yy1082; + if (yych <= 'Z') goto yy46; + goto yy4; + } else { + if (yych <= '`') { + if (yych <= '_') goto yy46; + goto yy4; + } else { + if (yych <= 'f') goto yy1082; + if (yych <= 'z') goto yy46; + goto yy4; + } + } + } +yy1082: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '?') { + if (yych <= '+') { + if (yych == '%') goto yy80; + if (yych <= '*') goto yy4; + goto yy80; + } else { + if (yych <= '.') { + if (yych <= ',') goto yy4; + goto yy46; + } else { + if (yych <= '/') goto yy4; + if (yych >= ':') goto yy4; + } + } + } else { + if (yych <= '^') { + if (yych <= '@') goto yy96; + if (yych <= 'F') goto yy1083; + if (yych <= 'Z') goto yy46; + goto yy4; + } else { + if (yych <= '`') { + if (yych <= '_') goto yy46; + goto yy4; + } else { + if (yych <= 'f') goto yy1083; + if (yych <= 'z') goto yy46; + goto yy4; + } + } + } +yy1083: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '?') { + if (yych <= '+') { + if (yych == '%') goto yy80; + if (yych <= '*') goto yy4; + goto yy80; + } else { + if (yych <= '.') { + if (yych <= ',') goto yy4; + goto yy46; + } else { + if (yych <= '/') goto yy4; + if (yych >= ':') goto yy4; + } + } + } else { + if (yych <= '^') { + if (yych <= '@') goto yy96; + if (yych <= 'F') goto yy1084; + if (yych <= 'Z') goto yy46; + goto yy4; + } else { + if (yych <= '`') { + if (yych <= '_') goto yy46; + goto yy4; + } else { + if (yych <= 'f') goto yy1084; + if (yych <= 'z') goto yy46; + goto yy4; + } + } + } +yy1084: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '?') { + if (yych <= '+') { + if (yych == '%') goto yy80; + if (yych <= '*') goto yy4; + goto yy80; + } else { + if (yych <= '.') { + if (yych <= ',') goto yy4; + goto yy46; + } else { + if (yych <= '/') goto yy4; + if (yych >= ':') goto yy4; + } + } + } else { + if (yych <= '^') { + if (yych <= '@') goto yy96; + if (yych <= 'F') goto yy1085; + if (yych <= 'Z') goto yy46; + goto yy4; + } else { + if (yych <= '`') { + if (yych <= '_') goto yy46; + goto yy4; + } else { + if (yych <= 'f') goto yy1085; + if (yych <= 'z') goto yy46; + goto yy4; + } + } + } +yy1085: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '?') { + if (yych <= '+') { + if (yych == '%') goto yy80; + if (yych <= '*') goto yy4; + goto yy80; + } else { + if (yych <= '.') { + if (yych <= ',') goto yy4; + goto yy46; + } else { + if (yych <= '/') goto yy4; + if (yych >= ':') goto yy4; + } + } + } else { + if (yych <= '^') { + if (yych <= '@') goto yy96; + if (yych <= 'F') goto yy1086; + if (yych <= 'Z') goto yy46; + goto yy4; + } else { + if (yych <= '`') { + if (yych <= '_') goto yy46; + goto yy4; + } else { + if (yych <= 'f') goto yy1086; + if (yych <= 'z') goto yy46; + goto yy4; + } + } + } +yy1086: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '?') { + if (yych <= '+') { + if (yych == '%') goto yy80; + if (yych <= '*') goto yy4; + goto yy80; + } else { + if (yych <= '.') { + if (yych <= ',') goto yy4; + goto yy46; + } else { + if (yych <= '/') goto yy4; + if (yych >= ':') goto yy4; + } + } + } else { + if (yych <= '^') { + if (yych <= '@') goto yy96; + if (yych <= 'F') goto yy1087; + if (yych <= 'Z') goto yy46; + goto yy4; + } else { + if (yych <= '`') { + if (yych <= '_') goto yy46; + goto yy4; + } else { + if (yych <= 'f') goto yy1087; + if (yych <= 'z') goto yy46; + goto yy4; + } + } + } +yy1087: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '?') { + if (yych <= '+') { + if (yych == '%') goto yy80; + if (yych <= '*') goto yy4; + goto yy80; + } else { + if (yych <= '.') { + if (yych <= ',') goto yy4; + goto yy46; + } else { + if (yych <= '/') goto yy4; + if (yych >= ':') goto yy4; + } + } + } else { + if (yych <= '^') { + if (yych <= '@') goto yy96; + if (yych <= 'F') goto yy1088; + if (yych <= 'Z') goto yy46; + goto yy4; + } else { + if (yych <= '`') { + if (yych <= '_') goto yy46; + goto yy4; + } else { + if (yych <= 'f') goto yy1088; + if (yych <= 'z') goto yy46; + goto yy4; + } + } + } +yy1088: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '?') { + if (yych <= '+') { + if (yych == '%') goto yy80; + if (yych <= '*') goto yy4; + goto yy80; + } else { + if (yych <= '.') { + if (yych <= ',') goto yy4; + goto yy46; + } else { + if (yych <= '/') goto yy4; + if (yych >= ':') goto yy4; + } + } + } else { + if (yych <= '^') { + if (yych <= '@') goto yy96; + if (yych <= 'F') goto yy1089; + if (yych <= 'Z') goto yy46; + goto yy4; + } else { + if (yych <= '`') { + if (yych <= '_') goto yy46; + goto yy4; + } else { + if (yych <= 'f') goto yy1089; + if (yych <= 'z') goto yy46; + goto yy4; + } + } + } +yy1089: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '?') { + if (yych <= '+') { + if (yych == '%') goto yy80; + if (yych <= '*') goto yy4; + goto yy80; + } else { + if (yych <= '.') { + if (yych <= ',') goto yy4; + goto yy46; + } else { + if (yych <= '/') goto yy4; + if (yych >= ':') goto yy4; + } + } + } else { + if (yych <= '^') { + if (yych <= '@') goto yy96; + if (yych <= 'F') goto yy1090; + if (yych <= 'Z') goto yy46; + goto yy4; + } else { + if (yych <= '`') { + if (yych <= '_') goto yy46; + goto yy4; + } else { + if (yych <= 'f') goto yy1090; + if (yych <= 'z') goto yy46; + goto yy4; + } + } + } +yy1090: + yyaccept = 35; + yych = *(YYMARKER = ++YYCURSOR); + if (yybm[1024+yych] & 16) { + goto yy46; + } + if (yych <= '%') { + if (yych <= '\r') { + if (yych <= 0x08) { + if (yych >= 0x01) goto yy4; + } else { + if (yych <= '\n') goto yy1091; + if (yych <= '\f') goto yy4; + } + } else { + if (yych <= 0x1F) { + if (yych != 0x1B) goto yy4; + } else { + if (yych <= '#') goto yy1091; + if (yych <= '$') goto yy4; + goto yy80; + } + } + } else { + if (yych <= '?') { + if (yych <= '+') { + if (yych >= '+') goto yy80; + } else { + if (yych <= ',') goto yy1091; + if (yych <= ':') goto yy4; + } + } else { + if (yych <= 0x7F) { + if (yych <= '@') goto yy96; + if (yych >= 0x7F) goto yy4; + } else { + if (yych <= 0xC1) goto yy1091; + if (yych <= 0xF4) goto yy4; + } + } + } +yy1091: +#line 234 "../../lnav/src/data_scanner_re.re" + { RET(DT_UUID); } +#line 36093 "../../lnav/src/data_scanner_re.cc" +} +#line 276 "../../lnav/src/data_scanner_re.re" + +} diff --git a/src/data_scanner_re.re b/src/data_scanner_re.re new file mode 100644 index 0000000..904aa9f --- /dev/null +++ b/src/data_scanner_re.re @@ -0,0 +1,277 @@ +/** + * Copyright (c) 2015, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include "base/date_time_scanner.hh" +#include "config.h" +#include "data_scanner.hh" + +nonstd::optional data_scanner::tokenize2() +{ + data_token_t token_out = DT_INVALID; + capture_t cap_all; + capture_t cap_inner; +# define YYCTYPE unsigned char +# define CAPTURE(tok) { \ + if (YYCURSOR.val == EMPTY) { \ + this->ds_next_offset = this->ds_input.length(); \ + } else { \ + this->ds_next_offset = YYCURSOR.val - this->ds_input.udata(); \ + } \ + cap_all.c_end = this->ds_next_offset; \ + cap_inner.c_end = this->ds_next_offset; \ + token_out = tok; \ + } + +# define RET(tok) { \ + CAPTURE(tok); \ + return tokenize_result{token_out, cap_all, cap_inner, this->ds_input.data()}; \ + } + static const unsigned char *EMPTY = (const unsigned char *) ""; + + struct _YYCURSOR { + YYCTYPE operator*() const { + if (this->val < this->lim) { + return *val; + } + return '\0'; + } + + operator const YYCTYPE *() const { + if (this->val < this->lim) { + return this->val; + } + return EMPTY; + } + + const YYCTYPE *operator=(const YYCTYPE *rhs) { + this->val = rhs; + return rhs; + } + + const YYCTYPE *operator+(int rhs) { + return this->val + rhs; + } + + const _YYCURSOR *operator-=(int rhs) { + this->val -= rhs; + return this; + } + + _YYCURSOR& operator++() { + this->val += 1; + return *this; + } + + const YYCTYPE *val{nullptr}; + const YYCTYPE *lim{nullptr}; + } YYCURSOR; + YYCURSOR = (const unsigned char *) this->ds_input.udata() + this->ds_next_offset; + _YYCURSOR yyt1; + _YYCURSOR yyt2; + _YYCURSOR yyt3; + _YYCURSOR yyt4; + const YYCTYPE *YYLIMIT = (const unsigned char *) this->ds_input.end(); + const YYCTYPE *YYMARKER = YYCURSOR; + + YYCURSOR.lim = YYLIMIT; + + cap_all.c_begin = this->ds_next_offset; + cap_all.c_end = this->ds_next_offset; + cap_inner.c_begin = this->ds_next_offset; + cap_inner.c_end = this->ds_next_offset; + + /*!re2c + re2c:yyfill:enable = 0; + re2c:flags:tags = 1; + + SPACE = [ \t\r]; + ALPHA = [a-zA-Z]; + ESC = "\x1b"; + NUM = [0-9]; + ALPHANUM = [a-zA-Z0-9_]; + EOF = "\x00"; + IPV4SEG = ("25"[0-5]|("2"[0-4]|"1"{0,1}[0-9]){0,1}[0-9]); + IPV4ADDR = (IPV4SEG"."){3,3}IPV4SEG; + IPV6SEG = [0-9a-fA-F]{1,4}; + IPV6ADDR = ( + (IPV6SEG":"){7,7}IPV6SEG| + (IPV6SEG":"){1,7}":"| + (IPV6SEG":"){1,6}":"IPV6SEG| + (IPV6SEG":"){1,5}(":"IPV6SEG){1,2}| + (IPV6SEG":"){1,4}(":"IPV6SEG){1,3}| + (IPV6SEG":"){1,3}(":"IPV6SEG){1,4}| + (IPV6SEG":"){1,2}(":"IPV6SEG){1,5}| + IPV6SEG":"((":"IPV6SEG){1,6})| + ":"((":"IPV6SEG){1,7}|":")| + [a-fA-F0-9]{4}":"(":"IPV6SEG){0,4}"%"[0-9a-zA-Z]{1,}| + "::"('ffff'(":0"{1,4}){0,1}":"){0,1}IPV4ADDR| + (IPV6SEG":"){1,4}":"IPV4ADDR + ); + + EOF { return nonstd::nullopt; } + + ("u"|"r")?'"'('\\'.|[^\x00\x1b"\\]|'""')*'"' { + CAPTURE(DT_QUOTED_STRING); + switch (this->ds_input[cap_inner.c_begin]) { + case 'u': + case 'r': + cap_inner.c_begin += 1; + break; + } + cap_inner.c_begin += 1; + cap_inner.c_end -= 1; + return tokenize_result{token_out, cap_all, cap_inner, this->ds_input.data()}; + } + [a-qstv-zA-QSTV-Z]"'" { + CAPTURE(DT_WORD); + } + ("u"|"r")?"'"('\\'.|"''"|[^\x00\x1b'\\])*"'"/[^sS] { + CAPTURE(DT_QUOTED_STRING); + switch (this->ds_input[cap_inner.c_begin]) { + case 'u': + case 'r': + cap_inner.c_begin += 1; + break; + } + cap_inner.c_begin += 1; + cap_inner.c_end -= 1; + return tokenize_result{token_out, cap_all, cap_inner, this->ds_input.data()}; + } + [a-zA-Z0-9]+":/""/"?[^\x00\x1b\r\n\t '"[\](){}]+[/a-zA-Z0-9\-=&?%] { RET(DT_URL); } + ("/"|"./"|"../"|[A-Z]":\\"|"\\\\")("Program Files"(" (x86)")?)?[a-zA-Z0-9_\.\-\~/\\!@#$%^&*()]* { RET(DT_PATH); } + (SPACE|NUM)NUM":"NUM{2}/[^:] { RET(DT_TIME); } + (SPACE|NUM)NUM?":"NUM{2}":"NUM{2}("."NUM{3,6})?/[^:] { RET(DT_TIME); } + [0-9a-fA-F][0-9a-fA-F]((":"|"-")[0-9a-fA-F][0-9a-fA-F])+ { + if ((YYCURSOR.val - (this->ds_input.udata() + this->ds_next_offset)) == 17) { + RET(DT_MAC_ADDRESS); + } else { + RET(DT_HEX_DUMP); + } + } + (NUM{4}"/"NUM{1,2}"/"NUM{1,2}|NUM{4}"-"NUM{1,2}"-"NUM{1,2}|NUM{2}"/"ALPHA{3}"/"NUM{4})("T"|" ")NUM{2}":"NUM{2}(":"NUM{2}("."NUM{3,6})?)? { + RET(DT_DATE_TIME); + } + ALPHA{3}(" "NUM|" "NUM{2})" "NUM{2}":"NUM{2}(":"NUM{2}("."NUM{3,6})?)? { + RET(DT_DATE_TIME); + } + (NUM{4}"/"NUM{1,2}"/"NUM{1,2}|NUM{4}"-"NUM{1,2}"-"NUM{1,2}|NUM{2}"/"ALPHA{3}"/"NUM{4}) { + RET(DT_DATE); + } + IPV6ADDR/[^:a-zA-Z0-9] { RET(DT_IPV6_ADDRESS); } + + "]+))?|SPACE*('"'(('\\'.|[^\x00"\\])+)'"'|"'"(('\\'.|[^\x00'\\])+)"'"))*SPACE*">" { + RET(DT_XML_DECL_TAG); + } + + "<""?"?[a-zA-Z0-9_:\-]+SPACE*([a-zA-Z0-9_:\-]+(SPACE*'='SPACE*('"'(('\\'.|[^\x00"\\])+)'"'|"'"(('\\'.|[^\x00'\\])+)"'"|[^\x00>]+))?)*SPACE*("/"|"?")">" { + RET(DT_XML_EMPTY_TAG); + } + + "<"[a-zA-Z0-9_:\-]+SPACE*([a-zA-Z0-9_:\-]+(SPACE*"="SPACE*('"'(('\\'.|[^\x00"\\])+)'"'|"'"(('\\'.|[^\x00'\\])+)"'"|[^\x00>]+))?)*SPACE*">" { + RET(DT_XML_OPEN_TAG); + } + + "" { + RET(DT_XML_CLOSE_TAG); + } + + "\n"[A-Z][A-Z _\-0-9]+"\n" { + RET(DT_H1); + } + + ESC"["[0-9=;?]*[a-zA-Z] { + RET(DT_CSI); + } + + ":" { RET(DT_COLON); } + "=" { RET(DT_EQUALS); } + "," { RET(DT_COMMA); } + ";" { RET(DT_SEMI); } + "()" | "{}" | "[]" { RET(DT_EMPTY_CONTAINER); } + "{" { RET(DT_LCURLY); } + "}" { RET(DT_RCURLY); } + "[" { RET(DT_LSQUARE); } + "]" { RET(DT_RSQUARE); } + "(" { RET(DT_LPAREN); } + ")" { RET(DT_RPAREN); } + "<" { RET(DT_LANGLE); } + ">" { RET(DT_RANGLE); } + + IPV4ADDR/[^0-9] { + RET(DT_IPV4_ADDRESS); + } + + [0-9a-fA-F]{8}("-"[0-9a-fA-F]{4}){3}"-"[0-9a-fA-F]{12} { RET(DT_UUID); } + + (NUM{4}" "NUM{4}" "NUM{4}" "NUM{4}|NUM{16})/[^0-9] { + CAPTURE(DT_CREDIT_CARD_NUMBER); + if (!this->is_credit_card(this->to_string_fragment(cap_all))) { + if (cap_all.length() > 16) { + cap_all.c_end = cap_all.c_begin + 4; + cap_inner.c_end = cap_inner.c_begin + 4; + } + this->ds_next_offset = cap_all.c_end; + token_out = DT_NUMBER; + } + return tokenize_result{token_out, cap_all, cap_inner, this->ds_input.data()}; + } + + [0-9]"."[0-9]+'e'[\-\+][0-9]+ { RET(DT_NUMBER); } + + [0-9]+("."[0-9]+[a-zA-Z0-9_]*){2,}("-"[a-zA-Z0-9_]+)?|[0-9]+("."[0-9]+[a-zA-Z0-9_]*)+"-"[a-zA-Z0-9_]+ { + RET(DT_VERSION_NUMBER); + } + + "-"?"0"[0-7]+ { RET(DT_OCTAL_NUMBER); } + "-"?[0-9]+("."[0-9]+)?[ ]*"%" { RET(DT_PERCENTAGE); } + "-"?[0-9]+("."[0-9]+)?([eE][\-+][0-9]+)? { RET(DT_NUMBER); } + "-"?("0x"|[0-9])[0-9a-fA-F]+ { RET(DT_HEX_NUMBER); } + + [a-zA-Z0-9\._%+-]+"@"[a-zA-Z0-9\.-]+"."[a-zA-Z]+ { RET(DT_EMAIL); } + + "true"|"True"|"TRUE"|"false"|"False"|"FALSE"|"None"|"null"|"NULL"/([\r\n\t \(\)!\*:;'\"\?,]|[\.\!,\?]SPACE|EOF) { RET(DT_CONSTANT); } + + ("re-")?[a-zA-Z][a-z']+/([\r\n\t \(\)!\*:;'\"\?,]|[\.\!,\?]SPACE|EOF) { RET(DT_WORD); } + + [^\x00\x1b"; \t\r\n:=,\(\)\{\}\[\]\+#!%\^&\*'\?<>\~`\|\.\\][^\x00\x1b"; \t\r\n:=,\(\)\{\}\[\]\+#!%\^&\*'\?<>\~`\|\\]*("::"[^\x00\x1b"; \r\n\t:=,\(\)\{\}\[\]\+#!%\^&\*'\?<>\~`\|\\]+)* { + RET(DT_SYMBOL); + } + + ("\r"?"\n"|"\\n") { RET(DT_LINE); } + SPACE+ { RET(DT_WHITE); } + "." { RET(DT_DOT); } + "\\". { RET(DT_ESCAPED_CHAR); } + . { RET(DT_GARBAGE); } + + */ +} diff --git a/src/db_sub_source.cc b/src/db_sub_source.cc new file mode 100644 index 0000000..795bda8 --- /dev/null +++ b/src/db_sub_source.cc @@ -0,0 +1,457 @@ +/** + * Copyright (c) 2014, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include "db_sub_source.hh" + +#include "base/date_time_scanner.hh" +#include "base/itertools.hh" +#include "base/time_util.hh" +#include "config.h" +#include "scn/scn.h" +#include "yajlpp/json_ptr.hh" + +const char db_label_source::NULL_STR[] = ""; + +constexpr size_t MAX_COLUMN_WIDTH = 120; +constexpr size_t MAX_JSON_WIDTH = 16 * 1024; + +void +db_label_source::text_value_for_line(textview_curses& tc, + int row, + std::string& label_out, + text_sub_source::line_flags_t flags) +{ + /* + * start_value is the result rowid, each bucket type is a column value + * label_out should be the raw text output. + */ + + label_out.clear(); + if (row >= (int) this->dls_rows.size()) { + return; + } + for (int lpc = 0; lpc < (int) this->dls_rows[row].size(); lpc++) { + auto actual_col_size + = std::min(MAX_COLUMN_WIDTH, this->dls_headers[lpc].hm_column_size); + auto cell_str = scrub_ws(this->dls_rows[row][lpc]); + + truncate_to(cell_str, MAX_COLUMN_WIDTH); + + auto cell_length + = utf8_string_length(cell_str).unwrapOr(actual_col_size); + auto padding = actual_col_size - cell_length; + this->dls_cell_width[lpc] = cell_str.length() + padding; + if (this->dls_headers[lpc].hm_column_type != SQLITE3_TEXT) { + label_out.append(padding, ' '); + } + label_out.append(cell_str); + if (this->dls_headers[lpc].hm_column_type == SQLITE3_TEXT) { + label_out.append(padding, ' '); + } + label_out.append(1, ' '); + } +} + +void +db_label_source::text_attrs_for_line(textview_curses& tc, + int row, + string_attrs_t& sa) +{ + struct line_range lr(0, 0); + const struct line_range lr2(0, -1); + + if (row >= (int) this->dls_rows.size()) { + return; + } + for (size_t lpc = 0; lpc < this->dls_headers.size() - 1; lpc++) { + if (row % 2 == 0) { + sa.emplace_back(lr2, VC_STYLE.value(text_attrs{A_BOLD})); + } + lr.lr_start += this->dls_cell_width[lpc]; + lr.lr_end = lr.lr_start + 1; + sa.emplace_back(lr, VC_GRAPHIC.value(ACS_VLINE)); + lr.lr_start += 1; + } + + int left = 0; + for (size_t lpc = 0; lpc < this->dls_headers.size(); lpc++) { + auto row_view = scn::string_view{this->dls_rows[row][lpc]}; + const auto& hm = this->dls_headers[lpc]; + + if (hm.hm_graphable) { + auto num_scan_res = scn::scan_value(row_view); + + if (num_scan_res) { + this->dls_chart.chart_attrs_for_value( + tc, left, hm.hm_name, num_scan_res.value(), sa); + } + } + if (row_view.length() > 2 && row_view.length() < MAX_JSON_WIDTH + && ((row_view.front() == '{' && row_view.back() == '}') + || (row_view.front() == '[' && row_view.back() == ']'))) + { + json_ptr_walk jpw; + + if (jpw.parse(row_view.data(), row_view.length()) == yajl_status_ok + && jpw.complete_parse() == yajl_status_ok) + { + for (const auto& jpw_value : jpw.jpw_values) { + if (jpw_value.wt_type != yajl_t_number) { + continue; + } + + auto num_scan_res + = scn::scan_value(jpw_value.wt_value); + + if (num_scan_res) { + this->dls_chart.chart_attrs_for_value( + tc, + left, + jpw_value.wt_ptr, + num_scan_res.value(), + sa); + } + } + } + } + } +} + +void +db_label_source::push_header(const std::string& colstr, + int type, + bool graphable) +{ + this->dls_headers.emplace_back(colstr); + this->dls_cell_width.push_back(0); + + header_meta& hm = this->dls_headers.back(); + + hm.hm_column_size = utf8_string_length(colstr).unwrapOr(colstr.length()); + hm.hm_column_type = type; + hm.hm_graphable = graphable; + if (colstr == "log_time") { + this->dls_time_column_index = this->dls_headers.size() - 1; + } +} + +void +db_label_source::push_column(const scoped_value_t& sv) +{ + auto& vc = view_colors::singleton(); + int index = this->dls_rows.back().size(); + auto& hm = this->dls_headers[index]; + + auto col_sf = sv.match( + [](const std::string& str) { return string_fragment::from_str(str); }, + [this](const string_fragment& sf) { + return sf.to_owned(*this->dls_allocator); + }, + [this](int64_t i) { + fmt::memory_buffer buf; + + fmt::format_to(std::back_inserter(buf), FMT_STRING("{}"), i); + return string_fragment::from_memory_buffer(buf).to_owned( + *this->dls_allocator); + }, + [this](double d) { + fmt::memory_buffer buf; + + fmt::format_to(std::back_inserter(buf), FMT_STRING("{}"), d); + return string_fragment::from_memory_buffer(buf).to_owned( + *this->dls_allocator); + }, + [](null_value_t) { return string_fragment::from_const(NULL_STR); }); + + if (index == this->dls_time_column_index) { + date_time_scanner dts; + struct timeval tv; + + if (!dts.convert_to_timeval( + col_sf.data(), col_sf.length(), nullptr, tv)) + { + tv.tv_sec = -1; + tv.tv_usec = -1; + } + if (!this->dls_time_column.empty() && tv < this->dls_time_column.back()) + { + this->dls_time_column_invalidated_at = this->dls_time_column.size(); + this->dls_time_column_index = -1; + this->dls_time_column.clear(); + } else { + this->dls_time_column.push_back(tv); + } + } + + this->dls_rows.back().push_back(col_sf.data()); + hm.hm_column_size + = std::max(this->dls_headers[index].hm_column_size, + (size_t) utf8_string_length(col_sf.data(), col_sf.length()) + .unwrapOr(col_sf.length())); + + if ((sv.is() || sv.is()) + && this->dls_headers[index].hm_graphable) + { + if (sv.is()) { + this->dls_chart.add_value(hm.hm_name, sv.get()); + } else { + this->dls_chart.add_value(hm.hm_name, sv.get()); + } + } else if (col_sf.length() > 2 + && ((col_sf.startswith("{") && col_sf.endswith("}")) + || (col_sf.startswith("[") && col_sf.endswith("]")))) + { + json_ptr_walk jpw; + + if (jpw.parse(col_sf.data(), col_sf.length()) == yajl_status_ok + && jpw.complete_parse() == yajl_status_ok) + { + for (const auto& jpw_value : jpw.jpw_values) { + if (jpw_value.wt_type != yajl_t_number) { + continue; + } + + auto num_scan_res = scn::scan_value(jpw_value.wt_value); + if (num_scan_res) { + this->dls_chart.add_value(jpw_value.wt_ptr, + num_scan_res.value()); + this->dls_chart.with_attrs_for_ident( + jpw_value.wt_ptr, vc.attrs_for_ident(jpw_value.wt_ptr)); + } + } + } + } +} + +void +db_label_source::clear() +{ + this->dls_chart.clear(); + this->dls_headers.clear(); + this->dls_rows.clear(); + this->dls_time_column.clear(); + this->dls_cell_width.clear(); + this->dls_allocator = std::make_unique>(64 * 1024); +} + +nonstd::optional +db_label_source::column_name_to_index(const std::string& name) const +{ + return this->dls_headers | lnav::itertools::find(name); +} + +nonstd::optional +db_label_source::row_for_time(struct timeval time_bucket) +{ + std::vector::iterator iter; + + iter = std::lower_bound(this->dls_time_column.begin(), + this->dls_time_column.end(), + time_bucket); + if (iter != this->dls_time_column.end()) { + return vis_line_t(std::distance(this->dls_time_column.begin(), iter)); + } + return nonstd::nullopt; +} + +size_t +db_overlay_source::list_overlay_count(const listview_curses& lv) +{ + size_t retval = 1; + + if (!this->dos_active || lv.get_inner_height() == 0) { + this->dos_lines.clear(); + + return retval; + } + + auto& vc = view_colors::singleton(); + auto top = lv.get_top(); + const auto& cols = this->dos_labels->dls_rows[top]; + unsigned long width; + vis_line_t height; + + lv.get_dimensions(height, width); + + this->dos_lines.clear(); + for (size_t col = 0; col < cols.size(); col++) { + const char* col_value = cols[col]; + size_t col_len = strlen(col_value); + + if (!(col_len >= 2 + && ((col_value[0] == '{' && col_value[col_len - 1] == '}') + || (col_value[0] == '[' && col_value[col_len - 1] == ']')))) + { + continue; + } + + json_ptr_walk jpw; + + if (jpw.parse(col_value, col_len) == yajl_status_ok + && jpw.complete_parse() == yajl_status_ok) + { + { + const std::string& header + = this->dos_labels->dls_headers[col].hm_name; + this->dos_lines.emplace_back(" JSON Column: " + header); + + retval += 1; + } + + stacked_bar_chart chart; + int start_line = this->dos_lines.size(); + + chart.with_stacking_enabled(false).with_margins(3, 0); + + for (auto& jpw_value : jpw.jpw_values) { + this->dos_lines.emplace_back(" " + jpw_value.wt_ptr + " = " + + jpw_value.wt_value); + + string_attrs_t& sa = this->dos_lines.back().get_attrs(); + struct line_range lr(1, 2); + + sa.emplace_back(lr, VC_GRAPHIC.value(ACS_LTEE)); + lr.lr_start = 3 + jpw_value.wt_ptr.size() + 3; + lr.lr_end = -1; + sa.emplace_back(lr, VC_STYLE.value(text_attrs{A_BOLD})); + + if (jpw_value.wt_type == yajl_t_number) { + auto num_scan_res + = scn::scan_value(jpw_value.wt_value); + + if (num_scan_res) { + auto attrs = vc.attrs_for_ident(jpw_value.wt_ptr); + + chart.add_value(jpw_value.wt_ptr, num_scan_res.value()); + chart.with_attrs_for_ident(jpw_value.wt_ptr, attrs); + } + } + + retval += 1; + } + + int curr_line = start_line; + for (auto iter = jpw.jpw_values.begin(); + iter != jpw.jpw_values.end(); + ++iter, curr_line++) + { + if (iter->wt_type != yajl_t_number) { + continue; + } + + auto num_scan_res = scn::scan_value(iter->wt_value); + + if (num_scan_res) { + auto& sa = this->dos_lines[curr_line].get_attrs(); + int left = 3; + + chart.chart_attrs_for_value( + lv, left, iter->wt_ptr, num_scan_res.value(), sa); + } + } + } + } + + if (retval > 1) { + this->dos_lines.emplace_back(""); + + string_attrs_t& sa = this->dos_lines.back().get_attrs(); + struct line_range lr(1, 2); + + sa.emplace_back(lr, VC_GRAPHIC.value(ACS_LLCORNER)); + lr.lr_start = 2; + lr.lr_end = -1; + sa.emplace_back(lr, VC_GRAPHIC.value(ACS_HLINE)); + + retval += 1; + } + + return retval; +} + +bool +db_overlay_source::list_value_for_overlay(const listview_curses& lv, + int y, + int bottom, + vis_line_t row, + attr_line_t& value_out) +{ + if (y == 0) { + this->list_overlay_count(lv); + std::string& line = value_out.get_string(); + db_label_source* dls = this->dos_labels; + string_attrs_t& sa = value_out.get_attrs(); + + for (size_t lpc = 0; lpc < this->dos_labels->dls_headers.size(); lpc++) + { + auto actual_col_size = std::min( + MAX_COLUMN_WIDTH, dls->dls_headers[lpc].hm_column_size); + std::string cell_title = dls->dls_headers[lpc].hm_name; + + truncate_to(cell_title, MAX_COLUMN_WIDTH); + + auto cell_length + = utf8_string_length(cell_title).unwrapOr(actual_col_size); + int before, total_fill = actual_col_size - cell_length; + auto line_len_before = line.length(); + + before = total_fill / 2; + total_fill -= before; + line.append(before, ' '); + line.append(cell_title); + line.append(total_fill, ' '); + line.append(1, ' '); + + struct line_range header_range(line_len_before, line.length()); + + text_attrs attrs; + if (this->dos_labels->dls_headers[lpc].hm_graphable) { + attrs = dls->dls_headers[lpc].hm_title_attrs + | text_attrs{A_REVERSE}; + } else { + attrs.ta_attrs = A_UNDERLINE; + } + sa.emplace_back(header_range, VC_STYLE.value(text_attrs{attrs})); + } + + struct line_range lr(0); + + sa.emplace_back(lr, VC_STYLE.value(text_attrs{A_BOLD | A_UNDERLINE})); + return true; + } else if (this->dos_active && y >= 2 + && ((size_t) y) < (this->dos_lines.size() + 2)) + { + value_out = this->dos_lines[y - 2]; + return true; + } + + return false; +} diff --git a/src/db_sub_source.hh b/src/db_sub_source.hh new file mode 100644 index 0000000..d727989 --- /dev/null +++ b/src/db_sub_source.hh @@ -0,0 +1,144 @@ +/** + * Copyright (c) 2007-2012, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef db_sub_source_hh +#define db_sub_source_hh + +#include +#include +#include + +#include + +#include "ArenaAlloc/arenaalloc.h" +#include "hist_source.hh" +#include "shlex.resolver.hh" +#include "textview_curses.hh" + +class db_label_source + : public text_sub_source + , public text_time_translator { +public: + ~db_label_source() override { this->clear(); } + + bool has_log_time_column() const { return !this->dls_time_column.empty(); } + + size_t text_line_count() override { return this->dls_rows.size(); } + + size_t text_size_for_line(textview_curses& tc, + int line, + line_flags_t flags) override + { + return this->text_line_width(tc); + } + + size_t text_line_width(textview_curses& curses) override + { + size_t retval = 0; + + for (auto& dls_header : this->dls_headers) { + retval += dls_header.hm_column_size + 1; + } + return retval; + } + + void text_value_for_line(textview_curses& tc, + int row, + std::string& label_out, + line_flags_t flags) override; + + void text_attrs_for_line(textview_curses& tc, + int row, + string_attrs_t& sa) override; + + void push_header(const std::string& colstr, int type, bool graphable); + + void push_column(const scoped_value_t& sv); + + void clear(); + + nonstd::optional column_name_to_index( + const std::string& name) const; + + nonstd::optional row_for_time( + struct timeval time_bucket) override; + + nonstd::optional time_for_row(vis_line_t row) override + { + if ((row < 0_vl) || (((size_t) row) >= this->dls_time_column.size())) { + return nonstd::nullopt; + } + + return this->dls_time_column[row]; + } + + struct header_meta { + explicit header_meta(std::string name) : hm_name(std::move(name)) {} + + bool operator==(const std::string& name) const + { + return this->hm_name == name; + } + + std::string hm_name; + int hm_column_type{SQLITE3_TEXT}; + unsigned int hm_sub_type{0}; + bool hm_graphable{false}; + size_t hm_column_size{0}; + text_attrs hm_title_attrs; + }; + + stacked_bar_chart dls_chart; + std::vector dls_headers; + std::vector> dls_rows; + std::vector dls_time_column; + std::vector dls_cell_width; + int dls_time_column_index{-1}; + nonstd::optional dls_time_column_invalidated_at; + std::unique_ptr> dls_allocator{ + std::make_unique>(64 * 1024)}; + + static const char NULL_STR[]; +}; + +class db_overlay_source : public list_overlay_source { +public: + size_t list_overlay_count(const listview_curses& lv); + + bool list_value_for_overlay(const listview_curses& lv, + int y, + int bottom, + vis_line_t row, + attr_line_t& value_out) override; + + bool dos_active{false}; + db_label_source* dos_labels{nullptr}; + std::vector dos_lines; +}; +#endif diff --git a/src/diseases.json b/src/diseases.json new file mode 100644 index 0000000..8ec4914 --- /dev/null +++ b/src/diseases.json @@ -0,0 +1,549 @@ +{ + "data": [ + "achondroplasia", + "acinetobacter-infections", + "acne", + "actinomycosis", + "addisons-disease", + "african-sleeping-sickness", + "agammaglobulinemia", + "albinism", + "alcoholic-hepatitis", + "allergy", + "alopecia", + "alopecia-areata", + "alzheimers-disease", + "amblyopia", + "amebiasis", + "ampylobacter-infection", + "amyloidosis", + "anaplasmosis", + "anemia", + "aneurdu", + "ankylosing-spondylitis", + "anorexia", + "anosmia", + "anotia", + "anthrax", + "anti-gbm", + "anti-tbm-nephritis", + "antiphospholipid-syndrome", + "appendicitis", + "apraxia", + "arcanobacterium-haemolyticum-infection", + "argentine-hemorrhagic-fever", + "argyria", + "arthritis", + "ascariasis", + "aseptic-meningitis", + "aspergillosis", + "asthenia", + "asthma", + "astigmatism", + "astrovirus-infection", + "atherosclerosis", + "athetosis", + "atrophy", + "autoimmune-angioedema", + "autoimmune-aplastic-anemia", + "autoimmune-dysautonomia", + "autoimmune-hepatitis", + "autoimmune-hyperlipidemia", + "autoimmune-immunodeficiency", + "autoimmune-inner-ear-disease", + "autoimmune-myocarditis", + "autoimmune-oophoritis", + "autoimmune-pancreatitis", + "autoimmune-retinopathy", + "autoimmune-thrombocytopenic-purpura", + "autoimmune-thyroid-disease", + "autoimmune-urticaria", + "axonal-&-neuronal-neuropathies", + "babesiosis", + "bacillary-dysentery", + "bacillus-cereus-infection", + "bacterial-meningitis", + "bacterial-pneumonia", + "bacterial-vaginosis", + "bacteroides-infection", + "balantidiasis", + "balo-disease", + "barbers-itch", + "baylisascaris-infection", + "behcets-disease", + "beriberi", + "bk-virus-infection", + "black-death", + "black-piedra", + "blastocystis-hominis-infection", + "blastomycosis", + "bolivian-hemorrhagic-fever", + "borrelia-infection", + "botulism", + "brazilian-hemorrhagic-fever", + "breast-cancer", + "bronchitis", + "brucellosis", + "bubonic-plague", + "bullous-pemphigoid", + "bunion", + "burkholderia-infection", + "buruli-ulcer", + "calculi", + "calicivirus-infection", + "campylobacteriosis", + "cancer", + "candidiasis", + "carbon-monoxide-poisoning", + "cardiomyopathy", + "castleman-disease", + "cat-scratch-disease", + "celiac-disease", + "celiacs-disease", + "cellulitis", + "cerebral-palsy", + "chagas-disease", + "chalazion", + "chancroid", + "chavia", + "cherubism", + "chickenpox", + "chikungunya", + "chlamydia", + "chlamydia-trachomatis", + "chlamydophila-pneumoniae-infection", + "cholera", + "chordoma", + "chorea", + "chromoblastomycosis", + "chronic-fatigue-syndrome", + "chronic-inflammatory-demyelinating-polyneuropathy", + "chronic-recurrent-multifocal-ostomyelitis", + "churg-strauss-syndrome", + "circadian-rhythm-sleep-disorder", + "clonorchiasis", + "clostridial-myonecrosis", + "clostridium-difficile-infection", + "coccidioidomycosis", + "cogans-syndrome", + "cold-agglutinin-disease", + "colitis", + "colorado-tick-fever", + "common-cold", + "condyloma", + "congenital-heart-block", + "congestive-heart-disease", + "coronary-heart-disease", + "cowpox", + "coxsackie-myocarditis", + "crest-disease", + "cretinism", + "creutzfeldt-jakob-disease", + "crimean-congo-hemorrhagic-fever", + "crohns-disease", + "cryptococcosis", + "cryptosporidiosis", + "cutaneous-larva-migrans", + "cyclosporiasis", + "cysticercosis", + "cytomegalovirus-infection", + "demyelinating-neuropathies", + "dengue-fever", + "dermatitis-herpetiformis", + "dermatomyositis", + "devics-disease", + "diabetes-mellitus", + "dientamoebiasis", + "diphtheria", + "diphyllobothriasis", + "discoid-lupus", + "donovanosis", + "dracunculiasis", + "dresslers-syndrome", + "ear-infection", + "ebola", + "ebola-hemorrhagic-fever", + "echinococcosis", + "ehrlichiosis", + "elephantiasis", + "emphysema", + "encephalitis", + "endometriosis", + "enterovirus-infection", + "eosinophilic-esophagitis", + "eosinophilic-fasciitis", + "epidemic-typhus", + "epilepsy", + "erectile-dysfunctions", + "erythema-infectiosum", + "erythema-nodosum", + "essential-mixed-cryoglobulinemia", + "evans-syndrome", + "exanthem-subitum", + "experimental-allergic-encephalomyelitis", + "fasciolopsiasis", + "fasciolosis", + "fatal-familial-insomnia", + "fibromyalgia", + "fibrosing-alveolitis", + "fifth-disease", + "filariasis", + "foodborne-illness", + "free-living-amebic-infection", + "fusobacterium-infection", + "gangrene", + "gas-gangrene", + "gastroenteritis", + "genital-herpes", + "geotrichosis", + "gerd", + "gerstmann-sträussler-scheinker-syndrome", + "giant-cell-arteritis", + "giant-cell-myocarditis", + "giardiasis", + "glanders", + "glomerulonephritis", + "gnathostomiasis", + "goitre", + "gonorrhea", + "goodpastures-syndrome", + "granuloma-inguinale", + "graves-disease", + "group-a-streptococcal-infection", + "group-b-streptococcal-infection", + "guillain-barre-syndrome", + "haemophilus-influenzae-infection", + "hand-foot-and-mouth-disease", + "hantavirus-pulmonary-syndrome", + "hashimotos-encephalitis", + "hashimotos-thyroiditis", + "heart-disease", + "heartland-virus-disease", + "helicobacter-pylori-infection", + "hemolytic-anemia", + "hemolytic-uremic-syndrome", + "henoch-schonlein-purpura", + "hepatitis-a", + "hepatitis-b", + "hepatitis-c", + "hepatitis-d", + "hepatitis-e", + "herpes-gestationis", + "herpes-simplex", + "histoplasmosis", + "hiv", + "hookworm-infection", + "human-bocavirus-infection", + "human-ewingii-ehrlichiosis", + "human-granulocytic-anaplasmosis", + "human-metapneumovirus-infection", + "human-monocytic-ehrlichiosis", + "human-papillomavirus-(hpv)", + "human-parainfluenza-virus-infection", + "huntingtons-disease", + "hymenolepiasis", + "hypermetropia", + "hyperopia", + "hyperthyroidism", + "hypogammaglobulinemia", + "hypothyroid", + "hypotonia", + "idiopathic-pulmonary-fibrosis", + "idiopathic-thrombocytopenic-purpura", + "iga-nephropathy", + "igg4-related-sclerosing-disease", + "ignious-syndrome", + "immunoregulatory-lipoproteins", + "impetigo", + "inclusion-body-myositis", + "infertility", + "influenza", + "interstitial-cystitis", + "iritis", + "iron-deficiency-anemia", + "irritable-bowel-syndrome", + "isosporiasis", + "jaundice", + "juvenile-arthritis", + "juvenile-diabetes", + "juvenile-myositis", + "kawasaki-disease", + "kawasaki-syndrome", + "keloids", + "keratitis", + "kingella-kingae-infection", + "kuru", + "kwashiorkor", + "lambert-eaton-syndrome", + "laryngitis", + "lassa-fever", + "lead-poisoning", + "legionellosis", + "legionnaires-disease", + "leishmaniasis", + "leprosy", + "leptospirosis", + "leukemia", + "leukocytoclastic-vasculitis", + "lice", + "lichen-planus", + "lichen-sclerosus", + "ligneous-conjunctivitis", + "listeriosis", + "lockjaw", + "loiasis", + "lung-cancer", + "lupus", + "lupus-erythematosus", + "lyme-disease", + "lymphocytic-choriomeningitis", + "lymphogranuloma-venereum", + "lymphoma", + "mad-cow-disease", + "malaria", + "marburg-fever", + "marburg-hemorrhagic-fever", + "mattticular-syndrome", + "measles", + "melanoma", + "melioidosis", + "menieres-disease", + "meningitis", + "meningococcal-disease", + "metagonimiasis", + "metastatic-cancer", + "microscopic-polyangiitis", + "microsporidiosis", + "middle-east-respiratory-syndrome", + "migraine", + "mixed-connective-tissue-disease", + "molluscum-contagiosum", + "monkeypox", + "mononucleosis", + "moorens-ulcer", + "morquio-syndrome", + "mucha-habermann-disease", + "multiple-myeloma", + "multiple-sclerosis", + "mumps", + "murine-typhus", + "muscular-dystrophy", + "myasthenia-gravis", + "mycetoma", + "mycoplasma-pneumonia", + "myelitis", + "myiasis", + "myoclonus", + "myopia", + "myositis", + "myxedema", + "ménières-disease", + "narcolepsy", + "necrotizing-fasciitis", + "neonatal-conjunctivitis", + "neoplasm", + "neuromyelitis-optica", + "neutropenia", + "night-blindness", + "nocardiosis", + "non-gonococcal-urethritis", + "obesity", + "ocular-cicatricial-pemphigoid", + "onchocerciasis", + "optic-neuritis", + "osteoarthritis", + "osteoporosis", + "otitis", + "palindromic-rheumatism", + "paracoccidioidomycosis", + "paragonimiasis", + "paraneoplastic-cerebellar-degeneration", + "paratyphoid-fever", + "parkinsons-disease", + "paroxysmal-nocturnal-hemoglobinuria", + "parry-romberg-syndrome", + "pars-planitis", + "parsonnage-turner-syndrome", + "pasteurellosis", + "pediculosis-capitis-(head-lice)", + "pediculosis-corporis-(body-lice)", + "pediculosis-pubis-(pubic-lice)", + "pelvic-inflammatory-disease", + "pemphigus", + "periodontal-disease", + "peripheral-neuropathy", + "peritonitis", + "perivenous-encephalomyelitis", + "pernicious-anemia", + "pertussis", + "phenylketonuria", + "pilia", + "pinworm-infection", + "plague", + "pneumococcal-infection", + "pneumocystis-pneumonia", + "pneumonia", + "pneumonia", + "poems-syndrome", + "poliomyelitis", + "polyarteritis-nodosa", + "polymyalgia-rheumatica", + "polymyositis", + "pontiac-fever", + "porphyria", + "postmyocardial-infarction-syndrome", + "postpericardiotomy-syndrome", + "prevotella-infection", + "primary-amoebic-meningoencephalitis", + "primary-biliary-cirrhosis", + "primary-sclerosing-cholangitis", + "progeria", + "progesterone-dermatitis", + "progressive-multifocal-leukoencephalopathy", + "prostatitis", + "psittacosis", + "psoriasis", + "psoriatic-arthritis", + "pubic-lice", + "pulmonary-embolism", + "pure-red-cell-aplasia", + "pyoderma-gangrenosum", + "q-fever", + "ques-fever", + "rabies", + "rat-bite-fever", + "raynauds-phenomenon", + "reactive-arthritis", + "reflex-sympathetic-dystrophy", + "reiters-syndrome", + "relapsing-polychondritis", + "repetitive-strain-injury", + "respiratory-syncytial-virus-infection", + "restless-legs-syndrome", + "retroperitoneal-fibrosis", + "rheumatic-fever", + "rheumatic-heart", + "rheumatism", + "rheumatoid-arthritis", + "rhinosporidiosis", + "rhinovirus-infection", + "rickets", + "rickettsial-infection", + "rickettsialpox", + "rift-valley-fever", + "ringworm", + "river-blindness", + "rocky-mountain-spotted-fever", + "rotavirus-infection", + "rubella", + "salmonellosis", + "sarcoidosis", + "sars", + "scabies", + "scarlet-fever", + "schistosomiasis", + "schizophrenia", + "schmidt-syndrome", + "sciatica", + "scleritis", + "scleroderma", + "scrapie", + "scurvy", + "sepsis", + "septicemia", + "shigellosis", + "shin-splints", + "shingles", + "sickle-cell-anemia", + "siderosis", + "sids", + "silicosis", + "sixth-disease", + "sjogrens-syndrome", + "smallpox", + "south-american-blastomycosis", + "sperm-&-testicular-autoimmunity", + "sporotrichosis", + "staphylococcal-food-poisoning", + "staphylococcal-infection", + "stevens-johnson-syndrome", + "stiff-person-syndrome", + "stomach-flu", + "stomach-ulcers", + "strabismus", + "strep-throat", + "streptococcal-infection", + "strongyloidiasis", + "subacute-bacterial-endocarditis", + "subacute-sclerosing-panencephalitis", + "susacs-syndrome", + "swine-influenza", + "sympathetic-ophthalmia", + "synovitis", + "syphilis", + "taeniasis", + "takayasus-arteritis", + "tay-sachs-disease", + "temporal-arteritis/giant-cell-arteritis", + "tennis-elbow", + "teratoma", + "tetanus", + "thalassaemia", + "thrombocytopenic-purpura", + "thrush", + "thymoma", + "tinea-barbae", + "tinnitus", + "tolosa-hunt-syndrome", + "tonsillitis", + "tooth-decay", + "toxic-shock-syndrome", + "toxocariasis", + "transverse-myelitis", + "trichinosis", + "trichomoniasis", + "trichuriasis", + "trinochccliasis", + "trisomy", + "tuberculosis", + "tularemia", + "tumor", + "tungiasis", + "type-1-diabetes", + "typhoid-fever", + "typhus", + "ulcerative-colitis", + "ulcers", + "undifferentiated-connective-tissue-disease", + "ureaplasma-urealyticum-infection", + "uremia", + "urticaria", + "uveitis", + "valley-fever", + "varicella", + "varicose-veins", + "vasculitis", + "vasovagal-syncope", + "venezuelan-equine-encephalitis", + "venezuelan-hemorrhagic-fever", + "vesiculobullous-dermatosis", + "viral-fever", + "viral-meningitis", + "viral-pneumonia", + "vitiligo", + "von-hippel-lindau-disease", + "warkany-syndrome", + "warts", + "watkins", + "wegeners-granulomatosis", + "west-nile-fever", + "whipworm-infection", + "white-piedra", + "whitmores-disease", + "whooping-cough", + "yellow-fever", + "yersinia-pseudotuberculosis-infection", + "yersiniosis", + "zygomycosis" + ] +} \ No newline at end of file diff --git a/src/doc_status_source.hh b/src/doc_status_source.hh new file mode 100644 index 0000000..0624fa4 --- /dev/null +++ b/src/doc_status_source.hh @@ -0,0 +1,81 @@ +/** + * Copyright (c) 2017, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef lnav_doc_status_source_hh +#define lnav_doc_status_source_hh + +#include + +#include "statusview_curses.hh" + +class doc_status_source : public status_data_source { +public: + typedef enum { + TSF_TITLE, + TSF_STITCH_TITLE, + TSF_DESCRIPTION, + + TSF__MAX + } field_t; + + doc_status_source() + { + this->tss_fields[TSF_TITLE].set_width(14); + this->tss_fields[TSF_TITLE].set_left_pad(1); + this->tss_fields[TSF_TITLE].set_role(role_t::VCR_STATUS_TITLE); + this->tss_fields[TSF_STITCH_TITLE].set_width(2); + this->tss_fields[TSF_STITCH_TITLE].set_stitch_value( + role_t::VCR_STATUS_STITCH_TITLE_TO_NORMAL, + role_t::VCR_STATUS_STITCH_NORMAL_TO_TITLE); + this->tss_fields[TSF_DESCRIPTION].set_share(1); + this->tss_fields[TSF_DESCRIPTION].set_role(role_t::VCR_STATUS); + } + + size_t statusview_fields() override { return TSF__MAX; } + + status_field& statusview_value_for_field(int field) override + { + return this->tss_fields[field]; + } + + void set_title(const std::string& title) + { + this->tss_fields[TSF_TITLE].set_value(title); + } + + void set_description(const std::string& description) + { + this->tss_fields[TSF_DESCRIPTION].set_value(description); + } + +private: + status_field tss_fields[TSF__MAX]; +}; + +#endif diff --git a/src/document.sections.cc b/src/document.sections.cc new file mode 100644 index 0000000..da9c33f --- /dev/null +++ b/src/document.sections.cc @@ -0,0 +1,540 @@ +/** + * Copyright (c) 2022, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include "document.sections.hh" + +#include "base/enum_util.hh" +#include "base/itertools.hh" +#include "base/lnav_log.hh" +#include "base/opt_util.hh" +#include "data_scanner.hh" + +namespace lnav { +namespace document { + +nonstd::optional +hier_node::lookup_child(section_key_t key) const +{ + return make_optional_from_nullable(key.match( + [this](const std::string& str) -> hier_node* { + auto iter = this->hn_named_children.find(str); + if (iter != this->hn_named_children.end()) { + return iter->second; + } + return nullptr; + }, + [this](size_t index) -> hier_node* { + if (index < this->hn_children.size()) { + return this->hn_children[index].get(); + } + return nullptr; + })); +} + +nonstd::optional +hier_node::lookup_path(const hier_node* root, + const std::vector& path) +{ + auto retval = make_optional_from_nullable(root); + + for (const auto& comp : path) { + if (!retval) { + break; + } + + retval = retval.value()->lookup_child(comp); + } + + if (!retval) { + return nonstd::nullopt; + } + + return retval; +} + +struct metadata_builder { + std::vector mb_intervals; + std::unique_ptr mb_root_node; + + metadata to_metadata() && + { + return { + std::move(this->mb_intervals), + std::move(this->mb_root_node), + }; + } +}; + +static void +discover_metadata_int(const attr_line_t& al, metadata_builder& mb) +{ + const auto& orig_attrs = al.get_attrs(); + auto headers = orig_attrs + | lnav::itertools::filter_in([](const string_attr& attr) { + if (attr.sa_type != &VC_ROLE) { + return false; + } + + auto role = attr.sa_value.get(); + switch (role) { + case role_t::VCR_H1: + case role_t::VCR_H2: + case role_t::VCR_H3: + case role_t::VCR_H4: + case role_t::VCR_H5: + case role_t::VCR_H6: + return true; + default: + return false; + } + }) + | lnav::itertools::sort_by(&string_attr::sa_range); + + // Remove headers from quoted text + for (const auto& orig_attr : orig_attrs) { + if (orig_attr.sa_type == &VC_ROLE + && orig_attr.sa_value.get() == role_t::VCR_QUOTED_TEXT) + { + remove_string_attr(headers, orig_attr.sa_range); + } + } + + auto& intervals = mb.mb_intervals; + + struct open_interval_t { + open_interval_t(uint32_t level, file_off_t start, section_key_t id) + : oi_level(level), oi_start(start), oi_id(std::move(id)) + { + } + + int32_t oi_level; + file_off_t oi_start; + section_key_t oi_id; + std::unique_ptr oi_node{std::make_unique()}; + }; + std::vector open_intervals; + auto root_node = std::make_unique(); + + for (const auto& hdr_attr : headers) { + auto role = hdr_attr.sa_value.get(); + auto role_num = lnav::enums::to_underlying(role) + - lnav::enums::to_underlying(role_t::VCR_H1); + std::vector new_open_intervals; + + for (auto& oi : open_intervals) { + if (oi.oi_level >= role_num) { + // close out this section + intervals.emplace_back( + oi.oi_start, hdr_attr.sa_range.lr_start - 1, oi.oi_id); + auto* node_ptr = oi.oi_node.get(); + auto* parent_node = oi.oi_node->hn_parent; + if (parent_node != nullptr) { + parent_node->hn_children.emplace_back( + std::move(oi.oi_node)); + parent_node->hn_named_children.insert({ + oi.oi_id.get(), + node_ptr, + }); + } + } else { + new_open_intervals.emplace_back(std::move(oi)); + } + } + auto* parent_node = new_open_intervals.empty() + ? root_node.get() + : new_open_intervals.back().oi_node.get(); + new_open_intervals.emplace_back(role_num, + hdr_attr.sa_range.lr_start, + al.get_substring(hdr_attr.sa_range)); + new_open_intervals.back().oi_node->hn_parent = parent_node; + new_open_intervals.back().oi_node->hn_start + = hdr_attr.sa_range.lr_start; + + open_intervals = std::move(new_open_intervals); + } + + for (auto& oi : open_intervals) { + // close out this section + intervals.emplace_back(oi.oi_start, al.length(), oi.oi_id); + auto* node_ptr = oi.oi_node.get(); + auto* parent_node = oi.oi_node->hn_parent; + if (parent_node == nullptr) { + root_node = std::move(oi.oi_node); + } else { + parent_node->hn_children.emplace_back(std::move(oi.oi_node)); + parent_node->hn_named_children.insert({ + oi.oi_id.get(), + node_ptr, + }); + } + } + + for (auto& interval : intervals) { + auto start_off_iter = find_string_attr_containing( + orig_attrs, &SA_ORIGIN_OFFSET, interval.start); + if (start_off_iter != orig_attrs.end()) { + interval.start += start_off_iter->sa_value.get(); + } + auto stop_off_iter = find_string_attr_containing( + orig_attrs, &SA_ORIGIN_OFFSET, interval.stop - 1); + if (stop_off_iter != orig_attrs.end()) { + interval.stop += stop_off_iter->sa_value.get(); + } + } + + hier_node::depth_first(root_node.get(), [&orig_attrs](hier_node* node) { + auto off_opt + = get_string_attr(orig_attrs, &SA_ORIGIN_OFFSET, node->hn_start); + + if (off_opt) { + node->hn_start += off_opt.value()->sa_value.get(); + } + }); + + if (!root_node->hn_children.empty() + || !root_node->hn_named_children.empty()) + { + mb.mb_root_node = std::move(root_node); + } +} + +metadata +discover_metadata(const attr_line_t& al) +{ + metadata_builder mb; + + discover_metadata_int(al, mb); + + return std::move(mb).to_metadata(); +} + +class structure_walker { +public: + explicit structure_walker(attr_line_t& al, line_range lr) + : sw_line(al), sw_range(lr), + sw_scanner(string_fragment::from_str_range( + al.get_string(), lr.lr_start, lr.lr_end)) + { + this->sw_interval_state.resize(1); + this->sw_hier_nodes.push_back(std::make_unique()); + } + + metadata walk() + { + metadata_builder mb; + size_t garbage_count = 0; + + while (garbage_count < 1000) { + auto tokenize_res = this->sw_scanner.tokenize2(); + if (!tokenize_res) { + break; + } + + auto dt = tokenize_res->tr_token; + element el(tokenize_res->tr_token, tokenize_res->tr_capture); + + switch (dt) { + case DT_XML_DECL_TAG: + case DT_XML_EMPTY_TAG: + this->sw_values.emplace_back(el); + break; + case DT_XML_OPEN_TAG: + this->flush_values(); + this->sw_interval_state.back().is_start + = el.e_capture.c_begin; + this->sw_interval_state.back().is_line_number + = this->sw_line_number; + this->sw_interval_state.back().is_name + = tokenize_res->to_string(); + this->sw_depth += 1; + this->sw_interval_state.resize(this->sw_depth + 1); + this->sw_hier_nodes.push_back( + std::make_unique()); + break; + case DT_XML_CLOSE_TAG: { + auto term = this->flush_values(); + if (this->sw_depth > 0) { + this->sw_depth -= 1; + this->append_child_node(term); + this->sw_interval_state.pop_back(); + this->sw_hier_stage + = std::move(this->sw_hier_nodes.back()); + this->sw_hier_nodes.pop_back(); + } + this->append_child_node(el.e_capture); + this->flush_values(); + break; + } + case DT_H1: { + this->sw_line.get_attrs().emplace_back( + line_range{ + this->sw_range.lr_start + el.e_capture.c_begin + 1, + this->sw_range.lr_start + el.e_capture.c_end - 1, + }, + VC_ROLE.value(role_t::VCR_H1)); + this->sw_line_number += 2; + break; + } + case DT_LCURLY: + case DT_LSQUARE: + case DT_LPAREN: { + this->flush_values(); + // this->append_child_node(term); + this->sw_depth += 1; + this->sw_interval_state.back().is_start + = el.e_capture.c_begin; + this->sw_interval_state.back().is_line_number + = this->sw_line_number; + this->sw_interval_state.resize(this->sw_depth + 1); + this->sw_hier_nodes.push_back( + std::make_unique()); + break; + } + case DT_RCURLY: + case DT_RSQUARE: + case DT_RPAREN: { + auto term = this->flush_values(); + if (this->sw_depth > 0) { + this->append_child_node(term); + this->sw_depth -= 1; + this->sw_interval_state.pop_back(); + this->sw_hier_stage + = std::move(this->sw_hier_nodes.back()); + this->sw_hier_nodes.pop_back(); + if (this->sw_interval_state.back().is_start) { + data_scanner::capture_t obj_cap = { + static_cast(this->sw_interval_state.back() + .is_start.value()), + el.e_capture.c_end, + }; + + auto sf + = this->sw_scanner.to_string_fragment(obj_cap); + if (!sf.find('\n')) { + this->sw_hier_stage->hn_named_children.clear(); + this->sw_hier_stage->hn_children.clear(); + while (!this->sw_intervals.empty() + && this->sw_intervals.back().start + > obj_cap.c_begin) + { + this->sw_intervals.pop_back(); + } + } + } + } + this->sw_values.emplace_back(el); + break; + } + case DT_COMMA: + if (this->sw_depth > 0) { + auto term = this->flush_values(); + this->append_child_node(term); + } + break; + case DT_LINE: + this->sw_line_number += 1; + break; + case DT_WHITE: + break; + default: + if (dt == DT_GARBAGE) { + garbage_count += 1; + } + this->sw_values.emplace_back(el); + break; + } + } + this->flush_values(); + + if (this->sw_hier_stage != nullptr) { + this->sw_hier_stage->hn_parent = this->sw_hier_nodes.back().get(); + this->sw_hier_nodes.back()->hn_children.push_back( + std::move(this->sw_hier_stage)); + } + this->sw_hier_stage = std::move(this->sw_hier_nodes.back()); + this->sw_hier_nodes.pop_back(); + if (this->sw_hier_stage->hn_children.size() == 1 + && this->sw_hier_stage->hn_named_children.empty()) + { + this->sw_hier_stage + = std::move(this->sw_hier_stage->hn_children.front()); + this->sw_hier_stage->hn_parent = nullptr; + } + + mb.mb_root_node = std::move(this->sw_hier_stage); + mb.mb_intervals = std::move(this->sw_intervals); + + discover_metadata_int(this->sw_line, mb); + + return std::move(mb).to_metadata(); + } + +private: + struct element { + element(data_token_t token, data_scanner::capture_t& cap) + : e_token(token), e_capture(cap) + { + } + + data_token_t e_token; + data_scanner::capture_t e_capture; + }; + + struct interval_state { + nonstd::optional is_start; + size_t is_line_number{0}; + std::string is_name; + }; + + nonstd::optional flush_values() + { + nonstd::optional last_key; + nonstd::optional retval; + + if (!this->sw_values.empty()) { + if (!this->sw_interval_state.back().is_start) { + this->sw_interval_state.back().is_start + = this->sw_values.front().e_capture.c_begin; + this->sw_interval_state.back().is_line_number + = this->sw_line_number; + } + retval = this->sw_values.back().e_capture; + } + for (const auto& el : this->sw_values) { + switch (el.e_token) { + case DT_SYMBOL: + case DT_CONSTANT: + case DT_WORD: + case DT_QUOTED_STRING: + last_key = el.e_capture; + break; + case DT_COLON: + case DT_EQUALS: + if (last_key) { + this->sw_interval_state.back().is_name + = this->sw_scanner + .to_string_fragment(last_key.value()) + .to_string(); + if (!this->sw_interval_state.back().is_name.empty()) { + this->sw_interval_state.back().is_start + = static_cast( + last_key.value().c_begin); + this->sw_interval_state.back().is_line_number + = this->sw_line_number; + } + last_key = nonstd::nullopt; + } + break; + default: + break; + } + } + + this->sw_values.clear(); + + return retval; + } + + void append_child_node(nonstd::optional terminator) + { + auto& ivstate = this->sw_interval_state.back(); + if (!ivstate.is_start || !terminator || this->sw_depth == 0) { + ivstate.is_start = nonstd::nullopt; + ivstate.is_line_number = 0; + ivstate.is_name.clear(); + return; + } + + auto new_node = this->sw_hier_stage != nullptr + ? std::move(this->sw_hier_stage) + : std::make_unique(); + auto iv_start = ivstate.is_start.value(); + auto iv_stop = static_cast(terminator.value().c_end); + auto* top_node = this->sw_hier_nodes.back().get(); + auto new_key = ivstate.is_name.empty() + ? lnav::document::section_key_t{top_node->hn_children.size()} + : lnav::document::section_key_t{ivstate.is_name}; + this->sw_intervals.emplace_back(iv_start, iv_stop, new_key); + auto* retval = new_node.get(); + new_node->hn_parent = top_node; + new_node->hn_start = this->sw_intervals.back().start; + new_node->hn_line_number = ivstate.is_line_number; + if (!ivstate.is_name.empty()) { + top_node->hn_named_children.insert({ + ivstate.is_name, + retval, + }); + } + top_node->hn_children.emplace_back(std::move(new_node)); + ivstate.is_start = nonstd::nullopt; + ivstate.is_line_number = 0; + ivstate.is_name.clear(); + } + + attr_line_t& sw_line; + line_range sw_range; + data_scanner sw_scanner; + int sw_depth{0}; + size_t sw_line_number{0}; + std::vector sw_values{}; + std::vector sw_interval_state; + std::vector sw_intervals; + std::vector> sw_hier_nodes; + std::unique_ptr sw_hier_stage; +}; + +metadata +discover_structure(attr_line_t& al, struct line_range lr) +{ + return structure_walker(al, lr).walk(); +} + +std::vector +metadata::possibility_provider(const std::vector& path) +{ + std::vector retval; + auto curr_node = lnav::document::hier_node::lookup_path( + this->m_sections_root.get(), path); + if (curr_node) { + auto* parent_node = curr_node.value()->hn_parent; + + if (parent_node != nullptr) { + for (const auto& sibling : parent_node->hn_named_children) { + retval.template emplace_back(sibling.first); + } + } + } + return retval; +} + +} // namespace document +} // namespace lnav diff --git a/src/document.sections.hh b/src/document.sections.hh new file mode 100644 index 0000000..94cd01a --- /dev/null +++ b/src/document.sections.hh @@ -0,0 +1,116 @@ +/** + * Copyright (c) 2022, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef lnav_attr_line_breadcrumbs_hh +#define lnav_attr_line_breadcrumbs_hh + +#include +#include +#include + +#include "base/attr_line.hh" +#include "base/file_range.hh" +#include "breadcrumb.hh" +#include "intervaltree/IntervalTree.h" +#include "mapbox/variant.hpp" +#include "optional.hpp" + +namespace lnav { +namespace document { + +using section_key_t = mapbox::util::variant; +using section_interval_t = interval_tree::Interval; +using sections_tree_t = interval_tree::IntervalTree; + +struct hier_node { + hier_node* hn_parent{nullptr}; + file_off_t hn_start{0}; + size_t hn_line_number{0}; + std::multimap hn_named_children; + std::vector> hn_children; + + nonstd::optional lookup_child(section_key_t key) const; + + nonstd::optional find_line_number(const std::string& str) const + { + auto iter = this->hn_named_children.find(str); + if (iter != this->hn_named_children.end()) { + return nonstd::make_optional(iter->second->hn_line_number); + } + + return nonstd::nullopt; + } + + nonstd::optional find_line_number(size_t index) const + { + if (index < this->hn_children.size()) { + return nonstd::make_optional( + this->hn_children[index]->hn_line_number); + } + return nonstd::nullopt; + } + + bool is_named_only() const + { + return this->hn_children.size() == this->hn_named_children.size(); + } + + static nonstd::optional lookup_path( + const hier_node* root, const std::vector& path); + + template + static void depth_first(hier_node* root, F func) + { + if (root == nullptr) { + return; + } + + for (auto& child : root->hn_children) { + depth_first(child.get(), func); + } + func(root); + } +}; + +struct metadata { + sections_tree_t m_sections_tree; + std::unique_ptr m_sections_root; + + std::vector possibility_provider( + const std::vector& path); +}; + +metadata discover_metadata(const attr_line_t& al); + +metadata discover_structure(attr_line_t& al, struct line_range lr); + +} // namespace document +} // namespace lnav + +#endif diff --git a/src/dump_internals.cc b/src/dump_internals.cc new file mode 100644 index 0000000..8317769 --- /dev/null +++ b/src/dump_internals.cc @@ -0,0 +1,93 @@ +/** + * Copyright (c) 2022, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "dump_internals.hh" + +#include "lnav.events.hh" +#include "lnav.hh" +#include "lnav_config.hh" +#include "log_format_loader.hh" +#include "sql_help.hh" +#include "view_helpers.examples.hh" +#include "yajlpp/yajlpp.hh" + +namespace lnav { + +void +dump_internals(const char* internals_dir) +{ + for (const auto* handlers : + std::initializer_list{ + &lnav_config_handlers, + &root_format_handler, + &lnav::events::file::open::handlers, + &lnav::events::file::format_detected::handlers, + &lnav::events::log::msg_detected::handlers, + &lnav::events::session::loaded::handlers, + }) + { + dump_schema_to(*handlers, internals_dir); + } + + execute_examples(); + + auto cmd_ref_path = ghc::filesystem::path(internals_dir) / "cmd-ref.rst"; + auto cmd_file = std::unique_ptr( + fopen(cmd_ref_path.c_str(), "w+"), fclose); + + if (cmd_file != nullptr) { + std::set unique_cmds; + + for (auto& cmd : lnav_commands) { + if (unique_cmds.find(cmd.second) != unique_cmds.end()) { + continue; + } + unique_cmds.insert(cmd.second); + format_help_text_for_rst( + cmd.second->c_help, eval_example, cmd_file.get()); + } + } + + auto sql_ref_path = ghc::filesystem::path(internals_dir) / "sql-ref.rst"; + auto sql_file = std::unique_ptr( + fopen(sql_ref_path.c_str(), "w+"), fclose); + std::set unique_sql_help; + + if (sql_file != nullptr) { + for (auto& sql : sqlite_function_help) { + if (unique_sql_help.find(sql.second) != unique_sql_help.end()) { + continue; + } + unique_sql_help.insert(sql.second); + format_help_text_for_rst(*sql.second, eval_example, sql_file.get()); + } + } +} + +} // namespace lnav diff --git a/src/dump_internals.hh b/src/dump_internals.hh new file mode 100644 index 0000000..52a4815 --- /dev/null +++ b/src/dump_internals.hh @@ -0,0 +1,41 @@ +/** + * Copyright (c) 2022, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef dump_internals_hh +#define dump_internals_hh + +#include + +namespace lnav { + +void dump_internals(const char* dir); + +} // namespace lnav + +#endif diff --git a/src/elem_to_json.cc b/src/elem_to_json.cc new file mode 100644 index 0000000..4b04dfb --- /dev/null +++ b/src/elem_to_json.cc @@ -0,0 +1,222 @@ +/** + * Copyright (c) 2016, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include "elem_to_json.hh" + +#include "base/itertools.hh" +#include "config.h" +#include "yajlpp/yajlpp.hh" + +static void +element_to_json(yajl_gen gen, data_parser& dp, const data_parser::element& elem) +{ + size_t value_len; + const char* value_str = dp.get_element_string(elem, value_len); + + switch (elem.value_token()) { + case DT_NUMBER: { + yajl_gen_number(gen, value_str, value_len); + break; + } + case DNT_GROUP: { + elements_to_json( + gen, dp, elem.get_value_elem().e_sub_elements, false); + break; + } + case DNT_PAIR: { + const data_parser::element& pair_elem = elem.get_pair_elem(); + const auto key_str + = dp.get_element_string(pair_elem.e_sub_elements->front()); + + if (!key_str.empty()) { + yajlpp_map singleton_map(gen); + + singleton_map.gen(key_str); + element_to_json(gen, dp, pair_elem.get_pair_value()); + } else { + element_to_json(gen, dp, pair_elem.get_pair_value()); + } + break; + } + case DT_CONSTANT: { + if (strncasecmp("true", value_str, value_len) == 0) { + yajl_gen_bool(gen, true); + } else if (strncasecmp("false", value_str, value_len) == 0) { + yajl_gen_bool(gen, false); + } else { + yajl_gen_null(gen); + } + break; + } + default: + yajl_gen_pstring(gen, value_str, value_len); + break; + } +} + +static void +map_elements_to_json2(yajl_gen gen, + data_parser& dp, + data_parser::element_list_t* el) +{ + yajlpp_map root_map(gen); + int col = 0; + + for (auto& iter : *el) { + if (iter.e_token != DNT_PAIR) { + log_warning("dropping non-pair element: %s", + dp.get_element_string(iter).c_str()); + continue; + } + + const data_parser::element& pvalue = iter.get_pair_value(); + + if (pvalue.value_token() == DT_INVALID) { + log_debug("invalid!!"); + // continue; + } + + std::string key_str + = dp.get_element_string(iter.e_sub_elements->front()); + + if (key_str.empty()) { + key_str = fmt::format(FMT_STRING("col_{}"), col); + col += 1; + } + root_map.gen(key_str); + element_to_json(gen, dp, pvalue); + } +} + +static void +list_body_elements_to_json(yajl_gen gen, + data_parser& dp, + data_parser::element_list_t* el) +{ + for (auto& iter : *el) { + element_to_json(gen, dp, iter); + } +} + +static void +list_elements_to_json(yajl_gen gen, + data_parser& dp, + data_parser::element_list_t* el) +{ + yajlpp_array root_array(gen); + + list_body_elements_to_json(gen, dp, el); +} + +static void +map_elements_to_json(yajl_gen gen, + data_parser& dp, + data_parser::element_list_t* el) +{ + bool unique_names = el->size() > 1; + std::vector names; + + for (auto& iter : *el) { + if (iter.e_token != DNT_PAIR) { + unique_names = false; + continue; + } + + const auto& pvalue = iter.get_pair_value(); + + if (pvalue.value_token() == DT_INVALID) { + log_debug("invalid!!"); + // continue; + } + + std::string key_str + = dp.get_element_string(iter.e_sub_elements->front()); + if (key_str.empty()) { + continue; + } + if (names | lnav::itertools::find(key_str)) { + unique_names = false; + break; + } + names.push_back(key_str); + } + + names.clear(); + + if (unique_names) { + map_elements_to_json2(gen, dp, el); + } else { + list_elements_to_json(gen, dp, el); + } +} + +void +elements_to_json(yajl_gen gen, + data_parser& dp, + data_parser::element_list_t* el, + bool root) +{ + if (el->empty()) { + yajl_gen_null(gen); + } else { + switch (el->front().e_token) { + case DNT_PAIR: { + if (root && el->size() == 1) { + const data_parser::element& pair_elem + = el->front().get_pair_elem(); + std::string key_str = dp.get_element_string( + pair_elem.e_sub_elements->front()); + + if (key_str.empty() + && el->front().get_pair_value().value_token() + == DNT_GROUP) + { + element_to_json(gen, dp, el->front().get_pair_value()); + } else { + yajlpp_map singleton_map(gen); + + if (key_str.empty()) { + key_str = "col_0"; + } + singleton_map.gen(key_str); + element_to_json(gen, dp, pair_elem.get_pair_value()); + } + } else { + map_elements_to_json(gen, dp, el); + } + break; + } + default: + list_elements_to_json(gen, dp, el); + break; + } + } +} diff --git a/src/elem_to_json.hh b/src/elem_to_json.hh new file mode 100644 index 0000000..f4c14b2 --- /dev/null +++ b/src/elem_to_json.hh @@ -0,0 +1,41 @@ +/** + * Copyright (c) 2016, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef elem_to_json_hh +#define elem_to_json_hh + +#include "data_parser.hh" +#include "yajl/api/yajl_gen.h" + +void elements_to_json(yajl_gen gen, + data_parser& dp, + data_parser::element_list_t* el, + bool root = true); + +#endif diff --git a/src/emojis.json b/src/emojis.json new file mode 100644 index 0000000..62f2507 --- /dev/null +++ b/src/emojis.json @@ -0,0 +1,4036 @@ +{ + "emojis": [ + {"emoji": "👩‍👩‍👧‍👧", "name": "family: woman, woman, girl, girl", "shortname": ":woman_woman_girl_girl:", "unicode": "1F469 200D 1F469 200D 1F467 200D 1F467", "html": "👩‍👩‍👧‍👧", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩‍👩‍👧‍👦", "name": "family: woman, woman, girl, boy", "shortname": ":woman_woman_girl_boy:", "unicode": "1F469 200D 1F469 200D 1F467 200D 1F466", "html": "👩‍👩‍👧‍👦", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩‍👩‍👦‍👦", "name": "family: woman, woman, boy, boy", "shortname": ":woman_woman_boy_boy:", "unicode": "1F469 200D 1F469 200D 1F466 200D 1F466", "html": "👩‍👩‍👦‍👦", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨‍👩‍👧‍👧", "name": "family: man, woman, girl, girl", "shortname": ":man_woman_girl_girl:", "unicode": "1F468 200D 1F469 200D 1F467 200D 1F467", "html": "👨‍👩‍👧‍👧", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨‍👩‍👧‍👦", "name": "family: man, woman, girl, boy", "shortname": ":man_woman_girl_boy:", "unicode": "1F468 200D 1F469 200D 1F467 200D 1F466", "html": "👨‍👩‍👧‍👦", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨‍👩‍👦‍👦", "name": "family: man, woman, boy, boy", "shortname": ":man_woman_boy_boy:", "unicode": "1F468 200D 1F469 200D 1F466 200D 1F466", "html": "👨‍👩‍👦‍👦", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨‍👨‍👧‍👧", "name": "family: man, man, girl, girl", "shortname": ":man_man_girl_girl:", "unicode": "1F468 200D 1F468 200D 1F467 200D 1F467", "html": "👨‍👨‍👧‍👧", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨‍👨‍👧‍👦", "name": "family: man, man, girl, boy", "shortname": ":man_man_girl_boy:", "unicode": "1F468 200D 1F468 200D 1F467 200D 1F466", "html": "👨‍👨‍👧‍👦", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨‍👨‍👦‍👦", "name": "family: man, man, boy, boy", "shortname": ":man_man_boy_boy:", "unicode": "1F468 200D 1F468 200D 1F466 200D 1F466", "html": "👨‍👨‍👦‍👦", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩‍👩‍👧", "name": "family: woman, woman, girl", "shortname": ":woman_woman_girl:", "unicode": "1F469 200D 1F469 200D 1F467", "html": "👩‍👩‍👧", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩‍👩‍👦", "name": "family: woman, woman, boy", "shortname": ":woman_woman_boy:", "unicode": "1F469 200D 1F469 200D 1F466", "html": "👩‍👩‍👦", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩‍👧‍👧", "name": "family: woman, girl, girl", "shortname": ":woman_girl_girl:", "unicode": "1F469 200D 1F467 200D 1F467", "html": "👩‍👧‍👧", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩‍👧‍👦", "name": "family: woman, girl, boy", "shortname": ":woman_girl_boy:", "unicode": "1F469 200D 1F467 200D 1F466", "html": "👩‍👧‍👦", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩‍👦‍👦", "name": "family: woman, boy, boy", "shortname": ":woman_boy_boy:", "unicode": "1F469 200D 1F466 200D 1F466", "html": "👩‍👦‍👦", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨‍👩‍👧", "name": "family: man, woman, girl", "shortname": ":man_woman_girl:", "unicode": "1F468 200D 1F469 200D 1F467", "html": "👨‍👩‍👧", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨‍👨‍👧", "name": "family: man, man, girl", "shortname": ":man_man_girl:", "unicode": "1F468 200D 1F468 200D 1F467", "html": "👨‍👨‍👧", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨‍👨‍👦", "name": "family: man, man, boy", "shortname": ":man_man_boy:", "unicode": "1F468 200D 1F468 200D 1F466", "html": "👨‍👨‍👦", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨‍👧‍👧", "name": "family: man, girl, girl", "shortname": ":man_girl_girl:", "unicode": "1F468 200D 1F467 200D 1F467", "html": "👨‍👧‍👧", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨‍👧‍👦", "name": "family: man, girl, boy", "shortname": ":man_girl_boy:", "unicode": "1F468 200D 1F467 200D 1F466", "html": "👨‍👧‍👦", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨‍👦‍👦", "name": "family: man, boy, boy", "shortname": ":man_boy_boy:", "unicode": "1F468 200D 1F466 200D 1F466", "html": "👨‍👦‍👦", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩‍👧", "name": "family: woman, girl", "shortname": ":woman_girl:", "unicode": "1F469 200D 1F467", "html": "👩‍👧", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩‍👦", "name": "family: woman, boy", "shortname": ":woman_boy:", "unicode": "1F469 200D 1F466", "html": "👩‍👦", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨‍👧", "name": "family: man, girl", "shortname": ":man_girl:", "unicode": "1F468 200D 1F467", "html": "👨‍👧", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨‍👦", "name": "family: man, boy", "shortname": ":man_boy:", "unicode": "1F468 200D 1F466", "html": "👨‍👦", "category": "People & Body (family)", "order": ""}, + {"emoji": "😂", "name": "face with tears of joy", "shortname": ":joy:", "unicode": "1f602", "html": "😂", "category": "Smileys & Emotion (face-smiling)", "order": "3"}, + {"emoji": "❤️", "name": "red heart", "shortname": ":heart:", "unicode": "2764", "html": "❤", "category": "Smileys & Emotion (emotion)", "order": "1286"}, + {"emoji": "♥️", "name": "heart suit", "shortname": ":heart_suit:", "unicode": "2665 FE0F", "html": "♥️", "category": "Activities (game)", "order": ""}, + {"emoji": "😍", "name": "smiling face with heart-eyes", "shortname": ":heart_eyes:", "unicode": "1f60d", "html": "😍", "category": "Smileys & Emotion (face-affection)", "order": "13"}, + {"emoji": "😭", "name": "loudly crying face", "shortname": ":sob:", "unicode": "1f62d", "html": "😭", "category": "Smileys & Emotion (face-concerned)", "order": "55"}, + {"emoji": "😊", "name": "smiling face with smiling eyes", "shortname": ":blush:", "unicode": "1f60a", "html": "😊", "category": "Smileys & Emotion (face-smiling)", "order": "10"}, + {"emoji": "😒", "name": "unamused face", "shortname": ":unamused:", "unicode": "1f612", "html": "😒", "category": "Smileys & Emotion (face-neutral-skeptical)", "order": "41"}, + {"emoji": "😘", "name": "face blowing a kiss", "shortname": ":kissing_heart:", "unicode": "1f618", "html": "😘", "category": "Smileys & Emotion (face-affection)", "order": "14"}, + {"emoji": "💕", "name": "two hearts", "shortname": ":two_hearts:", "unicode": "1f495", "html": "💕", "category": "Smileys & Emotion (emotion)", "order": "1289"}, + {"emoji": "☺️", "name": "smiling face", "shortname": ":smiling:", "unicode": "263A FE0F", "html": "☺", "category": "Smileys & Emotion (face-affection)", "order": ""}, + {"emoji": "😩", "name": "weary face", "shortname": ":weary:", "unicode": "1f629", "html": "😩", "category": "Smileys & Emotion (face-concerned)", "order": "59"}, + {"emoji": "👌🏿", "name": "OK hand: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F44C 1F3FF", "html": "👌🏿", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "👌🏾", "name": "OK hand: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F44C 1F3FE", "html": "👌🏾", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "👌🏽", "name": "OK hand: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F44C 1F3FD", "html": "👌🏽", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "👌🏼", "name": "OK hand: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F44C 1F3FC", "html": "👌🏼", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "👌🏻", "name": "OK hand: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F44C 1F3FB", "html": "👌🏻", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "👌", "name": "OK hand", "shortname": ":ok_hand:", "unicode": "1f44c", "html": "👌", "category": "People & Body (hand-fingers-partial)", "order": "1170"}, + {"emoji": "😔", "name": "pensive face", "shortname": ":pensive:", "unicode": "1f614", "html": "😔", "category": "Smileys & Emotion (face-sleepy)", "order": "43"}, + {"emoji": "😏", "name": "smirking face", "shortname": ":smirk:", "unicode": "1f60f", "html": "😏", "category": "Smileys & Emotion (face-neutral-skeptical)", "order": "26"}, + {"emoji": "😁", "name": "beaming face with smiling eyes", "shortname": ":grin:", "unicode": "1f601", "html": "😁", "category": "Smileys & Emotion (face-smiling)", "order": "2"}, + {"emoji": "♻", "name": "recycling symbol", "shortname": ":recycle:", "unicode": "267b", "html": "♻", "category": "Symbols (other-symbol)", "order": "2072"}, + {"emoji": "😉", "name": "winking face", "shortname": ":wink:", "unicode": "1f609", "html": "😉", "category": "Smileys & Emotion (face-smiling)", "order": "9"}, + {"emoji": "👍🏿", "name": "thumbs up: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F44D 1F3FF", "html": "👍🏿", "category": "People & Body (hand-fingers-closed)", "order": ""}, + {"emoji": "👍🏾", "name": "thumbs up: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F44D 1F3FE", "html": "👍🏾", "category": "People & Body (hand-fingers-closed)", "order": ""}, + {"emoji": "👍🏽", "name": "thumbs up: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F44D 1F3FD", "html": "👍🏽", "category": "People & Body (hand-fingers-closed)", "order": ""}, + {"emoji": "👍🏼", "name": "thumbs up: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F44D 1F3FC", "html": "👍🏼", "category": "People & Body (hand-fingers-closed)", "order": ""}, + {"emoji": "👍🏻", "name": "thumbs up: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F44D 1F3FB", "html": "👍🏻", "category": "People & Body (hand-fingers-closed)", "order": ""}, + {"emoji": "👍", "name": "thumbs up", "shortname": ":thumbsup:", "unicode": "1f44d", "html": "👍", "category": "People & Body (hand-fingers-closed)", "order": "1176"}, + {"emoji": "🙏🏿", "name": "folded hands: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F64F 1F3FF", "html": "🙏🏿", "category": "People & Body (hands)", "order": ""}, + {"emoji": "🙏🏾", "name": "folded hands: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F64F 1F3FE", "html": "🙏🏾", "category": "People & Body (hands)", "order": ""}, + {"emoji": "🙏🏽", "name": "folded hands: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F64F 1F3FD", "html": "🙏🏽", "category": "People & Body (hands)", "order": ""}, + {"emoji": "🙏🏼", "name": "folded hands: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F64F 1F3FC", "html": "🙏🏼", "category": "People & Body (hands)", "order": ""}, + {"emoji": "🙏🏻", "name": "folded hands: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F64F 1F3FB", "html": "🙏🏻", "category": "People & Body (hands)", "order": ""}, + {"emoji": "🙏", "name": "folded hands", "shortname": ":pray:", "unicode": "1f64f", "html": "🙏", "category": "People & Body (hands)", "order": "1248"}, + {"emoji": "😌", "name": "relieved face", "shortname": ":relieved:", "unicode": "1f60c", "html": "😌", "category": "Smileys & Emotion (face-sleepy)", "order": "35"}, + {"emoji": "🎶", "name": "musical notes", "shortname": ":notes:", "unicode": "1f3b6", "html": "🎶", "category": "Objects (music)", "order": "1825"}, + {"emoji": "😳", "name": "flushed face", "shortname": ":flushed:", "unicode": "1f633", "html": "😳", "category": "Smileys & Emotion (face-concerned)", "order": "63"}, + {"emoji": "🙌🏿", "name": "raising hands: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F64C 1F3FF", "html": "🙌🏿", "category": "People & Body (hands)", "order": ""}, + {"emoji": "🙌🏾", "name": "raising hands: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F64C 1F3FE", "html": "🙌🏾", "category": "People & Body (hands)", "order": ""}, + {"emoji": "🙌🏽", "name": "raising hands: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F64C 1F3FD", "html": "🙌🏽", "category": "People & Body (hands)", "order": ""}, + {"emoji": "🙌🏼", "name": "raising hands: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F64C 1F3FC", "html": "🙌🏼", "category": "People & Body (hands)", "order": ""}, + {"emoji": "🙌🏻", "name": "raising hands: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F64C 1F3FB", "html": "🙌🏻", "category": "People & Body (hands)", "order": ""}, + {"emoji": "🙌", "name": "raising hands", "shortname": ":raised_hands:", "unicode": "1f64c", "html": "🙌", "category": "People & Body (hands)", "order": "1242"}, + {"emoji": "🙈", "name": "see-no-evil monkey", "shortname": ":see_no_evil:", "unicode": "1f648", "html": "🙈", "category": "Smileys & Emotion (monkey-face)", "order": "96"}, + {"emoji": "😢", "name": "crying face", "shortname": ":cry:", "unicode": "1f622", "html": "😢", "category": "Smileys & Emotion (face-concerned)", "order": "54"}, + {"emoji": "😎", "name": "smiling face with sunglasses", "shortname": ":sunglasses:", "unicode": "1f60e", "html": "😎", "category": "Smileys & Emotion (face-glasses)", "order": "12"}, + {"emoji": "✌🏿", "name": "victory hand: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "270C 1F3FF", "html": "✌🏿", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "✌🏾", "name": "victory hand: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "270C 1F3FE", "html": "✌🏾", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "✌🏽", "name": "victory hand: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "270C 1F3FD", "html": "✌🏽", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "✌🏼", "name": "victory hand: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "270C 1F3FC", "html": "✌🏼", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "✌🏻", "name": "victory hand: light skin tone", "shortname": ":light_skin_tone:", "unicode": "270C 1F3FB", "html": "✌🏻", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "✌️", "name": "victory hand", "shortname": ":v:", "unicode": "270c", "html": "✌", "category": "People & Body (hand-fingers-partial)", "order": "1128"}, + {"emoji": "👀", "name": "eyes", "shortname": ":eyes:", "unicode": "1f440", "html": "👀", "category": "People & Body (body-parts)", "order": "1279"}, + {"emoji": "😅", "name": "grinning face with sweat", "shortname": ":sweat_smile:", "unicode": "1f605", "html": "😅", "category": "Smileys & Emotion (face-smiling)", "order": "7"}, + {"emoji": "✨", "name": "sparkles", "shortname": ":sparkles:", "unicode": "2728", "html": "✨", "category": "Activities (event)", "order": "1760"}, + {"emoji": "😴", "name": "sleeping face", "shortname": ":sleeping:", "unicode": "1f634", "html": "😴", "category": "Smileys & Emotion (face-sleepy)", "order": "34"}, + {"emoji": "😄", "name": "grinning face with smiling eyes", "shortname": ":smile:", "unicode": "1f604", "html": "😄", "category": "Smileys & Emotion (face-smiling)", "order": "6"}, + {"emoji": "💜", "name": "purple heart", "shortname": ":purple_heart:", "unicode": "1f49c", "html": "💜", "category": "Smileys & Emotion (emotion)", "order": "1295"}, + {"emoji": "💔", "name": "broken heart", "shortname": ":broken_heart:", "unicode": "1f494", "html": "💔", "category": "Smileys & Emotion (emotion)", "order": "1288"}, + {"emoji": "💯", "name": "hundred points", "shortname": ":hundred_points:", "unicode": "1F4AF", "html": "💯", "category": "Smileys & Emotion (emotion)", "order": ""}, + {"emoji": "😑", "name": "expressionless face", "shortname": ":expressionless:", "unicode": "1f611", "html": "😑", "category": "Smileys & Emotion (face-neutral-skeptical)", "order": "23"}, + {"emoji": "💖", "name": "sparkling heart", "shortname": ":sparkling_heart:", "unicode": "1f496", "html": "💖", "category": "Smileys & Emotion (emotion)", "order": "1290"}, + {"emoji": "💙", "name": "blue heart", "shortname": ":blue_heart:", "unicode": "1f499", "html": "💙", "category": "Smileys & Emotion (emotion)", "order": "1292"}, + {"emoji": "😕", "name": "confused face", "shortname": ":confused:", "unicode": "1f615", "html": "😕", "category": "Smileys & Emotion (face-concerned)", "order": "44"}, + {"emoji": "💁🏿‍♂", "name": "man tipping hand: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F481 1F3FF 200D 2642", "html": "💁🏿‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "💁🏾‍♂", "name": "man tipping hand: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F481 1F3FE 200D 2642", "html": "💁🏾‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "💁🏽‍♂", "name": "man tipping hand: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F481 1F3FD 200D 2642", "html": "💁🏽‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "💁🏼‍♂", "name": "man tipping hand: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F481 1F3FC 200D 2642", "html": "💁🏼‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "💁🏻‍♂", "name": "man tipping hand: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F481 1F3FB 200D 2642", "html": "💁🏻‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "💁‍♂", "name": "man tipping hand", "shortname": ":man_tipping_hand:", "unicode": "1F481 200D 2642", "html": "💁‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "💁🏿", "name": "person tipping hand: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F481 1F3FF", "html": "💁🏿", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "💁🏾", "name": "person tipping hand: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F481 1F3FE", "html": "💁🏾", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "💁🏽", "name": "person tipping hand: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F481 1F3FD", "html": "💁🏽", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "💁🏼", "name": "person tipping hand: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F481 1F3FC", "html": "💁🏼", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "💁🏻", "name": "person tipping hand: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F481 1F3FB", "html": "💁🏻", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "💁", "name": "person tipping hand", "shortname": ":information_desk_person:", "unicode": "1f481", "html": "💁", "category": "People & Body (person-gesture)", "order": "567"}, + {"emoji": "😜", "name": "winking face with tongue", "shortname": ":stuck_out_tongue_winking_eye:", "unicode": "1f61c", "html": "😜", "category": "Smileys & Emotion (face-tongue)", "order": "38"}, + {"emoji": "😞", "name": "disappointed face", "shortname": ":disappointed:", "unicode": "1f61e", "html": "😞", "category": "Smileys & Emotion (face-concerned)", "order": "51"}, + {"emoji": "😋", "name": "face savoring food", "shortname": ":yum:", "unicode": "1f60b", "html": "😋", "category": "Smileys & Emotion (face-tongue)", "order": "11"}, + {"emoji": "😐", "name": "neutral face", "shortname": ":neutral_face:", "unicode": "1f610", "html": "😐", "category": "Smileys & Emotion (face-neutral-skeptical)", "order": "22"}, + {"emoji": "😪", "name": "sleepy face", "shortname": ":sleepy:", "unicode": "1f62a", "html": "😪", "category": "Smileys & Emotion (face-sleepy)", "order": "32"}, + {"emoji": "👏🏿", "name": "clapping hands: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F44F 1F3FF", "html": "👏🏿", "category": "People & Body (hands)", "order": ""}, + {"emoji": "👏🏾", "name": "clapping hands: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F44F 1F3FE", "html": "👏🏾", "category": "People & Body (hands)", "order": ""}, + {"emoji": "👏🏽", "name": "clapping hands: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F44F 1F3FD", "html": "👏🏽", "category": "People & Body (hands)", "order": ""}, + {"emoji": "👏🏼", "name": "clapping hands: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F44F 1F3FC", "html": "👏🏼", "category": "People & Body (hands)", "order": ""}, + {"emoji": "👏🏻", "name": "clapping hands: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F44F 1F3FB", "html": "👏🏻", "category": "People & Body (hands)", "order": ""}, + {"emoji": "👏", "name": "clapping hands", "shortname": ":clap:", "unicode": "1f44f", "html": "👏", "category": "People & Body (hands)", "order": "1224"}, + {"emoji": "💘", "name": "heart with arrow", "shortname": ":cupid:", "unicode": "1f498", "html": "💘", "category": "Smileys & Emotion (emotion)", "order": "1285"}, + {"emoji": "💗", "name": "growing heart", "shortname": ":heartpulse:", "unicode": "1f497", "html": "💗", "category": "Smileys & Emotion (emotion)", "order": "1291"}, + {"emoji": "💞", "name": "revolving hearts", "shortname": ":revolving_hearts:", "unicode": "1f49e", "html": "💞", "category": "Smileys & Emotion (emotion)", "order": "1298"}, + {"emoji": "⬅️", "name": "left arrow", "shortname": ":arrow_left:", "unicode": "2b05", "html": "⬅", "category": "Symbols (arrow)", "order": "2008"}, + {"emoji": "🙊", "name": "speak-no-evil monkey", "shortname": ":speak_no_evil:", "unicode": "1f64a", "html": "🙊", "category": "Smileys & Emotion (monkey-face)", "order": "98"}, + {"emoji": "✋🏿", "name": "raised hand: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "270B 1F3FF", "html": "✋🏿", "category": "People & Body (hand-fingers-open)", "order": ""}, + {"emoji": "✋🏾", "name": "raised hand: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "270B 1F3FE", "html": "✋🏾", "category": "People & Body (hand-fingers-open)", "order": ""}, + {"emoji": "✋🏽", "name": "raised hand: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "270B 1F3FD", "html": "✋🏽", "category": "People & Body (hand-fingers-open)", "order": ""}, + {"emoji": "✋🏼", "name": "raised hand: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "270B 1F3FC", "html": "✋🏼", "category": "People & Body (hand-fingers-open)", "order": ""}, + {"emoji": "✋🏻", "name": "raised hand: light skin tone", "shortname": ":light_skin_tone:", "unicode": "270B 1F3FB", "html": "✋🏻", "category": "People & Body (hand-fingers-open)", "order": ""}, + {"emoji": "✋", "name": "raised hand", "shortname": ":raised_hand:", "unicode": "270B", "html": "✋", "category": "People & Body (hand-fingers-open)", "order": ""}, + {"emoji": "💋", "name": "kiss mark", "shortname": ":kiss:", "unicode": "1f48b", "html": "💋", "category": "Smileys & Emotion (emotion)", "order": "1284"}, + {"emoji": "👉🏿", "name": "backhand index pointing right: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F449 1F3FF", "html": "👉🏿", "category": "People & Body (hand-single-finger)", "order": ""}, + {"emoji": "👉🏾", "name": "backhand index pointing right: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F449 1F3FE", "html": "👉🏾", "category": "People & Body (hand-single-finger)", "order": ""}, + {"emoji": "👉🏽", "name": "backhand index pointing right: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F449 1F3FD", "html": "👉🏽", "category": "People & Body (hand-single-finger)", "order": ""}, + {"emoji": "👉🏼", "name": "backhand index pointing right: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F449 1F3FC", "html": "👉🏼", "category": "People & Body (hand-single-finger)", "order": ""}, + {"emoji": "👉🏻", "name": "backhand index pointing right: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F449 1F3FB", "html": "👉🏻", "category": "People & Body (hand-single-finger)", "order": ""}, + {"emoji": "👉", "name": "backhand index pointing right", "shortname": ":point_right:", "unicode": "1f449", "html": "👉", "category": "People & Body (hand-single-finger)", "order": "1098"}, + {"emoji": "🌸", "name": "cherry blossom", "shortname": ":cherry_blossom:", "unicode": "1f338", "html": "🌸", "category": "Animals & Nature (plant-flower)", "order": "1428"}, + {"emoji": "😱", "name": "face screaming in fear", "shortname": ":scream:", "unicode": "1f631", "html": "😱", "category": "Smileys & Emotion (face-concerned)", "order": "62"}, + {"emoji": "🔥", "name": "fire", "shortname": ":fire:", "unicode": "1f525", "html": "🔥", "category": "Travel & Places (sky & weather)", "order": "1753"}, + {"emoji": "😡", "name": "pouting face", "shortname": ":rage:", "unicode": "1f621", "html": "😡", "category": "Smileys & Emotion (face-negative)", "order": "65"}, + {"emoji": "😃", "name": "grinning face with big eyes", "shortname": ":smiley:", "unicode": "1f603", "html": "😃", "category": "Smileys & Emotion (face-smiling)", "order": "5"}, + {"emoji": "🎉", "name": "party popper", "shortname": ":tada:", "unicode": "1f389", "html": "🎉", "category": "Activities (event)", "order": "1762"}, + {"emoji": "👊🏿", "name": "oncoming fist: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F44A 1F3FF", "html": "👊🏿", "category": "People & Body (hand-fingers-closed)", "order": ""}, + {"emoji": "👊🏾", "name": "oncoming fist: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F44A 1F3FE", "html": "👊🏾", "category": "People & Body (hand-fingers-closed)", "order": ""}, + {"emoji": "👊🏽", "name": "oncoming fist: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F44A 1F3FD", "html": "👊🏽", "category": "People & Body (hand-fingers-closed)", "order": ""}, + {"emoji": "👊🏼", "name": "oncoming fist: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F44A 1F3FC", "html": "👊🏼", "category": "People & Body (hand-fingers-closed)", "order": ""}, + {"emoji": "👊🏻", "name": "oncoming fist: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F44A 1F3FB", "html": "👊🏻", "category": "People & Body (hand-fingers-closed)", "order": ""}, + {"emoji": "👊", "name": "oncoming fist", "shortname": ":oncoming_fist:", "unicode": "1F44A", "html": "👊", "category": "People & Body (hand-fingers-closed)", "order": ""}, + {"emoji": "😫", "name": "tired face", "shortname": ":tired_face:", "unicode": "1f62b", "html": "😫", "category": "Smileys & Emotion (face-concerned)", "order": "33"}, + {"emoji": "📷", "name": "camera", "shortname": ":camera:", "unicode": "1f4f7", "html": "📷", "category": "Objects (light & video)", "order": "1861"}, + {"emoji": "🌹", "name": "rose", "shortname": ":rose:", "unicode": "1f339", "html": "🌹", "category": "Animals & Nature (plant-flower)", "order": "1431"}, + {"emoji": "😝", "name": "squinting face with tongue", "shortname": ":stuck_out_tongue_closed_eyes:", "unicode": "1f61d", "html": "😝", "category": "Smileys & Emotion (face-tongue)", "order": "39"}, + {"emoji": "💪🏿", "name": "flexed biceps: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F4AA 1F3FF", "html": "💪🏿", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "💪🏾", "name": "flexed biceps: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F4AA 1F3FE", "html": "💪🏾", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "💪🏽", "name": "flexed biceps: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F4AA 1F3FD", "html": "💪🏽", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "💪🏼", "name": "flexed biceps: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F4AA 1F3FC", "html": "💪🏼", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "💪🏻", "name": "flexed biceps: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F4AA 1F3FB", "html": "💪🏻", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "💪", "name": "flexed biceps", "shortname": ":muscle:", "unicode": "1f4aa", "html": "💪", "category": "People & Body (body-parts)", "order": "1080"}, + {"emoji": "💀", "name": "skull", "shortname": ":skull:", "unicode": "1f480", "html": "💀", "category": "Smileys & Emotion (face-negative)", "order": "80"}, + {"emoji": "☀️", "name": "sun", "shortname": ":sunny:", "unicode": "2600", "html": "☀", "category": "Travel & Places (sky & weather)", "order": "1724"}, + {"emoji": "💛", "name": "yellow heart", "shortname": ":yellow_heart:", "unicode": "1f49b", "html": "💛", "category": "Smileys & Emotion (emotion)", "order": "1294"}, + {"emoji": "😤", "name": "face with steam from nose", "shortname": ":triumph:", "unicode": "1f624", "html": "😤", "category": "Smileys & Emotion (face-negative)", "order": "53"}, + {"emoji": "🌚", "name": "new moon face", "shortname": ":new_moon_with_face:", "unicode": "1f31a", "html": "🌚", "category": "Travel & Places (sky & weather)", "order": "1720"}, + {"emoji": "😆", "name": "grinning squinting face", "shortname": ":laughing:", "unicode": "1f606", "html": "😆", "category": "Smileys & Emotion (face-smiling)", "order": "8"}, + {"emoji": "😓", "name": "downcast face with sweat", "shortname": ":sweat:", "unicode": "1f613", "html": "😓", "category": "Smileys & Emotion (face-concerned)", "order": "42"}, + {"emoji": "👈🏿", "name": "backhand index pointing left: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F448 1F3FF", "html": "👈🏿", "category": "People & Body (hand-single-finger)", "order": ""}, + {"emoji": "👈🏾", "name": "backhand index pointing left: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F448 1F3FE", "html": "👈🏾", "category": "People & Body (hand-single-finger)", "order": ""}, + {"emoji": "👈🏽", "name": "backhand index pointing left: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F448 1F3FD", "html": "👈🏽", "category": "People & Body (hand-single-finger)", "order": ""}, + {"emoji": "👈🏼", "name": "backhand index pointing left: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F448 1F3FC", "html": "👈🏼", "category": "People & Body (hand-single-finger)", "order": ""}, + {"emoji": "👈🏻", "name": "backhand index pointing left: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F448 1F3FB", "html": "👈🏻", "category": "People & Body (hand-single-finger)", "order": ""}, + {"emoji": "👈", "name": "backhand index pointing left", "shortname": ":point_left:", "unicode": "1f448", "html": "👈", "category": "People & Body (hand-single-finger)", "order": "1092"}, + {"emoji": "✔️", "name": "check mark", "shortname": ":heavy_check_mark:", "unicode": "2714", "html": "✔", "category": "Symbols (other-symbol)", "order": "2080"}, + {"emoji": "😻", "name": "smiling cat with heart-eyes", "shortname": ":heart_eyes_cat:", "unicode": "1f63b", "html": "😻", "category": "Smileys & Emotion (cat-face)", "order": "90"}, + {"emoji": "😀", "name": "grinning face", "shortname": ":grinning:", "unicode": "1f600", "html": "😀", "category": "Smileys & Emotion (face-smiling)", "order": "1"}, + {"emoji": "😷", "name": "face with medical mask", "shortname": ":mask:", "unicode": "1f637", "html": "😷", "category": "Smileys & Emotion (face-unwell)", "order": "71"}, + {"emoji": "💚", "name": "green heart", "shortname": ":green_heart:", "unicode": "1f49a", "html": "💚", "category": "Smileys & Emotion (emotion)", "order": "1293"}, + {"emoji": "👋🏿", "name": "waving hand: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F44B 1F3FF", "html": "👋🏿", "category": "People & Body (hand-fingers-open)", "order": ""}, + {"emoji": "👋🏾", "name": "waving hand: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F44B 1F3FE", "html": "👋🏾", "category": "People & Body (hand-fingers-open)", "order": ""}, + {"emoji": "👋🏽", "name": "waving hand: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F44B 1F3FD", "html": "👋🏽", "category": "People & Body (hand-fingers-open)", "order": ""}, + {"emoji": "👋🏼", "name": "waving hand: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F44B 1F3FC", "html": "👋🏼", "category": "People & Body (hand-fingers-open)", "order": ""}, + {"emoji": "👋🏻", "name": "waving hand: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F44B 1F3FB", "html": "👋🏻", "category": "People & Body (hand-fingers-open)", "order": ""}, + {"emoji": "👋", "name": "waving hand", "shortname": ":wave:", "unicode": "1f44b", "html": "👋", "category": "People & Body (hand-fingers-open)", "order": "1218"}, + {"emoji": "😣", "name": "persevering face", "shortname": ":persevere:", "unicode": "1f623", "html": "😣", "category": "Smileys & Emotion (face-concerned)", "order": "27"}, + {"emoji": "💓", "name": "beating heart", "shortname": ":heartbeat:", "unicode": "1f493", "html": "💓", "category": "Smileys & Emotion (emotion)", "order": "1287"}, + {"emoji": "▶️", "name": "play button", "shortname": ":arrow_forward:", "unicode": "25b6", "html": "▶", "category": "Symbols (av-symbol)", "order": "2051"}, + {"emoji": "◀️", "name": "reverse button", "shortname": ":arrow_backward:", "unicode": "25c0", "html": "◀", "category": "Symbols (av-symbol)", "order": "2055"}, + {"emoji": "↪️", "name": "left arrow curving right", "shortname": ":arrow_right_hook:", "unicode": "21aa", "html": "↪", "category": "Symbols (arrow)", "order": "2013"}, + {"emoji": "↩️", "name": "right arrow curving left", "shortname": ":leftwards_arrow_with_hook:", "unicode": "21a9", "html": "↩", "category": "Symbols (arrow)", "order": "2012"}, + {"emoji": "👑", "name": "crown", "shortname": ":crown:", "unicode": "1f451", "html": "👑", "category": "Objects (clothing)", "order": "1333"}, + {"emoji": "😚", "name": "kissing face with closed eyes", "shortname": ":kissing_closed_eyes:", "unicode": "1f61a", "html": "😚", "category": "Smileys & Emotion (face-affection)", "order": "17"}, + {"emoji": "😛", "name": "face with tongue", "shortname": ":stuck_out_tongue:", "unicode": "1f61b", "html": "😛", "category": "Smileys & Emotion (face-tongue)", "order": "37"}, + {"emoji": "😥", "name": "sad but relieved face", "shortname": ":disappointed_relieved:", "unicode": "1f625", "html": "😥", "category": "Smileys & Emotion (face-concerned)", "order": "28"}, + {"emoji": "😇", "name": "smiling face with halo", "shortname": ":innocent:", "unicode": "1f607", "html": "😇", "category": "Smileys & Emotion (face-smiling)", "order": "67"}, + {"emoji": "🎧", "name": "headphone", "shortname": ":headphones:", "unicode": "1f3a7", "html": "🎧", "category": "Objects (music)", "order": "1830"}, + {"emoji": "✅", "name": "check mark button", "shortname": ":white_check_mark:", "unicode": "2705", "html": "✅", "category": "Symbols (other-symbol)", "order": "2078"}, + {"emoji": "😖", "name": "confounded face", "shortname": ":confounded:", "unicode": "1f616", "html": "😖", "category": "Smileys & Emotion (face-concerned)", "order": "50"}, + {"emoji": "➡", "name": "right arrow", "shortname": ":arrow_right:", "unicode": "27a1", "html": "➡", "category": "Symbols (arrow)", "order": "2004"}, + {"emoji": "😠", "name": "angry face", "shortname": ":angry:", "unicode": "1f620", "html": "😠", "category": "Smileys & Emotion (face-negative)", "order": "66"}, + {"emoji": "😬", "name": "grimacing face", "shortname": ":grimacing:", "unicode": "1f62c", "html": "😬", "category": "Smileys & Emotion (face-neutral-skeptical)", "order": "60"}, + {"emoji": "🌟", "name": "glowing star", "shortname": ":star2:", "unicode": "1f31f", "html": "🌟", "category": "Travel & Places (sky & weather)", "order": "1728"}, + {"emoji": "🔫", "name": "pistol", "shortname": ":gun:", "unicode": "1f52b", "html": "🔫", "category": "Objects (tool)", "order": "1956"}, + {"emoji": "🙋🏿‍♂", "name": "man raising hand: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F64B 1F3FF 200D 2642", "html": "🙋🏿‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙋🏾‍♂", "name": "man raising hand: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F64B 1F3FE 200D 2642", "html": "🙋🏾‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙋🏽‍♂", "name": "man raising hand: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F64B 1F3FD 200D 2642", "html": "🙋🏽‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙋🏼‍♂", "name": "man raising hand: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F64B 1F3FC 200D 2642", "html": "🙋🏼‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙋🏻‍♂", "name": "man raising hand: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F64B 1F3FB 200D 2642", "html": "🙋🏻‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙋‍♂", "name": "man raising hand", "shortname": ":man_raising_hand:", "unicode": "1F64B 200D 2642", "html": "🙋‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙋🏿", "name": "person raising hand: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F64B 1F3FF", "html": "🙋🏿", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙋🏾", "name": "person raising hand: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F64B 1F3FE", "html": "🙋🏾", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙋🏽", "name": "person raising hand: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F64B 1F3FD", "html": "🙋🏽", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙋🏼", "name": "person raising hand: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F64B 1F3FC", "html": "🙋🏼", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙋🏻", "name": "person raising hand: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F64B 1F3FB", "html": "🙋🏻", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙋", "name": "person raising hand", "shortname": ":raising_hand:", "unicode": "1f64b", "html": "🙋", "category": "People & Body (person-gesture)", "order": "585"}, + {"emoji": "👎🏿", "name": "thumbs down: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F44E 1F3FF", "html": "👎🏿", "category": "People & Body (hand-fingers-closed)", "order": ""}, + {"emoji": "👎🏾", "name": "thumbs down: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F44E 1F3FE", "html": "👎🏾", "category": "People & Body (hand-fingers-closed)", "order": ""}, + {"emoji": "👎🏽", "name": "thumbs down: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F44E 1F3FD", "html": "👎🏽", "category": "People & Body (hand-fingers-closed)", "order": ""}, + {"emoji": "👎🏼", "name": "thumbs down: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F44E 1F3FC", "html": "👎🏼", "category": "People & Body (hand-fingers-closed)", "order": ""}, + {"emoji": "👎🏻", "name": "thumbs down: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F44E 1F3FB", "html": "👎🏻", "category": "People & Body (hand-fingers-closed)", "order": ""}, + {"emoji": "👎", "name": "thumbs down", "shortname": ":thumbsdown:", "unicode": "1f44e", "html": "👎", "category": "People & Body (hand-fingers-closed)", "order": "1182"}, + {"emoji": "💃🏿", "name": "woman dancing: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F483 1F3FF", "html": "💃🏿", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💃🏾", "name": "woman dancing: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F483 1F3FE", "html": "💃🏾", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💃🏽", "name": "woman dancing: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F483 1F3FD", "html": "💃🏽", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💃🏼", "name": "woman dancing: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F483 1F3FC", "html": "💃🏼", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💃🏻", "name": "woman dancing: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F483 1F3FB", "html": "💃🏻", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💃", "name": "woman dancing", "shortname": ":dancer:", "unicode": "1f483", "html": "💃", "category": "People & Body (person-activity)", "order": "729"}, + {"emoji": "🎵", "name": "musical note", "shortname": ":musical_note:", "unicode": "1f3b5", "html": "🎵", "category": "Objects (music)", "order": "1824"}, + {"emoji": "😶", "name": "face without mouth", "shortname": ":no_mouth:", "unicode": "1f636", "html": "😶", "category": "Smileys & Emotion (face-neutral-skeptical)", "order": "24"}, + {"emoji": "💫", "name": "dizzy", "shortname": ":dizzy:", "unicode": "1f4ab", "html": "💫", "category": "Smileys & Emotion (emotion)", "order": "1308"}, + {"emoji": "✊🏿", "name": "raised fist: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "270A 1F3FF", "html": "✊🏿", "category": "People & Body (hand-fingers-closed)", "order": ""}, + {"emoji": "✊🏾", "name": "raised fist: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "270A 1F3FE", "html": "✊🏾", "category": "People & Body (hand-fingers-closed)", "order": ""}, + {"emoji": "✊🏽", "name": "raised fist: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "270A 1F3FD", "html": "✊🏽", "category": "People & Body (hand-fingers-closed)", "order": ""}, + {"emoji": "✊🏼", "name": "raised fist: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "270A 1F3FC", "html": "✊🏼", "category": "People & Body (hand-fingers-closed)", "order": ""}, + {"emoji": "✊🏻", "name": "raised fist: light skin tone", "shortname": ":light_skin_tone:", "unicode": "270A 1F3FB", "html": "✊🏻", "category": "People & Body (hand-fingers-closed)", "order": ""}, + {"emoji": "✊", "name": "raised fist", "shortname": ":fist:", "unicode": "270a", "html": "✊", "category": "People & Body (hand-fingers-closed)", "order": "1188"}, + {"emoji": "👇🏿", "name": "backhand index pointing down: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F447 1F3FF", "html": "👇🏿", "category": "People & Body (hand-single-finger)", "order": ""}, + {"emoji": "👇🏾", "name": "backhand index pointing down: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F447 1F3FE", "html": "👇🏾", "category": "People & Body (hand-single-finger)", "order": ""}, + {"emoji": "👇🏽", "name": "backhand index pointing down: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F447 1F3FD", "html": "👇🏽", "category": "People & Body (hand-single-finger)", "order": ""}, + {"emoji": "👇🏼", "name": "backhand index pointing down: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F447 1F3FC", "html": "👇🏼", "category": "People & Body (hand-single-finger)", "order": ""}, + {"emoji": "👇🏻", "name": "backhand index pointing down: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F447 1F3FB", "html": "👇🏻", "category": "People & Body (hand-single-finger)", "order": ""}, + {"emoji": "👇", "name": "backhand index pointing down", "shortname": ":point_down:", "unicode": "1f447", "html": "👇", "category": "People & Body (hand-single-finger)", "order": "1122"}, + {"emoji": "🔴", "name": "red circle", "shortname": ":red_circle:", "unicode": "1f534", "html": "🔴", "category": "Symbols (geometric)", "order": "2179"}, + {"emoji": "🙅🏿‍♂", "name": "man gesturing NO: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F645 1F3FF 200D 2642", "html": "🙅🏿‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙅🏾‍♂", "name": "man gesturing NO: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F645 1F3FE 200D 2642", "html": "🙅🏾‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙅🏽‍♂", "name": "man gesturing NO: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F645 1F3FD 200D 2642", "html": "🙅🏽‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙅🏼‍♂", "name": "man gesturing NO: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F645 1F3FC 200D 2642", "html": "🙅🏼‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙅🏻‍♂", "name": "man gesturing NO: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F645 1F3FB 200D 2642", "html": "🙅🏻‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙅‍♂", "name": "man gesturing NO", "shortname": ":man_gesturing_NO:", "unicode": "1F645 200D 2642", "html": "🙅‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙅🏿", "name": "person gesturing NO: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F645 1F3FF", "html": "🙅🏿", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙅🏾", "name": "person gesturing NO: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F645 1F3FE", "html": "🙅🏾", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙅🏽", "name": "person gesturing NO: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F645 1F3FD", "html": "🙅🏽", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙅🏼", "name": "person gesturing NO: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F645 1F3FC", "html": "🙅🏼", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙅🏻", "name": "person gesturing NO: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F645 1F3FB", "html": "🙅🏻", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙅", "name": "person gesturing NO", "shortname": ":no_good:", "unicode": "1f645", "html": "🙅", "category": "People & Body (person-gesture)", "order": "531"}, + {"emoji": "💥", "name": "collision", "shortname": ":boom:", "unicode": "1f4a5", "html": "💥", "category": "Smileys & Emotion (emotion)", "order": "1305"}, + {"emoji": "©️", "name": "copyright", "shortname": ":copyright:", "unicode": "00A9 FE0F", "html": "©️", "category": "Symbols (other-symbol)", "order": ""}, + {"emoji": "💭", "name": "thought balloon", "shortname": ":thought_balloon:", "unicode": "1f4ad", "html": "💭", "category": "Smileys & Emotion (emotion)", "order": "1312"}, + {"emoji": "👅", "name": "tongue", "shortname": ":tongue:", "unicode": "1f445", "html": "👅", "category": "People & Body (body-parts)", "order": "1282"}, + {"emoji": "💩", "name": "pile of poo", "shortname": ":poop:", "unicode": "1f4a9", "html": "💩", "category": "Smileys & Emotion (face-costume)", "order": "85"}, + {"emoji": "😰", "name": "anxious face with sweat", "shortname": ":cold_sweat:", "unicode": "1f630", "html": "😰", "category": "Smileys & Emotion (face-concerned)", "order": "61"}, + {"emoji": "💎", "name": "gem stone", "shortname": ":gem:", "unicode": "1f48e", "html": "💎", "category": "Objects (clothing)", "order": "1341"}, + {"emoji": "🙆🏿‍♂", "name": "man gesturing OK: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F646 1F3FF 200D 2642", "html": "🙆🏿‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙆🏾‍♂", "name": "man gesturing OK: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F646 1F3FE 200D 2642", "html": "🙆🏾‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙆🏽‍♂", "name": "man gesturing OK: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F646 1F3FD 200D 2642", "html": "🙆🏽‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙆🏼‍♂", "name": "man gesturing OK: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F646 1F3FC 200D 2642", "html": "🙆🏼‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙆🏻‍♂", "name": "man gesturing OK: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F646 1F3FB 200D 2642", "html": "🙆🏻‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙆‍♂", "name": "man gesturing OK", "shortname": ":man_gesturing_OK:", "unicode": "1F646 200D 2642", "html": "🙆‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙆🏿", "name": "person gesturing OK: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F646 1F3FF", "html": "🙆🏿", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙆🏾", "name": "person gesturing OK: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F646 1F3FE", "html": "🙆🏾", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙆🏽", "name": "person gesturing OK: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F646 1F3FD", "html": "🙆🏽", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙆🏼", "name": "person gesturing OK: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F646 1F3FC", "html": "🙆🏼", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙆🏻", "name": "person gesturing OK: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F646 1F3FB", "html": "🙆🏻", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙆", "name": "person gesturing OK", "shortname": ":ok_woman:", "unicode": "1f646", "html": "🙆", "category": "People & Body (person-gesture)", "order": "549"}, + {"emoji": "🍕", "name": "pizza", "shortname": ":pizza:", "unicode": "1f355", "html": "🍕", "category": "Food & Drink (food-prepared)", "order": "1484"}, + {"emoji": "😹", "name": "cat with tears of joy", "shortname": ":joy_cat:", "unicode": "1f639", "html": "😹", "category": "Smileys & Emotion (cat-face)", "order": "89"}, + {"emoji": "🌞", "name": "sun with face", "shortname": ":sun_with_face:", "unicode": "1f31e", "html": "🌞", "category": "Travel & Places (sky & weather)", "order": "1726"}, + {"emoji": "🍃", "name": "leaf fluttering in wind", "shortname": ":leaves:", "unicode": "1f343", "html": "🍃", "category": "Animals & Nature (plant-other)", "order": "1448"}, + {"emoji": "💦", "name": "sweat droplets", "shortname": ":sweat_drops:", "unicode": "1f4a6", "html": "💦", "category": "Smileys & Emotion (emotion)", "order": "1306"}, + {"emoji": "🐧", "name": "penguin", "shortname": ":penguin:", "unicode": "1f427", "html": "🐧", "category": "Animals & Nature (animal-bird)", "order": "1394"}, + {"emoji": "💤", "name": "zzz", "shortname": ":zzz:", "unicode": "1f4a4", "html": "💤", "category": "Smileys & Emotion (emotion)", "order": "1302"}, + {"emoji": "🚶🏿‍♀", "name": "woman walking: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F6B6 1F3FF 200D 2640", "html": "🚶🏿‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🚶🏾‍♀", "name": "woman walking: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F6B6 1F3FE 200D 2640", "html": "🚶🏾‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🚶🏽‍♀", "name": "woman walking: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F6B6 1F3FD 200D 2640", "html": "🚶🏽‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🚶🏼‍♀", "name": "woman walking: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F6B6 1F3FC 200D 2640", "html": "🚶🏼‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🚶🏻‍♀", "name": "woman walking: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F6B6 1F3FB 200D 2640", "html": "🚶🏻‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🚶‍♀", "name": "woman walking", "shortname": ":woman_walking:", "unicode": "1F6B6 200D 2640", "html": "🚶‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🚶🏿", "name": "person walking: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F6B6 1F3FF", "html": "🚶🏿", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🚶🏾", "name": "person walking: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F6B6 1F3FE", "html": "🚶🏾", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🚶🏽", "name": "person walking: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F6B6 1F3FD", "html": "🚶🏽", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🚶🏼", "name": "person walking: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F6B6 1F3FC", "html": "🚶🏼", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🚶🏻", "name": "person walking: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F6B6 1F3FB", "html": "🚶🏻", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🚶", "name": "person walking", "shortname": ":walking:", "unicode": "1f6b6", "html": "🚶", "category": "People & Body (person-activity)", "order": "693"}, + {"emoji": "✈️", "name": "airplane", "shortname": ":airplane:", "unicode": "2708", "html": "✈", "category": "Travel & Places (transport-air)", "order": "1650"}, + {"emoji": "🎈", "name": "balloon", "shortname": ":balloon:", "unicode": "1f388", "html": "🎈", "category": "Activities (event)", "order": "1761"}, + {"emoji": "⭐", "name": "star", "shortname": ":star:", "unicode": "2b50", "html": "⭐", "category": "Travel & Places (sky & weather)", "order": "1727"}, + {"emoji": "🎀", "name": "ribbon", "shortname": ":ribbon:", "unicode": "1f380", "html": "🎀", "category": "Activities (event)", "order": "1770"}, + {"emoji": "☑️", "name": "check box with check", "shortname": ":ballot_box_with_check:", "unicode": "2611", "html": "☑", "category": "Symbols (other-symbol)", "order": "2079"}, + {"emoji": "😟", "name": "worried face", "shortname": ":worried:", "unicode": "1f61f", "html": "😟", "category": "Smileys & Emotion (face-concerned)", "order": "52"}, + {"emoji": "🔞", "name": "no one under eighteen", "shortname": ":underage:", "unicode": "1f51e", "html": "🔞", "category": "Symbols (warning)", "order": "1999"}, + {"emoji": "😨", "name": "fearful face", "shortname": ":fearful:", "unicode": "1f628", "html": "😨", "category": "Smileys & Emotion (face-concerned)", "order": "58"}, + {"emoji": "🍀", "name": "four leaf clover", "shortname": ":four_leaf_clover:", "unicode": "1f340", "html": "🍀", "category": "Animals & Nature (plant-other)", "order": "1445"}, + {"emoji": "🌺", "name": "hibiscus", "shortname": ":hibiscus:", "unicode": "1f33a", "html": "🌺", "category": "Animals & Nature (plant-flower)", "order": "1433"}, + {"emoji": "🎤", "name": "microphone", "shortname": ":microphone:", "unicode": "1f3a4", "html": "🎤", "category": "Objects (music)", "order": "1829"}, + {"emoji": "👐🏿", "name": "open hands: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F450 1F3FF", "html": "👐🏿", "category": "People & Body (hands)", "order": ""}, + {"emoji": "👐🏾", "name": "open hands: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F450 1F3FE", "html": "👐🏾", "category": "People & Body (hands)", "order": ""}, + {"emoji": "👐🏽", "name": "open hands: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F450 1F3FD", "html": "👐🏽", "category": "People & Body (hands)", "order": ""}, + {"emoji": "👐🏼", "name": "open hands: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F450 1F3FC", "html": "👐🏼", "category": "People & Body (hands)", "order": ""}, + {"emoji": "👐🏻", "name": "open hands: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F450 1F3FB", "html": "👐🏻", "category": "People & Body (hands)", "order": ""}, + {"emoji": "👐", "name": "open hands", "shortname": ":open_hands:", "unicode": "1f450", "html": "👐", "category": "People & Body (hands)", "order": "1236"}, + {"emoji": "👻", "name": "ghost", "shortname": ":ghost:", "unicode": "1f47b", "html": "👻", "category": "Smileys & Emotion (face-costume)", "order": "82"}, + {"emoji": "🌴", "name": "palm tree", "shortname": ":palm_tree:", "unicode": "1f334", "html": "🌴", "category": "Animals & Nature (plant-other)", "order": "1440"}, + {"emoji": "‼️", "name": "double exclamation mark", "shortname": ":bangbang:", "unicode": "203c", "html": "‼", "category": "Symbols (other-symbol)", "order": "2096"}, + {"emoji": "💅🏿", "name": "nail polish: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F485 1F3FF", "html": "💅🏿", "category": "People & Body (hand-prop)", "order": ""}, + {"emoji": "💅🏾", "name": "nail polish: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F485 1F3FE", "html": "💅🏾", "category": "People & Body (hand-prop)", "order": ""}, + {"emoji": "💅🏽", "name": "nail polish: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F485 1F3FD", "html": "💅🏽", "category": "People & Body (hand-prop)", "order": ""}, + {"emoji": "💅🏼", "name": "nail polish: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F485 1F3FC", "html": "💅🏼", "category": "People & Body (hand-prop)", "order": ""}, + {"emoji": "💅🏻", "name": "nail polish: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F485 1F3FB", "html": "💅🏻", "category": "People & Body (hand-prop)", "order": ""}, + {"emoji": "💅", "name": "nail polish", "shortname": ":nail_care:", "unicode": "1f485", "html": "💅", "category": "People & Body (hand-prop)", "order": "1260"}, + {"emoji": "❌", "name": "cross mark", "shortname": ":x:", "unicode": "274c", "html": "❌", "category": "Symbols (other-symbol)", "order": "2082"}, + {"emoji": "👽", "name": "alien", "shortname": ":alien:", "unicode": "1f47d", "html": "👽", "category": "Smileys & Emotion (face-costume)", "order": "83"}, + {"emoji": "🙇🏿‍♀", "name": "woman bowing: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F647 1F3FF 200D 2640", "html": "🙇🏿‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙇🏾‍♀", "name": "woman bowing: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F647 1F3FE 200D 2640", "html": "🙇🏾‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙇🏽‍♀", "name": "woman bowing: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F647 1F3FD 200D 2640", "html": "🙇🏽‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙇🏼‍♀", "name": "woman bowing: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F647 1F3FC 200D 2640", "html": "🙇🏼‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙇🏻‍♀", "name": "woman bowing: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F647 1F3FB 200D 2640", "html": "🙇🏻‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙇‍♀", "name": "woman bowing", "shortname": ":woman_bowing:", "unicode": "1F647 200D 2640", "html": "🙇‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙇🏿", "name": "person bowing: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F647 1F3FF", "html": "🙇🏿", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙇🏾", "name": "person bowing: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F647 1F3FE", "html": "🙇🏾", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙇🏽", "name": "person bowing: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F647 1F3FD", "html": "🙇🏽", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙇🏼", "name": "person bowing: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F647 1F3FC", "html": "🙇🏼", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙇🏻", "name": "person bowing: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F647 1F3FB", "html": "🙇🏻", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙇", "name": "person bowing", "shortname": ":bow:", "unicode": "1f647", "html": "🙇", "category": "People & Body (person-gesture)", "order": "603"}, + {"emoji": "☁", "name": "cloud", "shortname": ":cloud:", "unicode": "2601", "html": "☁", "category": "Travel & Places (sky & weather)", "order": "1730"}, + {"emoji": "⚽", "name": "soccer ball", "shortname": ":soccer:", "unicode": "26bd", "html": "⚽", "category": "Activities (sport)", "order": "1781"}, + {"emoji": "👼🏿", "name": "baby angel: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F47C 1F3FF", "html": "👼🏿", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "👼🏾", "name": "baby angel: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F47C 1F3FE", "html": "👼🏾", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "👼🏽", "name": "baby angel: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F47C 1F3FD", "html": "👼🏽", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "👼🏼", "name": "baby angel: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F47C 1F3FC", "html": "👼🏼", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "👼🏻", "name": "baby angel: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F47C 1F3FB", "html": "👼🏻", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "👼", "name": "baby angel", "shortname": ":angel:", "unicode": "1f47c", "html": "👼", "category": "People & Body (person-fantasy)", "order": "141"}, + {"emoji": "👯‍♂", "name": "men with bunny ears", "shortname": ":men_with_bunny_ears:", "unicode": "1F46F 200D 2642", "html": "👯‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👯", "name": "people with bunny ears", "shortname": ":dancers:", "unicode": "1f46f", "html": "👯", "category": "People & Body (person-activity)", "order": "741"}, + {"emoji": "❗", "name": "exclamation mark", "shortname": ":exclamation:", "unicode": "2757", "html": "❗", "category": "Symbols (other-symbol)", "order": "2101"}, + {"emoji": "❄️", "name": "snowflake", "shortname": ":snowflake:", "unicode": "2744", "html": "❄", "category": "Travel & Places (sky & weather)", "order": "1749"}, + {"emoji": "☝🏿", "name": "index pointing up: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "261D 1F3FF", "html": "☝🏿", "category": "People & Body (hand-single-finger)", "order": ""}, + {"emoji": "☝🏾", "name": "index pointing up: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "261D 1F3FE", "html": "☝🏾", "category": "People & Body (hand-single-finger)", "order": ""}, + {"emoji": "☝🏽", "name": "index pointing up: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "261D 1F3FD", "html": "☝🏽", "category": "People & Body (hand-single-finger)", "order": ""}, + {"emoji": "☝🏼", "name": "index pointing up: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "261D 1F3FC", "html": "☝🏼", "category": "People & Body (hand-single-finger)", "order": ""}, + {"emoji": "☝🏻", "name": "index pointing up: light skin tone", "shortname": ":light_skin_tone:", "unicode": "261D 1F3FB", "html": "☝🏻", "category": "People & Body (hand-single-finger)", "order": ""}, + {"emoji": "☝️", "name": "index pointing up", "shortname": ":point_up:", "unicode": "261d", "html": "☝", "category": "People & Body (hand-single-finger)", "order": "1104"}, + {"emoji": "😙", "name": "kissing face with smiling eyes", "shortname": ":kissing_smiling_eyes:", "unicode": "1f619", "html": "😙", "category": "Smileys & Emotion (face-affection)", "order": "16"}, + {"emoji": "🌈", "name": "rainbow", "shortname": ":rainbow:", "unicode": "1f308", "html": "🌈", "category": "Travel & Places (sky & weather)", "order": "1743"}, + {"emoji": "🌙", "name": "crescent moon", "shortname": ":crescent_moon:", "unicode": "1f319", "html": "🌙", "category": "Travel & Places (sky & weather)", "order": "1719"}, + {"emoji": "💟", "name": "heart decoration", "shortname": ":heart_decoration:", "unicode": "1f49f", "html": "💟", "category": "Smileys & Emotion (emotion)", "order": "1299"}, + {"emoji": "💝", "name": "heart with ribbon", "shortname": ":gift_heart:", "unicode": "1f49d", "html": "💝", "category": "Smileys & Emotion (emotion)", "order": "1297"}, + {"emoji": "🎁", "name": "wrapped gift", "shortname": ":gift:", "unicode": "1f381", "html": "🎁", "category": "Activities (event)", "order": "1771"}, + {"emoji": "🍻", "name": "clinking beer mugs", "shortname": ":beers:", "unicode": "1f37b", "html": "🍻", "category": "Food & Drink (drink)", "order": "1530"}, + {"emoji": "😧", "name": "anguished face", "shortname": ":anguished:", "unicode": "1f627", "html": "😧", "category": "Smileys & Emotion (face-concerned)", "order": "57"}, + {"emoji": "🌍", "name": "globe showing Europe-Africa", "shortname": ":earth_africa:", "unicode": "1f30d", "html": "🌍", "category": "Travel & Places (place-map)", "order": "1538"}, + {"emoji": "🎥", "name": "movie camera", "shortname": ":movie_camera:", "unicode": "1f3a5", "html": "🎥", "category": "Objects (light & video)", "order": "1856"}, + {"emoji": "⚓", "name": "anchor", "shortname": ":anchor:", "unicode": "2693", "html": "⚓", "category": "Travel & Places (transport-water)", "order": "1642"}, + {"emoji": "⚡", "name": "high voltage", "shortname": ":zap:", "unicode": "26a1", "html": "⚡", "category": "Travel & Places (sky & weather)", "order": "1748"}, + {"emoji": "♣️", "name": "club suit", "shortname": ":club_suit:", "unicode": "2663 FE0F", "html": "♣️", "category": "Activities (game)", "order": ""}, + {"emoji": "✖️", "name": "multiplication sign", "shortname": ":heavy_multiplication_x:", "unicode": "2716", "html": "✖", "category": "Symbols (other-symbol)", "order": "2081"}, + {"emoji": "🏃🏿‍♀", "name": "woman running: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F3C3 1F3FF 200D 2640", "html": "🏃🏿‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🏃🏾‍♀", "name": "woman running: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F3C3 1F3FE 200D 2640", "html": "🏃🏾‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🏃🏽‍♀", "name": "woman running: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F3C3 1F3FD 200D 2640", "html": "🏃🏽‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🏃🏼‍♀", "name": "woman running: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F3C3 1F3FC 200D 2640", "html": "🏃🏼‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🏃🏻‍♀", "name": "woman running: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F3C3 1F3FB 200D 2640", "html": "🏃🏻‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🏃‍♀", "name": "woman running", "shortname": ":woman_running:", "unicode": "1F3C3 200D 2640", "html": "🏃‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🏃🏿", "name": "person running: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F3C3 1F3FF", "html": "🏃🏿", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🏃🏾", "name": "person running: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F3C3 1F3FE", "html": "🏃🏾", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🏃🏽", "name": "person running: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F3C3 1F3FD", "html": "🏃🏽", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🏃🏼", "name": "person running: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F3C3 1F3FC", "html": "🏃🏼", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🏃🏻", "name": "person running: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F3C3 1F3FB", "html": "🏃🏻", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🏃", "name": "person running", "shortname": ":runner:", "unicode": "1f3c3", "html": "🏃", "category": "People & Body (person-activity)", "order": "711"}, + {"emoji": "🌻", "name": "sunflower", "shortname": ":sunflower:", "unicode": "1f33b", "html": "🌻", "category": "Animals & Nature (plant-flower)", "order": "1434"}, + {"emoji": "🌎", "name": "globe showing Americas", "shortname": ":earth_americas:", "unicode": "1f30e", "html": "🌎", "category": "Travel & Places (place-map)", "order": "1539"}, + {"emoji": "💐", "name": "bouquet", "shortname": ":bouquet:", "unicode": "1f490", "html": "💐", "category": "Animals & Nature (plant-flower)", "order": "1427"}, + {"emoji": "🐶", "name": "dog face", "shortname": ":dog:", "unicode": "1f436", "html": "🐶", "category": "Animals & Nature (animal-mammal)", "order": "1345"}, + {"emoji": "💰", "name": "money bag", "shortname": ":moneybag:", "unicode": "1f4b0", "html": "💰", "category": "Objects (money)", "order": "1891"}, + {"emoji": "🌿", "name": "herb", "shortname": ":herb:", "unicode": "1f33f", "html": "🌿", "category": "Animals & Nature (plant-other)", "order": "1443"}, + {"emoji": "👫", "name": "woman and man holding hands", "shortname": ":couple:", "unicode": "1f46b", "html": "👫", "category": "People & Body (family)", "order": "1018"}, + {"emoji": "🍂", "name": "fallen leaf", "shortname": ":fallen_leaf:", "unicode": "1f342", "html": "🍂", "category": "Animals & Nature (plant-other)", "order": "1447"}, + {"emoji": "🌷", "name": "tulip", "shortname": ":tulip:", "unicode": "1f337", "html": "🌷", "category": "Animals & Nature (plant-flower)", "order": "1436"}, + {"emoji": "🎂", "name": "birthday cake", "shortname": ":birthday:", "unicode": "1f382", "html": "🎂", "category": "Food & Drink (food-sweet)", "order": "1513"}, + {"emoji": "🐱", "name": "cat face", "shortname": ":cat:", "unicode": "1f431", "html": "🐱", "category": "Animals & Nature (animal-mammal)", "order": "1350"}, + {"emoji": "☕", "name": "hot beverage", "shortname": ":coffee:", "unicode": "2615", "html": "☕", "category": "Food & Drink (drink)", "order": "1522"}, + {"emoji": "😵", "name": "dizzy face", "shortname": ":dizzy_face:", "unicode": "1f635", "html": "😵", "category": "Smileys & Emotion (face-unwell)", "order": "64"}, + {"emoji": "👆🏿", "name": "backhand index pointing up: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F446 1F3FF", "html": "👆🏿", "category": "People & Body (hand-single-finger)", "order": ""}, + {"emoji": "👆🏾", "name": "backhand index pointing up: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F446 1F3FE", "html": "👆🏾", "category": "People & Body (hand-single-finger)", "order": ""}, + {"emoji": "👆🏽", "name": "backhand index pointing up: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F446 1F3FD", "html": "👆🏽", "category": "People & Body (hand-single-finger)", "order": ""}, + {"emoji": "👆🏼", "name": "backhand index pointing up: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F446 1F3FC", "html": "👆🏼", "category": "People & Body (hand-single-finger)", "order": ""}, + {"emoji": "👆🏻", "name": "backhand index pointing up: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F446 1F3FB", "html": "👆🏻", "category": "People & Body (hand-single-finger)", "order": ""}, + {"emoji": "👆", "name": "backhand index pointing up", "shortname": ":point_up_2:", "unicode": "1f446", "html": "👆", "category": "People & Body (hand-single-finger)", "order": "1110"}, + {"emoji": "😮", "name": "face with open mouth", "shortname": ":open_mouth:", "unicode": "1f62e", "html": "😮", "category": "Smileys & Emotion (face-concerned)", "order": "29"}, + {"emoji": "😯", "name": "hushed face", "shortname": ":hushed:", "unicode": "1f62f", "html": "😯", "category": "Smileys & Emotion (face-concerned)", "order": "31"}, + {"emoji": "🏀", "name": "basketball", "shortname": ":basketball:", "unicode": "1f3c0", "html": "🏀", "category": "Activities (sport)", "order": "1783"}, + {"emoji": "🎄", "name": "Christmas tree", "shortname": ":christmas_tree:", "unicode": "1f384", "html": "🎄", "category": "Activities (event)", "order": "1757"}, + {"emoji": "💍", "name": "ring", "shortname": ":ring:", "unicode": "1f48d", "html": "💍", "category": "Objects (clothing)", "order": "1340"}, + {"emoji": "🌝", "name": "full moon face", "shortname": ":full_moon_with_face:", "unicode": "1f31d", "html": "🌝", "category": "Travel & Places (sky & weather)", "order": "1725"}, + {"emoji": "😲", "name": "astonished face", "shortname": ":astonished:", "unicode": "1f632", "html": "😲", "category": "Smileys & Emotion (face-concerned)", "order": "47"}, + {"emoji": "👭", "name": "women holding hands", "shortname": ":two_women_holding_hands:", "unicode": "1f46d", "html": "👭", "category": "People & Body (family)", "order": "1030"}, + {"emoji": "💸", "name": "money with wings", "shortname": ":money_with_wings:", "unicode": "1f4b8", "html": "💸", "category": "Objects (money)", "order": "1896"}, + {"emoji": "😿", "name": "crying cat", "shortname": ":crying_cat_face:", "unicode": "1f63f", "html": "😿", "category": "Smileys & Emotion (cat-face)", "order": "94"}, + {"emoji": "🙉", "name": "hear-no-evil monkey", "shortname": ":hear_no_evil:", "unicode": "1f649", "html": "🙉", "category": "Smileys & Emotion (monkey-face)", "order": "97"}, + {"emoji": "💨", "name": "dashing away", "shortname": ":dash:", "unicode": "1f4a8", "html": "💨", "category": "Smileys & Emotion (emotion)", "order": "1307"}, + {"emoji": "🌵", "name": "cactus", "shortname": ":cactus:", "unicode": "1f335", "html": "🌵", "category": "Animals & Nature (plant-other)", "order": "1441"}, + {"emoji": "♨️", "name": "hot springs", "shortname": ":hotsprings:", "unicode": "2668", "html": "♨", "category": "Travel & Places (place-other)", "order": "1591"}, + {"emoji": "☎️", "name": "telephone", "shortname": ":telephone:", "unicode": "260e", "html": "☎", "category": "Objects (phone)", "order": "1840"}, + {"emoji": "🍁", "name": "maple leaf", "shortname": ":maple_leaf:", "unicode": "1f341", "html": "🍁", "category": "Animals & Nature (plant-other)", "order": "1446"}, + {"emoji": "👸🏿", "name": "princess: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F478 1F3FF", "html": "👸🏿", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👸🏾", "name": "princess: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F478 1F3FE", "html": "👸🏾", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👸🏽", "name": "princess: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F478 1F3FD", "html": "👸🏽", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👸🏼", "name": "princess: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F478 1F3FC", "html": "👸🏼", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👸🏻", "name": "princess: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F478 1F3FB", "html": "👸🏻", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👸", "name": "princess", "shortname": ":princess:", "unicode": "1f478", "html": "👸", "category": "People & Body (person-role)", "order": "459"}, + {"emoji": "💆🏿‍♂", "name": "man getting massage: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F486 1F3FF 200D 2642", "html": "💆🏿‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💆🏾‍♂", "name": "man getting massage: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F486 1F3FE 200D 2642", "html": "💆🏾‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💆🏽‍♂", "name": "man getting massage: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F486 1F3FD 200D 2642", "html": "💆🏽‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💆🏼‍♂", "name": "man getting massage: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F486 1F3FC 200D 2642", "html": "💆🏼‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💆🏻‍♂", "name": "man getting massage: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F486 1F3FB 200D 2642", "html": "💆🏻‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💆‍♂", "name": "man getting massage", "shortname": ":man_getting_massage:", "unicode": "1F486 200D 2642", "html": "💆‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💆🏿", "name": "person getting massage: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F486 1F3FF", "html": "💆🏿", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💆🏾", "name": "person getting massage: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F486 1F3FE", "html": "💆🏾", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💆🏽", "name": "person getting massage: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F486 1F3FD", "html": "💆🏽", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💆🏼", "name": "person getting massage: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F486 1F3FC", "html": "💆🏼", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💆🏻", "name": "person getting massage: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F486 1F3FB", "html": "💆🏻", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💆", "name": "person getting massage", "shortname": ":massage:", "unicode": "1f486", "html": "💆", "category": "People & Body (person-activity)", "order": "657"}, + {"emoji": "💌", "name": "love letter", "shortname": ":love_letter:", "unicode": "1f48c", "html": "💌", "category": "Smileys & Emotion (emotion)", "order": "1301"}, + {"emoji": "🏆", "name": "trophy", "shortname": ":trophy:", "unicode": "1f3c6", "html": "🏆", "category": "Activities (award-medal)", "order": "1776"}, + {"emoji": "🙍🏿‍♂", "name": "man frowning: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F64D 1F3FF 200D 2642", "html": "🙍🏿‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙍🏾‍♂", "name": "man frowning: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F64D 1F3FE 200D 2642", "html": "🙍🏾‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙍🏽‍♂", "name": "man frowning: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F64D 1F3FD 200D 2642", "html": "🙍🏽‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙍🏼‍♂", "name": "man frowning: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F64D 1F3FC 200D 2642", "html": "🙍🏼‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙍🏻‍♂", "name": "man frowning: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F64D 1F3FB 200D 2642", "html": "🙍🏻‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙍‍♂", "name": "man frowning", "shortname": ":man_frowning:", "unicode": "1F64D 200D 2642", "html": "🙍‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙍🏿", "name": "person frowning: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F64D 1F3FF", "html": "🙍🏿", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙍🏾", "name": "person frowning: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F64D 1F3FE", "html": "🙍🏾", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙍🏽", "name": "person frowning: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F64D 1F3FD", "html": "🙍🏽", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙍🏼", "name": "person frowning: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F64D 1F3FC", "html": "🙍🏼", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙍🏻", "name": "person frowning: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F64D 1F3FB", "html": "🙍🏻", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙍", "name": "person frowning", "shortname": ":person_frowning:", "unicode": "1f64d", "html": "🙍", "category": "People & Body (person-gesture)", "order": "495"}, + {"emoji": "🇺🇸", "name": "flag: United States", "shortname": ":us:", "unicode": "1f1fa", "html": "🇺", "category": "Flags (country-flag)", "order": ""}, + {"emoji": "🎊", "name": "confetti ball", "shortname": ":confetti_ball:", "unicode": "1f38a", "html": "🎊", "category": "Activities (event)", "order": "1763"}, + {"emoji": "🌼", "name": "blossom", "shortname": ":blossom:", "unicode": "1f33c", "html": "🌼", "category": "Animals & Nature (plant-flower)", "order": "1435"}, + {"emoji": "🔪", "name": "kitchen knife", "shortname": ":kitchen_knife:", "unicode": "1F52A", "html": "🔪", "category": "Food & Drink (dishware)", "order": ""}, + {"emoji": "👄", "name": "mouth", "shortname": ":lips:", "unicode": "1f444", "html": "👄", "category": "People & Body (body-parts)", "order": "1283"}, + {"emoji": "🍟", "name": "french fries", "shortname": ":fries:", "unicode": "1f35f", "html": "🍟", "category": "Food & Drink (food-prepared)", "order": "1483"}, + {"emoji": "🍩", "name": "doughnut", "shortname": ":doughnut:", "unicode": "1f369", "html": "🍩", "category": "Food & Drink (food-sweet)", "order": "1511"}, + {"emoji": "😦", "name": "frowning face with open mouth", "shortname": ":frowning:", "unicode": "1f626", "html": "😦", "category": "Smileys & Emotion (face-concerned)", "order": "56"}, + {"emoji": "🌊", "name": "water wave", "shortname": ":ocean:", "unicode": "1f30a", "html": "🌊", "category": "Travel & Places (sky & weather)", "order": "1755"}, + {"emoji": "💣", "name": "bomb", "shortname": ":bomb:", "unicode": "1f4a3", "html": "💣", "category": "Smileys & Emotion (emotion)", "order": "1304"}, + {"emoji": "🆗", "name": "OK button", "shortname": ":ok:", "unicode": "1f197", "html": "🆗", "category": "Symbols (alphanum)", "order": "2137"}, + {"emoji": "🌀", "name": "cyclone", "shortname": ":cyclone:", "unicode": "1f300", "html": "🌀", "category": "Travel & Places (sky & weather)", "order": "1742"}, + {"emoji": "🚀", "name": "rocket", "shortname": ":rocket:", "unicode": "1f680", "html": "🚀", "category": "Travel & Places (transport-air)", "order": "1659"}, + {"emoji": "☔", "name": "umbrella with rain drops", "shortname": ":umbrella:", "unicode": "2614", "html": "☔", "category": "Travel & Places (sky & weather)", "order": "1746"}, + {"emoji": "💏", "name": "kiss", "shortname": ":couplekiss:", "unicode": "1f48f", "html": "💏", "category": "People & Body (family)", "order": "1036"}, + {"emoji": "👩‍❤️‍💋‍👩", "name": "kiss: woman, woman", "shortname": ":couple_woman_kiss:", "unicode": "1F469 200D 2764 FE0F 200D 1F48B 200D 1F469", "html": "👩‍❤‍💋‍👩", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨‍❤️‍💋‍👨", "name": "kiss: man, man", "shortname": ":couple_man_kiss:", "unicode": "1F468 200D 2764 FE0F 200D 1F48B 200D 1F468", "html": "👨‍❤‍💋‍👨", "category": "People & Body (family)", "order": ""}, + {"emoji": "💑", "name": "couple with heart", "shortname": ":couple_with_heart:", "unicode": "1f491", "html": "💑", "category": "People & Body (family)", "order": "1040"}, + {"emoji": "👩‍❤️‍👩", "name": "couple with heart: woman, woman", "shortname": ":woman_woman:", "unicode": "1F469 200D 2764 FE0F 200D 1F469", "html": "👩‍❤‍👩", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨‍❤️‍👨", "name": "couple with heart: man, man", "shortname": ":man_man_love:", "unicode": "1F468 200D 2764 FE0F 200D 1F468", "html": "👨‍❤‍👨", "category": "People & Body (family)", "order": ""}, + {"emoji": "🍭", "name": "lollipop", "shortname": ":lollipop:", "unicode": "1f36d", "html": "🍭", "category": "Food & Drink (food-sweet)", "order": "1517"}, + {"emoji": "🎬", "name": "clapper board", "shortname": ":clapper:", "unicode": "1f3ac", "html": "🎬", "category": "Objects (light & video)", "order": "1859"}, + {"emoji": "🐷", "name": "pig face", "shortname": ":pig:", "unicode": "1f437", "html": "🐷", "category": "Animals & Nature (animal-mammal)", "order": "1364"}, + {"emoji": "😈", "name": "smiling face with horns", "shortname": ":smiling_imp:", "unicode": "1f608", "html": "😈", "category": "Smileys & Emotion (face-negative)", "order": "76"}, + {"emoji": "👿", "name": "angry face with horns", "shortname": ":imp:", "unicode": "1f47f", "html": "👿", "category": "Smileys & Emotion (face-negative)", "order": "77"}, + {"emoji": "🐝", "name": "honeybee", "shortname": ":bee:", "unicode": "1f41d", "html": "🐝", "category": "Animals & Nature (animal-bug)", "order": "1422"}, + {"emoji": "😽", "name": "kissing cat", "shortname": ":kissing_cat:", "unicode": "1f63d", "html": "😽", "category": "Smileys & Emotion (cat-face)", "order": "92"}, + {"emoji": "💢", "name": "anger symbol", "shortname": ":anger:", "unicode": "1f4a2", "html": "💢", "category": "Smileys & Emotion (emotion)", "order": "1303"}, + {"emoji": "🎼", "name": "musical score", "shortname": ":musical_score:", "unicode": "1f3bc", "html": "🎼", "category": "Objects (music)", "order": "1823"}, + {"emoji": "🎅🏿", "name": "Santa Claus: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F385 1F3FF", "html": "🎅🏿", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🎅🏾", "name": "Santa Claus: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F385 1F3FE", "html": "🎅🏾", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🎅🏽", "name": "Santa Claus: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F385 1F3FD", "html": "🎅🏽", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🎅🏼", "name": "Santa Claus: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F385 1F3FC", "html": "🎅🏼", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🎅🏻", "name": "Santa Claus: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F385 1F3FB", "html": "🎅🏻", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🎅", "name": "Santa Claus", "shortname": ":santa:", "unicode": "1f385", "html": "🎅", "category": "People & Body (person-fantasy)", "order": "447"}, + {"emoji": "🌏", "name": "globe showing Asia-Australia", "shortname": ":earth_asia:", "unicode": "1f30f", "html": "🌏", "category": "Travel & Places (place-map)", "order": "1540"}, + {"emoji": "🏈", "name": "american football", "shortname": ":football:", "unicode": "1f3c8", "html": "🏈", "category": "Activities (sport)", "order": "1785"}, + {"emoji": "🎸", "name": "guitar", "shortname": ":guitar:", "unicode": "1f3b8", "html": "🎸", "category": "Objects (musical-instrument)", "order": "1833"}, + {"emoji": "♦️", "name": "diamond suit", "shortname": ":diamond_suit:", "unicode": "2666 FE0F", "html": "♦️", "category": "Activities (game)", "order": ""}, + {"emoji": "🐼", "name": "panda", "shortname": ":panda_face:", "unicode": "1f43c", "html": "🐼", "category": "Animals & Nature (animal-mammal)", "order": "1385"}, + {"emoji": "💬", "name": "speech balloon", "shortname": ":speech_balloon:", "unicode": "1f4ac", "html": "💬", "category": "Smileys & Emotion (emotion)", "order": "1309"}, + {"emoji": "🍓", "name": "strawberry", "shortname": ":strawberry:", "unicode": "1f353", "html": "🍓", "category": "Food & Drink (food-fruit)", "order": "1461"}, + {"emoji": "😼", "name": "cat with wry smile", "shortname": ":smirk_cat:", "unicode": "1f63c", "html": "😼", "category": "Smileys & Emotion (cat-face)", "order": "91"}, + {"emoji": "🍌", "name": "banana", "shortname": ":banana:", "unicode": "1f34c", "html": "🍌", "category": "Food & Drink (food-fruit)", "order": "1454"}, + {"emoji": "🍉", "name": "watermelon", "shortname": ":watermelon:", "unicode": "1f349", "html": "🍉", "category": "Food & Drink (food-fruit)", "order": "1451"}, + {"emoji": "⛄", "name": "snowman without snow", "shortname": ":snowman:", "unicode": "26c4", "html": "⛄", "category": "Travel & Places (sky & weather)", "order": "1751"}, + {"emoji": "😸", "name": "grinning cat with smiling eyes", "shortname": ":smile_cat:", "unicode": "1f638", "html": "😸", "category": "Smileys & Emotion (cat-face)", "order": "88"}, + {"emoji": "♠️", "name": "spade suit", "shortname": ":spade_suit:", "unicode": "2660 FE0F", "html": "♠️", "category": "Activities (game)", "order": ""}, + {"emoji": "🔝", "name": "TOP arrow", "shortname": ":top:", "unicode": "1f51d", "html": "🔝", "category": "Symbols (arrow)", "order": "2022"}, + {"emoji": "🍆", "name": "eggplant", "shortname": ":eggplant:", "unicode": "1f346", "html": "🍆", "category": "Food & Drink (food-vegetable)", "order": "1465"}, + {"emoji": "🔮", "name": "crystal ball", "shortname": ":crystal_ball:", "unicode": "1f52e", "html": "🔮", "category": "Activities (game)", "order": "1974"}, + {"emoji": "🍴", "name": "fork and knife", "shortname": ":fork_and_knife:", "unicode": "1f374", "html": "🍴", "category": "Food & Drink (dishware)", "order": "1534"}, + {"emoji": "📲", "name": "mobile phone with arrow", "shortname": ":calling:", "unicode": "1f4f2", "html": "📲", "category": "Objects (phone)", "order": "1839"}, + {"emoji": "📱", "name": "mobile phone", "shortname": ":iphone:", "unicode": "1f4f1", "html": "📱", "category": "Objects (phone)", "order": "1838"}, + {"emoji": "⛅", "name": "sun behind cloud", "shortname": ":partly_sunny:", "unicode": "26c5", "html": "⛅", "category": "Travel & Places (sky & weather)", "order": "1731"}, + {"emoji": "⚠️", "name": "warning", "shortname": ":warning:", "unicode": "26a0", "html": "⚠", "category": "Symbols (warning)", "order": "1989"}, + {"emoji": "🙀", "name": "weary cat", "shortname": ":scream_cat:", "unicode": "1f640", "html": "🙀", "category": "Smileys & Emotion (cat-face)", "order": "93"}, + {"emoji": "🔸", "name": "small orange diamond", "shortname": ":small_orange_diamond:", "unicode": "1f538", "html": "🔸", "category": "Symbols (geometric)", "order": "2169"}, + {"emoji": "👶🏿", "name": "baby: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F476 1F3FF", "html": "👶🏿", "category": "People & Body (person)", "order": ""}, + {"emoji": "👶🏾", "name": "baby: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F476 1F3FE", "html": "👶🏾", "category": "People & Body (person)", "order": ""}, + {"emoji": "👶🏽", "name": "baby: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F476 1F3FD", "html": "👶🏽", "category": "People & Body (person)", "order": ""}, + {"emoji": "👶🏼", "name": "baby: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F476 1F3FC", "html": "👶🏼", "category": "People & Body (person)", "order": ""}, + {"emoji": "👶🏻", "name": "baby: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F476 1F3FB", "html": "👶🏻", "category": "People & Body (person)", "order": ""}, + {"emoji": "👶", "name": "baby", "shortname": ":baby:", "unicode": "1f476", "html": "👶", "category": "People & Body (person)", "order": "135"}, + {"emoji": "🐾", "name": "paw prints", "shortname": ":feet:", "unicode": "1f43e", "html": "🐾", "category": "Animals & Nature (animal-mammal)", "order": "1386"}, + {"emoji": "👣", "name": "footprints", "shortname": ":footprints:", "unicode": "1f463", "html": "👣", "category": "People & Body (person-symbol)", "order": "1278"}, + {"emoji": "🍺", "name": "beer mug", "shortname": ":beer:", "unicode": "1f37a", "html": "🍺", "category": "Food & Drink (drink)", "order": "1529"}, + {"emoji": "🍷", "name": "wine glass", "shortname": ":wine_glass:", "unicode": "1f377", "html": "🍷", "category": "Food & Drink (drink)", "order": "1526"}, + {"emoji": "⭕", "name": "hollow red circle", "shortname": ":o:", "unicode": "2b55", "html": "⭕", "category": "Symbols (other-symbol)", "order": "2077"}, + {"emoji": "📹", "name": "video camera", "shortname": ":video_camera:", "unicode": "1f4f9", "html": "📹", "category": "Objects (light & video)", "order": "1863"}, + {"emoji": "🐰", "name": "rabbit face", "shortname": ":rabbit:", "unicode": "1f430", "html": "🐰", "category": "Animals & Nature (animal-mammal)", "order": "1379"}, + {"emoji": "🍹", "name": "tropical drink", "shortname": ":tropical_drink:", "unicode": "1f379", "html": "🍹", "category": "Food & Drink (drink)", "order": "1528"}, + {"emoji": "🚬", "name": "cigarette", "shortname": ":smoking:", "unicode": "1f6ac", "html": "🚬", "category": "Objects (other-object)", "order": "1969"}, + {"emoji": "👾", "name": "alien monster", "shortname": ":space_invader:", "unicode": "1f47e", "html": "👾", "category": "Smileys & Emotion (face-costume)", "order": "84"}, + {"emoji": "🍑", "name": "peach", "shortname": ":peach:", "unicode": "1f351", "html": "🍑", "category": "Food & Drink (food-fruit)", "order": "1459"}, + {"emoji": "🐍", "name": "snake", "shortname": ":snake:", "unicode": "1f40d", "html": "🐍", "category": "Animals & Nature (animal-reptile)", "order": "1403"}, + {"emoji": "🐢", "name": "turtle", "shortname": ":turtle:", "unicode": "1f422", "html": "🐢", "category": "Animals & Nature (animal-reptile)", "order": "1401"}, + {"emoji": "🍒", "name": "cherries", "shortname": ":cherries:", "unicode": "1f352", "html": "🍒", "category": "Food & Drink (food-fruit)", "order": "1460"}, + {"emoji": "😗", "name": "kissing face", "shortname": ":kissing:", "unicode": "1f617", "html": "😗", "category": "Smileys & Emotion (face-affection)", "order": "15"}, + {"emoji": "🐸", "name": "frog", "shortname": ":frog:", "unicode": "1f438", "html": "🐸", "category": "Animals & Nature (animal-amphibian)", "order": "1399"}, + {"emoji": "🌌", "name": "milky way", "shortname": ":milky_way:", "unicode": "1f30c", "html": "🌌", "category": "Travel & Places (sky & weather)", "order": "1592"}, + {"emoji": "🚨", "name": "police car light", "shortname": ":rotating_light:", "unicode": "1f6a8", "html": "🚨", "category": "Travel & Places (transport-ground)", "order": "1637"}, + {"emoji": "🐣", "name": "hatching chick", "shortname": ":hatching_chick:", "unicode": "1f423", "html": "🐣", "category": "Animals & Nature (animal-bird)", "order": "1390"}, + {"emoji": "📕", "name": "closed book", "shortname": ":closed_book:", "unicode": "1f4d5", "html": "📕", "category": "Objects (book-paper)", "order": "1875"}, + {"emoji": "🍬", "name": "candy", "shortname": ":candy:", "unicode": "1f36c", "html": "🍬", "category": "Food & Drink (food-sweet)", "order": "1516"}, + {"emoji": "🍔", "name": "hamburger", "shortname": ":hamburger:", "unicode": "1f354", "html": "🍔", "category": "Food & Drink (food-prepared)", "order": "1482"}, + {"emoji": "🐻", "name": "bear", "shortname": ":bear:", "unicode": "1f43b", "html": "🐻", "category": "Animals & Nature (animal-mammal)", "order": "1383"}, + {"emoji": "🐯", "name": "tiger face", "shortname": ":tiger:", "unicode": "1f42f", "html": "🐯", "category": "Animals & Nature (animal-mammal)", "order": "1353"}, + {"emoji": "🚗", "name": "automobile", "shortname": ":automobile:", "unicode": "1F697", "html": "🚗", "category": "Travel & Places (transport-ground)", "order": ""}, + {"emoji": "⏩", "name": "fast-forward button", "shortname": ":fast_forward:", "unicode": "2.3E+10", "html": "⏩", "category": "Symbols (av-symbol)", "order": "2052"}, + {"emoji": "🍦", "name": "soft ice cream", "shortname": ":icecream:", "unicode": "1f366", "html": "🍦", "category": "Food & Drink (food-sweet)", "order": "1508"}, + {"emoji": "🍍", "name": "pineapple", "shortname": ":pineapple:", "unicode": "1f34d", "html": "🍍", "category": "Food & Drink (food-fruit)", "order": "1455"}, + {"emoji": "🌾", "name": "sheaf of rice", "shortname": ":ear_of_rice:", "unicode": "1f33e", "html": "🌾", "category": "Animals & Nature (plant-other)", "order": "1442"}, + {"emoji": "💉", "name": "syringe", "shortname": ":syringe:", "unicode": "1f489", "html": "💉", "category": "Objects (medical)", "order": "1967"}, + {"emoji": "🚮", "name": "litter in bin sign", "shortname": ":put_litter_in_its_place:", "unicode": "1f6ae", "html": "🚮", "category": "Symbols (transport-sign)", "order": "1977"}, + {"emoji": "🍫", "name": "chocolate bar", "shortname": ":chocolate_bar:", "unicode": "1f36b", "html": "🍫", "category": "Food & Drink (food-sweet)", "order": "1515"}, + {"emoji": "▪️", "name": "black small square", "shortname": ":black_small_square:", "unicode": "25aa", "html": "▪", "category": "Symbols (geometric)", "order": "2159"}, + {"emoji": "📺", "name": "television", "shortname": ":tv:", "unicode": "1f4fa", "html": "📺", "category": "Objects (light & video)", "order": "1860"}, + {"emoji": "💊", "name": "pill", "shortname": ":pill:", "unicode": "1f48a", "html": "💊", "category": "Objects (medical)", "order": "1968"}, + {"emoji": "🐙", "name": "octopus", "shortname": ":octopus:", "unicode": "1f419", "html": "🐙", "category": "Animals & Nature (animal-marine)", "order": "1413"}, + {"emoji": "🎃", "name": "jack-o-lantern", "shortname": ":jack_o_lantern:", "unicode": "1f383", "html": "🎃", "category": "Activities (event)", "order": "1756"}, + {"emoji": "🍇", "name": "grapes", "shortname": ":grapes:", "unicode": "1f347", "html": "🍇", "category": "Food & Drink (food-fruit)", "order": "1449"}, + {"emoji": "😺", "name": "grinning cat", "shortname": ":smiley_cat:", "unicode": "1f63a", "html": "😺", "category": "Smileys & Emotion (cat-face)", "order": "87"}, + {"emoji": "💿", "name": "optical disk", "shortname": ":cd:", "unicode": "1f4bf", "html": "💿", "category": "Objects (computer)", "order": "1854"}, + {"emoji": "🍸", "name": "cocktail glass", "shortname": ":cocktail:", "unicode": "1f378", "html": "🍸", "category": "Food & Drink (drink)", "order": "1527"}, + {"emoji": "🍰", "name": "shortcake", "shortname": ":cake:", "unicode": "1f370", "html": "🍰", "category": "Food & Drink (food-sweet)", "order": "1514"}, + {"emoji": "🎮", "name": "video game", "shortname": ":video_game:", "unicode": "1f3ae", "html": "🎮", "category": "Activities (game)", "order": "1804"}, + {"emoji": "™️", "name": "trade mark", "shortname": ":trade_mark:", "unicode": "2122 FE0F", "html": "™️", "category": "Symbols (other-symbol)", "order": ""}, + {"emoji": "⬇️", "name": "down arrow", "shortname": ":arrow_down:", "unicode": "2b07", "html": "⬇", "category": "Symbols (arrow)", "order": "2006"}, + {"emoji": "🚫", "name": "prohibited", "shortname": ":no_entry_sign:", "unicode": "1f6ab", "html": "🚫", "category": "Symbols (warning)", "order": "1992"}, + {"emoji": "💄", "name": "lipstick", "shortname": ":lipstick:", "unicode": "1f484", "html": "💄", "category": "Objects (clothing)", "order": "1339"}, + {"emoji": "🐳", "name": "spouting whale", "shortname": ":whale:", "unicode": "1f433", "html": "🐳", "category": "Animals & Nature (animal-marine)", "order": "1406"}, + {"emoji": "📝", "name": "memo", "shortname": ":memo:", "unicode": "1F4DD", "html": "📝", "category": "Objects (writing)", "order": ""}, + {"emoji": "®️", "name": "registered", "shortname": ":registered:", "unicode": "00AE FE0F", "html": "®️", "category": "Symbols (other-symbol)", "order": ""}, + {"emoji": "🍪", "name": "cookie", "shortname": ":cookie:", "unicode": "1f36a", "html": "🍪", "category": "Food & Drink (food-sweet)", "order": "1512"}, + {"emoji": "🐬", "name": "dolphin", "shortname": ":dolphin:", "unicode": "1f42c", "html": "🐬", "category": "Animals & Nature (animal-marine)", "order": "1408"}, + {"emoji": "🔊", "name": "speaker high volume", "shortname": ":loud_sound:", "unicode": "1f50a", "html": "🔊", "category": "Objects (sound)", "order": "1817"}, + {"emoji": "👨🏿", "name": "man: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F468 1F3FF", "html": "👨🏿", "category": "People & Body (person)", "order": ""}, + {"emoji": "👨🏾", "name": "man: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F468 1F3FE", "html": "👨🏾", "category": "People & Body (person)", "order": ""}, + {"emoji": "👨🏽", "name": "man: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F468 1F3FD", "html": "👨🏽", "category": "People & Body (person)", "order": ""}, + {"emoji": "👨🏼", "name": "man: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F468 1F3FC", "html": "👨🏼", "category": "People & Body (person)", "order": ""}, + {"emoji": "👨🏻", "name": "man: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F468 1F3FB", "html": "👨🏻", "category": "People & Body (person)", "order": ""}, + {"emoji": "👨", "name": "man", "shortname": ":man:", "unicode": "1f468", "html": "👨", "category": "People & Body (person)", "order": "111"}, + {"emoji": "🐥", "name": "front-facing baby chick", "shortname": ":hatched_chick:", "unicode": "1f425", "html": "🐥", "category": "Animals & Nature (animal-bird)", "order": "1392"}, + {"emoji": "🐒", "name": "monkey", "shortname": ":monkey:", "unicode": "1f412", "html": "🐒", "category": "Animals & Nature (animal-mammal)", "order": "1343"}, + {"emoji": "📚", "name": "books", "shortname": ":books:", "unicode": "1f4da", "html": "📚", "category": "Objects (book-paper)", "order": "1880"}, + {"emoji": "👹", "name": "ogre", "shortname": ":japanese_ogre:", "unicode": "1f479", "html": "👹", "category": "Smileys & Emotion (face-costume)", "order": "78"}, + {"emoji": "💂🏿‍♀", "name": "woman guard: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F482 1F3FF 200D 2640", "html": "💂🏿‍♀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "💂🏾‍♀", "name": "woman guard: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F482 1F3FE 200D 2640", "html": "💂🏾‍♀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "💂🏽‍♀", "name": "woman guard: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F482 1F3FD 200D 2640", "html": "💂🏽‍♀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "💂🏼‍♀", "name": "woman guard: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F482 1F3FC 200D 2640", "html": "💂🏼‍♀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "💂🏻‍♀", "name": "woman guard: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F482 1F3FB 200D 2640", "html": "💂🏻‍♀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "💂‍♀", "name": "woman guard", "shortname": ":woman_guard:", "unicode": "1F482 200D 2640", "html": "💂‍♀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "💂🏿", "name": "guard: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F482 1F3FF", "html": "💂🏿", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "💂🏾", "name": "guard: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F482 1F3FE", "html": "💂🏾", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "💂🏽", "name": "guard: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F482 1F3FD", "html": "💂🏽", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "💂🏼", "name": "guard: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F482 1F3FC", "html": "💂🏼", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "💂🏻", "name": "guard: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F482 1F3FB", "html": "💂🏻", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "💂", "name": "guard", "shortname": ":guardsman:", "unicode": "1f482", "html": "💂", "category": "People & Body (person-role)", "order": "375"}, + {"emoji": "📢", "name": "loudspeaker", "shortname": ":loudspeaker:", "unicode": "1f4e2", "html": "📢", "category": "Objects (sound)", "order": "1818"}, + {"emoji": "✂️", "name": "scissors", "shortname": ":scissors:", "unicode": "2702", "html": "✂", "category": "Objects (office)", "order": "1940"}, + {"emoji": "👧🏿", "name": "girl: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F467 1F3FF", "html": "👧🏿", "category": "People & Body (person)", "order": ""}, + {"emoji": "👧🏾", "name": "girl: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F467 1F3FE", "html": "👧🏾", "category": "People & Body (person)", "order": ""}, + {"emoji": "👧🏽", "name": "girl: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F467 1F3FD", "html": "👧🏽", "category": "People & Body (person)", "order": ""}, + {"emoji": "👧🏼", "name": "girl: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F467 1F3FC", "html": "👧🏼", "category": "People & Body (person)", "order": ""}, + {"emoji": "👧🏻", "name": "girl: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F467 1F3FB", "html": "👧🏻", "category": "People & Body (person)", "order": ""}, + {"emoji": "👧", "name": "girl", "shortname": ":girl:", "unicode": "1f467", "html": "👧", "category": "People & Body (person)", "order": "105"}, + {"emoji": "🎓", "name": "graduation cap", "shortname": ":mortar_board:", "unicode": "1f393", "html": "🎓", "category": "Objects (clothing)", "order": "1336"}, + {"emoji": "🇫🇷", "name": "flag: France", "shortname": ":fr:", "unicode": "1f1eb", "html": "🇫", "category": "Flags (country-flag)", "order": ""}, + {"emoji": "⚾️", "name": "", "shortname": ":baseball:", "unicode": "26be", "html": "⚾", "category": "", "order": "1782"}, + {"emoji": "🚦", "name": "vertical traffic light", "shortname": ":vertical_traffic_light:", "unicode": "1f6a6", "html": "🚦", "category": "Travel & Places (transport-ground)", "order": "1639"}, + {"emoji": "👩🏿", "name": "woman: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F469 1F3FF", "html": "👩🏿", "category": "People & Body (person)", "order": ""}, + {"emoji": "👩🏾", "name": "woman: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F469 1F3FE", "html": "👩🏾", "category": "People & Body (person)", "order": ""}, + {"emoji": "👩🏽", "name": "woman: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F469 1F3FD", "html": "👩🏽", "category": "People & Body (person)", "order": ""}, + {"emoji": "👩🏼", "name": "woman: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F469 1F3FC", "html": "👩🏼", "category": "People & Body (person)", "order": ""}, + {"emoji": "👩🏻", "name": "woman: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F469 1F3FB", "html": "👩🏻", "category": "People & Body (person)", "order": ""}, + {"emoji": "👩", "name": "woman", "shortname": ":woman:", "unicode": "1f469", "html": "👩", "category": "People & Body (person)", "order": "117"}, + {"emoji": "🎆", "name": "fireworks", "shortname": ":fireworks:", "unicode": "1f386", "html": "🎆", "category": "Activities (event)", "order": "1758"}, + {"emoji": "🌠", "name": "shooting star", "shortname": ":stars:", "unicode": "1f320", "html": "🌠", "category": "Travel & Places (sky & weather)", "order": "1729"}, + {"emoji": "🆘", "name": "SOS button", "shortname": ":sos:", "unicode": "1f198", "html": "🆘", "category": "Symbols (alphanum)", "order": "2139"}, + {"emoji": "🍄", "name": "mushroom", "shortname": ":mushroom:", "unicode": "1f344", "html": "🍄", "category": "Food & Drink (food-vegetable)", "order": "1471"}, + {"emoji": "😾", "name": "pouting cat", "shortname": ":pouting_cat:", "unicode": "1f63e", "html": "😾", "category": "Smileys & Emotion (cat-face)", "order": "95"}, + {"emoji": "🛅", "name": "left luggage", "shortname": ":left_luggage:", "unicode": "1f6c5", "html": "🛅", "category": "Symbols (transport-sign)", "order": "1988"}, + {"emoji": "👠", "name": "high-heeled shoe", "shortname": ":high_heel:", "unicode": "1f460", "html": "👠", "category": "Objects (clothing)", "order": "1330"}, + {"emoji": "🎯", "name": "direct hit", "shortname": ":dart:", "unicode": "1f3af", "html": "🎯", "category": "Activities (game)", "order": "1798"}, + {"emoji": "🏊🏿‍♀", "name": "woman swimming: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F3CA 1F3FF 200D 2640", "html": "🏊🏿‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏊🏾‍♀", "name": "woman swimming: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F3CA 1F3FE 200D 2640", "html": "🏊🏾‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏊🏽‍♀", "name": "woman swimming: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F3CA 1F3FD 200D 2640", "html": "🏊🏽‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏊🏼‍♀", "name": "woman swimming: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F3CA 1F3FC 200D 2640", "html": "🏊🏼‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏊🏻‍♀", "name": "woman swimming: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F3CA 1F3FB 200D 2640", "html": "🏊🏻‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏊‍♀", "name": "woman swimming", "shortname": ":woman_swimming:", "unicode": "1F3CA 200D 2640", "html": "🏊‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏊🏿", "name": "person swimming: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F3CA 1F3FF", "html": "🏊🏿", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏊🏾", "name": "person swimming: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F3CA 1F3FE", "html": "🏊🏾", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏊🏽", "name": "person swimming: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F3CA 1F3FD", "html": "🏊🏽", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏊🏼", "name": "person swimming: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F3CA 1F3FC", "html": "🏊🏼", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏊🏻", "name": "person swimming: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F3CA 1F3FB", "html": "🏊🏻", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏊", "name": "person swimming", "shortname": ":swimmer:", "unicode": "1f3ca", "html": "🏊", "category": "People & Body (person-sport)", "order": "836"}, + {"emoji": "🔑", "name": "key", "shortname": ":key:", "unicode": "1f511", "html": "🔑", "category": "Objects (lock)", "order": "1948"}, + {"emoji": "👙", "name": "bikini", "shortname": ":bikini:", "unicode": "1f459", "html": "👙", "category": "Objects (clothing)", "order": "1321"}, + {"emoji": "👪", "name": "family", "shortname": ":family:", "unicode": "1f46a", "html": "👪", "category": "People & Body (family)", "order": "1044"}, + {"emoji": "✏", "name": "pencil", "shortname": ":pencil2:", "unicode": "270f", "html": "✏", "category": "Objects (writing)", "order": "1914"}, + {"emoji": "🐘", "name": "elephant", "shortname": ":elephant:", "unicode": "1f418", "html": "🐘", "category": "Animals & Nature (animal-mammal)", "order": "1373"}, + {"emoji": "💧", "name": "droplet", "shortname": ":droplet:", "unicode": "1f4a7", "html": "💧", "category": "Travel & Places (sky & weather)", "order": "1754"}, + {"emoji": "🌱", "name": "seedling", "shortname": ":seedling:", "unicode": "1f331", "html": "🌱", "category": "Animals & Nature (plant-other)", "order": "1437"}, + {"emoji": "🍎", "name": "red apple", "shortname": ":apple:", "unicode": "1f34e", "html": "🍎", "category": "Food & Drink (food-fruit)", "order": "1456"}, + {"emoji": "🆒", "name": "COOL button", "shortname": ":cool:", "unicode": "1f192", "html": "🆒", "category": "Symbols (alphanum)", "order": "2129"}, + {"emoji": "📞", "name": "telephone receiver", "shortname": ":telephone_receiver:", "unicode": "1f4de", "html": "📞", "category": "Objects (phone)", "order": "1841"}, + {"emoji": "💵", "name": "dollar banknote", "shortname": ":dollar:", "unicode": "1f4b5", "html": "💵", "category": "Objects (money)", "order": "1893"}, + {"emoji": "🏡", "name": "house with garden", "shortname": ":house_with_garden:", "unicode": "1f3e1", "html": "🏡", "category": "Travel & Places (place-building)", "order": "1560"}, + {"emoji": "📖", "name": "open book", "shortname": ":book:", "unicode": "1f4d6", "html": "📖", "category": "Objects (book-paper)", "order": "1876"}, + {"emoji": "💇🏿‍♂", "name": "man getting haircut: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F487 1F3FF 200D 2642", "html": "💇🏿‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💇🏾‍♂", "name": "man getting haircut: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F487 1F3FE 200D 2642", "html": "💇🏾‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💇🏽‍♂", "name": "man getting haircut: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F487 1F3FD 200D 2642", "html": "💇🏽‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💇🏼‍♂", "name": "man getting haircut: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F487 1F3FC 200D 2642", "html": "💇🏼‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💇🏻‍♂", "name": "man getting haircut: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F487 1F3FB 200D 2642", "html": "💇🏻‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💇‍♂", "name": "man getting haircut", "shortname": ":man_getting_haircut:", "unicode": "1F487 200D 2642", "html": "💇‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💇🏿", "name": "person getting haircut: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F487 1F3FF", "html": "💇🏿", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💇🏾", "name": "person getting haircut: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F487 1F3FE", "html": "💇🏾", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💇🏽", "name": "person getting haircut: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F487 1F3FD", "html": "💇🏽", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💇🏼", "name": "person getting haircut: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F487 1F3FC", "html": "💇🏼", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💇🏻", "name": "person getting haircut: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F487 1F3FB", "html": "💇🏻", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💇", "name": "person getting haircut", "shortname": ":haircut:", "unicode": "1f487", "html": "💇", "category": "People & Body (person-activity)", "order": "675"}, + {"emoji": "💻", "name": "laptop", "shortname": ":computer:", "unicode": "1f4bb", "html": "💻", "category": "Objects (computer)", "order": "1846"}, + {"emoji": "💡", "name": "light bulb", "shortname": ":bulb:", "unicode": "1f4a1", "html": "💡", "category": "Objects (light & video)", "order": "1871"}, + {"emoji": "❓", "name": "question mark", "shortname": ":question:", "unicode": "2753", "html": "❓", "category": "Symbols (other-symbol)", "order": "2098"}, + {"emoji": "🔙", "name": "BACK arrow", "shortname": ":back:", "unicode": "1f519", "html": "🔙", "category": "Symbols (arrow)", "order": "2018"}, + {"emoji": "👦🏿", "name": "boy: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F466 1F3FF", "html": "👦🏿", "category": "People & Body (person)", "order": ""}, + {"emoji": "👦🏾", "name": "boy: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F466 1F3FE", "html": "👦🏾", "category": "People & Body (person)", "order": ""}, + {"emoji": "👦🏽", "name": "boy: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F466 1F3FD", "html": "👦🏽", "category": "People & Body (person)", "order": ""}, + {"emoji": "👦🏼", "name": "boy: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F466 1F3FC", "html": "👦🏼", "category": "People & Body (person)", "order": ""}, + {"emoji": "👦🏻", "name": "boy: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F466 1F3FB", "html": "👦🏻", "category": "People & Body (person)", "order": ""}, + {"emoji": "👦", "name": "boy", "shortname": ":boy:", "unicode": "1f466", "html": "👦", "category": "People & Body (person)", "order": "99"}, + {"emoji": "🔐", "name": "locked with key", "shortname": ":closed_lock_with_key:", "unicode": "1f510", "html": "🔐", "category": "Objects (lock)", "order": "1947"}, + {"emoji": "🙎🏿‍♂", "name": "man pouting: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F64E 1F3FF 200D 2642", "html": "🙎🏿‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙎🏾‍♂", "name": "man pouting: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F64E 1F3FE 200D 2642", "html": "🙎🏾‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙎🏽‍♂", "name": "man pouting: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F64E 1F3FD 200D 2642", "html": "🙎🏽‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙎🏼‍♂", "name": "man pouting: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F64E 1F3FC 200D 2642", "html": "🙎🏼‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙎🏻‍♂", "name": "man pouting: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F64E 1F3FB 200D 2642", "html": "🙎🏻‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙎‍♂", "name": "man pouting", "shortname": ":man_pouting:", "unicode": "1F64E 200D 2642", "html": "🙎‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙎🏿", "name": "person pouting: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F64E 1F3FF", "html": "🙎🏿", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙎🏾", "name": "person pouting: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F64E 1F3FE", "html": "🙎🏾", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙎🏽", "name": "person pouting: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F64E 1F3FD", "html": "🙎🏽", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙎🏼", "name": "person pouting: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F64E 1F3FC", "html": "🙎🏼", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙎🏻", "name": "person pouting: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F64E 1F3FB", "html": "🙎🏻", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙎", "name": "person pouting", "shortname": ":person_with_pouting_face:", "unicode": "1f64e", "html": "🙎", "category": "People & Body (person-gesture)", "order": "513"}, + {"emoji": "🍊", "name": "tangerine", "shortname": ":tangerine:", "unicode": "1f34a", "html": "🍊", "category": "Food & Drink (food-fruit)", "order": "1452"}, + {"emoji": "↔️", "name": "left-right arrow", "shortname": ":leftright_arrow:", "unicode": "2194 FE0F", "html": "↔️", "category": "Symbols (arrow)", "order": ""}, + {"emoji": "🌅", "name": "sunrise", "shortname": ":sunrise:", "unicode": "1f305", "html": "🌅", "category": "Travel & Places (place-other)", "order": "1587"}, + {"emoji": "🍗", "name": "poultry leg", "shortname": ":poultry_leg:", "unicode": "1f357", "html": "🍗", "category": "Food & Drink (food-prepared)", "order": "1480"}, + {"emoji": "🔵", "name": "blue circle", "shortname": ":blue_circle:", "unicode": "1f535", "html": "🔵", "category": "Symbols (geometric)", "order": "2180"}, + {"emoji": "🚘", "name": "oncoming automobile", "shortname": ":oncoming_automobile:", "unicode": "1f698", "html": "🚘", "category": "Travel & Places (transport-ground)", "order": "1625"}, + {"emoji": "🍧", "name": "shaved ice", "shortname": ":shaved_ice:", "unicode": "1f367", "html": "🍧", "category": "Food & Drink (food-sweet)", "order": "1509"}, + {"emoji": "🇮🇹", "name": "flag: Italy", "shortname": ":it:", "unicode": "1F1EE 1F1F9", "html": "🇮", "category": "Flags (country-flag)", "order": ""}, + {"emoji": "🐦", "name": "bird", "shortname": ":bird:", "unicode": "1f426", "html": "🐦", "category": "Animals & Nature (animal-bird)", "order": "1393"}, + {"emoji": "🇬🇧", "name": "flag: United Kingdom", "shortname": ":gb:", "unicode": "1F1EC 1F1E7", "html": "🇬", "category": "Flags (country-flag)", "order": ""}, + {"emoji": "🌛", "name": "first quarter moon face", "shortname": ":first_quarter_moon_with_face:", "unicode": "1f31b", "html": "🌛", "category": "Travel & Places (sky & weather)", "order": "1721"}, + {"emoji": "👓", "name": "glasses", "shortname": ":eyeglasses:", "unicode": "1f453", "html": "👓", "category": "Objects (clothing)", "order": "1314"}, + {"emoji": "🐐", "name": "goat", "shortname": ":goat:", "unicode": "1f410", "html": "🐐", "category": "Animals & Nature (animal-mammal)", "order": "1370"}, + {"emoji": "🌃", "name": "night with stars", "shortname": ":night_with_stars:", "unicode": "1f303", "html": "🌃", "category": "Travel & Places (place-other)", "order": "1585"}, + {"emoji": "👵🏿", "name": "old woman: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F475 1F3FF", "html": "👵🏿", "category": "People & Body (person)", "order": ""}, + {"emoji": "👵🏾", "name": "old woman: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F475 1F3FE", "html": "👵🏾", "category": "People & Body (person)", "order": ""}, + {"emoji": "👵🏽", "name": "old woman: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F475 1F3FD", "html": "👵🏽", "category": "People & Body (person)", "order": ""}, + {"emoji": "👵🏼", "name": "old woman: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F475 1F3FC", "html": "👵🏼", "category": "People & Body (person)", "order": ""}, + {"emoji": "👵🏻", "name": "old woman: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F475 1F3FB", "html": "👵🏻", "category": "People & Body (person)", "order": ""}, + {"emoji": "👵", "name": "old woman", "shortname": ":older_woman:", "unicode": "1f475", "html": "👵", "category": "People & Body (person)", "order": "129"}, + {"emoji": "⚫", "name": "black circle", "shortname": ":black_circle:", "unicode": "26ab", "html": "⚫", "category": "Symbols (geometric)", "order": "2178"}, + {"emoji": "🌑", "name": "new moon", "shortname": ":new_moon:", "unicode": "1f311", "html": "🌑", "category": "Travel & Places (sky & weather)", "order": "1711"}, + {"emoji": "👬", "name": "men holding hands", "shortname": ":two_men_holding_hands:", "unicode": "1f46c", "html": "👬", "category": "People & Body (family)", "order": "1024"}, + {"emoji": "⚪", "name": "white circle", "shortname": ":white_circle:", "unicode": "26aa", "html": "⚪", "category": "Symbols (geometric)", "order": "2177"}, + {"emoji": "🛃", "name": "customs", "shortname": ":customs:", "unicode": "1f6c3", "html": "🛃", "category": "Symbols (transport-sign)", "order": "1986"}, + {"emoji": "🐠", "name": "tropical fish", "shortname": ":tropical_fish:", "unicode": "1f420", "html": "🐠", "category": "Animals & Nature (animal-marine)", "order": "1410"}, + {"emoji": "🏠", "name": "house", "shortname": ":house:", "unicode": "1f3e0", "html": "🏠", "category": "Travel & Places (place-building)", "order": "1559"}, + {"emoji": "🔃", "name": "clockwise vertical arrows", "shortname": ":arrows_clockwise:", "unicode": "1f503", "html": "🔃", "category": "Symbols (arrow)", "order": "2016"}, + {"emoji": "🌜", "name": "last quarter moon face", "shortname": ":last_quarter_moon_with_face:", "unicode": "1f31c", "html": "🌜", "category": "Travel & Places (sky & weather)", "order": "1722"}, + {"emoji": "📍", "name": "round pushpin", "shortname": ":round_pushpin:", "unicode": "1f4cd", "html": "📍", "category": "Objects (office)", "order": "1935"}, + {"emoji": "🌕", "name": "full moon", "shortname": ":full_moon:", "unicode": "1f315", "html": "🌕", "category": "Travel & Places (sky & weather)", "order": "1715"}, + {"emoji": "👟", "name": "running shoe", "shortname": ":athletic_shoe:", "unicode": "1f45f", "html": "👟", "category": "Objects (clothing)", "order": "1329"}, + {"emoji": "🍋", "name": "lemon", "shortname": ":lemon:", "unicode": "1f34b", "html": "🍋", "category": "Food & Drink (food-fruit)", "order": "1453"}, + {"emoji": "🍼", "name": "baby bottle", "shortname": ":baby_bottle:", "unicode": "1f37c", "html": "🍼", "category": "Food & Drink (drink)", "order": "1520"}, + {"emoji": "🎨", "name": "artist palette", "shortname": ":artist_palette:", "unicode": "1F3A8", "html": "🎨", "category": "Activities (arts & crafts)", "order": ""}, + {"emoji": "✉️", "name": "envelope", "shortname": ":envelope:", "unicode": "2709 FE0F", "html": "✉", "category": "Objects (mail)", "order": ""}, + {"emoji": "🍝", "name": "spaghetti", "shortname": ":spaghetti:", "unicode": "1f35d", "html": "🍝", "category": "Food & Drink (food-asian)", "order": "1501"}, + {"emoji": "🎐", "name": "wind chime", "shortname": ":wind_chime:", "unicode": "1f390", "html": "🎐", "category": "Activities (event)", "order": "1768"}, + {"emoji": "🍥", "name": "fish cake with swirl", "shortname": ":fish_cake:", "unicode": "1f365", "html": "🍥", "category": "Food & Drink (food-asian)", "order": "1506"}, + {"emoji": "🌲", "name": "evergreen tree", "shortname": ":evergreen_tree:", "unicode": "1f332", "html": "🌲", "category": "Animals & Nature (plant-other)", "order": "1438"}, + {"emoji": "🆙", "name": "UP! button", "shortname": ":up:", "unicode": "1f199", "html": "🆙", "category": "Symbols (alphanum)", "order": "2140"}, + {"emoji": "⬆️", "name": "up arrow", "shortname": ":arrow_up:", "unicode": "2b06", "html": "⬆", "category": "Symbols (arrow)", "order": "2002"}, + {"emoji": "↗️", "name": "up-right arrow", "shortname": ":arrow_upper_right:", "unicode": "2197", "html": "↗", "category": "Symbols (arrow)", "order": "2003"}, + {"emoji": "↘️", "name": "down-right arrow", "shortname": ":arrow_lower_right:", "unicode": "2198", "html": "↘", "category": "Symbols (arrow)", "order": "2005"}, + {"emoji": "↙️", "name": "down-left arrow", "shortname": ":arrow_lower_left:", "unicode": "2199", "html": "↙", "category": "Symbols (arrow)", "order": "2007"}, + {"emoji": "🎭", "name": "performing arts", "shortname": ":performing_arts:", "unicode": "1f3ad", "html": "🎭", "category": "Activities (arts & crafts)", "order": "1598"}, + {"emoji": "👃🏿", "name": "nose: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F443 1F3FF", "html": "👃🏿", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "👃🏾", "name": "nose: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F443 1F3FE", "html": "👃🏾", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "👃🏽", "name": "nose: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F443 1F3FD", "html": "👃🏽", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "👃🏼", "name": "nose: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F443 1F3FC", "html": "👃🏼", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "👃🏻", "name": "nose: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F443 1F3FB", "html": "👃🏻", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "👃", "name": "nose", "shortname": ":nose:", "unicode": "1f443", "html": "👃", "category": "People & Body (body-parts)", "order": "1272"}, + {"emoji": "🐽", "name": "pig nose", "shortname": ":pig_nose:", "unicode": "1f43d", "html": "🐽", "category": "Animals & Nature (animal-mammal)", "order": "1367"}, + {"emoji": "🐟", "name": "fish", "shortname": ":fish:", "unicode": "1f41f", "html": "🐟", "category": "Animals & Nature (animal-marine)", "order": "1409"}, + {"emoji": "👳🏿‍♀", "name": "woman wearing turban: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F473 1F3FF 200D 2640", "html": "👳🏿‍♀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👳🏾‍♀", "name": "woman wearing turban: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F473 1F3FE 200D 2640", "html": "👳🏾‍♀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👳🏽‍♀", "name": "woman wearing turban: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F473 1F3FD 200D 2640", "html": "👳🏽‍♀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👳🏼‍♀", "name": "woman wearing turban: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F473 1F3FC 200D 2640", "html": "👳🏼‍♀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👳🏻‍♀", "name": "woman wearing turban: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F473 1F3FB 200D 2640", "html": "👳🏻‍♀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👳‍♀", "name": "woman wearing turban", "shortname": ":woman_wearing_turban:", "unicode": "1F473 200D 2640", "html": "👳‍♀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👳🏿", "name": "person wearing turban: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F473 1F3FF", "html": "👳🏿", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👳🏾", "name": "person wearing turban: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F473 1F3FE", "html": "👳🏾", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👳🏽", "name": "person wearing turban: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F473 1F3FD", "html": "👳🏽", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👳🏼", "name": "person wearing turban: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F473 1F3FC", "html": "👳🏼", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👳🏻", "name": "person wearing turban: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F473 1F3FB", "html": "👳🏻", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👳", "name": "person wearing turban", "shortname": ":man_with_turban:", "unicode": "1f473", "html": "👳", "category": "People & Body (person-role)", "order": "411"}, + {"emoji": "🐨", "name": "koala", "shortname": ":koala:", "unicode": "1f428", "html": "🐨", "category": "Animals & Nature (animal-mammal)", "order": "1384"}, + {"emoji": "👂🏿", "name": "ear: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F442 1F3FF", "html": "👂🏿", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "👂🏾", "name": "ear: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F442 1F3FE", "html": "👂🏾", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "👂🏽", "name": "ear: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F442 1F3FD", "html": "👂🏽", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "👂🏼", "name": "ear: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F442 1F3FC", "html": "👂🏼", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "👂🏻", "name": "ear: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F442 1F3FB", "html": "👂🏻", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "👂", "name": "ear", "shortname": ":ear:", "unicode": "1f442", "html": "👂", "category": "People & Body (body-parts)", "order": "1266"}, + {"emoji": "✳️", "name": "eight-spoked asterisk", "shortname": ":eight_spoked_asterisk:", "unicode": "2733", "html": "✳", "category": "Symbols (other-symbol)", "order": "2093"}, + {"emoji": "🔹", "name": "small blue diamond", "shortname": ":small_blue_diamond:", "unicode": "1f539", "html": "🔹", "category": "Symbols (geometric)", "order": "2170"}, + {"emoji": "🚿", "name": "shower", "shortname": ":shower:", "unicode": "1f6bf", "html": "🚿", "category": "Objects (household)", "order": "1672"}, + {"emoji": "🐛", "name": "bug", "shortname": ":bug:", "unicode": "1f41b", "html": "🐛", "category": "Animals & Nature (animal-bug)", "order": "1420"}, + {"emoji": "🍜", "name": "steaming bowl", "shortname": ":ramen:", "unicode": "1f35c", "html": "🍜", "category": "Food & Drink (food-asian)", "order": "1500"}, + {"emoji": "🎩", "name": "top hat", "shortname": ":tophat:", "unicode": "1f3a9", "html": "🎩", "category": "Objects (clothing)", "order": "1335"}, + {"emoji": "👰🏿", "name": "bride with veil: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F470 1F3FF", "html": "👰🏿", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👰🏾", "name": "bride with veil: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F470 1F3FE", "html": "👰🏾", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👰🏽", "name": "bride with veil: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F470 1F3FD", "html": "👰🏽", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👰🏼", "name": "bride with veil: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F470 1F3FC", "html": "👰🏼", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👰🏻", "name": "bride with veil: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F470 1F3FB", "html": "👰🏻", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👰", "name": "bride with veil", "shortname": ":bride_with_veil:", "unicode": "1f470", "html": "👰", "category": "People & Body (person-role)", "order": "471"}, + {"emoji": "⛽", "name": "fuel pump", "shortname": ":fuelpump:", "unicode": "26fd", "html": "⛽", "category": "Travel & Places (transport-ground)", "order": "1636"}, + {"emoji": "🏁", "name": "chequered flag", "shortname": ":checkered_flag:", "unicode": "1f3c1", "html": "🏁", "category": "Flags (flag)", "order": "2181"}, + {"emoji": "🐴", "name": "horse face", "shortname": ":horse:", "unicode": "1f434", "html": "🐴", "category": "Animals & Nature (animal-mammal)", "order": "1356"}, + {"emoji": "⌚", "name": "watch", "shortname": ":watch:", "unicode": "231a", "html": "⌚", "category": "Travel & Places (time)", "order": "1682"}, + {"emoji": "🐵", "name": "monkey face", "shortname": ":monkey_face:", "unicode": "1f435", "html": "🐵", "category": "Animals & Nature (animal-mammal)", "order": "1342"}, + {"emoji": "🚼", "name": "baby symbol", "shortname": ":baby_symbol:", "unicode": "1f6bc", "html": "🚼", "category": "Symbols (transport-sign)", "order": "1983"}, + {"emoji": "🆕", "name": "NEW button", "shortname": ":new:", "unicode": "1f195", "html": "🆕", "category": "Symbols (alphanum)", "order": "2134"}, + {"emoji": "🆓", "name": "FREE button", "shortname": ":free:", "unicode": "1f193", "html": "🆓", "category": "Symbols (alphanum)", "order": "2130"}, + {"emoji": "🎇", "name": "sparkler", "shortname": ":sparkler:", "unicode": "1f387", "html": "🎇", "category": "Activities (event)", "order": "1759"}, + {"emoji": "🌽", "name": "ear of corn", "shortname": ":corn:", "unicode": "1f33d", "html": "🌽", "category": "Food & Drink (food-vegetable)", "order": "1468"}, + {"emoji": "🎾", "name": "tennis", "shortname": ":tennis:", "unicode": "1f3be", "html": "🎾", "category": "Activities (sport)", "order": "1787"}, + {"emoji": "⏰", "name": "alarm clock", "shortname": ":alarm_clock:", "unicode": "23f0", "html": "⏰", "category": "Travel & Places (time)", "order": "1683"}, + {"emoji": "🔋", "name": "battery", "shortname": ":battery:", "unicode": "1f50b", "html": "🔋", "category": "Objects (computer)", "order": "1844"}, + {"emoji": "❕", "name": "white exclamation mark", "shortname": ":grey_exclamation:", "unicode": "2755", "html": "❕", "category": "Symbols (other-symbol)", "order": "2100"}, + {"emoji": "🐺", "name": "wolf", "shortname": ":wolf:", "unicode": "1f43a", "html": "🐺", "category": "Animals & Nature (animal-mammal)", "order": "1348"}, + {"emoji": "🗿", "name": "moai", "shortname": ":moyai:", "unicode": "1f5ff", "html": "🗿", "category": "Objects (other-object)", "order": "1972"}, + {"emoji": "🐮", "name": "cow face", "shortname": ":cow:", "unicode": "1f42e", "html": "🐮", "category": "Animals & Nature (animal-mammal)", "order": "1360"}, + {"emoji": "📣", "name": "megaphone", "shortname": ":mega:", "unicode": "1f4e3", "html": "📣", "category": "Objects (sound)", "order": "1819"}, + {"emoji": "👴🏿", "name": "old man: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F474 1F3FF", "html": "👴🏿", "category": "People & Body (person)", "order": ""}, + {"emoji": "👴🏾", "name": "old man: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F474 1F3FE", "html": "👴🏾", "category": "People & Body (person)", "order": ""}, + {"emoji": "👴🏽", "name": "old man: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F474 1F3FD", "html": "👴🏽", "category": "People & Body (person)", "order": ""}, + {"emoji": "👴🏼", "name": "old man: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F474 1F3FC", "html": "👴🏼", "category": "People & Body (person)", "order": ""}, + {"emoji": "👴🏻", "name": "old man: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F474 1F3FB", "html": "👴🏻", "category": "People & Body (person)", "order": ""}, + {"emoji": "👴", "name": "old man", "shortname": ":older_man:", "unicode": "1f474", "html": "👴", "category": "People & Body (person)", "order": "123"}, + {"emoji": "👗", "name": "dress", "shortname": ":dress:", "unicode": "1f457", "html": "👗", "category": "Objects (clothing)", "order": "1319"}, + {"emoji": "🔗", "name": "link", "shortname": ":link:", "unicode": "1f517", "html": "🔗", "category": "Objects (tool)", "order": "1965"}, + {"emoji": "🐔", "name": "chicken", "shortname": ":chicken:", "unicode": "1f414", "html": "🐔", "category": "Animals & Nature (animal-bird)", "order": "1388"}, + {"emoji": "🍳", "name": "cooking", "shortname": ":cooking:", "unicode": "1F373", "html": "🍳", "category": "Food & Drink (food-prepared)", "order": ""}, + {"emoji": "🐋", "name": "whale", "shortname": ":whale2:", "unicode": "1f40b", "html": "🐋", "category": "Animals & Nature (animal-marine)", "order": "1407"}, + {"emoji": "↖", "name": "up-left arrow", "shortname": ":arrow_upper_left:", "unicode": "2196", "html": "↖", "category": "Symbols (arrow)", "order": "2009"}, + {"emoji": "🌳", "name": "deciduous tree", "shortname": ":deciduous_tree:", "unicode": "1f333", "html": "🌳", "category": "Animals & Nature (plant-other)", "order": "1439"}, + {"emoji": "🍱", "name": "bento box", "shortname": ":bento:", "unicode": "1f371", "html": "🍱", "category": "Food & Drink (food-asian)", "order": "1495"}, + {"emoji": "📌", "name": "pushpin", "shortname": ":pushpin:", "unicode": "1f4cc", "html": "📌", "category": "Objects (office)", "order": "1934"}, + {"emoji": "🔜", "name": "SOON arrow", "shortname": ":soon:", "unicode": "1f51c", "html": "🔜", "category": "Symbols (arrow)", "order": "2021"}, + {"emoji": "🔁", "name": "repeat button", "shortname": ":repeat:", "unicode": "1f501", "html": "🔁", "category": "Symbols (av-symbol)", "order": "2049"}, + {"emoji": "🐉", "name": "dragon", "shortname": ":dragon:", "unicode": "1f409", "html": "🐉", "category": "Animals & Nature (animal-reptile)", "order": "1405"}, + {"emoji": "🐹", "name": "hamster", "shortname": ":hamster:", "unicode": "1f439", "html": "🐹", "category": "Animals & Nature (animal-mammal)", "order": "1378"}, + {"emoji": "⛳", "name": "flag in hole", "shortname": ":golf:", "unicode": "26f3", "html": "⛳", "category": "Activities (sport)", "order": "1799"}, + {"emoji": "🏄🏿‍♀", "name": "woman surfing: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F3C4 1F3FF 200D 2640", "html": "🏄🏿‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏄🏾‍♀", "name": "woman surfing: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F3C4 1F3FE 200D 2640", "html": "🏄🏾‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏄🏽‍♀", "name": "woman surfing: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F3C4 1F3FD 200D 2640", "html": "🏄🏽‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏄🏼‍♀", "name": "woman surfing: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F3C4 1F3FC 200D 2640", "html": "🏄🏼‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏄🏻‍♀", "name": "woman surfing: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F3C4 1F3FB 200D 2640", "html": "🏄🏻‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏄‍♀", "name": "woman surfing", "shortname": ":woman_surfing:", "unicode": "1F3C4 200D 2640", "html": "🏄‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏄🏿", "name": "person surfing: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F3C4 1F3FF", "html": "🏄🏿", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏄🏾", "name": "person surfing: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F3C4 1F3FE", "html": "🏄🏾", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏄🏽", "name": "person surfing: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F3C4 1F3FD", "html": "🏄🏽", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏄🏼", "name": "person surfing: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F3C4 1F3FC", "html": "🏄🏼", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏄🏻", "name": "person surfing: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F3C4 1F3FB", "html": "🏄🏻", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏄", "name": "person surfing", "shortname": ":surfer:", "unicode": "1f3c4", "html": "🏄", "category": "People & Body (person-sport)", "order": "800"}, + {"emoji": "🐭", "name": "mouse face", "shortname": ":mouse:", "unicode": "1f42d", "html": "🐭", "category": "Animals & Nature (animal-mammal)", "order": "1375"}, + {"emoji": "🌒", "name": "waxing crescent moon", "shortname": ":waxing_crescent_moon:", "unicode": "1f312", "html": "🌒", "category": "Travel & Places (sky & weather)", "order": "1712"}, + {"emoji": "🚙", "name": "sport utility vehicle", "shortname": ":blue_car:", "unicode": "1f699", "html": "🚙", "category": "Travel & Places (transport-ground)", "order": "1626"}, + {"emoji": "🅰️", "name": "A button (blood type)", "shortname": ":a:", "unicode": "1f170", "html": "🅰", "category": "Symbols (alphanum)", "order": "2125"}, + {"emoji": "⁉️", "name": "exclamation question mark", "shortname": ":interrobang:", "unicode": "2049", "html": "⁉", "category": "Symbols (other-symbol)", "order": "2097"}, + {"emoji": "🈹", "name": "Japanese discount button", "shortname": ":u5272:", "unicode": "1f239", "html": "🈹", "category": "Symbols (alphanum)", "order": "2148"}, + {"emoji": "🔌", "name": "electric plug", "shortname": ":electric_plug:", "unicode": "1f50c", "html": "🔌", "category": "Objects (computer)", "order": "1845"}, + {"emoji": "🌓", "name": "first quarter moon", "shortname": ":first_quarter_moon:", "unicode": "1f313", "html": "🌓", "category": "Travel & Places (sky & weather)", "order": "1713"}, + {"emoji": "♋", "name": "Cancer", "shortname": ":cancer:", "unicode": "264b", "html": "♋", "category": "Symbols (zodiac)", "order": "2038"}, + {"emoji": "🔱", "name": "trident emblem", "shortname": ":trident:", "unicode": "1f531", "html": "🔱", "category": "Symbols (other-symbol)", "order": "2076"}, + {"emoji": "🍞", "name": "bread", "shortname": ":bread:", "unicode": "1f35e", "html": "🍞", "category": "Food & Drink (food-prepared)", "order": "1474"}, + {"emoji": "👮🏿‍♀", "name": "woman police officer: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F46E 1F3FF 200D 2640", "html": "👮🏿‍♀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👮🏾‍♀", "name": "woman police officer: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F46E 1F3FE 200D 2640", "html": "👮🏾‍♀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👮🏽‍♀", "name": "woman police officer: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F46E 1F3FD 200D 2640", "html": "👮🏽‍♀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👮🏼‍♀", "name": "woman police officer: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F46E 1F3FC 200D 2640", "html": "👮🏼‍♀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👮🏻‍♀", "name": "woman police officer: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F46E 1F3FB 200D 2640", "html": "👮🏻‍♀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👮‍♀", "name": "woman police officer", "shortname": ":woman_police_officer:", "unicode": "1F46E 200D 2640", "html": "👮‍♀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👮🏿", "name": "police officer: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F46E 1F3FF", "html": "👮🏿", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👮🏾", "name": "police officer: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F46E 1F3FE", "html": "👮🏾", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👮🏽", "name": "police officer: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F46E 1F3FD", "html": "👮🏽", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👮🏼", "name": "police officer: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F46E 1F3FC", "html": "👮🏼", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👮🏻", "name": "police officer: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F46E 1F3FB", "html": "👮🏻", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👮", "name": "police officer", "shortname": ":cop:", "unicode": "1f46e", "html": "👮", "category": "People & Body (person-role)", "order": "339"}, + {"emoji": "🍵", "name": "teacup without handle", "shortname": ":tea:", "unicode": "1f375", "html": "🍵", "category": "Food & Drink (drink)", "order": "1523"}, + {"emoji": "🎣", "name": "fishing pole", "shortname": ":fishing_pole_and_fish:", "unicode": "1f3a3", "html": "🎣", "category": "Activities (sport)", "order": "1801"}, + {"emoji": "🌔", "name": "waxing gibbous moon", "shortname": ":waxing_gibbous_moon:", "unicode": "1F314", "html": "🌔", "category": "Travel & Places (sky & weather)", "order": ""}, + {"emoji": "🚲", "name": "bicycle", "shortname": ":bike:", "unicode": "1f6b2", "html": "🚲", "category": "Travel & Places (transport-ground)", "order": "1630"}, + {"emoji": "👤", "name": "bust in silhouette", "shortname": ":bust_in_silhouette:", "unicode": "1F464", "html": "👤", "category": "People & Body (person-symbol)", "order": ""}, + {"emoji": "🍚", "name": "cooked rice", "shortname": ":rice:", "unicode": "1f35a", "html": "🍚", "category": "Food & Drink (food-asian)", "order": "1498"}, + {"emoji": "📻", "name": "radio", "shortname": ":radio:", "unicode": "1f4fb", "html": "📻", "category": "Objects (music)", "order": "1831"}, + {"emoji": "🐤", "name": "baby chick", "shortname": ":baby_chick:", "unicode": "1f424", "html": "🐤", "category": "Animals & Nature (animal-bird)", "order": "1391"}, + {"emoji": "⤵️", "name": "right arrow curving down", "shortname": ":arrow_heading_down:", "unicode": "2935", "html": "⤵", "category": "Symbols (arrow)", "order": "2015"}, + {"emoji": "🌘", "name": "waning crescent moon", "shortname": ":waning_crescent_moon:", "unicode": "1f318", "html": "🌘", "category": "Travel & Places (sky & weather)", "order": "1718"}, + {"emoji": "↕", "name": "up-down arrow", "shortname": ":arrow_up_down:", "unicode": "2195", "html": "↕", "category": "Symbols (arrow)", "order": "2010"}, + {"emoji": "🇪", "name": "", "shortname": "", "unicode": "", "html": "🇪", "category": "", "order": ""}, + {"emoji": "🌗", "name": "last quarter moon", "shortname": ":last_quarter_moon:", "unicode": "1f317", "html": "🌗", "category": "Travel & Places (sky & weather)", "order": "1717"}, + {"emoji": "🔘", "name": "radio button", "shortname": ":radio_button:", "unicode": "1f518", "html": "🔘", "category": "Symbols (geometric)", "order": "2174"}, + {"emoji": "🐑", "name": "ewe", "shortname": ":sheep:", "unicode": "1f411", "html": "🐑", "category": "Animals & Nature (animal-mammal)", "order": "1369"}, + {"emoji": "👱🏿‍♀", "name": "woman: dark skin tone, blond hair", "shortname": ":dark_skin_tone_blond_hair:", "unicode": "1F471 1F3FF 200D 2640", "html": "👱🏿‍♀", "category": "People & Body (person)", "order": ""}, + {"emoji": "👱🏾‍♀", "name": "woman: medium-dark skin tone, blond hair", "shortname": ":mediumdark_skin_tone_blond_hair:", "unicode": "1F471 1F3FE 200D 2640", "html": "👱🏾‍♀", "category": "People & Body (person)", "order": ""}, + {"emoji": "👱🏽‍♀", "name": "woman: medium skin tone, blond hair", "shortname": ":medium_skin_tone_blond_hair:", "unicode": "1F471 1F3FD 200D 2640", "html": "👱🏽‍♀", "category": "People & Body (person)", "order": ""}, + {"emoji": "👱🏼‍♀", "name": "woman: medium-light skin tone, blond hair", "shortname": ":mediumlight_skin_tone_blond_hair:", "unicode": "1F471 1F3FC 200D 2640", "html": "👱🏼‍♀", "category": "People & Body (person)", "order": ""}, + {"emoji": "👱🏻‍♀", "name": "woman: light skin tone, blond hair", "shortname": ":light_skin_tone_blond_hair:", "unicode": "1F471 1F3FB 200D 2640", "html": "👱🏻‍♀", "category": "People & Body (person)", "order": ""}, + {"emoji": "👱‍♀", "name": "woman: blond hair", "shortname": ":blond_hair:", "unicode": "1F471 200D 2640", "html": "👱‍♀", "category": "People & Body (person)", "order": ""}, + {"emoji": "👱🏿", "name": "person: dark skin tone, blond hair", "shortname": ":dark_skin_tone_blond_hair:", "unicode": "1F471 1F3FF", "html": "👱🏿", "category": "People & Body (person)", "order": ""}, + {"emoji": "👱🏾", "name": "person: medium-dark skin tone, blond hair", "shortname": ":mediumdark_skin_tone_blond_hair:", "unicode": "1F471 1F3FE", "html": "👱🏾", "category": "People & Body (person)", "order": ""}, + {"emoji": "👱🏽", "name": "person: medium skin tone, blond hair", "shortname": ":medium_skin_tone_blond_hair:", "unicode": "1F471 1F3FD", "html": "👱🏽", "category": "People & Body (person)", "order": ""}, + {"emoji": "👱🏼", "name": "person: medium-light skin tone, blond hair", "shortname": ":mediumlight_skin_tone_blond_hair:", "unicode": "1F471 1F3FC", "html": "👱🏼", "category": "People & Body (person)", "order": ""}, + {"emoji": "👱🏻", "name": "person: light skin tone, blond hair", "shortname": ":light_skin_tone_blond_hair:", "unicode": "1F471 1F3FB", "html": "👱🏻", "category": "People & Body (person)", "order": ""}, + {"emoji": "👱", "name": "person: blond hair", "shortname": ":person_with_blond_hair:", "unicode": "1f471", "html": "👱", "category": "People & Body (person)", "order": "429"}, + {"emoji": "🌖", "name": "waning gibbous moon", "shortname": ":waning_gibbous_moon:", "unicode": "1f316", "html": "🌖", "category": "Travel & Places (sky & weather)", "order": "1716"}, + {"emoji": "🔒", "name": "locked", "shortname": ":lock:", "unicode": "1f512", "html": "🔒", "category": "Objects (lock)", "order": "1944"}, + {"emoji": "🍏", "name": "green apple", "shortname": ":green_apple:", "unicode": "1f34f", "html": "🍏", "category": "Food & Drink (food-fruit)", "order": "1457"}, + {"emoji": "👺", "name": "goblin", "shortname": ":japanese_goblin:", "unicode": "1f47a", "html": "👺", "category": "Smileys & Emotion (face-costume)", "order": "79"}, + {"emoji": "➰", "name": "curly loop", "shortname": ":curly_loop:", "unicode": "27b0", "html": "➰", "category": "Symbols (other-symbol)", "order": "2090"}, + {"emoji": "🚩", "name": "triangular flag", "shortname": ":triangular_flag_on_post:", "unicode": "1f6a9", "html": "🚩", "category": "Flags (flag)", "order": "2182"}, + {"emoji": "🔄", "name": "counterclockwise arrows button", "shortname": ":arrows_counterclockwise:", "unicode": "1f504", "html": "🔄", "category": "Symbols (arrow)", "order": "2017"}, + {"emoji": "🐎", "name": "horse", "shortname": ":racehorse:", "unicode": "1f40e", "html": "🐎", "category": "Animals & Nature (animal-mammal)", "order": "1357"}, + {"emoji": "🍤", "name": "fried shrimp", "shortname": ":fried_shrimp:", "unicode": "1f364", "html": "🍤", "category": "Food & Drink (food-asian)", "order": "1505"}, + {"emoji": "🌄", "name": "sunrise over mountains", "shortname": ":sunrise_over_mountains:", "unicode": "1f304", "html": "🌄", "category": "Travel & Places (place-other)", "order": "1586"}, + {"emoji": "🌋", "name": "volcano", "shortname": ":volcano:", "unicode": "1f30b", "html": "🌋", "category": "Travel & Places (place-geographic)", "order": "1546"}, + {"emoji": "🐓", "name": "rooster", "shortname": ":rooster:", "unicode": "1f413", "html": "🐓", "category": "Animals & Nature (animal-bird)", "order": "1389"}, + {"emoji": "📥", "name": "inbox tray", "shortname": ":inbox_tray:", "unicode": "1f4e5", "html": "📥", "category": "Objects (mail)", "order": "1906"}, + {"emoji": "💒", "name": "wedding", "shortname": ":wedding:", "unicode": "1f492", "html": "💒", "category": "Travel & Places (place-building)", "order": "1574"}, + {"emoji": "🍣", "name": "sushi", "shortname": ":sushi:", "unicode": "1f363", "html": "🍣", "category": "Food & Drink (food-asian)", "order": "1504"}, + {"emoji": "〰", "name": "wavy dash", "shortname": ":wavy_dash:", "unicode": "3030", "html": "〰", "category": "Symbols (other-symbol)", "order": "2102"}, + {"emoji": "🍨", "name": "ice cream", "shortname": ":ice_cream:", "unicode": "1f368", "html": "🍨", "category": "Food & Drink (food-sweet)", "order": "1510"}, + {"emoji": "⏪", "name": "fast reverse button", "shortname": ":rewind:", "unicode": "23ea", "html": "⏪", "category": "Symbols (av-symbol)", "order": "2056"}, + {"emoji": "🍅", "name": "tomato", "shortname": ":tomato:", "unicode": "1f345", "html": "🍅", "category": "Food & Drink (food-fruit)", "order": "1463"}, + {"emoji": "🐇", "name": "rabbit", "shortname": ":rabbit2:", "unicode": "1f407", "html": "🐇", "category": "Animals & Nature (animal-mammal)", "order": "1380"}, + {"emoji": "✴️", "name": "eight-pointed star", "shortname": ":eight_pointed_black_star:", "unicode": "2734", "html": "✴", "category": "Symbols (other-symbol)", "order": "2094"}, + {"emoji": "🔺", "name": "red triangle pointed up", "shortname": ":small_red_triangle:", "unicode": "1f53a", "html": "🔺", "category": "Symbols (geometric)", "order": "2171"}, + {"emoji": "🔆", "name": "bright button", "shortname": ":high_brightness:", "unicode": "1f506", "html": "🔆", "category": "Symbols (av-symbol)", "order": "2068"}, + {"emoji": "➕", "name": "plus sign", "shortname": ":heavy_plus_sign:", "unicode": "2795", "html": "➕", "category": "Symbols (other-symbol)", "order": "2084"}, + {"emoji": "👲🏿", "name": "man with skullcap: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F472 1F3FF", "html": "👲🏿", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👲🏾", "name": "man with skullcap: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F472 1F3FE", "html": "👲🏾", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👲🏽", "name": "man with skullcap: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F472 1F3FD", "html": "👲🏽", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👲🏼", "name": "man with skullcap: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F472 1F3FC", "html": "👲🏼", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👲🏻", "name": "man with skullcap: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F472 1F3FB", "html": "👲🏻", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👲", "name": "man with skullcap", "shortname": ":man_with_gua_pi_mao:", "unicode": "1f472", "html": "👲", "category": "People & Body (person-role)", "order": "489"}, + {"emoji": "🏪", "name": "convenience store", "shortname": ":convenience_store:", "unicode": "1f3ea", "html": "🏪", "category": "Travel & Places (place-building)", "order": "1568"}, + {"emoji": "👥", "name": "busts in silhouette", "shortname": ":busts_in_silhouette:", "unicode": "1f465", "html": "👥", "category": "People & Body (person-symbol)", "order": "767"}, + {"emoji": "🐞", "name": "lady beetle", "shortname": ":beetle:", "unicode": "1f41e", "html": "🐞", "category": "Animals & Nature (animal-bug)", "order": "1423"}, + {"emoji": "🔻", "name": "red triangle pointed down", "shortname": ":small_red_triangle_down:", "unicode": "1f53b", "html": "🔻", "category": "Symbols (geometric)", "order": "2172"}, + {"emoji": "🇩🇪", "name": "flag: Germany", "shortname": ":ger:", "unicode": "1F1E9 1F1EA", "html": "🇩", "category": "Flags (country-flag)", "order": ""}, + {"emoji": "⤴️", "name": "right arrow curving up", "shortname": ":arrow_heading_up:", "unicode": "2934", "html": "⤴", "category": "Symbols (arrow)", "order": "2014"}, + {"emoji": "📛", "name": "name badge", "shortname": ":name_badge:", "unicode": "1f4db", "html": "📛", "category": "Symbols (other-symbol)", "order": "2073"}, + {"emoji": "🛀🏿", "name": "person taking bath: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F6C0 1F3FF", "html": "🛀🏿", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🛀🏾", "name": "person taking bath: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F6C0 1F3FE", "html": "🛀🏾", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🛀🏽", "name": "person taking bath: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F6C0 1F3FD", "html": "🛀🏽", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🛀🏼", "name": "person taking bath: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F6C0 1F3FC", "html": "🛀🏼", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🛀🏻", "name": "person taking bath: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F6C0 1F3FB", "html": "🛀🏻", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🛀", "name": "person taking bath", "shortname": ":bath:", "unicode": "1f6c0", "html": "🛀", "category": "People & Body (person-resting)", "order": "1673"}, + {"emoji": "⛔", "name": "no entry", "shortname": ":no_entry:", "unicode": "26d4", "html": "⛔", "category": "Symbols (warning)", "order": "1991"}, + {"emoji": "🐊", "name": "crocodile", "shortname": ":crocodile:", "unicode": "1f40a", "html": "🐊", "category": "Animals & Nature (animal-reptile)", "order": "1400"}, + {"emoji": "🌰", "name": "chestnut", "shortname": ":chestnut:", "unicode": "1F330", "html": "🌰", "category": "Food & Drink (food-vegetable)", "order": ""}, + {"emoji": "🐕", "name": "dog", "shortname": ":dog2:", "unicode": "1f415", "html": "🐕", "category": "Animals & Nature (animal-mammal)", "order": "1346"}, + {"emoji": "🐈", "name": "cat", "shortname": ":cat2:", "unicode": "1f408", "html": "🐈", "category": "Animals & Nature (animal-mammal)", "order": "1351"}, + {"emoji": "🔨", "name": "hammer", "shortname": ":hammer:", "unicode": "1f528", "html": "🔨", "category": "Objects (tool)", "order": "1950"}, + {"emoji": "🍖", "name": "meat on bone", "shortname": ":meat_on_bone:", "unicode": "1f356", "html": "🍖", "category": "Food & Drink (food-prepared)", "order": "1479"}, + {"emoji": "🐚", "name": "spiral shell", "shortname": ":shell:", "unicode": "1f41a", "html": "🐚", "category": "Animals & Nature (animal-marine)", "order": "1414"}, + {"emoji": "❇️", "name": "sparkle", "shortname": ":sparkle:", "unicode": "2747", "html": "❇", "category": "Symbols (other-symbol)", "order": "2095"}, + {"emoji": "⛵", "name": "sailboat", "shortname": ":sailboat:", "unicode": "26F5", "html": "⛵", "category": "Travel & Places (transport-water)", "order": ""}, + {"emoji": "🅱️", "name": "B button (blood type)", "shortname": ":b:", "unicode": "1f171", "html": "🅱", "category": "Symbols (alphanum)", "order": "2127"}, + {"emoji": "Ⓜ️", "name": "circled M", "shortname": ":m:", "unicode": "24c2", "html": "Ⓜ", "category": "Symbols (alphanum)", "order": "2133"}, + {"emoji": "🐩", "name": "poodle", "shortname": ":poodle:", "unicode": "1f429", "html": "🐩", "category": "Animals & Nature (animal-mammal)", "order": "1347"}, + {"emoji": "♒", "name": "Aquarius", "shortname": ":aquarius:", "unicode": "2652", "html": "♒", "category": "Symbols (zodiac)", "order": "2045"}, + {"emoji": "🍲", "name": "pot of food", "shortname": ":stew:", "unicode": "1f372", "html": "🍲", "category": "Food & Drink (food-prepared)", "order": "1492"}, + {"emoji": "👖", "name": "jeans", "shortname": ":jeans:", "unicode": "1f456", "html": "👖", "category": "Objects (clothing)", "order": "1318"}, + {"emoji": "🍯", "name": "honey pot", "shortname": ":honey_pot:", "unicode": "1f36f", "html": "🍯", "category": "Food & Drink (food-sweet)", "order": "1519"}, + {"emoji": "🎹", "name": "musical keyboard", "shortname": ":musical_keyboard:", "unicode": "1f3b9", "html": "🎹", "category": "Objects (musical-instrument)", "order": "1834"}, + {"emoji": "🔓", "name": "unlocked", "shortname": ":unlock:", "unicode": "1f513", "html": "🔓", "category": "Objects (lock)", "order": "1945"}, + {"emoji": "✒", "name": "black nib", "shortname": ":black_nib:", "unicode": "2712", "html": "✒", "category": "Objects (writing)", "order": "1915"}, + {"emoji": "🗽", "name": "Statue of Liberty", "shortname": ":statue_of_liberty:", "unicode": "1f5fd", "html": "🗽", "category": "Travel & Places (place-building)", "order": "1576"}, + {"emoji": "💲", "name": "heavy dollar sign", "shortname": ":heavy_dollar_sign:", "unicode": "1f4b2", "html": "💲", "category": "Objects (money)", "order": "1900"}, + {"emoji": "🏂", "name": "snowboarder", "shortname": ":snowboarder:", "unicode": "1f3c2", "html": "🏂", "category": "People & Body (person-sport)", "order": "776"}, + {"emoji": "💮", "name": "white flower", "shortname": ":white_flower:", "unicode": "1f4ae", "html": "💮", "category": "Animals & Nature (plant-flower)", "order": "1429"}, + {"emoji": "👔", "name": "necktie", "shortname": ":necktie:", "unicode": "1f454", "html": "👔", "category": "Objects (clothing)", "order": "1316"}, + {"emoji": "💠", "name": "diamond with a dot", "shortname": ":diamond_shape_with_a_dot_inside:", "unicode": "1f4a0", "html": "💠", "category": "Symbols (geometric)", "order": "2173"}, + {"emoji": "♈", "name": "Aries", "shortname": ":aries:", "unicode": "2648", "html": "♈", "category": "Symbols (zodiac)", "order": "2035"}, + {"emoji": "🚺", "name": "women’s room", "shortname": ":womens:", "unicode": "1f6ba", "html": "🚺", "category": "Symbols (transport-sign)", "order": "1981"}, + {"emoji": "🐜", "name": "ant", "shortname": ":ant:", "unicode": "1f41c", "html": "🐜", "category": "Animals & Nature (animal-bug)", "order": "1421"}, + {"emoji": "♏", "name": "Scorpio", "shortname": ":scorpius:", "unicode": "264f", "html": "♏", "category": "Symbols (zodiac)", "order": "2042"}, + {"emoji": "🌇", "name": "sunset", "shortname": ":city_sunset:", "unicode": "1f307", "html": "🌇", "category": "Travel & Places (place-other)", "order": "1589"}, + {"emoji": "⏳", "name": "hourglass not done", "shortname": ":hourglass_flowing_sand:", "unicode": "23f3", "html": "⏳", "category": "Travel & Places (time)", "order": "1681"}, + {"emoji": "🅾️", "name": "O button (blood type)", "shortname": ":o2:", "unicode": "1f17e", "html": "🅾", "category": "Symbols (alphanum)", "order": "2136"}, + {"emoji": "🐲", "name": "dragon face", "shortname": ":dragon_face:", "unicode": "1f432", "html": "🐲", "category": "Animals & Nature (animal-reptile)", "order": "1404"}, + {"emoji": "🐌", "name": "snail", "shortname": ":snail:", "unicode": "1f40c", "html": "🐌", "category": "Animals & Nature (animal-bug)", "order": "1419"}, + {"emoji": "📀", "name": "dvd", "shortname": ":dvd:", "unicode": "1f4c0", "html": "📀", "category": "Objects (computer)", "order": "1855"}, + {"emoji": "👕", "name": "t-shirt", "shortname": ":shirt:", "unicode": "1f455", "html": "👕", "category": "Objects (clothing)", "order": "1317"}, + {"emoji": "🎲", "name": "game die", "shortname": ":game_die:", "unicode": "1f3b2", "html": "🎲", "category": "Activities (game)", "order": "1806"}, + {"emoji": "➖", "name": "minus sign", "shortname": ":heavy_minus_sign:", "unicode": "2796", "html": "➖", "category": "Symbols (other-symbol)", "order": "2088"}, + {"emoji": "🎎", "name": "Japanese dolls", "shortname": ":dolls:", "unicode": "1f38e", "html": "🎎", "category": "Activities (event)", "order": "1766"}, + {"emoji": "♐", "name": "Sagittarius", "shortname": ":sagittarius:", "unicode": "2650", "html": "♐", "category": "Symbols (zodiac)", "order": "2043"}, + {"emoji": "🎱", "name": "pool 8 ball", "shortname": ":8ball:", "unicode": "1f3b1", "html": "🎱", "category": "Activities (game)", "order": "1788"}, + {"emoji": "🚌", "name": "bus", "shortname": ":bus:", "unicode": "1f68c", "html": "🚌", "category": "Travel & Places (transport-ground)", "order": "1614"}, + {"emoji": "🍮", "name": "custard", "shortname": ":custard:", "unicode": "1f36e", "html": "🍮", "category": "Food & Drink (food-sweet)", "order": "1518"}, + {"emoji": "🎌", "name": "crossed flags", "shortname": ":crossed_flags:", "unicode": "1f38c", "html": "🎌", "category": "Flags (flag)", "order": "2183"}, + {"emoji": "〽️", "name": "part alternation mark", "shortname": ":part_alternation_mark:", "unicode": "303d", "html": "〽", "category": "Symbols (other-symbol)", "order": "2092"}, + {"emoji": "🐫", "name": "two-hump camel", "shortname": ":camel:", "unicode": "1f42b", "html": "🐫", "category": "Animals & Nature (animal-mammal)", "order": "1372"}, + {"emoji": "🍛", "name": "curry rice", "shortname": ":curry:", "unicode": "1f35b", "html": "🍛", "category": "Food & Drink (food-asian)", "order": "1499"}, + {"emoji": "🚂", "name": "locomotive", "shortname": ":steam_locomotive:", "unicode": "1f682", "html": "🚂", "category": "Travel & Places (transport-ground)", "order": "1602"}, + {"emoji": "🏥", "name": "hospital", "shortname": ":hospital:", "unicode": "1f3e5", "html": "🏥", "category": "Travel & Places (place-building)", "order": "1564"}, + {"emoji": "🇯🇵", "name": "flag: Japan", "shortname": ":jp:", "unicode": "1F1EF 1F1F5", "html": "🇯", "category": "Flags (country-flag)", "order": ""}, + {"emoji": "🔷", "name": "large blue diamond", "shortname": ":large_blue_diamond:", "unicode": "1f537", "html": "🔷", "category": "Symbols (geometric)", "order": "2168"}, + {"emoji": "🎋", "name": "tanabata tree", "shortname": ":tanabata_tree:", "unicode": "1f38b", "html": "🎋", "category": "Activities (event)", "order": "1764"}, + {"emoji": "🔔", "name": "bell", "shortname": ":bell:", "unicode": "1f514", "html": "🔔", "category": "Objects (sound)", "order": "1821"}, + {"emoji": "♌", "name": "Leo", "shortname": ":leo:", "unicode": "264c", "html": "♌", "category": "Symbols (zodiac)", "order": "2039"}, + {"emoji": "♊", "name": "Gemini", "shortname": ":gemini:", "unicode": "264a", "html": "♊", "category": "Symbols (zodiac)", "order": "2037"}, + {"emoji": "🍐", "name": "pear", "shortname": ":pear:", "unicode": "1f350", "html": "🍐", "category": "Food & Drink (food-fruit)", "order": "1458"}, + {"emoji": "🔶", "name": "large orange diamond", "shortname": ":large_orange_diamond:", "unicode": "1f536", "html": "🔶", "category": "Symbols (geometric)", "order": "2167"}, + {"emoji": "♉", "name": "Taurus", "shortname": ":taurus:", "unicode": "2649", "html": "♉", "category": "Symbols (zodiac)", "order": "2036"}, + {"emoji": "🌐", "name": "globe with meridians", "shortname": ":globe_with_meridians:", "unicode": "1f310", "html": "🌐", "category": "Travel & Places (place-map)", "order": "1541"}, + {"emoji": "🚪", "name": "door", "shortname": ":door:", "unicode": "1f6aa", "html": "🚪", "category": "Objects (household)", "order": "1662"}, + {"emoji": "🕕", "name": "six o’clock", "shortname": ":clock6:", "unicode": "1f555", "html": "🕕", "category": "Travel & Places (time)", "order": "1699"}, + {"emoji": "🚔", "name": "oncoming police car", "shortname": ":oncoming_police_car:", "unicode": "1f694", "html": "🚔", "category": "Travel & Places (transport-ground)", "order": "1621"}, + {"emoji": "📩", "name": "envelope with arrow", "shortname": ":envelope_with_arrow:", "unicode": "1f4e9", "html": "📩", "category": "Objects (mail)", "order": "1904"}, + {"emoji": "🌂", "name": "closed umbrella", "shortname": ":closed_umbrella:", "unicode": "1f302", "html": "🌂", "category": "Travel & Places (sky & weather)", "order": "1744"}, + {"emoji": "🎷", "name": "saxophone", "shortname": ":saxophone:", "unicode": "1f3b7", "html": "🎷", "category": "Objects (musical-instrument)", "order": "1832"}, + {"emoji": "⛪", "name": "church", "shortname": ":church:", "unicode": "26ea", "html": "⛪", "category": "Travel & Places (place-religious)", "order": "1577"}, + {"emoji": "🚴🏿‍♀", "name": "woman biking: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F6B4 1F3FF 200D 2640", "html": "🚴🏿‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚴🏾‍♀", "name": "woman biking: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F6B4 1F3FE 200D 2640", "html": "🚴🏾‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚴🏽‍♀", "name": "woman biking: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F6B4 1F3FD 200D 2640", "html": "🚴🏽‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚴🏼‍♀", "name": "woman biking: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F6B4 1F3FC 200D 2640", "html": "🚴🏼‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚴🏻‍♀", "name": "woman biking: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F6B4 1F3FB 200D 2640", "html": "🚴🏻‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚴‍♀", "name": "woman biking", "shortname": ":woman_biking:", "unicode": "1F6B4 200D 2640", "html": "🚴‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚴🏿", "name": "person biking: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F6B4 1F3FF", "html": "🚴🏿", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚴🏾", "name": "person biking: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F6B4 1F3FE", "html": "🚴🏾", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚴🏽", "name": "person biking: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F6B4 1F3FD", "html": "🚴🏽", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚴🏼", "name": "person biking: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F6B4 1F3FC", "html": "🚴🏼", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚴🏻", "name": "person biking: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F6B4 1F3FB", "html": "🚴🏻", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚴", "name": "person biking", "shortname": ":bicyclist:", "unicode": "1f6b4", "html": "🚴", "category": "People & Body (person-sport)", "order": "890"}, + {"emoji": "♓", "name": "Pisces", "shortname": ":pisces:", "unicode": "2653", "html": "♓", "category": "Symbols (zodiac)", "order": "2046"}, + {"emoji": "🍡", "name": "dango", "shortname": ":dango:", "unicode": "1f361", "html": "🍡", "category": "Food & Drink (food-asian)", "order": "1507"}, + {"emoji": "♑", "name": "Capricorn", "shortname": ":capricorn:", "unicode": "2651", "html": "♑", "category": "Symbols (zodiac)", "order": "2044"}, + {"emoji": "🏢", "name": "office building", "shortname": ":office:", "unicode": "1f3e2", "html": "🏢", "category": "Travel & Places (place-building)", "order": "1561"}, + {"emoji": "🚣🏿‍♀", "name": "woman rowing boat: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F6A3 1F3FF 200D 2640", "html": "🚣🏿‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚣🏾‍♀", "name": "woman rowing boat: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F6A3 1F3FE 200D 2640", "html": "🚣🏾‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚣🏽‍♀", "name": "woman rowing boat: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F6A3 1F3FD 200D 2640", "html": "🚣🏽‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚣🏼‍♀", "name": "woman rowing boat: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F6A3 1F3FC 200D 2640", "html": "🚣🏼‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚣🏻‍♀", "name": "woman rowing boat: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F6A3 1F3FB 200D 2640", "html": "🚣🏻‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚣‍♀", "name": "woman rowing boat", "shortname": ":woman_rowing_boat:", "unicode": "1F6A3 200D 2640", "html": "🚣‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚣🏿", "name": "person rowing boat: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F6A3 1F3FF", "html": "🚣🏿", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚣🏾", "name": "person rowing boat: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F6A3 1F3FE", "html": "🚣🏾", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚣🏽", "name": "person rowing boat: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F6A3 1F3FD", "html": "🚣🏽", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚣🏼", "name": "person rowing boat: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F6A3 1F3FC", "html": "🚣🏼", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚣🏻", "name": "person rowing boat: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F6A3 1F3FB", "html": "🚣🏻", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚣", "name": "person rowing boat", "shortname": ":rowboat:", "unicode": "1f6a3", "html": "🚣", "category": "People & Body (person-sport)", "order": "818"}, + {"emoji": "👒", "name": "woman’s hat", "shortname": ":womans_hat:", "unicode": "1f452", "html": "👒", "category": "Objects (clothing)", "order": "1334"}, + {"emoji": "👞", "name": "man’s shoe", "shortname": ":mans_shoe:", "unicode": "1f45e", "html": "👞", "category": "Objects (clothing)", "order": "1328"}, + {"emoji": "🏩", "name": "love hotel", "shortname": ":love_hotel:", "unicode": "1f3e9", "html": "🏩", "category": "Travel & Places (place-building)", "order": "1567"}, + {"emoji": "🗻", "name": "mount fuji", "shortname": ":mount_fuji:", "unicode": "1f5fb", "html": "🗻", "category": "Travel & Places (place-geographic)", "order": "1547"}, + {"emoji": "🐪", "name": "camel", "shortname": ":dromedary_camel:", "unicode": "1f42a", "html": "🐪", "category": "Animals & Nature (animal-mammal)", "order": "1371"}, + {"emoji": "👜", "name": "handbag", "shortname": ":handbag:", "unicode": "1f45c", "html": "👜", "category": "Objects (clothing)", "order": "1324"}, + {"emoji": "⌛", "name": "hourglass done", "shortname": ":hourglass:", "unicode": "231b", "html": "⌛", "category": "Travel & Places (time)", "order": "1680"}, + {"emoji": "❎", "name": "cross mark button", "shortname": ":negative_squared_cross_mark:", "unicode": "274e", "html": "❎", "category": "Symbols (other-symbol)", "order": "2083"}, + {"emoji": "🎺", "name": "trumpet", "shortname": ":trumpet:", "unicode": "1f3ba", "html": "🎺", "category": "Objects (musical-instrument)", "order": "1835"}, + {"emoji": "🏫", "name": "school", "shortname": ":school:", "unicode": "1f3eb", "html": "🏫", "category": "Travel & Places (place-building)", "order": "1569"}, + {"emoji": "🐄", "name": "cow", "shortname": ":cow2:", "unicode": "1f404", "html": "🐄", "category": "Animals & Nature (animal-mammal)", "order": "1363"}, + {"emoji": "🌆", "name": "cityscape at dusk", "shortname": ":cityscape_at_dusk:", "unicode": "1F306", "html": "🌆", "category": "Travel & Places (place-other)", "order": ""}, + {"emoji": "👷🏿‍♀", "name": "woman construction worker: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F477 1F3FF 200D 2640", "html": "👷🏿‍♀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👷🏾‍♀", "name": "woman construction worker: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F477 1F3FE 200D 2640", "html": "👷🏾‍♀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👷🏽‍♀", "name": "woman construction worker: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F477 1F3FD 200D 2640", "html": "👷🏽‍♀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👷🏼‍♀", "name": "woman construction worker: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F477 1F3FC 200D 2640", "html": "👷🏼‍♀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👷🏻‍♀", "name": "woman construction worker: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F477 1F3FB 200D 2640", "html": "👷🏻‍♀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👷‍♀", "name": "woman construction worker", "shortname": ":woman_construction_worker:", "unicode": "1F477 200D 2640", "html": "👷‍♀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👷🏿", "name": "construction worker: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F477 1F3FF", "html": "👷🏿", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👷🏾", "name": "construction worker: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F477 1F3FE", "html": "👷🏾", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👷🏽", "name": "construction worker: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F477 1F3FD", "html": "👷🏽", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👷🏼", "name": "construction worker: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F477 1F3FC", "html": "👷🏼", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👷🏻", "name": "construction worker: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F477 1F3FB", "html": "👷🏻", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👷", "name": "construction worker", "shortname": ":construction_worker:", "unicode": "1f477", "html": "👷", "category": "People & Body (person-role)", "order": "393"}, + {"emoji": "🚽", "name": "toilet", "shortname": ":toilet:", "unicode": "1f6bd", "html": "🚽", "category": "Objects (household)", "order": "1671"}, + {"emoji": "🐖", "name": "pig", "shortname": ":pig2:", "unicode": "1f416", "html": "🐖", "category": "Animals & Nature (animal-mammal)", "order": "1365"}, + {"emoji": "❔", "name": "white question mark", "shortname": ":grey_question:", "unicode": "2754", "html": "❔", "category": "Symbols (other-symbol)", "order": "2099"}, + {"emoji": "🔰", "name": "Japanese symbol for beginner", "shortname": ":beginner:", "unicode": "1f530", "html": "🔰", "category": "Symbols (other-symbol)", "order": "2075"}, + {"emoji": "🎻", "name": "violin", "shortname": ":violin:", "unicode": "1f3bb", "html": "🎻", "category": "Objects (musical-instrument)", "order": "1836"}, + {"emoji": "🔛", "name": "ON! arrow", "shortname": ":on:", "unicode": "1f51b", "html": "🔛", "category": "Symbols (arrow)", "order": "2020"}, + {"emoji": "💳", "name": "credit card", "shortname": ":credit_card:", "unicode": "1f4b3", "html": "💳", "category": "Objects (money)", "order": "1897"}, + {"emoji": "🆔", "name": "ID button", "shortname": ":id:", "unicode": "1f194", "html": "🆔", "category": "Symbols (alphanum)", "order": "2132"}, + {"emoji": "㊙", "name": "Japanese secret button", "shortname": ":secret:", "unicode": "3299", "html": "㊙", "category": "Symbols (alphanum)", "order": "2156"}, + {"emoji": "🎡", "name": "ferris wheel", "shortname": ":ferris_wheel:", "unicode": "1f3a1", "html": "🎡", "category": "Travel & Places (place-other)", "order": "1594"}, + {"emoji": "🎳", "name": "bowling", "shortname": ":bowling:", "unicode": "1f3b3", "html": "🎳", "category": "Activities (sport)", "order": "1789"}, + {"emoji": "♎", "name": "Libra", "shortname": ":libra:", "unicode": "264e", "html": "♎", "category": "Symbols (zodiac)", "order": "2041"}, + {"emoji": "♍", "name": "Virgo", "shortname": ":virgo:", "unicode": "264d", "html": "♍", "category": "Symbols (zodiac)", "order": "2040"}, + {"emoji": "💈", "name": "barber pole", "shortname": ":barber:", "unicode": "1f488", "html": "💈", "category": "Travel & Places (place-other)", "order": "1596"}, + {"emoji": "👛", "name": "purse", "shortname": ":purse:", "unicode": "1f45b", "html": "👛", "category": "Objects (clothing)", "order": "1323"}, + {"emoji": "🎢", "name": "roller coaster", "shortname": ":roller_coaster:", "unicode": "1f3a2", "html": "🎢", "category": "Travel & Places (place-other)", "order": "1595"}, + {"emoji": "🐀", "name": "rat", "shortname": ":rat:", "unicode": "1f400", "html": "🐀", "category": "Animals & Nature (animal-mammal)", "order": "1377"}, + {"emoji": "📅", "name": "calendar", "shortname": ":date:", "unicode": "1f4c5", "html": "📅", "category": "Objects (office)", "order": "1925"}, + {"emoji": "🏉", "name": "rugby football", "shortname": ":rugby_football:", "unicode": "1f3c9", "html": "🏉", "category": "Activities (sport)", "order": "1786"}, + {"emoji": "🐏", "name": "ram", "shortname": ":ram:", "unicode": "1f40f", "html": "🐏", "category": "Animals & Nature (animal-mammal)", "order": "1368"}, + {"emoji": "🔼", "name": "upwards button", "shortname": ":arrow_up_small:", "unicode": "1f53c", "html": "🔼", "category": "Symbols (av-symbol)", "order": "2058"}, + {"emoji": "🔲", "name": "black square button", "shortname": ":black_square_button:", "unicode": "1f532", "html": "🔲", "category": "Symbols (geometric)", "order": "2175"}, + {"emoji": "📴", "name": "mobile phone off", "shortname": ":mobile_phone_off:", "unicode": "1f4f4", "html": "📴", "category": "Symbols (av-symbol)", "order": "2071"}, + {"emoji": "🗼", "name": "Tokyo tower", "shortname": ":tokyo_tower:", "unicode": "1f5fc", "html": "🗼", "category": "Travel & Places (place-building)", "order": "1575"}, + {"emoji": "㊗", "name": "Japanese congratulations button", "shortname": ":congratulations:", "unicode": "3297", "html": "㊗", "category": "Symbols (alphanum)", "order": "2155"}, + {"emoji": "👘", "name": "kimono", "shortname": ":kimono:", "unicode": "1f458", "html": "👘", "category": "Objects (clothing)", "order": "1320"}, + {"emoji": "🇷🇺", "name": "flag: Russia", "shortname": ":ru:", "unicode": "1F1F7 1F1FA", "html": "🇷", "category": "Flags (country-flag)", "order": ""}, + {"emoji": "🚢", "name": "ship", "shortname": ":ship:", "unicode": "1f6a2", "html": "🚢", "category": "Travel & Places (transport-water)", "order": "1649"}, + {"emoji": "🔎", "name": "magnifying glass tilted right", "shortname": ":mag_right:", "unicode": "1f50e", "html": "🔎", "category": "Objects (light & video)", "order": "1866"}, + {"emoji": "🔍", "name": "magnifying glass tilted left", "shortname": ":mag:", "unicode": "1f50d", "html": "🔍", "category": "Objects (light & video)", "order": "1865"}, + {"emoji": "🚒", "name": "fire engine", "shortname": ":fire_engine:", "unicode": "1f692", "html": "🚒", "category": "Travel & Places (transport-ground)", "order": "1619"}, + {"emoji": "🕦", "name": "eleven-thirty", "shortname": ":clock1130:", "unicode": "1f566", "html": "🕦", "category": "Travel & Places (time)", "order": "1710"}, + {"emoji": "🚓", "name": "police car", "shortname": ":police_car:", "unicode": "1f693", "html": "🚓", "category": "Travel & Places (transport-ground)", "order": "1620"}, + {"emoji": "🃏", "name": "joker", "shortname": ":black_joker:", "unicode": "1f0cf", "html": "🃏", "category": "Activities (game)", "order": "1811"}, + {"emoji": "🌉", "name": "bridge at night", "shortname": ":bridge_at_night:", "unicode": "1f309", "html": "🌉", "category": "Travel & Places (place-other)", "order": "1590"}, + {"emoji": "📦", "name": "package", "shortname": ":package:", "unicode": "1f4e6", "html": "📦", "category": "Objects (mail)", "order": "1907"}, + {"emoji": "🚖", "name": "oncoming taxi", "shortname": ":oncoming_taxi:", "unicode": "1f696", "html": "🚖", "category": "Travel & Places (transport-ground)", "order": "1623"}, + {"emoji": "📆", "name": "tear-off calendar", "shortname": ":calendar:", "unicode": "1f4c6", "html": "📆", "category": "Objects (office)", "order": "1926"}, + {"emoji": "🏇", "name": "horse racing", "shortname": ":horse_racing:", "unicode": "1f3c7", "html": "🏇", "category": "People & Body (person-sport)", "order": "769"}, + {"emoji": "🐅", "name": "tiger", "shortname": ":tiger2:", "unicode": "1f405", "html": "🐅", "category": "Animals & Nature (animal-mammal)", "order": "1354"}, + {"emoji": "👢", "name": "woman’s boot", "shortname": ":boot:", "unicode": "1f462", "html": "👢", "category": "Objects (clothing)", "order": "1332"}, + {"emoji": "🚑", "name": "ambulance", "shortname": ":ambulance:", "unicode": "1f691", "html": "🚑", "category": "Travel & Places (transport-ground)", "order": "1618"}, + {"emoji": "🔳", "name": "white square button", "shortname": ":white_square_button:", "unicode": "1f533", "html": "🔳", "category": "Symbols (geometric)", "order": "2176"}, + {"emoji": "🐗", "name": "boar", "shortname": ":boar:", "unicode": "1f417", "html": "🐗", "category": "Animals & Nature (animal-mammal)", "order": "1366"}, + {"emoji": "🎒", "name": "backpack", "shortname": ":school_satchel:", "unicode": "1f392", "html": "🎒", "category": "Objects (clothing)", "order": "1327"}, + {"emoji": "➿", "name": "double curly loop", "shortname": ":loop:", "unicode": "27bf", "html": "➿", "category": "Symbols (other-symbol)", "order": "2091"}, + {"emoji": "💷", "name": "pound banknote", "shortname": ":pound:", "unicode": "1f4b7", "html": "💷", "category": "Objects (money)", "order": "1895"}, + {"emoji": "ℹ", "name": "information", "shortname": ":information_source:", "unicode": "2139", "html": "ℹ", "category": "Symbols (alphanum)", "order": "2131"}, + {"emoji": "🐂", "name": "ox", "shortname": ":ox:", "unicode": "1f402", "html": "🐂", "category": "Animals & Nature (animal-mammal)", "order": "1361"}, + {"emoji": "🍙", "name": "rice ball", "shortname": ":rice_ball:", "unicode": "1f359", "html": "🍙", "category": "Food & Drink (food-asian)", "order": "1497"}, + {"emoji": "🆚", "name": "VS button", "shortname": ":vs:", "unicode": "1f19a", "html": "🆚", "category": "Symbols (alphanum)", "order": "2141"}, + {"emoji": "🔚", "name": "END arrow", "shortname": ":end:", "unicode": "1f51a", "html": "🔚", "category": "Symbols (arrow)", "order": "2019"}, + {"emoji": "🅿️", "name": "P button", "shortname": ":parking:", "unicode": "1f17f", "html": "🅿", "category": "Symbols (alphanum)", "order": "2138"}, + {"emoji": "👡", "name": "woman’s sandal", "shortname": ":sandal:", "unicode": "1f461", "html": "👡", "category": "Objects (clothing)", "order": "1331"}, + {"emoji": "⛺", "name": "tent", "shortname": ":tent:", "unicode": "26fa", "html": "⛺", "category": "Travel & Places (place-other)", "order": "1583"}, + {"emoji": "💺", "name": "seat", "shortname": ":seat:", "unicode": "1f4ba", "html": "💺", "category": "Travel & Places (transport-air)", "order": "1654"}, + {"emoji": "🚕", "name": "taxi", "shortname": ":taxi:", "unicode": "1f695", "html": "🚕", "category": "Travel & Places (transport-ground)", "order": "1622"}, + {"emoji": "◾", "name": "black medium-small square", "shortname": ":black_medium_small_square:", "unicode": "25fe", "html": "◾", "category": "Symbols (geometric)", "order": "2164"}, + {"emoji": "💼", "name": "briefcase", "shortname": ":briefcase:", "unicode": "1f4bc", "html": "💼", "category": "Objects (office)", "order": "1921"}, + {"emoji": "📰", "name": "newspaper", "shortname": ":newspaper:", "unicode": "1f4f0", "html": "📰", "category": "Objects (book-paper)", "order": "1886"}, + {"emoji": "🎪", "name": "circus tent", "shortname": ":circus_tent:", "unicode": "1f3aa", "html": "🎪", "category": "Travel & Places (place-other)", "order": "1597"}, + {"emoji": "🔯", "name": "dotted six-pointed star", "shortname": ":six_pointed_star:", "unicode": "1f52f", "html": "🔯", "category": "Symbols (religion)", "order": "2034"}, + {"emoji": "🚹", "name": "men’s room", "shortname": ":mens:", "unicode": "1f6b9", "html": "🚹", "category": "Symbols (transport-sign)", "order": "1980"}, + {"emoji": "🏰", "name": "castle", "shortname": ":european_castle:", "unicode": "1f3f0", "html": "🏰", "category": "Travel & Places (place-building)", "order": "1573"}, + {"emoji": "🔦", "name": "flashlight", "shortname": ":flashlight:", "unicode": "1f526", "html": "🔦", "category": "Objects (light & video)", "order": "1872"}, + {"emoji": "🌁", "name": "foggy", "shortname": ":foggy:", "unicode": "1f301", "html": "🌁", "category": "Travel & Places (place-other)", "order": "1584"}, + {"emoji": "⏫", "name": "fast up button", "shortname": ":arrow_double_up:", "unicode": "23eb", "html": "⏫", "category": "Symbols (av-symbol)", "order": "2059"}, + {"emoji": "🎍", "name": "pine decoration", "shortname": ":bamboo:", "unicode": "1f38d", "html": "🎍", "category": "Activities (event)", "order": "1765"}, + {"emoji": "🎫", "name": "ticket", "shortname": ":ticket:", "unicode": "1f3ab", "html": "🎫", "category": "Activities (event)", "order": "1774"}, + {"emoji": "🚁", "name": "helicopter", "shortname": ":helicopter:", "unicode": "1f681", "html": "🚁", "category": "Travel & Places (transport-air)", "order": "1655"}, + {"emoji": "💽", "name": "computer disk", "shortname": ":minidisc:", "unicode": "1f4bd", "html": "💽", "category": "Objects (computer)", "order": "1852"}, + {"emoji": "🚍", "name": "oncoming bus", "shortname": ":oncoming_bus:", "unicode": "1f68d", "html": "🚍", "category": "Travel & Places (transport-ground)", "order": "1615"}, + {"emoji": "🍈", "name": "melon", "shortname": ":melon:", "unicode": "1f348", "html": "🍈", "category": "Food & Drink (food-fruit)", "order": "1450"}, + {"emoji": "▫", "name": "white small square", "shortname": ":white_small_square:", "unicode": "25ab", "html": "▫", "category": "Symbols (geometric)", "order": "2160"}, + {"emoji": "🏤", "name": "post office", "shortname": ":european_post_office:", "unicode": "1f3e4", "html": "🏤", "category": "Travel & Places (place-building)", "order": "1563"}, + {"emoji": "🔟", "name": "keycap: 10", "shortname": ":keycap_ten:", "unicode": "1f51f", "html": "🔟", "category": "Symbols (keycap)", "order": "2118"}, + {"emoji": "📓", "name": "notebook", "shortname": ":notebook:", "unicode": "1f4d3", "html": "📓", "category": "Objects (book-paper)", "order": "1881"}, + {"emoji": "🔕", "name": "bell with slash", "shortname": ":no_bell:", "unicode": "1f515", "html": "🔕", "category": "Objects (sound)", "order": "1822"}, + {"emoji": "🍢", "name": "oden", "shortname": ":oden:", "unicode": "1f362", "html": "🍢", "category": "Food & Drink (food-asian)", "order": "1503"}, + {"emoji": "🎏", "name": "carp streamer", "shortname": ":flags:", "unicode": "1f38f", "html": "🎏", "category": "Activities (event)", "order": "1767"}, + {"emoji": "🎠", "name": "carousel horse", "shortname": ":carousel_horse:", "unicode": "1f3a0", "html": "🎠", "category": "Travel & Places (place-other)", "order": "1593"}, + {"emoji": "🐡", "name": "blowfish", "shortname": ":blowfish:", "unicode": "1f421", "html": "🐡", "category": "Animals & Nature (animal-marine)", "order": "1411"}, + {"emoji": "📈", "name": "chart increasing", "shortname": ":chart_with_upwards_trend:", "unicode": "1f4c8", "html": "📈", "category": "Objects (office)", "order": "1930"}, + {"emoji": "🍠", "name": "roasted sweet potato", "shortname": ":sweet_potato:", "unicode": "1f360", "html": "🍠", "category": "Food & Drink (food-asian)", "order": "1502"}, + {"emoji": "🎿", "name": "skis", "shortname": ":ski:", "unicode": "1f3bf", "html": "🎿", "category": "Activities (sport)", "order": "1803"}, + {"emoji": "🕛", "name": "twelve o’clock", "shortname": ":clock12:", "unicode": "1f55b", "html": "🕛", "category": "Travel & Places (time)", "order": "1687"}, + {"emoji": "📶", "name": "antenna bars", "shortname": ":signal_strength:", "unicode": "1f4f6", "html": "📶", "category": "Symbols (av-symbol)", "order": "2069"}, + {"emoji": "🚧", "name": "construction", "shortname": ":construction:", "unicode": "1f6a7", "html": "🚧", "category": "Travel & Places (transport-ground)", "order": "1640"}, + {"emoji": "#", "name": "", "shortname": "", "unicode": "", "html": "#", "category": "", "order": ""}, + {"emoji": "◼", "name": "black medium square", "shortname": ":black_medium_square:", "unicode": "25fc", "html": "◼", "category": "Symbols (geometric)", "order": "2162"}, + {"emoji": "📡", "name": "satellite antenna", "shortname": ":satellite:", "unicode": "1f4e1", "html": "📡", "category": "Objects (science)", "order": "1869"}, + {"emoji": "💶", "name": "euro banknote", "shortname": ":euro:", "unicode": "1f4b6", "html": "💶", "category": "Objects (money)", "order": "1894"}, + {"emoji": "👚", "name": "woman’s clothes", "shortname": ":womans_clothes:", "unicode": "1f45a", "html": "👚", "category": "Objects (clothing)", "order": "1322"}, + {"emoji": "📒", "name": "ledger", "shortname": ":ledger:", "unicode": "1f4d2", "html": "📒", "category": "Objects (book-paper)", "order": "1882"}, + {"emoji": "🐆", "name": "leopard", "shortname": ":leopard:", "unicode": "1f406", "html": "🐆", "category": "Animals & Nature (animal-mammal)", "order": "1355"}, + {"emoji": "🔅", "name": "dim button", "shortname": ":low_brightness:", "unicode": "1f505", "html": "🔅", "category": "Symbols (av-symbol)", "order": "2067"}, + {"emoji": "🕒", "name": "three o’clock", "shortname": ":clock3:", "unicode": "1f552", "html": "🕒", "category": "Travel & Places (time)", "order": "1693"}, + {"emoji": "🏬", "name": "department store", "shortname": ":department_store:", "unicode": "1f3ec", "html": "🏬", "category": "Travel & Places (place-building)", "order": "1570"}, + {"emoji": "🚚", "name": "delivery truck", "shortname": ":truck:", "unicode": "1f69a", "html": "🚚", "category": "Travel & Places (transport-ground)", "order": "1627"}, + {"emoji": "🍶", "name": "sake", "shortname": ":sake:", "unicode": "1f376", "html": "🍶", "category": "Food & Drink (drink)", "order": "1524"}, + {"emoji": "🚃", "name": "railway car", "shortname": ":railway_car:", "unicode": "1f683", "html": "🚃", "category": "Travel & Places (transport-ground)", "order": "1603"}, + {"emoji": "🚤", "name": "speedboat", "shortname": ":speedboat:", "unicode": "1f6a4", "html": "🚤", "category": "Travel & Places (transport-water)", "order": "1645"}, + {"emoji": "🇰🇷", "name": "flag: South Korea", "shortname": ":ko:", "unicode": "1F1F0 1F1F7", "html": "🇰", "category": "Flags (country-flag)", "order": ""}, + {"emoji": "📼", "name": "videocassette", "shortname": ":vhs:", "unicode": "1f4fc", "html": "📼", "category": "Objects (light & video)", "order": "1864"}, + {"emoji": "🕐", "name": "one o’clock", "shortname": ":clock1:", "unicode": "1f550", "html": "🕐", "category": "Travel & Places (time)", "order": "1689"}, + {"emoji": "⏬", "name": "fast down button", "shortname": ":arrow_double_down:", "unicode": "23ec", "html": "⏬", "category": "Symbols (av-symbol)", "order": "2061"}, + {"emoji": "🐃", "name": "water buffalo", "shortname": ":water_buffalo:", "unicode": "1f403", "html": "🐃", "category": "Animals & Nature (animal-mammal)", "order": "1362"}, + {"emoji": "🔽", "name": "downwards button", "shortname": ":arrow_down_small:", "unicode": "1f53d", "html": "🔽", "category": "Symbols (av-symbol)", "order": "2060"}, + {"emoji": "💴", "name": "yen banknote", "shortname": ":yen:", "unicode": "1f4b4", "html": "💴", "category": "Objects (money)", "order": "1892"}, + {"emoji": "🔇", "name": "muted speaker", "shortname": ":mute:", "unicode": "1f507", "html": "🔇", "category": "Objects (sound)", "order": "1814"}, + {"emoji": "🎽", "name": "running shirt", "shortname": ":running_shirt_with_sash:", "unicode": "1f3bd", "html": "🎽", "category": "Activities (sport)", "order": "1802"}, + {"emoji": "⬜", "name": "white large square", "shortname": ":white_large_square:", "unicode": "2b1c", "html": "⬜", "category": "Symbols (geometric)", "order": "2166"}, + {"emoji": "♿", "name": "wheelchair symbol", "shortname": ":wheelchair:", "unicode": "267f", "html": "♿", "category": "Symbols (transport-sign)", "order": "1979"}, + {"emoji": "🕑", "name": "two o’clock", "shortname": ":clock2:", "unicode": "1f551", "html": "🕑", "category": "Travel & Places (time)", "order": "1691"}, + {"emoji": "📎", "name": "paperclip", "shortname": ":paperclip:", "unicode": "1f4ce", "html": "📎", "category": "Objects (office)", "order": "1936"}, + {"emoji": "🏧", "name": "ATM sign", "shortname": ":atm:", "unicode": "1f3e7", "html": "🏧", "category": "Symbols (transport-sign)", "order": "1976"}, + {"emoji": "🎦", "name": "cinema", "shortname": ":cinema:", "unicode": "1f3a6", "html": "🎦", "category": "Symbols (av-symbol)", "order": "2066"}, + {"emoji": "🔭", "name": "telescope", "shortname": ":telescope:", "unicode": "1f52d", "html": "🔭", "category": "Objects (science)", "order": "1868"}, + {"emoji": "🎑", "name": "moon viewing ceremony", "shortname": ":rice_scene:", "unicode": "1f391", "html": "🎑", "category": "Activities (event)", "order": "1769"}, + {"emoji": "📘", "name": "blue book", "shortname": ":blue_book:", "unicode": "1f4d8", "html": "📘", "category": "Objects (book-paper)", "order": "1878"}, + {"emoji": "◻️", "name": "white medium square", "shortname": ":white_medium_square:", "unicode": "25fb", "html": "◻", "category": "Symbols (geometric)", "order": "2161"}, + {"emoji": "📮", "name": "postbox", "shortname": ":postbox:", "unicode": "1f4ee", "html": "📮", "category": "Objects (mail)", "order": "1912"}, + {"emoji": "📧", "name": "e-mail", "shortname": ":e-mail:", "unicode": "1f4e7", "html": "📧", "category": "Objects (mail)", "order": "1902"}, + {"emoji": "🐁", "name": "mouse", "shortname": ":mouse2:", "unicode": "1f401", "html": "🐁", "category": "Animals & Nature (animal-mammal)", "order": "1376"}, + {"emoji": "🚄", "name": "high-speed train", "shortname": ":bullettrain_side:", "unicode": "1f684", "html": "🚄", "category": "Travel & Places (transport-ground)", "order": "1604"}, + {"emoji": "🉐", "name": "Japanese bargain button", "shortname": ":ideograph_advantage:", "unicode": "1f250", "html": "🉐", "category": "Symbols (alphanum)", "order": "2147"}, + {"emoji": "🔩", "name": "nut and bolt", "shortname": ":nut_and_bolt:", "unicode": "1f529", "html": "🔩", "category": "Objects (tool)", "order": "1960"}, + {"emoji": "🆖", "name": "NG button", "shortname": ":ng:", "unicode": "1f196", "html": "🆖", "category": "Symbols (alphanum)", "order": "2135"}, + {"emoji": "🏨", "name": "hotel", "shortname": ":hotel:", "unicode": "1f3e8", "html": "🏨", "category": "Travel & Places (place-building)", "order": "1566"}, + {"emoji": "🚾", "name": "water closet", "shortname": ":wc:", "unicode": "1f6be", "html": "🚾", "category": "Symbols (transport-sign)", "order": "1984"}, + {"emoji": "🏮", "name": "red paper lantern", "shortname": ":izakaya_lantern:", "unicode": "1f3ee", "html": "🏮", "category": "Objects (light & video)", "order": "1873"}, + {"emoji": "🔂", "name": "repeat single button", "shortname": ":repeat_one:", "unicode": "1f502", "html": "🔂", "category": "Symbols (av-symbol)", "order": "2050"}, + {"emoji": "📬", "name": "open mailbox with raised flag", "shortname": ":mailbox_with_mail:", "unicode": "1f4ec", "html": "📬", "category": "Objects (mail)", "order": "1910"}, + {"emoji": "📉", "name": "chart decreasing", "shortname": ":chart_with_downwards_trend:", "unicode": "1f4c9", "html": "📉", "category": "Objects (office)", "order": "1931"}, + {"emoji": "📗", "name": "green book", "shortname": ":green_book:", "unicode": "1f4d7", "html": "📗", "category": "Objects (book-paper)", "order": "1877"}, + {"emoji": "🚜", "name": "tractor", "shortname": ":tractor:", "unicode": "1f69c", "html": "🚜", "category": "Travel & Places (transport-ground)", "order": "1629"}, + {"emoji": "⛲", "name": "fountain", "shortname": ":fountain:", "unicode": "26f2", "html": "⛲", "category": "Travel & Places (place-other)", "order": "1582"}, + {"emoji": "🚇", "name": "metro", "shortname": ":metro:", "unicode": "1f687", "html": "🚇", "category": "Travel & Places (transport-ground)", "order": "1607"}, + {"emoji": "📋", "name": "clipboard", "shortname": ":clipboard:", "unicode": "1f4cb", "html": "📋", "category": "Objects (office)", "order": "1933"}, + {"emoji": "📵", "name": "no mobile phones", "shortname": ":no_mobile_phones:", "unicode": "1f4f5", "html": "📵", "category": "Symbols (warning)", "order": "1998"}, + {"emoji": "🕓", "name": "four o’clock", "shortname": ":clock4:", "unicode": "1f553", "html": "🕓", "category": "Travel & Places (time)", "order": "1695"}, + {"emoji": "🚭", "name": "no smoking", "shortname": ":no_smoking:", "unicode": "1f6ad", "html": "🚭", "category": "Symbols (warning)", "order": "1994"}, + {"emoji": "⬛", "name": "black large square", "shortname": ":black_large_square:", "unicode": "2b1b", "html": "⬛", "category": "Symbols (geometric)", "order": "2165"}, + {"emoji": "🎰", "name": "slot machine", "shortname": ":slot_machine:", "unicode": "1f3b0", "html": "🎰", "category": "Activities (game)", "order": "1601"}, + {"emoji": "🕔", "name": "five o’clock", "shortname": ":clock5:", "unicode": "1f554", "html": "🕔", "category": "Travel & Places (time)", "order": "1697"}, + {"emoji": "🛁", "name": "bathtub", "shortname": ":bathtub:", "unicode": "1f6c1", "html": "🛁", "category": "Objects (household)", "order": "1679"}, + {"emoji": "📜", "name": "scroll", "shortname": ":scroll:", "unicode": "1f4dc", "html": "📜", "category": "Objects (book-paper)", "order": "1884"}, + {"emoji": "🚉", "name": "station", "shortname": ":station:", "unicode": "1f689", "html": "🚉", "category": "Travel & Places (transport-ground)", "order": "1609"}, + {"emoji": "🍘", "name": "rice cracker", "shortname": ":rice_cracker:", "unicode": "1f358", "html": "🍘", "category": "Food & Drink (food-asian)", "order": "1496"}, + {"emoji": "🏦", "name": "bank", "shortname": ":bank:", "unicode": "1f3e6", "html": "🏦", "category": "Travel & Places (place-building)", "order": "1565"}, + {"emoji": "🔧", "name": "wrench", "shortname": ":wrench:", "unicode": "1f527", "html": "🔧", "category": "Objects (tool)", "order": "1959"}, + {"emoji": "🈯️", "name": "", "shortname": ":u6307:", "unicode": "1f22f", "html": "🈯", "category": "", "order": "2146"}, + {"emoji": "🚛", "name": "articulated lorry", "shortname": ":articulated_lorry:", "unicode": "1f69b", "html": "🚛", "category": "Travel & Places (transport-ground)", "order": "1628"}, + {"emoji": "📄", "name": "page facing up", "shortname": ":page_facing_up:", "unicode": "1f4c4", "html": "📄", "category": "Objects (book-paper)", "order": "1885"}, + {"emoji": "⛎", "name": "Ophiuchus", "shortname": ":ophiuchus:", "unicode": "26ce", "html": "⛎", "category": "Symbols (zodiac)", "order": "2047"}, + {"emoji": "📊", "name": "bar chart", "shortname": ":bar_chart:", "unicode": "1f4ca", "html": "📊", "category": "Objects (office)", "order": "1932"}, + {"emoji": "🚷", "name": "no pedestrians", "shortname": ":no_pedestrians:", "unicode": "1f6b7", "html": "🚷", "category": "Symbols (warning)", "order": "1997"}, + {"emoji": "🇨🇳", "name": "flag: China", "shortname": ":cn:", "unicode": "1F1E8 1F1F3", "html": "🇨", "category": "Flags (country-flag)", "order": ""}, + {"emoji": "📳", "name": "vibration mode", "shortname": ":vibration_mode:", "unicode": "1f4f3", "html": "📳", "category": "Symbols (av-symbol)", "order": "2070"}, + {"emoji": "🕙", "name": "ten o’clock", "shortname": ":clock10:", "unicode": "1f559", "html": "🕙", "category": "Travel & Places (time)", "order": "1707"}, + {"emoji": "🕘", "name": "nine o’clock", "shortname": ":clock9:", "unicode": "1f558", "html": "🕘", "category": "Travel & Places (time)", "order": "1705"}, + {"emoji": "🚅", "name": "bullet train", "shortname": ":bullettrain_front:", "unicode": "1f685", "html": "🚅", "category": "Travel & Places (transport-ground)", "order": "1605"}, + {"emoji": "🚐", "name": "minibus", "shortname": ":minibus:", "unicode": "1f690", "html": "🚐", "category": "Travel & Places (transport-ground)", "order": "1617"}, + {"emoji": "🚊", "name": "tram", "shortname": ":tram:", "unicode": "1f68a", "html": "🚊", "category": "Travel & Places (transport-ground)", "order": "1610"}, + {"emoji": "🕗", "name": "eight o’clock", "shortname": ":clock8:", "unicode": "1f557", "html": "🕗", "category": "Travel & Places (time)", "order": "1703"}, + {"emoji": "🈳", "name": "Japanese vacancy button", "shortname": ":u7a7a:", "unicode": "1f233", "html": "🈳", "category": "Symbols (alphanum)", "order": "2154"}, + {"emoji": "🚥", "name": "horizontal traffic light", "shortname": ":traffic_light:", "unicode": "1f6a5", "html": "🚥", "category": "Travel & Places (transport-ground)", "order": "1638"}, + {"emoji": "🚵🏿‍♀", "name": "woman mountain biking: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F6B5 1F3FF 200D 2640", "html": "🚵🏿‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚵🏾‍♀", "name": "woman mountain biking: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F6B5 1F3FE 200D 2640", "html": "🚵🏾‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚵🏽‍♀", "name": "woman mountain biking: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F6B5 1F3FD 200D 2640", "html": "🚵🏽‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚵🏼‍♀", "name": "woman mountain biking: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F6B5 1F3FC 200D 2640", "html": "🚵🏼‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚵🏻‍♀", "name": "woman mountain biking: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F6B5 1F3FB 200D 2640", "html": "🚵🏻‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚵‍♀", "name": "woman mountain biking", "shortname": ":woman_mountain_biking:", "unicode": "1F6B5 200D 2640", "html": "🚵‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚵🏿", "name": "person mountain biking: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F6B5 1F3FF", "html": "🚵🏿", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚵🏾", "name": "person mountain biking: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F6B5 1F3FE", "html": "🚵🏾", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚵🏽", "name": "person mountain biking: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F6B5 1F3FD", "html": "🚵🏽", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚵🏼", "name": "person mountain biking: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F6B5 1F3FC", "html": "🚵🏼", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚵🏻", "name": "person mountain biking: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F6B5 1F3FB", "html": "🚵🏻", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚵", "name": "person mountain biking", "shortname": ":mountain_bicyclist:", "unicode": "1f6b5", "html": "🚵", "category": "People & Body (person-sport)", "order": "908"}, + {"emoji": "🔬", "name": "microscope", "shortname": ":microscope:", "unicode": "1f52c", "html": "🔬", "category": "Objects (science)", "order": "1867"}, + {"emoji": "🏯", "name": "Japanese castle", "shortname": ":japanese_castle:", "unicode": "1f3ef", "html": "🏯", "category": "Travel & Places (place-building)", "order": "1572"}, + {"emoji": "🔖", "name": "bookmark", "shortname": ":bookmark:", "unicode": "1f516", "html": "🔖", "category": "Objects (book-paper)", "order": "1889"}, + {"emoji": "📑", "name": "bookmark tabs", "shortname": ":bookmark_tabs:", "unicode": "1f4d1", "html": "📑", "category": "Objects (book-paper)", "order": "1888"}, + {"emoji": "👝", "name": "clutch bag", "shortname": ":pouch:", "unicode": "1f45d", "html": "👝", "category": "Objects (clothing)", "order": "1325"}, + {"emoji": "🆎", "name": "AB button (blood type)", "shortname": ":ab:", "unicode": "1f18e", "html": "🆎", "category": "Symbols (alphanum)", "order": "2126"}, + {"emoji": "📃", "name": "page with curl", "shortname": ":page_with_curl:", "unicode": "1f4c3", "html": "📃", "category": "Objects (book-paper)", "order": "1883"}, + {"emoji": "🎴", "name": "flower playing cards", "shortname": ":flower_playing_cards:", "unicode": "1f3b4", "html": "🎴", "category": "Activities (game)", "order": "1813"}, + {"emoji": "🕚", "name": "eleven o’clock", "shortname": ":clock11:", "unicode": "1f55a", "html": "🕚", "category": "Travel & Places (time)", "order": "1709"}, + {"emoji": "📠", "name": "fax machine", "shortname": ":fax:", "unicode": "1f4e0", "html": "📠", "category": "Objects (phone)", "order": "1843"}, + {"emoji": "🕖", "name": "seven o’clock", "shortname": ":clock7:", "unicode": "1f556", "html": "🕖", "category": "Travel & Places (time)", "order": "1701"}, + {"emoji": "◽", "name": "white medium-small square", "shortname": ":white_medium_small_square:", "unicode": "25fd", "html": "◽", "category": "Symbols (geometric)", "order": "2163"}, + {"emoji": "💱", "name": "currency exchange", "shortname": ":currency_exchange:", "unicode": "1f4b1", "html": "💱", "category": "Objects (money)", "order": "1899"}, + {"emoji": "🔉", "name": "speaker medium volume", "shortname": ":sound:", "unicode": "1f509", "html": "🔉", "category": "Objects (sound)", "order": "1816"}, + {"emoji": "💹", "name": "chart increasing with yen", "shortname": ":chart:", "unicode": "1f4b9", "html": "💹", "category": "Objects (money)", "order": "1898"}, + {"emoji": "🆑", "name": "CL button", "shortname": ":cl:", "unicode": "1f191", "html": "🆑", "category": "Symbols (alphanum)", "order": "2128"}, + {"emoji": "💾", "name": "floppy disk", "shortname": ":floppy_disk:", "unicode": "1f4be", "html": "💾", "category": "Objects (computer)", "order": "1853"}, + {"emoji": "🏣", "name": "Japanese post office", "shortname": ":post_office:", "unicode": "1f3e3", "html": "🏣", "category": "Travel & Places (place-building)", "order": "1562"}, + {"emoji": "🔈", "name": "speaker low volume", "shortname": ":speaker:", "unicode": "1f508", "html": "🔈", "category": "Objects (sound)", "order": "1815"}, + {"emoji": "🗾", "name": "map of Japan", "shortname": ":japan:", "unicode": "1f5fe", "html": "🗾", "category": "Travel & Places (place-map)", "order": "1543"}, + {"emoji": "🈺", "name": "Japanese open for business button", "shortname": ":u55b6:", "unicode": "1f23a", "html": "🈺", "category": "Symbols (alphanum)", "order": "2157"}, + {"emoji": "🀄", "name": "mahjong red dragon", "shortname": ":mahjong:", "unicode": "1f004", "html": "🀄", "category": "Activities (game)", "order": "1812"}, + {"emoji": "📨", "name": "incoming envelope", "shortname": ":incoming_envelope:", "unicode": "1f4e8", "html": "📨", "category": "Objects (mail)", "order": "1903"}, + {"emoji": "📙", "name": "orange book", "shortname": ":orange_book:", "unicode": "1f4d9", "html": "📙", "category": "Objects (book-paper)", "order": "1879"}, + {"emoji": "🚻", "name": "restroom", "shortname": ":restroom:", "unicode": "1f6bb", "html": "🚻", "category": "Symbols (transport-sign)", "order": "1982"}, + {"emoji": "🈚️", "name": "", "shortname": ":u7121:", "unicode": "1f21a", "html": "🈚", "category": "", "order": "2149"}, + {"emoji": "🈶", "name": "Japanese not free of charge button", "shortname": ":u6709:", "unicode": "1f236", "html": "🈶", "category": "Symbols (alphanum)", "order": "2145"}, + {"emoji": "📐", "name": "triangular ruler", "shortname": ":triangular_ruler:", "unicode": "1f4d0", "html": "📐", "category": "Objects (office)", "order": "1939"}, + {"emoji": "🚋", "name": "tram car", "shortname": ":train:", "unicode": "1f68b", "html": "🚋", "category": "Travel & Places (transport-ground)", "order": "1613"}, + {"emoji": "🈸", "name": "Japanese application button", "shortname": ":u7533:", "unicode": "1f238", "html": "🈸", "category": "Symbols (alphanum)", "order": "2152"}, + {"emoji": "🚎", "name": "trolleybus", "shortname": ":trolleybus:", "unicode": "1f68e", "html": "🚎", "category": "Travel & Places (transport-ground)", "order": "1616"}, + {"emoji": "🈷", "name": "Japanese monthly amount button", "shortname": ":u6708:", "unicode": "1f237", "html": "🈷", "category": "Symbols (alphanum)", "order": "2144"}, + {"emoji": "🔢", "name": "input numbers", "shortname": ":input_numbers:", "unicode": "1F522", "html": "🔢", "category": "Symbols (alphanum)", "order": ""}, + {"emoji": "📔", "name": "notebook with decorative cover", "shortname": ":notebook_with_decorative_cover:", "unicode": "1f4d4", "html": "📔", "category": "Objects (book-paper)", "order": "1874"}, + {"emoji": "🈲", "name": "Japanese prohibited button", "shortname": ":u7981:", "unicode": "1f232", "html": "🈲", "category": "Symbols (alphanum)", "order": "2150"}, + {"emoji": "🈵", "name": "Japanese no vacancy button", "shortname": ":u6e80:", "unicode": "1f235", "html": "🈵", "category": "Symbols (alphanum)", "order": "2158"}, + {"emoji": "📯", "name": "postal horn", "shortname": ":postal_horn:", "unicode": "1f4ef", "html": "📯", "category": "Objects (sound)", "order": "1820"}, + {"emoji": "🏭", "name": "factory", "shortname": ":factory:", "unicode": "1f3ed", "html": "🏭", "category": "Travel & Places (place-building)", "order": "1571"}, + {"emoji": "🚸", "name": "children crossing", "shortname": ":children_crossing:", "unicode": "1f6b8", "html": "🚸", "category": "Symbols (warning)", "order": "1990"}, + {"emoji": "🚆", "name": "train", "shortname": ":train2:", "unicode": "1f686", "html": "🚆", "category": "Travel & Places (transport-ground)", "order": "1606"}, + {"emoji": "📏", "name": "straight ruler", "shortname": ":straight_ruler:", "unicode": "1f4cf", "html": "📏", "category": "Objects (office)", "order": "1938"}, + {"emoji": "📟", "name": "pager", "shortname": ":pager:", "unicode": "1f4df", "html": "📟", "category": "Objects (phone)", "order": "1842"}, + {"emoji": "🉑", "name": "Japanese acceptable button", "shortname": ":accept:", "unicode": "1f251", "html": "🉑", "category": "Symbols (alphanum)", "order": "2151"}, + {"emoji": "🈴", "name": "Japanese passing grade button", "shortname": ":u5408:", "unicode": "1f234", "html": "🈴", "category": "Symbols (alphanum)", "order": "2153"}, + {"emoji": "🔏", "name": "locked with pen", "shortname": ":lock_with_ink_pen:", "unicode": "1f50f", "html": "🔏", "category": "Objects (lock)", "order": "1946"}, + {"emoji": "🕜", "name": "one-thirty", "shortname": ":clock130:", "unicode": "1f55c", "html": "🕜", "category": "Travel & Places (time)", "order": "1690"}, + {"emoji": "🈂️", "name": "Japanese service charge button", "shortname": ":sa:", "unicode": "1f202", "html": "🈂", "category": "Symbols (alphanum)", "order": "2143"}, + {"emoji": "📤", "name": "outbox tray", "shortname": ":outbox_tray:", "unicode": "1f4e4", "html": "📤", "category": "Objects (mail)", "order": "1905"}, + {"emoji": "🔀", "name": "shuffle tracks button", "shortname": ":twisted_rightwards_arrows:", "unicode": "1f500", "html": "🔀", "category": "Symbols (av-symbol)", "order": "2048"}, + {"emoji": "📫", "name": "closed mailbox with raised flag", "shortname": ":mailbox:", "unicode": "1f4eb", "html": "📫", "category": "Objects (mail)", "order": "1908"}, + {"emoji": "🚈", "name": "light rail", "shortname": ":light_rail:", "unicode": "1f688", "html": "🚈", "category": "Travel & Places (transport-ground)", "order": "1608"}, + {"emoji": "🕤", "name": "nine-thirty", "shortname": ":clock930:", "unicode": "1f564", "html": "🕤", "category": "Travel & Places (time)", "order": "1706"}, + {"emoji": "🚏", "name": "bus stop", "shortname": ":busstop:", "unicode": "1f68f", "html": "🚏", "category": "Travel & Places (transport-ground)", "order": "1633"}, + {"emoji": "📂", "name": "open file folder", "shortname": ":open_file_folder:", "unicode": "1f4c2", "html": "📂", "category": "Objects (office)", "order": "1923"}, + {"emoji": "📁", "name": "file folder", "shortname": ":file_folder:", "unicode": "1f4c1", "html": "📁", "category": "Objects (office)", "order": "1922"}, + {"emoji": "🚰", "name": "potable water", "shortname": ":potable_water:", "unicode": "1f6b0", "html": "🚰", "category": "Symbols (transport-sign)", "order": "1978"}, + {"emoji": "📇", "name": "card index", "shortname": ":card_index:", "unicode": "1f4c7", "html": "📇", "category": "Objects (office)", "order": "1929"}, + {"emoji": "🕝", "name": "two-thirty", "shortname": ":clock230:", "unicode": "1f55d", "html": "🕝", "category": "Travel & Places (time)", "order": "1692"}, + {"emoji": "🚝", "name": "monorail", "shortname": ":monorail:", "unicode": "1f69d", "html": "🚝", "category": "Travel & Places (transport-ground)", "order": "1611"}, + {"emoji": "🕧", "name": "twelve-thirty", "shortname": ":clock1230:", "unicode": "1f567", "html": "🕧", "category": "Travel & Places (time)", "order": "1688"}, + {"emoji": "🕥", "name": "ten-thirty", "shortname": ":clock1030:", "unicode": "1f565", "html": "🕥", "category": "Travel & Places (time)", "order": "1708"}, + {"emoji": "🔤", "name": "input latin letters", "shortname": ":abc:", "unicode": "1f524", "html": "🔤", "category": "Symbols (alphanum)", "order": "2124"}, + {"emoji": "📪", "name": "closed mailbox with lowered flag", "shortname": ":mailbox_closed:", "unicode": "1f4ea", "html": "📪", "category": "Objects (mail)", "order": "1909"}, + {"emoji": "🕟", "name": "four-thirty", "shortname": ":clock430:", "unicode": "1f55f", "html": "🕟", "category": "Travel & Places (time)", "order": "1696"}, + {"emoji": "🚞", "name": "mountain railway", "shortname": ":mountain_railway:", "unicode": "1f69e", "html": "🚞", "category": "Travel & Places (transport-ground)", "order": "1612"}, + {"emoji": "🚯", "name": "no littering", "shortname": ":do_not_litter:", "unicode": "1f6af", "html": "🚯", "category": "Symbols (warning)", "order": "1995"}, + {"emoji": "🕞", "name": "three-thirty", "shortname": ":clock330:", "unicode": "1f55e", "html": "🕞", "category": "Travel & Places (time)", "order": "1694"}, + {"emoji": "➗", "name": "division sign", "shortname": ":heavy_division_sign:", "unicode": "2797", "html": "➗", "category": "Symbols (other-symbol)", "order": "2089"}, + {"emoji": "🕢", "name": "seven-thirty", "shortname": ":clock730:", "unicode": "1f562", "html": "🕢", "category": "Travel & Places (time)", "order": "1702"}, + {"emoji": "🕠", "name": "five-thirty", "shortname": ":clock530:", "unicode": "1f560", "html": "🕠", "category": "Travel & Places (time)", "order": "1698"}, + {"emoji": "🔠", "name": "input latin uppercase", "shortname": ":capital_abcd:", "unicode": "1f520", "html": "🔠", "category": "Symbols (alphanum)", "order": "2120"}, + {"emoji": "📭", "name": "open mailbox with lowered flag", "shortname": ":mailbox_with_no_mail:", "unicode": "1f4ed", "html": "📭", "category": "Objects (mail)", "order": "1911"}, + {"emoji": "🔣", "name": "input symbols", "shortname": ":symbols:", "unicode": "1f523", "html": "🔣", "category": "Symbols (alphanum)", "order": "2123"}, + {"emoji": "🚡", "name": "aerial tramway", "shortname": ":aerial_tramway:", "unicode": "1f6a1", "html": "🚡", "category": "Travel & Places (transport-air)", "order": "1658"}, + {"emoji": "🕣", "name": "eight-thirty", "shortname": ":clock830:", "unicode": "1f563", "html": "🕣", "category": "Travel & Places (time)", "order": "1704"}, + {"emoji": "🕡", "name": "six-thirty", "shortname": ":clock630:", "unicode": "1f561", "html": "🕡", "category": "Travel & Places (time)", "order": "1700"}, + {"emoji": "🔡", "name": "input latin lowercase", "shortname": ":abcd:", "unicode": "1f521", "html": "🔡", "category": "Symbols (alphanum)", "order": "2121"}, + {"emoji": "🚠", "name": "mountain cableway", "shortname": ":mountain_cableway:", "unicode": "1f6a0", "html": "🚠", "category": "Travel & Places (transport-air)", "order": "1657"}, + {"emoji": "🈁", "name": "Japanese here button", "shortname": ":koko:", "unicode": "1f201", "html": "🈁", "category": "Symbols (alphanum)", "order": "2142"}, + {"emoji": "🛂", "name": "passport control", "shortname": ":passport_control:", "unicode": "1f6c2", "html": "🛂", "category": "Symbols (transport-sign)", "order": "1985"}, + {"emoji": "🚱", "name": "non-potable water", "shortname": ":non-potable_water:", "unicode": "1f6b1", "html": "🚱", "category": "Symbols (warning)", "order": "1996"}, + {"emoji": "🚟", "name": "suspension railway", "shortname": ":suspension_railway:", "unicode": "1f69f", "html": "🚟", "category": "Travel & Places (transport-air)", "order": "1656"}, + {"emoji": "🛄", "name": "baggage claim", "shortname": ":baggage_claim:", "unicode": "1f6c4", "html": "🛄", "category": "Symbols (transport-sign)", "order": "1987"}, + {"emoji": "🚳", "name": "no bicycles", "shortname": ":no_bicycles:", "unicode": "1f6b3", "html": "🚳", "category": "Symbols (warning)", "order": "1993"}, + {"emoji": "🏳‍🌈", "name": "rainbow flag", "shortname": ":rainbow_flag:", "unicode": "1F3F3 200D 1F308", "html": "🏳‍🌈", "category": "Flags (flag)", "order": ""}, + {"emoji": "🕵🏿‍♀", "name": "woman detective: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F575 1F3FF 200D 2640", "html": "🕵🏿‍♀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🕵🏾‍♀", "name": "woman detective: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F575 1F3FE 200D 2640", "html": "🕵🏾‍♀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🕵🏽‍♀", "name": "woman detective: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F575 1F3FD 200D 2640", "html": "🕵🏽‍♀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🕵🏼‍♀", "name": "woman detective: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F575 1F3FC 200D 2640", "html": "🕵🏼‍♀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🕵🏻‍♀", "name": "woman detective: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F575 1F3FB 200D 2640", "html": "🕵🏻‍♀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🕵‍♀", "name": "woman detective", "shortname": ":woman_detective:", "unicode": "1F575 200D 2640", "html": "🕵‍♀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🕵🏿", "name": "detective: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F575 1F3FF", "html": "🕵🏿", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🕵🏾", "name": "detective: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F575 1F3FE", "html": "🕵🏾", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🕵🏽", "name": "detective: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F575 1F3FD", "html": "🕵🏽", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🕵🏼", "name": "detective: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F575 1F3FC", "html": "🕵🏼", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🕵🏻", "name": "detective: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F575 1F3FB", "html": "🕵🏻", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🕵", "name": "detective", "shortname": ":detective:", "unicode": "1F575", "html": "🕵", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "☹", "name": "frowning face", "shortname": ":frowning_face:", "unicode": "2639", "html": "☹", "category": "Smileys & Emotion (face-concerned)", "order": ""}, + {"emoji": "☠", "name": "skull and crossbones", "shortname": ":skull_crossbones:", "unicode": "2620", "html": "☠", "category": "Smileys & Emotion (face-negative)", "order": "81"}, + {"emoji": "🤗", "name": "hugging face", "shortname": ":hugging:", "unicode": "1f917", "html": "🤗", "category": "Smileys & Emotion (face-hand)", "order": "20"}, + {"emoji": "🤖", "name": "robot", "shortname": ":robot:", "unicode": "1F916", "html": "🤖", "category": "Smileys & Emotion (face-costume)", "order": ""}, + {"emoji": "🤕", "name": "face with head-bandage", "shortname": ":face_with_headbandage:", "unicode": "1F915", "html": "🤕", "category": "Smileys & Emotion (face-unwell)", "order": ""}, + {"emoji": "🤔", "name": "thinking face", "shortname": ":thinking:", "unicode": "1f914", "html": "🤔", "category": "Smileys & Emotion (face-hand)", "order": "21"}, + {"emoji": "🤓", "name": "nerd face", "shortname": ":nerd:", "unicode": "1f913", "html": "🤓", "category": "Smileys & Emotion (face-glasses)", "order": "36"}, + {"emoji": "🤒", "name": "face with thermometer", "shortname": ":face_with_thermometer:", "unicode": "1F912", "html": "🤒", "category": "Smileys & Emotion (face-unwell)", "order": ""}, + {"emoji": "🤑", "name": "money-mouth face", "shortname": ":moneymouth_face:", "unicode": "1F911", "html": "🤑", "category": "Smileys & Emotion (face-tongue)", "order": ""}, + {"emoji": "🤐", "name": "zipper-mouth face", "shortname": ":zipper_mouth:", "unicode": "1f910", "html": "🤐", "category": "Smileys & Emotion (face-neutral-skeptical)", "order": "30"}, + {"emoji": "🙄", "name": "face with rolling eyes", "shortname": ":rolling_eyes:", "unicode": "1f644", "html": "🙄", "category": "Smileys & Emotion (face-neutral-skeptical)", "order": "25"}, + {"emoji": "🙃", "name": "upside-down face", "shortname": ":upside_down:", "unicode": "1f643", "html": "🙃", "category": "Smileys & Emotion (face-smiling)", "order": "45"}, + {"emoji": "🙂", "name": "slightly smiling face", "shortname": ":slight_smile:", "unicode": "1f642", "html": "🙂", "category": "Smileys & Emotion (face-smiling)", "order": "19"}, + {"emoji": "🙁", "name": "slightly frowning face", "shortname": ":slightly_frowning_face:", "unicode": "1F641", "html": "🙁", "category": "Smileys & Emotion (face-concerned)", "order": ""}, + {"emoji": "🤘🏿", "name": "sign of the horns: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F918 1F3FF", "html": "🤘🏿", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "🤘🏾", "name": "sign of the horns: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F918 1F3FE", "html": "🤘🏾", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "🤘🏽", "name": "sign of the horns: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F918 1F3FD", "html": "🤘🏽", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "🤘🏼", "name": "sign of the horns: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F918 1F3FC", "html": "🤘🏼", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "🤘🏻", "name": "sign of the horns: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F918 1F3FB", "html": "🤘🏻", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "🤘", "name": "sign of the horns", "shortname": ":sign_of_the_horns:", "unicode": "1F918", "html": "🤘", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "🖖🏿", "name": "vulcan salute: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F596 1F3FF", "html": "🖖🏿", "category": "People & Body (hand-fingers-open)", "order": ""}, + {"emoji": "🖖🏾", "name": "vulcan salute: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F596 1F3FE", "html": "🖖🏾", "category": "People & Body (hand-fingers-open)", "order": ""}, + {"emoji": "🖖🏽", "name": "vulcan salute: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F596 1F3FD", "html": "🖖🏽", "category": "People & Body (hand-fingers-open)", "order": ""}, + {"emoji": "🖖🏼", "name": "vulcan salute: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F596 1F3FC", "html": "🖖🏼", "category": "People & Body (hand-fingers-open)", "order": ""}, + {"emoji": "🖖🏻", "name": "vulcan salute: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F596 1F3FB", "html": "🖖🏻", "category": "People & Body (hand-fingers-open)", "order": ""}, + {"emoji": "🖖", "name": "vulcan salute", "shortname": ":vulcan_salute:", "unicode": "1F596", "html": "🖖", "category": "People & Body (hand-fingers-open)", "order": ""}, + {"emoji": "🖕🏿", "name": "middle finger: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F595 1F3FF", "html": "🖕🏿", "category": "People & Body (hand-single-finger)", "order": ""}, + {"emoji": "🖕🏾", "name": "middle finger: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F595 1F3FE", "html": "🖕🏾", "category": "People & Body (hand-single-finger)", "order": ""}, + {"emoji": "🖕🏽", "name": "middle finger: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F595 1F3FD", "html": "🖕🏽", "category": "People & Body (hand-single-finger)", "order": ""}, + {"emoji": "🖕🏼", "name": "middle finger: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F595 1F3FC", "html": "🖕🏼", "category": "People & Body (hand-single-finger)", "order": ""}, + {"emoji": "🖕🏻", "name": "middle finger: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F595 1F3FB", "html": "🖕🏻", "category": "People & Body (hand-single-finger)", "order": ""}, + {"emoji": "🖕", "name": "middle finger", "shortname": ":middle_finger:", "unicode": "1f595", "html": "🖕", "category": "People & Body (hand-single-finger)", "order": "1116"}, + {"emoji": "🖐🏿", "name": "hand with fingers splayed: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F590 1F3FF", "html": "🖐🏿", "category": "People & Body (hand-fingers-open)", "order": ""}, + {"emoji": "🖐🏾", "name": "hand with fingers splayed: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F590 1F3FE", "html": "🖐🏾", "category": "People & Body (hand-fingers-open)", "order": ""}, + {"emoji": "🖐🏽", "name": "hand with fingers splayed: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F590 1F3FD", "html": "🖐🏽", "category": "People & Body (hand-fingers-open)", "order": ""}, + {"emoji": "🖐🏼", "name": "hand with fingers splayed: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F590 1F3FC", "html": "🖐🏼", "category": "People & Body (hand-fingers-open)", "order": ""}, + {"emoji": "🖐🏻", "name": "hand with fingers splayed: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F590 1F3FB", "html": "🖐🏻", "category": "People & Body (hand-fingers-open)", "order": ""}, + {"emoji": "🖐", "name": "hand with fingers splayed", "shortname": ":hand_with_fingers_splayed:", "unicode": "1F590", "html": "🖐", "category": "People & Body (hand-fingers-open)", "order": ""}, + {"emoji": "✍🏿", "name": "writing hand: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "270D 1F3FF", "html": "✍🏿", "category": "People & Body (hand-prop)", "order": ""}, + {"emoji": "✍🏾", "name": "writing hand: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "270D 1F3FE", "html": "✍🏾", "category": "People & Body (hand-prop)", "order": ""}, + {"emoji": "✍🏽", "name": "writing hand: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "270D 1F3FD", "html": "✍🏽", "category": "People & Body (hand-prop)", "order": ""}, + {"emoji": "✍🏼", "name": "writing hand: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "270D 1F3FC", "html": "✍🏼", "category": "People & Body (hand-prop)", "order": ""}, + {"emoji": "✍🏻", "name": "writing hand: light skin tone", "shortname": ":light_skin_tone:", "unicode": "270D 1F3FB", "html": "✍🏻", "category": "People & Body (hand-prop)", "order": ""}, + {"emoji": "✍", "name": "writing hand", "shortname": ":writing_hand:", "unicode": "270d", "html": "✍", "category": "People & Body (hand-prop)", "order": "1230"}, + {"emoji": "🕶", "name": "sunglasses", "shortname": ":dark_sunglasses:", "unicode": "1f576", "html": "🕶", "category": "Objects (clothing)", "order": "1315"}, + {"emoji": "👁‍🗨", "name": "eye in speech bubble", "shortname": ":eye_speachbubble:", "unicode": "1F441 200D 1F5E8", "html": "👁‍🗨", "category": "Smileys & Emotion (emotion)", "order": ""}, + {"emoji": "👁", "name": "eye", "shortname": ":eye:", "unicode": "1f441", "html": "👁", "category": "People & Body (body-parts)", "order": "1280"}, + {"emoji": "🏋🏿‍♀", "name": "woman lifting weights: dark skin tone", "shortname": ":weightlifter_woman_dt:", "unicode": "1F3CB 1F3FF 200D 2640", "html": "🏋🏿‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏋🏾‍♀", "name": "woman lifting weights: medium-dark skin tone", "shortname": ":weightlifter_woman_mdt:", "unicode": "1F3CB 1F3FE 200D 2640", "html": "🏋🏾‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏋🏽‍♀", "name": "woman lifting weights: medium skin tone", "shortname": ":weightlifter_woman_mt:", "unicode": "1F3CB 1F3FD 200D 2640", "html": "🏋🏽‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏋🏼‍♀", "name": "woman lifting weights: medium-light skin tone", "shortname": ":weightlifter_woman_mlt:", "unicode": "1F3CB 1F3FC 200D 2640", "html": "🏋🏼‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏋🏻‍♀", "name": "woman lifting weights: light skin tone", "shortname": ":weightlifter_woman_lt:", "unicode": "1F3CB 1F3FB 200D 2640", "html": "🏋🏻‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏋‍♀", "name": "woman lifting weights", "shortname": ":weightlifter_woman:", "unicode": "1F3CB 200D 2640", "html": "🏋‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏋🏿", "name": "person lifting weights: dark skin tone", "shortname": ":weightlifter_dt:", "unicode": "1F3CB 1F3FF", "html": "🏋🏿", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏋🏾", "name": "person lifting weights: medium-dark skin tone", "shortname": ":weightlifter_mdt:", "unicode": "1F3CB 1F3FE", "html": "🏋🏾", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏋🏽", "name": "person lifting weights: medium skin tone", "shortname": ":weightlifter_mt:", "unicode": "1F3CB 1F3FD", "html": "🏋🏽", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏋🏼", "name": "person lifting weights: medium-light skin tone", "shortname": ":weightlifter_mlt:", "unicode": "1F3CB 1F3FC", "html": "🏋🏼", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏋🏻", "name": "person lifting weights: light skin tone", "shortname": ":weightlifter_lt:", "unicode": "1F3CB 1F3FB", "html": "🏋🏻", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏋", "name": "person lifting weights", "shortname": ":weightlifter:", "unicode": "1F3CB", "html": "🏋", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "⛹🏿‍♀", "name": "woman bouncing ball: dark skin tone", "shortname": ":basketballer_woman_dt:", "unicode": ":basketballer:", "html": "⛹🏿‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "⛹🏾‍♀", "name": "woman bouncing ball: medium-dark skin tone", "shortname": ":basketballer_woman_mdt:", "unicode": ":basketballer:", "html": "⛹🏾‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "⛹🏽‍♀", "name": "woman bouncing ball: medium skin tone", "shortname": ":basketballer_woman_mt:", "unicode": ":basketballer:", "html": "⛹🏽‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "⛹🏼‍♀", "name": "woman bouncing ball: medium-light skin tone", "shortname": ":basketballer_woman_mlt:", "unicode": ":basketballer:", "html": "⛹🏼‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "⛹🏻‍♀", "name": "woman bouncing ball: light skin tone", "shortname": ":basketballer_woman_lt:", "unicode": ":basketballer:", "html": "⛹🏻‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "⛹‍♀", "name": "woman bouncing ball", "shortname": ":woman_bouncing_ball:", "unicode": ":basketballer_woman:", "html": "⛹‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "⛹🏿", "name": "person bouncing ball: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": ":basketballer_dt:", "html": "⛹🏿", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "⛹🏾", "name": "person bouncing ball: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": ":basketballer_mdt:", "html": "⛹🏾", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "⛹🏽", "name": "person bouncing ball: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": ":basketballer_mt:", "html": "⛹🏽", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "⛹🏼", "name": "person bouncing ball: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": ":basketballer_mlt:", "html": "⛹🏼", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "⛹🏻", "name": "person bouncing ball: light skin tone", "shortname": ":basketballer_lt:", "unicode": "26F9 1F3FB", "html": "⛹🏻", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "⛹", "name": "person bouncing ball", "shortname": ":basketballer:", "unicode": "26F9", "html": "⛹", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🕴", "name": "man in suit levitating", "shortname": ":man_in_suit:", "unicode": "1f574", "html": "🕴", "category": "People & Body (person-activity)", "order": "784"}, + {"emoji": "🏌", "name": "person golfing", "shortname": ":golfer:", "unicode": "1f3cc", "html": "🏌", "category": "People & Body (person-sport)", "order": "782"}, + {"emoji": "🏌‍♀", "name": "woman golfing", "shortname": ":golfer_woman:", "unicode": "1F3CC 200D 2640", "html": "🏌‍♀", "category": "People & Body (person-sport)", "order": "783"}, + {"emoji": "*️⃣", "name": "keycap: #", "shortname": "*", "unicode": "0023 FE0F 20E3", "html": "*", "category": "Symbols (keycap)", "order": "2118"}, + {"emoji": "❣️", "name": "heart exclamation", "shortname": ":heart_exclamation:", "unicode": "2763", "html": "❣", "category": "Smileys & Emotion (emotion)", "order": "1300"}, + {"emoji": "✡️", "name": "star of David", "shortname": ":star_of_david:", "unicode": "2721", "html": "✡", "category": "Symbols (religion)", "order": "2026"}, + {"emoji": "✝️", "name": "latin cross", "shortname": ":cross:", "unicode": "271d", "html": "✝", "category": "Symbols (religion)", "order": "2029"}, + {"emoji": "⚜", "name": "fleur-de-lis", "shortname": ":fleur-de-lis:", "unicode": "269c", "html": "⚜", "category": "Symbols (other-symbol)", "order": "2074"}, + {"emoji": "⚛", "name": "atom symbol", "shortname": ":atom:", "unicode": "269b", "html": "⚛", "category": "Symbols (religion)", "order": "2024"}, + {"emoji": "☸", "name": "wheel of dharma", "shortname": ":wheel_of_dharma:", "unicode": "2638", "html": "☸", "category": "Symbols (religion)", "order": "2027"}, + {"emoji": "☯", "name": "yin yang", "shortname": ":yin_yang:", "unicode": "262f", "html": "☯", "category": "Symbols (religion)", "order": "2028"}, + {"emoji": "☮", "name": "peace symbol", "shortname": ":peace:", "unicode": "262e", "html": "☮", "category": "Symbols (religion)", "order": "2032"}, + {"emoji": "☪", "name": "star and crescent", "shortname": ":star_and_crescent:", "unicode": "262a", "html": "☪", "category": "Symbols (religion)", "order": "2031"}, + {"emoji": "☦", "name": "orthodox cross", "shortname": ":orthodox_cross:", "unicode": "2626", "html": "☦", "category": "Symbols (religion)", "order": "2030"}, + {"emoji": "☣", "name": "biohazard", "shortname": ":biohazard:", "unicode": "2623", "html": "☣", "category": "Symbols (warning)", "order": "2001"}, + {"emoji": "☢", "name": "radioactive", "shortname": ":radioactive:", "unicode": "2622", "html": "☢", "category": "Symbols (warning)", "order": "2000"}, + {"emoji": "🛐", "name": "place of worship", "shortname": ":place_of_worship:", "unicode": "1f6d0", "html": "🛐", "category": "Symbols (religion)", "order": "2023"}, + {"emoji": "🗯", "name": "right anger bubble", "shortname": ":anger_right:", "unicode": "1f5ef", "html": "🗯", "category": "Smileys & Emotion (emotion)", "order": "1311"}, + {"emoji": "🕎", "name": "menorah", "shortname": ":menorah:", "unicode": "1f54e", "html": "🕎", "category": "Symbols (religion)", "order": "2033"}, + {"emoji": "🕉", "name": "om", "shortname": ":om_symbol:", "unicode": "1f549", "html": "🕉", "category": "Symbols (religion)", "order": "2025"}, + {"emoji": "⚱", "name": "funeral urn", "shortname": ":funeral_urn:", "unicode": "26B1", "html": "⚱", "category": "Objects (other-object)", "order": ""}, + {"emoji": "⚰", "name": "coffin", "shortname": ":coffin:", "unicode": "26b0", "html": "⚰", "category": "Objects (other-object)", "order": "1970"}, + {"emoji": "⚙", "name": "gear", "shortname": ":gear:", "unicode": "2699", "html": "⚙", "category": "Objects (tool)", "order": "1961"}, + {"emoji": "⚗", "name": "alembic", "shortname": ":alembic:", "unicode": "2697", "html": "⚗", "category": "Objects (science)", "order": "1963"}, + {"emoji": "⚖", "name": "balance scale", "shortname": ":scales:", "unicode": "2696", "html": "⚖", "category": "Objects (tool)", "order": "1964"}, + {"emoji": "⚔", "name": "crossed swords", "shortname": ":crossed_swords:", "unicode": "2694", "html": "⚔", "category": "Objects (tool)", "order": "1955"}, + {"emoji": "⌨", "name": "keyboard", "shortname": ":keyboard:", "unicode": "2328", "html": "⌨", "category": "Objects (computer)", "order": "1849"}, + {"emoji": "🛢", "name": "oil drum", "shortname": ":oil_drum:", "unicode": "1F6E2", "html": "🛢", "category": "Travel & Places (transport-ground)", "order": ""}, + {"emoji": "🛡", "name": "shield", "shortname": ":shield:", "unicode": "1f6e1", "html": "🛡", "category": "Objects (tool)", "order": "1958"}, + {"emoji": "🛠", "name": "hammer and wrench", "shortname": ":hammer_and_wrench:", "unicode": "1F6E0", "html": "🛠", "category": "Objects (tool)", "order": ""}, + {"emoji": "🛏", "name": "bed", "shortname": ":bed:", "unicode": "1f6cf", "html": "🛏", "category": "Objects (household)", "order": "1669"}, + {"emoji": "🛎", "name": "bellhop bell", "shortname": ":bellhop_bell:", "unicode": "1F6CE", "html": "🛎", "category": "Travel & Places (hotel)", "order": ""}, + {"emoji": "🛍", "name": "shopping bags", "shortname": ":shopping_bags:", "unicode": "1f6cd", "html": "🛍", "category": "Objects (clothing)", "order": "1326"}, + {"emoji": "🛌", "name": "person in bed", "shortname": ":sleeping_accommodation:", "unicode": "1f6cc", "html": "🛌", "category": "People & Body (person-resting)", "order": "1663"}, + {"emoji": "🛋", "name": "couch and lamp", "shortname": ":couch_and_lamp:", "unicode": "1F6CB", "html": "🛋", "category": "Objects (household)", "order": ""}, + {"emoji": "🗳", "name": "ballot box with ballot", "shortname": ":ballot_box:", "unicode": "1f5f3", "html": "🗳", "category": "Objects (mail)", "order": "1913"}, + {"emoji": "🗡", "name": "dagger", "shortname": ":dagger:", "unicode": "1F5E1", "html": "🗡", "category": "Objects (tool)", "order": ""}, + {"emoji": "🗞", "name": "rolled-up newspaper", "shortname": ":rolledup_newspaper:", "unicode": "1F5DE", "html": "🗞", "category": "Objects (book-paper)", "order": ""}, + {"emoji": "🗝", "name": "old key", "shortname": ":old_key:", "unicode": "1F5DD", "html": "🗝", "category": "Objects (lock)", "order": ""}, + {"emoji": "🗜", "name": "clamp", "shortname": ":compression:", "unicode": "1f5dc", "html": "🗜", "category": "Objects (tool)", "order": "1962"}, + {"emoji": "🗓", "name": "spiral calendar", "shortname": ":spiral_calendar:", "unicode": "1F5D3", "html": "🗓", "category": "Objects (office)", "order": ""}, + {"emoji": "🗒", "name": "spiral notepad", "shortname": ":spiral_notepad:", "unicode": "1F5D2", "html": "🗒", "category": "Objects (office)", "order": ""}, + {"emoji": "🗑", "name": "wastebasket", "shortname": ":wastebasket:", "unicode": "1f5d1", "html": "🗑", "category": "Objects (office)", "order": "1943"}, + {"emoji": "🗄", "name": "file cabinet", "shortname": ":file_cabinet:", "unicode": "1f5c4", "html": "🗄", "category": "Objects (office)", "order": "1942"}, + {"emoji": "🗃", "name": "card file box", "shortname": ":card_file_box:", "unicode": "1F5C3", "html": "🗃", "category": "Objects (office)", "order": ""}, + {"emoji": "🗂", "name": "card index dividers", "shortname": ":card_index_dividers:", "unicode": "1F5C2", "html": "🗂", "category": "Objects (office)", "order": ""}, + {"emoji": "🖼", "name": "framed picture", "shortname": ":framed_picture:", "unicode": "1F5BC", "html": "🖼", "category": "Activities (arts & crafts)", "order": ""}, + {"emoji": "🖲", "name": "trackball", "shortname": ":trackball:", "unicode": "1f5b2", "html": "🖲", "category": "Objects (computer)", "order": "1851"}, + {"emoji": "🖱", "name": "computer mouse", "shortname": ":computer_mouse:", "unicode": "1F5B1", "html": "🖱", "category": "Objects (computer)", "order": ""}, + {"emoji": "🖨", "name": "printer", "shortname": ":printer:", "unicode": "1f5a8", "html": "🖨", "category": "Objects (computer)", "order": "1848"}, + {"emoji": "🖥", "name": "desktop computer", "shortname": ":desktop_computer:", "unicode": "1F5A5", "html": "🖥", "category": "Objects (computer)", "order": ""}, + {"emoji": "🖍", "name": "crayon", "shortname": ":crayon:", "unicode": "1F58D", "html": "🖍", "category": "Objects (writing)", "order": ""}, + {"emoji": "🖌", "name": "paintbrush", "shortname": ":paintbrush:", "unicode": "1F58C", "html": "🖌", "category": "Objects (writing)", "order": ""}, + {"emoji": "🖋", "name": "fountain pen", "shortname": ":fountain_pen:", "unicode": "1F58B", "html": "🖋", "category": "Objects (writing)", "order": ""}, + {"emoji": "🖊", "name": "pen", "shortname": ":pen:", "unicode": "1F58A", "html": "🖊", "category": "Objects (writing)", "order": ""}, + {"emoji": "🖇", "name": "linked paperclips", "shortname": ":linked_paperclips:", "unicode": "1F587", "html": "🖇", "category": "Objects (office)", "order": ""}, + {"emoji": "🕹", "name": "joystick", "shortname": ":joystick:", "unicode": "1f579", "html": "🕹", "category": "Activities (game)", "order": "1805"}, + {"emoji": "🕳", "name": "hole", "shortname": ":hole:", "unicode": "1f573", "html": "🕳", "category": "Smileys & Emotion (emotion)", "order": "1313"}, + {"emoji": "🕰", "name": "mantelpiece clock", "shortname": ":mantelpiece_clock:", "unicode": "1F570", "html": "🕰", "category": "Travel & Places (time)", "order": ""}, + {"emoji": "🕯", "name": "candle", "shortname": ":candle:", "unicode": "1f56f", "html": "🕯", "category": "Objects (light & video)", "order": "1870"}, + {"emoji": "📿", "name": "prayer beads", "shortname": ":prayer_beads:", "unicode": "1f4ff", "html": "📿", "category": "Objects (clothing)", "order": "1338"}, + {"emoji": "📽", "name": "film projector", "shortname": ":film_projector:", "unicode": "1F4FD", "html": "📽", "category": "Objects (light & video)", "order": ""}, + {"emoji": "📸", "name": "camera with flash", "shortname": ":camera_with_flash:", "unicode": "1f4f8", "html": "📸", "category": "Objects (light & video)", "order": "1862"}, + {"emoji": "🏺", "name": "amphora", "shortname": ":amphora:", "unicode": "1f3fa", "html": "🏺", "category": "Food & Drink (dishware)", "order": "1537"}, + {"emoji": "🏷", "name": "label", "shortname": ":label:", "unicode": "1f3f7", "html": "🏷", "category": "Objects (book-paper)", "order": "1890"}, + {"emoji": "🏴", "name": "black flag", "shortname": ":flag_black:", "unicode": "1f3f4", "html": "🏴", "category": "Flags (flag)", "order": "2184"}, + {"emoji": "🏳", "name": "white flag", "shortname": ":flag_white:", "unicode": "1f3f3", "html": "🏳", "category": "Flags (flag)", "order": "2185"}, + {"emoji": "🎞", "name": "film frames", "shortname": ":film_frames:", "unicode": "1f39e", "html": "🎞", "category": "Objects (light & video)", "order": "1857"}, + {"emoji": "🎛", "name": "control knobs", "shortname": ":control_knobs:", "unicode": "1f39b", "html": "🎛", "category": "Objects (music)", "order": "1828"}, + {"emoji": "🎚", "name": "level slider", "shortname": ":level_slider:", "unicode": "1f39a", "html": "🎚", "category": "Objects (music)", "order": "1827"}, + {"emoji": "🎙", "name": "studio microphone", "shortname": ":studio_microphone:", "unicode": "1F399", "html": "🎙", "category": "Objects (music)", "order": ""}, + {"emoji": "🌡", "name": "thermometer", "shortname": ":thermometer:", "unicode": "1f321", "html": "🌡", "category": "Travel & Places (sky & weather)", "order": "1723"}, + {"emoji": "🛳", "name": "passenger ship", "shortname": ":passenger_ship:", "unicode": "1F6F3", "html": "🛳", "category": "Travel & Places (transport-water)", "order": ""}, + {"emoji": "🛰", "name": "satellite", "shortname": ":satellite:", "unicode": "1F6F0", "html": "🛰", "category": "Travel & Places (transport-air)", "order": ""}, + {"emoji": "🛬", "name": "airplane arrival", "shortname": ":airplane_arriving:", "unicode": "1f6ec", "html": "🛬", "category": "Travel & Places (transport-air)", "order": "1653"}, + {"emoji": "🛫", "name": "airplane departure", "shortname": ":airplane_departure:", "unicode": "1f6eb", "html": "🛫", "category": "Travel & Places (transport-air)", "order": "1652"}, + {"emoji": "🛩", "name": "small airplane", "shortname": ":small_airplane:", "unicode": "1F6E9", "html": "🛩", "category": "Travel & Places (transport-air)", "order": ""}, + {"emoji": "🛥", "name": "motor boat", "shortname": ":motor_boat:", "unicode": "1F6E5", "html": "🛥", "category": "Travel & Places (transport-water)", "order": ""}, + {"emoji": "🛤", "name": "railway track", "shortname": ":railway_track:", "unicode": "1f6e4", "html": "🛤", "category": "Travel & Places (transport-ground)", "order": "1635"}, + {"emoji": "🛣", "name": "motorway", "shortname": ":motorway:", "unicode": "1f6e3", "html": "🛣", "category": "Travel & Places (transport-ground)", "order": "1634"}, + {"emoji": "🗺", "name": "world map", "shortname": ":world_map:", "unicode": "1F5FA", "html": "🗺", "category": "Travel & Places (place-map)", "order": ""}, + {"emoji": "🕍", "name": "synagogue", "shortname": ":synagogue:", "unicode": "1f54d", "html": "🕍", "category": "Travel & Places (place-religious)", "order": "1579"}, + {"emoji": "🕌", "name": "mosque", "shortname": ":mosque:", "unicode": "1f54c", "html": "🕌", "category": "Travel & Places (place-religious)", "order": "1578"}, + {"emoji": "🕋", "name": "kaaba", "shortname": ":kaaba:", "unicode": "1f54b", "html": "🕋", "category": "Travel & Places (place-religious)", "order": "1581"}, + {"emoji": "🏟", "name": "stadium", "shortname": ":stadium:", "unicode": "1f3df", "html": "🏟", "category": "Travel & Places (place-building)", "order": "1553"}, + {"emoji": "🏞", "name": "national park", "shortname": ":national_park:", "unicode": "1F3DE", "html": "🏞", "category": "Travel & Places (place-geographic)", "order": ""}, + {"emoji": "🏝", "name": "desert island", "shortname": ":desert_island:", "unicode": "1F3DD", "html": "🏝", "category": "Travel & Places (place-geographic)", "order": ""}, + {"emoji": "🏜", "name": "desert", "shortname": ":desert:", "unicode": "1f3dc", "html": "🏜", "category": "Travel & Places (place-geographic)", "order": "1550"}, + {"emoji": "🏛", "name": "classical building", "shortname": ":classical_building:", "unicode": "1f3db", "html": "🏛", "category": "Travel & Places (place-building)", "order": "1554"}, + {"emoji": "🏚", "name": "derelict house", "shortname": ":derelict_house:", "unicode": "1F3DA", "html": "🏚", "category": "Travel & Places (place-building)", "order": ""}, + {"emoji": "🏙", "name": "cityscape", "shortname": ":cityscape:", "unicode": "1f3d9", "html": "🏙", "category": "Travel & Places (place-other)", "order": "1557"}, + {"emoji": "🏘", "name": "houses", "shortname": ":houses:", "unicode": "1F3D8", "html": "🏘", "category": "Travel & Places (place-building)", "order": ""}, + {"emoji": "🏗", "name": "building construction", "shortname": ":building_construction:", "unicode": "1F3D7", "html": "🏗", "category": "Travel & Places (place-building)", "order": ""}, + {"emoji": "🏖", "name": "beach with umbrella", "shortname": ":beach_with_umbrella:", "unicode": "1F3D6", "html": "🏖", "category": "Travel & Places (place-geographic)", "order": ""}, + {"emoji": "🏕", "name": "camping", "shortname": ":camping:", "unicode": "1f3d5", "html": "🏕", "category": "Travel & Places (place-geographic)", "order": "1548"}, + {"emoji": "🏔", "name": "snow-capped mountain", "shortname": ":snowcapped_mountain:", "unicode": "1F3D4", "html": "🏔", "category": "Travel & Places (place-geographic)", "order": ""}, + {"emoji": "🏎", "name": "racing car", "shortname": ":racing_car:", "unicode": "1F3CE", "html": "🏎", "category": "Travel & Places (transport-ground)", "order": ""}, + {"emoji": "🏍", "name": "motorcycle", "shortname": ":motorcycle:", "unicode": "1F3CD", "html": "🏍", "category": "Travel & Places (transport-ground)", "order": ""}, + {"emoji": "🏹", "name": "bow and arrow", "shortname": ":bow_and_arrow:", "unicode": "1f3f9", "html": "🏹", "category": "Objects (tool)", "order": "1957"}, + {"emoji": "🏸", "name": "badminton", "shortname": ":badminton:", "unicode": "1F3F8", "html": "🏸", "category": "Activities (sport)", "order": ""}, + {"emoji": "🏵", "name": "rosette", "shortname": ":rosette:", "unicode": "1f3f5", "html": "🏵", "category": "Animals & Nature (plant-flower)", "order": "1430"}, + {"emoji": "🏓", "name": "ping pong", "shortname": ":ping_pong:", "unicode": "1F3D3", "html": "🏓", "category": "Activities (sport)", "order": ""}, + {"emoji": "🏒", "name": "ice hockey", "shortname": ":ice_hockey:", "unicode": "1F3D2", "html": "🏒", "category": "Activities (sport)", "order": ""}, + {"emoji": "🏑", "name": "field hockey", "shortname": ":field_hockey:", "unicode": "1F3D1", "html": "🏑", "category": "Activities (sport)", "order": ""}, + {"emoji": "🏐", "name": "volleyball", "shortname": ":volleyball:", "unicode": "1f3d0", "html": "🏐", "category": "Activities (sport)", "order": "1784"}, + {"emoji": "🏏", "name": "cricket game", "shortname": ":cricket_game:", "unicode": "1F3CF", "html": "🏏", "category": "Activities (sport)", "order": ""}, + {"emoji": "🏅", "name": "sports medal", "shortname": ":medal:", "unicode": "1f3c5", "html": "🏅", "category": "Activities (award-medal)", "order": "1777"}, + {"emoji": "🎟", "name": "admission tickets", "shortname": ":admission_tickets:", "unicode": "1F39F", "html": "🎟", "category": "Activities (event)", "order": ""}, + {"emoji": "🎗", "name": "reminder ribbon", "shortname": ":reminder_ribbon:", "unicode": "1f397", "html": "🎗", "category": "Activities (event)", "order": "1772"}, + {"emoji": "🎖", "name": "military medal", "shortname": ":military_medal:", "unicode": "1F396", "html": "🎖", "category": "Activities (award-medal)", "order": ""}, + {"emoji": "🧀", "name": "cheese wedge", "shortname": ":cheese_wedge:", "unicode": "1F9C0", "html": "🧀", "category": "Food & Drink (food-prepared)", "order": ""}, + {"emoji": "🍿", "name": "popcorn", "shortname": ":popcorn:", "unicode": "1f37f", "html": "🍿", "category": "Food & Drink (food-prepared)", "order": "1494"}, + {"emoji": "🍾", "name": "bottle with popping cork", "shortname": ":champagne:", "unicode": "1f37e", "html": "🍾", "category": "Food & Drink (drink)", "order": "1525"}, + {"emoji": "🍽", "name": "fork and knife with plate", "shortname": ":fork_and_knife_with_plate:", "unicode": "1F37D", "html": "🍽", "category": "Food & Drink (dishware)", "order": ""}, + {"emoji": "🌶", "name": "hot pepper", "shortname": ":hot_pepper:", "unicode": "1f336", "html": "🌶", "category": "Food & Drink (food-vegetable)", "order": "1469"}, + {"emoji": "🌯", "name": "burrito", "shortname": ":burrito:", "unicode": "1f32f", "html": "🌯", "category": "Food & Drink (food-prepared)", "order": "1487"}, + {"emoji": "🌮", "name": "taco", "shortname": ":taco:", "unicode": "1f32e", "html": "🌮", "category": "Food & Drink (food-prepared)", "order": "1486"}, + {"emoji": "🌭", "name": "hot dog", "shortname": ":hotdog:", "unicode": "1f32d", "html": "🌭", "category": "Food & Drink (food-prepared)", "order": "1485"}, + {"emoji": "☘", "name": "shamrock", "shortname": ":shamrock:", "unicode": "2618", "html": "☘", "category": "Animals & Nature (plant-other)", "order": "1444"}, + {"emoji": "☄", "name": "comet", "shortname": ":comet:", "unicode": "2604", "html": "☄", "category": "Travel & Places (sky & weather)", "order": "1752"}, + {"emoji": "☃️", "name": "snowman", "shortname": ":snowman:", "unicode": "2603 FE0F", "html": "☃", "category": "Travel & Places (sky & weather)", "order": ""}, + {"emoji": "☂️", "name": "umbrella", "shortname": ":umbrella:", "unicode": "2602 FE0F", "html": "☂", "category": "Travel & Places (sky & weather)", "order": ""}, + {"emoji": "🦄", "name": "unicorn", "shortname": ":unicorn:", "unicode": "1F984", "html": "🦄", "category": "Animals & Nature (animal-mammal)", "order": ""}, + {"emoji": "🦃", "name": "turkey", "shortname": ":turkey:", "unicode": "1f983", "html": "🦃", "category": "Animals & Nature (animal-bird)", "order": "1387"}, + {"emoji": "🦂", "name": "scorpion", "shortname": ":scorpion:", "unicode": "1f982", "html": "🦂", "category": "Animals & Nature (animal-bug)", "order": "1426"}, + {"emoji": "🦁", "name": "lion", "shortname": ":lion_face:", "unicode": "1f981", "html": "🦁", "category": "Animals & Nature (animal-mammal)", "order": "1352"}, + {"emoji": "🦀", "name": "crab", "shortname": ":crab:", "unicode": "1f980", "html": "🦀", "category": "Food & Drink (food-marine)", "order": "1415"}, + {"emoji": "🕸", "name": "spider web", "shortname": ":spider_web:", "unicode": "1f578", "html": "🕸", "category": "Animals & Nature (animal-bug)", "order": "1425"}, + {"emoji": "🕷", "name": "spider", "shortname": ":spider:", "unicode": "1f577", "html": "🕷", "category": "Animals & Nature (animal-bug)", "order": "1424"}, + {"emoji": "🕊", "name": "dove", "shortname": ":dove:", "unicode": "1F54A", "html": "🕊", "category": "Animals & Nature (animal-bird)", "order": ""}, + {"emoji": "🐿", "name": "chipmunk", "shortname": ":chipmunk:", "unicode": "1f43f", "html": "🐿", "category": "Animals & Nature (animal-mammal)", "order": "1381"}, + {"emoji": "🌬", "name": "wind face", "shortname": ":wind_blowing_face:", "unicode": "1f32c", "html": "🌬", "category": "Travel & Places (sky & weather)", "order": "1741"}, + {"emoji": "🌫", "name": "fog", "shortname": ":fog:", "unicode": "1f32b", "html": "🌫", "category": "Travel & Places (sky & weather)", "order": "1740"}, + {"emoji": "🌪", "name": "tornado", "shortname": ":tornado:", "unicode": "1F32A", "html": "🌪", "category": "Travel & Places (sky & weather)", "order": ""}, + {"emoji": "🌩", "name": "cloud with lightning", "shortname": ":cloud_with_lightning:", "unicode": "1F329", "html": "🌩", "category": "Travel & Places (sky & weather)", "order": ""}, + {"emoji": "🌨", "name": "cloud with snow", "shortname": ":cloud_with_snow:", "unicode": "1F328", "html": "🌨", "category": "Travel & Places (sky & weather)", "order": ""}, + {"emoji": "🌧", "name": "cloud with rain", "shortname": ":cloud_with_rain:", "unicode": "1F327", "html": "🌧", "category": "Travel & Places (sky & weather)", "order": ""}, + {"emoji": "🌦", "name": "sun behind rain cloud", "shortname": ":sun_behind_rain_cloud:", "unicode": "1F326", "html": "🌦", "category": "Travel & Places (sky & weather)", "order": ""}, + {"emoji": "🌥", "name": "sun behind large cloud", "shortname": ":sun_behind_large_cloud:", "unicode": "1F325", "html": "🌥", "category": "Travel & Places (sky & weather)", "order": ""}, + {"emoji": "🌤", "name": "sun behind small cloud", "shortname": ":sun_behind_small_cloud:", "unicode": "1F324", "html": "🌤", "category": "Travel & Places (sky & weather)", "order": ""}, + {"emoji": "🗣", "name": "speaking head", "shortname": ":speaking_head:", "unicode": "1F5E3", "html": "🗣", "category": "People & Body (person-symbol)", "order": ""}, + {"emoji": "⏺", "name": "record button", "shortname": ":record_button:", "unicode": "23FA", "html": "⏺", "category": "Symbols (av-symbol)", "order": ""}, + {"emoji": "⏹", "name": "stop button", "shortname": ":stop_button:", "unicode": "23F9", "html": "⏹", "category": "Symbols (av-symbol)", "order": ""}, + {"emoji": "⏸", "name": "pause button", "shortname": ":pause_button:", "unicode": "23F8", "html": "⏸", "category": "Symbols (av-symbol)", "order": ""}, + {"emoji": "⏯", "name": "play or pause button", "shortname": ":play_pause:", "unicode": "23ef", "html": "⏯", "category": "Symbols (av-symbol)", "order": "2054"}, + {"emoji": "⏮", "name": "last track button", "shortname": ":track_previous:", "unicode": "23ee", "html": "⏮", "category": "Symbols (av-symbol)", "order": "2057"}, + {"emoji": "⏭", "name": "next track button", "shortname": ":track_next:", "unicode": "23ed", "html": "⏭", "category": "Symbols (av-symbol)", "order": "2053"}, + {"emoji": "⛱", "name": "umbrella on ground", "shortname": ":beach_umbrella:", "unicode": "26f1", "html": "⛱", "category": "Travel & Places (sky & weather)", "order": "1747"}, + {"emoji": "⛓", "name": "chains", "shortname": ":chains:", "unicode": "26d3", "html": "⛓", "category": "Objects (tool)", "order": "1966"}, + {"emoji": "⛏", "name": "pick", "shortname": ":pick:", "unicode": "26cf", "html": "⛏", "category": "Objects (tool)", "order": "1951"}, + {"emoji": "⚒", "name": "hammer and pick", "shortname": ":hammer_and_pick:", "unicode": "2692", "html": "⚒", "category": "Objects (tool)", "order": ""}, + {"emoji": "⏲", "name": "timer clock", "shortname": ":timer_clock:", "unicode": "23F2", "html": "⏲", "category": "Travel & Places (time)", "order": ""}, + {"emoji": "⏱", "name": "stopwatch", "shortname": ":stopwatch:", "unicode": "23f1", "html": "⏱", "category": "Travel & Places (time)", "order": "1684"}, + {"emoji": "⛴", "name": "ferry", "shortname": ":ferry:", "unicode": "26f4", "html": "⛴", "category": "Travel & Places (transport-water)", "order": "1647"}, + {"emoji": "⛰", "name": "mountain", "shortname": ":mountain:", "unicode": "26f0", "html": "⛰", "category": "Travel & Places (place-geographic)", "order": "1545"}, + {"emoji": "⛩", "name": "shinto shrine", "shortname": ":shinto_shrine:", "unicode": "2.6E+10", "html": "⛩", "category": "Travel & Places (place-religious)", "order": "1580"}, + {"emoji": "⛸", "name": "ice skate", "shortname": ":ice_skate:", "unicode": "26f8", "html": "⛸", "category": "Activities (sport)", "order": "1800"}, + {"emoji": "⛷", "name": "skier", "shortname": ":skier:", "unicode": "26f7", "html": "⛷", "category": "People & Body (person-sport)", "order": "775"}, + {"emoji": "⛈", "name": "cloud with lightning and rain", "shortname": ":cloud_with_lightning_and_rain:", "unicode": "26C8", "html": "⛈", "category": "Travel & Places (sky & weather)", "order": ""}, + {"emoji": "⛑", "name": "rescue worker’s helmet", "shortname": ":rescue_worker’s_helmet:", "unicode": "26D1", "html": "⛑", "category": "Objects (clothing)", "order": ""}, + {"emoji": "🇦🇨", "name": "flag: Ascension Island", "shortname": ":flag_ac:", "unicode": "1f1e6-1f1e8", "html": "🇦🇨", "category": "Flags (country-flag)", "order": "2187"}, + {"emoji": "🇦🇩", "name": "flag: Andorra", "shortname": ":flag_ad:", "unicode": "1f1e6-1f1e9", "html": "🇦🇩", "category": "Flags (country-flag)", "order": "2188"}, + {"emoji": "🇦🇪", "name": "flag: United Arab Emirates", "shortname": ":flag_ae:", "unicode": "1f1e6-1f1ea", "html": "🇦🇪", "category": "Flags (country-flag)", "order": "2189"}, + {"emoji": "🇦🇫", "name": "flag: Afghanistan", "shortname": ":flag_af:", "unicode": "1f1e6-1f1eb", "html": "🇦🇫", "category": "Flags (country-flag)", "order": "2190"}, + {"emoji": "🇦🇬", "name": "flag: Antigua & Barbuda", "shortname": ":flag_ag:", "unicode": "1f1e6-1f1ec", "html": "🇦🇬", "category": "Flags (country-flag)", "order": "2191"}, + {"emoji": "🇦🇮", "name": "flag: Anguilla", "shortname": ":flag_ai:", "unicode": "1f1e6-1f1ee", "html": "🇦🇮", "category": "Flags (country-flag)", "order": "2192"}, + {"emoji": "🇦🇱", "name": "flag: Albania", "shortname": ":flag_al:", "unicode": "1f1e6-1f1f1", "html": "🇦🇱", "category": "Flags (country-flag)", "order": "2193"}, + {"emoji": "🇦🇲", "name": "flag: Armenia", "shortname": ":flag_am:", "unicode": "1f1e6-1f1f2", "html": "🇦🇲", "category": "Flags (country-flag)", "order": "2194"}, + {"emoji": "🇦🇴", "name": "flag: Angola", "shortname": ":flag-ao:", "unicode": "1f1e6-1f1f4", "html": "🇦🇴", "category": "Flags (country-flag)", "order": "2195"}, + {"emoji": "🇦🇶", "name": "flag: Antarctica", "shortname": ":flag-aq:", "unicode": "1f1e6-1f1f6", "html": "🇦🇶", "category": "Flags (country-flag)", "order": "2196"}, + {"emoji": "🇦🇷", "name": "flag: Argentina", "shortname": ":flag-ar:", "unicode": "1f1e6-1f1f7", "html": "🇦🇷", "category": "Flags (country-flag)", "order": "2197"}, + {"emoji": "🇦🇸", "name": "flag: American Samoa", "shortname": ":flag-as:", "unicode": "1f1e6-1f1f8", "html": "🇦🇸", "category": "Flags (country-flag)", "order": "2198"}, + {"emoji": "🇦🇹", "name": "flag: Austria", "shortname": ":flag-at:", "unicode": "1f1e6-1f1f9", "html": "🇦🇹", "category": "Flags (country-flag)", "order": "2199"}, + {"emoji": "🇦🇺", "name": "flag: Australia", "shortname": ":flag-au:", "unicode": "1f1e6-1f1fa", "html": "🇦🇺", "category": "Flags (country-flag)", "order": "2200"}, + {"emoji": "🇦🇼", "name": "flag: Aruba", "shortname": ":flag-aw:", "unicode": "1f1e6-1f1fc", "html": "🇦🇼", "category": "Flags (country-flag)", "order": "2201"}, + {"emoji": "🇦🇽", "name": "flag: Åland Islands", "shortname": ":flag-ax:", "unicode": "1f1e6-1f1fd", "html": "🇦🇽", "category": "Flags (country-flag)", "order": "2202"}, + {"emoji": "🇦🇿", "name": "flag: Azerbaijan", "shortname": ":flag-az:", "unicode": "1f1e6-1f1ff", "html": "🇦🇿", "category": "Flags (country-flag)", "order": "2203"}, + {"emoji": "🇧🇦", "name": "flag: Bosnia & Herzegovina", "shortname": ":flag-ba:", "unicode": "1f1e7-1f1e6", "html": "🇧🇦", "category": "Flags (country-flag)", "order": "2204"}, + {"emoji": "🇧🇧", "name": "flag: Barbados", "shortname": ":flag-bb:", "unicode": "1f1e7-1f1e7", "html": "🇧🇧", "category": "Flags (country-flag)", "order": "2205"}, + {"emoji": "🇧🇩", "name": "flag: Bangladesh", "shortname": ":flag-bd:", "unicode": "1f1e7-1f1e9", "html": "🇧🇩", "category": "Flags (country-flag)", "order": "2206"}, + {"emoji": "🇧🇪", "name": "flag: Belgium", "shortname": ":flag-be:", "unicode": "1f1e7-1f1ea", "html": "🇧🇪", "category": "Flags (country-flag)", "order": "2207"}, + {"emoji": "🇧🇫", "name": "flag: Burkina Faso", "shortname": ":flag-bf:", "unicode": "1f1e7-1f1eb", "html": "🇧🇫", "category": "Flags (country-flag)", "order": "2208"}, + {"emoji": "🇧🇬", "name": "flag: Bulgaria", "shortname": ":flag-bg:", "unicode": "1f1e7-1f1ec", "html": "🇧🇬", "category": "Flags (country-flag)", "order": "2209"}, + {"emoji": "🇧🇭", "name": "flag: Bahrain", "shortname": ":flag-bh:", "unicode": "1f1e7-1f1ed", "html": "🇧🇭", "category": "Flags (country-flag)", "order": "2210"}, + {"emoji": "🇧🇮", "name": "flag: Burundi", "shortname": ":flag-bi:", "unicode": "1f1e7-1f1ee", "html": "🇧🇮", "category": "Flags (country-flag)", "order": "2211"}, + {"emoji": "🇧🇯", "name": "flag: Benin", "shortname": ":flag-bj:", "unicode": "1f1e7-1f1ef", "html": "🇧🇯", "category": "Flags (country-flag)", "order": "2212"}, + {"emoji": "🇧🇱", "name": "flag: St. Barthélemy", "shortname": ":flag-bl:", "unicode": "1f1e7-1f1f1", "html": "🇧🇱", "category": "Flags (country-flag)", "order": "2213"}, + {"emoji": "🇧🇲", "name": "flag: Bermuda", "shortname": ":flag-bm:", "unicode": "1f1e7-1f1f2", "html": "🇧🇲", "category": "Flags (country-flag)", "order": "2214"}, + {"emoji": "🇧🇳", "name": "flag: Brunei", "shortname": ":flag-bn:", "unicode": "1f1e7-1f1f3", "html": "🇧🇳", "category": "Flags (country-flag)", "order": "2215"}, + {"emoji": "🇧🇴", "name": "flag: Bolivia", "shortname": ":flag-bo:", "unicode": "1f1e7-1f1f4", "html": "🇧🇴", "category": "Flags (country-flag)", "order": "2216"}, + {"emoji": "🇧🇶", "name": "flag: Caribbean Netherlands", "shortname": ":flag-bq:", "unicode": "1f1e7-1f1f6", "html": "🇧🇶", "category": "Flags (country-flag)", "order": "2217"}, + {"emoji": "🇧🇷", "name": "flag: Brazil", "shortname": ":flag-br:", "unicode": "1f1e7-1f1f7", "html": "🇧🇷", "category": "Flags (country-flag)", "order": "2218"}, + {"emoji": "🇧🇸", "name": "flag: Bahamas", "shortname": ":flag-bs:", "unicode": "1f1e7-1f1f8", "html": "🇧🇸", "category": "Flags (country-flag)", "order": "2219"}, + {"emoji": "🇧🇹", "name": "flag: Bhutan", "shortname": ":flag-bt:", "unicode": "1f1e7-1f1f9", "html": "🇧🇹", "category": "Flags (country-flag)", "order": "2220"}, + {"emoji": "🇧🇻", "name": "flag: Bouvet Island", "shortname": ":flag-bv:", "unicode": "1f1e7-1f1fb", "html": "🇧🇻", "category": "Flags (country-flag)", "order": "2221"}, + {"emoji": "🇧🇼", "name": "flag: Botswana", "shortname": ":flag-bw:", "unicode": "1f1e7-1f1fc", "html": "🇧🇼", "category": "Flags (country-flag)", "order": "2222"}, + {"emoji": "🇧🇾", "name": "flag: Belarus", "shortname": ":flag-by:", "unicode": "1f1e7-1f1fe", "html": "🇧🇾", "category": "Flags (country-flag)", "order": "2223"}, + {"emoji": "🇧🇿", "name": "flag: Belize", "shortname": ":flag-bz:", "unicode": "1f1e7-1f1ff", "html": "🇧🇿", "category": "Flags (country-flag)", "order": "2224"}, + {"emoji": "🇨🇦", "name": "flag: Canada", "shortname": ":flag-ca:", "unicode": "1f1e8-1f1e6", "html": "🇨🇦", "category": "Flags (country-flag)", "order": "2225"}, + {"emoji": "🇨🇨", "name": "flag: Cocos (Keeling) Islands", "shortname": ":flag-cc:", "unicode": "1f1e8-1f1e8", "html": "🇨🇨", "category": "Flags (country-flag)", "order": "2226"}, + {"emoji": "🇨🇩", "name": "flag: Congo - Kinshasa", "shortname": ":flag-cd:", "unicode": "1f1e8-1f1e9", "html": "🇨🇩", "category": "Flags (country-flag)", "order": "2227"}, + {"emoji": "🇨🇫", "name": "flag: Central African Republic", "shortname": ":flag-cf:", "unicode": "1f1e8-1f1eb", "html": "🇨🇫", "category": "Flags (country-flag)", "order": "2228"}, + {"emoji": "🇨🇬", "name": "flag: Congo - Brazzaville", "shortname": ":flag-cg:", "unicode": "1f1e8-1f1ec", "html": "🇨🇬", "category": "Flags (country-flag)", "order": "2229"}, + {"emoji": "🇨🇭", "name": "flag: Switzerland", "shortname": ":flag-ch:", "unicode": "1f1e8-1f1ed", "html": "🇨🇭", "category": "Flags (country-flag)", "order": "2230"}, + {"emoji": "🇨🇮", "name": "flag: Côte d’Ivoire", "shortname": ":flag-ci:", "unicode": "1f1e8-1f1ee", "html": "🇨🇮", "category": "Flags (country-flag)", "order": "2231"}, + {"emoji": "🇨🇰", "name": "flag: Cook Islands", "shortname": ":flag-ck:", "unicode": "1f1e8-1f1f0", "html": "🇨🇰", "category": "Flags (country-flag)", "order": "2232"}, + {"emoji": "🇨🇱", "name": "flag: Chile", "shortname": ":flag-cl:", "unicode": "1f1e8-1f1f1", "html": "🇨🇱", "category": "Flags (country-flag)", "order": "2233"}, + {"emoji": "🇨🇲", "name": "flag: Cameroon", "shortname": ":flag-cm:", "unicode": "1f1e8-1f1f2", "html": "🇨🇲", "category": "Flags (country-flag)", "order": "2234"}, + {"emoji": "🇨🇳", "name": "flag: China", "shortname": ":flag-cn:", "unicode": "1f1e8-1f1f3", "html": "🇨🇳", "category": "Flags (country-flag)", "order": "2235"}, + {"emoji": "🇨🇴", "name": "flag: Colombia", "shortname": ":flag-co:", "unicode": "1f1e8-1f1f4", "html": "🇨🇴", "category": "Flags (country-flag)", "order": "2236"}, + {"emoji": "🇨🇵", "name": "flag: Clipperton Island", "shortname": ":flag-cp:", "unicode": "1f1e8-1f1f5", "html": "🇨🇵", "category": "Flags (country-flag)", "order": "2237"}, + {"emoji": "🇨🇷", "name": "flag: Costa Rica", "shortname": ":flag-cr:", "unicode": "1f1e8-1f1f7", "html": "🇨🇷", "category": "Flags (country-flag)", "order": "2238"}, + {"emoji": "🇨🇺", "name": "flag: Cuba", "shortname": ":flag-cu:", "unicode": "1f1e8-1f1fa", "html": "🇨🇺", "category": "Flags (country-flag)", "order": "2239"}, + {"emoji": "🇨🇻", "name": "flag: Cape Verde", "shortname": ":flag-cv:", "unicode": "1f1e8-1f1fb", "html": "🇨🇻", "category": "Flags (country-flag)", "order": "2240"}, + {"emoji": "🇨🇼", "name": "flag: Curaçao", "shortname": ":flag-cw:", "unicode": "1f1e8-1f1fc", "html": "🇨🇼", "category": "Flags (country-flag)", "order": "2241"}, + {"emoji": "🇨🇽", "name": "flag: Christmas Island", "shortname": ":flag-cx:", "unicode": "1f1e8-1f1fd", "html": "🇨🇽", "category": "Flags (country-flag)", "order": "2242"}, + {"emoji": "🇨🇾", "name": "flag: Cyprus", "shortname": ":flag-cy:", "unicode": "1f1e8-1f1fe", "html": "🇨🇾", "category": "Flags (country-flag)", "order": "2243"}, + {"emoji": "🇨🇿", "name": "flag: Czechia", "shortname": ":flag-cz:", "unicode": "1f1e8-1f1ff", "html": "🇨🇿", "category": "Flags (country-flag)", "order": "2244"}, + {"emoji": "🇩🇪", "name": "flag: Germany", "shortname": ":flag-de:", "unicode": "1f1e9-1f1ea", "html": "🇩🇪", "category": "Flags (country-flag)", "order": "2245"}, + {"emoji": "🇩🇬", "name": "flag: Diego Garcia", "shortname": ":flag-dg:", "unicode": "1f1e9-1f1ec", "html": "🇩🇬", "category": "Flags (country-flag)", "order": "2246"}, + {"emoji": "🇩🇯", "name": "flag: Djibouti", "shortname": ":flag-dj:", "unicode": "1f1e9-1f1ef", "html": "🇩🇯", "category": "Flags (country-flag)", "order": "2247"}, + {"emoji": "🇩🇰", "name": "flag: Denmark", "shortname": ":flag-dk:", "unicode": "1f1e9-1f1f0", "html": "🇩🇰", "category": "Flags (country-flag)", "order": "2248"}, + {"emoji": "🇩🇲", "name": "flag: Dominica", "shortname": ":flag-dm:", "unicode": "1f1e9-1f1f2", "html": "🇩🇲", "category": "Flags (country-flag)", "order": "2249"}, + {"emoji": "🇩🇴", "name": "flag: Dominican Republic", "shortname": ":flag-do:", "unicode": "1f1e9-1f1f4", "html": "🇩🇴", "category": "Flags (country-flag)", "order": "2250"}, + {"emoji": "🇩🇿", "name": "flag: Algeria", "shortname": ":flag-dz:", "unicode": "1f1e9-1f1ff", "html": "🇩🇿", "category": "Flags (country-flag)", "order": "2251"}, + {"emoji": "🇪🇦", "name": "flag: Ceuta & Melilla", "shortname": ":flag-ea:", "unicode": "1f1ea-1f1e6", "html": "🇪🇦", "category": "Flags (country-flag)", "order": "2252"}, + {"emoji": "🇪🇨", "name": "flag: Ecuador", "shortname": ":flag-ec:", "unicode": "1f1ea-1f1e8", "html": "🇪🇨", "category": "Flags (country-flag)", "order": "2253"}, + {"emoji": "🇪🇪", "name": "flag: Estonia", "shortname": ":flag-ee:", "unicode": "1f1ea-1f1ea", "html": "🇪🇪", "category": "Flags (country-flag)", "order": "2254"}, + {"emoji": "🇪🇬", "name": "flag: Egypt", "shortname": ":flag-eg:", "unicode": "1f1ea-1f1ec", "html": "🇪🇬", "category": "Flags (country-flag)", "order": "2255"}, + {"emoji": "🇪🇭", "name": "flag: Western Sahara", "shortname": ":flag-eh:", "unicode": "1f1ea-1f1ed", "html": "🇪🇭", "category": "Flags (country-flag)", "order": "2256"}, + {"emoji": "🇪🇷", "name": "flag: Eritrea", "shortname": ":flag-er:", "unicode": "1f1ea-1f1f7", "html": "🇪🇷", "category": "Flags (country-flag)", "order": "2257"}, + {"emoji": "🇪🇸", "name": "flag: Spain", "shortname": ":flag-es:", "unicode": "1f1ea-1f1f8", "html": "🇪🇸", "category": "Flags (country-flag)", "order": "2258"}, + {"emoji": "🇪🇹", "name": "flag: Ethiopia", "shortname": ":flag-et:", "unicode": "1f1ea-1f1f9", "html": "🇪🇹", "category": "Flags (country-flag)", "order": "2259"}, + {"emoji": "🇪🇺", "name": "flag: European Union", "shortname": ":flag-eu:", "unicode": "1f1ea-1f1fa", "html": "🇪🇺", "category": "Flags (country-flag)", "order": "2260"}, + {"emoji": "🇫🇮", "name": "flag: Finland", "shortname": ":flag-fi:", "unicode": "1f1eb-1f1ee", "html": "🇫🇮", "category": "Flags (country-flag)", "order": "2261"}, + {"emoji": "🇫🇯", "name": "flag: Fiji", "shortname": ":flag-fj:", "unicode": "1f1eb-1f1ef", "html": "🇫🇯", "category": "Flags (country-flag)", "order": "2262"}, + {"emoji": "🇫🇰", "name": "flag: Falkland Islands", "shortname": ":flag-fk:", "unicode": "1f1eb-1f1f0", "html": "🇫🇰", "category": "Flags (country-flag)", "order": "2263"}, + {"emoji": "🇫🇲", "name": "flag: Micronesia", "shortname": ":flag-fm:", "unicode": "1f1eb-1f1f2", "html": "🇫🇲", "category": "Flags (country-flag)", "order": "2264"}, + {"emoji": "🇫🇴", "name": "flag: Faroe Islands", "shortname": ":flag-fo:", "unicode": "1f1eb-1f1f4", "html": "🇫🇴", "category": "Flags (country-flag)", "order": "2265"}, + {"emoji": "🇫🇷", "name": "flag: France", "shortname": ":flag-fr:", "unicode": "1f1eb-1f1f7", "html": "🇫🇷", "category": "Flags (country-flag)", "order": "2266"}, + {"emoji": "🇬🇦", "name": "flag: Gabon", "shortname": ":flag-ga:", "unicode": "1f1ec-1f1e6", "html": "🇬🇦", "category": "Flags (country-flag)", "order": "2267"}, + {"emoji": "🇬🇧", "name": "flag: United Kingdom", "shortname": ":flag-gb:", "unicode": "1f1ec-1f1e7", "html": "🇬🇧", "category": "Flags (country-flag)", "order": "2268"}, + {"emoji": "🇬🇩", "name": "flag: Grenada", "shortname": ":flag-gd:", "unicode": "1f1ec-1f1e9", "html": "🇬🇩", "category": "Flags (country-flag)", "order": "2269"}, + {"emoji": "🇬🇪", "name": "flag: Georgia", "shortname": ":flag-ge:", "unicode": "1f1ec-1f1ea", "html": "🇬🇪", "category": "Flags (country-flag)", "order": "2270"}, + {"emoji": "🇬🇫", "name": "flag: French Guiana", "shortname": ":flag-gf:", "unicode": "1f1ec-1f1eb", "html": "🇬🇫", "category": "Flags (country-flag)", "order": "2271"}, + {"emoji": "🇬🇬", "name": "flag: Guernsey", "shortname": ":flag-gg:", "unicode": "1f1ec-1f1ec", "html": "🇬🇬", "category": "Flags (country-flag)", "order": "2272"}, + {"emoji": "🇬🇭", "name": "flag: Ghana", "shortname": ":flag-gh:", "unicode": "1f1ec-1f1ed", "html": "🇬🇭", "category": "Flags (country-flag)", "order": "2273"}, + {"emoji": "🇬🇮", "name": "flag: Gibraltar", "shortname": ":flag-gi:", "unicode": "1f1ec-1f1ee", "html": "🇬🇮", "category": "Flags (country-flag)", "order": "2274"}, + {"emoji": "🇬🇱", "name": "flag: Greenland", "shortname": ":flag-gl:", "unicode": "1f1ec-1f1f1", "html": "🇬🇱", "category": "Flags (country-flag)", "order": "2275"}, + {"emoji": "🇬🇲", "name": "flag: Gambia", "shortname": ":flag-gm:", "unicode": "1f1ec-1f1f2", "html": "🇬🇲", "category": "Flags (country-flag)", "order": "2276"}, + {"emoji": "🇬🇳", "name": "flag: Guinea", "shortname": ":flag-gn:", "unicode": "1f1ec-1f1f3", "html": "🇬🇳", "category": "Flags (country-flag)", "order": "2277"}, + {"emoji": "🇬🇵", "name": "flag: Guadeloupe", "shortname": ":flag-gp:", "unicode": "1f1ec-1f1f5", "html": "🇬🇵", "category": "Flags (country-flag)", "order": "2278"}, + {"emoji": "🇬🇶", "name": "flag: Equatorial Guinea", "shortname": ":flag-gq:", "unicode": "1f1ec-1f1f6", "html": "🇬🇶", "category": "Flags (country-flag)", "order": "2279"}, + {"emoji": "🇬🇷", "name": "flag: Greece", "shortname": ":flag-gr:", "unicode": "1f1ec-1f1f7", "html": "🇬🇷", "category": "Flags (country-flag)", "order": "2280"}, + {"emoji": "🇬🇸", "name": "flag: South Georgia & South Sandwich Islands", "shortname": ":flag-gs:", "unicode": "1f1ec-1f1f8", "html": "🇬🇸", "category": "Flags (country-flag)", "order": "2281"}, + {"emoji": "🇬🇹", "name": "flag: Guatemala", "shortname": ":flag-gt:", "unicode": "1f1ec-1f1f9", "html": "🇬🇹", "category": "Flags (country-flag)", "order": "2282"}, + {"emoji": "🇬🇺", "name": "flag: Guam", "shortname": ":flag-gu:", "unicode": "1f1ec-1f1fa", "html": "🇬🇺", "category": "Flags (country-flag)", "order": "2283"}, + {"emoji": "🇬🇼", "name": "flag: Guinea-Bissau", "shortname": ":flag-gw:", "unicode": "1f1ec-1f1fc", "html": "🇬🇼", "category": "Flags (country-flag)", "order": "2284"}, + {"emoji": "🇬🇾", "name": "flag: Guyana", "shortname": ":flag-gy:", "unicode": "1f1ec-1f1fe", "html": "🇬🇾", "category": "Flags (country-flag)", "order": "2285"}, + {"emoji": "🇭🇰", "name": "flag: Hong Kong SAR China", "shortname": ":flag-hk:", "unicode": "1f1ed-1f1f0", "html": "🇭🇰", "category": "Flags (country-flag)", "order": "2286"}, + {"emoji": "🇭🇲", "name": "flag: Heard & McDonald Islands", "shortname": ":flag-hm:", "unicode": "1f1ed-1f1f2", "html": "🇭🇲", "category": "Flags (country-flag)", "order": "2287"}, + {"emoji": "🇭🇳", "name": "flag: Honduras", "shortname": ":flag-hn:", "unicode": "1f1ed-1f1f3", "html": "🇭🇳", "category": "Flags (country-flag)", "order": "2288"}, + {"emoji": "🇭🇷", "name": "flag: Croatia", "shortname": ":flag-hr:", "unicode": "1f1ed-1f1f7", "html": "🇭🇷", "category": "Flags (country-flag)", "order": "2289"}, + {"emoji": "🇭🇹", "name": "flag: Haiti", "shortname": ":flag-ht:", "unicode": "1f1ed-1f1f9", "html": "🇭🇹", "category": "Flags (country-flag)", "order": "2290"}, + {"emoji": "🇭🇺", "name": "flag: Hungary", "shortname": ":flag-hu:", "unicode": "1f1ed-1f1fa", "html": "🇭🇺", "category": "Flags (country-flag)", "order": "2291"}, + {"emoji": "🇮🇨", "name": "flag: Canary Islands", "shortname": ":flag-ic:", "unicode": "1f1ee-1f1e8", "html": "🇮🇨", "category": "Flags (country-flag)", "order": "2292"}, + {"emoji": "🇮🇩", "name": "flag: Indonesia", "shortname": ":flag-id:", "unicode": "1f1ee-1f1e9", "html": "🇮🇩", "category": "Flags (country-flag)", "order": "2293"}, + {"emoji": "🇮🇪", "name": "flag: Ireland", "shortname": ":flag-ie:", "unicode": "1f1ee-1f1ea", "html": "🇮🇪", "category": "Flags (country-flag)", "order": "2294"}, + {"emoji": "🇮🇱", "name": "flag: Israel", "shortname": ":flag-il:", "unicode": "1f1ee-1f1f1", "html": "🇮🇱", "category": "Flags (country-flag)", "order": "2295"}, + {"emoji": "🇮🇲", "name": "flag: Isle of Man", "shortname": ":flag-im:", "unicode": "1f1ee-1f1f2", "html": "🇮🇲", "category": "Flags (country-flag)", "order": "2296"}, + {"emoji": "🇮🇳", "name": "flag: India", "shortname": ":flag-in:", "unicode": "1f1ee-1f1f3", "html": "🇮🇳", "category": "Flags (country-flag)", "order": "2297"}, + {"emoji": "🇮🇴", "name": "flag: British Indian Ocean Territory", "shortname": ":flag-io:", "unicode": "1f1ee-1f1f4", "html": "🇮🇴", "category": "Flags (country-flag)", "order": "2298"}, + {"emoji": "🇮🇶", "name": "flag: Iraq", "shortname": ":flag-iq:", "unicode": "1f1ee-1f1f6", "html": "🇮🇶", "category": "Flags (country-flag)", "order": "2299"}, + {"emoji": "🇮🇷", "name": "flag: Iran", "shortname": ":flag-ir:", "unicode": "1f1ee-1f1f7", "html": "🇮🇷", "category": "Flags (country-flag)", "order": "2300"}, + {"emoji": "🇮🇸", "name": "flag: Iceland", "shortname": ":flag-is:", "unicode": "1f1ee-1f1f8", "html": "🇮🇸", "category": "Flags (country-flag)", "order": "2301"}, + {"emoji": "🇮🇹", "name": "flag: Italy", "shortname": ":flag-it:", "unicode": "1f1ee-1f1f9", "html": "🇮🇹", "category": "Flags (country-flag)", "order": "2302"}, + {"emoji": "🇯🇪", "name": "flag: Jersey", "shortname": ":flag-je:", "unicode": "1f1ef-1f1ea", "html": "🇯🇪", "category": "Flags (country-flag)", "order": "2303"}, + {"emoji": "🇯🇲", "name": "flag: Jamaica", "shortname": ":flag-jm:", "unicode": "1f1ef-1f1f2", "html": "🇯🇲", "category": "Flags (country-flag)", "order": "2304"}, + {"emoji": "🇯🇴", "name": "flag: Jordan", "shortname": ":flag-jo:", "unicode": "1f1ef-1f1f4", "html": "🇯🇴", "category": "Flags (country-flag)", "order": "2305"}, + {"emoji": "🇯🇵", "name": "flag: Japan", "shortname": ":flag-jp:", "unicode": "1f1ef-1f1f5", "html": "🇯🇵", "category": "Flags (country-flag)", "order": "2306"}, + {"emoji": "🇰🇪", "name": "flag: Kenya", "shortname": ":flag-ke:", "unicode": "1f1f0-1f1ea", "html": "🇰🇪", "category": "Flags (country-flag)", "order": "2307"}, + {"emoji": "🇰🇬", "name": "flag: Kyrgyzstan", "shortname": ":flag-kg:", "unicode": "1f1f0-1f1ec", "html": "🇰🇬", "category": "Flags (country-flag)", "order": "2308"}, + {"emoji": "🇰🇭", "name": "flag: Cambodia", "shortname": ":flag-kh:", "unicode": "1f1f0-1f1ed", "html": "🇰🇭", "category": "Flags (country-flag)", "order": "2309"}, + {"emoji": "🇰🇮", "name": "flag: Kiribati", "shortname": ":flag-ki:", "unicode": "1f1f0-1f1ee", "html": "🇰🇮", "category": "Flags (country-flag)", "order": "2310"}, + {"emoji": "🇰🇲", "name": "flag: Comoros", "shortname": ":flag-km:", "unicode": "1f1f0-1f1f2", "html": "🇰🇲", "category": "Flags (country-flag)", "order": "2311"}, + {"emoji": "🇰🇳", "name": "flag: St. Kitts & Nevis", "shortname": ":flag-kn:", "unicode": "1f1f0-1f1f3", "html": "🇰🇳", "category": "Flags (country-flag)", "order": "2312"}, + {"emoji": "🇰🇵", "name": "flag: North Korea", "shortname": ":flag-kp:", "unicode": "1f1f0-1f1f5", "html": "🇰🇵", "category": "Flags (country-flag)", "order": "2313"}, + {"emoji": "🇰🇷", "name": "flag: South Korea", "shortname": ":flag-kr:", "unicode": "1f1f0-1f1f7", "html": "🇰🇷", "category": "Flags (country-flag)", "order": "2314"}, + {"emoji": "🇰🇼", "name": "flag: Kuwait", "shortname": ":flag-kw:", "unicode": "1f1f0-1f1fc", "html": "🇰🇼", "category": "Flags (country-flag)", "order": "2315"}, + {"emoji": "🇰🇾", "name": "flag: Cayman Islands", "shortname": ":flag-ky:", "unicode": "1f1f0-1f1fe", "html": "🇰🇾", "category": "Flags (country-flag)", "order": "2316"}, + {"emoji": "🇰🇿", "name": "flag: Kazakhstan", "shortname": ":flag-kz:", "unicode": "1f1f0-1f1ff", "html": "🇰🇿", "category": "Flags (country-flag)", "order": "2317"}, + {"emoji": "🇱🇦", "name": "flag: Laos", "shortname": ":flag-la:", "unicode": "1f1f1-1f1e6", "html": "🇱🇦", "category": "Flags (country-flag)", "order": "2318"}, + {"emoji": "🇱🇧", "name": "flag: Lebanon", "shortname": ":flag-lb:", "unicode": "1f1f1-1f1e7", "html": "🇱🇧", "category": "Flags (country-flag)", "order": "2319"}, + {"emoji": "🇱🇨", "name": "flag: St. Lucia", "shortname": ":flag-lc:", "unicode": "1f1f1-1f1e8", "html": "🇱🇨", "category": "Flags (country-flag)", "order": "2320"}, + {"emoji": "🇱🇮", "name": "flag: Liechtenstein", "shortname": ":flag-li:", "unicode": "1f1f1-1f1ee", "html": "🇱🇮", "category": "Flags (country-flag)", "order": "2321"}, + {"emoji": "🇱🇰", "name": "flag: Sri Lanka", "shortname": ":flag-lk:", "unicode": "1f1f1-1f1f0", "html": "🇱🇰", "category": "Flags (country-flag)", "order": "2322"}, + {"emoji": "🇱🇷", "name": "flag: Liberia", "shortname": ":flag-lr:", "unicode": "1f1f1-1f1f7", "html": "🇱🇷", "category": "Flags (country-flag)", "order": "2323"}, + {"emoji": "🇱🇸", "name": "flag: Lesotho", "shortname": ":flag-ls:", "unicode": "1f1f1-1f1f8", "html": "🇱🇸", "category": "Flags (country-flag)", "order": "2324"}, + {"emoji": "🇱🇹", "name": "flag: Lithuania", "shortname": ":flag-lt:", "unicode": "1f1f1-1f1f9", "html": "🇱🇹", "category": "Flags (country-flag)", "order": "2325"}, + {"emoji": "🇱🇺", "name": "flag: Luxembourg", "shortname": ":flag-lu:", "unicode": "1f1f1-1f1fa", "html": "🇱🇺", "category": "Flags (country-flag)", "order": "2326"}, + {"emoji": "🇱🇻", "name": "flag: Latvia", "shortname": ":flag-lv:", "unicode": "1f1f1-1f1fb", "html": "🇱🇻", "category": "Flags (country-flag)", "order": "2327"}, + {"emoji": "🇱🇾", "name": "flag: Libya", "shortname": ":flag-ly:", "unicode": "1f1f1-1f1fe", "html": "🇱🇾", "category": "Flags (country-flag)", "order": "2328"}, + {"emoji": "🇲🇦", "name": "flag: Morocco", "shortname": ":flag-ma:", "unicode": "1f1f2-1f1e6", "html": "🇲🇦", "category": "Flags (country-flag)", "order": "2329"}, + {"emoji": "🇲🇨", "name": "flag: Monaco", "shortname": ":flag-mc:", "unicode": "1f1f2-1f1e8", "html": "🇲🇨", "category": "Flags (country-flag)", "order": "2330"}, + {"emoji": "🇲🇩", "name": "flag: Moldova", "shortname": ":flag-md:", "unicode": "1f1f2-1f1e9", "html": "🇲🇩", "category": "Flags (country-flag)", "order": "2331"}, + {"emoji": "🇲🇪", "name": "flag: Montenegro", "shortname": ":flag-me:", "unicode": "1f1f2-1f1ea", "html": "🇲🇪", "category": "Flags (country-flag)", "order": "2332"}, + {"emoji": "🇲🇫", "name": "flag: St. Martin", "shortname": ":flag-mf:", "unicode": "1f1f2-1f1eb", "html": "🇲🇫", "category": "Flags (country-flag)", "order": "2333"}, + {"emoji": "🇲🇬", "name": "flag: Madagascar", "shortname": ":flag-mg:", "unicode": "1f1f2-1f1ec", "html": "🇲🇬", "category": "Flags (country-flag)", "order": "2334"}, + {"emoji": "🇲🇭", "name": "flag: Marshall Islands", "shortname": ":flag-mh:", "unicode": "1f1f2-1f1ed", "html": "🇲🇭", "category": "Flags (country-flag)", "order": "2335"}, + {"emoji": "🇲🇰", "name": "flag: North Macedonia", "shortname": ":flag-mk:", "unicode": "1f1f2-1f1f0", "html": "🇲🇰", "category": "Flags (country-flag)", "order": "2336"}, + {"emoji": "🇲🇱", "name": "flag: Mali", "shortname": ":flag-ml:", "unicode": "1f1f2-1f1f1", "html": "🇲🇱", "category": "Flags (country-flag)", "order": "2337"}, + {"emoji": "🇲🇲", "name": "flag: Myanmar (Burma)", "shortname": ":flag-mm:", "unicode": "1f1f2-1f1f2", "html": "🇲🇲", "category": "Flags (country-flag)", "order": "2338"}, + {"emoji": "🇲🇳", "name": "flag: Mongolia", "shortname": ":flag-mn:", "unicode": "1f1f2-1f1f3", "html": "🇲🇳", "category": "Flags (country-flag)", "order": "2339"}, + {"emoji": "🇲🇴", "name": "flag: Macao SAR China", "shortname": ":flag-mo:", "unicode": "1f1f2-1f1f4", "html": "🇲🇴", "category": "Flags (country-flag)", "order": "2340"}, + {"emoji": "🇲🇵", "name": "flag: Northern Mariana Islands", "shortname": ":flag-mp:", "unicode": "1f1f2-1f1f5", "html": "🇲🇵", "category": "Flags (country-flag)", "order": "2341"}, + {"emoji": "🇲🇶", "name": "flag: Martinique", "shortname": ":flag-mq:", "unicode": "1f1f2-1f1f6", "html": "🇲🇶", "category": "Flags (country-flag)", "order": "2342"}, + {"emoji": "🇲🇷", "name": "flag: Mauritania", "shortname": ":flag-mr:", "unicode": "1f1f2-1f1f7", "html": "🇲🇷", "category": "Flags (country-flag)", "order": "2343"}, + {"emoji": "🇲🇸", "name": "flag: Montserrat", "shortname": ":flag-ms:", "unicode": "1f1f2-1f1f8", "html": "🇲🇸", "category": "Flags (country-flag)", "order": "2344"}, + {"emoji": "🇲🇹", "name": "flag: Malta", "shortname": ":flag-mt:", "unicode": "1f1f2-1f1f9", "html": "🇲🇹", "category": "Flags (country-flag)", "order": "2345"}, + {"emoji": "🇲🇺", "name": "flag: Mauritius", "shortname": ":flag-mu:", "unicode": "1f1f2-1f1fa", "html": "🇲🇺", "category": "Flags (country-flag)", "order": "2346"}, + {"emoji": "🇲🇻", "name": "flag: Maldives", "shortname": ":flag-mv:", "unicode": "1f1f2-1f1fb", "html": "🇲🇻", "category": "Flags (country-flag)", "order": "2347"}, + {"emoji": "🇲🇼", "name": "flag: Malawi", "shortname": ":flag-mw:", "unicode": "1f1f2-1f1fc", "html": "🇲🇼", "category": "Flags (country-flag)", "order": "2348"}, + {"emoji": "🇲🇽", "name": "flag: Mexico", "shortname": ":flag-mx:", "unicode": "1f1f2-1f1fd", "html": "🇲🇽", "category": "Flags (country-flag)", "order": "2349"}, + {"emoji": "🇲🇾", "name": "flag: Malaysia", "shortname": ":flag-my:", "unicode": "1f1f2-1f1fe", "html": "🇲🇾", "category": "Flags (country-flag)", "order": "2350"}, + {"emoji": "🇲🇿", "name": "flag: Mozambique", "shortname": ":flag-mz:", "unicode": "1f1f2-1f1ff", "html": "🇲🇿", "category": "Flags (country-flag)", "order": "2351"}, + {"emoji": "🇳🇦", "name": "flag: Namibia", "shortname": ":flag-na:", "unicode": "1f1f3-1f1e6", "html": "🇳🇦", "category": "Flags (country-flag)", "order": "2352"}, + {"emoji": "🇳🇨", "name": "flag: New Caledonia", "shortname": ":flag-nc:", "unicode": "1f1f3-1f1e8", "html": "🇳🇨", "category": "Flags (country-flag)", "order": "2353"}, + {"emoji": "🇳🇪", "name": "flag: Niger", "shortname": ":flag-ne:", "unicode": "1f1f3-1f1ea", "html": "🇳🇪", "category": "Flags (country-flag)", "order": "2354"}, + {"emoji": "🇳🇫", "name": "flag: Norfolk Island", "shortname": ":flag-nf:", "unicode": "1f1f3-1f1eb", "html": "🇳🇫", "category": "Flags (country-flag)", "order": "2355"}, + {"emoji": "🇳🇬", "name": "flag: Nigeria", "shortname": ":flag-ng:", "unicode": "1f1f3-1f1ec", "html": "🇳🇬", "category": "Flags (country-flag)", "order": "2356"}, + {"emoji": "🇳🇮", "name": "flag: Nicaragua", "shortname": ":flag-ni:", "unicode": "1f1f3-1f1ee", "html": "🇳🇮", "category": "Flags (country-flag)", "order": "2357"}, + {"emoji": "🇳🇱", "name": "flag: Netherlands", "shortname": ":flag-nl:", "unicode": "1f1f3-1f1f1", "html": "🇳🇱", "category": "Flags (country-flag)", "order": "2358"}, + {"emoji": "🇳🇴", "name": "flag: Norway", "shortname": ":flag-no:", "unicode": "1f1f3-1f1f4", "html": "🇳🇴", "category": "Flags (country-flag)", "order": "2359"}, + {"emoji": "🇳🇵", "name": "flag: Nepal", "shortname": ":flag-np:", "unicode": "1f1f3-1f1f5", "html": "🇳🇵", "category": "Flags (country-flag)", "order": "2360"}, + {"emoji": "🇳🇷", "name": "flag: Nauru", "shortname": ":flag-nr:", "unicode": "1f1f3-1f1f7", "html": "🇳🇷", "category": "Flags (country-flag)", "order": "2361"}, + {"emoji": "🇳🇺", "name": "flag: Niue", "shortname": ":flag-nu:", "unicode": "1f1f3-1f1fa", "html": "🇳🇺", "category": "Flags (country-flag)", "order": "2362"}, + {"emoji": "🇳🇿", "name": "flag: New Zealand", "shortname": ":flag-nz:", "unicode": "1f1f3-1f1ff", "html": "🇳🇿", "category": "Flags (country-flag)", "order": "2363"}, + {"emoji": "🇴🇲", "name": "flag: Oman", "shortname": ":flag-om:", "unicode": "1f1f4-1f1f2", "html": "🇴🇲", "category": "Flags (country-flag)", "order": "2364"}, + {"emoji": "🇵🇦", "name": "flag: Panama", "shortname": ":flag-pa:", "unicode": "1f1f5-1f1e6", "html": "🇵🇦", "category": "Flags (country-flag)", "order": "2365"}, + {"emoji": "🇵🇪", "name": "flag: Peru", "shortname": ":flag-pe:", "unicode": "1f1f5-1f1ea", "html": "🇵🇪", "category": "Flags (country-flag)", "order": "2366"}, + {"emoji": "🇵🇫", "name": "flag: French Polynesia", "shortname": ":flag-pf:", "unicode": "1f1f5-1f1eb", "html": "🇵🇫", "category": "Flags (country-flag)", "order": "2367"}, + {"emoji": "🇵🇬", "name": "flag: Papua New Guinea", "shortname": ":flag-pg:", "unicode": "1f1f5-1f1ec", "html": "🇵🇬", "category": "Flags (country-flag)", "order": "2368"}, + {"emoji": "🇵🇭", "name": "flag: Philippines", "shortname": ":flag-ph:", "unicode": "1f1f5-1f1ed", "html": "🇵🇭", "category": "Flags (country-flag)", "order": "2369"}, + {"emoji": "🇵🇰", "name": "flag: Pakistan", "shortname": ":flag-pk:", "unicode": "1f1f5-1f1f0", "html": "🇵🇰", "category": "Flags (country-flag)", "order": "2370"}, + {"emoji": "🇵🇱", "name": "flag: Poland", "shortname": ":flag-pl:", "unicode": "1f1f5-1f1f1", "html": "🇵🇱", "category": "Flags (country-flag)", "order": "2371"}, + {"emoji": "🇵🇲", "name": "flag: St. Pierre & Miquelon", "shortname": ":flag-pm:", "unicode": "1f1f5-1f1f2", "html": "🇵🇲", "category": "Flags (country-flag)", "order": "2372"}, + {"emoji": "🇵🇳", "name": "flag: Pitcairn Islands", "shortname": ":flag-pn:", "unicode": "1f1f5-1f1f3", "html": "🇵🇳", "category": "Flags (country-flag)", "order": "2373"}, + {"emoji": "🇵🇷", "name": "flag: Puerto Rico", "shortname": ":flag-pr:", "unicode": "1f1f5-1f1f7", "html": "🇵🇷", "category": "Flags (country-flag)", "order": "2374"}, + {"emoji": "🇵🇸", "name": "flag: Palestinian Territories", "shortname": ":flag-ps:", "unicode": "1f1f5-1f1f8", "html": "🇵🇸", "category": "Flags (country-flag)", "order": "2375"}, + {"emoji": "🇵🇹", "name": "flag: Portugal", "shortname": ":flag-pt:", "unicode": "1f1f5-1f1f9", "html": "🇵🇹", "category": "Flags (country-flag)", "order": "2376"}, + {"emoji": "🇵🇼", "name": "flag: Palau", "shortname": ":flag-pw:", "unicode": "1f1f5-1f1fc", "html": "🇵🇼", "category": "Flags (country-flag)", "order": "2377"}, + {"emoji": "🇵🇾", "name": "flag: Paraguay", "shortname": ":flag-py:", "unicode": "1f1f5-1f1fe", "html": "🇵🇾", "category": "Flags (country-flag)", "order": "2378"}, + {"emoji": "🇶🇦", "name": "flag: Qatar", "shortname": ":flag-qa:", "unicode": "1f1f6-1f1e6", "html": "🇶🇦", "category": "Flags (country-flag)", "order": "2379"}, + {"emoji": "🇷🇪", "name": "flag: Réunion", "shortname": ":flag-re:", "unicode": "1f1f7-1f1ea", "html": "🇷🇪", "category": "Flags (country-flag)", "order": "2380"}, + {"emoji": "🇷🇴", "name": "flag: Romania", "shortname": ":flag-ro:", "unicode": "1f1f7-1f1f4", "html": "🇷🇴", "category": "Flags (country-flag)", "order": "2381"}, + {"emoji": "🇷🇸", "name": "flag: Serbia", "shortname": ":flag-rs:", "unicode": "1f1f7-1f1f8", "html": "🇷🇸", "category": "Flags (country-flag)", "order": "2382"}, + {"emoji": "🇷🇺", "name": "flag: Russia", "shortname": ":flag-ru:", "unicode": "1f1f7-1f1fa", "html": "🇷🇺", "category": "Flags (country-flag)", "order": "2383"}, + {"emoji": "🇷🇼", "name": "flag: Rwanda", "shortname": ":flag-rw:", "unicode": "1f1f7-1f1fc", "html": "🇷🇼", "category": "Flags (country-flag)", "order": "2384"}, + {"emoji": "🇸🇦", "name": "flag: Saudi Arabia", "shortname": ":flag-sa:", "unicode": "1f1f8-1f1e6", "html": "🇸🇦", "category": "Flags (country-flag)", "order": "2385"}, + {"emoji": "🇸🇧", "name": "flag: Solomon Islands", "shortname": ":flag-sb:", "unicode": "1f1f8-1f1e7", "html": "🇸🇧", "category": "Flags (country-flag)", "order": "2386"}, + {"emoji": "🇸🇨", "name": "flag: Seychelles", "shortname": ":flag-sc:", "unicode": "1f1f8-1f1e8", "html": "🇸🇨", "category": "Flags (country-flag)", "order": "2387"}, + {"emoji": "🇸🇩", "name": "flag: Sudan", "shortname": ":flag-sd:", "unicode": "1f1f8-1f1e9", "html": "🇸🇩", "category": "Flags (country-flag)", "order": "2388"}, + {"emoji": "🇸🇪", "name": "flag: Sweden", "shortname": ":flag-se:", "unicode": "1f1f8-1f1ea", "html": "🇸🇪", "category": "Flags (country-flag)", "order": "2389"}, + {"emoji": "🇸🇬", "name": "flag: Singapore", "shortname": ":flag-sg:", "unicode": "1f1f8-1f1ec", "html": "🇸🇬", "category": "Flags (country-flag)", "order": "2390"}, + {"emoji": "🇸🇭", "name": "flag: St. Helena", "shortname": ":flag-sh:", "unicode": "1f1f8-1f1ed", "html": "🇸🇭", "category": "Flags (country-flag)", "order": "2391"}, + {"emoji": "🇸🇮", "name": "flag: Slovenia", "shortname": ":flag-si:", "unicode": "1f1f8-1f1ee", "html": "🇸🇮", "category": "Flags (country-flag)", "order": "2392"}, + {"emoji": "🇸🇯", "name": "flag: Svalbard & Jan Mayen", "shortname": ":flag-sj:", "unicode": "1f1f8-1f1ef", "html": "🇸🇯", "category": "Flags (country-flag)", "order": "2393"}, + {"emoji": "🇸🇰", "name": "flag: Slovakia", "shortname": ":flag-sk:", "unicode": "1f1f8-1f1f0", "html": "🇸🇰", "category": "Flags (country-flag)", "order": "2394"}, + {"emoji": "🇸🇱", "name": "flag: Sierra Leone", "shortname": ":flag-sl:", "unicode": "1f1f8-1f1f1", "html": "🇸🇱", "category": "Flags (country-flag)", "order": "2395"}, + {"emoji": "🇸🇲", "name": "flag: San Marino", "shortname": ":flag-sm:", "unicode": "1f1f8-1f1f2", "html": "🇸🇲", "category": "Flags (country-flag)", "order": "2396"}, + {"emoji": "🇸🇳", "name": "flag: Senegal", "shortname": ":flag-sn:", "unicode": "1f1f8-1f1f3", "html": "🇸🇳", "category": "Flags (country-flag)", "order": "2397"}, + {"emoji": "🇸🇴", "name": "flag: Somalia", "shortname": ":flag-so:", "unicode": "1f1f8-1f1f4", "html": "🇸🇴", "category": "Flags (country-flag)", "order": "2398"}, + {"emoji": "🇸🇷", "name": "flag: Suriname", "shortname": ":flag-sr:", "unicode": "1f1f8-1f1f7", "html": "🇸🇷", "category": "Flags (country-flag)", "order": "2399"}, + {"emoji": "🇸🇸", "name": "flag: South Sudan", "shortname": ":flag-ss:", "unicode": "1f1f8-1f1f8", "html": "🇸🇸", "category": "Flags (country-flag)", "order": "2400"}, + {"emoji": "🇸🇹", "name": "flag: São Tomé & Príncipe", "shortname": ":flag-st:", "unicode": "1f1f8-1f1f9", "html": "🇸🇹", "category": "Flags (country-flag)", "order": "2401"}, + {"emoji": "🇸🇻", "name": "flag: El Salvador", "shortname": ":flag-sv:", "unicode": "1f1f8-1f1fb", "html": "🇸🇻", "category": "Flags (country-flag)", "order": "2402"}, + {"emoji": "🇸🇽", "name": "flag: Sint Maarten", "shortname": ":flag-sx:", "unicode": "1f1f8-1f1fd", "html": "🇸🇽", "category": "Flags (country-flag)", "order": "2403"}, + {"emoji": "🇸🇾", "name": "flag: Syria", "shortname": ":flag-sy:", "unicode": "1f1f8-1f1fe", "html": "🇸🇾", "category": "Flags (country-flag)", "order": "2404"}, + {"emoji": "🇸🇿", "name": "flag: Eswatini", "shortname": ":flag-sz:", "unicode": "1f1f8-1f1ff", "html": "🇸🇿", "category": "Flags (country-flag)", "order": "2405"}, + {"emoji": "🇹🇦", "name": "flag: Tristan da Cunha", "shortname": ":flag-ta:", "unicode": "1f1f9-1f1e6", "html": "🇹🇦", "category": "Flags (country-flag)", "order": "2406"}, + {"emoji": "🇹🇨", "name": "flag: Turks & Caicos Islands", "shortname": ":flag-tc:", "unicode": "1f1f9-1f1e8", "html": "🇹🇨", "category": "Flags (country-flag)", "order": "2407"}, + {"emoji": "🇹🇩", "name": "flag: Chad", "shortname": ":flag-td:", "unicode": "1f1f9-1f1e9", "html": "🇹🇩", "category": "Flags (country-flag)", "order": "2408"}, + {"emoji": "🇹🇫", "name": "flag: French Southern Territories", "shortname": ":flag-tf:", "unicode": "1f1f9-1f1eb", "html": "🇹🇫", "category": "Flags (country-flag)", "order": "2409"}, + {"emoji": "🇹🇬", "name": "flag: Togo", "shortname": ":flag-tg:", "unicode": "1f1f9-1f1ec", "html": "🇹🇬", "category": "Flags (country-flag)", "order": "2410"}, + {"emoji": "🇹🇭", "name": "flag: Thailand", "shortname": ":flag-th:", "unicode": "1f1f9-1f1ed", "html": "🇹🇭", "category": "Flags (country-flag)", "order": "2411"}, + {"emoji": "🇹🇯", "name": "flag: Tajikistan", "shortname": ":flag-tj:", "unicode": "1f1f9-1f1ef", "html": "🇹🇯", "category": "Flags (country-flag)", "order": "2412"}, + {"emoji": "🇹🇰", "name": "flag: Tokelau", "shortname": ":flag-tk:", "unicode": "1f1f9-1f1f0", "html": "🇹🇰", "category": "Flags (country-flag)", "order": "2413"}, + {"emoji": "🇹🇱", "name": "flag: Timor-Leste", "shortname": ":flag-tl:", "unicode": "1f1f9-1f1f1", "html": "🇹🇱", "category": "Flags (country-flag)", "order": "2414"}, + {"emoji": "🇹🇲", "name": "flag: Turkmenistan", "shortname": ":flag-tm:", "unicode": "1f1f9-1f1f2", "html": "🇹🇲", "category": "Flags (country-flag)", "order": "2415"}, + {"emoji": "🇹🇳", "name": "flag: Tunisia", "shortname": ":flag-tn:", "unicode": "1f1f9-1f1f3", "html": "🇹🇳", "category": "Flags (country-flag)", "order": "2416"}, + {"emoji": "🇹🇴", "name": "flag: Tonga", "shortname": ":flag-to:", "unicode": "1f1f9-1f1f4", "html": "🇹🇴", "category": "Flags (country-flag)", "order": "2417"}, + {"emoji": "🇹🇷", "name": "flag: Turkey", "shortname": ":flag-tr:", "unicode": "1f1f9-1f1f7", "html": "🇹🇷", "category": "Flags (country-flag)", "order": "2418"}, + {"emoji": "🇹🇹", "name": "flag: Trinidad & Tobago", "shortname": ":flag-tt:", "unicode": "1f1f9-1f1f9", "html": "🇹🇹", "category": "Flags (country-flag)", "order": "2419"}, + {"emoji": "🇹🇻", "name": "flag: Tuvalu", "shortname": ":flag-tv:", "unicode": "1f1f9-1f1fb", "html": "🇹🇻", "category": "Flags (country-flag)", "order": "2420"}, + {"emoji": "🇹🇼", "name": "flag: Taiwan", "shortname": ":flag-tw:", "unicode": "1f1f9-1f1fc", "html": "🇹🇼", "category": "Flags (country-flag)", "order": "2421"}, + {"emoji": "🇹🇿", "name": "flag: Tanzania", "shortname": ":flag-tz:", "unicode": "1f1f9-1f1ff", "html": "🇹🇿", "category": "Flags (country-flag)", "order": "2422"}, + {"emoji": "🇺🇦", "name": "flag: Ukraine", "shortname": ":flag-ua:", "unicode": "1f1fa-1f1e6", "html": "🇺🇦", "category": "Flags (country-flag)", "order": "2423"}, + {"emoji": "🇺🇬", "name": "flag: Uganda", "shortname": ":flag-ug:", "unicode": "1f1fa-1f1ec", "html": "🇺🇬", "category": "Flags (country-flag)", "order": "2424"}, + {"emoji": "🇺🇲", "name": "flag: U.S. Outlying Islands", "shortname": ":flag-um:", "unicode": "1f1fa-1f1f2", "html": "🇺🇲", "category": "Flags (country-flag)", "order": "2425"}, + {"emoji": "🇺🇸", "name": "flag: United States", "shortname": ":flag-us:", "unicode": "1f1fa-1f1f8", "html": "🇺🇸", "category": "Flags (country-flag)", "order": "2427"}, + {"emoji": "🇺🇾", "name": "flag: Uruguay", "shortname": ":flag-uy:", "unicode": "1f1fa-1f1fe", "html": "🇺🇾", "category": "Flags (country-flag)", "order": "2428"}, + {"emoji": "🇺🇿", "name": "flag: Uzbekistan", "shortname": ":flag-uz:", "unicode": "1f1fa-1f1ff", "html": "🇺🇿", "category": "Flags (country-flag)", "order": "2429"}, + {"emoji": "🇻🇦", "name": "flag: Vatican City", "shortname": ":flag-va:", "unicode": "1f1fb-1f1e6", "html": "🇻🇦", "category": "Flags (country-flag)", "order": "2430"}, + {"emoji": "🇻🇨", "name": "flag: St. Vincent & Grenadines", "shortname": ":flag-vc:", "unicode": "1f1fb-1f1e8", "html": "🇻🇨", "category": "Flags (country-flag)", "order": "2431"}, + {"emoji": "🇻🇪", "name": "flag: Venezuela", "shortname": ":flag-ve:", "unicode": "1f1fb-1f1ea", "html": "🇻🇪", "category": "Flags (country-flag)", "order": "2432"}, + {"emoji": "🇻🇬", "name": "flag: British Virgin Islands", "shortname": ":flag-vg:", "unicode": "1f1fb-1f1ec", "html": "🇻🇬", "category": "Flags (country-flag)", "order": "2433"}, + {"emoji": "🇻🇮", "name": "flag: U.S. Virgin Islands", "shortname": ":flag-vi:", "unicode": "1f1fb-1f1ee", "html": "🇻🇮", "category": "Flags (country-flag)", "order": "2434"}, + {"emoji": "🇻🇳", "name": "flag: Vietnam", "shortname": ":flag-vn:", "unicode": "1f1fb-1f1f3", "html": "🇻🇳", "category": "Flags (country-flag)", "order": "2435"}, + {"emoji": "🇻🇺", "name": "flag: Vanuatu", "shortname": ":flag_vu:", "unicode": "1f1fb-1f1fa", "html": "🇻🇺", "category": "Flags (country-flag)", "order": "2436"}, + {"emoji": "🇼🇫", "name": "flag: Wallis & Futuna", "shortname": ":flag_wf:", "unicode": "1f1fc-1f1eb", "html": "🇼🇫", "category": "Flags (country-flag)", "order": "2437"}, + {"emoji": "🇼🇸", "name": "flag: Samoa", "shortname": ":flag_ws:", "unicode": "1f1fc-1f1f8", "html": "🇼🇸", "category": "Flags (country-flag)", "order": "2438"}, + {"emoji": "🇽🇰", "name": "flag: Kosovo", "shortname": ":flag_xk:", "unicode": "1f1fd-1f1f0", "html": "🇽🇰", "category": "Flags (country-flag)", "order": "2439"}, + {"emoji": "🇾🇪", "name": "flag: Yemen", "shortname": ":flag_ye:", "unicode": "1f1fe-1f1ea", "html": "🇾🇪", "category": "Flags (country-flag)", "order": "2440"}, + {"emoji": "🇾🇹", "name": "flag: Mayotte", "shortname": ":flag_yt:", "unicode": "1f1fe-1f1f9", "html": "🇾🇹", "category": "Flags (country-flag)", "order": "2441"}, + {"emoji": "🇿🇦", "name": "flag: South Africa", "shortname": ":flag_za:", "unicode": "1f1ff-1f1e6", "html": "🇿🇦", "category": "Flags (country-flag)", "order": "2442"}, + {"emoji": "🇿🇲", "name": "flag: Zambia", "shortname": ":flag_zm:", "unicode": "1f1ff-1f1f2", "html": "🇿🇲", "category": "Flags (country-flag)", "order": "2443"}, + {"emoji": "🇿🇼", "name": "flag: Zimbabwe", "shortname": ":flag_zw:", "unicode": "1f1ff-1f1fc", "html": "🇿🇼", "category": "Flags (country-flag)", "order": "2444"}, + {"emoji": "🖤", "name": "black heart", "shortname": ":black_heart:", "unicode": "1f5a4", "html": "🖤", "category": "Smileys & Emotion (emotion)", "order": "1296"}, + {"emoji": "🗨", "name": "left speech bubble", "shortname": ":speech_left:", "unicode": "1f5e8", "html": "🗨", "category": "Smileys & Emotion (emotion)", "order": "1310"}, + {"emoji": "🥚", "name": "egg", "shortname": ":egg:", "unicode": "1f95a", "html": "🥚", "category": "Food & Drink (food-prepared)", "order": "1489"}, + {"emoji": "🛑", "name": "stop sign", "shortname": ":octagonal_sign:", "unicode": "1f6d1", "html": "🛑", "category": "Travel & Places (transport-ground)", "order": "1641"}, + {"emoji": "♠", "name": "spade suit", "shortname": ":spades:", "unicode": "2660", "html": "♠", "category": "Activities (game)", "order": "1807"}, + {"emoji": "♥", "name": "heart suit", "shortname": ":hearts:", "unicode": "2665", "html": "♥", "category": "Activities (game)", "order": "1808"}, + {"emoji": "♦", "name": "diamond suit", "shortname": ":diamonds:", "unicode": "2666", "html": "♦", "category": "Activities (game)", "order": "1809"}, + {"emoji": "♣", "name": "club suit", "shortname": ":clubs:", "unicode": "2663", "html": "♣", "category": "Activities (game)", "order": "1810"}, + {"emoji": "🥁", "name": "drum", "shortname": ":drum:", "unicode": "1f941", "html": "🥁", "category": "Objects (musical-instrument)", "order": "1837"}, + {"emoji": "↔", "name": "left-right arrow", "shortname": ":left_right_arrow:", "unicode": "2194", "html": "↔", "category": "Symbols (arrow)", "order": "2011"}, + {"emoji": "©", "name": "copyright", "shortname": ":copyright:", "unicode": "00a9", "html": "©", "category": "Symbols (other-symbol)", "order": "2103"}, + {"emoji": "®", "name": "registered", "shortname": ":registered:", "unicode": "00ae", "html": "®", "category": "Symbols (other-symbol)", "order": "2104"}, + {"emoji": "™", "name": "trade mark", "shortname": ":tm:", "unicode": "2122", "html": "™", "category": "Symbols (other-symbol)", "order": "2105"}, + {"emoji": "0️⃣", "name": "keycap: 0", "shortname": ":0:", "unicode": "0030 FE0F 20E3", "html": "0", "category": "Symbols (keycap)", "order": ""}, + {"emoji": "1️⃣", "name": "keycap: 1", "shortname": ":1:", "unicode": "0031 FE0F 20E3", "html": "1", "category": "Symbols (keycap)", "order": ""}, + {"emoji": "2️⃣", "name": "keycap: 2", "shortname": ":2:", "unicode": "0032 FE0F 20E3", "html": "2", "category": "Symbols (keycap)", "order": ""}, + {"emoji": "3️⃣", "name": "keycap: 3", "shortname": ":3:", "unicode": "0033 FE0F 20E3", "html": "3", "category": "Symbols (keycap)", "order": ""}, + {"emoji": "4️⃣", "name": "keycap: 4", "shortname": ":4:", "unicode": "0034 FE0F 20E3", "html": "4", "category": "Symbols (keycap)", "order": ""}, + {"emoji": "5️⃣", "name": "keycap: 5", "shortname": ":5:", "unicode": "0035 FE0F 20E3", "html": "5", "category": "Symbols (keycap)", "order": ""}, + {"emoji": "6️⃣", "name": "keycap: 6", "shortname": ":6:", "unicode": "0036 FE0F 20E3", "html": "6", "category": "Symbols (keycap)", "order": ""}, + {"emoji": "7️⃣", "name": "keycap: 7", "shortname": ":7:", "unicode": "0037 FE0F 20E3", "html": "7", "category": "Symbols (keycap)", "order": ""}, + {"emoji": "8️⃣", "name": "keycap: 8", "shortname": ":8:", "unicode": "0038 FE0F 20E3", "html": "8", "category": "Symbols (keycap)", "order": ""}, + {"emoji": "9️⃣", "name": "keycap: 9", "shortname": ":9:", "unicode": "0039 FE0F 20E3", "html": "9", "category": "Symbols (keycap)", "order": ""}, + {"emoji": "#⃣", "name": "keycap: #", "shortname": ":hash:", "unicode": "0023-20e3", "html": "#⃣", "category": "Symbols (keycap)", "order": "2106"}, + {"emoji": "*⃣", "name": "keycap: #", "shortname": ":asterisk:", "unicode": "002a-20e3", "html": "*⃣", "category": "Symbols (keycap)", "order": "2107"}, + {"emoji": "0⃣", "name": "keycap: 0", "shortname": ":zero:", "unicode": "0030-20e3", "html": "0⃣", "category": "Symbols (keycap)", "order": "2108"}, + {"emoji": "1⃣", "name": "keycap: 1", "shortname": ":one:", "unicode": "0031-20e3", "html": "1⃣", "category": "Symbols (keycap)", "order": "2109"}, + {"emoji": "2⃣", "name": "keycap: 2", "shortname": ":two:", "unicode": "0032-20e3", "html": "2⃣", "category": "Symbols (keycap)", "order": "2110"}, + {"emoji": "3⃣", "name": "keycap: 3", "shortname": ":three:", "unicode": "0033-20e3", "html": "3⃣", "category": "Symbols (keycap)", "order": "2111"}, + {"emoji": "4⃣", "name": "keycap: 4", "shortname": ":four:", "unicode": "0034-20e3", "html": "4⃣", "category": "Symbols (keycap)", "order": "2112"}, + {"emoji": "5⃣", "name": "keycap: 5", "shortname": ":five:", "unicode": "0035-20e3", "html": "5⃣", "category": "Symbols (keycap)", "order": "2113"}, + {"emoji": "6⃣", "name": "keycap: 6", "shortname": ":six:", "unicode": "0036-20e3", "html": "6⃣", "category": "Symbols (keycap)", "order": "2114"}, + {"emoji": "7⃣", "name": "keycap: 7", "shortname": ":seven:", "unicode": "0037-20e3", "html": "7⃣", "category": "Symbols (keycap)", "order": "2115"}, + {"emoji": "8⃣", "name": "keycap: 8", "shortname": ":eight:", "unicode": "0038-20e3", "html": "8⃣", "category": "Symbols (keycap)", "order": "2116"}, + {"emoji": "9⃣", "name": "keycap: 9", "shortname": ":nine:", "unicode": "0039-20e3", "html": "9⃣", "category": "Symbols (keycap)", "order": "2117"}, + {"emoji": "🤣", "name": "rolling on the floor laughing", "shortname": ":rolling_on_the_floor_laughing:", "unicode": "1F923", "html": "🤣", "category": "Smileys & Emotion (face-smiling)", "order": ""}, + {"emoji": "🥰", "name": "smiling face with hearts", "shortname": ":smiling_face_with_hearts:", "unicode": "1F970", "html": "🥰", "category": "Smileys & Emotion (face-affection)", "order": ""}, + {"emoji": "🤩", "name": "star-struck", "shortname": ":starstruck:", "unicode": "1F929", "html": "🤩", "category": "Smileys & Emotion (face-affection)", "order": ""}, + {"emoji": "☺", "name": "smiling face", "shortname": ":smiling_face:", "unicode": "263A", "html": "☺", "category": "Smileys & Emotion (face-affection)", "order": ""}, + {"emoji": "🤪", "name": "zany face", "shortname": ":zany_face:", "unicode": "1F92A", "html": "🤪", "category": "Smileys & Emotion (face-tongue)", "order": ""}, + {"emoji": "🤭", "name": "face with hand over mouth", "shortname": ":face_with_hand_over_mouth:", "unicode": "1F92D", "html": "🤭", "category": "Smileys & Emotion (face-hand)", "order": ""}, + {"emoji": "🤫", "name": "shushing face", "shortname": ":shushing_face:", "unicode": "1F92B", "html": "🤫", "category": "Smileys & Emotion (face-hand)", "order": ""}, + {"emoji": "🤨", "name": "face with raised eyebrow", "shortname": ":face_with_raised_eyebrow:", "unicode": "1F928", "html": "🤨", "category": "Smileys & Emotion (face-neutral-skeptical)", "order": ""}, + {"emoji": "🤥", "name": "lying face", "shortname": ":lying_face:", "unicode": "1F925", "html": "🤥", "category": "Smileys & Emotion (face-neutral-skeptical)", "order": ""}, + {"emoji": "🤤", "name": "drooling face", "shortname": ":drooling_face:", "unicode": "1F924", "html": "🤤", "category": "Smileys & Emotion (face-sleepy)", "order": ""}, + {"emoji": "🤢", "name": "nauseated face", "shortname": ":nauseated_face:", "unicode": "1F922", "html": "🤢", "category": "Smileys & Emotion (face-unwell)", "order": ""}, + {"emoji": "🤮", "name": "face vomiting", "shortname": ":face_vomiting:", "unicode": "1F92E", "html": "🤮", "category": "Smileys & Emotion (face-unwell)", "order": ""}, + {"emoji": "🤧", "name": "sneezing face", "shortname": ":sneezing_face:", "unicode": "1F927", "html": "🤧", "category": "Smileys & Emotion (face-unwell)", "order": ""}, + {"emoji": "🥵", "name": "hot face", "shortname": ":hot_face:", "unicode": "1F975", "html": "🥵", "category": "Smileys & Emotion (face-unwell)", "order": ""}, + {"emoji": "🥶", "name": "cold face", "shortname": ":cold_face:", "unicode": "1F976", "html": "🥶", "category": "Smileys & Emotion (face-unwell)", "order": ""}, + {"emoji": "🥴", "name": "woozy face", "shortname": ":woozy_face:", "unicode": "1F974", "html": "🥴", "category": "Smileys & Emotion (face-unwell)", "order": ""}, + {"emoji": "🤯", "name": "exploding head", "shortname": ":exploding_head:", "unicode": "1F92F", "html": "🤯", "category": "Smileys & Emotion (face-unwell)", "order": ""}, + {"emoji": "🤠", "name": "cowboy hat face", "shortname": ":cowboy_hat_face:", "unicode": "1F920", "html": "🤠", "category": "Smileys & Emotion (face-hat)", "order": ""}, + {"emoji": "🥳", "name": "partying face", "shortname": ":partying_face:", "unicode": "1F973", "html": "🥳", "category": "Smileys & Emotion (face-hat)", "order": ""}, + {"emoji": "🧐", "name": "face with monocle", "shortname": ":face_with_monocle:", "unicode": "1F9D0", "html": "🧐", "category": "Smileys & Emotion (face-glasses)", "order": ""}, + {"emoji": "☹️", "name": "frowning face", "shortname": ":frowning_face:", "unicode": "2639 FE0F", "html": "☹️", "category": "Smileys & Emotion (face-concerned)", "order": ""}, + {"emoji": "🥺", "name": "pleading face", "shortname": ":pleading_face:", "unicode": "1F97A", "html": "🥺", "category": "Smileys & Emotion (face-concerned)", "order": ""}, + {"emoji": "🥱", "name": "yawning face", "shortname": ":yawning_face:", "unicode": "1F971", "html": "🥱", "category": "Smileys & Emotion (face-concerned)", "order": ""}, + {"emoji": "🤬", "name": "face with symbols on mouth", "shortname": ":face_with_symbols_on_mouth:", "unicode": "1F92C", "html": "🤬", "category": "Smileys & Emotion (face-negative)", "order": ""}, + {"emoji": "☠️", "name": "skull and crossbones", "shortname": ":skull_and_crossbones:", "unicode": "2620 FE0F", "html": "☠️", "category": "Smileys & Emotion (face-negative)", "order": ""}, + {"emoji": "🤡", "name": "clown face", "shortname": ":clown_face:", "unicode": "1F921", "html": "🤡", "category": "Smileys & Emotion (face-costume)", "order": ""}, + {"emoji": "❣", "name": "heart exclamation", "shortname": ":heart_exclamation:", "unicode": "2763", "html": "❣", "category": "Smileys & Emotion (emotion)", "order": ""}, + {"emoji": "❤", "name": "red heart", "shortname": ":red_heart:", "unicode": "2764", "html": "❤", "category": "Smileys & Emotion (emotion)", "order": ""}, + {"emoji": "🧡", "name": "orange heart", "shortname": ":orange_heart:", "unicode": "1F9E1", "html": "🧡", "category": "Smileys & Emotion (emotion)", "order": ""}, + {"emoji": "🤎", "name": "brown heart", "shortname": ":brown_heart:", "unicode": "1F90E", "html": "🤎", "category": "Smileys & Emotion (emotion)", "order": ""}, + {"emoji": "🤍", "name": "white heart", "shortname": ":white_heart:", "unicode": "1F90D", "html": "🤍", "category": "Smileys & Emotion (emotion)", "order": ""}, + {"emoji": "🕳️", "name": "hole", "shortname": ":hole:", "unicode": "1F573 FE0F", "html": "🕳️", "category": "Smileys & Emotion (emotion)", "order": ""}, + {"emoji": "👁️‍🗨️", "name": "eye in speech bubble", "shortname": ":eye_in_speech_bubble:", "unicode": "1F441 FE0F 200D 1F5E8 FE0F", "html": "👁️‍🗨️", "category": "Smileys & Emotion (emotion)", "order": ""}, + {"emoji": "👁‍🗨️", "name": "eye in speech bubble", "shortname": ":eye_in_speech_bubble:", "unicode": "1F441 200D 1F5E8 FE0F", "html": "👁‍🗨️", "category": "Smileys & Emotion (emotion)", "order": ""}, + {"emoji": "👁️‍🗨", "name": "eye in speech bubble", "shortname": ":eye_in_speech_bubble:", "unicode": "1F441 FE0F 200D 1F5E8", "html": "👁️‍🗨", "category": "Smileys & Emotion (emotion)", "order": ""}, + {"emoji": "🗨️", "name": "left speech bubble", "shortname": ":left_speech_bubble:", "unicode": "1F5E8 FE0F", "html": "🗨️", "category": "Smileys & Emotion (emotion)", "order": ""}, + {"emoji": "🗯️", "name": "right anger bubble", "shortname": ":right_anger_bubble:", "unicode": "1F5EF FE0F", "html": "🗯️", "category": "Smileys & Emotion (emotion)", "order": ""}, + {"emoji": "🤚", "name": "raised back of hand", "shortname": ":raised_back_of_hand:", "unicode": "1F91A", "html": "🤚", "category": "People & Body (hand-fingers-open)", "order": ""}, + {"emoji": "🤚🏻", "name": "raised back of hand: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F91A 1F3FB", "html": "🤚🏻", "category": "People & Body (hand-fingers-open)", "order": ""}, + {"emoji": "🤚🏼", "name": "raised back of hand: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F91A 1F3FC", "html": "🤚🏼", "category": "People & Body (hand-fingers-open)", "order": ""}, + {"emoji": "🤚🏽", "name": "raised back of hand: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F91A 1F3FD", "html": "🤚🏽", "category": "People & Body (hand-fingers-open)", "order": ""}, + {"emoji": "🤚🏾", "name": "raised back of hand: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F91A 1F3FE", "html": "🤚🏾", "category": "People & Body (hand-fingers-open)", "order": ""}, + {"emoji": "🤚🏿", "name": "raised back of hand: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F91A 1F3FF", "html": "🤚🏿", "category": "People & Body (hand-fingers-open)", "order": ""}, + {"emoji": "🖐️", "name": "hand with fingers splayed", "shortname": ":hand_with_fingers_splayed:", "unicode": "1F590 FE0F", "html": "🖐️", "category": "People & Body (hand-fingers-open)", "order": ""}, + {"emoji": "🤏", "name": "pinching hand", "shortname": ":pinching_hand:", "unicode": "1F90F", "html": "🤏", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "🤏🏻", "name": "pinching hand: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F90F 1F3FB", "html": "🤏🏻", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "🤏🏼", "name": "pinching hand: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F90F 1F3FC", "html": "🤏🏼", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "🤏🏽", "name": "pinching hand: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F90F 1F3FD", "html": "🤏🏽", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "🤏🏾", "name": "pinching hand: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F90F 1F3FE", "html": "🤏🏾", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "🤏🏿", "name": "pinching hand: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F90F 1F3FF", "html": "🤏🏿", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "✌", "name": "victory hand", "shortname": ":victory_hand:", "unicode": "270C", "html": "✌", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "🤞", "name": "crossed fingers", "shortname": ":crossed_fingers:", "unicode": "1F91E", "html": "🤞", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "🤞🏻", "name": "crossed fingers: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F91E 1F3FB", "html": "🤞🏻", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "🤞🏼", "name": "crossed fingers: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F91E 1F3FC", "html": "🤞🏼", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "🤞🏽", "name": "crossed fingers: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F91E 1F3FD", "html": "🤞🏽", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "🤞🏾", "name": "crossed fingers: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F91E 1F3FE", "html": "🤞🏾", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "🤞🏿", "name": "crossed fingers: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F91E 1F3FF", "html": "🤞🏿", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "🤟", "name": "love-you gesture", "shortname": ":loveyou_gesture:", "unicode": "1F91F", "html": "🤟", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "🤟🏻", "name": "love-you gesture: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F91F 1F3FB", "html": "🤟🏻", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "🤟🏼", "name": "love-you gesture: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F91F 1F3FC", "html": "🤟🏼", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "🤟🏽", "name": "love-you gesture: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F91F 1F3FD", "html": "🤟🏽", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "🤟🏾", "name": "love-you gesture: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F91F 1F3FE", "html": "🤟🏾", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "🤟🏿", "name": "love-you gesture: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F91F 1F3FF", "html": "🤟🏿", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "🤙", "name": "call me hand", "shortname": ":call_me_hand:", "unicode": "1F919", "html": "🤙", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "🤙🏻", "name": "call me hand: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F919 1F3FB", "html": "🤙🏻", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "🤙🏼", "name": "call me hand: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F919 1F3FC", "html": "🤙🏼", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "🤙🏽", "name": "call me hand: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F919 1F3FD", "html": "🤙🏽", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "🤙🏾", "name": "call me hand: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F919 1F3FE", "html": "🤙🏾", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "🤙🏿", "name": "call me hand: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F919 1F3FF", "html": "🤙🏿", "category": "People & Body (hand-fingers-partial)", "order": ""}, + {"emoji": "☝", "name": "index pointing up", "shortname": ":index_pointing_up:", "unicode": "261D", "html": "☝", "category": "People & Body (hand-single-finger)", "order": ""}, + {"emoji": "🤛", "name": "left-facing fist", "shortname": ":leftfacing_fist:", "unicode": "1F91B", "html": "🤛", "category": "People & Body (hand-fingers-closed)", "order": ""}, + {"emoji": "🤛🏻", "name": "left-facing fist: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F91B 1F3FB", "html": "🤛🏻", "category": "People & Body (hand-fingers-closed)", "order": ""}, + {"emoji": "🤛🏼", "name": "left-facing fist: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F91B 1F3FC", "html": "🤛🏼", "category": "People & Body (hand-fingers-closed)", "order": ""}, + {"emoji": "🤛🏽", "name": "left-facing fist: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F91B 1F3FD", "html": "🤛🏽", "category": "People & Body (hand-fingers-closed)", "order": ""}, + {"emoji": "🤛🏾", "name": "left-facing fist: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F91B 1F3FE", "html": "🤛🏾", "category": "People & Body (hand-fingers-closed)", "order": ""}, + {"emoji": "🤛🏿", "name": "left-facing fist: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F91B 1F3FF", "html": "🤛🏿", "category": "People & Body (hand-fingers-closed)", "order": ""}, + {"emoji": "🤜", "name": "right-facing fist", "shortname": ":rightfacing_fist:", "unicode": "1F91C", "html": "🤜", "category": "People & Body (hand-fingers-closed)", "order": ""}, + {"emoji": "🤜🏻", "name": "right-facing fist: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F91C 1F3FB", "html": "🤜🏻", "category": "People & Body (hand-fingers-closed)", "order": ""}, + {"emoji": "🤜🏼", "name": "right-facing fist: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F91C 1F3FC", "html": "🤜🏼", "category": "People & Body (hand-fingers-closed)", "order": ""}, + {"emoji": "🤜🏽", "name": "right-facing fist: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F91C 1F3FD", "html": "🤜🏽", "category": "People & Body (hand-fingers-closed)", "order": ""}, + {"emoji": "🤜🏾", "name": "right-facing fist: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F91C 1F3FE", "html": "🤜🏾", "category": "People & Body (hand-fingers-closed)", "order": ""}, + {"emoji": "🤜🏿", "name": "right-facing fist: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F91C 1F3FF", "html": "🤜🏿", "category": "People & Body (hand-fingers-closed)", "order": ""}, + {"emoji": "🤲", "name": "palms up together", "shortname": ":palms_up_together:", "unicode": "1F932", "html": "🤲", "category": "People & Body (hands)", "order": ""}, + {"emoji": "🤲🏻", "name": "palms up together: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F932 1F3FB", "html": "🤲🏻", "category": "People & Body (hands)", "order": ""}, + {"emoji": "🤲🏼", "name": "palms up together: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F932 1F3FC", "html": "🤲🏼", "category": "People & Body (hands)", "order": ""}, + {"emoji": "🤲🏽", "name": "palms up together: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F932 1F3FD", "html": "🤲🏽", "category": "People & Body (hands)", "order": ""}, + {"emoji": "🤲🏾", "name": "palms up together: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F932 1F3FE", "html": "🤲🏾", "category": "People & Body (hands)", "order": ""}, + {"emoji": "🤲🏿", "name": "palms up together: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F932 1F3FF", "html": "🤲🏿", "category": "People & Body (hands)", "order": ""}, + {"emoji": "🤝", "name": "handshake", "shortname": ":handshake:", "unicode": "1F91D", "html": "🤝", "category": "People & Body (hands)", "order": ""}, + {"emoji": "✍️", "name": "writing hand", "shortname": ":writing_hand:", "unicode": "270D FE0F", "html": "✍️", "category": "People & Body (hand-prop)", "order": ""}, + {"emoji": "🤳", "name": "selfie", "shortname": ":selfie:", "unicode": "1F933", "html": "🤳", "category": "People & Body (hand-prop)", "order": ""}, + {"emoji": "🤳🏻", "name": "selfie: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F933 1F3FB", "html": "🤳🏻", "category": "People & Body (hand-prop)", "order": ""}, + {"emoji": "🤳🏼", "name": "selfie: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F933 1F3FC", "html": "🤳🏼", "category": "People & Body (hand-prop)", "order": ""}, + {"emoji": "🤳🏽", "name": "selfie: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F933 1F3FD", "html": "🤳🏽", "category": "People & Body (hand-prop)", "order": ""}, + {"emoji": "🤳🏾", "name": "selfie: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F933 1F3FE", "html": "🤳🏾", "category": "People & Body (hand-prop)", "order": ""}, + {"emoji": "🤳🏿", "name": "selfie: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F933 1F3FF", "html": "🤳🏿", "category": "People & Body (hand-prop)", "order": ""}, + {"emoji": "🦾", "name": "mechanical arm", "shortname": ":mechanical_arm:", "unicode": "1F9BE", "html": "🦾", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "🦿", "name": "mechanical leg", "shortname": ":mechanical_leg:", "unicode": "1F9BF", "html": "🦿", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "🦵", "name": "leg", "shortname": ":leg:", "unicode": "1F9B5", "html": "🦵", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "🦵🏻", "name": "leg: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9B5 1F3FB", "html": "🦵🏻", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "🦵🏼", "name": "leg: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9B5 1F3FC", "html": "🦵🏼", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "🦵🏽", "name": "leg: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9B5 1F3FD", "html": "🦵🏽", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "🦵🏾", "name": "leg: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9B5 1F3FE", "html": "🦵🏾", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "🦵🏿", "name": "leg: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9B5 1F3FF", "html": "🦵🏿", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "🦶", "name": "foot", "shortname": ":foot:", "unicode": "1F9B6", "html": "🦶", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "🦶🏻", "name": "foot: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9B6 1F3FB", "html": "🦶🏻", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "🦶🏼", "name": "foot: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9B6 1F3FC", "html": "🦶🏼", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "🦶🏽", "name": "foot: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9B6 1F3FD", "html": "🦶🏽", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "🦶🏾", "name": "foot: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9B6 1F3FE", "html": "🦶🏾", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "🦶🏿", "name": "foot: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9B6 1F3FF", "html": "🦶🏿", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "🦻", "name": "ear with hearing aid", "shortname": ":ear_with_hearing_aid:", "unicode": "1F9BB", "html": "🦻", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "🦻🏻", "name": "ear with hearing aid: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9BB 1F3FB", "html": "🦻🏻", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "🦻🏼", "name": "ear with hearing aid: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9BB 1F3FC", "html": "🦻🏼", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "🦻🏽", "name": "ear with hearing aid: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9BB 1F3FD", "html": "🦻🏽", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "🦻🏾", "name": "ear with hearing aid: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9BB 1F3FE", "html": "🦻🏾", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "🦻🏿", "name": "ear with hearing aid: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9BB 1F3FF", "html": "🦻🏿", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "🧠", "name": "brain", "shortname": ":brain:", "unicode": "1F9E0", "html": "🧠", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "🦷", "name": "tooth", "shortname": ":tooth:", "unicode": "1F9B7", "html": "🦷", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "🦴", "name": "bone", "shortname": ":bone:", "unicode": "1F9B4", "html": "🦴", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "👁️", "name": "eye", "shortname": ":eye:", "unicode": "1F441 FE0F", "html": "👁️", "category": "People & Body (body-parts)", "order": ""}, + {"emoji": "🧒", "name": "child", "shortname": ":child:", "unicode": "1F9D2", "html": "🧒", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧒🏻", "name": "child: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D2 1F3FB", "html": "🧒🏻", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧒🏼", "name": "child: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D2 1F3FC", "html": "🧒🏼", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧒🏽", "name": "child: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D2 1F3FD", "html": "🧒🏽", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧒🏾", "name": "child: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D2 1F3FE", "html": "🧒🏾", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧒🏿", "name": "child: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D2 1F3FF", "html": "🧒🏿", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧑", "name": "person", "shortname": ":person:", "unicode": "1F9D1", "html": "🧑", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧑🏻", "name": "person: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D1 1F3FB", "html": "🧑🏻", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧑🏼", "name": "person: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D1 1F3FC", "html": "🧑🏼", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧑🏽", "name": "person: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D1 1F3FD", "html": "🧑🏽", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧑🏾", "name": "person: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D1 1F3FE", "html": "🧑🏾", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧑🏿", "name": "person: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D1 1F3FF", "html": "🧑🏿", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧔", "name": "man: beard", "shortname": ":beard:", "unicode": "1F9D4", "html": "🧔", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧔🏻", "name": "man: light skin tone, beard", "shortname": ":light_skin_tone_beard:", "unicode": "1F9D4 1F3FB", "html": "🧔🏻", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧔🏼", "name": "man: medium-light skin tone, beard", "shortname": ":mediumlight_skin_tone_beard:", "unicode": "1F9D4 1F3FC", "html": "🧔🏼", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧔🏽", "name": "man: medium skin tone, beard", "shortname": ":medium_skin_tone_beard:", "unicode": "1F9D4 1F3FD", "html": "🧔🏽", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧔🏾", "name": "man: medium-dark skin tone, beard", "shortname": ":mediumdark_skin_tone_beard:", "unicode": "1F9D4 1F3FE", "html": "🧔🏾", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧔🏿", "name": "man: dark skin tone, beard", "shortname": ":dark_skin_tone_beard:", "unicode": "1F9D4 1F3FF", "html": "🧔🏿", "category": "People & Body (person)", "order": ""}, + {"emoji": "👨‍🦰", "name": "man: red hair", "shortname": ":red_hair:", "unicode": "1F468 200D 1F9B0", "html": "👨‍🦰", "category": "People & Body (person)", "order": ""}, + {"emoji": "👨🏻‍🦰", "name": "man: light skin tone, red hair", "shortname": ":light_skin_tone_red_hair:", "unicode": "1F468 1F3FB 200D 1F9B0", "html": "👨🏻‍🦰", "category": "People & Body (person)", "order": ""}, + {"emoji": "👨🏼‍🦰", "name": "man: medium-light skin tone, red hair", "shortname": ":mediumlight_skin_tone_red_hair:", "unicode": "1F468 1F3FC 200D 1F9B0", "html": "👨🏼‍🦰", "category": "People & Body (person)", "order": ""}, + {"emoji": "👨🏽‍🦰", "name": "man: medium skin tone, red hair", "shortname": ":medium_skin_tone_red_hair:", "unicode": "1F468 1F3FD 200D 1F9B0", "html": "👨🏽‍🦰", "category": "People & Body (person)", "order": ""}, + {"emoji": "👨🏾‍🦰", "name": "man: medium-dark skin tone, red hair", "shortname": ":mediumdark_skin_tone_red_hair:", "unicode": "1F468 1F3FE 200D 1F9B0", "html": "👨🏾‍🦰", "category": "People & Body (person)", "order": ""}, + {"emoji": "👨🏿‍🦰", "name": "man: dark skin tone, red hair", "shortname": ":dark_skin_tone_red_hair:", "unicode": "1F468 1F3FF 200D 1F9B0", "html": "👨🏿‍🦰", "category": "People & Body (person)", "order": ""}, + {"emoji": "👨‍🦱", "name": "man: curly hair", "shortname": ":curly_hair:", "unicode": "1F468 200D 1F9B1", "html": "👨‍🦱", "category": "People & Body (person)", "order": ""}, + {"emoji": "👨🏻‍🦱", "name": "man: light skin tone, curly hair", "shortname": ":light_skin_tone_curly_hair:", "unicode": "1F468 1F3FB 200D 1F9B1", "html": "👨🏻‍🦱", "category": "People & Body (person)", "order": ""}, + {"emoji": "👨🏼‍🦱", "name": "man: medium-light skin tone, curly hair", "shortname": ":mediumlight_skin_tone_curly_hair:", "unicode": "1F468 1F3FC 200D 1F9B1", "html": "👨🏼‍🦱", "category": "People & Body (person)", "order": ""}, + {"emoji": "👨🏽‍🦱", "name": "man: medium skin tone, curly hair", "shortname": ":medium_skin_tone_curly_hair:", "unicode": "1F468 1F3FD 200D 1F9B1", "html": "👨🏽‍🦱", "category": "People & Body (person)", "order": ""}, + {"emoji": "👨🏾‍🦱", "name": "man: medium-dark skin tone, curly hair", "shortname": ":mediumdark_skin_tone_curly_hair:", "unicode": "1F468 1F3FE 200D 1F9B1", "html": "👨🏾‍🦱", "category": "People & Body (person)", "order": ""}, + {"emoji": "👨🏿‍🦱", "name": "man: dark skin tone, curly hair", "shortname": ":dark_skin_tone_curly_hair:", "unicode": "1F468 1F3FF 200D 1F9B1", "html": "👨🏿‍🦱", "category": "People & Body (person)", "order": ""}, + {"emoji": "👨‍🦳", "name": "man: white hair", "shortname": ":white_hair:", "unicode": "1F468 200D 1F9B3", "html": "👨‍🦳", "category": "People & Body (person)", "order": ""}, + {"emoji": "👨🏻‍🦳", "name": "man: light skin tone, white hair", "shortname": ":light_skin_tone_white_hair:", "unicode": "1F468 1F3FB 200D 1F9B3", "html": "👨🏻‍🦳", "category": "People & Body (person)", "order": ""}, + {"emoji": "👨🏼‍🦳", "name": "man: medium-light skin tone, white hair", "shortname": ":mediumlight_skin_tone_white_hair:", "unicode": "1F468 1F3FC 200D 1F9B3", "html": "👨🏼‍🦳", "category": "People & Body (person)", "order": ""}, + {"emoji": "👨🏽‍🦳", "name": "man: medium skin tone, white hair", "shortname": ":medium_skin_tone_white_hair:", "unicode": "1F468 1F3FD 200D 1F9B3", "html": "👨🏽‍🦳", "category": "People & Body (person)", "order": ""}, + {"emoji": "👨🏾‍🦳", "name": "man: medium-dark skin tone, white hair", "shortname": ":mediumdark_skin_tone_white_hair:", "unicode": "1F468 1F3FE 200D 1F9B3", "html": "👨🏾‍🦳", "category": "People & Body (person)", "order": ""}, + {"emoji": "👨🏿‍🦳", "name": "man: dark skin tone, white hair", "shortname": ":dark_skin_tone_white_hair:", "unicode": "1F468 1F3FF 200D 1F9B3", "html": "👨🏿‍🦳", "category": "People & Body (person)", "order": ""}, + {"emoji": "👨‍🦲", "name": "man: bald", "shortname": ":bald:", "unicode": "1F468 200D 1F9B2", "html": "👨‍🦲", "category": "People & Body (person)", "order": ""}, + {"emoji": "👨🏻‍🦲", "name": "man: light skin tone, bald", "shortname": ":light_skin_tone_bald:", "unicode": "1F468 1F3FB 200D 1F9B2", "html": "👨🏻‍🦲", "category": "People & Body (person)", "order": ""}, + {"emoji": "👨🏼‍🦲", "name": "man: medium-light skin tone, bald", "shortname": ":mediumlight_skin_tone_bald:", "unicode": "1F468 1F3FC 200D 1F9B2", "html": "👨🏼‍🦲", "category": "People & Body (person)", "order": ""}, + {"emoji": "👨🏽‍🦲", "name": "man: medium skin tone, bald", "shortname": ":medium_skin_tone_bald:", "unicode": "1F468 1F3FD 200D 1F9B2", "html": "👨🏽‍🦲", "category": "People & Body (person)", "order": ""}, + {"emoji": "👨🏾‍🦲", "name": "man: medium-dark skin tone, bald", "shortname": ":mediumdark_skin_tone_bald:", "unicode": "1F468 1F3FE 200D 1F9B2", "html": "👨🏾‍🦲", "category": "People & Body (person)", "order": ""}, + {"emoji": "👨🏿‍🦲", "name": "man: dark skin tone, bald", "shortname": ":dark_skin_tone_bald:", "unicode": "1F468 1F3FF 200D 1F9B2", "html": "👨🏿‍🦲", "category": "People & Body (person)", "order": ""}, + {"emoji": "👩‍🦰", "name": "woman: red hair", "shortname": ":red_hair:", "unicode": "1F469 200D 1F9B0", "html": "👩‍🦰", "category": "People & Body (person)", "order": ""}, + {"emoji": "👩🏻‍🦰", "name": "woman: light skin tone, red hair", "shortname": ":light_skin_tone_red_hair:", "unicode": "1F469 1F3FB 200D 1F9B0", "html": "👩🏻‍🦰", "category": "People & Body (person)", "order": ""}, + {"emoji": "👩🏼‍🦰", "name": "woman: medium-light skin tone, red hair", "shortname": ":mediumlight_skin_tone_red_hair:", "unicode": "1F469 1F3FC 200D 1F9B0", "html": "👩🏼‍🦰", "category": "People & Body (person)", "order": ""}, + {"emoji": "👩🏽‍🦰", "name": "woman: medium skin tone, red hair", "shortname": ":medium_skin_tone_red_hair:", "unicode": "1F469 1F3FD 200D 1F9B0", "html": "👩🏽‍🦰", "category": "People & Body (person)", "order": ""}, + {"emoji": "👩🏾‍🦰", "name": "woman: medium-dark skin tone, red hair", "shortname": ":mediumdark_skin_tone_red_hair:", "unicode": "1F469 1F3FE 200D 1F9B0", "html": "👩🏾‍🦰", "category": "People & Body (person)", "order": ""}, + {"emoji": "👩🏿‍🦰", "name": "woman: dark skin tone, red hair", "shortname": ":dark_skin_tone_red_hair:", "unicode": "1F469 1F3FF 200D 1F9B0", "html": "👩🏿‍🦰", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧑‍🦰", "name": "person: red hair", "shortname": ":red_hair:", "unicode": "1F9D1 200D 1F9B0", "html": "🧑‍🦰", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧑🏻‍🦰", "name": "person: light skin tone, red hair", "shortname": ":light_skin_tone_red_hair:", "unicode": "1F9D1 1F3FB 200D 1F9B0", "html": "🧑🏻‍🦰", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧑🏼‍🦰", "name": "person: medium-light skin tone, red hair", "shortname": ":mediumlight_skin_tone_red_hair:", "unicode": "1F9D1 1F3FC 200D 1F9B0", "html": "🧑🏼‍🦰", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧑🏽‍🦰", "name": "person: medium skin tone, red hair", "shortname": ":medium_skin_tone_red_hair:", "unicode": "1F9D1 1F3FD 200D 1F9B0", "html": "🧑🏽‍🦰", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧑🏾‍🦰", "name": "person: medium-dark skin tone, red hair", "shortname": ":mediumdark_skin_tone_red_hair:", "unicode": "1F9D1 1F3FE 200D 1F9B0", "html": "🧑🏾‍🦰", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧑🏿‍🦰", "name": "person: dark skin tone, red hair", "shortname": ":dark_skin_tone_red_hair:", "unicode": "1F9D1 1F3FF 200D 1F9B0", "html": "🧑🏿‍🦰", "category": "People & Body (person)", "order": ""}, + {"emoji": "👩‍🦱", "name": "woman: curly hair", "shortname": ":curly_hair:", "unicode": "1F469 200D 1F9B1", "html": "👩‍🦱", "category": "People & Body (person)", "order": ""}, + {"emoji": "👩🏻‍🦱", "name": "woman: light skin tone, curly hair", "shortname": ":light_skin_tone_curly_hair:", "unicode": "1F469 1F3FB 200D 1F9B1", "html": "👩🏻‍🦱", "category": "People & Body (person)", "order": ""}, + {"emoji": "👩🏼‍🦱", "name": "woman: medium-light skin tone, curly hair", "shortname": ":mediumlight_skin_tone_curly_hair:", "unicode": "1F469 1F3FC 200D 1F9B1", "html": "👩🏼‍🦱", "category": "People & Body (person)", "order": ""}, + {"emoji": "👩🏽‍🦱", "name": "woman: medium skin tone, curly hair", "shortname": ":medium_skin_tone_curly_hair:", "unicode": "1F469 1F3FD 200D 1F9B1", "html": "👩🏽‍🦱", "category": "People & Body (person)", "order": ""}, + {"emoji": "👩🏾‍🦱", "name": "woman: medium-dark skin tone, curly hair", "shortname": ":mediumdark_skin_tone_curly_hair:", "unicode": "1F469 1F3FE 200D 1F9B1", "html": "👩🏾‍🦱", "category": "People & Body (person)", "order": ""}, + {"emoji": "👩🏿‍🦱", "name": "woman: dark skin tone, curly hair", "shortname": ":dark_skin_tone_curly_hair:", "unicode": "1F469 1F3FF 200D 1F9B1", "html": "👩🏿‍🦱", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧑‍🦱", "name": "person: curly hair", "shortname": ":curly_hair:", "unicode": "1F9D1 200D 1F9B1", "html": "🧑‍🦱", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧑🏻‍🦱", "name": "person: light skin tone, curly hair", "shortname": ":light_skin_tone_curly_hair:", "unicode": "1F9D1 1F3FB 200D 1F9B1", "html": "🧑🏻‍🦱", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧑🏼‍🦱", "name": "person: medium-light skin tone, curly hair", "shortname": ":mediumlight_skin_tone_curly_hair:", "unicode": "1F9D1 1F3FC 200D 1F9B1", "html": "🧑🏼‍🦱", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧑🏽‍🦱", "name": "person: medium skin tone, curly hair", "shortname": ":medium_skin_tone_curly_hair:", "unicode": "1F9D1 1F3FD 200D 1F9B1", "html": "🧑🏽‍🦱", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧑🏾‍🦱", "name": "person: medium-dark skin tone, curly hair", "shortname": ":mediumdark_skin_tone_curly_hair:", "unicode": "1F9D1 1F3FE 200D 1F9B1", "html": "🧑🏾‍🦱", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧑🏿‍🦱", "name": "person: dark skin tone, curly hair", "shortname": ":dark_skin_tone_curly_hair:", "unicode": "1F9D1 1F3FF 200D 1F9B1", "html": "🧑🏿‍🦱", "category": "People & Body (person)", "order": ""}, + {"emoji": "👩‍🦳", "name": "woman: white hair", "shortname": ":white_hair:", "unicode": "1F469 200D 1F9B3", "html": "👩‍🦳", "category": "People & Body (person)", "order": ""}, + {"emoji": "👩🏻‍🦳", "name": "woman: light skin tone, white hair", "shortname": ":light_skin_tone_white_hair:", "unicode": "1F469 1F3FB 200D 1F9B3", "html": "👩🏻‍🦳", "category": "People & Body (person)", "order": ""}, + {"emoji": "👩🏼‍🦳", "name": "woman: medium-light skin tone, white hair", "shortname": ":mediumlight_skin_tone_white_hair:", "unicode": "1F469 1F3FC 200D 1F9B3", "html": "👩🏼‍🦳", "category": "People & Body (person)", "order": ""}, + {"emoji": "👩🏽‍🦳", "name": "woman: medium skin tone, white hair", "shortname": ":medium_skin_tone_white_hair:", "unicode": "1F469 1F3FD 200D 1F9B3", "html": "👩🏽‍🦳", "category": "People & Body (person)", "order": ""}, + {"emoji": "👩🏾‍🦳", "name": "woman: medium-dark skin tone, white hair", "shortname": ":mediumdark_skin_tone_white_hair:", "unicode": "1F469 1F3FE 200D 1F9B3", "html": "👩🏾‍🦳", "category": "People & Body (person)", "order": ""}, + {"emoji": "👩🏿‍🦳", "name": "woman: dark skin tone, white hair", "shortname": ":dark_skin_tone_white_hair:", "unicode": "1F469 1F3FF 200D 1F9B3", "html": "👩🏿‍🦳", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧑‍🦳", "name": "person: white hair", "shortname": ":white_hair:", "unicode": "1F9D1 200D 1F9B3", "html": "🧑‍🦳", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧑🏻‍🦳", "name": "person: light skin tone, white hair", "shortname": ":light_skin_tone_white_hair:", "unicode": "1F9D1 1F3FB 200D 1F9B3", "html": "🧑🏻‍🦳", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧑🏼‍🦳", "name": "person: medium-light skin tone, white hair", "shortname": ":mediumlight_skin_tone_white_hair:", "unicode": "1F9D1 1F3FC 200D 1F9B3", "html": "🧑🏼‍🦳", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧑🏽‍🦳", "name": "person: medium skin tone, white hair", "shortname": ":medium_skin_tone_white_hair:", "unicode": "1F9D1 1F3FD 200D 1F9B3", "html": "🧑🏽‍🦳", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧑🏾‍🦳", "name": "person: medium-dark skin tone, white hair", "shortname": ":mediumdark_skin_tone_white_hair:", "unicode": "1F9D1 1F3FE 200D 1F9B3", "html": "🧑🏾‍🦳", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧑🏿‍🦳", "name": "person: dark skin tone, white hair", "shortname": ":dark_skin_tone_white_hair:", "unicode": "1F9D1 1F3FF 200D 1F9B3", "html": "🧑🏿‍🦳", "category": "People & Body (person)", "order": ""}, + {"emoji": "👩‍🦲", "name": "woman: bald", "shortname": ":bald:", "unicode": "1F469 200D 1F9B2", "html": "👩‍🦲", "category": "People & Body (person)", "order": ""}, + {"emoji": "👩🏻‍🦲", "name": "woman: light skin tone, bald", "shortname": ":light_skin_tone_bald:", "unicode": "1F469 1F3FB 200D 1F9B2", "html": "👩🏻‍🦲", "category": "People & Body (person)", "order": ""}, + {"emoji": "👩🏼‍🦲", "name": "woman: medium-light skin tone, bald", "shortname": ":mediumlight_skin_tone_bald:", "unicode": "1F469 1F3FC 200D 1F9B2", "html": "👩🏼‍🦲", "category": "People & Body (person)", "order": ""}, + {"emoji": "👩🏽‍🦲", "name": "woman: medium skin tone, bald", "shortname": ":medium_skin_tone_bald:", "unicode": "1F469 1F3FD 200D 1F9B2", "html": "👩🏽‍🦲", "category": "People & Body (person)", "order": ""}, + {"emoji": "👩🏾‍🦲", "name": "woman: medium-dark skin tone, bald", "shortname": ":mediumdark_skin_tone_bald:", "unicode": "1F469 1F3FE 200D 1F9B2", "html": "👩🏾‍🦲", "category": "People & Body (person)", "order": ""}, + {"emoji": "👩🏿‍🦲", "name": "woman: dark skin tone, bald", "shortname": ":dark_skin_tone_bald:", "unicode": "1F469 1F3FF 200D 1F9B2", "html": "👩🏿‍🦲", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧑‍🦲", "name": "person: bald", "shortname": ":bald:", "unicode": "1F9D1 200D 1F9B2", "html": "🧑‍🦲", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧑🏻‍🦲", "name": "person: light skin tone, bald", "shortname": ":light_skin_tone_bald:", "unicode": "1F9D1 1F3FB 200D 1F9B2", "html": "🧑🏻‍🦲", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧑🏼‍🦲", "name": "person: medium-light skin tone, bald", "shortname": ":mediumlight_skin_tone_bald:", "unicode": "1F9D1 1F3FC 200D 1F9B2", "html": "🧑🏼‍🦲", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧑🏽‍🦲", "name": "person: medium skin tone, bald", "shortname": ":medium_skin_tone_bald:", "unicode": "1F9D1 1F3FD 200D 1F9B2", "html": "🧑🏽‍🦲", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧑🏾‍🦲", "name": "person: medium-dark skin tone, bald", "shortname": ":mediumdark_skin_tone_bald:", "unicode": "1F9D1 1F3FE 200D 1F9B2", "html": "🧑🏾‍🦲", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧑🏿‍🦲", "name": "person: dark skin tone, bald", "shortname": ":dark_skin_tone_bald:", "unicode": "1F9D1 1F3FF 200D 1F9B2", "html": "🧑🏿‍🦲", "category": "People & Body (person)", "order": ""}, + {"emoji": "👱‍♀️", "name": "woman: blond hair", "shortname": ":blond_hair:", "unicode": "1F471 200D 2640 FE0F", "html": "👱‍♀️", "category": "People & Body (person)", "order": ""}, + {"emoji": "👱🏻‍♀️", "name": "woman: light skin tone, blond hair", "shortname": ":light_skin_tone_blond_hair:", "unicode": "1F471 1F3FB 200D 2640 FE0F", "html": "👱🏻‍♀️", "category": "People & Body (person)", "order": ""}, + {"emoji": "👱🏼‍♀️", "name": "woman: medium-light skin tone, blond hair", "shortname": ":mediumlight_skin_tone_blond_hair:", "unicode": "1F471 1F3FC 200D 2640 FE0F", "html": "👱🏼‍♀️", "category": "People & Body (person)", "order": ""}, + {"emoji": "👱🏽‍♀️", "name": "woman: medium skin tone, blond hair", "shortname": ":medium_skin_tone_blond_hair:", "unicode": "1F471 1F3FD 200D 2640 FE0F", "html": "👱🏽‍♀️", "category": "People & Body (person)", "order": ""}, + {"emoji": "👱🏾‍♀️", "name": "woman: medium-dark skin tone, blond hair", "shortname": ":mediumdark_skin_tone_blond_hair:", "unicode": "1F471 1F3FE 200D 2640 FE0F", "html": "👱🏾‍♀️", "category": "People & Body (person)", "order": ""}, + {"emoji": "👱🏿‍♀️", "name": "woman: dark skin tone, blond hair", "shortname": ":dark_skin_tone_blond_hair:", "unicode": "1F471 1F3FF 200D 2640 FE0F", "html": "👱🏿‍♀️", "category": "People & Body (person)", "order": ""}, + {"emoji": "👱‍♂️", "name": "man: blond hair", "shortname": ":blond_hair:", "unicode": "1F471 200D 2642 FE0F", "html": "👱‍♂️", "category": "People & Body (person)", "order": ""}, + {"emoji": "👱‍♂", "name": "man: blond hair", "shortname": ":blond_hair:", "unicode": "1F471 200D 2642", "html": "👱‍♂", "category": "People & Body (person)", "order": ""}, + {"emoji": "👱🏻‍♂️", "name": "man: light skin tone, blond hair", "shortname": ":light_skin_tone_blond_hair:", "unicode": "1F471 1F3FB 200D 2642 FE0F", "html": "👱🏻‍♂️", "category": "People & Body (person)", "order": ""}, + {"emoji": "👱🏻‍♂", "name": "man: light skin tone, blond hair", "shortname": ":light_skin_tone_blond_hair:", "unicode": "1F471 1F3FB 200D 2642", "html": "👱🏻‍♂", "category": "People & Body (person)", "order": ""}, + {"emoji": "👱🏼‍♂️", "name": "man: medium-light skin tone, blond hair", "shortname": ":mediumlight_skin_tone_blond_hair:", "unicode": "1F471 1F3FC 200D 2642 FE0F", "html": "👱🏼‍♂️", "category": "People & Body (person)", "order": ""}, + {"emoji": "👱🏼‍♂", "name": "man: medium-light skin tone, blond hair", "shortname": ":mediumlight_skin_tone_blond_hair:", "unicode": "1F471 1F3FC 200D 2642", "html": "👱🏼‍♂", "category": "People & Body (person)", "order": ""}, + {"emoji": "👱🏽‍♂️", "name": "man: medium skin tone, blond hair", "shortname": ":medium_skin_tone_blond_hair:", "unicode": "1F471 1F3FD 200D 2642 FE0F", "html": "👱🏽‍♂️", "category": "People & Body (person)", "order": ""}, + {"emoji": "👱🏽‍♂", "name": "man: medium skin tone, blond hair", "shortname": ":medium_skin_tone_blond_hair:", "unicode": "1F471 1F3FD 200D 2642", "html": "👱🏽‍♂", "category": "People & Body (person)", "order": ""}, + {"emoji": "👱🏾‍♂️", "name": "man: medium-dark skin tone, blond hair", "shortname": ":mediumdark_skin_tone_blond_hair:", "unicode": "1F471 1F3FE 200D 2642 FE0F", "html": "👱🏾‍♂️", "category": "People & Body (person)", "order": ""}, + {"emoji": "👱🏾‍♂", "name": "man: medium-dark skin tone, blond hair", "shortname": ":mediumdark_skin_tone_blond_hair:", "unicode": "1F471 1F3FE 200D 2642", "html": "👱🏾‍♂", "category": "People & Body (person)", "order": ""}, + {"emoji": "👱🏿‍♂️", "name": "man: dark skin tone, blond hair", "shortname": ":dark_skin_tone_blond_hair:", "unicode": "1F471 1F3FF 200D 2642 FE0F", "html": "👱🏿‍♂️", "category": "People & Body (person)", "order": ""}, + {"emoji": "👱🏿‍♂", "name": "man: dark skin tone, blond hair", "shortname": ":dark_skin_tone_blond_hair:", "unicode": "1F471 1F3FF 200D 2642", "html": "👱🏿‍♂", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧓", "name": "older person", "shortname": ":older_person:", "unicode": "1F9D3", "html": "🧓", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧓🏻", "name": "older person: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D3 1F3FB", "html": "🧓🏻", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧓🏼", "name": "older person: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D3 1F3FC", "html": "🧓🏼", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧓🏽", "name": "older person: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D3 1F3FD", "html": "🧓🏽", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧓🏾", "name": "older person: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D3 1F3FE", "html": "🧓🏾", "category": "People & Body (person)", "order": ""}, + {"emoji": "🧓🏿", "name": "older person: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D3 1F3FF", "html": "🧓🏿", "category": "People & Body (person)", "order": ""}, + {"emoji": "🙍‍♂️", "name": "man frowning", "shortname": ":man_frowning:", "unicode": "1F64D 200D 2642 FE0F", "html": "🙍‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙍🏻‍♂️", "name": "man frowning: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F64D 1F3FB 200D 2642 FE0F", "html": "🙍🏻‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙍🏼‍♂️", "name": "man frowning: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F64D 1F3FC 200D 2642 FE0F", "html": "🙍🏼‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙍🏽‍♂️", "name": "man frowning: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F64D 1F3FD 200D 2642 FE0F", "html": "🙍🏽‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙍🏾‍♂️", "name": "man frowning: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F64D 1F3FE 200D 2642 FE0F", "html": "🙍🏾‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙍🏿‍♂️", "name": "man frowning: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F64D 1F3FF 200D 2642 FE0F", "html": "🙍🏿‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙍‍♀️", "name": "woman frowning", "shortname": ":woman_frowning:", "unicode": "1F64D 200D 2640 FE0F", "html": "🙍‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙍‍♀", "name": "woman frowning", "shortname": ":woman_frowning:", "unicode": "1F64D 200D 2640", "html": "🙍‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙍🏻‍♀️", "name": "woman frowning: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F64D 1F3FB 200D 2640 FE0F", "html": "🙍🏻‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙍🏻‍♀", "name": "woman frowning: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F64D 1F3FB 200D 2640", "html": "🙍🏻‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙍🏼‍♀️", "name": "woman frowning: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F64D 1F3FC 200D 2640 FE0F", "html": "🙍🏼‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙍🏼‍♀", "name": "woman frowning: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F64D 1F3FC 200D 2640", "html": "🙍🏼‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙍🏽‍♀️", "name": "woman frowning: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F64D 1F3FD 200D 2640 FE0F", "html": "🙍🏽‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙍🏽‍♀", "name": "woman frowning: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F64D 1F3FD 200D 2640", "html": "🙍🏽‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙍🏾‍♀️", "name": "woman frowning: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F64D 1F3FE 200D 2640 FE0F", "html": "🙍🏾‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙍🏾‍♀", "name": "woman frowning: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F64D 1F3FE 200D 2640", "html": "🙍🏾‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙍🏿‍♀️", "name": "woman frowning: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F64D 1F3FF 200D 2640 FE0F", "html": "🙍🏿‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙍🏿‍♀", "name": "woman frowning: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F64D 1F3FF 200D 2640", "html": "🙍🏿‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙎‍♂️", "name": "man pouting", "shortname": ":man_pouting:", "unicode": "1F64E 200D 2642 FE0F", "html": "🙎‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙎🏻‍♂️", "name": "man pouting: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F64E 1F3FB 200D 2642 FE0F", "html": "🙎🏻‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙎🏼‍♂️", "name": "man pouting: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F64E 1F3FC 200D 2642 FE0F", "html": "🙎🏼‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙎🏽‍♂️", "name": "man pouting: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F64E 1F3FD 200D 2642 FE0F", "html": "🙎🏽‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙎🏾‍♂️", "name": "man pouting: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F64E 1F3FE 200D 2642 FE0F", "html": "🙎🏾‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙎🏿‍♂️", "name": "man pouting: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F64E 1F3FF 200D 2642 FE0F", "html": "🙎🏿‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙎‍♀️", "name": "woman pouting", "shortname": ":woman_pouting:", "unicode": "1F64E 200D 2640 FE0F", "html": "🙎‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙎‍♀", "name": "woman pouting", "shortname": ":woman_pouting:", "unicode": "1F64E 200D 2640", "html": "🙎‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙎🏻‍♀️", "name": "woman pouting: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F64E 1F3FB 200D 2640 FE0F", "html": "🙎🏻‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙎🏻‍♀", "name": "woman pouting: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F64E 1F3FB 200D 2640", "html": "🙎🏻‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙎🏼‍♀️", "name": "woman pouting: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F64E 1F3FC 200D 2640 FE0F", "html": "🙎🏼‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙎🏼‍♀", "name": "woman pouting: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F64E 1F3FC 200D 2640", "html": "🙎🏼‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙎🏽‍♀️", "name": "woman pouting: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F64E 1F3FD 200D 2640 FE0F", "html": "🙎🏽‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙎🏽‍♀", "name": "woman pouting: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F64E 1F3FD 200D 2640", "html": "🙎🏽‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙎🏾‍♀️", "name": "woman pouting: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F64E 1F3FE 200D 2640 FE0F", "html": "🙎🏾‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙎🏾‍♀", "name": "woman pouting: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F64E 1F3FE 200D 2640", "html": "🙎🏾‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙎🏿‍♀️", "name": "woman pouting: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F64E 1F3FF 200D 2640 FE0F", "html": "🙎🏿‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙎🏿‍♀", "name": "woman pouting: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F64E 1F3FF 200D 2640", "html": "🙎🏿‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙅‍♂️", "name": "man gesturing NO", "shortname": ":man_gesturing_NO:", "unicode": "1F645 200D 2642 FE0F", "html": "🙅‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙅🏻‍♂️", "name": "man gesturing NO: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F645 1F3FB 200D 2642 FE0F", "html": "🙅🏻‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙅🏼‍♂️", "name": "man gesturing NO: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F645 1F3FC 200D 2642 FE0F", "html": "🙅🏼‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙅🏽‍♂️", "name": "man gesturing NO: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F645 1F3FD 200D 2642 FE0F", "html": "🙅🏽‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙅🏾‍♂️", "name": "man gesturing NO: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F645 1F3FE 200D 2642 FE0F", "html": "🙅🏾‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙅🏿‍♂️", "name": "man gesturing NO: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F645 1F3FF 200D 2642 FE0F", "html": "🙅🏿‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙅‍♀️", "name": "woman gesturing NO", "shortname": ":woman_gesturing_NO:", "unicode": "1F645 200D 2640 FE0F", "html": "🙅‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙅‍♀", "name": "woman gesturing NO", "shortname": ":woman_gesturing_NO:", "unicode": "1F645 200D 2640", "html": "🙅‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙅🏻‍♀️", "name": "woman gesturing NO: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F645 1F3FB 200D 2640 FE0F", "html": "🙅🏻‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙅🏻‍♀", "name": "woman gesturing NO: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F645 1F3FB 200D 2640", "html": "🙅🏻‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙅🏼‍♀️", "name": "woman gesturing NO: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F645 1F3FC 200D 2640 FE0F", "html": "🙅🏼‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙅🏼‍♀", "name": "woman gesturing NO: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F645 1F3FC 200D 2640", "html": "🙅🏼‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙅🏽‍♀️", "name": "woman gesturing NO: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F645 1F3FD 200D 2640 FE0F", "html": "🙅🏽‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙅🏽‍♀", "name": "woman gesturing NO: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F645 1F3FD 200D 2640", "html": "🙅🏽‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙅🏾‍♀️", "name": "woman gesturing NO: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F645 1F3FE 200D 2640 FE0F", "html": "🙅🏾‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙅🏾‍♀", "name": "woman gesturing NO: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F645 1F3FE 200D 2640", "html": "🙅🏾‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙅🏿‍♀️", "name": "woman gesturing NO: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F645 1F3FF 200D 2640 FE0F", "html": "🙅🏿‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙅🏿‍♀", "name": "woman gesturing NO: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F645 1F3FF 200D 2640", "html": "🙅🏿‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙆‍♂️", "name": "man gesturing OK", "shortname": ":man_gesturing_OK:", "unicode": "1F646 200D 2642 FE0F", "html": "🙆‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙆🏻‍♂️", "name": "man gesturing OK: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F646 1F3FB 200D 2642 FE0F", "html": "🙆🏻‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙆🏼‍♂️", "name": "man gesturing OK: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F646 1F3FC 200D 2642 FE0F", "html": "🙆🏼‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙆🏽‍♂️", "name": "man gesturing OK: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F646 1F3FD 200D 2642 FE0F", "html": "🙆🏽‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙆🏾‍♂️", "name": "man gesturing OK: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F646 1F3FE 200D 2642 FE0F", "html": "🙆🏾‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙆🏿‍♂️", "name": "man gesturing OK: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F646 1F3FF 200D 2642 FE0F", "html": "🙆🏿‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙆‍♀️", "name": "woman gesturing OK", "shortname": ":woman_gesturing_OK:", "unicode": "1F646 200D 2640 FE0F", "html": "🙆‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙆‍♀", "name": "woman gesturing OK", "shortname": ":woman_gesturing_OK:", "unicode": "1F646 200D 2640", "html": "🙆‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙆🏻‍♀️", "name": "woman gesturing OK: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F646 1F3FB 200D 2640 FE0F", "html": "🙆🏻‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙆🏻‍♀", "name": "woman gesturing OK: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F646 1F3FB 200D 2640", "html": "🙆🏻‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙆🏼‍♀️", "name": "woman gesturing OK: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F646 1F3FC 200D 2640 FE0F", "html": "🙆🏼‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙆🏼‍♀", "name": "woman gesturing OK: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F646 1F3FC 200D 2640", "html": "🙆🏼‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙆🏽‍♀️", "name": "woman gesturing OK: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F646 1F3FD 200D 2640 FE0F", "html": "🙆🏽‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙆🏽‍♀", "name": "woman gesturing OK: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F646 1F3FD 200D 2640", "html": "🙆🏽‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙆🏾‍♀️", "name": "woman gesturing OK: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F646 1F3FE 200D 2640 FE0F", "html": "🙆🏾‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙆🏾‍♀", "name": "woman gesturing OK: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F646 1F3FE 200D 2640", "html": "🙆🏾‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙆🏿‍♀️", "name": "woman gesturing OK: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F646 1F3FF 200D 2640 FE0F", "html": "🙆🏿‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙆🏿‍♀", "name": "woman gesturing OK: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F646 1F3FF 200D 2640", "html": "🙆🏿‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "💁‍♂️", "name": "man tipping hand", "shortname": ":man_tipping_hand:", "unicode": "1F481 200D 2642 FE0F", "html": "💁‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "💁🏻‍♂️", "name": "man tipping hand: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F481 1F3FB 200D 2642 FE0F", "html": "💁🏻‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "💁🏼‍♂️", "name": "man tipping hand: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F481 1F3FC 200D 2642 FE0F", "html": "💁🏼‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "💁🏽‍♂️", "name": "man tipping hand: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F481 1F3FD 200D 2642 FE0F", "html": "💁🏽‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "💁🏾‍♂️", "name": "man tipping hand: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F481 1F3FE 200D 2642 FE0F", "html": "💁🏾‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "💁🏿‍♂️", "name": "man tipping hand: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F481 1F3FF 200D 2642 FE0F", "html": "💁🏿‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "💁‍♀️", "name": "woman tipping hand", "shortname": ":woman_tipping_hand:", "unicode": "1F481 200D 2640 FE0F", "html": "💁‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "💁‍♀", "name": "woman tipping hand", "shortname": ":woman_tipping_hand:", "unicode": "1F481 200D 2640", "html": "💁‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "💁🏻‍♀️", "name": "woman tipping hand: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F481 1F3FB 200D 2640 FE0F", "html": "💁🏻‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "💁🏻‍♀", "name": "woman tipping hand: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F481 1F3FB 200D 2640", "html": "💁🏻‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "💁🏼‍♀️", "name": "woman tipping hand: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F481 1F3FC 200D 2640 FE0F", "html": "💁🏼‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "💁🏼‍♀", "name": "woman tipping hand: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F481 1F3FC 200D 2640", "html": "💁🏼‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "💁🏽‍♀️", "name": "woman tipping hand: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F481 1F3FD 200D 2640 FE0F", "html": "💁🏽‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "💁🏽‍♀", "name": "woman tipping hand: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F481 1F3FD 200D 2640", "html": "💁🏽‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "💁🏾‍♀️", "name": "woman tipping hand: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F481 1F3FE 200D 2640 FE0F", "html": "💁🏾‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "💁🏾‍♀", "name": "woman tipping hand: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F481 1F3FE 200D 2640", "html": "💁🏾‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "💁🏿‍♀️", "name": "woman tipping hand: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F481 1F3FF 200D 2640 FE0F", "html": "💁🏿‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "💁🏿‍♀", "name": "woman tipping hand: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F481 1F3FF 200D 2640", "html": "💁🏿‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙋‍♂️", "name": "man raising hand", "shortname": ":man_raising_hand:", "unicode": "1F64B 200D 2642 FE0F", "html": "🙋‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙋🏻‍♂️", "name": "man raising hand: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F64B 1F3FB 200D 2642 FE0F", "html": "🙋🏻‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙋🏼‍♂️", "name": "man raising hand: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F64B 1F3FC 200D 2642 FE0F", "html": "🙋🏼‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙋🏽‍♂️", "name": "man raising hand: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F64B 1F3FD 200D 2642 FE0F", "html": "🙋🏽‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙋🏾‍♂️", "name": "man raising hand: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F64B 1F3FE 200D 2642 FE0F", "html": "🙋🏾‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙋🏿‍♂️", "name": "man raising hand: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F64B 1F3FF 200D 2642 FE0F", "html": "🙋🏿‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙋‍♀️", "name": "woman raising hand", "shortname": ":woman_raising_hand:", "unicode": "1F64B 200D 2640 FE0F", "html": "🙋‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙋‍♀", "name": "woman raising hand", "shortname": ":woman_raising_hand:", "unicode": "1F64B 200D 2640", "html": "🙋‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙋🏻‍♀️", "name": "woman raising hand: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F64B 1F3FB 200D 2640 FE0F", "html": "🙋🏻‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙋🏻‍♀", "name": "woman raising hand: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F64B 1F3FB 200D 2640", "html": "🙋🏻‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙋🏼‍♀️", "name": "woman raising hand: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F64B 1F3FC 200D 2640 FE0F", "html": "🙋🏼‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙋🏼‍♀", "name": "woman raising hand: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F64B 1F3FC 200D 2640", "html": "🙋🏼‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙋🏽‍♀️", "name": "woman raising hand: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F64B 1F3FD 200D 2640 FE0F", "html": "🙋🏽‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙋🏽‍♀", "name": "woman raising hand: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F64B 1F3FD 200D 2640", "html": "🙋🏽‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙋🏾‍♀️", "name": "woman raising hand: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F64B 1F3FE 200D 2640 FE0F", "html": "🙋🏾‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙋🏾‍♀", "name": "woman raising hand: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F64B 1F3FE 200D 2640", "html": "🙋🏾‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙋🏿‍♀️", "name": "woman raising hand: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F64B 1F3FF 200D 2640 FE0F", "html": "🙋🏿‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙋🏿‍♀", "name": "woman raising hand: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F64B 1F3FF 200D 2640", "html": "🙋🏿‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🧏", "name": "deaf person", "shortname": ":deaf_person:", "unicode": "1F9CF", "html": "🧏", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🧏🏻", "name": "deaf person: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9CF 1F3FB", "html": "🧏🏻", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🧏🏼", "name": "deaf person: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9CF 1F3FC", "html": "🧏🏼", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🧏🏽", "name": "deaf person: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9CF 1F3FD", "html": "🧏🏽", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🧏🏾", "name": "deaf person: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9CF 1F3FE", "html": "🧏🏾", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🧏🏿", "name": "deaf person: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9CF 1F3FF", "html": "🧏🏿", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🧏‍♂️", "name": "deaf man", "shortname": ":deaf_man:", "unicode": "1F9CF 200D 2642 FE0F", "html": "🧏‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🧏‍♂", "name": "deaf man", "shortname": ":deaf_man:", "unicode": "1F9CF 200D 2642", "html": "🧏‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🧏🏻‍♂️", "name": "deaf man: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9CF 1F3FB 200D 2642 FE0F", "html": "🧏🏻‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🧏🏻‍♂", "name": "deaf man: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9CF 1F3FB 200D 2642", "html": "🧏🏻‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🧏🏼‍♂️", "name": "deaf man: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9CF 1F3FC 200D 2642 FE0F", "html": "🧏🏼‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🧏🏼‍♂", "name": "deaf man: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9CF 1F3FC 200D 2642", "html": "🧏🏼‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🧏🏽‍♂️", "name": "deaf man: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9CF 1F3FD 200D 2642 FE0F", "html": "🧏🏽‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🧏🏽‍♂", "name": "deaf man: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9CF 1F3FD 200D 2642", "html": "🧏🏽‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🧏🏾‍♂️", "name": "deaf man: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9CF 1F3FE 200D 2642 FE0F", "html": "🧏🏾‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🧏🏾‍♂", "name": "deaf man: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9CF 1F3FE 200D 2642", "html": "🧏🏾‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🧏🏿‍♂️", "name": "deaf man: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9CF 1F3FF 200D 2642 FE0F", "html": "🧏🏿‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🧏🏿‍♂", "name": "deaf man: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9CF 1F3FF 200D 2642", "html": "🧏🏿‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🧏‍♀️", "name": "deaf woman", "shortname": ":deaf_woman:", "unicode": "1F9CF 200D 2640 FE0F", "html": "🧏‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🧏‍♀", "name": "deaf woman", "shortname": ":deaf_woman:", "unicode": "1F9CF 200D 2640", "html": "🧏‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🧏🏻‍♀️", "name": "deaf woman: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9CF 1F3FB 200D 2640 FE0F", "html": "🧏🏻‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🧏🏻‍♀", "name": "deaf woman: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9CF 1F3FB 200D 2640", "html": "🧏🏻‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🧏🏼‍♀️", "name": "deaf woman: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9CF 1F3FC 200D 2640 FE0F", "html": "🧏🏼‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🧏🏼‍♀", "name": "deaf woman: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9CF 1F3FC 200D 2640", "html": "🧏🏼‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🧏🏽‍♀️", "name": "deaf woman: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9CF 1F3FD 200D 2640 FE0F", "html": "🧏🏽‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🧏🏽‍♀", "name": "deaf woman: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9CF 1F3FD 200D 2640", "html": "🧏🏽‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🧏🏾‍♀️", "name": "deaf woman: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9CF 1F3FE 200D 2640 FE0F", "html": "🧏🏾‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🧏🏾‍♀", "name": "deaf woman: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9CF 1F3FE 200D 2640", "html": "🧏🏾‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🧏🏿‍♀️", "name": "deaf woman: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9CF 1F3FF 200D 2640 FE0F", "html": "🧏🏿‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🧏🏿‍♀", "name": "deaf woman: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9CF 1F3FF 200D 2640", "html": "🧏🏿‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙇‍♂️", "name": "man bowing", "shortname": ":man_bowing:", "unicode": "1F647 200D 2642 FE0F", "html": "🙇‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙇‍♂", "name": "man bowing", "shortname": ":man_bowing:", "unicode": "1F647 200D 2642", "html": "🙇‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙇🏻‍♂️", "name": "man bowing: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F647 1F3FB 200D 2642 FE0F", "html": "🙇🏻‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙇🏻‍♂", "name": "man bowing: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F647 1F3FB 200D 2642", "html": "🙇🏻‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙇🏼‍♂️", "name": "man bowing: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F647 1F3FC 200D 2642 FE0F", "html": "🙇🏼‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙇🏼‍♂", "name": "man bowing: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F647 1F3FC 200D 2642", "html": "🙇🏼‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙇🏽‍♂️", "name": "man bowing: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F647 1F3FD 200D 2642 FE0F", "html": "🙇🏽‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙇🏽‍♂", "name": "man bowing: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F647 1F3FD 200D 2642", "html": "🙇🏽‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙇🏾‍♂️", "name": "man bowing: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F647 1F3FE 200D 2642 FE0F", "html": "🙇🏾‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙇🏾‍♂", "name": "man bowing: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F647 1F3FE 200D 2642", "html": "🙇🏾‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙇🏿‍♂️", "name": "man bowing: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F647 1F3FF 200D 2642 FE0F", "html": "🙇🏿‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙇🏿‍♂", "name": "man bowing: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F647 1F3FF 200D 2642", "html": "🙇🏿‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙇‍♀️", "name": "woman bowing", "shortname": ":woman_bowing:", "unicode": "1F647 200D 2640 FE0F", "html": "🙇‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙇🏻‍♀️", "name": "woman bowing: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F647 1F3FB 200D 2640 FE0F", "html": "🙇🏻‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙇🏼‍♀️", "name": "woman bowing: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F647 1F3FC 200D 2640 FE0F", "html": "🙇🏼‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙇🏽‍♀️", "name": "woman bowing: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F647 1F3FD 200D 2640 FE0F", "html": "🙇🏽‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙇🏾‍♀️", "name": "woman bowing: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F647 1F3FE 200D 2640 FE0F", "html": "🙇🏾‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🙇🏿‍♀️", "name": "woman bowing: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F647 1F3FF 200D 2640 FE0F", "html": "🙇🏿‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤦", "name": "person facepalming", "shortname": ":person_facepalming:", "unicode": "1F926", "html": "🤦", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤦🏻", "name": "person facepalming: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F926 1F3FB", "html": "🤦🏻", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤦🏼", "name": "person facepalming: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F926 1F3FC", "html": "🤦🏼", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤦🏽", "name": "person facepalming: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F926 1F3FD", "html": "🤦🏽", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤦🏾", "name": "person facepalming: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F926 1F3FE", "html": "🤦🏾", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤦🏿", "name": "person facepalming: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F926 1F3FF", "html": "🤦🏿", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤦‍♂️", "name": "man facepalming", "shortname": ":man_facepalming:", "unicode": "1F926 200D 2642 FE0F", "html": "🤦‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤦‍♂", "name": "man facepalming", "shortname": ":man_facepalming:", "unicode": "1F926 200D 2642", "html": "🤦‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤦🏻‍♂️", "name": "man facepalming: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F926 1F3FB 200D 2642 FE0F", "html": "🤦🏻‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤦🏻‍♂", "name": "man facepalming: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F926 1F3FB 200D 2642", "html": "🤦🏻‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤦🏼‍♂️", "name": "man facepalming: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F926 1F3FC 200D 2642 FE0F", "html": "🤦🏼‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤦🏼‍♂", "name": "man facepalming: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F926 1F3FC 200D 2642", "html": "🤦🏼‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤦🏽‍♂️", "name": "man facepalming: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F926 1F3FD 200D 2642 FE0F", "html": "🤦🏽‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤦🏽‍♂", "name": "man facepalming: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F926 1F3FD 200D 2642", "html": "🤦🏽‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤦🏾‍♂️", "name": "man facepalming: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F926 1F3FE 200D 2642 FE0F", "html": "🤦🏾‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤦🏾‍♂", "name": "man facepalming: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F926 1F3FE 200D 2642", "html": "🤦🏾‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤦🏿‍♂️", "name": "man facepalming: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F926 1F3FF 200D 2642 FE0F", "html": "🤦🏿‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤦🏿‍♂", "name": "man facepalming: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F926 1F3FF 200D 2642", "html": "🤦🏿‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤦‍♀️", "name": "woman facepalming", "shortname": ":woman_facepalming:", "unicode": "1F926 200D 2640 FE0F", "html": "🤦‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤦‍♀", "name": "woman facepalming", "shortname": ":woman_facepalming:", "unicode": "1F926 200D 2640", "html": "🤦‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤦🏻‍♀️", "name": "woman facepalming: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F926 1F3FB 200D 2640 FE0F", "html": "🤦🏻‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤦🏻‍♀", "name": "woman facepalming: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F926 1F3FB 200D 2640", "html": "🤦🏻‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤦🏼‍♀️", "name": "woman facepalming: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F926 1F3FC 200D 2640 FE0F", "html": "🤦🏼‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤦🏼‍♀", "name": "woman facepalming: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F926 1F3FC 200D 2640", "html": "🤦🏼‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤦🏽‍♀️", "name": "woman facepalming: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F926 1F3FD 200D 2640 FE0F", "html": "🤦🏽‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤦🏽‍♀", "name": "woman facepalming: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F926 1F3FD 200D 2640", "html": "🤦🏽‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤦🏾‍♀️", "name": "woman facepalming: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F926 1F3FE 200D 2640 FE0F", "html": "🤦🏾‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤦🏾‍♀", "name": "woman facepalming: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F926 1F3FE 200D 2640", "html": "🤦🏾‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤦🏿‍♀️", "name": "woman facepalming: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F926 1F3FF 200D 2640 FE0F", "html": "🤦🏿‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤦🏿‍♀", "name": "woman facepalming: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F926 1F3FF 200D 2640", "html": "🤦🏿‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤷", "name": "person shrugging", "shortname": ":person_shrugging:", "unicode": "1F937", "html": "🤷", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤷🏻", "name": "person shrugging: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F937 1F3FB", "html": "🤷🏻", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤷🏼", "name": "person shrugging: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F937 1F3FC", "html": "🤷🏼", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤷🏽", "name": "person shrugging: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F937 1F3FD", "html": "🤷🏽", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤷🏾", "name": "person shrugging: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F937 1F3FE", "html": "🤷🏾", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤷🏿", "name": "person shrugging: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F937 1F3FF", "html": "🤷🏿", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤷‍♂️", "name": "man shrugging", "shortname": ":man_shrugging:", "unicode": "1F937 200D 2642 FE0F", "html": "🤷‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤷‍♂", "name": "man shrugging", "shortname": ":man_shrugging:", "unicode": "1F937 200D 2642", "html": "🤷‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤷🏻‍♂️", "name": "man shrugging: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F937 1F3FB 200D 2642 FE0F", "html": "🤷🏻‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤷🏻‍♂", "name": "man shrugging: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F937 1F3FB 200D 2642", "html": "🤷🏻‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤷🏼‍♂️", "name": "man shrugging: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F937 1F3FC 200D 2642 FE0F", "html": "🤷🏼‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤷🏼‍♂", "name": "man shrugging: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F937 1F3FC 200D 2642", "html": "🤷🏼‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤷🏽‍♂️", "name": "man shrugging: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F937 1F3FD 200D 2642 FE0F", "html": "🤷🏽‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤷🏽‍♂", "name": "man shrugging: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F937 1F3FD 200D 2642", "html": "🤷🏽‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤷🏾‍♂️", "name": "man shrugging: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F937 1F3FE 200D 2642 FE0F", "html": "🤷🏾‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤷🏾‍♂", "name": "man shrugging: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F937 1F3FE 200D 2642", "html": "🤷🏾‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤷🏿‍♂️", "name": "man shrugging: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F937 1F3FF 200D 2642 FE0F", "html": "🤷🏿‍♂️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤷🏿‍♂", "name": "man shrugging: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F937 1F3FF 200D 2642", "html": "🤷🏿‍♂", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤷‍♀️", "name": "woman shrugging", "shortname": ":woman_shrugging:", "unicode": "1F937 200D 2640 FE0F", "html": "🤷‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤷‍♀", "name": "woman shrugging", "shortname": ":woman_shrugging:", "unicode": "1F937 200D 2640", "html": "🤷‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤷🏻‍♀️", "name": "woman shrugging: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F937 1F3FB 200D 2640 FE0F", "html": "🤷🏻‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤷🏻‍♀", "name": "woman shrugging: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F937 1F3FB 200D 2640", "html": "🤷🏻‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤷🏼‍♀️", "name": "woman shrugging: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F937 1F3FC 200D 2640 FE0F", "html": "🤷🏼‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤷🏼‍♀", "name": "woman shrugging: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F937 1F3FC 200D 2640", "html": "🤷🏼‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤷🏽‍♀️", "name": "woman shrugging: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F937 1F3FD 200D 2640 FE0F", "html": "🤷🏽‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤷🏽‍♀", "name": "woman shrugging: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F937 1F3FD 200D 2640", "html": "🤷🏽‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤷🏾‍♀️", "name": "woman shrugging: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F937 1F3FE 200D 2640 FE0F", "html": "🤷🏾‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤷🏾‍♀", "name": "woman shrugging: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F937 1F3FE 200D 2640", "html": "🤷🏾‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤷🏿‍♀️", "name": "woman shrugging: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F937 1F3FF 200D 2640 FE0F", "html": "🤷🏿‍♀️", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🤷🏿‍♀", "name": "woman shrugging: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F937 1F3FF 200D 2640", "html": "🤷🏿‍♀", "category": "People & Body (person-gesture)", "order": ""}, + {"emoji": "🧑‍⚕️", "name": "health worker", "shortname": ":health_worker:", "unicode": "1F9D1 200D 2695 FE0F", "html": "🧑‍⚕️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑‍⚕", "name": "health worker", "shortname": ":health_worker:", "unicode": "1F9D1 200D 2695", "html": "🧑‍⚕", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏻‍⚕️", "name": "health worker: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D1 1F3FB 200D 2695 FE0F", "html": "🧑🏻‍⚕️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏻‍⚕", "name": "health worker: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D1 1F3FB 200D 2695", "html": "🧑🏻‍⚕", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏼‍⚕️", "name": "health worker: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D1 1F3FC 200D 2695 FE0F", "html": "🧑🏼‍⚕️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏼‍⚕", "name": "health worker: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D1 1F3FC 200D 2695", "html": "🧑🏼‍⚕", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏽‍⚕️", "name": "health worker: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D1 1F3FD 200D 2695 FE0F", "html": "🧑🏽‍⚕️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏽‍⚕", "name": "health worker: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D1 1F3FD 200D 2695", "html": "🧑🏽‍⚕", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏾‍⚕️", "name": "health worker: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D1 1F3FE 200D 2695 FE0F", "html": "🧑🏾‍⚕️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏾‍⚕", "name": "health worker: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D1 1F3FE 200D 2695", "html": "🧑🏾‍⚕", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏿‍⚕️", "name": "health worker: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D1 1F3FF 200D 2695 FE0F", "html": "🧑🏿‍⚕️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏿‍⚕", "name": "health worker: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D1 1F3FF 200D 2695", "html": "🧑🏿‍⚕", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨‍⚕️", "name": "man health worker", "shortname": ":man_health_worker:", "unicode": "1F468 200D 2695 FE0F", "html": "👨‍⚕️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨‍⚕", "name": "man health worker", "shortname": ":man_health_worker:", "unicode": "1F468 200D 2695", "html": "👨‍⚕", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏻‍⚕️", "name": "man health worker: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F468 1F3FB 200D 2695 FE0F", "html": "👨🏻‍⚕️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏻‍⚕", "name": "man health worker: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F468 1F3FB 200D 2695", "html": "👨🏻‍⚕", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏼‍⚕️", "name": "man health worker: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F468 1F3FC 200D 2695 FE0F", "html": "👨🏼‍⚕️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏼‍⚕", "name": "man health worker: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F468 1F3FC 200D 2695", "html": "👨🏼‍⚕", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏽‍⚕️", "name": "man health worker: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F468 1F3FD 200D 2695 FE0F", "html": "👨🏽‍⚕️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏽‍⚕", "name": "man health worker: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F468 1F3FD 200D 2695", "html": "👨🏽‍⚕", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏾‍⚕️", "name": "man health worker: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F468 1F3FE 200D 2695 FE0F", "html": "👨🏾‍⚕️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏾‍⚕", "name": "man health worker: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F468 1F3FE 200D 2695", "html": "👨🏾‍⚕", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏿‍⚕️", "name": "man health worker: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F468 1F3FF 200D 2695 FE0F", "html": "👨🏿‍⚕️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏿‍⚕", "name": "man health worker: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F468 1F3FF 200D 2695", "html": "👨🏿‍⚕", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩‍⚕️", "name": "woman health worker", "shortname": ":woman_health_worker:", "unicode": "1F469 200D 2695 FE0F", "html": "👩‍⚕️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩‍⚕", "name": "woman health worker", "shortname": ":woman_health_worker:", "unicode": "1F469 200D 2695", "html": "👩‍⚕", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏻‍⚕️", "name": "woman health worker: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F469 1F3FB 200D 2695 FE0F", "html": "👩🏻‍⚕️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏻‍⚕", "name": "woman health worker: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F469 1F3FB 200D 2695", "html": "👩🏻‍⚕", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏼‍⚕️", "name": "woman health worker: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F469 1F3FC 200D 2695 FE0F", "html": "👩🏼‍⚕️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏼‍⚕", "name": "woman health worker: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F469 1F3FC 200D 2695", "html": "👩🏼‍⚕", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏽‍⚕️", "name": "woman health worker: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F469 1F3FD 200D 2695 FE0F", "html": "👩🏽‍⚕️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏽‍⚕", "name": "woman health worker: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F469 1F3FD 200D 2695", "html": "👩🏽‍⚕", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏾‍⚕️", "name": "woman health worker: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F469 1F3FE 200D 2695 FE0F", "html": "👩🏾‍⚕️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏾‍⚕", "name": "woman health worker: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F469 1F3FE 200D 2695", "html": "👩🏾‍⚕", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏿‍⚕️", "name": "woman health worker: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F469 1F3FF 200D 2695 FE0F", "html": "👩🏿‍⚕️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏿‍⚕", "name": "woman health worker: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F469 1F3FF 200D 2695", "html": "👩🏿‍⚕", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑‍🎓", "name": "student", "shortname": ":student:", "unicode": "1F9D1 200D 1F393", "html": "🧑‍🎓", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏻‍🎓", "name": "student: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D1 1F3FB 200D 1F393", "html": "🧑🏻‍🎓", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏼‍🎓", "name": "student: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D1 1F3FC 200D 1F393", "html": "🧑🏼‍🎓", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏽‍🎓", "name": "student: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D1 1F3FD 200D 1F393", "html": "🧑🏽‍🎓", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏾‍🎓", "name": "student: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D1 1F3FE 200D 1F393", "html": "🧑🏾‍🎓", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏿‍🎓", "name": "student: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D1 1F3FF 200D 1F393", "html": "🧑🏿‍🎓", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨‍🎓", "name": "man student", "shortname": ":man_student:", "unicode": "1F468 200D 1F393", "html": "👨‍🎓", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏻‍🎓", "name": "man student: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F468 1F3FB 200D 1F393", "html": "👨🏻‍🎓", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏼‍🎓", "name": "man student: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F468 1F3FC 200D 1F393", "html": "👨🏼‍🎓", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏽‍🎓", "name": "man student: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F468 1F3FD 200D 1F393", "html": "👨🏽‍🎓", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏾‍🎓", "name": "man student: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F468 1F3FE 200D 1F393", "html": "👨🏾‍🎓", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏿‍🎓", "name": "man student: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F468 1F3FF 200D 1F393", "html": "👨🏿‍🎓", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩‍🎓", "name": "woman student", "shortname": ":woman_student:", "unicode": "1F469 200D 1F393", "html": "👩‍🎓", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏻‍🎓", "name": "woman student: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F469 1F3FB 200D 1F393", "html": "👩🏻‍🎓", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏼‍🎓", "name": "woman student: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F469 1F3FC 200D 1F393", "html": "👩🏼‍🎓", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏽‍🎓", "name": "woman student: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F469 1F3FD 200D 1F393", "html": "👩🏽‍🎓", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏾‍🎓", "name": "woman student: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F469 1F3FE 200D 1F393", "html": "👩🏾‍🎓", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏿‍🎓", "name": "woman student: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F469 1F3FF 200D 1F393", "html": "👩🏿‍🎓", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑‍🏫", "name": "teacher", "shortname": ":teacher:", "unicode": "1F9D1 200D 1F3EB", "html": "🧑‍🏫", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏻‍🏫", "name": "teacher: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D1 1F3FB 200D 1F3EB", "html": "🧑🏻‍🏫", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏼‍🏫", "name": "teacher: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D1 1F3FC 200D 1F3EB", "html": "🧑🏼‍🏫", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏽‍🏫", "name": "teacher: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D1 1F3FD 200D 1F3EB", "html": "🧑🏽‍🏫", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏾‍🏫", "name": "teacher: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D1 1F3FE 200D 1F3EB", "html": "🧑🏾‍🏫", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏿‍🏫", "name": "teacher: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D1 1F3FF 200D 1F3EB", "html": "🧑🏿‍🏫", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨‍🏫", "name": "man teacher", "shortname": ":man_teacher:", "unicode": "1F468 200D 1F3EB", "html": "👨‍🏫", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏻‍🏫", "name": "man teacher: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F468 1F3FB 200D 1F3EB", "html": "👨🏻‍🏫", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏼‍🏫", "name": "man teacher: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F468 1F3FC 200D 1F3EB", "html": "👨🏼‍🏫", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏽‍🏫", "name": "man teacher: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F468 1F3FD 200D 1F3EB", "html": "👨🏽‍🏫", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏾‍🏫", "name": "man teacher: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F468 1F3FE 200D 1F3EB", "html": "👨🏾‍🏫", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏿‍🏫", "name": "man teacher: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F468 1F3FF 200D 1F3EB", "html": "👨🏿‍🏫", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩‍🏫", "name": "woman teacher", "shortname": ":woman_teacher:", "unicode": "1F469 200D 1F3EB", "html": "👩‍🏫", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏻‍🏫", "name": "woman teacher: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F469 1F3FB 200D 1F3EB", "html": "👩🏻‍🏫", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏼‍🏫", "name": "woman teacher: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F469 1F3FC 200D 1F3EB", "html": "👩🏼‍🏫", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏽‍🏫", "name": "woman teacher: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F469 1F3FD 200D 1F3EB", "html": "👩🏽‍🏫", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏾‍🏫", "name": "woman teacher: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F469 1F3FE 200D 1F3EB", "html": "👩🏾‍🏫", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏿‍🏫", "name": "woman teacher: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F469 1F3FF 200D 1F3EB", "html": "👩🏿‍🏫", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑‍⚖️", "name": "judge", "shortname": ":judge:", "unicode": "1F9D1 200D 2696 FE0F", "html": "🧑‍⚖️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑‍⚖", "name": "judge", "shortname": ":judge:", "unicode": "1F9D1 200D 2696", "html": "🧑‍⚖", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏻‍⚖️", "name": "judge: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D1 1F3FB 200D 2696 FE0F", "html": "🧑🏻‍⚖️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏻‍⚖", "name": "judge: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D1 1F3FB 200D 2696", "html": "🧑🏻‍⚖", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏼‍⚖️", "name": "judge: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D1 1F3FC 200D 2696 FE0F", "html": "🧑🏼‍⚖️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏼‍⚖", "name": "judge: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D1 1F3FC 200D 2696", "html": "🧑🏼‍⚖", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏽‍⚖️", "name": "judge: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D1 1F3FD 200D 2696 FE0F", "html": "🧑🏽‍⚖️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏽‍⚖", "name": "judge: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D1 1F3FD 200D 2696", "html": "🧑🏽‍⚖", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏾‍⚖️", "name": "judge: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D1 1F3FE 200D 2696 FE0F", "html": "🧑🏾‍⚖️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏾‍⚖", "name": "judge: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D1 1F3FE 200D 2696", "html": "🧑🏾‍⚖", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏿‍⚖️", "name": "judge: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D1 1F3FF 200D 2696 FE0F", "html": "🧑🏿‍⚖️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏿‍⚖", "name": "judge: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D1 1F3FF 200D 2696", "html": "🧑🏿‍⚖", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨‍⚖️", "name": "man judge", "shortname": ":man_judge:", "unicode": "1F468 200D 2696 FE0F", "html": "👨‍⚖️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨‍⚖", "name": "man judge", "shortname": ":man_judge:", "unicode": "1F468 200D 2696", "html": "👨‍⚖", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏻‍⚖️", "name": "man judge: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F468 1F3FB 200D 2696 FE0F", "html": "👨🏻‍⚖️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏻‍⚖", "name": "man judge: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F468 1F3FB 200D 2696", "html": "👨🏻‍⚖", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏼‍⚖️", "name": "man judge: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F468 1F3FC 200D 2696 FE0F", "html": "👨🏼‍⚖️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏼‍⚖", "name": "man judge: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F468 1F3FC 200D 2696", "html": "👨🏼‍⚖", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏽‍⚖️", "name": "man judge: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F468 1F3FD 200D 2696 FE0F", "html": "👨🏽‍⚖️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏽‍⚖", "name": "man judge: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F468 1F3FD 200D 2696", "html": "👨🏽‍⚖", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏾‍⚖️", "name": "man judge: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F468 1F3FE 200D 2696 FE0F", "html": "👨🏾‍⚖️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏾‍⚖", "name": "man judge: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F468 1F3FE 200D 2696", "html": "👨🏾‍⚖", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏿‍⚖️", "name": "man judge: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F468 1F3FF 200D 2696 FE0F", "html": "👨🏿‍⚖️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏿‍⚖", "name": "man judge: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F468 1F3FF 200D 2696", "html": "👨🏿‍⚖", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩‍⚖️", "name": "woman judge", "shortname": ":woman_judge:", "unicode": "1F469 200D 2696 FE0F", "html": "👩‍⚖️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩‍⚖", "name": "woman judge", "shortname": ":woman_judge:", "unicode": "1F469 200D 2696", "html": "👩‍⚖", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏻‍⚖️", "name": "woman judge: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F469 1F3FB 200D 2696 FE0F", "html": "👩🏻‍⚖️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏻‍⚖", "name": "woman judge: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F469 1F3FB 200D 2696", "html": "👩🏻‍⚖", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏼‍⚖️", "name": "woman judge: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F469 1F3FC 200D 2696 FE0F", "html": "👩🏼‍⚖️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏼‍⚖", "name": "woman judge: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F469 1F3FC 200D 2696", "html": "👩🏼‍⚖", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏽‍⚖️", "name": "woman judge: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F469 1F3FD 200D 2696 FE0F", "html": "👩🏽‍⚖️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏽‍⚖", "name": "woman judge: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F469 1F3FD 200D 2696", "html": "👩🏽‍⚖", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏾‍⚖️", "name": "woman judge: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F469 1F3FE 200D 2696 FE0F", "html": "👩🏾‍⚖️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏾‍⚖", "name": "woman judge: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F469 1F3FE 200D 2696", "html": "👩🏾‍⚖", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏿‍⚖️", "name": "woman judge: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F469 1F3FF 200D 2696 FE0F", "html": "👩🏿‍⚖️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏿‍⚖", "name": "woman judge: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F469 1F3FF 200D 2696", "html": "👩🏿‍⚖", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑‍🌾", "name": "farmer", "shortname": ":farmer:", "unicode": "1F9D1 200D 1F33E", "html": "🧑‍🌾", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏻‍🌾", "name": "farmer: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D1 1F3FB 200D 1F33E", "html": "🧑🏻‍🌾", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏼‍🌾", "name": "farmer: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D1 1F3FC 200D 1F33E", "html": "🧑🏼‍🌾", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏽‍🌾", "name": "farmer: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D1 1F3FD 200D 1F33E", "html": "🧑🏽‍🌾", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏾‍🌾", "name": "farmer: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D1 1F3FE 200D 1F33E", "html": "🧑🏾‍🌾", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏿‍🌾", "name": "farmer: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D1 1F3FF 200D 1F33E", "html": "🧑🏿‍🌾", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨‍🌾", "name": "man farmer", "shortname": ":man_farmer:", "unicode": "1F468 200D 1F33E", "html": "👨‍🌾", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏻‍🌾", "name": "man farmer: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F468 1F3FB 200D 1F33E", "html": "👨🏻‍🌾", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏼‍🌾", "name": "man farmer: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F468 1F3FC 200D 1F33E", "html": "👨🏼‍🌾", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏽‍🌾", "name": "man farmer: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F468 1F3FD 200D 1F33E", "html": "👨🏽‍🌾", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏾‍🌾", "name": "man farmer: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F468 1F3FE 200D 1F33E", "html": "👨🏾‍🌾", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏿‍🌾", "name": "man farmer: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F468 1F3FF 200D 1F33E", "html": "👨🏿‍🌾", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩‍🌾", "name": "woman farmer", "shortname": ":woman_farmer:", "unicode": "1F469 200D 1F33E", "html": "👩‍🌾", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏻‍🌾", "name": "woman farmer: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F469 1F3FB 200D 1F33E", "html": "👩🏻‍🌾", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏼‍🌾", "name": "woman farmer: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F469 1F3FC 200D 1F33E", "html": "👩🏼‍🌾", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏽‍🌾", "name": "woman farmer: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F469 1F3FD 200D 1F33E", "html": "👩🏽‍🌾", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏾‍🌾", "name": "woman farmer: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F469 1F3FE 200D 1F33E", "html": "👩🏾‍🌾", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏿‍🌾", "name": "woman farmer: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F469 1F3FF 200D 1F33E", "html": "👩🏿‍🌾", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑‍🍳", "name": "cook", "shortname": ":cook:", "unicode": "1F9D1 200D 1F373", "html": "🧑‍🍳", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏻‍🍳", "name": "cook: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D1 1F3FB 200D 1F373", "html": "🧑🏻‍🍳", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏼‍🍳", "name": "cook: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D1 1F3FC 200D 1F373", "html": "🧑🏼‍🍳", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏽‍🍳", "name": "cook: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D1 1F3FD 200D 1F373", "html": "🧑🏽‍🍳", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏾‍🍳", "name": "cook: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D1 1F3FE 200D 1F373", "html": "🧑🏾‍🍳", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏿‍🍳", "name": "cook: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D1 1F3FF 200D 1F373", "html": "🧑🏿‍🍳", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨‍🍳", "name": "man cook", "shortname": ":man_cook:", "unicode": "1F468 200D 1F373", "html": "👨‍🍳", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏻‍🍳", "name": "man cook: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F468 1F3FB 200D 1F373", "html": "👨🏻‍🍳", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏼‍🍳", "name": "man cook: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F468 1F3FC 200D 1F373", "html": "👨🏼‍🍳", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏽‍🍳", "name": "man cook: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F468 1F3FD 200D 1F373", "html": "👨🏽‍🍳", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏾‍🍳", "name": "man cook: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F468 1F3FE 200D 1F373", "html": "👨🏾‍🍳", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏿‍🍳", "name": "man cook: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F468 1F3FF 200D 1F373", "html": "👨🏿‍🍳", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩‍🍳", "name": "woman cook", "shortname": ":woman_cook:", "unicode": "1F469 200D 1F373", "html": "👩‍🍳", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏻‍🍳", "name": "woman cook: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F469 1F3FB 200D 1F373", "html": "👩🏻‍🍳", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏼‍🍳", "name": "woman cook: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F469 1F3FC 200D 1F373", "html": "👩🏼‍🍳", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏽‍🍳", "name": "woman cook: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F469 1F3FD 200D 1F373", "html": "👩🏽‍🍳", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏾‍🍳", "name": "woman cook: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F469 1F3FE 200D 1F373", "html": "👩🏾‍🍳", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏿‍🍳", "name": "woman cook: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F469 1F3FF 200D 1F373", "html": "👩🏿‍🍳", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑‍🔧", "name": "mechanic", "shortname": ":mechanic:", "unicode": "1F9D1 200D 1F527", "html": "🧑‍🔧", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏻‍🔧", "name": "mechanic: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D1 1F3FB 200D 1F527", "html": "🧑🏻‍🔧", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏼‍🔧", "name": "mechanic: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D1 1F3FC 200D 1F527", "html": "🧑🏼‍🔧", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏽‍🔧", "name": "mechanic: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D1 1F3FD 200D 1F527", "html": "🧑🏽‍🔧", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏾‍🔧", "name": "mechanic: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D1 1F3FE 200D 1F527", "html": "🧑🏾‍🔧", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏿‍🔧", "name": "mechanic: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D1 1F3FF 200D 1F527", "html": "🧑🏿‍🔧", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨‍🔧", "name": "man mechanic", "shortname": ":man_mechanic:", "unicode": "1F468 200D 1F527", "html": "👨‍🔧", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏻‍🔧", "name": "man mechanic: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F468 1F3FB 200D 1F527", "html": "👨🏻‍🔧", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏼‍🔧", "name": "man mechanic: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F468 1F3FC 200D 1F527", "html": "👨🏼‍🔧", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏽‍🔧", "name": "man mechanic: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F468 1F3FD 200D 1F527", "html": "👨🏽‍🔧", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏾‍🔧", "name": "man mechanic: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F468 1F3FE 200D 1F527", "html": "👨🏾‍🔧", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏿‍🔧", "name": "man mechanic: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F468 1F3FF 200D 1F527", "html": "👨🏿‍🔧", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩‍🔧", "name": "woman mechanic", "shortname": ":woman_mechanic:", "unicode": "1F469 200D 1F527", "html": "👩‍🔧", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏻‍🔧", "name": "woman mechanic: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F469 1F3FB 200D 1F527", "html": "👩🏻‍🔧", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏼‍🔧", "name": "woman mechanic: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F469 1F3FC 200D 1F527", "html": "👩🏼‍🔧", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏽‍🔧", "name": "woman mechanic: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F469 1F3FD 200D 1F527", "html": "👩🏽‍🔧", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏾‍🔧", "name": "woman mechanic: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F469 1F3FE 200D 1F527", "html": "👩🏾‍🔧", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏿‍🔧", "name": "woman mechanic: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F469 1F3FF 200D 1F527", "html": "👩🏿‍🔧", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑‍🏭", "name": "factory worker", "shortname": ":factory_worker:", "unicode": "1F9D1 200D 1F3ED", "html": "🧑‍🏭", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏻‍🏭", "name": "factory worker: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D1 1F3FB 200D 1F3ED", "html": "🧑🏻‍🏭", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏼‍🏭", "name": "factory worker: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D1 1F3FC 200D 1F3ED", "html": "🧑🏼‍🏭", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏽‍🏭", "name": "factory worker: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D1 1F3FD 200D 1F3ED", "html": "🧑🏽‍🏭", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏾‍🏭", "name": "factory worker: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D1 1F3FE 200D 1F3ED", "html": "🧑🏾‍🏭", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏿‍🏭", "name": "factory worker: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D1 1F3FF 200D 1F3ED", "html": "🧑🏿‍🏭", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨‍🏭", "name": "man factory worker", "shortname": ":man_factory_worker:", "unicode": "1F468 200D 1F3ED", "html": "👨‍🏭", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏻‍🏭", "name": "man factory worker: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F468 1F3FB 200D 1F3ED", "html": "👨🏻‍🏭", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏼‍🏭", "name": "man factory worker: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F468 1F3FC 200D 1F3ED", "html": "👨🏼‍🏭", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏽‍🏭", "name": "man factory worker: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F468 1F3FD 200D 1F3ED", "html": "👨🏽‍🏭", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏾‍🏭", "name": "man factory worker: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F468 1F3FE 200D 1F3ED", "html": "👨🏾‍🏭", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏿‍🏭", "name": "man factory worker: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F468 1F3FF 200D 1F3ED", "html": "👨🏿‍🏭", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩‍🏭", "name": "woman factory worker", "shortname": ":woman_factory_worker:", "unicode": "1F469 200D 1F3ED", "html": "👩‍🏭", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏻‍🏭", "name": "woman factory worker: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F469 1F3FB 200D 1F3ED", "html": "👩🏻‍🏭", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏼‍🏭", "name": "woman factory worker: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F469 1F3FC 200D 1F3ED", "html": "👩🏼‍🏭", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏽‍🏭", "name": "woman factory worker: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F469 1F3FD 200D 1F3ED", "html": "👩🏽‍🏭", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏾‍🏭", "name": "woman factory worker: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F469 1F3FE 200D 1F3ED", "html": "👩🏾‍🏭", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏿‍🏭", "name": "woman factory worker: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F469 1F3FF 200D 1F3ED", "html": "👩🏿‍🏭", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑‍💼", "name": "office worker", "shortname": ":office_worker:", "unicode": "1F9D1 200D 1F4BC", "html": "🧑‍💼", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏻‍💼", "name": "office worker: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D1 1F3FB 200D 1F4BC", "html": "🧑🏻‍💼", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏼‍💼", "name": "office worker: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D1 1F3FC 200D 1F4BC", "html": "🧑🏼‍💼", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏽‍💼", "name": "office worker: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D1 1F3FD 200D 1F4BC", "html": "🧑🏽‍💼", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏾‍💼", "name": "office worker: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D1 1F3FE 200D 1F4BC", "html": "🧑🏾‍💼", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏿‍💼", "name": "office worker: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D1 1F3FF 200D 1F4BC", "html": "🧑🏿‍💼", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨‍💼", "name": "man office worker", "shortname": ":man_office_worker:", "unicode": "1F468 200D 1F4BC", "html": "👨‍💼", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏻‍💼", "name": "man office worker: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F468 1F3FB 200D 1F4BC", "html": "👨🏻‍💼", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏼‍💼", "name": "man office worker: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F468 1F3FC 200D 1F4BC", "html": "👨🏼‍💼", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏽‍💼", "name": "man office worker: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F468 1F3FD 200D 1F4BC", "html": "👨🏽‍💼", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏾‍💼", "name": "man office worker: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F468 1F3FE 200D 1F4BC", "html": "👨🏾‍💼", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏿‍💼", "name": "man office worker: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F468 1F3FF 200D 1F4BC", "html": "👨🏿‍💼", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩‍💼", "name": "woman office worker", "shortname": ":woman_office_worker:", "unicode": "1F469 200D 1F4BC", "html": "👩‍💼", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏻‍💼", "name": "woman office worker: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F469 1F3FB 200D 1F4BC", "html": "👩🏻‍💼", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏼‍💼", "name": "woman office worker: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F469 1F3FC 200D 1F4BC", "html": "👩🏼‍💼", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏽‍💼", "name": "woman office worker: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F469 1F3FD 200D 1F4BC", "html": "👩🏽‍💼", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏾‍💼", "name": "woman office worker: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F469 1F3FE 200D 1F4BC", "html": "👩🏾‍💼", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏿‍💼", "name": "woman office worker: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F469 1F3FF 200D 1F4BC", "html": "👩🏿‍💼", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑‍🔬", "name": "scientist", "shortname": ":scientist:", "unicode": "1F9D1 200D 1F52C", "html": "🧑‍🔬", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏻‍🔬", "name": "scientist: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D1 1F3FB 200D 1F52C", "html": "🧑🏻‍🔬", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏼‍🔬", "name": "scientist: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D1 1F3FC 200D 1F52C", "html": "🧑🏼‍🔬", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏽‍🔬", "name": "scientist: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D1 1F3FD 200D 1F52C", "html": "🧑🏽‍🔬", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏾‍🔬", "name": "scientist: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D1 1F3FE 200D 1F52C", "html": "🧑🏾‍🔬", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏿‍🔬", "name": "scientist: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D1 1F3FF 200D 1F52C", "html": "🧑🏿‍🔬", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨‍🔬", "name": "man scientist", "shortname": ":man_scientist:", "unicode": "1F468 200D 1F52C", "html": "👨‍🔬", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏻‍🔬", "name": "man scientist: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F468 1F3FB 200D 1F52C", "html": "👨🏻‍🔬", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏼‍🔬", "name": "man scientist: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F468 1F3FC 200D 1F52C", "html": "👨🏼‍🔬", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏽‍🔬", "name": "man scientist: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F468 1F3FD 200D 1F52C", "html": "👨🏽‍🔬", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏾‍🔬", "name": "man scientist: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F468 1F3FE 200D 1F52C", "html": "👨🏾‍🔬", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏿‍🔬", "name": "man scientist: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F468 1F3FF 200D 1F52C", "html": "👨🏿‍🔬", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩‍🔬", "name": "woman scientist", "shortname": ":woman_scientist:", "unicode": "1F469 200D 1F52C", "html": "👩‍🔬", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏻‍🔬", "name": "woman scientist: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F469 1F3FB 200D 1F52C", "html": "👩🏻‍🔬", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏼‍🔬", "name": "woman scientist: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F469 1F3FC 200D 1F52C", "html": "👩🏼‍🔬", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏽‍🔬", "name": "woman scientist: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F469 1F3FD 200D 1F52C", "html": "👩🏽‍🔬", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏾‍🔬", "name": "woman scientist: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F469 1F3FE 200D 1F52C", "html": "👩🏾‍🔬", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏿‍🔬", "name": "woman scientist: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F469 1F3FF 200D 1F52C", "html": "👩🏿‍🔬", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑‍💻", "name": "technologist", "shortname": ":technologist:", "unicode": "1F9D1 200D 1F4BB", "html": "🧑‍💻", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏻‍💻", "name": "technologist: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D1 1F3FB 200D 1F4BB", "html": "🧑🏻‍💻", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏼‍💻", "name": "technologist: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D1 1F3FC 200D 1F4BB", "html": "🧑🏼‍💻", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏽‍💻", "name": "technologist: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D1 1F3FD 200D 1F4BB", "html": "🧑🏽‍💻", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏾‍💻", "name": "technologist: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D1 1F3FE 200D 1F4BB", "html": "🧑🏾‍💻", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏿‍💻", "name": "technologist: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D1 1F3FF 200D 1F4BB", "html": "🧑🏿‍💻", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨‍💻", "name": "man technologist", "shortname": ":man_technologist:", "unicode": "1F468 200D 1F4BB", "html": "👨‍💻", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏻‍💻", "name": "man technologist: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F468 1F3FB 200D 1F4BB", "html": "👨🏻‍💻", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏼‍💻", "name": "man technologist: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F468 1F3FC 200D 1F4BB", "html": "👨🏼‍💻", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏽‍💻", "name": "man technologist: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F468 1F3FD 200D 1F4BB", "html": "👨🏽‍💻", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏾‍💻", "name": "man technologist: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F468 1F3FE 200D 1F4BB", "html": "👨🏾‍💻", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏿‍💻", "name": "man technologist: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F468 1F3FF 200D 1F4BB", "html": "👨🏿‍💻", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩‍💻", "name": "woman technologist", "shortname": ":woman_technologist:", "unicode": "1F469 200D 1F4BB", "html": "👩‍💻", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏻‍💻", "name": "woman technologist: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F469 1F3FB 200D 1F4BB", "html": "👩🏻‍💻", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏼‍💻", "name": "woman technologist: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F469 1F3FC 200D 1F4BB", "html": "👩🏼‍💻", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏽‍💻", "name": "woman technologist: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F469 1F3FD 200D 1F4BB", "html": "👩🏽‍💻", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏾‍💻", "name": "woman technologist: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F469 1F3FE 200D 1F4BB", "html": "👩🏾‍💻", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏿‍💻", "name": "woman technologist: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F469 1F3FF 200D 1F4BB", "html": "👩🏿‍💻", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑‍🎤", "name": "singer", "shortname": ":singer:", "unicode": "1F9D1 200D 1F3A4", "html": "🧑‍🎤", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏻‍🎤", "name": "singer: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D1 1F3FB 200D 1F3A4", "html": "🧑🏻‍🎤", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏼‍🎤", "name": "singer: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D1 1F3FC 200D 1F3A4", "html": "🧑🏼‍🎤", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏽‍🎤", "name": "singer: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D1 1F3FD 200D 1F3A4", "html": "🧑🏽‍🎤", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏾‍🎤", "name": "singer: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D1 1F3FE 200D 1F3A4", "html": "🧑🏾‍🎤", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏿‍🎤", "name": "singer: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D1 1F3FF 200D 1F3A4", "html": "🧑🏿‍🎤", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨‍🎤", "name": "man singer", "shortname": ":man_singer:", "unicode": "1F468 200D 1F3A4", "html": "👨‍🎤", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏻‍🎤", "name": "man singer: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F468 1F3FB 200D 1F3A4", "html": "👨🏻‍🎤", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏼‍🎤", "name": "man singer: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F468 1F3FC 200D 1F3A4", "html": "👨🏼‍🎤", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏽‍🎤", "name": "man singer: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F468 1F3FD 200D 1F3A4", "html": "👨🏽‍🎤", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏾‍🎤", "name": "man singer: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F468 1F3FE 200D 1F3A4", "html": "👨🏾‍🎤", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏿‍🎤", "name": "man singer: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F468 1F3FF 200D 1F3A4", "html": "👨🏿‍🎤", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩‍🎤", "name": "woman singer", "shortname": ":woman_singer:", "unicode": "1F469 200D 1F3A4", "html": "👩‍🎤", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏻‍🎤", "name": "woman singer: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F469 1F3FB 200D 1F3A4", "html": "👩🏻‍🎤", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏼‍🎤", "name": "woman singer: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F469 1F3FC 200D 1F3A4", "html": "👩🏼‍🎤", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏽‍🎤", "name": "woman singer: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F469 1F3FD 200D 1F3A4", "html": "👩🏽‍🎤", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏾‍🎤", "name": "woman singer: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F469 1F3FE 200D 1F3A4", "html": "👩🏾‍🎤", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏿‍🎤", "name": "woman singer: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F469 1F3FF 200D 1F3A4", "html": "👩🏿‍🎤", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑‍🎨", "name": "artist", "shortname": ":artist:", "unicode": "1F9D1 200D 1F3A8", "html": "🧑‍🎨", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏻‍🎨", "name": "artist: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D1 1F3FB 200D 1F3A8", "html": "🧑🏻‍🎨", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏼‍🎨", "name": "artist: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D1 1F3FC 200D 1F3A8", "html": "🧑🏼‍🎨", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏽‍🎨", "name": "artist: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D1 1F3FD 200D 1F3A8", "html": "🧑🏽‍🎨", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏾‍🎨", "name": "artist: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D1 1F3FE 200D 1F3A8", "html": "🧑🏾‍🎨", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏿‍🎨", "name": "artist: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D1 1F3FF 200D 1F3A8", "html": "🧑🏿‍🎨", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨‍🎨", "name": "man artist", "shortname": ":man_artist:", "unicode": "1F468 200D 1F3A8", "html": "👨‍🎨", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏻‍🎨", "name": "man artist: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F468 1F3FB 200D 1F3A8", "html": "👨🏻‍🎨", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏼‍🎨", "name": "man artist: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F468 1F3FC 200D 1F3A8", "html": "👨🏼‍🎨", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏽‍🎨", "name": "man artist: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F468 1F3FD 200D 1F3A8", "html": "👨🏽‍🎨", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏾‍🎨", "name": "man artist: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F468 1F3FE 200D 1F3A8", "html": "👨🏾‍🎨", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏿‍🎨", "name": "man artist: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F468 1F3FF 200D 1F3A8", "html": "👨🏿‍🎨", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩‍🎨", "name": "woman artist", "shortname": ":woman_artist:", "unicode": "1F469 200D 1F3A8", "html": "👩‍🎨", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏻‍🎨", "name": "woman artist: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F469 1F3FB 200D 1F3A8", "html": "👩🏻‍🎨", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏼‍🎨", "name": "woman artist: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F469 1F3FC 200D 1F3A8", "html": "👩🏼‍🎨", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏽‍🎨", "name": "woman artist: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F469 1F3FD 200D 1F3A8", "html": "👩🏽‍🎨", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏾‍🎨", "name": "woman artist: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F469 1F3FE 200D 1F3A8", "html": "👩🏾‍🎨", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏿‍🎨", "name": "woman artist: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F469 1F3FF 200D 1F3A8", "html": "👩🏿‍🎨", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑‍✈️", "name": "pilot", "shortname": ":pilot:", "unicode": "1F9D1 200D 2708 FE0F", "html": "🧑‍✈️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑‍✈", "name": "pilot", "shortname": ":pilot:", "unicode": "1F9D1 200D 2708", "html": "🧑‍✈", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏻‍✈️", "name": "pilot: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D1 1F3FB 200D 2708 FE0F", "html": "🧑🏻‍✈️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏻‍✈", "name": "pilot: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D1 1F3FB 200D 2708", "html": "🧑🏻‍✈", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏼‍✈️", "name": "pilot: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D1 1F3FC 200D 2708 FE0F", "html": "🧑🏼‍✈️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏼‍✈", "name": "pilot: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D1 1F3FC 200D 2708", "html": "🧑🏼‍✈", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏽‍✈️", "name": "pilot: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D1 1F3FD 200D 2708 FE0F", "html": "🧑🏽‍✈️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏽‍✈", "name": "pilot: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D1 1F3FD 200D 2708", "html": "🧑🏽‍✈", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏾‍✈️", "name": "pilot: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D1 1F3FE 200D 2708 FE0F", "html": "🧑🏾‍✈️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏾‍✈", "name": "pilot: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D1 1F3FE 200D 2708", "html": "🧑🏾‍✈", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏿‍✈️", "name": "pilot: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D1 1F3FF 200D 2708 FE0F", "html": "🧑🏿‍✈️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏿‍✈", "name": "pilot: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D1 1F3FF 200D 2708", "html": "🧑🏿‍✈", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨‍✈️", "name": "man pilot", "shortname": ":man_pilot:", "unicode": "1F468 200D 2708 FE0F", "html": "👨‍✈️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨‍✈", "name": "man pilot", "shortname": ":man_pilot:", "unicode": "1F468 200D 2708", "html": "👨‍✈", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏻‍✈️", "name": "man pilot: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F468 1F3FB 200D 2708 FE0F", "html": "👨🏻‍✈️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏻‍✈", "name": "man pilot: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F468 1F3FB 200D 2708", "html": "👨🏻‍✈", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏼‍✈️", "name": "man pilot: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F468 1F3FC 200D 2708 FE0F", "html": "👨🏼‍✈️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏼‍✈", "name": "man pilot: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F468 1F3FC 200D 2708", "html": "👨🏼‍✈", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏽‍✈️", "name": "man pilot: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F468 1F3FD 200D 2708 FE0F", "html": "👨🏽‍✈️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏽‍✈", "name": "man pilot: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F468 1F3FD 200D 2708", "html": "👨🏽‍✈", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏾‍✈️", "name": "man pilot: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F468 1F3FE 200D 2708 FE0F", "html": "👨🏾‍✈️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏾‍✈", "name": "man pilot: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F468 1F3FE 200D 2708", "html": "👨🏾‍✈", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏿‍✈️", "name": "man pilot: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F468 1F3FF 200D 2708 FE0F", "html": "👨🏿‍✈️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏿‍✈", "name": "man pilot: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F468 1F3FF 200D 2708", "html": "👨🏿‍✈", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩‍✈️", "name": "woman pilot", "shortname": ":woman_pilot:", "unicode": "1F469 200D 2708 FE0F", "html": "👩‍✈️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩‍✈", "name": "woman pilot", "shortname": ":woman_pilot:", "unicode": "1F469 200D 2708", "html": "👩‍✈", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏻‍✈️", "name": "woman pilot: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F469 1F3FB 200D 2708 FE0F", "html": "👩🏻‍✈️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏻‍✈", "name": "woman pilot: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F469 1F3FB 200D 2708", "html": "👩🏻‍✈", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏼‍✈️", "name": "woman pilot: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F469 1F3FC 200D 2708 FE0F", "html": "👩🏼‍✈️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏼‍✈", "name": "woman pilot: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F469 1F3FC 200D 2708", "html": "👩🏼‍✈", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏽‍✈️", "name": "woman pilot: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F469 1F3FD 200D 2708 FE0F", "html": "👩🏽‍✈️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏽‍✈", "name": "woman pilot: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F469 1F3FD 200D 2708", "html": "👩🏽‍✈", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏾‍✈️", "name": "woman pilot: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F469 1F3FE 200D 2708 FE0F", "html": "👩🏾‍✈️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏾‍✈", "name": "woman pilot: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F469 1F3FE 200D 2708", "html": "👩🏾‍✈", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏿‍✈️", "name": "woman pilot: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F469 1F3FF 200D 2708 FE0F", "html": "👩🏿‍✈️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏿‍✈", "name": "woman pilot: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F469 1F3FF 200D 2708", "html": "👩🏿‍✈", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑‍🚀", "name": "astronaut", "shortname": ":astronaut:", "unicode": "1F9D1 200D 1F680", "html": "🧑‍🚀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏻‍🚀", "name": "astronaut: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D1 1F3FB 200D 1F680", "html": "🧑🏻‍🚀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏼‍🚀", "name": "astronaut: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D1 1F3FC 200D 1F680", "html": "🧑🏼‍🚀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏽‍🚀", "name": "astronaut: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D1 1F3FD 200D 1F680", "html": "🧑🏽‍🚀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏾‍🚀", "name": "astronaut: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D1 1F3FE 200D 1F680", "html": "🧑🏾‍🚀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏿‍🚀", "name": "astronaut: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D1 1F3FF 200D 1F680", "html": "🧑🏿‍🚀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨‍🚀", "name": "man astronaut", "shortname": ":man_astronaut:", "unicode": "1F468 200D 1F680", "html": "👨‍🚀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏻‍🚀", "name": "man astronaut: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F468 1F3FB 200D 1F680", "html": "👨🏻‍🚀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏼‍🚀", "name": "man astronaut: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F468 1F3FC 200D 1F680", "html": "👨🏼‍🚀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏽‍🚀", "name": "man astronaut: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F468 1F3FD 200D 1F680", "html": "👨🏽‍🚀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏾‍🚀", "name": "man astronaut: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F468 1F3FE 200D 1F680", "html": "👨🏾‍🚀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏿‍🚀", "name": "man astronaut: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F468 1F3FF 200D 1F680", "html": "👨🏿‍🚀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩‍🚀", "name": "woman astronaut", "shortname": ":woman_astronaut:", "unicode": "1F469 200D 1F680", "html": "👩‍🚀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏻‍🚀", "name": "woman astronaut: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F469 1F3FB 200D 1F680", "html": "👩🏻‍🚀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏼‍🚀", "name": "woman astronaut: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F469 1F3FC 200D 1F680", "html": "👩🏼‍🚀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏽‍🚀", "name": "woman astronaut: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F469 1F3FD 200D 1F680", "html": "👩🏽‍🚀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏾‍🚀", "name": "woman astronaut: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F469 1F3FE 200D 1F680", "html": "👩🏾‍🚀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏿‍🚀", "name": "woman astronaut: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F469 1F3FF 200D 1F680", "html": "👩🏿‍🚀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑‍🚒", "name": "firefighter", "shortname": ":firefighter:", "unicode": "1F9D1 200D 1F692", "html": "🧑‍🚒", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏻‍🚒", "name": "firefighter: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D1 1F3FB 200D 1F692", "html": "🧑🏻‍🚒", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏼‍🚒", "name": "firefighter: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D1 1F3FC 200D 1F692", "html": "🧑🏼‍🚒", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏽‍🚒", "name": "firefighter: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D1 1F3FD 200D 1F692", "html": "🧑🏽‍🚒", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏾‍🚒", "name": "firefighter: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D1 1F3FE 200D 1F692", "html": "🧑🏾‍🚒", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧑🏿‍🚒", "name": "firefighter: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D1 1F3FF 200D 1F692", "html": "🧑🏿‍🚒", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨‍🚒", "name": "man firefighter", "shortname": ":man_firefighter:", "unicode": "1F468 200D 1F692", "html": "👨‍🚒", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏻‍🚒", "name": "man firefighter: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F468 1F3FB 200D 1F692", "html": "👨🏻‍🚒", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏼‍🚒", "name": "man firefighter: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F468 1F3FC 200D 1F692", "html": "👨🏼‍🚒", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏽‍🚒", "name": "man firefighter: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F468 1F3FD 200D 1F692", "html": "👨🏽‍🚒", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏾‍🚒", "name": "man firefighter: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F468 1F3FE 200D 1F692", "html": "👨🏾‍🚒", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👨🏿‍🚒", "name": "man firefighter: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F468 1F3FF 200D 1F692", "html": "👨🏿‍🚒", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩‍🚒", "name": "woman firefighter", "shortname": ":woman_firefighter:", "unicode": "1F469 200D 1F692", "html": "👩‍🚒", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏻‍🚒", "name": "woman firefighter: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F469 1F3FB 200D 1F692", "html": "👩🏻‍🚒", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏼‍🚒", "name": "woman firefighter: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F469 1F3FC 200D 1F692", "html": "👩🏼‍🚒", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏽‍🚒", "name": "woman firefighter: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F469 1F3FD 200D 1F692", "html": "👩🏽‍🚒", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏾‍🚒", "name": "woman firefighter: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F469 1F3FE 200D 1F692", "html": "👩🏾‍🚒", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👩🏿‍🚒", "name": "woman firefighter: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F469 1F3FF 200D 1F692", "html": "👩🏿‍🚒", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👮‍♂️", "name": "man police officer", "shortname": ":man_police_officer:", "unicode": "1F46E 200D 2642 FE0F", "html": "👮‍♂️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👮‍♂", "name": "man police officer", "shortname": ":man_police_officer:", "unicode": "1F46E 200D 2642", "html": "👮‍♂", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👮🏻‍♂️", "name": "man police officer: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F46E 1F3FB 200D 2642 FE0F", "html": "👮🏻‍♂️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👮🏻‍♂", "name": "man police officer: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F46E 1F3FB 200D 2642", "html": "👮🏻‍♂", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👮🏼‍♂️", "name": "man police officer: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F46E 1F3FC 200D 2642 FE0F", "html": "👮🏼‍♂️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👮🏼‍♂", "name": "man police officer: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F46E 1F3FC 200D 2642", "html": "👮🏼‍♂", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👮🏽‍♂️", "name": "man police officer: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F46E 1F3FD 200D 2642 FE0F", "html": "👮🏽‍♂️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👮🏽‍♂", "name": "man police officer: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F46E 1F3FD 200D 2642", "html": "👮🏽‍♂", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👮🏾‍♂️", "name": "man police officer: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F46E 1F3FE 200D 2642 FE0F", "html": "👮🏾‍♂️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👮🏾‍♂", "name": "man police officer: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F46E 1F3FE 200D 2642", "html": "👮🏾‍♂", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👮🏿‍♂️", "name": "man police officer: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F46E 1F3FF 200D 2642 FE0F", "html": "👮🏿‍♂️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👮🏿‍♂", "name": "man police officer: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F46E 1F3FF 200D 2642", "html": "👮🏿‍♂", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👮‍♀️", "name": "woman police officer", "shortname": ":woman_police_officer:", "unicode": "1F46E 200D 2640 FE0F", "html": "👮‍♀️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👮🏻‍♀️", "name": "woman police officer: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F46E 1F3FB 200D 2640 FE0F", "html": "👮🏻‍♀️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👮🏼‍♀️", "name": "woman police officer: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F46E 1F3FC 200D 2640 FE0F", "html": "👮🏼‍♀️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👮🏽‍♀️", "name": "woman police officer: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F46E 1F3FD 200D 2640 FE0F", "html": "👮🏽‍♀️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👮🏾‍♀️", "name": "woman police officer: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F46E 1F3FE 200D 2640 FE0F", "html": "👮🏾‍♀️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👮🏿‍♀️", "name": "woman police officer: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F46E 1F3FF 200D 2640 FE0F", "html": "👮🏿‍♀️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🕵️", "name": "detective", "shortname": ":detective:", "unicode": "1F575 FE0F", "html": "🕵️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🕵️‍♂️", "name": "man detective", "shortname": ":man_detective:", "unicode": "1F575 FE0F 200D 2642 FE0F", "html": "🕵️‍♂️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🕵‍♂️", "name": "man detective", "shortname": ":man_detective:", "unicode": "1F575 200D 2642 FE0F", "html": "🕵‍♂️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🕵️‍♂", "name": "man detective", "shortname": ":man_detective:", "unicode": "1F575 FE0F 200D 2642", "html": "🕵️‍♂", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🕵‍♂", "name": "man detective", "shortname": ":man_detective:", "unicode": "1F575 200D 2642", "html": "🕵‍♂", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🕵🏻‍♂️", "name": "man detective: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F575 1F3FB 200D 2642 FE0F", "html": "🕵🏻‍♂️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🕵🏻‍♂", "name": "man detective: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F575 1F3FB 200D 2642", "html": "🕵🏻‍♂", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🕵🏼‍♂️", "name": "man detective: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F575 1F3FC 200D 2642 FE0F", "html": "🕵🏼‍♂️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🕵🏼‍♂", "name": "man detective: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F575 1F3FC 200D 2642", "html": "🕵🏼‍♂", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🕵🏽‍♂️", "name": "man detective: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F575 1F3FD 200D 2642 FE0F", "html": "🕵🏽‍♂️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🕵🏽‍♂", "name": "man detective: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F575 1F3FD 200D 2642", "html": "🕵🏽‍♂", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🕵🏾‍♂️", "name": "man detective: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F575 1F3FE 200D 2642 FE0F", "html": "🕵🏾‍♂️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🕵🏾‍♂", "name": "man detective: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F575 1F3FE 200D 2642", "html": "🕵🏾‍♂", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🕵🏿‍♂️", "name": "man detective: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F575 1F3FF 200D 2642 FE0F", "html": "🕵🏿‍♂️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🕵🏿‍♂", "name": "man detective: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F575 1F3FF 200D 2642", "html": "🕵🏿‍♂", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🕵️‍♀️", "name": "woman detective", "shortname": ":woman_detective:", "unicode": "1F575 FE0F 200D 2640 FE0F", "html": "🕵️‍♀️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🕵‍♀️", "name": "woman detective", "shortname": ":woman_detective:", "unicode": "1F575 200D 2640 FE0F", "html": "🕵‍♀️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🕵️‍♀", "name": "woman detective", "shortname": ":woman_detective:", "unicode": "1F575 FE0F 200D 2640", "html": "🕵️‍♀", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🕵🏻‍♀️", "name": "woman detective: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F575 1F3FB 200D 2640 FE0F", "html": "🕵🏻‍♀️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🕵🏼‍♀️", "name": "woman detective: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F575 1F3FC 200D 2640 FE0F", "html": "🕵🏼‍♀️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🕵🏽‍♀️", "name": "woman detective: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F575 1F3FD 200D 2640 FE0F", "html": "🕵🏽‍♀️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🕵🏾‍♀️", "name": "woman detective: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F575 1F3FE 200D 2640 FE0F", "html": "🕵🏾‍♀️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🕵🏿‍♀️", "name": "woman detective: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F575 1F3FF 200D 2640 FE0F", "html": "🕵🏿‍♀️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "💂‍♂️", "name": "man guard", "shortname": ":man_guard:", "unicode": "1F482 200D 2642 FE0F", "html": "💂‍♂️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "💂‍♂", "name": "man guard", "shortname": ":man_guard:", "unicode": "1F482 200D 2642", "html": "💂‍♂", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "💂🏻‍♂️", "name": "man guard: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F482 1F3FB 200D 2642 FE0F", "html": "💂🏻‍♂️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "💂🏻‍♂", "name": "man guard: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F482 1F3FB 200D 2642", "html": "💂🏻‍♂", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "💂🏼‍♂️", "name": "man guard: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F482 1F3FC 200D 2642 FE0F", "html": "💂🏼‍♂️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "💂🏼‍♂", "name": "man guard: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F482 1F3FC 200D 2642", "html": "💂🏼‍♂", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "💂🏽‍♂️", "name": "man guard: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F482 1F3FD 200D 2642 FE0F", "html": "💂🏽‍♂️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "💂🏽‍♂", "name": "man guard: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F482 1F3FD 200D 2642", "html": "💂🏽‍♂", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "💂🏾‍♂️", "name": "man guard: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F482 1F3FE 200D 2642 FE0F", "html": "💂🏾‍♂️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "💂🏾‍♂", "name": "man guard: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F482 1F3FE 200D 2642", "html": "💂🏾‍♂", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "💂🏿‍♂️", "name": "man guard: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F482 1F3FF 200D 2642 FE0F", "html": "💂🏿‍♂️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "💂🏿‍♂", "name": "man guard: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F482 1F3FF 200D 2642", "html": "💂🏿‍♂", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "💂‍♀️", "name": "woman guard", "shortname": ":woman_guard:", "unicode": "1F482 200D 2640 FE0F", "html": "💂‍♀️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "💂🏻‍♀️", "name": "woman guard: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F482 1F3FB 200D 2640 FE0F", "html": "💂🏻‍♀️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "💂🏼‍♀️", "name": "woman guard: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F482 1F3FC 200D 2640 FE0F", "html": "💂🏼‍♀️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "💂🏽‍♀️", "name": "woman guard: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F482 1F3FD 200D 2640 FE0F", "html": "💂🏽‍♀️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "💂🏾‍♀️", "name": "woman guard: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F482 1F3FE 200D 2640 FE0F", "html": "💂🏾‍♀️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "💂🏿‍♀️", "name": "woman guard: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F482 1F3FF 200D 2640 FE0F", "html": "💂🏿‍♀️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👷‍♂️", "name": "man construction worker", "shortname": ":man_construction_worker:", "unicode": "1F477 200D 2642 FE0F", "html": "👷‍♂️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👷‍♂", "name": "man construction worker", "shortname": ":man_construction_worker:", "unicode": "1F477 200D 2642", "html": "👷‍♂", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👷🏻‍♂️", "name": "man construction worker: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F477 1F3FB 200D 2642 FE0F", "html": "👷🏻‍♂️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👷🏻‍♂", "name": "man construction worker: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F477 1F3FB 200D 2642", "html": "👷🏻‍♂", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👷🏼‍♂️", "name": "man construction worker: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F477 1F3FC 200D 2642 FE0F", "html": "👷🏼‍♂️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👷🏼‍♂", "name": "man construction worker: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F477 1F3FC 200D 2642", "html": "👷🏼‍♂", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👷🏽‍♂️", "name": "man construction worker: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F477 1F3FD 200D 2642 FE0F", "html": "👷🏽‍♂️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👷🏽‍♂", "name": "man construction worker: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F477 1F3FD 200D 2642", "html": "👷🏽‍♂", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👷🏾‍♂️", "name": "man construction worker: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F477 1F3FE 200D 2642 FE0F", "html": "👷🏾‍♂️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👷🏾‍♂", "name": "man construction worker: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F477 1F3FE 200D 2642", "html": "👷🏾‍♂", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👷🏿‍♂️", "name": "man construction worker: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F477 1F3FF 200D 2642 FE0F", "html": "👷🏿‍♂️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👷🏿‍♂", "name": "man construction worker: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F477 1F3FF 200D 2642", "html": "👷🏿‍♂", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👷‍♀️", "name": "woman construction worker", "shortname": ":woman_construction_worker:", "unicode": "1F477 200D 2640 FE0F", "html": "👷‍♀️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👷🏻‍♀️", "name": "woman construction worker: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F477 1F3FB 200D 2640 FE0F", "html": "👷🏻‍♀️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👷🏼‍♀️", "name": "woman construction worker: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F477 1F3FC 200D 2640 FE0F", "html": "👷🏼‍♀️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👷🏽‍♀️", "name": "woman construction worker: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F477 1F3FD 200D 2640 FE0F", "html": "👷🏽‍♀️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👷🏾‍♀️", "name": "woman construction worker: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F477 1F3FE 200D 2640 FE0F", "html": "👷🏾‍♀️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👷🏿‍♀️", "name": "woman construction worker: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F477 1F3FF 200D 2640 FE0F", "html": "👷🏿‍♀️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🤴", "name": "prince", "shortname": ":prince:", "unicode": "1F934", "html": "🤴", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🤴🏻", "name": "prince: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F934 1F3FB", "html": "🤴🏻", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🤴🏼", "name": "prince: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F934 1F3FC", "html": "🤴🏼", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🤴🏽", "name": "prince: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F934 1F3FD", "html": "🤴🏽", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🤴🏾", "name": "prince: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F934 1F3FE", "html": "🤴🏾", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🤴🏿", "name": "prince: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F934 1F3FF", "html": "🤴🏿", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👳‍♂️", "name": "man wearing turban", "shortname": ":man_wearing_turban:", "unicode": "1F473 200D 2642 FE0F", "html": "👳‍♂️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👳‍♂", "name": "man wearing turban", "shortname": ":man_wearing_turban:", "unicode": "1F473 200D 2642", "html": "👳‍♂", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👳🏻‍♂️", "name": "man wearing turban: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F473 1F3FB 200D 2642 FE0F", "html": "👳🏻‍♂️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👳🏻‍♂", "name": "man wearing turban: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F473 1F3FB 200D 2642", "html": "👳🏻‍♂", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👳🏼‍♂️", "name": "man wearing turban: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F473 1F3FC 200D 2642 FE0F", "html": "👳🏼‍♂️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👳🏼‍♂", "name": "man wearing turban: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F473 1F3FC 200D 2642", "html": "👳🏼‍♂", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👳🏽‍♂️", "name": "man wearing turban: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F473 1F3FD 200D 2642 FE0F", "html": "👳🏽‍♂️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👳🏽‍♂", "name": "man wearing turban: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F473 1F3FD 200D 2642", "html": "👳🏽‍♂", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👳🏾‍♂️", "name": "man wearing turban: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F473 1F3FE 200D 2642 FE0F", "html": "👳🏾‍♂️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👳🏾‍♂", "name": "man wearing turban: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F473 1F3FE 200D 2642", "html": "👳🏾‍♂", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👳🏿‍♂️", "name": "man wearing turban: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F473 1F3FF 200D 2642 FE0F", "html": "👳🏿‍♂️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👳🏿‍♂", "name": "man wearing turban: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F473 1F3FF 200D 2642", "html": "👳🏿‍♂", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👳‍♀️", "name": "woman wearing turban", "shortname": ":woman_wearing_turban:", "unicode": "1F473 200D 2640 FE0F", "html": "👳‍♀️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👳🏻‍♀️", "name": "woman wearing turban: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F473 1F3FB 200D 2640 FE0F", "html": "👳🏻‍♀️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👳🏼‍♀️", "name": "woman wearing turban: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F473 1F3FC 200D 2640 FE0F", "html": "👳🏼‍♀️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👳🏽‍♀️", "name": "woman wearing turban: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F473 1F3FD 200D 2640 FE0F", "html": "👳🏽‍♀️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👳🏾‍♀️", "name": "woman wearing turban: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F473 1F3FE 200D 2640 FE0F", "html": "👳🏾‍♀️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "👳🏿‍♀️", "name": "woman wearing turban: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F473 1F3FF 200D 2640 FE0F", "html": "👳🏿‍♀️", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧕", "name": "woman with headscarf", "shortname": ":woman_with_headscarf:", "unicode": "1F9D5", "html": "🧕", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧕🏻", "name": "woman with headscarf: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D5 1F3FB", "html": "🧕🏻", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧕🏼", "name": "woman with headscarf: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D5 1F3FC", "html": "🧕🏼", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧕🏽", "name": "woman with headscarf: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D5 1F3FD", "html": "🧕🏽", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧕🏾", "name": "woman with headscarf: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D5 1F3FE", "html": "🧕🏾", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🧕🏿", "name": "woman with headscarf: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D5 1F3FF", "html": "🧕🏿", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🤵", "name": "man in tuxedo", "shortname": ":man_in_tuxedo:", "unicode": "1F935", "html": "🤵", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🤵🏻", "name": "man in tuxedo: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F935 1F3FB", "html": "🤵🏻", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🤵🏼", "name": "man in tuxedo: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F935 1F3FC", "html": "🤵🏼", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🤵🏽", "name": "man in tuxedo: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F935 1F3FD", "html": "🤵🏽", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🤵🏾", "name": "man in tuxedo: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F935 1F3FE", "html": "🤵🏾", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🤵🏿", "name": "man in tuxedo: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F935 1F3FF", "html": "🤵🏿", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🤰", "name": "pregnant woman", "shortname": ":pregnant_woman:", "unicode": "1F930", "html": "🤰", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🤰🏻", "name": "pregnant woman: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F930 1F3FB", "html": "🤰🏻", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🤰🏼", "name": "pregnant woman: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F930 1F3FC", "html": "🤰🏼", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🤰🏽", "name": "pregnant woman: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F930 1F3FD", "html": "🤰🏽", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🤰🏾", "name": "pregnant woman: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F930 1F3FE", "html": "🤰🏾", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🤰🏿", "name": "pregnant woman: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F930 1F3FF", "html": "🤰🏿", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🤱", "name": "breast-feeding", "shortname": ":breastfeeding:", "unicode": "1F931", "html": "🤱", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🤱🏻", "name": "breast-feeding: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F931 1F3FB", "html": "🤱🏻", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🤱🏼", "name": "breast-feeding: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F931 1F3FC", "html": "🤱🏼", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🤱🏽", "name": "breast-feeding: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F931 1F3FD", "html": "🤱🏽", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🤱🏾", "name": "breast-feeding: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F931 1F3FE", "html": "🤱🏾", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🤱🏿", "name": "breast-feeding: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F931 1F3FF", "html": "🤱🏿", "category": "People & Body (person-role)", "order": ""}, + {"emoji": "🤶", "name": "Mrs. Claus", "shortname": ":Mrs_Claus:", "unicode": "1F936", "html": "🤶", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🤶🏻", "name": "Mrs. Claus: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F936 1F3FB", "html": "🤶🏻", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🤶🏼", "name": "Mrs. Claus: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F936 1F3FC", "html": "🤶🏼", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🤶🏽", "name": "Mrs. Claus: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F936 1F3FD", "html": "🤶🏽", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🤶🏾", "name": "Mrs. Claus: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F936 1F3FE", "html": "🤶🏾", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🤶🏿", "name": "Mrs. Claus: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F936 1F3FF", "html": "🤶🏿", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦸", "name": "superhero", "shortname": ":superhero:", "unicode": "1F9B8", "html": "🦸", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦸🏻", "name": "superhero: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9B8 1F3FB", "html": "🦸🏻", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦸🏼", "name": "superhero: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9B8 1F3FC", "html": "🦸🏼", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦸🏽", "name": "superhero: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9B8 1F3FD", "html": "🦸🏽", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦸🏾", "name": "superhero: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9B8 1F3FE", "html": "🦸🏾", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦸🏿", "name": "superhero: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9B8 1F3FF", "html": "🦸🏿", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦸‍♂️", "name": "man superhero", "shortname": ":man_superhero:", "unicode": "1F9B8 200D 2642 FE0F", "html": "🦸‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦸‍♂", "name": "man superhero", "shortname": ":man_superhero:", "unicode": "1F9B8 200D 2642", "html": "🦸‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦸🏻‍♂️", "name": "man superhero: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9B8 1F3FB 200D 2642 FE0F", "html": "🦸🏻‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦸🏻‍♂", "name": "man superhero: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9B8 1F3FB 200D 2642", "html": "🦸🏻‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦸🏼‍♂️", "name": "man superhero: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9B8 1F3FC 200D 2642 FE0F", "html": "🦸🏼‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦸🏼‍♂", "name": "man superhero: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9B8 1F3FC 200D 2642", "html": "🦸🏼‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦸🏽‍♂️", "name": "man superhero: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9B8 1F3FD 200D 2642 FE0F", "html": "🦸🏽‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦸🏽‍♂", "name": "man superhero: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9B8 1F3FD 200D 2642", "html": "🦸🏽‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦸🏾‍♂️", "name": "man superhero: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9B8 1F3FE 200D 2642 FE0F", "html": "🦸🏾‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦸🏾‍♂", "name": "man superhero: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9B8 1F3FE 200D 2642", "html": "🦸🏾‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦸🏿‍♂️", "name": "man superhero: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9B8 1F3FF 200D 2642 FE0F", "html": "🦸🏿‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦸🏿‍♂", "name": "man superhero: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9B8 1F3FF 200D 2642", "html": "🦸🏿‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦸‍♀️", "name": "woman superhero", "shortname": ":woman_superhero:", "unicode": "1F9B8 200D 2640 FE0F", "html": "🦸‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦸‍♀", "name": "woman superhero", "shortname": ":woman_superhero:", "unicode": "1F9B8 200D 2640", "html": "🦸‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦸🏻‍♀️", "name": "woman superhero: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9B8 1F3FB 200D 2640 FE0F", "html": "🦸🏻‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦸🏻‍♀", "name": "woman superhero: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9B8 1F3FB 200D 2640", "html": "🦸🏻‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦸🏼‍♀️", "name": "woman superhero: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9B8 1F3FC 200D 2640 FE0F", "html": "🦸🏼‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦸🏼‍♀", "name": "woman superhero: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9B8 1F3FC 200D 2640", "html": "🦸🏼‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦸🏽‍♀️", "name": "woman superhero: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9B8 1F3FD 200D 2640 FE0F", "html": "🦸🏽‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦸🏽‍♀", "name": "woman superhero: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9B8 1F3FD 200D 2640", "html": "🦸🏽‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦸🏾‍♀️", "name": "woman superhero: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9B8 1F3FE 200D 2640 FE0F", "html": "🦸🏾‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦸🏾‍♀", "name": "woman superhero: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9B8 1F3FE 200D 2640", "html": "🦸🏾‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦸🏿‍♀️", "name": "woman superhero: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9B8 1F3FF 200D 2640 FE0F", "html": "🦸🏿‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦸🏿‍♀", "name": "woman superhero: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9B8 1F3FF 200D 2640", "html": "🦸🏿‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦹", "name": "supervillain", "shortname": ":supervillain:", "unicode": "1F9B9", "html": "🦹", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦹🏻", "name": "supervillain: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9B9 1F3FB", "html": "🦹🏻", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦹🏼", "name": "supervillain: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9B9 1F3FC", "html": "🦹🏼", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦹🏽", "name": "supervillain: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9B9 1F3FD", "html": "🦹🏽", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦹🏾", "name": "supervillain: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9B9 1F3FE", "html": "🦹🏾", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦹🏿", "name": "supervillain: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9B9 1F3FF", "html": "🦹🏿", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦹‍♂️", "name": "man supervillain", "shortname": ":man_supervillain:", "unicode": "1F9B9 200D 2642 FE0F", "html": "🦹‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦹‍♂", "name": "man supervillain", "shortname": ":man_supervillain:", "unicode": "1F9B9 200D 2642", "html": "🦹‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦹🏻‍♂️", "name": "man supervillain: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9B9 1F3FB 200D 2642 FE0F", "html": "🦹🏻‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦹🏻‍♂", "name": "man supervillain: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9B9 1F3FB 200D 2642", "html": "🦹🏻‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦹🏼‍♂️", "name": "man supervillain: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9B9 1F3FC 200D 2642 FE0F", "html": "🦹🏼‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦹🏼‍♂", "name": "man supervillain: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9B9 1F3FC 200D 2642", "html": "🦹🏼‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦹🏽‍♂️", "name": "man supervillain: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9B9 1F3FD 200D 2642 FE0F", "html": "🦹🏽‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦹🏽‍♂", "name": "man supervillain: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9B9 1F3FD 200D 2642", "html": "🦹🏽‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦹🏾‍♂️", "name": "man supervillain: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9B9 1F3FE 200D 2642 FE0F", "html": "🦹🏾‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦹🏾‍♂", "name": "man supervillain: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9B9 1F3FE 200D 2642", "html": "🦹🏾‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦹🏿‍♂️", "name": "man supervillain: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9B9 1F3FF 200D 2642 FE0F", "html": "🦹🏿‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦹🏿‍♂", "name": "man supervillain: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9B9 1F3FF 200D 2642", "html": "🦹🏿‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦹‍♀️", "name": "woman supervillain", "shortname": ":woman_supervillain:", "unicode": "1F9B9 200D 2640 FE0F", "html": "🦹‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦹‍♀", "name": "woman supervillain", "shortname": ":woman_supervillain:", "unicode": "1F9B9 200D 2640", "html": "🦹‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦹🏻‍♀️", "name": "woman supervillain: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9B9 1F3FB 200D 2640 FE0F", "html": "🦹🏻‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦹🏻‍♀", "name": "woman supervillain: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9B9 1F3FB 200D 2640", "html": "🦹🏻‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦹🏼‍♀️", "name": "woman supervillain: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9B9 1F3FC 200D 2640 FE0F", "html": "🦹🏼‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦹🏼‍♀", "name": "woman supervillain: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9B9 1F3FC 200D 2640", "html": "🦹🏼‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦹🏽‍♀️", "name": "woman supervillain: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9B9 1F3FD 200D 2640 FE0F", "html": "🦹🏽‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦹🏽‍♀", "name": "woman supervillain: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9B9 1F3FD 200D 2640", "html": "🦹🏽‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦹🏾‍♀️", "name": "woman supervillain: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9B9 1F3FE 200D 2640 FE0F", "html": "🦹🏾‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦹🏾‍♀", "name": "woman supervillain: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9B9 1F3FE 200D 2640", "html": "🦹🏾‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦹🏿‍♀️", "name": "woman supervillain: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9B9 1F3FF 200D 2640 FE0F", "html": "🦹🏿‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🦹🏿‍♀", "name": "woman supervillain: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9B9 1F3FF 200D 2640", "html": "🦹🏿‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧙", "name": "mage", "shortname": ":mage:", "unicode": "1F9D9", "html": "🧙", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧙🏻", "name": "mage: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D9 1F3FB", "html": "🧙🏻", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧙🏼", "name": "mage: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D9 1F3FC", "html": "🧙🏼", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧙🏽", "name": "mage: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D9 1F3FD", "html": "🧙🏽", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧙🏾", "name": "mage: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D9 1F3FE", "html": "🧙🏾", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧙🏿", "name": "mage: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D9 1F3FF", "html": "🧙🏿", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧙‍♂️", "name": "man mage", "shortname": ":man_mage:", "unicode": "1F9D9 200D 2642 FE0F", "html": "🧙‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧙‍♂", "name": "man mage", "shortname": ":man_mage:", "unicode": "1F9D9 200D 2642", "html": "🧙‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧙🏻‍♂️", "name": "man mage: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D9 1F3FB 200D 2642 FE0F", "html": "🧙🏻‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧙🏻‍♂", "name": "man mage: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D9 1F3FB 200D 2642", "html": "🧙🏻‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧙🏼‍♂️", "name": "man mage: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D9 1F3FC 200D 2642 FE0F", "html": "🧙🏼‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧙🏼‍♂", "name": "man mage: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D9 1F3FC 200D 2642", "html": "🧙🏼‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧙🏽‍♂️", "name": "man mage: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D9 1F3FD 200D 2642 FE0F", "html": "🧙🏽‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧙🏽‍♂", "name": "man mage: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D9 1F3FD 200D 2642", "html": "🧙🏽‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧙🏾‍♂️", "name": "man mage: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D9 1F3FE 200D 2642 FE0F", "html": "🧙🏾‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧙🏾‍♂", "name": "man mage: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D9 1F3FE 200D 2642", "html": "🧙🏾‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧙🏿‍♂️", "name": "man mage: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D9 1F3FF 200D 2642 FE0F", "html": "🧙🏿‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧙🏿‍♂", "name": "man mage: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D9 1F3FF 200D 2642", "html": "🧙🏿‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧙‍♀️", "name": "woman mage", "shortname": ":woman_mage:", "unicode": "1F9D9 200D 2640 FE0F", "html": "🧙‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧙‍♀", "name": "woman mage", "shortname": ":woman_mage:", "unicode": "1F9D9 200D 2640", "html": "🧙‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧙🏻‍♀️", "name": "woman mage: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D9 1F3FB 200D 2640 FE0F", "html": "🧙🏻‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧙🏻‍♀", "name": "woman mage: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D9 1F3FB 200D 2640", "html": "🧙🏻‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧙🏼‍♀️", "name": "woman mage: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D9 1F3FC 200D 2640 FE0F", "html": "🧙🏼‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧙🏼‍♀", "name": "woman mage: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D9 1F3FC 200D 2640", "html": "🧙🏼‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧙🏽‍♀️", "name": "woman mage: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D9 1F3FD 200D 2640 FE0F", "html": "🧙🏽‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧙🏽‍♀", "name": "woman mage: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D9 1F3FD 200D 2640", "html": "🧙🏽‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧙🏾‍♀️", "name": "woman mage: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D9 1F3FE 200D 2640 FE0F", "html": "🧙🏾‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧙🏾‍♀", "name": "woman mage: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D9 1F3FE 200D 2640", "html": "🧙🏾‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧙🏿‍♀️", "name": "woman mage: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D9 1F3FF 200D 2640 FE0F", "html": "🧙🏿‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧙🏿‍♀", "name": "woman mage: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D9 1F3FF 200D 2640", "html": "🧙🏿‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧚", "name": "fairy", "shortname": ":fairy:", "unicode": "1F9DA", "html": "🧚", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧚🏻", "name": "fairy: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9DA 1F3FB", "html": "🧚🏻", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧚🏼", "name": "fairy: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9DA 1F3FC", "html": "🧚🏼", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧚🏽", "name": "fairy: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9DA 1F3FD", "html": "🧚🏽", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧚🏾", "name": "fairy: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9DA 1F3FE", "html": "🧚🏾", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧚🏿", "name": "fairy: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9DA 1F3FF", "html": "🧚🏿", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧚‍♂️", "name": "man fairy", "shortname": ":man_fairy:", "unicode": "1F9DA 200D 2642 FE0F", "html": "🧚‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧚‍♂", "name": "man fairy", "shortname": ":man_fairy:", "unicode": "1F9DA 200D 2642", "html": "🧚‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧚🏻‍♂️", "name": "man fairy: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9DA 1F3FB 200D 2642 FE0F", "html": "🧚🏻‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧚🏻‍♂", "name": "man fairy: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9DA 1F3FB 200D 2642", "html": "🧚🏻‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧚🏼‍♂️", "name": "man fairy: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9DA 1F3FC 200D 2642 FE0F", "html": "🧚🏼‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧚🏼‍♂", "name": "man fairy: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9DA 1F3FC 200D 2642", "html": "🧚🏼‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧚🏽‍♂️", "name": "man fairy: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9DA 1F3FD 200D 2642 FE0F", "html": "🧚🏽‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧚🏽‍♂", "name": "man fairy: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9DA 1F3FD 200D 2642", "html": "🧚🏽‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧚🏾‍♂️", "name": "man fairy: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9DA 1F3FE 200D 2642 FE0F", "html": "🧚🏾‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧚🏾‍♂", "name": "man fairy: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9DA 1F3FE 200D 2642", "html": "🧚🏾‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧚🏿‍♂️", "name": "man fairy: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9DA 1F3FF 200D 2642 FE0F", "html": "🧚🏿‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧚🏿‍♂", "name": "man fairy: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9DA 1F3FF 200D 2642", "html": "🧚🏿‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧚‍♀️", "name": "woman fairy", "shortname": ":woman_fairy:", "unicode": "1F9DA 200D 2640 FE0F", "html": "🧚‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧚‍♀", "name": "woman fairy", "shortname": ":woman_fairy:", "unicode": "1F9DA 200D 2640", "html": "🧚‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧚🏻‍♀️", "name": "woman fairy: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9DA 1F3FB 200D 2640 FE0F", "html": "🧚🏻‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧚🏻‍♀", "name": "woman fairy: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9DA 1F3FB 200D 2640", "html": "🧚🏻‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧚🏼‍♀️", "name": "woman fairy: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9DA 1F3FC 200D 2640 FE0F", "html": "🧚🏼‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧚🏼‍♀", "name": "woman fairy: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9DA 1F3FC 200D 2640", "html": "🧚🏼‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧚🏽‍♀️", "name": "woman fairy: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9DA 1F3FD 200D 2640 FE0F", "html": "🧚🏽‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧚🏽‍♀", "name": "woman fairy: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9DA 1F3FD 200D 2640", "html": "🧚🏽‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧚🏾‍♀️", "name": "woman fairy: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9DA 1F3FE 200D 2640 FE0F", "html": "🧚🏾‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧚🏾‍♀", "name": "woman fairy: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9DA 1F3FE 200D 2640", "html": "🧚🏾‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧚🏿‍♀️", "name": "woman fairy: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9DA 1F3FF 200D 2640 FE0F", "html": "🧚🏿‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧚🏿‍♀", "name": "woman fairy: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9DA 1F3FF 200D 2640", "html": "🧚🏿‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧛", "name": "vampire", "shortname": ":vampire:", "unicode": "1F9DB", "html": "🧛", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧛🏻", "name": "vampire: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9DB 1F3FB", "html": "🧛🏻", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧛🏼", "name": "vampire: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9DB 1F3FC", "html": "🧛🏼", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧛🏽", "name": "vampire: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9DB 1F3FD", "html": "🧛🏽", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧛🏾", "name": "vampire: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9DB 1F3FE", "html": "🧛🏾", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧛🏿", "name": "vampire: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9DB 1F3FF", "html": "🧛🏿", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧛‍♂️", "name": "man vampire", "shortname": ":man_vampire:", "unicode": "1F9DB 200D 2642 FE0F", "html": "🧛‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧛‍♂", "name": "man vampire", "shortname": ":man_vampire:", "unicode": "1F9DB 200D 2642", "html": "🧛‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧛🏻‍♂️", "name": "man vampire: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9DB 1F3FB 200D 2642 FE0F", "html": "🧛🏻‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧛🏻‍♂", "name": "man vampire: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9DB 1F3FB 200D 2642", "html": "🧛🏻‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧛🏼‍♂️", "name": "man vampire: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9DB 1F3FC 200D 2642 FE0F", "html": "🧛🏼‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧛🏼‍♂", "name": "man vampire: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9DB 1F3FC 200D 2642", "html": "🧛🏼‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧛🏽‍♂️", "name": "man vampire: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9DB 1F3FD 200D 2642 FE0F", "html": "🧛🏽‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧛🏽‍♂", "name": "man vampire: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9DB 1F3FD 200D 2642", "html": "🧛🏽‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧛🏾‍♂️", "name": "man vampire: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9DB 1F3FE 200D 2642 FE0F", "html": "🧛🏾‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧛🏾‍♂", "name": "man vampire: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9DB 1F3FE 200D 2642", "html": "🧛🏾‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧛🏿‍♂️", "name": "man vampire: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9DB 1F3FF 200D 2642 FE0F", "html": "🧛🏿‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧛🏿‍♂", "name": "man vampire: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9DB 1F3FF 200D 2642", "html": "🧛🏿‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧛‍♀️", "name": "woman vampire", "shortname": ":woman_vampire:", "unicode": "1F9DB 200D 2640 FE0F", "html": "🧛‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧛‍♀", "name": "woman vampire", "shortname": ":woman_vampire:", "unicode": "1F9DB 200D 2640", "html": "🧛‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧛🏻‍♀️", "name": "woman vampire: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9DB 1F3FB 200D 2640 FE0F", "html": "🧛🏻‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧛🏻‍♀", "name": "woman vampire: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9DB 1F3FB 200D 2640", "html": "🧛🏻‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧛🏼‍♀️", "name": "woman vampire: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9DB 1F3FC 200D 2640 FE0F", "html": "🧛🏼‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧛🏼‍♀", "name": "woman vampire: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9DB 1F3FC 200D 2640", "html": "🧛🏼‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧛🏽‍♀️", "name": "woman vampire: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9DB 1F3FD 200D 2640 FE0F", "html": "🧛🏽‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧛🏽‍♀", "name": "woman vampire: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9DB 1F3FD 200D 2640", "html": "🧛🏽‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧛🏾‍♀️", "name": "woman vampire: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9DB 1F3FE 200D 2640 FE0F", "html": "🧛🏾‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧛🏾‍♀", "name": "woman vampire: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9DB 1F3FE 200D 2640", "html": "🧛🏾‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧛🏿‍♀️", "name": "woman vampire: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9DB 1F3FF 200D 2640 FE0F", "html": "🧛🏿‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧛🏿‍♀", "name": "woman vampire: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9DB 1F3FF 200D 2640", "html": "🧛🏿‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧜", "name": "merperson", "shortname": ":merperson:", "unicode": "1F9DC", "html": "🧜", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧜🏻", "name": "merperson: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9DC 1F3FB", "html": "🧜🏻", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧜🏼", "name": "merperson: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9DC 1F3FC", "html": "🧜🏼", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧜🏽", "name": "merperson: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9DC 1F3FD", "html": "🧜🏽", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧜🏾", "name": "merperson: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9DC 1F3FE", "html": "🧜🏾", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧜🏿", "name": "merperson: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9DC 1F3FF", "html": "🧜🏿", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧜‍♂️", "name": "merman", "shortname": ":merman:", "unicode": "1F9DC 200D 2642 FE0F", "html": "🧜‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧜‍♂", "name": "merman", "shortname": ":merman:", "unicode": "1F9DC 200D 2642", "html": "🧜‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧜🏻‍♂️", "name": "merman: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9DC 1F3FB 200D 2642 FE0F", "html": "🧜🏻‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧜🏻‍♂", "name": "merman: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9DC 1F3FB 200D 2642", "html": "🧜🏻‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧜🏼‍♂️", "name": "merman: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9DC 1F3FC 200D 2642 FE0F", "html": "🧜🏼‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧜🏼‍♂", "name": "merman: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9DC 1F3FC 200D 2642", "html": "🧜🏼‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧜🏽‍♂️", "name": "merman: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9DC 1F3FD 200D 2642 FE0F", "html": "🧜🏽‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧜🏽‍♂", "name": "merman: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9DC 1F3FD 200D 2642", "html": "🧜🏽‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧜🏾‍♂️", "name": "merman: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9DC 1F3FE 200D 2642 FE0F", "html": "🧜🏾‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧜🏾‍♂", "name": "merman: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9DC 1F3FE 200D 2642", "html": "🧜🏾‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧜🏿‍♂️", "name": "merman: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9DC 1F3FF 200D 2642 FE0F", "html": "🧜🏿‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧜🏿‍♂", "name": "merman: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9DC 1F3FF 200D 2642", "html": "🧜🏿‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧜‍♀️", "name": "mermaid", "shortname": ":mermaid:", "unicode": "1F9DC 200D 2640 FE0F", "html": "🧜‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧜‍♀", "name": "mermaid", "shortname": ":mermaid:", "unicode": "1F9DC 200D 2640", "html": "🧜‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧜🏻‍♀️", "name": "mermaid: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9DC 1F3FB 200D 2640 FE0F", "html": "🧜🏻‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧜🏻‍♀", "name": "mermaid: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9DC 1F3FB 200D 2640", "html": "🧜🏻‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧜🏼‍♀️", "name": "mermaid: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9DC 1F3FC 200D 2640 FE0F", "html": "🧜🏼‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧜🏼‍♀", "name": "mermaid: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9DC 1F3FC 200D 2640", "html": "🧜🏼‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧜🏽‍♀️", "name": "mermaid: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9DC 1F3FD 200D 2640 FE0F", "html": "🧜🏽‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧜🏽‍♀", "name": "mermaid: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9DC 1F3FD 200D 2640", "html": "🧜🏽‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧜🏾‍♀️", "name": "mermaid: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9DC 1F3FE 200D 2640 FE0F", "html": "🧜🏾‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧜🏾‍♀", "name": "mermaid: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9DC 1F3FE 200D 2640", "html": "🧜🏾‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧜🏿‍♀️", "name": "mermaid: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9DC 1F3FF 200D 2640 FE0F", "html": "🧜🏿‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧜🏿‍♀", "name": "mermaid: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9DC 1F3FF 200D 2640", "html": "🧜🏿‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧝", "name": "elf", "shortname": ":elf:", "unicode": "1F9DD", "html": "🧝", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧝🏻", "name": "elf: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9DD 1F3FB", "html": "🧝🏻", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧝🏼", "name": "elf: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9DD 1F3FC", "html": "🧝🏼", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧝🏽", "name": "elf: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9DD 1F3FD", "html": "🧝🏽", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧝🏾", "name": "elf: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9DD 1F3FE", "html": "🧝🏾", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧝🏿", "name": "elf: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9DD 1F3FF", "html": "🧝🏿", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧝‍♂️", "name": "man elf", "shortname": ":man_elf:", "unicode": "1F9DD 200D 2642 FE0F", "html": "🧝‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧝‍♂", "name": "man elf", "shortname": ":man_elf:", "unicode": "1F9DD 200D 2642", "html": "🧝‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧝🏻‍♂️", "name": "man elf: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9DD 1F3FB 200D 2642 FE0F", "html": "🧝🏻‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧝🏻‍♂", "name": "man elf: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9DD 1F3FB 200D 2642", "html": "🧝🏻‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧝🏼‍♂️", "name": "man elf: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9DD 1F3FC 200D 2642 FE0F", "html": "🧝🏼‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧝🏼‍♂", "name": "man elf: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9DD 1F3FC 200D 2642", "html": "🧝🏼‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧝🏽‍♂️", "name": "man elf: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9DD 1F3FD 200D 2642 FE0F", "html": "🧝🏽‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧝🏽‍♂", "name": "man elf: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9DD 1F3FD 200D 2642", "html": "🧝🏽‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧝🏾‍♂️", "name": "man elf: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9DD 1F3FE 200D 2642 FE0F", "html": "🧝🏾‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧝🏾‍♂", "name": "man elf: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9DD 1F3FE 200D 2642", "html": "🧝🏾‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧝🏿‍♂️", "name": "man elf: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9DD 1F3FF 200D 2642 FE0F", "html": "🧝🏿‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧝🏿‍♂", "name": "man elf: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9DD 1F3FF 200D 2642", "html": "🧝🏿‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧝‍♀️", "name": "woman elf", "shortname": ":woman_elf:", "unicode": "1F9DD 200D 2640 FE0F", "html": "🧝‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧝‍♀", "name": "woman elf", "shortname": ":woman_elf:", "unicode": "1F9DD 200D 2640", "html": "🧝‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧝🏻‍♀️", "name": "woman elf: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9DD 1F3FB 200D 2640 FE0F", "html": "🧝🏻‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧝🏻‍♀", "name": "woman elf: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9DD 1F3FB 200D 2640", "html": "🧝🏻‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧝🏼‍♀️", "name": "woman elf: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9DD 1F3FC 200D 2640 FE0F", "html": "🧝🏼‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧝🏼‍♀", "name": "woman elf: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9DD 1F3FC 200D 2640", "html": "🧝🏼‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧝🏽‍♀️", "name": "woman elf: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9DD 1F3FD 200D 2640 FE0F", "html": "🧝🏽‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧝🏽‍♀", "name": "woman elf: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9DD 1F3FD 200D 2640", "html": "🧝🏽‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧝🏾‍♀️", "name": "woman elf: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9DD 1F3FE 200D 2640 FE0F", "html": "🧝🏾‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧝🏾‍♀", "name": "woman elf: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9DD 1F3FE 200D 2640", "html": "🧝🏾‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧝🏿‍♀️", "name": "woman elf: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9DD 1F3FF 200D 2640 FE0F", "html": "🧝🏿‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧝🏿‍♀", "name": "woman elf: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9DD 1F3FF 200D 2640", "html": "🧝🏿‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧞", "name": "genie", "shortname": ":genie:", "unicode": "1F9DE", "html": "🧞", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧞‍♂️", "name": "man genie", "shortname": ":man_genie:", "unicode": "1F9DE 200D 2642 FE0F", "html": "🧞‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧞‍♂", "name": "man genie", "shortname": ":man_genie:", "unicode": "1F9DE 200D 2642", "html": "🧞‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧞‍♀️", "name": "woman genie", "shortname": ":woman_genie:", "unicode": "1F9DE 200D 2640 FE0F", "html": "🧞‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧞‍♀", "name": "woman genie", "shortname": ":woman_genie:", "unicode": "1F9DE 200D 2640", "html": "🧞‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧟", "name": "zombie", "shortname": ":zombie:", "unicode": "1F9DF", "html": "🧟", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧟‍♂️", "name": "man zombie", "shortname": ":man_zombie:", "unicode": "1F9DF 200D 2642 FE0F", "html": "🧟‍♂️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧟‍♂", "name": "man zombie", "shortname": ":man_zombie:", "unicode": "1F9DF 200D 2642", "html": "🧟‍♂", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧟‍♀️", "name": "woman zombie", "shortname": ":woman_zombie:", "unicode": "1F9DF 200D 2640 FE0F", "html": "🧟‍♀️", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "🧟‍♀", "name": "woman zombie", "shortname": ":woman_zombie:", "unicode": "1F9DF 200D 2640", "html": "🧟‍♀", "category": "People & Body (person-fantasy)", "order": ""}, + {"emoji": "💆‍♂️", "name": "man getting massage", "shortname": ":man_getting_massage:", "unicode": "1F486 200D 2642 FE0F", "html": "💆‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💆🏻‍♂️", "name": "man getting massage: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F486 1F3FB 200D 2642 FE0F", "html": "💆🏻‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💆🏼‍♂️", "name": "man getting massage: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F486 1F3FC 200D 2642 FE0F", "html": "💆🏼‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💆🏽‍♂️", "name": "man getting massage: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F486 1F3FD 200D 2642 FE0F", "html": "💆🏽‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💆🏾‍♂️", "name": "man getting massage: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F486 1F3FE 200D 2642 FE0F", "html": "💆🏾‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💆🏿‍♂️", "name": "man getting massage: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F486 1F3FF 200D 2642 FE0F", "html": "💆🏿‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💆‍♀️", "name": "woman getting massage", "shortname": ":woman_getting_massage:", "unicode": "1F486 200D 2640 FE0F", "html": "💆‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💆‍♀", "name": "woman getting massage", "shortname": ":woman_getting_massage:", "unicode": "1F486 200D 2640", "html": "💆‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💆🏻‍♀️", "name": "woman getting massage: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F486 1F3FB 200D 2640 FE0F", "html": "💆🏻‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💆🏻‍♀", "name": "woman getting massage: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F486 1F3FB 200D 2640", "html": "💆🏻‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💆🏼‍♀️", "name": "woman getting massage: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F486 1F3FC 200D 2640 FE0F", "html": "💆🏼‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💆🏼‍♀", "name": "woman getting massage: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F486 1F3FC 200D 2640", "html": "💆🏼‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💆🏽‍♀️", "name": "woman getting massage: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F486 1F3FD 200D 2640 FE0F", "html": "💆🏽‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💆🏽‍♀", "name": "woman getting massage: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F486 1F3FD 200D 2640", "html": "💆🏽‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💆🏾‍♀️", "name": "woman getting massage: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F486 1F3FE 200D 2640 FE0F", "html": "💆🏾‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💆🏾‍♀", "name": "woman getting massage: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F486 1F3FE 200D 2640", "html": "💆🏾‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💆🏿‍♀️", "name": "woman getting massage: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F486 1F3FF 200D 2640 FE0F", "html": "💆🏿‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💆🏿‍♀", "name": "woman getting massage: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F486 1F3FF 200D 2640", "html": "💆🏿‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💇‍♂️", "name": "man getting haircut", "shortname": ":man_getting_haircut:", "unicode": "1F487 200D 2642 FE0F", "html": "💇‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💇🏻‍♂️", "name": "man getting haircut: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F487 1F3FB 200D 2642 FE0F", "html": "💇🏻‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💇🏼‍♂️", "name": "man getting haircut: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F487 1F3FC 200D 2642 FE0F", "html": "💇🏼‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💇🏽‍♂️", "name": "man getting haircut: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F487 1F3FD 200D 2642 FE0F", "html": "💇🏽‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💇🏾‍♂️", "name": "man getting haircut: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F487 1F3FE 200D 2642 FE0F", "html": "💇🏾‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💇🏿‍♂️", "name": "man getting haircut: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F487 1F3FF 200D 2642 FE0F", "html": "💇🏿‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💇‍♀️", "name": "woman getting haircut", "shortname": ":woman_getting_haircut:", "unicode": "1F487 200D 2640 FE0F", "html": "💇‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💇‍♀", "name": "woman getting haircut", "shortname": ":woman_getting_haircut:", "unicode": "1F487 200D 2640", "html": "💇‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💇🏻‍♀️", "name": "woman getting haircut: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F487 1F3FB 200D 2640 FE0F", "html": "💇🏻‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💇🏻‍♀", "name": "woman getting haircut: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F487 1F3FB 200D 2640", "html": "💇🏻‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💇🏼‍♀️", "name": "woman getting haircut: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F487 1F3FC 200D 2640 FE0F", "html": "💇🏼‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💇🏼‍♀", "name": "woman getting haircut: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F487 1F3FC 200D 2640", "html": "💇🏼‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💇🏽‍♀️", "name": "woman getting haircut: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F487 1F3FD 200D 2640 FE0F", "html": "💇🏽‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💇🏽‍♀", "name": "woman getting haircut: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F487 1F3FD 200D 2640", "html": "💇🏽‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💇🏾‍♀️", "name": "woman getting haircut: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F487 1F3FE 200D 2640 FE0F", "html": "💇🏾‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💇🏾‍♀", "name": "woman getting haircut: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F487 1F3FE 200D 2640", "html": "💇🏾‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💇🏿‍♀️", "name": "woman getting haircut: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F487 1F3FF 200D 2640 FE0F", "html": "💇🏿‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "💇🏿‍♀", "name": "woman getting haircut: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F487 1F3FF 200D 2640", "html": "💇🏿‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🚶‍♂️", "name": "man walking", "shortname": ":man_walking:", "unicode": "1F6B6 200D 2642 FE0F", "html": "🚶‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🚶‍♂", "name": "man walking", "shortname": ":man_walking:", "unicode": "1F6B6 200D 2642", "html": "🚶‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🚶🏻‍♂️", "name": "man walking: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F6B6 1F3FB 200D 2642 FE0F", "html": "🚶🏻‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🚶🏻‍♂", "name": "man walking: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F6B6 1F3FB 200D 2642", "html": "🚶🏻‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🚶🏼‍♂️", "name": "man walking: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F6B6 1F3FC 200D 2642 FE0F", "html": "🚶🏼‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🚶🏼‍♂", "name": "man walking: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F6B6 1F3FC 200D 2642", "html": "🚶🏼‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🚶🏽‍♂️", "name": "man walking: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F6B6 1F3FD 200D 2642 FE0F", "html": "🚶🏽‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🚶🏽‍♂", "name": "man walking: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F6B6 1F3FD 200D 2642", "html": "🚶🏽‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🚶🏾‍♂️", "name": "man walking: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F6B6 1F3FE 200D 2642 FE0F", "html": "🚶🏾‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🚶🏾‍♂", "name": "man walking: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F6B6 1F3FE 200D 2642", "html": "🚶🏾‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🚶🏿‍♂️", "name": "man walking: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F6B6 1F3FF 200D 2642 FE0F", "html": "🚶🏿‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🚶🏿‍♂", "name": "man walking: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F6B6 1F3FF 200D 2642", "html": "🚶🏿‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🚶‍♀️", "name": "woman walking", "shortname": ":woman_walking:", "unicode": "1F6B6 200D 2640 FE0F", "html": "🚶‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🚶🏻‍♀️", "name": "woman walking: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F6B6 1F3FB 200D 2640 FE0F", "html": "🚶🏻‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🚶🏼‍♀️", "name": "woman walking: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F6B6 1F3FC 200D 2640 FE0F", "html": "🚶🏼‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🚶🏽‍♀️", "name": "woman walking: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F6B6 1F3FD 200D 2640 FE0F", "html": "🚶🏽‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🚶🏾‍♀️", "name": "woman walking: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F6B6 1F3FE 200D 2640 FE0F", "html": "🚶🏾‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🚶🏿‍♀️", "name": "woman walking: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F6B6 1F3FF 200D 2640 FE0F", "html": "🚶🏿‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧍", "name": "person standing", "shortname": ":person_standing:", "unicode": "1F9CD", "html": "🧍", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧍🏻", "name": "person standing: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9CD 1F3FB", "html": "🧍🏻", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧍🏼", "name": "person standing: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9CD 1F3FC", "html": "🧍🏼", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧍🏽", "name": "person standing: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9CD 1F3FD", "html": "🧍🏽", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧍🏾", "name": "person standing: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9CD 1F3FE", "html": "🧍🏾", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧍🏿", "name": "person standing: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9CD 1F3FF", "html": "🧍🏿", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧍‍♂️", "name": "man standing", "shortname": ":man_standing:", "unicode": "1F9CD 200D 2642 FE0F", "html": "🧍‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧍‍♂", "name": "man standing", "shortname": ":man_standing:", "unicode": "1F9CD 200D 2642", "html": "🧍‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧍🏻‍♂️", "name": "man standing: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9CD 1F3FB 200D 2642 FE0F", "html": "🧍🏻‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧍🏻‍♂", "name": "man standing: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9CD 1F3FB 200D 2642", "html": "🧍🏻‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧍🏼‍♂️", "name": "man standing: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9CD 1F3FC 200D 2642 FE0F", "html": "🧍🏼‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧍🏼‍♂", "name": "man standing: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9CD 1F3FC 200D 2642", "html": "🧍🏼‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧍🏽‍♂️", "name": "man standing: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9CD 1F3FD 200D 2642 FE0F", "html": "🧍🏽‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧍🏽‍♂", "name": "man standing: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9CD 1F3FD 200D 2642", "html": "🧍🏽‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧍🏾‍♂️", "name": "man standing: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9CD 1F3FE 200D 2642 FE0F", "html": "🧍🏾‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧍🏾‍♂", "name": "man standing: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9CD 1F3FE 200D 2642", "html": "🧍🏾‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧍🏿‍♂️", "name": "man standing: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9CD 1F3FF 200D 2642 FE0F", "html": "🧍🏿‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧍🏿‍♂", "name": "man standing: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9CD 1F3FF 200D 2642", "html": "🧍🏿‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧍‍♀️", "name": "woman standing", "shortname": ":woman_standing:", "unicode": "1F9CD 200D 2640 FE0F", "html": "🧍‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧍‍♀", "name": "woman standing", "shortname": ":woman_standing:", "unicode": "1F9CD 200D 2640", "html": "🧍‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧍🏻‍♀️", "name": "woman standing: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9CD 1F3FB 200D 2640 FE0F", "html": "🧍🏻‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧍🏻‍♀", "name": "woman standing: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9CD 1F3FB 200D 2640", "html": "🧍🏻‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧍🏼‍♀️", "name": "woman standing: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9CD 1F3FC 200D 2640 FE0F", "html": "🧍🏼‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧍🏼‍♀", "name": "woman standing: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9CD 1F3FC 200D 2640", "html": "🧍🏼‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧍🏽‍♀️", "name": "woman standing: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9CD 1F3FD 200D 2640 FE0F", "html": "🧍🏽‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧍🏽‍♀", "name": "woman standing: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9CD 1F3FD 200D 2640", "html": "🧍🏽‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧍🏾‍♀️", "name": "woman standing: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9CD 1F3FE 200D 2640 FE0F", "html": "🧍🏾‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧍🏾‍♀", "name": "woman standing: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9CD 1F3FE 200D 2640", "html": "🧍🏾‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧍🏿‍♀️", "name": "woman standing: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9CD 1F3FF 200D 2640 FE0F", "html": "🧍🏿‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧍🏿‍♀", "name": "woman standing: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9CD 1F3FF 200D 2640", "html": "🧍🏿‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧎", "name": "person kneeling", "shortname": ":person_kneeling:", "unicode": "1F9CE", "html": "🧎", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧎🏻", "name": "person kneeling: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9CE 1F3FB", "html": "🧎🏻", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧎🏼", "name": "person kneeling: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9CE 1F3FC", "html": "🧎🏼", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧎🏽", "name": "person kneeling: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9CE 1F3FD", "html": "🧎🏽", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧎🏾", "name": "person kneeling: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9CE 1F3FE", "html": "🧎🏾", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧎🏿", "name": "person kneeling: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9CE 1F3FF", "html": "🧎🏿", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧎‍♂️", "name": "man kneeling", "shortname": ":man_kneeling:", "unicode": "1F9CE 200D 2642 FE0F", "html": "🧎‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧎‍♂", "name": "man kneeling", "shortname": ":man_kneeling:", "unicode": "1F9CE 200D 2642", "html": "🧎‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧎🏻‍♂️", "name": "man kneeling: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9CE 1F3FB 200D 2642 FE0F", "html": "🧎🏻‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧎🏻‍♂", "name": "man kneeling: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9CE 1F3FB 200D 2642", "html": "🧎🏻‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧎🏼‍♂️", "name": "man kneeling: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9CE 1F3FC 200D 2642 FE0F", "html": "🧎🏼‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧎🏼‍♂", "name": "man kneeling: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9CE 1F3FC 200D 2642", "html": "🧎🏼‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧎🏽‍♂️", "name": "man kneeling: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9CE 1F3FD 200D 2642 FE0F", "html": "🧎🏽‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧎🏽‍♂", "name": "man kneeling: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9CE 1F3FD 200D 2642", "html": "🧎🏽‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧎🏾‍♂️", "name": "man kneeling: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9CE 1F3FE 200D 2642 FE0F", "html": "🧎🏾‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧎🏾‍♂", "name": "man kneeling: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9CE 1F3FE 200D 2642", "html": "🧎🏾‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧎🏿‍♂️", "name": "man kneeling: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9CE 1F3FF 200D 2642 FE0F", "html": "🧎🏿‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧎🏿‍♂", "name": "man kneeling: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9CE 1F3FF 200D 2642", "html": "🧎🏿‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧎‍♀️", "name": "woman kneeling", "shortname": ":woman_kneeling:", "unicode": "1F9CE 200D 2640 FE0F", "html": "🧎‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧎‍♀", "name": "woman kneeling", "shortname": ":woman_kneeling:", "unicode": "1F9CE 200D 2640", "html": "🧎‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧎🏻‍♀️", "name": "woman kneeling: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9CE 1F3FB 200D 2640 FE0F", "html": "🧎🏻‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧎🏻‍♀", "name": "woman kneeling: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9CE 1F3FB 200D 2640", "html": "🧎🏻‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧎🏼‍♀️", "name": "woman kneeling: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9CE 1F3FC 200D 2640 FE0F", "html": "🧎🏼‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧎🏼‍♀", "name": "woman kneeling: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9CE 1F3FC 200D 2640", "html": "🧎🏼‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧎🏽‍♀️", "name": "woman kneeling: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9CE 1F3FD 200D 2640 FE0F", "html": "🧎🏽‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧎🏽‍♀", "name": "woman kneeling: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9CE 1F3FD 200D 2640", "html": "🧎🏽‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧎🏾‍♀️", "name": "woman kneeling: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9CE 1F3FE 200D 2640 FE0F", "html": "🧎🏾‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧎🏾‍♀", "name": "woman kneeling: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9CE 1F3FE 200D 2640", "html": "🧎🏾‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧎🏿‍♀️", "name": "woman kneeling: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9CE 1F3FF 200D 2640 FE0F", "html": "🧎🏿‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧎🏿‍♀", "name": "woman kneeling: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9CE 1F3FF 200D 2640", "html": "🧎🏿‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧑‍🦯", "name": "person with probing cane", "shortname": ":person_with_probing_cane:", "unicode": "1F9D1 200D 1F9AF", "html": "🧑‍🦯", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧑🏻‍🦯", "name": "person with probing cane: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D1 1F3FB 200D 1F9AF", "html": "🧑🏻‍🦯", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧑🏼‍🦯", "name": "person with probing cane: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D1 1F3FC 200D 1F9AF", "html": "🧑🏼‍🦯", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧑🏽‍🦯", "name": "person with probing cane: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D1 1F3FD 200D 1F9AF", "html": "🧑🏽‍🦯", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧑🏾‍🦯", "name": "person with probing cane: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D1 1F3FE 200D 1F9AF", "html": "🧑🏾‍🦯", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧑🏿‍🦯", "name": "person with probing cane: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D1 1F3FF 200D 1F9AF", "html": "🧑🏿‍🦯", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👨‍🦯", "name": "man with probing cane", "shortname": ":man_with_probing_cane:", "unicode": "1F468 200D 1F9AF", "html": "👨‍🦯", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👨🏻‍🦯", "name": "man with probing cane: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F468 1F3FB 200D 1F9AF", "html": "👨🏻‍🦯", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👨🏼‍🦯", "name": "man with probing cane: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F468 1F3FC 200D 1F9AF", "html": "👨🏼‍🦯", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👨🏽‍🦯", "name": "man with probing cane: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F468 1F3FD 200D 1F9AF", "html": "👨🏽‍🦯", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👨🏾‍🦯", "name": "man with probing cane: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F468 1F3FE 200D 1F9AF", "html": "👨🏾‍🦯", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👨🏿‍🦯", "name": "man with probing cane: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F468 1F3FF 200D 1F9AF", "html": "👨🏿‍🦯", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👩‍🦯", "name": "woman with probing cane", "shortname": ":woman_with_probing_cane:", "unicode": "1F469 200D 1F9AF", "html": "👩‍🦯", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👩🏻‍🦯", "name": "woman with probing cane: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F469 1F3FB 200D 1F9AF", "html": "👩🏻‍🦯", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👩🏼‍🦯", "name": "woman with probing cane: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F469 1F3FC 200D 1F9AF", "html": "👩🏼‍🦯", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👩🏽‍🦯", "name": "woman with probing cane: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F469 1F3FD 200D 1F9AF", "html": "👩🏽‍🦯", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👩🏾‍🦯", "name": "woman with probing cane: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F469 1F3FE 200D 1F9AF", "html": "👩🏾‍🦯", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👩🏿‍🦯", "name": "woman with probing cane: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F469 1F3FF 200D 1F9AF", "html": "👩🏿‍🦯", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧑‍🦼", "name": "person in motorized wheelchair", "shortname": ":person_in_motorized_wheelchair:", "unicode": "1F9D1 200D 1F9BC", "html": "🧑‍🦼", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧑🏻‍🦼", "name": "person in motorized wheelchair: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D1 1F3FB 200D 1F9BC", "html": "🧑🏻‍🦼", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧑🏼‍🦼", "name": "person in motorized wheelchair: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D1 1F3FC 200D 1F9BC", "html": "🧑🏼‍🦼", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧑🏽‍🦼", "name": "person in motorized wheelchair: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D1 1F3FD 200D 1F9BC", "html": "🧑🏽‍🦼", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧑🏾‍🦼", "name": "person in motorized wheelchair: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D1 1F3FE 200D 1F9BC", "html": "🧑🏾‍🦼", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧑🏿‍🦼", "name": "person in motorized wheelchair: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D1 1F3FF 200D 1F9BC", "html": "🧑🏿‍🦼", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👨‍🦼", "name": "man in motorized wheelchair", "shortname": ":man_in_motorized_wheelchair:", "unicode": "1F468 200D 1F9BC", "html": "👨‍🦼", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👨🏻‍🦼", "name": "man in motorized wheelchair: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F468 1F3FB 200D 1F9BC", "html": "👨🏻‍🦼", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👨🏼‍🦼", "name": "man in motorized wheelchair: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F468 1F3FC 200D 1F9BC", "html": "👨🏼‍🦼", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👨🏽‍🦼", "name": "man in motorized wheelchair: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F468 1F3FD 200D 1F9BC", "html": "👨🏽‍🦼", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👨🏾‍🦼", "name": "man in motorized wheelchair: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F468 1F3FE 200D 1F9BC", "html": "👨🏾‍🦼", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👨🏿‍🦼", "name": "man in motorized wheelchair: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F468 1F3FF 200D 1F9BC", "html": "👨🏿‍🦼", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👩‍🦼", "name": "woman in motorized wheelchair", "shortname": ":woman_in_motorized_wheelchair:", "unicode": "1F469 200D 1F9BC", "html": "👩‍🦼", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👩🏻‍🦼", "name": "woman in motorized wheelchair: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F469 1F3FB 200D 1F9BC", "html": "👩🏻‍🦼", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👩🏼‍🦼", "name": "woman in motorized wheelchair: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F469 1F3FC 200D 1F9BC", "html": "👩🏼‍🦼", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👩🏽‍🦼", "name": "woman in motorized wheelchair: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F469 1F3FD 200D 1F9BC", "html": "👩🏽‍🦼", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👩🏾‍🦼", "name": "woman in motorized wheelchair: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F469 1F3FE 200D 1F9BC", "html": "👩🏾‍🦼", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👩🏿‍🦼", "name": "woman in motorized wheelchair: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F469 1F3FF 200D 1F9BC", "html": "👩🏿‍🦼", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧑‍🦽", "name": "person in manual wheelchair", "shortname": ":person_in_manual_wheelchair:", "unicode": "1F9D1 200D 1F9BD", "html": "🧑‍🦽", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧑🏻‍🦽", "name": "person in manual wheelchair: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D1 1F3FB 200D 1F9BD", "html": "🧑🏻‍🦽", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧑🏼‍🦽", "name": "person in manual wheelchair: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D1 1F3FC 200D 1F9BD", "html": "🧑🏼‍🦽", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧑🏽‍🦽", "name": "person in manual wheelchair: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D1 1F3FD 200D 1F9BD", "html": "🧑🏽‍🦽", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧑🏾‍🦽", "name": "person in manual wheelchair: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D1 1F3FE 200D 1F9BD", "html": "🧑🏾‍🦽", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧑🏿‍🦽", "name": "person in manual wheelchair: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D1 1F3FF 200D 1F9BD", "html": "🧑🏿‍🦽", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👨‍🦽", "name": "man in manual wheelchair", "shortname": ":man_in_manual_wheelchair:", "unicode": "1F468 200D 1F9BD", "html": "👨‍🦽", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👨🏻‍🦽", "name": "man in manual wheelchair: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F468 1F3FB 200D 1F9BD", "html": "👨🏻‍🦽", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👨🏼‍🦽", "name": "man in manual wheelchair: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F468 1F3FC 200D 1F9BD", "html": "👨🏼‍🦽", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👨🏽‍🦽", "name": "man in manual wheelchair: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F468 1F3FD 200D 1F9BD", "html": "👨🏽‍🦽", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👨🏾‍🦽", "name": "man in manual wheelchair: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F468 1F3FE 200D 1F9BD", "html": "👨🏾‍🦽", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👨🏿‍🦽", "name": "man in manual wheelchair: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F468 1F3FF 200D 1F9BD", "html": "👨🏿‍🦽", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👩‍🦽", "name": "woman in manual wheelchair", "shortname": ":woman_in_manual_wheelchair:", "unicode": "1F469 200D 1F9BD", "html": "👩‍🦽", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👩🏻‍🦽", "name": "woman in manual wheelchair: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F469 1F3FB 200D 1F9BD", "html": "👩🏻‍🦽", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👩🏼‍🦽", "name": "woman in manual wheelchair: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F469 1F3FC 200D 1F9BD", "html": "👩🏼‍🦽", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👩🏽‍🦽", "name": "woman in manual wheelchair: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F469 1F3FD 200D 1F9BD", "html": "👩🏽‍🦽", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👩🏾‍🦽", "name": "woman in manual wheelchair: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F469 1F3FE 200D 1F9BD", "html": "👩🏾‍🦽", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👩🏿‍🦽", "name": "woman in manual wheelchair: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F469 1F3FF 200D 1F9BD", "html": "👩🏿‍🦽", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🏃‍♂️", "name": "man running", "shortname": ":man_running:", "unicode": "1F3C3 200D 2642 FE0F", "html": "🏃‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🏃‍♂", "name": "man running", "shortname": ":man_running:", "unicode": "1F3C3 200D 2642", "html": "🏃‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🏃🏻‍♂️", "name": "man running: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F3C3 1F3FB 200D 2642 FE0F", "html": "🏃🏻‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🏃🏻‍♂", "name": "man running: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F3C3 1F3FB 200D 2642", "html": "🏃🏻‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🏃🏼‍♂️", "name": "man running: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F3C3 1F3FC 200D 2642 FE0F", "html": "🏃🏼‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🏃🏼‍♂", "name": "man running: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F3C3 1F3FC 200D 2642", "html": "🏃🏼‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🏃🏽‍♂️", "name": "man running: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F3C3 1F3FD 200D 2642 FE0F", "html": "🏃🏽‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🏃🏽‍♂", "name": "man running: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F3C3 1F3FD 200D 2642", "html": "🏃🏽‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🏃🏾‍♂️", "name": "man running: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F3C3 1F3FE 200D 2642 FE0F", "html": "🏃🏾‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🏃🏾‍♂", "name": "man running: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F3C3 1F3FE 200D 2642", "html": "🏃🏾‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🏃🏿‍♂️", "name": "man running: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F3C3 1F3FF 200D 2642 FE0F", "html": "🏃🏿‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🏃🏿‍♂", "name": "man running: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F3C3 1F3FF 200D 2642", "html": "🏃🏿‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🏃‍♀️", "name": "woman running", "shortname": ":woman_running:", "unicode": "1F3C3 200D 2640 FE0F", "html": "🏃‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🏃🏻‍♀️", "name": "woman running: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F3C3 1F3FB 200D 2640 FE0F", "html": "🏃🏻‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🏃🏼‍♀️", "name": "woman running: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F3C3 1F3FC 200D 2640 FE0F", "html": "🏃🏼‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🏃🏽‍♀️", "name": "woman running: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F3C3 1F3FD 200D 2640 FE0F", "html": "🏃🏽‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🏃🏾‍♀️", "name": "woman running: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F3C3 1F3FE 200D 2640 FE0F", "html": "🏃🏾‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🏃🏿‍♀️", "name": "woman running: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F3C3 1F3FF 200D 2640 FE0F", "html": "🏃🏿‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🕺", "name": "man dancing", "shortname": ":man_dancing:", "unicode": "1F57A", "html": "🕺", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🕺🏻", "name": "man dancing: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F57A 1F3FB", "html": "🕺🏻", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🕺🏼", "name": "man dancing: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F57A 1F3FC", "html": "🕺🏼", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🕺🏽", "name": "man dancing: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F57A 1F3FD", "html": "🕺🏽", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🕺🏾", "name": "man dancing: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F57A 1F3FE", "html": "🕺🏾", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🕺🏿", "name": "man dancing: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F57A 1F3FF", "html": "🕺🏿", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🕴️", "name": "man in suit levitating", "shortname": ":man_in_suit_levitating:", "unicode": "1F574 FE0F", "html": "🕴️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🕴🏻", "name": "man in suit levitating: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F574 1F3FB", "html": "🕴🏻", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🕴🏼", "name": "man in suit levitating: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F574 1F3FC", "html": "🕴🏼", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🕴🏽", "name": "man in suit levitating: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F574 1F3FD", "html": "🕴🏽", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🕴🏾", "name": "man in suit levitating: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F574 1F3FE", "html": "🕴🏾", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🕴🏿", "name": "man in suit levitating: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F574 1F3FF", "html": "🕴🏿", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👯‍♂️", "name": "men with bunny ears", "shortname": ":men_with_bunny_ears:", "unicode": "1F46F 200D 2642 FE0F", "html": "👯‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👯‍♀️", "name": "women with bunny ears", "shortname": ":women_with_bunny_ears:", "unicode": "1F46F 200D 2640 FE0F", "html": "👯‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "👯‍♀", "name": "women with bunny ears", "shortname": ":women_with_bunny_ears:", "unicode": "1F46F 200D 2640", "html": "👯‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧖", "name": "person in steamy room", "shortname": ":person_in_steamy_room:", "unicode": "1F9D6", "html": "🧖", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧖🏻", "name": "person in steamy room: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D6 1F3FB", "html": "🧖🏻", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧖🏼", "name": "person in steamy room: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D6 1F3FC", "html": "🧖🏼", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧖🏽", "name": "person in steamy room: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D6 1F3FD", "html": "🧖🏽", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧖🏾", "name": "person in steamy room: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D6 1F3FE", "html": "🧖🏾", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧖🏿", "name": "person in steamy room: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D6 1F3FF", "html": "🧖🏿", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧖‍♂️", "name": "man in steamy room", "shortname": ":man_in_steamy_room:", "unicode": "1F9D6 200D 2642 FE0F", "html": "🧖‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧖‍♂", "name": "man in steamy room", "shortname": ":man_in_steamy_room:", "unicode": "1F9D6 200D 2642", "html": "🧖‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧖🏻‍♂️", "name": "man in steamy room: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D6 1F3FB 200D 2642 FE0F", "html": "🧖🏻‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧖🏻‍♂", "name": "man in steamy room: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D6 1F3FB 200D 2642", "html": "🧖🏻‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧖🏼‍♂️", "name": "man in steamy room: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D6 1F3FC 200D 2642 FE0F", "html": "🧖🏼‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧖🏼‍♂", "name": "man in steamy room: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D6 1F3FC 200D 2642", "html": "🧖🏼‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧖🏽‍♂️", "name": "man in steamy room: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D6 1F3FD 200D 2642 FE0F", "html": "🧖🏽‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧖🏽‍♂", "name": "man in steamy room: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D6 1F3FD 200D 2642", "html": "🧖🏽‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧖🏾‍♂️", "name": "man in steamy room: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D6 1F3FE 200D 2642 FE0F", "html": "🧖🏾‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧖🏾‍♂", "name": "man in steamy room: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D6 1F3FE 200D 2642", "html": "🧖🏾‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧖🏿‍♂️", "name": "man in steamy room: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D6 1F3FF 200D 2642 FE0F", "html": "🧖🏿‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧖🏿‍♂", "name": "man in steamy room: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D6 1F3FF 200D 2642", "html": "🧖🏿‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧖‍♀️", "name": "woman in steamy room", "shortname": ":woman_in_steamy_room:", "unicode": "1F9D6 200D 2640 FE0F", "html": "🧖‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧖‍♀", "name": "woman in steamy room", "shortname": ":woman_in_steamy_room:", "unicode": "1F9D6 200D 2640", "html": "🧖‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧖🏻‍♀️", "name": "woman in steamy room: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D6 1F3FB 200D 2640 FE0F", "html": "🧖🏻‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧖🏻‍♀", "name": "woman in steamy room: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D6 1F3FB 200D 2640", "html": "🧖🏻‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧖🏼‍♀️", "name": "woman in steamy room: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D6 1F3FC 200D 2640 FE0F", "html": "🧖🏼‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧖🏼‍♀", "name": "woman in steamy room: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D6 1F3FC 200D 2640", "html": "🧖🏼‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧖🏽‍♀️", "name": "woman in steamy room: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D6 1F3FD 200D 2640 FE0F", "html": "🧖🏽‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧖🏽‍♀", "name": "woman in steamy room: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D6 1F3FD 200D 2640", "html": "🧖🏽‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧖🏾‍♀️", "name": "woman in steamy room: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D6 1F3FE 200D 2640 FE0F", "html": "🧖🏾‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧖🏾‍♀", "name": "woman in steamy room: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D6 1F3FE 200D 2640", "html": "🧖🏾‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧖🏿‍♀️", "name": "woman in steamy room: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D6 1F3FF 200D 2640 FE0F", "html": "🧖🏿‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧖🏿‍♀", "name": "woman in steamy room: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D6 1F3FF 200D 2640", "html": "🧖🏿‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧗", "name": "person climbing", "shortname": ":person_climbing:", "unicode": "1F9D7", "html": "🧗", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧗🏻", "name": "person climbing: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D7 1F3FB", "html": "🧗🏻", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧗🏼", "name": "person climbing: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D7 1F3FC", "html": "🧗🏼", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧗🏽", "name": "person climbing: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D7 1F3FD", "html": "🧗🏽", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧗🏾", "name": "person climbing: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D7 1F3FE", "html": "🧗🏾", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧗🏿", "name": "person climbing: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D7 1F3FF", "html": "🧗🏿", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧗‍♂️", "name": "man climbing", "shortname": ":man_climbing:", "unicode": "1F9D7 200D 2642 FE0F", "html": "🧗‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧗‍♂", "name": "man climbing", "shortname": ":man_climbing:", "unicode": "1F9D7 200D 2642", "html": "🧗‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧗🏻‍♂️", "name": "man climbing: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D7 1F3FB 200D 2642 FE0F", "html": "🧗🏻‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧗🏻‍♂", "name": "man climbing: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D7 1F3FB 200D 2642", "html": "🧗🏻‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧗🏼‍♂️", "name": "man climbing: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D7 1F3FC 200D 2642 FE0F", "html": "🧗🏼‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧗🏼‍♂", "name": "man climbing: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D7 1F3FC 200D 2642", "html": "🧗🏼‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧗🏽‍♂️", "name": "man climbing: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D7 1F3FD 200D 2642 FE0F", "html": "🧗🏽‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧗🏽‍♂", "name": "man climbing: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D7 1F3FD 200D 2642", "html": "🧗🏽‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧗🏾‍♂️", "name": "man climbing: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D7 1F3FE 200D 2642 FE0F", "html": "🧗🏾‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧗🏾‍♂", "name": "man climbing: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D7 1F3FE 200D 2642", "html": "🧗🏾‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧗🏿‍♂️", "name": "man climbing: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D7 1F3FF 200D 2642 FE0F", "html": "🧗🏿‍♂️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧗🏿‍♂", "name": "man climbing: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D7 1F3FF 200D 2642", "html": "🧗🏿‍♂", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧗‍♀️", "name": "woman climbing", "shortname": ":woman_climbing:", "unicode": "1F9D7 200D 2640 FE0F", "html": "🧗‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧗‍♀", "name": "woman climbing", "shortname": ":woman_climbing:", "unicode": "1F9D7 200D 2640", "html": "🧗‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧗🏻‍♀️", "name": "woman climbing: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D7 1F3FB 200D 2640 FE0F", "html": "🧗🏻‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧗🏻‍♀", "name": "woman climbing: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D7 1F3FB 200D 2640", "html": "🧗🏻‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧗🏼‍♀️", "name": "woman climbing: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D7 1F3FC 200D 2640 FE0F", "html": "🧗🏼‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧗🏼‍♀", "name": "woman climbing: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D7 1F3FC 200D 2640", "html": "🧗🏼‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧗🏽‍♀️", "name": "woman climbing: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D7 1F3FD 200D 2640 FE0F", "html": "🧗🏽‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧗🏽‍♀", "name": "woman climbing: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D7 1F3FD 200D 2640", "html": "🧗🏽‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧗🏾‍♀️", "name": "woman climbing: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D7 1F3FE 200D 2640 FE0F", "html": "🧗🏾‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧗🏾‍♀", "name": "woman climbing: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D7 1F3FE 200D 2640", "html": "🧗🏾‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧗🏿‍♀️", "name": "woman climbing: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D7 1F3FF 200D 2640 FE0F", "html": "🧗🏿‍♀️", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🧗🏿‍♀", "name": "woman climbing: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D7 1F3FF 200D 2640", "html": "🧗🏿‍♀", "category": "People & Body (person-activity)", "order": ""}, + {"emoji": "🤺", "name": "person fencing", "shortname": ":person_fencing:", "unicode": "1F93A", "html": "🤺", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏇🏻", "name": "horse racing: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F3C7 1F3FB", "html": "🏇🏻", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏇🏼", "name": "horse racing: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F3C7 1F3FC", "html": "🏇🏼", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏇🏽", "name": "horse racing: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F3C7 1F3FD", "html": "🏇🏽", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏇🏾", "name": "horse racing: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F3C7 1F3FE", "html": "🏇🏾", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏇🏿", "name": "horse racing: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F3C7 1F3FF", "html": "🏇🏿", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "⛷️", "name": "skier", "shortname": ":skier:", "unicode": "26F7 FE0F", "html": "⛷️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏂🏻", "name": "snowboarder: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F3C2 1F3FB", "html": "🏂🏻", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏂🏼", "name": "snowboarder: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F3C2 1F3FC", "html": "🏂🏼", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏂🏽", "name": "snowboarder: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F3C2 1F3FD", "html": "🏂🏽", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏂🏾", "name": "snowboarder: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F3C2 1F3FE", "html": "🏂🏾", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏂🏿", "name": "snowboarder: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F3C2 1F3FF", "html": "🏂🏿", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏌️", "name": "person golfing", "shortname": ":person_golfing:", "unicode": "1F3CC FE0F", "html": "🏌️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏌🏻", "name": "person golfing: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F3CC 1F3FB", "html": "🏌🏻", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏌🏼", "name": "person golfing: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F3CC 1F3FC", "html": "🏌🏼", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏌🏽", "name": "person golfing: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F3CC 1F3FD", "html": "🏌🏽", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏌🏾", "name": "person golfing: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F3CC 1F3FE", "html": "🏌🏾", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏌🏿", "name": "person golfing: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F3CC 1F3FF", "html": "🏌🏿", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏌️‍♂️", "name": "man golfing", "shortname": ":man_golfing:", "unicode": "1F3CC FE0F 200D 2642 FE0F", "html": "🏌️‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏌‍♂️", "name": "man golfing", "shortname": ":man_golfing:", "unicode": "1F3CC 200D 2642 FE0F", "html": "🏌‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏌️‍♂", "name": "man golfing", "shortname": ":man_golfing:", "unicode": "1F3CC FE0F 200D 2642", "html": "🏌️‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏌‍♂", "name": "man golfing", "shortname": ":man_golfing:", "unicode": "1F3CC 200D 2642", "html": "🏌‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏌🏻‍♂️", "name": "man golfing: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F3CC 1F3FB 200D 2642 FE0F", "html": "🏌🏻‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏌🏻‍♂", "name": "man golfing: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F3CC 1F3FB 200D 2642", "html": "🏌🏻‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏌🏼‍♂️", "name": "man golfing: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F3CC 1F3FC 200D 2642 FE0F", "html": "🏌🏼‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏌🏼‍♂", "name": "man golfing: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F3CC 1F3FC 200D 2642", "html": "🏌🏼‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏌🏽‍♂️", "name": "man golfing: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F3CC 1F3FD 200D 2642 FE0F", "html": "🏌🏽‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏌🏽‍♂", "name": "man golfing: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F3CC 1F3FD 200D 2642", "html": "🏌🏽‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏌🏾‍♂️", "name": "man golfing: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F3CC 1F3FE 200D 2642 FE0F", "html": "🏌🏾‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏌🏾‍♂", "name": "man golfing: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F3CC 1F3FE 200D 2642", "html": "🏌🏾‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏌🏿‍♂️", "name": "man golfing: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F3CC 1F3FF 200D 2642 FE0F", "html": "🏌🏿‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏌🏿‍♂", "name": "man golfing: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F3CC 1F3FF 200D 2642", "html": "🏌🏿‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏌️‍♀️", "name": "woman golfing", "shortname": ":woman_golfing:", "unicode": "1F3CC FE0F 200D 2640 FE0F", "html": "🏌️‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏌‍♀️", "name": "woman golfing", "shortname": ":woman_golfing:", "unicode": "1F3CC 200D 2640 FE0F", "html": "🏌‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏌️‍♀", "name": "woman golfing", "shortname": ":woman_golfing:", "unicode": "1F3CC FE0F 200D 2640", "html": "🏌️‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏌🏻‍♀️", "name": "woman golfing: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F3CC 1F3FB 200D 2640 FE0F", "html": "🏌🏻‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏌🏻‍♀", "name": "woman golfing: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F3CC 1F3FB 200D 2640", "html": "🏌🏻‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏌🏼‍♀️", "name": "woman golfing: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F3CC 1F3FC 200D 2640 FE0F", "html": "🏌🏼‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏌🏼‍♀", "name": "woman golfing: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F3CC 1F3FC 200D 2640", "html": "🏌🏼‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏌🏽‍♀️", "name": "woman golfing: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F3CC 1F3FD 200D 2640 FE0F", "html": "🏌🏽‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏌🏽‍♀", "name": "woman golfing: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F3CC 1F3FD 200D 2640", "html": "🏌🏽‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏌🏾‍♀️", "name": "woman golfing: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F3CC 1F3FE 200D 2640 FE0F", "html": "🏌🏾‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏌🏾‍♀", "name": "woman golfing: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F3CC 1F3FE 200D 2640", "html": "🏌🏾‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏌🏿‍♀️", "name": "woman golfing: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F3CC 1F3FF 200D 2640 FE0F", "html": "🏌🏿‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏌🏿‍♀", "name": "woman golfing: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F3CC 1F3FF 200D 2640", "html": "🏌🏿‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏄‍♂️", "name": "man surfing", "shortname": ":man_surfing:", "unicode": "1F3C4 200D 2642 FE0F", "html": "🏄‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏄‍♂", "name": "man surfing", "shortname": ":man_surfing:", "unicode": "1F3C4 200D 2642", "html": "🏄‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏄🏻‍♂️", "name": "man surfing: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F3C4 1F3FB 200D 2642 FE0F", "html": "🏄🏻‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏄🏻‍♂", "name": "man surfing: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F3C4 1F3FB 200D 2642", "html": "🏄🏻‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏄🏼‍♂️", "name": "man surfing: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F3C4 1F3FC 200D 2642 FE0F", "html": "🏄🏼‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏄🏼‍♂", "name": "man surfing: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F3C4 1F3FC 200D 2642", "html": "🏄🏼‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏄🏽‍♂️", "name": "man surfing: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F3C4 1F3FD 200D 2642 FE0F", "html": "🏄🏽‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏄🏽‍♂", "name": "man surfing: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F3C4 1F3FD 200D 2642", "html": "🏄🏽‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏄🏾‍♂️", "name": "man surfing: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F3C4 1F3FE 200D 2642 FE0F", "html": "🏄🏾‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏄🏾‍♂", "name": "man surfing: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F3C4 1F3FE 200D 2642", "html": "🏄🏾‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏄🏿‍♂️", "name": "man surfing: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F3C4 1F3FF 200D 2642 FE0F", "html": "🏄🏿‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏄🏿‍♂", "name": "man surfing: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F3C4 1F3FF 200D 2642", "html": "🏄🏿‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏄‍♀️", "name": "woman surfing", "shortname": ":woman_surfing:", "unicode": "1F3C4 200D 2640 FE0F", "html": "🏄‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏄🏻‍♀️", "name": "woman surfing: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F3C4 1F3FB 200D 2640 FE0F", "html": "🏄🏻‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏄🏼‍♀️", "name": "woman surfing: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F3C4 1F3FC 200D 2640 FE0F", "html": "🏄🏼‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏄🏽‍♀️", "name": "woman surfing: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F3C4 1F3FD 200D 2640 FE0F", "html": "🏄🏽‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏄🏾‍♀️", "name": "woman surfing: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F3C4 1F3FE 200D 2640 FE0F", "html": "🏄🏾‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏄🏿‍♀️", "name": "woman surfing: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F3C4 1F3FF 200D 2640 FE0F", "html": "🏄🏿‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚣‍♂️", "name": "man rowing boat", "shortname": ":man_rowing_boat:", "unicode": "1F6A3 200D 2642 FE0F", "html": "🚣‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚣‍♂", "name": "man rowing boat", "shortname": ":man_rowing_boat:", "unicode": "1F6A3 200D 2642", "html": "🚣‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚣🏻‍♂️", "name": "man rowing boat: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F6A3 1F3FB 200D 2642 FE0F", "html": "🚣🏻‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚣🏻‍♂", "name": "man rowing boat: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F6A3 1F3FB 200D 2642", "html": "🚣🏻‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚣🏼‍♂️", "name": "man rowing boat: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F6A3 1F3FC 200D 2642 FE0F", "html": "🚣🏼‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚣🏼‍♂", "name": "man rowing boat: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F6A3 1F3FC 200D 2642", "html": "🚣🏼‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚣🏽‍♂️", "name": "man rowing boat: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F6A3 1F3FD 200D 2642 FE0F", "html": "🚣🏽‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚣🏽‍♂", "name": "man rowing boat: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F6A3 1F3FD 200D 2642", "html": "🚣🏽‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚣🏾‍♂️", "name": "man rowing boat: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F6A3 1F3FE 200D 2642 FE0F", "html": "🚣🏾‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚣🏾‍♂", "name": "man rowing boat: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F6A3 1F3FE 200D 2642", "html": "🚣🏾‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚣🏿‍♂️", "name": "man rowing boat: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F6A3 1F3FF 200D 2642 FE0F", "html": "🚣🏿‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚣🏿‍♂", "name": "man rowing boat: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F6A3 1F3FF 200D 2642", "html": "🚣🏿‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚣‍♀️", "name": "woman rowing boat", "shortname": ":woman_rowing_boat:", "unicode": "1F6A3 200D 2640 FE0F", "html": "🚣‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚣🏻‍♀️", "name": "woman rowing boat: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F6A3 1F3FB 200D 2640 FE0F", "html": "🚣🏻‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚣🏼‍♀️", "name": "woman rowing boat: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F6A3 1F3FC 200D 2640 FE0F", "html": "🚣🏼‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚣🏽‍♀️", "name": "woman rowing boat: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F6A3 1F3FD 200D 2640 FE0F", "html": "🚣🏽‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚣🏾‍♀️", "name": "woman rowing boat: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F6A3 1F3FE 200D 2640 FE0F", "html": "🚣🏾‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚣🏿‍♀️", "name": "woman rowing boat: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F6A3 1F3FF 200D 2640 FE0F", "html": "🚣🏿‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏊‍♂️", "name": "man swimming", "shortname": ":man_swimming:", "unicode": "1F3CA 200D 2642 FE0F", "html": "🏊‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏊‍♂", "name": "man swimming", "shortname": ":man_swimming:", "unicode": "1F3CA 200D 2642", "html": "🏊‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏊🏻‍♂️", "name": "man swimming: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F3CA 1F3FB 200D 2642 FE0F", "html": "🏊🏻‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏊🏻‍♂", "name": "man swimming: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F3CA 1F3FB 200D 2642", "html": "🏊🏻‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏊🏼‍♂️", "name": "man swimming: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F3CA 1F3FC 200D 2642 FE0F", "html": "🏊🏼‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏊🏼‍♂", "name": "man swimming: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F3CA 1F3FC 200D 2642", "html": "🏊🏼‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏊🏽‍♂️", "name": "man swimming: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F3CA 1F3FD 200D 2642 FE0F", "html": "🏊🏽‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏊🏽‍♂", "name": "man swimming: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F3CA 1F3FD 200D 2642", "html": "🏊🏽‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏊🏾‍♂️", "name": "man swimming: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F3CA 1F3FE 200D 2642 FE0F", "html": "🏊🏾‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏊🏾‍♂", "name": "man swimming: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F3CA 1F3FE 200D 2642", "html": "🏊🏾‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏊🏿‍♂️", "name": "man swimming: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F3CA 1F3FF 200D 2642 FE0F", "html": "🏊🏿‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏊🏿‍♂", "name": "man swimming: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F3CA 1F3FF 200D 2642", "html": "🏊🏿‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏊‍♀️", "name": "woman swimming", "shortname": ":woman_swimming:", "unicode": "1F3CA 200D 2640 FE0F", "html": "🏊‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏊🏻‍♀️", "name": "woman swimming: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F3CA 1F3FB 200D 2640 FE0F", "html": "🏊🏻‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏊🏼‍♀️", "name": "woman swimming: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F3CA 1F3FC 200D 2640 FE0F", "html": "🏊🏼‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏊🏽‍♀️", "name": "woman swimming: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F3CA 1F3FD 200D 2640 FE0F", "html": "🏊🏽‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏊🏾‍♀️", "name": "woman swimming: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F3CA 1F3FE 200D 2640 FE0F", "html": "🏊🏾‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏊🏿‍♀️", "name": "woman swimming: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F3CA 1F3FF 200D 2640 FE0F", "html": "🏊🏿‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "⛹️", "name": "person bouncing ball", "shortname": ":person_bouncing_ball:", "unicode": "26F9 FE0F", "html": "⛹️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "⛹️‍♂️", "name": "man bouncing ball", "shortname": ":man_bouncing_ball:", "unicode": "26F9 FE0F 200D 2642 FE0F", "html": "⛹️‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "⛹‍♂️", "name": "man bouncing ball", "shortname": ":man_bouncing_ball:", "unicode": "26F9 200D 2642 FE0F", "html": "⛹‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "⛹️‍♂", "name": "man bouncing ball", "shortname": ":man_bouncing_ball:", "unicode": "26F9 FE0F 200D 2642", "html": "⛹️‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "⛹‍♂", "name": "man bouncing ball", "shortname": ":man_bouncing_ball:", "unicode": "26F9 200D 2642", "html": "⛹‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "⛹🏻‍♂️", "name": "man bouncing ball: light skin tone", "shortname": ":light_skin_tone:", "unicode": "26F9 1F3FB 200D 2642 FE0F", "html": "⛹🏻‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "⛹🏻‍♂", "name": "man bouncing ball: light skin tone", "shortname": ":light_skin_tone:", "unicode": "26F9 1F3FB 200D 2642", "html": "⛹🏻‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "⛹🏼‍♂️", "name": "man bouncing ball: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "26F9 1F3FC 200D 2642 FE0F", "html": "⛹🏼‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "⛹🏼‍♂", "name": "man bouncing ball: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "26F9 1F3FC 200D 2642", "html": "⛹🏼‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "⛹🏽‍♂️", "name": "man bouncing ball: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "26F9 1F3FD 200D 2642 FE0F", "html": "⛹🏽‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "⛹🏽‍♂", "name": "man bouncing ball: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "26F9 1F3FD 200D 2642", "html": "⛹🏽‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "⛹🏾‍♂️", "name": "man bouncing ball: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "26F9 1F3FE 200D 2642 FE0F", "html": "⛹🏾‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "⛹🏾‍♂", "name": "man bouncing ball: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "26F9 1F3FE 200D 2642", "html": "⛹🏾‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "⛹🏿‍♂️", "name": "man bouncing ball: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "26F9 1F3FF 200D 2642 FE0F", "html": "⛹🏿‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "⛹🏿‍♂", "name": "man bouncing ball: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "26F9 1F3FF 200D 2642", "html": "⛹🏿‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "⛹️‍♀️", "name": "woman bouncing ball", "shortname": ":woman_bouncing_ball:", "unicode": "26F9 FE0F 200D 2640 FE0F", "html": "⛹️‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "⛹‍♀️", "name": "woman bouncing ball", "shortname": ":woman_bouncing_ball:", "unicode": "26F9 200D 2640 FE0F", "html": "⛹‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "⛹️‍♀", "name": "woman bouncing ball", "shortname": ":woman_bouncing_ball:", "unicode": "26F9 FE0F 200D 2640", "html": "⛹️‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "⛹🏻‍♀️", "name": "woman bouncing ball: light skin tone", "shortname": ":light_skin_tone:", "unicode": "26F9 1F3FB 200D 2640 FE0F", "html": "⛹🏻‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "⛹🏼‍♀️", "name": "woman bouncing ball: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "26F9 1F3FC 200D 2640 FE0F", "html": "⛹🏼‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "⛹🏽‍♀️", "name": "woman bouncing ball: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "26F9 1F3FD 200D 2640 FE0F", "html": "⛹🏽‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "⛹🏾‍♀️", "name": "woman bouncing ball: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "26F9 1F3FE 200D 2640 FE0F", "html": "⛹🏾‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "⛹🏿‍♀️", "name": "woman bouncing ball: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "26F9 1F3FF 200D 2640 FE0F", "html": "⛹🏿‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏋️", "name": "person lifting weights", "shortname": ":person_lifting_weights:", "unicode": "1F3CB FE0F", "html": "🏋️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏋️‍♂️", "name": "man lifting weights", "shortname": ":man_lifting_weights:", "unicode": "1F3CB FE0F 200D 2642 FE0F", "html": "🏋️‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏋‍♂️", "name": "man lifting weights", "shortname": ":man_lifting_weights:", "unicode": "1F3CB 200D 2642 FE0F", "html": "🏋‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏋️‍♂", "name": "man lifting weights", "shortname": ":man_lifting_weights:", "unicode": "1F3CB FE0F 200D 2642", "html": "🏋️‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏋‍♂", "name": "man lifting weights", "shortname": ":man_lifting_weights:", "unicode": "1F3CB 200D 2642", "html": "🏋‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏋🏻‍♂️", "name": "man lifting weights: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F3CB 1F3FB 200D 2642 FE0F", "html": "🏋🏻‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏋🏻‍♂", "name": "man lifting weights: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F3CB 1F3FB 200D 2642", "html": "🏋🏻‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏋🏼‍♂️", "name": "man lifting weights: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F3CB 1F3FC 200D 2642 FE0F", "html": "🏋🏼‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏋🏼‍♂", "name": "man lifting weights: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F3CB 1F3FC 200D 2642", "html": "🏋🏼‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏋🏽‍♂️", "name": "man lifting weights: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F3CB 1F3FD 200D 2642 FE0F", "html": "🏋🏽‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏋🏽‍♂", "name": "man lifting weights: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F3CB 1F3FD 200D 2642", "html": "🏋🏽‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏋🏾‍♂️", "name": "man lifting weights: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F3CB 1F3FE 200D 2642 FE0F", "html": "🏋🏾‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏋🏾‍♂", "name": "man lifting weights: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F3CB 1F3FE 200D 2642", "html": "🏋🏾‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏋🏿‍♂️", "name": "man lifting weights: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F3CB 1F3FF 200D 2642 FE0F", "html": "🏋🏿‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏋🏿‍♂", "name": "man lifting weights: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F3CB 1F3FF 200D 2642", "html": "🏋🏿‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏋️‍♀️", "name": "woman lifting weights", "shortname": ":woman_lifting_weights:", "unicode": "1F3CB FE0F 200D 2640 FE0F", "html": "🏋️‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏋‍♀️", "name": "woman lifting weights", "shortname": ":woman_lifting_weights:", "unicode": "1F3CB 200D 2640 FE0F", "html": "🏋‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏋️‍♀", "name": "woman lifting weights", "shortname": ":woman_lifting_weights:", "unicode": "1F3CB FE0F 200D 2640", "html": "🏋️‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏋🏻‍♀️", "name": "woman lifting weights: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F3CB 1F3FB 200D 2640 FE0F", "html": "🏋🏻‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏋🏼‍♀️", "name": "woman lifting weights: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F3CB 1F3FC 200D 2640 FE0F", "html": "🏋🏼‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏋🏽‍♀️", "name": "woman lifting weights: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F3CB 1F3FD 200D 2640 FE0F", "html": "🏋🏽‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏋🏾‍♀️", "name": "woman lifting weights: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F3CB 1F3FE 200D 2640 FE0F", "html": "🏋🏾‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🏋🏿‍♀️", "name": "woman lifting weights: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F3CB 1F3FF 200D 2640 FE0F", "html": "🏋🏿‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚴‍♂️", "name": "man biking", "shortname": ":man_biking:", "unicode": "1F6B4 200D 2642 FE0F", "html": "🚴‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚴‍♂", "name": "man biking", "shortname": ":man_biking:", "unicode": "1F6B4 200D 2642", "html": "🚴‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚴🏻‍♂️", "name": "man biking: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F6B4 1F3FB 200D 2642 FE0F", "html": "🚴🏻‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚴🏻‍♂", "name": "man biking: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F6B4 1F3FB 200D 2642", "html": "🚴🏻‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚴🏼‍♂️", "name": "man biking: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F6B4 1F3FC 200D 2642 FE0F", "html": "🚴🏼‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚴🏼‍♂", "name": "man biking: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F6B4 1F3FC 200D 2642", "html": "🚴🏼‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚴🏽‍♂️", "name": "man biking: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F6B4 1F3FD 200D 2642 FE0F", "html": "🚴🏽‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚴🏽‍♂", "name": "man biking: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F6B4 1F3FD 200D 2642", "html": "🚴🏽‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚴🏾‍♂️", "name": "man biking: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F6B4 1F3FE 200D 2642 FE0F", "html": "🚴🏾‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚴🏾‍♂", "name": "man biking: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F6B4 1F3FE 200D 2642", "html": "🚴🏾‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚴🏿‍♂️", "name": "man biking: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F6B4 1F3FF 200D 2642 FE0F", "html": "🚴🏿‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚴🏿‍♂", "name": "man biking: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F6B4 1F3FF 200D 2642", "html": "🚴🏿‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚴‍♀️", "name": "woman biking", "shortname": ":woman_biking:", "unicode": "1F6B4 200D 2640 FE0F", "html": "🚴‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚴🏻‍♀️", "name": "woman biking: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F6B4 1F3FB 200D 2640 FE0F", "html": "🚴🏻‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚴🏼‍♀️", "name": "woman biking: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F6B4 1F3FC 200D 2640 FE0F", "html": "🚴🏼‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚴🏽‍♀️", "name": "woman biking: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F6B4 1F3FD 200D 2640 FE0F", "html": "🚴🏽‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚴🏾‍♀️", "name": "woman biking: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F6B4 1F3FE 200D 2640 FE0F", "html": "🚴🏾‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚴🏿‍♀️", "name": "woman biking: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F6B4 1F3FF 200D 2640 FE0F", "html": "🚴🏿‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚵‍♂️", "name": "man mountain biking", "shortname": ":man_mountain_biking:", "unicode": "1F6B5 200D 2642 FE0F", "html": "🚵‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚵‍♂", "name": "man mountain biking", "shortname": ":man_mountain_biking:", "unicode": "1F6B5 200D 2642", "html": "🚵‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚵🏻‍♂️", "name": "man mountain biking: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F6B5 1F3FB 200D 2642 FE0F", "html": "🚵🏻‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚵🏻‍♂", "name": "man mountain biking: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F6B5 1F3FB 200D 2642", "html": "🚵🏻‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚵🏼‍♂️", "name": "man mountain biking: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F6B5 1F3FC 200D 2642 FE0F", "html": "🚵🏼‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚵🏼‍♂", "name": "man mountain biking: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F6B5 1F3FC 200D 2642", "html": "🚵🏼‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚵🏽‍♂️", "name": "man mountain biking: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F6B5 1F3FD 200D 2642 FE0F", "html": "🚵🏽‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚵🏽‍♂", "name": "man mountain biking: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F6B5 1F3FD 200D 2642", "html": "🚵🏽‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚵🏾‍♂️", "name": "man mountain biking: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F6B5 1F3FE 200D 2642 FE0F", "html": "🚵🏾‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚵🏾‍♂", "name": "man mountain biking: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F6B5 1F3FE 200D 2642", "html": "🚵🏾‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚵🏿‍♂️", "name": "man mountain biking: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F6B5 1F3FF 200D 2642 FE0F", "html": "🚵🏿‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚵🏿‍♂", "name": "man mountain biking: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F6B5 1F3FF 200D 2642", "html": "🚵🏿‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚵‍♀️", "name": "woman mountain biking", "shortname": ":woman_mountain_biking:", "unicode": "1F6B5 200D 2640 FE0F", "html": "🚵‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚵🏻‍♀️", "name": "woman mountain biking: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F6B5 1F3FB 200D 2640 FE0F", "html": "🚵🏻‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚵🏼‍♀️", "name": "woman mountain biking: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F6B5 1F3FC 200D 2640 FE0F", "html": "🚵🏼‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚵🏽‍♀️", "name": "woman mountain biking: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F6B5 1F3FD 200D 2640 FE0F", "html": "🚵🏽‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚵🏾‍♀️", "name": "woman mountain biking: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F6B5 1F3FE 200D 2640 FE0F", "html": "🚵🏾‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🚵🏿‍♀️", "name": "woman mountain biking: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F6B5 1F3FF 200D 2640 FE0F", "html": "🚵🏿‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤸", "name": "person cartwheeling", "shortname": ":person_cartwheeling:", "unicode": "1F938", "html": "🤸", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤸🏻", "name": "person cartwheeling: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F938 1F3FB", "html": "🤸🏻", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤸🏼", "name": "person cartwheeling: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F938 1F3FC", "html": "🤸🏼", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤸🏽", "name": "person cartwheeling: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F938 1F3FD", "html": "🤸🏽", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤸🏾", "name": "person cartwheeling: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F938 1F3FE", "html": "🤸🏾", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤸🏿", "name": "person cartwheeling: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F938 1F3FF", "html": "🤸🏿", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤸‍♂️", "name": "man cartwheeling", "shortname": ":man_cartwheeling:", "unicode": "1F938 200D 2642 FE0F", "html": "🤸‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤸‍♂", "name": "man cartwheeling", "shortname": ":man_cartwheeling:", "unicode": "1F938 200D 2642", "html": "🤸‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤸🏻‍♂️", "name": "man cartwheeling: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F938 1F3FB 200D 2642 FE0F", "html": "🤸🏻‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤸🏻‍♂", "name": "man cartwheeling: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F938 1F3FB 200D 2642", "html": "🤸🏻‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤸🏼‍♂️", "name": "man cartwheeling: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F938 1F3FC 200D 2642 FE0F", "html": "🤸🏼‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤸🏼‍♂", "name": "man cartwheeling: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F938 1F3FC 200D 2642", "html": "🤸🏼‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤸🏽‍♂️", "name": "man cartwheeling: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F938 1F3FD 200D 2642 FE0F", "html": "🤸🏽‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤸🏽‍♂", "name": "man cartwheeling: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F938 1F3FD 200D 2642", "html": "🤸🏽‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤸🏾‍♂️", "name": "man cartwheeling: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F938 1F3FE 200D 2642 FE0F", "html": "🤸🏾‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤸🏾‍♂", "name": "man cartwheeling: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F938 1F3FE 200D 2642", "html": "🤸🏾‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤸🏿‍♂️", "name": "man cartwheeling: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F938 1F3FF 200D 2642 FE0F", "html": "🤸🏿‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤸🏿‍♂", "name": "man cartwheeling: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F938 1F3FF 200D 2642", "html": "🤸🏿‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤸‍♀️", "name": "woman cartwheeling", "shortname": ":woman_cartwheeling:", "unicode": "1F938 200D 2640 FE0F", "html": "🤸‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤸‍♀", "name": "woman cartwheeling", "shortname": ":woman_cartwheeling:", "unicode": "1F938 200D 2640", "html": "🤸‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤸🏻‍♀️", "name": "woman cartwheeling: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F938 1F3FB 200D 2640 FE0F", "html": "🤸🏻‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤸🏻‍♀", "name": "woman cartwheeling: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F938 1F3FB 200D 2640", "html": "🤸🏻‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤸🏼‍♀️", "name": "woman cartwheeling: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F938 1F3FC 200D 2640 FE0F", "html": "🤸🏼‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤸🏼‍♀", "name": "woman cartwheeling: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F938 1F3FC 200D 2640", "html": "🤸🏼‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤸🏽‍♀️", "name": "woman cartwheeling: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F938 1F3FD 200D 2640 FE0F", "html": "🤸🏽‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤸🏽‍♀", "name": "woman cartwheeling: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F938 1F3FD 200D 2640", "html": "🤸🏽‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤸🏾‍♀️", "name": "woman cartwheeling: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F938 1F3FE 200D 2640 FE0F", "html": "🤸🏾‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤸🏾‍♀", "name": "woman cartwheeling: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F938 1F3FE 200D 2640", "html": "🤸🏾‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤸🏿‍♀️", "name": "woman cartwheeling: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F938 1F3FF 200D 2640 FE0F", "html": "🤸🏿‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤸🏿‍♀", "name": "woman cartwheeling: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F938 1F3FF 200D 2640", "html": "🤸🏿‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤼", "name": "people wrestling", "shortname": ":people_wrestling:", "unicode": "1F93C", "html": "🤼", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤼‍♂️", "name": "men wrestling", "shortname": ":men_wrestling:", "unicode": "1F93C 200D 2642 FE0F", "html": "🤼‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤼‍♂", "name": "men wrestling", "shortname": ":men_wrestling:", "unicode": "1F93C 200D 2642", "html": "🤼‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤼‍♀️", "name": "women wrestling", "shortname": ":women_wrestling:", "unicode": "1F93C 200D 2640 FE0F", "html": "🤼‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤼‍♀", "name": "women wrestling", "shortname": ":women_wrestling:", "unicode": "1F93C 200D 2640", "html": "🤼‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤽", "name": "person playing water polo", "shortname": ":person_playing_water_polo:", "unicode": "1F93D", "html": "🤽", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤽🏻", "name": "person playing water polo: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F93D 1F3FB", "html": "🤽🏻", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤽🏼", "name": "person playing water polo: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F93D 1F3FC", "html": "🤽🏼", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤽🏽", "name": "person playing water polo: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F93D 1F3FD", "html": "🤽🏽", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤽🏾", "name": "person playing water polo: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F93D 1F3FE", "html": "🤽🏾", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤽🏿", "name": "person playing water polo: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F93D 1F3FF", "html": "🤽🏿", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤽‍♂️", "name": "man playing water polo", "shortname": ":man_playing_water_polo:", "unicode": "1F93D 200D 2642 FE0F", "html": "🤽‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤽‍♂", "name": "man playing water polo", "shortname": ":man_playing_water_polo:", "unicode": "1F93D 200D 2642", "html": "🤽‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤽🏻‍♂️", "name": "man playing water polo: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F93D 1F3FB 200D 2642 FE0F", "html": "🤽🏻‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤽🏻‍♂", "name": "man playing water polo: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F93D 1F3FB 200D 2642", "html": "🤽🏻‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤽🏼‍♂️", "name": "man playing water polo: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F93D 1F3FC 200D 2642 FE0F", "html": "🤽🏼‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤽🏼‍♂", "name": "man playing water polo: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F93D 1F3FC 200D 2642", "html": "🤽🏼‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤽🏽‍♂️", "name": "man playing water polo: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F93D 1F3FD 200D 2642 FE0F", "html": "🤽🏽‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤽🏽‍♂", "name": "man playing water polo: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F93D 1F3FD 200D 2642", "html": "🤽🏽‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤽🏾‍♂️", "name": "man playing water polo: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F93D 1F3FE 200D 2642 FE0F", "html": "🤽🏾‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤽🏾‍♂", "name": "man playing water polo: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F93D 1F3FE 200D 2642", "html": "🤽🏾‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤽🏿‍♂️", "name": "man playing water polo: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F93D 1F3FF 200D 2642 FE0F", "html": "🤽🏿‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤽🏿‍♂", "name": "man playing water polo: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F93D 1F3FF 200D 2642", "html": "🤽🏿‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤽‍♀️", "name": "woman playing water polo", "shortname": ":woman_playing_water_polo:", "unicode": "1F93D 200D 2640 FE0F", "html": "🤽‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤽‍♀", "name": "woman playing water polo", "shortname": ":woman_playing_water_polo:", "unicode": "1F93D 200D 2640", "html": "🤽‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤽🏻‍♀️", "name": "woman playing water polo: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F93D 1F3FB 200D 2640 FE0F", "html": "🤽🏻‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤽🏻‍♀", "name": "woman playing water polo: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F93D 1F3FB 200D 2640", "html": "🤽🏻‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤽🏼‍♀️", "name": "woman playing water polo: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F93D 1F3FC 200D 2640 FE0F", "html": "🤽🏼‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤽🏼‍♀", "name": "woman playing water polo: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F93D 1F3FC 200D 2640", "html": "🤽🏼‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤽🏽‍♀️", "name": "woman playing water polo: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F93D 1F3FD 200D 2640 FE0F", "html": "🤽🏽‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤽🏽‍♀", "name": "woman playing water polo: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F93D 1F3FD 200D 2640", "html": "🤽🏽‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤽🏾‍♀️", "name": "woman playing water polo: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F93D 1F3FE 200D 2640 FE0F", "html": "🤽🏾‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤽🏾‍♀", "name": "woman playing water polo: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F93D 1F3FE 200D 2640", "html": "🤽🏾‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤽🏿‍♀️", "name": "woman playing water polo: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F93D 1F3FF 200D 2640 FE0F", "html": "🤽🏿‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤽🏿‍♀", "name": "woman playing water polo: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F93D 1F3FF 200D 2640", "html": "🤽🏿‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤾", "name": "person playing handball", "shortname": ":person_playing_handball:", "unicode": "1F93E", "html": "🤾", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤾🏻", "name": "person playing handball: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F93E 1F3FB", "html": "🤾🏻", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤾🏼", "name": "person playing handball: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F93E 1F3FC", "html": "🤾🏼", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤾🏽", "name": "person playing handball: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F93E 1F3FD", "html": "🤾🏽", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤾🏾", "name": "person playing handball: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F93E 1F3FE", "html": "🤾🏾", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤾🏿", "name": "person playing handball: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F93E 1F3FF", "html": "🤾🏿", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤾‍♂️", "name": "man playing handball", "shortname": ":man_playing_handball:", "unicode": "1F93E 200D 2642 FE0F", "html": "🤾‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤾‍♂", "name": "man playing handball", "shortname": ":man_playing_handball:", "unicode": "1F93E 200D 2642", "html": "🤾‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤾🏻‍♂️", "name": "man playing handball: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F93E 1F3FB 200D 2642 FE0F", "html": "🤾🏻‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤾🏻‍♂", "name": "man playing handball: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F93E 1F3FB 200D 2642", "html": "🤾🏻‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤾🏼‍♂️", "name": "man playing handball: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F93E 1F3FC 200D 2642 FE0F", "html": "🤾🏼‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤾🏼‍♂", "name": "man playing handball: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F93E 1F3FC 200D 2642", "html": "🤾🏼‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤾🏽‍♂️", "name": "man playing handball: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F93E 1F3FD 200D 2642 FE0F", "html": "🤾🏽‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤾🏽‍♂", "name": "man playing handball: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F93E 1F3FD 200D 2642", "html": "🤾🏽‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤾🏾‍♂️", "name": "man playing handball: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F93E 1F3FE 200D 2642 FE0F", "html": "🤾🏾‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤾🏾‍♂", "name": "man playing handball: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F93E 1F3FE 200D 2642", "html": "🤾🏾‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤾🏿‍♂️", "name": "man playing handball: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F93E 1F3FF 200D 2642 FE0F", "html": "🤾🏿‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤾🏿‍♂", "name": "man playing handball: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F93E 1F3FF 200D 2642", "html": "🤾🏿‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤾‍♀️", "name": "woman playing handball", "shortname": ":woman_playing_handball:", "unicode": "1F93E 200D 2640 FE0F", "html": "🤾‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤾‍♀", "name": "woman playing handball", "shortname": ":woman_playing_handball:", "unicode": "1F93E 200D 2640", "html": "🤾‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤾🏻‍♀️", "name": "woman playing handball: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F93E 1F3FB 200D 2640 FE0F", "html": "🤾🏻‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤾🏻‍♀", "name": "woman playing handball: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F93E 1F3FB 200D 2640", "html": "🤾🏻‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤾🏼‍♀️", "name": "woman playing handball: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F93E 1F3FC 200D 2640 FE0F", "html": "🤾🏼‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤾🏼‍♀", "name": "woman playing handball: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F93E 1F3FC 200D 2640", "html": "🤾🏼‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤾🏽‍♀️", "name": "woman playing handball: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F93E 1F3FD 200D 2640 FE0F", "html": "🤾🏽‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤾🏽‍♀", "name": "woman playing handball: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F93E 1F3FD 200D 2640", "html": "🤾🏽‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤾🏾‍♀️", "name": "woman playing handball: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F93E 1F3FE 200D 2640 FE0F", "html": "🤾🏾‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤾🏾‍♀", "name": "woman playing handball: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F93E 1F3FE 200D 2640", "html": "🤾🏾‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤾🏿‍♀️", "name": "woman playing handball: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F93E 1F3FF 200D 2640 FE0F", "html": "🤾🏿‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤾🏿‍♀", "name": "woman playing handball: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F93E 1F3FF 200D 2640", "html": "🤾🏿‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤹", "name": "person juggling", "shortname": ":person_juggling:", "unicode": "1F939", "html": "🤹", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤹🏻", "name": "person juggling: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F939 1F3FB", "html": "🤹🏻", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤹🏼", "name": "person juggling: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F939 1F3FC", "html": "🤹🏼", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤹🏽", "name": "person juggling: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F939 1F3FD", "html": "🤹🏽", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤹🏾", "name": "person juggling: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F939 1F3FE", "html": "🤹🏾", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤹🏿", "name": "person juggling: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F939 1F3FF", "html": "🤹🏿", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤹‍♂️", "name": "man juggling", "shortname": ":man_juggling:", "unicode": "1F939 200D 2642 FE0F", "html": "🤹‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤹‍♂", "name": "man juggling", "shortname": ":man_juggling:", "unicode": "1F939 200D 2642", "html": "🤹‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤹🏻‍♂️", "name": "man juggling: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F939 1F3FB 200D 2642 FE0F", "html": "🤹🏻‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤹🏻‍♂", "name": "man juggling: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F939 1F3FB 200D 2642", "html": "🤹🏻‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤹🏼‍♂️", "name": "man juggling: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F939 1F3FC 200D 2642 FE0F", "html": "🤹🏼‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤹🏼‍♂", "name": "man juggling: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F939 1F3FC 200D 2642", "html": "🤹🏼‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤹🏽‍♂️", "name": "man juggling: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F939 1F3FD 200D 2642 FE0F", "html": "🤹🏽‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤹🏽‍♂", "name": "man juggling: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F939 1F3FD 200D 2642", "html": "🤹🏽‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤹🏾‍♂️", "name": "man juggling: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F939 1F3FE 200D 2642 FE0F", "html": "🤹🏾‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤹🏾‍♂", "name": "man juggling: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F939 1F3FE 200D 2642", "html": "🤹🏾‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤹🏿‍♂️", "name": "man juggling: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F939 1F3FF 200D 2642 FE0F", "html": "🤹🏿‍♂️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤹🏿‍♂", "name": "man juggling: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F939 1F3FF 200D 2642", "html": "🤹🏿‍♂", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤹‍♀️", "name": "woman juggling", "shortname": ":woman_juggling:", "unicode": "1F939 200D 2640 FE0F", "html": "🤹‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤹‍♀", "name": "woman juggling", "shortname": ":woman_juggling:", "unicode": "1F939 200D 2640", "html": "🤹‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤹🏻‍♀️", "name": "woman juggling: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F939 1F3FB 200D 2640 FE0F", "html": "🤹🏻‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤹🏻‍♀", "name": "woman juggling: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F939 1F3FB 200D 2640", "html": "🤹🏻‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤹🏼‍♀️", "name": "woman juggling: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F939 1F3FC 200D 2640 FE0F", "html": "🤹🏼‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤹🏼‍♀", "name": "woman juggling: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F939 1F3FC 200D 2640", "html": "🤹🏼‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤹🏽‍♀️", "name": "woman juggling: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F939 1F3FD 200D 2640 FE0F", "html": "🤹🏽‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤹🏽‍♀", "name": "woman juggling: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F939 1F3FD 200D 2640", "html": "🤹🏽‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤹🏾‍♀️", "name": "woman juggling: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F939 1F3FE 200D 2640 FE0F", "html": "🤹🏾‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤹🏾‍♀", "name": "woman juggling: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F939 1F3FE 200D 2640", "html": "🤹🏾‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤹🏿‍♀️", "name": "woman juggling: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F939 1F3FF 200D 2640 FE0F", "html": "🤹🏿‍♀️", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🤹🏿‍♀", "name": "woman juggling: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F939 1F3FF 200D 2640", "html": "🤹🏿‍♀", "category": "People & Body (person-sport)", "order": ""}, + {"emoji": "🧘", "name": "person in lotus position", "shortname": ":person_in_lotus_position:", "unicode": "1F9D8", "html": "🧘", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🧘🏻", "name": "person in lotus position: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D8 1F3FB", "html": "🧘🏻", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🧘🏼", "name": "person in lotus position: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D8 1F3FC", "html": "🧘🏼", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🧘🏽", "name": "person in lotus position: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D8 1F3FD", "html": "🧘🏽", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🧘🏾", "name": "person in lotus position: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D8 1F3FE", "html": "🧘🏾", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🧘🏿", "name": "person in lotus position: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D8 1F3FF", "html": "🧘🏿", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🧘‍♂️", "name": "man in lotus position", "shortname": ":man_in_lotus_position:", "unicode": "1F9D8 200D 2642 FE0F", "html": "🧘‍♂️", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🧘‍♂", "name": "man in lotus position", "shortname": ":man_in_lotus_position:", "unicode": "1F9D8 200D 2642", "html": "🧘‍♂", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🧘🏻‍♂️", "name": "man in lotus position: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D8 1F3FB 200D 2642 FE0F", "html": "🧘🏻‍♂️", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🧘🏻‍♂", "name": "man in lotus position: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D8 1F3FB 200D 2642", "html": "🧘🏻‍♂", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🧘🏼‍♂️", "name": "man in lotus position: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D8 1F3FC 200D 2642 FE0F", "html": "🧘🏼‍♂️", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🧘🏼‍♂", "name": "man in lotus position: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D8 1F3FC 200D 2642", "html": "🧘🏼‍♂", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🧘🏽‍♂️", "name": "man in lotus position: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D8 1F3FD 200D 2642 FE0F", "html": "🧘🏽‍♂️", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🧘🏽‍♂", "name": "man in lotus position: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D8 1F3FD 200D 2642", "html": "🧘🏽‍♂", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🧘🏾‍♂️", "name": "man in lotus position: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D8 1F3FE 200D 2642 FE0F", "html": "🧘🏾‍♂️", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🧘🏾‍♂", "name": "man in lotus position: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D8 1F3FE 200D 2642", "html": "🧘🏾‍♂", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🧘🏿‍♂️", "name": "man in lotus position: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D8 1F3FF 200D 2642 FE0F", "html": "🧘🏿‍♂️", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🧘🏿‍♂", "name": "man in lotus position: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D8 1F3FF 200D 2642", "html": "🧘🏿‍♂", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🧘‍♀️", "name": "woman in lotus position", "shortname": ":woman_in_lotus_position:", "unicode": "1F9D8 200D 2640 FE0F", "html": "🧘‍♀️", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🧘‍♀", "name": "woman in lotus position", "shortname": ":woman_in_lotus_position:", "unicode": "1F9D8 200D 2640", "html": "🧘‍♀", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🧘🏻‍♀️", "name": "woman in lotus position: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D8 1F3FB 200D 2640 FE0F", "html": "🧘🏻‍♀️", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🧘🏻‍♀", "name": "woman in lotus position: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D8 1F3FB 200D 2640", "html": "🧘🏻‍♀", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🧘🏼‍♀️", "name": "woman in lotus position: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D8 1F3FC 200D 2640 FE0F", "html": "🧘🏼‍♀️", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🧘🏼‍♀", "name": "woman in lotus position: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D8 1F3FC 200D 2640", "html": "🧘🏼‍♀", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🧘🏽‍♀️", "name": "woman in lotus position: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D8 1F3FD 200D 2640 FE0F", "html": "🧘🏽‍♀️", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🧘🏽‍♀", "name": "woman in lotus position: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D8 1F3FD 200D 2640", "html": "🧘🏽‍♀", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🧘🏾‍♀️", "name": "woman in lotus position: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D8 1F3FE 200D 2640 FE0F", "html": "🧘🏾‍♀️", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🧘🏾‍♀", "name": "woman in lotus position: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D8 1F3FE 200D 2640", "html": "🧘🏾‍♀", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🧘🏿‍♀️", "name": "woman in lotus position: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D8 1F3FF 200D 2640 FE0F", "html": "🧘🏿‍♀️", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🧘🏿‍♀", "name": "woman in lotus position: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D8 1F3FF 200D 2640", "html": "🧘🏿‍♀", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🛌🏻", "name": "person in bed: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F6CC 1F3FB", "html": "🛌🏻", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🛌🏼", "name": "person in bed: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F6CC 1F3FC", "html": "🛌🏼", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🛌🏽", "name": "person in bed: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F6CC 1F3FD", "html": "🛌🏽", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🛌🏾", "name": "person in bed: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F6CC 1F3FE", "html": "🛌🏾", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🛌🏿", "name": "person in bed: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F6CC 1F3FF", "html": "🛌🏿", "category": "People & Body (person-resting)", "order": ""}, + {"emoji": "🧑‍🤝‍🧑", "name": "people holding hands", "shortname": ":people_holding_hands:", "unicode": "1F9D1 200D 1F91D 200D 1F9D1", "html": "🧑‍🤝‍🧑", "category": "People & Body (family)", "order": ""}, + {"emoji": "🧑🏻‍🤝‍🧑🏻", "name": "people holding hands: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F9D1 1F3FB 200D 1F91D 200D 1F9D1 1F3FB", "html": "🧑🏻‍🤝‍🧑🏻", "category": "People & Body (family)", "order": ""}, + {"emoji": "🧑🏻‍🤝‍🧑🏼", "name": "people holding hands: light skin tone, medium-light skin tone", "shortname": ":light_skin_tone_mediumlight_skin_tone:", "unicode": "1F9D1 1F3FB 200D 1F91D 200D 1F9D1 1F3FC", "html": "🧑🏻‍🤝‍🧑🏼", "category": "People & Body (family)", "order": ""}, + {"emoji": "🧑🏻‍🤝‍🧑🏽", "name": "people holding hands: light skin tone, medium skin tone", "shortname": ":light_skin_tone_medium_skin_tone:", "unicode": "1F9D1 1F3FB 200D 1F91D 200D 1F9D1 1F3FD", "html": "🧑🏻‍🤝‍🧑🏽", "category": "People & Body (family)", "order": ""}, + {"emoji": "🧑🏻‍🤝‍🧑🏾", "name": "people holding hands: light skin tone, medium-dark skin tone", "shortname": ":light_skin_tone_mediumdark_skin_tone:", "unicode": "1F9D1 1F3FB 200D 1F91D 200D 1F9D1 1F3FE", "html": "🧑🏻‍🤝‍🧑🏾", "category": "People & Body (family)", "order": ""}, + {"emoji": "🧑🏻‍🤝‍🧑🏿", "name": "people holding hands: light skin tone, dark skin tone", "shortname": ":light_skin_tone_dark_skin_tone:", "unicode": "1F9D1 1F3FB 200D 1F91D 200D 1F9D1 1F3FF", "html": "🧑🏻‍🤝‍🧑🏿", "category": "People & Body (family)", "order": ""}, + {"emoji": "🧑🏼‍🤝‍🧑🏻", "name": "people holding hands: medium-light skin tone, light skin tone", "shortname": ":mediumlight_skin_tone_light_skin_tone:", "unicode": "1F9D1 1F3FC 200D 1F91D 200D 1F9D1 1F3FB", "html": "🧑🏼‍🤝‍🧑🏻", "category": "People & Body (family)", "order": ""}, + {"emoji": "🧑🏼‍🤝‍🧑🏼", "name": "people holding hands: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F9D1 1F3FC 200D 1F91D 200D 1F9D1 1F3FC", "html": "🧑🏼‍🤝‍🧑🏼", "category": "People & Body (family)", "order": ""}, + {"emoji": "🧑🏼‍🤝‍🧑🏽", "name": "people holding hands: medium-light skin tone, medium skin tone", "shortname": ":mediumlight_skin_tone_medium_skin_tone:", "unicode": "1F9D1 1F3FC 200D 1F91D 200D 1F9D1 1F3FD", "html": "🧑🏼‍🤝‍🧑🏽", "category": "People & Body (family)", "order": ""}, + {"emoji": "🧑🏼‍🤝‍🧑🏾", "name": "people holding hands: medium-light skin tone, medium-dark skin tone", "shortname": ":mediumlight_skin_tone_medium-dark_skin_tone:", "unicode": "1F9D1 1F3FC 200D 1F91D 200D 1F9D1 1F3FE", "html": "🧑🏼‍🤝‍🧑🏾", "category": "People & Body (family)", "order": ""}, + {"emoji": "🧑🏼‍🤝‍🧑🏿", "name": "people holding hands: medium-light skin tone, dark skin tone", "shortname": ":mediumlight_skin_tone_dark_skin_tone:", "unicode": "1F9D1 1F3FC 200D 1F91D 200D 1F9D1 1F3FF", "html": "🧑🏼‍🤝‍🧑🏿", "category": "People & Body (family)", "order": ""}, + {"emoji": "🧑🏽‍🤝‍🧑🏻", "name": "people holding hands: medium skin tone, light skin tone", "shortname": ":medium_skin_tone_light_skin_tone:", "unicode": "1F9D1 1F3FD 200D 1F91D 200D 1F9D1 1F3FB", "html": "🧑🏽‍🤝‍🧑🏻", "category": "People & Body (family)", "order": ""}, + {"emoji": "🧑🏽‍🤝‍🧑🏼", "name": "people holding hands: medium skin tone, medium-light skin tone", "shortname": ":medium_skin_tone_mediumlight_skin_tone:", "unicode": "1F9D1 1F3FD 200D 1F91D 200D 1F9D1 1F3FC", "html": "🧑🏽‍🤝‍🧑🏼", "category": "People & Body (family)", "order": ""}, + {"emoji": "🧑🏽‍🤝‍🧑🏽", "name": "people holding hands: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F9D1 1F3FD 200D 1F91D 200D 1F9D1 1F3FD", "html": "🧑🏽‍🤝‍🧑🏽", "category": "People & Body (family)", "order": ""}, + {"emoji": "🧑🏽‍🤝‍🧑🏾", "name": "people holding hands: medium skin tone, medium-dark skin tone", "shortname": ":medium_skin_tone_mediumdark_skin_tone:", "unicode": "1F9D1 1F3FD 200D 1F91D 200D 1F9D1 1F3FE", "html": "🧑🏽‍🤝‍🧑🏾", "category": "People & Body (family)", "order": ""}, + {"emoji": "🧑🏽‍🤝‍🧑🏿", "name": "people holding hands: medium skin tone, dark skin tone", "shortname": ":medium_skin_tone_dark_skin_tone:", "unicode": "1F9D1 1F3FD 200D 1F91D 200D 1F9D1 1F3FF", "html": "🧑🏽‍🤝‍🧑🏿", "category": "People & Body (family)", "order": ""}, + {"emoji": "🧑🏾‍🤝‍🧑🏻", "name": "people holding hands: medium-dark skin tone, light skin tone", "shortname": ":mediumdark_skin_tone_light_skin_tone:", "unicode": "1F9D1 1F3FE 200D 1F91D 200D 1F9D1 1F3FB", "html": "🧑🏾‍🤝‍🧑🏻", "category": "People & Body (family)", "order": ""}, + {"emoji": "🧑🏾‍🤝‍🧑🏼", "name": "people holding hands: medium-dark skin tone, medium-light skin tone", "shortname": ":mediumdark_skin_tone_medium-light_skin_tone:", "unicode": "1F9D1 1F3FE 200D 1F91D 200D 1F9D1 1F3FC", "html": "🧑🏾‍🤝‍🧑🏼", "category": "People & Body (family)", "order": ""}, + {"emoji": "🧑🏾‍🤝‍🧑🏽", "name": "people holding hands: medium-dark skin tone, medium skin tone", "shortname": ":mediumdark_skin_tone_medium_skin_tone:", "unicode": "1F9D1 1F3FE 200D 1F91D 200D 1F9D1 1F3FD", "html": "🧑🏾‍🤝‍🧑🏽", "category": "People & Body (family)", "order": ""}, + {"emoji": "🧑🏾‍🤝‍🧑🏾", "name": "people holding hands: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F9D1 1F3FE 200D 1F91D 200D 1F9D1 1F3FE", "html": "🧑🏾‍🤝‍🧑🏾", "category": "People & Body (family)", "order": ""}, + {"emoji": "🧑🏾‍🤝‍🧑🏿", "name": "people holding hands: medium-dark skin tone, dark skin tone", "shortname": ":mediumdark_skin_tone_dark_skin_tone:", "unicode": "1F9D1 1F3FE 200D 1F91D 200D 1F9D1 1F3FF", "html": "🧑🏾‍🤝‍🧑🏿", "category": "People & Body (family)", "order": ""}, + {"emoji": "🧑🏿‍🤝‍🧑🏻", "name": "people holding hands: dark skin tone, light skin tone", "shortname": ":dark_skin_tone_light_skin_tone:", "unicode": "1F9D1 1F3FF 200D 1F91D 200D 1F9D1 1F3FB", "html": "🧑🏿‍🤝‍🧑🏻", "category": "People & Body (family)", "order": ""}, + {"emoji": "🧑🏿‍🤝‍🧑🏼", "name": "people holding hands: dark skin tone, medium-light skin tone", "shortname": ":dark_skin_tone_mediumlight_skin_tone:", "unicode": "1F9D1 1F3FF 200D 1F91D 200D 1F9D1 1F3FC", "html": "🧑🏿‍🤝‍🧑🏼", "category": "People & Body (family)", "order": ""}, + {"emoji": "🧑🏿‍🤝‍🧑🏽", "name": "people holding hands: dark skin tone, medium skin tone", "shortname": ":dark_skin_tone_medium_skin_tone:", "unicode": "1F9D1 1F3FF 200D 1F91D 200D 1F9D1 1F3FD", "html": "🧑🏿‍🤝‍🧑🏽", "category": "People & Body (family)", "order": ""}, + {"emoji": "🧑🏿‍🤝‍🧑🏾", "name": "people holding hands: dark skin tone, medium-dark skin tone", "shortname": ":dark_skin_tone_mediumdark_skin_tone:", "unicode": "1F9D1 1F3FF 200D 1F91D 200D 1F9D1 1F3FE", "html": "🧑🏿‍🤝‍🧑🏾", "category": "People & Body (family)", "order": ""}, + {"emoji": "🧑🏿‍🤝‍🧑🏿", "name": "people holding hands: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F9D1 1F3FF 200D 1F91D 200D 1F9D1 1F3FF", "html": "🧑🏿‍🤝‍🧑🏿", "category": "People & Body (family)", "order": ""}, + {"emoji": "👭🏻", "name": "women holding hands: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F46D 1F3FB", "html": "👭🏻", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏻‍🤝‍👩🏼", "name": "women holding hands: light skin tone, medium-light skin tone", "shortname": ":light_skin_tone_mediumlight_skin_tone:", "unicode": "1F469 1F3FB 200D 1F91D 200D 1F469 1F3FC", "html": "👩🏻‍🤝‍👩🏼", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏻‍🤝‍👩🏽", "name": "women holding hands: light skin tone, medium skin tone", "shortname": ":light_skin_tone_medium_skin_tone:", "unicode": "1F469 1F3FB 200D 1F91D 200D 1F469 1F3FD", "html": "👩🏻‍🤝‍👩🏽", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏻‍🤝‍👩🏾", "name": "women holding hands: light skin tone, medium-dark skin tone", "shortname": ":light_skin_tone_mediumdark_skin_tone:", "unicode": "1F469 1F3FB 200D 1F91D 200D 1F469 1F3FE", "html": "👩🏻‍🤝‍👩🏾", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏻‍🤝‍👩🏿", "name": "women holding hands: light skin tone, dark skin tone", "shortname": ":light_skin_tone_dark_skin_tone:", "unicode": "1F469 1F3FB 200D 1F91D 200D 1F469 1F3FF", "html": "👩🏻‍🤝‍👩🏿", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏼‍🤝‍👩🏻", "name": "women holding hands: medium-light skin tone, light skin tone", "shortname": ":mediumlight_skin_tone_light_skin_tone:", "unicode": "1F469 1F3FC 200D 1F91D 200D 1F469 1F3FB", "html": "👩🏼‍🤝‍👩🏻", "category": "People & Body (family)", "order": ""}, + {"emoji": "👭🏼", "name": "women holding hands: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F46D 1F3FC", "html": "👭🏼", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏼‍🤝‍👩🏽", "name": "women holding hands: medium-light skin tone, medium skin tone", "shortname": ":mediumlight_skin_tone_medium_skin_tone:", "unicode": "1F469 1F3FC 200D 1F91D 200D 1F469 1F3FD", "html": "👩🏼‍🤝‍👩🏽", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏼‍🤝‍👩🏾", "name": "women holding hands: medium-light skin tone, medium-dark skin tone", "shortname": ":mediumlight_skin_tone_medium-dark_skin_tone:", "unicode": "1F469 1F3FC 200D 1F91D 200D 1F469 1F3FE", "html": "👩🏼‍🤝‍👩🏾", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏼‍🤝‍👩🏿", "name": "women holding hands: medium-light skin tone, dark skin tone", "shortname": ":mediumlight_skin_tone_dark_skin_tone:", "unicode": "1F469 1F3FC 200D 1F91D 200D 1F469 1F3FF", "html": "👩🏼‍🤝‍👩🏿", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏽‍🤝‍👩🏻", "name": "women holding hands: medium skin tone, light skin tone", "shortname": ":medium_skin_tone_light_skin_tone:", "unicode": "1F469 1F3FD 200D 1F91D 200D 1F469 1F3FB", "html": "👩🏽‍🤝‍👩🏻", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏽‍🤝‍👩🏼", "name": "women holding hands: medium skin tone, medium-light skin tone", "shortname": ":medium_skin_tone_mediumlight_skin_tone:", "unicode": "1F469 1F3FD 200D 1F91D 200D 1F469 1F3FC", "html": "👩🏽‍🤝‍👩🏼", "category": "People & Body (family)", "order": ""}, + {"emoji": "👭🏽", "name": "women holding hands: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F46D 1F3FD", "html": "👭🏽", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏽‍🤝‍👩🏾", "name": "women holding hands: medium skin tone, medium-dark skin tone", "shortname": ":medium_skin_tone_mediumdark_skin_tone:", "unicode": "1F469 1F3FD 200D 1F91D 200D 1F469 1F3FE", "html": "👩🏽‍🤝‍👩🏾", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏽‍🤝‍👩🏿", "name": "women holding hands: medium skin tone, dark skin tone", "shortname": ":medium_skin_tone_dark_skin_tone:", "unicode": "1F469 1F3FD 200D 1F91D 200D 1F469 1F3FF", "html": "👩🏽‍🤝‍👩🏿", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏾‍🤝‍👩🏻", "name": "women holding hands: medium-dark skin tone, light skin tone", "shortname": ":mediumdark_skin_tone_light_skin_tone:", "unicode": "1F469 1F3FE 200D 1F91D 200D 1F469 1F3FB", "html": "👩🏾‍🤝‍👩🏻", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏾‍🤝‍👩🏼", "name": "women holding hands: medium-dark skin tone, medium-light skin tone", "shortname": ":mediumdark_skin_tone_medium-light_skin_tone:", "unicode": "1F469 1F3FE 200D 1F91D 200D 1F469 1F3FC", "html": "👩🏾‍🤝‍👩🏼", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏾‍🤝‍👩🏽", "name": "women holding hands: medium-dark skin tone, medium skin tone", "shortname": ":mediumdark_skin_tone_medium_skin_tone:", "unicode": "1F469 1F3FE 200D 1F91D 200D 1F469 1F3FD", "html": "👩🏾‍🤝‍👩🏽", "category": "People & Body (family)", "order": ""}, + {"emoji": "👭🏾", "name": "women holding hands: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F46D 1F3FE", "html": "👭🏾", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏾‍🤝‍👩🏿", "name": "women holding hands: medium-dark skin tone, dark skin tone", "shortname": ":mediumdark_skin_tone_dark_skin_tone:", "unicode": "1F469 1F3FE 200D 1F91D 200D 1F469 1F3FF", "html": "👩🏾‍🤝‍👩🏿", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏿‍🤝‍👩🏻", "name": "women holding hands: dark skin tone, light skin tone", "shortname": ":dark_skin_tone_light_skin_tone:", "unicode": "1F469 1F3FF 200D 1F91D 200D 1F469 1F3FB", "html": "👩🏿‍🤝‍👩🏻", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏿‍🤝‍👩🏼", "name": "women holding hands: dark skin tone, medium-light skin tone", "shortname": ":dark_skin_tone_mediumlight_skin_tone:", "unicode": "1F469 1F3FF 200D 1F91D 200D 1F469 1F3FC", "html": "👩🏿‍🤝‍👩🏼", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏿‍🤝‍👩🏽", "name": "women holding hands: dark skin tone, medium skin tone", "shortname": ":dark_skin_tone_medium_skin_tone:", "unicode": "1F469 1F3FF 200D 1F91D 200D 1F469 1F3FD", "html": "👩🏿‍🤝‍👩🏽", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏿‍🤝‍👩🏾", "name": "women holding hands: dark skin tone, medium-dark skin tone", "shortname": ":dark_skin_tone_mediumdark_skin_tone:", "unicode": "1F469 1F3FF 200D 1F91D 200D 1F469 1F3FE", "html": "👩🏿‍🤝‍👩🏾", "category": "People & Body (family)", "order": ""}, + {"emoji": "👭🏿", "name": "women holding hands: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F46D 1F3FF", "html": "👭🏿", "category": "People & Body (family)", "order": ""}, + {"emoji": "👫🏻", "name": "woman and man holding hands: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F46B 1F3FB", "html": "👫🏻", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏻‍🤝‍👨🏼", "name": "woman and man holding hands: light skin tone, medium-light skin tone", "shortname": ":light_skin_tone_mediumlight_skin_tone:", "unicode": "1F469 1F3FB 200D 1F91D 200D 1F468 1F3FC", "html": "👩🏻‍🤝‍👨🏼", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏻‍🤝‍👨🏽", "name": "woman and man holding hands: light skin tone, medium skin tone", "shortname": ":light_skin_tone_medium_skin_tone:", "unicode": "1F469 1F3FB 200D 1F91D 200D 1F468 1F3FD", "html": "👩🏻‍🤝‍👨🏽", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏻‍🤝‍👨🏾", "name": "woman and man holding hands: light skin tone, medium-dark skin tone", "shortname": ":light_skin_tone_mediumdark_skin_tone:", "unicode": "1F469 1F3FB 200D 1F91D 200D 1F468 1F3FE", "html": "👩🏻‍🤝‍👨🏾", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏻‍🤝‍👨🏿", "name": "woman and man holding hands: light skin tone, dark skin tone", "shortname": ":light_skin_tone_dark_skin_tone:", "unicode": "1F469 1F3FB 200D 1F91D 200D 1F468 1F3FF", "html": "👩🏻‍🤝‍👨🏿", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏼‍🤝‍👨🏻", "name": "woman and man holding hands: medium-light skin tone, light skin tone", "shortname": ":mediumlight_skin_tone_light_skin_tone:", "unicode": "1F469 1F3FC 200D 1F91D 200D 1F468 1F3FB", "html": "👩🏼‍🤝‍👨🏻", "category": "People & Body (family)", "order": ""}, + {"emoji": "👫🏼", "name": "woman and man holding hands: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F46B 1F3FC", "html": "👫🏼", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏼‍🤝‍👨🏽", "name": "woman and man holding hands: medium-light skin tone, medium skin tone", "shortname": ":mediumlight_skin_tone_medium_skin_tone:", "unicode": "1F469 1F3FC 200D 1F91D 200D 1F468 1F3FD", "html": "👩🏼‍🤝‍👨🏽", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏼‍🤝‍👨🏾", "name": "woman and man holding hands: medium-light skin tone, medium-dark skin tone", "shortname": ":mediumlight_skin_tone_medium-dark_skin_tone:", "unicode": "1F469 1F3FC 200D 1F91D 200D 1F468 1F3FE", "html": "👩🏼‍🤝‍👨🏾", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏼‍🤝‍👨🏿", "name": "woman and man holding hands: medium-light skin tone, dark skin tone", "shortname": ":mediumlight_skin_tone_dark_skin_tone:", "unicode": "1F469 1F3FC 200D 1F91D 200D 1F468 1F3FF", "html": "👩🏼‍🤝‍👨🏿", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏽‍🤝‍👨🏻", "name": "woman and man holding hands: medium skin tone, light skin tone", "shortname": ":medium_skin_tone_light_skin_tone:", "unicode": "1F469 1F3FD 200D 1F91D 200D 1F468 1F3FB", "html": "👩🏽‍🤝‍👨🏻", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏽‍🤝‍👨🏼", "name": "woman and man holding hands: medium skin tone, medium-light skin tone", "shortname": ":medium_skin_tone_mediumlight_skin_tone:", "unicode": "1F469 1F3FD 200D 1F91D 200D 1F468 1F3FC", "html": "👩🏽‍🤝‍👨🏼", "category": "People & Body (family)", "order": ""}, + {"emoji": "👫🏽", "name": "woman and man holding hands: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F46B 1F3FD", "html": "👫🏽", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏽‍🤝‍👨🏾", "name": "woman and man holding hands: medium skin tone, medium-dark skin tone", "shortname": ":medium_skin_tone_mediumdark_skin_tone:", "unicode": "1F469 1F3FD 200D 1F91D 200D 1F468 1F3FE", "html": "👩🏽‍🤝‍👨🏾", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏽‍🤝‍👨🏿", "name": "woman and man holding hands: medium skin tone, dark skin tone", "shortname": ":medium_skin_tone_dark_skin_tone:", "unicode": "1F469 1F3FD 200D 1F91D 200D 1F468 1F3FF", "html": "👩🏽‍🤝‍👨🏿", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏾‍🤝‍👨🏻", "name": "woman and man holding hands: medium-dark skin tone, light skin tone", "shortname": ":mediumdark_skin_tone_light_skin_tone:", "unicode": "1F469 1F3FE 200D 1F91D 200D 1F468 1F3FB", "html": "👩🏾‍🤝‍👨🏻", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏾‍🤝‍👨🏼", "name": "woman and man holding hands: medium-dark skin tone, medium-light skin tone", "shortname": ":mediumdark_skin_tone_medium-light_skin_tone:", "unicode": "1F469 1F3FE 200D 1F91D 200D 1F468 1F3FC", "html": "👩🏾‍🤝‍👨🏼", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏾‍🤝‍👨🏽", "name": "woman and man holding hands: medium-dark skin tone, medium skin tone", "shortname": ":mediumdark_skin_tone_medium_skin_tone:", "unicode": "1F469 1F3FE 200D 1F91D 200D 1F468 1F3FD", "html": "👩🏾‍🤝‍👨🏽", "category": "People & Body (family)", "order": ""}, + {"emoji": "👫🏾", "name": "woman and man holding hands: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F46B 1F3FE", "html": "👫🏾", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏾‍🤝‍👨🏿", "name": "woman and man holding hands: medium-dark skin tone, dark skin tone", "shortname": ":mediumdark_skin_tone_dark_skin_tone:", "unicode": "1F469 1F3FE 200D 1F91D 200D 1F468 1F3FF", "html": "👩🏾‍🤝‍👨🏿", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏿‍🤝‍👨🏻", "name": "woman and man holding hands: dark skin tone, light skin tone", "shortname": ":dark_skin_tone_light_skin_tone:", "unicode": "1F469 1F3FF 200D 1F91D 200D 1F468 1F3FB", "html": "👩🏿‍🤝‍👨🏻", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏿‍🤝‍👨🏼", "name": "woman and man holding hands: dark skin tone, medium-light skin tone", "shortname": ":dark_skin_tone_mediumlight_skin_tone:", "unicode": "1F469 1F3FF 200D 1F91D 200D 1F468 1F3FC", "html": "👩🏿‍🤝‍👨🏼", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏿‍🤝‍👨🏽", "name": "woman and man holding hands: dark skin tone, medium skin tone", "shortname": ":dark_skin_tone_medium_skin_tone:", "unicode": "1F469 1F3FF 200D 1F91D 200D 1F468 1F3FD", "html": "👩🏿‍🤝‍👨🏽", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩🏿‍🤝‍👨🏾", "name": "woman and man holding hands: dark skin tone, medium-dark skin tone", "shortname": ":dark_skin_tone_mediumdark_skin_tone:", "unicode": "1F469 1F3FF 200D 1F91D 200D 1F468 1F3FE", "html": "👩🏿‍🤝‍👨🏾", "category": "People & Body (family)", "order": ""}, + {"emoji": "👫🏿", "name": "woman and man holding hands: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F46B 1F3FF", "html": "👫🏿", "category": "People & Body (family)", "order": ""}, + {"emoji": "👬🏻", "name": "men holding hands: light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F46C 1F3FB", "html": "👬🏻", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨🏻‍🤝‍👨🏼", "name": "men holding hands: light skin tone, medium-light skin tone", "shortname": ":light_skin_tone_mediumlight_skin_tone:", "unicode": "1F468 1F3FB 200D 1F91D 200D 1F468 1F3FC", "html": "👨🏻‍🤝‍👨🏼", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨🏻‍🤝‍👨🏽", "name": "men holding hands: light skin tone, medium skin tone", "shortname": ":light_skin_tone_medium_skin_tone:", "unicode": "1F468 1F3FB 200D 1F91D 200D 1F468 1F3FD", "html": "👨🏻‍🤝‍👨🏽", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨🏻‍🤝‍👨🏾", "name": "men holding hands: light skin tone, medium-dark skin tone", "shortname": ":light_skin_tone_mediumdark_skin_tone:", "unicode": "1F468 1F3FB 200D 1F91D 200D 1F468 1F3FE", "html": "👨🏻‍🤝‍👨🏾", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨🏻‍🤝‍👨🏿", "name": "men holding hands: light skin tone, dark skin tone", "shortname": ":light_skin_tone_dark_skin_tone:", "unicode": "1F468 1F3FB 200D 1F91D 200D 1F468 1F3FF", "html": "👨🏻‍🤝‍👨🏿", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨🏼‍🤝‍👨🏻", "name": "men holding hands: medium-light skin tone, light skin tone", "shortname": ":mediumlight_skin_tone_light_skin_tone:", "unicode": "1F468 1F3FC 200D 1F91D 200D 1F468 1F3FB", "html": "👨🏼‍🤝‍👨🏻", "category": "People & Body (family)", "order": ""}, + {"emoji": "👬🏼", "name": "men holding hands: medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F46C 1F3FC", "html": "👬🏼", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨🏼‍🤝‍👨🏽", "name": "men holding hands: medium-light skin tone, medium skin tone", "shortname": ":mediumlight_skin_tone_medium_skin_tone:", "unicode": "1F468 1F3FC 200D 1F91D 200D 1F468 1F3FD", "html": "👨🏼‍🤝‍👨🏽", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨🏼‍🤝‍👨🏾", "name": "men holding hands: medium-light skin tone, medium-dark skin tone", "shortname": ":mediumlight_skin_tone_medium-dark_skin_tone:", "unicode": "1F468 1F3FC 200D 1F91D 200D 1F468 1F3FE", "html": "👨🏼‍🤝‍👨🏾", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨🏼‍🤝‍👨🏿", "name": "men holding hands: medium-light skin tone, dark skin tone", "shortname": ":mediumlight_skin_tone_dark_skin_tone:", "unicode": "1F468 1F3FC 200D 1F91D 200D 1F468 1F3FF", "html": "👨🏼‍🤝‍👨🏿", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨🏽‍🤝‍👨🏻", "name": "men holding hands: medium skin tone, light skin tone", "shortname": ":medium_skin_tone_light_skin_tone:", "unicode": "1F468 1F3FD 200D 1F91D 200D 1F468 1F3FB", "html": "👨🏽‍🤝‍👨🏻", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨🏽‍🤝‍👨🏼", "name": "men holding hands: medium skin tone, medium-light skin tone", "shortname": ":medium_skin_tone_mediumlight_skin_tone:", "unicode": "1F468 1F3FD 200D 1F91D 200D 1F468 1F3FC", "html": "👨🏽‍🤝‍👨🏼", "category": "People & Body (family)", "order": ""}, + {"emoji": "👬🏽", "name": "men holding hands: medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F46C 1F3FD", "html": "👬🏽", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨🏽‍🤝‍👨🏾", "name": "men holding hands: medium skin tone, medium-dark skin tone", "shortname": ":medium_skin_tone_mediumdark_skin_tone:", "unicode": "1F468 1F3FD 200D 1F91D 200D 1F468 1F3FE", "html": "👨🏽‍🤝‍👨🏾", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨🏽‍🤝‍👨🏿", "name": "men holding hands: medium skin tone, dark skin tone", "shortname": ":medium_skin_tone_dark_skin_tone:", "unicode": "1F468 1F3FD 200D 1F91D 200D 1F468 1F3FF", "html": "👨🏽‍🤝‍👨🏿", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨🏾‍🤝‍👨🏻", "name": "men holding hands: medium-dark skin tone, light skin tone", "shortname": ":mediumdark_skin_tone_light_skin_tone:", "unicode": "1F468 1F3FE 200D 1F91D 200D 1F468 1F3FB", "html": "👨🏾‍🤝‍👨🏻", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨🏾‍🤝‍👨🏼", "name": "men holding hands: medium-dark skin tone, medium-light skin tone", "shortname": ":mediumdark_skin_tone_medium-light_skin_tone:", "unicode": "1F468 1F3FE 200D 1F91D 200D 1F468 1F3FC", "html": "👨🏾‍🤝‍👨🏼", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨🏾‍🤝‍👨🏽", "name": "men holding hands: medium-dark skin tone, medium skin tone", "shortname": ":mediumdark_skin_tone_medium_skin_tone:", "unicode": "1F468 1F3FE 200D 1F91D 200D 1F468 1F3FD", "html": "👨🏾‍🤝‍👨🏽", "category": "People & Body (family)", "order": ""}, + {"emoji": "👬🏾", "name": "men holding hands: medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F46C 1F3FE", "html": "👬🏾", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨🏾‍🤝‍👨🏿", "name": "men holding hands: medium-dark skin tone, dark skin tone", "shortname": ":mediumdark_skin_tone_dark_skin_tone:", "unicode": "1F468 1F3FE 200D 1F91D 200D 1F468 1F3FF", "html": "👨🏾‍🤝‍👨🏿", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨🏿‍🤝‍👨🏻", "name": "men holding hands: dark skin tone, light skin tone", "shortname": ":dark_skin_tone_light_skin_tone:", "unicode": "1F468 1F3FF 200D 1F91D 200D 1F468 1F3FB", "html": "👨🏿‍🤝‍👨🏻", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨🏿‍🤝‍👨🏼", "name": "men holding hands: dark skin tone, medium-light skin tone", "shortname": ":dark_skin_tone_mediumlight_skin_tone:", "unicode": "1F468 1F3FF 200D 1F91D 200D 1F468 1F3FC", "html": "👨🏿‍🤝‍👨🏼", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨🏿‍🤝‍👨🏽", "name": "men holding hands: dark skin tone, medium skin tone", "shortname": ":dark_skin_tone_medium_skin_tone:", "unicode": "1F468 1F3FF 200D 1F91D 200D 1F468 1F3FD", "html": "👨🏿‍🤝‍👨🏽", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨🏿‍🤝‍👨🏾", "name": "men holding hands: dark skin tone, medium-dark skin tone", "shortname": ":dark_skin_tone_mediumdark_skin_tone:", "unicode": "1F468 1F3FF 200D 1F91D 200D 1F468 1F3FE", "html": "👨🏿‍🤝‍👨🏾", "category": "People & Body (family)", "order": ""}, + {"emoji": "👬🏿", "name": "men holding hands: dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F46C 1F3FF", "html": "👬🏿", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩‍❤️‍💋‍👨", "name": "kiss: woman, man", "shortname": ":woman_man:", "unicode": "1F469 200D 2764 FE0F 200D 1F48B 200D 1F468", "html": "👩‍❤️‍💋‍👨", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩‍❤‍💋‍👨", "name": "kiss: woman, man", "shortname": ":woman_man:", "unicode": "1F469 200D 2764 200D 1F48B 200D 1F468", "html": "👩‍❤‍💋‍👨", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨‍❤‍💋‍👨", "name": "kiss: man, man", "shortname": ":man_man:", "unicode": "1F468 200D 2764 200D 1F48B 200D 1F468", "html": "👨‍❤‍💋‍👨", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩‍❤‍💋‍👩", "name": "kiss: woman, woman", "shortname": ":woman_woman:", "unicode": "1F469 200D 2764 200D 1F48B 200D 1F469", "html": "👩‍❤‍💋‍👩", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩‍❤️‍👨", "name": "couple with heart: woman, man", "shortname": ":woman_man:", "unicode": "1F469 200D 2764 FE0F 200D 1F468", "html": "👩‍❤️‍👨", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩‍❤‍👨", "name": "couple with heart: woman, man", "shortname": ":woman_man:", "unicode": "1F469 200D 2764 200D 1F468", "html": "👩‍❤‍👨", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨‍❤‍👨", "name": "couple with heart: man, man", "shortname": ":man_man:", "unicode": "1F468 200D 2764 200D 1F468", "html": "👨‍❤‍👨", "category": "People & Body (family)", "order": ""}, + {"emoji": "👩‍❤‍👩", "name": "couple with heart: woman, woman", "shortname": ":woman_woman:", "unicode": "1F469 200D 2764 200D 1F469", "html": "👩‍❤‍👩", "category": "People & Body (family)", "order": ""}, + {"emoji": "👨‍👩‍👦", "name": "family: man, woman, boy", "shortname": ":man_woman_boy:", "unicode": "1F468 200D 1F469 200D 1F466", "html": "👨‍👩‍👦", "category": "People & Body (family)", "order": ""}, + {"emoji": "🗣️", "name": "speaking head", "shortname": ":speaking_head:", "unicode": "1F5E3 FE0F", "html": "🗣️", "category": "People & Body (person-symbol)", "order": ""}, + {"emoji": "🏻", "name": "light skin tone", "shortname": ":light_skin_tone:", "unicode": "1F3FB", "html": "🏻", "category": "Component (skin-tone)", "order": ""}, + {"emoji": "🏼", "name": "medium-light skin tone", "shortname": ":mediumlight_skin_tone:", "unicode": "1F3FC", "html": "🏼", "category": "Component (skin-tone)", "order": ""}, + {"emoji": "🏽", "name": "medium skin tone", "shortname": ":medium_skin_tone:", "unicode": "1F3FD", "html": "🏽", "category": "Component (skin-tone)", "order": ""}, + {"emoji": "🏾", "name": "medium-dark skin tone", "shortname": ":mediumdark_skin_tone:", "unicode": "1F3FE", "html": "🏾", "category": "Component (skin-tone)", "order": ""}, + {"emoji": "🏿", "name": "dark skin tone", "shortname": ":dark_skin_tone:", "unicode": "1F3FF", "html": "🏿", "category": "Component (skin-tone)", "order": ""}, + {"emoji": "🦰", "name": "red hair", "shortname": ":red_hair:", "unicode": "1F9B0", "html": "🦰", "category": "Component (hair-style)", "order": ""}, + {"emoji": "🦱", "name": "curly hair", "shortname": ":curly_hair:", "unicode": "1F9B1", "html": "🦱", "category": "Component (hair-style)", "order": ""}, + {"emoji": "🦳", "name": "white hair", "shortname": ":white_hair:", "unicode": "1F9B3", "html": "🦳", "category": "Component (hair-style)", "order": ""}, + {"emoji": "🦲", "name": "bald", "shortname": ":bald:", "unicode": "1F9B2", "html": "🦲", "category": "Component (hair-style)", "order": ""}, + {"emoji": "🦍", "name": "gorilla", "shortname": ":gorilla:", "unicode": "1F98D", "html": "🦍", "category": "Animals & Nature (animal-mammal)", "order": ""}, + {"emoji": "🦧", "name": "orangutan", "shortname": ":orangutan:", "unicode": "1F9A7", "html": "🦧", "category": "Animals & Nature (animal-mammal)", "order": ""}, + {"emoji": "🦮", "name": "guide dog", "shortname": ":guide_dog:", "unicode": "1F9AE", "html": "🦮", "category": "Animals & Nature (animal-mammal)", "order": ""}, + {"emoji": "🐕‍🦺", "name": "service dog", "shortname": ":service_dog:", "unicode": "1F415 200D 1F9BA", "html": "🐕‍🦺", "category": "Animals & Nature (animal-mammal)", "order": ""}, + {"emoji": "🦊", "name": "fox", "shortname": ":fox:", "unicode": "1F98A", "html": "🦊", "category": "Animals & Nature (animal-mammal)", "order": ""}, + {"emoji": "🦝", "name": "raccoon", "shortname": ":raccoon:", "unicode": "1F99D", "html": "🦝", "category": "Animals & Nature (animal-mammal)", "order": ""}, + {"emoji": "🦓", "name": "zebra", "shortname": ":zebra:", "unicode": "1F993", "html": "🦓", "category": "Animals & Nature (animal-mammal)", "order": ""}, + {"emoji": "🦌", "name": "deer", "shortname": ":deer:", "unicode": "1F98C", "html": "🦌", "category": "Animals & Nature (animal-mammal)", "order": ""}, + {"emoji": "🦙", "name": "llama", "shortname": ":llama:", "unicode": "1F999", "html": "🦙", "category": "Animals & Nature (animal-mammal)", "order": ""}, + {"emoji": "🦒", "name": "giraffe", "shortname": ":giraffe:", "unicode": "1F992", "html": "🦒", "category": "Animals & Nature (animal-mammal)", "order": ""}, + {"emoji": "🦏", "name": "rhinoceros", "shortname": ":rhinoceros:", "unicode": "1F98F", "html": "🦏", "category": "Animals & Nature (animal-mammal)", "order": ""}, + {"emoji": "🦛", "name": "hippopotamus", "shortname": ":hippopotamus:", "unicode": "1F99B", "html": "🦛", "category": "Animals & Nature (animal-mammal)", "order": ""}, + {"emoji": "🐿️", "name": "chipmunk", "shortname": ":chipmunk:", "unicode": "1F43F FE0F", "html": "🐿️", "category": "Animals & Nature (animal-mammal)", "order": ""}, + {"emoji": "🦔", "name": "hedgehog", "shortname": ":hedgehog:", "unicode": "1F994", "html": "🦔", "category": "Animals & Nature (animal-mammal)", "order": ""}, + {"emoji": "🦇", "name": "bat", "shortname": ":bat:", "unicode": "1F987", "html": "🦇", "category": "Animals & Nature (animal-mammal)", "order": ""}, + {"emoji": "🦥", "name": "sloth", "shortname": ":sloth:", "unicode": "1F9A5", "html": "🦥", "category": "Animals & Nature (animal-mammal)", "order": ""}, + {"emoji": "🦦", "name": "otter", "shortname": ":otter:", "unicode": "1F9A6", "html": "🦦", "category": "Animals & Nature (animal-mammal)", "order": ""}, + {"emoji": "🦨", "name": "skunk", "shortname": ":skunk:", "unicode": "1F9A8", "html": "🦨", "category": "Animals & Nature (animal-mammal)", "order": ""}, + {"emoji": "🦘", "name": "kangaroo", "shortname": ":kangaroo:", "unicode": "1F998", "html": "🦘", "category": "Animals & Nature (animal-mammal)", "order": ""}, + {"emoji": "🦡", "name": "badger", "shortname": ":badger:", "unicode": "1F9A1", "html": "🦡", "category": "Animals & Nature (animal-mammal)", "order": ""}, + {"emoji": "🕊️", "name": "dove", "shortname": ":dove:", "unicode": "1F54A FE0F", "html": "🕊️", "category": "Animals & Nature (animal-bird)", "order": ""}, + {"emoji": "🦅", "name": "eagle", "shortname": ":eagle:", "unicode": "1F985", "html": "🦅", "category": "Animals & Nature (animal-bird)", "order": ""}, + {"emoji": "🦆", "name": "duck", "shortname": ":duck:", "unicode": "1F986", "html": "🦆", "category": "Animals & Nature (animal-bird)", "order": ""}, + {"emoji": "🦢", "name": "swan", "shortname": ":swan:", "unicode": "1F9A2", "html": "🦢", "category": "Animals & Nature (animal-bird)", "order": ""}, + {"emoji": "🦉", "name": "owl", "shortname": ":owl:", "unicode": "1F989", "html": "🦉", "category": "Animals & Nature (animal-bird)", "order": ""}, + {"emoji": "🦩", "name": "flamingo", "shortname": ":flamingo:", "unicode": "1F9A9", "html": "🦩", "category": "Animals & Nature (animal-bird)", "order": ""}, + {"emoji": "🦚", "name": "peacock", "shortname": ":peacock:", "unicode": "1F99A", "html": "🦚", "category": "Animals & Nature (animal-bird)", "order": ""}, + {"emoji": "🦜", "name": "parrot", "shortname": ":parrot:", "unicode": "1F99C", "html": "🦜", "category": "Animals & Nature (animal-bird)", "order": ""}, + {"emoji": "🦎", "name": "lizard", "shortname": ":lizard:", "unicode": "1F98E", "html": "🦎", "category": "Animals & Nature (animal-reptile)", "order": ""}, + {"emoji": "🦕", "name": "sauropod", "shortname": ":sauropod:", "unicode": "1F995", "html": "🦕", "category": "Animals & Nature (animal-reptile)", "order": ""}, + {"emoji": "🦖", "name": "T-Rex", "shortname": ":TRex:", "unicode": "1F996", "html": "🦖", "category": "Animals & Nature (animal-reptile)", "order": ""}, + {"emoji": "🦈", "name": "shark", "shortname": ":shark:", "unicode": "1F988", "html": "🦈", "category": "Animals & Nature (animal-marine)", "order": ""}, + {"emoji": "🦋", "name": "butterfly", "shortname": ":butterfly:", "unicode": "1F98B", "html": "🦋", "category": "Animals & Nature (animal-bug)", "order": ""}, + {"emoji": "🦗", "name": "cricket", "shortname": ":cricket:", "unicode": "1F997", "html": "🦗", "category": "Animals & Nature (animal-bug)", "order": ""}, + {"emoji": "🕷️", "name": "spider", "shortname": ":spider:", "unicode": "1F577 FE0F", "html": "🕷️", "category": "Animals & Nature (animal-bug)", "order": ""}, + {"emoji": "🕸️", "name": "spider web", "shortname": ":spider_web:", "unicode": "1F578 FE0F", "html": "🕸️", "category": "Animals & Nature (animal-bug)", "order": ""}, + {"emoji": "🦟", "name": "mosquito", "shortname": ":mosquito:", "unicode": "1F99F", "html": "🦟", "category": "Animals & Nature (animal-bug)", "order": ""}, + {"emoji": "🦠", "name": "microbe", "shortname": ":microbe:", "unicode": "1F9A0", "html": "🦠", "category": "Animals & Nature (animal-bug)", "order": ""}, + {"emoji": "🏵️", "name": "rosette", "shortname": ":rosette:", "unicode": "1F3F5 FE0F", "html": "🏵️", "category": "Animals & Nature (plant-flower)", "order": ""}, + {"emoji": "🥀", "name": "wilted flower", "shortname": ":wilted_flower:", "unicode": "1F940", "html": "🥀", "category": "Animals & Nature (plant-flower)", "order": ""}, + {"emoji": "☘️", "name": "shamrock", "shortname": ":shamrock:", "unicode": "2618 FE0F", "html": "☘️", "category": "Animals & Nature (plant-other)", "order": ""}, + {"emoji": "🥭", "name": "mango", "shortname": ":mango:", "unicode": "1F96D", "html": "🥭", "category": "Food & Drink (food-fruit)", "order": ""}, + {"emoji": "🥝", "name": "kiwi fruit", "shortname": ":kiwi_fruit:", "unicode": "1F95D", "html": "🥝", "category": "Food & Drink (food-fruit)", "order": ""}, + {"emoji": "🥥", "name": "coconut", "shortname": ":coconut:", "unicode": "1F965", "html": "🥥", "category": "Food & Drink (food-fruit)", "order": ""}, + {"emoji": "🥑", "name": "avocado", "shortname": ":avocado:", "unicode": "1F951", "html": "🥑", "category": "Food & Drink (food-vegetable)", "order": ""}, + {"emoji": "🥔", "name": "potato", "shortname": ":potato:", "unicode": "1F954", "html": "🥔", "category": "Food & Drink (food-vegetable)", "order": ""}, + {"emoji": "🥕", "name": "carrot", "shortname": ":carrot:", "unicode": "1F955", "html": "🥕", "category": "Food & Drink (food-vegetable)", "order": ""}, + {"emoji": "🌶️", "name": "hot pepper", "shortname": ":hot_pepper:", "unicode": "1F336 FE0F", "html": "🌶️", "category": "Food & Drink (food-vegetable)", "order": ""}, + {"emoji": "🥒", "name": "cucumber", "shortname": ":cucumber:", "unicode": "1F952", "html": "🥒", "category": "Food & Drink (food-vegetable)", "order": ""}, + {"emoji": "🥬", "name": "leafy green", "shortname": ":leafy_green:", "unicode": "1F96C", "html": "🥬", "category": "Food & Drink (food-vegetable)", "order": ""}, + {"emoji": "🥦", "name": "broccoli", "shortname": ":broccoli:", "unicode": "1F966", "html": "🥦", "category": "Food & Drink (food-vegetable)", "order": ""}, + {"emoji": "🧄", "name": "garlic", "shortname": ":garlic:", "unicode": "1F9C4", "html": "🧄", "category": "Food & Drink (food-vegetable)", "order": ""}, + {"emoji": "🧅", "name": "onion", "shortname": ":onion:", "unicode": "1F9C5", "html": "🧅", "category": "Food & Drink (food-vegetable)", "order": ""}, + {"emoji": "🥜", "name": "peanuts", "shortname": ":peanuts:", "unicode": "1F95C", "html": "🥜", "category": "Food & Drink (food-vegetable)", "order": ""}, + {"emoji": "🥐", "name": "croissant", "shortname": ":croissant:", "unicode": "1F950", "html": "🥐", "category": "Food & Drink (food-prepared)", "order": ""}, + {"emoji": "🥖", "name": "baguette bread", "shortname": ":baguette_bread:", "unicode": "1F956", "html": "🥖", "category": "Food & Drink (food-prepared)", "order": ""}, + {"emoji": "🥨", "name": "pretzel", "shortname": ":pretzel:", "unicode": "1F968", "html": "🥨", "category": "Food & Drink (food-prepared)", "order": ""}, + {"emoji": "🥯", "name": "bagel", "shortname": ":bagel:", "unicode": "1F96F", "html": "🥯", "category": "Food & Drink (food-prepared)", "order": ""}, + {"emoji": "🥞", "name": "pancakes", "shortname": ":pancakes:", "unicode": "1F95E", "html": "🥞", "category": "Food & Drink (food-prepared)", "order": ""}, + {"emoji": "🧇", "name": "waffle", "shortname": ":waffle:", "unicode": "1F9C7", "html": "🧇", "category": "Food & Drink (food-prepared)", "order": ""}, + {"emoji": "🥩", "name": "cut of meat", "shortname": ":cut_of_meat:", "unicode": "1F969", "html": "🥩", "category": "Food & Drink (food-prepared)", "order": ""}, + {"emoji": "🥓", "name": "bacon", "shortname": ":bacon:", "unicode": "1F953", "html": "🥓", "category": "Food & Drink (food-prepared)", "order": ""}, + {"emoji": "🥪", "name": "sandwich", "shortname": ":sandwich:", "unicode": "1F96A", "html": "🥪", "category": "Food & Drink (food-prepared)", "order": ""}, + {"emoji": "🥙", "name": "stuffed flatbread", "shortname": ":stuffed_flatbread:", "unicode": "1F959", "html": "🥙", "category": "Food & Drink (food-prepared)", "order": ""}, + {"emoji": "🧆", "name": "falafel", "shortname": ":falafel:", "unicode": "1F9C6", "html": "🧆", "category": "Food & Drink (food-prepared)", "order": ""}, + {"emoji": "🥘", "name": "shallow pan of food", "shortname": ":shallow_pan_of_food:", "unicode": "1F958", "html": "🥘", "category": "Food & Drink (food-prepared)", "order": ""}, + {"emoji": "🥣", "name": "bowl with spoon", "shortname": ":bowl_with_spoon:", "unicode": "1F963", "html": "🥣", "category": "Food & Drink (food-prepared)", "order": ""}, + {"emoji": "🥗", "name": "green salad", "shortname": ":green_salad:", "unicode": "1F957", "html": "🥗", "category": "Food & Drink (food-prepared)", "order": ""}, + {"emoji": "🧈", "name": "butter", "shortname": ":butter:", "unicode": "1F9C8", "html": "🧈", "category": "Food & Drink (food-prepared)", "order": ""}, + {"emoji": "🧂", "name": "salt", "shortname": ":salt:", "unicode": "1F9C2", "html": "🧂", "category": "Food & Drink (food-prepared)", "order": ""}, + {"emoji": "🥫", "name": "canned food", "shortname": ":canned_food:", "unicode": "1F96B", "html": "🥫", "category": "Food & Drink (food-prepared)", "order": ""}, + {"emoji": "🥮", "name": "moon cake", "shortname": ":moon_cake:", "unicode": "1F96E", "html": "🥮", "category": "Food & Drink (food-asian)", "order": ""}, + {"emoji": "🥟", "name": "dumpling", "shortname": ":dumpling:", "unicode": "1F95F", "html": "🥟", "category": "Food & Drink (food-asian)", "order": ""}, + {"emoji": "🥠", "name": "fortune cookie", "shortname": ":fortune_cookie:", "unicode": "1F960", "html": "🥠", "category": "Food & Drink (food-asian)", "order": ""}, + {"emoji": "🥡", "name": "takeout box", "shortname": ":takeout_box:", "unicode": "1F961", "html": "🥡", "category": "Food & Drink (food-asian)", "order": ""}, + {"emoji": "🦞", "name": "lobster", "shortname": ":lobster:", "unicode": "1F99E", "html": "🦞", "category": "Food & Drink (food-marine)", "order": ""}, + {"emoji": "🦐", "name": "shrimp", "shortname": ":shrimp:", "unicode": "1F990", "html": "🦐", "category": "Food & Drink (food-marine)", "order": ""}, + {"emoji": "🦑", "name": "squid", "shortname": ":squid:", "unicode": "1F991", "html": "🦑", "category": "Food & Drink (food-marine)", "order": ""}, + {"emoji": "🦪", "name": "oyster", "shortname": ":oyster:", "unicode": "1F9AA", "html": "🦪", "category": "Food & Drink (food-marine)", "order": ""}, + {"emoji": "🧁", "name": "cupcake", "shortname": ":cupcake:", "unicode": "1F9C1", "html": "🧁", "category": "Food & Drink (food-sweet)", "order": ""}, + {"emoji": "🥧", "name": "pie", "shortname": ":pie:", "unicode": "1F967", "html": "🥧", "category": "Food & Drink (food-sweet)", "order": ""}, + {"emoji": "🥛", "name": "glass of milk", "shortname": ":glass_of_milk:", "unicode": "1F95B", "html": "🥛", "category": "Food & Drink (drink)", "order": ""}, + {"emoji": "🥂", "name": "clinking glasses", "shortname": ":clinking_glasses:", "unicode": "1F942", "html": "🥂", "category": "Food & Drink (drink)", "order": ""}, + {"emoji": "🥃", "name": "tumbler glass", "shortname": ":tumbler_glass:", "unicode": "1F943", "html": "🥃", "category": "Food & Drink (drink)", "order": ""}, + {"emoji": "🥤", "name": "cup with straw", "shortname": ":cup_with_straw:", "unicode": "1F964", "html": "🥤", "category": "Food & Drink (drink)", "order": ""}, + {"emoji": "🧃", "name": "beverage box", "shortname": ":beverage_box:", "unicode": "1F9C3", "html": "🧃", "category": "Food & Drink (drink)", "order": ""}, + {"emoji": "🧉", "name": "mate", "shortname": ":mate:", "unicode": "1F9C9", "html": "🧉", "category": "Food & Drink (drink)", "order": ""}, + {"emoji": "🧊", "name": "ice", "shortname": ":ice:", "unicode": "1F9CA", "html": "🧊", "category": "Food & Drink (drink)", "order": ""}, + {"emoji": "🥢", "name": "chopsticks", "shortname": ":chopsticks:", "unicode": "1F962", "html": "🥢", "category": "Food & Drink (dishware)", "order": ""}, + {"emoji": "🍽️", "name": "fork and knife with plate", "shortname": ":fork_and_knife_with_plate:", "unicode": "1F37D FE0F", "html": "🍽️", "category": "Food & Drink (dishware)", "order": ""}, + {"emoji": "🥄", "name": "spoon", "shortname": ":spoon:", "unicode": "1F944", "html": "🥄", "category": "Food & Drink (dishware)", "order": ""}, + {"emoji": "🗺️", "name": "world map", "shortname": ":world_map:", "unicode": "1F5FA FE0F", "html": "🗺️", "category": "Travel & Places (place-map)", "order": ""}, + {"emoji": "🧭", "name": "compass", "shortname": ":compass:", "unicode": "1F9ED", "html": "🧭", "category": "Travel & Places (place-map)", "order": ""}, + {"emoji": "🏔️", "name": "snow-capped mountain", "shortname": ":snowcapped_mountain:", "unicode": "1F3D4 FE0F", "html": "🏔️", "category": "Travel & Places (place-geographic)", "order": ""}, + {"emoji": "⛰️", "name": "mountain", "shortname": ":mountain:", "unicode": "26F0 FE0F", "html": "⛰️", "category": "Travel & Places (place-geographic)", "order": ""}, + {"emoji": "🏕️", "name": "camping", "shortname": ":camping:", "unicode": "1F3D5 FE0F", "html": "🏕️", "category": "Travel & Places (place-geographic)", "order": ""}, + {"emoji": "🏖️", "name": "beach with umbrella", "shortname": ":beach_with_umbrella:", "unicode": "1F3D6 FE0F", "html": "🏖️", "category": "Travel & Places (place-geographic)", "order": ""}, + {"emoji": "🏜️", "name": "desert", "shortname": ":desert:", "unicode": "1F3DC FE0F", "html": "🏜️", "category": "Travel & Places (place-geographic)", "order": ""}, + {"emoji": "🏝️", "name": "desert island", "shortname": ":desert_island:", "unicode": "1F3DD FE0F", "html": "🏝️", "category": "Travel & Places (place-geographic)", "order": ""}, + {"emoji": "🏞️", "name": "national park", "shortname": ":national_park:", "unicode": "1F3DE FE0F", "html": "🏞️", "category": "Travel & Places (place-geographic)", "order": ""}, + {"emoji": "🏟️", "name": "stadium", "shortname": ":stadium:", "unicode": "1F3DF FE0F", "html": "🏟️", "category": "Travel & Places (place-building)", "order": ""}, + {"emoji": "🏛️", "name": "classical building", "shortname": ":classical_building:", "unicode": "1F3DB FE0F", "html": "🏛️", "category": "Travel & Places (place-building)", "order": ""}, + {"emoji": "🏗️", "name": "building construction", "shortname": ":building_construction:", "unicode": "1F3D7 FE0F", "html": "🏗️", "category": "Travel & Places (place-building)", "order": ""}, + {"emoji": "🧱", "name": "brick", "shortname": ":brick:", "unicode": "1F9F1", "html": "🧱", "category": "Travel & Places (place-building)", "order": ""}, + {"emoji": "🏘️", "name": "houses", "shortname": ":houses:", "unicode": "1F3D8 FE0F", "html": "🏘️", "category": "Travel & Places (place-building)", "order": ""}, + {"emoji": "🏚️", "name": "derelict house", "shortname": ":derelict_house:", "unicode": "1F3DA FE0F", "html": "🏚️", "category": "Travel & Places (place-building)", "order": ""}, + {"emoji": "🛕", "name": "hindu temple", "shortname": ":hindu_temple:", "unicode": "1F6D5", "html": "🛕", "category": "Travel & Places (place-religious)", "order": ""}, + {"emoji": "⛩️", "name": "shinto shrine", "shortname": ":shinto_shrine:", "unicode": "26E9 FE0F", "html": "⛩️", "category": "Travel & Places (place-religious)", "order": ""}, + {"emoji": "🏙️", "name": "cityscape", "shortname": ":cityscape:", "unicode": "1F3D9 FE0F", "html": "🏙️", "category": "Travel & Places (place-other)", "order": ""}, + {"emoji": "♨", "name": "hot springs", "shortname": ":hot_springs:", "unicode": "2668", "html": "♨", "category": "Travel & Places (place-other)", "order": ""}, + {"emoji": "🏎️", "name": "racing car", "shortname": ":racing_car:", "unicode": "1F3CE FE0F", "html": "🏎️", "category": "Travel & Places (transport-ground)", "order": ""}, + {"emoji": "🏍️", "name": "motorcycle", "shortname": ":motorcycle:", "unicode": "1F3CD FE0F", "html": "🏍️", "category": "Travel & Places (transport-ground)", "order": ""}, + {"emoji": "🛵", "name": "motor scooter", "shortname": ":motor_scooter:", "unicode": "1F6F5", "html": "🛵", "category": "Travel & Places (transport-ground)", "order": ""}, + {"emoji": "🦽", "name": "manual wheelchair", "shortname": ":manual_wheelchair:", "unicode": "1F9BD", "html": "🦽", "category": "Travel & Places (transport-ground)", "order": ""}, + {"emoji": "🦼", "name": "motorized wheelchair", "shortname": ":motorized_wheelchair:", "unicode": "1F9BC", "html": "🦼", "category": "Travel & Places (transport-ground)", "order": ""}, + {"emoji": "🛺", "name": "auto rickshaw", "shortname": ":auto_rickshaw:", "unicode": "1F6FA", "html": "🛺", "category": "Travel & Places (transport-ground)", "order": ""}, + {"emoji": "🛴", "name": "kick scooter", "shortname": ":kick_scooter:", "unicode": "1F6F4", "html": "🛴", "category": "Travel & Places (transport-ground)", "order": ""}, + {"emoji": "🛹", "name": "skateboard", "shortname": ":skateboard:", "unicode": "1F6F9", "html": "🛹", "category": "Travel & Places (transport-ground)", "order": ""}, + {"emoji": "🛣️", "name": "motorway", "shortname": ":motorway:", "unicode": "1F6E3 FE0F", "html": "🛣️", "category": "Travel & Places (transport-ground)", "order": ""}, + {"emoji": "🛤️", "name": "railway track", "shortname": ":railway_track:", "unicode": "1F6E4 FE0F", "html": "🛤️", "category": "Travel & Places (transport-ground)", "order": ""}, + {"emoji": "🛢️", "name": "oil drum", "shortname": ":oil_drum:", "unicode": "1F6E2 FE0F", "html": "🛢️", "category": "Travel & Places (transport-ground)", "order": ""}, + {"emoji": "🛶", "name": "canoe", "shortname": ":canoe:", "unicode": "1F6F6", "html": "🛶", "category": "Travel & Places (transport-water)", "order": ""}, + {"emoji": "🛳️", "name": "passenger ship", "shortname": ":passenger_ship:", "unicode": "1F6F3 FE0F", "html": "🛳️", "category": "Travel & Places (transport-water)", "order": ""}, + {"emoji": "⛴️", "name": "ferry", "shortname": ":ferry:", "unicode": "26F4 FE0F", "html": "⛴️", "category": "Travel & Places (transport-water)", "order": ""}, + {"emoji": "🛥️", "name": "motor boat", "shortname": ":motor_boat:", "unicode": "1F6E5 FE0F", "html": "🛥️", "category": "Travel & Places (transport-water)", "order": ""}, + {"emoji": "✈", "name": "airplane", "shortname": ":airplane:", "unicode": "2708", "html": "✈", "category": "Travel & Places (transport-air)", "order": ""}, + {"emoji": "🛩️", "name": "small airplane", "shortname": ":small_airplane:", "unicode": "1F6E9 FE0F", "html": "🛩️", "category": "Travel & Places (transport-air)", "order": ""}, + {"emoji": "🪂", "name": "parachute", "shortname": ":parachute:", "unicode": "1FA82", "html": "🪂", "category": "Travel & Places (transport-air)", "order": ""}, + {"emoji": "🛰️", "name": "satellite", "shortname": ":satellite:", "unicode": "1F6F0 FE0F", "html": "🛰️", "category": "Travel & Places (transport-air)", "order": ""}, + {"emoji": "🛸", "name": "flying saucer", "shortname": ":flying_saucer:", "unicode": "1F6F8", "html": "🛸", "category": "Travel & Places (transport-air)", "order": ""}, + {"emoji": "🛎️", "name": "bellhop bell", "shortname": ":bellhop_bell:", "unicode": "1F6CE FE0F", "html": "🛎️", "category": "Travel & Places (hotel)", "order": ""}, + {"emoji": "🧳", "name": "luggage", "shortname": ":luggage:", "unicode": "1F9F3", "html": "🧳", "category": "Travel & Places (hotel)", "order": ""}, + {"emoji": "⏱️", "name": "stopwatch", "shortname": ":stopwatch:", "unicode": "23F1 FE0F", "html": "⏱️", "category": "Travel & Places (time)", "order": ""}, + {"emoji": "⏲️", "name": "timer clock", "shortname": ":timer_clock:", "unicode": "23F2 FE0F", "html": "⏲️", "category": "Travel & Places (time)", "order": ""}, + {"emoji": "🕰️", "name": "mantelpiece clock", "shortname": ":mantelpiece_clock:", "unicode": "1F570 FE0F", "html": "🕰️", "category": "Travel & Places (time)", "order": ""}, + {"emoji": "🌡️", "name": "thermometer", "shortname": ":thermometer:", "unicode": "1F321 FE0F", "html": "🌡️", "category": "Travel & Places (sky & weather)", "order": ""}, + {"emoji": "☀", "name": "sun", "shortname": ":sun:", "unicode": "2600", "html": "☀", "category": "Travel & Places (sky & weather)", "order": ""}, + {"emoji": "🪐", "name": "ringed planet", "shortname": ":ringed_planet:", "unicode": "1FA90", "html": "🪐", "category": "Travel & Places (sky & weather)", "order": ""}, + {"emoji": "☁️", "name": "cloud", "shortname": ":cloud:", "unicode": "2601 FE0F", "html": "☁️", "category": "Travel & Places (sky & weather)", "order": ""}, + {"emoji": "⛈️", "name": "cloud with lightning and rain", "shortname": ":cloud_with_lightning_and_rain:", "unicode": "26C8 FE0F", "html": "⛈️", "category": "Travel & Places (sky & weather)", "order": ""}, + {"emoji": "🌤️", "name": "sun behind small cloud", "shortname": ":sun_behind_small_cloud:", "unicode": "1F324 FE0F", "html": "🌤️", "category": "Travel & Places (sky & weather)", "order": ""}, + {"emoji": "🌥️", "name": "sun behind large cloud", "shortname": ":sun_behind_large_cloud:", "unicode": "1F325 FE0F", "html": "🌥️", "category": "Travel & Places (sky & weather)", "order": ""}, + {"emoji": "🌦️", "name": "sun behind rain cloud", "shortname": ":sun_behind_rain_cloud:", "unicode": "1F326 FE0F", "html": "🌦️", "category": "Travel & Places (sky & weather)", "order": ""}, + {"emoji": "🌧️", "name": "cloud with rain", "shortname": ":cloud_with_rain:", "unicode": "1F327 FE0F", "html": "🌧️", "category": "Travel & Places (sky & weather)", "order": ""}, + {"emoji": "🌨️", "name": "cloud with snow", "shortname": ":cloud_with_snow:", "unicode": "1F328 FE0F", "html": "🌨️", "category": "Travel & Places (sky & weather)", "order": ""}, + {"emoji": "🌩️", "name": "cloud with lightning", "shortname": ":cloud_with_lightning:", "unicode": "1F329 FE0F", "html": "🌩️", "category": "Travel & Places (sky & weather)", "order": ""}, + {"emoji": "🌪️", "name": "tornado", "shortname": ":tornado:", "unicode": "1F32A FE0F", "html": "🌪️", "category": "Travel & Places (sky & weather)", "order": ""}, + {"emoji": "🌫️", "name": "fog", "shortname": ":fog:", "unicode": "1F32B FE0F", "html": "🌫️", "category": "Travel & Places (sky & weather)", "order": ""}, + {"emoji": "🌬️", "name": "wind face", "shortname": ":wind_face:", "unicode": "1F32C FE0F", "html": "🌬️", "category": "Travel & Places (sky & weather)", "order": ""}, + {"emoji": "☂", "name": "umbrella", "shortname": ":umbrella:", "unicode": "2602", "html": "☂", "category": "Travel & Places (sky & weather)", "order": ""}, + {"emoji": "⛱️", "name": "umbrella on ground", "shortname": ":umbrella_on_ground:", "unicode": "26F1 FE0F", "html": "⛱️", "category": "Travel & Places (sky & weather)", "order": ""}, + {"emoji": "❄", "name": "snowflake", "shortname": ":snowflake:", "unicode": "2744", "html": "❄", "category": "Travel & Places (sky & weather)", "order": ""}, + {"emoji": "☃", "name": "snowman", "shortname": ":snowman:", "unicode": "2603", "html": "☃", "category": "Travel & Places (sky & weather)", "order": ""}, + {"emoji": "☄️", "name": "comet", "shortname": ":comet:", "unicode": "2604 FE0F", "html": "☄️", "category": "Travel & Places (sky & weather)", "order": ""}, + {"emoji": "🧨", "name": "firecracker", "shortname": ":firecracker:", "unicode": "1F9E8", "html": "🧨", "category": "Activities (event)", "order": ""}, + {"emoji": "🧧", "name": "red envelope", "shortname": ":red_envelope:", "unicode": "1F9E7", "html": "🧧", "category": "Activities (event)", "order": ""}, + {"emoji": "🎗️", "name": "reminder ribbon", "shortname": ":reminder_ribbon:", "unicode": "1F397 FE0F", "html": "🎗️", "category": "Activities (event)", "order": ""}, + {"emoji": "🎟️", "name": "admission tickets", "shortname": ":admission_tickets:", "unicode": "1F39F FE0F", "html": "🎟️", "category": "Activities (event)", "order": ""}, + {"emoji": "🎖️", "name": "military medal", "shortname": ":military_medal:", "unicode": "1F396 FE0F", "html": "🎖️", "category": "Activities (award-medal)", "order": ""}, + {"emoji": "🥇", "name": "1st place medal", "shortname": ":1st_place_medal:", "unicode": "1F947", "html": "🥇", "category": "Activities (award-medal)", "order": ""}, + {"emoji": "🥈", "name": "2nd place medal", "shortname": ":2nd_place_medal:", "unicode": "1F948", "html": "🥈", "category": "Activities (award-medal)", "order": ""}, + {"emoji": "🥉", "name": "3rd place medal", "shortname": ":3rd_place_medal:", "unicode": "1F949", "html": "🥉", "category": "Activities (award-medal)", "order": ""}, + {"emoji": "⚾", "name": "baseball", "shortname": ":baseball:", "unicode": "26BE", "html": "⚾", "category": "Activities (sport)", "order": ""}, + {"emoji": "🥎", "name": "softball", "shortname": ":softball:", "unicode": "1F94E", "html": "🥎", "category": "Activities (sport)", "order": ""}, + {"emoji": "🥏", "name": "flying disc", "shortname": ":flying_disc:", "unicode": "1F94F", "html": "🥏", "category": "Activities (sport)", "order": ""}, + {"emoji": "🥍", "name": "lacrosse", "shortname": ":lacrosse:", "unicode": "1F94D", "html": "🥍", "category": "Activities (sport)", "order": ""}, + {"emoji": "🥊", "name": "boxing glove", "shortname": ":boxing_glove:", "unicode": "1F94A", "html": "🥊", "category": "Activities (sport)", "order": ""}, + {"emoji": "🥋", "name": "martial arts uniform", "shortname": ":martial_arts_uniform:", "unicode": "1F94B", "html": "🥋", "category": "Activities (sport)", "order": ""}, + {"emoji": "🥅", "name": "goal net", "shortname": ":goal_net:", "unicode": "1F945", "html": "🥅", "category": "Activities (sport)", "order": ""}, + {"emoji": "⛸️", "name": "ice skate", "shortname": ":ice_skate:", "unicode": "26F8 FE0F", "html": "⛸️", "category": "Activities (sport)", "order": ""}, + {"emoji": "🤿", "name": "diving mask", "shortname": ":diving_mask:", "unicode": "1F93F", "html": "🤿", "category": "Activities (sport)", "order": ""}, + {"emoji": "🛷", "name": "sled", "shortname": ":sled:", "unicode": "1F6F7", "html": "🛷", "category": "Activities (sport)", "order": ""}, + {"emoji": "🥌", "name": "curling stone", "shortname": ":curling_stone:", "unicode": "1F94C", "html": "🥌", "category": "Activities (sport)", "order": ""}, + {"emoji": "🪀", "name": "yo-yo", "shortname": ":yoyo:", "unicode": "1FA80", "html": "🪀", "category": "Activities (game)", "order": ""}, + {"emoji": "🪁", "name": "kite", "shortname": ":kite:", "unicode": "1FA81", "html": "🪁", "category": "Activities (game)", "order": ""}, + {"emoji": "🧿", "name": "nazar amulet", "shortname": ":nazar_amulet:", "unicode": "1F9FF", "html": "🧿", "category": "Activities (game)", "order": ""}, + {"emoji": "🕹️", "name": "joystick", "shortname": ":joystick:", "unicode": "1F579 FE0F", "html": "🕹️", "category": "Activities (game)", "order": ""}, + {"emoji": "🧩", "name": "puzzle piece", "shortname": ":puzzle_piece:", "unicode": "1F9E9", "html": "🧩", "category": "Activities (game)", "order": ""}, + {"emoji": "🧸", "name": "teddy bear", "shortname": ":teddy_bear:", "unicode": "1F9F8", "html": "🧸", "category": "Activities (game)", "order": ""}, + {"emoji": "♟️", "name": "chess pawn", "shortname": ":chess_pawn:", "unicode": "265F FE0F", "html": "♟️", "category": "Activities (game)", "order": ""}, + {"emoji": "♟", "name": "chess pawn", "shortname": ":chess_pawn:", "unicode": "265F", "html": "♟", "category": "Activities (game)", "order": ""}, + {"emoji": "🖼️", "name": "framed picture", "shortname": ":framed_picture:", "unicode": "1F5BC FE0F", "html": "🖼️", "category": "Activities (arts & crafts)", "order": ""}, + {"emoji": "🧵", "name": "thread", "shortname": ":thread:", "unicode": "1F9F5", "html": "🧵", "category": "Activities (arts & crafts)", "order": ""}, + {"emoji": "🧶", "name": "yarn", "shortname": ":yarn:", "unicode": "1F9F6", "html": "🧶", "category": "Activities (arts & crafts)", "order": ""}, + {"emoji": "🕶️", "name": "sunglasses", "shortname": ":sunglasses:", "unicode": "1F576 FE0F", "html": "🕶️", "category": "Objects (clothing)", "order": ""}, + {"emoji": "🥽", "name": "goggles", "shortname": ":goggles:", "unicode": "1F97D", "html": "🥽", "category": "Objects (clothing)", "order": ""}, + {"emoji": "🥼", "name": "lab coat", "shortname": ":lab_coat:", "unicode": "1F97C", "html": "🥼", "category": "Objects (clothing)", "order": ""}, + {"emoji": "🦺", "name": "safety vest", "shortname": ":safety_vest:", "unicode": "1F9BA", "html": "🦺", "category": "Objects (clothing)", "order": ""}, + {"emoji": "🧣", "name": "scarf", "shortname": ":scarf:", "unicode": "1F9E3", "html": "🧣", "category": "Objects (clothing)", "order": ""}, + {"emoji": "🧤", "name": "gloves", "shortname": ":gloves:", "unicode": "1F9E4", "html": "🧤", "category": "Objects (clothing)", "order": ""}, + {"emoji": "🧥", "name": "coat", "shortname": ":coat:", "unicode": "1F9E5", "html": "🧥", "category": "Objects (clothing)", "order": ""}, + {"emoji": "🧦", "name": "socks", "shortname": ":socks:", "unicode": "1F9E6", "html": "🧦", "category": "Objects (clothing)", "order": ""}, + {"emoji": "🥻", "name": "sari", "shortname": ":sari:", "unicode": "1F97B", "html": "🥻", "category": "Objects (clothing)", "order": ""}, + {"emoji": "🩱", "name": "one-piece swimsuit", "shortname": ":onepiece_swimsuit:", "unicode": "1FA71", "html": "🩱", "category": "Objects (clothing)", "order": ""}, + {"emoji": "🩲", "name": "briefs", "shortname": ":briefs:", "unicode": "1FA72", "html": "🩲", "category": "Objects (clothing)", "order": ""}, + {"emoji": "🩳", "name": "shorts", "shortname": ":shorts:", "unicode": "1FA73", "html": "🩳", "category": "Objects (clothing)", "order": ""}, + {"emoji": "🛍️", "name": "shopping bags", "shortname": ":shopping_bags:", "unicode": "1F6CD FE0F", "html": "🛍️", "category": "Objects (clothing)", "order": ""}, + {"emoji": "🥾", "name": "hiking boot", "shortname": ":hiking_boot:", "unicode": "1F97E", "html": "🥾", "category": "Objects (clothing)", "order": ""}, + {"emoji": "🥿", "name": "flat shoe", "shortname": ":flat_shoe:", "unicode": "1F97F", "html": "🥿", "category": "Objects (clothing)", "order": ""}, + {"emoji": "🩰", "name": "ballet shoes", "shortname": ":ballet_shoes:", "unicode": "1FA70", "html": "🩰", "category": "Objects (clothing)", "order": ""}, + {"emoji": "🧢", "name": "billed cap", "shortname": ":billed_cap:", "unicode": "1F9E2", "html": "🧢", "category": "Objects (clothing)", "order": ""}, + {"emoji": "⛑️", "name": "rescue worker’s helmet", "shortname": ":rescue_worker’s_helmet:", "unicode": "26D1 FE0F", "html": "⛑️", "category": "Objects (clothing)", "order": ""}, + {"emoji": "🎙️", "name": "studio microphone", "shortname": ":studio_microphone:", "unicode": "1F399 FE0F", "html": "🎙️", "category": "Objects (music)", "order": ""}, + {"emoji": "🎚️", "name": "level slider", "shortname": ":level_slider:", "unicode": "1F39A FE0F", "html": "🎚️", "category": "Objects (music)", "order": ""}, + {"emoji": "🎛️", "name": "control knobs", "shortname": ":control_knobs:", "unicode": "1F39B FE0F", "html": "🎛️", "category": "Objects (music)", "order": ""}, + {"emoji": "🪕", "name": "banjo", "shortname": ":banjo:", "unicode": "1FA95", "html": "🪕", "category": "Objects (musical-instrument)", "order": ""}, + {"emoji": "☎", "name": "telephone", "shortname": ":telephone:", "unicode": "260E", "html": "☎", "category": "Objects (phone)", "order": ""}, + {"emoji": "🖥️", "name": "desktop computer", "shortname": ":desktop_computer:", "unicode": "1F5A5 FE0F", "html": "🖥️", "category": "Objects (computer)", "order": ""}, + {"emoji": "🖨️", "name": "printer", "shortname": ":printer:", "unicode": "1F5A8 FE0F", "html": "🖨️", "category": "Objects (computer)", "order": ""}, + {"emoji": "⌨️", "name": "keyboard", "shortname": ":keyboard:", "unicode": "2328 FE0F", "html": "⌨️", "category": "Objects (computer)", "order": ""}, + {"emoji": "🖱️", "name": "computer mouse", "shortname": ":computer_mouse:", "unicode": "1F5B1 FE0F", "html": "🖱️", "category": "Objects (computer)", "order": ""}, + {"emoji": "🖲️", "name": "trackball", "shortname": ":trackball:", "unicode": "1F5B2 FE0F", "html": "🖲️", "category": "Objects (computer)", "order": ""}, + {"emoji": "🧮", "name": "abacus", "shortname": ":abacus:", "unicode": "1F9EE", "html": "🧮", "category": "Objects (computer)", "order": ""}, + {"emoji": "🎞️", "name": "film frames", "shortname": ":film_frames:", "unicode": "1F39E FE0F", "html": "🎞️", "category": "Objects (light & video)", "order": ""}, + {"emoji": "📽️", "name": "film projector", "shortname": ":film_projector:", "unicode": "1F4FD FE0F", "html": "📽️", "category": "Objects (light & video)", "order": ""}, + {"emoji": "🕯️", "name": "candle", "shortname": ":candle:", "unicode": "1F56F FE0F", "html": "🕯️", "category": "Objects (light & video)", "order": ""}, + {"emoji": "🪔", "name": "diya lamp", "shortname": ":diya_lamp:", "unicode": "1FA94", "html": "🪔", "category": "Objects (light & video)", "order": ""}, + {"emoji": "🗞️", "name": "rolled-up newspaper", "shortname": ":rolledup_newspaper:", "unicode": "1F5DE FE0F", "html": "🗞️", "category": "Objects (book-paper)", "order": ""}, + {"emoji": "🏷️", "name": "label", "shortname": ":label:", "unicode": "1F3F7 FE0F", "html": "🏷️", "category": "Objects (book-paper)", "order": ""}, + {"emoji": "🧾", "name": "receipt", "shortname": ":receipt:", "unicode": "1F9FE", "html": "🧾", "category": "Objects (money)", "order": ""}, + {"emoji": "✉", "name": "envelope", "shortname": ":envelope:", "unicode": "2709", "html": "✉", "category": "Objects (mail)", "order": ""}, + {"emoji": "🗳️", "name": "ballot box with ballot", "shortname": ":ballot_box_with_ballot:", "unicode": "1F5F3 FE0F", "html": "🗳️", "category": "Objects (mail)", "order": ""}, + {"emoji": "✏️", "name": "pencil", "shortname": ":pencil:", "unicode": "270F FE0F", "html": "✏️", "category": "Objects (writing)", "order": ""}, + {"emoji": "✒️", "name": "black nib", "shortname": ":black_nib:", "unicode": "2712 FE0F", "html": "✒️", "category": "Objects (writing)", "order": ""}, + {"emoji": "🖋️", "name": "fountain pen", "shortname": ":fountain_pen:", "unicode": "1F58B FE0F", "html": "🖋️", "category": "Objects (writing)", "order": ""}, + {"emoji": "🖊️", "name": "pen", "shortname": ":pen:", "unicode": "1F58A FE0F", "html": "🖊️", "category": "Objects (writing)", "order": ""}, + {"emoji": "🖌️", "name": "paintbrush", "shortname": ":paintbrush:", "unicode": "1F58C FE0F", "html": "🖌️", "category": "Objects (writing)", "order": ""}, + {"emoji": "🖍️", "name": "crayon", "shortname": ":crayon:", "unicode": "1F58D FE0F", "html": "🖍️", "category": "Objects (writing)", "order": ""}, + {"emoji": "🗂️", "name": "card index dividers", "shortname": ":card_index_dividers:", "unicode": "1F5C2 FE0F", "html": "🗂️", "category": "Objects (office)", "order": ""}, + {"emoji": "🗒️", "name": "spiral notepad", "shortname": ":spiral_notepad:", "unicode": "1F5D2 FE0F", "html": "🗒️", "category": "Objects (office)", "order": ""}, + {"emoji": "🗓️", "name": "spiral calendar", "shortname": ":spiral_calendar:", "unicode": "1F5D3 FE0F", "html": "🗓️", "category": "Objects (office)", "order": ""}, + {"emoji": "🖇️", "name": "linked paperclips", "shortname": ":linked_paperclips:", "unicode": "1F587 FE0F", "html": "🖇️", "category": "Objects (office)", "order": ""}, + {"emoji": "✂", "name": "scissors", "shortname": ":scissors:", "unicode": "2702", "html": "✂", "category": "Objects (office)", "order": ""}, + {"emoji": "🗃️", "name": "card file box", "shortname": ":card_file_box:", "unicode": "1F5C3 FE0F", "html": "🗃️", "category": "Objects (office)", "order": ""}, + {"emoji": "🗄️", "name": "file cabinet", "shortname": ":file_cabinet:", "unicode": "1F5C4 FE0F", "html": "🗄️", "category": "Objects (office)", "order": ""}, + {"emoji": "🗑️", "name": "wastebasket", "shortname": ":wastebasket:", "unicode": "1F5D1 FE0F", "html": "🗑️", "category": "Objects (office)", "order": ""}, + {"emoji": "🗝️", "name": "old key", "shortname": ":old_key:", "unicode": "1F5DD FE0F", "html": "🗝️", "category": "Objects (lock)", "order": ""}, + {"emoji": "🪓", "name": "axe", "shortname": ":axe:", "unicode": "1FA93", "html": "🪓", "category": "Objects (tool)", "order": ""}, + {"emoji": "⛏️", "name": "pick", "shortname": ":pick:", "unicode": "26CF FE0F", "html": "⛏️", "category": "Objects (tool)", "order": ""}, + {"emoji": "⚒️", "name": "hammer and pick", "shortname": ":hammer_and_pick:", "unicode": "2692 FE0F", "html": "⚒️", "category": "Objects (tool)", "order": ""}, + {"emoji": "🛠️", "name": "hammer and wrench", "shortname": ":hammer_and_wrench:", "unicode": "1F6E0 FE0F", "html": "🛠️", "category": "Objects (tool)", "order": ""}, + {"emoji": "🗡️", "name": "dagger", "shortname": ":dagger:", "unicode": "1F5E1 FE0F", "html": "🗡️", "category": "Objects (tool)", "order": ""}, + {"emoji": "⚔️", "name": "crossed swords", "shortname": ":crossed_swords:", "unicode": "2694 FE0F", "html": "⚔️", "category": "Objects (tool)", "order": ""}, + {"emoji": "🛡️", "name": "shield", "shortname": ":shield:", "unicode": "1F6E1 FE0F", "html": "🛡️", "category": "Objects (tool)", "order": ""}, + {"emoji": "⚙️", "name": "gear", "shortname": ":gear:", "unicode": "2699 FE0F", "html": "⚙️", "category": "Objects (tool)", "order": ""}, + {"emoji": "🗜️", "name": "clamp", "shortname": ":clamp:", "unicode": "1F5DC FE0F", "html": "🗜️", "category": "Objects (tool)", "order": ""}, + {"emoji": "⚖️", "name": "balance scale", "shortname": ":balance_scale:", "unicode": "2696 FE0F", "html": "⚖️", "category": "Objects (tool)", "order": ""}, + {"emoji": "🦯", "name": "probing cane", "shortname": ":probing_cane:", "unicode": "1F9AF", "html": "🦯", "category": "Objects (tool)", "order": ""}, + {"emoji": "⛓️", "name": "chains", "shortname": ":chains:", "unicode": "26D3 FE0F", "html": "⛓️", "category": "Objects (tool)", "order": ""}, + {"emoji": "🧰", "name": "toolbox", "shortname": ":toolbox:", "unicode": "1F9F0", "html": "🧰", "category": "Objects (tool)", "order": ""}, + {"emoji": "🧲", "name": "magnet", "shortname": ":magnet:", "unicode": "1F9F2", "html": "🧲", "category": "Objects (tool)", "order": ""}, + {"emoji": "⚗️", "name": "alembic", "shortname": ":alembic:", "unicode": "2697 FE0F", "html": "⚗️", "category": "Objects (science)", "order": ""}, + {"emoji": "🧪", "name": "test tube", "shortname": ":test_tube:", "unicode": "1F9EA", "html": "🧪", "category": "Objects (science)", "order": ""}, + {"emoji": "🧫", "name": "petri dish", "shortname": ":petri_dish:", "unicode": "1F9EB", "html": "🧫", "category": "Objects (science)", "order": ""}, + {"emoji": "🧬", "name": "dna", "shortname": ":dna:", "unicode": "1F9EC", "html": "🧬", "category": "Objects (science)", "order": ""}, + {"emoji": "🩸", "name": "drop of blood", "shortname": ":drop_of_blood:", "unicode": "1FA78", "html": "🩸", "category": "Objects (medical)", "order": ""}, + {"emoji": "🩹", "name": "adhesive bandage", "shortname": ":adhesive_bandage:", "unicode": "1FA79", "html": "🩹", "category": "Objects (medical)", "order": ""}, + {"emoji": "🩺", "name": "stethoscope", "shortname": ":stethoscope:", "unicode": "1FA7A", "html": "🩺", "category": "Objects (medical)", "order": ""}, + {"emoji": "🛏️", "name": "bed", "shortname": ":bed:", "unicode": "1F6CF FE0F", "html": "🛏️", "category": "Objects (household)", "order": ""}, + {"emoji": "🛋️", "name": "couch and lamp", "shortname": ":couch_and_lamp:", "unicode": "1F6CB FE0F", "html": "🛋️", "category": "Objects (household)", "order": ""}, + {"emoji": "🪑", "name": "chair", "shortname": ":chair:", "unicode": "1FA91", "html": "🪑", "category": "Objects (household)", "order": ""}, + {"emoji": "🪒", "name": "razor", "shortname": ":razor:", "unicode": "1FA92", "html": "🪒", "category": "Objects (household)", "order": ""}, + {"emoji": "🧴", "name": "lotion bottle", "shortname": ":lotion_bottle:", "unicode": "1F9F4", "html": "🧴", "category": "Objects (household)", "order": ""}, + {"emoji": "🧷", "name": "safety pin", "shortname": ":safety_pin:", "unicode": "1F9F7", "html": "🧷", "category": "Objects (household)", "order": ""}, + {"emoji": "🧹", "name": "broom", "shortname": ":broom:", "unicode": "1F9F9", "html": "🧹", "category": "Objects (household)", "order": ""}, + {"emoji": "🧺", "name": "basket", "shortname": ":basket:", "unicode": "1F9FA", "html": "🧺", "category": "Objects (household)", "order": ""}, + {"emoji": "🧻", "name": "roll of paper", "shortname": ":roll_of_paper:", "unicode": "1F9FB", "html": "🧻", "category": "Objects (household)", "order": ""}, + {"emoji": "🧼", "name": "soap", "shortname": ":soap:", "unicode": "1F9FC", "html": "🧼", "category": "Objects (household)", "order": ""}, + {"emoji": "🧽", "name": "sponge", "shortname": ":sponge:", "unicode": "1F9FD", "html": "🧽", "category": "Objects (household)", "order": ""}, + {"emoji": "🧯", "name": "fire extinguisher", "shortname": ":fire_extinguisher:", "unicode": "1F9EF", "html": "🧯", "category": "Objects (household)", "order": ""}, + {"emoji": "🛒", "name": "shopping cart", "shortname": ":shopping_cart:", "unicode": "1F6D2", "html": "🛒", "category": "Objects (household)", "order": ""}, + {"emoji": "⚰️", "name": "coffin", "shortname": ":coffin:", "unicode": "26B0 FE0F", "html": "⚰️", "category": "Objects (other-object)", "order": ""}, + {"emoji": "⚱️", "name": "funeral urn", "shortname": ":funeral_urn:", "unicode": "26B1 FE0F", "html": "⚱️", "category": "Objects (other-object)", "order": ""}, + {"emoji": "⚠", "name": "warning", "shortname": ":warning:", "unicode": "26A0", "html": "⚠", "category": "Symbols (warning)", "order": ""}, + {"emoji": "☢️", "name": "radioactive", "shortname": ":radioactive:", "unicode": "2622 FE0F", "html": "☢️", "category": "Symbols (warning)", "order": ""}, + {"emoji": "☣️", "name": "biohazard", "shortname": ":biohazard:", "unicode": "2623 FE0F", "html": "☣️", "category": "Symbols (warning)", "order": ""}, + {"emoji": "⬆", "name": "up arrow", "shortname": ":up_arrow:", "unicode": "2B06", "html": "⬆", "category": "Symbols (arrow)", "order": ""}, + {"emoji": "↗", "name": "up-right arrow", "shortname": ":upright_arrow:", "unicode": "2197", "html": "↗", "category": "Symbols (arrow)", "order": ""}, + {"emoji": "➡️", "name": "right arrow", "shortname": ":right_arrow:", "unicode": "27A1 FE0F", "html": "➡️", "category": "Symbols (arrow)", "order": ""}, + {"emoji": "↘", "name": "down-right arrow", "shortname": ":downright_arrow:", "unicode": "2198", "html": "↘", "category": "Symbols (arrow)", "order": ""}, + {"emoji": "⬇", "name": "down arrow", "shortname": ":down_arrow:", "unicode": "2B07", "html": "⬇", "category": "Symbols (arrow)", "order": ""}, + {"emoji": "↙", "name": "down-left arrow", "shortname": ":downleft_arrow:", "unicode": "2199", "html": "↙", "category": "Symbols (arrow)", "order": ""}, + {"emoji": "⬅", "name": "left arrow", "shortname": ":left_arrow:", "unicode": "2B05", "html": "⬅", "category": "Symbols (arrow)", "order": ""}, + {"emoji": "↖️", "name": "up-left arrow", "shortname": ":upleft_arrow:", "unicode": "2196 FE0F", "html": "↖️", "category": "Symbols (arrow)", "order": ""}, + {"emoji": "↕️", "name": "up-down arrow", "shortname": ":updown_arrow:", "unicode": "2195 FE0F", "html": "↕️", "category": "Symbols (arrow)", "order": ""}, + {"emoji": "↩", "name": "right arrow curving left", "shortname": ":right_arrow_curving_left:", "unicode": "21A9", "html": "↩", "category": "Symbols (arrow)", "order": ""}, + {"emoji": "↪", "name": "left arrow curving right", "shortname": ":left_arrow_curving_right:", "unicode": "21AA", "html": "↪", "category": "Symbols (arrow)", "order": ""}, + {"emoji": "⤴", "name": "right arrow curving up", "shortname": ":right_arrow_curving_up:", "unicode": "2934", "html": "⤴", "category": "Symbols (arrow)", "order": ""}, + {"emoji": "⤵", "name": "right arrow curving down", "shortname": ":right_arrow_curving_down:", "unicode": "2935", "html": "⤵", "category": "Symbols (arrow)", "order": ""}, + {"emoji": "⚛️", "name": "atom symbol", "shortname": ":atom_symbol:", "unicode": "269B FE0F", "html": "⚛️", "category": "Symbols (religion)", "order": ""}, + {"emoji": "🕉️", "name": "om", "shortname": ":om:", "unicode": "1F549 FE0F", "html": "🕉️", "category": "Symbols (religion)", "order": ""}, + {"emoji": "✡", "name": "star of David", "shortname": ":star_of_David:", "unicode": "2721", "html": "✡", "category": "Symbols (religion)", "order": ""}, + {"emoji": "☸️", "name": "wheel of dharma", "shortname": ":wheel_of_dharma:", "unicode": "2638 FE0F", "html": "☸️", "category": "Symbols (religion)", "order": ""}, + {"emoji": "☯️", "name": "yin yang", "shortname": ":yin_yang:", "unicode": "262F FE0F", "html": "☯️", "category": "Symbols (religion)", "order": ""}, + {"emoji": "✝", "name": "latin cross", "shortname": ":latin_cross:", "unicode": "271D", "html": "✝", "category": "Symbols (religion)", "order": ""}, + {"emoji": "☦️", "name": "orthodox cross", "shortname": ":orthodox_cross:", "unicode": "2626 FE0F", "html": "☦️", "category": "Symbols (religion)", "order": ""}, + {"emoji": "☪️", "name": "star and crescent", "shortname": ":star_and_crescent:", "unicode": "262A FE0F", "html": "☪️", "category": "Symbols (religion)", "order": ""}, + {"emoji": "☮️", "name": "peace symbol", "shortname": ":peace_symbol:", "unicode": "262E FE0F", "html": "☮️", "category": "Symbols (religion)", "order": ""}, + {"emoji": "▶", "name": "play button", "shortname": ":play_button:", "unicode": "25B6", "html": "▶", "category": "Symbols (av-symbol)", "order": ""}, + {"emoji": "⏭️", "name": "next track button", "shortname": ":next_track_button:", "unicode": "23ED FE0F", "html": "⏭️", "category": "Symbols (av-symbol)", "order": ""}, + {"emoji": "⏯️", "name": "play or pause button", "shortname": ":play_or_pause_button:", "unicode": "23EF FE0F", "html": "⏯️", "category": "Symbols (av-symbol)", "order": ""}, + {"emoji": "◀", "name": "reverse button", "shortname": ":reverse_button:", "unicode": "25C0", "html": "◀", "category": "Symbols (av-symbol)", "order": ""}, + {"emoji": "⏮️", "name": "last track button", "shortname": ":last_track_button:", "unicode": "23EE FE0F", "html": "⏮️", "category": "Symbols (av-symbol)", "order": ""}, + {"emoji": "⏸️", "name": "pause button", "shortname": ":pause_button:", "unicode": "23F8 FE0F", "html": "⏸️", "category": "Symbols (av-symbol)", "order": ""}, + {"emoji": "⏹️", "name": "stop button", "shortname": ":stop_button:", "unicode": "23F9 FE0F", "html": "⏹️", "category": "Symbols (av-symbol)", "order": ""}, + {"emoji": "⏺️", "name": "record button", "shortname": ":record_button:", "unicode": "23FA FE0F", "html": "⏺️", "category": "Symbols (av-symbol)", "order": ""}, + {"emoji": "⏏️", "name": "eject button", "shortname": ":eject_button:", "unicode": "23CF FE0F", "html": "⏏️", "category": "Symbols (av-symbol)", "order": ""}, + {"emoji": "⏏", "name": "eject button", "shortname": ":eject_button:", "unicode": "23CF", "html": "⏏", "category": "Symbols (av-symbol)", "order": ""}, + {"emoji": "♀️", "name": "female sign", "shortname": ":female_sign:", "unicode": "2640 FE0F", "html": "♀️", "category": "Symbols (gender)", "order": ""}, + {"emoji": "♀", "name": "female sign", "shortname": ":female_sign:", "unicode": "2640", "html": "♀", "category": "Symbols (gender)", "order": ""}, + {"emoji": "♂️", "name": "male sign", "shortname": ":male_sign:", "unicode": "2642 FE0F", "html": "♂️", "category": "Symbols (gender)", "order": ""}, + {"emoji": "♂", "name": "male sign", "shortname": ":male_sign:", "unicode": "2642", "html": "♂", "category": "Symbols (gender)", "order": ""}, + {"emoji": "⚕️", "name": "medical symbol", "shortname": ":medical_symbol:", "unicode": "2695 FE0F", "html": "⚕️", "category": "Symbols (other-symbol)", "order": ""}, + {"emoji": "⚕", "name": "medical symbol", "shortname": ":medical_symbol:", "unicode": "2695", "html": "⚕", "category": "Symbols (other-symbol)", "order": ""}, + {"emoji": "♾️", "name": "infinity", "shortname": ":infinity:", "unicode": "267E FE0F", "html": "♾️", "category": "Symbols (other-symbol)", "order": ""}, + {"emoji": "♾", "name": "infinity", "shortname": ":infinity:", "unicode": "267E", "html": "♾", "category": "Symbols (other-symbol)", "order": ""}, + {"emoji": "♻️", "name": "recycling symbol", "shortname": ":recycling_symbol:", "unicode": "267B FE0F", "html": "♻️", "category": "Symbols (other-symbol)", "order": ""}, + {"emoji": "⚜️", "name": "fleur-de-lis", "shortname": ":fleurde-lis:", "unicode": "269C FE0F", "html": "⚜️", "category": "Symbols (other-symbol)", "order": ""}, + {"emoji": "☑", "name": "check box with check", "shortname": ":check_box_with_check:", "unicode": "2611", "html": "☑", "category": "Symbols (other-symbol)", "order": ""}, + {"emoji": "✔", "name": "check mark", "shortname": ":check_mark:", "unicode": "2714", "html": "✔", "category": "Symbols (other-symbol)", "order": ""}, + {"emoji": "✖", "name": "multiplication sign", "shortname": ":multiplication_sign:", "unicode": "2716", "html": "✖", "category": "Symbols (other-symbol)", "order": ""}, + {"emoji": "〽", "name": "part alternation mark", "shortname": ":part_alternation_mark:", "unicode": "303D", "html": "〽", "category": "Symbols (other-symbol)", "order": ""}, + {"emoji": "✳", "name": "eight-spoked asterisk", "shortname": ":eightspoked_asterisk:", "unicode": "2733", "html": "✳", "category": "Symbols (other-symbol)", "order": ""}, + {"emoji": "✴", "name": "eight-pointed star", "shortname": ":eightpointed_star:", "unicode": "2734", "html": "✴", "category": "Symbols (other-symbol)", "order": ""}, + {"emoji": "❇", "name": "sparkle", "shortname": ":sparkle:", "unicode": "2747", "html": "❇", "category": "Symbols (other-symbol)", "order": ""}, + {"emoji": "‼", "name": "double exclamation mark", "shortname": ":double_exclamation_mark:", "unicode": "203C", "html": "‼", "category": "Symbols (other-symbol)", "order": ""}, + {"emoji": "⁉", "name": "exclamation question mark", "shortname": ":exclamation_question_mark:", "unicode": "2049", "html": "⁉", "category": "Symbols (other-symbol)", "order": ""}, + {"emoji": "〰️", "name": "wavy dash", "shortname": ":wavy_dash:", "unicode": "3030 FE0F", "html": "〰️", "category": "Symbols (other-symbol)", "order": ""}, + {"emoji": "#️⃣", "name": "keycap: #", "shortname": ":#:", "unicode": "0023 FE0F 20E3", "html": "#️⃣", "category": "Symbols (keycap)", "order": ""}, + {"emoji": "🅰", "name": "A button (blood type)", "shortname": ":A_button_blood_type:", "unicode": "1F170", "html": "🅰", "category": "Symbols (alphanum)", "order": ""}, + {"emoji": "🅱", "name": "B button (blood type)", "shortname": ":B_button_blood_type:", "unicode": "1F171", "html": "🅱", "category": "Symbols (alphanum)", "order": ""}, + {"emoji": "ℹ️", "name": "information", "shortname": ":information:", "unicode": "2139 FE0F", "html": "ℹ️", "category": "Symbols (alphanum)", "order": ""}, + {"emoji": "Ⓜ", "name": "circled M", "shortname": ":circled_M:", "unicode": "24C2", "html": "Ⓜ", "category": "Symbols (alphanum)", "order": ""}, + {"emoji": "🅾", "name": "O button (blood type)", "shortname": ":O_button_blood_type:", "unicode": "1F17E", "html": "🅾", "category": "Symbols (alphanum)", "order": ""}, + {"emoji": "🅿", "name": "P button", "shortname": ":P_button:", "unicode": "1F17F", "html": "🅿", "category": "Symbols (alphanum)", "order": ""}, + {"emoji": "🈂", "name": "Japanese service charge button", "shortname": ":Japanese_service_charge_button:", "unicode": "1F202", "html": "🈂", "category": "Symbols (alphanum)", "order": ""}, + {"emoji": "🈷️", "name": "Japanese monthly amount button", "shortname": ":Japanese_monthly_amount_button:", "unicode": "1F237 FE0F", "html": "🈷️", "category": "Symbols (alphanum)", "order": ""}, + {"emoji": "🈯", "name": "Japanese reserved button", "shortname": ":Japanese_reserved_button:", "unicode": "1F22F", "html": "🈯", "category": "Symbols (alphanum)", "order": ""}, + {"emoji": "🈚", "name": "Japanese free of charge button", "shortname": ":Japanese_free_of_charge_button:", "unicode": "1F21A", "html": "🈚", "category": "Symbols (alphanum)", "order": ""}, + {"emoji": "㊗️", "name": "Japanese congratulations button", "shortname": ":Japanese_congratulations_button:", "unicode": "3297 FE0F", "html": "㊗️", "category": "Symbols (alphanum)", "order": ""}, + {"emoji": "㊙️", "name": "Japanese secret button", "shortname": ":Japanese_secret_button:", "unicode": "3299 FE0F", "html": "㊙️", "category": "Symbols (alphanum)", "order": ""}, + {"emoji": "🟠", "name": "orange circle", "shortname": ":orange_circle:", "unicode": "1F7E0", "html": "🟠", "category": "Symbols (geometric)", "order": ""}, + {"emoji": "🟡", "name": "yellow circle", "shortname": ":yellow_circle:", "unicode": "1F7E1", "html": "🟡", "category": "Symbols (geometric)", "order": ""}, + {"emoji": "🟢", "name": "green circle", "shortname": ":green_circle:", "unicode": "1F7E2", "html": "🟢", "category": "Symbols (geometric)", "order": ""}, + {"emoji": "🟣", "name": "purple circle", "shortname": ":purple_circle:", "unicode": "1F7E3", "html": "🟣", "category": "Symbols (geometric)", "order": ""}, + {"emoji": "🟤", "name": "brown circle", "shortname": ":brown_circle:", "unicode": "1F7E4", "html": "🟤", "category": "Symbols (geometric)", "order": ""}, + {"emoji": "🟥", "name": "red square", "shortname": ":red_square:", "unicode": "1F7E5", "html": "🟥", "category": "Symbols (geometric)", "order": ""}, + {"emoji": "🟧", "name": "orange square", "shortname": ":orange_square:", "unicode": "1F7E7", "html": "🟧", "category": "Symbols (geometric)", "order": ""}, + {"emoji": "🟨", "name": "yellow square", "shortname": ":yellow_square:", "unicode": "1F7E8", "html": "🟨", "category": "Symbols (geometric)", "order": ""}, + {"emoji": "🟩", "name": "green square", "shortname": ":green_square:", "unicode": "1F7E9", "html": "🟩", "category": "Symbols (geometric)", "order": ""}, + {"emoji": "🟦", "name": "blue square", "shortname": ":blue_square:", "unicode": "1F7E6", "html": "🟦", "category": "Symbols (geometric)", "order": ""}, + {"emoji": "▪", "name": "black small square", "shortname": ":black_small_square:", "unicode": "25AA", "html": "▪", "category": "Symbols (geometric)", "order": ""}, + {"emoji": "▫️", "name": "white small square", "shortname": ":white_small_square:", "unicode": "25AB FE0F", "html": "▫️", "category": "Symbols (geometric)", "order": ""}, + {"emoji": "🏳️", "name": "white flag", "shortname": ":white_flag:", "unicode": "1F3F3 FE0F", "html": "🏳️", "category": "Flags (flag)", "order": ""}, + {"emoji": "🏳️‍🌈", "name": "rainbow flag", "shortname": ":rainbow_flag:", "unicode": "1F3F3 FE0F 200D 1F308", "html": "🏳️‍🌈", "category": "Flags (flag)", "order": ""}, + {"emoji": "🏴‍☠️", "name": "pirate flag", "shortname": ":pirate_flag:", "unicode": "1F3F4 200D 2620 FE0F", "html": "🏴‍☠️", "category": "Flags (flag)", "order": ""}, + {"emoji": "🏴‍☠", "name": "pirate flag", "shortname": ":pirate_flag:", "unicode": "1F3F4 200D 2620", "html": "🏴‍☠", "category": "Flags (flag)", "order": ""}, + {"emoji": "🇺🇳", "name": "flag: United Nations", "shortname": ":United_Nations:", "unicode": "1F1FA 1F1F3", "html": "🇺🇳", "category": "Flags (country-flag)", "order": ""}, + {"emoji": "🏴󠁧󠁢󠁥󠁮󠁧󠁿", "name": "flag: England", "shortname": ":England:", "unicode": "1F3F4 E0067 E0062 E0065 E006E E0067 E007F", "html": "🏴󠁧󠁢󠁥󠁮󠁧󠁿", "category": "Flags (subdivision-flag)", "order": ""}, + {"emoji": "🏴󠁧󠁢󠁳󠁣󠁴󠁿", "name": "flag: Scotland", "shortname": ":Scotland:", "unicode": "1F3F4 E0067 E0062 E0073 E0063 E0074 E007F", "html": "🏴󠁧󠁢󠁳󠁣󠁴󠁿", "category": "Flags (subdivision-flag)", "order": ""}, + {"emoji": "🏴󠁧󠁢󠁷󠁬󠁳󠁿", "name": "Wales", "shortname": ":wales:", "unicode": "1F3F4 E0067 E0062 E0077 E006C E0073 E007F", "html": "🏴󠁧󠁢󠁷󠁬󠁳󠁿", "category": "(subdivision-flag)", "order": ""} + ] +} \ No newline at end of file diff --git a/src/environ_vtab.cc b/src/environ_vtab.cc new file mode 100644 index 0000000..1265f4c --- /dev/null +++ b/src/environ_vtab.cc @@ -0,0 +1,338 @@ +/** + * Copyright (c) 2014, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "environ_vtab.hh" + +#include +#include + +#include "base/auto_mem.hh" +#include "base/lnav_log.hh" +#include "config.h" + +extern char** environ; + +const char* const ENVIRON_CREATE_STMT = R"( +-- Access lnav's environment variables through this table. +CREATE TABLE environ ( + name TEXT PRIMARY KEY, + value TEXT +); +)"; + +struct env_vtab { + sqlite3_vtab base; + sqlite3* db; +}; + +struct env_vtab_cursor { + sqlite3_vtab_cursor base; + char** env_cursor; +}; + +static int vt_destructor(sqlite3_vtab* p_svt); + +static int +vt_create(sqlite3* db, + void* pAux, + int argc, + const char* const* argv, + sqlite3_vtab** pp_vt, + char** pzErr) +{ + env_vtab* p_vt; + + /* Allocate the sqlite3_vtab/vtab structure itself */ + p_vt = (env_vtab*) sqlite3_malloc(sizeof(*p_vt)); + + if (p_vt == NULL) { + return SQLITE_NOMEM; + } + + memset(&p_vt->base, 0, sizeof(sqlite3_vtab)); + p_vt->db = db; + + *pp_vt = &p_vt->base; + + int rc = sqlite3_declare_vtab(db, ENVIRON_CREATE_STMT); + + return rc; +} + +static int +vt_destructor(sqlite3_vtab* p_svt) +{ + env_vtab* p_vt = (env_vtab*) p_svt; + + /* Free the SQLite structure */ + sqlite3_free(p_vt); + + return SQLITE_OK; +} + +static int +vt_connect(sqlite3* db, + void* p_aux, + int argc, + const char* const* argv, + sqlite3_vtab** pp_vt, + char** pzErr) +{ + return vt_create(db, p_aux, argc, argv, pp_vt, pzErr); +} + +static int +vt_disconnect(sqlite3_vtab* pVtab) +{ + return vt_destructor(pVtab); +} + +static int +vt_destroy(sqlite3_vtab* p_vt) +{ + return vt_destructor(p_vt); +} + +static int vt_next(sqlite3_vtab_cursor* cur); + +static int +vt_open(sqlite3_vtab* p_svt, sqlite3_vtab_cursor** pp_cursor) +{ + env_vtab* p_vt = (env_vtab*) p_svt; + + p_vt->base.zErrMsg = NULL; + + env_vtab_cursor* p_cur = (env_vtab_cursor*) new env_vtab_cursor(); + + if (p_cur == NULL) { + return SQLITE_NOMEM; + } else { + *pp_cursor = (sqlite3_vtab_cursor*) p_cur; + + p_cur->base.pVtab = p_svt; + p_cur->env_cursor = environ; + } + + return SQLITE_OK; +} + +static int +vt_close(sqlite3_vtab_cursor* cur) +{ + env_vtab_cursor* p_cur = (env_vtab_cursor*) cur; + + /* Free cursor struct. */ + delete p_cur; + + return SQLITE_OK; +} + +static int +vt_eof(sqlite3_vtab_cursor* cur) +{ + env_vtab_cursor* vc = (env_vtab_cursor*) cur; + + return vc->env_cursor[0] == NULL; +} + +static int +vt_next(sqlite3_vtab_cursor* cur) +{ + env_vtab_cursor* vc = (env_vtab_cursor*) cur; + + if (vc->env_cursor[0] != NULL) { + vc->env_cursor += 1; + } + + return SQLITE_OK; +} + +static int +vt_column(sqlite3_vtab_cursor* cur, sqlite3_context* ctx, int col) +{ + env_vtab_cursor* vc = (env_vtab_cursor*) cur; + const char* eq = strchr(vc->env_cursor[0], '='); + + switch (col) { + case 0: + sqlite3_result_text(ctx, + vc->env_cursor[0], + eq - vc->env_cursor[0], + SQLITE_TRANSIENT); + break; + case 1: + sqlite3_result_text(ctx, eq + 1, -1, SQLITE_TRANSIENT); + break; + } + + return SQLITE_OK; +} + +static int +vt_rowid(sqlite3_vtab_cursor* cur, sqlite_int64* p_rowid) +{ + env_vtab_cursor* p_cur = (env_vtab_cursor*) cur; + + *p_rowid = (int64_t) p_cur->env_cursor[0]; + + return SQLITE_OK; +} + +static int +vt_best_index(sqlite3_vtab* tab, sqlite3_index_info* p_info) +{ + return SQLITE_OK; +} + +static int +vt_filter(sqlite3_vtab_cursor* p_vtc, + int idxNum, + const char* idxStr, + int argc, + sqlite3_value** argv) +{ + return SQLITE_OK; +} + +static int +vt_update(sqlite3_vtab* tab, + int argc, + sqlite3_value** argv, + sqlite_int64* rowid) +{ + const char* name + = (argc > 2 ? (const char*) sqlite3_value_text(argv[2]) : nullptr); + env_vtab* p_vt = (env_vtab*) tab; + int retval = SQLITE_ERROR; + + if (argc != 1 + && (argc < 3 || sqlite3_value_type(argv[2]) == SQLITE_NULL + || sqlite3_value_type(argv[3]) == SQLITE_NULL + || sqlite3_value_text(argv[2])[0] == '\0')) + { + tab->zErrMsg = sqlite3_mprintf( + "A non-empty name and value must be provided when inserting an " + "environment variable"); + + return SQLITE_ERROR; + } + if (name != nullptr && strchr(name, '=') != nullptr) { + tab->zErrMsg = sqlite3_mprintf( + "Environment variable names cannot contain an equals sign (=)"); + + return SQLITE_ERROR; + } + + if (sqlite3_value_type(argv[0]) != SQLITE_NULL) { + int64_t index = sqlite3_value_int64(argv[0]); + const char* var = (const char*) index; + const char* eq = strchr(var, '='); + size_t namelen = eq - var; + char name[namelen + 1]; + + memcpy(name, var, namelen); + name[namelen] = '\0'; + unsetenv(name); + + retval = SQLITE_OK; + } else if (name != nullptr && getenv(name) != nullptr) { +#ifdef SQLITE_FAIL + int rc; + + rc = sqlite3_vtab_on_conflict(p_vt->db); + switch (rc) { + case SQLITE_FAIL: + case SQLITE_ABORT: + tab->zErrMsg = sqlite3_mprintf( + "An environment variable with the name '%s' already exists", + name); + return rc; + case SQLITE_IGNORE: + return SQLITE_OK; + case SQLITE_REPLACE: + break; + default: + return rc; + } +#endif + } + + if (name != nullptr && argc == 4) { + const unsigned char* value = sqlite3_value_text(argv[3]); + + setenv((const char*) name, (const char*) value, 1); + + return SQLITE_OK; + } + + return retval; +} + +static sqlite3_module vtab_module = { + 0, /* iVersion */ + vt_create, /* xCreate - create a vtable */ + vt_connect, /* xConnect - associate a vtable with a connection */ + vt_best_index, /* xBestIndex - best index */ + vt_disconnect, /* xDisconnect - disassociate a vtable with a connection */ + vt_destroy, /* xDestroy - destroy a vtable */ + vt_open, /* xOpen - open a cursor */ + vt_close, /* xClose - close a cursor */ + vt_filter, /* xFilter - configure scan constraints */ + vt_next, /* xNext - advance a cursor */ + vt_eof, /* xEof - inidicate end of result set*/ + vt_column, /* xColumn - read data */ + vt_rowid, /* xRowid - read data */ + vt_update, /* xUpdate - write data */ + NULL, /* xBegin - begin transaction */ + NULL, /* xSync - sync transaction */ + NULL, /* xCommit - commit transaction */ + NULL, /* xRollback - rollback transaction */ + NULL, /* xFindFunction - function overloading */ +}; + +int +register_environ_vtab(sqlite3* db) +{ + auto_mem errmsg; + int rc; + + rc = sqlite3_create_module(db, "environ_vtab_impl", &vtab_module, NULL); + ensure(rc == SQLITE_OK); + if ((rc = sqlite3_exec( + db, + "CREATE VIRTUAL TABLE environ USING environ_vtab_impl()", + NULL, + NULL, + errmsg.out())) + != SQLITE_OK) + { + fprintf(stderr, "unable to create environ table %s\n", errmsg.in()); + } + return rc; +} diff --git a/src/environ_vtab.hh b/src/environ_vtab.hh new file mode 100644 index 0000000..0b307d3 --- /dev/null +++ b/src/environ_vtab.hh @@ -0,0 +1,39 @@ +/** + * Copyright (c) 2014, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef environ_vtab_hh +#define environ_vtab_hh + +#include + +int register_environ_vtab(sqlite3* db); + +extern const char* const ENVIRON_CREATE_STMT; + +#endif diff --git a/src/extension-functions.cc b/src/extension-functions.cc new file mode 100644 index 0000000..461a382 --- /dev/null +++ b/src/extension-functions.cc @@ -0,0 +1,2828 @@ +/* +This library will provide common mathematical and string functions in +SQL queries using the operating system libraries or provided +definitions. It includes the following functions: + +Math: acos, asin, atan, atn2, atan2, acosh, asinh, atanh, difference, +degrees, radians, cos, sin, tan, cot, cosh, sinh, tanh, coth, exp, +log, log10, power, sign, sqrt, square, ceil, floor, pi. + +String: replicate, charindex, leftstr, rightstr, ltrim, rtrim, trim, +replace, reverse, proper, padl, padr, padc, strfilter. + +Aggregate: stdev, variance, mode, median, lower_quartile, +upper_quartile. + +The string functions ltrim, rtrim, trim, replace are included in +recent versions of SQLite and so by default do not build. + +Compilation instructions: + Compile this C source file into a dynamic library as follows: + * Linux: + gcc -fPIC -lm -shared extension-functions.c -o libsqlitefunctions.so + * Mac OS X: + gcc -fno-common -dynamiclib extension-functions.c -o libsqlitefunctions.dylib + (You may need to add flags + -I /opt/local/include/ -L/opt/local/lib -lsqlite3 + if your sqlite3 is installed from Mac ports, or + -I /sw/include/ -L/sw/lib -lsqlite3 + if installed with Fink.) + * Windows: + 1. Install MinGW (http://www.mingw.org/) and you will get the gcc + (gnu compiler collection) + 2. add the path to your path variable (isn't done during the + installation!) + 3. compile: + gcc -shared -I "path" -o libsqlitefunctions.so extension-functions.c + (path = path of sqlite3ext.h; i.e. C:\programs\sqlite) + +Usage instructions for applications calling the sqlite3 API functions: + In your application, call sqlite3_enable_load_extension(db,1) to + allow loading external libraries. Then load the library libsqlitefunctions + using sqlite3_load_extension; the third argument should be 0. + See http://www.sqlite.org/cvstrac/wiki?p=LoadableExtensions. + Select statements may now use these functions, as in + SELECT cos(radians(inclination)) FROM satsum WHERE satnum = 25544; + +Usage instructions for the sqlite3 program: + If the program is built so that loading extensions is permitted, + the following will work: + sqlite> SELECT load_extension('./libsqlitefunctions.so'); + sqlite> select cos(radians(45)); + 0.707106781186548 + Note: Loading extensions is by default prohibited as a + security measure; see "Security Considerations" in + http://www.sqlite.org/cvstrac/wiki?p=LoadableExtensions. + If the sqlite3 program and library are built this + way, you cannot use these functions from the program, you + must write your own program using the sqlite3 API, and call + sqlite3_enable_load_extension as described above, or else + rebuilt the sqlite3 program to allow loadable extensions. + +Alterations: +The instructions are for Linux, Mac OS X, and Windows; users of other +OSes may need to modify this procedure. In particular, if your math +library lacks one or more of the needed trig or log functions, comment +out the appropriate HAVE_ #define at the top of file. If you do not +wish to make a loadable module, comment out the define for +COMPILE_SQLITE_EXTENSIONS_AS_LOADABLE_MODULE. If you are using a +version of SQLite without the trim functions and replace, comment out +the HAVE_TRIM #define. + +Liam Healy + +History: +2010-01-06 Correct check for argc in squareFunc, and add Windows +compilation instructions. +2009-06-24 Correct check for argc in properFunc. +2008-09-14 Add check that memory was actually allocated after +sqlite3_malloc or sqlite3StrDup, call sqlite3_result_error_nomem if +not. Thanks to Robert Simpson. +2008-06-13 Change to instructions to indicate use of the math library +and that program might work. +2007-10-01 Minor clarification to instructions. +2007-09-29 Compilation as loadable module is optional with +COMPILE_SQLITE_EXTENSIONS_AS_LOADABLE_MODULE. +2007-09-28 Use sqlite3_extension_init and macros +SQLITE_EXTENSION_INIT1, SQLITE_EXTENSION_INIT2, so that it works with +sqlite3_load_extension. Thanks to Eric Higashino and Joe Wilson. +New instructions for Mac compilation. +2007-09-17 With help from Joe Wilson and Nuno Luca, made use of +external interfaces so that compilation is no longer dependent on +SQLite source code. Merged source, header, and README into a single +file. Added casts so that Mac will compile without warnings (unsigned +and signed char). +2007-09-05 Included some definitions from sqlite 3.3.13 so that this +will continue to work in newer versions of sqlite. Completed +description of functions available. +2007-03-27 Revised description. +2007-03-23 Small cleanup and a bug fix on the code. This was mainly +letting errno flag errors encountered in the math library and checking +the result, rather than pre-checking. This fixes a bug in power that +would cause an error if any non-positive number was raised to any +power. +2007-02-07 posted by Mikey C to sqlite mailing list. +Original code 2006 June 05 by relicoder. + +*/ + +//#include "config.h" + +// #define COMPILE_SQLITE_EXTENSIONS_AS_LOADABLE_MODULE 1 +#define HAVE_ACOSH 1 +#define HAVE_ASINH 1 +#define HAVE_ATANH 1 +#define HAVE_SINH 1 +#define HAVE_COSH 1 +#define HAVE_TANH 1 +#define HAVE_LOG10 1 +#define HAVE_ISBLANK 1 +#define SQLITE_SOUNDEX 1 +#define HAVE_TRIM 1 /* LMH 2007-03-25 if sqlite has trim functions */ + +#define __STDC_FORMAT_MACROS + +#ifdef COMPILE_SQLITE_EXTENSIONS_AS_LOADABLE_MODULE +# include "sqlite3ext.h" +SQLITE_EXTENSION_INIT1 +#else +# include "sqlite3.h" +#endif + +#include +/* relicoder */ +#include +#include /* LMH 2007-03-25 */ +#include +#include +#include +#include + +#ifndef _MAP_H_ +# define _MAP_H_ + +# include +# include + +# include "sqlite-extension-func.hh" + +/* +** Simple binary tree implementation to use in median, mode and quartile +*calculations +** Tree is not necessarily balanced. That would require something like red&black +*trees of AVL +*/ + +typedef int (*cmp_func)(const void*, const void*); +typedef void (*map_iterator)(void*, int64_t, void*); + +typedef struct node { + struct node* l; + struct node* r; + void* data; + int64_t count; +} node; + +typedef struct map { + node* base; + cmp_func cmp; + short free; +} map; + +/* +** creates a map given a comparison function +*/ +map map_make(cmp_func cmp); + +/* +** inserts the element e into map m +*/ +void map_insert(map* m, void* e); + +/* +** executes function iter over all elements in the map, in key increasing order +*/ +void map_iterate(map* m, map_iterator iter, void* p); + +/* +** frees all memory used by a map +*/ +void map_destroy(map* m); + +/* +** compares 2 integers +** to use with map_make +*/ +int int_cmp(const void* a, const void* b); + +/* +** compares 2 doubles +** to use with map_make +*/ +int double_cmp(const void* a, const void* b); + +#endif /* _MAP_H_ */ + +typedef uint8_t u8; +typedef uint16_t u16; +typedef int64_t i64; + +static char* +sqlite3StrDup(const char* z) +{ + char* res = (char*) sqlite3_malloc(strlen(z) + 1); + return strcpy(res, z); +} + +/* +** These are copied verbatim from fun.c so as to not have the names exported +*/ + +/* LMH from sqlite3 3.3.13 */ +/* +** This table maps from the first byte of a UTF-8 character to the number +** of trailing bytes expected. A value '4' indicates that the table key +** is not a legal first byte for a UTF-8 character. +*/ +static const u8 xtra_utf8_bytes[256] = { + /* 0xxxxxxx */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* 10wwwwww */ + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + + /* 110yyyyy */ + 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, + + /* 1110zzzz */ + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + + /* 11110yyy */ + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, +}; + +/* +** This table maps from the number of trailing bytes in a UTF-8 character +** to an integer constant that is effectively calculated for each character +** read by a naive implementation of a UTF-8 character reader. The code +** in the READ_UTF8 macro explains things best. +*/ +static const int xtra_utf8_bits[] = { + 0, + 12416, /* (0xC0 << 6) + (0x80) */ + 925824, /* (0xE0 << 12) + (0x80 << 6) + (0x80) */ + 63447168 /* (0xF0 << 18) + (0x80 << 12) + (0x80 << 6) + 0x80 */ +}; + +/* +** If a UTF-8 character contains N bytes extra bytes (N bytes follow +** the initial byte so that the total character length is N+1) then +** masking the character with utf8_mask[N] must produce a non-zero +** result. Otherwise, we have an (illegal) overlong encoding. +*/ +static const unsigned long utf_mask[] = { + 0x00000000, + 0xffffff80, + 0xfffff800, + 0xffff0000, +}; + +/* LMH salvaged from sqlite3 3.3.13 source code src/utf.c */ +#define READ_UTF8(zIn, c) \ + { \ + int xtra; \ + c = *(zIn)++; \ + xtra = xtra_utf8_bytes[c]; \ + switch (xtra) { \ + case 4: \ + c = (int) 0xFFFD; \ + break; \ + case 3: \ + c = (c << 6) + *(zIn)++; \ + case 2: \ + c = (c << 6) + *(zIn)++; \ + case 1: \ + c = (c << 6) + *(zIn)++; \ + c -= xtra_utf8_bits[xtra]; \ + if ((utf_mask[xtra] & c) == 0 || (c & 0xFFFFF800) == 0xD800 \ + || (c & 0xFFFFFFFE) == 0xFFFE) \ + { \ + c = 0xFFFD; \ + } \ + } \ + } + +static int +sqlite3ReadUtf8(const unsigned char* z) +{ + int c; + READ_UTF8(z, c); + return c; +} + +#define SKIP_UTF8(zIn) \ + { \ + zIn += (xtra_utf8_bytes[*(u8*) zIn] + 1); \ + } + +/* +** pZ is a UTF-8 encoded unicode string. If nByte is less than zero, +** return the number of unicode characters in pZ up to (but not including) +** the first 0x00 byte. If nByte is not less than zero, return the +** number of unicode characters in the first nByte of pZ (or up to +** the first 0x00, whichever comes first). +*/ +static int +sqlite3Utf8CharLen(const char* z, int nByte) +{ + int r = 0; + const char* zTerm; + if (nByte >= 0) { + zTerm = &z[nByte]; + } else { + zTerm = (const char*) (-1); + } + assert(z <= zTerm); + while (*z != 0 && z < zTerm) { + SKIP_UTF8(z); + r++; + } + return r; +} + +/* +** X is a pointer to the first byte of a UTF-8 character. Increment +** X so that it points to the next character. This only works right +** if X points to a well-formed UTF-8 string. +*/ +#define sqliteNextChar(X) \ + while ((0xc0 & *++(X)) == 0x80) { \ + } +#define sqliteCharVal(X) sqlite3ReadUtf8(X) + +/* +** This is a macro that facilitates writting wrappers for math.h functions +** it creates code for a function to use in SQlite that gets one numeric input +** and returns a floating point value. +** +** Could have been implemented using pointers to functions but this way it's +*inline +** and thus more efficient. Lower * ranking though... +** +** Parameters: +** name: function name to de defined (eg: sinFunc) +** function: function defined in math.h to wrap (eg: sin) +** domain: boolean condition that CAN'T happen in terms of the input +*parameter rVal +** (eg: rval<0 for sqrt) +*/ +/* LMH 2007-03-25 Changed to use errno and remove domain; no pre-checking for + * errors. */ +#define GEN_MATH_WRAP_DOUBLE_1(name, function) \ + static void name(sqlite3_context* context, int argc, sqlite3_value** argv) \ + { \ + double rVal = 0.0, val; \ + assert(argc == 1); \ + switch (sqlite3_value_type(argv[0])) { \ + case SQLITE_NULL: { \ + sqlite3_result_null(context); \ + break; \ + } \ + default: { \ + rVal = sqlite3_value_double(argv[0]); \ + errno = 0; \ + val = function(rVal); \ + if (errno == 0) { \ + sqlite3_result_double(context, val); \ + } else { \ + sqlite3_result_error(context, strerror(errno), errno); \ + } \ + break; \ + } \ + } \ + } + +/* +** Example of GEN_MATH_WRAP_DOUBLE_1 usage +** this creates function sqrtFunc to wrap the math.h standard function +*sqrt(x)=x^0.5 +*/ +GEN_MATH_WRAP_DOUBLE_1(sqrtFunc, sqrt) + +/* trignometric functions */ +GEN_MATH_WRAP_DOUBLE_1(acosFunc, acos) +GEN_MATH_WRAP_DOUBLE_1(asinFunc, asin) +GEN_MATH_WRAP_DOUBLE_1(atanFunc, atan) + +/* +** Many of systems don't have inverse hyperbolic trig functions so this will +*emulate +** them on those systems in terms of log and sqrt (formulas are too trivial to +*demand +** written proof here) +*/ + +#ifndef HAVE_ACOSH +static double +acosh(double x) +{ + return log(x + sqrt(x * x - 1.0)); +} +#endif + +GEN_MATH_WRAP_DOUBLE_1(acoshFunc, acosh) + +#ifndef HAVE_ASINH +static double +asinh(double x) +{ + return log(x + sqrt(x * x + 1.0)); +} +#endif + +GEN_MATH_WRAP_DOUBLE_1(asinhFunc, asinh) + +#ifndef HAVE_ATANH +static double +atanh(double x) +{ + return (1.0 / 2.0) * log((1 + x) / (1 - x)); +} +#endif + +GEN_MATH_WRAP_DOUBLE_1(atanhFunc, atanh) + +/* +** math.h doesn't require cot (cotangent) so it's defined here +*/ +static double +cot(double x) +{ + return 1.0 / tan(x); +} + +GEN_MATH_WRAP_DOUBLE_1(sinFunc, sin) +GEN_MATH_WRAP_DOUBLE_1(cosFunc, cos) +GEN_MATH_WRAP_DOUBLE_1(tanFunc, tan) +GEN_MATH_WRAP_DOUBLE_1(cotFunc, cot) + +static double +coth(double x) +{ + return 1.0 / tanh(x); +} + +/* +** Many systems don't have hyperbolic trigonometric functions so this will +*emulate +** them on those systems directly from the definition in terms of exp +*/ +#ifndef HAVE_SINH +static double +sinh(double x) +{ + return (exp(x) - exp(-x)) / 2.0; +} +#endif + +GEN_MATH_WRAP_DOUBLE_1(sinhFunc, sinh) + +#ifndef HAVE_COSH +static double +cosh(double x) +{ + return (exp(x) + exp(-x)) / 2.0; +} +#endif + +GEN_MATH_WRAP_DOUBLE_1(coshFunc, cosh) + +#ifndef HAVE_TANH +static double +tanh(double x) +{ + return sinh(x) / cosh(x); +} +#endif + +GEN_MATH_WRAP_DOUBLE_1(tanhFunc, tanh) + +GEN_MATH_WRAP_DOUBLE_1(cothFunc, coth) + +/* +** Some systems lack log in base 10. This will emulate it +*/ + +#ifndef HAVE_LOG10 +static double +log10(double x) +{ + static double l10 = -1.0; + if (l10 < 0.0) { + l10 = log(10.0); + } + return log(x) / l10; +} +#endif + +GEN_MATH_WRAP_DOUBLE_1(logFunc, log) +GEN_MATH_WRAP_DOUBLE_1(log10Func, log10) +GEN_MATH_WRAP_DOUBLE_1(expFunc, exp) + +/* +** Fallback for systems where math.h doesn't define M_PI +*/ +#undef M_PI +#ifndef M_PI +/* +** static double PI = acos(-1.0); +** #define M_PI (PI) +*/ +# define M_PI 3.14159265358979323846 +#endif + +/* Convert Degrees into Radians */ +static double +deg2rad(double x) +{ + return x * M_PI / 180.0; +} + +/* Convert Radians into Degrees */ +static double +rad2deg(double x) +{ + return 180.0 * x / M_PI; +} + +GEN_MATH_WRAP_DOUBLE_1(rad2degFunc, rad2deg) +GEN_MATH_WRAP_DOUBLE_1(deg2radFunc, deg2rad) + +/* constant function that returns the value of PI=3.1415... */ +static void +piFunc(sqlite3_context* context, int argc, sqlite3_value** argv) +{ + sqlite3_result_double(context, M_PI); +} + +/* +** Implements the sqrt function, it has the peculiarity of returning an integer +*when the +** the argument is an integer. +** Since SQLite isn't strongly typed (almost untyped actually) this is a bit +*pedantic +*/ +static void +squareFunc(sqlite3_context* context, int argc, sqlite3_value** argv) +{ + i64 iVal = 0; + double rVal = 0.0; + assert(argc == 1); + switch (sqlite3_value_type(argv[0])) { + case SQLITE_INTEGER: { + iVal = sqlite3_value_int64(argv[0]); + sqlite3_result_int64(context, iVal * iVal); + break; + } + case SQLITE_NULL: { + sqlite3_result_null(context); + break; + } + default: { + rVal = sqlite3_value_double(argv[0]); + sqlite3_result_double(context, rVal * rVal); + break; + } + } +} + +/* +** Wraps the pow math.h function +** When both the base and the exponent are integers the result should be integer +** (see sqrt just before this). Here the result is always double +*/ +/* LMH 2007-03-25 Changed to use errno; no pre-checking for errors. Also + removes but that was present in the pre-checking that called + sqlite3_result_error on + a non-positive first argument, which is not always an error. */ +static void +powerFunc(sqlite3_context* context, int argc, sqlite3_value** argv) +{ + double r1 = 0.0; + double r2 = 0.0; + double val; + + assert(argc == 2); + + if (sqlite3_value_type(argv[0]) == SQLITE_NULL + || sqlite3_value_type(argv[1]) == SQLITE_NULL) + { + sqlite3_result_null(context); + } else { + r1 = sqlite3_value_double(argv[0]); + r2 = sqlite3_value_double(argv[1]); + errno = 0; + val = pow(r1, r2); + if (errno == 0) { + sqlite3_result_double(context, val); + } else { + sqlite3_result_error(context, strerror(errno), errno); + } + } +} + +/* +** atan2 wrapper +*/ +static void +atn2Func(sqlite3_context* context, int argc, sqlite3_value** argv) +{ + double r1 = 0.0; + double r2 = 0.0; + + assert(argc == 2); + + if (sqlite3_value_type(argv[0]) == SQLITE_NULL + || sqlite3_value_type(argv[1]) == SQLITE_NULL) + { + sqlite3_result_null(context); + } else { + r1 = sqlite3_value_double(argv[0]); + r2 = sqlite3_value_double(argv[1]); + sqlite3_result_double(context, atan2(r1, r2)); + } +} + +/* +** Implementation of the sign() function +** return one of 3 possibilities +1,0 or -1 when the argument is respectively +** positive, 0 or negative. +** When the argument is NULL the result is also NULL (completly conventional) +*/ +static void +signFunc(sqlite3_context* context, int argc, sqlite3_value** argv) +{ + double rVal = 0.0; + i64 iVal = 0; + assert(argc == 1); + switch (sqlite3_value_type(argv[0])) { + case SQLITE_INTEGER: { + iVal = sqlite3_value_int64(argv[0]); + iVal = (iVal > 0) ? 1 : (iVal < 0) ? -1 : 0; + sqlite3_result_int64(context, iVal); + break; + } + case SQLITE_NULL: { + sqlite3_result_null(context); + break; + } + default: { + /* 2nd change below. Line for abs was: if( rVal<0 ) rVal = rVal * + * -1.0; */ + + rVal = sqlite3_value_double(argv[0]); + rVal = (rVal > 0) ? 1 : (rVal < 0) ? -1 : 0; + sqlite3_result_double(context, rVal); + break; + } + } +} + +/* +** smallest integer value not less than argument +*/ +static void +ceilFunc(sqlite3_context* context, int argc, sqlite3_value** argv) +{ + double rVal = 0.0; + assert(argc == 1); + switch (sqlite3_value_type(argv[0])) { + case SQLITE_INTEGER: { + i64 iVal = sqlite3_value_int64(argv[0]); + sqlite3_result_int64(context, iVal); + break; + } + case SQLITE_NULL: { + sqlite3_result_null(context); + break; + } + default: { + rVal = sqlite3_value_double(argv[0]); + sqlite3_result_int64(context, (i64) ceil(rVal)); + break; + } + } +} + +/* +** largest integer value not greater than argument +*/ +static void +floorFunc(sqlite3_context* context, int argc, sqlite3_value** argv) +{ + double rVal = 0.0; + assert(argc == 1); + switch (sqlite3_value_type(argv[0])) { + case SQLITE_INTEGER: { + i64 iVal = sqlite3_value_int64(argv[0]); + sqlite3_result_int64(context, iVal); + break; + } + case SQLITE_NULL: { + sqlite3_result_null(context); + break; + } + default: { + rVal = sqlite3_value_double(argv[0]); + sqlite3_result_int64(context, (i64) floor(rVal)); + break; + } + } +} + +/* +** Given a string (s) in the first argument and an integer (n) in the second +*returns the +** string that constains s contatenated n times +*/ +static void +replicateFunc(sqlite3_context* context, int argc, sqlite3_value** argv) +{ + static const char* EMPTY = ""; + unsigned char* z; /* result string */ + i64 iCount; /* times to repeat */ + i64 nLen; /* length of the input string (no multibyte considerations) */ + i64 nTLen; /* length of the result string (no multibyte considerations) */ + i64 i = 0; + + if (argc != 2 || SQLITE_NULL == sqlite3_value_type(argv[0])) + return; + + iCount = sqlite3_value_int64(argv[1]); + + if (iCount < 0) { + sqlite3_result_error(context, "domain error", -1); + return; + } + + if (iCount == 0) { + sqlite3_result_text(context, EMPTY, 0, SQLITE_STATIC); + return; + } + + nLen = sqlite3_value_bytes(argv[0]); + nTLen = nLen * iCount; + z = (unsigned char*) sqlite3_malloc(nTLen + 1); + if (!z) { + sqlite3_result_error_nomem(context); + if (z) + sqlite3_free(z); + return; + } + auto zo = sqlite3_value_text(argv[0]); + + for (i = 0; i < iCount; ++i) { + strcpy((char*) (z + i * nLen), (char*) zo); + } + + sqlite3_result_text(context, (char*) z, -1, sqlite3_free); +} + +/* +** Some systems (win32 among others) don't have an isblank function, this will +*emulate it. +** This function is not UFT-8 safe since it only analyses a byte character. +*/ +#ifndef HAVE_ISBLANK +int +isblank(char c) +{ + return (' ' == c || '\t' == c); +} +#endif + +static void +properFunc(sqlite3_context* context, int argc, sqlite3_value** argv) +{ + const unsigned char* z; /* input string */ + unsigned char* zo; /* output string */ + unsigned char* zt; /* iterator */ + char r; + int c = 1; + + assert(argc == 1); + if (SQLITE_NULL == sqlite3_value_type(argv[0])) { + sqlite3_result_null(context); + return; + } + + z = sqlite3_value_text(argv[0]); + zo = (unsigned char*) sqlite3StrDup((char*) z); + if (!zo) { + sqlite3_result_error_nomem(context); + return; + } + zt = zo; + + while ((r = *(z++)) != 0) { + if (isblank(r)) { + c = 1; + } else { + if (c == 1) { + r = toupper(r); + } else { + r = tolower(r); + } + c = 0; + } + *(zt++) = r; + } + *zt = '\0'; + + sqlite3_result_text(context, (char*) zo, -1, SQLITE_TRANSIENT); + sqlite3_free(zo); +} + +/* +** given an input string (s) and an integer (n) adds spaces at the begining of s +** until it has a length of n characters. +** When s has a length >=n it's a NOP +** padl(NULL) = NULL +*/ +static void +padlFunc(sqlite3_context* context, int argc, sqlite3_value** argv) +{ + i64 ilen; /* length to pad to */ + i64 zl; /* length of the input string (UTF-8 chars) */ + int i = 0; + const char* zi; /* input string */ + char* zo; /* output string */ + char* zt; + + assert(argc == 2); + + if (sqlite3_value_type(argv[0]) == SQLITE_NULL) { + sqlite3_result_null(context); + } else { + zi = (char*) sqlite3_value_text(argv[0]); + ilen = sqlite3_value_int64(argv[1]); + /* check domain */ + if (ilen < 0) { + sqlite3_result_error(context, "domain error", -1); + return; + } + zl = sqlite3Utf8CharLen(zi, -1); + if (zl >= ilen) { + /* string is longer than the requested pad length, return the same + * string (dup it) */ + zo = sqlite3StrDup(zi); + if (!zo) { + sqlite3_result_error_nomem(context); + return; + } + sqlite3_result_text(context, zo, -1, SQLITE_TRANSIENT); + } else { + zo = (char*) sqlite3_malloc(strlen(zi) + ilen - zl + 1); + if (!zo) { + sqlite3_result_error_nomem(context); + return; + } + zt = zo; + for (i = 1; i + zl <= ilen; ++i) { + *(zt++) = ' '; + } + /* no need to take UTF-8 into consideration here */ + strcpy(zt, zi); + } + sqlite3_result_text(context, zo, -1, SQLITE_TRANSIENT); + sqlite3_free(zo); + } +} + +/* +** given an input string (s) and an integer (n) appends spaces at the end of s +** until it has a length of n characters. +** When s has a length >=n it's a NOP +** padl(NULL) = NULL +*/ +static void +padrFunc(sqlite3_context* context, int argc, sqlite3_value** argv) +{ + i64 ilen; /* length to pad to */ + i64 zl; /* length of the input string (UTF-8 chars) */ + i64 zll; /* length of the input string (bytes) */ + int i = 0; + const char* zi; /* input string */ + char* zo; /* output string */ + char* zt; + + assert(argc == 2); + + if (sqlite3_value_type(argv[0]) == SQLITE_NULL) { + sqlite3_result_null(context); + } else { + zi = (char*) sqlite3_value_text(argv[0]); + ilen = sqlite3_value_int64(argv[1]); + /* check domain */ + if (ilen < 0) { + sqlite3_result_error(context, "domain error", -1); + return; + } + zl = sqlite3Utf8CharLen(zi, -1); + if (zl >= ilen) { + /* string is longer than the requested pad length, return the same + * string (dup it) */ + zo = sqlite3StrDup(zi); + if (!zo) { + sqlite3_result_error_nomem(context); + return; + } + sqlite3_result_text(context, zo, -1, SQLITE_TRANSIENT); + } else { + zll = strlen(zi); + zo = (char*) sqlite3_malloc(zll + ilen - zl + 1); + if (!zo) { + sqlite3_result_error_nomem(context); + return; + } + zt = strcpy(zo, zi) + zll; + for (i = 1; i + zl <= ilen; ++i) { + *(zt++) = ' '; + } + *zt = '\0'; + } + sqlite3_result_text(context, zo, -1, SQLITE_TRANSIENT); + sqlite3_free(zo); + } +} + +/* +** given an input string (s) and an integer (n) appends spaces at the end of s +** and adds spaces at the begining of s until it has a length of n characters. +** Tries to add has many characters at the left as at the right. +** When s has a length >=n it's a NOP +** padl(NULL) = NULL +*/ +static void +padcFunc(sqlite3_context* context, int argc, sqlite3_value** argv) +{ + i64 ilen; /* length to pad to */ + i64 zl; /* length of the input string (UTF-8 chars) */ + i64 zll; /* length of the input string (bytes) */ + int i = 0; + const char* zi; /* input string */ + char* zo; /* output string */ + char* zt; + + assert(argc == 2); + + if (sqlite3_value_type(argv[0]) == SQLITE_NULL) { + sqlite3_result_null(context); + } else { + zi = (char*) sqlite3_value_text(argv[0]); + ilen = sqlite3_value_int64(argv[1]); + /* check domain */ + if (ilen < 0) { + sqlite3_result_error(context, "domain error", -1); + return; + } + zl = sqlite3Utf8CharLen(zi, -1); + if (zl >= ilen) { + /* string is longer than the requested pad length, return the same + * string (dup it) */ + zo = sqlite3StrDup(zi); + if (!zo) { + sqlite3_result_error_nomem(context); + return; + } + sqlite3_result_text(context, zo, -1, SQLITE_TRANSIENT); + } else { + zll = strlen(zi); + zo = (char*) sqlite3_malloc(zll + ilen - zl + 1); + if (!zo) { + sqlite3_result_error_nomem(context); + return; + } + zt = zo; + for (i = 1; 2 * i + zl <= ilen; ++i) { + *(zt++) = ' '; + } + strcpy(zt, zi); + zt += zll; + for (; i + zl <= ilen; ++i) { + *(zt++) = ' '; + } + *zt = '\0'; + } + sqlite3_result_text(context, zo, -1, SQLITE_TRANSIENT); + sqlite3_free(zo); + } +} + +/* +** given 2 string (s1,s2) returns the string s1 with the characters NOT in s2 +*removed +** assumes strings are UTF-8 encoded +*/ +static void +strfilterFunc(sqlite3_context* context, int argc, sqlite3_value** argv) +{ + const char* zi1; /* first parameter string (searched string) */ + const char* zi2; /* second parameter string (vcontains valid characters) */ + const char* z1; + const char* z21; + const char* z22; + char* zo; /* output string */ + char* zot; + int c1 = 0; + int c2 = 0; + + assert(argc == 2); + + if (sqlite3_value_type(argv[0]) == SQLITE_NULL + || sqlite3_value_type(argv[1]) == SQLITE_NULL) + { + sqlite3_result_null(context); + } else { + zi1 = (char*) sqlite3_value_text(argv[0]); + zi2 = (char*) sqlite3_value_text(argv[1]); + /* + ** maybe I could allocate less, but that would imply 2 passes, rather + *waste + ** (possibly) some memory + */ + zo = (char*) sqlite3_malloc(strlen(zi1) + 1); + if (!zo) { + sqlite3_result_error_nomem(context); + return; + } + zot = zo; + z1 = zi1; + while ((c1 = sqliteCharVal((unsigned char*) z1)) != 0) { + z21 = zi2; + while ((c2 = sqliteCharVal((unsigned char*) z21)) != 0 && c2 != c1) + { + sqliteNextChar(z21); + } + if (c2 != 0) { + z22 = z21; + sqliteNextChar(z22); + strncpy(zot, z21, z22 - z21); + zot += z22 - z21; + } + sqliteNextChar(z1); + } + *zot = '\0'; + + sqlite3_result_text(context, zo, -1, SQLITE_TRANSIENT); + sqlite3_free(zo); + } +} + +/* +** Given a string z1, retutns the (0 based) index of it's first occurence +** in z2 after the first s characters. +** Returns -1 when there isn't a match. +** updates p to point to the character where the match occured. +** This is an auxiliary function. +*/ +static int +_substr(const char* z1, const char* z2, int s, const char** p) +{ + int c = 0; + int rVal = -1; + const char* zt1; + const char* zt2; + int c1, c2; + + if ('\0' == *z1) { + return -1; + } + + while ((sqliteCharVal((unsigned char*) z2) != 0) && (c++) < s) { + sqliteNextChar(z2); + } + + c = 0; + while ((sqliteCharVal((unsigned char*) z2)) != 0) { + zt1 = z1; + zt2 = z2; + + do { + c1 = sqliteCharVal((unsigned char*) zt1); + c2 = sqliteCharVal((unsigned char*) zt2); + if (c1 == 0) { + break; + } + if (c2 == 0) { + break; + } + sqliteNextChar(zt1); + sqliteNextChar(zt2); + } while (c1 == c2 && c1 != 0 && c2 != 0); + + if (c1 == 0) { + rVal = c; + break; + } + + sqliteNextChar(z2); + ++c; + } + if (p) { + *p = z2; + } + return rVal >= 0 ? rVal + s : rVal; +} + +/* +** given 2 input strings (s1,s2) and an integer (n) searches from the nth +*character +** for the string s1. Returns the position where the match occured. +** Characters are counted from 1. +** 0 is returned when no match occurs. +*/ + +static void +charindexFunc(sqlite3_context* context, int argc, sqlite3_value** argv) +{ + const u8* z1; /* s1 string */ + u8* z2; /* s2 string */ + int s = 0; + int rVal = 0; + + assert(argc == 3 || argc == 2); + + if (SQLITE_NULL == sqlite3_value_type(argv[0]) + || SQLITE_NULL == sqlite3_value_type(argv[1])) + { + sqlite3_result_null(context); + return; + } + + z1 = sqlite3_value_text(argv[0]); + if (z1 == 0) + return; + z2 = (u8*) sqlite3_value_text(argv[1]); + if (argc == 3) { + s = sqlite3_value_int(argv[2]) - 1; + if (s < 0) { + s = 0; + } + } else { + s = 0; + } + + rVal = _substr((char*) z1, (char*) z2, s, NULL); + sqlite3_result_int(context, rVal + 1); +} + +/* +** given a string (s) and an integer (n) returns the n leftmost (UTF-8) +*characters +** if the string has a length<=n or is NULL this function is NOP +*/ +static void +leftFunc(sqlite3_context* context, int argc, sqlite3_value** argv) +{ + int c = 0; + int cc = 0; + int l = 0; + const unsigned char* z; /* input string */ + const unsigned char* zt; + unsigned char* rz; /* output string */ + + assert(argc == 2); + + if (SQLITE_NULL == sqlite3_value_type(argv[0]) + || SQLITE_NULL == sqlite3_value_type(argv[1])) + { + sqlite3_result_null(context); + return; + } + + z = sqlite3_value_text(argv[0]); + l = sqlite3_value_int(argv[1]); + zt = z; + + while (sqliteCharVal(zt) && c++ < l) + sqliteNextChar(zt); + + cc = zt - z; + + rz = (unsigned char*) sqlite3_malloc(zt - z + 1); + if (!rz) { + sqlite3_result_error_nomem(context); + return; + } + strncpy((char*) rz, (char*) z, zt - z); + *(rz + cc) = '\0'; + sqlite3_result_text(context, (char*) rz, -1, SQLITE_TRANSIENT); + sqlite3_free(rz); +} + +/* +** given a string (s) and an integer (n) returns the n rightmost (UTF-8) +*characters +** if the string has a length<=n or is NULL this function is NOP +*/ +static void +rightFunc(sqlite3_context* context, int argc, sqlite3_value** argv) +{ + int l = 0; + int c = 0; + int cc = 0; + const char* z; + const char* zt; + const char* ze; + char* rz; + + assert(argc == 2); + + if (SQLITE_NULL == sqlite3_value_type(argv[0]) + || SQLITE_NULL == sqlite3_value_type(argv[1])) + { + sqlite3_result_null(context); + return; + } + + z = (char*) sqlite3_value_text(argv[0]); + l = sqlite3_value_int(argv[1]); + zt = z; + + while (sqliteCharVal((unsigned char*) zt) != 0) { + sqliteNextChar(zt); + ++c; + } + + ze = zt; + zt = z; + + cc = c - l; + if (cc < 0) + cc = 0; + + while (cc-- > 0) { + sqliteNextChar(zt); + } + + rz = (char*) sqlite3_malloc(ze - zt + 1); + if (!rz) { + sqlite3_result_error_nomem(context); + return; + } + strcpy((char*) rz, (char*) (zt)); + sqlite3_result_text(context, (char*) rz, -1, SQLITE_TRANSIENT); + sqlite3_free(rz); +} + +#ifndef HAVE_TRIM +/* +** removes the whitespaces at the begining of a string. +*/ +const char* +ltrim(const char* s) +{ + while (*s == ' ') + ++s; + return s; +} + +/* +** removes the whitespaces at the end of a string. +** !mutates the input string! +*/ +void +rtrim(char* s) +{ + char* ss = s + strlen(s) - 1; + while (ss >= s && *ss == ' ') + --ss; + *(ss + 1) = '\0'; +} + +/* +** Removes the whitespace at the begining of a string +*/ +static void +ltrimFunc(sqlite3_context* context, int argc, sqlite3_value** argv) +{ + const char* z; + + assert(argc == 1); + + if (SQLITE_NULL == sqlite3_value_type(argv[0])) { + sqlite3_result_null(context); + return; + } + z = sqlite3_value_text(argv[0]); + sqlite3_result_text(context, ltrim(z), -1, SQLITE_TRANSIENT); +} + +/* +** Removes the whitespace at the end of a string +*/ +static void +rtrimFunc(sqlite3_context* context, int argc, sqlite3_value** argv) +{ + const char* z; + char* rz; + /* try not to change data in argv */ + + assert(argc == 1); + + if (SQLITE_NULL == sqlite3_value_type(argv[0])) { + sqlite3_result_null(context); + return; + } + z = sqlite3_value_text(argv[0]); + rz = sqlite3StrDup(z); + rtrim(rz); + sqlite3_result_text(context, rz, -1, SQLITE_TRANSIENT); + sqlite3_free(rz); +} + +/* +** Removes the whitespace at the begining and end of a string +*/ +static void +trimFunc(sqlite3_context* context, int argc, sqlite3_value** argv) +{ + const char* z; + char* rz; + /* try not to change data in argv */ + + assert(argc == 1); + + if (SQLITE_NULL == sqlite3_value_type(argv[0])) { + sqlite3_result_null(context); + return; + } + z = sqlite3_value_text(argv[0]); + rz = sqlite3StrDup(z); + rtrim(rz); + sqlite3_result_text(context, ltrim(rz), -1, SQLITE_TRANSIENT); + sqlite3_free(rz); +} +#endif + +/* +** given a pointer to a string s1, the length of that string (l1), a new string +*(s2) +** and it's length (l2) appends s2 to s1. +** All lengths in bytes. +** This is just an auxiliary function +*/ +// static void _append(char **s1, int l1, const char *s2, int l2){ +// *s1 = realloc(*s1, (l1+l2+1)*sizeof(char)); +// strncpy((*s1)+l1, s2, l2); +// *(*(s1)+l1+l2) = '\0'; +// } + +#ifndef HAVE_TRIM + +/* +** given strings s, s1 and s2 replaces occurrences of s1 in s by s2 +*/ +static void +replaceFunc(sqlite3_context* context, int argc, sqlite3_value** argv) +{ + const char* z1; /* string s (first parameter) */ + const char* z2; /* string s1 (second parameter) string to look for */ + const char* z3; /* string s2 (third parameter) string to replace occurrences + of s1 with */ + int lz1; + int lz2; + int lz3; + int lzo = 0; + char* zo = 0; + int ret = 0; + const char* zt1; + const char* zt2; + + assert(3 == argc); + + if (SQLITE_NULL == sqlite3_value_type(argv[0])) { + sqlite3_result_null(context); + return; + } + + z1 = sqlite3_value_text(argv[0]); + z2 = sqlite3_value_text(argv[1]); + z3 = sqlite3_value_text(argv[2]); + /* handle possible null values */ + if (0 == z2) { + z2 = ""; + } + if (0 == z3) { + z3 = ""; + } + + lz1 = strlen(z1); + lz2 = strlen(z2); + lz3 = strlen(z3); + +# if 0 + /* special case when z2 is empty (or null) nothing will be changed */ + if( 0==lz2 ){ + sqlite3_result_text(context, z1, -1, SQLITE_TRANSIENT); + return; + } +# endif + + zt1 = z1; + zt2 = z1; + + while (1) { + ret = _substr(z2, zt1, 0, &zt2); + + if (ret < 0) + break; + + _append(&zo, lzo, zt1, zt2 - zt1); + lzo += zt2 - zt1; + _append(&zo, lzo, z3, lz3); + lzo += lz3; + + zt1 = zt2 + lz2; + } + _append(&zo, lzo, zt1, lz1 - (zt1 - z1)); + sqlite3_result_text(context, zo, -1, SQLITE_TRANSIENT); + sqlite3_free(zo); +} +#endif + +/* +** given a string returns the same string but with the characters in reverse +*order +*/ +static void +reverseFunc(sqlite3_context* context, int argc, sqlite3_value** argv) +{ + const char* z; + const char* zt; + char* rz; + char* rzt; + int l = 0; + int i = 0; + + assert(1 == argc); + + if (SQLITE_NULL == sqlite3_value_type(argv[0])) { + sqlite3_result_null(context); + return; + } + z = (char*) sqlite3_value_text(argv[0]); + l = strlen(z); + rz = (char*) sqlite3_malloc(l + 1); + if (!rz) { + sqlite3_result_error_nomem(context); + return; + } + rzt = rz + l; + *(rzt--) = '\0'; + + zt = z; + while (sqliteCharVal((unsigned char*) zt) != 0) { + z = zt; + sqliteNextChar(zt); + for (i = 1; zt - i >= z; ++i) { + *(rzt--) = *(zt - i); + } + } + + sqlite3_result_text(context, rz, -1, SQLITE_TRANSIENT); + sqlite3_free(rz); +} + +/* +** An instance of the following structure holds the context of a +** stdev() or variance() aggregate computation. +** implementaion of +*http://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Algorithm_II +** less prone to rounding errors +*/ +typedef struct StdevCtx StdevCtx; +struct StdevCtx { + double rM; + double rS; + i64 cnt; /* number of elements */ +}; + +/* +** An instance of the following structure holds the context of a +** mode() or median() aggregate computation. +** Depends on structures defined in map.c (see map & map) +** These aggregate functions only work for integers and floats although +** they could be made to work for strings. This is usually considered +*meaningless. +** Only usuall order (for median), no use of collation functions (would this +*even make sense?) +*/ +typedef struct ModeCtx ModeCtx; +struct ModeCtx { + i64 riM; /* integer value found so far */ + double rdM; /* double value found so far */ + i64 cnt; /* number of elements so far */ + double pcnt; /* number of elements smaller than a percentile */ + i64 mcnt; /* maximum number of occurrences (for mode) */ + i64 mn; /* number of occurrences (for mode and percentiles) */ + i64 is_double; /* whether the computation is being done for doubles (>0) or + integers (=0) */ + map* m; /* map structure used for the computation */ + int done; /* whether the answer has been found */ +}; + +/* +** called for each value received during a calculation of stdev or variance +*/ +static void +varianceStep(sqlite3_context* context, int argc, sqlite3_value** argv) +{ + StdevCtx* p; + + double delta; + double x; + + assert(argc == 1); + p = (StdevCtx*) sqlite3_aggregate_context(context, sizeof(*p)); + /* only consider non-null values */ + if (SQLITE_NULL != sqlite3_value_numeric_type(argv[0])) { + p->cnt++; + x = sqlite3_value_double(argv[0]); + delta = (x - p->rM); + p->rM += delta / p->cnt; + p->rS += delta * (x - p->rM); + } +} + +/* +** called for each value received during a calculation of mode of median +*/ +static void +modeStep(sqlite3_context* context, int argc, sqlite3_value** argv) +{ + ModeCtx* p; + i64 xi = 0; + double xd = 0.0; + i64* iptr; + double* dptr; + int type; + + assert(argc == 1); + type = sqlite3_value_numeric_type(argv[0]); + + if (type == SQLITE_NULL) + return; + + p = (ModeCtx*) sqlite3_aggregate_context(context, sizeof(*p)); + + if (0 == (p->m)) { + p->m = (map*) calloc(1, sizeof(map)); + if (type == SQLITE_INTEGER) { + /* map will be used for integers */ + *(p->m) = map_make(int_cmp); + p->is_double = 0; + } else { + p->is_double = 1; + /* map will be used for doubles */ + *(p->m) = map_make(double_cmp); + } + } + + ++(p->cnt); + + if (0 == p->is_double) { + xi = sqlite3_value_int64(argv[0]); + iptr = (i64*) calloc(1, sizeof(i64)); + *iptr = xi; + map_insert(p->m, iptr); + } else { + xd = sqlite3_value_double(argv[0]); + dptr = (double*) calloc(1, sizeof(double)); + *dptr = xd; + map_insert(p->m, dptr); + } +} + +/* +** Auxiliary function that iterates all elements in a map and finds the mode +** (most frequent value) +*/ +static void +modeIterate(void* e, i64 c, void* pp) +{ + i64 ei; + double ed; + ModeCtx* p = (ModeCtx*) pp; + + if (0 == p->is_double) { + ei = *(int*) (e); + + if (p->mcnt == c) { + ++p->mn; + } else if (p->mcnt < c) { + p->riM = ei; + p->mcnt = c; + p->mn = 1; + } + } else { + ed = *(double*) (e); + + if (p->mcnt == c) { + ++p->mn; + } else if (p->mcnt < c) { + p->rdM = ed; + p->mcnt = c; + p->mn = 1; + } + } +} + +/* +** Auxiliary function that iterates all elements in a map and finds the median +** (the value such that the number of elements smaller is equal the number of +** elements larger) +*/ +static void +medianIterate(void* e, i64 c, void* pp) +{ + i64 ei; + double ed; + double iL; + double iR; + int il; + int ir; + ModeCtx* p = (ModeCtx*) pp; + + if (p->done > 0) + return; + + iL = p->pcnt; + iR = p->cnt - p->pcnt; + il = p->mcnt + c; + ir = p->cnt - p->mcnt; + + if (il >= iL) { + if (ir >= iR) { + ++p->mn; + if (0 == p->is_double) { + ei = *(int*) (e); + p->riM += ei; + } else { + ed = *(double*) (e); + p->rdM += ed; + } + } else { + p->done = 1; + } + } + p->mcnt += c; +} + +/* +** Returns the mode value +*/ +static void +modeFinalize(sqlite3_context* context) +{ + ModeCtx* p; + p = (ModeCtx*) sqlite3_aggregate_context(context, 0); + if (p && p->m) { + map_iterate(p->m, modeIterate, p); + map_destroy(p->m); + free(p->m); + + if (1 == p->mn) { + if (0 == p->is_double) + sqlite3_result_int64(context, p->riM); + else + sqlite3_result_double(context, p->rdM); + } + } +} + +/* +** auxiliary function for percentiles +*/ +static void +_medianFinalize(sqlite3_context* context) +{ + ModeCtx* p; + p = (ModeCtx*) sqlite3_aggregate_context(context, 0); + if (p && p->m) { + p->done = 0; + map_iterate(p->m, medianIterate, p); + map_destroy(p->m); + free(p->m); + + if (0 == p->is_double) + if (1 == p->mn) + sqlite3_result_int64(context, p->riM); + else + sqlite3_result_double(context, p->riM * 1.0 / p->mn); + else + sqlite3_result_double(context, p->rdM / p->mn); + } +} + +/* +** Returns the median value +*/ +static void +medianFinalize(sqlite3_context* context) +{ + ModeCtx* p; + p = (ModeCtx*) sqlite3_aggregate_context(context, 0); + if (p != 0) { + p->pcnt = (p->cnt) / 2.0; + _medianFinalize(context); + } +} + +/* +** Returns the lower_quartile value +*/ +static void +lower_quartileFinalize(sqlite3_context* context) +{ + ModeCtx* p; + p = (ModeCtx*) sqlite3_aggregate_context(context, 0); + if (p != 0) { + p->pcnt = (p->cnt) / 4.0; + _medianFinalize(context); + } +} + +/* +** Returns the upper_quartile value +*/ +static void +upper_quartileFinalize(sqlite3_context* context) +{ + ModeCtx* p; + p = (ModeCtx*) sqlite3_aggregate_context(context, 0); + if (p != 0) { + p->pcnt = (p->cnt) * 3 / 4.0; + _medianFinalize(context); + } +} + +/* +** Returns the stdev value +*/ +static void +stdevFinalize(sqlite3_context* context) +{ + StdevCtx* p; + p = (StdevCtx*) sqlite3_aggregate_context(context, 0); + if (p && p->cnt > 1) { + sqlite3_result_double(context, sqrt(p->rS / (p->cnt - 1))); + } else { + sqlite3_result_double(context, 0.0); + } +} + +/* +** Returns the variance value +*/ +static void +varianceFinalize(sqlite3_context* context) +{ + StdevCtx* p; + p = (StdevCtx*) sqlite3_aggregate_context(context, 0); + if (p && p->cnt > 1) { + sqlite3_result_double(context, p->rS / (p->cnt - 1)); + } else { + sqlite3_result_double(context, 0.0); + } +} + +#ifdef SQLITE_SOUNDEX + +/* relicoder factored code */ +/* +** Calculates the soundex value of a string +*/ + +static void +soundex(const u8* zIn, char* zResult) +{ + int i, j; + static const unsigned char iCode[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 2, 3, 0, 1, 2, 0, 0, 2, 2, 4, 5, 5, 0, 1, 2, 6, 2, 3, 0, 1, 0, + 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 0, 1, 2, 0, 0, 2, 2, 4, 5, + 5, 0, 1, 2, 6, 2, 3, 0, 1, 0, 2, 0, 2, 0, 0, 0, 0, 0, + }; + + for (i = 0; zIn[i] && !isalpha(zIn[i]); i++) { + } + if (zIn[i]) { + zResult[0] = toupper(zIn[i]); + for (j = 1; j < 4 && zIn[i]; i++) { + int code = iCode[zIn[i] & 0x7f]; + if (code > 0) { + zResult[j++] = code + '0'; + } + } + while (j < 4) { + zResult[j++] = '0'; + } + zResult[j] = 0; + } else { + strcpy(zResult, "?000"); + } +} + +/* +** computes the number of different characters between the soundex value fo 2 +*strings +*/ +static void +differenceFunc(sqlite3_context* context, int argc, sqlite3_value** argv) +{ + char zResult1[8]; + char zResult2[8]; + char* zR1 = zResult1; + char* zR2 = zResult2; + int rVal = 0; + int i = 0; + const u8* zIn1; + const u8* zIn2; + + assert(argc == 2); + + if (sqlite3_value_type(argv[0]) == SQLITE_NULL + || sqlite3_value_type(argv[1]) == SQLITE_NULL) + { + sqlite3_result_null(context); + return; + } + + zIn1 = (u8*) sqlite3_value_text(argv[0]); + zIn2 = (u8*) sqlite3_value_text(argv[1]); + + soundex(zIn1, zR1); + soundex(zIn2, zR2); + + for (i = 0; i < 4; ++i) { + if (sqliteCharVal((unsigned char*) zR1) + == sqliteCharVal((unsigned char*) zR2)) + ++rVal; + sqliteNextChar(zR1); + sqliteNextChar(zR2); + } + sqlite3_result_int(context, rVal); +} +#endif + +/* +** This function registered all of the above C functions as SQL +** functions. This should be the only routine in this file with +** external linkage. +*/ +int +common_extension_functions(struct FuncDef** basic_funcs, + struct FuncDefAgg** agg_funcs) +{ + static struct FuncDef aFuncs[] = { + /* math.h */ + { + "acos", + 1, + SQLITE_UTF8, + 0, + acosFunc, + help_text("acos") + .sql_function() + .with_summary("Returns the arccosine of a number, in radians") + .with_parameter( + {"num", "A cosine value that is between -1 and 1"}) + .with_tags({"math"}) + .with_example( + {"To get the arccosine of 0.2", "SELECT acos(0.2)"}), + }, + { + "asin", + 1, + SQLITE_UTF8, + 0, + asinFunc, + help_text("asin") + .sql_function() + .with_summary("Returns the arcsine of a number, in radians") + .with_parameter( + {"num", "A sine value that is between -1 and 1"}) + .with_tags({"math"}) + .with_example( + {"To get the arcsine of 0.2", "SELECT asin(0.2)"}), + }, + { + "atan", + 1, + SQLITE_UTF8, + 0, + atanFunc, + help_text("atan") + .sql_function() + .with_summary("Returns the arctangent of a number, in radians") + .with_parameter({"num", "The number"}) + .with_tags({"math"}) + .with_example( + {"To get the arctangent of 0.2", "SELECT atan(0.2)"}), + }, + { + "atn2", + 2, + SQLITE_UTF8, + 0, + atn2Func, + help_text("atn2") + .sql_function() + .with_summary("Returns the angle in the plane between the " + "positive X axis " + "and the ray from (0, 0) to the point (x, y)") + .with_parameter({"y", "The y coordinate of the point"}) + .with_parameter({"x", "The x coordinate of the point"}) + .with_tags({"math"}) + .with_example({ + "To get the angle, in degrees, for the point at (5, 5)", + "SELECT degrees(atn2(5, 5))", + }), + }, + /* XXX alias */ + { + "atan2", + 2, + SQLITE_UTF8, + 0, + atn2Func, + help_text("atan2") + .sql_function() + .with_summary("Returns the angle in the plane between the " + "positive X axis " + "and the ray from (0, 0) to the point (x, y)") + .with_parameter({"y", "The y coordinate of the point"}) + .with_parameter({"x", "The x coordinate of the point"}) + .with_tags({"math"}) + .with_example({ + "To get the angle, in degrees, for the point at (5, 5)", + "SELECT degrees(atan2(5, 5))", + }), + }, + { + "acosh", + 1, + SQLITE_UTF8, + 0, + acoshFunc, + help_text("acosh") + .sql_function() + .with_summary("Returns the hyperbolic arccosine of a number") + .with_parameter({"num", "A number that is one or more"}) + .with_tags({"math"}) + .with_example({ + "To get the hyperbolic arccosine of 1.2", + "SELECT acosh(1.2)", + }), + }, + { + "asinh", + 1, + SQLITE_UTF8, + 0, + asinhFunc, + help_text("asinh") + .sql_function() + .with_summary("Returns the hyperbolic arcsine of a number") + .with_parameter({"num", "The number"}) + .with_tags({"math"}) + .with_example({ + "To get the hyperbolic arcsine of 0.2", + "SELECT asinh(0.2)", + }), + }, + { + "atanh", + 1, + SQLITE_UTF8, + 0, + atanhFunc, + help_text("atanh") + .sql_function() + .with_summary("Returns the hyperbolic arctangent of a number") + .with_parameter({"num", "The number"}) + .with_tags({"math"}) + .with_example({ + "To get the hyperbolic arctangent of 0.2", + "SELECT atanh(0.2)", + }), + }, + + {"difference", 2, SQLITE_UTF8, 0, differenceFunc}, + { + "degrees", + 1, + SQLITE_UTF8, + 0, + rad2degFunc, + help_text("degrees") + .sql_function() + .with_summary("Converts radians to degrees") + .with_parameter( + {"radians", "The radians value to convert to degrees"}) + .with_tags({"math"}) + .with_example( + {"To convert PI to degrees", "SELECT degrees(pi())"}), + }, + { + "radians", + 1, + SQLITE_UTF8, + 0, + deg2radFunc, + help_text("radians") + .sql_function() + .with_summary("Converts degrees to radians") + .with_parameter( + {"degrees", "The degrees value to convert to radians"}) + .with_tags({"math"}) + .with_example({ + "To convert 180 degrees to radians", + "SELECT radians(180)", + }), + }, + + {"cos", 1, SQLITE_UTF8, 0, cosFunc}, + {"sin", 1, SQLITE_UTF8, 0, sinFunc}, + {"tan", 1, SQLITE_UTF8, 0, tanFunc}, + {"cot", 1, SQLITE_UTF8, 0, cotFunc}, + {"cosh", 1, SQLITE_UTF8, 0, coshFunc}, + {"sinh", 1, SQLITE_UTF8, 0, sinhFunc}, + {"tanh", 1, SQLITE_UTF8, 0, tanhFunc}, + {"coth", 1, SQLITE_UTF8, 0, cothFunc}, + + { + "exp", + 1, + SQLITE_UTF8, + 0, + expFunc, + help_text("exp") + .sql_function() + .with_summary("Returns the value of e raised to the power of x") + .with_parameter({"x", "The exponent"}) + .with_tags({"math"}) + .with_example({"To raise e to 2", "SELECT exp(2)"}), + }, + { + "log", + 1, + SQLITE_UTF8, + 0, + logFunc, + help_text("log") + .sql_function() + .with_summary("Returns the natural logarithm of x") + .with_parameter({"x", "The number"}) + .with_tags({"math"}) + .with_example( + {"To get the natual logarithm of 8", "SELECT log(8)"}), + }, + { + "log10", + 1, + SQLITE_UTF8, + 0, + log10Func, + help_text("log10") + .sql_function() + .with_summary("Returns the base-10 logarithm of X") + .with_parameter({"x", "The number"}) + .with_tags({"math"}) + .with_example( + {"To get the logarithm of 100", "SELECT log10(100)"}), + }, + { + "power", + 2, + SQLITE_UTF8, + 0, + powerFunc, + help_text("power") + .sql_function() + .with_summary("Returns the base to the given exponent") + .with_parameter({"base", "The base number"}) + .with_parameter({"exp", "The exponent"}) + .with_tags({"math"}) + .with_example({ + "To raise two to the power of three", + "SELECT power(2, 3)", + }), + }, + { + "sign", + 1, + SQLITE_UTF8, + 0, + signFunc, + help_text("sign") + .sql_function() + .with_summary( + "Returns the sign of the given number as -1, 0, or 1") + .with_parameter({"num", "The number"}) + .with_tags({"math"}) + .with_example({"To get the sign of 10", "SELECT sign(10)"}) + .with_example({"To get the sign of 0", "SELECT sign(0)"}) + .with_example({"To get the sign of -10", "SELECT sign(-10)"}), + }, + {"sqrt", 1, SQLITE_UTF8, 0, sqrtFunc}, + { + "square", + 1, + SQLITE_UTF8, + 0, + squareFunc, + help_text("square") + .sql_function() + .with_summary("Returns the square of the argument") + .with_parameter({"num", "The number to square"}) + .with_tags({"math"}) + .with_example({"To get the square of two", "SELECT square(2)"}), + }, + + { + "ceil", + 1, + SQLITE_UTF8, + 0, + ceilFunc, + help_text("ceil") + .sql_function() + .with_summary( + "Returns the smallest integer that is not less than " + "the argument") + .with_parameter({"num", "The number to raise to the ceiling"}) + .with_tags({"math"}) + .with_example( + {"To get the ceiling of 1.23", "SELECT ceil(1.23)"}), + }, + { + "floor", + 1, + SQLITE_UTF8, + 0, + floorFunc, + help_text("floor") + .sql_function() + .with_summary("Returns the largest integer that is not greater " + "than the argument") + .with_parameter({"num", "The number to lower to the floor"}) + .with_tags({"math"}) + .with_example( + {"To get the floor of 1.23", "SELECT floor(1.23)"}), + }, + + { + "pi", + 0, + SQLITE_UTF8, + 1, + piFunc, + help_text("pi") + .sql_function() + .with_summary("Returns the value of PI") + .with_tags({"math"}) + .with_example({"To get the value of PI", "SELECT pi()"}), + }, + + /* string */ + { + "replicate", + 2, + SQLITE_UTF8, + 0, + replicateFunc, + help_text("replicate") + .sql_function() + .with_summary("Returns the given string concatenated N times.") + .with_parameter({"str", "The string to replicate."}) + .with_parameter( + {"N", "The number of times to replicate the string."}) + .with_tags({"string"}) + .with_example({ + "To repeat the string 'abc' three times", + "SELECT replicate('abc', 3)", + }), + }, + {"charindex", 2, SQLITE_UTF8, 0, charindexFunc}, + { + "charindex", + 3, + SQLITE_UTF8, + 0, + charindexFunc, + help_text("charindex") + .sql_function() + .with_summary("Finds the first occurrence of the needle within " + "the haystack " + "and returns the number of prior characters plus " + "1, or 0 if Y " + "is nowhere found within X") + .with_parameter( + {"needle", "The string to look for in the haystack"}) + .with_parameter({"haystack", "The string to search within"}) + .with_parameter(help_text("start", + "The one-based index within the " + "haystack to start the search") + .optional()) + .with_tags({"string"}) + .with_example({ + "To search for the string 'abc' within 'abcabc' " + "and starting at position 2", + "SELECT charindex('abc', 'abcabc', 2)", + }) + .with_example({ + "To search for the string 'abc' within 'abcdef' " + "and starting at position 2", + "SELECT charindex('abc', 'abcdef', 2)", + }), + }, + { + "leftstr", + 2, + SQLITE_UTF8, + 0, + leftFunc, + help_text("leftstr") + .sql_function() + .with_summary( + "Returns the N leftmost (UTF-8) characters in the " + "given string.") + .with_parameter({"str", "The string to return subset."}) + .with_parameter( + {"N", + "The number of characters from the left side of " + "the string to return."}) + .with_tags({"string"}) + .with_example({ + "To get the first character of the string 'abc'", + "SELECT leftstr('abc', 1)", + }) + .with_example({ + "To get the first ten characters of a string, " + "regardless of size", + "SELECT leftstr('abc', 10)", + }), + }, + { + "rightstr", + 2, + SQLITE_UTF8, + 0, + rightFunc, + help_text("rightstr") + .sql_function() + .with_summary( + "Returns the N rightmost (UTF-8) characters in the " + "given string.") + .with_parameter({"str", "The string to return subset."}) + .with_parameter( + {"N", + "The number of characters from the right side of " + "the string to return."}) + .with_tags({"string"}) + .with_example({ + "To get the last character of the string 'abc'", + "SELECT rightstr('abc', 1)", + }) + .with_example({ + "To get the last ten characters of a string, " + "regardless of size", + "SELECT rightstr('abc', 10)", + }), + }, +#ifndef HAVE_TRIM + {"ltrim", 1, SQLITE_UTF8, 0, ltrimFunc}, + {"rtrim", 1, SQLITE_UTF8, 0, rtrimFunc}, + {"trim", 1, SQLITE_UTF8, 0, trimFunc}, + {"replace", 3, SQLITE_UTF8, 0, replaceFunc}, +#endif + { + "reverse", + 1, + SQLITE_UTF8, + 0, + reverseFunc, + help_text("reverse") + .sql_function() + .with_summary("Returns the reverse of the given string.") + .with_parameter({"str", "The string to reverse."}) + .with_tags({"string"}) + .with_example( + {"To reverse the string 'abc'", "SELECT reverse('abc')"}), + }, + { + "proper", + 1, + SQLITE_UTF8, + 0, + properFunc, + help_text("proper") + .sql_function() + .with_summary("Capitalize the first character of words in the " + "given string") + .with_parameter({"str", "The string to capitalize."}) + .with_tags({"string"}) + .with_example({ + "To capitalize the words in the string 'hello, world!'", + "SELECT proper('hello, world!')", + }), + }, + { + "padl", + 2, + SQLITE_UTF8, + 0, + padlFunc, + help_text("padl") + .sql_function() + .with_summary( + "Pad the given string with leading spaces until it " + "reaches the desired length") + .with_parameter({"str", "The string to pad"}) + .with_parameter( + {"len", "The minimum desired length of the output string"}) + .with_tags({"string"}) + .with_example( + {"To pad the string 'abc' to a length of six characters", + "SELECT padl('abc', 6)"}) + .with_example({ + "To pad the string 'abcdef' to a length of four " + "characters", + "SELECT padl('abcdef', 4)", + }), + }, + { + "padr", + 2, + SQLITE_UTF8, + 0, + padrFunc, + help_text("padr") + .sql_function() + .with_summary( + "Pad the given string with trailing spaces until it " + "reaches the desired length") + .with_parameter({"str", "The string to pad"}) + .with_parameter( + {"len", "The minimum desired length of the output string"}) + .with_tags({"string"}) + .with_example( + {"To pad the string 'abc' to a length of six characters", + "SELECT padr('abc', 6) || 'def'"}) + .with_example({ + "To pad the string 'abcdef' to a length of four characters", + "SELECT padr('abcdef', 4) || 'ghi'", + }), + }, + { + "padc", + 2, + SQLITE_UTF8, + 0, + padcFunc, + help_text("padc") + .sql_function() + .with_summary( + "Pad the given string with enough spaces to make it " + "centered within the given length") + .with_parameter({"str", "The string to pad"}) + .with_parameter( + {"len", "The minimum desired length of the output string"}) + .with_tags({"string"}) + .with_example( + {"To pad the string 'abc' to a length of six characters", + "SELECT padc('abc', 6) || 'def'"}) + .with_example({ + "To pad the string 'abcdef' to a length of " + "eight characters", + "SELECT padc('abcdef', 8) || 'ghi'", + }), + }, + { + "strfilter", + 2, + SQLITE_UTF8, + 0, + strfilterFunc, + help_text("strfilter") + .sql_function() + .with_summary( + "Returns the source string with only the characters " + "given in the second parameter") + .with_parameter({"source", "The string to filter"}) + .with_parameter( + {"include", "The characters to include in the result"}) + .with_tags({"string"}) + .with_example({ + "To get the 'b', 'c', and 'd' characters from the " + "string 'abcabc'", + "SELECT strfilter('abcabc', 'bcd')", + }), + }, + + {nullptr}, + }; + + /* Aggregate functions */ + static struct FuncDefAgg aAggs[] = { + {"stdev", 1, 0, varianceStep, stdevFinalize}, + {"stddev", 1, 0, varianceStep, stdevFinalize}, + {"variance", 1, 0, varianceStep, varianceFinalize}, + {"mode", 1, 0, modeStep, modeFinalize}, + {"median", 1, 0, modeStep, medianFinalize}, + {"lower_quartile", 1, 0, modeStep, lower_quartileFinalize}, + {"upper_quartile", 1, 0, modeStep, upper_quartileFinalize}, + + {nullptr}, + }; + + *basic_funcs = aFuncs; + *agg_funcs = aAggs; + + return SQLITE_OK; +} + +#ifdef COMPILE_SQLITE_EXTENSIONS_AS_LOADABLE_MODULE +int +sqlite3_extension_init(sqlite3* db, + char** pzErrMsg, + const sqlite3_api_routines* pApi) +{ + SQLITE_EXTENSION_INIT2(pApi); + RegisterExtensionFunctions(db); + return 0; +} +#endif /* COMPILE_SQLITE_EXTENSIONS_AS_LOADABLE_MODULE */ + +map +map_make(cmp_func cmp) +{ + map r; + r.cmp = cmp; + r.base = 0; + r.free = 0; + + return r; +} + +void* +xcalloc(size_t nmemb, size_t size, const char* s) +{ + void* ret = calloc(nmemb, size); + return ret; +} + +static void +xfree(void* p) +{ + free(p); +} + +void +node_insert(node** n, cmp_func cmp, void* e) +{ + int c; + node* nn; + if (*n == 0) { + nn = (node*) xcalloc(1, sizeof(node), "for node"); + nn->data = e; + nn->count = 1; + *n = nn; + } else { + c = cmp((*n)->data, e); + if (0 == c) { + ++((*n)->count); + xfree(e); + } else if (c > 0) { + /* put it right here */ + node_insert(&((*n)->l), cmp, e); + } else { + node_insert(&((*n)->r), cmp, e); + } + } +} + +void +map_insert(map* m, void* e) +{ + node_insert(&(m->base), m->cmp, e); +} + +void +node_iterate(node* n, map_iterator iter, void* p) +{ + if (n) { + if (n->l) + node_iterate(n->l, iter, p); + iter(n->data, n->count, p); + if (n->r) + node_iterate(n->r, iter, p); + } +} + +void +map_iterate(map* m, map_iterator iter, void* p) +{ + node_iterate(m->base, iter, p); +} + +void +node_destroy(node* n) +{ + if (0 != n) { + xfree(n->data); + if (n->l) + node_destroy(n->l); + if (n->r) + node_destroy(n->r); + + xfree(n); + } +} + +void +map_destroy(map* m) +{ + node_destroy(m->base); +} + +int +int_cmp(const void* a, const void* b) +{ + int64_t aa = *(int64_t*) (a); + int64_t bb = *(int64_t*) (b); + /* printf("cmp %d <=> %d\n",aa,bb); */ + if (aa == bb) + return 0; + else if (aa < bb) + return -1; + else + return 1; +} + +int +double_cmp(const void* a, const void* b) +{ + double aa = *(double*) (a); + double bb = *(double*) (b); + /* printf("cmp %d <=> %d\n",aa,bb); */ + if (aa == bb) + return 0; + else if (aa < bb) + return -1; + else + return 1; +} + +void +print_elem(void* e, int64_t c, void* p) +{ + int ee = *(int*) (e); + printf("%d => %" PRId64 "\n", ee, c); +} diff --git a/src/field_overlay_source.cc b/src/field_overlay_source.cc new file mode 100644 index 0000000..288d073 --- /dev/null +++ b/src/field_overlay_source.cc @@ -0,0 +1,574 @@ +/** + * Copyright (c) 2015, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "field_overlay_source.hh" + +#include "base/ansi_scrubber.hh" +#include "base/humanize.time.hh" +#include "base/snippet_highlighters.hh" +#include "config.h" +#include "log_format_ext.hh" +#include "log_vtab_impl.hh" +#include "md2attr_line.hh" +#include "readline_highlighters.hh" +#include "relative_time.hh" +#include "vtab_module.hh" +#include "vtab_module_json.hh" + +json_string extract(const char* str); + +void +field_overlay_source::build_field_lines(const listview_curses& lv) +{ + auto& lss = this->fos_lss; + auto& vc = view_colors::singleton(); + + this->fos_lines.clear(); + + if (lss.text_line_count() == 0) { + this->fos_log_helper.clear(); + + return; + } + + content_line_t cl = lss.at(lv.get_top()); + std::shared_ptr file = lss.find(cl); + auto ll = file->begin() + cl; + auto format = file->get_format(); + bool display = false; + + if (ll->is_time_skewed() + || ll->get_msg_level() == log_level_t::LEVEL_INVALID) + { + display = true; + } + if (!this->fos_contexts.empty()) { + display = display || this->fos_contexts.top().c_show; + } + + this->build_meta_line(lv, this->fos_lines, lv.get_top()); + + if (!display) { + return; + } + + if (!this->fos_log_helper.parse_line(lv.get_top())) { + return; + } + + if (ll->get_msg_level() == LEVEL_INVALID) { + for (const auto& sattr : this->fos_log_helper.ldh_line_attrs) { + if (sattr.sa_type != &SA_INVALID) { + continue; + } + + auto emsg = fmt::format( + FMT_STRING(" Invalid log message: {}"), + sattr.sa_value.get()); + auto al = attr_line_t(emsg) + .with_attr(string_attr( + line_range{1, 2}, VC_GRAPHIC.value(ACS_LLCORNER))) + .with_attr(string_attr( + line_range{0, 22}, + VC_ROLE.value(role_t::VCR_INVALID_MSG))); + this->fos_lines.emplace_back(al); + } + } + + char old_timestamp[64], curr_timestamp[64], orig_timestamp[64]; + struct timeval curr_tv, offset_tv, orig_tv, diff_tv = {0, 0}; + attr_line_t time_line; + auto& time_str = time_line.get_string(); + struct line_range time_lr; + + sql_strftime(curr_timestamp, + sizeof(curr_timestamp), + ll->get_time(), + ll->get_millis(), + 'T'); + + if (ll->is_time_skewed()) { + time_lr.lr_start = 1; + time_lr.lr_end = 2; + time_line.with_attr( + string_attr(time_lr, VC_GRAPHIC.value(ACS_LLCORNER))); + time_str.append(" Out-Of-Time-Order Message"); + time_lr.lr_start = 3; + time_lr.lr_end = time_str.length(); + time_line.with_attr( + string_attr(time_lr, VC_ROLE.value(role_t::VCR_SKEWED_TIME))); + time_str.append(" --"); + } + + time_str.append(" Received Time: "); + time_lr.lr_start = time_str.length(); + time_str.append(curr_timestamp); + time_lr.lr_end = time_str.length(); + time_line.with_attr( + string_attr(time_lr, VC_STYLE.value(text_attrs{A_BOLD}))); + time_str.append(" -- "); + time_lr.lr_start = time_str.length(); + time_str.append(humanize::time::point::from_tv(ll->get_timeval()) + .with_convert_to_local(true) + .as_precise_time_ago()); + time_lr.lr_end = time_str.length(); + time_line.with_attr( + string_attr(time_lr, VC_STYLE.value(text_attrs{A_BOLD}))); + + struct line_range time_range = find_string_attr_range( + this->fos_log_helper.ldh_line_attrs, &logline::L_TIMESTAMP); + + curr_tv = this->fos_log_helper.ldh_line->get_timeval(); + if (ll->is_time_skewed() && time_range.lr_end != -1) { + const char* time_src + = this->fos_log_helper.ldh_line_values.lvv_sbr.get_data() + + time_range.lr_start; + struct timeval actual_tv; + date_time_scanner dts; + struct exttm tm; + + dts.set_base_time(format->lf_date_time.dts_base_time, + format->lf_date_time.dts_base_tm.et_tm); + if (format->lf_date_time.scan(time_src, + time_range.length(), + format->get_timestamp_formats(), + &tm, + actual_tv, + false) + || dts.scan( + time_src, time_range.length(), nullptr, &tm, actual_tv, false)) + { + sql_strftime( + orig_timestamp, sizeof(orig_timestamp), actual_tv, 'T'); + time_str.append("; Actual Time: "); + time_lr.lr_start = time_str.length(); + time_str.append(orig_timestamp); + time_lr.lr_end = time_str.length(); + time_line.with_attr( + string_attr(time_lr, VC_ROLE.value(role_t::VCR_SKEWED_TIME))); + + timersub(&curr_tv, &actual_tv, &diff_tv); + time_str.append("; Diff: "); + time_lr.lr_start = time_str.length(); + time_str.append( + humanize::time::duration::from_tv(diff_tv).to_string()); + time_lr.lr_end = time_str.length(); + time_line.with_attr( + string_attr(time_lr, VC_STYLE.value(text_attrs{A_BOLD}))); + } + } + + offset_tv = this->fos_log_helper.ldh_file->get_time_offset(); + timersub(&curr_tv, &offset_tv, &orig_tv); + sql_strftime(old_timestamp, + sizeof(old_timestamp), + orig_tv.tv_sec, + orig_tv.tv_usec / 1000, + 'T'); + if (offset_tv.tv_sec || offset_tv.tv_usec) { + time_str.append(" Pre-adjust Time: "); + time_str.append(old_timestamp); + fmt::format_to(std::back_inserter(time_str), + FMT_STRING(" Offset: {:+}.{:03}"), + offset_tv.tv_sec, + std::chrono::duration_cast( + std::chrono::microseconds(offset_tv.tv_usec)) + .count()); + } + + if (format->lf_date_time.dts_fmt_lock != -1) { + const auto* ts_formats = format->get_timestamp_formats(); + if (ts_formats == nullptr) { + ts_formats = PTIMEC_FORMAT_STR; + } + time_line.append(" Format: ") + .append(lnav::roles::symbol( + ts_formats[format->lf_date_time.dts_fmt_lock])); + } + + if ((!this->fos_contexts.empty() && this->fos_contexts.top().c_show) + || diff_tv.tv_sec > 0) + { + this->fos_lines.emplace_back(time_line); + } + + if (this->fos_contexts.empty() || !this->fos_contexts.top().c_show) { + return; + } + + this->fos_known_key_size = LOG_BODY.length(); + if (!this->fos_contexts.empty()) { + this->fos_known_key_size += this->fos_contexts.top().c_prefix.length(); + } + this->fos_unknown_key_size = 0; + + for (auto& ldh_line_value : this->fos_log_helper.ldh_line_values.lvv_values) + { + auto& meta = ldh_line_value.lv_meta; + int this_key_size = meta.lvm_name.size(); + + if (!this->fos_contexts.empty()) { + this_key_size += this->fos_contexts.top().c_prefix.length(); + } + if (meta.lvm_kind == value_kind_t::VALUE_STRUCT) { + this_key_size += 9; + } + if (!meta.lvm_struct_name.empty()) { + this_key_size += meta.lvm_struct_name.size() + 11; + } + this->fos_known_key_size + = std::max(this->fos_known_key_size, this_key_size); + } + + for (auto iter = this->fos_log_helper.ldh_parser->dp_pairs.begin(); + iter != this->fos_log_helper.ldh_parser->dp_pairs.end(); + ++iter) + { + std::string colname + = this->fos_log_helper.ldh_parser->get_element_string( + iter->e_sub_elements->front()); + + colname + = this->fos_log_helper.ldh_namer->add_column(colname).to_string(); + this->fos_unknown_key_size + = std::max(this->fos_unknown_key_size, (int) colname.length()); + } + + auto lf = this->fos_log_helper.ldh_file->get_format(); + if (!lf->get_pattern_regex(cl).empty()) { + attr_line_t pattern_al; + std::string& pattern_str = pattern_al.get_string(); + pattern_str = " Pattern: " + lf->get_pattern_path(cl) + " = "; + int skip = pattern_str.length(); + pattern_str += lf->get_pattern_regex(cl); + lnav::snippets::regex_highlighter( + pattern_al, + pattern_al.length(), + line_range{skip, (int) pattern_al.length()}); + this->fos_lines.emplace_back(pattern_al); + } + + if (this->fos_log_helper.ldh_line_values.lvv_values.empty()) { + this->fos_lines.emplace_back(" No known message fields"); + } + + const log_format* last_format = nullptr; + + for (auto& lv : this->fos_log_helper.ldh_line_values.lvv_values) { + if (!lv.lv_meta.lvm_format) { + continue; + } + + auto* curr_format = lv.lv_meta.lvm_format.value(); + auto* curr_elf = dynamic_cast(curr_format); + const auto format_name = curr_format->get_name().to_string(); + attr_line_t al; + std::string str, value_str = lv.to_string(); + + if (curr_format != last_format) { + this->fos_lines.emplace_back(" Known message fields for table " + + format_name + ":"); + this->fos_lines.back().with_attr( + string_attr(line_range(32, 32 + format_name.length()), + VC_STYLE.value(vc.attrs_for_ident(format_name) + | text_attrs{A_BOLD}))); + last_format = curr_format; + } + + std::string field_name, orig_field_name; + if (lv.lv_meta.lvm_struct_name.empty()) { + if (curr_elf && curr_elf->elf_body_field == lv.lv_meta.lvm_name) { + field_name = LOG_BODY; + } else if (curr_elf + && curr_elf->lf_timestamp_field == lv.lv_meta.lvm_name) + { + field_name = LOG_TIME; + } else { + field_name = lv.lv_meta.lvm_name.to_string(); + } + orig_field_name = field_name; + if (!this->fos_contexts.empty()) { + field_name = this->fos_contexts.top().c_prefix + field_name; + } + str = " " + field_name; + } else { + auto_mem jgetter; + + jgetter = sqlite3_mprintf(" jget(%s, '/%q')", + lv.lv_meta.lvm_struct_name.get(), + lv.lv_meta.lvm_name.get()); + str = jgetter; + } + str.append(this->fos_known_key_size - (str.length() - 3), ' '); + str += " = " + value_str; + + al.with_string(str); + if (lv.lv_meta.lvm_struct_name.empty()) { + auto prefix_len = field_name.length() - orig_field_name.length(); + al.with_attr(string_attr( + line_range(3 + prefix_len, 3 + prefix_len + field_name.size()), + VC_STYLE.value(vc.attrs_for_ident(orig_field_name)))); + } else { + al.with_attr(string_attr( + line_range(8, 8 + lv.lv_meta.lvm_struct_name.size()), + VC_STYLE.value( + vc.attrs_for_ident(lv.lv_meta.lvm_struct_name)))); + } + + this->fos_lines.emplace_back(al); + this->add_key_line_attrs(this->fos_known_key_size); + + if (lv.lv_meta.lvm_kind == value_kind_t::VALUE_STRUCT) { + json_string js = extract(value_str.c_str()); + + al.clear() + .append(" extract(") + .append(lv.lv_meta.lvm_name.get(), + VC_STYLE.value(vc.attrs_for_ident(lv.lv_meta.lvm_name))) + .append(")") + .append(this->fos_known_key_size - lv.lv_meta.lvm_name.size() + - 9 + 3, + ' ') + .append(" = ") + .append( + string_fragment::from_bytes(js.js_content.in(), js.js_len)); + this->fos_lines.emplace_back(al); + this->add_key_line_attrs(this->fos_known_key_size); + } + } + + std::map::iterator + json_iter; + + if (!this->fos_log_helper.ldh_json_pairs.empty()) { + this->fos_lines.emplace_back(" JSON fields:"); + } + + for (json_iter = this->fos_log_helper.ldh_json_pairs.begin(); + json_iter != this->fos_log_helper.ldh_json_pairs.end(); + ++json_iter) + { + json_ptr_walk::walk_list_t& jpairs = json_iter->second; + + for (size_t lpc = 0; lpc < jpairs.size(); lpc++) { + this->fos_lines.emplace_back( + " " + + this->fos_log_helper.format_json_getter(json_iter->first, lpc) + + " = " + jpairs[lpc].wt_value); + this->add_key_line_attrs(0); + } + } + + if (!this->fos_log_helper.ldh_xml_pairs.empty()) { + this->fos_lines.emplace_back(" XML fields:"); + } + + for (const auto& xml_pair : this->fos_log_helper.ldh_xml_pairs) { + auto_mem qname; + auto_mem xp_call; + + qname = sql_quote_ident(xml_pair.first.first.get()); + xp_call = sqlite3_mprintf( + "xpath(%Q, %s)", xml_pair.first.second.c_str(), qname.in()); + this->fos_lines.emplace_back( + fmt::format(FMT_STRING(" {} = {}"), xp_call, xml_pair.second)); + this->add_key_line_attrs(0); + } + + if (!this->fos_contexts.empty() + && !this->fos_contexts.top().c_show_discovered) + { + return; + } + + if (this->fos_log_helper.ldh_parser->dp_pairs.empty()) { + this->fos_lines.emplace_back(" No discovered message fields"); + } else { + this->fos_lines.emplace_back( + " Discovered fields for logline table from message format: "); + this->fos_lines.back().with_attr( + string_attr(line_range(23, 23 + 7), + VC_STYLE.value(vc.attrs_for_ident("logline")))); + auto& al = this->fos_lines.back(); + auto& disc_str = al.get_string(); + + al.with_attr(string_attr(line_range(disc_str.length(), -1), + VC_STYLE.value(text_attrs{A_BOLD}))); + disc_str.append(this->fos_log_helper.ldh_msg_format); + } + + auto iter = this->fos_log_helper.ldh_parser->dp_pairs.begin(); + for (size_t lpc = 0; lpc < this->fos_log_helper.ldh_parser->dp_pairs.size(); + lpc++, ++iter) + { + auto name = this->fos_log_helper.ldh_namer->cn_names[lpc]; + auto val = this->fos_log_helper.ldh_parser->get_element_string( + iter->e_sub_elements->back()); + attr_line_t al(fmt::format(FMT_STRING(" {} = {}"), name, val)); + + al.with_attr( + string_attr(line_range(3, 3 + name.length()), + VC_STYLE.value(vc.attrs_for_ident(name.to_string())))); + + this->fos_lines.emplace_back(al); + this->add_key_line_attrs( + this->fos_unknown_key_size, + lpc == (this->fos_log_helper.ldh_parser->dp_pairs.size() - 1)); + } +} + +void +field_overlay_source::build_meta_line(const listview_curses& lv, + std::vector& dst, + vis_line_t row) +{ + auto line_meta_opt = this->fos_lss.find_bookmark_metadata(row); + + if (!line_meta_opt) { + return; + } + auto& vc = view_colors::singleton(); + const auto& line_meta = *(line_meta_opt.value()); + size_t filename_width = this->fos_lss.get_filename_offset(); + const auto* tc = dynamic_cast(&lv); + + if (!line_meta.bm_comment.empty()) { + const auto* lead = line_meta.bm_tags.empty() ? " \u2514 " : " \u251c "; + md2attr_line mdal; + attr_line_t al; + + auto parse_res = md4cpp::parse(line_meta.bm_comment, mdal); + if (parse_res.isOk()) { + al = parse_res.unwrap(); + } else { + log_error("%d: cannot convert comment to markdown: %s", + (int) row, + parse_res.unwrapErr().c_str()); + al = line_meta.bm_comment; + } + + auto comment_lines = al.rtrim().split_lines(); + for (size_t lpc = 0; lpc < comment_lines.size(); lpc++) { + auto& comment_line = comment_lines[lpc]; + + if (lpc == 0 && comment_line.empty()) { + continue; + } + comment_line.with_attr_for_all(VC_ROLE.value(role_t::VCR_COMMENT)); + comment_line.insert( + 0, lpc == comment_lines.size() - 1 ? lead : " \u2502 "); + comment_line.insert(0, filename_width, ' '); + if (tc != nullptr) { + auto hl = tc->get_highlights(); + auto hl_iter = hl.find({highlight_source_t::PREVIEW, "search"}); + + if (hl_iter != hl.end()) { + hl_iter->second.annotate(comment_line, filename_width); + } + } + + dst.emplace_back(comment_line); + } + } + if (!line_meta.bm_tags.empty()) { + attr_line_t al; + + al.with_string(" \u2514"); + for (const auto& str : line_meta.bm_tags) { + al.append(1, ' ').append(str, + VC_STYLE.value(vc.attrs_for_ident(str))); + } + + if (tc != nullptr) { + const auto& hm = tc->get_highlights(); + auto hl_iter = hm.find({highlight_source_t::PREVIEW, "search"}); + + if (hl_iter != hm.end()) { + hl_iter->second.annotate(al, 2); + } + } + al.insert(0, filename_width, ' '); + if (tc != nullptr) { + auto hl = tc->get_highlights(); + auto hl_iter = hl.find({highlight_source_t::PREVIEW, "search"}); + + if (hl_iter != hl.end()) { + hl_iter->second.annotate(al, filename_width); + } + } + dst.emplace_back(al); + } +} + +void +field_overlay_source::add_key_line_attrs(int key_size, bool last_line) +{ + string_attrs_t& sa = this->fos_lines.back().get_attrs(); + struct line_range lr(1, 2); + int64_t graphic = (int64_t) (last_line ? ACS_LLCORNER : ACS_LTEE); + sa.emplace_back(lr, VC_GRAPHIC.value(graphic)); + + lr.lr_start = 3 + key_size + 3; + lr.lr_end = -1; + sa.emplace_back(lr, VC_STYLE.value(text_attrs{A_BOLD})); +} + +bool +field_overlay_source::list_value_for_overlay(const listview_curses& lv, + int y, + int bottom, + vis_line_t row, + attr_line_t& value_out) +{ + if (y == 0) { + this->build_field_lines(lv); + return false; + } + + if (1 <= y && y <= (int) this->fos_lines.size()) { + value_out = this->fos_lines[y - 1]; + return true; + } + + if (!this->fos_meta_lines.empty() && this->fos_meta_lines_row == row - 1_vl) + { + value_out = this->fos_meta_lines.front(); + this->fos_meta_lines.erase(this->fos_meta_lines.begin()); + + return true; + } + + if (row < lv.get_inner_height()) { + this->fos_meta_lines.clear(); + this->build_meta_line(lv, this->fos_meta_lines, row); + this->fos_meta_lines_row = row; + } + + return false; +} diff --git a/src/field_overlay_source.hh b/src/field_overlay_source.hh new file mode 100644 index 0000000..ab1d17f --- /dev/null +++ b/src/field_overlay_source.hh @@ -0,0 +1,86 @@ +/** + * Copyright (c) 2015, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LNAV_FIELD_OVERLAY_SOURCE_H +#define LNAV_FIELD_OVERLAY_SOURCE_H + +#include +#include + +#include "listview_curses.hh" +#include "log_data_helper.hh" +#include "logfile_sub_source.hh" +#include "textfile_sub_source.hh" + +class field_overlay_source : public list_overlay_source { +public: + explicit field_overlay_source(logfile_sub_source& lss, + textfile_sub_source& tss) + : fos_lss(lss), fos_tss(tss), fos_log_helper(lss) + { + } + + void add_key_line_attrs(int key_size, bool last_line = false); + + bool list_value_for_overlay(const listview_curses& lv, + int y, + int bottom, + vis_line_t row, + attr_line_t& value_out) override; + + void build_field_lines(const listview_curses& lv); + void build_meta_line(const listview_curses& lv, + std::vector& dst, + vis_line_t row); + + struct context { + context(std::string prefix, bool show, bool show_discovered) + : c_prefix(std::move(prefix)), c_show(show), + c_show_discovered(show_discovered) + { + } + + std::string c_prefix; + bool c_show{false}; + bool c_show_discovered{true}; + }; + + bool fos_show_status{true}; + std::stack fos_contexts; + logfile_sub_source& fos_lss; + textfile_sub_source& fos_tss; + log_data_helper fos_log_helper; + int fos_known_key_size{0}; + int fos_unknown_key_size{0}; + std::vector fos_lines; + vis_line_t fos_meta_lines_row{0_vl}; + std::vector fos_meta_lines; +}; + +#endif // LNAV_FIELD_OVERLAY_SOURCE_H diff --git a/src/file_collection.cc b/src/file_collection.cc new file mode 100644 index 0000000..19e71c2 --- /dev/null +++ b/src/file_collection.cc @@ -0,0 +1,649 @@ +/** + * Copyright (c) 2020, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @file file_collection.cc + */ + +#include + +#include "file_collection.hh" + +#include + +#include "base/humanize.network.hh" +#include "base/isc.hh" +#include "base/itertools.hh" +#include "base/opt_util.hh" +#include "base/string_util.hh" +#include "config.h" +#include "lnav_util.hh" +#include "logfile.hh" +#include "pcap_manager.hh" +#include "service_tags.hh" +#include "tailer/tailer.looper.hh" + +static std::mutex REALPATH_CACHE_MUTEX; +static std::unordered_map REALPATH_CACHE; + +child_poll_result_t +child_poller::poll(file_collection& fc) +{ + if (!this->cp_child) { + return child_poll_result_t::FINISHED; + } + + auto poll_res = std::move(this->cp_child.value()).poll(); + this->cp_child = nonstd::nullopt; + return poll_res.match( + [this](auto_pid& alive) { + this->cp_child = std::move(alive); + return child_poll_result_t::ALIVE; + }, + [this, &fc](auto_pid& finished) { + require(this->cp_finalizer); + + this->cp_finalizer(fc, finished); + return child_poll_result_t::FINISHED; + }); +} + +void +file_collection::close_files(const std::vector>& files) +{ + for (const auto& lf : files) { + auto actual_path_opt = lf->get_actual_path(); + + if (actual_path_opt) { + std::lock_guard lg(REALPATH_CACHE_MUTEX); + auto path_str = actual_path_opt.value().string(); + + for (auto iter = REALPATH_CACHE.begin(); + iter != REALPATH_CACHE.end();) + { + if (iter->first == path_str || iter->second == path_str) { + iter = REALPATH_CACHE.erase(iter); + } else { + ++iter; + } + } + } else { + this->fc_file_names.erase(lf->get_filename()); + } + auto file_iter = find(this->fc_files.begin(), this->fc_files.end(), lf); + if (file_iter != this->fc_files.end()) { + this->fc_files.erase(file_iter); + } + } + this->fc_files_generation += 1; + + this->regenerate_unique_file_names(); +} + +void +file_collection::regenerate_unique_file_names() +{ + unique_path_generator upg; + + for (const auto& lf : this->fc_files) { + upg.add_source(lf); + } + + upg.generate(); + + this->fc_largest_path_length = 0; + for (const auto& pair : this->fc_name_to_errors) { + auto path = ghc::filesystem::path(pair.first).filename().string(); + + if (path.length() > this->fc_largest_path_length) { + this->fc_largest_path_length = path.length(); + } + } + for (const auto& lf : this->fc_files) { + const auto& path = lf->get_unique_path(); + + if (path.length() > this->fc_largest_path_length) { + this->fc_largest_path_length = path.length(); + } + } + for (const auto& pair : this->fc_other_files) { + switch (pair.second.ofd_format) { + case file_format_t::UNKNOWN: + case file_format_t::ARCHIVE: + case file_format_t::PCAP: + case file_format_t::SQLITE_DB: { + auto bn = ghc::filesystem::path(pair.first).filename().string(); + if (bn.length() > this->fc_largest_path_length) { + this->fc_largest_path_length = bn.length(); + } + break; + } + case file_format_t::REMOTE: { + if (pair.first.length() > this->fc_largest_path_length) { + this->fc_largest_path_length = pair.first.length(); + } + break; + } + } + } +} + +void +file_collection::merge(file_collection& other) +{ + this->fc_recursive = this->fc_recursive || other.fc_recursive; + this->fc_rotated = this->fc_rotated || other.fc_rotated; + + this->fc_synced_files.insert(other.fc_synced_files.begin(), + other.fc_synced_files.end()); + this->fc_name_to_errors.insert(other.fc_name_to_errors.begin(), + other.fc_name_to_errors.end()); + this->fc_file_names.insert( + std::make_move_iterator(other.fc_file_names.begin()), + std::make_move_iterator(other.fc_file_names.end())); + if (!other.fc_files.empty()) { + for (const auto& lf : other.fc_files) { + this->fc_name_to_errors.erase(lf->get_filename()); + } + this->fc_files.insert( + this->fc_files.end(), other.fc_files.begin(), other.fc_files.end()); + this->fc_files_generation += 1; + } + for (auto& pair : other.fc_renamed_files) { + pair.first->set_filename(pair.second); + } + this->fc_closed_files.insert(other.fc_closed_files.begin(), + other.fc_closed_files.end()); + this->fc_other_files.insert(other.fc_other_files.begin(), + other.fc_other_files.end()); + if (!other.fc_child_pollers.empty()) { + this->fc_child_pollers.insert( + this->fc_child_pollers.begin(), + std::make_move_iterator(other.fc_child_pollers.begin()), + std::make_move_iterator(other.fc_child_pollers.end())); + other.fc_child_pollers.clear(); + } +} + +/** + * Functor used to compare files based on their device and inode number. + */ +struct same_file { + explicit same_file(const struct stat& stat) : sf_stat(stat){}; + + /** + * Compare the given log file against the 'stat' given in the constructor. + * @param lf The log file to compare. + * @return True if the dev/inode values in the stat given in the + * constructor matches the stat in the logfile object. + */ + bool operator()(const std::shared_ptr& lf) const + { + return !lf->is_closed() && this->sf_stat.st_dev == lf->get_stat().st_dev + && this->sf_stat.st_ino == lf->get_stat().st_ino; + } + + const struct stat& sf_stat; +}; + +/** + * Try to load the given file as a log file. If the file has not already been + * loaded, it will be loaded. If the file has already been loaded, the file + * name will be updated. + * + * @param filename The file name to check. + * @param fd An already-opened descriptor for 'filename'. + * @param required Specifies whether or not the file must exist and be valid. + */ +std::future +file_collection::watch_logfile(const std::string& filename, + logfile_open_options& loo, + bool required) +{ + file_collection retval; + struct stat st; + int rc; + + if (this->fc_closed_files.count(filename)) { + return lnav::futures::make_ready_future(std::move(retval)); + } + + if (loo.loo_fd != -1) { + rc = fstat(loo.loo_fd, &st); + if (rc == 0) { + loo.with_stat_for_temp(st); + } + } else if (loo.loo_temp_file) { + memset(&st, 0, sizeof(st)); + st.st_dev = loo.loo_temp_dev; + st.st_ino = loo.loo_temp_ino; + st.st_mode = S_IFREG; + rc = 0; + } else { + rc = stat(filename.c_str(), &st); + } + + if (rc == 0) { + if (S_ISDIR(st.st_mode) && this->fc_recursive) { + std::string wilddir = filename + "/*"; + + if (this->fc_file_names.find(wilddir) == this->fc_file_names.end()) + { + retval.fc_file_names.emplace(wilddir, logfile_open_options()); + } + return lnav::futures::make_ready_future(std::move(retval)); + } + if (!S_ISREG(st.st_mode)) { + if (required) { + rc = -1; + errno = EINVAL; + } else { + return lnav::futures::make_ready_future(std::move(retval)); + } + } + auto err_iter = this->fc_name_to_errors.find(filename); + if (err_iter != this->fc_name_to_errors.end()) { + if (err_iter->second.fei_mtime != st.st_mtime) { + this->fc_name_to_errors.erase(err_iter); + } + } + } + if (rc == -1) { + if (required) { + retval.fc_name_to_errors.emplace(filename, + file_error_info{ + time(nullptr), + std::string(strerror(errno)), + }); + } + return lnav::futures::make_ready_future(std::move(retval)); + } + + if (this->fc_new_stats | lnav::itertools::find_if([&st](const auto& elem) { + return st.st_ino == elem.st_ino && st.st_dev == elem.st_dev; + })) + { + // this file is probably a link that we have already scanned in this + // pass. + return lnav::futures::make_ready_future(std::move(retval)); + } + + this->fc_new_stats.emplace_back(st); + + auto file_iter = std::find_if( + this->fc_files.begin(), this->fc_files.end(), same_file(st)); + + if (file_iter == this->fc_files.end()) { + if (this->fc_other_files.find(filename) != this->fc_other_files.end()) { + return lnav::futures::make_ready_future(std::move(retval)); + } + + require(this->fc_progress.get() != nullptr); + + auto func = [filename, + st, + loo2 = std::move(loo), + prog = this->fc_progress, + errs = this->fc_name_to_errors]() mutable { + file_collection retval; + + if (errs.find(filename) != errs.end()) { + // The file is broken, no reason to try and reopen + return retval; + } + + auto ff = loo2.loo_temp_file ? file_format_t::UNKNOWN + : detect_file_format(filename); + + loo2.loo_file_format = ff; + switch (ff) { + case file_format_t::SQLITE_DB: + retval.fc_other_files[filename].ofd_format = ff; + break; + + case file_format_t::PCAP: { + auto res = pcap_manager::convert(filename); + + if (res.isOk()) { + auto convert_res = res.unwrap(); + + loo2.with_fd(std::move(convert_res.cr_destination)); + retval.fc_child_pollers.emplace_back(child_poller{ + std::move(convert_res.cr_child), + [filename, + st, + error_queue = convert_res.cr_error_queue]( + auto& fc, auto& child) { + if (child.was_normal_exit() + && child.exit_status() == EXIT_SUCCESS) + { + log_info("pcap[%d] exited normally", + child.in()); + return; + } + log_error("pcap[%d] exited with %d", + child.in(), + child.status()); + fc.fc_name_to_errors.emplace( + filename, + file_error_info{ + st.st_mtime, + fmt::format( + FMT_STRING("{}"), + fmt::join(*error_queue, "\n")), + }); + }, + }); + auto open_res = logfile::open(filename, loo2); + if (open_res.isOk()) { + retval.fc_files.push_back(open_res.unwrap()); + } else { + retval.fc_name_to_errors.emplace( + filename, + file_error_info{ + st.st_mtime, + open_res.unwrapErr(), + }); + } + } else { + retval.fc_name_to_errors.emplace(filename, + file_error_info{ + st.st_mtime, + res.unwrapErr(), + }); + } + break; + } + + case file_format_t::ARCHIVE: { + nonstd::optional< + std::list::iterator> + prog_iter_opt; + + if (loo2.loo_source == logfile_name_source::ARCHIVE) { + // Don't try to open nested archives + return retval; + } + + auto res = archive_manager::walk_archive_files( + filename, + [prog, &prog_iter_opt](const auto& path, + const auto total) { + safe::WriteAccess sp(*prog); + + prog_iter_opt | [&sp](auto prog_iter) { + sp->sp_extractions.erase(prog_iter); + }; + auto prog_iter = sp->sp_extractions.emplace( + sp->sp_extractions.begin(), path, total); + prog_iter_opt = prog_iter; + + return &(*prog_iter); + }, + [&filename, &retval](const auto& tmp_path, + const auto& entry) { + auto arc_path = ghc::filesystem::relative( + entry.path(), tmp_path); + auto custom_name = filename / arc_path; + bool is_visible = true; + + if (entry.file_size() == 0) { + log_info("hiding empty archive file: %s", + entry.path().c_str()); + is_visible = false; + } + + log_info("adding file from archive: %s/%s", + filename.c_str(), + entry.path().c_str()); + retval.fc_file_names[entry.path().string()] + .with_filename(custom_name.string()) + .with_source(logfile_name_source::ARCHIVE) + .with_visibility(is_visible) + .with_non_utf_visibility(false) + .with_visible_size_limit(256 * 1024); + }); + if (res.isErr()) { + log_error("archive extraction failed: %s", + res.unwrapErr().c_str()); + retval.clear(); + retval.fc_name_to_errors.emplace(filename, + file_error_info{ + st.st_mtime, + res.unwrapErr(), + }); + } else { + retval.fc_other_files[filename] = ff; + } + { + prog_iter_opt | [&prog](auto prog_iter) { + prog->writeAccess()->sp_extractions.erase( + prog_iter); + }; + } + break; + } + + default: + log_info("loading new file: filename=%s", filename.c_str()); + + auto open_res = logfile::open(filename, loo2); + if (open_res.isOk()) { + retval.fc_files.push_back(open_res.unwrap()); + } else { + retval.fc_name_to_errors.emplace( + filename, + file_error_info{ + st.st_mtime, + open_res.unwrapErr(), + }); + } + break; + } + + return retval; + }; + + return std::async(std::launch::async, std::move(func)); + } + + auto lf = *file_iter; + + if (lf->is_valid_filename() && lf->get_filename() != filename) { + /* The file is already loaded, but has been found under a different + * name. We just need to update the stored file name. + */ + retval.fc_renamed_files.emplace_back(lf, filename); + } + + return lnav::futures::make_ready_future(std::move(retval)); +} + +/** + * Expand a glob pattern and call watch_logfile with the file names that match + * the pattern. + * @param path The glob pattern to expand. + * @param required Passed to watch_logfile. + */ +void +file_collection::expand_filename( + lnav::futures::future_queue& fq, + const std::string& path, + logfile_open_options& loo, + bool required) +{ + static_root_mem gl; + + { + std::lock_guard lg(REALPATH_CACHE_MUTEX); + + if (REALPATH_CACHE.find(path) != REALPATH_CACHE.end()) { + return; + } + } + + if (is_url(path.c_str())) { + return; + } + + if (glob(path.c_str(), GLOB_NOCHECK, nullptr, gl.inout()) == 0) { + int lpc; + + if (gl->gl_pathc == 1 /*&& gl.gl_matchc == 0*/) { + /* It's a pattern that doesn't match any files + * yet, allow it through since we'll load it in + * dynamically. + */ + if (access(gl->gl_pathv[0], F_OK) == -1) { + auto rp_opt = humanize::network::path::from_str(path); + if (rp_opt) { + auto iter = this->fc_other_files.find(path); + auto rp = *rp_opt; + + if (iter != this->fc_other_files.end()) { + return; + } + + file_collection retval; + logfile_open_options_base loo_base{loo}; + + isc::to().send( + [rp, loo_base](auto& tlooper) { + tlooper.add_remote(rp, loo_base); + }); + retval.fc_other_files[path] = file_format_t::REMOTE; + { + this->fc_progress->writeAccess() + ->sp_tailers[fmt::to_string(rp.home())] + .tp_message + = "Initializing..."; + } + + fq.push_back( + lnav::futures::make_ready_future(std::move(retval))); + return; + } + + required = false; + } + } + if (gl->gl_pathc > 1 || strcmp(path.c_str(), gl->gl_pathv[0]) != 0) { + required = false; + } + + std::lock_guard lg(REALPATH_CACHE_MUTEX); + for (lpc = 0; lpc < (int) gl->gl_pathc; lpc++) { + auto path_str = std::string(gl->gl_pathv[lpc]); + auto iter = REALPATH_CACHE.find(path_str); + + if (iter == REALPATH_CACHE.end()) { + auto_mem abspath; + + if ((abspath = realpath(gl->gl_pathv[lpc], nullptr)) == nullptr) + { + auto* errmsg = strerror(errno); + + if (required) { + fprintf(stderr, + "Cannot find file: %s -- %s", + gl->gl_pathv[lpc], + errmsg); + } else if (loo.loo_source != logfile_name_source::REMOTE) { + // XXX The remote code path adds the file name before + // the file exists... not sure checking for that here + // is a good idea (prolly not) + file_collection retval; + + if (gl->gl_pathc == 1) { + retval.fc_name_to_errors.emplace(path, + file_error_info{ + time(nullptr), + errmsg, + }); + } else { + retval.fc_name_to_errors.emplace(path_str, + file_error_info{ + time(nullptr), + errmsg, + }); + } + fq.push_back(lnav::futures::make_ready_future( + std::move(retval))); + } + continue; + } + + auto p = REALPATH_CACHE.emplace(path_str, abspath.in()); + + iter = p.first; + } + + if (required || access(iter->second.c_str(), R_OK) == 0) { + fq.push_back(watch_logfile(iter->second, loo, required)); + } + } + } +} + +file_collection +file_collection::rescan_files(bool required) +{ + file_collection retval; + lnav::futures::future_queue fq( + [&retval](auto& fc) { retval.merge(fc); }); + + for (auto& pair : this->fc_file_names) { + if (!pair.second.loo_temp_file) { + this->expand_filename(fq, pair.first, pair.second, required); + if (this->fc_rotated) { + std::string path = pair.first + ".*"; + + this->expand_filename(fq, path, pair.second, false); + } + } else if (pair.second.loo_fd.get() != -1) { + fq.push_back(watch_logfile(pair.first, pair.second, required)); + } + + if (retval.fc_files.size() >= 100) { + log_debug("too many new files, breaking..."); + break; + } + } + + fq.pop_to(); + + this->fc_new_stats.clear(); + + return retval; +} + +void +file_collection::request_close(const std::shared_ptr& lf) +{ + lf->close(); + this->fc_files_generation += 1; +} diff --git a/src/file_collection.hh b/src/file_collection.hh new file mode 100644 index 0000000..926f8f1 --- /dev/null +++ b/src/file_collection.hh @@ -0,0 +1,180 @@ +/** + * Copyright (c) 2020, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @file file_collection.hh + */ + +#ifndef lnav_file_collection_hh +#define lnav_file_collection_hh + +#include +#include +#include +#include +#include +#include + +#include "archive_manager.hh" +#include "base/future_util.hh" +#include "file_format.hh" +#include "logfile_fwd.hh" +#include "safe/safe.h" +#include "tailer/tailer.looper.hh" + +struct tailer_progress { + std::string tp_message; +}; + +struct scan_progress { + std::list sp_extractions; + std::map sp_tailers; +}; + +using safe_scan_progress = safe::Safe; + +struct other_file_descriptor { + file_format_t ofd_format; + std::string ofd_description; + + other_file_descriptor(file_format_t format = file_format_t::UNKNOWN, + std::string description = "") + : ofd_format(format), ofd_description(std::move(description)) + { + } +}; + +struct file_error_info { + const time_t fei_mtime; + const std::string fei_description; +}; + +struct file_collection; + +enum class child_poll_result_t { + ALIVE, + FINISHED, +}; + +class child_poller { +public: + explicit child_poller( + auto_pid child, + std::function&)> finalizer) + : cp_child(std::move(child)), cp_finalizer(std::move(finalizer)) + { + ensure(this->cp_finalizer); + } + + child_poller(child_poller&& other) noexcept + : cp_child(std::move(other.cp_child)), + cp_finalizer(std::move(other.cp_finalizer)) + { + ensure(this->cp_finalizer); + } + + child_poller& operator=(child_poller&& other) noexcept + { + require(other.cp_finalizer); + + this->cp_child = std::move(other.cp_child); + this->cp_finalizer = std::move(other.cp_finalizer); + + return *this; + } + + ~child_poller() noexcept = default; + + child_poller(const child_poller&) = delete; + + child_poller& operator=(const child_poller&) = delete; + + child_poll_result_t poll(file_collection& fc); + +private: + nonstd::optional> cp_child; + std::function&)> + cp_finalizer; +}; + +struct file_collection { + bool fc_invalidate_merge{false}; + + bool fc_recursive{false}; + bool fc_rotated{false}; + + std::map fc_name_to_errors; + std::map fc_file_names; + std::vector> fc_files; + int fc_files_generation{0}; + std::vector, std::string>> + fc_renamed_files; + std::set fc_closed_files; + std::map fc_other_files; + std::set fc_synced_files; + std::shared_ptr fc_progress; + std::vector fc_new_stats; + std::list fc_child_pollers; + size_t fc_largest_path_length{0}; + + file_collection() + : fc_progress(std::make_shared>()) + { + } + + void clear() + { + this->fc_name_to_errors.clear(); + this->fc_file_names.clear(); + this->fc_files.clear(); + this->fc_closed_files.clear(); + this->fc_other_files.clear(); + this->fc_new_stats.clear(); + } + + file_collection rescan_files(bool required = false); + + void expand_filename(lnav::futures::future_queue& fq, + const std::string& path, + logfile_open_options& loo, + bool required); + + std::future watch_logfile(const std::string& filename, + logfile_open_options& loo, + bool required); + + void merge(file_collection& other); + + void request_close(const std::shared_ptr& lf); + + void close_files(const std::vector>& files); + + void regenerate_unique_file_names(); +}; + +#endif diff --git a/src/file_format.cc b/src/file_format.cc new file mode 100644 index 0000000..d82b78c --- /dev/null +++ b/src/file_format.cc @@ -0,0 +1,119 @@ +/** + * Copyright (c) 2020, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @file file_format.hh + */ + +#include + +#include "file_format.hh" + +#include "archive_manager.hh" +#include "base/auto_fd.hh" +#include "base/fs_util.hh" +#include "base/intern_string.hh" +#include "base/lnav_log.hh" +#include "config.h" + +static bool +is_pcap_header(uint8_t* buffer) +{ + size_t offset = 0; + if (buffer[0] == 0x0a && buffer[1] == 0x0d && buffer[2] == 0x0d + && buffer[3] == 0x0a) + { + offset += sizeof(uint32_t) * 2; + if (buffer[offset + 0] == 0x1a && buffer[offset + 1] == 0x2b + && buffer[offset + 2] == 0x3c && buffer[offset + 3] == 0x4d) + { + return true; + } + + if (buffer[offset + 0] == 0x4d && buffer[offset + 1] == 0x3c + && buffer[offset + 2] == 0x2b && buffer[offset + 3] == 0x1a) + { + return true; + } + return false; + } + + if (buffer[0] == 0xa1 && buffer[1] == 0xb2 && buffer[2] == 0xc3 + && buffer[3] == 0xd4) + { + return true; + } + + if (buffer[0] == 0xd4 && buffer[1] == 0xc3 && buffer[2] == 0xb2 + && buffer[3] == 0xa1) + { + return true; + } + + if (buffer[0] == 0xa1 && buffer[1] == 0xb2 && buffer[2] == 0x3c + && buffer[3] == 0x4d) + { + return true; + } + + if (buffer[0] == 0x4d && buffer[1] == 0x3c && buffer[2] == 0xb2 + && buffer[3] == 0xa1) + { + return true; + } + + return false; +} + +file_format_t +detect_file_format(const ghc::filesystem::path& filename) +{ + if (archive_manager::is_archive(filename)) { + return file_format_t::ARCHIVE; + } + + file_format_t retval = file_format_t::UNKNOWN; + auto_fd fd; + + if ((fd = lnav::filesystem::openp(filename, O_RDONLY)) != -1) { + uint8_t buffer[32]; + ssize_t rc; + + if ((rc = read(fd, buffer, sizeof(buffer))) > 0) { + static auto SQLITE3_HEADER = "SQLite format 3"; + auto header_frag = string_fragment(buffer, 0, rc); + + if (header_frag.startswith(SQLITE3_HEADER)) { + retval = file_format_t::SQLITE_DB; + } else if (rc > 24 && is_pcap_header(buffer)) { + retval = file_format_t::PCAP; + } + } + } + + return retval; +} diff --git a/src/file_format.hh b/src/file_format.hh new file mode 100644 index 0000000..a8eb3e4 --- /dev/null +++ b/src/file_format.hh @@ -0,0 +1,76 @@ +/** + * Copyright (c) 2020, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @file file_format.hh + */ + +#ifndef lnav_file_format_hh +#define lnav_file_format_hh + +#include "fmt/format.h" +#include "ghc/filesystem.hpp" + +enum class file_format_t : int { + UNKNOWN, + SQLITE_DB, + ARCHIVE, + PCAP, + REMOTE, +}; + +file_format_t detect_file_format(const ghc::filesystem::path& filename); + +namespace fmt { +template<> +struct formatter : formatter { + template + auto format(file_format_t ff, FormatContext& ctx) + { + string_view name = "unknown"; + switch (ff) { + case file_format_t::SQLITE_DB: + name = "\U0001F5C2 SQLite DB"; + break; + case file_format_t::ARCHIVE: + name = "\U0001F5C4 Archive"; + break; + case file_format_t::PCAP: + name = "\U0001F5A5 Pcap"; + break; + case file_format_t::REMOTE: + name = "\U0001F5A5 Remote"; + break; + default: + break; + } + return formatter::format(name, ctx); + } +}; +} // namespace fmt + +#endif diff --git a/src/file_vtab.cc b/src/file_vtab.cc new file mode 100644 index 0000000..aab5d5b --- /dev/null +++ b/src/file_vtab.cc @@ -0,0 +1,345 @@ +/** + * Copyright (c) 2017, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include "base/injector.bind.hh" +#include "base/lnav.gzip.hh" +#include "base/lnav_log.hh" +#include "config.h" +#include "file_collection.hh" +#include "file_vtab.cfg.hh" +#include "log_format.hh" +#include "logfile.hh" +#include "session_data.hh" +#include "vtab_module.hh" + +struct lnav_file : public tvt_iterator_cursor { + using iterator = std::vector>::iterator; + + static constexpr const char* NAME = "lnav_file"; + static constexpr const char* CREATE_STMT = R"( +-- Access lnav's open file list through this table. +CREATE TABLE lnav_file ( + device integer, -- The device the file is stored on. + inode integer, -- The inode for the file on the device. + filepath text, -- The path to the file. + mimetype text, -- The MIME type for the file. + content_id text, -- The hash of some unique content in the file. + format text, -- The log file format for the file. + lines integer, -- The number of lines in the file. + time_offset integer, -- The millisecond offset for timestamps. + + content BLOB HIDDEN -- The contents of the file. +); +)"; + + explicit lnav_file(file_collection& fc) : lf_collection(fc) {} + + iterator begin() { return this->lf_collection.fc_files.begin(); } + + iterator end() { return this->lf_collection.fc_files.end(); } + + int get_column(const cursor& vc, sqlite3_context* ctx, int col) + { + auto lf = *vc.iter; + const struct stat& st = lf->get_stat(); + const auto& name = lf->get_filename(); + auto format = lf->get_format(); + const char* format_name = format != nullptr ? format->get_name().get() + : nullptr; + + switch (col) { + case 0: + to_sqlite(ctx, (int64_t) st.st_dev); + break; + case 1: + to_sqlite(ctx, (int64_t) st.st_ino); + break; + case 2: + to_sqlite(ctx, name); + break; + case 3: + to_sqlite(ctx, fmt::to_string(lf->get_text_format())); + break; + case 4: + to_sqlite( + ctx, + fmt::format(FMT_STRING("v1:{}"), lf->get_content_id())); + break; + case 5: + to_sqlite(ctx, format_name); + break; + case 6: + to_sqlite(ctx, (int64_t) lf->size()); + break; + case 7: { + auto tv = lf->get_time_offset(); + int64_t ms = (tv.tv_sec * 1000LL) + tv.tv_usec / 1000LL; + + to_sqlite(ctx, ms); + break; + } + case 8: { + auto& cfg = injector::get(); + auto lf_stat = lf->get_stat(); + + if (lf_stat.st_size > cfg.fvc_max_content_size) { + sqlite3_result_error(ctx, "file is too large", -1); + } else { + auto fd = lf->get_fd(); + auto_mem buf; + buf = (char*) malloc(lf_stat.st_size); + auto rc = pread(fd, buf, lf_stat.st_size, 0); + + if (rc == -1) { + auto errmsg + = fmt::format(FMT_STRING("unable to read file: {}"), + strerror(errno)); + + sqlite3_result_error( + ctx, errmsg.c_str(), errmsg.length()); + } else if (rc != lf_stat.st_size) { + auto errmsg = fmt::format( + FMT_STRING("short read of file: {} < {}"), + rc, + lf_stat.st_size); + + sqlite3_result_error( + ctx, errmsg.c_str(), errmsg.length()); + } else if (lnav::gzip::is_gzipped(buf, rc)) { + lnav::gzip::uncompress(lf->get_unique_path(), buf, rc) + .then([ctx](auto uncomp) { + auto pair = uncomp.release(); + + sqlite3_result_blob64( + ctx, pair.first, pair.second, free); + }) + .otherwise([ctx](auto msg) { + sqlite3_result_error( + ctx, msg.c_str(), msg.size()); + }); + } else { + sqlite3_result_blob64(ctx, buf.release(), rc, free); + } + } + break; + } + default: + ensure(0); + break; + } + + return SQLITE_OK; + } + + int delete_row(sqlite3_vtab* vt, sqlite3_int64 rowid) + { + vt->zErrMsg = sqlite3_mprintf("Rows cannot be deleted from this table"); + return SQLITE_ERROR; + } + + int insert_row(sqlite3_vtab* tab, sqlite3_int64& rowid_out) + { + tab->zErrMsg + = sqlite3_mprintf("Rows cannot be inserted into this table"); + return SQLITE_ERROR; + } + + int update_row(sqlite3_vtab* tab, + sqlite3_int64& rowid, + int64_t device, + int64_t inode, + std::string path, + const char* text_format, + const char* content_id, + const char* format, + int64_t lines, + int64_t time_offset, + const char* content) + { + auto lf = this->lf_collection.fc_files[rowid]; + struct timeval tv = { + (int) (time_offset / 1000LL), + (int) (time_offset / (1000LL * 1000LL)), + }; + + lf->adjust_content_time(0, tv, true); + + if (path != lf->get_filename()) { + if (lf->is_valid_filename()) { + throw sqlite_func_error( + "real file paths cannot be updated, only symbolic ones"); + } + + auto iter + = this->lf_collection.fc_file_names.find(lf->get_filename()); + + if (iter != this->lf_collection.fc_file_names.end()) { + auto loo = std::move(iter->second); + + this->lf_collection.fc_file_names.erase(iter); + + loo.loo_include_in_session = true; + this->lf_collection.fc_file_names[path] = std::move(loo); + lf->set_filename(path); + this->lf_collection.regenerate_unique_file_names(); + + init_session(); + load_session(); + } + } + + return SQLITE_OK; + } + + file_collection& lf_collection; +}; + +struct lnav_file_metadata { + static constexpr const char* NAME = "lnav_file_metadata"; + static constexpr const char* CREATE_STMT = R"( +-- Access the metadata embedded in open files +CREATE TABLE lnav_file_metadata ( + filepath text, -- The path to the file. + descriptor text, -- The descriptor that identifies the source of the metadata. + mimetype text, -- The MIME type of the metadata. + content text -- The metadata itself. +); +)"; + + struct cursor { + struct metadata_row { + metadata_row(std::shared_ptr lf, std::string desc) + : mr_logfile(lf), mr_descriptor(std::move(desc)) + { + } + std::shared_ptr mr_logfile; + std::string mr_descriptor; + }; + + sqlite3_vtab_cursor base; + lnav_file_metadata& c_meta; + std::vector::iterator c_iter; + std::vector c_rows; + + cursor(sqlite3_vtab* vt) + : base({vt}), + c_meta(((vtab_module::vtab*) vt)->v_impl) + { + for (auto& lf : this->c_meta.lfm_collection.fc_files) { + auto& lf_meta = lf->get_embedded_metadata(); + + for (const auto& meta_pair : lf_meta) { + this->c_rows.emplace_back(lf, meta_pair.first); + } + } + } + + ~cursor() { this->c_iter = this->c_rows.end(); } + + int next() + { + if (this->c_iter != this->c_rows.end()) { + ++this->c_iter; + } + return SQLITE_OK; + } + + int eof() { return this->c_iter == this->c_rows.end(); } + + int reset() + { + this->c_iter = this->c_rows.begin(); + return SQLITE_OK; + } + + int get_rowid(sqlite3_int64& rowid_out) + { + rowid_out = this->c_iter - this->c_rows.begin(); + + return SQLITE_OK; + } + }; + + explicit lnav_file_metadata(file_collection& fc) : lfm_collection(fc) {} + + int get_column(const cursor& vc, sqlite3_context* ctx, int col) + { + auto& mr = *vc.c_iter; + + switch (col) { + case 0: + to_sqlite(ctx, mr.mr_logfile->get_filename()); + break; + case 1: + to_sqlite(ctx, mr.mr_descriptor); + break; + case 2: + to_sqlite( + ctx, + fmt::to_string( + mr.mr_logfile->get_embedded_metadata()[mr.mr_descriptor] + .m_format)); + break; + case 3: + to_sqlite( + ctx, + fmt::to_string( + mr.mr_logfile->get_embedded_metadata()[mr.mr_descriptor] + .m_value)); + break; + default: + ensure(0); + break; + } + + return SQLITE_OK; + } + + file_collection& lfm_collection; +}; + +struct injectable_lnav_file : vtab_module { + using vtab_module::vtab_module; + using injectable = injectable_lnav_file(file_collection&); +}; + +struct injectable_lnav_file_metadata + : vtab_module> { + using vtab_module>::vtab_module; + using injectable = injectable_lnav_file_metadata(file_collection&); +}; + +static auto file_binder + = injector::bind_multiple().add(); + +static auto file_meta_binder = injector::bind_multiple() + .add(); diff --git a/src/file_vtab.cfg.hh b/src/file_vtab.cfg.hh new file mode 100644 index 0000000..8e99b20 --- /dev/null +++ b/src/file_vtab.cfg.hh @@ -0,0 +1,43 @@ +/** + * Copyright (c) 2021, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @file file_vtab.cfg.hh + */ + +#ifndef lnav_file_vtab_cfg_hh +#define lnav_file_vtab_cfg_hh + +namespace file_vtab { + +struct config { + int64_t fvc_max_content_size{32 * 1024 * 1024}; +}; + +} // namespace file_vtab + +#endif diff --git a/src/files_sub_source.cc b/src/files_sub_source.cc new file mode 100644 index 0000000..48a3f56 --- /dev/null +++ b/src/files_sub_source.cc @@ -0,0 +1,437 @@ +/** + * Copyright (c) 2020, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "files_sub_source.hh" + +#include "base/ansi_scrubber.hh" +#include "base/humanize.hh" +#include "base/humanize.network.hh" +#include "base/opt_util.hh" +#include "base/string_util.hh" +#include "config.h" +#include "lnav.hh" +#include "mapbox/variant.hpp" + +namespace files_model { +files_list_selection +from_selection(vis_line_t sel_vis) +{ + auto& fc = lnav_data.ld_active_files; + int sel = (int) sel_vis; + + if (sel < fc.fc_name_to_errors.size()) { + auto iter = fc.fc_name_to_errors.begin(); + + std::advance(iter, sel); + return error_selection::build(sel, iter); + } + + sel -= fc.fc_name_to_errors.size(); + + if (sel < fc.fc_other_files.size()) { + auto iter = fc.fc_other_files.begin(); + + std::advance(iter, sel); + return other_selection::build(sel, iter); + } + + sel -= fc.fc_other_files.size(); + + if (sel < fc.fc_files.size()) { + auto iter = fc.fc_files.begin(); + + std::advance(iter, sel); + return file_selection::build(sel, iter); + } + + return no_selection{}; +} +} // namespace files_model + +files_sub_source::files_sub_source() {} + +bool +files_sub_source::list_input_handle_key(listview_curses& lv, int ch) +{ + switch (ch) { + case KEY_ENTER: + case '\r': { + auto sel = files_model::from_selection(lv.get_selection()); + + sel.match([](files_model::no_selection) {}, + [](files_model::error_selection) {}, + [](files_model::other_selection) {}, + [&](files_model::file_selection& fs) { + auto& lss = lnav_data.ld_log_source; + auto lf = *fs.sb_iter; + + lss.find_data(lf) | [](auto ld) { + ld->set_visibility(true); + lnav_data.ld_log_source.text_filters_changed(); + }; + + if (lf->get_format() != nullptr) { + auto& log_view = lnav_data.ld_views[LNV_LOG]; + lss.row_for_time(lf->front().get_timeval()) | + [](auto row) { + lnav_data.ld_views[LNV_LOG].set_top(row); + }; + ensure_view(&log_view); + } else { + auto& tv = lnav_data.ld_views[LNV_TEXT]; + auto& tss = lnav_data.ld_text_source; + + tss.to_front(lf); + tv.reload_data(); + ensure_view(&tv); + } + + lv.reload_data(); + lnav_data.ld_mode = ln_mode_t::PAGING; + }); + + return true; + } + + case ' ': { + auto sel = files_model::from_selection(lv.get_selection()); + + sel.match([](files_model::no_selection) {}, + [](files_model::error_selection) {}, + [](files_model::other_selection) {}, + [&](files_model::file_selection& fs) { + auto& lss = lnav_data.ld_log_source; + auto lf = *fs.sb_iter; + + lss.find_data(lf) | [](auto ld) { + ld->set_visibility(!ld->ld_visible); + }; + + auto top_view = *lnav_data.ld_view_stack.top(); + auto tss = top_view->get_sub_source(); + + if (tss != nullptr) { + tss->text_filters_changed(); + top_view->reload_data(); + } + + lv.reload_data(); + }); + return true; + } + case 'n': { + execute_command(lnav_data.ld_exec_context, "next-mark search"); + return true; + } + case 'N': { + execute_command(lnav_data.ld_exec_context, "prev-mark search"); + return true; + } + case '/': { + execute_command(lnav_data.ld_exec_context, "prompt search-files"); + return true; + } + case 'X': { + auto sel = files_model::from_selection(lv.get_selection()); + + sel.match( + [](files_model::no_selection) {}, + [&](files_model::error_selection& es) { + auto& fc = lnav_data.ld_active_files; + + fc.fc_file_names.erase(es.sb_iter->first); + + auto name_iter = fc.fc_file_names.begin(); + while (name_iter != fc.fc_file_names.end()) { + if (name_iter->first == es.sb_iter->first) { + name_iter = fc.fc_file_names.erase(name_iter); + continue; + } + + auto rp_opt = humanize::network::path::from_str( + name_iter->first); + + if (rp_opt) { + auto rp = *rp_opt; + + if (fmt::to_string(rp.home()) == es.sb_iter->first) + { + fc.fc_other_files.erase(name_iter->first); + name_iter = fc.fc_file_names.erase(name_iter); + continue; + } + } + ++name_iter; + } + + fc.fc_name_to_errors.erase(es.sb_iter); + fc.fc_invalidate_merge = true; + lv.reload_data(); + }, + [](files_model::other_selection) {}, + [](files_model::file_selection) {}); + return true; + } + } + return false; +} + +void +files_sub_source::list_input_handle_scroll_out(listview_curses& lv) +{ + lnav_data.ld_mode = ln_mode_t::PAGING; + lnav_data.ld_filter_view.reload_data(); +} + +size_t +files_sub_source::text_line_count() +{ + const auto& fc = lnav_data.ld_active_files; + + return fc.fc_name_to_errors.size() + fc.fc_other_files.size() + + fc.fc_files.size(); +} + +size_t +files_sub_source::text_line_width(textview_curses& curses) +{ + return 512; +} + +void +files_sub_source::text_value_for_line(textview_curses& tc, + int line, + std::string& value_out, + text_sub_source::line_flags_t flags) +{ + const auto dim = tc.get_dimensions(); + const auto& fc = lnav_data.ld_active_files; + auto filename_width + = std::min(fc.fc_largest_path_length, + std::max((size_t) 40, (size_t) dim.second - 30)); + + if (line < fc.fc_name_to_errors.size()) { + auto iter = fc.fc_name_to_errors.begin(); + std::advance(iter, line); + auto path = ghc::filesystem::path(iter->first); + auto fn = path.filename().string(); + + truncate_to(fn, filename_width); + value_out = fmt::format(FMT_STRING(" {:<{}} {}"), + fn, + filename_width, + iter->second.fei_description); + return; + } + + line -= fc.fc_name_to_errors.size(); + + if (line < fc.fc_other_files.size()) { + auto iter = fc.fc_other_files.begin(); + std::advance(iter, line); + auto path = ghc::filesystem::path(iter->first); + auto fn = path.string(); + + truncate_to(fn, filename_width); + value_out = fmt::format(FMT_STRING(" {:<{}} {:14} {}"), + fn, + filename_width, + iter->second.ofd_format, + iter->second.ofd_description); + return; + } + + line -= fc.fc_other_files.size(); + + const auto& lf = fc.fc_files[line]; + auto fn = lf->get_unique_path(); + char start_time[64] = "", end_time[64] = ""; + std::vector file_notes; + + if (lf->get_format() != nullptr) { + sql_strftime(start_time, sizeof(start_time), lf->front().get_timeval()); + sql_strftime(end_time, sizeof(end_time), lf->back().get_timeval()); + } + truncate_to(fn, filename_width); + for (const auto& pair : lf->get_notes()) { + file_notes.push_back(pair.second); + } + value_out = fmt::format(FMT_STRING(" {:<{}} {:>8} {} \u2014 {} {}"), + fn, + filename_width, + humanize::file_size(lf->get_index_size(), + humanize::alignment::columnar), + start_time, + end_time, + fmt::join(file_notes, "; ")); + this->fss_last_line_len + = filename_width + 23 + strlen(start_time) + strlen(end_time); +} + +void +files_sub_source::text_attrs_for_line(textview_curses& tc, + int line, + string_attrs_t& value_out) +{ + bool selected + = lnav_data.ld_mode == ln_mode_t::FILES && line == tc.get_selection(); + const auto& fc = lnav_data.ld_active_files; + const auto dim = tc.get_dimensions(); + auto filename_width + = std::min(fc.fc_largest_path_length, + std::max((size_t) 40, (size_t) dim.second - 30)); + + if (selected) { + value_out.emplace_back(line_range{0, 1}, + VC_GRAPHIC.value(ACS_RARROW)); + } + + if (line < fc.fc_name_to_errors.size()) { + if (selected) { + value_out.emplace_back( + line_range{0, -1}, + VC_ROLE.value(role_t::VCR_DISABLED_FOCUSED)); + } + + value_out.emplace_back( + line_range{4 + (int) filename_width, -1}, + VC_ROLE_FG.value(role_t::VCR_ERROR)); + return; + } + line -= fc.fc_name_to_errors.size(); + + if (line < fc.fc_other_files.size()) { + if (selected) { + value_out.emplace_back( + line_range{0, -1}, + VC_ROLE.value(role_t::VCR_DISABLED_FOCUSED)); + } + if (line == fc.fc_other_files.size() - 1) { + value_out.emplace_back(line_range{0, -1}, + VC_STYLE.value(text_attrs{A_UNDERLINE})); + } + return; + } + + line -= fc.fc_other_files.size(); + + if (selected) { + value_out.emplace_back( + line_range{0, -1}, + VC_ROLE.value(role_t::VCR_FOCUSED)); + } + + auto& lss = lnav_data.ld_log_source; + auto& lf = fc.fc_files[line]; + auto ld_opt = lss.find_data(lf); + + chtype visible = ACS_DIAMOND; + if (ld_opt && !ld_opt.value()->ld_visible) { + visible = ' '; + } + value_out.emplace_back(line_range{2, 3}, + VC_GRAPHIC.value(visible)); + if (visible == ACS_DIAMOND) { + value_out.emplace_back(line_range{2, 3}, + VC_FOREGROUND.value(COLOR_GREEN)); + } + + auto lr = line_range{ + (int) filename_width + 3 + 4, + (int) filename_width + 3 + 10, + }; + value_out.emplace_back(lr, VC_STYLE.value(text_attrs{A_BOLD})); + + lr.lr_start = this->fss_last_line_len; + lr.lr_end = -1; + value_out.emplace_back(lr, VC_FOREGROUND.value(COLOR_YELLOW)); +} + +size_t +files_sub_source::text_size_for_line(textview_curses& tc, + int line, + text_sub_source::line_flags_t raw) +{ + return 0; +} + +static auto +spinner_index() +{ + auto now = ui_clock::now(); + + return std::chrono::duration_cast( + now.time_since_epoch()) + .count() + / 100; +} + +bool +files_overlay_source::list_value_for_overlay(const listview_curses& lv, + int y, + int bottom, + vis_line_t line, + attr_line_t& value_out) +{ + if (y == 0) { + static const char PROG[] = "-\\|/"; + constexpr size_t PROG_SIZE = sizeof(PROG) - 1; + + auto& fc = lnav_data.ld_active_files; + auto fc_prog = fc.fc_progress; + safe::WriteAccess sp(*fc_prog); + + if (!sp->sp_extractions.empty()) { + const auto& prog = sp->sp_extractions.front(); + + value_out.with_ansi_string(fmt::format( + "{} Extracting " ANSI_COLOR(COLOR_CYAN) "{}" ANSI_NORM + "... {:>8}/{}", + PROG[spinner_index() % PROG_SIZE], + prog.ep_path.filename().string(), + humanize::file_size(prog.ep_out_size, + humanize::alignment::none), + humanize::file_size(prog.ep_total_size, + humanize::alignment::none))); + return true; + } + if (!sp->sp_tailers.empty()) { + auto first_iter = sp->sp_tailers.begin(); + + value_out.with_ansi_string(fmt::format( + "{} Connecting to " ANSI_COLOR(COLOR_CYAN) "{}" ANSI_NORM + ": {}", + PROG[spinner_index() % PROG_SIZE], + first_iter->first, + first_iter->second.tp_message)); + return true; + } + } + return false; +} diff --git a/src/files_sub_source.hh b/src/files_sub_source.hh new file mode 100644 index 0000000..7c3b325 --- /dev/null +++ b/src/files_sub_source.hh @@ -0,0 +1,117 @@ +/** + * Copyright (c) 2020, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef files_sub_source_hh +#define files_sub_source_hh + +#include "file_collection.hh" +#include "textview_curses.hh" + +class files_sub_source + : public text_sub_source + , public list_input_delegate { +public: + files_sub_source(); + + bool list_input_handle_key(listview_curses& lv, int ch) override; + + void list_input_handle_scroll_out(listview_curses& lv) override; + + size_t text_line_count() override; + + size_t text_line_width(textview_curses& curses) override; + + void text_value_for_line(textview_curses& tc, + int line, + std::string& value_out, + line_flags_t flags) override; + + void text_attrs_for_line(textview_curses& tc, + int line, + string_attrs_t& value_out) override; + + size_t text_size_for_line(textview_curses& tc, + int line, + line_flags_t raw) override; + + size_t fss_last_line_len{0}; +}; + +struct files_overlay_source : public list_overlay_source { + bool list_value_for_overlay(const listview_curses& lv, + int y, + int bottom, + vis_line_t line, + attr_line_t& value_out) override; +}; + +namespace files_model { + +struct no_selection { +}; + +template +struct selection_base { + int sb_index{0}; + T sb_iter; + + static C build(int index, T iter) + { + C retval; + + retval.sb_index = index; + retval.sb_iter = iter; + return retval; + } +}; + +struct error_selection + : public selection_base::iterator> { +}; + +struct other_selection + : public selection_base< + other_selection, + std::map::iterator> { +}; + +struct file_selection + : public selection_base>::iterator> { +}; + +using files_list_selection = mapbox::util:: + variant; + +files_list_selection from_selection(vis_line_t sel_vis); + +} // namespace files_model + +#endif diff --git a/src/filter_observer.cc b/src/filter_observer.cc new file mode 100644 index 0000000..0cdaca5 --- /dev/null +++ b/src/filter_observer.cc @@ -0,0 +1,111 @@ +/** + * Copyright (c) 2019, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "filter_observer.hh" + +#include "config.h" +#include "log_format.hh" + +void +line_filter_observer::logline_new_lines(const logfile& lf, + logfile::const_iterator ll_begin, + logfile::const_iterator ll_end, + shared_buffer_ref& sbr) +{ + size_t offset = std::distance(lf.begin(), ll_begin); + + require(&lf == this->lfo_filter_state.tfs_logfile.get()); + + this->lfo_filter_state.resize(lf.size()); + if (this->lfo_filter_stack.empty()) { + return; + } + + for (; ll_begin != ll_end; ++ll_begin) { + if (lf.get_format() != nullptr) { + lf.get_format()->get_subline(*ll_begin, sbr); + } + for (auto& filter : this->lfo_filter_stack) { + if (filter->lf_deleted) { + continue; + } + if (offset + >= this->lfo_filter_state.tfs_filter_count[filter->get_index()]) + { + filter->add_line(this->lfo_filter_state, ll_begin, sbr); + } + } + } +} + +void +line_filter_observer::logline_eof(const logfile& lf) +{ + for (auto& iter : this->lfo_filter_stack) { + if (iter->lf_deleted) { + continue; + } + iter->end_of_message(this->lfo_filter_state); + } +} + +size_t +line_filter_observer::get_min_count(size_t max) const +{ + size_t retval = max; + + for (auto& filter : this->lfo_filter_stack) { + if (filter->lf_deleted) { + continue; + } + retval = std::min( + retval, + this->lfo_filter_state.tfs_filter_count[filter->get_index()]); + } + + return retval; +} + +void +line_filter_observer::clear_deleted_filter_state() +{ + uint32_t used_mask = 0; + + for (auto& filter : this->lfo_filter_stack) { + if (filter->lf_deleted) { + log_debug("skipping deleted %p %d %d", + filter.get(), + filter->get_index(), + filter->get_lang()); + continue; + } + used_mask |= (1UL << filter->get_index()); + } + this->lfo_filter_state.clear_deleted_filter_state(used_mask); +} diff --git a/src/filter_observer.hh b/src/filter_observer.hh new file mode 100644 index 0000000..008def3 --- /dev/null +++ b/src/filter_observer.hh @@ -0,0 +1,78 @@ +/** + * Copyright (c) 2015, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef filter_observer_hh +#define filter_observer_hh + +#include + +#include "logfile.hh" +#include "textview_curses.hh" + +class line_filter_observer : public logline_observer { +public: + line_filter_observer(filter_stack& fs, std::shared_ptr lf) + : lfo_filter_stack(fs), lfo_filter_state(lf) + { + } + + void logline_restart(const logfile& lf, file_size_t rollback_size) override + { + for (auto& filter : this->lfo_filter_stack) { + filter->revert_to_last(this->lfo_filter_state, rollback_size); + } + } + + void logline_new_lines(const logfile& lf, + logfile::const_iterator ll_begin, + logfile::const_iterator ll_end, + shared_buffer_ref& sbr) override; + + void logline_eof(const logfile& lf) override; + + bool excluded(uint32_t filter_in_mask, + uint32_t filter_out_mask, + size_t offset) const + { + bool filtered_in = (filter_in_mask == 0) + || (this->lfo_filter_state.tfs_mask[offset] & filter_in_mask) != 0; + bool filtered_out + = (this->lfo_filter_state.tfs_mask[offset] & filter_out_mask) != 0; + return !filtered_in || filtered_out; + } + + size_t get_min_count(size_t max) const; + + void clear_deleted_filter_state(); + + filter_stack& lfo_filter_stack; + logfile_filter_state lfo_filter_state; +}; + +#endif diff --git a/src/filter_status_source.cc b/src/filter_status_source.cc new file mode 100644 index 0000000..cbc56c0 --- /dev/null +++ b/src/filter_status_source.cc @@ -0,0 +1,332 @@ +/** + * Copyright (c) 2018, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "filter_status_source.hh" + +#include "base/ansi_scrubber.hh" +#include "base/opt_util.hh" +#include "config.h" +#include "files_sub_source.hh" +#include "filter_sub_source.hh" +#include "lnav.hh" + +static auto TOGGLE_MSG = "Press " ANSI_BOLD("TAB") " to edit "; +static auto EXIT_MSG = "Press " ANSI_BOLD("q") " to exit "; + +static auto CREATE_HELP = ANSI_BOLD("i") "/" ANSI_BOLD("o") ": Create in/out"; +static auto ENABLE_HELP = ANSI_BOLD("SPC") ": "; +static auto EDIT_HELP = ANSI_BOLD("ENTER") ": Edit"; +static auto TOGGLE_HELP = ANSI_BOLD("t") ": To "; +static auto DELETE_HELP = ANSI_BOLD("D") ": Delete"; +static auto FILTERING_HELP = ANSI_BOLD("f") ": "; +static auto JUMP_HELP = ANSI_BOLD("ENTER") ": Jump To"; +static auto CLOSE_HELP = ANSI_BOLD("X") ": Close"; + +filter_status_source::filter_status_source() +{ + this->tss_fields[TSF_TITLE].set_width(14); + this->tss_fields[TSF_TITLE].set_role(role_t::VCR_STATUS_TITLE); + this->tss_fields[TSF_TITLE].set_value(" " ANSI_ROLE("T") "ext Filters ", + role_t::VCR_STATUS_TITLE_HOTKEY); + + this->tss_fields[TSF_STITCH_TITLE].set_width(2); + this->tss_fields[TSF_STITCH_TITLE].set_stitch_value( + role_t::VCR_STATUS_STITCH_TITLE_TO_NORMAL, + role_t::VCR_STATUS_STITCH_NORMAL_TO_TITLE); + + this->tss_fields[TSF_COUNT].set_min_width(16); + this->tss_fields[TSF_COUNT].set_share(1); + this->tss_fields[TSF_COUNT].set_role(role_t::VCR_STATUS); + + this->tss_fields[TSF_FILTERED].set_min_width(20); + this->tss_fields[TSF_FILTERED].set_share(1); + this->tss_fields[TSF_FILTERED].set_role(role_t::VCR_STATUS); + + this->tss_fields[TSF_FILES_TITLE].set_width(7); + this->tss_fields[TSF_FILES_TITLE].set_role( + role_t::VCR_STATUS_DISABLED_TITLE); + this->tss_fields[TSF_FILES_TITLE].set_value(" " ANSI_ROLE("F") "iles ", + role_t::VCR_STATUS_HOTKEY); + + this->tss_fields[TSF_FILES_RIGHT_STITCH].set_width(2); + this->tss_fields[TSF_FILES_RIGHT_STITCH].set_stitch_value( + role_t::VCR_STATUS, role_t::VCR_STATUS); + + this->tss_fields[TSF_HELP].right_justify(true); + this->tss_fields[TSF_HELP].set_width(20); + this->tss_fields[TSF_HELP].set_value(TOGGLE_MSG); + this->tss_fields[TSF_HELP].set_left_pad(1); + + this->tss_error.set_min_width(20); + this->tss_error.set_share(1); + this->tss_error.set_role(role_t::VCR_ALERT_STATUS); +} + +size_t +filter_status_source::statusview_fields() +{ + switch (lnav_data.ld_mode) { + case ln_mode_t::SEARCH_FILTERS: + case ln_mode_t::SEARCH_FILES: + this->tss_fields[TSF_HELP].set_value(""); + break; + case ln_mode_t::FILTER: + case ln_mode_t::FILES: + this->tss_fields[TSF_HELP].set_value(EXIT_MSG); + break; + default: + this->tss_fields[TSF_HELP].set_value(TOGGLE_MSG); + break; + } + + if (lnav_data.ld_mode == ln_mode_t::FILES + || lnav_data.ld_mode == ln_mode_t::SEARCH_FILES) + { + this->tss_fields[TSF_FILES_TITLE].set_value( + " " ANSI_ROLE("F") "iles ", role_t::VCR_STATUS_TITLE_HOTKEY); + this->tss_fields[TSF_FILES_TITLE].set_role(role_t::VCR_STATUS_TITLE); + this->tss_fields[TSF_FILES_RIGHT_STITCH].set_stitch_value( + role_t::VCR_STATUS_STITCH_TITLE_TO_NORMAL, + role_t::VCR_STATUS_STITCH_NORMAL_TO_TITLE); + this->tss_fields[TSF_TITLE].set_value(" " ANSI_ROLE("T") "ext Filters ", + role_t::VCR_STATUS_HOTKEY); + this->tss_fields[TSF_TITLE].set_role(role_t::VCR_STATUS_DISABLED_TITLE); + this->tss_fields[TSF_STITCH_TITLE].set_stitch_value(role_t::VCR_STATUS, + role_t::VCR_STATUS); + } else { + this->tss_fields[TSF_FILES_TITLE].set_value(" " ANSI_ROLE("F") "iles ", + role_t::VCR_STATUS_HOTKEY); + if (lnav_data.ld_active_files.fc_name_to_errors.empty()) { + this->tss_fields[TSF_FILES_TITLE].set_role( + role_t::VCR_STATUS_DISABLED_TITLE); + } else { + this->tss_fields[TSF_FILES_TITLE].set_role( + role_t::VCR_ALERT_STATUS); + + auto& fc = lnav_data.ld_active_files; + if (fc.fc_name_to_errors.size() == 1) { + this->tss_error.set_value(" error: a file cannot be opened "); + } else { + this->tss_error.set_value( + " error: %u files cannot be opened ", + lnav_data.ld_active_files.fc_name_to_errors.size()); + } + } + this->tss_fields[TSF_FILES_RIGHT_STITCH].set_stitch_value( + role_t::VCR_STATUS_STITCH_NORMAL_TO_TITLE, + role_t::VCR_STATUS_STITCH_TITLE_TO_NORMAL); + this->tss_fields[TSF_TITLE].set_value(" " ANSI_ROLE("T") "ext Filters ", + role_t::VCR_STATUS_TITLE_HOTKEY); + this->tss_fields[TSF_TITLE].set_role(role_t::VCR_STATUS_TITLE); + this->tss_fields[TSF_STITCH_TITLE].set_stitch_value( + role_t::VCR_STATUS_STITCH_TITLE_TO_NORMAL, + role_t::VCR_STATUS_STITCH_NORMAL_TO_TITLE); + } + + lnav_data.ld_view_stack.top() | [this](auto tc) { + text_sub_source* tss = tc->get_sub_source(); + if (tss == nullptr) { + return; + } + + filter_stack& fs = tss->get_filters(); + auto enabled_count = 0, filter_count = 0; + + for (const auto& tf : fs) { + if (tf->is_enabled()) { + enabled_count += 1; + } + filter_count += 1; + } + if (filter_count == 0) { + this->tss_fields[TSF_COUNT].set_value(""); + } else { + this->tss_fields[TSF_COUNT].set_value( + " " ANSI_BOLD("%d") " of " ANSI_BOLD("%d") " enabled ", + enabled_count, + filter_count); + } + }; + + return TSF__MAX; +} + +status_field& +filter_status_source::statusview_value_for_field(int field) +{ + if (field == TSF_FILTERED + && !lnav_data.ld_active_files.fc_name_to_errors.empty()) + { + return this->tss_error; + } + + return this->tss_fields[field]; +} + +void +filter_status_source::update_filtered(text_sub_source* tss) +{ + if (tss == nullptr) { + return; + } + + auto& sf = this->tss_fields[TSF_FILTERED]; + + if (tss->get_filtered_count() == 0) { + if (tss->tss_apply_filters) { + sf.clear(); + } else { + sf.set_value( + " \u2718 Filtering disabled, re-enable with " ANSI_BOLD_START + ":toggle-filtering" ANSI_NORM); + } + } else { + auto& timer = ui_periodic_timer::singleton(); + auto& al = sf.get_value(); + + if (tss->get_filtered_count() == this->bss_last_filtered_count) { + if (timer.fade_diff(this->bss_filter_counter) == 0) { + this->tss_fields[TSF_FILTERED].set_role(role_t::VCR_STATUS); + al.with_attr(string_attr(line_range{0, -1}, + VC_STYLE.value(text_attrs{A_BOLD}))); + } + } else { + this->tss_fields[TSF_FILTERED].set_role(role_t::VCR_ALERT_STATUS); + this->bss_last_filtered_count = tss->get_filtered_count(); + timer.start_fade(this->bss_filter_counter, 3); + } + sf.set_value("%'9d Lines not shown ", tss->get_filtered_count()); + } +} + +filter_help_status_source::filter_help_status_source() +{ + this->fss_help.set_min_width(10); + this->fss_help.set_share(1); + this->fss_prompt.set_left_pad(1); + this->fss_prompt.set_min_width(35); + this->fss_prompt.set_share(1); + this->fss_error.set_left_pad(25); + this->fss_error.set_min_width(35); + this->fss_error.set_share(1); +} + +size_t +filter_help_status_source::statusview_fields() +{ + lnav_data.ld_view_stack.top() | [this](auto tc) { + text_sub_source* tss = tc->get_sub_source(); + if (tss == nullptr) { + return; + } + + if (lnav_data.ld_mode == ln_mode_t::FILTER) { + static auto* editor = injector::get(); + auto& lv = lnav_data.ld_filter_view; + auto& fs = tss->get_filters(); + + if (editor->fss_editing) { + auto tf = *(fs.begin() + lv.get_selection()); + auto lang = tf->get_lang() == filter_lang_t::SQL ? "an SQL" + : "a regular"; + + if (tf->get_type() == text_filter::type_t::INCLUDE) { + this->fss_help.set_value( + " " + "Enter %s expression to match lines to filter in:", + lang); + } else { + this->fss_help.set_value( + " " + "Enter %s expression to match lines to filter out:", + lang); + } + } else if (fs.empty()) { + this->fss_help.set_value(" %s", CREATE_HELP); + } else { + auto tf = *(fs.begin() + lv.get_selection()); + + this->fss_help.set_value( + " %s %s%s %s %s%s %s %s%s", + CREATE_HELP, + ENABLE_HELP, + tf->is_enabled() ? "Disable" : "Enable ", + EDIT_HELP, + TOGGLE_HELP, + tf->get_type() == text_filter::type_t::INCLUDE ? "OUT" + : "IN ", + DELETE_HELP, + FILTERING_HELP, + tss->tss_apply_filters ? "Disable Filtering" + : "Enable Filtering"); + } + } else if (lnav_data.ld_mode == ln_mode_t::FILES + && lnav_data.ld_session_loaded) + { + auto& lv = lnav_data.ld_files_view; + auto sel = files_model::from_selection(lv.get_selection()); + + sel.match( + [this](files_model::no_selection) { this->fss_help.clear(); }, + [this](files_model::error_selection) { + this->fss_help.set_value(" %s", CLOSE_HELP); + }, + [this](files_model::other_selection) { + this->fss_help.clear(); + }, + [this](files_model::file_selection& fs) { + auto& lss = lnav_data.ld_log_source; + auto vis_help = "Hide"; + auto ld_opt = lss.find_data(*fs.sb_iter); + if (ld_opt && !ld_opt.value()->ld_visible) { + vis_help = "Show"; + } + + this->fss_help.set_value( + " %s%s %s", ENABLE_HELP, vis_help, JUMP_HELP); + }); + } + }; + + return 1; +} + +status_field& +filter_help_status_source::statusview_value_for_field(int field) +{ + if (!this->fss_error.empty()) { + return this->fss_error; + } + + if (!this->fss_prompt.empty()) { + return this->fss_prompt; + } + + return this->fss_help; +} diff --git a/src/filter_status_source.hh b/src/filter_status_source.hh new file mode 100644 index 0000000..7d62966 --- /dev/null +++ b/src/filter_status_source.hh @@ -0,0 +1,82 @@ +/** + * Copyright (c) 2018, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef lnav_filter_status_source_hh +#define lnav_filter_status_source_hh + +#include + +#include "statusview_curses.hh" +#include "textview_curses.hh" + +class filter_status_source : public status_data_source { +public: + typedef enum { + TSF_FILES_TITLE, + TSF_FILES_RIGHT_STITCH, + TSF_TITLE, + TSF_STITCH_TITLE, + TSF_COUNT, + TSF_FILTERED, + TSF_HELP, + + TSF__MAX + } field_t; + + filter_status_source(); + + size_t statusview_fields() override; + + status_field& statusview_value_for_field(int field) override; + + void update_filtered(text_sub_source* tss); + +private: + status_field tss_fields[TSF__MAX]; + status_field tss_error; + int bss_last_filtered_count{0}; + sig_atomic_t bss_filter_counter{0}; +}; + +class filter_help_status_source : public status_data_source { +public: + filter_help_status_source(); + + size_t statusview_fields() override; + + status_field& statusview_value_for_field(int field) override; + + status_field fss_prompt{1024, role_t::VCR_STATUS}; + status_field fss_error{1024, role_t::VCR_ALERT_STATUS}; + +private: + status_field fss_help; +}; + +#endif diff --git a/src/filter_sub_source.cc b/src/filter_sub_source.cc new file mode 100644 index 0000000..a3bf6e4 --- /dev/null +++ b/src/filter_sub_source.cc @@ -0,0 +1,670 @@ +/** + * Copyright (c) 2018, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "filter_sub_source.hh" + +#include "base/enum_util.hh" +#include "base/func_util.hh" +#include "base/opt_util.hh" +#include "config.h" +#include "lnav.hh" +#include "readline_highlighters.hh" +#include "readline_possibilities.hh" + +using namespace lnav::roles::literals; + +filter_sub_source::filter_sub_source(std::shared_ptr editor) + : fss_editor(editor) +{ + this->fss_editor->set_save_history(!(lnav_data.ld_flags & LNF_SECURE_MODE)); + this->fss_regex_context.set_highlighter(readline_regex_highlighter) + .set_append_character(0); + this->fss_editor->add_context(filter_lang_t::REGEX, + this->fss_regex_context); + this->fss_sql_context.set_highlighter(readline_sqlite_highlighter) + .set_append_character(0); + this->fss_editor->add_context(filter_lang_t::SQL, this->fss_sql_context); + this->fss_editor->set_change_action( + bind_mem(&filter_sub_source::rl_change, this)); + this->fss_editor->set_perform_action( + bind_mem(&filter_sub_source::rl_perform, this)); + this->fss_editor->set_abort_action( + bind_mem(&filter_sub_source::rl_abort, this)); + this->fss_editor->set_display_match_action( + bind_mem(&filter_sub_source::rl_display_matches, this)); + this->fss_editor->set_display_next_action( + bind_mem(&filter_sub_source::rl_display_next, this)); + this->fss_match_view.set_sub_source(&this->fss_match_source); + this->fss_match_view.set_height(0_vl); + this->fss_match_view.set_show_scrollbar(true); + this->fss_match_view.set_default_role(role_t::VCR_POPUP); +} + +bool +filter_sub_source::list_input_handle_key(listview_curses& lv, int ch) +{ + if (this->fss_editing) { + switch (ch) { + case KEY_CTRL_RBRACKET: + this->fss_editor->abort(); + return true; + default: + this->fss_editor->handle_key(ch); + return true; + } + } + + switch (ch) { + case 'f': { + auto* top_view = *lnav_data.ld_view_stack.top(); + auto* tss = top_view->get_sub_source(); + + tss->toggle_apply_filters(); + top_view->reload_data(); + break; + } + case ' ': { + textview_curses* top_view = *lnav_data.ld_view_stack.top(); + text_sub_source* tss = top_view->get_sub_source(); + filter_stack& fs = tss->get_filters(); + + if (fs.empty()) { + return true; + } + + auto tf = *(fs.begin() + lv.get_selection()); + + fs.set_filter_enabled(tf, !tf->is_enabled()); + tss->text_filters_changed(); + lv.reload_data(); + top_view->reload_data(); + return true; + } + case 't': { + textview_curses* top_view = *lnav_data.ld_view_stack.top(); + text_sub_source* tss = top_view->get_sub_source(); + filter_stack& fs = tss->get_filters(); + + if (fs.empty()) { + return true; + } + + auto tf = *(fs.begin() + lv.get_selection()); + + if (tf->get_type() == text_filter::INCLUDE) { + tf->set_type(text_filter::EXCLUDE); + } else { + tf->set_type(text_filter::INCLUDE); + } + + tss->text_filters_changed(); + lv.reload_data(); + top_view->reload_data(); + return true; + } + case 'D': { + textview_curses* top_view = *lnav_data.ld_view_stack.top(); + text_sub_source* tss = top_view->get_sub_source(); + filter_stack& fs = tss->get_filters(); + + if (fs.empty()) { + return true; + } + + auto tf = *(fs.begin() + lv.get_selection()); + + fs.delete_filter(tf->get_id()); + lv.reload_data(); + tss->text_filters_changed(); + top_view->reload_data(); + return true; + } + case 'i': { + textview_curses* top_view = *lnav_data.ld_view_stack.top(); + text_sub_source* tss = top_view->get_sub_source(); + filter_stack& fs = tss->get_filters(); + auto filter_index = fs.next_index(); + + if (!filter_index) { + lnav_data.ld_filter_help_status_source.fss_error.set_value( + "error: too many filters"); + return true; + } + + auto ef = std::make_shared( + text_filter::type_t::INCLUDE, *filter_index); + fs.add_filter(ef); + lv.set_selection(vis_line_t(fs.size() - 1)); + lv.reload_data(); + + this->fss_editing = true; + + add_view_text_possibilities(this->fss_editor.get(), + filter_lang_t::REGEX, + "*", + top_view, + text_quoting::regex); + this->fss_editor->set_window(lv.get_window()); + this->fss_editor->set_visible(true); + this->fss_editor->set_y( + lv.get_y() + (int) (lv.get_selection() - lv.get_top())); + this->fss_editor->set_left(25); + this->fss_editor->set_width(this->tss_view->get_width() - 8 - 1); + this->fss_editor->focus(filter_lang_t::REGEX, "", ""); + this->fss_filter_state = true; + ef->disable(); + return true; + } + case 'o': { + auto* top_view = *lnav_data.ld_view_stack.top(); + auto* tss = top_view->get_sub_source(); + auto& fs = tss->get_filters(); + auto filter_index = fs.next_index(); + + if (!filter_index) { + lnav_data.ld_filter_help_status_source.fss_error.set_value( + "error: too many filters"); + return true; + } + + auto ef = std::make_shared( + text_filter::type_t::EXCLUDE, *filter_index); + fs.add_filter(ef); + lv.set_selection(vis_line_t(fs.size() - 1)); + lv.reload_data(); + + this->fss_editing = true; + + add_view_text_possibilities(this->fss_editor.get(), + filter_lang_t::REGEX, + "*", + top_view, + text_quoting::regex); + this->fss_editor->set_window(lv.get_window()); + this->fss_editor->set_visible(true); + this->fss_editor->set_y( + lv.get_y() + (int) (lv.get_selection() - lv.get_top())); + this->fss_editor->set_left(25); + this->fss_editor->set_width(this->tss_view->get_width() - 8 - 1); + this->fss_editor->focus(filter_lang_t::REGEX, "", ""); + this->fss_filter_state = true; + ef->disable(); + return true; + } + case '\r': + case KEY_ENTER: { + textview_curses* top_view = *lnav_data.ld_view_stack.top(); + text_sub_source* tss = top_view->get_sub_source(); + filter_stack& fs = tss->get_filters(); + + if (fs.empty()) { + return true; + } + + auto tf = *(fs.begin() + lv.get_selection()); + + this->fss_editing = true; + + auto tq = tf->get_lang() == filter_lang_t::SQL + ? text_quoting::sql + : text_quoting::regex; + add_view_text_possibilities( + this->fss_editor.get(), tf->get_lang(), "*", top_view, tq); + add_filter_expr_possibilities( + this->fss_editor.get(), filter_lang_t::SQL, "*"); + this->fss_editor->set_window(lv.get_window()); + this->fss_editor->set_visible(true); + this->fss_editor->set_y( + lv.get_y() + (int) (lv.get_selection() - lv.get_top())); + this->fss_editor->set_left(25); + this->fss_editor->set_width(this->tss_view->get_width() - 8 - 1); + this->fss_editor->focus(tf->get_lang(), ""); + this->fss_editor->rewrite_line(0, tf->get_id().c_str()); + this->fss_filter_state = tf->is_enabled(); + tf->disable(); + tss->text_filters_changed(); + return true; + } + case 'n': { + execute_command(lnav_data.ld_exec_context, "next-mark search"); + return true; + } + case 'N': { + execute_command(lnav_data.ld_exec_context, "prev-mark search"); + return true; + } + case '/': { + execute_command(lnav_data.ld_exec_context, "prompt search-filters"); + return true; + } + default: + log_debug("unhandled %x", ch); + break; + } + + return false; +} + +size_t +filter_sub_source::text_line_count() +{ + return (lnav_data.ld_view_stack.top() | + [](auto tc) { + text_sub_source* tss = tc->get_sub_source(); + filter_stack& fs = tss->get_filters(); + + return nonstd::make_optional(fs.size()); + }) + .value_or(0); +} + +size_t +filter_sub_source::text_line_width(textview_curses& curses) +{ + textview_curses* top_view = *lnav_data.ld_view_stack.top(); + text_sub_source* tss = top_view->get_sub_source(); + filter_stack& fs = tss->get_filters(); + size_t retval = 0; + + for (auto& filter : fs) { + retval = std::max(filter->get_id().size() + 8, retval); + } + + return retval; +} + +void +filter_sub_source::text_value_for_line(textview_curses& tc, + int line, + std::string& value_out, + text_sub_source::line_flags_t flags) +{ + auto* top_view = *lnav_data.ld_view_stack.top(); + auto* tss = top_view->get_sub_source(); + auto& fs = tss->get_filters(); + auto tf = *(fs.begin() + line); + + value_out = " "; + switch (tf->get_type()) { + case text_filter::INCLUDE: + value_out.append(" IN "); + break; + case text_filter::EXCLUDE: + if (tf->get_lang() == filter_lang_t::REGEX) { + value_out.append("OUT "); + } else { + value_out.append(" "); + } + break; + default: + ensure(0); + break; + } + + if (this->fss_editing && line == tc.get_selection()) { + fmt::format_to( + std::back_inserter(value_out), FMT_STRING("{:>9} hits | "), "-"); + } else { + fmt::format_to(std::back_inserter(value_out), + FMT_STRING("{:>9L} hits | "), + tss->get_filtered_count_for(tf->get_index())); + } + + value_out.append(tf->get_id()); +} + +void +filter_sub_source::text_attrs_for_line(textview_curses& tc, + int line, + string_attrs_t& value_out) +{ + textview_curses* top_view = *lnav_data.ld_view_stack.top(); + text_sub_source* tss = top_view->get_sub_source(); + filter_stack& fs = tss->get_filters(); + auto tf = *(fs.begin() + line); + bool selected + = lnav_data.ld_mode == ln_mode_t::FILTER && line == tc.get_selection(); + + if (selected) { + value_out.emplace_back(line_range{0, 1}, VC_GRAPHIC.value(ACS_RARROW)); + } + + chtype enabled = tf->is_enabled() ? ACS_DIAMOND : ' '; + + line_range lr{2, 3}; + value_out.emplace_back(lr, VC_GRAPHIC.value(enabled)); + if (tf->is_enabled()) { + value_out.emplace_back(lr, VC_FOREGROUND.value(COLOR_GREEN)); + } + + if (selected) { + value_out.emplace_back(line_range{0, -1}, + VC_ROLE.value(role_t::VCR_FOCUSED)); + } + + role_t fg_role = tf->get_type() == text_filter::INCLUDE ? role_t::VCR_OK + : role_t::VCR_ERROR; + value_out.emplace_back(line_range{4, 7}, VC_ROLE.value(fg_role)); + value_out.emplace_back(line_range{4, 7}, + VC_STYLE.value(text_attrs{A_BOLD})); + + value_out.emplace_back(line_range{8, 17}, + VC_STYLE.value(text_attrs{A_BOLD})); + value_out.emplace_back(line_range{23, 24}, VC_GRAPHIC.value(ACS_VLINE)); + + attr_line_t content{tf->get_id()}; + auto& content_attrs = content.get_attrs(); + + switch (tf->get_lang()) { + case filter_lang_t::REGEX: + readline_regex_highlighter(content, content.length()); + break; + case filter_lang_t::SQL: + readline_sqlite_highlighter(content, content.length()); + break; + case filter_lang_t::NONE: + break; + } + + shift_string_attrs(content_attrs, 0, 25); + value_out.insert( + value_out.end(), content_attrs.begin(), content_attrs.end()); +} + +size_t +filter_sub_source::text_size_for_line(textview_curses& tc, + int line, + text_sub_source::line_flags_t raw) +{ + textview_curses* top_view = *lnav_data.ld_view_stack.top(); + text_sub_source* tss = top_view->get_sub_source(); + filter_stack& fs = tss->get_filters(); + auto tf = *(fs.begin() + line); + + return 8 + tf->get_id().size(); +} + +void +filter_sub_source::rl_change(readline_curses* rc) +{ + textview_curses* top_view = *lnav_data.ld_view_stack.top(); + text_sub_source* tss = top_view->get_sub_source(); + filter_stack& fs = tss->get_filters(); + if (fs.empty()) { + return; + } + + auto iter = fs.begin() + this->tss_view->get_selection(); + auto tf = *iter; + auto new_value = rc->get_line_buffer(); + + switch (tf->get_lang()) { + case filter_lang_t::NONE: + break; + case filter_lang_t::REGEX: { + auto regex_res + = lnav::pcre2pp::code::from(new_value, PCRE2_CASELESS); + + if (regex_res.isErr()) { + auto pe = regex_res.unwrapErr(); + lnav_data.ld_filter_help_status_source.fss_error.set_value( + "error: %s", pe.get_message().c_str()); + } else { + auto& hm = top_view->get_highlights(); + highlighter hl(regex_res.unwrap().to_shared()); + auto role = tf->get_type() == text_filter::EXCLUDE + ? role_t::VCR_DIFF_DELETE + : role_t::VCR_DIFF_ADD; + hl.with_role(role); + hl.with_attrs(text_attrs{A_BLINK | A_REVERSE}); + + hm[{highlight_source_t::PREVIEW, "preview"}] = hl; + top_view->set_needs_update(); + lnav_data.ld_filter_help_status_source.fss_error.clear(); + } + break; + } + case filter_lang_t::SQL: { + auto full_sql + = fmt::format(FMT_STRING("SELECT 1 WHERE {}"), new_value); + auto_mem stmt(sqlite3_finalize); +#ifdef SQLITE_PREPARE_PERSISTENT + auto retcode = sqlite3_prepare_v3(lnav_data.ld_db.in(), + full_sql.c_str(), + full_sql.size(), + SQLITE_PREPARE_PERSISTENT, + stmt.out(), + nullptr); +#else + auto retcode = sqlite3_prepare_v2(lnav_data.ld_db.in(), + full_sql.c_str(), + full_sql.size(), + stmt.out(), + nullptr); +#endif + if (retcode != SQLITE_OK) { + lnav_data.ld_filter_help_status_source.fss_error.set_value( + "error: %s", sqlite3_errmsg(lnav_data.ld_db)); + } else { + auto set_res = lnav_data.ld_log_source.set_preview_sql_filter( + stmt.release()); + + if (set_res.isErr()) { + lnav_data.ld_filter_help_status_source.fss_error.set_value( + "error: %s", + set_res.unwrapErr() + .to_attr_line() + .get_string() + .c_str()); + } else { + top_view->set_needs_update(); + lnav_data.ld_filter_help_status_source.fss_error.clear(); + } + } + break; + } + } +} + +void +filter_sub_source::rl_perform(readline_curses* rc) +{ + static const intern_string_t INPUT_SRC = intern_string::lookup("input"); + + textview_curses* top_view = *lnav_data.ld_view_stack.top(); + text_sub_source* tss = top_view->get_sub_source(); + filter_stack& fs = tss->get_filters(); + auto iter = fs.begin() + this->tss_view->get_selection(); + auto tf = *iter; + auto new_value = rc->get_value().get_string(); + + if (new_value.empty()) { + this->rl_abort(rc); + } else { + top_view->get_highlights().erase( + {highlight_source_t::PREVIEW, "preview"}); + switch (tf->get_lang()) { + case filter_lang_t::NONE: + case filter_lang_t::REGEX: { + auto compile_res + = lnav::pcre2pp::code::from(new_value, PCRE2_CASELESS); + + if (compile_res.isErr()) { + auto ce = compile_res.unwrapErr(); + auto um = lnav::console::to_user_message(INPUT_SRC, ce); + lnav_data.ld_exec_context.ec_error_callback_stack.back()( + um); + this->rl_abort(rc); + } else { + tf->lf_deleted = true; + tss->text_filters_changed(); + + auto pf = std::make_shared( + tf->get_type(), + new_value, + tf->get_index(), + compile_res.unwrap().to_shared()); + + *iter = pf; + tss->text_filters_changed(); + } + break; + } + case filter_lang_t::SQL: { + auto full_sql + = fmt::format(FMT_STRING("SELECT 1 WHERE {}"), new_value); + auto_mem stmt(sqlite3_finalize); +#ifdef SQLITE_PREPARE_PERSISTENT + auto retcode = sqlite3_prepare_v3(lnav_data.ld_db.in(), + full_sql.c_str(), + full_sql.size(), + SQLITE_PREPARE_PERSISTENT, + stmt.out(), + nullptr); +#else + auto retcode = sqlite3_prepare_v2(lnav_data.ld_db.in(), + full_sql.c_str(), + full_sql.size(), + stmt.out(), + nullptr); +#endif + if (retcode != SQLITE_OK) { + auto sqlerr = annotate_sql_with_error( + lnav_data.ld_db.in(), full_sql.c_str(), nullptr); + auto um + = lnav::console::user_message::error( + "invalid SQL expression") + .with_reason(sqlite3_errmsg(lnav_data.ld_db.in())) + .with_snippet(lnav::console::snippet::from( + INPUT_SRC, sqlerr)); + lnav_data.ld_exec_context.ec_error_callback_stack.back()( + um); + this->rl_abort(rc); + } else { + lnav_data.ld_log_source.set_sql_filter(new_value, + stmt.release()); + tss->text_filters_changed(); + } + break; + } + } + } + + lnav_data.ld_log_source.set_preview_sql_filter(nullptr); + lnav_data.ld_filter_help_status_source.fss_prompt.clear(); + this->fss_editing = false; + this->fss_editor->set_visible(false); + top_view->reload_data(); + this->tss_view->reload_data(); +} + +void +filter_sub_source::rl_abort(readline_curses* rc) +{ + textview_curses* top_view = *lnav_data.ld_view_stack.top(); + text_sub_source* tss = top_view->get_sub_source(); + filter_stack& fs = tss->get_filters(); + auto iter = fs.begin() + this->tss_view->get_selection(); + auto tf = *iter; + + lnav_data.ld_log_source.set_preview_sql_filter(nullptr); + lnav_data.ld_filter_help_status_source.fss_prompt.clear(); + lnav_data.ld_filter_help_status_source.fss_error.clear(); + top_view->get_highlights().erase({highlight_source_t::PREVIEW, "preview"}); + top_view->reload_data(); + fs.delete_filter(""); + this->tss_view->reload_data(); + this->fss_editor->set_visible(false); + this->fss_editing = false; + this->tss_view->set_needs_update(); + tf->set_enabled(this->fss_filter_state); + tss->text_filters_changed(); + this->tss_view->reload_data(); +} + +void +filter_sub_source::rl_display_matches(readline_curses* rc) +{ + const std::vector& matches = rc->get_matches(); + unsigned long width = 0; + + if (matches.empty()) { + this->fss_match_source.clear(); + this->fss_match_view.set_height(0_vl); + this->tss_view->set_needs_update(); + } else { + auto current_match = rc->get_match_string(); + attr_line_t al; + vis_line_t line, selected_line; + + for (const auto& match : matches) { + if (match == current_match) { + al.append(match, VC_STYLE.value(text_attrs{A_REVERSE})); + selected_line = line; + } else { + al.append(match); + } + al.append(1, '\n'); + width = std::max(width, (unsigned long) match.size()); + line += 1_vl; + } + + this->fss_match_view.set_selection(selected_line); + this->fss_match_source.replace_with(al); + this->fss_match_view.set_height( + std::min(vis_line_t(matches.size()), 3_vl)); + } + + this->fss_match_view.set_window(this->tss_view->get_window()); + this->fss_match_view.set_y(rc->get_y() + 1); + this->fss_match_view.set_x(rc->get_left() + rc->get_match_start()); + this->fss_match_view.set_width(width + 3); + this->fss_match_view.set_needs_update(); + this->fss_match_view.scroll_selection_into_view(); + this->fss_match_view.reload_data(); +} + +void +filter_sub_source::rl_display_next(readline_curses* rc) +{ + textview_curses& tc = this->fss_match_view; + + if (tc.get_top() >= (tc.get_top_for_last_row() - 1)) { + tc.set_top(0_vl); + } else { + tc.shift_top(tc.get_height()); + } +} + +void +filter_sub_source::list_input_handle_scroll_out(listview_curses& lv) +{ + lnav_data.ld_mode = ln_mode_t::PAGING; + lnav_data.ld_filter_view.reload_data(); +} diff --git a/src/filter_sub_source.hh b/src/filter_sub_source.hh new file mode 100644 index 0000000..11587da --- /dev/null +++ b/src/filter_sub_source.hh @@ -0,0 +1,92 @@ +/** + * Copyright (c) 2018, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef filter_sub_source_hh +#define filter_sub_source_hh + +#include "base/injector.hh" +#include "plain_text_source.hh" +#include "readline_curses.hh" +#include "textview_curses.hh" + +class filter_sub_source + : public text_sub_source + , public list_input_delegate { +public: + filter_sub_source(std::shared_ptr editor); + + using injectable + = filter_sub_source(std::shared_ptr editor); + + filter_sub_source(const filter_sub_source*) = delete; + + ~filter_sub_source() override = default; + + bool list_input_handle_key(listview_curses& lv, int ch) override; + + void list_input_handle_scroll_out(listview_curses& lv) override; + + size_t text_line_count() override; + + size_t text_line_width(textview_curses& curses) override; + + void text_value_for_line(textview_curses& tc, + int line, + std::string& value_out, + line_flags_t flags) override; + + void text_attrs_for_line(textview_curses& tc, + int line, + string_attrs_t& value_out) override; + + size_t text_size_for_line(textview_curses& tc, + int line, + line_flags_t raw) override; + + void rl_change(readline_curses* rc); + + void rl_perform(readline_curses* rc); + + void rl_abort(readline_curses* rc); + + void rl_display_matches(readline_curses* rc); + + void rl_display_next(readline_curses* rc); + + readline_context fss_regex_context{"filter-regex", nullptr, false}; + readline_context fss_sql_context{"filter-sql", nullptr, false}; + std::shared_ptr fss_editor; + plain_text_source fss_match_source; + textview_curses fss_match_view; + + bool fss_editing{false}; + bool fss_filter_state{false}; +}; + +#endif diff --git a/src/fmtlib/Makefile.am b/src/fmtlib/Makefile.am new file mode 100644 index 0000000..6c6fa12 --- /dev/null +++ b/src/fmtlib/Makefile.am @@ -0,0 +1,22 @@ + +noinst_HEADERS = \ + fmt/args.h \ + fmt/chrono.h \ + fmt/color.h \ + fmt/compile.h \ + fmt/core.h \ + fmt/format-inl.h \ + fmt/format.h \ + fmt/locale.h \ + fmt/os.h \ + fmt/ostream.h \ + fmt/printf.h \ + fmt/ranges.h \ + fmt/std.h \ + fmt/xchar.h + +noinst_LIBRARIES = libcppfmt.a + +libcppfmt_a_SOURCES = \ + format.cc \ + os.cc diff --git a/src/fmtlib/fmt/args.h b/src/fmtlib/fmt/args.h new file mode 100644 index 0000000..a3966d1 --- /dev/null +++ b/src/fmtlib/fmt/args.h @@ -0,0 +1,234 @@ +// Formatting library for C++ - dynamic format arguments +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#ifndef FMT_ARGS_H_ +#define FMT_ARGS_H_ + +#include // std::reference_wrapper +#include // std::unique_ptr +#include + +#include "core.h" + +FMT_BEGIN_NAMESPACE + +namespace detail { + +template struct is_reference_wrapper : std::false_type {}; +template +struct is_reference_wrapper> : std::true_type {}; + +template const T& unwrap(const T& v) { return v; } +template const T& unwrap(const std::reference_wrapper& v) { + return static_cast(v); +} + +class dynamic_arg_list { + // Workaround for clang's -Wweak-vtables. Unlike for regular classes, for + // templates it doesn't complain about inability to deduce single translation + // unit for placing vtable. So storage_node_base is made a fake template. + template struct node { + virtual ~node() = default; + std::unique_ptr> next; + }; + + template struct typed_node : node<> { + T value; + + template + FMT_CONSTEXPR typed_node(const Arg& arg) : value(arg) {} + + template + FMT_CONSTEXPR typed_node(const basic_string_view& arg) + : value(arg.data(), arg.size()) {} + }; + + std::unique_ptr> head_; + + public: + template const T& push(const Arg& arg) { + auto new_node = std::unique_ptr>(new typed_node(arg)); + auto& value = new_node->value; + new_node->next = std::move(head_); + head_ = std::move(new_node); + return value; + } +}; +} // namespace detail + +/** + \rst + A dynamic version of `fmt::format_arg_store`. + It's equipped with a storage to potentially temporary objects which lifetimes + could be shorter than the format arguments object. + + It can be implicitly converted into `~fmt::basic_format_args` for passing + into type-erased formatting functions such as `~fmt::vformat`. + \endrst + */ +template +class dynamic_format_arg_store +#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 + // Workaround a GCC template argument substitution bug. + : public basic_format_args +#endif +{ + private: + using char_type = typename Context::char_type; + + template struct need_copy { + static constexpr detail::type mapped_type = + detail::mapped_type_constant::value; + + enum { + value = !(detail::is_reference_wrapper::value || + std::is_same>::value || + std::is_same>::value || + (mapped_type != detail::type::cstring_type && + mapped_type != detail::type::string_type && + mapped_type != detail::type::custom_type)) + }; + }; + + template + using stored_type = conditional_t< + std::is_convertible>::value && + !detail::is_reference_wrapper::value, + std::basic_string, T>; + + // Storage of basic_format_arg must be contiguous. + std::vector> data_; + std::vector> named_info_; + + // Storage of arguments not fitting into basic_format_arg must grow + // without relocation because items in data_ refer to it. + detail::dynamic_arg_list dynamic_args_; + + friend class basic_format_args; + + unsigned long long get_types() const { + return detail::is_unpacked_bit | data_.size() | + (named_info_.empty() + ? 0ULL + : static_cast(detail::has_named_args_bit)); + } + + const basic_format_arg* data() const { + return named_info_.empty() ? data_.data() : data_.data() + 1; + } + + template void emplace_arg(const T& arg) { + data_.emplace_back(detail::make_arg(arg)); + } + + template + void emplace_arg(const detail::named_arg& arg) { + if (named_info_.empty()) { + constexpr const detail::named_arg_info* zero_ptr{nullptr}; + data_.insert(data_.begin(), {zero_ptr, 0}); + } + data_.emplace_back(detail::make_arg(detail::unwrap(arg.value))); + auto pop_one = [](std::vector>* data) { + data->pop_back(); + }; + std::unique_ptr>, decltype(pop_one)> + guard{&data_, pop_one}; + named_info_.push_back({arg.name, static_cast(data_.size() - 2u)}); + data_[0].value_.named_args = {named_info_.data(), named_info_.size()}; + guard.release(); + } + + public: + constexpr dynamic_format_arg_store() = default; + + /** + \rst + Adds an argument into the dynamic store for later passing to a formatting + function. + + Note that custom types and string types (but not string views) are copied + into the store dynamically allocating memory if necessary. + + **Example**:: + + fmt::dynamic_format_arg_store store; + store.push_back(42); + store.push_back("abc"); + store.push_back(1.5f); + std::string result = fmt::vformat("{} and {} and {}", store); + \endrst + */ + template void push_back(const T& arg) { + if (detail::const_check(need_copy::value)) + emplace_arg(dynamic_args_.push>(arg)); + else + emplace_arg(detail::unwrap(arg)); + } + + /** + \rst + Adds a reference to the argument into the dynamic store for later passing to + a formatting function. + + **Example**:: + + fmt::dynamic_format_arg_store store; + char band[] = "Rolling Stones"; + store.push_back(std::cref(band)); + band[9] = 'c'; // Changing str affects the output. + std::string result = fmt::vformat("{}", store); + // result == "Rolling Scones" + \endrst + */ + template void push_back(std::reference_wrapper arg) { + static_assert( + need_copy::value, + "objects of built-in types and string views are always copied"); + emplace_arg(arg.get()); + } + + /** + Adds named argument into the dynamic store for later passing to a formatting + function. ``std::reference_wrapper`` is supported to avoid copying of the + argument. The name is always copied into the store. + */ + template + void push_back(const detail::named_arg& arg) { + const char_type* arg_name = + dynamic_args_.push>(arg.name).c_str(); + if (detail::const_check(need_copy::value)) { + emplace_arg( + fmt::arg(arg_name, dynamic_args_.push>(arg.value))); + } else { + emplace_arg(fmt::arg(arg_name, arg.value)); + } + } + + /** Erase all elements from the store */ + void clear() { + data_.clear(); + named_info_.clear(); + dynamic_args_ = detail::dynamic_arg_list(); + } + + /** + \rst + Reserves space to store at least *new_cap* arguments including + *new_cap_named* named arguments. + \endrst + */ + void reserve(size_t new_cap, size_t new_cap_named) { + FMT_ASSERT(new_cap >= new_cap_named, + "Set of arguments includes set of named arguments"); + data_.reserve(new_cap); + named_info_.reserve(new_cap_named); + } +}; + +FMT_END_NAMESPACE + +#endif // FMT_ARGS_H_ diff --git a/src/fmtlib/fmt/chrono.h b/src/fmtlib/fmt/chrono.h new file mode 100644 index 0000000..c3c52bf --- /dev/null +++ b/src/fmtlib/fmt/chrono.h @@ -0,0 +1,2080 @@ +// Formatting library for C++ - chrono support +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#ifndef FMT_CHRONO_H_ +#define FMT_CHRONO_H_ + +#include +#include +#include // std::isfinite +#include // std::memcpy +#include +#include +#include +#include +#include + +#include "format.h" + +FMT_BEGIN_NAMESPACE + +// Enable tzset. +#ifndef FMT_USE_TZSET +// UWP doesn't provide _tzset. +# if FMT_HAS_INCLUDE("winapifamily.h") +# include +# endif +# if defined(_WIN32) && (!defined(WINAPI_FAMILY) || \ + (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP)) +# define FMT_USE_TZSET 1 +# else +# define FMT_USE_TZSET 0 +# endif +#endif + +// Enable safe chrono durations, unless explicitly disabled. +#ifndef FMT_SAFE_DURATION_CAST +# define FMT_SAFE_DURATION_CAST 1 +#endif +#if FMT_SAFE_DURATION_CAST + +// For conversion between std::chrono::durations without undefined +// behaviour or erroneous results. +// This is a stripped down version of duration_cast, for inclusion in fmt. +// See https://github.com/pauldreik/safe_duration_cast +// +// Copyright Paul Dreik 2019 +namespace safe_duration_cast { + +template ::value && + std::numeric_limits::is_signed == + std::numeric_limits::is_signed)> +FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) { + ec = 0; + using F = std::numeric_limits; + using T = std::numeric_limits; + static_assert(F::is_integer, "From must be integral"); + static_assert(T::is_integer, "To must be integral"); + + // A and B are both signed, or both unsigned. + if (detail::const_check(F::digits <= T::digits)) { + // From fits in To without any problem. + } else { + // From does not always fit in To, resort to a dynamic check. + if (from < (T::min)() || from > (T::max)()) { + // outside range. + ec = 1; + return {}; + } + } + return static_cast(from); +} + +/** + * converts From to To, without loss. If the dynamic value of from + * can't be converted to To without loss, ec is set. + */ +template ::value && + std::numeric_limits::is_signed != + std::numeric_limits::is_signed)> +FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) { + ec = 0; + using F = std::numeric_limits; + using T = std::numeric_limits; + static_assert(F::is_integer, "From must be integral"); + static_assert(T::is_integer, "To must be integral"); + + if (detail::const_check(F::is_signed && !T::is_signed)) { + // From may be negative, not allowed! + if (fmt::detail::is_negative(from)) { + ec = 1; + return {}; + } + // From is positive. Can it always fit in To? + if (detail::const_check(F::digits > T::digits) && + from > static_cast(detail::max_value())) { + ec = 1; + return {}; + } + } + + if (detail::const_check(!F::is_signed && T::is_signed && + F::digits >= T::digits) && + from > static_cast(detail::max_value())) { + ec = 1; + return {}; + } + return static_cast(from); // Lossless conversion. +} + +template ::value)> +FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) { + ec = 0; + return from; +} // function + +// clang-format off +/** + * converts From to To if possible, otherwise ec is set. + * + * input | output + * ---------------------------------|--------------- + * NaN | NaN + * Inf | Inf + * normal, fits in output | converted (possibly lossy) + * normal, does not fit in output | ec is set + * subnormal | best effort + * -Inf | -Inf + */ +// clang-format on +template ::value)> +FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) { + ec = 0; + using T = std::numeric_limits; + static_assert(std::is_floating_point::value, "From must be floating"); + static_assert(std::is_floating_point::value, "To must be floating"); + + // catch the only happy case + if (std::isfinite(from)) { + if (from >= T::lowest() && from <= (T::max)()) { + return static_cast(from); + } + // not within range. + ec = 1; + return {}; + } + + // nan and inf will be preserved + return static_cast(from); +} // function + +template ::value)> +FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) { + ec = 0; + static_assert(std::is_floating_point::value, "From must be floating"); + return from; +} + +/** + * safe duration cast between integral durations + */ +template ::value), + FMT_ENABLE_IF(std::is_integral::value)> +To safe_duration_cast(std::chrono::duration from, + int& ec) { + using From = std::chrono::duration; + ec = 0; + // the basic idea is that we need to convert from count() in the from type + // to count() in the To type, by multiplying it with this: + struct Factor + : std::ratio_divide {}; + + static_assert(Factor::num > 0, "num must be positive"); + static_assert(Factor::den > 0, "den must be positive"); + + // the conversion is like this: multiply from.count() with Factor::num + // /Factor::den and convert it to To::rep, all this without + // overflow/underflow. let's start by finding a suitable type that can hold + // both To, From and Factor::num + using IntermediateRep = + typename std::common_type::type; + + // safe conversion to IntermediateRep + IntermediateRep count = + lossless_integral_conversion(from.count(), ec); + if (ec) return {}; + // multiply with Factor::num without overflow or underflow + if (detail::const_check(Factor::num != 1)) { + const auto max1 = detail::max_value() / Factor::num; + if (count > max1) { + ec = 1; + return {}; + } + const auto min1 = + (std::numeric_limits::min)() / Factor::num; + if (count < min1) { + ec = 1; + return {}; + } + count *= Factor::num; + } + + if (detail::const_check(Factor::den != 1)) count /= Factor::den; + auto tocount = lossless_integral_conversion(count, ec); + return ec ? To() : To(tocount); +} + +/** + * safe duration_cast between floating point durations + */ +template ::value), + FMT_ENABLE_IF(std::is_floating_point::value)> +To safe_duration_cast(std::chrono::duration from, + int& ec) { + using From = std::chrono::duration; + ec = 0; + if (std::isnan(from.count())) { + // nan in, gives nan out. easy. + return To{std::numeric_limits::quiet_NaN()}; + } + // maybe we should also check if from is denormal, and decide what to do about + // it. + + // +-inf should be preserved. + if (std::isinf(from.count())) { + return To{from.count()}; + } + + // the basic idea is that we need to convert from count() in the from type + // to count() in the To type, by multiplying it with this: + struct Factor + : std::ratio_divide {}; + + static_assert(Factor::num > 0, "num must be positive"); + static_assert(Factor::den > 0, "den must be positive"); + + // the conversion is like this: multiply from.count() with Factor::num + // /Factor::den and convert it to To::rep, all this without + // overflow/underflow. let's start by finding a suitable type that can hold + // both To, From and Factor::num + using IntermediateRep = + typename std::common_type::type; + + // force conversion of From::rep -> IntermediateRep to be safe, + // even if it will never happen be narrowing in this context. + IntermediateRep count = + safe_float_conversion(from.count(), ec); + if (ec) { + return {}; + } + + // multiply with Factor::num without overflow or underflow + if (detail::const_check(Factor::num != 1)) { + constexpr auto max1 = detail::max_value() / + static_cast(Factor::num); + if (count > max1) { + ec = 1; + return {}; + } + constexpr auto min1 = std::numeric_limits::lowest() / + static_cast(Factor::num); + if (count < min1) { + ec = 1; + return {}; + } + count *= static_cast(Factor::num); + } + + // this can't go wrong, right? den>0 is checked earlier. + if (detail::const_check(Factor::den != 1)) { + using common_t = typename std::common_type::type; + count /= static_cast(Factor::den); + } + + // convert to the to type, safely + using ToRep = typename To::rep; + + const ToRep tocount = safe_float_conversion(count, ec); + if (ec) { + return {}; + } + return To{tocount}; +} +} // namespace safe_duration_cast +#endif + +// Prevents expansion of a preceding token as a function-style macro. +// Usage: f FMT_NOMACRO() +#define FMT_NOMACRO + +namespace detail { +template struct null {}; +inline null<> localtime_r FMT_NOMACRO(...) { return null<>(); } +inline null<> localtime_s(...) { return null<>(); } +inline null<> gmtime_r(...) { return null<>(); } +inline null<> gmtime_s(...) { return null<>(); } + +inline const std::locale& get_classic_locale() { + static const auto& locale = std::locale::classic(); + return locale; +} + +template struct codecvt_result { + static constexpr const size_t max_size = 32; + CodeUnit buf[max_size]; + CodeUnit* end; +}; +template +constexpr const size_t codecvt_result::max_size; + +template +void write_codecvt(codecvt_result& out, string_view in_buf, + const std::locale& loc) { +#if FMT_CLANG_VERSION +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wdeprecated" + auto& f = std::use_facet>(loc); +# pragma clang diagnostic pop +#else + auto& f = std::use_facet>(loc); +#endif + auto mb = std::mbstate_t(); + const char* from_next = nullptr; + auto result = f.in(mb, in_buf.begin(), in_buf.end(), from_next, + std::begin(out.buf), std::end(out.buf), out.end); + if (result != std::codecvt_base::ok) + FMT_THROW(format_error("failed to format time")); +} + +template +auto write_encoded_tm_str(OutputIt out, string_view in, const std::locale& loc) + -> OutputIt { + if (detail::is_utf8() && loc != get_classic_locale()) { + // char16_t and char32_t codecvts are broken in MSVC (linkage errors) and + // gcc-4. +#if FMT_MSC_VERSION != 0 || \ + (defined(__GLIBCXX__) && !defined(_GLIBCXX_USE_DUAL_ABI)) + // The _GLIBCXX_USE_DUAL_ABI macro is always defined in libstdc++ from gcc-5 + // and newer. + using code_unit = wchar_t; +#else + using code_unit = char32_t; +#endif + + using unit_t = codecvt_result; + unit_t unit; + write_codecvt(unit, in, loc); + // In UTF-8 is used one to four one-byte code units. + auto&& buf = basic_memory_buffer(); + for (code_unit* p = unit.buf; p != unit.end; ++p) { + uint32_t c = static_cast(*p); + if (sizeof(code_unit) == 2 && c >= 0xd800 && c <= 0xdfff) { + // surrogate pair + ++p; + if (p == unit.end || (c & 0xfc00) != 0xd800 || + (*p & 0xfc00) != 0xdc00) { + FMT_THROW(format_error("failed to format time")); + } + c = (c << 10) + static_cast(*p) - 0x35fdc00; + } + if (c < 0x80) { + buf.push_back(static_cast(c)); + } else if (c < 0x800) { + buf.push_back(static_cast(0xc0 | (c >> 6))); + buf.push_back(static_cast(0x80 | (c & 0x3f))); + } else if ((c >= 0x800 && c <= 0xd7ff) || (c >= 0xe000 && c <= 0xffff)) { + buf.push_back(static_cast(0xe0 | (c >> 12))); + buf.push_back(static_cast(0x80 | ((c & 0xfff) >> 6))); + buf.push_back(static_cast(0x80 | (c & 0x3f))); + } else if (c >= 0x10000 && c <= 0x10ffff) { + buf.push_back(static_cast(0xf0 | (c >> 18))); + buf.push_back(static_cast(0x80 | ((c & 0x3ffff) >> 12))); + buf.push_back(static_cast(0x80 | ((c & 0xfff) >> 6))); + buf.push_back(static_cast(0x80 | (c & 0x3f))); + } else { + FMT_THROW(format_error("failed to format time")); + } + } + return copy_str(buf.data(), buf.data() + buf.size(), out); + } + return copy_str(in.data(), in.data() + in.size(), out); +} + +template ::value)> +auto write_tm_str(OutputIt out, string_view sv, const std::locale& loc) + -> OutputIt { + codecvt_result unit; + write_codecvt(unit, sv, loc); + return copy_str(unit.buf, unit.end, out); +} + +template ::value)> +auto write_tm_str(OutputIt out, string_view sv, const std::locale& loc) + -> OutputIt { + return write_encoded_tm_str(out, sv, loc); +} + +template +inline void do_write(buffer& buf, const std::tm& time, + const std::locale& loc, char format, char modifier) { + auto&& format_buf = formatbuf>(buf); + auto&& os = std::basic_ostream(&format_buf); + os.imbue(loc); + using iterator = std::ostreambuf_iterator; + const auto& facet = std::use_facet>(loc); + auto end = facet.put(os, os, Char(' '), &time, format, modifier); + if (end.failed()) FMT_THROW(format_error("failed to format time")); +} + +template ::value)> +auto write(OutputIt out, const std::tm& time, const std::locale& loc, + char format, char modifier = 0) -> OutputIt { + auto&& buf = get_buffer(out); + do_write(buf, time, loc, format, modifier); + return buf.out(); +} + +template ::value)> +auto write(OutputIt out, const std::tm& time, const std::locale& loc, + char format, char modifier = 0) -> OutputIt { + auto&& buf = basic_memory_buffer(); + do_write(buf, time, loc, format, modifier); + return write_encoded_tm_str(out, string_view(buf.data(), buf.size()), loc); +} + +} // namespace detail + +FMT_MODULE_EXPORT_BEGIN + +/** + Converts given time since epoch as ``std::time_t`` value into calendar time, + expressed in local time. Unlike ``std::localtime``, this function is + thread-safe on most platforms. + */ +inline std::tm localtime(std::time_t time) { + struct dispatcher { + std::time_t time_; + std::tm tm_; + + dispatcher(std::time_t t) : time_(t) {} + + bool run() { + using namespace fmt::detail; + return handle(localtime_r(&time_, &tm_)); + } + + bool handle(std::tm* tm) { return tm != nullptr; } + + bool handle(detail::null<>) { + using namespace fmt::detail; + return fallback(localtime_s(&tm_, &time_)); + } + + bool fallback(int res) { return res == 0; } + +#if !FMT_MSC_VERSION + bool fallback(detail::null<>) { + using namespace fmt::detail; + std::tm* tm = std::localtime(&time_); + if (tm) tm_ = *tm; + return tm != nullptr; + } +#endif + }; + dispatcher lt(time); + // Too big time values may be unsupported. + if (!lt.run()) FMT_THROW(format_error("time_t value out of range")); + return lt.tm_; +} + +inline std::tm localtime( + std::chrono::time_point time_point) { + return localtime(std::chrono::system_clock::to_time_t(time_point)); +} + +/** + Converts given time since epoch as ``std::time_t`` value into calendar time, + expressed in Coordinated Universal Time (UTC). Unlike ``std::gmtime``, this + function is thread-safe on most platforms. + */ +inline std::tm gmtime(std::time_t time) { + struct dispatcher { + std::time_t time_; + std::tm tm_; + + dispatcher(std::time_t t) : time_(t) {} + + bool run() { + using namespace fmt::detail; + return handle(gmtime_r(&time_, &tm_)); + } + + bool handle(std::tm* tm) { return tm != nullptr; } + + bool handle(detail::null<>) { + using namespace fmt::detail; + return fallback(gmtime_s(&tm_, &time_)); + } + + bool fallback(int res) { return res == 0; } + +#if !FMT_MSC_VERSION + bool fallback(detail::null<>) { + std::tm* tm = std::gmtime(&time_); + if (tm) tm_ = *tm; + return tm != nullptr; + } +#endif + }; + dispatcher gt(time); + // Too big time values may be unsupported. + if (!gt.run()) FMT_THROW(format_error("time_t value out of range")); + return gt.tm_; +} + +inline std::tm gmtime( + std::chrono::time_point time_point) { + return gmtime(std::chrono::system_clock::to_time_t(time_point)); +} + +FMT_BEGIN_DETAIL_NAMESPACE + +// Writes two-digit numbers a, b and c separated by sep to buf. +// The method by Pavel Novikov based on +// https://johnnylee-sde.github.io/Fast-unsigned-integer-to-time-string/. +inline void write_digit2_separated(char* buf, unsigned a, unsigned b, + unsigned c, char sep) { + unsigned long long digits = + a | (b << 24) | (static_cast(c) << 48); + // Convert each value to BCD. + // We have x = a * 10 + b and we want to convert it to BCD y = a * 16 + b. + // The difference is + // y - x = a * 6 + // a can be found from x: + // a = floor(x / 10) + // then + // y = x + a * 6 = x + floor(x / 10) * 6 + // floor(x / 10) is (x * 205) >> 11 (needs 16 bits). + digits += (((digits * 205) >> 11) & 0x000f00000f00000f) * 6; + // Put low nibbles to high bytes and high nibbles to low bytes. + digits = ((digits & 0x00f00000f00000f0) >> 4) | + ((digits & 0x000f00000f00000f) << 8); + auto usep = static_cast(sep); + // Add ASCII '0' to each digit byte and insert separators. + digits |= 0x3030003030003030 | (usep << 16) | (usep << 40); + + constexpr const size_t len = 8; + if (const_check(is_big_endian())) { + char tmp[len]; + std::memcpy(tmp, &digits, len); + std::reverse_copy(tmp, tmp + len, buf); + } else { + std::memcpy(buf, &digits, len); + } +} + +template FMT_CONSTEXPR inline const char* get_units() { + if (std::is_same::value) return "as"; + if (std::is_same::value) return "fs"; + if (std::is_same::value) return "ps"; + if (std::is_same::value) return "ns"; + if (std::is_same::value) return "µs"; + if (std::is_same::value) return "ms"; + if (std::is_same::value) return "cs"; + if (std::is_same::value) return "ds"; + if (std::is_same>::value) return "s"; + if (std::is_same::value) return "das"; + if (std::is_same::value) return "hs"; + if (std::is_same::value) return "ks"; + if (std::is_same::value) return "Ms"; + if (std::is_same::value) return "Gs"; + if (std::is_same::value) return "Ts"; + if (std::is_same::value) return "Ps"; + if (std::is_same::value) return "Es"; + if (std::is_same>::value) return "m"; + if (std::is_same>::value) return "h"; + return nullptr; +} + +enum class numeric_system { + standard, + // Alternative numeric system, e.g. 十二 instead of 12 in ja_JP locale. + alternative +}; + +// Parses a put_time-like format string and invokes handler actions. +template +FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin, + const Char* end, + Handler&& handler) { + auto ptr = begin; + while (ptr != end) { + auto c = *ptr; + if (c == '}') break; + if (c != '%') { + ++ptr; + continue; + } + if (begin != ptr) handler.on_text(begin, ptr); + ++ptr; // consume '%' + if (ptr == end) FMT_THROW(format_error("invalid format")); + c = *ptr++; + switch (c) { + case '%': + handler.on_text(ptr - 1, ptr); + break; + case 'n': { + const Char newline[] = {'\n'}; + handler.on_text(newline, newline + 1); + break; + } + case 't': { + const Char tab[] = {'\t'}; + handler.on_text(tab, tab + 1); + break; + } + // Year: + case 'Y': + handler.on_year(numeric_system::standard); + break; + case 'y': + handler.on_short_year(numeric_system::standard); + break; + case 'C': + handler.on_century(numeric_system::standard); + break; + case 'G': + handler.on_iso_week_based_year(); + break; + case 'g': + handler.on_iso_week_based_short_year(); + break; + // Day of the week: + case 'a': + handler.on_abbr_weekday(); + break; + case 'A': + handler.on_full_weekday(); + break; + case 'w': + handler.on_dec0_weekday(numeric_system::standard); + break; + case 'u': + handler.on_dec1_weekday(numeric_system::standard); + break; + // Month: + case 'b': + case 'h': + handler.on_abbr_month(); + break; + case 'B': + handler.on_full_month(); + break; + case 'm': + handler.on_dec_month(numeric_system::standard); + break; + // Day of the year/month: + case 'U': + handler.on_dec0_week_of_year(numeric_system::standard); + break; + case 'W': + handler.on_dec1_week_of_year(numeric_system::standard); + break; + case 'V': + handler.on_iso_week_of_year(numeric_system::standard); + break; + case 'j': + handler.on_day_of_year(); + break; + case 'd': + handler.on_day_of_month(numeric_system::standard); + break; + case 'e': + handler.on_day_of_month_space(numeric_system::standard); + break; + // Hour, minute, second: + case 'H': + handler.on_24_hour(numeric_system::standard); + break; + case 'I': + handler.on_12_hour(numeric_system::standard); + break; + case 'M': + handler.on_minute(numeric_system::standard); + break; + case 'S': + handler.on_second(numeric_system::standard); + break; + // Other: + case 'c': + handler.on_datetime(numeric_system::standard); + break; + case 'x': + handler.on_loc_date(numeric_system::standard); + break; + case 'X': + handler.on_loc_time(numeric_system::standard); + break; + case 'D': + handler.on_us_date(); + break; + case 'F': + handler.on_iso_date(); + break; + case 'r': + handler.on_12_hour_time(); + break; + case 'R': + handler.on_24_hour_time(); + break; + case 'T': + handler.on_iso_time(); + break; + case 'p': + handler.on_am_pm(); + break; + case 'Q': + handler.on_duration_value(); + break; + case 'q': + handler.on_duration_unit(); + break; + case 'z': + handler.on_utc_offset(); + break; + case 'Z': + handler.on_tz_name(); + break; + // Alternative representation: + case 'E': { + if (ptr == end) FMT_THROW(format_error("invalid format")); + c = *ptr++; + switch (c) { + case 'Y': + handler.on_year(numeric_system::alternative); + break; + case 'y': + handler.on_offset_year(); + break; + case 'C': + handler.on_century(numeric_system::alternative); + break; + case 'c': + handler.on_datetime(numeric_system::alternative); + break; + case 'x': + handler.on_loc_date(numeric_system::alternative); + break; + case 'X': + handler.on_loc_time(numeric_system::alternative); + break; + default: + FMT_THROW(format_error("invalid format")); + } + break; + } + case 'O': + if (ptr == end) FMT_THROW(format_error("invalid format")); + c = *ptr++; + switch (c) { + case 'y': + handler.on_short_year(numeric_system::alternative); + break; + case 'm': + handler.on_dec_month(numeric_system::alternative); + break; + case 'U': + handler.on_dec0_week_of_year(numeric_system::alternative); + break; + case 'W': + handler.on_dec1_week_of_year(numeric_system::alternative); + break; + case 'V': + handler.on_iso_week_of_year(numeric_system::alternative); + break; + case 'd': + handler.on_day_of_month(numeric_system::alternative); + break; + case 'e': + handler.on_day_of_month_space(numeric_system::alternative); + break; + case 'w': + handler.on_dec0_weekday(numeric_system::alternative); + break; + case 'u': + handler.on_dec1_weekday(numeric_system::alternative); + break; + case 'H': + handler.on_24_hour(numeric_system::alternative); + break; + case 'I': + handler.on_12_hour(numeric_system::alternative); + break; + case 'M': + handler.on_minute(numeric_system::alternative); + break; + case 'S': + handler.on_second(numeric_system::alternative); + break; + default: + FMT_THROW(format_error("invalid format")); + } + break; + default: + FMT_THROW(format_error("invalid format")); + } + begin = ptr; + } + if (begin != ptr) handler.on_text(begin, ptr); + return ptr; +} + +template struct null_chrono_spec_handler { + FMT_CONSTEXPR void unsupported() { + static_cast(this)->unsupported(); + } + FMT_CONSTEXPR void on_year(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_short_year(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_offset_year() { unsupported(); } + FMT_CONSTEXPR void on_century(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_iso_week_based_year() { unsupported(); } + FMT_CONSTEXPR void on_iso_week_based_short_year() { unsupported(); } + FMT_CONSTEXPR void on_abbr_weekday() { unsupported(); } + FMT_CONSTEXPR void on_full_weekday() { unsupported(); } + FMT_CONSTEXPR void on_dec0_weekday(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_dec1_weekday(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_abbr_month() { unsupported(); } + FMT_CONSTEXPR void on_full_month() { unsupported(); } + FMT_CONSTEXPR void on_dec_month(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_dec0_week_of_year(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_dec1_week_of_year(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_iso_week_of_year(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_day_of_year() { unsupported(); } + FMT_CONSTEXPR void on_day_of_month(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_day_of_month_space(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_24_hour(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_12_hour(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_minute(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_second(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_datetime(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_loc_date(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_loc_time(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_us_date() { unsupported(); } + FMT_CONSTEXPR void on_iso_date() { unsupported(); } + FMT_CONSTEXPR void on_12_hour_time() { unsupported(); } + FMT_CONSTEXPR void on_24_hour_time() { unsupported(); } + FMT_CONSTEXPR void on_iso_time() { unsupported(); } + FMT_CONSTEXPR void on_am_pm() { unsupported(); } + FMT_CONSTEXPR void on_duration_value() { unsupported(); } + FMT_CONSTEXPR void on_duration_unit() { unsupported(); } + FMT_CONSTEXPR void on_utc_offset() { unsupported(); } + FMT_CONSTEXPR void on_tz_name() { unsupported(); } +}; + +struct tm_format_checker : null_chrono_spec_handler { + FMT_NORETURN void unsupported() { FMT_THROW(format_error("no format")); } + + template + FMT_CONSTEXPR void on_text(const Char*, const Char*) {} + FMT_CONSTEXPR void on_year(numeric_system) {} + FMT_CONSTEXPR void on_short_year(numeric_system) {} + FMT_CONSTEXPR void on_offset_year() {} + FMT_CONSTEXPR void on_century(numeric_system) {} + FMT_CONSTEXPR void on_iso_week_based_year() {} + FMT_CONSTEXPR void on_iso_week_based_short_year() {} + FMT_CONSTEXPR void on_abbr_weekday() {} + FMT_CONSTEXPR void on_full_weekday() {} + FMT_CONSTEXPR void on_dec0_weekday(numeric_system) {} + FMT_CONSTEXPR void on_dec1_weekday(numeric_system) {} + FMT_CONSTEXPR void on_abbr_month() {} + FMT_CONSTEXPR void on_full_month() {} + FMT_CONSTEXPR void on_dec_month(numeric_system) {} + FMT_CONSTEXPR void on_dec0_week_of_year(numeric_system) {} + FMT_CONSTEXPR void on_dec1_week_of_year(numeric_system) {} + FMT_CONSTEXPR void on_iso_week_of_year(numeric_system) {} + FMT_CONSTEXPR void on_day_of_year() {} + FMT_CONSTEXPR void on_day_of_month(numeric_system) {} + FMT_CONSTEXPR void on_day_of_month_space(numeric_system) {} + FMT_CONSTEXPR void on_24_hour(numeric_system) {} + FMT_CONSTEXPR void on_12_hour(numeric_system) {} + FMT_CONSTEXPR void on_minute(numeric_system) {} + FMT_CONSTEXPR void on_second(numeric_system) {} + FMT_CONSTEXPR void on_datetime(numeric_system) {} + FMT_CONSTEXPR void on_loc_date(numeric_system) {} + FMT_CONSTEXPR void on_loc_time(numeric_system) {} + FMT_CONSTEXPR void on_us_date() {} + FMT_CONSTEXPR void on_iso_date() {} + FMT_CONSTEXPR void on_12_hour_time() {} + FMT_CONSTEXPR void on_24_hour_time() {} + FMT_CONSTEXPR void on_iso_time() {} + FMT_CONSTEXPR void on_am_pm() {} + FMT_CONSTEXPR void on_utc_offset() {} + FMT_CONSTEXPR void on_tz_name() {} +}; + +inline const char* tm_wday_full_name(int wday) { + static constexpr const char* full_name_list[] = { + "Sunday", "Monday", "Tuesday", "Wednesday", + "Thursday", "Friday", "Saturday"}; + return wday >= 0 && wday <= 6 ? full_name_list[wday] : "?"; +} +inline const char* tm_wday_short_name(int wday) { + static constexpr const char* short_name_list[] = {"Sun", "Mon", "Tue", "Wed", + "Thu", "Fri", "Sat"}; + return wday >= 0 && wday <= 6 ? short_name_list[wday] : "???"; +} + +inline const char* tm_mon_full_name(int mon) { + static constexpr const char* full_name_list[] = { + "January", "February", "March", "April", "May", "June", + "July", "August", "September", "October", "November", "December"}; + return mon >= 0 && mon <= 11 ? full_name_list[mon] : "?"; +} +inline const char* tm_mon_short_name(int mon) { + static constexpr const char* short_name_list[] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", + }; + return mon >= 0 && mon <= 11 ? short_name_list[mon] : "???"; +} + +template +struct has_member_data_tm_gmtoff : std::false_type {}; +template +struct has_member_data_tm_gmtoff> + : std::true_type {}; + +template +struct has_member_data_tm_zone : std::false_type {}; +template +struct has_member_data_tm_zone> + : std::true_type {}; + +#if FMT_USE_TZSET +inline void tzset_once() { + static bool init = []() -> bool { + _tzset(); + return true; + }(); + ignore_unused(init); +} +#endif + +template class tm_writer { + private: + static constexpr int days_per_week = 7; + + const std::locale& loc_; + const bool is_classic_; + OutputIt out_; + const std::tm& tm_; + + auto tm_sec() const noexcept -> int { + FMT_ASSERT(tm_.tm_sec >= 0 && tm_.tm_sec <= 61, ""); + return tm_.tm_sec; + } + auto tm_min() const noexcept -> int { + FMT_ASSERT(tm_.tm_min >= 0 && tm_.tm_min <= 59, ""); + return tm_.tm_min; + } + auto tm_hour() const noexcept -> int { + FMT_ASSERT(tm_.tm_hour >= 0 && tm_.tm_hour <= 23, ""); + return tm_.tm_hour; + } + auto tm_mday() const noexcept -> int { + FMT_ASSERT(tm_.tm_mday >= 1 && tm_.tm_mday <= 31, ""); + return tm_.tm_mday; + } + auto tm_mon() const noexcept -> int { + FMT_ASSERT(tm_.tm_mon >= 0 && tm_.tm_mon <= 11, ""); + return tm_.tm_mon; + } + auto tm_year() const noexcept -> long long { return 1900ll + tm_.tm_year; } + auto tm_wday() const noexcept -> int { + FMT_ASSERT(tm_.tm_wday >= 0 && tm_.tm_wday <= 6, ""); + return tm_.tm_wday; + } + auto tm_yday() const noexcept -> int { + FMT_ASSERT(tm_.tm_yday >= 0 && tm_.tm_yday <= 365, ""); + return tm_.tm_yday; + } + + auto tm_hour12() const noexcept -> int { + const auto h = tm_hour(); + const auto z = h < 12 ? h : h - 12; + return z == 0 ? 12 : z; + } + + // POSIX and the C Standard are unclear or inconsistent about what %C and %y + // do if the year is negative or exceeds 9999. Use the convention that %C + // concatenated with %y yields the same output as %Y, and that %Y contains at + // least 4 characters, with more only if necessary. + auto split_year_lower(long long year) const noexcept -> int { + auto l = year % 100; + if (l < 0) l = -l; // l in [0, 99] + return static_cast(l); + } + + // Algorithm: + // https://en.wikipedia.org/wiki/ISO_week_date#Calculating_the_week_number_from_a_month_and_day_of_the_month_or_ordinal_date + auto iso_year_weeks(long long curr_year) const noexcept -> int { + const auto prev_year = curr_year - 1; + const auto curr_p = + (curr_year + curr_year / 4 - curr_year / 100 + curr_year / 400) % + days_per_week; + const auto prev_p = + (prev_year + prev_year / 4 - prev_year / 100 + prev_year / 400) % + days_per_week; + return 52 + ((curr_p == 4 || prev_p == 3) ? 1 : 0); + } + auto iso_week_num(int tm_yday, int tm_wday) const noexcept -> int { + return (tm_yday + 11 - (tm_wday == 0 ? days_per_week : tm_wday)) / + days_per_week; + } + auto tm_iso_week_year() const noexcept -> long long { + const auto year = tm_year(); + const auto w = iso_week_num(tm_yday(), tm_wday()); + if (w < 1) return year - 1; + if (w > iso_year_weeks(year)) return year + 1; + return year; + } + auto tm_iso_week_of_year() const noexcept -> int { + const auto year = tm_year(); + const auto w = iso_week_num(tm_yday(), tm_wday()); + if (w < 1) return iso_year_weeks(year - 1); + if (w > iso_year_weeks(year)) return 1; + return w; + } + + void write1(int value) { + *out_++ = static_cast('0' + to_unsigned(value) % 10); + } + void write2(int value) { + const char* d = digits2(to_unsigned(value) % 100); + *out_++ = *d++; + *out_++ = *d; + } + + void write_year_extended(long long year) { + // At least 4 characters. + int width = 4; + if (year < 0) { + *out_++ = '-'; + year = 0 - year; + --width; + } + uint32_or_64_or_128_t n = to_unsigned(year); + const int num_digits = count_digits(n); + if (width > num_digits) out_ = std::fill_n(out_, width - num_digits, '0'); + out_ = format_decimal(out_, n, num_digits).end; + } + void write_year(long long year) { + if (year >= 0 && year < 10000) { + write2(static_cast(year / 100)); + write2(static_cast(year % 100)); + } else { + write_year_extended(year); + } + } + + void write_utc_offset(long offset) { + if (offset < 0) { + *out_++ = '-'; + offset = -offset; + } else { + *out_++ = '+'; + } + offset /= 60; + write2(static_cast(offset / 60)); + write2(static_cast(offset % 60)); + } + template ::value)> + void format_utc_offset_impl(const T& tm) { + write_utc_offset(tm.tm_gmtoff); + } + template ::value)> + void format_utc_offset_impl(const T& tm) { +#if defined(_WIN32) && defined(_UCRT) +# if FMT_USE_TZSET + tzset_once(); +# endif + long offset = 0; + _get_timezone(&offset); + if (tm.tm_isdst) { + long dstbias = 0; + _get_dstbias(&dstbias); + offset += dstbias; + } + write_utc_offset(-offset); +#else + ignore_unused(tm); + format_localized('z'); +#endif + } + + template ::value)> + void format_tz_name_impl(const T& tm) { + if (is_classic_) + out_ = write_tm_str(out_, tm.tm_zone, loc_); + else + format_localized('Z'); + } + template ::value)> + void format_tz_name_impl(const T&) { + format_localized('Z'); + } + + void format_localized(char format, char modifier = 0) { + out_ = write(out_, tm_, loc_, format, modifier); + } + + public: + tm_writer(const std::locale& loc, OutputIt out, const std::tm& tm) + : loc_(loc), + is_classic_(loc_ == get_classic_locale()), + out_(out), + tm_(tm) {} + + OutputIt out() const { return out_; } + + FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) { + out_ = copy_str(begin, end, out_); + } + + void on_abbr_weekday() { + if (is_classic_) + out_ = write(out_, tm_wday_short_name(tm_wday())); + else + format_localized('a'); + } + void on_full_weekday() { + if (is_classic_) + out_ = write(out_, tm_wday_full_name(tm_wday())); + else + format_localized('A'); + } + void on_dec0_weekday(numeric_system ns) { + if (is_classic_ || ns == numeric_system::standard) return write1(tm_wday()); + format_localized('w', 'O'); + } + void on_dec1_weekday(numeric_system ns) { + if (is_classic_ || ns == numeric_system::standard) { + auto wday = tm_wday(); + write1(wday == 0 ? days_per_week : wday); + } else { + format_localized('u', 'O'); + } + } + + void on_abbr_month() { + if (is_classic_) + out_ = write(out_, tm_mon_short_name(tm_mon())); + else + format_localized('b'); + } + void on_full_month() { + if (is_classic_) + out_ = write(out_, tm_mon_full_name(tm_mon())); + else + format_localized('B'); + } + + void on_datetime(numeric_system ns) { + if (is_classic_) { + on_abbr_weekday(); + *out_++ = ' '; + on_abbr_month(); + *out_++ = ' '; + on_day_of_month_space(numeric_system::standard); + *out_++ = ' '; + on_iso_time(); + *out_++ = ' '; + on_year(numeric_system::standard); + } else { + format_localized('c', ns == numeric_system::standard ? '\0' : 'E'); + } + } + void on_loc_date(numeric_system ns) { + if (is_classic_) + on_us_date(); + else + format_localized('x', ns == numeric_system::standard ? '\0' : 'E'); + } + void on_loc_time(numeric_system ns) { + if (is_classic_) + on_iso_time(); + else + format_localized('X', ns == numeric_system::standard ? '\0' : 'E'); + } + void on_us_date() { + char buf[8]; + write_digit2_separated(buf, to_unsigned(tm_mon() + 1), + to_unsigned(tm_mday()), + to_unsigned(split_year_lower(tm_year())), '/'); + out_ = copy_str(std::begin(buf), std::end(buf), out_); + } + void on_iso_date() { + auto year = tm_year(); + char buf[10]; + size_t offset = 0; + if (year >= 0 && year < 10000) { + copy2(buf, digits2(static_cast(year / 100))); + } else { + offset = 4; + write_year_extended(year); + year = 0; + } + write_digit2_separated(buf + 2, static_cast(year % 100), + to_unsigned(tm_mon() + 1), to_unsigned(tm_mday()), + '-'); + out_ = copy_str(std::begin(buf) + offset, std::end(buf), out_); + } + + void on_utc_offset() { format_utc_offset_impl(tm_); } + void on_tz_name() { format_tz_name_impl(tm_); } + + void on_year(numeric_system ns) { + if (is_classic_ || ns == numeric_system::standard) + return write_year(tm_year()); + format_localized('Y', 'E'); + } + void on_short_year(numeric_system ns) { + if (is_classic_ || ns == numeric_system::standard) + return write2(split_year_lower(tm_year())); + format_localized('y', 'O'); + } + void on_offset_year() { + if (is_classic_) return write2(split_year_lower(tm_year())); + format_localized('y', 'E'); + } + + void on_century(numeric_system ns) { + if (is_classic_ || ns == numeric_system::standard) { + auto year = tm_year(); + auto upper = year / 100; + if (year >= -99 && year < 0) { + // Zero upper on negative year. + *out_++ = '-'; + *out_++ = '0'; + } else if (upper >= 0 && upper < 100) { + write2(static_cast(upper)); + } else { + out_ = write(out_, upper); + } + } else { + format_localized('C', 'E'); + } + } + + void on_dec_month(numeric_system ns) { + if (is_classic_ || ns == numeric_system::standard) + return write2(tm_mon() + 1); + format_localized('m', 'O'); + } + + void on_dec0_week_of_year(numeric_system ns) { + if (is_classic_ || ns == numeric_system::standard) + return write2((tm_yday() + days_per_week - tm_wday()) / days_per_week); + format_localized('U', 'O'); + } + void on_dec1_week_of_year(numeric_system ns) { + if (is_classic_ || ns == numeric_system::standard) { + auto wday = tm_wday(); + write2((tm_yday() + days_per_week - + (wday == 0 ? (days_per_week - 1) : (wday - 1))) / + days_per_week); + } else { + format_localized('W', 'O'); + } + } + void on_iso_week_of_year(numeric_system ns) { + if (is_classic_ || ns == numeric_system::standard) + return write2(tm_iso_week_of_year()); + format_localized('V', 'O'); + } + + void on_iso_week_based_year() { write_year(tm_iso_week_year()); } + void on_iso_week_based_short_year() { + write2(split_year_lower(tm_iso_week_year())); + } + + void on_day_of_year() { + auto yday = tm_yday() + 1; + write1(yday / 100); + write2(yday % 100); + } + void on_day_of_month(numeric_system ns) { + if (is_classic_ || ns == numeric_system::standard) return write2(tm_mday()); + format_localized('d', 'O'); + } + void on_day_of_month_space(numeric_system ns) { + if (is_classic_ || ns == numeric_system::standard) { + auto mday = to_unsigned(tm_mday()) % 100; + const char* d2 = digits2(mday); + *out_++ = mday < 10 ? ' ' : d2[0]; + *out_++ = d2[1]; + } else { + format_localized('e', 'O'); + } + } + + void on_24_hour(numeric_system ns) { + if (is_classic_ || ns == numeric_system::standard) return write2(tm_hour()); + format_localized('H', 'O'); + } + void on_12_hour(numeric_system ns) { + if (is_classic_ || ns == numeric_system::standard) + return write2(tm_hour12()); + format_localized('I', 'O'); + } + void on_minute(numeric_system ns) { + if (is_classic_ || ns == numeric_system::standard) return write2(tm_min()); + format_localized('M', 'O'); + } + void on_second(numeric_system ns) { + if (is_classic_ || ns == numeric_system::standard) return write2(tm_sec()); + format_localized('S', 'O'); + } + + void on_12_hour_time() { + if (is_classic_) { + char buf[8]; + write_digit2_separated(buf, to_unsigned(tm_hour12()), + to_unsigned(tm_min()), to_unsigned(tm_sec()), ':'); + out_ = copy_str(std::begin(buf), std::end(buf), out_); + *out_++ = ' '; + on_am_pm(); + } else { + format_localized('r'); + } + } + void on_24_hour_time() { + write2(tm_hour()); + *out_++ = ':'; + write2(tm_min()); + } + void on_iso_time() { + char buf[8]; + write_digit2_separated(buf, to_unsigned(tm_hour()), to_unsigned(tm_min()), + to_unsigned(tm_sec()), ':'); + out_ = copy_str(std::begin(buf), std::end(buf), out_); + } + + void on_am_pm() { + if (is_classic_) { + *out_++ = tm_hour() < 12 ? 'A' : 'P'; + *out_++ = 'M'; + } else { + format_localized('p'); + } + } + + // These apply to chrono durations but not tm. + void on_duration_value() {} + void on_duration_unit() {} +}; + +struct chrono_format_checker : null_chrono_spec_handler { + FMT_NORETURN void unsupported() { FMT_THROW(format_error("no date")); } + + template + FMT_CONSTEXPR void on_text(const Char*, const Char*) {} + FMT_CONSTEXPR void on_24_hour(numeric_system) {} + FMT_CONSTEXPR void on_12_hour(numeric_system) {} + FMT_CONSTEXPR void on_minute(numeric_system) {} + FMT_CONSTEXPR void on_second(numeric_system) {} + FMT_CONSTEXPR void on_12_hour_time() {} + FMT_CONSTEXPR void on_24_hour_time() {} + FMT_CONSTEXPR void on_iso_time() {} + FMT_CONSTEXPR void on_am_pm() {} + FMT_CONSTEXPR void on_duration_value() {} + FMT_CONSTEXPR void on_duration_unit() {} +}; + +template ::value)> +inline bool isfinite(T) { + return true; +} + +// Converts value to Int and checks that it's in the range [0, upper). +template ::value)> +inline Int to_nonnegative_int(T value, Int upper) { + FMT_ASSERT(value >= 0 && to_unsigned(value) <= to_unsigned(upper), + "invalid value"); + (void)upper; + return static_cast(value); +} +template ::value)> +inline Int to_nonnegative_int(T value, Int upper) { + if (value < 0 || value > static_cast(upper)) + FMT_THROW(format_error("invalid value")); + return static_cast(value); +} + +template ::value)> +inline T mod(T x, int y) { + return x % static_cast(y); +} +template ::value)> +inline T mod(T x, int y) { + return std::fmod(x, static_cast(y)); +} + +// If T is an integral type, maps T to its unsigned counterpart, otherwise +// leaves it unchanged (unlike std::make_unsigned). +template ::value> +struct make_unsigned_or_unchanged { + using type = T; +}; + +template struct make_unsigned_or_unchanged { + using type = typename std::make_unsigned::type; +}; + +#if FMT_SAFE_DURATION_CAST +// throwing version of safe_duration_cast +template +To fmt_safe_duration_cast(std::chrono::duration from) { + int ec; + To to = safe_duration_cast::safe_duration_cast(from, ec); + if (ec) FMT_THROW(format_error("cannot format duration")); + return to; +} +#endif + +template ::value)> +inline std::chrono::duration get_milliseconds( + std::chrono::duration d) { + // this may overflow and/or the result may not fit in the + // target type. +#if FMT_SAFE_DURATION_CAST + using CommonSecondsType = + typename std::common_type::type; + const auto d_as_common = fmt_safe_duration_cast(d); + const auto d_as_whole_seconds = + fmt_safe_duration_cast(d_as_common); + // this conversion should be nonproblematic + const auto diff = d_as_common - d_as_whole_seconds; + const auto ms = + fmt_safe_duration_cast>(diff); + return ms; +#else + auto s = std::chrono::duration_cast(d); + return std::chrono::duration_cast(d - s); +#endif +} + +// Counts the number of fractional digits in the range [0, 18] according to the +// C++20 spec. If more than 18 fractional digits are required then returns 6 for +// microseconds precision. +template () / 10)> +struct count_fractional_digits { + static constexpr int value = + Num % Den == 0 ? N : count_fractional_digits::value; +}; + +// Base case that doesn't instantiate any more templates +// in order to avoid overflow. +template +struct count_fractional_digits { + static constexpr int value = (Num % Den == 0) ? N : 6; +}; + +constexpr long long pow10(std::uint32_t n) { + return n == 0 ? 1 : 10 * pow10(n - 1); +} + +template ::is_signed)> +constexpr std::chrono::duration abs( + std::chrono::duration d) { + // We need to compare the duration using the count() method directly + // due to a compiler bug in clang-11 regarding the spaceship operator, + // when -Wzero-as-null-pointer-constant is enabled. + // In clang-12 the bug has been fixed. See + // https://bugs.llvm.org/show_bug.cgi?id=46235 and the reproducible example: + // https://www.godbolt.org/z/Knbb5joYx. + return d.count() >= d.zero().count() ? d : -d; +} + +template ::is_signed)> +constexpr std::chrono::duration abs( + std::chrono::duration d) { + return d; +} + +template ::value)> +OutputIt format_duration_value(OutputIt out, Rep val, int) { + return write(out, val); +} + +template ::value)> +OutputIt format_duration_value(OutputIt out, Rep val, int precision) { + auto specs = basic_format_specs(); + specs.precision = precision; + specs.type = precision >= 0 ? presentation_type::fixed_lower + : presentation_type::general_lower; + return write(out, val, specs); +} + +template +OutputIt copy_unit(string_view unit, OutputIt out, Char) { + return std::copy(unit.begin(), unit.end(), out); +} + +template +OutputIt copy_unit(string_view unit, OutputIt out, wchar_t) { + // This works when wchar_t is UTF-32 because units only contain characters + // that have the same representation in UTF-16 and UTF-32. + utf8_to_utf16 u(unit); + return std::copy(u.c_str(), u.c_str() + u.size(), out); +} + +template +OutputIt format_duration_unit(OutputIt out) { + if (const char* unit = get_units()) + return copy_unit(string_view(unit), out, Char()); + *out++ = '['; + out = write(out, Period::num); + if (const_check(Period::den != 1)) { + *out++ = '/'; + out = write(out, Period::den); + } + *out++ = ']'; + *out++ = 's'; + return out; +} + +class get_locale { + private: + union { + std::locale locale_; + }; + bool has_locale_ = false; + + public: + get_locale(bool localized, locale_ref loc) : has_locale_(localized) { + if (localized) + ::new (&locale_) std::locale(loc.template get()); + } + ~get_locale() { + if (has_locale_) locale_.~locale(); + } + operator const std::locale&() const { + return has_locale_ ? locale_ : get_classic_locale(); + } +}; + +template +struct chrono_formatter { + FormatContext& context; + OutputIt out; + int precision; + bool localized = false; + // rep is unsigned to avoid overflow. + using rep = + conditional_t::value && sizeof(Rep) < sizeof(int), + unsigned, typename make_unsigned_or_unchanged::type>; + rep val; + using seconds = std::chrono::duration; + seconds s; + using milliseconds = std::chrono::duration; + bool negative; + + using char_type = typename FormatContext::char_type; + using tm_writer_type = tm_writer; + + chrono_formatter(FormatContext& ctx, OutputIt o, + std::chrono::duration d) + : context(ctx), + out(o), + val(static_cast(d.count())), + negative(false) { + if (d.count() < 0) { + val = 0 - val; + negative = true; + } + + // this may overflow and/or the result may not fit in the + // target type. +#if FMT_SAFE_DURATION_CAST + // might need checked conversion (rep!=Rep) + auto tmpval = std::chrono::duration(val); + s = fmt_safe_duration_cast(tmpval); +#else + s = std::chrono::duration_cast( + std::chrono::duration(val)); +#endif + } + + // returns true if nan or inf, writes to out. + bool handle_nan_inf() { + if (isfinite(val)) { + return false; + } + if (isnan(val)) { + write_nan(); + return true; + } + // must be +-inf + if (val > 0) { + write_pinf(); + } else { + write_ninf(); + } + return true; + } + + Rep hour() const { return static_cast(mod((s.count() / 3600), 24)); } + + Rep hour12() const { + Rep hour = static_cast(mod((s.count() / 3600), 12)); + return hour <= 0 ? 12 : hour; + } + + Rep minute() const { return static_cast(mod((s.count() / 60), 60)); } + Rep second() const { return static_cast(mod(s.count(), 60)); } + + std::tm time() const { + auto time = std::tm(); + time.tm_hour = to_nonnegative_int(hour(), 24); + time.tm_min = to_nonnegative_int(minute(), 60); + time.tm_sec = to_nonnegative_int(second(), 60); + return time; + } + + void write_sign() { + if (negative) { + *out++ = '-'; + negative = false; + } + } + + void write(Rep value, int width) { + write_sign(); + if (isnan(value)) return write_nan(); + uint32_or_64_or_128_t n = + to_unsigned(to_nonnegative_int(value, max_value())); + int num_digits = detail::count_digits(n); + if (width > num_digits) out = std::fill_n(out, width - num_digits, '0'); + out = format_decimal(out, n, num_digits).end; + } + + template void write_fractional_seconds(Duration d) { + FMT_ASSERT(!std::is_floating_point::value, ""); + constexpr auto num_fractional_digits = + count_fractional_digits::value; + + using subsecond_precision = std::chrono::duration< + typename std::common_type::type, + std::ratio<1, detail::pow10(num_fractional_digits)>>; + if (std::ratio_less::value) { + *out++ = '.'; + auto fractional = + detail::abs(d) - std::chrono::duration_cast(d); + auto subseconds = + std::chrono::treat_as_floating_point< + typename subsecond_precision::rep>::value + ? fractional.count() + : std::chrono::duration_cast(fractional) + .count(); + uint32_or_64_or_128_t n = + to_unsigned(to_nonnegative_int(subseconds, max_value())); + int num_digits = detail::count_digits(n); + if (num_fractional_digits > num_digits) + out = std::fill_n(out, num_fractional_digits - num_digits, '0'); + out = format_decimal(out, n, num_digits).end; + } + } + + void write_nan() { std::copy_n("nan", 3, out); } + void write_pinf() { std::copy_n("inf", 3, out); } + void write_ninf() { std::copy_n("-inf", 4, out); } + + template + void format_tm(const tm& time, Callback cb, Args... args) { + if (isnan(val)) return write_nan(); + get_locale loc(localized, context.locale()); + auto w = tm_writer_type(loc, out, time); + (w.*cb)(args...); + out = w.out(); + } + + void on_text(const char_type* begin, const char_type* end) { + std::copy(begin, end, out); + } + + // These are not implemented because durations don't have date information. + void on_abbr_weekday() {} + void on_full_weekday() {} + void on_dec0_weekday(numeric_system) {} + void on_dec1_weekday(numeric_system) {} + void on_abbr_month() {} + void on_full_month() {} + void on_datetime(numeric_system) {} + void on_loc_date(numeric_system) {} + void on_loc_time(numeric_system) {} + void on_us_date() {} + void on_iso_date() {} + void on_utc_offset() {} + void on_tz_name() {} + void on_year(numeric_system) {} + void on_short_year(numeric_system) {} + void on_offset_year() {} + void on_century(numeric_system) {} + void on_iso_week_based_year() {} + void on_iso_week_based_short_year() {} + void on_dec_month(numeric_system) {} + void on_dec0_week_of_year(numeric_system) {} + void on_dec1_week_of_year(numeric_system) {} + void on_iso_week_of_year(numeric_system) {} + void on_day_of_year() {} + void on_day_of_month(numeric_system) {} + void on_day_of_month_space(numeric_system) {} + + void on_24_hour(numeric_system ns) { + if (handle_nan_inf()) return; + + if (ns == numeric_system::standard) return write(hour(), 2); + auto time = tm(); + time.tm_hour = to_nonnegative_int(hour(), 24); + format_tm(time, &tm_writer_type::on_24_hour, ns); + } + + void on_12_hour(numeric_system ns) { + if (handle_nan_inf()) return; + + if (ns == numeric_system::standard) return write(hour12(), 2); + auto time = tm(); + time.tm_hour = to_nonnegative_int(hour12(), 12); + format_tm(time, &tm_writer_type::on_12_hour, ns); + } + + void on_minute(numeric_system ns) { + if (handle_nan_inf()) return; + + if (ns == numeric_system::standard) return write(minute(), 2); + auto time = tm(); + time.tm_min = to_nonnegative_int(minute(), 60); + format_tm(time, &tm_writer_type::on_minute, ns); + } + + void on_second(numeric_system ns) { + if (handle_nan_inf()) return; + + if (ns == numeric_system::standard) { + if (std::is_floating_point::value) { + constexpr auto num_fractional_digits = + count_fractional_digits::value; + auto buf = memory_buffer(); + format_to(std::back_inserter(buf), runtime("{:.{}f}"), + std::fmod(val * static_cast(Period::num) / + static_cast(Period::den), + 60), + num_fractional_digits); + if (negative) *out++ = '-'; + if (buf.size() < 2 || buf[1] == '.') *out++ = '0'; + out = std::copy(buf.begin(), buf.end(), out); + } else { + write(second(), 2); + write_fractional_seconds(std::chrono::duration(val)); + } + return; + } + auto time = tm(); + time.tm_sec = to_nonnegative_int(second(), 60); + format_tm(time, &tm_writer_type::on_second, ns); + } + + void on_12_hour_time() { + if (handle_nan_inf()) return; + format_tm(time(), &tm_writer_type::on_12_hour_time); + } + + void on_24_hour_time() { + if (handle_nan_inf()) { + *out++ = ':'; + handle_nan_inf(); + return; + } + + write(hour(), 2); + *out++ = ':'; + write(minute(), 2); + } + + void on_iso_time() { + on_24_hour_time(); + *out++ = ':'; + if (handle_nan_inf()) return; + on_second(numeric_system::standard); + } + + void on_am_pm() { + if (handle_nan_inf()) return; + format_tm(time(), &tm_writer_type::on_am_pm); + } + + void on_duration_value() { + if (handle_nan_inf()) return; + write_sign(); + out = format_duration_value(out, val, precision); + } + + void on_duration_unit() { + out = format_duration_unit(out); + } +}; + +FMT_END_DETAIL_NAMESPACE + +#if defined(__cpp_lib_chrono) && __cpp_lib_chrono >= 201907 +using weekday = std::chrono::weekday; +#else +// A fallback version of weekday. +class weekday { + private: + unsigned char value; + + public: + weekday() = default; + explicit constexpr weekday(unsigned wd) noexcept + : value(static_cast(wd != 7 ? wd : 0)) {} + constexpr unsigned c_encoding() const noexcept { return value; } +}; + +class year_month_day {}; +#endif + +// A rudimentary weekday formatter. +template struct formatter { + private: + bool localized = false; + + public: + FMT_CONSTEXPR auto parse(basic_format_parse_context& ctx) + -> decltype(ctx.begin()) { + auto begin = ctx.begin(), end = ctx.end(); + if (begin != end && *begin == 'L') { + ++begin; + localized = true; + } + return begin; + } + + template + auto format(weekday wd, FormatContext& ctx) const -> decltype(ctx.out()) { + auto time = std::tm(); + time.tm_wday = static_cast(wd.c_encoding()); + detail::get_locale loc(localized, ctx.locale()); + auto w = detail::tm_writer(loc, ctx.out(), time); + w.on_abbr_weekday(); + return w.out(); + } +}; + +template +struct formatter, Char> { + private: + basic_format_specs specs; + int precision = -1; + using arg_ref_type = detail::arg_ref; + arg_ref_type width_ref; + arg_ref_type precision_ref; + bool localized = false; + basic_string_view format_str; + using duration = std::chrono::duration; + + struct spec_handler { + formatter& f; + basic_format_parse_context& context; + basic_string_view format_str; + + template FMT_CONSTEXPR arg_ref_type make_arg_ref(Id arg_id) { + context.check_arg_id(arg_id); + return arg_ref_type(arg_id); + } + + FMT_CONSTEXPR arg_ref_type make_arg_ref(basic_string_view arg_id) { + context.check_arg_id(arg_id); + return arg_ref_type(arg_id); + } + + FMT_CONSTEXPR arg_ref_type make_arg_ref(detail::auto_id) { + return arg_ref_type(context.next_arg_id()); + } + + void on_error(const char* msg) { FMT_THROW(format_error(msg)); } + FMT_CONSTEXPR void on_fill(basic_string_view fill) { + f.specs.fill = fill; + } + FMT_CONSTEXPR void on_align(align_t align) { f.specs.align = align; } + FMT_CONSTEXPR void on_width(int width) { f.specs.width = width; } + FMT_CONSTEXPR void on_precision(int _precision) { + f.precision = _precision; + } + FMT_CONSTEXPR void end_precision() {} + + template FMT_CONSTEXPR void on_dynamic_width(Id arg_id) { + f.width_ref = make_arg_ref(arg_id); + } + + template FMT_CONSTEXPR void on_dynamic_precision(Id arg_id) { + f.precision_ref = make_arg_ref(arg_id); + } + }; + + using iterator = typename basic_format_parse_context::iterator; + struct parse_range { + iterator begin; + iterator end; + }; + + FMT_CONSTEXPR parse_range do_parse(basic_format_parse_context& ctx) { + auto begin = ctx.begin(), end = ctx.end(); + if (begin == end || *begin == '}') return {begin, begin}; + spec_handler handler{*this, ctx, format_str}; + begin = detail::parse_align(begin, end, handler); + if (begin == end) return {begin, begin}; + begin = detail::parse_width(begin, end, handler); + if (begin == end) return {begin, begin}; + if (*begin == '.') { + if (std::is_floating_point::value) + begin = detail::parse_precision(begin, end, handler); + else + handler.on_error("precision not allowed for this argument type"); + } + if (begin != end && *begin == 'L') { + ++begin; + localized = true; + } + end = detail::parse_chrono_format(begin, end, + detail::chrono_format_checker()); + return {begin, end}; + } + + public: + FMT_CONSTEXPR auto parse(basic_format_parse_context& ctx) + -> decltype(ctx.begin()) { + auto range = do_parse(ctx); + format_str = basic_string_view( + &*range.begin, detail::to_unsigned(range.end - range.begin)); + return range.end; + } + + template + auto format(const duration& d, FormatContext& ctx) const + -> decltype(ctx.out()) { + auto specs_copy = specs; + auto precision_copy = precision; + auto begin = format_str.begin(), end = format_str.end(); + // As a possible future optimization, we could avoid extra copying if width + // is not specified. + basic_memory_buffer buf; + auto out = std::back_inserter(buf); + detail::handle_dynamic_spec(specs_copy.width, + width_ref, ctx); + detail::handle_dynamic_spec(precision_copy, + precision_ref, ctx); + if (begin == end || *begin == '}') { + out = detail::format_duration_value(out, d.count(), precision_copy); + detail::format_duration_unit(out); + } else { + detail::chrono_formatter f( + ctx, out, d); + f.precision = precision_copy; + f.localized = localized; + detail::parse_chrono_format(begin, end, f); + } + return detail::write( + ctx.out(), basic_string_view(buf.data(), buf.size()), specs_copy); + } +}; + +template +struct formatter, + Char> : formatter { + FMT_CONSTEXPR formatter() { + this->do_parse(default_specs, + default_specs + sizeof(default_specs) / sizeof(Char)); + } + + template + FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { + return this->do_parse(ctx.begin(), ctx.end(), true); + } + + template + auto format(std::chrono::time_point val, + FormatContext& ctx) const -> decltype(ctx.out()) { + return formatter::format(localtime(val), ctx); + } + + static constexpr const Char default_specs[] = {'%', 'F', ' ', '%', 'T'}; +}; + +template +constexpr const Char + formatter, + Char>::default_specs[]; + +template struct formatter { + private: + enum class spec { + unknown, + year_month_day, + hh_mm_ss, + }; + spec spec_ = spec::unknown; + basic_string_view specs; + + protected: + template + FMT_CONSTEXPR auto do_parse(It begin, It end, bool with_default = false) + -> It { + if (begin != end && *begin == ':') ++begin; + end = detail::parse_chrono_format(begin, end, detail::tm_format_checker()); + if (!with_default || end != begin) + specs = {begin, detail::to_unsigned(end - begin)}; + // basic_string_view<>::compare isn't constexpr before C++17. + if (specs.size() == 2 && specs[0] == Char('%')) { + if (specs[1] == Char('F')) + spec_ = spec::year_month_day; + else if (specs[1] == Char('T')) + spec_ = spec::hh_mm_ss; + } + return end; + } + + public: + template + FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { + return this->do_parse(ctx.begin(), ctx.end()); + } + + template + auto format(const std::tm& tm, FormatContext& ctx) const + -> decltype(ctx.out()) { + const auto loc_ref = ctx.locale(); + detail::get_locale loc(static_cast(loc_ref), loc_ref); + auto w = detail::tm_writer(loc, ctx.out(), tm); + if (spec_ == spec::year_month_day) + w.on_iso_date(); + else if (spec_ == spec::hh_mm_ss) + w.on_iso_time(); + else + detail::parse_chrono_format(specs.begin(), specs.end(), w); + return w.out(); + } +}; + +FMT_MODULE_EXPORT_END +FMT_END_NAMESPACE + +#endif // FMT_CHRONO_H_ diff --git a/src/fmtlib/fmt/color.h b/src/fmtlib/fmt/color.h new file mode 100644 index 0000000..e6212d2 --- /dev/null +++ b/src/fmtlib/fmt/color.h @@ -0,0 +1,651 @@ +// Formatting library for C++ - color support +// +// Copyright (c) 2018 - present, Victor Zverovich and fmt contributors +// All rights reserved. +// +// For the license information refer to format.h. + +#ifndef FMT_COLOR_H_ +#define FMT_COLOR_H_ + +#include "format.h" + +FMT_BEGIN_NAMESPACE +FMT_MODULE_EXPORT_BEGIN + +enum class color : uint32_t { + alice_blue = 0xF0F8FF, // rgb(240,248,255) + antique_white = 0xFAEBD7, // rgb(250,235,215) + aqua = 0x00FFFF, // rgb(0,255,255) + aquamarine = 0x7FFFD4, // rgb(127,255,212) + azure = 0xF0FFFF, // rgb(240,255,255) + beige = 0xF5F5DC, // rgb(245,245,220) + bisque = 0xFFE4C4, // rgb(255,228,196) + black = 0x000000, // rgb(0,0,0) + blanched_almond = 0xFFEBCD, // rgb(255,235,205) + blue = 0x0000FF, // rgb(0,0,255) + blue_violet = 0x8A2BE2, // rgb(138,43,226) + brown = 0xA52A2A, // rgb(165,42,42) + burly_wood = 0xDEB887, // rgb(222,184,135) + cadet_blue = 0x5F9EA0, // rgb(95,158,160) + chartreuse = 0x7FFF00, // rgb(127,255,0) + chocolate = 0xD2691E, // rgb(210,105,30) + coral = 0xFF7F50, // rgb(255,127,80) + cornflower_blue = 0x6495ED, // rgb(100,149,237) + cornsilk = 0xFFF8DC, // rgb(255,248,220) + crimson = 0xDC143C, // rgb(220,20,60) + cyan = 0x00FFFF, // rgb(0,255,255) + dark_blue = 0x00008B, // rgb(0,0,139) + dark_cyan = 0x008B8B, // rgb(0,139,139) + dark_golden_rod = 0xB8860B, // rgb(184,134,11) + dark_gray = 0xA9A9A9, // rgb(169,169,169) + dark_green = 0x006400, // rgb(0,100,0) + dark_khaki = 0xBDB76B, // rgb(189,183,107) + dark_magenta = 0x8B008B, // rgb(139,0,139) + dark_olive_green = 0x556B2F, // rgb(85,107,47) + dark_orange = 0xFF8C00, // rgb(255,140,0) + dark_orchid = 0x9932CC, // rgb(153,50,204) + dark_red = 0x8B0000, // rgb(139,0,0) + dark_salmon = 0xE9967A, // rgb(233,150,122) + dark_sea_green = 0x8FBC8F, // rgb(143,188,143) + dark_slate_blue = 0x483D8B, // rgb(72,61,139) + dark_slate_gray = 0x2F4F4F, // rgb(47,79,79) + dark_turquoise = 0x00CED1, // rgb(0,206,209) + dark_violet = 0x9400D3, // rgb(148,0,211) + deep_pink = 0xFF1493, // rgb(255,20,147) + deep_sky_blue = 0x00BFFF, // rgb(0,191,255) + dim_gray = 0x696969, // rgb(105,105,105) + dodger_blue = 0x1E90FF, // rgb(30,144,255) + fire_brick = 0xB22222, // rgb(178,34,34) + floral_white = 0xFFFAF0, // rgb(255,250,240) + forest_green = 0x228B22, // rgb(34,139,34) + fuchsia = 0xFF00FF, // rgb(255,0,255) + gainsboro = 0xDCDCDC, // rgb(220,220,220) + ghost_white = 0xF8F8FF, // rgb(248,248,255) + gold = 0xFFD700, // rgb(255,215,0) + golden_rod = 0xDAA520, // rgb(218,165,32) + gray = 0x808080, // rgb(128,128,128) + green = 0x008000, // rgb(0,128,0) + green_yellow = 0xADFF2F, // rgb(173,255,47) + honey_dew = 0xF0FFF0, // rgb(240,255,240) + hot_pink = 0xFF69B4, // rgb(255,105,180) + indian_red = 0xCD5C5C, // rgb(205,92,92) + indigo = 0x4B0082, // rgb(75,0,130) + ivory = 0xFFFFF0, // rgb(255,255,240) + khaki = 0xF0E68C, // rgb(240,230,140) + lavender = 0xE6E6FA, // rgb(230,230,250) + lavender_blush = 0xFFF0F5, // rgb(255,240,245) + lawn_green = 0x7CFC00, // rgb(124,252,0) + lemon_chiffon = 0xFFFACD, // rgb(255,250,205) + light_blue = 0xADD8E6, // rgb(173,216,230) + light_coral = 0xF08080, // rgb(240,128,128) + light_cyan = 0xE0FFFF, // rgb(224,255,255) + light_golden_rod_yellow = 0xFAFAD2, // rgb(250,250,210) + light_gray = 0xD3D3D3, // rgb(211,211,211) + light_green = 0x90EE90, // rgb(144,238,144) + light_pink = 0xFFB6C1, // rgb(255,182,193) + light_salmon = 0xFFA07A, // rgb(255,160,122) + light_sea_green = 0x20B2AA, // rgb(32,178,170) + light_sky_blue = 0x87CEFA, // rgb(135,206,250) + light_slate_gray = 0x778899, // rgb(119,136,153) + light_steel_blue = 0xB0C4DE, // rgb(176,196,222) + light_yellow = 0xFFFFE0, // rgb(255,255,224) + lime = 0x00FF00, // rgb(0,255,0) + lime_green = 0x32CD32, // rgb(50,205,50) + linen = 0xFAF0E6, // rgb(250,240,230) + magenta = 0xFF00FF, // rgb(255,0,255) + maroon = 0x800000, // rgb(128,0,0) + medium_aquamarine = 0x66CDAA, // rgb(102,205,170) + medium_blue = 0x0000CD, // rgb(0,0,205) + medium_orchid = 0xBA55D3, // rgb(186,85,211) + medium_purple = 0x9370DB, // rgb(147,112,219) + medium_sea_green = 0x3CB371, // rgb(60,179,113) + medium_slate_blue = 0x7B68EE, // rgb(123,104,238) + medium_spring_green = 0x00FA9A, // rgb(0,250,154) + medium_turquoise = 0x48D1CC, // rgb(72,209,204) + medium_violet_red = 0xC71585, // rgb(199,21,133) + midnight_blue = 0x191970, // rgb(25,25,112) + mint_cream = 0xF5FFFA, // rgb(245,255,250) + misty_rose = 0xFFE4E1, // rgb(255,228,225) + moccasin = 0xFFE4B5, // rgb(255,228,181) + navajo_white = 0xFFDEAD, // rgb(255,222,173) + navy = 0x000080, // rgb(0,0,128) + old_lace = 0xFDF5E6, // rgb(253,245,230) + olive = 0x808000, // rgb(128,128,0) + olive_drab = 0x6B8E23, // rgb(107,142,35) + orange = 0xFFA500, // rgb(255,165,0) + orange_red = 0xFF4500, // rgb(255,69,0) + orchid = 0xDA70D6, // rgb(218,112,214) + pale_golden_rod = 0xEEE8AA, // rgb(238,232,170) + pale_green = 0x98FB98, // rgb(152,251,152) + pale_turquoise = 0xAFEEEE, // rgb(175,238,238) + pale_violet_red = 0xDB7093, // rgb(219,112,147) + papaya_whip = 0xFFEFD5, // rgb(255,239,213) + peach_puff = 0xFFDAB9, // rgb(255,218,185) + peru = 0xCD853F, // rgb(205,133,63) + pink = 0xFFC0CB, // rgb(255,192,203) + plum = 0xDDA0DD, // rgb(221,160,221) + powder_blue = 0xB0E0E6, // rgb(176,224,230) + purple = 0x800080, // rgb(128,0,128) + rebecca_purple = 0x663399, // rgb(102,51,153) + red = 0xFF0000, // rgb(255,0,0) + rosy_brown = 0xBC8F8F, // rgb(188,143,143) + royal_blue = 0x4169E1, // rgb(65,105,225) + saddle_brown = 0x8B4513, // rgb(139,69,19) + salmon = 0xFA8072, // rgb(250,128,114) + sandy_brown = 0xF4A460, // rgb(244,164,96) + sea_green = 0x2E8B57, // rgb(46,139,87) + sea_shell = 0xFFF5EE, // rgb(255,245,238) + sienna = 0xA0522D, // rgb(160,82,45) + silver = 0xC0C0C0, // rgb(192,192,192) + sky_blue = 0x87CEEB, // rgb(135,206,235) + slate_blue = 0x6A5ACD, // rgb(106,90,205) + slate_gray = 0x708090, // rgb(112,128,144) + snow = 0xFFFAFA, // rgb(255,250,250) + spring_green = 0x00FF7F, // rgb(0,255,127) + steel_blue = 0x4682B4, // rgb(70,130,180) + tan = 0xD2B48C, // rgb(210,180,140) + teal = 0x008080, // rgb(0,128,128) + thistle = 0xD8BFD8, // rgb(216,191,216) + tomato = 0xFF6347, // rgb(255,99,71) + turquoise = 0x40E0D0, // rgb(64,224,208) + violet = 0xEE82EE, // rgb(238,130,238) + wheat = 0xF5DEB3, // rgb(245,222,179) + white = 0xFFFFFF, // rgb(255,255,255) + white_smoke = 0xF5F5F5, // rgb(245,245,245) + yellow = 0xFFFF00, // rgb(255,255,0) + yellow_green = 0x9ACD32 // rgb(154,205,50) +}; // enum class color + +enum class terminal_color : uint8_t { + black = 30, + red, + green, + yellow, + blue, + magenta, + cyan, + white, + bright_black = 90, + bright_red, + bright_green, + bright_yellow, + bright_blue, + bright_magenta, + bright_cyan, + bright_white +}; + +enum class emphasis : uint8_t { + bold = 1, + faint = 1 << 1, + italic = 1 << 2, + underline = 1 << 3, + blink = 1 << 4, + reverse = 1 << 5, + conceal = 1 << 6, + strikethrough = 1 << 7, +}; + +// rgb is a struct for red, green and blue colors. +// Using the name "rgb" makes some editors show the color in a tooltip. +struct rgb { + FMT_CONSTEXPR rgb() : r(0), g(0), b(0) {} + FMT_CONSTEXPR rgb(uint8_t r_, uint8_t g_, uint8_t b_) : r(r_), g(g_), b(b_) {} + FMT_CONSTEXPR rgb(uint32_t hex) + : r((hex >> 16) & 0xFF), g((hex >> 8) & 0xFF), b(hex & 0xFF) {} + FMT_CONSTEXPR rgb(color hex) + : r((uint32_t(hex) >> 16) & 0xFF), + g((uint32_t(hex) >> 8) & 0xFF), + b(uint32_t(hex) & 0xFF) {} + uint8_t r; + uint8_t g; + uint8_t b; +}; + +FMT_BEGIN_DETAIL_NAMESPACE + +// color is a struct of either a rgb color or a terminal color. +struct color_type { + FMT_CONSTEXPR color_type() noexcept : is_rgb(), value{} {} + FMT_CONSTEXPR color_type(color rgb_color) noexcept : is_rgb(true), value{} { + value.rgb_color = static_cast(rgb_color); + } + FMT_CONSTEXPR color_type(rgb rgb_color) noexcept : is_rgb(true), value{} { + value.rgb_color = (static_cast(rgb_color.r) << 16) | + (static_cast(rgb_color.g) << 8) | rgb_color.b; + } + FMT_CONSTEXPR color_type(terminal_color term_color) noexcept + : is_rgb(), value{} { + value.term_color = static_cast(term_color); + } + bool is_rgb; + union color_union { + uint8_t term_color; + uint32_t rgb_color; + } value; +}; + +FMT_END_DETAIL_NAMESPACE + +/** A text style consisting of foreground and background colors and emphasis. */ +class text_style { + public: + FMT_CONSTEXPR text_style(emphasis em = emphasis()) noexcept + : set_foreground_color(), set_background_color(), ems(em) {} + + FMT_CONSTEXPR text_style& operator|=(const text_style& rhs) { + if (!set_foreground_color) { + set_foreground_color = rhs.set_foreground_color; + foreground_color = rhs.foreground_color; + } else if (rhs.set_foreground_color) { + if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb) + FMT_THROW(format_error("can't OR a terminal color")); + foreground_color.value.rgb_color |= rhs.foreground_color.value.rgb_color; + } + + if (!set_background_color) { + set_background_color = rhs.set_background_color; + background_color = rhs.background_color; + } else if (rhs.set_background_color) { + if (!background_color.is_rgb || !rhs.background_color.is_rgb) + FMT_THROW(format_error("can't OR a terminal color")); + background_color.value.rgb_color |= rhs.background_color.value.rgb_color; + } + + ems = static_cast(static_cast(ems) | + static_cast(rhs.ems)); + return *this; + } + + friend FMT_CONSTEXPR text_style operator|(text_style lhs, + const text_style& rhs) { + return lhs |= rhs; + } + + FMT_CONSTEXPR bool has_foreground() const noexcept { + return set_foreground_color; + } + FMT_CONSTEXPR bool has_background() const noexcept { + return set_background_color; + } + FMT_CONSTEXPR bool has_emphasis() const noexcept { + return static_cast(ems) != 0; + } + FMT_CONSTEXPR detail::color_type get_foreground() const noexcept { + FMT_ASSERT(has_foreground(), "no foreground specified for this style"); + return foreground_color; + } + FMT_CONSTEXPR detail::color_type get_background() const noexcept { + FMT_ASSERT(has_background(), "no background specified for this style"); + return background_color; + } + FMT_CONSTEXPR emphasis get_emphasis() const noexcept { + FMT_ASSERT(has_emphasis(), "no emphasis specified for this style"); + return ems; + } + + private: + FMT_CONSTEXPR text_style(bool is_foreground, + detail::color_type text_color) noexcept + : set_foreground_color(), set_background_color(), ems() { + if (is_foreground) { + foreground_color = text_color; + set_foreground_color = true; + } else { + background_color = text_color; + set_background_color = true; + } + } + + friend FMT_CONSTEXPR text_style fg(detail::color_type foreground) noexcept; + + friend FMT_CONSTEXPR text_style bg(detail::color_type background) noexcept; + + detail::color_type foreground_color; + detail::color_type background_color; + bool set_foreground_color; + bool set_background_color; + emphasis ems; +}; + +/** Creates a text style from the foreground (text) color. */ +FMT_CONSTEXPR inline text_style fg(detail::color_type foreground) noexcept { + return text_style(true, foreground); +} + +/** Creates a text style from the background color. */ +FMT_CONSTEXPR inline text_style bg(detail::color_type background) noexcept { + return text_style(false, background); +} + +FMT_CONSTEXPR inline text_style operator|(emphasis lhs, emphasis rhs) noexcept { + return text_style(lhs) | rhs; +} + +FMT_BEGIN_DETAIL_NAMESPACE + +template struct ansi_color_escape { + FMT_CONSTEXPR ansi_color_escape(detail::color_type text_color, + const char* esc) noexcept { + // If we have a terminal color, we need to output another escape code + // sequence. + if (!text_color.is_rgb) { + bool is_background = esc == string_view("\x1b[48;2;"); + uint32_t value = text_color.value.term_color; + // Background ASCII codes are the same as the foreground ones but with + // 10 more. + if (is_background) value += 10u; + + size_t index = 0; + buffer[index++] = static_cast('\x1b'); + buffer[index++] = static_cast('['); + + if (value >= 100u) { + buffer[index++] = static_cast('1'); + value %= 100u; + } + buffer[index++] = static_cast('0' + value / 10u); + buffer[index++] = static_cast('0' + value % 10u); + + buffer[index++] = static_cast('m'); + buffer[index++] = static_cast('\0'); + return; + } + + for (int i = 0; i < 7; i++) { + buffer[i] = static_cast(esc[i]); + } + rgb color(text_color.value.rgb_color); + to_esc(color.r, buffer + 7, ';'); + to_esc(color.g, buffer + 11, ';'); + to_esc(color.b, buffer + 15, 'm'); + buffer[19] = static_cast(0); + } + FMT_CONSTEXPR ansi_color_escape(emphasis em) noexcept { + uint8_t em_codes[num_emphases] = {}; + if (has_emphasis(em, emphasis::bold)) em_codes[0] = 1; + if (has_emphasis(em, emphasis::faint)) em_codes[1] = 2; + if (has_emphasis(em, emphasis::italic)) em_codes[2] = 3; + if (has_emphasis(em, emphasis::underline)) em_codes[3] = 4; + if (has_emphasis(em, emphasis::blink)) em_codes[4] = 5; + if (has_emphasis(em, emphasis::reverse)) em_codes[5] = 7; + if (has_emphasis(em, emphasis::conceal)) em_codes[6] = 8; + if (has_emphasis(em, emphasis::strikethrough)) em_codes[7] = 9; + + size_t index = 0; + for (size_t i = 0; i < num_emphases; ++i) { + if (!em_codes[i]) continue; + buffer[index++] = static_cast('\x1b'); + buffer[index++] = static_cast('['); + buffer[index++] = static_cast('0' + em_codes[i]); + buffer[index++] = static_cast('m'); + } + buffer[index++] = static_cast(0); + } + FMT_CONSTEXPR operator const Char*() const noexcept { return buffer; } + + FMT_CONSTEXPR const Char* begin() const noexcept { return buffer; } + FMT_CONSTEXPR_CHAR_TRAITS const Char* end() const noexcept { + return buffer + std::char_traits::length(buffer); + } + + private: + static constexpr size_t num_emphases = 8; + Char buffer[7u + 3u * num_emphases + 1u]; + + static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out, + char delimiter) noexcept { + out[0] = static_cast('0' + c / 100); + out[1] = static_cast('0' + c / 10 % 10); + out[2] = static_cast('0' + c % 10); + out[3] = static_cast(delimiter); + } + static FMT_CONSTEXPR bool has_emphasis(emphasis em, emphasis mask) noexcept { + return static_cast(em) & static_cast(mask); + } +}; + +template +FMT_CONSTEXPR ansi_color_escape make_foreground_color( + detail::color_type foreground) noexcept { + return ansi_color_escape(foreground, "\x1b[38;2;"); +} + +template +FMT_CONSTEXPR ansi_color_escape make_background_color( + detail::color_type background) noexcept { + return ansi_color_escape(background, "\x1b[48;2;"); +} + +template +FMT_CONSTEXPR ansi_color_escape make_emphasis(emphasis em) noexcept { + return ansi_color_escape(em); +} + +template inline void fputs(const Char* chars, FILE* stream) { + int result = std::fputs(chars, stream); + if (result < 0) + FMT_THROW(system_error(errno, FMT_STRING("cannot write to file"))); +} + +template <> inline void fputs(const wchar_t* chars, FILE* stream) { + int result = std::fputws(chars, stream); + if (result < 0) + FMT_THROW(system_error(errno, FMT_STRING("cannot write to file"))); +} + +template inline void reset_color(FILE* stream) { + fputs("\x1b[0m", stream); +} + +template <> inline void reset_color(FILE* stream) { + fputs(L"\x1b[0m", stream); +} + +template inline void reset_color(buffer& buffer) { + auto reset_color = string_view("\x1b[0m"); + buffer.append(reset_color.begin(), reset_color.end()); +} + +template struct styled_arg { + const T& value; + text_style style; +}; + +template +void vformat_to(buffer& buf, const text_style& ts, + basic_string_view format_str, + basic_format_args>> args) { + bool has_style = false; + if (ts.has_emphasis()) { + has_style = true; + auto emphasis = detail::make_emphasis(ts.get_emphasis()); + buf.append(emphasis.begin(), emphasis.end()); + } + if (ts.has_foreground()) { + has_style = true; + auto foreground = detail::make_foreground_color(ts.get_foreground()); + buf.append(foreground.begin(), foreground.end()); + } + if (ts.has_background()) { + has_style = true; + auto background = detail::make_background_color(ts.get_background()); + buf.append(background.begin(), background.end()); + } + detail::vformat_to(buf, format_str, args, {}); + if (has_style) detail::reset_color(buf); +} + +FMT_END_DETAIL_NAMESPACE + +template > +void vprint(std::FILE* f, const text_style& ts, const S& format, + basic_format_args>> args) { + basic_memory_buffer buf; + detail::vformat_to(buf, ts, detail::to_string_view(format), args); + if (detail::is_utf8()) { + detail::print(f, basic_string_view(buf.begin(), buf.size())); + } else { + buf.push_back(Char(0)); + detail::fputs(buf.data(), f); + } +} + +/** + \rst + Formats a string and prints it to the specified file stream using ANSI + escape sequences to specify text formatting. + + **Example**:: + + fmt::print(fmt::emphasis::bold | fg(fmt::color::red), + "Elapsed time: {0:.2f} seconds", 1.23); + \endrst + */ +template ::value)> +void print(std::FILE* f, const text_style& ts, const S& format_str, + const Args&... args) { + vprint(f, ts, format_str, + fmt::make_format_args>>(args...)); +} + +/** + \rst + Formats a string and prints it to stdout using ANSI escape sequences to + specify text formatting. + + **Example**:: + + fmt::print(fmt::emphasis::bold | fg(fmt::color::red), + "Elapsed time: {0:.2f} seconds", 1.23); + \endrst + */ +template ::value)> +void print(const text_style& ts, const S& format_str, const Args&... args) { + return print(stdout, ts, format_str, args...); +} + +template > +inline std::basic_string vformat( + const text_style& ts, const S& format_str, + basic_format_args>> args) { + basic_memory_buffer buf; + detail::vformat_to(buf, ts, detail::to_string_view(format_str), args); + return fmt::to_string(buf); +} + +/** + \rst + Formats arguments and returns the result as a string using ANSI + escape sequences to specify text formatting. + + **Example**:: + + #include + std::string message = fmt::format(fmt::emphasis::bold | fg(fmt::color::red), + "The answer is {}", 42); + \endrst +*/ +template > +inline std::basic_string format(const text_style& ts, const S& format_str, + const Args&... args) { + return fmt::vformat(ts, detail::to_string_view(format_str), + fmt::make_format_args>(args...)); +} + +/** + Formats a string with the given text_style and writes the output to ``out``. + */ +template ::value)> +OutputIt vformat_to( + OutputIt out, const text_style& ts, basic_string_view format_str, + basic_format_args>> args) { + auto&& buf = detail::get_buffer(out); + detail::vformat_to(buf, ts, format_str, args); + return detail::get_iterator(buf); +} + +/** + \rst + Formats arguments with the given text_style, writes the result to the output + iterator ``out`` and returns the iterator past the end of the output range. + + **Example**:: + + std::vector out; + fmt::format_to(std::back_inserter(out), + fmt::emphasis::bold | fg(fmt::color::red), "{}", 42); + \endrst +*/ +template >::value&& + detail::is_string::value> +inline auto format_to(OutputIt out, const text_style& ts, const S& format_str, + Args&&... args) -> + typename std::enable_if::type { + return vformat_to(out, ts, detail::to_string_view(format_str), + fmt::make_format_args>>(args...)); +} + +template +struct formatter, Char> : formatter { + template + auto format(const detail::styled_arg& arg, FormatContext& ctx) const + -> decltype(ctx.out()) { + const auto& ts = arg.style; + const auto& value = arg.value; + auto out = ctx.out(); + + bool has_style = false; + if (ts.has_emphasis()) { + has_style = true; + auto emphasis = detail::make_emphasis(ts.get_emphasis()); + out = std::copy(emphasis.begin(), emphasis.end(), out); + } + if (ts.has_foreground()) { + has_style = true; + auto foreground = + detail::make_foreground_color(ts.get_foreground()); + out = std::copy(foreground.begin(), foreground.end(), out); + } + if (ts.has_background()) { + has_style = true; + auto background = + detail::make_background_color(ts.get_background()); + out = std::copy(background.begin(), background.end(), out); + } + out = formatter::format(value, ctx); + if (has_style) { + auto reset_color = string_view("\x1b[0m"); + out = std::copy(reset_color.begin(), reset_color.end(), out); + } + return out; + } +}; + +/** + \rst + Returns an argument that will be formatted using ANSI escape sequences, + to be used in a formatting function. + + **Example**:: + + fmt::print("Elapsed time: {s:.2f} seconds", + fmt::styled(1.23, fmt::fg(fmt::color::green) | + fmt::bg(fmt::color::blue))); + \endrst + */ +template +FMT_CONSTEXPR auto styled(const T& value, text_style ts) + -> detail::styled_arg> { + return detail::styled_arg>{value, ts}; +} + +FMT_MODULE_EXPORT_END +FMT_END_NAMESPACE + +#endif // FMT_COLOR_H_ diff --git a/src/fmtlib/fmt/compile.h b/src/fmtlib/fmt/compile.h new file mode 100644 index 0000000..c09dd6f --- /dev/null +++ b/src/fmtlib/fmt/compile.h @@ -0,0 +1,603 @@ +// Formatting library for C++ - experimental format string compilation +// +// Copyright (c) 2012 - present, Victor Zverovich and fmt contributors +// All rights reserved. +// +// For the license information refer to format.h. + +#ifndef FMT_COMPILE_H_ +#define FMT_COMPILE_H_ + +#include "format.h" + +FMT_BEGIN_NAMESPACE +namespace detail { + +template +inline counting_iterator copy_str(InputIt begin, InputIt end, + counting_iterator it) { + return it + (end - begin); +} + +template class truncating_iterator_base { + protected: + OutputIt out_; + size_t limit_; + size_t count_ = 0; + + truncating_iterator_base() : out_(), limit_(0) {} + + truncating_iterator_base(OutputIt out, size_t limit) + : out_(out), limit_(limit) {} + + public: + using iterator_category = std::output_iterator_tag; + using value_type = typename std::iterator_traits::value_type; + using difference_type = std::ptrdiff_t; + using pointer = void; + using reference = void; + FMT_UNCHECKED_ITERATOR(truncating_iterator_base); + + OutputIt base() const { return out_; } + size_t count() const { return count_; } +}; + +// An output iterator that truncates the output and counts the number of objects +// written to it. +template ::value_type>::type> +class truncating_iterator; + +template +class truncating_iterator + : public truncating_iterator_base { + mutable typename truncating_iterator_base::value_type blackhole_; + + public: + using value_type = typename truncating_iterator_base::value_type; + + truncating_iterator() = default; + + truncating_iterator(OutputIt out, size_t limit) + : truncating_iterator_base(out, limit) {} + + truncating_iterator& operator++() { + if (this->count_++ < this->limit_) ++this->out_; + return *this; + } + + truncating_iterator operator++(int) { + auto it = *this; + ++*this; + return it; + } + + value_type& operator*() const { + return this->count_ < this->limit_ ? *this->out_ : blackhole_; + } +}; + +template +class truncating_iterator + : public truncating_iterator_base { + public: + truncating_iterator() = default; + + truncating_iterator(OutputIt out, size_t limit) + : truncating_iterator_base(out, limit) {} + + template truncating_iterator& operator=(T val) { + if (this->count_++ < this->limit_) *this->out_++ = val; + return *this; + } + + truncating_iterator& operator++() { return *this; } + truncating_iterator& operator++(int) { return *this; } + truncating_iterator& operator*() { return *this; } +}; + +// A compile-time string which is compiled into fast formatting code. +class compiled_string {}; + +template +struct is_compiled_string : std::is_base_of {}; + +/** + \rst + Converts a string literal *s* into a format string that will be parsed at + compile time and converted into efficient formatting code. Requires C++17 + ``constexpr if`` compiler support. + + **Example**:: + + // Converts 42 into std::string using the most efficient method and no + // runtime format string processing. + std::string s = fmt::format(FMT_COMPILE("{}"), 42); + \endrst + */ +#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) +# define FMT_COMPILE(s) \ + FMT_STRING_IMPL(s, fmt::detail::compiled_string, explicit) +#else +# define FMT_COMPILE(s) FMT_STRING(s) +#endif + +#if FMT_USE_NONTYPE_TEMPLATE_ARGS +template Str> +struct udl_compiled_string : compiled_string { + using char_type = Char; + explicit constexpr operator basic_string_view() const { + return {Str.data, N - 1}; + } +}; +#endif + +template +const T& first(const T& value, const Tail&...) { + return value; +} + +#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) +template struct type_list {}; + +// Returns a reference to the argument at index N from [first, rest...]. +template +constexpr const auto& get([[maybe_unused]] const T& first, + [[maybe_unused]] const Args&... rest) { + static_assert(N < 1 + sizeof...(Args), "index is out of bounds"); + if constexpr (N == 0) + return first; + else + return detail::get(rest...); +} + +template +constexpr int get_arg_index_by_name(basic_string_view name, + type_list) { + return get_arg_index_by_name(name); +} + +template struct get_type_impl; + +template struct get_type_impl> { + using type = + remove_cvref_t(std::declval()...))>; +}; + +template +using get_type = typename get_type_impl::type; + +template struct is_compiled_format : std::false_type {}; + +template struct text { + basic_string_view data; + using char_type = Char; + + template + constexpr OutputIt format(OutputIt out, const Args&...) const { + return write(out, data); + } +}; + +template +struct is_compiled_format> : std::true_type {}; + +template +constexpr text make_text(basic_string_view s, size_t pos, + size_t size) { + return {{&s[pos], size}}; +} + +template struct code_unit { + Char value; + using char_type = Char; + + template + constexpr OutputIt format(OutputIt out, const Args&...) const { + return write(out, value); + } +}; + +// This ensures that the argument type is convertible to `const T&`. +template +constexpr const T& get_arg_checked(const Args&... args) { + const auto& arg = detail::get(args...); + if constexpr (detail::is_named_arg>()) { + return arg.value; + } else { + return arg; + } +} + +template +struct is_compiled_format> : std::true_type {}; + +// A replacement field that refers to argument N. +template struct field { + using char_type = Char; + + template + constexpr OutputIt format(OutputIt out, const Args&... args) const { + return write(out, get_arg_checked(args...)); + } +}; + +template +struct is_compiled_format> : std::true_type {}; + +// A replacement field that refers to argument with name. +template struct runtime_named_field { + using char_type = Char; + basic_string_view name; + + template + constexpr static bool try_format_argument( + OutputIt& out, + // [[maybe_unused]] due to unused-but-set-parameter warning in GCC 7,8,9 + [[maybe_unused]] basic_string_view arg_name, const T& arg) { + if constexpr (is_named_arg::type>::value) { + if (arg_name == arg.name) { + out = write(out, arg.value); + return true; + } + } + return false; + } + + template + constexpr OutputIt format(OutputIt out, const Args&... args) const { + bool found = (try_format_argument(out, name, args) || ...); + if (!found) { + FMT_THROW(format_error("argument with specified name is not found")); + } + return out; + } +}; + +template +struct is_compiled_format> : std::true_type {}; + +// A replacement field that refers to argument N and has format specifiers. +template struct spec_field { + using char_type = Char; + formatter fmt; + + template + constexpr FMT_INLINE OutputIt format(OutputIt out, + const Args&... args) const { + const auto& vargs = + fmt::make_format_args>(args...); + basic_format_context ctx(out, vargs); + return fmt.format(get_arg_checked(args...), ctx); + } +}; + +template +struct is_compiled_format> : std::true_type {}; + +template struct concat { + L lhs; + R rhs; + using char_type = typename L::char_type; + + template + constexpr OutputIt format(OutputIt out, const Args&... args) const { + out = lhs.format(out, args...); + return rhs.format(out, args...); + } +}; + +template +struct is_compiled_format> : std::true_type {}; + +template +constexpr concat make_concat(L lhs, R rhs) { + return {lhs, rhs}; +} + +struct unknown_format {}; + +template +constexpr size_t parse_text(basic_string_view str, size_t pos) { + for (size_t size = str.size(); pos != size; ++pos) { + if (str[pos] == '{' || str[pos] == '}') break; + } + return pos; +} + +template +constexpr auto compile_format_string(S format_str); + +template +constexpr auto parse_tail(T head, S format_str) { + if constexpr (POS != + basic_string_view(format_str).size()) { + constexpr auto tail = compile_format_string(format_str); + if constexpr (std::is_same, + unknown_format>()) + return tail; + else + return make_concat(head, tail); + } else { + return head; + } +} + +template struct parse_specs_result { + formatter fmt; + size_t end; + int next_arg_id; +}; + +constexpr int manual_indexing_id = -1; + +template +constexpr parse_specs_result parse_specs(basic_string_view str, + size_t pos, int next_arg_id) { + str.remove_prefix(pos); + auto ctx = compile_parse_context(str, max_value(), nullptr, {}, + next_arg_id); + auto f = formatter(); + auto end = f.parse(ctx); + return {f, pos + fmt::detail::to_unsigned(end - str.data()) + 1, + next_arg_id == 0 ? manual_indexing_id : ctx.next_arg_id()}; +} + +template struct arg_id_handler { + arg_ref arg_id; + + constexpr int operator()() { + FMT_ASSERT(false, "handler cannot be used with automatic indexing"); + return 0; + } + constexpr int operator()(int id) { + arg_id = arg_ref(id); + return 0; + } + constexpr int operator()(basic_string_view id) { + arg_id = arg_ref(id); + return 0; + } + + constexpr void on_error(const char* message) { + FMT_THROW(format_error(message)); + } +}; + +template struct parse_arg_id_result { + arg_ref arg_id; + const Char* arg_id_end; +}; + +template +constexpr auto parse_arg_id(const Char* begin, const Char* end) { + auto handler = arg_id_handler{arg_ref{}}; + auto arg_id_end = parse_arg_id(begin, end, handler); + return parse_arg_id_result{handler.arg_id, arg_id_end}; +} + +template struct field_type { + using type = remove_cvref_t; +}; + +template +struct field_type::value>> { + using type = remove_cvref_t; +}; + +template +constexpr auto parse_replacement_field_then_tail(S format_str) { + using char_type = typename S::char_type; + constexpr auto str = basic_string_view(format_str); + constexpr char_type c = END_POS != str.size() ? str[END_POS] : char_type(); + if constexpr (c == '}') { + return parse_tail( + field::type, ARG_INDEX>(), + format_str); + } else if constexpr (c == ':') { + constexpr auto result = parse_specs::type>( + str, END_POS + 1, NEXT_ID == manual_indexing_id ? 0 : NEXT_ID); + return parse_tail( + spec_field::type, ARG_INDEX>{ + result.fmt}, + format_str); + } +} + +// Compiles a non-empty format string and returns the compiled representation +// or unknown_format() on unrecognized input. +template +constexpr auto compile_format_string(S format_str) { + using char_type = typename S::char_type; + constexpr auto str = basic_string_view(format_str); + if constexpr (str[POS] == '{') { + if constexpr (POS + 1 == str.size()) + FMT_THROW(format_error("unmatched '{' in format string")); + if constexpr (str[POS + 1] == '{') { + return parse_tail(make_text(str, POS, 1), format_str); + } else if constexpr (str[POS + 1] == '}' || str[POS + 1] == ':') { + static_assert(ID != manual_indexing_id, + "cannot switch from manual to automatic argument indexing"); + constexpr auto next_id = + ID != manual_indexing_id ? ID + 1 : manual_indexing_id; + return parse_replacement_field_then_tail, Args, + POS + 1, ID, next_id>( + format_str); + } else { + constexpr auto arg_id_result = + parse_arg_id(str.data() + POS + 1, str.data() + str.size()); + constexpr auto arg_id_end_pos = arg_id_result.arg_id_end - str.data(); + constexpr char_type c = + arg_id_end_pos != str.size() ? str[arg_id_end_pos] : char_type(); + static_assert(c == '}' || c == ':', "missing '}' in format string"); + if constexpr (arg_id_result.arg_id.kind == arg_id_kind::index) { + static_assert( + ID == manual_indexing_id || ID == 0, + "cannot switch from automatic to manual argument indexing"); + constexpr auto arg_index = arg_id_result.arg_id.val.index; + return parse_replacement_field_then_tail, + Args, arg_id_end_pos, + arg_index, manual_indexing_id>( + format_str); + } else if constexpr (arg_id_result.arg_id.kind == arg_id_kind::name) { + constexpr auto arg_index = + get_arg_index_by_name(arg_id_result.arg_id.val.name, Args{}); + if constexpr (arg_index != invalid_arg_index) { + constexpr auto next_id = + ID != manual_indexing_id ? ID + 1 : manual_indexing_id; + return parse_replacement_field_then_tail< + decltype(get_type::value), Args, arg_id_end_pos, + arg_index, next_id>(format_str); + } else { + if constexpr (c == '}') { + return parse_tail( + runtime_named_field{arg_id_result.arg_id.val.name}, + format_str); + } else if constexpr (c == ':') { + return unknown_format(); // no type info for specs parsing + } + } + } + } + } else if constexpr (str[POS] == '}') { + if constexpr (POS + 1 == str.size()) + FMT_THROW(format_error("unmatched '}' in format string")); + return parse_tail(make_text(str, POS, 1), format_str); + } else { + constexpr auto end = parse_text(str, POS + 1); + if constexpr (end - POS > 1) { + return parse_tail(make_text(str, POS, end - POS), + format_str); + } else { + return parse_tail(code_unit{str[POS]}, + format_str); + } + } +} + +template ::value)> +constexpr auto compile(S format_str) { + constexpr auto str = basic_string_view(format_str); + if constexpr (str.size() == 0) { + return detail::make_text(str, 0, 0); + } else { + constexpr auto result = + detail::compile_format_string, 0, 0>( + format_str); + return result; + } +} +#endif // defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) +} // namespace detail + +FMT_MODULE_EXPORT_BEGIN + +#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) + +template ::value)> +FMT_INLINE std::basic_string format(const CompiledFormat& cf, + const Args&... args) { + auto s = std::basic_string(); + cf.format(std::back_inserter(s), args...); + return s; +} + +template ::value)> +constexpr FMT_INLINE OutputIt format_to(OutputIt out, const CompiledFormat& cf, + const Args&... args) { + return cf.format(out, args...); +} + +template ::value)> +FMT_INLINE std::basic_string format(const S&, + Args&&... args) { + if constexpr (std::is_same::value) { + constexpr auto str = basic_string_view(S()); + if constexpr (str.size() == 2 && str[0] == '{' && str[1] == '}') { + const auto& first = detail::first(args...); + if constexpr (detail::is_named_arg< + remove_cvref_t>::value) { + return fmt::to_string(first.value); + } else { + return fmt::to_string(first); + } + } + } + constexpr auto compiled = detail::compile(S()); + if constexpr (std::is_same, + detail::unknown_format>()) { + return fmt::format( + static_cast>(S()), + std::forward(args)...); + } else { + return fmt::format(compiled, std::forward(args)...); + } +} + +template ::value)> +FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) { + constexpr auto compiled = detail::compile(S()); + if constexpr (std::is_same, + detail::unknown_format>()) { + return fmt::format_to( + out, static_cast>(S()), + std::forward(args)...); + } else { + return fmt::format_to(out, compiled, std::forward(args)...); + } +} +#endif + +template ::value)> +format_to_n_result format_to_n(OutputIt out, size_t n, + const S& format_str, Args&&... args) { + auto it = fmt::format_to(detail::truncating_iterator(out, n), + format_str, std::forward(args)...); + return {it.base(), it.count()}; +} + +template ::value)> +size_t formatted_size(const S& format_str, const Args&... args) { + return fmt::format_to(detail::counting_iterator(), format_str, args...) + .count(); +} + +template ::value)> +void print(std::FILE* f, const S& format_str, const Args&... args) { + memory_buffer buffer; + fmt::format_to(std::back_inserter(buffer), format_str, args...); + detail::print(f, {buffer.data(), buffer.size()}); +} + +template ::value)> +void print(const S& format_str, const Args&... args) { + print(stdout, format_str, args...); +} + +#if FMT_USE_NONTYPE_TEMPLATE_ARGS +inline namespace literals { +template constexpr auto operator""_cf() { + using char_t = remove_cvref_t; + return detail::udl_compiled_string(); +} +} // namespace literals +#endif + +FMT_MODULE_EXPORT_END +FMT_END_NAMESPACE + +#endif // FMT_COMPILE_H_ diff --git a/src/fmtlib/fmt/core.h b/src/fmtlib/fmt/core.h new file mode 100644 index 0000000..0e7843b --- /dev/null +++ b/src/fmtlib/fmt/core.h @@ -0,0 +1,3280 @@ +// Formatting library for C++ - the core API for char/UTF-8 +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#ifndef FMT_CORE_H_ +#define FMT_CORE_H_ + +#include // std::byte +#include // std::FILE +#include // std::strlen +#include +#include +#include +#include + +// The fmt library version in the form major * 10000 + minor * 100 + patch. +#define FMT_VERSION 90000 + +#if defined(__clang__) && !defined(__ibmxl__) +# define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__) +#else +# define FMT_CLANG_VERSION 0 +#endif + +#if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER) && \ + !defined(__NVCOMPILER) +# define FMT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) +#else +# define FMT_GCC_VERSION 0 +#endif + +#ifndef FMT_GCC_PRAGMA +// Workaround _Pragma bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59884. +# if FMT_GCC_VERSION >= 504 +# define FMT_GCC_PRAGMA(arg) _Pragma(arg) +# else +# define FMT_GCC_PRAGMA(arg) +# endif +#endif + +#ifdef __ICL +# define FMT_ICC_VERSION __ICL +#elif defined(__INTEL_COMPILER) +# define FMT_ICC_VERSION __INTEL_COMPILER +#else +# define FMT_ICC_VERSION 0 +#endif + +#ifdef _MSC_VER +# define FMT_MSC_VERSION _MSC_VER +# define FMT_MSC_WARNING(...) __pragma(warning(__VA_ARGS__)) +#else +# define FMT_MSC_VERSION 0 +# define FMT_MSC_WARNING(...) +#endif + +#ifdef _MSVC_LANG +# define FMT_CPLUSPLUS _MSVC_LANG +#else +# define FMT_CPLUSPLUS __cplusplus +#endif + +#ifdef __has_feature +# define FMT_HAS_FEATURE(x) __has_feature(x) +#else +# define FMT_HAS_FEATURE(x) 0 +#endif + +#if (defined(__has_include) || FMT_ICC_VERSION >= 1600 || \ + FMT_MSC_VERSION > 1900) && \ + !defined(__INTELLISENSE__) +# define FMT_HAS_INCLUDE(x) __has_include(x) +#else +# define FMT_HAS_INCLUDE(x) 0 +#endif + +#ifdef __has_cpp_attribute +# define FMT_HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x) +#else +# define FMT_HAS_CPP_ATTRIBUTE(x) 0 +#endif + +#define FMT_HAS_CPP14_ATTRIBUTE(attribute) \ + (FMT_CPLUSPLUS >= 201402L && FMT_HAS_CPP_ATTRIBUTE(attribute)) + +#define FMT_HAS_CPP17_ATTRIBUTE(attribute) \ + (FMT_CPLUSPLUS >= 201703L && FMT_HAS_CPP_ATTRIBUTE(attribute)) + +// Check if relaxed C++14 constexpr is supported. +// GCC doesn't allow throw in constexpr until version 6 (bug 67371). +#ifndef FMT_USE_CONSTEXPR +# if (FMT_HAS_FEATURE(cxx_relaxed_constexpr) || FMT_MSC_VERSION >= 1912 || \ + (FMT_GCC_VERSION >= 600 && FMT_CPLUSPLUS >= 201402L)) && \ + !FMT_ICC_VERSION && !defined(__NVCC__) +# define FMT_USE_CONSTEXPR 1 +# else +# define FMT_USE_CONSTEXPR 0 +# endif +#endif +#if FMT_USE_CONSTEXPR +# define FMT_CONSTEXPR constexpr +#else +# define FMT_CONSTEXPR +#endif + +#if ((FMT_CPLUSPLUS >= 202002L) && \ + (!defined(_GLIBCXX_RELEASE) || _GLIBCXX_RELEASE > 9)) || \ + (FMT_CPLUSPLUS >= 201709L && FMT_GCC_VERSION >= 1002) +# define FMT_CONSTEXPR20 constexpr +#else +# define FMT_CONSTEXPR20 +#endif + +// Check if constexpr std::char_traits<>::{compare,length} are supported. +#if defined(__GLIBCXX__) +# if FMT_CPLUSPLUS >= 201703L && defined(_GLIBCXX_RELEASE) && \ + _GLIBCXX_RELEASE >= 7 // GCC 7+ libstdc++ has _GLIBCXX_RELEASE. +# define FMT_CONSTEXPR_CHAR_TRAITS constexpr +# endif +#elif defined(_LIBCPP_VERSION) && FMT_CPLUSPLUS >= 201703L && \ + _LIBCPP_VERSION >= 4000 +# define FMT_CONSTEXPR_CHAR_TRAITS constexpr +#elif FMT_MSC_VERSION >= 1914 && FMT_CPLUSPLUS >= 201703L +# define FMT_CONSTEXPR_CHAR_TRAITS constexpr +#endif +#ifndef FMT_CONSTEXPR_CHAR_TRAITS +# define FMT_CONSTEXPR_CHAR_TRAITS +#endif + +// Check if exceptions are disabled. +#ifndef FMT_EXCEPTIONS +# if (defined(__GNUC__) && !defined(__EXCEPTIONS)) || \ + (FMT_MSC_VERSION && !_HAS_EXCEPTIONS) +# define FMT_EXCEPTIONS 0 +# else +# define FMT_EXCEPTIONS 1 +# endif +#endif + +#ifndef FMT_DEPRECATED +# if FMT_HAS_CPP14_ATTRIBUTE(deprecated) || FMT_MSC_VERSION >= 1900 +# define FMT_DEPRECATED [[deprecated]] +# else +# if (defined(__GNUC__) && !defined(__LCC__)) || defined(__clang__) +# define FMT_DEPRECATED __attribute__((deprecated)) +# elif FMT_MSC_VERSION +# define FMT_DEPRECATED __declspec(deprecated) +# else +# define FMT_DEPRECATED /* deprecated */ +# endif +# endif +#endif + +// [[noreturn]] is disabled on MSVC and NVCC because of bogus unreachable code +// warnings. +#if FMT_EXCEPTIONS && FMT_HAS_CPP_ATTRIBUTE(noreturn) && !FMT_MSC_VERSION && \ + !defined(__NVCC__) +# define FMT_NORETURN [[noreturn]] +#else +# define FMT_NORETURN +#endif + +#if FMT_HAS_CPP17_ATTRIBUTE(fallthrough) +# define FMT_FALLTHROUGH [[fallthrough]] +#elif defined(__clang__) +# define FMT_FALLTHROUGH [[clang::fallthrough]] +#elif FMT_GCC_VERSION >= 700 && \ + (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= 520) +# define FMT_FALLTHROUGH [[gnu::fallthrough]] +#else +# define FMT_FALLTHROUGH +#endif + +#ifndef FMT_NODISCARD +# if FMT_HAS_CPP17_ATTRIBUTE(nodiscard) +# define FMT_NODISCARD [[nodiscard]] +# else +# define FMT_NODISCARD +# endif +#endif + +#ifndef FMT_USE_FLOAT +# define FMT_USE_FLOAT 1 +#endif +#ifndef FMT_USE_DOUBLE +# define FMT_USE_DOUBLE 1 +#endif +#ifndef FMT_USE_LONG_DOUBLE +# define FMT_USE_LONG_DOUBLE 1 +#endif + +#ifndef FMT_INLINE +# if FMT_GCC_VERSION || FMT_CLANG_VERSION +# define FMT_INLINE inline __attribute__((always_inline)) +# else +# define FMT_INLINE inline +# endif +#endif + +#ifdef _MSC_VER +# define FMT_UNCHECKED_ITERATOR(It) \ + using _Unchecked_type = It // Mark iterator as checked. +#else +# define FMT_UNCHECKED_ITERATOR(It) using unchecked_type = It +#endif + +#ifndef FMT_BEGIN_NAMESPACE +# define FMT_BEGIN_NAMESPACE \ + namespace fmt { \ + inline namespace v9 { +# define FMT_END_NAMESPACE \ + } \ + } +#endif + +#ifndef FMT_MODULE_EXPORT +# define FMT_MODULE_EXPORT +# define FMT_MODULE_EXPORT_BEGIN +# define FMT_MODULE_EXPORT_END +# define FMT_BEGIN_DETAIL_NAMESPACE namespace detail { +# define FMT_END_DETAIL_NAMESPACE } +#endif + +#if !defined(FMT_HEADER_ONLY) && defined(_WIN32) +# define FMT_CLASS_API FMT_MSC_WARNING(suppress : 4275) +# ifdef FMT_EXPORT +# define FMT_API __declspec(dllexport) +# elif defined(FMT_SHARED) +# define FMT_API __declspec(dllimport) +# endif +#else +# define FMT_CLASS_API +# if defined(FMT_EXPORT) || defined(FMT_SHARED) +# if defined(__GNUC__) || defined(__clang__) +# define FMT_API __attribute__((visibility("default"))) +# endif +# endif +#endif +#ifndef FMT_API +# define FMT_API +#endif + +// libc++ supports string_view in pre-c++17. +#if FMT_HAS_INCLUDE() && \ + (FMT_CPLUSPLUS >= 201703L || defined(_LIBCPP_VERSION)) +# include +# define FMT_USE_STRING_VIEW +#elif FMT_HAS_INCLUDE("experimental/string_view") && FMT_CPLUSPLUS >= 201402L +# include +# define FMT_USE_EXPERIMENTAL_STRING_VIEW +#endif + +#ifndef FMT_UNICODE +# define FMT_UNICODE !FMT_MSC_VERSION +#endif + +#ifndef FMT_CONSTEVAL +# if ((FMT_GCC_VERSION >= 1000 || FMT_CLANG_VERSION >= 1101) && \ + FMT_CPLUSPLUS >= 202002L && !defined(__apple_build_version__)) || \ + (defined(__cpp_consteval) && \ + (!FMT_MSC_VERSION || _MSC_FULL_VER >= 193030704)) +// consteval is broken in MSVC before VS2022 and Apple clang 13. +# define FMT_CONSTEVAL consteval +# define FMT_HAS_CONSTEVAL +# else +# define FMT_CONSTEVAL +# endif +#endif + +#ifndef FMT_USE_NONTYPE_TEMPLATE_ARGS +# if defined(__cpp_nontype_template_args) && \ + ((FMT_GCC_VERSION >= 903 && FMT_CPLUSPLUS >= 201709L) || \ + __cpp_nontype_template_args >= 201911L) +# define FMT_USE_NONTYPE_TEMPLATE_ARGS 1 +# else +# define FMT_USE_NONTYPE_TEMPLATE_ARGS 0 +# endif +#endif + +// Enable minimal optimizations for more compact code in debug mode. +FMT_GCC_PRAGMA("GCC push_options") +#if !defined(__OPTIMIZE__) && !defined(__NVCOMPILER) +FMT_GCC_PRAGMA("GCC optimize(\"Og\")") +#endif + +FMT_BEGIN_NAMESPACE +FMT_MODULE_EXPORT_BEGIN + +// Implementations of enable_if_t and other metafunctions for older systems. +template +using enable_if_t = typename std::enable_if::type; +template +using conditional_t = typename std::conditional::type; +template using bool_constant = std::integral_constant; +template +using remove_reference_t = typename std::remove_reference::type; +template +using remove_const_t = typename std::remove_const::type; +template +using remove_cvref_t = typename std::remove_cv>::type; +template struct type_identity { using type = T; }; +template using type_identity_t = typename type_identity::type; +template +using underlying_t = typename std::underlying_type::type; + +template struct disjunction : std::false_type {}; +template struct disjunction

: P {}; +template +struct disjunction + : conditional_t> {}; + +template struct conjunction : std::true_type {}; +template struct conjunction

: P {}; +template +struct conjunction + : conditional_t, P1> {}; + +struct monostate { + constexpr monostate() {} +}; + +// An enable_if helper to be used in template parameters which results in much +// shorter symbols: https://godbolt.org/z/sWw4vP. Extra parentheses are needed +// to workaround a bug in MSVC 2019 (see #1140 and #1186). +#ifdef FMT_DOC +# define FMT_ENABLE_IF(...) +#else +# define FMT_ENABLE_IF(...) enable_if_t<(__VA_ARGS__), int> = 0 +#endif + +FMT_BEGIN_DETAIL_NAMESPACE + +// Suppresses "unused variable" warnings with the method described in +// https://herbsutter.com/2009/10/18/mailbag-shutting-up-compiler-warnings/. +// (void)var does not work on many Intel compilers. +template FMT_CONSTEXPR void ignore_unused(const T&...) {} + +constexpr FMT_INLINE auto is_constant_evaluated( + bool default_value = false) noexcept -> bool { +#ifdef __cpp_lib_is_constant_evaluated + ignore_unused(default_value); + return std::is_constant_evaluated(); +#else + return default_value; +#endif +} + +// Suppresses "conditional expression is constant" warnings. +template constexpr FMT_INLINE auto const_check(T value) -> T { + return value; +} + +FMT_NORETURN FMT_API void assert_fail(const char* file, int line, + const char* message); + +#ifndef FMT_ASSERT +# ifdef NDEBUG +// FMT_ASSERT is not empty to avoid -Wempty-body. +# define FMT_ASSERT(condition, message) \ + ::fmt::detail::ignore_unused((condition), (message)) +# else +# define FMT_ASSERT(condition, message) \ + ((condition) /* void() fails with -Winvalid-constexpr on clang 4.0.1 */ \ + ? (void)0 \ + : ::fmt::detail::assert_fail(__FILE__, __LINE__, (message))) +# endif +#endif + +#if defined(FMT_USE_STRING_VIEW) +template using std_string_view = std::basic_string_view; +#elif defined(FMT_USE_EXPERIMENTAL_STRING_VIEW) +template +using std_string_view = std::experimental::basic_string_view; +#else +template struct std_string_view {}; +#endif + +#ifdef FMT_USE_INT128 +// Do nothing. +#elif defined(__SIZEOF_INT128__) && !defined(__NVCC__) && \ + !(FMT_CLANG_VERSION && FMT_MSC_VERSION) +# define FMT_USE_INT128 1 +using int128_opt = __int128_t; // An optional native 128-bit integer. +using uint128_opt = __uint128_t; +template inline auto convert_for_visit(T value) -> T { + return value; +} +#else +# define FMT_USE_INT128 0 +#endif +#if !FMT_USE_INT128 +enum class int128_opt {}; +enum class uint128_opt {}; +// Reduce template instantiations. +template auto convert_for_visit(T) -> monostate { return {}; } +#endif + +// Casts a nonnegative integer to unsigned. +template +FMT_CONSTEXPR auto to_unsigned(Int value) -> + typename std::make_unsigned::type { + FMT_ASSERT(value >= 0, "negative value"); + return static_cast::type>(value); +} + +FMT_MSC_WARNING(suppress : 4566) constexpr unsigned char micro[] = "\u00B5"; + +constexpr auto is_utf8() -> bool { + // Avoid buggy sign extensions in MSVC's constant evaluation mode (#2297). + using uchar = unsigned char; + return FMT_UNICODE || (sizeof(micro) == 3 && uchar(micro[0]) == 0xC2 && + uchar(micro[1]) == 0xB5); +} +FMT_END_DETAIL_NAMESPACE + +/** + An implementation of ``std::basic_string_view`` for pre-C++17. It provides a + subset of the API. ``fmt::basic_string_view`` is used for format strings even + if ``std::string_view`` is available to prevent issues when a library is + compiled with a different ``-std`` option than the client code (which is not + recommended). + */ +template class basic_string_view { + private: + const Char* data_; + size_t size_; + + public: + using value_type = Char; + using iterator = const Char*; + + constexpr basic_string_view() noexcept : data_(nullptr), size_(0) {} + + /** Constructs a string reference object from a C string and a size. */ + constexpr basic_string_view(const Char* s, size_t count) noexcept + : data_(s), size_(count) {} + + /** + \rst + Constructs a string reference object from a C string computing + the size with ``std::char_traits::length``. + \endrst + */ + FMT_CONSTEXPR_CHAR_TRAITS + FMT_INLINE + basic_string_view(const Char* s) + : data_(s), + size_(detail::const_check(std::is_same::value && + !detail::is_constant_evaluated(true)) + ? std::strlen(reinterpret_cast(s)) + : std::char_traits::length(s)) {} + + /** Constructs a string reference from a ``std::basic_string`` object. */ + template + FMT_CONSTEXPR basic_string_view( + const std::basic_string& s) noexcept + : data_(s.data()), size_(s.size()) {} + + template >::value)> + FMT_CONSTEXPR basic_string_view(S s) noexcept + : data_(s.data()), size_(s.size()) {} + + /** Returns a pointer to the string data. */ + constexpr auto data() const noexcept -> const Char* { return data_; } + + /** Returns the string size. */ + constexpr auto size() const noexcept -> size_t { return size_; } + + constexpr auto begin() const noexcept -> iterator { return data_; } + constexpr auto end() const noexcept -> iterator { return data_ + size_; } + + constexpr auto operator[](size_t pos) const noexcept -> const Char& { + return data_[pos]; + } + + FMT_CONSTEXPR void remove_prefix(size_t n) noexcept { + data_ += n; + size_ -= n; + } + + // Lexicographically compare this string reference to other. + FMT_CONSTEXPR_CHAR_TRAITS auto compare(basic_string_view other) const -> int { + size_t str_size = size_ < other.size_ ? size_ : other.size_; + int result = std::char_traits::compare(data_, other.data_, str_size); + if (result == 0) + result = size_ == other.size_ ? 0 : (size_ < other.size_ ? -1 : 1); + return result; + } + + FMT_CONSTEXPR_CHAR_TRAITS friend auto operator==(basic_string_view lhs, + basic_string_view rhs) + -> bool { + return lhs.compare(rhs) == 0; + } + friend auto operator!=(basic_string_view lhs, basic_string_view rhs) -> bool { + return lhs.compare(rhs) != 0; + } + friend auto operator<(basic_string_view lhs, basic_string_view rhs) -> bool { + return lhs.compare(rhs) < 0; + } + friend auto operator<=(basic_string_view lhs, basic_string_view rhs) -> bool { + return lhs.compare(rhs) <= 0; + } + friend auto operator>(basic_string_view lhs, basic_string_view rhs) -> bool { + return lhs.compare(rhs) > 0; + } + friend auto operator>=(basic_string_view lhs, basic_string_view rhs) -> bool { + return lhs.compare(rhs) >= 0; + } +}; + +using string_view = basic_string_view; + +/** Specifies if ``T`` is a character type. Can be specialized by users. */ +template struct is_char : std::false_type {}; +template <> struct is_char : std::true_type {}; + +FMT_BEGIN_DETAIL_NAMESPACE + +// A base class for compile-time strings. +struct compile_string {}; + +template +struct is_compile_string : std::is_base_of {}; + +// Returns a string view of `s`. +template ::value)> +FMT_INLINE auto to_string_view(const Char* s) -> basic_string_view { + return s; +} +template +inline auto to_string_view(const std::basic_string& s) + -> basic_string_view { + return s; +} +template +constexpr auto to_string_view(basic_string_view s) + -> basic_string_view { + return s; +} +template >::value)> +inline auto to_string_view(std_string_view s) -> basic_string_view { + return s; +} +template ::value)> +constexpr auto to_string_view(const S& s) + -> basic_string_view { + return basic_string_view(s); +} +void to_string_view(...); + +// Specifies whether S is a string type convertible to fmt::basic_string_view. +// It should be a constexpr function but MSVC 2017 fails to compile it in +// enable_if and MSVC 2015 fails to compile it as an alias template. +// ADL invocation of to_string_view is DEPRECATED! +template +struct is_string : std::is_class()))> { +}; + +template struct char_t_impl {}; +template struct char_t_impl::value>> { + using result = decltype(to_string_view(std::declval())); + using type = typename result::value_type; +}; + +enum class type { + none_type, + // Integer types should go first, + int_type, + uint_type, + long_long_type, + ulong_long_type, + int128_type, + uint128_type, + bool_type, + char_type, + last_integer_type = char_type, + // followed by floating-point types. + float_type, + double_type, + long_double_type, + last_numeric_type = long_double_type, + cstring_type, + string_type, + pointer_type, + custom_type +}; + +// Maps core type T to the corresponding type enum constant. +template +struct type_constant : std::integral_constant {}; + +#define FMT_TYPE_CONSTANT(Type, constant) \ + template \ + struct type_constant \ + : std::integral_constant {} + +FMT_TYPE_CONSTANT(int, int_type); +FMT_TYPE_CONSTANT(unsigned, uint_type); +FMT_TYPE_CONSTANT(long long, long_long_type); +FMT_TYPE_CONSTANT(unsigned long long, ulong_long_type); +FMT_TYPE_CONSTANT(int128_opt, int128_type); +FMT_TYPE_CONSTANT(uint128_opt, uint128_type); +FMT_TYPE_CONSTANT(bool, bool_type); +FMT_TYPE_CONSTANT(Char, char_type); +FMT_TYPE_CONSTANT(float, float_type); +FMT_TYPE_CONSTANT(double, double_type); +FMT_TYPE_CONSTANT(long double, long_double_type); +FMT_TYPE_CONSTANT(const Char*, cstring_type); +FMT_TYPE_CONSTANT(basic_string_view, string_type); +FMT_TYPE_CONSTANT(const void*, pointer_type); + +constexpr bool is_integral_type(type t) { + return t > type::none_type && t <= type::last_integer_type; +} + +constexpr bool is_arithmetic_type(type t) { + return t > type::none_type && t <= type::last_numeric_type; +} + +FMT_NORETURN FMT_API void throw_format_error(const char* message); + +struct error_handler { + constexpr error_handler() = default; + constexpr error_handler(const error_handler&) = default; + + // This function is intentionally not constexpr to give a compile-time error. + FMT_NORETURN void on_error(const char* message) { + throw_format_error(message); + } +}; +FMT_END_DETAIL_NAMESPACE + +/** String's character type. */ +template using char_t = typename detail::char_t_impl::type; + +/** + \rst + Parsing context consisting of a format string range being parsed and an + argument counter for automatic indexing. + You can use the ``format_parse_context`` type alias for ``char`` instead. + \endrst + */ +template +class basic_format_parse_context : private ErrorHandler { + private: + basic_string_view format_str_; + int next_arg_id_; + + FMT_CONSTEXPR void do_check_arg_id(int id); + + public: + using char_type = Char; + using iterator = typename basic_string_view::iterator; + + explicit constexpr basic_format_parse_context( + basic_string_view format_str, ErrorHandler eh = {}, + int next_arg_id = 0) + : ErrorHandler(eh), format_str_(format_str), next_arg_id_(next_arg_id) {} + + /** + Returns an iterator to the beginning of the format string range being + parsed. + */ + constexpr auto begin() const noexcept -> iterator { + return format_str_.begin(); + } + + /** + Returns an iterator past the end of the format string range being parsed. + */ + constexpr auto end() const noexcept -> iterator { return format_str_.end(); } + + /** Advances the begin iterator to ``it``. */ + FMT_CONSTEXPR void advance_to(iterator it) { + format_str_.remove_prefix(detail::to_unsigned(it - begin())); + } + + /** + Reports an error if using the manual argument indexing; otherwise returns + the next argument index and switches to the automatic indexing. + */ + FMT_CONSTEXPR auto next_arg_id() -> int { + if (next_arg_id_ < 0) { + on_error("cannot switch from manual to automatic argument indexing"); + return 0; + } + int id = next_arg_id_++; + do_check_arg_id(id); + return id; + } + + /** + Reports an error if using the automatic argument indexing; otherwise + switches to the manual indexing. + */ + FMT_CONSTEXPR void check_arg_id(int id) { + if (next_arg_id_ > 0) { + on_error("cannot switch from automatic to manual argument indexing"); + return; + } + next_arg_id_ = -1; + do_check_arg_id(id); + } + + FMT_CONSTEXPR void check_arg_id(basic_string_view) {} + + FMT_CONSTEXPR void on_error(const char* message) { + ErrorHandler::on_error(message); + } + + constexpr auto error_handler() const -> ErrorHandler { return *this; } +}; + +using format_parse_context = basic_format_parse_context; + +FMT_BEGIN_DETAIL_NAMESPACE +// A parse context with extra data used only in compile-time checks. +template +class compile_parse_context + : public basic_format_parse_context { + private: + int num_args_; + const type* types_; + using base = basic_format_parse_context; + + public: + explicit FMT_CONSTEXPR compile_parse_context( + basic_string_view format_str, int num_args, const type* types, + ErrorHandler eh = {}, int next_arg_id = 0) + : base(format_str, eh, next_arg_id), num_args_(num_args), types_(types) {} + + constexpr int num_args() const { return num_args_; } + + FMT_CONSTEXPR auto next_arg_id() -> int { + int id = base::next_arg_id(); + if (id >= num_args_) this->on_error("argument not found"); + return id; + } + + FMT_CONSTEXPR void check_arg_id(int id) { + base::check_arg_id(id); + if (id >= num_args_) this->on_error("argument not found"); + } + using base::check_arg_id; +}; +FMT_END_DETAIL_NAMESPACE + +template +FMT_CONSTEXPR void +basic_format_parse_context::do_check_arg_id(int id) { + // Argument id is only checked at compile-time during parsing because + // formatting has its own validation. + if (detail::is_constant_evaluated() && FMT_GCC_VERSION >= 1200) { + using context = detail::compile_parse_context; + if (id >= static_cast(this)->num_args()) + on_error("argument not found"); + } +} + +template class basic_format_arg; +template class basic_format_args; +template class dynamic_format_arg_store; + +// A formatter for objects of type T. +template +struct formatter { + // A deleted default constructor indicates a disabled formatter. + formatter() = delete; +}; + +// Specifies if T has an enabled formatter specialization. A type can be +// formattable even if it doesn't have a formatter e.g. via a conversion. +template +using has_formatter = + std::is_constructible>; + +// Checks whether T is a container with contiguous storage. +template struct is_contiguous : std::false_type {}; +template +struct is_contiguous> : std::true_type {}; + +class appender; + +FMT_BEGIN_DETAIL_NAMESPACE + +template +constexpr auto has_const_formatter_impl(T*) + -> decltype(typename Context::template formatter_type().format( + std::declval(), std::declval()), + true) { + return true; +} +template +constexpr auto has_const_formatter_impl(...) -> bool { + return false; +} +template +constexpr auto has_const_formatter() -> bool { + return has_const_formatter_impl(static_cast(nullptr)); +} + +// Extracts a reference to the container from back_insert_iterator. +template +inline auto get_container(std::back_insert_iterator it) + -> Container& { + using base = std::back_insert_iterator; + struct accessor : base { + accessor(base b) : base(b) {} + using base::container; + }; + return *accessor(it).container; +} + +template +FMT_CONSTEXPR auto copy_str(InputIt begin, InputIt end, OutputIt out) + -> OutputIt { + while (begin != end) *out++ = static_cast(*begin++); + return out; +} + +template , U>::value&& is_char::value)> +FMT_CONSTEXPR auto copy_str(T* begin, T* end, U* out) -> U* { + if (is_constant_evaluated()) return copy_str(begin, end, out); + auto size = to_unsigned(end - begin); + memcpy(out, begin, size * sizeof(U)); + return out + size; +} + +/** + \rst + A contiguous memory buffer with an optional growing ability. It is an internal + class and shouldn't be used directly, only via `~fmt::basic_memory_buffer`. + \endrst + */ +template class buffer { + private: + T* ptr_; + size_t size_; + size_t capacity_; + + protected: + // Don't initialize ptr_ since it is not accessed to save a few cycles. + FMT_MSC_WARNING(suppress : 26495) + buffer(size_t sz) noexcept : size_(sz), capacity_(sz) {} + + FMT_CONSTEXPR20 buffer(T* p = nullptr, size_t sz = 0, size_t cap = 0) noexcept + : ptr_(p), size_(sz), capacity_(cap) {} + + FMT_CONSTEXPR20 ~buffer() = default; + buffer(buffer&&) = default; + + /** Sets the buffer data and capacity. */ + FMT_CONSTEXPR void set(T* buf_data, size_t buf_capacity) noexcept { + ptr_ = buf_data; + capacity_ = buf_capacity; + } + + /** Increases the buffer capacity to hold at least *capacity* elements. */ + virtual FMT_CONSTEXPR20 void grow(size_t capacity) = 0; + + public: + using value_type = T; + using const_reference = const T&; + + buffer(const buffer&) = delete; + void operator=(const buffer&) = delete; + + auto begin() noexcept -> T* { return ptr_; } + auto end() noexcept -> T* { return ptr_ + size_; } + + auto begin() const noexcept -> const T* { return ptr_; } + auto end() const noexcept -> const T* { return ptr_ + size_; } + + /** Returns the size of this buffer. */ + constexpr auto size() const noexcept -> size_t { return size_; } + + /** Returns the capacity of this buffer. */ + constexpr auto capacity() const noexcept -> size_t { return capacity_; } + + /** Returns a pointer to the buffer data. */ + FMT_CONSTEXPR auto data() noexcept -> T* { return ptr_; } + + /** Returns a pointer to the buffer data. */ + FMT_CONSTEXPR auto data() const noexcept -> const T* { return ptr_; } + + /** Clears this buffer. */ + void clear() { size_ = 0; } + + // Tries resizing the buffer to contain *count* elements. If T is a POD type + // the new elements may not be initialized. + FMT_CONSTEXPR20 void try_resize(size_t count) { + try_reserve(count); + size_ = count <= capacity_ ? count : capacity_; + } + + // Tries increasing the buffer capacity to *new_capacity*. It can increase the + // capacity by a smaller amount than requested but guarantees there is space + // for at least one additional element either by increasing the capacity or by + // flushing the buffer if it is full. + FMT_CONSTEXPR20 void try_reserve(size_t new_capacity) { + if (new_capacity > capacity_) grow(new_capacity); + } + + FMT_CONSTEXPR20 void push_back(const T& value) { + try_reserve(size_ + 1); + ptr_[size_++] = value; + } + + /** Appends data to the end of the buffer. */ + template void append(const U* begin, const U* end); + + template FMT_CONSTEXPR auto operator[](I index) -> T& { + return ptr_[index]; + } + template + FMT_CONSTEXPR auto operator[](I index) const -> const T& { + return ptr_[index]; + } +}; + +struct buffer_traits { + explicit buffer_traits(size_t) {} + auto count() const -> size_t { return 0; } + auto limit(size_t size) -> size_t { return size; } +}; + +class fixed_buffer_traits { + private: + size_t count_ = 0; + size_t limit_; + + public: + explicit fixed_buffer_traits(size_t limit) : limit_(limit) {} + auto count() const -> size_t { return count_; } + auto limit(size_t size) -> size_t { + size_t n = limit_ > count_ ? limit_ - count_ : 0; + count_ += size; + return size < n ? size : n; + } +}; + +// A buffer that writes to an output iterator when flushed. +template +class iterator_buffer final : public Traits, public buffer { + private: + OutputIt out_; + enum { buffer_size = 256 }; + T data_[buffer_size]; + + protected: + FMT_CONSTEXPR20 void grow(size_t) override { + if (this->size() == buffer_size) flush(); + } + + void flush() { + auto size = this->size(); + this->clear(); + out_ = copy_str(data_, data_ + this->limit(size), out_); + } + + public: + explicit iterator_buffer(OutputIt out, size_t n = buffer_size) + : Traits(n), buffer(data_, 0, buffer_size), out_(out) {} + iterator_buffer(iterator_buffer&& other) + : Traits(other), buffer(data_, 0, buffer_size), out_(other.out_) {} + ~iterator_buffer() { flush(); } + + auto out() -> OutputIt { + flush(); + return out_; + } + auto count() const -> size_t { return Traits::count() + this->size(); } +}; + +template +class iterator_buffer final + : public fixed_buffer_traits, + public buffer { + private: + T* out_; + enum { buffer_size = 256 }; + T data_[buffer_size]; + + protected: + FMT_CONSTEXPR20 void grow(size_t) override { + if (this->size() == this->capacity()) flush(); + } + + void flush() { + size_t n = this->limit(this->size()); + if (this->data() == out_) { + out_ += n; + this->set(data_, buffer_size); + } + this->clear(); + } + + public: + explicit iterator_buffer(T* out, size_t n = buffer_size) + : fixed_buffer_traits(n), buffer(out, 0, n), out_(out) {} + iterator_buffer(iterator_buffer&& other) + : fixed_buffer_traits(other), + buffer(std::move(other)), + out_(other.out_) { + if (this->data() != out_) { + this->set(data_, buffer_size); + this->clear(); + } + } + ~iterator_buffer() { flush(); } + + auto out() -> T* { + flush(); + return out_; + } + auto count() const -> size_t { + return fixed_buffer_traits::count() + this->size(); + } +}; + +template class iterator_buffer final : public buffer { + protected: + FMT_CONSTEXPR20 void grow(size_t) override {} + + public: + explicit iterator_buffer(T* out, size_t = 0) : buffer(out, 0, ~size_t()) {} + + auto out() -> T* { return &*this->end(); } +}; + +// A buffer that writes to a container with the contiguous storage. +template +class iterator_buffer, + enable_if_t::value, + typename Container::value_type>> + final : public buffer { + private: + Container& container_; + + protected: + FMT_CONSTEXPR20 void grow(size_t capacity) override { + container_.resize(capacity); + this->set(&container_[0], capacity); + } + + public: + explicit iterator_buffer(Container& c) + : buffer(c.size()), container_(c) {} + explicit iterator_buffer(std::back_insert_iterator out, size_t = 0) + : iterator_buffer(get_container(out)) {} + + auto out() -> std::back_insert_iterator { + return std::back_inserter(container_); + } +}; + +// A buffer that counts the number of code units written discarding the output. +template class counting_buffer final : public buffer { + private: + enum { buffer_size = 256 }; + T data_[buffer_size]; + size_t count_ = 0; + + protected: + FMT_CONSTEXPR20 void grow(size_t) override { + if (this->size() != buffer_size) return; + count_ += this->size(); + this->clear(); + } + + public: + counting_buffer() : buffer(data_, 0, buffer_size) {} + + auto count() -> size_t { return count_ + this->size(); } +}; + +template +using buffer_appender = conditional_t::value, appender, + std::back_insert_iterator>>; + +// Maps an output iterator to a buffer. +template +auto get_buffer(OutputIt out) -> iterator_buffer { + return iterator_buffer(out); +} + +template +auto get_iterator(Buffer& buf) -> decltype(buf.out()) { + return buf.out(); +} +template auto get_iterator(buffer& buf) -> buffer_appender { + return buffer_appender(buf); +} + +template +struct fallback_formatter { + fallback_formatter() = delete; +}; + +// Specifies if T has an enabled fallback_formatter specialization. +template +using has_fallback_formatter = +#ifdef FMT_DEPRECATED_OSTREAM + std::is_constructible>; +#else + std::false_type; +#endif + +struct view {}; + +template struct named_arg : view { + const Char* name; + const T& value; + named_arg(const Char* n, const T& v) : name(n), value(v) {} +}; + +template struct named_arg_info { + const Char* name; + int id; +}; + +template +struct arg_data { + // args_[0].named_args points to named_args_ to avoid bloating format_args. + // +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning. + T args_[1 + (NUM_ARGS != 0 ? NUM_ARGS : +1)]; + named_arg_info named_args_[NUM_NAMED_ARGS]; + + template + arg_data(const U&... init) : args_{T(named_args_, NUM_NAMED_ARGS), init...} {} + arg_data(const arg_data& other) = delete; + auto args() const -> const T* { return args_ + 1; } + auto named_args() -> named_arg_info* { return named_args_; } +}; + +template +struct arg_data { + // +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning. + T args_[NUM_ARGS != 0 ? NUM_ARGS : +1]; + + template + FMT_CONSTEXPR FMT_INLINE arg_data(const U&... init) : args_{init...} {} + FMT_CONSTEXPR FMT_INLINE auto args() const -> const T* { return args_; } + FMT_CONSTEXPR FMT_INLINE auto named_args() -> std::nullptr_t { + return nullptr; + } +}; + +template +inline void init_named_args(named_arg_info*, int, int) {} + +template struct is_named_arg : std::false_type {}; +template struct is_statically_named_arg : std::false_type {}; + +template +struct is_named_arg> : std::true_type {}; + +template ::value)> +void init_named_args(named_arg_info* named_args, int arg_count, + int named_arg_count, const T&, const Tail&... args) { + init_named_args(named_args, arg_count + 1, named_arg_count, args...); +} + +template ::value)> +void init_named_args(named_arg_info* named_args, int arg_count, + int named_arg_count, const T& arg, const Tail&... args) { + named_args[named_arg_count++] = {arg.name, arg_count}; + init_named_args(named_args, arg_count + 1, named_arg_count, args...); +} + +template +FMT_CONSTEXPR FMT_INLINE void init_named_args(std::nullptr_t, int, int, + const Args&...) {} + +template constexpr auto count() -> size_t { return B ? 1 : 0; } +template constexpr auto count() -> size_t { + return (B1 ? 1 : 0) + count(); +} + +template constexpr auto count_named_args() -> size_t { + return count::value...>(); +} + +template +constexpr auto count_statically_named_args() -> size_t { + return count::value...>(); +} + +struct unformattable {}; +struct unformattable_char : unformattable {}; +struct unformattable_const : unformattable {}; +struct unformattable_pointer : unformattable {}; + +template struct string_value { + const Char* data; + size_t size; +}; + +template struct named_arg_value { + const named_arg_info* data; + size_t size; +}; + +template struct custom_value { + using parse_context = typename Context::parse_context_type; + void* value; + void (*format)(void* arg, parse_context& parse_ctx, Context& ctx); +}; + +// A formatting argument value. +template class value { + public: + using char_type = typename Context::char_type; + + union { + monostate no_value; + int int_value; + unsigned uint_value; + long long long_long_value; + unsigned long long ulong_long_value; + int128_opt int128_value; + uint128_opt uint128_value; + bool bool_value; + char_type char_value; + float float_value; + double double_value; + long double long_double_value; + const void* pointer; + string_value string; + custom_value custom; + named_arg_value named_args; + }; + + constexpr FMT_INLINE value() : no_value() {} + constexpr FMT_INLINE value(int val) : int_value(val) {} + constexpr FMT_INLINE value(unsigned val) : uint_value(val) {} + constexpr FMT_INLINE value(long long val) : long_long_value(val) {} + constexpr FMT_INLINE value(unsigned long long val) : ulong_long_value(val) {} + FMT_INLINE value(int128_opt val) : int128_value(val) {} + FMT_INLINE value(uint128_opt val) : uint128_value(val) {} + constexpr FMT_INLINE value(float val) : float_value(val) {} + constexpr FMT_INLINE value(double val) : double_value(val) {} + FMT_INLINE value(long double val) : long_double_value(val) {} + constexpr FMT_INLINE value(bool val) : bool_value(val) {} + constexpr FMT_INLINE value(char_type val) : char_value(val) {} + FMT_CONSTEXPR FMT_INLINE value(const char_type* val) { + string.data = val; + if (is_constant_evaluated()) string.size = {}; + } + FMT_CONSTEXPR FMT_INLINE value(basic_string_view val) { + string.data = val.data(); + string.size = val.size(); + } + FMT_INLINE value(const void* val) : pointer(val) {} + FMT_INLINE value(const named_arg_info* args, size_t size) + : named_args{args, size} {} + + template FMT_CONSTEXPR FMT_INLINE value(T& val) { + using value_type = remove_cvref_t; + custom.value = const_cast(&val); + // Get the formatter type through the context to allow different contexts + // have different extension points, e.g. `formatter` for `format` and + // `printf_formatter` for `printf`. + custom.format = format_custom_arg< + value_type, + conditional_t::value, + typename Context::template formatter_type, + fallback_formatter>>; + } + value(unformattable); + value(unformattable_char); + value(unformattable_const); + value(unformattable_pointer); + + private: + // Formats an argument of a custom type, such as a user-defined class. + template + static void format_custom_arg(void* arg, + typename Context::parse_context_type& parse_ctx, + Context& ctx) { + auto f = Formatter(); + parse_ctx.advance_to(f.parse(parse_ctx)); + using qualified_type = + conditional_t(), const T, T>; + ctx.advance_to(f.format(*static_cast(arg), ctx)); + } +}; + +template +FMT_CONSTEXPR auto make_arg(T&& value) -> basic_format_arg; + +// To minimize the number of types we need to deal with, long is translated +// either to int or to long long depending on its size. +enum { long_short = sizeof(long) == sizeof(int) }; +using long_type = conditional_t; +using ulong_type = conditional_t; + +#ifdef __cpp_lib_byte +inline auto format_as(std::byte b) -> unsigned char { + return static_cast(b); +} +#endif + +template struct has_format_as { + template ::value&& std::is_integral::value)> + static auto check(U*) -> std::true_type; + static auto check(...) -> std::false_type; + + enum { value = decltype(check(static_cast(nullptr)))::value }; +}; + +// Maps formatting arguments to core types. +// arg_mapper reports errors by returning unformattable instead of using +// static_assert because it's used in the is_formattable trait. +template struct arg_mapper { + using char_type = typename Context::char_type; + + FMT_CONSTEXPR FMT_INLINE auto map(signed char val) -> int { return val; } + FMT_CONSTEXPR FMT_INLINE auto map(unsigned char val) -> unsigned { + return val; + } + FMT_CONSTEXPR FMT_INLINE auto map(short val) -> int { return val; } + FMT_CONSTEXPR FMT_INLINE auto map(unsigned short val) -> unsigned { + return val; + } + FMT_CONSTEXPR FMT_INLINE auto map(int val) -> int { return val; } + FMT_CONSTEXPR FMT_INLINE auto map(unsigned val) -> unsigned { return val; } + FMT_CONSTEXPR FMT_INLINE auto map(long val) -> long_type { return val; } + FMT_CONSTEXPR FMT_INLINE auto map(unsigned long val) -> ulong_type { + return val; + } + FMT_CONSTEXPR FMT_INLINE auto map(long long val) -> long long { return val; } + FMT_CONSTEXPR FMT_INLINE auto map(unsigned long long val) + -> unsigned long long { + return val; + } + FMT_CONSTEXPR FMT_INLINE auto map(int128_opt val) -> int128_opt { + return val; + } + FMT_CONSTEXPR FMT_INLINE auto map(uint128_opt val) -> uint128_opt { + return val; + } + FMT_CONSTEXPR FMT_INLINE auto map(bool val) -> bool { return val; } + + template ::value || + std::is_same::value)> + FMT_CONSTEXPR FMT_INLINE auto map(T val) -> char_type { + return val; + } + template ::value || +#ifdef __cpp_char8_t + std::is_same::value || +#endif + std::is_same::value || + std::is_same::value) && + !std::is_same::value, + int> = 0> + FMT_CONSTEXPR FMT_INLINE auto map(T) -> unformattable_char { + return {}; + } + + FMT_CONSTEXPR FMT_INLINE auto map(float val) -> float { return val; } + FMT_CONSTEXPR FMT_INLINE auto map(double val) -> double { return val; } + FMT_CONSTEXPR FMT_INLINE auto map(long double val) -> long double { + return val; + } + + FMT_CONSTEXPR FMT_INLINE auto map(char_type* val) -> const char_type* { + return val; + } + FMT_CONSTEXPR FMT_INLINE auto map(const char_type* val) -> const char_type* { + return val; + } + template ::value && !std::is_pointer::value && + std::is_same>::value)> + FMT_CONSTEXPR FMT_INLINE auto map(const T& val) + -> basic_string_view { + return to_string_view(val); + } + template ::value && !std::is_pointer::value && + !std::is_same>::value)> + FMT_CONSTEXPR FMT_INLINE auto map(const T&) -> unformattable_char { + return {}; + } + template >::value && + !is_string::value && !has_formatter::value && + !has_fallback_formatter::value)> + FMT_CONSTEXPR FMT_INLINE auto map(const T& val) + -> basic_string_view { + return basic_string_view(val); + } + template >::value && + !std::is_convertible>::value && + !is_string::value && !has_formatter::value && + !has_fallback_formatter::value)> + FMT_CONSTEXPR FMT_INLINE auto map(const T& val) + -> basic_string_view { + return std_string_view(val); + } + + FMT_CONSTEXPR FMT_INLINE auto map(void* val) -> const void* { return val; } + FMT_CONSTEXPR FMT_INLINE auto map(const void* val) -> const void* { + return val; + } + FMT_CONSTEXPR FMT_INLINE auto map(std::nullptr_t val) -> const void* { + return val; + } + + // We use SFINAE instead of a const T* parameter to avoid conflicting with + // the C array overload. + template < + typename T, + FMT_ENABLE_IF( + std::is_pointer::value || std::is_member_pointer::value || + std::is_function::type>::value || + (std::is_convertible::value && + !std::is_convertible::value && + !has_formatter::value))> + FMT_CONSTEXPR auto map(const T&) -> unformattable_pointer { + return {}; + } + + template ::value)> + FMT_CONSTEXPR FMT_INLINE auto map(const T (&values)[N]) -> const T (&)[N] { + return values; + } + + template ::value&& std::is_convertible::value && + !has_format_as::value && !has_formatter::value && + !has_fallback_formatter::value)> + FMT_DEPRECATED FMT_CONSTEXPR FMT_INLINE auto map(const T& val) + -> decltype(std::declval().map( + static_cast>(val))) { + return map(static_cast>(val)); + } + + template ::value && + !has_formatter::value)> + FMT_CONSTEXPR FMT_INLINE auto map(const T& val) + -> decltype(std::declval().map(format_as(T()))) { + return map(format_as(val)); + } + + template > + struct formattable + : bool_constant() || + !std::is_const>::value || + has_fallback_formatter::value> {}; + +#if (FMT_MSC_VERSION != 0 && FMT_MSC_VERSION < 1910) || \ + FMT_ICC_VERSION != 0 || defined(__NVCC__) + // Workaround a bug in MSVC and Intel (Issue 2746). + template FMT_CONSTEXPR FMT_INLINE auto do_map(T&& val) -> T& { + return val; + } +#else + template ::value)> + FMT_CONSTEXPR FMT_INLINE auto do_map(T&& val) -> T& { + return val; + } + template ::value)> + FMT_CONSTEXPR FMT_INLINE auto do_map(T&&) -> unformattable_const { + return {}; + } +#endif + + template , + FMT_ENABLE_IF(!is_string::value && !is_char::value && + !std::is_array::value && + !std::is_pointer::value && + !has_format_as::value && + (has_formatter::value || + has_fallback_formatter::value))> + FMT_CONSTEXPR FMT_INLINE auto map(T&& val) + -> decltype(this->do_map(std::forward(val))) { + return do_map(std::forward(val)); + } + + template ::value)> + FMT_CONSTEXPR FMT_INLINE auto map(const T& named_arg) + -> decltype(std::declval().map(named_arg.value)) { + return map(named_arg.value); + } + + auto map(...) -> unformattable { return {}; } +}; + +// A type constant after applying arg_mapper. +template +using mapped_type_constant = + type_constant().map(std::declval())), + typename Context::char_type>; + +enum { packed_arg_bits = 4 }; +// Maximum number of arguments with packed types. +enum { max_packed_args = 62 / packed_arg_bits }; +enum : unsigned long long { is_unpacked_bit = 1ULL << 63 }; +enum : unsigned long long { has_named_args_bit = 1ULL << 62 }; + +FMT_END_DETAIL_NAMESPACE + +// An output iterator that appends to a buffer. +// It is used to reduce symbol sizes for the common case. +class appender : public std::back_insert_iterator> { + using base = std::back_insert_iterator>; + + template + friend auto get_buffer(appender out) -> detail::buffer& { + return detail::get_container(out); + } + + public: + using std::back_insert_iterator>::back_insert_iterator; + appender(base it) noexcept : base(it) {} + FMT_UNCHECKED_ITERATOR(appender); + + auto operator++() noexcept -> appender& { return *this; } + auto operator++(int) noexcept -> appender { return *this; } +}; + +// A formatting argument. It is a trivially copyable/constructible type to +// allow storage in basic_memory_buffer. +template class basic_format_arg { + private: + detail::value value_; + detail::type type_; + + template + friend FMT_CONSTEXPR auto detail::make_arg(T&& value) + -> basic_format_arg; + + template + friend FMT_CONSTEXPR auto visit_format_arg(Visitor&& vis, + const basic_format_arg& arg) + -> decltype(vis(0)); + + friend class basic_format_args; + friend class dynamic_format_arg_store; + + using char_type = typename Context::char_type; + + template + friend struct detail::arg_data; + + basic_format_arg(const detail::named_arg_info* args, size_t size) + : value_(args, size) {} + + public: + class handle { + public: + explicit handle(detail::custom_value custom) : custom_(custom) {} + + void format(typename Context::parse_context_type& parse_ctx, + Context& ctx) const { + custom_.format(custom_.value, parse_ctx, ctx); + } + + private: + detail::custom_value custom_; + }; + + constexpr basic_format_arg() : type_(detail::type::none_type) {} + + constexpr explicit operator bool() const noexcept { + return type_ != detail::type::none_type; + } + + auto type() const -> detail::type { return type_; } + + auto is_integral() const -> bool { return detail::is_integral_type(type_); } + auto is_arithmetic() const -> bool { + return detail::is_arithmetic_type(type_); + } +}; + +/** + \rst + Visits an argument dispatching to the appropriate visit method based on + the argument type. For example, if the argument type is ``double`` then + ``vis(value)`` will be called with the value of type ``double``. + \endrst + */ +template +FMT_CONSTEXPR FMT_INLINE auto visit_format_arg( + Visitor&& vis, const basic_format_arg& arg) -> decltype(vis(0)) { + switch (arg.type_) { + case detail::type::none_type: + break; + case detail::type::int_type: + return vis(arg.value_.int_value); + case detail::type::uint_type: + return vis(arg.value_.uint_value); + case detail::type::long_long_type: + return vis(arg.value_.long_long_value); + case detail::type::ulong_long_type: + return vis(arg.value_.ulong_long_value); + case detail::type::int128_type: + return vis(detail::convert_for_visit(arg.value_.int128_value)); + case detail::type::uint128_type: + return vis(detail::convert_for_visit(arg.value_.uint128_value)); + case detail::type::bool_type: + return vis(arg.value_.bool_value); + case detail::type::char_type: + return vis(arg.value_.char_value); + case detail::type::float_type: + return vis(arg.value_.float_value); + case detail::type::double_type: + return vis(arg.value_.double_value); + case detail::type::long_double_type: + return vis(arg.value_.long_double_value); + case detail::type::cstring_type: + return vis(arg.value_.string.data); + case detail::type::string_type: + using sv = basic_string_view; + return vis(sv(arg.value_.string.data, arg.value_.string.size)); + case detail::type::pointer_type: + return vis(arg.value_.pointer); + case detail::type::custom_type: + return vis(typename basic_format_arg::handle(arg.value_.custom)); + } + return vis(monostate()); +} + +FMT_BEGIN_DETAIL_NAMESPACE + +template +auto copy_str(InputIt begin, InputIt end, appender out) -> appender { + get_container(out).append(begin, end); + return out; +} + +#if FMT_GCC_VERSION && FMT_GCC_VERSION < 500 +// A workaround for gcc 4.8 to make void_t work in a SFINAE context. +template struct void_t_impl { using type = void; }; +template +using void_t = typename detail::void_t_impl::type; +#else +template using void_t = void; +#endif + +template +struct is_output_iterator : std::false_type {}; + +template +struct is_output_iterator< + It, T, + void_t::iterator_category, + decltype(*std::declval() = std::declval())>> + : std::true_type {}; + +template +struct is_back_insert_iterator : std::false_type {}; +template +struct is_back_insert_iterator> + : std::true_type {}; + +template +struct is_contiguous_back_insert_iterator : std::false_type {}; +template +struct is_contiguous_back_insert_iterator> + : is_contiguous {}; +template <> +struct is_contiguous_back_insert_iterator : std::true_type {}; + +// A type-erased reference to an std::locale to avoid a heavy include. +class locale_ref { + private: + const void* locale_; // A type-erased pointer to std::locale. + + public: + constexpr locale_ref() : locale_(nullptr) {} + template explicit locale_ref(const Locale& loc); + + explicit operator bool() const noexcept { return locale_ != nullptr; } + + template auto get() const -> Locale; +}; + +template constexpr auto encode_types() -> unsigned long long { + return 0; +} + +template +constexpr auto encode_types() -> unsigned long long { + return static_cast(mapped_type_constant::value) | + (encode_types() << packed_arg_bits); +} + +template +FMT_CONSTEXPR FMT_INLINE auto make_value(T&& val) -> value { + const auto& arg = arg_mapper().map(std::forward(val)); + + constexpr bool formattable_char = + !std::is_same::value; + static_assert(formattable_char, "Mixing character types is disallowed."); + + constexpr bool formattable_const = + !std::is_same::value; + static_assert(formattable_const, "Cannot format a const argument."); + + // Formatting of arbitrary pointers is disallowed. If you want to output + // a pointer cast it to "void *" or "const void *". In particular, this + // forbids formatting of "[const] volatile char *" which is printed as bool + // by iostreams. + constexpr bool formattable_pointer = + !std::is_same::value; + static_assert(formattable_pointer, + "Formatting of non-void pointers is disallowed."); + + constexpr bool formattable = + !std::is_same::value; + static_assert( + formattable, + "Cannot format an argument. To make type T formattable provide a " + "formatter specialization: https://fmt.dev/latest/api.html#udt"); + return {arg}; +} + +template +FMT_CONSTEXPR auto make_arg(T&& value) -> basic_format_arg { + basic_format_arg arg; + arg.type_ = mapped_type_constant::value; + arg.value_ = make_value(value); + return arg; +} + +// The type template parameter is there to avoid an ODR violation when using +// a fallback formatter in one translation unit and an implicit conversion in +// another (not recommended). +template +FMT_CONSTEXPR FMT_INLINE auto make_arg(T&& val) -> value { + return make_value(val); +} + +template +FMT_CONSTEXPR inline auto make_arg(T&& value) -> basic_format_arg { + return make_arg(value); +} +FMT_END_DETAIL_NAMESPACE + +// Formatting context. +template class basic_format_context { + public: + /** The character type for the output. */ + using char_type = Char; + + private: + OutputIt out_; + basic_format_args args_; + detail::locale_ref loc_; + + public: + using iterator = OutputIt; + using format_arg = basic_format_arg; + using parse_context_type = basic_format_parse_context; + template using formatter_type = formatter; + + basic_format_context(basic_format_context&&) = default; + basic_format_context(const basic_format_context&) = delete; + void operator=(const basic_format_context&) = delete; + /** + Constructs a ``basic_format_context`` object. References to the arguments are + stored in the object so make sure they have appropriate lifetimes. + */ + constexpr basic_format_context( + OutputIt out, basic_format_args ctx_args, + detail::locale_ref loc = detail::locale_ref()) + : out_(out), args_(ctx_args), loc_(loc) {} + + constexpr auto arg(int id) const -> format_arg { return args_.get(id); } + FMT_CONSTEXPR auto arg(basic_string_view name) -> format_arg { + return args_.get(name); + } + FMT_CONSTEXPR auto arg_id(basic_string_view name) -> int { + return args_.get_id(name); + } + auto args() const -> const basic_format_args& { + return args_; + } + + FMT_CONSTEXPR auto error_handler() -> detail::error_handler { return {}; } + void on_error(const char* message) { error_handler().on_error(message); } + + // Returns an iterator to the beginning of the output range. + FMT_CONSTEXPR auto out() -> iterator { return out_; } + + // Advances the begin iterator to ``it``. + void advance_to(iterator it) { + if (!detail::is_back_insert_iterator()) out_ = it; + } + + FMT_CONSTEXPR auto locale() -> detail::locale_ref { return loc_; } +}; + +template +using buffer_context = + basic_format_context, Char>; +using format_context = buffer_context; + +// Workaround an alias issue: https://stackoverflow.com/q/62767544/471164. +#define FMT_BUFFER_CONTEXT(Char) \ + basic_format_context, Char> + +template +using is_formattable = bool_constant< + !std::is_base_of>().map( + std::declval()))>::value && + !detail::has_fallback_formatter::value>; + +/** + \rst + An array of references to arguments. It can be implicitly converted into + `~fmt::basic_format_args` for passing into type-erased formatting functions + such as `~fmt::vformat`. + \endrst + */ +template +class format_arg_store +#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 + // Workaround a GCC template argument substitution bug. + : public basic_format_args +#endif +{ + private: + static const size_t num_args = sizeof...(Args); + static const size_t num_named_args = detail::count_named_args(); + static const bool is_packed = num_args <= detail::max_packed_args; + + using value_type = conditional_t, + basic_format_arg>; + + detail::arg_data + data_; + + friend class basic_format_args; + + static constexpr unsigned long long desc = + (is_packed ? detail::encode_types() + : detail::is_unpacked_bit | num_args) | + (num_named_args != 0 + ? static_cast(detail::has_named_args_bit) + : 0); + + public: + template + FMT_CONSTEXPR FMT_INLINE format_arg_store(T&&... args) + : +#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 + basic_format_args(*this), +#endif + data_{detail::make_arg< + is_packed, Context, + detail::mapped_type_constant, Context>::value>( + std::forward(args))...} { + detail::init_named_args(data_.named_args(), 0, 0, args...); + } +}; + +/** + \rst + Constructs a `~fmt::format_arg_store` object that contains references to + arguments and can be implicitly converted to `~fmt::format_args`. `Context` + can be omitted in which case it defaults to `~fmt::context`. + See `~fmt::arg` for lifetime considerations. + \endrst + */ +template +constexpr auto make_format_args(Args&&... args) + -> format_arg_store...> { + return {std::forward(args)...}; +} + +/** + \rst + Returns a named argument to be used in a formatting function. + It should only be used in a call to a formatting function or + `dynamic_format_arg_store::push_back`. + + **Example**:: + + fmt::print("Elapsed time: {s:.2f} seconds", fmt::arg("s", 1.23)); + \endrst + */ +template +inline auto arg(const Char* name, const T& arg) -> detail::named_arg { + static_assert(!detail::is_named_arg(), "nested named arguments"); + return {name, arg}; +} + +/** + \rst + A view of a collection of formatting arguments. To avoid lifetime issues it + should only be used as a parameter type in type-erased functions such as + ``vformat``:: + + void vlog(string_view format_str, format_args args); // OK + format_args args = make_format_args(42); // Error: dangling reference + \endrst + */ +template class basic_format_args { + public: + using size_type = int; + using format_arg = basic_format_arg; + + private: + // A descriptor that contains information about formatting arguments. + // If the number of arguments is less or equal to max_packed_args then + // argument types are passed in the descriptor. This reduces binary code size + // per formatting function call. + unsigned long long desc_; + union { + // If is_packed() returns true then argument values are stored in values_; + // otherwise they are stored in args_. This is done to improve cache + // locality and reduce compiled code size since storing larger objects + // may require more code (at least on x86-64) even if the same amount of + // data is actually copied to stack. It saves ~10% on the bloat test. + const detail::value* values_; + const format_arg* args_; + }; + + constexpr auto is_packed() const -> bool { + return (desc_ & detail::is_unpacked_bit) == 0; + } + auto has_named_args() const -> bool { + return (desc_ & detail::has_named_args_bit) != 0; + } + + FMT_CONSTEXPR auto type(int index) const -> detail::type { + int shift = index * detail::packed_arg_bits; + unsigned int mask = (1 << detail::packed_arg_bits) - 1; + return static_cast((desc_ >> shift) & mask); + } + + constexpr FMT_INLINE basic_format_args(unsigned long long desc, + const detail::value* values) + : desc_(desc), values_(values) {} + constexpr basic_format_args(unsigned long long desc, const format_arg* args) + : desc_(desc), args_(args) {} + + public: + constexpr basic_format_args() : desc_(0), args_(nullptr) {} + + /** + \rst + Constructs a `basic_format_args` object from `~fmt::format_arg_store`. + \endrst + */ + template + constexpr FMT_INLINE basic_format_args( + const format_arg_store& store) + : basic_format_args(format_arg_store::desc, + store.data_.args()) {} + + /** + \rst + Constructs a `basic_format_args` object from + `~fmt::dynamic_format_arg_store`. + \endrst + */ + constexpr FMT_INLINE basic_format_args( + const dynamic_format_arg_store& store) + : basic_format_args(store.get_types(), store.data()) {} + + /** + \rst + Constructs a `basic_format_args` object from a dynamic set of arguments. + \endrst + */ + constexpr basic_format_args(const format_arg* args, int count) + : basic_format_args(detail::is_unpacked_bit | detail::to_unsigned(count), + args) {} + + /** Returns the argument with the specified id. */ + FMT_CONSTEXPR auto get(int id) const -> format_arg { + format_arg arg; + if (!is_packed()) { + if (id < max_size()) arg = args_[id]; + return arg; + } + if (id >= detail::max_packed_args) return arg; + arg.type_ = type(id); + if (arg.type_ == detail::type::none_type) return arg; + arg.value_ = values_[id]; + return arg; + } + + template + auto get(basic_string_view name) const -> format_arg { + int id = get_id(name); + return id >= 0 ? get(id) : format_arg(); + } + + template + auto get_id(basic_string_view name) const -> int { + if (!has_named_args()) return -1; + const auto& named_args = + (is_packed() ? values_[-1] : args_[-1].value_).named_args; + for (size_t i = 0; i < named_args.size; ++i) { + if (named_args.data[i].name == name) return named_args.data[i].id; + } + return -1; + } + + auto max_size() const -> int { + unsigned long long max_packed = detail::max_packed_args; + return static_cast(is_packed() ? max_packed + : desc_ & ~detail::is_unpacked_bit); + } +}; + +/** An alias to ``basic_format_args``. */ +// A separate type would result in shorter symbols but break ABI compatibility +// between clang and gcc on ARM (#1919). +using format_args = basic_format_args; + +// We cannot use enum classes as bit fields because of a gcc bug, so we put them +// in namespaces instead (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61414). +// Additionally, if an underlying type is specified, older gcc incorrectly warns +// that the type is too small. Both bugs are fixed in gcc 9.3. +#if FMT_GCC_VERSION && FMT_GCC_VERSION < 903 +# define FMT_ENUM_UNDERLYING_TYPE(type) +#else +# define FMT_ENUM_UNDERLYING_TYPE(type) : type +#endif +namespace align { +enum type FMT_ENUM_UNDERLYING_TYPE(unsigned char){none, left, right, center, + numeric}; +} +using align_t = align::type; +namespace sign { +enum type FMT_ENUM_UNDERLYING_TYPE(unsigned char){none, minus, plus, space}; +} +using sign_t = sign::type; + +FMT_BEGIN_DETAIL_NAMESPACE + +// Workaround an array initialization issue in gcc 4.8. +template struct fill_t { + private: + enum { max_size = 4 }; + Char data_[max_size] = {Char(' '), Char(0), Char(0), Char(0)}; + unsigned char size_ = 1; + + public: + FMT_CONSTEXPR void operator=(basic_string_view s) { + auto size = s.size(); + if (size > max_size) return throw_format_error("invalid fill"); + for (size_t i = 0; i < size; ++i) data_[i] = s[i]; + size_ = static_cast(size); + } + + constexpr auto size() const -> size_t { return size_; } + constexpr auto data() const -> const Char* { return data_; } + + FMT_CONSTEXPR auto operator[](size_t index) -> Char& { return data_[index]; } + FMT_CONSTEXPR auto operator[](size_t index) const -> const Char& { + return data_[index]; + } +}; +FMT_END_DETAIL_NAMESPACE + +enum class presentation_type : unsigned char { + none, + // Integer types should go first, + dec, // 'd' + oct, // 'o' + hex_lower, // 'x' + hex_upper, // 'X' + bin_lower, // 'b' + bin_upper, // 'B' + hexfloat_lower, // 'a' + hexfloat_upper, // 'A' + exp_lower, // 'e' + exp_upper, // 'E' + fixed_lower, // 'f' + fixed_upper, // 'F' + general_lower, // 'g' + general_upper, // 'G' + chr, // 'c' + string, // 's' + pointer, // 'p' + debug // '?' +}; + +// Format specifiers for built-in and string types. +template struct basic_format_specs { + int width; + int precision; + presentation_type type; + align_t align : 4; + sign_t sign : 3; + bool alt : 1; // Alternate form ('#'). + bool localized : 1; + detail::fill_t fill; + + constexpr basic_format_specs() + : width(0), + precision(-1), + type(presentation_type::none), + align(align::none), + sign(sign::none), + alt(false), + localized(false) {} +}; + +using format_specs = basic_format_specs; + +FMT_BEGIN_DETAIL_NAMESPACE + +enum class arg_id_kind { none, index, name }; + +// An argument reference. +template struct arg_ref { + FMT_CONSTEXPR arg_ref() : kind(arg_id_kind::none), val() {} + + FMT_CONSTEXPR explicit arg_ref(int index) + : kind(arg_id_kind::index), val(index) {} + FMT_CONSTEXPR explicit arg_ref(basic_string_view name) + : kind(arg_id_kind::name), val(name) {} + + FMT_CONSTEXPR auto operator=(int idx) -> arg_ref& { + kind = arg_id_kind::index; + val.index = idx; + return *this; + } + + arg_id_kind kind; + union value { + FMT_CONSTEXPR value(int id = 0) : index{id} {} + FMT_CONSTEXPR value(basic_string_view n) : name(n) {} + + int index; + basic_string_view name; + } val; +}; + +// Format specifiers with width and precision resolved at formatting rather +// than parsing time to allow re-using the same parsed specifiers with +// different sets of arguments (precompilation of format strings). +template +struct dynamic_format_specs : basic_format_specs { + arg_ref width_ref; + arg_ref precision_ref; +}; + +struct auto_id {}; + +// A format specifier handler that sets fields in basic_format_specs. +template class specs_setter { + protected: + basic_format_specs& specs_; + + public: + explicit FMT_CONSTEXPR specs_setter(basic_format_specs& specs) + : specs_(specs) {} + + FMT_CONSTEXPR specs_setter(const specs_setter& other) + : specs_(other.specs_) {} + + FMT_CONSTEXPR void on_align(align_t align) { specs_.align = align; } + FMT_CONSTEXPR void on_fill(basic_string_view fill) { + specs_.fill = fill; + } + FMT_CONSTEXPR void on_sign(sign_t s) { specs_.sign = s; } + FMT_CONSTEXPR void on_hash() { specs_.alt = true; } + FMT_CONSTEXPR void on_localized() { specs_.localized = true; } + + FMT_CONSTEXPR void on_zero() { + if (specs_.align == align::none) specs_.align = align::numeric; + specs_.fill[0] = Char('0'); + } + + FMT_CONSTEXPR void on_width(int width) { specs_.width = width; } + FMT_CONSTEXPR void on_precision(int precision) { + specs_.precision = precision; + } + FMT_CONSTEXPR void end_precision() {} + + FMT_CONSTEXPR void on_type(presentation_type type) { specs_.type = type; } +}; + +// Format spec handler that saves references to arguments representing dynamic +// width and precision to be resolved at formatting time. +template +class dynamic_specs_handler + : public specs_setter { + public: + using char_type = typename ParseContext::char_type; + + FMT_CONSTEXPR dynamic_specs_handler(dynamic_format_specs& specs, + ParseContext& ctx) + : specs_setter(specs), specs_(specs), context_(ctx) {} + + FMT_CONSTEXPR dynamic_specs_handler(const dynamic_specs_handler& other) + : specs_setter(other), + specs_(other.specs_), + context_(other.context_) {} + + template FMT_CONSTEXPR void on_dynamic_width(Id arg_id) { + specs_.width_ref = make_arg_ref(arg_id); + } + + template FMT_CONSTEXPR void on_dynamic_precision(Id arg_id) { + specs_.precision_ref = make_arg_ref(arg_id); + } + + FMT_CONSTEXPR void on_error(const char* message) { + context_.on_error(message); + } + + private: + dynamic_format_specs& specs_; + ParseContext& context_; + + using arg_ref_type = arg_ref; + + FMT_CONSTEXPR auto make_arg_ref(int arg_id) -> arg_ref_type { + context_.check_arg_id(arg_id); + return arg_ref_type(arg_id); + } + + FMT_CONSTEXPR auto make_arg_ref(auto_id) -> arg_ref_type { + return arg_ref_type(context_.next_arg_id()); + } + + FMT_CONSTEXPR auto make_arg_ref(basic_string_view arg_id) + -> arg_ref_type { + context_.check_arg_id(arg_id); + basic_string_view format_str( + context_.begin(), to_unsigned(context_.end() - context_.begin())); + return arg_ref_type(arg_id); + } +}; + +template constexpr bool is_ascii_letter(Char c) { + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); +} + +// Converts a character to ASCII. Returns a number > 127 on conversion failure. +template ::value)> +constexpr auto to_ascii(Char c) -> Char { + return c; +} +template ::value)> +constexpr auto to_ascii(Char c) -> underlying_t { + return c; +} + +template +FMT_CONSTEXPR auto code_point_length(const Char* begin) -> int { + if (const_check(sizeof(Char) != 1)) return 1; + auto lengths = + "\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\0\0\0\0\0\0\0\2\2\2\2\3\3\4"; + int len = lengths[static_cast(*begin) >> 3]; + + // Compute the pointer to the next character early so that the next + // iteration can start working on the next character. Neither Clang + // nor GCC figure out this reordering on their own. + return len + !len; +} + +// Return the result via the out param to workaround gcc bug 77539. +template +FMT_CONSTEXPR auto find(Ptr first, Ptr last, T value, Ptr& out) -> bool { + for (out = first; out != last; ++out) { + if (*out == value) return true; + } + return false; +} + +template <> +inline auto find(const char* first, const char* last, char value, + const char*& out) -> bool { + out = static_cast( + std::memchr(first, value, to_unsigned(last - first))); + return out != nullptr; +} + +// Parses the range [begin, end) as an unsigned integer. This function assumes +// that the range is non-empty and the first character is a digit. +template +FMT_CONSTEXPR auto parse_nonnegative_int(const Char*& begin, const Char* end, + int error_value) noexcept -> int { + FMT_ASSERT(begin != end && '0' <= *begin && *begin <= '9', ""); + unsigned value = 0, prev = 0; + auto p = begin; + do { + prev = value; + value = value * 10 + unsigned(*p - '0'); + ++p; + } while (p != end && '0' <= *p && *p <= '9'); + auto num_digits = p - begin; + begin = p; + if (num_digits <= std::numeric_limits::digits10) + return static_cast(value); + // Check for overflow. + const unsigned max = to_unsigned((std::numeric_limits::max)()); + return num_digits == std::numeric_limits::digits10 + 1 && + prev * 10ull + unsigned(p[-1] - '0') <= max + ? static_cast(value) + : error_value; +} + +// Parses fill and alignment. +template +FMT_CONSTEXPR auto parse_align(const Char* begin, const Char* end, + Handler&& handler) -> const Char* { + FMT_ASSERT(begin != end, ""); + auto align = align::none; + auto p = begin + code_point_length(begin); + if (end - p <= 0) p = begin; + for (;;) { + switch (to_ascii(*p)) { + case '<': + align = align::left; + break; + case '>': + align = align::right; + break; + case '^': + align = align::center; + break; + default: + break; + } + if (align != align::none) { + if (p != begin) { + auto c = *begin; + if (c == '{') + return handler.on_error("invalid fill character '{'"), begin; + handler.on_fill(basic_string_view(begin, to_unsigned(p - begin))); + begin = p + 1; + } else + ++begin; + handler.on_align(align); + break; + } else if (p == begin) { + break; + } + p = begin; + } + return begin; +} + +template FMT_CONSTEXPR bool is_name_start(Char c) { + return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || '_' == c; +} + +template +FMT_CONSTEXPR auto do_parse_arg_id(const Char* begin, const Char* end, + IDHandler&& handler) -> const Char* { + FMT_ASSERT(begin != end, ""); + Char c = *begin; + if (c >= '0' && c <= '9') { + int index = 0; + if (c != '0') + index = + parse_nonnegative_int(begin, end, (std::numeric_limits::max)()); + else + ++begin; + if (begin == end || (*begin != '}' && *begin != ':')) + handler.on_error("invalid format string"); + else + handler(index); + return begin; + } + if (!is_name_start(c)) { + handler.on_error("invalid format string"); + return begin; + } + auto it = begin; + do { + ++it; + } while (it != end && (is_name_start(c = *it) || ('0' <= c && c <= '9'))); + handler(basic_string_view(begin, to_unsigned(it - begin))); + return it; +} + +template +FMT_CONSTEXPR FMT_INLINE auto parse_arg_id(const Char* begin, const Char* end, + IDHandler&& handler) -> const Char* { + Char c = *begin; + if (c != '}' && c != ':') return do_parse_arg_id(begin, end, handler); + handler(); + return begin; +} + +template +FMT_CONSTEXPR auto parse_width(const Char* begin, const Char* end, + Handler&& handler) -> const Char* { + using detail::auto_id; + struct width_adapter { + Handler& handler; + + FMT_CONSTEXPR void operator()() { handler.on_dynamic_width(auto_id()); } + FMT_CONSTEXPR void operator()(int id) { handler.on_dynamic_width(id); } + FMT_CONSTEXPR void operator()(basic_string_view id) { + handler.on_dynamic_width(id); + } + FMT_CONSTEXPR void on_error(const char* message) { + if (message) handler.on_error(message); + } + }; + + FMT_ASSERT(begin != end, ""); + if ('0' <= *begin && *begin <= '9') { + int width = parse_nonnegative_int(begin, end, -1); + if (width != -1) + handler.on_width(width); + else + handler.on_error("number is too big"); + } else if (*begin == '{') { + ++begin; + if (begin != end) begin = parse_arg_id(begin, end, width_adapter{handler}); + if (begin == end || *begin != '}') + return handler.on_error("invalid format string"), begin; + ++begin; + } + return begin; +} + +template +FMT_CONSTEXPR auto parse_precision(const Char* begin, const Char* end, + Handler&& handler) -> const Char* { + using detail::auto_id; + struct precision_adapter { + Handler& handler; + + FMT_CONSTEXPR void operator()() { handler.on_dynamic_precision(auto_id()); } + FMT_CONSTEXPR void operator()(int id) { handler.on_dynamic_precision(id); } + FMT_CONSTEXPR void operator()(basic_string_view id) { + handler.on_dynamic_precision(id); + } + FMT_CONSTEXPR void on_error(const char* message) { + if (message) handler.on_error(message); + } + }; + + ++begin; + auto c = begin != end ? *begin : Char(); + if ('0' <= c && c <= '9') { + auto precision = parse_nonnegative_int(begin, end, -1); + if (precision != -1) + handler.on_precision(precision); + else + handler.on_error("number is too big"); + } else if (c == '{') { + ++begin; + if (begin != end) + begin = parse_arg_id(begin, end, precision_adapter{handler}); + if (begin == end || *begin++ != '}') + return handler.on_error("invalid format string"), begin; + } else { + return handler.on_error("missing precision specifier"), begin; + } + handler.end_precision(); + return begin; +} + +template +FMT_CONSTEXPR auto parse_presentation_type(Char type) -> presentation_type { + switch (to_ascii(type)) { + case 'd': + return presentation_type::dec; + case 'o': + return presentation_type::oct; + case 'x': + return presentation_type::hex_lower; + case 'X': + return presentation_type::hex_upper; + case 'b': + return presentation_type::bin_lower; + case 'B': + return presentation_type::bin_upper; + case 'a': + return presentation_type::hexfloat_lower; + case 'A': + return presentation_type::hexfloat_upper; + case 'e': + return presentation_type::exp_lower; + case 'E': + return presentation_type::exp_upper; + case 'f': + return presentation_type::fixed_lower; + case 'F': + return presentation_type::fixed_upper; + case 'g': + return presentation_type::general_lower; + case 'G': + return presentation_type::general_upper; + case 'c': + return presentation_type::chr; + case 's': + return presentation_type::string; + case 'p': + return presentation_type::pointer; + case '?': + return presentation_type::debug; + default: + return presentation_type::none; + } +} + +// Parses standard format specifiers and sends notifications about parsed +// components to handler. +template +FMT_CONSTEXPR FMT_INLINE auto parse_format_specs(const Char* begin, + const Char* end, + SpecHandler&& handler) + -> const Char* { + if (1 < end - begin && begin[1] == '}' && is_ascii_letter(*begin) && + *begin != 'L') { + presentation_type type = parse_presentation_type(*begin++); + if (type == presentation_type::none) + handler.on_error("invalid type specifier"); + handler.on_type(type); + return begin; + } + + if (begin == end) return begin; + + begin = parse_align(begin, end, handler); + if (begin == end) return begin; + + // Parse sign. + switch (to_ascii(*begin)) { + case '+': + handler.on_sign(sign::plus); + ++begin; + break; + case '-': + handler.on_sign(sign::minus); + ++begin; + break; + case ' ': + handler.on_sign(sign::space); + ++begin; + break; + default: + break; + } + if (begin == end) return begin; + + if (*begin == '#') { + handler.on_hash(); + if (++begin == end) return begin; + } + + // Parse zero flag. + if (*begin == '0') { + handler.on_zero(); + if (++begin == end) return begin; + } + + begin = parse_width(begin, end, handler); + if (begin == end) return begin; + + // Parse precision. + if (*begin == '.') { + begin = parse_precision(begin, end, handler); + if (begin == end) return begin; + } + + if (*begin == 'L') { + handler.on_localized(); + ++begin; + } + + // Parse type. + if (begin != end && *begin != '}') { + presentation_type type = parse_presentation_type(*begin++); + if (type == presentation_type::none) + handler.on_error("invalid type specifier"); + handler.on_type(type); + } + return begin; +} + +template +FMT_CONSTEXPR auto parse_replacement_field(const Char* begin, const Char* end, + Handler&& handler) -> const Char* { + struct id_adapter { + Handler& handler; + int arg_id; + + FMT_CONSTEXPR void operator()() { arg_id = handler.on_arg_id(); } + FMT_CONSTEXPR void operator()(int id) { arg_id = handler.on_arg_id(id); } + FMT_CONSTEXPR void operator()(basic_string_view id) { + arg_id = handler.on_arg_id(id); + } + FMT_CONSTEXPR void on_error(const char* message) { + if (message) handler.on_error(message); + } + }; + + ++begin; + if (begin == end) return handler.on_error("invalid format string"), end; + if (*begin == '}') { + handler.on_replacement_field(handler.on_arg_id(), begin); + } else if (*begin == '{') { + handler.on_text(begin, begin + 1); + } else { + auto adapter = id_adapter{handler, 0}; + begin = parse_arg_id(begin, end, adapter); + Char c = begin != end ? *begin : Char(); + if (c == '}') { + handler.on_replacement_field(adapter.arg_id, begin); + } else if (c == ':') { + begin = handler.on_format_specs(adapter.arg_id, begin + 1, end); + if (begin == end || *begin != '}') + return handler.on_error("unknown format specifier"), end; + } else { + return handler.on_error("missing '}' in format string"), end; + } + } + return begin + 1; +} + +template +FMT_CONSTEXPR FMT_INLINE void parse_format_string( + basic_string_view format_str, Handler&& handler) { + // Workaround a name-lookup bug in MSVC's modules implementation. + using detail::find; + + auto begin = format_str.data(); + auto end = begin + format_str.size(); + if (end - begin < 32) { + // Use a simple loop instead of memchr for small strings. + const Char* p = begin; + while (p != end) { + auto c = *p++; + if (c == '{') { + handler.on_text(begin, p - 1); + begin = p = parse_replacement_field(p - 1, end, handler); + } else if (c == '}') { + if (p == end || *p != '}') + return handler.on_error("unmatched '}' in format string"); + handler.on_text(begin, p); + begin = ++p; + } + } + handler.on_text(begin, end); + return; + } + struct writer { + FMT_CONSTEXPR void operator()(const Char* from, const Char* to) { + if (from == to) return; + for (;;) { + const Char* p = nullptr; + if (!find(from, to, Char('}'), p)) + return handler_.on_text(from, to); + ++p; + if (p == to || *p != '}') + return handler_.on_error("unmatched '}' in format string"); + handler_.on_text(from, p); + from = p + 1; + } + } + Handler& handler_; + } write = {handler}; + while (begin != end) { + // Doing two passes with memchr (one for '{' and another for '}') is up to + // 2.5x faster than the naive one-pass implementation on big format strings. + const Char* p = begin; + if (*begin != '{' && !find(begin + 1, end, Char('{'), p)) + return write(begin, end); + write(begin, p); + begin = parse_replacement_field(p, end, handler); + } +} + +template ::value> struct strip_named_arg { + using type = T; +}; +template struct strip_named_arg { + using type = remove_cvref_t; +}; + +template +FMT_CONSTEXPR auto parse_format_specs(ParseContext& ctx) + -> decltype(ctx.begin()) { + using char_type = typename ParseContext::char_type; + using context = buffer_context; + using stripped_type = typename strip_named_arg::type; + using mapped_type = conditional_t< + mapped_type_constant::value != type::custom_type, + decltype(arg_mapper().map(std::declval())), + stripped_type>; + auto f = conditional_t::value, + formatter, + fallback_formatter>(); + return f.parse(ctx); +} + +template +FMT_CONSTEXPR void check_int_type_spec(presentation_type type, + ErrorHandler&& eh) { + if (type > presentation_type::bin_upper && type != presentation_type::chr) + eh.on_error("invalid type specifier"); +} + +// Checks char specs and returns true if the type spec is char (and not int). +template +FMT_CONSTEXPR auto check_char_specs(const basic_format_specs& specs, + ErrorHandler&& eh = {}) -> bool { + if (specs.type != presentation_type::none && + specs.type != presentation_type::chr && + specs.type != presentation_type::debug) { + check_int_type_spec(specs.type, eh); + return false; + } + if (specs.align == align::numeric || specs.sign != sign::none || specs.alt) + eh.on_error("invalid format specifier for char"); + return true; +} + +// A floating-point presentation format. +enum class float_format : unsigned char { + general, // General: exponent notation or fixed point based on magnitude. + exp, // Exponent notation with the default precision of 6, e.g. 1.2e-3. + fixed, // Fixed point with the default precision of 6, e.g. 0.0012. + hex +}; + +struct float_specs { + int precision; + float_format format : 8; + sign_t sign : 8; + bool upper : 1; + bool locale : 1; + bool binary32 : 1; + bool showpoint : 1; +}; + +template +FMT_CONSTEXPR auto parse_float_type_spec(const basic_format_specs& specs, + ErrorHandler&& eh = {}) + -> float_specs { + auto result = float_specs(); + result.showpoint = specs.alt; + result.locale = specs.localized; + switch (specs.type) { + case presentation_type::none: + result.format = float_format::general; + break; + case presentation_type::general_upper: + result.upper = true; + FMT_FALLTHROUGH; + case presentation_type::general_lower: + result.format = float_format::general; + break; + case presentation_type::exp_upper: + result.upper = true; + FMT_FALLTHROUGH; + case presentation_type::exp_lower: + result.format = float_format::exp; + result.showpoint |= specs.precision != 0; + break; + case presentation_type::fixed_upper: + result.upper = true; + FMT_FALLTHROUGH; + case presentation_type::fixed_lower: + result.format = float_format::fixed; + result.showpoint |= specs.precision != 0; + break; + case presentation_type::hexfloat_upper: + result.upper = true; + FMT_FALLTHROUGH; + case presentation_type::hexfloat_lower: + result.format = float_format::hex; + break; + default: + eh.on_error("invalid type specifier"); + break; + } + return result; +} + +template +FMT_CONSTEXPR auto check_cstring_type_spec(presentation_type type, + ErrorHandler&& eh = {}) -> bool { + if (type == presentation_type::none || type == presentation_type::string) + return true; + if (type != presentation_type::pointer) eh.on_error("invalid type specifier"); + return false; +} + +template +FMT_CONSTEXPR void check_string_type_spec(presentation_type type, + ErrorHandler&& eh = {}) { + if (type != presentation_type::none && type != presentation_type::string && + type != presentation_type::debug) + eh.on_error("invalid type specifier"); +} + +template +FMT_CONSTEXPR void check_pointer_type_spec(presentation_type type, + ErrorHandler&& eh) { + if (type != presentation_type::none && type != presentation_type::pointer) + eh.on_error("invalid type specifier"); +} + +// A parse_format_specs handler that checks if specifiers are consistent with +// the argument type. +template class specs_checker : public Handler { + private: + detail::type arg_type_; + + FMT_CONSTEXPR void require_numeric_argument() { + if (!is_arithmetic_type(arg_type_)) + this->on_error("format specifier requires numeric argument"); + } + + public: + FMT_CONSTEXPR specs_checker(const Handler& handler, detail::type arg_type) + : Handler(handler), arg_type_(arg_type) {} + + FMT_CONSTEXPR void on_align(align_t align) { + if (align == align::numeric) require_numeric_argument(); + Handler::on_align(align); + } + + FMT_CONSTEXPR void on_sign(sign_t s) { + require_numeric_argument(); + if (is_integral_type(arg_type_) && arg_type_ != type::int_type && + arg_type_ != type::long_long_type && arg_type_ != type::int128_type && + arg_type_ != type::char_type) { + this->on_error("format specifier requires signed argument"); + } + Handler::on_sign(s); + } + + FMT_CONSTEXPR void on_hash() { + require_numeric_argument(); + Handler::on_hash(); + } + + FMT_CONSTEXPR void on_localized() { + require_numeric_argument(); + Handler::on_localized(); + } + + FMT_CONSTEXPR void on_zero() { + require_numeric_argument(); + Handler::on_zero(); + } + + FMT_CONSTEXPR void end_precision() { + if (is_integral_type(arg_type_) || arg_type_ == type::pointer_type) + this->on_error("precision not allowed for this argument type"); + } +}; + +constexpr int invalid_arg_index = -1; + +#if FMT_USE_NONTYPE_TEMPLATE_ARGS +template +constexpr auto get_arg_index_by_name(basic_string_view name) -> int { + if constexpr (detail::is_statically_named_arg()) { + if (name == T::name) return N; + } + if constexpr (sizeof...(Args) > 0) + return get_arg_index_by_name(name); + (void)name; // Workaround an MSVC bug about "unused" parameter. + return invalid_arg_index; +} +#endif + +template +FMT_CONSTEXPR auto get_arg_index_by_name(basic_string_view name) -> int { +#if FMT_USE_NONTYPE_TEMPLATE_ARGS + if constexpr (sizeof...(Args) > 0) + return get_arg_index_by_name<0, Args...>(name); +#endif + (void)name; + return invalid_arg_index; +} + +template +class format_string_checker { + private: + // In the future basic_format_parse_context will replace compile_parse_context + // here and will use is_constant_evaluated and downcasting to access the data + // needed for compile-time checks: https://godbolt.org/z/GvWzcTjh1. + using parse_context_type = compile_parse_context; + static constexpr int num_args = sizeof...(Args); + + // Format specifier parsing function. + using parse_func = const Char* (*)(parse_context_type&); + + parse_context_type context_; + parse_func parse_funcs_[num_args > 0 ? static_cast(num_args) : 1]; + type types_[num_args > 0 ? static_cast(num_args) : 1]; + + public: + explicit FMT_CONSTEXPR format_string_checker( + basic_string_view format_str, ErrorHandler eh) + : context_(format_str, num_args, types_, eh), + parse_funcs_{&parse_format_specs...}, + types_{type_constant::value...} {} + + FMT_CONSTEXPR void on_text(const Char*, const Char*) {} + + FMT_CONSTEXPR auto on_arg_id() -> int { return context_.next_arg_id(); } + FMT_CONSTEXPR auto on_arg_id(int id) -> int { + return context_.check_arg_id(id), id; + } + FMT_CONSTEXPR auto on_arg_id(basic_string_view id) -> int { +#if FMT_USE_NONTYPE_TEMPLATE_ARGS + auto index = get_arg_index_by_name(id); + if (index == invalid_arg_index) on_error("named argument is not found"); + return context_.check_arg_id(index), index; +#else + (void)id; + on_error("compile-time checks for named arguments require C++20 support"); + return 0; +#endif + } + + FMT_CONSTEXPR void on_replacement_field(int, const Char*) {} + + FMT_CONSTEXPR auto on_format_specs(int id, const Char* begin, const Char*) + -> const Char* { + context_.advance_to(context_.begin() + (begin - &*context_.begin())); + // id >= 0 check is a workaround for gcc 10 bug (#2065). + return id >= 0 && id < num_args ? parse_funcs_[id](context_) : begin; + } + + FMT_CONSTEXPR void on_error(const char* message) { + context_.on_error(message); + } +}; + +// Reports a compile-time error if S is not a valid format string. +template ::value)> +FMT_INLINE void check_format_string(const S&) { +#ifdef FMT_ENFORCE_COMPILE_STRING + static_assert(is_compile_string::value, + "FMT_ENFORCE_COMPILE_STRING requires all format strings to use " + "FMT_STRING."); +#endif +} +template ::value)> +void check_format_string(S format_str) { + FMT_CONSTEXPR auto s = basic_string_view(format_str); + using checker = format_string_checker...>; + FMT_CONSTEXPR bool invalid_format = + (parse_format_string(s, checker(s, {})), true); + ignore_unused(invalid_format); +} + +template +void vformat_to( + buffer& buf, basic_string_view fmt, + basic_format_args)> args, + locale_ref loc = {}); + +FMT_API void vprint_mojibake(std::FILE*, string_view, format_args); +#ifndef _WIN32 +inline void vprint_mojibake(std::FILE*, string_view, format_args) {} +#endif +FMT_END_DETAIL_NAMESPACE + +// A formatter specialization for the core types corresponding to detail::type +// constants. +template +struct formatter::value != + detail::type::custom_type>> { + private: + detail::dynamic_format_specs specs_; + + public: + // Parses format specifiers stopping either at the end of the range or at the + // terminating '}'. + template + FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { + auto begin = ctx.begin(), end = ctx.end(); + if (begin == end) return begin; + using handler_type = detail::dynamic_specs_handler; + auto type = detail::type_constant::value; + auto checker = + detail::specs_checker(handler_type(specs_, ctx), type); + auto it = detail::parse_format_specs(begin, end, checker); + auto eh = ctx.error_handler(); + switch (type) { + case detail::type::none_type: + FMT_ASSERT(false, "invalid argument type"); + break; + case detail::type::bool_type: + if (specs_.type == presentation_type::none || + specs_.type == presentation_type::string) { + break; + } + FMT_FALLTHROUGH; + case detail::type::int_type: + case detail::type::uint_type: + case detail::type::long_long_type: + case detail::type::ulong_long_type: + case detail::type::int128_type: + case detail::type::uint128_type: + detail::check_int_type_spec(specs_.type, eh); + break; + case detail::type::char_type: + detail::check_char_specs(specs_, eh); + break; + case detail::type::float_type: + if (detail::const_check(FMT_USE_FLOAT)) + detail::parse_float_type_spec(specs_, eh); + else + FMT_ASSERT(false, "float support disabled"); + break; + case detail::type::double_type: + if (detail::const_check(FMT_USE_DOUBLE)) + detail::parse_float_type_spec(specs_, eh); + else + FMT_ASSERT(false, "double support disabled"); + break; + case detail::type::long_double_type: + if (detail::const_check(FMT_USE_LONG_DOUBLE)) + detail::parse_float_type_spec(specs_, eh); + else + FMT_ASSERT(false, "long double support disabled"); + break; + case detail::type::cstring_type: + detail::check_cstring_type_spec(specs_.type, eh); + break; + case detail::type::string_type: + detail::check_string_type_spec(specs_.type, eh); + break; + case detail::type::pointer_type: + detail::check_pointer_type_spec(specs_.type, eh); + break; + case detail::type::custom_type: + // Custom format specifiers are checked in parse functions of + // formatter specializations. + break; + } + return it; + } + + template + FMT_CONSTEXPR auto format(const T& val, FormatContext& ctx) const + -> decltype(ctx.out()); +}; + +#define FMT_FORMAT_AS(Type, Base) \ + template \ + struct formatter : formatter { \ + template \ + auto format(Type const& val, FormatContext& ctx) const \ + -> decltype(ctx.out()) { \ + return formatter::format(static_cast(val), ctx); \ + } \ + } + +FMT_FORMAT_AS(signed char, int); +FMT_FORMAT_AS(unsigned char, unsigned); +FMT_FORMAT_AS(short, int); +FMT_FORMAT_AS(unsigned short, unsigned); +FMT_FORMAT_AS(long, long long); +FMT_FORMAT_AS(unsigned long, unsigned long long); +FMT_FORMAT_AS(Char*, const Char*); +FMT_FORMAT_AS(std::basic_string, basic_string_view); +FMT_FORMAT_AS(std::nullptr_t, const void*); +FMT_FORMAT_AS(detail::std_string_view, basic_string_view); + +template struct basic_runtime { basic_string_view str; }; + +/** A compile-time format string. */ +template class basic_format_string { + private: + basic_string_view str_; + + public: + template >::value)> + FMT_CONSTEVAL FMT_INLINE basic_format_string(const S& s) : str_(s) { + static_assert( + detail::count< + (std::is_base_of>::value && + std::is_reference::value)...>() == 0, + "passing views as lvalues is disallowed"); +#ifdef FMT_HAS_CONSTEVAL + if constexpr (detail::count_named_args() == + detail::count_statically_named_args()) { + using checker = detail::format_string_checker...>; + detail::parse_format_string(str_, checker(s, {})); + } +#else + detail::check_format_string(s); +#endif + } + basic_format_string(basic_runtime r) : str_(r.str) {} + + FMT_INLINE operator basic_string_view() const { return str_; } +}; + +#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 +// Workaround broken conversion on older gcc. +template using format_string = string_view; +inline auto runtime(string_view s) -> basic_string_view { return s; } +#else +template +using format_string = basic_format_string...>; +/** + \rst + Creates a runtime format string. + + **Example**:: + + // Check format string at runtime instead of compile-time. + fmt::print(fmt::runtime("{:d}"), "I am not a number"); + \endrst + */ +inline auto runtime(string_view s) -> basic_runtime { return {{s}}; } +#endif + +FMT_API auto vformat(string_view fmt, format_args args) -> std::string; + +/** + \rst + Formats ``args`` according to specifications in ``fmt`` and returns the result + as a string. + + **Example**:: + + #include + std::string message = fmt::format("The answer is {}.", 42); + \endrst +*/ +template +FMT_NODISCARD FMT_INLINE auto format(format_string fmt, T&&... args) + -> std::string { + return vformat(fmt, fmt::make_format_args(args...)); +} + +/** Formats a string and writes the output to ``out``. */ +template ::value)> +auto vformat_to(OutputIt out, string_view fmt, format_args args) -> OutputIt { + using detail::get_buffer; + auto&& buf = get_buffer(out); + detail::vformat_to(buf, fmt, args, {}); + return detail::get_iterator(buf); +} + +/** + \rst + Formats ``args`` according to specifications in ``fmt``, writes the result to + the output iterator ``out`` and returns the iterator past the end of the output + range. `format_to` does not append a terminating null character. + + **Example**:: + + auto out = std::vector(); + fmt::format_to(std::back_inserter(out), "{}", 42); + \endrst + */ +template ::value)> +FMT_INLINE auto format_to(OutputIt out, format_string fmt, T&&... args) + -> OutputIt { + return vformat_to(out, fmt, fmt::make_format_args(args...)); +} + +template struct format_to_n_result { + /** Iterator past the end of the output range. */ + OutputIt out; + /** Total (not truncated) output size. */ + size_t size; +}; + +template ::value)> +auto vformat_to_n(OutputIt out, size_t n, string_view fmt, format_args args) + -> format_to_n_result { + using traits = detail::fixed_buffer_traits; + auto buf = detail::iterator_buffer(out, n); + detail::vformat_to(buf, fmt, args, {}); + return {buf.out(), buf.count()}; +} + +/** + \rst + Formats ``args`` according to specifications in ``fmt``, writes up to ``n`` + characters of the result to the output iterator ``out`` and returns the total + (not truncated) output size and the iterator past the end of the output range. + `format_to_n` does not append a terminating null character. + \endrst + */ +template ::value)> +FMT_INLINE auto format_to_n(OutputIt out, size_t n, format_string fmt, + T&&... args) -> format_to_n_result { + return vformat_to_n(out, n, fmt, fmt::make_format_args(args...)); +} + +/** Returns the number of chars in the output of ``format(fmt, args...)``. */ +template +FMT_NODISCARD FMT_INLINE auto formatted_size(format_string fmt, + T&&... args) -> size_t { + auto buf = detail::counting_buffer<>(); + detail::vformat_to(buf, string_view(fmt), fmt::make_format_args(args...), {}); + return buf.count(); +} + +FMT_API void vprint(string_view fmt, format_args args); +FMT_API void vprint(std::FILE* f, string_view fmt, format_args args); + +/** + \rst + Formats ``args`` according to specifications in ``fmt`` and writes the output + to ``stdout``. + + **Example**:: + + fmt::print("Elapsed time: {0:.2f} seconds", 1.23); + \endrst + */ +template +FMT_INLINE void print(format_string fmt, T&&... args) { + const auto& vargs = fmt::make_format_args(args...); + return detail::is_utf8() ? vprint(fmt, vargs) + : detail::vprint_mojibake(stdout, fmt, vargs); +} + +/** + \rst + Formats ``args`` according to specifications in ``fmt`` and writes the + output to the file ``f``. + + **Example**:: + + fmt::print(stderr, "Don't {}!", "panic"); + \endrst + */ +template +FMT_INLINE void print(std::FILE* f, format_string fmt, T&&... args) { + const auto& vargs = fmt::make_format_args(args...); + return detail::is_utf8() ? vprint(f, fmt, vargs) + : detail::vprint_mojibake(f, fmt, vargs); +} + +FMT_MODULE_EXPORT_END +FMT_GCC_PRAGMA("GCC pop_options") +FMT_END_NAMESPACE + +#ifdef FMT_HEADER_ONLY +# include "format.h" +#endif +#endif // FMT_CORE_H_ diff --git a/src/fmtlib/fmt/format-inl.h b/src/fmtlib/fmt/format-inl.h new file mode 100644 index 0000000..f44df01 --- /dev/null +++ b/src/fmtlib/fmt/format-inl.h @@ -0,0 +1,1733 @@ +// Formatting library for C++ - implementation +// +// Copyright (c) 2012 - 2016, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#ifndef FMT_FORMAT_INL_H_ +#define FMT_FORMAT_INL_H_ + +#include +#include +#include // errno +#include +#include +#include +#include // std::memmove +#include +#include + +#ifndef FMT_STATIC_THOUSANDS_SEPARATOR +# include +#endif + +#ifdef _WIN32 +# include // _isatty +#endif + +#include "format.h" + +FMT_BEGIN_NAMESPACE +namespace detail { + +FMT_FUNC void assert_fail(const char* file, int line, const char* message) { + // Use unchecked std::fprintf to avoid triggering another assertion when + // writing to stderr fails + std::fprintf(stderr, "%s:%d: assertion failed: %s", file, line, message); + // Chosen instead of std::abort to satisfy Clang in CUDA mode during device + // code pass. + std::terminate(); +} + +FMT_FUNC void throw_format_error(const char* message) { + FMT_THROW(format_error(message)); +} + +FMT_FUNC void format_error_code(detail::buffer& out, int error_code, + string_view message) noexcept { + // Report error code making sure that the output fits into + // inline_buffer_size to avoid dynamic memory allocation and potential + // bad_alloc. + out.try_resize(0); + static const char SEP[] = ": "; + static const char ERROR_STR[] = "error "; + // Subtract 2 to account for terminating null characters in SEP and ERROR_STR. + size_t error_code_size = sizeof(SEP) + sizeof(ERROR_STR) - 2; + auto abs_value = static_cast>(error_code); + if (detail::is_negative(error_code)) { + abs_value = 0 - abs_value; + ++error_code_size; + } + error_code_size += detail::to_unsigned(detail::count_digits(abs_value)); + auto it = buffer_appender(out); + if (message.size() <= inline_buffer_size - error_code_size) + format_to(it, FMT_STRING("{}{}"), message, SEP); + format_to(it, FMT_STRING("{}{}"), ERROR_STR, error_code); + FMT_ASSERT(out.size() <= inline_buffer_size, ""); +} + +FMT_FUNC void report_error(format_func func, int error_code, + const char* message) noexcept { + memory_buffer full_message; + func(full_message, error_code, message); + // Don't use fwrite_fully because the latter may throw. + if (std::fwrite(full_message.data(), full_message.size(), 1, stderr) > 0) + std::fputc('\n', stderr); +} + +// A wrapper around fwrite that throws on error. +inline void fwrite_fully(const void* ptr, size_t size, size_t count, + FILE* stream) { + size_t written = std::fwrite(ptr, size, count, stream); + if (written < count) + FMT_THROW(system_error(errno, FMT_STRING("cannot write to file"))); +} + +#ifndef FMT_STATIC_THOUSANDS_SEPARATOR +template +locale_ref::locale_ref(const Locale& loc) : locale_(&loc) { + static_assert(std::is_same::value, ""); +} + +template Locale locale_ref::get() const { + static_assert(std::is_same::value, ""); + return locale_ ? *static_cast(locale_) : std::locale(); +} + +template +FMT_FUNC auto thousands_sep_impl(locale_ref loc) -> thousands_sep_result { + auto& facet = std::use_facet>(loc.get()); + auto grouping = facet.grouping(); + auto thousands_sep = grouping.empty() ? Char() : facet.thousands_sep(); + return {std::move(grouping), thousands_sep}; +} +template FMT_FUNC Char decimal_point_impl(locale_ref loc) { + return std::use_facet>(loc.get()) + .decimal_point(); +} +#else +template +FMT_FUNC auto thousands_sep_impl(locale_ref) -> thousands_sep_result { + return {"\03", FMT_STATIC_THOUSANDS_SEPARATOR}; +} +template FMT_FUNC Char decimal_point_impl(locale_ref) { + return '.'; +} +#endif +} // namespace detail + +#if !FMT_MSC_VERSION +FMT_API FMT_FUNC format_error::~format_error() noexcept = default; +#endif + +FMT_FUNC std::system_error vsystem_error(int error_code, string_view format_str, + format_args args) { + auto ec = std::error_code(error_code, std::generic_category()); + return std::system_error(ec, vformat(format_str, args)); +} + +namespace detail { + +template inline bool operator==(basic_fp x, basic_fp y) { + return x.f == y.f && x.e == y.e; +} + +// Compilers should be able to optimize this into the ror instruction. +FMT_CONSTEXPR inline uint32_t rotr(uint32_t n, uint32_t r) noexcept { + r &= 31; + return (n >> r) | (n << (32 - r)); +} +FMT_CONSTEXPR inline uint64_t rotr(uint64_t n, uint32_t r) noexcept { + r &= 63; + return (n >> r) | (n << (64 - r)); +} + +// Computes 128-bit result of multiplication of two 64-bit unsigned integers. +inline uint128_fallback umul128(uint64_t x, uint64_t y) noexcept { +#if FMT_USE_INT128 + auto p = static_cast(x) * static_cast(y); + return {static_cast(p >> 64), static_cast(p)}; +#elif defined(_MSC_VER) && defined(_M_X64) + auto result = uint128_fallback(); + result.lo_ = _umul128(x, y, &result.hi_); + return result; +#else + const uint64_t mask = static_cast(max_value()); + + uint64_t a = x >> 32; + uint64_t b = x & mask; + uint64_t c = y >> 32; + uint64_t d = y & mask; + + uint64_t ac = a * c; + uint64_t bc = b * c; + uint64_t ad = a * d; + uint64_t bd = b * d; + + uint64_t intermediate = (bd >> 32) + (ad & mask) + (bc & mask); + + return {ac + (intermediate >> 32) + (ad >> 32) + (bc >> 32), + (intermediate << 32) + (bd & mask)}; +#endif +} + +// Implementation of Dragonbox algorithm: https://github.com/jk-jeon/dragonbox. +namespace dragonbox { +// Computes upper 64 bits of multiplication of two 64-bit unsigned integers. +inline uint64_t umul128_upper64(uint64_t x, uint64_t y) noexcept { +#if FMT_USE_INT128 + auto p = static_cast(x) * static_cast(y); + return static_cast(p >> 64); +#elif defined(_MSC_VER) && defined(_M_X64) + return __umulh(x, y); +#else + return umul128(x, y).high(); +#endif +} + +// Computes upper 128 bits of multiplication of a 64-bit unsigned integer and a +// 128-bit unsigned integer. +inline uint128_fallback umul192_upper128(uint64_t x, + uint128_fallback y) noexcept { + uint128_fallback r = umul128(x, y.high()); + r += umul128_upper64(x, y.low()); + return r; +} + +// Computes upper 64 bits of multiplication of a 32-bit unsigned integer and a +// 64-bit unsigned integer. +inline uint64_t umul96_upper64(uint32_t x, uint64_t y) noexcept { + return umul128_upper64(static_cast(x) << 32, y); +} + +// Computes lower 128 bits of multiplication of a 64-bit unsigned integer and a +// 128-bit unsigned integer. +inline uint128_fallback umul192_lower128(uint64_t x, + uint128_fallback y) noexcept { + uint64_t high = x * y.high(); + uint128_fallback high_low = umul128(x, y.low()); + return {high + high_low.high(), high_low.low()}; +} + +// Computes lower 64 bits of multiplication of a 32-bit unsigned integer and a +// 64-bit unsigned integer. +inline uint64_t umul96_lower64(uint32_t x, uint64_t y) noexcept { + return x * y; +} + +// Computes floor(log10(pow(2, e))) for e in [-2620, 2620] using the method from +// https://fmt.dev/papers/Dragonbox.pdf#page=28, section 6.1. +inline int floor_log10_pow2(int e) noexcept { + FMT_ASSERT(e <= 2620 && e >= -2620, "too large exponent"); + static_assert((-1 >> 1) == -1, "right shift is not arithmetic"); + return (e * 315653) >> 20; +} + +// Various fast log computations. +inline int floor_log2_pow10(int e) noexcept { + FMT_ASSERT(e <= 1233 && e >= -1233, "too large exponent"); + return (e * 1741647) >> 19; +} +inline int floor_log10_pow2_minus_log10_4_over_3(int e) noexcept { + FMT_ASSERT(e <= 2936 && e >= -2985, "too large exponent"); + return (e * 631305 - 261663) >> 21; +} + +static constexpr struct { + uint32_t divisor; + int shift_amount; +} div_small_pow10_infos[] = {{10, 16}, {100, 16}}; + +// Replaces n by floor(n / pow(10, N)) returning true if and only if n is +// divisible by pow(10, N). +// Precondition: n <= pow(10, N + 1). +template +bool check_divisibility_and_divide_by_pow10(uint32_t& n) noexcept { + // The numbers below are chosen such that: + // 1. floor(n/d) = floor(nm / 2^k) where d=10 or d=100, + // 2. nm mod 2^k < m if and only if n is divisible by d, + // where m is magic_number, k is shift_amount + // and d is divisor. + // + // Item 1 is a common technique of replacing division by a constant with + // multiplication, see e.g. "Division by Invariant Integers Using + // Multiplication" by Granlund and Montgomery (1994). magic_number (m) is set + // to ceil(2^k/d) for large enough k. + // The idea for item 2 originates from Schubfach. + constexpr auto info = div_small_pow10_infos[N - 1]; + FMT_ASSERT(n <= info.divisor * 10, "n is too large"); + constexpr uint32_t magic_number = + (1u << info.shift_amount) / info.divisor + 1; + n *= magic_number; + const uint32_t comparison_mask = (1u << info.shift_amount) - 1; + bool result = (n & comparison_mask) < magic_number; + n >>= info.shift_amount; + return result; +} + +// Computes floor(n / pow(10, N)) for small n and N. +// Precondition: n <= pow(10, N + 1). +template uint32_t small_division_by_pow10(uint32_t n) noexcept { + constexpr auto info = div_small_pow10_infos[N - 1]; + FMT_ASSERT(n <= info.divisor * 10, "n is too large"); + constexpr uint32_t magic_number = + (1u << info.shift_amount) / info.divisor + 1; + return (n * magic_number) >> info.shift_amount; +} + +// Computes floor(n / 10^(kappa + 1)) (float) +inline uint32_t divide_by_10_to_kappa_plus_1(uint32_t n) noexcept { + // 1374389535 = ceil(2^37/100) + return static_cast((static_cast(n) * 1374389535) >> 37); +} +// Computes floor(n / 10^(kappa + 1)) (double) +inline uint64_t divide_by_10_to_kappa_plus_1(uint64_t n) noexcept { + // 2361183241434822607 = ceil(2^(64+7)/1000) + return umul128_upper64(n, 2361183241434822607ull) >> 7; +} + +// Various subroutines using pow10 cache +template struct cache_accessor; + +template <> struct cache_accessor { + using carrier_uint = float_info::carrier_uint; + using cache_entry_type = uint64_t; + + static uint64_t get_cached_power(int k) noexcept { + FMT_ASSERT(k >= float_info::min_k && k <= float_info::max_k, + "k is out of range"); + static constexpr const uint64_t pow10_significands[] = { + 0x81ceb32c4b43fcf5, 0xa2425ff75e14fc32, 0xcad2f7f5359a3b3f, + 0xfd87b5f28300ca0e, 0x9e74d1b791e07e49, 0xc612062576589ddb, + 0xf79687aed3eec552, 0x9abe14cd44753b53, 0xc16d9a0095928a28, + 0xf1c90080baf72cb2, 0x971da05074da7bef, 0xbce5086492111aeb, + 0xec1e4a7db69561a6, 0x9392ee8e921d5d08, 0xb877aa3236a4b44a, + 0xe69594bec44de15c, 0x901d7cf73ab0acda, 0xb424dc35095cd810, + 0xe12e13424bb40e14, 0x8cbccc096f5088cc, 0xafebff0bcb24aaff, + 0xdbe6fecebdedd5bf, 0x89705f4136b4a598, 0xabcc77118461cefd, + 0xd6bf94d5e57a42bd, 0x8637bd05af6c69b6, 0xa7c5ac471b478424, + 0xd1b71758e219652c, 0x83126e978d4fdf3c, 0xa3d70a3d70a3d70b, + 0xcccccccccccccccd, 0x8000000000000000, 0xa000000000000000, + 0xc800000000000000, 0xfa00000000000000, 0x9c40000000000000, + 0xc350000000000000, 0xf424000000000000, 0x9896800000000000, + 0xbebc200000000000, 0xee6b280000000000, 0x9502f90000000000, + 0xba43b74000000000, 0xe8d4a51000000000, 0x9184e72a00000000, + 0xb5e620f480000000, 0xe35fa931a0000000, 0x8e1bc9bf04000000, + 0xb1a2bc2ec5000000, 0xde0b6b3a76400000, 0x8ac7230489e80000, + 0xad78ebc5ac620000, 0xd8d726b7177a8000, 0x878678326eac9000, + 0xa968163f0a57b400, 0xd3c21bcecceda100, 0x84595161401484a0, + 0xa56fa5b99019a5c8, 0xcecb8f27f4200f3a, 0x813f3978f8940985, + 0xa18f07d736b90be6, 0xc9f2c9cd04674edf, 0xfc6f7c4045812297, + 0x9dc5ada82b70b59e, 0xc5371912364ce306, 0xf684df56c3e01bc7, + 0x9a130b963a6c115d, 0xc097ce7bc90715b4, 0xf0bdc21abb48db21, + 0x96769950b50d88f5, 0xbc143fa4e250eb32, 0xeb194f8e1ae525fe, + 0x92efd1b8d0cf37bf, 0xb7abc627050305ae, 0xe596b7b0c643c71a, + 0x8f7e32ce7bea5c70, 0xb35dbf821ae4f38c, 0xe0352f62a19e306f}; + return pow10_significands[k - float_info::min_k]; + } + + struct compute_mul_result { + carrier_uint result; + bool is_integer; + }; + struct compute_mul_parity_result { + bool parity; + bool is_integer; + }; + + static compute_mul_result compute_mul( + carrier_uint u, const cache_entry_type& cache) noexcept { + auto r = umul96_upper64(u, cache); + return {static_cast(r >> 32), + static_cast(r) == 0}; + } + + static uint32_t compute_delta(const cache_entry_type& cache, + int beta) noexcept { + return static_cast(cache >> (64 - 1 - beta)); + } + + static compute_mul_parity_result compute_mul_parity( + carrier_uint two_f, const cache_entry_type& cache, int beta) noexcept { + FMT_ASSERT(beta >= 1, ""); + FMT_ASSERT(beta < 64, ""); + + auto r = umul96_lower64(two_f, cache); + return {((r >> (64 - beta)) & 1) != 0, + static_cast(r >> (32 - beta)) == 0}; + } + + static carrier_uint compute_left_endpoint_for_shorter_interval_case( + const cache_entry_type& cache, int beta) noexcept { + return static_cast( + (cache - (cache >> (num_significand_bits() + 2))) >> + (64 - num_significand_bits() - 1 - beta)); + } + + static carrier_uint compute_right_endpoint_for_shorter_interval_case( + const cache_entry_type& cache, int beta) noexcept { + return static_cast( + (cache + (cache >> (num_significand_bits() + 1))) >> + (64 - num_significand_bits() - 1 - beta)); + } + + static carrier_uint compute_round_up_for_shorter_interval_case( + const cache_entry_type& cache, int beta) noexcept { + return (static_cast( + cache >> (64 - num_significand_bits() - 2 - beta)) + + 1) / + 2; + } +}; + +template <> struct cache_accessor { + using carrier_uint = float_info::carrier_uint; + using cache_entry_type = uint128_fallback; + + static uint128_fallback get_cached_power(int k) noexcept { + FMT_ASSERT(k >= float_info::min_k && k <= float_info::max_k, + "k is out of range"); + + static constexpr const uint128_fallback pow10_significands[] = { +#if FMT_USE_FULL_CACHE_DRAGONBOX + {0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7b}, + {0x9faacf3df73609b1, 0x77b191618c54e9ad}, + {0xc795830d75038c1d, 0xd59df5b9ef6a2418}, + {0xf97ae3d0d2446f25, 0x4b0573286b44ad1e}, + {0x9becce62836ac577, 0x4ee367f9430aec33}, + {0xc2e801fb244576d5, 0x229c41f793cda740}, + {0xf3a20279ed56d48a, 0x6b43527578c11110}, + {0x9845418c345644d6, 0x830a13896b78aaaa}, + {0xbe5691ef416bd60c, 0x23cc986bc656d554}, + {0xedec366b11c6cb8f, 0x2cbfbe86b7ec8aa9}, + {0x94b3a202eb1c3f39, 0x7bf7d71432f3d6aa}, + {0xb9e08a83a5e34f07, 0xdaf5ccd93fb0cc54}, + {0xe858ad248f5c22c9, 0xd1b3400f8f9cff69}, + {0x91376c36d99995be, 0x23100809b9c21fa2}, + {0xb58547448ffffb2d, 0xabd40a0c2832a78b}, + {0xe2e69915b3fff9f9, 0x16c90c8f323f516d}, + {0x8dd01fad907ffc3b, 0xae3da7d97f6792e4}, + {0xb1442798f49ffb4a, 0x99cd11cfdf41779d}, + {0xdd95317f31c7fa1d, 0x40405643d711d584}, + {0x8a7d3eef7f1cfc52, 0x482835ea666b2573}, + {0xad1c8eab5ee43b66, 0xda3243650005eed0}, + {0xd863b256369d4a40, 0x90bed43e40076a83}, + {0x873e4f75e2224e68, 0x5a7744a6e804a292}, + {0xa90de3535aaae202, 0x711515d0a205cb37}, + {0xd3515c2831559a83, 0x0d5a5b44ca873e04}, + {0x8412d9991ed58091, 0xe858790afe9486c3}, + {0xa5178fff668ae0b6, 0x626e974dbe39a873}, + {0xce5d73ff402d98e3, 0xfb0a3d212dc81290}, + {0x80fa687f881c7f8e, 0x7ce66634bc9d0b9a}, + {0xa139029f6a239f72, 0x1c1fffc1ebc44e81}, + {0xc987434744ac874e, 0xa327ffb266b56221}, + {0xfbe9141915d7a922, 0x4bf1ff9f0062baa9}, + {0x9d71ac8fada6c9b5, 0x6f773fc3603db4aa}, + {0xc4ce17b399107c22, 0xcb550fb4384d21d4}, + {0xf6019da07f549b2b, 0x7e2a53a146606a49}, + {0x99c102844f94e0fb, 0x2eda7444cbfc426e}, + {0xc0314325637a1939, 0xfa911155fefb5309}, + {0xf03d93eebc589f88, 0x793555ab7eba27cb}, + {0x96267c7535b763b5, 0x4bc1558b2f3458df}, + {0xbbb01b9283253ca2, 0x9eb1aaedfb016f17}, + {0xea9c227723ee8bcb, 0x465e15a979c1cadd}, + {0x92a1958a7675175f, 0x0bfacd89ec191eca}, + {0xb749faed14125d36, 0xcef980ec671f667c}, + {0xe51c79a85916f484, 0x82b7e12780e7401b}, + {0x8f31cc0937ae58d2, 0xd1b2ecb8b0908811}, + {0xb2fe3f0b8599ef07, 0x861fa7e6dcb4aa16}, + {0xdfbdcece67006ac9, 0x67a791e093e1d49b}, + {0x8bd6a141006042bd, 0xe0c8bb2c5c6d24e1}, + {0xaecc49914078536d, 0x58fae9f773886e19}, + {0xda7f5bf590966848, 0xaf39a475506a899f}, + {0x888f99797a5e012d, 0x6d8406c952429604}, + {0xaab37fd7d8f58178, 0xc8e5087ba6d33b84}, + {0xd5605fcdcf32e1d6, 0xfb1e4a9a90880a65}, + {0x855c3be0a17fcd26, 0x5cf2eea09a550680}, + {0xa6b34ad8c9dfc06f, 0xf42faa48c0ea481f}, + {0xd0601d8efc57b08b, 0xf13b94daf124da27}, + {0x823c12795db6ce57, 0x76c53d08d6b70859}, + {0xa2cb1717b52481ed, 0x54768c4b0c64ca6f}, + {0xcb7ddcdda26da268, 0xa9942f5dcf7dfd0a}, + {0xfe5d54150b090b02, 0xd3f93b35435d7c4d}, + {0x9efa548d26e5a6e1, 0xc47bc5014a1a6db0}, + {0xc6b8e9b0709f109a, 0x359ab6419ca1091c}, + {0xf867241c8cc6d4c0, 0xc30163d203c94b63}, + {0x9b407691d7fc44f8, 0x79e0de63425dcf1e}, + {0xc21094364dfb5636, 0x985915fc12f542e5}, + {0xf294b943e17a2bc4, 0x3e6f5b7b17b2939e}, + {0x979cf3ca6cec5b5a, 0xa705992ceecf9c43}, + {0xbd8430bd08277231, 0x50c6ff782a838354}, + {0xece53cec4a314ebd, 0xa4f8bf5635246429}, + {0x940f4613ae5ed136, 0x871b7795e136be9a}, + {0xb913179899f68584, 0x28e2557b59846e40}, + {0xe757dd7ec07426e5, 0x331aeada2fe589d0}, + {0x9096ea6f3848984f, 0x3ff0d2c85def7622}, + {0xb4bca50b065abe63, 0x0fed077a756b53aa}, + {0xe1ebce4dc7f16dfb, 0xd3e8495912c62895}, + {0x8d3360f09cf6e4bd, 0x64712dd7abbbd95d}, + {0xb080392cc4349dec, 0xbd8d794d96aacfb4}, + {0xdca04777f541c567, 0xecf0d7a0fc5583a1}, + {0x89e42caaf9491b60, 0xf41686c49db57245}, + {0xac5d37d5b79b6239, 0x311c2875c522ced6}, + {0xd77485cb25823ac7, 0x7d633293366b828c}, + {0x86a8d39ef77164bc, 0xae5dff9c02033198}, + {0xa8530886b54dbdeb, 0xd9f57f830283fdfd}, + {0xd267caa862a12d66, 0xd072df63c324fd7c}, + {0x8380dea93da4bc60, 0x4247cb9e59f71e6e}, + {0xa46116538d0deb78, 0x52d9be85f074e609}, + {0xcd795be870516656, 0x67902e276c921f8c}, + {0x806bd9714632dff6, 0x00ba1cd8a3db53b7}, + {0xa086cfcd97bf97f3, 0x80e8a40eccd228a5}, + {0xc8a883c0fdaf7df0, 0x6122cd128006b2ce}, + {0xfad2a4b13d1b5d6c, 0x796b805720085f82}, + {0x9cc3a6eec6311a63, 0xcbe3303674053bb1}, + {0xc3f490aa77bd60fc, 0xbedbfc4411068a9d}, + {0xf4f1b4d515acb93b, 0xee92fb5515482d45}, + {0x991711052d8bf3c5, 0x751bdd152d4d1c4b}, + {0xbf5cd54678eef0b6, 0xd262d45a78a0635e}, + {0xef340a98172aace4, 0x86fb897116c87c35}, + {0x9580869f0e7aac0e, 0xd45d35e6ae3d4da1}, + {0xbae0a846d2195712, 0x8974836059cca10a}, + {0xe998d258869facd7, 0x2bd1a438703fc94c}, + {0x91ff83775423cc06, 0x7b6306a34627ddd0}, + {0xb67f6455292cbf08, 0x1a3bc84c17b1d543}, + {0xe41f3d6a7377eeca, 0x20caba5f1d9e4a94}, + {0x8e938662882af53e, 0x547eb47b7282ee9d}, + {0xb23867fb2a35b28d, 0xe99e619a4f23aa44}, + {0xdec681f9f4c31f31, 0x6405fa00e2ec94d5}, + {0x8b3c113c38f9f37e, 0xde83bc408dd3dd05}, + {0xae0b158b4738705e, 0x9624ab50b148d446}, + {0xd98ddaee19068c76, 0x3badd624dd9b0958}, + {0x87f8a8d4cfa417c9, 0xe54ca5d70a80e5d7}, + {0xa9f6d30a038d1dbc, 0x5e9fcf4ccd211f4d}, + {0xd47487cc8470652b, 0x7647c32000696720}, + {0x84c8d4dfd2c63f3b, 0x29ecd9f40041e074}, + {0xa5fb0a17c777cf09, 0xf468107100525891}, + {0xcf79cc9db955c2cc, 0x7182148d4066eeb5}, + {0x81ac1fe293d599bf, 0xc6f14cd848405531}, + {0xa21727db38cb002f, 0xb8ada00e5a506a7d}, + {0xca9cf1d206fdc03b, 0xa6d90811f0e4851d}, + {0xfd442e4688bd304a, 0x908f4a166d1da664}, + {0x9e4a9cec15763e2e, 0x9a598e4e043287ff}, + {0xc5dd44271ad3cdba, 0x40eff1e1853f29fe}, + {0xf7549530e188c128, 0xd12bee59e68ef47d}, + {0x9a94dd3e8cf578b9, 0x82bb74f8301958cf}, + {0xc13a148e3032d6e7, 0xe36a52363c1faf02}, + {0xf18899b1bc3f8ca1, 0xdc44e6c3cb279ac2}, + {0x96f5600f15a7b7e5, 0x29ab103a5ef8c0ba}, + {0xbcb2b812db11a5de, 0x7415d448f6b6f0e8}, + {0xebdf661791d60f56, 0x111b495b3464ad22}, + {0x936b9fcebb25c995, 0xcab10dd900beec35}, + {0xb84687c269ef3bfb, 0x3d5d514f40eea743}, + {0xe65829b3046b0afa, 0x0cb4a5a3112a5113}, + {0x8ff71a0fe2c2e6dc, 0x47f0e785eaba72ac}, + {0xb3f4e093db73a093, 0x59ed216765690f57}, + {0xe0f218b8d25088b8, 0x306869c13ec3532d}, + {0x8c974f7383725573, 0x1e414218c73a13fc}, + {0xafbd2350644eeacf, 0xe5d1929ef90898fb}, + {0xdbac6c247d62a583, 0xdf45f746b74abf3a}, + {0x894bc396ce5da772, 0x6b8bba8c328eb784}, + {0xab9eb47c81f5114f, 0x066ea92f3f326565}, + {0xd686619ba27255a2, 0xc80a537b0efefebe}, + {0x8613fd0145877585, 0xbd06742ce95f5f37}, + {0xa798fc4196e952e7, 0x2c48113823b73705}, + {0xd17f3b51fca3a7a0, 0xf75a15862ca504c6}, + {0x82ef85133de648c4, 0x9a984d73dbe722fc}, + {0xa3ab66580d5fdaf5, 0xc13e60d0d2e0ebbb}, + {0xcc963fee10b7d1b3, 0x318df905079926a9}, + {0xffbbcfe994e5c61f, 0xfdf17746497f7053}, + {0x9fd561f1fd0f9bd3, 0xfeb6ea8bedefa634}, + {0xc7caba6e7c5382c8, 0xfe64a52ee96b8fc1}, + {0xf9bd690a1b68637b, 0x3dfdce7aa3c673b1}, + {0x9c1661a651213e2d, 0x06bea10ca65c084f}, + {0xc31bfa0fe5698db8, 0x486e494fcff30a63}, + {0xf3e2f893dec3f126, 0x5a89dba3c3efccfb}, + {0x986ddb5c6b3a76b7, 0xf89629465a75e01d}, + {0xbe89523386091465, 0xf6bbb397f1135824}, + {0xee2ba6c0678b597f, 0x746aa07ded582e2d}, + {0x94db483840b717ef, 0xa8c2a44eb4571cdd}, + {0xba121a4650e4ddeb, 0x92f34d62616ce414}, + {0xe896a0d7e51e1566, 0x77b020baf9c81d18}, + {0x915e2486ef32cd60, 0x0ace1474dc1d122f}, + {0xb5b5ada8aaff80b8, 0x0d819992132456bb}, + {0xe3231912d5bf60e6, 0x10e1fff697ed6c6a}, + {0x8df5efabc5979c8f, 0xca8d3ffa1ef463c2}, + {0xb1736b96b6fd83b3, 0xbd308ff8a6b17cb3}, + {0xddd0467c64bce4a0, 0xac7cb3f6d05ddbdf}, + {0x8aa22c0dbef60ee4, 0x6bcdf07a423aa96c}, + {0xad4ab7112eb3929d, 0x86c16c98d2c953c7}, + {0xd89d64d57a607744, 0xe871c7bf077ba8b8}, + {0x87625f056c7c4a8b, 0x11471cd764ad4973}, + {0xa93af6c6c79b5d2d, 0xd598e40d3dd89bd0}, + {0xd389b47879823479, 0x4aff1d108d4ec2c4}, + {0x843610cb4bf160cb, 0xcedf722a585139bb}, + {0xa54394fe1eedb8fe, 0xc2974eb4ee658829}, + {0xce947a3da6a9273e, 0x733d226229feea33}, + {0x811ccc668829b887, 0x0806357d5a3f5260}, + {0xa163ff802a3426a8, 0xca07c2dcb0cf26f8}, + {0xc9bcff6034c13052, 0xfc89b393dd02f0b6}, + {0xfc2c3f3841f17c67, 0xbbac2078d443ace3}, + {0x9d9ba7832936edc0, 0xd54b944b84aa4c0e}, + {0xc5029163f384a931, 0x0a9e795e65d4df12}, + {0xf64335bcf065d37d, 0x4d4617b5ff4a16d6}, + {0x99ea0196163fa42e, 0x504bced1bf8e4e46}, + {0xc06481fb9bcf8d39, 0xe45ec2862f71e1d7}, + {0xf07da27a82c37088, 0x5d767327bb4e5a4d}, + {0x964e858c91ba2655, 0x3a6a07f8d510f870}, + {0xbbe226efb628afea, 0x890489f70a55368c}, + {0xeadab0aba3b2dbe5, 0x2b45ac74ccea842f}, + {0x92c8ae6b464fc96f, 0x3b0b8bc90012929e}, + {0xb77ada0617e3bbcb, 0x09ce6ebb40173745}, + {0xe55990879ddcaabd, 0xcc420a6a101d0516}, + {0x8f57fa54c2a9eab6, 0x9fa946824a12232e}, + {0xb32df8e9f3546564, 0x47939822dc96abfa}, + {0xdff9772470297ebd, 0x59787e2b93bc56f8}, + {0x8bfbea76c619ef36, 0x57eb4edb3c55b65b}, + {0xaefae51477a06b03, 0xede622920b6b23f2}, + {0xdab99e59958885c4, 0xe95fab368e45ecee}, + {0x88b402f7fd75539b, 0x11dbcb0218ebb415}, + {0xaae103b5fcd2a881, 0xd652bdc29f26a11a}, + {0xd59944a37c0752a2, 0x4be76d3346f04960}, + {0x857fcae62d8493a5, 0x6f70a4400c562ddc}, + {0xa6dfbd9fb8e5b88e, 0xcb4ccd500f6bb953}, + {0xd097ad07a71f26b2, 0x7e2000a41346a7a8}, + {0x825ecc24c873782f, 0x8ed400668c0c28c9}, + {0xa2f67f2dfa90563b, 0x728900802f0f32fb}, + {0xcbb41ef979346bca, 0x4f2b40a03ad2ffba}, + {0xfea126b7d78186bc, 0xe2f610c84987bfa9}, + {0x9f24b832e6b0f436, 0x0dd9ca7d2df4d7ca}, + {0xc6ede63fa05d3143, 0x91503d1c79720dbc}, + {0xf8a95fcf88747d94, 0x75a44c6397ce912b}, + {0x9b69dbe1b548ce7c, 0xc986afbe3ee11abb}, + {0xc24452da229b021b, 0xfbe85badce996169}, + {0xf2d56790ab41c2a2, 0xfae27299423fb9c4}, + {0x97c560ba6b0919a5, 0xdccd879fc967d41b}, + {0xbdb6b8e905cb600f, 0x5400e987bbc1c921}, + {0xed246723473e3813, 0x290123e9aab23b69}, + {0x9436c0760c86e30b, 0xf9a0b6720aaf6522}, + {0xb94470938fa89bce, 0xf808e40e8d5b3e6a}, + {0xe7958cb87392c2c2, 0xb60b1d1230b20e05}, + {0x90bd77f3483bb9b9, 0xb1c6f22b5e6f48c3}, + {0xb4ecd5f01a4aa828, 0x1e38aeb6360b1af4}, + {0xe2280b6c20dd5232, 0x25c6da63c38de1b1}, + {0x8d590723948a535f, 0x579c487e5a38ad0f}, + {0xb0af48ec79ace837, 0x2d835a9df0c6d852}, + {0xdcdb1b2798182244, 0xf8e431456cf88e66}, + {0x8a08f0f8bf0f156b, 0x1b8e9ecb641b5900}, + {0xac8b2d36eed2dac5, 0xe272467e3d222f40}, + {0xd7adf884aa879177, 0x5b0ed81dcc6abb10}, + {0x86ccbb52ea94baea, 0x98e947129fc2b4ea}, + {0xa87fea27a539e9a5, 0x3f2398d747b36225}, + {0xd29fe4b18e88640e, 0x8eec7f0d19a03aae}, + {0x83a3eeeef9153e89, 0x1953cf68300424ad}, + {0xa48ceaaab75a8e2b, 0x5fa8c3423c052dd8}, + {0xcdb02555653131b6, 0x3792f412cb06794e}, + {0x808e17555f3ebf11, 0xe2bbd88bbee40bd1}, + {0xa0b19d2ab70e6ed6, 0x5b6aceaeae9d0ec5}, + {0xc8de047564d20a8b, 0xf245825a5a445276}, + {0xfb158592be068d2e, 0xeed6e2f0f0d56713}, + {0x9ced737bb6c4183d, 0x55464dd69685606c}, + {0xc428d05aa4751e4c, 0xaa97e14c3c26b887}, + {0xf53304714d9265df, 0xd53dd99f4b3066a9}, + {0x993fe2c6d07b7fab, 0xe546a8038efe402a}, + {0xbf8fdb78849a5f96, 0xde98520472bdd034}, + {0xef73d256a5c0f77c, 0x963e66858f6d4441}, + {0x95a8637627989aad, 0xdde7001379a44aa9}, + {0xbb127c53b17ec159, 0x5560c018580d5d53}, + {0xe9d71b689dde71af, 0xaab8f01e6e10b4a7}, + {0x9226712162ab070d, 0xcab3961304ca70e9}, + {0xb6b00d69bb55c8d1, 0x3d607b97c5fd0d23}, + {0xe45c10c42a2b3b05, 0x8cb89a7db77c506b}, + {0x8eb98a7a9a5b04e3, 0x77f3608e92adb243}, + {0xb267ed1940f1c61c, 0x55f038b237591ed4}, + {0xdf01e85f912e37a3, 0x6b6c46dec52f6689}, + {0x8b61313bbabce2c6, 0x2323ac4b3b3da016}, + {0xae397d8aa96c1b77, 0xabec975e0a0d081b}, + {0xd9c7dced53c72255, 0x96e7bd358c904a22}, + {0x881cea14545c7575, 0x7e50d64177da2e55}, + {0xaa242499697392d2, 0xdde50bd1d5d0b9ea}, + {0xd4ad2dbfc3d07787, 0x955e4ec64b44e865}, + {0x84ec3c97da624ab4, 0xbd5af13bef0b113f}, + {0xa6274bbdd0fadd61, 0xecb1ad8aeacdd58f}, + {0xcfb11ead453994ba, 0x67de18eda5814af3}, + {0x81ceb32c4b43fcf4, 0x80eacf948770ced8}, + {0xa2425ff75e14fc31, 0xa1258379a94d028e}, + {0xcad2f7f5359a3b3e, 0x096ee45813a04331}, + {0xfd87b5f28300ca0d, 0x8bca9d6e188853fd}, + {0x9e74d1b791e07e48, 0x775ea264cf55347e}, + {0xc612062576589dda, 0x95364afe032a819e}, + {0xf79687aed3eec551, 0x3a83ddbd83f52205}, + {0x9abe14cd44753b52, 0xc4926a9672793543}, + {0xc16d9a0095928a27, 0x75b7053c0f178294}, + {0xf1c90080baf72cb1, 0x5324c68b12dd6339}, + {0x971da05074da7bee, 0xd3f6fc16ebca5e04}, + {0xbce5086492111aea, 0x88f4bb1ca6bcf585}, + {0xec1e4a7db69561a5, 0x2b31e9e3d06c32e6}, + {0x9392ee8e921d5d07, 0x3aff322e62439fd0}, + {0xb877aa3236a4b449, 0x09befeb9fad487c3}, + {0xe69594bec44de15b, 0x4c2ebe687989a9b4}, + {0x901d7cf73ab0acd9, 0x0f9d37014bf60a11}, + {0xb424dc35095cd80f, 0x538484c19ef38c95}, + {0xe12e13424bb40e13, 0x2865a5f206b06fba}, + {0x8cbccc096f5088cb, 0xf93f87b7442e45d4}, + {0xafebff0bcb24aafe, 0xf78f69a51539d749}, + {0xdbe6fecebdedd5be, 0xb573440e5a884d1c}, + {0x89705f4136b4a597, 0x31680a88f8953031}, + {0xabcc77118461cefc, 0xfdc20d2b36ba7c3e}, + {0xd6bf94d5e57a42bc, 0x3d32907604691b4d}, + {0x8637bd05af6c69b5, 0xa63f9a49c2c1b110}, + {0xa7c5ac471b478423, 0x0fcf80dc33721d54}, + {0xd1b71758e219652b, 0xd3c36113404ea4a9}, + {0x83126e978d4fdf3b, 0x645a1cac083126ea}, + {0xa3d70a3d70a3d70a, 0x3d70a3d70a3d70a4}, + {0xcccccccccccccccc, 0xcccccccccccccccd}, + {0x8000000000000000, 0x0000000000000000}, + {0xa000000000000000, 0x0000000000000000}, + {0xc800000000000000, 0x0000000000000000}, + {0xfa00000000000000, 0x0000000000000000}, + {0x9c40000000000000, 0x0000000000000000}, + {0xc350000000000000, 0x0000000000000000}, + {0xf424000000000000, 0x0000000000000000}, + {0x9896800000000000, 0x0000000000000000}, + {0xbebc200000000000, 0x0000000000000000}, + {0xee6b280000000000, 0x0000000000000000}, + {0x9502f90000000000, 0x0000000000000000}, + {0xba43b74000000000, 0x0000000000000000}, + {0xe8d4a51000000000, 0x0000000000000000}, + {0x9184e72a00000000, 0x0000000000000000}, + {0xb5e620f480000000, 0x0000000000000000}, + {0xe35fa931a0000000, 0x0000000000000000}, + {0x8e1bc9bf04000000, 0x0000000000000000}, + {0xb1a2bc2ec5000000, 0x0000000000000000}, + {0xde0b6b3a76400000, 0x0000000000000000}, + {0x8ac7230489e80000, 0x0000000000000000}, + {0xad78ebc5ac620000, 0x0000000000000000}, + {0xd8d726b7177a8000, 0x0000000000000000}, + {0x878678326eac9000, 0x0000000000000000}, + {0xa968163f0a57b400, 0x0000000000000000}, + {0xd3c21bcecceda100, 0x0000000000000000}, + {0x84595161401484a0, 0x0000000000000000}, + {0xa56fa5b99019a5c8, 0x0000000000000000}, + {0xcecb8f27f4200f3a, 0x0000000000000000}, + {0x813f3978f8940984, 0x4000000000000000}, + {0xa18f07d736b90be5, 0x5000000000000000}, + {0xc9f2c9cd04674ede, 0xa400000000000000}, + {0xfc6f7c4045812296, 0x4d00000000000000}, + {0x9dc5ada82b70b59d, 0xf020000000000000}, + {0xc5371912364ce305, 0x6c28000000000000}, + {0xf684df56c3e01bc6, 0xc732000000000000}, + {0x9a130b963a6c115c, 0x3c7f400000000000}, + {0xc097ce7bc90715b3, 0x4b9f100000000000}, + {0xf0bdc21abb48db20, 0x1e86d40000000000}, + {0x96769950b50d88f4, 0x1314448000000000}, + {0xbc143fa4e250eb31, 0x17d955a000000000}, + {0xeb194f8e1ae525fd, 0x5dcfab0800000000}, + {0x92efd1b8d0cf37be, 0x5aa1cae500000000}, + {0xb7abc627050305ad, 0xf14a3d9e40000000}, + {0xe596b7b0c643c719, 0x6d9ccd05d0000000}, + {0x8f7e32ce7bea5c6f, 0xe4820023a2000000}, + {0xb35dbf821ae4f38b, 0xdda2802c8a800000}, + {0xe0352f62a19e306e, 0xd50b2037ad200000}, + {0x8c213d9da502de45, 0x4526f422cc340000}, + {0xaf298d050e4395d6, 0x9670b12b7f410000}, + {0xdaf3f04651d47b4c, 0x3c0cdd765f114000}, + {0x88d8762bf324cd0f, 0xa5880a69fb6ac800}, + {0xab0e93b6efee0053, 0x8eea0d047a457a00}, + {0xd5d238a4abe98068, 0x72a4904598d6d880}, + {0x85a36366eb71f041, 0x47a6da2b7f864750}, + {0xa70c3c40a64e6c51, 0x999090b65f67d924}, + {0xd0cf4b50cfe20765, 0xfff4b4e3f741cf6d}, + {0x82818f1281ed449f, 0xbff8f10e7a8921a5}, + {0xa321f2d7226895c7, 0xaff72d52192b6a0e}, + {0xcbea6f8ceb02bb39, 0x9bf4f8a69f764491}, + {0xfee50b7025c36a08, 0x02f236d04753d5b5}, + {0x9f4f2726179a2245, 0x01d762422c946591}, + {0xc722f0ef9d80aad6, 0x424d3ad2b7b97ef6}, + {0xf8ebad2b84e0d58b, 0xd2e0898765a7deb3}, + {0x9b934c3b330c8577, 0x63cc55f49f88eb30}, + {0xc2781f49ffcfa6d5, 0x3cbf6b71c76b25fc}, + {0xf316271c7fc3908a, 0x8bef464e3945ef7b}, + {0x97edd871cfda3a56, 0x97758bf0e3cbb5ad}, + {0xbde94e8e43d0c8ec, 0x3d52eeed1cbea318}, + {0xed63a231d4c4fb27, 0x4ca7aaa863ee4bde}, + {0x945e455f24fb1cf8, 0x8fe8caa93e74ef6b}, + {0xb975d6b6ee39e436, 0xb3e2fd538e122b45}, + {0xe7d34c64a9c85d44, 0x60dbbca87196b617}, + {0x90e40fbeea1d3a4a, 0xbc8955e946fe31ce}, + {0xb51d13aea4a488dd, 0x6babab6398bdbe42}, + {0xe264589a4dcdab14, 0xc696963c7eed2dd2}, + {0x8d7eb76070a08aec, 0xfc1e1de5cf543ca3}, + {0xb0de65388cc8ada8, 0x3b25a55f43294bcc}, + {0xdd15fe86affad912, 0x49ef0eb713f39ebf}, + {0x8a2dbf142dfcc7ab, 0x6e3569326c784338}, + {0xacb92ed9397bf996, 0x49c2c37f07965405}, + {0xd7e77a8f87daf7fb, 0xdc33745ec97be907}, + {0x86f0ac99b4e8dafd, 0x69a028bb3ded71a4}, + {0xa8acd7c0222311bc, 0xc40832ea0d68ce0d}, + {0xd2d80db02aabd62b, 0xf50a3fa490c30191}, + {0x83c7088e1aab65db, 0x792667c6da79e0fb}, + {0xa4b8cab1a1563f52, 0x577001b891185939}, + {0xcde6fd5e09abcf26, 0xed4c0226b55e6f87}, + {0x80b05e5ac60b6178, 0x544f8158315b05b5}, + {0xa0dc75f1778e39d6, 0x696361ae3db1c722}, + {0xc913936dd571c84c, 0x03bc3a19cd1e38ea}, + {0xfb5878494ace3a5f, 0x04ab48a04065c724}, + {0x9d174b2dcec0e47b, 0x62eb0d64283f9c77}, + {0xc45d1df942711d9a, 0x3ba5d0bd324f8395}, + {0xf5746577930d6500, 0xca8f44ec7ee3647a}, + {0x9968bf6abbe85f20, 0x7e998b13cf4e1ecc}, + {0xbfc2ef456ae276e8, 0x9e3fedd8c321a67f}, + {0xefb3ab16c59b14a2, 0xc5cfe94ef3ea101f}, + {0x95d04aee3b80ece5, 0xbba1f1d158724a13}, + {0xbb445da9ca61281f, 0x2a8a6e45ae8edc98}, + {0xea1575143cf97226, 0xf52d09d71a3293be}, + {0x924d692ca61be758, 0x593c2626705f9c57}, + {0xb6e0c377cfa2e12e, 0x6f8b2fb00c77836d}, + {0xe498f455c38b997a, 0x0b6dfb9c0f956448}, + {0x8edf98b59a373fec, 0x4724bd4189bd5ead}, + {0xb2977ee300c50fe7, 0x58edec91ec2cb658}, + {0xdf3d5e9bc0f653e1, 0x2f2967b66737e3ee}, + {0x8b865b215899f46c, 0xbd79e0d20082ee75}, + {0xae67f1e9aec07187, 0xecd8590680a3aa12}, + {0xda01ee641a708de9, 0xe80e6f4820cc9496}, + {0x884134fe908658b2, 0x3109058d147fdcde}, + {0xaa51823e34a7eede, 0xbd4b46f0599fd416}, + {0xd4e5e2cdc1d1ea96, 0x6c9e18ac7007c91b}, + {0x850fadc09923329e, 0x03e2cf6bc604ddb1}, + {0xa6539930bf6bff45, 0x84db8346b786151d}, + {0xcfe87f7cef46ff16, 0xe612641865679a64}, + {0x81f14fae158c5f6e, 0x4fcb7e8f3f60c07f}, + {0xa26da3999aef7749, 0xe3be5e330f38f09e}, + {0xcb090c8001ab551c, 0x5cadf5bfd3072cc6}, + {0xfdcb4fa002162a63, 0x73d9732fc7c8f7f7}, + {0x9e9f11c4014dda7e, 0x2867e7fddcdd9afb}, + {0xc646d63501a1511d, 0xb281e1fd541501b9}, + {0xf7d88bc24209a565, 0x1f225a7ca91a4227}, + {0x9ae757596946075f, 0x3375788de9b06959}, + {0xc1a12d2fc3978937, 0x0052d6b1641c83af}, + {0xf209787bb47d6b84, 0xc0678c5dbd23a49b}, + {0x9745eb4d50ce6332, 0xf840b7ba963646e1}, + {0xbd176620a501fbff, 0xb650e5a93bc3d899}, + {0xec5d3fa8ce427aff, 0xa3e51f138ab4cebf}, + {0x93ba47c980e98cdf, 0xc66f336c36b10138}, + {0xb8a8d9bbe123f017, 0xb80b0047445d4185}, + {0xe6d3102ad96cec1d, 0xa60dc059157491e6}, + {0x9043ea1ac7e41392, 0x87c89837ad68db30}, + {0xb454e4a179dd1877, 0x29babe4598c311fc}, + {0xe16a1dc9d8545e94, 0xf4296dd6fef3d67b}, + {0x8ce2529e2734bb1d, 0x1899e4a65f58660d}, + {0xb01ae745b101e9e4, 0x5ec05dcff72e7f90}, + {0xdc21a1171d42645d, 0x76707543f4fa1f74}, + {0x899504ae72497eba, 0x6a06494a791c53a9}, + {0xabfa45da0edbde69, 0x0487db9d17636893}, + {0xd6f8d7509292d603, 0x45a9d2845d3c42b7}, + {0x865b86925b9bc5c2, 0x0b8a2392ba45a9b3}, + {0xa7f26836f282b732, 0x8e6cac7768d7141f}, + {0xd1ef0244af2364ff, 0x3207d795430cd927}, + {0x8335616aed761f1f, 0x7f44e6bd49e807b9}, + {0xa402b9c5a8d3a6e7, 0x5f16206c9c6209a7}, + {0xcd036837130890a1, 0x36dba887c37a8c10}, + {0x802221226be55a64, 0xc2494954da2c978a}, + {0xa02aa96b06deb0fd, 0xf2db9baa10b7bd6d}, + {0xc83553c5c8965d3d, 0x6f92829494e5acc8}, + {0xfa42a8b73abbf48c, 0xcb772339ba1f17fa}, + {0x9c69a97284b578d7, 0xff2a760414536efc}, + {0xc38413cf25e2d70d, 0xfef5138519684abb}, + {0xf46518c2ef5b8cd1, 0x7eb258665fc25d6a}, + {0x98bf2f79d5993802, 0xef2f773ffbd97a62}, + {0xbeeefb584aff8603, 0xaafb550ffacfd8fb}, + {0xeeaaba2e5dbf6784, 0x95ba2a53f983cf39}, + {0x952ab45cfa97a0b2, 0xdd945a747bf26184}, + {0xba756174393d88df, 0x94f971119aeef9e5}, + {0xe912b9d1478ceb17, 0x7a37cd5601aab85e}, + {0x91abb422ccb812ee, 0xac62e055c10ab33b}, + {0xb616a12b7fe617aa, 0x577b986b314d600a}, + {0xe39c49765fdf9d94, 0xed5a7e85fda0b80c}, + {0x8e41ade9fbebc27d, 0x14588f13be847308}, + {0xb1d219647ae6b31c, 0x596eb2d8ae258fc9}, + {0xde469fbd99a05fe3, 0x6fca5f8ed9aef3bc}, + {0x8aec23d680043bee, 0x25de7bb9480d5855}, + {0xada72ccc20054ae9, 0xaf561aa79a10ae6b}, + {0xd910f7ff28069da4, 0x1b2ba1518094da05}, + {0x87aa9aff79042286, 0x90fb44d2f05d0843}, + {0xa99541bf57452b28, 0x353a1607ac744a54}, + {0xd3fa922f2d1675f2, 0x42889b8997915ce9}, + {0x847c9b5d7c2e09b7, 0x69956135febada12}, + {0xa59bc234db398c25, 0x43fab9837e699096}, + {0xcf02b2c21207ef2e, 0x94f967e45e03f4bc}, + {0x8161afb94b44f57d, 0x1d1be0eebac278f6}, + {0xa1ba1ba79e1632dc, 0x6462d92a69731733}, + {0xca28a291859bbf93, 0x7d7b8f7503cfdcff}, + {0xfcb2cb35e702af78, 0x5cda735244c3d43f}, + {0x9defbf01b061adab, 0x3a0888136afa64a8}, + {0xc56baec21c7a1916, 0x088aaa1845b8fdd1}, + {0xf6c69a72a3989f5b, 0x8aad549e57273d46}, + {0x9a3c2087a63f6399, 0x36ac54e2f678864c}, + {0xc0cb28a98fcf3c7f, 0x84576a1bb416a7de}, + {0xf0fdf2d3f3c30b9f, 0x656d44a2a11c51d6}, + {0x969eb7c47859e743, 0x9f644ae5a4b1b326}, + {0xbc4665b596706114, 0x873d5d9f0dde1fef}, + {0xeb57ff22fc0c7959, 0xa90cb506d155a7eb}, + {0x9316ff75dd87cbd8, 0x09a7f12442d588f3}, + {0xb7dcbf5354e9bece, 0x0c11ed6d538aeb30}, + {0xe5d3ef282a242e81, 0x8f1668c8a86da5fb}, + {0x8fa475791a569d10, 0xf96e017d694487bd}, + {0xb38d92d760ec4455, 0x37c981dcc395a9ad}, + {0xe070f78d3927556a, 0x85bbe253f47b1418}, + {0x8c469ab843b89562, 0x93956d7478ccec8f}, + {0xaf58416654a6babb, 0x387ac8d1970027b3}, + {0xdb2e51bfe9d0696a, 0x06997b05fcc0319f}, + {0x88fcf317f22241e2, 0x441fece3bdf81f04}, + {0xab3c2fddeeaad25a, 0xd527e81cad7626c4}, + {0xd60b3bd56a5586f1, 0x8a71e223d8d3b075}, + {0x85c7056562757456, 0xf6872d5667844e4a}, + {0xa738c6bebb12d16c, 0xb428f8ac016561dc}, + {0xd106f86e69d785c7, 0xe13336d701beba53}, + {0x82a45b450226b39c, 0xecc0024661173474}, + {0xa34d721642b06084, 0x27f002d7f95d0191}, + {0xcc20ce9bd35c78a5, 0x31ec038df7b441f5}, + {0xff290242c83396ce, 0x7e67047175a15272}, + {0x9f79a169bd203e41, 0x0f0062c6e984d387}, + {0xc75809c42c684dd1, 0x52c07b78a3e60869}, + {0xf92e0c3537826145, 0xa7709a56ccdf8a83}, + {0x9bbcc7a142b17ccb, 0x88a66076400bb692}, + {0xc2abf989935ddbfe, 0x6acff893d00ea436}, + {0xf356f7ebf83552fe, 0x0583f6b8c4124d44}, + {0x98165af37b2153de, 0xc3727a337a8b704b}, + {0xbe1bf1b059e9a8d6, 0x744f18c0592e4c5d}, + {0xeda2ee1c7064130c, 0x1162def06f79df74}, + {0x9485d4d1c63e8be7, 0x8addcb5645ac2ba9}, + {0xb9a74a0637ce2ee1, 0x6d953e2bd7173693}, + {0xe8111c87c5c1ba99, 0xc8fa8db6ccdd0438}, + {0x910ab1d4db9914a0, 0x1d9c9892400a22a3}, + {0xb54d5e4a127f59c8, 0x2503beb6d00cab4c}, + {0xe2a0b5dc971f303a, 0x2e44ae64840fd61e}, + {0x8da471a9de737e24, 0x5ceaecfed289e5d3}, + {0xb10d8e1456105dad, 0x7425a83e872c5f48}, + {0xdd50f1996b947518, 0xd12f124e28f7771a}, + {0x8a5296ffe33cc92f, 0x82bd6b70d99aaa70}, + {0xace73cbfdc0bfb7b, 0x636cc64d1001550c}, + {0xd8210befd30efa5a, 0x3c47f7e05401aa4f}, + {0x8714a775e3e95c78, 0x65acfaec34810a72}, + {0xa8d9d1535ce3b396, 0x7f1839a741a14d0e}, + {0xd31045a8341ca07c, 0x1ede48111209a051}, + {0x83ea2b892091e44d, 0x934aed0aab460433}, + {0xa4e4b66b68b65d60, 0xf81da84d56178540}, + {0xce1de40642e3f4b9, 0x36251260ab9d668f}, + {0x80d2ae83e9ce78f3, 0xc1d72b7c6b42601a}, + {0xa1075a24e4421730, 0xb24cf65b8612f820}, + {0xc94930ae1d529cfc, 0xdee033f26797b628}, + {0xfb9b7cd9a4a7443c, 0x169840ef017da3b2}, + {0x9d412e0806e88aa5, 0x8e1f289560ee864f}, + {0xc491798a08a2ad4e, 0xf1a6f2bab92a27e3}, + {0xf5b5d7ec8acb58a2, 0xae10af696774b1dc}, + {0x9991a6f3d6bf1765, 0xacca6da1e0a8ef2a}, + {0xbff610b0cc6edd3f, 0x17fd090a58d32af4}, + {0xeff394dcff8a948e, 0xddfc4b4cef07f5b1}, + {0x95f83d0a1fb69cd9, 0x4abdaf101564f98f}, + {0xbb764c4ca7a4440f, 0x9d6d1ad41abe37f2}, + {0xea53df5fd18d5513, 0x84c86189216dc5ee}, + {0x92746b9be2f8552c, 0x32fd3cf5b4e49bb5}, + {0xb7118682dbb66a77, 0x3fbc8c33221dc2a2}, + {0xe4d5e82392a40515, 0x0fabaf3feaa5334b}, + {0x8f05b1163ba6832d, 0x29cb4d87f2a7400f}, + {0xb2c71d5bca9023f8, 0x743e20e9ef511013}, + {0xdf78e4b2bd342cf6, 0x914da9246b255417}, + {0x8bab8eefb6409c1a, 0x1ad089b6c2f7548f}, + {0xae9672aba3d0c320, 0xa184ac2473b529b2}, + {0xda3c0f568cc4f3e8, 0xc9e5d72d90a2741f}, + {0x8865899617fb1871, 0x7e2fa67c7a658893}, + {0xaa7eebfb9df9de8d, 0xddbb901b98feeab8}, + {0xd51ea6fa85785631, 0x552a74227f3ea566}, + {0x8533285c936b35de, 0xd53a88958f872760}, + {0xa67ff273b8460356, 0x8a892abaf368f138}, + {0xd01fef10a657842c, 0x2d2b7569b0432d86}, + {0x8213f56a67f6b29b, 0x9c3b29620e29fc74}, + {0xa298f2c501f45f42, 0x8349f3ba91b47b90}, + {0xcb3f2f7642717713, 0x241c70a936219a74}, + {0xfe0efb53d30dd4d7, 0xed238cd383aa0111}, + {0x9ec95d1463e8a506, 0xf4363804324a40ab}, + {0xc67bb4597ce2ce48, 0xb143c6053edcd0d6}, + {0xf81aa16fdc1b81da, 0xdd94b7868e94050b}, + {0x9b10a4e5e9913128, 0xca7cf2b4191c8327}, + {0xc1d4ce1f63f57d72, 0xfd1c2f611f63a3f1}, + {0xf24a01a73cf2dccf, 0xbc633b39673c8ced}, + {0x976e41088617ca01, 0xd5be0503e085d814}, + {0xbd49d14aa79dbc82, 0x4b2d8644d8a74e19}, + {0xec9c459d51852ba2, 0xddf8e7d60ed1219f}, + {0x93e1ab8252f33b45, 0xcabb90e5c942b504}, + {0xb8da1662e7b00a17, 0x3d6a751f3b936244}, + {0xe7109bfba19c0c9d, 0x0cc512670a783ad5}, + {0x906a617d450187e2, 0x27fb2b80668b24c6}, + {0xb484f9dc9641e9da, 0xb1f9f660802dedf7}, + {0xe1a63853bbd26451, 0x5e7873f8a0396974}, + {0x8d07e33455637eb2, 0xdb0b487b6423e1e9}, + {0xb049dc016abc5e5f, 0x91ce1a9a3d2cda63}, + {0xdc5c5301c56b75f7, 0x7641a140cc7810fc}, + {0x89b9b3e11b6329ba, 0xa9e904c87fcb0a9e}, + {0xac2820d9623bf429, 0x546345fa9fbdcd45}, + {0xd732290fbacaf133, 0xa97c177947ad4096}, + {0x867f59a9d4bed6c0, 0x49ed8eabcccc485e}, + {0xa81f301449ee8c70, 0x5c68f256bfff5a75}, + {0xd226fc195c6a2f8c, 0x73832eec6fff3112}, + {0x83585d8fd9c25db7, 0xc831fd53c5ff7eac}, + {0xa42e74f3d032f525, 0xba3e7ca8b77f5e56}, + {0xcd3a1230c43fb26f, 0x28ce1bd2e55f35ec}, + {0x80444b5e7aa7cf85, 0x7980d163cf5b81b4}, + {0xa0555e361951c366, 0xd7e105bcc3326220}, + {0xc86ab5c39fa63440, 0x8dd9472bf3fefaa8}, + {0xfa856334878fc150, 0xb14f98f6f0feb952}, + {0x9c935e00d4b9d8d2, 0x6ed1bf9a569f33d4}, + {0xc3b8358109e84f07, 0x0a862f80ec4700c9}, + {0xf4a642e14c6262c8, 0xcd27bb612758c0fb}, + {0x98e7e9cccfbd7dbd, 0x8038d51cb897789d}, + {0xbf21e44003acdd2c, 0xe0470a63e6bd56c4}, + {0xeeea5d5004981478, 0x1858ccfce06cac75}, + {0x95527a5202df0ccb, 0x0f37801e0c43ebc9}, + {0xbaa718e68396cffd, 0xd30560258f54e6bb}, + {0xe950df20247c83fd, 0x47c6b82ef32a206a}, + {0x91d28b7416cdd27e, 0x4cdc331d57fa5442}, + {0xb6472e511c81471d, 0xe0133fe4adf8e953}, + {0xe3d8f9e563a198e5, 0x58180fddd97723a7}, + {0x8e679c2f5e44ff8f, 0x570f09eaa7ea7649}, + {0xb201833b35d63f73, 0x2cd2cc6551e513db}, + {0xde81e40a034bcf4f, 0xf8077f7ea65e58d2}, + {0x8b112e86420f6191, 0xfb04afaf27faf783}, + {0xadd57a27d29339f6, 0x79c5db9af1f9b564}, + {0xd94ad8b1c7380874, 0x18375281ae7822bd}, + {0x87cec76f1c830548, 0x8f2293910d0b15b6}, + {0xa9c2794ae3a3c69a, 0xb2eb3875504ddb23}, + {0xd433179d9c8cb841, 0x5fa60692a46151ec}, + {0x849feec281d7f328, 0xdbc7c41ba6bcd334}, + {0xa5c7ea73224deff3, 0x12b9b522906c0801}, + {0xcf39e50feae16bef, 0xd768226b34870a01}, + {0x81842f29f2cce375, 0xe6a1158300d46641}, + {0xa1e53af46f801c53, 0x60495ae3c1097fd1}, + {0xca5e89b18b602368, 0x385bb19cb14bdfc5}, + {0xfcf62c1dee382c42, 0x46729e03dd9ed7b6}, + {0x9e19db92b4e31ba9, 0x6c07a2c26a8346d2}, + {0xc5a05277621be293, 0xc7098b7305241886}, + { 0xf70867153aa2db38, + 0xb8cbee4fc66d1ea8 } +#else + {0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7b}, + {0xce5d73ff402d98e3, 0xfb0a3d212dc81290}, + {0xa6b34ad8c9dfc06f, 0xf42faa48c0ea481f}, + {0x86a8d39ef77164bc, 0xae5dff9c02033198}, + {0xd98ddaee19068c76, 0x3badd624dd9b0958}, + {0xafbd2350644eeacf, 0xe5d1929ef90898fb}, + {0x8df5efabc5979c8f, 0xca8d3ffa1ef463c2}, + {0xe55990879ddcaabd, 0xcc420a6a101d0516}, + {0xb94470938fa89bce, 0xf808e40e8d5b3e6a}, + {0x95a8637627989aad, 0xdde7001379a44aa9}, + {0xf1c90080baf72cb1, 0x5324c68b12dd6339}, + {0xc350000000000000, 0x0000000000000000}, + {0x9dc5ada82b70b59d, 0xf020000000000000}, + {0xfee50b7025c36a08, 0x02f236d04753d5b5}, + {0xcde6fd5e09abcf26, 0xed4c0226b55e6f87}, + {0xa6539930bf6bff45, 0x84db8346b786151d}, + {0x865b86925b9bc5c2, 0x0b8a2392ba45a9b3}, + {0xd910f7ff28069da4, 0x1b2ba1518094da05}, + {0xaf58416654a6babb, 0x387ac8d1970027b3}, + {0x8da471a9de737e24, 0x5ceaecfed289e5d3}, + {0xe4d5e82392a40515, 0x0fabaf3feaa5334b}, + {0xb8da1662e7b00a17, 0x3d6a751f3b936244}, + { 0x95527a5202df0ccb, + 0x0f37801e0c43ebc9 } +#endif + }; + +#if FMT_USE_FULL_CACHE_DRAGONBOX + return pow10_significands[k - float_info::min_k]; +#else + static constexpr const uint64_t powers_of_5_64[] = { + 0x0000000000000001, 0x0000000000000005, 0x0000000000000019, + 0x000000000000007d, 0x0000000000000271, 0x0000000000000c35, + 0x0000000000003d09, 0x000000000001312d, 0x000000000005f5e1, + 0x00000000001dcd65, 0x00000000009502f9, 0x0000000002e90edd, + 0x000000000e8d4a51, 0x0000000048c27395, 0x000000016bcc41e9, + 0x000000071afd498d, 0x0000002386f26fc1, 0x000000b1a2bc2ec5, + 0x000003782dace9d9, 0x00001158e460913d, 0x000056bc75e2d631, + 0x0001b1ae4d6e2ef5, 0x000878678326eac9, 0x002a5a058fc295ed, + 0x00d3c21bcecceda1, 0x0422ca8b0a00a425, 0x14adf4b7320334b9}; + + static const int compression_ratio = 27; + + // Compute base index. + int cache_index = (k - float_info::min_k) / compression_ratio; + int kb = cache_index * compression_ratio + float_info::min_k; + int offset = k - kb; + + // Get base cache. + uint128_fallback base_cache = pow10_significands[cache_index]; + if (offset == 0) return base_cache; + + // Compute the required amount of bit-shift. + int alpha = floor_log2_pow10(kb + offset) - floor_log2_pow10(kb) - offset; + FMT_ASSERT(alpha > 0 && alpha < 64, "shifting error detected"); + + // Try to recover the real cache. + uint64_t pow5 = powers_of_5_64[offset]; + uint128_fallback recovered_cache = umul128(base_cache.high(), pow5); + uint128_fallback middle_low = umul128(base_cache.low(), pow5); + + recovered_cache += middle_low.high(); + + uint64_t high_to_middle = recovered_cache.high() << (64 - alpha); + uint64_t middle_to_low = recovered_cache.low() << (64 - alpha); + + recovered_cache = + uint128_fallback{(recovered_cache.low() >> alpha) | high_to_middle, + ((middle_low.low() >> alpha) | middle_to_low)}; + FMT_ASSERT(recovered_cache.low() + 1 != 0, ""); + return {recovered_cache.high(), recovered_cache.low() + 1}; +#endif + } + + struct compute_mul_result { + carrier_uint result; + bool is_integer; + }; + struct compute_mul_parity_result { + bool parity; + bool is_integer; + }; + + static compute_mul_result compute_mul( + carrier_uint u, const cache_entry_type& cache) noexcept { + auto r = umul192_upper128(u, cache); + return {r.high(), r.low() == 0}; + } + + static uint32_t compute_delta(cache_entry_type const& cache, + int beta) noexcept { + return static_cast(cache.high() >> (64 - 1 - beta)); + } + + static compute_mul_parity_result compute_mul_parity( + carrier_uint two_f, const cache_entry_type& cache, int beta) noexcept { + FMT_ASSERT(beta >= 1, ""); + FMT_ASSERT(beta < 64, ""); + + auto r = umul192_lower128(two_f, cache); + return {((r.high() >> (64 - beta)) & 1) != 0, + ((r.high() << beta) | (r.low() >> (64 - beta))) == 0}; + } + + static carrier_uint compute_left_endpoint_for_shorter_interval_case( + const cache_entry_type& cache, int beta) noexcept { + return (cache.high() - + (cache.high() >> (num_significand_bits() + 2))) >> + (64 - num_significand_bits() - 1 - beta); + } + + static carrier_uint compute_right_endpoint_for_shorter_interval_case( + const cache_entry_type& cache, int beta) noexcept { + return (cache.high() + + (cache.high() >> (num_significand_bits() + 1))) >> + (64 - num_significand_bits() - 1 - beta); + } + + static carrier_uint compute_round_up_for_shorter_interval_case( + const cache_entry_type& cache, int beta) noexcept { + return ((cache.high() >> (64 - num_significand_bits() - 2 - beta)) + + 1) / + 2; + } +}; + +// Various integer checks +template +bool is_left_endpoint_integer_shorter_interval(int exponent) noexcept { + const int case_shorter_interval_left_endpoint_lower_threshold = 2; + const int case_shorter_interval_left_endpoint_upper_threshold = 3; + return exponent >= case_shorter_interval_left_endpoint_lower_threshold && + exponent <= case_shorter_interval_left_endpoint_upper_threshold; +} + +// Remove trailing zeros from n and return the number of zeros removed (float) +FMT_INLINE int remove_trailing_zeros(uint32_t& n) noexcept { + FMT_ASSERT(n != 0, ""); + const uint32_t mod_inv_5 = 0xcccccccd; + const uint32_t mod_inv_25 = mod_inv_5 * mod_inv_5; + + int s = 0; + while (true) { + auto q = rotr(n * mod_inv_25, 2); + if (q > max_value() / 100) break; + n = q; + s += 2; + } + auto q = rotr(n * mod_inv_5, 1); + if (q <= max_value() / 10) { + n = q; + s |= 1; + } + + return s; +} + +// Removes trailing zeros and returns the number of zeros removed (double) +FMT_INLINE int remove_trailing_zeros(uint64_t& n) noexcept { + FMT_ASSERT(n != 0, ""); + + // This magic number is ceil(2^90 / 10^8). + constexpr uint64_t magic_number = 12379400392853802749ull; + auto nm = umul128(n, magic_number); + + // Is n is divisible by 10^8? + if ((nm.high() & ((1ull << (90 - 64)) - 1)) == 0 && nm.low() < magic_number) { + // If yes, work with the quotient. + auto n32 = static_cast(nm.high() >> (90 - 64)); + + const uint32_t mod_inv_5 = 0xcccccccd; + const uint32_t mod_inv_25 = mod_inv_5 * mod_inv_5; + + int s = 8; + while (true) { + auto q = rotr(n32 * mod_inv_25, 2); + if (q > max_value() / 100) break; + n32 = q; + s += 2; + } + auto q = rotr(n32 * mod_inv_5, 1); + if (q <= max_value() / 10) { + n32 = q; + s |= 1; + } + + n = n32; + return s; + } + + // If n is not divisible by 10^8, work with n itself. + const uint64_t mod_inv_5 = 0xcccccccccccccccd; + const uint64_t mod_inv_25 = mod_inv_5 * mod_inv_5; + + int s = 0; + while (true) { + auto q = rotr(n * mod_inv_25, 2); + if (q > max_value() / 100) break; + n = q; + s += 2; + } + auto q = rotr(n * mod_inv_5, 1); + if (q <= max_value() / 10) { + n = q; + s |= 1; + } + + return s; +} + +// The main algorithm for shorter interval case +template +FMT_INLINE decimal_fp shorter_interval_case(int exponent) noexcept { + decimal_fp ret_value; + // Compute k and beta + const int minus_k = floor_log10_pow2_minus_log10_4_over_3(exponent); + const int beta = exponent + floor_log2_pow10(-minus_k); + + // Compute xi and zi + using cache_entry_type = typename cache_accessor::cache_entry_type; + const cache_entry_type cache = cache_accessor::get_cached_power(-minus_k); + + auto xi = cache_accessor::compute_left_endpoint_for_shorter_interval_case( + cache, beta); + auto zi = cache_accessor::compute_right_endpoint_for_shorter_interval_case( + cache, beta); + + // If the left endpoint is not an integer, increase it + if (!is_left_endpoint_integer_shorter_interval(exponent)) ++xi; + + // Try bigger divisor + ret_value.significand = zi / 10; + + // If succeed, remove trailing zeros if necessary and return + if (ret_value.significand * 10 >= xi) { + ret_value.exponent = minus_k + 1; + ret_value.exponent += remove_trailing_zeros(ret_value.significand); + return ret_value; + } + + // Otherwise, compute the round-up of y + ret_value.significand = + cache_accessor::compute_round_up_for_shorter_interval_case(cache, + beta); + ret_value.exponent = minus_k; + + // When tie occurs, choose one of them according to the rule + if (exponent >= float_info::shorter_interval_tie_lower_threshold && + exponent <= float_info::shorter_interval_tie_upper_threshold) { + ret_value.significand = ret_value.significand % 2 == 0 + ? ret_value.significand + : ret_value.significand - 1; + } else if (ret_value.significand < xi) { + ++ret_value.significand; + } + return ret_value; +} + +template decimal_fp to_decimal(T x) noexcept { + // Step 1: integer promotion & Schubfach multiplier calculation. + + using carrier_uint = typename float_info::carrier_uint; + using cache_entry_type = typename cache_accessor::cache_entry_type; + auto br = bit_cast(x); + + // Extract significand bits and exponent bits. + const carrier_uint significand_mask = + (static_cast(1) << num_significand_bits()) - 1; + carrier_uint significand = (br & significand_mask); + int exponent = + static_cast((br & exponent_mask()) >> num_significand_bits()); + + if (exponent != 0) { // Check if normal. + exponent -= exponent_bias() + num_significand_bits(); + + // Shorter interval case; proceed like Schubfach. + // In fact, when exponent == 1 and significand == 0, the interval is + // regular. However, it can be shown that the end-results are anyway same. + if (significand == 0) return shorter_interval_case(exponent); + + significand |= (static_cast(1) << num_significand_bits()); + } else { + // Subnormal case; the interval is always regular. + if (significand == 0) return {0, 0}; + exponent = + std::numeric_limits::min_exponent - num_significand_bits() - 1; + } + + const bool include_left_endpoint = (significand % 2 == 0); + const bool include_right_endpoint = include_left_endpoint; + + // Compute k and beta. + const int minus_k = floor_log10_pow2(exponent) - float_info::kappa; + const cache_entry_type cache = cache_accessor::get_cached_power(-minus_k); + const int beta = exponent + floor_log2_pow10(-minus_k); + + // Compute zi and deltai. + // 10^kappa <= deltai < 10^(kappa + 1) + const uint32_t deltai = cache_accessor::compute_delta(cache, beta); + const carrier_uint two_fc = significand << 1; + + // For the case of binary32, the result of integer check is not correct for + // 29711844 * 2^-82 + // = 6.1442653300000000008655037797566933477355632930994033813476... * 10^-18 + // and 29711844 * 2^-81 + // = 1.2288530660000000001731007559513386695471126586198806762695... * 10^-17, + // and they are the unique counterexamples. However, since 29711844 is even, + // this does not cause any problem for the endpoints calculations; it can only + // cause a problem when we need to perform integer check for the center. + // Fortunately, with these inputs, that branch is never executed, so we are + // fine. + const typename cache_accessor::compute_mul_result z_mul = + cache_accessor::compute_mul((two_fc | 1) << beta, cache); + + // Step 2: Try larger divisor; remove trailing zeros if necessary. + + // Using an upper bound on zi, we might be able to optimize the division + // better than the compiler; we are computing zi / big_divisor here. + decimal_fp ret_value; + ret_value.significand = divide_by_10_to_kappa_plus_1(z_mul.result); + uint32_t r = static_cast(z_mul.result - float_info::big_divisor * + ret_value.significand); + + if (r < deltai) { + // Exclude the right endpoint if necessary. + if (r == 0 && z_mul.is_integer && !include_right_endpoint) { + --ret_value.significand; + r = float_info::big_divisor; + goto small_divisor_case_label; + } + } else if (r > deltai) { + goto small_divisor_case_label; + } else { + // r == deltai; compare fractional parts. + const carrier_uint two_fl = two_fc - 1; + + if (!include_left_endpoint || + exponent < float_info::case_fc_pm_half_lower_threshold || + exponent > float_info::divisibility_check_by_5_threshold) { + // If the left endpoint is not included, the condition for + // success is z^(f) < delta^(f) (odd parity). + // Otherwise, the inequalities on exponent ensure that + // x is not an integer, so if z^(f) >= delta^(f) (even parity), we in fact + // have strict inequality. + if (!cache_accessor::compute_mul_parity(two_fl, cache, beta).parity) { + goto small_divisor_case_label; + } + } else { + const typename cache_accessor::compute_mul_parity_result x_mul = + cache_accessor::compute_mul_parity(two_fl, cache, beta); + if (!x_mul.parity && !x_mul.is_integer) { + goto small_divisor_case_label; + } + } + } + ret_value.exponent = minus_k + float_info::kappa + 1; + + // We may need to remove trailing zeros. + ret_value.exponent += remove_trailing_zeros(ret_value.significand); + return ret_value; + + // Step 3: Find the significand with the smaller divisor. + +small_divisor_case_label: + ret_value.significand *= 10; + ret_value.exponent = minus_k + float_info::kappa; + + uint32_t dist = r - (deltai / 2) + (float_info::small_divisor / 2); + const bool approx_y_parity = + ((dist ^ (float_info::small_divisor / 2)) & 1) != 0; + + // Is dist divisible by 10^kappa? + const bool divisible_by_small_divisor = + check_divisibility_and_divide_by_pow10::kappa>(dist); + + // Add dist / 10^kappa to the significand. + ret_value.significand += dist; + + if (!divisible_by_small_divisor) return ret_value; + + // Check z^(f) >= epsilon^(f). + // We have either yi == zi - epsiloni or yi == (zi - epsiloni) - 1, + // where yi == zi - epsiloni if and only if z^(f) >= epsilon^(f). + // Since there are only 2 possibilities, we only need to care about the + // parity. Also, zi and r should have the same parity since the divisor + // is an even number. + const auto y_mul = cache_accessor::compute_mul_parity(two_fc, cache, beta); + + // If z^(f) >= epsilon^(f), we might have a tie when z^(f) == epsilon^(f), + // or equivalently, when y is an integer. + if (y_mul.parity != approx_y_parity) + --ret_value.significand; + else if (y_mul.is_integer && ret_value.significand % 2 != 0) + --ret_value.significand; + return ret_value; +} +} // namespace dragonbox + +#ifdef _MSC_VER +FMT_FUNC auto fmt_snprintf(char* buf, size_t size, const char* fmt, ...) + -> int { + auto args = va_list(); + va_start(args, fmt); + int result = vsnprintf_s(buf, size, _TRUNCATE, fmt, args); + va_end(args); + return result; +} +#endif +} // namespace detail + +template <> struct formatter { + FMT_CONSTEXPR auto parse(format_parse_context& ctx) + -> format_parse_context::iterator { + return ctx.begin(); + } + + template + auto format(const detail::bigint& n, FormatContext& ctx) const -> + typename FormatContext::iterator { + auto out = ctx.out(); + bool first = true; + for (auto i = n.bigits_.size(); i > 0; --i) { + auto value = n.bigits_[i - 1u]; + if (first) { + out = format_to(out, FMT_STRING("{:x}"), value); + first = false; + continue; + } + out = format_to(out, FMT_STRING("{:08x}"), value); + } + if (n.exp_ > 0) + out = format_to(out, FMT_STRING("p{}"), + n.exp_ * detail::bigint::bigit_bits); + return out; + } +}; + +FMT_FUNC detail::utf8_to_utf16::utf8_to_utf16(string_view s) { + for_each_codepoint(s, [this](uint32_t cp, string_view) { + if (cp == invalid_code_point) FMT_THROW(std::runtime_error("invalid utf8")); + if (cp <= 0xFFFF) { + buffer_.push_back(static_cast(cp)); + } else { + cp -= 0x10000; + buffer_.push_back(static_cast(0xD800 + (cp >> 10))); + buffer_.push_back(static_cast(0xDC00 + (cp & 0x3FF))); + } + return true; + }); + buffer_.push_back(0); +} + +FMT_FUNC void format_system_error(detail::buffer& out, int error_code, + const char* message) noexcept { + FMT_TRY { + auto ec = std::error_code(error_code, std::generic_category()); + write(std::back_inserter(out), std::system_error(ec, message).what()); + return; + } + FMT_CATCH(...) {} + format_error_code(out, error_code, message); +} + +FMT_FUNC void report_system_error(int error_code, + const char* message) noexcept { + report_error(format_system_error, error_code, message); +} + +FMT_FUNC std::string vformat(string_view fmt, format_args args) { + // Don't optimize the "{}" case to keep the binary size small and because it + // can be better optimized in fmt::format anyway. + auto buffer = memory_buffer(); + detail::vformat_to(buffer, fmt, args); + return to_string(buffer); +} + +#ifdef _WIN32 +namespace detail { +using dword = conditional_t; +extern "C" __declspec(dllimport) int __stdcall WriteConsoleW( // + void*, const void*, dword, dword*, void*); +} // namespace detail +#endif + +namespace detail { +FMT_FUNC void print(std::FILE* f, string_view text) { +#ifdef _WIN32 + auto fd = _fileno(f); + if (_isatty(fd)) { + detail::utf8_to_utf16 u16(string_view(text.data(), text.size())); + auto written = detail::dword(); + if (detail::WriteConsoleW(reinterpret_cast(_get_osfhandle(fd)), + u16.c_str(), static_cast(u16.size()), + &written, nullptr)) { + return; + } + // Fallback to fwrite on failure. It can happen if the output has been + // redirected to NUL. + } +#endif + detail::fwrite_fully(text.data(), 1, text.size(), f); +} +} // namespace detail + +FMT_FUNC void vprint(std::FILE* f, string_view format_str, format_args args) { + memory_buffer buffer; + detail::vformat_to(buffer, format_str, args); + detail::print(f, {buffer.data(), buffer.size()}); +} + +#ifdef _WIN32 +// Print assuming legacy (non-Unicode) encoding. +FMT_FUNC void detail::vprint_mojibake(std::FILE* f, string_view format_str, + format_args args) { + memory_buffer buffer; + detail::vformat_to(buffer, format_str, + basic_format_args>(args)); + fwrite_fully(buffer.data(), 1, buffer.size(), f); +} +#endif + +FMT_FUNC void vprint(string_view format_str, format_args args) { + vprint(stdout, format_str, args); +} + +namespace detail { + +struct singleton { + unsigned char upper; + unsigned char lower_count; +}; + +inline auto is_printable(uint16_t x, const singleton* singletons, + size_t singletons_size, + const unsigned char* singleton_lowers, + const unsigned char* normal, size_t normal_size) + -> bool { + auto upper = x >> 8; + auto lower_start = 0; + for (size_t i = 0; i < singletons_size; ++i) { + auto s = singletons[i]; + auto lower_end = lower_start + s.lower_count; + if (upper < s.upper) break; + if (upper == s.upper) { + for (auto j = lower_start; j < lower_end; ++j) { + if (singleton_lowers[j] == (x & 0xff)) return false; + } + } + lower_start = lower_end; + } + + auto xsigned = static_cast(x); + auto current = true; + for (size_t i = 0; i < normal_size; ++i) { + auto v = static_cast(normal[i]); + auto len = (v & 0x80) != 0 ? (v & 0x7f) << 8 | normal[++i] : v; + xsigned -= len; + if (xsigned < 0) break; + current = !current; + } + return current; +} + +// This code is generated by support/printable.py. +FMT_FUNC auto is_printable(uint32_t cp) -> bool { + static constexpr singleton singletons0[] = { + {0x00, 1}, {0x03, 5}, {0x05, 6}, {0x06, 3}, {0x07, 6}, {0x08, 8}, + {0x09, 17}, {0x0a, 28}, {0x0b, 25}, {0x0c, 20}, {0x0d, 16}, {0x0e, 13}, + {0x0f, 4}, {0x10, 3}, {0x12, 18}, {0x13, 9}, {0x16, 1}, {0x17, 5}, + {0x18, 2}, {0x19, 3}, {0x1a, 7}, {0x1c, 2}, {0x1d, 1}, {0x1f, 22}, + {0x20, 3}, {0x2b, 3}, {0x2c, 2}, {0x2d, 11}, {0x2e, 1}, {0x30, 3}, + {0x31, 2}, {0x32, 1}, {0xa7, 2}, {0xa9, 2}, {0xaa, 4}, {0xab, 8}, + {0xfa, 2}, {0xfb, 5}, {0xfd, 4}, {0xfe, 3}, {0xff, 9}, + }; + static constexpr unsigned char singletons0_lower[] = { + 0xad, 0x78, 0x79, 0x8b, 0x8d, 0xa2, 0x30, 0x57, 0x58, 0x8b, 0x8c, 0x90, + 0x1c, 0x1d, 0xdd, 0x0e, 0x0f, 0x4b, 0x4c, 0xfb, 0xfc, 0x2e, 0x2f, 0x3f, + 0x5c, 0x5d, 0x5f, 0xb5, 0xe2, 0x84, 0x8d, 0x8e, 0x91, 0x92, 0xa9, 0xb1, + 0xba, 0xbb, 0xc5, 0xc6, 0xc9, 0xca, 0xde, 0xe4, 0xe5, 0xff, 0x00, 0x04, + 0x11, 0x12, 0x29, 0x31, 0x34, 0x37, 0x3a, 0x3b, 0x3d, 0x49, 0x4a, 0x5d, + 0x84, 0x8e, 0x92, 0xa9, 0xb1, 0xb4, 0xba, 0xbb, 0xc6, 0xca, 0xce, 0xcf, + 0xe4, 0xe5, 0x00, 0x04, 0x0d, 0x0e, 0x11, 0x12, 0x29, 0x31, 0x34, 0x3a, + 0x3b, 0x45, 0x46, 0x49, 0x4a, 0x5e, 0x64, 0x65, 0x84, 0x91, 0x9b, 0x9d, + 0xc9, 0xce, 0xcf, 0x0d, 0x11, 0x29, 0x45, 0x49, 0x57, 0x64, 0x65, 0x8d, + 0x91, 0xa9, 0xb4, 0xba, 0xbb, 0xc5, 0xc9, 0xdf, 0xe4, 0xe5, 0xf0, 0x0d, + 0x11, 0x45, 0x49, 0x64, 0x65, 0x80, 0x84, 0xb2, 0xbc, 0xbe, 0xbf, 0xd5, + 0xd7, 0xf0, 0xf1, 0x83, 0x85, 0x8b, 0xa4, 0xa6, 0xbe, 0xbf, 0xc5, 0xc7, + 0xce, 0xcf, 0xda, 0xdb, 0x48, 0x98, 0xbd, 0xcd, 0xc6, 0xce, 0xcf, 0x49, + 0x4e, 0x4f, 0x57, 0x59, 0x5e, 0x5f, 0x89, 0x8e, 0x8f, 0xb1, 0xb6, 0xb7, + 0xbf, 0xc1, 0xc6, 0xc7, 0xd7, 0x11, 0x16, 0x17, 0x5b, 0x5c, 0xf6, 0xf7, + 0xfe, 0xff, 0x80, 0x0d, 0x6d, 0x71, 0xde, 0xdf, 0x0e, 0x0f, 0x1f, 0x6e, + 0x6f, 0x1c, 0x1d, 0x5f, 0x7d, 0x7e, 0xae, 0xaf, 0xbb, 0xbc, 0xfa, 0x16, + 0x17, 0x1e, 0x1f, 0x46, 0x47, 0x4e, 0x4f, 0x58, 0x5a, 0x5c, 0x5e, 0x7e, + 0x7f, 0xb5, 0xc5, 0xd4, 0xd5, 0xdc, 0xf0, 0xf1, 0xf5, 0x72, 0x73, 0x8f, + 0x74, 0x75, 0x96, 0x2f, 0x5f, 0x26, 0x2e, 0x2f, 0xa7, 0xaf, 0xb7, 0xbf, + 0xc7, 0xcf, 0xd7, 0xdf, 0x9a, 0x40, 0x97, 0x98, 0x30, 0x8f, 0x1f, 0xc0, + 0xc1, 0xce, 0xff, 0x4e, 0x4f, 0x5a, 0x5b, 0x07, 0x08, 0x0f, 0x10, 0x27, + 0x2f, 0xee, 0xef, 0x6e, 0x6f, 0x37, 0x3d, 0x3f, 0x42, 0x45, 0x90, 0x91, + 0xfe, 0xff, 0x53, 0x67, 0x75, 0xc8, 0xc9, 0xd0, 0xd1, 0xd8, 0xd9, 0xe7, + 0xfe, 0xff, + }; + static constexpr singleton singletons1[] = { + {0x00, 6}, {0x01, 1}, {0x03, 1}, {0x04, 2}, {0x08, 8}, {0x09, 2}, + {0x0a, 5}, {0x0b, 2}, {0x0e, 4}, {0x10, 1}, {0x11, 2}, {0x12, 5}, + {0x13, 17}, {0x14, 1}, {0x15, 2}, {0x17, 2}, {0x19, 13}, {0x1c, 5}, + {0x1d, 8}, {0x24, 1}, {0x6a, 3}, {0x6b, 2}, {0xbc, 2}, {0xd1, 2}, + {0xd4, 12}, {0xd5, 9}, {0xd6, 2}, {0xd7, 2}, {0xda, 1}, {0xe0, 5}, + {0xe1, 2}, {0xe8, 2}, {0xee, 32}, {0xf0, 4}, {0xf8, 2}, {0xf9, 2}, + {0xfa, 2}, {0xfb, 1}, + }; + static constexpr unsigned char singletons1_lower[] = { + 0x0c, 0x27, 0x3b, 0x3e, 0x4e, 0x4f, 0x8f, 0x9e, 0x9e, 0x9f, 0x06, 0x07, + 0x09, 0x36, 0x3d, 0x3e, 0x56, 0xf3, 0xd0, 0xd1, 0x04, 0x14, 0x18, 0x36, + 0x37, 0x56, 0x57, 0x7f, 0xaa, 0xae, 0xaf, 0xbd, 0x35, 0xe0, 0x12, 0x87, + 0x89, 0x8e, 0x9e, 0x04, 0x0d, 0x0e, 0x11, 0x12, 0x29, 0x31, 0x34, 0x3a, + 0x45, 0x46, 0x49, 0x4a, 0x4e, 0x4f, 0x64, 0x65, 0x5c, 0xb6, 0xb7, 0x1b, + 0x1c, 0x07, 0x08, 0x0a, 0x0b, 0x14, 0x17, 0x36, 0x39, 0x3a, 0xa8, 0xa9, + 0xd8, 0xd9, 0x09, 0x37, 0x90, 0x91, 0xa8, 0x07, 0x0a, 0x3b, 0x3e, 0x66, + 0x69, 0x8f, 0x92, 0x6f, 0x5f, 0xee, 0xef, 0x5a, 0x62, 0x9a, 0x9b, 0x27, + 0x28, 0x55, 0x9d, 0xa0, 0xa1, 0xa3, 0xa4, 0xa7, 0xa8, 0xad, 0xba, 0xbc, + 0xc4, 0x06, 0x0b, 0x0c, 0x15, 0x1d, 0x3a, 0x3f, 0x45, 0x51, 0xa6, 0xa7, + 0xcc, 0xcd, 0xa0, 0x07, 0x19, 0x1a, 0x22, 0x25, 0x3e, 0x3f, 0xc5, 0xc6, + 0x04, 0x20, 0x23, 0x25, 0x26, 0x28, 0x33, 0x38, 0x3a, 0x48, 0x4a, 0x4c, + 0x50, 0x53, 0x55, 0x56, 0x58, 0x5a, 0x5c, 0x5e, 0x60, 0x63, 0x65, 0x66, + 0x6b, 0x73, 0x78, 0x7d, 0x7f, 0x8a, 0xa4, 0xaa, 0xaf, 0xb0, 0xc0, 0xd0, + 0xae, 0xaf, 0x79, 0xcc, 0x6e, 0x6f, 0x93, + }; + static constexpr unsigned char normal0[] = { + 0x00, 0x20, 0x5f, 0x22, 0x82, 0xdf, 0x04, 0x82, 0x44, 0x08, 0x1b, 0x04, + 0x06, 0x11, 0x81, 0xac, 0x0e, 0x80, 0xab, 0x35, 0x28, 0x0b, 0x80, 0xe0, + 0x03, 0x19, 0x08, 0x01, 0x04, 0x2f, 0x04, 0x34, 0x04, 0x07, 0x03, 0x01, + 0x07, 0x06, 0x07, 0x11, 0x0a, 0x50, 0x0f, 0x12, 0x07, 0x55, 0x07, 0x03, + 0x04, 0x1c, 0x0a, 0x09, 0x03, 0x08, 0x03, 0x07, 0x03, 0x02, 0x03, 0x03, + 0x03, 0x0c, 0x04, 0x05, 0x03, 0x0b, 0x06, 0x01, 0x0e, 0x15, 0x05, 0x3a, + 0x03, 0x11, 0x07, 0x06, 0x05, 0x10, 0x07, 0x57, 0x07, 0x02, 0x07, 0x15, + 0x0d, 0x50, 0x04, 0x43, 0x03, 0x2d, 0x03, 0x01, 0x04, 0x11, 0x06, 0x0f, + 0x0c, 0x3a, 0x04, 0x1d, 0x25, 0x5f, 0x20, 0x6d, 0x04, 0x6a, 0x25, 0x80, + 0xc8, 0x05, 0x82, 0xb0, 0x03, 0x1a, 0x06, 0x82, 0xfd, 0x03, 0x59, 0x07, + 0x15, 0x0b, 0x17, 0x09, 0x14, 0x0c, 0x14, 0x0c, 0x6a, 0x06, 0x0a, 0x06, + 0x1a, 0x06, 0x59, 0x07, 0x2b, 0x05, 0x46, 0x0a, 0x2c, 0x04, 0x0c, 0x04, + 0x01, 0x03, 0x31, 0x0b, 0x2c, 0x04, 0x1a, 0x06, 0x0b, 0x03, 0x80, 0xac, + 0x06, 0x0a, 0x06, 0x21, 0x3f, 0x4c, 0x04, 0x2d, 0x03, 0x74, 0x08, 0x3c, + 0x03, 0x0f, 0x03, 0x3c, 0x07, 0x38, 0x08, 0x2b, 0x05, 0x82, 0xff, 0x11, + 0x18, 0x08, 0x2f, 0x11, 0x2d, 0x03, 0x20, 0x10, 0x21, 0x0f, 0x80, 0x8c, + 0x04, 0x82, 0x97, 0x19, 0x0b, 0x15, 0x88, 0x94, 0x05, 0x2f, 0x05, 0x3b, + 0x07, 0x02, 0x0e, 0x18, 0x09, 0x80, 0xb3, 0x2d, 0x74, 0x0c, 0x80, 0xd6, + 0x1a, 0x0c, 0x05, 0x80, 0xff, 0x05, 0x80, 0xdf, 0x0c, 0xee, 0x0d, 0x03, + 0x84, 0x8d, 0x03, 0x37, 0x09, 0x81, 0x5c, 0x14, 0x80, 0xb8, 0x08, 0x80, + 0xcb, 0x2a, 0x38, 0x03, 0x0a, 0x06, 0x38, 0x08, 0x46, 0x08, 0x0c, 0x06, + 0x74, 0x0b, 0x1e, 0x03, 0x5a, 0x04, 0x59, 0x09, 0x80, 0x83, 0x18, 0x1c, + 0x0a, 0x16, 0x09, 0x4c, 0x04, 0x80, 0x8a, 0x06, 0xab, 0xa4, 0x0c, 0x17, + 0x04, 0x31, 0xa1, 0x04, 0x81, 0xda, 0x26, 0x07, 0x0c, 0x05, 0x05, 0x80, + 0xa5, 0x11, 0x81, 0x6d, 0x10, 0x78, 0x28, 0x2a, 0x06, 0x4c, 0x04, 0x80, + 0x8d, 0x04, 0x80, 0xbe, 0x03, 0x1b, 0x03, 0x0f, 0x0d, + }; + static constexpr unsigned char normal1[] = { + 0x5e, 0x22, 0x7b, 0x05, 0x03, 0x04, 0x2d, 0x03, 0x66, 0x03, 0x01, 0x2f, + 0x2e, 0x80, 0x82, 0x1d, 0x03, 0x31, 0x0f, 0x1c, 0x04, 0x24, 0x09, 0x1e, + 0x05, 0x2b, 0x05, 0x44, 0x04, 0x0e, 0x2a, 0x80, 0xaa, 0x06, 0x24, 0x04, + 0x24, 0x04, 0x28, 0x08, 0x34, 0x0b, 0x01, 0x80, 0x90, 0x81, 0x37, 0x09, + 0x16, 0x0a, 0x08, 0x80, 0x98, 0x39, 0x03, 0x63, 0x08, 0x09, 0x30, 0x16, + 0x05, 0x21, 0x03, 0x1b, 0x05, 0x01, 0x40, 0x38, 0x04, 0x4b, 0x05, 0x2f, + 0x04, 0x0a, 0x07, 0x09, 0x07, 0x40, 0x20, 0x27, 0x04, 0x0c, 0x09, 0x36, + 0x03, 0x3a, 0x05, 0x1a, 0x07, 0x04, 0x0c, 0x07, 0x50, 0x49, 0x37, 0x33, + 0x0d, 0x33, 0x07, 0x2e, 0x08, 0x0a, 0x81, 0x26, 0x52, 0x4e, 0x28, 0x08, + 0x2a, 0x56, 0x1c, 0x14, 0x17, 0x09, 0x4e, 0x04, 0x1e, 0x0f, 0x43, 0x0e, + 0x19, 0x07, 0x0a, 0x06, 0x48, 0x08, 0x27, 0x09, 0x75, 0x0b, 0x3f, 0x41, + 0x2a, 0x06, 0x3b, 0x05, 0x0a, 0x06, 0x51, 0x06, 0x01, 0x05, 0x10, 0x03, + 0x05, 0x80, 0x8b, 0x62, 0x1e, 0x48, 0x08, 0x0a, 0x80, 0xa6, 0x5e, 0x22, + 0x45, 0x0b, 0x0a, 0x06, 0x0d, 0x13, 0x39, 0x07, 0x0a, 0x36, 0x2c, 0x04, + 0x10, 0x80, 0xc0, 0x3c, 0x64, 0x53, 0x0c, 0x48, 0x09, 0x0a, 0x46, 0x45, + 0x1b, 0x48, 0x08, 0x53, 0x1d, 0x39, 0x81, 0x07, 0x46, 0x0a, 0x1d, 0x03, + 0x47, 0x49, 0x37, 0x03, 0x0e, 0x08, 0x0a, 0x06, 0x39, 0x07, 0x0a, 0x81, + 0x36, 0x19, 0x80, 0xb7, 0x01, 0x0f, 0x32, 0x0d, 0x83, 0x9b, 0x66, 0x75, + 0x0b, 0x80, 0xc4, 0x8a, 0xbc, 0x84, 0x2f, 0x8f, 0xd1, 0x82, 0x47, 0xa1, + 0xb9, 0x82, 0x39, 0x07, 0x2a, 0x04, 0x02, 0x60, 0x26, 0x0a, 0x46, 0x0a, + 0x28, 0x05, 0x13, 0x82, 0xb0, 0x5b, 0x65, 0x4b, 0x04, 0x39, 0x07, 0x11, + 0x40, 0x05, 0x0b, 0x02, 0x0e, 0x97, 0xf8, 0x08, 0x84, 0xd6, 0x2a, 0x09, + 0xa2, 0xf7, 0x81, 0x1f, 0x31, 0x03, 0x11, 0x04, 0x08, 0x81, 0x8c, 0x89, + 0x04, 0x6b, 0x05, 0x0d, 0x03, 0x09, 0x07, 0x10, 0x93, 0x60, 0x80, 0xf6, + 0x0a, 0x73, 0x08, 0x6e, 0x17, 0x46, 0x80, 0x9a, 0x14, 0x0c, 0x57, 0x09, + 0x19, 0x80, 0x87, 0x81, 0x47, 0x03, 0x85, 0x42, 0x0f, 0x15, 0x85, 0x50, + 0x2b, 0x80, 0xd5, 0x2d, 0x03, 0x1a, 0x04, 0x02, 0x81, 0x70, 0x3a, 0x05, + 0x01, 0x85, 0x00, 0x80, 0xd7, 0x29, 0x4c, 0x04, 0x0a, 0x04, 0x02, 0x83, + 0x11, 0x44, 0x4c, 0x3d, 0x80, 0xc2, 0x3c, 0x06, 0x01, 0x04, 0x55, 0x05, + 0x1b, 0x34, 0x02, 0x81, 0x0e, 0x2c, 0x04, 0x64, 0x0c, 0x56, 0x0a, 0x80, + 0xae, 0x38, 0x1d, 0x0d, 0x2c, 0x04, 0x09, 0x07, 0x02, 0x0e, 0x06, 0x80, + 0x9a, 0x83, 0xd8, 0x08, 0x0d, 0x03, 0x0d, 0x03, 0x74, 0x0c, 0x59, 0x07, + 0x0c, 0x14, 0x0c, 0x04, 0x38, 0x08, 0x0a, 0x06, 0x28, 0x08, 0x22, 0x4e, + 0x81, 0x54, 0x0c, 0x15, 0x03, 0x03, 0x05, 0x07, 0x09, 0x19, 0x07, 0x07, + 0x09, 0x03, 0x0d, 0x07, 0x29, 0x80, 0xcb, 0x25, 0x0a, 0x84, 0x06, + }; + auto lower = static_cast(cp); + if (cp < 0x10000) { + return is_printable(lower, singletons0, + sizeof(singletons0) / sizeof(*singletons0), + singletons0_lower, normal0, sizeof(normal0)); + } + if (cp < 0x20000) { + return is_printable(lower, singletons1, + sizeof(singletons1) / sizeof(*singletons1), + singletons1_lower, normal1, sizeof(normal1)); + } + if (0x2a6de <= cp && cp < 0x2a700) return false; + if (0x2b735 <= cp && cp < 0x2b740) return false; + if (0x2b81e <= cp && cp < 0x2b820) return false; + if (0x2cea2 <= cp && cp < 0x2ceb0) return false; + if (0x2ebe1 <= cp && cp < 0x2f800) return false; + if (0x2fa1e <= cp && cp < 0x30000) return false; + if (0x3134b <= cp && cp < 0xe0100) return false; + if (0xe01f0 <= cp && cp < 0x110000) return false; + return cp < 0x110000; +} + +} // namespace detail + +FMT_END_NAMESPACE + +#endif // FMT_FORMAT_INL_H_ diff --git a/src/fmtlib/fmt/format.h b/src/fmtlib/fmt/format.h new file mode 100644 index 0000000..0bd2fdb --- /dev/null +++ b/src/fmtlib/fmt/format.h @@ -0,0 +1,4192 @@ +/* + Formatting library for C++ + + Copyright (c) 2012 - present, Victor Zverovich + + 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. + + --- Optional exception to the license --- + + As an exception, if, as a result of your compiling your source code, portions + of this Software are embedded into a machine-executable object form of such + source code, you may redistribute such embedded portions in such object form + without including the above copyright and permission notices. + */ + +#ifndef FMT_FORMAT_H_ +#define FMT_FORMAT_H_ + +#include // std::signbit +#include // uint32_t +#include // std::memcpy +#include // std::numeric_limits +#include // std::uninitialized_copy +#include // std::runtime_error +#include // std::system_error + +#ifdef __cpp_lib_bit_cast +# include // std::bitcast +#endif + +#include "core.h" + +#if FMT_GCC_VERSION +# define FMT_GCC_VISIBILITY_HIDDEN __attribute__((visibility("hidden"))) +#else +# define FMT_GCC_VISIBILITY_HIDDEN +#endif + +#ifdef __NVCC__ +# define FMT_CUDA_VERSION (__CUDACC_VER_MAJOR__ * 100 + __CUDACC_VER_MINOR__) +#else +# define FMT_CUDA_VERSION 0 +#endif + +#ifdef __has_builtin +# define FMT_HAS_BUILTIN(x) __has_builtin(x) +#else +# define FMT_HAS_BUILTIN(x) 0 +#endif + +#if FMT_GCC_VERSION || FMT_CLANG_VERSION +# define FMT_NOINLINE __attribute__((noinline)) +#else +# define FMT_NOINLINE +#endif + +#if FMT_MSC_VERSION +# define FMT_MSC_DEFAULT = default +#else +# define FMT_MSC_DEFAULT +#endif + +#ifndef FMT_THROW +# if FMT_EXCEPTIONS +# if FMT_MSC_VERSION || defined(__NVCC__) +FMT_BEGIN_NAMESPACE +namespace detail { +template inline void do_throw(const Exception& x) { + // Silence unreachable code warnings in MSVC and NVCC because these + // are nearly impossible to fix in a generic code. + volatile bool b = true; + if (b) throw x; +} +} // namespace detail +FMT_END_NAMESPACE +# define FMT_THROW(x) detail::do_throw(x) +# else +# define FMT_THROW(x) throw x +# endif +# else +# define FMT_THROW(x) \ + do { \ + FMT_ASSERT(false, (x).what()); \ + } while (false) +# endif +#endif + +#if FMT_EXCEPTIONS +# define FMT_TRY try +# define FMT_CATCH(x) catch (x) +#else +# define FMT_TRY if (true) +# define FMT_CATCH(x) if (false) +#endif + +#ifndef FMT_MAYBE_UNUSED +# if FMT_HAS_CPP17_ATTRIBUTE(maybe_unused) +# define FMT_MAYBE_UNUSED [[maybe_unused]] +# else +# define FMT_MAYBE_UNUSED +# endif +#endif + +#ifndef FMT_USE_USER_DEFINED_LITERALS +// EDG based compilers (Intel, NVIDIA, Elbrus, etc), GCC and MSVC support UDLs. +# if (FMT_HAS_FEATURE(cxx_user_literals) || FMT_GCC_VERSION >= 407 || \ + FMT_MSC_VERSION >= 1900) && \ + (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= /* UDL feature */ 480) +# define FMT_USE_USER_DEFINED_LITERALS 1 +# else +# define FMT_USE_USER_DEFINED_LITERALS 0 +# endif +#endif + +// Defining FMT_REDUCE_INT_INSTANTIATIONS to 1, will reduce the number of +// integer formatter template instantiations to just one by only using the +// largest integer type. This results in a reduction in binary size but will +// cause a decrease in integer formatting performance. +#if !defined(FMT_REDUCE_INT_INSTANTIATIONS) +# define FMT_REDUCE_INT_INSTANTIATIONS 0 +#endif + +// __builtin_clz is broken in clang with Microsoft CodeGen: +// https://github.com/fmtlib/fmt/issues/519. +#if !FMT_MSC_VERSION +# if FMT_HAS_BUILTIN(__builtin_clz) || FMT_GCC_VERSION || FMT_ICC_VERSION +# define FMT_BUILTIN_CLZ(n) __builtin_clz(n) +# endif +# if FMT_HAS_BUILTIN(__builtin_clzll) || FMT_GCC_VERSION || FMT_ICC_VERSION +# define FMT_BUILTIN_CLZLL(n) __builtin_clzll(n) +# endif +#endif + +// __builtin_ctz is broken in Intel Compiler Classic on Windows: +// https://github.com/fmtlib/fmt/issues/2510. +#ifndef __ICL +# if FMT_HAS_BUILTIN(__builtin_ctz) || FMT_GCC_VERSION || FMT_ICC_VERSION || \ + defined(__NVCOMPILER) +# define FMT_BUILTIN_CTZ(n) __builtin_ctz(n) +# endif +# if FMT_HAS_BUILTIN(__builtin_ctzll) || FMT_GCC_VERSION || \ + FMT_ICC_VERSION || defined(__NVCOMPILER) +# define FMT_BUILTIN_CTZLL(n) __builtin_ctzll(n) +# endif +#endif + +#if FMT_MSC_VERSION +# include // _BitScanReverse[64], _BitScanForward[64], _umul128 +#endif + +// Some compilers masquerade as both MSVC and GCC-likes or otherwise support +// __builtin_clz and __builtin_clzll, so only define FMT_BUILTIN_CLZ using the +// MSVC intrinsics if the clz and clzll builtins are not available. +#if FMT_MSC_VERSION && !defined(FMT_BUILTIN_CLZLL) && \ + !defined(FMT_BUILTIN_CTZLL) +FMT_BEGIN_NAMESPACE +namespace detail { +// Avoid Clang with Microsoft CodeGen's -Wunknown-pragmas warning. +# if !defined(__clang__) +# pragma intrinsic(_BitScanForward) +# pragma intrinsic(_BitScanReverse) +# if defined(_WIN64) +# pragma intrinsic(_BitScanForward64) +# pragma intrinsic(_BitScanReverse64) +# endif +# endif + +inline auto clz(uint32_t x) -> int { + unsigned long r = 0; + _BitScanReverse(&r, x); + FMT_ASSERT(x != 0, ""); + // Static analysis complains about using uninitialized data + // "r", but the only way that can happen is if "x" is 0, + // which the callers guarantee to not happen. + FMT_MSC_WARNING(suppress : 6102) + return 31 ^ static_cast(r); +} +# define FMT_BUILTIN_CLZ(n) detail::clz(n) + +inline auto clzll(uint64_t x) -> int { + unsigned long r = 0; +# ifdef _WIN64 + _BitScanReverse64(&r, x); +# else + // Scan the high 32 bits. + if (_BitScanReverse(&r, static_cast(x >> 32))) return 63 ^ (r + 32); + // Scan the low 32 bits. + _BitScanReverse(&r, static_cast(x)); +# endif + FMT_ASSERT(x != 0, ""); + FMT_MSC_WARNING(suppress : 6102) // Suppress a bogus static analysis warning. + return 63 ^ static_cast(r); +} +# define FMT_BUILTIN_CLZLL(n) detail::clzll(n) + +inline auto ctz(uint32_t x) -> int { + unsigned long r = 0; + _BitScanForward(&r, x); + FMT_ASSERT(x != 0, ""); + FMT_MSC_WARNING(suppress : 6102) // Suppress a bogus static analysis warning. + return static_cast(r); +} +# define FMT_BUILTIN_CTZ(n) detail::ctz(n) + +inline auto ctzll(uint64_t x) -> int { + unsigned long r = 0; + FMT_ASSERT(x != 0, ""); + FMT_MSC_WARNING(suppress : 6102) // Suppress a bogus static analysis warning. +# ifdef _WIN64 + _BitScanForward64(&r, x); +# else + // Scan the low 32 bits. + if (_BitScanForward(&r, static_cast(x))) return static_cast(r); + // Scan the high 32 bits. + _BitScanForward(&r, static_cast(x >> 32)); + r += 32; +# endif + return static_cast(r); +} +# define FMT_BUILTIN_CTZLL(n) detail::ctzll(n) +} // namespace detail +FMT_END_NAMESPACE +#endif + +FMT_BEGIN_NAMESPACE +namespace detail { + +FMT_CONSTEXPR inline void abort_fuzzing_if(bool condition) { + ignore_unused(condition); +#ifdef FMT_FUZZ + if (condition) throw std::runtime_error("fuzzing limit reached"); +#endif +} + +template class formatbuf : public Streambuf { + private: + using char_type = typename Streambuf::char_type; + using streamsize = decltype(std::declval().sputn(nullptr, 0)); + using int_type = typename Streambuf::int_type; + using traits_type = typename Streambuf::traits_type; + + buffer& buffer_; + + public: + explicit formatbuf(buffer& buf) : buffer_(buf) {} + + protected: + // The put area is always empty. This makes the implementation simpler and has + // the advantage that the streambuf and the buffer are always in sync and + // sputc never writes into uninitialized memory. A disadvantage is that each + // call to sputc always results in a (virtual) call to overflow. There is no + // disadvantage here for sputn since this always results in a call to xsputn. + + auto overflow(int_type ch) -> int_type override { + if (!traits_type::eq_int_type(ch, traits_type::eof())) + buffer_.push_back(static_cast(ch)); + return ch; + } + + auto xsputn(const char_type* s, streamsize count) -> streamsize override { + buffer_.append(s, s + count); + return count; + } +}; + +// Implementation of std::bit_cast for pre-C++20. +template +FMT_CONSTEXPR20 auto bit_cast(const From& from) -> To { +#ifdef __cpp_lib_bit_cast + if (is_constant_evaluated()) return std::bit_cast(from); +#endif + auto to = To(); + std::memcpy(&to, &from, sizeof(to)); + return to; +} + +inline auto is_big_endian() -> bool { +#ifdef _WIN32 + return false; +#elif defined(__BIG_ENDIAN__) + return true; +#elif defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) + return __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__; +#else + struct bytes { + char data[sizeof(int)]; + }; + return bit_cast(1).data[0] == 0; +#endif +} + +class uint128_fallback { + private: + uint64_t lo_, hi_; + + friend uint128_fallback umul128(uint64_t x, uint64_t y) noexcept; + + public: + constexpr uint128_fallback(uint64_t hi, uint64_t lo) : lo_(lo), hi_(hi) {} + constexpr uint128_fallback(uint64_t value = 0) : lo_(value), hi_(0) {} + + constexpr uint64_t high() const noexcept { return hi_; } + constexpr uint64_t low() const noexcept { return lo_; } + + template ::value)> + constexpr explicit operator T() const { + return static_cast(lo_); + } + + friend constexpr auto operator==(const uint128_fallback& lhs, + const uint128_fallback& rhs) -> bool { + return lhs.hi_ == rhs.hi_ && lhs.lo_ == rhs.lo_; + } + friend constexpr auto operator!=(const uint128_fallback& lhs, + const uint128_fallback& rhs) -> bool { + return !(lhs == rhs); + } + friend constexpr auto operator>(const uint128_fallback& lhs, + const uint128_fallback& rhs) -> bool { + return lhs.hi_ != rhs.hi_ ? lhs.hi_ > rhs.hi_ : lhs.lo_ > rhs.lo_; + } + friend constexpr auto operator|(const uint128_fallback& lhs, + const uint128_fallback& rhs) + -> uint128_fallback { + return {lhs.hi_ | rhs.hi_, lhs.lo_ | rhs.lo_}; + } + friend constexpr auto operator&(const uint128_fallback& lhs, + const uint128_fallback& rhs) + -> uint128_fallback { + return {lhs.hi_ & rhs.hi_, lhs.lo_ & rhs.lo_}; + } + friend auto operator+(const uint128_fallback& lhs, + const uint128_fallback& rhs) -> uint128_fallback { + auto result = uint128_fallback(lhs); + result += rhs; + return result; + } + friend auto operator*(const uint128_fallback& lhs, uint32_t rhs) + -> uint128_fallback { + FMT_ASSERT(lhs.hi_ == 0, ""); + uint64_t hi = (lhs.lo_ >> 32) * rhs; + uint64_t lo = (lhs.lo_ & ~uint32_t()) * rhs; + uint64_t new_lo = (hi << 32) + lo; + return {(hi >> 32) + (new_lo < lo ? 1 : 0), new_lo}; + } + friend auto operator-(const uint128_fallback& lhs, uint64_t rhs) + -> uint128_fallback { + return {lhs.hi_ - (lhs.lo_ < rhs ? 1 : 0), lhs.lo_ - rhs}; + } + FMT_CONSTEXPR auto operator>>(int shift) const -> uint128_fallback { + if (shift == 64) return {0, hi_}; + return {hi_ >> shift, (hi_ << (64 - shift)) | (lo_ >> shift)}; + } + FMT_CONSTEXPR auto operator<<(int shift) const -> uint128_fallback { + if (shift == 64) return {lo_, 0}; + return {hi_ << shift | (lo_ >> (64 - shift)), (lo_ << shift)}; + } + FMT_CONSTEXPR auto operator>>=(int shift) -> uint128_fallback& { + return *this = *this >> shift; + } + FMT_CONSTEXPR void operator+=(uint128_fallback n) { + uint64_t new_lo = lo_ + n.lo_; + uint64_t new_hi = hi_ + n.hi_ + (new_lo < lo_ ? 1 : 0); + FMT_ASSERT(new_hi >= hi_, ""); + lo_ = new_lo; + hi_ = new_hi; + } + + FMT_CONSTEXPR20 uint128_fallback& operator+=(uint64_t n) noexcept { + if (is_constant_evaluated()) { + lo_ += n; + hi_ += (lo_ < n ? 1 : 0); + return *this; + } +#if FMT_HAS_BUILTIN(__builtin_addcll) + unsigned long long carry; + lo_ = __builtin_addcll(lo_, n, 0, &carry); + hi_ += carry; +#elif FMT_HAS_BUILTIN(__builtin_ia32_addcarryx_u64) + unsigned long long result; + auto carry = __builtin_ia32_addcarryx_u64(0, lo_, n, &result); + lo_ = result; + hi_ += carry; +#elif defined(_MSC_VER) && defined(_M_X64) + auto carry = _addcarry_u64(0, lo_, n, &lo_); + _addcarry_u64(carry, hi_, 0, &hi_); +#else + lo_ += n; + hi_ += (lo_ < n ? 1 : 0); +#endif + return *this; + } +}; + +using uint128_t = conditional_t; + +#ifdef UINTPTR_MAX +using uintptr_t = ::uintptr_t; +#else +using uintptr_t = uint128_t; +#endif + +// Returns the largest possible value for type T. Same as +// std::numeric_limits::max() but shorter and not affected by the max macro. +template constexpr auto max_value() -> T { + return (std::numeric_limits::max)(); +} +template constexpr auto num_bits() -> int { + return std::numeric_limits::digits; +} +// std::numeric_limits::digits may return 0 for 128-bit ints. +template <> constexpr auto num_bits() -> int { return 128; } +template <> constexpr auto num_bits() -> int { return 128; } + +// A heterogeneous bit_cast used for converting 96-bit long double to uint128_t +// and 128-bit pointers to uint128_fallback. +template sizeof(From))> +inline auto bit_cast(const From& from) -> To { + constexpr auto size = static_cast(sizeof(From) / sizeof(unsigned)); + struct data_t { + unsigned value[static_cast(size)]; + } data = bit_cast(from); + auto result = To(); + if (const_check(is_big_endian())) { + for (int i = 0; i < size; ++i) + result = (result << num_bits()) | data.value[i]; + } else { + for (int i = size - 1; i >= 0; --i) + result = (result << num_bits()) | data.value[i]; + } + return result; +} + +FMT_INLINE void assume(bool condition) { + (void)condition; +#if FMT_HAS_BUILTIN(__builtin_assume) && !FMT_ICC_VERSION + __builtin_assume(condition); +#endif +} + +// An approximation of iterator_t for pre-C++20 systems. +template +using iterator_t = decltype(std::begin(std::declval())); +template using sentinel_t = decltype(std::end(std::declval())); + +// A workaround for std::string not having mutable data() until C++17. +template +inline auto get_data(std::basic_string& s) -> Char* { + return &s[0]; +} +template +inline auto get_data(Container& c) -> typename Container::value_type* { + return c.data(); +} + +#if defined(_SECURE_SCL) && _SECURE_SCL +// Make a checked iterator to avoid MSVC warnings. +template using checked_ptr = stdext::checked_array_iterator; +template +constexpr auto make_checked(T* p, size_t size) -> checked_ptr { + return {p, size}; +} +#else +template using checked_ptr = T*; +template constexpr auto make_checked(T* p, size_t) -> T* { + return p; +} +#endif + +// Attempts to reserve space for n extra characters in the output range. +// Returns a pointer to the reserved range or a reference to it. +template ::value)> +#if FMT_CLANG_VERSION >= 307 && !FMT_ICC_VERSION +__attribute__((no_sanitize("undefined"))) +#endif +inline auto +reserve(std::back_insert_iterator it, size_t n) + -> checked_ptr { + Container& c = get_container(it); + size_t size = c.size(); + c.resize(size + n); + return make_checked(get_data(c) + size, n); +} + +template +inline auto reserve(buffer_appender it, size_t n) -> buffer_appender { + buffer& buf = get_container(it); + buf.try_reserve(buf.size() + n); + return it; +} + +template +constexpr auto reserve(Iterator& it, size_t) -> Iterator& { + return it; +} + +template +using reserve_iterator = + remove_reference_t(), 0))>; + +template +constexpr auto to_pointer(OutputIt, size_t) -> T* { + return nullptr; +} +template auto to_pointer(buffer_appender it, size_t n) -> T* { + buffer& buf = get_container(it); + auto size = buf.size(); + if (buf.capacity() < size + n) return nullptr; + buf.try_resize(size + n); + return buf.data() + size; +} + +template ::value)> +inline auto base_iterator(std::back_insert_iterator& it, + checked_ptr) + -> std::back_insert_iterator { + return it; +} + +template +constexpr auto base_iterator(Iterator, Iterator it) -> Iterator { + return it; +} + +// is spectacularly slow to compile in C++20 so use a simple fill_n +// instead (#1998). +template +FMT_CONSTEXPR auto fill_n(OutputIt out, Size count, const T& value) + -> OutputIt { + for (Size i = 0; i < count; ++i) *out++ = value; + return out; +} +template +FMT_CONSTEXPR20 auto fill_n(T* out, Size count, char value) -> T* { + if (is_constant_evaluated()) { + return fill_n(out, count, value); + } + std::memset(out, value, to_unsigned(count)); + return out + count; +} + +#ifdef __cpp_char8_t +using char8_type = char8_t; +#else +enum char8_type : unsigned char {}; +#endif + +template +FMT_CONSTEXPR FMT_NOINLINE auto copy_str_noinline(InputIt begin, InputIt end, + OutputIt out) -> OutputIt { + return copy_str(begin, end, out); +} + +// A public domain branchless UTF-8 decoder by Christopher Wellons: +// https://github.com/skeeto/branchless-utf8 +/* Decode the next character, c, from s, reporting errors in e. + * + * Since this is a branchless decoder, four bytes will be read from the + * buffer regardless of the actual length of the next character. This + * means the buffer _must_ have at least three bytes of zero padding + * following the end of the data stream. + * + * Errors are reported in e, which will be non-zero if the parsed + * character was somehow invalid: invalid byte sequence, non-canonical + * encoding, or a surrogate half. + * + * The function returns a pointer to the next character. When an error + * occurs, this pointer will be a guess that depends on the particular + * error, but it will always advance at least one byte. + */ +FMT_CONSTEXPR inline auto utf8_decode(const char* s, uint32_t* c, int* e) + -> const char* { + constexpr const int masks[] = {0x00, 0x7f, 0x1f, 0x0f, 0x07}; + constexpr const uint32_t mins[] = {4194304, 0, 128, 2048, 65536}; + constexpr const int shiftc[] = {0, 18, 12, 6, 0}; + constexpr const int shifte[] = {0, 6, 4, 2, 0}; + + int len = code_point_length(s); + const char* next = s + len; + + // Assume a four-byte character and load four bytes. Unused bits are + // shifted out. + *c = uint32_t(s[0] & masks[len]) << 18; + *c |= uint32_t(s[1] & 0x3f) << 12; + *c |= uint32_t(s[2] & 0x3f) << 6; + *c |= uint32_t(s[3] & 0x3f) << 0; + *c >>= shiftc[len]; + + // Accumulate the various error conditions. + using uchar = unsigned char; + *e = (*c < mins[len]) << 6; // non-canonical encoding + *e |= ((*c >> 11) == 0x1b) << 7; // surrogate half? + *e |= (*c > 0x10FFFF) << 8; // out of range? + *e |= (uchar(s[1]) & 0xc0) >> 2; + *e |= (uchar(s[2]) & 0xc0) >> 4; + *e |= uchar(s[3]) >> 6; + *e ^= 0x2a; // top two bits of each tail byte correct? + *e >>= shifte[len]; + + return next; +} + +constexpr uint32_t invalid_code_point = ~uint32_t(); + +// Invokes f(cp, sv) for every code point cp in s with sv being the string view +// corresponding to the code point. cp is invalid_code_point on error. +template +FMT_CONSTEXPR void for_each_codepoint(string_view s, F f) { + auto decode = [f](const char* buf_ptr, const char* ptr) { + auto cp = uint32_t(); + auto error = 0; + auto end = utf8_decode(buf_ptr, &cp, &error); + bool result = f(error ? invalid_code_point : cp, + string_view(ptr, to_unsigned(end - buf_ptr))); + return result ? end : nullptr; + }; + auto p = s.data(); + const size_t block_size = 4; // utf8_decode always reads blocks of 4 chars. + if (s.size() >= block_size) { + for (auto end = p + s.size() - block_size + 1; p < end;) { + p = decode(p, p); + if (!p) return; + } + } + if (auto num_chars_left = s.data() + s.size() - p) { + char buf[2 * block_size - 1] = {}; + copy_str(p, p + num_chars_left, buf); + const char* buf_ptr = buf; + do { + auto end = decode(buf_ptr, p); + if (!end) return; + p += end - buf_ptr; + buf_ptr = end; + } while (buf_ptr - buf < num_chars_left); + } +} + +template +inline auto compute_width(basic_string_view s) -> size_t { + return s.size(); +} + +// Computes approximate display width of a UTF-8 string. +FMT_CONSTEXPR inline size_t compute_width(string_view s) { + size_t num_code_points = 0; + // It is not a lambda for compatibility with C++14. + struct count_code_points { + size_t* count; + FMT_CONSTEXPR auto operator()(uint32_t cp, string_view) const -> bool { + *count += detail::to_unsigned( + 1 + + (cp >= 0x1100 && + (cp <= 0x115f || // Hangul Jamo init. consonants + cp == 0x2329 || // LEFT-POINTING ANGLE BRACKET + cp == 0x232a || // RIGHT-POINTING ANGLE BRACKET + // CJK ... Yi except IDEOGRAPHIC HALF FILL SPACE: + (cp >= 0x2e80 && cp <= 0xa4cf && cp != 0x303f) || + (cp >= 0xac00 && cp <= 0xd7a3) || // Hangul Syllables + (cp >= 0xf900 && cp <= 0xfaff) || // CJK Compatibility Ideographs + (cp >= 0xfe10 && cp <= 0xfe19) || // Vertical Forms + (cp >= 0xfe30 && cp <= 0xfe6f) || // CJK Compatibility Forms + (cp >= 0xff00 && cp <= 0xff60) || // Fullwidth Forms + (cp >= 0xffe0 && cp <= 0xffe6) || // Fullwidth Forms + (cp >= 0x20000 && cp <= 0x2fffd) || // CJK + (cp >= 0x30000 && cp <= 0x3fffd) || + // Miscellaneous Symbols and Pictographs + Emoticons: + (cp >= 0x1f300 && cp <= 0x1f64f) || + // Supplemental Symbols and Pictographs: + (cp >= 0x1f900 && cp <= 0x1f9ff)))); + return true; + } + }; + for_each_codepoint(s, count_code_points{&num_code_points}); + return num_code_points; +} + +inline auto compute_width(basic_string_view s) -> size_t { + return compute_width( + string_view(reinterpret_cast(s.data()), s.size())); +} + +template +inline auto code_point_index(basic_string_view s, size_t n) -> size_t { + size_t size = s.size(); + return n < size ? n : size; +} + +// Calculates the index of the nth code point in a UTF-8 string. +inline auto code_point_index(string_view s, size_t n) -> size_t { + const char* data = s.data(); + size_t num_code_points = 0; + for (size_t i = 0, size = s.size(); i != size; ++i) { + if ((data[i] & 0xc0) != 0x80 && ++num_code_points > n) return i; + } + return s.size(); +} + +inline auto code_point_index(basic_string_view s, size_t n) + -> size_t { + return code_point_index( + string_view(reinterpret_cast(s.data()), s.size()), n); +} + +#ifndef FMT_USE_FLOAT128 +# ifdef __SIZEOF_FLOAT128__ +# define FMT_USE_FLOAT128 1 +# else +# define FMT_USE_FLOAT128 0 +# endif +#endif +#if FMT_USE_FLOAT128 +using float128 = __float128; +#else +using float128 = void; +#endif +template using is_float128 = std::is_same; + +template +using is_floating_point = + bool_constant::value || is_float128::value>; + +template ::value> +struct is_fast_float : bool_constant::is_iec559 && + sizeof(T) <= sizeof(double)> {}; +template struct is_fast_float : std::false_type {}; + +template +using is_double_double = bool_constant::digits == 106>; + +#ifndef FMT_USE_FULL_CACHE_DRAGONBOX +# define FMT_USE_FULL_CACHE_DRAGONBOX 0 +#endif + +template +template +void buffer::append(const U* begin, const U* end) { + while (begin != end) { + auto count = to_unsigned(end - begin); + try_reserve(size_ + count); + auto free_cap = capacity_ - size_; + if (free_cap < count) count = free_cap; + std::uninitialized_copy_n(begin, count, make_checked(ptr_ + size_, count)); + size_ += count; + begin += count; + } +} + +template +struct is_locale : std::false_type {}; +template +struct is_locale> : std::true_type {}; +} // namespace detail + +FMT_MODULE_EXPORT_BEGIN + +// The number of characters to store in the basic_memory_buffer object itself +// to avoid dynamic memory allocation. +enum { inline_buffer_size = 500 }; + +/** + \rst + A dynamically growing memory buffer for trivially copyable/constructible types + with the first ``SIZE`` elements stored in the object itself. + + You can use the ``memory_buffer`` type alias for ``char`` instead. + + **Example**:: + + auto out = fmt::memory_buffer(); + format_to(std::back_inserter(out), "The answer is {}.", 42); + + This will append the following output to the ``out`` object: + + .. code-block:: none + + The answer is 42. + + The output can be converted to an ``std::string`` with ``to_string(out)``. + \endrst + */ +template > +class basic_memory_buffer final : public detail::buffer { + private: + T store_[SIZE]; + + // Don't inherit from Allocator avoid generating type_info for it. + Allocator alloc_; + + // Deallocate memory allocated by the buffer. + FMT_CONSTEXPR20 void deallocate() { + T* data = this->data(); + if (data != store_) alloc_.deallocate(data, this->capacity()); + } + + protected: + FMT_CONSTEXPR20 void grow(size_t size) override; + + public: + using value_type = T; + using const_reference = const T&; + + FMT_CONSTEXPR20 explicit basic_memory_buffer( + const Allocator& alloc = Allocator()) + : alloc_(alloc) { + this->set(store_, SIZE); + if (detail::is_constant_evaluated()) detail::fill_n(store_, SIZE, T()); + } + FMT_CONSTEXPR20 ~basic_memory_buffer() { deallocate(); } + + private: + // Move data from other to this buffer. + FMT_CONSTEXPR20 void move(basic_memory_buffer& other) { + alloc_ = std::move(other.alloc_); + T* data = other.data(); + size_t size = other.size(), capacity = other.capacity(); + if (data == other.store_) { + this->set(store_, capacity); + detail::copy_str(other.store_, other.store_ + size, + detail::make_checked(store_, capacity)); + } else { + this->set(data, capacity); + // Set pointer to the inline array so that delete is not called + // when deallocating. + other.set(other.store_, 0); + other.clear(); + } + this->resize(size); + } + + public: + /** + \rst + Constructs a :class:`fmt::basic_memory_buffer` object moving the content + of the other object to it. + \endrst + */ + FMT_CONSTEXPR20 basic_memory_buffer(basic_memory_buffer&& other) noexcept { + move(other); + } + + /** + \rst + Moves the content of the other ``basic_memory_buffer`` object to this one. + \endrst + */ + auto operator=(basic_memory_buffer&& other) noexcept -> basic_memory_buffer& { + FMT_ASSERT(this != &other, ""); + deallocate(); + move(other); + return *this; + } + + // Returns a copy of the allocator associated with this buffer. + auto get_allocator() const -> Allocator { return alloc_; } + + /** + Resizes the buffer to contain *count* elements. If T is a POD type new + elements may not be initialized. + */ + FMT_CONSTEXPR20 void resize(size_t count) { this->try_resize(count); } + + /** Increases the buffer capacity to *new_capacity*. */ + void reserve(size_t new_capacity) { this->try_reserve(new_capacity); } + + // Directly append data into the buffer + using detail::buffer::append; + template + void append(const ContiguousRange& range) { + append(range.data(), range.data() + range.size()); + } +}; + +template +FMT_CONSTEXPR20 void basic_memory_buffer::grow( + size_t size) { + detail::abort_fuzzing_if(size > 5000); + const size_t max_size = std::allocator_traits::max_size(alloc_); + size_t old_capacity = this->capacity(); + size_t new_capacity = old_capacity + old_capacity / 2; + if (size > new_capacity) + new_capacity = size; + else if (new_capacity > max_size) + new_capacity = size > max_size ? size : max_size; + T* old_data = this->data(); + T* new_data = + std::allocator_traits::allocate(alloc_, new_capacity); + // The following code doesn't throw, so the raw pointer above doesn't leak. + std::uninitialized_copy(old_data, old_data + this->size(), + detail::make_checked(new_data, new_capacity)); + this->set(new_data, new_capacity); + // deallocate must not throw according to the standard, but even if it does, + // the buffer already uses the new storage and will deallocate it in + // destructor. + if (old_data != store_) alloc_.deallocate(old_data, old_capacity); +} + +using memory_buffer = basic_memory_buffer; + +template +struct is_contiguous> : std::true_type { +}; + +namespace detail { +FMT_API void print(std::FILE*, string_view); +} + +/** A formatting error such as invalid format string. */ +FMT_CLASS_API +class FMT_API format_error : public std::runtime_error { + public: + explicit format_error(const char* message) : std::runtime_error(message) {} + explicit format_error(const std::string& message) + : std::runtime_error(message) {} + format_error(const format_error&) = default; + format_error& operator=(const format_error&) = default; + format_error(format_error&&) = default; + format_error& operator=(format_error&&) = default; + ~format_error() noexcept override FMT_MSC_DEFAULT; +}; + +namespace detail_exported { +#if FMT_USE_NONTYPE_TEMPLATE_ARGS +template struct fixed_string { + constexpr fixed_string(const Char (&str)[N]) { + detail::copy_str(static_cast(str), + str + N, data); + } + Char data[N] = {}; +}; +#endif + +// Converts a compile-time string to basic_string_view. +template +constexpr auto compile_string_to_view(const Char (&s)[N]) + -> basic_string_view { + // Remove trailing NUL character if needed. Won't be present if this is used + // with a raw character array (i.e. not defined as a string). + return {s, N - (std::char_traits::to_int_type(s[N - 1]) == 0 ? 1 : 0)}; +} +template +constexpr auto compile_string_to_view(detail::std_string_view s) + -> basic_string_view { + return {s.data(), s.size()}; +} +} // namespace detail_exported + +FMT_BEGIN_DETAIL_NAMESPACE + +template struct is_integral : std::is_integral {}; +template <> struct is_integral : std::true_type {}; +template <> struct is_integral : std::true_type {}; + +template +using is_signed = + std::integral_constant::is_signed || + std::is_same::value>; + +// Returns true if value is negative, false otherwise. +// Same as `value < 0` but doesn't produce warnings if T is an unsigned type. +template ::value)> +constexpr auto is_negative(T value) -> bool { + return value < 0; +} +template ::value)> +constexpr auto is_negative(T) -> bool { + return false; +} + +template +FMT_CONSTEXPR auto is_supported_floating_point(T) -> bool { + if (std::is_same()) return FMT_USE_FLOAT; + if (std::is_same()) return FMT_USE_DOUBLE; + if (std::is_same()) return FMT_USE_LONG_DOUBLE; + return true; +} + +// Smallest of uint32_t, uint64_t, uint128_t that is large enough to +// represent all values of an integral type T. +template +using uint32_or_64_or_128_t = + conditional_t() <= 32 && !FMT_REDUCE_INT_INSTANTIATIONS, + uint32_t, + conditional_t() <= 64, uint64_t, uint128_t>>; +template +using uint64_or_128_t = conditional_t() <= 64, uint64_t, uint128_t>; + +#define FMT_POWERS_OF_10(factor) \ + factor * 10, (factor)*100, (factor)*1000, (factor)*10000, (factor)*100000, \ + (factor)*1000000, (factor)*10000000, (factor)*100000000, \ + (factor)*1000000000 + +// Converts value in the range [0, 100) to a string. +constexpr const char* digits2(size_t value) { + // GCC generates slightly better code when value is pointer-size. + return &"0001020304050607080910111213141516171819" + "2021222324252627282930313233343536373839" + "4041424344454647484950515253545556575859" + "6061626364656667686970717273747576777879" + "8081828384858687888990919293949596979899"[value * 2]; +} + +// Sign is a template parameter to workaround a bug in gcc 4.8. +template constexpr Char sign(Sign s) { +#if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 604 + static_assert(std::is_same::value, ""); +#endif + return static_cast("\0-+ "[s]); +} + +template FMT_CONSTEXPR auto count_digits_fallback(T n) -> int { + int count = 1; + for (;;) { + // Integer division is slow so do it for a group of four digits instead + // of for every digit. The idea comes from the talk by Alexandrescu + // "Three Optimization Tips for C++". See speed-test for a comparison. + if (n < 10) return count; + if (n < 100) return count + 1; + if (n < 1000) return count + 2; + if (n < 10000) return count + 3; + n /= 10000u; + count += 4; + } +} +#if FMT_USE_INT128 +FMT_CONSTEXPR inline auto count_digits(uint128_opt n) -> int { + return count_digits_fallback(n); +} +#endif + +#ifdef FMT_BUILTIN_CLZLL +// It is a separate function rather than a part of count_digits to workaround +// the lack of static constexpr in constexpr functions. +inline auto do_count_digits(uint64_t n) -> int { + // This has comparable performance to the version by Kendall Willets + // (https://github.com/fmtlib/format-benchmark/blob/master/digits10) + // but uses smaller tables. + // Maps bsr(n) to ceil(log10(pow(2, bsr(n) + 1) - 1)). + static constexpr uint8_t bsr2log10[] = { + 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, + 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, + 10, 11, 11, 11, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 15, 15, + 15, 16, 16, 16, 16, 17, 17, 17, 18, 18, 18, 19, 19, 19, 19, 20}; + auto t = bsr2log10[FMT_BUILTIN_CLZLL(n | 1) ^ 63]; + static constexpr const uint64_t zero_or_powers_of_10[] = { + 0, 0, FMT_POWERS_OF_10(1U), FMT_POWERS_OF_10(1000000000ULL), + 10000000000000000000ULL}; + return t - (n < zero_or_powers_of_10[t]); +} +#endif + +// Returns the number of decimal digits in n. Leading zeros are not counted +// except for n == 0 in which case count_digits returns 1. +FMT_CONSTEXPR20 inline auto count_digits(uint64_t n) -> int { +#ifdef FMT_BUILTIN_CLZLL + if (!is_constant_evaluated()) { + return do_count_digits(n); + } +#endif + return count_digits_fallback(n); +} + +// Counts the number of digits in n. BITS = log2(radix). +template +FMT_CONSTEXPR auto count_digits(UInt n) -> int { +#ifdef FMT_BUILTIN_CLZ + if (!is_constant_evaluated() && num_bits() == 32) + return (FMT_BUILTIN_CLZ(static_cast(n) | 1) ^ 31) / BITS + 1; +#endif + // Lambda avoids unreachable code warnings from NVHPC. + return [](UInt m) { + int num_digits = 0; + do { + ++num_digits; + } while ((m >>= BITS) != 0); + return num_digits; + }(n); +} + +#ifdef FMT_BUILTIN_CLZ +// It is a separate function rather than a part of count_digits to workaround +// the lack of static constexpr in constexpr functions. +FMT_INLINE auto do_count_digits(uint32_t n) -> int { +// An optimization by Kendall Willets from https://bit.ly/3uOIQrB. +// This increments the upper 32 bits (log10(T) - 1) when >= T is added. +# define FMT_INC(T) (((sizeof(# T) - 1ull) << 32) - T) + static constexpr uint64_t table[] = { + FMT_INC(0), FMT_INC(0), FMT_INC(0), // 8 + FMT_INC(10), FMT_INC(10), FMT_INC(10), // 64 + FMT_INC(100), FMT_INC(100), FMT_INC(100), // 512 + FMT_INC(1000), FMT_INC(1000), FMT_INC(1000), // 4096 + FMT_INC(10000), FMT_INC(10000), FMT_INC(10000), // 32k + FMT_INC(100000), FMT_INC(100000), FMT_INC(100000), // 256k + FMT_INC(1000000), FMT_INC(1000000), FMT_INC(1000000), // 2048k + FMT_INC(10000000), FMT_INC(10000000), FMT_INC(10000000), // 16M + FMT_INC(100000000), FMT_INC(100000000), FMT_INC(100000000), // 128M + FMT_INC(1000000000), FMT_INC(1000000000), FMT_INC(1000000000), // 1024M + FMT_INC(1000000000), FMT_INC(1000000000) // 4B + }; + auto inc = table[FMT_BUILTIN_CLZ(n | 1) ^ 31]; + return static_cast((n + inc) >> 32); +} +#endif + +// Optional version of count_digits for better performance on 32-bit platforms. +FMT_CONSTEXPR20 inline auto count_digits(uint32_t n) -> int { +#ifdef FMT_BUILTIN_CLZ + if (!is_constant_evaluated()) { + return do_count_digits(n); + } +#endif + return count_digits_fallback(n); +} + +template constexpr auto digits10() noexcept -> int { + return std::numeric_limits::digits10; +} +template <> constexpr auto digits10() noexcept -> int { return 38; } +template <> constexpr auto digits10() noexcept -> int { return 38; } + +template struct thousands_sep_result { + std::string grouping; + Char thousands_sep; +}; + +template +FMT_API auto thousands_sep_impl(locale_ref loc) -> thousands_sep_result; +template +inline auto thousands_sep(locale_ref loc) -> thousands_sep_result { + auto result = thousands_sep_impl(loc); + return {result.grouping, Char(result.thousands_sep)}; +} +template <> +inline auto thousands_sep(locale_ref loc) -> thousands_sep_result { + return thousands_sep_impl(loc); +} + +template +FMT_API auto decimal_point_impl(locale_ref loc) -> Char; +template inline auto decimal_point(locale_ref loc) -> Char { + return Char(decimal_point_impl(loc)); +} +template <> inline auto decimal_point(locale_ref loc) -> wchar_t { + return decimal_point_impl(loc); +} + +// Compares two characters for equality. +template auto equal2(const Char* lhs, const char* rhs) -> bool { + return lhs[0] == Char(rhs[0]) && lhs[1] == Char(rhs[1]); +} +inline auto equal2(const char* lhs, const char* rhs) -> bool { + return memcmp(lhs, rhs, 2) == 0; +} + +// Copies two characters from src to dst. +template +FMT_CONSTEXPR20 FMT_INLINE void copy2(Char* dst, const char* src) { + if (!is_constant_evaluated() && sizeof(Char) == sizeof(char)) { + memcpy(dst, src, 2); + return; + } + *dst++ = static_cast(*src++); + *dst = static_cast(*src); +} + +template struct format_decimal_result { + Iterator begin; + Iterator end; +}; + +// Formats a decimal unsigned integer value writing into out pointing to a +// buffer of specified size. The caller must ensure that the buffer is large +// enough. +template +FMT_CONSTEXPR20 auto format_decimal(Char* out, UInt value, int size) + -> format_decimal_result { + FMT_ASSERT(size >= count_digits(value), "invalid digit count"); + out += size; + Char* end = out; + while (value >= 100) { + // Integer division is slow so do it for a group of two digits instead + // of for every digit. The idea comes from the talk by Alexandrescu + // "Three Optimization Tips for C++". See speed-test for a comparison. + out -= 2; + copy2(out, digits2(static_cast(value % 100))); + value /= 100; + } + if (value < 10) { + *--out = static_cast('0' + value); + return {out, end}; + } + out -= 2; + copy2(out, digits2(static_cast(value))); + return {out, end}; +} + +template >::value)> +inline auto format_decimal(Iterator out, UInt value, int size) + -> format_decimal_result { + // Buffer is large enough to hold all digits (digits10 + 1). + Char buffer[digits10() + 1]; + auto end = format_decimal(buffer, value, size).end; + return {out, detail::copy_str_noinline(buffer, end, out)}; +} + +template +FMT_CONSTEXPR auto format_uint(Char* buffer, UInt value, int num_digits, + bool upper = false) -> Char* { + buffer += num_digits; + Char* end = buffer; + do { + const char* digits = upper ? "0123456789ABCDEF" : "0123456789abcdef"; + unsigned digit = static_cast(value & ((1 << BASE_BITS) - 1)); + *--buffer = static_cast(BASE_BITS < 4 ? static_cast('0' + digit) + : digits[digit]); + } while ((value >>= BASE_BITS) != 0); + return end; +} + +template +inline auto format_uint(It out, UInt value, int num_digits, bool upper = false) + -> It { + if (auto ptr = to_pointer(out, to_unsigned(num_digits))) { + format_uint(ptr, value, num_digits, upper); + return out; + } + // Buffer should be large enough to hold all digits (digits / BASE_BITS + 1). + char buffer[num_bits() / BASE_BITS + 1]; + format_uint(buffer, value, num_digits, upper); + return detail::copy_str_noinline(buffer, buffer + num_digits, out); +} + +// A converter from UTF-8 to UTF-16. +class utf8_to_utf16 { + private: + basic_memory_buffer buffer_; + + public: + FMT_API explicit utf8_to_utf16(string_view s); + operator basic_string_view() const { return {&buffer_[0], size()}; } + auto size() const -> size_t { return buffer_.size() - 1; } + auto c_str() const -> const wchar_t* { return &buffer_[0]; } + auto str() const -> std::wstring { return {&buffer_[0], size()}; } +}; + +namespace dragonbox { + +// Type-specific information that Dragonbox uses. +template struct float_info; + +template <> struct float_info { + using carrier_uint = uint32_t; + static const int exponent_bits = 8; + static const int kappa = 1; + static const int big_divisor = 100; + static const int small_divisor = 10; + static const int min_k = -31; + static const int max_k = 46; + static const int divisibility_check_by_5_threshold = 39; + static const int case_fc_pm_half_lower_threshold = -1; + static const int shorter_interval_tie_lower_threshold = -35; + static const int shorter_interval_tie_upper_threshold = -35; +}; + +template <> struct float_info { + using carrier_uint = uint64_t; + static const int exponent_bits = 11; + static const int kappa = 2; + static const int big_divisor = 1000; + static const int small_divisor = 100; + static const int min_k = -292; + static const int max_k = 326; + static const int divisibility_check_by_5_threshold = 86; + static const int case_fc_pm_half_lower_threshold = -2; + static const int shorter_interval_tie_lower_threshold = -77; + static const int shorter_interval_tie_upper_threshold = -77; +}; + +// An 80- or 128-bit floating point number. +template +struct float_info::digits == 64 || + std::numeric_limits::digits == 113 || + is_float128::value>> { + using carrier_uint = detail::uint128_t; + static const int exponent_bits = 15; +}; + +// A double-double floating point number. +template +struct float_info::value>> { + using carrier_uint = detail::uint128_t; +}; + +template struct decimal_fp { + using significand_type = typename float_info::carrier_uint; + significand_type significand; + int exponent; +}; + +template FMT_API auto to_decimal(T x) noexcept -> decimal_fp; +} // namespace dragonbox + +// Returns true iff Float has the implicit bit which is not stored. +template constexpr bool has_implicit_bit() { + // An 80-bit FP number has a 64-bit significand an no implicit bit. + return std::numeric_limits::digits != 64; +} + +// Returns the number of significand bits stored in Float. The implicit bit is +// not counted since it is not stored. +template constexpr int num_significand_bits() { + // std::numeric_limits may not support __float128. + return is_float128() ? 112 + : (std::numeric_limits::digits - + (has_implicit_bit() ? 1 : 0)); +} + +template +constexpr auto exponent_mask() -> + typename dragonbox::float_info::carrier_uint { + using uint = typename dragonbox::float_info::carrier_uint; + return ((uint(1) << dragonbox::float_info::exponent_bits) - 1) + << num_significand_bits(); +} +template constexpr auto exponent_bias() -> int { + // std::numeric_limits may not support __float128. + return is_float128() ? 16383 + : std::numeric_limits::max_exponent - 1; +} + +// Writes the exponent exp in the form "[+-]d{2,3}" to buffer. +template +FMT_CONSTEXPR auto write_exponent(int exp, It it) -> It { + FMT_ASSERT(-10000 < exp && exp < 10000, "exponent out of range"); + if (exp < 0) { + *it++ = static_cast('-'); + exp = -exp; + } else { + *it++ = static_cast('+'); + } + if (exp >= 100) { + const char* top = digits2(to_unsigned(exp / 100)); + if (exp >= 1000) *it++ = static_cast(top[0]); + *it++ = static_cast(top[1]); + exp %= 100; + } + const char* d = digits2(to_unsigned(exp)); + *it++ = static_cast(d[0]); + *it++ = static_cast(d[1]); + return it; +} + +// A floating-point number f * pow(2, e) where F is an unsigned type. +template struct basic_fp { + F f; + int e; + + static constexpr const int num_significand_bits = + static_cast(sizeof(F) * num_bits()); + + constexpr basic_fp() : f(0), e(0) {} + constexpr basic_fp(uint64_t f_val, int e_val) : f(f_val), e(e_val) {} + + // Constructs fp from an IEEE754 floating-point number. + template FMT_CONSTEXPR basic_fp(Float n) { assign(n); } + + // Assigns n to this and return true iff predecessor is closer than successor. + template ::value)> + FMT_CONSTEXPR auto assign(Float n) -> bool { + static_assert(std::numeric_limits::digits <= 113, "unsupported FP"); + // Assume Float is in the format [sign][exponent][significand]. + using carrier_uint = typename dragonbox::float_info::carrier_uint; + const auto num_float_significand_bits = + detail::num_significand_bits(); + const auto implicit_bit = carrier_uint(1) << num_float_significand_bits; + const auto significand_mask = implicit_bit - 1; + auto u = bit_cast(n); + f = static_cast(u & significand_mask); + auto biased_e = static_cast((u & exponent_mask()) >> + num_float_significand_bits); + // The predecessor is closer if n is a normalized power of 2 (f == 0) + // other than the smallest normalized number (biased_e > 1). + auto is_predecessor_closer = f == 0 && biased_e > 1; + if (biased_e == 0) + biased_e = 1; // Subnormals use biased exponent 1 (min exponent). + else if (has_implicit_bit()) + f += static_cast(implicit_bit); + e = biased_e - exponent_bias() - num_float_significand_bits; + if (!has_implicit_bit()) ++e; + return is_predecessor_closer; + } + + template ::value)> + FMT_CONSTEXPR auto assign(Float n) -> bool { + static_assert(std::numeric_limits::is_iec559, "unsupported FP"); + return assign(static_cast(n)); + } +}; + +using fp = basic_fp; + +// Normalizes the value converted from double and multiplied by (1 << SHIFT). +template +FMT_CONSTEXPR basic_fp normalize(basic_fp value) { + // Handle subnormals. + const auto implicit_bit = F(1) << num_significand_bits(); + const auto shifted_implicit_bit = implicit_bit << SHIFT; + while ((value.f & shifted_implicit_bit) == 0) { + value.f <<= 1; + --value.e; + } + // Subtract 1 to account for hidden bit. + const auto offset = basic_fp::num_significand_bits - + num_significand_bits() - SHIFT - 1; + value.f <<= offset; + value.e -= offset; + return value; +} + +// Computes lhs * rhs / pow(2, 64) rounded to nearest with half-up tie breaking. +FMT_CONSTEXPR inline uint64_t multiply(uint64_t lhs, uint64_t rhs) { +#if FMT_USE_INT128 + auto product = static_cast<__uint128_t>(lhs) * rhs; + auto f = static_cast(product >> 64); + return (static_cast(product) & (1ULL << 63)) != 0 ? f + 1 : f; +#else + // Multiply 32-bit parts of significands. + uint64_t mask = (1ULL << 32) - 1; + uint64_t a = lhs >> 32, b = lhs & mask; + uint64_t c = rhs >> 32, d = rhs & mask; + uint64_t ac = a * c, bc = b * c, ad = a * d, bd = b * d; + // Compute mid 64-bit of result and round. + uint64_t mid = (bd >> 32) + (ad & mask) + (bc & mask) + (1U << 31); + return ac + (ad >> 32) + (bc >> 32) + (mid >> 32); +#endif +} + +FMT_CONSTEXPR inline fp operator*(fp x, fp y) { + return {multiply(x.f, y.f), x.e + y.e + 64}; +} + +template struct basic_data { + // Normalized 64-bit significands of pow(10, k), for k = -348, -340, ..., 340. + // These are generated by support/compute-powers.py. + static constexpr uint64_t pow10_significands[87] = { + 0xfa8fd5a0081c0288, 0xbaaee17fa23ebf76, 0x8b16fb203055ac76, + 0xcf42894a5dce35ea, 0x9a6bb0aa55653b2d, 0xe61acf033d1a45df, + 0xab70fe17c79ac6ca, 0xff77b1fcbebcdc4f, 0xbe5691ef416bd60c, + 0x8dd01fad907ffc3c, 0xd3515c2831559a83, 0x9d71ac8fada6c9b5, + 0xea9c227723ee8bcb, 0xaecc49914078536d, 0x823c12795db6ce57, + 0xc21094364dfb5637, 0x9096ea6f3848984f, 0xd77485cb25823ac7, + 0xa086cfcd97bf97f4, 0xef340a98172aace5, 0xb23867fb2a35b28e, + 0x84c8d4dfd2c63f3b, 0xc5dd44271ad3cdba, 0x936b9fcebb25c996, + 0xdbac6c247d62a584, 0xa3ab66580d5fdaf6, 0xf3e2f893dec3f126, + 0xb5b5ada8aaff80b8, 0x87625f056c7c4a8b, 0xc9bcff6034c13053, + 0x964e858c91ba2655, 0xdff9772470297ebd, 0xa6dfbd9fb8e5b88f, + 0xf8a95fcf88747d94, 0xb94470938fa89bcf, 0x8a08f0f8bf0f156b, + 0xcdb02555653131b6, 0x993fe2c6d07b7fac, 0xe45c10c42a2b3b06, + 0xaa242499697392d3, 0xfd87b5f28300ca0e, 0xbce5086492111aeb, + 0x8cbccc096f5088cc, 0xd1b71758e219652c, 0x9c40000000000000, + 0xe8d4a51000000000, 0xad78ebc5ac620000, 0x813f3978f8940984, + 0xc097ce7bc90715b3, 0x8f7e32ce7bea5c70, 0xd5d238a4abe98068, + 0x9f4f2726179a2245, 0xed63a231d4c4fb27, 0xb0de65388cc8ada8, + 0x83c7088e1aab65db, 0xc45d1df942711d9a, 0x924d692ca61be758, + 0xda01ee641a708dea, 0xa26da3999aef774a, 0xf209787bb47d6b85, + 0xb454e4a179dd1877, 0x865b86925b9bc5c2, 0xc83553c5c8965d3d, + 0x952ab45cfa97a0b3, 0xde469fbd99a05fe3, 0xa59bc234db398c25, + 0xf6c69a72a3989f5c, 0xb7dcbf5354e9bece, 0x88fcf317f22241e2, + 0xcc20ce9bd35c78a5, 0x98165af37b2153df, 0xe2a0b5dc971f303a, + 0xa8d9d1535ce3b396, 0xfb9b7cd9a4a7443c, 0xbb764c4ca7a44410, + 0x8bab8eefb6409c1a, 0xd01fef10a657842c, 0x9b10a4e5e9913129, + 0xe7109bfba19c0c9d, 0xac2820d9623bf429, 0x80444b5e7aa7cf85, + 0xbf21e44003acdd2d, 0x8e679c2f5e44ff8f, 0xd433179d9c8cb841, + 0x9e19db92b4e31ba9, 0xeb96bf6ebadf77d9, 0xaf87023b9bf0ee6b, + }; + +#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wnarrowing" +#endif + // Binary exponents of pow(10, k), for k = -348, -340, ..., 340, corresponding + // to significands above. + static constexpr int16_t pow10_exponents[87] = { + -1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980, -954, + -927, -901, -874, -847, -821, -794, -768, -741, -715, -688, -661, + -635, -608, -582, -555, -529, -502, -475, -449, -422, -396, -369, + -343, -316, -289, -263, -236, -210, -183, -157, -130, -103, -77, + -50, -24, 3, 30, 56, 83, 109, 136, 162, 189, 216, + 242, 269, 295, 322, 348, 375, 402, 428, 455, 481, 508, + 534, 561, 588, 614, 641, 667, 694, 720, 747, 774, 800, + 827, 853, 880, 907, 933, 960, 986, 1013, 1039, 1066}; +#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 +# pragma GCC diagnostic pop +#endif + + static constexpr uint64_t power_of_10_64[20] = { + 1, FMT_POWERS_OF_10(1ULL), FMT_POWERS_OF_10(1000000000ULL), + 10000000000000000000ULL}; +}; + +#if FMT_CPLUSPLUS < 201703L +template constexpr uint64_t basic_data::pow10_significands[]; +template constexpr int16_t basic_data::pow10_exponents[]; +template constexpr uint64_t basic_data::power_of_10_64[]; +#endif + +// This is a struct rather than an alias to avoid shadowing warnings in gcc. +struct data : basic_data<> {}; + +// Returns a cached power of 10 `c_k = c_k.f * pow(2, c_k.e)` such that its +// (binary) exponent satisfies `min_exponent <= c_k.e <= min_exponent + 28`. +FMT_CONSTEXPR inline fp get_cached_power(int min_exponent, + int& pow10_exponent) { + const int shift = 32; + // log10(2) = 0x0.4d104d427de7fbcc... + const int64_t significand = 0x4d104d427de7fbcc; + int index = static_cast( + ((min_exponent + fp::num_significand_bits - 1) * (significand >> shift) + + ((int64_t(1) << shift) - 1)) // ceil + >> 32 // arithmetic shift + ); + // Decimal exponent of the first (smallest) cached power of 10. + const int first_dec_exp = -348; + // Difference between 2 consecutive decimal exponents in cached powers of 10. + const int dec_exp_step = 8; + index = (index - first_dec_exp - 1) / dec_exp_step + 1; + pow10_exponent = first_dec_exp + index * dec_exp_step; + return {data::pow10_significands[index], data::pow10_exponents[index]}; +} + +#ifndef _MSC_VER +# define FMT_SNPRINTF snprintf +#else +FMT_API auto fmt_snprintf(char* buf, size_t size, const char* fmt, ...) -> int; +# define FMT_SNPRINTF fmt_snprintf +#endif // _MSC_VER + +// Formats a floating-point number with snprintf using the hexfloat format. +template +auto snprintf_float(T value, int precision, float_specs specs, + buffer& buf) -> int { + // Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail. + FMT_ASSERT(buf.capacity() > buf.size(), "empty buffer"); + FMT_ASSERT(specs.format == float_format::hex, ""); + static_assert(!std::is_same::value, ""); + + // Build the format string. + char format[7]; // The longest format is "%#.*Le". + char* format_ptr = format; + *format_ptr++ = '%'; + if (specs.showpoint) *format_ptr++ = '#'; + if (precision >= 0) { + *format_ptr++ = '.'; + *format_ptr++ = '*'; + } + if (std::is_same()) *format_ptr++ = 'L'; + *format_ptr++ = specs.upper ? 'A' : 'a'; + *format_ptr = '\0'; + + // Format using snprintf. + auto offset = buf.size(); + for (;;) { + auto begin = buf.data() + offset; + auto capacity = buf.capacity() - offset; + abort_fuzzing_if(precision > 100000); + // Suppress the warning about a nonliteral format string. + // Cannot use auto because of a bug in MinGW (#1532). + int (*snprintf_ptr)(char*, size_t, const char*, ...) = FMT_SNPRINTF; + int result = precision >= 0 + ? snprintf_ptr(begin, capacity, format, precision, value) + : snprintf_ptr(begin, capacity, format, value); + if (result < 0) { + // The buffer will grow exponentially. + buf.try_reserve(buf.capacity() + 1); + continue; + } + auto size = to_unsigned(result); + // Size equal to capacity means that the last character was truncated. + if (size < capacity) { + buf.try_resize(size + offset); + return 0; + } + buf.try_reserve(size + offset + 1); // Add 1 for the terminating '\0'. + } +} + +template +using convert_float_result = + conditional_t::value || sizeof(T) == sizeof(double), + double, T>; + +template +constexpr auto convert_float(T value) -> convert_float_result { + return static_cast>(value); +} + +template +FMT_NOINLINE FMT_CONSTEXPR auto fill(OutputIt it, size_t n, + const fill_t& fill) -> OutputIt { + auto fill_size = fill.size(); + if (fill_size == 1) return detail::fill_n(it, n, fill[0]); + auto data = fill.data(); + for (size_t i = 0; i < n; ++i) + it = copy_str(data, data + fill_size, it); + return it; +} + +// Writes the output of f, padded according to format specifications in specs. +// size: output size in code units. +// width: output display width in (terminal) column positions. +template +FMT_CONSTEXPR auto write_padded(OutputIt out, + const basic_format_specs& specs, + size_t size, size_t width, F&& f) -> OutputIt { + static_assert(align == align::left || align == align::right, ""); + unsigned spec_width = to_unsigned(specs.width); + size_t padding = spec_width > width ? spec_width - width : 0; + // Shifts are encoded as string literals because static constexpr is not + // supported in constexpr functions. + auto* shifts = align == align::left ? "\x1f\x1f\x00\x01" : "\x00\x1f\x00\x01"; + size_t left_padding = padding >> shifts[specs.align]; + size_t right_padding = padding - left_padding; + auto it = reserve(out, size + padding * specs.fill.size()); + if (left_padding != 0) it = fill(it, left_padding, specs.fill); + it = f(it); + if (right_padding != 0) it = fill(it, right_padding, specs.fill); + return base_iterator(out, it); +} + +template +constexpr auto write_padded(OutputIt out, const basic_format_specs& specs, + size_t size, F&& f) -> OutputIt { + return write_padded(out, specs, size, size, f); +} + +template +FMT_CONSTEXPR auto write_bytes(OutputIt out, string_view bytes, + const basic_format_specs& specs) + -> OutputIt { + return write_padded( + out, specs, bytes.size(), [bytes](reserve_iterator it) { + const char* data = bytes.data(); + return copy_str(data, data + bytes.size(), it); + }); +} + +template +auto write_ptr(OutputIt out, UIntPtr value, + const basic_format_specs* specs) -> OutputIt { + int num_digits = count_digits<4>(value); + auto size = to_unsigned(num_digits) + size_t(2); + auto write = [=](reserve_iterator it) { + *it++ = static_cast('0'); + *it++ = static_cast('x'); + return format_uint<4, Char>(it, value, num_digits); + }; + return specs ? write_padded(out, *specs, size, write) + : base_iterator(out, write(reserve(out, size))); +} + +// Returns true iff the code point cp is printable. +FMT_API auto is_printable(uint32_t cp) -> bool; + +inline auto needs_escape(uint32_t cp) -> bool { + return cp < 0x20 || cp == 0x7f || cp == '"' || cp == '\\' || + !is_printable(cp); +} + +template struct find_escape_result { + const Char* begin; + const Char* end; + uint32_t cp; +}; + +template +using make_unsigned_char = + typename conditional_t::value, + std::make_unsigned, + type_identity>::type; + +template +auto find_escape(const Char* begin, const Char* end) + -> find_escape_result { + for (; begin != end; ++begin) { + uint32_t cp = static_cast>(*begin); + if (const_check(sizeof(Char) == 1) && cp >= 0x80) continue; + if (needs_escape(cp)) return {begin, begin + 1, cp}; + } + return {begin, nullptr, 0}; +} + +inline auto find_escape(const char* begin, const char* end) + -> find_escape_result { + if (!is_utf8()) return find_escape(begin, end); + auto result = find_escape_result{end, nullptr, 0}; + for_each_codepoint(string_view(begin, to_unsigned(end - begin)), + [&](uint32_t cp, string_view sv) { + if (needs_escape(cp)) { + result = {sv.begin(), sv.end(), cp}; + return false; + } + return true; + }); + return result; +} + +#define FMT_STRING_IMPL(s, base, explicit) \ + [] { \ + /* Use the hidden visibility as a workaround for a GCC bug (#1973). */ \ + /* Use a macro-like name to avoid shadowing warnings. */ \ + struct FMT_GCC_VISIBILITY_HIDDEN FMT_COMPILE_STRING : base { \ + using char_type = fmt::remove_cvref_t; \ + FMT_MAYBE_UNUSED FMT_CONSTEXPR explicit \ + operator fmt::basic_string_view() const { \ + return fmt::detail_exported::compile_string_to_view(s); \ + } \ + }; \ + return FMT_COMPILE_STRING(); \ + }() + +/** + \rst + Constructs a compile-time format string from a string literal *s*. + + **Example**:: + + // A compile-time error because 'd' is an invalid specifier for strings. + std::string s = fmt::format(FMT_STRING("{:d}"), "foo"); + \endrst + */ +#define FMT_STRING(s) FMT_STRING_IMPL(s, fmt::detail::compile_string, ) + +template +auto write_codepoint(OutputIt out, char prefix, uint32_t cp) -> OutputIt { + *out++ = static_cast('\\'); + *out++ = static_cast(prefix); + Char buf[width]; + fill_n(buf, width, static_cast('0')); + format_uint<4>(buf, cp, width); + return copy_str(buf, buf + width, out); +} + +template +auto write_escaped_cp(OutputIt out, const find_escape_result& escape) + -> OutputIt { + auto c = static_cast(escape.cp); + switch (escape.cp) { + case '\n': + *out++ = static_cast('\\'); + c = static_cast('n'); + break; + case '\r': + *out++ = static_cast('\\'); + c = static_cast('r'); + break; + case '\t': + *out++ = static_cast('\\'); + c = static_cast('t'); + break; + case '"': + FMT_FALLTHROUGH; + case '\'': + FMT_FALLTHROUGH; + case '\\': + *out++ = static_cast('\\'); + break; + default: + if (is_utf8()) { + if (escape.cp < 0x100) { + return write_codepoint<2, Char>(out, 'x', escape.cp); + } + if (escape.cp < 0x10000) { + return write_codepoint<4, Char>(out, 'u', escape.cp); + } + if (escape.cp < 0x110000) { + return write_codepoint<8, Char>(out, 'U', escape.cp); + } + } + for (Char escape_char : basic_string_view( + escape.begin, to_unsigned(escape.end - escape.begin))) { + out = write_codepoint<2, Char>(out, 'x', + static_cast(escape_char) & 0xFF); + } + return out; + } + *out++ = c; + return out; +} + +template +auto write_escaped_string(OutputIt out, basic_string_view str) + -> OutputIt { + *out++ = static_cast('"'); + auto begin = str.begin(), end = str.end(); + do { + auto escape = find_escape(begin, end); + out = copy_str(begin, escape.begin, out); + begin = escape.end; + if (!begin) break; + out = write_escaped_cp(out, escape); + } while (begin != end); + *out++ = static_cast('"'); + return out; +} + +template +auto write_escaped_char(OutputIt out, Char v) -> OutputIt { + *out++ = static_cast('\''); + if ((needs_escape(static_cast(v)) && v != static_cast('"')) || + v == static_cast('\'')) { + out = write_escaped_cp( + out, find_escape_result{&v, &v + 1, static_cast(v)}); + } else { + *out++ = v; + } + *out++ = static_cast('\''); + return out; +} + +template +FMT_CONSTEXPR auto write_char(OutputIt out, Char value, + const basic_format_specs& specs) + -> OutputIt { + bool is_debug = specs.type == presentation_type::debug; + return write_padded(out, specs, 1, [=](reserve_iterator it) { + if (is_debug) return write_escaped_char(it, value); + *it++ = value; + return it; + }); +} +template +FMT_CONSTEXPR auto write(OutputIt out, Char value, + const basic_format_specs& specs, + locale_ref loc = {}) -> OutputIt { + return check_char_specs(specs) + ? write_char(out, value, specs) + : write(out, static_cast(value), specs, loc); +} + +// Data for write_int that doesn't depend on output iterator type. It is used to +// avoid template code bloat. +template struct write_int_data { + size_t size; + size_t padding; + + FMT_CONSTEXPR write_int_data(int num_digits, unsigned prefix, + const basic_format_specs& specs) + : size((prefix >> 24) + to_unsigned(num_digits)), padding(0) { + if (specs.align == align::numeric) { + auto width = to_unsigned(specs.width); + if (width > size) { + padding = width - size; + size = width; + } + } else if (specs.precision > num_digits) { + size = (prefix >> 24) + to_unsigned(specs.precision); + padding = to_unsigned(specs.precision - num_digits); + } + } +}; + +// Writes an integer in the format +// +// where are written by write_digits(it). +// prefix contains chars in three lower bytes and the size in the fourth byte. +template +FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, int num_digits, + unsigned prefix, + const basic_format_specs& specs, + W write_digits) -> OutputIt { + // Slightly faster check for specs.width == 0 && specs.precision == -1. + if ((specs.width | (specs.precision + 1)) == 0) { + auto it = reserve(out, to_unsigned(num_digits) + (prefix >> 24)); + if (prefix != 0) { + for (unsigned p = prefix & 0xffffff; p != 0; p >>= 8) + *it++ = static_cast(p & 0xff); + } + return base_iterator(out, write_digits(it)); + } + auto data = write_int_data(num_digits, prefix, specs); + return write_padded( + out, specs, data.size, [=](reserve_iterator it) { + for (unsigned p = prefix & 0xffffff; p != 0; p >>= 8) + *it++ = static_cast(p & 0xff); + it = detail::fill_n(it, data.padding, static_cast('0')); + return write_digits(it); + }); +} + +template class digit_grouping { + private: + thousands_sep_result sep_; + + struct next_state { + std::string::const_iterator group; + int pos; + }; + next_state initial_state() const { return {sep_.grouping.begin(), 0}; } + + // Returns the next digit group separator position. + int next(next_state& state) const { + if (!sep_.thousands_sep) return max_value(); + if (state.group == sep_.grouping.end()) + return state.pos += sep_.grouping.back(); + if (*state.group <= 0 || *state.group == max_value()) + return max_value(); + state.pos += *state.group++; + return state.pos; + } + + public: + explicit digit_grouping(locale_ref loc, bool localized = true) { + if (localized) + sep_ = thousands_sep(loc); + else + sep_.thousands_sep = Char(); + } + explicit digit_grouping(thousands_sep_result sep) : sep_(sep) {} + + Char separator() const { return sep_.thousands_sep; } + + int count_separators(int num_digits) const { + int count = 0; + auto state = initial_state(); + while (num_digits > next(state)) ++count; + return count; + } + + // Applies grouping to digits and write the output to out. + template + Out apply(Out out, basic_string_view digits) const { + auto num_digits = static_cast(digits.size()); + auto separators = basic_memory_buffer(); + separators.push_back(0); + auto state = initial_state(); + while (int i = next(state)) { + if (i >= num_digits) break; + separators.push_back(i); + } + for (int i = 0, sep_index = static_cast(separators.size() - 1); + i < num_digits; ++i) { + if (num_digits - i == separators[sep_index]) { + *out++ = separator(); + --sep_index; + } + *out++ = static_cast(digits[to_unsigned(i)]); + } + return out; + } +}; + +template +auto write_int_localized(OutputIt out, UInt value, unsigned prefix, + const basic_format_specs& specs, + const digit_grouping& grouping) -> OutputIt { + static_assert(std::is_same, UInt>::value, ""); + int num_digits = count_digits(value); + char digits[40]; + format_decimal(digits, value, num_digits); + unsigned size = to_unsigned((prefix != 0 ? 1 : 0) + num_digits + + grouping.count_separators(num_digits)); + return write_padded( + out, specs, size, size, [&](reserve_iterator it) { + if (prefix != 0) *it++ = static_cast(prefix); + return grouping.apply(it, string_view(digits, to_unsigned(num_digits))); + }); +} + +template +auto write_int_localized(OutputIt& out, UInt value, unsigned prefix, + const basic_format_specs& specs, locale_ref loc) + -> bool { + auto grouping = digit_grouping(loc); + out = write_int_localized(out, value, prefix, specs, grouping); + return true; +} + +FMT_CONSTEXPR inline void prefix_append(unsigned& prefix, unsigned value) { + prefix |= prefix != 0 ? value << 8 : value; + prefix += (1u + (value > 0xff ? 1 : 0)) << 24; +} + +template struct write_int_arg { + UInt abs_value; + unsigned prefix; +}; + +template +FMT_CONSTEXPR auto make_write_int_arg(T value, sign_t sign) + -> write_int_arg> { + auto prefix = 0u; + auto abs_value = static_cast>(value); + if (is_negative(value)) { + prefix = 0x01000000 | '-'; + abs_value = 0 - abs_value; + } else { + constexpr const unsigned prefixes[4] = {0, 0, 0x1000000u | '+', + 0x1000000u | ' '}; + prefix = prefixes[sign]; + } + return {abs_value, prefix}; +} + +template +FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, write_int_arg arg, + const basic_format_specs& specs, + locale_ref loc) -> OutputIt { + static_assert(std::is_same>::value, ""); + auto abs_value = arg.abs_value; + auto prefix = arg.prefix; + switch (specs.type) { + case presentation_type::none: + case presentation_type::dec: { + if (specs.localized && + write_int_localized(out, static_cast>(abs_value), + prefix, specs, loc)) { + return out; + } + auto num_digits = count_digits(abs_value); + return write_int( + out, num_digits, prefix, specs, [=](reserve_iterator it) { + return format_decimal(it, abs_value, num_digits).end; + }); + } + case presentation_type::hex_lower: + case presentation_type::hex_upper: { + bool upper = specs.type == presentation_type::hex_upper; + if (specs.alt) + prefix_append(prefix, unsigned(upper ? 'X' : 'x') << 8 | '0'); + int num_digits = count_digits<4>(abs_value); + return write_int( + out, num_digits, prefix, specs, [=](reserve_iterator it) { + return format_uint<4, Char>(it, abs_value, num_digits, upper); + }); + } + case presentation_type::bin_lower: + case presentation_type::bin_upper: { + bool upper = specs.type == presentation_type::bin_upper; + if (specs.alt) + prefix_append(prefix, unsigned(upper ? 'B' : 'b') << 8 | '0'); + int num_digits = count_digits<1>(abs_value); + return write_int(out, num_digits, prefix, specs, + [=](reserve_iterator it) { + return format_uint<1, Char>(it, abs_value, num_digits); + }); + } + case presentation_type::oct: { + int num_digits = count_digits<3>(abs_value); + // Octal prefix '0' is counted as a digit, so only add it if precision + // is not greater than the number of digits. + if (specs.alt && specs.precision <= num_digits && abs_value != 0) + prefix_append(prefix, '0'); + return write_int(out, num_digits, prefix, specs, + [=](reserve_iterator it) { + return format_uint<3, Char>(it, abs_value, num_digits); + }); + } + case presentation_type::chr: + return write_char(out, static_cast(abs_value), specs); + default: + throw_format_error("invalid type specifier"); + } + return out; +} +template +FMT_CONSTEXPR FMT_NOINLINE auto write_int_noinline( + OutputIt out, write_int_arg arg, const basic_format_specs& specs, + locale_ref loc) -> OutputIt { + return write_int(out, arg, specs, loc); +} +template ::value && + !std::is_same::value && + std::is_same>::value)> +FMT_CONSTEXPR FMT_INLINE auto write(OutputIt out, T value, + const basic_format_specs& specs, + locale_ref loc) -> OutputIt { + return write_int_noinline(out, make_write_int_arg(value, specs.sign), specs, + loc); +} +// An inlined version of write used in format string compilation. +template ::value && + !std::is_same::value && + !std::is_same>::value)> +FMT_CONSTEXPR FMT_INLINE auto write(OutputIt out, T value, + const basic_format_specs& specs, + locale_ref loc) -> OutputIt { + return write_int(out, make_write_int_arg(value, specs.sign), specs, loc); +} + +// An output iterator that counts the number of objects written to it and +// discards them. +class counting_iterator { + private: + size_t count_; + + public: + using iterator_category = std::output_iterator_tag; + using difference_type = std::ptrdiff_t; + using pointer = void; + using reference = void; + FMT_UNCHECKED_ITERATOR(counting_iterator); + + struct value_type { + template void operator=(const T&) {} + }; + + counting_iterator() : count_(0) {} + + size_t count() const { return count_; } + + counting_iterator& operator++() { + ++count_; + return *this; + } + counting_iterator operator++(int) { + auto it = *this; + ++*this; + return it; + } + + friend counting_iterator operator+(counting_iterator it, difference_type n) { + it.count_ += static_cast(n); + return it; + } + + value_type operator*() const { return {}; } +}; + +template +FMT_CONSTEXPR auto write(OutputIt out, basic_string_view s, + const basic_format_specs& specs) -> OutputIt { + auto data = s.data(); + auto size = s.size(); + if (specs.precision >= 0 && to_unsigned(specs.precision) < size) + size = code_point_index(s, to_unsigned(specs.precision)); + bool is_debug = specs.type == presentation_type::debug; + size_t width = 0; + if (specs.width != 0) { + if (is_debug) + width = write_escaped_string(counting_iterator{}, s).count(); + else + width = compute_width(basic_string_view(data, size)); + } + return write_padded(out, specs, size, width, + [=](reserve_iterator it) { + if (is_debug) return write_escaped_string(it, s); + return copy_str(data, data + size, it); + }); +} +template +FMT_CONSTEXPR auto write(OutputIt out, + basic_string_view> s, + const basic_format_specs& specs, locale_ref) + -> OutputIt { + check_string_type_spec(specs.type); + return write(out, s, specs); +} +template +FMT_CONSTEXPR auto write(OutputIt out, const Char* s, + const basic_format_specs& specs, locale_ref) + -> OutputIt { + return check_cstring_type_spec(specs.type) + ? write(out, basic_string_view(s), specs, {}) + : write_ptr(out, bit_cast(s), &specs); +} + +template ::value && + !std::is_same::value && + !std::is_same::value)> +FMT_CONSTEXPR auto write(OutputIt out, T value) -> OutputIt { + auto abs_value = static_cast>(value); + bool negative = is_negative(value); + // Don't do -abs_value since it trips unsigned-integer-overflow sanitizer. + if (negative) abs_value = ~abs_value + 1; + int num_digits = count_digits(abs_value); + auto size = (negative ? 1 : 0) + static_cast(num_digits); + auto it = reserve(out, size); + if (auto ptr = to_pointer(it, size)) { + if (negative) *ptr++ = static_cast('-'); + format_decimal(ptr, abs_value, num_digits); + return out; + } + if (negative) *it++ = static_cast('-'); + it = format_decimal(it, abs_value, num_digits).end; + return base_iterator(out, it); +} + +template +FMT_CONSTEXPR20 auto write_nonfinite(OutputIt out, bool isnan, + basic_format_specs specs, + const float_specs& fspecs) -> OutputIt { + auto str = + isnan ? (fspecs.upper ? "NAN" : "nan") : (fspecs.upper ? "INF" : "inf"); + constexpr size_t str_size = 3; + auto sign = fspecs.sign; + auto size = str_size + (sign ? 1 : 0); + // Replace '0'-padding with space for non-finite values. + const bool is_zero_fill = + specs.fill.size() == 1 && *specs.fill.data() == static_cast('0'); + if (is_zero_fill) specs.fill[0] = static_cast(' '); + return write_padded(out, specs, size, [=](reserve_iterator it) { + if (sign) *it++ = detail::sign(sign); + return copy_str(str, str + str_size, it); + }); +} + +// A decimal floating-point number significand * pow(10, exp). +struct big_decimal_fp { + const char* significand; + int significand_size; + int exponent; +}; + +constexpr auto get_significand_size(const big_decimal_fp& f) -> int { + return f.significand_size; +} +template +inline auto get_significand_size(const dragonbox::decimal_fp& f) -> int { + return count_digits(f.significand); +} + +template +constexpr auto write_significand(OutputIt out, const char* significand, + int significand_size) -> OutputIt { + return copy_str(significand, significand + significand_size, out); +} +template +inline auto write_significand(OutputIt out, UInt significand, + int significand_size) -> OutputIt { + return format_decimal(out, significand, significand_size).end; +} +template +FMT_CONSTEXPR20 auto write_significand(OutputIt out, T significand, + int significand_size, int exponent, + const Grouping& grouping) -> OutputIt { + if (!grouping.separator()) { + out = write_significand(out, significand, significand_size); + return detail::fill_n(out, exponent, static_cast('0')); + } + auto buffer = memory_buffer(); + write_significand(appender(buffer), significand, significand_size); + detail::fill_n(appender(buffer), exponent, '0'); + return grouping.apply(out, string_view(buffer.data(), buffer.size())); +} + +template ::value)> +inline auto write_significand(Char* out, UInt significand, int significand_size, + int integral_size, Char decimal_point) -> Char* { + if (!decimal_point) + return format_decimal(out, significand, significand_size).end; + out += significand_size + 1; + Char* end = out; + int floating_size = significand_size - integral_size; + for (int i = floating_size / 2; i > 0; --i) { + out -= 2; + copy2(out, digits2(static_cast(significand % 100))); + significand /= 100; + } + if (floating_size % 2 != 0) { + *--out = static_cast('0' + significand % 10); + significand /= 10; + } + *--out = decimal_point; + format_decimal(out - integral_size, significand, integral_size); + return end; +} + +template >::value)> +inline auto write_significand(OutputIt out, UInt significand, + int significand_size, int integral_size, + Char decimal_point) -> OutputIt { + // Buffer is large enough to hold digits (digits10 + 1) and a decimal point. + Char buffer[digits10() + 2]; + auto end = write_significand(buffer, significand, significand_size, + integral_size, decimal_point); + return detail::copy_str_noinline(buffer, end, out); +} + +template +FMT_CONSTEXPR auto write_significand(OutputIt out, const char* significand, + int significand_size, int integral_size, + Char decimal_point) -> OutputIt { + out = detail::copy_str_noinline(significand, + significand + integral_size, out); + if (!decimal_point) return out; + *out++ = decimal_point; + return detail::copy_str_noinline(significand + integral_size, + significand + significand_size, out); +} + +template +FMT_CONSTEXPR20 auto write_significand(OutputIt out, T significand, + int significand_size, int integral_size, + Char decimal_point, + const Grouping& grouping) -> OutputIt { + if (!grouping.separator()) { + return write_significand(out, significand, significand_size, integral_size, + decimal_point); + } + auto buffer = basic_memory_buffer(); + write_significand(buffer_appender(buffer), significand, + significand_size, integral_size, decimal_point); + grouping.apply( + out, basic_string_view(buffer.data(), to_unsigned(integral_size))); + return detail::copy_str_noinline(buffer.data() + integral_size, + buffer.end(), out); +} + +template > +FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& f, + const basic_format_specs& specs, + float_specs fspecs, locale_ref loc) + -> OutputIt { + auto significand = f.significand; + int significand_size = get_significand_size(f); + const Char zero = static_cast('0'); + auto sign = fspecs.sign; + size_t size = to_unsigned(significand_size) + (sign ? 1 : 0); + using iterator = reserve_iterator; + + Char decimal_point = + fspecs.locale ? detail::decimal_point(loc) : static_cast('.'); + + int output_exp = f.exponent + significand_size - 1; + auto use_exp_format = [=]() { + if (fspecs.format == float_format::exp) return true; + if (fspecs.format != float_format::general) return false; + // Use the fixed notation if the exponent is in [exp_lower, exp_upper), + // e.g. 0.0001 instead of 1e-04. Otherwise use the exponent notation. + const int exp_lower = -4, exp_upper = 16; + return output_exp < exp_lower || + output_exp >= (fspecs.precision > 0 ? fspecs.precision : exp_upper); + }; + if (use_exp_format()) { + int num_zeros = 0; + if (fspecs.showpoint) { + num_zeros = fspecs.precision - significand_size; + if (num_zeros < 0) num_zeros = 0; + size += to_unsigned(num_zeros); + } else if (significand_size == 1) { + decimal_point = Char(); + } + auto abs_output_exp = output_exp >= 0 ? output_exp : -output_exp; + int exp_digits = 2; + if (abs_output_exp >= 100) exp_digits = abs_output_exp >= 1000 ? 4 : 3; + + size += to_unsigned((decimal_point ? 1 : 0) + 2 + exp_digits); + char exp_char = fspecs.upper ? 'E' : 'e'; + auto write = [=](iterator it) { + if (sign) *it++ = detail::sign(sign); + // Insert a decimal point after the first digit and add an exponent. + it = write_significand(it, significand, significand_size, 1, + decimal_point); + if (num_zeros > 0) it = detail::fill_n(it, num_zeros, zero); + *it++ = static_cast(exp_char); + return write_exponent(output_exp, it); + }; + return specs.width > 0 ? write_padded(out, specs, size, write) + : base_iterator(out, write(reserve(out, size))); + } + + int exp = f.exponent + significand_size; + if (f.exponent >= 0) { + // 1234e5 -> 123400000[.0+] + size += to_unsigned(f.exponent); + int num_zeros = fspecs.precision - exp; + abort_fuzzing_if(num_zeros > 5000); + if (fspecs.showpoint) { + ++size; + if (num_zeros <= 0 && fspecs.format != float_format::fixed) num_zeros = 1; + if (num_zeros > 0) size += to_unsigned(num_zeros); + } + auto grouping = Grouping(loc, fspecs.locale); + size += to_unsigned(grouping.count_separators(exp)); + return write_padded(out, specs, size, [&](iterator it) { + if (sign) *it++ = detail::sign(sign); + it = write_significand(it, significand, significand_size, + f.exponent, grouping); + if (!fspecs.showpoint) return it; + *it++ = decimal_point; + return num_zeros > 0 ? detail::fill_n(it, num_zeros, zero) : it; + }); + } else if (exp > 0) { + // 1234e-2 -> 12.34[0+] + int num_zeros = fspecs.showpoint ? fspecs.precision - significand_size : 0; + size += 1 + to_unsigned(num_zeros > 0 ? num_zeros : 0); + auto grouping = Grouping(loc, fspecs.locale); + size += to_unsigned(grouping.count_separators(significand_size)); + return write_padded(out, specs, size, [&](iterator it) { + if (sign) *it++ = detail::sign(sign); + it = write_significand(it, significand, significand_size, exp, + decimal_point, grouping); + return num_zeros > 0 ? detail::fill_n(it, num_zeros, zero) : it; + }); + } + // 1234e-6 -> 0.001234 + int num_zeros = -exp; + if (significand_size == 0 && fspecs.precision >= 0 && + fspecs.precision < num_zeros) { + num_zeros = fspecs.precision; + } + bool pointy = num_zeros != 0 || significand_size != 0 || fspecs.showpoint; + size += 1 + (pointy ? 1 : 0) + to_unsigned(num_zeros); + return write_padded(out, specs, size, [&](iterator it) { + if (sign) *it++ = detail::sign(sign); + *it++ = zero; + if (!pointy) return it; + *it++ = decimal_point; + it = detail::fill_n(it, num_zeros, zero); + return write_significand(it, significand, significand_size); + }); +} + +template class fallback_digit_grouping { + public: + constexpr fallback_digit_grouping(locale_ref, bool) {} + + constexpr Char separator() const { return Char(); } + + constexpr int count_separators(int) const { return 0; } + + template + constexpr Out apply(Out out, basic_string_view) const { + return out; + } +}; + +template +FMT_CONSTEXPR20 auto write_float(OutputIt out, const DecimalFP& f, + const basic_format_specs& specs, + float_specs fspecs, locale_ref loc) + -> OutputIt { + if (is_constant_evaluated()) { + return do_write_float>(out, f, specs, fspecs, + loc); + } else { + return do_write_float(out, f, specs, fspecs, loc); + } +} + +template constexpr bool isnan(T value) { + return !(value >= value); // std::isnan doesn't support __float128. +} + +template +struct has_isfinite : std::false_type {}; + +template +struct has_isfinite> + : std::true_type {}; + +template ::value&& + has_isfinite::value)> +FMT_CONSTEXPR20 bool isfinite(T value) { + constexpr T inf = T(std::numeric_limits::infinity()); + if (is_constant_evaluated()) + return !detail::isnan(value) && value != inf && value != -inf; + return std::isfinite(value); +} +template ::value)> +FMT_CONSTEXPR bool isfinite(T value) { + T inf = T(std::numeric_limits::infinity()); + // std::isfinite doesn't support __float128. + return !detail::isnan(value) && value != inf && value != -inf; +} + +template ::value)> +FMT_INLINE FMT_CONSTEXPR bool signbit(T value) { + if (is_constant_evaluated()) { +#ifdef __cpp_if_constexpr + if constexpr (std::numeric_limits::is_iec559) { + auto bits = detail::bit_cast(static_cast(value)); + return (bits >> (num_bits() - 1)) != 0; + } +#endif + } + return std::signbit(static_cast(value)); +} + +enum class round_direction { unknown, up, down }; + +// Given the divisor (normally a power of 10), the remainder = v % divisor for +// some number v and the error, returns whether v should be rounded up, down, or +// whether the rounding direction can't be determined due to error. +// error should be less than divisor / 2. +FMT_CONSTEXPR inline round_direction get_round_direction(uint64_t divisor, + uint64_t remainder, + uint64_t error) { + FMT_ASSERT(remainder < divisor, ""); // divisor - remainder won't overflow. + FMT_ASSERT(error < divisor, ""); // divisor - error won't overflow. + FMT_ASSERT(error < divisor - error, ""); // error * 2 won't overflow. + // Round down if (remainder + error) * 2 <= divisor. + if (remainder <= divisor - remainder && error * 2 <= divisor - remainder * 2) + return round_direction::down; + // Round up if (remainder - error) * 2 >= divisor. + if (remainder >= error && + remainder - error >= divisor - (remainder - error)) { + return round_direction::up; + } + return round_direction::unknown; +} + +namespace digits { +enum result { + more, // Generate more digits. + done, // Done generating digits. + error // Digit generation cancelled due to an error. +}; +} + +struct gen_digits_handler { + char* buf; + int size; + int precision; + int exp10; + bool fixed; + + FMT_CONSTEXPR digits::result on_digit(char digit, uint64_t divisor, + uint64_t remainder, uint64_t error, + bool integral) { + FMT_ASSERT(remainder < divisor, ""); + buf[size++] = digit; + if (!integral && error >= remainder) return digits::error; + if (size < precision) return digits::more; + if (!integral) { + // Check if error * 2 < divisor with overflow prevention. + // The check is not needed for the integral part because error = 1 + // and divisor > (1 << 32) there. + if (error >= divisor || error >= divisor - error) return digits::error; + } else { + FMT_ASSERT(error == 1 && divisor > 2, ""); + } + auto dir = get_round_direction(divisor, remainder, error); + if (dir != round_direction::up) + return dir == round_direction::down ? digits::done : digits::error; + ++buf[size - 1]; + for (int i = size - 1; i > 0 && buf[i] > '9'; --i) { + buf[i] = '0'; + ++buf[i - 1]; + } + if (buf[0] > '9') { + buf[0] = '1'; + if (fixed) + buf[size++] = '0'; + else + ++exp10; + } + return digits::done; + } +}; + +inline FMT_CONSTEXPR20 void adjust_precision(int& precision, int exp10) { + // Adjust fixed precision by exponent because it is relative to decimal + // point. + if (exp10 > 0 && precision > max_value() - exp10) + FMT_THROW(format_error("number is too big")); + precision += exp10; +} + +// Generates output using the Grisu digit-gen algorithm. +// error: the size of the region (lower, upper) outside of which numbers +// definitely do not round to value (Delta in Grisu3). +FMT_INLINE FMT_CONSTEXPR20 auto grisu_gen_digits(fp value, uint64_t error, + int& exp, + gen_digits_handler& handler) + -> digits::result { + const fp one(1ULL << -value.e, value.e); + // The integral part of scaled value (p1 in Grisu) = value / one. It cannot be + // zero because it contains a product of two 64-bit numbers with MSB set (due + // to normalization) - 1, shifted right by at most 60 bits. + auto integral = static_cast(value.f >> -one.e); + FMT_ASSERT(integral != 0, ""); + FMT_ASSERT(integral == value.f >> -one.e, ""); + // The fractional part of scaled value (p2 in Grisu) c = value % one. + uint64_t fractional = value.f & (one.f - 1); + exp = count_digits(integral); // kappa in Grisu. + // Non-fixed formats require at least one digit and no precision adjustment. + if (handler.fixed) { + adjust_precision(handler.precision, exp + handler.exp10); + // Check if precision is satisfied just by leading zeros, e.g. + // format("{:.2f}", 0.001) gives "0.00" without generating any digits. + if (handler.precision <= 0) { + if (handler.precision < 0) return digits::done; + // Divide by 10 to prevent overflow. + uint64_t divisor = data::power_of_10_64[exp - 1] << -one.e; + auto dir = get_round_direction(divisor, value.f / 10, error * 10); + if (dir == round_direction::unknown) return digits::error; + handler.buf[handler.size++] = dir == round_direction::up ? '1' : '0'; + return digits::done; + } + } + // Generate digits for the integral part. This can produce up to 10 digits. + do { + uint32_t digit = 0; + auto divmod_integral = [&](uint32_t divisor) { + digit = integral / divisor; + integral %= divisor; + }; + // This optimization by Milo Yip reduces the number of integer divisions by + // one per iteration. + switch (exp) { + case 10: + divmod_integral(1000000000); + break; + case 9: + divmod_integral(100000000); + break; + case 8: + divmod_integral(10000000); + break; + case 7: + divmod_integral(1000000); + break; + case 6: + divmod_integral(100000); + break; + case 5: + divmod_integral(10000); + break; + case 4: + divmod_integral(1000); + break; + case 3: + divmod_integral(100); + break; + case 2: + divmod_integral(10); + break; + case 1: + digit = integral; + integral = 0; + break; + default: + FMT_ASSERT(false, "invalid number of digits"); + } + --exp; + auto remainder = (static_cast(integral) << -one.e) + fractional; + auto result = handler.on_digit(static_cast('0' + digit), + data::power_of_10_64[exp] << -one.e, + remainder, error, true); + if (result != digits::more) return result; + } while (exp > 0); + // Generate digits for the fractional part. + for (;;) { + fractional *= 10; + error *= 10; + char digit = static_cast('0' + (fractional >> -one.e)); + fractional &= one.f - 1; + --exp; + auto result = handler.on_digit(digit, one.f, fractional, error, false); + if (result != digits::more) return result; + } +} + +class bigint { + private: + // A bigint is stored as an array of bigits (big digits), with bigit at index + // 0 being the least significant one. + using bigit = uint32_t; + using double_bigit = uint64_t; + enum { bigits_capacity = 32 }; + basic_memory_buffer bigits_; + int exp_; + + FMT_CONSTEXPR20 bigit operator[](int index) const { + return bigits_[to_unsigned(index)]; + } + FMT_CONSTEXPR20 bigit& operator[](int index) { + return bigits_[to_unsigned(index)]; + } + + static constexpr const int bigit_bits = num_bits(); + + friend struct formatter; + + FMT_CONSTEXPR20 void subtract_bigits(int index, bigit other, bigit& borrow) { + auto result = static_cast((*this)[index]) - other - borrow; + (*this)[index] = static_cast(result); + borrow = static_cast(result >> (bigit_bits * 2 - 1)); + } + + FMT_CONSTEXPR20 void remove_leading_zeros() { + int num_bigits = static_cast(bigits_.size()) - 1; + while (num_bigits > 0 && (*this)[num_bigits] == 0) --num_bigits; + bigits_.resize(to_unsigned(num_bigits + 1)); + } + + // Computes *this -= other assuming aligned bigints and *this >= other. + FMT_CONSTEXPR20 void subtract_aligned(const bigint& other) { + FMT_ASSERT(other.exp_ >= exp_, "unaligned bigints"); + FMT_ASSERT(compare(*this, other) >= 0, ""); + bigit borrow = 0; + int i = other.exp_ - exp_; + for (size_t j = 0, n = other.bigits_.size(); j != n; ++i, ++j) + subtract_bigits(i, other.bigits_[j], borrow); + while (borrow > 0) subtract_bigits(i, 0, borrow); + remove_leading_zeros(); + } + + FMT_CONSTEXPR20 void multiply(uint32_t value) { + const double_bigit wide_value = value; + bigit carry = 0; + for (size_t i = 0, n = bigits_.size(); i < n; ++i) { + double_bigit result = bigits_[i] * wide_value + carry; + bigits_[i] = static_cast(result); + carry = static_cast(result >> bigit_bits); + } + if (carry != 0) bigits_.push_back(carry); + } + + template ::value || + std::is_same::value)> + FMT_CONSTEXPR20 void multiply(UInt value) { + using half_uint = + conditional_t::value, uint64_t, uint32_t>; + const int shift = num_bits() - bigit_bits; + const UInt lower = static_cast(value); + const UInt upper = value >> num_bits(); + UInt carry = 0; + for (size_t i = 0, n = bigits_.size(); i < n; ++i) { + UInt result = lower * bigits_[i] + static_cast(carry); + carry = (upper * bigits_[i] << shift) + (result >> bigit_bits) + + (carry >> bigit_bits); + bigits_[i] = static_cast(result); + } + while (carry != 0) { + bigits_.push_back(static_cast(carry)); + carry >>= bigit_bits; + } + } + + template ::value || + std::is_same::value)> + FMT_CONSTEXPR20 void assign(UInt n) { + size_t num_bigits = 0; + do { + bigits_[num_bigits++] = static_cast(n); + n >>= bigit_bits; + } while (n != 0); + bigits_.resize(num_bigits); + exp_ = 0; + } + + public: + FMT_CONSTEXPR20 bigint() : exp_(0) {} + explicit bigint(uint64_t n) { assign(n); } + + bigint(const bigint&) = delete; + void operator=(const bigint&) = delete; + + FMT_CONSTEXPR20 void assign(const bigint& other) { + auto size = other.bigits_.size(); + bigits_.resize(size); + auto data = other.bigits_.data(); + std::copy(data, data + size, make_checked(bigits_.data(), size)); + exp_ = other.exp_; + } + + template FMT_CONSTEXPR20 void operator=(Int n) { + FMT_ASSERT(n > 0, ""); + assign(uint64_or_128_t(n)); + } + + FMT_CONSTEXPR20 int num_bigits() const { + return static_cast(bigits_.size()) + exp_; + } + + FMT_NOINLINE FMT_CONSTEXPR20 bigint& operator<<=(int shift) { + FMT_ASSERT(shift >= 0, ""); + exp_ += shift / bigit_bits; + shift %= bigit_bits; + if (shift == 0) return *this; + bigit carry = 0; + for (size_t i = 0, n = bigits_.size(); i < n; ++i) { + bigit c = bigits_[i] >> (bigit_bits - shift); + bigits_[i] = (bigits_[i] << shift) + carry; + carry = c; + } + if (carry != 0) bigits_.push_back(carry); + return *this; + } + + template FMT_CONSTEXPR20 bigint& operator*=(Int value) { + FMT_ASSERT(value > 0, ""); + multiply(uint32_or_64_or_128_t(value)); + return *this; + } + + friend FMT_CONSTEXPR20 int compare(const bigint& lhs, const bigint& rhs) { + int num_lhs_bigits = lhs.num_bigits(), num_rhs_bigits = rhs.num_bigits(); + if (num_lhs_bigits != num_rhs_bigits) + return num_lhs_bigits > num_rhs_bigits ? 1 : -1; + int i = static_cast(lhs.bigits_.size()) - 1; + int j = static_cast(rhs.bigits_.size()) - 1; + int end = i - j; + if (end < 0) end = 0; + for (; i >= end; --i, --j) { + bigit lhs_bigit = lhs[i], rhs_bigit = rhs[j]; + if (lhs_bigit != rhs_bigit) return lhs_bigit > rhs_bigit ? 1 : -1; + } + if (i != j) return i > j ? 1 : -1; + return 0; + } + + // Returns compare(lhs1 + lhs2, rhs). + friend FMT_CONSTEXPR20 int add_compare(const bigint& lhs1, const bigint& lhs2, + const bigint& rhs) { + auto minimum = [](int a, int b) { return a < b ? a : b; }; + auto maximum = [](int a, int b) { return a > b ? a : b; }; + int max_lhs_bigits = maximum(lhs1.num_bigits(), lhs2.num_bigits()); + int num_rhs_bigits = rhs.num_bigits(); + if (max_lhs_bigits + 1 < num_rhs_bigits) return -1; + if (max_lhs_bigits > num_rhs_bigits) return 1; + auto get_bigit = [](const bigint& n, int i) -> bigit { + return i >= n.exp_ && i < n.num_bigits() ? n[i - n.exp_] : 0; + }; + double_bigit borrow = 0; + int min_exp = minimum(minimum(lhs1.exp_, lhs2.exp_), rhs.exp_); + for (int i = num_rhs_bigits - 1; i >= min_exp; --i) { + double_bigit sum = + static_cast(get_bigit(lhs1, i)) + get_bigit(lhs2, i); + bigit rhs_bigit = get_bigit(rhs, i); + if (sum > rhs_bigit + borrow) return 1; + borrow = rhs_bigit + borrow - sum; + if (borrow > 1) return -1; + borrow <<= bigit_bits; + } + return borrow != 0 ? -1 : 0; + } + + // Assigns pow(10, exp) to this bigint. + FMT_CONSTEXPR20 void assign_pow10(int exp) { + FMT_ASSERT(exp >= 0, ""); + if (exp == 0) return *this = 1; + // Find the top bit. + int bitmask = 1; + while (exp >= bitmask) bitmask <<= 1; + bitmask >>= 1; + // pow(10, exp) = pow(5, exp) * pow(2, exp). First compute pow(5, exp) by + // repeated squaring and multiplication. + *this = 5; + bitmask >>= 1; + while (bitmask != 0) { + square(); + if ((exp & bitmask) != 0) *this *= 5; + bitmask >>= 1; + } + *this <<= exp; // Multiply by pow(2, exp) by shifting. + } + + FMT_CONSTEXPR20 void square() { + int num_bigits = static_cast(bigits_.size()); + int num_result_bigits = 2 * num_bigits; + basic_memory_buffer n(std::move(bigits_)); + bigits_.resize(to_unsigned(num_result_bigits)); + auto sum = uint128_t(); + for (int bigit_index = 0; bigit_index < num_bigits; ++bigit_index) { + // Compute bigit at position bigit_index of the result by adding + // cross-product terms n[i] * n[j] such that i + j == bigit_index. + for (int i = 0, j = bigit_index; j >= 0; ++i, --j) { + // Most terms are multiplied twice which can be optimized in the future. + sum += static_cast(n[i]) * n[j]; + } + (*this)[bigit_index] = static_cast(sum); + sum >>= num_bits(); // Compute the carry. + } + // Do the same for the top half. + for (int bigit_index = num_bigits; bigit_index < num_result_bigits; + ++bigit_index) { + for (int j = num_bigits - 1, i = bigit_index - j; i < num_bigits;) + sum += static_cast(n[i++]) * n[j--]; + (*this)[bigit_index] = static_cast(sum); + sum >>= num_bits(); + } + remove_leading_zeros(); + exp_ *= 2; + } + + // If this bigint has a bigger exponent than other, adds trailing zero to make + // exponents equal. This simplifies some operations such as subtraction. + FMT_CONSTEXPR20 void align(const bigint& other) { + int exp_difference = exp_ - other.exp_; + if (exp_difference <= 0) return; + int num_bigits = static_cast(bigits_.size()); + bigits_.resize(to_unsigned(num_bigits + exp_difference)); + for (int i = num_bigits - 1, j = i + exp_difference; i >= 0; --i, --j) + bigits_[j] = bigits_[i]; + std::uninitialized_fill_n(bigits_.data(), exp_difference, 0); + exp_ -= exp_difference; + } + + // Divides this bignum by divisor, assigning the remainder to this and + // returning the quotient. + FMT_CONSTEXPR20 int divmod_assign(const bigint& divisor) { + FMT_ASSERT(this != &divisor, ""); + if (compare(*this, divisor) < 0) return 0; + FMT_ASSERT(divisor.bigits_[divisor.bigits_.size() - 1u] != 0, ""); + align(divisor); + int quotient = 0; + do { + subtract_aligned(divisor); + ++quotient; + } while (compare(*this, divisor) >= 0); + return quotient; + } +}; + +// format_dragon flags. +enum dragon { + predecessor_closer = 1, + fixup = 2, // Run fixup to correct exp10 which can be off by one. + fixed = 4, +}; + +// Formats a floating-point number using a variation of the Fixed-Precision +// Positive Floating-Point Printout ((FPP)^2) algorithm by Steele & White: +// https://fmt.dev/papers/p372-steele.pdf. +FMT_CONSTEXPR20 inline void format_dragon(basic_fp value, + unsigned flags, int num_digits, + buffer& buf, int& exp10) { + bigint numerator; // 2 * R in (FPP)^2. + bigint denominator; // 2 * S in (FPP)^2. + // lower and upper are differences between value and corresponding boundaries. + bigint lower; // (M^- in (FPP)^2). + bigint upper_store; // upper's value if different from lower. + bigint* upper = nullptr; // (M^+ in (FPP)^2). + // Shift numerator and denominator by an extra bit or two (if lower boundary + // is closer) to make lower and upper integers. This eliminates multiplication + // by 2 during later computations. + bool is_predecessor_closer = (flags & dragon::predecessor_closer) != 0; + int shift = is_predecessor_closer ? 2 : 1; + if (value.e >= 0) { + numerator = value.f; + numerator <<= value.e + shift; + lower = 1; + lower <<= value.e; + if (is_predecessor_closer) { + upper_store = 1; + upper_store <<= value.e + 1; + upper = &upper_store; + } + denominator.assign_pow10(exp10); + denominator <<= shift; + } else if (exp10 < 0) { + numerator.assign_pow10(-exp10); + lower.assign(numerator); + if (is_predecessor_closer) { + upper_store.assign(numerator); + upper_store <<= 1; + upper = &upper_store; + } + numerator *= value.f; + numerator <<= shift; + denominator = 1; + denominator <<= shift - value.e; + } else { + numerator = value.f; + numerator <<= shift; + denominator.assign_pow10(exp10); + denominator <<= shift - value.e; + lower = 1; + if (is_predecessor_closer) { + upper_store = 1ULL << 1; + upper = &upper_store; + } + } + bool even = (value.f & 1) == 0; + if (!upper) upper = &lower; + if ((flags & dragon::fixup) != 0) { + if (add_compare(numerator, *upper, denominator) + even <= 0) { + --exp10; + numerator *= 10; + if (num_digits < 0) { + lower *= 10; + if (upper != &lower) *upper *= 10; + } + } + if ((flags & dragon::fixed) != 0) adjust_precision(num_digits, exp10 + 1); + } + // Invariant: value == (numerator / denominator) * pow(10, exp10). + if (num_digits < 0) { + // Generate the shortest representation. + num_digits = 0; + char* data = buf.data(); + for (;;) { + int digit = numerator.divmod_assign(denominator); + bool low = compare(numerator, lower) - even < 0; // numerator <[=] lower. + // numerator + upper >[=] pow10: + bool high = add_compare(numerator, *upper, denominator) + even > 0; + data[num_digits++] = static_cast('0' + digit); + if (low || high) { + if (!low) { + ++data[num_digits - 1]; + } else if (high) { + int result = add_compare(numerator, numerator, denominator); + // Round half to even. + if (result > 0 || (result == 0 && (digit % 2) != 0)) + ++data[num_digits - 1]; + } + buf.try_resize(to_unsigned(num_digits)); + exp10 -= num_digits - 1; + return; + } + numerator *= 10; + lower *= 10; + if (upper != &lower) *upper *= 10; + } + } + // Generate the given number of digits. + exp10 -= num_digits - 1; + if (num_digits == 0) { + denominator *= 10; + auto digit = add_compare(numerator, numerator, denominator) > 0 ? '1' : '0'; + buf.push_back(digit); + return; + } + buf.try_resize(to_unsigned(num_digits)); + for (int i = 0; i < num_digits - 1; ++i) { + int digit = numerator.divmod_assign(denominator); + buf[i] = static_cast('0' + digit); + numerator *= 10; + } + int digit = numerator.divmod_assign(denominator); + auto result = add_compare(numerator, numerator, denominator); + if (result > 0 || (result == 0 && (digit % 2) != 0)) { + if (digit == 9) { + const auto overflow = '0' + 10; + buf[num_digits - 1] = overflow; + // Propagate the carry. + for (int i = num_digits - 1; i > 0 && buf[i] == overflow; --i) { + buf[i] = '0'; + ++buf[i - 1]; + } + if (buf[0] == overflow) { + buf[0] = '1'; + ++exp10; + } + return; + } + ++digit; + } + buf[num_digits - 1] = static_cast('0' + digit); +} + +template +FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs, + buffer& buf) -> int { + // float is passed as double to reduce the number of instantiations. + static_assert(!std::is_same::value, ""); + FMT_ASSERT(value >= 0, "value is negative"); + auto converted_value = convert_float(value); + + const bool fixed = specs.format == float_format::fixed; + if (value <= 0) { // <= instead of == to silence a warning. + if (precision <= 0 || !fixed) { + buf.push_back('0'); + return 0; + } + buf.try_resize(to_unsigned(precision)); + fill_n(buf.data(), precision, '0'); + return -precision; + } + + int exp = 0; + bool use_dragon = true; + unsigned dragon_flags = 0; + if (!is_fast_float()) { + const auto inv_log2_10 = 0.3010299956639812; // 1 / log2(10) + using info = dragonbox::float_info; + const auto f = basic_fp(converted_value); + // Compute exp, an approximate power of 10, such that + // 10^(exp - 1) <= value < 10^exp or 10^exp <= value < 10^(exp + 1). + // This is based on log10(value) == log2(value) / log2(10) and approximation + // of log2(value) by e + num_fraction_bits idea from double-conversion. + exp = static_cast( + std::ceil((f.e + count_digits<1>(f.f) - 1) * inv_log2_10 - 1e-10)); + dragon_flags = dragon::fixup; + } else if (!is_constant_evaluated() && precision < 0) { + // Use Dragonbox for the shortest format. + if (specs.binary32) { + auto dec = dragonbox::to_decimal(static_cast(value)); + write(buffer_appender(buf), dec.significand); + return dec.exponent; + } + auto dec = dragonbox::to_decimal(static_cast(value)); + write(buffer_appender(buf), dec.significand); + return dec.exponent; + } else { + // Use Grisu + Dragon4 for the given precision: + // https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf. + const int min_exp = -60; // alpha in Grisu. + int cached_exp10 = 0; // K in Grisu. + fp normalized = normalize(fp(converted_value)); + const auto cached_pow = get_cached_power( + min_exp - (normalized.e + fp::num_significand_bits), cached_exp10); + normalized = normalized * cached_pow; + gen_digits_handler handler{buf.data(), 0, precision, -cached_exp10, fixed}; + if (grisu_gen_digits(normalized, 1, exp, handler) != digits::error && + !is_constant_evaluated()) { + exp += handler.exp10; + buf.try_resize(to_unsigned(handler.size)); + use_dragon = false; + } else { + exp += handler.size - cached_exp10 - 1; + precision = handler.precision; + } + } + if (use_dragon) { + auto f = basic_fp(); + bool is_predecessor_closer = specs.binary32 + ? f.assign(static_cast(value)) + : f.assign(converted_value); + if (is_predecessor_closer) dragon_flags |= dragon::predecessor_closer; + if (fixed) dragon_flags |= dragon::fixed; + // Limit precision to the maximum possible number of significant digits in + // an IEEE754 double because we don't need to generate zeros. + const int max_double_digits = 767; + if (precision > max_double_digits) precision = max_double_digits; + format_dragon(f, dragon_flags, precision, buf, exp); + } + if (!fixed && !specs.showpoint) { + // Remove trailing zeros. + auto num_digits = buf.size(); + while (num_digits > 0 && buf[num_digits - 1] == '0') { + --num_digits; + ++exp; + } + buf.try_resize(num_digits); + } + return exp; +} + +template ::value)> +FMT_CONSTEXPR20 auto write(OutputIt out, T value, + basic_format_specs specs, locale_ref loc = {}) + -> OutputIt { + if (const_check(!is_supported_floating_point(value))) return out; + float_specs fspecs = parse_float_type_spec(specs); + fspecs.sign = specs.sign; + if (detail::signbit(value)) { // value < 0 is false for NaN so use signbit. + fspecs.sign = sign::minus; + value = -value; + } else if (fspecs.sign == sign::minus) { + fspecs.sign = sign::none; + } + + if (!detail::isfinite(value)) + return write_nonfinite(out, detail::isnan(value), specs, fspecs); + + if (specs.align == align::numeric && fspecs.sign) { + auto it = reserve(out, 1); + *it++ = detail::sign(fspecs.sign); + out = base_iterator(out, it); + fspecs.sign = sign::none; + if (specs.width != 0) --specs.width; + } + + memory_buffer buffer; + if (fspecs.format == float_format::hex) { + if (fspecs.sign) buffer.push_back(detail::sign(fspecs.sign)); + snprintf_float(convert_float(value), specs.precision, fspecs, buffer); + return write_bytes(out, {buffer.data(), buffer.size()}, + specs); + } + int precision = specs.precision >= 0 || specs.type == presentation_type::none + ? specs.precision + : 6; + if (fspecs.format == float_format::exp) { + if (precision == max_value()) + throw_format_error("number is too big"); + else + ++precision; + } else if (fspecs.format != float_format::fixed && precision == 0) { + precision = 1; + } + if (const_check(std::is_same())) fspecs.binary32 = true; + int exp = format_float(convert_float(value), precision, fspecs, buffer); + fspecs.precision = precision; + auto f = big_decimal_fp{buffer.data(), static_cast(buffer.size()), exp}; + return write_float(out, f, specs, fspecs, loc); +} + +template ::value)> +FMT_CONSTEXPR20 auto write(OutputIt out, T value) -> OutputIt { + if (is_constant_evaluated()) + return write(out, value, basic_format_specs()); + if (const_check(!is_supported_floating_point(value))) return out; + + auto fspecs = float_specs(); + if (detail::signbit(value)) { + fspecs.sign = sign::minus; + value = -value; + } + + constexpr auto specs = basic_format_specs(); + using floaty = conditional_t::value, double, T>; + using uint = typename dragonbox::float_info::carrier_uint; + uint mask = exponent_mask(); + if ((bit_cast(value) & mask) == mask) + return write_nonfinite(out, std::isnan(value), specs, fspecs); + + auto dec = dragonbox::to_decimal(static_cast(value)); + return write_float(out, dec, specs, fspecs, {}); +} + +template ::value && + !is_fast_float::value)> +inline auto write(OutputIt out, T value) -> OutputIt { + return write(out, value, basic_format_specs()); +} + +template +auto write(OutputIt out, monostate, basic_format_specs = {}, + locale_ref = {}) -> OutputIt { + FMT_ASSERT(false, ""); + return out; +} + +template +FMT_CONSTEXPR auto write(OutputIt out, basic_string_view value) + -> OutputIt { + auto it = reserve(out, value.size()); + it = copy_str_noinline(value.begin(), value.end(), it); + return base_iterator(out, it); +} + +template ::value)> +constexpr auto write(OutputIt out, const T& value) -> OutputIt { + return write(out, to_string_view(value)); +} + +// FMT_ENABLE_IF() condition separated to workaround an MSVC bug. +template < + typename Char, typename OutputIt, typename T, + bool check = + std::is_enum::value && !std::is_same::value && + mapped_type_constant>::value != + type::custom_type, + FMT_ENABLE_IF(check)> +FMT_CONSTEXPR auto write(OutputIt out, T value) -> OutputIt { + return write(out, static_cast>(value)); +} + +template ::value)> +FMT_CONSTEXPR auto write(OutputIt out, T value, + const basic_format_specs& specs = {}, + locale_ref = {}) -> OutputIt { + return specs.type != presentation_type::none && + specs.type != presentation_type::string + ? write(out, value ? 1 : 0, specs, {}) + : write_bytes(out, value ? "true" : "false", specs); +} + +template +FMT_CONSTEXPR auto write(OutputIt out, Char value) -> OutputIt { + auto it = reserve(out, 1); + *it++ = value; + return base_iterator(out, it); +} + +template +FMT_CONSTEXPR_CHAR_TRAITS auto write(OutputIt out, const Char* value) + -> OutputIt { + if (!value) { + throw_format_error("string pointer is null"); + } else { + out = write(out, basic_string_view(value)); + } + return out; +} + +template ::value)> +auto write(OutputIt out, const T* value, + const basic_format_specs& specs = {}, locale_ref = {}) + -> OutputIt { + check_pointer_type_spec(specs.type, error_handler()); + return write_ptr(out, bit_cast(value), &specs); +} + +// A write overload that handles implicit conversions. +template > +FMT_CONSTEXPR auto write(OutputIt out, const T& value) -> enable_if_t< + std::is_class::value && !is_string::value && + !is_floating_point::value && !std::is_same::value && + !std::is_same().map(value))>::value, + OutputIt> { + return write(out, arg_mapper().map(value)); +} + +template > +FMT_CONSTEXPR auto write(OutputIt out, const T& value) + -> enable_if_t::value == type::custom_type, + OutputIt> { + using formatter_type = + conditional_t::value, + typename Context::template formatter_type, + fallback_formatter>; + auto ctx = Context(out, {}, {}); + return formatter_type().format(value, ctx); +} + +// An argument visitor that formats the argument and writes it via the output +// iterator. It's a class and not a generic lambda for compatibility with C++11. +template struct default_arg_formatter { + using iterator = buffer_appender; + using context = buffer_context; + + iterator out; + basic_format_args args; + locale_ref loc; + + template auto operator()(T value) -> iterator { + return write(out, value); + } + auto operator()(typename basic_format_arg::handle h) -> iterator { + basic_format_parse_context parse_ctx({}); + context format_ctx(out, args, loc); + h.format(parse_ctx, format_ctx); + return format_ctx.out(); + } +}; + +template struct arg_formatter { + using iterator = buffer_appender; + using context = buffer_context; + + iterator out; + const basic_format_specs& specs; + locale_ref locale; + + template + FMT_CONSTEXPR FMT_INLINE auto operator()(T value) -> iterator { + return detail::write(out, value, specs, locale); + } + auto operator()(typename basic_format_arg::handle) -> iterator { + // User-defined types are handled separately because they require access + // to the parse context. + return out; + } +}; + +template struct custom_formatter { + basic_format_parse_context& parse_ctx; + buffer_context& ctx; + + void operator()( + typename basic_format_arg>::handle h) const { + h.format(parse_ctx, ctx); + } + template void operator()(T) const {} +}; + +template +using is_integer = + bool_constant::value && !std::is_same::value && + !std::is_same::value && + !std::is_same::value>; + +template class width_checker { + public: + explicit FMT_CONSTEXPR width_checker(ErrorHandler& eh) : handler_(eh) {} + + template ::value)> + FMT_CONSTEXPR auto operator()(T value) -> unsigned long long { + if (is_negative(value)) handler_.on_error("negative width"); + return static_cast(value); + } + + template ::value)> + FMT_CONSTEXPR auto operator()(T) -> unsigned long long { + handler_.on_error("width is not integer"); + return 0; + } + + private: + ErrorHandler& handler_; +}; + +template class precision_checker { + public: + explicit FMT_CONSTEXPR precision_checker(ErrorHandler& eh) : handler_(eh) {} + + template ::value)> + FMT_CONSTEXPR auto operator()(T value) -> unsigned long long { + if (is_negative(value)) handler_.on_error("negative precision"); + return static_cast(value); + } + + template ::value)> + FMT_CONSTEXPR auto operator()(T) -> unsigned long long { + handler_.on_error("precision is not integer"); + return 0; + } + + private: + ErrorHandler& handler_; +}; + +template